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