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