Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/elfmz/far2l.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelfmz <fenix1905@tut.by>2021-12-31 00:27:16 +0300
committerelfmz <fenix1905@tut.by>2021-12-31 02:27:02 +0300
commita14dc1a81c797928d4f1b7d6a6b46ecc63f98308 (patch)
treec27c61ac33582bc4d469c6608cd042add388f230 /far2l/src/treelist.cpp
parentd5f1bf245e96834d44390d1723cfef3dfbb1fb02 (diff)
shuffle a bit far2l sources
Diffstat (limited to 'far2l/src/treelist.cpp')
-rw-r--r--far2l/src/treelist.cpp2204
1 files changed, 2204 insertions, 0 deletions
diff --git a/far2l/src/treelist.cpp b/far2l/src/treelist.cpp
new file mode 100644
index 00000000..1f5120a4
--- /dev/null
+++ b/far2l/src/treelist.cpp
@@ -0,0 +1,2204 @@
+/*
+treelist.cpp
+
+Tree panel
+*/
+/*
+Copyright (c) 1996 Eugene Roshal
+Copyright (c) 2000 Far Group
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "headers.hpp"
+
+
+#include "treelist.hpp"
+#include "keyboard.hpp"
+#include "colors.hpp"
+#include "lang.hpp"
+#include "keys.hpp"
+#include "filepanels.hpp"
+#include "filelist.hpp"
+#include "cmdline.hpp"
+#include "chgprior.hpp"
+#include "scantree.hpp"
+#include "copy.hpp"
+#include "qview.hpp"
+#include "savescr.hpp"
+#include "ctrlobj.hpp"
+#include "help.hpp"
+#include "lockscrn.hpp"
+#include "macroopcode.hpp"
+#include "RefreshFrameManager.hpp"
+#include "scrbuf.hpp"
+#include "TPreRedrawFunc.hpp"
+#include "cddrv.hpp"
+#include "interf.hpp"
+#include "message.hpp"
+#include "clipboard.hpp"
+#include "config.hpp"
+#include "delete.hpp"
+#include "mkdir.hpp"
+#include "setattr.hpp"
+#include "execute.hpp"
+#include "Bookmarks.hpp"
+#include "dirmix.hpp"
+#include "pathmix.hpp"
+#include "processname.hpp"
+#include "constitle.hpp"
+#include "syslog.hpp"
+#include "cache.hpp"
+#include "filestr.hpp"
+#include "wakeful.hpp"
+
+static int _cdecl SortList(const void *el1,const void *el2);
+static int _cdecl SortCacheList(const void *el1,const void *el2);
+static int StaticSortNumeric;
+static int StaticSortCaseSensitive;
+static int TreeCmp(const wchar_t *Str1,const wchar_t *Str2, int Numeric, int CaseSensitive);
+static clock_t TreeStartTime;
+static int LastScrX = -1;
+static int LastScrY = -1;
+
+static struct TreeListCache
+{
+ FARString strTreeName;
+ wchar_t **ListName;
+ int TreeCount;
+ int TreeSize;
+
+ TreeListCache()
+ {
+ ListName=nullptr;
+ TreeCount=0;
+ TreeSize=0;
+ }
+
+ void Resize()
+ {
+ if (TreeCount==TreeSize)
+ {
+ TreeSize+=TreeSize?TreeSize>>2:32;
+ wchar_t **NewPtr=(wchar_t**)realloc(ListName,sizeof(wchar_t*)*TreeSize);
+
+ if (!NewPtr)
+ return;
+
+ ListName=NewPtr;
+ }
+ }
+
+ void Add(const wchar_t* name)
+ {
+ Resize();
+ ListName[TreeCount++]=wcsdup(name);
+ }
+
+ void Insert(int idx,const wchar_t* name)
+ {
+ Resize();
+ memmove(ListName+idx+1,ListName+idx,sizeof(wchar_t*)*(TreeCount-idx));
+ ListName[idx]=wcsdup(name);
+ TreeCount++;
+ }
+
+ void Delete(int idx)
+ {
+ if (ListName[idx]) free(ListName[idx]);
+
+ memmove(ListName+idx,ListName+idx+1,sizeof(wchar_t*)*(TreeCount-idx-1));
+ TreeCount--;
+ }
+
+ void Clean()
+ {
+ if (!TreeSize)return;
+
+ for (int i=0; i<TreeCount; i++)
+ {
+ if (ListName[i]) free(ListName[i]);
+ }
+
+ if (ListName) free(ListName);
+
+ ListName=nullptr;
+ TreeCount=0;
+ TreeSize=0;
+ strTreeName.Clear();
+ }
+
+ //TODO: необходимо оптимизировать!
+ void Copy(TreeListCache *Dest)
+ {
+ Dest->Clean();
+
+ for (int I=0; I < TreeCount; I++)
+ Dest->Add(ListName[I]);
+ }
+
+} TreeCache, tempTreeCache;
+
+
+TreeList::TreeList(int IsPanel):
+ PrevMacroMode(-1),
+ ListData(nullptr),
+ TreeCount(0),
+ WorkDir(0),
+ GetSelPosition(0),
+ NumericSort(FALSE),
+ CaseSensitiveSort(FALSE),
+ ExitCode(1),
+ SaveListData(nullptr)
+{
+ Type=TREE_PANEL;
+ CurFile=CurTopFile=0;
+ Flags.Set(FTREELIST_UPDATEREQUIRED);
+ Flags.Clear(FTREELIST_TREEISPREPARED);
+ Flags.Change(FTREELIST_ISPANEL,IsPanel);
+}
+
+
+TreeList::~TreeList()
+{
+ if (ListData)
+ {
+ for (long i=0; i<TreeCount; i++)
+ delete ListData[i];
+
+ free(ListData);
+ }
+
+ if (SaveListData) delete [] SaveListData;
+
+ tempTreeCache.Clean();
+ FlushCache();
+ SetMacroMode(TRUE);
+}
+
+void TreeList::SetRootDir(const wchar_t *NewRootDir)
+{
+ strRoot = NewRootDir;
+ strCurDir = NewRootDir;
+}
+
+
+void TreeList::DisplayObject()
+{
+ if (Flags.Check(FSCROBJ_ISREDRAWING))
+ return;
+
+ Flags.Set(FSCROBJ_ISREDRAWING);
+
+ if (Flags.Check(FTREELIST_UPDATEREQUIRED))
+ Update(0);
+
+ if (ExitCode)
+ {
+ Panel *RootPanel=GetRootPanel();
+
+ if (RootPanel->GetType()==FILE_PANEL)
+ {
+ int RootCaseSensitiveSort=RootPanel->GetCaseSensitiveSort();
+ int RootNumeric=RootPanel->GetNumericSort();
+
+ if (RootNumeric != NumericSort || RootCaseSensitiveSort!=CaseSensitiveSort)
+ {
+ NumericSort=RootNumeric;
+ CaseSensitiveSort=RootCaseSensitiveSort;
+ StaticSortNumeric=NumericSort;
+ StaticSortCaseSensitive=CaseSensitiveSort;
+ far_qsort(ListData,TreeCount,sizeof(*ListData),SortList);
+ FillLastData();
+ SyncDir();
+ }
+ }
+
+ DisplayTree(FALSE);
+ }
+
+ Flags.Clear(FSCROBJ_ISREDRAWING);
+}
+
+
+FARString &TreeList::GetTitle(FARString &strTitle,int SubLen,int TruncSize)
+{
+ strTitle.Format(L" %ls ",ModalMode ? MSG(MFindFolderTitle):MSG(MTreeTitle));
+ TruncStr(strTitle,X2-X1-3);
+ return strTitle;
+}
+
+void TreeList::DisplayTree(int Fast)
+{
+ wchar_t TreeLineSymbol[4][3]=
+ {
+ {L' ', L' ', 0},
+ {BoxSymbols[BS_V1], L' ', 0},
+ {BoxSymbols[BS_LB_H1V1],BoxSymbols[BS_H1],0},
+ {BoxSymbols[BS_L_H1V1], BoxSymbols[BS_H1],0},
+ };
+ TreeItem *CurPtr;
+ FARString strTitle;
+ LockScreen *LckScreen=nullptr;
+
+ if (CtrlObject->Cp()->GetAnotherPanel(this)->GetType() == QVIEW_PANEL)
+ LckScreen=new LockScreen;
+
+ CorrectPosition();
+
+ if (TreeCount>0)
+ strCurDir = ListData[CurFile]->strName; //BUGBUG
+
+ if (!Fast)
+ {
+ Box(X1,Y1,X2,Y2,COL_PANELBOX,DOUBLE_BOX);
+ DrawSeparator(Y2-2-(ModalMode));
+ GetTitle(strTitle);
+
+ if (!strTitle.IsEmpty())
+ {
+ SetColor((Focus || ModalMode) ? COL_PANELSELECTEDTITLE:COL_PANELTITLE);
+ GotoXY(X1+(X2-X1+1-(int)strTitle.GetLength())/2,Y1);
+ Text(strTitle);
+ }
+ }
+
+ for (int I=Y1+1,J=CurTopFile; I<Y2-2-(ModalMode); I++,J++)
+ {
+ GotoXY(X1+1,I);
+ SetColor(COL_PANELTEXT);
+ Text(L" ");
+
+ if (J<TreeCount && Flags.Check(FTREELIST_TREEISPREPARED))
+ {
+ CurPtr=ListData[J];
+
+ if (!J)
+ {
+ DisplayTreeName(strRoot.CPtr(),J);
+ }
+ else
+ {
+ FARString strOutStr;
+
+ for (int i=0; i<CurPtr->Depth-1 && WhereX()+3*i<X2-6; i++)
+ {
+ strOutStr+=TreeLineSymbol[CurPtr->Last[i] ? 0 : 1];
+ }
+
+ strOutStr+=TreeLineSymbol[CurPtr->Last[CurPtr->Depth-1] ? 2 : 3];
+ BoxText(strOutStr);
+ const wchar_t *ChPtr = LastSlash(CurPtr->strName);
+
+ if (ChPtr)
+ DisplayTreeName(ChPtr+1,J);
+ }
+ }
+
+ SetColor(COL_PANELTEXT);
+
+ if (WhereX()<X2)
+ {
+ FS<<fmt::Width(X2-WhereX())<<L"";
+ }
+ }
+
+ if (Opt.ShowPanelScrollbar)
+ {
+ SetColor(COL_PANELSCROLLBAR);
+ ScrollBarEx(X2,Y1+1,Y2-Y1-3,CurTopFile,TreeCount);
+ }
+
+ SetColor(COL_PANELTEXT);
+ SetScreen(X1+1,Y2-(ModalMode?2:1),X2-1,Y2-1,L' ',COL_PANELTEXT);
+
+ if (TreeCount>0)
+ {
+ GotoXY(X1+1,Y2-1);
+ FS<<fmt::LeftAlign()<<fmt::Width(X2-X1-1)<<fmt::Precision(X2-X1-1)<<ListData[CurFile]->strName;
+ }
+
+ UpdateViewPanel();
+ SetTitle(); // не забудим прорисовать заголовок
+
+ if (LckScreen)
+ delete LckScreen;
+}
+
+
+void TreeList::DisplayTreeName(const wchar_t *Name,int Pos)
+{
+ if (WhereX()>X2-4)
+ GotoXY(X2-4,WhereY());
+
+ if (Pos==CurFile)
+ {
+ GotoXY(WhereX()-1,WhereY());
+
+ if (Focus || ModalMode)
+ {
+ SetColor((Pos==WorkDir) ? COL_PANELSELECTEDCURSOR:COL_PANELCURSOR);
+ FS<<L" "<<fmt::Precision(X2-WhereX()-3)<<Name<<L" ";
+ }
+ else
+ {
+ SetColor((Pos==WorkDir) ? COL_PANELSELECTEDTEXT:COL_PANELTEXT);
+ FS<<L"["<<fmt::Precision(X2-WhereX()-3)<<Name<<L"]";
+ }
+ }
+ else
+ {
+ SetColor((Pos==WorkDir) ? COL_PANELSELECTEDTEXT:COL_PANELTEXT);
+ FS<<fmt::Precision(X2-WhereX()-1)<<Name;
+ }
+}
+
+
+void TreeList::Update(int Mode)
+{
+ if (!EnableUpdate)
+ return;
+
+ if (!IsVisible())
+ {
+ Flags.Set(FTREELIST_UPDATEREQUIRED);
+ return;
+ }
+
+ Flags.Clear(FTREELIST_UPDATEREQUIRED);
+ GetRoot();
+ int LastTreeCount=TreeCount;
+ int RetFromReadTree=TRUE;
+ Flags.Clear(FTREELIST_TREEISPREPARED);
+ int TreeFilePresent=ReadTreeFile();
+
+ if (!TreeFilePresent)
+ RetFromReadTree=ReadTree();
+
+ Flags.Set(FTREELIST_TREEISPREPARED);
+
+ if (!RetFromReadTree && !Flags.Check(FTREELIST_ISPANEL))
+ {
+ ExitCode=0;
+ return;
+ }
+
+ if (RetFromReadTree && TreeCount>0 && (!(Mode & UPDATE_KEEP_SELECTION) || LastTreeCount!=TreeCount))
+ {
+ SyncDir();
+ TreeItem *CurPtr=ListData[CurFile];
+
+ if (apiGetFileAttributes(CurPtr->strName)==INVALID_FILE_ATTRIBUTES)
+ {
+ DelTreeName(CurPtr->strName);
+ Update(UPDATE_KEEP_SELECTION);
+ Show();
+ }
+ }
+ else if (!RetFromReadTree)
+ {
+ Show();
+
+ if (!Flags.Check(FTREELIST_ISPANEL))
+ {
+ Panel *AnotherPanel=CtrlObject->Cp()->GetAnotherPanel(this);
+ AnotherPanel->Update(UPDATE_KEEP_SELECTION|UPDATE_SECONDARY);
+ AnotherPanel->Redraw();
+ }
+ }
+}
+
+
+int TreeList::ReadTree()
+{
+ ChangePriority ChPriority(ChangePriority::NORMAL);
+ //SaveScreen SaveScr;
+ TPreRedrawFuncGuard preRedrawFuncGuard(TreeList::PR_MsgReadTree);
+ ScanTree ScTree(FALSE);
+ FAR_FIND_DATA_EX fdata;
+ FARString strFullName;
+ SaveState();
+ FlushCache();
+ GetRoot();
+
+ if (ListData)
+ {
+ for (long i=0; i<TreeCount; i++)
+ delete ListData[i];
+
+ free(ListData);
+ }
+
+ TreeCount=0;
+
+ if (!(ListData=(TreeItem**)malloc((TreeCount+256+1)*sizeof(TreeItem*))))
+ {
+ RestoreState();
+ return FALSE;
+ }
+
+ ListData[0] = new TreeItem;
+ ListData[0]->Clear();
+ ListData[0]->strName = strRoot;
+ SaveScreen SaveScrTree;
+ UndoGlobalSaveScrPtr UndSaveScr(&SaveScrTree);
+ /* Т.к. мы можем вызвать диалог подтверждения (который не перерисовывает панельки,
+ а восстанавливает сохраненный образ экрана, то нарисуем чистую панель */
+ //Redraw();
+ TreeCount=1;
+ int FirstCall=TRUE, AscAbort=FALSE;
+ TreeStartTime = GetProcessUptimeMSec();
+ RefreshFrameManager frref(ScrX,ScrY,TreeStartTime,FALSE);//DontRedrawFrame);
+ ScTree.SetFindPath(strRoot, L"*", FSCANTREE_NOFILES | FSCANTREE_NODEVICES);
+ LastScrX = ScrX;
+ LastScrY = ScrY;
+ wakeful W;
+ while (ScTree.GetNextName(&fdata,strFullName))
+ {
+// if(TreeCount > 3)
+ TreeList::MsgReadTree(TreeCount,FirstCall);
+
+ if (CheckForEscSilent())
+ {
+ AscAbort=ConfirmAbortOp();
+ FirstCall=TRUE;
+ }
+
+ if (AscAbort)
+ break;
+
+ if (!(TreeCount & 255))
+ {
+ TreeItem **TmpListData=(TreeItem **)realloc(ListData,(TreeCount+256+1)*sizeof(TreeItem*));
+
+ if (!TmpListData)
+ {
+ AscAbort=TRUE;
+ break;
+ }
+
+ ListData = TmpListData;
+ }
+
+ if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ continue;
+
+ ListData[TreeCount] = new TreeItem;
+ ListData[TreeCount]->Clear();
+ ListData[TreeCount]->strName = strFullName;
+ TreeCount++;
+ }
+
+ if (AscAbort && !Flags.Check(FTREELIST_ISPANEL))
+ {
+ if (ListData)
+ {
+ for (long i=0; i<TreeCount; i++)
+ delete ListData[i];
+
+ free(ListData);
+ }
+
+ ListData=nullptr;
+ TreeCount=0;
+ RestoreState();
+ return FALSE;
+ }
+
+ StaticSortNumeric=NumericSort=StaticSortCaseSensitive=CaseSensitiveSort=FALSE;
+ far_qsort(ListData,TreeCount,sizeof(*ListData),SortList);
+
+ if (!FillLastData())
+ return FALSE;
+
+ if (!AscAbort)
+ SaveTreeFile();
+
+ if (!FirstCall && !Flags.Check(FTREELIST_ISPANEL))
+ { // Перерисуем другую панель - удалим следы сообщений :)
+ Panel *AnotherPanel=CtrlObject->Cp()->GetAnotherPanel(this);
+ AnotherPanel->Redraw();
+ }
+
+ return TRUE;
+}
+
+
+void TreeList::SaveTreeFile()
+{
+ if (TreeCount<4)
+ return;
+
+ FARString strName;
+ long I;
+ size_t RootLength=strRoot.IsEmpty()?0:strRoot.GetLength()-1;
+ MkTreeFileName(strRoot, strName);
+ // получим и сразу сбросим атрибуты (если получится)
+ DWORD FileAttributes=apiGetFileAttributes(strName);
+
+ if (FileAttributes != INVALID_FILE_ATTRIBUTES)
+ apiSetFileAttributes(strName,FILE_ATTRIBUTE_NORMAL);
+
+ File TreeFile;
+ if (!TreeFile.Open(strName,GENERIC_WRITE,FILE_SHARE_READ,nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL))
+ {
+ /* $ 16.10.2000 tran
+ если диск должен кешироваться, то и пытаться не стоит */
+ if (MustBeCached(strRoot))
+ if (!GetCacheTreeName(strRoot,strName,TRUE) || !TreeFile.Open(strName,GENERIC_WRITE,FILE_SHARE_READ,nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL))
+ return;
+
+ /* tran $ */
+ }
+
+ bool Success=true;
+ CachedWrite Cache(TreeFile);
+ for (I=0; I<TreeCount && Success; I++)
+ {
+ if (RootLength>=ListData[I]->strName.GetLength())
+ {
+ DWORD Size=2*sizeof(WCHAR);
+ Success=Cache.Write(L"/\n",Size);
+ }
+ else
+ {
+ DWORD Size=static_cast<DWORD>((ListData[I]->strName.GetLength()-RootLength)*sizeof(WCHAR));
+ Success=Cache.Write(ListData[I]->strName.SubStr(RootLength),Size);
+ Size=1*sizeof(WCHAR);
+ Success=Cache.Write(L"\n",Size);
+ }
+ }
+ Cache.Flush();
+ TreeFile.Close();
+
+ if (!Success)
+ {
+ apiDeleteFile(strName);
+ Message(MSG_WARNING|MSG_ERRORTYPE,1,MSG(MError),MSG(MCannotSaveTree),strName,MSG(MOk));
+ }
+ else if (FileAttributes != INVALID_FILE_ATTRIBUTES) // вернем атрибуты (если получится :-)
+ apiSetFileAttributes(strName,FileAttributes);
+}
+
+
+int TreeList::GetCacheTreeName(const wchar_t *Root, FARString &strName,int CreateDir)
+{
+ struct statvfs svfs = {};
+
+ if (sdc_statvfs(Wide2MB(Root).c_str(), &svfs) != 0) {
+ return FALSE;
+ }
+
+ FARString strFolderName;
+ FARString strFarPath;
+ MkTreeCacheFolderName(strFarPath, strFolderName);
+
+ if (CreateDir)
+ {
+ apiCreateDirectory(strFolderName, nullptr);
+ apiSetFileAttributes(strFolderName,Opt.Tree.TreeFileAttr);
+ }
+
+ strName.Format(L"%ls/%lx", strFolderName.CPtr(), (unsigned long)svfs.f_fsid);
+ return TRUE;
+}
+
+
+void TreeList::GetRoot()
+{
+ FARString strPanelDir;
+ Panel *RootPanel=GetRootPanel();
+ RootPanel->GetCurDir(strPanelDir);
+ strRoot = strPanelDir;
+}
+
+
+Panel* TreeList::GetRootPanel()
+{
+ Panel *RootPanel;
+
+ if (ModalMode)
+ {
+ if (ModalMode==MODALTREE_ACTIVE)
+ RootPanel=CtrlObject->Cp()->ActivePanel;
+ else if (ModalMode==MODALTREE_FREE)
+ RootPanel=this;
+ else
+ {
+ RootPanel=CtrlObject->Cp()->GetAnotherPanel(CtrlObject->Cp()->ActivePanel);
+
+ if (!RootPanel->IsVisible())
+ RootPanel=CtrlObject->Cp()->ActivePanel;
+ }
+ }
+ else
+ RootPanel=CtrlObject->Cp()->GetAnotherPanel(this);
+
+ return(RootPanel);
+}
+
+
+void TreeList::SyncDir()
+{
+ FARString strPanelDir;
+ Panel *AnotherPanel=GetRootPanel();
+ AnotherPanel->GetCurDir(strPanelDir);
+
+ if (!strPanelDir.IsEmpty())
+ {
+ if (AnotherPanel->GetType()==FILE_PANEL)
+ {
+ if (!SetDirPosition(strPanelDir))
+ {
+ ReadSubTree(strPanelDir);
+ ReadTreeFile();
+ SetDirPosition(strPanelDir);
+ }
+ }
+ else
+ SetDirPosition(strPanelDir);
+ }
+}
+
+
+void TreeList::PR_MsgReadTree()
+{
+ int FirstCall=1;
+ PreRedrawItem preRedrawItem=PreRedraw.Peek();
+ TreeList::MsgReadTree(preRedrawItem.Param.Flags,FirstCall);
+}
+
+int TreeList::MsgReadTree(int TreeCount,int &FirstCall)
+{
+ /* $ 24.09.2001 VVM
+ ! Писать сообщение о чтении дерева только, если это заняло более 500 мсек. */
+ BOOL IsChangeConsole = LastScrX != ScrX || LastScrY != ScrY;
+
+ if (IsChangeConsole)
+ {
+ LastScrX = ScrX;
+ LastScrY = ScrY;
+ }
+
+ if (IsChangeConsole || (GetProcessUptimeMSec() - TreeStartTime) > 1000)
+ {
+ wchar_t NumStr[32];
+ _itow(TreeCount,NumStr,10); //BUGBUG
+ Message((FirstCall ? 0:MSG_KEEPBACKGROUND),0,MSG(MTreeTitle),
+ MSG(MReadingTree),NumStr);
+ PreRedrawItem preRedrawItem=PreRedraw.Peek();
+ preRedrawItem.Param.Flags=TreeCount;
+ PreRedraw.SetParam(preRedrawItem.Param);
+ TreeStartTime = GetProcessUptimeMSec();
+ }
+
+ return 1;
+}
+
+
+bool TreeList::FillLastData()
+{
+ const size_t RootLength =
+ strRoot.IsEmpty() ? 0 : strRoot.GetLength() - 1;
+
+ for (int I = 1; I < TreeCount; I++)
+ {
+ int PathLength;
+ size_t Pos, Depth ;
+
+ if (ListData[I]->strName.RPos(Pos,GOOD_SLASH))
+ PathLength = (int)Pos+1;
+ else
+ PathLength = 0;
+
+ ListData[I]->Depth = Depth =
+ CountSlash(ListData[I]->strName.CPtr()+RootLength);
+
+ if (!Depth)
+ return false;
+
+ bool Last;
+ int J, SubDirPos;
+ for (J = I + 1, SubDirPos = I, Last = true; J < TreeCount; J++)
+ {
+ if (CountSlash(ListData[J]->strName.CPtr()+RootLength)>Depth)
+ {
+ SubDirPos = J;
+ continue;
+ }
+ else
+ {
+ if (!StrCmpNI(ListData[I]->strName, ListData[J]->strName, PathLength))
+ Last = false;
+
+ break;
+ }
+ }
+
+ for (int J = I; J <= SubDirPos; J++)
+ {
+ TreeItem::LastT &JLast = ListData[J]->Last;
+ if (Depth == JLast.size()) {
+ JLast.push_back(Depth);
+ } else {
+ if (Depth > JLast.size())
+ JLast.resize(Depth);
+
+ JLast[Depth - 1] = Last;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+UINT TreeList::CountSlash(const wchar_t *Str)
+{
+ UINT Count=0;
+
+ for (; *Str; Str++)
+ if (IsSlash(*Str))
+ Count++;
+
+ return(Count);
+}
+
+
+int64_t TreeList::VMProcess(int OpCode,void *vParam,int64_t iParam)
+{
+ switch (OpCode)
+ {
+ case MCODE_C_EMPTY:
+ return TreeCount<=0;
+ case MCODE_C_EOF:
+ return CurFile==TreeCount-1;
+ case MCODE_C_BOF:
+ return !CurFile;
+ case MCODE_C_SELECTED:
+ return 0;
+ case MCODE_V_ITEMCOUNT:
+ return TreeCount;
+ case MCODE_V_CURPOS:
+ return CurFile+1;
+ }
+
+ return 0;
+}
+
+int TreeList::ProcessKey(int Key)
+{
+ if (!IsVisible())
+ return FALSE;
+
+ if (!TreeCount && Key!=KEY_CTRLR)
+ return FALSE;
+
+ FARString strTemp;
+
+ if (Key>=KEY_CTRLSHIFT0 && Key<=KEY_CTRLSHIFT9)
+ {
+ SaveShortcutFolder(Key-KEY_CTRLSHIFT0);
+ return TRUE;
+ }
+
+ if (Key>=KEY_RCTRL0 && Key<=KEY_RCTRL9)
+ {
+ ExecShortcutFolder(Key-KEY_RCTRL0);
+ return TRUE;
+ }
+
+ switch (Key)
+ {
+ case KEY_F1:
+ {
+ Help Hlp(L"TreePanel");
+ return TRUE;
+ }
+ case KEY_SHIFTNUMENTER:
+ case KEY_CTRLNUMENTER:
+ case KEY_SHIFTENTER:
+ case KEY_CTRLENTER:
+ case KEY_CTRLF:
+ case KEY_CTRLALTINS:
+ case KEY_CTRLALTNUMPAD0:
+ {
+ FARString strQuotedName=ListData[CurFile]->strName;
+ EscapeSpace(strQuotedName);
+
+ if (Key==KEY_CTRLALTINS||Key==KEY_CTRLALTNUMPAD0)
+ {
+ CopyToClipboard(strQuotedName);
+ }
+ else
+ {
+ if (Key == KEY_SHIFTENTER||Key == KEY_SHIFTNUMENTER)
+ {
+ Execute(strQuotedName,TRUE,TRUE);
+ }
+ else
+ {
+ strQuotedName+=L" ";
+ CtrlObject->CmdLine->InsertString(strQuotedName);
+ }
+ }
+
+ return TRUE;
+ }
+ case KEY_CTRLBACKSLASH:
+ {
+ CurFile=0;
+ ProcessEnter();
+ return TRUE;
+ }
+ case KEY_NUMENTER:
+ case KEY_ENTER:
+ {
+ if (!ModalMode && CtrlObject->CmdLine->GetLength()>0)
+ break;
+
+ ProcessEnter();
+ return TRUE;
+ }
+ case KEY_F4:
+ case KEY_CTRLA:
+ {
+ if (SetCurPath())
+ ShellSetFileAttributes(this);
+
+ return TRUE;
+ }
+ case KEY_CTRLR:
+ {
+ ReadTree();
+
+ if (TreeCount>0)
+ SyncDir();
+
+ Redraw();
+ break;
+ }
+ case KEY_SHIFTF5:
+ case KEY_SHIFTF6:
+ {
+ if (SetCurPath())
+ {
+ int ToPlugin=0;
+ ShellCopy ShCopy(this,Key==KEY_SHIFTF6,FALSE,TRUE,TRUE,ToPlugin,nullptr);
+ }
+
+ return TRUE;
+ }
+ case KEY_F5:
+ case KEY_DRAGCOPY:
+ case KEY_F6:
+ case KEY_ALTF6:
+ case KEY_DRAGMOVE:
+ {
+ if (SetCurPath() && TreeCount>0)
+ {
+ Panel *AnotherPanel=CtrlObject->Cp()->GetAnotherPanel(this);
+ int Ask=((Key!=KEY_DRAGCOPY && Key!=KEY_DRAGMOVE) || Opt.Confirm.Drag);
+ int Move=(Key==KEY_F6 || Key==KEY_DRAGMOVE);
+ int ToPlugin=AnotherPanel->GetMode()==PLUGIN_PANEL &&
+ AnotherPanel->IsVisible() &&
+ !CtrlObject->Plugins.UseFarCommand(AnotherPanel->GetPluginHandle(),PLUGIN_FARPUTFILES);
+ int Link=(Key==KEY_ALTF6 && !ToPlugin);
+
+ if (Key==KEY_ALTF6 && !Link) // молча отвалим :-)
+ return TRUE;
+
+ {
+ ShellCopy ShCopy(this,Move,Link,FALSE,Ask,ToPlugin,nullptr);
+ }
+
+ if (ToPlugin==1)
+ {
+ PluginPanelItem *ItemList=new PluginPanelItem[1];
+ int ItemNumber=1;
+ HANDLE hAnotherPlugin=AnotherPanel->GetPluginHandle();
+ FileList::FileNameToPluginItem(ListData[CurFile]->strName,ItemList);
+ int PutCode=CtrlObject->Plugins.PutFiles(hAnotherPlugin,ItemList,ItemNumber,Move,0);
+
+ if (PutCode==1 || PutCode==2)
+ AnotherPanel->SetPluginModified();
+
+ if (ItemList) delete[] ItemList;
+
+ if (Move)
+ ReadSubTree(ListData[CurFile]->strName);
+
+ Update(0);
+ Redraw();
+ AnotherPanel->Update(UPDATE_KEEP_SELECTION);
+ AnotherPanel->Redraw();
+ }
+ }
+
+ return TRUE;
+ }
+ case KEY_F7:
+ {
+ if (SetCurPath())
+ ShellMakeDir(this);
+
+ return TRUE;
+ }
+ /*
+ Удаление Shift-Del, Shift-F8, F8
+
+ Удаление файлов и папок. F8 и Shift-Del удаляют все выбранные
+ файлы, Shift-F8 - только файл под курсором. Shift-Del всегда удаляет
+ файлы, не используя Корзину (Recycle Bin). Использование Корзины
+ командами F8 и Shift-F8 зависит от конфигурации.
+
+ Уничтожение файлов и папок Alt-Del
+ */
+ case KEY_F8:
+ case KEY_SHIFTDEL:
+ case KEY_SHIFTNUMDEL:
+ case KEY_SHIFTDECIMAL:
+ case KEY_ALTNUMDEL:
+ case KEY_ALTDECIMAL:
+ case KEY_ALTDEL:
+ {
+ if (SetCurPath())
+ {
+ int SaveOpt=Opt.DeleteToRecycleBin;
+
+ if (Key==KEY_SHIFTDEL||Key==KEY_SHIFTNUMDEL||Key==KEY_SHIFTDECIMAL)
+ Opt.DeleteToRecycleBin=0;
+
+ ShellDelete(this,Key==KEY_ALTDEL||Key==KEY_ALTNUMDEL||Key==KEY_ALTDECIMAL);
+ // Надобно не забыть обновить противоположную панель...
+ Panel *AnotherPanel=CtrlObject->Cp()->GetAnotherPanel(this);
+ AnotherPanel->Update(UPDATE_KEEP_SELECTION);
+ AnotherPanel->Redraw();
+ Opt.DeleteToRecycleBin=SaveOpt;
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+ }
+
+ return TRUE;
+ }
+ case KEY_MSWHEEL_UP:
+ case(KEY_MSWHEEL_UP | KEY_ALT):
+ {
+ Scroll(Key & KEY_ALT?-1:-Opt.MsWheelDelta);
+ return TRUE;
+ }
+ case KEY_MSWHEEL_DOWN:
+ case(KEY_MSWHEEL_DOWN | KEY_ALT):
+ {
+ Scroll(Key & KEY_ALT?1:Opt.MsWheelDelta);
+ return TRUE;
+ }
+ case KEY_MSWHEEL_LEFT:
+ case(KEY_MSWHEEL_LEFT | KEY_ALT):
+ {
+ int Roll = Key & KEY_ALT?1:Opt.MsHWheelDelta;
+
+ for (int i=0; i<Roll; i++)
+ ProcessKey(KEY_LEFT);
+
+ return TRUE;
+ }
+ case KEY_MSWHEEL_RIGHT:
+ case(KEY_MSWHEEL_RIGHT | KEY_ALT):
+ {
+ int Roll = Key & KEY_ALT?1:Opt.MsHWheelDelta;
+
+ for (int i=0; i<Roll; i++)
+ ProcessKey(KEY_RIGHT);
+
+ return TRUE;
+ }
+ case KEY_HOME: case KEY_NUMPAD7:
+ {
+ Up(0x7fffff);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+ case KEY_ADD: // OFM: Gray+/Gray- navigation
+ {
+ CurFile=GetNextNavPos();
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+ else
+ DisplayTree(TRUE);
+
+ return TRUE;
+ }
+ case KEY_SUBTRACT: // OFM: Gray+/Gray- navigation
+ {
+ CurFile=GetPrevNavPos();
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+ else
+ DisplayTree(TRUE);
+
+ return TRUE;
+ }
+ case KEY_END: case KEY_NUMPAD1:
+ {
+ Down(0x7fffff);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+ case KEY_UP: case KEY_NUMPAD8:
+ {
+ Up(1);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+ case KEY_DOWN: case KEY_NUMPAD2:
+ {
+ Down(1);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+ case KEY_PGUP: case KEY_NUMPAD9:
+ {
+ CurTopFile-=Y2-Y1-3-ModalMode;
+ CurFile-=Y2-Y1-3-ModalMode;
+ DisplayTree(TRUE);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+ case KEY_PGDN: case KEY_NUMPAD3:
+ {
+ CurTopFile+=Y2-Y1-3-ModalMode;
+ CurFile+=Y2-Y1-3-ModalMode;
+ DisplayTree(TRUE);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+ default:
+
+ if ((Key>=KEY_ALT_BASE+0x01 && Key<=KEY_ALT_BASE+255) ||
+ (Key>=KEY_ALTSHIFT_BASE+0x01 && Key<=KEY_ALTSHIFT_BASE+255))
+ {
+ FastFind(Key);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+ }
+ else
+ break;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int TreeList::GetNextNavPos()
+{
+ int NextPos=CurFile;
+
+ if (CurFile+1 < TreeCount)
+ {
+ int CurDepth=ListData[CurFile]->Depth;
+
+ for (int I=CurFile+1; I < TreeCount; ++I)
+ if (ListData[I]->Depth == CurDepth)
+ {
+ NextPos=I;
+ break;
+ }
+ }
+
+ return NextPos;
+}
+
+int TreeList::GetPrevNavPos()
+{
+ int PrevPos=CurFile;
+
+ if (CurFile-1 > 0)
+ {
+ int CurDepth=ListData[CurFile]->Depth;
+
+ for (int I=CurFile-1; I > 0; --I)
+ if (ListData[I]->Depth == CurDepth)
+ {
+ PrevPos=I;
+ break;
+ }
+ }
+
+ return PrevPos;
+}
+
+void TreeList::Up(int Count)
+{
+ CurFile-=Count;
+ DisplayTree(TRUE);
+}
+
+
+void TreeList::Down(int Count)
+{
+ CurFile+=Count;
+ DisplayTree(TRUE);
+}
+
+void TreeList::Scroll(int Count)
+{
+ CurFile+=Count;
+ CurTopFile+=Count;
+ DisplayTree(TRUE);
+}
+
+void TreeList::CorrectPosition()
+{
+ if (!TreeCount)
+ {
+ CurFile=CurTopFile=0;
+ return;
+ }
+
+ int Height=Y2-Y1-3-(ModalMode);
+
+ if (CurTopFile+Height>TreeCount)
+ CurTopFile=TreeCount-Height;
+
+ if (CurFile<0)
+ CurFile=0;
+
+ if (CurFile > TreeCount-1)
+ CurFile=TreeCount-1;
+
+ if (CurTopFile<0)
+ CurTopFile=0;
+
+ if (CurTopFile > TreeCount-1)
+ CurTopFile=TreeCount-1;
+
+ if (CurFile<CurTopFile)
+ CurTopFile=CurFile;
+
+ if (CurFile>CurTopFile+Height-1)
+ CurTopFile=CurFile-(Height-1);
+}
+
+BOOL TreeList::SetCurDir(const wchar_t *NewDir,int ClosePlugin)
+{
+ if (!TreeCount)
+ Update(0);
+
+ if (TreeCount>0 && !SetDirPosition(NewDir))
+ {
+ Update(0);
+ SetDirPosition(NewDir);
+ }
+
+ if (GetFocus())
+ {
+ CtrlObject->CmdLine->SetCurDir(NewDir);
+ CtrlObject->CmdLine->Show();
+ }
+
+ return TRUE; //???
+}
+
+int TreeList::SetDirPosition(const wchar_t *NewDir)
+{
+ long I;
+
+ for (I=0; I<TreeCount; I++)
+ {
+ if (!StrCmpI(NewDir,ListData[I]->strName))
+ {
+ WorkDir=CurFile=I;
+ CurTopFile=CurFile-(Y2-Y1-1)/2;
+ CorrectPosition();
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int TreeList::GetCurDir(FARString &strCurDir)
+{
+ if (!TreeCount)
+ {
+ if (ModalMode==MODALTREE_FREE)
+ strCurDir = strRoot;
+ else
+ strCurDir.Clear();
+ }
+ else
+ strCurDir = ListData[CurFile]->strName; //BUGBUG
+
+ return (int)strCurDir.GetLength();
+}
+
+
+
+int TreeList::ProcessMouse(MOUSE_EVENT_RECORD *MouseEvent)
+{
+ int OldFile=CurFile;
+ int RetCode;
+
+ if (Opt.ShowPanelScrollbar && MouseX==X2 &&
+ (MouseEvent->dwButtonState & 1) && !IsDragging())
+ {
+ int ScrollY=Y1+1;
+ int Height=Y2-Y1-3;
+
+ if (MouseY==ScrollY)
+ {
+ while (IsMouseButtonPressed())
+ ProcessKey(KEY_UP);
+
+ if (!ModalMode)
+ SetFocus();
+
+ return TRUE;
+ }
+
+ if (MouseY==ScrollY+Height-1)
+ {
+ while (IsMouseButtonPressed())
+ ProcessKey(KEY_DOWN);
+
+ if (!ModalMode)
+ SetFocus();
+
+ return TRUE;
+ }
+
+ if (MouseY>ScrollY && MouseY<ScrollY+Height-1 && Height>2)
+ {
+ CurFile=(TreeCount-1)*(MouseY-ScrollY)/(Height-2);
+ DisplayTree(TRUE);
+
+ if (!ModalMode)
+ SetFocus();
+
+ return TRUE;
+ }
+ }
+
+ if (Panel::PanelProcessMouse(MouseEvent,RetCode))
+ return(RetCode);
+
+ if (MouseEvent->dwMousePosition.Y>Y1 && MouseEvent->dwMousePosition.Y<Y2-2)
+ {
+ if (!ModalMode)
+ SetFocus();
+
+ MoveToMouse(MouseEvent);
+ DisplayTree(TRUE);
+
+ if (!TreeCount)
+ return TRUE;
+
+ if (((MouseEvent->dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) &&
+ MouseEvent->dwEventFlags==DOUBLE_CLICK) ||
+ ((MouseEvent->dwButtonState & RIGHTMOST_BUTTON_PRESSED) &&
+ !MouseEvent->dwEventFlags) ||
+ (OldFile!=CurFile && Opt.Tree.AutoChangeFolder && !ModalMode))
+ {
+ ProcessEnter();
+ return TRUE;
+ }
+
+ return TRUE;
+ }
+
+ if (MouseEvent->dwMousePosition.Y<=Y1+1)
+ {
+ if (!ModalMode)
+ SetFocus();
+
+ if (!TreeCount)
+ return TRUE;
+
+ while (IsMouseButtonPressed() && MouseY<=Y1+1)
+ Up(1);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+
+ if (MouseEvent->dwMousePosition.Y>=Y2-2)
+ {
+ if (!ModalMode)
+ SetFocus();
+
+ if (!TreeCount)
+ return TRUE;
+
+ while (IsMouseButtonPressed() && MouseY>=Y2-2)
+ Down(1);
+
+ if (Opt.Tree.AutoChangeFolder && !ModalMode)
+ ProcessKey(KEY_ENTER);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void TreeList::MoveToMouse(MOUSE_EVENT_RECORD *MouseEvent)
+{
+ CurFile=CurTopFile+MouseEvent->dwMousePosition.Y-Y1-1;
+ CorrectPosition();
+}
+
+
+void TreeList::ProcessEnter()
+{
+ TreeItem *CurPtr;
+ DWORD Attr;
+ CurPtr=ListData[CurFile];
+
+ if ((Attr=apiGetFileAttributes(CurPtr->strName))!=INVALID_FILE_ATTRIBUTES && (Attr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ if (!ModalMode && FarChDir(CurPtr->strName))
+ {
+ Panel *AnotherPanel=GetRootPanel();
+ SetCurDir(CurPtr->strName,TRUE);
+ Show();
+ AnotherPanel->SetCurDir(CurPtr->strName,TRUE);
+ AnotherPanel->Redraw();
+ }
+ }
+ else
+ {
+ DelTreeName(CurPtr->strName);
+ Update(UPDATE_KEEP_SELECTION);
+ Show();
+ }
+}
+
+
+int TreeList::ReadTreeFile()
+{
+ size_t RootLength=strRoot.IsEmpty()?0:strRoot.GetLength()-1;
+ FARString strName;
+ //SaveState();
+ FlushCache();
+ MkTreeFileName(strRoot,strName);
+
+ File TreeFile;
+ if (MustBeCached(strRoot) || (!TreeFile.Open(strName, FILE_READ_DATA, FILE_SHARE_READ, nullptr, OPEN_EXISTING)))
+ {
+ if (!GetCacheTreeName(strRoot,strName,FALSE) || (!TreeFile.Open(strName, FILE_READ_DATA, FILE_SHARE_READ, nullptr, OPEN_EXISTING)))
+ {
+ //RestoreState();
+ return FALSE;
+ }
+ }
+
+ if (ListData)
+ {
+ for (long i=0; i<TreeCount; i++)
+ delete ListData[i];
+
+ free(ListData);
+ }
+
+ ListData=nullptr;
+ TreeCount=0;
+ {
+ FARString strLastDirName;
+ GetFileString GetStr(TreeFile);
+ LPWSTR Record=nullptr;
+ int RecordLength=0;
+ while(GetStr.GetString(&Record, CP_WIDE_LE, RecordLength) > 0)
+ {
+ FARString strDirName(strRoot, RootLength);
+ strDirName.Append(Record, RecordLength);
+ if (!IsSlash(*Record) || !StrCmpI(strDirName,strLastDirName))
+ {
+ continue;
+ }
+
+ strLastDirName=strDirName;
+ size_t Pos=0;
+ if(strDirName.Pos(Pos, L'\n'))
+ {
+ strDirName.Truncate(Pos);
+ }
+
+ if (RootLength>0 && strDirName.At(RootLength-1)!=L':' && IsSlash(strDirName.At(RootLength)) && !strDirName.At(RootLength+1))
+ {
+ strDirName.Truncate(RootLength);
+ }
+
+ if (!(TreeCount & 255))
+ {
+ TreeItem **TmpListData=(TreeItem **)realloc(ListData,(TreeCount+256+1)*sizeof(TreeItem*));
+
+ if (!TmpListData)
+ {
+ if (ListData)
+ {
+ for (long i=0; i<TreeCount; i++)
+ delete ListData[i];
+
+ free(ListData);
+ }
+
+ ListData=nullptr;
+ TreeCount=0;
+ TreeFile.Close();
+ //RestoreState();
+ return FALSE;
+ }
+
+ ListData = TmpListData;
+ }
+
+ ListData[TreeCount] = new TreeItem;
+ ListData[TreeCount]->Clear();
+ ListData[TreeCount]->strName = strDirName;
+ TreeCount++;
+ }
+ }
+
+ TreeFile.Close();
+
+ if (!TreeCount)
+ return FALSE;
+
+ NumericSort=FALSE;
+ CaseSensitiveSort=FALSE;
+ far_qsort(TreeCache.ListName,TreeCache.TreeCount,sizeof(wchar_t*),SortCacheList);
+ return FillLastData();
+}
+
+
+int TreeList::FindPartName(const wchar_t *Name,int Next,int Direct,int ExcludeSets)
+{
+ FARString strMask;
+ strMask = Name;
+ strMask += L"*";
+
+ if (ExcludeSets)
+ {
+ ReplaceStrings(strMask,L"[",L"<[%>",-1,1);
+ ReplaceStrings(strMask,L"]",L"[]]",-1,1);
+ ReplaceStrings(strMask,L"<[%>",L"[[]",-1,1);
+ }
+
+ for (int i=CurFile+(Next?Direct:0); i >= 0 && i < TreeCount; i+=Direct)
+ {
+ if (CmpName(strMask,ListData[i]->strName,true))
+ {
+ CurFile=i;
+ CurTopFile=CurFile-(Y2-Y1-1)/2;
+ DisplayTree(TRUE);
+ return TRUE;
+ }
+ }
+
+ for (int i=(Direct > 0)?0:TreeCount-1; (Direct > 0) ? i < CurFile:i > CurFile; i+=Direct)
+ {
+ if (CmpName(strMask,ListData[i]->strName,true))
+ {
+ CurFile=i;
+ CurTopFile=CurFile-(Y2-Y1-1)/2;
+ DisplayTree(TRUE);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int TreeList::GetSelCount()
+{
+ return 1;
+}
+
+
+int TreeList::GetSelName(FARString *strName,DWORD &FileAttr,DWORD &FileMode,FAR_FIND_DATA_EX *fd)
+{
+ FileMode = 0640;
+
+ if (!strName)
+ {
+ GetSelPosition=0;
+ return TRUE;
+ }
+
+ if (!GetSelPosition)
+ {
+ GetCurDir(*strName);
+
+ FileAttr = FILE_ATTRIBUTE_DIRECTORY;
+ FileMode|= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ GetSelPosition++;
+ return TRUE;
+ }
+
+ GetSelPosition=0;
+ return FALSE;
+}
+
+
+int TreeList::GetCurName(FARString &strName)
+{
+ if (!TreeCount)
+ {
+ strName.Clear();
+ return FALSE;
+ }
+
+ strName = ListData[CurFile]->strName;
+ return TRUE;
+}
+
+void TreeList::AddTreeName(const wchar_t *Name)
+{
+ if (!*Name)
+ return;
+
+ FARString strFullName;
+ ConvertNameToFull(Name, strFullName);
+ Name = strFullName;
+
+ if (!LastSlash(Name))
+ return;
+
+ ReadCache(strFullName);
+
+ for (long CachePos = 0; CachePos < TreeCache.TreeCount; CachePos++)
+ {
+ int Result = StrCmpI(TreeCache.ListName[CachePos], Name);
+
+ if (!Result)
+ break;
+
+ if (Result > 0)
+ {
+ TreeCache.Insert(CachePos, Name);
+ break;
+ }
+ }
+}
+
+
+void TreeList::DelTreeName(const wchar_t *Name)
+{
+ if (!*Name)
+ return;
+
+ FARString strFullName;
+ ConvertNameToFull(Name, strFullName);
+ Name = strFullName;
+ ReadCache(strFullName);
+
+ for (long CachePos = 0; CachePos < TreeCache.TreeCount; CachePos++)
+ {
+ const wchar_t* wszDirName = TreeCache.ListName[CachePos];
+ int Length = StrLength(Name);
+ int DirLength = StrLength(wszDirName);
+
+ if (DirLength < Length) continue;
+
+ if (!StrCmpNI(Name, wszDirName, Length) && (!wszDirName[Length] || IsSlash(wszDirName[Length])))
+ {
+ TreeCache.Delete(CachePos);
+ CachePos--;
+ }
+ }
+}
+
+
+void TreeList::RenTreeName(const wchar_t *SrcName,const wchar_t *DestName)
+{
+ if (!*SrcName || !*DestName)
+ return;
+
+ FARString SrcNameFull, DestNameFull;
+ ConvertNameToFull(SrcName, SrcNameFull);
+ ConvertNameToFull(DestName, DestNameFull);
+ ReadCache(SrcNameFull);
+ int SrcLength = StrLength(SrcName);
+
+ for (int CachePos = 0; CachePos < TreeCache.TreeCount; CachePos++)
+ {
+ const wchar_t* DirName = TreeCache.ListName[CachePos];
+
+ if (!StrCmpNI(SrcName,DirName,SrcLength) && (!DirName[SrcLength] || IsSlash(DirName[SrcLength])))
+ {
+ FARString strNewName = DestName;
+ strNewName += DirName + SrcLength;
+
+ if (TreeCache.ListName[CachePos])
+ free(TreeCache.ListName[CachePos]);
+
+ TreeCache.ListName[CachePos] = wcsdup(strNewName);
+ }
+ }
+}
+
+
+void TreeList::ReadSubTree(const wchar_t *Path)
+{
+ ChangePriority ChPriority(ChangePriority::NORMAL);
+ //SaveScreen SaveScr;
+ TPreRedrawFuncGuard preRedrawFuncGuard(TreeList::PR_MsgReadTree);
+ ScanTree ScTree(FALSE);
+ FAR_FIND_DATA_EX fdata;
+ FARString strDirName;
+ FARString strFullName;
+ int Count=0;
+ DWORD FileAttr;
+
+ if ((FileAttr=apiGetFileAttributes(Path))==INVALID_FILE_ATTRIBUTES || !(FileAttr & FILE_ATTRIBUTE_DIRECTORY))
+ return;
+
+ ConvertNameToFull(Path, strDirName);
+ AddTreeName(strDirName);
+ int FirstCall=TRUE, AscAbort=FALSE;
+ ScTree.SetFindPath(strDirName,L"*",0);
+ LastScrX = ScrX;
+ LastScrY = ScrY;
+
+ while (ScTree.GetNextName(&fdata, strFullName))
+ {
+ if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ TreeList::MsgReadTree(Count+1,FirstCall);
+
+ if (CheckForEscSilent())
+ {
+ AscAbort=ConfirmAbortOp();
+ FirstCall=TRUE;
+ }
+
+ if (AscAbort)
+ break;
+
+ AddTreeName(strFullName);
+ ++Count;
+ }
+ }
+}
+
+
+void TreeList::ClearCache(int EnableFreeMem)
+{
+ TreeCache.Clean();
+}
+
+
+void TreeList::ReadCache(const wchar_t *TreeRoot)
+{
+ FARString strTreeName;
+ FILE *TreeFile=nullptr;
+
+ if (!StrCmp(MkTreeFileName(TreeRoot,strTreeName),TreeCache.strTreeName))
+ return;
+
+ if (TreeCache.TreeCount)
+ FlushCache();
+
+ if (MustBeCached(TreeRoot) || !(TreeFile=fopen(Wide2MB(strTreeName).c_str(),FOPEN_READ)))
+ if (!GetCacheTreeName(TreeRoot,strTreeName,FALSE) || !(TreeFile=fopen(Wide2MB(strTreeName).c_str(),FOPEN_READ)))
+ {
+ ClearCache(1);
+ return;
+ }
+
+ TreeCache.strTreeName = strTreeName;
+ wchar_t *DirName=new(std::nothrow) wchar_t[NT_MAX_PATH];
+
+ if (DirName)
+ {
+ while (fgetws(DirName,NT_MAX_PATH,TreeFile))
+ {
+ if (!IsSlash(*DirName))
+ continue;
+
+ wchar_t *ChPtr=wcschr(DirName,L'\n');
+
+ if (ChPtr)
+ *ChPtr=0;
+
+ TreeCache.Add(DirName);
+ }
+
+ delete[] DirName;
+ }
+
+ fclose(TreeFile);
+}
+
+
+void TreeList::FlushCache()
+{
+ FILE *TreeFile;
+
+ if (!TreeCache.strTreeName.IsEmpty())
+ {
+ DWORD FileAttributes=apiGetFileAttributes(TreeCache.strTreeName);
+
+ if (FileAttributes != INVALID_FILE_ATTRIBUTES)
+ apiSetFileAttributes(TreeCache.strTreeName,FILE_ATTRIBUTE_NORMAL);
+
+ if (!(TreeFile=fopen(Wide2MB(TreeCache.strTreeName).c_str(), FOPEN_WRITE)))
+ {
+ ClearCache(1);
+ return;
+ }
+
+ far_qsort(TreeCache.ListName,TreeCache.TreeCount,sizeof(wchar_t*),SortCacheList);
+
+ for (int i=0; i<TreeCache.TreeCount; i++)
+ fwprintf(TreeFile,L"%ls\n",TreeCache.ListName[i]);
+
+ if (fclose(TreeFile)==EOF)
+ {
+ clearerr(TreeFile);
+ fclose(TreeFile);
+ apiDeleteFile(TreeCache.strTreeName);
+ Message(MSG_WARNING|MSG_ERRORTYPE,1,MSG(MError),MSG(MCannotSaveTree),
+ TreeCache.strTreeName,MSG(MOk));
+ }
+ else if (FileAttributes != INVALID_FILE_ATTRIBUTES) // вернем атрибуты (если получится :-)
+ apiSetFileAttributes(TreeCache.strTreeName,FileAttributes);
+ }
+
+ ClearCache(1);
+}
+
+
+void TreeList::UpdateViewPanel()
+{
+ if (!ModalMode)
+ {
+ Panel *AnotherPanel=GetRootPanel();
+ FARString strCurName;
+ GetCurDir(strCurName);
+
+ if (AnotherPanel->GetType()==QVIEW_PANEL && SetCurPath())
+ ((QuickView *)AnotherPanel)->ShowFile(strCurName,FALSE,nullptr);
+ }
+}
+
+
+int TreeList::GoToFile(long idxItem)
+{
+ if ((DWORD)idxItem < (DWORD)TreeCount)
+ {
+ CurFile=idxItem;
+ CorrectPosition();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int TreeList::GoToFile(const wchar_t *Name,BOOL OnlyPartName)
+{
+ return GoToFile(FindFile(Name,OnlyPartName));
+}
+
+long TreeList::FindFile(const wchar_t *Name,BOOL OnlyPartName)
+{
+ for (long I=0; I<TreeCount; I++)
+ {
+ const wchar_t *CurPtrName=OnlyPartName?PointToName(ListData[I]->strName):ListData[I]->strName.CPtr();
+
+ if (!StrCmp(Name,CurPtrName))
+ return I;
+
+ if (!StrCmpI(Name,CurPtrName))
+ return I;
+ }
+
+ return -1;
+}
+
+
+long TreeList::FindFirst(const wchar_t *Name)
+{
+ return FindNext(0,Name);
+}
+
+long TreeList::FindNext(int StartPos, const wchar_t *Name)
+{
+ if ((DWORD)StartPos < (DWORD)TreeCount)
+ {
+ for (int I=StartPos; I < TreeCount; I++)
+ {
+ if (CmpName(Name,ListData[I]->strName,true))
+ if (!TestParentFolderName(ListData[I]->strName))
+ return I;
+ }
+ }
+
+ return -1;
+}
+
+int TreeList::GetFileName(FARString &strName,int Pos,DWORD &FileAttr)
+{
+ if (Pos < 0 || Pos >= TreeCount)
+ return FALSE;
+
+ strName = ListData[Pos]->strName;
+ FileAttr=FILE_ATTRIBUTE_DIRECTORY|apiGetFileAttributes(ListData[Pos]->strName);
+ return TRUE;
+}
+
+int _cdecl SortList(const void *el1,const void *el2)
+{
+ return TreeCmp(((TreeItem **)el1)[0]->strName, ((TreeItem **)el2)[0]->strName, StaticSortNumeric, StaticSortCaseSensitive);
+}
+
+int _cdecl SortCacheList(const void *el1,const void *el2)
+{
+ return TreeCmp(*(wchar_t **)el1, *(wchar_t **)el2, StaticSortNumeric, 0);
+}
+
+int TreeCmp(const wchar_t *Str1, const wchar_t *Str2, int Numeric, int CaseSensitive)
+{
+ typedef int (__cdecl *CMPFUNC)(const wchar_t *, int, const wchar_t *, int);
+ static CMPFUNC funcs[2][2] = { {StrCmpNN, StrCmpNNI}, {NumStrCmpN, NumStrCmpNI} };
+ CMPFUNC cmpfunc = funcs[Numeric?1:0][CaseSensitive?0:1];
+
+ if (*Str1 == GOOD_SLASH && *Str2 == GOOD_SLASH)
+ {
+ Str1++;
+ Str2++;
+ }
+
+ const wchar_t *s1 = wcschr(Str1, GOOD_SLASH);
+ const wchar_t *s2 = wcschr(Str2, GOOD_SLASH);
+
+ while (s1 && s2)
+ {
+ int r = cmpfunc(Str1,static_cast<int>(s1-Str1),Str2,static_cast<int>(s2-Str2));
+
+ if (r)
+ return r;
+
+ Str1 = s1 + 1;
+ Str2 = s2 + 1;
+ s1 = wcschr(Str1, GOOD_SLASH);
+ s2 = wcschr(Str2, GOOD_SLASH);
+ }
+
+ if (s1 || s2)
+ {
+ int r = cmpfunc(Str1,s1?static_cast<int>(s1-Str1):-1,Str2,s2?static_cast<int>(s2-Str2):-1);
+
+ if (r)
+ return r;
+
+ return s1 ? 1 : -1;
+ }
+
+ return cmpfunc(Str1, -1, Str2,-1);
+}
+
+/* $ 16.10.2000 tran
+ функция, определяющаяя необходимость кеширования
+ файла */
+int TreeList::MustBeCached(const wchar_t *Root)
+{
+ UINT type;
+ type=FAR_GetDriveType(Root);
+
+ if (type==DRIVE_UNKNOWN ||
+ type==DRIVE_NO_ROOT_DIR ||
+ type==DRIVE_REMOVABLE ||
+ IsDriveTypeCDROM(type)
+ )
+ {
+ if (type==DRIVE_REMOVABLE)
+ {
+ if (Upper(Root[0])==L'A' || Upper(Root[0])==L'B')
+ return FALSE; // это дискеты
+ }
+
+ return TRUE;
+ // кешируются CD, removable и неизвестно что :)
+ }
+
+ /* остались
+ DRIVE_REMOTE
+ DRIVE_RAMDISK
+ DRIVE_FIXED
+ */
+ return FALSE;
+}
+
+void TreeList::SetFocus()
+{
+ Panel::SetFocus();
+ SetTitle();
+ SetMacroMode(FALSE);
+}
+
+void TreeList::KillFocus()
+{
+ if (CurFile<TreeCount)
+ {
+ if (apiGetFileAttributes(ListData[CurFile]->strName)==INVALID_FILE_ATTRIBUTES)
+ {
+ DelTreeName(ListData[CurFile]->strName);
+ Update(UPDATE_KEEP_SELECTION);
+ }
+ }
+
+ Panel::KillFocus();
+ SetMacroMode(TRUE);
+}
+
+void TreeList::SetMacroMode(int Restore)
+{
+ if (!CtrlObject)
+ return;
+
+ if (PrevMacroMode == -1)
+ PrevMacroMode = CtrlObject->Macro.GetMode();
+
+ CtrlObject->Macro.SetMode(Restore ? PrevMacroMode:MACRO_TREEPANEL);
+}
+
+BOOL TreeList::UpdateKeyBar()
+{
+ KeyBar *KB = CtrlObject->MainKeyBar;
+ KB->SetAllGroup(KBL_MAIN, MKBTreeF1, 12);
+ KB->SetAllGroup(KBL_SHIFT, MKBTreeShiftF1, 12);
+ KB->SetAllGroup(KBL_ALT, MKBTreeAltF1, 12);
+ KB->SetAllGroup(KBL_CTRL, MKBTreeCtrlF1, 12);
+ KB->SetAllGroup(KBL_CTRLSHIFT, MKBTreeCtrlShiftF1, 12);
+ KB->SetAllGroup(KBL_CTRLALT, MKBTreeCtrlAltF1, 12);
+ KB->SetAllGroup(KBL_ALTSHIFT, MKBTreeAltShiftF1, 12);
+ KB->SetAllGroup(KBL_CTRLALTSHIFT, MKBTreeCtrlAltShiftF1, 12);
+ DynamicUpdateKeyBar();
+ return TRUE;
+}
+
+void TreeList::DynamicUpdateKeyBar()
+{
+ KeyBar *KB = CtrlObject->MainKeyBar;
+ KB->ReadRegGroup(L"Tree",Opt.strLanguage);
+ KB->SetAllRegGroup();
+}
+
+void TreeList::SetTitle()
+{
+ if (GetFocus())
+ {
+ FARString strTitleDir(L"{");
+
+ const wchar_t *Ptr = ListData ? ListData[CurFile]->strName.CPtr() : L"";
+
+ if (*Ptr)
+ {
+ strTitleDir += Ptr;
+ strTitleDir += L" - ";
+ }
+
+ strTitleDir += L"Tree}";
+
+ ConsoleTitle::SetFarTitle(strTitleDir);
+ }
+}
+
+/*
+ "Local AppData" = [HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders]/Local AppData
+ "AppData" = [HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders]/AppData
+
+*/
+
+static void MkTreeName(FARString &out, const wchar_t *RootDir, const char *ext)
+{
+ struct stat s = {};
+ int r = sdc_stat(Wide2MB(RootDir).c_str(), &s);
+ if (r == 0) {
+ char tmp[128];
+ sprintf(tmp, "tree/%llx-%llx.%s",
+ (unsigned long long)s.st_rdev, (unsigned long long)s.st_ino, ext);
+ out = InMyTemp(tmp);
+ } else {
+ std::string tmp = InMyTemp("tree/wtf-");
+ const std::string &RootMB = Wide2MB(RootDir);
+ for (char c : RootMB) {
+ tmp+= (c==GOOD_SLASH) ? '@' : c;
+ }
+ tmp+= '.';
+ tmp+= ext;
+ out = tmp;
+ }
+}
+
+FARString &TreeList::MkTreeFileName(const wchar_t *RootDir,FARString &strDest)
+{
+ MkTreeName(strDest, RootDir, "far");
+ return strDest;
+}
+
+FARString &TreeList::MkTreeCacheFolderName(const wchar_t *RootDir,FARString &strDest)
+{
+ MkTreeName(strDest, RootDir, "cache");
+ return strDest;
+}
+
+
+/*
+ Opt.Tree.LocalDisk
+ Opt.Tree.NetDisk
+ Opt.Tree.NetPath
+ Opt.Tree.RemovableDisk
+ Opt.Tree.CDROM
+ Opt.Tree.SavedTreePath
+
+ локальных дисков - "X.nnnnnnnn.tree"
+ сетевых дисков - "X.nnnnnnnn.tree"
+ сетевых путей - "Server.share.tree"
+ сменных дисков(DRIVE_REMOVABLE) - "Far.nnnnnnnn.tree"
+ сменных дисков(CD) - "Label.nnnnnnnn.tree"
+
+*/
+FARString &TreeList::CreateTreeFileName(const wchar_t *Path,FARString &strDest)
+{
+#if 0
+ char RootPath[NM];
+ RootPath = ExtractPathRoot(Path);
+ UINT DriveType = FAR_GetDriveType(RootPath,nullptr,FALSE);
+ // получение инфы о томе
+ char VolumeName[NM],FileSystemName[NM];
+ DWORD MaxNameLength,FileSystemFlags,VolumeNumber;
+
+ if (!GetVolumeInformation(RootDir,VolumeName,sizeof(VolumeName),&VolumeNumber,
+ &MaxNameLength,&FileSystemFlags,
+ FileSystemName,sizeof(FileSystemName)))
+ Opt.Tree.SavedTreePath
+#endif
+ return strDest;
+}
+
+BOOL TreeList::GetItem(int Index,void *Dest)
+{
+ if (Index == -1 || Index == -2)
+ Index=GetCurrentPos();
+
+ if (Index >= (int)TreeCount)
+ return FALSE;
+
+ *((TreeItem *)Dest) = *ListData[Index];
+ return TRUE;
+}
+
+int TreeList::GetCurrentPos()
+{
+ return CurFile;
+}
+
+bool TreeList::SaveState()
+{
+ if (SaveListData) delete [] SaveListData;
+
+ SaveListData=nullptr;
+ SaveTreeCount=SaveWorkDir=0;
+
+ if (TreeCount > 0)
+ {
+ SaveListData = new(std::nothrow) TreeItem[TreeCount];
+
+ if (SaveListData)
+ {
+ for (int i=0; i<TreeCount; i++)
+ SaveListData[i] = *ListData[i];
+
+ SaveTreeCount=TreeCount;
+ SaveWorkDir=WorkDir;
+ TreeCache.Copy(&tempTreeCache);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool TreeList::RestoreState()
+{
+ if (ListData)
+ {
+ for (long i=0; i<TreeCount; i++)
+ delete ListData[i];
+
+ free(ListData);
+ }
+
+ TreeCount=WorkDir=0;
+ ListData=nullptr;
+
+ if (SaveTreeCount > 0 && (ListData=(TreeItem **)malloc(SaveTreeCount*sizeof(TreeItem*))) )
+ {
+ for (int i=0; i<SaveTreeCount; i++)
+ {
+ ListData[i] = new TreeItem;
+ *ListData[i] = SaveListData[i];
+ }
+
+ TreeCount=SaveTreeCount;
+ WorkDir=SaveWorkDir;
+ tempTreeCache.Copy(&TreeCache);
+ tempTreeCache.Clean();
+ return true;
+ }
+
+ return false;
+}