• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // BrowseDialog2.cpp
2 
3 #include "StdAfx.h"
4 
5 #ifdef UNDER_CE
6 #include <commdlg.h>
7 #endif
8 
9 #include <windowsx.h>
10 
11 #include "../../../Common/IntToString.h"
12 #include "../../../Common/StringConvert.h"
13 #include "../../../Common/Wildcard.h"
14 
15 #include "../../../Windows/DLL.h"
16 #include "../../../Windows/FileFind.h"
17 #include "../../../Windows/FileDir.h"
18 #include "../../../Windows/FileName.h"
19 #include "../../../Windows/Menu.h"
20 #include "../../../Windows/ProcessUtils.h"
21 #include "../../../Windows/PropVariantConv.h"
22 #include "../../../Windows/Control/ComboBox.h"
23 #include "../../../Windows/Control/Dialog.h"
24 #include "../../../Windows/Control/Edit.h"
25 #include "../../../Windows/Control/ListView.h"
26 
27 #include "../Explorer/MyMessages.h"
28 
29 #ifndef Z7_NO_REGISTRY
30 #include "HelpUtils.h"
31 #endif
32 
33 #include "../Common/PropIDUtils.h"
34 
35 #include "PropertyNameRes.h"
36 #include "RegistryUtils.h"
37 #include "SysIconUtils.h"
38 #include "FormatUtils.h"
39 #include "LangUtils.h"
40 
41 #include "resource.h"
42 #include "BrowseDialog2Res.h"
43 #include "BrowseDialog2.h"
44 
45 using namespace NWindows;
46 using namespace NFile;
47 using namespace NName;
48 using namespace NFind;
49 
50 #ifndef _UNICODE
51 extern bool g_IsNT;
52 #endif
53 
54 extern bool g_LVN_ITEMACTIVATE_Support;
55 
56 static const int kParentIndex = -1;
57 // static const UINT k_Message_RefreshPathEdit = WM_APP + 1;
58 
59 
60 static const wchar_t *k_Message_Link_operation_was_Blocked =
61     L"link openning was blocked by 7-Zip";
62 
63 extern UString HResultToMessage(HRESULT errorCode);
64 
MessageBox_HResError(HWND wnd,HRESULT errorCode,const wchar_t * name)65 static void MessageBox_HResError(HWND wnd, HRESULT errorCode, const wchar_t *name)
66 {
67   UString s = HResultToMessage(errorCode);
68   if (name)
69   {
70     s.Add_LF();
71     s += name;
72   }
73   ShowErrorMessage(wnd, s);
74 }
75 
MessageBox_LastError_path(HWND wnd,const FString & path)76 static void MessageBox_LastError_path(HWND wnd, const FString &path)
77 {
78   const HRESULT hres = GetLastError_noZero_HRESULT();
79   MessageBox_HResError(wnd, hres, fs2us(path));
80 }
81 
82 
83 static const UInt32 k_EnumerateDirsLimit = 200;
84 static const UInt32 k_EnumerateFilesLimit = 2000;
85 
86 struct CBrowseItem
87 {
88   unsigned MainFileIndex;
89   int SubFileIndex;
90   bool WasInterrupted;
91   UInt32 NumFiles;
92   UInt32 NumDirs;
93   UInt32 NumRootItems;
94   UInt64 Size;
95 
CBrowseItemCBrowseItem96   CBrowseItem():
97     // MainFileIndex(0),
98     SubFileIndex(-1),
99     WasInterrupted(false),
100     NumFiles(0),
101     NumDirs(0),
102     NumRootItems(0),
103     Size(0)
104     {}
105 };
106 
107 
108 struct CBrowseEnumerator
109 {
110   FString Path; // folder path without slash at the end
111   CFileInfo fi; // temp
112   CFileInfo fi_SubFile;
113   CBrowseItem bi;
114 
115   void Enumerate(unsigned level);
NeedInterruptCBrowseEnumerator116   bool NeedInterrupt() const
117   {
118     return bi.NumFiles >= k_EnumerateFilesLimit
119         || bi.NumDirs  >= k_EnumerateDirsLimit;
120   }
121 };
122 
Enumerate(unsigned level)123 void CBrowseEnumerator::Enumerate(unsigned level)
124 {
125   Path.Add_PathSepar();
126   const unsigned len = Path.Len();
127   CObjectVector<FString> names;
128   {
129     CEnumerator enumerator;
130     enumerator.SetDirPrefix(Path);
131     while (enumerator.Next(fi))
132     {
133       if (level == 0)
134       {
135         if (bi.NumRootItems == 0)
136           fi_SubFile = fi;
137         bi.NumRootItems++;
138       }
139 
140       if (fi.IsDir())
141       {
142         bi.NumDirs++;
143         if (!fi.HasReparsePoint())
144           names.Add(fi.Name);
145       }
146       else
147       {
148         bi.NumFiles++;
149         bi.Size += fi.Size;
150       }
151 
152       if (level != 0 || bi.NumRootItems > 1)
153         if (NeedInterrupt())
154         {
155           bi.WasInterrupted = true;
156           return;
157         }
158     }
159   }
160 
161   FOR_VECTOR (i, names)
162   {
163     if (NeedInterrupt())
164     {
165       bi.WasInterrupted = true;
166       return;
167     }
168     Path.DeleteFrom(len);
169     Path += names[i];
170     Enumerate(level + 1);
171   }
172 }
173 
174 
175 
176 
177 class CBrowseDialog2: public NControl::CModalDialog
178 {
179   CRecordVector<CBrowseItem> _items;
180   CObjectVector<CFileInfo> _files;
181 
182   NControl::CListView _list;
183   // NControl::CEdit _pathEdit;
184   NControl::CComboBox _filterCombo;
185 
186   CExtToIconMap _extToIconMap;
187   int _sortIndex;
188   int _columnIndex_fileNameInDir;
189   int _columnIndex_NumFiles;
190   int _columnIndex_NumDirs;
191   bool _ascending;
192  #ifndef Z7_SFX
193   bool _showDots;
194  #endif
195   UString _topDirPrefix; // we don't open parent of that folder
196   UString DirPrefix;
197 
198   virtual bool OnInit() Z7_override;
199   virtual bool OnSize(WPARAM wParam, int xSize, int ySize) Z7_override;
200   virtual bool OnMessage(UINT message, WPARAM wParam, LPARAM lParam) Z7_override;
201   virtual bool OnNotify(UINT controlID, LPNMHDR header) Z7_override;
202   // virtual bool OnCommand(unsigned code, unsigned itemID, LPARAM lParam) Z7_override;
203   virtual bool OnButtonClicked(unsigned buttonID, HWND buttonHWND) Z7_override;
204   virtual void OnOK() Z7_override;
205 
206   bool OnKeyDown(LPNMLVKEYDOWN keyDownInfo);
207 
208   // void Post_RefreshPathEdit() { PostMsg(k_Message_RefreshPathEdit); }
209   bool GetParentPath(const UString &path, UString &parentPrefix, UString &name);
210   // Reload changes DirPrefix. Don't send DirPrefix in pathPrefix parameter
211   HRESULT Reload(const UString &pathPrefix, const UStringVector &selectedNames, const UString &focusedName);
212   HRESULT Reload(const UString &pathPrefix, const UString &selectedNames);
213   HRESULT Reload();
214 
215   void ChangeSorting_and_Reload(int columnIndex);
216 
Get_MainFileInfo_for_realIndex(unsigned realIndex) const217   const CFileInfo & Get_MainFileInfo_for_realIndex(unsigned realIndex) const
218   {
219     return _files[_items[realIndex].MainFileIndex];
220   }
221 
Get_MainFileName_for_realIndex(unsigned realIndex) const222   const FString & Get_MainFileName_for_realIndex(unsigned realIndex) const
223   {
224     return Get_MainFileInfo_for_realIndex(realIndex).Name;
225   }
226 
227   void Reload_WithErrorMessage();
228   void OpenParentFolder();
229   // void SetPathEditText();
230   void PrintFileProps(UString &s, const CFileInfo &file);
231   void Show_FileProps_Window(const CFileInfo &file);
232   void OnItemEnter();
233   // void FinishOnOK();
234   void OnDelete(/* bool toRecycleBin */);
235   virtual void OnHelp() Z7_override;
236   bool OnContextMenu(HANDLE windowHandle, int xPos, int yPos);
237 
GetRealItemIndex(int indexInListView) const238   int GetRealItemIndex(int indexInListView) const
239   {
240     LPARAM param;
241     if (!_list.GetItemParam((unsigned)indexInListView, param))
242       return (int)-1;
243     return (int)param;
244   }
245 
246   void GetSelected_RealIndexes(CUIntVector &vector);
247 
248 public:
249   // bool TempMode;
250   // bool Show_Non7zDirs_InTemp;
251   // int FilterIndex;  // [in / out]
252   // CObjectVector<CBrowseFilterInfo> Filters;
253 
254   UString TempFolderPath; // with slash
255   UString Title;
256 
IsExactTempFolder(const UString & pathPrefix) const257   bool IsExactTempFolder(const UString &pathPrefix) const
258   {
259     return CompareFileNames(pathPrefix, TempFolderPath) == 0;
260   }
261 
CBrowseDialog2()262   CBrowseDialog2():
263    #ifndef Z7_SFX
264       _showDots(false)
265    #endif
266       // , TempMode(false)
267       // Show_Non7zDirs_InTemp(false),
268       // FilterIndex(-1)
269     {}
Create(HWND parent=NULL)270   INT_PTR Create(HWND parent = NULL) { return CModalDialog::Create(IDD_BROWSE2, parent); }
271   int CompareItems(LPARAM lParam1, LPARAM lParam2) const;
272 };
273 
274 
275 #ifdef Z7_LANG
276 static const UInt32 kLangIDs[] =
277 {
278   IDS_BUTTON_DELETE,
279   IDM_VIEW_REFRESH
280 };
281 #endif
282 
OnInit()283 bool CBrowseDialog2::OnInit()
284 {
285   #ifdef Z7_LANG
286   LangSetDlgItems(*this, kLangIDs, Z7_ARRAY_SIZE(kLangIDs));
287   #endif
288   if (!Title.IsEmpty())
289     SetText(Title);
290 
291   _list.Attach(GetItem(IDL_BROWSE2));
292   _filterCombo.Attach(GetItem(IDC_BROWSE2_FILTER));
293 
294   _ascending = true;
295   _sortIndex = 0;
296   _columnIndex_fileNameInDir = -1;
297   _columnIndex_NumFiles = -1;
298   _columnIndex_NumDirs = -1;
299   // _pathEdit.Attach(GetItem(IDE_BROWSE_PATH));
300 
301   #ifndef UNDER_CE
302   _list.SetUnicodeFormat();
303   #endif
304 
305   #ifndef Z7_SFX
306   {
307     CFmSettings st;
308     st.Load();
309 
310     DWORD extendedStyle = 0;
311     if (st.FullRow)
312       extendedStyle |= LVS_EX_FULLROWSELECT;
313     if (st.ShowGrid)
314       extendedStyle |= LVS_EX_GRIDLINES;
315     if (st.SingleClick)
316     {
317       extendedStyle |= LVS_EX_ONECLICKACTIVATE | LVS_EX_TRACKSELECT;
318       /*
319       if (ReadUnderline())
320       extendedStyle |= LVS_EX_UNDERLINEHOT;
321       */
322     }
323     if (extendedStyle)
324       _list.SetExtendedListViewStyle(extendedStyle);
325     _showDots = st.ShowDots;
326   }
327   #endif
328 
329   {
330     /*
331     Filters.Clear(); // for debug
332     if (Filters.IsEmpty() && !FolderMode)
333     {
334       CBrowseFilterInfo &f = Filters.AddNew();
335       const UString mask("*.*");
336       f.Masks.Add(mask);
337       // f.Description = "(";
338       f.Description += mask;
339       // f.Description += ")";
340     }
341     */
342     _filterCombo.AddString(L"7-Zip temp files (7z*)");
343     _filterCombo.SetCurSel(0);
344     EnableItem(IDC_BROWSE2_FILTER, false);
345 #if 0
346     FOR_VECTOR (i, Filters)
347     {
348       _filterCombo.AddString(Filters[i].Description);
349     }
350     if (Filters.Size() <= 1)
351     {
352       EnableItem(IDC_BROWSE_FILTER, false);
353     }
354     if (/* FilterIndex >= 0 && */ (unsigned)FilterIndex < Filters.Size())
355       _filterCombo.SetCurSel(FilterIndex);
356 #endif
357   }
358 
359   _list.SetImageList(GetSysImageList(true), LVSIL_SMALL);
360   _list.SetImageList(GetSysImageList(false), LVSIL_NORMAL);
361 
362   unsigned columnIndex = 0;
363   _list.InsertColumn(columnIndex++, LangString(IDS_PROP_NAME), 100);
364   _list.InsertColumn(columnIndex++, LangString(IDS_PROP_MTIME), 100);
365   {
366     LV_COLUMNW column;
367     column.iSubItem = (int)columnIndex;
368     column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
369     column.fmt = LVCFMT_RIGHT;
370     column.cx = 100;
371     UString s = LangString(IDS_PROP_SIZE);
372     column.pszText = s.Ptr_non_const();
373     _list.InsertColumn(columnIndex++, &column);
374 
375     // if (TempMode)
376     {
377       _columnIndex_NumFiles = (int)columnIndex;
378       s = LangString(IDS_PROP_FILES);
379       column.pszText = s.Ptr_non_const();
380       _list.InsertColumn(columnIndex++, &column);
381 
382       _columnIndex_NumDirs = (int)columnIndex;
383       s = LangString(IDS_PROP_FOLDERS);
384       column.pszText = s.Ptr_non_const();
385       _list.InsertColumn(columnIndex++, &column);
386 
387       _columnIndex_fileNameInDir = (int)columnIndex;
388       s = LangString(IDS_PROP_NAME);
389       s += "-2";
390       _list.InsertColumn(columnIndex++, s, 100);
391     }
392   }
393 
394   _list.InsertItem(0, L"12345678901234567"
395       #ifndef UNDER_CE
396       L"1234567890"
397       #endif
398       );
399   _list.SetSubItem(0, 1, L"2009-09-09"
400       #ifndef UNDER_CE
401       L" 09:09:09"
402       #endif
403       );
404   _list.SetSubItem(0, 2, L"99999 MB+");
405 
406   if (_columnIndex_NumFiles >= 0)
407     _list.SetSubItem(0, (unsigned)_columnIndex_NumFiles, L"123456789+");
408   if (_columnIndex_NumDirs >= 0)
409     _list.SetSubItem(0, (unsigned)_columnIndex_NumDirs, L"123456789+");
410   if (_columnIndex_fileNameInDir >= 0)
411     _list.SetSubItem(0, (unsigned)_columnIndex_fileNameInDir, L"12345678901234567890");
412 
413   for (unsigned i = 0; i < columnIndex; i++)
414     _list.SetColumnWidthAuto((int)i);
415   _list.DeleteAllItems();
416 
417   // if (TempMode)
418   {
419     _sortIndex = 1; // for MTime column
420     // _ascending = false;
421   }
422 
423 
424   NormalizeSize();
425 
426   _topDirPrefix.Empty();
427   {
428     unsigned rootSize = GetRootPrefixSize(TempFolderPath);
429     #if defined(_WIN32) && !defined(UNDER_CE)
430     // We can go up from root folder to drives list
431     if (IsDrivePath(TempFolderPath))
432       rootSize = 0;
433     else if (IsSuperPath(TempFolderPath))
434     {
435       if (IsDrivePath(TempFolderPath.Ptr(kSuperPathPrefixSize)))
436         rootSize = kSuperPathPrefixSize;
437     }
438     #endif
439     _topDirPrefix.SetFrom(TempFolderPath, rootSize);
440   }
441 
442   if (Reload(TempFolderPath, UString()) != S_OK)
443   {
444     // return false;
445   }
446 /*
447   UString name;
448   DirPrefix = TempFolderPath;
449   for (;;)
450   {
451     UString baseFolder = DirPrefix;
452     if (Reload(baseFolder, name) == S_OK)
453       break;
454     name.Empty();
455     if (DirPrefix.IsEmpty())
456       break;
457     UString parent, name2;
458     GetParentPath(DirPrefix, parent, name2);
459     DirPrefix = parent;
460   }
461 */
462 
463   #ifndef UNDER_CE
464   /* If we clear UISF_HIDEFOCUS, the focus rectangle in ListView will be visible,
465      even if we use mouse for pressing the button to open this dialog. */
466   PostMsg(Z7_WIN_WM_UPDATEUISTATE, MAKEWPARAM(Z7_WIN_UIS_CLEAR, Z7_WIN_UISF_HIDEFOCUS));
467   #endif
468 
469   /*
470   */
471 
472   return CModalDialog::OnInit();
473 }
474 
475 
OnSize(WPARAM,int xSize,int ySize)476 bool CBrowseDialog2::OnSize(WPARAM /* wParam */, int xSize, int ySize)
477 {
478   int mx, my;
479   {
480     RECT r;
481     GetClientRectOfItem(IDS_BUTTON_DELETE, r);
482     mx = r.left;
483     my = r.top;
484   }
485   InvalidateRect(NULL);
486 
487   const int xLim = xSize - mx;
488   {
489     RECT r;
490     GetClientRectOfItem(IDT_BROWSE2_FOLDER, r);
491     MoveItem(IDT_BROWSE2_FOLDER, r.left, r.top, xLim - r.left, RECT_SIZE_Y(r));
492   }
493 
494   int bx1, bx2, by;
495   GetItemSizes(IDCLOSE, bx1, by);
496   GetItemSizes(IDHELP,  bx2, by);
497   const int y = ySize - my - by;
498   const int x = xLim - bx1;
499   MoveItem(IDCLOSE, x - mx - bx2, y, bx1, by);
500   MoveItem(IDHELP,  x,            y, bx2, by);
501   /*
502   int yPathSize;
503   {
504     RECT r;
505     GetClientRectOfItem(IDE_BROWSE_PATH, r);
506     yPathSize = RECT_SIZE_Y(r);
507     _pathEdit.Move(r.left, y - my - yPathSize - my - yPathSize, xLim - r.left, yPathSize);
508   }
509   */
510   // Y_Size of ComboBox is tricky. Can we use it?
511   int yFilterSize;
512   {
513     RECT r;
514     GetClientRectOfItem(IDC_BROWSE2_FILTER, r);
515     yFilterSize = RECT_SIZE_Y(r);
516     _filterCombo.Move(r.left, y - my - yFilterSize, xLim - r.left, yFilterSize);
517   }
518   {
519     RECT r;
520     GetClientRectOfItem(IDL_BROWSE2, r);
521     _list.Move(r.left, r.top, xLim - r.left, y - my - yFilterSize - my - r.top);
522   }
523   return false;
524 }
525 
526 
OnMessage(UINT message,WPARAM wParam,LPARAM lParam)527 bool CBrowseDialog2::OnMessage(UINT message, WPARAM wParam, LPARAM lParam)
528 {
529   /*
530   if (message == k_Message_RefreshPathEdit)
531   {
532     // SetPathEditText();
533     return true;
534   }
535   */
536   if (message == WM_CONTEXTMENU)
537   {
538     if (OnContextMenu((HANDLE)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))
539       return true;
540   }
541   return CModalDialog::OnMessage(message, wParam, lParam);
542 }
543 
544 
545 /*
546 bool CBrowseDialog2::OnCommand(unsigned code, unsigned itemID, LPARAM lParam)
547 {
548   if (code == CBN_SELCHANGE)
549   {
550     switch (itemID)
551     {
552       case IDC_BROWSE2_FILTER:
553       {
554         Reload();
555         return true;
556       }
557     }
558   }
559   return CModalDialog::OnCommand(code, itemID, lParam);
560 }
561 */
562 
OnNotify(UINT,LPNMHDR header)563 bool CBrowseDialog2::OnNotify(UINT /* controlID */, LPNMHDR header)
564 {
565   if (header->hwndFrom != _list)
566   {
567     if (::GetParent(header->hwndFrom) == _list)
568     {
569       // NMHDR:code is UINT
570       // NM_RCLICK is unsigned in windows sdk
571       // NM_RCLICK is int      in MinGW
572       if (header->code == (UINT)NM_RCLICK)
573       {
574 #ifdef UNDER_CE
575 #define MY_NMLISTVIEW_NMITEMACTIVATE NMLISTVIEW
576 #else
577 #define MY_NMLISTVIEW_NMITEMACTIVATE NMITEMACTIVATE
578 #endif
579         MY_NMLISTVIEW_NMITEMACTIVATE *itemActivate = (MY_NMLISTVIEW_NMITEMACTIVATE *)header;
580         if (itemActivate->hdr.hwndFrom == HWND(_list))
581           return false;
582         /*
583           POINT point;
584           ::GetCursorPos(&point);
585           ShowColumnsContextMenu(point.x, point.y);
586         */
587         // we want to disable menu for columns.
588         // to return the value from a dialog procedure we must
589         // call SetMsgResult(val) and return true;
590         // NM_RCLICK : Return nonzero to not allow the default processing
591         SetMsgResult(TRUE); // do not allow default processing
592         return true;
593       }
594     }
595     return false;
596   }
597 
598   switch (header->code)
599   {
600     case LVN_ITEMACTIVATE:
601       if (g_LVN_ITEMACTIVATE_Support)
602         OnItemEnter();
603       break;
604     case NM_DBLCLK:
605     case NM_RETURN: // probably it's unused
606       if (!g_LVN_ITEMACTIVATE_Support)
607         OnItemEnter();
608       break;
609     case LVN_COLUMNCLICK:
610     {
611       const int index = LPNMLISTVIEW(header)->iSubItem;
612       ChangeSorting_and_Reload(index);
613       return false;
614     }
615     case LVN_KEYDOWN:
616     {
617       bool boolResult = OnKeyDown(LPNMLVKEYDOWN(header));
618       // Post_RefreshPathEdit();
619       return boolResult;
620     }
621     /*
622     case NM_RCLICK:
623     case NM_CLICK:
624     case LVN_BEGINDRAG:
625       Post_RefreshPathEdit();
626       break;
627     */
628   }
629 
630   return false;
631 }
632 
OnKeyDown(LPNMLVKEYDOWN keyDownInfo)633 bool CBrowseDialog2::OnKeyDown(LPNMLVKEYDOWN keyDownInfo)
634 {
635   const bool ctrl = IsKeyDown(VK_CONTROL);
636   // const bool alt = IsKeyDown(VK_MENU);
637   // const bool leftCtrl = IsKeyDown(VK_LCONTROL);
638   // const bool rightCtrl = IsKeyDown(VK_RCONTROL);
639   // const bool shift = IsKeyDown(VK_SHIFT);
640 
641   switch (keyDownInfo->wVKey)
642   {
643     case VK_BACK:
644       OpenParentFolder();
645       return true;
646     case 'R':
647       if (ctrl)
648       {
649         Reload_WithErrorMessage();
650         return true;
651       }
652       return false;
653     case VK_F3:
654     case VK_F5:
655     case VK_F6:
656       if (ctrl)
657       {
658         int index = 0; // name
659               if (keyDownInfo->wVKey == VK_F5)  index = 1; // MTime
660         else  if (keyDownInfo->wVKey == VK_F6)  index = 2; // Size
661         ChangeSorting_and_Reload(index);
662         Reload_WithErrorMessage();
663         return true;
664       }
665       return false;
666     case 'A':
667       if (ctrl)
668       {
669         // if (TempMode)
670           _list.SelectAll();
671         return true;
672       }
673       return false;
674 
675     case VK_DELETE:
676       // if (TempMode)
677         OnDelete(/* !shift */);
678       return true;
679 #if 0
680     case VK_NEXT:
681     case VK_PRIOR:
682     {
683       if (ctrl && !alt && !shift)
684       {
685         if (keyDownInfo->wVKey == VK_NEXT)
686           OnItemEnter();
687         else
688           OpenParentFolder();
689         SetMsgResult(TRUE); // to disable processing
690         return true;
691       }
692       break;
693     }
694 #endif
695   }
696   return false;
697 }
698 
699 
OnButtonClicked(unsigned buttonID,HWND buttonHWND)700 bool CBrowseDialog2::OnButtonClicked(unsigned buttonID, HWND buttonHWND)
701 {
702   switch (buttonID)
703   {
704     case IDB_BROWSE2_PARENT: OpenParentFolder(); break;
705     case IDS_BUTTON_DELETE:
706     {
707       OnDelete(/* !IsKeyDown(VK_SHIFT) */);
708       break;
709     }
710     case IDM_VIEW_REFRESH:
711     {
712       Reload_WithErrorMessage();
713       break;
714     }
715     default: return CModalDialog::OnButtonClicked(buttonID, buttonHWND);
716   }
717   _list.SetFocus();
718   return true;
719 }
720 
721 
722 
PrintPropsPrefix(UString & s,UInt32 id)723 static void PrintPropsPrefix(UString &s, UInt32 id)
724 {
725   s.Add_LF();
726   s += "    ";
727   AddLangString(s, id);
728   s += ": ";
729 }
730 
731 wchar_t *Browse_ConvertSizeToString(UInt64 v, wchar_t *s);
732 
Browse_ConvertSizeToString(const CBrowseItem & bi,wchar_t * s)733 static void Browse_ConvertSizeToString(const CBrowseItem &bi, wchar_t *s)
734 {
735   s = Browse_ConvertSizeToString(bi.Size, s);
736   if (bi.WasInterrupted)
737   {
738     *s++ = '+';
739     *s = 0;
740   }
741 }
742 
743 void AddSizeValue(UString &s, UInt64 value);
744 
PrintProps_Size(UString & s,UInt64 size)745 static void PrintProps_Size(UString &s, UInt64 size)
746 {
747   PrintPropsPrefix(s, IDS_PROP_SIZE);
748 #if 1
749   AddSizeValue(s, size);
750 #else
751   s.Add_UInt64(size);
752   if (size >= 10000)
753   {
754     s += " (";
755     wchar_t temp[64];
756     Browse_ConvertSizeToString(size, temp);
757     s += temp;
758     s.Add_Char(')');
759   }
760 #endif
761 }
762 
PrintProps_MTime(UString & s,const CFileInfo & fi)763 static void PrintProps_MTime(UString &s, const CFileInfo &fi)
764 {
765   PrintPropsPrefix(s, IDS_PROP_MTIME);
766   char t[64];
767   ConvertUtcFileTimeToString(fi.MTime, t);
768   s += t;
769 }
770 
771 
PrintProps_Name(UString & s,const CFileInfo & fi)772 static void PrintProps_Name(UString &s, const CFileInfo &fi)
773 {
774   s += fs2us(fi.Name);
775   if (fi.IsDir())
776     s.Add_PathSepar();
777 }
778 
PrintProps_Attrib(UString & s,const CFileInfo & fi)779 static void PrintProps_Attrib(UString &s, const CFileInfo &fi)
780 {
781   PrintPropsPrefix(s, IDS_PROP_ATTRIBUTES);
782   char props[64];
783   ConvertWinAttribToString(props, fi.Attrib);
784   s += props;
785 #if 0
786   if (fi.HasReparsePoint())
787   {
788     s.Add_LF();
789     s += "IsLink: +";
790   }
791 #endif
792 }
793 
PrintProps(UString & s,const CBrowseItem & bi,const CFileInfo & fi,const CFileInfo * fi2)794 static void PrintProps(UString &s, const CBrowseItem &bi,
795     const CFileInfo &fi, const CFileInfo *fi2)
796 {
797   PrintProps_Name(s, fi);
798   PrintProps_Attrib(s, fi);
799   if (bi.NumDirs != 0)
800   {
801     PrintPropsPrefix(s, IDS_PROP_FOLDERS);
802     s.Add_UInt32(bi.NumDirs);
803     if (bi.WasInterrupted)
804       s += "+";
805   }
806   if (bi.NumFiles != 0)
807   {
808     PrintPropsPrefix(s, IDS_PROP_FILES);
809     s.Add_UInt32(bi.NumFiles);
810     if (bi.WasInterrupted)
811       s += "+";
812   }
813   {
814     PrintProps_Size(s, bi.Size);
815     if (bi.WasInterrupted)
816       s += "+";
817   }
818 
819   PrintProps_MTime(s, fi);
820 
821   if (fi2)
822   {
823     s.Add_LF();
824     s += "----------------";
825     s.Add_LF();
826     PrintProps_Name(s, *fi2);
827     PrintProps_Attrib(s, *fi2);
828     if (!fi2->IsDir())
829       PrintProps_Size(s, fi2->Size);
830     PrintProps_MTime(s, *fi2);
831   }
832 }
833 
834 
GetSelected_RealIndexes(CUIntVector & vector)835 void CBrowseDialog2::GetSelected_RealIndexes(CUIntVector &vector)
836 {
837   vector.Clear();
838   int index = -1;
839   for (;;)
840   {
841     index = _list.GetNextSelectedItem(index);
842     if (index < 0)
843       break;
844     const int realIndex = GetRealItemIndex(index);
845     if (realIndex >= 0)
846       vector.Add((unsigned)realIndex);
847   }
848 }
849 
850 
PrintFileProps(UString & s,const CFileInfo & file)851 void CBrowseDialog2::PrintFileProps(UString &s, const CFileInfo &file)
852 {
853   CFileInfo file2;
854   FString path = us2fs(DirPrefix);
855   path += file.Name;
856   if (!file2.Find(path))
857   {
858     MessageBox_LastError_path(*this, path);
859     Reload_WithErrorMessage();
860     return;
861   }
862   CBrowseEnumerator enumer;
863   enumer.bi.Size = file2.Size;
864   if (file2.IsDir() && !file2.HasReparsePoint())
865   {
866     enumer.Path = path;
867     enumer.Enumerate(0); // level
868   }
869   PrintProps(s, enumer.bi, file2,
870       enumer.bi.NumRootItems == 1 ? &enumer.fi_SubFile : NULL);
871 }
872 
873 
Show_FileProps_Window(const CFileInfo & file)874 void CBrowseDialog2::Show_FileProps_Window(const CFileInfo &file)
875 {
876   UString s;
877   PrintFileProps(s, file);
878   MessageBoxW(*this, s, LangString(IDS_PROPERTIES), MB_OK);
879 }
880 
881 
OnDelete()882 void CBrowseDialog2::OnDelete(/* bool toRecycleBin */)
883 {
884 #if 1
885   // we don't want deleting in non temp folders
886   if (!DirPrefix.IsPrefixedBy(TempFolderPath))
887     return;
888 #endif
889 
890   CUIntVector indices;
891   GetSelected_RealIndexes(indices);
892   if (indices.Size() == 0)
893     return;
894   {
895     UInt32 titleID, messageID;
896     UString messageParam;
897     UString s2;
898     if (indices.Size() == 1)
899     {
900       const unsigned index = indices[0];
901       const CBrowseItem &bi = _items[index];
902       const CFileInfo &file = _files[bi.MainFileIndex];
903       PrintFileProps(s2, file);
904       messageParam = fs2us(file.Name);
905       if (file.IsDir())
906       {
907         titleID = IDS_CONFIRM_FOLDER_DELETE;
908         messageID = IDS_WANT_TO_DELETE_FOLDER;
909       }
910       else
911       {
912         titleID = IDS_CONFIRM_FILE_DELETE;
913         messageID = IDS_WANT_TO_DELETE_FILE;
914       }
915     }
916     else
917     {
918       titleID = IDS_CONFIRM_ITEMS_DELETE;
919       messageID = IDS_WANT_TO_DELETE_ITEMS;
920       messageParam = NumberToString(indices.Size());
921 
922       for (UInt32 i = 0; i < indices.Size(); i++)
923       {
924         if (i >= 10)
925         {
926           s2 += "...";
927           break;
928         }
929         const CBrowseItem &bi = _items[indices[i]];
930         const CFileInfo &fi = _files[bi.MainFileIndex];
931         PrintProps_Name(s2, fi);
932         s2.Add_LF();
933       }
934     }
935     UString s = MyFormatNew(messageID, messageParam);
936     if (!s2.IsEmpty())
937     {
938       s.Add_LF();
939       s.Add_LF();
940       s += s2;
941     }
942     if (::MessageBoxW((HWND)*this, s, LangString(titleID), MB_OKCANCEL | MB_ICONQUESTION) != IDOK)
943       return;
944   }
945 
946   for (UInt32 i = 0; i < indices.Size(); i++)
947   {
948     const unsigned index = indices[i];
949     bool result = true;
950     const CBrowseItem &bi = _items[index];
951     const CFileInfo &fi = _files[bi.MainFileIndex];
952     if (fi.Name.IsEmpty())
953       return; // some error
954     const FString fullPath = us2fs(DirPrefix) + fi.Name;
955     if (fi.IsDir())
956       result = NFile::NDir::RemoveDirWithSubItems(fullPath);
957     else
958       result = NFile::NDir::DeleteFileAlways(fullPath);
959     if (!result)
960     {
961       MessageBox_LastError_path(*this, fullPath);
962       return;
963     }
964   }
965 
966   Reload_WithErrorMessage();
967 }
968 
969 
970 #ifndef Z7_NO_REGISTRY
971 #define kHelpTopic "fm/temp.htm"
OnHelp()972 void CBrowseDialog2::OnHelp()
973 {
974   ShowHelpWindow(kHelpTopic);
975   CModalDialog::OnHelp();
976 }
977 #endif
978 
979 
980 HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process);
StartApplication(const UString & dir,const UString & path,HWND window,CProcess & process)981 HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process)
982 {
983   UString path2 = path;
984 
985   #ifdef _WIN32
986   {
987     const int dot = path2.ReverseFind_Dot();
988     const int separ = path2.ReverseFind_PathSepar();
989     if (dot < 0 || dot < separ)
990       path2.Add_Dot();
991   }
992   #endif
993 
994   UINT32 result;
995 
996 #ifndef _UNICODE
997   if (g_IsNT)
998   {
999     SHELLEXECUTEINFOW execInfo;
1000     execInfo.cbSize = sizeof(execInfo);
1001     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT;
1002     execInfo.hwnd = NULL;
1003     execInfo.lpVerb = NULL;
1004     execInfo.lpFile = path2;
1005     execInfo.lpParameters = NULL;
1006     execInfo.lpDirectory = dir.IsEmpty() ? NULL : (LPCWSTR)dir;
1007     execInfo.nShow = SW_SHOWNORMAL;
1008     execInfo.hProcess = NULL;
1009 typedef BOOL (WINAPI * Func_ShellExecuteExW)(LPSHELLEXECUTEINFOW lpExecInfo);
1010 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
1011     const
1012     Func_ShellExecuteExW
1013        f_ShellExecuteExW = Z7_GET_PROC_ADDRESS(
1014     Func_ShellExecuteExW, ::GetModuleHandleW(L"shell32.dll"),
1015         "ShellExecuteExW");
1016     if (!f_ShellExecuteExW)
1017       return 0;
1018     f_ShellExecuteExW(&execInfo);
1019     result = (UINT32)(UINT_PTR)execInfo.hInstApp;
1020     process.Attach(execInfo.hProcess);
1021   }
1022   else
1023 #endif
1024   {
1025     SHELLEXECUTEINFO execInfo;
1026     execInfo.cbSize = sizeof(execInfo);
1027     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS
1028       #ifndef UNDER_CE
1029       | SEE_MASK_FLAG_DDEWAIT
1030       #endif
1031       ;
1032     execInfo.hwnd = NULL;
1033     execInfo.lpVerb = NULL;
1034     const CSysString sysPath (GetSystemString(path2));
1035     const CSysString sysDir (GetSystemString(dir));
1036     execInfo.lpFile = sysPath;
1037     execInfo.lpParameters = NULL;
1038     execInfo.lpDirectory =
1039       #ifdef UNDER_CE
1040         NULL
1041       #else
1042         sysDir.IsEmpty() ? NULL : (LPCTSTR)sysDir
1043       #endif
1044       ;
1045     execInfo.nShow = SW_SHOWNORMAL;
1046     execInfo.hProcess = NULL;
1047     ::ShellExecuteEx(&execInfo);
1048     result = (UINT32)(UINT_PTR)execInfo.hInstApp;
1049     process.Attach(execInfo.hProcess);
1050   }
1051 
1052   // DEBUG_PRINT_NUM("-- ShellExecuteEx -- execInfo.hInstApp = ", result)
1053 
1054   if (result <= 32)
1055   {
1056     switch (result)
1057     {
1058       case SE_ERR_NOASSOC:
1059         MessageBox_HResError(window,
1060           GetLastError_noZero_HRESULT(),
1061           NULL
1062           // L"There is no application associated with the given file name extension",
1063           );
1064     }
1065 
1066     return E_FAIL; // fixed in 15.13. Can we use it for any Windows version?
1067   }
1068 
1069   return S_OK;
1070 }
1071 
1072 void StartApplicationDontWait(const UString &dir, const UString &path, HWND window);
StartApplicationDontWait(const UString & dir,const UString & path,HWND window)1073 void StartApplicationDontWait(const UString &dir, const UString &path, HWND window)
1074 {
1075   CProcess process;
1076   StartApplication(dir, path, window, process);
1077 }
1078 
1079 
GetQuotedString2(const UString & s)1080 static UString GetQuotedString2(const UString &s)
1081 {
1082   UString s2 ('\"');
1083   s2 += s;
1084   s2.Add_Char('\"');
1085   return s2;
1086 }
1087 
1088 
OnContextMenu(HANDLE windowHandle,int xPos,int yPos)1089 bool CBrowseDialog2::OnContextMenu(HANDLE windowHandle, int xPos, int yPos)
1090 {
1091   if (windowHandle != _list)
1092     return false;
1093 
1094   CUIntVector indices;
1095   GetSelected_RealIndexes(indices);
1096 
1097   // negative x,y are possible for multi-screen modes.
1098   // x=-1 && y=-1 for keyboard call (SHIFT+F10 and others).
1099 #if 1 // 0 : for debug
1100   if (xPos == -1 && yPos == -1)
1101 #endif
1102   {
1103 /*
1104     if (indices.Size() == 0)
1105     {
1106       xPos = 0;
1107       yPos = 0;
1108     }
1109     else
1110 */
1111     {
1112       const int itemIndex = _list.GetFocusedItem();
1113       if (itemIndex == -1)
1114         return false;
1115       RECT rect;
1116       if (!_list.GetItemRect(itemIndex, &rect, LVIR_ICON))
1117         return false;
1118       // rect : rect of file icon relative to listVeiw.
1119       xPos = (rect.left + rect.right) / 2;
1120       yPos = (rect.top + rect.bottom) / 2;
1121       RECT r;
1122       GetClientRectOfItem(IDL_BROWSE2, r);
1123       if (yPos < 0 || yPos >= RECT_SIZE_Y(r))
1124         yPos = 0;
1125     }
1126     POINT point = {xPos, yPos};
1127     _list.ClientToScreen(&point);
1128     xPos = point.x;
1129     yPos = point.y;
1130   }
1131 
1132   const UInt32 k_CmdId_Delete = 1;
1133   const UInt32 k_CmdId_Open_Explorer = 2;
1134   const UInt32 k_CmdId_Open_7zip = 3;
1135   const UInt32 k_CmdId_Props = 4;
1136   int menuResult;
1137   {
1138     CMenu menu;
1139     CMenuDestroyer menuDestroyer(menu);
1140     menu.CreatePopup();
1141 
1142     unsigned numMenuItems = 0;
1143     // unsigned defaultCmd = 0;
1144 
1145     for (unsigned cmd = k_CmdId_Delete; cmd <= k_CmdId_Props; cmd++)
1146     {
1147       if (cmd == k_CmdId_Delete)
1148       {
1149         if (/* !TempMode || */ indices.Size() == 0)
1150           continue;
1151         // defaultCmd = cmd;
1152       }
1153       else if (indices.Size() > 1)
1154         break;
1155 
1156 
1157       if (numMenuItems != 0)
1158       {
1159         if (cmd == k_CmdId_Open_Explorer)
1160           menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)NULL);
1161         if (cmd == k_CmdId_Props)
1162           menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)NULL);
1163       }
1164 
1165       const UINT flags = MF_STRING;
1166       UString s;
1167       if (cmd == k_CmdId_Delete)
1168       {
1169         s = LangString(IDS_BUTTON_DELETE);
1170         s += "\tDelete";
1171       }
1172       else if (cmd == k_CmdId_Open_Explorer)
1173       {
1174         s = LangString(IDM_OPEN_OUTSIDE);
1175         if (s.IsEmpty())
1176           s = "Open Outside";
1177         s += "\tShift+Enter";
1178       }
1179       else if (cmd == k_CmdId_Open_7zip)
1180       {
1181         s = LangString(IDM_OPEN_OUTSIDE);
1182         if (s.IsEmpty())
1183           s = "Open Outside";
1184         s += " : 7-Zip";
1185       }
1186       else if (cmd == k_CmdId_Props)
1187       {
1188         s = LangString(IDS_PROPERTIES);
1189         if (s.IsEmpty())
1190           s = "Properties";
1191         s += "\tAlt+Enter";
1192       }
1193       else
1194         break;
1195       s.RemoveChar(L'&');
1196       menu.AppendItem(flags, cmd, s);
1197       numMenuItems++;
1198     }
1199     // default item is useless for us
1200     /*
1201     if (defaultCmd != 0)
1202       SetMenuDefaultItem(menu, (unsigned)defaultCmd,
1203         FALSE); // byPos
1204     */
1205     /* hwnd for TrackPopupMenuEx(): DOCS:
1206       A handle to the window that owns the shortcut menu.
1207       This window receives all messages from the menu.
1208       The window does not receive a WM_COMMAND message from the menu
1209       until the function returns.
1210       If you specify TPM_NONOTIFY in the fuFlags parameter,
1211       the function does not send messages to the window identified by hwnd.
1212     */
1213     if (numMenuItems == 0)
1214       return true;
1215     menuResult = menu.Track(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY,
1216         xPos, yPos, *this);
1217     /* menu.Track() return value is zero, if the user cancels
1218        the menu without making a selection, or if an error occurs */
1219     if (menuResult <= 0)
1220       return true;
1221   }
1222 
1223   if (menuResult == k_CmdId_Delete)
1224   {
1225     OnDelete(/* !IsKeyDown(VK_SHIFT) */);
1226     return true;
1227   }
1228 
1229   if (indices.Size() <= 1)
1230   {
1231     UString fullPath = DirPrefix;
1232     if (indices.Size() != 0)
1233     {
1234       const CBrowseItem &bi = _items[indices[0]];
1235       const CFileInfo &file = _files[bi.MainFileIndex];
1236       if (file.HasReparsePoint())
1237       {
1238         // we don't want external program was used to work with Links
1239         ShowErrorMessage(*this, k_Message_Link_operation_was_Blocked);
1240         return true;
1241       }
1242       fullPath += fs2us(file.Name);
1243     }
1244     if (menuResult == k_CmdId_Open_Explorer)
1245     {
1246       StartApplicationDontWait(DirPrefix, fullPath, (HWND)*this);
1247       return true;
1248     }
1249 
1250     if (menuResult == k_CmdId_Open_7zip)
1251     {
1252       UString imageName = fs2us(NWindows::NDLL::GetModuleDirPrefix());
1253       imageName += "7zFM.exe";
1254       WRes wres;
1255       {
1256         CProcess process;
1257         wres = process.Create(imageName, GetQuotedString2(fullPath), NULL); // curDir
1258       }
1259       if (wres != 0)
1260       {
1261         const HRESULT hres = HRESULT_FROM_WIN32(wres);
1262         MessageBox_HResError(*this, hres, imageName);
1263       }
1264       return true;
1265     }
1266 
1267     if (indices.Size() == 1)
1268     if (menuResult == k_CmdId_Props)
1269     {
1270       const CBrowseItem &bi = _items[indices[0]];
1271       const CFileInfo &file = _files[bi.MainFileIndex];
1272       Show_FileProps_Window(file);
1273       return true;
1274     }
1275   }
1276 
1277   return true;
1278 }
1279 
1280 
1281 
1282 struct CWaitCursor2
1283 {
1284   HCURSOR _waitCursor;
1285   HCURSOR _oldCursor;
1286 
CWaitCursor2CWaitCursor21287   CWaitCursor2():
1288       _waitCursor(NULL),
1289       _oldCursor(NULL)
1290     {}
SetCWaitCursor21291   void Set()
1292   {
1293     if (!_waitCursor)
1294     {
1295       _waitCursor = LoadCursor(NULL, IDC_WAIT);
1296       if (_waitCursor)
1297         _oldCursor = SetCursor(_waitCursor);
1298     }
1299   }
~CWaitCursor2CWaitCursor21300   ~CWaitCursor2()
1301   {
1302     if (_waitCursor)
1303       SetCursor(_oldCursor);
1304   }
1305 };
1306 
1307 
OnOK()1308 void CBrowseDialog2::OnOK()
1309 {
1310   /* DOCS:
1311     If a dialog box or one of its controls currently has the input focus,
1312     then pressing the ENTER key causes Windows to send a WM_COMMAND message
1313     with the idItem (wParam) parameter set to the ID of the default command button.
1314     If the dialog box does not have a default command button,
1315     then the idItem parameter is set to IDOK by default.
1316 
1317     We process IDOK here for Enter pressing, because we have no DEFPUSHBUTTON.
1318   */
1319   if (GetFocus() == _list)
1320   {
1321     OnItemEnter();
1322     return;
1323   }
1324   // Enter can be pressed in another controls (Edit).
1325   // So we don't need End() call here
1326 }
1327 
1328 
GetParentPath(const UString & path,UString & parentPrefix,UString & name)1329 bool CBrowseDialog2::GetParentPath(const UString &path, UString &parentPrefix, UString &name)
1330 {
1331   parentPrefix.Empty();
1332   name.Empty();
1333   if (path.IsEmpty())
1334     return false;
1335   if (_topDirPrefix == path)
1336     return false;
1337   UString s = path;
1338   if (IS_PATH_SEPAR(s.Back()))
1339     s.DeleteBack();
1340   if (s.IsEmpty())
1341     return false;
1342   if (IS_PATH_SEPAR(s.Back()))
1343     return false;
1344   const unsigned pos1 = (unsigned)(s.ReverseFind_PathSepar() + 1);
1345   parentPrefix.SetFrom(s, pos1);
1346   name = s.Ptr(pos1);
1347   return true;
1348 }
1349 
1350 
CompareItems(LPARAM lParam1,LPARAM lParam2) const1351 int CBrowseDialog2::CompareItems(LPARAM lParam1, LPARAM lParam2) const
1352 {
1353   if (lParam1 == lParam2)      return 0;
1354   if (lParam1 == kParentIndex) return -1;
1355   if (lParam2 == kParentIndex) return 1;
1356 
1357   const int index1 = (int)lParam1;
1358   const int index2 = (int)lParam2;
1359 
1360   const CBrowseItem &item1 = _items[index1];
1361   const CBrowseItem &item2 = _items[index2];
1362 
1363   const CFileInfo &f1 = _files[item1.MainFileIndex];
1364   const CFileInfo &f2 = _files[item2.MainFileIndex];
1365 
1366   const bool isDir2 = f2.IsDir();
1367   if (f1.IsDir())
1368   {
1369     if (!isDir2) return -1;
1370   }
1371   else if (isDir2) return 1;
1372 
1373   const int res2 = MyCompare(index1, index2);
1374   int res = 0;
1375   switch (_sortIndex)
1376   {
1377     case 0: res = CompareFileNames(fs2us(f1.Name), fs2us(f2.Name)); break;
1378     case 1: res = CompareFileTime(&f1.MTime, &f2.MTime); break;
1379     case 2: res = MyCompare(item1.Size, item2.Size); break;
1380     case 3: res = MyCompare(item1.NumFiles, item2.NumFiles); break;
1381     case 4: res = MyCompare(item1.NumDirs, item2.NumDirs); break;
1382     case 5:
1383     {
1384       const int sub1 = item1.SubFileIndex;
1385       const int sub2 = item2.SubFileIndex;
1386       if (sub1 < 0)
1387       {
1388         if (sub2 >= 0)
1389           res = -1;
1390       }
1391       else if (sub2 < 0)
1392         res = 1;
1393       else
1394         res = CompareFileNames(fs2us(_files[sub1].Name), fs2us(_files[sub2].Name));
1395       break;
1396     }
1397   }
1398   if (res == 0)
1399     res = res2;
1400   return _ascending ? res: -res;
1401 }
1402 
CompareItems2(LPARAM lParam1,LPARAM lParam2,LPARAM lpData)1403 static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
1404 {
1405   return ((CBrowseDialog2 *)lpData)->CompareItems(lParam1, lParam2);
1406 }
1407 
1408 
FindNonHexChar_F(const FChar * s)1409 static const FChar *FindNonHexChar_F(const FChar *s) throw()
1410 {
1411   for (;;)
1412   {
1413     const FChar c = (FChar)*s++; // pointer can go 1 byte after end
1414     if ( (c < '0' || c > '9')
1415       && (c < 'a' || c > 'z')
1416       && (c < 'A' || c > 'Z'))
1417     return s - 1;
1418   }
1419 }
1420 
1421 
Reload_WithErrorMessage()1422 void CBrowseDialog2::Reload_WithErrorMessage()
1423 {
1424   const HRESULT res = Reload();
1425   if (res != S_OK)
1426     MessageBox_HResError(*this, res, DirPrefix);
1427 }
1428 
ChangeSorting_and_Reload(int columnIndex)1429 void CBrowseDialog2::ChangeSorting_and_Reload(int columnIndex)
1430 {
1431   if (columnIndex == _sortIndex)
1432     _ascending = !_ascending;
1433   else
1434   {
1435     _ascending = (columnIndex == 0 || columnIndex == _columnIndex_fileNameInDir); // for name columns
1436     _sortIndex = columnIndex;
1437   }
1438   Reload_WithErrorMessage();
1439 }
1440 
1441 
1442 // Reload changes DirPrefix. Don't send DirPrefix in pathPrefix parameter
Reload(const UString & pathPrefix,const UString & selectedName)1443 HRESULT CBrowseDialog2::Reload(const UString &pathPrefix, const UString &selectedName)
1444 {
1445   UStringVector selectedVector;
1446   if (!selectedName.IsEmpty())
1447     selectedVector.Add(selectedName);
1448   return Reload(pathPrefix, selectedVector, selectedName);
1449 }
1450 
1451 
Reload(const UString & pathPrefix,const UStringVector & selectedVector2,const UString & focusedName)1452 HRESULT CBrowseDialog2::Reload(const UString &pathPrefix, const UStringVector &selectedVector2, const UString &focusedName)
1453 {
1454   UStringVector selectedVector = selectedVector2;
1455   selectedVector.Sort();
1456   CObjectVector<CFileInfo> files;
1457   CRecordVector<CBrowseItem> items;
1458   CWaitCursor2 waitCursor;
1459 
1460   #ifndef UNDER_CE
1461   bool isDrive = false;
1462   if (pathPrefix.IsEmpty() || pathPrefix.IsEqualTo(kSuperPathPrefix))
1463   {
1464     isDrive = true;
1465     FStringVector drives;
1466     if (!MyGetLogicalDriveStrings(drives))
1467       return GetLastError_noZero_HRESULT();
1468     FOR_VECTOR (i, drives)
1469     {
1470       const FString &d = drives[i];
1471       if (d.Len() < 2 || d.Back() != '\\')
1472         return E_FAIL;
1473       CBrowseItem item;
1474       item.MainFileIndex = files.Size();
1475       CFileInfo &fi = files.AddNew();
1476       fi.SetAsDir();
1477       fi.Name = d;
1478       fi.Name.DeleteBack();
1479       items.Add(item);
1480     }
1481   }
1482   else
1483   #endif
1484   {
1485     {
1486       CEnumerator enumerator;
1487       enumerator.SetDirPrefix(us2fs(pathPrefix));
1488       CFileInfo fi;
1489       FString tail;
1490 
1491       const bool isTempFolder = (
1492           // TempMode &&
1493           IsExactTempFolder(pathPrefix)
1494           );
1495       for (;;)
1496       {
1497         {
1498           bool found;
1499           if (!enumerator.Next(fi, found))
1500             return GetLastError_noZero_HRESULT();
1501           if (!found)
1502             break;
1503         }
1504         if (isTempFolder)
1505         {
1506           // if (!Show_Non7zDirs_InTemp)
1507           {
1508             if (!fi.Name.IsPrefixedBy_Ascii_NoCase("7z"))
1509               continue;
1510             tail = fi.Name.Ptr(2);
1511             if ( !tail.IsPrefixedBy_Ascii_NoCase("E") // drag and drop / Copy / create to email
1512               && !tail.IsPrefixedBy_Ascii_NoCase("O") // open
1513               && !tail.IsPrefixedBy_Ascii_NoCase("S")) // SFXSetup
1514                continue;
1515             const FChar *beg = tail.Ptr(1);
1516             const FChar *end = FindNonHexChar_F(beg);
1517             if (end - beg != 8 || *end != 0)
1518               continue;
1519           }
1520         }
1521         CBrowseItem item;
1522         item.MainFileIndex = files.Size();
1523         item.Size = fi.Size;
1524         files.Add(fi);
1525         items.Add(item);
1526       }
1527     }
1528 
1529     UInt64 cnt = items.Size();
1530     // if (TempMode)
1531     {
1532       FOR_VECTOR (i, items)
1533       {
1534         CBrowseItem &item = items[i];
1535         const CFileInfo &fi = files[item.MainFileIndex];
1536         if (!fi.IsDir() || fi.HasReparsePoint())
1537           continue;
1538 
1539         CBrowseEnumerator enumer;
1540         // we need to keep MainFileIndex and Size value of item:
1541         enumer.bi = item; // don't change it
1542         enumer.Path = us2fs(pathPrefix);
1543         enumer.Path += fi.Name;
1544         enumer.Enumerate(0); // level
1545         item = enumer.bi;
1546         if (item.NumRootItems == 1)
1547         {
1548           item.SubFileIndex = (int)files.Size();
1549           files.Add(enumer.fi_SubFile);
1550         }
1551         cnt += item.NumDirs;
1552         cnt += item.NumFiles;
1553         if (cnt > 1000)
1554           waitCursor.Set();
1555       }
1556     }
1557   }
1558   _items = items;
1559   _files = files;
1560 
1561   DirPrefix = pathPrefix;
1562 
1563   EnableItem(IDB_BROWSE2_PARENT, !IsExactTempFolder(pathPrefix));
1564 
1565   SetItemText(IDT_BROWSE2_FOLDER, DirPrefix);
1566 
1567   _list.SetRedraw(false);
1568   _list.DeleteAllItems();
1569 
1570   LVITEMW item;
1571 
1572   unsigned index = 0;
1573   int cursorIndex = -1;
1574 
1575   #ifndef Z7_SFX
1576   if (_showDots && _topDirPrefix != DirPrefix)
1577   {
1578     item.iItem = (int)index;
1579     const UString itemName ("..");
1580     if (focusedName == itemName)
1581       cursorIndex = (int)index;
1582     /*
1583     if (selectedVector.IsEmpty()
1584         // && focusedName.IsEmpty()
1585         // && focusedName == ".."
1586         )
1587       cursorIndex = (int)index;
1588     */
1589     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
1590     unsigned subItem = 0;
1591     item.iSubItem = (int)(subItem++);
1592     item.lParam = kParentIndex;
1593     item.pszText = itemName.Ptr_non_const();
1594     item.iImage = _extToIconMap.GetIconIndex(FILE_ATTRIBUTE_DIRECTORY, DirPrefix);
1595     if (item.iImage < 0)
1596       item.iImage = 0;
1597     _list.InsertItem(&item);
1598 #if 0
1599     for (int k = 1; k < 6; k++)
1600       _list.SetSubItem(index, subItem++, L"2");
1601 #endif
1602     index++;
1603   }
1604   #endif
1605 
1606   for (unsigned i = 0; i < _items.Size(); i++, index++)
1607   {
1608     item.iItem = (int)index;
1609     const CBrowseItem &bi = _items[i];
1610     const CFileInfo &fi = _files[bi.MainFileIndex];
1611     const UString name = fs2us(fi.Name);
1612     // if (!selectedName.IsEmpty() && CompareFileNames(name, selectedName) == 0)
1613     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
1614     item.state = 0;
1615     if (selectedVector.FindInSorted(name) != -1)
1616     {
1617       /*
1618       if (cursorIndex == -1)
1619         cursorIndex = (int)index;
1620       */
1621       item.mask |= LVIF_STATE;
1622       item.state |= LVIS_SELECTED;
1623     }
1624     if (focusedName == name)
1625     {
1626       if (cursorIndex == -1)
1627         cursorIndex = (int)index;
1628       item.mask |= LVIF_STATE;
1629       item.state |= LVIS_FOCUSED;
1630     }
1631 
1632     unsigned subItem = 0;
1633     item.iSubItem = (int)(subItem++);
1634     item.lParam = (LPARAM)i;
1635     item.pszText = name.Ptr_non_const();
1636 
1637     const UString fullPath = DirPrefix + name;
1638     #ifndef UNDER_CE
1639     if (isDrive)
1640     {
1641       if (GetRealIconIndex(fi.Name + FCHAR_PATH_SEPARATOR, FILE_ATTRIBUTE_DIRECTORY, item.iImage) == 0)
1642         item.iImage = 0;
1643     }
1644     else
1645     #endif
1646       item.iImage = _extToIconMap.GetIconIndex(fi.Attrib, fullPath);
1647     if (item.iImage < 0)
1648       item.iImage = 0;
1649 
1650     _list.InsertItem(&item);
1651     wchar_t s[64];
1652     {
1653       s[0] = 0;
1654       if (!FILETIME_IsZero(fi.MTime))
1655         ConvertUtcFileTimeToString(fi.MTime, s,
1656             #ifndef UNDER_CE
1657               kTimestampPrintLevel_MIN
1658             #else
1659               kTimestampPrintLevel_DAY
1660             #endif
1661               );
1662       _list.SetSubItem(index, subItem++, s);
1663     }
1664 
1665     {
1666       s[0] = 0;
1667       Browse_ConvertSizeToString(bi, s);
1668       _list.SetSubItem(index, subItem++, s);
1669     }
1670     if (_columnIndex_NumFiles >= 0)
1671     {
1672       UString s2;
1673       if (fi.HasReparsePoint())
1674       {
1675         s2 = "Link";
1676       }
1677       else if (bi.NumFiles != 0)
1678       {
1679         s2.Add_UInt32(bi.NumFiles);
1680         if (bi.WasInterrupted)
1681           s2 += "+";
1682       }
1683       _list.SetSubItem(index, subItem, s2);
1684     }
1685     subItem++;
1686     if (_columnIndex_NumDirs >= 0 && bi.NumDirs != 0)
1687     {
1688       UString s2;
1689       s2.Add_UInt32(bi.NumDirs);
1690       if (bi.WasInterrupted)
1691         s2 += "+";
1692       _list.SetSubItem(index, subItem, s2);
1693     }
1694     subItem++;
1695     if (_columnIndex_fileNameInDir >= 0 && bi.SubFileIndex >= 0)
1696     {
1697       _list.SetSubItem(index, subItem, fs2us(_files[bi.SubFileIndex].Name));
1698     }
1699     subItem++;
1700   }
1701 
1702   if (_list.GetItemCount() > 0 && cursorIndex >= 0)
1703   {
1704     // _list.SetItemState_FocusedSelected(cursorIndex);
1705     // _list.SetItemState_Focused(cursorIndex);
1706   }
1707   _list.SortItems(CompareItems2, (LPARAM)this);
1708   if (_list.GetItemCount() > 0 && cursorIndex < 0)
1709   {
1710     if (selectedVector.IsEmpty())
1711       _list.SetItemState_FocusedSelected(0);
1712     else
1713        _list.SetItemState(0, LVIS_FOCUSED, LVIS_FOCUSED);
1714   }
1715   _list.EnsureVisible(_list.GetFocusedItem(), false);
1716   _list.SetRedraw(true);
1717   _list.InvalidateRect(NULL, true);
1718   return S_OK;
1719 }
1720 
1721 
1722 
Reload()1723 HRESULT CBrowseDialog2::Reload()
1724 {
1725   UStringVector selected;
1726   {
1727     CUIntVector indexes;
1728     GetSelected_RealIndexes(indexes);
1729     FOR_VECTOR (i, indexes)
1730       selected.Add(fs2us(Get_MainFileName_for_realIndex(indexes[i])));
1731   }
1732   UString focusedName;
1733   const int focusedItem = _list.GetFocusedItem();
1734   if (focusedItem >= 0)
1735   {
1736     const int realIndex = GetRealItemIndex(focusedItem);
1737     if (realIndex != kParentIndex)
1738       focusedName = fs2us(Get_MainFileName_for_realIndex((unsigned)realIndex));
1739   }
1740   const UString dirPathTemp = DirPrefix;
1741   return Reload(dirPathTemp, selected, focusedName);
1742 }
1743 
1744 
OpenParentFolder()1745 void CBrowseDialog2::OpenParentFolder()
1746 {
1747 #if 1 // 0 : for debug
1748   // we don't allow to go to parent of TempFolder.
1749   // if (TempMode)
1750   {
1751     if (IsExactTempFolder(DirPrefix))
1752       return;
1753   }
1754 #endif
1755 
1756   UString parent, selected;
1757   if (GetParentPath(DirPrefix, parent, selected))
1758     Reload(parent, selected);
1759 }
1760 
1761 
OnItemEnter()1762 void CBrowseDialog2::OnItemEnter()
1763 {
1764   const bool alt = IsKeyDown(VK_MENU);
1765   const bool ctrl = IsKeyDown(VK_CONTROL);
1766   const bool shift = IsKeyDown(VK_SHIFT);
1767 
1768   const int index = _list.GetNextSelectedItem(-1);
1769   if (index < 0)
1770     return;
1771   if (_list.GetNextSelectedItem(index) >= 0)
1772     return; // more than one selected
1773   const int realIndex = GetRealItemIndex(index);
1774   if (realIndex == kParentIndex)
1775     OpenParentFolder();
1776   else
1777   {
1778     const CBrowseItem &bi = _items[realIndex];
1779     const CFileInfo &file = _files[bi.MainFileIndex];
1780     if (alt)
1781     {
1782       Show_FileProps_Window(file);
1783       return;
1784     }
1785     if (file.HasReparsePoint())
1786     {
1787       // we don't want Link open operation,
1788       // because user can think that it's usual folder/file (non-link).
1789       ShowErrorMessage(*this, k_Message_Link_operation_was_Blocked);
1790       return;
1791     }
1792     bool needExternal = true;
1793     if (file.IsDir())
1794     {
1795       if (!shift || alt || ctrl) // open folder in Explorer:
1796         needExternal = false;
1797     }
1798     const UString fullPath = DirPrefix + fs2us(file.Name);
1799     if (needExternal)
1800     {
1801       StartApplicationDontWait(DirPrefix, fullPath, (HWND)*this);
1802       return;
1803     }
1804     UString s = fullPath;
1805     s.Add_PathSepar();
1806     const HRESULT res = Reload(s, UString());
1807     if (res != S_OK)
1808       MessageBox_HResError(*this, res, s);
1809     // SetPathEditText();
1810   }
1811 }
1812 
1813 
MyBrowseForTempFolder(HWND owner)1814 void MyBrowseForTempFolder(HWND owner)
1815 {
1816   FString tempPathF;
1817   if (!NFile::NDir::MyGetTempPath(tempPathF) || tempPathF.IsEmpty())
1818   {
1819     MessageBox_LastError_path(owner, tempPathF);
1820     return;
1821   }
1822   CBrowseDialog2 dialog;
1823 
1824   LangString_OnlyFromLangFile(IDM_TEMP_DIR, dialog.Title);
1825   dialog.Title.Replace(L"...", L"");
1826   if (dialog.Title.IsEmpty())
1827     dialog.Title = "Delete Temporary Files";
1828 
1829   dialog.TempFolderPath = fs2us(tempPathF);
1830   dialog.Create(owner);
1831   // we can exit from dialog with 2 ways:
1832   // IDCANCEL : Esc Key, or close icons
1833   // IDCLOSE  : with Close button
1834 }
1835