• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(GetSysImageList(true), LVSIL_SMALL);
212   _list.SetImageList(GetSysImageList(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       if (GetRealIconIndex(fi.Name + FCHAR_PATH_SEPARATOR, FILE_ATTRIBUTE_DIRECTORY, item.iImage) == 0)
694         item.iImage = 0;
695     }
696     else
697     #endif
698       item.iImage = _extToIconMap.GetIconIndex(fi.Attrib, fullPath);
699     if (item.iImage < 0)
700       item.iImage = 0;
701     _list.InsertItem(&item);
702     wchar_t s[64];
703     {
704       s[0] = 0;
705       ConvertUtcFileTimeToString(fi.MTime, s,
706             #ifndef UNDER_CE
707               kTimestampPrintLevel_MIN
708             #else
709               kTimestampPrintLevel_DAY
710             #endif
711               );
712       _list.SetSubItem(index, subItem++, s);
713     }
714     {
715       s[0] = 0;
716       if (!fi.IsDir())
717         Browse_ConvertSizeToString(fi.Size, s);
718       _list.SetSubItem(index, subItem++, s);
719     }
720   }
721 
722   if (_list.GetItemCount() > 0 && cursorIndex >= 0)
723     _list.SetItemState_FocusedSelected(cursorIndex);
724   _list.SortItems(CompareItems2, (LPARAM)this);
725   if (_list.GetItemCount() > 0 && cursorIndex < 0)
726     _list.SetItemState(0, LVIS_FOCUSED, LVIS_FOCUSED);
727   _list.EnsureVisible(_list.GetFocusedItem(), false);
728   _list.SetRedraw(true);
729   _list.InvalidateRect(NULL, true);
730   return S_OK;
731 }
732 
Reload()733 HRESULT CBrowseDialog::Reload()
734 {
735   UString selected;
736   const int index = _list.GetNextSelectedItem(-1);
737   if (index >= 0)
738   {
739     const int fileIndex = GetRealItemIndex(index);
740     if (fileIndex != kParentIndex)
741       selected = fs2us(_files[fileIndex].Name);
742   }
743   const UString dirPathTemp = DirPrefix;
744   return Reload(dirPathTemp, selected);
745 }
746 
OpenParentFolder()747 void CBrowseDialog::OpenParentFolder()
748 {
749   UString parent, selected;
750   if (GetParentPath(DirPrefix, parent, selected))
751   {
752     Reload(parent, selected);
753     SetPathEditText();
754   }
755 }
756 
SetPathEditText()757 void CBrowseDialog::SetPathEditText()
758 {
759   const int index = _list.GetNextSelectedItem(-1);
760   if (index < 0)
761   {
762     if (FolderMode)
763       _pathEdit.SetText(DirPrefix);
764     return;
765   }
766   const int fileIndex = GetRealItemIndex(index);
767   if (fileIndex == kParentIndex)
768   {
769     if (FolderMode)
770       _pathEdit.SetText(L".." WSTRING_PATH_SEPARATOR);
771     return;
772   }
773   const CFileInfo &file = _files[fileIndex];
774   if (file.IsDir())
775   {
776     if (!FolderMode)
777       return;
778     _pathEdit.SetText(fs2us(file.Name) + WCHAR_PATH_SEPARATOR);
779   }
780   else
781     _pathEdit.SetText(fs2us(file.Name));
782 }
783 
OnCreateDir()784 void CBrowseDialog::OnCreateDir()
785 {
786   UString name;
787   {
788     UString enteredName;
789     Dlg_CreateFolder((HWND)*this, enteredName);
790     if (enteredName.IsEmpty())
791       return;
792     if (!CorrectFsPath(DirPrefix, enteredName, name))
793     {
794       MessageBox_HResError((HWND)*this, ERROR_INVALID_NAME, name);
795       return;
796     }
797   }
798   if (name.IsEmpty())
799     return;
800 
801   FString destPath;
802   if (GetFullPath(us2fs(DirPrefix), us2fs(name), destPath))
803   {
804     if (!NDir::CreateComplexDir(destPath))
805     {
806       MessageBox_HResError((HWND)*this, GetLastError_noZero_HRESULT(), fs2us(destPath));
807     }
808     else
809     {
810       UString tempPath = DirPrefix;
811       Reload(tempPath, name);
812       SetPathEditText();
813     }
814     _list.SetFocus();
815   }
816 }
817 
OnItemEnter()818 void CBrowseDialog::OnItemEnter()
819 {
820   const int index = _list.GetNextSelectedItem(-1);
821   if (index < 0)
822     return;
823   const int fileIndex = GetRealItemIndex(index);
824   if (fileIndex == kParentIndex)
825     OpenParentFolder();
826   else
827   {
828     const CFileInfo &file = _files[fileIndex];
829     if (!file.IsDir())
830     {
831       if (!FolderMode)
832         FinishOnOK();
833       /*
834       MessageBox_Error_Global(*this, FolderMode ?
835             L"You must select some folder":
836             L"You must select some file");
837       */
838       return;
839     }
840     UString s = DirPrefix;
841     s += fs2us(file.Name);
842     s.Add_PathSepar();
843     const HRESULT res = Reload(s, UString());
844     if (res != S_OK)
845       MessageBox_HResError(*this, res, s);
846     SetPathEditText();
847   }
848 }
849 
FinishOnOK()850 void CBrowseDialog::FinishOnOK()
851 {
852   UString s;
853   _pathEdit.GetText(s);
854   FString destPath;
855   if (!GetFullPath(us2fs(DirPrefix), us2fs(s), destPath))
856   {
857     MessageBox_HResError((HWND)*this, ERROR_INVALID_NAME, s);
858     return;
859   }
860   FilePath = fs2us(destPath);
861   if (FolderMode)
862     NormalizeDirPathPrefix(FilePath);
863   FilterIndex = _filterCombo.GetCurSel();
864   End(IDOK);
865 }
866 
867 #endif // USE_MY_BROWSE_DIALOG
868 
869 
870 
MyBrowseForFolder(HWND owner,LPCWSTR title,LPCWSTR path,UString & resultPath)871 bool MyBrowseForFolder(HWND owner, LPCWSTR title, LPCWSTR path, UString &resultPath)
872 {
873   resultPath.Empty();
874 
875   #ifndef UNDER_CE
876 
877 #ifdef USE_MY_BROWSE_DIALOG
878   if (!IsSuperOrDevicePath(path))
879   if (MyStringLen(path) < MAX_PATH)
880 #endif
881     return NShell::BrowseForFolder(owner, title, path, resultPath);
882 
883   #endif //  UNDER_CE
884 
885   #ifdef USE_MY_BROWSE_DIALOG
886 
887   CBrowseDialog dialog;
888   dialog.FolderMode = true;
889   if (title)
890     dialog.Title = title;
891   if (path)
892     dialog.FilePath = path;
893   if (dialog.Create(owner) != IDOK)
894     return false;
895   resultPath = dialog.FilePath;
896   return true;
897 
898   #endif
899 }
900 
901 
902 // LPCWSTR filterDescription, LPCWSTR filter,
903 
BrowseForFile(const CObjectVector<CBrowseFilterInfo> & filters)904 bool CBrowseInfo::BrowseForFile(const CObjectVector<CBrowseFilterInfo> &filters)
905 {
906 #ifndef UNDER_CE
907 #ifdef USE_MY_BROWSE_DIALOG
908   /* win10:
909      GetOpenFileName() for FilePath doesn't support super prefix "\\\\?\\"
910      GetOpenFileName() for FilePath doesn't support long path
911   */
912   if (!IsSuperOrDevicePath(FilePath))
913   // if (filters.Size() > 100) // for debug
914 #endif
915   {
916     const UString filePath_Store = FilePath;
917     UString dirPrefix;
918     {
919       FString prefix, name;
920       if (NDir::GetFullPathAndSplit(us2fs(FilePath), prefix, name))
921       {
922         dirPrefix = fs2us(prefix);
923         FilePath = fs2us(name);
924       }
925     }
926     UStringVector filters2;
927     FOR_VECTOR (i, filters)
928     {
929       const CBrowseFilterInfo &fi = filters[i];
930       filters2.Add(fi.Description);
931       UString s;
932       FOR_VECTOR (k, fi.Masks)
933       {
934         if (k != 0)
935           s += ";";
936         s += fi.Masks[k];
937       }
938       filters2.Add(s);
939     }
940     if (CommonDlg_BrowseForFile(!dirPrefix.IsEmpty() ? dirPrefix.Ptr(): NULL, filters2))
941       return true;
942     FilePath = filePath_Store;
943 
944   #ifdef UNDER_CE
945     return false;
946   #else
947     // maybe we must use GetLastError in WinCE.
948     const DWORD errorCode = CommDlgExtendedError();
949   #ifdef USE_MY_BROWSE_DIALOG
950     // FNERR_INVALIDFILENAME is expected error, if long path was used
951     if (errorCode != FNERR_INVALIDFILENAME
952         || FilePath.Len() < MAX_PATH)
953   #endif
954     {
955       if (errorCode == 0)  // cancel or close on dialog
956         return false;
957       const char *message = NULL;
958       if (errorCode == FNERR_INVALIDFILENAME)
959         message = "Invalid file name";
960       UString s ("Open Dialog Error:");
961       s.Add_LF();
962       if (message)
963         s += message;
964       else
965       {
966         char temp[16];
967         ConvertUInt32ToHex8Digits(errorCode, temp);
968         s += "Error #";
969         s += temp;
970       }
971       s.Add_LF();
972       s += FilePath;
973       MessageBox_Error_Global(hwndOwner, s);
974     }
975   #endif // UNDER_CE
976   }
977 
978 #endif // UNDER_CE
979 
980 #ifdef USE_MY_BROWSE_DIALOG
981 
982   CBrowseDialog dialog;
983 
984   dialog.FolderMode = false;
985   dialog.SaveMode = SaveMode;
986   dialog.FilterIndex = FilterIndex;
987   dialog.Filters = filters;
988 
989   if (lpstrTitle)
990     dialog.Title = lpstrTitle;
991   dialog.FilePath = FilePath;
992   if (dialog.Create(hwndOwner) != IDOK)
993     return false;
994   FilePath = dialog.FilePath;
995   FilterIndex = dialog.FilterIndex;
996 #endif
997 
998   return true;
999 }
1000 
1001 
1002 #ifdef _WIN32
1003 
RemoveDotsAndSpaces(UString & path)1004 static void RemoveDotsAndSpaces(UString &path)
1005 {
1006   while (!path.IsEmpty())
1007   {
1008     wchar_t c = path.Back();
1009     if (c != ' ' && c != '.')
1010       return;
1011     path.DeleteBack();
1012   }
1013 }
1014 
1015 
CorrectFsPath(const UString & relBase,const UString & path2,UString & result)1016 bool CorrectFsPath(const UString &relBase, const UString &path2, UString &result)
1017 {
1018   result.Empty();
1019 
1020   UString path = path2;
1021   #ifdef _WIN32
1022   path.Replace(L'/', WCHAR_PATH_SEPARATOR);
1023   #endif
1024   unsigned start = 0;
1025   UString base;
1026 
1027   if (IsAbsolutePath(path))
1028   {
1029     #if defined(_WIN32) && !defined(UNDER_CE)
1030     if (IsSuperOrDevicePath(path))
1031     {
1032       result = path;
1033       return true;
1034     }
1035     #endif
1036     start = GetRootPrefixSize(path);
1037   }
1038   else
1039   {
1040     #if defined(_WIN32) && !defined(UNDER_CE)
1041     if (IsSuperOrDevicePath(relBase))
1042     {
1043       result = path;
1044       return true;
1045     }
1046     #endif
1047     base = relBase;
1048   }
1049 
1050   /* We can't use backward, since we must change only disk paths */
1051   /*
1052   for (;;)
1053   {
1054     if (path.Len() <= start)
1055       break;
1056     if (DoesFileOrDirExist(us2fs(path)))
1057       break;
1058     if (path.Back() == WCHAR_PATH_SEPARATOR)
1059     {
1060       path.DeleteBack();
1061       result.Insert(0, WCHAR_PATH_SEPARATOR);
1062     }
1063     int pos = path.ReverseFind(WCHAR_PATH_SEPARATOR) + 1;
1064     UString cur = path.Ptr(pos);
1065     RemoveDotsAndSpaces(cur);
1066     result.Insert(0, cur);
1067     path.DeleteFrom(pos);
1068   }
1069   result.Insert(0, path);
1070   return true;
1071   */
1072 
1073   result += path.Left(start);
1074   bool checkExist = true;
1075   UString cur;
1076 
1077   for (;;)
1078   {
1079     if (start == path.Len())
1080       break;
1081     const int slashPos = path.Find(WCHAR_PATH_SEPARATOR, start);
1082     cur.SetFrom(path.Ptr(start), (slashPos < 0 ? path.Len() : (unsigned)slashPos) - start);
1083     if (checkExist)
1084     {
1085       CFileInfo fi;
1086       if (fi.Find(us2fs(base + result + cur)))
1087       {
1088         if (!fi.IsDir())
1089         {
1090           result = path;
1091           break;
1092         }
1093       }
1094       else
1095         checkExist = false;
1096     }
1097     if (!checkExist)
1098       RemoveDotsAndSpaces(cur);
1099     result += cur;
1100     if (slashPos < 0)
1101       break;
1102     start = (unsigned)(slashPos + 1);
1103     result.Add_PathSepar();
1104   }
1105 
1106   return true;
1107 }
1108 
1109 #else
1110 
CorrectFsPath(const UString &,const UString & path,UString & result)1111 bool CorrectFsPath(const UString & /* relBase */, const UString &path, UString &result)
1112 {
1113   result = path;
1114   return true;
1115 }
1116 
1117 #endif
1118 
Dlg_CreateFolder(HWND wnd,UString & destName)1119 bool Dlg_CreateFolder(HWND wnd, UString &destName)
1120 {
1121   destName.Empty();
1122   CComboDialog dlg;
1123   LangString(IDS_CREATE_FOLDER, dlg.Title);
1124   LangString(IDS_CREATE_FOLDER_NAME, dlg.Static);
1125   LangString(IDS_CREATE_FOLDER_DEFAULT_NAME, dlg.Value);
1126   if (dlg.Create(wnd) != IDOK)
1127     return false;
1128   destName = dlg.Value;
1129   return true;
1130 }
1131