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