Home | History | Annotate | Download | only in FileManager
      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 
     54 using namespace NWindows;
     55 using namespace NFile;
     56 using namespace NName;
     57 using namespace NFind;
     58 
     59 #ifdef USE_MY_BROWSE_DIALOG
     60 
     61 extern bool g_LVN_ITEMACTIVATE_Support;
     62 
     63 static const int kParentIndex = -1;
     64 static const UINT k_Message_RefreshPathEdit = WM_APP + 1;
     65 
     66 static HRESULT GetNormalizedError()
     67 {
     68   DWORD errorCode = GetLastError();
     69   return errorCode == 0 ? E_FAIL : errorCode;
     70 }
     71 
     72 extern UString HResultToMessage(HRESULT errorCode);
     73 
     74 static void MessageBox_Error_Global(HWND wnd, const wchar_t *message)
     75 {
     76   ::MessageBoxW(wnd, message, L"7-Zip", MB_ICONERROR);
     77 }
     78 
     79 static 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 
     90 class 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 
    134 public:
    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 
    148 void 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 
    179 bool 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 
    310 bool 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 
    361 bool 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 
    371 bool CBrowseDialog::OnNotify(UINT /* controlID */, LPNMHDR header)
    372 {
    373   if (header->hwndFrom != _list)
    374     return false;
    375   switch (header->code)
    376   {
    377     case LVN_ITEMACTIVATE:
    378       if (g_LVN_ITEMACTIVATE_Support)
    379         OnItemEnter();
    380       break;
    381     case NM_DBLCLK:
    382     case NM_RETURN: // probabably it's unused
    383       if (!g_LVN_ITEMACTIVATE_Support)
    384         OnItemEnter();
    385       break;
    386     case LVN_COLUMNCLICK:
    387     {
    388       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 
    414 bool 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 
    437 bool 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 
    449 void 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 
    462 bool 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 
    483 int 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 
    505 static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
    506 {
    507   return ((CBrowseDialog *)lpData)->CompareItems(lParam1, lParam2);
    508 }
    509 
    510 static 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 
    528 HRESULT 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 
    676 HRESULT 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 
    690 void CBrowseDialog::OpenParentFolder()
    691 {
    692   UString parent, selected;
    693   if (GetParentPath(DirPrefix, parent, selected))
    694   {
    695     Reload(parent, selected);
    696     SetPathEditText();
    697   }
    698 }
    699 
    700 void 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 
    727 void 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 
    761 void 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 
    791 void 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 
    809 bool 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 
    838 bool 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 
    898 static 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 
    910 bool 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 
   1005 bool CorrectFsPath(const UString & /* relBase */, const UString &path, UString &result)
   1006 {
   1007   result = path;
   1008   return true;
   1009 }
   1010 
   1011 #endif
   1012 
   1013 bool 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