winefile: Add traditional Chinese resource.
[wine] / programs / winefile / winefile.c
1 /*
2  * Winefile
3  *
4  * Copyright 2000, 2003, 2004, 2005 Martin Fuchs
5  * Copyright 2006 Jason Green
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #ifdef __WINE__
23 #include "config.h"
24 #include "wine/port.h"
25
26 /* for unix filesystem function calls */
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #endif
31
32 #define COBJMACROS
33
34 #include "winefile.h"
35 #include "resource.h"
36
37 #ifdef _NO_EXTENSIONS
38 #undef _LEFT_FILES
39 #endif
40
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
48
49 #ifdef NONAMELESSUNION
50 #define UNION_MEMBER(x) DUMMYUNIONNAME.x
51 #else
52 #define UNION_MEMBER(x) x
53 #endif
54
55
56 #ifdef _SHELL_FOLDERS
57 #define DEFAULT_SPLIT_POS       300
58 #else
59 #define DEFAULT_SPLIT_POS       200
60 #endif
61
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'};
70
71 enum ENTRY_TYPE {
72         ET_WINDOWS,
73         ET_UNIX,
74 #ifdef _SHELL_FOLDERS
75         ET_SHELL
76 #endif
77 };
78
79 typedef struct _Entry {
80         struct _Entry*  next;
81         struct _Entry*  down;
82         struct _Entry*  up;
83
84         BOOL                    expanded;
85         BOOL                    scanned;
86         int                             level;
87
88         WIN32_FIND_DATA data;
89
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;
101
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;
110
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
124 };
125
126 typedef enum {
127         SORT_NAME,
128         SORT_EXT,
129         SORT_SIZE,
130         SORT_DATE
131 } SORT_ORDER;
132
133 typedef struct {
134         HWND    hwnd;
135 #ifndef _NO_EXTENSIONS
136         HWND    hwndHeader;
137 #endif
138
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];
146
147         BOOL    treePane;
148         int             visible_cols;
149         Entry*  root;
150         Entry*  cur;
151 } Pane;
152
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;
161
162         TCHAR   path[MAX_PATH];
163         TCHAR   filter_pattern[MAX_PATH];
164         int             filter_flags;
165         Root    root;
166
167         SORT_ORDER sortOrder;
168 } ChildWnd;
169
170
171
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);
178
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);
182
183
184 /* globals */
185 WINEFILE_GLOBALS Globals;
186
187 static int last_split;
188
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'};
194
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'};
198
199 static const TCHAR sLongHexFmt[] = {'%','I','6','4','X','\0'};
200 static const TCHAR sLongNumFmt[] = {'%','I','6','4','u','\0'};
201
202
203 /* load resource string */
204 static LPTSTR load_string(LPTSTR buffer, UINT id)
205 {
206         LoadString(Globals.hInstance, id, buffer, BUFFER_LEN);
207
208         return buffer;
209 }
210
211 #define RS(b, i) load_string(b, i)
212
213
214 /* display error message for the specified WIN32 error code */
215 static void display_error(HWND hwnd, DWORD error)
216 {
217         TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
218         PTSTR msg;
219
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);
225
226         LocalFree(msg);
227 }
228
229
230 /* display network error message using WNetGetLastError() */
231 static void display_network_error(HWND hwnd)
232 {
233         TCHAR msg[BUFFER_LEN], provider[BUFFER_LEN], b2[BUFFER_LEN];
234         DWORD error;
235
236         if (WNetGetLastError(&error, msg, BUFFER_LEN, provider, BUFFER_LEN) == NO_ERROR)
237                 MessageBox(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
238 }
239
240 static inline BOOL get_check(HWND hwnd, INT id)
241 {
242         return BST_CHECKED&SendMessageW(GetDlgItem(hwnd, id), BM_GETSTATE, 0, 0);
243 }
244
245 static inline INT set_check(HWND hwnd, INT id, BOOL on)
246 {
247         return SendMessageW(GetDlgItem(hwnd, id), BM_SETCHECK, on?BST_CHECKED:BST_UNCHECKED, 0);
248 }
249
250 static inline void choose_font(HWND hwnd)
251 {
252         WCHAR dlg_name[BUFFER_LEN], dlg_info[BUFFER_LEN];
253         CHOOSEFONTW chFont;
254         LOGFONTW lFont;
255
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;
271
272         if (ChooseFontW(&chFont)) {
273                 HWND childWnd;
274                 HFONT hFontOld;
275
276                 DeleteObject(Globals.hfont);
277                 Globals.hfont = CreateFontIndirectW(&lFont);
278                 hFontOld = SelectObject(hdc, Globals.hfont);
279                 GetTextExtentPoint32W(hdc, sSpace, 1, &Globals.spaceSize);
280
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);
290                 }
291
292                 SelectObject(hdc, hFontOld);
293         }
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);
298         }
299
300         ReleaseDC(hwnd, hdc);
301 }
302
303 #ifdef __WINE__
304
305 #ifdef UNICODE
306
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, ...)
310 {
311         static int (__cdecl *pvswprintf)(WCHAR*, const WCHAR*, va_list) = NULL;
312         va_list ap;
313         int ret;
314
315         if (!pvswprintf) {
316                 HMODULE hModMsvcrt = LoadLibraryA("msvcrt");
317                 pvswprintf = (int(__cdecl*)(WCHAR*,const WCHAR*,va_list)) GetProcAddress(hModMsvcrt, "vswprintf");
318         }
319
320         va_start(ap, fmt);
321         ret = (*pvswprintf)(buffer, fmt, ap);
322         va_end(ap);
323
324         return ret;
325 }
326
327 static LPCWSTR my_wcsrchr(LPCWSTR str, WCHAR c)
328 {
329         LPCWSTR p = str;
330
331         while(*p)
332                 ++p;
333
334         do {
335                 if (--p < str)
336                         return NULL;
337         } while(*p != c);
338
339         return p;
340 }
341
342 #define _tcsrchr my_wcsrchr
343 #else   /* UNICODE */
344 #define _tcsrchr strrchr
345 #endif  /* UNICODE */
346
347 #endif  /* __WINE__ */
348
349
350 /* allocate and initialise a directory entry */
351 static Entry* alloc_entry(void)
352 {
353         Entry* entry = HeapAlloc(GetProcessHeap(), 0, sizeof(Entry));
354
355 #ifdef _SHELL_FOLDERS
356         entry->pidl = NULL;
357         entry->folder = NULL;
358         entry->hicon = 0;
359 #endif
360
361         return entry;
362 }
363
364 /* free a directory entry */
365 static void free_entry(Entry* entry)
366 {
367 #ifdef _SHELL_FOLDERS
368         if (entry->hicon && entry->hicon!=(HICON)-1)
369                 DestroyIcon(entry->hicon);
370
371         if (entry->folder && entry->folder!=Globals.iDesktop)
372                 IShellFolder_Release(entry->folder);
373
374         if (entry->pidl)
375                 IMalloc_Free(Globals.iMalloc, entry->pidl);
376 #endif
377
378         HeapFree(GetProcessHeap(), 0, entry);
379 }
380
381 /* recursively free all child entries */
382 static void free_entries(Entry* dir)
383 {
384         Entry *entry, *next=dir->down;
385
386         if (next) {
387                 dir->down = 0;
388
389                 do {
390                         entry = next;
391                         next = entry->next;
392
393                         free_entries(entry);
394                         free_entry(entry);
395                 } while(next);
396         }
397 }
398
399
400 static void read_directory_win(Entry* dir, LPCTSTR path)
401 {
402         Entry* first_entry = NULL;
403         Entry* last = NULL;
404         Entry* entry;
405
406         int level = dir->level + 1;
407         WIN32_FIND_DATA w32fd;
408         HANDLE hFind;
409 #ifndef _NO_EXTENSIONS
410         HANDLE hFile;
411 #endif
412
413         TCHAR buffer[MAX_PATH], *p;
414         for(p=buffer; *path; )
415                 *p++ = *path++;
416
417         *p++ = '\\';
418         p[0] = '*';
419         p[1] = '\0';
420
421         hFind = FindFirstFile(buffer, &w32fd);
422
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;
429
430                                 if (name[0]=='.' && name[1]=='\0')
431                                         continue;
432                         }
433 #endif
434                         entry = alloc_entry();
435
436                         if (!first_entry)
437                                 first_entry = entry;
438
439                         if (last)
440                                 last->next = entry;
441
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;
448
449 #ifndef _NO_EXTENSIONS
450                         entry->etype = ET_WINDOWS;
451                         entry->bhfi_valid = FALSE;
452
453                         lstrcpy(p, entry->data.cFileName);
454
455                         hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
456                                                                 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
457
458                         if (hFile != INVALID_HANDLE_VALUE) {
459                                 if (GetFileInformationByHandle(hFile, &entry->bhfi))
460                                         entry->bhfi_valid = TRUE;
461
462                                 CloseHandle(hFile);
463                         }
464 #endif
465
466                         last = entry;
467                 } while(FindNextFile(hFind, &w32fd));
468
469                 if (last)
470                         last->next = NULL;
471
472                 FindClose(hFind);
473         }
474
475         dir->down = first_entry;
476         dir->scanned = TRUE;
477 }
478
479
480 static Entry* find_entry_win(Entry* dir, LPCTSTR name)
481 {
482         Entry* entry;
483
484         for(entry=dir->down; entry; entry=entry->next) {
485                 LPCTSTR p = name;
486                 LPCTSTR q = entry->data.cFileName;
487
488                 do {
489                         if (!*p || *p == '\\' || *p == '/')
490                                 return entry;
491                 } while(tolower(*p++) == tolower(*q++));
492
493                 p = name;
494                 q = entry->data.cAlternateFileName;
495
496                 do {
497                         if (!*p || *p == '\\' || *p == '/')
498                                 return entry;
499                 } while(tolower(*p++) == tolower(*q++));
500         }
501
502         return 0;
503 }
504
505
506 static Entry* read_tree_win(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
507 {
508         TCHAR buffer[MAX_PATH];
509         Entry* entry = &root->entry;
510         LPCTSTR s = path;
511         PTSTR d = buffer;
512
513         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
514
515 #ifndef _NO_EXTENSIONS
516         entry->etype = ET_WINDOWS;
517 #endif
518
519         while(entry) {
520                 while(*s && *s != '\\' && *s != '/')
521                         *d++ = *s++;
522
523                 while(*s == '\\' || *s == '/')
524                         s++;
525
526                 *d++ = '\\';
527                 *d = '\0';
528
529                 read_directory(entry, buffer, sortOrder, hwnd);
530
531                 if (entry->down)
532                         entry->expanded = TRUE;
533
534                 if (!*s)
535                         break;
536
537                 entry = find_entry_win(entry, s);
538         }
539
540         SetCursor(old_cursor);
541
542         return entry;
543 }
544
545
546 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
547
548 static BOOL time_to_filetime(const time_t* t, FILETIME* ftime)
549 {
550         struct tm* tm = gmtime(t);
551         SYSTEMTIME stime;
552
553         if (!tm)
554                 return FALSE;
555
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;
563
564         return SystemTimeToFileTime(&stime, ftime);
565 }
566
567 static void read_directory_unix(Entry* dir, LPCTSTR path)
568 {
569         Entry* first_entry = NULL;
570         Entry* last = NULL;
571         Entry* entry;
572         DIR* pdir;
573
574         int level = dir->level + 1;
575 #ifdef UNICODE
576         char cpath[MAX_PATH];
577
578         WideCharToMultiByte(CP_UNIXCP, 0, path, -1, cpath, MAX_PATH, NULL, NULL);
579 #else
580         const char* cpath = path;
581 #endif
582
583         pdir = opendir(cpath);
584
585         if (pdir) {
586                 struct stat st;
587                 struct dirent* ent;
588                 char buffer[MAX_PATH], *p;
589                 const char* s;
590
591                 for(p=buffer,s=cpath; *s; )
592                         *p++ = *s++;
593
594                 if (p==buffer || p[-1]!='/')
595                         *p++ = '/';
596
597                 while((ent=readdir(pdir))) {
598                         entry = alloc_entry();
599
600                         if (!first_entry)
601                                 first_entry = entry;
602
603                         if (last)
604                                 last->next = entry;
605
606                         entry->etype = ET_UNIX;
607
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
614
615                         if (!stat(buffer, &st)) {
616                                 entry->data.dwFileAttributes = p[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
617
618                                 if (S_ISDIR(st.st_mode))
619                                         entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
620
621                                 entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
622                                 entry->data.nFileSizeHigh = st.st_size >> 32;
623
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);
627
628                                 entry->bhfi.nFileIndexLow = ent->d_ino;
629                                 entry->bhfi.nFileIndexHigh = 0;
630
631                                 entry->bhfi.nNumberOfLinks = st.st_nlink;
632
633                                 entry->bhfi_valid = TRUE;
634                         } else {
635                                 entry->data.nFileSizeLow = 0;
636                                 entry->data.nFileSizeHigh = 0;
637                                 entry->bhfi_valid = FALSE;
638                         }
639
640                         entry->down = NULL;
641                         entry->up = dir;
642                         entry->expanded = FALSE;
643                         entry->scanned = FALSE;
644                         entry->level = level;
645
646                         last = entry;
647                 }
648
649                 if (last)
650                         last->next = NULL;
651
652                 closedir(pdir);
653         }
654
655         dir->down = first_entry;
656         dir->scanned = TRUE;
657 }
658
659 static Entry* find_entry_unix(Entry* dir, LPCTSTR name)
660 {
661         Entry* entry;
662
663         for(entry=dir->down; entry; entry=entry->next) {
664                 LPCTSTR p = name;
665                 LPCTSTR q = entry->data.cFileName;
666
667                 do {
668                         if (!*p || *p == '/')
669                                 return entry;
670                 } while(*p++ == *q++);
671         }
672
673         return 0;
674 }
675
676 static Entry* read_tree_unix(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
677 {
678         TCHAR buffer[MAX_PATH];
679         Entry* entry = &root->entry;
680         LPCTSTR s = path;
681         PTSTR d = buffer;
682
683         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
684
685         entry->etype = ET_UNIX;
686
687         while(entry) {
688                 while(*s && *s != '/')
689                         *d++ = *s++;
690
691                 while(*s == '/')
692                         s++;
693
694                 *d++ = '/';
695                 *d = '\0';
696
697                 read_directory(entry, buffer, sortOrder, hwnd);
698
699                 if (entry->down)
700                         entry->expanded = TRUE;
701
702                 if (!*s)
703                         break;
704
705                 entry = find_entry_unix(entry, s);
706         }
707
708         SetCursor(old_cursor);
709
710         return entry;
711 }
712
713 #endif /* !defined(_NO_EXTENSIONS) && defined(__WINE__) */
714
715
716 #ifdef _SHELL_FOLDERS
717
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
725
726
727 static void free_strret(STRRET* str)
728 {
729         if (str->uType == STRRET_WSTR)
730                 IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
731 }
732
733
734 #ifndef UNICODE
735
736 static LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
737 {
738  LPCSTR s;
739  LPSTR d = dest;
740
741  for(s=source; count&&(*d++=*s++); )
742   count--;
743
744  return dest;
745 }
746
747 static void get_strretA(STRRET* str, const SHITEMID* shiid, LPSTR buffer, int len)
748 {
749  switch(str->uType) {
750   case STRRET_WSTR:
751         WideCharToMultiByte(CP_ACP, 0, str->UNION_MEMBER(pOleStr), -1, buffer, len, NULL, NULL);
752         break;
753
754   case STRRET_OFFSET:
755         strcpyn(buffer, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), len);
756         break;
757
758   case STRRET_CSTR:
759         strcpyn(buffer, str->UNION_MEMBER(cStr), len);
760  }
761 }
762
763 static HRESULT path_from_pidlA(IShellFolder* folder, LPITEMIDLIST pidl, LPSTR buffer, int len)
764 {
765         STRRET str;
766
767          /* SHGDN_FORPARSING: get full path of id list */
768         HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
769
770         if (SUCCEEDED(hr)) {
771                 get_strretA(&str, &pidl->mkid, buffer, len);
772                 free_strret(&str);
773         } else
774                 buffer[0] = '\0';
775
776         return hr;
777 }
778
779 #endif
780
781 static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
782 {
783  LPCWSTR s;
784  LPWSTR d = dest;
785
786  for(s=source; count&&(*d++=*s++); )
787   count--;
788
789  return dest;
790 }
791
792 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
793 {
794  switch(str->uType) {
795   case STRRET_WSTR:
796         wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
797         break;
798
799   case STRRET_OFFSET:
800         MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
801         break;
802
803   case STRRET_CSTR:
804         MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
805  }
806 }
807
808
809 static HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
810 {
811         STRRET str;
812
813         HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str);
814
815         if (SUCCEEDED(hr)) {
816                 get_strret(&str, &pidl->mkid, buffer, len);
817                 free_strret(&str);
818         } else
819                 buffer[0] = '\0';
820
821         return hr;
822 }
823
824
825 static HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len)
826 {
827         STRRET str;
828
829          /* SHGDN_FORPARSING: get full path of id list */
830         HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
831
832         if (SUCCEEDED(hr)) {
833                 get_strretW(&str, &pidl->mkid, buffer, len);
834                 free_strret(&str);
835         } else
836                 buffer[0] = '\0';
837
838         return hr;
839 }
840
841
842  /* create an item id list from a file system path */
843
844 static LPITEMIDLIST get_path_pidl(LPTSTR path, HWND hwnd)
845 {
846         LPITEMIDLIST pidl;
847         HRESULT hr;
848         ULONG len;
849
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
856
857         hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
858         if (FAILED(hr))
859                 return NULL;
860
861         return pidl;
862 }
863
864
865  /* convert an item id list from relative to absolute (=relative to the desktop) format */
866
867 static LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd)
868 {
869         if (entry->up && entry->up->etype==ET_SHELL) {
870                 LPITEMIDLIST idl = NULL;
871
872                 while (entry->up) {
873                         idl = ILCombine(ILClone(entry->pidl), idl);
874                         entry = entry->up;
875                 }
876
877                 return idl;
878         } else if (entry->etype == ET_WINDOWS) {
879                 TCHAR path[MAX_PATH];
880
881                 get_path(entry, path);
882
883                 return get_path_pidl(path, hwnd);
884         } else if (entry->pidl)
885                 return ILClone(entry->pidl);
886
887         return NULL;
888 }
889
890
891 static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
892 {
893         IExtractIcon* pExtract;
894
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;
900
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 */
905
906                                 if ((int)ExtractIconEx(path, idx, 0, &hicon, 1) > 0)
907                                         flags &= ~GIL_DONTCACHE;
908                         } else {
909                                 HICON hIconLarge = 0;
910
911                                 HRESULT hr = IExtractIconW_Extract(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
912
913                                 if (SUCCEEDED(hr))
914                                         DestroyIcon(hIconLarge);
915                         }
916
917                         return hicon;
918                 }
919         }
920
921         return 0;
922 }
923
924
925 static Entry* find_entry_shell(Entry* dir, LPCITEMIDLIST pidl)
926 {
927         Entry* entry;
928
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;
933         }
934
935         return 0;
936 }
937
938 static Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, SORT_ORDER sortOrder, HWND hwnd)
939 {
940         Entry* entry = &root->entry;
941         Entry* next;
942         LPITEMIDLIST next_pidl = pidl;
943         IShellFolder* folder;
944         IShellFolder* child = NULL;
945         HRESULT hr;
946
947         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
948
949 #ifndef _NO_EXTENSIONS
950         entry->etype = ET_SHELL;
951 #endif
952
953         folder = Globals.iDesktop;
954
955         while(entry) {
956                 entry->pidl = next_pidl;
957                 entry->folder = folder;
958
959                 if (!pidl->mkid.cb)
960                         break;
961
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;
966
967                 hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
968                 if (!SUCCEEDED(hr))
969                         break;
970
971                 read_directory(entry, NULL, sortOrder, hwnd);
972
973                 if (entry->down)
974                         entry->expanded = TRUE;
975
976                 next = find_entry_shell(entry, next_pidl);
977                 if (!next)
978                         break;
979
980                 folder = child;
981                 entry = next;
982
983                  /* go to next element */
984                 pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
985         }
986
987         SetCursor(old_cursor);
988
989         return entry;
990 }
991
992
993 static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* w32fdata)
994 {
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;
999
1000                 STGMEDIUM medium = {0, {0}, 0};
1001                 FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
1002
1003                 HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
1004
1005                 if (SUCCEEDED(hr)) {
1006                         hr = IDataObject_GetData(pDataObj, &fmt, &medium);
1007
1008                         IDataObject_Release(pDataObj);
1009
1010                         if (SUCCEEDED(hr)) {
1011                                 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
1012                                 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
1013
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;
1019
1020                                         if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1021                                                 w32fdata->nFileSizeLow = fad.nFileSizeLow;
1022                                                 w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
1023                                         }
1024                                 }
1025
1026                                 SetErrorMode(sem_org);
1027
1028                                 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
1029                                 GlobalFree(medium.UNION_MEMBER(hGlobal));
1030                         }
1031                 }
1032         }
1033
1034         if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
1035                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
1036
1037         if (attribs & SFGAO_READONLY)
1038                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
1039
1040         if (attribs & SFGAO_COMPRESSED)
1041                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
1042 }
1043
1044
1045 static void read_directory_shell(Entry* dir, HWND hwnd)
1046 {
1047         IShellFolder* folder = dir->folder;
1048         int level = dir->level + 1;
1049         HRESULT hr;
1050
1051         IShellFolder* child;
1052         IEnumIDList* idlist;
1053
1054         Entry* first_entry = NULL;
1055         Entry* last = NULL;
1056         Entry* entry;
1057
1058         if (!folder)
1059                 return;
1060
1061         hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
1062
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;
1070
1071                         memset(pidls, 0, sizeof(pidls));
1072
1073                         hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
1074                         if (!SUCCEEDED(hr))
1075                                 break;
1076
1077                         if (hr == S_FALSE)
1078                                 break;
1079
1080                         for(n=0; n<cnt; ++n) {
1081                                 entry = alloc_entry();
1082
1083                                 if (!first_entry)
1084                                         first_entry = entry;
1085
1086                                 if (last)
1087                                         last->next = entry;
1088
1089                                 memset(&entry->data, 0, sizeof(WIN32_FIND_DATA));
1090                                 entry->bhfi_valid = FALSE;
1091
1092                                 attribs = ~SFGAO_FILESYSTEM;    /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
1093
1094                                 hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
1095
1096                                 if (SUCCEEDED(hr)) {
1097                                         if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
1098                                                 fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
1099
1100                                                 entry->bhfi_valid = TRUE;
1101                                         } else
1102                                                 attribs = 0;
1103                                 } else
1104                                         attribs = 0;
1105
1106                                 entry->pidl = pidls[n];
1107
1108                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1109                                         hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
1110
1111                                         if (SUCCEEDED(hr))
1112                                                 entry->folder = child;
1113                                         else
1114                                                 entry->folder = NULL;
1115                                 }
1116                                 else
1117                                         entry->folder = NULL;
1118
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*/);
1121
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]);
1126
1127                                         if (!entry->hicon)
1128                                                 entry->hicon = (HICON)-1;       /* don't try again later */
1129                                 }
1130
1131                                 entry->down = NULL;
1132                                 entry->up = dir;
1133                                 entry->expanded = FALSE;
1134                                 entry->scanned = FALSE;
1135                                 entry->level = level;
1136
1137 #ifndef _NO_EXTENSIONS
1138                                 entry->etype = ET_SHELL;
1139                                 entry->bhfi_valid = FALSE;
1140 #endif
1141
1142                                 last = entry;
1143                         }
1144                 }
1145
1146                 IEnumIDList_Release(idlist);
1147         }
1148
1149         if (last)
1150                 last->next = NULL;
1151
1152         dir->down = first_entry;
1153         dir->scanned = TRUE;
1154 }
1155
1156 #endif /* _SHELL_FOLDERS */
1157
1158
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
1166 };
1167
1168 /* distinguish between ".", ".." and any other directory names */
1169 static int TypeOrderFromDirname(LPCTSTR name)
1170 {
1171         if (name[0] == '.') {
1172                 if (name[1] == '\0')
1173                         return TO_DOT;  /* "." */
1174
1175                 if (name[1]=='.' && name[2]=='\0')
1176                         return TO_DOTDOT;       /* ".." */
1177         }
1178
1179         return TO_OTHER_DIR;    /* anything else */
1180 }
1181
1182 /* directories first... */
1183 static int compareType(const WIN32_FIND_DATA* fd1, const WIN32_FIND_DATA* fd2)
1184 {
1185         int order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1186         int order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1187
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);
1192         }
1193
1194         return order2==order1? 0: order1<order2? -1: 1;
1195 }
1196
1197
1198 static int compareName(const void* arg1, const void* arg2)
1199 {
1200         const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1201         const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1202
1203         int cmp = compareType(fd1, fd2);
1204         if (cmp)
1205                 return cmp;
1206
1207         return lstrcmpi(fd1->cFileName, fd2->cFileName);
1208 }
1209
1210 static int compareExt(const void* arg1, const void* arg2)
1211 {
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;
1215
1216         int cmp = compareType(fd1, fd2);
1217         if (cmp)
1218                 return cmp;
1219
1220         name1 = fd1->cFileName;
1221         name2 = fd2->cFileName;
1222
1223         ext1 = _tcsrchr(name1, '.');
1224         ext2 = _tcsrchr(name2, '.');
1225
1226         if (ext1)
1227                 ext1++;
1228         else
1229                 ext1 = sEmpty;
1230
1231         if (ext2)
1232                 ext2++;
1233         else
1234                 ext2 = sEmpty;
1235
1236         cmp = lstrcmpi(ext1, ext2);
1237         if (cmp)
1238                 return cmp;
1239
1240         return lstrcmpi(name1, name2);
1241 }
1242
1243 static int compareSize(const void* arg1, const void* arg2)
1244 {
1245         const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1246         const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1247
1248         int cmp = compareType(fd1, fd2);
1249         if (cmp)
1250                 return cmp;
1251
1252         cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
1253
1254         if (cmp < 0)
1255                 return -1;
1256         else if (cmp > 0)
1257                 return 1;
1258
1259         cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
1260
1261         return cmp<0? -1: cmp>0? 1: 0;
1262 }
1263
1264 static int compareDate(const void* arg1, const void* arg2)
1265 {
1266         const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1267         const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1268
1269         int cmp = compareType(fd1, fd2);
1270         if (cmp)
1271                 return cmp;
1272
1273         return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
1274 }
1275
1276
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 */
1282 };
1283
1284
1285 static void SortDirectory(Entry* dir, SORT_ORDER sortOrder)
1286 {
1287         Entry* entry = dir->down;
1288         Entry** array, **p;
1289         int len;
1290
1291         len = 0;
1292         for(entry=dir->down; entry; entry=entry->next)
1293                 len++;
1294
1295         if (len) {
1296                 array = HeapAlloc(GetProcessHeap(), 0, len*sizeof(Entry*));
1297
1298                 p = array;
1299                 for(entry=dir->down; entry; entry=entry->next)
1300                         *p++ = entry;
1301
1302                 /* call qsort with the appropriate compare function */
1303                 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
1304
1305                 dir->down = array[0];
1306
1307                 for(p=array; --len; p++)
1308                         p[0]->next = p[1];
1309
1310                 (*p)->next = 0;
1311
1312                 HeapFree(GetProcessHeap(), 0, array);
1313         }
1314 }
1315
1316
1317 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
1318 {
1319         TCHAR buffer[MAX_PATH];
1320         Entry* entry;
1321         LPCTSTR s;
1322         PTSTR d;
1323
1324 #ifdef _SHELL_FOLDERS
1325         if (dir->etype == ET_SHELL)
1326         {
1327                 read_directory_shell(dir, hwnd);
1328
1329                 if (Globals.prescan_node) {
1330                         s = path;
1331                         d = buffer;
1332
1333                         while(*s)
1334                                 *d++ = *s++;
1335
1336                         *d++ = '\\';
1337
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);
1342                                 }
1343                 }
1344         }
1345         else
1346 #endif
1347 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1348         if (dir->etype == ET_UNIX)
1349         {
1350                 read_directory_unix(dir, path);
1351
1352                 if (Globals.prescan_node) {
1353                         s = path;
1354                         d = buffer;
1355
1356                         while(*s)
1357                                 *d++ = *s++;
1358
1359                         *d++ = '/';
1360
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);
1366                                 }
1367                 }
1368         }
1369         else
1370 #endif
1371         {
1372                 read_directory_win(dir, path);
1373
1374                 if (Globals.prescan_node) {
1375                         s = path;
1376                         d = buffer;
1377
1378                         while(*s)
1379                                 *d++ = *s++;
1380
1381                         *d++ = '\\';
1382
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);
1388                                 }
1389                 }
1390         }
1391
1392         SortDirectory(dir, sortOrder);
1393 }
1394
1395
1396 static Entry* read_tree(Root* root, LPCTSTR path, LPITEMIDLIST pidl, LPTSTR drv, SORT_ORDER sortOrder, HWND hwnd)
1397 {
1398 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1399         static const TCHAR sSlash[] = {'/', '\0'};
1400 #endif
1401         static const TCHAR sBackslash[] = {'\\', '\0'};
1402
1403 #ifdef _SHELL_FOLDERS
1404         if (pidl)
1405         {
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);
1413
1414                 return read_tree_shell(root, pidl, sortOrder, hwnd);
1415         }
1416         else
1417 #endif
1418 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1419         if (*path == '/')
1420         {
1421                  /* read unix file system tree */
1422                 root->drive_type = GetDriveType(path);
1423
1424                 lstrcat(drv, sSlash);
1425                 load_string(root->volname, IDS_ROOT_FS);
1426                 root->fs_flags = 0;
1427                 load_string(root->fs, IDS_UNIXFS);
1428
1429                 lstrcpy(root->path, sSlash);
1430
1431                 return read_tree_unix(root, path, sortOrder, hwnd);
1432         }
1433 #endif
1434
1435          /* read WIN32 file system tree */
1436         root->drive_type = GetDriveType(path);
1437
1438         lstrcat(drv, sBackslash);
1439         GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1440
1441         lstrcpy(root->path, drv);
1442
1443         return read_tree_win(root, path, sortOrder, hwnd);
1444 }
1445
1446
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
1455 };
1456
1457
1458 static ChildWnd* alloc_child_window(LPCTSTR path, LPITEMIDLIST pidl, HWND hwnd)
1459 {
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'};
1464
1465         ChildWnd* child = HeapAlloc(GetProcessHeap(), 0, sizeof(ChildWnd));
1466         Root* root = &child->root;
1467         Entry* entry;
1468
1469         memset(child, 0, sizeof(ChildWnd));
1470
1471         child->left.treePane = TRUE;
1472         child->left.visible_cols = 0;
1473
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
1480
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;
1488
1489         child->focus_pane = 0;
1490         child->split_pos = DEFAULT_SPLIT_POS;
1491         child->sortOrder = SORT_NAME;
1492         child->header_wdths_ok = FALSE;
1493
1494         if (path)
1495         {
1496                 lstrcpy(child->path, path);
1497
1498                 _tsplitpath(path, drv, dir, name, ext);
1499         }
1500
1501         lstrcpy(child->filter_pattern, sAsterics);
1502         child->filter_flags = TF_ALL;
1503
1504         root->entry.level = 0;
1505
1506         lstrcpy(dir_path, drv);
1507         lstrcat(dir_path, dir);
1508         entry = read_tree(root, dir_path, pidl, drv, child->sortOrder, hwnd);
1509
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);
1516
1517         root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1518
1519         child->left.root = &root->entry;
1520         child->right.root = NULL;
1521
1522         set_curdir(child, entry, 0, hwnd);
1523
1524         return child;
1525 }
1526
1527
1528 /* free all memory associated with a child window */
1529 static void free_child_window(ChildWnd* child)
1530 {
1531         free_entries(&child->root.entry);
1532         HeapFree(GetProcessHeap(), 0, child);
1533 }
1534
1535
1536 /* get full path of specified directory entry */
1537 static void get_path(Entry* dir, PTSTR path)
1538 {
1539         Entry* entry;
1540         int len = 0;
1541         int level = 0;
1542
1543 #ifdef _SHELL_FOLDERS
1544         if (dir->etype == ET_SHELL)
1545         {
1546                 SFGAOF attribs;
1547                 HRESULT hr = S_OK;
1548
1549                 path[0] = '\0';
1550
1551                 attribs = 0;
1552
1553                 if (dir->folder)
1554                         hr = IShellFolder_GetAttributesOf(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1555
1556                 if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1557                         IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1558
1559                         hr = path_from_pidl(parent, dir->pidl, path, MAX_PATH);
1560                 }
1561         }
1562         else
1563 #endif
1564         {
1565                 for(entry=dir; entry; level++) {
1566                         LPCTSTR name;
1567                         int l;
1568
1569                         {
1570                                 LPCTSTR s;
1571                                 name = entry->data.cFileName;
1572                                 s = name;
1573
1574                                 for(l=0; *s && *s != '/' && *s != '\\'; s++)
1575                                         l++;
1576                         }
1577
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;
1583
1584 #ifndef _NO_EXTENSIONS
1585                                         if (entry->etype == ET_UNIX)
1586                                                 path[0] = '/';
1587                                         else
1588 #endif
1589                                         path[0] = '\\';
1590                                 }
1591
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;
1598                         }
1599                 }
1600
1601                 if (!level) {
1602 #ifndef _NO_EXTENSIONS
1603                         if (entry->etype == ET_UNIX)
1604                                 path[len++] = '/';
1605                         else
1606 #endif
1607                                 path[len++] = '\\';
1608                 }
1609
1610                 path[len] = '\0';
1611         }
1612 }
1613
1614 static windowOptions load_registry_settings(void)
1615 {
1616         DWORD size;
1617         DWORD type;
1618         HKEY hKey;
1619         windowOptions opts;
1620         LOGFONT logfont;
1621
1622         RegOpenKeyExW( HKEY_CURRENT_USER, registry_key,
1623                        0, KEY_QUERY_VALUE, &hKey );
1624
1625         size = sizeof(DWORD);
1626
1627         if( RegQueryValueExW( hKey, reg_start_x, NULL, &type,
1628                               (LPBYTE) &opts.start_x, &size ) != ERROR_SUCCESS )
1629                 opts.start_x = CW_USEDEFAULT;
1630
1631         if( RegQueryValueExW( hKey, reg_start_y, NULL, &type,
1632                               (LPBYTE) &opts.start_y, &size ) != ERROR_SUCCESS )
1633                 opts.start_y = CW_USEDEFAULT;
1634
1635         if( RegQueryValueExW( hKey, reg_width, NULL, &type,
1636                               (LPBYTE) &opts.width, &size ) != ERROR_SUCCESS )
1637                 opts.width = CW_USEDEFAULT;
1638
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);
1646
1647         RegCloseKey( hKey );
1648
1649         Globals.hfont = CreateFontIndirect(&logfont);
1650         return opts;
1651 }
1652
1653 static void save_registry_settings(void)
1654 {
1655         WINDOWINFO wi;
1656         HKEY hKey;
1657         INT width, height;
1658         LOGFONT logfont;
1659
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;
1664
1665         if ( RegOpenKeyExW( HKEY_CURRENT_USER, registry_key,
1666                             0, KEY_SET_VALUE, &hKey ) != ERROR_SUCCESS )
1667         {
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 )
1672                 {
1673                         /* FIXME: Cannot create key */
1674                         return;
1675                 }
1676         }
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) );
1689
1690         /* TODO: Save more settings here (List vs. Detailed View, etc.) */
1691         RegCloseKey( hKey );
1692 }
1693
1694 static void resize_frame_rect(HWND hwnd, PRECT prect)
1695 {
1696         int new_top;
1697         RECT rt;
1698
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;
1704         }
1705
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;
1713         }
1714
1715         if (IsWindowVisible(Globals.hstatusbar)) {
1716                 int parts[] = {300, 500};
1717
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;
1722         }
1723
1724         MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1725 }
1726
1727 static void resize_frame(HWND hwnd, int cx, int cy)
1728 {
1729         RECT rect;
1730
1731         rect.left   = 0;
1732         rect.top    = 0;
1733         rect.right  = cx;
1734         rect.bottom = cy;
1735
1736         resize_frame_rect(hwnd, &rect);
1737 }
1738
1739 static void resize_frame_client(HWND hwnd)
1740 {
1741         RECT rect;
1742
1743         GetClientRect(hwnd, &rect);
1744
1745         resize_frame_rect(hwnd, &rect);
1746 }
1747
1748
1749 static HHOOK hcbthook;
1750 static ChildWnd* newchild = NULL;
1751
1752 static LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
1753 {
1754         if (code==HCBT_CREATEWND && newchild) {
1755                 ChildWnd* child = newchild;
1756                 newchild = NULL;
1757
1758                 child->hwnd = (HWND) wparam;
1759                 SetWindowLongPtr(child->hwnd, GWLP_USERDATA, (LPARAM)child);
1760         }
1761
1762         return CallNextHookEx(hcbthook, code, wparam, lparam);
1763 }
1764
1765 static HWND create_child_window(ChildWnd* child)
1766 {
1767         MDICREATESTRUCT mcs;
1768         int idx;
1769
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;
1779
1780         hcbthook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
1781
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;
1787         }
1788
1789         UnhookWindowsHookEx(hcbthook);
1790
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));
1793
1794         idx = SendMessage(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
1795         SendMessage(child->left.hwnd, LB_SETCURSEL, idx, 0);
1796
1797         return child->hwnd;
1798 }
1799
1800
1801 struct ExecuteDialog {
1802         TCHAR   cmd[MAX_PATH];
1803         int             cmdshow;
1804 };
1805
1806 static INT_PTR CALLBACK ExecuteDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1807 {
1808         static struct ExecuteDialog* dlg;
1809
1810         switch(nmsg) {
1811                 case WM_INITDIALOG:
1812                         dlg = (struct ExecuteDialog*) lparam;
1813                         return 1;
1814
1815                 case WM_COMMAND: {
1816                         int id = (int)wparam;
1817
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);
1824
1825                         return 1;}
1826         }
1827
1828         return 0;
1829 }
1830
1831
1832 static INT_PTR CALLBACK DestinationDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1833 {
1834         TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
1835
1836         switch(nmsg) {
1837                 case WM_INITDIALOG:
1838                         SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
1839                         SetWindowText(GetDlgItem(hwnd, 201), (LPCTSTR)lparam);
1840                         return 1;
1841
1842                 case WM_COMMAND: {
1843                         int id = (int)wparam;
1844
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;}
1851
1852                           case IDCANCEL:
1853                                 EndDialog(hwnd, id);
1854                                 break;
1855
1856                           case 254:
1857                                 MessageBox(hwnd, RS(b1,IDS_NO_IMPL), RS(b2,IDS_WINEFILE), MB_OK);
1858                                 break;
1859                         }
1860
1861                         return 1;
1862                 }
1863         }
1864
1865         return 0;
1866 }
1867
1868
1869 struct FilterDialog {
1870         TCHAR   pattern[MAX_PATH];
1871         int             flags;
1872 };
1873
1874 static INT_PTR CALLBACK FilterDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1875 {
1876         static struct FilterDialog* dlg;
1877
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;
1888
1889                 case WM_COMMAND: {
1890                         int id = (int)wparam;
1891
1892                         if (id == IDOK) {
1893                                 int flags = 0;
1894
1895                                 GetWindowText(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern, MAX_PATH);
1896
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;
1902
1903                                 dlg->flags = flags;
1904
1905                                 EndDialog(hwnd, id);
1906                         } else if (id == IDCANCEL)
1907                                 EndDialog(hwnd, id);
1908
1909                         return 1;}
1910         }
1911
1912         return 0;
1913 }
1914
1915
1916 struct PropertiesDialog {
1917         TCHAR   path[MAX_PATH];
1918         Entry   entry;
1919         void*   pVersionData;
1920 };
1921
1922 /* Structure used to store enumerated languages and code pages. */
1923 struct LANGANDCODEPAGE {
1924         WORD wLanguage;
1925         WORD wCodePage;
1926 } *lpTranslate;
1927
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
1942 };
1943
1944 static void PropDlg_DisplayValue(HWND hlbox, HWND hedit)
1945 {
1946         int idx = SendMessage(hlbox, LB_GETCURSEL, 0, 0);
1947
1948         if (idx != LB_ERR) {
1949                 LPCTSTR pValue = (LPCTSTR) SendMessage(hlbox, LB_GETITEMDATA, idx, 0);
1950
1951                 if (pValue)
1952                         SetWindowText(hedit, pValue);
1953         }
1954 }
1955
1956 static void CheckForFileInfo(struct PropertiesDialog* dlg, HWND hwnd, LPCTSTR strFilename)
1957 {
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);
1963
1964         if (dwVersionDataLen) {
1965                 dlg->pVersionData = HeapAlloc(GetProcessHeap(), 0, dwVersionDataLen);
1966
1967                 if (GetFileVersionInfo(strFilename, 0, dwVersionDataLen, dlg->pVersionData)) {
1968                         LPVOID pVal;
1969                         UINT nValLen;
1970
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];
1975
1976                                         sprintf(buffer, "%d.%d.%d.%d",
1977                                                 HIWORD(pFixedFileInfo->dwFileVersionMS), LOWORD(pFixedFileInfo->dwFileVersionMS),
1978                                                 HIWORD(pFixedFileInfo->dwFileVersionLS), LOWORD(pFixedFileInfo->dwFileVersionLS));
1979
1980                                         SetDlgItemTextA(hwnd, IDC_STATIC_PROP_VERSION, buffer);
1981                                 }
1982                         }
1983
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);
1988
1989                                 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
1990
1991                                 /* Read the file description for each language and code page. */
1992                                 for(; pTranslate<pEnd; ++pTranslate) {
1993                                         LPCSTR* p;
1994
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;
2002
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);
2010
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);
2015                                                 }
2016                                         }
2017                                 }
2018
2019                                 SendMessage(hlbox, LB_SETCURSEL, 0, 0);
2020
2021                                 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
2022                         }
2023                 }
2024         }
2025 }
2026
2027 static INT_PTR CALLBACK PropertiesDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2028 {
2029         static struct PropertiesDialog* dlg;
2030
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;
2037
2038                         dlg = (struct PropertiesDialog*) lparam;
2039                         pWFD = (LPWIN32_FIND_DATA) &dlg->entry.data;
2040
2041                         GetWindowText(hwnd, b1, MAX_PATH);
2042                         wsprintf(b2, b1, pWFD->cFileName);
2043                         SetWindowText(hwnd, b2);
2044
2045                         format_date(&pWFD->ftLastWriteTime, b1, COL_DATE|COL_TIME);
2046                         SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_LASTCHANGE), b1);
2047
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);
2052
2053                         SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_FILENAME), pWFD->cFileName);
2054                         SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_PATH), dlg->path);
2055
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);
2061
2062                         CheckForFileInfo(dlg, hwnd, dlg->path);
2063                         return 1;}
2064
2065                 case WM_COMMAND: {
2066                         int id = (int)wparam;
2067
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;
2073                           }
2074
2075                           case BN_CLICKED:
2076                                 if (id==IDOK || id==IDCANCEL)
2077                                         EndDialog(hwnd, id);
2078                         }
2079
2080                         return 1;}
2081
2082                 case WM_NCDESTROY:
2083                         HeapFree(GetProcessHeap(), 0, dlg->pVersionData);
2084                         dlg->pVersionData = NULL;
2085                         break;
2086         }
2087
2088         return 0;
2089 }
2090
2091 static void show_properties_dlg(Entry* entry, HWND hwnd)
2092 {
2093         struct PropertiesDialog dlg;
2094
2095         memset(&dlg, 0, sizeof(struct PropertiesDialog));
2096         get_path(entry, dlg.path);
2097         memcpy(&dlg.entry, entry, sizeof(Entry));
2098
2099         DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_DIALOG_PROPERTIES), hwnd, PropertiesDialogDlgProc, (LPARAM)&dlg);
2100 }
2101
2102
2103 #ifndef _NO_EXTENSIONS
2104
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
2113 };
2114
2115 static void frame_get_clientspace(HWND hwnd, PRECT prect)
2116 {
2117         RECT rt;
2118
2119         if (!IsIconic(hwnd))
2120                 GetClientRect(hwnd, prect);
2121         else {
2122                 WINDOWPLACEMENT wp;
2123
2124                 GetWindowPlacement(hwnd, &wp);
2125
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);
2132         }
2133
2134         if (IsWindowVisible(Globals.htoolbar)) {
2135                 GetClientRect(Globals.htoolbar, &rt);
2136                 prect->top += rt.bottom+2;
2137         }
2138
2139         if (IsWindowVisible(Globals.hdrivebar)) {
2140                 GetClientRect(Globals.hdrivebar, &rt);
2141                 prect->top += rt.bottom+2;
2142         }
2143
2144         if (IsWindowVisible(Globals.hstatusbar)) {
2145                 GetClientRect(Globals.hstatusbar, &rt);
2146                 prect->bottom -= rt.bottom;
2147         }
2148 }
2149
2150 static BOOL toggle_fullscreen(HWND hwnd)
2151 {
2152         RECT rt;
2153
2154         if ((g_fullscreen.mode=!g_fullscreen.mode)) {
2155                 GetWindowRect(hwnd, &g_fullscreen.orgPos);
2156                 g_fullscreen.wasZoomed = IsZoomed(hwnd);
2157
2158                 Frame_CalcFrameClient(hwnd, &rt);
2159                 ClientToScreen(hwnd, (LPPOINT)&rt.left);
2160                 ClientToScreen(hwnd, (LPPOINT)&rt.right);
2161
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;
2166
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);
2172
2173                 if (g_fullscreen.wasZoomed)
2174                         ShowWindow(hwnd, WS_MAXIMIZE);
2175         }
2176
2177         return g_fullscreen.mode;
2178 }
2179
2180 static void fullscreen_move(HWND hwnd)
2181 {
2182         RECT rt, pos;
2183         GetWindowRect(hwnd, &pos);
2184
2185         Frame_CalcFrameClient(hwnd, &rt);
2186         ClientToScreen(hwnd, (LPPOINT)&rt.left);
2187         ClientToScreen(hwnd, (LPPOINT)&rt.right);
2188
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;
2193
2194         MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2195 }
2196
2197 #endif
2198
2199
2200 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
2201 {
2202         BOOL vis = IsWindowVisible(hchild);
2203
2204         CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
2205
2206         ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
2207
2208 #ifndef _NO_EXTENSIONS
2209         if (g_fullscreen.mode)
2210                 fullscreen_move(hwnd);
2211 #endif
2212
2213         resize_frame_client(hwnd);
2214 }
2215
2216 static BOOL activate_drive_window(LPCTSTR path)
2217 {
2218         TCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
2219         HWND child_wnd;
2220
2221         _tsplitpath(path, drv1, 0, 0, 0);
2222
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);
2226
2227                 if (child) {
2228                         _tsplitpath(child->root.path, drv2, 0, 0, 0);
2229
2230                         if (!lstrcmpi(drv2, drv1)) {
2231                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2232
2233                                 if (IsIconic(child_wnd))
2234                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
2235
2236                                 return TRUE;
2237                         }
2238                 }
2239         }
2240
2241         return FALSE;
2242 }
2243
2244 static BOOL activate_fs_window(LPCTSTR filesys)
2245 {
2246         HWND child_wnd;
2247
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);
2251
2252                 if (child) {
2253                         if (!lstrcmpi(child->root.fs, filesys)) {
2254                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2255
2256                                 if (IsIconic(child_wnd))
2257                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
2258
2259                                 return TRUE;
2260                         }
2261                 }
2262         }
2263
2264         return FALSE;
2265 }
2266
2267 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2268 {
2269         TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2270
2271         switch(nmsg) {
2272                 case WM_CLOSE:
2273                         if (Globals.saveSettings)
2274                                 save_registry_settings();  
2275                         
2276                         DestroyWindow(hwnd);
2277
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;
2286
2287                 case WM_DESTROY:
2288                         PostQuitMessage(0);
2289                         break;
2290
2291                 case WM_INITMENUPOPUP: {
2292                         HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2293
2294                         if (!SendMessage(hwndClient, WM_INITMENUPOPUP, wparam, lparam))
2295                                 return 0;
2296                         break;}
2297
2298                 case WM_COMMAND: {
2299                         UINT cmd = LOWORD(wparam);
2300                         HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2301
2302                         if (SendMessage(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
2303                                 break;
2304
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;
2310
2311                                 for(i=cmd-ID_DRIVE_FIRST; i--; root++)
2312                                         while(*root)
2313                                                 root++;
2314
2315                                 if (activate_drive_window(root))
2316                                         return 0;
2317
2318                                 _tsplitpath(root, drv, 0, 0, 0);
2319
2320                                 if (!SetCurrentDirectory(drv)) {
2321                                         display_error(hwnd, GetLastError());
2322                                         return 0;
2323                                 }
2324
2325                                 GetCurrentDirectory(MAX_PATH, path); /*TODO: store last directory per drive */
2326                                 child = alloc_child_window(path, NULL, hwnd);
2327
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;
2334
2335                                 case ID_WINDOW_NEW: {
2336                                         TCHAR path[MAX_PATH];
2337                                         ChildWnd* child;
2338
2339                                         GetCurrentDirectory(MAX_PATH, path);
2340                                         child = alloc_child_window(path, NULL, hwnd);
2341
2342                                         if (!create_child_window(child))
2343                                                 HeapFree(GetProcessHeap(), 0, child);
2344                                         break;}
2345
2346                                 case ID_REFRESH:
2347                                         refresh_drives();
2348                                         break;
2349
2350                                 case ID_WINDOW_CASCADE:
2351                                         SendMessage(Globals.hmdiclient, WM_MDICASCADE, 0, 0);
2352                                         break;
2353
2354                                 case ID_WINDOW_TILE_HORZ:
2355                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
2356                                         break;
2357
2358                                 case ID_WINDOW_TILE_VERT:
2359                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_VERTICAL, 0);
2360                                         break;
2361
2362                                 case ID_WINDOW_ARRANGE:
2363                                         SendMessage(Globals.hmdiclient, WM_MDIICONARRANGE, 0, 0);
2364                                         break;
2365
2366                                 case ID_SELECT_FONT:
2367                                         choose_font(hwnd);
2368                                         break;
2369
2370                                 case ID_VIEW_TOOL_BAR:
2371                                         toggle_child(hwnd, cmd, Globals.htoolbar);
2372                                         break;
2373
2374                                 case ID_VIEW_DRIVE_BAR:
2375                                         toggle_child(hwnd, cmd, Globals.hdrivebar);
2376                                         break;
2377
2378                                 case ID_VIEW_STATUSBAR:
2379                                         toggle_child(hwnd, cmd, Globals.hstatusbar);
2380                                         break;
2381
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;
2387
2388                                 case ID_EXECUTE: {
2389                                         struct ExecuteDialog dlg;
2390
2391                                         memset(&dlg, 0, sizeof(struct ExecuteDialog));
2392
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);
2395
2396                                                 if (PtrToUlong(hinst) <= 32)
2397                                                         display_error(hwnd, GetLastError());
2398                                         }
2399                                         break;}
2400
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);
2410                                         }
2411                                         break;}
2412
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);
2422                                         }
2423                                         break;}
2424
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;}
2431
2432                                 case ID_HELP:
2433                                         WinHelp(hwnd, RS(b1,IDS_WINEFILE), HELP_INDEX, 0);
2434                                         break;
2435
2436 #ifndef _NO_EXTENSIONS
2437                                 case ID_VIEW_FULLSCREEN:
2438                                         CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
2439                                         break;
2440
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;
2448
2449                                         if (activate_fs_window(RS(b1,IDS_UNIXFS)))
2450                                                 break;
2451
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);
2459
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;
2468
2469                                         if (activate_fs_window(RS(b1,IDS_SHELL)))
2470                                                 break;
2471
2472                                         GetCurrentDirectory(MAX_PATH, path);
2473                                         child = alloc_child_window(path, get_path_pidl(path,hwnd), hwnd);
2474
2475                                         if (!create_child_window(child))
2476                                                 HeapFree(GetProcessHeap(), 0, child);
2477                                         break;}
2478 #endif
2479 #endif
2480
2481                                 /*TODO: There are even more menu items! */
2482
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;
2488
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);
2495
2496                                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2497                         }
2498                         break;}
2499
2500                 case WM_SIZE:
2501                         resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
2502                         break;  /* do not pass message to DefFrameProc */
2503
2504                 case WM_DEVICECHANGE:
2505                         SendMessage(hwnd, WM_COMMAND, MAKELONG(ID_REFRESH,0), 0);
2506                         break;
2507
2508 #ifndef _NO_EXTENSIONS
2509                 case WM_GETMINMAXINFO: {
2510                         LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
2511
2512                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
2513                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
2514                         break;}
2515
2516                 case FRM_CALC_CLIENT:
2517                         frame_get_clientspace(hwnd, (PRECT)lparam);
2518                         return TRUE;
2519 #endif /* _NO_EXTENSIONS */
2520
2521                 default:
2522                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2523         }
2524
2525         return 0;
2526 }
2527
2528
2529 static TCHAR g_pos_names[COLUMNS][20] = {
2530         {'\0'}  /* symbol */
2531 };
2532
2533 static const int g_pos_align[] = {
2534         0,
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
2548 };
2549
2550 static void resize_tree(ChildWnd* child, int cx, int cy)
2551 {
2552         HDWP hdwp = BeginDeferWindowPos(4);
2553         RECT rt;
2554
2555         rt.left   = 0;
2556         rt.top    = 0;
2557         rt.right  = cx;
2558         rt.bottom = cy;
2559
2560         cx = child->split_pos + SPLIT_WIDTH/2;
2561
2562 #ifndef _NO_EXTENSIONS
2563         {
2564                 WINDOWPOS wp;
2565                 HD_LAYOUT hdl;
2566
2567                 hdl.prc   = &rt;
2568                 hdl.pwpos = &wp;
2569
2570                 SendMessage(child->left.hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hdl);
2571
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);
2576         }
2577 #endif /* _NO_EXTENSIONS */
2578
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);
2581
2582         EndDeferWindowPos(hdwp);
2583 }
2584
2585
2586 #ifndef _NO_EXTENSIONS
2587
2588 static HWND create_header(HWND parent, Pane* pane, UINT id)
2589 {
2590         HD_ITEM hdi;
2591         int idx;
2592
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;
2597
2598         SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), FALSE);
2599
2600         hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
2601
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);
2607         }
2608
2609         return hwnd;
2610 }
2611
2612 #endif /* _NO_EXTENSIONS */
2613
2614
2615 static void init_output(HWND hwnd)
2616 {
2617         static const WCHAR s1000[] = {'1','0','0','0','\0'};
2618         WCHAR b[16];
2619         HFONT old_font;
2620         HDC hdc = GetDC(hwnd);
2621
2622         if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, s1000, 0, b, 16) > 4)
2623                 Globals.num_sep = b[1];
2624         else
2625                 Globals.num_sep = '.';
2626
2627         old_font = SelectObject(hdc, Globals.hfont);
2628         GetTextExtentPoint32W(hdc, sSpace, 1, &Globals.spaceSize);
2629         SelectObject(hdc, old_font);
2630         ReleaseDC(hwnd, hdc);
2631 }
2632
2633 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
2634
2635
2636 /* calculate preferred width for all visible columns */
2637
2638 static BOOL calc_widths(Pane* pane, BOOL anyway)
2639 {
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;
2647
2648         if (!anyway) {
2649                 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2650                 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2651         }
2652
2653         for(col=0; col<COLUMNS; col++)
2654                 pane->widths[col] = 0;
2655
2656         hdc = GetDC(pane->hwnd);
2657         hfontOld = SelectObject(hdc, Globals.hfont);
2658
2659         for(cnt=0; cnt<entries; cnt++) {
2660                 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2661
2662                 DRAWITEMSTRUCT dis;
2663
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; */
2676
2677                 draw_item(pane, &dis, entry, COLUMNS);
2678         }
2679
2680         SelectObject(hdc, hfontOld);
2681         ReleaseDC(pane->hwnd, hdc);
2682
2683         x = 0;
2684         for(col=0; col<COLUMNS; col++) {
2685                 pane->positions[col] = x;
2686                 cx = pane->widths[col];
2687
2688                 if (cx) {
2689                         cx += spc;
2690
2691                         if (cx < IMAGE_WIDTH)
2692                                 cx = IMAGE_WIDTH;
2693
2694                         pane->widths[col] = cx;
2695                 }
2696
2697                 x += cx;
2698         }
2699
2700         pane->positions[COLUMNS] = x;
2701
2702         SendMessage(pane->hwnd, LB_SETHORIZONTALEXTENT, x, 0);
2703
2704         /* no change? */
2705         if (!anyway && !memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2706                 return FALSE;
2707
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));
2713
2714                 return FALSE;
2715         }
2716
2717         InvalidateRect(pane->hwnd, 0, TRUE);
2718
2719         return TRUE;
2720 }
2721
2722
2723 /* calculate one preferred column width */
2724
2725 static void calc_single_width(Pane* pane, int col)
2726 {
2727         HFONT hfontOld;
2728         int x, cx;
2729         int entries = SendMessage(pane->hwnd, LB_GETCOUNT, 0, 0);
2730         int cnt;
2731         HDC hdc;
2732
2733         pane->widths[col] = 0;
2734
2735         hdc = GetDC(pane->hwnd);
2736         hfontOld = SelectObject(hdc, Globals.hfont);
2737
2738         for(cnt=0; cnt<entries; cnt++) {
2739                 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2740                 DRAWITEMSTRUCT dis;
2741
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; */
2754
2755                 draw_item(pane, &dis, entry, col);
2756         }
2757
2758         SelectObject(hdc, hfontOld);
2759         ReleaseDC(pane->hwnd, hdc);
2760
2761         cx = pane->widths[col];
2762
2763         if (cx) {
2764                 cx += 3*Globals.spaceSize.cx;
2765
2766                 if (cx < IMAGE_WIDTH)
2767                         cx = IMAGE_WIDTH;
2768         }
2769
2770         pane->widths[col] = cx;
2771
2772         x = pane->positions[col] + cx;
2773
2774         for(; col<COLUMNS; ) {
2775                 pane->positions[++col] = x;
2776                 x += pane->widths[col];
2777         }
2778
2779         SendMessage(pane->hwnd, LB_SETHORIZONTALEXTENT, x, 0);
2780 }
2781
2782
2783 static BOOL pattern_match(LPCTSTR str, LPCTSTR pattern)
2784 {
2785         for( ; *str&&*pattern; str++,pattern++) {
2786                 if (*pattern == '*') {
2787                         do pattern++;
2788                         while(*pattern == '*');
2789
2790                         if (!*pattern)
2791                                 return TRUE;
2792
2793                         for(; *str; str++)
2794                                 if (*str==*pattern && pattern_match(str, pattern))
2795                                         return TRUE;
2796
2797                         return FALSE;
2798                 }
2799                 else if (*str!=*pattern && *pattern!='?')
2800                         return FALSE;
2801         }
2802
2803         if (*str || *pattern)
2804                 if (*pattern!='*' || pattern[1]!='\0')
2805                         return FALSE;
2806
2807         return TRUE;
2808 }
2809
2810 static BOOL pattern_imatch(LPCTSTR str, LPCTSTR pattern)
2811 {
2812         TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2813
2814         lstrcpy(b1, str);
2815         lstrcpy(b2, pattern);
2816         CharUpper(b1);
2817         CharUpper(b2);
2818
2819         return pattern_match(b1, b2);
2820 }
2821
2822
2823 enum FILE_TYPE {
2824         FT_OTHER                = 0,
2825         FT_EXECUTABLE   = 1,
2826         FT_DOCUMENT             = 2
2827 };
2828
2829 static enum FILE_TYPE get_file_type(LPCTSTR filename);
2830
2831
2832 /* insert listbox entries after index idx */
2833
2834 static int insert_entries(Pane* pane, Entry* dir, LPCTSTR pattern, int filter_flags, int idx)
2835 {
2836         Entry* entry = dir;
2837
2838         if (!entry)
2839                 return idx;
2840
2841         ShowWindow(pane->hwnd, SW_HIDE);
2842
2843         for(; entry; entry=entry->next) {
2844 #ifndef _LEFT_FILES
2845                 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2846                         continue;
2847 #endif
2848
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;
2858
2859                         /* filter directories in right pane */
2860                         if (!pane->treePane && !(filter_flags&TF_DIRECTORIES))
2861                                 continue;
2862                 }
2863
2864                 /* filter using the file name pattern */
2865                 if (pattern)
2866                         if (!pattern_imatch(entry->data.cFileName, pattern))
2867                                 continue;
2868
2869                 /* filter system and hidden files */
2870                 if (!(filter_flags&TF_HIDDEN) && (entry->data.dwFileAttributes&(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)))
2871                         continue;
2872
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;
2880
2881                           case FT_DOCUMENT:
2882                                 if (!(filter_flags & TF_DOCUMENTS))
2883                                         continue;
2884                                 break;
2885
2886                           default: /* TF_OTHERS */
2887                                 if (!(filter_flags & TF_OTHERS))
2888                                         continue;
2889                         }
2890
2891                 if (idx != -1)
2892                         idx++;
2893
2894                 SendMessage(pane->hwnd, LB_INSERTSTRING, idx, (LPARAM) entry);
2895
2896                 if (pane->treePane && entry->expanded)
2897                         idx = insert_entries(pane, entry->down, pattern, filter_flags, idx);
2898         }
2899
2900         ShowWindow(pane->hwnd, SW_SHOW);
2901
2902         return idx;
2903 }
2904
2905
2906 static void format_bytes(LPTSTR buffer, LONGLONG bytes)
2907 {
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'};
2911
2912         float fBytes = (float)bytes;
2913
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);
2922 }
2923
2924 static void set_space_status(void)
2925 {
2926         ULARGE_INTEGER ulFreeBytesToCaller, ulTotalBytes, ulFreeBytes;
2927         TCHAR fmt[64], b1[64], b2[64], buffer[BUFFER_LEN];
2928
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);
2935
2936         SendMessage(Globals.hstatusbar, SB_SETTEXT, 0, (LPARAM)buffer);
2937 }
2938
2939
2940 static WNDPROC g_orgTreeWndProc;
2941
2942 static void create_tree_window(HWND parent, Pane* pane, UINT id, UINT id_header, LPCTSTR pattern, int filter_flags)
2943 {
2944         static const TCHAR sListBox[] = {'L','i','s','t','B','o','x','\0'};
2945
2946         static int s_init = 0;
2947         Entry* entry = pane->root;
2948
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);
2952
2953         SetWindowLongPtr(pane->hwnd, GWLP_USERDATA, (LPARAM)pane);
2954         g_orgTreeWndProc = (WNDPROC) SetWindowLongPtr(pane->hwnd, GWLP_WNDPROC, (LPARAM)TreeWndProc);
2955
2956         SendMessage(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2957
2958         /* insert entries into listbox */
2959         if (entry)
2960                 insert_entries(pane, entry, pattern, filter_flags, -1);
2961
2962         /* calculate column widths */
2963         if (!s_init) {
2964                 s_init = 1;
2965                 init_output(pane->hwnd);
2966         }
2967
2968         calc_widths(pane, TRUE);
2969
2970 #ifndef _NO_EXTENSIONS
2971         pane->hwndHeader = create_header(parent, pane, id_header);
2972 #endif
2973 }
2974
2975
2976 static void InitChildWindow(ChildWnd* child)
2977 {
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);
2980 }
2981
2982
2983 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
2984 {
2985         SYSTEMTIME systime;
2986         FILETIME lft;
2987         int len = 0;
2988
2989         *buffer = '\0';
2990
2991         if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2992                 return;
2993
2994         if (!FileTimeToLocalFileTime(ft, &lft))
2995                 {err: lstrcpy(buffer,sQMarks); return;}
2996
2997         if (!FileTimeToSystemTime(&lft, &systime))
2998                 goto err;
2999
3000         if (visible_cols & COL_DATE) {
3001                 len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
3002                 if (!len)
3003                         goto err;
3004         }
3005
3006         if (visible_cols & COL_TIME) {
3007                 if (len)
3008                         buffer[len-1] = ' ';
3009
3010                 buffer[len++] = ' ';
3011
3012                 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
3013                         buffer[len] = '\0';
3014         }
3015 }
3016
3017
3018 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3019 {
3020         RECT rt = {0, 0, 0, 0};
3021
3022         DrawText(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
3023
3024         if (rt.right > pane->widths[col])
3025                 pane->widths[col] = rt.right;
3026 }
3027
3028 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3029 {
3030         RECT rt = {0, 0, 0, 0};
3031
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);*/
3034
3035         DrawText(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
3036         /*FIXME rt (0,0) ??? */
3037
3038         if (rt.right > pane->widths[col])
3039                 pane->widths[col] = rt.right;
3040 }
3041
3042
3043 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str, DWORD flags)
3044 {
3045         int x = dis->rcItem.left;
3046         RECT rt;
3047
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;
3052
3053         DrawText(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
3054 }
3055
3056 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3057 {
3058         int x = dis->rcItem.left;
3059         RECT rt;
3060
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;
3065
3066 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
3067         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
3068
3069         DrawText(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
3070 }
3071
3072 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3073 {
3074         int x = dis->rcItem.left;
3075         RECT rt;
3076         LPCTSTR s = str;
3077         TCHAR b[128];
3078         LPTSTR d = b;
3079         int pos;
3080
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;
3085
3086         if (*s)
3087                 *d++ = *s++;
3088
3089         /* insert number separator characters */
3090         pos = lstrlen(s) % 3;
3091
3092         while(*s)
3093                 if (pos--)
3094                         *d++ = *s++;
3095                 else {
3096                         *d++ = Globals.num_sep;
3097                         pos = 3;
3098                 }
3099
3100         DrawText(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
3101 }
3102
3103
3104 static BOOL is_exe_file(LPCTSTR ext)
3105 {
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'}
3117         };
3118
3119         TCHAR ext_buffer[_MAX_EXT];
3120         const TCHAR (*p)[4];
3121         LPCTSTR s;
3122         LPTSTR d;
3123
3124         for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
3125                 d++;
3126
3127         for(p=executable_extensions; (*p)[0]; p++)
3128                 if (!lstrcmpi(ext_buffer, *p))
3129                         return TRUE;
3130
3131         return FALSE;
3132 }
3133
3134 static BOOL is_registered_type(LPCTSTR ext)
3135 {
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;
3139
3140         return FALSE;
3141 }
3142
3143 static enum FILE_TYPE get_file_type(LPCTSTR filename)
3144 {
3145         LPCTSTR ext = _tcsrchr(filename, '.');
3146         if (!ext)
3147                 ext = sEmpty;
3148
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;
3155 }
3156
3157
3158 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
3159 {
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;
3169
3170         if (entry) {
3171                 attrs = entry->data.dwFileAttributes;
3172
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;
3194                         }
3195                 }
3196         } else {
3197                 attrs = 0;
3198                 img = IMG_NONE;
3199         }
3200
3201         if (pane->treePane) {
3202                 if (entry) {
3203                         img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+TREE_LINE_DX);
3204
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;
3212
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;
3217
3218                                 hrgn = CreateRectRgnIndirect(&rt_clip);
3219
3220                                 if (!GetClipRgn(dis->hDC, hrgn_org)) {
3221                                         DeleteObject(hrgn_org);
3222                                         hrgn_org = 0;
3223                                 }
3224
3225                                 /* HGDIOBJ holdPen = SelectObject(dis->hDC, GetStockObject(BLACK_PEN)); */
3226                                 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
3227                                 DeleteObject(hrgn);
3228
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);
3232
3233                                         x = img_pos - IMAGE_WIDTH/2;
3234
3235                                         do {
3236                                                 x -= IMAGE_WIDTH+TREE_LINE_DX;
3237
3238                                                 if (up->next
3239 #ifndef _LEFT_FILES
3240                                                         && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3241 #endif
3242                                                         ) {
3243                                                         MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3244                                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
3245                                                 }
3246                                         } while((up=up->up) != NULL);
3247                                 }
3248
3249                                 x = img_pos - IMAGE_WIDTH/2;
3250
3251                                 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3252                                 LineTo(dis->hDC, x, y);
3253
3254                                 if (entry->next
3255 #ifndef _LEFT_FILES
3256                                         && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
3257 #endif
3258                                         )
3259                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
3260
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;
3266
3267                                 if (right > pane->widths[col])
3268                                         pane->widths[col] = right;
3269                         }
3270                 } else  {
3271                         img_pos = dis->rcItem.left;
3272                 }
3273         } else {
3274                 img_pos = dis->rcItem.left;
3275
3276                 if (calcWidthCol==col || calcWidthCol==COLUMNS)
3277                         pane->widths[col] = IMAGE_WIDTH;
3278         }
3279
3280         if (calcWidthCol == -1) {
3281                 focusRect.left = img_pos -2;
3282
3283 #ifdef _NO_EXTENSIONS
3284                 if (pane->treePane && entry) {
3285                         RECT rt = {0};
3286
3287                         DrawText(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
3288
3289                         focusRect.right = dis->rcItem.left+pane->positions[col+1]+TREE_LINE_DX + rt.right +2;
3290                 }
3291 #else
3292
3293                 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
3294                         textcolor = COLOR_COMPRESSED;
3295                 else
3296 #endif /* _NO_EXTENSIONS */
3297                         textcolor = RGB(0,0,0);
3298
3299                 if (dis->itemState & ODS_FOCUS) {
3300                         textcolor = RGB(255,255,255);
3301                         bkcolor = COLOR_SELECTION;
3302                 } else {
3303                         bkcolor = RGB(255,255,255);
3304                 }
3305
3306                 hbrush = CreateSolidBrush(bkcolor);
3307                 FillRect(dis->hDC, &focusRect, hbrush);
3308                 DeleteObject(hbrush);
3309
3310                 SetBkMode(dis->hDC, TRANSPARENT);
3311                 SetTextColor(dis->hDC, textcolor);
3312
3313                 cx = pane->widths[col];
3314
3315                 if (cx && img!=IMG_NONE) {
3316                         if (cx > IMAGE_WIDTH)
3317                                 cx = IMAGE_WIDTH;
3318
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);
3327                 }
3328         }
3329
3330         if (!entry)
3331                 return;
3332
3333 #ifdef _NO_EXTENSIONS
3334         if (img >= IMG_FOLDER_UP)
3335                 return;
3336 #endif
3337
3338         col++;
3339
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);
3345
3346         col++;
3347
3348 #ifdef _NO_EXTENSIONS
3349   if (!pane->treePane) {
3350 #endif
3351
3352         /* display file size */
3353         if (visible_cols & COL_SIZE) {
3354 #ifdef _NO_EXTENSIONS
3355                 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
3356 #endif
3357                 {
3358                         ULONGLONG size;
3359
3360                         size = ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow;
3361
3362                         _stprintf(buffer, sLongNumFmt, size);
3363
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 */
3368                 }
3369
3370                 col++;
3371         }
3372
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++;
3382
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 */
3390
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++;
3397         }
3398
3399 #ifndef _NO_EXTENSIONS
3400         if (entry->bhfi_valid) {
3401             ULONGLONG index = ((ULONGLONG)entry->bhfi.nFileIndexHigh << 32) | entry->bhfi.nFileIndexLow;
3402
3403                 if (visible_cols & COL_INDEX) {
3404                         _stprintf(buffer, sLongHexFmt, index);
3405
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);
3410
3411                         col++;
3412                 }
3413
3414                 if (visible_cols & COL_LINKS) {
3415                         wsprintf(buffer, sNumFmt, entry->bhfi.nNumberOfLinks);
3416
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);
3421
3422                         col++;
3423                 }
3424         } else
3425                 col += 2;
3426 #endif /* _NO_EXTENSIONS */
3427
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
3437
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 */
3454                 }
3455
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);
3460
3461                 col++;
3462         }
3463
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'
3473                 };
3474
3475                 DWORD rights = get_access_mask();
3476
3477                 lstrcpy(buffer, sSecTabs);
3478
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';
3491
3492                 output_text(dis, col++, buffer, DT_LEFT, 3, psize);
3493         }
3494
3495         if (flags.description) {
3496                 get_description(buffer);
3497                 output_text(dis, col++, buffer, 0, psize);
3498         }
3499 */
3500
3501 #ifdef _NO_EXTENSIONS
3502   }
3503
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;
3511
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));
3517
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);
3525         }
3526 #endif /* _NO_EXTENSIONS */
3527 }
3528
3529
3530 #ifdef _NO_EXTENSIONS
3531
3532 static void draw_splitbar(HWND hwnd, int x)
3533 {
3534         RECT rt;
3535         HDC hdc = GetDC(hwnd);
3536
3537         GetClientRect(hwnd, &rt);
3538
3539         rt.left = x - SPLIT_WIDTH/2;
3540         rt.right = x + SPLIT_WIDTH/2+1;
3541
3542         InvertRect(hdc, &rt);
3543
3544         ReleaseDC(hwnd, hdc);
3545 }
3546
3547 #endif /* _NO_EXTENSIONS */
3548
3549
3550 #ifndef _NO_EXTENSIONS
3551
3552 static void set_header(Pane* pane)
3553 {
3554         HD_ITEM item;
3555         int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3556         int i=0, x=0;
3557
3558         item.mask = HDI_WIDTH;
3559         item.cxy = 0;
3560
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);
3564         }
3565
3566         if (i < COLUMNS) {
3567                 x += pane->widths[i];
3568                 item.cxy = x - scroll_pos;
3569                 SendMessage(pane->hwndHeader, HDM_SETITEM, i++, (LPARAM) &item);
3570
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);
3575                 }
3576         }
3577 }
3578
3579 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
3580 {
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;
3587
3588                         RECT clnt;
3589                         GetClientRect(pane->hwnd, &clnt);
3590
3591                         pane->widths[idx] += dx;
3592
3593                         for(i=idx; ++i<=COLUMNS; )
3594                                 pane->positions[i] += dx;
3595
3596                         {
3597                                 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3598                                 RECT rt_scr;
3599                                 RECT rt_clip;
3600
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;
3605
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;
3610
3611                                 if (rt_scr.left < 0) rt_scr.left = 0;
3612                                 if (rt_clip.left < 0) rt_clip.left = 0;
3613
3614                                 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
3615
3616                                 rt_clip.right = pane->positions[idx+1];
3617                                 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
3618
3619                                 if (pnmh->code == HDN_ENDTRACK) {
3620                                         SendMessage(pane->hwnd, LB_SETHORIZONTALEXTENT, pane->positions[COLUMNS], 0);
3621
3622                                         if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
3623                                                 set_header(pane);
3624                                 }
3625                         }
3626
3627                         return FALSE;
3628                 }
3629
3630                 case HDN_DIVIDERDBLCLICK: {
3631                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
3632                         HD_ITEM item;
3633
3634                         calc_single_width(pane, phdn->iItem);
3635                         item.mask = HDI_WIDTH;
3636                         item.cxy = pane->widths[phdn->iItem];
3637
3638                         SendMessage(pane->hwndHeader, HDM_SETITEM, phdn->iItem, (LPARAM) &item);
3639                         InvalidateRect(pane->hwnd, 0, TRUE);
3640                         break;}
3641         }
3642
3643         return 0;
3644 }
3645
3646 #endif /* _NO_EXTENSIONS */
3647
3648
3649 static void scan_entry(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3650 {
3651         TCHAR path[MAX_PATH];
3652         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
3653
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;
3658
3659                 if (res==LB_ERR || !sub || sub->level<=entry->level)
3660                         break;
3661
3662                 SendMessage(child->left.hwnd, LB_DELETESTRING, idx+1, 0);
3663         }
3664
3665         /* empty right pane */
3666         SendMessage(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3667
3668         /* release memory */
3669         free_entries(entry);
3670
3671         /* read contents from disk */
3672 #ifdef _SHELL_FOLDERS
3673         if (entry->etype == ET_SHELL)
3674         {
3675                 read_directory(entry, NULL, child->sortOrder, hwnd);
3676         }
3677         else
3678 #endif
3679         {
3680                 get_path(entry, path);
3681                 read_directory(entry, path, child->sortOrder, hwnd);
3682         }
3683
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
3690
3691         child->header_wdths_ok = FALSE;
3692
3693         SetCursor(old_cursor);
3694 }
3695
3696
3697 /* expand a directory entry */
3698
3699 static BOOL expand_entry(ChildWnd* child, Entry* dir)
3700 {
3701         int idx;
3702         Entry* p;
3703
3704         if (!dir || dir->expanded || !dir->down)
3705                 return FALSE;
3706
3707         p = dir->down;
3708
3709         if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
3710                 p = p->next;
3711
3712                 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
3713                                 p->data.cFileName[2]=='\0' && p->next)
3714                         p = p->next;
3715         }
3716
3717         /* no subdirectories ? */
3718         if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
3719                 return FALSE;
3720
3721         idx = SendMessage(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3722
3723         dir->expanded = TRUE;
3724
3725         /* insert entries in left pane */
3726         insert_entries(&child->left, p, NULL, TF_ALL, idx);
3727
3728         if (!child->header_wdths_ok) {
3729                 if (calc_widths(&child->left, FALSE)) {
3730 #ifndef _NO_EXTENSIONS
3731                         set_header(&child->left);
3732 #endif
3733
3734                         child->header_wdths_ok = TRUE;
3735                 }
3736         }
3737
3738         return TRUE;
3739 }
3740
3741
3742 static void collapse_entry(Pane* pane, Entry* dir)
3743 {
3744         int idx = SendMessage(pane->hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3745
3746         ShowWindow(pane->hwnd, SW_HIDE);
3747
3748         /* hide sub entries */
3749         for(;;) {
3750                 LRESULT res = SendMessage(pane->hwnd, LB_GETITEMDATA, idx+1, 0);
3751                 Entry* sub = (Entry*) res;
3752
3753                 if (res==LB_ERR || !sub || sub->level<=dir->level)
3754                         break;
3755
3756                 SendMessage(pane->hwnd, LB_DELETESTRING, idx+1, 0);
3757         }
3758
3759         dir->expanded = FALSE;
3760
3761         ShowWindow(pane->hwnd, SW_SHOW);
3762 }
3763
3764
3765 static void refresh_right_pane(ChildWnd* child)
3766 {
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);
3770
3771 #ifndef _NO_EXTENSIONS
3772         set_header(&child->right);
3773 #endif
3774 }
3775
3776 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3777 {
3778         TCHAR path[MAX_PATH];
3779
3780         if (!entry)
3781                 return;
3782
3783         path[0] = '\0';
3784
3785         child->left.cur = entry;
3786
3787         child->right.root = entry->down? entry->down: entry;
3788         child->right.cur = entry;
3789
3790         if (!entry->scanned)
3791                 scan_entry(child, entry, idx, hwnd);
3792         else
3793                 refresh_right_pane(child);
3794
3795         get_path(entry, path);
3796         lstrcpy(child->path, path);
3797
3798         if (child->hwnd)        /* only change window title, if the window already exists */
3799                 SetWindowText(child->hwnd, path);
3800
3801         if (path[0])
3802                 if (SetCurrentDirectory(path))
3803                         set_space_status();
3804 }
3805
3806
3807 static void refresh_child(ChildWnd* child)
3808 {
3809         TCHAR path[MAX_PATH], drv[_MAX_DRIVE+1];
3810         Entry* entry;
3811         int idx;
3812
3813         get_path(child->left.cur, path);
3814         _tsplitpath(path, drv, NULL, NULL, NULL);
3815
3816         child->right.root = NULL;
3817
3818         scan_entry(child, &child->root.entry, 0, child->hwnd);
3819
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);
3826
3827         if (!entry)
3828                 entry = &child->root.entry;
3829
3830         insert_entries(&child->left, child->root.entry.down, NULL, TF_ALL, 0);
3831
3832         set_curdir(child, entry, 0, child->hwnd);
3833
3834         idx = SendMessage(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
3835         SendMessage(child->left.hwnd, LB_SETCURSEL, idx, 0);
3836 }
3837
3838
3839 static void create_drive_bar(void)
3840 {
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;
3847
3848         GetLogicalDriveStrings(BUFFER_LEN, Globals.drives);
3849
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));
3853
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);
3861
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);
3871
3872         drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3873         SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3874         drivebarBtn.iString++;
3875 #endif
3876
3877         /* register windows drive root strings */
3878         SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)Globals.drives);
3879 #endif
3880
3881         drivebarBtn.idCommand = ID_DRIVE_FIRST;
3882
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;
3895                 }
3896
3897                 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3898                 drivebarBtn.idCommand++;
3899                 drivebarBtn.iString++;
3900
3901                 while(*p++);
3902         }
3903 }
3904
3905 static void refresh_drives(void)
3906 {
3907         RECT rect;
3908
3909         /* destroy drive bar */
3910         DestroyWindow(Globals.hdrivebar);
3911         Globals.hdrivebar = 0;
3912
3913         /* re-create drive bar */
3914         create_drive_bar();
3915
3916         /* update window layout */
3917         GetClientRect(Globals.hMainWnd, &rect);
3918         SendMessage(Globals.hMainWnd, WM_SIZE, 0, MAKELONG(rect.right, rect.bottom));
3919 }
3920
3921
3922 static BOOL launch_file(HWND hwnd, LPCTSTR cmd, UINT nCmdShow)
3923 {
3924         HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3925
3926         if (PtrToUlong(hinst) <= 32) {
3927                 display_error(hwnd, GetLastError());
3928                 return FALSE;
3929         }
3930
3931         return TRUE;
3932 }
3933
3934
3935 static BOOL launch_entry(Entry* entry, HWND hwnd, UINT nCmdShow)
3936 {
3937         TCHAR cmd[MAX_PATH];
3938
3939 #ifdef _SHELL_FOLDERS
3940         if (entry->etype == ET_SHELL) {
3941                 BOOL ret = TRUE;
3942
3943                 SHELLEXECUTEINFO shexinfo;
3944
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);
3954
3955                 if (!ShellExecuteEx(&shexinfo)) {
3956                         display_error(hwnd, GetLastError());
3957                         ret = FALSE;
3958                 }
3959
3960                 if (shexinfo.lpIDList != entry->pidl)
3961                         IMalloc_Free(Globals.iMalloc, shexinfo.lpIDList);
3962
3963                 return ret;
3964         }
3965 #endif
3966
3967         get_path(entry, cmd);
3968
3969          /* start program, open document... */
3970         return launch_file(hwnd, cmd, nCmdShow);
3971 }
3972
3973
3974 static void activate_entry(ChildWnd* child, Pane* pane, HWND hwnd)
3975 {
3976         Entry* entry = pane->cur;
3977
3978         if (!entry)
3979                 return;
3980
3981         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3982                 int scanned_old = entry->scanned;
3983
3984                 if (!scanned_old)
3985                 {
3986                         int idx = SendMessage(child->left.hwnd, LB_GETCURSEL, 0, 0);
3987                         scan_entry(child, entry, idx, hwnd);
3988                 }
3989
3990 #ifndef _NO_EXTENSIONS
3991                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3992                         return;
3993 #endif
3994
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);
4003
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);
4009                         }
4010                 }
4011
4012                 if (!scanned_old) {
4013                         calc_widths(pane, FALSE);
4014
4015 #ifndef _NO_EXTENSIONS
4016                         set_header(pane);
4017 #endif
4018                 }
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);
4024         }
4025 }
4026
4027
4028 static BOOL pane_command(Pane* pane, UINT cmd)
4029 {
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);
4042                         }
4043                         break;
4044
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);
4056                         }
4057                         break;
4058
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
4066
4067                         /* TODO: more command ids... */
4068
4069                 default:
4070                         return FALSE;
4071         }
4072
4073         return TRUE;
4074 }
4075
4076
4077 static void set_sort_order(ChildWnd* child, SORT_ORDER sortOrder)
4078 {
4079         if (child->sortOrder != sortOrder) {
4080                 child->sortOrder = sortOrder;
4081                 refresh_child(child);
4082         }
4083 }
4084
4085 static void update_view_menu(ChildWnd* child)
4086 {
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);
4091 }
4092
4093
4094 static BOOL is_directory(LPCTSTR target)
4095 {
4096         /*TODO correctly handle UNIX paths */
4097         DWORD target_attr = GetFileAttributes(target);
4098
4099         if (target_attr == INVALID_FILE_ATTRIBUTES)
4100                 return FALSE;
4101
4102         return target_attr&FILE_ATTRIBUTE_DIRECTORY? TRUE: FALSE;
4103 }
4104         
4105 static BOOL prompt_target(Pane* pane, LPTSTR source, LPTSTR target)
4106 {
4107         TCHAR path[MAX_PATH];
4108         int len;
4109
4110         get_path(pane->cur, path);
4111
4112         if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_SELECT_DESTINATION), pane->hwnd, DestinationDlgProc, (LPARAM)path) != IDOK)
4113                 return FALSE;
4114
4115         get_path(pane->cur, source);
4116
4117         /* convert relative targets to absolute paths */
4118         if (path[0]!='/' && path[1]!=':') {
4119                 get_path(pane->cur->up, target);
4120                 len = lstrlen(target);
4121
4122                 if (target[len-1]!='\\' && target[len-1]!='/')
4123                         target[len++] = '/';
4124
4125                 lstrcpy(target+len, path);
4126         } else
4127                 lstrcpy(target, path);
4128
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'};
4133
4134                 _tsplitpath(source, NULL, NULL, fname, ext);
4135
4136                 wsprintf(target, sAppend, path, fname, ext);
4137         }
4138
4139         return TRUE;
4140 }
4141
4142
4143 static IContextMenu2* s_pctxmenu2 = NULL;
4144 static IContextMenu3* s_pctxmenu3 = NULL;
4145
4146 static void CtxMenu_reset(void)
4147 {
4148         s_pctxmenu2 = NULL;
4149         s_pctxmenu3 = NULL;
4150 }
4151
4152 static IContextMenu* CtxMenu_query_interfaces(IContextMenu* pcm1)
4153 {
4154         IContextMenu* pcm = NULL;
4155
4156         CtxMenu_reset();
4157
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;
4162
4163         if (pcm) {
4164                 IContextMenu_Release(pcm1);
4165                 return pcm;
4166         } else
4167                 return pcm1;
4168 }
4169
4170 static BOOL CtxMenu_HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam)
4171 {
4172         if (s_pctxmenu3) {
4173                 if (SUCCEEDED(IContextMenu3_HandleMenuMsg(s_pctxmenu3, nmsg, wparam, lparam)))
4174                         return TRUE;
4175         }
4176
4177         if (s_pctxmenu2)
4178                 if (SUCCEEDED(IContextMenu2_HandleMenuMsg(s_pctxmenu2, nmsg, wparam, lparam)))
4179                         return TRUE;
4180
4181         return FALSE;
4182 }
4183
4184
4185 static HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, LPCITEMIDLIST* apidl, int x, int y)
4186 {
4187         IContextMenu* pcm;
4188         BOOL executed = FALSE;
4189
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); */
4192
4193         if (SUCCEEDED(hr)) {
4194                 HMENU hmenu = CreatePopupMenu();
4195
4196                 pcm = CtxMenu_query_interfaces(pcm);
4197
4198                 if (hmenu) {
4199                         hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL);
4200
4201                         if (SUCCEEDED(hr)) {
4202                                 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL);
4203
4204                                 CtxMenu_reset();
4205
4206                                 if (idCmd) {
4207                                   CMINVOKECOMMANDINFO cmi;
4208
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;
4218
4219                                   hr = IContextMenu_InvokeCommand(pcm, &cmi);
4220                                         executed = TRUE;
4221                                 }
4222                         } else
4223                                 CtxMenu_reset();
4224                 }
4225
4226                 IContextMenu_Release(pcm);
4227         }
4228
4229         return FAILED(hr)? hr: executed? S_OK: S_FALSE;
4230 }
4231
4232
4233 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4234 {
4235         ChildWnd* child = (ChildWnd*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
4236         ASSERT(child);
4237
4238         switch(nmsg) {
4239                 case WM_DRAWITEM: {
4240                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
4241                         Entry* entry = (Entry*) dis->itemData;
4242
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;
4249
4250                         return TRUE;}
4251
4252                 case WM_CREATE:
4253                         InitChildWindow(child);
4254                         break;
4255
4256                 case WM_NCDESTROY:
4257                         free_child_window(child);
4258                         SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
4259                         break;
4260
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;}
4278
4279                 case WM_SETCURSOR:
4280                         if (LOWORD(lparam) == HTCLIENT) {
4281                                 POINT pt;
4282                                 GetCursorPos(&pt);
4283                                 ScreenToClient(hwnd, &pt);
4284
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;
4288                                 }
4289                         }
4290                         goto def;
4291
4292                 case WM_LBUTTONDOWN: {
4293                         RECT rt;
4294                         int x = (short)LOWORD(lparam);
4295
4296                         GetClientRect(hwnd, &rt);
4297
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);
4304                         }
4305
4306                         break;}
4307
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();
4320                         }
4321                         break;
4322
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
4329
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));
4344                                 }
4345                         break;
4346
4347                 case WM_MOUSEMOVE:
4348                         if (GetCapture() == hwnd) {
4349                                 RECT rt;
4350                                 int x = (short)LOWORD(lparam);
4351
4352 #ifdef _NO_EXTENSIONS
4353                                 HDC hdc = GetDC(hwnd);
4354                                 GetClientRect(hwnd, &rt);
4355
4356                                 rt.left = last_split-SPLIT_WIDTH/2;
4357                                 rt.right = last_split+SPLIT_WIDTH/2+1;
4358                                 InvertRect(hdc, &rt);
4359
4360                                 last_split = x;
4361                                 rt.left = x-SPLIT_WIDTH/2;
4362                                 rt.right = x+SPLIT_WIDTH/2+1;
4363                                 InvertRect(hdc, &rt);
4364
4365                                 ReleaseDC(hwnd, hdc);
4366 #else
4367                                 GetClientRect(hwnd, &rt);
4368
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);
4378                                 }
4379 #endif
4380                         }
4381                         break;
4382
4383 #ifndef _NO_EXTENSIONS
4384                 case WM_GETMINMAXINFO:
4385                         DefMDIChildProc(hwnd, nmsg, wparam, lparam);
4386
4387                         {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
4388
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 */
4393
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;
4399
4400                 case WM_DISPATCH_COMMAND: {
4401                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4402
4403                         switch(LOWORD(wparam)) {
4404                                 case ID_WINDOW_NEW: {
4405                                         ChildWnd* new_child = alloc_child_window(child->path, NULL, hwnd);
4406
4407                                         if (!create_child_window(new_child))
4408                                                 HeapFree(GetProcessHeap(), 0, new_child);
4409
4410                                         break;}
4411
4412                                 case ID_REFRESH:
4413                                         refresh_drives();
4414                                         refresh_child(child);
4415                                         break;
4416
4417                                 case ID_ACTIVATE:
4418                                         activate_entry(child, pane, hwnd);
4419                                         break;
4420
4421                                 case ID_FILE_MOVE: {
4422                                         TCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4423
4424                                         if (prompt_target(pane, source, target)) {
4425                                                 SHFILEOPSTRUCT shfo = {hwnd, FO_MOVE, source, target};
4426
4427                                                 source[lstrlen(source)+1] = '\0';
4428                                                 target[lstrlen(target)+1] = '\0';
4429
4430                                                 if (!SHFileOperation(&shfo))
4431                                                         refresh_child(child);
4432                                         }
4433                                         break;}
4434
4435                                 case ID_FILE_COPY: {
4436                                         TCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4437
4438                                         if (prompt_target(pane, source, target)) {
4439                                                 SHFILEOPSTRUCT shfo = {hwnd, FO_COPY, source, target};
4440
4441                                                 source[lstrlen(source)+1] = '\0';
4442                                                 target[lstrlen(target)+1] = '\0';
4443
4444                                                 if (!SHFileOperation(&shfo))
4445                                                         refresh_child(child);
4446                                         }
4447                                         break;}
4448
4449                                 case ID_FILE_DELETE: {
4450                                         TCHAR path[BUFFER_LEN];
4451                                         SHFILEOPSTRUCT shfo = {hwnd, FO_DELETE, path, NULL, FOF_ALLOWUNDO};
4452
4453                                         get_path(pane->cur, path);
4454
4455                                         path[lstrlen(path)+1] = '\0';
4456
4457                                         if (!SHFileOperation(&shfo))
4458                                                 refresh_child(child);
4459                                         break;}
4460
4461                                 case ID_VIEW_SORT_NAME:
4462                                         set_sort_order(child, SORT_NAME);
4463                                         break;
4464
4465                                 case ID_VIEW_SORT_TYPE:
4466                                         set_sort_order(child, SORT_EXT);
4467                                         break;
4468
4469                                 case ID_VIEW_SORT_SIZE:
4470                                         set_sort_order(child, SORT_SIZE);
4471                                         break;
4472
4473                                 case ID_VIEW_SORT_DATE:
4474                                         set_sort_order(child, SORT_DATE);
4475                                         break;
4476
4477                                 case ID_VIEW_FILTER: {
4478                                         struct FilterDialog dlg;
4479
4480                                         memset(&dlg, 0, sizeof(struct FilterDialog));
4481                                         lstrcpy(dlg.pattern, child->filter_pattern);
4482                                         dlg.flags = child->filter_flags;
4483
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);
4488                                         }
4489                                         break;}
4490
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;}
4498
4499                                 case ID_EDIT_PROPERTIES:
4500                                         show_properties_dlg(pane->cur, child->hwnd);
4501                                         break;
4502
4503                                 default:
4504                                         return pane_command(pane, LOWORD(wparam));
4505                         }
4506
4507                         return TRUE;}
4508
4509                 case WM_COMMAND: {
4510                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4511
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);
4516
4517                                         if (pane == &child->left)
4518                                                 set_curdir(child, entry, idx, hwnd);
4519                                         else
4520                                                 pane->cur = entry;
4521                                         break;}
4522
4523                                 case LBN_DBLCLK:
4524                                         activate_entry(child, pane, hwnd);
4525                                         break;
4526                         }
4527                         break;}
4528
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
4534
4535 #ifdef _SHELL_FOLDERS
4536                 case WM_CONTEXTMENU: {
4537                         POINT pt, pt_clnt;
4538                         Pane* pane;
4539                         int idx;
4540
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));
4548
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);
4552
4553                         if (idx != -1) {
4554                                 Entry* entry = (Entry*) SendMessage(pane->hwnd, LB_GETITEMDATA, idx, 0);
4555
4556                                 LPITEMIDLIST pidl_abs = get_to_absolute_pidl(entry, hwnd);
4557
4558                                 if (pidl_abs) {
4559                                         IShellFolder* parentFolder;
4560                                         LPCITEMIDLIST pidlLast;
4561
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);
4566
4567                                                 IShellFolder_Release(parentFolder);
4568                                         }
4569
4570                                         IMalloc_Free(Globals.iMalloc, pidl_abs);
4571                                 }
4572                         }
4573                         break;}
4574 #endif
4575
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;
4581
4582                         break;
4583
4584                   case WM_INITMENUPOPUP:
4585                         if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4586                                 return 0;
4587
4588                         update_view_menu(child);
4589                         break;
4590
4591                   case WM_MENUCHAR:     /* only supported by IContextMenu3 */
4592                    if (s_pctxmenu3) {
4593                            LRESULT lResult = 0;
4594
4595                            IContextMenu3_HandleMenuMsg2(s_pctxmenu3, nmsg, wparam, lparam, &lResult);
4596
4597                            return lResult;
4598                    }
4599
4600                    break;
4601
4602                 case WM_SIZE:
4603                         if (wparam != SIZE_MINIMIZED)
4604                                 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
4605                         /* fall through */
4606
4607                 default: def:
4608                         return DefMDIChildProc(hwnd, nmsg, wparam, lparam);
4609         }
4610
4611         return 0;
4612 }
4613
4614
4615 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4616 {
4617         ChildWnd* child = (ChildWnd*) GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
4618         Pane* pane = (Pane*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
4619         ASSERT(child);
4620
4621         switch(nmsg) {
4622 #ifndef _NO_EXTENSIONS
4623                 case WM_HSCROLL:
4624                         set_header(pane);
4625                         break;
4626 #endif
4627
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;
4633
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);
4638                         }
4639         }
4640
4641         return CallWindowProc(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
4642 }
4643
4644
4645 static void InitInstance(HINSTANCE hinstance)
4646 {
4647         static const TCHAR sFont[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
4648
4649         WNDCLASSEX wcFrame;
4650         WNDCLASS wcChild;
4651         ATOM hChildClass;
4652         int col;
4653
4654         INITCOMMONCONTROLSEX icc = {
4655                 sizeof(INITCOMMONCONTROLSEX),
4656                 ICC_BAR_CLASSES
4657         };
4658
4659         HDC hdc = GetDC(0);
4660
4661         setlocale(LC_COLLATE, "");      /* set collating rules to local settings for compareName */
4662
4663         InitCommonControlsEx(&icc);
4664
4665
4666         /* register frame window class */
4667
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);
4685
4686         Globals.hframeClass = RegisterClassEx(&wcFrame);
4687
4688
4689         /* register tree windows class */
4690
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;
4701
4702         hChildClass = RegisterClass(&wcChild);
4703
4704
4705         Globals.haccel = LoadAccelerators(hinstance, MAKEINTRESOURCE(IDA_WINEFILE));
4706
4707         Globals.hfont = CreateFont(-MulDiv(8,GetDeviceCaps(hdc,LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sFont);
4708
4709         ReleaseDC(0, hdc);
4710
4711         Globals.hInstance = hinstance;
4712
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
4719
4720         /* load column strings */
4721         col = 1;
4722
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
4736 }
4737
4738
4739 static void show_frame(HWND hwndParent, int cmdshow, LPCTSTR path)
4740 {
4741         static const TCHAR sMDICLIENT[] = {'M','D','I','C','L','I','E','N','T','\0'};
4742
4743         TCHAR buffer[MAX_PATH], b1[BUFFER_LEN];
4744         ChildWnd* child;
4745         HMENU hMenuFrame, hMenuWindow;
4746         windowOptions opts;
4747
4748         CLIENTCREATESTRUCT ccs;
4749
4750         if (Globals.hMainWnd)
4751                 return;
4752
4753         opts = load_registry_settings();
4754         hMenuFrame = LoadMenu(Globals.hInstance, MAKEINTRESOURCE(IDM_WINEFILE));
4755         hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
4756
4757         Globals.hMenuFrame = hMenuFrame;
4758         Globals.hMenuView = GetSubMenu(hMenuFrame, 3);
4759         Globals.hMenuOptions = GetSubMenu(hMenuFrame, 4);
4760
4761         ccs.hWindowMenu  = hMenuWindow;
4762         ccs.idFirstChild = IDW_FIRST_CHILD;
4763
4764
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*/);
4769
4770
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);
4775   
4776         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
4777         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_SAVESETTINGS, MF_BYCOMMAND);
4778
4779         create_drive_bar();
4780
4781         {
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 */              };
4792
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);
4797         }
4798
4799         Globals.hstatusbar = CreateStatusWindow(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
4800         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
4801
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);*/
4806
4807         /*TODO: read paths from registry */
4808
4809         if (!path || !*path) {
4810                 GetCurrentDirectory(MAX_PATH, buffer);
4811                 path = buffer;
4812         }
4813
4814         ShowWindow(Globals.hMainWnd, cmdshow);
4815
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
4822
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;
4828
4829         if (!create_child_window(child))
4830                 HeapFree(GetProcessHeap(), 0, child);
4831
4832         SetWindowPlacement(child->hwnd, &child->pos);
4833
4834         Globals.himl = ImageList_LoadBitmap(Globals.hInstance, MAKEINTRESOURCE(IDB_IMAGES), 16, 0, RGB(0,255,0));
4835
4836         Globals.prescan_node = FALSE;
4837
4838         UpdateWindow(Globals.hMainWnd);
4839
4840         if (path && path[0])
4841         {
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];
4845
4846                 memset(name,0,sizeof(name));
4847                 memset(name,0,sizeof(ext));
4848                 _tsplitpath(path, drv, dir, name, ext);
4849                 if (name[0])
4850                 {
4851                         count = SendMessage(child->right.hwnd, LB_GETCOUNT, 0, 0);
4852                         lstrcpy(fullname,name);
4853                         lstrcat(fullname,ext);
4854
4855                         for (index = 0; index < count; index ++)
4856                         {
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)
4860                                 {
4861                                         SendMessage(child->right.hwnd, LB_SETCURSEL, index, 0);
4862                                         SetFocus(child->right.hwnd);
4863                                         break;
4864                                 }
4865                         }
4866                 }
4867         }
4868 }
4869
4870 static void ExitInstance(void)
4871 {
4872 #ifdef _SHELL_FOLDERS
4873         IShellFolder_Release(Globals.iDesktop);
4874         IMalloc_Release(Globals.iMalloc);
4875         CoUninitialize();
4876 #endif
4877
4878         DeleteObject(Globals.hfont);
4879         ImageList_Destroy(Globals.himl);
4880 }
4881
4882 #ifdef _NO_EXTENSIONS
4883
4884 /* search for already running win[e]files */
4885
4886 static int g_foundPrevInstance = 0;
4887
4888 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
4889 {
4890         TCHAR cls[128];
4891
4892         GetClassName(hwnd, cls, 128);
4893
4894         if (!lstrcmp(cls, (LPCTSTR)lparam)) {
4895                 g_foundPrevInstance++;
4896                 return FALSE;
4897         }
4898
4899         return TRUE;
4900 }
4901
4902 /* search for window of given class name to allow only one running instance */
4903 static int find_window_class(LPCTSTR classname)
4904 {
4905         EnumWindows(EnumWndProc, (LPARAM)classname);
4906
4907         if (g_foundPrevInstance)
4908                 return 1;
4909
4910         return 0;
4911 }
4912
4913 #endif
4914
4915 static int winefile_main(HINSTANCE hinstance, int cmdshow, LPCTSTR path)
4916 {
4917         MSG msg;
4918   
4919         InitInstance(hinstance);
4920
4921         show_frame(0, cmdshow, path);
4922
4923         while(GetMessage(&msg, 0, 0, 0)) {
4924                 if (Globals.hmdiclient && TranslateMDISysAccel(Globals.hmdiclient, &msg))
4925                         continue;
4926
4927                 if (Globals.hMainWnd && TranslateAccelerator(Globals.hMainWnd, Globals.haccel, &msg))
4928                         continue;
4929
4930                 TranslateMessage(&msg);
4931                 DispatchMessage(&msg);
4932         }
4933
4934         ExitInstance();
4935
4936         return msg.wParam;
4937 }
4938
4939
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
4945 {
4946 #ifdef _NO_EXTENSIONS
4947         if (find_window_class(sWINEFILEFRAME))
4948                 return 1;
4949 #endif
4950
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);
4956         }
4957 #else
4958         winefile_main(hinstance, cmdshow, cmdline);
4959 #endif
4960
4961         return 0;
4962 }