1
0
mirror of https://github.com/winscp/winscp.git synced 2026-10-24 02:28:55 +03:00
Files
winscp/source/components/UnixDriveView.cpp
Martin Prikryl d98b72806c Invalid assertion
Source commit: c2dc0d4c9ea0432ae7b469c22b6b792f71f3e026
2021-05-06 11:49:27 +02:00

925 lines
26 KiB
C++

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <Common.h>
#include "UnixDriveView.h"
#ifndef DESIGN_ONLY
#include <Terminal.h>
#include <RemoteFiles.h>
#include <VCLCommon.h>
#endif
#pragma package(smart_init)
//---------------------------------------------------------------------------
static inline void ValidCtrCheck(TUnixDriveView *)
{
new TUnixDriveView(NULL);
}
//---------------------------------------------------------------------------
namespace Unixdriveview
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TUnixDriveView)};
RegisterComponents(L"Scp", classes, 0);
}
}
//---------------------------------------------------------------------------
__fastcall TUnixDriveView::TUnixDriveView(TComponent * Owner) :
TCustomUnixDriveView(Owner)
{
}
//---------------------------------------------------------------------------
struct TNodeData
{
TRemoteFileList * FileList;
TRemoteFile * File;
UnicodeString Directory;
};
//---------------------------------------------------------------------------
__fastcall TCustomUnixDriveView::TCustomUnixDriveView(TComponent* Owner) :
TCustomDriveView(Owner)
{
FTerminal = NULL;
FRootName = Customunixdirview_SUnixDefaultRootName;
FIgnoreChange = false;
FPrevSelected = NULL;
FDDAllowMove = false;
FShowInaccesibleDirectories = true;
FDummyDragFile = NULL;
FPendingDelete = new TList();
FDragDropFilesEx->PreferCopy = true;
}
//---------------------------------------------------------------------------
__fastcall TCustomUnixDriveView::~TCustomUnixDriveView()
{
Terminal = NULL;
if (FDummyDragFile != NULL)
{
#ifndef DESIGN_ONLY
SAFE_DESTROY(FDummyDragFile);
#endif
}
SAFE_DESTROY(FPendingDelete);
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::CreateWnd()
{
TCustomDriveView::CreateWnd();
FDragDropFilesEx->TargetEffects = TDropEffectSet() << deCopy << deMove;
// in case the items were recreated
FPrevSelected = Selected;
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::DestroyWnd()
{
// in case we are recreating (TCustomTreeView.DestroyWnd deletes items)
FPrevSelected = NULL;
TCustomDriveView::DestroyWnd();
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::SetTerminal(TTerminal * value)
{
#ifndef DESIGN_ONLY
if ((FTerminal != value) || ((FTerminal != NULL) && !FTerminal->Active)) // Abused by TCustomScpExplorerForm::DisconnectSession
{
FTerminal = value;
Items->Clear();
// If terminal is not active initially, we will never load fixed paths, when it become active.
// But actually terminal is not active here, only when we are replacing an abandoned terminal
// with a dummy one (which will never become active)
if ((FTerminal != NULL) && FTerminal->Active)
{
TStrings * FixedPaths = FTerminal->FixedPaths;
if (FixedPaths != NULL)
{
for (int i = 0; i < FixedPaths->Count; i++)
{
LoadPath(FixedPaths->Strings[i]);
}
}
}
}
#else
DebugUsedParam(value);
#endif
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::SetDirView(TUnixDirView * Value)
{
if (FDirView != Value)
{
if (FDirView != NULL)
{
FDirView->DriveView = NULL;
}
FDirView = Value;
if (FDirView != NULL)
{
FDirView->DriveView = this;
}
}
}
//---------------------------------------------------------------------------
TCustomDirView * __fastcall TCustomUnixDriveView::GetCustomDirView()
{
return FDirView;
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::SetCustomDirView(TCustomDirView * Value)
{
SetDirView(dynamic_cast<TUnixDirView *>(Value));
}
//---------------------------------------------------------------------------
bool __fastcall TCustomUnixDriveView::IsRootNameStored()
{
return (FRootName != Customunixdirview_SUnixDefaultRootName);
}
//---------------------------------------------------------------------------
bool __fastcall TCustomUnixDriveView::NodeIsHidden(const TTreeNode * Node)
{
#ifndef DESIGN_ONLY
TNodeData * Data = NodeData(Node);
TRemoteFile * File = Data->File;
return
((File != NULL) && File->IsHidden) ||
((File == NULL) && IsUnixHiddenFile(UnixExtractFileName(Data->Directory)));
#else
return false;
#endif
}
//---------------------------------------------------------------------------
bool __fastcall TCustomUnixDriveView::NodeTryDelete(TTreeNode * Node, bool RememberIfFails)
{
bool Result =
(Selected == NULL) ||
((Selected != Node) && !Selected->HasAsParent(Node));
if (Result)
{
Node->Delete();
}
else
{
if (RememberIfFails)
{
int I = FPendingDelete->IndexOf(Node);
if (I < 0)
{
FPendingDelete->Add(Node);
}
}
}
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::UpdatePath(TTreeNode * Node, bool Force,
bool CanLoad)
{
#ifndef DESIGN_ONLY
TNodeData * Data = NodeData(Node);
UnicodeString Path = Data->Directory;
TDateTime Timestamp = (Data->FileList != NULL) ? Data->FileList->Timestamp : TDateTime();
TRemoteFileList * FileList = FTerminal->DirectoryFileList(Path, Timestamp, CanLoad);
if ((FileList != NULL) ||
((Data->FileList != NULL) && Force))
{
TStringList * ChildrenDirs = new TStringList();
TRemoteFileList * OldFileList = NULL;
try
{
if (FileList != NULL)
{
OldFileList = Data->FileList;
Data->FileList = FileList;
}
ChildrenDirs->Duplicates = Types::dupAccept;
ChildrenDirs->CaseSensitive = true;
bool FirstChild = true;
TTreeNode * ChildNode = Node->getFirstChild();
while (ChildNode != NULL)
{
TNodeData * ChildData = NodeData(ChildNode);
ChildData->File = NULL;
ChildrenDirs->AddObject(UnixExtractFileName(ChildData->Directory),
ChildNode);
ChildNode = Node->GetNextChild(ChildNode);
FirstChild = false;
}
// sort only after adding all items.
// should be faster then keeping the list sorted since beginning
ChildrenDirs->Sorted = true;
for (int i = 0; i < Data->FileList->Count; i++)
{
TRemoteFile * File = Data->FileList->Files[i];
if (File->IsDirectory && IsRealFile(File->FileName) &&
(ShowHiddenDirs || !File->IsHidden) &&
(ShowInaccesibleDirectories || !File->IsInaccesibleDirectory))
{
int ChildIndex = ChildrenDirs->IndexOf(File->FileName);
if (ChildIndex >= 0)
{
TTreeNode * ChildNode =
dynamic_cast<TTreeNode *>(ChildrenDirs->Objects[ChildIndex]);
TNodeData * ChildData = NodeData(ChildNode);
ChildData->File = File;
UpdatePath(ChildNode, Force);
}
else
{
UnicodeString ChildPath = UnixIncludeTrailingBackslash(Path) + File->FileName;
DebugAssert(!IsUnixRootPath(ChildPath));
LoadPathEasy(Node, ChildPath, File);
if (FirstChild)
{
LoadNodeState(Node, Path);
FirstChild = false;
}
}
}
}
for (int i = 0; i < ChildrenDirs->Count; i++)
{
TTreeNode * ChildNode = dynamic_cast<TTreeNode *>(ChildrenDirs->Objects[i]);
TNodeData * ChildData = NodeData(ChildNode);
if (ChildData->File == NULL)
{
NodeTryDelete(ChildNode, true);
}
}
Node->AlphaSort(false);
}
__finally
{
delete ChildrenDirs;
// Relese only files only now, once they are no longer references in the tree
delete OldFileList; // if not NULL
}
}
else if (Force)
{
TTreeNode * ChildNode = Node->GetLastChild();
while (ChildNode != NULL)
{
TTreeNode * PrevChildNode = Node->GetPrevChild(ChildNode);
TRemoteFile * File = NodeFile(ChildNode);
if (((ShowHiddenDirs || !NodeIsHidden(ChildNode)) &&
(ShowInaccesibleDirectories || (File == NULL) || !File->IsInaccesibleDirectory)) ||
!NodeTryDelete(ChildNode, true))
{
UpdatePath(ChildNode, true);
}
ChildNode = PrevChildNode;
}
}
#else
DebugUsedParam(Node);
DebugUsedParam(Force);
DebugUsedParam(CanLoad);
#endif
}
//---------------------------------------------------------------------------
TTreeNode * __fastcall TCustomUnixDriveView::LoadPathEasy(TTreeNode * Parent,
UnicodeString Path, TRemoteFile * File)
{
#ifndef DESIGN_ONLY
DebugAssert(Path == UnixExcludeTrailingBackslash(Path));
UnicodeString DirName;
if (IsUnixRootPath(Path))
{
DirName = RootName;
}
else
{
DirName = UnixExtractFileName(Path);
}
TTreeNode * Node = Items->AddChild(Parent, DirName);
TNodeData * Data = new TNodeData();
Node->Data = Data;
Data->Directory = Path;
Data->File = File;
Data->FileList = NULL;
UpdatePath(Node, true);
return Node;
#else
DebugUsedParam(Parent);
DebugUsedParam(File);
return NULL;
#endif
}
//---------------------------------------------------------------------------
TTreeNode * __fastcall TCustomUnixDriveView::LoadPath(UnicodeString Path)
{
#ifndef DESIGN_ONLY
if (Path.IsEmpty())
{
Path = ROOTDIRECTORY;
}
TTreeNode * Node = FindNodeToPath(Path);
if (Node == NULL)
{
Path = UnixExcludeTrailingBackslash(Path);
TTreeNode * Parent = NULL;
TRemoteFile * File = NULL;
UnicodeString ParentPath;
if (!IsUnixRootPath(Path))
{
ParentPath = UnixExtractFileDir(Path);
Parent = LoadPath(ParentPath);
}
Node = FindNodeToPath(Path);
if (Node == NULL)
{
// node still does not exist, this should happen only when
// if the parent directory is not in cache
if (Parent != NULL)
{
TRemoteFileList * ParentFileList = NodeFileList(Parent);
if (ParentFileList != NULL)
{
File = ParentFileList->FindFile(UnixExtractFileName(Path));
}
}
Node = LoadPathEasy(Parent, Path, File);
if (Parent != NULL)
{
LoadNodeState(Parent, ParentPath);
Parent->AlphaSort(false);
}
}
else
{
UpdatePath(Node, false);
}
}
else
{
UpdatePath(Node, false);
}
return Node;
#else
return NULL;
#endif
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::LoadDirectory()
{
#ifndef DESIGN_ONLY
DebugAssert(!FIgnoreChange);
FIgnoreChange = true;
try
{
Selected = LoadPath(FTerminal->Files->Directory);
DebugAssert(Selected != NULL);
FPrevSelected = Selected;
}
__finally
{
FIgnoreChange = false;
FDirectoryLoaded = true;
}
#endif
}
//---------------------------------------------------------------------------
TNodeData * __fastcall TCustomUnixDriveView::NodeData(const TTreeNode * Node)
{
DebugAssert(Node->Data != NULL);
return static_cast<TNodeData*>(Node->Data);
}
//---------------------------------------------------------------------------
TRemoteFileList * __fastcall TCustomUnixDriveView::NodeFileList(const TTreeNode * Node)
{
DebugAssert(Node->Data != NULL);
return static_cast<TNodeData*>(Node->Data)->FileList;
}
//---------------------------------------------------------------------------
TRemoteFile * __fastcall TCustomUnixDriveView::NodeFile(const TTreeNode * Node)
{
DebugAssert(Node->Data != NULL);
return static_cast<TNodeData*>(Node->Data)->File;
}
//---------------------------------------------------------------------------
TRemoteFile * __fastcall TCustomUnixDriveView::NodeFileForce(TTreeNode * Node)
{
TRemoteFile * File = NodeFile(Node);
if (File == NULL)
{
#ifndef DESIGN_ONLY
SAFE_DESTROY(FDummyDragFile);
FDummyDragFile = new TRemoteDirectoryFile();
FDummyDragFile->FileName = Node->Text;
FDummyDragFile->FullFileName = NodePathName(Node);
File = FDummyDragFile;
#endif
}
return File;
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::Delete(TTreeNode * Node)
{
if (Node == FPrevSelected)
{
FPrevSelected = NULL;
}
// optimization check
if (FPendingDelete->Count > 0)
{
int I = FPendingDelete->IndexOf(Node);
if (I >= 0)
{
FPendingDelete->Delete(I);
}
}
TNodeData * Data = NULL;
if (Node != NULL)
{
Data = NodeData(Node);
}
TCustomDriveView::Delete(Node);
// delete file list at last, when we are sure that no child nodes exist
if (Data != NULL)
{
#ifndef DESIGN_ONLY
delete Data->FileList;
#endif
delete Data;
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::Change(TTreeNode * Node)
{
#ifndef DESIGN_ONLY
bool Expand = false;
try
{
// During D&D Selected is set to NULL and then back to previous selection,
// prevent actually changing directory in such case
if (Reading || ControlState.Contains(csRecreating) || FRecreatingHandle ||
(Node == NULL) || (Node == FPrevSelected))
{
TCustomDriveView::Change(Node);
}
else
{
// if previous node is child to newly selected one, do not expand it.
// it is either already expanded and it is even being collapsed.
Expand = (FPrevSelected == NULL) || !FPrevSelected->HasAsParent(Node);
if (FIgnoreChange)
{
TCustomDriveView::Change(Node);
}
else
{
if (FDirView != NULL)
{
// remember current directory, so it gets selected if we move to parent
// directory
FDirView->ContinueSession(true);
}
FDirectoryLoaded = false;
bool SetBusy = !ControlState.Contains(csRecreating);
if (SetBusy)
{
StartBusy();
}
try
{
Terminal->ChangeDirectory(NodePathName(Node));
TCustomDriveView::Change(Node);
}
__finally
{
if (SetBusy)
{
EndBusy();
}
if (!FDirectoryLoaded)
{
DebugAssert(!FIgnoreChange);
Expand = false;
FIgnoreChange = true;
try
{
Selected = FPrevSelected;
}
__finally
{
FIgnoreChange = false;
}
}
}
}
}
}
__finally
{
FPrevSelected = Selected;
if (Expand)
{
Selected->Expand(false);
}
CheckPendingDeletes();
}
#else
TCustomDriveView::Change(Node);
#endif
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::CheckPendingDeletes()
{
int Index = 0;
while (Index < FPendingDelete->Count)
{
TTreeNode * Node = static_cast<TTreeNode *>(FPendingDelete->Items[Index]);
// this as well deletes node from FPendingDelete
if (!NodeTryDelete(Node, false))
{
Index++;
}
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::PerformDragDropFileOperation(
TTreeNode * Node, int Effect)
{
if (OnDDFileOperation)
{
// see a comment in TUnixDirView::PerformItemDragDropOperation
if (DragDropFilesEx->FileList->Count > 0)
{
DebugAssert(Node != NULL);
UnicodeString SourceDirectory;
UnicodeString TargetDirectory;
SourceDirectory = ExtractFilePath(DragDropFilesEx->FileList->Items[0]->Name);
TargetDirectory = NodeData(Node)->Directory;
bool DoFileOperation = true;
OnDDFileOperation(
this, Effect, SourceDirectory, TargetDirectory, false, DoFileOperation);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::DDChooseEffect(int KeyState, int & Effect, int PreferredEffect)
{
// if any drop effect is allowed at all (e.g. no drop to self and drop to parent)
if (Effect != DROPEFFECT_NONE)
{
if (DropTarget != NULL)
{
if ((KeyState & (MK_CONTROL | MK_SHIFT)) == 0)
{
Effect = DROPEFFECT_COPY;
}
}
else
{
Effect = DROPEFFECT_NONE;
}
}
TCustomDriveView::DDChooseEffect(KeyState, Effect, PreferredEffect);
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::UpdateDropTarget()
{
// should never be NULL
if (DropTarget != NULL)
{
UpdatePath(DropTarget, false, true);
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::UpdateDropSource()
{
// DragNode may be NULL if its parent directory was reloaded as result
// of D&D operation and thus all child nodes are recreated
if ((DragNode != NULL) && (DragNode->Parent != NULL))
{
UpdatePath(DragNode->Parent, false, true);
}
}
//---------------------------------------------------------------------------
TStrings * __fastcall TCustomUnixDriveView::DragFileList()
{
DebugAssert(DragNode != NULL);
TStrings * FileList = new TStringList();
try
{
#ifndef DESIGN_ONLY
FileList->AddObject(ExcludeTrailingBackslash(NodePathName(DragNode)),
NodeFileForce(DragNode));
#endif
}
catch(...)
{
delete FileList;
throw;
}
return FileList;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomUnixDriveView::DragCompleteFileList()
{
return true;
}
//---------------------------------------------------------------------------
TDropEffectSet __fastcall TCustomUnixDriveView::DDSourceEffects()
{
TDropEffectSet Result;
Result << deCopy;
if (DDAllowMove)
{
Result << deMove;
}
return Result;
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomUnixDriveView::NodePathName(TTreeNode * Node)
{
// same as NodePath
return NodeData(Node)->Directory;
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::ClearDragFileList(TFileList * FileList)
{
#ifndef DESIGN_ONLY
if (FDummyDragFile != NULL)
{
SAFE_DESTROY(FDummyDragFile);
}
#endif
TCustomDriveView::ClearDragFileList(FileList);
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::AddToDragFileList(TFileList * FileList,
TTreeNode * Node)
{
UnicodeString FileName = NodePathName(Node);
TRemoteFile * File = NodeFileForce(Node);
if (OnDDDragFileName != NULL)
{
OnDDDragFileName(this, File, FileName);
}
FileList->AddItem(NULL, FileName);
}
//---------------------------------------------------------------------------
UnicodeString __fastcall TCustomUnixDriveView::NodePath(TTreeNode * Node)
{
// same as NodePathName
return NodeData(Node)->Directory;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomUnixDriveView::NodeIsRecycleBin(TTreeNode * /*Node*/)
{
return false;
}
//---------------------------------------------------------------------------
bool __fastcall TCustomUnixDriveView::NodePathExists(TTreeNode * /*Node*/)
{
return true;
}
//---------------------------------------------------------------------------
TColor __fastcall TCustomUnixDriveView::NodeColor(TTreeNode * Node)
{
DebugAssert(Node != NULL);
TColor Result = static_cast<TColor>(clDefaultItemColor);
#ifndef DESIGN_ONLY
if (FDimmHiddenDirs && !Node->Selected)
{
if (NodeIsHidden(Node))
{
Result = clGrayText;
}
}
#else
DebugUsedParam(Node);
#endif
return Result;
}
//---------------------------------------------------------------------------
Word __fastcall TCustomUnixDriveView::NodeOverlayIndexes(TTreeNode * Node)
{
#ifndef DESIGN_ONLY
Word Result = oiNoOverlay;
// Cannot query root node for file
if (Node->Parent != NULL)
{
TRemoteFile * File = NodeFile(Node);
if (File != NULL)
{
if (File->IsSymLink)
{
// broken link cannot probably happen anyway
// as broken links are treated as files
Result |= File->BrokenLink ? oiBrokenLink : oiLink;
}
if (File->IsEncrypted)
{
Result |= oiEncrypted;
}
}
}
return Result;
#else
DebugUsedParam(Node);
return 0;
#endif
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::GetImageIndex(TTreeNode * Node)
{
TCustomDriveView::GetImageIndex(Node);
Node->ImageIndex = StdDirIcon;
Node->SelectedIndex = StdDirSelIcon;
}
//---------------------------------------------------------------------------
TTreeNode * __fastcall TCustomUnixDriveView::FindNodeToPath(UnicodeString Path)
{
TTreeNode * Result;
#ifndef DESIGN_ONLY
if (IsUnixRootPath(Path))
{
Result = Items->GetFirstNode();
}
else
{
Result = NULL;
Path = UnixExcludeTrailingBackslash(Path);
TTreeNode * Parent = NULL;
if (!IsUnixRootPath(Path))
{
Parent = FindNodeToPath(UnixExtractFileDir(Path));
}
if ((Parent != NULL) && (Parent->getFirstChild() != NULL))
{
UnicodeString DirName = UnixExtractFileName(Path);
int StartIndex = 0;
int EndIndex = Parent->Count - 1;
while (true)
{
int Index = (StartIndex + EndIndex) / 2;
UnicodeString NodeDir = Parent->Item[Index]->Text;
int C = DoCompareText(DirName, NodeDir);
if (C == 0)
{
Result = Parent->Item[Index];
break;
}
else if (C < 0)
{
if (Index == StartIndex)
{
break;
}
EndIndex = Index - 1;
}
else
{
if (Index == EndIndex)
{
break;
}
StartIndex = Index + 1;
}
}
}
}
#else
Result = NULL;
#endif
return Result;
}
//---------------------------------------------------------------------------
TTreeNode * __fastcall TCustomUnixDriveView::FindPathNode(UnicodeString Path)
{
TTreeNode * Result = NULL;
#ifndef DESIGN_ONLY
if (Items->GetFirstNode() != NULL)
{
do
{
Result = FindNodeToPath(Path);
if (Result == NULL)
{
DebugAssert(!IsUnixRootPath(Path));
Path = UnixExtractFileDir(UnixExcludeTrailingBackslash(Path));
}
}
while (Result == NULL);
}
#endif
return Result;
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::ValidateDirectoryEx(TTreeNode * /*Node*/,
TRecursiveScan /*Recurse*/, bool /*NewDirs*/)
{
// nothing
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::RebuildTree()
{
TTreeNode * First = Items->GetFirstNode();
if (First != NULL)
{
UpdatePath(First, true);
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::SetShowInaccesibleDirectories(bool value)
{
if (FShowInaccesibleDirectories != value)
{
FShowInaccesibleDirectories = value;
RebuildTree();
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::CMShowingChanged(TMessage & Message)
{
TCustomDriveView::Dispatch(&Message);
if (Showing && (Terminal != NULL))
{
LoadDirectory();
}
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::DisplayContextMenu(TTreeNode * /*Node*/,
const TPoint & /*ScreenPos*/)
{
// TODO
}
//---------------------------------------------------------------------------
void __fastcall TCustomUnixDriveView::DisplayPropertiesMenu(TTreeNode * /*Node*/)
{
// TODO
}
//---------------------------------------------------------------------------
void TCustomUnixDriveView::LoadNodeState(TTreeNode * Node, const UnicodeString & Path)
{
if ((FDirView != NULL) && (FDirView->FAnnouncedDriveViewState != NULL))
{
TStrings * AnnouncedDriveViewState = DebugNotNull(dynamic_cast<TStrings *>(FDirView->FAnnouncedDriveViewState));
if (AnnouncedDriveViewState->IndexOf(Path) >= 0)
{
Node->Expand(false);
}
}
}
//---------------------------------------------------------------------------
TObject * TCustomUnixDriveView::SaveState()
{
#ifndef DESIGN_ONLY
std::unique_ptr<TStrings> ExpandedNodes(CreateSortedStringList());
TTreeNode * Node = Items->GetFirstNode();
while (Node != NULL)
{
// Node->HasChildren would be false in extreme cases only (like while closing the window)
if (Node->Expanded && Node->HasChildren)
{
ExpandedNodes->Add(NodePathName(Node));
}
Node = Node->GetNext();
}
return ExpandedNodes.release();
#else
return NULL;
#endif
}