- 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',' '