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