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