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