Add -lc when linking dlls to avoid warnings on FreeBSD.
[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 #ifndef _WIN32
22 #include "config.h"
23 #include "wine/port.h"
24 #endif
25
26 #include "winefile.h"
27 #include "resource.h"
28
29
30 /* for read_directory_unix() */
31 #if !defined(_NO_EXTENSIONS) && !defined(_WIN32)
32 #include <dirent.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <time.h>
36 #endif
37
38 #ifdef _NO_EXTENSIONS
39 #undef _LEFT_FILES
40 #endif
41
42 #ifndef _MAX_PATH
43 #define _MAX_DRIVE          3
44 #define _MAX_FNAME          256
45 #define _MAX_DIR            _MAX_FNAME
46 #define _MAX_EXT            _MAX_FNAME
47 #define _MAX_PATH           260
48 #endif
49
50 WINEFILE_GLOBALS Globals;
51
52 extern void WineLicense(HWND hWnd);
53 extern void WineWarranty(HWND hWnd);
54
55 typedef struct _Entry {
56         struct _Entry*  next;
57         struct _Entry*  down;
58         struct _Entry*  up;
59
60         BOOL    expanded;
61         BOOL    scanned;
62         int             level;
63
64         WIN32_FIND_DATA data;
65
66 #ifndef _NO_EXTENSIONS
67         BY_HANDLE_FILE_INFORMATION bhfi;
68         BOOL    bhfi_valid;
69         BOOL    unix_dir;
70 #endif
71 } Entry;
72
73 typedef struct {
74         Entry   entry;
75         TCHAR   path[MAX_PATH];
76         TCHAR   volname[_MAX_FNAME];
77         TCHAR   fs[_MAX_DIR];
78         DWORD   drive_type;
79         DWORD   fs_flags;
80 } Root;
81
82 enum COLUMN_FLAGS {
83         COL_SIZE                = 0x01,
84         COL_DATE                = 0x02,
85         COL_TIME                = 0x04,
86         COL_ATTRIBUTES  = 0x08,
87         COL_DOSNAMES    = 0x10,
88 #ifdef _NO_EXTENSIONS
89         COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES
90 #else
91         COL_INDEX               = 0x20,
92         COL_LINKS               = 0x40,
93         COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES|COL_INDEX|COL_LINKS
94 #endif
95 };
96
97 typedef enum {
98         SORT_NAME,
99         SORT_EXT,
100         SORT_SIZE,
101         SORT_DATE
102 } SORT_ORDER;
103
104 typedef struct {
105         HWND    hwnd;
106 #ifndef _NO_EXTENSIONS
107         HWND    hwndHeader;
108 #endif
109
110 #ifndef _NO_EXTENSIONS
111 #define COLUMNS 10
112 #else
113 #define COLUMNS 5
114 #endif
115         int             widths[COLUMNS];
116         int             positions[COLUMNS+1];
117
118         BOOL    treePane;
119         int             visible_cols;
120         Entry*  root;
121         Entry*  cur;
122 } Pane;
123
124 typedef struct {
125         HWND    hwnd;
126         Pane    left;
127         Pane    right;
128         int             focus_pane;             /* 0: left  1: right */
129         WINDOWPLACEMENT pos;
130         int             split_pos;
131         BOOL    header_wdths_ok;
132
133         TCHAR   path[MAX_PATH];
134         Root    root;
135
136         SORT_ORDER sortOrder;
137 } ChildWnd;
138
139
140 static void read_directory(Entry* parent, LPCTSTR path, int sortOrder);
141 static void set_curdir(ChildWnd* child, Entry* entry);
142
143 LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
144 LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
145 LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
146
147
148 static void display_error(HWND hwnd, DWORD error)
149 {
150         PTSTR msg;
151
152         if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
153                 0, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (PTSTR)&msg, 0, NULL))
154                 MessageBox(hwnd, msg, _T("Winefile"), MB_OK);
155         else
156                 MessageBox(hwnd, _T("Error"), _T("Winefile"), MB_OK);
157
158         LocalFree(msg);
159 }
160
161
162 static void read_directory_win(Entry* parent, LPCTSTR path)
163 {
164         Entry* entry = (Entry*) malloc(sizeof(Entry));
165         int level = parent->level + 1;
166         Entry* last = 0;
167         HANDLE hFind;
168 #ifndef _NO_EXTENSIONS
169         HANDLE hFile;
170 #endif
171
172         TCHAR buffer[MAX_PATH], *p;
173         for(p=buffer; *path; )
174                 *p++ = *path++;
175
176         lstrcpy(p, _T("\\*"));
177
178         hFind = FindFirstFile(buffer, &entry->data);
179
180         if (hFind != INVALID_HANDLE_VALUE) {
181                 parent->down = entry;
182
183                 do {
184                         entry->down = 0;
185                         entry->up = parent;
186                         entry->expanded = FALSE;
187                         entry->scanned = FALSE;
188                         entry->level = level;
189
190 #ifdef _NO_EXTENSIONS
191                         /* hide directory entry "." */
192                         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
193                                 LPCTSTR name = entry->data.cFileName;
194
195                                 if (name[0]=='.' && name[1]=='\0')
196                                         continue;
197                         }
198 #else
199                         entry->unix_dir = FALSE;
200                         entry->bhfi_valid = FALSE;
201
202                         lstrcpy(p+1, entry->data.cFileName);
203
204                         hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
205                                                                 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
206
207                         if (hFile != INVALID_HANDLE_VALUE) {
208                                 if (GetFileInformationByHandle(hFile, &entry->bhfi))
209                                         entry->bhfi_valid = TRUE;
210
211                                 CloseHandle(hFile);
212                         }
213 #endif
214
215                         last = entry;
216
217                         entry = (Entry*) malloc(sizeof(Entry));
218
219                         if (last)
220                                 last->next = entry;
221                 } while(FindNextFile(hFind, &entry->data));
222
223                 last->next = 0;
224
225                 FindClose(hFind);
226         } else
227                 parent->down = 0;
228
229         free(entry);
230
231         parent->scanned = TRUE;
232 }
233
234
235 static Entry* find_entry_win(Entry* parent, LPCTSTR name)
236 {
237         Entry* entry;
238
239         for(entry=parent->down; entry; entry=entry->next) {
240                 LPCTSTR p = name;
241                 LPCTSTR q = entry->data.cFileName;
242
243                 do {
244                         if (!*p || *p==_T('\\') || *p==_T('/'))
245                                 return entry;
246                 } while(tolower(*p++) == tolower(*q++));
247
248                 p = name;
249                 q = entry->data.cAlternateFileName;
250
251                 do {
252                         if (!*p || *p==_T('\\') || *p==_T('/'))
253                                 return entry;
254                 } while(tolower(*p++) == tolower(*q++));
255         }
256
257         return 0;
258 }
259
260
261 static Entry* read_tree_win(Root* root, LPCTSTR path, int sortOrder)
262 {
263         TCHAR buffer[MAX_PATH];
264         Entry* entry = &root->entry;
265         LPCTSTR s = path;
266         PTSTR d = buffer;
267
268 #ifndef _NO_EXTENSIONS
269         entry->unix_dir = FALSE;
270 #endif
271
272         while(entry) {
273                 while(*s && *s!=_T('\\') && *s!=_T('/'))
274                         *d++ = *s++;
275
276                 while(*s==_T('\\') || *s==_T('/'))
277                         s++;
278
279                 *d++ = _T('\\');
280                 *d = _T('\0');
281
282                 read_directory(entry, buffer, sortOrder);
283
284                 if (entry->down)
285                         entry->expanded = TRUE;
286
287                 if (!*s)
288                         break;
289
290                 entry = find_entry_win(entry, s);
291         }
292
293         return entry;
294 }
295
296
297 #if !defined(_NO_EXTENSIONS) && defined(__linux__)
298
299 BOOL to_filetime(const time_t* t, FILETIME* ftime)
300 {
301         struct tm* tm = gmtime(t);
302         SYSTEMTIME stime;
303
304         if (!tm)
305                 return FALSE;
306
307         stime.wYear = tm->tm_year+1900;
308         stime.wMonth = tm->tm_mon+1;
309         /*      stime.wDayOfWeek */
310         stime.wDay = tm->tm_mday;
311         stime.wHour = tm->tm_hour;
312         stime.wMinute = tm->tm_min;
313         stime.wSecond = tm->tm_sec;
314
315         return SystemTimeToFileTime(&stime, ftime);
316 }
317
318 static void read_directory_unix(Entry* parent, LPCTSTR path)
319 {
320         Entry* entry = (Entry*) malloc(sizeof(Entry));
321         int level = parent->level + 1;
322         Entry* last = 0;
323
324         DIR* dir = opendir(path);
325
326         if (dir) {
327                 struct stat st;
328                 struct dirent* ent;
329                 TCHAR buffer[MAX_PATH], *p;
330
331                 for(p=buffer; *path; )
332                         *p++ = *path++;
333
334                 if (p==buffer || p[-1]!='/')
335                         *p++ = '/';
336
337                 parent->down = entry;
338
339                 while((ent=readdir(dir))) {
340                         entry->unix_dir = TRUE;
341                         lstrcpy(entry->data.cFileName, ent->d_name);
342                         entry->data.dwFileAttributes = ent->d_name[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
343
344                         strcpy(p, ent->d_name);
345
346                         if (!stat(buffer, &st)) {
347                                 if (S_ISDIR(st.st_mode))
348                                         entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
349
350                                 entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
351                                 entry->data.nFileSizeHigh = st.st_size >> 32;
352
353                                 memset(&entry->data.ftCreationTime, 0, sizeof(FILETIME));
354                                 to_filetime(&st.st_atime, &entry->data.ftLastAccessTime);
355                                 to_filetime(&st.st_mtime, &entry->data.ftLastWriteTime);
356
357                                 entry->bhfi.nFileIndexLow = ent->d_ino;
358                                 entry->bhfi.nFileIndexHigh = 0;
359
360                                 entry->bhfi.nNumberOfLinks = st.st_nlink;
361
362                                 entry->bhfi_valid = TRUE;
363                         } else {
364                                 entry->data.nFileSizeLow = 0;
365                                 entry->data.nFileSizeHigh = 0;
366                                 entry->bhfi_valid = FALSE;
367                         }
368
369                         entry->down = 0;
370                         entry->up = parent;
371                         entry->expanded = FALSE;
372                         entry->scanned = FALSE;
373                         entry->level = level;
374
375                         last = entry;
376
377                         entry = (Entry*) malloc(sizeof(Entry));
378
379                         if (last)
380                                 last->next = entry;
381                 }
382
383                 last->next = 0;
384
385                 closedir(dir);
386         } else
387                 parent->down = 0;
388
389         free(entry);
390
391         parent->scanned = TRUE;
392 }
393
394 static Entry* find_entry_unix(Entry* parent, LPCTSTR name)
395 {
396         Entry* entry;
397
398         for(entry=parent->down; entry; entry=entry->next) {
399                 LPCTSTR p = name;
400                 LPCTSTR q = entry->data.cFileName;
401
402                 do {
403                         if (!*p || *p==_T('/'))
404                                 return entry;
405                 } while(*p++ == *q++);
406         }
407
408         return 0;
409 }
410
411 static Entry* read_tree_unix(Root* root, LPCTSTR path, int sortOrder)
412 {
413         TCHAR buffer[MAX_PATH];
414         Entry* entry = &root->entry;
415         LPCTSTR s = path;
416         PTSTR d = buffer;
417
418         entry->unix_dir = TRUE;
419
420         while(entry) {
421                 while(*s && *s!=_T('/'))
422                         *d++ = *s++;
423
424                 while(*s == _T('/'))
425                         s++;
426
427                 *d++ = _T('/');
428                 *d = _T('\0');
429
430                 read_directory(entry, buffer, sortOrder);
431
432                 if (entry->down)
433                         entry->expanded = TRUE;
434
435                 if (!*s)
436                         break;
437
438                 entry = find_entry_unix(entry, s);
439         }
440
441         return entry;
442 }
443
444 #endif
445
446
447 /* directories first... */
448 static int compareType(const WIN32_FIND_DATA* fd1, const WIN32_FIND_DATA* fd2)
449 {
450         int dir1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
451         int dir2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
452
453         return dir2==dir1? 0: dir2<dir1? -1: 1;
454 }
455
456
457 static int compareName(const void* arg1, const void* arg2)
458 {
459         const WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
460         const WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
461
462         int cmp = compareType(fd1, fd2);
463         if (cmp)
464                 return cmp;
465
466         return lstrcmpi(fd1->cFileName, fd2->cFileName);
467 }
468
469 static int compareExt(const void* arg1, const void* arg2)
470 {
471         const WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
472         const WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
473         const TCHAR *name1, *name2, *ext1, *ext2;
474
475         int cmp = compareType(fd1, fd2);
476         if (cmp)
477                 return cmp;
478
479         name1 = fd1->cFileName;
480         name2 = fd2->cFileName;
481
482         ext1 = _tcsrchr(name1, _T('.'));
483         ext2 = _tcsrchr(name2, _T('.'));
484
485         if (ext1)
486                 ext1++;
487         else
488                 ext1 = _T("");
489
490         if (ext2)
491                 ext2++;
492         else
493                 ext2 = _T("");
494
495         cmp = lstrcmpi(ext1, ext2);
496         if (cmp)
497                 return cmp;
498
499         return lstrcmpi(name1, name2);
500 }
501
502 static int compareSize(const void* arg1, const void* arg2)
503 {
504         WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
505         WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
506
507         int cmp = compareType(fd1, fd2);
508         if (cmp)
509                 return cmp;
510
511         cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
512
513         if (cmp < 0)
514                 return -1;
515         else if (cmp > 0)
516                 return 1;
517
518         cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
519
520         return cmp<0? -1: cmp>0? 1: 0;
521 }
522
523 static int compareDate(const void* arg1, const void* arg2)
524 {
525         WIN32_FIND_DATA* fd1 = &(*(Entry**)arg1)->data;
526         WIN32_FIND_DATA* fd2 = &(*(Entry**)arg2)->data;
527
528         int cmp = compareType(fd1, fd2);
529         if (cmp)
530                 return cmp;
531
532         return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
533 }
534
535
536 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
537         compareName,    /* SORT_NAME */
538         compareExt,             /* SORT_EXT */
539         compareSize,    /* SORT_SIZE */
540         compareDate             /* SORT_DATE */
541 };
542
543
544 static void SortDirectory(Entry* parent, SORT_ORDER sortOrder)
545 {
546         Entry* entry = parent->down;
547         Entry** array, **p;
548         int len;
549
550         len = 0;
551         for(entry=parent->down; entry; entry=entry->next)
552                 len++;
553
554         if (len) {
555                 array = (Entry**) alloca(len*sizeof(Entry*));
556
557                 p = array;
558                 for(entry=parent->down; entry; entry=entry->next)
559                         *p++ = entry;
560
561                 /* call qsort with the appropriate compare function */
562                 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
563
564                 parent->down = array[0];
565
566                 for(p=array; --len; p++)
567                         p[0]->next = p[1];
568
569                 (*p)->next = 0;
570         }
571 }
572
573
574 static void read_directory(Entry* parent, LPCTSTR path, int sortOrder)
575 {
576         TCHAR buffer[MAX_PATH];
577         Entry* entry;
578         LPCTSTR s;
579         PTSTR d;
580
581 #if !defined(_NO_EXTENSIONS) && defined(__linux__)
582         if (parent->unix_dir)
583         {
584                 read_directory_unix(parent, path);
585
586                 if (Globals.prescan_node) {
587                         s = path;
588                         d = buffer;
589
590                         while(*s)
591                                 *d++ = *s++;
592
593                         *d++ = _T('/');
594
595                         for(entry=parent->down; entry; entry=entry->next)
596                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
597                                         lstrcpy(d, entry->data.cFileName);
598                                         read_directory_unix(entry, buffer);
599                                         SortDirectory(entry, sortOrder);
600                                 }
601                 }
602         }
603         else
604 #endif
605         {
606                 read_directory_win(parent, path);
607
608                 if (Globals.prescan_node) {
609                         s = path;
610                         d = buffer;
611
612                         while(*s)
613                                 *d++ = *s++;
614
615                         *d++ = _T('\\');
616
617                         for(entry=parent->down; entry; entry=entry->next)
618                                 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
619                                         lstrcpy(d, entry->data.cFileName);
620                                         read_directory_win(entry, buffer);
621                                         SortDirectory(entry, sortOrder);
622                                 }
623                 }
624         }
625
626         SortDirectory(parent, sortOrder);
627 }
628
629
630 static ChildWnd* alloc_child_window(LPCTSTR path)
631 {
632         TCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
633         ChildWnd* child = (ChildWnd*) malloc(sizeof(ChildWnd));
634         Root* root = &child->root;
635         Entry* entry;
636
637         memset(child, 0, sizeof(ChildWnd));
638
639         child->left.treePane = TRUE;
640         child->left.visible_cols = 0;
641
642         child->right.treePane = FALSE;
643 #ifndef _NO_EXTENSIONS
644         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS;
645 #else
646         child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES;
647 #endif
648
649         child->pos.length = sizeof(WINDOWPLACEMENT);
650         child->pos.flags = 0;
651         child->pos.showCmd = SW_SHOWNORMAL;
652         child->pos.rcNormalPosition.left = CW_USEDEFAULT;
653         child->pos.rcNormalPosition.top = CW_USEDEFAULT;
654         child->pos.rcNormalPosition.right = CW_USEDEFAULT;
655         child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
656
657         child->focus_pane = 0;
658         child->split_pos = 200;
659         child->sortOrder = SORT_NAME;
660         child->header_wdths_ok = FALSE;
661
662         lstrcpy(child->path, path);
663
664         _tsplitpath(path, drv, dir, name, ext);
665
666 #if !defined(_NO_EXTENSIONS) && defined(__linux__)
667         if (*path == '/')
668         {
669                 root->drive_type = GetDriveType(path);
670
671                 lstrcat(drv, _T("/"));
672                 lstrcpy(root->volname, _T("root fs"));
673                 root->fs_flags = 0;
674                 lstrcpy(root->fs, _T("unixfs"));
675
676                 lstrcpy(root->path, _T("/"));
677                 entry = read_tree_unix(root, path, child->sortOrder);
678         }
679         else
680 #endif
681         {
682                 root->drive_type = GetDriveType(path);
683
684                 lstrcat(drv, _T("\\"));
685                 GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
686
687                 lstrcpy(root->path, drv);
688                 entry = read_tree_win(root, path, child->sortOrder);
689         }
690
691         /*@@lstrcpy(root->entry.data.cFileName, drv); */
692         wsprintf(root->entry.data.cFileName, _T("%s - %s"), drv, root->fs);
693
694         root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
695
696         child->left.root = &root->entry;
697
698         set_curdir(child, entry);
699
700         return child;
701 }
702
703
704 /* recursively free all child entries */
705 static void free_entries(Entry* parent)
706 {
707         Entry *entry, *next=parent->down;
708
709         if (next) {
710                 parent->down = 0;
711
712                 do {
713                         entry = next;
714                         next = entry->next;
715
716                         free_entries(entry);
717                         free(entry);
718                 } while(next);
719         }
720 }
721
722 /* free all memory associated with a child window */
723 static void free_child_window(ChildWnd* child)
724 {
725         free_entries(&child->root.entry);
726         free(child);
727 }
728
729
730 /* get full path of specified directory entry */
731 static void get_path(Entry* dir, PTSTR path)
732 {
733         Entry* entry;
734         int len = 0;
735         int level = 0;
736
737         for(entry=dir; entry; level++) {
738                 LPCTSTR name = entry->data.cFileName;
739                 LPCTSTR s = name;
740                 int l;
741
742                 for(l=0; *s && *s!=_T('/') && *s!=_T('\\'); s++)
743                         l++;
744
745                 if (entry->up) {
746                         memmove(path+l+1, path, len*sizeof(TCHAR));
747                         memcpy(path+1, name, l*sizeof(TCHAR));
748                         len += l+1;
749
750 #ifndef _NO_EXTENSIONS
751                         if (entry->unix_dir)
752                                 path[0] = _T('/');
753                         else
754 #endif
755                                 path[0] = _T('\\');
756
757                         entry = entry->up;
758                 } else {
759                         memmove(path+l, path, len*sizeof(TCHAR));
760                         memcpy(path, name, l*sizeof(TCHAR));
761                         len += l;
762                         break;
763                 }
764         }
765
766         if (!level) {
767 #ifndef _NO_EXTENSIONS
768                 if (entry->unix_dir)
769                         path[len++] = _T('/');
770                 else
771 #endif
772                         path[len++] = _T('\\');
773         }
774
775         path[len] = _T('\0');
776 }
777
778
779 static void resize_frame_rect(HWND hwnd, PRECT prect)
780 {
781         int new_top;
782         RECT rt;
783
784         if (IsWindowVisible(Globals.htoolbar)) {
785                 SendMessage(Globals.htoolbar, WM_SIZE, 0, 0);
786                 GetClientRect(Globals.htoolbar, &rt);
787                 prect->top = rt.bottom+3;
788                 prect->bottom -= rt.bottom+3;
789         }
790
791         if (IsWindowVisible(Globals.hdrivebar)) {
792                 SendMessage(Globals.hdrivebar, WM_SIZE, 0, 0);
793                 GetClientRect(Globals.hdrivebar, &rt);
794                 new_top = --prect->top + rt.bottom+3;
795                 MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
796                 prect->top = new_top;
797                 prect->bottom -= rt.bottom+2;
798         }
799
800         if (IsWindowVisible(Globals.hstatusbar)) {
801                 int parts[] = {300, 500};
802
803                 SendMessage(Globals.hstatusbar, WM_SIZE, 0, 0);
804                 SendMessage(Globals.hstatusbar, SB_SETPARTS, 2, (LPARAM)&parts);
805                 GetClientRect(Globals.hstatusbar, &rt);
806                 prect->bottom -= rt.bottom;
807         }
808
809         MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
810 }
811
812 static void resize_frame(HWND hwnd, int cx, int cy)
813 {
814         RECT rect;
815
816         rect.left   = 0;
817         rect.top    = 0;
818         rect.right  = cx;
819         rect.bottom = cy;
820
821         resize_frame_rect(hwnd, &rect);
822 }
823
824 static void resize_frame_client(HWND hwnd)
825 {
826         RECT rect;
827
828         GetClientRect(hwnd, &rect);
829
830         resize_frame_rect(hwnd, &rect);
831 }
832
833
834 static HHOOK hcbthook;
835 static ChildWnd* newchild = NULL;
836
837 LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
838 {
839         if (code==HCBT_CREATEWND && newchild) {
840                 ChildWnd* child = newchild;
841                 newchild = NULL;
842
843                 child->hwnd = (HWND) wparam;
844                 SetWindowLong(child->hwnd, GWL_USERDATA, (LPARAM)child);
845         }
846
847         return CallNextHookEx(hcbthook, code, wparam, lparam);
848 }
849
850 static HWND create_child_window(ChildWnd* child)
851 {
852         MDICREATESTRUCT mcs;
853         int idx;
854
855         mcs.szClass = WINEFILETREE;
856         mcs.szTitle = (LPTSTR)child->path;
857         mcs.hOwner  = Globals.hInstance;
858         mcs.x       = child->pos.rcNormalPosition.left;
859         mcs.y       = child->pos.rcNormalPosition.top;
860         mcs.cx      = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
861         mcs.cy      = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
862         mcs.style   = 0;
863         mcs.lParam  = 0;
864
865         hcbthook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
866
867         newchild = child;
868         child->hwnd = (HWND) SendMessage(Globals.hmdiclient, WM_MDICREATE, 0, (LPARAM)&mcs);
869         if (!child->hwnd)
870                 return 0;
871
872         UnhookWindowsHookEx(hcbthook);
873
874         idx = ListBox_FindItemData(child->left.hwnd, ListBox_GetCurSel(child->left.hwnd), child->left.cur);
875         ListBox_SetCurSel(child->left.hwnd, idx);
876
877         return child->hwnd;
878 }
879
880
881 struct ExecuteDialog {
882         TCHAR   cmd[MAX_PATH];
883         int             cmdshow;
884 };
885
886
887 static BOOL CALLBACK ExecuteDialogWndProg(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
888 {
889         static struct ExecuteDialog* dlg;
890
891         switch(nmsg) {
892                 case WM_INITDIALOG:
893                         dlg = (struct ExecuteDialog*) lparam;
894                         return 1;
895
896                 case WM_COMMAND: {
897                         int id = (int)wparam;
898
899                         if (id == IDOK) {
900                                 GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, MAX_PATH);
901                                 dlg->cmdshow = Button_GetState(GetDlgItem(hwnd,214))&BST_CHECKED?
902                                                                                                 SW_SHOWMINIMIZED: SW_SHOWNORMAL;
903                                 EndDialog(hwnd, id);
904                         } else if (id == IDCANCEL)
905                                 EndDialog(hwnd, id);
906
907                         return 1;}
908         }
909
910         return 0;
911 }
912
913
914 #ifndef _NO_EXTENSIONS
915
916 static struct FullScreenParameters {
917         BOOL    mode;
918         RECT    orgPos;
919         BOOL    wasZoomed;
920 } g_fullscreen = {
921         FALSE   /* mode */
922 };
923
924 void frame_get_clientspace(HWND hwnd, PRECT prect)
925 {
926         RECT rt;
927
928         if (!IsIconic(hwnd))
929                 GetClientRect(hwnd, prect);
930         else {
931                 WINDOWPLACEMENT wp;
932
933                 GetWindowPlacement(hwnd, &wp);
934
935                 prect->left = prect->top = 0;
936                 prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
937                                                 2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
938                 prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
939                                                 2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
940                                                 GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
941         }
942
943         if (IsWindowVisible(Globals.htoolbar)) {
944                 GetClientRect(Globals.htoolbar, &rt);
945                 prect->top += rt.bottom+2;
946         }
947
948         if (IsWindowVisible(Globals.hdrivebar)) {
949                 GetClientRect(Globals.hdrivebar, &rt);
950                 prect->top += rt.bottom+2;
951         }
952
953         if (IsWindowVisible(Globals.hstatusbar)) {
954                 GetClientRect(Globals.hstatusbar, &rt);
955                 prect->bottom -= rt.bottom;
956         }
957 }
958
959 static BOOL toggle_fullscreen(HWND hwnd)
960 {
961         RECT rt;
962
963         if ((g_fullscreen.mode=!g_fullscreen.mode)) {
964                 GetWindowRect(hwnd, &g_fullscreen.orgPos);
965                 g_fullscreen.wasZoomed = IsZoomed(hwnd);
966
967                 Frame_CalcFrameClient(hwnd, &rt);
968                 ClientToScreen(hwnd, (LPPOINT)&rt.left);
969                 ClientToScreen(hwnd, (LPPOINT)&rt.right);
970
971                 rt.left = g_fullscreen.orgPos.left-rt.left;
972                 rt.top = g_fullscreen.orgPos.top-rt.top;
973                 rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
974                 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
975
976                 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
977         } else {
978                 MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
979                                                         g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
980                                                         g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
981
982                 if (g_fullscreen.wasZoomed)
983                         ShowWindow(hwnd, WS_MAXIMIZE);
984         }
985
986         return g_fullscreen.mode;
987 }
988
989 static void fullscreen_move(HWND hwnd)
990 {
991         RECT rt, pos;
992         GetWindowRect(hwnd, &pos);
993
994         Frame_CalcFrameClient(hwnd, &rt);
995         ClientToScreen(hwnd, (LPPOINT)&rt.left);
996         ClientToScreen(hwnd, (LPPOINT)&rt.right);
997
998         rt.left = pos.left-rt.left;
999         rt.top = pos.top-rt.top;
1000         rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
1001         rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
1002
1003         MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1004 }
1005
1006 #endif
1007
1008
1009 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
1010 {
1011         BOOL vis = IsWindowVisible(hchild);
1012
1013         CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
1014
1015         ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
1016
1017 #ifndef _NO_EXTENSIONS
1018         if (g_fullscreen.mode)
1019                 fullscreen_move(hwnd);
1020 #endif
1021
1022         resize_frame_client(hwnd);
1023 }
1024
1025 BOOL activate_drive_window(LPCTSTR path)
1026 {
1027         TCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
1028         HWND child_wnd;
1029
1030         _tsplitpath(path, drv1, 0, 0, 0);
1031
1032         /* search for a already open window for the same drive */
1033         for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
1034                 ChildWnd* child = (ChildWnd*) GetWindowLong(child_wnd, GWL_USERDATA);
1035
1036                 if (child) {
1037                         _tsplitpath(child->root.path, drv2, 0, 0, 0);
1038
1039                         if (!lstrcmpi(drv2, drv1)) {
1040                                 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
1041
1042                                 if (IsMinimized(child_wnd))
1043                                         ShowWindow(child_wnd, SW_SHOWNORMAL);
1044
1045                                 return TRUE;
1046                         }
1047                 }
1048         }
1049
1050         return FALSE;
1051 }
1052
1053 LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1054 {
1055         switch(nmsg) {
1056                 case WM_CLOSE:
1057                         DestroyWindow(hwnd);
1058                         break;
1059
1060                 case WM_DESTROY:
1061                         PostQuitMessage(0);
1062                         break;
1063
1064                 case WM_COMMAND: {
1065                         UINT cmd = LOWORD(wparam);
1066                         HWND hwndClient = (HWND) SendMessage(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
1067
1068                         if (SendMessage(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
1069                                 break;
1070
1071                         if (cmd>=ID_DRIVE_FIRST && cmd<=ID_DRIVE_FIRST+0xFF) {
1072                                 TCHAR drv[_MAX_DRIVE], path[MAX_PATH];
1073                                 ChildWnd* child;
1074                                 LPCTSTR root = Globals.drives;
1075                                 int i;
1076
1077                                 for(i=cmd-ID_DRIVE_FIRST; i--; root++)
1078                                         while(*root)
1079                                                 root++;
1080
1081                                 if (activate_drive_window(root))
1082                                         return 0;
1083
1084                                 _tsplitpath(root, drv, 0, 0, 0);
1085
1086                                 if (!SetCurrentDirectory(drv)) {
1087                                         display_error(hwnd, GetLastError());
1088                                         return 0;
1089                                 }
1090
1091                                 GetCurrentDirectory(MAX_PATH, path); /*@@ letztes Verzeichnis pro Laufwerk speichern */
1092                                 child = alloc_child_window(path);
1093
1094                                 if (!create_child_window(child))
1095                                         free(child);
1096                         } else switch(cmd) {
1097                                 case ID_FILE_EXIT:
1098                                         PostQuitMessage(0);
1099                                         break;
1100
1101                                 case ID_WINDOW_NEW: {
1102                                         TCHAR path[MAX_PATH];
1103                                         ChildWnd* child;
1104
1105                                         GetCurrentDirectory(MAX_PATH, path);
1106                                         child = alloc_child_window(path);
1107
1108                                         if (!create_child_window(child))
1109                                                 free(child);
1110                                         break;}
1111
1112                                 case ID_WINDOW_CASCADE:
1113                                         SendMessage(Globals.hmdiclient, WM_MDICASCADE, 0, 0);
1114                                         break;
1115
1116                                 case ID_WINDOW_TILE_HORZ:
1117                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
1118                                         break;
1119
1120                                 case ID_WINDOW_TILE_VERT:
1121                                         SendMessage(Globals.hmdiclient, WM_MDITILE, MDITILE_VERTICAL, 0);
1122                                         break;
1123
1124                                 case ID_WINDOW_ARRANGE:
1125                                         SendMessage(Globals.hmdiclient, WM_MDIICONARRANGE, 0, 0);
1126                                         break;
1127
1128                                 case ID_VIEW_TOOL_BAR:
1129                                         toggle_child(hwnd, cmd, Globals.htoolbar);
1130                                         break;
1131
1132                                 case ID_VIEW_DRIVE_BAR:
1133                                         toggle_child(hwnd, cmd, Globals.hdrivebar);
1134                                         break;
1135
1136                                 case ID_VIEW_STATUSBAR:
1137                                         toggle_child(hwnd, cmd, Globals.hstatusbar);
1138                                         break;
1139
1140                                 case ID_EXECUTE: {
1141                                         struct ExecuteDialog dlg = {{0}};
1142                                         if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_EXECUTE), hwnd, ExecuteDialogWndProg, (LPARAM)&dlg) == IDOK) {
1143                                                 HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, dlg.cmd/*file*/, NULL/*parameters*/, NULL/*dir*/, dlg.cmdshow);
1144
1145                                                 if ((int)hinst <= 32)
1146                                                         display_error(hwnd, GetLastError());
1147                                         }
1148                                         break;}
1149
1150                                 case ID_HELP:
1151                                         WinHelp(hwnd, _T("winfile"), HELP_INDEX, 0);
1152                                         break;
1153
1154 #ifndef _NO_EXTENSIONS
1155                                 case ID_VIEW_FULLSCREEN:
1156                                         CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
1157                                         break;
1158
1159 #ifdef __linux__
1160                                 case ID_DRIVE_UNIX_FS: {
1161                                         TCHAR path[MAX_PATH];
1162                                         ChildWnd* child;
1163
1164                                         if (activate_drive_window(_T("/")))
1165                                                 break;
1166
1167                                         getcwd(path, MAX_PATH);
1168                                         child = alloc_child_window(path);
1169
1170                                         if (!create_child_window(child))
1171                                                 free(child);
1172                                         break;}
1173 #endif
1174 #endif
1175
1176                                 /*TODO: There are even more menu items! */
1177
1178 #ifndef _NO_EXTENSIONS
1179                                 case ID_LICENSE:
1180                                         WineLicense(Globals.hMainWnd);
1181                                         break;
1182
1183                                 case ID_NO_WARRANTY:
1184                                         WineWarranty(Globals.hMainWnd);
1185                                         break;
1186
1187                                 case ID_ABOUT_WINE:
1188                                         ShellAbout(hwnd, _T("WINE"), _T("Winefile"), 0);
1189                                         break;
1190 #endif
1191
1192                                 default:
1193                                         /*@@if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
1194                                                 STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
1195                                         else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
1196                                                 (cmd<SC_SIZE || cmd>SC_RESTORE))
1197                                                 MessageBox(hwnd, _T("Not yet implemented"), _T("Winefile"), MB_OK);
1198
1199                                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
1200                         }
1201                         break;}
1202
1203                 case WM_SIZE:
1204                         resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
1205                         break;  /* do not pass message to DefFrameProc */
1206
1207 #ifndef _NO_EXTENSIONS
1208                 case WM_GETMINMAXINFO: {
1209                         LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
1210
1211                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
1212                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
1213                         break;}
1214
1215                 case FRM_CALC_CLIENT:
1216                         frame_get_clientspace(hwnd, (PRECT)lparam);
1217                         return TRUE;
1218 #endif
1219
1220                 default:
1221                         return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
1222         }
1223
1224         return 0;
1225 }
1226
1227
1228 const static LPTSTR g_pos_names[COLUMNS] = {
1229         _T(""),                 /* symbol */
1230         _T("Name"),
1231         _T("Size"),
1232         _T("CDate"),
1233 #ifndef _NO_EXTENSIONS
1234         _T("ADate"),
1235         _T("MDate"),
1236         _T("Index/Inode"),
1237         _T("Links"),
1238 #endif
1239         _T("Attributes"),
1240 #ifndef _NO_EXTENSIONS
1241         _T("Security")
1242 #endif
1243 };
1244
1245 const static int g_pos_align[] = {
1246         0,
1247         HDF_LEFT,       /* Name */
1248         HDF_RIGHT,      /* Size */
1249         HDF_LEFT,       /* CDate */
1250 #ifndef _NO_EXTENSIONS
1251         HDF_LEFT,       /* ADate */
1252         HDF_LEFT,       /* MDate */
1253         HDF_LEFT,       /* Index */
1254         HDF_CENTER,     /* Links */
1255 #endif
1256         HDF_CENTER,     /* Attributes */
1257 #ifndef _NO_EXTENSIONS
1258         HDF_LEFT        /* Security */
1259 #endif
1260 };
1261
1262 static void resize_tree(ChildWnd* child, int cx, int cy)
1263 {
1264         HDWP hdwp = BeginDeferWindowPos(4);
1265         RECT rt;
1266
1267         rt.left   = 0;
1268         rt.top    = 0;
1269         rt.right  = cx;
1270         rt.bottom = cy;
1271
1272         cx = child->split_pos + SPLIT_WIDTH/2;
1273
1274 #ifndef _NO_EXTENSIONS
1275         {
1276                 WINDOWPOS wp;
1277                 HD_LAYOUT hdl;
1278
1279                 hdl.prc   = &rt;
1280                 hdl.pwpos = &wp;
1281
1282                 Header_Layout(child->left.hwndHeader, &hdl);
1283
1284                 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
1285                                                 wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
1286                 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
1287                                                 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
1288         }
1289 #endif
1290
1291         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);
1292         DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
1293
1294         EndDeferWindowPos(hdwp);
1295 }
1296
1297
1298 #ifndef _NO_EXTENSIONS
1299
1300 static HWND create_header(HWND parent, Pane* pane, int id)
1301 {
1302         HD_ITEM hdi = {HDI_TEXT|HDI_WIDTH|HDI_FORMAT};
1303         int idx;
1304
1305         HWND hwnd = CreateWindow(WC_HEADER, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ/*TODO: |HDS_BUTTONS + sort orders*/,
1306                                                                 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
1307         if (!hwnd)
1308                 return 0;
1309
1310         SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), FALSE);
1311
1312         for(idx=0; idx<COLUMNS; idx++) {
1313                 hdi.pszText = g_pos_names[idx];
1314                 hdi.fmt = HDF_STRING | g_pos_align[idx];
1315                 hdi.cxy = pane->widths[idx];
1316                 Header_InsertItem(hwnd, idx, &hdi);
1317         }
1318
1319         return hwnd;
1320 }
1321
1322 #endif
1323
1324
1325 static void init_output(HWND hwnd)
1326 {
1327         TCHAR b[16];
1328         HFONT old_font;
1329         HDC hdc = GetDC(hwnd);
1330
1331         if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, _T("1000"), 0, b, 16) > 4)
1332                 Globals.num_sep = b[1];
1333         else
1334                 Globals.num_sep = _T('.');
1335
1336         old_font = SelectFont(hdc, Globals.hfont);
1337         GetTextExtentPoint32(hdc, _T(" "), 1, &Globals.spaceSize);
1338         SelectFont(hdc, old_font);
1339         ReleaseDC(hwnd, hdc);
1340 }
1341
1342 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
1343
1344
1345 /* calculate prefered width for all visible columns */
1346
1347 static BOOL calc_widths(Pane* pane, BOOL anyway)
1348 {
1349         int col, x, cx, spc=3*Globals.spaceSize.cx;
1350         int entries = ListBox_GetCount(pane->hwnd);
1351         int orgWidths[COLUMNS];
1352         int orgPositions[COLUMNS+1];
1353         HFONT hfontOld;
1354         HDC hdc;
1355         int cnt;
1356
1357         if (!anyway) {
1358                 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
1359                 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
1360         }
1361
1362         for(col=0; col<COLUMNS; col++)
1363                 pane->widths[col] = 0;
1364
1365         hdc = GetDC(pane->hwnd);
1366         hfontOld = SelectFont(hdc, Globals.hfont);
1367
1368         for(cnt=0; cnt<entries; cnt++) {
1369                 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
1370
1371                 DRAWITEMSTRUCT dis;
1372
1373                 dis.CtlType               = 0;
1374                 dis.CtlID                 = 0;
1375                 dis.itemID                = 0;
1376                 dis.itemAction    = 0;
1377                 dis.itemState     = 0;
1378                 dis.hwndItem      = pane->hwnd;
1379                 dis.hDC                   = hdc;
1380                 dis.rcItem.left   = 0;
1381                 dis.rcItem.top    = 0;
1382                 dis.rcItem.right  = 0;
1383                 dis.rcItem.bottom = 0;
1384                 /*dis.itemData    = 0; */
1385
1386                 draw_item(pane, &dis, entry, COLUMNS);
1387         }
1388
1389         SelectObject(hdc, hfontOld);
1390         ReleaseDC(pane->hwnd, hdc);
1391
1392         x = 0;
1393         for(col=0; col<COLUMNS; col++) {
1394                 pane->positions[col] = x;
1395                 cx = pane->widths[col];
1396
1397                 if (cx) {
1398                         cx += spc;
1399
1400                         if (cx < IMAGE_WIDTH)
1401                                 cx = IMAGE_WIDTH;
1402
1403                         pane->widths[col] = cx;
1404                 }
1405
1406                 x += cx;
1407         }
1408
1409         pane->positions[COLUMNS] = x;
1410
1411         ListBox_SetHorizontalExtent(pane->hwnd, x);
1412
1413         /* no change? */
1414         if (!memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
1415                 return FALSE;
1416
1417         /* don't move, if only collapsing an entry */
1418         if (!anyway && pane->widths[0]<orgWidths[0] &&
1419                 !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
1420                 pane->widths[0] = orgWidths[0];
1421                 memcpy(pane->positions, orgPositions, sizeof(orgPositions));
1422
1423                 return FALSE;
1424         }
1425
1426         InvalidateRect(pane->hwnd, 0, TRUE);
1427
1428         return TRUE;
1429 }
1430
1431
1432 /* calculate one prefered column width */
1433
1434 static void calc_single_width(Pane* pane, int col)
1435 {
1436         HFONT hfontOld;
1437         int x, cx;
1438         int entries = ListBox_GetCount(pane->hwnd);
1439         int cnt;
1440         HDC hdc;
1441
1442         pane->widths[col] = 0;
1443
1444         hdc = GetDC(pane->hwnd);
1445         hfontOld = SelectFont(hdc, Globals.hfont);
1446
1447         for(cnt=0; cnt<entries; cnt++) {
1448                 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
1449                 DRAWITEMSTRUCT dis;
1450
1451                 dis.CtlType               = 0;
1452                 dis.CtlID                 = 0;
1453                 dis.itemID                = 0;
1454                 dis.itemAction    = 0;
1455                 dis.itemState     = 0;
1456                 dis.hwndItem      = pane->hwnd;
1457                 dis.hDC                   = hdc;
1458                 dis.rcItem.left   = 0;
1459                 dis.rcItem.top    = 0;
1460                 dis.rcItem.right  = 0;
1461                 dis.rcItem.bottom = 0;
1462                 /*dis.itemData    = 0; */
1463
1464                 draw_item(pane, &dis, entry, col);
1465         }
1466
1467         SelectObject(hdc, hfontOld);
1468         ReleaseDC(pane->hwnd, hdc);
1469
1470         cx = pane->widths[col];
1471
1472         if (cx) {
1473                 cx += 3*Globals.spaceSize.cx;
1474
1475                 if (cx < IMAGE_WIDTH)
1476                         cx = IMAGE_WIDTH;
1477         }
1478
1479         pane->widths[col] = cx;
1480
1481         x = pane->positions[col] + cx;
1482
1483         for(; col<COLUMNS; ) {
1484                 pane->positions[++col] = x;
1485                 x += pane->widths[col];
1486         }
1487
1488         ListBox_SetHorizontalExtent(pane->hwnd, x);
1489 }
1490
1491
1492 /* insert listbox entries after index idx */
1493
1494 static void insert_entries(Pane* pane, Entry* parent, int idx)
1495 {
1496         Entry* entry = parent;
1497
1498         if (!entry)
1499                 return;
1500
1501         ShowWindow(pane->hwnd, SW_HIDE);
1502
1503         for(; entry; entry=entry->next) {
1504 #ifndef _LEFT_FILES
1505                 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
1506                         continue;
1507 #endif
1508
1509                 /* don't display entries "." and ".." in the left pane */
1510                 if (pane->treePane && (entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
1511                                 && entry->data.cFileName[0]==_T('.'))
1512                         if (
1513 #ifndef _NO_EXTENSIONS
1514                                 entry->data.cFileName[1]==_T('\0') ||
1515 #endif
1516                                 (entry->data.cFileName[1]==_T('.') && entry->data.cFileName[2]==_T('\0')))
1517                                 continue;
1518
1519                 if (idx != -1)
1520                         idx++;
1521
1522                 ListBox_InsertItemData(pane->hwnd, idx, entry);
1523
1524                 if (pane->treePane && entry->expanded)
1525                         insert_entries(pane, entry->down, idx);
1526         }
1527
1528         ShowWindow(pane->hwnd, SW_SHOW);
1529 }
1530
1531
1532 static WNDPROC g_orgTreeWndProc;
1533
1534 static void create_tree_window(HWND parent, Pane* pane, int id, int id_header)
1535 {
1536         static int s_init = 0;
1537         Entry* entry = pane->root;
1538
1539         pane->hwnd = CreateWindow(_T("ListBox"), _T(""), WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
1540                                                                 LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
1541                                                                 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
1542
1543         SetWindowLong(pane->hwnd, GWL_USERDATA, (LPARAM)pane);
1544         g_orgTreeWndProc = SubclassWindow(pane->hwnd, TreeWndProc);
1545
1546         SendMessage(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
1547
1548         /* insert entries into listbox */
1549         if (entry)
1550                 insert_entries(pane, entry, -1);
1551
1552         /* calculate column widths */
1553         if (!s_init) {
1554                 s_init = 1;
1555                 init_output(pane->hwnd);
1556         }
1557
1558         calc_widths(pane, TRUE);
1559
1560 #ifndef _NO_EXTENSIONS
1561         pane->hwndHeader = create_header(parent, pane, id_header);
1562 #endif
1563 }
1564
1565
1566 static void InitChildWindow(ChildWnd* child)
1567 {
1568         create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT);
1569         create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT);
1570 }
1571
1572
1573 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
1574 {
1575         SYSTEMTIME systime;
1576         FILETIME lft;
1577         int len = 0;
1578
1579         *buffer = _T('\0');
1580
1581         if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1582                 return;
1583
1584         if (!FileTimeToLocalFileTime(ft, &lft))
1585                 {err: _tcscpy(buffer,_T("???")); return;}
1586
1587         if (!FileTimeToSystemTime(&lft, &systime))
1588                 goto err;
1589
1590         if (visible_cols & COL_DATE) {
1591                 len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
1592                 if (!len)
1593                         goto err;
1594         }
1595
1596         if (visible_cols & COL_TIME) {
1597                 if (len)
1598                         buffer[len-1] = ' ';
1599
1600                 buffer[len++] = ' ';
1601
1602                 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
1603                         buffer[len] = _T('\0');
1604         }
1605 }
1606
1607
1608 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
1609 {
1610         RECT rt = {0};
1611
1612         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
1613
1614         if (rt.right > pane->widths[col])
1615                 pane->widths[col] = rt.right;
1616 }
1617
1618 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
1619 {
1620         RECT rt = {0};
1621
1622 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
1623         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
1624
1625         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
1626         /*@@ rt (0,0) ??? */
1627
1628         if (rt.right > pane->widths[col])
1629                 pane->widths[col] = rt.right;
1630 }
1631
1632
1633 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str, DWORD flags)
1634 {
1635         int x = dis->rcItem.left;
1636         RECT rt;
1637
1638         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
1639         rt.top    = dis->rcItem.top;
1640         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
1641         rt.bottom = dis->rcItem.bottom;
1642
1643         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
1644 }
1645
1646 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
1647 {
1648         int x = dis->rcItem.left;
1649         RECT rt;
1650
1651         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
1652         rt.top    = dis->rcItem.top;
1653         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
1654         rt.bottom = dis->rcItem.bottom;
1655
1656 /*      DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
1657         DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
1658
1659         DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
1660 }
1661
1662 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
1663 {
1664         int x = dis->rcItem.left;
1665         RECT rt;
1666         LPCTSTR s = str;
1667         TCHAR b[128];
1668         LPTSTR d = b;
1669         int pos;
1670
1671         rt.left   = x+pane->positions[col]+Globals.spaceSize.cx;
1672         rt.top    = dis->rcItem.top;
1673         rt.right  = x+pane->positions[col+1]-Globals.spaceSize.cx;
1674         rt.bottom = dis->rcItem.bottom;
1675
1676         if (*s)
1677                 *d++ = *s++;
1678
1679         /* insert number separator characters */
1680         pos = lstrlen(s) % 3;
1681
1682         while(*s)
1683                 if (pos--)
1684                         *d++ = *s++;
1685                 else {
1686                         *d++ = Globals.num_sep;
1687                         pos = 3;
1688                 }
1689
1690         DrawText(dis->hDC, b, d-b, &rt, DT_RIGHT|DT_SINGLELINE|DT_NOPREFIX|DT_END_ELLIPSIS);
1691 }
1692
1693
1694 static int is_exe_file(LPCTSTR ext)
1695 {
1696         const static LPCTSTR executable_extensions[] = {
1697                 _T("COM"),
1698                 _T("EXE"),
1699                 _T("BAT"),
1700                 _T("CMD"),
1701 #ifndef _NO_EXTENSIONS
1702                 _T("CMM"),
1703                 _T("BTM"),
1704                 _T("AWK"),
1705 #endif
1706                 0
1707         };
1708
1709         TCHAR ext_buffer[_MAX_EXT];
1710         const LPCTSTR* p;
1711         LPCTSTR s;
1712         LPTSTR d;
1713
1714         for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
1715                 d++;
1716
1717         for(p=executable_extensions; *p; p++)
1718                 if (!_tcscmp(ext_buffer, *p))
1719                         return 1;
1720
1721         return 0;
1722 }
1723
1724 static int is_registered_type(LPCTSTR ext)
1725 {
1726         /* TODO */
1727
1728         return 1;
1729 }
1730
1731
1732 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
1733 {
1734         TCHAR buffer[BUFFER_LEN];
1735         DWORD attrs;
1736         int visible_cols = pane->visible_cols;
1737         COLORREF bkcolor, textcolor;
1738         RECT focusRect = dis->rcItem;
1739         HBRUSH hbrush;
1740         enum IMAGE img;
1741         int img_pos, cx;
1742         int col = 0;
1743
1744         if (entry) {
1745                 attrs = entry->data.dwFileAttributes;
1746
1747                 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
1748                         if (entry->data.cFileName[0]==_T('.') && entry->data.cFileName[1]==_T('.')
1749                                         && entry->data.cFileName[2]==_T('\0'))
1750                                 img = IMG_FOLDER_UP;
1751 #ifndef _NO_EXTENSIONS
1752                         else if (entry->data.cFileName[0]==_T('.') && entry->data.cFileName[1]==_T('\0'))
1753                                 img = IMG_FOLDER_CUR;
1754 #endif
1755                         else if (
1756 #ifdef _NO_EXTENSIONS
1757                                          entry->expanded ||
1758 #endif
1759                                          (pane->treePane && (dis->itemState&ODS_FOCUS)))
1760                                 img = IMG_OPEN_FOLDER;
1761                         else
1762                                 img = IMG_FOLDER;
1763                 } else {
1764                         LPCTSTR ext = _tcsrchr(entry->data.cFileName, '.');
1765                         if (!ext)
1766                                 ext = _T("");
1767
1768                         if (is_exe_file(ext))
1769                                 img = IMG_EXECUTABLE;
1770                         else if (is_registered_type(ext))
1771                                 img = IMG_DOCUMENT;
1772                         else
1773                                 img = IMG_FILE;
1774                 }
1775         } else {
1776                 attrs = 0;
1777                 img = IMG_NONE;
1778         }
1779
1780         if (pane->treePane) {
1781                 if (entry) {
1782                         img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+Globals.spaceSize.cx);
1783
1784                         if (calcWidthCol == -1) {
1785                                 int x;
1786                                 int y = dis->rcItem.top + IMAGE_HEIGHT/2;
1787                                 Entry* up;
1788                                 RECT rt_clip;
1789                                 HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
1790                                 HRGN hrgn;
1791
1792                                 rt_clip.left   = dis->rcItem.left;
1793                                 rt_clip.top    = dis->rcItem.top;
1794                                 rt_clip.right  = dis->rcItem.left+pane->widths[col];
1795                                 rt_clip.bottom = dis->rcItem.bottom;
1796
1797                                 hrgn = CreateRectRgnIndirect(&rt_clip);
1798
1799                                 if (!GetClipRgn(dis->hDC, hrgn_org)) {
1800                                         DeleteObject(hrgn_org);
1801                                         hrgn_org = 0;
1802                                 }
1803
1804                                 /*                              HGDIOBJ holdPen = SelectObject(dis->hDC, GetStockObject(BLACK_PEN)); */
1805                                 ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
1806                                 DeleteObject(hrgn);
1807
1808                                 if ((up=entry->up) != NULL) {
1809                                         MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
1810                                         LineTo(dis->hDC, img_pos-2, y);
1811
1812                                         x = img_pos - IMAGE_WIDTH/2;
1813
1814                                         do {
1815                                                 x -= IMAGE_WIDTH+Globals.spaceSize.cx;
1816
1817                                                 if (up->next
1818 #ifndef _LEFT_FILES
1819                                                         && (up->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
1820 #endif
1821                                                         ) {
1822                                                         MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
1823                                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
1824                                                 }
1825                                         } while((up=up->up) != NULL);
1826                                 }
1827
1828                                 x = img_pos - IMAGE_WIDTH/2;
1829
1830                                 MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
1831                                 LineTo(dis->hDC, x, y);
1832
1833                                 if (entry->next
1834 #ifndef _LEFT_FILES
1835                                         && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
1836 #endif
1837                                         )
1838                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
1839
1840                                 if (entry->down && entry->expanded) {
1841                                         x += IMAGE_WIDTH+Globals.spaceSize.cx;
1842                                         MoveToEx(dis->hDC, x, dis->rcItem.top+IMAGE_HEIGHT, 0);
1843                                         LineTo(dis->hDC, x, dis->rcItem.bottom);
1844                                 }
1845
1846                                 SelectClipRgn(dis->hDC, hrgn_org);
1847                                 if (hrgn_org) DeleteObject(hrgn_org);
1848                                 /*                              SelectObject(dis->hDC, holdPen); */
1849                         } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
1850                                 int right = img_pos + IMAGE_WIDTH - Globals.spaceSize.cx;
1851
1852                                 if (right > pane->widths[col])
1853                                         pane->widths[col] = right;
1854                         }
1855                 } else  {
1856                         img_pos = dis->rcItem.left;
1857                 }
1858         } else {
1859                 img_pos = dis->rcItem.left;
1860
1861                 if (calcWidthCol==col || calcWidthCol==COLUMNS)
1862                         pane->widths[col] = IMAGE_WIDTH;
1863         }
1864
1865         if (calcWidthCol == -1) {
1866                 focusRect.left = img_pos -2;
1867
1868 #ifdef _NO_EXTENSIONS
1869                 if (pane->treePane && entry) {
1870                         RECT rt = {0};
1871
1872                         DrawText(dis->hDC, entry->data.cFileName, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
1873
1874                         focusRect.right = dis->rcItem.left+pane->positions[col+1]+Globals.spaceSize.cx + rt.right +2;
1875                 }
1876 #else
1877
1878                 if (attrs & FILE_ATTRIBUTE_COMPRESSED)
1879                         textcolor = COLOR_COMPRESSED;
1880                 else
1881 #endif
1882                         textcolor = RGB(0,0,0);
1883
1884                 if (dis->itemState & ODS_FOCUS) {
1885                         textcolor = RGB(255,255,255);
1886                         bkcolor = COLOR_SELECTION;
1887                 } else {
1888                         bkcolor = RGB(255,255,255);
1889                 }
1890
1891                 hbrush = CreateSolidBrush(bkcolor);
1892                 FillRect(dis->hDC, &focusRect, hbrush);
1893                 DeleteObject(hbrush);
1894
1895                 SetBkMode(dis->hDC, TRANSPARENT);
1896                 SetTextColor(dis->hDC, textcolor);
1897
1898                 cx = pane->widths[col];
1899
1900                 if (cx && img!=IMG_NONE) {
1901                         if (cx > IMAGE_WIDTH)
1902                                 cx = IMAGE_WIDTH;
1903
1904                         ImageList_DrawEx(Globals.himl, img, dis->hDC,
1905                                                                 img_pos, dis->rcItem.top, cx,
1906                                                                 IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
1907                 }
1908         }
1909
1910         if (!entry)
1911                 return;
1912
1913 #ifdef _NO_EXTENSIONS
1914         if (img >= IMG_FOLDER_UP)
1915                 return;
1916 #endif
1917
1918         col++;
1919
1920         /* ouput file name */
1921         if (calcWidthCol == -1)
1922                 output_text(pane, dis, col, entry->data.cFileName, 0);
1923         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1924                 calc_width(pane, dis, col, entry->data.cFileName);
1925
1926         col++;
1927
1928 #ifdef _NO_EXTENSIONS
1929   if (!pane->treePane) {
1930 #endif
1931
1932         /* display file size */
1933         if (visible_cols & COL_SIZE) {
1934 #ifdef _NO_EXTENSIONS
1935                 if (!(attrs&FILE_ATTRIBUTE_DIRECTORY))
1936 #endif
1937                 {
1938                         ULONGLONG size;
1939
1940                         size = ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow;
1941
1942                         _stprintf(buffer, _T("%") LONGLONGARG _T("d"), size);
1943
1944                         if (calcWidthCol == -1)
1945                                 output_number(pane, dis, col, buffer);
1946                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1947                                 calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
1948                 }
1949
1950                 col++;
1951         }
1952
1953         /* display file date */
1954         if (visible_cols & (COL_DATE|COL_TIME)) {
1955 #ifndef _NO_EXTENSIONS
1956                 format_date(&entry->data.ftCreationTime, buffer, visible_cols);
1957                 if (calcWidthCol == -1)
1958                         output_text(pane, dis, col, buffer, 0);
1959                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1960                         calc_width(pane, dis, col, buffer);
1961                 col++;
1962
1963                 format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
1964                 if (calcWidthCol == -1)
1965                         output_text(pane, dis, col, buffer, 0);
1966                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1967                         calc_width(pane, dis, col, buffer);
1968                 col++;
1969 #endif
1970
1971                 format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
1972                 if (calcWidthCol == -1)
1973                         output_text(pane, dis, col, buffer, 0);
1974                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1975                         calc_width(pane, dis, col, buffer);
1976                 col++;
1977         }
1978
1979 #ifndef _NO_EXTENSIONS
1980         if (entry->bhfi_valid) {
1981             ULONGLONG index = ((ULONGLONG)entry->bhfi.nFileIndexHigh << 32) | entry->bhfi.nFileIndexLow;
1982
1983                 if (visible_cols & COL_INDEX) {
1984                         _stprintf(buffer, _T("%") LONGLONGARG _T("X"), index);
1985                         if (calcWidthCol == -1)
1986                                 output_text(pane, dis, col, buffer, DT_RIGHT);
1987                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1988                                 calc_width(pane, dis, col, buffer);
1989                         col++;
1990                 }
1991
1992                 if (visible_cols & COL_LINKS) {
1993                         wsprintf(buffer, _T("%d"), entry->bhfi.nNumberOfLinks);
1994                         if (calcWidthCol == -1)
1995                                 output_text(pane, dis, col, buffer, DT_CENTER);
1996                         else if (calcWidthCol==col || calcWidthCol==COLUMNS)
1997                                 calc_width(pane, dis, col, buffer);
1998                         col++;
1999                 }
2000         } else
2001                 col += 2;
2002 #endif
2003
2004         /* show file attributes */
2005         if (visible_cols & COL_ATTRIBUTES) {
2006 #ifdef _NO_EXTENSIONS
2007                 _tcscpy(buffer, _T(" \t \t \t \t "));
2008 #else
2009                 _tcscpy(buffer, _T(" \t \t \t \t \t \t \t \t \t \t \t "));
2010 #endif
2011
2012                 if (attrs & FILE_ATTRIBUTE_NORMAL)                                      buffer[ 0] = 'N';
2013                 else {
2014                         if (attrs & FILE_ATTRIBUTE_READONLY)                    buffer[ 2] = 'R';
2015                         if (attrs & FILE_ATTRIBUTE_HIDDEN)                              buffer[ 4] = 'H';
2016                         if (attrs & FILE_ATTRIBUTE_SYSTEM)                              buffer[ 6] = 'S';
2017                         if (attrs & FILE_ATTRIBUTE_ARCHIVE)                             buffer[ 8] = 'A';
2018                         if (attrs & FILE_ATTRIBUTE_COMPRESSED)                  buffer[10] = 'C';
2019 #ifndef _NO_EXTENSIONS
2020                         if (attrs & FILE_ATTRIBUTE_DIRECTORY)                   buffer[12] = 'D';
2021                         if (attrs & FILE_ATTRIBUTE_ENCRYPTED)                   buffer[14] = 'E';
2022                         if (attrs & FILE_ATTRIBUTE_TEMPORARY)                   buffer[16] = 'T';
2023                         if (attrs & FILE_ATTRIBUTE_SPARSE_FILE)                 buffer[18] = 'P';
2024                         if (attrs & FILE_ATTRIBUTE_REPARSE_POINT)               buffer[20] = 'Q';
2025                         if (attrs & FILE_ATTRIBUTE_OFFLINE)                             buffer[22] = 'O';
2026                         if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
2027 #endif
2028                 }
2029
2030                 if (calcWidthCol == -1)
2031                         output_tabbed_text(pane, dis, col, buffer);
2032                 else if (calcWidthCol==col || calcWidthCol==COLUMNS)
2033                         calc_tabbed_width(pane, dis, col, buffer);
2034
2035                 col++;
2036         }
2037
2038 /*TODO
2039         if (flags.security) {
2040                 DWORD rights = get_access_mask();
2041
2042                 tcscpy(buffer, _T(" \t \t \t  \t  \t \t \t  \t  \t \t \t "));
2043
2044                 if (rights & FILE_READ_DATA)                    buffer[ 0] = 'R';
2045                 if (rights & FILE_WRITE_DATA)                   buffer[ 2] = 'W';
2046                 if (rights & FILE_APPEND_DATA)                  buffer[ 4] = 'A';
2047                 if (rights & FILE_READ_EA)                              {buffer[6] = 'entry'; buffer[ 7] = 'R';}
2048                 if (rights & FILE_WRITE_EA)                             {buffer[9] = 'entry'; buffer[10] = 'W';}
2049                 if (rights & FILE_EXECUTE)                              buffer[12] = 'X';
2050                 if (rights & FILE_DELETE_CHILD)                 buffer[14] = 'D';
2051                 if (rights & FILE_READ_ATTRIBUTES)              {buffer[16] = 'a'; buffer[17] = 'R';}
2052                 if (rights & FILE_WRITE_ATTRIBUTES)             {buffer[19] = 'a'; buffer[20] = 'W';}
2053                 if (rights & WRITE_DAC)                                 buffer[22] = 'C';
2054                 if (rights & WRITE_OWNER)                               buffer[24] = 'O';
2055                 if (rights & SYNCHRONIZE)                               buffer[26] = 'S';
2056
2057                 output_text(dis, col++, buffer, DT_LEFT, 3, psize);
2058         }
2059
2060         if (flags.description) {
2061                 get_description(buffer);
2062                 output_text(dis, col++, buffer, 0, psize);
2063         }
2064 */
2065
2066 #ifdef _NO_EXTENSIONS
2067   }
2068
2069         /* draw focus frame */
2070         if ((dis->itemState&ODS_FOCUS) && calcWidthCol==-1) {
2071                 /* Currently [04/2000] Wine neither behaves exactly the same */
2072                 /* way as WIN 95 nor like Windows NT... */
2073                 HGDIOBJ lastBrush;
2074                 HPEN lastPen;
2075                 HPEN hpen;
2076
2077                 if (!(GetVersion() & 0x80000000)) {     /* Windows NT? */
2078                         LOGBRUSH lb = {PS_SOLID, RGB(255,255,255)};
2079                         hpen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, 0);
2080                 } else
2081                         hpen = CreatePen(PS_DOT, 0, RGB(255,255,255));
2082
2083                 lastPen = SelectPen(dis->hDC, hpen);
2084                 lastBrush = SelectObject(dis->hDC, GetStockObject(HOLLOW_BRUSH));
2085                 SetROP2(dis->hDC, R2_XORPEN);
2086                 Rectangle(dis->hDC, focusRect.left, focusRect.top, focusRect.right, focusRect.bottom);
2087                 SelectObject(dis->hDC, lastBrush);
2088                 SelectObject(dis->hDC, lastPen);
2089                 DeleteObject(hpen);
2090         }
2091 #endif
2092 }
2093
2094
2095 #ifdef _NO_EXTENSIONS
2096
2097 static void draw_splitbar(HWND hwnd, int x)
2098 {
2099         RECT rt;
2100         HDC hdc = GetDC(hwnd);
2101
2102         GetClientRect(hwnd, &rt);
2103
2104         rt.left = x - SPLIT_WIDTH/2;
2105         rt.right = x + SPLIT_WIDTH/2+1;
2106
2107         InvertRect(hdc, &rt);
2108
2109         ReleaseDC(hwnd, hdc);
2110 }
2111
2112 #endif
2113
2114
2115 #ifndef _NO_EXTENSIONS
2116
2117 static void set_header(Pane* pane)
2118 {
2119         HD_ITEM item;
2120         int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
2121         int i=0, x=0;
2122
2123         item.mask = HDI_WIDTH;
2124         item.cxy = 0;
2125
2126         for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
2127                 x += pane->widths[i];
2128                 Header_SetItem(pane->hwndHeader, i, &item);
2129         }
2130
2131         if (i < COLUMNS) {
2132                 x += pane->widths[i];
2133                 item.cxy = x - scroll_pos;
2134                 Header_SetItem(pane->hwndHeader, i++, &item);
2135
2136                 for(; i<COLUMNS; i++) {
2137                         item.cxy = pane->widths[i];
2138                         x += pane->widths[i];
2139                         Header_SetItem(pane->hwndHeader, i, &item);
2140                 }
2141         }
2142 }
2143
2144 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
2145 {
2146         switch(pnmh->code) {
2147                 case HDN_TRACK:
2148                 case HDN_ENDTRACK: {
2149                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
2150                         int idx = phdn->iItem;
2151                         int dx = phdn->pitem->cxy - pane->widths[idx];
2152                         int i;
2153
2154                         RECT clnt;
2155                         GetClientRect(pane->hwnd, &clnt);
2156
2157                         /* move immediate to simulate HDS_FULLDRAG (for now [04/2000] not realy needed with WINELIB) */
2158                         Header_SetItem(pane->hwndHeader, idx, phdn->pitem);
2159
2160                         pane->widths[idx] += dx;
2161
2162                         for(i=idx; ++i<=COLUMNS; )
2163                                 pane->positions[i] += dx;
2164
2165                         {
2166                                 int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
2167                                 RECT rt_scr;
2168                                 RECT rt_clip;
2169
2170                                 rt_scr.left   = pane->positions[idx+1]-scroll_pos;
2171                                 rt_scr.top    = 0;
2172                                 rt_scr.right  = clnt.right;
2173                                 rt_scr.bottom = clnt.bottom;
2174
2175                                 rt_clip.left   = pane->positions[idx]-scroll_pos;
2176                                 rt_clip.top    = 0;
2177                                 rt_clip.right  = clnt.right;
2178                                 rt_clip.bottom = clnt.bottom;
2179
2180                                 if (rt_scr.left < 0) rt_scr.left = 0;
2181                                 if (rt_clip.left < 0) rt_clip.left = 0;
2182
2183                                 ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
2184
2185                                 rt_clip.right = pane->positions[idx+1];
2186                                 RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
2187
2188                                 if (pnmh->code == HDN_ENDTRACK) {
2189                                         ListBox_SetHorizontalExtent(pane->hwnd, pane->positions[COLUMNS]);
2190
2191                                         if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
2192                                                 set_header(pane);
2193                                 }
2194                         }
2195
2196                         return FALSE;
2197                 }
2198
2199                 case HDN_DIVIDERDBLCLICK: {
2200                         HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
2201                         HD_ITEM item;
2202
2203                         calc_single_width(pane, phdn->iItem);
2204                         item.mask = HDI_WIDTH;
2205                         item.cxy = pane->widths[phdn->iItem];
2206
2207                         Header_SetItem(pane->hwndHeader, phdn->iItem, &item);
2208                         InvalidateRect(pane->hwnd, 0, TRUE);
2209                         break;}
2210         }
2211
2212         return 0;
2213 }
2214
2215 #endif
2216
2217
2218 static void scan_entry(ChildWnd* child, Entry* entry)
2219 {
2220         TCHAR path[MAX_PATH];
2221         int idx = ListBox_GetCurSel(child->left.hwnd);
2222         HCURSOR crsrOld = SetCursor(LoadCursor(0, IDC_WAIT));
2223
2224         /* delete sub entries in left pane */
2225         for(;;) {
2226                 LRESULT res = ListBox_GetItemData(child->left.hwnd, idx+1);
2227                 Entry* sub = (Entry*) res;
2228
2229                 if (res==LB_ERR || !sub || sub->level<=entry->level)
2230                         break;
2231
2232                 ListBox_DeleteString(child->left.hwnd, idx+1);
2233         }
2234
2235         /* empty right pane */
2236         ListBox_ResetContent(child->right.hwnd);
2237
2238         /* release memory */
2239         free_entries(entry);
2240
2241         /* read contents from disk */
2242         get_path(entry, path);
2243         read_directory(entry, path, child->sortOrder);
2244
2245         /* insert found entries in right pane */
2246         insert_entries(&child->right, entry->down, -1);
2247         calc_widths(&child->right, FALSE);
2248 #ifndef _NO_EXTENSIONS
2249         set_header(&child->right);
2250 #endif
2251
2252         child->header_wdths_ok = FALSE;
2253
2254         SetCursor(crsrOld);
2255 }
2256
2257
2258 /* expand a directory entry */
2259
2260 static BOOL expand_entry(ChildWnd* child, Entry* dir)
2261 {
2262         int idx;
2263         Entry* p;
2264
2265         if (!dir || dir->expanded || !dir->down)
2266                 return FALSE;
2267
2268         p = dir->down;
2269
2270         if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
2271                 p = p->next;
2272
2273                 if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
2274                                 p->data.cFileName[2]=='\0' && p->next)
2275                         p = p->next;
2276         }
2277
2278         /* no subdirectories ? */
2279         if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2280                 return FALSE;
2281
2282         idx = ListBox_FindItemData(child->left.hwnd, 0, dir);
2283
2284         dir->expanded = TRUE;
2285
2286         /* insert entries in left pane */
2287         insert_entries(&child->left, p, idx);
2288
2289         if (!child->header_wdths_ok) {
2290                 if (calc_widths(&child->left, FALSE)) {
2291 #ifndef _NO_EXTENSIONS
2292                         set_header(&child->left);
2293 #endif
2294
2295                         child->header_wdths_ok = TRUE;
2296                 }
2297         }
2298
2299         return TRUE;
2300 }
2301
2302
2303 static void collapse_entry(Pane* pane, Entry* dir)
2304 {
2305         int idx = ListBox_FindItemData(pane->hwnd, 0, dir);
2306
2307         ShowWindow(pane->hwnd, SW_HIDE);
2308
2309         /* hide sub entries */
2310         for(;;) {
2311                 LRESULT res = ListBox_GetItemData(pane->hwnd, idx+1);
2312                 Entry* sub = (Entry*) res;
2313
2314                 if (res==LB_ERR || !sub || sub->level<=dir->level)
2315                         break;
2316
2317                 ListBox_DeleteString(pane->hwnd, idx+1);
2318         }
2319
2320         dir->expanded = FALSE;
2321
2322         ShowWindow(pane->hwnd, SW_SHOW);
2323 }
2324
2325
2326 static void set_curdir(ChildWnd* child, Entry* entry)
2327 {
2328         TCHAR path[MAX_PATH];
2329
2330         child->left.cur = entry;
2331         child->right.root = entry->down;
2332         child->right.cur = entry;
2333
2334         if (!entry->scanned)
2335                 scan_entry(child, entry);
2336         else {
2337                 ListBox_ResetContent(child->right.hwnd);
2338                 insert_entries(&child->right, entry->down, -1);
2339                 calc_widths(&child->right, FALSE);
2340 #ifndef _NO_EXTENSIONS
2341                 set_header(&child->right);
2342 #endif
2343         }
2344
2345         get_path(entry, path);
2346         lstrcpy(child->path, path);
2347
2348         if (child->hwnd)        /* only change window title, if the window already exists */
2349                 SetWindowText(child->hwnd, path);
2350
2351         SetCurrentDirectory(path);
2352 }
2353
2354
2355 static void activate_entry(ChildWnd* child, Pane* pane)
2356 {
2357         Entry* entry = pane->cur;
2358
2359         if (!entry)
2360                 return;
2361
2362         if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2363                 int scanned_old = entry->scanned;
2364
2365                 if (!scanned_old)
2366                         scan_entry(child, entry);
2367
2368 #ifndef _NO_EXTENSIONS
2369                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
2370                         return;
2371 #endif
2372
2373                 if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
2374                         entry = child->left.cur->up;
2375                         collapse_entry(&child->left, entry);
2376                         goto focus_entry;
2377                 } else if (entry->expanded)
2378                         collapse_entry(pane, child->left.cur);
2379                 else {
2380                         expand_entry(child, child->left.cur);
2381
2382                         if (!pane->treePane) focus_entry: {
2383                                 int idx = ListBox_FindItemData(child->left.hwnd, ListBox_GetCurSel(child->left.hwnd), entry);
2384                                 ListBox_SetCurSel(child->left.hwnd, idx);
2385                                 set_curdir(child, entry);
2386                         }
2387                 }
2388
2389                 if (!scanned_old) {
2390                         calc_widths(pane, FALSE);
2391
2392 #ifndef _NO_EXTENSIONS
2393                         set_header(pane);
2394 #endif
2395                 }
2396         } else {
2397                 TCHAR cmd[MAX_PATH];
2398                 HINSTANCE hinst;
2399
2400                 get_path(entry, cmd);
2401
2402                  /* start program, open document... */
2403                 hinst = ShellExecute(child->hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, SW_SHOWNORMAL);
2404
2405                 if ((int)hinst <= 32)
2406                         display_error(child->hwnd, GetLastError());
2407         }
2408 }
2409
2410
2411 static BOOL pane_command(Pane* pane, UINT cmd)
2412 {
2413         switch(cmd) {
2414                 case ID_VIEW_NAME:
2415                         if (pane->visible_cols) {
2416                                 pane->visible_cols = 0;
2417                                 calc_widths(pane, TRUE);
2418 #ifndef _NO_EXTENSIONS
2419                                 set_header(pane);
2420 #endif
2421                                 InvalidateRect(pane->hwnd, 0, TRUE);
2422                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
2423                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
2424                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
2425                         }
2426                         break;
2427
2428                 case ID_VIEW_ALL_ATTRIBUTES:
2429                         if (pane->visible_cols != COL_ALL) {
2430                                 pane->visible_cols = COL_ALL;
2431                                 calc_widths(pane, TRUE);
2432 #ifndef _NO_EXTENSIONS
2433                                 set_header(pane);
2434 #endif
2435                                 InvalidateRect(pane->hwnd, 0, TRUE);
2436                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
2437                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
2438                                 CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
2439                         }
2440                         break;
2441
2442 #ifndef _NO_EXTENSIONS
2443                 case ID_PREFERED_SIZES: {
2444                         calc_widths(pane, TRUE);
2445                         set_header(pane);
2446                         InvalidateRect(pane->hwnd, 0, TRUE);
2447                         break;}
2448 #endif
2449
2450                         /* TODO: more command ids... */
2451
2452                 default:
2453                         return FALSE;
2454         }
2455
2456         return TRUE;
2457 }
2458
2459
2460 LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2461 {
2462         static int last_split;
2463
2464         ChildWnd* child = (ChildWnd*) GetWindowLong(hwnd, GWL_USERDATA);
2465         ASSERT(child);
2466
2467         switch(nmsg) {
2468                 case WM_DRAWITEM: {
2469                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
2470                         Entry* entry = (Entry*) dis->itemData;
2471
2472                         if (dis->CtlID == IDW_TREE_LEFT)
2473                                 draw_item(&child->left, dis, entry, -1);
2474                         else
2475                                 draw_item(&child->right, dis, entry, -1);
2476
2477                         return TRUE;}
2478
2479                 case WM_CREATE:
2480                         InitChildWindow(child);
2481                         break;
2482
2483                 case WM_NCDESTROY:
2484                         free_child_window(child);
2485                         SetWindowLong(hwnd, GWL_USERDATA, 0);
2486                         break;
2487
2488                 case WM_PAINT: {
2489                         PAINTSTRUCT ps;
2490                         HBRUSH lastBrush;
2491                         RECT rt;
2492                         GetClientRect(hwnd, &rt);
2493                         BeginPaint(hwnd, &ps);
2494                         rt.left = child->split_pos-SPLIT_WIDTH/2;
2495                         rt.right = child->split_pos+SPLIT_WIDTH/2+1;
2496                         lastBrush = SelectBrush(ps.hdc, (HBRUSH)GetStockObject(COLOR_SPLITBAR));
2497                         Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
2498                         SelectObject(ps.hdc, lastBrush);
2499 #ifdef _NO_EXTENSIONS
2500                         rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
2501                         FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
2502 #endif
2503                         EndPaint(hwnd, &ps);
2504                         break;}
2505
2506                 case WM_SETCURSOR:
2507                         if (LOWORD(lparam) == HTCLIENT) {
2508                                 POINT pt;
2509                                 GetCursorPos(&pt);
2510                                 ScreenToClient(hwnd, &pt);
2511
2512                                 if (pt.x>=child->split_pos-SPLIT_WIDTH/2 && pt.x<child->split_pos+SPLIT_WIDTH/2+1) {
2513                                         SetCursor(LoadCursor(0, IDC_SIZEWE));
2514                                         return TRUE;
2515                                 }
2516                         }
2517                         goto def;
2518
2519                 case WM_LBUTTONDOWN: {
2520                         RECT rt;
2521                         int x = LOWORD(lparam);
2522
2523                         GetClientRect(hwnd, &rt);
2524
2525                         if (x>=child->split_pos-SPLIT_WIDTH/2 && x<child->split_pos+SPLIT_WIDTH/2+1) {
2526                                 last_split = child->split_pos;
2527 #ifdef _NO_EXTENSIONS
2528                                 draw_splitbar(hwnd, last_split);
2529 #endif
2530                                 SetCapture(hwnd);
2531                         }
2532
2533                         break;}
2534
2535                 case WM_LBUTTONUP:
2536                         if (GetCapture() == hwnd) {
2537 #ifdef _NO_EXTENSIONS
2538                                 RECT rt;
2539                                 int x = LOWORD(lparam);
2540                                 draw_splitbar(hwnd, last_split);
2541                                 last_split = -1;
2542                                 GetClientRect(hwnd, &rt);
2543                                 child->split_pos = x;
2544                                 resize_tree(child, rt.right, rt.bottom);
2545 #endif
2546                                 ReleaseCapture();
2547                         }
2548                         break;
2549
2550 #ifdef _NO_EXTENSIONS
2551                 case WM_CAPTURECHANGED:
2552                         if (GetCapture()==hwnd && last_split>=0)
2553                                 draw_splitbar(hwnd, last_split);
2554                         break;
2555 #endif
2556
2557                 case WM_KEYDOWN:
2558                         if (wparam == VK_ESCAPE)
2559                                 if (GetCapture() == hwnd) {
2560                                         RECT rt;
2561 #ifdef _NO_EXTENSIONS
2562                                         draw_splitbar(hwnd, last_split);
2563 #else
2564                                         child->split_pos = last_split;
2565 #endif
2566                                         GetClientRect(hwnd, &rt);
2567                                         resize_tree(child, rt.right, rt.bottom);
2568                                         last_split = -1;
2569                                         ReleaseCapture();
2570                                         SetCursor(LoadCursor(0, IDC_ARROW));
2571                                 }
2572                         break;
2573
2574                 case WM_MOUSEMOVE:
2575                         if (GetCapture() == hwnd) {
2576                                 RECT rt;
2577                                 int x = LOWORD(lparam);
2578
2579 #ifdef _NO_EXTENSIONS
2580                                 HDC hdc = GetDC(hwnd);
2581                                 GetClientRect(hwnd, &rt);
2582
2583                                 rt.left = last_split-SPLIT_WIDTH/2;
2584                                 rt.right = last_split+SPLIT_WIDTH/2+1;
2585                                 InvertRect(hdc, &rt);
2586
2587                                 last_split = x;
2588                                 rt.left = x-SPLIT_WIDTH/2;
2589                                 rt.right = x+SPLIT_WIDTH/2+1;
2590                                 InvertRect(hdc, &rt);
2591
2592                                 ReleaseDC(hwnd, hdc);
2593 #else
2594                                 GetClientRect(hwnd, &rt);
2595
2596                                 if (x>=0 && x<rt.right) {
2597                                         child->split_pos = x;
2598                                         resize_tree(child, rt.right, rt.bottom);
2599                                         rt.left = x-SPLIT_WIDTH/2;
2600                                         rt.right = x+SPLIT_WIDTH/2+1;
2601                                         InvalidateRect(hwnd, &rt, FALSE);
2602                                         UpdateWindow(child->left.hwnd);
2603                                         UpdateWindow(hwnd);
2604                                         UpdateWindow(child->right.hwnd);
2605                                 }
2606 #endif
2607                         }
2608                         break;
2609
2610 #ifndef _NO_EXTENSIONS
2611                 case WM_GETMINMAXINFO:
2612                         DefMDIChildProc(hwnd, nmsg, wparam, lparam);
2613
2614                         {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
2615
2616                         lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
2617                         lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
2618                         break;}
2619 #endif
2620
2621                 case WM_SETFOCUS:
2622                         SetCurrentDirectory(child->path);
2623                         SetFocus(child->focus_pane? child->right.hwnd: child->left.hwnd);
2624                         break;
2625
2626                 case WM_DISPATCH_COMMAND: {
2627                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
2628
2629                         switch(LOWORD(wparam)) {
2630                                 case ID_WINDOW_NEW: {
2631                                         ChildWnd* new_child = alloc_child_window(child->path);
2632
2633                                         if (!create_child_window(new_child))
2634                                                 free(new_child);
2635
2636                                         break;}
2637
2638                                 case ID_REFRESH:
2639                                         scan_entry(child, pane->cur);
2640                                         break;
2641
2642                                 case ID_ACTIVATE:
2643                                         activate_entry(child, pane);
2644                                         break;
2645
2646                                 default:
2647                                         return pane_command(pane, LOWORD(wparam));
2648                         }
2649
2650                         return TRUE;}
2651
2652                 case WM_COMMAND: {
2653                         Pane* pane = GetFocus()==child->left.hwnd? &child->left: &child->right;
2654
2655                         switch(HIWORD(wparam)) {
2656                                 case LBN_SELCHANGE: {
2657                                         int idx = ListBox_GetCurSel(pane->hwnd);
2658                                         Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, idx);
2659
2660                                         if (pane == &child->left)
2661                                                 set_curdir(child, entry);
2662                                         else
2663                                                 pane->cur = entry;
2664                                         break;}
2665
2666                                 case LBN_DBLCLK:
2667                                         activate_entry(child, pane);
2668                                         break;
2669                         }
2670                         break;}
2671
2672 #ifndef _NO_EXTENSIONS
2673                 case WM_NOTIFY: {
2674                         NMHDR* pnmh = (NMHDR*) lparam;
2675                         return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &child->left: &child->right, pnmh);}
2676 #endif
2677
2678                 case WM_SIZE:
2679                         if (wparam != SIZE_MINIMIZED)
2680                                 resize_tree(child, LOWORD(lparam), HIWORD(lparam));
2681                         /* fall through */
2682
2683                 default: def:
2684                         return DefMDIChildProc(hwnd, nmsg, wparam, lparam);
2685         }
2686
2687         return 0;
2688 }
2689
2690
2691 LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2692 {
2693         ChildWnd* child = (ChildWnd*) GetWindowLong(GetParent(hwnd), GWL_USERDATA);
2694         Pane* pane = (Pane*) GetWindowLong(hwnd, GWL_USERDATA);
2695         ASSERT(child);
2696
2697         switch(nmsg) {
2698 #ifndef _NO_EXTENSIONS
2699                 case WM_HSCROLL:
2700                         set_header(pane);
2701                         break;
2702 #endif
2703
2704                 case WM_SETFOCUS:
2705                         child->focus_pane = pane==&child->right? 1: 0;
2706                         ListBox_SetSel(hwnd, TRUE, 1);
2707                         /*TODO: check menu items */
2708                         break;
2709
2710                 case WM_KEYDOWN:
2711                         if (wparam == VK_TAB) {
2712                                 /*TODO: SetFocus(Globals.hdrivebar) */
2713                                 SetFocus(child->focus_pane? child->left.hwnd: child->right.hwnd);
2714                         }
2715         }
2716
2717         return CallWindowProc(g_orgTreeWndProc, hwnd, nmsg, wparam, lparam);
2718 }
2719
2720
2721 static void InitInstance(HINSTANCE hinstance)
2722 {
2723         WNDCLASSEX wcFrame;
2724         ATOM hframeClass;
2725         WNDCLASS wcChild;
2726         WINE_UNUSED ATOM hChildClass;
2727         HMENU hMenuFrame = LoadMenu(hinstance, MAKEINTRESOURCE(IDM_WINEFILE));
2728         HMENU hMenuWindow = GetSubMenu(hMenuFrame, GetMenuItemCount(hMenuFrame)-2);
2729
2730         CLIENTCREATESTRUCT ccs;
2731
2732         INITCOMMONCONTROLSEX icc = {
2733                 sizeof(INITCOMMONCONTROLSEX),
2734                 ICC_BAR_CLASSES
2735         };
2736
2737         ChildWnd* child;
2738         TCHAR path[MAX_PATH];
2739
2740         HDC hdc = GetDC(0);
2741
2742
2743         wcFrame.cbSize        = sizeof(WNDCLASSEX);
2744         wcFrame.style         = 0;
2745         wcFrame.lpfnWndProc   = FrameWndProc;
2746         wcFrame.cbClsExtra    = 0;
2747         wcFrame.cbWndExtra    = 0;
2748         wcFrame.hInstance     = hinstance;
2749         wcFrame.hIcon         = LoadIcon(hinstance,
2750                                          MAKEINTRESOURCE(IDI_WINEFILE));
2751         wcFrame.hCursor       = LoadCursor(0, IDC_ARROW);
2752         wcFrame.hbrBackground = 0;
2753         wcFrame.lpszMenuName  = 0;
2754         wcFrame.lpszClassName = WINEFILEFRAME;
2755         wcFrame.hIconSm       = (HICON)LoadImage(hinstance,
2756                                                  MAKEINTRESOURCE(IDI_WINEFILE),
2757                                                  IMAGE_ICON,
2758                                                  GetSystemMetrics(SM_CXSMICON),
2759                                                  GetSystemMetrics(SM_CYSMICON),
2760                                                  LR_SHARED);
2761
2762         /* register frame window class */
2763         hframeClass = RegisterClassEx(&wcFrame);
2764
2765         wcChild.style         = CS_CLASSDC|CS_DBLCLKS|CS_VREDRAW;
2766         wcChild.lpfnWndProc   = ChildWndProc;
2767         wcChild.cbClsExtra    = 0;
2768         wcChild.cbWndExtra    = 0;
2769         wcChild.hInstance     = hinstance;
2770         wcChild.hIcon         = 0;
2771         wcChild.hCursor       = LoadCursor(0, IDC_ARROW);
2772         wcChild.hbrBackground = 0;
2773         wcChild.lpszMenuName  = 0;
2774         wcChild.lpszClassName = WINEFILETREE;
2775
2776         /* register tree windows class */
2777         hChildClass = RegisterClass(&wcChild);
2778
2779         ccs.hWindowMenu  = hMenuWindow;
2780         ccs.idFirstChild = IDW_FIRST_CHILD;
2781
2782         Globals.hMenuFrame = hMenuFrame;
2783         Globals.hMenuView = GetSubMenu(hMenuFrame, 3);
2784         Globals.hMenuOptions = GetSubMenu(hMenuFrame, 4);
2785
2786         Globals.haccel = LoadAccelerators(hinstance, MAKEINTRESOURCE(IDA_WINEFILE));
2787
2788         Globals.hfont = CreateFont(-MulDiv(8,GetDeviceCaps(hdc,LOGPIXELSY),72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("MS Sans Serif"));
2789
2790         ReleaseDC(0, hdc);
2791
2792         Globals.hInstance = hinstance;
2793
2794         /* create main window */
2795         Globals.hMainWnd = CreateWindowEx(0, (LPCTSTR)(int)hframeClass, _T("Wine File"), WS_OVERLAPPEDWINDOW,
2796                                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
2797                                         0/*hWndParent*/, Globals.hMenuFrame, hinstance, 0/*lpParam*/);
2798
2799
2800         Globals.hmdiclient = CreateWindowEx(0, _T("MDICLIENT"), NULL,
2801                                         WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL|WS_HSCROLL|WS_VISIBLE|WS_BORDER,
2802                                         0, 0, 0, 0,
2803                                         Globals.hMainWnd, 0, hinstance, &ccs);
2804
2805
2806         InitCommonControlsEx(&icc);
2807
2808         {
2809                 TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP};
2810                 int btn = 1;
2811                 PTSTR p;
2812
2813                 Globals.hdrivebar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE|CCS_NOMOVEY|TBSTYLE_LIST,
2814                                         IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
2815                                         1, 16, 13, 16, 13, sizeof(TBBUTTON));
2816                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_DRIVE_BAR, MF_BYCOMMAND|MF_CHECKED);
2817
2818                 GetLogicalDriveStrings(BUFFER_LEN, Globals.drives);
2819
2820                 drivebarBtn.fsStyle = TBSTYLE_BUTTON;
2821
2822 #ifndef _NO_EXTENSIONS
2823 #ifdef __linux__
2824                 /* insert unix file system button */
2825                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)_T("/\0"));
2826
2827                 drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
2828                 SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
2829                 drivebarBtn.iString++;
2830 #endif
2831
2832                 /* register windows drive root strings */
2833                 SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)Globals.drives);
2834 #endif
2835
2836                 drivebarBtn.idCommand = ID_DRIVE_FIRST;
2837
2838                 for(p=Globals.drives; *p; ) {
2839 #ifdef _NO_EXTENSIONS
2840                   /* insert drive letter */
2841                         TCHAR b[3] = {tolower(*p)};
2842                         SendMessage(Globals.hdrivebar, TB_ADDSTRING, 0, (LPARAM)b);
2843 #endif
2844                         switch(GetDriveType(p)) {
2845                                 case DRIVE_REMOVABLE:   drivebarBtn.iBitmap = 1;        break;
2846                                 case DRIVE_CDROM:               drivebarBtn.iBitmap = 3;        break;
2847                                 case DRIVE_REMOTE:              drivebarBtn.iBitmap = 4;        break;
2848                                 case DRIVE_RAMDISK:             drivebarBtn.iBitmap = 5;        break;
2849                                 default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
2850                         }
2851
2852                         SendMessage(Globals.hdrivebar, TB_INSERTBUTTON, btn++, (LPARAM)&drivebarBtn);
2853                         drivebarBtn.idCommand++;
2854                         drivebarBtn.iString++;
2855
2856                         while(*p++);
2857                 }
2858         }
2859
2860         {
2861                 TBBUTTON toolbarBtns[] = {
2862                         {0, 0, 0, TBSTYLE_SEP},
2863                         {0, ID_WINDOW_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON},
2864                         {1, ID_WINDOW_CASCADE, TBSTATE_ENABLED, TBSTYLE_BUTTON},
2865                         {2, ID_WINDOW_TILE_HORZ, TBSTATE_ENABLED, TBSTYLE_BUTTON},
2866                         {3, ID_WINDOW_TILE_VERT, TBSTATE_ENABLED, TBSTYLE_BUTTON},
2867                         {4, 2/*TODO: ID_...*/, TBSTATE_ENABLED, TBSTYLE_BUTTON},
2868                         {5, 2/*TODO: ID_...*/, TBSTATE_ENABLED, TBSTYLE_BUTTON},
2869                 };
2870
2871                 Globals.htoolbar = CreateToolbarEx(Globals.hMainWnd, WS_CHILD|WS_VISIBLE,
2872                         IDW_TOOLBAR, 2, Globals.hInstance, IDB_TOOLBAR, toolbarBtns,
2873                         sizeof(toolbarBtns)/sizeof(TBBUTTON), 16, 15, 16, 15, sizeof(TBBUTTON));
2874                 CheckMenuItem(Globals.hMenuOptions, ID_VIEW_TOOL_BAR, MF_BYCOMMAND|MF_CHECKED);
2875         }
2876
2877         Globals.hstatusbar = CreateStatusWindow(WS_CHILD|WS_VISIBLE, 0, Globals.hMainWnd, IDW_STATUSBAR);
2878         CheckMenuItem(Globals.hMenuOptions, ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
2879
2880 /* CreateStatusWindow does not accept WS_BORDER
2881         Globals.hstatusbar = CreateWindowEx(WS_EX_NOPARENTNOTIFY, STATUSCLASSNAME, 0,
2882                                         WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_BORDER|CCS_NODIVIDER, 0,0,0,0,
2883                                         Globals.hMainWnd, (HMENU)IDW_STATUSBAR, hinstance, 0);*/
2884
2885         /*TODO: read paths and window placements from registry */
2886         GetCurrentDirectory(MAX_PATH, path);
2887         child = alloc_child_window(path);
2888
2889         child->pos.showCmd = SW_SHOWMAXIMIZED;
2890         child->pos.rcNormalPosition.left = 0;
2891         child->pos.rcNormalPosition.top = 0;
2892         child->pos.rcNormalPosition.right = 320;
2893         child->pos.rcNormalPosition.bottom = 280;
2894
2895         if (!create_child_window(child))
2896                 free(child);
2897
2898         SetWindowPlacement(child->hwnd, &child->pos);
2899
2900         Globals.himl = ImageList_LoadBitmap(Globals.hInstance, MAKEINTRESOURCE(IDB_IMAGES), 16, 0, RGB(0,255,0));
2901
2902         Globals.prescan_node = FALSE;
2903 }
2904
2905 void ExitInstance()
2906 {
2907         ImageList_Destroy(Globals.himl);
2908 }
2909
2910
2911 #ifdef _NO_EXTENSIONS
2912
2913 /* search for already running win[e]files */
2914
2915 static int g_foundPrevInstance = 0;
2916
2917 static BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lparam)
2918 {
2919         TCHAR cls[128];
2920
2921         GetClassName(hwnd, cls, 128);
2922
2923         if (!lstrcmp(cls, (LPCTSTR)lparam)) {
2924                 g_foundPrevInstance++;
2925                 return FALSE;
2926         }
2927
2928         return TRUE;
2929 }
2930
2931 #endif
2932
2933
2934 int APIENTRY WinMain(HINSTANCE hinstance,
2935                                          HINSTANCE previnstance,
2936                                          LPSTR     cmdline,
2937                                          int       cmdshow)
2938 {
2939         MSG msg;
2940
2941 #ifdef _NO_EXTENSIONS
2942         /* allow only one running instance */
2943         EnumWindows(EnumWndProc, (LPARAM)WINEFILEFRAME);
2944
2945         if (g_foundPrevInstance)
2946                 return 1;
2947 #endif
2948
2949         InitInstance(hinstance);
2950
2951         if (cmdshow == SW_SHOWNORMAL) {
2952                 /*TODO: read window placement from registry */
2953                 cmdshow = SW_MAXIMIZE;
2954         }
2955
2956         ShowWindow(Globals.hMainWnd, cmdshow);
2957         UpdateWindow(Globals.hMainWnd);
2958
2959         while(GetMessage(&msg, 0, 0, 0)) {
2960                 if (!TranslateMDISysAccel(Globals.hmdiclient, &msg) &&
2961                         !TranslateAccelerator(Globals.hMainWnd, Globals.haccel, &msg))
2962                 {
2963                         TranslateMessage(&msg);
2964                         DispatchMessage(&msg);
2965                 }
2966         }
2967
2968         ExitInstance();
2969
2970         return 0;
2971 }