- userinit, usetup, vmwinst, welcome, winefile, winlogon, winver.
[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 #define COBJMACROS
32
33 #include "winefile.h"
34
35 #include "resource.h"
36
37
38 #ifdef _NO_EXTENSIONS
39 #undef _LEFT_FILES
40 #endif
41
42 #ifndef _MAX_PATH
43 #define _MAX_DRIVE 3
44 #define _MAX_FNAME 256
45 #define _MAX_DIR _MAX_FNAME
46 #define _MAX_EXT _MAX_FNAME
47 #define _MAX_PATH 260
48 #endif
49
50 #ifdef NONAMELESSUNION
51 #define UNION_MEMBER(x) DUMMYUNIONNAME.x
52 #else
53 #define UNION_MEMBER(x) x
54 #endif
55
56
57 #ifdef _SHELL_FOLDERS
58 #define DEFAULT_SPLIT_POS 300
59 #else
60 #define DEFAULT_SPLIT_POS 200
61 #endif
62
63
64 enum ENTRY_TYPE {
65 ET_WINDOWS,
66 ET_UNIX,
67 #ifdef _SHELL_FOLDERS
68 ET_SHELL
69 #endif
70 };
71
72 typedef struct _Entry {
73 struct _Entry* next;
74 struct _Entry* down;
75 struct _Entry* up;
76
77 BOOL expanded;
78 BOOL scanned;
79 int level;
80
81 WIN32_FIND_DATA data;
82
83 #ifndef _NO_EXTENSIONS
84 BY_HANDLE_FILE_INFORMATION bhfi;
85 BOOL bhfi_valid;
86 enum ENTRY_TYPE etype;
87 #endif
88 #ifdef _SHELL_FOLDERS
89 LPITEMIDLIST pidl;
90 IShellFolder* folder;
91 HICON hicon;
92 #endif
93 } Entry;
94
95 typedef struct {
96 Entry entry;
97 TCHAR path[MAX_PATH];
98 TCHAR volname[_MAX_FNAME];
99 TCHAR fs[_MAX_DIR];
100 DWORD drive_type;
101 DWORD fs_flags;
102 } Root;
103
104 enum COLUMN_FLAGS {
105 COL_SIZE = 0x01,
106 COL_DATE = 0x02,
107 COL_TIME = 0x04,
108 COL_ATTRIBUTES = 0x08,
109 COL_DOSNAMES = 0x10,
110 #ifdef _NO_EXTENSIONS
111 COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES
112 #else
113 COL_INDEX = 0x20,
114 COL_LINKS = 0x40,
115 COL_ALL = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_DOSNAMES|COL_INDEX|COL_LINKS
116 #endif
117 };
118
119 typedef enum {
120 SORT_NAME,
121 SORT_EXT,
122 SORT_SIZE,
123 SORT_DATE
124 } SORT_ORDER;
125
126 typedef struct {
127 HWND hwnd;
128 #ifndef _NO_EXTENSIONS
129 HWND hwndHeader;
130 #endif
131
132 #ifndef _NO_EXTENSIONS
133 #define COLUMNS 10
134 #else
135 #define COLUMNS 5
136 #endif
137 int widths[COLUMNS];
138 int positions[COLUMNS+1];
139
140 BOOL treePane;
141 int visible_cols;
142 Entry* root;
143 Entry* cur;
144 } Pane;
145
146 typedef struct {
147 HWND hwnd;
148 Pane left;
149 Pane right;
150 int focus_pane; /* 0: left 1: right */
151 WINDOWPLACEMENT pos;
152 int split_pos;
153 BOOL header_wdths_ok;
154
155 TCHAR path[MAX_PATH];
156 TCHAR filter_pattern[MAX_PATH];
157 int filter_flags;
158 Root root;
159
160 SORT_ORDER sortOrder;
161 } ChildWnd;
162
163
164
165 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd);
166 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd);
167 static void refresh_child(ChildWnd* child);
168 static void refresh_drives(void);
169 static void get_path(Entry* dir, PTSTR path);
170 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols);
171
172 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
173 static LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
174 static LRESULT CALLBACK TreeWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
175
176
177 /* globals */
178 WINEFILE_GLOBALS Globals;
179
180 static int last_split;
181
182
183 /* some common string constants */
184 const static TCHAR sEmpty[] = {'\0'};
185 const static TCHAR sSpace[] = {' ', '\0'};
186 const static TCHAR sNumFmt[] = {'%','d','\0'};
187 const static TCHAR sQMarks[] = {'?','?','?','\0'};
188
189 /* window class names */
190 const static TCHAR sWINEFILEFRAME[] = {'W','F','S','_','F','r','a','m','e','\0'};
191 const static TCHAR sWINEFILETREE[] = {'W','F','S','_','T','r','e','e','\0'};
192
193 #ifdef _MSC_VER
194 /* #define LONGLONGARG _T("I64") */
195 const static TCHAR sLongHexFmt[] = {'%','I','6','4','X','\0'};
196 const static TCHAR sLongNumFmt[] = {'%','I','6','4','d','\0'};
197 #else
198 /* #define LONGLONGARG _T("L") */
199 const static TCHAR sLongHexFmt[] = {'%','L','X','\0'};
200 const static TCHAR sLongNumFmt[] = {'%','L','d','\0'};
201 #endif
202
203
204 /* load resource string */
205 static LPTSTR load_string(LPTSTR buffer, UINT id)
206 {
207 LoadString(Globals.hInstance, id, buffer, BUFFER_LEN);
208
209 return buffer;
210 }
211
212 #define RS(b, i) load_string(b, i)
213
214
215 /* display error message for the specified WIN32 error code */
216 static void display_error(HWND hwnd, DWORD error)
217 {
218 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
219 PTSTR msg;
220
221 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
222 0, error, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (PTSTR)&msg, 0, NULL))
223 MessageBox(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
224 else
225 MessageBox(hwnd, RS(b1,IDS_ERROR), RS(b2,IDS_WINEFILE), MB_OK);
226
227 LocalFree(msg);
228 }
229
230
231 /* display network error message using WNetGetLastError() */
232 static void display_network_error(HWND hwnd)
233 {
234 TCHAR msg[BUFFER_LEN], provider[BUFFER_LEN], b2[BUFFER_LEN];
235 DWORD error;
236
237 if (WNetGetLastError(&error, msg, BUFFER_LEN, provider, BUFFER_LEN) == NO_ERROR)
238 MessageBox(hwnd, msg, RS(b2,IDS_WINEFILE), MB_OK);
239 }
240
241
242 #ifdef __WINE__
243
244 #ifdef UNICODE
245
246 /* call vswprintf() in msvcrt.dll */
247 /*TODO: fix swprintf() in non-msvcrt mode, so that this dynamic linking function can be removed */
248 static int msvcrt_swprintf(WCHAR* buffer, const WCHAR* fmt, ...)
249 {
250 static int (__cdecl *pvswprintf)(WCHAR*, const WCHAR*, va_list) = NULL;
251 va_list ap;
252 int ret;
253
254 if (!pvswprintf) {
255 HMODULE hModMsvcrt = LoadLibraryA("msvcrt");
256 pvswprintf = (int(__cdecl*)(WCHAR*,const WCHAR*,va_list)) GetProcAddress(hModMsvcrt, "vswprintf");
257 }
258
259 va_start(ap, fmt);
260 ret = (*pvswprintf)(buffer, fmt, ap);
261 va_end(ap);
262
263 return ret;
264 }
265
266 static LPCWSTR my_wcsrchr(LPCWSTR str, WCHAR c)
267 {
268 LPCWSTR p = str;
269
270 while(*p)
271 ++p;
272
273 do {
274 if (--p < str)
275 return NULL;
276 } while(*p != c);
277
278 return p;
279 }
280
281 #define _tcsrchr my_wcsrchr
282 #else /* UNICODE */
283 #define _tcsrchr strrchr
284 #endif /* UNICODE */
285
286 #endif /* __WINE__ */
287
288
289 /* allocate and initialise a directory entry */
290 static Entry* alloc_entry(void)
291 {
292 Entry* entry = (Entry*) malloc(sizeof(Entry));
293
294 #ifdef _SHELL_FOLDERS
295 entry->pidl = NULL;
296 entry->folder = NULL;
297 entry->hicon = 0;
298 #endif
299
300 return entry;
301 }
302
303 /* free a directory entry */
304 static void free_entry(Entry* entry)
305 {
306 #ifdef _SHELL_FOLDERS
307 if (entry->hicon && entry->hicon!=(HICON)-1)
308 DestroyIcon(entry->hicon);
309
310 if (entry->folder && entry->folder!=Globals.iDesktop)
311 IShellFolder_Release(entry->folder);
312
313 if (entry->pidl)
314 IMalloc_Free(Globals.iMalloc, entry->pidl);
315 #endif
316
317 free(entry);
318 }
319
320 /* recursively free all child entries */
321 static void free_entries(Entry* dir)
322 {
323 Entry *entry, *next=dir->down;
324
325 if (next) {
326 dir->down = 0;
327
328 do {
329 entry = next;
330 next = entry->next;
331
332 free_entries(entry);
333 free_entry(entry);
334 } while(next);
335 }
336 }
337
338
339 static void read_directory_win(Entry* dir, LPCTSTR path)
340 {
341 Entry* first_entry = NULL;
342 Entry* last = NULL;
343 Entry* entry;
344
345 int level = dir->level + 1;
346 WIN32_FIND_DATA w32fd;
347 HANDLE hFind;
348 #ifndef _NO_EXTENSIONS
349 HANDLE hFile;
350 #endif
351
352 TCHAR buffer[MAX_PATH], *p;
353 for(p=buffer; *path; )
354 *p++ = *path++;
355
356 *p++ = '\\';
357 p[0] = '*';
358 p[1] = '\0';
359
360 hFind = FindFirstFile(buffer, &w32fd);
361
362 if (hFind != INVALID_HANDLE_VALUE) {
363 do {
364 #ifdef _NO_EXTENSIONS
365 /* hide directory entry "." */
366 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
367 LPCTSTR name = w32fd.cFileName;
368
369 if (name[0]=='.' && name[1]=='\0')
370 continue;
371 }
372 #endif
373 entry = alloc_entry();
374
375 if (!first_entry)
376 first_entry = entry;
377
378 if (last)
379 last->next = entry;
380
381 memcpy(&entry->data, &w32fd, sizeof(WIN32_FIND_DATA));
382 entry->down = NULL;
383 entry->up = dir;
384 entry->expanded = FALSE;
385 entry->scanned = FALSE;
386 entry->level = level;
387
388 #ifndef _NO_EXTENSIONS
389 entry->etype = ET_WINDOWS;
390 entry->bhfi_valid = FALSE;
391
392 lstrcpy(p, entry->data.cFileName);
393
394 hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
395 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
396
397 if (hFile != INVALID_HANDLE_VALUE) {
398 if (GetFileInformationByHandle(hFile, &entry->bhfi))
399 entry->bhfi_valid = TRUE;
400
401 CloseHandle(hFile);
402 }
403 #endif
404
405 last = entry;
406 } while(FindNextFile(hFind, &w32fd));
407
408 if (last)
409 last->next = NULL;
410
411 FindClose(hFind);
412 }
413
414 dir->down = first_entry;
415 dir->scanned = TRUE;
416 }
417
418
419 static Entry* find_entry_win(Entry* dir, LPCTSTR name)
420 {
421 Entry* entry;
422
423 for(entry=dir->down; entry; entry=entry->next) {
424 LPCTSTR p = name;
425 LPCTSTR q = entry->data.cFileName;
426
427 do {
428 if (!*p || *p==TEXT('\\') || *p==TEXT('/'))
429 return entry;
430 } while(tolower(*p++) == tolower(*q++));
431
432 p = name;
433 q = entry->data.cAlternateFileName;
434
435 do {
436 if (!*p || *p==TEXT('\\') || *p==TEXT('/'))
437 return entry;
438 } while(tolower(*p++) == tolower(*q++));
439 }
440
441 return 0;
442 }
443
444
445 static Entry* read_tree_win(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
446 {
447 TCHAR buffer[MAX_PATH];
448 Entry* entry = &root->entry;
449 LPCTSTR s = path;
450 PTSTR d = buffer;
451
452 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
453
454 #ifndef _NO_EXTENSIONS
455 entry->etype = ET_WINDOWS;
456 #endif
457
458 while(entry) {
459 while(*s && *s!=TEXT('\\') && *s!=TEXT('/'))
460 *d++ = *s++;
461
462 while(*s==TEXT('\\') || *s==TEXT('/'))
463 s++;
464
465 *d++ = TEXT('\\');
466 *d = TEXT('\0');
467
468 read_directory(entry, buffer, sortOrder, hwnd);
469
470 if (entry->down)
471 entry->expanded = TRUE;
472
473 if (!*s)
474 break;
475
476 entry = find_entry_win(entry, s);
477 }
478
479 SetCursor(old_cursor);
480
481 return entry;
482 }
483
484
485 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
486
487 static BOOL time_to_filetime(const time_t* t, FILETIME* ftime)
488 {
489 struct tm* tm = gmtime(t);
490 SYSTEMTIME stime;
491
492 if (!tm)
493 return FALSE;
494
495 stime.wYear = tm->tm_year+1900;
496 stime.wMonth = tm->tm_mon+1;
497 /* stime.wDayOfWeek */
498 stime.wDay = tm->tm_mday;
499 stime.wHour = tm->tm_hour;
500 stime.wMinute = tm->tm_min;
501 stime.wSecond = tm->tm_sec;
502
503 return SystemTimeToFileTime(&stime, ftime);
504 }
505
506 static void read_directory_unix(Entry* dir, LPCTSTR path)
507 {
508 Entry* first_entry = NULL;
509 Entry* last = NULL;
510 Entry* entry;
511 DIR* pdir;
512
513 int level = dir->level + 1;
514 #ifdef UNICODE
515 char cpath[MAX_PATH];
516
517 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, cpath, MAX_PATH, NULL, NULL);
518 #else
519 const char* cpath = path;
520 #endif
521
522 pdir = opendir(cpath);
523
524 if (pdir) {
525 struct stat st;
526 struct dirent* ent;
527 char buffer[MAX_PATH], *p;
528 const char* s;
529
530 for(p=buffer,s=cpath; *s; )
531 *p++ = *s++;
532
533 if (p==buffer || p[-1]!='/')
534 *p++ = '/';
535
536 while((ent=readdir(pdir))) {
537 entry = alloc_entry();
538
539 if (!first_entry)
540 first_entry = entry;
541
542 if (last)
543 last->next = entry;
544
545 entry->etype = ET_UNIX;
546
547 strcpy(p, ent->d_name);
548 #ifdef UNICODE
549 MultiByteToWideChar(CP_UNIXCP, 0, p, -1, entry->data.cFileName, MAX_PATH);
550 #else
551 lstrcpy(entry->data.cFileName, p);
552 #endif
553
554 if (!stat(buffer, &st)) {
555 entry->data.dwFileAttributes = p[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
556
557 if (S_ISDIR(st.st_mode))
558 entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
559
560 entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
561 entry->data.nFileSizeHigh = st.st_size >> 32;
562
563 memset(&entry->data.ftCreationTime, 0, sizeof(FILETIME));
564 time_to_filetime(&st.st_atime, &entry->data.ftLastAccessTime);
565 time_to_filetime(&st.st_mtime, &entry->data.ftLastWriteTime);
566
567 entry->bhfi.nFileIndexLow = ent->d_ino;
568 entry->bhfi.nFileIndexHigh = 0;
569
570 entry->bhfi.nNumberOfLinks = st.st_nlink;
571
572 entry->bhfi_valid = TRUE;
573 } else {
574 entry->data.nFileSizeLow = 0;
575 entry->data.nFileSizeHigh = 0;
576 entry->bhfi_valid = FALSE;
577 }
578
579 entry->down = NULL;
580 entry->up = dir;
581 entry->expanded = FALSE;
582 entry->scanned = FALSE;
583 entry->level = level;
584
585 last = entry;
586 }
587
588 if (last)
589 last->next = NULL;
590
591 closedir(pdir);
592 }
593
594 dir->down = first_entry;
595 dir->scanned = TRUE;
596 }
597
598 static Entry* find_entry_unix(Entry* dir, LPCTSTR name)
599 {
600 Entry* entry;
601
602 for(entry=dir->down; entry; entry=entry->next) {
603 LPCTSTR p = name;
604 LPCTSTR q = entry->data.cFileName;
605
606 do {
607 if (!*p || *p==TEXT('/'))
608 return entry;
609 } while(*p++ == *q++);
610 }
611
612 return 0;
613 }
614
615 static Entry* read_tree_unix(Root* root, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
616 {
617 TCHAR buffer[MAX_PATH];
618 Entry* entry = &root->entry;
619 LPCTSTR s = path;
620 PTSTR d = buffer;
621
622 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
623
624 entry->etype = ET_UNIX;
625
626 while(entry) {
627 while(*s && *s!=TEXT('/'))
628 *d++ = *s++;
629
630 while(*s == TEXT('/'))
631 s++;
632
633 *d++ = TEXT('/');
634 *d = TEXT('\0');
635
636 read_directory(entry, buffer, sortOrder, hwnd);
637
638 if (entry->down)
639 entry->expanded = TRUE;
640
641 if (!*s)
642 break;
643
644 entry = find_entry_unix(entry, s);
645 }
646
647 SetCursor(old_cursor);
648
649 return entry;
650 }
651
652 #endif /* !defined(_NO_EXTENSIONS) && defined(__WINE__) */
653
654
655 #ifdef _SHELL_FOLDERS
656
657 #ifdef UNICODE
658 #define get_strret get_strretW
659 #define path_from_pidl path_from_pidlW
660 #else
661 #define get_strret get_strretA
662 #define path_from_pidl path_from_pidlA
663 #endif
664
665
666 static void free_strret(STRRET* str)
667 {
668 if (str->uType == STRRET_WSTR)
669 IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
670 }
671
672
673 #ifndef UNICODE
674
675 static LPSTR strcpyn(LPSTR dest, LPCSTR source, size_t count)
676 {
677 LPCSTR s;
678 LPSTR d = dest;
679
680 for(s=source; count&&(*d++=*s++); )
681 count--;
682
683 return dest;
684 }
685
686 static void get_strretA(STRRET* str, const SHITEMID* shiid, LPSTR buffer, int len)
687 {
688 switch(str->uType) {
689 case STRRET_WSTR:
690 WideCharToMultiByte(CP_ACP, 0, str->UNION_MEMBER(pOleStr), -1, buffer, len, NULL, NULL);
691 break;
692
693 case STRRET_OFFSET:
694 strcpyn(buffer, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), len);
695 break;
696
697 case STRRET_CSTR:
698 strcpyn(buffer, str->UNION_MEMBER(cStr), len);
699 }
700 }
701
702 static HRESULT path_from_pidlA(IShellFolder* folder, LPITEMIDLIST pidl, LPSTR buffer, int len)
703 {
704 STRRET str;
705
706 /* SHGDN_FORPARSING: get full path of id list */
707 HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
708
709 if (SUCCEEDED(hr)) {
710 get_strretA(&str, &pidl->mkid, buffer, len);
711 free_strret(&str);
712 } else
713 buffer[0] = '\0';
714
715 return hr;
716 }
717
718 #endif
719
720 static LPWSTR wcscpyn(LPWSTR dest, LPCWSTR source, size_t count)
721 {
722 LPCWSTR s;
723 LPWSTR d = dest;
724
725 for(s=source; count&&(*d++=*s++); )
726 count--;
727
728 return dest;
729 }
730
731 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
732 {
733 switch(str->uType) {
734 case STRRET_WSTR:
735 wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
736 break;
737
738 case STRRET_OFFSET:
739 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
740 break;
741
742 case STRRET_CSTR:
743 MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
744 }
745 }
746
747
748 static HRESULT name_from_pidl(IShellFolder* folder, LPITEMIDLIST pidl, LPTSTR buffer, int len, SHGDNF flags)
749 {
750 STRRET str;
751
752 HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str);
753
754 if (SUCCEEDED(hr)) {
755 get_strret(&str, &pidl->mkid, buffer, len);
756 free_strret(&str);
757 } else
758 buffer[0] = '\0';
759
760 return hr;
761 }
762
763
764 static HRESULT path_from_pidlW(IShellFolder* folder, LPITEMIDLIST pidl, LPWSTR buffer, int len)
765 {
766 STRRET str;
767
768 /* SHGDN_FORPARSING: get full path of id list */
769 HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
770
771 if (SUCCEEDED(hr)) {
772 get_strretW(&str, &pidl->mkid, buffer, len);
773 free_strret(&str);
774 } else
775 buffer[0] = '\0';
776
777 return hr;
778 }
779
780
781 /* create an item id list from a file system path */
782
783 static LPITEMIDLIST get_path_pidl(LPTSTR path, HWND hwnd)
784 {
785 LPITEMIDLIST pidl;
786 HRESULT hr;
787 ULONG len;
788
789 #ifdef UNICODE
790 LPWSTR buffer = path;
791 #else
792 WCHAR buffer[MAX_PATH];
793 MultiByteToWideChar(CP_ACP, 0, path, -1, buffer, MAX_PATH);
794 #endif
795
796 hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
797 if (FAILED(hr))
798 return NULL;
799
800 return pidl;
801 }
802
803
804 /* convert an item id list from relative to absolute (=relative to the desktop) format */
805
806 static LPITEMIDLIST get_to_absolute_pidl(Entry* entry, HWND hwnd)
807 {
808 if (entry->up && entry->up->etype==ET_SHELL) {
809 IShellFolder* folder = entry->up->folder;
810 WCHAR buffer[MAX_PATH];
811
812 HRESULT hr = path_from_pidlW(folder, entry->pidl, buffer, MAX_PATH);
813
814 if (SUCCEEDED(hr)) {
815 LPITEMIDLIST pidl;
816 ULONG len;
817
818 hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
819
820 if (SUCCEEDED(hr))
821 return pidl;
822 }
823 } else if (entry->etype == ET_WINDOWS) {
824 TCHAR path[MAX_PATH];
825
826 get_path(entry, path);
827
828 return get_path_pidl(path, hwnd);
829 } else if (entry->pidl)
830 return ILClone(entry->pidl);
831
832 return NULL;
833 }
834
835
836 static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
837 {
838 IExtractIcon* pExtract;
839
840 if (SUCCEEDED(IShellFolder_GetUIObjectOf(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIcon, 0, (LPVOID*)&pExtract))) {
841 TCHAR path[_MAX_PATH];
842 unsigned flags;
843 HICON hicon;
844 int idx;
845
846 if (SUCCEEDED((*pExtract->lpVtbl->GetIconLocation)(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
847 if (!(flags & GIL_NOTFILENAME)) {
848 if (idx == -1)
849 idx = 0; /* special case for some control panel applications */
850
851 if ((int)ExtractIconEx(path, idx, 0, &hicon, 1) > 0)
852 flags &= ~GIL_DONTCACHE;
853 } else {
854 HICON hIconLarge = 0;
855
856 HRESULT hr = (*pExtract->lpVtbl->Extract)(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
857
858 if (SUCCEEDED(hr))
859 DestroyIcon(hIconLarge);
860 }
861
862 return hicon;
863 }
864 }
865
866 return 0;
867 }
868
869
870 static Entry* find_entry_shell(Entry* dir, LPCITEMIDLIST pidl)
871 {
872 Entry* entry;
873
874 for(entry=dir->down; entry; entry=entry->next) {
875 if (entry->pidl->mkid.cb == pidl->mkid.cb &&
876 !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb))
877 return entry;
878 }
879
880 return 0;
881 }
882
883 static Entry* read_tree_shell(Root* root, LPITEMIDLIST pidl, SORT_ORDER sortOrder, HWND hwnd)
884 {
885 Entry* entry = &root->entry;
886 Entry* next;
887 LPITEMIDLIST next_pidl = pidl;
888 IShellFolder* folder;
889 IShellFolder* child = NULL;
890 HRESULT hr;
891
892 HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
893
894 #ifndef _NO_EXTENSIONS
895 entry->etype = ET_SHELL;
896 #endif
897
898 folder = Globals.iDesktop;
899
900 while(entry) {
901 entry->pidl = next_pidl;
902 entry->folder = folder;
903
904 if (!pidl->mkid.cb)
905 break;
906
907 /* copy first element of item idlist */
908 next_pidl = IMalloc_Alloc(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT));
909 memcpy(next_pidl, pidl, pidl->mkid.cb);
910 ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0;
911
912 hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
913 if (!SUCCEEDED(hr))
914 break;
915
916 read_directory(entry, NULL, sortOrder, hwnd);
917
918 if (entry->down)
919 entry->expanded = TRUE;
920
921 next = find_entry_shell(entry, next_pidl);
922 if (!next)
923 break;
924
925 folder = child;
926 entry = next;
927
928 /* go to next element */
929 pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
930 }
931
932 SetCursor(old_cursor);
933
934 return entry;
935 }
936
937
938 static void fill_w32fdata_shell(IShellFolder* folder, LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* w32fdata)
939 {
940 if (!(attribs & SFGAO_FILESYSTEM) ||
941 FAILED(SHGetDataFromIDList(folder, pidl, SHGDFIL_FINDDATA, w32fdata, sizeof(WIN32_FIND_DATA)))) {
942 WIN32_FILE_ATTRIBUTE_DATA fad;
943 IDataObject* pDataObj;
944
945 STGMEDIUM medium = {0, {0}, 0};
946 FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
947
948 HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
949
950 if (SUCCEEDED(hr)) {
951 hr = IDataObject_GetData(pDataObj, &fmt, &medium);
952
953 IDataObject_Release(pDataObj);
954
955 if (SUCCEEDED(hr)) {
956 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
957 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
958
959 if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
960 w32fdata->dwFileAttributes = fad.dwFileAttributes;
961 w32fdata->ftCreationTime = fad.ftCreationTime;
962 w32fdata->ftLastAccessTime = fad.ftLastAccessTime;
963 w32fdata->ftLastWriteTime = fad.ftLastWriteTime;
964
965 if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
966 w32fdata->nFileSizeLow = fad.nFileSizeLow;
967 w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
968 }
969 }
970
971 SetErrorMode(sem_org);
972
973 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
974 GlobalFree(medium.UNION_MEMBER(hGlobal));
975 }
976 }
977 }
978
979 if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
980 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
981
982 if (attribs & SFGAO_READONLY)
983 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
984
985 if (attribs & SFGAO_COMPRESSED)
986 w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
987 }
988
989
990 static void read_directory_shell(Entry* dir, HWND hwnd)
991 {
992 IShellFolder* folder = dir->folder;
993 int level = dir->level + 1;
994 HRESULT hr;
995
996 IShellFolder* child;
997 IEnumIDList* idlist;
998
999 Entry* first_entry = NULL;
1000 Entry* last = NULL;
1001 Entry* entry;
1002
1003 if (!folder)
1004 return;
1005
1006 hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
1007
1008 if (SUCCEEDED(hr)) {
1009 for(;;) {
1010 #define FETCH_ITEM_COUNT 32
1011 LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
1012 SFGAOF attribs;
1013 ULONG cnt = 0;
1014 ULONG n;
1015
1016 memset(pidls, 0, sizeof(pidls));
1017
1018 hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
1019 if (!SUCCEEDED(hr))
1020 break;
1021
1022 if (hr == S_FALSE)
1023 break;
1024
1025 for(n=0; n<cnt; ++n) {
1026 entry = alloc_entry();
1027
1028 if (!first_entry)
1029 first_entry = entry;
1030
1031 if (last)
1032 last->next = entry;
1033
1034 memset(&entry->data, 0, sizeof(WIN32_FIND_DATA));
1035 entry->bhfi_valid = FALSE;
1036
1037 attribs = ~SFGAO_FILESYSTEM; /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
1038
1039 hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
1040
1041 if (SUCCEEDED(hr)) {
1042 if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
1043 fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
1044
1045 entry->bhfi_valid = TRUE;
1046 } else
1047 attribs = 0;
1048 } else
1049 attribs = 0;
1050
1051 entry->pidl = pidls[n];
1052
1053 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1054 hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
1055
1056 if (SUCCEEDED(hr))
1057 entry->folder = child;
1058 else
1059 entry->folder = NULL;
1060 }
1061 else
1062 entry->folder = NULL;
1063
1064 if (!entry->data.cFileName[0])
1065 /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
1066
1067 /* get display icons for files and virtual objects */
1068 if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
1069 !(attribs & SFGAO_FILESYSTEM)) {
1070 entry->hicon = extract_icon(folder, pidls[n]);
1071
1072 if (!entry->hicon)
1073 entry->hicon = (HICON)-1; /* don't try again later */
1074 }
1075
1076 entry->down = NULL;
1077 entry->up = dir;
1078 entry->expanded = FALSE;
1079 entry->scanned = FALSE;
1080 entry->level = level;
1081
1082 #ifndef _NO_EXTENSIONS
1083 entry->etype = ET_SHELL;
1084 entry->bhfi_valid = FALSE;
1085 #endif
1086
1087 last = entry;
1088 }
1089 }
1090
1091 IEnumIDList_Release(idlist);
1092 }
1093
1094 if (last)
1095 last->next = NULL;
1096
1097 dir->down = first_entry;
1098 dir->scanned = TRUE;
1099 }
1100
1101 #endif /* _SHELL_FOLDERS */
1102
1103
1104 /* sort order for different directory/file types */
1105 enum TYPE_ORDER {
1106 TO_DIR = 0,
1107 TO_DOT = 1,
1108 TO_DOTDOT = 2,
1109 TO_OTHER_DIR = 3,
1110 TO_FILE = 4
1111 };
1112
1113 /* distinguish between ".", ".." and any other directory names */
1114 static int TypeOrderFromDirname(LPCTSTR name)
1115 {
1116 if (name[0] == '.') {
1117 if (name[1] == '\0')
1118 return TO_DOT; /* "." */
1119
1120 if (name[1]=='.' && name[2]=='\0')
1121 return TO_DOTDOT; /* ".." */
1122 }
1123
1124 return TO_OTHER_DIR; /* anything else */
1125 }
1126
1127 /* directories first... */
1128 static int __cdecl compareType(const WIN32_FIND_DATA* fd1, const WIN32_FIND_DATA* fd2)
1129 {
1130 int order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1131 int order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1132
1133 /* Handle "." and ".." as special case and move them at the very first beginning. */
1134 if (order1==TO_DIR && order2==TO_DIR) {
1135 order1 = TypeOrderFromDirname(fd1->cFileName);
1136 order2 = TypeOrderFromDirname(fd2->cFileName);
1137 }
1138
1139 return order2==order1? 0: order1<order2? -1: 1;
1140 }
1141
1142
1143 static int __cdecl compareName(const void* arg1, const void* arg2)
1144 {
1145 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1146 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1147
1148 int cmp = compareType(fd1, fd2);
1149 if (cmp)
1150 return cmp;
1151
1152 return lstrcmpi(fd1->cFileName, fd2->cFileName);
1153 }
1154
1155 static int __cdecl compareExt(const void* arg1, const void* arg2)
1156 {
1157 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1158 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1159 const TCHAR *name1, *name2, *ext1, *ext2;
1160
1161 int cmp = compareType(fd1, fd2);
1162 if (cmp)
1163 return cmp;
1164
1165 name1 = fd1->cFileName;
1166 name2 = fd2->cFileName;
1167
1168 ext1 = _tcsrchr(name1, TEXT('.'));
1169 ext2 = _tcsrchr(name2, TEXT('.'));
1170
1171 if (ext1)
1172 ext1++;
1173 else
1174 ext1 = sEmpty;
1175
1176 if (ext2)
1177 ext2++;
1178 else
1179 ext2 = sEmpty;
1180
1181 cmp = lstrcmpi(ext1, ext2);
1182 if (cmp)
1183 return cmp;
1184
1185 return lstrcmpi(name1, name2);
1186 }
1187
1188 static int __cdecl compareSize(const void* arg1, const void* arg2)
1189 {
1190 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1191 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1192
1193 int cmp = compareType(fd1, fd2);
1194 if (cmp)
1195 return cmp;
1196
1197 cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
1198
1199 if (cmp < 0)
1200 return -1;
1201 else if (cmp > 0)
1202 return 1;
1203
1204 cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
1205
1206 return cmp<0? -1: cmp>0? 1: 0;
1207 }
1208
1209 static int __cdecl compareDate(const void* arg1, const void* arg2)
1210 {
1211 const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data;
1212 const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data;
1213
1214 int cmp = compareType(fd1, fd2);
1215 if (cmp)
1216 return cmp;
1217
1218 return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
1219 }
1220
1221
1222 static int (__cdecl *sortFunctions[])(const void* arg1, const void* arg2) = {
1223 compareName, /* SORT_NAME */
1224 compareExt, /* SORT_EXT */
1225 compareSize, /* SORT_SIZE */
1226 compareDate /* SORT_DATE */
1227 };
1228
1229
1230 static void SortDirectory(Entry* dir, SORT_ORDER sortOrder)
1231 {
1232 Entry* entry = dir->down;
1233 Entry** array, **p;
1234 int len;
1235
1236 len = 0;
1237 for(entry=dir->down; entry; entry=entry->next)
1238 len++;
1239
1240 if (len) {
1241 array = HeapAlloc(GetProcessHeap(), 0, len*sizeof(Entry*));
1242
1243 p = array;
1244 for(entry=dir->down; entry; entry=entry->next)
1245 *p++ = entry;
1246
1247 /* call qsort with the appropriate compare function */
1248 qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
1249
1250 dir->down = array[0];
1251
1252 for(p=array; --len; p++)
1253 p[0]->next = p[1];
1254
1255 (*p)->next = 0;
1256
1257 HeapFree(GetProcessHeap(), 0, array);
1258 }
1259 }
1260
1261
1262 static void read_directory(Entry* dir, LPCTSTR path, SORT_ORDER sortOrder, HWND hwnd)
1263 {
1264 TCHAR buffer[MAX_PATH];
1265 Entry* entry;
1266 LPCTSTR s;
1267 PTSTR d;
1268
1269 #ifdef _SHELL_FOLDERS
1270 if (dir->etype == ET_SHELL)
1271 {
1272 read_directory_shell(dir, hwnd);
1273
1274 if (Globals.prescan_node) {
1275 s = path;
1276 d = buffer;
1277
1278 while(*s)
1279 *d++ = *s++;
1280
1281 *d++ = TEXT('\\');
1282
1283 for(entry=dir->down; entry; entry=entry->next)
1284 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1285 read_directory_shell(entry, hwnd);
1286 SortDirectory(entry, sortOrder);
1287 }
1288 }
1289 }
1290 else
1291 #endif
1292 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1293 if (dir->etype == ET_UNIX)
1294 {
1295 read_directory_unix(dir, path);
1296
1297 if (Globals.prescan_node) {
1298 s = path;
1299 d = buffer;
1300
1301 while(*s)
1302 *d++ = *s++;
1303
1304 *d++ = TEXT('/');
1305
1306 for(entry=dir->down; entry; entry=entry->next)
1307 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1308 lstrcpy(d, entry->data.cFileName);
1309 read_directory_unix(entry, buffer);
1310 SortDirectory(entry, sortOrder);
1311 }
1312 }
1313 }
1314 else
1315 #endif
1316 {
1317 read_directory_win(dir, path);
1318
1319 if (Globals.prescan_node) {
1320 s = path;
1321 d = buffer;
1322
1323 while(*s)
1324 *d++ = *s++;
1325
1326 *d++ = TEXT('\\');
1327
1328 for(entry=dir->down; entry; entry=entry->next)
1329 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1330 lstrcpy(d, entry->data.cFileName);
1331 read_directory_win(entry, buffer);
1332 SortDirectory(entry, sortOrder);
1333 }
1334 }
1335 }
1336
1337 SortDirectory(dir, sortOrder);
1338 }
1339
1340
1341 static Entry* read_tree(Root* root, LPCTSTR path, LPITEMIDLIST pidl, LPTSTR drv, SORT_ORDER sortOrder, HWND hwnd)
1342 {
1343 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1344 const static TCHAR sSlash[] = {'/', '\0'};
1345 #endif
1346 const static TCHAR sBackslash[] = {'\\', '\0'};
1347
1348 #ifdef _SHELL_FOLDERS
1349 if (pidl)
1350 {
1351 /* read shell namespace tree */
1352 root->drive_type = DRIVE_UNKNOWN;
1353 drv[0] = '\\';
1354 drv[1] = '\0';
1355 load_string(root->volname, IDS_DESKTOP);
1356 root->fs_flags = 0;
1357 load_string(root->fs, IDS_SHELL);
1358
1359 return read_tree_shell(root, pidl, sortOrder, hwnd);
1360 }
1361 else
1362 #endif
1363 #if !defined(_NO_EXTENSIONS) && defined(__WINE__)
1364 if (*path == '/')
1365 {
1366 /* read unix file system tree */
1367 root->drive_type = GetDriveType(path);
1368
1369 lstrcat(drv, sSlash);
1370 load_string(root->volname, IDS_ROOT_FS);
1371 root->fs_flags = 0;
1372 load_string(root->fs, IDS_UNIXFS);
1373
1374 lstrcpy(root->path, sSlash);
1375
1376 return read_tree_unix(root, path, sortOrder, hwnd);
1377 }
1378 #endif
1379
1380 /* read WIN32 file system tree */
1381 root->drive_type = GetDriveType(path);
1382
1383 lstrcat(drv, sBackslash);
1384 GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1385
1386 lstrcpy(root->path, drv);
1387
1388 return read_tree_win(root, path, sortOrder, hwnd);
1389 }
1390
1391
1392 /* flags to filter different file types */
1393 enum TYPE_FILTER {
1394 TF_DIRECTORIES = 0x01,
1395 TF_PROGRAMS = 0x02,
1396 TF_DOCUMENTS = 0x04,
1397 TF_OTHERS = 0x08,
1398 TF_HIDDEN = 0x10,
1399 TF_ALL = 0x1F
1400 };
1401
1402
1403 static ChildWnd* alloc_child_window(LPCTSTR path, LPITEMIDLIST pidl, HWND hwnd)
1404 {
1405 TCHAR drv[_MAX_DRIVE+1], dir[_MAX_DIR], name[_MAX_FNAME], ext[_MAX_EXT];
1406 TCHAR b1[BUFFER_LEN];
1407 const static TCHAR sAsterics[] = {'*', '\0'};
1408
1409 ChildWnd* child = (ChildWnd*) malloc(sizeof(ChildWnd));
1410 Root* root = &child->root;
1411 Entry* entry;
1412
1413 memset(child, 0, sizeof(ChildWnd));
1414
1415 child->left.treePane = TRUE;
1416 child->left.visible_cols = 0;
1417
1418 child->right.treePane = FALSE;
1419 #ifndef _NO_EXTENSIONS
1420 child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS;
1421 #else
1422 child->right.visible_cols = COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES;
1423 #endif
1424
1425 child->pos.length = sizeof(WINDOWPLACEMENT);
1426 child->pos.flags = 0;
1427 child->pos.showCmd = SW_SHOWNORMAL;
1428 child->pos.rcNormalPosition.left = CW_USEDEFAULT;
1429 child->pos.rcNormalPosition.top = CW_USEDEFAULT;
1430 child->pos.rcNormalPosition.right = CW_USEDEFAULT;
1431 child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
1432
1433 child->focus_pane = 0;
1434 child->split_pos = DEFAULT_SPLIT_POS;
1435 child->sortOrder = SORT_NAME;
1436 child->header_wdths_ok = FALSE;
1437
1438 if (path)
1439 {
1440 lstrcpy(child->path, path);
1441
1442 _tsplitpath(path, drv, dir, name, ext);
1443 }
1444
1445 lstrcpy(child->filter_pattern, sAsterics);
1446 child->filter_flags = TF_ALL;
1447
1448 root->entry.level = 0;
1449
1450 entry = read_tree(root, path, pidl, drv, child->sortOrder, hwnd);
1451
1452 #ifdef _SHELL_FOLDERS
1453 if (root->entry.etype == ET_SHELL)
1454 load_string(root->entry.data.cFileName, IDS_DESKTOP);
1455 else
1456 #endif
1457 wsprintf(root->entry.data.cFileName, RS(b1,IDS_TITLEFMT), drv, root->fs);
1458
1459 root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1460
1461 child->left.root = &root->entry;
1462 child->right.root = NULL;
1463
1464 set_curdir(child, entry, 0, hwnd);
1465
1466 return child;
1467 }
1468
1469
1470 /* free all memory associated with a child window */
1471 static void free_child_window(ChildWnd* child)
1472 {
1473 free_entries(&child->root.entry);
1474 free(child);
1475 }
1476
1477
1478 /* get full path of specified directory entry */
1479 static void get_path(Entry* dir, PTSTR path)
1480 {
1481 Entry* entry;
1482 int len = 0;
1483 int level = 0;
1484
1485 #ifdef _SHELL_FOLDERS
1486 if (dir->etype == ET_SHELL)
1487 {
1488 SFGAOF attribs;
1489 HRESULT hr = S_OK;
1490
1491 path[0] = TEXT('\0');
1492
1493 attribs = 0;
1494
1495 if (dir->folder)
1496 hr = IShellFolder_GetAttributesOf(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1497
1498 if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1499 IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1500
1501 hr = path_from_pidl(parent, dir->pidl, path, MAX_PATH);
1502 }
1503 }
1504 else
1505 #endif
1506 {
1507 for(entry=dir; entry; level++) {
1508 LPCTSTR name;
1509 int l;
1510
1511 {
1512 LPCTSTR s;
1513 name = entry->data.cFileName;
1514 s = name;
1515
1516 for(l=0; *s && *s!=TEXT('/') && *s!=TEXT('\\'); s++)
1517 l++;
1518 }
1519
1520 if (entry->up) {
1521 if (l > 0) {
1522 memmove(path+l+1, path, len*sizeof(TCHAR));
1523 memcpy(path+1, name, l*sizeof(TCHAR));
1524 len += l+1;
1525
1526 #ifndef _NO_EXTENSIONS
1527 if (entry->etype == ET_UNIX)
1528 path[0] = TEXT('/');
1529 else
1530 #endif
1531 path[0] = TEXT('\\');
1532 }
1533
1534 entry = entry->up;
1535 } else {
1536 memmove(path+l, path, len*sizeof(TCHAR));
1537 memcpy(path, name, l*sizeof(TCHAR));
1538 len += l;
1539 break;
1540 }
1541 }
1542
1543 if (!level) {
1544 #ifndef _NO_EXTENSIONS
1545 if (entry->etype == ET_UNIX)
1546 path[len++] = TEXT('/');
1547 else
1548 #endif
1549 path[len++] = TEXT('\\');
1550 }
1551
1552 path[len] = TEXT('\0');
1553 }
1554 }
1555
1556
1557 static void resize_frame_rect(HWND hwnd, PRECT prect)
1558 {
1559 int new_top;
1560 RECT rt;
1561
1562 if (IsWindowVisible(Globals.htoolbar)) {
1563 SendMessage(Globals.htoolbar, WM_SIZE, 0, 0);
1564 GetClientRect(Globals.htoolbar, &rt);
1565 prect->top = rt.bottom+3;
1566 prect->bottom -= rt.bottom+3;
1567 }
1568
1569 if (IsWindowVisible(Globals.hdrivebar)) {
1570 SendMessage(Globals.hdrivebar, WM_SIZE, 0, 0);
1571 GetClientRect(Globals.hdrivebar, &rt);
1572 new_top = --prect->top + rt.bottom+3;
1573 MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
1574 prect->top = new_top;
1575 prect->bottom -= rt.bottom+2;
1576 }
1577
1578 if (IsWindowVisible(Globals.hstatusbar)) {
1579 int parts[] = {300, 500};
1580
1581 SendMessage(Globals.hstatusbar, WM_SIZE, 0, 0);
1582 SendMessage(Globals.hstatusbar, SB_SETPARTS, 2, (LPARAM)&parts);
1583 GetClientRect(Globals.hstatusbar, &rt);
1584 prect->bottom -= rt.bottom;
1585 }
1586
1587 MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1588 }
1589
1590 static void resize_frame(HWND hwnd, int cx, int cy)
1591 {
1592 RECT rect;
1593
1594 rect.left = 0;
1595 rect.top = 0;
1596 rect.right = cx;
1597 rect.bottom = cy;
1598
1599 resize_frame_rect(hwnd, &rect);
1600 }
1601
1602 static void resize_frame_client(HWND hwnd)
1603 {
1604 RECT rect;
1605
1606 GetClientRect(hwnd, &rect);
1607
1608 resize_frame_rect(hwnd, &rect);
1609 }
1610
1611
1612 static HHOOK hcbthook;
1613 static ChildWnd* newchild = NULL;
1614
1615 static LRESULT CALLBACK CBTProc(int code, WPARAM wparam, LPARAM lparam)
1616 {
1617 if (code==HCBT_CREATEWND && newchild) {
1618 ChildWnd* child = newchild;
1619 newchild = NULL;
1620
1621 child->hwnd = (HWND) wparam;
1622 SetWindowLongPtr(child->hwnd, GWLP_USERDATA, (LPARAM)child);
1623 }
1624
1625 return CallNextHookEx(hcbthook, code, wparam, lparam);
1626 }
1627
1628 static HWND create_child_window(ChildWnd* child)
1629 {
1630 MDICREATESTRUCT mcs;
1631 int idx;
1632
1633 mcs.szClass = sWINEFILETREE;
1634 mcs.szTitle = (LPTSTR)child->path;
1635 mcs.hOwner = Globals.hInstance;
1636 mcs.x = child->pos.rcNormalPosition.left;
1637 mcs.y = child->pos.rcNormalPosition.top;
1638 mcs.cx = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
1639 mcs.cy = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
1640 mcs.style = 0;
1641 mcs.lParam = 0;
1642
1643 hcbthook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
1644
1645 newchild = child;
1646 child->hwnd = (HWND) SendMessage(Globals.hmdiclient, WM_MDICREATE, 0, (LPARAM)&mcs);
1647 if (!child->hwnd) {
1648 UnhookWindowsHookEx(hcbthook);
1649 return 0;
1650 }
1651
1652 UnhookWindowsHookEx(hcbthook);
1653
1654 ListBox_SetItemHeight(child->left.hwnd, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
1655 ListBox_SetItemHeight(child->right.hwnd, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
1656
1657 idx = ListBox_FindItemData(child->left.hwnd, 0, child->left.cur);
1658 ListBox_SetCurSel(child->left.hwnd, idx);
1659
1660 return child->hwnd;
1661 }
1662
1663
1664 struct ExecuteDialog {
1665 TCHAR cmd[MAX_PATH];
1666 int cmdshow;
1667 };
1668
1669 static INT_PTR CALLBACK ExecuteDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1670 {
1671 static struct ExecuteDialog* dlg;
1672
1673 switch(nmsg) {
1674 case WM_INITDIALOG:
1675 dlg = (struct ExecuteDialog*) lparam;
1676 return 1;
1677
1678 case WM_COMMAND: {
1679 int id = (int)wparam;
1680
1681 if (id == IDOK) {
1682 GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, MAX_PATH);
1683 dlg->cmdshow = Button_GetState(GetDlgItem(hwnd,214))&BST_CHECKED?
1684 SW_SHOWMINIMIZED: SW_SHOWNORMAL;
1685 EndDialog(hwnd, id);
1686 } else if (id == IDCANCEL)
1687 EndDialog(hwnd, id);
1688
1689 return 1;}
1690 }
1691
1692 return 0;
1693 }
1694
1695
1696 static INT_PTR CALLBACK DestinationDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1697 {
1698 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
1699
1700 switch(nmsg) {
1701 case WM_INITDIALOG:
1702 SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
1703 SetWindowText(GetDlgItem(hwnd, 201), (LPCTSTR)lparam);
1704 return 1;
1705
1706 case WM_COMMAND: {
1707 int id = (int)wparam;
1708
1709 switch(id) {
1710 case IDOK: {
1711 LPTSTR dest = (LPTSTR) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1712 GetWindowText(GetDlgItem(hwnd, 201), dest, MAX_PATH);
1713 EndDialog(hwnd, id);
1714 break;}
1715
1716 case IDCANCEL:
1717 EndDialog(hwnd, id);
1718 break;
1719
1720 case 254:
1721 MessageBox(hwnd, RS(b1,IDS_NO_IMPL), RS(b2,IDS_WINEFILE), MB_OK);
1722 break;
1723 }
1724
1725 return 1;
1726 }
1727 }
1728
1729 return 0;
1730 }
1731
1732
1733 struct FilterDialog {
1734 TCHAR pattern[MAX_PATH];
1735 int flags;
1736 };
1737
1738 static INT_PTR CALLBACK FilterDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1739 {
1740 static struct FilterDialog* dlg;
1741
1742 switch(nmsg) {
1743 case WM_INITDIALOG:
1744 dlg = (struct FilterDialog*) lparam;
1745 SetWindowText(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern);
1746 Button_SetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_DIRECTORIES), (dlg->flags&TF_DIRECTORIES? BST_CHECKED: BST_UNCHECKED));
1747 Button_SetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_PROGRAMS), dlg->flags&TF_PROGRAMS? BST_CHECKED: BST_UNCHECKED);
1748 Button_SetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_DOCUMENTS), dlg->flags&TF_DOCUMENTS? BST_CHECKED: BST_UNCHECKED);
1749 Button_SetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_OTHERS), dlg->flags&TF_OTHERS? BST_CHECKED: BST_UNCHECKED);
1750 Button_SetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_HIDDEN), dlg->flags&TF_HIDDEN? BST_CHECKED: BST_UNCHECKED);
1751 return 1;
1752
1753 case WM_COMMAND: {
1754 int id = (int)wparam;
1755
1756 if (id == IDOK) {
1757 int flags = 0;
1758
1759 GetWindowText(GetDlgItem(hwnd, IDC_VIEW_PATTERN), dlg->pattern, MAX_PATH);
1760
1761 flags |= Button_GetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_DIRECTORIES))&BST_CHECKED? TF_DIRECTORIES: 0;
1762 flags |= Button_GetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_PROGRAMS))&BST_CHECKED? TF_PROGRAMS: 0;
1763 flags |= Button_GetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_DOCUMENTS))&BST_CHECKED? TF_DOCUMENTS: 0;
1764 flags |= Button_GetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_OTHERS))&BST_CHECKED? TF_OTHERS: 0;
1765 flags |= Button_GetCheck(GetDlgItem(hwnd,IDC_VIEW_TYPE_HIDDEN))&BST_CHECKED? TF_HIDDEN: 0;
1766
1767 dlg->flags = flags;
1768
1769 EndDialog(hwnd, id);
1770 } else if (id == IDCANCEL)
1771 EndDialog(hwnd, id);
1772
1773 return 1;}
1774 }
1775
1776 return 0;
1777 }
1778
1779
1780 struct PropertiesDialog {
1781 TCHAR path[MAX_PATH];
1782 Entry entry;
1783 void* pVersionData;
1784 };
1785
1786 /* Structure used to store enumerated languages and code pages. */
1787 struct LANGANDCODEPAGE {
1788 WORD wLanguage;
1789 WORD wCodePage;
1790 } *lpTranslate;
1791
1792 static LPCSTR InfoStrings[] = {
1793 "Comments",
1794 "CompanyName",
1795 "FileDescription",
1796 "FileVersion",
1797 "InternalName",
1798 "LegalCopyright",
1799 "LegalTrademarks",
1800 "OriginalFilename",
1801 "PrivateBuild",
1802 "ProductName",
1803 "ProductVersion",
1804 "SpecialBuild",
1805 NULL
1806 };
1807
1808 static void PropDlg_DisplayValue(HWND hlbox, HWND hedit)
1809 {
1810 int idx = ListBox_GetCurSel(hlbox);
1811
1812 if (idx != LB_ERR) {
1813 LPCTSTR pValue = (LPCTSTR) ListBox_GetItemData(hlbox, idx);
1814
1815 if (pValue)
1816 SetWindowText(hedit, pValue);
1817 }
1818 }
1819
1820 static void CheckForFileInfo(struct PropertiesDialog* dlg, HWND hwnd, LPCTSTR strFilename)
1821 {
1822 static TCHAR sBackSlash[] = {'\\','\0'};
1823 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'};
1824 static TCHAR sStringFileInfo[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\',
1825 '%','0','4','x','%','0','4','x','\\','%','s','\0'};
1826 DWORD dwVersionDataLen = GetFileVersionInfoSize((LPTSTR)strFilename, NULL); /* VC6 and MinGW headers use LPTSTR instead of LPCTSTR */
1827
1828 if (dwVersionDataLen) {
1829 dlg->pVersionData = malloc(dwVersionDataLen);
1830
1831 if (GetFileVersionInfo((LPTSTR)strFilename, 0, dwVersionDataLen, dlg->pVersionData)) { /* VC6 and MinGW headers use LPTSTR instead of LPCTSTR */
1832 LPVOID pVal;
1833 UINT nValLen;
1834
1835 if (VerQueryValue(dlg->pVersionData, sBackSlash, &pVal, &nValLen)) {
1836 if (nValLen == sizeof(VS_FIXEDFILEINFO)) {
1837 VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)pVal;
1838 char buffer[BUFFER_LEN];
1839
1840 sprintf(buffer, "%d.%d.%d.%d",
1841 HIWORD(pFixedFileInfo->dwFileVersionMS), LOWORD(pFixedFileInfo->dwFileVersionMS),
1842 HIWORD(pFixedFileInfo->dwFileVersionLS), LOWORD(pFixedFileInfo->dwFileVersionLS));
1843
1844 SetDlgItemTextA(hwnd, IDC_STATIC_PROP_VERSION, buffer);
1845 }
1846 }
1847
1848 /* Read the list of languages and code pages. */
1849 if (VerQueryValue(dlg->pVersionData, sTranslation, &pVal, &nValLen)) {
1850 struct LANGANDCODEPAGE* pTranslate = (struct LANGANDCODEPAGE*)pVal;
1851 struct LANGANDCODEPAGE* pEnd = (struct LANGANDCODEPAGE*)((LPBYTE)pVal+nValLen);
1852
1853 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
1854
1855 /* Read the file description for each language and code page. */
1856 for(; pTranslate<pEnd; ++pTranslate) {
1857 LPCSTR* p;
1858
1859 for(p=InfoStrings; *p; ++p) {
1860 TCHAR subblock[200];
1861 #ifdef UNICODE
1862 TCHAR infoStr[100];
1863 #endif
1864 LPCTSTR pTxt;
1865 UINT nValLen;
1866
1867 LPCSTR pInfoString = *p;
1868 #ifdef UNICODE
1869 MultiByteToWideChar(CP_ACP, 0, pInfoString, -1, infoStr, 100);
1870 #else
1871 #define infoStr pInfoString
1872 #endif
1873 wsprintf(subblock, sStringFileInfo, pTranslate->wLanguage, pTranslate->wCodePage, infoStr);
1874
1875 /* Retrieve file description for language and code page */
1876 if (VerQueryValue(dlg->pVersionData, subblock, (PVOID)&pTxt, &nValLen)) {
1877 int idx = ListBox_AddString(hlbox, infoStr);
1878 ListBox_SetItemData(hlbox, idx, pTxt);
1879 }
1880 }
1881 }
1882
1883 ListBox_SetCurSel(hlbox, 0);
1884
1885 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
1886 }
1887 }
1888 }
1889 }
1890
1891 static INT_PTR CALLBACK PropertiesDialogDlgProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
1892 {
1893 static struct PropertiesDialog* dlg;
1894
1895 switch(nmsg) {
1896 case WM_INITDIALOG: {
1897 const static TCHAR sByteFmt[] = {'%','s',' ','B','y','t','e','s','\0'};
1898 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
1899 LPWIN32_FIND_DATA pWFD;
1900 ULONGLONG size;
1901
1902 dlg = (struct PropertiesDialog*) lparam;
1903 pWFD = (LPWIN32_FIND_DATA) &dlg->entry.data;
1904
1905 GetWindowText(hwnd, b1, MAX_PATH);
1906 wsprintf(b2, b1, pWFD->cFileName);
1907 SetWindowText(hwnd, b2);
1908
1909 format_date(&pWFD->ftLastWriteTime, b1, COL_DATE|COL_TIME);
1910 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_LASTCHANGE), b1);
1911
1912 size = ((ULONGLONG)pWFD->nFileSizeHigh << 32) | pWFD->nFileSizeLow;
1913 _stprintf(b1, sLongNumFmt, size);
1914 wsprintf(b2, sByteFmt, b1);
1915 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_SIZE), b2);
1916
1917 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_FILENAME), pWFD->cFileName);
1918 SetWindowText(GetDlgItem(hwnd, IDC_STATIC_PROP_PATH), dlg->path);
1919
1920 Button_SetCheck(GetDlgItem(hwnd,IDC_CHECK_READONLY), (pWFD->dwFileAttributes&FILE_ATTRIBUTE_READONLY? BST_CHECKED: BST_UNCHECKED));
1921 Button_SetCheck(GetDlgItem(hwnd,IDC_CHECK_ARCHIVE), (pWFD->dwFileAttributes&FILE_ATTRIBUTE_ARCHIVE? BST_CHECKED: BST_UNCHECKED));
1922 Button_SetCheck(GetDlgItem(hwnd,IDC_CHECK_COMPRESSED), (pWFD->dwFileAttributes&FILE_ATTRIBUTE_COMPRESSED? BST_CHECKED: BST_UNCHECKED));
1923 Button_SetCheck(GetDlgItem(hwnd,IDC_CHECK_HIDDEN), (pWFD->dwFileAttributes&FILE_ATTRIBUTE_HIDDEN? BST_CHECKED: BST_UNCHECKED));
1924 Button_SetCheck(GetDlgItem(hwnd,IDC_CHECK_SYSTEM), (pWFD->dwFileAttributes&FILE_ATTRIBUTE_SYSTEM? BST_CHECKED: BST_UNCHECKED));
1925
1926 CheckForFileInfo(dlg, hwnd, dlg->path);
1927 return 1;}
1928
1929 case WM_COMMAND: {
1930 int id = (int)wparam;
1931
1932 switch(HIWORD(wparam)) {
1933 case LBN_SELCHANGE: {
1934 HWND hlbox = GetDlgItem(hwnd, IDC_LIST_PROP_VERSION_TYPES);
1935 PropDlg_DisplayValue(hlbox, GetDlgItem(hwnd,IDC_LIST_PROP_VERSION_VALUES));
1936 break;
1937 }
1938
1939 case BN_CLICKED:
1940 if (id==IDOK || id==IDCANCEL)
1941 EndDialog(hwnd, id);
1942 }
1943
1944 return 1;}
1945
1946 case WM_NCDESTROY:
1947 free(dlg->pVersionData);
1948 dlg->pVersionData = NULL;
1949 break;
1950 }
1951
1952 return 0;
1953 }
1954
1955 static void show_properties_dlg(Entry* entry, HWND hwnd)
1956 {
1957 struct PropertiesDialog dlg;
1958
1959 memset(&dlg, 0, sizeof(struct PropertiesDialog));
1960 get_path(entry, dlg.path);
1961 memcpy(&dlg.entry, entry, sizeof(Entry));
1962
1963 DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_DIALOG_PROPERTIES), hwnd, PropertiesDialogDlgProc, (LPARAM)&dlg);
1964 }
1965
1966
1967 #ifndef _NO_EXTENSIONS
1968
1969 static struct FullScreenParameters {
1970 BOOL mode;
1971 RECT orgPos;
1972 BOOL wasZoomed;
1973 } g_fullscreen = {
1974 FALSE, /* mode */
1975 {0, 0, 0, 0},
1976 FALSE
1977 };
1978
1979 static void frame_get_clientspace(HWND hwnd, PRECT prect)
1980 {
1981 RECT rt;
1982
1983 if (!IsIconic(hwnd))
1984 GetClientRect(hwnd, prect);
1985 else {
1986 WINDOWPLACEMENT wp;
1987
1988 GetWindowPlacement(hwnd, &wp);
1989
1990 prect->left = prect->top = 0;
1991 prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
1992 2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
1993 prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
1994 2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
1995 GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
1996 }
1997
1998 if (IsWindowVisible(Globals.htoolbar)) {
1999 GetClientRect(Globals.htoolbar, &rt);
2000 prect->top += rt.bottom+2;
2001 }
2002
2003 if (IsWindowVisible(Globals.hdrivebar)) {
2004 GetClientRect(Globals.hdrivebar, &rt);
2005 prect->top += rt.bottom+2;
2006 }
2007
2008 if (IsWindowVisible(Globals.hstatusbar)) {
2009 GetClientRect(Globals.hstatusbar, &rt);
2010 prect->bottom -= rt.bottom;
2011 }
2012 }
2013
2014 static BOOL toggle_fullscreen(HWND hwnd)
2015 {
2016 RECT rt;
2017
2018 if ((g_fullscreen.mode=!g_fullscreen.mode)) {
2019 GetWindowRect(hwnd, &g_fullscreen.orgPos);
2020 g_fullscreen.wasZoomed = IsZoomed(hwnd);
2021
2022 Frame_CalcFrameClient(hwnd, &rt);
2023 ClientToScreen(hwnd, (LPPOINT)&rt.left);
2024 ClientToScreen(hwnd, (LPPOINT)&rt.right);
2025
2026 rt.left = g_fullscreen.orgPos.left-rt.left;
2027 rt.top = g_fullscreen.orgPos.top-rt.top;
2028 rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
2029 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
2030
2031 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2032 } else {
2033 MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
2034 g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
2035 g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
2036
2037 if (g_fullscreen.wasZoomed)
2038 ShowWindow(hwnd, WS_MAXIMIZE);
2039 }
2040
2041 return g_fullscreen.mode;
2042 }
2043
2044 static void fullscreen_move(HWND hwnd)
2045 {
2046 RECT rt, pos;
2047 GetWindowRect(hwnd, &pos);
2048
2049 Frame_CalcFrameClient(hwnd, &rt);
2050 ClientToScreen(hwnd, (LPPOINT)&rt.left);
2051 ClientToScreen(hwnd, (LPPOINT)&rt.right);
2052
2053 rt.left = pos.left-rt.left;
2054 rt.top = pos.top-rt.top;
2055 rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
2056 rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
2057
2058 MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
2059 }
2060
2061 #endif
2062
2063
2064 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
2065 {
2066 BOOL vis = IsWindowVisible(hchild);
2067
2068 CheckMenuItem(Globals.hMenuOptions, cmd, vis?MF_BYCOMMAND:MF_BYCOMMAND|MF_CHECKED);
2069
2070 ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
2071
2072 #ifndef _NO_EXTENSIONS
2073 if (g_fullscreen.mode)
2074 fullscreen_move(hwnd);
2075 #endif
2076
2077 resize_frame_client(hwnd);
2078 }
2079
2080 static BOOL activate_drive_window(LPCTSTR path)
2081 {
2082 TCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
2083 HWND child_wnd;
2084
2085 _tsplitpath(path, drv1, 0, 0, 0);
2086
2087 /* search for a already open window for the same drive */
2088 for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2089 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(child_wnd, GWLP_USERDATA);
2090
2091 if (child) {
2092 _tsplitpath(child->root.path, drv2, 0, 0, 0);
2093
2094 if (!lstrcmpi(drv2, drv1)) {
2095 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2096
2097 if (IsMinimized(child_wnd))
2098 ShowWindow(child_wnd, SW_SHOWNORMAL);
2099
2100 return TRUE;
2101 }
2102 }
2103 }
2104
2105 return FALSE;
2106 }
2107
2108 static BOOL activate_fs_window(LPCTSTR filesys)
2109 {
2110 HWND child_wnd;
2111
2112 /* search for a already open window of the given file system name */
2113 for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2114 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(child_wnd, GWLP_USERDATA);
2115
2116 if (child) {
2117 if (!lstrcmpi(child->root.fs, filesys)) {
2118 SendMessage(Globals.hmdiclient, WM_MDIACTIVATE, (WPARAM)child_wnd, 0);
2119
2120 if (IsMinimized(child_wnd))
2121 ShowWindow(child_wnd, SW_SHOWNORMAL);
2122
2123 return TRUE;
2124 }
2125 }
2126 }
2127
2128 return FALSE;
2129 }
2130
2131 static LRESULT CALLBACK FrameWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
2132 {
2133 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2134
2135 switch(nmsg) {
2136 case WM_CLOSE:
2137 DestroyWindow(hwnd);
2138
2139 /* clear handle variables */
2140 Globals.hMenuFrame = 0;
2141 Globals.hMenuView = 0;
2142 Globals.hMenuOptions = 0;
2143 Globals.hMainWnd = 0;
2144 Globals.hmdiclient = 0;
2145 Globals.hdrivebar = 0;
2146 break;
2147
2148 case WM_DESTROY:
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 HFONT hFontOld;
2251
2252 DeleteObject(Globals.hfont);
2253 Globals.hfont = CreateFontIndirect(&lFont);
2254 hFontOld = SelectFont(hdc, Globals.hfont);
2255 GetTextExtentPoint32(hdc, sSpace, 1, &Globals.spaceSize);
2256
2257 /* change font in all open child windows */
2258 for(childWnd=GetWindow(Globals.hmdiclient,GW_CHILD); childWnd; childWnd=GetNextWindow(childWnd,GW_HWNDNEXT)) {
2259 ChildWnd* child = (ChildWnd*) GetWindowLongPtr(childWnd, GWLP_USERDATA);
2260 SetWindowFont(child->left.hwnd, Globals.hfont, TRUE);
2261 SetWindowFont(child->right.hwnd, Globals.hfont, TRUE);
2262 ListBox_SetItemHeight(child->left.hwnd, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
2263 ListBox_SetItemHeight(child->right.hwnd, 1, max(Globals.spaceSize.cy,IMAGE_HEIGHT+3));
2264 InvalidateRect(child->left.hwnd, NULL, TRUE);
2265 InvalidateRect(child->right.hwnd, NULL, TRUE);
2266 }
2267
2268 SelectFont(hdc, hFontOld);
2269 }
2270 else if (CommDlgExtendedError()) {
2271 LoadString(Globals.hInstance, IDS_FONT_SEL_DLG_NAME, dlg_name, BUFFER_LEN);
2272 LoadString(Globals.hInstance, IDS_FONT_SEL_ERROR, dlg_info, BUFFER_LEN);
2273 MessageBox(hwnd, dlg_info, dlg_name, MB_OK);
2274 }
2275
2276 ReleaseDC(hwnd, hdc);
2277 break;
2278 }
2279
2280 case ID_VIEW_TOOL_BAR:
2281 toggle_child(hwnd, cmd, Globals.htoolbar);
2282 break;
2283
2284 case ID_VIEW_DRIVE_BAR:
2285 toggle_child(hwnd, cmd, Globals.hdrivebar);
2286 break;
2287
2288 case ID_VIEW_STATUSBAR:
2289 toggle_child(hwnd, cmd, Globals.hstatusbar);
2290 break;
2291
2292 case ID_EXECUTE: {
2293 struct ExecuteDialog dlg;
2294
2295 memset(&dlg, 0, sizeof(struct ExecuteDialog));
2296
2297 if (DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(IDD_EXECUTE), hwnd, ExecuteDialogDlgProc, (LPARAM)&dlg) == IDOK) {
2298 HINSTANCE hinst = ShellExecute(hwnd, NULL/*operation*/, dlg.cmd/*file*/, NULL/*parameters*/, NULL/*dir*/, dlg.cmdshow);
2299
2300 if ((int)hinst <= 32)
2301 display_error(hwnd, GetLastError());
2302 }
2303 break;}
2304
2305 case ID_CONNECT_NETWORK_DRIVE: {
2306 DWORD ret = WNetConnectionDialog(hwnd, RESOURCETYPE_DISK);
2307 if (ret == NO_ERROR)
2308 refresh_drives();
2309 else if (ret != (DWORD)-1) {
2310 if (ret == ERROR_EXTENDED_ERROR)
2311 display_network_error(hwnd);
2312 else
2313 display_error(hwnd, ret);
2314 }
2315 break;}
2316
2317 case ID_DISCONNECT_NETWORK_DRIVE: {
2318 DWORD ret = WNetDisconnectDialog(hwnd, RESOURCETYPE_DISK);
2319 if (ret == NO_ERROR)
2320 refresh_drives();
2321 else if (ret != (DWORD)-1) {
2322 if (ret == ERROR_EXTENDED_ERROR)
2323 display_network_error(hwnd);
2324 else
2325 display_error(hwnd, ret);
2326 }
2327 break;}
2328
2329 #ifndef __MINGW32__ /* SHFormatDrive missing in MinGW (as of 13.5.2005) */
2330 case ID_FORMAT_DISK: {
2331 UINT sem_org = SetErrorMode(0); /* Get the current Error Mode settings. */
2332 SetErrorMode(sem_org & ~SEM_FAILCRITICALERRORS); /* Force O/S to handle */
2333 SHFormatDrive(hwnd, 0 /* A: */, SHFMT_ID_DEFAULT, 0);
2334 SetErrorMode(sem_org); /* Put it back the way it was. */
2335 break;}
2336 #endif
2337
2338 case ID_HELP:
2339 WinHelp(hwnd, RS(b1,IDS_WINEFILE), HELP_INDEX, 0);
2340 break;
2341
2342 #ifndef _NO_EXTENSIONS
2343 case ID_VIEW_FULLSCREEN:
2344 CheckMenuItem(Globals.hMenuOptions, cmd, toggle_fullscreen(hwnd)?MF_CHECKED:0);
2345 break;
2346
2347 #ifdef __WINE__
2348 case ID_DRIVE_UNIX_FS: {
2349 TCHAR path[MAX_PATH];
2350 #ifdef UNICODE
2351 char cpath[MAX_PATH];
2352 #endif
2353 ChildWnd* child;
2354
2355 if (activate_fs_window(RS(b1,IDS_UNIXFS)))
2356 break;
2357
2358
2359 #ifdef UNICODE
2360 getcwd(cpath, MAX_PATH);
2361 MultiByteToWideChar(CP_UNIXCP, 0, cpath, -1, path, MAX_PATH);
2362 #else
2363 getcwd(path, MAX_PATH);
2364 #endif
2365 child = alloc_child_window(path, NULL, hwnd);
2366
2367 if (!create_child_window(child))
2368 free(child);
2369 break;}
2370 #endif
2371
2372 #ifdef _SHELL_FOLDERS
2373 case ID_DRIVE_SHELL_NS: {
2374 TCHAR path[MAX_PATH];
2375 ChildWnd* child;
2376
2377 if (activate_fs_window(RS(b1,IDS_SHELL)))
2378 break;
2379
2380 GetCurrentDirectory(MAX_PATH, path);
2381 child = alloc_child_window(path, get_path_pidl(path,hwnd), hwnd);
2382
2383 if (!create_child_window(child))
2384 free(child);
2385 break;}
2386 #endif
2387 #endif
2388
2389 /*TODO: There are even more menu items! */
2390
2391 #ifndef _NO_EXTENSIONS
2392 #ifdef __WINE__
2393 case ID_LICENSE:
2394 WineLicense(Globals.hMainWnd);
2395 break;
2396
2397 case ID_NO_WARRANTY:
2398 WineWarranty(Globals.hMainWnd);
2399 break;
2400
2401 case ID_ABOUT_WINE:
2402 ShellAbout(hwnd, RS(b2,IDS_WINE), RS(b1,IDS_WINEFILE), 0);
2403 break;
2404 #endif
2405
2406 case ID_ABOUT:
2407 ShellAbout(hwnd, RS(b1,IDS_WINEFILE), NULL, 0);
2408 break;
2409 #endif /* _NO_EXTENSIONS */
2410
2411 default:
2412 /*TODO: if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
2413 STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
2414 else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
2415 (cmd<SC_SIZE || cmd>SC_RESTORE))
2416 MessageBox(hwnd, RS(b2,IDS_NO_IMPL), RS(b1,IDS_WINEFILE), MB_OK);
2417
2418 return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2419 }
2420 break;}
2421
2422 case WM_SIZE:
2423 resize_frame(hwnd, LOWORD(lparam), HIWORD(lparam));
2424 break; /* do not pass message to DefFrameProc */
2425
2426 #ifndef _NO_EXTENSIONS
2427 case WM_GETMINMAXINFO: {
2428 LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
2429
2430 lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
2431 lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
2432 break;}
2433
2434 case FRM_CALC_CLIENT:
2435 frame_get_clientspace(hwnd, (PRECT)lparam);
2436 return TRUE;
2437 #endif /* _NO_EXTENSIONS */
2438
2439 default:
2440 return DefFrameProc(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2441 }
2442
2443 return 0;
2444 }
2445
2446
2447 static TCHAR g_pos_names[COLUMNS][20] = {
2448 {'\0'} /* symbol */
2449 };
2450
2451 static const int g_pos_align[] = {
2452 0,
2453 HDF_LEFT, /* Name */
2454 HDF_RIGHT, /* Size */
2455 HDF_LEFT, /* CDate */
2456 #ifndef _NO_EXTENSIONS
2457 HDF_LEFT, /* ADate */
2458 HDF_LEFT, /* MDate */
2459 HDF_LEFT, /* Index */
2460 HDF_CENTER, /* Links */
2461 #endif
2462 HDF_CENTER, /* Attributes */
2463 #ifndef _NO_EXTENSIONS
2464 HDF_LEFT /* Security */
2465 #endif
2466 };
2467
2468 static void resize_tree(ChildWnd* child, int cx, int cy)
2469 {
2470 HDWP hdwp = BeginDeferWindowPos(4);
2471 RECT rt;
2472
2473 rt.left = 0;
2474 rt.top = 0;
2475 rt.right = cx;
2476 rt.bottom = cy;
2477
2478 cx = child->split_pos + SPLIT_WIDTH/2;
2479
2480 #ifndef _NO_EXTENSIONS
2481 {
2482 WINDOWPOS wp;
2483 HD_LAYOUT hdl;
2484
2485 hdl.prc = &rt;
2486 hdl.pwpos = &wp;
2487
2488 Header_Layout(child->left.hwndHeader, &hdl);
2489
2490 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
2491 wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
2492 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
2493 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
2494 }
2495 #endif /* _NO_EXTENSIONS */
2496
2497 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);
2498 DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
2499
2500 EndDeferWindowPos(hdwp);
2501 }
2502
2503
2504 #ifndef _NO_EXTENSIONS
2505
2506 static HWND create_header(HWND parent, Pane* pane, int id)
2507 {
2508 HD_ITEM hdi;
2509 int idx;
2510
2511 HWND hwnd = CreateWindow(WC_HEADER, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ/*TODO: |HDS_BUTTONS + sort orders*/,
2512 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
2513 if (!hwnd)
2514 return 0;
2515
2516 SetWindowFont(hwnd, GetStockObject(DEFAULT_GUI_FONT), FALSE);
2517
2518 hdi.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
2519
2520 for(idx=0; idx<COLUMNS; idx++) {
2521 hdi.pszText = g_pos_names[idx];
2522 hdi.fmt = HDF_STRING | g_pos_align[idx];
2523 hdi.cxy = pane->widths[idx];
2524 Header_InsertItem(hwnd, idx, &hdi);
2525 }
2526
2527 return hwnd;
2528 }
2529
2530 #endif /* _NO_EXTENSIONS */
2531
2532
2533 static void init_output(HWND hwnd)
2534 {
2535 const static TCHAR s1000[] = {'1','0','0','0','\0'};
2536
2537 TCHAR b[16];
2538 HFONT old_font;
2539 HDC hdc = GetDC(hwnd);
2540
2541 if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, s1000, 0, b, 16) > 4)
2542 Globals.num_sep = b[1];
2543 else
2544 Globals.num_sep = TEXT('.');
2545
2546 old_font = SelectFont(hdc, Globals.hfont);
2547 GetTextExtentPoint32(hdc, sSpace, 1, &Globals.spaceSize);
2548 SelectFont(hdc, old_font);
2549 ReleaseDC(hwnd, hdc);
2550 }
2551
2552 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
2553
2554
2555 /* calculate preferred width for all visible columns */
2556
2557 static BOOL calc_widths(Pane* pane, BOOL anyway)
2558 {
2559 int col, x, cx, spc=3*Globals.spaceSize.cx;
2560 int entries = ListBox_GetCount(pane->hwnd);
2561 int orgWidths[COLUMNS];
2562 int orgPositions[COLUMNS+1];
2563 HFONT hfontOld;
2564 HDC hdc;
2565 int cnt;
2566
2567 if (!anyway) {
2568 memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2569 memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2570 }
2571
2572 for(col=0; col<COLUMNS; col++)
2573 pane->widths[col] = 0;
2574
2575 hdc = GetDC(pane->hwnd);
2576 hfontOld = SelectFont(hdc, Globals.hfont);
2577
2578 for(cnt=0; cnt<entries; cnt++) {
2579 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
2580
2581 DRAWITEMSTRUCT dis;
2582
2583 dis.CtlType = 0;
2584 dis.CtlID = 0;
2585 dis.itemID = 0;
2586 dis.itemAction = 0;
2587 dis.itemState = 0;
2588 dis.hwndItem = pane->hwnd;
2589 dis.hDC = hdc;
2590 dis.rcItem.left = 0;
2591 dis.rcItem.top = 0;
2592 dis.rcItem.right = 0;
2593 dis.rcItem.bottom = 0;
2594 /*dis.itemData = 0; */
2595
2596 draw_item(pane, &dis, entry, COLUMNS);
2597 }
2598
2599 SelectObject(hdc, hfontOld);
2600 ReleaseDC(pane->hwnd, hdc);
2601
2602 x = 0;
2603 for(col=0; col<COLUMNS; col++) {
2604 pane->positions[col] = x;
2605 cx = pane->widths[col];
2606
2607 if (cx) {
2608 cx += spc;
2609
2610 if (cx < IMAGE_WIDTH)
2611 cx = IMAGE_WIDTH;
2612
2613 pane->widths[col] = cx;
2614 }
2615
2616 x += cx;
2617 }
2618
2619 pane->positions[COLUMNS] = x;
2620
2621 ListBox_SetHorizontalExtent(pane->hwnd, x);
2622
2623 /* no change? */
2624 if (!memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2625 return FALSE;
2626
2627 /* don't move, if only collapsing an entry */
2628 if (!anyway && pane->widths[0]<orgWidths[0] &&
2629 !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
2630 pane->widths[0] = orgWidths[0];
2631 memcpy(pane->positions, orgPositions, sizeof(orgPositions));
2632
2633 return FALSE;
2634 }
2635
2636 InvalidateRect(pane->hwnd, 0, TRUE);
2637
2638 return TRUE;
2639 }
2640
2641
2642 /* calculate one preferred column width */
2643
2644 static void calc_single_width(Pane* pane, int col)
2645 {
2646 HFONT hfontOld;
2647 int x, cx;
2648 int entries = ListBox_GetCount(pane->hwnd);
2649 int cnt;
2650 HDC hdc;
2651
2652 pane->widths[col] = 0;
2653
2654 hdc = GetDC(pane->hwnd);
2655 hfontOld = SelectFont(hdc, Globals.hfont);
2656
2657 for(cnt=0; cnt<entries; cnt++) {
2658 Entry* entry = (Entry*) ListBox_GetItemData(pane->hwnd, cnt);
2659 DRAWITEMSTRUCT dis;
2660
2661 dis.CtlType = 0;
2662 dis.CtlID = 0;
2663 dis.itemID = 0;
2664 dis.itemAction = 0;
2665 dis.itemState = 0;
2666 dis.hwndItem = pane->hwnd;
2667 dis.hDC = hdc;
2668 dis.rcItem.left = 0;
2669 dis.rcItem.top = 0;
2670 dis.rcItem.right = 0;
2671 dis.rcItem.bottom = 0;
2672 /*dis.itemData = 0; */
2673
2674 draw_item(pane, &dis, entry, col);
2675 }
2676
2677 SelectObject(hdc, hfontOld);
2678 ReleaseDC(pane->hwnd, hdc);
2679
2680 cx = pane->widths[col];
2681
2682 if (cx) {
2683 cx += 3*Globals.spaceSize.cx;
2684
2685 if (cx < IMAGE_WIDTH)
2686 cx = IMAGE_WIDTH;
2687 }
2688
2689 pane->widths[col] = cx;
2690
2691 x = pane->positions[col] + cx;
2692
2693 for(; col<COLUMNS; ) {
2694 pane->positions[++col] = x;
2695 x += pane->widths[col];
2696 }
2697
2698 ListBox_SetHorizontalExtent(pane->hwnd, x);
2699 }
2700
2701
2702 static BOOL pattern_match(LPCTSTR str, LPCTSTR pattern)
2703 {
2704 for( ; *str&&*pattern; str++,pattern++) {
2705 if (*pattern == '*') {
2706 do pattern++;
2707 while(*pattern == '*');
2708
2709 if (!*pattern)
2710 return TRUE;
2711
2712 for(; *str; str++)
2713 if (*str==*pattern && pattern_match(str, pattern))
2714 return TRUE;
2715
2716 return FALSE;
2717 }
2718 else if (*str!=*pattern && *pattern!='?')
2719 return FALSE;
2720 }
2721
2722 if (*str || *pattern)
2723 if (*pattern!='*' || pattern[1]!='\0')
2724 return FALSE;
2725
2726 return TRUE;
2727 }
2728
2729 static BOOL pattern_imatch(LPCTSTR str, LPCTSTR pattern)
2730 {
2731 TCHAR b1[BUFFER_LEN], b2[BUFFER_LEN];
2732
2733 lstrcpy(b1, str);
2734 lstrcpy(b2, pattern);
2735 CharUpper(b1);
2736 CharUpper(b2);
2737
2738 return pattern_match(b1, b2);
2739 }
2740
2741
2742 enum FILE_TYPE {
2743 FT_OTHER = 0,
2744 FT_EXECUTABLE = 1,
2745 FT_DOCUMENT = 2
2746 };
2747
2748 static enum FILE_TYPE get_file_type(LPCTSTR filename);
2749
2750
2751 /* insert listbox entries after index idx */
2752
2753 static int insert_entries(Pane* pane, Entry* dir, LPCTSTR pattern, int filter_flags, int idx)
2754 {
2755 Entry* entry = dir;
2756
2757 if (!entry)
2758 return idx;
2759
2760 ShowWindow(pane->hwnd, SW_HIDE);
2761
2762 for(; entry; entry=entry->next) {
2763 #ifndef _LEFT_FILES
2764 if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2765 continue;
2766 #endif
2767
2768 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2769 /* don't display entries "." and ".." in the left pane */
2770 if (pane->treePane && entry->data.cFileName[0]==TEXT('.'))
2771 if (
2772 #ifndef _NO_EXTENSIONS
2773 entry->data.cFileName[1]==TEXT('\0') ||
2774 #endif
2775 (entry->data.cFileName[1]==TEXT('.') && entry->data.cFileName[2]==TEXT('\0')))
2776 continue;
2777
2778 /* filter directories in right pane */
2779 if (!pane->treePane && !(filter_flags&TF_DIRECTORIES))
2780 continue;
2781 }
2782
2783 /* filter using the file name pattern */
2784 if (pattern)
2785 if (!pattern_imatch(entry->data.cFileName, pattern))
2786 continue;
2787
2788 /* filter system and hidden files */
2789 if (!(filter_flags&TF_HIDDEN) && (entry->data.dwFileAttributes&(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)))
2790 continue;
2791
2792 /* filter looking at the file type */
2793 if ((filter_flags&(TF_PROGRAMS|TF_DOCUMENTS|TF_OTHERS)) != (TF_PROGRAMS|TF_DOCUMENTS|TF_OTHERS))
2794 switch(get_file_type(entry->data.cFileName)) {
2795 case FT_EXECUTABLE:
2796 if (!(filter_flags & TF_PROGRAMS))
2797 continue;
2798 break;
2799
2800 case FT_DOCUMENT:
2801 if (!(filter_flags & TF_DOCUMENTS))
2802 continue;
2803 break;
2804
2805 default: /* TF_OTHERS */
2806 if (!(filter_flags & TF_OTHERS))
2807 continue;
2808 }
2809
2810 if (idx != -1)
2811 idx++;
2812
2813 ListBox_InsertItemData(pane->hwnd, idx, entry);
2814
2815 if (pane->treePane && entry->expanded)
2816 idx = insert_entries(pane, entry->down, pattern, filter_flags, idx);
2817 }
2818
2819 ShowWindow(pane->hwnd, SW_SHOW);
2820
2821 return idx;
2822 }
2823
2824
2825 static void format_bytes(LPTSTR buffer, LONGLONG bytes)
2826 {
2827 const static TCHAR sFmtGB[] = {'%', '.', '1', 'f', ' ', 'G', 'B', '\0'};
2828 const static TCHAR sFmtMB[] = {'%', '.', '1', 'f', ' ', 'M', 'B', '\0'};
2829 const static TCHAR sFmtkB[] = {'%', '.', '1', 'f', ' ', 'k', 'B', '\0'};
2830
2831 float fBytes = (float)bytes;
2832
2833 #ifdef __WINE__ /* work around for incorrect implementation of wsprintf()/_stprintf() in WINE */
2834 if (bytes >= 1073741824) /* 1 GB */
2835 wsprintf(buffer, sFmtGB, fBytes/1073741824.f+.5f);
2836 else if (bytes >= 1048576) /* 1 MB */
2837 wsprintf(buffer, sFmtMB, fBytes/1048576.f+.5f);
2838 else if (bytes >= 1024) /* 1 kB */
2839 wsprintf(buffer, sFmtkB, fBytes/1024.f+.5f);
2840 #else
2841 if (bytes >= 1073741824) /* 1 GB */
2842 _stprintf(buffer, sFmtGB, fBytes/1073741824.f+.5f);
2843 else if (bytes >= 1048576) /* 1 MB */
2844 _stprintf(buffer, sFmtMB, fBytes/1048576.f+.5f);
2845 else if (bytes >= 1024) /* 1 kB */
2846 _stprintf(buffer, sFmtkB, fBytes/1024.f+.5f);
2847 #endif
2848 else
2849 _stprintf(buffer, sLongNumFmt, bytes);
2850 }
2851
2852 static void set_space_status(void)
2853 {
2854 ULARGE_INTEGER ulFreeBytesToCaller, ulTotalBytes, ulFreeBytes;
2855 TCHAR fmt[64], b1[64], b2[64], buffer[BUFFER_LEN];
2856
2857 if (GetDiskFreeSpaceEx(NULL, &ulFreeBytesToCaller, &ulTotalBytes, &ulFreeBytes)) {
2858 format_bytes(b1, ulFreeBytesToCaller.QuadPart);
2859 format_bytes(b2, ulTotalBytes.QuadPart);
2860 wsprintf(buffer, RS(fmt,IDS_FREE_SPACE_FMT), b1, b2);
2861 } else
2862 lstrcpy(buffer, sQMarks);
2863
2864 SendMessage(Globals.hstatusbar, SB_SETTEXT, 0, (LPARAM)buffer);
2865 }
2866
2867
2868 static WNDPROC g_orgTreeWndProc;
2869
2870 static void create_tree_window(HWND parent, Pane* pane, int id, int id_header, LPCTSTR pattern, int filter_flags)
2871 {
2872 const static TCHAR sListBox[] = {'L','i','s','t','B','o','x','\0'};
2873
2874 static int s_init = 0;
2875 Entry* entry = pane->root;
2876
2877 pane->hwnd = CreateWindow(sListBox, sEmpty, WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL|
2878 LBS_DISABLENOSCROLL|LBS_NOINTEGRALHEIGHT|LBS_OWNERDRAWFIXED|LBS_NOTIFY,
2879 0, 0, 0, 0, parent, (HMENU)id, Globals.hInstance, 0);
2880
2881 SetWindowLongPtr(pane->hwnd, GWLP_USERDATA, (LPARAM)pane);
2882 g_orgTreeWndProc = SubclassWindow(pane->hwnd, TreeWndProc);
2883
2884 SetWindowFont(pane->hwnd, Globals.hfont, FALSE);
2885
2886 /* insert entries into listbox */
2887 if (entry)
2888 insert_entries(pane, entry, pattern, filter_flags, -1);
2889
2890 /* calculate column widths */
2891 if (!s_init) {
2892 s_init = 1;
2893 init_output(pane->hwnd);
2894 }
2895
2896 calc_widths(pane, TRUE);
2897
2898 #ifndef _NO_EXTENSIONS
2899 pane->hwndHeader = create_header(parent, pane, id_header);
2900 #endif
2901 }
2902
2903
2904 static void InitChildWindow(ChildWnd* child)
2905 {
2906 create_tree_window(child->hwnd, &child->left, IDW_TREE_LEFT, IDW_HEADER_LEFT, NULL, TF_ALL);
2907 create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, child->filter_pattern, child->filter_flags);
2908 }
2909
2910
2911 static void format_date(const FILETIME* ft, TCHAR* buffer, int visible_cols)
2912 {
2913 SYSTEMTIME systime;
2914 FILETIME lft;
2915 int len = 0;
2916
2917 *buffer = TEXT('\0');
2918
2919 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2920 return;
2921
2922 if (!FileTimeToLocalFileTime(ft, &lft))
2923 {err: lstrcpy(buffer,sQMarks); return;}
2924
2925 if (!FileTimeToSystemTime(&lft, &systime))
2926 goto err;
2927
2928 if (visible_cols & COL_DATE) {
2929 len = GetDateFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer, BUFFER_LEN);
2930 if (!len)
2931 goto err;
2932 }
2933
2934 if (visible_cols & COL_TIME) {
2935 if (len)
2936 buffer[len-1] = ' ';
2937
2938 buffer[len++] = ' ';
2939
2940 if (!GetTimeFormat(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
2941 buffer[len] = TEXT('\0');
2942 }
2943 }
2944
2945
2946 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2947 {
2948 RECT rt = {0, 0, 0, 0};
2949
2950 DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX);
2951
2952 if (rt.right > pane->widths[col])
2953 pane->widths[col] = rt.right;
2954 }
2955
2956 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2957 {
2958 RECT rt = {0, 0, 0, 0};
2959
2960 /* DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
2961 DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
2962
2963 DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_CALCRECT|DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2964 /*FIXME rt (0,0) ??? */
2965
2966 if (rt.right > pane->widths[col])
2967 pane->widths[col] = rt.right;
2968 }
2969
2970
2971 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str, DWORD flags)
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 DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
2982 }
2983
2984 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
2985 {
2986 int x = dis->rcItem.left;
2987 RECT rt;
2988
2989 rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
2990 rt.top = dis->rcItem.top;
2991 rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
2992 rt.bottom = dis->rcItem.bottom;
2993
2994 /* DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS), 2};
2995 DrawTextEx(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|DT_EXPANDTABS|DT_TABSTOP, &dtp);*/
2996
2997 DrawText(dis->hDC, (LPTSTR)str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2998 }
2999
3000 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCTSTR str)
3001 {
3002 int x