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