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