xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/BrowseDialog2.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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(Shell_Get_SysImageList_smallIcons(true), LVSIL_SMALL);
360   _list.SetImageList(Shell_Get_SysImageList_smallIcons(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),
943         MB_YESNOCANCEL | MB_ICONQUESTION) != IDYES)
944       return;
945   }
946 
947   for (UInt32 i = 0; i < indices.Size(); i++)
948   {
949     const unsigned index = indices[i];
950     bool result = true;
951     const CBrowseItem &bi = _items[index];
952     const CFileInfo &fi = _files[bi.MainFileIndex];
953     if (fi.Name.IsEmpty())
954       return; // some error
955     const FString fullPath = us2fs(DirPrefix) + fi.Name;
956     if (fi.IsDir())
957       result = NFile::NDir::RemoveDirWithSubItems(fullPath);
958     else
959       result = NFile::NDir::DeleteFileAlways(fullPath);
960     if (!result)
961     {
962       MessageBox_LastError_path(*this, fullPath);
963       return;
964     }
965   }
966 
967   Reload_WithErrorMessage();
968 }
969 
970 
971 #ifndef Z7_NO_REGISTRY
972 #define kHelpTopic "fm/temp.htm"
OnHelp()973 void CBrowseDialog2::OnHelp()
974 {
975   ShowHelpWindow(kHelpTopic);
976   CModalDialog::OnHelp();
977 }
978 #endif
979 
980 
981 HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process);
StartApplication(const UString & dir,const UString & path,HWND window,CProcess & process)982 HRESULT StartApplication(const UString &dir, const UString &path, HWND window, CProcess &process)
983 {
984   UString path2 = path;
985 
986   #ifdef _WIN32
987   {
988     const int dot = path2.ReverseFind_Dot();
989     const int separ = path2.ReverseFind_PathSepar();
990     if (dot < 0 || dot < separ)
991       path2.Add_Dot();
992   }
993   #endif
994 
995   UINT32 result;
996 
997 #ifndef _UNICODE
998   if (g_IsNT)
999   {
1000     SHELLEXECUTEINFOW execInfo;
1001     execInfo.cbSize = sizeof(execInfo);
1002     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT;
1003     execInfo.hwnd = NULL;
1004     execInfo.lpVerb = NULL;
1005     execInfo.lpFile = path2;
1006     execInfo.lpParameters = NULL;
1007     execInfo.lpDirectory = dir.IsEmpty() ? NULL : (LPCWSTR)dir;
1008     execInfo.nShow = SW_SHOWNORMAL;
1009     execInfo.hProcess = NULL;
1010 typedef BOOL (WINAPI * Func_ShellExecuteExW)(LPSHELLEXECUTEINFOW lpExecInfo);
1011 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
1012     const
1013     Func_ShellExecuteExW
1014        f_ShellExecuteExW = Z7_GET_PROC_ADDRESS(
1015     Func_ShellExecuteExW, ::GetModuleHandleW(L"shell32.dll"),
1016         "ShellExecuteExW");
1017     if (!f_ShellExecuteExW)
1018       return 0;
1019     f_ShellExecuteExW(&execInfo);
1020     result = (UINT32)(UINT_PTR)execInfo.hInstApp;
1021     process.Attach(execInfo.hProcess);
1022   }
1023   else
1024 #endif
1025   {
1026     SHELLEXECUTEINFO execInfo;
1027     execInfo.cbSize = sizeof(execInfo);
1028     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS
1029       #ifndef UNDER_CE
1030       | SEE_MASK_FLAG_DDEWAIT
1031       #endif
1032       ;
1033     execInfo.hwnd = NULL;
1034     execInfo.lpVerb = NULL;
1035     const CSysString sysPath (GetSystemString(path2));
1036     const CSysString sysDir (GetSystemString(dir));
1037     execInfo.lpFile = sysPath;
1038     execInfo.lpParameters = NULL;
1039     execInfo.lpDirectory =
1040       #ifdef UNDER_CE
1041         NULL
1042       #else
1043         sysDir.IsEmpty() ? NULL : (LPCTSTR)sysDir
1044       #endif
1045       ;
1046     execInfo.nShow = SW_SHOWNORMAL;
1047     execInfo.hProcess = NULL;
1048     ::ShellExecuteEx(&execInfo);
1049     result = (UINT32)(UINT_PTR)execInfo.hInstApp;
1050     process.Attach(execInfo.hProcess);
1051   }
1052 
1053   // DEBUG_PRINT_NUM("-- ShellExecuteEx -- execInfo.hInstApp = ", result)
1054 
1055   if (result <= 32)
1056   {
1057     switch (result)
1058     {
1059       case SE_ERR_NOASSOC:
1060         MessageBox_HResError(window,
1061           GetLastError_noZero_HRESULT(),
1062           NULL
1063           // L"There is no application associated with the given file name extension",
1064           );
1065     }
1066 
1067     return E_FAIL; // fixed in 15.13. Can we use it for any Windows version?
1068   }
1069 
1070   return S_OK;
1071 }
1072 
1073 void StartApplicationDontWait(const UString &dir, const UString &path, HWND window);
StartApplicationDontWait(const UString & dir,const UString & path,HWND window)1074 void StartApplicationDontWait(const UString &dir, const UString &path, HWND window)
1075 {
1076   CProcess process;
1077   StartApplication(dir, path, window, process);
1078 }
1079 
1080 
GetQuotedString2(const UString & s)1081 static UString GetQuotedString2(const UString &s)
1082 {
1083   UString s2 ('\"');
1084   s2 += s;
1085   s2.Add_Char('\"');
1086   return s2;
1087 }
1088 
1089 
OnContextMenu(HANDLE windowHandle,int xPos,int yPos)1090 bool CBrowseDialog2::OnContextMenu(HANDLE windowHandle, int xPos, int yPos)
1091 {
1092   if (windowHandle != _list)
1093     return false;
1094 
1095   CUIntVector indices;
1096   GetSelected_RealIndexes(indices);
1097 
1098   // negative x,y are possible for multi-screen modes.
1099   // x=-1 && y=-1 for keyboard call (SHIFT+F10 and others).
1100 #if 1 // 0 : for debug
1101   if (xPos == -1 && yPos == -1)
1102 #endif
1103   {
1104 /*
1105     if (indices.Size() == 0)
1106     {
1107       xPos = 0;
1108       yPos = 0;
1109     }
1110     else
1111 */
1112     {
1113       const int itemIndex = _list.GetFocusedItem();
1114       if (itemIndex == -1)
1115         return false;
1116       RECT rect;
1117       if (!_list.GetItemRect(itemIndex, &rect, LVIR_ICON))
1118         return false;
1119       // rect : rect of file icon relative to listVeiw.
1120       xPos = (rect.left + rect.right) / 2;
1121       yPos = (rect.top + rect.bottom) / 2;
1122       RECT r;
1123       GetClientRectOfItem(IDL_BROWSE2, r);
1124       if (yPos < 0 || yPos >= RECT_SIZE_Y(r))
1125         yPos = 0;
1126     }
1127     POINT point = {xPos, yPos};
1128     _list.ClientToScreen(&point);
1129     xPos = point.x;
1130     yPos = point.y;
1131   }
1132 
1133   const UInt32 k_CmdId_Delete = 1;
1134   const UInt32 k_CmdId_Open_Explorer = 2;
1135   const UInt32 k_CmdId_Open_7zip = 3;
1136   const UInt32 k_CmdId_Props = 4;
1137   int menuResult;
1138   {
1139     CMenu menu;
1140     CMenuDestroyer menuDestroyer(menu);
1141     menu.CreatePopup();
1142 
1143     unsigned numMenuItems = 0;
1144     // unsigned defaultCmd = 0;
1145 
1146     for (unsigned cmd = k_CmdId_Delete; cmd <= k_CmdId_Props; cmd++)
1147     {
1148       if (cmd == k_CmdId_Delete)
1149       {
1150         if (/* !TempMode || */ indices.Size() == 0)
1151           continue;
1152         // defaultCmd = cmd;
1153       }
1154       else if (indices.Size() > 1)
1155         break;
1156 
1157 
1158       if (numMenuItems != 0)
1159       {
1160         if (cmd == k_CmdId_Open_Explorer)
1161           menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)NULL);
1162         if (cmd == k_CmdId_Props)
1163           menu.AppendItem(MF_SEPARATOR, 0, (LPCTSTR)NULL);
1164       }
1165 
1166       const UINT flags = MF_STRING;
1167       UString s;
1168       if (cmd == k_CmdId_Delete)
1169       {
1170         s = LangString(IDS_BUTTON_DELETE);
1171         s += "\tDelete";
1172       }
1173       else if (cmd == k_CmdId_Open_Explorer)
1174       {
1175         s = LangString(IDM_OPEN_OUTSIDE);
1176         if (s.IsEmpty())
1177           s = "Open Outside";
1178         s += "\tShift+Enter";
1179       }
1180       else if (cmd == k_CmdId_Open_7zip)
1181       {
1182         s = LangString(IDM_OPEN_OUTSIDE);
1183         if (s.IsEmpty())
1184           s = "Open Outside";
1185         s += " : 7-Zip";
1186       }
1187       else if (cmd == k_CmdId_Props)
1188       {
1189         s = LangString(IDS_PROPERTIES);
1190         if (s.IsEmpty())
1191           s = "Properties";
1192         s += "\tAlt+Enter";
1193       }
1194       else
1195         break;
1196       s.RemoveChar(L'&');
1197       menu.AppendItem(flags, cmd, s);
1198       numMenuItems++;
1199     }
1200     // default item is useless for us
1201     /*
1202     if (defaultCmd != 0)
1203       SetMenuDefaultItem(menu, (unsigned)defaultCmd,
1204         FALSE); // byPos
1205     */
1206     /* hwnd for TrackPopupMenuEx(): DOCS:
1207       A handle to the window that owns the shortcut menu.
1208       This window receives all messages from the menu.
1209       The window does not receive a WM_COMMAND message from the menu
1210       until the function returns.
1211       If you specify TPM_NONOTIFY in the fuFlags parameter,
1212       the function does not send messages to the window identified by hwnd.
1213     */
1214     if (numMenuItems == 0)
1215       return true;
1216     menuResult = menu.Track(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY,
1217         xPos, yPos, *this);
1218     /* menu.Track() return value is zero, if the user cancels
1219        the menu without making a selection, or if an error occurs */
1220     if (menuResult <= 0)
1221       return true;
1222   }
1223 
1224   if (menuResult == k_CmdId_Delete)
1225   {
1226     OnDelete(/* !IsKeyDown(VK_SHIFT) */);
1227     return true;
1228   }
1229 
1230   if (indices.Size() <= 1)
1231   {
1232     UString fullPath = DirPrefix;
1233     if (indices.Size() != 0)
1234     {
1235       const CBrowseItem &bi = _items[indices[0]];
1236       const CFileInfo &file = _files[bi.MainFileIndex];
1237       if (file.HasReparsePoint())
1238       {
1239         // we don't want external program was used to work with Links
1240         ShowErrorMessage(*this, k_Message_Link_operation_was_Blocked);
1241         return true;
1242       }
1243       fullPath += fs2us(file.Name);
1244     }
1245     if (menuResult == k_CmdId_Open_Explorer)
1246     {
1247       StartApplicationDontWait(DirPrefix, fullPath, (HWND)*this);
1248       return true;
1249     }
1250 
1251     if (menuResult == k_CmdId_Open_7zip)
1252     {
1253       UString imageName = fs2us(NWindows::NDLL::GetModuleDirPrefix());
1254       imageName += "7zFM.exe";
1255       WRes wres;
1256       {
1257         CProcess process;
1258         wres = process.Create(imageName, GetQuotedString2(fullPath), NULL); // curDir
1259       }
1260       if (wres != 0)
1261       {
1262         const HRESULT hres = HRESULT_FROM_WIN32(wres);
1263         MessageBox_HResError(*this, hres, imageName);
1264       }
1265       return true;
1266     }
1267 
1268     if (indices.Size() == 1)
1269     if (menuResult == k_CmdId_Props)
1270     {
1271       const CBrowseItem &bi = _items[indices[0]];
1272       const CFileInfo &file = _files[bi.MainFileIndex];
1273       Show_FileProps_Window(file);
1274       return true;
1275     }
1276   }
1277 
1278   return true;
1279 }
1280 
1281 
1282 
1283 struct CWaitCursor2
1284 {
1285   HCURSOR _waitCursor;
1286   HCURSOR _oldCursor;
1287 
CWaitCursor2CWaitCursor21288   CWaitCursor2():
1289       _waitCursor(NULL),
1290       _oldCursor(NULL)
1291     {}
SetCWaitCursor21292   void Set()
1293   {
1294     if (!_waitCursor)
1295     {
1296       _waitCursor = LoadCursor(NULL, IDC_WAIT);
1297       if (_waitCursor)
1298         _oldCursor = SetCursor(_waitCursor);
1299     }
1300   }
~CWaitCursor2CWaitCursor21301   ~CWaitCursor2()
1302   {
1303     if (_waitCursor)
1304       SetCursor(_oldCursor);
1305   }
1306 };
1307 
1308 
OnOK()1309 void CBrowseDialog2::OnOK()
1310 {
1311   /* DOCS:
1312     If a dialog box or one of its controls currently has the input focus,
1313     then pressing the ENTER key causes Windows to send a WM_COMMAND message
1314     with the idItem (wParam) parameter set to the ID of the default command button.
1315     If the dialog box does not have a default command button,
1316     then the idItem parameter is set to IDOK by default.
1317 
1318     We process IDOK here for Enter pressing, because we have no DEFPUSHBUTTON.
1319   */
1320   if (GetFocus() == _list)
1321   {
1322     OnItemEnter();
1323     return;
1324   }
1325   // Enter can be pressed in another controls (Edit).
1326   // So we don't need End() call here
1327 }
1328 
1329 
GetParentPath(const UString & path,UString & parentPrefix,UString & name)1330 bool CBrowseDialog2::GetParentPath(const UString &path, UString &parentPrefix, UString &name)
1331 {
1332   parentPrefix.Empty();
1333   name.Empty();
1334   if (path.IsEmpty())
1335     return false;
1336   if (_topDirPrefix == path)
1337     return false;
1338   UString s = path;
1339   if (IS_PATH_SEPAR(s.Back()))
1340     s.DeleteBack();
1341   if (s.IsEmpty())
1342     return false;
1343   if (IS_PATH_SEPAR(s.Back()))
1344     return false;
1345   const unsigned pos1 = (unsigned)(s.ReverseFind_PathSepar() + 1);
1346   parentPrefix.SetFrom(s, pos1);
1347   name = s.Ptr(pos1);
1348   return true;
1349 }
1350 
1351 
CompareItems(LPARAM lParam1,LPARAM lParam2) const1352 int CBrowseDialog2::CompareItems(LPARAM lParam1, LPARAM lParam2) const
1353 {
1354   if (lParam1 == lParam2)      return 0;
1355   if (lParam1 == kParentIndex) return -1;
1356   if (lParam2 == kParentIndex) return 1;
1357 
1358   const int index1 = (int)lParam1;
1359   const int index2 = (int)lParam2;
1360 
1361   const CBrowseItem &item1 = _items[index1];
1362   const CBrowseItem &item2 = _items[index2];
1363 
1364   const CFileInfo &f1 = _files[item1.MainFileIndex];
1365   const CFileInfo &f2 = _files[item2.MainFileIndex];
1366 
1367   const bool isDir2 = f2.IsDir();
1368   if (f1.IsDir())
1369   {
1370     if (!isDir2) return -1;
1371   }
1372   else if (isDir2) return 1;
1373 
1374   const int res2 = MyCompare(index1, index2);
1375   int res = 0;
1376   switch (_sortIndex)
1377   {
1378     case 0: res = CompareFileNames(fs2us(f1.Name), fs2us(f2.Name)); break;
1379     case 1: res = CompareFileTime(&f1.MTime, &f2.MTime); break;
1380     case 2: res = MyCompare(item1.Size, item2.Size); break;
1381     case 3: res = MyCompare(item1.NumFiles, item2.NumFiles); break;
1382     case 4: res = MyCompare(item1.NumDirs, item2.NumDirs); break;
1383     case 5:
1384     {
1385       const int sub1 = item1.SubFileIndex;
1386       const int sub2 = item2.SubFileIndex;
1387       if (sub1 < 0)
1388       {
1389         if (sub2 >= 0)
1390           res = -1;
1391       }
1392       else if (sub2 < 0)
1393         res = 1;
1394       else
1395         res = CompareFileNames(fs2us(_files[sub1].Name), fs2us(_files[sub2].Name));
1396       break;
1397     }
1398   }
1399   if (res == 0)
1400     res = res2;
1401   return _ascending ? res: -res;
1402 }
1403 
CompareItems2(LPARAM lParam1,LPARAM lParam2,LPARAM lpData)1404 static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
1405 {
1406   return ((CBrowseDialog2 *)lpData)->CompareItems(lParam1, lParam2);
1407 }
1408 
1409 
FindNonHexChar_F(const FChar * s)1410 static const FChar *FindNonHexChar_F(const FChar *s) throw()
1411 {
1412   for (;;)
1413   {
1414     const FChar c = (FChar)*s++; // pointer can go 1 byte after end
1415     if ( (c < '0' || c > '9')
1416       && (c < 'a' || c > 'z')
1417       && (c < 'A' || c > 'Z'))
1418     return s - 1;
1419   }
1420 }
1421 
1422 
Reload_WithErrorMessage()1423 void CBrowseDialog2::Reload_WithErrorMessage()
1424 {
1425   const HRESULT res = Reload();
1426   if (res != S_OK)
1427     MessageBox_HResError(*this, res, DirPrefix);
1428 }
1429 
ChangeSorting_and_Reload(int columnIndex)1430 void CBrowseDialog2::ChangeSorting_and_Reload(int columnIndex)
1431 {
1432   if (columnIndex == _sortIndex)
1433     _ascending = !_ascending;
1434   else
1435   {
1436     _ascending = (columnIndex == 0 || columnIndex == _columnIndex_fileNameInDir); // for name columns
1437     _sortIndex = columnIndex;
1438   }
1439   Reload_WithErrorMessage();
1440 }
1441 
1442 
1443 // Reload changes DirPrefix. Don't send DirPrefix in pathPrefix parameter
Reload(const UString & pathPrefix,const UString & selectedName)1444 HRESULT CBrowseDialog2::Reload(const UString &pathPrefix, const UString &selectedName)
1445 {
1446   UStringVector selectedVector;
1447   if (!selectedName.IsEmpty())
1448     selectedVector.Add(selectedName);
1449   return Reload(pathPrefix, selectedVector, selectedName);
1450 }
1451 
1452 
Reload(const UString & pathPrefix,const UStringVector & selectedVector2,const UString & focusedName)1453 HRESULT CBrowseDialog2::Reload(const UString &pathPrefix, const UStringVector &selectedVector2, const UString &focusedName)
1454 {
1455   UStringVector selectedVector = selectedVector2;
1456   selectedVector.Sort();
1457   CObjectVector<CFileInfo> files;
1458   CRecordVector<CBrowseItem> items;
1459   CWaitCursor2 waitCursor;
1460 
1461   #ifndef UNDER_CE
1462   bool isDrive = false;
1463   if (pathPrefix.IsEmpty() || pathPrefix.IsEqualTo(kSuperPathPrefix))
1464   {
1465     isDrive = true;
1466     FStringVector drives;
1467     if (!MyGetLogicalDriveStrings(drives))
1468       return GetLastError_noZero_HRESULT();
1469     FOR_VECTOR (i, drives)
1470     {
1471       const FString &d = drives[i];
1472       if (d.Len() < 2 || d.Back() != '\\')
1473         return E_FAIL;
1474       CBrowseItem item;
1475       item.MainFileIndex = files.Size();
1476       CFileInfo &fi = files.AddNew();
1477       fi.SetAsDir();
1478       fi.Name = d;
1479       fi.Name.DeleteBack();
1480       items.Add(item);
1481     }
1482   }
1483   else
1484   #endif
1485   {
1486     {
1487       CEnumerator enumerator;
1488       enumerator.SetDirPrefix(us2fs(pathPrefix));
1489       CFileInfo fi;
1490       FString tail;
1491 
1492       const bool isTempFolder = (
1493           // TempMode &&
1494           IsExactTempFolder(pathPrefix)
1495           );
1496       for (;;)
1497       {
1498         {
1499           bool found;
1500           if (!enumerator.Next(fi, found))
1501             return GetLastError_noZero_HRESULT();
1502           if (!found)
1503             break;
1504         }
1505         if (isTempFolder)
1506         {
1507           // if (!Show_Non7zDirs_InTemp)
1508           {
1509             if (!fi.Name.IsPrefixedBy_Ascii_NoCase("7z"))
1510               continue;
1511             tail = fi.Name.Ptr(2);
1512             if ( !tail.IsPrefixedBy_Ascii_NoCase("E") // drag and drop / Copy / create to email
1513               && !tail.IsPrefixedBy_Ascii_NoCase("O") // open
1514               && !tail.IsPrefixedBy_Ascii_NoCase("S")) // SFXSetup
1515                continue;
1516             const FChar *beg = tail.Ptr(1);
1517             const FChar *end = FindNonHexChar_F(beg);
1518             if (end - beg != 8 || *end != 0)
1519               continue;
1520           }
1521         }
1522         CBrowseItem item;
1523         item.MainFileIndex = files.Size();
1524         item.Size = fi.Size;
1525         files.Add(fi);
1526         items.Add(item);
1527       }
1528     }
1529 
1530     UInt64 cnt = items.Size();
1531     // if (TempMode)
1532     {
1533       FOR_VECTOR (i, items)
1534       {
1535         CBrowseItem &item = items[i];
1536         const CFileInfo &fi = files[item.MainFileIndex];
1537         if (!fi.IsDir() || fi.HasReparsePoint())
1538           continue;
1539 
1540         CBrowseEnumerator enumer;
1541         // we need to keep MainFileIndex and Size value of item:
1542         enumer.bi = item; // don't change it
1543         enumer.Path = us2fs(pathPrefix);
1544         enumer.Path += fi.Name;
1545         enumer.Enumerate(0); // level
1546         item = enumer.bi;
1547         if (item.NumRootItems == 1)
1548         {
1549           item.SubFileIndex = (int)files.Size();
1550           files.Add(enumer.fi_SubFile);
1551         }
1552         cnt += item.NumDirs;
1553         cnt += item.NumFiles;
1554         if (cnt > 1000)
1555           waitCursor.Set();
1556       }
1557     }
1558   }
1559   _items = items;
1560   _files = files;
1561 
1562   DirPrefix = pathPrefix;
1563 
1564   EnableItem(IDB_BROWSE2_PARENT, !IsExactTempFolder(pathPrefix));
1565 
1566   SetItemText(IDT_BROWSE2_FOLDER, DirPrefix);
1567 
1568   _list.SetRedraw(false);
1569   _list.DeleteAllItems();
1570 
1571   LVITEMW item;
1572 
1573   unsigned index = 0;
1574   int cursorIndex = -1;
1575 
1576   #ifndef Z7_SFX
1577   if (_showDots && _topDirPrefix != DirPrefix)
1578   {
1579     item.iItem = (int)index;
1580     const UString itemName ("..");
1581     if (focusedName == itemName)
1582       cursorIndex = (int)index;
1583     /*
1584     if (selectedVector.IsEmpty()
1585         // && focusedName.IsEmpty()
1586         // && focusedName == ".."
1587         )
1588       cursorIndex = (int)index;
1589     */
1590     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
1591     unsigned subItem = 0;
1592     item.iSubItem = (int)(subItem++);
1593     item.lParam = kParentIndex;
1594     item.pszText = itemName.Ptr_non_const();
1595     item.iImage = _extToIconMap.GetIconIndex(FILE_ATTRIBUTE_DIRECTORY, DirPrefix);
1596     if (item.iImage < 0)
1597       item.iImage = 0;
1598     _list.InsertItem(&item);
1599 #if 0
1600     for (int k = 1; k < 6; k++)
1601       _list.SetSubItem(index, subItem++, L"2");
1602 #endif
1603     index++;
1604   }
1605   #endif
1606 
1607   for (unsigned i = 0; i < _items.Size(); i++, index++)
1608   {
1609     item.iItem = (int)index;
1610     const CBrowseItem &bi = _items[i];
1611     const CFileInfo &fi = _files[bi.MainFileIndex];
1612     const UString name = fs2us(fi.Name);
1613     // if (!selectedName.IsEmpty() && CompareFileNames(name, selectedName) == 0)
1614     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
1615     item.state = 0;
1616     if (selectedVector.FindInSorted(name) != -1)
1617     {
1618       /*
1619       if (cursorIndex == -1)
1620         cursorIndex = (int)index;
1621       */
1622       item.mask |= LVIF_STATE;
1623       item.state |= LVIS_SELECTED;
1624     }
1625     if (focusedName == name)
1626     {
1627       if (cursorIndex == -1)
1628         cursorIndex = (int)index;
1629       item.mask |= LVIF_STATE;
1630       item.state |= LVIS_FOCUSED;
1631     }
1632 
1633     unsigned subItem = 0;
1634     item.iSubItem = (int)(subItem++);
1635     item.lParam = (LPARAM)i;
1636     item.pszText = name.Ptr_non_const();
1637 
1638     const UString fullPath = DirPrefix + name;
1639     #ifndef UNDER_CE
1640     if (isDrive)
1641     {
1642       item.iImage = Shell_GetFileInfo_SysIconIndex_for_Path(
1643           fi.Name + FCHAR_PATH_SEPARATOR,
1644           FILE_ATTRIBUTE_DIRECTORY);
1645     }
1646     else
1647     #endif
1648       item.iImage = _extToIconMap.GetIconIndex(fi.Attrib, fullPath);
1649     if (item.iImage < 0)
1650         item.iImage = 0;
1651     _list.InsertItem(&item);
1652     wchar_t s[64];
1653     {
1654       s[0] = 0;
1655       if (!FILETIME_IsZero(fi.MTime))
1656         ConvertUtcFileTimeToString(fi.MTime, s,
1657             #ifndef UNDER_CE
1658               kTimestampPrintLevel_MIN
1659             #else
1660               kTimestampPrintLevel_DAY
1661             #endif
1662               );
1663       _list.SetSubItem(index, subItem++, s);
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