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