Include sys/filio.h to get the FIONREAD definition on Solaris.
[wine.git] / programs / winefile / winefile.c
bloba51c80b8fe453c3230550c8d51452420800cdf14
1 /*
2 * Winefile
4 * Copyright 2000, 2003, 2004, 2005 Martin Fuchs
5 * Copyright 2006 Jason Green
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #ifdef __WINE__
23 #include "config.h"
24 #include "wine/port.h"
26 /* for unix filesystem function calls */
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #endif
32 #define COBJMACROS
34 #include "winefile.h"
35 #include "resource.h"
37 #ifdef _NO_EXTENSIONS
38 #undef _LEFT_FILES
39 #endif
41 #ifndef _MAX_PATH
42 #define _MAX_DRIVE 3
43 #define _MAX_FNAME 256
44 #define _MAX_DIR _MAX_FNAME
45 #define _MAX_EXT _MAX_FNAME
46 #define _MAX_PATH 260
47 #endif
49 #ifdef NONAMELESSUNION
50 #define UNION_MEMBER(x) DUMMYUNIONNAME.x
51 #else
52 #define UNION_MEMBER(x) x
53 #endif
56 #ifdef _SHELL_FOLDERS
57 #define DEFAULT_SPLIT_POS 300
58 #else
59 #define DEFAULT_SPLIT_POS 200
60 #endif
62 static const WCHAR registry_key[] = { 'S','o','f','t','w','a','r','e','\\',
63 'W','i','n','e','\\',
64 'W','i','n','e','F','i','l','e','\0'};
65 static const WCHAR reg_start_x[] = { 's','t','a','r','t','X','\0'};
66 static const WCHAR reg_start_y[] = { 's','t','a','r','t','Y','\0'};
67 static const WCHAR reg_width[] = { 'w','i','d','t','h','\0'};
68 static const WCHAR reg_height[] = { 'h','e','i','g','h','t','\0'};
69 static const WCHAR reg_logfont[] = { 'l','o','g','f','o','n','t','\0'};
71 enum ENTRY_TYPE {
72 ET_WINDOWS,
73 ET_UNIX,
74 #ifdef _SHELL_FOLDERS
75 ET_SHELL
76 #endif
79 typedef struct _Entry {
80 struct _Entry* next;
81 struct _Entry* down;
82 struct _Entry* up;
84 BOOL expanded;
85 BOOL scanned;
86 int level;
88 WIN32_FIND_DATA data;
90 #ifndef _NO_EXTENSIONS
91 BY_HANDLE_FILE_INFORMATION bhfi;
92 BOOL bhfi_valid;
93 enum ENTRY_TYPE etype;
94 #endif
95 #ifdef _SHELL_FOLDERS
96 LPITEMIDLIST pidl;
97 IShellFolder* folder;
98 HICON hicon;
99 #endif
100 } Entry;
102 typedef struct {
103 Entry entry;
104 TCHAR path[MAX_PATH];
105 TCHAR volname[_MAX_FNAME];
106 TCHAR fs[_MAX_DIR];
107 DWORD drive_type;
108 DWORD fs_flags;
109 } Root;
111 enum COLUMN_FLAGS {
112 COL_SIZE = 0x01,
113 COL_DATE = 0x02,
114 COL_TIME = 0x04,
115 COL_ATTRIBUTES = 0x08,
116 COL_DOSNAMES = 0x10,
117 #ifdef _NO_EXTENSIONS
118 COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES
119 #else
120 COL_INDEX = 0x20,
121 COL_LINKS = 0x40,
122 COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES|COL_INDEX|COL_LINKS
123 #endif
126 typedef enum {
127 SORT_NAME,
128 SORT_EXT,
129 SORT_SIZE,
130 SORT_DATE
131 } SORT_ORDER;
133 typedef struct {
134 HWND hwnd;
135 #ifndef _NO_EXTENSIONS
136 HWND hwndHeader;
137 #endif
139 #ifndef _NO_EXTENSIONS
140 #define COLUMNS 10
141 #else
142 #define COLUMNS 5
143 #endif
144 int widths[COLUMNS];
145 int positions[COLUMNS+1];
147 BOOL treePane;
148 int visible_cols;
149 Entry* root;
150 Entry* cur;
151 } Pane;
153 typedef struct {
154 HWND hwnd;
155 Pane left;
156 Pane right;
157 int focus_pane; /* 0: left 1: right */
158 WINDOWPLACEMENT pos;
159 int split_pos;
160 BOOL header_wdths_ok;
162 TCHAR path[MAX_PATH];
163 TCHAR filter_pattern[MAX_PATH];
164 int filter_flags;
165 Root root;
167 SORT_ORDER sortOrder;
168 } ChildWnd;
172 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd);
173 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd);
174 static void refresh_child(ChildWnd* child);
175 static void refresh_drives(void);
176 static void get_path(Entry* dir, PTSTR path);
177 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols);
179 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
180 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
181 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
184 /* globals */
185 WINEFILE_GLOBALS Globals;
187 static int last_split;
189 /* some common string constants */
190 static const TCHAR sEmpty[] = {'\0'};
191 static const WCHAR sSpace[] = {' ', '\0'};
192 static const TCHAR sNumFmt[] = {'%','d','\0'};
193 static const TCHAR sQMarks[] = {'?','?','?','\0'};
195 /* window class names */
196 static const TCHAR sWINEFILEFRAME[] = {'W','F','S','_','F','r','a','m','e','\0'};
197 static const TCHAR sWINEFILETREE[] = {'W','F','S','_','T','r','e','e','\0'};
199 static const TCHAR sLongHexFmt[] = {'%','I','6','4','X','\0'};
200 static const TCHAR sLongNumFmt[] = {'%','I','6','4','u','\0'};
203 /* load resource string */
204 static LPTSTR load_string(LPTSTR buffer, UINT id)
206 LoadString(Globals.hInstance, id, buffer, BUFFER_LEN);
208 return buffer;
211 #define RS(b, i) load_string(b, i)
214 /* display error message for the specified WIN32 error code */
215 static void display_error(HWND hwnd, DWORD error)
217 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
218 PTSTR msg;
220 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
221 0, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (PTSTR)&msg, 0, NULL))
222 MessageBox(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
223 else
224 MessageBox(hwnd, RS(b1,IDS_ERROR), RS(b2,IDS_WINEFILE), MB_OK);
226 LocalFree(msg);
230 /* display network error message using WNetGetLastError() */
231 static void display_network_error(HWND hwnd)
233 TCHAR msg[BUFFER_LEN], provider[BUFFER_LEN], b2[BUFFER_LEN];
234 DWORD error;
236 if (WNetGetLastError(&error, msg, BUFFER_LEN, provider, BUFFER_LEN) == NO_ERROR)
237 MessageBox(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
240 static inline BOOL get_check(HWND hwnd, INT id)
242 return BST_CHECKED&SendMessageW(GetDlgItem(hwnd, id), BM_GETSTATE, 0, 0);
245 static inline INT set_check(HWND hwnd, INT id, BOOL on)
247 return SendMessageW(GetDlgItem(hwnd, id), BM_SETCHECK, on?BST_CHECKED:BST_UNCHECKED, 0);
250 static inline void choose_font(HWND hwnd)
252 WCHAR dlg_name[BUFFER_LEN], dlg_info[BUFFER_LEN];
253 CHOOSEFONTW chFont;
254 LOGFONTW lFont;
256 HDC hdc = GetDC(hwnd);
257 chFont.lStructSize = sizeof(CHOOSEFONT);
258 chFont.hwndOwner = hwnd;
259 chFont.hDC = NULL;
260 chFont.lpLogFont = &lFont;
261 chFont.Flags = CF_SCREENFONTS | CF_FORCEFONTEXIST | CF_LIMITSIZE | CF_NOSCRIPTSEL;
262 chFont.rgbColors = RGB(0,0,0);
263 chFont.lCustData = 0;
264 chFont.lpfnHook = NULL;
265 chFont.lpTemplateName = NULL;
266 chFont.hInstance = Globals.hInstance;
267 chFont.lpszStyle = NULL;
268 chFont.nFontType = SIMULATED_FONTTYPE;
269 chFont.nSizeMin = 0;
270 chFont.nSizeMax = 24;
272 if (ChooseFontW(&chFont)) {
273 HWND childWnd;
274 HFONT hFontOld;
276 DeleteObject(Globals.hfont);
277 Globals.hfont = CreateFontIndirectW(&lFont);
278 hFontOld = SelectObject(hdc, Globals.hfont);
279 GetTextExtentPoint32W(hdc, sSpace, 1, &Globals.spaceSize);
281 /* change font in all open child windows */
282 for(childWnd=GetWindow(Globals.hmdiclient,GW_CHILD); childWnd; childWnd=GetNextWindow(childWnd,GW_HWNDNEXT)) {
283 ChildWnd* child = (ChildWnd*) GetWindowLongPtrW(childWnd, GWLP_USERDATA);
284 SendMessageW(child->left.hwnd, WM_SETFONT, (WPARAM)Globals.hfont, TRUE);
285 SendMessageW(child->right.hwnd, WM_SETFONT, (WPARAM)Globals.hfont, TRUE);
286 SendMessageW(child->left.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
287 SendMessageW(child->right.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
288 InvalidateRect(child->left.hwnd, NULL, TRUE);
289 InvalidateRect(child->right.hwnd, NULL, TRUE);
292 SelectObject(hdc, hFontOld);
294 else if (CommDlgExtendedError()) {
295 LoadStringW(Globals.hInstance, IDS_FONT_SEL_DLG_NAME, dlg_name, BUFFER_LEN);
296 LoadStringW(Globals.hInstance, IDS_FONT_SEL_ERROR, dlg_info, BUFFER_LEN);
297 MessageBoxW(hwnd, dlg_info, dlg_name, MB_OK);
300 ReleaseDC(hwnd, hdc);
303 #ifdef __WINE__
305 #ifdef UNICODE
307 /* call vswprintf() in msvcrt.dll */
308 /*TODO: fix swprintf() in non-msvcrt mode, so that this dynamic linking function can be removed */
309 static int msvcrt_swprintf(WCHAR* buffer, const WCHAR* fmt, ...)
311 static int (__cdecl *pvswprintf)(WCHAR*, const WCHAR*, va_list) = NULL;
312 va_list ap;
313 int ret;
315 if (!pvswprintf) {
316 HMODULE hModMsvcrt = LoadLibraryA("msvcrt");
317 pvswprintf = (int(__cdecl*)(WCHAR*,const WCHAR*,va_list)) GetProcAddress(hModMsvcrt, "vswprintf");
320 va_start(ap, fmt);
321 ret = (*pvswprintf)(buffer, fmt, ap);
322 va_end(ap);
324 return ret;
327 static LPCWSTR my_wcsrchr(LPCWSTR str, WCHAR c)
329 LPCWSTR p = str;
331 while(*p)
332 ++p;
334 do {
335 if (--p < str)
336 return NULL;
337 } while(*p != c);
339 return p;
342 #define _tcsrchr my_wcsrchr
343 #else /* UNICODE */
344 #define _tcsrchr strrchr
345 #endif /* UNICODE */
347 #endif /* __WINE__ */
350 /* allocate and initialise a directory entry */
351 static Entry* alloc_entry(void)
353 Entry* entry = HeapAlloc(GetProcessHeap(), 0, sizeof(Entry));
355 #ifdef _SHELL_FOLDERS
356 entry->pidl = NULL;
357 entry->folder = NULL;
358 entry->hicon = 0;
359 #endif
361 return entry;
364 /* free a directory entry */
365 static void free_entry(Entry* entry)
367 #ifdef _SHELL_FOLDERS
368 if (entry->hicon && entry->hicon!=(HICON)-1)
369 DestroyIcon(entry->hicon);
371 if (entry->folder && entry->folder!=Globals.iDesktop)
372 IShellFolder_Release(entry->folder);
374 if (entry->pidl)
375 IMalloc_Free(Globals.iMalloc, entry->pidl);
376 #endif
378 HeapFree(GetProcessHeap(), 0, entry);
381 /* recursively free all child entries */
382 static void free_entries(Entry* dir)
384 Entry *entry, *next=dir->down;
386 if (next) {
387 dir->down = 0;
389 do {
390 entry = next;
391 next = entry->next;
393 free_entries(entry);
394 free_entry(entry);
395 } while(next);
400 static void read_directory_win(Entry* dir, LPCTSTR path)
402 Entry* first_entry = NULL;
403 Entry* last = NULL;
404 Entry* entry;
406 int level = dir->level + 1;
407 WIN32_FIND_DATA w32fd;
408 HANDLE hFind;
409 #ifndef _NO_EXTENSIONS
410 HANDLE hFile;
411 #endif
413 TCHAR buffer[MAX_PATH], *p;
414 for(p=buffer; *path; )
415 *p++ = *path++;
417 *p++ = '\\';
418 p[0] = '*';
419 p[1] = '\0';
421 hFind = FindFirstFile(buffer, &w32fd);
423 if (hFind != INVALID_HANDLE_VALUE) {
424 do {
425 #ifdef _NO_EXTENSIONS
426 /* hide directory entry "." */
427 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
428 LPCTSTR name = w32fd.cFileName;
430 if (name[0]=='.' && name[1]=='\0')
431 continue;
433 #endif
434 entry = alloc_entry();
436 if (!first_entry)
437 first_entry = entry;
439 if (last)
440 last->next = entry;
442 memcpy(&entry->data, &w32fd, sizeof(WIN32_FIND_DATA));
443 entry->down = NULL;
444 entry->up = dir;
445 entry->expanded = FALSE;
446 entry->scanned = FALSE;
447 entry->level = level;
449 #ifndef _NO_EXTENSIONS
450 entry->etype = ET_WINDOWS;
451 entry->bhfi_valid = FALSE;
453 lstrcpy(p, entry->data.cFileName);
455 hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
456 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
458 if (hFile != INVALID_HANDLE_VALUE) {
459 if (GetFileInformationByHandle(hFile, &entry->bhfi))
460 entry->bhfi_valid = TRUE;
462 CloseHandle(hFile);
464 #endif
466 last = entry;
467 } while(FindNextFile(hFind, &w32fd));
469 if (last)
470 last->next = NULL;
472 FindClose(hFind);
475 dir->down = first_entry;
476 dir->scanned = TRUE;
480 static Entry* find_entry_win(Entry* dir, LPCTSTR name)
482 Entry* entry;
484 for(entry=dir->down; entry; entry=entry->next) {
485 LPCTSTR p = name;
486 LPCTSTR q = entry->data.cFileName;
488 do {
489 if (!*p || *p == '\\' || *p == '/')
490 return entry;
491 } while(tolower(*p++) == tolower(*q++));
493 p = name;
494 q = entry->data.cAlternateFileName;
496 do {
497 if (!*p || *p == '\\' || *p == '/')
498 return entry;
499 } while(tolower(*p++) == tolower(*q++));
502 return 0;
506 static Entry* read_tree_win(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
508 TCHAR buffer[MAX_PATH];
509 Entry* entry = &root->entry;
510 LPCTSTR s = path;
511 PTSTR d = buffer;
513 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
515 #ifndef _NO_EXTENSIONS
516 entry->etype = ET_WINDOWS;
517 #endif
519 while(entry) {
520 while(*s && *s != '\\' && *s != '/')
521 *d++ = *s++;
523 while(*s == '\\' || *s == '/')
524 s++;
526 *d++ = '\\';
527 *d = '\0';
529 read_directory(entry, buffer, sortOrder, hwnd);
531 if (entry->down)
532 entry->expanded = TRUE;
534 if (!*s)
535 break;
537 entry = find_entry_win(entry, s);
540 SetCursor(old_cursor);
542 return entry;
546 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
548 static BOOL time_to_filetime(const time_t* t, FILETIME* ftime)
550 struct tm* tm = gmtime(t);
551 SYSTEMTIME stime;
553 if (!tm)
554 return FALSE;
556 stime.wYear = tm->tm_year+1900;
557 stime.wMonth = tm->tm_mon+1;
558 /* stime.wDayOfWeek */
559 stime.wDay = tm->tm_mday;
560 stime.wHour = tm->tm_hour;
561 stime.wMinute = tm->tm_min;
562 stime.wSecond = tm->tm_sec;
564 return SystemTimeToFileTime(&stime, ftime);
567 static void read_directory_unix(Entry* dir, LPCTSTR path)
569 Entry* first_entry = NULL;
570 Entry* last = NULL;
571 Entry* entry;
572 DIR* pdir;
574 int level = dir->level + 1;
575 #ifdef UNICODE
576 char cpath[MAX_PATH];
578 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, cpath, MAX_PATH, NULL, NULL);
579 #else
580 const char* cpath = path;
581 #endif
583 pdir = opendir(cpath);
585 if (pdir) {
586 struct stat st;
587 struct dirent* ent;
588 char buffer[MAX_PATH], *p;
589 const char* s;
591 for(p=buffer,s=cpath; *s; )
592 *p++ = *s++;
594 if (p==buffer || p[-1]!='/')
595 *p++ = '/';
597 while((ent=readdir(pdir))) {
598 entry = alloc_entry();
600 if (!first_entry)
601 first_entry = entry;
603 if (last)
604 last->next = entry;
606 entry->etype = ET_UNIX;
608 strcpy(p, ent->d_name);
609 #ifdef UNICODE
610 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, entry->data.cFileName, MAX_PATH);
611 #else
612 lstrcpy(entry->data.cFileName, p);
613 #endif
615 if (!stat(buffer, &st)) {
616 entry->data.dwFileAttributes = p[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
618 if (S_ISDIR(st.st_mode))
619 entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
621 entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
622 entry->data.nFileSizeHigh = st.st_size >> 32;
624 memset(&entry->data.ftCreationTime, 0, sizeof(FILETIME));
625 time_to_filetime(&st.st_atime, &entry->data.ftLastAccessTime);
626 time_to_filetime(&st.st_mtime, &entry->data.ftLastWriteTime);
628 entry->bhfi.nFileIndexLow = ent->d_ino;
629 entry->bhfi.nFileIndexHigh = 0;
631 entry->bhfi.nNumberOfLinks = st.st_nlink;
633 entry->bhfi_valid = TRUE;
634 } else {
635 entry->data.nFileSizeLow = 0;
636 entry->data.nFileSizeHigh = 0;
637 entry->bhfi_valid = FALSE;
640 entry->down = NULL;
641 entry->up = dir;
642 entry->expanded = FALSE;
643 entry->scanned = FALSE;
644 entry->level = level;
646 last = entry;
649 if (last)
650 last->next = NULL;
652 closedir(pdir);
655 dir->down = first_entry;
656 dir->scanned = TRUE;
659 static Entry* find_entry_unix(Entry* dir, LPCTSTR name)
661 Entry* entry;
663 for(entry=dir->down; entry; entry=entry->next) {
664 LPCTSTR p = name;
665 LPCTSTR q = entry->data.cFileName;
667 do {
668 if (!*p || *p == '/')
669 return entry;
670 } while(*p++ == *q++);
673 return 0;
676 static Entry* read_tree_unix(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
678 TCHAR buffer[MAX_PATH];
679 Entry* entry = &root->entry;
680 LPCTSTR s = path;
681 PTSTR d = buffer;
683 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
685 entry->etype = ET_UNIX;
687 while(entry) {
688 while(*s && *s != '/')
689 *d++ = *s++;
691 while(*s == '/')
692 s++;
694 *d++ = '/';
695 *d = '\0';
697 read_directory(entry, buffer, sortOrder, hwnd);
699 if (entry->down)
700 entry->expanded = TRUE;
702 if (!*s)
703 break;
705 entry = find_entry_unix(entry, s);
708 SetCursor(old_cursor);
710 return entry;
713 #endif /* !defined(_NO_EXTENSIONS) && defined(__WINE__) */
716 #ifdef _SHELL_FOLDERS
718 #ifdef UNICODE
719 #define get_strret get_strretW
720 #define path_from_pidl path_from_pidlW
721 #else
722 #define get_strret get_strretA
723 #define path_from_pidl path_from_pidlA
724 #endif
727 static void free_strret(STRRET* str)
729 if (str->uType == STRRET_WSTR)
730 IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
734 #ifndef UNICODE
736 static LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
738 LPCSTR s;
739 LPSTR d = dest;
741 for(s=source; count&&(*d++=*s++); )
742 count--;
744 return dest;
747 static void get_strretA(STRRET* str, const SHITEMID* shiid, LPSTR buffer, int len)
749 switch(str->uType) {
750 case STRRET_WSTR:
751 WideCharToMultiByte(CP_ACP, 0, str->UNION_MEMBER(pOleStr), -1, buffer, len, NULL, NULL);
752 break;
754 case STRRET_OFFSET:
755 strcpyn(buffer, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), len);
756 break;
758 case STRRET_CSTR:
759 strcpyn(buffer, str->UNION_MEMBER(cStr), len);
763 static HRESULT path_from_pidlA(IShellFolder* folder, LPITEMIDLIST pidl, LPSTR buffer, int len)
765 STRRET str;
767 /* SHGDN_FORPARSING: get full path of id list */
768 HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
770 if (SUCCEEDED(hr)) {
771 get_strretA(&str, &pidl->mkid, buffer, len);
772 free_strret(&str);
773 } else
774 buffer[0] = '\0';
776 return hr;
779 #endif
781 static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
783 LPCWSTR s;
784 LPWSTR d = dest;
786 for(s=source; count&&(*d++=*s++); )
787 count--;
789 return dest;
792 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
794 switch(str->uType) {
795 case STRRET_WSTR:
796 wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
797 break;
799 case STRRET_OFFSET:
800 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
801 break;
803 case STRRET_CSTR:
804 MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
809 static HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
811 STRRET str;
813 HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str);
815 if (SUCCEEDED(hr)) {
816 get_strret(&str, &pidl->mkid, buffer, len);
817 free_strret(&str);
818 } else
819 buffer[0] = '\0';
821 return hr;
825 static HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len)
827 STRRET str;
829 /* SHGDN_FORPARSING: get full path of id list */
830 HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
832 if (SUCCEEDED(hr)) {
833 get_strretW(&str, &pidl->mkid, buffer, len);
834 free_strret(&str);
835 } else
836 buffer[0] = '\0';
838 return hr;
842 /* create an item id list from a file system path */
844 static LPITEMIDLIST get_path_pidl(LPTSTR path, HWND hwnd)
846 LPITEMIDLIST pidl;
847 HRESULT hr;
848 ULONG len;
850 #ifdef UNICODE
851 LPWSTR buffer = path;
852 #else
853 WCHAR buffer[MAX_PATH];
854 MultiByteToWideChar(CP_ACP, 0, path, -1, buffer, MAX_PATH);
855 #endif
857 hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
858 if (FAILED(hr))
859 return NULL;
861 return pidl;
865 /* convert an item id list from relative to absolute (=relative to the desktop) format */
867 static LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd)
869 if (entry->up && entry->up->etype==ET_SHELL) {
870 LPITEMIDLIST idl = NULL;
872 while (entry->up) {
873 idl = ILCombine(ILClone(entry->pidl), idl);
874 entry = entry->up;
877 return idl;
878 } else if (entry->etype == ET_WINDOWS) {
879 TCHAR path[MAX_PATH];
881 get_path(entry, path);
883 return get_path_pidl(path, hwnd);
884 } else if (entry->pidl)
885 return ILClone(entry->pidl);
887 return NULL;
891 static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
893 IExtractIcon* pExtract;
895 if (SUCCEEDED(IShellFolder_GetUIObjectOf(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIcon, 0, (LPVOID*)&pExtract))) {
896 TCHAR path[_MAX_PATH];
897 unsigned flags;
898 HICON hicon;
899 int idx;
901 if (SUCCEEDED(IExtractIconW_GetIconLocation(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
902 if (!(flags & GIL_NOTFILENAME)) {
903 if (idx == -1)
904 idx = 0; /* special case for some control panel applications */
906 if ((int)ExtractIconEx(path, idx, 0, &hicon, 1) > 0)
907 flags &= ~GIL_DONTCACHE;
908 } else {
909 HICON hIconLarge = 0;
911 HRESULT hr = IExtractIconW_Extract(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
913 if (SUCCEEDED(hr))
914 DestroyIcon(hIconLarge);
917 return hicon;
921 return 0;
925 static Entry* find_entry_shell(Entry* dir, LPCITEMIDLIST pidl)
927 Entry* entry;
929 for(entry=dir->down; entry; entry=entry->next) {
930 if (entry->pidl->mkid.cb == pidl->mkid.cb &&
931 !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb))
932 return entry;
935 return 0;
938 static Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, SORT_ORDER sortOrder, HWND hwnd)
940 Entry* entry = &root->entry;
941 Entry* next;
942 LPITEMIDLIST next_pidl = pidl;
943 IShellFolder* folder;
944 IShellFolder* child = NULL;
945 HRESULT hr;
947 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
949 #ifndef _NO_EXTENSIONS
950 entry->etype = ET_SHELL;
951 #endif
953 folder = Globals.iDesktop;
955 while(entry) {
956 entry->pidl = next_pidl;
957 entry->folder = folder;
959 if (!pidl->mkid.cb)
960 break;
962 /* copy first element of item idlist */
963 next_pidl = IMalloc_Alloc(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT));
964 memcpy(next_pidl, pidl, pidl->mkid.cb);
965 ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0;
967 hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
968 if (!SUCCEEDED(hr))
969 break;
971 read_directory(entry, NULL, sortOrder, hwnd);
973 if (entry->down)
974 entry->expanded = TRUE;
976 next = find_entry_shell(entry, next_pidl);
977 if (!next)
978 break;
980 folder = child;
981 entry = next;
983 /* go to next element */
984 pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
987 SetCursor(old_cursor);
989 return entry;
993 static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* w32fdata)
995 if (!(attribs & SFGAO_FILESYSTEM) ||
996 FAILED(SHGetDataFromIDList(folder, pidl, SHGDFIL_FINDDATA, w32fdata, sizeof(WIN32_FIND_DATA)))) {
997 WIN32_FILE_ATTRIBUTE_DATA fad;
998 IDataObject* pDataObj;
1000 STGMEDIUM medium = {0, {0}, 0};
1001 FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
1003 HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
1005 if (SUCCEEDED(hr)) {
1006 hr = IDataObject_GetData(pDataObj, &fmt, &medium);
1008 IDataObject_Release(pDataObj);
1010 if (SUCCEEDED(hr)) {
1011 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
1012 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
1014 if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
1015 w32fdata->dwFileAttributes = fad.dwFileAttributes;
1016 w32fdata->ftCreationTime = fad.ftCreationTime;
1017 w32fdata->ftLastAccessTime = fad.ftLastAccessTime;
1018 w32fdata->ftLastWriteTime = fad.ftLastWriteTime;
1020 if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1021 w32fdata->nFileSizeLow = fad.nFileSizeLow;
1022 w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
1026 SetErrorMode(sem_org);
1028 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
1029 GlobalFree(medium.UNION_MEMBER(hGlobal));
1034 if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
1035 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
1037 if (attribs & SFGAO_READONLY)
1038 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
1040 if (attribs & SFGAO_COMPRESSED)
1041 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
1045 static void read_directory_shell(Entry* dir, HWND hwnd)
1047 IShellFolder* folder = dir->folder;
1048 int level = dir->level + 1;
1049 HRESULT hr;
1051 IShellFolder* child;
1052 IEnumIDList* idlist;
1054 Entry* first_entry = NULL;
1055 Entry* last = NULL;
1056 Entry* entry;
1058 if (!folder)
1059 return;
1061 hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
1063 if (SUCCEEDED(hr)) {
1064 for(;;) {
1065 #define FETCH_ITEM_COUNT 32
1066 LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
1067 SFGAOF attribs;
1068 ULONG cnt = 0;
1069 ULONG n;
1071 memset(pidls, 0, sizeof(pidls));
1073 hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
1074 if (!SUCCEEDED(hr))
1075 break;
1077 if (hr == S_FALSE)
1078 break;
1080 for(n=0; n<cnt; ++n) {
1081 entry = alloc_entry();
1083 if (!first_entry)
1084 first_entry = entry;
1086 if (last)
1087 last->next = entry;
1089 memset(&entry->data, 0, sizeof(WIN32_FIND_DATA));
1090 entry->bhfi_valid = FALSE;
1092 attribs = ~SFGAO_FILESYSTEM; /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
1094 hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
1096 if (SUCCEEDED(hr)) {
1097 if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
1098 fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
1100 entry->bhfi_valid = TRUE;
1101 } else
1102 attribs = 0;
1103 } else
1104 attribs = 0;
1106 entry->pidl = pidls[n];
1108 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1109 hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
1111 if (SUCCEEDED(hr))
1112 entry->folder = child;
1113 else
1114 entry->folder = NULL;
1116 else
1117 entry->folder = NULL;
1119 if (!entry->data.cFileName[0])
1120 /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
1122 /* get display icons for files and virtual objects */
1123 if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
1124 !(attribs & SFGAO_FILESYSTEM)) {
1125 entry->hicon = extract_icon(folder, pidls[n]);
1127 if (!entry->hicon)
1128 entry->hicon = (HICON)-1; /* don't try again later */
1131 entry->down = NULL;
1132 entry->up = dir;
1133 entry->expanded = FALSE;
1134 entry->scanned = FALSE;
1135 entry->level = level;
1137 #ifndef _NO_EXTENSIONS
1138 entry->etype = ET_SHELL;
1139 entry->bhfi_valid = FALSE;
1140 #endif
1142 last = entry;
1146 IEnumIDList_Release(idlist);
1149 if (last)
1150 last->next = NULL;
1152 dir->down = first_entry;
1153 dir->scanned = TRUE;
1156 #endif /* _SHELL_FOLDERS */
1159 /* sort order for different directory/file types */
1160 enum TYPE_ORDER {
1161 TO_DIR = 0,
1162 TO_DOT = 1,
1163 TO_DOTDOT = 2,
1164 TO_OTHER_DIR = 3,
1165 TO_FILE = 4
1168 /* distinguish between ".", ".." and any other directory names */
1169 static int TypeOrderFromDirname(LPCTSTR name)
1171 if (name[0] == '.') {
1172 if (name[1] == '\0')
1173 return TO_DOT; /* "." */
1175 if (name[1]=='.' && name[2]=='\0')
1176 return TO_DOTDOT; /* ".." */
1179 return TO_OTHER_DIR; /* anything else */
1182 /* directories first... */
1183 static int compareType(const WIN32_FIND_DATA* fd1, const WIN32_FIND_DATA* fd2)
1185 int order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1186 int order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1188 /* Handle "." and ".." as special case and move them at the very first beginning. */
1189 if (order1==TO_DIR && order2==TO_DIR) {
1190 order1 = TypeOrderFromDirname(fd1->cFileName);
1191 order2 = TypeOrderFromDirname(fd2->cFileName);
1194 return order2==order1? 0: order1<order2? -1: 1;
1198 static int compareName(const void* arg1, const void* arg2)
1200 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1201 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1203 int cmp = compareType(fd1, fd2);
1204 if (cmp)
1205 return cmp;
1207 return lstrcmpi(fd1->cFileName, fd2->cFileName);
1210 static int compareExt(const void* arg1, const void* arg2)
1212 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1213 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1214 const TCHAR *name1, *name2, *ext1, *ext2;
1216 int cmp = compareType(fd1, fd2);
1217 if (cmp)
1218 return cmp;
1220 name1 = fd1->cFileName;
1221 name2 = fd2->cFileName;
1223 ext1 = _tcsrchr(name1, '.');
1224 ext2 = _tcsrchr(name2, '.');
1226 if (ext1)
1227 ext1++;
1228 else
1229 ext1 = sEmpty;
1231 if (ext2)
1232 ext2++;
1233 else
1234 ext2 = sEmpty;
1236 cmp = lstrcmpi(ext1, ext2);
1237 if (cmp)
1238 return cmp;
1240 return lstrcmpi(name1, name2);
1243 static int compareSize(const void* arg1, const void* arg2)
1245 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1246 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1248 int cmp = compareType(fd1, fd2);
1249 if (cmp)
1250 return cmp;
1252 cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
1254 if (cmp < 0)
1255 return -1;
1256 else if (cmp > 0)
1257 return 1;
1259 cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
1261 return cmp<0? -1: cmp>0? 1: 0;
1264 static int compareDate(const void* arg1, const void* arg2)
1266 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1267 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1269 int cmp = compareType(fd1, fd2);
1270 if (cmp)
1271 return cmp;
1273 return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
1277 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
1278 compareName, /* SORT_NAME */
1279 compareExt, /* SORT_EXT */
1280 compareSize, /* SORT_SIZE */
1281 compareDate /* SORT_DATE */
1285 static void SortDirectory(Entry* dir, SORT_ORDER sortOrder)
1287 Entry* entry = dir->down;
1288 Entry** array, **p;
1289 int len;
1291 len = 0;
1292 for(entry=dir->down; entry; entry=entry->next)
1293 len++;
1295 if (len) {
1296 array = HeapAlloc(GetProcessHeap(), 0, len*sizeof(Entry*));
1298 p = array;
1299 for(entry=dir->down; entry; entry=entry->next)
1300 *p++ = entry;
1302 /* call qsort with the appropriate compare function */
1303 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
1305 dir->down = array[0];
1307 for(p=array; --len; p++)
1308 p[0]->next = p[1];
1310 (*p)->next = 0;
1312 HeapFree(GetProcessHeap(), 0, array);
1317 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
1319 TCHAR buffer[MAX_PATH];
1320 Entry* entry;
1321 LPCTSTR s;
1322 PTSTR d;
1324 #ifdef _SHELL_FOLDERS
1325 if (dir->etype == ET_SHELL)
1327 read_directory_shell(dir, hwnd);
1329 if (Globals.prescan_node) {
1330 s = path;
1331 d = buffer;
1333 while(*s)
1334 *d++ = *s++;
1336 *d++ = '\\';
1338 for(entry=dir->down; entry; entry=entry->next)
1339 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1340 read_directory_shell(entry, hwnd);
1341 SortDirectory(entry, sortOrder);
1345 else
1346 #endif
1347 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1348 if (dir->etype == ET_UNIX)
1350 read_directory_unix(dir, path);
1352 if (Globals.prescan_node) {
1353 s = path;
1354 d = buffer;
1356 while(*s)
1357 *d++ = *s++;
1359 *d++ = '/';
1361 for(entry=dir->down; entry; entry=entry->next)
1362 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1363 lstrcpy(d, entry->data.cFileName);
1364 read_directory_unix(entry, buffer);
1365 SortDirectory(entry, sortOrder);
1369 else
1370 #endif
1372 read_directory_win(dir, path);
1374 if (Globals.prescan_node) {
1375 s = path;
1376 d = buffer;
1378 while(*s)
1379 *d++ = *s++;
1381 *d++ = '\\';
1383 for(entry=dir->down; entry; entry=entry->next)
1384 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1385 lstrcpy(d, entry->data.cFileName);
1386 read_directory_win(entry, buffer);
1387 SortDirectory(entry, sortOrder);
1392 SortDirectory(dir, sortOrder);
1396 static Entry* read_tree(Root* root, LPCTSTR path, LPITEMIDLIST pidl, LPTSTR drv, SORT_ORDER sortOrder, HWND hwnd)
1398 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1399 static const TCHAR sSlash[] = {'/', '\0'};
1400 #endif
1401 static const TCHAR sBackslash[] = {'\\', '\0'};
1403 #ifdef _SHELL_FOLDERS
1404 if (pidl)
1406 /* read shell namespace tree */
1407 root->drive_type = DRIVE_UNKNOWN;
1408 drv[0] = '\\';
1409 drv[1] = '\0';
1410 load_string(root->volname, IDS_DESKTOP);
1411 root->fs_flags = 0;
1412 load_string(root->fs, IDS_SHELL);
1414 return read_tree_shell(root, pidl, sortOrder, hwnd);
1416 else
1417 #endif
1418 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1419 if (*path == '/')
1421 /* read unix file system tree */
1422 root->drive_type = GetDriveType(path);
1424 lstrcat(drv, sSlash);
1425 load_string(root->volname, IDS_ROOT_FS);
1426 root->fs_flags = 0;
1427 load_string(root->fs, IDS_UNIXFS);
1429 lstrcpy(root->path, sSlash);
1431 return read_tree_unix(root, path, sortOrder, hwnd);
1433 #endif
1435 /* read WIN32 file system tree */
1436 root->drive_type = GetDriveType(path);
1438 lstrcat(drv, sBackslash);
1439 GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1441 lstrcpy(root->path, drv);
1443 return read_tree_win(root, path, sortOrder, hwnd);
1447 /* flags to filter different file types */
1448 enum TYPE_FILTER {
1449 TF_DIRECTORIES = 0x01,
1450 TF_PROGRAMS = 0x02,
1451 TF_DOCUMENTS = 0x04,
1452 TF_OTHERS = 0x08,
1453 TF_HIDDEN = 0x10,
1454 TF_ALL = 0x1F
1458 static ChildWnd* alloc_child_window(LPCTSTR path, LPITEMIDLIST pidl, HWND hwnd)
1460 TCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
1461 TCHAR dir_path[MAX_PATH];
1462 TCHAR b1[BUFFER_LEN];
1463 static const TCHAR sAsterics[] = {'*', '\0'};
1465 ChildWnd* child = HeapAlloc(GetProcessHeap(), 0, sizeof(ChildWnd));
1466 Root* root = &child->root;
1467 Entry* entry;
1469 memset(child, 0, sizeof(ChildWnd));
1471 child->left.treePane = TRUE;
1472 child->left.visible_cols = 0;
1474 child->right.treePane = FALSE;
1475 #ifndef _NO_EXTENSIONS
1476 child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS;
1477 #else
1478 child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES;
1479 #endif
1481 child->pos.length = sizeof(WINDOWPLACEMENT);
1482 child->pos.flags = 0;
1483 child->pos.showCmd = SW_SHOWNORMAL;
1484 child->pos.rcNormalPosition.left = CW_USEDEFAULT;
1485 child->pos.rcNormalPosition.top = CW_USEDEFAULT;
1486 child->pos.rcNormalPosition.right = CW_USEDEFAULT;
1487 child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
1489 child->focus_pane = 0;
1490 child->split_pos = DEFAULT_SPLIT_POS;
1491 child->sortOrder = SORT_NAME;
1492 child->header_wdths_ok = FALSE;
1494 if (path)
1496 lstrcpy(child->path, path);
1498 _tsplitpath(path, drv, dir, name, ext);
1501 lstrcpy(child->filter_pattern, sAsterics);
1502 child->filter_flags = TF_ALL;
1504 root->entry.level = 0;
1506 lstrcpy(dir_path, drv);
1507 lstrcat(dir_path, dir);
1508 entry = read_tree(root, dir_path, pidl, drv, child->sortOrder, hwnd);
1510 #ifdef _SHELL_FOLDERS
1511 if (root->entry.etype == ET_SHELL)
1512 load_string(root->entry.data.cFileName, IDS_DESKTOP);
1513 else
1514 #endif
1515 wsprintf(root->entry.data.cFileName, RS(b1,IDS_TITLEFMT), drv, root->fs);
1517 root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1519 child->left.root = &root->entry;
1520 child->right.root = NULL;
1522 set_curdir(child, entry, 0, hwnd);
1524 return child;
1528 /* free all memory associated with a child window */
1529 static void free_child_window(ChildWnd* child)
1531 free_entries(&child->root.entry);
1532 HeapFree(GetProcessHeap(), 0, child);
1536 /* get full path of specified directory entry */
1537 static void get_path(Entry* dir, PTSTR path)
1539 Entry* entry;
1540 int len = 0;
1541 int level = 0;
1543 #ifdef _SHELL_FOLDERS
1544 if (dir->etype == ET_SHELL)
1546 SFGAOF attribs;
1547 HRESULT hr = S_OK;
1549 path[0] = '\0';
1551 attribs = 0;
1553 if (dir->folder)
1554 hr = IShellFolder_GetAttributesOf(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1556 if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1557 IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1559 hr = path_from_pidl(parent, dir->pidl, path, MAX_PATH);
1562 else
1563 #endif
1565 for(entry=dir; entry; level++) {
1566 LPCTSTR name;
1567 int l;
1570 LPCTSTR s;
1571 name = entry->data.cFileName;
1572 s = name;
1574 for(l=0; *s && *s != '/' && *s != '\\'; s++)
1575 l++;
1578 if (entry->up) {
1579 if (l > 0) {
1580 memmove(path+l+1, path, len*sizeof(TCHAR));
1581 memcpy(path+1, name, l*sizeof(TCHAR));
1582 len += l+1;
1584 #ifndef _NO_EXTENSIONS
1585 if (entry->etype == ET_UNIX)
1586 path[0] = '/';
1587 else
1588 #endif
1589 path[0] = '\\';
1592 entry = entry->up;
1593 } else {
1594 memmove(path+l, path, len*sizeof(TCHAR));
1595 memcpy(path, name, l*sizeof(TCHAR));
1596 len += l;
1597 break;
1601 if (!level) {
1602 #ifndef _NO_EXTENSIONS
1603 if (entry->etype == ET_UNIX)
1604 path[len++] = '/';
1605 else
1606 #endif
1607 path[len++] = '\\';
1610 path[len] = '\0';
1614 static windowOptions load_registry_settings(void)
1616 DWORD size;
1617 DWORD type;
1618 HKEY hKey;
1619 windowOptions opts;
1620 LOGFONT logfont;
1622 RegOpenKeyExW( HKEY_CURRENT_USER, registry_key,
1623 0, KEY_QUERY_VALUE, &hKey );
1625 size = sizeof(DWORD);
1627 if( RegQueryValueExW( hKey, reg_start_x, NULL, &type,
1628 (LPBYTE) &opts.start_x, &size ) != ERROR_SUCCESS )
1629 opts.start_x = CW_USEDEFAULT;
1631 if( RegQueryValueExW( hKey, reg_start_y, NULL, &type,
1632 (LPBYTE) &opts.start_y, &size ) != ERROR_SUCCESS )
1633 opts.start_y = CW_USEDEFAULT;
1635 if( RegQueryValueExW( hKey, reg_width, NULL, &type,
1636 (LPBYTE) &opts.width, &size ) != ERROR_SUCCESS )
1637 opts.width = CW_USEDEFAULT;
1639 if( RegQueryValueExW( hKey, reg_height, NULL, &type,
1640 (LPBYTE) &opts.height, &size ) != ERROR_SUCCESS )
1641 opts.height = CW_USEDEFAULT;
1642 size=sizeof(logfont);
1643 if( RegQueryValueExW( hKey, reg_logfont, NULL, &type,
1644 (LPBYTE) &logfont, &size ) != ERROR_SUCCESS )
1645 GetObject(GetStockObject(DEFAULT_GUI_FONT),sizeof(logfont),&logfont);
1647 RegCloseKey( hKey );
1649 Globals.hfont = CreateFontIndirect(&logfont);
1650 return opts;
1653 static void save_registry_settings(void)
1655 WINDOWINFO wi;
1656 HKEY hKey;
1657 INT width, height;
1658 LOGFONT logfont;
1660 wi.cbSize = sizeof( WINDOWINFO );
1661 GetWindowInfo(Globals.hMainWnd, &wi);
1662 width = wi.rcWindow.right - wi.rcWindow.left;
1663 height = wi.rcWindow.bottom - wi.rcWindow.top;
1665 if ( RegOpenKeyExW( HKEY_CURRENT_USER, registry_key,
1666 0, KEY_SET_VALUE, &hKey ) != ERROR_SUCCESS )
1668 /* Unable to save registry settings - try to create key */
1669 if ( RegCreateKeyExW( HKEY_CURRENT_USER, registry_key,
1670 0, NULL, REG_OPTION_NON_VOLATILE,
1671 KEY_SET_VALUE, NULL, &hKey, NULL ) != ERROR_SUCCESS )
1673 /* FIXME: Cannot create key */
1674 return;
1677 /* Save all of the settings */
1678 RegSetValueExW( hKey, reg_start_x, 0, REG_DWORD,
1679 (LPBYTE) &wi.rcWindow.left, sizeof(DWORD) );
1680 RegSetValueExW( hKey, reg_start_y, 0, REG_DWORD,
1681 (LPBYTE) &wi.rcWindow.top, sizeof(DWORD) );
1682 RegSetValueExW( hKey, reg_width, 0, REG_DWORD,
1683 (LPBYTE) &width, sizeof(DWORD) );
1684 RegSetValueExW( hKey, reg_height, 0, REG_DWORD,
1685 (LPBYTE) &height, sizeof(DWORD) );
1686 GetObject(Globals.hfont, sizeof(logfont), &logfont);
1687 RegSetValueExW( hKey, reg_logfont, 0, REG_BINARY,
1688 (LPBYTE) &logfont, sizeof(LOGFONT) );
1690 /* TODO: Save more settings here (List vs. Detailed View, etc.) */
1691 RegCloseKey( hKey );
1694 static void resize_frame_rect(HWND hwnd, PRECT prect)
1696 int new_top;
1697 RECT rt;
1699 if (IsWindowVisible(Globals.htoolbar)) {
1700 SendMessage(Globals.htoolbar, WM_SIZE, 0, 0);
1701 GetClientRect(Globals.htoolbar, &rt);
1702 prect->top = rt.bottom+3;
1703 prect->bottom -= rt.bottom+3;
1706 if (IsWindowVisible(Globals.hdrivebar)) {
1707 SendMessage(Globals.hdrivebar, WM_SIZE, 0, 0);
1708 GetClientRect(Globals.hdrivebar, &rt);
1709 new_top = --prect->top + rt.bottom+3;
1710 MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
1711 prect->top = new_top;
1712 prect->bottom -= rt.bottom+2;
1715 if (IsWindowVisible(Globals.hstatusbar)) {
1716 int parts[] = {300, 500};
1718 SendMessage(Globals.hstatusbar, WM_SIZE, 0, 0);
1719 SendMessage(Globals.hstatusbar, SB_SETPARTS, 2, (LPARAM)&parts);
1720 GetClientRect(Globals.hstatusbar, &rt);
1721 prect->bottom -= rt.bottom;
1724 MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1727 static void resize_frame(HWND hwnd, int cx, int cy)
1729 RECT rect;
1731 rect.left = 0;
1732 rect.top = 0;
1733 rect.right = cx;
1734 rect.bottom = cy;
1736 resize_frame_rect(hwnd, &rect);
1739 static void resize_frame_client(HWND hwnd)
1741 RECT rect;
1743 GetClientRect(hwnd, &rect);
1745 resize_frame_rect(hwnd, &rect);
1749 static HHOOK hcbthook;
1750 static ChildWnd* newchild = NULL;
1752 static LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
1754 if (code==HCBT_CREATEWND && newchild) {
1755 ChildWnd* child = newchild;
1756 newchild = NULL;
1758 child->hwnd = (HWND) wparam;
1759 SetWindowLongPtr(child->hwnd, GWLP_USERDATA, (LPARAM)child);
1762 return CallNextHookEx(hcbthook, code, wparam, lparam);
1765 static HWND create_child_window(ChildWnd* child)
1767 MDICREATESTRUCT mcs;
1768 int idx;
1770 mcs.szClass = sWINEFILETREE;
1771 mcs.szTitle = (LPTSTR)child->path;
1772 mcs.hOwner = Globals.hInstance;
1773 mcs.x = child->pos.rcNormalPosition.left;
1774 mcs.y = child->pos.rcNormalPosition.top;
1775 mcs.cx = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
1776 mcs.cy = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
1777 mcs.style = 0;
1778 mcs.lParam = 0;
1780 hcbthook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
1782 newchild = child;
1783 child->hwnd = (HWND) SendMessage(Globals.hmdiclient, WM_MDICREATE, 0, (LPARAM)&mcs);
1784 if (!child->hwnd) {
1785 UnhookWindowsHookEx(hcbthook);
1786 return 0;
1789 UnhookWindowsHookEx(hcbthook);
1791 SendMessage(child->left.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
1792 SendMessage(child->right.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
1794 idx = SendMessage(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
1795 SendMessage(child->left.hwnd, LB_SETCURSEL, idx, 0);
1797 return child->hwnd;
1801 struct ExecuteDialog {
1802 TCHAR cmd[MAX_PATH];
1803 int cmdshow;
1806 static INT_PTR CALLBACK ExecuteDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1808 static struct ExecuteDialog* dlg;
1810 switch(nmsg) {
1811 case WM_INITDIALOG:
1812 dlg = (struct ExecuteDialog*) lparam;
1813 return 1;
1815 case WM_COMMAND: {
1816 int id = (int)wparam;
1818 if (id == IDOK) {
1819 GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, MAX_PATH);
1820 dlg->cmdshow = get_check(hwnd,214) ? SW_SHOWMINIMIZED : SW_SHOWNORMAL;
1821 EndDialog(hwnd, id);
1822 } else if (id == IDCANCEL)
1823 EndDialog(hwnd, id);
1825 return 1;}
1828 return 0;
1832 static INT_PTR CALLBACK DestinationDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1834 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
1836 switch(nmsg) {
1837 case WM_INITDIALOG:
1838 SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
1839 SetWindowText(GetDlgItem(hwnd, 201), (LPCTSTR)lparam);
1840 return 1;
1842 case WM_COMMAND: {
1843 int id = (int)wparam;
1845 switch(id) {
1846 case IDOK: {
1847 LPTSTR dest = (LPTSTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1848 GetWindowText(GetDlgItem(hwnd, 201), dest, MAX_PATH);
1849 EndDialog(hwnd, id);
1850 break;}
1852 case IDCANCEL:
1853 EndDialog(hwnd, id);
1854 break;
1856 case 254:
1857 MessageBox(hwnd, RS(b1,IDS_NO_IMPL), RS(b2,IDS_WINEFILE), MB_OK);
1858 break;
1861 return 1;
1865 return 0;
1869 struct FilterDialog {
1870 TCHAR pattern[MAX_PATH];
1871 int flags;
1874 static INT_PTR CALLBACK FilterDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1876 static struct FilterDialog* dlg;
1878 switch(nmsg) {
1879 case WM_INITDIALOG:
1880 dlg = (struct FilterDialog*) lparam;
1881 SetWindowText(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern);
1882 set_check(hwnd, IDC_VIEW_TYPE_DIRECTORIES, dlg->flags&TF_DIRECTORIES);
1883 set_check(hwnd, IDC_VIEW_TYPE_PROGRAMS, dlg->flags&TF_PROGRAMS);
1884 set_check(hwnd, IDC_VIEW_TYPE_DOCUMENTS, dlg->flags&TF_DOCUMENTS);
1885 set_check(hwnd, IDC_VIEW_TYPE_OTHERS, dlg->flags&TF_OTHERS);
1886 set_check(hwnd, IDC_VIEW_TYPE_HIDDEN, dlg->flags&TF_HIDDEN);
1887 return 1;
1889 case WM_COMMAND: {
1890 int id = (int)wparam;
1892 if (id == IDOK) {
1893 int flags = 0;
1895 GetWindowText(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern, MAX_PATH);
1897 flags |= get_check(hwnd, IDC_VIEW_TYPE_DIRECTORIES) ? TF_DIRECTORIES : 0;
1898 flags |= get_check(hwnd, IDC_VIEW_TYPE_PROGRAMS) ? TF_PROGRAMS : 0;
1899 flags |= get_check(hwnd, IDC_VIEW_TYPE_DOCUMENTS) ? TF_DOCUMENTS : 0;
1900 flags |= get_check(hwnd, IDC_VIEW_TYPE_OTHERS) ? TF_OTHERS : 0;
1901 flags |= get_check(hwnd, IDC_VIEW_TYPE_HIDDEN) ? TF_HIDDEN : 0;
1903 dlg->flags = flags;
1905 EndDialog(hwnd, id);
1906 } else if (id == IDCANCEL)
1907 EndDialog(hwnd, id);
1909 return 1;}
1912 return 0;
1916 struct PropertiesDialog {
1917 TCHAR path[MAX_PATH];
1918 Entry entry;
1919 void* pVersionData;
1922 /* Structure used to store enumerated languages and code pages. */
1923 struct LANGANDCODEPAGE {
1924 WORD wLanguage;
1925 WORD wCodePage;
1926 } *lpTranslate;
1928 static LPCSTR InfoStrings[] = {
1929 "Comments",
1930 "CompanyName",
1931 "FileDescription",
1932 "FileVersion",
1933 "InternalName",
1934 "LegalCopyright",
1935 "LegalTrademarks",
1936 "OriginalFilename",
1937 "PrivateBuild",
1938 "ProductName",
1939 "ProductVersion",
1940 "SpecialBuild",
1941 NULL
1944 static void PropDlg_DisplayValue(HWND hlbox, HWND hedit)
1946 int idx = SendMessage(hlbox, LB_GETCURSEL, 0, 0);
1948 if (idx != LB_ERR) {
1949 LPCTSTR pValue = (LPCTSTR) SendMessage(hlbox, LB_GETITEMDATA, idx, 0);
1951 if (pValue)
1952 SetWindowText(hedit, pValue);
1956 static void CheckForFileInfo(struct PropertiesDialog* dlg, HWND hwnd, LPCTSTR strFilename)
1958 static TCHAR sBackSlash[] = {'\\','\0'};
1959 static TCHAR sTranslation[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n','\0'};
1960 static TCHAR sStringFileInfo[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\',
1961 '%','0','4','x','%','0','4','x','\\','%','s','\0'};
1962 DWORD dwVersionDataLen = GetFileVersionInfoSize(strFilename, NULL);
1964 if (dwVersionDataLen) {
1965 dlg->pVersionData = HeapAlloc(GetProcessHeap(), 0, dwVersionDataLen);
1967 if (GetFileVersionInfo(strFilename, 0, dwVersionDataLen, dlg->pVersionData)) {
1968 LPVOID pVal;
1969 UINT nValLen;
1971 if (VerQueryValue(dlg->pVersionData, sBackSlash, &pVal, &nValLen)) {
1972 if (nValLen == sizeof(VS_FIXEDFILEINFO)) {
1973 VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)pVal;
1974 char buffer[BUFFER_LEN];
1976 sprintf(buffer, "%d.%d.%d.%d",
1977 HIWORD(pFixedFileInfo->dwFileVersionMS), LOWORD(pFixedFileInfo->dwFileVersionMS),
1978 HIWORD(pFixedFileInfo->dwFileVersionLS), LOWORD(pFixedFileInfo->dwFileVersionLS));
1980 SetDlgItemTextA(hwnd, IDC_STATIC_PROP_VERSION, buffer);
1984 /* Read the list of languages and code pages. */
1985 if (VerQueryValue(dlg->pVersionData, sTranslation, &pVal, &nValLen)) {
1986 struct LANGANDCODEPAGE* pTranslate = (struct LANGANDCODEPAGE*)pVal;
1987 struct LANGANDCODEPAGE* pEnd = (struct LANGANDCODEPAGE*)((LPBYTE)pVal+nValLen);
1989 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
1991 /* Read the file description for each language and code page. */
1992 for(; pTranslate<pEnd; ++pTranslate) {
1993 LPCSTR* p;
1995 for(p=InfoStrings; *p; ++p) {
1996 TCHAR subblock[200];
1997 #ifdef UNICODE
1998 TCHAR infoStr[100];
1999 #endif
2000 LPCTSTR pTxt;
2001 UINT nValLen;
2003 LPCSTR pInfoString = *p;
2004 #ifdef UNICODE
2005 MultiByteToWideChar(CP_ACP, 0, pInfoString, -1, infoStr, 100);
2006 #else
2007 #define infoStr pInfoString
2008 #endif
2009 wsprintf(subblock, sStringFileInfo, pTranslate->wLanguage, pTranslate->wCodePage, infoStr);
2011 /* Retrieve file description for language and code page */
2012 if (VerQueryValue(dlg->pVersionData, subblock, (PVOID)&pTxt, &nValLen)) {
2013 int idx = SendMessage(hlbox, LB_ADDSTRING, 0L, (LPARAM)infoStr);
2014 SendMessage(hlbox, LB_SETITEMDATA, idx, (LPARAM) pTxt);
2019 SendMessage(hlbox, LB_SETCURSEL, 0, 0);
2021 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
2027 static INT_PTR CALLBACK PropertiesDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2029 static struct PropertiesDialog* dlg;
2031 switch(nmsg) {
2032 case WM_INITDIALOG: {
2033 static const TCHAR sByteFmt[] = {'%','s',' ','B','y','t','e','s','\0'};
2034 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2035 LPWIN32_FIND_DATA pWFD;
2036 ULONGLONG size;
2038 dlg = (struct PropertiesDialog*) lparam;
2039 pWFD = (LPWIN32_FIND_DATA) &dlg->entry.data;
2041 GetWindowText(hwnd, b1, MAX_PATH);
2042 wsprintf(b2, b1, pWFD->cFileName);
2043 SetWindowText(hwnd, b2);
2045 format_date(&pWFD->ftLastWriteTime, b1, COL_DATE|COL_TIME);
2046 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_LASTCHANGE), b1);
2048 size = ((ULONGLONG)pWFD->nFileSizeHigh << 32) | pWFD->nFileSizeLow;
2049 _stprintf(b1, sLongNumFmt, size);
2050 wsprintf(b2, sByteFmt, b1);
2051 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_SIZE), b2);
2053 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_FILENAME), pWFD->cFileName);
2054 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_PATH), dlg->path);
2056 set_check(hwnd, IDC_CHECK_READONLY, pWFD->dwFileAttributes&FILE_ATTRIBUTE_READONLY);
2057 set_check(hwnd, IDC_CHECK_ARCHIVE, pWFD->dwFileAttributes&FILE_ATTRIBUTE_ARCHIVE);
2058 set_check(hwnd, IDC_CHECK_COMPRESSED, pWFD->dwFileAttributes&FILE_ATTRIBUTE_COMPRESSED);
2059 set_check(hwnd, IDC_CHECK_HIDDEN, pWFD->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN);
2060 set_check(hwnd, IDC_CHECK_SYSTEM, pWFD->dwFileAttributes&FILE_ATTRIBUTE_SYSTEM);
2062 CheckForFileInfo(dlg, hwnd, dlg->path);
2063 return 1;}
2065 case WM_COMMAND: {
2066 int id = (int)wparam;
2068 switch(HIWORD(wparam)) {
2069 case LBN_SELCHANGE: {
2070 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
2071 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
2072 break;
2075 case BN_CLICKED:
2076 if (id==IDOK || id==IDCANCEL)
2077 EndDialog(hwnd, id);
2080 return 1;}
2082 case WM_NCDESTROY:
2083 HeapFree(GetProcessHeap(), 0, dlg->pVersionData);
2084 dlg->pVersionData = NULL;
2085 break;
2088 return 0;
2091 static void show_properties_dlg(Entry* entry, HWND hwnd)
2093 struct PropertiesDialog dlg;
2095 memset(&dlg, 0, sizeof(struct PropertiesDialog));
2096 get_path(entry, dlg.path);
2097 memcpy(&dlg.entry, entry, sizeof(Entry));
2099 DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_DIALOG_PROPERTIES), hwnd, PropertiesDialogDlgProc, (LPARAM)&dlg);
2103 #ifndef _NO_EXTENSIONS
2105 static struct FullScreenParameters {
2106 BOOL mode;
2107 RECT orgPos;
2108 BOOL wasZoomed;
2109 } g_fullscreen = {
2110 FALSE, /* mode */
2111 {0, 0, 0, 0},
2112 FALSE
2115 static void frame_get_clientspace(HWND hwnd, PRECT prect)
2117 RECT rt;
2119 if (!IsIconic(hwnd))
2120 GetClientRect(hwnd, prect);
2121 else {
2122 WINDOWPLACEMENT wp;
2124 GetWindowPlacement(hwnd, &wp);
2126 prect->left = prect->top = 0;
2127 prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
2128 2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
2129 prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
2130 2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
2131 GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
2134 if (IsWindowVisible(Globals.htoolbar)) {
2135 GetClientRect(Globals.htoolbar, &rt);
2136 prect->top += rt.bottom+2;
2139 if (IsWindowVisible(Globals.hdrivebar)) {
2140 GetClientRect(Globals.hdrivebar, &rt);
2141 prect->top += rt.bottom+2;
2144 if (IsWindowVisible(Globals.hstatusbar)) {
2145 GetClientRect(Globals.hstatusbar, &rt);
2146 prect->bottom -= rt.bottom;
2150 static BOOL toggle_fullscreen(HWND hwnd)
2152 RECT rt;
2154 if ((g_fullscreen.mode=!g_fullscreen.mode)) {
2155 GetWindowRect(hwnd, &g_fullscreen.orgPos);
2156 g_fullscreen.wasZoomed = IsZoomed(hwnd);
2158 Frame_CalcFrameClient(hwnd, &rt);
2159 ClientToScreen(hwnd, (LPPOINT)&rt.left);
2160 ClientToScreen(hwnd, (LPPOINT)&rt.right);
2162 rt.left = g_fullscreen.orgPos.left-rt.left;
2163 rt.top = g_fullscreen.orgPos.top-rt.top;
2164 rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
2165 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
2167 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2168 } else {
2169 MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
2170 g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
2171 g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
2173 if (g_fullscreen.wasZoomed)
2174 ShowWindow(hwnd, WS_MAXIMIZE);
2177 return g_fullscreen.mode;
2180 static void fullscreen_move(HWND hwnd)
2182 RECT rt, pos;
2183 GetWindowRect(hwnd, &pos);
2185 Frame_CalcFrameClient(hwnd, &rt);
2186 ClientToScreen(hwnd, (LPPOINT)&rt.left);
2187 ClientToScreen(hwnd, (LPPOINT)&rt.right);
2189 rt.left = pos.left-rt.left;
2190 rt.top = pos.top-rt.top;
2191 rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
2192 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
2194 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2197 #endif
2200 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
2202 BOOL vis = IsWindowVisible(hchild);
2204 CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
2206 ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
2208 #ifndef _NO_EXTENSIONS
2209 if (g_fullscreen.mode)
2210 fullscreen_move(hwnd);
2211 #endif
2213 resize_frame_client(hwnd);
2216 static BOOL activate_drive_window(LPCTSTR path)
2218 TCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
2219 HWND child_wnd;
2221 _tsplitpath(path, drv1, 0, 0, 0);
2223 /* search for a already open window for the same drive */
2224 for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2225 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(child_wnd, GWLP_USERDATA);
2227 if (child) {
2228 _tsplitpath(child->root.path, drv2, 0, 0, 0);
2230 if (!lstrcmpi(drv2, drv1)) {
2231 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2233 if (IsIconic(child_wnd))
2234 ShowWindow(child_wnd, SW_SHOWNORMAL);
2236 return TRUE;
2241 return FALSE;
2244 static BOOL activate_fs_window(LPCTSTR filesys)
2246 HWND child_wnd;
2248 /* search for a already open window of the given file system name */
2249 for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2250 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(child_wnd, GWLP_USERDATA);
2252 if (child) {
2253 if (!lstrcmpi(child->root.fs, filesys)) {
2254 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2256 if (IsIconic(child_wnd))
2257 ShowWindow(child_wnd, SW_SHOWNORMAL);
2259 return TRUE;
2264 return FALSE;
2267 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2269 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2271 switch(nmsg) {
2272 case WM_CLOSE:
2273 if (Globals.saveSettings)
2274 save_registry_settings();
2276 DestroyWindow(hwnd);
2278 /* clear handle variables */
2279 Globals.hMenuFrame = 0;
2280 Globals.hMenuView = 0;
2281 Globals.hMenuOptions = 0;
2282 Globals.hMainWnd = 0;
2283 Globals.hmdiclient = 0;
2284 Globals.hdrivebar = 0;
2285 break;
2287 case WM_DESTROY:
2288 PostQuitMessage(0);
2289 break;
2291 case WM_INITMENUPOPUP: {
2292 HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2294 if (!SendMessage(hwndClient, WM_INITMENUPOPUP, wparam, lparam))
2295 return 0;
2296 break;}
2298 case WM_COMMAND: {
2299 UINT cmd = LOWORD(wparam);
2300 HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2302 if (SendMessage(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
2303 break;
2305 if (cmd>=ID_DRIVE_FIRST && cmd<=ID_DRIVE_FIRST+0xFF) {
2306 TCHAR drv[_MAX_DRIVE], path[MAX_PATH];
2307 ChildWnd* child;
2308 LPCTSTR root = Globals.drives;
2309 int i;
2311 for(i=cmd-ID_DRIVE_FIRST; i--; root++)
2312 while(*root)
2313 root++;
2315 if (activate_drive_window(root))
2316 return 0;
2318 _tsplitpath(root, drv, 0, 0, 0);
2320 if (!SetCurrentDirectory(drv)) {
2321 display_error(hwnd, GetLastError());
2322 return 0;
2325 GetCurrentDirectory(MAX_PATH, path); /*TODO: store last directory per drive */
2326 child = alloc_child_window(path, NULL, hwnd);
2328 if (!create_child_window(child))
2329 HeapFree(GetProcessHeap(), 0, child);
2330 } else switch(cmd) {
2331 case ID_FILE_EXIT:
2332 SendMessage(hwnd, WM_CLOSE, 0, 0);
2333 break;
2335 case ID_WINDOW_NEW: {
2336 TCHAR path[MAX_PATH];
2337 ChildWnd* child;
2339 GetCurrentDirectory(MAX_PATH, path);
2340 child = alloc_child_window(path, NULL, hwnd);
2342 if (!create_child_window(child))
2343 HeapFree(GetProcessHeap(), 0, child);
2344 break;}
2346 case ID_REFRESH:
2347 refresh_drives();
2348 break;
2350 case ID_WINDOW_CASCADE:
2351 SendMessage(Globals.hmdiclient, WM_MDICASCADE, 0, 0);
2352 break;
2354 case ID_WINDOW_TILE_HORZ:
2355 SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
2356 break;
2358 case ID_WINDOW_TILE_VERT:
2359 SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_VERTICAL, 0);
2360 break;
2362 case ID_WINDOW_ARRANGE:
2363 SendMessage(Globals.hmdiclient, WM_MDIICONARRANGE, 0, 0);
2364 break;
2366 case ID_SELECT_FONT:
2367 choose_font(hwnd);
2368 break;
2370 case ID_VIEW_TOOL_BAR:
2371 toggle_child(hwnd, cmd, Globals.htoolbar);
2372 break;
2374 case ID_VIEW_DRIVE_BAR:
2375 toggle_child(hwnd, cmd, Globals.hdrivebar);
2376 break;
2378 case ID_VIEW_STATUSBAR:
2379 toggle_child(hwnd, cmd, Globals.hstatusbar);
2380 break;
2382 case ID_VIEW_SAVESETTINGS:
2383 Globals.saveSettings = !Globals.saveSettings;
2384 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_SAVESETTINGS,
2385 Globals.saveSettings ? MF_CHECKED : MF_UNCHECKED );
2386 break;
2388 case ID_EXECUTE: {
2389 struct ExecuteDialog dlg;
2391 memset(&dlg, 0, sizeof(struct ExecuteDialog));
2393 if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_EXECUTE), hwnd, ExecuteDialogDlgProc, (LPARAM)&dlg) == IDOK) {
2394 HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, dlg.cmd/*file*/, NULL/*parameters*/, NULL/*dir*/, dlg.cmdshow);
2396 if (PtrToUlong(hinst) <= 32)
2397 display_error(hwnd, GetLastError());
2399 break;}
2401 case ID_CONNECT_NETWORK_DRIVE: {
2402 DWORD ret = WNetConnectionDialog(hwnd, RESOURCETYPE_DISK);
2403 if (ret == NO_ERROR)
2404 refresh_drives();
2405 else if (ret != (DWORD)-1) {
2406 if (ret == ERROR_EXTENDED_ERROR)
2407 display_network_error(hwnd);
2408 else
2409 display_error(hwnd, ret);
2411 break;}
2413 case ID_DISCONNECT_NETWORK_DRIVE: {
2414 DWORD ret = WNetDisconnectDialog(hwnd, RESOURCETYPE_DISK);
2415 if (ret == NO_ERROR)
2416 refresh_drives();
2417 else if (ret != (DWORD)-1) {
2418 if (ret == ERROR_EXTENDED_ERROR)
2419 display_network_error(hwnd);
2420 else
2421 display_error(hwnd, ret);
2423 break;}
2425 case ID_FORMAT_DISK: {
2426 UINT sem_org = SetErrorMode(0); /* Get the current Error Mode settings. */
2427 SetErrorMode(sem_org & ~SEM_FAILCRITICALERRORS); /* Force O/S to handle */
2428 SHFormatDrive(hwnd, 0 /* A: */, SHFMT_ID_DEFAULT, 0);
2429 SetErrorMode(sem_org); /* Put it back the way it was. */
2430 break;}
2432 case ID_HELP:
2433 WinHelp(hwnd, RS(b1,IDS_WINEFILE), HELP_INDEX, 0);
2434 break;
2436 #ifndef _NO_EXTENSIONS
2437 case ID_VIEW_FULLSCREEN:
2438 CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
2439 break;
2441 #ifdef __WINE__
2442 case ID_DRIVE_UNIX_FS: {
2443 TCHAR path[MAX_PATH];
2444 #ifdef UNICODE
2445 char cpath[MAX_PATH];
2446 #endif
2447 ChildWnd* child;
2449 if (activate_fs_window(RS(b1,IDS_UNIXFS)))
2450 break;
2452 #ifdef UNICODE
2453 getcwd(cpath, MAX_PATH);
2454 MultiByteToWideChar(CP_UNIXCP, 0, cpath, -1, path, MAX_PATH);
2455 #else
2456 getcwd(path, MAX_PATH);
2457 #endif
2458 child = alloc_child_window(path, NULL, hwnd);
2460 if (!create_child_window(child))
2461 HeapFree(GetProcessHeap(), 0, child);
2462 break;}
2463 #endif
2464 #ifdef _SHELL_FOLDERS
2465 case ID_DRIVE_SHELL_NS: {
2466 TCHAR path[MAX_PATH];
2467 ChildWnd* child;
2469 if (activate_fs_window(RS(b1,IDS_SHELL)))
2470 break;
2472 GetCurrentDirectory(MAX_PATH, path);
2473 child = alloc_child_window(path, get_path_pidl(path,hwnd), hwnd);
2475 if (!create_child_window(child))
2476 HeapFree(GetProcessHeap(), 0, child);
2477 break;}
2478 #endif
2479 #endif
2481 /*TODO: There are even more menu items! */
2483 case ID_ABOUT:
2484 ShellAbout(hwnd, RS(b1,IDS_WINEFILE), NULL,
2485 LoadImage( Globals.hInstance, MAKEINTRESOURCE(IDI_WINEFILE),
2486 IMAGE_ICON, 48, 48, LR_SHARED ));
2487 break;
2489 default:
2490 /*TODO: if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
2491 STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
2492 else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
2493 (cmd<SC_SIZE || cmd>SC_RESTORE))
2494 MessageBox(hwnd, RS(b2,IDS_NO_IMPL), RS(b1,IDS_WINEFILE), MB_OK);
2496 return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2498 break;}
2500 case WM_SIZE:
2501 resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
2502 break; /* do not pass message to DefFrameProc */
2504 case WM_DEVICECHANGE:
2505 SendMessage(hwnd, WM_COMMAND, MAKELONG(ID_REFRESH,0), 0);
2506 break;
2508 #ifndef _NO_EXTENSIONS
2509 case WM_GETMINMAXINFO: {
2510 LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
2512 lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
2513 lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
2514 break;}
2516 case FRM_CALC_CLIENT:
2517 frame_get_clientspace(hwnd, (PRECT)lparam);
2518 return TRUE;
2519 #endif /* _NO_EXTENSIONS */
2521 default:
2522 return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2525 return 0;
2529 static TCHAR g_pos_names[COLUMNS][20] = {
2530 {'\0'} /* symbol */
2533 static const int g_pos_align[] = {
2535 HDF_LEFT, /* Name */
2536 HDF_RIGHT, /* Size */
2537 HDF_LEFT, /* CDate */
2538 #ifndef _NO_EXTENSIONS
2539 HDF_LEFT, /* ADate */
2540 HDF_LEFT, /* MDate */
2541 HDF_LEFT, /* Index */
2542 HDF_CENTER, /* Links */
2543 #endif
2544 HDF_CENTER, /* Attributes */
2545 #ifndef _NO_EXTENSIONS
2546 HDF_LEFT /* Security */
2547 #endif
2550 static void resize_tree(ChildWnd* child, int cx, int cy)
2552 HDWP hdwp = BeginDeferWindowPos(4);
2553 RECT rt;
2555 rt.left = 0;
2556 rt.top = 0;
2557 rt.right = cx;
2558 rt.bottom = cy;
2560 cx = child->split_pos + SPLIT_WIDTH/2;
2562 #ifndef _NO_EXTENSIONS
2564 WINDOWPOS wp;
2565 HD_LAYOUT hdl;
2567 hdl.prc = &rt;
2568 hdl.pwpos = &wp;
2570 SendMessage(child->left.hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hdl);
2572 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
2573 wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
2574 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
2575 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
2577 #endif /* _NO_EXTENSIONS */
2579 DeferWindowPos(hdwp, child->left.hwnd, 0, rt.left, rt.top, child->split_pos-SPLIT_WIDTH/2-rt.left, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
2580 DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
2582 EndDeferWindowPos(hdwp);
2586 #ifndef _NO_EXTENSIONS
2588 static HWND create_header(HWND parent, Pane* pane, UINT id)
2590 HD_ITEM hdi;
2591 int idx;
2593 HWND hwnd = CreateWindow(WC_HEADER, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ|HDS_FULLDRAG/*TODO: |HDS_BUTTONS + sort orders*/,
2594 0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2595 if (!hwnd)
2596 return 0;
2598 SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), FALSE);
2600 hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
2602 for(idx=0; idx<COLUMNS; idx++) {
2603 hdi.pszText = g_pos_names[idx];
2604 hdi.fmt = HDF_STRING | g_pos_align[idx];
2605 hdi.cxy = pane->widths[idx];
2606 SendMessage(hwnd, HDM_INSERTITEM, idx, (LPARAM) &hdi);
2609 return hwnd;
2612 #endif /* _NO_EXTENSIONS */
2615 static void init_output(HWND hwnd)
2617 static const WCHAR s1000[] = {'1','0','0','0','\0'};
2618 WCHAR b[16];
2619 HFONT old_font;
2620 HDC hdc = GetDC(hwnd);
2622 if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, s1000, 0, b, 16) > 4)
2623 Globals.num_sep = b[1];
2624 else
2625 Globals.num_sep = '.';
2627 old_font = SelectObject(hdc, Globals.hfont);
2628 GetTextExtentPoint32W(hdc, sSpace, 1, &Globals.spaceSize);
2629 SelectObject(hdc, old_font);
2630 ReleaseDC(hwnd, hdc);
2633 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
2636 /* calculate preferred width for all visible columns */
2638 static BOOL calc_widths(Pane* pane, BOOL anyway)
2640 int col, x, cx, spc=3*Globals.spaceSize.cx;
2641 int entries = SendMessage(pane->hwnd, LB_GETCOUNT, 0, 0);
2642 int orgWidths[COLUMNS];
2643 int orgPositions[COLUMNS+1];
2644 HFONT hfontOld;
2645 HDC hdc;
2646 int cnt;
2648 if (!anyway) {
2649 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2650 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2653 for(col=0; col<COLUMNS; col++)
2654 pane->widths[col] = 0;
2656 hdc = GetDC(pane->hwnd);
2657 hfontOld = SelectObject(hdc, Globals.hfont);
2659 for(cnt=0; cnt<entries; cnt++) {
2660 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2662 DRAWITEMSTRUCT dis;
2664 dis.CtlType = 0;
2665 dis.CtlID = 0;
2666 dis.itemID = 0;
2667 dis.itemAction = 0;
2668 dis.itemState = 0;
2669 dis.hwndItem = pane->hwnd;
2670 dis.hDC = hdc;
2671 dis.rcItem.left = 0;
2672 dis.rcItem.top = 0;
2673 dis.rcItem.right = 0;
2674 dis.rcItem.bottom = 0;
2675 /*dis.itemData = 0; */
2677 draw_item(pane, &dis, entry, COLUMNS);
2680 SelectObject(hdc, hfontOld);
2681 ReleaseDC(pane->hwnd, hdc);
2683 x = 0;
2684 for(col=0; col<COLUMNS; col++) {
2685 pane->positions[col] = x;
2686 cx = pane->widths[col];
2688 if (cx) {
2689 cx += spc;
2691 if (cx < IMAGE_WIDTH)
2692 cx = IMAGE_WIDTH;
2694 pane->widths[col] = cx;
2697 x += cx;
2700 pane->positions[COLUMNS] = x;
2702 SendMessage(pane->hwnd, LB_SETHORIZONTALEXTENT, x, 0);
2704 /* no change? */
2705 if (!anyway && !memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2706 return FALSE;
2708 /* don't move, if only collapsing an entry */
2709 if (!anyway && pane->widths[0]<orgWidths[0] &&
2710 !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
2711 pane->widths[0] = orgWidths[0];
2712 memcpy(pane->positions, orgPositions, sizeof(orgPositions));
2714 return FALSE;
2717 InvalidateRect(pane->hwnd, 0, TRUE);
2719 return TRUE;
2723 /* calculate one preferred column width */
2725 static void calc_single_width(Pane* pane, int col)
2727 HFONT hfontOld;
2728 int x, cx;
2729 int entries = SendMessage(pane->hwnd, LB_GETCOUNT, 0, 0);
2730 int cnt;
2731 HDC hdc;
2733 pane->widths[col] = 0;
2735 hdc = GetDC(pane->hwnd);
2736 hfontOld = SelectObject(hdc, Globals.hfont);
2738 for(cnt=0; cnt<entries; cnt++) {
2739 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2740 DRAWITEMSTRUCT dis;
2742 dis.CtlType = 0;
2743 dis.CtlID = 0;
2744 dis.itemID = 0;
2745 dis.itemAction = 0;
2746 dis.itemState = 0;
2747 dis.hwndItem = pane->hwnd;
2748 dis.hDC = hdc;
2749 dis.rcItem.left = 0;
2750 dis.rcItem.top = 0;
2751 dis.rcItem.right = 0;
2752 dis.rcItem.bottom = 0;
2753 /*dis.itemData = 0; */
2755 draw_item(pane, &dis, entry, col);
2758 SelectObject(hdc, hfontOld);
2759 ReleaseDC(pane->hwnd, hdc);
2761 cx = pane->widths[col];
2763 if (cx) {
2764 cx += 3*Globals.spaceSize.cx;
2766 if (cx < IMAGE_WIDTH)
2767 cx = IMAGE_WIDTH;
2770 pane->widths[col] = cx;
2772 x = pane->positions[col] + cx;
2774 for(; col<COLUMNS; ) {
2775 pane->positions[++col] = x;
2776 x += pane->widths[col];
2779 SendMessage(pane->hwnd, LB_SETHORIZONTALEXTENT, x, 0);
2783 static BOOL pattern_match(LPCTSTR str, LPCTSTR pattern)
2785 for( ; *str&&*pattern; str++,pattern++) {
2786 if (*pattern == '*') {
2787 do pattern++;
2788 while(*pattern == '*');
2790 if (!*pattern)
2791 return TRUE;
2793 for(; *str; str++)
2794 if (*str==*pattern && pattern_match(str, pattern))
2795 return TRUE;
2797 return FALSE;
2799 else if (*str!=*pattern && *pattern!='?')
2800 return FALSE;
2803 if (*str || *pattern)
2804 if (*pattern!='*' || pattern[1]!='\0')
2805 return FALSE;
2807 return TRUE;
2810 static BOOL pattern_imatch(LPCTSTR str, LPCTSTR pattern)
2812 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2814 lstrcpy(b1, str);
2815 lstrcpy(b2, pattern);
2816 CharUpper(b1);
2817 CharUpper(b2);
2819 return pattern_match(b1, b2);
2823 enum FILE_TYPE {
2824 FT_OTHER = 0,
2825 FT_EXECUTABLE = 1,
2826 FT_DOCUMENT = 2
2829 static enum FILE_TYPE get_file_type(LPCTSTR filename);
2832 /* insert listbox entries after index idx */
2834 static int insert_entries(Pane* pane, Entry* dir, LPCTSTR pattern, int filter_flags, int idx)
2836 Entry* entry = dir;
2838 if (!entry)
2839 return idx;
2841 ShowWindow(pane->hwnd, SW_HIDE);
2843 for(; entry; entry=entry->next) {
2844 #ifndef _LEFT_FILES
2845 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2846 continue;
2847 #endif
2849 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2850 /* don't display entries "." and ".." in the left pane */
2851 if (pane->treePane && entry->data.cFileName[0] == '.')
2852 if (
2853 #ifndef _NO_EXTENSIONS
2854 entry->data.cFileName[1] == '\0' ||
2855 #endif
2856 (entry->data.cFileName[1] == '.' && entry->data.cFileName[2] == '\0'))
2857 continue;
2859 /* filter directories in right pane */
2860 if (!pane->treePane && !(filter_flags&TF_DIRECTORIES))
2861 continue;
2864 /* filter using the file name pattern */
2865 if (pattern)
2866 if (!pattern_imatch(entry->data.cFileName, pattern))
2867 continue;
2869 /* filter system and hidden files */
2870 if (!(filter_flags&TF_HIDDEN) && (entry->data.dwFileAttributes&(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)))
2871 continue;
2873 /* filter looking at the file type */
2874 if ((filter_flags&(TF_PROGRAMS|TF_DOCUMENTS|TF_OTHERS)) != (TF_PROGRAMS|TF_DOCUMENTS|TF_OTHERS))
2875 switch(get_file_type(entry->data.cFileName)) {
2876 case FT_EXECUTABLE:
2877 if (!(filter_flags & TF_PROGRAMS))
2878 continue;
2879 break;
2881 case FT_DOCUMENT:
2882 if (!(filter_flags & TF_DOCUMENTS))
2883 continue;
2884 break;
2886 default: /* TF_OTHERS */
2887 if (!(filter_flags & TF_OTHERS))
2888 continue;
2891 if (idx != -1)
2892 idx++;
2894 SendMessage(pane->hwnd, LB_INSERTSTRING, idx, (LPARAM) entry);
2896 if (pane->treePane && entry->expanded)
2897 idx = insert_entries(pane, entry->down, pattern, filter_flags, idx);
2900 ShowWindow(pane->hwnd, SW_SHOW);
2902 return idx;
2906 static void format_bytes(LPTSTR buffer, LONGLONG bytes)
2908 static const TCHAR sFmtGB[] = {'%', '.', '1', 'f', ' ', 'G', 'B', '\0'};
2909 static const TCHAR sFmtMB[] = {'%', '.', '1', 'f', ' ', 'M', 'B', '\0'};
2910 static const TCHAR sFmtkB[] = {'%', '.', '1', 'f', ' ', 'k', 'B', '\0'};
2912 float fBytes = (float)bytes;
2914 if (bytes >= 1073741824) /* 1 GB */
2915 _stprintf(buffer, sFmtGB, fBytes/1073741824.f+.5f);
2916 else if (bytes >= 1048576) /* 1 MB */
2917 _stprintf(buffer, sFmtMB, fBytes/1048576.f+.5f);
2918 else if (bytes >= 1024) /* 1 kB */
2919 _stprintf(buffer, sFmtkB, fBytes/1024.f+.5f);
2920 else
2921 _stprintf(buffer, sLongNumFmt, bytes);
2924 static void set_space_status(void)
2926 ULARGE_INTEGER ulFreeBytesToCaller, ulTotalBytes, ulFreeBytes;
2927 TCHAR fmt[64], b1[64], b2[64], buffer[BUFFER_LEN];
2929 if (GetDiskFreeSpaceEx(NULL, &ulFreeBytesToCaller, &ulTotalBytes, &ulFreeBytes)) {
2930 format_bytes(b1, ulFreeBytesToCaller.QuadPart);
2931 format_bytes(b2, ulTotalBytes.QuadPart);
2932 wsprintf(buffer, RS(fmt,IDS_FREE_SPACE_FMT), b1, b2);
2933 } else
2934 lstrcpy(buffer, sQMarks);
2936 SendMessage(Globals.hstatusbar, SB_SETTEXT, 0, (LPARAM)buffer);
2940 static WNDPROC g_orgTreeWndProc;
2942 static void create_tree_window(HWND parent, Pane* pane, UINT id, UINT id_header, LPCTSTR pattern, int filter_flags)
2944 static const TCHAR sListBox[] = {'L','i','s','t','B','o','x','\0'};
2946 static int s_init = 0;
2947 Entry* entry = pane->root;
2949 pane->hwnd = CreateWindow(sListBox, sEmpty, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
2950 LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
2951 0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2953 SetWindowLongPtr(pane->hwnd, GWLP_USERDATA, (LPARAM)pane);
2954 g_orgTreeWndProc = (WNDPROC) SetWindowLongPtr(pane->hwnd, GWLP_WNDPROC, (LPARAM)TreeWndProc);
2956 SendMessage(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2958 /* insert entries into listbox */
2959 if (entry)
2960 insert_entries(pane, entry, pattern, filter_flags, -1);
2962 /* calculate column widths */
2963 if (!s_init) {
2964 s_init = 1;
2965 init_output(pane->hwnd);
2968 calc_widths(pane, TRUE);
2970 #ifndef _NO_EXTENSIONS
2971 pane->hwndHeader = create_header(parent, pane, id_header);
2972 #endif
2976 static void InitChildWindow(ChildWnd* child)
2978 create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT, NULL, TF_ALL);
2979 create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, child->filter_pattern, child->filter_flags);
2983 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
2985 SYSTEMTIME systime;
2986 FILETIME lft;
2987 int len = 0;
2989 *buffer = '\0';
2991 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2992 return;
2994 if (!FileTimeToLocalFileTime(ft, &lft))
2995 {err: lstrcpy(buffer,sQMarks); return;}
2997 if (!FileTimeToSystemTime(&lft, &systime))
2998 goto err;
3000 if (visible_cols & COL_DATE) {
3001 len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
3002 if (!len)
3003 goto err;
3006 if (visible_cols & COL_TIME) {
3007 if (len)
3008 buffer[len-1] = ' ';
3010 buffer[len++] = ' ';
3012 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
3013 buffer[len] = '\0';
3018 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3020 RECT rt = {0, 0, 0, 0};
3022 DrawText(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
3024 if (rt.right > pane->widths[col])
3025 pane->widths[col] = rt.right;
3028 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3030 RECT rt = {0, 0, 0, 0};
3032 /* DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
3033 DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
3035 DrawText(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
3036 /*FIXME rt (0,0) ??? */
3038 if (rt.right > pane->widths[col])
3039 pane->widths[col] = rt.right;
3043 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str, DWORD flags)
3045 int x = dis->rcItem.left;
3046 RECT rt;
3048 rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
3049 rt.top = dis->rcItem.top;
3050 rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
3051 rt.bottom = dis->rcItem.bottom;
3053 DrawText(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
3056 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3058 int x = dis->rcItem.left;
3059 RECT rt;
3061 rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
3062 rt.top = dis->rcItem.top;
3063 rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
3064 rt.bottom = dis->rcItem.bottom;
3066 /* DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
3067 DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
3069 DrawText(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
3072 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3074 int x = dis->rcItem.left;
3075 RECT rt;
3076 LPCTSTR s = str;
3077 TCHAR b[128];
3078 LPTSTR d = b;
3079 int pos;
3081 rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
3082 rt.top = dis->rcItem.top;
3083 rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
3084 rt.bottom = dis->rcItem.bottom;
3086 if (*s)
3087 *d++ = *s++;
3089 /* insert number separator characters */
3090 pos = lstrlen(s) % 3;
3092 while(*s)
3093 if (pos--)
3094 *d++ = *s++;
3095 else {
3096 *d++ = Globals.num_sep;
3097 pos = 3;
3100 DrawText(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
3104 static BOOL is_exe_file(LPCTSTR ext)
3106 static const TCHAR executable_extensions[][4] = {
3107 {'C','O','M','\0'},
3108 {'E','X','E','\0'},
3109 {'B','A','T','\0'},
3110 {'C','M','D','\0'},
3111 #ifndef _NO_EXTENSIONS
3112 {'C','M','M','\0'},
3113 {'B','T','M','\0'},
3114 {'A','W','K','\0'},
3115 #endif /* _NO_EXTENSIONS */
3116 {'\0'}
3119 TCHAR ext_buffer[_MAX_EXT];
3120 const TCHAR (*p)[4];
3121 LPCTSTR s;
3122 LPTSTR d;
3124 for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
3125 d++;
3127 for(p=executable_extensions; (*p)[0]; p++)
3128 if (!lstrcmpi(ext_buffer, *p))
3129 return TRUE;
3131 return FALSE;
3134 static BOOL is_registered_type(LPCTSTR ext)
3136 /* check if there exists a classname for this file extension in the registry */
3137 if (!RegQueryValue(HKEY_CLASSES_ROOT, ext, NULL, NULL))
3138 return TRUE;
3140 return FALSE;
3143 static enum FILE_TYPE get_file_type(LPCTSTR filename)
3145 LPCTSTR ext = _tcsrchr(filename, '.');
3146 if (!ext)
3147 ext = sEmpty;
3149 if (is_exe_file(ext))
3150 return FT_EXECUTABLE;
3151 else if (is_registered_type(ext))
3152 return FT_DOCUMENT;
3153 else
3154 return FT_OTHER;
3158 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
3160 TCHAR buffer[BUFFER_LEN];
3161 DWORD attrs;
3162 int visible_cols = pane->visible_cols;
3163 COLORREF bkcolor, textcolor;
3164 RECT focusRect = dis->rcItem;
3165 HBRUSH hbrush;
3166 enum IMAGE img;
3167 int img_pos, cx;
3168 int col = 0;
3170 if (entry) {
3171 attrs = entry->data.dwFileAttributes;
3173 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
3174 if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '.'
3175 && entry->data.cFileName[2] == '\0')
3176 img = IMG_FOLDER_UP;
3177 #ifndef _NO_EXTENSIONS
3178 else if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '\0')
3179 img = IMG_FOLDER_CUR;
3180 #endif
3181 else if (
3182 #ifdef _NO_EXTENSIONS
3183 entry->expanded ||
3184 #endif
3185 (pane->treePane && (dis->itemState&ODS_FOCUS)))
3186 img = IMG_OPEN_FOLDER;
3187 else
3188 img = IMG_FOLDER;
3189 } else {
3190 switch(get_file_type(entry->data.cFileName)) {
3191 case FT_EXECUTABLE: img = IMG_EXECUTABLE; break;
3192 case FT_DOCUMENT: img = IMG_DOCUMENT; break;
3193 default: img = IMG_FILE;
3196 } else {
3197 attrs = 0;
3198 img = IMG_NONE;
3201 if (pane->treePane) {
3202 if (entry) {
3203 img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+TREE_LINE_DX);
3205 if (calcWidthCol == -1) {
3206 int x;
3207 int y = dis->rcItem.top + IMAGE_HEIGHT/2;
3208 Entry* up;
3209 RECT rt_clip;
3210 HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
3211 HRGN hrgn;
3213 rt_clip.left = dis->rcItem.left;
3214 rt_clip.top = dis->rcItem.top;
3215 rt_clip.right = dis->rcItem.left+pane->widths[col];
3216 rt_clip.bottom = dis->rcItem.bottom;
3218 hrgn = CreateRectRgnIndirect(&rt_clip);
3220 if (!GetClipRgn(dis->hDC, hrgn_org)) {
3221 DeleteObject(hrgn_org);
3222 hrgn_org = 0;
3225 /* HGDIOBJ holdPen = SelectObject(dis->hDC, GetStockObject(BLACK_PEN)); */
3226 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
3227 DeleteObject(hrgn);
3229 if ((up=entry->up) != NULL) {
3230 MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
3231 LineTo(dis->hDC, img_pos-2, y);
3233 x = img_pos - IMAGE_WIDTH/2;
3235 do {
3236 x -= IMAGE_WIDTH+TREE_LINE_DX;
3238 if (up->next
3239 #ifndef _LEFT_FILES
3240 && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3241 #endif
3243 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3244 LineTo(dis->hDC, x, dis->rcItem.bottom);
3246 } while((up=up->up) != NULL);
3249 x = img_pos - IMAGE_WIDTH/2;
3251 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3252 LineTo(dis->hDC, x, y);
3254 if (entry->next
3255 #ifndef _LEFT_FILES
3256 && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
3257 #endif
3259 LineTo(dis->hDC, x, dis->rcItem.bottom);
3261 SelectClipRgn(dis->hDC, hrgn_org);
3262 if (hrgn_org) DeleteObject(hrgn_org);
3263 /* SelectObject(dis->hDC, holdPen); */
3264 } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
3265 int right = img_pos + IMAGE_WIDTH - TREE_LINE_DX;
3267 if (right > pane->widths[col])
3268 pane->widths[col] = right;
3270 } else {
3271 img_pos = dis->rcItem.left;
3273 } else {
3274 img_pos = dis->rcItem.left;
3276 if (calcWidthCol==col || calcWidthCol==COLUMNS)
3277 pane->widths[col] = IMAGE_WIDTH;
3280 if (calcWidthCol == -1) {
3281 focusRect.left = img_pos -2;
3283 #ifdef _NO_EXTENSIONS
3284 if (pane->treePane && entry) {
3285 RECT rt = {0};
3287 DrawText(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
3289 focusRect.right = dis->rcItem.left+pane->positions[col+1]+TREE_LINE_DX + rt.right +2;
3291 #else
3293 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
3294 textcolor = COLOR_COMPRESSED;
3295 else
3296 #endif /* _NO_EXTENSIONS */
3297 textcolor = RGB(0,0,0);
3299 if (dis->itemState & ODS_FOCUS) {
3300 textcolor = RGB(255,255,255);
3301 bkcolor = COLOR_SELECTION;
3302 } else {
3303 bkcolor = RGB(255,255,255);
3306 hbrush = CreateSolidBrush(bkcolor);
3307 FillRect(dis->hDC, &focusRect, hbrush);
3308 DeleteObject(hbrush);
3310 SetBkMode(dis->hDC, TRANSPARENT);
3311 SetTextColor(dis->hDC, textcolor);
3313 cx = pane->widths[col];
3315 if (cx && img!=IMG_NONE) {
3316 if (cx > IMAGE_WIDTH)
3317 cx = IMAGE_WIDTH;
3319 #ifdef _SHELL_FOLDERS
3320 if (entry->hicon && entry->hicon!=(HICON)-1)
3321 DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->hicon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
3322 else
3323 #endif
3324 ImageList_DrawEx(Globals.himl, img, dis->hDC,
3325 img_pos, dis->rcItem.top, cx,
3326 IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
3330 if (!entry)
3331 return;
3333 #ifdef _NO_EXTENSIONS
3334 if (img >= IMG_FOLDER_UP)
3335 return;
3336 #endif
3338 col++;
3340 /* ouput file name */
3341 if (calcWidthCol == -1)
3342 output_text(pane, dis, col, entry->data.cFileName, 0);
3343 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3344 calc_width(pane, dis, col, entry->data.cFileName);
3346 col++;
3348 #ifdef _NO_EXTENSIONS
3349 if (!pane->treePane) {
3350 #endif
3352 /* display file size */
3353 if (visible_cols & COL_SIZE) {
3354 #ifdef _NO_EXTENSIONS
3355 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
3356 #endif
3358 ULONGLONG size;
3360 size = ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow;
3362 _stprintf(buffer, sLongNumFmt, size);
3364 if (calcWidthCol == -1)
3365 output_number(pane, dis, col, buffer);
3366 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3367 calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
3370 col++;
3373 /* display file date */
3374 if (visible_cols & (COL_DATE|COL_TIME)) {
3375 #ifndef _NO_EXTENSIONS
3376 format_date(&entry->data.ftCreationTime, buffer, visible_cols);
3377 if (calcWidthCol == -1)
3378 output_text(pane, dis, col, buffer, 0);
3379 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3380 calc_width(pane, dis, col, buffer);
3381 col++;
3383 format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
3384 if (calcWidthCol == -1)
3385 output_text(pane, dis, col, buffer, 0);
3386 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3387 calc_width(pane, dis, col, buffer);
3388 col++;
3389 #endif /* _NO_EXTENSIONS */
3391 format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
3392 if (calcWidthCol == -1)
3393 output_text(pane, dis, col, buffer, 0);
3394 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3395 calc_width(pane, dis, col, buffer);
3396 col++;
3399 #ifndef _NO_EXTENSIONS
3400 if (entry->bhfi_valid) {
3401 ULONGLONG index = ((ULONGLONG)entry->bhfi.nFileIndexHigh << 32) | entry->bhfi.nFileIndexLow;
3403 if (visible_cols & COL_INDEX) {
3404 _stprintf(buffer, sLongHexFmt, index);
3406 if (calcWidthCol == -1)
3407 output_text(pane, dis, col, buffer, DT_RIGHT);
3408 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3409 calc_width(pane, dis, col, buffer);
3411 col++;
3414 if (visible_cols & COL_LINKS) {
3415 wsprintf(buffer, sNumFmt, entry->bhfi.nNumberOfLinks);
3417 if (calcWidthCol == -1)
3418 output_text(pane, dis, col, buffer, DT_CENTER);
3419 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3420 calc_width(pane, dis, col, buffer);
3422 col++;
3424 } else
3425 col += 2;
3426 #endif /* _NO_EXTENSIONS */
3428 /* show file attributes */
3429 if (visible_cols & COL_ATTRIBUTES) {
3430 #ifdef _NO_EXTENSIONS
3431 static const TCHAR s4Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3432 lstrcpy(buffer, s4Tabs);
3433 #else
3434 static const TCHAR s11Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3435 lstrcpy(buffer, s11Tabs);
3436 #endif
3438 if (attrs & FILE_ATTRIBUTE_NORMAL) buffer[ 0] = 'N';
3439 else {
3440 if (attrs & FILE_ATTRIBUTE_READONLY) buffer[ 2] = 'R';
3441 if (attrs & FILE_ATTRIBUTE_HIDDEN) buffer[ 4] = 'H';
3442 if (attrs & FILE_ATTRIBUTE_SYSTEM) buffer[ 6] = 'S';
3443 if (attrs & FILE_ATTRIBUTE_ARCHIVE) buffer[ 8] = 'A';
3444 if (attrs & FILE_ATTRIBUTE_COMPRESSED) buffer[10] = 'C';
3445 #ifndef _NO_EXTENSIONS
3446 if (attrs & FILE_ATTRIBUTE_DIRECTORY) buffer[12] = 'D';
3447 if (attrs & FILE_ATTRIBUTE_ENCRYPTED) buffer[14] = 'E';
3448 if (attrs & FILE_ATTRIBUTE_TEMPORARY) buffer[16] = 'T';
3449 if (attrs & FILE_ATTRIBUTE_SPARSE_FILE) buffer[18] = 'P';
3450 if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) buffer[20] = 'Q';
3451 if (attrs & FILE_ATTRIBUTE_OFFLINE) buffer[22] = 'O';
3452 if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
3453 #endif /* _NO_EXTENSIONS */
3456 if (calcWidthCol == -1)
3457 output_tabbed_text(pane, dis, col, buffer);
3458 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3459 calc_tabbed_width(pane, dis, col, buffer);
3461 col++;
3464 /*TODO
3465 if (flags.security) {
3466 static const TCHAR sSecTabs[] = {
3467 ' ','\t',' ','\t',' ','\t',' ',
3468 ' ','\t',' ',
3469 ' ','\t',' ','\t',' ','\t',' ',
3470 ' ','\t',' ',
3471 ' ','\t',' ','\t',' ','\t',' ',
3472 '\0'
3475 DWORD rights = get_access_mask();
3477 lstrcpy(buffer, sSecTabs);
3479 if (rights & FILE_READ_DATA) buffer[ 0] = 'R';
3480 if (rights & FILE_WRITE_DATA) buffer[ 2] = 'W';
3481 if (rights & FILE_APPEND_DATA) buffer[ 4] = 'A';
3482 if (rights & FILE_READ_EA) {buffer[6] = 'entry'; buffer[ 7] = 'R';}
3483 if (rights & FILE_WRITE_EA) {buffer[9] = 'entry'; buffer[10] = 'W';}
3484 if (rights & FILE_EXECUTE) buffer[12] = 'X';
3485 if (rights & FILE_DELETE_CHILD) buffer[14] = 'D';
3486 if (rights & FILE_READ_ATTRIBUTES) {buffer[16] = 'a'; buffer[17] = 'R';}
3487 if (rights & FILE_WRITE_ATTRIBUTES) {buffer[19] = 'a'; buffer[20] = 'W';}
3488 if (rights & WRITE_DAC) buffer[22] = 'C';
3489 if (rights & WRITE_OWNER) buffer[24] = 'O';
3490 if (rights & SYNCHRONIZE) buffer[26] = 'S';
3492 output_text(dis, col++, buffer, DT_LEFT, 3, psize);
3495 if (flags.description) {
3496 get_description(buffer);
3497 output_text(dis, col++, buffer, 0, psize);
3501 #ifdef _NO_EXTENSIONS
3504 /* draw focus frame */
3505 if ((dis->itemState&ODS_FOCUS) && calcWidthCol==-1) {
3506 /* Currently [04/2000] Wine neither behaves exactly the same */
3507 /* way as WIN 95 nor like Windows NT... */
3508 HGDIOBJ lastBrush;
3509 HPEN lastPen;
3510 HPEN hpen;
3512 if (!(GetVersion() & 0x80000000)) { /* Windows NT? */
3513 LOGBRUSH lb = {PS_SOLID, RGB(255,255,255)};
3514 hpen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, 0);
3515 } else
3516 hpen = CreatePen(PS_DOT, 0, RGB(255,255,255));
3518 lastPen = SelectPen(dis->hDC, hpen);
3519 lastBrush = SelectObject(dis->hDC, GetStockObject(HOLLOW_BRUSH));
3520 SetROP2(dis->hDC, R2_XORPEN);
3521 Rectangle(dis->hDC, focusRect.left, focusRect.top, focusRect.right, focusRect.bottom);
3522 SelectObject(dis->hDC, lastBrush);
3523 SelectObject(dis->hDC, lastPen);
3524 DeleteObject(hpen);
3526 #endif /* _NO_EXTENSIONS */
3530 #ifdef _NO_EXTENSIONS
3532 static void draw_splitbar(HWND hwnd, int x)
3534 RECT rt;
3535 HDC hdc = GetDC(hwnd);
3537 GetClientRect(hwnd, &rt);
3539 rt.left = x - SPLIT_WIDTH/2;
3540 rt.right = x + SPLIT_WIDTH/2+1;
3542 InvertRect(hdc, &rt);
3544 ReleaseDC(hwnd, hdc);
3547 #endif /* _NO_EXTENSIONS */
3550 #ifndef _NO_EXTENSIONS
3552 static void set_header(Pane* pane)
3554 HD_ITEM item;
3555 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3556 int i=0, x=0;
3558 item.mask = HDI_WIDTH;
3559 item.cxy = 0;
3561 for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
3562 x += pane->widths[i];
3563 SendMessage(pane->hwndHeader, HDM_SETITEM, i, (LPARAM) &item);
3566 if (i < COLUMNS) {
3567 x += pane->widths[i];
3568 item.cxy = x - scroll_pos;
3569 SendMessage(pane->hwndHeader, HDM_SETITEM, i++, (LPARAM) &item);
3571 for(; i<COLUMNS; i++) {
3572 item.cxy = pane->widths[i];
3573 x += pane->widths[i];
3574 SendMessage(pane->hwndHeader, HDM_SETITEM, i, (LPARAM) &item);
3579 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
3581 switch(pnmh->code) {
3582 case HDN_ITEMCHANGED: {
3583 HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
3584 int idx = phdn->iItem;
3585 int dx = phdn->pitem->cxy - pane->widths[idx];
3586 int i;
3588 RECT clnt;
3589 GetClientRect(pane->hwnd, &clnt);
3591 pane->widths[idx] += dx;
3593 for(i=idx; ++i<=COLUMNS; )
3594 pane->positions[i] += dx;
3597 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3598 RECT rt_scr;
3599 RECT rt_clip;
3601 rt_scr.left = pane->positions[idx+1]-scroll_pos;
3602 rt_scr.top = 0;
3603 rt_scr.right = clnt.right;
3604 rt_scr.bottom = clnt.bottom;
3606 rt_clip.left = pane->positions[idx]-scroll_pos;
3607 rt_clip.top = 0;
3608 rt_clip.right = clnt.right;
3609 rt_clip.bottom = clnt.bottom;
3611 if (rt_scr.left < 0) rt_scr.left = 0;
3612 if (rt_clip.left < 0) rt_clip.left = 0;
3614 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
3616 rt_clip.right = pane->positions[idx+1];
3617 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
3619 if (pnmh->code == HDN_ENDTRACK) {
3620 SendMessage(pane->hwnd, LB_SETHORIZONTALEXTENT, pane->positions[COLUMNS], 0);
3622 if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
3623 set_header(pane);
3627 return FALSE;
3630 case HDN_DIVIDERDBLCLICK: {
3631 HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
3632 HD_ITEM item;
3634 calc_single_width(pane, phdn->iItem);
3635 item.mask = HDI_WIDTH;
3636 item.cxy = pane->widths[phdn->iItem];
3638 SendMessage(pane->hwndHeader, HDM_SETITEM, phdn->iItem, (LPARAM) &item);
3639 InvalidateRect(pane->hwnd, 0, TRUE);
3640 break;}
3643 return 0;
3646 #endif /* _NO_EXTENSIONS */
3649 static void scan_entry(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3651 TCHAR path[MAX_PATH];
3652 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
3654 /* delete sub entries in left pane */
3655 for(;;) {
3656 LRESULT res = SendMessage(child->left.hwnd, LB_GETITEMDATA, idx+1, 0);
3657 Entry* sub = (Entry*) res;
3659 if (res==LB_ERR || !sub || sub->level<=entry->level)
3660 break;
3662 SendMessage(child->left.hwnd, LB_DELETESTRING, idx+1, 0);
3665 /* empty right pane */
3666 SendMessage(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3668 /* release memory */
3669 free_entries(entry);
3671 /* read contents from disk */
3672 #ifdef _SHELL_FOLDERS
3673 if (entry->etype == ET_SHELL)
3675 read_directory(entry, NULL, child->sortOrder, hwnd);
3677 else
3678 #endif
3680 get_path(entry, path);
3681 read_directory(entry, path, child->sortOrder, hwnd);
3684 /* insert found entries in right pane */
3685 insert_entries(&child->right, entry->down, child->filter_pattern, child->filter_flags, -1);
3686 calc_widths(&child->right, FALSE);
3687 #ifndef _NO_EXTENSIONS
3688 set_header(&child->right);
3689 #endif
3691 child->header_wdths_ok = FALSE;
3693 SetCursor(old_cursor);
3697 /* expand a directory entry */
3699 static BOOL expand_entry(ChildWnd* child, Entry* dir)
3701 int idx;
3702 Entry* p;
3704 if (!dir || dir->expanded || !dir->down)
3705 return FALSE;
3707 p = dir->down;
3709 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
3710 p = p->next;
3712 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
3713 p->data.cFileName[2]=='\0' && p->next)
3714 p = p->next;
3717 /* no subdirectories ? */
3718 if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
3719 return FALSE;
3721 idx = SendMessage(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3723 dir->expanded = TRUE;
3725 /* insert entries in left pane */
3726 insert_entries(&child->left, p, NULL, TF_ALL, idx);
3728 if (!child->header_wdths_ok) {
3729 if (calc_widths(&child->left, FALSE)) {
3730 #ifndef _NO_EXTENSIONS
3731 set_header(&child->left);
3732 #endif
3734 child->header_wdths_ok = TRUE;
3738 return TRUE;
3742 static void collapse_entry(Pane* pane, Entry* dir)
3744 int idx = SendMessage(pane->hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3746 ShowWindow(pane->hwnd, SW_HIDE);
3748 /* hide sub entries */
3749 for(;;) {
3750 LRESULT res = SendMessage(pane->hwnd, LB_GETITEMDATA, idx+1, 0);
3751 Entry* sub = (Entry*) res;
3753 if (res==LB_ERR || !sub || sub->level<=dir->level)
3754 break;
3756 SendMessage(pane->hwnd, LB_DELETESTRING, idx+1, 0);
3759 dir->expanded = FALSE;
3761 ShowWindow(pane->hwnd, SW_SHOW);
3765 static void refresh_right_pane(ChildWnd* child)
3767 SendMessage(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3768 insert_entries(&child->right, child->right.root, child->filter_pattern, child->filter_flags, -1);
3769 calc_widths(&child->right, FALSE);
3771 #ifndef _NO_EXTENSIONS
3772 set_header(&child->right);
3773 #endif
3776 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3778 TCHAR path[MAX_PATH];
3780 if (!entry)
3781 return;
3783 path[0] = '\0';
3785 child->left.cur = entry;
3787 child->right.root = entry->down? entry->down: entry;
3788 child->right.cur = entry;
3790 if (!entry->scanned)
3791 scan_entry(child, entry, idx, hwnd);
3792 else
3793 refresh_right_pane(child);
3795 get_path(entry, path);
3796 lstrcpy(child->path, path);
3798 if (child->hwnd) /* only change window title, if the window already exists */
3799 SetWindowText(child->hwnd, path);
3801 if (path[0])
3802 if (SetCurrentDirectory(path))
3803 set_space_status();
3807 static void refresh_child(ChildWnd* child)
3809 TCHAR path[MAX_PATH], drv[_MAX_DRIVE+1];
3810 Entry* entry;
3811 int idx;
3813 get_path(child->left.cur, path);
3814 _tsplitpath(path, drv, NULL, NULL, NULL);
3816 child->right.root = NULL;
3818 scan_entry(child, &child->root.entry, 0, child->hwnd);
3820 #ifdef _SHELL_FOLDERS
3821 if (child->root.entry.etype == ET_SHELL)
3822 entry = read_tree(&child->root, NULL, get_path_pidl(path,child->hwnd), drv, child->sortOrder, child->hwnd);
3823 else
3824 #endif
3825 entry = read_tree(&child->root, path, NULL, drv, child->sortOrder, child->hwnd);
3827 if (!entry)
3828 entry = &child->root.entry;
3830 insert_entries(&child->left, child->root.entry.down, NULL, TF_ALL, 0);
3832 set_curdir(child, entry, 0, child->hwnd);
3834 idx = SendMessage(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
3835 SendMessage(child->left.hwnd, LB_SETCURSEL, idx, 0);
3839 static void create_drive_bar(void)
3841 TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0};
3842 #ifndef _NO_EXTENSIONS
3843 TCHAR b1[BUFFER_LEN];
3844 #endif
3845 int btn = 1;
3846 PTSTR p;
3848 GetLogicalDriveStrings(BUFFER_LEN, Globals.drives);
3850 Globals.hdrivebar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE|CCS_NOMOVEY|TBSTYLE_LIST,
3851 IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
3852 0, 16, 13, 16, 13, sizeof(TBBUTTON));
3854 #ifndef _NO_EXTENSIONS
3855 #ifdef __WINE__
3856 /* insert unix file system button */
3857 b1[0] = '/';
3858 b1[1] = '\0';
3859 b1[2] = '\0';
3860 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)b1);
3862 drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
3863 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3864 drivebarBtn.iString++;
3865 #endif
3866 #ifdef _SHELL_FOLDERS
3867 /* insert shell namespace button */
3868 load_string(b1, IDS_SHELL);
3869 b1[lstrlen(b1)+1] = '\0';
3870 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)b1);
3872 drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3873 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3874 drivebarBtn.iString++;
3875 #endif
3877 /* register windows drive root strings */
3878 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)Globals.drives);
3879 #endif
3881 drivebarBtn.idCommand = ID_DRIVE_FIRST;
3883 for(p=Globals.drives; *p; ) {
3884 #ifdef _NO_EXTENSIONS
3885 /* insert drive letter */
3886 TCHAR b[3] = {tolower(*p)};
3887 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)b);
3888 #endif
3889 switch(GetDriveType(p)) {
3890 case DRIVE_REMOVABLE: drivebarBtn.iBitmap = 1; break;
3891 case DRIVE_CDROM: drivebarBtn.iBitmap = 3; break;
3892 case DRIVE_REMOTE: drivebarBtn.iBitmap = 4; break;
3893 case DRIVE_RAMDISK: drivebarBtn.iBitmap = 5; break;
3894 default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
3897 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3898 drivebarBtn.idCommand++;
3899 drivebarBtn.iString++;
3901 while(*p++);
3905 static void refresh_drives(void)
3907 RECT rect;
3909 /* destroy drive bar */
3910 DestroyWindow(Globals.hdrivebar);
3911 Globals.hdrivebar = 0;
3913 /* re-create drive bar */
3914 create_drive_bar();
3916 /* update window layout */
3917 GetClientRect(Globals.hMainWnd, &rect);
3918 SendMessage(Globals.hMainWnd, WM_SIZE, 0, MAKELONG(rect.right, rect.bottom));
3922 static BOOL launch_file(HWND hwnd, LPCTSTR cmd, UINT nCmdShow)
3924 HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3926 if (PtrToUlong(hinst) <= 32) {
3927 display_error(hwnd, GetLastError());
3928 return FALSE;
3931 return TRUE;
3935 static BOOL launch_entry(Entry* entry, HWND hwnd, UINT nCmdShow)
3937 TCHAR cmd[MAX_PATH];
3939 #ifdef _SHELL_FOLDERS
3940 if (entry->etype == ET_SHELL) {
3941 BOOL ret = TRUE;
3943 SHELLEXECUTEINFO shexinfo;
3945 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
3946 shexinfo.fMask = SEE_MASK_IDLIST;
3947 shexinfo.hwnd = hwnd;
3948 shexinfo.lpVerb = NULL;
3949 shexinfo.lpFile = NULL;
3950 shexinfo.lpParameters = NULL;
3951 shexinfo.lpDirectory = NULL;
3952 shexinfo.nShow = nCmdShow;
3953 shexinfo.lpIDList = get_to_absolute_pidl(entry, hwnd);
3955 if (!ShellExecuteEx(&shexinfo)) {
3956 display_error(hwnd, GetLastError());
3957 ret = FALSE;
3960 if (shexinfo.lpIDList != entry->pidl)
3961 IMalloc_Free(Globals.iMalloc, shexinfo.lpIDList);
3963 return ret;
3965 #endif
3967 get_path(entry, cmd);
3969 /* start program, open document... */
3970 return launch_file(hwnd, cmd, nCmdShow);
3974 static void activate_entry(ChildWnd* child, Pane* pane, HWND hwnd)
3976 Entry* entry = pane->cur;
3978 if (!entry)
3979 return;
3981 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3982 int scanned_old = entry->scanned;
3984 if (!scanned_old)
3986 int idx = SendMessage(child->left.hwnd, LB_GETCURSEL, 0, 0);
3987 scan_entry(child, entry, idx, hwnd);
3990 #ifndef _NO_EXTENSIONS
3991 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3992 return;
3993 #endif
3995 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
3996 entry = child->left.cur->up;
3997 collapse_entry(&child->left, entry);
3998 goto focus_entry;
3999 } else if (entry->expanded)
4000 collapse_entry(pane, child->left.cur);
4001 else {
4002 expand_entry(child, child->left.cur);
4004 if (!pane->treePane) focus_entry: {
4005 int idxstart = SendMessage(child->left.hwnd, LB_GETCURSEL, 0, 0);
4006 int idx = SendMessage(child->left.hwnd, LB_FINDSTRING, idxstart, (LPARAM)entry);
4007 SendMessage(child->left.hwnd, LB_SETCURSEL, idx, 0);
4008 set_curdir(child, entry, idx, hwnd);
4012 if (!scanned_old) {
4013 calc_widths(pane, FALSE);
4015 #ifndef _NO_EXTENSIONS
4016 set_header(pane);
4017 #endif
4019 } else {
4020 if (GetKeyState(VK_MENU) < 0)
4021 show_properties_dlg(entry, child->hwnd);
4022 else
4023 launch_entry(entry, child->hwnd, SW_SHOWNORMAL);
4028 static BOOL pane_command(Pane* pane, UINT cmd)
4030 switch(cmd) {
4031 case ID_VIEW_NAME:
4032 if (pane->visible_cols) {
4033 pane->visible_cols = 0;
4034 calc_widths(pane, TRUE);
4035 #ifndef _NO_EXTENSIONS
4036 set_header(pane);
4037 #endif
4038 InvalidateRect(pane->hwnd, 0, TRUE);
4039 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
4040 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
4041 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
4043 break;
4045 case ID_VIEW_ALL_ATTRIBUTES:
4046 if (pane->visible_cols != COL_ALL) {
4047 pane->visible_cols = COL_ALL;
4048 calc_widths(pane, TRUE);
4049 #ifndef _NO_EXTENSIONS
4050 set_header(pane);
4051 #endif
4052 InvalidateRect(pane->hwnd, 0, TRUE);
4053 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
4054 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
4055 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
4057 break;
4059 #ifndef _NO_EXTENSIONS
4060 case ID_PREFERRED_SIZES: {
4061 calc_widths(pane, TRUE);
4062 set_header(pane);
4063 InvalidateRect(pane->hwnd, 0, TRUE);
4064 break;}
4065 #endif
4067 /* TODO: more command ids... */
4069 default:
4070 return FALSE;
4073 return TRUE;
4077 static void set_sort_order(ChildWnd* child, SORT_ORDER sortOrder)
4079 if (child->sortOrder != sortOrder) {
4080 child->sortOrder = sortOrder;
4081 refresh_child(child);
4085 static void update_view_menu(ChildWnd* child)
4087 CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_NAME, child->sortOrder==SORT_NAME? MF_CHECKED: MF_UNCHECKED);
4088 CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_TYPE, child->sortOrder==SORT_EXT? MF_CHECKED: MF_UNCHECKED);
4089 CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_SIZE, child->sortOrder==SORT_SIZE? MF_CHECKED: MF_UNCHECKED);
4090 CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_DATE, child->sortOrder==SORT_DATE? MF_CHECKED: MF_UNCHECKED);
4094 static BOOL is_directory(LPCTSTR target)
4096 /*TODO correctly handle UNIX paths */
4097 DWORD target_attr = GetFileAttributes(target);
4099 if (target_attr == INVALID_FILE_ATTRIBUTES)
4100 return FALSE;
4102 return target_attr&FILE_ATTRIBUTE_DIRECTORY? TRUE: FALSE;
4105 static BOOL prompt_target(Pane* pane, LPTSTR source, LPTSTR target)
4107 TCHAR path[MAX_PATH];
4108 int len;
4110 get_path(pane->cur, path);
4112 if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_SELECT_DESTINATION), pane->hwnd, DestinationDlgProc, (LPARAM)path) != IDOK)
4113 return FALSE;
4115 get_path(pane->cur, source);
4117 /* convert relative targets to absolute paths */
4118 if (path[0]!='/' && path[1]!=':') {
4119 get_path(pane->cur->up, target);
4120 len = lstrlen(target);
4122 if (target[len-1]!='\\' && target[len-1]!='/')
4123 target[len++] = '/';
4125 lstrcpy(target+len, path);
4126 } else
4127 lstrcpy(target, path);
4129 /* If the target already exists as directory, create a new target below this. */
4130 if (is_directory(path)) {
4131 TCHAR fname[_MAX_FNAME], ext[_MAX_EXT];
4132 static const TCHAR sAppend[] = {'%','s','/','%','s','%','s','\0'};
4134 _tsplitpath(source, NULL, NULL, fname, ext);
4136 wsprintf(target, sAppend, path, fname, ext);
4139 return TRUE;
4143 static IContextMenu2* s_pctxmenu2 = NULL;
4144 static IContextMenu3* s_pctxmenu3 = NULL;
4146 static void CtxMenu_reset(void)
4148 s_pctxmenu2 = NULL;
4149 s_pctxmenu3 = NULL;
4152 static IContextMenu* CtxMenu_query_interfaces(IContextMenu* pcm1)
4154 IContextMenu* pcm = NULL;
4156 CtxMenu_reset();
4158 if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu3, (void**)&pcm) == NOERROR)
4159 s_pctxmenu3 = (LPCONTEXTMENU3)pcm;
4160 else if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu2, (void**)&pcm) == NOERROR)
4161 s_pctxmenu2 = (LPCONTEXTMENU2)pcm;
4163 if (pcm) {
4164 IContextMenu_Release(pcm1);
4165 return pcm;
4166 } else
4167 return pcm1;
4170 static BOOL CtxMenu_HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam)
4172 if (s_pctxmenu3) {
4173 if (SUCCEEDED(IContextMenu3_HandleMenuMsg(s_pctxmenu3, nmsg, wparam, lparam)))
4174 return TRUE;
4177 if (s_pctxmenu2)
4178 if (SUCCEEDED(IContextMenu2_HandleMenuMsg(s_pctxmenu2, nmsg, wparam, lparam)))
4179 return TRUE;
4181 return FALSE;
4185 static HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, LPCITEMIDLIST* apidl, int x, int y)
4187 IContextMenu* pcm;
4188 BOOL executed = FALSE;
4190 HRESULT hr = IShellFolder_GetUIObjectOf(shell_folder, hwndParent, cidl, apidl, &IID_IContextMenu, NULL, (LPVOID*)&pcm);
4191 /* HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm); */
4193 if (SUCCEEDED(hr)) {
4194 HMENU hmenu = CreatePopupMenu();
4196 pcm = CtxMenu_query_interfaces(pcm);
4198 if (hmenu) {
4199 hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL);
4201 if (SUCCEEDED(hr)) {
4202 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL);
4204 CtxMenu_reset();
4206 if (idCmd) {
4207 CMINVOKECOMMANDINFO cmi;
4209 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
4210 cmi.fMask = 0;
4211 cmi.hwnd = hwndParent;
4212 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
4213 cmi.lpParameters = NULL;
4214 cmi.lpDirectory = NULL;
4215 cmi.nShow = SW_SHOWNORMAL;
4216 cmi.dwHotKey = 0;
4217 cmi.hIcon = 0;
4219 hr = IContextMenu_InvokeCommand(pcm, &cmi);
4220 executed = TRUE;
4222 } else
4223 CtxMenu_reset();
4226 IContextMenu_Release(pcm);
4229 return FAILED(hr)? hr: executed? S_OK: S_FALSE;
4233 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4235 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
4236 ASSERT(child);
4238 switch(nmsg) {
4239 case WM_DRAWITEM: {
4240 LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
4241 Entry* entry = (Entry*) dis->itemData;
4243 if (dis->CtlID == IDW_TREE_LEFT)
4244 draw_item(&child->left, dis, entry, -1);
4245 else if (dis->CtlID == IDW_TREE_RIGHT)
4246 draw_item(&child->right, dis, entry, -1);
4247 else
4248 goto draw_menu_item;
4250 return TRUE;}
4252 case WM_CREATE:
4253 InitChildWindow(child);
4254 break;
4256 case WM_NCDESTROY:
4257 free_child_window(child);
4258 SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
4259 break;
4261 case WM_PAINT: {
4262 PAINTSTRUCT ps;
4263 HBRUSH lastBrush;
4264 RECT rt;
4265 GetClientRect(hwnd, &rt);
4266 BeginPaint(hwnd, &ps);
4267 rt.left = child->split_pos-SPLIT_WIDTH/2;
4268 rt.right = child->split_pos+SPLIT_WIDTH/2+1;
4269 lastBrush = SelectObject(ps.hdc, GetStockObject(COLOR_SPLITBAR));
4270 Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
4271 SelectObject(ps.hdc, lastBrush);
4272 #ifdef _NO_EXTENSIONS
4273 rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
4274 FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
4275 #endif
4276 EndPaint(hwnd, &ps);
4277 break;}
4279 case WM_SETCURSOR:
4280 if (LOWORD(lparam) == HTCLIENT) {
4281 POINT pt;
4282 GetCursorPos(&pt);
4283 ScreenToClient(hwnd, &pt);
4285 if (pt.x>=child->split_pos-SPLIT_WIDTH/2 && pt.x<child->split_pos+SPLIT_WIDTH/2+1) {
4286 SetCursor(LoadCursor(0, IDC_SIZEWE));
4287 return TRUE;
4290 goto def;
4292 case WM_LBUTTONDOWN: {
4293 RECT rt;
4294 int x = (short)LOWORD(lparam);
4296 GetClientRect(hwnd, &rt);
4298 if (x>=child->split_pos-SPLIT_WIDTH/2 && x<child->split_pos+SPLIT_WIDTH/2+1) {
4299 last_split = child->split_pos;
4300 #ifdef _NO_EXTENSIONS
4301 draw_splitbar(hwnd, last_split);
4302 #endif
4303 SetCapture(hwnd);
4306 break;}
4308 case WM_LBUTTONUP:
4309 if (GetCapture() == hwnd) {
4310 #ifdef _NO_EXTENSIONS
4311 RECT rt;
4312 int x = (short)LOWORD(lparam);
4313 draw_splitbar(hwnd, last_split);
4314 last_split = -1;
4315 GetClientRect(hwnd, &rt);
4316 child->split_pos = x;
4317 resize_tree(child, rt.right, rt.bottom);
4318 #endif
4319 ReleaseCapture();
4321 break;
4323 #ifdef _NO_EXTENSIONS
4324 case WM_CAPTURECHANGED:
4325 if (GetCapture()==hwnd && last_split>=0)
4326 draw_splitbar(hwnd, last_split);
4327 break;
4328 #endif
4330 case WM_KEYDOWN:
4331 if (wparam == VK_ESCAPE)
4332 if (GetCapture() == hwnd) {
4333 RECT rt;
4334 #ifdef _NO_EXTENSIONS
4335 draw_splitbar(hwnd, last_split);
4336 #else
4337 child->split_pos = last_split;
4338 #endif
4339 GetClientRect(hwnd, &rt);
4340 resize_tree(child, rt.right, rt.bottom);
4341 last_split = -1;
4342 ReleaseCapture();
4343 SetCursor(LoadCursor(0, IDC_ARROW));
4345 break;
4347 case WM_MOUSEMOVE:
4348 if (GetCapture() == hwnd) {
4349 RECT rt;
4350 int x = (short)LOWORD(lparam);
4352 #ifdef _NO_EXTENSIONS
4353 HDC hdc = GetDC(hwnd);
4354 GetClientRect(hwnd, &rt);
4356 rt.left = last_split-SPLIT_WIDTH/2;
4357 rt.right = last_split+SPLIT_WIDTH/2+1;
4358 InvertRect(hdc, &rt);
4360 last_split = x;
4361 rt.left = x-SPLIT_WIDTH/2;
4362 rt.right = x+SPLIT_WIDTH/2+1;
4363 InvertRect(hdc, &rt);
4365 ReleaseDC(hwnd, hdc);
4366 #else
4367 GetClientRect(hwnd, &rt);
4369 if (x>=0 && x<rt.right) {
4370 child->split_pos = x;
4371 resize_tree(child, rt.right, rt.bottom);
4372 rt.left = x-SPLIT_WIDTH/2;
4373 rt.right = x+SPLIT_WIDTH/2+1;
4374 InvalidateRect(hwnd, &rt, FALSE);
4375 UpdateWindow(child->left.hwnd);
4376 UpdateWindow(hwnd);
4377 UpdateWindow(child->right.hwnd);
4379 #endif
4381 break;
4383 #ifndef _NO_EXTENSIONS
4384 case WM_GETMINMAXINFO:
4385 DefMDIChildProc(hwnd, nmsg, wparam, lparam);
4387 {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
4389 lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
4390 lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
4391 break;}
4392 #endif /* _NO_EXTENSIONS */
4394 case WM_SETFOCUS:
4395 if (SetCurrentDirectory(child->path))
4396 set_space_status();
4397 SetFocus(child->focus_pane? child->right.hwnd: child->left.hwnd);
4398 break;
4400 case WM_DISPATCH_COMMAND: {
4401 Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4403 switch(LOWORD(wparam)) {
4404 case ID_WINDOW_NEW: {
4405 ChildWnd* new_child = alloc_child_window(child->path, NULL, hwnd);
4407 if (!create_child_window(new_child))
4408 HeapFree(GetProcessHeap(), 0, new_child);
4410 break;}
4412 case ID_REFRESH:
4413 refresh_drives();
4414 refresh_child(child);
4415 break;
4417 case ID_ACTIVATE:
4418 activate_entry(child, pane, hwnd);
4419 break;
4421 case ID_FILE_MOVE: {
4422 TCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4424 if (prompt_target(pane, source, target)) {
4425 SHFILEOPSTRUCT shfo = {hwnd, FO_MOVE, source, target};
4427 source[lstrlen(source)+1] = '\0';
4428 target[lstrlen(target)+1] = '\0';
4430 if (!SHFileOperation(&shfo))
4431 refresh_child(child);
4433 break;}
4435 case ID_FILE_COPY: {
4436 TCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4438 if (prompt_target(pane, source, target)) {
4439 SHFILEOPSTRUCT shfo = {hwnd, FO_COPY, source, target};
4441 source[lstrlen(source)+1] = '\0';
4442 target[lstrlen(target)+1] = '\0';
4444 if (!SHFileOperation(&shfo))
4445 refresh_child(child);
4447 break;}
4449 case ID_FILE_DELETE: {
4450 TCHAR path[BUFFER_LEN];
4451 SHFILEOPSTRUCT shfo = {hwnd, FO_DELETE, path, NULL, FOF_ALLOWUNDO};
4453 get_path(pane->cur, path);
4455 path[lstrlen(path)+1] = '\0';
4457 if (!SHFileOperation(&shfo))
4458 refresh_child(child);
4459 break;}
4461 case ID_VIEW_SORT_NAME:
4462 set_sort_order(child, SORT_NAME);
4463 break;
4465 case ID_VIEW_SORT_TYPE:
4466 set_sort_order(child, SORT_EXT);
4467 break;
4469 case ID_VIEW_SORT_SIZE:
4470 set_sort_order(child, SORT_SIZE);
4471 break;
4473 case ID_VIEW_SORT_DATE:
4474 set_sort_order(child, SORT_DATE);
4475 break;
4477 case ID_VIEW_FILTER: {
4478 struct FilterDialog dlg;
4480 memset(&dlg, 0, sizeof(struct FilterDialog));
4481 lstrcpy(dlg.pattern, child->filter_pattern);
4482 dlg.flags = child->filter_flags;
4484 if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_DIALOG_VIEW_TYPE), hwnd, FilterDialogDlgProc, (LPARAM)&dlg) == IDOK) {
4485 lstrcpy(child->filter_pattern, dlg.pattern);
4486 child->filter_flags = dlg.flags;
4487 refresh_right_pane(child);
4489 break;}
4491 case ID_VIEW_SPLIT: {
4492 last_split = child->split_pos;
4493 #ifdef _NO_EXTENSIONS
4494 draw_splitbar(hwnd, last_split);
4495 #endif
4496 SetCapture(hwnd);
4497 break;}
4499 case ID_EDIT_PROPERTIES:
4500 show_properties_dlg(pane->cur, child->hwnd);
4501 break;
4503 default:
4504 return pane_command(pane, LOWORD(wparam));
4507 return TRUE;}
4509 case WM_COMMAND: {
4510 Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4512 switch(HIWORD(wparam)) {
4513 case LBN_SELCHANGE: {
4514 int idx = SendMessage(pane->hwnd, LB_GETCURSEL, 0, 0);
4515 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, idx, 0);
4517 if (pane == &child->left)
4518 set_curdir(child, entry, idx, hwnd);
4519 else
4520 pane->cur = entry;
4521 break;}
4523 case LBN_DBLCLK:
4524 activate_entry(child, pane, hwnd);
4525 break;
4527 break;}
4529 #ifndef _NO_EXTENSIONS
4530 case WM_NOTIFY: {
4531 NMHDR* pnmh = (NMHDR*) lparam;
4532 return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &child->left: &child->right, pnmh);}
4533 #endif
4535 #ifdef _SHELL_FOLDERS
4536 case WM_CONTEXTMENU: {
4537 POINT pt, pt_clnt;
4538 Pane* pane;
4539 int idx;
4541 /* first select the current item in the listbox */
4542 HWND hpanel = (HWND) wparam;
4543 pt_clnt.x = pt.x = (short)LOWORD(lparam);
4544 pt_clnt.y = pt.y = (short)HIWORD(lparam);
4545 ScreenToClient(hpanel, &pt_clnt);
4546 SendMessage(hpanel, WM_LBUTTONDOWN, 0, MAKELONG(pt_clnt.x, pt_clnt.y));
4547 SendMessage(hpanel, WM_LBUTTONUP, 0, MAKELONG(pt_clnt.x, pt_clnt.y));
4549 /* now create the popup menu using shell namespace and IContextMenu */
4550 pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4551 idx = SendMessage(pane->hwnd, LB_GETCURSEL, 0, 0);
4553 if (idx != -1) {
4554 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, idx, 0);
4556 LPITEMIDLIST pidl_abs = get_to_absolute_pidl(entry, hwnd);
4558 if (pidl_abs) {
4559 IShellFolder* parentFolder;
4560 LPCITEMIDLIST pidlLast;
4562 /* get and use the parent folder to display correct context menu in all cases */
4563 if (SUCCEEDED(SHBindToParent(pidl_abs, &IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast))) {
4564 if (ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pt.x, pt.y) == S_OK)
4565 refresh_child(child);
4567 IShellFolder_Release(parentFolder);
4570 IMalloc_Free(Globals.iMalloc, pidl_abs);
4573 break;}
4574 #endif
4576 case WM_MEASUREITEM:
4577 draw_menu_item:
4578 if (!wparam) /* Is the message menu-related? */
4579 if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4580 return TRUE;
4582 break;
4584 case WM_INITMENUPOPUP:
4585 if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4586 return 0;
4588 update_view_menu(child);
4589 break;
4591 case WM_MENUCHAR: /* only supported by IContextMenu3 */
4592 if (s_pctxmenu3) {
4593 LRESULT lResult = 0;
4595 IContextMenu3_HandleMenuMsg2(s_pctxmenu3, nmsg, wparam, lparam, &lResult);
4597 return lResult;
4600 break;
4602 case WM_SIZE:
4603 if (wparam != SIZE_MINIMIZED)
4604 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
4605 /* fall through */
4607 default: def:
4608 return DefMDIChildProc(hwnd, nmsg, wparam, lparam);
4611 return 0;
4615 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4617 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
4618 Pane* pane = (Pane*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
4619 ASSERT(child);
4621 switch(nmsg) {
4622 #ifndef _NO_EXTENSIONS
4623 case WM_HSCROLL:
4624 set_header(pane);
4625 break;
4626 #endif
4628 case WM_SETFOCUS:
4629 child->focus_pane = pane==&child->right? 1: 0;
4630 SendMessage(hwnd, LB_SETSEL, TRUE, 1);
4631 /*TODO: check menu items */
4632 break;
4634 case WM_KEYDOWN:
4635 if (wparam == VK_TAB) {
4636 /*TODO: SetFocus(Globals.hdrivebar) */
4637 SetFocus(child->focus_pane? child->left.hwnd: child->right.hwnd);
4641 return CallWindowProc(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
4645 static void InitInstance(HINSTANCE hinstance)
4647 static const TCHAR sFont[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
4649 WNDCLASSEX wcFrame;
4650 WNDCLASS wcChild;
4651 ATOM hChildClass;
4652 int col;
4654 INITCOMMONCONTROLSEX icc = {
4655 sizeof(INITCOMMONCONTROLSEX),
4656 ICC_BAR_CLASSES
4659 HDC hdc = GetDC(0);
4661 setlocale(LC_COLLATE, ""); /* set collating rules to local settings for compareName */
4663 InitCommonControlsEx(&icc);
4666 /* register frame window class */
4668 wcFrame.cbSize = sizeof(WNDCLASSEX);
4669 wcFrame.style = 0;
4670 wcFrame.lpfnWndProc = FrameWndProc;
4671 wcFrame.cbClsExtra = 0;
4672 wcFrame.cbWndExtra = 0;
4673 wcFrame.hInstance = hinstance;
4674 wcFrame.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_WINEFILE));
4675 wcFrame.hCursor = LoadCursor(0, IDC_ARROW);
4676 wcFrame.hbrBackground = 0;
4677 wcFrame.lpszMenuName = 0;
4678 wcFrame.lpszClassName = sWINEFILEFRAME;
4679 wcFrame.hIconSm = (HICON)LoadImage(hinstance,
4680 MAKEINTRESOURCE(IDI_WINEFILE),
4681 IMAGE_ICON,
4682 GetSystemMetrics(SM_CXSMICON),
4683 GetSystemMetrics(SM_CYSMICON),
4684 LR_SHARED);
4686 Globals.hframeClass = RegisterClassEx(&wcFrame);
4689 /* register tree windows class */
4691 wcChild.style = CS_CLASSDC|CS_DBLCLKS|CS_VREDRAW;
4692 wcChild.lpfnWndProc = ChildWndProc;
4693 wcChild.cbClsExtra = 0;
4694 wcChild.cbWndExtra = 0;
4695 wcChild.hInstance = hinstance;
4696 wcChild.hIcon = 0;
4697 wcChild.hCursor = LoadCursor(0, IDC_ARROW);
4698 wcChild.hbrBackground = 0;
4699 wcChild.lpszMenuName = 0;
4700 wcChild.lpszClassName = sWINEFILETREE;
4702 hChildClass = RegisterClass(&wcChild);
4705 Globals.haccel = LoadAccelerators(hinstance, MAKEINTRESOURCE(IDA_WINEFILE));
4707 Globals.hfont = CreateFont(-MulDiv(8,GetDeviceCaps(hdc,LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sFont);
4709 ReleaseDC(0, hdc);
4711 Globals.hInstance = hinstance;
4713 #ifdef _SHELL_FOLDERS
4714 CoInitialize(NULL);
4715 CoGetMalloc(MEMCTX_TASK, &Globals.iMalloc);
4716 SHGetDesktopFolder(&Globals.iDesktop);
4717 Globals.cfStrFName = RegisterClipboardFormat(CFSTR_FILENAME);
4718 #endif
4720 /* load column strings */
4721 col = 1;
4723 load_string(g_pos_names[col++], IDS_COL_NAME);
4724 load_string(g_pos_names[col++], IDS_COL_SIZE);
4725 load_string(g_pos_names[col++], IDS_COL_CDATE);
4726 #ifndef _NO_EXTENSIONS
4727 load_string(g_pos_names[col++], IDS_COL_ADATE);
4728 load_string(g_pos_names[col++], IDS_COL_MDATE);
4729 load_string(g_pos_names[col++], IDS_COL_IDX);
4730 load_string(g_pos_names[col++], IDS_COL_LINKS);
4731 #endif
4732 load_string(g_pos_names[col++], IDS_COL_ATTR);
4733 #ifndef _NO_EXTENSIONS
4734 load_string(g_pos_names[col++], IDS_COL_SEC);
4735 #endif
4739 static void show_frame(HWND hwndParent, int cmdshow, LPCTSTR path)
4741 static const TCHAR sMDICLIENT[] = {'M','D','I','C','L','I','E','N','T','\0'};
4743 TCHAR buffer[MAX_PATH], b1[BUFFER_LEN];
4744 ChildWnd* child;
4745 HMENU hMenuFrame, hMenuWindow;
4746 windowOptions opts;
4748 CLIENTCREATESTRUCT ccs;
4750 if (Globals.hMainWnd)
4751 return;
4753 opts = load_registry_settings();
4754 hMenuFrame = LoadMenu(Globals.hInstance, MAKEINTRESOURCE(IDM_WINEFILE));
4755 hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
4757 Globals.hMenuFrame = hMenuFrame;
4758 Globals.hMenuView = GetSubMenu(hMenuFrame, 3);
4759 Globals.hMenuOptions = GetSubMenu(hMenuFrame, 4);
4761 ccs.hWindowMenu = hMenuWindow;
4762 ccs.idFirstChild = IDW_FIRST_CHILD;
4765 /* create main window */
4766 Globals.hMainWnd = CreateWindowEx(0, MAKEINTRESOURCE(Globals.hframeClass), RS(b1,IDS_WINE_FILE), WS_OVERLAPPEDWINDOW,
4767 opts.start_x, opts.start_y, opts.width, opts.height,
4768 hwndParent, Globals.hMenuFrame, Globals.hInstance, 0/*lpParam*/);
4771 Globals.hmdiclient = CreateWindowEx(0, sMDICLIENT, NULL,
4772 WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|WS_BORDER,
4773 0, 0, 0, 0,
4774 Globals.hMainWnd, 0, Globals.hInstance, &ccs);
4776 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
4777 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_SAVESETTINGS, MF_BYCOMMAND);
4779 create_drive_bar();
4782 TBBUTTON toolbarBtns[] = {
4783 {0, 0, 0, BTNS_SEP, {0, 0}, 0, 0},
4784 {0, ID_WINDOW_NEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4785 {1, ID_WINDOW_CASCADE, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4786 {2, ID_WINDOW_TILE_HORZ, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4787 {3, ID_WINDOW_TILE_VERT, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4788 /*TODO
4789 {4, ID_... , TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4790 {5, ID_... , TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4791 */ };
4793 Globals.htoolbar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE,
4794 IDW_TOOLBAR, 2, Globals.hInstance, IDB_TOOLBAR, toolbarBtns,
4795 sizeof(toolbarBtns)/sizeof(TBBUTTON), 16, 15, 16, 15, sizeof(TBBUTTON));
4796 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_TOOL_BAR, MF_BYCOMMAND|MF_CHECKED);
4799 Globals.hstatusbar = CreateStatusWindow(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
4800 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
4802 /* CreateStatusWindow does not accept WS_BORDER
4803 Globals.hstatusbar = CreateWindowEx(WS_EX_NOPARENTNOTIFY, STATUSCLASSNAME, 0,
4804 WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_BORDER|CCS_NODIVIDER, 0,0,0,0,
4805 Globals.hMainWnd, (HMENU)IDW_STATUSBAR, hinstance, 0);*/
4807 /*TODO: read paths from registry */
4809 if (!path || !*path) {
4810 GetCurrentDirectory(MAX_PATH, buffer);
4811 path = buffer;
4814 ShowWindow(Globals.hMainWnd, cmdshow);
4816 #if defined(_SHELL_FOLDERS) && !defined(__WINE__)
4817 /* Shell Namespace as default: */
4818 child = alloc_child_window(path, get_path_pidl(path,Globals.hMainWnd), Globals.hMainWnd);
4819 #else
4820 child = alloc_child_window(path, NULL, Globals.hMainWnd);
4821 #endif
4823 child->pos.showCmd = SW_SHOWMAXIMIZED;
4824 child->pos.rcNormalPosition.left = 0;
4825 child->pos.rcNormalPosition.top = 0;
4826 child->pos.rcNormalPosition.right = 320;
4827 child->pos.rcNormalPosition.bottom = 280;
4829 if (!create_child_window(child))
4830 HeapFree(GetProcessHeap(), 0, child);
4832 SetWindowPlacement(child->hwnd, &child->pos);
4834 Globals.himl = ImageList_LoadBitmap(Globals.hInstance, MAKEINTRESOURCE(IDB_IMAGES), 16, 0, RGB(0,255,0));
4836 Globals.prescan_node = FALSE;
4838 UpdateWindow(Globals.hMainWnd);
4840 if (path && path[0])
4842 int index,count;
4843 TCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
4844 TCHAR fullname[_MAX_FNAME+_MAX_EXT+1];
4846 memset(name,0,sizeof(name));
4847 memset(name,0,sizeof(ext));
4848 _tsplitpath(path, drv, dir, name, ext);
4849 if (name[0])
4851 count = SendMessage(child->right.hwnd, LB_GETCOUNT, 0, 0);
4852 lstrcpy(fullname,name);
4853 lstrcat(fullname,ext);
4855 for (index = 0; index < count; index ++)
4857 Entry* entry = (Entry*) SendMessage(child->right.hwnd, LB_GETITEMDATA, index, 0);
4858 if (lstrcmp(entry->data.cFileName,fullname)==0 ||
4859 lstrcmp(entry->data.cAlternateFileName,fullname)==0)
4861 SendMessage(child->right.hwnd, LB_SETCURSEL, index, 0);
4862 SetFocus(child->right.hwnd);
4863 break;
4870 static void ExitInstance(void)
4872 #ifdef _SHELL_FOLDERS
4873 IShellFolder_Release(Globals.iDesktop);
4874 IMalloc_Release(Globals.iMalloc);
4875 CoUninitialize();
4876 #endif
4878 DeleteObject(Globals.hfont);
4879 ImageList_Destroy(Globals.himl);
4882 #ifdef _NO_EXTENSIONS
4884 /* search for already running win[e]files */
4886 static int g_foundPrevInstance = 0;
4888 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
4890 TCHAR cls[128];
4892 GetClassName(hwnd, cls, 128);
4894 if (!lstrcmp(cls, (LPCTSTR)lparam)) {
4895 g_foundPrevInstance++;
4896 return FALSE;
4899 return TRUE;
4902 /* search for window of given class name to allow only one running instance */
4903 static int find_window_class(LPCTSTR classname)
4905 EnumWindows(EnumWndProc, (LPARAM)classname);
4907 if (g_foundPrevInstance)
4908 return 1;
4910 return 0;
4913 #endif
4915 static int winefile_main(HINSTANCE hinstance, int cmdshow, LPCTSTR path)
4917 MSG msg;
4919 InitInstance(hinstance);
4921 show_frame(0, cmdshow, path);
4923 while(GetMessage(&msg, 0, 0, 0)) {
4924 if (Globals.hmdiclient && TranslateMDISysAccel(Globals.hmdiclient, &msg))
4925 continue;
4927 if (Globals.hMainWnd && TranslateAccelerator(Globals.hMainWnd, Globals.haccel, &msg))
4928 continue;
4930 TranslateMessage(&msg);
4931 DispatchMessage(&msg);
4934 ExitInstance();
4936 return msg.wParam;
4940 #if defined(UNICODE) && defined(_MSC_VER)
4941 int APIENTRY wWinMain(HINSTANCE hinstance, HINSTANCE previnstance, LPWSTR cmdline, int cmdshow)
4942 #else
4943 int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE previnstance, LPSTR cmdline, int cmdshow)
4944 #endif
4946 #ifdef _NO_EXTENSIONS
4947 if (find_window_class(sWINEFILEFRAME))
4948 return 1;
4949 #endif
4951 #if defined(UNICODE) && !defined(_MSC_VER)
4952 { /* convert ANSI cmdline into WCS path string */
4953 TCHAR buffer[MAX_PATH];
4954 MultiByteToWideChar(CP_ACP, 0, cmdline, -1, buffer, MAX_PATH);
4955 winefile_main(hinstance, cmdshow, buffer);
4957 #else
4958 winefile_main(hinstance, cmdshow, cmdline);
4959 #endif
4961 return 0;