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