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