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