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