1 // BrowseDialog.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifndef UNDER_CE
6 #include "../../../Windows/CommonDialog.h"
7 #include "../../../Windows/Shell.h"
8 #endif
9 
10 #include "../../../Windows/FileName.h"
11 #include "../../../Windows/FileFind.h"
12 
13 #ifdef UNDER_CE
14 #include <commdlg.h>
15 #endif
16 
17 #include "BrowseDialog.h"
18 
19 #define USE_MY_BROWSE_DIALOG
20 
21 #ifdef USE_MY_BROWSE_DIALOG
22 
23 #include "../../../Common/Defs.h"
24 #include "../../../Common/IntToString.h"
25 #include "../../../Common/Wildcard.h"
26 
27 #include "../../../Windows/FileDir.h"
28 #include "../../../Windows/PropVariantConv.h"
29 
30 #include "../../../Windows/Control/ComboBox.h"
31 #include "../../../Windows/Control/Dialog.h"
32 #include "../../../Windows/Control/Edit.h"
33 #include "../../../Windows/Control/ListView.h"
34 
35 #include "BrowseDialogRes.h"
36 #include "PropertyNameRes.h"
37 #include "SysIconUtils.h"
38 
39 #ifndef _SFX
40 #include "RegistryUtils.h"
41 #endif
42 
43 #endif
44 
45 #include "ComboDialog.h"
46 #include "LangUtils.h"
47 
48 #include "resource.h"
49 
50 using namespace NWindows;
51 using namespace NFile;
52 using namespace NName;
53 using namespace NFind;
54 
55 #ifdef USE_MY_BROWSE_DIALOG
56 
57 extern bool g_LVN_ITEMACTIVATE_Support;
58 
59 static const int kParentIndex = -1;
60 static const UINT k_Message_RefreshPathEdit = WM_APP + 1;
61 
GetNormalizedError()62 static HRESULT GetNormalizedError()
63 {
64   DWORD errorCode = GetLastError();
65   return errorCode == 0 ? E_FAIL : errorCode;
66 }
67 
68 extern UString HResultToMessage(HRESULT errorCode);
69 
MessageBox_Error_Global(HWND wnd,const wchar_t * message)70 static void MessageBox_Error_Global(HWND wnd, const wchar_t *message)
71 {
72   ::MessageBoxW(wnd, message, L"7-Zip", MB_ICONERROR);
73 }
74 
MessageBox_HResError(HWND wnd,HRESULT errorCode,const wchar_t * name)75 static void MessageBox_HResError(HWND wnd, HRESULT errorCode, const wchar_t *name)
76 {
77   UString s = HResultToMessage(errorCode);
78   if (name)
79   {
80     s += L'\n';
81     s += name;
82   }
83   MessageBox_Error_Global(wnd, s);
84 }
85 
86 class CBrowseDialog: public NControl::CModalDialog
87 {
88   NControl::CListView _list;
89   NControl::CEdit _pathEdit;
90   NControl::CComboBox _filterCombo;
91 
92   CObjectVector<CFileInfo> _files;
93 
94   CExtToIconMap _extToIconMap;
95   int _sortIndex;
96   bool _ascending;
97   bool _showDots;
98   UString _topDirPrefix; // we don't open parent of that folder
99   UString DirPrefix;
100 
101   virtual bool OnInit();
102   virtual bool OnSize(WPARAM wParam, int xSize, int ySize);
103   virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam);
104   virtual bool OnNotify(UINT controlID, LPNMHDR header);
105   virtual bool OnKeyDown(LPNMLVKEYDOWN keyDownInfo);
106   virtual bool OnButtonClicked(int buttonID, HWND buttonHWND);
107   virtual void OnOK();
108 
Post_RefreshPathEdit()109   void Post_RefreshPathEdit() { PostMessage(k_Message_RefreshPathEdit); }
110 
111   bool GetParentPath(const UString &path, UString &parentPrefix, UString &name);
112   // Reload changes DirPrefix. Don't send DirPrefix in pathPrefix parameter
113   HRESULT Reload(const UString &pathPrefix, const UString &selectedName);
114   HRESULT Reload();
115 
116   void OpenParentFolder();
117   void SetPathEditText();
118   void OnCreateDir();
119   void OnItemEnter();
120   void FinishOnOK();
121 
GetRealItemIndex(int indexInListView) const122   int GetRealItemIndex(int indexInListView) const
123   {
124     LPARAM param;
125     if (!_list.GetItemParam(indexInListView, param))
126       return (int)-1;
127     return (int)param;
128   }
129 
130 public:
131   bool FolderMode;
132   UString Title;
133   UString FilePath;  // input/ result path
134   bool ShowAllFiles;
135   UStringVector Filters;
136   UString FilterDescription;
137 
CBrowseDialog()138   CBrowseDialog(): FolderMode(false), _showDots(false), ShowAllFiles(true) {}
139   void SetFilter(const UString &s);
Create(HWND parent=0)140   INT_PTR Create(HWND parent = 0) { return CModalDialog::Create(IDD_BROWSE, parent); }
141   int CompareItems(LPARAM lParam1, LPARAM lParam2);
142 };
143 
SetFilter(const UString & s)144 void CBrowseDialog::SetFilter(const UString &s)
145 {
146   Filters.Clear();
147   UString mask;
148   unsigned i;
149   for (i = 0; i < s.Len(); i++)
150   {
151     wchar_t c = s[i];
152     if (c == ';')
153     {
154       if (!mask.IsEmpty())
155         Filters.Add(mask);
156       mask.Empty();
157     }
158     else
159       mask += c;
160   }
161   if (!mask.IsEmpty())
162     Filters.Add(mask);
163   ShowAllFiles = Filters.IsEmpty();
164   for (i = 0; i < Filters.Size(); i++)
165   {
166     const UString &f = Filters[i];
167     if (f == L"*.*" || f == L"*")
168     {
169       ShowAllFiles = true;
170       break;
171     }
172   }
173 }
174 
OnInit()175 bool CBrowseDialog::OnInit()
176 {
177   #ifdef LANG
178   LangSetDlgItems(*this, NULL, 0);
179   #endif
180   if (!Title.IsEmpty())
181     SetText(Title);
182   _list.Attach(GetItem(IDL_BROWSE));
183   _filterCombo.Attach(GetItem(IDC_BROWSE_FILTER));
184   _pathEdit.Attach(GetItem(IDE_BROWSE_PATH));
185 
186   if (FolderMode)
187     HideItem(IDC_BROWSE_FILTER);
188   else
189     EnableItem(IDC_BROWSE_FILTER, false);
190 
191   #ifndef UNDER_CE
192   _list.SetUnicodeFormat();
193   #endif
194 
195   #ifndef _SFX
196   if (ReadSingleClick())
197     _list.SetExtendedListViewStyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_TRACKSELECT);
198   _showDots = ReadShowDots();
199   #endif
200 
201   {
202     UString s;
203     if (!FilterDescription.IsEmpty())
204       s = FilterDescription;
205     else if (ShowAllFiles)
206       s = L"*.*";
207     else
208     {
209       FOR_VECTOR (i, Filters)
210       {
211         if (i != 0)
212           s += L' ';
213         s += Filters[i];
214       }
215     }
216     _filterCombo.AddString(s);
217     _filterCombo.SetCurSel(0);
218   }
219 
220   _list.SetImageList(GetSysImageList(true), LVSIL_SMALL);
221   _list.SetImageList(GetSysImageList(false), LVSIL_NORMAL);
222 
223   _list.InsertColumn(0, LangString(IDS_PROP_NAME), 100);
224   _list.InsertColumn(1, LangString(IDS_PROP_MTIME), 100);
225   {
226     LV_COLUMNW column;
227     column.iSubItem = 2;
228     column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
229     column.fmt = LVCFMT_RIGHT;
230     column.cx = 100;
231     const UString s = LangString(IDS_PROP_SIZE);
232     column.pszText = (wchar_t *)(const wchar_t *)s;
233     _list.InsertColumn(2, &column);
234   }
235 
236   _list.InsertItem(0, L"12345678901234567"
237       #ifndef UNDER_CE
238       L"1234567890"
239       #endif
240       );
241   _list.SetSubItem(0, 1, L"2009-09-09"
242       #ifndef UNDER_CE
243       L" 09:09"
244       #endif
245       );
246   _list.SetSubItem(0, 2, L"9999 MB");
247   for (int i = 0; i < 3; i++)
248     _list.SetColumnWidthAuto(i);
249   _list.DeleteAllItems();
250 
251   _ascending = true;
252   _sortIndex = 0;
253 
254   NormalizeSize();
255 
256   _topDirPrefix.Empty();
257   {
258     int rootSize = GetRootPrefixSize(FilePath);
259     // We can go up from root folder to drives list
260     if (NName::IsDrivePath(FilePath))
261       rootSize = 0;
262     else if (IsSuperPath(FilePath))
263     {
264       if (NName::IsDrivePath(&FilePath[kSuperPathPrefixSize]))
265         rootSize = kSuperPathPrefixSize;
266     }
267     _topDirPrefix.SetFrom(FilePath, rootSize);
268   }
269 
270   UString name;
271   if (!GetParentPath(FilePath, DirPrefix, name))
272     DirPrefix = _topDirPrefix;
273 
274   for(;;)
275   {
276     UString baseFolder = DirPrefix;
277     if (Reload(baseFolder, name) == S_OK)
278       break;
279     name.Empty();
280     if (DirPrefix.IsEmpty())
281       break;
282     UString parent, name2;
283     GetParentPath(DirPrefix, parent, name2);
284     DirPrefix = parent;
285   }
286 
287   if (name.IsEmpty())
288     name = FilePath;
289   if (FolderMode)
290     NormalizeDirPathPrefix(name);
291   _pathEdit.SetText(name);
292 
293   #ifndef UNDER_CE
294   /* If we clear UISF_HIDEFOCUS, the focus rectangle in ListView will be visible,
295      even if we use mouse for pressing the button to open this dialog. */
296   PostMessage(MY__WM_UPDATEUISTATE, MAKEWPARAM(MY__UIS_CLEAR, MY__UISF_HIDEFOCUS));
297   #endif
298 
299   return CModalDialog::OnInit();
300 }
301 
OnSize(WPARAM,int xSize,int ySize)302 bool CBrowseDialog::OnSize(WPARAM /* wParam */, int xSize, int ySize)
303 {
304   int mx, my;
305   {
306     RECT r;
307     GetClientRectOfItem(IDB_BROWSE_PARENT, r);
308     mx = r.left;
309     my = r.top;
310   }
311   InvalidateRect(NULL);
312 
313   int xLim = xSize - mx;
314   {
315     RECT r;
316     GetClientRectOfItem(IDT_BROWSE_FOLDER, r);
317     MoveItem(IDT_BROWSE_FOLDER, r.left, r.top, xLim - r.left, RECT_SIZE_Y(r));
318   }
319 
320   int bx1, bx2, by;
321   GetItemSizes(IDCANCEL, bx1, by);
322   GetItemSizes(IDOK, bx2, by);
323   int y = ySize - my - by;
324   int x = xLim - bx1;
325   MoveItem(IDCANCEL, x, y, bx1, by);
326   MoveItem(IDOK, x - mx - bx2, y, bx2, by);
327 
328   // Y_Size of ComboBox is tricky. So we use Y_Size of _pathEdit instead
329 
330   int yPathSize;
331   {
332     RECT r;
333     GetClientRectOfItem(IDE_BROWSE_PATH, r);
334     yPathSize = RECT_SIZE_Y(r);
335     _pathEdit.Move(r.left, y - my - yPathSize - my - yPathSize, xLim - r.left, yPathSize);
336   }
337 
338   {
339     RECT r;
340     GetClientRectOfItem(IDC_BROWSE_FILTER, r);
341     _filterCombo.Move(r.left, y - my - yPathSize, xLim - r.left, RECT_SIZE_Y(r));
342   }
343 
344   {
345     RECT r;
346     GetClientRectOfItem(IDL_BROWSE, r);
347     _list.Move(r.left, r.top, xLim - r.left, y - my - yPathSize - my - yPathSize - my - r.top);
348   }
349 
350   return false;
351 }
352 
OnMessage(UINT message,WPARAM wParam,LPARAM lParam)353 bool CBrowseDialog::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
354 {
355   if (message == k_Message_RefreshPathEdit)
356   {
357     SetPathEditText();
358     return true;
359   }
360   return CModalDialog::OnMessage(message, wParam, lParam);
361 }
362 
OnNotify(UINT,LPNMHDR header)363 bool CBrowseDialog::OnNotify(UINT /* controlID */, LPNMHDR header)
364 {
365   if (header->hwndFrom != _list)
366     return false;
367   switch (header->code)
368   {
369     case LVN_ITEMACTIVATE:
370       if (g_LVN_ITEMACTIVATE_Support)
371         OnItemEnter();
372       break;
373     case NM_DBLCLK:
374     case NM_RETURN: // probabably it's unused
375       if (!g_LVN_ITEMACTIVATE_Support)
376         OnItemEnter();
377       break;
378     case LVN_COLUMNCLICK:
379     {
380       int index = LPNMLISTVIEW(header)->iSubItem;
381       if (index == _sortIndex)
382         _ascending = !_ascending;
383       else
384       {
385         _ascending = (index == 0);
386         _sortIndex = index;
387       }
388       Reload();
389       return false;
390     }
391     case LVN_KEYDOWN:
392     {
393       bool boolResult = OnKeyDown(LPNMLVKEYDOWN(header));
394       Post_RefreshPathEdit();
395       return boolResult;
396     }
397     case NM_RCLICK:
398     case NM_CLICK:
399     case LVN_BEGINDRAG:
400       Post_RefreshPathEdit();
401       break;
402   }
403   return false;
404 }
405 
OnKeyDown(LPNMLVKEYDOWN keyDownInfo)406 bool CBrowseDialog::OnKeyDown(LPNMLVKEYDOWN keyDownInfo)
407 {
408   bool ctrl = IsKeyDown(VK_CONTROL);
409 
410   switch (keyDownInfo->wVKey)
411   {
412     case VK_BACK:
413       OpenParentFolder();
414       return true;
415     case 'R':
416       if (ctrl)
417       {
418         Reload();
419         return true;
420       }
421       return false;
422     case VK_F7:
423       OnCreateDir();
424       return true;
425   }
426   return false;
427 }
428 
OnButtonClicked(int buttonID,HWND buttonHWND)429 bool CBrowseDialog::OnButtonClicked(int buttonID, HWND buttonHWND)
430 {
431   switch (buttonID)
432   {
433     case IDB_BROWSE_PARENT: OpenParentFolder(); break;
434     case IDB_BROWSE_CREATE_DIR: OnCreateDir(); break;
435     default: return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
436   }
437   _list.SetFocus();
438   return true;
439 }
440 
OnOK()441 void CBrowseDialog::OnOK()
442 {
443   /* When we press "Enter" in listview, Windows sends message to first Button.
444      We check that message was from ListView; */
445   if (GetFocus() == _list)
446   {
447     OnItemEnter();
448     return;
449   }
450   FinishOnOK();
451 }
452 
453 
GetParentPath(const UString & path,UString & parentPrefix,UString & name)454 bool CBrowseDialog::GetParentPath(const UString &path, UString &parentPrefix, UString &name)
455 {
456   parentPrefix.Empty();
457   name.Empty();
458   if (path.IsEmpty())
459     return false;
460   if (_topDirPrefix == path)
461     return false;
462   UString s = path;
463   if (s.Back() == WCHAR_PATH_SEPARATOR)
464     s.DeleteBack();
465   if (s.IsEmpty())
466     return false;
467   if (s.Back() == WCHAR_PATH_SEPARATOR)
468     return false;
469   int pos = s.ReverseFind(WCHAR_PATH_SEPARATOR);
470   parentPrefix.SetFrom(s, pos + 1);
471   name = s.Ptr(pos + 1);
472   return true;
473 }
474 
CompareItems(LPARAM lParam1,LPARAM lParam2)475 int CBrowseDialog::CompareItems(LPARAM lParam1, LPARAM lParam2)
476 {
477   if (lParam1 == kParentIndex) return -1;
478   if (lParam2 == kParentIndex) return 1;
479   const CFileInfo &f1 = _files[(int)lParam1];
480   const CFileInfo &f2 = _files[(int)lParam2];
481 
482   bool isDir1 = f1.IsDir();
483   bool isDir2 = f2.IsDir();
484   if (isDir1 && !isDir2) return -1;
485   if (isDir2 && !isDir1) return 1;
486 
487   int res = 0;
488   switch (_sortIndex)
489   {
490     case 0: res = CompareFileNames(fs2us(f1.Name), fs2us(f2.Name)); break;
491     case 1: res = CompareFileTime(&f1.MTime, &f2.MTime); break;
492     case 2: res = MyCompare(f1.Size, f2.Size); break;
493   }
494   return _ascending ? res: -res;
495 }
496 
CompareItems2(LPARAM lParam1,LPARAM lParam2,LPARAM lpData)497 static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
498 {
499   return ((CBrowseDialog *)lpData)->CompareItems(lParam1, lParam2);
500 }
501 
ConvertSizeToString(UInt64 v,wchar_t * s)502 static void ConvertSizeToString(UInt64 v, wchar_t *s)
503 {
504   Byte c = 0;
505        if (v >= ((UInt64)10000 << 20)) { v >>= 30; c = 'G'; }
506   else if (v >= ((UInt64)10000 << 10)) { v >>= 20; c = 'M'; }
507   else if (v >= ((UInt64)10000 <<  0)) { v >>= 10; c = 'K'; }
508   ConvertUInt64ToString(v, s);
509   if (c != 0)
510   {
511     s += MyStringLen(s);
512     *s++ = ' ';
513     *s++ = c;
514     *s++ = 0;
515   }
516 }
517 
518 // Reload changes DirPrefix. Don't send DirPrefix in pathPrefix parameter
519 
Reload(const UString & pathPrefix,const UString & selectedName)520 HRESULT CBrowseDialog::Reload(const UString &pathPrefix, const UString &selectedName)
521 {
522   CObjectVector<CFileInfo> files;
523 
524   #ifndef UNDER_CE
525   bool isDrive = false;
526   if (pathPrefix.IsEmpty() || pathPrefix == kSuperPathPrefix)
527   {
528     isDrive = true;
529     FStringVector drives;
530     if (!MyGetLogicalDriveStrings(drives))
531       return GetNormalizedError();
532     FOR_VECTOR (i, drives)
533     {
534       FString d = drives[i];
535       if (d.Len() < 3 || d.Back() != '\\')
536         return E_FAIL;
537       d.DeleteBack();
538       CFileInfo &fi = files.AddNew();
539       fi.SetAsDir();
540       fi.Name = d;
541     }
542   }
543   else
544   #endif
545   {
546     CEnumerator enumerator(us2fs(pathPrefix + L'*'));
547     for (;;)
548     {
549       bool found;
550       CFileInfo fi;
551       if (!enumerator.Next(fi, found))
552         return GetNormalizedError();
553       if (!found)
554         break;
555       if (!fi.IsDir())
556       {
557         if (FolderMode)
558           continue;
559         if (!ShowAllFiles)
560         {
561           unsigned i;
562           for (i = 0; i < Filters.Size(); i++)
563             if (DoesWildcardMatchName(Filters[i], fs2us(fi.Name)))
564               break;
565           if (i == Filters.Size())
566             continue;
567         }
568       }
569       files.Add(fi);
570     }
571   }
572 
573   DirPrefix = pathPrefix;
574 
575   _files = files;
576 
577   SetItemText(IDT_BROWSE_FOLDER, DirPrefix);
578 
579   _list.SetRedraw(false);
580   _list.DeleteAllItems();
581 
582   LVITEMW item;
583 
584   int index = 0;
585   int cursorIndex = -1;
586 
587   #ifndef _SFX
588   if (_showDots && _topDirPrefix != DirPrefix)
589   {
590     item.iItem = index;
591     const UString itemName = L"..";
592     if (selectedName.IsEmpty())
593       cursorIndex = index;
594     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
595     int subItem = 0;
596     item.iSubItem = subItem++;
597     item.lParam = kParentIndex;
598     item.pszText = (wchar_t *)(const wchar_t *)itemName;
599     item.iImage = _extToIconMap.GetIconIndex(FILE_ATTRIBUTE_DIRECTORY, DirPrefix);
600     if (item.iImage < 0)
601       item.iImage = 0;
602     _list.InsertItem(&item);
603     _list.SetSubItem(index, subItem++, L"");
604     _list.SetSubItem(index, subItem++, L"");
605     index++;
606   }
607   #endif
608 
609   for (unsigned i = 0; i < _files.Size(); i++, index++)
610   {
611     item.iItem = index;
612     const CFileInfo &fi = _files[i];
613     const UString name = fs2us(fi.Name);
614     if (!selectedName.IsEmpty() && CompareFileNames(name, selectedName) == 0)
615       cursorIndex = index;
616     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
617     int subItem = 0;
618     item.iSubItem = subItem++;
619     item.lParam = i;
620     item.pszText = (wchar_t *)(const wchar_t *)name;
621 
622     const UString fullPath = DirPrefix + name;
623     #ifndef UNDER_CE
624     if (isDrive)
625     {
626       if (GetRealIconIndex(fi.Name + FCHAR_PATH_SEPARATOR, FILE_ATTRIBUTE_DIRECTORY, item.iImage) == 0)
627         item.iImage = 0;
628     }
629     else
630     #endif
631       item.iImage = _extToIconMap.GetIconIndex(fi.Attrib, fullPath);
632     if (item.iImage < 0)
633       item.iImage = 0;
634     _list.InsertItem(&item);
635     wchar_t s[32];
636     {
637       FILETIME ft;
638       s[0] = 0;
639       if (FileTimeToLocalFileTime(&fi.MTime, &ft))
640         ConvertFileTimeToString(ft, s,
641             #ifndef UNDER_CE
642               true
643             #else
644               false
645             #endif
646             , false);
647       _list.SetSubItem(index, subItem++, s);
648     }
649     {
650       s[0] = 0;
651       if (!fi.IsDir())
652         ConvertSizeToString(fi.Size, s);
653       _list.SetSubItem(index, subItem++, s);
654     }
655   }
656 
657   if (_list.GetItemCount() > 0 && cursorIndex >= 0)
658     _list.SetItemState_FocusedSelected(cursorIndex);
659   _list.SortItems(CompareItems2, (LPARAM)this);
660   if (_list.GetItemCount() > 0 && cursorIndex < 0)
661     _list.SetItemState(0, LVIS_FOCUSED, LVIS_FOCUSED);
662   _list.EnsureVisible(_list.GetFocusedItem(), false);
663   _list.SetRedraw(true);
664   _list.InvalidateRect(NULL, true);
665   return S_OK;
666 }
667 
Reload()668 HRESULT CBrowseDialog::Reload()
669 {
670   UString selected;
671   int index = _list.GetNextSelectedItem(-1);
672   if (index >= 0)
673   {
674     int fileIndex = GetRealItemIndex(index);
675     if (fileIndex != kParentIndex)
676       selected = fs2us(_files[fileIndex].Name);
677   }
678   UString dirPathTemp = DirPrefix;
679   return Reload(dirPathTemp, selected);
680 }
681 
OpenParentFolder()682 void CBrowseDialog::OpenParentFolder()
683 {
684   UString parent, selected;
685   if (GetParentPath(DirPrefix, parent, selected))
686   {
687     Reload(parent, selected);
688     SetPathEditText();
689   }
690 }
691 
SetPathEditText()692 void CBrowseDialog::SetPathEditText()
693 {
694   int index = _list.GetNextSelectedItem(-1);
695   if (index < 0)
696   {
697     if (FolderMode)
698       _pathEdit.SetText(DirPrefix);
699     return;
700   }
701   int fileIndex = GetRealItemIndex(index);
702   if (fileIndex == kParentIndex)
703   {
704     if (FolderMode)
705       _pathEdit.SetText(L".." WSTRING_PATH_SEPARATOR);
706     return;
707   }
708   const CFileInfo &file = _files[fileIndex];
709   if (file.IsDir())
710   {
711     if (!FolderMode)
712       return;
713     _pathEdit.SetText(fs2us(file.Name) + WCHAR_PATH_SEPARATOR);
714   }
715   else
716     _pathEdit.SetText(fs2us(file.Name));
717 }
718 
OnCreateDir()719 void CBrowseDialog::OnCreateDir()
720 {
721   UString name;
722   {
723     UString enteredName;
724     Dlg_CreateFolder((HWND)*this, enteredName);
725     if (enteredName.IsEmpty())
726       return;
727     if (!CorrectFsPath(DirPrefix, enteredName, name))
728     {
729       MessageBox_HResError((HWND)*this, ERROR_INVALID_NAME, name);
730       return;
731     }
732   }
733   if (name.IsEmpty())
734     return;
735 
736   FString destPath;
737   if (GetFullPath(us2fs(DirPrefix), us2fs(name), destPath))
738   {
739     if (!NDir::CreateComplexDir(destPath))
740     {
741       MessageBox_HResError((HWND)*this, GetNormalizedError(), fs2us(destPath));
742     }
743     else
744     {
745       UString tempPath = DirPrefix;
746       Reload(tempPath, name);
747       SetPathEditText();
748     }
749     _list.SetFocus();
750   }
751 }
752 
OnItemEnter()753 void CBrowseDialog::OnItemEnter()
754 {
755   int index = _list.GetNextSelectedItem(-1);
756   if (index < 0)
757     return;
758   int fileIndex = GetRealItemIndex(index);
759   if (fileIndex == kParentIndex)
760     OpenParentFolder();
761   else
762   {
763     const CFileInfo &file = _files[fileIndex];
764     if (!file.IsDir())
765     {
766       if (!FolderMode)
767         FinishOnOK();
768       /*
769       MessageBox_Error_Global(*this, FolderMode ?
770             L"You must select some folder":
771             L"You must select some file");
772       */
773       return;
774     }
775     UString s = DirPrefix + fs2us(file.Name) + WCHAR_PATH_SEPARATOR;
776     HRESULT res = Reload(s, L"");
777     if (res != S_OK)
778       MessageBox_HResError(*this, res, s);
779     SetPathEditText();
780   }
781 }
782 
FinishOnOK()783 void CBrowseDialog::FinishOnOK()
784 {
785   UString s;
786   _pathEdit.GetText(s);
787   FString destPath;
788   if (!GetFullPath(us2fs(DirPrefix), us2fs(s), destPath))
789   {
790     MessageBox_HResError((HWND)*this, ERROR_INVALID_NAME, s);
791     return;
792   }
793   FilePath = fs2us(destPath);
794   if (FolderMode)
795     NormalizeDirPathPrefix(FilePath);
796   End(IDOK);
797 }
798 
799 #endif
800 
MyBrowseForFolder(HWND owner,LPCWSTR title,LPCWSTR path,UString & resultPath)801 bool MyBrowseForFolder(HWND owner, LPCWSTR title, LPCWSTR path, UString &resultPath)
802 {
803   resultPath.Empty();
804 
805   #ifndef UNDER_CE
806 
807   #ifdef USE_MY_BROWSE_DIALOG
808   if (!IsSuperOrDevicePath(path))
809   #endif
810     return NShell::BrowseForFolder(owner, title, path, resultPath);
811 
812   #endif
813 
814   #ifdef USE_MY_BROWSE_DIALOG
815 
816   CBrowseDialog dialog;
817   dialog.FolderMode = true;
818   if (title)
819     dialog.Title = title;
820   if (path)
821     dialog.FilePath = path;
822   if (dialog.Create(owner) != IDOK)
823     return false;
824   resultPath = dialog.FilePath;
825   #endif
826 
827   return true;
828 }
829 
MyBrowseForFile(HWND owner,LPCWSTR title,LPCWSTR path,LPCWSTR filterDescription,LPCWSTR filter,UString & resultPath)830 bool MyBrowseForFile(HWND owner, LPCWSTR title, LPCWSTR path,
831     LPCWSTR filterDescription, LPCWSTR filter, UString &resultPath)
832 {
833   resultPath.Empty();
834 
835   #ifndef UNDER_CE
836 
837   #ifdef USE_MY_BROWSE_DIALOG
838   if (!IsSuperOrDevicePath(path))
839   #endif
840   {
841     if (MyGetOpenFileName(owner, title, NULL, path, filterDescription, filter, resultPath))
842       return true;
843     #ifdef UNDER_CE
844     return false;
845     #else
846     // maybe we must use GetLastError in WinCE.
847     DWORD errorCode = CommDlgExtendedError();
848     const wchar_t *errorMessage = NULL;
849     switch (errorCode)
850     {
851       case 0: return false; // cancel or close obn dialog
852       case FNERR_INVALIDFILENAME: errorMessage = L"Invalid File Name"; break;
853       default: errorMessage = L"Open Dialog Error";
854     }
855     if (!errorMessage)
856       return false;
857     {
858       UString s = errorMessage;
859       s += L"\n";
860       s += path;
861       MessageBox_Error_Global(owner, s);
862     }
863     #endif
864   }
865 
866   #endif
867 
868   #ifdef USE_MY_BROWSE_DIALOG
869   CBrowseDialog dialog;
870   if (title)
871     dialog.Title = title;
872   if (path)
873     dialog.FilePath = path;
874   dialog.FolderMode = false;
875   if (filter)
876     dialog.SetFilter(filter);
877   if (filterDescription)
878     dialog.FilterDescription = filterDescription;
879   if (dialog.Create(owner) != IDOK)
880     return false;
881   resultPath = dialog.FilePath;
882   #endif
883 
884   return true;
885 }
886 
887 
888 #ifdef _WIN32
889 
RemoveDotsAndSpaces(UString & path)890 static void RemoveDotsAndSpaces(UString &path)
891 {
892   while (!path.IsEmpty())
893   {
894     wchar_t c = path.Back();
895     if (c != ' ' && c != '.')
896       return;
897     path.DeleteBack();
898   }
899 }
900 
901 
CorrectFsPath(const UString & relBase,const UString & path2,UString & result)902 bool CorrectFsPath(const UString &relBase, const UString &path2, UString &result)
903 {
904   result.Empty();
905 
906   UString path = path2;
907   path.Replace('/', WCHAR_PATH_SEPARATOR);
908   unsigned start = 0;
909   UString base;
910   if (NName::IsAbsolutePath(path))
911   {
912     if (IsSuperOrDevicePath(path))
913     {
914       result = path;
915       return true;
916     }
917     int pos = GetRootPrefixSize(path);
918     if (pos > 0)
919       start = pos;
920   }
921   else
922   {
923     if (IsSuperOrDevicePath(relBase))
924     {
925       result = path;
926       return true;
927     }
928     base = relBase;
929   }
930 
931   /* We can't use backward, since we must change only disk paths */
932   /*
933   for (;;)
934   {
935     if (path.Len() <= start)
936       break;
937     if (DoesFileOrDirExist(us2fs(path)))
938       break;
939     if (path.Back() == WCHAR_PATH_SEPARATOR)
940     {
941       path.DeleteBack();
942       result.Insert(0, WCHAR_PATH_SEPARATOR);;
943     }
944     int pos = path.ReverseFind(WCHAR_PATH_SEPARATOR) + 1;
945     UString cur = path.Ptr(pos);
946     RemoveDotsAndSpaces(cur);
947     result.Insert(0, cur);
948     path.DeleteFrom(pos);
949   }
950   result.Insert(0, path);
951   return true;
952   */
953 
954   result += path.Left(start);
955   bool checkExist = true;
956   UString cur;
957   for (;;)
958   {
959     if (start == path.Len())
960       break;
961     int slashPos = path.Find(WCHAR_PATH_SEPARATOR, start);
962     cur.SetFrom(path.Ptr(start), (slashPos < 0 ? path.Len() : slashPos) - start);
963     if (checkExist)
964     {
965       CFileInfo fi;
966       if (fi.Find(us2fs(base + result + cur)))
967       {
968         if (!fi.IsDir())
969         {
970           result = path;
971           break;
972         }
973       }
974       else
975         checkExist = false;
976     }
977     if (!checkExist)
978       RemoveDotsAndSpaces(cur);
979     result += cur;
980     if (slashPos < 0)
981       break;
982     result += WCHAR_PATH_SEPARATOR;
983     start = slashPos + 1;
984   }
985 
986   return true;
987 }
988 
989 #else
CorrectFsPath(const UString &,const UString & path,UString & result)990 bool CorrectFsPath(const UString & /* relBase */, const UString &path, UString &result)
991 {
992   result = path;
993   return true;
994 }
995 #endif
996 
Dlg_CreateFolder(HWND wnd,UString & destName)997 bool Dlg_CreateFolder(HWND wnd, UString &destName)
998 {
999   destName.Empty();
1000   CComboDialog dlg;
1001   LangString(IDS_CREATE_FOLDER, dlg.Title);
1002   LangString(IDS_CREATE_FOLDER_NAME, dlg.Static);
1003   LangString(IDS_CREATE_FOLDER_DEFAULT_NAME, dlg.Value);
1004   if (dlg.Create(wnd) != IDOK)
1005     return false;
1006   destName = dlg.Value;
1007   return true;
1008 }
1009