// PanelFolderChange.cpp

#include "StdAfx.h"

#include "../../../Common/StringConvert.h"
#include "../../../Common/Wildcard.h"

#include "../../../Windows/FileName.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/PropVariant.h"

#include "../../PropID.h"

#ifdef UNDER_CE
#include "FSFolder.h"
#else
#include "FSDrives.h"
#endif
#include "LangUtils.h"
#include "ListViewDialog.h"
#include "Panel.h"
#include "RootFolder.h"
#include "ViewSettings.h"

#include "resource.h"

using namespace NWindows;
using namespace NFile;
using namespace NFind;

void CPanel::ReleaseFolder()
{
  DeleteListItems();

  _folder.Release();

  _folderCompare.Release();
  _folderGetItemName.Release();
  _folderRawProps.Release();
  _folderAltStreams.Release();
  _folderOperations.Release();
  
  _thereAreDeletedItems = false;
}

void CPanel::SetNewFolder(IFolderFolder *newFolder)
{
  ReleaseFolder();
  _folder = newFolder;
  if (_folder)
  {
    _folder.QueryInterface(IID_IFolderCompare, &_folderCompare);
    _folder.QueryInterface(IID_IFolderGetItemName, &_folderGetItemName);
    _folder.QueryInterface(IID_IArchiveGetRawProps, &_folderRawProps);
    _folder.QueryInterface(IID_IFolderAltStreams, &_folderAltStreams);
    _folder.QueryInterface(IID_IFolderOperations, &_folderOperations);
  }
}

void CPanel::SetToRootFolder()
{
  ReleaseFolder();
  _library.Free();
  
  CRootFolder *rootFolderSpec = new CRootFolder;
  SetNewFolder(rootFolderSpec);
  rootFolderSpec->Init();
}


static bool DoesNameContainWildcard_SkipRoot(const UString &path)
{
  return DoesNameContainWildcard(path.Ptr(NName::GetRootPrefixSize(path)));
}

HRESULT CPanel::BindToPath(const UString &fullPath, const UString &arcFormat, COpenResult &openRes)
{
  UString path = fullPath;
  #ifdef _WIN32
  path.Replace(L'/', WCHAR_PATH_SEPARATOR);
  #endif

  openRes.ArchiveIsOpened = false;
  openRes.Encrypted = false;
  
  CDisableTimerProcessing disableTimerProcessing(*this);
  CDisableNotify disableNotify(*this);

  for (; !_parentFolders.IsEmpty(); CloseOneLevel())
  {
    // ---------- we try to use open archive ----------

    const CFolderLink &link = _parentFolders.Back();
    const UString &virtPath = link.VirtualPath;
    if (!path.IsPrefixedBy(virtPath))
      continue;
    UString relatPath = path.Ptr(virtPath.Len());
    if (!relatPath.IsEmpty())
    {
      if (!IS_PATH_SEPAR(relatPath[0]))
        continue;
      else
        relatPath.Delete(0);
    }
    
    UString relatPath2 = relatPath;
    if (!relatPath2.IsEmpty() && !IS_PATH_SEPAR(relatPath2.Back()))
      relatPath2.Add_PathSepar();

    for (;;)
    {
      const UString foldPath = GetFolderPath(_folder);
      if (relatPath2 == foldPath)
        break;
      if (relatPath.IsPrefixedBy(foldPath))
      {
        path = relatPath.Ptr(foldPath.Len());
        break;
      }
      CMyComPtr<IFolderFolder> newFolder;
      if (_folder->BindToParentFolder(&newFolder) != S_OK)
        throw 20140918;
      if (!newFolder) // we exit from loop above if (relatPath.IsPrefixedBy(empty path for root folder)
        throw 20140918;
      SetNewFolder(newFolder);
    }
    break;
  }

  if (_parentFolders.IsEmpty())
  {
    // ---------- we open file or folder from file system ----------

    CloseOpenFolders();
    UString sysPath = path;
    /* we will Empty() sysPath variable, if we need to BindToFolder()
       directly with (path) variable */
    const unsigned prefixSize = NName::GetRootPrefixSize(sysPath);
    if (prefixSize == 0 || sysPath[prefixSize] == 0)
      sysPath.Empty();
    
    #if defined(_WIN32) && !defined(UNDER_CE)
    if (!sysPath.IsEmpty() && sysPath.Back() == ':' &&
      (sysPath.Len() != 2 || !NName::IsDrivePath2(sysPath)))
    {
      // if base item for alt streams prefix "base:" exists, we will use it
      UString baseFile = sysPath;
      baseFile.DeleteBack();
      if (NFind::DoesFileOrDirExist(us2fs(baseFile)))
        sysPath.Empty();
    }
    #endif
    
    CFileInfo fileInfo;
    
    while (!sysPath.IsEmpty())
    {
      if (sysPath.Len() <= prefixSize)
      {
        path.DeleteFrom(prefixSize);
        sysPath.Empty();
        break;
      }

      fileInfo.ClearBase();
      if (IsPathSepar(sysPath.Back()))
      {
        /* Windows 10 by default doesn't allow look "Local Settings" that is junction to "AppData\Local",
           but it does allow look "Local Settings\Temp\*"
           22.02: at first we try to use paths with slashes "path\" */
        CFileInfo fi;
        // CFindFile findFile;
        // FString path2 = us2fs(sysPath);
        // path2 += '*'; // CHAR_ANY_MASK;
        // if (findFile.FindFirst(path2, fi))
        CEnumerator enumerator;
        enumerator.SetDirPrefix(us2fs(sysPath));
        bool found = false;
        if (enumerator.Next(fi, found))
        {
          // sysPath.DeleteBack();
          fileInfo.SetAsDir();
          fileInfo.Size = 0;
          fileInfo.Name.Empty();
          break;
        }
        sysPath.DeleteBack();
        continue;
      }

      if (fileInfo.Find(us2fs(sysPath)))
        break;
      int pos = sysPath.ReverseFind_PathSepar();
      if (pos < 0)
      {
        sysPath.Empty();
        break;
      }
      {
        if ((unsigned)pos != sysPath.Len() - 1)
          pos++;
        sysPath.DeleteFrom((unsigned)pos);
      }
    }
    
    SetToRootFolder();

    CMyComPtr<IFolderFolder> newFolder;
  
    if (sysPath.IsEmpty())
    {
      _folder->BindToFolder(path, &newFolder);
    }
    else if (fileInfo.IsDir())
    {
      #ifdef _WIN32
      if (DoesNameContainWildcard_SkipRoot(sysPath))
      {
        FString dirPrefix, fileName;
        NDir::GetFullPathAndSplit(us2fs(sysPath), dirPrefix, fileName);
        if (DoesNameContainWildcard_SkipRoot(fs2us(dirPrefix)))
          return E_INVALIDARG;
        sysPath = fs2us(dirPrefix + fileInfo.Name);
      }
      #endif

      NName::NormalizeDirPathPrefix(sysPath);
      _folder->BindToFolder(sysPath, &newFolder);
    }
    else
    {
      FString dirPrefix, fileName;
      
      NDir::GetFullPathAndSplit(us2fs(sysPath), dirPrefix, fileName);

      HRESULT res = S_OK;
      
      #ifdef _WIN32
      if (DoesNameContainWildcard_SkipRoot(fs2us(dirPrefix)))
        return E_INVALIDARG;

      if (DoesNameContainWildcard(fs2us(fileName)))
        res = S_FALSE;
      else
      #endif
      {
        CTempFileInfo tfi;
        tfi.RelPath = fs2us(fileName);
        tfi.FolderPath = dirPrefix;
        tfi.FilePath = us2fs(sysPath);
        res = OpenAsArc(NULL, tfi, sysPath, arcFormat, openRes);
      }
      
      if (res == S_FALSE)
        _folder->BindToFolder(fs2us(dirPrefix), &newFolder);
      else
      {
        RINOK(res)
        openRes.ArchiveIsOpened = true;
        _parentFolders.Back().ParentFolderPath = fs2us(dirPrefix);
        path.DeleteFrontal(sysPath.Len());
        if (!path.IsEmpty() && IS_PATH_SEPAR(path[0]))
          path.Delete(0);
      }
    }
    
    if (newFolder)
    {
      SetNewFolder(newFolder);
      // LoadFullPath();
      return S_OK;
    }
  }
  
  {
    // ---------- we open folder remPath in archive and sub archives ----------

    for (unsigned curPos = 0; curPos != path.Len();)
    {
      UString s = path.Ptr(curPos);
      const int slashPos = NName::FindSepar(s);
      unsigned skipLen = s.Len();
      if (slashPos >= 0)
      {
        s.DeleteFrom((unsigned)slashPos);
        skipLen = (unsigned)slashPos + 1;
      }

      CMyComPtr<IFolderFolder> newFolder;
      _folder->BindToFolder(s, &newFolder);
      if (newFolder)
        curPos += skipLen;
      else if (_folderAltStreams)
      {
        const int pos = s.Find(L':');
        if (pos >= 0)
        {
          UString baseName = s;
          baseName.DeleteFrom((unsigned)pos);
          if (_folderAltStreams->BindToAltStreams(baseName, &newFolder) == S_OK && newFolder)
            curPos += (unsigned)pos + 1;
        }
      }
      
      if (!newFolder)
        break;

      SetNewFolder(newFolder);
    }
  }

  return S_OK;
}

HRESULT CPanel::BindToPathAndRefresh(const UString &path)
{
  CDisableTimerProcessing disableTimerProcessing(*this);
  CDisableNotify disableNotify(*this);
  COpenResult openRes;
  UString s = path;
  
  #ifdef _WIN32
    if (!s.IsEmpty() && s[0] == '\"' && s.Back() == '\"')
    {
      s.DeleteBack();
      s.Delete(0);
    }
  #endif

  HRESULT res = BindToPath(s, UString(), openRes);
  RefreshListCtrl();
  return res;
}

void CPanel::SetBookmark(unsigned index)
{
  _appState->FastFolders.SetString(index, _currentFolderPrefix);
}

void CPanel::OpenBookmark(unsigned index)
{
  BindToPathAndRefresh(_appState->FastFolders.GetString(index));
}

UString GetFolderPath(IFolderFolder *folder)
{
  {
    NCOM::CPropVariant prop;
    if (folder->GetFolderProperty(kpidPath, &prop) == S_OK)
      if (prop.vt == VT_BSTR)
        return (wchar_t *)prop.bstrVal;
  }
  return UString();
}

void CPanel::LoadFullPath()
{
  _currentFolderPrefix.Empty();
  FOR_VECTOR (i, _parentFolders)
  {
    const CFolderLink &folderLink = _parentFolders[i];
    _currentFolderPrefix += folderLink.ParentFolderPath;
        // GetFolderPath(folderLink.ParentFolder);
    _currentFolderPrefix += folderLink.RelPath;
    _currentFolderPrefix.Add_PathSepar();
  }
  if (_folder)
    _currentFolderPrefix += GetFolderPath(_folder);
}



static int GetRealIconIndex_for_DirPath(CFSTR path, DWORD attrib)
{
  attrib |= FILE_ATTRIBUTE_DIRECTORY; // optional
  int index = -1;
  if (Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef(path, attrib, index))
    if (index >= 0)
      return index;
  return g_Ext_to_Icon_Map.GetIconIndex_DIR(attrib);
}


extern UString RootFolder_GetName_Computer(int &iconIndex);
extern UString RootFolder_GetName_Network(int &iconIndex);
extern UString RootFolder_GetName_Documents(int &iconIndex);


static int Find_FileExtension_DotPos_in_path(const wchar_t *path)
{
  int dotPos = -1;
  unsigned i;
  for (i = 0;; i++)
  {
    const wchar_t c = path[i];
    if (c == 0)
      return dotPos;
    if (c == '.')
      dotPos = (int)i;
    else if (IS_PATH_SEPAR(c) || c == ':')
      dotPos = -1;
  }
}


void CPanel::LoadFullPathAndShow()
{
  LoadFullPath();
  _appState->FolderHistory.AddString(_currentFolderPrefix);

  _headerComboBox.SetText(_currentFolderPrefix);

  #ifndef UNDER_CE

  COMBOBOXEXITEM item;
  item.mask = 0;
  item.iImage = -1;

  UString path = _currentFolderPrefix;
  // path = "\\\\.\\PhysicalDrive1\\"; // for debug
  // path = "\\\\.\\y:\\"; // for debug
  if (!path.IsEmpty())
  {
    const unsigned rootPrefixSize = NName::GetRootPrefixSize(path);
    if (rootPrefixSize == 0 && path[0] != '\\')
    {
      int iconIndex = -1;
      UString name_Computer = RootFolder_GetName_Computer(iconIndex);
      name_Computer.Add_PathSepar();
      if (path == name_Computer
          || path == L"\\\\?\\")
        item.iImage = iconIndex;
      else
      {
        UString name = RootFolder_GetName_Network(iconIndex);
        name.Add_PathSepar();
        if (path == name)
          item.iImage = iconIndex;
      }
    }

    if (item.iImage < 0)
    {
      if (rootPrefixSize == 0 || rootPrefixSize == path.Len())
      {
        DWORD attrib = FILE_ATTRIBUTE_DIRECTORY;
        CFileInfo info;
        if (info.Find(us2fs(path)))
          attrib = info.Attrib;
        NName::If_IsSuperPath_RemoveSuperPrefix(path);
        item.iImage = GetRealIconIndex_for_DirPath(us2fs(path), attrib);
      }
      else if (rootPrefixSize == NName::kDevicePathPrefixSize
          && NName::IsDevicePath(us2fs(path.Left(path.Len() - 1))))
      {
        if (path.IsPrefixedBy_Ascii_NoCase("\\\\.\\"))
          path.DeleteFrontal(4);
        if (path.Len() > 3) // is not "c:\\"
        {
          // PhysicalDrive
          if (path.Back() == '\\')
            path.DeleteBack();
        }
        item.iImage = Shell_GetFileInfo_SysIconIndex_for_Path(us2fs(path), FILE_ATTRIBUTE_ARCHIVE);
      }
      else
      {
        if (path.Back() == '\\')
          path.DeleteBack();
        bool need_Fs_Check = true;
        bool is_File = false;
        if (!_parentFolders.IsEmpty())
        {
          const CFolderLink &link = _parentFolders.Back();
          if (link.VirtualPath == path)
          {
            is_File = true;
            if (_parentFolders.Size() != 1)
              need_Fs_Check = false;
          }
          else
            need_Fs_Check = false;
        }
        if (need_Fs_Check)
        {
          CFileInfo info;
          const bool finded = info.Find(us2fs(path));
          DWORD attrib = FILE_ATTRIBUTE_DIRECTORY;
          if (finded)
            attrib = info.Attrib;
          item.iImage = Shell_GetFileInfo_SysIconIndex_for_Path(us2fs(path), attrib);
        }
        if (item.iImage <= 0 && is_File)
        {
          int dotPos = Find_FileExtension_DotPos_in_path(path);
          if (dotPos < 0)
            dotPos = (int)path.Len();
          item.iImage = g_Ext_to_Icon_Map.GetIconIndex(FILE_ATTRIBUTE_ARCHIVE, path.Ptr(dotPos));
        }
      }
    }
  }

  if (item.iImage < 0)
    item.iImage = g_Ext_to_Icon_Map.GetIconIndex_DIR();
  // if (item.iImage < 0) item.iImage = 0;
  // item.iImage = -1; // for debug
  if (item.iImage >= 0)
  {
    item.iSelectedImage = item.iImage;
    item.mask |= (CBEIF_IMAGE | CBEIF_SELECTEDIMAGE);
  }
  item.iItem = -1;
  _headerComboBox.SetItem(&item);
  
  #endif

  RefreshTitle();
}

#ifndef UNDER_CE
LRESULT CPanel::OnNotifyComboBoxEnter(const UString &s)
{
  if (BindToPathAndRefresh(GetUnicodeString(s)) == S_OK)
  {
    PostMsg(kSetFocusToListView);
    return TRUE;
  }
  return FALSE;
}

bool CPanel::OnNotifyComboBoxEndEdit(PNMCBEENDEDITW info, LRESULT &result)
{
  if (info->iWhy == CBENF_ESCAPE)
  {
    _headerComboBox.SetText(_currentFolderPrefix);
    PostMsg(kSetFocusToListView);
    result = FALSE;
    return true;
  }

  /*
  if (info->iWhy == CBENF_DROPDOWN)
  {
    result = FALSE;
    return true;
  }
  */

  if (info->iWhy == CBENF_RETURN)
  {
    // When we use Edit control and press Enter.
    UString s;
    _headerComboBox.GetText(s);
    result = OnNotifyComboBoxEnter(s);
    return true;
  }
  return false;
}
#endif

#ifndef _UNICODE
bool CPanel::OnNotifyComboBoxEndEdit(PNMCBEENDEDIT info, LRESULT &result)
{
  if (info->iWhy == CBENF_ESCAPE)
  {
    _headerComboBox.SetText(_currentFolderPrefix);
    PostMsg(kSetFocusToListView);
    result = FALSE;
    return true;
  }
  /*
  if (info->iWhy == CBENF_DROPDOWN)
  {
    result = FALSE;
    return true;
  }
  */

  if (info->iWhy == CBENF_RETURN)
  {
    UString s;
    _headerComboBox.GetText(s);
    // GetUnicodeString(info->szText)
    result = OnNotifyComboBoxEnter(s);
    return true;
  }
  return false;
}
#endif

void CPanel::AddComboBoxItem(const UString &name, int iconIndex, unsigned indent, bool addToList)
{
  #ifdef UNDER_CE

  UString s;
  iconIndex = iconIndex;
  for (unsigned i = 0; i < indent; i++)
    s += "  ";
  _headerComboBox.AddString(s + name);
  
  #else
  
  COMBOBOXEXITEMW item;
  item.mask = CBEIF_TEXT | CBEIF_INDENT;
  if (iconIndex < 0)
    iconIndex = g_Ext_to_Icon_Map.GetIconIndex_DIR();
  item.iSelectedImage = item.iImage = iconIndex;
  if (iconIndex >= 0)
    item.mask |= (CBEIF_IMAGE | CBEIF_SELECTEDIMAGE);
  item.iItem = -1;
  item.iIndent = (int)indent;
  item.pszText = name.Ptr_non_const();
  _headerComboBox.InsertItem(&item);
  
  #endif

  if (addToList)
  {
    UString s = name;
    s.Add_PathSepar();
    ComboBoxPaths.Add(s);
  }
}


bool CPanel::OnComboBoxCommand(UINT code, LPARAM /* param */, LRESULT &result)
{
  result = FALSE;
  switch (code)
  {
    case CBN_DROPDOWN:
    {
      ComboBoxPaths.Clear();
      _headerComboBox.ResetContent();
      
      UString sumPath;
      UStringVector pathParts;
      unsigned indent = 0;
      {
        UString path = _currentFolderPrefix;
        // path = L"\\\\.\\y:\\"; // for debug
        UString prefix0;
        if (path.IsPrefixedBy_Ascii_NoCase("\\\\"))
        {
          const int separ = FindCharPosInString(path.Ptr(2), '\\');
          if (separ > 0
            && (separ > 1 || path[2] != '.')) // "\\\\.\\" will be processed later
          {
            const UString s = path.Left(2 + separ);
            prefix0 = s;
            prefix0.Add_PathSepar();
            AddComboBoxItem(s,
                GetRealIconIndex_for_DirPath(us2fs(prefix0), FILE_ATTRIBUTE_DIRECTORY),
                indent++,
                false); // addToList
            ComboBoxPaths.Add(prefix0);
          }
        }
        
        unsigned rootPrefixSize = NName::GetRootPrefixSize(path);

        sumPath = path;
        
        if (rootPrefixSize <= prefix0.Len())
        {
          rootPrefixSize = prefix0.Len();
          sumPath.DeleteFrom(rootPrefixSize);
        }
        else
        {
          // rootPrefixSize > prefix0.Len()
          sumPath.DeleteFrom(rootPrefixSize);
          
          CFileInfo info;
          DWORD attrib = FILE_ATTRIBUTE_DIRECTORY;
          if (info.Find(us2fs(sumPath)) && info.IsDir())
            attrib = info.Attrib;
          UString s = sumPath.Ptr(prefix0.Len());
          if (!s.IsEmpty())
          {
            const wchar_t c = s.Back();
            if (IS_PATH_SEPAR(c))
              s.DeleteBack();
          }
          UString path_for_icon = sumPath;
          NName::If_IsSuperPath_RemoveSuperPrefix(path_for_icon);
          
          AddComboBoxItem(s,
              GetRealIconIndex_for_DirPath(us2fs(path_for_icon), attrib),
              indent++,
              false); // addToList
          ComboBoxPaths.Add(sumPath);
        }
          
        path.DeleteFrontal(rootPrefixSize);
        SplitPathToParts(path, pathParts);
      }

      // it's expected that pathParts.Back() is empty, because _currentFolderPrefix has PathSeparator.
      unsigned next_Arc_index = 0;
      int iconIndex_Computer;
      const UString name_Computer = RootFolder_GetName_Computer(iconIndex_Computer);

      // const bool is_devicePrefix = (sumPath == L"\\\\.\\");

      if (pathParts.Size() > 1)
      if (!sumPath.IsEmpty()
          || pathParts.Size() != 2
          || pathParts[0] != name_Computer)
      for (unsigned i = 0; i + 1 < pathParts.Size(); i++)
      {
        UString name = pathParts[i];
        sumPath += name;

        bool isRootDir_inLink = false;
        if (next_Arc_index < _parentFolders.Size())
        {
          const CFolderLink &link = _parentFolders[next_Arc_index];
          if (link.VirtualPath == sumPath)
          {
            isRootDir_inLink = true;
            next_Arc_index++;
          }
        }
        
        int iconIndex = -1;
        DWORD attrib = isRootDir_inLink ?
            FILE_ATTRIBUTE_ARCHIVE:
            FILE_ATTRIBUTE_DIRECTORY;
        if (next_Arc_index == 0
            || (next_Arc_index == 1 && isRootDir_inLink))
        {
          if (i == 0 && NName::IsDevicePath(us2fs(sumPath)))
          {
            UString path = name;
            path.Add_PathSepar();
            attrib = FILE_ATTRIBUTE_ARCHIVE;
              // FILE_ATTRIBUTE_DIRECTORY;
          }
          else
          {
            CFileInfo info;
            if (info.Find(us2fs(sumPath)))
              attrib = info.Attrib;
          }
          iconIndex = Shell_GetFileInfo_SysIconIndex_for_Path(us2fs(sumPath), attrib);
        }
        
        if (iconIndex < 0)
          iconIndex = g_Ext_to_Icon_Map.GetIconIndex(attrib, name);
        // iconIndex = -1; // for debug
        if (iconIndex < 0 && isRootDir_inLink)
          iconIndex = 0; // default file

        sumPath.Add_PathSepar();

        ComboBoxPaths.Add(sumPath);
        if (name.IsEmpty())
          name.Add_PathSepar();
        AddComboBoxItem(name, iconIndex, indent++,
            false); // addToList
      }

#ifndef UNDER_CE

      {
        int iconIndex;
        const UString name = RootFolder_GetName_Documents(iconIndex);
        // iconIndex = -1; // for debug
        AddComboBoxItem(name, iconIndex, 0, true);
      }
      AddComboBoxItem(name_Computer, iconIndex_Computer, 0, true);
      {
        FStringVector driveStrings;
        MyGetLogicalDriveStrings(driveStrings);
        FOR_VECTOR (i, driveStrings)
        {
          FString s = driveStrings[i];
          ComboBoxPaths.Add(fs2us(s));
          int iconIndex2 = GetRealIconIndex_for_DirPath(s, FILE_ATTRIBUTE_DIRECTORY);
          if (!s.IsEmpty())
          {
            const FChar c = s.Back();
            if (IS_PATH_SEPAR(c))
              s.DeleteBack();
          }
          // iconIndex2 = -1; // for debug
          AddComboBoxItem(fs2us(s), iconIndex2, 1, false);
        }
      }
      {
        int iconIndex;
        const UString name = RootFolder_GetName_Network(iconIndex);
        AddComboBoxItem(name, iconIndex, 0, true);
      }

#endif
    
      return false;
    }

    case CBN_SELENDOK:
    {
      int index = _headerComboBox.GetCurSel();
      if (index >= 0)
      {
        const UString path = ComboBoxPaths[index];
        _headerComboBox.SetCurSel(-1);
        // _headerComboBox.SetText(pass); // it's fix for selecting by mouse.
        if (BindToPathAndRefresh(path) == S_OK)
        {
          PostMsg(kSetFocusToListView);
          #ifdef UNDER_CE
          PostMsg(kRefresh_HeaderComboBox);
          #endif
          return true;
        }
      }
      return false;
    }
    /*
    case CBN_CLOSEUP:
    {
      LoadFullPathAndShow();
      true;

    }
    case CBN_SELCHANGE:
    {
      // LoadFullPathAndShow();
      return true;
    }
    */
  }
  return false;
}

bool CPanel::OnNotifyComboBox(LPNMHDR NON_CE_VAR(header), LRESULT & NON_CE_VAR(result))
{
  #ifndef UNDER_CE
  switch (header->code)
  {
    case CBEN_BEGINEDIT:
    {
      _lastFocusedIsList = false;
      _panelCallback->PanelWasFocused();
      break;
    }
    #ifndef _UNICODE
    case CBEN_ENDEDIT:
    {
      return OnNotifyComboBoxEndEdit((PNMCBEENDEDIT)header, result);
    }
    #endif
    case CBEN_ENDEDITW:
    {
      return OnNotifyComboBoxEndEdit((PNMCBEENDEDITW)header, result);
    }
  }
  #endif
  return false;
}


void CPanel::FoldersHistory()
{
  CListViewDialog listViewDialog;
  listViewDialog.DeleteIsAllowed = true;
  listViewDialog.SelectFirst = true;
  LangString(IDS_FOLDERS_HISTORY, listViewDialog.Title);
  _appState->FolderHistory.GetList(listViewDialog.Strings);
  if (listViewDialog.Create(GetParent()) != IDOK)
    return;
  UString selectString;
  if (listViewDialog.StringsWereChanged)
  {
    _appState->FolderHistory.RemoveAll();
    for (int i = (int)listViewDialog.Strings.Size() - 1; i >= 0; i--)
      _appState->FolderHistory.AddString(listViewDialog.Strings[i]);
    if (listViewDialog.FocusedItemIndex >= 0)
      selectString = listViewDialog.Strings[listViewDialog.FocusedItemIndex];
  }
  else
  {
    if (listViewDialog.FocusedItemIndex >= 0)
      selectString = listViewDialog.Strings[listViewDialog.FocusedItemIndex];
  }
  if (listViewDialog.FocusedItemIndex >= 0)
    BindToPathAndRefresh(selectString);
}


UString CPanel::GetParentDirPrefix() const
{
  UString s;
  if (!_currentFolderPrefix.IsEmpty())
  {
    wchar_t c = _currentFolderPrefix.Back();
    if (IS_PATH_SEPAR(c) || c == ':')
    {
      s = _currentFolderPrefix;
      s.DeleteBack();
      if (s != L"\\\\." &&
          s != L"\\\\?")
      {
        int pos = s.ReverseFind_PathSepar();
        if (pos >= 0)
          s.DeleteFrom((unsigned)(pos + 1));
      }
    }
  }
  return s;
}


void CPanel::OpenParentFolder()
{
  LoadFullPath(); // Maybe we don't need it ??
  
  UString parentFolderPrefix;
  UString focusedName;
  
  if (!_currentFolderPrefix.IsEmpty())
  {
    wchar_t c = _currentFolderPrefix.Back();
    if (IS_PATH_SEPAR(c) || c == ':')
    {
      focusedName = _currentFolderPrefix;
      focusedName.DeleteBack();
      /*
      if (c == ':' && !focusedName.IsEmpty() && IS_PATH_SEPAR(focusedName.Back()))
      {
        focusedName.DeleteBack();
      }
      else
      */
      if (focusedName != L"\\\\." &&
          focusedName != L"\\\\?")
      {
        const int pos = focusedName.ReverseFind_PathSepar();
        if (pos >= 0)
        {
          parentFolderPrefix = focusedName;
          parentFolderPrefix.DeleteFrom((unsigned)(pos + 1));
          focusedName.DeleteFrontal((unsigned)(pos + 1));
        }
      }
    }
  }

  CDisableTimerProcessing disableTimerProcessing(*this);
  CDisableNotify disableNotify(*this);
  
  CMyComPtr<IFolderFolder> newFolder;
  _folder->BindToParentFolder(&newFolder);

  // newFolder.Release(); // for test
  
  if (newFolder)
    SetNewFolder(newFolder);
  else
  {
    bool needSetFolder = true;
    if (!_parentFolders.IsEmpty())
    {
      {
        const CFolderLink &link = _parentFolders.Back();
        parentFolderPrefix = link.ParentFolderPath;
        focusedName = link.RelPath;
      }
      CloseOneLevel();
      needSetFolder = (!_folder);
    }
    
    if (needSetFolder)
    {
      {
        COpenResult openRes;
        BindToPath(parentFolderPrefix, UString(), openRes);
      }
    }
  }
    
  CSelectedState state;
  state.FocusedName = focusedName;
  state.FocusedName_Defined = true;
  /*
  if (!focusedName.IsEmpty())
    state.SelectedNames.Add(focusedName);
  */
  LoadFullPath();
  // ::SetCurrentDirectory(::_currentFolderPrefix);
  RefreshListCtrl(state);
  // _listView.EnsureVisible(_listView.GetFocusedItem(), false);
}


void CPanel::CloseOneLevel()
{
  ReleaseFolder();
  _library.Free();
  {
    CFolderLink &link = _parentFolders.Back();
    if (link.ParentFolder)
      SetNewFolder(link.ParentFolder);
    _library.Attach(link.Library.Detach());
  }
  if (_parentFolders.Size() > 1)
    OpenParentArchiveFolder();
  _parentFolders.DeleteBack();
  if (_parentFolders.IsEmpty())
    _flatMode = _flatModeForDisk;
}

void CPanel::CloseOpenFolders()
{
  while (!_parentFolders.IsEmpty())
    CloseOneLevel();
  _flatMode = _flatModeForDisk;
  ReleaseFolder();
  _library.Free();
}

void CPanel::OpenRootFolder()
{
  CDisableTimerProcessing disableTimerProcessing(*this);
  CDisableNotify disableNotify(*this);
  _parentFolders.Clear();
  SetToRootFolder();
  RefreshListCtrl();
  // ::SetCurrentDirectory(::_currentFolderPrefix);
  /*
  BeforeChangeFolder();
  _currentFolderPrefix.Empty();
  AfterChangeFolder();
  SetCurrentPathText();
  RefreshListCtrl(UString(), 0, UStringVector());
  _listView.EnsureVisible(_listView.GetFocusedItem(), false);
  */
}

void CPanel::OpenDrivesFolder()
{
  CloseOpenFolders();
  #ifdef UNDER_CE
  NFsFolder::CFSFolder *folderSpec = new NFsFolder::CFSFolder;
  SetNewFolder(folderSpec);
  folderSpec->InitToRoot();
  #else
  CFSDrives *folderSpec = new CFSDrives;
  SetNewFolder(folderSpec);
  folderSpec->Init();
  #endif
  RefreshListCtrl();
}

void CPanel::OpenFolder(unsigned index)
{
  if (index == kParentIndex)
  {
    OpenParentFolder();
    return;
  }
  CMyComPtr<IFolderFolder> newFolder;
  const HRESULT res = _folder->BindToFolder((unsigned)index, &newFolder);
  if (res != 0)
  {
    MessageBox_Error_HRESULT(res);
    return;
  }
  if (!newFolder)
    return;
  SetNewFolder(newFolder);
  LoadFullPath();
  RefreshListCtrl();
  // 17.02: fixed : now we don't select first item
  // _listView.SetItemState_Selected(_listView.GetFocusedItem());
  _listView.EnsureVisible(_listView.GetFocusedItem(), false);
}

void CPanel::OpenAltStreams()
{
  CRecordVector<UInt32> indices;
  Get_ItemIndices_Operated(indices);
  Int32 realIndex = -1;
  if (indices.Size() > 1)
    return;
  if (indices.Size() == 1)
    realIndex = (Int32)indices[0];

  if (_folderAltStreams)
  {
    CMyComPtr<IFolderFolder> newFolder;
    _folderAltStreams->BindToAltStreams((UInt32)realIndex, &newFolder);
    if (newFolder)
    {
      CDisableTimerProcessing disableTimerProcessing(*this);
      CDisableNotify disableNotify(*this);
      SetNewFolder(newFolder);
      RefreshListCtrl();
      return;
    }
    return;
  }
  
  #if defined(_WIN32) && !defined(UNDER_CE)
  UString path;
  if (realIndex >= 0)
    path = GetItemFullPath((UInt32)realIndex);
  else
  {
    path = GetFsPath();
    if (!NName::IsDriveRootPath_SuperAllowed(us2fs(path)))
      if (!path.IsEmpty() && IS_PATH_SEPAR(path.Back()))
        path.DeleteBack();
  }

  path.Add_Colon();
  BindToPathAndRefresh(path);
  #endif
}
