ReactOS  0.4.12-dev-685-gf36cbf7
winefile.c
Go to the documentation of this file.
1 /*
2  * Winefile
3  *
4  * Copyright 2000, 2003, 2004, 2005 Martin Fuchs
5  * Copyright 2006 Jason Green
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #ifdef __WINE__
23 #include "config.h"
24 #include "wine/port.h"
25 
26 /* for unix filesystem function calls */
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #endif
31 
32 #define COBJMACROS
33 
34 #include "winefile.h"
35 #include "resource.h"
36 #include "wine/unicode.h"
37 
38 #ifndef _MAX_PATH
39 #define _MAX_DRIVE 3
40 #define _MAX_FNAME 256
41 #define _MAX_DIR _MAX_FNAME
42 #define _MAX_EXT _MAX_FNAME
43 #define _MAX_PATH 260
44 #endif
45 
46 #ifdef NONAMELESSUNION
47 #define UNION_MEMBER(x) DUMMYUNIONNAME.x
48 #else
49 #define UNION_MEMBER(x) x
50 #endif
51 
52 #define DEFAULT_SPLIT_POS 300
53 
54 static const WCHAR registry_key[] = { 'S','o','f','t','w','a','r','e','\\',
55  'W','i','n','e','\\',
56  'W','i','n','e','F','i','l','e','\0'};
57 static const WCHAR reg_start_x[] = { 's','t','a','r','t','X','\0'};
58 static const WCHAR reg_start_y[] = { 's','t','a','r','t','Y','\0'};
59 static const WCHAR reg_width[] = { 'w','i','d','t','h','\0'};
60 static const WCHAR reg_height[] = { 'h','e','i','g','h','t','\0'};
61 static const WCHAR reg_logfont[] = { 'l','o','g','f','o','n','t','\0'};
62 
63 enum ENTRY_TYPE {
67 };
68 
69 typedef struct _Entry {
70  struct _Entry* next;
71  struct _Entry* down;
72  struct _Entry* up;
73 
76  int level;
77 
79 
86 } Entry;
87 
88 typedef struct {
91  WCHAR volname[_MAX_FNAME];
95 } Root;
96 
98  COL_SIZE = 0x01,
99  COL_DATE = 0x02,
100  COL_TIME = 0x04,
102  COL_DOSNAMES = 0x10,
103  COL_INDEX = 0x20,
104  COL_LINKS = 0x40,
106 };
107 
108 typedef enum {
113 } SORT_ORDER;
114 
115 typedef struct {
118 
119 #define COLUMNS 10
120  int widths[COLUMNS];
121  int positions[COLUMNS+1];
122 
127 } Pane;
128 
129 typedef struct {
133  int focus_pane; /* 0: left 1: right */
137 
139  WCHAR filter_pattern[MAX_PATH];
142 
144 } ChildWnd;
145 
146 
147 
148 static void read_directory(Entry* dir, LPCWSTR path, SORT_ORDER sortOrder, HWND hwnd);
149 static void set_curdir(ChildWnd* child, Entry* entry, int idx, HWND hwnd);
150 static void refresh_child(ChildWnd* child);
151 static void refresh_drives(void);
152 static void get_path(Entry* dir, PWSTR path);
153 static void format_date(const FILETIME* ft, WCHAR* buffer, int visible_cols);
154 
158 
159 
160 /* globals */
162 
163 static int last_split;
164 
165 /* some common string constants */
166 static const WCHAR sEmpty[] = {'\0'};
167 static const WCHAR sSpace[] = {' ', '\0'};
168 static const WCHAR sNumFmt[] = {'%','d','\0'};
169 static const WCHAR sQMarks[] = {'?','?','?','\0'};
170 
171 /* window class names */
172 static const WCHAR sWINEFILEFRAME[] = {'W','F','S','_','F','r','a','m','e','\0'};
173 static const WCHAR sWINEFILETREE[] = {'W','F','S','_','T','r','e','e','\0'};
174 
176 {
177  WCHAR buffer[65], *p = &buffer[64];
178 
179  *p = 0;
180  do {
181  *(--p) = '0' + val % 10;
182  val /= 10;
183  } while (val);
184  lstrcpyW( ret, p );
185 }
186 
187 
188 /* load resource string */
190 {
192  return buffer;
193 }
194 
195 #define RS(b, i) load_string(b, sizeof(b)/sizeof(b[0]), i)
196 
197 
198 /* display error message for the specified WIN32 error code */
200 {
202  PWSTR msg;
203 
207  else
209 
210  LocalFree(msg);
211 }
212 
213 
214 /* display network error message using WNetGetLastErrorW() */
216 {
217  WCHAR msg[BUFFER_LEN], provider[BUFFER_LEN], b2[BUFFER_LEN];
218  DWORD error;
219 
222 }
223 
224 static inline BOOL get_check(HWND hwnd, INT id)
225 {
226  return BST_CHECKED&SendMessageW(GetDlgItem(hwnd, id), BM_GETSTATE, 0, 0);
227 }
228 
229 static inline INT set_check(HWND hwnd, INT id, BOOL on)
230 {
232 }
233 
234 static inline void choose_font(HWND hwnd)
235 {
236  WCHAR dlg_name[BUFFER_LEN], dlg_info[BUFFER_LEN];
237  CHOOSEFONTW chFont;
238  LOGFONTW lFont;
239 
240  HDC hdc = GetDC(hwnd);
241 
242  GetObjectW(Globals.hfont, sizeof(LOGFONTW), &lFont);
243 
244  chFont.lStructSize = sizeof(CHOOSEFONTW);
245  chFont.hwndOwner = hwnd;
246  chFont.hDC = NULL;
247  chFont.lpLogFont = &lFont;
249  chFont.rgbColors = RGB(0,0,0);
250  chFont.lCustData = 0;
251  chFont.lpfnHook = NULL;
252  chFont.lpTemplateName = NULL;
253  chFont.hInstance = Globals.hInstance;
254  chFont.lpszStyle = NULL;
255  chFont.nFontType = SIMULATED_FONTTYPE;
256  chFont.nSizeMin = 0;
257  chFont.nSizeMax = 24;
258 
259  if (ChooseFontW(&chFont)) {
260  HWND childWnd;
261  HFONT hFontOld;
262 
265  hFontOld = SelectObject(hdc, Globals.hfont);
267 
268  /* change font in all open child windows */
269  for(childWnd=GetWindow(Globals.hmdiclient,GW_CHILD); childWnd; childWnd=GetNextWindow(childWnd,GW_HWNDNEXT)) {
275  InvalidateRect(child->left.hwnd, NULL, TRUE);
276  InvalidateRect(child->right.hwnd, NULL, TRUE);
277  }
278 
279  SelectObject(hdc, hFontOld);
280  }
281  else if (CommDlgExtendedError()) {
284  MessageBoxW(hwnd, dlg_info, dlg_name, MB_OK);
285  }
286 
287  ReleaseDC(hwnd, hdc);
288 }
289 
290 
291 /* allocate and initialise a directory entry */
292 static Entry* alloc_entry(void)
293 {
294  Entry* entry = HeapAlloc(GetProcessHeap(), 0, sizeof(Entry));
295 
296  entry->pidl = NULL;
297  entry->folder = NULL;
298  entry->hicon = 0;
299 
300  return entry;
301 }
302 
303 /* free a directory entry */
304 static void free_entry(Entry* entry)
305 {
306  if (entry->hicon && entry->hicon!=(HICON)-1)
307  DestroyIcon(entry->hicon);
308 
309  if (entry->folder && entry->folder!=Globals.iDesktop)
310  IShellFolder_Release(entry->folder);
311 
312  if (entry->pidl)
313  IMalloc_Free(Globals.iMalloc, entry->pidl);
314 
316 }
317 
318 /* recursively free all child entries */
319 static void free_entries(Entry* dir)
320 {
321  Entry *entry, *next=dir->down;
322 
323  if (next) {
324  dir->down = 0;
325 
326  do {
327  entry = next;
328  next = entry->next;
329 
331  free_entry(entry);
332  } while(next);
333  }
334 }
335 
336 
338 {
339  Entry* first_entry = NULL;
340  Entry* last = NULL;
341  Entry* entry;
342 
343  int level = dir->level + 1;
344  WIN32_FIND_DATAW w32fd;
345  HANDLE hFind;
346  HANDLE hFile;
347 
348  WCHAR buffer[MAX_PATH], *p;
349  for(p=buffer; *path; )
350  *p++ = *path++;
351 
352  *p++ = '\\';
353  p[0] = '*';
354  p[1] = '\0';
355 
356  hFind = FindFirstFileW(buffer, &w32fd);
357 
358  if (hFind != INVALID_HANDLE_VALUE) {
359  do {
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_DATAW));
369  entry->down = NULL;
370  entry->up = dir;
371  entry->expanded = FALSE;
372  entry->scanned = FALSE;
373  entry->level = level;
374  entry->etype = ET_WINDOWS;
375  entry->bhfi_valid = FALSE;
376 
377  lstrcpyW(p, entry->data.cFileName);
378 
381 
382  if (hFile != INVALID_HANDLE_VALUE) {
384  entry->bhfi_valid = TRUE;
385 
387  }
388 
389  last = entry;
390  } while(FindNextFileW(hFind, &w32fd));
391 
392  if (last)
393  last->next = NULL;
394 
395  FindClose(hFind);
396  }
397 
398  dir->down = first_entry;
399  dir->scanned = TRUE;
400 }
401 
402 
404 {
405  Entry* entry;
406 
407  for(entry=dir->down; entry; entry=entry->next) {
408  LPCWSTR p = name;
409  LPCWSTR q = entry->data.cFileName;
410 
411  do {
412  if (!*p || *p == '\\' || *p == '/')
413  return entry;
414  } while(tolower(*p++) == tolower(*q++));
415 
416  p = name;
417  q = entry->data.cAlternateFileName;
418 
419  do {
420  if (!*p || *p == '\\' || *p == '/')
421  return entry;
422  } while(tolower(*p++) == tolower(*q++));
423  }
424 
425  return 0;
426 }
427 
428 
430 {
432  Entry* entry = &root->entry;
433  LPCWSTR s = path;
434  PWSTR d = buffer;
435 
436  HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
437 
438  entry->etype = ET_WINDOWS;
439  while(entry) {
440  while(*s && *s != '\\' && *s != '/')
441  *d++ = *s++;
442 
443  while(*s == '\\' || *s == '/')
444  s++;
445 
446  *d++ = '\\';
447  *d = '\0';
448 
449  read_directory(entry, buffer, sortOrder, hwnd);
450 
451  if (entry->down)
452  entry->expanded = TRUE;
453 
454  if (!*s)
455  break;
456 
458  }
459 
460  SetCursor(old_cursor);
461 
462  return entry;
463 }
464 
465 
466 #ifdef __WINE__
467 
469 {
470  struct tm* tm = gmtime(&t);
471  SYSTEMTIME stime;
472 
473  if (!tm)
474  return FALSE;
475 
476  stime.wYear = tm->tm_year+1900;
477  stime.wMonth = tm->tm_mon+1;
478  /* stime.wDayOfWeek */
479  stime.wDay = tm->tm_mday;
480  stime.wHour = tm->tm_hour;
481  stime.wMinute = tm->tm_min;
482  stime.wSecond = tm->tm_sec;
483  stime.wMilliseconds = 0;
484 
485  return SystemTimeToFileTime(&stime, ftime);
486 }
487 
488 static void read_directory_unix(Entry* dir, LPCWSTR path)
489 {
490  Entry* first_entry = NULL;
491  Entry* last = NULL;
492  Entry* entry;
493  DIR* pdir;
494 
495  int level = dir->level + 1;
496  char cpath[MAX_PATH];
497 
498  WideCharToMultiByte(CP_UNIXCP, 0, path, -1, cpath, MAX_PATH, NULL, NULL);
499  pdir = opendir(cpath);
500 
501  if (pdir) {
502  struct stat st;
503  struct dirent* ent;
504  char buffer[MAX_PATH], *p;
505  const char* s;
506 
507  for(p=buffer,s=cpath; *s; )
508  *p++ = *s++;
509 
510  if (p==buffer || p[-1]!='/')
511  *p++ = '/';
512 
513  while((ent=readdir(pdir))) {
514  entry = alloc_entry();
515 
516  if (!first_entry)
517  first_entry = entry;
518 
519  if (last)
520  last->next = entry;
521 
522  entry->etype = ET_UNIX;
523 
524  strcpy(p, ent->d_name);
525  MultiByteToWideChar(CP_UNIXCP, 0, p, -1, entry->data.cFileName, MAX_PATH);
526 
527  if (!stat(buffer, &st)) {
528  entry->data.dwFileAttributes = p[0]=='.'? FILE_ATTRIBUTE_HIDDEN: 0;
529 
530  if (S_ISDIR(st.st_mode))
531  entry->data.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
532 
533  entry->data.nFileSizeLow = st.st_size & 0xFFFFFFFF;
534  entry->data.nFileSizeHigh = st.st_size >> 32;
535 
536  memset(&entry->data.ftCreationTime, 0, sizeof(FILETIME));
537  time_to_filetime(st.st_atime, &entry->data.ftLastAccessTime);
538  time_to_filetime(st.st_mtime, &entry->data.ftLastWriteTime);
539 
540  entry->bhfi.nFileIndexLow = ent->d_ino;
541  entry->bhfi.nFileIndexHigh = 0;
542 
543  entry->bhfi.nNumberOfLinks = st.st_nlink;
544 
545  entry->bhfi_valid = TRUE;
546  } else {
547  entry->data.nFileSizeLow = 0;
548  entry->data.nFileSizeHigh = 0;
549  entry->bhfi_valid = FALSE;
550  }
551 
552  entry->down = NULL;
553  entry->up = dir;
554  entry->expanded = FALSE;
555  entry->scanned = FALSE;
556  entry->level = level;
557 
558  last = entry;
559  }
560 
561  if (last)
562  last->next = NULL;
563 
564  closedir(pdir);
565  }
566 
567  dir->down = first_entry;
568  dir->scanned = TRUE;
569 }
570 
571 static Entry* find_entry_unix(Entry* dir, LPCWSTR name)
572 {
573  Entry* entry;
574 
575  for(entry=dir->down; entry; entry=entry->next) {
576  LPCWSTR p = name;
577  LPCWSTR q = entry->data.cFileName;
578 
579  do {
580  if (!*p || *p == '/')
581  return entry;
582  } while(*p++ == *q++);
583  }
584 
585  return 0;
586 }
587 
588 static Entry* read_tree_unix(Root* root, LPCWSTR path, SORT_ORDER sortOrder, HWND hwnd)
589 {
591  Entry* entry = &root->entry;
592  LPCWSTR s = path;
593  PWSTR d = buffer;
594 
595  HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
596 
597  entry->etype = ET_UNIX;
598 
599  while(entry) {
600  while(*s && *s != '/')
601  *d++ = *s++;
602 
603  while(*s == '/')
604  s++;
605 
606  *d++ = '/';
607  *d = '\0';
608 
609  read_directory(entry, buffer, sortOrder, hwnd);
610 
611  if (entry->down)
612  entry->expanded = TRUE;
613 
614  if (!*s)
615  break;
616 
617  entry = find_entry_unix(entry, s);
618  }
619 
620  SetCursor(old_cursor);
621 
622  return entry;
623 }
624 
625 #endif /* __WINE__ */
626 
627 static void free_strret(STRRET* str)
628 {
629  if (str->uType == STRRET_WSTR)
630  IMalloc_Free(Globals.iMalloc, str->UNION_MEMBER(pOleStr));
631 }
632 
634 {
635  LPCWSTR s;
636  LPWSTR d = dest;
637 
638  for(s=source; count&&(*d++=*s++); )
639  count--;
640 
641  return dest;
642 }
643 
644 static void get_strretW(STRRET* str, const SHITEMID* shiid, LPWSTR buffer, int len)
645 {
646  switch(str->uType) {
647  case STRRET_WSTR:
648  wcscpyn(buffer, str->UNION_MEMBER(pOleStr), len);
649  break;
650 
651  case STRRET_OFFSET:
652  MultiByteToWideChar(CP_ACP, 0, (LPCSTR)shiid+str->UNION_MEMBER(uOffset), -1, buffer, len);
653  break;
654 
655  case STRRET_CSTR:
656  MultiByteToWideChar(CP_ACP, 0, str->UNION_MEMBER(cStr), -1, buffer, len);
657  }
658 }
659 
660 
662 {
663  STRRET str;
664 
665  HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, flags, &str);
666 
667  if (SUCCEEDED(hr)) {
668  get_strretW(&str, &pidl->mkid, buffer, len);
669  free_strret(&str);
670  } else
671  buffer[0] = '\0';
672 
673  return hr;
674 }
675 
676 
678 {
679  STRRET str;
680 
681  /* SHGDN_FORPARSING: get full path of id list */
682  HRESULT hr = IShellFolder_GetDisplayNameOf(folder, pidl, SHGDN_FORPARSING, &str);
683 
684  if (SUCCEEDED(hr)) {
685  get_strretW(&str, &pidl->mkid, buffer, len);
686  free_strret(&str);
687  } else
688  buffer[0] = '\0';
689 
690  return hr;
691 }
692 
693 
694  /* create an item id list from a file system path */
695 
697 {
698  LPITEMIDLIST pidl;
699  HRESULT hr;
700  ULONG len;
701  LPWSTR buffer = path;
702 
703  hr = IShellFolder_ParseDisplayName(Globals.iDesktop, hwnd, NULL, buffer, &len, &pidl, NULL);
704  if (FAILED(hr))
705  return NULL;
706 
707  return pidl;
708 }
709 
710 
711  /* convert an item id list from relative to absolute (=relative to the desktop) format */
712 
714 {
715  if (entry->up && entry->up->etype==ET_SHELL) {
716  LPITEMIDLIST idl = NULL;
717 
718  while (entry->up) {
719  idl = ILCombine(ILClone(entry->pidl), idl);
720  entry = entry->up;
721  }
722 
723  return idl;
724  } else if (entry->etype == ET_WINDOWS) {
726 
727  get_path(entry, path);
728 
729  return get_path_pidl(path, hwnd);
730  } else if (entry->pidl)
731  return ILClone(entry->pidl);
732 
733  return NULL;
734 }
735 
736 
738 {
739  IExtractIconW* pExtract;
740 
741  if (SUCCEEDED(IShellFolder_GetUIObjectOf(folder, 0, 1, (LPCITEMIDLIST*)&pidl, &IID_IExtractIconW, 0, (LPVOID*)&pExtract))) {
743  unsigned flags;
744  HICON hicon;
745  int idx;
746 
747  if (SUCCEEDED(IExtractIconW_GetIconLocation(pExtract, GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
748  if (!(flags & GIL_NOTFILENAME)) {
749  if (idx == -1)
750  idx = 0; /* special case for some control panel applications */
751 
752  if ((int)ExtractIconExW(path, idx, 0, &hicon, 1) > 0)
753  flags &= ~GIL_DONTCACHE;
754  } else {
755  HICON hIconLarge = 0;
756 
757  HRESULT hr = IExtractIconW_Extract(pExtract, path, idx, &hIconLarge, &hicon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
758 
759  if (SUCCEEDED(hr))
760  DestroyIcon(hIconLarge);
761  }
762 
763  return hicon;
764  }
765  }
766 
767  return 0;
768 }
769 
770 
772 {
773  Entry* entry;
774 
775  for(entry=dir->down; entry; entry=entry->next) {
776  if (entry->pidl->mkid.cb == pidl->mkid.cb &&
777  !memcmp(entry->pidl, pidl, entry->pidl->mkid.cb))
778  return entry;
779  }
780 
781  return 0;
782 }
783 
785 {
786  Entry* entry = &root->entry;
787  Entry* next;
788  LPITEMIDLIST next_pidl = pidl;
791  HRESULT hr;
792 
793  HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
794 
795  entry->etype = ET_SHELL;
797 
798  while(entry) {
799  entry->pidl = next_pidl;
800  entry->folder = folder;
801 
802  if (!pidl->mkid.cb)
803  break;
804 
805  /* copy first element of item idlist */
806  next_pidl = IMalloc_Alloc(Globals.iMalloc, pidl->mkid.cb+sizeof(USHORT));
807  memcpy(next_pidl, pidl, pidl->mkid.cb);
808  ((LPITEMIDLIST)((LPBYTE)next_pidl+pidl->mkid.cb))->mkid.cb = 0;
809 
810  hr = IShellFolder_BindToObject(folder, next_pidl, 0, &IID_IShellFolder, (void**)&child);
811  if (FAILED(hr))
812  break;
813 
814  read_directory(entry, NULL, sortOrder, hwnd);
815 
816  if (entry->down)
817  entry->expanded = TRUE;
818 
819  next = find_entry_shell(entry, next_pidl);
820  if (!next)
821  break;
822 
823  folder = child;
824  entry = next;
825 
826  /* go to next element */
827  pidl = (LPITEMIDLIST) ((LPBYTE)pidl+pidl->mkid.cb);
828  }
829 
830  SetCursor(old_cursor);
831 
832  return entry;
833 }
834 
835 
837 {
838  if (!(attribs & SFGAO_FILESYSTEM) ||
841  IDataObject* pDataObj;
842 
843  STGMEDIUM medium = {0, {0}, 0};
844  FORMATETC fmt = {Globals.cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
845 
846  HRESULT hr = IShellFolder_GetUIObjectOf(folder, 0, 1, &pidl, &IID_IDataObject, 0, (LPVOID*)&pDataObj);
847 
848  if (SUCCEEDED(hr)) {
849  hr = IDataObject_GetData(pDataObj, &fmt, &medium);
850 
851  IDataObject_Release(pDataObj);
852 
853  if (SUCCEEDED(hr)) {
854  LPCWSTR path = GlobalLock(medium.UNION_MEMBER(hGlobal));
856 
858  w32fdata->dwFileAttributes = fad.dwFileAttributes;
859  w32fdata->ftCreationTime = fad.ftCreationTime;
860  w32fdata->ftLastAccessTime = fad.ftLastAccessTime;
861  w32fdata->ftLastWriteTime = fad.ftLastWriteTime;
862 
864  w32fdata->nFileSizeLow = fad.nFileSizeLow;
865  w32fdata->nFileSizeHigh = fad.nFileSizeHigh;
866  }
867  }
868 
869  SetErrorMode(sem_org);
870 
871  GlobalUnlock(medium.UNION_MEMBER(hGlobal));
872  GlobalFree(medium.UNION_MEMBER(hGlobal));
873  }
874  }
875  }
876 
877  if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
878  w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
879 
880  if (attribs & SFGAO_READONLY)
881  w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
882 
883  if (attribs & SFGAO_COMPRESSED)
884  w32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
885 }
886 
887 
889 {
890  IShellFolder* folder = dir->folder;
891  int level = dir->level + 1;
892  HRESULT hr;
893 
895  IEnumIDList* idlist;
896 
897  Entry* first_entry = NULL;
898  Entry* last = NULL;
899  Entry* entry;
900 
901  if (!folder)
902  return;
903 
904  hr = IShellFolder_EnumObjects(folder, hwnd, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE, &idlist);
905 
906  if (SUCCEEDED(hr)) {
907  for(;;) {
908 #define FETCH_ITEM_COUNT 32
910  SFGAOF attribs;
911  ULONG cnt = 0;
912  ULONG n;
913 
914  memset(pidls, 0, sizeof(pidls));
915 
916  hr = IEnumIDList_Next(idlist, FETCH_ITEM_COUNT, pidls, &cnt);
917  if (FAILED(hr))
918  break;
919 
920  if (hr == S_FALSE)
921  break;
922 
923  for(n=0; n<cnt; ++n) {
924  entry = alloc_entry();
925 
926  if (!first_entry)
927  first_entry = entry;
928 
929  if (last)
930  last->next = entry;
931 
932  memset(&entry->data, 0, sizeof(WIN32_FIND_DATAW));
933  entry->bhfi_valid = FALSE;
934 
935  attribs = ~SFGAO_FILESYSTEM; /*SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird */
936 
937  hr = IShellFolder_GetAttributesOf(folder, 1, (LPCITEMIDLIST*)&pidls[n], &attribs);
938 
939  if (SUCCEEDED(hr)) {
940  if (attribs != (SFGAOF)~SFGAO_FILESYSTEM) {
941  fill_w32fdata_shell(folder, pidls[n], attribs, &entry->data);
942 
943  entry->bhfi_valid = TRUE;
944  } else
945  attribs = 0;
946  } else
947  attribs = 0;
948 
949  entry->pidl = pidls[n];
950 
951  if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
952  hr = IShellFolder_BindToObject(folder, pidls[n], 0, &IID_IShellFolder, (void**)&child);
953 
954  if (SUCCEEDED(hr))
955  entry->folder = child;
956  else
957  entry->folder = NULL;
958  }
959  else
960  entry->folder = NULL;
961 
962  if (!entry->data.cFileName[0])
963  /*hr = */name_from_pidl(folder, pidls[n], entry->data.cFileName, MAX_PATH, /*SHGDN_INFOLDER*/0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
964 
965  /* get display icons for files and virtual objects */
966  if (!(entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
967  !(attribs & SFGAO_FILESYSTEM)) {
968  entry->hicon = extract_icon(folder, pidls[n]);
969 
970  if (!entry->hicon)
971  entry->hicon = (HICON)-1; /* don't try again later */
972  }
973 
974  entry->down = NULL;
975  entry->up = dir;
976  entry->expanded = FALSE;
977  entry->scanned = FALSE;
978  entry->level = level;
979 
980  entry->etype = ET_SHELL;
981  entry->bhfi_valid = FALSE;
982 
983  last = entry;
984  }
985  }
986 
987  IEnumIDList_Release(idlist);
988  }
989 
990  if (last)
991  last->next = NULL;
992 
993  dir->down = first_entry;
994  dir->scanned = TRUE;
995 }
996 
997 /* sort order for different directory/file types */
999  TO_DIR = 0,
1000  TO_DOT = 1,
1004 };
1005 
1006 /* distinguish between ".", ".." and any other directory names */
1008 {
1009  if (name[0] == '.') {
1010  if (name[1] == '\0')
1011  return TO_DOT; /* "." */
1012 
1013  if (name[1]=='.' && name[2]=='\0')
1014  return TO_DOTDOT; /* ".." */
1015  }
1016 
1017  return TO_OTHER_DIR; /* anything else */
1018 }
1019 
1020 /* directories first... */
1021 static int compareType(const WIN32_FIND_DATAW* fd1, const WIN32_FIND_DATAW* fd2)
1022 {
1023  int order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1024  int order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE;
1025 
1026  /* Handle "." and ".." as special case and move them at the very first beginning. */
1027  if (order1==TO_DIR && order2==TO_DIR) {
1028  order1 = TypeOrderFromDirname(fd1->cFileName);
1029  order2 = TypeOrderFromDirname(fd2->cFileName);
1030  }
1031 
1032  return order2==order1? 0: order1<order2? -1: 1;
1033 }
1034 
1035 
1036 static int compareName(const void* arg1, const void* arg2)
1037 {
1038  const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1039  const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1040 
1041  int cmp = compareType(fd1, fd2);
1042  if (cmp)
1043  return cmp;
1044 
1045  return lstrcmpiW(fd1->cFileName, fd2->cFileName);
1046 }
1047 
1048 static int compareExt(const void* arg1, const void* arg2)
1049 {
1050  const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1051  const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1052  const WCHAR *name1, *name2, *ext1, *ext2;
1053 
1054  int cmp = compareType(fd1, fd2);
1055  if (cmp)
1056  return cmp;
1057 
1058  name1 = fd1->cFileName;
1059  name2 = fd2->cFileName;
1060 
1061  ext1 = strrchrW(name1, '.');
1062  ext2 = strrchrW(name2, '.');
1063 
1064  if (ext1)
1065  ext1++;
1066  else
1067  ext1 = sEmpty;
1068 
1069  if (ext2)
1070  ext2++;
1071  else
1072  ext2 = sEmpty;
1073 
1074  cmp = lstrcmpiW(ext1, ext2);
1075  if (cmp)
1076  return cmp;
1077 
1078  return lstrcmpiW(name1, name2);
1079 }
1080 
1081 static int compareSize(const void* arg1, const void* arg2)
1082 {
1083  const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1084  const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1085 
1086  int cmp = compareType(fd1, fd2);
1087  if (cmp)
1088  return cmp;
1089 
1090  cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh;
1091 
1092  if (cmp < 0)
1093  return -1;
1094  else if (cmp > 0)
1095  return 1;
1096 
1097  cmp = fd2->nFileSizeLow - fd1->nFileSizeLow;
1098 
1099  return cmp<0? -1: cmp>0? 1: 0;
1100 }
1101 
1102 static int compareDate(const void* arg1, const void* arg2)
1103 {
1104  const WIN32_FIND_DATAW* fd1 = &(*(const Entry* const*)arg1)->data;
1105  const WIN32_FIND_DATAW* fd2 = &(*(const Entry* const*)arg2)->data;
1106 
1107  int cmp = compareType(fd1, fd2);
1108  if (cmp)
1109  return cmp;
1110 
1111  return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime);
1112 }
1113 
1114 
1115 static int (*sortFunctions[])(const void* arg1, const void* arg2) = {
1116  compareName, /* SORT_NAME */
1117  compareExt, /* SORT_EXT */
1118  compareSize, /* SORT_SIZE */
1119  compareDate /* SORT_DATE */
1120 };
1121 
1122 
1123 static void SortDirectory(Entry* dir, SORT_ORDER sortOrder)
1124 {
1125  Entry* entry;
1126  Entry** array, **p;
1127  int len;
1128 
1129  len = 0;
1130  for(entry=dir->down; entry; entry=entry->next)
1131  len++;
1132 
1133  if (len) {
1134  array = HeapAlloc(GetProcessHeap(), 0, len*sizeof(Entry*));
1135 
1136  p = array;
1137  for(entry=dir->down; entry; entry=entry->next)
1138  *p++ = entry;
1139 
1140  /* call qsort with the appropriate compare function */
1141  qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]);
1142 
1143  dir->down = array[0];
1144 
1145  for(p=array; --len; p++)
1146  p[0]->next = p[1];
1147 
1148  (*p)->next = 0;
1149 
1150  HeapFree(GetProcessHeap(), 0, array);
1151  }
1152 }
1153 
1154 
1156 {
1158  Entry* entry;
1159  LPCWSTR s;
1160  PWSTR d;
1161 
1162  if (dir->etype == ET_SHELL)
1163  {
1165 
1166  if (Globals.prescan_node) {
1167  s = path;
1168  d = buffer;
1169 
1170  while(*s)
1171  *d++ = *s++;
1172 
1173  *d++ = '\\';
1174 
1175  for(entry=dir->down; entry; entry=entry->next)
1176  if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1178  SortDirectory(entry, sortOrder);
1179  }
1180  }
1181  }
1182  else
1183 #ifdef __WINE__
1184  if (dir->etype == ET_UNIX)
1185  {
1186  read_directory_unix(dir, path);
1187 
1188  if (Globals.prescan_node) {
1189  s = path;
1190  d = buffer;
1191 
1192  while(*s)
1193  *d++ = *s++;
1194 
1195  *d++ = '/';
1196 
1197  for(entry=dir->down; entry; entry=entry->next)
1198  if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1199  lstrcpyW(d, entry->data.cFileName);
1200  read_directory_unix(entry, buffer);
1201  SortDirectory(entry, sortOrder);
1202  }
1203  }
1204  }
1205  else
1206 #endif
1207  {
1209 
1210  if (Globals.prescan_node) {
1211  s = path;
1212  d = buffer;
1213 
1214  while(*s)
1215  *d++ = *s++;
1216 
1217  *d++ = '\\';
1218 
1219  for(entry=dir->down; entry; entry=entry->next)
1220  if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1221  lstrcpyW(d, entry->data.cFileName);
1223  SortDirectory(entry, sortOrder);
1224  }
1225  }
1226  }
1227 
1228  SortDirectory(dir, sortOrder);
1229 }
1230 
1231 
1233 {
1234 #ifdef __WINE__
1235  static const WCHAR sSlash[] = {'/', '\0'};
1236 #endif
1237  static const WCHAR sBackslash[] = {'\\', '\0'};
1238 
1239  if (pidl)
1240  {
1241  /* read shell namespace tree */
1242  root->drive_type = DRIVE_UNKNOWN;
1243  drv[0] = '\\';
1244  drv[1] = '\0';
1245  load_string(root->volname, sizeof(root->volname)/sizeof(root->volname[0]), IDS_DESKTOP);
1246  root->fs_flags = 0;
1247  load_string(root->fs, sizeof(root->fs)/sizeof(root->fs[0]), IDS_SHELL);
1248 
1249  return read_tree_shell(root, pidl, sortOrder, hwnd);
1250  }
1251  else
1252 #ifdef __WINE__
1253  if (*path == '/')
1254  {
1255  /* read unix file system tree */
1256  root->drive_type = GetDriveTypeW(path);
1257 
1258  lstrcatW(drv, sSlash);
1259  load_string(root->volname, sizeof(root->volname)/sizeof(root->volname[0]), IDS_ROOT_FS);
1260  root->fs_flags = 0;
1261  load_string(root->fs, sizeof(root->fs)/sizeof(root->fs[0]), IDS_UNIXFS);
1262 
1263  lstrcpyW(root->path, sSlash);
1264 
1265  return read_tree_unix(root, path, sortOrder, hwnd);
1266  }
1267 #endif
1268 
1269  /* read WIN32 file system tree */
1270  root->drive_type = GetDriveTypeW(path);
1271 
1272  lstrcatW(drv, sBackslash);
1273  GetVolumeInformationW(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR);
1274 
1275  lstrcpyW(root->path, drv);
1276 
1277  return read_tree_win(root, path, sortOrder, hwnd);
1278 }
1279 
1280 
1281 /* flags to filter different file types */
1284  TF_PROGRAMS = 0x02,
1286  TF_OTHERS = 0x08,
1287  TF_HIDDEN = 0x10,
1288  TF_ALL = 0x1F
1289 };
1290 
1291 
1293 {
1295  WCHAR dir_path[MAX_PATH];
1296  static const WCHAR sAsterics[] = {'*', '\0'};
1297  static const WCHAR sTitleFmt[] = {'%','s',' ','-',' ','%','s','\0'};
1298 
1299  ChildWnd* child = HeapAlloc(GetProcessHeap(), 0, sizeof(ChildWnd));
1300  Root* root = &child->root;
1301  Entry* entry;
1302 
1303  memset(child, 0, sizeof(ChildWnd));
1304 
1305  child->left.treePane = TRUE;
1306  child->left.visible_cols = 0;
1307 
1308  child->right.treePane = FALSE;
1310 
1311  child->pos.length = sizeof(WINDOWPLACEMENT);
1312  child->pos.flags = 0;
1313  child->pos.showCmd = SW_SHOWNORMAL;
1314  child->pos.rcNormalPosition.left = CW_USEDEFAULT;
1315  child->pos.rcNormalPosition.top = CW_USEDEFAULT;
1316  child->pos.rcNormalPosition.right = CW_USEDEFAULT;
1317  child->pos.rcNormalPosition.bottom = CW_USEDEFAULT;
1318 
1319  child->focus_pane = 0;
1320  child->split_pos = DEFAULT_SPLIT_POS;
1321  child->sortOrder = SORT_NAME;
1322  child->header_wdths_ok = FALSE;
1323 
1324  if (path)
1325  {
1326  int pathlen = strlenW(path);
1327  const WCHAR *npath = path;
1328 
1329  if (path[0] == '"' && path[pathlen - 1] == '"')
1330  {
1331  npath++;
1332  pathlen--;
1333  }
1334  lstrcpynW(child->path, npath, pathlen + 1);
1335 
1336  _wsplitpath(child->path, drv, dir, name, ext);
1337  }
1338 
1339  lstrcpyW(child->filter_pattern, sAsterics);
1340  child->filter_flags = TF_ALL;
1341 
1342  root->entry.level = 0;
1343 
1344  lstrcpyW(dir_path, drv);
1345  lstrcatW(dir_path, dir);
1346  entry = read_tree(root, dir_path, pidl, drv, child->sortOrder, hwnd);
1347 
1348  if (root->entry.etype == ET_SHELL)
1349  load_string(root->entry.data.cFileName, sizeof(root->entry.data.cFileName)/sizeof(root->entry.data.cFileName[0]), IDS_DESKTOP);
1350  else
1351  wsprintfW(root->entry.data.cFileName, sTitleFmt, drv, root->fs);
1352 
1353  root->entry.data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1354 
1355  child->left.root = &root->entry;
1356  child->right.root = NULL;
1357 
1358  set_curdir(child, entry, 0, hwnd);
1359 
1360  return child;
1361 }
1362 
1363 
1364 /* free all memory associated with a child window */
1366 {
1367  free_entries(&child->root.entry);
1368  HeapFree(GetProcessHeap(), 0, child);
1369 }
1370 
1371 
1372 /* get full path of specified directory entry */
1373 static void get_path(Entry* dir, PWSTR path)
1374 {
1375  Entry* entry;
1376  int len = 0;
1377  int level = 0;
1378 
1379  if (dir->etype == ET_SHELL)
1380  {
1381  SFGAOF attribs;
1382  HRESULT hr = S_OK;
1383 
1384  path[0] = '\0';
1385 
1386  attribs = 0;
1387 
1388  if (dir->folder)
1389  hr = IShellFolder_GetAttributesOf(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs);
1390 
1391  if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) {
1392  IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop;
1393 
1394  hr = path_from_pidlW(parent, dir->pidl, path, MAX_PATH);
1395  }
1396  }
1397  else
1398  {
1399  for(entry=dir; entry; level++) {
1400  LPCWSTR name;
1401  int l;
1402 
1403  {
1404  LPCWSTR s;
1405  name = entry->data.cFileName;
1406  s = name;
1407 
1408  for(l=0; *s && *s != '/' && *s != '\\'; s++)
1409  l++;
1410  }
1411 
1412  if (entry->up) {
1413  if (l > 0) {
1414  memmove(path+l+1, path, len*sizeof(WCHAR));
1415  memcpy(path+1, name, l*sizeof(WCHAR));
1416  len += l+1;
1417 
1418  if (entry->etype == ET_UNIX)
1419  path[0] = '/';
1420  else
1421  path[0] = '\\';
1422  }
1423 
1424  entry = entry->up;
1425  } else {
1426  memmove(path+l, path, len*sizeof(WCHAR));
1427  memcpy(path, name, l*sizeof(WCHAR));
1428  len += l;
1429  break;
1430  }
1431  }
1432 
1433  if (!level) {
1434  if (entry->etype == ET_UNIX)
1435  path[len++] = '/';
1436  else
1437  path[len++] = '\\';
1438  }
1439 
1440  path[len] = '\0';
1441  }
1442 }
1443 
1445 {
1446  DWORD size;
1447  DWORD type;
1448  HKEY hKey;
1449  windowOptions opts;
1450  LOGFONTW logfont;
1451 
1453  0, KEY_QUERY_VALUE, &hKey );
1454 
1455  size = sizeof(DWORD);
1456 
1457  if( RegQueryValueExW( hKey, reg_start_x, NULL, &type,
1458  (LPBYTE) &opts.start_x, &size ) != ERROR_SUCCESS )
1459  opts.start_x = CW_USEDEFAULT;
1460 
1461  if( RegQueryValueExW( hKey, reg_start_y, NULL, &type,
1462  (LPBYTE) &opts.start_y, &size ) != ERROR_SUCCESS )
1463  opts.start_y = CW_USEDEFAULT;
1464 
1465  if( RegQueryValueExW( hKey, reg_width, NULL, &type,
1466  (LPBYTE) &opts.width, &size ) != ERROR_SUCCESS )
1467  opts.width = CW_USEDEFAULT;
1468 
1469  if( RegQueryValueExW( hKey, reg_height, NULL, &type,
1470  (LPBYTE) &opts.height, &size ) != ERROR_SUCCESS )
1471  opts.height = CW_USEDEFAULT;
1472  size=sizeof(logfont);
1473  if( RegQueryValueExW( hKey, reg_logfont, NULL, &type,
1474  (LPBYTE) &logfont, &size ) != ERROR_SUCCESS )
1475  GetObjectW(GetStockObject(DEFAULT_GUI_FONT),sizeof(logfont),&logfont);
1476 
1477  RegCloseKey( hKey );
1478 
1479  Globals.hfont = CreateFontIndirectW(&logfont);
1480  return opts;
1481 }
1482 
1483 static void save_registry_settings(void)
1484 {
1485  WINDOWINFO wi;
1486  HKEY hKey;
1487  INT width, height;
1488  LOGFONTW logfont;
1489 
1490  wi.cbSize = sizeof( WINDOWINFO );
1492  width = wi.rcWindow.right - wi.rcWindow.left;
1493  height = wi.rcWindow.bottom - wi.rcWindow.top;
1494 
1496  0, KEY_SET_VALUE, &hKey ) != ERROR_SUCCESS )
1497  {
1498  /* Unable to save registry settings - try to create key */
1501  KEY_SET_VALUE, NULL, &hKey, NULL ) != ERROR_SUCCESS )
1502  {
1503  /* FIXME: Cannot create key */
1504  return;
1505  }
1506  }
1507  /* Save all of the settings */
1509  (LPBYTE) &wi.rcWindow.left, sizeof(DWORD) );
1511  (LPBYTE) &wi.rcWindow.top, sizeof(DWORD) );
1512  RegSetValueExW( hKey, reg_width, 0, REG_DWORD,
1513  (LPBYTE) &width, sizeof(DWORD) );
1514  RegSetValueExW( hKey, reg_height, 0, REG_DWORD,
1515  (LPBYTE) &height, sizeof(DWORD) );
1516  GetObjectW(Globals.hfont, sizeof(logfont), &logfont);
1518  (LPBYTE)&logfont, sizeof(LOGFONTW) );
1519 
1520  /* TODO: Save more settings here (List vs. Detailed View, etc.) */
1521  RegCloseKey( hKey );
1522 }
1523 
1524 static void resize_frame_rect(HWND hwnd, PRECT prect)
1525 {
1526  int new_top;
1527  RECT rt;
1528 
1532  prect->top = rt.bottom+3;
1533  prect->bottom -= rt.bottom+3;
1534  }
1535 
1539  new_top = --prect->top + rt.bottom+3;
1540  MoveWindow(Globals.hdrivebar, 0, prect->top, rt.right, new_top, TRUE);
1541  prect->top = new_top;
1542  prect->bottom -= rt.bottom+2;
1543  }
1544 
1546  int parts[] = {300, 500};
1547 
1551  prect->bottom -= rt.bottom;
1552  }
1553 
1554  MoveWindow(Globals.hmdiclient, prect->left-1,prect->top-1,prect->right+2,prect->bottom+1, TRUE);
1555 }
1556 
1557 static void resize_frame(HWND hwnd, int cx, int cy)
1558 {
1559  RECT rect;
1560 
1561  rect.left = 0;
1562  rect.top = 0;
1563  rect.right = cx;
1564  rect.bottom = cy;
1565 
1567 }
1568 
1570 {
1571  RECT rect;
1572 
1573  GetClientRect(hwnd, &rect);
1574 
1576 }
1577 
1578 
1579 static HHOOK hcbthook;
1581 
1583 {
1584  if (code==HCBT_CREATEWND && newchild) {
1585  ChildWnd* child = newchild;
1586  newchild = NULL;
1587 
1588  child->hwnd = (HWND) wparam;
1590  }
1591 
1593 }
1594 
1596 {
1598  int idx;
1599 
1600  mcs.szClass = sWINEFILETREE;
1601  mcs.szTitle = child->path;
1602  mcs.hOwner = Globals.hInstance;
1603  mcs.x = child->pos.rcNormalPosition.left;
1604  mcs.y = child->pos.rcNormalPosition.top;
1605  mcs.cx = child->pos.rcNormalPosition.right-child->pos.rcNormalPosition.left;
1606  mcs.cy = child->pos.rcNormalPosition.bottom-child->pos.rcNormalPosition.top;
1607  mcs.style = 0;
1608  mcs.lParam = 0;
1609 
1611 
1612  newchild = child;
1614  if (!child->hwnd) {
1616  return 0;
1617  }
1618 
1620 
1623 
1624  idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
1625  SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
1626 
1627  return child->hwnd;
1628 }
1629 
1630 #define RFF_NODEFAULT 0x02 /* No default item selected. */
1631 
1632 static void WineFile_OnRun( HWND hwnd )
1633 {
1634  static const WCHAR shell32_dll[] = {'S','H','E','L','L','3','2','.','D','L','L',0};
1635  void (WINAPI *pRunFileDlgAW )(HWND, HICON, LPWSTR, LPWSTR, LPWSTR, DWORD);
1636  HMODULE hshell = GetModuleHandleW( shell32_dll );
1637 
1638  pRunFileDlgAW = (void*)GetProcAddress(hshell, (LPCSTR)61);
1639  if (pRunFileDlgAW) pRunFileDlgAW( hwnd, 0, NULL, NULL, NULL, RFF_NODEFAULT);
1640 }
1641 
1643 {
1645 
1646  switch(nmsg) {
1647  case WM_INITDIALOG:
1650  return 1;
1651 
1652  case WM_COMMAND: {
1653  int id = (int)wparam;
1654 
1655  switch(id) {
1656  case IDOK: {
1659  EndDialog(hwnd, id);
1660  break;}
1661 
1662  case IDCANCEL:
1663  EndDialog(hwnd, id);
1664  break;
1665 
1666  case 254:
1668  break;
1669  }
1670 
1671  return 1;
1672  }
1673  }
1674 
1675  return 0;
1676 }
1677 
1678 
1681  int flags;
1682 };
1683 
1685 {
1686  static struct FilterDialog* dlg;
1687 
1688  switch(nmsg) {
1689  case WM_INITDIALOG:
1690  dlg = (struct FilterDialog*) lparam;
1697  return 1;
1698 
1699  case WM_COMMAND: {
1700  int id = (int)wparam;
1701 
1702  if (id == IDOK) {
1703  int flags = 0;
1704 
1706 
1712 
1713  dlg->flags = flags;
1714 
1715  EndDialog(hwnd, id);
1716  } else if (id == IDCANCEL)
1717  EndDialog(hwnd, id);
1718 
1719  return 1;}
1720  }
1721 
1722  return 0;
1723 }
1724 
1725 
1730 };
1731 
1732 /* Structure used to store enumerated languages and code pages. */
1736 } *lpTranslate;
1737 
1738 static LPCSTR InfoStrings[] = {
1739  "Comments",
1740  "CompanyName",
1741  "FileDescription",
1742  "FileVersion",
1743  "InternalName",
1744  "LegalCopyright",
1745  "LegalTrademarks",
1746  "OriginalFilename",
1747  "PrivateBuild",
1748  "ProductName",
1749  "ProductVersion",
1750  "SpecialBuild",
1751  NULL
1752 };
1753 
1754 static void PropDlg_DisplayValue(HWND hlbox, HWND hedit)
1755 {
1756  int idx = SendMessageW(hlbox, LB_GETCURSEL, 0, 0);
1757 
1758  if (idx != LB_ERR) {
1759  LPCWSTR pValue = (LPCWSTR)SendMessageW(hlbox, LB_GETITEMDATA, idx, 0);
1760 
1761  if (pValue)
1762  SetWindowTextW(hedit, pValue);
1763  }
1764 }
1765 
1766 static void CheckForFileInfo(struct PropertiesDialog* dlg, HWND hwnd, LPCWSTR strFilename)
1767 {
1768  static const WCHAR sBackSlash[] = {'\\','\0'};
1769  static const WCHAR sTranslation[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n','\0'};
1770  static const WCHAR sStringFileInfo[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o','\\',
1771  '%','0','4','x','%','0','4','x','\\','%','s','\0'};
1772  static const WCHAR sFmt[] = {'%','d','.','%','d','.','%','d','.','%','d','\0'};
1773  DWORD dwVersionDataLen = GetFileVersionInfoSizeW(strFilename, NULL);
1774 
1775  if (dwVersionDataLen) {
1776  dlg->pVersionData = HeapAlloc(GetProcessHeap(), 0, dwVersionDataLen);
1777 
1778  if (GetFileVersionInfoW(strFilename, 0, dwVersionDataLen, dlg->pVersionData)) {
1779  LPVOID pVal;
1780  UINT nValLen;
1781 
1782  if (VerQueryValueW(dlg->pVersionData, sBackSlash, &pVal, &nValLen)) {
1783  if (nValLen == sizeof(VS_FIXEDFILEINFO)) {
1784  VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)pVal;
1786 
1787  sprintfW(buffer, sFmt,
1788  HIWORD(pFixedFileInfo->dwFileVersionMS), LOWORD(pFixedFileInfo->dwFileVersionMS),
1789  HIWORD(pFixedFileInfo->dwFileVersionLS), LOWORD(pFixedFileInfo->dwFileVersionLS));
1790 
1792  }
1793  }
1794 
1795  /* Read the list of languages and code pages. */
1796  if (VerQueryValueW(dlg->pVersionData, sTranslation, &pVal, &nValLen)) {
1797  struct LANGANDCODEPAGE* pTranslate = (struct LANGANDCODEPAGE*)pVal;
1798  struct LANGANDCODEPAGE* pEnd = (struct LANGANDCODEPAGE*)((LPBYTE)pVal+nValLen);
1799 
1801 
1802  /* Read the file description for each language and code page. */
1803  for(; pTranslate<pEnd; ++pTranslate) {
1804  LPCSTR* p;
1805 
1806  for(p=InfoStrings; *p; ++p) {
1807  WCHAR subblock[200];
1808  WCHAR infoStr[100];
1809  LPCWSTR pTxt;
1810  UINT nValLen;
1811 
1812  LPCSTR pInfoString = *p;
1813  MultiByteToWideChar(CP_ACP, 0, pInfoString, -1, infoStr, 100);
1814  wsprintfW(subblock, sStringFileInfo, pTranslate->wLanguage, pTranslate->wCodePage, infoStr);
1815 
1816  /* Retrieve file description for language and code page */
1817  if (VerQueryValueW(dlg->pVersionData, subblock, (PVOID)&pTxt, &nValLen)) {
1818  int idx = SendMessageW(hlbox, LB_ADDSTRING, 0L, (LPARAM)infoStr);
1819  SendMessageW(hlbox, LB_SETITEMDATA, idx, (LPARAM)pTxt);
1820  }
1821  }
1822  }
1823 
1824  SendMessageW(hlbox, LB_SETCURSEL, 0, 0);
1825 
1827  }
1828  }
1829  }
1830 }
1831 
1833 {
1834  static struct PropertiesDialog* dlg;
1835 
1836  switch(nmsg) {
1837  case WM_INITDIALOG: {
1838  static const WCHAR sByteFmt[] = {'%','s',' ','B','y','t','e','s','\0'};
1840  LPWIN32_FIND_DATAW pWFD;
1841 
1842  dlg = (struct PropertiesDialog*) lparam;
1843  pWFD = (LPWIN32_FIND_DATAW)&dlg->entry.data;
1844 
1846  wsprintfW(b2, b1, pWFD->cFileName);
1848 
1851 
1852  format_longlong( b1, ((ULONGLONG)pWFD->nFileSizeHigh << 32) | pWFD->nFileSizeLow );
1853  wsprintfW(b2, sByteFmt, b1);
1855 
1858 
1864 
1865  CheckForFileInfo(dlg, hwnd, dlg->path);
1866  return 1;}
1867 
1868  case WM_COMMAND: {
1869  int id = (int)wparam;
1870 
1871  switch(HIWORD(wparam)) {
1872  case LBN_SELCHANGE: {
1875  break;
1876  }
1877 
1878  case BN_CLICKED:
1879  if (id==IDOK || id==IDCANCEL)
1880  EndDialog(hwnd, id);
1881  }
1882 
1883  return 1;}
1884 
1885  case WM_NCDESTROY:
1886  HeapFree(GetProcessHeap(), 0, dlg->pVersionData);
1887  dlg->pVersionData = NULL;
1888  break;
1889  }
1890 
1891  return 0;
1892 }
1893 
1895 {
1896  struct PropertiesDialog dlg;
1897 
1898  memset(&dlg, 0, sizeof(struct PropertiesDialog));
1899  get_path(entry, dlg.path);
1900  memcpy(&dlg.entry, entry, sizeof(Entry));
1901 
1903 }
1904 
1905 static struct FullScreenParameters {
1909 } g_fullscreen = {
1910  FALSE, /* mode */
1911  {0, 0, 0, 0},
1912  FALSE
1913 };
1914 
1916 {
1917  RECT rt;
1918 
1919  if (!IsIconic(hwnd))
1920  GetClientRect(hwnd, prect);
1921  else {
1922  WINDOWPLACEMENT wp;
1923 
1924  GetWindowPlacement(hwnd, &wp);
1925 
1926  prect->left = prect->top = 0;
1932  }
1933 
1936  prect->top += rt.bottom+2;
1937  }
1938 
1941  prect->top += rt.bottom+2;
1942  }
1943 
1946  prect->bottom -= rt.bottom;
1947  }
1948 }
1949 
1951 {
1952  RECT rt;
1953 
1954  if ((g_fullscreen.mode=!g_fullscreen.mode)) {
1955  GetWindowRect(hwnd, &g_fullscreen.orgPos);
1956  g_fullscreen.wasZoomed = IsZoomed(hwnd);
1957 
1959  MapWindowPoints( hwnd, 0, (POINT *)&rt, 2 );
1960 
1961  rt.left = g_fullscreen.orgPos.left-rt.left;
1962  rt.top = g_fullscreen.orgPos.top-rt.top;
1963  rt.right = GetSystemMetrics(SM_CXSCREEN)+g_fullscreen.orgPos.right-rt.right;
1964  rt.bottom = GetSystemMetrics(SM_CYSCREEN)+g_fullscreen.orgPos.bottom-rt.bottom;
1965 
1966  MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1967  } else {
1968  MoveWindow(hwnd, g_fullscreen.orgPos.left, g_fullscreen.orgPos.top,
1969  g_fullscreen.orgPos.right-g_fullscreen.orgPos.left,
1970  g_fullscreen.orgPos.bottom-g_fullscreen.orgPos.top, TRUE);
1971 
1972  if (g_fullscreen.wasZoomed)
1974  }
1975 
1976  return g_fullscreen.mode;
1977 }
1978 
1980 {
1981  RECT rt, pos;
1982  GetWindowRect(hwnd, &pos);
1983 
1985  MapWindowPoints( hwnd, 0, (POINT *)&rt, 2 );
1986 
1987  rt.left = pos.left-rt.left;
1988  rt.top = pos.top-rt.top;
1989  rt.right = GetSystemMetrics(SM_CXSCREEN)+pos.right-rt.right;
1990  rt.bottom = GetSystemMetrics(SM_CYSCREEN)+pos.bottom-rt.bottom;
1991 
1992  MoveWindow(hwnd, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, TRUE);
1993 }
1994 
1995 static void toggle_child(HWND hwnd, UINT cmd, HWND hchild)
1996 {
1997  BOOL vis = IsWindowVisible(hchild);
1998 
2000 
2001  ShowWindow(hchild, vis?SW_HIDE:SW_SHOW);
2002 
2003  if (g_fullscreen.mode)
2005 
2007 }
2008 
2010 {
2011  WCHAR drv1[_MAX_DRIVE], drv2[_MAX_DRIVE];
2012  HWND child_wnd;
2013 
2014  _wsplitpath(path, drv1, 0, 0, 0);
2015 
2016  /* search for an already open window for the same drive */
2017  for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2019 
2020  if (child) {
2021  _wsplitpath(child->root.path, drv2, 0, 0, 0);
2022 
2023  if (!lstrcmpiW(drv2, drv1)) {
2025 
2026  if (IsIconic(child_wnd))
2027  ShowWindow(child_wnd, SW_SHOWNORMAL);
2028 
2029  return TRUE;
2030  }
2031  }
2032  }
2033 
2034  return FALSE;
2035 }
2036 
2038 {
2039  HWND child_wnd;
2040 
2041  /* search for an already open window of the given file system name */
2042  for(child_wnd=GetNextWindow(Globals.hmdiclient,GW_CHILD); child_wnd; child_wnd=GetNextWindow(child_wnd, GW_HWNDNEXT)) {
2044 
2045  if (child) {
2046  if (!lstrcmpiW(child->root.fs, filesys)) {
2048 
2049  if (IsIconic(child_wnd))
2050  ShowWindow(child_wnd, SW_SHOWNORMAL);
2051 
2052  return TRUE;
2053  }
2054  }
2055  }
2056 
2057  return FALSE;
2058 }
2059 
2061 {
2063 
2064  switch(nmsg) {
2065  case WM_CLOSE:
2066  if (Globals.saveSettings)
2068 
2070 
2071  /* clear handle variables */
2072  Globals.hMenuFrame = 0;
2073  Globals.hMenuView = 0;
2074  Globals.hMenuOptions = 0;
2075  Globals.hMainWnd = 0;
2076  Globals.hmdiclient = 0;
2077  Globals.hdrivebar = 0;
2078  break;
2079 
2080  case WM_DESTROY:
2081  PostQuitMessage(0);
2082  break;
2083 
2084  case WM_INITMENUPOPUP: {
2085  HWND hwndClient = (HWND)SendMessageW(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2086 
2087  if (!SendMessageW(hwndClient, WM_INITMENUPOPUP, wparam, lparam))
2088  return 0;
2089  break;}
2090 
2091  case WM_COMMAND: {
2092  UINT cmd = LOWORD(wparam);
2093  HWND hwndClient = (HWND)SendMessageW(Globals.hmdiclient, WM_MDIGETACTIVE, 0, 0);
2094 
2095  if (SendMessageW(hwndClient, WM_DISPATCH_COMMAND, wparam, lparam))
2096  break;
2097 
2098  if (cmd>=ID_DRIVE_FIRST && cmd<=ID_DRIVE_FIRST+0xFF) {
2099  WCHAR drv[_MAX_DRIVE], path[MAX_PATH];
2100  ChildWnd* child;
2102  int i;
2103 
2104  for(i=cmd-ID_DRIVE_FIRST; i--; root++)
2105  while(*root)
2106  root++;
2107 
2109  return 0;
2110 
2111  _wsplitpath(root, drv, 0, 0, 0);
2112 
2113  if (!SetCurrentDirectoryW(drv)) {
2115  return 0;
2116  }
2117 
2118  GetCurrentDirectoryW(MAX_PATH, path); /*TODO: store last directory per drive */
2120 
2121  if (!create_child_window(child))
2122  HeapFree(GetProcessHeap(), 0, child);
2123  } else switch(cmd) {
2124  case ID_FILE_EXIT:
2125  SendMessageW(hwnd, WM_CLOSE, 0, 0);
2126  break;
2127 
2128  case ID_WINDOW_NEW: {
2129  WCHAR path[MAX_PATH];
2130  ChildWnd* child;
2131 
2134 
2135  if (!create_child_window(child))
2136  HeapFree(GetProcessHeap(), 0, child);
2137  break;}
2138 
2139  case ID_REFRESH:
2140  refresh_drives();
2141  break;
2142 
2143  case ID_WINDOW_CASCADE:
2145  break;
2146 
2147  case ID_WINDOW_TILE_HORZ:
2149  break;
2150 
2151  case ID_WINDOW_TILE_VERT:
2153  break;
2154 
2155  case ID_WINDOW_ARRANGE:
2157  break;
2158 
2159  case ID_SELECT_FONT:
2160  choose_font(hwnd);
2161  break;
2162 
2163  case ID_VIEW_TOOL_BAR:
2165  break;
2166 
2167  case ID_VIEW_DRIVE_BAR:
2169  break;
2170 
2171  case ID_VIEW_STATUSBAR:
2173  break;
2174 
2175  case ID_VIEW_SAVESETTINGS:
2179  break;
2180 
2181  case ID_RUN:
2182  WineFile_OnRun( hwnd );
2183  break;
2184 
2185  case ID_CONNECT_NETWORK_DRIVE: {
2187  if (ret == NO_ERROR)
2188  refresh_drives();
2189  else if (ret != (DWORD)-1) {
2190  if (ret == ERROR_EXTENDED_ERROR)
2192  else
2194  }
2195  break;}
2196 
2199  if (ret == NO_ERROR)
2200  refresh_drives();
2201  else if (ret != (DWORD)-1) {
2202  if (ret == ERROR_EXTENDED_ERROR)
2204  else
2206  }
2207  break;}
2208 
2209  case ID_HELP:
2211  break;
2212 
2213  case ID_VIEW_FULLSCREEN:
2215  break;
2216 
2217 #ifdef __WINE__
2218  case ID_DRIVE_UNIX_FS: {
2219  WCHAR path[MAX_PATH];
2220  char cpath[MAX_PATH];
2221  ChildWnd* child;
2222 
2224  break;
2225 
2226  getcwd(cpath, MAX_PATH);
2227  MultiByteToWideChar(CP_UNIXCP, 0, cpath, -1, path, MAX_PATH);
2229 
2230  if (!create_child_window(child))
2231  HeapFree(GetProcessHeap(), 0, child);
2232  break;}
2233 #endif
2234  case ID_DRIVE_SHELL_NS: {
2235  WCHAR path[MAX_PATH];
2236  ChildWnd* child;
2237 
2239  break;
2240 
2243 
2244  if (!create_child_window(child))
2245  HeapFree(GetProcessHeap(), 0, child);
2246  break;}
2247 
2248  /*TODO: There are even more menu items! */
2249 
2250  case ID_ABOUT:
2253  IMAGE_ICON, 48, 48, LR_SHARED ));
2254  break;
2255 
2256  default:
2257  /*TODO: if (wParam >= PM_FIRST_LANGUAGE && wParam <= PM_LAST_LANGUAGE)
2258  STRING_SelectLanguageByNumber(wParam - PM_FIRST_LANGUAGE);
2259  else */if ((cmd<IDW_FIRST_CHILD || cmd>=IDW_FIRST_CHILD+0x100) &&
2262 
2263  return DefFrameProcW(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2264  }
2265  break;}
2266 
2267  case WM_SIZE:
2269  break; /* do not pass message to DefFrameProcW */
2270 
2271  case WM_DEVICECHANGE:
2273  break;
2274 
2275  case WM_GETMINMAXINFO: {
2276  LPMINMAXINFO lpmmi = (LPMINMAXINFO)lparam;
2277 
2278  lpmmi->ptMaxTrackSize.x <<= 1;/*2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN */
2279  lpmmi->ptMaxTrackSize.y <<= 1;/*2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN */
2280  break;}
2281 
2282  case FRM_CALC_CLIENT:
2284  return TRUE;
2285 
2286  default:
2287  return DefFrameProcW(hwnd, Globals.hmdiclient, nmsg, wparam, lparam);
2288  }
2289 
2290  return 0;
2291 }
2292 
2293 
2294 static WCHAR g_pos_names[COLUMNS][40] = {
2295  {'\0'} /* symbol */
2296 };
2297 
2298 static const int g_pos_align[] = {
2299  0,
2300  HDF_LEFT, /* Name */
2301  HDF_RIGHT, /* Size */
2302  HDF_LEFT, /* CDate */
2303  HDF_LEFT, /* ADate */
2304  HDF_LEFT, /* MDate */
2305  HDF_LEFT, /* Index */
2306  HDF_CENTER, /* Links */
2307  HDF_CENTER, /* Attributes */
2308  HDF_LEFT /* Security */
2309 };
2310 
2311 static void resize_tree(ChildWnd* child, int cx, int cy)
2312 {
2313  HDWP hdwp = BeginDeferWindowPos(4);
2314  RECT rt;
2315  WINDOWPOS wp;
2316  HD_LAYOUT hdl;
2317 
2318  rt.left = 0;
2319  rt.top = 0;
2320  rt.right = cx;
2321  rt.bottom = cy;
2322 
2323  cx = child->split_pos + SPLIT_WIDTH/2;
2324  hdl.prc = &rt;
2325  hdl.pwpos = &wp;
2326 
2327  SendMessageW(child->left.hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hdl);
2328 
2329  DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
2330  wp.x-1, wp.y, child->split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
2331  DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
2332  rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
2333  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);
2334  DeferWindowPos(hdwp, child->right.hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
2335 
2336  EndDeferWindowPos(hdwp);
2337 }
2338 
2340 {
2341  HDITEMW hdi;
2342  int idx;
2343 
2344  HWND hwnd = CreateWindowW(WC_HEADERW, 0, WS_CHILD|WS_VISIBLE|HDS_HORZ|HDS_FULLDRAG/*TODO: |HDS_BUTTONS + sort orders*/,
2345  0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2346  if (!hwnd)
2347  return 0;
2348 
2350 
2352 
2353  for(idx=0; idx<COLUMNS; idx++) {
2354  hdi.pszText = g_pos_names[idx];
2355  hdi.fmt = HDF_STRING | g_pos_align[idx];
2356  hdi.cxy = pane->widths[idx];
2358  }
2359 
2360  return hwnd;
2361 }
2362 
2363 static void init_output(HWND hwnd)
2364 {
2365  static const WCHAR s1000[] = {'1','0','0','0','\0'};
2366  WCHAR b[16];
2367  HFONT old_font;
2368  HDC hdc = GetDC(hwnd);
2369 
2370  if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, s1000, 0, b, 16) > 4)
2371  Globals.num_sep = b[1];
2372  else
2373  Globals.num_sep = '.';
2374 
2375  old_font = SelectObject(hdc, Globals.hfont);
2377  SelectObject(hdc, old_font);
2378  ReleaseDC(hwnd, hdc);
2379 }
2380 
2381 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol);
2382 
2383 
2384 /* calculate preferred width for all visible columns */
2385 
2386 static BOOL calc_widths(Pane* pane, BOOL anyway)
2387 {
2388  int col, x, cx, spc=3*Globals.spaceSize.cx;
2389  int entries = SendMessageW(pane->hwnd, LB_GETCOUNT, 0, 0);
2390  int orgWidths[COLUMNS];
2391  int orgPositions[COLUMNS+1];
2392  HFONT hfontOld;
2393  HDC hdc;
2394  int cnt;
2395 
2396  if (!anyway) {
2397  memcpy(orgWidths, pane->widths, sizeof(orgWidths));
2398  memcpy(orgPositions, pane->positions, sizeof(orgPositions));
2399  }
2400 
2401  for(col=0; col<COLUMNS; col++)
2402  pane->widths[col] = 0;
2403 
2404  hdc = GetDC(pane->hwnd);
2405  hfontOld = SelectObject(hdc, Globals.hfont);
2406 
2407  for(cnt=0; cnt<entries; cnt++) {
2408  Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2409 
2410  DRAWITEMSTRUCT dis;
2411 
2412  dis.CtlType = 0;
2413  dis.CtlID = 0;
2414  dis.itemID = 0;
2415  dis.itemAction = 0;
2416  dis.itemState = 0;
2417  dis.hwndItem = pane->hwnd;
2418  dis.hDC = hdc;
2419  dis.rcItem.left = 0;
2420  dis.rcItem.top = 0;
2421  dis.rcItem.right = 0;
2422  dis.rcItem.bottom = 0;
2423  /*dis.itemData = 0; */
2424 
2425  draw_item(pane, &dis, entry, COLUMNS);
2426  }
2427 
2428  SelectObject(hdc, hfontOld);
2429  ReleaseDC(pane->hwnd, hdc);
2430 
2431  x = 0;
2432  for(col=0; col<COLUMNS; col++) {
2433  pane->positions[col] = x;
2434  cx = pane->widths[col];
2435 
2436  if (cx) {
2437  cx += spc;
2438 
2439  if (cx < IMAGE_WIDTH)
2440  cx = IMAGE_WIDTH;
2441 
2442  pane->widths[col] = cx;
2443  }
2444 
2445  x += cx;
2446  }
2447 
2448  pane->positions[COLUMNS] = x;
2449 
2451 
2452  /* no change? */
2453  if (!anyway && !memcmp(orgWidths, pane->widths, sizeof(orgWidths)))
2454  return FALSE;
2455 
2456  /* don't move, if only collapsing an entry */
2457  if (!anyway && pane->widths[0]<orgWidths[0] &&
2458  !memcmp(orgWidths+1, pane->widths+1, sizeof(orgWidths)-sizeof(int))) {
2459  pane->widths[0] = orgWidths[0];
2460  memcpy(pane->positions, orgPositions, sizeof(orgPositions));
2461 
2462  return FALSE;
2463  }
2464 
2465  InvalidateRect(pane->hwnd, 0, TRUE);
2466 
2467  return TRUE;
2468 }
2469 
2470 /* calculate one preferred column width */
2471 static void calc_single_width(Pane* pane, int col)
2472 {
2473  HFONT hfontOld;
2474  int x, cx;
2475  int entries = SendMessageW(pane->hwnd, LB_GETCOUNT, 0, 0);
2476  int cnt;
2477  HDC hdc;
2478 
2479  pane->widths[col] = 0;
2480 
2481  hdc = GetDC(pane->hwnd);
2482  hfontOld = SelectObject(hdc, Globals.hfont);
2483 
2484  for(cnt=0; cnt<entries; cnt++) {
2485  Entry* entry = (Entry*)SendMessageW(pane->hwnd, LB_GETITEMDATA, cnt, 0);
2486  DRAWITEMSTRUCT dis;
2487 
2488  dis.CtlType = 0;
2489  dis.CtlID = 0;
2490  dis.itemID = 0;
2491  dis.itemAction = 0;
2492  dis.itemState = 0;
2493  dis.hwndItem = pane->hwnd;
2494  dis.hDC = hdc;
2495  dis.rcItem.left = 0;
2496  dis.rcItem.top = 0;
2497  dis.rcItem.right = 0;
2498  dis.rcItem.bottom = 0;
2499  /*dis.itemData = 0; */
2500 
2501  draw_item(pane, &dis, entry, col);
2502  }
2503 
2504  SelectObject(hdc, hfontOld);
2505  ReleaseDC(pane->hwnd, hdc);
2506 
2507  cx = pane->widths[col];
2508 
2509  if (cx) {
2510  cx += 3*Globals.spaceSize.cx;
2511 
2512  if (cx < IMAGE_WIDTH)
2513  cx = IMAGE_WIDTH;
2514  }
2515 
2516  pane->widths[col] = cx;
2517 
2518  x = pane->positions[col] + cx;
2519 
2520  for(; col<COLUMNS-1; ) {
2521  pane->positions[++col] = x;
2522  x += pane->widths[col];
2523  }
2524 
2526 }
2527 
2529 {
2530  for( ; *str&&*pattern; str++,pattern++) {
2531  if (*pattern == '*') {
2532  do pattern++;
2533  while(*pattern == '*');
2534 
2535  if (!*pattern)
2536  return TRUE;
2537 
2538  for(; *str; str++)
2539  if (*str==*pattern && pattern_match(str, pattern))
2540  return TRUE;
2541 
2542  return FALSE;
2543  }
2544  else if (*str!=*pattern && *pattern!='?')
2545  return FALSE;
2546  }
2547 
2548  if (*str || *pattern)
2549  if (*pattern!='*' || pattern[1]!='\0')
2550  return FALSE;
2551 
2552  return TRUE;
2553 }
2554 
2556 {
2558 
2559  lstrcpyW(b1, str);
2560  lstrcpyW(b2, pattern);
2561  CharUpperW(b1);
2562  CharUpperW(b2);
2563 
2564  return pattern_match(b1, b2);
2565 }
2566 
2567 
2572 };
2573 
2575 
2576 
2577 /* insert listbox entries after index idx */
2578 
2579 static int insert_entries(Pane* pane, Entry* dir, LPCWSTR pattern, int filter_flags, int idx)
2580 {
2581  Entry* entry = dir;
2582 
2583  if (!entry)
2584  return idx;
2585 
2586  ShowWindow(pane->hwnd, SW_HIDE);
2587 
2588  for(; entry; entry=entry->next) {
2589  if (pane->treePane && !(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2590  continue;
2591 
2592  if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2593  /* don't display entries "." and ".." in the left pane */
2594  if (pane->treePane && entry->data.cFileName[0] == '.')
2595  if (entry->data.cFileName[1] == '\0' ||
2596  (entry->data.cFileName[1] == '.' &&
2597  entry->data.cFileName[2] == '\0'))
2598  continue;
2599 
2600  /* filter directories in right pane */
2601  if (!pane->treePane && !(filter_flags&TF_DIRECTORIES))
2602  continue;
2603  }
2604 
2605  /* filter using the file name pattern */
2606  if (pattern)
2607  if (!pattern_imatch(entry->data.cFileName, pattern))
2608  continue;
2609 
2610  /* filter system and hidden files */
2611  if (!(filter_flags&TF_HIDDEN) && (entry->data.dwFileAttributes&(FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)))
2612  continue;
2613 
2614  /* filter looking at the file type */
2616  switch(get_file_type(entry->data.cFileName)) {
2617  case FT_EXECUTABLE:
2618  if (!(filter_flags & TF_PROGRAMS))
2619  continue;
2620  break;
2621 
2622  case FT_DOCUMENT:
2623  if (!(filter_flags & TF_DOCUMENTS))
2624  continue;
2625  break;
2626 
2627  default: /* TF_OTHERS */
2628  if (!(filter_flags & TF_OTHERS))
2629  continue;
2630  }
2631 
2632  if (idx != -1)
2633  idx++;
2634 
2636 
2637  if (pane->treePane && entry->expanded)
2638  idx = insert_entries(pane, entry->down, pattern, filter_flags, idx);
2639  }
2640 
2641  ShowWindow(pane->hwnd, SW_SHOW);
2642 
2643  return idx;
2644 }
2645 
2646 
2648 {
2649  static const WCHAR sFmtSmall[] = {'%', 'u', 0};
2650  static const WCHAR sFmtBig[] = {'%', '.', '1', 'f', ' ', '%', 's', '\0'};
2651 
2652  if (bytes < 1024)
2653  sprintfW(buffer, sFmtSmall, (DWORD)bytes);
2654  else
2655  {
2656  WCHAR unit[64];
2657  UINT resid;
2658  float fBytes;
2659  if (bytes >= 1073741824) /* 1 GB */
2660  {
2661  fBytes = ((float)bytes)/1073741824.f+.5f;
2662  resid = IDS_UNIT_GB;
2663  }
2664  else if (bytes >= 1048576) /* 1 MB */
2665  {
2666  fBytes = ((float)bytes)/1048576.f+.5f;
2667  resid = IDS_UNIT_MB;
2668  }
2669  else /* bytes >= 1024 */ /* 1 kB */
2670  {
2671  fBytes = ((float)bytes)/1024.f+.5f;
2672  resid = IDS_UNIT_KB;
2673  }
2674  LoadStringW(Globals.hInstance, resid, unit, sizeof(unit)/sizeof(*unit));
2675  sprintfW(buffer, sFmtBig, fBytes, unit);
2676  }
2677 }
2678 
2679 static void set_space_status(void)
2680 {
2681  ULARGE_INTEGER ulFreeBytesToCaller, ulTotalBytes, ulFreeBytes;
2682  WCHAR fmt[64], b1[64], b2[64], buffer[BUFFER_LEN];
2683 
2684  if (GetDiskFreeSpaceExW(NULL, &ulFreeBytesToCaller, &ulTotalBytes, &ulFreeBytes)) {
2685  DWORD_PTR args[2];
2686  format_bytes(b1, ulFreeBytesToCaller.QuadPart);
2687  format_bytes(b2, ulTotalBytes.QuadPart);
2688  args[0] = (DWORD_PTR)b1;
2689  args[1] = (DWORD_PTR)b2;
2691  RS(fmt,IDS_FREE_SPACE_FMT), 0, 0, buffer,
2692  sizeof(buffer)/sizeof(*buffer), (__ms_va_list*)args);
2693  } else
2695 
2697 }
2698 
2699 
2701 
2702 static void create_tree_window(HWND parent, Pane* pane, UINT id, UINT id_header, LPCWSTR pattern, int filter_flags)
2703 {
2704  static const WCHAR sListBox[] = {'L','i','s','t','B','o','x','\0'};
2705 
2706  static BOOL s_init = FALSE;
2707  Entry* entry = pane->root;
2708 
2711  0, 0, 0, 0, parent, (HMENU)ULongToHandle(id), Globals.hInstance, 0);
2712 
2713  SetWindowLongPtrW(pane->hwnd, GWLP_USERDATA, (LPARAM)pane);
2715 
2716  SendMessageW(pane->hwnd, WM_SETFONT, (WPARAM)Globals.hfont, FALSE);
2717 
2718  /* insert entries into listbox */
2719  if (entry)
2720  insert_entries(pane, entry, pattern, filter_flags, -1);
2721 
2722  /* calculate column widths */
2723  if (!s_init) {
2724  s_init = TRUE;
2725  init_output(pane->hwnd);
2726  }
2727 
2728  calc_widths(pane, TRUE);
2729 
2730  pane->hwndHeader = create_header(parent, pane, id_header);
2731 }
2732 
2733 
2735 {
2737  create_tree_window(child->hwnd, &child->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, child->filter_pattern, child->filter_flags);
2738 }
2739 
2740 
2741 static void format_date(const FILETIME* ft, WCHAR* buffer, int visible_cols)
2742 {
2743  SYSTEMTIME systime;
2744  FILETIME lft;
2745  int len = 0;
2746 
2747  *buffer = '\0';
2748 
2749  if (!ft->dwLowDateTime && !ft->dwHighDateTime)
2750  return;
2751 
2752  if (!FileTimeToLocalFileTime(ft, &lft))
2753  {err: lstrcpyW(buffer,sQMarks); return;}
2754 
2755  if (!FileTimeToSystemTime(&lft, &systime))
2756  goto err;
2757 
2758  if (visible_cols & COL_DATE) {
2760  if (!len)
2761  goto err;
2762  }
2763 
2764  if (visible_cols & COL_TIME) {
2765  if (len)
2766  buffer[len-1] = ' ';
2767 
2768  buffer[len++] = ' ';
2769 
2770  if (!GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &systime, 0, buffer+len, BUFFER_LEN-len))
2771  buffer[len] = '\0';
2772  }
2773 }
2774 
2775 
2776 static void calc_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2777 {
2778  RECT rt = {0, 0, 0, 0};
2779 
2781 
2782  if (rt.right > pane->widths[col])
2783  pane->widths[col] = rt.right;
2784 }
2785 
2786 static void calc_tabbed_width(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2787 {
2788  RECT rt = {0, 0, 0, 0};
2789 
2791  /*FIXME rt (0,0) ??? */
2792 
2793  if (rt.right > pane->widths[col])
2794  pane->widths[col] = rt.right;
2795 }
2796 
2797 
2798 static void output_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str, DWORD flags)
2799 {
2800  int x = dis->rcItem.left;
2801  RECT rt;
2802 
2803  rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
2804  rt.top = dis->rcItem.top;
2805  rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
2806  rt.bottom = dis->rcItem.bottom;
2807 
2808  DrawTextW(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_NOPREFIX|flags);
2809 }
2810 
2811 static void output_tabbed_text(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2812 {
2813  int x = dis->rcItem.left;
2814  RECT rt;
2815 
2816  rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
2817  rt.top = dis->rcItem.top;
2818  rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
2819  rt.bottom = dis->rcItem.bottom;
2820 
2821  DrawTextW(dis->hDC, str, -1, &rt, DT_SINGLELINE|DT_EXPANDTABS|DT_TABSTOP|(2<<8));
2822 }
2823 
2824 static void output_number(Pane* pane, LPDRAWITEMSTRUCT dis, int col, LPCWSTR str)
2825 {
2826  int x = dis->rcItem.left;
2827  RECT rt;
2828  LPCWSTR s = str;
2829  WCHAR b[128];
2830  LPWSTR d = b;
2831  int pos;
2832 
2833  rt.left = x+pane->positions[col]+Globals.spaceSize.cx;
2834  rt.top = dis->rcItem.top;
2835  rt.right = x+pane->positions[col+1]-Globals.spaceSize.cx;
2836  rt.bottom = dis->rcItem.bottom;
2837 
2838  if (*s)
2839  *d++ = *s++;
2840 
2841  /* insert number separator characters */
2842  pos = lstrlenW(s) % 3;
2843 
2844  while(*s)
2845  if (pos--)
2846  *d++ = *s++;
2847  else {
2848  *d++ = Globals.num_sep;
2849  pos = 3;
2850  }
2851 
2853 }
2854 
2855 
2857 {
2858  static const WCHAR executable_extensions[][4] = {
2859  {'C','O','M','\0'},
2860  {'E','X','E','\0'},
2861  {'B','A','T','\0'},
2862  {'C','M','D','\0'},
2863  {'C','M','M','\0'},
2864  {'B','T','M','\0'},
2865  {'A','W','K','\0'},
2866  {'\0'}
2867  };
2868 
2869  WCHAR ext_buffer[_MAX_EXT];
2870  const WCHAR (*p)[4];
2871  LPCWSTR s;
2872  LPWSTR d;
2873 
2874  for(s=ext+1,d=ext_buffer; (*d=tolower(*s)); s++)
2875  d++;
2876 
2877  for(p=executable_extensions; (*p)[0]; p++)
2878  if (!lstrcmpiW(ext_buffer, *p))
2879  return TRUE;
2880 
2881  return FALSE;
2882 }
2883 
2885 {
2886  /* check if there exists a classname for this file extension in the registry */
2888  return TRUE;
2889 
2890  return FALSE;
2891 }
2892 
2894 {
2895  LPCWSTR ext = strrchrW(filename, '.');
2896  if (!ext)
2897  ext = sEmpty;
2898 
2899  if (is_exe_file(ext))
2900  return FT_EXECUTABLE;
2901  else if (is_registered_type(ext))
2902  return FT_DOCUMENT;
2903  else
2904  return FT_OTHER;
2905 }
2906 
2907 
2908 static void draw_item(Pane* pane, LPDRAWITEMSTRUCT dis, Entry* entry, int calcWidthCol)
2909 {
2911  DWORD attrs;
2912  int visible_cols = pane->visible_cols;
2913  COLORREF bkcolor, textcolor;
2914  RECT focusRect = dis->rcItem;
2915  HBRUSH hbrush;
2916  enum IMAGE img;
2917  int img_pos, cx;
2918  int col = 0;
2919 
2920  if (entry) {
2921  attrs = entry->data.dwFileAttributes;
2922 
2923  if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
2924  if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '.'
2925  && entry->data.cFileName[2] == '\0')
2926  img = IMG_FOLDER_UP;
2927  else if (entry->data.cFileName[0] == '.' && entry->data.cFileName[1] == '\0')
2928  img = IMG_FOLDER_CUR;
2929  else if (pane->treePane && (dis->itemState&ODS_FOCUS))
2930  img = IMG_OPEN_FOLDER;
2931  else
2932  img = IMG_FOLDER;
2933  } else {
2934  switch(get_file_type(entry->data.cFileName)) {
2935  case FT_EXECUTABLE: img = IMG_EXECUTABLE; break;
2936  case FT_DOCUMENT: img = IMG_DOCUMENT; break;
2937  default: img = IMG_FILE;
2938  }
2939  }
2940  } else {
2941  attrs = 0;
2942  img = IMG_NONE;
2943  }
2944 
2945  if (pane->treePane) {
2946  if (entry) {
2947  img_pos = dis->rcItem.left + entry->level*(IMAGE_WIDTH+TREE_LINE_DX);
2948 
2949  if (calcWidthCol == -1) {
2950  int x;
2951  int y = dis->rcItem.top + IMAGE_HEIGHT/2;
2952  Entry* up;
2953  RECT rt_clip;
2954  HRGN hrgn_org = CreateRectRgn(0, 0, 0, 0);
2955  HRGN hrgn;
2956 
2957  rt_clip.left = dis->rcItem.left;
2958  rt_clip.top = dis->rcItem.top;
2959  rt_clip.right = dis->rcItem.left+pane->widths[col];
2960  rt_clip.bottom = dis->rcItem.bottom;
2961 
2962  hrgn = CreateRectRgnIndirect(&rt_clip);
2963 
2964  if (!GetClipRgn(dis->hDC, hrgn_org)) {
2965  DeleteObject(hrgn_org);
2966  hrgn_org = 0;
2967  }
2968 
2969  ExtSelectClipRgn(dis->hDC, hrgn, RGN_AND);
2970  DeleteObject(hrgn);
2971 
2972  if ((up=entry->up) != NULL) {
2973  MoveToEx(dis->hDC, img_pos-IMAGE_WIDTH/2, y, 0);
2974  LineTo(dis->hDC, img_pos-2, y);
2975 
2976  x = img_pos - IMAGE_WIDTH/2;
2977 
2978  do {
2980 
2981  if (up->next
2982  && (up->next->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
2983  ) {
2984  MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
2985  LineTo(dis->hDC, x, dis->rcItem.bottom);
2986  }
2987  } while((up=up->up) != NULL);
2988  }
2989 
2990  x = img_pos - IMAGE_WIDTH/2;
2991 
2992  MoveToEx(dis->hDC, x, dis->rcItem.top, 0);
2993  LineTo(dis->hDC, x, y);
2994 
2995  if (entry->next
2996  && (entry->next->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
2997  LineTo(dis->hDC, x, dis->rcItem.bottom);
2998 
2999  SelectClipRgn(dis->hDC, hrgn_org);
3000  if (hrgn_org) DeleteObject(hrgn_org);
3001  } else if (calcWidthCol==col || calcWidthCol==COLUMNS) {
3002  int right = img_pos + IMAGE_WIDTH - TREE_LINE_DX;
3003 
3004  if (right > pane->widths[col])
3005  pane->widths[col] = right;
3006  }
3007  } else {
3008  img_pos = dis->rcItem.left;
3009  }
3010  } else {
3011  img_pos = dis->rcItem.left;
3012 
3013  if (calcWidthCol==col || calcWidthCol==COLUMNS)
3014  pane->widths[col] = IMAGE_WIDTH;
3015  }
3016 
3017  if (calcWidthCol == -1) {
3018  focusRect.left = img_pos -2;
3019 
3020  if (attrs & FILE_ATTRIBUTE_COMPRESSED)
3021  textcolor = COLOR_COMPRESSED;
3022  else
3023  textcolor = RGB(0,0,0);
3024 
3025  if (dis->itemState & ODS_FOCUS) {
3026  textcolor = RGB(255,255,255);
3027  bkcolor = COLOR_SELECTION;
3028  } else {
3029  bkcolor = RGB(255,255,255);
3030  }
3031 
3032  hbrush = CreateSolidBrush(bkcolor);
3033  FillRect(dis->hDC, &focusRect, hbrush);
3035 
3036  SetBkMode(dis->hDC, TRANSPARENT);
3037  SetTextColor(dis->hDC, textcolor);
3038 
3039  cx = pane->widths[col];
3040 
3041  if (cx && img!=IMG_NONE) {
3042  if (cx > IMAGE_WIDTH)
3043  cx = IMAGE_WIDTH;
3044 
3045  if (entry->hicon && entry->hicon!=(HICON)-1)
3046  DrawIconEx(dis->hDC, img_pos, dis->rcItem.top, entry->hicon, cx, GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL);
3047  else
3049  img_pos, dis->rcItem.top, cx,
3050  IMAGE_HEIGHT, bkcolor, CLR_DEFAULT, ILD_NORMAL);
3051  }
3052  }
3053 
3054  if (!entry)
3055  return;
3056 
3057  col++;
3058 
3059  /* output file name */
3060  if (calcWidthCol == -1)
3061  output_text(pane, dis, col, entry->data.cFileName, 0);
3062  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3063  calc_width(pane, dis, col, entry->data.cFileName);
3064 
3065  col++;
3066 
3067  /* display file size */
3068  if (visible_cols & COL_SIZE) {
3069  format_longlong( buffer, ((ULONGLONG)entry->data.nFileSizeHigh << 32) | entry->data.nFileSizeLow );
3070 
3071  if (calcWidthCol == -1)
3072  output_number(pane, dis, col, buffer);
3073  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3074  calc_width(pane, dis, col, buffer);/*TODO: not ever time enough */
3075 
3076  col++;
3077  }
3078 
3079  /* display file date */
3080  if (visible_cols & (COL_DATE|COL_TIME)) {
3081  format_date(&entry->data.ftCreationTime, buffer, visible_cols);
3082  if (calcWidthCol == -1)
3083  output_text(pane, dis, col, buffer, 0);
3084  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3085  calc_width(pane, dis, col, buffer);
3086  col++;
3087 
3088  format_date(&entry->data.ftLastAccessTime, buffer, visible_cols);
3089  if (calcWidthCol == -1)
3090  output_text(pane, dis, col, buffer, 0);
3091  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3092  calc_width(pane, dis, col, buffer);
3093  col++;
3094 
3095  format_date(&entry->data.ftLastWriteTime, buffer, visible_cols);
3096  if (calcWidthCol == -1)
3097  output_text(pane, dis, col, buffer, 0);
3098  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3099  calc_width(pane, dis, col, buffer);
3100  col++;
3101  }
3102 
3103  if (entry->bhfi_valid) {
3104  if (visible_cols & COL_INDEX) {
3105  static const WCHAR fmtlow[] = {'%','X',0};
3106  static const WCHAR fmthigh[] = {'%','X','%','0','8','X',0};
3107 
3108  if (entry->bhfi.nFileIndexHigh)
3109  wsprintfW(buffer, fmthigh,
3110  entry->bhfi.nFileIndexHigh, entry->bhfi.nFileIndexLow );
3111  else
3112  wsprintfW(buffer, fmtlow, entry->bhfi.nFileIndexLow );
3113 
3114  if (calcWidthCol == -1)
3115  output_text(pane, dis, col, buffer, DT_RIGHT);
3116  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3117  calc_width(pane, dis, col, buffer);
3118 
3119  col++;
3120  }
3121 
3122  if (visible_cols & COL_LINKS) {
3123  wsprintfW(buffer, sNumFmt, entry->bhfi.nNumberOfLinks);
3124 
3125  if (calcWidthCol == -1)
3126  output_text(pane, dis, col, buffer, DT_CENTER);
3127  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3128  calc_width(pane, dis, col, buffer);
3129 
3130  col++;
3131  }
3132  } else
3133  col += 2;
3134 
3135  /* show file attributes */
3136  if (visible_cols & COL_ATTRIBUTES) {
3137  static const WCHAR s11Tabs[] = {' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\t',' ','\0'};
3138  lstrcpyW(buffer, s11Tabs);
3139 
3140  if (attrs & FILE_ATTRIBUTE_NORMAL) buffer[ 0] = 'N';
3141  else {
3142  if (attrs & FILE_ATTRIBUTE_READONLY) buffer[ 2] = 'R';
3143  if (attrs & FILE_ATTRIBUTE_HIDDEN) buffer[ 4] = 'H';
3144  if (attrs & FILE_ATTRIBUTE_SYSTEM) buffer[ 6] = 'S';
3145  if (attrs & FILE_ATTRIBUTE_ARCHIVE) buffer[ 8] = 'A';
3146  if (attrs & FILE_ATTRIBUTE_COMPRESSED) buffer[10] = 'C';
3147  if (attrs & FILE_ATTRIBUTE_DIRECTORY) buffer[12] = 'D';
3148  if (attrs & FILE_ATTRIBUTE_ENCRYPTED) buffer[14] = 'E';
3149  if (attrs & FILE_ATTRIBUTE_TEMPORARY) buffer[16] = 'T';
3150  if (attrs & FILE_ATTRIBUTE_SPARSE_FILE) buffer[18] = 'P';
3151  if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) buffer[20] = 'Q';
3152  if (attrs & FILE_ATTRIBUTE_OFFLINE) buffer[22] = 'O';
3153  if (attrs & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) buffer[24] = 'X';
3154  }
3155 
3156  if (calcWidthCol == -1)
3157  output_tabbed_text(pane, dis, col, buffer);
3158  else if (calcWidthCol==col || calcWidthCol==COLUMNS)
3159  calc_tabbed_width(pane, dis, col, buffer);
3160 
3161  col++;
3162  }
3163 }
3164 
3165 static void set_header(Pane* pane)
3166 {
3167  HDITEMW item;
3168  int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3169  int i=0, x=0;
3170 
3171  item.mask = HDI_WIDTH;
3172  item.cxy = 0;
3173 
3174  for(; (i < COLUMNS) && (x+pane->widths[i] < scroll_pos); i++) {
3175  x += pane->widths[i];
3177  }
3178 
3179  if (i < COLUMNS) {
3180  x += pane->widths[i];
3181  item.cxy = x - scroll_pos;
3183 
3184  for(; i < COLUMNS; i++) {
3185  item.cxy = pane->widths[i];
3186  x += pane->widths[i];
3188  }
3189  }
3190 }
3191 
3192 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
3193 {
3194  switch(pnmh->code) {
3195  case HDN_ITEMCHANGEDW: {
3196  LPNMHEADERW phdn = (LPNMHEADERW)pnmh;
3197  int idx = phdn->iItem;
3198  int dx = phdn->pitem->cxy - pane->widths[idx];
3199  int i;
3200 
3201  RECT clnt;
3202  GetClientRect(pane->hwnd, &clnt);
3203 
3204  pane->widths[idx] += dx;
3205 
3206  for(i=idx; ++i<=COLUMNS; )
3207  pane->positions[i] += dx;
3208 
3209  {
3210  int scroll_pos = GetScrollPos(pane->hwnd, SB_HORZ);
3211  RECT rt_scr;
3212  RECT rt_clip;
3213 
3214  rt_scr.left = pane->positions[idx+1]-scroll_pos;
3215  rt_scr.top = 0;
3216  rt_scr.right = clnt.right;
3217  rt_scr.bottom = clnt.bottom;
3218 
3219  rt_clip.left = pane->positions[idx]-scroll_pos;
3220  rt_clip.top = 0;
3221  rt_clip.right = clnt.right;
3222  rt_clip.bottom = clnt.bottom;
3223 
3224  if (rt_scr.left < 0) rt_scr.left = 0;
3225  if (rt_clip.left < 0) rt_clip.left = 0;
3226 
3227  ScrollWindowEx(pane->hwnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
3228 
3229  rt_clip.right = pane->positions[idx+1];
3230  RedrawWindow(pane->hwnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
3231 
3232  if (pnmh->code == HDN_ENDTRACKW) {
3234 
3235  if (GetScrollPos(pane->hwnd, SB_HORZ) != scroll_pos)
3236  set_header(pane);
3237  }
3238  }
3239 
3240  return FALSE;
3241  }
3242 
3243  case HDN_DIVIDERDBLCLICKW: {
3244  LPNMHEADERW phdn = (LPNMHEADERW)pnmh;
3245  HDITEMW item;
3246 
3247  calc_single_width(pane, phdn->iItem);
3248  item.mask = HDI_WIDTH;
3249  item.cxy = pane->widths[phdn->iItem];
3250 
3252  InvalidateRect(pane->hwnd, 0, TRUE);
3253  break;}
3254  }
3255 
3256  return 0;
3257 }
3258 
3260 {
3261  WCHAR path[MAX_PATH];
3262  HCURSOR old_cursor = SetCursor(LoadCursorW(0, (LPCWSTR)IDC_WAIT));
3263 
3264  /* delete sub entries in left pane */
3265  for(;;) {
3266  LRESULT res = SendMessageW(child->left.hwnd, LB_GETITEMDATA, idx+1, 0);
3267  Entry* sub = (Entry*) res;
3268 
3269  if (res==LB_ERR || !sub || sub->level<=entry->level)
3270  break;
3271 
3272  SendMessageW(child->left.hwnd, LB_DELETESTRING, idx+1, 0);
3273  }
3274 
3275  /* empty right pane */
3276  SendMessageW(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3277 
3278  /* release memory */
3280 
3281  /* read contents from disk */
3282  if (entry->etype == ET_SHELL)
3283  {
3284  read_directory(entry, NULL, child->sortOrder, hwnd);
3285  }
3286  else
3287  {
3288  get_path(entry, path);
3289  read_directory(entry, path, child->sortOrder, hwnd);
3290  }
3291 
3292  /* insert found entries in right pane */
3293  insert_entries(&child->right, entry->down, child->filter_pattern, child->filter_flags, -1);
3294  calc_widths(&child->right, FALSE);
3295  set_header(&child->right);
3296 
3297  child->header_wdths_ok = FALSE;
3298 
3299  SetCursor(old_cursor);
3300 }
3301 
3302 
3303 /* expand a directory entry */
3304 
3306 {
3307  int idx;
3308  Entry* p;
3309 
3310  if (!dir || dir->expanded || !dir->down)
3311  return FALSE;
3312 
3313  p = dir->down;
3314 
3315  if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='\0' && p->next) {
3316  p = p->next;
3317 
3318  if (p->data.cFileName[0]=='.' && p->data.cFileName[1]=='.' &&
3319  p->data.cFileName[2]=='\0' && p->next)
3320  p = p->next;
3321  }
3322 
3323  /* no subdirectories ? */
3324  if (!(p->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
3325  return FALSE;
3326 
3327  idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3328 
3329  dir->expanded = TRUE;
3330 
3331  /* insert entries in left pane */
3332  insert_entries(&child->left, p, NULL, TF_ALL, idx);
3333 
3334  if (!child->header_wdths_ok) {
3335  if (calc_widths(&child->left, FALSE)) {
3336  set_header(&child->left);
3337 
3338  child->header_wdths_ok = TRUE;
3339  }
3340  }
3341 
3342  return TRUE;
3343 }
3344 
3345 
3346 static void collapse_entry(Pane* pane, Entry* dir)
3347 {
3348  int idx;
3349 
3350  if (!dir) return;
3351  idx = SendMessageW(pane->hwnd, LB_FINDSTRING, 0, (LPARAM)dir);
3352 
3353  ShowWindow(pane->hwnd, SW_HIDE);
3354 
3355  /* hide sub entries */
3356  for(;;) {
3357  LRESULT res = SendMessageW(pane->hwnd, LB_GETITEMDATA, idx+1, 0);
3358  Entry* sub = (Entry*) res;
3359 
3360  if (res==LB_ERR || !sub || sub->level<=dir->level)
3361  break;
3362 
3363  SendMessageW(pane->hwnd, LB_DELETESTRING, idx+1, 0);
3364  }
3365 
3366  dir->expanded = FALSE;
3367 
3368  ShowWindow(pane->hwnd, SW_SHOW);
3369 }
3370 
3371 
3373 {
3374  SendMessageW(child->right.hwnd, LB_RESETCONTENT, 0, 0);
3375  insert_entries(&child->right, child->right.root, child->filter_pattern, child->filter_flags, -1);
3376  calc_widths(&child->right, FALSE);
3377 
3378  set_header(&child->right);
3379 }
3380 
3382 {
3383  WCHAR path[MAX_PATH];
3384 
3385  if (!entry)
3386  return;
3387 
3388  path[0] = '\0';
3389 
3390  child->left.cur = entry;
3391 
3392  child->right.root = entry->down? entry->down: entry;
3393  child->right.cur = entry;
3394 
3395  if (!entry->scanned)
3397  else
3399 
3400  get_path(entry, path);
3401  lstrcpyW(child->path, path);
3402 
3403  if (child->hwnd) /* only change window title, if the window already exists */
3404  SetWindowTextW(child->hwnd, path);
3405 
3406  if (path[0])
3408  set_space_status();
3409 }
3410 
3411 
3413 {
3414  WCHAR path[MAX_PATH], drv[_MAX_DRIVE+1];
3415  Entry* entry;
3416  int idx;
3417 
3418  get_path(child->left.cur, path);
3419  _wsplitpath(path, drv, NULL, NULL, NULL);
3420 
3421  child->right.root = NULL;
3422 
3423  scan_entry(child, &child->root.entry, 0, child->hwnd);
3424 
3425  if (child->root.entry.etype == ET_SHELL)
3426  {
3427  LPITEMIDLIST local_pidl = get_path_pidl(path,child->hwnd);
3428  if (local_pidl)
3429  entry = read_tree(&child->root, NULL, local_pidl , drv, child->sortOrder, child->hwnd);
3430  else
3431  entry = NULL;
3432  }
3433  else
3434  entry = read_tree(&child->root, path, NULL, drv, child->sortOrder, child->hwnd);
3435 
3436  if (!entry)
3437  entry = &child->root.entry;
3438 
3439  insert_entries(&child->left, child->root.entry.down, NULL, TF_ALL, 0);
3440 
3441  set_curdir(child, entry, 0, child->hwnd);
3442 
3443  idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, 0, (LPARAM)child->left.cur);
3444  SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
3445 }
3446 
3447 
3448 static void create_drive_bar(void)
3449 {
3450  TBBUTTON drivebarBtn = {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0, 0}, 0, 0};
3451  WCHAR b1[BUFFER_LEN];
3452  int btn = 1;
3453  PWSTR p;
3454 
3456 
3458  IDW_DRIVEBAR, 2, Globals.hInstance, IDB_DRIVEBAR, &drivebarBtn,
3459  0, 16, 13, 16, 13, sizeof(TBBUTTON));
3460 
3461 #ifdef __WINE__
3462  /* insert unix file system button */
3463  b1[0] = '/';
3464  b1[1] = '\0';
3465  b1[2] = '\0';
3467 
3468  drivebarBtn.idCommand = ID_DRIVE_UNIX_FS;
3469  SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3470  drivebarBtn.iString++;
3471 #endif
3472  /* insert shell namespace button */
3473  load_string(b1, sizeof(b1)/sizeof(b1[0]), IDS_SHELL);
3474  b1[lstrlenW(b1)+1] = '\0';
3476 
3477  drivebarBtn.idCommand = ID_DRIVE_SHELL_NS;
3478  SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3479  drivebarBtn.iString++;
3480 
3481  /* register windows drive root strings */
3483 
3484  drivebarBtn.idCommand = ID_DRIVE_FIRST;
3485 
3486  for(p=Globals.drives; *p; ) {
3487  switch(GetDriveTypeW(p)) {
3488  case DRIVE_REMOVABLE: drivebarBtn.iBitmap = 1; break;
3489  case DRIVE_CDROM: drivebarBtn.iBitmap = 3; break;
3490  case DRIVE_REMOTE: drivebarBtn.iBitmap = 4; break;
3491  case DRIVE_RAMDISK: drivebarBtn.iBitmap = 5; break;
3492  default:/*DRIVE_FIXED*/ drivebarBtn.iBitmap = 2;
3493  }
3494 
3495  SendMessageW(Globals.hdrivebar, TB_INSERTBUTTONW, btn++, (LPARAM)&drivebarBtn);
3496  drivebarBtn.idCommand++;
3497  drivebarBtn.iString++;
3498 
3499  while(*p++);
3500  }
3501 }
3502 
3503 static void refresh_drives(void)
3504 {
3505  RECT rect;
3506 
3507  /* destroy drive bar */
3509  Globals.hdrivebar = 0;
3510 
3511  /* re-create drive bar */
3512  create_drive_bar();
3513 
3514  /* update window layout */
3516  SendMessageW(Globals.hMainWnd, WM_SIZE, 0, MAKELONG(rect.right, rect.bottom));
3517 }
3518 
3519 
3521 {
3522  HINSTANCE hinst = ShellExecuteW(hwnd, NULL/*operation*/, cmd, NULL/*parameters*/, NULL/*dir*/, nCmdShow);
3523 
3524  if (PtrToUlong(hinst) <= 32) {
3526  return FALSE;
3527  }
3528 
3529  return TRUE;
3530 }
3531 
3532 
3534 {
3535  WCHAR cmd[MAX_PATH];
3536 
3537  if (entry->etype == ET_SHELL) {
3538  BOOL ret = TRUE;
3539 
3540  SHELLEXECUTEINFOW shexinfo;
3541 
3542  shexinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
3543  shexinfo.fMask = SEE_MASK_IDLIST;
3544  shexinfo.hwnd = hwnd;
3545  shexinfo.lpVerb = NULL;
3546  shexinfo.lpFile = NULL;
3547  shexinfo.lpParameters = NULL;
3548  shexinfo.lpDirectory = NULL;
3549  shexinfo.nShow = nCmdShow;
3550  shexinfo.lpIDList = get_to_absolute_pidl(entry, hwnd);
3551 
3552  if (!ShellExecuteExW(&shexinfo)) {
3554  ret = FALSE;
3555  }
3556 
3557  if (shexinfo.lpIDList != entry->pidl)
3558  IMalloc_Free(Globals.iMalloc, shexinfo.lpIDList);
3559 
3560  return ret;
3561  }
3562 
3563  get_path(entry, cmd);
3564 
3565  /* start program, open document... */
3566  return launch_file(hwnd, cmd, nCmdShow);
3567 }
3568 
3569 
3571 {
3572  Entry* entry = pane->cur;
3573 
3574  if (!entry)
3575  return;
3576 
3577  if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
3578  int scanned_old = entry->scanned;
3579 
3580  if (!scanned_old)
3581  {
3582  int idx = SendMessageW(child->left.hwnd, LB_GETCURSEL, 0, 0);
3584  }
3585 
3586  if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='\0')
3587  return;
3588 
3589  if (entry->data.cFileName[0]=='.' && entry->data.cFileName[1]=='.' && entry->data.cFileName[2]=='\0') {
3590  entry = child->left.cur->up;
3591  collapse_entry(&child->left, entry);
3592  goto focus_entry;
3593  } else if (entry->expanded)
3594  collapse_entry(pane, child->left.cur);
3595  else {
3596  expand_entry(child, child->left.cur);
3597 
3598  if (!pane->treePane) focus_entry: {
3599  int idxstart = SendMessageW(child->left.hwnd, LB_GETCURSEL, 0, 0);
3600  int idx = SendMessageW(child->left.hwnd, LB_FINDSTRING, idxstart, (LPARAM)entry);
3601  SendMessageW(child->left.hwnd, LB_SETCURSEL, idx, 0);
3603  }
3604  }
3605 
3606  if (!scanned_old) {
3607  calc_widths(pane, FALSE);
3608 
3609  set_header(pane);
3610  }
3611  } else {
3612  if (GetKeyState(VK_MENU) < 0)
3614  else
3616  }
3617 }
3618 
3619 
3621 {
3622  switch(cmd) {
3623  case ID_VIEW_NAME:
3624  if (pane->visible_cols) {
3625  pane->visible_cols = 0;
3626  calc_widths(pane, TRUE);
3627  set_header(pane);
3628  InvalidateRect(pane->hwnd, 0, TRUE);
3631  }
3632  break;
3633 
3635  if (pane->visible_cols != COL_ALL) {
3636  pane->visible_cols = COL_ALL;
3637  calc_widths(pane, TRUE);
3638  set_header(pane);
3639  InvalidateRect(pane->hwnd, 0, TRUE);
3642  }
3643  break;
3644 
3645  case ID_PREFERRED_SIZES: {
3646  calc_widths(pane, TRUE);
3647  set_header(pane);
3648  InvalidateRect(pane->hwnd, 0, TRUE);
3649  break;}
3650 
3651  /* TODO: more command ids... */
3652 
3653  default:
3654  return FALSE;
3655  }
3656 
3657  return TRUE;
3658 }
3659 
3660 
3661 static void set_sort_order(ChildWnd* child, SORT_ORDER sortOrder)
3662 {
3663  if (child->sortOrder != sortOrder) {
3664  child->sortOrder = sortOrder;
3666  }
3667 }
3668 
3670 {
3675 }
3676 
3677 
3679 {
3680  /*TODO correctly handle UNIX paths */
3681  DWORD target_attr = GetFileAttributesW(target);
3682 
3683  if (target_attr == INVALID_FILE_ATTRIBUTES)
3684  return FALSE;
3685 
3686  return (target_attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
3687 }
3688 
3690 {
3691  WCHAR path[MAX_PATH];
3692  int len;
3693 
3694  get_path(pane->cur, path);
3695 
3697  return FALSE;
3698 
3699  get_path(pane->cur, source);
3700 
3701  /* convert relative targets to absolute paths */
3702  if (path[0]!='/' && path[1]!=':') {
3703  get_path(pane->cur->up, target);
3704  len = lstrlenW(target);
3705 
3706  if (target[len-1]!='\\' && target[len-1]!='/')
3707  target[len++] = '/';
3708 
3709  lstrcpyW(target+len, path);
3710  } else
3711  lstrcpyW(target, path);
3712 
3713  /* If the target already exists as directory, create a new target below this. */
3714  if (is_directory(path)) {
3715  WCHAR fname[_MAX_FNAME], ext[_MAX_EXT];
3716  static const WCHAR sAppend[] = {'%','s','/','%','s','%','s','\0'};
3717 
3718  _wsplitpath(source, NULL, NULL, fname, ext);
3719 
3720  wsprintfW(target, sAppend, path, fname, ext);
3721  }
3722 
3723  return TRUE;
3724 }
3725 
3726 
3729 
3730 static void CtxMenu_reset(void)
3731 {
3732  s_pctxmenu2 = NULL;
3733  s_pctxmenu3 = NULL;
3734 }
3735 
3737 {
3738  IContextMenu* pcm = NULL;
3739 
3740  CtxMenu_reset();
3741 
3742  if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu3, (void**)&pcm) == NOERROR)
3743  s_pctxmenu3 = (LPCONTEXTMENU3)pcm;
3744  else if (IContextMenu_QueryInterface(pcm1, &IID_IContextMenu2, (void**)&pcm) == NOERROR)
3745  s_pctxmenu2 = (LPCONTEXTMENU2)pcm;
3746 
3747  if (pcm) {
3748  IContextMenu_Release(pcm1);
3749  return pcm;
3750  } else
3751  return pcm1;
3752 }
3753 
3755 {
3756  if (s_pctxmenu3) {
3757  if (SUCCEEDED(IContextMenu3_HandleMenuMsg(s_pctxmenu3, nmsg, wparam, lparam)))
3758  return TRUE;
3759  }
3760 
3761  if (s_pctxmenu2)
3762  if (SUCCEEDED(IContextMenu2_HandleMenuMsg(s_pctxmenu2, nmsg, wparam, lparam)))
3763  return TRUE;
3764 
3765  return FALSE;
3766 }
3767 
3768 static HRESULT ShellFolderContextMenu(IShellFolder* shell_folder, HWND hwndParent, int cidl, LPCITEMIDLIST* apidl, int x, int y)
3769 {
3770  IContextMenu* pcm;
3771  BOOL executed = FALSE;
3772 
3773  HRESULT hr = IShellFolder_GetUIObjectOf(shell_folder, hwndParent, cidl, apidl, &IID_IContextMenu, NULL, (LPVOID*)&pcm);
3774 
3775  if (SUCCEEDED(hr)) {
3777 
3778  pcm = CtxMenu_query_interfaces(pcm);
3779 
3780  if (hmenu) {
3781  hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, CMF_NORMAL);
3782 
3783  if (SUCCEEDED(hr)) {
3785 
3786  CtxMenu_reset();
3787 
3788  if (idCmd) {
3789  CMINVOKECOMMANDINFO cmi;
3790 
3791  cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
3792  cmi.fMask = 0;
3793  cmi.hwnd = hwndParent;
3794  cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
3795  cmi.lpParameters = NULL;
3796  cmi.lpDirectory = NULL;
3797  cmi.nShow = SW_SHOWNORMAL;
3798  cmi.dwHotKey = 0;
3799  cmi.hIcon = 0;
3800 
3801  hr = IContextMenu_InvokeCommand(pcm, &cmi);
3802  executed = TRUE;
3803  }
3804  } else
3805  CtxMenu_reset();
3806  }
3807 
3808  IContextMenu_Release(pcm);
3809  }
3810 
3811  return FAILED(hr)? hr: executed? S_OK: S_FALSE;
3812 }
3813 
3815 {
3817  ASSERT(child);
3818 
3819  switch(nmsg) {
3820  case WM_DRAWITEM: {
3822  Entry* entry = (Entry*) dis->itemData;
3823 
3824  if (dis->CtlID == IDW_TREE_LEFT)
3825  draw_item(&child->left, dis, entry, -1);
3826  else if (dis->CtlID == IDW_TREE_RIGHT)
3827  draw_item(&child->right, dis, entry, -1);
3828  else
3829  goto draw_menu_item;
3830 
3831  return TRUE;}
3832 
3833  case WM_CREATE:
3835  break;
3836 
3837  case WM_NCDESTROY:
3840  break;
3841 
3842  case WM_PAINT: {
3843  PAINTSTRUCT ps;
3844  HBRUSH lastBrush;
3845  RECT rt;
3846  GetClientRect(hwnd, &rt);
3847  BeginPaint(hwnd, &ps);
3848  rt.left = child->split_pos-SPLIT_WIDTH/2;
3849  rt.right = child->split_pos+SPLIT_WIDTH/2+1;
3850  lastBrush = SelectObject(ps.hdc, GetStockObject(COLOR_SPLITBAR));
3851  Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
3852  SelectObject(ps.hdc, lastBrush);
3853  EndPaint(hwnd, &ps);
3854  break;}
3855 
3856  case WM_SETCURSOR:
3857  if (LOWORD(lparam) == HTCLIENT) {
3858  POINT pt;
3859