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