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