From d1b58fa87b95bfeed2a55a90c18810dd090ce2a3 Mon Sep 17 00:00:00 2001 From: Fernando Godoy Date: Sat, 4 Apr 2026 11:59:15 -0300 Subject: [PATCH 1/2] Add panel tree view with layout preservation fixes --- .gitignore | 9 + src/cfgdlg.h | 2 + src/dialogs4.cpp | 2 + src/dialogs5.cpp | 1 + src/fileswn0.cpp | 1 + src/fileswn1.cpp | 489 +++++++++++++++++++++++++++++++++++++++++++++ src/fileswn2.cpp | 303 ++++++++++++++++++++++++++++ src/fileswnb.cpp | 173 +++++++++++++++- src/fileswnd.h | 38 ++++ src/lang/lang.rc | 32 +-- src/lang/lang.rh | 1 + src/lang/texts.rc2 | 1 + src/mainwnd.h | 7 + src/mainwnd1.cpp | 21 ++ src/mainwnd2.cpp | 10 + src/mainwnd3.cpp | 29 ++- src/mainwnd4.cpp | 97 +++++++++ src/menu4.cpp | 1 + src/resource.rh2 | 3 + src/salamand.rc | 1 + src/texts.rh2 | 1 + 21 files changed, 1203 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 2781864c6..bfb80a468 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,15 @@ Thumbs.db .vs/ *.aps *.vcxproj.user + +# Local build output +.localbuild/ +out/ + +# Generated plugin build output +/src/plugins/pictview/vcxproj/exif/salamander/ +/src/plugins/pictview/vcxproj/salamander/ + /src/plugins/automation/generated/automation.tlb /src/plugins/automation/generated/dlldata.c /src/plugins/automation/generated/salamander_h.h diff --git a/src/cfgdlg.h b/src/cfgdlg.h index d5d7882d0..a927c86a9 100644 --- a/src/cfgdlg.h +++ b/src/cfgdlg.h @@ -211,6 +211,7 @@ struct CConfiguration HotPathsBarVisible, // toolbar visibility DriveBarVisible, // drive bar visibility DriveBar2Visible, // second drive bar visibility + TreeViewVisible, // active-panel tree visibility UseSalOpen, // should salopen.exe be used (otherwise association runs directly) NetwareFastDirMove, // should fast-dir-move (rename directories) be used on the Novell Netware? (otherwise rename files only, directories are created + old empty ones deleted) (REASON: for some users, fast-dir-move works on Novell and they don’t want to wait) UseAsyncCopyAlg, // Win7+ only (older OS: always FALSE): should asynchronous file copy algorithm be used on network drives? @@ -384,6 +385,7 @@ struct CConfiguration int DriveBarIndex; int DriveBarBreak; int DriveBarWidth; + int TreeViewWidth; int GripsVisible; // Change drive diff --git a/src/dialogs4.cpp b/src/dialogs4.cpp index 95cae2a08..71df3f714 100644 --- a/src/dialogs4.cpp +++ b/src/dialogs4.cpp @@ -317,6 +317,7 @@ CConfiguration::CConfiguration() HotPathsBarVisible = FALSE; DriveBarVisible = TRUE; DriveBar2Visible = FALSE; + TreeViewVisible = FALSE; IconSpacingVert = 43; IconSpacingHorz = 43; TileSpacingVert = 8; @@ -491,6 +492,7 @@ CConfiguration::CConfiguration() DriveBarIndex = 5; DriveBarBreak = TRUE; DriveBarWidth = 1; // dummy + TreeViewWidth = 200; GripsVisible = TRUE; diff --git a/src/dialogs5.cpp b/src/dialogs5.cpp index 5bd999d5b..c6a0d1566 100644 --- a/src/dialogs5.cpp +++ b/src/dialogs5.cpp @@ -3404,6 +3404,7 @@ void CCfgPagePanels::Transfer(CTransferInfo& ti) ti.CheckBox(IDC_QUICKSEARCH_ALT, Configuration.QuickSearchEnterAlt); ti.CheckBox(IDE_PRIMARYCTXMENU, Configuration.PrimaryContextMenu); ti.CheckBox(IDC_SHIFTFORHOTPATHS, Configuration.ShiftForHotPaths); + ti.CheckBox(IDC_PANELTREEVIEW, Configuration.TreeViewVisible); ti.CheckBox(IDC_CLICKTORENAME, Configuration.ClickQuickRename); ti.CheckBox(IDC_SORTUSESLOCALE, Configuration.SortUsesLocale); ti.CheckBox(IDC_SORTDETECTNUMBERS, Configuration.SortDetectNumbers); diff --git a/src/fileswn0.cpp b/src/fileswn0.cpp index 882e101c2..0a6cddabc 100644 --- a/src/fileswn0.cpp +++ b/src/fileswn0.cpp @@ -3291,6 +3291,7 @@ void CFilesWindow::SetupListBoxScrollBars() void CFilesWindow::RefreshForConfig() { CALL_STACK_MESSAGE1("CFilesWindow::RefreshForConfig()"); + UpdateTreeView(MainWindow->LeftPanel == this); if (Is(ptZIPArchive)) { // we ensure a refresh of the archive by corrupting the archive's size SetZIPArchiveSize(CQuadWord(-1, -1)); diff --git a/src/fileswn1.cpp b/src/fileswn1.cpp index 1b504d80c..a6f7d4e2a 100644 --- a/src/fileswn1.cpp +++ b/src/fileswn1.cpp @@ -19,6 +19,240 @@ #include "geticon.h" #include "shiconov.h" +struct CTreeViewPopulateEntry +{ + char Name[MAX_PATH]; + char FullPath[MAX_PATH]; + BOOL IsDirectory; +}; + +static char* DuplicateTreeViewString(const char* text) +{ + int len = (int)strlen(text) + 1; + char* copy = (char*)malloc(len); + if (copy != NULL) + memcpy(copy, text, len); + return copy; +} + +static void FreeTreeViewNodeData(CTreeViewNodeData* itemData) +{ + if (itemData == NULL) + return; + + free(itemData->FullPath); + free(itemData->FocusPath); + free(itemData->FocusName); + free(itemData); +} + +static BOOL GetTreeViewItemData(HWND hTreeView, HTREEITEM hItem, CTreeViewNodeData* itemData) +{ + if (hTreeView == NULL || hItem == NULL) + return FALSE; + + TVITEM item; + memset(&item, 0, sizeof(item)); + item.mask = TVIF_PARAM; + item.hItem = hItem; + if (!TreeView_GetItem(hTreeView, &item)) + return FALSE; + if (item.lParam == 0) + return FALSE; + + *itemData = *(CTreeViewNodeData*)item.lParam; + return TRUE; +} + +static CTreeViewNodeData* GetTreeViewItemDataPtr(HWND hTreeView, HTREEITEM hItem) +{ + if (hTreeView == NULL || hItem == NULL) + return NULL; + + TVITEM item; + memset(&item, 0, sizeof(item)); + item.mask = TVIF_PARAM; + item.hItem = hItem; + if (!TreeView_GetItem(hTreeView, &item) || item.lParam == 0) + return NULL; + + return (CTreeViewNodeData*)item.lParam; +} + +static const char* GetTreeViewItemPath(HWND hTreeView, HTREEITEM hItem) +{ + CTreeViewNodeData itemData; + if (!GetTreeViewItemData(hTreeView, hItem, &itemData)) + return NULL; + + return itemData.FullPath; +} + +static BOOL IsTreeViewDirectoryItem(HWND hTreeView, HTREEITEM hItem) +{ + CTreeViewNodeData itemData; + if (!GetTreeViewItemData(hTreeView, hItem, &itemData)) + return FALSE; + + return itemData.Type == tvntDirectory; +} + +static HTREEITEM FindTreeViewChildByPath(HWND hTreeView, HTREEITEM hParent, const char* path) +{ + HTREEITEM hChild = TreeView_GetChild(hTreeView, hParent); + while (hChild != NULL) + { + const char* childPath = GetTreeViewItemPath(hTreeView, hChild); + if (childPath != NULL && IsTheSamePath(childPath, path)) + return hChild; + hChild = TreeView_GetNextSibling(hTreeView, hChild); + } + return NULL; +} + +static void SetTreeViewItemChildren(HWND hTreeView, HTREEITEM hItem, int children) +{ + TVITEM item; + memset(&item, 0, sizeof(item)); + item.mask = TVIF_CHILDREN; + item.hItem = hItem; + item.cChildren = children; + TreeView_SetItem(hTreeView, &item); +} + +static BOOL GetTreeViewShellIconIndexes(const char* path, BOOL isDirectory, + int* imageIndex, int* selectedImageIndex) +{ + SHFILEINFO sfi; + memset(&sfi, 0, sizeof(sfi)); + + DWORD attributes = isDirectory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL; + UINT flags = SHGFI_SYSICONINDEX | SHGFI_SMALLICON; + if (path == NULL || path[0] == 0) + flags |= SHGFI_USEFILEATTRIBUTES; + + if (SHGetFileInfo(path, attributes, &sfi, sizeof(sfi), flags) == 0) + return FALSE; + + *imageIndex = sfi.iIcon; + + if (isDirectory) + { + SHFILEINFO selectedSfi; + memset(&selectedSfi, 0, sizeof(selectedSfi)); + if (SHGetFileInfo(path, attributes, &selectedSfi, sizeof(selectedSfi), + flags | SHGFI_OPENICON) != 0) + *selectedImageIndex = selectedSfi.iIcon; + else + *selectedImageIndex = *imageIndex; + } + else + *selectedImageIndex = *imageIndex; + + return TRUE; +} + +static CTreeViewNodeData* CreateTreeViewNodeData(CTreeViewNodeTypeEnum type, const char* fullPath, + const char* focusPath, const char* focusName) +{ + CTreeViewNodeData* itemData = (CTreeViewNodeData*)malloc(sizeof(CTreeViewNodeData)); + if (itemData == NULL) + return NULL; + memset(itemData, 0, sizeof(CTreeViewNodeData)); + + itemData->Type = type; + itemData->FullPath = DuplicateTreeViewString(fullPath); + itemData->FocusPath = DuplicateTreeViewString(focusPath != NULL ? focusPath : fullPath); + if (focusName != NULL) + itemData->FocusName = DuplicateTreeViewString(focusName); + + if (itemData->FullPath == NULL || itemData->FocusPath == NULL || (focusName != NULL && itemData->FocusName == NULL)) + { + FreeTreeViewNodeData(itemData); + return NULL; + } + + if (!GetTreeViewShellIconIndexes(fullPath, type == tvntDirectory, + &itemData->ImageIndex, &itemData->SelectedImageIndex)) + { + itemData->ImageIndex = I_IMAGECALLBACK; + itemData->SelectedImageIndex = I_IMAGECALLBACK; + } + + return itemData; +} + +static HTREEITEM InsertTreeViewItem(HWND hTreeView, HTREEITEM hParent, const char* text, + CTreeViewNodeTypeEnum type, const char* fullPath, + const char* focusPath, const char* focusName, BOOL hasChildren) +{ + CTreeViewNodeData* itemData = CreateTreeViewNodeData(type, fullPath, focusPath, focusName); + if (itemData == NULL) + return NULL; + + TVINSERTSTRUCT tvis; + memset(&tvis, 0, sizeof(tvis)); + tvis.hParent = hParent; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvis.item.pszText = (char*)text; + tvis.item.lParam = (LPARAM)itemData; + tvis.item.cChildren = hasChildren ? 1 : 0; + tvis.item.iImage = itemData->ImageIndex; + tvis.item.iSelectedImage = itemData->SelectedImageIndex; + + HTREEITEM hItem = TreeView_InsertItem(hTreeView, &tvis); + if (hItem == NULL) + FreeTreeViewNodeData(itemData); + return hItem; +} + +enum +{ + TREEVIEW_MIN_WIDTH = 120, + TREEVIEW_MIN_LIST_WIDTH = 50, + TREEVIEW_SPLITTER_WIDTH = 4 +}; + +static int __cdecl CompareTreeViewPopulateEntries(const void* p1, const void* p2) +{ + const CTreeViewPopulateEntry* e1 = (const CTreeViewPopulateEntry*)p1; + const CTreeViewPopulateEntry* e2 = (const CTreeViewPopulateEntry*)p2; + return lstrcmpi(e1->Name, e2->Name); +} + +static BOOL AddTreeViewPopulateEntry(CTreeViewPopulateEntry** entries, int* count, + const char* name, const char* fullPath, BOOL isDirectory) +{ + CTreeViewPopulateEntry* newEntries = (CTreeViewPopulateEntry*)realloc(*entries, + (*count + 1) * sizeof(CTreeViewPopulateEntry)); + if (newEntries == NULL) + return FALSE; + + *entries = newEntries; + lstrcpyn(newEntries[*count].Name, name, MAX_PATH); + lstrcpyn(newEntries[*count].FullPath, fullPath, MAX_PATH); + newEntries[*count].IsDirectory = isDirectory; + (*count)++; + return TRUE; +} + +static BOOL ShouldSkipTreeViewEntry(const WIN32_FIND_DATA* findData) +{ + if (strcmp(findData->cFileName, ".") == 0 || strcmp(findData->cFileName, "..") == 0) + return TRUE; + + int len = (int)strlen(findData->cFileName); + const char* st = findData->cFileName + len - 1; + if (Configuration.NotHiddenSystemFiles && + !IsFilePlaceholder(findData) && + (findData->dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) && + (len != 2 || *st != '.' || *(st + 1) != '.')) + return TRUE; + + return FALSE; +} + // // **************************************************************************** // CFilesWindowAncestor @@ -295,6 +529,255 @@ void CFilesWindowAncestor::SetPath(const char* path) { ((CFilesWindow*)this)->SetAutomaticRefresh(TRUE, TRUE); } + + if (MainWindow != NULL && MainWindow->LeftPanel != NULL) + MainWindow->LeftPanel->RefreshTreeView(); + else + ((CFilesWindow*)this)->RefreshTreeView(); +} + +void CFilesWindow::RefreshTreeView() +{ + CALL_STACK_MESSAGE1("CFilesWindow::RefreshTreeView()"); + + if (!IsTreeViewHost() || HTreeView == NULL) + return; + + if (!TreeViewActive) + { + EnableWindow(HTreeView, FALSE); + return; + } + + BOOL hadSelectedFile = FALSE; + char selectedFileFullPath[MAX_PATH]; + char selectedFileFocusPath[MAX_PATH]; + selectedFileFullPath[0] = 0; + selectedFileFocusPath[0] = 0; + HTREEITEM hSelected = TreeView_GetSelection(HTreeView); + if (hSelected != NULL) + { + CTreeViewNodeData selectedItemData; + if (GetTreeViewItemData(HTreeView, hSelected, &selectedItemData) && + selectedItemData.Type == tvntFile && + selectedItemData.FullPath != NULL && selectedItemData.FocusPath != NULL) + { + hadSelectedFile = TRUE; + lstrcpyn(selectedFileFullPath, selectedItemData.FullPath, MAX_PATH); + lstrcpyn(selectedFileFocusPath, selectedItemData.FocusPath, MAX_PATH); + } + } + + TreeViewDisableNotify = TRUE; + SendMessage(HTreeView, WM_SETREDRAW, FALSE, 0); + + do + { + CFilesWindow* sourcePanel = GetTreeViewSourcePanel(); + if (sourcePanel == NULL || !sourcePanel->Is(ptDisk)) + { + EnableWindow(HTreeView, FALSE); + TreeView_DeleteAllItems(HTreeView); + break; + } + + EnableWindow(HTreeView, TRUE); + UpdateTreeViewColors(); + + const char* sourcePath = sourcePanel->GetPath(); + char root[MAX_PATH]; + GetRootPath(root, sourcePath); + if (root[0] == 0) + { + TreeView_DeleteAllItems(HTreeView); + break; + } + + HTREEITEM hCurrent = TreeView_GetRoot(HTreeView); + if (hCurrent == NULL) + hCurrent = InsertTreeViewItem(HTreeView, TVI_ROOT, root, tvntDirectory, root, root, NULL, TRUE); + else + { + const char* rootPath = GetTreeViewItemPath(HTreeView, hCurrent); + if (rootPath == NULL || !IsTheSamePath(rootPath, root)) + { + TreeView_DeleteAllItems(HTreeView); + hCurrent = InsertTreeViewItem(HTreeView, TVI_ROOT, root, tvntDirectory, root, root, NULL, TRUE); + } + } + if (hCurrent == NULL) + break; + + PopulateTreeViewItem(hCurrent); + TreeView_Expand(HTreeView, hCurrent, TVE_EXPAND); + + if (!IsTheSamePath(root, sourcePath)) + { + char currentPath[MAX_PATH]; + lstrcpyn(currentPath, root, MAX_PATH); + + const char* segment = sourcePath + strlen(root); + while (*segment == '\\' || *segment == '/') + segment++; + + while (*segment != 0) + { + char nextPath[MAX_PATH]; + lstrcpyn(nextPath, currentPath, MAX_PATH); + + char name[MAX_PATH]; + int len = 0; + while (segment[len] != 0 && segment[len] != '\\' && segment[len] != '/') + len++; + memcpy(name, segment, len); + name[len] = 0; + + if (!SalPathAppend(nextPath, name, MAX_PATH)) + break; + + HTREEITEM hChild = FindTreeViewChildByPath(HTreeView, hCurrent, nextPath); + if (hChild == NULL) + { + PopulateTreeViewItem(hCurrent, TRUE); + hChild = FindTreeViewChildByPath(HTreeView, hCurrent, nextPath); + } + + if (hChild == NULL) + break; + + hCurrent = hChild; + lstrcpyn(currentPath, nextPath, MAX_PATH); + PopulateTreeViewItem(hCurrent); + TreeView_Expand(HTreeView, hCurrent, TVE_EXPAND); + + segment += len; + while (*segment == '\\' || *segment == '/') + segment++; + } + } + + PopulateTreeViewItem(hCurrent, TRUE); + + HTREEITEM hSelect = hCurrent; + if (hadSelectedFile && IsTheSamePath(selectedFileFocusPath, sourcePath)) + { + HTREEITEM hSelectedFile = FindTreeViewChildByPath(HTreeView, hCurrent, selectedFileFullPath); + if (hSelectedFile != NULL) + hSelect = hSelectedFile; + } + + TreeView_SelectItem(HTreeView, hSelect); + TreeView_EnsureVisible(HTreeView, hSelect); + } while (0); + + SendMessage(HTreeView, WM_SETREDRAW, TRUE, 0); + RedrawWindow(HTreeView, NULL, NULL, RDW_INVALIDATE | RDW_NOERASE | RDW_UPDATENOW); + TreeViewDisableNotify = FALSE; +} + +BOOL CFilesWindow::PopulateTreeViewItem(HTREEITEM hItem, BOOL forceRefresh) +{ + CALL_STACK_MESSAGE1("CFilesWindow::PopulateTreeViewItem()"); + + CFilesWindow* sourcePanel = GetTreeViewSourcePanel(); + if (HTreeView == NULL || hItem == NULL || sourcePanel == NULL || !sourcePanel->Is(ptDisk)) + return FALSE; + + CTreeViewNodeData* itemData = GetTreeViewItemDataPtr(HTreeView, hItem); + if (itemData == NULL) + return FALSE; + + const char* itemPath = GetTreeViewItemPath(HTreeView, hItem); + if (itemPath == NULL || itemPath[0] == 0) + return FALSE; + if (!IsTreeViewDirectoryItem(HTreeView, hItem)) + { + SetTreeViewItemChildren(HTreeView, hItem, 0); + return FALSE; + } + + if (!forceRefresh && itemData->Populated) + return TreeView_GetChild(HTreeView, hItem) != NULL; + + HTREEITEM hChild = TreeView_GetChild(HTreeView, hItem); + while (hChild != NULL) + { + HTREEITEM hNext = TreeView_GetNextSibling(HTreeView, hChild); + TreeView_DeleteItem(HTreeView, hChild); + hChild = hNext; + } + + char searchPath[MAX_PATH]; + lstrcpyn(searchPath, itemPath, MAX_PATH); + if (!SalPathAppend(searchPath, "*", MAX_PATH)) + { + SetTreeViewItemChildren(HTreeView, hItem, 0); + return FALSE; + } + + WIN32_FIND_DATA data; + HANDLE find = FindFirstFile(searchPath, &data); + if (find == INVALID_HANDLE_VALUE) + { + SetTreeViewItemChildren(HTreeView, hItem, 0); + return FALSE; + } + + CTreeViewPopulateEntry* dirEntries = NULL; + CTreeViewPopulateEntry* fileEntries = NULL; + int dirCount = 0; + int fileCount = 0; + BOOL hasChildren = FALSE; + do + { + if (ShouldSkipTreeViewEntry(&data)) + continue; + + BOOL isDirectory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + char childPath[MAX_PATH]; + lstrcpyn(childPath, itemPath, MAX_PATH); + if (!SalPathAppend(childPath, data.cFileName, MAX_PATH)) + continue; + + if (!AddTreeViewPopulateEntry(isDirectory ? &dirEntries : &fileEntries, + isDirectory ? &dirCount : &fileCount, + data.cFileName, childPath, isDirectory)) + { + FindClose(find); + free(dirEntries); + free(fileEntries); + SetTreeViewItemChildren(HTreeView, hItem, 0); + return FALSE; + } + } while (FindNextFile(find, &data)); + + FindClose(find); + + if (dirCount > 1) + qsort(dirEntries, dirCount, sizeof(CTreeViewPopulateEntry), CompareTreeViewPopulateEntries); + if (fileCount > 1) + qsort(fileEntries, fileCount, sizeof(CTreeViewPopulateEntry), CompareTreeViewPopulateEntries); + + int i; + for (i = 0; i < dirCount; i++) + { + if (InsertTreeViewItem(HTreeView, hItem, dirEntries[i].Name, tvntDirectory, + dirEntries[i].FullPath, dirEntries[i].FullPath, NULL, TRUE) != NULL) + hasChildren = TRUE; + } + for (i = 0; i < fileCount; i++) + { + if (InsertTreeViewItem(HTreeView, hItem, fileEntries[i].Name, tvntFile, + fileEntries[i].FullPath, itemPath, fileEntries[i].Name, FALSE) != NULL) + hasChildren = TRUE; + } + + free(dirEntries); + free(fileEntries); + + itemData->Populated = TRUE; + SetTreeViewItemChildren(HTreeView, hItem, dirCount > 0 ? 1 : 0); + return hasChildren; } CFilesArray* @@ -1363,9 +1846,15 @@ CFilesWindow::CFilesWindow(CMainWindow* parent) ListBox = NULL; StatusLine = NULL; DirectoryLine = NULL; + HTreeView = NULL; + HTreeSplit = NULL; StatusLineVisible = TRUE; DirectoryLineVisible = TRUE; HeaderLineVisible = TRUE; + TreeViewActive = FALSE; + TreeViewDisableNotify = FALSE; + TreeViewSplitDragging = FALSE; + TreeViewSplitOffset = 0; SortType = stName; ReverseSort = FALSE; diff --git a/src/fileswn2.cpp b/src/fileswn2.cpp index 823328a09..70afd0b1a 100644 --- a/src/fileswn2.cpp +++ b/src/fileswn2.cpp @@ -4,6 +4,8 @@ #include "precomp.h" +#include + #include "cfgdlg.h" #include "mainwnd.h" #include "usermenu.h" @@ -25,11 +27,203 @@ extern "C" #include "salshlib.h" #include "shellib.h" +enum +{ + TREEVIEW_MIN_WIDTH = 120, + TREEVIEW_MIN_LIST_WIDTH = 50, + TREEVIEW_SPLITTER_WIDTH = 4 +}; + +static const char* TREEVIEW_SPLIT_SUBCLASSPROC = "SAL_TREEVIEW_SPLIT_SUBCLASSPROC"; +static const char* TREEVIEW_SPLIT_OWNER = "SAL_TREEVIEW_SPLIT_OWNER"; + +static COLORREF GetPanelBrushColor(HBRUSH hBrush, COLORREF fallback) +{ + if (hBrush != NULL) + { + LOGBRUSH brushInfo; + if (GetObject(hBrush, sizeof(brushInfo), &brushInfo) == sizeof(brushInfo)) + return brushInfo.lbColor; + } + return fallback; +} + +static int ClampTreeViewWidth(int clientWidth, int requestedWidth) +{ + int maxWidth = clientWidth - TREEVIEW_MIN_LIST_WIDTH - TREEVIEW_SPLITTER_WIDTH; + if (maxWidth < 0) + maxWidth = 0; + + int minWidth = TREEVIEW_MIN_WIDTH; + if (minWidth > maxWidth) + minWidth = maxWidth; + + if (requestedWidth < minWidth) + requestedWidth = minWidth; + if (requestedWidth > maxWidth) + requestedWidth = maxWidth; + return requestedWidth; +} + +static LRESULT CALLBACK TreeViewSplitSubclassProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, TREEVIEW_SPLIT_SUBCLASSPROC); + CFilesWindow* panel = (CFilesWindow*)GetProp(hwnd, TREEVIEW_SPLIT_OWNER); + if (oldWndProc == NULL) + return DefWindowProc(hwnd, message, wParam, lParam); + + switch (message) + { + case WM_SETCURSOR: + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + return TRUE; + + case WM_LBUTTONDOWN: + { + if (panel != NULL && panel->TreeViewActive) + { + POINT pt; + GetCursorPos(&pt); + RECT r; + GetWindowRect(hwnd, &r); + panel->TreeViewSplitDragging = TRUE; + panel->TreeViewSplitOffset = pt.x - r.left; + SetCapture(hwnd); + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + return 0; + } + break; + } + + case WM_MOUSEMOVE: + { + if (panel != NULL && panel->TreeViewSplitDragging && GetCapture() == hwnd) + { + POINT pt; + GetCursorPos(&pt); + ScreenToClient(panel->HWindow, &pt); + panel->SetTreeViewWidth(pt.x - panel->TreeViewSplitOffset); + return 0; + } + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + return 0; + } + + case WM_LBUTTONUP: + if (panel != NULL) + panel->TreeViewSplitDragging = FALSE; + if (GetCapture() == hwnd) + ReleaseCapture(); + return 0; + + case WM_CAPTURECHANGED: + case WM_CANCELMODE: + if (panel != NULL) + panel->TreeViewSplitDragging = FALSE; + break; + + case WM_NCDESTROY: + { + WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + if (currentWndProc == TreeViewSplitSubclassProc) + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)oldWndProc); + RemoveProp(hwnd, TREEVIEW_SPLIT_SUBCLASSPROC); + RemoveProp(hwnd, TREEVIEW_SPLIT_OWNER); + break; + } + } + + return CallWindowProc(oldWndProc, hwnd, message, wParam, lParam); +} + // // **************************************************************************** // CFilesWindow // +BOOL CFilesWindow::IsTreeViewHost() +{ + return MainWindow != NULL && MainWindow->LeftPanel == this; +} + +CFilesWindow* CFilesWindow::GetTreeViewSourcePanel() +{ + if (MainWindow != NULL) + { + CFilesWindow* activePanel = MainWindow->GetActivePanel(); + if (activePanel != NULL) + return activePanel; + if (MainWindow->LeftPanel != NULL) + return MainWindow->LeftPanel; + } + return this; +} + +int CFilesWindow::GetTreeViewWidth(int clientWidth) +{ + return ClampTreeViewWidth(clientWidth, Configuration.TreeViewWidth); +} + +int CFilesWindow::GetTreeViewReservedWidth(int clientWidth) +{ + if (!IsTreeViewHost() || !TreeViewActive) + return 0; + return GetTreeViewWidth(clientWidth) + TREEVIEW_SPLITTER_WIDTH; +} + +COLORREF CFilesWindow::GetTreeViewTextColor() +{ + return GetCOLORREF(CurrentColors[ITEM_FG_NORMAL]); +} + +COLORREF CFilesWindow::GetTreeViewBkColor() +{ + return GetPanelBrushColor(HNormalBkBrush, GetCOLORREF(CurrentColors[ITEM_BK_NORMAL])); +} + +COLORREF CFilesWindow::GetTreeViewSelectionTextColor() +{ + BOOL useFocusedSelection = MainWindow != NULL && MainWindow->CaptionIsActive; + return GetCOLORREF(CurrentColors[useFocusedSelection ? ITEM_FG_FOCSEL : ITEM_FG_SELECTED]); +} + +COLORREF CFilesWindow::GetTreeViewSelectionBkColor() +{ + BOOL useFocusedSelection = MainWindow != NULL && MainWindow->CaptionIsActive; + return GetPanelBrushColor(useFocusedSelection ? HFocSelBkBrush : HSelectedBkBrush, + GetCOLORREF(CurrentColors[useFocusedSelection ? ITEM_BK_FOCSEL : ITEM_BK_SELECTED])); +} + +void CFilesWindow::UpdateTreeViewColors() +{ + if (HTreeView == NULL) + return; + + TreeView_SetTextColor(HTreeView, GetTreeViewTextColor()); + TreeView_SetBkColor(HTreeView, GetTreeViewBkColor()); + TreeView_SetLineColor(HTreeView, GetTreeViewTextColor()); + InvalidateRect(HTreeView, NULL, FALSE); +} + +void CFilesWindow::SetTreeViewWidth(int width) +{ + if (HWindow != NULL) + { + RECT r; + GetClientRect(HWindow, &r); + width = ClampTreeViewWidth(r.right - r.left, width); + } + Configuration.TreeViewWidth = width; + + if (HWindow != NULL) + { + RECT r; + GetClientRect(HWindow, &r); + SendMessage(HWindow, WM_SIZE, SIZE_RESTORED, + MAKELONG(r.right - r.left, r.bottom - r.top)); + } +} + void CFilesWindow::HandsOff(BOOL off) { CALL_STACK_MESSAGE2("CFilesWindow::HandsOff(%d)", off); @@ -1373,6 +1567,115 @@ BOOL CFilesWindow::PrepareCloseCurrentPath(HWND parent, BOOL canForce, BOOL canD } } +void CFilesWindow::CreateTreeView() +{ + CALL_STACK_MESSAGE1("CFilesWindow::CreateTreeView()"); + if (!IsTreeViewHost() || HWindow == NULL) + { + if (HWindow == NULL) + TRACE_E("HWindow == NULL"); + return; + } + if (HTreeView == NULL) + { + BOOL appIsThemed = IsAppThemed(); + HTreeView = CreateWindowEx(WS_EX_STATICEDGE, WC_TREEVIEW, "", + WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VSCROLL | + TVS_DISABLEDRAGDROP | TVS_HASBUTTONS | TVS_LINESATROOT | + TVS_SHOWSELALWAYS | (appIsThemed ? TVS_FULLROWSELECT : TVS_HASLINES), + 0, 0, 0, 0, + HWindow, (HMENU)IDC_TREEVIEW, HInstance, NULL); + if (HTreeView == NULL) + { + TRACE_E("Unable to create tree-view."); + return; + } + if (appIsThemed) + SetWindowTheme(HTreeView, (L" "), (L" ")); + + SHFILEINFO sfi; + memset(&sfi, 0, sizeof(sfi)); + HIMAGELIST hImageList = (HIMAGELIST)SHGetFileInfo("C:\\", FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi), + SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); + if (hImageList != NULL) + TreeView_SetImageList(HTreeView, hImageList, TVSIL_NORMAL); + UpdateTreeViewColors(); + } + + if (HTreeSplit == NULL) + { + HTreeSplit = CreateWindowEx(0, "STATIC", "", + WS_CHILD | WS_CLIPSIBLINGS | SS_NOTIFY, + 0, 0, 0, 0, + HWindow, (HMENU)IDC_TREESPLIT, HInstance, NULL); + if (HTreeSplit == NULL) + { + TRACE_E("Unable to create tree-view splitter."); + return; + } + + WNDPROC oldWndProc = (WNDPROC)GetWindowLongPtr(HTreeSplit, GWLP_WNDPROC); + if (!SetProp(HTreeSplit, TREEVIEW_SPLIT_SUBCLASSPROC, (HANDLE)oldWndProc) || + !SetProp(HTreeSplit, TREEVIEW_SPLIT_OWNER, (HANDLE)this)) + { + TRACE_E("Unable to subclass tree-view splitter."); + } + else + SetWindowLongPtr(HTreeSplit, GWLP_WNDPROC, (LONG_PTR)TreeViewSplitSubclassProc); + } +} + +void CFilesWindow::DestroyTreeView() +{ + CALL_STACK_MESSAGE1("CFilesWindow::DestroyTreeView()"); + if (HTreeSplit != NULL) + { + DestroyWindow(HTreeSplit); + HTreeSplit = NULL; + } + if (HTreeView != NULL) + { + TreeView_SetImageList(HTreeView, NULL, TVSIL_NORMAL); + DestroyWindow(HTreeView); + HTreeView = NULL; + } +} + +void CFilesWindow::UpdateTreeView(BOOL active) +{ + CALL_STACK_MESSAGE2("CFilesWindow::UpdateTreeView(%d)", active); + if (!IsTreeViewHost()) + { + TreeViewActive = FALSE; + DestroyTreeView(); + } + else + { + TreeViewActive = active && Configuration.TreeViewVisible; + if (Configuration.TreeViewVisible) + CreateTreeView(); + else + DestroyTreeView(); + } + + if (HTreeView != NULL) + { + ShowWindow(HTreeView, TreeViewActive ? SW_SHOW : SW_HIDE); + if (TreeViewActive) + RefreshTreeView(); + } + if (HTreeSplit != NULL) + ShowWindow(HTreeSplit, TreeViewActive ? SW_SHOW : SW_HIDE); + + if (HWindow != NULL) + { + RECT r; + GetClientRect(HWindow, &r); + SendMessage(HWindow, WM_SIZE, SIZE_RESTORED, + MAKELONG(r.right - r.left, r.bottom - r.top)); + } +} + void CFilesWindow::CloseCurrentPath(HWND parent, BOOL cancel, BOOL detachFS, BOOL newPathIsTheSame, BOOL isRefresh, BOOL canChangeSourceUID) { diff --git a/src/fileswnb.cpp b/src/fileswnb.cpp index bb9328661..d3babe1a3 100644 --- a/src/fileswnb.cpp +++ b/src/fileswnb.cpp @@ -54,6 +54,40 @@ BOOL IsCustomEventGUID(LPARAM lParam, REFGUID guidEvent) return ret; } +static BOOL GetTreeViewNotifyItemData(HWND hTreeView, HTREEITEM hItem, CTreeViewNodeData* itemData) +{ + if (hTreeView == NULL || hItem == NULL) + return FALSE; + + TVITEM item; + memset(&item, 0, sizeof(item)); + item.mask = TVIF_PARAM; + item.hItem = hItem; + if (!TreeView_GetItem(hTreeView, &item)) + return FALSE; + if (item.lParam == 0) + return FALSE; + + *itemData = *(CTreeViewNodeData*)item.lParam; + return TRUE; +} + +static void FreeTreeViewNodeData(CTreeViewNodeData* itemData) +{ + if (itemData == NULL) + return; + + free(itemData->FullPath); + free(itemData->FocusPath); + free(itemData->FocusName); + free(itemData); +} + +enum +{ + TREEVIEW_SPLITTER_WIDTH = 4 +}; + //**************************************************************************** // // WindowProc @@ -78,6 +112,13 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) int dlHeight = 3; int stHeight = 0; + int treeWidth = 0; + int treeHeight = height; + int listX = 0; + int listWidth = width; + int listHeight = height; + int statusLineX = 0; + int statusLineWidth = width; int windowsCount = 1; if (DirectoryLine->HWindow != NULL) { @@ -91,9 +132,21 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) if (StatusLine->HWindow != NULL) { stHeight = StatusLine->GetNeededHeight(); + listHeight -= stHeight; InvalidateRect(StatusLine->HWindow, NULL, FALSE); windowsCount++; } + treeHeight -= dlHeight; + listHeight -= dlHeight; + if (HTreeView != NULL && TreeViewActive) + { + treeWidth = GetTreeViewWidth(width); + listX = treeWidth + TREEVIEW_SPLITTER_WIDTH; + listWidth = width - listX; + statusLineX = listX; + statusLineWidth = listWidth; + windowsCount += HTreeSplit != NULL ? 2 : 1; + } HDWP hdwp = HANDLES(BeginDeferWindowPos(windowsCount)); if (hdwp != NULL) @@ -103,13 +156,23 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) 0, 0, width, dlHeight, SWP_NOACTIVATE | SWP_NOZORDER)); + if (HTreeView != NULL && TreeViewActive) + hdwp = HANDLES(DeferWindowPos(hdwp, HTreeView, NULL, + 0, dlHeight, treeWidth, treeHeight, + SWP_NOACTIVATE | SWP_NOZORDER)); + + if (HTreeSplit != NULL && TreeViewActive) + hdwp = HANDLES(DeferWindowPos(hdwp, HTreeSplit, NULL, + treeWidth, dlHeight, TREEVIEW_SPLITTER_WIDTH, treeHeight, + SWP_NOACTIVATE | SWP_NOZORDER)); + hdwp = HANDLES(DeferWindowPos(hdwp, ListBox->HWindow, NULL, - 0, dlHeight, width, height - stHeight - dlHeight, + listX, dlHeight, listWidth, listHeight, SWP_NOACTIVATE | SWP_NOZORDER)); if (StatusLine->HWindow != NULL) hdwp = HANDLES(DeferWindowPos(hdwp, StatusLine->HWindow, NULL, - 0, height - stHeight, width, stHeight, + statusLineX, height - stHeight, statusLineWidth, stHeight, SWP_NOACTIVATE | SWP_NOZORDER)); HANDLES(EndDeferWindowPos(hdwp)); @@ -134,6 +197,98 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) return TRUE; } + case WM_NOTIFY: + { + LPNMHDR lphdr = (LPNMHDR)lParam; + if (lphdr != NULL && lphdr->hwndFrom == HTreeView) + { + if (lphdr->code == TVN_DELETEITEM) + { + LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam; + if (pnmtv->itemOld.lParam != 0) + FreeTreeViewNodeData((CTreeViewNodeData*)pnmtv->itemOld.lParam); + return 0; + } + + if (TreeViewDisableNotify) + return 0; + + switch (lphdr->code) + { + case NM_CUSTOMDRAW: + { + LPNMTVCUSTOMDRAW pnmcd = (LPNMTVCUSTOMDRAW)lParam; + if (pnmcd->nmcd.dwDrawStage == CDDS_PREPAINT) + return CDRF_NOTIFYITEMDRAW; + + if (pnmcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) + { + pnmcd->clrText = GetTreeViewTextColor(); + pnmcd->clrTextBk = GetTreeViewBkColor(); + if ((pnmcd->nmcd.uItemState & CDIS_SELECTED) != 0) + { + HBRUSH hBrush = HANDLES(CreateSolidBrush(GetTreeViewSelectionBkColor())); + if (hBrush != NULL) + { + FillRect(pnmcd->nmcd.hdc, &pnmcd->nmcd.rc, hBrush); + HANDLES(DeleteObject(hBrush)); + } + pnmcd->clrText = GetTreeViewSelectionTextColor(); + pnmcd->clrTextBk = GetTreeViewSelectionBkColor(); + pnmcd->nmcd.uItemState &= ~(CDIS_SELECTED | CDIS_FOCUS); + } + return CDRF_NEWFONT; + } + return CDRF_DODEFAULT; + } + + case TVN_ITEMEXPANDING: + { + LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam; + if (pnmtv->action == TVE_EXPAND) + PopulateTreeViewItem(pnmtv->itemNew.hItem); + return 0; + } + + case TVN_SELCHANGED: + { + LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam; + CTreeViewNodeData itemData; + CFilesWindow* sourcePanel = GetTreeViewSourcePanel(); + if (!TreeViewActive || sourcePanel == NULL || !sourcePanel->Is(ptDisk) || + !GetTreeViewNotifyItemData(HTreeView, pnmtv->itemNew.hItem, &itemData)) + return 0; + + if (itemData.Type == tvntDirectory) + { + if (itemData.FullPath != NULL && itemData.FullPath[0] != 0 && + !IsTheSamePath(itemData.FullPath, sourcePanel->GetPath())) + { + char treePath[MAX_PATH]; + lstrcpyn(treePath, itemData.FullPath, MAX_PATH); + sourcePanel->ChangePathToDisk(sourcePanel->HWindow, treePath); + } + } + else + { + if (itemData.FocusPath != NULL && itemData.FocusPath[0] != 0 && + itemData.FocusName != NULL && itemData.FocusName[0] != 0) + { + char focusPath[MAX_PATH + 200]; + char focusName[MAX_PATH + 200]; + lstrcpyn(focusPath, itemData.FocusPath, MAX_PATH + 200); + lstrcpyn(focusName, itemData.FocusName, MAX_PATH + 200); + MainWindow->PostFocusNameInPanel(sourcePanel == MainWindow->LeftPanel ? PANEL_LEFT : PANEL_RIGHT, + focusPath, focusName); + } + } + return 0; + } + } + } + break; + } + case WM_DEVICECHANGE: { switch (wParam) @@ -1044,6 +1199,7 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) } SelectViewTemplate(index, FALSE, FALSE); ShowWindow(ListBox->HWindow, SW_SHOW); + UpdateTreeView(MainWindow->LeftPanel == this); // srovname nastaveni promenne AutomaticRefresh a directory-liny SetAutomaticRefresh(AutomaticRefresh, TRUE); @@ -1062,6 +1218,7 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) DetachDirectory(this); //--- uvolneni child-oken RevokeDragDrop(); + DestroyTreeView(); ListBox->DetachWindow(); delete ListBox; ListBox = NULL; // pro jistotu, at se chyby ukazou... @@ -1281,6 +1438,18 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_SETFOCUS: { + if (HTreeView != NULL && TreeViewActive) + { + POINT pt; + RECT r; + GetCursorPos(&pt); + GetWindowRect(HTreeView, &r); + if ((GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0 && PtInRect(&r, pt)) + { + SetFocus(HTreeView); + break; + } + } SetFocus(ListBox->HWindow); break; } diff --git a/src/fileswnd.h b/src/fileswnd.h index 79e605b94..2508cd298 100644 --- a/src/fileswnd.h +++ b/src/fileswnd.h @@ -702,6 +702,23 @@ typedef PVOID HDEVNOTIFY; enum CViewModeEnum; +enum CTreeViewNodeTypeEnum +{ + tvntDirectory, + tvntFile +}; + +struct CTreeViewNodeData +{ + CTreeViewNodeTypeEnum Type; + char* FullPath; + char* FocusPath; + char* FocusName; + int ImageIndex; + int SelectedImageIndex; + BOOL Populated; +}; + class CFilesWindow : public CFilesWindowAncestor { public: @@ -752,10 +769,16 @@ class CFilesWindow : public CFilesWindowAncestor CFilesBox* ListBox; CStatusWindow *StatusLine, *DirectoryLine; + HWND HTreeView; + HWND HTreeSplit; BOOL StatusLineVisible; BOOL DirectoryLineVisible; BOOL HeaderLineVisible; + BOOL TreeViewActive; + BOOL TreeViewDisableNotify; + BOOL TreeViewSplitDragging; + int TreeViewSplitOffset; CMainWindow* Parent; @@ -1243,6 +1266,21 @@ class CFilesWindow : public CFilesWindowAncestor void ToggleStatusLine(); void ToggleDirectoryLine(); void ToggleHeaderLine(); + BOOL IsTreeViewHost(); + CFilesWindow* GetTreeViewSourcePanel(); + int GetTreeViewWidth(int clientWidth); + int GetTreeViewReservedWidth(int clientWidth); + void SetTreeViewWidth(int width); + COLORREF GetTreeViewTextColor(); + COLORREF GetTreeViewBkColor(); + COLORREF GetTreeViewSelectionTextColor(); + COLORREF GetTreeViewSelectionBkColor(); + void UpdateTreeViewColors(); + void CreateTreeView(); + void DestroyTreeView(); + void UpdateTreeView(BOOL active); + void RefreshTreeView(); + BOOL PopulateTreeViewItem(HTREEITEM hItem, BOOL forceRefresh = FALSE); void ConnectNet(BOOL readOnlyUNC, const char* netRootPath = NULL, BOOL changeToNewDrive = TRUE, char* newlyMappedDrive = NULL); void DisconnectNet(); diff --git a/src/lang/lang.rc b/src/lang/lang.rc index 90ff495bc..7907f34d0 100644 --- a/src/lang/lang.rc +++ b/src/lang/lang.rc @@ -1870,7 +1870,7 @@ BEGIN LTEXT "items.",IDC_STATIC_8,175,181,22,8 END -IDD_CFGPAGE_PANELS DIALOGEX 64, 22, 299, 231 +IDD_CFGPAGE_PANELS DIALOGEX 64, 22, 299, 243 STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CAPTION CAPTION "Panels" FONT 8, "MS Shell Dlg", 400, 0, 0x1 @@ -1889,28 +1889,30 @@ BEGIN "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,78,256,12 CONTROL "&Use Shift+number for go to Hot Path (it conflicts with national keyboard layouts)",IDC_SHIFTFORHOTPATHS, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,90,282,12 - LTEXT "Mouse",IDC_STATIC_3,1,105,24,8 - CONTROL "",IDC_STATIC_8,"Static",SS_ETCHEDHORZ | WS_GROUP,27,109,270,1 + CONTROL "Show active panel &Tree View",IDC_PANELTREEVIEW, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,102,156,12 + LTEXT "Mouse",IDC_STATIC_3,1,117,24,8 + CONTROL "",IDC_STATIC_8,"Static",SS_ETCHEDHORZ | WS_GROUP,27,121,270,1 CONTROL "&Right-click to open shortcut menu (hold CTRL to select/unselect)",IDE_PRIMARYCTXMENU, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,116,228,12 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,128,228,12 CONTROL "R&ename by slowly clicking the name twice",IDC_CLICKTORENAME, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,128,153,12 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,140,153,12 CONTROL "Cancel &drag-and-drop operations shorter than:",IDC_DISABLEDANDD, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,140,170,12 - EDITTEXT IDE_DRAGDROPDELAY,181,140,28,12,ES_AUTOHSCROLL - LTEXT "ms",IDC_STATIC_4,213,142,13,8,NOT WS_GROUP - LTEXT "Sorting",IDC_STATIC_6,1,156,26,8 - CONTROL "",IDC_STATIC_9,"Static",SS_ETCHEDHORZ | WS_GROUP,29,160,268,1 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,152,170,12 + EDITTEXT IDE_DRAGDROPDELAY,181,152,28,12,ES_AUTOHSCROLL + LTEXT "ms",IDC_STATIC_4,213,154,13,8,NOT WS_GROUP + LTEXT "Sorting",IDC_STATIC_6,1,168,26,8 + CONTROL "",IDC_STATIC_9,"Static",SS_ETCHEDHORZ | WS_GROUP,29,172,268,1 CONTROL "S&ort strings with respect to Regional Settings",IDC_SORTUSESLOCALE, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,166,163,12 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,178,163,12 CONTROL "Use nu&merical sort for numbers contained in strings",IDC_SORTDETECTNUMBERS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,178,186,12 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,190,186,12 CONTROL "Sor&t by Time order: newer items on top",IDC_SORTNEWERONTOP, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,190,146,12 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,202,146,12 CONTROL "Sort by T&ime: sort directories by name",IDC_SORTDIRSBYNAME, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,202,141,12 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,214,141,12 CONTROL "Treat dire&ctories as if have extensions (also show extensions in Ext column)",IDC_SORTDIRSBYEXT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,214,265,12 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,226,265,12 END IDD_CANNOTSETATTRSINFO DIALOGEX 15, 33, 353, 89 diff --git a/src/lang/lang.rh b/src/lang/lang.rh index b3deae919..596aaca06 100644 --- a/src/lang/lang.rh +++ b/src/lang/lang.rh @@ -307,6 +307,7 @@ #define IDC_PANELCAPTION 614 #define IDC_SHIFTFORHOTPATHS 615 #define IDC_PANELZOOM 616 +#define IDC_PANELTREEVIEW 617 #define IDC_CHD_SHOWNET 619 #define IDC_THUMBNAILSIZE 620 #define IDC_THUMBNAILSIZE_UPDOWN 621 diff --git a/src/lang/texts.rc2 b/src/lang/texts.rc2 index 069bb047e..3be7108fb 100644 --- a/src/lang/texts.rc2 +++ b/src/lang/texts.rc2 @@ -142,6 +142,7 @@ STRINGTABLE IDS_MENU_OPT_VSB_DB, "&Drive Bar" IDS_MENU_OPT_VSB_DB2, "T&wo Drive Bars" IDS_MENU_OPT_VSB_EDIT, "C&ommand Line" + IDS_MENU_OPT_VSB_TREE, "&Tree View" IDS_MENU_OPT_VSB_BOTTOM, "&Bottom Toolbar" IDS_MENU_OPT_VSB_UMLABELS, "Te&xt Labels in User Menu Bar" IDS_MENU_OPT_VSB_GRIPS, "&Lock the Toolbars" diff --git a/src/mainwnd.h b/src/mainwnd.h index 355e19c57..98603d822 100644 --- a/src/mainwnd.h +++ b/src/mainwnd.h @@ -475,6 +475,7 @@ class CMainWindow : public CMainWindowAncestor BeforeZoomSplitPosition, // split position before panel zoom DragSplitPosition; // shown in the tooltip CToolTipWindow ToolTipWindow; + BOOL KeepSplitPositionCenteredOnVisiblePanes; BOOL FirstActivateApp; // WM_ACTIVATEAPP uses this variable during startup @@ -492,6 +493,11 @@ class CMainWindow : public CMainWindowAncestor void ClearHistory(); // clears all histories void GetSplitRect(RECT& r); + void GetPanelWidthsFromSplitPosition(double splitPosition, int& leftWidth, int& rightWidth); + double GetVisibleLeftPanelRatio(); + double GetSplitPositionForVisibleLeftPanelRatio(double leftVisibleRatio); + double GetVisiblePanesCenteredSplitPosition(); + void UpdateCenteredSplitPosition(); BOOL IsGood(); @@ -582,6 +588,7 @@ class CMainWindow : public CMainWindowAncestor BOOL TogglePluginsBar(BOOL storePos = TRUE); BOOL ToggleMiddleToolBar(); BOOL ToggleBottomToolBar(); + BOOL ToggleTreeView(); BOOL ToggleUserMenuToolBar(BOOL storePos = TRUE); BOOL ToggleHotPathsBar(BOOL storePos = TRUE); // If 'twoDriveBars' is TRUE, the user wants two drive lists; otherwise only one diff --git a/src/mainwnd1.cpp b/src/mainwnd1.cpp index f71f50329..d9cab4ca8 100644 --- a/src/mainwnd1.cpp +++ b/src/mainwnd1.cpp @@ -321,6 +321,7 @@ CMainWindow::CMainWindow() : ChangeNotifArray(3, 5) WindowWidth = WindowHeight = EditHeight = 0; SplitPosition = 0.5; // split is in the middle BeforeZoomSplitPosition = 0.5; + KeepSplitPositionCenteredOnVisiblePanes = FALSE; DragMode = FALSE; ContextMenuNew = new CMenuNew; ContextMenuChngDrv = NULL; @@ -766,6 +767,25 @@ BOOL CMainWindow::ToggleBottomToolBar() } } +BOOL CMainWindow::ToggleTreeView() +{ + CALL_STACK_MESSAGE1("CMainWindow::ToggleTreeView()"); + + double visibleLeftRatio = GetVisibleLeftPanelRatio(); + + Configuration.TreeViewVisible = !Configuration.TreeViewVisible; + + LeftPanel->UpdateTreeView(TRUE); + RightPanel->UpdateTreeView(FALSE); + + if (KeepSplitPositionCenteredOnVisiblePanes) + UpdateCenteredSplitPosition(); + else + SplitPosition = GetSplitPositionForVisibleLeftPanelRatio(visibleLeftRatio); + + return TRUE; +} + void CMainWindow::ToggleToolBarGrips() { CALL_STACK_MESSAGE1("CMainWindow::ToggleToolBarGrips()"); @@ -2438,6 +2458,7 @@ MENU_TEMPLATE_ITEM InfoLineMenu[] = // evaluate the result if (hit == mwhteSplitLine) { + KeepSplitPositionCenteredOnVisiblePanes = FALSE; SplitPosition = (double)cmd / 10; LayoutWindows(); return; diff --git a/src/mainwnd2.cpp b/src/mainwnd2.cpp index 76fe3d8ee..ff610973d 100644 --- a/src/mainwnd2.cpp +++ b/src/mainwnd2.cpp @@ -437,6 +437,7 @@ const char* CONFIG_USERMENUTOOLBARVISIBLE_REG = "Show User Menu ToolBar"; const char* CONFIG_HOTPATHSBARVISIBLE_REG = "Hot Paths Bar"; const char* CONFIG_DRIVEBARVISIBLE_REG = "Show Drive Bar"; const char* CONFIG_DRIVEBAR2VISIBLE_REG = "Show Drive Bar2"; +const char* CONFIG_TREEVIEWVISIBLE_REG = "Show Tree View"; const char* CONFIG_BOTTOMTOOLBARVISIBLE_REG = "Show Bottom ToolBar"; const char* CONFIG_EXPLORERLOOK_REG = "Explorer Look"; const char* CONFIG_FULLROWSELECT_REG = "Full Row Select"; @@ -535,6 +536,7 @@ const char* CONFIG_HOTPATHSWIDTH_REG = "Hot Paths Width"; const char* CONFIG_DRIVEBARINDEX_REG = "Drive Bar Index"; const char* CONFIG_DRIVEBARBREAK_REG = "Drive Bar Break"; const char* CONFIG_DRIVEBARWIDTH_REG = "Drive Bar Width"; +const char* CONFIG_TREEVIEWWIDTH_REG = "Tree View Width"; const char* CONFIG_GRIPSVISIBLE_REG = "Grips Visible"; const char* SALAMANDER_CONFIRMATION_REG = "Confirmation"; @@ -1710,6 +1712,8 @@ void CMainWindow::SaveConfig(HWND parent) &Configuration.DriveBarBreak, sizeof(DWORD)); SetValue(actKey, CONFIG_DRIVEBARWIDTH_REG, REG_DWORD, &Configuration.DriveBarWidth, sizeof(DWORD)); + SetValue(actKey, CONFIG_TREEVIEWWIDTH_REG, REG_DWORD, + &Configuration.TreeViewWidth, sizeof(DWORD)); SetValue(actKey, CONFIG_GRIPSVISIBLE_REG, REG_DWORD, &Configuration.GripsVisible, sizeof(DWORD)); @@ -2013,6 +2017,8 @@ void CMainWindow::SaveConfig(HWND parent) &Configuration.DriveBarVisible, sizeof(DWORD)); SetValue(actKey, CONFIG_DRIVEBAR2VISIBLE_REG, REG_DWORD, &Configuration.DriveBar2Visible, sizeof(DWORD)); + SetValue(actKey, CONFIG_TREEVIEWVISIBLE_REG, REG_DWORD, + &Configuration.TreeViewVisible, sizeof(DWORD)); SetValue(actKey, CONFIG_BOTTOMTOOLBARVISIBLE_REG, REG_DWORD, &Configuration.BottomToolBarVisible, sizeof(DWORD)); @@ -3201,6 +3207,8 @@ BOOL CMainWindow::LoadConfig(BOOL importingOldConfig, const CCommandLineParams* &Configuration.DriveBarBreak, sizeof(DWORD)); GetValue(actKey, CONFIG_DRIVEBARWIDTH_REG, REG_DWORD, &Configuration.DriveBarWidth, sizeof(DWORD)); + GetValue(actKey, CONFIG_TREEVIEWWIDTH_REG, REG_DWORD, + &Configuration.TreeViewWidth, sizeof(DWORD)); GetValue(actKey, CONFIG_GRIPSVISIBLE_REG, REG_DWORD, &Configuration.GripsVisible, sizeof(DWORD)); //--- top rebar end @@ -3604,6 +3612,8 @@ BOOL CMainWindow::LoadConfig(BOOL importingOldConfig, const CCommandLineParams* &Configuration.DriveBarVisible, sizeof(DWORD)); GetValue(actKey, CONFIG_DRIVEBAR2VISIBLE_REG, REG_DWORD, &Configuration.DriveBar2Visible, sizeof(DWORD)); + GetValue(actKey, CONFIG_TREEVIEWVISIBLE_REG, REG_DWORD, + &Configuration.TreeViewVisible, sizeof(DWORD)); if (ret) // if we return FALSE, everything will be inserted later { diff --git a/src/mainwnd3.cpp b/src/mainwnd3.cpp index 333896cc9..860fa99a2 100644 --- a/src/mainwnd3.cpp +++ b/src/mainwnd3.cpp @@ -4054,6 +4054,16 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = break; } + case CM_TOGGLETREEVIEW: + { + ToggleTreeView(); + IdleRefreshStates = TRUE; // on the next Idle, force a check of status variables + if (KeepSplitPositionCenteredOnVisiblePanes) + UpdateCenteredSplitPosition(); + LayoutWindows(); + break; + } + case CM_TOGGLE_UMLABELS: { UMToolBar->ToggleLabels(); @@ -4449,6 +4459,7 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = if (IsPanelZoomed(TRUE) || IsPanelZoomed(FALSE)) { SplitPosition = BeforeZoomSplitPosition; + KeepSplitPositionCenteredOnVisiblePanes = FALSE; // better protect ourselves against a bad value in BeforeZoomSplitPosition if (IsPanelZoomed(TRUE) || IsPanelZoomed(FALSE)) SplitPosition = 0.5; @@ -4456,6 +4467,7 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = else { BeforeZoomSplitPosition = SplitPosition; + KeepSplitPositionCenteredOnVisiblePanes = FALSE; if (LOWORD(wParam) == CM_ACTIVEZOOMPANEL) { if (activePanel == LeftPanel) @@ -4941,6 +4953,7 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = popup->CheckItem(CM_TOGGLEDRIVEBAR2, FALSE, DriveBar2->HWindow != NULL); popup->CheckItem(CM_TOGGLEEDITLINE, FALSE, EditPermanentVisible); popup->CheckItem(CM_TOGGLEBOTTOMTOOLBAR, FALSE, BottomToolBar->HWindow != NULL); + popup->CheckItem(CM_TOGGLETREEVIEW, FALSE, Configuration.TreeViewVisible); popup->CheckItem(CM_TOGGLE_UMLABELS, FALSE, Configuration.UserMenuToolbarLabels); popup->CheckItem(CM_TOGGLE_GRIPS, FALSE, !Configuration.GripsVisible); break; @@ -5096,12 +5109,17 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = } if (uMsg == WM_LBUTTONDBLCLK) { - if (SplitPosition != 0.5) + double targetSplitPosition = GetVisiblePanesCenteredSplitPosition(); + + if (fabs(SplitPosition - targetSplitPosition) > 0.0001) { - SplitPosition = 0.5; + KeepSplitPositionCenteredOnVisiblePanes = TRUE; + SplitPosition = targetSplitPosition; LayoutWindows(); FocusPanel(GetActivePanel()); } + else + KeepSplitPositionCenteredOnVisiblePanes = TRUE; return 0; } } @@ -5168,6 +5186,7 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = if (DragSplitX != leftWidth) { DragSplitX = leftWidth; + KeepSplitPositionCenteredOnVisiblePanes = FALSE; SplitPosition = DragSplitPosition; LayoutWindows(); } @@ -5206,6 +5225,7 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = // accept the position only when the drag finishes legally // int splitWidth = MainWindow->GetSplitBarWidth(); // SplitPosition = (double)DragSplitX / (WindowWidth - splitWidth); + KeepSplitPositionCenteredOnVisiblePanes = FALSE; SplitPosition = DragSplitPosition; LayoutWindows(); } @@ -5368,6 +5388,9 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = WindowWidth = LOWORD(lParam); WindowHeight = HIWORD(lParam); + if (KeepSplitPositionCenteredOnVisiblePanes) + UpdateCenteredSplitPosition(); + if (SplitPosition < 0) SplitPosition = 0; if (SplitPosition > 1) @@ -5495,6 +5518,8 @@ MENU_TEMPLATE_ITEM AddToSystemMenu[] = CFilesWindow* panel = GetActivePanel(); if (panel != NULL && panel->DirectoryLine != NULL) panel->DirectoryLine->InvalidateAndUpdate(!CaptionIsActive); + if (LeftPanel != NULL) + LeftPanel->UpdateTreeViewColors(); if (!CaptionIsActive) { diff --git a/src/mainwnd4.cpp b/src/mainwnd4.cpp index d545c40ce..d285d9805 100644 --- a/src/mainwnd4.cpp +++ b/src/mainwnd4.cpp @@ -114,6 +114,98 @@ void CMainWindow::GetWindowSplitRect(RECT& r) r.bottom = WindowHeight - EditHeight - BottomToolBarHeight; } +void CMainWindow::GetPanelWidthsFromSplitPosition(double splitPosition, int& leftWidth, int& rightWidth) +{ + int splitWidth = GetSplitBarWidth(); + leftWidth = (int)((WindowWidth - splitWidth) * splitPosition) - 1; + if (leftWidth < MIN_WIN_WIDTH) + leftWidth = MIN_WIN_WIDTH; + + rightWidth = WindowWidth - 2 - leftWidth - splitWidth; + if (rightWidth < MIN_WIN_WIDTH) + { + rightWidth = MIN_WIN_WIDTH; + leftWidth = WindowWidth - 2 - rightWidth - splitWidth; + } +} + +double CMainWindow::GetVisibleLeftPanelRatio() +{ + int leftWidth; + int rightWidth; + GetPanelWidthsFromSplitPosition(SplitPosition, leftWidth, rightWidth); + + int leftVisibleWidth = leftWidth; + if (LeftPanel != NULL) + { + int reservedWidth = LeftPanel->GetTreeViewReservedWidth(leftWidth); + if (reservedWidth > leftVisibleWidth) + reservedWidth = leftVisibleWidth; + leftVisibleWidth -= reservedWidth; + } + + int totalVisibleWidth = leftVisibleWidth + rightWidth; + if (totalVisibleWidth <= 0) + return 0.5; + + double leftVisibleRatio = (double)leftVisibleWidth / totalVisibleWidth; + if (leftVisibleRatio < 0) + leftVisibleRatio = 0; + if (leftVisibleRatio > 1) + leftVisibleRatio = 1; + return leftVisibleRatio; +} + +double CMainWindow::GetSplitPositionForVisibleLeftPanelRatio(double leftVisibleRatio) +{ + if (leftVisibleRatio < 0) + leftVisibleRatio = 0; + if (leftVisibleRatio > 1) + leftVisibleRatio = 1; + + int splitWidth = GetSplitBarWidth(); + int totalPanelsWidth = WindowWidth - 2 - splitWidth; + if (totalPanelsWidth <= 0) + return 0.5; + + int targetLeftWidth = (int)(totalPanelsWidth * leftVisibleRatio + 0.5); + if (LeftPanel != NULL) + { + int probeLeftWidth = targetLeftWidth; + for (int i = 0; i < 4; i++) + { + int reservedWidth = LeftPanel->GetTreeViewReservedWidth(probeLeftWidth); + int totalVisibleWidth = totalPanelsWidth - reservedWidth; + if (totalVisibleWidth < 0) + totalVisibleWidth = 0; + + targetLeftWidth = (int)(leftVisibleRatio * totalVisibleWidth + 0.5) + reservedWidth; + if (targetLeftWidth < 0) + targetLeftWidth = 0; + if (targetLeftWidth > totalPanelsWidth) + targetLeftWidth = totalPanelsWidth; + probeLeftWidth = targetLeftWidth; + } + } + + double splitPosition = (double)(targetLeftWidth + 1) / (WindowWidth - splitWidth); + if (splitPosition < 0) + splitPosition = 0; + if (splitPosition > 1) + splitPosition = 1; + return splitPosition; +} + +double CMainWindow::GetVisiblePanesCenteredSplitPosition() +{ + return GetSplitPositionForVisibleLeftPanelRatio(0.5); +} + +void CMainWindow::UpdateCenteredSplitPosition() +{ + SplitPosition = GetVisiblePanesCenteredSplitPosition(); +} + BOOL CMainWindow::PtInChild(HWND hChild, POINT p) { if (hChild == NULL) @@ -1180,6 +1272,7 @@ void CMainWindow::ChangePanel(BOOL force) SplitPosition = 0.0; else SplitPosition = 1.0; + KeepSplitPositionCenteredOnVisiblePanes = FALSE; LayoutWindows(); change = TRUE; } @@ -1188,6 +1281,8 @@ void CMainWindow::ChangePanel(BOOL force) if (change) { SetActivePanel(p2); + LeftPanel->UpdateTreeView(TRUE); + RightPanel->UpdateTreeView(FALSE); // ensure the active panel header is redrawn if (p1->DirectoryLine != NULL) @@ -1241,6 +1336,8 @@ void CMainWindow::FocusPanel(CFilesWindow* focus, BOOL testIfMainWndActive) CFilesWindow* old = GetActivePanel(); SetActivePanel(focus); + LeftPanel->UpdateTreeView(TRUE); + RightPanel->UpdateTreeView(FALSE); UpdateDriveBars(); // press the correct drive in the drive bar diff --git a/src/menu4.cpp b/src/menu4.cpp index df8fe7965..eb6b76b43 100644 --- a/src/menu4.cpp +++ b/src/menu4.cpp @@ -195,6 +195,7 @@ MENU_TEMPLATE_ITEM MainMenuTemplate[] = {MNTT_IT, IDS_MENU_OPT_VSB_DB2, MNTS_B | MNTS_I | MNTS_A, CM_TOGGLEDRIVEBAR2, -1, 0, NULL}, {MNTT_IT, IDS_MENU_OPT_VSB_MID, MNTS_B | MNTS_I | MNTS_A, CM_TOGGLEMIDDLETOOLBAR, -1, 0, NULL}, {MNTT_IT, IDS_MENU_OPT_VSB_EDIT, MNTS_B | MNTS_I | MNTS_A, CM_TOGGLEEDITLINE, -1, 0, NULL}, + {MNTT_IT, IDS_MENU_OPT_VSB_TREE, MNTS_B | MNTS_I | MNTS_A, CM_TOGGLETREEVIEW, -1, 0, NULL}, {MNTT_IT, IDS_MENU_OPT_VSB_BOTTOM, MNTS_B | MNTS_I | MNTS_A, CM_TOGGLEBOTTOMTOOLBAR, -1, 0, NULL}, {MNTT_SP, -1, MNTS_B | MNTS_I | MNTS_A, 0, -1, 0, NULL}, {MNTT_IT, IDS_MENU_OPT_VSB_GRIPS, MNTS_B | MNTS_I | MNTS_A, CM_TOGGLE_GRIPS, -1, 0, NULL}, diff --git a/src/resource.rh2 b/src/resource.rh2 index db2fd652b..9087d9a2a 100644 --- a/src/resource.rh2 +++ b/src/resource.rh2 @@ -272,6 +272,7 @@ #define CM_STORESEL 866 #define CM_RESTORESEL 867 +#define CM_TOGGLETREEVIEW 868 #define CM_NEWDROP 870 @@ -323,6 +324,8 @@ #define IDC_EDITWINDOW 955 #define IDC_EDITLINE 956 #define IDC_DIRTEXT 957 +#define IDC_TREEVIEW 958 +#define IDC_TREESPLIT 959 // icons #define IDI_SALAMANDER 960 diff --git a/src/salamand.rc b/src/salamand.rc index 6d8897a5b..ffdded2e3 100644 --- a/src/salamand.rc +++ b/src/salamand.rc @@ -120,6 +120,7 @@ END IDA_MAINACCELS2 ACCELERATORS BEGIN + "T", CM_TOGGLETREEVIEW, VIRTKEY, SHIFT, CONTROL VK_ADD, CM_ACTIVESELECT, VIRTKEY VK_SUBTRACT, CM_ACTIVEUNSELECT, VIRTKEY VK_MULTIPLY, CM_ACTIVEINVERTSEL, VIRTKEY diff --git a/src/texts.rh2 b/src/texts.rh2 index 7674be6a6..93bc07bbe 100644 --- a/src/texts.rh2 +++ b/src/texts.rh2 @@ -2198,6 +2198,7 @@ #define IDS_MENU_OPT_VSB_PLG 13165 #define IDS_MENU_OPT_CST_PLG 13166 #define IDS_MENU_OPT_IMPORTCONFIG 13167 +#define IDS_MENU_OPT_VSB_TREE 13168 #define IDS_MENU_RIGHT 13170 #define IDS_MENU_RIGHT_CHANGEDRIVE 13171 From 85b0120f74e098957a6da67f97000fa556648769 Mon Sep 17 00:00:00 2001 From: Fernando Godoy Date: Wed, 8 Apr 2026 10:43:02 -0300 Subject: [PATCH 2/2] fix: keep panel tree layout aligned when toggling --- src/fileswn2.cpp | 4 ++++ src/fileswnb.cpp | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/fileswn2.cpp b/src/fileswn2.cpp index 70afd0b1a..60a416bec 100644 --- a/src/fileswn2.cpp +++ b/src/fileswn2.cpp @@ -221,6 +221,8 @@ void CFilesWindow::SetTreeViewWidth(int width) GetClientRect(HWindow, &r); SendMessage(HWindow, WM_SIZE, SIZE_RESTORED, MAKELONG(r.right - r.left, r.bottom - r.top)); + RedrawWindow(HWindow, NULL, NULL, + RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW); } } @@ -1673,6 +1675,8 @@ void CFilesWindow::UpdateTreeView(BOOL active) GetClientRect(HWindow, &r); SendMessage(HWindow, WM_SIZE, SIZE_RESTORED, MAKELONG(r.right - r.left, r.bottom - r.top)); + RedrawWindow(HWindow, NULL, NULL, + RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW); } } diff --git a/src/fileswnb.cpp b/src/fileswnb.cpp index d3babe1a3..23467d0d4 100644 --- a/src/fileswnb.cpp +++ b/src/fileswnb.cpp @@ -114,9 +114,12 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) int stHeight = 0; int treeWidth = 0; int treeHeight = height; + int treeY = 0; int listX = 0; int listWidth = width; int listHeight = height; + int directoryLineX = 0; + int directoryLineWidth = width; int statusLineX = 0; int statusLineWidth = width; int windowsCount = 1; @@ -136,34 +139,39 @@ CFilesWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) InvalidateRect(StatusLine->HWindow, NULL, FALSE); windowsCount++; } - treeHeight -= dlHeight; listHeight -= dlHeight; if (HTreeView != NULL && TreeViewActive) { treeWidth = GetTreeViewWidth(width); listX = treeWidth + TREEVIEW_SPLITTER_WIDTH; listWidth = width - listX; + directoryLineX = listX; + directoryLineWidth = listWidth; statusLineX = listX; statusLineWidth = listWidth; + treeY = 0; + treeHeight = height; windowsCount += HTreeSplit != NULL ? 2 : 1; } + else + treeHeight -= dlHeight; HDWP hdwp = HANDLES(BeginDeferWindowPos(windowsCount)); if (hdwp != NULL) { if (DirectoryLine->HWindow != NULL) hdwp = HANDLES(DeferWindowPos(hdwp, DirectoryLine->HWindow, NULL, - 0, 0, width, dlHeight, + directoryLineX, 0, directoryLineWidth, dlHeight, SWP_NOACTIVATE | SWP_NOZORDER)); if (HTreeView != NULL && TreeViewActive) hdwp = HANDLES(DeferWindowPos(hdwp, HTreeView, NULL, - 0, dlHeight, treeWidth, treeHeight, + 0, treeY, treeWidth, treeHeight, SWP_NOACTIVATE | SWP_NOZORDER)); if (HTreeSplit != NULL && TreeViewActive) hdwp = HANDLES(DeferWindowPos(hdwp, HTreeSplit, NULL, - treeWidth, dlHeight, TREEVIEW_SPLITTER_WIDTH, treeHeight, + treeWidth, treeY, TREEVIEW_SPLITTER_WIDTH, treeHeight, SWP_NOACTIVATE | SWP_NOZORDER)); hdwp = HANDLES(DeferWindowPos(hdwp, ListBox->HWindow, NULL,