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