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