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