Home | History | Annotate | Download | only in FileManager
      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 
     50 using namespace NWindows;
     51 using namespace NFile;
     52 using namespace NName;
     53 using namespace NFind;
     54 
     55 #ifdef USE_MY_BROWSE_DIALOG
     56 
     57 extern bool g_LVN_ITEMACTIVATE_Support;
     58 
     59 static const int kParentIndex = -1;
     60 static const UINT k_Message_RefreshPathEdit = WM_APP + 1;
     61 
     62 static HRESULT GetNormalizedError()
     63 {
     64   DWORD errorCode = GetLastError();
     65   return errorCode == 0 ? E_FAIL : errorCode;
     66 }
     67 
     68 extern UString HResultToMessage(HRESULT errorCode);
     69 
     70 static void MessageBox_Error_Global(HWND wnd, const wchar_t *message)
     71 {
     72   ::MessageBoxW(wnd, message, L"7-Zip", MB_ICONERROR);
     73 }
     74 
     75 static 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 
     86 class 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 
    130 public:
    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 
    144 void 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 
    175 bool 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 
    302 bool 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 
    353 bool 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 
    363 bool 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 
    406 bool 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 
    429 bool 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 
    441 void 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 
    454 bool 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 
    475 int 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 
    497 static int CALLBACK CompareItems2(LPARAM lParam1, LPARAM lParam2, LPARAM lpData)
    498 {
    499   return ((CBrowseDialog *)lpData)->CompareItems(lParam1, lParam2);
    500 }
    501 
    502 static 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 
    520 HRESULT 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 
    668 HRESULT 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 
    682 void CBrowseDialog::OpenParentFolder()
    683 {
    684   UString parent, selected;
    685   if (GetParentPath(DirPrefix, parent, selected))
    686   {
    687     Reload(parent, selected);
    688     SetPathEditText();
    689   }
    690 }
    691 
    692 void 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 
    719 void 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 
    753 void 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 
    783 void 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 
    801 bool 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 
    830 bool 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 
    890 static 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 
    902 bool 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
    990 bool CorrectFsPath(const UString & /* relBase */, const UString &path, UString &result)
    991 {
    992   result = path;
    993   return true;
    994 }
    995 #endif
    996 
    997 bool 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