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