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