include: Make "long" numeric constants LP64/LLP64 proof (shlwapi.h).
[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;
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         static const WCHAR sAsterics[] = {'*', '\0'};
1357         static const WCHAR sTitleFmt[] = {'%','s',' ','-',' ','%','s','\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, sTitleFmt, 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                 DWORD_PTR args[2];
2778                 format_bytes(b1, ulFreeBytesToCaller.QuadPart);
2779                 format_bytes(b2, ulTotalBytes.QuadPart);
2780                 args[0] = (DWORD_PTR)b1;
2781                 args[1] = (DWORD_PTR)b2;
2782                 FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
2783                                RS(fmt,IDS_FREE_SPACE_FMT), 0, 0, buffer,
2784                                sizeof(buffer)/sizeof(*buffer), (__ms_va_list*)args);
2785         } else
2786                 lstrcpyW(buffer, sQMarks);
2787
2788         SendMessageW(Globals.hstatusbar, SB_SETTEXTW, 0, (LPARAM)buffer);
2789 }
2790
2791
2792 static WNDPROC g_orgTreeWndProc;
2793
2794 static void create_tree_window(HWND parent, Pane* pane, UINT id, UINT id_header, LPCWSTR pattern, int filter_flags)
2795 {
2796         static const WCHAR sListBox[] = {'L','i','s','t','B','o','x','\0'};
2797
2798         static int s_init = 0;
2799         Entry* entry = pane->root;
2800
2801         pane->hwnd = CreateWindowW(sListBox, sEmpty, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
2802                                   LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
2803                                   0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2804
2805         SetWindowLongPtrW(pane->hwnd, GWLP_USERDATA, (LPARAM)pane);
2806         g_orgTreeWndProc = (WNDPROC)SetWindowLongPtrW(pane->hwnd, GWLP_WNDPROC, (LPARAM)TreeWndProc);
2807
2808         SendMessageW(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2809
2810         /* insert entries into listbox */
2811         if (entry)
2812                 insert_entries(pane, entry, pattern, filter_flags, -1);
2813
2814         /* calculate column widths */
2815         if (!s_init) {
2816                 s_init = 1;
2817                 init_output(pane->hwnd);
2818         }
2819
2820         calc_widths(pane, TRUE);
2821
2822 #ifndef _NO_EXTENSIONS
2823         pane->hwndHeader = create_header(parent, pane, id_header);
2824 #endif
2825 }
2826
2827
2828 static void InitChildWindow(ChildWnd* child)
2829 {
2830         create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT, NULL, TF_ALL);
2831         create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, child->filter_pattern, child->filter_flags);
2832 }
2833
2834
2835 static void format_date(const FILETIME* ft, WCHAR* buffer, int visible_cols)
2836 {
2837         SYSTEMTIME systime;
2838         FILETIME lft;
2839         int len = 0;
2840
2841         *buffer = '\0';
2842
2843         if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2844                 return;
2845
2846         if (!FileTimeToLocalFileTime(ft, &lft))
2847                 {err: lstrcpyW(buffer,sQMarks); return;}
2848
2849         if (!FileTimeToSystemTime(&lft, &systime))
2850                 goto err;
2851
2852         if (visible_cols & COL_DATE) {
2853                 len = GetDateFormatW(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
2854                 if (!len)
2855                         goto err;
2856         }
2857
2858         if (visible_cols & COL_TIME) {
2859                 if (len)
2860                         buffer[len-1] = ' ';
2861
2862                 buffer[len++] = ' ';
2863
2864                 if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
2865                         buffer[len] = '\0';
2866         }
2867 }
2868
2869
2870 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2871 {
2872         RECT rt = {0, 0, 0, 0};
2873
2874         DrawTextW(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2875
2876         if (rt.right > pane->widths[col])
2877                 pane->widths[col] = rt.right;
2878 }
2879
2880 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2881 {
2882         RECT rt = {0, 0, 0, 0};
2883
2884         DrawTextW(dis->hDC, str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2885         /*FIXME rt (0,0) ??? */
2886
2887         if (rt.right > pane->widths[col])
2888                 pane->widths[col] = rt.right;
2889 }
2890
2891
2892 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str, DWORD flags)
2893 {
2894         int x = dis->rcItem.left;
2895         RECT rt;
2896
2897         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2898         rt.top    = dis->rcItem.top;
2899         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2900         rt.bottom = dis->rcItem.bottom;
2901
2902         DrawTextW(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
2903 }
2904
2905 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2906 {
2907         int x = dis->rcItem.left;
2908         RECT rt;
2909
2910         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2911         rt.top    = dis->rcItem.top;
2912         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2913         rt.bottom = dis->rcItem.bottom;
2914
2915         DrawTextW(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2916 }
2917
2918 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2919 {
2920         int x = dis->rcItem.left;
2921         RECT rt;
2922         LPCWSTR s = str;
2923         WCHAR b[128];
2924         LPWSTR d = b;
2925         int pos;
2926
2927         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2928         rt.top    = dis->rcItem.top;
2929         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2930         rt.bottom = dis->rcItem.bottom;
2931
2932         if (*s)
2933                 *d++ = *s++;
2934
2935         /* insert number separator characters */
2936         pos = lstrlenW(s) % 3;
2937
2938         while(*s)
2939                 if (pos--)
2940                         *d++ = *s++;
2941                 else {
2942                         *d++ = Globals.num_sep;
2943                         pos = 3;
2944                 }
2945
2946         DrawTextW(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
2947 }
2948
2949
2950 static BOOL is_exe_file(LPCWSTR ext)
2951 {
2952         static const WCHAR executable_extensions[][4] = {
2953                 {'C','O','M','\0'},
2954                 {'E','X','E','\0'},
2955                 {'B','A','T','\0'},
2956                 {'C','M','D','\0'},
2957 #ifndef _NO_EXTENSIONS
2958                 {'C','M','M','\0'},
2959                 {'B','T','M','\0'},
2960                 {'A','W','K','\0'},
2961 #endif /* _NO_EXTENSIONS */
2962                 {'\0'}
2963         };
2964
2965         WCHAR ext_buffer[_MAX_EXT];
2966         const WCHAR (*p)[4];
2967         LPCWSTR s;
2968         LPWSTR d;
2969
2970         for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
2971                 d++;
2972
2973         for(p=executable_extensions; (*p)[0]; p++)
2974                 if (!lstrcmpiW(ext_buffer, *p))
2975                         return TRUE;
2976
2977         return FALSE;
2978 }
2979
2980 static BOOL is_registered_type(LPCWSTR ext)
2981 {
2982         /* check if there exists a classname for this file extension in the registry */
2983         if (!RegQueryValueW(HKEY_CLASSES_ROOT, ext, NULL, NULL))
2984                 return TRUE;
2985
2986         return FALSE;
2987 }
2988
2989 static enum FILE_TYPE get_file_type(LPCWSTR filename)
2990 {
2991         LPCWSTR ext = strrchrW(filename, '.');
2992         if (!ext)
2993                 ext = sEmpty;
2994
2995         if (is_exe_file(ext))
2996                 return FT_EXECUTABLE;
2997         else if (is_registered_type(ext))
2998                 return FT_DOCUMENT;
2999         else
3000                 return FT_OTHER;
3001 }
3002
3003
3004 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
3005 {
3006         WCHAR buffer[BUFFER_LEN];
3007         DWORD attrs;
3008         int visible_cols = pane->visible_cols;
3009         COLORREF bkcolor, textcolor;
3010         RECT focusRect = dis->rcItem;
3011         HBRUSH hbrush;
3012         enum IMAGE img;
3013         int img_pos, cx;
3014         int col = 0;
3015
3016         if (entry) {
3017                 attrs = entry->data.dwFileAttributes;
3018
3019                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
3020                         if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '.'
3021                                         && entry->data.cFileName[2] == '\0')
3022                                 img = IMG_FOLDER_UP;
3023 #ifndef _NO_EXTENSIONS
3024                         else if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '\0')
3025                                 img = IMG_FOLDER_CUR;
3026 #endif
3027                         else if (
3028 #ifdef _NO_EXTENSIONS
3029                                          entry->expanded ||
3030 #endif
3031                                          (pane->treePane && (dis->itemState&ODS_FOCUS)))
3032                                 img = IMG_OPEN_FOLDER;
3033                         else
3034                                 img = IMG_FOLDER;
3035                 } else {
3036                         switch(get_file_type(entry->data.cFileName)) {
3037                           case FT_EXECUTABLE:   img = IMG_EXECUTABLE;   break;
3038                           case FT_DOCUMENT:             img = IMG_DOCUMENT;             break;
3039                           default:                              img = IMG_FILE;
3040                         }
3041                 }
3042         } else {
3043                 attrs = 0;
3044                 img = IMG_NONE;
3045         }
3046
3047         if (pane->treePane) {
3048                 if (entry) {
3049                         img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+TREE_LINE_DX);
3050
3051                         if (calcWidthCol == -1) {
3052                                 int x;
3053                                 int y = dis->rcItem.top + IMAGE_HEIGHT/2;
3054                                 Entry* up;
3055                                 RECT rt_clip;
3056                                 HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
3057                                 HRGN hrgn;
3058
3059                                 rt_clip.left   = dis->rcItem.left;
3060                                 rt_clip.top    = dis->rcItem.top;
3061                                 rt_clip.right  = dis->rcItem.left+pane->widths[col];
3062                                 rt_clip.bottom = dis->rcItem.bottom;
3063
3064                                 hrgn = CreateRectRgnIndirect(&rt_clip);
3065
3066                                 if (!GetClipRgn(dis->hDC, hrgn_org)) {
3067                                         DeleteObject(hrgn_org);
3068                                         hrgn_org = 0;
3069                                 }
3070
3071                                 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
3072                                 DeleteObject(hrgn);
3073
3074                                 if ((up=entry->up) != NULL) {
3075                                         MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
3076                                         LineTo(dis->hDC, img_pos-2, y);
3077
3078                                         x = img_pos - IMAGE_WIDTH/2;
3079
3080                                         do {
3081                                                 x -= IMAGE_WIDTH+TREE_LINE_DX;
3082
3083                                                 if (up->next
3084 #ifndef _LEFT_FILES
3085                                                         && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
3086 #endif
3087                                                         ) {
3088                                                         MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3089                                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
3090                                                 }
3091                                         } while((up=up->up) != NULL);
3092                                 }
3093
3094                                 x = img_pos - IMAGE_WIDTH/2;
3095
3096                                 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
3097                                 LineTo(dis->hDC, x, y);
3098
3099                                 if (entry->next
3100 #ifndef _LEFT_FILES
3101                                         && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
3102 #endif
3103                                         )
3104                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
3105
3106                                 SelectClipRgn(dis->hDC, hrgn_org);
3107                                 if (hrgn_org) DeleteObject(hrgn_org);
3108                         } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
3109                                 int right = img_pos + IMAGE_WIDTH - TREE_LINE_DX;
3110
3111                                 if (right > pane->widths[col])
3112                                         pane->widths[col] = right;
3113                         }
3114                 } else  {
3115                         img_pos = dis->rcItem.left;
3116                 }
3117         } else {
3118                 img_pos = dis->rcItem.left;
3119
3120                 if (calcWidthCol==col || calcWidthCol==COLUMNS)
3121                         pane->widths[col] = IMAGE_WIDTH;
3122         }
3123
3124         if (calcWidthCol == -1) {
3125                 focusRect.left = img_pos -2;
3126
3127 #ifdef _NO_EXTENSIONS
3128                 if (pane->treePane && entry) {
3129                         RECT rt = {0};
3130
3131                         DrawTextW(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
3132
3133                         focusRect.right = dis->rcItem.left+pane->positions[col+1]+TREE_LINE_DX + rt.right +2;
3134                 }
3135 #else
3136
3137                 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
3138                         textcolor = COLOR_COMPRESSED;
3139                 else
3140 #endif /* _NO_EXTENSIONS */
3141                         textcolor = RGB(0,0,0);
3142
3143                 if (dis->itemState & ODS_FOCUS) {
3144                         textcolor = RGB(255,255,255);
3145                         bkcolor = COLOR_SELECTION;
3146                 } else {
3147                         bkcolor = RGB(255,255,255);
3148                 }
3149
3150                 hbrush = CreateSolidBrush(bkcolor);
3151                 FillRect(dis->hDC, &focusRect, hbrush);
3152                 DeleteObject(hbrush);
3153
3154                 SetBkMode(dis->hDC, TRANSPARENT);
3155                 SetTextColor(dis->hDC, textcolor);
3156
3157                 cx = pane->widths[col];
3158
3159                 if (cx && img!=IMG_NONE) {
3160                         if (cx > IMAGE_WIDTH)
3161                                 cx = IMAGE_WIDTH;
3162
3163 #ifdef _SHELL_FOLDERS
3164                         if (entry->hicon && entry->hicon!=(HICON)-1)
3165                                 DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->hicon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
3166                         else
3167 #endif
3168                                 ImageList_DrawEx(Globals.himl, img, dis->hDC,
3169                                                                  img_pos, dis->rcItem.top, cx,
3170                                                                  IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
3171                 }
3172         }
3173
3174         if (!entry)
3175                 return;
3176
3177 #ifdef _NO_EXTENSIONS
3178         if (img >= IMG_FOLDER_UP)
3179                 return;
3180 #endif
3181
3182         col++;
3183
3184         /* output file name */
3185         if (calcWidthCol == -1)
3186                 output_text(pane, dis, col, entry->data.cFileName, 0);
3187         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3188                 calc_width(pane, dis, col, entry->data.cFileName);
3189
3190         col++;
3191
3192 #ifdef _NO_EXTENSIONS
3193   if (!pane->treePane) {
3194 #endif
3195
3196         /* display file size */
3197         if (visible_cols & COL_SIZE) {
3198 #ifdef _NO_EXTENSIONS
3199                 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
3200 #endif
3201                 {
3202                         format_longlong( buffer, ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow );
3203
3204                         if (calcWidthCol == -1)
3205                                 output_number(pane, dis, col, buffer);
3206                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3207                                 calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
3208                 }
3209
3210                 col++;
3211         }
3212
3213         /* display file date */
3214         if (visible_cols & (COL_DATE|COL_TIME)) {
3215 #ifndef _NO_EXTENSIONS
3216                 format_date(&entry->data.ftCreationTime, buffer, visible_cols);
3217                 if (calcWidthCol == -1)
3218                         output_text(pane, dis, col, buffer, 0);
3219                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3220                         calc_width(pane, dis, col, buffer);
3221                 col++;
3222
3223                 format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
3224                 if (calcWidthCol == -1)
3225                         output_text(pane, dis, col, buffer, 0);
3226                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3227                         calc_width(pane, dis, col, buffer);
3228                 col++;
3229 #endif /* _NO_EXTENSIONS */
3230
3231                 format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
3232                 if (calcWidthCol == -1)
3233                         output_text(pane, dis, col, buffer, 0);
3234                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3235                         calc_width(pane, dis, col, buffer);
3236                 col++;
3237         }
3238
3239 #ifndef _NO_EXTENSIONS
3240         if (entry->bhfi_valid) {
3241                 if (visible_cols & COL_INDEX) {
3242                         static const WCHAR fmtlow[] = {'%','X',0};
3243                         static const WCHAR fmthigh[] = {'%','X','%','0','8','X',0};
3244
3245                         if (entry->bhfi.nFileIndexHigh)
3246                             wsprintfW(buffer, fmthigh,
3247                                      entry->bhfi.nFileIndexHigh, entry->bhfi.nFileIndexLow );
3248                         else
3249                             wsprintfW(buffer, fmtlow, entry->bhfi.nFileIndexLow );
3250
3251                         if (calcWidthCol == -1)
3252                                 output_text(pane, dis, col, buffer, DT_RIGHT);
3253                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3254                                 calc_width(pane, dis, col, buffer);
3255
3256                         col++;
3257                 }
3258
3259                 if (visible_cols & COL_LINKS) {
3260                         wsprintfW(buffer, sNumFmt, entry->bhfi.nNumberOfLinks);
3261
3262                         if (calcWidthCol == -1)
3263                                 output_text(pane, dis, col, buffer, DT_CENTER);
3264                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3265                                 calc_width(pane, dis, col, buffer);
3266
3267                         col++;
3268                 }
3269         } else
3270                 col += 2;
3271 #endif /* _NO_EXTENSIONS */
3272
3273         /* show file attributes */
3274         if (visible_cols & COL_ATTRIBUTES) {
3275 #ifdef _NO_EXTENSIONS
3276                 static const WCHAR s4Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3277                 lstrcpyW(buffer, s4Tabs);
3278 #else
3279                 static const WCHAR s11Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3280                 lstrcpyW(buffer, s11Tabs);
3281 #endif
3282
3283                 if (attrs & FILE_ATTRIBUTE_NORMAL)                                      buffer[ 0] = 'N';
3284                 else {
3285                         if (attrs & FILE_ATTRIBUTE_READONLY)                    buffer[ 2] = 'R';
3286                         if (attrs & FILE_ATTRIBUTE_HIDDEN)                              buffer[ 4] = 'H';
3287                         if (attrs & FILE_ATTRIBUTE_SYSTEM)                              buffer[ 6] = 'S';
3288                         if (attrs & FILE_ATTRIBUTE_ARCHIVE)                             buffer[ 8] = 'A';
3289                         if (attrs & FILE_ATTRIBUTE_COMPRESSED)                  buffer[10] = 'C';
3290 #ifndef _NO_EXTENSIONS
3291                         if (attrs & FILE_ATTRIBUTE_DIRECTORY)                   buffer[12] = 'D';
3292                         if (attrs & FILE_ATTRIBUTE_ENCRYPTED)                   buffer[14] = 'E';
3293                         if (attrs & FILE_ATTRIBUTE_TEMPORARY)                   buffer[16] = 'T';
3294                         if (attrs & FILE_ATTRIBUTE_SPARSE_FILE)                 buffer[18] = 'P';
3295                         if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)               buffer[20] = 'Q';
3296                         if (attrs & FILE_ATTRIBUTE_OFFLINE)                             buffer[22] = 'O';
3297                         if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
3298 #endif /* _NO_EXTENSIONS */
3299                 }
3300
3301                 if (calcWidthCol == -1)
3302                         output_tabbed_text(pane, dis, col, buffer);
3303                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3304                         calc_tabbed_width(pane, dis, col, buffer);
3305
3306                 col++;
3307         }
3308
3309 #ifdef _NO_EXTENSIONS
3310   }
3311
3312         /* draw focus frame */
3313         if ((dis->itemState&ODS_FOCUS) && calcWidthCol==-1) {
3314                 /* Currently [04/2000] Wine neither behaves exactly the same */
3315                 /* way as WIN 95 nor like Windows NT... */
3316                 HGDIOBJ lastBrush;
3317                 HPEN lastPen;
3318                 HPEN hpen;
3319
3320                 if (!(GetVersion() & 0x80000000)) {     /* Windows NT? */
3321                         LOGBRUSH lb = {PS_SOLID, RGB(255,255,255)};
3322                         hpen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, 0);
3323                 } else
3324                         hpen = CreatePen(PS_DOT, 0, RGB(255,255,255));
3325
3326                 lastPen = SelectObject(dis->hDC, hpen);
3327                 lastBrush = SelectObject(dis->hDC, GetStockObject(HOLLOW_BRUSH));
3328                 SetROP2(dis->hDC, R2_XORPEN);
3329                 Rectangle(dis->hDC, focusRect.left, focusRect.top, focusRect.right, focusRect.bottom);
3330                 SelectObject(dis->hDC, lastBrush);
3331                 SelectObject(dis->hDC, lastPen);
3332                 DeleteObject(hpen);
3333         }
3334 #endif /* _NO_EXTENSIONS */
3335 }
3336
3337
3338 #ifdef _NO_EXTENSIONS
3339
3340 static void draw_splitbar(HWND hwnd, int x)
3341 {
3342         RECT rt;
3343         HDC hdc = GetDC(hwnd);
3344
3345         GetClientRect(hwnd, &rt);
3346
3347         rt.left = x - SPLIT_WIDTH/2;
3348         rt.right = x + SPLIT_WIDTH/2+1;
3349
3350         InvertRect(hdc, &rt);
3351
3352         ReleaseDC(hwnd, hdc);
3353 }
3354
3355 #endif /* _NO_EXTENSIONS */
3356
3357
3358 #ifndef _NO_EXTENSIONS
3359
3360 static void set_header(Pane* pane)
3361 {
3362         HDITEMW item;
3363         int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3364         int i=0, x=0;
3365
3366         item.mask = HDI_WIDTH;
3367         item.cxy = 0;
3368
3369         for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
3370                 x += pane->widths[i];
3371                 SendMessageW(pane->hwndHeader, HDM_SETITEMW, i, (LPARAM)&item);
3372         }
3373
3374         if (i < COLUMNS) {
3375                 x += pane->widths[i];
3376                 item.cxy = x - scroll_pos;
3377                 SendMessageW(pane->hwndHeader, HDM_SETITEMW, i++, (LPARAM)&item);
3378
3379                 for(; i<COLUMNS; i++) {
3380                         item.cxy = pane->widths[i];
3381                         x += pane->widths[i];
3382                         SendMessageW(pane->hwndHeader, HDM_SETITEMW, i, (LPARAM)&item);
3383                 }
3384         }
3385 }
3386
3387 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
3388 {
3389         switch(pnmh->code) {
3390                 case HDN_ITEMCHANGEDW: {
3391                         LPNMHEADERW phdn = (LPNMHEADERW)pnmh;
3392                         int idx = phdn->iItem;
3393                         int dx = phdn->pitem->cxy - pane->widths[idx];
3394                         int i;
3395
3396                         RECT clnt;
3397                         GetClientRect(pane->hwnd, &clnt);
3398
3399                         pane->widths[idx] += dx;
3400
3401                         for(i=idx; ++i<=COLUMNS; )
3402                                 pane->positions[i] += dx;
3403
3404                         {
3405                                 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3406                                 RECT rt_scr;
3407                                 RECT rt_clip;
3408
3409                                 rt_scr.left   = pane->positions[idx+1]-scroll_pos;
3410                                 rt_scr.top    = 0;
3411                                 rt_scr.right  = clnt.right;
3412                                 rt_scr.bottom = clnt.bottom;
3413
3414                                 rt_clip.left   = pane->positions[idx]-scroll_pos;
3415                                 rt_clip.top    = 0;
3416                                 rt_clip.right  = clnt.right;
3417                                 rt_clip.bottom = clnt.bottom;
3418
3419                                 if (rt_scr.left < 0) rt_scr.left = 0;
3420                                 if (rt_clip.left < 0) rt_clip.left = 0;
3421
3422                                 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
3423
3424                                 rt_clip.right = pane->positions[idx+1];
3425                                 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
3426
3427                                 if (pnmh->code == HDN_ENDTRACKW) {
3428                                         SendMessageW(pane->hwnd, LB_SETHORIZONTALEXTENT, pane->positions[COLUMNS], 0);
3429
3430                                         if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
3431                                                 set_header(pane);
3432                                 }
3433                         }
3434
3435                         return FALSE;
3436                 }
3437
3438                 case HDN_DIVIDERDBLCLICKW: {
3439                         LPNMHEADERW phdn = (LPNMHEADERW)pnmh;
3440                         HDITEMW item;
3441
3442                         calc_single_width(pane, phdn->iItem);
3443                         item.mask = HDI_WIDTH;
3444                         item.cxy = pane->widths[phdn->iItem];
3445
3446                         SendMessageW(pane->hwndHeader, HDM_SETITEMW, phdn->iItem, (LPARAM)&item);
3447                         InvalidateRect(pane->hwnd, 0, TRUE);
3448                         break;}
3449         }
3450
3451         return 0;
3452 }
3453
3454 #endif /* _NO_EXTENSIONS */
3455
3456
3457 static void scan_entry(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3458 {
3459         WCHAR path[MAX_PATH];
3460         HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
3461
3462         /* delete sub entries in left pane */
3463         for(;;) {
3464                 LRESULT res = SendMessageW(child->left.hwnd, LB_GETITEMDATA, idx+1, 0);
3465                 Entry* sub = (Entry*) res;
3466
3467                 if (res==LB_ERR || !sub || sub->level<=entry->level)
3468                         break;
3469
3470                 SendMessageW(child->left.hwnd, LB_DELETESTRING, idx+1, 0);
3471         }
3472
3473         /* empty right pane */
3474         SendMessageW(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3475
3476         /* release memory */
3477         free_entries(entry);
3478
3479         /* read contents from disk */
3480 #ifdef _SHELL_FOLDERS
3481         if (entry->etype == ET_SHELL)
3482         {
3483                 read_directory(entry, NULL, child->sortOrder, hwnd);
3484         }
3485         else
3486 #endif
3487         {
3488                 get_path(entry, path);
3489                 read_directory(entry, path, child->sortOrder, hwnd);
3490         }
3491
3492         /* insert found entries in right pane */
3493         insert_entries(&child->right, entry->down, child->filter_pattern, child->filter_flags, -1);
3494         calc_widths(&child->right, FALSE);
3495 #ifndef _NO_EXTENSIONS
3496         set_header(&child->right);
3497 #endif
3498
3499         child->header_wdths_ok = FALSE;
3500
3501         SetCursor(old_cursor);
3502 }
3503
3504
3505 /* expand a directory entry */
3506
3507 static BOOL expand_entry(ChildWnd* child, Entry* dir)
3508 {
3509         int idx;
3510         Entry* p;
3511
3512         if (!dir || dir->expanded || !dir->down)
3513                 return FALSE;
3514
3515         p = dir->down;
3516
3517         if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
3518                 p = p->next;
3519
3520                 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
3521                                 p->data.cFileName[2]=='\0' && p->next)
3522                         p = p->next;
3523         }
3524
3525         /* no subdirectories ? */
3526         if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
3527                 return FALSE;
3528
3529         idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3530
3531         dir->expanded = TRUE;
3532
3533         /* insert entries in left pane */
3534         insert_entries(&child->left, p, NULL, TF_ALL, idx);
3535
3536         if (!child->header_wdths_ok) {
3537                 if (calc_widths(&child->left, FALSE)) {
3538 #ifndef _NO_EXTENSIONS
3539                         set_header(&child->left);
3540 #endif
3541
3542                         child->header_wdths_ok = TRUE;
3543                 }
3544         }
3545
3546         return TRUE;
3547 }
3548
3549
3550 static void collapse_entry(Pane* pane, Entry* dir)
3551 {
3552         int idx = SendMessageW(pane->hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3553
3554         ShowWindow(pane->hwnd, SW_HIDE);
3555
3556         /* hide sub entries */
3557         for(;;) {
3558                 LRESULT res = SendMessageW(pane->hwnd, LB_GETITEMDATA, idx+1, 0);
3559                 Entry* sub = (Entry*) res;
3560
3561                 if (res==LB_ERR || !sub || sub->level<=dir->level)
3562                         break;
3563
3564                 SendMessageW(pane->hwnd, LB_DELETESTRING, idx+1, 0);
3565         }
3566
3567         dir->expanded = FALSE;
3568
3569         ShowWindow(pane->hwnd, SW_SHOW);
3570 }
3571
3572
3573 static void refresh_right_pane(ChildWnd* child)
3574 {
3575         SendMessageW(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3576         insert_entries(&child->right, child->right.root, child->filter_pattern, child->filter_flags, -1);
3577         calc_widths(&child->right, FALSE);
3578
3579 #ifndef _NO_EXTENSIONS
3580         set_header(&child->right);
3581 #endif
3582 }
3583
3584 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd)
3585 {
3586         WCHAR path[MAX_PATH];
3587
3588         if (!entry)
3589                 return;
3590
3591         path[0] = '\0';
3592
3593         child->left.cur = entry;
3594
3595         child->right.root = entry->down? entry->down: entry;
3596         child->right.cur = entry;
3597
3598         if (!entry->scanned)
3599                 scan_entry(child, entry, idx, hwnd);
3600         else
3601                 refresh_right_pane(child);
3602
3603         get_path(entry, path);
3604         lstrcpyW(child->path, path);
3605
3606         if (child->hwnd)        /* only change window title, if the window already exists */
3607                 SetWindowTextW(child->hwnd, path);
3608
3609         if (path[0])
3610                 if (SetCurrentDirectoryW(path))
3611                         set_space_status();
3612 }
3613
3614
3615 static void refresh_child(ChildWnd* child)
3616 {
3617         WCHAR path[MAX_PATH], drv[_MAX_DRIVE+1];
3618         Entry* entry;
3619         int idx;
3620
3621         get_path(child->left.cur, path);
3622         _wsplitpath(path, drv, NULL, NULL, NULL);
3623
3624         child->right.root = NULL;
3625
3626         scan_entry(child, &child->root.entry, 0, child->hwnd);
3627
3628 #ifdef _SHELL_FOLDERS
3629
3630         if (child->root.entry.etype == ET_SHELL)
3631         {
3632                 LPITEMIDLIST local_pidl = get_path_pidl(path,child->hwnd);
3633                 if (local_pidl)
3634                         entry = read_tree(&child->root, NULL, local_pidl , drv, child->sortOrder, child->hwnd);
3635                 else
3636                         entry = NULL;
3637         }
3638         else
3639 #endif
3640                 entry = read_tree(&child->root, path, NULL, drv, child->sortOrder, child->hwnd);
3641
3642         if (!entry)
3643                 entry = &child->root.entry;
3644
3645         insert_entries(&child->left, child->root.entry.down, NULL, TF_ALL, 0);
3646
3647         set_curdir(child, entry, 0, child->hwnd);
3648
3649         idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
3650         SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
3651 }
3652
3653
3654 static void create_drive_bar(void)
3655 {
3656         TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0};
3657 #ifndef _NO_EXTENSIONS
3658         WCHAR b1[BUFFER_LEN];
3659 #endif
3660         int btn = 1;
3661         PWSTR p;
3662
3663         GetLogicalDriveStringsW(BUFFER_LEN, Globals.drives);
3664
3665         Globals.hdrivebar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE|CCS_NOMOVEY|TBSTYLE_LIST,
3666                                 IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
3667                                 0, 16, 13, 16, 13, sizeof(TBBUTTON));
3668
3669 #ifndef _NO_EXTENSIONS
3670 #ifdef __WINE__
3671         /* insert unix file system button */
3672         b1[0] = '/';
3673         b1[1] = '\0';
3674         b1[2] = '\0';
3675         SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)b1);
3676
3677         drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
3678         SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3679         drivebarBtn.iString++;
3680 #endif
3681 #ifdef _SHELL_FOLDERS
3682         /* insert shell namespace button */
3683         load_string(b1, sizeof(b1)/sizeof(b1[0]), IDS_SHELL);
3684         b1[lstrlenW(b1)+1] = '\0';
3685         SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)b1);
3686
3687         drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3688         SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3689         drivebarBtn.iString++;
3690 #endif
3691
3692         /* register windows drive root strings */
3693         SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)Globals.drives);
3694 #endif
3695
3696         drivebarBtn.idCommand = ID_DRIVE_FIRST;
3697
3698         for(p=Globals.drives; *p; ) {
3699 #ifdef _NO_EXTENSIONS
3700                 /* insert drive letter */
3701                 WCHAR b[3] = {tolower(*p)};
3702                 SendMessageW(Globals.hdrivebar, TB_ADDSTRINGW, 0, (LPARAM)b);
3703 #endif
3704                 switch(GetDriveTypeW(p)) {
3705                         case DRIVE_REMOVABLE:   drivebarBtn.iBitmap = 1;        break;
3706                         case DRIVE_CDROM:               drivebarBtn.iBitmap = 3;        break;
3707                         case DRIVE_REMOTE:              drivebarBtn.iBitmap = 4;        break;
3708                         case DRIVE_RAMDISK:             drivebarBtn.iBitmap = 5;        break;
3709                         default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
3710                 }
3711
3712                 SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3713                 drivebarBtn.idCommand++;
3714                 drivebarBtn.iString++;
3715
3716                 while(*p++);
3717         }
3718 }
3719
3720 static void refresh_drives(void)
3721 {
3722         RECT rect;
3723
3724         /* destroy drive bar */
3725         DestroyWindow(Globals.hdrivebar);
3726         Globals.hdrivebar = 0;
3727
3728         /* re-create drive bar */
3729         create_drive_bar();
3730
3731         /* update window layout */
3732         GetClientRect(Globals.hMainWnd, &rect);
3733         SendMessageW(Globals.hMainWnd, WM_SIZE, 0, MAKELONG(rect.right, rect.bottom));
3734 }
3735
3736
3737 static BOOL launch_file(HWND hwnd, LPCWSTR cmd, UINT nCmdShow)
3738 {
3739         HINSTANCE hinst = ShellExecuteW(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3740
3741         if (PtrToUlong(hinst) <= 32) {
3742                 display_error(hwnd, GetLastError());
3743                 return FALSE;
3744         }
3745
3746         return TRUE;
3747 }
3748
3749
3750 static BOOL launch_entry(Entry* entry, HWND hwnd, UINT nCmdShow)
3751 {
3752         WCHAR cmd[MAX_PATH];
3753
3754 #ifdef _SHELL_FOLDERS
3755         if (entry->etype == ET_SHELL) {
3756                 BOOL ret = TRUE;
3757
3758                 SHELLEXECUTEINFOW shexinfo;
3759
3760                 shexinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
3761                 shexinfo.fMask = SEE_MASK_IDLIST;
3762                 shexinfo.hwnd = hwnd;
3763                 shexinfo.lpVerb = NULL;
3764                 shexinfo.lpFile = NULL;
3765                 shexinfo.lpParameters = NULL;
3766                 shexinfo.lpDirectory = NULL;
3767                 shexinfo.nShow = nCmdShow;
3768                 shexinfo.lpIDList = get_to_absolute_pidl(entry, hwnd);
3769
3770                 if (!ShellExecuteExW(&shexinfo)) {
3771                         display_error(hwnd, GetLastError());
3772                         ret = FALSE;
3773                 }
3774
3775                 if (shexinfo.lpIDList != entry->pidl)
3776                         IMalloc_Free(Globals.iMalloc, shexinfo.lpIDList);
3777
3778                 return ret;
3779         }
3780 #endif
3781
3782         get_path(entry, cmd);
3783
3784          /* start program, open document... */
3785         return launch_file(hwnd, cmd, nCmdShow);
3786 }
3787
3788
3789 static void activate_entry(ChildWnd* child, Pane* pane, HWND hwnd)
3790 {
3791         Entry* entry = pane->cur;
3792
3793         if (!entry)
3794                 return;
3795
3796         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3797                 int scanned_old = entry->scanned;
3798
3799                 if (!scanned_old)
3800                 {
3801                         int idx = SendMessageW(child->left.hwnd, LB_GETCURSEL, 0, 0);
3802                         scan_entry(child, entry, idx, hwnd);
3803                 }
3804
3805 #ifndef _NO_EXTENSIONS
3806                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3807                         return;
3808 #endif
3809
3810                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
3811                         entry = child->left.cur->up;
3812                         collapse_entry(&child->left, entry);
3813                         goto focus_entry;
3814                 } else if (entry->expanded)
3815                         collapse_entry(pane, child->left.cur);
3816                 else {
3817                         expand_entry(child, child->left.cur);
3818
3819                         if (!pane->treePane) focus_entry: {
3820                                 int idxstart = SendMessageW(child->left.hwnd, LB_GETCURSEL, 0, 0);
3821                                 int idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, idxstart, (LPARAM)entry);
3822                                 SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
3823                                 set_curdir(child, entry, idx, hwnd);
3824                         }
3825                 }
3826
3827                 if (!scanned_old) {
3828                         calc_widths(pane, FALSE);
3829
3830 #ifndef _NO_EXTENSIONS
3831                         set_header(pane);
3832 #endif
3833                 }
3834         } else {
3835                 if (GetKeyState(VK_MENU) < 0)
3836                         show_properties_dlg(entry, child->hwnd);
3837                 else
3838                         launch_entry(entry, child->hwnd, SW_SHOWNORMAL);
3839         }
3840 }
3841
3842
3843 static BOOL pane_command(Pane* pane, UINT cmd)
3844 {
3845         switch(cmd) {
3846                 case ID_VIEW_NAME:
3847                         if (pane->visible_cols) {
3848                                 pane->visible_cols = 0;
3849                                 calc_widths(pane, TRUE);
3850 #ifndef _NO_EXTENSIONS
3851                                 set_header(pane);
3852 #endif
3853                                 InvalidateRect(pane->hwnd, 0, TRUE);
3854                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
3855                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
3856                         }
3857                         break;
3858
3859                 case ID_VIEW_ALL_ATTRIBUTES:
3860                         if (pane->visible_cols != COL_ALL) {
3861                                 pane->visible_cols = COL_ALL;
3862                                 calc_widths(pane, TRUE);
3863 #ifndef _NO_EXTENSIONS
3864                                 set_header(pane);
3865 #endif
3866                                 InvalidateRect(pane->hwnd, 0, TRUE);
3867                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
3868                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
3869                         }
3870                         break;
3871
3872 #ifndef _NO_EXTENSIONS
3873                 case ID_PREFERRED_SIZES: {
3874                         calc_widths(pane, TRUE);
3875                         set_header(pane);
3876                         InvalidateRect(pane->hwnd, 0, TRUE);
3877                         break;}
3878 #endif
3879
3880                         /* TODO: more command ids... */
3881
3882                 default:
3883                         return FALSE;
3884         }
3885
3886         return TRUE;
3887 }
3888
3889
3890 static void set_sort_order(ChildWnd* child, SORT_ORDER sortOrder)
3891 {
3892         if (child->sortOrder != sortOrder) {
3893                 child->sortOrder = sortOrder;
3894                 refresh_child(child);
3895         }
3896 }
3897
3898 static void update_view_menu(ChildWnd* child)
3899 {
3900         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_NAME, child->sortOrder==SORT_NAME? MF_CHECKED: MF_UNCHECKED);
3901         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_TYPE, child->sortOrder==SORT_EXT? MF_CHECKED: MF_UNCHECKED);
3902         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_SIZE, child->sortOrder==SORT_SIZE? MF_CHECKED: MF_UNCHECKED);
3903         CheckMenuItem(Globals.hMenuView, ID_VIEW_SORT_DATE, child->sortOrder==SORT_DATE? MF_CHECKED: MF_UNCHECKED);
3904 }
3905
3906
3907 static BOOL is_directory(LPCWSTR target)
3908 {
3909         /*TODO correctly handle UNIX paths */
3910         DWORD target_attr = GetFileAttributesW(target);
3911
3912         if (target_attr == INVALID_FILE_ATTRIBUTES)
3913                 return FALSE;
3914
3915         return target_attr&FILE_ATTRIBUTE_DIRECTORY? TRUE: FALSE;
3916 }
3917
3918 static BOOL prompt_target(Pane* pane, LPWSTR source, LPWSTR target)
3919 {
3920         WCHAR path[MAX_PATH];
3921         int len;
3922
3923         get_path(pane->cur, path);
3924
3925         if (DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_SELECT_DESTINATION), pane->hwnd, DestinationDlgProc, (LPARAM)path) != IDOK)
3926                 return FALSE;
3927
3928         get_path(pane->cur, source);
3929
3930         /* convert relative targets to absolute paths */
3931         if (path[0]!='/' && path[1]!=':') {
3932                 get_path(pane->cur->up, target);
3933                 len = lstrlenW(target);
3934
3935                 if (target[len-1]!='\\' && target[len-1]!='/')
3936                         target[len++] = '/';
3937
3938                 lstrcpyW(target+len, path);
3939         } else
3940                 lstrcpyW(target, path);
3941
3942         /* If the target already exists as directory, create a new target below this. */
3943         if (is_directory(path)) {
3944                 WCHAR fname[_MAX_FNAME], ext[_MAX_EXT];
3945                 static const WCHAR sAppend[] = {'%','s','/','%','s','%','s','\0'};
3946
3947                 _wsplitpath(source, NULL, NULL, fname, ext);
3948
3949                 wsprintfW(target, sAppend, path, fname, ext);
3950         }
3951
3952         return TRUE;
3953 }
3954
3955
3956 static IContextMenu2* s_pctxmenu2 = NULL;
3957 static IContextMenu3* s_pctxmenu3 = NULL;
3958
3959 static void CtxMenu_reset(void)
3960 {
3961         s_pctxmenu2 = NULL;
3962         s_pctxmenu3 = NULL;
3963 }
3964
3965 static IContextMenu* CtxMenu_query_interfaces(IContextMenu* pcm1)
3966 {
3967         IContextMenu* pcm = NULL;
3968
3969         CtxMenu_reset();
3970
3971         if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu3, (void**)&pcm) == NOERROR)
3972                 s_pctxmenu3 = (LPCONTEXTMENU3)pcm;
3973         else if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu2, (void**)&pcm) == NOERROR)
3974                 s_pctxmenu2 = (LPCONTEXTMENU2)pcm;
3975
3976         if (pcm) {
3977                 IContextMenu_Release(pcm1);
3978                 return pcm;
3979         } else
3980                 return pcm1;
3981 }
3982
3983 static BOOL CtxMenu_HandleMenuMsg(UINT nmsg, WPARAM wparam, LPARAM lparam)
3984 {
3985         if (s_pctxmenu3) {
3986                 if (SUCCEEDED(IContextMenu3_HandleMenuMsg(s_pctxmenu3, nmsg, wparam, lparam)))
3987                         return TRUE;
3988         }
3989
3990         if (s_pctxmenu2)
3991                 if (SUCCEEDED(IContextMenu2_HandleMenuMsg(s_pctxmenu2, nmsg, wparam, lparam)))
3992                         return TRUE;
3993
3994         return FALSE;
3995 }
3996
3997
3998 #ifndef _NO_EXTENSIONS
3999 static HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, LPCITEMIDLIST* apidl, int x, int y)
4000 {
4001         IContextMenu* pcm;
4002         BOOL executed = FALSE;
4003
4004         HRESULT hr = IShellFolder_GetUIObjectOf(shell_folder, hwndParent, cidl, apidl, &IID_IContextMenu, NULL, (LPVOID*)&pcm);
4005
4006         if (SUCCEEDED(hr)) {
4007                 HMENU hmenu = CreatePopupMenu();
4008
4009                 pcm = CtxMenu_query_interfaces(pcm);
4010
4011                 if (hmenu) {
4012                         hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL);
4013
4014                         if (SUCCEEDED(hr)) {
4015                                 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, hwndParent, NULL);
4016
4017                                 CtxMenu_reset();
4018
4019                                 if (idCmd) {
4020                                   CMINVOKECOMMANDINFO cmi;
4021
4022                                   cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
4023                                   cmi.fMask = 0;
4024                                   cmi.hwnd = hwndParent;
4025                                   cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
4026                                   cmi.lpParameters = NULL;
4027                                   cmi.lpDirectory = NULL;
4028                                   cmi.nShow = SW_SHOWNORMAL;
4029                                   cmi.dwHotKey = 0;
4030                                   cmi.hIcon = 0;
4031
4032                                   hr = IContextMenu_InvokeCommand(pcm, &cmi);
4033                                         executed = TRUE;
4034                                 }
4035                         } else
4036                                 CtxMenu_reset();
4037                 }
4038
4039                 IContextMenu_Release(pcm);
4040         }
4041
4042         return FAILED(hr)? hr: executed? S_OK: S_FALSE;
4043 }
4044 #endif /* _NO_EXTENSIONS */
4045
4046
4047 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4048 {
4049         ChildWnd* child = (ChildWnd*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
4050         ASSERT(child);
4051
4052         switch(nmsg) {
4053                 case WM_DRAWITEM: {
4054                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
4055                         Entry* entry = (Entry*) dis->itemData;
4056
4057                         if (dis->CtlID == IDW_TREE_LEFT)
4058                                 draw_item(&child->left, dis, entry, -1);
4059                         else if (dis->CtlID == IDW_TREE_RIGHT)
4060                                 draw_item(&child->right, dis, entry, -1);
4061                         else
4062                                 goto draw_menu_item;
4063
4064                         return TRUE;}
4065
4066                 case WM_CREATE:
4067                         InitChildWindow(child);
4068                         break;
4069
4070                 case WM_NCDESTROY:
4071                         free_child_window(child);
4072                         SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
4073                         break;
4074
4075                 case WM_PAINT: {
4076                         PAINTSTRUCT ps;
4077                         HBRUSH lastBrush;
4078                         RECT rt;
4079                         GetClientRect(hwnd, &rt);
4080                         BeginPaint(hwnd, &ps);
4081                         rt.left = child->split_pos-SPLIT_WIDTH/2;
4082                         rt.right = child->split_pos+SPLIT_WIDTH/2+1;
4083                         lastBrush = SelectObject(ps.hdc, GetStockObject(COLOR_SPLITBAR));
4084                         Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
4085                         SelectObject(ps.hdc, lastBrush);
4086 #ifdef _NO_EXTENSIONS
4087                         rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
4088                         FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
4089 #endif
4090                         EndPaint(hwnd, &ps);
4091                         break;}
4092
4093                 case WM_SETCURSOR:
4094                         if (LOWORD(lparam) == HTCLIENT) {
4095                                 POINT pt;
4096                                 GetCursorPos(&pt);
4097                                 ScreenToClient(hwnd, &pt);
4098
4099                                 if (pt.x>=child->split_pos-SPLIT_WIDTH/2 && pt.x<child->split_pos+SPLIT_WIDTH/2+1) {
4100                                         SetCursor(LoadCursorW(0, (LPCWSTR)IDC_SIZEWE));
4101                                         return TRUE;
4102                                 }
4103                         }
4104                         goto def;
4105
4106                 case WM_LBUTTONDOWN: {
4107                         RECT rt;
4108                         int x = (short)LOWORD(lparam);
4109
4110                         GetClientRect(hwnd, &rt);
4111
4112                         if (x>=child->split_pos-SPLIT_WIDTH/2 && x<child->split_pos+SPLIT_WIDTH/2+1) {
4113                                 last_split = child->split_pos;
4114 #ifdef _NO_EXTENSIONS
4115                                 draw_splitbar(hwnd, last_split);
4116 #endif
4117                                 SetCapture(hwnd);
4118                         }
4119
4120                         break;}
4121
4122                 case WM_LBUTTONUP:
4123                         if (GetCapture() == hwnd) {
4124 #ifdef _NO_EXTENSIONS
4125                                 RECT rt;
4126                                 int x = (short)LOWORD(lparam);
4127                                 draw_splitbar(hwnd, last_split);
4128                                 last_split = -1;
4129                                 GetClientRect(hwnd, &rt);
4130                                 child->split_pos = x;
4131                                 resize_tree(child, rt.right, rt.bottom);
4132 #endif
4133                                 ReleaseCapture();
4134                         }
4135                         break;
4136
4137 #ifdef _NO_EXTENSIONS
4138                 case WM_CAPTURECHANGED:
4139                         if (GetCapture()==hwnd && last_split>=0)
4140                                 draw_splitbar(hwnd, last_split);
4141                         break;
4142 #endif
4143
4144                 case WM_KEYDOWN:
4145                         if (wparam == VK_ESCAPE)
4146                                 if (GetCapture() == hwnd) {
4147                                         RECT rt;
4148 #ifdef _NO_EXTENSIONS
4149                                         draw_splitbar(hwnd, last_split);
4150 #else
4151                                         child->split_pos = last_split;
4152 #endif
4153                                         GetClientRect(hwnd, &rt);
4154                                         resize_tree(child, rt.right, rt.bottom);
4155                                         last_split = -1;
4156                                         ReleaseCapture();
4157                                         SetCursor(LoadCursorW(0, (LPCWSTR)IDC_ARROW));
4158                                 }
4159                         break;
4160
4161                 case WM_MOUSEMOVE:
4162                         if (GetCapture() == hwnd) {
4163                                 RECT rt;
4164                                 int x = (short)LOWORD(lparam);
4165
4166 #ifdef _NO_EXTENSIONS
4167                                 HDC hdc = GetDC(hwnd);
4168                                 GetClientRect(hwnd, &rt);
4169
4170                                 rt.left = last_split-SPLIT_WIDTH/2;
4171                                 rt.right = last_split+SPLIT_WIDTH/2+1;
4172                                 InvertRect(hdc, &rt);
4173
4174                                 last_split = x;
4175                                 rt.left = x-SPLIT_WIDTH/2;
4176                                 rt.right = x+SPLIT_WIDTH/2+1;
4177                                 InvertRect(hdc, &rt);
4178
4179                                 ReleaseDC(hwnd, hdc);
4180 #else
4181                                 GetClientRect(hwnd, &rt);
4182
4183                                 if (x>=0 && x<rt.right) {
4184                                         child->split_pos = x;
4185                                         resize_tree(child, rt.right, rt.bottom);
4186                                         rt.left = x-SPLIT_WIDTH/2;
4187                                         rt.right = x+SPLIT_WIDTH/2+1;
4188                                         InvalidateRect(hwnd, &rt, FALSE);
4189                                         UpdateWindow(child->left.hwnd);
4190                                         UpdateWindow(hwnd);
4191                                         UpdateWindow(child->right.hwnd);
4192                                 }
4193 #endif
4194                         }
4195                         break;
4196
4197 #ifndef _NO_EXTENSIONS
4198                 case WM_GETMINMAXINFO:
4199                         DefMDIChildProcW(hwnd, nmsg, wparam, lparam);
4200
4201                         {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
4202
4203                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
4204                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
4205                         break;}
4206 #endif /* _NO_EXTENSIONS */
4207
4208                 case WM_SETFOCUS:
4209                         if (SetCurrentDirectoryW(child->path))
4210                                 set_space_status();
4211                         SetFocus(child->focus_pane? child->right.hwnd: child->left.hwnd);
4212                         break;
4213
4214                 case WM_DISPATCH_COMMAND: {
4215                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4216
4217                         switch(LOWORD(wparam)) {
4218                                 case ID_WINDOW_NEW: {
4219                                         ChildWnd* new_child = alloc_child_window(child->path, NULL, hwnd);
4220
4221                                         if (!create_child_window(new_child))
4222                                                 HeapFree(GetProcessHeap(), 0, new_child);
4223
4224                                         break;}
4225
4226                                 case ID_REFRESH:
4227                                         refresh_drives();
4228                                         refresh_child(child);
4229                                         break;
4230
4231                                 case ID_ACTIVATE:
4232                                         activate_entry(child, pane, hwnd);
4233                                         break;
4234
4235                                 case ID_FILE_MOVE: {
4236                                         WCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4237
4238                                         if (prompt_target(pane, source, target)) {
4239                                                 SHFILEOPSTRUCTW shfo = {hwnd, FO_MOVE, source, target};
4240
4241                                                 source[lstrlenW(source)+1] = '\0';
4242                                                 target[lstrlenW(target)+1] = '\0';
4243
4244                                                 if (!SHFileOperationW(&shfo))
4245                                                         refresh_child(child);
4246                                         }
4247                                         break;}
4248
4249                                 case ID_FILE_COPY: {
4250                                         WCHAR source[BUFFER_LEN], target[BUFFER_LEN];
4251
4252                                         if (prompt_target(pane, source, target)) {
4253                                                 SHFILEOPSTRUCTW shfo = {hwnd, FO_COPY, source, target};
4254
4255                                                 source[lstrlenW(source)+1] = '\0';
4256                                                 target[lstrlenW(target)+1] = '\0';
4257
4258                                                 if (!SHFileOperationW(&shfo))
4259                                                         refresh_child(child);
4260                                         }
4261                                         break;}
4262
4263                                 case ID_FILE_DELETE: {
4264                                         WCHAR path[BUFFER_LEN];
4265                                         SHFILEOPSTRUCTW shfo = {hwnd, FO_DELETE, path, NULL, FOF_ALLOWUNDO};
4266
4267                                         get_path(pane->cur, path);
4268
4269                                         path[lstrlenW(path)+1] = '\0';
4270
4271                                         if (!SHFileOperationW(&shfo))
4272                                                 refresh_child(child);
4273                                         break;}
4274
4275                                 case ID_VIEW_SORT_NAME:
4276                                         set_sort_order(child, SORT_NAME);
4277                                         break;
4278
4279                                 case ID_VIEW_SORT_TYPE:
4280                                         set_sort_order(child, SORT_EXT);
4281                                         break;
4282
4283                                 case ID_VIEW_SORT_SIZE:
4284                                         set_sort_order(child, SORT_SIZE);
4285                                         break;
4286
4287                                 case ID_VIEW_SORT_DATE:
4288                                         set_sort_order(child, SORT_DATE);
4289                                         break;
4290
4291                                 case ID_VIEW_FILTER: {
4292                                         struct FilterDialog dlg;
4293
4294                                         memset(&dlg, 0, sizeof(struct FilterDialog));
4295                                         lstrcpyW(dlg.pattern, child->filter_pattern);
4296                                         dlg.flags = child->filter_flags;
4297
4298                                         if (DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_DIALOG_VIEW_TYPE), hwnd, FilterDialogDlgProc, (LPARAM)&dlg) == IDOK) {
4299                                                 lstrcpyW(child->filter_pattern, dlg.pattern);
4300                                                 child->filter_flags = dlg.flags;
4301                                                 refresh_right_pane(child);
4302                                         }
4303                                         break;}
4304
4305                                 case ID_VIEW_SPLIT: {
4306                                         last_split = child->split_pos;
4307 #ifdef _NO_EXTENSIONS
4308                                         draw_splitbar(hwnd, last_split);
4309 #endif
4310                                         SetCapture(hwnd);
4311                                         break;}
4312
4313                                 case ID_EDIT_PROPERTIES:
4314                                         show_properties_dlg(pane->cur, child->hwnd);
4315                                         break;
4316
4317                                 default:
4318                                         return pane_command(pane, LOWORD(wparam));
4319                         }
4320
4321                         return TRUE;}
4322
4323                 case WM_COMMAND: {
4324                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4325
4326                         switch(HIWORD(wparam)) {
4327                                 case LBN_SELCHANGE: {
4328                                         int idx = SendMessageW(pane->hwnd, LB_GETCURSEL, 0, 0);
4329                                         Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, idx, 0);
4330
4331                                         if (pane == &child->left)
4332                                                 set_curdir(child, entry, idx, hwnd);
4333                                         else
4334                                                 pane->cur = entry;
4335                                         break;}
4336
4337                                 case LBN_DBLCLK:
4338                                         activate_entry(child, pane, hwnd);
4339                                         break;
4340                         }
4341                         break;}
4342
4343 #ifndef _NO_EXTENSIONS
4344                 case WM_NOTIFY: {
4345                         NMHDR* pnmh = (NMHDR*) lparam;
4346                         return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &child->left: &child->right, pnmh);}
4347 #endif
4348
4349 #ifdef _SHELL_FOLDERS
4350                 case WM_CONTEXTMENU: {
4351                         POINT pt, pt_clnt;
4352                         Pane* pane;
4353                         int idx;
4354
4355                          /* first select the current item in the listbox */
4356                         HWND hpanel = (HWND) wparam;
4357                         pt_clnt.x = pt.x = (short)LOWORD(lparam);
4358                         pt_clnt.y = pt.y = (short)HIWORD(lparam);
4359                         ScreenToClient(hpanel, &pt_clnt);
4360                         SendMessageW(hpanel, WM_LBUTTONDOWN, 0, MAKELONG(pt_clnt.x, pt_clnt.y));
4361                         SendMessageW(hpanel, WM_LBUTTONUP, 0, MAKELONG(pt_clnt.x, pt_clnt.y));
4362
4363                          /* now create the popup menu using shell namespace and IContextMenu */
4364                         pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
4365                         idx = SendMessageW(pane->hwnd, LB_GETCURSEL, 0, 0);
4366
4367                         if (idx != -1) {
4368                                 Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, idx, 0);
4369
4370                                 LPITEMIDLIST pidl_abs = get_to_absolute_pidl(entry, hwnd);
4371
4372                                 if (pidl_abs) {
4373                                         IShellFolder* parentFolder;
4374                                         LPCITEMIDLIST pidlLast;
4375
4376                                          /* get and use the parent folder to display correct context menu in all cases */
4377                                         if (SUCCEEDED(SHBindToParent(pidl_abs, &IID_IShellFolder, (LPVOID*)&parentFolder, &pidlLast))) {
4378                                                 if (ShellFolderContextMenu(parentFolder, hwnd, 1, &pidlLast, pt.x, pt.y) == S_OK)
4379                                                         refresh_child(child);
4380
4381                                                 IShellFolder_Release(parentFolder);
4382                                         }
4383
4384                                         IMalloc_Free(Globals.iMalloc, pidl_abs);
4385                                 }
4386                         }
4387                         break;}
4388 #endif
4389
4390                   case WM_MEASUREITEM:
4391                   draw_menu_item:
4392                         if (!wparam)    /* Is the message menu-related? */
4393                                 if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4394                                         return TRUE;
4395
4396                         break;
4397
4398                   case WM_INITMENUPOPUP:
4399                         if (CtxMenu_HandleMenuMsg(nmsg, wparam, lparam))
4400                                 return 0;
4401
4402                         update_view_menu(child);
4403                         break;
4404
4405                   case WM_MENUCHAR:     /* only supported by IContextMenu3 */
4406                    if (s_pctxmenu3) {
4407                            LRESULT lResult = 0;
4408
4409                            IContextMenu3_HandleMenuMsg2(s_pctxmenu3, nmsg, wparam, lparam, &lResult);
4410
4411                            return lResult;
4412                    }
4413
4414                    break;
4415
4416                 case WM_SIZE:
4417                         if (wparam != SIZE_MINIMIZED)
4418                                 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
4419                         /* fall through */
4420
4421                 default: def:
4422                         return DefMDIChildProcW(hwnd, nmsg, wparam, lparam);
4423         }
4424
4425         return 0;
4426 }
4427
4428
4429 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
4430 {
4431         ChildWnd* child = (ChildWnd*)GetWindowLongPtrW(GetParent(hwnd), GWLP_USERDATA);
4432         Pane* pane = (Pane*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
4433         ASSERT(child);
4434
4435         switch(nmsg) {
4436 #ifndef _NO_EXTENSIONS
4437                 case WM_HSCROLL:
4438                         set_header(pane);
4439                         break;
4440 #endif
4441
4442                 case WM_SETFOCUS:
4443                         child->focus_pane = pane==&child->right? 1: 0;
4444                         SendMessageW(hwnd, LB_SETSEL, TRUE, 1);
4445                         /*TODO: check menu items */
4446                         break;
4447
4448                 case WM_KEYDOWN:
4449                         if (wparam == VK_TAB) {
4450                                 /*TODO: SetFocus(Globals.hdrivebar) */
4451                                 SetFocus(child->focus_pane? child->left.hwnd: child->right.hwnd);
4452                         }
4453         }
4454
4455         return CallWindowProcW(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
4456 }
4457
4458
4459 static void InitInstance(HINSTANCE hinstance)
4460 {
4461         static const WCHAR sFont[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
4462
4463         WNDCLASSEXW wcFrame;
4464         WNDCLASSW wcChild;
4465         int col;
4466
4467         INITCOMMONCONTROLSEX icc = {
4468                 sizeof(INITCOMMONCONTROLSEX),
4469                 ICC_BAR_CLASSES
4470         };
4471
4472         HDC hdc = GetDC(0);
4473
4474         setlocale(LC_COLLATE, "");      /* set collating rules to local settings for compareName */
4475
4476         InitCommonControlsEx(&icc);
4477
4478
4479         /* register frame window class */
4480
4481         wcFrame.cbSize        = sizeof(WNDCLASSEXW);
4482         wcFrame.style         = 0;
4483         wcFrame.lpfnWndProc   = FrameWndProc;
4484         wcFrame.cbClsExtra    = 0;
4485         wcFrame.cbWndExtra    = 0;
4486         wcFrame.hInstance     = hinstance;
4487         wcFrame.hIcon         = LoadIconW(hinstance, MAKEINTRESOURCEW(IDI_WINEFILE));
4488         wcFrame.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
4489         wcFrame.hbrBackground = 0;
4490         wcFrame.lpszMenuName  = 0;
4491         wcFrame.lpszClassName = sWINEFILEFRAME;
4492         wcFrame.hIconSm       = LoadImageW(hinstance, MAKEINTRESOURCEW(IDI_WINEFILE), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
4493
4494         Globals.hframeClass = RegisterClassExW(&wcFrame);
4495
4496
4497         /* register tree windows class */
4498
4499         wcChild.style         = CS_CLASSDC|CS_DBLCLKS|CS_VREDRAW;
4500         wcChild.lpfnWndProc   = ChildWndProc;
4501         wcChild.cbClsExtra    = 0;
4502         wcChild.cbWndExtra    = 0;
4503         wcChild.hInstance     = hinstance;
4504         wcChild.hIcon         = 0;
4505         wcChild.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
4506         wcChild.hbrBackground = 0;
4507         wcChild.lpszMenuName  = 0;
4508         wcChild.lpszClassName = sWINEFILETREE;
4509
4510         RegisterClassW(&wcChild);
4511
4512
4513         Globals.haccel = LoadAcceleratorsW(hinstance, MAKEINTRESOURCEW(IDA_WINEFILE));
4514
4515         Globals.hfont = CreateFontW(-MulDiv(8,GetDeviceCaps(hdc,LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sFont);
4516
4517         ReleaseDC(0, hdc);
4518
4519         Globals.hInstance = hinstance;
4520
4521 #ifdef _SHELL_FOLDERS
4522         CoInitialize(NULL);
4523         CoGetMalloc(MEMCTX_TASK, &Globals.iMalloc);
4524         SHGetDesktopFolder(&Globals.iDesktop);
4525         Globals.cfStrFName = RegisterClipboardFormatW(CFSTR_FILENAMEW);
4526 #endif
4527
4528         /* load column strings */
4529         col = 1;
4530
4531         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_NAME);
4532         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_SIZE);
4533         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_CDATE);
4534 #ifndef _NO_EXTENSIONS
4535         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_ADATE);
4536         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_MDATE);
4537         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_IDX);
4538         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_LINKS);
4539 #endif
4540         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_ATTR);
4541 #ifndef _NO_EXTENSIONS
4542         load_string(g_pos_names[col++], sizeof(g_pos_names[col])/sizeof(g_pos_names[col][0]), IDS_COL_SEC);
4543 #endif
4544 }
4545
4546
4547 static BOOL show_frame(HWND hwndParent, int cmdshow, LPCWSTR path)
4548 {
4549         static const WCHAR sMDICLIENT[] = {'M','D','I','C','L','I','E','N','T','\0'};
4550
4551         WCHAR buffer[MAX_PATH], b1[BUFFER_LEN];
4552         ChildWnd* child;
4553         HMENU hMenuFrame, hMenuWindow;
4554         windowOptions opts;
4555
4556         CLIENTCREATESTRUCT ccs;
4557
4558         if (Globals.hMainWnd)
4559                 return TRUE;
4560
4561         opts = load_registry_settings();
4562         hMenuFrame = LoadMenuW(Globals.hInstance, MAKEINTRESOURCEW(IDM_WINEFILE));
4563         hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
4564
4565         Globals.hMenuFrame = hMenuFrame;
4566         Globals.hMenuView = GetSubMenu(hMenuFrame, 2);
4567         Globals.hMenuOptions = GetSubMenu(hMenuFrame, 3);
4568
4569         ccs.hWindowMenu  = hMenuWindow;
4570         ccs.idFirstChild = IDW_FIRST_CHILD;
4571
4572
4573         /* create main window */
4574         Globals.hMainWnd = CreateWindowExW(0, MAKEINTRESOURCEW(Globals.hframeClass), RS(b1,IDS_WINEFILE), WS_OVERLAPPEDWINDOW,
4575                                         opts.start_x, opts.start_y, opts.width, opts.height,
4576                                         hwndParent, Globals.hMenuFrame, Globals.hInstance, 0/*lpParam*/);
4577
4578
4579         Globals.hmdiclient = CreateWindowExW(0, sMDICLIENT, NULL,
4580                                         WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|WS_BORDER,
4581                                         0, 0, 0, 0,
4582                                         Globals.hMainWnd, 0, Globals.hInstance, &ccs);
4583   
4584         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
4585         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_SAVESETTINGS, MF_BYCOMMAND);
4586
4587         create_drive_bar();
4588
4589         {
4590                 TBBUTTON toolbarBtns[] = {
4591                         {0, 0, 0, BTNS_SEP, {0, 0}, 0, 0},
4592                         {0, ID_WINDOW_NEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4593                         {1, ID_WINDOW_CASCADE, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4594                         {2, ID_WINDOW_TILE_HORZ, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4595                         {3, ID_WINDOW_TILE_VERT, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
4596                 };
4597
4598                 Globals.htoolbar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE,
4599                         IDW_TOOLBAR, 2, Globals.hInstance, IDB_TOOLBAR, toolbarBtns,
4600                         sizeof(toolbarBtns)/sizeof(TBBUTTON), 16, 15, 16, 15, sizeof(TBBUTTON));
4601                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_TOOL_BAR, MF_BYCOMMAND|MF_CHECKED);
4602         }
4603
4604         Globals.hstatusbar = CreateStatusWindowW(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
4605         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
4606
4607         /*TODO: read paths from registry */
4608
4609         if (!path || !*path) {
4610                 GetCurrentDirectoryW(MAX_PATH, buffer);
4611                 path = buffer;
4612         }
4613
4614         ShowWindow(Globals.hMainWnd, cmdshow);
4615
4616 #if defined(_SHELL_FOLDERS) && !defined(__WINE__)
4617          /* Shell Namespace as default: */
4618         child = alloc_child_window(path, get_path_pidl(path,Globals.hMainWnd), Globals.hMainWnd);
4619 #else
4620         child = alloc_child_window(path, NULL, Globals.hMainWnd);
4621 #endif
4622
4623         child->pos.showCmd = SW_SHOWMAXIMIZED;
4624         child->pos.rcNormalPosition.left = 0;
4625         child->pos.rcNormalPosition.top = 0;
4626         child->pos.rcNormalPosition.right = 320;
4627         child->pos.rcNormalPosition.bottom = 280;
4628
4629         if (!create_child_window(child)) {
4630                 HeapFree(GetProcessHeap(), 0, child);
4631                 return FALSE;
4632         }
4633
4634         SetWindowPlacement(child->hwnd, &child->pos);
4635
4636         Globals.himl = ImageList_LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDB_IMAGES), 16, 0, RGB(0,255,0), IMAGE_BITMAP, 0);
4637
4638         Globals.prescan_node = FALSE;
4639
4640         UpdateWindow(Globals.hMainWnd);
4641
4642         if (child->hwnd && path && path[0])
4643         {
4644                 int index,count;
4645                 WCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
4646                 WCHAR fullname[_MAX_FNAME+_MAX_EXT+1];
4647
4648                 memset(name,0,sizeof(name));
4649                 memset(name,0,sizeof(ext));
4650                 _wsplitpath(path, drv, dir, name, ext);
4651                 if (name[0])
4652                 {
4653                         count = SendMessageW(child->right.hwnd, LB_GETCOUNT, 0, 0);
4654                         lstrcpyW(fullname,name);
4655                         lstrcatW(fullname,ext);
4656
4657                         for (index = 0; index < count; index ++)
4658                         {
4659                                 Entry* entry = (Entry*)SendMessageW(child->right.hwnd, LB_GETITEMDATA, index, 0);
4660                                 if (lstrcmpW(entry->data.cFileName,fullname)==0 ||
4661                                                 lstrcmpW(entry->data.cAlternateFileName,fullname)==0)
4662                                 {
4663                                         SendMessageW(child->right.hwnd, LB_SETCURSEL, index, 0);
4664                                         SetFocus(child->right.hwnd);
4665                                         break;
4666                                 }
4667                         }
4668                 }
4669         }
4670         return TRUE;
4671 }
4672
4673 static void ExitInstance(void)
4674 {
4675 #ifdef _SHELL_FOLDERS
4676         IShellFolder_Release(Globals.iDesktop);
4677         IMalloc_Release(Globals.iMalloc);
4678         CoUninitialize();
4679 #endif
4680
4681         DeleteObject(Globals.hfont);
4682         ImageList_Destroy(Globals.himl);
4683 }
4684
4685 #ifdef _NO_EXTENSIONS
4686
4687 /* search for already running win[e]files */
4688
4689 static int g_foundPrevInstance = 0;
4690
4691 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
4692 {
4693         WCHAR cls[128];
4694
4695         GetClassNameW(hwnd, cls, 128);
4696
4697         if (!lstrcmpW(cls, (LPCWSTR)lparam)) {
4698                 g_foundPrevInstance++;
4699                 return FALSE;
4700         }
4701
4702         return TRUE;
4703 }
4704
4705 /* search for window of given class name to allow only one running instance */
4706 static int find_window_class(LPCWSTR classname)
4707 {
4708         EnumWindows(EnumWndProc, (LPARAM)classname);
4709
4710         if (g_foundPrevInstance)
4711                 return 1;
4712
4713         return 0;
4714 }
4715
4716 #endif
4717
4718 static int winefile_main(HINSTANCE hinstance, int cmdshow, LPCWSTR path)
4719 {
4720         MSG msg;
4721
4722         InitInstance(hinstance);
4723
4724         if( !show_frame(0, cmdshow, path) )
4725         {
4726                 ExitInstance();
4727                 return 1;
4728         }
4729
4730         while(GetMessageW(&msg, 0, 0, 0)) {
4731                 if (Globals.hmdiclient && TranslateMDISysAccel(Globals.hmdiclient, &msg))
4732                         continue;
4733
4734                 if (Globals.hMainWnd && TranslateAcceleratorW(Globals.hMainWnd, Globals.haccel, &msg))
4735                         continue;
4736
4737                 TranslateMessage(&msg);
4738                 DispatchMessageW(&msg);
4739         }
4740
4741         ExitInstance();
4742
4743         return msg.wParam;
4744 }
4745
4746
4747 #if defined(_MSC_VER)
4748 int APIENTRY wWinMain(HINSTANCE hinstance, HINSTANCE previnstance, LPWSTR cmdline, int cmdshow)
4749 #else
4750 int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE previnstance, LPSTR cmdline, int cmdshow)
4751 #endif
4752 {
4753 #ifdef _NO_EXTENSIONS
4754         if (find_window_class(sWINEFILEFRAME))
4755                 return 1;
4756 #endif
4757
4758         { /* convert ANSI cmdline into WCS path string */
4759         WCHAR buffer[MAX_PATH];
4760         MultiByteToWideChar(CP_ACP, 0, cmdline, -1, buffer, MAX_PATH);
4761         winefile_main(hinstance, cmdshow, buffer);
4762         }
4763
4764         return 0;
4765 }