kernel32: Moved QueryWorkingSet and QueryWorkingSetEx implementation to kernel32.
[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 #include "wine/unicode.h"
37
38 #ifdef _NO_EXTENSIONS
39 #undef _LEFT_FILES
40 #endif
41
42 #ifndef _MAX_PATH
43 #define _MAX_DRIVE          3
44 #define _MAX_FNAME          256
45 #define _MAX_DIR            _MAX_FNAME
46 #define _MAX_EXT            _MAX_FNAME
47 #define _MAX_PATH           260
48 #endif
49
50 #ifdef NONAMELESSUNION
51 #define UNION_MEMBER(x) DUMMYUNIONNAME.x
52 #else
53 #define UNION_MEMBER(x) x
54 #endif
55
56
57 #ifdef _SHELL_FOLDERS
58 #define DEFAULT_SPLIT_POS       300
59 #else
60 #define DEFAULT_SPLIT_POS       200
61 #endif
62
63 static const WCHAR registry_key[] = { 'S','o','f','t','w','a','r','e','\\',
64                                       'W','i','n','e','\\',
65                                       'W','i','n','e','F','i','l','e','\0'};
66 static const WCHAR reg_start_x[] = { 's','t','a','r','t','X','\0'};
67 static const WCHAR reg_start_y[] = { 's','t','a','r','t','Y','\0'};
68 static const WCHAR reg_width[] = { 'w','i','d','t','h','\0'};
69 static const WCHAR reg_height[] = { 'h','e','i','g','h','t','\0'};
70 static const WCHAR reg_logfont[] = { 'l','o','g','f','o','n','t','\0'};
71
72 enum ENTRY_TYPE {
73         ET_WINDOWS,
74         ET_UNIX,
75 #ifdef _SHELL_FOLDERS
76         ET_SHELL
77 #endif
78 };
79
80 typedef struct _Entry {
81         struct _Entry*  next;
82         struct _Entry*  down;
83         struct _Entry*  up;
84
85         BOOL                    expanded;
86         BOOL                    scanned;
87         int                             level;
88
89         WIN32_FIND_DATAW        data;
90
91 #ifndef _NO_EXTENSIONS
92         BY_HANDLE_FILE_INFORMATION bhfi;
93         BOOL                    bhfi_valid;
94         enum ENTRY_TYPE etype;
95 #endif
96 #ifdef _SHELL_FOLDERS
97         LPITEMIDLIST    pidl;
98         IShellFolder*   folder;
99         HICON                   hicon;
100 #endif
101 } Entry;
102
103 typedef struct {
104         Entry   entry;
105         WCHAR   path[MAX_PATH];
106         WCHAR   volname[_MAX_FNAME];
107         WCHAR   fs[_MAX_DIR];
108         DWORD   drive_type;
109         DWORD   fs_flags;
110 } Root;
111
112 enum COLUMN_FLAGS {
113         COL_SIZE                = 0x01,
114         COL_DATE                = 0x02,
115         COL_TIME                = 0x04,
116         COL_ATTRIBUTES  = 0x08,
117         COL_DOSNAMES    = 0x10,
118 #ifdef _NO_EXTENSIONS
119         COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES
120 #else
121         COL_INDEX               = 0x20,
122         COL_LINKS               = 0x40,
123         COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES|COL_INDEX|COL_LINKS
124 #endif
125 };
126
127 typedef enum {
128         SORT_NAME,
129         SORT_EXT,
130         SORT_SIZE,
131         SORT_DATE
132 } SORT_ORDER;
133
134 typedef struct {
135         HWND    hwnd;
136 #ifndef _NO_EXTENSIONS
137         HWND    hwndHeader;
138 #endif
139
140 #ifndef _NO_EXTENSIONS
141 #define COLUMNS 10
142 #else
143 #define COLUMNS 5
144 #endif
145         int             widths[COLUMNS];
146         int             positions[COLUMNS+1];
147
148         BOOL    treePane;
149         int             visible_cols;
150         Entry*  root;
151         Entry*  cur;
152 } Pane;
153
154 typedef struct {
155         HWND    hwnd;
156         Pane    left;
157         Pane    right;
158         int             focus_pane;             /* 0: left  1: right */
159         WINDOWPLACEMENT pos;
160         int             split_pos;
161         BOOL    header_wdths_ok;
162
163         WCHAR   path[MAX_PATH];
164         WCHAR   filter_pattern[MAX_PATH];
165         int             filter_flags;
166         Root    root;
167
168         SORT_ORDER sortOrder;
169 } ChildWnd;
170
171
172
173 static void read_directory(Entry* dir, LPCWSTR path, SORT_ORDER sortOrder, HWND hwnd);
174 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd);
175 static void refresh_child(ChildWnd* child);
176 static void refresh_drives(void);
177 static void get_path(Entry* dir, PWSTR path);
178 static void format_date(const FILETIME* ft, WCHAR* buffer, int visible_cols);
179
180 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
181 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
182 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
183
184
185 /* globals */
186 WINEFILE_GLOBALS Globals;
187
188 static int last_split;
189
190 /* some common string constants */
191 static const WCHAR sEmpty[] = {'\0'};
192 static const WCHAR sSpace[] = {' ', '\0'};
193 static const WCHAR sNumFmt[] = {'%','d','\0'};
194 static const WCHAR sQMarks[] = {'?','?','?','\0'};
195
196 /* window class names */
197 static const WCHAR sWINEFILEFRAME[] = {'W','F','S','_','F','r','a','m','e','\0'};
198 static const WCHAR sWINEFILETREE[] = {'W','F','S','_','T','r','e','e','\0'};
199
200 static void format_longlong(LPWSTR ret, ULONGLONG val)
201 {
202     WCHAR buffer[65], *p = &buffer[64];
203
204     *p = 0;
205     do {
206         *(--p) = '0' + val % 10;
207         val /= 10;
208     } while (val);
209     lstrcpyW( ret, p );
210 }
211
212
213 /* load resource string */
214 static LPWSTR load_string(LPWSTR buffer, DWORD size, UINT id)
215 {
216         LoadStringW(Globals.hInstance, id, buffer, size);
217         return buffer;
218 }
219
220 #define RS(b, i) load_string(b, sizeof(b)/sizeof(b[0]), i)
221
222
223 /* display error message for the specified WIN32 error code */
224 static void display_error(HWND hwnd, DWORD error)
225 {
226         WCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
227         PWSTR msg;
228
229         if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
230                 0, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (PWSTR)&msg, 0, NULL))
231                 MessageBoxW(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
232         else
233                 MessageBoxW(hwnd, RS(b1,IDS_ERROR), RS(b2,IDS_WINEFILE), MB_OK);
234
235         LocalFree(msg);
236 }
237
238
239 /* display network error message using WNetGetLastErrorW() */
240 static void display_network_error(HWND hwnd)
241 {
242         WCHAR msg[BUFFER_LEN], provider[BUFFER_LEN], b2[BUFFER_LEN];
243         DWORD error;
244
245         if (WNetGetLastErrorW(&error, msg, BUFFER_LEN, provider, BUFFER_LEN) == NO_ERROR)
246                 MessageBoxW(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
247 }
248
249 static inline BOOL get_check(HWND hwnd, INT id)
250 {
251         return BST_CHECKED&SendMessageW(GetDlgItem(hwnd, id), BM_GETSTATE, 0, 0);
252 }
253
254 static inline INT set_check(HWND hwnd, INT id, BOOL on)
255 {
256         return SendMessageW(GetDlgItem(hwnd, id), BM_SETCHECK, on?BST_CHECKED:BST_UNCHECKED, 0);
257 }
258
259 static inline void choose_font(HWND hwnd)
260 {
261         WCHAR dlg_name[BUFFER_LEN], dlg_info[BUFFER_LEN];
262         CHOOSEFONTW chFont;
263         LOGFONTW lFont;
264
265         HDC hdc = GetDC(hwnd);
266
267         GetObjectW(Globals.hfont, sizeof(LOGFONTW), &lFont);
268
269         chFont.lStructSize = sizeof(CHOOSEFONTW);
270         chFont.hwndOwner = hwnd;
271         chFont.hDC = NULL;
272         chFont.lpLogFont = &lFont;
273         chFont.Flags = CF_SCREENFONTS | CF_FORCEFONTEXIST | CF_LIMITSIZE | CF_NOSCRIPTSEL | CF_INITTOLOGFONTSTRUCT;
274         chFont.rgbColors = RGB(0,0,0);
275         chFont.lCustData = 0;
276         chFont.lpfnHook = NULL;
277         chFont.lpTemplateName = NULL;
278         chFont.hInstance = Globals.hInstance;
279         chFont.lpszStyle = NULL;
280         chFont.nFontType = SIMULATED_FONTTYPE;
281         chFont.nSizeMin = 0;
282         chFont.nSizeMax = 24;
283
284         if (ChooseFontW(&chFont)) {
285                 HWND childWnd;
286                 HFONT hFontOld;
287
288                 DeleteObject(Globals.hfont);
289                 Globals.hfont = CreateFontIndirectW(&lFont);
290                 hFontOld = SelectObject(hdc, Globals.hfont);
291                 GetTextExtentPoint32W(hdc, sSpace, 1, &Globals.spaceSize);
292
293                 /* change font in all open child windows */
294                 for(childWnd=GetWindow(Globals.hmdiclient,GW_CHILD); childWnd; childWnd=GetNextWindow(childWnd,GW_HWNDNEXT)) {
295                         ChildWnd* child = (ChildWnd*) GetWindowLongPtrW(childWnd, GWLP_USERDATA);
296                         SendMessageW(child->left.hwnd, WM_SETFONT, (WPARAM)Globals.hfont, TRUE);
297                         SendMessageW(child->right.hwnd, WM_SETFONT, (WPARAM)Globals.hfont, TRUE);
298                         SendMessageW(child->left.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
299                         SendMessageW(child->right.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
300                         InvalidateRect(child->left.hwnd, NULL, TRUE);
301                         InvalidateRect(child->right.hwnd, NULL, TRUE);
302                 }
303
304                 SelectObject(hdc, hFontOld);
305         }
306         else if (CommDlgExtendedError()) {
307                 LoadStringW(Globals.hInstance, IDS_FONT_SEL_DLG_NAME, dlg_name, BUFFER_LEN);
308                 LoadStringW(Globals.hInstance, IDS_FONT_SEL_ERROR, dlg_info, BUFFER_LEN);
309                 MessageBoxW(hwnd, dlg_info, dlg_name, MB_OK);
310         }
311
312         ReleaseDC(hwnd, hdc);
313 }
314
315
316 /* allocate and initialise a directory entry */
317 static Entry* alloc_entry(void)
318 {
319         Entry* entry = HeapAlloc(GetProcessHeap(), 0, sizeof(Entry));
320
321 #ifdef _SHELL_FOLDERS
322         entry->pidl = NULL;
323         entry->folder = NULL;
324         entry->hicon = 0;
325 #endif
326
327         return entry;
328 }
329
330 /* free a directory entry */
331 static void free_entry(Entry* entry)
332 {
333 #ifdef _SHELL_FOLDERS
334         if (entry->hicon && entry->hicon!=(HICON)-1)
335                 DestroyIcon(entry->hicon);
336
337         if (entry->folder && entry->folder!=Globals.iDesktop)
338                 IShellFolder_Release(entry->folder);
339
340         if (entry->pidl)
341                 IMalloc_Free(Globals.iMalloc, entry->pidl);
342 #endif
343
344         HeapFree(GetProcessHeap(), 0, entry);
345 }
346
347 /* recursively free all child entries */
348 static void free_entries(Entry* dir)
349 {
350         Entry *entry, *next=dir->down;
351
352         if (next) {
353                 dir->down = 0;
354
355                 do {
356                         entry = next;
357                         next = entry->next;
358
359                         free_entries(entry);
360                         free_entry(entry);
361                 } while(next);
362         }
363 }
364
365
366 static void read_directory_win(Entry* dir, LPCWSTR path)
367 {
368         Entry* first_entry = NULL;
369         Entry* last = NULL;
370         Entry* entry;
371
372         int level = dir->level + 1;
373         WIN32_FIND_DATAW w32fd;
374         HANDLE hFind;
375 #ifndef _NO_EXTENSIONS
376         HANDLE hFile;
377 #endif
378
379         WCHAR buffer[MAX_PATH], *p;
380         for(p=buffer; *path; )
381                 *p++ = *path++;
382
383         *p++ = '\\';
384         p[0] = '*';
385         p[1] = '\0';
386
387         hFind = FindFirstFileW(buffer, &w32fd);
388
389         if (hFind != INVALID_HANDLE_VALUE) {
390                 do {
391 #ifdef _NO_EXTENSIONS
392                         /* hide directory entry "." */
393                         if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
394                                 LPCWSTR name = w32fd.cFileName;
395
396                                 if (name[0]=='.' && name[1]=='\0')
397                                         continue;
398                         }
399 #endif
400                         entry = alloc_entry();
401
402                         if (!first_entry)
403                                 first_entry = entry;
404
405                         if (last)
406                                 last->next = entry;
407
408                         memcpy(&entry->data, &w32fd, sizeof(WIN32_FIND_DATAW));
409                         entry->down = NULL;
410                         entry->up = dir;
411                         entry->expanded = FALSE;
412                         entry->scanned = FALSE;
413                         entry->level = level;
414
415 #ifndef _NO_EXTENSIONS
416                         entry->etype = ET_WINDOWS;
417                         entry->bhfi_valid = FALSE;
418
419                         lstrcpyW(p, entry->data.cFileName);
420
421                         hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
422                                                                 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
423
424                         if (hFile != INVALID_HANDLE_VALUE) {
425                                 if (GetFileInformationByHandle(hFile, &entry->bhfi))
426                                         entry->bhfi_valid = TRUE;
427
428                                 CloseHandle(hFile);
429                         }
430 #endif
431
432                         last = entry;
433                 } while(FindNextFileW(hFind, &w32fd));
434
435                 if (last)
436                         last->next = NULL;
437
438                 FindClose(hFind);
439         }
440
441         dir->down = first_entry;
442         dir->scanned = TRUE;
443 }
444
445
446 static Entry* find_entry_win(Entry* dir, LPCWSTR name)
447 {
448         Entry* entry;
449
450         for(entry=dir->down; entry; entry=entry->next) {
451                 LPCWSTR p = name;
452                 LPCWSTR q = entry->data.cFileName;
453
454                 do {
455                         if (!*p || *p == '\\' || *p == '/')
456                                 return entry;
457                 } while(tolower(*p++) == tolower(*q++));
458
459                 p = name;
460                 q = entry->data.cAlternateFileName;
461
462                 do {
463                         if (!*p || *p == '\\' || *p == '/')
464                                 return entry;
465                 } while(tolower(*p++) == tolower(*q++));
466         }
467
468         return 0;
469 }
470
471
472 static Entry* read_tree_win(Root* root, LPCWSTR path, SORT_ORDER sortOrder, HWND hwnd)
473 {
474         WCHAR buffer[MAX_PATH];
475         Entry* entry = &root->entry;
476         LPCWSTR s = path;
477         PWSTR d = buffer;
478
479         HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
480
481 #ifndef _NO_EXTENSIONS
482         entry->etype = ET_WINDOWS;
483 #endif
484
485         while(entry) {
486                 while(*s && *s != '\\' && *s != '/')
487                         *d++ = *s++;
488
489                 while(*s == '\\' || *s == '/')
490                         s++;
491
492                 *d++ = '\\';
493                 *d = '\0';
494
495                 read_directory(entry, buffer, sortOrder, hwnd);
496
497                 if (entry->down)
498                         entry->expanded = TRUE;
499
500                 if (!*s)
501                         break;
502
503                 entry = find_entry_win(entry, s);
504         }
505
506         SetCursor(old_cursor);
507
508         return entry;
509 }
510
511
512 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
513
514 static BOOL time_to_filetime(const time_t* t, FILETIME* ftime)
515 {
516         struct tm* tm = gmtime(t);
517         SYSTEMTIME stime;
518
519         if (!tm)
520                 return FALSE;
521
522         stime.wYear = tm->tm_year+1900;
523         stime.wMonth = tm->tm_mon+1;
524         /*      stime.wDayOfWeek */
525         stime.wDay = tm->tm_mday;
526         stime.wHour = tm->tm_hour;
527         stime.wMinute = tm->tm_min;
528         stime.wSecond = tm->tm_sec;
529
530         return SystemTimeToFileTime(&stime, ftime);
531 }
532
533 static void read_directory_unix(Entry* dir, LPCWSTR path)
534 {
535         Entry* first_entry = NULL;
536         Entry* last = NULL;
537         Entry* entry;
538         DIR* pdir;
539
540         int level = dir->level + 1;
541         char cpath[MAX_PATH];
542
543         WideCharToMultiByte(CP_UNIXCP, 0, path, -1, cpath, MAX_PATH, NULL, NULL);
544         pdir = opendir(cpath);
545
546         if (pdir) {
547                 struct stat st;
548                 struct dirent* ent;
549                 char buffer[MAX_PATH], *p;
550                 const char* s;
551
552                 for(p=buffer,s=cpath; *s; )
553                         *p++ = *s++;
554
555                 if (p==buffer || p[-1]!='/')
556                         *p++ = '/';
557
558                 while((ent=readdir(pdir))) {
559                         entry = alloc_entry();
560
561                         if (!first_entry)
562                                 first_entry = entry;
563
564                         if (last)
565                                 last->next = entry;
566
567                         entry->etype = ET_UNIX;
568
569                         strcpy(p, ent->d_name);
570                         MultiByteToWideChar(CP_UNIXCP, 0, p, -1, entry->data.cFileName, MAX_PATH);
571
572                         if (!stat(buffer, &st)) {
573                                 entry->data.dwFileAttributes = p[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
574
575                                 if (S_ISDIR(st.st_mode))
576                                         entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
577
578                                 entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
579                                 entry->data.nFileSizeHigh = st.st_size >> 32;
580
581                                 memset(&entry->data.ftCreationTime, 0, sizeof(FILETIME));
582                                 time_to_filetime(&st.st_atime, &entry->data.ftLastAccessTime);
583                                 time_to_filetime(&st.st_mtime, &entry->data.ftLastWriteTime);
584
585                                 entry->bhfi.nFileIndexLow = ent->d_ino;
586                                 entry->bhfi.nFileIndexHigh = 0;
587
588                                 entry->bhfi.nNumberOfLinks = st.st_nlink;
589
590                                 entry->bhfi_valid = TRUE;
591                         } else {
592                                 entry->data.nFileSizeLow = 0;
593                                 entry->data.nFileSizeHigh = 0;
594                                 entry->bhfi_valid = FALSE;
595                         }
596
597                         entry->down = NULL;
598                         entry->up = dir;
599                         entry->expanded = FALSE;
600                         entry->scanned = FALSE;
601                         entry->level = level;
602
603                         last = entry;
604                 }
605
606                 if (last)
607                         last->next = NULL;
608
609                 closedir(pdir);
610         }
611
612         dir->down = first_entry;
613         dir->scanned = TRUE;
614 }
615
616 static Entry* find_entry_unix(Entry* dir, LPCWSTR name)
617 {
618         Entry* entry;
619
620         for(entry=dir->down; entry; entry=entry->next) {
621                 LPCWSTR p = name;
622                 LPCWSTR q = entry->data.cFileName;
623
624                 do {
625                         if (!*p || *p == '/')
626                                 return entry;
627                 } while(*p++ == *q++);
628         }
629
630         return 0;
631 }
632
633 static Entry* read_tree_unix(Root* root, LPCWSTR path, SORT_ORDER sortOrder, HWND hwnd)
634 {
635         WCHAR buffer[MAX_PATH];
636         Entry* entry = &root->entry;
637         LPCWSTR s = path;
638         PWSTR d = buffer;
639
640         HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
641
642         entry->etype = ET_UNIX;
643
644         while(entry) {
645                 while(*s && *s != '/')
646                         *d++ = *s++;
647
648                 while(*s == '/')
649                         s++;
650
651                 *d++ = '/';
652                 *d = '\0';
653
654                 read_directory(entry, buffer, sortOrder, hwnd);
655
656                 if (entry->down)
657                         entry->expanded = TRUE;
658
659                 if (!*s)
660                         break;
661
662                 entry = find_entry_unix(entry, s);
663         }
664
665         SetCursor(old_cursor);
666
667         return entry;
668 }
669
670 #endif /* !defined(_NO_EXTENSIONS) && defined(__WINE__) */
671
672
673 #ifdef _SHELL_FOLDERS
674
675 static void free_strret(STRRET* str)
676 {
677         if (str->uType == STRRET_WSTR)
678                 IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
679 }
680
681 static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
682 {
683  LPCWSTR s;
684  LPWSTR d = dest;
685
686  for(s=source; count&&(*d++=*s++); )
687   count--;
688
689  return dest;
690 }
691
692 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
693 {
694  switch(str->uType) {
695   case STRRET_WSTR:
696         wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
697         break;
698
699   case STRRET_OFFSET:
700         MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
701         break;
702
703   case STRRET_CSTR:
704         MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
705  }
706 }
707
708
709 static HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len, SHGDNF flags)
710 {
711         STRRET str;
712
713         HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str);
714
715         if (SUCCEEDED(hr)) {
716                 get_strretW(&str, &pidl->mkid, buffer, len);
717                 free_strret(&str);
718         } else
719                 buffer[0] = '\0';
720
721         return hr;
722 }
723
724
725 static HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len)
726 {
727         STRRET str;
728
729          /* SHGDN_FORPARSING: get full path of id list */
730         HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
731
732         if (SUCCEEDED(hr)) {
733                 get_strretW(&str, &pidl->mkid, buffer, len);
734                 free_strret(&str);
735         } else
736                 buffer[0] = '\0';
737
738         return hr;
739 }
740
741
742  /* create an item id list from a file system path */
743
744 static LPITEMIDLIST get_path_pidl(LPWSTR path, HWND hwnd)
745 {
746         LPITEMIDLIST pidl;
747         HRESULT hr;
748         ULONG len;
749         LPWSTR buffer = path;
750
751         hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
752         if (FAILED(hr))
753                 return NULL;
754
755         return pidl;
756 }
757
758
759  /* convert an item id list from relative to absolute (=relative to the desktop) format */
760
761 static LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd)
762 {
763         if (entry->up && entry->up->etype==ET_SHELL) {
764                 LPITEMIDLIST idl = NULL;
765
766                 while (entry->up) {
767                         idl = ILCombine(ILClone(entry->pidl), idl);
768                         entry = entry->up;
769                 }
770
771                 return idl;
772         } else if (entry->etype == ET_WINDOWS) {
773                 WCHAR path[MAX_PATH];
774
775                 get_path(entry, path);
776
777                 return get_path_pidl(path, hwnd);
778         } else if (entry->pidl)
779                 return ILClone(entry->pidl);
780
781         return NULL;
782 }
783
784
785 static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
786 {
787         IExtractIconW* pExtract;
788
789         if (SUCCEEDED(IShellFolder_GetUIObjectOf(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIconW, 0, (LPVOID*)&pExtract))) {
790                 WCHAR path[_MAX_PATH];
791                 unsigned flags;
792                 HICON hicon;
793                 int idx;
794
795                 if (SUCCEEDED(IExtractIconW_GetIconLocation(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
796                         if (!(flags & GIL_NOTFILENAME)) {
797                                 if (idx == -1)
798                                         idx = 0;        /* special case for some control panel applications */
799
800                                 if ((int)ExtractIconExW(path, idx, 0, &hicon, 1) > 0)
801                                         flags &= ~GIL_DONTCACHE;
802                         } else {
803                                 HICON hIconLarge = 0;
804
805                                 HRESULT hr = IExtractIconW_Extract(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
806
807                                 if (SUCCEEDED(hr))
808                                         DestroyIcon(hIconLarge);
809                         }
810
811                         return hicon;
812                 }
813         }
814
815         return 0;
816 }
817
818
819 static Entry* find_entry_shell(Entry* dir, LPCITEMIDLIST pidl)
820 {
821         Entry* entry;
822
823         for(entry=dir->down; entry; entry=entry->next) {
824                 if (entry->pidl->mkid.cb == pidl->mkid.cb &&
825                         !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb))
826                         return entry;
827         }
828
829         return 0;
830 }
831
832 static Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, SORT_ORDER sortOrder, HWND hwnd)
833 {
834         Entry* entry = &root->entry;
835         Entry* next;
836         LPITEMIDLIST next_pidl = pidl;
837         IShellFolder* folder;
838         IShellFolder* child = NULL;
839         HRESULT hr;
840
841         HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
842
843 #ifndef _NO_EXTENSIONS
844         entry->etype = ET_SHELL;
845 #endif
846
847         folder = Globals.iDesktop;
848
849         while(entry) {
850                 entry->pidl = next_pidl;
851                 entry->folder = folder;
852
853                 if (!pidl->mkid.cb)
854                         break;
855
856                  /* copy first element of item idlist */
857                 next_pidl = IMalloc_Alloc(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT));
858                 memcpy(next_pidl, pidl, pidl->mkid.cb);
859                 ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0;
860
861                 hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
862                 if (FAILED(hr))
863                         break;
864
865                 read_directory(entry, NULL, sortOrder, hwnd);
866
867                 if (entry->down)
868                         entry->expanded = TRUE;
869
870                 next = find_entry_shell(entry, next_pidl);
871                 if (!next)
872                         break;
873
874                 folder = child;
875                 entry = next;
876
877                  /* go to next element */
878                 pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
879         }
880
881         SetCursor(old_cursor);
882
883         return entry;
884 }
885
886
887 static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATAW* w32fdata)
888 {
889         if (!(attribs & SFGAO_FILESYSTEM) ||
890                         FAILED(SHGetDataFromIDListW(folder, pidl, SHGDFIL_FINDDATA, w32fdata, sizeof(WIN32_FIND_DATAW)))) {
891                 WIN32_FILE_ATTRIBUTE_DATA fad;
892                 IDataObject* pDataObj;
893
894                 STGMEDIUM medium = {0, {0}, 0};
895                 FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
896
897                 HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
898
899                 if (SUCCEEDED(hr)) {
900                         hr = IDataObject_GetData(pDataObj, &fmt, &medium);
901
902                         IDataObject_Release(pDataObj);
903
904                         if (SUCCEEDED(hr)) {
905                                 LPCWSTR path = GlobalLock(medium.UNION_MEMBER(hGlobal));
906                                 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
907
908                                 if (GetFileAttributesExW(path, GetFileExInfoStandard, &fad)) {
909                                         w32fdata->dwFileAttributes = fad.dwFileAttributes;
910                                         w32fdata->ftCreationTime = fad.ftCreationTime;
911                                         w32fdata->ftLastAccessTime = fad.ftLastAccessTime;
912                                         w32fdata->ftLastWriteTime = fad.ftLastWriteTime;
913
914                                         if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
915                                                 w32fdata->nFileSizeLow = fad.nFileSizeLow;
916                                                 w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
917                                         }
918                                 }
919
920                                 SetErrorMode(sem_org);
921
922                                 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
923                                 GlobalFree(medium.UNION_MEMBER(hGlobal));
924                         }
925                 }
926         }
927
928         if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
929                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
930
931         if (attribs & SFGAO_READONLY)
932                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
933
934         if (attribs & SFGAO_COMPRESSED)
935                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
936 }
937
938
939 static void read_directory_shell(Entry* dir, HWND hwnd)
940 {
941         IShellFolder* folder = dir->folder;
942         int level = dir->level + 1;
943         HRESULT hr;
944
945         IShellFolder* child;
946         IEnumIDList* idlist;
947
948         Entry* first_entry = NULL;
949         Entry* last = NULL;
950         Entry* entry;
951
952         if (!folder)
953                 return;
954
955         hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
956
957         if (SUCCEEDED(hr)) {
958                 for(;;) {
959 #define FETCH_ITEM_COUNT        32
960                         LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
961                         SFGAOF attribs;
962                         ULONG cnt = 0;
963                         ULONG n;
964
965                         memset(pidls, 0, sizeof(pidls));
966
967                         hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
968                         if (FAILED(hr))
969                                 break;
970
971                         if (hr == S_FALSE)
972                                 break;
973
974                         for(n=0; n<cnt; ++n) {
975                                 entry = alloc_entry();
976
977                                 if (!first_entry)
978                                         first_entry = entry;
979
980                                 if (last)
981                                         last->next = entry;
982
983                                 memset(&entry->data, 0, sizeof(WIN32_FIND_DATAW));
984                                 entry->bhfi_valid = FALSE;
985
986                                 attribs = ~SFGAO_FILESYSTEM;    /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
987
988                                 hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
989
990                                 if (SUCCEEDED(hr)) {
991                                         if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
992                                                 fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
993
994                                                 entry->bhfi_valid = TRUE;
995                                         } else
996                                                 attribs = 0;
997                                 } else
998                                         attribs = 0;
999
1000                                 entry->pidl = pidls[n];
1001
1002                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1003                                         hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
1004
1005                                         if (SUCCEEDED(hr))
1006                                                 entry->folder = child;
1007                                         else
1008                                                 entry->folder = NULL;
1009                                 }
1010                                 else
1011                                         entry->folder = NULL;
1012
1013                                 if (!entry->data.cFileName[0])
1014                                         /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
1015
1016                                  /* get display icons for files and virtual objects */
1017                                 if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
1018                                         !(attribs & SFGAO_FILESYSTEM)) {
1019                                         entry->hicon = extract_icon(folder, pidls[n]);
1020
1021                                         if (!entry->hicon)
1022                                                 entry->hicon = (HICON)-1;       /* don't try again later */
1023                                 }
1024
1025                                 entry->down = NULL;
1026                                 entry->up = dir;
1027                                 entry->expanded = FALSE;
1028                                 entry->scanned = FALSE;
1029                                 entry->level = level;
1030
1031 #ifndef _NO_EXTENSIONS
1032                                 entry->etype = ET_SHELL;
1033                                 entry->bhfi_valid = FALSE;
1034 #endif
1035
1036                                 last = entry;
1037                         }
1038                 }
1039
1040                 IEnumIDList_Release(idlist);
1041         }
1042
1043         if (last)
1044                 last->next = NULL;
1045
1046         dir->down = first_entry;
1047         dir->scanned = TRUE;
1048 }
1049
1050 #endif /* _SHELL_FOLDERS */
1051
1052
1053 /* sort order for different directory/file types */
1054 enum TYPE_ORDER {
1055         TO_DIR = 0,
1056         TO_DOT = 1,
1057         TO_DOTDOT = 2,
1058         TO_OTHER_DIR = 3,
1059         TO_FILE = 4
1060 };
1061
1062 /* distinguish between ".", ".." and any other directory names */
1063 static int TypeOrderFromDirname(LPCWSTR name)
1064 {
1065         if (name[0] == '.') {
1066                 if (name[1] == '\0')
1067                         return TO_DOT;  /* "." */
1068
1069                 if (name[1]=='.' && name[2]=='\0')
1070                         return TO_DOTDOT;       /* ".." */
1071         }
1072
1073         return TO_OTHER_DIR;    /* anything else */
1074 }
1075
1076 /* directories first... */
1077 static int compareType(const WIN32_FIND_DATAW* fd1, const WIN32_FIND_DATAW* fd2)
1078 {
1079         int order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1080         int order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1081
1082         /* Handle "." and ".." as special case and move them at the very first beginning. */
1083         if (order1==TO_DIR && order2==TO_DIR) {
1084                 order1 = TypeOrderFromDirname(fd1->cFileName);
1085                 order2 = TypeOrderFromDirname(fd2->cFileName);
1086         }
1087
1088         return order2==order1? 0: order1<order2? -1: 1;
1089 }
1090
1091
1092 static int compareName(const void* arg1, const void* arg2)
1093 {
1094         const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1095         const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1096
1097         int cmp = compareType(fd1, fd2);
1098         if (cmp)
1099                 return cmp;
1100
1101         return lstrcmpiW(fd1->cFileName, fd2->cFileName);
1102 }
1103
1104 static int compareExt(const void* arg1, const void* arg2)
1105 {
1106         const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1107         const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1108         const WCHAR *name1, *name2, *ext1, *ext2;
1109
1110         int cmp = compareType(fd1, fd2);
1111         if (cmp)
1112                 return cmp;
1113
1114         name1 = fd1->cFileName;
1115         name2 = fd2->cFileName;
1116
1117         ext1 = strrchrW(name1, '.');
1118         ext2 = strrchrW(name2, '.');
1119
1120         if (ext1)
1121                 ext1++;
1122         else
1123                 ext1 = sEmpty;
1124
1125         if (ext2)
1126                 ext2++;
1127         else
1128                 ext2 = sEmpty;
1129
1130         cmp = lstrcmpiW(ext1, ext2);
1131         if (cmp)
1132                 return cmp;
1133
1134         return lstrcmpiW(name1, name2);
1135 }
1136
1137 static int compareSize(const void* arg1, const void* arg2)
1138 {
1139         const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1140         const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1141
1142         int cmp = compareType(fd1, fd2);
1143         if (cmp)
1144                 return cmp;
1145
1146         cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
1147
1148         if (cmp < 0)
1149                 return -1;
1150         else if (cmp > 0)
1151                 return 1;
1152
1153         cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
1154
1155         return cmp<0? -1: cmp>0? 1: 0;
1156 }
1157
1158 static int compareDate(const void* arg1, const void* arg2)
1159 {
1160         const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1161         const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1162
1163         int cmp = compareType(fd1, fd2);
1164         if (cmp)
1165                 return cmp;
1166
1167         return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
1168 }
1169
1170
1171 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
1172         compareName,    /* SORT_NAME */
1173         compareExt,             /* SORT_EXT */
1174         compareSize,    /* SORT_SIZE */
1175         compareDate             /* SORT_DATE */
1176 };
1177
1178
1179 static void SortDirectory(Entry* dir, SORT_ORDER sortOrder)
1180 {
1181         Entry* entry = dir->down;
1182         Entry** array, **p;
1183         int len;
1184
1185         len = 0;
1186         for(entry=dir->down; entry; entry=entry->next)
1187                 len++;
1188
1189         if (len) {
1190                 array = HeapAlloc(GetProcessHeap(), 0, len*sizeof(Entry*));
1191
1192                 p = array;
1193                 for(entry=dir->down; entry; entry=entry->next)
1194                         *p++ = entry;
1195
1196                 /* call qsort with the appropriate compare function */
1197                 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
1198
1199                 dir->down = array[0];
1200
1201                 for(p=array; --len; p++)
1202                         p[0]->next = p[1];
1203
1204                 (*p)->next = 0;
1205
1206                 HeapFree(GetProcessHeap(), 0, array);
1207         }
1208 }
1209
1210
1211 static void read_directory(Entry* dir, LPCWSTR path, SORT_ORDER sortOrder, HWND hwnd)
1212 {
1213         WCHAR buffer[MAX_PATH];
1214         Entry* entry;
1215         LPCWSTR s;
1216         PWSTR d;
1217
1218 #ifdef _SHELL_FOLDERS
1219         if (dir->etype == ET_SHELL)
1220         {
1221                 read_directory_shell(dir, hwnd);
1222
1223                 if (Globals.prescan_node) {
1224                         s = path;
1225                         d = buffer;
1226
1227                         while(*s)
1228                                 *d++ = *s++;
1229
1230                         *d++ = '\\';
1231
1232                         for(entry=dir->down; entry; entry=entry->next)
1233                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1234                                         read_directory_shell(entry, hwnd);
1235                                         SortDirectory(entry, sortOrder);
1236                                 }
1237                 }
1238         }
1239         else
1240 #endif
1241 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1242         if (dir->etype == ET_UNIX)
1243         {
1244                 read_directory_unix(dir, path);
1245
1246                 if (Globals.prescan_node) {
1247                         s = path;
1248                         d = buffer;
1249
1250                         while(*s)
1251                                 *d++ = *s++;
1252
1253                         *d++ = '/';
1254
1255                         for(entry=dir->down; entry; entry=entry->next)
1256                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1257                                         lstrcpyW(d, entry->data.cFileName);
1258                                         read_directory_unix(entry, buffer);
1259                                         SortDirectory(entry, sortOrder);
1260                                 }
1261                 }
1262         }
1263         else
1264 #endif
1265         {
1266                 read_directory_win(dir, path);
1267
1268                 if (Globals.prescan_node) {
1269                         s = path;
1270                         d = buffer;
1271
1272                         while(*s)
1273                                 *d++ = *s++;
1274
1275                         *d++ = '\\';
1276
1277                         for(entry=dir->down; entry; entry=entry->next)
1278                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1279                                         lstrcpyW(d, entry->data.cFileName);
1280                                         read_directory_win(entry, buffer);
1281                                         SortDirectory(entry, sortOrder);
1282                                 }
1283                 }
1284         }
1285
1286         SortDirectory(dir, sortOrder);
1287 }
1288
1289
1290 static Entry* read_tree(Root* root, LPCWSTR path, LPITEMIDLIST pidl, LPWSTR drv, SORT_ORDER sortOrder, HWND hwnd)
1291 {
1292 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1293         static const WCHAR sSlash[] = {'/', '\0'};
1294 #endif
1295         static const WCHAR sBackslash[] = {'\\', '\0'};
1296
1297 #ifdef _SHELL_FOLDERS
1298         if (pidl)
1299         {
1300                  /* read shell namespace tree */
1301                 root->drive_type = DRIVE_UNKNOWN;
1302                 drv[0] = '\\';
1303                 drv[1] = '\0';
1304                 load_string(root->volname, sizeof(root->volname)/sizeof(root->volname[0]), IDS_DESKTOP);
1305                 root->fs_flags = 0;
1306                 load_string(root->fs, sizeof(root->fs)/sizeof(root->fs[0]), IDS_SHELL);
1307
1308                 return read_tree_shell(root, pidl, sortOrder, hwnd);
1309         }
1310         else
1311 #endif
1312 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1313         if (*path == '/')
1314         {
1315                 /* read unix file system tree */
1316                 root->drive_type = GetDriveTypeW(path);
1317
1318                 lstrcatW(drv, sSlash);
1319                 load_string(root->volname, sizeof(root->volname)/sizeof(root->volname[0]), IDS_ROOT_FS);
1320                 root->fs_flags = 0;
1321                 load_string(root->fs, sizeof(root->fs)/sizeof(root->fs[0]), IDS_UNIXFS);
1322
1323                 lstrcpyW(root->path, sSlash);
1324
1325                 return read_tree_unix(root, path, sortOrder, hwnd);
1326         }
1327 #endif
1328
1329          /* read WIN32 file system tree */
1330        root->drive_type = GetDriveTypeW(path);
1331
1332         lstrcatW(drv, sBackslash);
1333         GetVolumeInformationW(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1334
1335         lstrcpyW(root->path, drv);
1336
1337         return read_tree_win(root, path, sortOrder, hwnd);
1338 }
1339
1340
1341 /* flags to filter different file types */
1342 enum TYPE_FILTER {
1343         TF_DIRECTORIES  = 0x01,
1344         TF_PROGRAMS             = 0x02,
1345         TF_DOCUMENTS    = 0x04,
1346         TF_OTHERS               = 0x08,
1347         TF_HIDDEN               = 0x10,
1348         TF_ALL                  = 0x1F
1349 };
1350
1351
1352 static ChildWnd* alloc_child_window(LPCWSTR path, LPITEMIDLIST pidl, HWND hwnd)
1353 {
1354         WCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
1355         WCHAR dir_path[MAX_PATH];
1356         WCHAR b1[BUFFER_LEN];
1357         static const WCHAR sAsterics[] = {'*', '\0'};
1358
1359         ChildWnd* child = HeapAlloc(GetProcessHeap(), 0, sizeof(ChildWnd));
1360         Root* root = &child->root;
1361         Entry* entry;
1362
1363         memset(child, 0, sizeof(ChildWnd));
1364
1365         child->left.treePane = TRUE;
1366         child->left.visible_cols = 0;
1367
1368         child->right.treePane = FALSE;
1369 #ifndef _NO_EXTENSIONS
1370         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS;
1371 #else
1372         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES;
1373 #endif
1374
1375         child->pos.length = sizeof(WINDOWPLACEMENT);
1376         child->pos.flags = 0;
1377         child->pos.showCmd = SW_SHOWNORMAL;
1378         child->pos.rcNormalPosition.left = CW_USEDEFAULT;
1379         child->pos.rcNormalPosition.top = CW_USEDEFAULT;
1380         child->pos.rcNormalPosition.right = CW_USEDEFAULT;
1381         child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
1382
1383         child->focus_pane = 0;
1384         child->split_pos = DEFAULT_SPLIT_POS;
1385         child->sortOrder = SORT_NAME;
1386         child->header_wdths_ok = FALSE;
1387
1388         if (path)
1389         {
1390                 lstrcpyW(child->path, path);
1391
1392                 _wsplitpath(path, drv, dir, name, ext);
1393         }
1394
1395         lstrcpyW(child->filter_pattern, sAsterics);
1396         child->filter_flags = TF_ALL;
1397
1398         root->entry.level = 0;
1399
1400         lstrcpyW(dir_path, drv);
1401         lstrcatW(dir_path, dir);
1402         entry = read_tree(root, dir_path, pidl, drv, child->sortOrder, hwnd);
1403
1404 #ifdef _SHELL_FOLDERS
1405         if (root->entry.etype == ET_SHELL)
1406                 load_string(root->entry.data.cFileName, sizeof(root->entry.data.cFileName)/sizeof(root->entry.data.cFileName[0]), IDS_DESKTOP);
1407         else
1408 #endif
1409                 wsprintfW(root->entry.data.cFileName, RS(b1,IDS_TITLEFMT), drv, root->fs);
1410
1411         root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1412
1413         child->left.root = &root->entry;
1414         child->right.root = NULL;
1415
1416         set_curdir(child, entry, 0, hwnd);
1417
1418         return child;
1419 }
1420
1421
1422 /* free all memory associated with a child window */
1423 static void free_child_window(ChildWnd* child)
1424 {
1425         free_entries(&child->root.entry);
1426         HeapFree(GetProcessHeap(), 0, child);
1427 }
1428
1429
1430 /* get full path of specified directory entry */
1431 static void get_path(Entry* dir, PWSTR path)
1432 {
1433         Entry* entry;
1434         int len = 0;
1435         int level = 0;
1436
1437 #ifdef _SHELL_FOLDERS
1438         if (dir->etype == ET_SHELL)
1439         {
1440                 SFGAOF attribs;
1441                 HRESULT hr = S_OK;
1442
1443                 path[0] = '\0';
1444
1445                 attribs = 0;
1446
1447                 if (dir->folder)
1448                         hr = IShellFolder_GetAttributesOf(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1449
1450                 if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1451                         IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1452
1453                         hr = path_from_pidlW(parent, dir->pidl, path, MAX_PATH);
1454                 }
1455         }
1456         else
1457 #endif
1458         {
1459                 for(entry=dir; entry; level++) {
1460                         LPCWSTR name;
1461                         int l;
1462
1463                         {
1464                                 LPCWSTR s;
1465                                 name = entry->data.cFileName;
1466                                 s = name;
1467
1468                                 for(l=0; *s && *s != '/' && *s != '\\'; s++)
1469                                         l++;
1470                         }
1471
1472                         if (entry->up) {
1473                                 if (l > 0) {
1474                                         memmove(path+l+1, path, len*sizeof(WCHAR));
1475                                         memcpy(path+1, name, l*sizeof(WCHAR));
1476                                         len += l+1;
1477
1478 #ifndef _NO_EXTENSIONS
1479                                         if (entry->etype == ET_UNIX)
1480                                                 path[0] = '/';
1481                                         else
1482 #endif
1483                                         path[0] = '\\';
1484                                 }
1485
1486                                 entry = entry->up;
1487                         } else {
1488                                 memmove(path+l, path, len*sizeof(WCHAR));
1489                                 memcpy(path, name, l*sizeof(WCHAR));
1490                                 len += l;
1491                                 break;
1492                         }
1493                 }
1494
1495                 if (!level) {
1496 #ifndef _NO_EXTENSIONS
1497                         if (entry->etype == ET_UNIX)
1498                                 path[len++] = '/';
1499                         else
1500 #endif
1501                                 path[len++] = '\\';
1502                 }
1503
1504                 path[len] = '\0';
1505         }
1506 }
1507
1508 static windowOptions load_registry_settings(void)
1509 {
1510         DWORD size;
1511         DWORD type;
1512         HKEY hKey;
1513         windowOptions opts;
1514         LOGFONTW logfont;
1515
1516         RegOpenKeyExW( HKEY_CURRENT_USER, registry_key,
1517                        0, KEY_QUERY_VALUE, &hKey );
1518
1519         size = sizeof(DWORD);
1520
1521         if( RegQueryValueExW( hKey, reg_start_x, NULL, &type,
1522                               (LPBYTE) &opts.start_x, &size ) != ERROR_SUCCESS )
1523                 opts.start_x = CW_USEDEFAULT;
1524
1525         if( RegQueryValueExW( hKey, reg_start_y, NULL, &type,
1526                               (LPBYTE) &opts.start_y, &size ) != ERROR_SUCCESS )
1527                 opts.start_y = CW_USEDEFAULT;
1528
1529         if( RegQueryValueExW( hKey, reg_width, NULL, &type,
1530                               (LPBYTE) &opts.width, &size ) != ERROR_SUCCESS )
1531                 opts.width = CW_USEDEFAULT;
1532
1533         if( RegQueryValueExW( hKey, reg_height, NULL, &type,
1534                               (LPBYTE) &opts.height, &size ) != ERROR_SUCCESS )
1535                 opts.height = CW_USEDEFAULT;
1536         size=sizeof(logfont);
1537         if( RegQueryValueExW( hKey, reg_logfont, NULL, &type,
1538                               (LPBYTE) &logfont, &size ) != ERROR_SUCCESS )
1539                 GetObjectW(GetStockObject(DEFAULT_GUI_FONT),sizeof(logfont),&logfont);
1540
1541         RegCloseKey( hKey );
1542
1543         Globals.hfont = CreateFontIndirectW(&logfont);
1544         return opts;
1545 }
1546
1547 static void save_registry_settings(void)
1548 {
1549         WINDOWINFO wi;
1550         HKEY hKey;
1551         INT width, height;
1552         LOGFONTW logfont;
1553
1554         wi.cbSize = sizeof( WINDOWINFO );
1555         GetWindowInfo(Globals.hMainWnd, &wi);
1556         width = wi.rcWindow.right - wi.rcWindow.left;
1557         height = wi.rcWindow.bottom - wi.rcWindow.top;
1558
1559         if ( RegOpenKeyExW( HKEY_CURRENT_USER, registry_key,
1560                             0, KEY_SET_VALUE, &hKey ) != ERROR_SUCCESS )
1561         {
1562                 /* Unable to save registry settings - try to create key */
1563                 if ( RegCreateKeyExW( HKEY_CURRENT_USER, registry_key,
1564                                       0, NULL, REG_OPTION_NON_VOLATILE,
1565                                       KEY_SET_VALUE, NULL, &hKey, NULL ) != ERROR_SUCCESS )
1566                 {
1567                         /* FIXME: Cannot create key */
1568                         return;
1569                 }
1570         }
1571         /* Save all of the settings */
1572         RegSetValueExW( hKey, reg_start_x, 0, REG_DWORD,
1573                         (LPBYTE) &wi.rcWindow.left, sizeof(DWORD) );
1574         RegSetValueExW( hKey, reg_start_y, 0, REG_DWORD,
1575                         (LPBYTE) &wi.rcWindow.top, sizeof(DWORD) );
1576         RegSetValueExW( hKey, reg_width, 0, REG_DWORD,
1577                         (LPBYTE) &width, sizeof(DWORD) );
1578         RegSetValueExW( hKey, reg_height, 0, REG_DWORD,
1579                         (LPBYTE) &height, sizeof(DWORD) );
1580         GetObjectW(Globals.hfont, sizeof(logfont), &logfont);
1581         RegSetValueExW( hKey, reg_logfont, 0, REG_BINARY,
1582                         (LPBYTE)&logfont, sizeof(LOGFONTW) );
1583
1584         /* TODO: Save more settings here (List vs. Detailed View, etc.) */
1585         RegCloseKey( hKey );
1586 }
1587
1588 static void resize_frame_rect(HWND hwnd, PRECT prect)
1589 {
1590         int new_top;
1591         RECT rt;
1592
1593         if (IsWindowVisible(Globals.htoolbar)) {
1594                 SendMessageW(Globals.htoolbar, WM_SIZE, 0, 0);
1595                 GetClientRect(Globals.htoolbar, &rt);
1596                 prect->top = rt.bottom+3;
1597                 prect->bottom -= rt.bottom+3;
1598         }
1599
1600         if (IsWindowVisible(Globals.hdrivebar)) {
1601                 SendMessageW(Globals.hdrivebar, WM_SIZE, 0, 0);
1602                 GetClientRect(Globals.hdrivebar, &rt);
1603                 new_top = --prect->top + rt.bottom+3;
1604                 MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
1605                 prect->top = new_top;
1606                 prect->bottom -= rt.bottom+2;
1607         }
1608
1609         if (IsWindowVisible(Globals.hstatusbar)) {
1610                 int parts[] = {300, 500};
1611
1612                 SendMessageW(Globals.hstatusbar, WM_SIZE, 0, 0);
1613                 SendMessageW(Globals.hstatusbar, SB_SETPARTS, 2, (LPARAM)&parts);
1614                 GetClientRect(Globals.hstatusbar, &rt);
1615                 prect->bottom -= rt.bottom;
1616         }
1617
1618         MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1619 }
1620
1621 static void resize_frame(HWND hwnd, int cx, int cy)
1622 {
1623         RECT rect;
1624
1625         rect.left   = 0;
1626         rect.top    = 0;
1627         rect.right  = cx;
1628         rect.bottom = cy;
1629
1630         resize_frame_rect(hwnd, &rect);
1631 }
1632
1633 static void resize_frame_client(HWND hwnd)
1634 {
1635         RECT rect;
1636
1637         GetClientRect(hwnd, &rect);
1638
1639         resize_frame_rect(hwnd, &rect);
1640 }
1641
1642
1643 static HHOOK hcbthook;
1644 static ChildWnd* newchild = NULL;
1645
1646 static LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
1647 {
1648         if (code==HCBT_CREATEWND && newchild) {
1649                 ChildWnd* child = newchild;
1650                 newchild = NULL;
1651
1652                 child->hwnd = (HWND) wparam;
1653                 SetWindowLongPtrW(child->hwnd, GWLP_USERDATA, (LPARAM)child);
1654         }
1655
1656         return CallNextHookEx(hcbthook, code, wparam, lparam);
1657 }
1658
1659 static HWND create_child_window(ChildWnd* child)
1660 {
1661         MDICREATESTRUCTW mcs;
1662         int idx;
1663
1664         mcs.szClass = sWINEFILETREE;
1665         mcs.szTitle = child->path;
1666         mcs.hOwner  = Globals.hInstance;
1667         mcs.x       = child->pos.rcNormalPosition.left;
1668         mcs.y       = child->pos.rcNormalPosition.top;
1669         mcs.cx      = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
1670         mcs.cy      = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
1671         mcs.style   = 0;
1672         mcs.lParam  = 0;
1673
1674         hcbthook = SetWindowsHookExW(WH_CBT, CBTProc, 0, GetCurrentThreadId());
1675
1676         newchild = child;
1677         child->hwnd = (HWND)SendMessageW(Globals.hmdiclient, WM_MDICREATE, 0, (LPARAM)&mcs);
1678         if (!child->hwnd) {
1679                 UnhookWindowsHookEx(hcbthook);
1680                 return 0;
1681         }
1682
1683         UnhookWindowsHookEx(hcbthook);
1684
1685         SendMessageW(child->left.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
1686         SendMessageW(child->right.hwnd, LB_SETITEMHEIGHT, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
1687
1688         idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
1689         SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
1690
1691         return child->hwnd;
1692 }
1693
1694 #define RFF_NODEFAULT           0x02    /* No default item selected. */
1695
1696 static void WineFile_OnRun( HWND hwnd )
1697 {
1698         static const WCHAR shell32_dll[] = {'S','H','E','L','L','3','2','.','D','L','L',0};
1699         void (WINAPI *pRunFileDlgAW )(HWND, HICON, LPWSTR, LPWSTR, LPWSTR, DWORD);
1700         HMODULE hshell = GetModuleHandleW( shell32_dll );
1701
1702         pRunFileDlgAW = (void*)GetProcAddress(hshell, (LPCSTR)61);
1703         if (pRunFileDlgAW) pRunFileDlgAW( hwnd, 0, NULL, NULL, NULL, RFF_NODEFAULT);
1704 }
1705
1706 static INT_PTR CALLBACK DestinationDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1707 {
1708         WCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
1709
1710         switch(nmsg) {
1711                 case WM_INITDIALOG:
1712                         SetWindowLongPtrW(hwnd, GWLP_USERDATA, lparam);
1713                         SetWindowTextW(GetDlgItem(hwnd, 201), (LPCWSTR)lparam);
1714                         return 1;
1715
1716                 case WM_COMMAND: {
1717                         int id = (int)wparam;
1718
1719                         switch(id) {
1720                           case IDOK: {
1721                                 LPWSTR dest = (LPWSTR)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
1722                                 GetWindowTextW(GetDlgItem(hwnd, 201), dest, MAX_PATH);
1723                                 EndDialog(hwnd, id);
1724                                 break;}
1725
1726                           case IDCANCEL:
1727                                 EndDialog(hwnd, id);
1728                                 break;
1729
1730                           case 254:
1731                                 MessageBoxW(hwnd, RS(b1,IDS_NO_IMPL), RS(b2,IDS_WINEFILE), MB_OK);
1732                                 break;
1733                         }
1734
1735                         return 1;
1736                 }
1737         }
1738
1739         return 0;
1740 }
1741
1742
1743 struct FilterDialog {
1744         WCHAR   pattern[MAX_PATH];
1745         int             flags;
1746 };
1747
1748 static INT_PTR CALLBACK FilterDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1749 {
1750         static struct FilterDialog* dlg;
1751
1752         switch(nmsg) {
1753                 case WM_INITDIALOG:
1754                         dlg = (struct FilterDialog*) lparam;
1755                         SetWindowTextW(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern);
1756                         set_check(hwnd, IDC_VIEW_TYPE_DIRECTORIES, dlg->flags&TF_DIRECTORIES);
1757                         set_check(hwnd, IDC_VIEW_TYPE_PROGRAMS, dlg->flags&TF_PROGRAMS);
1758                         set_check(hwnd, IDC_VIEW_TYPE_DOCUMENTS, dlg->flags&TF_DOCUMENTS);
1759                         set_check(hwnd, IDC_VIEW_TYPE_OTHERS, dlg->flags&TF_OTHERS);
1760                         set_check(hwnd, IDC_VIEW_TYPE_HIDDEN, dlg->flags&TF_HIDDEN);
1761                         return 1;
1762
1763                 case WM_COMMAND: {
1764                         int id = (int)wparam;
1765
1766                         if (id == IDOK) {
1767                                 int flags = 0;
1768
1769                                 GetWindowTextW(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern, MAX_PATH);
1770
1771                                 flags |= get_check(hwnd, IDC_VIEW_TYPE_DIRECTORIES) ? TF_DIRECTORIES : 0;
1772                                 flags |= get_check(hwnd, IDC_VIEW_TYPE_PROGRAMS) ? TF_PROGRAMS : 0;
1773                                 flags |= get_check(hwnd, IDC_VIEW_TYPE_DOCUMENTS) ? TF_DOCUMENTS : 0;
1774                                 flags |= get_check(hwnd, IDC_VIEW_TYPE_OTHERS) ? TF_OTHERS : 0;
1775                                 flags |= get_check(hwnd, IDC_VIEW_TYPE_HIDDEN) ? TF_HIDDEN : 0;
1776
1777                                 dlg->flags = flags;
1778
1779                                 EndDialog(hwnd, id);
1780                         } else if (id == IDCANCEL)
1781                                 EndDialog(hwnd, id);
1782
1783                         return 1;}
1784         }
1785
1786         return 0;
1787 }
1788
1789
1790 struct PropertiesDialog {
1791         WCHAR   path[MAX_PATH];
1792         Entry   entry;
1793         void*   pVersionData;
1794 };
1795
1796 /* Structure used to store enumerated languages and code pages. */
1797 struct LANGANDCODEPAGE {
1798         WORD wLanguage;
1799         WORD wCodePage;
1800 } *lpTranslate;
1801
1802 static LPCSTR InfoStrings[] = {
1803         "Comments",
1804         "CompanyName",
1805         "FileDescription",
1806         "FileVersion",
1807         "InternalName",
1808         "LegalCopyright",
1809         "LegalTrademarks",
1810         "OriginalFilename",
1811         "PrivateBuild",
1812         "ProductName",
1813         "ProductVersion",
1814         "SpecialBuild",
1815         NULL
1816 };
1817
1818 static void PropDlg_DisplayValue(HWND hlbox, HWND hedit)
1819 {
1820         int idx = SendMessageW(hlbox, LB_GETCURSEL, 0, 0);
1821
1822         if (idx != LB_ERR) {
1823                 LPCWSTR pValue = (LPCWSTR)SendMessageW(hlbox, LB_GETITEMDATA, idx, 0);
1824
1825                 if (pValue)
1826                         SetWindowTextW(hedit, pValue);
1827         }
1828 }
1829
1830 static void CheckForFileInfo(struct PropertiesDialog* dlg, HWND hwnd, LPCWSTR strFilename)
1831 {
1832         static WCHAR sBackSlash[] = {'\\','\0'};
1833         static WCHAR sTranslation[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n','\0'};
1834         static WCHAR sStringFileInfo[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\',
1835                                                                                 '%','0','4','x','%','0','4','x','\\','%','s','\0'};
1836         DWORD dwVersionDataLen = GetFileVersionInfoSizeW(strFilename, NULL);
1837
1838         if (dwVersionDataLen) {
1839                 dlg->pVersionData = HeapAlloc(GetProcessHeap(), 0, dwVersionDataLen);
1840
1841                 if (GetFileVersionInfoW(strFilename, 0, dwVersionDataLen, dlg->pVersionData)) {
1842                         LPVOID pVal;
1843                         UINT nValLen;
1844
1845                         if (VerQueryValueW(dlg->pVersionData, sBackSlash, &pVal, &nValLen)) {
1846                                 if (nValLen == sizeof(VS_FIXEDFILEINFO)) {
1847                                         VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)pVal;
1848                                         char buffer[BUFFER_LEN];
1849
1850                                         sprintf(buffer, "%d.%d.%d.%d",
1851                                                 HIWORD(pFixedFileInfo->dwFileVersionMS), LOWORD(pFixedFileInfo->dwFileVersionMS),
1852                                                 HIWORD(pFixedFileInfo->dwFileVersionLS), LOWORD(pFixedFileInfo->dwFileVersionLS));
1853
1854                                         SetDlgItemTextA(hwnd, IDC_STATIC_PROP_VERSION, buffer);
1855                                 }
1856                         }
1857
1858                         /* Read the list of languages and code pages. */
1859                         if (VerQueryValueW(dlg->pVersionData, sTranslation, &pVal, &nValLen)) {
1860                                 struct LANGANDCODEPAGE* pTranslate = (struct LANGANDCODEPAGE*)pVal;
1861                                 struct LANGANDCODEPAGE* pEnd = (struct LANGANDCODEPAGE*)((LPBYTE)pVal+nValLen);
1862
1863                                 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
1864
1865                                 /* Read the file description for each language and code page. */
1866                                 for(; pTranslate<pEnd; ++pTranslate) {
1867                                         LPCSTR* p;
1868
1869                                         for(p=InfoStrings; *p; ++p) {
1870                                                 WCHAR subblock[200];
1871                                                 WCHAR infoStr[100];
1872                                                 LPCWSTR pTxt;
1873                                                 UINT nValLen;
1874
1875                                                 LPCSTR pInfoString = *p;
1876                                                 MultiByteToWideChar(CP_ACP, 0, pInfoString, -1, infoStr, 100);
1877                                                 wsprintfW(subblock, sStringFileInfo, pTranslate->wLanguage, pTranslate->wCodePage, infoStr);
1878
1879                                                 /* Retrieve file description for language and code page */
1880                                                 if (VerQueryValueW(dlg->pVersionData, subblock, (PVOID)&pTxt, &nValLen)) {
1881                                                         int idx = SendMessageW(hlbox, LB_ADDSTRING, 0L, (LPARAM)infoStr);
1882                                                         SendMessageW(hlbox, LB_SETITEMDATA, idx, (LPARAM)pTxt);
1883                                                 }
1884                                         }
1885                                 }
1886
1887                                 SendMessageW(hlbox, LB_SETCURSEL, 0, 0);
1888
1889                                 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
1890                         }
1891                 }
1892         }
1893 }
1894
1895 static INT_PTR CALLBACK PropertiesDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1896 {
1897         static struct PropertiesDialog* dlg;
1898
1899         switch(nmsg) {
1900                 case WM_INITDIALOG: {
1901                         static const WCHAR sByteFmt[] = {'%','s',' ','B','y','t','e','s','\0'};
1902                         WCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
1903                         LPWIN32_FIND_DATAW pWFD;
1904
1905                         dlg = (struct PropertiesDialog*) lparam;
1906                         pWFD = (LPWIN32_FIND_DATAW)&dlg->entry.data;
1907
1908                         GetWindowTextW(hwnd, b1, MAX_PATH);
1909                         wsprintfW(b2, b1, pWFD->cFileName);
1910                         SetWindowTextW(hwnd, b2);
1911
1912                         format_date(&pWFD->ftLastWriteTime, b1, COL_DATE|COL_TIME);
1913                         SetWindowTextW(GetDlgItem(hwnd, IDC_STATIC_PROP_LASTCHANGE), b1);
1914
1915                         format_longlong( b1, ((ULONGLONG)pWFD->nFileSizeHigh << 32) | pWFD->nFileSizeLow );
1916                         wsprintfW(b2, sByteFmt, b1);
1917                         SetWindowTextW(GetDlgItem(hwnd, IDC_STATIC_PROP_SIZE), b2);
1918
1919                         SetWindowTextW(GetDlgItem(hwnd, IDC_STATIC_PROP_FILENAME), pWFD->cFileName);
1920                         SetWindowTextW(GetDlgItem(hwnd, IDC_STATIC_PROP_PATH), dlg->path);
1921
1922                         set_check(hwnd, IDC_CHECK_READONLY, pWFD->dwFileAttributes&FILE_ATTRIBUTE_READONLY);
1923                         set_check(hwnd, IDC_CHECK_ARCHIVE, pWFD->dwFileAttributes&FILE_ATTRIBUTE_ARCHIVE);
1924                         set_check(hwnd, IDC_CHECK_COMPRESSED, pWFD->dwFileAttributes&FILE_ATTRIBUTE_COMPRESSED);
1925                         set_check(hwnd, IDC_CHECK_HIDDEN, pWFD->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN);
1926                         set_check(hwnd, IDC_CHECK_SYSTEM, pWFD->dwFileAttributes&FILE_ATTRIBUTE_SYSTEM);
1927
1928                         CheckForFileInfo(dlg, hwnd, dlg->path);
1929                         return 1;}
1930
1931                 case WM_COMMAND: {
1932                         int id = (int)wparam;
1933
1934                         switch(HIWORD(wparam)) {
1935                           case LBN_SELCHANGE: {
1936                                 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
1937                                 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
1938                                 break;
1939                           }
1940
1941                           case BN_CLICKED:
1942                                 if (id==IDOK || id==IDCANCEL)
1943                                         EndDialog(hwnd, id);
1944                         }
1945
1946                         return 1;}
1947
1948                 case WM_NCDESTROY:
1949                         HeapFree(GetProcessHeap(), 0, dlg->pVersionData);
1950                         dlg->pVersionData = NULL;
1951                         break;
1952         }
1953
1954         return 0;
1955 }
1956
1957 static void show_properties_dlg(Entry* entry, HWND hwnd)
1958 {
1959         struct PropertiesDialog dlg;
1960
1961         memset(&dlg, 0, sizeof(struct PropertiesDialog));
1962         get_path(entry, dlg.path);
1963         memcpy(&dlg.entry, entry, sizeof(Entry));
1964
1965         DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_DIALOG_PROPERTIES), hwnd, PropertiesDialogDlgProc, (LPARAM)&dlg);
1966 }
1967
1968
1969 #ifndef _NO_EXTENSIONS
1970
1971 static struct FullScreenParameters {
1972         BOOL    mode;
1973         RECT    orgPos;
1974         BOOL    wasZoomed;
1975 } g_fullscreen = {
1976     FALSE,      /* mode */
1977         {0, 0, 0, 0},
1978         FALSE
1979 };
1980
1981 static void frame_get_clientspace(HWND hwnd, PRECT prect)
1982 {
1983         RECT rt;
1984
1985         if (!IsIconic(hwnd))
1986                 GetClientRect(hwnd, prect);
1987         else {
1988                 WINDOWPLACEMENT wp;
1989
1990                 GetWindowPlacement(hwnd, &wp);
1991
1992                 prect->left = prect->top = 0;
1993                 prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
1994                                                 2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
1995                 prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
1996                                                 2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
1997                                                 GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
1998         }
1999
2000         if (IsWindowVisible(Globals.htoolbar)) {
2001                 GetClientRect(Globals.htoolbar, &rt);
2002                 prect->top += rt.bottom+2;
2003         }
2004
2005         if (IsWindowVisible(Globals.hdrivebar)) {
2006                 GetClientRect(Globals.hdrivebar, &rt);
2007                 prect->top += rt.bottom+2;
2008         }
2009
2010         if (IsWindowVisible(Globals.hstatusbar)) {
2011                 GetClientRect(Globals.hstatusbar, &rt);
2012                 prect->bottom -= rt.bottom;
2013         }
2014 }
2015
2016 static BOOL toggle_fullscreen(HWND hwnd)
2017 {
2018         RECT rt;
2019
2020         if ((g_fullscreen.mode=!g_fullscreen.mode)) {
2021                 GetWindowRect(hwnd, &g_fullscreen.orgPos);
2022                 g_fullscreen.wasZoomed = IsZoomed(hwnd);
2023
2024                 Frame_CalcFrameClient(hwnd, &rt);
2025                 MapWindowPoints( hwnd, 0, (POINT *)&rt, 2 );
2026
2027                 rt.left = g_fullscreen.orgPos.left-rt.left;
2028                 rt.top = g_fullscreen.orgPos.top-rt.top;
2029                 rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
2030                 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
2031
2032                 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2033         } else {
2034                 MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
2035                                                         g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
2036                                                         g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
2037
2038                 if (g_fullscreen.wasZoomed)
2039                         ShowWindow(hwnd, WS_MAXIMIZE);
2040         }
2041
2042         return g_fullscreen.mode;
2043 }
2044
2045 static void fullscreen_move(HWND hwnd)
2046 {
2047         RECT rt, pos;
2048         GetWindowRect(hwnd, &pos);
2049
2050         Frame_CalcFrameClient(hwnd, &rt);
2051         MapWindowPoints( hwnd, 0, (POINT *)&rt, 2 );
2052
2053         rt.left = pos.left-rt.left;
2054         rt.top = pos.top-rt.top;
2055         rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
2056         rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
2057
2058         MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2059 }
2060
2061 #endif
2062
2063
2064 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
2065 {
2066         BOOL vis = IsWindowVisible(hchild);
2067
2068         CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
2069
2070         ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
2071
2072 #ifndef _NO_EXTENSIONS
2073         if (g_fullscreen.mode)
2074                 fullscreen_move(hwnd);
2075 #endif
2076
2077         resize_frame_client(hwnd);
2078 }
2079
2080 static BOOL activate_drive_window(LPCWSTR path)
2081 {
2082         WCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
2083         HWND child_wnd;
2084
2085         _wsplitpath(path, drv1, 0, 0, 0);
2086
2087         /* search for a already open window for the same drive */
2088         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2089                 ChildWnd* child = (ChildWnd*)GetWindowLongPtrW(child_wnd, GWLP_USERDATA);
2090
2091                 if (child) {
2092                         _wsplitpath(child->root.path, drv2, 0, 0, 0);
2093
2094                         if (!lstrcmpiW(drv2, drv1)) {
2095                                 SendMessageW(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2096
2097                                 if (IsIconic(child_wnd))
2098                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
2099
2100                                 return TRUE;
2101                         }
2102                 }
2103         }
2104
2105         return FALSE;
2106 }
2107
2108 #ifndef _NO_EXTENSIONS
2109 static BOOL activate_fs_window(LPCWSTR filesys)
2110 {
2111         HWND child_wnd;
2112
2113         /* search for a already open window of the given file system name */
2114         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2115                 ChildWnd* child = (ChildWnd*) GetWindowLongPtrW(child_wnd, GWLP_USERDATA);
2116
2117                 if (child) {
2118                         if (!lstrcmpiW(child->root.fs, filesys)) {
2119                                 SendMessageW(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2120
2121                                 if (IsIconic(child_wnd))
2122                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
2123
2124                                 return TRUE;
2125                         }
2126                 }
2127         }
2128
2129         return FALSE;
2130 }
2131 #endif /* _NO_EXTENSIONS */
2132
2133 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2134 {
2135         WCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2136
2137         switch(nmsg) {
2138                 case WM_CLOSE:
2139                         if (Globals.saveSettings)
2140                                 save_registry_settings();  
2141                         
2142                         DestroyWindow(hwnd);
2143
2144                          /* clear handle variables */
2145                         Globals.hMenuFrame = 0;
2146                         Globals.hMenuView = 0;
2147                         Globals.hMenuOptions = 0;
2148                         Globals.hMainWnd = 0;
2149                         Globals.hmdiclient = 0;
2150                         Globals.hdrivebar = 0;
2151                         break;
2152
2153                 case WM_DESTROY:
2154                         PostQuitMessage(0);
2155                         break;
2156
2157                 case WM_INITMENUPOPUP: {
2158                         HWND hwndClient = (HWND)SendMessageW(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2159
2160                         if (!SendMessageW(hwndClient, WM_INITMENUPOPUP, wparam, lparam))
2161                                 return 0;
2162                         break;}
2163
2164                 case WM_COMMAND: {
2165                         UINT cmd = LOWORD(wparam);
2166                         HWND hwndClient = (HWND)SendMessageW(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2167
2168                         if (SendMessageW(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
2169                                 break;
2170
2171                         if (cmd>=ID_DRIVE_FIRST && cmd<=ID_DRIVE_FIRST+0xFF) {
2172                                 WCHAR drv[_MAX_DRIVE], path[MAX_PATH];
2173                                 ChildWnd* child;
2174                                 LPCWSTR root = Globals.drives;
2175                                 int i;
2176
2177                                 for(i=cmd-ID_DRIVE_FIRST; i--; root++)
2178                                         while(*root)
2179                                                 root++;
2180
2181                                 if (activate_drive_window(root))
2182                                         return 0;
2183
2184                                 _wsplitpath(root, drv, 0, 0, 0);
2185
2186                                 if (!SetCurrentDirectoryW(drv)) {
2187                                         display_error(hwnd, GetLastError());
2188                                         return 0;
2189                                 }
2190
2191                                 GetCurrentDirectoryW(MAX_PATH, path); /*TODO: store last directory per drive */
2192                                 child = alloc_child_window(path, NULL, hwnd);
2193
2194                                 if (!create_child_window(child))
2195                                         HeapFree(GetProcessHeap(), 0, child);
2196                         } else switch(cmd) {
2197                                 case ID_FILE_EXIT:
2198                                         SendMessageW(hwnd, WM_CLOSE, 0, 0);
2199                                         break;
2200
2201                                 case ID_WINDOW_NEW: {
2202                                         WCHAR path[MAX_PATH];
2203                                         ChildWnd* child;
2204
2205                                         GetCurrentDirectoryW(MAX_PATH, path);
2206                                         child = alloc_child_window(path, NULL, hwnd);
2207
2208                                         if (!create_child_window(child))
2209                                                 HeapFree(GetProcessHeap(), 0, child);
2210                                         break;}
2211
2212                                 case ID_REFRESH:
2213                                         refresh_drives();
2214                                         break;
2215
2216                                 case ID_WINDOW_CASCADE:
2217                                         SendMessageW(Globals.hmdiclient, WM_MDICASCADE, 0, 0);
2218                                         break;
2219
2220                                 case ID_WINDOW_TILE_HORZ:
2221                                         SendMessageW(Globals.hmdiclient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
2222                                         break;
2223
2224                                 case ID_WINDOW_TILE_VERT:
2225                                         SendMessageW(Globals.hmdiclient, WM_MDITILE, MDITILE_VERTICAL, 0);
2226                                         break;
2227
2228                                 case ID_WINDOW_ARRANGE:
2229                                         SendMessageW(Globals.hmdiclient, WM_MDIICONARRANGE, 0, 0);
2230                                         break;
2231
2232                                 case ID_SELECT_FONT:
2233                                         choose_font(hwnd);
2234                                         break;
2235
2236                                 case ID_VIEW_TOOL_BAR:
2237                                         toggle_child(hwnd, cmd, Globals.htoolbar);
2238                                         break;
2239
2240                                 case ID_VIEW_DRIVE_BAR:
2241                                         toggle_child(hwnd, cmd, Globals.hdrivebar);
2242                                         break;
2243
2244                                 case ID_VIEW_STATUSBAR:
2245                                         toggle_child(hwnd, cmd, Globals.hstatusbar);
2246                                         break;
2247
2248                                 case ID_VIEW_SAVESETTINGS:
2249                                         Globals.saveSettings = !Globals.saveSettings;
2250                                         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_SAVESETTINGS,
2251                                                       Globals.saveSettings ? MF_CHECKED : MF_UNCHECKED );
2252                                         break;
2253
2254                                 case ID_RUN:
2255                                         WineFile_OnRun( hwnd );
2256                                         break;
2257
2258                                 case ID_CONNECT_NETWORK_DRIVE: {
2259                                         DWORD ret = WNetConnectionDialog(hwnd, RESOURCETYPE_DISK);
2260                                         if (ret == NO_ERROR)
2261                                                 refresh_drives();
2262                                         else if (ret != (DWORD)-1) {
2263                                                 if (ret == ERROR_EXTENDED_ERROR)
2264                                                         display_network_error(hwnd);
2265                                                 else
2266                                                         display_error(hwnd, ret);
2267                                         }
2268                                         break;}
2269
2270                                 case ID_DISCONNECT_NETWORK_DRIVE: {
2271                                         DWORD ret = WNetDisconnectDialog(hwnd, RESOURCETYPE_DISK);
2272                                         if (ret == NO_ERROR)
2273                                                 refresh_drives();
2274                                         else if (ret != (DWORD)-1) {
2275                                                 if (ret == ERROR_EXTENDED_ERROR)
2276                                                         display_network_error(hwnd);
2277                                                 else
2278                                                         display_error(hwnd, ret);
2279                                         }
2280                                         break;}
2281
2282                                 case ID_HELP:
2283                                         WinHelpW(hwnd, RS(b1,IDS_WINEFILE), HELP_INDEX, 0);
2284                                         break;
2285
2286 #ifndef _NO_EXTENSIONS
2287                                 case ID_VIEW_FULLSCREEN:
2288                                         CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
2289                                         break;
2290
2291 #ifdef __WINE__
2292                                 case ID_DRIVE_UNIX_FS: {
2293                                         WCHAR path[MAX_PATH];
2294                                         char cpath[MAX_PATH];
2295                                         ChildWnd* child;
2296
2297                                         if (activate_fs_window(RS(b1,IDS_UNIXFS)))
2298                                                 break;
2299
2300                                         getcwd(cpath, MAX_PATH);
2301                                         MultiByteToWideChar(CP_UNIXCP, 0, cpath, -1, path, MAX_PATH);
2302                                         child = alloc_child_window(path, NULL, hwnd);
2303
2304                                         if (!create_child_window(child))
2305                                                 HeapFree(GetProcessHeap(), 0, child);
2306                                         break;}
2307 #endif
2308 #ifdef _SHELL_FOLDERS
2309                                 case ID_DRIVE_SHELL_NS: {
2310                                         WCHAR path[MAX_PATH];
2311                                         ChildWnd* child;
2312
2313                                         if (activate_fs_window(RS(b1,IDS_SHELL)))
2314                                                 break;
2315
2316                                         GetCurrentDirectoryW(MAX_PATH, path);
2317                                         child = alloc_child_window(path, get_path_pidl(path,hwnd), hwnd);
2318
2319                                         if (!create_child_window(child))
2320                                                 HeapFree(GetProcessHeap(), 0, child);
2321                                         break;}
2322 #endif
2323 #endif
2324
2325                                 /*TODO: There are even more menu items! */
2326
2327                                 case ID_ABOUT:
2328                                         ShellAboutW(hwnd, RS(b1,IDS_WINEFILE), NULL,
2329                                                    LoadImageW( Globals.hInstance, MAKEINTRESOURCEW(IDI_WINEFILE),
2330                                                               IMAGE_ICON, 48, 48, LR_SHARED ));
2331                                         break;
2332
2333                                 default:
2334                                         /*TODO: if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
2335                                                 STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
2336                                         else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
2337                                                 (cmd<SC_SIZE || cmd>SC_RESTORE))
2338                                                 MessageBoxW(hwnd, RS(b2,IDS_NO_IMPL), RS(b1,IDS_WINEFILE), MB_OK);
2339
2340                                         return DefFrameProcW(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2341                         }
2342                         break;}
2343
2344                 case WM_SIZE:
2345                         resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
2346                         break;  /* do not pass message to DefFrameProcW */
2347
2348                 case WM_DEVICECHANGE:
2349                         SendMessageW(hwnd, WM_COMMAND, MAKELONG(ID_REFRESH,0), 0);
2350                         break;
2351
2352 #ifndef _NO_EXTENSIONS
2353                 case WM_GETMINMAXINFO: {
2354                         LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
2355
2356                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
2357                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
2358                         break;}
2359
2360                 case FRM_CALC_CLIENT:
2361                         frame_get_clientspace(hwnd, (PRECT)lparam);
2362                         return TRUE;
2363 #endif /* _NO_EXTENSIONS */
2364
2365                 default:
2366                         return DefFrameProcW(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2367         }
2368
2369         return 0;
2370 }
2371
2372
2373 static WCHAR g_pos_names[COLUMNS][20] = {
2374         {'\0'}  /* symbol */
2375 };
2376
2377 static const int g_pos_align[] = {
2378         0,
2379         HDF_LEFT,       /* Name */
2380         HDF_RIGHT,      /* Size */
2381         HDF_LEFT,       /* CDate */
2382 #ifndef _NO_EXTENSIONS
2383         HDF_LEFT,       /* ADate */
2384         HDF_LEFT,       /* MDate */
2385         HDF_LEFT,       /* Index */
2386         HDF_CENTER,     /* Links */
2387 #endif
2388         HDF_CENTER,     /* Attributes */
2389 #ifndef _NO_EXTENSIONS
2390         HDF_LEFT        /* Security */
2391 #endif
2392 };
2393
2394 static void resize_tree(ChildWnd* child, int cx, int cy)
2395 {
2396         HDWP hdwp = BeginDeferWindowPos(4);
2397         RECT rt;
2398
2399         rt.left   = 0;
2400         rt.top    = 0;
2401         rt.right  = cx;
2402         rt.bottom = cy;
2403
2404         cx = child->split_pos + SPLIT_WIDTH/2;
2405
2406 #ifndef _NO_EXTENSIONS
2407         {
2408                 WINDOWPOS wp;
2409                 HD_LAYOUT hdl;
2410
2411                 hdl.prc   = &rt;
2412                 hdl.pwpos = &wp;
2413
2414                 SendMessageW(child->left.hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hdl);
2415
2416                 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
2417                                                 wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
2418                 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
2419                                                 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
2420         }
2421 #endif /* _NO_EXTENSIONS */
2422
2423         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);
2424         DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
2425
2426         EndDeferWindowPos(hdwp);
2427 }
2428
2429
2430 #ifndef _NO_EXTENSIONS
2431
2432 static HWND create_header(HWND parent, Pane* pane, UINT id)
2433 {
2434         HDITEMW hdi;
2435         int idx;
2436
2437         HWND hwnd = CreateWindowW(WC_HEADERW, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ|HDS_FULLDRAG/*TODO: |HDS_BUTTONS + sort orders*/,
2438                                  0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2439         if (!hwnd)
2440                 return 0;
2441
2442         SendMessageW(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), FALSE);
2443
2444         hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
2445
2446         for(idx=0; idx<COLUMNS; idx++) {
2447                 hdi.pszText = g_pos_names[idx];
2448                 hdi.fmt = HDF_STRING | g_pos_align[idx];
2449                 hdi.cxy = pane->widths[idx];
2450                 SendMessageW(hwnd, HDM_INSERTITEMW, idx, (LPARAM)&hdi);
2451         }
2452
2453         return hwnd;
2454 }
2455
2456 #endif /* _NO_EXTENSIONS */
2457
2458
2459 static void init_output(HWND hwnd)
2460 {
2461         static const WCHAR s1000[] = {'1','0','0','0','\0'};
2462         WCHAR b[16];
2463         HFONT old_font;
2464         HDC hdc = GetDC(hwnd);
2465
2466         if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, s1000, 0, b, 16) > 4)
2467                 Globals.num_sep = b[1];
2468         else
2469                 Globals.num_sep = '.';
2470
2471         old_font = SelectObject(hdc, Globals.hfont);
2472         GetTextExtentPoint32W(hdc, sSpace, 1, &Globals.spaceSize);
2473         SelectObject(hdc, old_font);
2474         ReleaseDC(hwnd, hdc);
2475 }
2476
2477 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
2478
2479
2480 /* calculate preferred width for all visible columns */
2481
2482 static BOOL calc_widths(Pane* pane, BOOL anyway)
2483 {
2484         int col, x, cx, spc=3*Globals.spaceSize.cx;
2485         int entries = SendMessageW(pane->hwnd, LB_GETCOUNT, 0, 0);
2486         int orgWidths[COLUMNS];
2487         int orgPositions[COLUMNS+1];
2488         HFONT hfontOld;
2489         HDC hdc;
2490         int cnt;
2491
2492         if (!anyway) {
2493                 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2494                 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2495         }
2496
2497         for(col=0; col<COLUMNS; col++)
2498                 pane->widths[col] = 0;
2499
2500         hdc = GetDC(pane->hwnd);
2501         hfontOld = SelectObject(hdc, Globals.hfont);
2502
2503         for(cnt=0; cnt<entries; cnt++) {
2504                 Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2505
2506                 DRAWITEMSTRUCT dis;
2507
2508                 dis.CtlType               = 0;
2509                 dis.CtlID                 = 0;
2510                 dis.itemID                = 0;
2511                 dis.itemAction    = 0;
2512                 dis.itemState     = 0;
2513                 dis.hwndItem      = pane->hwnd;
2514                 dis.hDC                   = hdc;
2515                 dis.rcItem.left   = 0;
2516                 dis.rcItem.top    = 0;
2517                 dis.rcItem.right  = 0;
2518                 dis.rcItem.bottom = 0;
2519                 /*dis.itemData    = 0; */
2520
2521                 draw_item(pane, &dis, entry, COLUMNS);
2522         }
2523
2524         SelectObject(hdc, hfontOld);
2525         ReleaseDC(pane->hwnd, hdc);
2526
2527         x = 0;
2528         for(col=0; col<COLUMNS; col++) {
2529                 pane->positions[col] = x;
2530                 cx = pane->widths[col];
2531
2532                 if (cx) {
2533                         cx += spc;
2534
2535                         if (cx < IMAGE_WIDTH)
2536                                 cx = IMAGE_WIDTH;
2537
2538                         pane->widths[col] = cx;
2539                 }
2540
2541                 x += cx;
2542         }
2543
2544         pane->positions[COLUMNS] = x;
2545
2546         SendMessageW(pane->hwnd, LB_SETHORIZONTALEXTENT, x, 0);
2547
2548         /* no change? */
2549         if (!anyway && !memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2550                 return FALSE;
2551
2552         /* don't move, if only collapsing an entry */
2553         if (!anyway && pane->widths[0]<orgWidths[0] &&
2554                 !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
2555                 pane->widths[0] = orgWidths[0];
2556                 memcpy(pane->positions, orgPositions, sizeof(orgPositions));
2557
2558                 return FALSE;
2559         }
2560
2561         InvalidateRect(pane->hwnd, 0, TRUE);
2562
2563         return TRUE;
2564 }
2565
2566
2567 #ifndef _NO_EXTENSIONS
2568 /* calculate one preferred column width */
2569
2570 static void calc_single_width(Pane* pane, int col)
2571 {
2572         HFONT hfontOld;
2573         int x, cx;
2574         int entries = SendMessageW(pane->hwnd, LB_GETCOUNT, 0, 0);
2575         int cnt;
2576         HDC hdc;
2577
2578         pane->widths[col] = 0;
2579
2580         hdc = GetDC(pane->hwnd);
2581         hfontOld = SelectObject(hdc, Globals.hfont);
2582
2583         for(cnt=0; cnt<entries; cnt++) {
2584                 Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2585                 DRAWITEMSTRUCT dis;
2586
2587                 dis.CtlType               = 0;
2588                 dis.CtlID                 = 0;
2589                 dis.itemID                = 0;
2590                 dis.itemAction    = 0;
2591                 dis.itemState     = 0;
2592                 dis.hwndItem      = pane->hwnd;
2593                 dis.hDC                   = hdc;
2594                 dis.rcItem.left   = 0;
2595                 dis.rcItem.top    = 0;
2596                 dis.rcItem.right  = 0;
2597                 dis.rcItem.bottom = 0;
2598                 /*dis.itemData    = 0; */
2599
2600                 draw_item(pane, &dis, entry, col);
2601         }
2602
2603         SelectObject(hdc, hfontOld);
2604         ReleaseDC(pane->hwnd, hdc);
2605
2606         cx = pane->widths[col];
2607
2608         if (cx) {
2609                 cx += 3*Globals.spaceSize.cx;
2610
2611                 if (cx < IMAGE_WIDTH)
2612                         cx = IMAGE_WIDTH;
2613         }
2614
2615         pane->widths[col] = cx;
2616
2617         x = pane->positions[col] + cx;
2618
2619         for(; col<COLUMNS-1; ) {
2620                 pane->positions[++col] = x;
2621                 x += pane->widths[col];
2622         }
2623
2624         SendMessageW(pane->hwnd, LB_SETHORIZONTALEXTENT, x, 0);
2625 }
2626 #endif /* _NO_EXTENSIONS */
2627
2628
2629 static BOOL pattern_match(LPCWSTR str, LPCWSTR pattern)
2630 {
2631         for( ; *str&&*pattern; str++,pattern++) {
2632                 if (*pattern == '*') {
2633                         do pattern++;
2634                         while(*pattern == '*');
2635
2636                         if (!*pattern)
2637                                 return TRUE;
2638
2639                         for(; *str; str++)
2640                                 if (*str==*pattern && pattern_match(str, pattern))
2641                                         return TRUE;
2642
2643                         return FALSE;
2644                 }
2645                 else if (*str!=*pattern && *pattern!='?')
2646                         return FALSE;
2647         }
2648
2649         if (*str || *pattern)
2650                 if (*pattern!='*' || pattern[1]!='\0')
2651                         return FALSE;
2652
2653         return TRUE;
2654 }
2655
2656 static BOOL pattern_imatch(LPCWSTR str, LPCWSTR pattern)
2657 {
2658         WCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2659
2660         lstrcpyW(b1, str);
2661         lstrcpyW(b2, pattern);
2662         CharUpperW(b1);
2663         CharUpperW(b2);
2664
2665         return pattern_match(b1, b2);
2666 }
2667
2668
2669 enum FILE_TYPE {
2670         FT_OTHER                = 0,
2671         FT_EXECUTABLE   = 1,
2672         FT_DOCUMENT             = 2
2673 };
2674
2675 static enum FILE_TYPE get_file_type(LPCWSTR filename);
2676
2677
2678 /* insert listbox entries after index idx */
2679
2680 static int insert_entries(Pane* pane, Entry* dir, LPCWSTR pattern, int filter_flags, int idx)
2681 {
2682         Entry* entry = dir;
2683
2684         if (!entry)
2685                 return idx;
2686
2687         ShowWindow(pane->hwnd, SW_HIDE);
2688
2689         for(; entry; entry=entry->next) {
2690 #ifndef _LEFT_FILES
2691                 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2692                         continue;
2693 #endif
2694
2695                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2696                         /* don't display entries "." and ".." in the left pane */
2697                         if (pane->treePane && entry->data.cFileName[0] == '.')
2698                                 if (
2699 #ifndef _NO_EXTENSIONS
2700                                         entry->data.cFileName[1] == '\0' ||
2701 #endif
2702                                         (entry->data.cFileName[1] == '.' && entry->data.cFileName[2] == '\0'))
2703                                         continue;
2704
2705                         /* filter directories in right pane */
2706                         if (!pane->treePane && !(filter_flags&TF_DIRECTORIES))
2707                                 continue;
2708                 }
2709
2710                 /* filter using the file name pattern */
2711                 if (pattern)
2712                         if (!pattern_imatch(entry->data.cFileName, pattern))
2713                                 continue;
2714
2715                 /* filter system and hidden files */
2716                 if (!(filter_flags&TF_HIDDEN) && (entry->data.dwFileAttributes&(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)))
2717                         continue;
2718
2719                 /* filter looking at the file type */
2720                 if ((filter_flags&(TF_PROGRAMS|TF_DOCUMENTS|TF_OTHERS)) != (TF_PROGRAMS|TF_DOCUMENTS|TF_OTHERS))
2721                         switch(get_file_type(entry->data.cFileName)) {
2722                           case FT_EXECUTABLE:
2723                                 if (!(filter_flags & TF_PROGRAMS))
2724                                         continue;
2725                                 break;
2726
2727                           case FT_DOCUMENT:
2728                                 if (!(filter_flags & TF_DOCUMENTS))
2729                                         continue;
2730                                 break;
2731
2732                           default: /* TF_OTHERS */
2733                                 if (!(filter_flags & TF_OTHERS))
2734                                         continue;
2735                         }
2736
2737                 if (idx != -1)
2738                         idx++;
2739
2740                 SendMessageW(pane->hwnd, LB_INSERTSTRING, idx, (LPARAM)entry);
2741
2742                 if (pane->treePane && entry->expanded)
2743                         idx = insert_entries(pane, entry->down, pattern, filter_flags, idx);
2744         }
2745
2746         ShowWindow(pane->hwnd, SW_SHOW);
2747
2748         return idx;
2749 }
2750
2751
2752 static void format_bytes(LPWSTR buffer, LONGLONG bytes)
2753 {
2754         static const WCHAR sFmtGB[] = {'%', '.', '1', 'f', ' ', 'G', 'B', '\0'};
2755         static const WCHAR sFmtMB[] = {'%', '.', '1', 'f', ' ', 'M', 'B', '\0'};
2756         static const WCHAR sFmtkB[] = {'%', '.', '1', 'f', ' ', 'k', 'B', '\0'};
2757         static const WCHAR sFmtB[]  = {'%', 'u', 0};
2758
2759         float fBytes = (float)bytes;
2760
2761         if (bytes >= 1073741824)        /* 1 GB */
2762                 sprintfW(buffer, sFmtGB, fBytes/1073741824.f+.5f);
2763         else if (bytes >= 1048576)      /* 1 MB */
2764                 sprintfW(buffer, sFmtMB, fBytes/1048576.f+.5f);
2765         else if (bytes >= 1024)         /* 1 kB */
2766                 sprintfW(buffer, sFmtkB, fBytes/1024.f+.5f);
2767         else
2768                 sprintfW(buffer, sFmtB, (DWORD)bytes);
2769 }
2770
2771 static void set_space_status(void)
2772 {
2773         ULARGE_INTEGER ulFreeBytesToCaller, ulTotalBytes, ulFreeBytes;
2774         WCHAR fmt[64], b1[64], b2[64], buffer[BUFFER_LEN];
2775
2776         if (GetDiskFreeSpaceExW(NULL, &ulFreeBytesToCaller, &ulTotalBytes, &ulFreeBytes)) {
2777                 format_bytes(b1, ulFreeBytesToCaller.QuadPart);
2778                 format_bytes(b2, ulTotalBytes.QuadPart);
2779                 wsprintfW(buffer, RS(fmt,IDS_FREE_SPACE_FMT), b1, b2);
2780         } else
2781                 lstrcpyW(buffer, sQMarks);
2782
2783         SendMessageW(Globals.hstatusbar, SB_SETTEXTW, 0, (LPARAM)buffer);
2784 }
2785
2786
2787 static WNDPROC g_orgTreeWndProc;
2788
2789 static void create_tree_window(HWND parent, Pane* pane, UINT id, UINT id_header, LPCWSTR pattern, int filter_flags)
2790 {
2791         static const WCHAR sListBox[] = {'L','i','s','t','B','o','x','\0'};
2792
2793         static int s_init = 0;
2794         Entry* entry = pane->root;
2795
2796         pane->hwnd = CreateWindowW(sListBox, sEmpty, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
2797                                   LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
2798                                   0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2799
2800         SetWindowLongPtrW(pane->hwnd, GWLP_USERDATA, (LPARAM)pane);
2801         g_orgTreeWndProc = (WNDPROC)SetWindowLongPtrW(pane->hwnd, GWLP_WNDPROC, (LPARAM)TreeWndProc);
2802
2803         SendMessageW(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2804
2805         /* insert entries into listbox */
2806         if (entry)
2807                 insert_entries(pane, entry, pattern, filter_flags, -1);
2808
2809         /* calculate column widths */
2810         if (!s_init) {
2811                 s_init = 1;
2812                 init_output(pane->hwnd);
2813         }
2814
2815         calc_widths(pane, TRUE);
2816
2817 #ifndef _NO_EXTENSIONS
2818         pane->hwndHeader = create_header(parent, pane, id_header);
2819 #endif
2820 }
2821
2822
2823 static void InitChildWindow(ChildWnd* child)
2824 {
2825         create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT, NULL, TF_ALL);
2826         create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, child->filter_pattern, child->filter_flags);
2827 }
2828
2829
2830 static void format_date(const FILETIME* ft, WCHAR* buffer, int visible_cols)
2831 {
2832         SYSTEMTIME systime;
2833         FILETIME lft;
2834         int len = 0;
2835
2836         *buffer = '\0';
2837
2838         if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2839                 return;
2840
2841         if (!FileTimeToLocalFileTime(ft, &lft))
2842                 {err: lstrcpyW(buffer,sQMarks); return;}
2843
2844         if (!FileTimeToSystemTime(&lft, &systime))
2845                 goto err;
2846
2847         if (visible_cols & COL_DATE) {
2848                 len = GetDateFormatW(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
2849                 if (!len)
2850                         goto err;
2851         }
2852
2853         if (visible_cols & COL_TIME) {
2854                 if (len)
2855                         buffer[len-1] = ' ';
2856
2857                 buffer[len++] = ' ';
2858
2859                 if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
2860                         buffer[len] = '\0';
2861         }
2862 }
2863
2864
2865 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2866 {
2867         RECT rt = {0, 0, 0, 0};
2868
2869         DrawTextW(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2870
2871         if (rt.right > pane->widths[col])
2872                 pane->widths[col] = rt.right;
2873 }
2874
2875 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2876 {
2877         RECT rt = {0, 0, 0, 0};
2878
2879         DrawTextW(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2880         /*FIXME rt (0,0) ??? */
2881
2882         if (rt.right > pane->widths[col])
2883                 pane->widths[col] = rt.right;
2884 }
2885
2886
2887 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str, DWORD flags)
2888 {
2889         int x = dis->rcItem.left;
2890         RECT rt;
2891
2892         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2893         rt.top    = dis->rcItem.top;
2894         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2895         rt.bottom = dis->rcItem.bottom;
2896
2897         DrawTextW(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
2898 }
2899
2900 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2901 {
2902         int x = dis->rcItem.left;
2903         RECT rt;
2904
2905         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2906         rt.top    = dis->rcItem.top;
2907         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2908         rt.bottom = dis->rcItem.bottom;
2909
2910         DrawTextW(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2911 }
2912
2913 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2914 {
2915         int x = dis->rcItem.left;
2916         RECT rt;
2917         LPCWSTR s = str;
2918         WCHAR b[128];
2919         LPWSTR d = b;
2920         int pos;
2921
2922         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2923         rt.top    = dis->rcItem.top;
2924         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2925         rt.bottom = dis->rcItem.bottom;
2926
2927         if (*s)
2928                 *d++ = *s++;
2929
2930         /* insert number separator characters */
2931         pos = lstrlenW(s) % 3;
2932
2933         while(*s)
2934                 if (pos--)
2935                         *d++ = *s++;
2936                 else {
2937                         *d++ = Globals.num_sep;
2938                         pos = 3;
2939                 }
2940
2941         DrawTextW(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
2942 }
2943
2944
2945 static BOOL is_exe_file(LPCWSTR ext)
2946 {
2947         static const WCHAR executable_extensions[][4] = {
2948                 {'C','O','M','\0'},
2949                 {'E','X','E','\0'},
2950                 {'B','A','T','\0'},
2951                 {'C','M','D','\0'},
2952 #ifndef _NO_EXTENSIONS
2953                 {'C','M','M','\0'},
2954                 {'B','T','M','\0'},
2955                 {'A','W','K','\0'},
2956 #endif /* _NO_EXTENSIONS */
2957                 {'\0'}
2958         };
2959
2960         WCHAR ext_buffer[_MAX_EXT];
2961         const WCHAR (*p)[4];
2962         LPCWSTR s;
2963         LPWSTR d;
2964
2965         for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
2966                 d++;
2967
2968         for(p=executable_extensions; (*p)[0]; p++)
2969                 if (!lstrcmpiW(ext_buffer, *p))
2970                         return TRUE;
2971
2972         return FALSE;
2973 }
2974
2975 static BOOL is_registered_type(LPCWSTR ext)
2976 {
2977         /* check if there exists a classname for this file extension in the registry */
2978         if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, NULL, NULL))
2979                 return TRUE;
2980
2981         return FALSE;
2982 }
2983
2984 static enum FILE_TYPE get_file_type(LPCWSTR filename)
2985 {
2986         LPCWSTR ext = strrchrW(filename, '.');
2987         if (!ext)
2988                 ext = sEmpty;
2989
2990         if (is_exe_file(ext))
2991                 return FT_EXECUTABLE;
2992         else if (is_registered_type(ext))
2993                 return FT_DOCUMENT;
2994         else
2995                 return FT_OTHER;
2996 }
2997
2998
2999 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
3000 {
3001         WCHAR buffer[BUFFER_LEN];
3002         DWORD attrs;
3003         int visible_cols = pane->visible_cols;
3004         COLORREF bkcolor, textcolor;
3005         RECT focusRect = dis->rcItem;
3006         HBRUSH hbrush;
3007         enum IMAGE img;
3008         int img_pos, cx;
3009         int col = 0;
3010
3011         if (entry) {
3012                 attrs = entry->data.dwFileAttributes;
3013
3014                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
3015                         if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '.'
3016                                         && entry->data.cFileName[2] == '\0')
3017                                 img = IMG_FOLDER_UP;
3018 #ifndef _NO_EXTENSIONS
3019                         else if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '\0')
3020                                 img = IMG_FOLDER_CUR;
3021 #endif
3022                         else if (
3023 #ifdef _NO_EXTENSIONS
3024                                          entry->expanded ||
3025 #endif
3026                                          (pane->treePane && (dis->itemState&ODS_FOCUS)))
3027                                 img = IMG_OPEN_FOLDER;
3028                         else
3029                                 img = IMG_FOLDER;
3030                 } else {
3031                         switch(get_file_type(entry->data.cFileName)) {
3032                           case FT_EXECUTABLE:   img = IMG_EXECUTABLE;   break;
3033                           case FT_DOCUMENT:             img = IMG_DOCUMENT;             break;
3034                           default:                              img = IMG_FILE;
3035                         }
3036                 }
3037         } else {
3038                 attrs = 0;
3039                 img = IMG_NONE;
3040         }
3041
3042         if (pane->treePane) {
3043                 if (entry) {
3044                         img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+TREE_LINE_DX);
3045
3046                         if (calcWidthCol == -1) {
3047                                 int x;
3048                                 int y = dis->rcItem.top + IMAGE_HEIGHT/2;
3049                                 Entry* up;
3050                                 RECT rt_clip;
3051                                 HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
3052                                 HRGN hrgn;
3053
3054                                 rt_clip.left   = dis->rcItem.left;
3055                                 rt_clip.top    = dis->rcItem.top;
3056                                 rt_clip.right  = dis->rcItem.left+pane->widths[col];
3057                                 rt_clip.bottom = dis->rcItem.bottom;
3058
3059                                 hrgn = CreateRectRgnIndirect(&rt_clip);
3060
3061                                 if (!GetClipRgn(dis->hDC, hrgn_org)) {
3062                                         DeleteObject(hrgn_org);
3063                                         hrgn_org = 0;
3064                                 }
3065
3066                                 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
3067                                 DeleteObject(hrgn);
3068
3069                                 if ((up=entry->up) != NULL) {
3070                                         MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
3071                                         LineTo(dis->hDC, img_pos-2, y);
3072
3073                                         x = img_pos - IMAGE_WIDTH/2;
3074
3075                                         do {
3076                                                 x -= IMAGE_WIDTH+TREE_LINE_DX;
3077
3078                                                 if (up->next
3079 #ifndef _LEFT_FILES
3080                                                         && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3081 #endif
3082                                                         ) {
3083                                                         MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3084                                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
3085                                                 }
3086                                         } while((up=up->up) != NULL);
3087                                 }
3088
3089                                 x = img_pos - IMAGE_WIDTH/2;
3090
3091                                 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3092                                 LineTo(dis->hDC, x, y);
3093
3094                                 if (entry->next
3095 #ifndef _LEFT_FILES
3096                                         && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
3097 #endif
3098                                         )
3099                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
3100
3101                                 SelectClipRgn(dis->hDC, hrgn_org);
3102                                 if (hrgn_org) DeleteObject(hrgn_org);
3103                         } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
3104                                 int right = img_pos + IMAGE_WIDTH - TREE_LINE_DX;
3105
3106                                 if (right > pane->widths[col])
3107                                         pane->widths[col] = right;
3108                         }
3109                 } else  {
3110                         img_pos = dis->rcItem.left;
3111                 }
3112         } else {
3113                 img_pos = dis->rcItem.left;
3114
3115                 if (calcWidthCol==col || calcWidthCol==COLUMNS)
3116                         pane->widths[col] = IMAGE_WIDTH;
3117         }
3118
3119         if (calcWidthCol == -1) {
3120                 focusRect.left = img_pos -2;
3121
3122 #ifdef _NO_EXTENSIONS
3123                 if (pane->treePane && entry) {
3124                         RECT rt = {0};
3125
3126                         DrawTextW(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
3127
3128                         focusRect.right = dis->rcItem.left+pane->positions[col+1]+TREE_LINE_DX + rt.right +2;
3129                 }
3130 #else
3131
3132                 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
3133                         textcolor = COLOR_COMPRESSED;
3134                 else
3135 #endif /* _NO_EXTENSIONS */
3136                         textcolor = RGB(0,0,0);
3137
3138                 if (dis->itemState & ODS_FOCUS) {
3139                         textcolor = RGB(255,255,255);
3140                         bkcolor = COLOR_SELECTION;
3141                 } else {
3142                         bkcolor = RGB(255,255,255);
3143                 }
3144
3145                 hbrush = CreateSolidBrush(bkcolor);
3146                 FillRect(dis->hDC, &focusRect, hbrush);
3147                 DeleteObject(hbrush);
3148
3149                 SetBkMode(dis->hDC, TRANSPARENT);
3150                 SetTextColor(dis->hDC, textcolor);
3151
3152                 cx = pane->widths[col];
3153
3154                 if (cx && img!=IMG_NONE) {
3155                         if (cx > IMAGE_WIDTH)
3156                                 cx = IMAGE_WIDTH;
3157
3158 #ifdef _SHELL_FOLDERS
3159                         if (entry->hicon && entry->hicon!=(HICON)-1)
3160                                 DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->hicon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
3161                         else
3162 #endif
3163                                 ImageList_DrawEx(Globals.himl, img, dis->hDC,
3164                                                                  img_pos, dis->rcItem.top, cx,
3165                                                                  IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
3166                 }
3167         }
3168
3169         if (!entry)
3170                 return;
3171
3172 #ifdef _NO_EXTENSIONS
3173         if (img >= IMG_FOLDER_UP)
3174                 return;
3175 #endif
3176
3177         col++;
3178
3179         /* ouput file name */
3180         if (calcWidthCol == -1)
3181                 output_text(pane, dis, col, entry->data.cFileName, 0);
3182         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3183                 calc_width(pane, dis, col, entry->data.cFileName);
3184
3185         col++;
3186
3187 #ifdef _NO_EXTENSIONS
3188   if (!pane->treePane) {
3189 #endif
3190
3191         /* display file size */
3192         if (visible_cols & COL_SIZE) {
3193 #ifdef _NO_EXTENSIONS
3194                 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
3195 #endif
3196                 {
3197                         format_longlong( buffer, ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow );
3198
3199                         if (calcWidthCol == -1)
3200                                 output_number(pane, dis, col, buffer);
3201                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3202                                 calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
3203                 }
3204
3205                 col++;
3206         }
3207
3208         /* display file date */
3209         if (visible_cols & (COL_DATE|COL_TIME)) {
3210 #ifndef _NO_EXTENSIONS
3211                 format_date(&entry->data.ftCreationTime, buffer, visible_cols);
3212                 if (calcWidthCol == -1)
3213                         output_text(pane, dis, col, buffer, 0);
3214                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3215                         calc_width(pane, dis, col, buffer);
3216                 col++;
3217
3218                 format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
3219                 if (calcWidthCol == -1)
3220                         output_text(pane, dis, col, buffer, 0);
3221                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3222                         calc_width(pane, dis, col, buffer);
3223                 col++;
3224 #endif /* _NO_EXTENSIONS */
3225
3226                 format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
3227                 if (calcWidthCol == -1)
3228                         output_text(pane, dis, col, buffer, 0);
3229                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3230                         calc_width(pane, dis, col, buffer);
3231                 col++;
3232         }
3233
3234 #ifndef _NO_EXTENSIONS
3235         if (entry->bhfi_valid) {
3236                 if (visible_cols & COL_INDEX) {
3237                         static const WCHAR fmtlow[] = {'%','X',0};
3238                         static const WCHAR fmthigh[] = {'%','X','%','0','8','X',0};
3239
3240                         if (entry->bhfi.nFileIndexHigh)
3241                             wsprintfW(buffer, fmthigh,
3242                                      entry->bhfi.nFileIndexHigh, entry->bhfi.nFileIndexLow );
3243                         else
3244                             wsprintfW(buffer, fmtlow, entry->bhfi.nFileIndexLow );
3245
3246                         if (calcWidthCol == -1)
3247                                 output_text(pane, dis, col, buffer, DT_RIGHT);
3248                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3249                                 calc_width(pane, dis, col, buffer);
3250
3251                         col++;
3252                 }
3253
3254                 if (visible_cols & COL_LINKS) {
3255                         wsprintfW(buffer, sNumFmt, entry->bhfi.nNumberOfLinks);
3256
3257                         if (calcWidthCol == -1)
3258                                 output_text(pane, dis, col, buffer, DT_CENTER);
3259                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3260                                 calc_width(pane, dis, col, buffer);
3261
3262                         col++;
3263                 }
3264         } else
3265                 col += 2;
3266 #endif /* _NO_EXTENSIONS */
3267
3268         /* show file attributes */
3269         if (visible_cols & COL_ATTRIBUTES) {
3270 #ifdef _NO_EXTENSIONS
3271                 static const WCHAR s4Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3272                 lstrcpyW(buffer, s4Tabs);
3273 #else
3274                 static const WCHAR s11Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3275                 lstrcpyW(buffer, s11Tabs);
3276 #endif
3277
3278                 if (attrs & FILE_ATTRIBUTE_NORMAL)                                      buffer[ 0] = 'N';
3279                 else {
3280                         if (attrs & FILE_ATTRIBUTE_READONLY)                    buffer[ 2] = 'R';
3281                         if (attrs & FILE_ATTRIBUTE_HIDDEN)                              buffer[ 4] = 'H';
3282                         if (attrs & FILE_ATTRIBUTE_SYSTEM)                              buffer[ 6] = 'S';
3283                         if (attrs & FILE_ATTRIBUTE_ARCHIVE)                             buffer[ 8] = 'A';
3284                         if (attrs & FILE_ATTRIBUTE_COMPRESSED)                  buffer[10] = 'C';
3285 #ifndef _NO_EXTENSIONS
3286                         if (attrs & FILE_ATTRIBUTE_DIRECTORY)                   buffer[12] = 'D';
3287                         if (attrs & FILE_ATTRIBUTE_ENCRYPTED)                   buffer[14] = 'E';
3288                         if (attrs & FILE_ATTRIBUTE_TEMPORARY)                   buffer[16] = 'T';
3289                         if (attrs & FILE_ATTRIBUTE_SPARSE_FILE)                 buffer[18] = 'P';
3290                         if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)               buffer[20] = 'Q';
3291                         if (attrs & FILE_ATTRIBUTE_OFFLINE)                             buffer[22] = 'O';
3292                         if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
3293 #endif /* _NO_EXTENSIONS */
3294                 }
3295
3296                 if (calcWidthCol == -1)
3297                         output_tabbed_text(pane, dis, col, buffer);
3298                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3299                         calc_tabbed_width(pane, dis, col, buffer);
3300
3301                 col++;
3302         }
3303
3304 #ifdef _NO_EXTENSIONS
3305   }
3306
3307         /* draw focus frame */
3308         if ((dis->itemState&ODS_FOCUS) && calcWidthCol==-1) {
3309                 /* Currently [04/2000] Wine neither behaves exactly the same */
3310                 /* way as WIN 95 nor like Windows NT... */
3311                 HGDIOBJ lastBrush;
3312                 HPEN lastPen;
3313                 HPEN hpen;
3314
3315                 if (!(GetVersion() & 0x80000000)) {     /* Windows NT? */
3316                         LOGBRUSH lb = {PS_SOLID, RGB(255,255,255)};
3317                         hpen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, 0);
3318                 } else
3319                         hpen = CreatePen(PS_DOT, 0, RGB(255,255,255));
3320
3321                 lastPen = SelectObject(dis->hDC, hpen);
3322                 lastBrush = SelectObject(dis->hDC, GetStockObject(HOLLOW_BRUSH));
3323                 SetROP2(dis->hDC, R2_XORPEN);
3324                 Rectangle(dis->hDC, focusRect.left, focusRect.top, focusRect.right, focusRect.bottom);
3325                 SelectObject(dis->hDC, lastBrush);
3326                 SelectObject(dis->hDC, lastPen);
3327                 DeleteObject(hpen);
3328         }
3329 #endif /* _NO_EXTENSIONS */
3330 }
3331
3332
3333 #ifdef _NO_EXTENSIONS
3334
3335 static void draw_splitbar(HWND hwnd, int x)
3336 {
3337         RECT rt;
3338         HDC hdc = GetDC(hwnd);
3339
3340         GetClientRect(hwnd, &rt);
3341
3342         rt.left = x - SPLIT_WIDTH/2;
3343         rt.right = x + SPLIT_WIDTH/2+1;
3344
3345         InvertRect(hdc, &rt);
3346
3347         ReleaseDC(hwnd, hdc);
3348 }
3349
3350 #endif /* _NO_EXTENSIONS */
3351
3352
3353 #ifndef _NO_EXTENSIONS
3354
3355 static void set_header(Pane* pane)
3356 {
3357         HDITEMW item;
3358         int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3359         int i=0, x=0;
3360
3361         item.mask = HDI_WIDTH;
3362         item.cxy = 0;
3363
3364         for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
3365                 x += pane->widths[i];
3366                 SendMessageW(pane->hwndHeader, HDM_SETITEMW, i, (LPARAM)&item);
3367         }
3368
3369         if (i < COLUMNS) {
3370                 x += pane->widths[i];
3371                 item.cxy = x - scroll_pos;
3372                 SendMessageW(pane->hwndHeader, HDM_SETITEMW, i++, (LPARAM)&item);
3373
3374                 for(; i<COLUMNS; i++) {
3375                         item.cxy = pane->widths[i];
3376                         x += pane->widths[i];
3377                         SendMessageW(pane->hwndHeader, HDM_SETITEMW, i, (LPARAM)&item);
3378                 }
3379         }
3380 }
3381
3382 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
3383 {
3384         switch(pnmh->code) {
3385                 case HDN_ITEMCHANGEDW: {
3386                         LPNMHEADERW phdn = (LPNMHEADERW)pnmh;
3387                         int idx = phdn->iItem;
3388                         int dx = phdn->pitem->cxy - pane->widths[idx];
3389                         int i;
3390
3391                         RECT clnt;
3392                         GetClientRect(pane->hwnd, &clnt);
3393
3394                         pane->widths[idx] += dx;
3395
3396                         for(i=idx; ++i<=COLUMNS; )
3397                                 pane->positions[i] += dx;
3398
3399                         {
3400                                 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3401                                 RECT rt_scr;
3402                                 RECT rt_clip;
3403
3404                                 rt_scr.left   = pane->positions[idx+1]-scroll_pos;
3405                                 rt_scr.top    = 0;
3406                                 rt_scr.right  = clnt.right;
3407                                 rt_scr.bottom = clnt.bottom;
3408
3409                                 rt_clip.left   = pane->positions[idx]-scroll_pos;
3410                                 rt_clip.top    = 0;
3411                                 rt_clip.right  = clnt.right;
3412                                 rt_clip.bottom = clnt.bottom;
3413
3414                                 if (rt_scr.left < 0) rt_scr.left = 0;
3415                                 if (rt_clip.left < 0) rt_clip.left = 0;
3416
3417                                 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
3418
3419                                 rt_clip.right = pane->positions[idx+1];
3420                                 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
3421
3422                                 if (pnmh->code == HDN_ENDTRACKW) {
3423                                         SendMessageW(pane->hwnd, LB_SETHORIZONTALEXTENT, pane->positions[COLUMNS], 0);
3424
3425                                         if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
3426                                                 set_header(pane);
3427                                 }
3428                         }
3429
3430                         return FALSE;
3431                 }
3432
3433                 case HDN_DIVIDERDBLCLICKW: {
3434                         LPNMHEADERW phdn = (LPNMHEADERW)pnmh;
3435                         HDITEMW item;
3436
3437                         calc_single_width(pane, phdn->iItem);
3438                         item.mask = HDI_WIDTH;
3439                         item.cxy = pane->widths[phdn->iItem];
3440
3441                         SendMessageW(pane->hwndHeader, HDM_SETITEMW, phdn->iItem, (LPARAM)&item);
3442                         InvalidateRect(pane->hwnd, 0, TRUE);
3443                         break;}
3444         }
3445
3446         return 0;
3447 }
3448
3449 #endif /* _NO_EXTENSIONS */
3450
3451
3452 static void scan_entry(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3453 {
3454         WCHAR path[MAX_PATH];
3455         HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
3456
3457         /* delete sub entries in left pane */
3458         for(;;) {
3459                 LRESULT res = SendMessageW(child->left.hwnd, LB_GETITEMDATA, idx+1, 0);
3460                 Entry* sub = (Entry*) res;
3461
3462                 if (res==LB_ERR || !sub || sub->level<=entry->level)
3463                         break;
3464
3465                 SendMessageW(child->left.hwnd, LB_DELETESTRING, idx+1, 0);
3466         }
3467
3468         /* empty right pane */
3469         SendMessageW(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3470
3471         /* release memory */
3472         free_entries(entry);
3473
3474         /* read contents from disk */
3475 #ifdef _SHELL_FOLDERS
3476         if (entry->etype == ET_SHELL)
3477         {
3478                 read_directory(entry, NULL, child->sortOrder, hwnd);
3479         }
3480         else
3481 #endif
3482         {
3483                 get_path(entry, path);
3484                 read_directory(entry, path, child->sortOrder, hwnd);
3485         }
3486
3487         /* insert found entries in right pane */
3488         insert_entries(&child->right, entry->down, child->filter_pattern, child->filter_flags, -1);
3489         calc_widths(&child->right, FALSE);
3490 #ifndef _NO_EXTENSIONS
3491         set_header(&child->right);
3492 #endif
3493
3494         child->header_wdths_ok = FALSE;
3495
3496         SetCursor(old_cursor);
3497 }
3498
3499
3500 /* expand a directory entry */
3501
3502 static BOOL expand_entry(ChildWnd* child, Entry* dir)
3503 {
3504         int idx;
3505         Entry* p;
3506
3507         if (!dir || dir->expanded || !dir->down)
3508                 return FALSE;
3509
3510         p = dir->down;
3511
3512         if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
3513                 p = p->next;
3514
3515                 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
3516                                 p->data.cFileName[2]=='\0' && p->next)
3517                         p = p->next;
3518         }
3519
3520         /* no subdirectories ? */
3521         if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
3522                 return FALSE;
3523
3524         idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3525
3526         dir->expanded = TRUE;
3527
3528         /* insert entries in left pane */
3529         insert_entries(&child->left, p, NULL, TF_ALL, idx);
3530
3531         if (!child->header_wdths_ok) {
3532                 if (calc_widths(&child->left, FALSE)) {
3533 #ifndef _NO_EXTENSIONS
3534                         set_header(&child->left);
3535 #endif
3536
3537                         child->header_wdths_ok = TRUE;
3538                 }
3539         }
3540
3541         return TRUE;
3542 }
3543
3544
3545 static void collapse_entry(Pane* pane, Entry* dir)
3546 {
3547         int idx = SendMessageW(pane->hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3548
3549         ShowWindow(pane->hwnd, SW_HIDE);
3550
3551         /* hide sub entries */
3552         for(;;) {
3553                 LRESULT res = SendMessageW(pane->hwnd, LB_GETITEMDATA, idx+1, 0);
3554                 Entry* sub = (Entry*) res;
3555
3556                 if (res==LB_ERR || !sub || sub->level<=dir->level)
3557                         break;
3558
3559                 SendMessageW(pane->hwnd, LB_DELETESTRING, idx+1, 0);
3560         }
3561
3562         dir->expanded = FALSE;
3563
3564         ShowWindow(pane->hwnd, SW_SHOW);
3565 }
3566
3567
3568 static void refresh_right_pane(ChildWnd* child)
3569 {
3570         SendMessageW(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3571         insert_entries(&child->right, child->right.root, child->filter_pattern, child->filter_flags, -1);
3572         calc_widths(&child->right, FALSE);
3573
3574 #ifndef _NO_EXTENSIONS
3575         set_header(&child->right);
3576 #endif
3577 }
3578
3579 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3580 {
3581         WCHAR path[MAX_PATH];
3582
3583         if (!entry)
3584                 return;
3585
3586         path[0] = '\0';
3587
3588         child->left.cur = entry;
3589
3590         child->right.root = entry->down? entry->down: entry;
3591         child->right.cur = entry;
3592
3593         if (!entry->scanned)
3594                 scan_entry(child, entry, idx, hwnd);
3595         else
3596                 refresh_right_pane(child);
3597
3598         get_path(entry, path);
3599         lstrcpyW(child->path, path);
3600
3601         if (child->hwnd)        /* only change window title, if the window already exists */
3602                 SetWindowTextW(child->hwnd, path);
3603
3604         if (path[0])
3605                 if (SetCurrentDirectoryW(path))
3606                         set_space_status();
3607 }
3608
3609
3610 static void refresh_child(ChildWnd* child)
3611 {
3612         WCHAR path[MAX_PATH], drv[_MAX_DRIVE+1];
3613         Entry* entry;
3614         int idx;
3615
3616         get_path(child->left.cur, path);
3617         _wsplitpath(path, drv, NULL, NULL, NULL);
3618
3619         child->right.root = NULL;
3620
3621         scan_entry(child, &child->root.entry, 0, child->hwnd);
3622
3623 #ifdef _SHELL_FOLDERS
3624
3625         if (child->root.entry.etype == ET_SHELL)
3626         {
3627                 LPITEMIDLIST local_pidl = get_path_pidl(path,child->hwnd);
3628                 if (local_pidl)
3629                         entry = read_tree(&child->root, NULL, local_pidl , drv, child->sortOrder, child->hwnd);
3630                 else
3631                         entry = NULL;
3632         }
3633         else
3634 #endif
3635                 entry = read_tree(&child->root, path, NULL, drv, child->sortOrder, child->hwnd);
3636
3637         if (!entry)
3638                 entry = &child->root.entry;
3639
3640         insert_entries(&child->left, child->root.entry.down, NULL, TF_ALL, 0);
3641
3642         set_curdir(child, entry, 0, child->hwnd);
3643
3644         idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
3645         SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
3646 }
3647
3648
3649 static void create_drive_bar(void)
3650 {
3651         TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0};
3652 #ifndef _NO_EXTENSIONS
3653         WCHAR b1[BUFFER_LEN];
3654 #endif
3655         int btn = 1;
3656         PWSTR p;
3657
3658         GetLogicalDriveStringsW(BUFFER_LEN, Globals.drives);
3659
3660         Globals.hdrivebar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE|CCS_NOMOVEY|TBSTYLE_LIST,
3661                                 IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
3662                                 0, 16, 13, 16, 13, sizeof(TBBUTTON));
3663
3664 #ifndef _NO_EXTENSIONS
3665 #ifdef __WINE__
3666         /* insert unix file system button */
3667         b1[0] = '/';
3668         b1[1] = '\0';
3669         b1[2] = '\0';
3670         SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)b1);
3671
3672         drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
3673         SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3674         drivebarBtn.iString++;
3675 #endif
3676 #ifdef _SHELL_FOLDERS
3677         /* insert shell namespace button */
3678         load_string(b1, sizeof(b1)/sizeof(b1[0]), IDS_SHELL);
3679         b1[lstrlenW(b1)+1] = '\0';
3680         SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)b1);
3681
3682         drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3683         SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3684         drivebarBtn.iString++;
3685 #endif
3686
3687         /* register windows drive root strings */
3688         SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)Globals.drives);
3689 #endif
3690
3691         drivebarBtn.idCommand = ID_DRIVE_FIRST;
3692
3693         for(p=Globals.drives; *p; ) {
3694 #ifdef _NO_EXTENSIONS
3695                 /* insert drive letter */
3696                 WCHAR b[3] = {tolower(*p)};
3697                 SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)b);
3698 #endif
3699                 switch(GetDriveTypeW(p)) {
3700                         case DRIVE_REMOVABLE:   drivebarBtn.iBitmap = 1;        break;
3701                         case DRIVE_CDROM:               drivebarBtn.iBitmap = 3;        break;
3702                         case DRIVE_REMOTE:              drivebarBtn.iBitmap = 4;        break;
3703                         case DRIVE_RAMDISK:             drivebarBtn.iBitmap = 5;        break;
3704                         default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
3705                 }
3706
3707                 SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3708                 drivebarBtn.idCommand++;
3709                 drivebarBtn.iString++;
3710
3711                 while(*p++);
3712         }
3713 }
3714
3715 static void refresh_drives(void)
3716 {
3717         RECT rect;
3718
3719         /* destroy drive bar */
3720         DestroyWindow(Globals.hdrivebar);
3721         Globals.hdrivebar = 0;
3722
3723         /* re-create drive bar */
3724         create_drive_bar();
3725
3726         /* update window layout */
3727         GetClientRect(Globals.hMainWnd, &rect);
3728         SendMessageW(Globals.hMainWnd, WM_SIZE, 0, MAKELONG(rect.right, rect.bottom));
3729 }
3730
3731
3732 static BOOL launch_file(HWND hwnd, LPCWSTR cmd, UINT nCmdShow)
3733 {
3734         HINSTANCE hinst = ShellExecuteW(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3735
3736         if (PtrToUlong(hinst) <= 32) {
3737                 display_error(hwnd, GetLastError());
3738                 return FALSE;
3739         }
3740
3741         return TRUE;
3742 }
3743
3744
3745 static BOOL launch_entry(Entry* entry, HWND hwnd, UINT nCmdShow)
3746 {
3747         WCHAR cmd[MAX_PATH];
3748
3749 #ifdef _SHELL_FOLDERS
3750         if (entry->etype == ET_SHELL) {
3751                 BOOL ret = TRUE;
3752
3753                 SHELLEXECUTEINFOW shexinfo;
3754
3755                 shexinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
3756                 shexinfo.fMask = SEE_MASK_IDLIST;
3757                 shexinfo.hwnd = hwnd;
3758                 shexinfo.lpVerb = NULL;
3759                 shexinfo.lpFile = NULL;
3760                 shexinfo.lpParameters = NULL;
3761                 shexinfo.lpDirectory = NULL;
3762                 shexinfo.nShow = nCmdShow;
3763                 shexinfo.lpIDList = get_to_absolute_pidl(entry, hwnd);
3764
3765                 if (!ShellExecuteExW(&shexinfo)) {
3766                         display_error(hwnd, GetLastError());
3767                         ret = FALSE;
3768                 }
3769
3770                 if (shexinfo.lpIDList != entry->pidl)
3771                         IMalloc_Free(Globals.iMalloc, shexinfo.lpIDList);
3772
3773                 return ret;
3774         }
3775 #endif
3776
3777         get_path(entry, cmd);
3778
3779          /* start program, open document... */
3780         return launch_file(hwnd, cmd, nCmdShow);
3781 }
3782
3783
3784 static void activate_entry(ChildWnd* child, Pane* pane, HWND hwnd)
3785 {
3786         Entry* entry = pane->cur;
3787
3788         if (!entry)
3789                 return;
3790
3791         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3792                 int scanned_old = entry->scanned;
3793
3794                 if (!scanned_old)
3795                 {
3796                         int idx = SendMessageW(child->left.hwnd, LB_GETCURSEL, 0, 0);
3797                         scan_entry(child, entry, idx, hwnd);
3798                 }
3799
3800 #ifndef _NO_EXTENSIONS
3801                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3802                         return;
3803 #endif
3804
3805                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
3806                         entry = child->left.cur->up;
3807                         collapse_entry(&child->left, entry);
3808                         goto focus_entry;
3809                 } else if (entry->expanded)
3810                         collapse_entry(pane, child->left.cur);
3811                 else {
3812                         expand_entry(child, child->left.cur);
3813
3814                         if (!pane->treePane) focus_entry: {
3815                                 int idxstart = SendMessageW(child->left.hwnd, LB_GETCURSEL, 0, 0);
3816                                 int idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, idxstart, (LPARAM)entry);
3817                                 SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
3818                                 set_curdir(child, entry, idx, hwnd);
3819                         }
3820                 }
3821
3822                 if (!scanned_old) {
3823                         calc_widths(pane, FALSE);
3824
3825 #ifndef _NO_EXTENSIONS
3826                         set_header(pane);
3827 #endif
3828                 }
3829         } else {
3830                 if (GetKeyState(VK_MENU) < 0)
3831                         show_properties_dlg(entry, child->hwnd);
3832                 else
3833                         launch_entry(entry, child->hwnd, SW_SHOWNORMAL);
3834         }
3835 }
3836
3837
3838 static BOOL pane_command(Pane* pane, UINT cmd)
3839 {
3840         switch(cmd) {
3841                 case ID_VIEW_NAME:
3842                         if (pane->visible_cols) {
3843                                 pane->visible_cols = 0;
3844                                 calc_widths(pane, TRUE);
3845 #ifndef _NO_EXTENSIONS
3846                                 set_header(pane);
3847 #endif
3848                                 InvalidateRect(pane->hwnd, 0, TRUE);
3849                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
3850                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
3851                         }
3852                         break;
3853
3854                 case ID_VIEW_ALL_ATTRIBUTES:
3855                         if (pane->visible_cols != COL_ALL) {
3856                                 pane->visible_cols = COL_ALL;
3857                                 calc_widths(pane, TRUE);
3858 #ifndef _NO_EXTENSIONS
3859                                 set_header(pane);
3860 #endif
3861                                 InvalidateRect(pane->hwnd, 0, TRUE);
3862                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
3863                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
3864                         }
3865                         break;
3866
3867 #ifndef _NO_EXTENSIONS
3868                 case ID_PREFERRED_SIZES: {
3869                         calc_widths(pane, TRUE);
3870                         set_header(pane);
3871                         InvalidateRect(pane->hwnd, 0, TRUE);
3872                         break;}
3873 #endif
3874
3875                         /* TODO: more command ids... */
3876
3877                 default:
3878                         return FALSE;
3879         }
3880
3881         return TRUE;
3882 }
3883
3884
3885 static void set_sort_order(ChildWnd* child, SORT_ORDER sortOrder)
3886 {
3887         if (child->sortOrder != sortOrder) {
3888                 child->sortOrder = sortOrder;
3889                 refresh_child(child);
3890         }
3891 }
3892
3893 static void update_view_menu(ChildWnd* child)
3894 {
3895         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_NAME, child->sortOrder==SORT_NAME? MF_CHECKED: MF_UNCHECKED);
3896         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_TYPE, child->sortOrder==SORT_EXT? MF_CHECKED: MF_UNCHECKED);
3897         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_SIZE, child->sortOrder==SORT_SIZE? MF_CHECKED: MF_UNCHECKED);
3898         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_DATE, child->sortOrder==SORT_DATE? MF_CHECKED: MF_UNCHECKED);
3899 }
3900
3901
3902 static BOOL is_directory(LPCWSTR target)
3903 {
3904         /*TODO correctly handle UNIX paths */
3905         DWORD target_attr = GetFileAttributesW(target);
3906
3907         if (target_attr == INVALID_FILE_ATTRIBUTES)
3908                 return FALSE;
3909
3910         return target_attr&FILE_ATTRIBUTE_DIRECTORY? TRUE: FALSE;
3911 }
3912
3913 static BOOL prompt_target(Pane* pane, LPWSTR source, LPWSTR target)
3914 {
3915         WCHAR path[MAX_PATH];
3916         int len;
3917
3918         get_path(pane->cur, path);
3919
3920         if (DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_SELECT_DESTINATION), pane->hwnd, DestinationDlgProc, (LPARAM)path) != IDOK)
3921                 return FALSE;
3922
3923         get_path(pane->cur, source);
3924
3925         /* convert relative targets to absolute paths */
3926         if (path[0]!='/' && path[1]!=':') {
3927                 get_path(pane->cur->up, target);
3928                 len = lstrlenW(target);
3929
3930                 if (target[len-1]!='\\' && target[len-1]!='/')
3931                         target[len++] = '/';
3932
3933                 lstrcpyW(target+len, path);
3934         } else
3935                 lstrcpyW(target, path);
3936
3937         /* If the target already exists as directory, create a new target below this. */
3938         if (is_directory(path)) {
3939                 WCHAR fname[_MAX_FNAME], ext[_MAX_EXT];
3940                 static const WCHAR sAppend[] = {'%','s','/','%','s','%','s','\0'};
3941
3942                 _wsplitpath(source, NULL, NULL, fname, ext);
3943
3944                 wsprintfW(target, sAppend, path, fname, ext);
3945         }
3946
3947         return TRUE;
3948 }
3949
3950
3951 static IContextMenu2* s_pctxmenu2 = NULL;
3952 static IContextMenu3* s_pctxmenu3 = NULL;
3953
3954 static void CtxMenu_reset(void)
3955 {
3956         s_pctxmenu2 = NULL;
3957         s_pctxmenu3 = NULL;
3958 }
3959
3960 static IContextMenu* CtxMenu_query_interfaces(IContextMenu* pcm1)
3961 {
3962         IContextMenu* pcm = NULL;
3963
3964         CtxMenu_reset();
3965
3966         if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu3, (void**)&pcm) == NOERROR)
3967                 s_pctxmenu3 = (LPCONTEXTMENU3)pcm;
3968         else if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu2, (void**)&pcm) == NOERROR)
3969                 s_pctxmenu2 = (LPCONTEXTMENU2)pcm;
3970
3971         if (pcm) {
3972                 IContextMenu_Release(pcm1);
3973                 return pcm;
3974         } else
3975                 return pcm1;
3976 }
3977
3978 static BOOL CtxMenu_HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam)
3979 {
3980         if (s_pctxmenu3) {
3981                 if (SUCCEEDED(IContextMenu3_HandleMenuMsg(s_pctxmenu3, nmsg, wparam, lparam)))
3982                         return TRUE;
3983         }
3984
3985         if (s_pctxmenu2)
3986                 if (SUCCEEDED(IContextMenu2_HandleMenuMsg(s_pctxmenu2, nmsg, wparam, lparam)))
3987                         return TRUE;
3988
3989         return FALSE;
3990 }
3991
3992
3993 #ifndef _NO_EXTENSIONS
3994 static HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, LPCITEMIDLIST* apidl, int x, int y)
3995 {
3996         IContextMenu* pcm;
3997         BOOL executed = FALSE;
3998
3999         HRESULT hr = IShellFolder_GetUIObjectOf(shell_folder, hwndParent, cidl, apidl, &IID_IContextMenu, NULL, (LPVOID*)&pcm);
4000
4001         if (SUCCEEDED(hr)) {
4002                 HMENU hmenu = CreatePopupMenu();
4003
4004                 pcm = CtxMenu_query_interfaces(pcm);
4005
4006                 if (hmenu) {
4007                         hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL);
4008
4009                         if (SUCCEEDED(hr)) {
4010                                 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL);
4011
4012                                 CtxMenu_reset();
4013
4014                                 if (idCmd) {
4015                                   CMINVOKECOMMANDINFO cmi;
4016
4017                                   cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
4018                                   cmi.fMask = 0;
4019                                   cmi.hwnd = hwndParent;
4020                                   cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
4021                                   cmi.lpParameters = NULL;
4022                                   cmi.lpDirectory = NULL;
4023                                   cmi.nShow = SW_SHOWNORMAL;
4024                                   cmi.dwHotKey = 0;
4025                                   cmi.hIcon = 0;
4026
4027                                   hr = IContextMenu_InvokeCommand(pcm, &cmi);
4028                                         executed = TRUE;
4029                                 }
4030                         } else
4031                                 CtxMenu_reset();
4032                 }
4033
4034                 IContextMenu_Release(pcm);
4035         }
4036
4037         return FAILED(hr)? hr: executed? S_OK: S_FALSE;
4038 }
4039 #endif /* _NO_EXTENSIONS */
4040
4041
4042 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4043 {
4044         ChildWnd* child = (ChildWnd*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
4045         ASSERT(child);
4046
4047         switch(nmsg) {
4048                 case WM_DRAWITEM: {
4049                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
4050                         Entry* entry = (Entry*) dis->itemData;
4051
4052                         if (dis->CtlID == IDW_TREE_LEFT)
4053                                 draw_item(&child->left, dis, entry, -1);
4054                         else if (dis->CtlID == IDW_TREE_RIGHT)
4055                                 draw_item(&child->right, dis, entry, -1);
4056                         else
4057                                 goto draw_menu_item;
4058
4059                         return TRUE;}
4060
4061                 case WM_CREATE:
4062                         InitChildWindow(child);
4063                         break;
4064
4065                 case WM_NCDESTROY:
4066                         free_child_window(child);
4067                         SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
4068                         break;
4069
4070                 case WM_PAINT: {
4071                         PAINTSTRUCT ps;
4072                         HBRUSH lastBrush;
4073                         RECT rt;
4074                         GetClientRect(hwnd, &rt);
4075                         BeginPaint(hwnd, &ps);
4076                         rt.left = child->split_pos-SPLIT_WIDTH/2;
4077                         rt.right = child->split_pos+SPLIT_WIDTH/2+1;
4078                         lastBrush = SelectObject(ps.hdc, GetStockObject(COLOR_SPLITBAR));
4079                         Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
4080                         SelectObject(ps.hdc, lastBrush);
4081 #ifdef _NO_EXTENSIONS
4082                         rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
4083                         FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
4084 #endif
4085                         EndPaint(hwnd, &ps);
4086                         break;}
4087
4088                 case WM_SETCURSOR:
4089                         if (LOWORD(lparam) == HTCLIENT) {
4090                                 POINT pt;
4091                                 GetCursorPos(&pt);
4092                                 ScreenToClient(hwnd, &pt);
4093
4094                                 if (pt.x>=child->split_pos-SPLIT_WIDTH/2 && pt.x<child->split_pos+SPLIT_WIDTH/2+1) {
4095                                         SetCursor(LoadCursorW(0, (LPCWSTR)IDC_SIZEWE));
4096                                         return TRUE;
4097                                 }
4098                         }
4099                         goto def;
4100
4101                 case WM_LBUTTONDOWN: {
4102                         RECT rt;
4103                         int x = (short)LOWORD(lparam);
4104
4105                         GetClientRect(hwnd, &rt);
4106
4107                         if (x>=child->split_pos-SPLIT_WIDTH/2 && x<child->split_pos+SPLIT_WIDTH/2+1) {
4108                                 last_split = child->split_pos;
4109 #ifdef _NO_EXTENSIONS
4110                                 draw_splitbar(hwnd, last_split);
4111 #endif
4112                                 SetCapture(hwnd);
4113                         }
4114
4115                         break;}
4116
4117                 case WM_LBUTTONUP:
4118                         if (GetCapture() == hwnd) {
4119 #ifdef _NO_EXTENSIONS
4120                                 RECT rt;
4121                                 int x = (short)LOWORD(lparam);
4122                                 draw_splitbar(hwnd, last_split);
4123                                 last_split = -1;
4124                                 GetClientRect(hwnd, &rt);
4125                                 child->split_pos = x;
4126                                 resize_tree(child, rt.right, rt.bottom);
4127 #endif
4128                                 ReleaseCapture();
4129                         }
4130                         break;
4131
4132 #ifdef _NO_EXTENSIONS
4133                 case WM_CAPTURECHANGED:
4134                         if (GetCapture()==hwnd && last_split>=0)
4135                                 draw_splitbar(hwnd, last_split);
4136                         break;
4137 #endif
4138
4139                 case WM_KEYDOWN:
4140                         if (wparam == VK_ESCAPE)
4141                                 if (GetCapture() == hwnd) {
4142                                         RECT rt;
4143 #ifdef _NO_EXTENSIONS
4144                                         draw_splitbar(hwnd, last_split);
4145 #else
4146                                         child->split_pos = last_split;
4147 #endif
4148                                         GetClientRect(hwnd, &rt);
4149                                         resize_tree(child, rt.right, rt.bottom);
4150                                         last_split = -1;
4151                                         ReleaseCapture();
4152                                         SetCursor(LoadCursorW(0, (LPCWSTR)IDC_ARROW));
4153                                 }
4154                         break;
4155
4156                 case WM_MOUSEMOVE:
4157                         if (GetCapture() == hwnd) {
4158                                 RECT rt;
4159                                 int x = (short)LOWORD(lparam);
4160
4161 #ifdef _NO_EXTENSIONS
4162                                 HDC hdc = GetDC(hwnd);
4163                                 GetClientRect(hwnd, &rt);
4164
4165                                 rt.left = last_split-SPLIT_WIDTH/2;
4166                                 rt.right = last_split+SPLIT_WIDTH/2+1;
4167                                 InvertRect(hdc, &rt);
4168
4169                                 last_split = x;
4170                                 rt.left = x-SPLIT_WIDTH/2;
4171                                 rt.right = x+SPLIT_WIDTH/2+1;
4172                                 InvertRect(hdc, &rt);
4173
4174                                 ReleaseDC(hwnd, hdc);
4175 #else
4176                                 GetClientRect(hwnd, &rt);
4177
4178                                 if (x>=0 && x<rt.right) {
4179                                         child->split_pos = x;
4180                                         resize_tree(child, rt.right, rt.bottom);
4181                                         rt.left = x-SPLIT_WIDTH/2;
4182                                         rt.right = x+SPLIT_WIDTH/2+1;
4183                                         InvalidateRect(hwnd, &rt, FALSE);
4184                                         UpdateWindow(child->left.hwnd);
4185                                         UpdateWindow(hwnd);
4186                                         UpdateWindow(child->right.hwnd);
4187                                 }
4188 #endif
4189                         }
4190                         break;
4191
4192 #ifndef _NO_EXTENSIONS
4193                 case WM_GETMINMAXINFO:
4194                         DefMDIChildProcW(hwnd, nmsg, wparam, lparam);
4195
4196                         {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
4197
4198                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
4199                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
4200                         break;}
4201 #endif /* _NO_EXTENSIONS */
4202
4203                 case WM_SETFOCUS:
4204                         if (SetCurrentDirectoryW(child->path))
4205                                 set_space_status();
4206                         SetFocus(child->focus_pane? child->right.hwnd: child->left.hwnd);
4207                         break;
4208
4209                 case WM_DISPATCH_COMMAND: {
4210                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4211
4212                         switch(LOWORD(wparam)) {
4213                                 case ID_WINDOW_NEW: {
4214                                         ChildWnd* new_child = alloc_child_window(child->path, NULL, hwnd);
4215
4216                                         if (!create_child_window(new_child))
4217                                                 HeapFree(GetProcessHeap(), 0, new_child);
4218
4219                                         break;}
4220
4221                                 case ID_REFRESH:
4222                                         refresh_drives();
4223                                         refresh_child(child);
4224                                         break;
4225
4226                                 case ID_ACTIVATE:
4227                                         activate_entry(child, pane, hwnd);
4228                                         break;
4229
4230                                 case ID_FILE_MOVE: {
4231                                         WCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4232
4233                                         if (prompt_target(pane, source, target)) {
4234                                                 SHFILEOPSTRUCTW shfo = {hwnd, FO_MOVE, source, target};
4235
4236                                                 source[lstrlenW(source)+1] = '\0';
4237                                                 target[lstrlenW(target)+1] = '\0';
4238
4239                                                 if (!SHFileOperationW(&shfo))
4240                                                         refresh_child(child);
4241                                         }
4242                                         break;}
4243
4244                                 case ID_FILE_COPY: {
4245                                         WCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4246
4247                                         if (prompt_target(pane, source, target)) {
4248                                                 SHFILEOPSTRUCTW shfo = {hwnd, FO_COPY, source, target};
4249
4250                                                 source[lstrlenW(source)+1] = '\0';
4251                                                 target[lstrlenW(target)+1] = '\0';
4252
4253                                                 if (!SHFileOperationW(&shfo))
4254                                                         refresh_child(child);
4255                                         }
4256                                         break;}
4257
4258                                 case ID_FILE_DELETE: {
4259                                         WCHAR path[BUFFER_LEN];
4260                                         SHFILEOPSTRUCTW shfo = {hwnd, FO_DELETE, path, NULL, FOF_ALLOWUNDO};
4261
4262                                         get_path(pane->cur, path);
4263
4264                                         path[lstrlenW(path)+1] = '\0';
4265
4266                                         if (!SHFileOperationW(&shfo))
4267                                                 refresh_child(child);
4268                                         break;}
4269
4270                                 case ID_VIEW_SORT_NAME:
4271                                         set_sort_order(child, SORT_NAME);
4272                                         break;
4273
4274                                 case ID_VIEW_SORT_TYPE:
4275                                         set_sort_order(child, SORT_EXT);
4276                                         break;
4277
4278                                 case ID_VIEW_SORT_SIZE:
4279                                         set_sort_order(child, SORT_SIZE);
4280                                         break;
4281
4282                                 case ID_VIEW_SORT_DATE:
4283                                         set_sort_order(child, SORT_DATE);
4284                                         break;
4285
4286                                 case ID_VIEW_FILTER: {
4287                                         struct FilterDialog dlg;
4288
4289                                         memset(&dlg, 0, sizeof(struct FilterDialog));
4290                                         lstrcpyW(dlg.pattern, child->filter_pattern);
4291                                         dlg.flags = child->filter_flags;
4292
4293                                         if (DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_DIALOG_VIEW_TYPE), hwnd, FilterDialogDlgProc, (LPARAM)&dlg) == IDOK) {
4294                                                 lstrcpyW(child->filter_pattern, dlg.pattern);
4295                                                 child->filter_flags = dlg.flags;
4296                                                 refresh_right_pane(child);
4297                                         }
4298                                         break;}
4299
4300                                 case ID_VIEW_SPLIT: {
4301                                         last_split = child->split_pos;
4302 #ifdef _NO_EXTENSIONS
4303                                         draw_splitbar(hwnd, last_split);
4304 #endif
4305                                         SetCapture(hwnd);
4306                                         break;}
4307
4308                                 case ID_EDIT_PROPERTIES:
4309                                         show_properties_dlg(pane->cur, child->hwnd);
4310                                         break;
4311
4312                                 default:
4313                                         return pane_command(pane, LOWORD(wparam));
4314                         }
4315
4316                         return TRUE;}
4317
4318                 case WM_COMMAND: {
4319                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4320
4321                         switch(HIWORD(wparam)) {
4322                                 case LBN_SELCHANGE: {
4323                                         int idx = SendMessageW(pane->hwnd, LB_GETCURSEL, 0, 0);
4324                                         Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, idx, 0);
4325
4326                                         if (pane == &child->left)
4327                                                 set_curdir(child, entry, idx, hwnd);
4328                                         else
4329                                                 pane->cur = entry;
4330                                         break;}
4331
4332                                 case LBN_DBLCLK:
4333                                         activate_entry(child, pane, hwnd);
4334                                         break;
4335                         }
4336                         break;}
4337
4338 #ifndef _NO_EXTENSIONS
4339                 case WM_NOTIFY: {
4340                         NMHDR* pnmh = (NMHDR*) lparam;
4341                         return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &child->left: &child->right, pnmh);}
4342 #endif
4343
4344 #ifdef _SHELL_FOLDERS
4345                 case WM_CONTEXTMENU: {
4346                         POINT pt, pt_clnt;
4347                         Pane* pane;
4348                         int idx;
4349
4350                          /* first select the current item in the listbox */
4351                         HWND hpanel = (HWND) wparam;
4352                         pt_clnt.x = pt.x = (short)LOWORD(lparam);
4353                         pt_clnt.y = pt.y = (short)HIWORD(lparam);
4354                         ScreenToClient(hpanel, &pt_clnt);
4355                         SendMessageW(hpanel, WM_LBUTTONDOWN, 0, MAKELONG(pt_clnt.x, pt_clnt.y));
4356                         SendMessageW(hpanel, WM_LBUTTONUP, 0, MAKELONG(pt_clnt.x, pt_clnt.y));
4357
4358                          /* now create the popup menu using shell namespace and IContextMenu */
4359                         pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4360                         idx = SendMessageW(pane->hwnd, LB_GETCURSEL, 0, 0);
4361
4362                         if (idx != -1) {
4363                                 Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, idx, 0);
4364
4365                                 LPITEMIDLIST pidl_abs = get_to_absolute_pidl(entry, hwnd);
4366
4367                                 if (pidl_abs) {
4368                                         IShellFolder* parentFolder;
4369                                         LPCITEMIDLIST pidlLast;
4370
4371                                          /* get and use the parent folder to display correct context menu in all cases */
4372                                         if (SUCCEEDED(SHBindToParent(pidl_abs, &IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast))) {
4373                                                 if (ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pt.x, pt.y) == S_OK)
4374                                                         refresh_child(child);
4375
4376                                                 IShellFolder_Release(parentFolder);
4377                                         }
4378
4379                                         IMalloc_Free(Globals.iMalloc, pidl_abs);
4380                                 }
4381                         }
4382                         break;}
4383 #endif
4384
4385                   case WM_MEASUREITEM:
4386                   draw_menu_item:
4387                         if (!wparam)    /* Is the message menu-related? */
4388                                 if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4389                                         return TRUE;
4390
4391                         break;
4392
4393                   case WM_INITMENUPOPUP:
4394                         if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4395                                 return 0;
4396
4397                         update_view_menu(child);
4398                         break;
4399
4400                   case WM_MENUCHAR:     /* only supported by IContextMenu3 */
4401                    if (s_pctxmenu3) {
4402                            LRESULT lResult = 0;
4403
4404                            IContextMenu3_HandleMenuMsg2(s_pctxmenu3, nmsg, wparam, lparam, &lResult);
4405
4406                            return lResult;
4407                    }
4408
4409                    break;
4410
4411                 case WM_SIZE:
4412                         if (wparam != SIZE_MINIMIZED)
4413                                 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
4414                         /* fall through */
4415
4416                 default: def:
4417                         return DefMDIChildProcW(hwnd, nmsg, wparam, lparam);
4418         }
4419
4420         return 0;
4421 }
4422
4423
4424 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4425 {
4426         ChildWnd* child = (ChildWnd*)GetWindowLongPtrW(GetParent(hwnd), GWLP_USERDATA);
4427         Pane* pane = (Pane*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
4428         ASSERT(child);
4429
4430         switch(nmsg) {
4431 #ifndef _NO_EXTENSIONS
4432                 case WM_HSCROLL:
4433                         set_header(pane);
4434                         break;
4435 #endif
4436
4437                 case WM_SETFOCUS:
4438                         child->focus_pane = pane==&child->right? 1: 0;
4439                         SendMessageW(hwnd, LB_SETSEL, TRUE, 1);
4440                         /*TODO: check menu items */
4441                         break;
4442
4443                 case WM_KEYDOWN:
4444                         if (wparam == VK_TAB) {
4445                                 /*TODO: SetFocus(Globals.hdrivebar) */
4446                                 SetFocus(child->focus_pane? child->left.hwnd: child->right.hwnd);
4447                         }
4448         }
4449
4450         return CallWindowProcW(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
4451 }
4452
4453
4454 static void InitInstance(HINSTANCE hinstance)
4455 {
4456         static const WCHAR sFont[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
4457
4458         WNDCLASSEXW wcFrame;
4459         WNDCLASSW wcChild;
4460         int col;
4461
4462         INITCOMMONCONTROLSEX icc = {
4463                 sizeof(INITCOMMONCONTROLSEX),
4464                 ICC_BAR_CLASSES
4465         };
4466
4467         HDC hdc = GetDC(0);
4468
4469         setlocale(LC_COLLATE, "");      /* set collating rules to local settings for compareName */
4470
4471         InitCommonControlsEx(&icc);
4472
4473
4474         /* register frame window class */
4475
4476         wcFrame.cbSize        = sizeof(WNDCLASSEXW);
4477         wcFrame.style         = 0;
4478         wcFrame.lpfnWndProc   = FrameWndProc;
4479         wcFrame.cbClsExtra    = 0;
4480         wcFrame.cbWndExtra    = 0;
4481         wcFrame.hInstance     = hinstance;
4482         wcFrame.hIcon         = LoadIconW(hinstance, MAKEINTRESOURCEW(IDI_WINEFILE));
4483         wcFrame.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
4484         wcFrame.hbrBackground = 0;
4485         wcFrame.lpszMenuName  = 0;
4486         wcFrame.lpszClassName = sWINEFILEFRAME;
4487         wcFrame.hIconSm       = LoadImageW(hinstance, MAKEINTRESOURCEW(IDI_WINEFILE), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
4488
4489         Globals.hframeClass = RegisterClassExW(&wcFrame);
4490
4491
4492         /* register tree windows class */
4493
4494         wcChild.style         = CS_CLASSDC|CS_DBLCLKS|CS_VREDRAW;
4495         wcChild.lpfnWndProc   = ChildWndProc;
4496         wcChild.cbClsExtra    = 0;
4497         wcChild.cbWndExtra    = 0;
4498         wcChild.hInstance     = hinstance;
4499         wcChild.hIcon         = 0;
4500         wcChild.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
4501         wcChild.hbrBackground = 0;
4502         wcChild.lpszMenuName  = 0;
4503         wcChild.lpszClassName = sWINEFILETREE;
4504
4505         RegisterClassW(&wcChild);
4506
4507
4508         Globals.haccel = LoadAcceleratorsW(hinstance, MAKEINTRESOURCEW(IDA_WINEFILE));
4509
4510         Globals.hfont = CreateFontW(-MulDiv(8,GetDeviceCaps(hdc,LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sFont);
4511
4512         ReleaseDC(0, hdc);
4513
4514         Globals.hInstance = hinstance;
4515
4516 #ifdef _SHELL_FOLDERS
4517         CoInitialize(NULL);
4518         CoGetMalloc(MEMCTX_TASK, &Globals.iMalloc);
4519         SHGetDesktopFolder(&Globals.iDesktop);
4520         Globals.cfStrFName = RegisterClipboardFormatW(CFSTR_FILENAMEW);
4521 #endif
4522
4523         /* load column strings */
4524         col = 1;
4525
4526         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_NAME);
4527         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_SIZE);
4528         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_CDATE);
4529 #ifndef _NO_EXTENSIONS
4530         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_ADATE);
4531         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_MDATE);
4532         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_IDX);
4533         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_LINKS);
4534 #endif
4535         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_ATTR);
4536 #ifndef _NO_EXTENSIONS
4537         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_SEC);
4538 #endif
4539 }
4540
4541
4542 static BOOL show_frame(HWND hwndParent, int cmdshow, LPCWSTR path)
4543 {
4544         static const WCHAR sMDICLIENT[] = {'M','D','I','C','L','I','E','N','T','\0'};
4545
4546         WCHAR buffer[MAX_PATH], b1[BUFFER_LEN];
4547         ChildWnd* child;
4548         HMENU hMenuFrame, hMenuWindow;
4549         windowOptions opts;
4550
4551         CLIENTCREATESTRUCT ccs;
4552
4553         if (Globals.hMainWnd)
4554                 return TRUE;
4555
4556         opts = load_registry_settings();
4557         hMenuFrame = LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(IDM_WINEFILE));
4558         hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
4559
4560         Globals.hMenuFrame = hMenuFrame;
4561         Globals.hMenuView = GetSubMenu(hMenuFrame, 2);
4562         Globals.hMenuOptions = GetSubMenu(hMenuFrame, 3);
4563
4564         ccs.hWindowMenu  = hMenuWindow;
4565         ccs.idFirstChild = IDW_FIRST_CHILD;
4566
4567
4568         /* create main window */
4569         Globals.hMainWnd = CreateWindowExW(0, MAKEINTRESOURCEW(Globals.hframeClass), RS(b1,IDS_WINE_FILE), WS_OVERLAPPEDWINDOW,
4570                                         opts.start_x, opts.start_y, opts.width, opts.height,
4571                                         hwndParent, Globals.hMenuFrame, Globals.hInstance, 0/*lpParam*/);
4572
4573
4574         Globals.hmdiclient = CreateWindowExW(0, sMDICLIENT, NULL,
4575                                         WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|WS_BORDER,
4576                                         0, 0, 0, 0,
4577                                         Globals.hMainWnd, 0, Globals.hInstance, &ccs);
4578   
4579         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
4580         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_SAVESETTINGS, MF_BYCOMMAND);
4581
4582         create_drive_bar();
4583
4584         {
4585                 TBBUTTON toolbarBtns[] = {
4586                         {0, 0, 0, BTNS_SEP, {0, 0}, 0, 0},
4587                         {0, ID_WINDOW_NEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4588                         {1, ID_WINDOW_CASCADE, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4589                         {2, ID_WINDOW_TILE_HORZ, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4590                         {3, ID_WINDOW_TILE_VERT, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4591                 };
4592
4593                 Globals.htoolbar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE,
4594                         IDW_TOOLBAR, 2, Globals.hInstance, IDB_TOOLBAR, toolbarBtns,
4595                         sizeof(toolbarBtns)/sizeof(TBBUTTON), 16, 15, 16, 15, sizeof(TBBUTTON));
4596                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_TOOL_BAR, MF_BYCOMMAND|MF_CHECKED);
4597         }
4598
4599         Globals.hstatusbar = CreateStatusWindowW(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
4600         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
4601
4602         /*TODO: read paths from registry */
4603
4604         if (!path || !*path) {
4605                 GetCurrentDirectoryW(MAX_PATH, buffer);
4606                 path = buffer;
4607         }
4608
4609         ShowWindow(Globals.hMainWnd, cmdshow);
4610
4611 #if defined(_SHELL_FOLDERS) && !defined(__WINE__)
4612          /* Shell Namespace as default: */
4613         child = alloc_child_window(path, get_path_pidl(path,Globals.hMainWnd), Globals.hMainWnd);
4614 #else
4615         child = alloc_child_window(path, NULL, Globals.hMainWnd);
4616 #endif
4617
4618         child->pos.showCmd = SW_SHOWMAXIMIZED;
4619         child->pos.rcNormalPosition.left = 0;
4620         child->pos.rcNormalPosition.top = 0;
4621         child->pos.rcNormalPosition.right = 320;
4622         child->pos.rcNormalPosition.bottom = 280;
4623
4624         if (!create_child_window(child)) {
4625                 HeapFree(GetProcessHeap(), 0, child);
4626                 return FALSE;
4627         }
4628
4629         SetWindowPlacement(child->hwnd, &child->pos);
4630
4631         Globals.himl = ImageList_LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDB_IMAGES), 16, 0, RGB(0,255,0), IMAGE_BITMAP, 0);
4632
4633         Globals.prescan_node = FALSE;
4634
4635         UpdateWindow(Globals.hMainWnd);
4636
4637         if (child->hwnd && path && path[0])
4638         {
4639                 int index,count;
4640                 WCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
4641                 WCHAR fullname[_MAX_FNAME+_MAX_EXT+1];
4642
4643                 memset(name,0,sizeof(name));
4644                 memset(name,0,sizeof(ext));
4645                 _wsplitpath(path, drv, dir, name, ext);
4646                 if (name[0])
4647                 {
4648                         count = SendMessageW(child->right.hwnd, LB_GETCOUNT, 0, 0);
4649                         lstrcpyW(fullname,name);
4650                         lstrcatW(fullname,ext);
4651
4652                         for (index = 0; index < count; index ++)
4653                         {
4654                                 Entry* entry = (Entry*)SendMessageW(child->right.hwnd, LB_GETITEMDATA, index, 0);
4655                                 if (lstrcmpW(entry->data.cFileName,fullname)==0 ||
4656                                                 lstrcmpW(entry->data.cAlternateFileName,fullname)==0)
4657                                 {
4658                                         SendMessageW(child->right.hwnd, LB_SETCURSEL, index, 0);
4659                                         SetFocus(child->right.hwnd);
4660                                         break;
4661                                 }
4662                         }
4663                 }
4664         }
4665         return TRUE;
4666 }
4667
4668 static void ExitInstance(void)
4669 {
4670 #ifdef _SHELL_FOLDERS
4671         IShellFolder_Release(Globals.iDesktop);
4672         IMalloc_Release(Globals.iMalloc);
4673         CoUninitialize();
4674 #endif
4675
4676         DeleteObject(Globals.hfont);
4677         ImageList_Destroy(Globals.himl);
4678 }
4679
4680 #ifdef _NO_EXTENSIONS
4681
4682 /* search for already running win[e]files */
4683
4684 static int g_foundPrevInstance = 0;
4685
4686 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
4687 {
4688         WCHAR cls[128];
4689
4690         GetClassNameW(hwnd, cls, 128);
4691
4692         if (!lstrcmpW(cls, (LPCWSTR)lparam)) {
4693                 g_foundPrevInstance++;
4694                 return FALSE;
4695         }
4696
4697         return TRUE;
4698 }
4699
4700 /* search for window of given class name to allow only one running instance */
4701 static int find_window_class(LPCWSTR classname)
4702 {
4703         EnumWindows(EnumWndProc, (LPARAM)classname);
4704
4705         if (g_foundPrevInstance)
4706                 return 1;
4707
4708         return 0;
4709 }
4710
4711 #endif
4712
4713 static int winefile_main(HINSTANCE hinstance, int cmdshow, LPCWSTR path)
4714 {
4715         MSG msg;
4716
4717         InitInstance(hinstance);
4718
4719         if( !show_frame(0, cmdshow, path) )
4720         {
4721                 ExitInstance();
4722                 return 1;
4723         }
4724
4725         while(GetMessageW(&msg, 0, 0, 0)) {
4726                 if (Globals.hmdiclient && TranslateMDISysAccel(Globals.hmdiclient, &msg))
4727                         continue;
4728
4729                 if (Globals.hMainWnd && TranslateAcceleratorW(Globals.hMainWnd, Globals.haccel, &msg))
4730                         continue;
4731
4732                 TranslateMessage(&msg);
4733                 DispatchMessageW(&msg);
4734         }
4735
4736         ExitInstance();
4737
4738         return msg.wParam;
4739 }
4740
4741
4742 #if defined(_MSC_VER)
4743 int APIENTRY wWinMain(HINSTANCE hinstance, HINSTANCE previnstance, LPWSTR cmdline, int cmdshow)
4744 #else
4745 int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE previnstance, LPSTR cmdline, int cmdshow)
4746 #endif
4747 {
4748 #ifdef _NO_EXTENSIONS
4749         if (find_window_class(sWINEFILEFRAME))
4750                 return 1;
4751 #endif
4752
4753         { /* convert ANSI cmdline into WCS path string */
4754         WCHAR buffer[MAX_PATH];
4755         MultiByteToWideChar(CP_ACP, 0, cmdline, -1, buffer, MAX_PATH);
4756         winefile_main(hinstance, cmdshow, buffer);
4757         }
4758
4759         return 0;
4760 }