Added Italian resources.
[wine] / programs / winefile / winefile.c
1 /*
2  * Winefile
3  *
4  * Copyright 2000 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 <locale.h>
27
28 #define NONAMELESSUNION
29 #include "winefile.h"
30 #include "resource.h"
31
32 /* for read_directory_unix() */
33 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include <time.h>
40 #endif
41
42 #ifdef _NO_EXTENSIONS
43 #undef _LEFT_FILES
44 #endif
45
46 #ifndef _MAX_PATH
47 #define _MAX_DRIVE          3
48 #define _MAX_FNAME          256
49 #define _MAX_DIR            _MAX_FNAME
50 #define _MAX_EXT            _MAX_FNAME
51 #define _MAX_PATH           260
52 #endif
53
54 #ifdef NONAMELESSUNION
55 #define UNION_MEMBER(x) DUMMYUNIONNAME.x
56 #else
57 #define UNION_MEMBER(x) x
58 #endif
59
60
61 #ifdef _SHELL_FOLDERS
62 #define DEFAULT_SPLIT_POS       300;
63 #else
64 #define DEFAULT_SPLIT_POS       200;
65 #endif
66
67
68 WINEFILE_GLOBALS Globals;
69
70 extern void WineLicense(HWND hwnd);
71 extern void WineWarranty(HWND hwnd);
72
73 enum ENTRY_TYPE {
74         ET_WINDOWS,
75         ET_UNIX,
76 #ifdef _SHELL_FOLDERS
77         ET_SHELL
78 #endif
79 };
80
81 typedef struct _Entry {
82         struct _Entry*  next;
83         struct _Entry*  down;
84         struct _Entry*  up;
85
86         BOOL                    expanded;
87         BOOL                    scanned;
88         int                             level;
89
90         WIN32_FIND_DATA data;
91
92 #ifndef _NO_EXTENSIONS
93         BY_HANDLE_FILE_INFORMATION bhfi;
94         BOOL                    bhfi_valid;
95         enum ENTRY_TYPE etype;
96 #endif
97 #ifdef _SHELL_FOLDERS
98         LPITEMIDLIST    pidl;
99         IShellFolder*   folder;
100         HICON                   hicon;
101 #endif
102 } Entry;
103
104 typedef struct {
105         Entry   entry;
106         TCHAR   path[MAX_PATH];
107         TCHAR   volname[_MAX_FNAME];
108         TCHAR   fs[_MAX_DIR];
109         DWORD   drive_type;
110         DWORD   fs_flags;
111 } Root;
112
113 enum COLUMN_FLAGS {
114         COL_SIZE                = 0x01,
115         COL_DATE                = 0x02,
116         COL_TIME                = 0x04,
117         COL_ATTRIBUTES  = 0x08,
118         COL_DOSNAMES    = 0x10,
119 #ifdef _NO_EXTENSIONS
120         COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES
121 #else
122         COL_INDEX               = 0x20,
123         COL_LINKS               = 0x40,
124         COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES|COL_INDEX|COL_LINKS
125 #endif
126 };
127
128 typedef enum {
129         SORT_NAME,
130         SORT_EXT,
131         SORT_SIZE,
132         SORT_DATE
133 } SORT_ORDER;
134
135 typedef struct {
136         HWND    hwnd;
137 #ifndef _NO_EXTENSIONS
138         HWND    hwndHeader;
139 #endif
140
141 #ifndef _NO_EXTENSIONS
142 #define COLUMNS 10
143 #else
144 #define COLUMNS 5
145 #endif
146         int             widths[COLUMNS];
147         int             positions[COLUMNS+1];
148
149         BOOL    treePane;
150         int             visible_cols;
151         Entry*  root;
152         Entry*  cur;
153 } Pane;
154
155 typedef struct {
156         HWND    hwnd;
157         Pane    left;
158         Pane    right;
159         int             focus_pane;             /* 0: left  1: right */
160         WINDOWPLACEMENT pos;
161         int             split_pos;
162         BOOL    header_wdths_ok;
163
164         TCHAR   path[MAX_PATH];
165         Root    root;
166
167         SORT_ORDER sortOrder;
168 } ChildWnd;
169
170
171 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd);
172 static void set_curdir(ChildWnd* child, Entry* entry, HWND hwnd);
173
174 LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
175 LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
176 LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
177
178
179 static void display_error(HWND hwnd, DWORD error)
180 {
181         PTSTR msg;
182
183         if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
184                 0, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (PTSTR)&msg, 0, NULL))
185                 MessageBox(hwnd, msg, TEXT("Winefile"), MB_OK);
186         else
187                 MessageBox(hwnd, TEXT("Error"), TEXT("Winefile"), MB_OK);
188
189         LocalFree(msg);
190 }
191
192
193 /* allocate and initialise a directory entry */
194 static Entry* alloc_entry()
195 {
196         Entry* entry = (Entry*) malloc(sizeof(Entry));
197
198 #ifdef _SHELL_FOLDERS
199         entry->pidl = NULL;
200         entry->folder = NULL;
201         entry->hicon = 0;
202 #endif
203
204         return entry;
205 }
206
207 /* free a directory entry */
208 static void free_entry(Entry* entry)
209 {
210 #ifdef _SHELL_FOLDERS
211         if (entry->hicon && entry->hicon!=(HICON)-1)
212                 DestroyIcon(entry->hicon);
213
214         if (entry->folder && entry->folder!=Globals.iDesktop)
215                 (*entry->folder->lpVtbl->Release)(entry->folder);
216
217         if (entry->pidl)
218                 (*Globals.iMalloc->lpVtbl->Free)(Globals.iMalloc, entry->pidl);
219 #endif
220
221         free(entry);
222 }
223
224 /* recursively free all child entries */
225 static void free_entries(Entry* dir)
226 {
227         Entry *entry, *next=dir->down;
228
229         if (next) {
230                 dir->down = 0;
231
232                 do {
233                         entry = next;
234                         next = entry->next;
235
236                         free_entries(entry);
237                         free_entry(entry);
238                 } while(next);
239         }
240 }
241
242
243 static void read_directory_win(Entry* dir, LPCTSTR path)
244 {
245         Entry* first_entry = NULL;
246         Entry* last = NULL;
247         Entry* entry;
248
249         int level = dir->level + 1;
250         WIN32_FIND_DATA w32fd;
251         HANDLE hFind;
252 #ifndef _NO_EXTENSIONS
253         HANDLE hFile;
254 #endif
255
256         TCHAR buffer[MAX_PATH], *p;
257         for(p=buffer; *path; )
258                 *p++ = *path++;
259
260         lstrcpy(p, TEXT("\\*"));
261
262         hFind = FindFirstFile(buffer, &w32fd);
263
264         if (hFind != INVALID_HANDLE_VALUE) {
265                 do {
266                         entry = alloc_entry();
267
268                         if (!first_entry)
269                                 first_entry = entry;
270
271                         if (last)
272                                 last->next = entry;
273
274                         memcpy(&entry->data, &w32fd, sizeof(WIN32_FIND_DATA));
275                         entry->down = NULL;
276                         entry->up = dir;
277                         entry->expanded = FALSE;
278                         entry->scanned = FALSE;
279                         entry->level = level;
280
281 #ifdef _NO_EXTENSIONS
282                         /* hide directory entry "." */
283                         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
284                                 LPCTSTR name = entry->data.cFileName;
285
286                                 if (name[0]=='.' && name[1]=='\0')
287                                         continue;
288                         }
289 #else
290                         entry->etype = ET_WINDOWS;
291                         entry->bhfi_valid = FALSE;
292
293                         lstrcpy(p+1, entry->data.cFileName);
294
295                         hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
296                                                                 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
297
298                         if (hFile != INVALID_HANDLE_VALUE) {
299                                 if (GetFileInformationByHandle(hFile, &entry->bhfi))
300                                         entry->bhfi_valid = TRUE;
301
302                                 CloseHandle(hFile);
303                         }
304 #endif
305
306                         last = entry;
307                 } while(FindNextFile(hFind, &entry->data));
308
309                 last->next = NULL;
310
311                 FindClose(hFind);
312         }
313
314         dir->down = first_entry;
315         dir->scanned = TRUE;
316 }
317
318
319 static Entry* find_entry_win(Entry* dir, LPCTSTR name)
320 {
321         Entry* entry;
322
323         for(entry=dir->down; entry; entry=entry->next) {
324                 LPCTSTR p = name;
325                 LPCTSTR q = entry->data.cFileName;
326
327                 do {
328                         if (!*p || *p==TEXT('\\') || *p==TEXT('/'))
329                                 return entry;
330                 } while(tolower(*p++) == tolower(*q++));
331
332                 p = name;
333                 q = entry->data.cAlternateFileName;
334
335                 do {
336                         if (!*p || *p==TEXT('\\') || *p==TEXT('/'))
337                                 return entry;
338                 } while(tolower(*p++) == tolower(*q++));
339         }
340
341         return 0;
342 }
343
344
345 static Entry* read_tree_win(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
346 {
347         TCHAR buffer[MAX_PATH];
348         Entry* entry = &root->entry;
349         LPCTSTR s = path;
350         PTSTR d = buffer;
351
352         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
353
354 #ifndef _NO_EXTENSIONS
355         entry->etype = ET_WINDOWS;
356 #endif
357
358         while(entry) {
359                 while(*s && *s!=TEXT('\\') && *s!=TEXT('/'))
360                         *d++ = *s++;
361
362                 while(*s==TEXT('\\') || *s==TEXT('/'))
363                         s++;
364
365                 *d++ = TEXT('\\');
366                 *d = TEXT('\0');
367
368                 read_directory(entry, buffer, sortOrder, hwnd);
369
370                 if (entry->down)
371                         entry->expanded = TRUE;
372
373                 if (!*s)
374                         break;
375
376                 entry = find_entry_win(entry, s);
377         }
378
379         SetCursor(old_cursor);
380
381         return entry;
382 }
383
384
385 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
386
387 BOOL time_to_filetime(const time_t* t, FILETIME* ftime)
388 {
389         struct tm* tm = gmtime(t);
390         SYSTEMTIME stime;
391
392         if (!tm)
393                 return FALSE;
394
395         stime.wYear = tm->tm_year+1900;
396         stime.wMonth = tm->tm_mon+1;
397         /*      stime.wDayOfWeek */
398         stime.wDay = tm->tm_mday;
399         stime.wHour = tm->tm_hour;
400         stime.wMinute = tm->tm_min;
401         stime.wSecond = tm->tm_sec;
402
403         return SystemTimeToFileTime(&stime, ftime);
404 }
405
406 static void read_directory_unix(Entry* dir, LPCTSTR path)
407 {
408         Entry* first_entry = NULL;
409         Entry* last = NULL;
410         Entry* entry;
411
412         int level = dir->level + 1;
413
414         DIR* pdir = opendir(path);
415
416         if (pdir) {
417                 struct stat st;
418                 struct dirent* ent;
419                 TCHAR buffer[MAX_PATH], *p;
420
421                 for(p=buffer; *path; )
422                         *p++ = *path++;
423
424                 if (p==buffer || p[-1]!='/')
425                         *p++ = '/';
426
427                 while((ent=readdir(pdir))) {
428                         entry = alloc_entry();
429
430                         if (!first_entry)
431                                 first_entry = entry;
432
433                         if (last)
434                                 last->next = entry;
435
436                         entry->etype = ET_UNIX;
437
438                         lstrcpy(entry->data.cFileName, ent->d_name);
439                         entry->data.dwFileAttributes = ent->d_name[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
440
441                         strcpy(p, ent->d_name);
442
443                         if (!stat(buffer, &st)) {
444                                 if (S_ISDIR(st.st_mode))
445                                         entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
446
447                                 entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
448                                 entry->data.nFileSizeHigh = st.st_size >> 32;
449
450                                 memset(&entry->data.ftCreationTime, 0, sizeof(FILETIME));
451                                 time_to_filetime(&st.st_atime, &entry->data.ftLastAccessTime);
452                                 time_to_filetime(&st.st_mtime, &entry->data.ftLastWriteTime);
453
454                                 entry->bhfi.nFileIndexLow = ent->d_ino;
455                                 entry->bhfi.nFileIndexHigh = 0;
456
457                                 entry->bhfi.nNumberOfLinks = st.st_nlink;
458
459                                 entry->bhfi_valid = TRUE;
460                         } else {
461                                 entry->data.nFileSizeLow = 0;
462                                 entry->data.nFileSizeHigh = 0;
463                                 entry->bhfi_valid = FALSE;
464                         }
465
466                         entry->down = NULL;
467                         entry->up = dir;
468                         entry->expanded = FALSE;
469                         entry->scanned = FALSE;
470                         entry->level = level;
471
472                         last = entry;
473                 }
474
475                 last->next = NULL;
476
477                 closedir(pdir);
478         }
479
480         dir->down = first_entry;
481         dir->scanned = TRUE;
482 }
483
484 static Entry* find_entry_unix(Entry* dir, LPCTSTR name)
485 {
486         Entry* entry;
487
488         for(entry=dir->down; entry; entry=entry->next) {
489                 LPCTSTR p = name;
490                 LPCTSTR q = entry->data.cFileName;
491
492                 do {
493                         if (!*p || *p==TEXT('/'))
494                                 return entry;
495                 } while(*p++ == *q++);
496         }
497
498         return 0;
499 }
500
501 static Entry* read_tree_unix(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
502 {
503         TCHAR buffer[MAX_PATH];
504         Entry* entry = &root->entry;
505         LPCTSTR s = path;
506         PTSTR d = buffer;
507
508         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
509
510         entry->etype = ET_UNIX;
511
512         while(entry) {
513                 while(*s && *s!=TEXT('/'))
514                         *d++ = *s++;
515
516                 while(*s == TEXT('/'))
517                         s++;
518
519                 *d++ = TEXT('/');
520                 *d = TEXT('\0');
521
522                 read_directory(entry, buffer, sortOrder, hwnd);
523
524                 if (entry->down)
525                         entry->expanded = TRUE;
526
527                 if (!*s)
528                         break;
529
530                 entry = find_entry_unix(entry, s);
531         }
532
533         SetCursor(old_cursor);
534
535         return entry;
536 }
537
538 #endif /* !defined(_NO_EXTENSIONS) && defined(__WINE__) */
539
540
541 #ifdef _SHELL_FOLDERS
542
543 #ifdef UNICODE
544 #define tcscpyn strcpyn
545 #define get_strret get_strretW
546 #define path_from_pidl path_from_pidlW
547 #else
548 #define tcscpyn wcscpyn
549 #define get_strret get_strretA
550 #define path_from_pidl path_from_pidlA
551 #endif
552
553
554 static LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
555 {
556  LPCSTR s;
557  LPSTR d = dest;
558
559  for(s=source; count&&(*d++=*s++); )
560   count--;
561
562  return dest;
563 }
564
565 static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
566 {
567  LPCWSTR s;
568  LPWSTR d = dest;
569
570  for(s=source; count&&(*d++=*s++); )
571   count--;
572
573  return dest;
574 }
575
576
577 static void get_strretA(STRRET* str, const SHITEMID* shiid, LPSTR buffer, int len)
578 {
579  switch(str->uType) {
580   case STRRET_WSTR:
581         WideCharToMultiByte(CP_ACP, 0, str->UNION_MEMBER(pOleStr), -1, buffer, len, NULL, NULL);
582         break;
583
584   case STRRET_OFFSET:
585         strcpyn(buffer, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), len);
586         break;
587
588   case STRRET_CSTR:
589         strcpyn(buffer, str->UNION_MEMBER(cStr), len);
590  }
591 }
592
593 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
594 {
595  switch(str->uType) {
596   case STRRET_WSTR:
597         wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
598         break;
599
600   case STRRET_OFFSET:
601         MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
602         break;
603
604   case STRRET_CSTR:
605         MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
606  }
607 }
608
609
610 static void free_strret(STRRET* str)
611 {
612         if (str->uType == STRRET_WSTR)
613                 (*Globals.iMalloc->lpVtbl->Free)(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
614 }
615
616
617 HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
618 {
619         STRRET str;
620
621         HRESULT hr = (*folder->lpVtbl->GetDisplayNameOf)(folder, pidl, flags, &str);
622
623         if (SUCCEEDED(hr)) {
624                 get_strret(&str, &pidl->mkid, buffer, len);
625                 free_strret(&str);
626         } else
627                 buffer[0] = '\0';
628
629         return hr;
630 }
631
632
633 HRESULT path_from_pidlA(IShellFolder* folder, LPITEMIDLIST pidl, LPSTR buffer, int len)
634 {
635         STRRET str;
636
637          /* SHGDN_FORPARSING: get full path of id list */
638         HRESULT hr = (*folder->lpVtbl->GetDisplayNameOf)(folder, pidl, SHGDN_FORPARSING, &str);
639
640         if (SUCCEEDED(hr)) {
641                 get_strretA(&str, &pidl->mkid, buffer, len);
642                 free_strret(&str);
643         } else
644                 buffer[0] = '\0';
645
646         return hr;
647 }
648
649 HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len)
650 {
651         STRRET str;
652
653          /* SHGDN_FORPARSING: get full path of id list */
654         HRESULT hr = (*folder->lpVtbl->GetDisplayNameOf)(folder, pidl, SHGDN_FORPARSING, &str);
655
656         if (SUCCEEDED(hr)) {
657                 get_strretW(&str, &pidl->mkid, buffer, len);
658                 free_strret(&str);
659         } else
660                 buffer[0] = '\0';
661
662         return hr;
663 }
664
665
666  /* create an item id list from a file system path */
667
668 static LPITEMIDLIST get_path_pidl(LPTSTR path, HWND hwnd)
669 {
670         LPITEMIDLIST pidl;
671         HRESULT hr;
672         ULONG len;
673
674 #ifdef UNICODE
675         LPWSTR buffer = path;
676 #else
677         WCHAR buffer[MAX_PATH];
678         MultiByteToWideChar(CP_ACP, 0, path, -1, buffer, MAX_PATH);
679 #endif
680
681         hr = (*Globals.iDesktop->lpVtbl->ParseDisplayName)(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
682         if (FAILED(hr))
683                 return NULL;
684
685         return pidl;
686 }
687
688
689  /* convert an item id list from relative to absolute (=relative to the desktop) format */
690
691 static LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd)
692 {
693         if (entry->up && entry->up->etype==ET_SHELL) {
694                 IShellFolder* folder = entry->up->folder;
695                 WCHAR buffer[MAX_PATH];
696
697                 HRESULT hr = path_from_pidlW(folder, entry->pidl, buffer, MAX_PATH);
698
699                 if (SUCCEEDED(hr)) {
700                         LPITEMIDLIST pidl;
701                         ULONG len;
702
703                         hr = (*Globals.iDesktop->lpVtbl->ParseDisplayName)(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
704
705                         if (SUCCEEDED(hr))
706                                 return pidl;
707                 }
708         }
709
710         return entry->pidl;
711 }
712
713
714 HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
715 {
716         IExtractIcon* pExtract;
717
718         if (SUCCEEDED((*folder->lpVtbl->GetUIObjectOf)(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIcon, 0, (LPVOID*)&pExtract))) {
719                 TCHAR path[_MAX_PATH];
720                 unsigned flags;
721                 HICON hicon;
722                 int idx;
723
724                 if (SUCCEEDED((*pExtract->lpVtbl->GetIconLocation)(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
725                         if (!(flags & GIL_NOTFILENAME)) {
726                                 if (idx == -1)
727                                         idx = 0;        /* special case for some control panel applications */
728
729                                 if ((int)ExtractIconEx(path, idx, 0, &hicon, 1) > 0)
730                                         flags &= ~GIL_DONTCACHE;
731                         } else {
732                                 HICON hIconLarge = 0;
733
734                                 HRESULT hr = (*pExtract->lpVtbl->Extract)(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
735
736                                 if (SUCCEEDED(hr))
737                                         DestroyIcon(hIconLarge);
738                         }
739
740                         return hicon;
741                 }
742         }
743
744         return 0;
745 }
746
747
748 static Entry* find_entry_shell(Entry* dir, LPITEMIDLIST pidl)
749 {
750         Entry* entry;
751
752         for(entry=dir->down; entry; entry=entry->next) {
753                 if (entry->pidl->mkid.cb == pidl->mkid.cb &&
754                         !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb))
755                         return entry;
756         }
757
758         return 0;
759 }
760
761 static Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, SORT_ORDER sortOrder, HWND hwnd)
762 {
763         Entry* entry = &root->entry;
764         Entry* next;
765         LPITEMIDLIST next_pidl = pidl;
766         IShellFolder* folder;
767         IShellFolder* child = NULL;
768         HRESULT hr;
769
770         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
771
772 #ifndef _NO_EXTENSIONS
773         entry->etype = ET_SHELL;
774 #endif
775
776         folder = Globals.iDesktop;
777
778         while(entry) {
779                 entry->pidl = next_pidl;
780                 entry->folder = folder;
781
782                 if (!pidl->mkid.cb)
783                         break;
784
785                  /* copy first element of item idlist */
786                 next_pidl = (*Globals.iMalloc->lpVtbl->Alloc)(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT));
787                 memcpy(next_pidl, pidl, pidl->mkid.cb);
788                 ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0;
789
790                 hr = (*folder->lpVtbl->BindToObject)(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
791                 if (!SUCCEEDED(hr))
792                         break;
793
794                 read_directory(entry, NULL, sortOrder, hwnd);
795
796                 if (entry->down)
797                         entry->expanded = TRUE;
798
799                 next = find_entry_shell(entry, next_pidl);
800                 if (!next)
801                         break;
802
803                 folder = child;
804                 entry = next;
805
806                  /* go to next element */
807                 pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
808         }
809
810         SetCursor(old_cursor);
811
812         return entry;
813 }
814
815
816 static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* w32fdata)
817 {
818         if (!(attribs & SFGAO_FILESYSTEM) ||
819                   FAILED(SHGetDataFromIDList(folder, pidl, SHGDFIL_FINDDATA, w32fdata, sizeof(WIN32_FIND_DATA)))) {
820                 WIN32_FILE_ATTRIBUTE_DATA fad;
821                 IDataObject* pDataObj;
822
823                 STGMEDIUM medium = {0, {0}, 0};
824                 FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
825
826                 HRESULT hr = (*folder->lpVtbl->GetUIObjectOf)(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
827
828                 if (SUCCEEDED(hr)) {
829                         hr = (*pDataObj->lpVtbl->GetData)(pDataObj, &fmt, &medium);
830
831                         (*pDataObj->lpVtbl->Release)(pDataObj);
832
833                         if (SUCCEEDED(hr)) {
834                                 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
835                                 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
836
837                                 if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
838                                         w32fdata->dwFileAttributes = fad.dwFileAttributes;
839                                         w32fdata->ftCreationTime = fad.ftCreationTime;
840                                         w32fdata->ftLastAccessTime = fad.ftLastAccessTime;
841                                         w32fdata->ftLastWriteTime = fad.ftLastWriteTime;
842
843                                         if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
844                                                 w32fdata->nFileSizeLow = fad.nFileSizeLow;
845                                                 w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
846                                         }
847                                 }
848
849                                 SetErrorMode(sem_org);
850
851                                 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
852                                 GlobalFree(medium.UNION_MEMBER(hGlobal));
853                         }
854                 }
855         }
856
857         if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
858                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
859
860         if (attribs & SFGAO_READONLY)
861                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
862
863         if (attribs & SFGAO_COMPRESSED)
864                 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
865 }
866
867
868 static void read_directory_shell(Entry* dir, HWND hwnd)
869 {
870         IShellFolder* folder = dir->folder;
871         int level = dir->level + 1;
872         HRESULT hr;
873
874         IShellFolder* child;
875         IEnumIDList* idlist;
876
877         Entry* first_entry = NULL;
878         Entry* last = NULL;
879         Entry* entry;
880
881         if (!folder)
882                 return;
883
884         hr = (*folder->lpVtbl->EnumObjects)(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
885
886         if (SUCCEEDED(hr)) {
887                 for(;;) {
888 #define FETCH_ITEM_COUNT        32
889                         LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
890                         SFGAOF attribs;
891                         ULONG cnt = 0;
892                         ULONG n;
893
894                         memset(pidls, 0, sizeof(pidls));
895
896                         hr = (*idlist->lpVtbl->Next)(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
897                         if (!SUCCEEDED(hr))
898                                 break;
899
900                         if (hr == S_FALSE)
901                                 break;
902
903                         for(n=0; n<cnt; ++n) {
904                                 entry = alloc_entry();
905
906                                 if (!first_entry)
907                                         first_entry = entry;
908
909                                 if (last)
910                                         last->next = entry;
911
912                                 memset(&entry->data, 0, sizeof(WIN32_FIND_DATA));
913                                 entry->bhfi_valid = FALSE;
914
915                                 attribs = ~SFGAO_FILESYSTEM;    /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
916
917                                 hr = (*folder->lpVtbl->GetAttributesOf)(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
918
919                                 if (SUCCEEDED(hr)) {
920                                         if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
921                                                 fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
922
923                                                 entry->bhfi_valid = TRUE;
924                                         } else
925                                                 attribs = 0;
926                                 } else
927                                         attribs = 0;
928
929                                 entry->pidl = pidls[n];
930
931                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
932                                         hr = (*folder->lpVtbl->BindToObject)(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
933
934                                         if (SUCCEEDED(hr))
935                                                 entry->folder = child;
936                                         else
937                                                 entry->folder = NULL;
938                                 }
939                                 else
940                                         entry->folder = NULL;
941
942                                 if (!entry->data.cFileName[0])
943                                         /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
944
945                                  /* get display icons for files and virtual objects */
946                                 if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
947                                         !(attribs & SFGAO_FILESYSTEM)) {
948                                         entry->hicon = extract_icon(folder, pidls[n]);
949
950                                         if (!entry->hicon)
951                                                 entry->hicon = (HICON)-1;       /* don't try again later */
952                                 }
953
954                                 entry->down = NULL;
955                                 entry->up = dir;
956                                 entry->expanded = FALSE;
957                                 entry->scanned = FALSE;
958                                 entry->level = level;
959
960 #ifndef _NO_EXTENSIONS
961                                 entry->etype = ET_SHELL;
962                                 entry->bhfi_valid = FALSE;
963 #endif
964
965                                 last = entry;
966                         }
967                 }
968
969                 (*idlist->lpVtbl->Release)(idlist);
970         }
971
972         if (last)
973                 last->next = NULL;
974
975         dir->down = first_entry;
976         dir->scanned = TRUE;
977 }
978
979 #endif /* _SHELL_FOLDERS */
980
981
982 /* directories first... */
983 static int compareType(const WIN32_FIND_DATA* fd1, const WIN32_FIND_DATA* fd2)
984 {
985         int dir1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
986         int dir2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
987
988         return dir2==dir1? 0: dir2<dir1? -1: 1;
989 }
990
991
992 static int compareName(const void* arg1, const void* arg2)
993 {
994         const WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
995         const WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
996
997         int cmp = compareType(fd1, fd2);
998         if (cmp)
999                 return cmp;
1000
1001         return lstrcmpi(fd1->cFileName, fd2->cFileName);
1002 }
1003
1004 static int compareExt(const void* arg1, const void* arg2)
1005 {
1006         const WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
1007         const WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
1008         const TCHAR *name1, *name2, *ext1, *ext2;
1009
1010         int cmp = compareType(fd1, fd2);
1011         if (cmp)
1012                 return cmp;
1013
1014         name1 = fd1->cFileName;
1015         name2 = fd2->cFileName;
1016
1017         ext1 = _tcsrchr(name1, TEXT('.'));
1018         ext2 = _tcsrchr(name2, TEXT('.'));
1019
1020         if (ext1)
1021                 ext1++;
1022         else
1023                 ext1 = TEXT("");
1024
1025         if (ext2)
1026                 ext2++;
1027         else
1028                 ext2 = TEXT("");
1029
1030         cmp = lstrcmpi(ext1, ext2);
1031         if (cmp)
1032                 return cmp;
1033
1034         return lstrcmpi(name1, name2);
1035 }
1036
1037 static int compareSize(const void* arg1, const void* arg2)
1038 {
1039         WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
1040         WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
1041
1042         int cmp = compareType(fd1, fd2);
1043         if (cmp)
1044                 return cmp;
1045
1046         cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
1047
1048         if (cmp < 0)
1049                 return -1;
1050         else if (cmp > 0)
1051                 return 1;
1052
1053         cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
1054
1055         return cmp<0? -1: cmp>0? 1: 0;
1056 }
1057
1058 static int compareDate(const void* arg1, const void* arg2)
1059 {
1060         WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
1061         WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
1062
1063         int cmp = compareType(fd1, fd2);
1064         if (cmp)
1065                 return cmp;
1066
1067         return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
1068 }
1069
1070
1071 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
1072         compareName,    /* SORT_NAME */
1073         compareExt,             /* SORT_EXT */
1074         compareSize,    /* SORT_SIZE */
1075         compareDate             /* SORT_DATE */
1076 };
1077
1078
1079 static void SortDirectory(Entry* dir, SORT_ORDER sortOrder)
1080 {
1081         Entry* entry = dir->down;
1082         Entry** array, **p;
1083         int len;
1084
1085         len = 0;
1086         for(entry=dir->down; entry; entry=entry->next)
1087                 len++;
1088
1089         if (len) {
1090                 array = (Entry**) alloca(len*sizeof(Entry*));
1091
1092                 p = array;
1093                 for(entry=dir->down; entry; entry=entry->next)
1094                         *p++ = entry;
1095
1096                 /* call qsort with the appropriate compare function */
1097                 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
1098
1099                 dir->down = array[0];
1100
1101                 for(p=array; --len; p++)
1102                         p[0]->next = p[1];
1103
1104                 (*p)->next = 0;
1105         }
1106 }
1107
1108
1109 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
1110 {
1111         TCHAR buffer[MAX_PATH];
1112         Entry* entry;
1113         LPCTSTR s;
1114         PTSTR d;
1115
1116 #ifdef _SHELL_FOLDERS
1117         if (dir->etype == ET_SHELL)
1118         {
1119                 read_directory_shell(dir, hwnd);
1120
1121                 if (Globals.prescan_node) {
1122                         s = path;
1123                         d = buffer;
1124
1125                         while(*s)
1126                                 *d++ = *s++;
1127
1128                         *d++ = TEXT('\\');
1129
1130                         for(entry=dir->down; entry; entry=entry->next)
1131                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1132                                         read_directory_shell(entry, hwnd);
1133                                         SortDirectory(entry, sortOrder);
1134                                 }
1135                 }
1136         }
1137         else
1138 #endif
1139 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1140         if (dir->etype == ET_UNIX)
1141         {
1142                 read_directory_unix(dir, path);
1143
1144                 if (Globals.prescan_node) {
1145                         s = path;
1146                         d = buffer;
1147
1148                         while(*s)
1149                                 *d++ = *s++;
1150
1151                         *d++ = TEXT('/');
1152
1153                         for(entry=dir->down; entry; entry=entry->next)
1154                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1155                                         lstrcpy(d, entry->data.cFileName);
1156                                         read_directory_unix(entry, buffer);
1157                                         SortDirectory(entry, sortOrder);
1158                                 }
1159                 }
1160         }
1161         else
1162 #endif
1163         {
1164                 read_directory_win(dir, path);
1165
1166                 if (Globals.prescan_node) {
1167                         s = path;
1168                         d = buffer;
1169
1170                         while(*s)
1171                                 *d++ = *s++;
1172
1173                         *d++ = TEXT('\\');
1174
1175                         for(entry=dir->down; entry; entry=entry->next)
1176                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1177                                         lstrcpy(d, entry->data.cFileName);
1178                                         read_directory_win(entry, buffer);
1179                                         SortDirectory(entry, sortOrder);
1180                                 }
1181                 }
1182         }
1183
1184         SortDirectory(dir, sortOrder);
1185 }
1186
1187
1188 static ChildWnd* alloc_child_window(LPCTSTR path, LPITEMIDLIST pidl, HWND hwnd)
1189 {
1190         TCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
1191         ChildWnd* child = (ChildWnd*) malloc(sizeof(ChildWnd));
1192         Root* root = &child->root;
1193         Entry* entry;
1194
1195         memset(child, 0, sizeof(ChildWnd));
1196
1197         child->left.treePane = TRUE;
1198         child->left.visible_cols = 0;
1199
1200         child->right.treePane = FALSE;
1201 #ifndef _NO_EXTENSIONS
1202         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS;
1203 #else
1204         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES;
1205 #endif
1206
1207         child->pos.length = sizeof(WINDOWPLACEMENT);
1208         child->pos.flags = 0;
1209         child->pos.showCmd = SW_SHOWNORMAL;
1210         child->pos.rcNormalPosition.left = CW_USEDEFAULT;
1211         child->pos.rcNormalPosition.top = CW_USEDEFAULT;
1212         child->pos.rcNormalPosition.right = CW_USEDEFAULT;
1213         child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
1214
1215         child->focus_pane = 0;
1216         child->split_pos = DEFAULT_SPLIT_POS;
1217         child->sortOrder = SORT_NAME;
1218         child->header_wdths_ok = FALSE;
1219
1220         if (path)
1221         {
1222                 lstrcpy(child->path, path);
1223
1224                 _tsplitpath(path, drv, dir, name, ext);
1225         }
1226
1227         root->entry.level = 0;
1228
1229 #ifdef _SHELL_FOLDERS
1230         if (pidl)
1231         {
1232                 root->drive_type = DRIVE_UNKNOWN;
1233                 lstrcpy(drv, TEXT("\\"));
1234                 lstrcpy(root->volname, TEXT("Desktop"));
1235                 root->fs_flags = 0;
1236                 lstrcpy(root->fs, TEXT("Shell"));
1237
1238                 entry = read_tree_shell(root, pidl, child->sortOrder, hwnd);
1239         }
1240         else
1241 #endif
1242 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1243         if (*path == '/')
1244         {
1245                 root->drive_type = GetDriveType(path);
1246
1247                 lstrcat(drv, TEXT("/"));
1248                 lstrcpy(root->volname, TEXT("root fs"));
1249                 root->fs_flags = 0;
1250                 lstrcpy(root->fs, TEXT("unixfs"));
1251
1252                 lstrcpy(root->path, TEXT("/"));
1253                 entry = read_tree_unix(root, path, child->sortOrder, hwnd);
1254         }
1255         else
1256 #endif
1257         {
1258                 root->drive_type = GetDriveType(path);
1259
1260                 lstrcat(drv, TEXT("\\"));
1261                 GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1262
1263                 lstrcpy(root->path, drv);
1264                 entry = read_tree_win(root, path, child->sortOrder, hwnd);
1265         }
1266
1267 #ifdef _SHELL_FOLDERS
1268         if (root->entry.etype == ET_SHELL)
1269                 lstrcpy(root->entry.data.cFileName, TEXT("Desktop"));
1270         else
1271 #endif
1272                 wsprintf(root->entry.data.cFileName, TEXT("%s - %s"), drv, root->fs);
1273
1274         root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1275
1276         child->left.root = &root->entry;
1277         child->right.root = NULL;
1278
1279         set_curdir(child, entry, hwnd);
1280
1281         return child;
1282 }
1283
1284
1285 /* free all memory associated with a child window */
1286 static void free_child_window(ChildWnd* child)
1287 {
1288         free_entries(&child->root.entry);
1289         free(child);
1290 }
1291
1292
1293 /* get full path of specified directory entry */
1294 static void get_path(Entry* dir, PTSTR path)
1295 {
1296         Entry* entry;
1297         int len = 0;
1298         int level = 0;
1299
1300 #ifdef _SHELL_FOLDERS
1301         if (dir->etype == ET_SHELL)
1302         {
1303                 SFGAOF attribs;
1304                 HRESULT hr = S_OK;
1305
1306                 path[0] = TEXT('\0');
1307
1308                 attribs = 0;
1309
1310                 if (dir->folder)
1311                         hr = (*dir->folder->lpVtbl->GetAttributesOf)(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1312
1313                 if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1314                         IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1315
1316                         hr = path_from_pidl(parent, dir->pidl, path, MAX_PATH);
1317                 }
1318         }
1319         else
1320 #endif
1321         {
1322                 for(entry=dir; entry; level++) {
1323                         LPCTSTR name;
1324                         int l;
1325
1326                         {
1327                                 LPCTSTR s;
1328                                 name = entry->data.cFileName;
1329                                 s = name;
1330
1331                                 for(l=0; *s && *s!=TEXT('/') && *s!=TEXT('\\'); s++)
1332                                         l++;
1333                         }
1334
1335                         if (entry->up) {
1336                                 if (l > 0) {
1337                                         memmove(path+l+1, path, len*sizeof(TCHAR));
1338                                         memcpy(path+1, name, l*sizeof(TCHAR));
1339                                         len += l+1;
1340
1341 #ifndef _NO_EXTENSIONS
1342                                         if (entry->etype == ET_UNIX)
1343                                                 path[0] = TEXT('/');
1344                                         else
1345 #endif
1346                                         path[0] = TEXT('\\');
1347                                 }
1348
1349                                 entry = entry->up;
1350                         } else {
1351                                 memmove(path+l, path, len*sizeof(TCHAR));
1352                                 memcpy(path, name, l*sizeof(TCHAR));
1353                                 len += l;
1354                                 break;
1355                         }
1356                 }
1357
1358                 if (!level) {
1359 #ifndef _NO_EXTENSIONS
1360                         if (entry->etype == ET_UNIX)
1361                                 path[len++] = TEXT('/');
1362                         else
1363 #endif
1364                                 path[len++] = TEXT('\\');
1365                 }
1366
1367                 path[len] = TEXT('\0');
1368         }
1369 }
1370
1371
1372 static void resize_frame_rect(HWND hwnd, PRECT prect)
1373 {
1374         int new_top;
1375         RECT rt;
1376
1377         if (IsWindowVisible(Globals.htoolbar)) {
1378                 SendMessage(Globals.htoolbar, WM_SIZE, 0, 0);
1379                 GetClientRect(Globals.htoolbar, &rt);
1380                 prect->top = rt.bottom+3;
1381                 prect->bottom -= rt.bottom+3;
1382         }
1383
1384         if (IsWindowVisible(Globals.hdrivebar)) {
1385                 SendMessage(Globals.hdrivebar, WM_SIZE, 0, 0);
1386                 GetClientRect(Globals.hdrivebar, &rt);
1387                 new_top = --prect->top + rt.bottom+3;
1388                 MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
1389                 prect->top = new_top;
1390                 prect->bottom -= rt.bottom+2;
1391         }
1392
1393         if (IsWindowVisible(Globals.hstatusbar)) {
1394                 int parts[] = {300, 500};
1395
1396                 SendMessage(Globals.hstatusbar, WM_SIZE, 0, 0);
1397                 SendMessage(Globals.hstatusbar, SB_SETPARTS, 2, (LPARAM)&parts);
1398                 GetClientRect(Globals.hstatusbar, &rt);
1399                 prect->bottom -= rt.bottom;
1400         }
1401
1402         MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1403 }
1404
1405 static void resize_frame(HWND hwnd, int cx, int cy)
1406 {
1407         RECT rect;
1408
1409         rect.left   = 0;
1410         rect.top    = 0;
1411         rect.right  = cx;
1412         rect.bottom = cy;
1413
1414         resize_frame_rect(hwnd, &rect);
1415 }
1416
1417 static void resize_frame_client(HWND hwnd)
1418 {
1419         RECT rect;
1420
1421         GetClientRect(hwnd, &rect);
1422
1423         resize_frame_rect(hwnd, &rect);
1424 }
1425
1426
1427 static HHOOK hcbthook;
1428 static ChildWnd* newchild = NULL;
1429
1430 LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
1431 {
1432         if (code==HCBT_CREATEWND && newchild) {
1433                 ChildWnd* child = newchild;
1434                 newchild = NULL;
1435
1436                 child->hwnd = (HWND) wparam;
1437                 SetWindowLong(child->hwnd, GWL_USERDATA, (LPARAM)child);
1438         }
1439
1440         return CallNextHookEx(hcbthook, code, wparam, lparam);
1441 }
1442
1443 static HWND create_child_window(ChildWnd* child)
1444 {
1445         MDICREATESTRUCT mcs;
1446         int idx;
1447
1448         mcs.szClass = WINEFILETREE;
1449         mcs.szTitle = (LPTSTR)child->path;
1450         mcs.hOwner  = Globals.hInstance;
1451         mcs.x       = child->pos.rcNormalPosition.left;
1452         mcs.y       = child->pos.rcNormalPosition.top;
1453         mcs.cx      = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
1454         mcs.cy      = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
1455         mcs.style   = 0;
1456         mcs.lParam  = 0;
1457
1458         hcbthook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
1459
1460         newchild = child;
1461         child->hwnd = (HWND) SendMessage(Globals.hmdiclient, WM_MDICREATE, 0, (LPARAM)&mcs);
1462         if (!child->hwnd) {
1463                 UnhookWindowsHookEx(hcbthook);
1464                 return 0;
1465         }
1466
1467         UnhookWindowsHookEx(hcbthook);
1468
1469         idx = ListBox_FindItemData(child->left.hwnd, ListBox_GetCurSel(child->left.hwnd), child->left.cur);
1470         ListBox_SetCurSel(child->left.hwnd, idx);
1471
1472         return child->hwnd;
1473 }
1474
1475
1476 struct ExecuteDialog {
1477         TCHAR   cmd[MAX_PATH];
1478         int             cmdshow;
1479 };
1480
1481
1482 static BOOL CALLBACK ExecuteDialogWndProg(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1483 {
1484         static struct ExecuteDialog* dlg;
1485
1486         switch(nmsg) {
1487                 case WM_INITDIALOG:
1488                         dlg = (struct ExecuteDialog*) lparam;
1489                         return 1;
1490
1491                 case WM_COMMAND: {
1492                         int id = (int)wparam;
1493
1494                         if (id == IDOK) {
1495                                 GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, MAX_PATH);
1496                                 dlg->cmdshow = Button_GetState(GetDlgItem(hwnd,214))&BST_CHECKED?
1497                                                                                                 SW_SHOWMINIMIZED: SW_SHOWNORMAL;
1498                                 EndDialog(hwnd, id);
1499                         } else if (id == IDCANCEL)
1500                                 EndDialog(hwnd, id);
1501
1502                         return 1;}
1503         }
1504
1505         return 0;
1506 }
1507
1508
1509 #ifndef _NO_EXTENSIONS
1510
1511 static struct FullScreenParameters {
1512         BOOL    mode;
1513         RECT    orgPos;
1514         BOOL    wasZoomed;
1515 } g_fullscreen = {
1516     FALSE,      /* mode */
1517         {0, 0, 0, 0},
1518         FALSE
1519 };
1520
1521 void frame_get_clientspace(HWND hwnd, PRECT prect)
1522 {
1523         RECT rt;
1524
1525         if (!IsIconic(hwnd))
1526                 GetClientRect(hwnd, prect);
1527         else {
1528                 WINDOWPLACEMENT wp;
1529
1530                 GetWindowPlacement(hwnd, &wp);
1531
1532                 prect->left = prect->top = 0;
1533                 prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
1534                                                 2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
1535                 prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
1536                                                 2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
1537                                                 GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
1538         }
1539
1540         if (IsWindowVisible(Globals.htoolbar)) {
1541                 GetClientRect(Globals.htoolbar, &rt);
1542                 prect->top += rt.bottom+2;
1543         }
1544
1545         if (IsWindowVisible(Globals.hdrivebar)) {
1546                 GetClientRect(Globals.hdrivebar, &rt);
1547                 prect->top += rt.bottom+2;
1548         }
1549
1550         if (IsWindowVisible(Globals.hstatusbar)) {
1551                 GetClientRect(Globals.hstatusbar, &rt);
1552                 prect->bottom -= rt.bottom;
1553         }
1554 }
1555
1556 static BOOL toggle_fullscreen(HWND hwnd)
1557 {
1558         RECT rt;
1559
1560         if ((g_fullscreen.mode=!g_fullscreen.mode)) {
1561                 GetWindowRect(hwnd, &g_fullscreen.orgPos);
1562                 g_fullscreen.wasZoomed = IsZoomed(hwnd);
1563
1564                 Frame_CalcFrameClient(hwnd, &rt);
1565                 ClientToScreen(hwnd, (LPPOINT)&rt.left);
1566                 ClientToScreen(hwnd, (LPPOINT)&rt.right);
1567
1568                 rt.left = g_fullscreen.orgPos.left-rt.left;
1569                 rt.top = g_fullscreen.orgPos.top-rt.top;
1570                 rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
1571                 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
1572
1573                 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1574         } else {
1575                 MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
1576                                                         g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
1577                                                         g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
1578
1579                 if (g_fullscreen.wasZoomed)
1580                         ShowWindow(hwnd, WS_MAXIMIZE);
1581         }
1582
1583         return g_fullscreen.mode;
1584 }
1585
1586 static void fullscreen_move(HWND hwnd)
1587 {
1588         RECT rt, pos;
1589         GetWindowRect(hwnd, &pos);
1590
1591         Frame_CalcFrameClient(hwnd, &rt);
1592         ClientToScreen(hwnd, (LPPOINT)&rt.left);
1593         ClientToScreen(hwnd, (LPPOINT)&rt.right);
1594
1595         rt.left = pos.left-rt.left;
1596         rt.top = pos.top-rt.top;
1597         rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
1598         rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
1599
1600         MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1601 }
1602
1603 #endif
1604
1605
1606 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
1607 {
1608         BOOL vis = IsWindowVisible(hchild);
1609
1610         CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
1611
1612         ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
1613
1614 #ifndef _NO_EXTENSIONS
1615         if (g_fullscreen.mode)
1616                 fullscreen_move(hwnd);
1617 #endif
1618
1619         resize_frame_client(hwnd);
1620 }
1621
1622 BOOL activate_drive_window(LPCTSTR path)
1623 {
1624         TCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
1625         HWND child_wnd;
1626
1627         _tsplitpath(path, drv1, 0, 0, 0);
1628
1629         /* search for a already open window for the same drive */
1630         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
1631                 ChildWnd* child = (ChildWnd*) GetWindowLong(child_wnd, GWL_USERDATA);
1632
1633                 if (child) {
1634                         _tsplitpath(child->root.path, drv2, 0, 0, 0);
1635
1636                         if (!lstrcmpi(drv2, drv1)) {
1637                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
1638
1639                                 if (IsMinimized(child_wnd))
1640                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
1641
1642                                 return TRUE;
1643                         }
1644                 }
1645         }
1646
1647         return FALSE;
1648 }
1649
1650 BOOL activate_fs_window(LPCTSTR filesys)
1651 {
1652         HWND child_wnd;
1653
1654         /* search for a already open window of the given file system name */
1655         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
1656                 ChildWnd* child = (ChildWnd*) GetWindowLong(child_wnd, GWL_USERDATA);
1657
1658                 if (child) {
1659                         if (!lstrcmpi(child->root.fs, filesys)) {
1660                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
1661
1662                                 if (IsMinimized(child_wnd))
1663                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
1664
1665                                 return TRUE;
1666                         }
1667                 }
1668         }
1669
1670         return FALSE;
1671 }
1672
1673 LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1674 {
1675         switch(nmsg) {
1676                 case WM_CLOSE:
1677                         DestroyWindow(hwnd);
1678
1679                          /* clear handle variables */
1680                         Globals.hMenuFrame = 0;
1681                         Globals.hMenuView = 0;
1682                         Globals.hMenuOptions = 0;
1683                         Globals.hMainWnd = 0;
1684                         Globals.hmdiclient = 0;
1685                         Globals.hdrivebar = 0;
1686                         break;
1687
1688                 case WM_DESTROY:
1689                          /* don't exit desktop when closing file manager window */
1690                         if (!Globals.hwndParent)
1691                                 PostQuitMessage(0);
1692                         break;
1693
1694                 case WM_COMMAND: {
1695                         UINT cmd = LOWORD(wparam);
1696                         HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
1697
1698                         if (SendMessage(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
1699                                 break;
1700
1701                         if (cmd>=ID_DRIVE_FIRST && cmd<=ID_DRIVE_FIRST+0xFF) {
1702                                 TCHAR drv[_MAX_DRIVE], path[MAX_PATH];
1703                                 ChildWnd* child;
1704                                 LPCTSTR root = Globals.drives;
1705                                 int i;
1706
1707                                 for(i=cmd-ID_DRIVE_FIRST; i--; root++)
1708                                         while(*root)
1709                                                 root++;
1710
1711                                 if (activate_drive_window(root))
1712                                         return 0;
1713
1714                                 _tsplitpath(root, drv, 0, 0, 0);
1715
1716                                 if (!SetCurrentDirectory(drv)) {
1717                                         display_error(hwnd, GetLastError());
1718                                         return 0;
1719                                 }
1720
1721                                 GetCurrentDirectory(MAX_PATH, path); /*TODO: store last directory per drive */
1722                                 child = alloc_child_window(path, NULL, hwnd);
1723
1724                                 if (!create_child_window(child))
1725                                         free(child);
1726                         } else switch(cmd) {
1727                                 case ID_FILE_EXIT:
1728                                         SendMessage(hwnd, WM_CLOSE, 0, 0);
1729                                         break;
1730
1731                                 case ID_WINDOW_NEW: {
1732                                         TCHAR path[MAX_PATH];
1733                                         ChildWnd* child;
1734
1735                                         GetCurrentDirectory(MAX_PATH, path);
1736                                         child = alloc_child_window(path, NULL, hwnd);
1737
1738                                         if (!create_child_window(child))
1739                                                 free(child);
1740                                         break;}
1741
1742                                 case ID_WINDOW_CASCADE:
1743                                         SendMessage(Globals.hmdiclient, WM_MDICASCADE, 0, 0);
1744                                         break;
1745
1746                                 case ID_WINDOW_TILE_HORZ:
1747                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
1748                                         break;
1749
1750                                 case ID_WINDOW_TILE_VERT:
1751                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_VERTICAL, 0);
1752                                         break;
1753
1754                                 case ID_WINDOW_ARRANGE:
1755                                         SendMessage(Globals.hmdiclient, WM_MDIICONARRANGE, 0, 0);
1756                                         break;
1757
1758                                 case ID_VIEW_TOOL_BAR:
1759                                         toggle_child(hwnd, cmd, Globals.htoolbar);
1760                                         break;
1761
1762                                 case ID_VIEW_DRIVE_BAR:
1763                                         toggle_child(hwnd, cmd, Globals.hdrivebar);
1764                                         break;
1765
1766                                 case ID_VIEW_STATUSBAR:
1767                                         toggle_child(hwnd, cmd, Globals.hstatusbar);
1768                                         break;
1769
1770                                 case ID_EXECUTE: {
1771                                         struct ExecuteDialog dlg;
1772
1773                                         memset(&dlg, 0, sizeof(struct ExecuteDialog));
1774
1775                                         if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_EXECUTE), hwnd, ExecuteDialogWndProg, (LPARAM)&dlg) == IDOK) {
1776                                                 HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, dlg.cmd/*file*/, NULL/*parameters*/, NULL/*dir*/, dlg.cmdshow);
1777
1778                                                 if ((int)hinst <= 32)
1779                                                         display_error(hwnd, GetLastError());
1780                                         }
1781                                         break;}
1782
1783                                 case ID_HELP:
1784                                         WinHelp(hwnd, TEXT("winfile"), HELP_INDEX, 0);
1785                                         break;
1786
1787 #ifndef _NO_EXTENSIONS
1788                                 case ID_VIEW_FULLSCREEN:
1789                                         CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
1790                                         break;
1791
1792 #ifdef __WINE__
1793                                 case ID_DRIVE_UNIX_FS: {
1794                                         TCHAR path[MAX_PATH];
1795                                         ChildWnd* child;
1796
1797                                         if (activate_fs_window(TEXT("unixfs")))
1798                                                 break;
1799
1800                                         getcwd(path, MAX_PATH);
1801                                         child = alloc_child_window(path, NULL, hwnd);
1802
1803                                         if (!create_child_window(child))
1804                                                 free(child);
1805                                         break;}
1806 #endif
1807 #ifdef _SHELL_FOLDERS
1808                                 case ID_DRIVE_SHELL_NS: {
1809                                         TCHAR path[MAX_PATH];
1810                                         ChildWnd* child;
1811
1812                                         if (activate_fs_window(TEXT("Shell")))
1813                                                 break;
1814
1815                                         GetCurrentDirectory(MAX_PATH, path);
1816                                         child = alloc_child_window(path, get_path_pidl(path,hwnd), hwnd);
1817
1818                                         if (!create_child_window(child))
1819                                                 free(child);
1820                                         break;}
1821 #endif
1822 #endif
1823
1824                                 /*TODO: There are even more menu items! */
1825
1826 #ifndef _NO_EXTENSIONS
1827 #ifdef __WINE__
1828                                 case ID_LICENSE:
1829                                         WineLicense(Globals.hMainWnd);
1830                                         break;
1831
1832                                 case ID_NO_WARRANTY:
1833                                         WineWarranty(Globals.hMainWnd);
1834                                         break;
1835 #endif
1836
1837                                 case ID_ABOUT_WINE:
1838                                         ShellAbout(hwnd, TEXT("WINE"), TEXT("Winefile"), 0);
1839                                         break;
1840
1841                                 case ID_ABOUT:  /*ID_ABOUT_WINE: */
1842                                         ShellAbout(hwnd, TEXT("Winefile"), NULL, 0);
1843                                         break;
1844 #endif  /* _NO_EXTENSIONS */
1845
1846                                 default:
1847                                         /*TODO: if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
1848                                                 STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
1849                                         else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
1850                                                 (cmd<SC_SIZE || cmd>SC_RESTORE))
1851                                                 MessageBox(hwnd, TEXT("Not yet implemented"), TEXT("Winefile"), MB_OK);
1852
1853                                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
1854                         }
1855                         break;}
1856
1857                 case WM_SIZE:
1858                         resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
1859                         break;  /* do not pass message to DefFrameProc */
1860
1861 #ifndef _NO_EXTENSIONS
1862                 case WM_GETMINMAXINFO: {
1863                         LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
1864
1865                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
1866                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
1867                         break;}
1868
1869                 case FRM_CALC_CLIENT:
1870                         frame_get_clientspace(hwnd, (PRECT)lparam);
1871                         return TRUE;
1872 #endif /* _NO_EXTENSIONS */
1873
1874                 default:
1875                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
1876         }
1877
1878         return 0;
1879 }
1880
1881
1882 static const LPTSTR g_pos_names[COLUMNS] = {
1883         TEXT(""),                       /* symbol */
1884         TEXT("Name"),
1885         TEXT("Size"),
1886         TEXT("CDate"),
1887 #ifndef _NO_EXTENSIONS
1888         TEXT("ADate"),
1889         TEXT("MDate"),
1890         TEXT("Index/Inode"),
1891         TEXT("Links"),
1892 #endif /* _NO_EXTENSIONS */
1893         TEXT("Attributes"),
1894 #ifndef _NO_EXTENSIONS
1895         TEXT("Security")
1896 #endif
1897 };
1898
1899 static const int g_pos_align[] = {
1900         0,
1901         HDF_LEFT,       /* Name */
1902         HDF_RIGHT,      /* Size */
1903         HDF_LEFT,       /* CDate */
1904 #ifndef _NO_EXTENSIONS
1905         HDF_LEFT,       /* ADate */
1906         HDF_LEFT,       /* MDate */
1907         HDF_LEFT,       /* Index */
1908         HDF_CENTER,     /* Links */
1909 #endif
1910         HDF_CENTER,     /* Attributes */
1911 #ifndef _NO_EXTENSIONS
1912         HDF_LEFT        /* Security */
1913 #endif
1914 };
1915
1916 static void resize_tree(ChildWnd* child, int cx, int cy)
1917 {
1918         HDWP hdwp = BeginDeferWindowPos(4);
1919         RECT rt;
1920
1921         rt.left   = 0;
1922         rt.top    = 0;
1923         rt.right  = cx;
1924         rt.bottom = cy;
1925
1926         cx = child->split_pos + SPLIT_WIDTH/2;
1927
1928 #ifndef _NO_EXTENSIONS
1929         {
1930                 WINDOWPOS wp;
1931                 HD_LAYOUT hdl;
1932
1933                 hdl.prc   = &rt;
1934                 hdl.pwpos = &wp;
1935
1936                 Header_Layout(child->left.hwndHeader, &hdl);
1937
1938                 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
1939                                                 wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
1940                 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
1941                                                 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
1942         }
1943 #endif /* _NO_EXTENSIONS */
1944
1945         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);
1946         DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
1947
1948         EndDeferWindowPos(hdwp);
1949 }
1950
1951
1952 #ifndef _NO_EXTENSIONS
1953
1954 static HWND create_header(HWND parent, Pane* pane, int id)
1955 {
1956         HD_ITEM hdi;
1957         int idx;
1958
1959         HWND hwnd = CreateWindow(WC_HEADER, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ/*TODO: |HDS_BUTTONS + sort orders*/,
1960                                                                 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
1961         if (!hwnd)
1962                 return 0;
1963
1964         SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), FALSE);
1965
1966         hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
1967
1968         for(idx=0; idx<COLUMNS; idx++) {
1969                 hdi.pszText = g_pos_names[idx];
1970                 hdi.fmt = HDF_STRING | g_pos_align[idx];
1971                 hdi.cxy = pane->widths[idx];
1972                 Header_InsertItem(hwnd, idx, &hdi);
1973         }
1974
1975         return hwnd;
1976 }
1977
1978 #endif /* _NO_EXTENSIONS */
1979
1980
1981 static void init_output(HWND hwnd)
1982 {
1983         TCHAR b[16];
1984         HFONT old_font;
1985         HDC hdc = GetDC(hwnd);
1986
1987         if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, TEXT("1000"), 0, b, 16) > 4)
1988                 Globals.num_sep = b[1];
1989         else
1990                 Globals.num_sep = TEXT('.');
1991
1992         old_font = SelectFont(hdc, Globals.hfont);
1993         GetTextExtentPoint32(hdc, TEXT(" "), 1, &Globals.spaceSize);
1994         SelectFont(hdc, old_font);
1995         ReleaseDC(hwnd, hdc);
1996 }
1997
1998 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
1999
2000
2001 /* calculate prefered width for all visible columns */
2002
2003 static BOOL calc_widths(Pane* pane, BOOL anyway)
2004 {
2005         int col, x, cx, spc=3*Globals.spaceSize.cx;
2006         int entries = ListBox_GetCount(pane->hwnd);
2007         int orgWidths[COLUMNS];
2008         int orgPositions[COLUMNS+1];
2009         HFONT hfontOld;
2010         HDC hdc;
2011         int cnt;
2012
2013         if (!anyway) {
2014                 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2015                 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2016         }
2017
2018         for(col=0; col<COLUMNS; col++)
2019                 pane->widths[col] = 0;
2020
2021         hdc = GetDC(pane->hwnd);
2022         hfontOld = SelectFont(hdc, Globals.hfont);
2023
2024         for(cnt=0; cnt<entries; cnt++) {
2025                 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
2026
2027                 DRAWITEMSTRUCT dis;
2028
2029                 dis.CtlType               = 0;
2030                 dis.CtlID                 = 0;
2031                 dis.itemID                = 0;
2032                 dis.itemAction    = 0;
2033                 dis.itemState     = 0;
2034                 dis.hwndItem      = pane->hwnd;
2035                 dis.hDC                   = hdc;
2036                 dis.rcItem.left   = 0;
2037                 dis.rcItem.top    = 0;
2038                 dis.rcItem.right  = 0;
2039                 dis.rcItem.bottom = 0;
2040                 /*dis.itemData    = 0; */
2041
2042                 draw_item(pane, &dis, entry, COLUMNS);
2043         }
2044
2045         SelectObject(hdc, hfontOld);
2046         ReleaseDC(pane->hwnd, hdc);
2047
2048         x = 0;
2049         for(col=0; col<COLUMNS; col++) {
2050                 pane->positions[col] = x;
2051                 cx = pane->widths[col];
2052
2053                 if (cx) {
2054                         cx += spc;
2055
2056                         if (cx < IMAGE_WIDTH)
2057                                 cx = IMAGE_WIDTH;
2058
2059                         pane->widths[col] = cx;
2060                 }
2061
2062                 x += cx;
2063         }
2064
2065         pane->positions[COLUMNS] = x;
2066
2067         ListBox_SetHorizontalExtent(pane->hwnd, x);
2068
2069         /* no change? */
2070         if (!memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2071                 return FALSE;
2072
2073         /* don't move, if only collapsing an entry */
2074         if (!anyway && pane->widths[0]<orgWidths[0] &&
2075                 !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
2076                 pane->widths[0] = orgWidths[0];
2077                 memcpy(pane->positions, orgPositions, sizeof(orgPositions));
2078
2079                 return FALSE;
2080         }
2081
2082         InvalidateRect(pane->hwnd, 0, TRUE);
2083
2084         return TRUE;
2085 }
2086
2087
2088 /* calculate one prefered column width */
2089
2090 static void calc_single_width(Pane* pane, int col)
2091 {
2092         HFONT hfontOld;
2093         int x, cx;
2094         int entries = ListBox_GetCount(pane->hwnd);
2095         int cnt;
2096         HDC hdc;
2097
2098         pane->widths[col] = 0;
2099
2100         hdc = GetDC(pane->hwnd);
2101         hfontOld = SelectFont(hdc, Globals.hfont);
2102
2103         for(cnt=0; cnt<entries; cnt++) {
2104                 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
2105                 DRAWITEMSTRUCT dis;
2106
2107                 dis.CtlType               = 0;
2108                 dis.CtlID                 = 0;
2109                 dis.itemID                = 0;
2110                 dis.itemAction    = 0;
2111                 dis.itemState     = 0;
2112                 dis.hwndItem      = pane->hwnd;
2113                 dis.hDC                   = hdc;
2114                 dis.rcItem.left   = 0;
2115                 dis.rcItem.top    = 0;
2116                 dis.rcItem.right  = 0;
2117                 dis.rcItem.bottom = 0;
2118                 /*dis.itemData    = 0; */
2119
2120                 draw_item(pane, &dis, entry, col);
2121         }
2122
2123         SelectObject(hdc, hfontOld);
2124         ReleaseDC(pane->hwnd, hdc);
2125
2126         cx = pane->widths[col];
2127
2128         if (cx) {
2129                 cx += 3*Globals.spaceSize.cx;
2130
2131                 if (cx < IMAGE_WIDTH)
2132                         cx = IMAGE_WIDTH;
2133         }
2134
2135         pane->widths[col] = cx;
2136
2137         x = pane->positions[col] + cx;
2138
2139         for(; col<COLUMNS; ) {
2140                 pane->positions[++col] = x;
2141                 x += pane->widths[col];
2142         }
2143
2144         ListBox_SetHorizontalExtent(pane->hwnd, x);
2145 }
2146
2147
2148 /* insert listbox entries after index idx */
2149
2150 static void insert_entries(Pane* pane, Entry* dir, int idx)
2151 {
2152         Entry* entry = dir;
2153
2154         if (!entry)
2155                 return;
2156
2157         ShowWindow(pane->hwnd, SW_HIDE);
2158
2159         for(; entry; entry=entry->next) {
2160 #ifndef _LEFT_FILES
2161                 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2162                         continue;
2163 #endif
2164
2165                 /* don't display entries "." and ".." in the left pane */
2166                 if (pane->treePane && (entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
2167                                 && entry->data.cFileName[0]==TEXT('.'))
2168                         if (
2169 #ifndef _NO_EXTENSIONS
2170                                 entry->data.cFileName[1]==TEXT('\0') ||
2171 #endif
2172                                 (entry->data.cFileName[1]==TEXT('.') && entry->data.cFileName[2]==TEXT('\0')))
2173                                 continue;
2174
2175                 if (idx != -1)
2176                         idx++;
2177
2178                 ListBox_InsertItemData(pane->hwnd, idx, entry);
2179
2180                 if (pane->treePane && entry->expanded)
2181                         insert_entries(pane, entry->down, idx);
2182         }
2183
2184         ShowWindow(pane->hwnd, SW_SHOW);
2185 }
2186
2187
2188 static WNDPROC g_orgTreeWndProc;
2189
2190 static void create_tree_window(HWND parent, Pane* pane, int id, int id_header)
2191 {
2192         static int s_init = 0;
2193         Entry* entry = pane->root;
2194
2195         pane->hwnd = CreateWindow(TEXT("ListBox"), TEXT(""), WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
2196                                                                 LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
2197                                                                 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
2198
2199         SetWindowLong(pane->hwnd, GWL_USERDATA, (LPARAM)pane);
2200         g_orgTreeWndProc = SubclassWindow(pane->hwnd, TreeWndProc);
2201
2202         SendMessage(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2203
2204         /* insert entries into listbox */
2205         if (entry)
2206                 insert_entries(pane, entry, -1);
2207
2208         /* calculate column widths */
2209         if (!s_init) {
2210                 s_init = 1;
2211                 init_output(pane->hwnd);
2212         }
2213
2214         calc_widths(pane, TRUE);
2215
2216 #ifndef _NO_EXTENSIONS
2217         pane->hwndHeader = create_header(parent, pane, id_header);
2218 #endif
2219 }
2220
2221
2222 static void InitChildWindow(ChildWnd* child)
2223 {
2224         create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT);
2225         create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT);
2226 }
2227
2228
2229 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
2230 {
2231         SYSTEMTIME systime;
2232         FILETIME lft;
2233         int len = 0;
2234
2235         *buffer = TEXT('\0');
2236
2237         if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2238                 return;
2239
2240         if (!FileTimeToLocalFileTime(ft, &lft))
2241                 {err: _tcscpy(buffer,TEXT("???")); return;}
2242
2243         if (!FileTimeToSystemTime(&lft, &systime))
2244                 goto err;
2245
2246         if (visible_cols & COL_DATE) {
2247                 len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
2248                 if (!len)
2249                         goto err;
2250         }
2251
2252         if (visible_cols & COL_TIME) {
2253                 if (len)
2254                         buffer[len-1] = ' ';
2255
2256                 buffer[len++] = ' ';
2257
2258                 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
2259                         buffer[len] = TEXT('\0');
2260         }
2261 }
2262
2263
2264 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2265 {
2266         RECT rt = {0, 0, 0, 0};
2267
2268         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2269
2270         if (rt.right > pane->widths[col])
2271                 pane->widths[col] = rt.right;
2272 }
2273
2274 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2275 {
2276         RECT rt = {0, 0, 0, 0};
2277
2278 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
2279         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
2280
2281         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2282         /*FIXME rt (0,0) ??? */
2283
2284         if (rt.right > pane->widths[col])
2285                 pane->widths[col] = rt.right;
2286 }
2287
2288
2289 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str, DWORD flags)
2290 {
2291         int x = dis->rcItem.left;
2292         RECT rt;
2293
2294         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2295         rt.top    = dis->rcItem.top;
2296         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2297         rt.bottom = dis->rcItem.bottom;
2298
2299         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
2300 }
2301
2302 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2303 {
2304         int x = dis->rcItem.left;
2305         RECT rt;
2306
2307         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2308         rt.top    = dis->rcItem.top;
2309         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2310         rt.bottom = dis->rcItem.bottom;
2311
2312 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
2313         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
2314
2315         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2316 }
2317
2318 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2319 {
2320         int x = dis->rcItem.left;
2321         RECT rt;
2322         LPCTSTR s = str;
2323         TCHAR b[128];
2324         LPTSTR d = b;
2325         int pos;
2326
2327         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2328         rt.top    = dis->rcItem.top;
2329         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2330         rt.bottom = dis->rcItem.bottom;
2331
2332         if (*s)
2333                 *d++ = *s++;
2334
2335         /* insert number separator characters */
2336         pos = lstrlen(s) % 3;
2337
2338         while(*s)
2339                 if (pos--)
2340                         *d++ = *s++;
2341                 else {
2342                         *d++ = Globals.num_sep;
2343                         pos = 3;
2344                 }
2345
2346         DrawText(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
2347 }
2348
2349
2350 static int is_exe_file(LPCTSTR ext)
2351 {
2352         static const LPCTSTR executable_extensions[] = {
2353                 TEXT("COM"),
2354                 TEXT("EXE"),
2355                 TEXT("BAT"),
2356                 TEXT("CMD"),
2357 #ifndef _NO_EXTENSIONS
2358                 TEXT("CMM"),
2359                 TEXT("BTM"),
2360                 TEXT("AWK"),
2361 #endif /* _NO_EXTENSIONS */
2362                 0
2363         };
2364
2365         TCHAR ext_buffer[_MAX_EXT];
2366         const LPCTSTR* p;
2367         LPCTSTR s;
2368         LPTSTR d;
2369
2370         for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
2371                 d++;
2372
2373         for(p=executable_extensions; *p; p++)
2374                 if (!_tcscmp(ext_buffer, *p))
2375                         return 1;
2376
2377         return 0;
2378 }
2379
2380 static int is_registered_type(LPCTSTR ext)
2381 {
2382         /* TODO */
2383
2384         return 1;
2385 }
2386
2387
2388 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
2389 {
2390         TCHAR buffer[BUFFER_LEN];
2391         DWORD attrs;
2392         int visible_cols = pane->visible_cols;
2393         COLORREF bkcolor, textcolor;
2394         RECT focusRect = dis->rcItem;
2395         HBRUSH hbrush;
2396         enum IMAGE img;
2397         int img_pos, cx;
2398         int col = 0;
2399
2400         if (entry) {
2401                 attrs = entry->data.dwFileAttributes;
2402
2403                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
2404                         if (entry->data.cFileName[0]==TEXT('.') && entry->data.cFileName[1]==TEXT('.')
2405                                         && entry->data.cFileName[2]==TEXT('\0'))
2406                                 img = IMG_FOLDER_UP;
2407 #ifndef _NO_EXTENSIONS
2408                         else if (entry->data.cFileName[0]==TEXT('.') && entry->data.cFileName[1]==TEXT('\0'))
2409                                 img = IMG_FOLDER_CUR;
2410 #endif
2411                         else if (
2412 #ifdef _NO_EXTENSIONS
2413                                          entry->expanded ||
2414 #endif
2415                                          (pane->treePane && (dis->itemState&ODS_FOCUS)))
2416                                 img = IMG_OPEN_FOLDER;
2417                         else
2418                                 img = IMG_FOLDER;
2419                 } else {
2420                         LPCTSTR ext = _tcsrchr(entry->data.cFileName, '.');
2421                         if (!ext)
2422                                 ext = TEXT("");
2423
2424                         if (is_exe_file(ext))
2425                                 img = IMG_EXECUTABLE;
2426                         else if (is_registered_type(ext))
2427                                 img = IMG_DOCUMENT;
2428                         else
2429                                 img = IMG_FILE;
2430                 }
2431         } else {
2432                 attrs = 0;
2433                 img = IMG_NONE;
2434         }
2435
2436         if (pane->treePane) {
2437                 if (entry) {
2438                         img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+Globals.spaceSize.cx);
2439
2440                         if (calcWidthCol == -1) {
2441                                 int x;
2442                                 int y = dis->rcItem.top + IMAGE_HEIGHT/2;
2443                                 Entry* up;
2444                                 RECT rt_clip;
2445                                 HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
2446                                 HRGN hrgn;
2447
2448                                 rt_clip.left   = dis->rcItem.left;
2449                                 rt_clip.top    = dis->rcItem.top;
2450                                 rt_clip.right  = dis->rcItem.left+pane->widths[col];
2451                                 rt_clip.bottom = dis->rcItem.bottom;
2452
2453                                 hrgn = CreateRectRgnIndirect(&rt_clip);
2454
2455                                 if (!GetClipRgn(dis->hDC, hrgn_org)) {
2456                                         DeleteObject(hrgn_org);
2457                                         hrgn_org = 0;
2458                                 }
2459
2460                                 /* HGDIOBJ holdPen = SelectObject(dis->hDC, GetStockObject(BLACK_PEN)); */
2461                                 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
2462                                 DeleteObject(hrgn);
2463
2464                                 if ((up=entry->up) != NULL) {
2465                                         MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
2466                                         LineTo(dis->hDC, img_pos-2, y);
2467
2468                                         x = img_pos - IMAGE_WIDTH/2;
2469
2470                                         do {
2471                                                 x -= IMAGE_WIDTH+Globals.spaceSize.cx;
2472
2473                                                 if (up->next
2474 #ifndef _LEFT_FILES
2475                                                         && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2476 #endif
2477                                                         ) {
2478                                                         MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
2479                                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
2480                                                 }
2481                                         } while((up=up->up) != NULL);
2482                                 }
2483
2484                                 x = img_pos - IMAGE_WIDTH/2;
2485
2486                                 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
2487                                 LineTo(dis->hDC, x, y);
2488
2489                                 if (entry->next
2490 #ifndef _LEFT_FILES
2491                                         && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
2492 #endif
2493                                         )
2494                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
2495
2496                                 if (entry->down && entry->expanded) {
2497                                         x += IMAGE_WIDTH+Globals.spaceSize.cx;
2498                                         MoveToEx(dis->hDC, x, dis->rcItem.top+IMAGE_HEIGHT, 0);
2499                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
2500                                 }
2501
2502                                 SelectClipRgn(dis->hDC, hrgn_org);
2503                                 if (hrgn_org) DeleteObject(hrgn_org);
2504                                 /* SelectObject(dis->hDC, holdPen); */
2505                         } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
2506                                 int right = img_pos + IMAGE_WIDTH - Globals.spaceSize.cx;
2507
2508                                 if (right > pane->widths[col])
2509                                         pane->widths[col] = right;
2510                         }
2511                 } else  {
2512                         img_pos = dis->rcItem.left;
2513                 }
2514         } else {
2515                 img_pos = dis->rcItem.left;
2516
2517                 if (calcWidthCol==col || calcWidthCol==COLUMNS)
2518                         pane->widths[col] = IMAGE_WIDTH;
2519         }
2520
2521         if (calcWidthCol == -1) {
2522                 focusRect.left = img_pos -2;
2523
2524 #ifdef _NO_EXTENSIONS
2525                 if (pane->treePane && entry) {
2526                         RECT rt = {0};
2527
2528                         DrawText(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2529
2530                         focusRect.right = dis->rcItem.left+pane->positions[col+1]+Globals.spaceSize.cx + rt.right +2;
2531                 }
2532 #else
2533
2534                 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
2535                         textcolor = COLOR_COMPRESSED;
2536                 else
2537 #endif /* _NO_EXTENSIONS */
2538                         textcolor = RGB(0,0,0);
2539
2540                 if (dis->itemState & ODS_FOCUS) {
2541                         textcolor = RGB(255,255,255);
2542                         bkcolor = COLOR_SELECTION;
2543                 } else {
2544                         bkcolor = RGB(255,255,255);
2545                 }
2546
2547                 hbrush = CreateSolidBrush(bkcolor);
2548                 FillRect(dis->hDC, &focusRect, hbrush);
2549                 DeleteObject(hbrush);
2550
2551                 SetBkMode(dis->hDC, TRANSPARENT);
2552                 SetTextColor(dis->hDC, textcolor);
2553
2554                 cx = pane->widths[col];
2555
2556                 if (cx && img!=IMG_NONE) {
2557                         if (cx > IMAGE_WIDTH)
2558                                 cx = IMAGE_WIDTH;
2559
2560 #ifdef _SHELL_FOLDERS
2561                         if (entry->hicon && entry->hicon!=(HICON)-1)
2562                                 DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->hicon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
2563                         else
2564 #endif
2565                                 ImageList_DrawEx(Globals.himl, img, dis->hDC,
2566                                                                  img_pos, dis->rcItem.top, cx,
2567                                                                  IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
2568                 }
2569         }
2570
2571         if (!entry)
2572                 return;
2573
2574 #ifdef _NO_EXTENSIONS
2575         if (img >= IMG_FOLDER_UP)
2576                 return;
2577 #endif
2578
2579         col++;
2580
2581         /* ouput file name */
2582         if (calcWidthCol == -1)
2583                 output_text(pane, dis, col, entry->data.cFileName, 0);
2584         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2585                 calc_width(pane, dis, col, entry->data.cFileName);
2586
2587         col++;
2588
2589 #ifdef _NO_EXTENSIONS
2590   if (!pane->treePane) {
2591 #endif
2592
2593         /* display file size */
2594         if (visible_cols & COL_SIZE) {
2595 #ifdef _NO_EXTENSIONS
2596                 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
2597 #endif
2598                 {
2599                         ULONGLONG size;
2600
2601                         size = ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow;
2602
2603                         _stprintf(buffer, TEXT("%") LONGLONGARG TEXT("d"), size);
2604
2605                         if (calcWidthCol == -1)
2606                                 output_number(pane, dis, col, buffer);
2607                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2608                                 calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
2609                 }
2610
2611                 col++;
2612         }
2613
2614         /* display file date */
2615         if (visible_cols & (COL_DATE|COL_TIME)) {
2616 #ifndef _NO_EXTENSIONS
2617                 format_date(&entry->data.ftCreationTime, buffer, visible_cols);
2618                 if (calcWidthCol == -1)
2619                         output_text(pane, dis, col, buffer, 0);
2620                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2621                         calc_width(pane, dis, col, buffer);
2622                 col++;
2623
2624                 format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
2625                 if (calcWidthCol == -1)
2626                         output_text(pane, dis, col, buffer, 0);
2627                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2628                         calc_width(pane, dis, col, buffer);
2629                 col++;
2630 #endif /* _NO_EXTENSIONS */
2631
2632                 format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
2633                 if (calcWidthCol == -1)
2634                         output_text(pane, dis, col, buffer, 0);
2635                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2636                         calc_width(pane, dis, col, buffer);
2637                 col++;
2638         }
2639
2640 #ifndef _NO_EXTENSIONS
2641         if (entry->bhfi_valid) {
2642             ULONGLONG index = ((ULONGLONG)entry->bhfi.nFileIndexHigh << 32) | entry->bhfi.nFileIndexLow;
2643
2644                 if (visible_cols & COL_INDEX) {
2645                         _stprintf(buffer, TEXT("%") LONGLONGARG TEXT("X"), index);
2646                         if (calcWidthCol == -1)
2647                                 output_text(pane, dis, col, buffer, DT_RIGHT);
2648                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2649                                 calc_width(pane, dis, col, buffer);
2650                         col++;
2651                 }
2652
2653                 if (visible_cols & COL_LINKS) {
2654                         wsprintf(buffer, TEXT("%d"), entry->bhfi.nNumberOfLinks);
2655                         if (calcWidthCol == -1)
2656                                 output_text(pane, dis, col, buffer, DT_CENTER);
2657                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2658                                 calc_width(pane, dis, col, buffer);
2659                         col++;
2660                 }
2661         } else
2662                 col += 2;
2663 #endif /* _NO_EXTENSIONS */
2664
2665         /* show file attributes */
2666         if (visible_cols & COL_ATTRIBUTES) {
2667 #ifdef _NO_EXTENSIONS
2668                 _tcscpy(buffer, TEXT(" \t \t \t \t "));
2669 #else
2670                 _tcscpy(buffer, TEXT(" \t \t \t \t \t \t \t \t \t \t \t "));
2671 #endif
2672
2673                 if (attrs & FILE_ATTRIBUTE_NORMAL)                                      buffer[ 0] = 'N';
2674                 else {
2675                         if (attrs & FILE_ATTRIBUTE_READONLY)                    buffer[ 2] = 'R';
2676                         if (attrs & FILE_ATTRIBUTE_HIDDEN)                              buffer[ 4] = 'H';
2677                         if (attrs & FILE_ATTRIBUTE_SYSTEM)                              buffer[ 6] = 'S';
2678                         if (attrs & FILE_ATTRIBUTE_ARCHIVE)                             buffer[ 8] = 'A';
2679                         if (attrs & FILE_ATTRIBUTE_COMPRESSED)                  buffer[10] = 'C';
2680 #ifndef _NO_EXTENSIONS
2681                         if (attrs & FILE_ATTRIBUTE_DIRECTORY)                   buffer[12] = 'D';
2682                         if (attrs & FILE_ATTRIBUTE_ENCRYPTED)                   buffer[14] = 'E';
2683                         if (attrs & FILE_ATTRIBUTE_TEMPORARY)                   buffer[16] = 'T';
2684                         if (attrs & FILE_ATTRIBUTE_SPARSE_FILE)                 buffer[18] = 'P';
2685                         if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)               buffer[20] = 'Q';
2686                         if (attrs & FILE_ATTRIBUTE_OFFLINE)                             buffer[22] = 'O';
2687                         if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
2688 #endif /* _NO_EXTENSIONS */
2689                 }
2690
2691                 if (calcWidthCol == -1)
2692                         output_tabbed_text(pane, dis, col, buffer);
2693                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2694                         calc_tabbed_width(pane, dis, col, buffer);
2695
2696                 col++;
2697         }
2698
2699 /*TODO
2700         if (flags.security) {
2701                 DWORD rights = get_access_mask();
2702
2703                 tcscpy(buffer, TEXT(" \t \t \t  \t  \t \t \t  \t  \t \t \t "));
2704
2705                 if (rights & FILE_READ_DATA)                    buffer[ 0] = 'R';
2706                 if (rights & FILE_WRITE_DATA)                   buffer[ 2] = 'W';
2707                 if (rights & FILE_APPEND_DATA)                  buffer[ 4] = 'A';
2708                 if (rights & FILE_READ_EA)                              {buffer[6] = 'entry'; buffer[ 7] = 'R';}
2709                 if (rights & FILE_WRITE_EA)                             {buffer[9] = 'entry'; buffer[10] = 'W';}
2710                 if (rights & FILE_EXECUTE)                              buffer[12] = 'X';
2711                 if (rights & FILE_DELETE_CHILD)                 buffer[14] = 'D';
2712                 if (rights & FILE_READ_ATTRIBUTES)              {buffer[16] = 'a'; buffer[17] = 'R';}
2713                 if (rights & FILE_WRITE_ATTRIBUTES)             {buffer[19] = 'a'; buffer[20] = 'W';}
2714                 if (rights & WRITE_DAC)                                 buffer[22] = 'C';
2715                 if (rights & WRITE_OWNER)                               buffer[24] = 'O';
2716                 if (rights & SYNCHRONIZE)                               buffer[26] = 'S';
2717
2718                 output_text(dis, col++, buffer, DT_LEFT, 3, psize);
2719         }
2720
2721         if (flags.description) {
2722                 get_description(buffer);
2723                 output_text(dis, col++, buffer, 0, psize);
2724         }
2725 */
2726
2727 #ifdef _NO_EXTENSIONS
2728   }
2729
2730         /* draw focus frame */
2731         if ((dis->itemState&ODS_FOCUS) && calcWidthCol==-1) {
2732                 /* Currently [04/2000] Wine neither behaves exactly the same */
2733                 /* way as WIN 95 nor like Windows NT... */
2734                 HGDIOBJ lastBrush;
2735                 HPEN lastPen;
2736                 HPEN hpen;
2737
2738                 if (!(GetVersion() & 0x80000000)) {     /* Windows NT? */
2739                         LOGBRUSH lb = {PS_SOLID, RGB(255,255,255)};
2740                         hpen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, 0);
2741                 } else
2742                         hpen = CreatePen(PS_DOT, 0, RGB(255,255,255));
2743
2744                 lastPen = SelectPen(dis->hDC, hpen);
2745                 lastBrush = SelectObject(dis->hDC, GetStockObject(HOLLOW_BRUSH));
2746                 SetROP2(dis->hDC, R2_XORPEN);
2747                 Rectangle(dis->hDC, focusRect.left, focusRect.top, focusRect.right, focusRect.bottom);
2748                 SelectObject(dis->hDC, lastBrush);
2749                 SelectObject(dis->hDC, lastPen);
2750                 DeleteObject(hpen);
2751         }
2752 #endif /* _NO_EXTENSIONS */
2753 }
2754
2755
2756 #ifdef _NO_EXTENSIONS
2757
2758 static void draw_splitbar(HWND hwnd, int x)
2759 {
2760         RECT rt;
2761         HDC hdc = GetDC(hwnd);
2762
2763         GetClientRect(hwnd, &rt);
2764
2765         rt.left = x - SPLIT_WIDTH/2;
2766         rt.right = x + SPLIT_WIDTH/2+1;
2767
2768         InvertRect(hdc, &rt);
2769
2770         ReleaseDC(hwnd, hdc);
2771 }
2772
2773 #endif /* _NO_EXTENSIONS */
2774
2775
2776 #ifndef _NO_EXTENSIONS
2777
2778 static void set_header(Pane* pane)
2779 {
2780         HD_ITEM item;
2781         int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
2782         int i=0, x=0;
2783
2784         item.mask = HDI_WIDTH;
2785         item.cxy = 0;
2786
2787         for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
2788                 x += pane->widths[i];
2789                 Header_SetItem(pane->hwndHeader, i, &item);
2790         }
2791
2792         if (i < COLUMNS) {
2793                 x += pane->widths[i];
2794                 item.cxy = x - scroll_pos;
2795                 Header_SetItem(pane->hwndHeader, i++, &item);
2796
2797                 for(; i<COLUMNS; i++) {
2798                         item.cxy = pane->widths[i];
2799                         x += pane->widths[i];
2800                         Header_SetItem(pane->hwndHeader, i, &item);
2801                 }
2802         }
2803 }
2804
2805 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
2806 {
2807         switch(pnmh->code) {
2808                 case HDN_TRACK:
2809                 case HDN_ENDTRACK: {
2810                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
2811                         int idx = phdn->iItem;
2812                         int dx = phdn->pitem->cxy - pane->widths[idx];
2813                         int i;
2814
2815                         RECT clnt;
2816                         GetClientRect(pane->hwnd, &clnt);
2817
2818                         /* move immediate to simulate HDS_FULLDRAG (for now [04/2000] not realy needed with WINELIB) */
2819                         Header_SetItem(pane->hwndHeader, idx, phdn->pitem);
2820
2821                         pane->widths[idx] += dx;
2822
2823                         for(i=idx; ++i<=COLUMNS; )
2824                                 pane->positions[i] += dx;
2825
2826                         {
2827                                 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
2828                                 RECT rt_scr;
2829                                 RECT rt_clip;
2830
2831                                 rt_scr.left   = pane->positions[idx+1]-scroll_pos;
2832                                 rt_scr.top    = 0;
2833                                 rt_scr.right  = clnt.right;
2834                                 rt_scr.bottom = clnt.bottom;
2835
2836                                 rt_clip.left   = pane->positions[idx]-scroll_pos;
2837                                 rt_clip.top    = 0;
2838                                 rt_clip.right  = clnt.right;
2839                                 rt_clip.bottom = clnt.bottom;
2840
2841                                 if (rt_scr.left < 0) rt_scr.left = 0;
2842                                 if (rt_clip.left < 0) rt_clip.left = 0;
2843
2844                                 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
2845
2846                                 rt_clip.right = pane->positions[idx+1];
2847                                 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
2848
2849                                 if (pnmh->code == HDN_ENDTRACK) {
2850                                         ListBox_SetHorizontalExtent(pane->hwnd, pane->positions[COLUMNS]);
2851
2852                                         if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
2853                                                 set_header(pane);
2854                                 }
2855                         }
2856
2857                         return FALSE;
2858                 }
2859
2860                 case HDN_DIVIDERDBLCLICK: {
2861                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
2862                         HD_ITEM item;
2863
2864                         calc_single_width(pane, phdn->iItem);
2865                         item.mask = HDI_WIDTH;
2866                         item.cxy = pane->widths[phdn->iItem];
2867
2868                         Header_SetItem(pane->hwndHeader, phdn->iItem, &item);
2869                         InvalidateRect(pane->hwnd, 0, TRUE);
2870                         break;}
2871         }
2872
2873         return 0;
2874 }
2875
2876 #endif /* _NO_EXTENSIONS */
2877
2878
2879 static void scan_entry(ChildWnd* child, Entry* entry, HWND hwnd)
2880 {
2881         TCHAR path[MAX_PATH];
2882         int idx = ListBox_GetCurSel(child->left.hwnd);
2883         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
2884
2885         /* delete sub entries in left pane */
2886         for(;;) {
2887                 LRESULT res = ListBox_GetItemData(child->left.hwnd, idx+1);
2888                 Entry* sub = (Entry*) res;
2889
2890                 if (res==LB_ERR || !sub || sub->level<=entry->level)
2891                         break;
2892
2893                 ListBox_DeleteString(child->left.hwnd, idx+1);
2894         }
2895
2896         /* empty right pane */
2897         ListBox_ResetContent(child->right.hwnd);
2898
2899         /* release memory */
2900         free_entries(entry);
2901
2902         /* read contents from disk */
2903 #ifdef _SHELL_FOLDERS
2904         if (entry->etype == ET_SHELL)
2905         {
2906                 read_directory(entry, NULL, child->sortOrder, hwnd);
2907         }
2908         else
2909 #endif
2910         {
2911                 get_path(entry, path);
2912                 read_directory(entry, path, child->sortOrder, hwnd);
2913         }
2914
2915         /* insert found entries in right pane */
2916         insert_entries(&child->right, entry->down, -1);
2917         calc_widths(&child->right, FALSE);
2918 #ifndef _NO_EXTENSIONS
2919         set_header(&child->right);
2920 #endif
2921
2922         child->header_wdths_ok = FALSE;
2923
2924         SetCursor(old_cursor);
2925 }
2926
2927
2928 /* expand a directory entry */
2929
2930 static BOOL expand_entry(ChildWnd* child, Entry* dir)
2931 {
2932         int idx;
2933         Entry* p;
2934
2935         if (!dir || dir->expanded || !dir->down)
2936                 return FALSE;
2937
2938         p = dir->down;
2939
2940         if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
2941                 p = p->next;
2942
2943                 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
2944                                 p->data.cFileName[2]=='\0' && p->next)
2945                         p = p->next;
2946         }
2947
2948         /* no subdirectories ? */
2949         if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2950                 return FALSE;
2951
2952         idx = ListBox_FindItemData(child->left.hwnd, 0, dir);
2953
2954         dir->expanded = TRUE;
2955
2956         /* insert entries in left pane */
2957         insert_entries(&child->left, p, idx);
2958
2959         if (!child->header_wdths_ok) {
2960                 if (calc_widths(&child->left, FALSE)) {
2961 #ifndef _NO_EXTENSIONS
2962                         set_header(&child->left);
2963 #endif
2964
2965                         child->header_wdths_ok = TRUE;
2966                 }
2967         }
2968
2969         return TRUE;
2970 }
2971
2972
2973 static void collapse_entry(Pane* pane, Entry* dir)
2974 {
2975         int idx = ListBox_FindItemData(pane->hwnd, 0, dir);
2976
2977         ShowWindow(pane->hwnd, SW_HIDE);
2978
2979         /* hide sub entries */
2980         for(;;) {
2981                 LRESULT res = ListBox_GetItemData(pane->hwnd, idx+1);
2982                 Entry* sub = (Entry*) res;
2983
2984                 if (res==LB_ERR || !sub || sub->level<=dir->level)
2985                         break;
2986
2987                 ListBox_DeleteString(pane->hwnd, idx+1);
2988         }
2989
2990         dir->expanded = FALSE;
2991
2992         ShowWindow(pane->hwnd, SW_SHOW);
2993 }
2994
2995
2996 static void set_curdir(ChildWnd* child, Entry* entry, HWND hwnd)
2997 {
2998         TCHAR path[MAX_PATH];
2999
3000         path[0] = '\0';
3001
3002         child->left.cur = entry;
3003         child->right.root = entry->down? entry->down: entry;
3004         child->right.cur = entry;
3005
3006         if (!entry->scanned)
3007                 scan_entry(child, entry, hwnd);
3008         else {
3009                 ListBox_ResetContent(child->right.hwnd);
3010                 insert_entries(&child->right, entry->down, -1);
3011                 calc_widths(&child->right, FALSE);
3012 #ifndef _NO_EXTENSIONS
3013                 set_header(&child->right);
3014 #endif
3015         }
3016
3017         get_path(entry, path);
3018         lstrcpy(child->path, path);
3019
3020         if (child->hwnd)        /* only change window title, if the window already exists */
3021                 SetWindowText(child->hwnd, path);
3022
3023         if (path[0])
3024                 SetCurrentDirectory(path);
3025 }
3026
3027
3028 BOOL launch_file(HWND hwnd, LPCTSTR cmd, UINT nCmdShow)
3029 {
3030         HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3031
3032         if ((int)hinst <= 32) {
3033                 display_error(hwnd, GetLastError());
3034                 return FALSE;
3035         }
3036
3037         return TRUE;
3038 }
3039
3040 #ifdef UNICODE
3041 BOOL launch_fileA(HWND hwnd, LPSTR cmd, UINT nCmdShow)
3042 {
3043         HINSTANCE hinst = ShellExecuteA(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3044
3045         if ((int)hinst <= 32) {
3046                 display_error(hwnd, GetLastError());
3047                 return FALSE;
3048         }
3049
3050         return TRUE;
3051 }
3052 #endif
3053
3054
3055 BOOL launch_entry(Entry* entry, HWND hwnd, UINT nCmdShow)
3056 {
3057         TCHAR cmd[MAX_PATH];
3058
3059 #ifdef _SHELL_FOLDERS
3060         if (entry->etype == ET_SHELL) {
3061                 BOOL ret = TRUE;
3062
3063                 SHELLEXECUTEINFO shexinfo;
3064
3065                 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
3066                 shexinfo.fMask = SEE_MASK_IDLIST;
3067                 shexinfo.hwnd = hwnd;
3068                 shexinfo.nShow = nCmdShow;
3069                 shexinfo.lpIDList = get_to_absolute_pidl(entry, hwnd);
3070
3071                 if (!ShellExecuteEx(&shexinfo)) {
3072                         display_error(hwnd, GetLastError());
3073                         ret = FALSE;
3074                 }
3075
3076                 if (shexinfo.lpIDList != entry->pidl)
3077                         (*Globals.iMalloc->lpVtbl->Free)(Globals.iMalloc, shexinfo.lpIDList);
3078
3079                 return ret;
3080         }
3081 #endif
3082
3083         get_path(entry, cmd);
3084
3085          /* start program, open document... */
3086         return launch_file(hwnd, cmd, nCmdShow);
3087 }
3088
3089
3090 static void activate_entry(ChildWnd* child, Pane* pane, HWND hwnd)
3091 {
3092         Entry* entry = pane->cur;
3093
3094         if (!entry)
3095                 return;
3096
3097         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3098                 int scanned_old = entry->scanned;
3099
3100                 if (!scanned_old)
3101                         scan_entry(child, entry, hwnd);
3102
3103 #ifndef _NO_EXTENSIONS
3104                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3105                         return;
3106 #endif
3107
3108                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
3109                         entry = child->left.cur->up;
3110                         collapse_entry(&child->left, entry);
3111                         goto focus_entry;
3112                 } else if (entry->expanded)
3113                         collapse_entry(pane, child->left.cur);
3114                 else {
3115                         expand_entry(child, child->left.cur);
3116
3117                         if (!pane->treePane) focus_entry: {
3118                                 int idx = ListBox_FindItemData(child->left.hwnd, ListBox_GetCurSel(child->left.hwnd), entry);
3119                                 ListBox_SetCurSel(child->left.hwnd, idx);
3120                                 set_curdir(child, entry, hwnd);
3121                         }
3122                 }
3123
3124                 if (!scanned_old) {
3125                         calc_widths(pane, FALSE);
3126
3127 #ifndef _NO_EXTENSIONS
3128                         set_header(pane);
3129 #endif
3130                 }
3131         } else {
3132                 launch_entry(entry, child->hwnd, SW_SHOWNORMAL);
3133         }
3134 }
3135
3136
3137 static BOOL pane_command(Pane* pane, UINT cmd)
3138 {
3139         switch(cmd) {
3140                 case ID_VIEW_NAME:
3141                         if (pane->visible_cols) {
3142                                 pane->visible_cols = 0;
3143                                 calc_widths(pane, TRUE);
3144 #ifndef _NO_EXTENSIONS
3145                                 set_header(pane);
3146 #endif
3147                                 InvalidateRect(pane->hwnd, 0, TRUE);
3148                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
3149                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
3150                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
3151                         }
3152                         break;
3153
3154                 case ID_VIEW_ALL_ATTRIBUTES:
3155                         if (pane->visible_cols != COL_ALL) {
3156                                 pane->visible_cols = COL_ALL;
3157                                 calc_widths(pane, TRUE);
3158 #ifndef _NO_EXTENSIONS
3159                                 set_header(pane);
3160 #endif
3161                                 InvalidateRect(pane->hwnd, 0, TRUE);
3162                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
3163                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
3164                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
3165                         }
3166                         break;
3167
3168 #ifndef _NO_EXTENSIONS
3169                 case ID_PREFERED_SIZES: {
3170                         calc_widths(pane, TRUE);
3171                         set_header(pane);
3172                         InvalidateRect(pane->hwnd, 0, TRUE);
3173                         break;}
3174 #endif
3175
3176                         /* TODO: more command ids... */
3177
3178                 default:
3179                         return FALSE;
3180         }
3181
3182         return TRUE;
3183 }
3184
3185
3186 LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
3187 {
3188         static int last_split;
3189
3190         ChildWnd* child = (ChildWnd*) GetWindowLong(hwnd, GWL_USERDATA);
3191         ASSERT(child);
3192
3193         switch(nmsg) {
3194                 case WM_DRAWITEM: {
3195                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
3196                         Entry* entry = (Entry*) dis->itemData;
3197
3198                         if (dis->CtlID == IDW_TREE_LEFT)
3199                                 draw_item(&child->left, dis, entry, -1);
3200                         else
3201                                 draw_item(&child->right, dis, entry, -1);
3202
3203                         return TRUE;}
3204
3205                 case WM_CREATE:
3206                         InitChildWindow(child);
3207                         break;
3208
3209                 case WM_NCDESTROY:
3210                         free_child_window(child);
3211                         SetWindowLong(hwnd, GWL_USERDATA, 0);
3212                         break;
3213
3214                 case WM_PAINT: {
3215                         PAINTSTRUCT ps;
3216                         HBRUSH lastBrush;
3217                         RECT rt;
3218                         GetClientRect(hwnd, &rt);
3219                         BeginPaint(hwnd, &ps);
3220                         rt.left = child->split_pos-SPLIT_WIDTH/2;
3221                         rt.right = child->split_pos+SPLIT_WIDTH/2+1;
3222                         lastBrush = SelectBrush(ps.hdc, (HBRUSH)GetStockObject(COLOR_SPLITBAR));
3223                         Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
3224                         SelectObject(ps.hdc, lastBrush);
3225 #ifdef _NO_EXTENSIONS
3226                         rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
3227                         FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
3228 #endif
3229                         EndPaint(hwnd, &ps);
3230                         break;}
3231
3232                 case WM_SETCURSOR:
3233                         if (LOWORD(lparam) == HTCLIENT) {
3234                                 POINT pt;
3235                                 GetCursorPos(&pt);
3236                                 ScreenToClient(hwnd, &pt);
3237
3238                                 if (pt.x>=child->split_pos-SPLIT_WIDTH/2 && pt.x<child->split_pos+SPLIT_WIDTH/2+1) {
3239                                         SetCursor(LoadCursor(0, IDC_SIZEWE));
3240                                         return TRUE;
3241                                 }
3242                         }
3243                         goto def;
3244
3245                 case WM_LBUTTONDOWN: {
3246                         RECT rt;
3247                         int x = LOWORD(lparam);
3248
3249                         GetClientRect(hwnd, &rt);
3250
3251                         if (x>=child->split_pos-SPLIT_WIDTH/2 && x<child->split_pos+SPLIT_WIDTH/2+1) {
3252                                 last_split = child->split_pos;
3253 #ifdef _NO_EXTENSIONS
3254                                 draw_splitbar(hwnd, last_split);
3255 #endif
3256                                 SetCapture(hwnd);
3257                         }
3258
3259                         break;}
3260
3261                 case WM_LBUTTONUP:
3262                         if (GetCapture() == hwnd) {
3263 #ifdef _NO_EXTENSIONS
3264                                 RECT rt;
3265                                 int x = LOWORD(lparam);
3266                                 draw_splitbar(hwnd, last_split);
3267                                 last_split = -1;
3268                                 GetClientRect(hwnd, &rt);
3269                                 child->split_pos = x;
3270                                 resize_tree(child, rt.right, rt.bottom);
3271 #endif
3272                                 ReleaseCapture();
3273                         }
3274                         break;
3275
3276 #ifdef _NO_EXTENSIONS
3277                 case WM_CAPTURECHANGED:
3278                         if (GetCapture()==hwnd && last_split>=0)
3279                                 draw_splitbar(hwnd, last_split);
3280                         break;
3281 #endif
3282
3283                 case WM_KEYDOWN:
3284                         if (wparam == VK_ESCAPE)
3285                                 if (GetCapture() == hwnd) {
3286                                         RECT rt;
3287 #ifdef _NO_EXTENSIONS
3288                                         draw_splitbar(hwnd, last_split);
3289 #else
3290                                         child->split_pos = last_split;
3291 #endif
3292                                         GetClientRect(hwnd, &rt);
3293                                         resize_tree(child, rt.right, rt.bottom);
3294                                         last_split = -1;
3295                                         ReleaseCapture();
3296                                         SetCursor(LoadCursor(0, IDC_ARROW));
3297                                 }
3298                         break;
3299
3300                 case WM_MOUSEMOVE:
3301                         if (GetCapture() == hwnd) {
3302                                 RECT rt;
3303                                 int x = LOWORD(lparam);
3304
3305 #ifdef _NO_EXTENSIONS
3306                                 HDC hdc = GetDC(hwnd);
3307                                 GetClientRect(hwnd, &rt);
3308
3309                                 rt.left = last_split-SPLIT_WIDTH/2;
3310                                 rt.right = last_split+SPLIT_WIDTH/2+1;
3311                                 InvertRect(hdc, &rt);
3312
3313                                 last_split = x;
3314                                 rt.left = x-SPLIT_WIDTH/2;
3315                                 rt.right = x+SPLIT_WIDTH/2+1;
3316                                 InvertRect(hdc, &rt);
3317
3318                                 ReleaseDC(hwnd, hdc);
3319 #else
3320                                 GetClientRect(hwnd, &rt);
3321
3322                                 if (x>=0 && x<rt.right) {
3323                                         child->split_pos = x;
3324                                         resize_tree(child, rt.right, rt.bottom);
3325                                         rt.left = x-SPLIT_WIDTH/2;
3326                                         rt.right = x+SPLIT_WIDTH/2+1;
3327                                         InvalidateRect(hwnd, &rt, FALSE);
3328                                         UpdateWindow(child->left.hwnd);
3329                                         UpdateWindow(hwnd);
3330                                         UpdateWindow(child->right.hwnd);
3331                                 }
3332 #endif
3333                         }
3334                         break;
3335
3336 #ifndef _NO_EXTENSIONS
3337                 case WM_GETMINMAXINFO:
3338                         DefMDIChildProc(hwnd, nmsg, wparam, lparam);
3339
3340                         {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
3341
3342                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
3343                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
3344                         break;}
3345 #endif /* _NO_EXTENSIONS */
3346
3347                 case WM_SETFOCUS:
3348                         SetCurrentDirectory(child->path);
3349                         SetFocus(child->focus_pane? child->right.hwnd: child->left.hwnd);
3350                         break;
3351
3352                 case WM_DISPATCH_COMMAND: {
3353                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
3354
3355                         switch(LOWORD(wparam)) {
3356                                 case ID_WINDOW_NEW: {
3357                                         ChildWnd* new_child = alloc_child_window(child->path, NULL, hwnd);
3358
3359                                         if (!create_child_window(new_child))
3360                                                 free(new_child);
3361
3362                                         break;}
3363
3364                                 case ID_REFRESH:
3365                                         scan_entry(child, pane->cur, hwnd);
3366                                         break;
3367
3368                                 case ID_ACTIVATE:
3369                                         activate_entry(child, pane, hwnd);
3370                                         break;
3371
3372                                 default:
3373                                         return pane_command(pane, LOWORD(wparam));
3374                         }
3375
3376                         return TRUE;}
3377
3378                 case WM_COMMAND: {
3379                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
3380
3381                         switch(HIWORD(wparam)) {
3382                                 case LBN_SELCHANGE: {
3383                                         int idx = ListBox_GetCurSel(pane->hwnd);
3384                                         Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, idx);
3385
3386                                         if (pane == &child->left)
3387                                                 set_curdir(child, entry, hwnd);
3388                                         else
3389                                                 pane->cur = entry;
3390                                         break;}
3391
3392                                 case LBN_DBLCLK:
3393                                         activate_entry(child, pane, hwnd);
3394                                         break;
3395                         }
3396                         break;}
3397
3398 #ifndef _NO_EXTENSIONS
3399                 case WM_NOTIFY: {
3400                         NMHDR* pnmh = (NMHDR*) lparam;
3401                         return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &child->left: &child->right, pnmh);}
3402 #endif
3403
3404                 case WM_SIZE:
3405                         if (wparam != SIZE_MINIMIZED)
3406                                 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
3407                         /* fall through */
3408
3409                 default: def:
3410                         return DefMDIChildProc(hwnd, nmsg, wparam, lparam);
3411         }
3412
3413         return 0;
3414 }
3415
3416
3417 LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
3418 {
3419         ChildWnd* child = (ChildWnd*) GetWindowLong(GetParent(hwnd), GWL_USERDATA);
3420         Pane* pane = (Pane*) GetWindowLong(hwnd, GWL_USERDATA);
3421         ASSERT(child);
3422
3423         switch(nmsg) {
3424 #ifndef _NO_EXTENSIONS
3425                 case WM_HSCROLL:
3426                         set_header(pane);
3427                         break;
3428 #endif
3429
3430                 case WM_SETFOCUS:
3431                         child->focus_pane = pane==&child->right? 1: 0;
3432                         ListBox_SetSel(hwnd, TRUE, 1);
3433                         /*TODO: check menu items */
3434                         break;
3435
3436                 case WM_KEYDOWN:
3437                         if (wparam == VK_TAB) {
3438                                 /*TODO: SetFocus(Globals.hdrivebar) */
3439                                 SetFocus(child->focus_pane? child->left.hwnd: child->right.hwnd);
3440                         }
3441         }
3442
3443         return CallWindowProc(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
3444 }
3445
3446
3447 static void InitInstance(HINSTANCE hinstance)
3448 {
3449         WNDCLASSEX wcFrame;
3450         WNDCLASS wcChild;
3451         ATOM hChildClass;
3452
3453         INITCOMMONCONTROLSEX icc = {
3454                 sizeof(INITCOMMONCONTROLSEX),
3455                 ICC_BAR_CLASSES
3456         };
3457
3458         HDC hdc = GetDC(0);
3459
3460         setlocale(LC_COLLATE, "");      /* set collating rules to local settings for compareName */
3461
3462         InitCommonControlsEx(&icc);
3463
3464
3465         /* register frame window class */
3466
3467         wcFrame.cbSize        = sizeof(WNDCLASSEX);
3468         wcFrame.style         = 0;
3469         wcFrame.lpfnWndProc   = FrameWndProc;
3470         wcFrame.cbClsExtra    = 0;
3471         wcFrame.cbWndExtra    = 0;
3472         wcFrame.hInstance     = hinstance;
3473         wcFrame.hIcon         = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_WINEFILE));
3474         wcFrame.hCursor       = LoadCursor(0, IDC_ARROW);
3475         wcFrame.hbrBackground = 0;
3476         wcFrame.lpszMenuName  = 0;
3477         wcFrame.lpszClassName = WINEFILEFRAME;
3478         wcFrame.hIconSm       = (HICON)LoadImage(hinstance,
3479                                                                                          MAKEINTRESOURCE(IDI_WINEFILE),
3480                                                                                          IMAGE_ICON,
3481                                                                                          GetSystemMetrics(SM_CXSMICON),
3482                                                                                          GetSystemMetrics(SM_CYSMICON),
3483                                                                                          LR_SHARED);
3484
3485         Globals.hframeClass = RegisterClassEx(&wcFrame);
3486
3487
3488         /* register tree windows class */
3489
3490         wcChild.style         = CS_CLASSDC|CS_DBLCLKS|CS_VREDRAW;
3491         wcChild.lpfnWndProc   = ChildWndProc;
3492         wcChild.cbClsExtra    = 0;
3493         wcChild.cbWndExtra    = 0;
3494         wcChild.hInstance     = hinstance;
3495         wcChild.hIcon         = 0;
3496         wcChild.hCursor       = LoadCursor(0, IDC_ARROW);
3497         wcChild.hbrBackground = 0;
3498         wcChild.lpszMenuName  = 0;
3499         wcChild.lpszClassName = WINEFILETREE;
3500
3501         hChildClass = RegisterClass(&wcChild);
3502
3503
3504         Globals.haccel = LoadAccelerators(hinstance, MAKEINTRESOURCE(IDA_WINEFILE));
3505
3506         Globals.hfont = CreateFont(-MulDiv(8,GetDeviceCaps(hdc,LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, TEXT("MS Sans Serif"));
3507
3508         ReleaseDC(0, hdc);
3509
3510         Globals.hInstance = hinstance;
3511
3512 #ifdef _SHELL_FOLDERS
3513         CoInitialize(NULL);
3514         CoGetMalloc(MEMCTX_TASK, &Globals.iMalloc);
3515         SHGetDesktopFolder(&Globals.iDesktop);
3516         Globals.cfStrFName = RegisterClipboardFormat(CFSTR_FILENAME);
3517 #endif
3518 }
3519
3520
3521 void show_frame(HWND hwndParent, int cmdshow)
3522 {
3523         TCHAR path[MAX_PATH];
3524         ChildWnd* child;
3525         HMENU hMenuFrame, hMenuWindow;
3526
3527         CLIENTCREATESTRUCT ccs;
3528
3529         if (Globals.hMainWnd)
3530                 return;
3531
3532         Globals.hwndParent = hwndParent;
3533
3534         hMenuFrame = LoadMenu(Globals.hInstance, MAKEINTRESOURCE(IDM_WINEFILE));
3535         hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
3536
3537         Globals.hMenuFrame = hMenuFrame;
3538         Globals.hMenuView = GetSubMenu(hMenuFrame, 3);
3539         Globals.hMenuOptions = GetSubMenu(hMenuFrame, 4);
3540
3541         ccs.hWindowMenu  = hMenuWindow;
3542         ccs.idFirstChild = IDW_FIRST_CHILD;
3543
3544
3545         /* create main window */
3546         Globals.hMainWnd = CreateWindowEx(0, (LPCTSTR)(int)Globals.hframeClass, TEXT("Wine File"), WS_OVERLAPPEDWINDOW,
3547                                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
3548                                         hwndParent, Globals.hMenuFrame, Globals.hInstance, 0/*lpParam*/);
3549
3550
3551         Globals.hmdiclient = CreateWindowEx(0, TEXT("MDICLIENT"), NULL,
3552                                         WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|WS_BORDER,
3553                                         0, 0, 0, 0,
3554                                         Globals.hMainWnd, 0, Globals.hInstance, &ccs);
3555
3556
3557         {
3558                 TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0, 0}, 0, 0};
3559                 int btn = 1;
3560                 PTSTR p;
3561
3562                 Globals.hdrivebar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE|CCS_NOMOVEY|TBSTYLE_LIST,
3563                                         IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
3564                                         1, 16, 13, 16, 13, sizeof(TBBUTTON));
3565                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
3566
3567                 GetLogicalDriveStrings(BUFFER_LEN, Globals.drives);
3568
3569                 drivebarBtn.fsStyle = TBSTYLE_BUTTON;
3570
3571 #ifndef _NO_EXTENSIONS
3572 #ifdef __WINE__
3573                 /* insert unix file system button */
3574                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)TEXT("/\0"));
3575
3576                 drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
3577                 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3578                 drivebarBtn.iString++;
3579 #endif
3580 #ifdef _SHELL_FOLDERS
3581                 /* insert shell namespace button */
3582                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)TEXT("Shell\0"));
3583
3584                 drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3585                 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3586                 drivebarBtn.iString++;
3587 #endif
3588
3589                 /* register windows drive root strings */
3590                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)Globals.drives);
3591 #endif
3592
3593                 drivebarBtn.idCommand = ID_DRIVE_FIRST;
3594
3595                 for(p=Globals.drives; *p; ) {
3596 #ifdef _NO_EXTENSIONS
3597                   /* insert drive letter */
3598                         TCHAR b[3] = {tolower(*p)};
3599                         SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)b);
3600 #endif
3601                         switch(GetDriveType(p)) {
3602                                 case DRIVE_REMOVABLE:   drivebarBtn.iBitmap = 1;        break;
3603                                 case DRIVE_CDROM:               drivebarBtn.iBitmap = 3;        break;
3604                                 case DRIVE_REMOTE:              drivebarBtn.iBitmap = 4;        break;
3605                                 case DRIVE_RAMDISK:             drivebarBtn.iBitmap = 5;        break;
3606                                 default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
3607                         }
3608
3609                         SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3610                         drivebarBtn.idCommand++;
3611                         drivebarBtn.iString++;
3612
3613                         while(*p++);
3614                 }
3615         }
3616
3617         {
3618                 TBBUTTON toolbarBtns[] = {
3619                         {0, 0, 0, TBSTYLE_SEP, {0, 0}, 0, 0},
3620                         {0, ID_WINDOW_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0},
3621                         {1, ID_WINDOW_CASCADE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0},
3622                         {2, ID_WINDOW_TILE_HORZ, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0},
3623                         {3, ID_WINDOW_TILE_VERT, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0, 0}, 0, 0},
3624 /*TODO
3625                         {4, ID_... , TBSTATE_ENABLED, TBSTYLE_BUTTON},
3626                         {5, ID_... , TBSTATE_ENABLED, TBSTYLE_BUTTON},
3627 */              };
3628
3629                 Globals.htoolbar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE,
3630                         IDW_TOOLBAR, 2, Globals.hInstance, IDB_TOOLBAR, toolbarBtns,
3631                         sizeof(toolbarBtns)/sizeof(TBBUTTON), 16, 15, 16, 15, sizeof(TBBUTTON));
3632                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_TOOL_BAR, MF_BYCOMMAND|MF_CHECKED);
3633         }
3634
3635         Globals.hstatusbar = CreateStatusWindow(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
3636         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
3637
3638 /* CreateStatusWindow does not accept WS_BORDER
3639         Globals.hstatusbar = CreateWindowEx(WS_EX_NOPARENTNOTIFY, STATUSCLASSNAME, 0,
3640                                         WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_BORDER|CCS_NODIVIDER, 0,0,0,0,
3641                                         Globals.hMainWnd, (HMENU)IDW_STATUSBAR, hinstance, 0);*/
3642
3643         /*TODO: read paths and window placements from registry */
3644         GetCurrentDirectory(MAX_PATH, path);
3645
3646         ShowWindow(Globals.hMainWnd, cmdshow);
3647
3648 #if defined(_SHELL_FOLDERS) && !defined(__WINE__)
3649          /* Shell Namespace as default: */
3650         child = alloc_child_window(path, get_path_pidl(path,Globals.hMainWnd), Globals.hMainWnd);
3651 #else
3652         child = alloc_child_window(path, NULL, Globals.hMainWnd);
3653 #endif
3654
3655         child->pos.showCmd = SW_SHOWMAXIMIZED;
3656         child->pos.rcNormalPosition.left = 0;
3657         child->pos.rcNormalPosition.top = 0;
3658         child->pos.rcNormalPosition.right = 320;
3659         child->pos.rcNormalPosition.bottom = 280;
3660
3661         if (!create_child_window(child))
3662                 free(child);
3663
3664         SetWindowPlacement(child->hwnd, &child->pos);
3665
3666         Globals.himl = ImageList_LoadBitmap(Globals.hInstance, MAKEINTRESOURCE(IDB_IMAGES), 16, 0, RGB(0,255,0));
3667
3668         Globals.prescan_node = FALSE;
3669
3670         UpdateWindow(Globals.hMainWnd);
3671 }
3672
3673 void ExitInstance()
3674 {
3675 #ifdef _SHELL_FOLDERS
3676         (*Globals.iDesktop->lpVtbl->Release)(Globals.iDesktop);
3677         (*Globals.iMalloc->lpVtbl->Release)(Globals.iMalloc);
3678         CoUninitialize();
3679 #endif
3680
3681         ImageList_Destroy(Globals.himl);
3682 }
3683
3684
3685 /* search for already running win[e]files */
3686
3687 static int g_foundPrevInstance = 0;
3688
3689 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
3690 {
3691         TCHAR cls[128];
3692
3693         GetClassName(hwnd, cls, 128);
3694
3695         if (!lstrcmp(cls, (LPCTSTR)lparam)) {
3696                 g_foundPrevInstance++;
3697                 return FALSE;
3698         }
3699
3700         return TRUE;
3701 }
3702
3703 /* search for window of given class name to allow only one running instance */
3704 int find_window_class(LPCTSTR classname)
3705 {
3706         EnumWindows(EnumWndProc, (LPARAM)classname);
3707
3708         if (g_foundPrevInstance)
3709                 return 1;
3710
3711         return 0;
3712 }
3713
3714
3715 int winefile_main(HINSTANCE hinstance, HWND hwndParent, int cmdshow)
3716 {
3717         MSG msg;
3718
3719         InitInstance(hinstance);
3720
3721 #ifndef _ROS_   /* don't maximize if being called from the ROS desktop */
3722         if (cmdshow == SW_SHOWNORMAL)
3723                 /*TODO: read window placement from registry */
3724                 cmdshow = SW_MAXIMIZE;
3725 #endif
3726
3727         show_frame(hwndParent, cmdshow);
3728
3729         while(GetMessage(&msg, 0, 0, 0)) {
3730                 if (Globals.hmdiclient && TranslateMDISysAccel(Globals.hmdiclient, &msg))
3731                         continue;
3732
3733                 if (Globals.hMainWnd && TranslateAccelerator(Globals.hMainWnd, Globals.haccel, &msg))
3734                         continue;
3735
3736                 TranslateMessage(&msg);
3737                 DispatchMessage(&msg);
3738         }
3739
3740         ExitInstance();
3741
3742         return msg.wParam;
3743 }
3744
3745
3746 #ifndef _ROS_
3747
3748 int APIENTRY WinMain(HINSTANCE hinstance,
3749                                          HINSTANCE previnstance,
3750                                          LPSTR     cmdline,
3751                                          int       cmdshow)
3752 {
3753 #ifdef _NO_EXTENSIONS
3754         if (find_window_class(WINEFILEFRAME))
3755                 return 1;
3756 #endif
3757
3758         winefile_main(hinstance, 0, cmdshow);
3759
3760         return 0;
3761 }
3762
3763 #endif