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