Use the BTNS_* toolbar button style defines rather than the outdated
[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 = HeapAlloc(GetProcessHeap(), 0, 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                 HeapFree( GetProcessHeap(), 0, array );
1106         }
1107 }
1108
1109
1110 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
1111 {
1112         TCHAR buffer[MAX_PATH];
1113         Entry* entry;
1114         LPCTSTR s;
1115         PTSTR d;
1116
1117 #ifdef _SHELL_FOLDERS
1118         if (dir->etype == ET_SHELL)
1119         {
1120                 read_directory_shell(dir, hwnd);
1121
1122                 if (Globals.prescan_node) {
1123                         s = path;
1124                         d = buffer;
1125
1126                         while(*s)
1127                                 *d++ = *s++;
1128
1129                         *d++ = TEXT('\\');
1130
1131                         for(entry=dir->down; entry; entry=entry->next)
1132                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1133                                         read_directory_shell(entry, hwnd);
1134                                         SortDirectory(entry, sortOrder);
1135                                 }
1136                 }
1137         }
1138         else
1139 #endif
1140 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1141         if (dir->etype == ET_UNIX)
1142         {
1143                 read_directory_unix(dir, path);
1144
1145                 if (Globals.prescan_node) {
1146                         s = path;
1147                         d = buffer;
1148
1149                         while(*s)
1150                                 *d++ = *s++;
1151
1152                         *d++ = TEXT('/');
1153
1154                         for(entry=dir->down; entry; entry=entry->next)
1155                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1156                                         lstrcpy(d, entry->data.cFileName);
1157                                         read_directory_unix(entry, buffer);
1158                                         SortDirectory(entry, sortOrder);
1159                                 }
1160                 }
1161         }
1162         else
1163 #endif
1164         {
1165                 read_directory_win(dir, path);
1166
1167                 if (Globals.prescan_node) {
1168                         s = path;
1169                         d = buffer;
1170
1171                         while(*s)
1172                                 *d++ = *s++;
1173
1174                         *d++ = TEXT('\\');
1175
1176                         for(entry=dir->down; entry; entry=entry->next)
1177                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1178                                         lstrcpy(d, entry->data.cFileName);
1179                                         read_directory_win(entry, buffer);
1180                                         SortDirectory(entry, sortOrder);
1181                                 }
1182                 }
1183         }
1184
1185         SortDirectory(dir, sortOrder);
1186 }
1187
1188
1189 static ChildWnd* alloc_child_window(LPCTSTR path, LPITEMIDLIST pidl, HWND hwnd)
1190 {
1191         TCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
1192         ChildWnd* child = (ChildWnd*) malloc(sizeof(ChildWnd));
1193         Root* root = &child->root;
1194         Entry* entry;
1195
1196         memset(child, 0, sizeof(ChildWnd));
1197
1198         child->left.treePane = TRUE;
1199         child->left.visible_cols = 0;
1200
1201         child->right.treePane = FALSE;
1202 #ifndef _NO_EXTENSIONS
1203         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS;
1204 #else
1205         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES;
1206 #endif
1207
1208         child->pos.length = sizeof(WINDOWPLACEMENT);
1209         child->pos.flags = 0;
1210         child->pos.showCmd = SW_SHOWNORMAL;
1211         child->pos.rcNormalPosition.left = CW_USEDEFAULT;
1212         child->pos.rcNormalPosition.top = CW_USEDEFAULT;
1213         child->pos.rcNormalPosition.right = CW_USEDEFAULT;
1214         child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
1215
1216         child->focus_pane = 0;
1217         child->split_pos = DEFAULT_SPLIT_POS;
1218         child->sortOrder = SORT_NAME;
1219         child->header_wdths_ok = FALSE;
1220
1221         if (path)
1222         {
1223                 lstrcpy(child->path, path);
1224
1225                 _tsplitpath(path, drv, dir, name, ext);
1226         }
1227
1228         root->entry.level = 0;
1229
1230 #ifdef _SHELL_FOLDERS
1231         if (pidl)
1232         {
1233                 root->drive_type = DRIVE_UNKNOWN;
1234                 lstrcpy(drv, TEXT("\\"));
1235                 lstrcpy(root->volname, TEXT("Desktop"));
1236                 root->fs_flags = 0;
1237                 lstrcpy(root->fs, TEXT("Shell"));
1238
1239                 entry = read_tree_shell(root, pidl, child->sortOrder, hwnd);
1240         }
1241         else
1242 #endif
1243 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1244         if (*path == '/')
1245         {
1246                 root->drive_type = GetDriveType(path);
1247
1248                 lstrcat(drv, TEXT("/"));
1249                 lstrcpy(root->volname, TEXT("root fs"));
1250                 root->fs_flags = 0;
1251                 lstrcpy(root->fs, TEXT("unixfs"));
1252
1253                 lstrcpy(root->path, TEXT("/"));
1254                 entry = read_tree_unix(root, path, child->sortOrder, hwnd);
1255         }
1256         else
1257 #endif
1258         {
1259                 root->drive_type = GetDriveType(path);
1260
1261                 lstrcat(drv, TEXT("\\"));
1262                 GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1263
1264                 lstrcpy(root->path, drv);
1265                 entry = read_tree_win(root, path, child->sortOrder, hwnd);
1266         }
1267
1268 #ifdef _SHELL_FOLDERS
1269         if (root->entry.etype == ET_SHELL)
1270                 lstrcpy(root->entry.data.cFileName, TEXT("Desktop"));
1271         else
1272 #endif
1273                 wsprintf(root->entry.data.cFileName, TEXT("%s - %s"), drv, root->fs);
1274
1275         root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1276
1277         child->left.root = &root->entry;
1278         child->right.root = NULL;
1279
1280         set_curdir(child, entry, hwnd);
1281
1282         return child;
1283 }
1284
1285
1286 /* free all memory associated with a child window */
1287 static void free_child_window(ChildWnd* child)
1288 {
1289         free_entries(&child->root.entry);
1290         free(child);
1291 }
1292
1293
1294 /* get full path of specified directory entry */
1295 static void get_path(Entry* dir, PTSTR path)
1296 {
1297         Entry* entry;
1298         int len = 0;
1299         int level = 0;
1300
1301 #ifdef _SHELL_FOLDERS
1302         if (dir->etype == ET_SHELL)
1303         {
1304                 SFGAOF attribs;
1305                 HRESULT hr = S_OK;
1306
1307                 path[0] = TEXT('\0');
1308
1309                 attribs = 0;
1310
1311                 if (dir->folder)
1312                         hr = (*dir->folder->lpVtbl->GetAttributesOf)(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1313
1314                 if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1315                         IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1316
1317                         hr = path_from_pidl(parent, dir->pidl, path, MAX_PATH);
1318                 }
1319         }
1320         else
1321 #endif
1322         {
1323                 for(entry=dir; entry; level++) {
1324                         LPCTSTR name;
1325                         int l;
1326
1327                         {
1328                                 LPCTSTR s;
1329                                 name = entry->data.cFileName;
1330                                 s = name;
1331
1332                                 for(l=0; *s && *s!=TEXT('/') && *s!=TEXT('\\'); s++)
1333                                         l++;
1334                         }
1335
1336                         if (entry->up) {
1337                                 if (l > 0) {
1338                                         memmove(path+l+1, path, len*sizeof(TCHAR));
1339                                         memcpy(path+1, name, l*sizeof(TCHAR));
1340                                         len += l+1;
1341
1342 #ifndef _NO_EXTENSIONS
1343                                         if (entry->etype == ET_UNIX)
1344                                                 path[0] = TEXT('/');
1345                                         else
1346 #endif
1347                                         path[0] = TEXT('\\');
1348                                 }
1349
1350                                 entry = entry->up;
1351                         } else {
1352                                 memmove(path+l, path, len*sizeof(TCHAR));
1353                                 memcpy(path, name, l*sizeof(TCHAR));
1354                                 len += l;
1355                                 break;
1356                         }
1357                 }
1358
1359                 if (!level) {
1360 #ifndef _NO_EXTENSIONS
1361                         if (entry->etype == ET_UNIX)
1362                                 path[len++] = TEXT('/');
1363                         else
1364 #endif
1365                                 path[len++] = TEXT('\\');
1366                 }
1367
1368                 path[len] = TEXT('\0');
1369         }
1370 }
1371
1372
1373 static void resize_frame_rect(HWND hwnd, PRECT prect)
1374 {
1375         int new_top;
1376         RECT rt;
1377
1378         if (IsWindowVisible(Globals.htoolbar)) {
1379                 SendMessage(Globals.htoolbar, WM_SIZE, 0, 0);
1380                 GetClientRect(Globals.htoolbar, &rt);
1381                 prect->top = rt.bottom+3;
1382                 prect->bottom -= rt.bottom+3;
1383         }
1384
1385         if (IsWindowVisible(Globals.hdrivebar)) {
1386                 SendMessage(Globals.hdrivebar, WM_SIZE, 0, 0);
1387                 GetClientRect(Globals.hdrivebar, &rt);
1388                 new_top = --prect->top + rt.bottom+3;
1389                 MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
1390                 prect->top = new_top;
1391                 prect->bottom -= rt.bottom+2;
1392         }
1393
1394         if (IsWindowVisible(Globals.hstatusbar)) {
1395                 int parts[] = {300, 500};
1396
1397                 SendMessage(Globals.hstatusbar, WM_SIZE, 0, 0);
1398                 SendMessage(Globals.hstatusbar, SB_SETPARTS, 2, (LPARAM)&parts);
1399                 GetClientRect(Globals.hstatusbar, &rt);
1400                 prect->bottom -= rt.bottom;
1401         }
1402
1403         MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1404 }
1405
1406 static void resize_frame(HWND hwnd, int cx, int cy)
1407 {
1408         RECT rect;
1409
1410         rect.left   = 0;
1411         rect.top    = 0;
1412         rect.right  = cx;
1413         rect.bottom = cy;
1414
1415         resize_frame_rect(hwnd, &rect);
1416 }
1417
1418 static void resize_frame_client(HWND hwnd)
1419 {
1420         RECT rect;
1421
1422         GetClientRect(hwnd, &rect);
1423
1424         resize_frame_rect(hwnd, &rect);
1425 }
1426
1427
1428 static HHOOK hcbthook;
1429 static ChildWnd* newchild = NULL;
1430
1431 LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
1432 {
1433         if (code==HCBT_CREATEWND && newchild) {
1434                 ChildWnd* child = newchild;
1435                 newchild = NULL;
1436
1437                 child->hwnd = (HWND) wparam;
1438                 SetWindowLong(child->hwnd, GWL_USERDATA, (LPARAM)child);
1439         }
1440
1441         return CallNextHookEx(hcbthook, code, wparam, lparam);
1442 }
1443
1444 static HWND create_child_window(ChildWnd* child)
1445 {
1446         MDICREATESTRUCT mcs;
1447         int idx;
1448
1449         mcs.szClass = WINEFILETREE;
1450         mcs.szTitle = (LPTSTR)child->path;
1451         mcs.hOwner  = Globals.hInstance;
1452         mcs.x       = child->pos.rcNormalPosition.left;
1453         mcs.y       = child->pos.rcNormalPosition.top;
1454         mcs.cx      = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
1455         mcs.cy      = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
1456         mcs.style   = 0;
1457         mcs.lParam  = 0;
1458
1459         hcbthook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
1460
1461         newchild = child;
1462         child->hwnd = (HWND) SendMessage(Globals.hmdiclient, WM_MDICREATE, 0, (LPARAM)&mcs);
1463         if (!child->hwnd) {
1464                 UnhookWindowsHookEx(hcbthook);
1465                 return 0;
1466         }
1467
1468         UnhookWindowsHookEx(hcbthook);
1469
1470         idx = ListBox_FindItemData(child->left.hwnd, ListBox_GetCurSel(child->left.hwnd), child->left.cur);
1471         ListBox_SetCurSel(child->left.hwnd, idx);
1472
1473         return child->hwnd;
1474 }
1475
1476
1477 struct ExecuteDialog {
1478         TCHAR   cmd[MAX_PATH];
1479         int             cmdshow;
1480 };
1481
1482
1483 static BOOL CALLBACK ExecuteDialogWndProg(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1484 {
1485         static struct ExecuteDialog* dlg;
1486
1487         switch(nmsg) {
1488                 case WM_INITDIALOG:
1489                         dlg = (struct ExecuteDialog*) lparam;
1490                         return 1;
1491
1492                 case WM_COMMAND: {
1493                         int id = (int)wparam;
1494
1495                         if (id == IDOK) {
1496                                 GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, MAX_PATH);
1497                                 dlg->cmdshow = Button_GetState(GetDlgItem(hwnd,214))&BST_CHECKED?
1498                                                                                                 SW_SHOWMINIMIZED: SW_SHOWNORMAL;
1499                                 EndDialog(hwnd, id);
1500                         } else if (id == IDCANCEL)
1501                                 EndDialog(hwnd, id);
1502
1503                         return 1;}
1504         }
1505
1506         return 0;
1507 }
1508
1509
1510 #ifndef _NO_EXTENSIONS
1511
1512 static struct FullScreenParameters {
1513         BOOL    mode;
1514         RECT    orgPos;
1515         BOOL    wasZoomed;
1516 } g_fullscreen = {
1517     FALSE,      /* mode */
1518         {0, 0, 0, 0},
1519         FALSE
1520 };
1521
1522 void frame_get_clientspace(HWND hwnd, PRECT prect)
1523 {
1524         RECT rt;
1525
1526         if (!IsIconic(hwnd))
1527                 GetClientRect(hwnd, prect);
1528         else {
1529                 WINDOWPLACEMENT wp;
1530
1531                 GetWindowPlacement(hwnd, &wp);
1532
1533                 prect->left = prect->top = 0;
1534                 prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
1535                                                 2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
1536                 prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
1537                                                 2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
1538                                                 GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
1539         }
1540
1541         if (IsWindowVisible(Globals.htoolbar)) {
1542                 GetClientRect(Globals.htoolbar, &rt);
1543                 prect->top += rt.bottom+2;
1544         }
1545
1546         if (IsWindowVisible(Globals.hdrivebar)) {
1547                 GetClientRect(Globals.hdrivebar, &rt);
1548                 prect->top += rt.bottom+2;
1549         }
1550
1551         if (IsWindowVisible(Globals.hstatusbar)) {
1552                 GetClientRect(Globals.hstatusbar, &rt);
1553                 prect->bottom -= rt.bottom;
1554         }
1555 }
1556
1557 static BOOL toggle_fullscreen(HWND hwnd)
1558 {
1559         RECT rt;
1560
1561         if ((g_fullscreen.mode=!g_fullscreen.mode)) {
1562                 GetWindowRect(hwnd, &g_fullscreen.orgPos);
1563                 g_fullscreen.wasZoomed = IsZoomed(hwnd);
1564
1565                 Frame_CalcFrameClient(hwnd, &rt);
1566                 ClientToScreen(hwnd, (LPPOINT)&rt.left);
1567                 ClientToScreen(hwnd, (LPPOINT)&rt.right);
1568
1569                 rt.left = g_fullscreen.orgPos.left-rt.left;
1570                 rt.top = g_fullscreen.orgPos.top-rt.top;
1571                 rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
1572                 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
1573
1574                 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1575         } else {
1576                 MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
1577                                                         g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
1578                                                         g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
1579
1580                 if (g_fullscreen.wasZoomed)
1581                         ShowWindow(hwnd, WS_MAXIMIZE);
1582         }
1583
1584         return g_fullscreen.mode;
1585 }
1586
1587 static void fullscreen_move(HWND hwnd)
1588 {
1589         RECT rt, pos;
1590         GetWindowRect(hwnd, &pos);
1591
1592         Frame_CalcFrameClient(hwnd, &rt);
1593         ClientToScreen(hwnd, (LPPOINT)&rt.left);
1594         ClientToScreen(hwnd, (LPPOINT)&rt.right);
1595
1596         rt.left = pos.left-rt.left;
1597         rt.top = pos.top-rt.top;
1598         rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
1599         rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
1600
1601         MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1602 }
1603
1604 #endif
1605
1606
1607 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
1608 {
1609         BOOL vis = IsWindowVisible(hchild);
1610
1611         CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
1612
1613         ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
1614
1615 #ifndef _NO_EXTENSIONS
1616         if (g_fullscreen.mode)
1617                 fullscreen_move(hwnd);
1618 #endif
1619
1620         resize_frame_client(hwnd);
1621 }
1622
1623 BOOL activate_drive_window(LPCTSTR path)
1624 {
1625         TCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
1626         HWND child_wnd;
1627
1628         _tsplitpath(path, drv1, 0, 0, 0);
1629
1630         /* search for a already open window for the same drive */
1631         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
1632                 ChildWnd* child = (ChildWnd*) GetWindowLong(child_wnd, GWL_USERDATA);
1633
1634                 if (child) {
1635                         _tsplitpath(child->root.path, drv2, 0, 0, 0);
1636
1637                         if (!lstrcmpi(drv2, drv1)) {
1638                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
1639
1640                                 if (IsMinimized(child_wnd))
1641                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
1642
1643                                 return TRUE;
1644                         }
1645                 }
1646         }
1647
1648         return FALSE;
1649 }
1650
1651 BOOL activate_fs_window(LPCTSTR filesys)
1652 {
1653         HWND child_wnd;
1654
1655         /* search for a already open window of the given file system name */
1656         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
1657                 ChildWnd* child = (ChildWnd*) GetWindowLong(child_wnd, GWL_USERDATA);
1658
1659                 if (child) {
1660                         if (!lstrcmpi(child->root.fs, filesys)) {
1661                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
1662
1663                                 if (IsMinimized(child_wnd))
1664                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
1665
1666                                 return TRUE;
1667                         }
1668                 }
1669         }
1670
1671         return FALSE;
1672 }
1673
1674 LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1675 {
1676         switch(nmsg) {
1677                 case WM_CLOSE:
1678                         DestroyWindow(hwnd);
1679
1680                          /* clear handle variables */
1681                         Globals.hMenuFrame = 0;
1682                         Globals.hMenuView = 0;
1683                         Globals.hMenuOptions = 0;
1684                         Globals.hMainWnd = 0;
1685                         Globals.hmdiclient = 0;
1686                         Globals.hdrivebar = 0;
1687                         break;
1688
1689                 case WM_DESTROY:
1690                          /* don't exit desktop when closing file manager window */
1691                         if (!Globals.hwndParent)
1692                                 PostQuitMessage(0);
1693                         break;
1694
1695                 case WM_COMMAND: {
1696                         UINT cmd = LOWORD(wparam);
1697                         HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
1698
1699                         if (SendMessage(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
1700                                 break;
1701
1702                         if (cmd>=ID_DRIVE_FIRST && cmd<=ID_DRIVE_FIRST+0xFF) {
1703                                 TCHAR drv[_MAX_DRIVE], path[MAX_PATH];
1704                                 ChildWnd* child;
1705                                 LPCTSTR root = Globals.drives;
1706                                 int i;
1707
1708                                 for(i=cmd-ID_DRIVE_FIRST; i--; root++)
1709                                         while(*root)
1710                                                 root++;
1711
1712                                 if (activate_drive_window(root))
1713                                         return 0;
1714
1715                                 _tsplitpath(root, drv, 0, 0, 0);
1716
1717                                 if (!SetCurrentDirectory(drv)) {
1718                                         display_error(hwnd, GetLastError());
1719                                         return 0;
1720                                 }
1721
1722                                 GetCurrentDirectory(MAX_PATH, path); /*TODO: store last directory per drive */
1723                                 child = alloc_child_window(path, NULL, hwnd);
1724
1725                                 if (!create_child_window(child))
1726                                         free(child);
1727                         } else switch(cmd) {
1728                                 case ID_FILE_EXIT:
1729                                         SendMessage(hwnd, WM_CLOSE, 0, 0);
1730                                         break;
1731
1732                                 case ID_WINDOW_NEW: {
1733                                         TCHAR path[MAX_PATH];
1734                                         ChildWnd* child;
1735
1736                                         GetCurrentDirectory(MAX_PATH, path);
1737                                         child = alloc_child_window(path, NULL, hwnd);
1738
1739                                         if (!create_child_window(child))
1740                                                 free(child);
1741                                         break;}
1742
1743                                 case ID_WINDOW_CASCADE:
1744                                         SendMessage(Globals.hmdiclient, WM_MDICASCADE, 0, 0);
1745                                         break;
1746
1747                                 case ID_WINDOW_TILE_HORZ:
1748                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
1749                                         break;
1750
1751                                 case ID_WINDOW_TILE_VERT:
1752                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_VERTICAL, 0);
1753                                         break;
1754
1755                                 case ID_WINDOW_ARRANGE:
1756                                         SendMessage(Globals.hmdiclient, WM_MDIICONARRANGE, 0, 0);
1757                                         break;
1758
1759                                 case ID_VIEW_TOOL_BAR:
1760                                         toggle_child(hwnd, cmd, Globals.htoolbar);
1761                                         break;
1762
1763                                 case ID_VIEW_DRIVE_BAR:
1764                                         toggle_child(hwnd, cmd, Globals.hdrivebar);
1765                                         break;
1766
1767                                 case ID_VIEW_STATUSBAR:
1768                                         toggle_child(hwnd, cmd, Globals.hstatusbar);
1769                                         break;
1770
1771                                 case ID_EXECUTE: {
1772                                         struct ExecuteDialog dlg;
1773
1774                                         memset(&dlg, 0, sizeof(struct ExecuteDialog));
1775
1776                                         if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_EXECUTE), hwnd, ExecuteDialogWndProg, (LPARAM)&dlg) == IDOK) {
1777                                                 HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, dlg.cmd/*file*/, NULL/*parameters*/, NULL/*dir*/, dlg.cmdshow);
1778
1779                                                 if ((int)hinst <= 32)
1780                                                         display_error(hwnd, GetLastError());
1781                                         }
1782                                         break;}
1783
1784                                 case ID_HELP:
1785                                         WinHelp(hwnd, TEXT("winfile"), HELP_INDEX, 0);
1786                                         break;
1787
1788 #ifndef _NO_EXTENSIONS
1789                                 case ID_VIEW_FULLSCREEN:
1790                                         CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
1791                                         break;
1792
1793 #ifdef __WINE__
1794                                 case ID_DRIVE_UNIX_FS: {
1795                                         TCHAR path[MAX_PATH];
1796                                         ChildWnd* child;
1797
1798                                         if (activate_fs_window(TEXT("unixfs")))
1799                                                 break;
1800
1801                                         getcwd(path, MAX_PATH);
1802                                         child = alloc_child_window(path, NULL, hwnd);
1803
1804                                         if (!create_child_window(child))
1805                                                 free(child);
1806                                         break;}
1807 #endif
1808 #ifdef _SHELL_FOLDERS
1809                                 case ID_DRIVE_SHELL_NS: {
1810                                         TCHAR path[MAX_PATH];
1811                                         ChildWnd* child;
1812
1813                                         if (activate_fs_window(TEXT("Shell")))
1814                                                 break;
1815
1816                                         GetCurrentDirectory(MAX_PATH, path);
1817                                         child = alloc_child_window(path, get_path_pidl(path,hwnd), hwnd);
1818
1819                                         if (!create_child_window(child))
1820                                                 free(child);
1821                                         break;}
1822 #endif
1823 #endif
1824
1825                                 /*TODO: There are even more menu items! */
1826
1827 #ifndef _NO_EXTENSIONS
1828 #ifdef __WINE__
1829                                 case ID_LICENSE:
1830                                         WineLicense(Globals.hMainWnd);
1831                                         break;
1832
1833                                 case ID_NO_WARRANTY:
1834                                         WineWarranty(Globals.hMainWnd);
1835                                         break;
1836 #endif
1837
1838                                 case ID_ABOUT_WINE:
1839                                         ShellAbout(hwnd, TEXT("WINE"), TEXT("Winefile"), 0);
1840                                         break;
1841
1842                                 case ID_ABOUT:  /*ID_ABOUT_WINE: */
1843                                         ShellAbout(hwnd, TEXT("Winefile"), NULL, 0);
1844                                         break;
1845 #endif  /* _NO_EXTENSIONS */
1846
1847                                 default:
1848                                         /*TODO: if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
1849                                                 STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
1850                                         else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
1851                                                 (cmd<SC_SIZE || cmd>SC_RESTORE))
1852                                                 MessageBox(hwnd, TEXT("Not yet implemented"), TEXT("Winefile"), MB_OK);
1853
1854                                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
1855                         }
1856                         break;}
1857
1858                 case WM_SIZE:
1859                         resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
1860                         break;  /* do not pass message to DefFrameProc */
1861
1862 #ifndef _NO_EXTENSIONS
1863                 case WM_GETMINMAXINFO: {
1864                         LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
1865
1866                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
1867                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
1868                         break;}
1869
1870                 case FRM_CALC_CLIENT:
1871                         frame_get_clientspace(hwnd, (PRECT)lparam);
1872                         return TRUE;
1873 #endif /* _NO_EXTENSIONS */
1874
1875                 default:
1876                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
1877         }
1878
1879         return 0;
1880 }
1881
1882
1883 static const LPTSTR g_pos_names[COLUMNS] = {
1884         TEXT(""),                       /* symbol */
1885         TEXT("Name"),
1886         TEXT("Size"),
1887         TEXT("CDate"),
1888 #ifndef _NO_EXTENSIONS
1889         TEXT("ADate"),
1890         TEXT("MDate"),
1891         TEXT("Index/Inode"),
1892         TEXT("Links"),
1893 #endif /* _NO_EXTENSIONS */
1894         TEXT("Attributes"),
1895 #ifndef _NO_EXTENSIONS
1896         TEXT("Security")
1897 #endif
1898 };
1899
1900 static const int g_pos_align[] = {
1901         0,
1902         HDF_LEFT,       /* Name */
1903         HDF_RIGHT,      /* Size */
1904         HDF_LEFT,       /* CDate */
1905 #ifndef _NO_EXTENSIONS
1906         HDF_LEFT,       /* ADate */
1907         HDF_LEFT,       /* MDate */
1908         HDF_LEFT,       /* Index */
1909         HDF_CENTER,     /* Links */
1910 #endif
1911         HDF_CENTER,     /* Attributes */
1912 #ifndef _NO_EXTENSIONS
1913         HDF_LEFT        /* Security */
1914 #endif
1915 };
1916
1917 static void resize_tree(ChildWnd* child, int cx, int cy)
1918 {
1919         HDWP hdwp = BeginDeferWindowPos(4);
1920         RECT rt;
1921
1922         rt.left   = 0;
1923         rt.top    = 0;
1924         rt.right  = cx;
1925         rt.bottom = cy;
1926
1927         cx = child->split_pos + SPLIT_WIDTH/2;
1928
1929 #ifndef _NO_EXTENSIONS
1930         {
1931                 WINDOWPOS wp;
1932                 HD_LAYOUT hdl;
1933
1934                 hdl.prc   = &rt;
1935                 hdl.pwpos = &wp;
1936
1937                 Header_Layout(child->left.hwndHeader, &hdl);
1938
1939                 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
1940                                                 wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
1941                 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
1942                                                 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
1943         }
1944 #endif /* _NO_EXTENSIONS */
1945
1946         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);
1947         DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
1948
1949         EndDeferWindowPos(hdwp);
1950 }
1951
1952
1953 #ifndef _NO_EXTENSIONS
1954
1955 static HWND create_header(HWND parent, Pane* pane, int id)
1956 {
1957         HD_ITEM hdi;
1958         int idx;
1959
1960         HWND hwnd = CreateWindow(WC_HEADER, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ/*TODO: |HDS_BUTTONS + sort orders*/,
1961                                                                 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
1962         if (!hwnd)
1963                 return 0;
1964
1965         SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), FALSE);
1966
1967         hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
1968
1969         for(idx=0; idx<COLUMNS; idx++) {
1970                 hdi.pszText = g_pos_names[idx];
1971                 hdi.fmt = HDF_STRING | g_pos_align[idx];
1972                 hdi.cxy = pane->widths[idx];
1973                 Header_InsertItem(hwnd, idx, &hdi);
1974         }
1975
1976         return hwnd;
1977 }
1978
1979 #endif /* _NO_EXTENSIONS */
1980
1981
1982 static void init_output(HWND hwnd)
1983 {
1984         TCHAR b[16];
1985         HFONT old_font;
1986         HDC hdc = GetDC(hwnd);
1987
1988         if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, TEXT("1000"), 0, b, 16) > 4)
1989                 Globals.num_sep = b[1];
1990         else
1991                 Globals.num_sep = TEXT('.');
1992
1993         old_font = SelectFont(hdc, Globals.hfont);
1994         GetTextExtentPoint32(hdc, TEXT(" "), 1, &Globals.spaceSize);
1995         SelectFont(hdc, old_font);
1996         ReleaseDC(hwnd, hdc);
1997 }
1998
1999 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
2000
2001
2002 /* calculate prefered width for all visible columns */
2003
2004 static BOOL calc_widths(Pane* pane, BOOL anyway)
2005 {
2006         int col, x, cx, spc=3*Globals.spaceSize.cx;
2007         int entries = ListBox_GetCount(pane->hwnd);
2008         int orgWidths[COLUMNS];
2009         int orgPositions[COLUMNS+1];
2010         HFONT hfontOld;
2011         HDC hdc;
2012         int cnt;
2013
2014         if (!anyway) {
2015                 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2016                 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2017         }
2018
2019         for(col=0; col<COLUMNS; col++)
2020                 pane->widths[col] = 0;
2021
2022         hdc = GetDC(pane->hwnd);
2023         hfontOld = SelectFont(hdc, Globals.hfont);
2024
2025         for(cnt=0; cnt<entries; cnt++) {
2026                 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
2027
2028                 DRAWITEMSTRUCT dis;
2029
2030                 dis.CtlType               = 0;
2031                 dis.CtlID                 = 0;
2032                 dis.itemID                = 0;
2033                 dis.itemAction    = 0;
2034                 dis.itemState     = 0;
2035                 dis.hwndItem      = pane->hwnd;
2036                 dis.hDC                   = hdc;
2037                 dis.rcItem.left   = 0;
2038                 dis.rcItem.top    = 0;
2039                 dis.rcItem.right  = 0;
2040                 dis.rcItem.bottom = 0;
2041                 /*dis.itemData    = 0; */
2042
2043                 draw_item(pane, &dis, entry, COLUMNS);
2044         }
2045
2046         SelectObject(hdc, hfontOld);
2047         ReleaseDC(pane->hwnd, hdc);
2048
2049         x = 0;
2050         for(col=0; col<COLUMNS; col++) {
2051                 pane->positions[col] = x;
2052                 cx = pane->widths[col];
2053
2054                 if (cx) {
2055                         cx += spc;
2056
2057                         if (cx < IMAGE_WIDTH)
2058                                 cx = IMAGE_WIDTH;
2059
2060                         pane->widths[col] = cx;
2061                 }
2062
2063                 x += cx;
2064         }
2065
2066         pane->positions[COLUMNS] = x;
2067
2068         ListBox_SetHorizontalExtent(pane->hwnd, x);
2069
2070         /* no change? */
2071         if (!memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2072                 return FALSE;
2073
2074         /* don't move, if only collapsing an entry */
2075         if (!anyway && pane->widths[0]<orgWidths[0] &&
2076                 !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
2077                 pane->widths[0] = orgWidths[0];
2078                 memcpy(pane->positions, orgPositions, sizeof(orgPositions));
2079
2080                 return FALSE;
2081         }
2082
2083         InvalidateRect(pane->hwnd, 0, TRUE);
2084
2085         return TRUE;
2086 }
2087
2088
2089 /* calculate one prefered column width */
2090
2091 static void calc_single_width(Pane* pane, int col)
2092 {
2093         HFONT hfontOld;
2094         int x, cx;
2095         int entries = ListBox_GetCount(pane->hwnd);
2096         int cnt;
2097         HDC hdc;
2098
2099         pane->widths[col] = 0;
2100
2101         hdc = GetDC(pane->hwnd);
2102         hfontOld = SelectFont(hdc, Globals.hfont);
2103
2104         for(cnt=0; cnt<entries; cnt++) {
2105                 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
2106                 DRAWITEMSTRUCT dis;
2107
2108                 dis.CtlType               = 0;
2109                 dis.CtlID                 = 0;
2110                 dis.itemID                = 0;
2111                 dis.itemAction    = 0;
2112                 dis.itemState     = 0;
2113                 dis.hwndItem      = pane->hwnd;
2114                 dis.hDC                   = hdc;
2115                 dis.rcItem.left   = 0;
2116                 dis.rcItem.top    = 0;
2117                 dis.rcItem.right  = 0;
2118                 dis.rcItem.bottom = 0;
2119                 /*dis.itemData    = 0; */
2120
2121                 draw_item(pane, &dis, entry, col);
2122         }
2123
2124         SelectObject(hdc, hfontOld);
2125         ReleaseDC(pane->hwnd, hdc);
2126
2127         cx = pane->widths[col];
2128
2129         if (cx) {
2130                 cx += 3*Globals.spaceSize.cx;
2131
2132                 if (cx < IMAGE_WIDTH)
2133                         cx = IMAGE_WIDTH;
2134         }
2135
2136         pane->widths[col] = cx;
2137
2138         x = pane->positions[col] + cx;
2139
2140         for(; col<COLUMNS; ) {
2141                 pane->positions[++col] = x;
2142                 x += pane->widths[col];
2143         }
2144
2145         ListBox_SetHorizontalExtent(pane->hwnd, x);
2146 }
2147
2148
2149 /* insert listbox entries after index idx */
2150
2151 static void insert_entries(Pane* pane, Entry* dir, int idx)
2152 {
2153         Entry* entry = dir;
2154
2155         if (!entry)
2156                 return;
2157
2158         ShowWindow(pane->hwnd, SW_HIDE);
2159
2160         for(; entry; entry=entry->next) {
2161 #ifndef _LEFT_FILES
2162                 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2163                         continue;
2164 #endif
2165
2166                 /* don't display entries "." and ".." in the left pane */
2167                 if (pane->treePane && (entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
2168                                 && entry->data.cFileName[0]==TEXT('.'))
2169                         if (
2170 #ifndef _NO_EXTENSIONS
2171                                 entry->data.cFileName[1]==TEXT('\0') ||
2172 #endif
2173                                 (entry->data.cFileName[1]==TEXT('.') && entry->data.cFileName[2]==TEXT('\0')))
2174                                 continue;
2175
2176                 if (idx != -1)
2177                         idx++;
2178
2179                 ListBox_InsertItemData(pane->hwnd, idx, entry);
2180
2181                 if (pane->treePane && entry->expanded)
2182                         insert_entries(pane, entry->down, idx);
2183         }
2184
2185         ShowWindow(pane->hwnd, SW_SHOW);
2186 }
2187
2188
2189 static WNDPROC g_orgTreeWndProc;
2190
2191 static void create_tree_window(HWND parent, Pane* pane, int id, int id_header)
2192 {
2193         static int s_init = 0;
2194         Entry* entry = pane->root;
2195
2196         pane->hwnd = CreateWindow(TEXT("ListBox"), TEXT(""), WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
2197                                                                 LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
2198                                                                 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
2199
2200         SetWindowLong(pane->hwnd, GWL_USERDATA, (LPARAM)pane);
2201         g_orgTreeWndProc = SubclassWindow(pane->hwnd, TreeWndProc);
2202
2203         SendMessage(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2204
2205         /* insert entries into listbox */
2206         if (entry)
2207                 insert_entries(pane, entry, -1);
2208
2209         /* calculate column widths */
2210         if (!s_init) {
2211                 s_init = 1;
2212                 init_output(pane->hwnd);
2213         }
2214
2215         calc_widths(pane, TRUE);
2216
2217 #ifndef _NO_EXTENSIONS
2218         pane->hwndHeader = create_header(parent, pane, id_header);
2219 #endif
2220 }
2221
2222
2223 static void InitChildWindow(ChildWnd* child)
2224 {
2225         create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT);
2226         create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT);
2227 }
2228
2229
2230 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
2231 {
2232         SYSTEMTIME systime;
2233         FILETIME lft;
2234         int len = 0;
2235
2236         *buffer = TEXT('\0');
2237
2238         if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2239                 return;
2240
2241         if (!FileTimeToLocalFileTime(ft, &lft))
2242                 {err: _tcscpy(buffer,TEXT("???")); return;}
2243
2244         if (!FileTimeToSystemTime(&lft, &systime))
2245                 goto err;
2246
2247         if (visible_cols & COL_DATE) {
2248                 len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
2249                 if (!len)
2250                         goto err;
2251         }
2252
2253         if (visible_cols & COL_TIME) {
2254                 if (len)
2255                         buffer[len-1] = ' ';
2256
2257                 buffer[len++] = ' ';
2258
2259                 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
2260                         buffer[len] = TEXT('\0');
2261         }
2262 }
2263
2264
2265 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2266 {
2267         RECT rt = {0, 0, 0, 0};
2268
2269         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2270
2271         if (rt.right > pane->widths[col])
2272                 pane->widths[col] = rt.right;
2273 }
2274
2275 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2276 {
2277         RECT rt = {0, 0, 0, 0};
2278
2279 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
2280         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
2281
2282         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2283         /*FIXME rt (0,0) ??? */
2284
2285         if (rt.right > pane->widths[col])
2286                 pane->widths[col] = rt.right;
2287 }
2288
2289
2290 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str, DWORD flags)
2291 {
2292         int x = dis->rcItem.left;
2293         RECT rt;
2294
2295         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2296         rt.top    = dis->rcItem.top;
2297         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2298         rt.bottom = dis->rcItem.bottom;
2299
2300         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
2301 }
2302
2303 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2304 {
2305         int x = dis->rcItem.left;
2306         RECT rt;
2307
2308         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2309         rt.top    = dis->rcItem.top;
2310         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2311         rt.bottom = dis->rcItem.bottom;
2312
2313 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
2314         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
2315
2316         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2317 }
2318
2319 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2320 {
2321         int x = dis->rcItem.left;
2322         RECT rt;
2323         LPCTSTR s = str;
2324         TCHAR b[128];
2325         LPTSTR d = b;
2326         int pos;
2327
2328         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
2329         rt.top    = dis->rcItem.top;
2330         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
2331         rt.bottom = dis->rcItem.bottom;
2332
2333         if (*s)
2334                 *d++ = *s++;
2335
2336         /* insert number separator characters */
2337         pos = lstrlen(s) % 3;
2338
2339         while(*s)
2340                 if (pos--)
2341                         *d++ = *s++;
2342                 else {
2343                         *d++ = Globals.num_sep;
2344                         pos = 3;
2345                 }
2346
2347         DrawText(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
2348 }
2349
2350
2351 static int is_exe_file(LPCTSTR ext)
2352 {
2353         static const LPCTSTR executable_extensions[] = {
2354                 TEXT("COM"),
2355                 TEXT("EXE"),
2356                 TEXT("BAT"),
2357                 TEXT("CMD"),
2358 #ifndef _NO_EXTENSIONS
2359                 TEXT("CMM"),
2360                 TEXT("BTM"),
2361                 TEXT("AWK"),
2362 #endif /* _NO_EXTENSIONS */
2363                 0
2364         };
2365
2366         TCHAR ext_buffer[_MAX_EXT];
2367         const LPCTSTR* p;
2368         LPCTSTR s;
2369         LPTSTR d;
2370
2371         for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
2372                 d++;
2373
2374         for(p=executable_extensions; *p; p++)
2375                 if (!_tcscmp(ext_buffer, *p))
2376                         return 1;
2377
2378         return 0;
2379 }
2380
2381 static int is_registered_type(LPCTSTR ext)
2382 {
2383         /* TODO */
2384
2385         return 1;
2386 }
2387
2388
2389 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
2390 {
2391         TCHAR buffer[BUFFER_LEN];
2392         DWORD attrs;
2393         int visible_cols = pane->visible_cols;
2394         COLORREF bkcolor, textcolor;
2395         RECT focusRect = dis->rcItem;
2396         HBRUSH hbrush;
2397         enum IMAGE img;
2398         int img_pos, cx;
2399         int col = 0;
2400
2401         if (entry) {
2402                 attrs = entry->data.dwFileAttributes;
2403
2404                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
2405                         if (entry->data.cFileName[0]==TEXT('.') && entry->data.cFileName[1]==TEXT('.')
2406                                         && entry->data.cFileName[2]==TEXT('\0'))
2407                                 img = IMG_FOLDER_UP;
2408 #ifndef _NO_EXTENSIONS
2409                         else if (entry->data.cFileName[0]==TEXT('.') && entry->data.cFileName[1]==TEXT('\0'))
2410                                 img = IMG_FOLDER_CUR;
2411 #endif
2412                         else if (
2413 #ifdef _NO_EXTENSIONS
2414                                          entry->expanded ||
2415 #endif
2416                                          (pane->treePane && (dis->itemState&ODS_FOCUS)))
2417                                 img = IMG_OPEN_FOLDER;
2418                         else
2419                                 img = IMG_FOLDER;
2420                 } else {
2421                         LPCTSTR ext = _tcsrchr(entry->data.cFileName, '.');
2422                         if (!ext)
2423                                 ext = TEXT("");
2424
2425                         if (is_exe_file(ext))
2426                                 img = IMG_EXECUTABLE;
2427                         else if (is_registered_type(ext))
2428                                 img = IMG_DOCUMENT;
2429                         else
2430                                 img = IMG_FILE;
2431                 }
2432         } else {
2433                 attrs = 0;
2434                 img = IMG_NONE;
2435         }
2436
2437         if (pane->treePane) {
2438                 if (entry) {
2439                         img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+Globals.spaceSize.cx);
2440
2441                         if (calcWidthCol == -1) {
2442                                 int x;
2443                                 int y = dis->rcItem.top + IMAGE_HEIGHT/2;
2444                                 Entry* up;
2445                                 RECT rt_clip;
2446                                 HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
2447                                 HRGN hrgn;
2448
2449                                 rt_clip.left   = dis->rcItem.left;
2450                                 rt_clip.top    = dis->rcItem.top;
2451                                 rt_clip.right  = dis->rcItem.left+pane->widths[col];
2452                                 rt_clip.bottom = dis->rcItem.bottom;
2453
2454                                 hrgn = CreateRectRgnIndirect(&rt_clip);
2455
2456                                 if (!GetClipRgn(dis->hDC, hrgn_org)) {
2457                                         DeleteObject(hrgn_org);
2458                                         hrgn_org = 0;
2459                                 }
2460
2461                                 /* HGDIOBJ holdPen = SelectObject(dis->hDC, GetStockObject(BLACK_PEN)); */
2462                                 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
2463                                 DeleteObject(hrgn);
2464
2465                                 if ((up=entry->up) != NULL) {
2466                                         MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
2467                                         LineTo(dis->hDC, img_pos-2, y);
2468
2469                                         x = img_pos - IMAGE_WIDTH/2;
2470
2471                                         do {
2472                                                 x -= IMAGE_WIDTH+Globals.spaceSize.cx;
2473
2474                                                 if (up->next
2475 #ifndef _LEFT_FILES
2476                                                         && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2477 #endif
2478                                                         ) {
2479                                                         MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
2480                                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
2481                                                 }
2482                                         } while((up=up->up) != NULL);
2483                                 }
2484
2485                                 x = img_pos - IMAGE_WIDTH/2;
2486
2487                                 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
2488                                 LineTo(dis->hDC, x, y);
2489
2490                                 if (entry->next
2491 #ifndef _LEFT_FILES
2492                                         && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
2493 #endif
2494                                         )
2495                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
2496
2497                                 if (entry->down && entry->expanded) {
2498                                         x += IMAGE_WIDTH+Globals.spaceSize.cx;
2499                                         MoveToEx(dis->hDC, x, dis->rcItem.top+IMAGE_HEIGHT, 0);
2500                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
2501                                 }
2502
2503                                 SelectClipRgn(dis->hDC, hrgn_org);
2504                                 if (hrgn_org) DeleteObject(hrgn_org);
2505                                 /* SelectObject(dis->hDC, holdPen); */
2506                         } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
2507                                 int right = img_pos + IMAGE_WIDTH - Globals.spaceSize.cx;
2508
2509                                 if (right > pane->widths[col])
2510                                         pane->widths[col] = right;
2511                         }
2512                 } else  {
2513                         img_pos = dis->rcItem.left;
2514                 }
2515         } else {
2516                 img_pos = dis->rcItem.left;
2517
2518                 if (calcWidthCol==col || calcWidthCol==COLUMNS)
2519                         pane->widths[col] = IMAGE_WIDTH;
2520         }
2521
2522         if (calcWidthCol == -1) {
2523                 focusRect.left = img_pos -2;
2524
2525 #ifdef _NO_EXTENSIONS
2526                 if (pane->treePane && entry) {
2527                         RECT rt = {0};
2528
2529                         DrawText(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2530
2531                         focusRect.right = dis->rcItem.left+pane->positions[col+1]+Globals.spaceSize.cx + rt.right +2;
2532                 }
2533 #else
2534
2535                 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
2536                         textcolor = COLOR_COMPRESSED;
2537                 else
2538 #endif /* _NO_EXTENSIONS */
2539                         textcolor = RGB(0,0,0);
2540
2541                 if (dis->itemState & ODS_FOCUS) {
2542                         textcolor = RGB(255,255,255);
2543                         bkcolor = COLOR_SELECTION;
2544                 } else {
2545                         bkcolor = RGB(255,255,255);
2546                 }
2547
2548                 hbrush = CreateSolidBrush(bkcolor);
2549                 FillRect(dis->hDC, &focusRect, hbrush);
2550                 DeleteObject(hbrush);
2551
2552                 SetBkMode(dis->hDC, TRANSPARENT);
2553                 SetTextColor(dis->hDC, textcolor);
2554
2555                 cx = pane->widths[col];
2556
2557                 if (cx && img!=IMG_NONE) {
2558                         if (cx > IMAGE_WIDTH)
2559                                 cx = IMAGE_WIDTH;
2560
2561 #ifdef _SHELL_FOLDERS
2562                         if (entry->hicon && entry->hicon!=(HICON)-1)
2563                                 DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->hicon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
2564                         else
2565 #endif
2566                                 ImageList_DrawEx(Globals.himl, img, dis->hDC,
2567                                                                  img_pos, dis->rcItem.top, cx,
2568                                                                  IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
2569                 }
2570         }
2571
2572         if (!entry)
2573                 return;
2574
2575 #ifdef _NO_EXTENSIONS
2576         if (img >= IMG_FOLDER_UP)
2577                 return;
2578 #endif
2579
2580         col++;
2581
2582         /* ouput file name */
2583         if (calcWidthCol == -1)
2584                 output_text(pane, dis, col, entry->data.cFileName, 0);
2585         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2586                 calc_width(pane, dis, col, entry->data.cFileName);
2587
2588         col++;
2589
2590 #ifdef _NO_EXTENSIONS
2591   if (!pane->treePane) {
2592 #endif
2593
2594         /* display file size */
2595         if (visible_cols & COL_SIZE) {
2596 #ifdef _NO_EXTENSIONS
2597                 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
2598 #endif
2599                 {
2600                         ULONGLONG size;
2601
2602                         size = ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow;
2603
2604                         _stprintf(buffer, TEXT("%") LONGLONGARG TEXT("d"), size);
2605
2606                         if (calcWidthCol == -1)
2607                                 output_number(pane, dis, col, buffer);
2608                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2609                                 calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
2610                 }
2611
2612                 col++;
2613         }
2614
2615         /* display file date */
2616         if (visible_cols & (COL_DATE|COL_TIME)) {
2617 #ifndef _NO_EXTENSIONS
2618                 format_date(&entry->data.ftCreationTime, buffer, visible_cols);
2619                 if (calcWidthCol == -1)
2620                         output_text(pane, dis, col, buffer, 0);
2621                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2622                         calc_width(pane, dis, col, buffer);
2623                 col++;
2624
2625                 format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
2626                 if (calcWidthCol == -1)
2627                         output_text(pane, dis, col, buffer, 0);
2628                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2629                         calc_width(pane, dis, col, buffer);
2630                 col++;
2631 #endif /* _NO_EXTENSIONS */
2632
2633                 format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
2634                 if (calcWidthCol == -1)
2635                         output_text(pane, dis, col, buffer, 0);
2636                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2637                         calc_width(pane, dis, col, buffer);
2638                 col++;
2639         }
2640
2641 #ifndef _NO_EXTENSIONS
2642         if (entry->bhfi_valid) {
2643             ULONGLONG index = ((ULONGLONG)entry->bhfi.nFileIndexHigh << 32) | entry->bhfi.nFileIndexLow;
2644
2645                 if (visible_cols & COL_INDEX) {
2646                         _stprintf(buffer, TEXT("%") LONGLONGARG TEXT("X"), index);
2647                         if (calcWidthCol == -1)
2648                                 output_text(pane, dis, col, buffer, DT_RIGHT);
2649                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2650                                 calc_width(pane, dis, col, buffer);
2651                         col++;
2652                 }
2653
2654                 if (visible_cols & COL_LINKS) {
2655                         wsprintf(buffer, TEXT("%d"), entry->bhfi.nNumberOfLinks);
2656                         if (calcWidthCol == -1)
2657                                 output_text(pane, dis, col, buffer, DT_CENTER);
2658                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2659                                 calc_width(pane, dis, col, buffer);
2660                         col++;
2661                 }
2662         } else
2663                 col += 2;
2664 #endif /* _NO_EXTENSIONS */
2665
2666         /* show file attributes */
2667         if (visible_cols & COL_ATTRIBUTES) {
2668 #ifdef _NO_EXTENSIONS
2669                 _tcscpy(buffer, TEXT(" \t \t \t \t "));
2670 #else
2671                 _tcscpy(buffer, TEXT(" \t \t \t \t \t \t \t \t \t \t \t "));
2672 #endif
2673
2674                 if (attrs & FILE_ATTRIBUTE_NORMAL)                                      buffer[ 0] = 'N';
2675                 else {
2676                         if (attrs & FILE_ATTRIBUTE_READONLY)                    buffer[ 2] = 'R';
2677                         if (attrs & FILE_ATTRIBUTE_HIDDEN)                              buffer[ 4] = 'H';
2678                         if (attrs & FILE_ATTRIBUTE_SYSTEM)                              buffer[ 6] = 'S';
2679                         if (attrs & FILE_ATTRIBUTE_ARCHIVE)                             buffer[ 8] = 'A';
2680                         if (attrs & FILE_ATTRIBUTE_COMPRESSED)                  buffer[10] = 'C';
2681 #ifndef _NO_EXTENSIONS
2682                         if (attrs & FILE_ATTRIBUTE_DIRECTORY)                   buffer[12] = 'D';
2683                         if (attrs & FILE_ATTRIBUTE_ENCRYPTED)                   buffer[14] = 'E';
2684                         if (attrs & FILE_ATTRIBUTE_TEMPORARY)                   buffer[16] = 'T';
2685                         if (attrs & FILE_ATTRIBUTE_SPARSE_FILE)                 buffer[18] = 'P';
2686                         if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)               buffer[20] = 'Q';
2687                         if (attrs & FILE_ATTRIBUTE_OFFLINE)                             buffer[22] = 'O';
2688                         if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
2689 #endif /* _NO_EXTENSIONS */
2690                 }
2691
2692                 if (calcWidthCol == -1)
2693                         output_tabbed_text(pane, dis, col, buffer);
2694                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2695                         calc_tabbed_width(pane, dis, col, buffer);
2696
2697                 col++;
2698         }
2699
2700 /*TODO
2701         if (flags.security) {
2702                 DWORD rights = get_access_mask();
2703
2704                 tcscpy(buffer, TEXT(" \t \t \t  \t  \t \t \t  \t  \t \t \t "));
2705
2706                 if (rights & FILE_READ_DATA)                    buffer[ 0] = 'R';
2707                 if (rights & FILE_WRITE_DATA)                   buffer[ 2] = 'W';
2708                 if (rights & FILE_APPEND_DATA)                  buffer[ 4] = 'A';
2709                 if (rights & FILE_READ_EA)                              {buffer[6] = 'entry'; buffer[ 7] = 'R';}
2710                 if (rights & FILE_WRITE_EA)                             {buffer[9] = 'entry'; buffer[10] = 'W';}
2711                 if (rights & FILE_EXECUTE)                              buffer[12] = 'X';
2712                 if (rights & FILE_DELETE_CHILD)                 buffer[14] = 'D';
2713                 if (rights & FILE_READ_ATTRIBUTES)              {buffer[16] = 'a'; buffer[17] = 'R';}
2714                 if (rights & FILE_WRITE_ATTRIBUTES)             {buffer[19] = 'a'; buffer[20] = 'W';}
2715                 if (rights & WRITE_DAC)                                 buffer[22] = 'C';
2716                 if (rights & WRITE_OWNER)                               buffer[24] = 'O';
2717                 if (rights & SYNCHRONIZE)                               buffer[26] = 'S';
2718
2719                 output_text(dis, col++, buffer, DT_LEFT, 3, psize);
2720         }
2721
2722         if (flags.description) {
2723                 get_description(buffer);
2724                 output_text(dis, col++, buffer, 0, psize);
2725         }
2726 */
2727
2728 #ifdef _NO_EXTENSIONS
2729   }
2730
2731         /* draw focus frame */
2732         if ((dis->itemState&ODS_FOCUS) && calcWidthCol==-1) {
2733                 /* Currently [04/2000] Wine neither behaves exactly the same */
2734                 /* way as WIN 95 nor like Windows NT... */
2735                 HGDIOBJ lastBrush;
2736                 HPEN lastPen;
2737                 HPEN hpen;
2738
2739                 if (!(GetVersion() & 0x80000000)) {     /* Windows NT? */
2740                         LOGBRUSH lb = {PS_SOLID, RGB(255,255,255)};
2741                         hpen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, 0);
2742                 } else
2743                         hpen = CreatePen(PS_DOT, 0, RGB(255,255,255));
2744
2745                 lastPen = SelectPen(dis->hDC, hpen);
2746                 lastBrush = SelectObject(dis->hDC, GetStockObject(HOLLOW_BRUSH));
2747                 SetROP2(dis->hDC, R2_XORPEN);
2748                 Rectangle(dis->hDC, focusRect.left, focusRect.top, focusRect.right, focusRect.bottom);
2749                 SelectObject(dis->hDC, lastBrush);
2750                 SelectObject(dis->hDC, lastPen);
2751                 DeleteObject(hpen);
2752         }
2753 #endif /* _NO_EXTENSIONS */
2754 }
2755
2756
2757 #ifdef _NO_EXTENSIONS
2758
2759 static void draw_splitbar(HWND hwnd, int x)
2760 {
2761         RECT rt;
2762         HDC hdc = GetDC(hwnd);
2763
2764         GetClientRect(hwnd, &rt);
2765
2766         rt.left = x - SPLIT_WIDTH/2;
2767         rt.right = x + SPLIT_WIDTH/2+1;
2768
2769         InvertRect(hdc, &rt);
2770
2771         ReleaseDC(hwnd, hdc);
2772 }
2773
2774 #endif /* _NO_EXTENSIONS */
2775
2776
2777 #ifndef _NO_EXTENSIONS
2778
2779 static void set_header(Pane* pane)
2780 {
2781         HD_ITEM item;
2782         int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
2783         int i=0, x=0;
2784
2785         item.mask = HDI_WIDTH;
2786         item.cxy = 0;
2787
2788         for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
2789                 x += pane->widths[i];
2790                 Header_SetItem(pane->hwndHeader, i, &item);
2791         }
2792
2793         if (i < COLUMNS) {
2794                 x += pane->widths[i];
2795                 item.cxy = x - scroll_pos;
2796                 Header_SetItem(pane->hwndHeader, i++, &item);
2797
2798                 for(; i<COLUMNS; i++) {
2799                         item.cxy = pane->widths[i];
2800                         x += pane->widths[i];
2801                         Header_SetItem(pane->hwndHeader, i, &item);
2802                 }
2803         }
2804 }
2805
2806 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
2807 {
2808         switch(pnmh->code) {
2809                 case HDN_TRACK:
2810                 case HDN_ENDTRACK: {
2811                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
2812                         int idx = phdn->iItem;
2813                         int dx = phdn->pitem->cxy - pane->widths[idx];
2814                         int i;
2815
2816                         RECT clnt;
2817                         GetClientRect(pane->hwnd, &clnt);
2818
2819                         /* move immediate to simulate HDS_FULLDRAG (for now [04/2000] not realy needed with WINELIB) */
2820                         Header_SetItem(pane->hwndHeader, idx, phdn->pitem);
2821
2822                         pane->widths[idx] += dx;
2823
2824                         for(i=idx; ++i<=COLUMNS; )
2825                                 pane->positions[i] += dx;
2826
2827                         {
2828                                 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
2829                                 RECT rt_scr;
2830                                 RECT rt_clip;
2831
2832                                 rt_scr.left   = pane->positions[idx+1]-scroll_pos;
2833                                 rt_scr.top    = 0;
2834                                 rt_scr.right  = clnt.right;
2835                                 rt_scr.bottom = clnt.bottom;
2836
2837                                 rt_clip.left   = pane->positions[idx]-scroll_pos;
2838                                 rt_clip.top    = 0;
2839                                 rt_clip.right  = clnt.right;
2840                                 rt_clip.bottom = clnt.bottom;
2841
2842                                 if (rt_scr.left < 0) rt_scr.left = 0;
2843                                 if (rt_clip.left < 0) rt_clip.left = 0;
2844
2845                                 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
2846
2847                                 rt_clip.right = pane->positions[idx+1];
2848                                 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
2849
2850                                 if (pnmh->code == HDN_ENDTRACK) {
2851                                         ListBox_SetHorizontalExtent(pane->hwnd, pane->positions[COLUMNS]);
2852
2853                                         if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
2854                                                 set_header(pane);
2855                                 }
2856                         }
2857
2858                         return FALSE;
2859                 }
2860
2861                 case HDN_DIVIDERDBLCLICK: {
2862                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
2863                         HD_ITEM item;
2864
2865                         calc_single_width(pane, phdn->iItem);
2866                         item.mask = HDI_WIDTH;
2867                         item.cxy = pane->widths[phdn->iItem];
2868
2869                         Header_SetItem(pane->hwndHeader, phdn->iItem, &item);
2870                         InvalidateRect(pane->hwnd, 0, TRUE);
2871                         break;}
2872         }
2873
2874         return 0;
2875 }
2876
2877 #endif /* _NO_EXTENSIONS */
2878
2879
2880 static void scan_entry(ChildWnd* child, Entry* entry, HWND hwnd)
2881 {
2882         TCHAR path[MAX_PATH];
2883         int idx = ListBox_GetCurSel(child->left.hwnd);
2884         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
2885
2886         /* delete sub entries in left pane */
2887         for(;;) {
2888                 LRESULT res = ListBox_GetItemData(child->left.hwnd, idx+1);
2889                 Entry* sub = (Entry*) res;
2890
2891                 if (res==LB_ERR || !sub || sub->level<=entry->level)
2892                         break;
2893
2894                 ListBox_DeleteString(child->left.hwnd, idx+1);
2895         }
2896
2897         /* empty right pane */
2898         ListBox_ResetContent(child->right.hwnd);
2899
2900         /* release memory */
2901         free_entries(entry);
2902
2903         /* read contents from disk */
2904 #ifdef _SHELL_FOLDERS
2905         if (entry->etype == ET_SHELL)
2906         {
2907                 read_directory(entry, NULL, child->sortOrder, hwnd);
2908         }
2909         else
2910 #endif
2911         {
2912                 get_path(entry, path);
2913                 read_directory(entry, path, child->sortOrder, hwnd);
2914         }
2915
2916         /* insert found entries in right pane */
2917         insert_entries(&child->right, entry->down, -1);
2918         calc_widths(&child->right, FALSE);
2919 #ifndef _NO_EXTENSIONS
2920         set_header(&child->right);
2921 #endif
2922
2923         child->header_wdths_ok = FALSE;
2924
2925         SetCursor(old_cursor);
2926 }
2927
2928
2929 /* expand a directory entry */
2930
2931 static BOOL expand_entry(ChildWnd* child, Entry* dir)
2932 {
2933         int idx;
2934         Entry* p;
2935
2936         if (!dir || dir->expanded || !dir->down)
2937                 return FALSE;
2938
2939         p = dir->down;
2940
2941         if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
2942                 p = p->next;
2943
2944                 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
2945                                 p->data.cFileName[2]=='\0' && p->next)
2946                         p = p->next;
2947         }
2948
2949         /* no subdirectories ? */
2950         if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2951                 return FALSE;
2952
2953         idx = ListBox_FindItemData(child->left.hwnd, 0, dir);
2954
2955         dir->expanded = TRUE;
2956
2957         /* insert entries in left pane */
2958         insert_entries(&child->left, p, idx);
2959
2960         if (!child->header_wdths_ok) {
2961                 if (calc_widths(&child->left, FALSE)) {
2962 #ifndef _NO_EXTENSIONS
2963                         set_header(&child->left);
2964 #endif
2965
2966                         child->header_wdths_ok = TRUE;
2967                 }
2968         }
2969
2970         return TRUE;
2971 }
2972
2973
2974 static void collapse_entry(Pane* pane, Entry* dir)
2975 {
2976         int idx = ListBox_FindItemData(pane->hwnd, 0, dir);
2977
2978         ShowWindow(pane->hwnd, SW_HIDE);
2979
2980         /* hide sub entries */
2981         for(;;) {
2982                 LRESULT res = ListBox_GetItemData(pane->hwnd, idx+1);
2983                 Entry* sub = (Entry*) res;
2984
2985                 if (res==LB_ERR || !sub || sub->level<=dir->level)
2986                         break;
2987
2988                 ListBox_DeleteString(pane->hwnd, idx+1);
2989         }
2990
2991         dir->expanded = FALSE;
2992
2993         ShowWindow(pane->hwnd, SW_SHOW);
2994 }
2995
2996
2997 static void set_curdir(ChildWnd* child, Entry* entry, HWND hwnd)
2998 {
2999         TCHAR path[MAX_PATH];
3000
3001         path[0] = '\0';
3002
3003         child->left.cur = entry;
3004         child->right.root = entry->down? entry->down: entry;
3005         child->right.cur = entry;
3006
3007         if (!entry->scanned)
3008                 scan_entry(child, entry, hwnd);
3009         else {
3010                 ListBox_ResetContent(child->right.hwnd);
3011                 insert_entries(&child->right, entry->down, -1);
3012                 calc_widths(&child->right, FALSE);
3013 #ifndef _NO_EXTENSIONS
3014                 set_header(&child->right);
3015 #endif
3016         }
3017
3018         get_path(entry, path);
3019         lstrcpy(child->path, path);
3020
3021         if (child->hwnd)        /* only change window title, if the window already exists */
3022                 SetWindowText(child->hwnd, path);
3023
3024         if (path[0])
3025                 SetCurrentDirectory(path);
3026 }
3027
3028
3029 BOOL launch_file(HWND hwnd, LPCTSTR cmd, UINT nCmdShow)
3030 {
3031         HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3032
3033         if ((int)hinst <= 32) {
3034                 display_error(hwnd, GetLastError());
3035                 return FALSE;
3036         }
3037
3038         return TRUE;
3039 }
3040
3041 #ifdef UNICODE
3042 BOOL launch_fileA(HWND hwnd, LPSTR cmd, UINT nCmdShow)
3043 {
3044         HINSTANCE hinst = ShellExecuteA(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3045
3046         if ((int)hinst <= 32) {
3047                 display_error(hwnd, GetLastError());
3048                 return FALSE;
3049         }
3050
3051         return TRUE;
3052 }
3053 #endif
3054
3055
3056 BOOL launch_entry(Entry* entry, HWND hwnd, UINT nCmdShow)
3057 {
3058         TCHAR cmd[MAX_PATH];
3059
3060 #ifdef _SHELL_FOLDERS
3061         if (entry->etype == ET_SHELL) {
3062                 BOOL ret = TRUE;
3063
3064                 SHELLEXECUTEINFO shexinfo;
3065
3066                 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
3067                 shexinfo.fMask = SEE_MASK_IDLIST;
3068                 shexinfo.hwnd = hwnd;
3069                 shexinfo.lpVerb = NULL;
3070                 shexinfo.lpFile = NULL;
3071                 shexinfo.lpParameters = NULL;
3072                 shexinfo.lpDirectory = NULL;
3073                 shexinfo.nShow = nCmdShow;
3074                 shexinfo.lpIDList = get_to_absolute_pidl(entry, hwnd);
3075
3076                 if (!ShellExecuteEx(&shexinfo)) {
3077                         display_error(hwnd, GetLastError());
3078                         ret = FALSE;
3079                 }
3080
3081                 if (shexinfo.lpIDList != entry->pidl)
3082                         (*Globals.iMalloc->lpVtbl->Free)(Globals.iMalloc, shexinfo.lpIDList);
3083
3084                 return ret;
3085         }
3086 #endif
3087
3088         get_path(entry, cmd);
3089
3090          /* start program, open document... */
3091         return launch_file(hwnd, cmd, nCmdShow);
3092 }
3093
3094
3095 static void activate_entry(ChildWnd* child, Pane* pane, HWND hwnd)
3096 {
3097         Entry* entry = pane->cur;
3098
3099         if (!entry)
3100                 return;
3101
3102         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3103                 int scanned_old = entry->scanned;
3104
3105                 if (!scanned_old)
3106                         scan_entry(child, entry, hwnd);
3107
3108 #ifndef _NO_EXTENSIONS
3109                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3110                         return;
3111 #endif
3112
3113                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
3114                         entry = child->left.cur->up;
3115                         collapse_entry(&child->left, entry);
3116                         goto focus_entry;
3117                 } else if (entry->expanded)
3118                         collapse_entry(pane, child->left.cur);
3119                 else {
3120                         expand_entry(child, child->left.cur);
3121
3122                         if (!pane->treePane) focus_entry: {
3123                                 int idx = ListBox_FindItemData(child->left.hwnd, ListBox_GetCurSel(child->left.hwnd), entry);
3124                                 ListBox_SetCurSel(child->left.hwnd, idx);
3125                                 set_curdir(child, entry, hwnd);
3126                         }
3127                 }
3128
3129                 if (!scanned_old) {
3130                         calc_widths(pane, FALSE);
3131
3132 #ifndef _NO_EXTENSIONS
3133                         set_header(pane);
3134 #endif
3135                 }
3136         } else {
3137                 launch_entry(entry, child->hwnd, SW_SHOWNORMAL);
3138         }
3139 }
3140
3141
3142 static BOOL pane_command(Pane* pane, UINT cmd)
3143 {
3144         switch(cmd) {
3145                 case ID_VIEW_NAME:
3146                         if (pane->visible_cols) {
3147                                 pane->visible_cols = 0;
3148                                 calc_widths(pane, TRUE);
3149 #ifndef _NO_EXTENSIONS
3150                                 set_header(pane);
3151 #endif
3152                                 InvalidateRect(pane->hwnd, 0, TRUE);
3153                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
3154                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
3155                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
3156                         }
3157                         break;
3158
3159                 case ID_VIEW_ALL_ATTRIBUTES:
3160                         if (pane->visible_cols != COL_ALL) {
3161                                 pane->visible_cols = COL_ALL;
3162                                 calc_widths(pane, TRUE);
3163 #ifndef _NO_EXTENSIONS
3164                                 set_header(pane);
3165 #endif
3166                                 InvalidateRect(pane->hwnd, 0, TRUE);
3167                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
3168                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
3169                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
3170                         }
3171                         break;
3172
3173 #ifndef _NO_EXTENSIONS
3174                 case ID_PREFERED_SIZES: {
3175                         calc_widths(pane, TRUE);
3176                         set_header(pane);
3177                         InvalidateRect(pane->hwnd, 0, TRUE);
3178                         break;}
3179 #endif
3180
3181                         /* TODO: more command ids... */
3182
3183                 default:
3184                         return FALSE;
3185         }
3186
3187         return TRUE;
3188 }
3189
3190
3191 LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
3192 {
3193         static int last_split;
3194
3195         ChildWnd* child = (ChildWnd*) GetWindowLong(hwnd, GWL_USERDATA);
3196         ASSERT(child);
3197
3198         switch(nmsg) {
3199                 case WM_DRAWITEM: {
3200                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
3201                         Entry* entry = (Entry*) dis->itemData;
3202
3203                         if (dis->CtlID == IDW_TREE_LEFT)
3204                                 draw_item(&child->left, dis, entry, -1);
3205                         else
3206                                 draw_item(&child->right, dis, entry, -1);
3207
3208                         return TRUE;}
3209
3210                 case WM_CREATE:
3211                         InitChildWindow(child);
3212                         break;
3213
3214                 case WM_NCDESTROY:
3215                         free_child_window(child);
3216                         SetWindowLong(hwnd, GWL_USERDATA, 0);
3217                         break;
3218
3219                 case WM_PAINT: {
3220                         PAINTSTRUCT ps;
3221                         HBRUSH lastBrush;
3222                         RECT rt;
3223                         GetClientRect(hwnd, &rt);
3224                         BeginPaint(hwnd, &ps);
3225                         rt.left = child->split_pos-SPLIT_WIDTH/2;
3226                         rt.right = child->split_pos+SPLIT_WIDTH/2+1;
3227                         lastBrush = SelectBrush(ps.hdc, (HBRUSH)GetStockObject(COLOR_SPLITBAR));
3228                         Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
3229                         SelectObject(ps.hdc, lastBrush);
3230 #ifdef _NO_EXTENSIONS
3231                         rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
3232                         FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
3233 #endif
3234                         EndPaint(hwnd, &ps);
3235                         break;}
3236
3237                 case WM_SETCURSOR:
3238                         if (LOWORD(lparam) == HTCLIENT) {
3239                                 POINT pt;
3240                                 GetCursorPos(&pt);
3241                                 ScreenToClient(hwnd, &pt);
3242
3243                                 if (pt.x>=child->split_pos-SPLIT_WIDTH/2 && pt.x<child->split_pos+SPLIT_WIDTH/2+1) {
3244                                         SetCursor(LoadCursor(0, IDC_SIZEWE));
3245                                         return TRUE;
3246                                 }
3247                         }
3248                         goto def;
3249
3250                 case WM_LBUTTONDOWN: {
3251                         RECT rt;
3252                         int x = LOWORD(lparam);
3253
3254                         GetClientRect(hwnd, &rt);
3255
3256                         if (x>=child->split_pos-SPLIT_WIDTH/2 && x<child->split_pos+SPLIT_WIDTH/2+1) {
3257                                 last_split = child->split_pos;
3258 #ifdef _NO_EXTENSIONS
3259                                 draw_splitbar(hwnd, last_split);
3260 #endif
3261                                 SetCapture(hwnd);
3262                         }
3263
3264                         break;}
3265
3266                 case WM_LBUTTONUP:
3267                         if (GetCapture() == hwnd) {
3268 #ifdef _NO_EXTENSIONS
3269                                 RECT rt;
3270                                 int x = LOWORD(lparam);
3271                                 draw_splitbar(hwnd, last_split);
3272                                 last_split = -1;
3273                                 GetClientRect(hwnd, &rt);
3274                                 child->split_pos = x;
3275                                 resize_tree(child, rt.right, rt.bottom);
3276 #endif
3277                                 ReleaseCapture();
3278                         }
3279                         break;
3280
3281 #ifdef _NO_EXTENSIONS
3282                 case WM_CAPTURECHANGED:
3283                         if (GetCapture()==hwnd && last_split>=0)
3284                                 draw_splitbar(hwnd, last_split);
3285                         break;
3286 #endif
3287
3288                 case WM_KEYDOWN:
3289                         if (wparam == VK_ESCAPE)
3290                                 if (GetCapture() == hwnd) {
3291                                         RECT rt;
3292 #ifdef _NO_EXTENSIONS
3293                                         draw_splitbar(hwnd, last_split);
3294 #else
3295                                         child->split_pos = last_split;
3296 #endif
3297                                         GetClientRect(hwnd, &rt);
3298                                         resize_tree(child, rt.right, rt.bottom);
3299                                         last_split = -1;
3300                                         ReleaseCapture();
3301                                         SetCursor(LoadCursor(0, IDC_ARROW));
3302                                 }
3303                         break;
3304
3305                 case WM_MOUSEMOVE:
3306                         if (GetCapture() == hwnd) {
3307                                 RECT rt;
3308                                 int x = LOWORD(lparam);
3309
3310 #ifdef _NO_EXTENSIONS
3311                                 HDC hdc = GetDC(hwnd);
3312                                 GetClientRect(hwnd, &rt);
3313
3314                                 rt.left = last_split-SPLIT_WIDTH/2;
3315                                 rt.right = last_split+SPLIT_WIDTH/2+1;
3316                                 InvertRect(hdc, &rt);
3317
3318                                 last_split = x;
3319                                 rt.left = x-SPLIT_WIDTH/2;
3320                                 rt.right = x+SPLIT_WIDTH/2+1;
3321                                 InvertRect(hdc, &rt);
3322
3323                                 ReleaseDC(hwnd, hdc);
3324 #else
3325                                 GetClientRect(hwnd, &rt);
3326
3327                                 if (x>=0 && x<rt.right) {
3328                                         child->split_pos = x;
3329                                         resize_tree(child, rt.right, rt.bottom);
3330                                         rt.left = x-SPLIT_WIDTH/2;
3331                                         rt.right = x+SPLIT_WIDTH/2+1;
3332                                         InvalidateRect(hwnd, &rt, FALSE);
3333                                         UpdateWindow(child->left.hwnd);
3334                                         UpdateWindow(hwnd);
3335                                         UpdateWindow(child->right.hwnd);
3336                                 }
3337 #endif
3338                         }
3339                         break;
3340
3341 #ifndef _NO_EXTENSIONS
3342                 case WM_GETMINMAXINFO:
3343                         DefMDIChildProc(hwnd, nmsg, wparam, lparam);
3344
3345                         {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
3346
3347                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
3348                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
3349                         break;}
3350 #endif /* _NO_EXTENSIONS */
3351
3352                 case WM_SETFOCUS:
3353                         SetCurrentDirectory(child->path);
3354                         SetFocus(child->focus_pane? child->right.hwnd: child->left.hwnd);
3355                         break;
3356
3357                 case WM_DISPATCH_COMMAND: {
3358                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
3359
3360                         switch(LOWORD(wparam)) {
3361                                 case ID_WINDOW_NEW: {
3362                                         ChildWnd* new_child = alloc_child_window(child->path, NULL, hwnd);
3363
3364                                         if (!create_child_window(new_child))
3365                                                 free(new_child);
3366
3367                                         break;}
3368
3369                                 case ID_REFRESH:
3370                                         scan_entry(child, pane->cur, hwnd);
3371                                         break;
3372
3373                                 case ID_ACTIVATE:
3374                                         activate_entry(child, pane, hwnd);
3375                                         break;
3376
3377                                 default:
3378                                         return pane_command(pane, LOWORD(wparam));
3379                         }
3380
3381                         return TRUE;}
3382
3383                 case WM_COMMAND: {
3384                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
3385
3386                         switch(HIWORD(wparam)) {
3387                                 case LBN_SELCHANGE: {
3388                                         int idx = ListBox_GetCurSel(pane->hwnd);
3389                                         Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, idx);
3390
3391                                         if (pane == &child->left)
3392                                                 set_curdir(child, entry, hwnd);
3393                                         else
3394                                                 pane->cur = entry;
3395                                         break;}
3396
3397                                 case LBN_DBLCLK:
3398                                         activate_entry(child, pane, hwnd);
3399                                         break;
3400                         }
3401                         break;}
3402
3403 #ifndef _NO_EXTENSIONS
3404                 case WM_NOTIFY: {
3405                         NMHDR* pnmh = (NMHDR*) lparam;
3406                         return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &child->left: &child->right, pnmh);}
3407 #endif
3408
3409                 case WM_SIZE:
3410                         if (wparam != SIZE_MINIMIZED)
3411                                 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
3412                         /* fall through */
3413
3414                 default: def:
3415                         return DefMDIChildProc(hwnd, nmsg, wparam, lparam);
3416         }
3417
3418         return 0;
3419 }
3420
3421
3422 LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
3423 {
3424         ChildWnd* child = (ChildWnd*) GetWindowLong(GetParent(hwnd), GWL_USERDATA);
3425         Pane* pane = (Pane*) GetWindowLong(hwnd, GWL_USERDATA);
3426         ASSERT(child);
3427
3428         switch(nmsg) {
3429 #ifndef _NO_EXTENSIONS
3430                 case WM_HSCROLL:
3431                         set_header(pane);
3432                         break;
3433 #endif
3434
3435                 case WM_SETFOCUS:
3436                         child->focus_pane = pane==&child->right? 1: 0;
3437                         ListBox_SetSel(hwnd, TRUE, 1);
3438                         /*TODO: check menu items */
3439                         break;
3440
3441                 case WM_KEYDOWN:
3442                         if (wparam == VK_TAB) {
3443                                 /*TODO: SetFocus(Globals.hdrivebar) */
3444                                 SetFocus(child->focus_pane? child->left.hwnd: child->right.hwnd);
3445                         }
3446         }
3447
3448         return CallWindowProc(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
3449 }
3450
3451
3452 static void InitInstance(HINSTANCE hinstance)
3453 {
3454         WNDCLASSEX wcFrame;
3455         WNDCLASS wcChild;
3456         ATOM hChildClass;
3457
3458         INITCOMMONCONTROLSEX icc = {
3459                 sizeof(INITCOMMONCONTROLSEX),
3460                 ICC_BAR_CLASSES
3461         };
3462
3463         HDC hdc = GetDC(0);
3464
3465         setlocale(LC_COLLATE, "");      /* set collating rules to local settings for compareName */
3466
3467         InitCommonControlsEx(&icc);
3468
3469
3470         /* register frame window class */
3471
3472         wcFrame.cbSize        = sizeof(WNDCLASSEX);
3473         wcFrame.style         = 0;
3474         wcFrame.lpfnWndProc   = FrameWndProc;
3475         wcFrame.cbClsExtra    = 0;
3476         wcFrame.cbWndExtra    = 0;
3477         wcFrame.hInstance     = hinstance;
3478         wcFrame.hIcon         = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_WINEFILE));
3479         wcFrame.hCursor       = LoadCursor(0, IDC_ARROW);
3480         wcFrame.hbrBackground = 0;
3481         wcFrame.lpszMenuName  = 0;
3482         wcFrame.lpszClassName = WINEFILEFRAME;
3483         wcFrame.hIconSm       = (HICON)LoadImage(hinstance,
3484                                                                                          MAKEINTRESOURCE(IDI_WINEFILE),
3485                                                                                          IMAGE_ICON,
3486                                                                                          GetSystemMetrics(SM_CXSMICON),
3487                                                                                          GetSystemMetrics(SM_CYSMICON),
3488                                                                                          LR_SHARED);
3489
3490         Globals.hframeClass = RegisterClassEx(&wcFrame);
3491
3492
3493         /* register tree windows class */
3494
3495         wcChild.style         = CS_CLASSDC|CS_DBLCLKS|CS_VREDRAW;
3496         wcChild.lpfnWndProc   = ChildWndProc;
3497         wcChild.cbClsExtra    = 0;
3498         wcChild.cbWndExtra    = 0;
3499         wcChild.hInstance     = hinstance;
3500         wcChild.hIcon         = 0;
3501         wcChild.hCursor       = LoadCursor(0, IDC_ARROW);
3502         wcChild.hbrBackground = 0;
3503         wcChild.lpszMenuName  = 0;
3504         wcChild.lpszClassName = WINEFILETREE;
3505
3506         hChildClass = RegisterClass(&wcChild);
3507
3508
3509         Globals.haccel = LoadAccelerators(hinstance, MAKEINTRESOURCE(IDA_WINEFILE));
3510
3511         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"));
3512
3513         ReleaseDC(0, hdc);
3514
3515         Globals.hInstance = hinstance;
3516
3517 #ifdef _SHELL_FOLDERS
3518         CoInitialize(NULL);
3519         CoGetMalloc(MEMCTX_TASK, &Globals.iMalloc);
3520         SHGetDesktopFolder(&Globals.iDesktop);
3521         Globals.cfStrFName = RegisterClipboardFormat(CFSTR_FILENAME);
3522 #endif
3523 }
3524
3525
3526 void show_frame(HWND hwndParent, int cmdshow)
3527 {
3528         TCHAR path[MAX_PATH];
3529         ChildWnd* child;
3530         HMENU hMenuFrame, hMenuWindow;
3531
3532         CLIENTCREATESTRUCT ccs;
3533
3534         if (Globals.hMainWnd)
3535                 return;
3536
3537         Globals.hwndParent = hwndParent;
3538
3539         hMenuFrame = LoadMenu(Globals.hInstance, MAKEINTRESOURCE(IDM_WINEFILE));
3540         hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
3541
3542         Globals.hMenuFrame = hMenuFrame;
3543         Globals.hMenuView = GetSubMenu(hMenuFrame, 3);
3544         Globals.hMenuOptions = GetSubMenu(hMenuFrame, 4);
3545
3546         ccs.hWindowMenu  = hMenuWindow;
3547         ccs.idFirstChild = IDW_FIRST_CHILD;
3548
3549
3550         /* create main window */
3551         Globals.hMainWnd = CreateWindowEx(0, (LPCTSTR)(int)Globals.hframeClass, TEXT("Wine File"), WS_OVERLAPPEDWINDOW,
3552                                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
3553                                         hwndParent, Globals.hMenuFrame, Globals.hInstance, 0/*lpParam*/);
3554
3555
3556         Globals.hmdiclient = CreateWindowEx(0, TEXT("MDICLIENT"), NULL,
3557                                         WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|WS_BORDER,
3558                                         0, 0, 0, 0,
3559                                         Globals.hMainWnd, 0, Globals.hInstance, &ccs);
3560
3561
3562         {
3563                 TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, BTNS_SEP, {0, 0}, 0, 0};
3564                 int btn = 1;
3565                 PTSTR p;
3566
3567                 Globals.hdrivebar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE|CCS_NOMOVEY|TBSTYLE_LIST,
3568                                         IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
3569                                         1, 16, 13, 16, 13, sizeof(TBBUTTON));
3570                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
3571
3572                 GetLogicalDriveStrings(BUFFER_LEN, Globals.drives);
3573
3574                 drivebarBtn.fsStyle = BTNS_BUTTON;
3575
3576 #ifndef _NO_EXTENSIONS
3577 #ifdef __WINE__
3578                 /* insert unix file system button */
3579                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)TEXT("/\0"));
3580
3581                 drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
3582                 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3583                 drivebarBtn.iString++;
3584 #endif
3585 #ifdef _SHELL_FOLDERS
3586                 /* insert shell namespace button */
3587                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)TEXT("Shell\0"));
3588
3589                 drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3590                 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3591                 drivebarBtn.iString++;
3592 #endif
3593
3594                 /* register windows drive root strings */
3595                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)Globals.drives);
3596 #endif
3597
3598                 drivebarBtn.idCommand = ID_DRIVE_FIRST;
3599
3600                 for(p=Globals.drives; *p; ) {
3601 #ifdef _NO_EXTENSIONS
3602                   /* insert drive letter */
3603                         TCHAR b[3] = {tolower(*p)};
3604                         SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)b);
3605 #endif
3606                         switch(GetDriveType(p)) {
3607                                 case DRIVE_REMOVABLE:   drivebarBtn.iBitmap = 1;        break;
3608                                 case DRIVE_CDROM:               drivebarBtn.iBitmap = 3;        break;
3609                                 case DRIVE_REMOTE:              drivebarBtn.iBitmap = 4;        break;
3610                                 case DRIVE_RAMDISK:             drivebarBtn.iBitmap = 5;        break;
3611                                 default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
3612                         }
3613
3614                         SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
3615                         drivebarBtn.idCommand++;
3616                         drivebarBtn.iString++;
3617
3618                         while(*p++);
3619                 }
3620         }
3621
3622         {
3623                 TBBUTTON toolbarBtns[] = {
3624                         {0, 0, 0, BTNS_SEP, {0, 0}, 0, 0},
3625                         {0, ID_WINDOW_NEW, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
3626                         {1, ID_WINDOW_CASCADE, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
3627                         {2, ID_WINDOW_TILE_HORZ, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
3628                         {3, ID_WINDOW_TILE_VERT, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0},
3629 /*TODO
3630                         {4, ID_... , TBSTATE_ENABLED, BTNS_BUTTON},
3631                         {5, ID_... , TBSTATE_ENABLED, BTNS_BUTTON},
3632 */              };
3633
3634                 Globals.htoolbar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE,
3635                         IDW_TOOLBAR, 2, Globals.hInstance, IDB_TOOLBAR, toolbarBtns,
3636                         sizeof(toolbarBtns)/sizeof(TBBUTTON), 16, 15, 16, 15, sizeof(TBBUTTON));
3637                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_TOOL_BAR, MF_BYCOMMAND|MF_CHECKED);
3638         }
3639
3640         Globals.hstatusbar = CreateStatusWindow(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
3641         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
3642
3643 /* CreateStatusWindow does not accept WS_BORDER
3644         Globals.hstatusbar = CreateWindowEx(WS_EX_NOPARENTNOTIFY, STATUSCLASSNAME, 0,
3645                                         WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_BORDER|CCS_NODIVIDER, 0,0,0,0,
3646                                         Globals.hMainWnd, (HMENU)IDW_STATUSBAR, hinstance, 0);*/
3647
3648         /*TODO: read paths and window placements from registry */
3649         GetCurrentDirectory(MAX_PATH, path);
3650
3651         ShowWindow(Globals.hMainWnd, cmdshow);
3652
3653 #if defined(_SHELL_FOLDERS) && !defined(__WINE__)
3654          /* Shell Namespace as default: */
3655         child = alloc_child_window(path, get_path_pidl(path,Globals.hMainWnd), Globals.hMainWnd);
3656 #else
3657         child = alloc_child_window(path, NULL, Globals.hMainWnd);
3658 #endif
3659
3660         child->pos.showCmd = SW_SHOWMAXIMIZED;
3661         child->pos.rcNormalPosition.left = 0;
3662         child->pos.rcNormalPosition.top = 0;
3663         child->pos.rcNormalPosition.right = 320;
3664         child->pos.rcNormalPosition.bottom = 280;
3665
3666         if (!create_child_window(child))
3667                 free(child);
3668
3669         SetWindowPlacement(child->hwnd, &child->pos);
3670
3671         Globals.himl = ImageList_LoadBitmap(Globals.hInstance, MAKEINTRESOURCE(IDB_IMAGES), 16, 0, RGB(0,255,0));
3672
3673         Globals.prescan_node = FALSE;
3674
3675         UpdateWindow(Globals.hMainWnd);
3676 }
3677
3678 void ExitInstance()
3679 {
3680 #ifdef _SHELL_FOLDERS
3681         (*Globals.iDesktop->lpVtbl->Release)(Globals.iDesktop);
3682         (*Globals.iMalloc->lpVtbl->Release)(Globals.iMalloc);
3683         CoUninitialize();
3684 #endif
3685
3686         ImageList_Destroy(Globals.himl);
3687 }
3688
3689
3690 /* search for already running win[e]files */
3691
3692 static int g_foundPrevInstance = 0;
3693
3694 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
3695 {
3696         TCHAR cls[128];
3697
3698         GetClassName(hwnd, cls, 128);
3699
3700         if (!lstrcmp(cls, (LPCTSTR)lparam)) {
3701                 g_foundPrevInstance++;
3702                 return FALSE;
3703         }
3704
3705         return TRUE;
3706 }
3707
3708 /* search for window of given class name to allow only one running instance */
3709 int find_window_class(LPCTSTR classname)
3710 {
3711         EnumWindows(EnumWndProc, (LPARAM)classname);
3712
3713         if (g_foundPrevInstance)
3714                 return 1;
3715
3716         return 0;
3717 }
3718
3719
3720 int winefile_main(HINSTANCE hinstance, HWND hwndParent, int cmdshow)
3721 {
3722         MSG msg;
3723
3724         InitInstance(hinstance);
3725
3726 #ifndef _ROS_   /* don't maximize if being called from the ROS desktop */
3727         if (cmdshow == SW_SHOWNORMAL)
3728                 /*TODO: read window placement from registry */
3729                 cmdshow = SW_MAXIMIZE;
3730 #endif
3731
3732         show_frame(hwndParent, cmdshow);
3733
3734         while(GetMessage(&msg, 0, 0, 0)) {
3735                 if (Globals.hmdiclient && TranslateMDISysAccel(Globals.hmdiclient, &msg))
3736                         continue;
3737
3738                 if (Globals.hMainWnd && TranslateAccelerator(Globals.hMainWnd, Globals.haccel, &msg))
3739                         continue;
3740
3741                 TranslateMessage(&msg);
3742                 DispatchMessage(&msg);
3743         }
3744
3745         ExitInstance();
3746
3747         return msg.wParam;
3748 }
3749
3750
3751 #ifndef _ROS_
3752
3753 int APIENTRY WinMain(HINSTANCE hinstance,
3754                                          HINSTANCE previnstance,
3755                                          LPSTR     cmdline,
3756                                          int       cmdshow)
3757 {
3758 #ifdef _NO_EXTENSIONS
3759         if (find_window_class(WINEFILEFRAME))
3760                 return 1;
3761 #endif
3762
3763         winefile_main(hinstance, 0, cmdshow);
3764
3765         return 0;
3766 }
3767
3768 #endif