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