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