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 53using namespace NWindows; 54using namespace NFile; 55using namespace NName; 56using namespace NFind; 57 58static 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 65extern bool g_LVN_ITEMACTIVATE_Support; 66 67static const int kParentIndex = -1; 68static const UINT k_Message_RefreshPathEdit = WM_APP + 1; 69 70extern UString HResultToMessage(HRESULT errorCode); 71 72static 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 83class 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 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 123 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 131public: 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 141 CBrowseDialog(): 142 #ifndef Z7_SFX 143 _showDots(false), 144 #endif 145 SaveMode(false) 146 , FolderMode(false) 147 , FilterIndex(-1) 148 {} 149 INT_PTR Create(HWND parent = NULL) { return CModalDialog::Create(IDD_BROWSE, parent); } 150 int CompareItems(LPARAM lParam1, LPARAM lParam2) const; 151}; 152 153 154bool 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 292bool 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 343bool 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 354bool 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 371bool 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 414bool 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 438bool 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 450void 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 463bool 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 484int 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 506static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData) 507{ 508 return ((CBrowseDialog *)lpData)->CompareItems(lParam1, lParam2); 509} 510 511static 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 529HRESULT 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 691HRESULT 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 705void CBrowseDialog::OpenParentFolder() 706{ 707 UString parent, selected; 708 if (GetParentPath(DirPrefix, parent, selected)) 709 { 710 Reload(parent, selected); 711 SetPathEditText(); 712 } 713} 714 715void 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 742void 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 776void 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 808void 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 829bool 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 862bool 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 962static 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 974bool 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 1069bool CorrectFsPath(const UString & /* relBase */, const UString &path, UString &result) 1070{ 1071 result = path; 1072 return true; 1073} 1074 1075#endif 1076 1077bool 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