From 62a9f1d0fa39b3d40208bd913e59cfc514722ee8 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 8 Sep 2024 14:57:45 +0200 Subject: [PATCH 01/51] added Drag into List feature --- src/DropTarget.cpp | 82 ++++++++++++++++++++++++++++ src/DropTarget.h | 31 +++++++++++ src/MultiReplacePanel.cpp | 47 +++++++++++++++- src/MultiReplacePanel.h | 6 +- vs.proj/MultiReplace.vcxproj | 2 + vs.proj/MultiReplace.vcxproj.filters | 2 + 6 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 src/DropTarget.cpp create mode 100644 src/DropTarget.h diff --git a/src/DropTarget.cpp b/src/DropTarget.cpp new file mode 100644 index 0000000..4f55f90 --- /dev/null +++ b/src/DropTarget.cpp @@ -0,0 +1,82 @@ +#include "DropTarget.h" +#include "MultiReplacePanel.h" + +DropTarget::DropTarget(HWND hwnd, MultiReplace* parent) + : _refCount(1), _hwnd(hwnd), _parent(parent) {} + +HRESULT DropTarget::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { + UNREFERENCED_PARAMETER(pDataObj); + UNREFERENCED_PARAMETER(grfKeyState); + UNREFERENCED_PARAMETER(pt); + + *pdwEffect = DROPEFFECT_COPY; // Indicate that only copy operations are allowed + return S_OK; +} + +HRESULT DropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { + UNREFERENCED_PARAMETER(grfKeyState); + UNREFERENCED_PARAMETER(pt); + + *pdwEffect = DROPEFFECT_COPY; // Continue to allow only copy operations + return S_OK; +} + +HRESULT DropTarget::DragLeave() { + // Handle the drag leave event with minimal overhead + return S_OK; +} + +HRESULT DropTarget::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) { + UNREFERENCED_PARAMETER(grfKeyState); + UNREFERENCED_PARAMETER(pt); + + try { + STGMEDIUM stgMedium; + FORMATETC formatEtc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + + // Check if the data is in file format + if (pDataObj->GetData(&formatEtc, &stgMedium) == S_OK) { + HDROP hDrop = static_cast(GlobalLock(stgMedium.hGlobal)); + if (hDrop) { + UINT numFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); + if (numFiles > 0) { + wchar_t filePath[MAX_PATH]; + DragQueryFile(hDrop, 0, filePath, MAX_PATH); + _parent->loadListFromCsv(filePath); // Load CSV file + } + GlobalUnlock(stgMedium.hGlobal); // Unlock the global memory object + } + ReleaseStgMedium(&stgMedium); // Release the storage medium + } + + *pdwEffect = DROPEFFECT_COPY; // Indicate that the operation was a copy + return S_OK; + } + catch (...) { + // Handle errors silently + *pdwEffect = DROPEFFECT_NONE; + return E_FAIL; + } +} + +HRESULT DropTarget::QueryInterface(REFIID riid, void** ppvObject) { + if (riid == IID_IUnknown || riid == IID_IDropTarget) { + *ppvObject = this; + AddRef(); + return S_OK; + } + *ppvObject = nullptr; + return E_NOINTERFACE; +} + +ULONG DropTarget::AddRef() { + return InterlockedIncrement(&_refCount); +} + +ULONG DropTarget::Release() { + ULONG count = InterlockedDecrement(&_refCount); + if (count == 0) { + delete this; + } + return count; +} diff --git a/src/DropTarget.h b/src/DropTarget.h new file mode 100644 index 0000000..b9b5494 --- /dev/null +++ b/src/DropTarget.h @@ -0,0 +1,31 @@ +#ifndef DROP_TARGET_H +#define DROP_TARGET_H + +#include +#include // Include for IDropTarget and other shell functions + +class MultiReplace; // Forward declaration to avoid circular dependency + +class DropTarget : public IDropTarget { +public: + DropTarget(HWND hwnd, MultiReplace* parent); + virtual ~DropTarget() {} + + // Implement all IDropTarget methods + HRESULT STDMETHODCALLTYPE DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override; + HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override; + HRESULT STDMETHODCALLTYPE DragLeave() override; + HRESULT STDMETHODCALLTYPE Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) override; + + // IUnknown methods + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override; + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + +private: + LONG _refCount; + HWND _hwnd; + MultiReplace* _parent; +}; + +#endif // DROP_TARGET_H diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index be1ac93..2f6d9f7 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -307,6 +307,18 @@ void MultiReplace::initializeListView() { ListView_SetExtendedListViewStyle(_replaceListView, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES); } +void MultiReplace::initializeDragAndDrop() { + dropTarget = new DropTarget(_replaceListView, this); // Create an instance of DropTarget + HRESULT hr = ::RegisterDragDrop(_replaceListView, dropTarget); // Register the ListView as a drop target + + if (FAILED(hr)) { + // Safely release the DropTarget instance to avoid memory leaks + delete dropTarget; + dropTarget = nullptr; // Set to nullptr to prevent any unintended usage + } +} + + void MultiReplace::moveAndResizeControls() { // IDs of controls to be moved or resized const int controlIds[] = { @@ -1658,6 +1670,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializePluginStyle(); initializeCtrlMap(); initializeListView(); + initializeDragAndDrop(); loadSettings(); updateButtonVisibilityBasedOnMode(); updateStatisticsColumnButtonIcon(); @@ -1709,15 +1722,45 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (_replaceListView && originalListViewProc) { SetWindowLongPtr(_replaceListView, GWLP_WNDPROC, (LONG_PTR)originalListViewProc); } - saveSettings(); + + saveSettings(); // Save any settings before destroying + + // Unregister Drag-and-Drop + if (dropTarget) { + RevokeDragDrop(_replaceListView); + delete dropTarget; + dropTarget = nullptr; + } + if (hwndEdit) { DestroyWindow(hwndEdit); } + DeleteObject(_hFont); - DestroyWindow(_hSelf); + + // Close the debug window if open + if (hDebugWnd != NULL) { + RECT rect; + if (GetWindowRect(hDebugWnd, &rect)) { + debugWindowPosition.x = rect.left; + debugWindowPosition.y = rect.top; + debugWindowPositionSet = true; + debugWindowSize.cx = rect.right - rect.left; + debugWindowSize.cy = rect.bottom - rect.top; + debugWindowSizeSet = true; + } + PostMessage(hDebugWnd, WM_CLOSE, 0, 0); + hDebugWnd = NULL; // Reset the handle after closing + } + + DestroyWindow(_hSelf); // Destroy the main window + + // Post a quit message to ensure the application terminates cleanly + PostQuitMessage(0); } break; + case WM_SIZE: { if (isWindowOpen) { diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 08e9557..db222d4 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -19,6 +19,7 @@ #include "StaticDialog/StaticDialog.h" #include "StaticDialog/resource.h" #include "PluginInterface.h" +#include "DropTarget.h" #include #include @@ -299,6 +300,10 @@ class MultiReplace : public StaticDialog static std::vector logChanges; + // Drag-and-Drop functionality + DropTarget* dropTarget; // Pointer to DropTarget instance + void loadListFromCsv(const std::wstring& filePath); // used in DropTarget.cpp + void initializeDragAndDrop(); protected: virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) override; @@ -551,7 +556,6 @@ class MultiReplace : public StaticDialog bool saveListToCsvSilent(const std::wstring& filePath, const std::vector& list); void saveListToCsv(const std::wstring& filePath, const std::vector& list); void loadListFromCsvSilent(const std::wstring& filePath, std::vector& list); - void loadListFromCsv(const std::wstring& filePath); std::wstring escapeCsvValue(const std::wstring& value); std::wstring unescapeCsvValue(const std::wstring& value); diff --git a/vs.proj/MultiReplace.vcxproj b/vs.proj/MultiReplace.vcxproj index 60ca785..b7e8384 100644 --- a/vs.proj/MultiReplace.vcxproj +++ b/vs.proj/MultiReplace.vcxproj @@ -28,6 +28,7 @@ + @@ -63,6 +64,7 @@ + /w %(AdditionalOptions) diff --git a/vs.proj/MultiReplace.vcxproj.filters b/vs.proj/MultiReplace.vcxproj.filters index 11bd2c4..351c10d 100644 --- a/vs.proj/MultiReplace.vcxproj.filters +++ b/vs.proj/MultiReplace.vcxproj.filters @@ -109,6 +109,7 @@ + @@ -201,6 +202,7 @@ + From 3310cb93e192cd9d173fabc165d364e20eeb7449 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Wed, 11 Sep 2024 12:15:18 +0200 Subject: [PATCH 02/51] added Save Button --- src/MultiReplacePanel.cpp | 59 +++++++++++++++++++++++++++++++++---- src/MultiReplacePanel.h | 5 ++++ src/StaticDialog/resource.h | 19 +++++++----- src/language_mapping.cpp | 1 + 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 2f6d9f7..e012b16 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -108,7 +108,7 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) int comboWidth = windowWidth - 365; int frameX = windowWidth - 320; int listWidth = windowWidth - 260; - int listHeight = windowHeight - 295; + int listHeight = windowHeight - 310; int checkboxX = buttonX - 105; // Static positions and sizes @@ -168,6 +168,8 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, 223, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, 284, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, 319, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_SAVE_BUTTON] = { buttonX, 319, 35, 30, WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; // "Save" Button mit Symbol + ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + 40, 319, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; // "Save As" Button mit Text ctrlMap[IDC_EXPORT_BASH_BUTTON] = { buttonX, 354, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_export_to_bash"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_UP_BUTTON] = { buttonX + 5, 404, 30, 30, WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; ctrlMap[IDC_DOWN_BUTTON] = { buttonX + 5, 404 + 30 + 5, 30, 30, WC_BUTTON, L"▼", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; @@ -318,15 +320,14 @@ void MultiReplace::initializeDragAndDrop() { } } - void MultiReplace::moveAndResizeControls() { // IDs of controls to be moved or resized const int controlIds[] = { IDC_FIND_EDIT, IDC_REPLACE_EDIT, IDC_SWAP_BUTTON, IDC_STATIC_FRAME, IDC_COPY_TO_LIST_BUTTON, IDC_REPLACE_ALL_BUTTON, IDC_REPLACE_BUTTON, IDC_REPLACE_ALL_SMALL_BUTTON, IDC_2_BUTTONS_MODE, IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_FIND_PREV_BUTTON, IDC_MARK_BUTTON, IDC_MARK_MATCHES_BUTTON, IDC_CLEAR_MARKS_BUTTON, IDC_COPY_MARKED_TEXT_BUTTON, - IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_SAVE_TO_CSV_BUTTON, IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, - IDC_SHIFT_TEXT, IDC_EXPORT_BASH_BUTTON + IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_SAVE_TO_CSV_BUTTON, IDC_SAVE_BUTTON, IDC_SAVE_AS_BUTTON, + IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, IDC_SHIFT_TEXT, IDC_EXPORT_BASH_BUTTON }; std::unordered_map hwndMap; // Store HWNDs to avoid multiple calls to GetDlgItem @@ -392,6 +393,11 @@ void MultiReplace::updateButtonVisibilityBasedOnMode() { ShowWindow(GetDlgItem(_hSelf, IDC_MARK_MATCHES_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(_hSelf, IDC_MARK_BUTTON), twoButtonsMode ? SW_HIDE : SW_SHOW); + + // for save buttons + ShowWindow(GetDlgItem(_hSelf, IDC_SAVE_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(_hSelf, IDC_SAVE_AS_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(_hSelf, IDC_SAVE_TO_CSV_BUTTON), twoButtonsMode ? SW_HIDE : SW_SHOW); } void MultiReplace::setUIElementVisibility() { @@ -691,7 +697,7 @@ void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { ListView_SetColumnWidth(listView, 4, perColumnWidth); // Find Text ListView_SetColumnWidth(listView, 5, perColumnWidth); // Replace Text - MoveWindow(listHwnd, 20, 284, newWidth - 260, newHeight - 295, TRUE); + MoveWindow(listHwnd, 20, 284, newWidth - 260, newHeight - 310, TRUE); SendMessage(widths.listView, WM_SETREDRAW, TRUE, 0); //InvalidateRect(widths.listView, NULL, TRUE); @@ -2364,6 +2370,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l } break; + case IDC_SAVE_AS_BUTTON: case IDC_SAVE_TO_CSV_BUTTON: { std::wstring csvDescription = getLangStr(L"filetype_csv"); // "CSV Files (*.csv)" @@ -5787,6 +5794,39 @@ void MultiReplace::showStatusMessage(const std::wstring& messageText, COLORREF c UpdateWindow(GetParent(hStatusMessage)); } +void MultiReplace::showListFilePath() { + const size_t MAX_PATH_DISPLAY_LENGTH = 120; // Maximum length for the displayed path + + // Use the stored file path and shorten it if necessary + std::wstring displayPath = listFilePath; + + // Shorten the path if it exceeds the maximum length + if (displayPath.size() > MAX_PATH_DISPLAY_LENGTH) { + size_t lastSlashPos = displayPath.find_last_of(L"\\/"); + std::wstring fileName = (lastSlashPos != std::wstring::npos) ? displayPath.substr(lastSlashPos + 1) : displayPath; + if (fileName.size() > MAX_PATH_DISPLAY_LENGTH) { + displayPath = fileName.substr(0, MAX_PATH_DISPLAY_LENGTH - 3) + L"..."; + } + else { + size_t remainingLength = MAX_PATH_DISPLAY_LENGTH - fileName.size(); + displayPath = displayPath.substr(0, remainingLength) + L"..." + fileName; + } + } + + // Get the handle for the path display control + HWND hPathDisplay = GetDlgItem(_hSelf, IDC_PATH_DISPLAY); + + // Set the new path text + SetWindowText(hPathDisplay, displayPath.c_str()); + + // Invalidate the area of the parent where the control resides + RECT rect; + GetWindowRect(hPathDisplay, &rect); + MapWindowPoints(HWND_DESKTOP, GetParent(hPathDisplay), (LPPOINT)&rect, 2); + InvalidateRect(GetParent(hPathDisplay), &rect, TRUE); + UpdateWindow(GetParent(hPathDisplay)); +} + std::wstring MultiReplace::getSelectedText() { SIZE_T length = SendMessage(nppData._scintillaMainHandle, SCI_GETSELTEXT, 0, 0); @@ -6120,6 +6160,11 @@ void MultiReplace::saveListToCsv(const std::wstring& filePath, const std::vector EnableWindow(_replaceListView, TRUE); } +void MultiReplace::saveListToCurrentFile() { + // Dummy-Implementierung + showStatusMessage(getLangStr(L"status_save_to_file"), RGB(0, 128, 0)); +} + void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vector& list) { // Open file in binary mode to read UTF-8 data std::ifstream inFile(filePath); @@ -6184,6 +6229,10 @@ void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vect void MultiReplace::loadListFromCsv(const std::wstring& filePath) { try { loadListFromCsvSilent(filePath, replaceListData); + // Store the file path only if loading was successful + listFilePath = filePath; + // Display the path below the list + showListFilePath(); } catch (const CsvLoadException& ex) { // Resolve the error key to a localized string when displaying the message diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index db222d4..5b824e5 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -394,6 +394,9 @@ class MultiReplace : public StaticDialog SciFnDirect pSciMsg = nullptr; sptr_t pSciWndData = 0; + // List related + std::wstring listFilePath = L""; //to store the file path of loaded list + // GUI control-related constants const std::vector selectionRadioDisabledButtons = { IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_REPLACE_BUTTON @@ -536,6 +539,7 @@ class MultiReplace : public StaticDialog void updateHeaderSelection(); void updateHeaderSortDirection(); void showStatusMessage(const std::wstring& messageText, COLORREF color); + void showListFilePath(); void displayResultCentered(size_t posStart, size_t posEnd, bool isDownwards); std::wstring getSelectedText(); LRESULT getEOLLength(); @@ -555,6 +559,7 @@ class MultiReplace : public StaticDialog std::wstring MultiReplace::openFileDialog(bool saveFile, const std::vector>& filters, const WCHAR* title, DWORD flags, const std::wstring& fileExtension); bool saveListToCsvSilent(const std::wstring& filePath, const std::vector& list); void saveListToCsv(const std::wstring& filePath, const std::vector& list); + void saveListToCurrentFile(); void loadListFromCsvSilent(const std::wstring& filePath, std::vector& list); std::wstring escapeCsvValue(const std::wstring& value); std::wstring unescapeCsvValue(const std::wstring& value); diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index 5c3e45e..8e84ca2 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -45,17 +45,20 @@ #define IDC_DELETE_REPLACE_ITEM_BUTTON 5019 #define IDC_LOAD_FROM_CSV_BUTTON 5020 #define IDC_SAVE_TO_CSV_BUTTON 5021 -#define IDC_EXPORT_BASH_BUTTON 5022 -#define IDC_UP_BUTTON 5023 -#define IDC_DOWN_BUTTON 5024 -#define IDC_SHIFT_FRAME 5025 -#define IDC_SHIFT_TEXT 5026 -#define ID_REPLACE_ALL_OPTION 5027 -#define ID_REPLACE_IN_ALL_DOCS_OPTION 5028 +#define IDC_SAVE_BUTTON 5022 +#define IDC_SAVE_AS_BUTTON 5023 +#define IDC_EXPORT_BASH_BUTTON 5024 +#define IDC_UP_BUTTON 5025 +#define IDC_DOWN_BUTTON 5026 +#define IDC_SHIFT_FRAME 5027 +#define IDC_SHIFT_TEXT 5028 +#define ID_REPLACE_ALL_OPTION 5029 +#define ID_REPLACE_IN_ALL_DOCS_OPTION 5030 #define IDC_STATIC_FIND 5100 #define IDC_STATIC_REPLACE 5101 #define IDC_STATUS_MESSAGE 5102 +#define IDC_PATH_DISPLAY 5103 #define IDC_WHOLE_WORD_CHECKBOX 5200 #define IDC_MATCH_CASE_CHECKBOX 5201 @@ -126,7 +129,7 @@ #define POS_X 92 #define POS_Y 40 #define MIN_WIDTH 1023 -#define MIN_HEIGHT 485 +#define MIN_HEIGHT 500 #define IDC_WEBSITE_LINK_VALUE TEXT("https://github.com/daddel80/notepadpp-multireplace/issues") diff --git a/src/language_mapping.cpp b/src/language_mapping.cpp index d88f621..61cc121 100644 --- a/src/language_mapping.cpp +++ b/src/language_mapping.cpp @@ -30,6 +30,7 @@ std::unordered_map languageMap = { { L"panel_clear_all_marks", L"Clear all marks" }, { L"panel_load_list", L"Load List" }, { L"panel_save_list", L"Save List" }, +{ L"panel_save_as", L"Save As..." }, { L"panel_export_to_bash", L"Export to Bash" }, { L"panel_shift_lines", L"Shift Lines" }, { L"panel_use_list", L"Use List" }, From b5f4c69f2b21f6fcbc99d5da47eb4df66a3b6659 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 16 Sep 2024 11:54:19 +0200 Subject: [PATCH 03/51] added Path display --- src/MultiReplacePanel.cpp | 140 ++++++++++++++++++++++++++++++------ src/MultiReplacePanel.h | 4 +- src/StaticDialog/resource.h | 22 +++--- 3 files changed, 133 insertions(+), 33 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index e012b16..aefb3b9 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -167,6 +167,8 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_COPY_MARKED_TEXT_BUTTON] = { buttonX + 125, 188, 35, 30, WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_marked_text") }; ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, 223, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, 284, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_LOAD_LIST_BUTTON] = { buttonX, 284, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_CLEAR_LIST_BUTTON] = { buttonX + 125, 284, 35, 30, WC_BUTTON, L"🗑️", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_clear_list") }; ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, 319, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_SAVE_BUTTON] = { buttonX, 319, 35, 30, WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; // "Save" Button mit Symbol ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + 40, 319, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; // "Save As" Button mit Text @@ -176,6 +178,7 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_SHIFT_FRAME] = { buttonX, 404 - 14, 165, 85, WC_BUTTON, L"", BS_GROUPBOX, NULL }; ctrlMap[IDC_SHIFT_TEXT] = { buttonX + 38, 404 + 20, 120, 20, WC_STATIC, getLangStrLPCWSTR(L"panel_shift_lines"), SS_LEFT, NULL }; ctrlMap[IDC_REPLACE_LIST] = { 20, 284, listWidth, listHeight, WC_LISTVIEW, NULL, LVS_REPORT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_VSCROLL | LVS_SHOWSELALWAYS, NULL }; + ctrlMap[IDC_PATH_DISPLAY] = { 20, 284 + listHeight + 5, listWidth, 24, WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, 175, 95, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; ctrlMap[ID_STATISTICS_COLUMNS] = { 2, 285, 17, 24, WC_BUTTON, L"▶", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, getLangStrLPCWSTR(L"tooltip_display_statistics_columns") }; } @@ -326,8 +329,8 @@ void MultiReplace::moveAndResizeControls() { IDC_FIND_EDIT, IDC_REPLACE_EDIT, IDC_SWAP_BUTTON, IDC_STATIC_FRAME, IDC_COPY_TO_LIST_BUTTON, IDC_REPLACE_ALL_BUTTON, IDC_REPLACE_BUTTON, IDC_REPLACE_ALL_SMALL_BUTTON, IDC_2_BUTTONS_MODE, IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_FIND_PREV_BUTTON, IDC_MARK_BUTTON, IDC_MARK_MATCHES_BUTTON, IDC_CLEAR_MARKS_BUTTON, IDC_COPY_MARKED_TEXT_BUTTON, - IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_SAVE_TO_CSV_BUTTON, IDC_SAVE_BUTTON, IDC_SAVE_AS_BUTTON, - IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, IDC_SHIFT_TEXT, IDC_EXPORT_BASH_BUTTON + IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_LOAD_LIST_BUTTON, IDC_CLEAR_LIST_BUTTON, IDC_SAVE_TO_CSV_BUTTON, + IDC_SAVE_BUTTON, IDC_SAVE_AS_BUTTON, IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, IDC_SHIFT_TEXT, IDC_EXPORT_BASH_BUTTON, IDC_PATH_DISPLAY }; std::unordered_map hwndMap; // Store HWNDs to avoid multiple calls to GetDlgItem @@ -361,6 +364,8 @@ void MultiReplace::moveAndResizeControls() { } } + showListFilePath(); + /* // IDs of controls to be redrawn const int redrawIds[] = { @@ -394,6 +399,11 @@ void MultiReplace::updateButtonVisibilityBasedOnMode() { ShowWindow(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(_hSelf, IDC_MARK_BUTTON), twoButtonsMode ? SW_HIDE : SW_SHOW); + // for load buttons + ShowWindow(GetDlgItem(_hSelf, IDC_LOAD_LIST_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(_hSelf, IDC_CLEAR_LIST_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(_hSelf, IDC_LOAD_FROM_CSV_BUTTON), twoButtonsMode ? SW_HIDE : SW_SHOW); + // for save buttons ShowWindow(GetDlgItem(_hSelf, IDC_SAVE_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(_hSelf, IDC_SAVE_AS_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); @@ -1036,6 +1046,24 @@ void MultiReplace::resizeCountColumns() { } } +void MultiReplace::clearList() { + // Clear the replace list data vector + replaceListData.clear(); + + // Update the ListView to reflect the cleared list + ListView_SetItemCountEx(_replaceListView, 0, LVSICF_NOINVALIDATEALL); + InvalidateRect(_replaceListView, NULL, TRUE); + + // Reset listFilePath to an empty string + listFilePath.clear(); + + // Show a status message to indicate the list was cleared + showStatusMessage(getLangStr(L"status_list_cleared"), RGB(0, 128, 0)); + + // Call showListFilePath to update the UI with the cleared file path + showListFilePath(); +} + #pragma endregion @@ -1766,7 +1794,6 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l } break; - case WM_SIZE: { if (isWindowOpen) { @@ -2390,6 +2417,15 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l } break; + case IDC_SAVE_BUTTON: + { + if (!listFilePath.empty()) { + saveListToCsv(listFilePath, replaceListData); + } + } + break; + + case IDC_LOAD_LIST_BUTTON: case IDC_LOAD_FROM_CSV_BUTTON: { std::wstring csvDescription = getLangStr(L"filetype_csv"); // "CSV Files (*.csv)" @@ -2409,6 +2445,12 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l } break; + case IDC_CLEAR_LIST_BUTTON: + { + clearList(); + } + break; + case IDC_UP_BUTTON: { shiftListItem(_replaceListView, Direction::Up); @@ -5795,31 +5837,85 @@ void MultiReplace::showStatusMessage(const std::wstring& messageText, COLORREF c } void MultiReplace::showListFilePath() { - const size_t MAX_PATH_DISPLAY_LENGTH = 120; // Maximum length for the displayed path + // Define the path to be displayed + //std::wstring path = L"C:\\Users\\ExampleUser\\Documents\\Projects\\TestProject\\SubFolder\\AnotherSubFolder\\Users\\ExampleUser\\Documents\\Projects\\TestProject\\SubFolder\\AnotherSubFolderThisIsAReallyLongFileNameForTestingPurposes.txt"; - // Use the stored file path and shorten it if necessary - std::wstring displayPath = listFilePath; + std::wstring path = listFilePath; - // Shorten the path if it exceeds the maximum length - if (displayPath.size() > MAX_PATH_DISPLAY_LENGTH) { - size_t lastSlashPos = displayPath.find_last_of(L"\\/"); - std::wstring fileName = (lastSlashPos != std::wstring::npos) ? displayPath.substr(lastSlashPos + 1) : displayPath; - if (fileName.size() > MAX_PATH_DISPLAY_LENGTH) { - displayPath = fileName.substr(0, MAX_PATH_DISPLAY_LENGTH - 3) + L"..."; - } - else { - size_t remainingLength = MAX_PATH_DISPLAY_LENGTH - fileName.size(); - displayPath = displayPath.substr(0, remainingLength) + L"..." + fileName; + // Obtain handle and device context for the path display control + HWND hPathDisplay = GetDlgItem(_hSelf, IDC_PATH_DISPLAY); + HDC hDC = GetDC(hPathDisplay); + HFONT hFont = (HFONT)SendMessage(hPathDisplay, WM_GETFONT, 0, 0); + SelectObject(hDC, hFont); + + // Calculate the width of each character in the path and the width of the dots ("...") + double dotWidth = 0.0; + SIZE charSize; + std::vector characterWidths; + + for (wchar_t ch : path) { + GetTextExtentPoint32(hDC, &ch, 1, &charSize); + characterWidths.push_back(static_cast(charSize.cx)); + if (ch == L'.') { + dotWidth = static_cast(charSize.cx); // Store width of '.' separately } } - // Get the handle for the path display control - HWND hPathDisplay = GetDlgItem(_hSelf, IDC_PATH_DISPLAY); + ReleaseDC(hPathDisplay, hDC); - // Set the new path text - SetWindowText(hPathDisplay, displayPath.c_str()); + RECT rcPathDisplay; + GetClientRect(hPathDisplay, &rcPathDisplay); + int pathDisplayWidth = rcPathDisplay.right - rcPathDisplay.left; + double totalDotsWidth = dotWidth * 3; // Width for "..." - // Invalidate the area of the parent where the control resides + size_t lastSlashPos = path.find_last_of(L"\\/"); + std::wstring directoryPart = (lastSlashPos != std::wstring::npos) ? path.substr(0, lastSlashPos + 1) : L""; + std::wstring fileName = (lastSlashPos != std::wstring::npos) ? path.substr(lastSlashPos + 1) : path; + + // Calculate the width of directory and file name parts + double directoryWidth = 0.0, fileNameWidth = 0.0; + + for (size_t i = 0; i < directoryPart.size(); ++i) { + directoryWidth += characterWidths[i]; + } + + for (size_t i = directoryPart.size(); i < path.size(); ++i) { + fileNameWidth += characterWidths[i]; + } + + std::wstring displayPath; + double currentWidth = 0.0; + + // Check if file name needs to be shortened + if (fileNameWidth + totalDotsWidth > pathDisplayWidth) { + for (size_t i = directoryPart.size(); i < path.size(); ++i) { + if (currentWidth + characterWidths[i] + totalDotsWidth > pathDisplayWidth) { + break; + } + displayPath += path[i]; + currentWidth += characterWidths[i]; + } + displayPath += L"..."; + } + // Check if directory part needs to be shortened + else if (directoryWidth + fileNameWidth > pathDisplayWidth) { + for (size_t i = 0; i < directoryPart.size(); ++i) { + if (currentWidth + characterWidths[i] + totalDotsWidth + fileNameWidth > pathDisplayWidth) { + break; + } + displayPath += directoryPart[i]; + currentWidth += characterWidths[i]; + } + displayPath += L"..."; + displayPath += fileName; + } + else { + displayPath = path; // No shortening needed + } + + SetWindowTextW(hPathDisplay, displayPath.c_str()); + + // Update the parent window area where the control resides RECT rect; GetWindowRect(hPathDisplay, &rect); MapWindowPoints(HWND_DESKTOP, GetParent(hPathDisplay), (LPPOINT)&rect, 2); @@ -6179,7 +6275,7 @@ void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vect std::wstring line; std::getline(contentStream, line); // Skip the CSV header - + while (std::getline(contentStream, line)) { std::wstringstream lineStream(line); std::vector columns; diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 5b824e5..2a2e1df 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -445,6 +445,7 @@ class MultiReplace : public StaticDialog void resetCountColumns(); void updateCountColumns(size_t itemIndex, int findCount, int replaceCount = -1); void resizeCountColumns(); + void clearList(); //Contextmenu void toggleBooleanAt(int itemIndex, int Column); @@ -539,6 +540,7 @@ class MultiReplace : public StaticDialog void updateHeaderSelection(); void updateHeaderSortDirection(); void showStatusMessage(const std::wstring& messageText, COLORREF color); + void calculateCharacterWidths(); void showListFilePath(); void displayResultCentered(size_t posStart, size_t posEnd, bool isDownwards); std::wstring getSelectedText(); @@ -597,4 +599,4 @@ extern std::unordered_map languageMap; extern MultiReplace _MultiReplace; -#endif // MULTI_REPLACE_H +#endif // MULTI_REPLACE_H \ No newline at end of file diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index 8e84ca2..47676ee 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -44,16 +44,18 @@ #define IDC_REPLACE_ALL_IN_LIST_BUTTON 5018 #define IDC_DELETE_REPLACE_ITEM_BUTTON 5019 #define IDC_LOAD_FROM_CSV_BUTTON 5020 -#define IDC_SAVE_TO_CSV_BUTTON 5021 -#define IDC_SAVE_BUTTON 5022 -#define IDC_SAVE_AS_BUTTON 5023 -#define IDC_EXPORT_BASH_BUTTON 5024 -#define IDC_UP_BUTTON 5025 -#define IDC_DOWN_BUTTON 5026 -#define IDC_SHIFT_FRAME 5027 -#define IDC_SHIFT_TEXT 5028 -#define ID_REPLACE_ALL_OPTION 5029 -#define ID_REPLACE_IN_ALL_DOCS_OPTION 5030 +#define IDC_LOAD_LIST_BUTTON 5021 +#define IDC_CLEAR_LIST_BUTTON 5022 +#define IDC_SAVE_TO_CSV_BUTTON 5023 +#define IDC_SAVE_BUTTON 5024 +#define IDC_SAVE_AS_BUTTON 5025 +#define IDC_EXPORT_BASH_BUTTON 5026 +#define IDC_UP_BUTTON 5027 +#define IDC_DOWN_BUTTON 5028 +#define IDC_SHIFT_FRAME 5029 +#define IDC_SHIFT_TEXT 5030 +#define ID_REPLACE_ALL_OPTION 5031 +#define ID_REPLACE_IN_ALL_DOCS_OPTION 5032 #define IDC_STATIC_FIND 5100 #define IDC_STATIC_REPLACE 5101 From 04901a3c8b6f3ad7783c63d9ae4f16af357d0350 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Tue, 17 Sep 2024 18:13:07 +0200 Subject: [PATCH 04/51] saving listFilePath in INI --- src/MultiReplacePanel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index aefb3b9..e92b92d 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -6715,6 +6715,10 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { outFile << wstringToString(L"QuoteChar=" + quoteChar + L"\n"); outFile << wstringToString(L"HeaderLines=" + headerLines + L"\n"); + // Save the list file path + outFile << wstringToString(L"[Paths]\n"); + outFile << wstringToString(L"ListFilePath=" + listFilePath + L"\n"); + // Convert and Store "Find what" history LRESULT findWhatCount = SendMessage(GetDlgItem(_hSelf, IDC_FIND_EDIT), CB_GETCOUNT, 0, 0); outFile << wstringToString(L"[History]\n"); @@ -6836,6 +6840,10 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { CSVheaderLinesCount = readIntFromIniFile(iniFilePath, L"Scope", L"HeaderLines", 1); + // Load the list file path from the INI file + listFilePath = readStringFromIniFile(iniFilePath, L"Paths", L"ListFilePath", L""); + showListFilePath(); + // Adjusting UI elements based on the selected scope From a86af07e68bd9c57b29b32ac22d568cc11a69477 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Thu, 19 Sep 2024 15:44:32 +0200 Subject: [PATCH 05/51] changed Clear Button into New List Button --- src/MultiReplacePanel.cpp | 10 +++++----- src/StaticDialog/resource.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index e92b92d..3856b08 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -154,7 +154,7 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_STATIC_FRAME] = { frameX, 99, 285, 163, WC_BUTTON, L"", BS_GROUPBOX, NULL }; ctrlMap[IDC_REPLACE_ALL_BUTTON] = { buttonX, 118, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_all"), BS_SPLITBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_REPLACE_BUTTON] = { buttonX, 118, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_replace"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_ALL_SMALL_BUTTON] = { buttonX + 125, 118, 35, 30, WC_BUTTON, L"٭", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_replace_all") }; + ctrlMap[IDC_REPLACE_ALL_SMALL_BUTTON] = { buttonX + 125, 118, 35, 30, WC_BUTTON, L"↻", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_replace_all")}; ctrlMap[IDC_2_BUTTONS_MODE] = { checkbox2X, 118, 25, 25, WC_BUTTON, L"", BS_AUTOCHECKBOX | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_2_buttons_mode") }; ctrlMap[IDC_FIND_BUTTON] = { buttonX, 153, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_find_next"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; @@ -168,7 +168,7 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, 223, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, 284, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_LOAD_LIST_BUTTON] = { buttonX, 284, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_CLEAR_LIST_BUTTON] = { buttonX + 125, 284, 35, 30, WC_BUTTON, L"🗑️", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_clear_list") }; + ctrlMap[IDC_NEW_LIST_BUTTON] = { buttonX + 125, 284, 35, 30, WC_BUTTON, L"➕", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_new_list") }; ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, 319, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_SAVE_BUTTON] = { buttonX, 319, 35, 30, WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; // "Save" Button mit Symbol ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + 40, 319, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; // "Save As" Button mit Text @@ -329,7 +329,7 @@ void MultiReplace::moveAndResizeControls() { IDC_FIND_EDIT, IDC_REPLACE_EDIT, IDC_SWAP_BUTTON, IDC_STATIC_FRAME, IDC_COPY_TO_LIST_BUTTON, IDC_REPLACE_ALL_BUTTON, IDC_REPLACE_BUTTON, IDC_REPLACE_ALL_SMALL_BUTTON, IDC_2_BUTTONS_MODE, IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_FIND_PREV_BUTTON, IDC_MARK_BUTTON, IDC_MARK_MATCHES_BUTTON, IDC_CLEAR_MARKS_BUTTON, IDC_COPY_MARKED_TEXT_BUTTON, - IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_LOAD_LIST_BUTTON, IDC_CLEAR_LIST_BUTTON, IDC_SAVE_TO_CSV_BUTTON, + IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_LOAD_LIST_BUTTON, IDC_NEW_LIST_BUTTON, IDC_SAVE_TO_CSV_BUTTON, IDC_SAVE_BUTTON, IDC_SAVE_AS_BUTTON, IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, IDC_SHIFT_TEXT, IDC_EXPORT_BASH_BUTTON, IDC_PATH_DISPLAY }; @@ -401,7 +401,7 @@ void MultiReplace::updateButtonVisibilityBasedOnMode() { // for load buttons ShowWindow(GetDlgItem(_hSelf, IDC_LOAD_LIST_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(_hSelf, IDC_CLEAR_LIST_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(_hSelf, IDC_NEW_LIST_BUTTON), twoButtonsMode ? SW_SHOW : SW_HIDE); ShowWindow(GetDlgItem(_hSelf, IDC_LOAD_FROM_CSV_BUTTON), twoButtonsMode ? SW_HIDE : SW_SHOW); // for save buttons @@ -2445,7 +2445,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l } break; - case IDC_CLEAR_LIST_BUTTON: + case IDC_NEW_LIST_BUTTON: { clearList(); } diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index 47676ee..72a5aaa 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -45,7 +45,7 @@ #define IDC_DELETE_REPLACE_ITEM_BUTTON 5019 #define IDC_LOAD_FROM_CSV_BUTTON 5020 #define IDC_LOAD_LIST_BUTTON 5021 -#define IDC_CLEAR_LIST_BUTTON 5022 +#define IDC_NEW_LIST_BUTTON 5022 #define IDC_SAVE_TO_CSV_BUTTON 5023 #define IDC_SAVE_BUTTON 5024 #define IDC_SAVE_AS_BUTTON 5025 From 3d32fbc60facd84509a49215a4fc8354f5079680 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 22 Sep 2024 15:31:55 +0200 Subject: [PATCH 06/51] added messages and discover changes of list --- languages.ini | 51 ++++++-- src/MultiReplacePanel.cpp | 243 ++++++++++++++++++++++++++++++-------- src/MultiReplacePanel.h | 28 ++++- src/language_mapping.cpp | 6 +- 4 files changed, 265 insertions(+), 63 deletions(-) diff --git a/languages.ini b/languages.ini index 0468a07..b8965ae 100644 --- a/languages.ini +++ b/languages.ini @@ -28,6 +28,7 @@ panel_mark_matches_small="Mark Matches" panel_clear_all_marks="Clear all marks" panel_load_list="Load List" panel_save_list="Save List" +panel_save_as="Save As..." panel_export_to_bash="Export to Bash" panel_shift_lines="Shift Lines" panel_use_list="Use List" @@ -53,6 +54,8 @@ tooltip_copy_columns="Copy Columns to Clipboard" tooltip_column_highlight="Column highlight: On/Off" tooltip_copy_marked_text="Copy Marked Text" tooltip_display_statistics_columns="Show/Hide Statistics Columns" +tooltip_new_list="New List" +tooltip_save="Save List" ; List headers header_find_count="Find Count" @@ -133,7 +136,6 @@ status_no_matches_found_for="No matches found for '$REPLACE_STRING'." status_actual_position="Actual Position $REPLACE_STRING" status_items_loaded_from_csv="$REPLACE_STRING items loaded from CSV." status_wrapped_position="Wrapped at $REPLACE_STRING" -status_deleted_fields="Deleted $REPLACE_STRING fields." status_occurrences_marked="$REPLACE_STRING occurrences were marked." status_items_copied_to_clipboard="$REPLACE_STRING items copied into Clipboard." status_no_matches_after_wrap_for="No matches found for '$REPLACE_STRING' after wrap." @@ -147,6 +149,7 @@ msgbox_title_error="Error" msgbox_title_confirm="Confirm" msgbox_title_use_variables_syntax_error="Use Variables: Syntax Error" msgbox_title_use_variables_execution_error="Use Variables: Execution Error" +msgbox_title_save_list="Save list" ; MessageBox Messages msgbox_failed_create_control="Failed to create control with ID: $REPLACE_STRING1, GetLastError returned: $REPLACE_STRING2" @@ -156,6 +159,8 @@ msgbox_error_saving_settings="An error occurred while saving the settings:
$ msgbox_use_variables_execution_error="Execution halted due to execution failure in:
$REPLACE_STRING" msgbox_confirm_delete_single="Are you sure you want to delete this line?" msgbox_confirm_delete_multiple="Are you sure you want to delete $REPLACE_STRING lines?" +msgbox_save_list_file="Save list: $REPLACE_STRING?" +msgbox_save_list="Do you want to save the list?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transfer to Input Fields Alt+Up" @@ -200,6 +205,7 @@ panel_mark_matches_small="Alle markieren" panel_clear_all_marks="Markierung aufheben" panel_load_list="Lade Liste" panel_save_list="Speichere Liste" +panel_save_as="Speichern unter..." panel_export_to_bash="In Bash exportieren" panel_shift_lines="Zeilen verschieben" panel_use_list="Liste ein" @@ -225,6 +231,8 @@ tooltip_copy_columns="Spalten in Zwischenablage kopieren" tooltip_column_highlight="Spalte hervorheben: ein/aus" tooltip_copy_marked_text="Markierten Text kopieren" tooltip_display_statistics_columns="Statistikspalten ein/ausblenden" +tooltip_new_list="Neue Liste" +tooltip_save="Liste speichern" ; List headers header_find_count="Suchen Zähler" @@ -304,7 +312,6 @@ status_no_matches_found_for="Keine Übereinstimmungen gefunden für '$REPLACE_ST status_actual_position="Aktuelle Position $REPLACE_STRING" status_items_loaded_from_csv="$REPLACE_STRING Elemente aus CSV geladen." status_wrapped_position="Umbruch bei $REPLACE_STRING" -status_deleted_fields="$REPLACE_STRING Felder gelöscht." status_occurrences_marked="$REPLACE_STRING Vorkommnisse markiert." status_items_copied_to_clipboard="$REPLACE_STRING Elemente in Zwischenablage kopiert." status_no_matches_after_wrap_for="Keine Übereinstimmungen für '$REPLACE_STRING' nach Umbruch gefunden." @@ -318,6 +325,7 @@ msgbox_title_error="Fehler" msgbox_title_confirm="Bestätigen" msgbox_title_use_variables_syntax_error="Syntaxfehler: Variablen benutzen" msgbox_title_use_variables_execution_error="Ausführungsfehler: Variablen benutzen" +msgbox_title_save_list="Liste speichern" ; MessageBox Messages msgbox_failed_create_control="Erstellung des Steuerelements mit ID: $REPLACE_STRING1 fehlgeschlagen, GetLastError zurückgegeben: $REPLACE_STRING2" @@ -327,6 +335,8 @@ msgbox_error_saving_settings="Fehler beim Speichern der Einstellungen:
$REPL msgbox_use_variables_execution_error="Ausführung wegen Fehler angehalten:
$REPLACE_STRING" msgbox_confirm_delete_single="Sind Sie sicher, dass Sie diese Zeile löschen möchten?" msgbox_confirm_delete_multiple="Sind Sie sicher, dass Sie $REPLACE_STRING Zeilen löschen möchten?" +msgbox_save_list_file="Liste speichern: $REPLACE_STRING?" +msgbox_save_list="Möchten Sie die Liste speichern?" ; Context Menu ctxmenu_transfer_to_input_fields="&In Eingabefelder übertragen Alt+Hoch" @@ -371,6 +381,7 @@ panel_mark_matches_small="Evidenzia Corrisp." panel_clear_all_marks="Pulisci tutte le evidenz." panel_load_list="Carica elenco" panel_save_list="Salva elenco" +panel_save_as="Salva come..." panel_export_to_bash="Esporta a Bash" panel_shift_lines="Righe spostate" panel_use_list="Usa Elenco" @@ -396,6 +407,8 @@ tooltip_copy_columns="Copia Colonne negli Appunti" tooltip_column_highlight="Evidenzia Colonne: On/Off" tooltip_copy_marked_text="Copia Testo Evidenziato" tooltip_display_statistics_columns="Mostra/nascondi colonne delle statistiche" +tooltip_new_list="Nuovo elenco" +tooltip_save="Salva elenco" ; List headers header_find_count="Conteggio dei trovati" @@ -476,7 +489,6 @@ status_no_matches_found_for="Nessuna corrispondenza trovata per '$REPLACE_STRING status_actual_position="Posizione effettiva $REPLACE_STRING" status_items_loaded_from_csv="Voce $REPLACE_STRING caricata da CSV." status_wrapped_position="Wrapped at $REPLACE_STRING" -status_deleted_fields="Eliminati $REPLACE_STRING campi." status_occurrences_marked="$REPLACE_STRING corrispondenze evidenziate." status_items_copied_to_clipboard="$REPLACE_STRING voci copiate negli appunti." status_no_matches_after_wrap_for="No matches found for '$REPLACE_STRING' after wrap." @@ -490,15 +502,18 @@ msgbox_title_error="Errore" msgbox_title_confirm="Conferma" msgbox_title_use_variables_syntax_error="Usa Variabili: Errore di sintassi" msgbox_title_use_variables_execution_error="Usa Variabili: Errore di esecuzione" +msgbox_title_save_list="Salva elenco" ; MessageBox Messages msgbox_failed_create_control="Creazione del controllo non andato a buon fine con ID: $REPLACE_STRING1, GetLastError returned: $REPLACE_STRING2" msgbox_confirm_replace_all="Vuoi sostituire tutte le corrispondenze in tutti i documenti aperti?" -msgbox_confirm_delete_columns="Vuoi cancellare le colonne $REPLACE_STRING ?" +msgbox_confirm_delete_columns="Vuoi cancellare le colonne $REPLACE_STRING?" msgbox_error_saving_settings="Si è verificato un errore durante il salvataggio delle impostazioni:
$REPLACE_STRING" msgbox_use_variables_execution_error="Esecuzione interrotta per errore di esecuzione in:
$REPLACE_STRING" msgbox_confirm_delete_single="Vuoi cancellare questa riga?" -msgbox_confirm_delete_multiple="Vuoi cancellare la riga $REPLACE_STRING ?" +msgbox_confirm_delete_multiple="Vuoi cancellare la riga $REPLACE_STRING?" +msgbox_save_list_file="Salva elenco: $REPLACE_STRING?" +msgbox_save_list="Vuoi salvare l'elenco?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transferisci nei campi di input Alt+Up" @@ -543,6 +558,7 @@ panel_mark_matches_small="Megjelölések" panel_clear_all_marks="Az összes jelölés törlése" panel_load_list="Lista betöltése" panel_save_list="Lista mentése" +panel_save_as="Mentés másként..." panel_export_to_bash="Exportálás Bash-be" panel_shift_lines="Sorok mozgatása" panel_use_list="Lista használata" @@ -568,6 +584,8 @@ tooltip_copy_columns="Oszlopok másolása a vágólapra" tooltip_column_highlight="Oszlop kiemelése: Be/Ki" tooltip_copy_marked_text="Kiemelt szöveg másolása" tooltip_display_statistics_columns="Statisztikai oszlopok mutatása/rejtése" +tooltip_new_list="Új lista" +tooltip_save="Lista mentés" ; List headers header_find_count="Keres számláló" @@ -648,7 +666,6 @@ status_no_matches_found_for="Nem található egyezőség '$REPLACE_STRING' szám status_actual_position="Aktuális pozíció $REPLACE_STRING" status_items_loaded_from_csv="$REPLACE_STRING elem betöltve CSV-ből." status_wrapped_position="Körbeért $REPLACE_STRING-nál" -status_deleted_fields="$REPLACE_STRING mező törölve." status_occurrences_marked="$REPLACE_STRING előfordulás megjelölve." status_items_copied_to_clipboard="$REPLACE_STRING elem másolva a vágólapra." status_no_matches_after_wrap_for="Nem található egyezőség '$REPLACE_STRING' számára a körbeérés után." @@ -662,6 +679,7 @@ msgbox_title_error="Hiba" msgbox_title_confirm="Megerősítés" msgbox_title_use_variables_syntax_error="Változók: Szintaxis Hiba" msgbox_title_use_variables_execution_error="Változók: Végrehajtási Hiba" +msgbox_title_save_list="Lista mentése" ; MessageBox Messages msgbox_failed_create_control="Nem sikerült létrehozni a vezérlőt azonosítóval: $REPLACE_STRING1, A GetLastError visszatért: $REPLACE_STRING2" @@ -671,6 +689,8 @@ msgbox_error_saving_settings="Hiba történt a beállítások mentése közben:< msgbox_use_variables_execution_error="Végrehajtás megszakadt a következő miatt:
$REPLACE_STRING" msgbox_confirm_delete_single="Biztosan törölni szeretné ezt a sort?" msgbox_confirm_delete_multiple="Biztosan törölni szeretné $REPLACE_STRING sorokat?" +msgbox_save_list_file="Lista mentése: $REPLACE_STRING?" +msgbox_save_list="Szeretné menteni a listát?" ; Context Menu ctxmenu_transfer_to_input_fields="&Átvitel a bemeneti mezőkbe Alt+Up" @@ -686,7 +706,7 @@ ctxmenu_disable="&Letilt Alt+D" [russian] -; Panel labels (visible text on the control elements) +; Panel labels panel_find_what="Найти:" panel_replace_with="Заменить на:" panel_match_whole_word_only="Только целые слова" @@ -715,6 +735,7 @@ panel_mark_matches_small="Выдел. соотв." panel_clear_all_marks="Стереть все пометки" panel_load_list="Загрузить список" panel_save_list="Сохранить список" +panel_save_as="Сохранить как..." panel_export_to_bash="Экспортировать в Bash" panel_shift_lines="Переместить строки" panel_use_list="Список" @@ -740,6 +761,8 @@ tooltip_copy_columns="Скопировать столбцы в буфер обм tooltip_column_highlight="Подсветка столбцов: вкл./выкл." tooltip_copy_marked_text="Скопировать выделенный текст" tooltip_display_statistics_columns="Показать/Скрыть столбцы статистики" +tooltip_new_list="Новый список" +tooltip_save="Сохранить список" ; List headers header_find_count="Найдено" @@ -785,7 +808,6 @@ status_replace_one_none_left="Заменено 1 раз. Больше не на status_add_values_or_find_directly="Добавьте значения в список или снимите флажок 'Список' для прямой замены." status_wrapped="Циклический поиск." status_no_matches_found="Не найдено." - status_no_matches_after_wrap="Не найдено после циклического поиска." status_add_values_or_mark_directly="Добавьте значения в список или снимите флажок 'Список' для прямой замены." status_no_text_to_copy="Ничего не выделено. Копирование невозможно." @@ -821,7 +843,6 @@ status_no_matches_found_for="Не найдено совпадений с '$REPLA status_actual_position="Актуальная позиция $REPLACE_STRING" status_items_loaded_from_csv="$REPLACE_STRING элеметов загружено из CSV-Файла." status_wrapped_position="Поиск остановлен на $REPLACE_STRING" -status_deleted_fields="Удалено $REPLACE_STRING полей." status_occurrences_marked="Отмечено $REPLACE_STRING совпадений." status_items_copied_to_clipboard="Скопировано $REPLACE_STRING элементов в буфер обмена." status_no_matches_after_wrap_for="Не найдено совпадений с '$REPLACE_STRING' после циклического поиска." @@ -835,6 +856,7 @@ msgbox_title_error="Ошибка" msgbox_title_confirm="Ок" msgbox_title_use_variables_syntax_error="Ошибка при использовании переменных: Ошибка кода." msgbox_title_use_variables_execution_error="Ошибка при использовании переменных: Ошибка исполнения." +msgbox_title_save_list="Сохранить список" ; MessageBox Messages msgbox_failed_create_control="Не удалось создать элемент управления с идентификатором: $REPLACE_STRING1, GetLastError-Сообщение: $REPLACE_STRING2" @@ -844,6 +866,8 @@ msgbox_error_saving_settings="Произошла ошибка при сохра msgbox_use_variables_execution_error="Выполнение приостановлено из-за сбоя выполнения в:
$REPLACE_STRING" msgbox_confirm_delete_single="Вы уверены, что хотите удалить эту строку?" msgbox_confirm_delete_multiple="Вы уверены, что хотите удалить строки $REPLACE_STRING?" +msgbox_save_list_file="Сохранить список: $REPLACE_STRING?" +msgbox_save_list="Хотите сохранить список?" ; Context Menu ctxmenu_transfer_to_input_fields="Переместить в поля ввода Alt+Up" @@ -859,7 +883,7 @@ ctxmenu_disable="Деактивировать Alt+D" [spanish] -; Panel labels (visible text on the control elements) +; Panel labels panel_find_what="Qué buscar:" panel_replace_with="Sustituir con:" panel_match_whole_word_only="Solo palabras completas" @@ -888,6 +912,7 @@ panel_mark_matches_small="Marcar todo" panel_clear_all_marks="Borrar marcas" panel_load_list="Cargar lista" panel_save_list="Guardar lista" +panel_save_as="Guardar como..." panel_export_to_bash="Exportar a Bash" panel_shift_lines="Mover líneas" panel_use_list="Usar lista" @@ -913,6 +938,8 @@ tooltip_copy_columns="Copiar columnas al portapapeles" tooltip_column_highlight="Resaltar columna: Activar/Desactivar" tooltip_copy_marked_text="Copiar el texto marcado" tooltip_display_statistics_columns="Mostrar/Ocultar columnas de estadísticas" +tooltip_new_list="Nueva lista" +tooltip_save="Guardar lista" ; List headers header_find_count="Conteo de hallazgos" @@ -992,7 +1019,6 @@ status_no_matches_found_for="No se encontraron coincidencias para '$REPLACE_STRI status_actual_position="Posición actual $REPLACE_STRING" status_items_loaded_from_csv="$REPLACE_STRING elementos cargados desde CSV." status_wrapped_position="Envuelto en $REPLACE_STRING" -status_deleted_fields="$REPLACE_STRING campos eliminados." status_occurrences_marked="$REPLACE_STRING ocurrencias fueron marcadas." status_items_copied_to_clipboard="$REPLACE_STRING elementos copiados al portapapeles." status_no_matches_after_wrap_for="No se encontraron coincidencias para '$REPLACE_STRING' después de envolver." @@ -1006,6 +1032,7 @@ msgbox_title_error="Error" msgbox_title_confirm="Confirmar" msgbox_title_use_variables_syntax_error="Usar Variables: Error de Sintaxis" msgbox_title_use_variables_execution_error="Usar Variables: Error de Ejecución" +msgbox_title_save_list="Guardar lista" ; MessageBox Messages msgbox_failed_create_control="Error al crear el control con ID: $REPLACE_STRING1, GetLastError retornó: $REPLACE_STRING2" @@ -1015,6 +1042,8 @@ msgbox_error_saving_settings="Ocurrió un error al guardar la configuración:
> 2); + } + + return combinedHash; } #pragma endregion @@ -2400,16 +2419,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l case IDC_SAVE_AS_BUTTON: case IDC_SAVE_TO_CSV_BUTTON: { - std::wstring csvDescription = getLangStr(L"filetype_csv"); // "CSV Files (*.csv)" - std::wstring allFilesDescription = getLangStr(L"filetype_all_files"); // "All Files (*.*)" - - std::vector> filters = { - {csvDescription, L"*.csv"}, - {allFilesDescription, L"*.*"} - }; - - std::wstring dialogTitle = getLangStr(L"panel_save_list"); - std::wstring filePath = openFileDialog(true, filters, dialogTitle.c_str(), OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, L"csv"); + // Use the promptSaveListToCsv function to get the file path + std::wstring filePath = promptSaveListToCsv(); if (!filePath.empty()) { saveListToCsv(filePath, replaceListData); @@ -2422,6 +2433,14 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (!listFilePath.empty()) { saveListToCsv(listFilePath, replaceListData); } + else { + // If no file path is set, prompt the user to select a file path + std::wstring filePath = promptSaveListToCsv(); + + if (!filePath.empty()) { + saveListToCsv(filePath, replaceListData); + } + } } break; @@ -2437,7 +2456,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l }; std::wstring dialogTitle = getLangStr(L"panel_load_list"); - std::wstring filePath = openFileDialog(false, filters, dialogTitle.c_str(), OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST, L"csv"); + std::wstring filePath = openFileDialog(false, filters, dialogTitle.c_str(), OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST, L"csv", L""); if (!filePath.empty()) { loadListFromCsv(filePath); @@ -2474,7 +2493,13 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l }; std::wstring dialogTitle = getLangStr(L"panel_export_to_bash"); - std::wstring filePath = openFileDialog(true, filters, dialogTitle.c_str(), OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, L"sh"); + + // Set a default filename if none is provided + static int scriptCounter = 1; + std::wstring defaultFileName = L"Replace_Script_" + std::to_wstring(scriptCounter++) + L".sh"; + + // Open the file dialog with the default filename for bash scripts + std::wstring filePath = openFileDialog(true, filters, dialogTitle.c_str(), OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, L"sh", defaultFileName); if (!filePath.empty()) { exportToBashScript(filePath); @@ -4421,7 +4446,14 @@ bool MultiReplace::confirmColumnDeletion() { // Now columnDelimiterData should be populated with the parsed column data size_t columnCount = columnDelimiterData.columns.size(); std::wstring confirmMessage = getLangStr(L"msgbox_confirm_delete_columns", { std::to_wstring(columnCount) }); - int msgboxID = MessageBox(NULL, confirmMessage.c_str(), getLangStr(L"msgbox_title_confirm").c_str(), MB_ICONQUESTION | MB_YESNO); + + // Display a message box with Yes/No options and a question mark icon + int msgboxID = MessageBox( + _hSelf, + confirmMessage.c_str(), + getLangStr(L"msgbox_title_confirm").c_str(), + MB_ICONQUESTION | MB_YESNO + ); return (msgboxID == IDYES); // Return true if user confirmed, else false } @@ -5814,9 +5846,16 @@ void MultiReplace::updateHeaderSortDirection() { void MultiReplace::showStatusMessage(const std::wstring& messageText, COLORREF color) { - const size_t MAX_DISPLAY_LENGTH = 120; // Maximum length of the message to be displayed - // Cut the message and add "..." if it's too long - std::wstring strMessage = messageText; + const size_t MAX_DISPLAY_LENGTH = 120; // Maximum length of the message to be displayed + + // Filter out non-printable characters while keeping all printable Unicode characters + std::wstring strMessage; + for (wchar_t ch : messageText) { + if (iswprint(ch)) { + strMessage += ch; + } + } + if (strMessage.size() > MAX_DISPLAY_LENGTH) { strMessage = strMessage.substr(0, MAX_DISPLAY_LENGTH - 3) + L"..."; } @@ -5836,18 +5875,7 @@ void MultiReplace::showStatusMessage(const std::wstring& messageText, COLORREF c UpdateWindow(GetParent(hStatusMessage)); } -void MultiReplace::showListFilePath() { - // Define the path to be displayed - //std::wstring path = L"C:\\Users\\ExampleUser\\Documents\\Projects\\TestProject\\SubFolder\\AnotherSubFolder\\Users\\ExampleUser\\Documents\\Projects\\TestProject\\SubFolder\\AnotherSubFolderThisIsAReallyLongFileNameForTestingPurposes.txt"; - - std::wstring path = listFilePath; - - // Obtain handle and device context for the path display control - HWND hPathDisplay = GetDlgItem(_hSelf, IDC_PATH_DISPLAY); - HDC hDC = GetDC(hPathDisplay); - HFONT hFont = (HFONT)SendMessage(hPathDisplay, WM_GETFONT, 0, 0); - SelectObject(hDC, hFont); - +std::wstring MultiReplace::getShortenedFilePath(const std::wstring& path, int maxLength, HDC hDC) { // Calculate the width of each character in the path and the width of the dots ("...") double dotWidth = 0.0; SIZE charSize; @@ -5861,18 +5889,14 @@ void MultiReplace::showListFilePath() { } } - ReleaseDC(hPathDisplay, hDC); - - RECT rcPathDisplay; - GetClientRect(hPathDisplay, &rcPathDisplay); - int pathDisplayWidth = rcPathDisplay.right - rcPathDisplay.left; double totalDotsWidth = dotWidth * 3; // Width for "..." + // Split the directory and filename size_t lastSlashPos = path.find_last_of(L"\\/"); std::wstring directoryPart = (lastSlashPos != std::wstring::npos) ? path.substr(0, lastSlashPos + 1) : L""; std::wstring fileName = (lastSlashPos != std::wstring::npos) ? path.substr(lastSlashPos + 1) : path; - // Calculate the width of directory and file name parts + // Calculate widths for directory and file name double directoryWidth = 0.0, fileNameWidth = 0.0; for (size_t i = 0; i < directoryPart.size(); ++i) { @@ -5886,10 +5910,10 @@ void MultiReplace::showListFilePath() { std::wstring displayPath; double currentWidth = 0.0; - // Check if file name needs to be shortened - if (fileNameWidth + totalDotsWidth > pathDisplayWidth) { + // Shorten the file name if necessary + if (fileNameWidth + totalDotsWidth > maxLength) { for (size_t i = directoryPart.size(); i < path.size(); ++i) { - if (currentWidth + characterWidths[i] + totalDotsWidth > pathDisplayWidth) { + if (currentWidth + characterWidths[i] + totalDotsWidth > maxLength) { break; } displayPath += path[i]; @@ -5897,10 +5921,10 @@ void MultiReplace::showListFilePath() { } displayPath += L"..."; } - // Check if directory part needs to be shortened - else if (directoryWidth + fileNameWidth > pathDisplayWidth) { + // Shorten the directory part if necessary + else if (directoryWidth + fileNameWidth > maxLength) { for (size_t i = 0; i < directoryPart.size(); ++i) { - if (currentWidth + characterWidths[i] + totalDotsWidth + fileNameWidth > pathDisplayWidth) { + if (currentWidth + characterWidths[i] + totalDotsWidth + fileNameWidth > maxLength) { break; } displayPath += directoryPart[i]; @@ -5913,7 +5937,30 @@ void MultiReplace::showListFilePath() { displayPath = path; // No shortening needed } - SetWindowTextW(hPathDisplay, displayPath.c_str()); + return displayPath; +} + +void MultiReplace::showListFilePath() { + std::wstring path = listFilePath; + + // Obtain handle and device context for the path display control + HWND hPathDisplay = GetDlgItem(_hSelf, IDC_PATH_DISPLAY); + HDC hDC = GetDC(hPathDisplay); + HFONT hFont = (HFONT)SendMessage(hPathDisplay, WM_GETFONT, 0, 0); + SelectObject(hDC, hFont); + + // Get display width for IDC_PATH_DISPLAY + RECT rcPathDisplay; + GetClientRect(hPathDisplay, &rcPathDisplay); + int pathDisplayWidth = rcPathDisplay.right - rcPathDisplay.left; + + // Call the new function to get the shortened file path + std::wstring shortenedPath = getShortenedFilePath(path, pathDisplayWidth, hDC); + + // Display the shortened path + SetWindowTextW(hPathDisplay, shortenedPath.c_str()); + + ReleaseDC(hPathDisplay, hDC); // Update the parent window area where the control resides RECT rect; @@ -6184,10 +6231,15 @@ std::wstring MultiReplace::trim(const std::wstring& str) { #pragma region FileOperations -std::wstring MultiReplace::openFileDialog(bool saveFile, const std::vector>& filters, const WCHAR* title, DWORD flags, const std::wstring& fileExtension) { +std::wstring MultiReplace::openFileDialog(bool saveFile, const std::vector>& filters, const WCHAR* title, DWORD flags, const std::wstring& fileExtension, const std::wstring& defaultFilePath) { OPENFILENAME ofn = { 0 }; WCHAR szFile[MAX_PATH] = { 0 }; + // Safely copy the default file path into the buffer and ensure null-termination + if (!defaultFilePath.empty()) { + wcsncpy_s(szFile, defaultFilePath.c_str(), MAX_PATH); + } + std::vector filter = createFilterString(filters); ofn.lStructSize = sizeof(OPENFILENAME); @@ -6202,6 +6254,7 @@ std::wstring MultiReplace::openFileDialog(bool saveFile, const std::vector> filters = { + {csvDescription, L"*.csv"}, + {allFilesDescription, L"*.*"} + }; + + std::wstring dialogTitle = getLangStr(L"panel_save_list"); + std::wstring defaultFileName; + + if (!listFilePath.empty()) { + // If a file path already exists, use its directory and filename + defaultFileName = listFilePath; + } + else { + // If no file path is set, provide a default file name with a sequential number + static int fileCounter = 1; + defaultFileName = L"Replace_List_" + std::to_wstring(fileCounter++) + L".csv"; + } + + // Call openFileDialog with the default file path and name + std::wstring filePath = openFileDialog( + true, + filters, + dialogTitle.c_str(), + OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT, + L"csv", + defaultFileName + ); + + return filePath; +} + bool MultiReplace::saveListToCsvSilent(const std::wstring& filePath, const std::vector& list) { std::ofstream outFile(filePath); @@ -6254,11 +6342,70 @@ void MultiReplace::saveListToCsv(const std::wstring& filePath, const std::vector // Enable the ListView accordingly SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, BST_CHECKED, 0); EnableWindow(_replaceListView, TRUE); + + // Update the file path and original hash after a successful save + listFilePath = filePath; + originalListHash = computeListHash(); + + // Update the displayed file path below the list + showListFilePath(); } -void MultiReplace::saveListToCurrentFile() { - // Dummy-Implementierung - showStatusMessage(getLangStr(L"status_save_to_file"), RGB(0, 128, 0)); +int MultiReplace::checkForUnsavedChanges() { + std::size_t currentListHash = computeListHash(); + + if (currentListHash != originalListHash) { + HDC hDC = GetDC(_hSelf); + + // Define a reasonable width for the MessageBox path display + int maxLengthForMessageBox = 300; + + std::wstring message; + if (!listFilePath.empty()) { + // Get the shortened file path and build the message + std::wstring shortenedFilePath = getShortenedFilePath(listFilePath, maxLengthForMessageBox, hDC); + message = getLangStr(L"msgbox_save_list_file", { shortenedFilePath }); + } + else { + // If no file is associated, use the alternative message + message = getLangStr(L"msgbox_save_list"); + } + ReleaseDC(_hSelf, hDC); + + // Show the MessageBox with the appropriate message + int result = MessageBox( + _hSelf, + message.c_str(), + getLangStr(L"msgbox_title_save_list").c_str(), + MB_YESNOCANCEL | MB_ICONQUESTION + ); + + if (result == IDYES) { + if (!listFilePath.empty()) { + saveListToCsv(listFilePath, replaceListData); + return IDYES; // Proceed if saved successfully + } + else { + std::wstring filePath = promptSaveListToCsv(); + + if (!filePath.empty()) { + saveListToCsv(filePath, replaceListData); + return IDYES; // Proceed with clear after save + } + else { + return IDCANCEL; // Cancel if no file was selected + } + } + } + else if (result == IDNO) { + return IDNO; // Allow proceeding without saving + } + else if (result == IDCANCEL) { + return IDCANCEL; // Cancel the action + } + } + + return IDYES; // No unsaved changes, allow proceeding } void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vector& list) { @@ -6329,6 +6476,8 @@ void MultiReplace::loadListFromCsv(const std::wstring& filePath) { listFilePath = filePath; // Display the path below the list showListFilePath(); + // Calculate the original list hash after loading + originalListHash = computeListHash(); } catch (const CsvLoadException& ex) { // Resolve the error key to a localized string when displaying the message diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 2a2e1df..14acb8e 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -67,6 +67,21 @@ struct ReplaceItemData } }; +// Hash function for ReplaceItemData +struct ReplaceItemDataHasher { + std::size_t operator()(const ReplaceItemData& item) const { + std::size_t hash = std::hash{}(item.isEnabled); + hash ^= std::hash{}(item.findText) << 1; + hash ^= std::hash{}(item.replaceText) << 1; + hash ^= std::hash{}(item.wholeWord) << 1; + hash ^= std::hash{}(item.matchCase) << 1; + hash ^= std::hash{}(item.useVariables) << 1; + hash ^= std::hash{}(item.extended) << 1; + hash ^= std::hash{}(item.regex) << 1; + return hash; + } +}; + struct WindowSettings { int posX; int posY; @@ -396,6 +411,8 @@ class MultiReplace : public StaticDialog // List related std::wstring listFilePath = L""; //to store the file path of loaded list + const std::size_t golden_ratio_constant = 0x9e3779b9; // 2^32 / φ /uused for Hashing + std::size_t originalListHash = 0; // GUI control-related constants const std::vector selectionRadioDisabledButtons = { @@ -446,6 +463,7 @@ class MultiReplace : public StaticDialog void updateCountColumns(size_t itemIndex, int findCount, int replaceCount = -1); void resizeCountColumns(); void clearList(); + std::size_t computeListHash(); //Contextmenu void toggleBooleanAt(int itemIndex, int Column); @@ -541,6 +559,7 @@ class MultiReplace : public StaticDialog void updateHeaderSortDirection(); void showStatusMessage(const std::wstring& messageText, COLORREF color); void calculateCharacterWidths(); + std::wstring getShortenedFilePath(const std::wstring& path, int maxLength, HDC hDC); void showListFilePath(); void displayResultCentered(size_t posStart, size_t posEnd, bool isDownwards); std::wstring getSelectedText(); @@ -548,20 +567,20 @@ class MultiReplace : public StaticDialog std::string getEOLStyle(); sptr_t send(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0, bool useDirect = true); bool normalizeAndValidateNumber(std::string& str); - std::vector MultiReplace::createFilterString(const std::vector>& filters); + std::vector createFilterString(const std::vector>& filters); //StringHandling std::wstring stringToWString(const std::string& encodedInput) const; std::string wstringToString(const std::wstring& input) const; - std::wstring MultiReplace::utf8ToWString(const char* cstr) const; + std::wstring utf8ToWString(const char* cstr) const; std::string utf8ToCodepage(const std::string& utf8Str, int codepage) const; std::wstring trim(const std::wstring& str); //FileOperations - std::wstring MultiReplace::openFileDialog(bool saveFile, const std::vector>& filters, const WCHAR* title, DWORD flags, const std::wstring& fileExtension); + std::wstring promptSaveListToCsv(); + std::wstring openFileDialog(bool saveFile, const std::vector>& filters, const WCHAR* title, DWORD flags, const std::wstring& fileExtension, const std::wstring& defaultFilePath); bool saveListToCsvSilent(const std::wstring& filePath, const std::vector& list); void saveListToCsv(const std::wstring& filePath, const std::vector& list); - void saveListToCurrentFile(); void loadListFromCsvSilent(const std::wstring& filePath, std::vector& list); std::wstring escapeCsvValue(const std::wstring& value); std::wstring unescapeCsvValue(const std::wstring& value); @@ -577,6 +596,7 @@ class MultiReplace : public StaticDialog std::pair generateConfigFilePaths(); void saveSettingsToIni(const std::wstring& iniFilePath); void saveSettings(); + int checkForUnsavedChanges(); void loadSettingsFromIni(const std::wstring& iniFilePath); void loadSettings(); void loadUIConfigFromIni(); diff --git a/src/language_mapping.cpp b/src/language_mapping.cpp index 61cc121..3a9472b 100644 --- a/src/language_mapping.cpp +++ b/src/language_mapping.cpp @@ -56,6 +56,8 @@ std::unordered_map languageMap = { { L"tooltip_column_highlight", L"Column highlight: On/Off" }, { L"tooltip_copy_marked_text", L"Copy Marked Text" }, { L"tooltip_display_statistics_columns", L"Show/Hide Statistics Columns" }, +{ L"tooltip_new_list", L"New List" }, +{ L"tooltip_save", L"Save List" }, // header entries { L"header_find_count", L"Find Count" }, @@ -131,7 +133,6 @@ std::unordered_map languageMap = { { L"status_actual_position", L"Actual Position $REPLACE_STRING" }, { L"status_items_loaded_from_csv", L"$REPLACE_STRING items loaded from CSV." }, { L"status_wrapped_position", L"Wrapped at $REPLACE_STRING" }, -{ L"status_deleted_fields", L"Deleted $REPLACE_STRING fields." }, { L"status_occurrences_marked", L"$REPLACE_STRING occurrences were marked." }, { L"status_items_copied_to_clipboard", L"$REPLACE_STRING items copied into Clipboard." }, { L"status_no_matches_after_wrap_for", L"No matches found for '$REPLACE_STRING' after wrap." }, @@ -148,6 +149,7 @@ std::unordered_map languageMap = { { L"msgbox_title_confirm", L"Confirm" }, { L"msgbox_title_use_variables_syntax_error", L"Use Variables: Syntax Error" }, { L"msgbox_title_use_variables_execution_error", L"Use Variables: Execution Error" }, +{ L"msgbox_title_save_list", L"Save list" }, // MessageBox Messages { L"msgbox_failed_create_control", L"Failed to create control with ID: $REPLACE_STRING1, GetLastError returned: $REPLACE_STRING2" }, @@ -157,6 +159,8 @@ std::unordered_map languageMap = { { L"msgbox_use_variables_execution_error", L"Execution halted due to execution failure in:
$REPLACE_STRING" }, { L"msgbox_confirm_delete_single", L"Are you sure you want to delete this line?" }, { L"msgbox_confirm_delete_multiple", L"Are you sure you want to delete $REPLACE_STRING lines?" }, +{ L"msgbox_save_list_file", L"Save list: $REPLACE_STRING?" }, +{ L"msgbox_save_list", L"Do you want to save the list?" }, // Context Menu Strings { L"ctxmenu_transfer_to_input_fields", L"&Transfer to Input Fields\tAlt+Up" }, From a14c4b81572bb58e8d80187cea7173536c889c03 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 23 Sep 2024 11:06:41 +0200 Subject: [PATCH 07/51] save originalListHash in INI --- src/MultiReplacePanel.cpp | 33 ++++++++++++++++++++++++--------- src/MultiReplacePanel.h | 3 ++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index b667f6c..ab4a87d 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -1072,17 +1072,18 @@ void MultiReplace::clearList() { originalListHash = 0; } -std::size_t MultiReplace::computeListHash() { +std::size_t MultiReplace::computeListHash(const std::vector& list) { std::size_t combinedHash = 0; ReplaceItemDataHasher hasher; - for (const auto& item : replaceListData) { + for (const auto& item : list) { combinedHash ^= hasher(item) + golden_ratio_constant + (combinedHash << 6) + (combinedHash >> 2); } return combinedHash; } + #pragma endregion @@ -6345,14 +6346,14 @@ void MultiReplace::saveListToCsv(const std::wstring& filePath, const std::vector // Update the file path and original hash after a successful save listFilePath = filePath; - originalListHash = computeListHash(); + originalListHash = computeListHash(list); // Update the displayed file path below the list showListFilePath(); } int MultiReplace::checkForUnsavedChanges() { - std::size_t currentListHash = computeListHash(); + std::size_t currentListHash = computeListHash(replaceListData); if (currentListHash != originalListHash) { HDC hDC = GetDC(_hSelf); @@ -6477,7 +6478,7 @@ void MultiReplace::loadListFromCsv(const std::wstring& filePath) { // Display the path below the list showListFilePath(); // Calculate the original list hash after loading - originalListHash = computeListHash(); + originalListHash = computeListHash(replaceListData); } catch (const CsvLoadException& ex) { // Resolve the error key to a localized string when displaying the message @@ -6864,9 +6865,10 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { outFile << wstringToString(L"QuoteChar=" + quoteChar + L"\n"); outFile << wstringToString(L"HeaderLines=" + headerLines + L"\n"); - // Save the list file path - outFile << wstringToString(L"[Paths]\n"); + // Save the list file path and original hash + outFile << wstringToString(L"[File]\n"); outFile << wstringToString(L"ListFilePath=" + listFilePath + L"\n"); + outFile << wstringToString(L"OriginalListHash=" + std::to_wstring(originalListHash) + L"\n"); // Convert and Store "Find what" history LRESULT findWhatCount = SendMessage(GetDlgItem(_hSelf, IDC_FIND_EDIT), CB_GETCOUNT, 0, 0); @@ -6989,8 +6991,9 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { CSVheaderLinesCount = readIntFromIniFile(iniFilePath, L"Scope", L"HeaderLines", 1); - // Load the list file path from the INI file - listFilePath = readStringFromIniFile(iniFilePath, L"Paths", L"ListFilePath", L""); + // Load file path and original hash from the INI file + listFilePath = readStringFromIniFile(iniFilePath, L"File", L"ListFilePath", L""); + originalListHash = readSizeTFromIniFile(iniFilePath, L"File", L"OriginalListHash", 0); showListFilePath(); // Adjusting UI elements based on the selected scope @@ -7115,6 +7118,18 @@ int MultiReplace::readIntFromIniFile(const std::wstring& iniFilePath, const std: return ::GetPrivateProfileIntW(section.c_str(), key.c_str(), defaultValue, iniFilePath.c_str()); } +std::size_t MultiReplace::readSizeTFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, std::size_t defaultValue) { + WCHAR buffer[256] = { 0 }; + GetPrivateProfileStringW(section.c_str(), key.c_str(), std::to_wstring(defaultValue).c_str(), buffer, sizeof(buffer) / sizeof(WCHAR), iniFilePath.c_str()); + + try { + return std::stoull(buffer); // Convert the string to a size_t + } + catch (...) { + return defaultValue; // If conversion fails, return the default value + } +} + BYTE MultiReplace::readByteFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, BYTE defaultValue) { int intValue = ::GetPrivateProfileIntW(section.c_str(), key.c_str(), defaultValue, iniFilePath.c_str()); return static_cast(intValue); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 14acb8e..4a6d80f 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -463,7 +463,7 @@ class MultiReplace : public StaticDialog void updateCountColumns(size_t itemIndex, int findCount, int replaceCount = -1); void resizeCountColumns(); void clearList(); - std::size_t computeListHash(); + std::size_t computeListHash(const std::vector& list); //Contextmenu void toggleBooleanAt(int itemIndex, int Column); @@ -603,6 +603,7 @@ class MultiReplace : public StaticDialog std::wstring readStringFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, const std::wstring& defaultValue); bool readBoolFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, bool defaultValue); int readIntFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, int defaultValue); + std::size_t readSizeTFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, std::size_t defaultValue); void setTextInDialogItem(HWND hDlg, int itemID, const std::wstring& text); // Language From 92dd948f481da04d92791878705609589080c4c4 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 23 Sep 2024 12:23:43 +0200 Subject: [PATCH 08/51] casting to size_t --- src/MultiReplacePanel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index ab4a87d..98d82a0 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -7123,7 +7123,7 @@ std::size_t MultiReplace::readSizeTFromIniFile(const std::wstring& iniFilePath, GetPrivateProfileStringW(section.c_str(), key.c_str(), std::to_wstring(defaultValue).c_str(), buffer, sizeof(buffer) / sizeof(WCHAR), iniFilePath.c_str()); try { - return std::stoull(buffer); // Convert the string to a size_t + return static_cast(std::stoull(buffer)); // Convert and cast directly to size_t } catch (...) { return defaultValue; // If conversion fails, return the default value From 1a114924452c7c48f7d2c6c4ebca385b178feda5 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Tue, 24 Sep 2024 16:54:43 +0200 Subject: [PATCH 09/51] added check for file changes at stratup --- src/language_mapping.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/language_mapping.cpp b/src/language_mapping.cpp index 3a9472b..1da5480 100644 --- a/src/language_mapping.cpp +++ b/src/language_mapping.cpp @@ -121,6 +121,7 @@ std::unordered_map languageMap = { { L"status_unable_to_open_file", L"Failed to open the file." }, { L"status_invalid_column_count", L"Invalid number of columns in CSV file." }, { L"status_invalid_data_in_columns", L"Invalid data found in CSV columns." }, +{ L"status_no_find_replace_list_input", L"No 'Find' or 'Replace' string provided. Please enter a value." }, // Dynamic Status message entries { L"status_rows_shifted", L"$REPLACE_STRING rows successfully shifted." }, From 105bfde8ec6451bfa438a1e883b6ecb4d985fb7c Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Tue, 24 Sep 2024 16:55:54 +0200 Subject: [PATCH 10/51] added check for file changes at startup --- languages.ini | 48 +++++++++++++++--------- src/MultiReplacePanel.cpp | 78 +++++++++++++++++++++++++++++++-------- src/MultiReplacePanel.h | 3 +- src/language_mapping.cpp | 13 ++++--- 4 files changed, 102 insertions(+), 40 deletions(-) diff --git a/languages.ini b/languages.ini index b8965ae..f3407a0 100644 --- a/languages.ini +++ b/languages.ini @@ -117,9 +117,8 @@ status_unable_to_save_file="Error: Unable to open or write to file." status_saved_items_to_csv="$REPLACE_STRING items saved to CSV." status_no_valid_items_in_csv="No valid items found in the CSV file." status_list_exported_to_bash="List exported to BASH script." -status_unable_to_open_file="Failed to open the file." -status_invalid_column_count="Invalid number of columns in CSV file." -status_invalid_data_in_columns="Invalid data found in CSV columns." +status_invalid_column_count="File not loaded! Invalid number of columns in CSV file." +status_invalid_data_in_columns="File not loaded! Invalid data found in CSV columns." status_no_find_replace_list_input="No 'Find' or 'Replace' string provided. Please enter a value." status_found_in_list="Entry found in the list." status_not_found_in_list="No entry found in the list based on input fields." @@ -143,6 +142,7 @@ status_deleted_fields_count="Deleted $REPLACE_STRING fields." status_wrapped_find="Wrapped '$REPLACE_STRING1'. Position: $REPLACE_STRING2" status_wrapped_no_find="Wrapped Position: $REPLACE_STRING" status_line_and_column_position=" (Line: $REPLACE_STRING1, Column: $REPLACE_STRING2)" +status_unable_to_open_file="Failed to open the file: $REPLACE_STRING" ; MessageBox Titles msgbox_title_error="Error" @@ -150,6 +150,7 @@ msgbox_title_confirm="Confirm" msgbox_title_use_variables_syntax_error="Use Variables: Syntax Error" msgbox_title_use_variables_execution_error="Use Variables: Execution Error" msgbox_title_save_list="Save list" +msgbox_title_reload="Reload" ; MessageBox Messages msgbox_failed_create_control="Failed to create control with ID: $REPLACE_STRING1, GetLastError returned: $REPLACE_STRING2" @@ -161,6 +162,7 @@ msgbox_confirm_delete_single="Are you sure you want to delete this line?" msgbox_confirm_delete_multiple="Are you sure you want to delete $REPLACE_STRING lines?" msgbox_save_list_file="Save list: $REPLACE_STRING?" msgbox_save_list="Do you want to save the list?" +msgbox_file_modified_prompt="$REPLACE_STRING

The file has been modified by another program.
Do you want to load the changes and lose unsaved modifications?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transfer to Input Fields Alt+Up" @@ -293,9 +295,8 @@ status_unable_to_save_file=""Fehler: Datei kann nicht geöffnet oder beschrieben status_saved_items_to_csv="$REPLACE_STRING Einträge in die CSV-Datei gespeichert." status_no_valid_items_in_csv="Keine gültigen Einträge in der CSV-Datei gefunden." status_list_exported_to_bash="Liste in BASH-Skript exportiert." -status_unable_to_open_file="Fehler beim Öffnen der Datei." -status_invalid_column_count="Ungültige Anzahl von Spalten in der CSV-Datei." -status_invalid_data_in_columns="Ungültige Daten in CSV-Spalten gefunden." +status_invalid_column_count="Datei nicht geladen! Ungültige Anzahl von Spalten in der CSV-Datei." +status_invalid_data_in_columns="Datei nicht geladen! Ungültige Daten in CSV-Spalten gefunden." status_no_find_replace_list_input="Kein 'Suchen' oder 'Ersetzen' Text angegeben. Bitte einen Wert eingeben." status_found_in_list="Eintrag in der Liste gefunden." status_not_found_in_list="Kein Eintrag in der Liste auf Basis der Eingabefelder gefunden." @@ -319,6 +320,7 @@ status_no_matches_found_for_simple="Keine Übereinstimmungen gefunden für '$REP status_deleted_fields_count="$REPLACE_STRING Felder gelöscht." status_wrapped_find="Umbruch bei '$REPLACE_STRING1'. Position: $REPLACE_STRING2" status_line_and_column_position=" (Zeile: $REPLACE_STRING1, Spalte: $REPLACE_STRING2)" +status_unable_to_open_file="Fehler beim Öffnen der Datei: $REPLACE_STRING" ; MessageBox Titles msgbox_title_error="Fehler" @@ -326,6 +328,7 @@ msgbox_title_confirm="Bestätigen" msgbox_title_use_variables_syntax_error="Syntaxfehler: Variablen benutzen" msgbox_title_use_variables_execution_error="Ausführungsfehler: Variablen benutzen" msgbox_title_save_list="Liste speichern" +msgbox_title_reload="Erneut laden" ; MessageBox Messages msgbox_failed_create_control="Erstellung des Steuerelements mit ID: $REPLACE_STRING1 fehlgeschlagen, GetLastError zurückgegeben: $REPLACE_STRING2" @@ -337,6 +340,7 @@ msgbox_confirm_delete_single="Sind Sie sicher, dass Sie diese Zeile löschen mö msgbox_confirm_delete_multiple="Sind Sie sicher, dass Sie $REPLACE_STRING Zeilen löschen möchten?" msgbox_save_list_file="Liste speichern: $REPLACE_STRING?" msgbox_save_list="Möchten Sie die Liste speichern?" +msgbox_file_modified_prompt="$REPLACE_STRING

Die Datei wurde von einem anderen Programm geändert.
Möchten Sie die Änderungen laden und ungespeicherte Änderungen verlieren?" ; Context Menu ctxmenu_transfer_to_input_fields="&In Eingabefelder übertragen Alt+Hoch" @@ -470,9 +474,8 @@ status_unable_to_save_file="Errore: Impossibile aprire o scrivere il file." status_saved_items_to_csv="$REPLACE_STRING voci salvate nel CSV." status_no_valid_items_in_csv="Nessuna voce valida trovata nel file CSV." status_list_exported_to_bash="Elenco esportato in uno script BASH." -status_unable_to_open_file="Errore nell'apertura del file." -status_invalid_column_count="Numero di colonne non valido nel file CSV." -status_invalid_data_in_columns="Dati non validi nelle colonne CSV." +status_invalid_column_count="File non caricato! Numero di colonne non valido nel file CSV." +status_invalid_data_in_columns="File non caricato! Dati non validi nelle colonne CSV." status_no_find_replace_list_input="Nessuna stringa per 'Trova' o 'Sostituisci' fornita. Inserire un valore." status_found_in_list="Voce trovata nell'elenco." status_not_found_in_list="Nessuna voce trovata nell'elenco in base ai campi di input." @@ -496,6 +499,7 @@ status_deleted_fields_count="Eliminati $REPLACE_STRING campi." status_wrapped_find="Wrapped '$REPLACE_STRING1'. Posizione: $REPLACE_STRING2" status_wrapped_no_find="Posizione Wrapped: $REPLACE_STRING" status_line_and_column_position=" (Riga: $REPLACE_STRING1, Colonna: $REPLACE_STRING2)" +status_unable_to_open_file="Errore nell'apertura del file:: $REPLACE_STRING" ; MessageBox Titles msgbox_title_error="Errore" @@ -503,6 +507,7 @@ msgbox_title_confirm="Conferma" msgbox_title_use_variables_syntax_error="Usa Variabili: Errore di sintassi" msgbox_title_use_variables_execution_error="Usa Variabili: Errore di esecuzione" msgbox_title_save_list="Salva elenco" +msgbox_title_reload="Ricarica" ; MessageBox Messages msgbox_failed_create_control="Creazione del controllo non andato a buon fine con ID: $REPLACE_STRING1, GetLastError returned: $REPLACE_STRING2" @@ -514,6 +519,7 @@ msgbox_confirm_delete_single="Vuoi cancellare questa riga?" msgbox_confirm_delete_multiple="Vuoi cancellare la riga $REPLACE_STRING?" msgbox_save_list_file="Salva elenco: $REPLACE_STRING?" msgbox_save_list="Vuoi salvare l'elenco?" +msgbox_file_modified_prompt="$REPLACE_STRING

Il file è stato modificato da un altro programma.
Vuoi caricare le modifiche e perdere le modifiche non salvate?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transferisci nei campi di input Alt+Up" @@ -647,9 +653,8 @@ status_unable_to_save_file="Hiba: Nem sikerült a fájlt megnyitni vagy írni." status_saved_items_to_csv="$REPLACE_STRING elem CSV fájlba mentve." status_no_valid_items_in_csv="Nem található érvényes elem a CSV fájlban." status_list_exported_to_bash="A lista exportálva BASH scriptbe." -status_unable_to_open_file="Nem sikerült megnyitni a fájlt." -status_invalid_column_count="Érvénytelen oszlopszám a CSV fájlban." -status_invalid_data_in_columns="Érvénytelen adatok találhatók a CSV oszlopokban." +status_invalid_column_count="Fájl nem töltődött be! Érvénytelen oszlopszám a CSV fájlban." +status_invalid_data_in_columns="Fájl nem töltődött be! Érvénytelen adatok találhatók a CSV oszlopokban." status_no_find_replace_list_input="Nincs 'Keresés' vagy 'Csere' szöveg megadva. Érték megadása szükséges." status_found_in_list="Bejegyzés megtalálva a listában." status_not_found_in_list="Az input mezők alapján nem található bejegyzés a listában." @@ -673,6 +678,7 @@ status_no_matches_found_for_simple="Nem található egyezőség '$REPLACE_STRING status_deleted_fields_count="$REPLACE_STRING mező törölve." status_wrapped_find="Körbeért '$REPLACE_STRING1'. Pozíció: $REPLACE_STRING2" status_line_and_column_position=" (Sor: $REPLACE_STRING1, Oszlop: $REPLACE_STRING2)" +status_unable_to_open_file="Nem sikerült megnyitni a fájlt: $REPLACE_STRING" ; MessageBox Titles msgbox_title_error="Hiba" @@ -680,6 +686,7 @@ msgbox_title_confirm="Megerősítés" msgbox_title_use_variables_syntax_error="Változók: Szintaxis Hiba" msgbox_title_use_variables_execution_error="Változók: Végrehajtási Hiba" msgbox_title_save_list="Lista mentése" +msgbox_title_reload="Újratöltés" ; MessageBox Messages msgbox_failed_create_control="Nem sikerült létrehozni a vezérlőt azonosítóval: $REPLACE_STRING1, A GetLastError visszatért: $REPLACE_STRING2" @@ -691,6 +698,7 @@ msgbox_confirm_delete_single="Biztosan törölni szeretné ezt a sort?" msgbox_confirm_delete_multiple="Biztosan törölni szeretné $REPLACE_STRING sorokat?" msgbox_save_list_file="Lista mentése: $REPLACE_STRING?" msgbox_save_list="Szeretné menteni a listát?" +msgbox_file_modified_prompt="$REPLACE_STRING

A fájlt egy másik program módosította.
Szeretné betölteni a változtatásokat és elveszíteni a nem mentett módosításokat?" ; Context Menu ctxmenu_transfer_to_input_fields="&Átvitel a bemeneti mezőkbe Alt+Up" @@ -824,9 +832,8 @@ status_unable_to_save_file="Ошибка: Невозможно открыть и status_saved_items_to_csv="$REPLACE_STRING элементы, сохраненные в CSV-файле." status_no_valid_items_in_csv="Не найдено допустимых элеметнов в CSV-файле." status_list_exported_to_bash="Список сохранен как BASH-Скрипт." -status_unable_to_open_file="Невозиожно открыть файл." -status_invalid_column_count="Недопустимое колличество столбцов." -status_invalid_data_in_columns="Недопустимый формат даннйх в CSV-файле." +status_invalid_column_count="Файл не загружен! Недопустимое количество столбцов в CSV файле." +status_invalid_data_in_columns="Файл не загружен! Недопустимый формат даннйх в CSV-файле." status_no_find_replace_list_input="Поля 'Найти' или 'Заменить' не содержат значений. Пожалуйста внесите значения." status_found_in_list="Значение найдено в списке." status_not_found_in_list="Не найдено значений в списке на основе введенных значений." @@ -850,6 +857,7 @@ status_deleted_fields_count="Удалено $REPLACE_STRING полей." status_wrapped_find="Поиск остановлен '$REPLACE_STRING1'. Позиция: $REPLACE_STRING2" status_wrapped_no_find="Поиск остановлен. Позиция: $REPLACE_STRING" status_line_and_column_position="(Строка: $REPLACE_STRING1, Столбец: $REPLACE_STRING2)" +status_unable_to_open_file="Невозиожно открыть файл: $REPLACE_STRING" ; MessageBox Titles msgbox_title_error="Ошибка" @@ -857,6 +865,7 @@ msgbox_title_confirm="Ок" msgbox_title_use_variables_syntax_error="Ошибка при использовании переменных: Ошибка кода." msgbox_title_use_variables_execution_error="Ошибка при использовании переменных: Ошибка исполнения." msgbox_title_save_list="Сохранить список" +msgbox_title_reload="Перезагрузка" ; MessageBox Messages msgbox_failed_create_control="Не удалось создать элемент управления с идентификатором: $REPLACE_STRING1, GetLastError-Сообщение: $REPLACE_STRING2" @@ -868,6 +877,7 @@ msgbox_confirm_delete_single="Вы уверены, что хотите удал msgbox_confirm_delete_multiple="Вы уверены, что хотите удалить строки $REPLACE_STRING?" msgbox_save_list_file="Сохранить список: $REPLACE_STRING?" msgbox_save_list="Хотите сохранить список?" +msgbox_file_modified_prompt="$REPLACE_STRING

Файл был изменен другой программой.
Вы хотите загрузить изменения и потерять несохраненные данные?" ; Context Menu ctxmenu_transfer_to_input_fields="Переместить в поля ввода Alt+Up" @@ -1000,9 +1010,8 @@ status_unable_to_save_file="Error: No se puede abrir o escribir en el archivo." status_saved_items_to_csv="$REPLACE_STRING elementos guardados en CSV." status_no_valid_items_in_csv="No se encontraron elementos válidos en el archivo CSV." status_list_exported_to_bash="Lista exportada a script BASH." -status_unable_to_open_file="No se pudo abrir el archivo." -status_invalid_column_count="Número inválido de columnas en el archivo." -status_invalid_data_in_columns="Datos inválidos encontrados en las columnas." +status_invalid_column_count="Archivo no cargado! Número inválido de columnas en el archivo CSV." +status_invalid_data_in_columns="Archivo no cargado! Datos inválidos encontrados en las columnas." status_no_find_replace_list_input="No se proporcionó cadena de 'Buscar' o 'Reemplazar'. Por favor, ingrese un valor." status_found_in_list="Entrada encontrada en la lista." status_not_found_in_list="No se encontró ninguna entrada en la lista basada en los campos de entrada." @@ -1026,6 +1035,7 @@ status_deleted_fields_count="$REPLACE_STRING campos eliminados." status_wrapped_find="Envuelto '$REPLACE_STRING1'. Posición: $REPLACE_STRING2" status_wrapped_no_find="Posición envuelta: $REPLACE_STRING" status_line_and_column_position=" (Línea: $REPLACE_STRING1, Columna: $REPLACE_STRING2)" +status_unable_to_open_file="No se pudo abrir el archivo: $REPLACE_STRING" ; MessageBox Titles msgbox_title_error="Error" @@ -1033,6 +1043,7 @@ msgbox_title_confirm="Confirmar" msgbox_title_use_variables_syntax_error="Usar Variables: Error de Sintaxis" msgbox_title_use_variables_execution_error="Usar Variables: Error de Ejecución" msgbox_title_save_list="Guardar lista" +msgbox_title_reload="Recargar" ; MessageBox Messages msgbox_failed_create_control="Error al crear el control con ID: $REPLACE_STRING1, GetLastError retornó: $REPLACE_STRING2" @@ -1044,6 +1055,7 @@ msgbox_confirm_delete_single="¿Está seguro de que desea eliminar esta línea?" msgbox_confirm_delete_multiple="¿Está seguro de que desea eliminar $REPLACE_STRING líneas?" msgbox_save_list_file="Guardar lista: $REPLACE_STRING?" msgbox_save_list="¿Desea guardar la lista?" +msgbox_file_modified_prompt="$REPLACE_STRING

El archivo ha sido modificado por otro programa.
¿Desea cargar los cambios y perder las modificaciones no guardadas?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transferir a campos de entrada Alt+Up" diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 98d82a0..bd8353b 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -1726,6 +1726,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializeListView(); initializeDragAndDrop(); loadSettings(); + checkForFileChangesAtStartup(); updateButtonVisibilityBasedOnMode(); updateStatisticsColumnButtonIcon(); @@ -5877,20 +5878,28 @@ void MultiReplace::showStatusMessage(const std::wstring& messageText, COLORREF c } std::wstring MultiReplace::getShortenedFilePath(const std::wstring& path, int maxLength, HDC hDC) { - // Calculate the width of each character in the path and the width of the dots ("...") + bool hdcProvided = true; + + // If no HDC is provided, get the one for the main window (_hSelf) + if (hDC == nullptr) { + hDC = GetDC(_hSelf); // Get the device context for _hSelf (less accurate) + hdcProvided = false; // Mark that HDC was not provided externally + } + double dotWidth = 0.0; SIZE charSize; std::vector characterWidths; + // Calculate the width of each character in the path and the width of the dots ("...") for (wchar_t ch : path) { GetTextExtentPoint32(hDC, &ch, 1, &charSize); characterWidths.push_back(static_cast(charSize.cx)); if (ch == L'.') { - dotWidth = static_cast(charSize.cx); // Store width of '.' separately + dotWidth = static_cast(charSize.cx); // Store width of '.' separately } } - double totalDotsWidth = dotWidth * 3; // Width for "..." + double totalDotsWidth = dotWidth * 3; // Width for "..." // Split the directory and filename size_t lastSlashPos = path.find_last_of(L"\\/"); @@ -5938,6 +5947,11 @@ std::wstring MultiReplace::getShortenedFilePath(const std::wstring& path, int ma displayPath = path; // No shortening needed } + // If we obtained HDC ourselves, release it + if (!hdcProvided) { + ReleaseDC(_hSelf, hDC); + } + return displayPath; } @@ -5961,8 +5975,6 @@ void MultiReplace::showListFilePath() { // Display the shortened path SetWindowTextW(hPathDisplay, shortenedPath.c_str()); - ReleaseDC(hPathDisplay, hDC); - // Update the parent window area where the control resides RECT rect; GetWindowRect(hPathDisplay, &rect); @@ -6356,22 +6368,17 @@ int MultiReplace::checkForUnsavedChanges() { std::size_t currentListHash = computeListHash(replaceListData); if (currentListHash != originalListHash) { - HDC hDC = GetDC(_hSelf); - - // Define a reasonable width for the MessageBox path display - int maxLengthForMessageBox = 300; std::wstring message; if (!listFilePath.empty()) { // Get the shortened file path and build the message - std::wstring shortenedFilePath = getShortenedFilePath(listFilePath, maxLengthForMessageBox, hDC); + std::wstring shortenedFilePath = getShortenedFilePath(listFilePath, 500); message = getLangStr(L"msgbox_save_list_file", { shortenedFilePath }); } else { // If no file is associated, use the alternative message message = getLangStr(L"msgbox_save_list"); } - ReleaseDC(_hSelf, hDC); // Show the MessageBox with the appropriate message int result = MessageBox( @@ -6413,7 +6420,9 @@ void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vect // Open file in binary mode to read UTF-8 data std::ifstream inFile(filePath); if (!inFile.is_open()) { - throw CsvLoadException("status_unable_to_open_file"); + std::wstring shortenedFilePathW = getShortenedFilePath(filePath, 500); + std::string errorMessage = wstringToString(getLangStr(L"status_unable_to_open_file", { shortenedFilePathW })); + throw CsvLoadException(errorMessage); } std::vector tempList; // Temporary list to hold items @@ -6445,7 +6454,7 @@ void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vect columns.push_back(unescapeCsvValue(currentValue)); if (columns.size() != 8) { - throw CsvLoadException("status_invalid_column_count"); + throw CsvLoadException(wstringToString(getLangStr(L"status_invalid_column_count"))); } ReplaceItemData item; @@ -6462,7 +6471,7 @@ void MultiReplace::loadListFromCsvSilent(const std::wstring& filePath, std::vect tempList.push_back(item); } catch (const std::exception&) { - throw CsvLoadException("status_invalid_data_in_columns"); + throw CsvLoadException(wstringToString(getLangStr(L"status_invalid_data_in_columns"))); } } @@ -6482,7 +6491,7 @@ void MultiReplace::loadListFromCsv(const std::wstring& filePath) { } catch (const CsvLoadException& ex) { // Resolve the error key to a localized string when displaying the message - showStatusMessage(getLangStr(stringToWString(ex.what())), RGB(255, 0, 0)); + showStatusMessage(stringToWString(ex.what()), RGB(255, 0, 0)); return; } @@ -6498,6 +6507,45 @@ void MultiReplace::loadListFromCsv(const std::wstring& filePath) { InvalidateRect(_replaceListView, NULL, TRUE); } +void MultiReplace::checkForFileChangesAtStartup() { + if (listFilePath.empty()) { + return; + } + + std::wstring shortenedFilePath = getShortenedFilePath(listFilePath, 500); + + try { + std::vector tempListFromFile; + loadListFromCsvSilent(listFilePath, tempListFromFile); // Load the list into a temporary list + + std::size_t newFileHash = computeListHash(tempListFromFile); // Calculate the new file hash + + // Check if the file has been modified externally + if (newFileHash != originalListHash) { + std::wstring message = getLangStr(L"msgbox_file_modified_prompt", { shortenedFilePath }); + + int response = MessageBox(_hSelf, message.c_str(), getLangStr(L"msgbox_title_reload").c_str(), MB_YESNO | MB_ICONQUESTION); + + if (response == IDYES) { + replaceListData = tempListFromFile; + originalListHash = newFileHash; + } + } + } + catch (const CsvLoadException& ex) { + // Resolve the error key to a localized string when displaying the message + showStatusMessage(stringToWString(ex.what()), RGB(255, 0, 0)); + return; + } + + if (replaceListData.empty()) { + showStatusMessage(getLangStr(L"status_no_valid_items_in_csv"), RGB(255, 0, 0)); + } + else { + showStatusMessage(getLangStr(L"status_items_loaded_from_csv", { std::to_wstring(replaceListData.size()) }), RGB(0, 128, 0)); + } +} + std::wstring MultiReplace::escapeCsvValue(const std::wstring& value) { std::wstring escapedValue = L"\""; diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 4a6d80f..1970b84 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -559,7 +559,7 @@ class MultiReplace : public StaticDialog void updateHeaderSortDirection(); void showStatusMessage(const std::wstring& messageText, COLORREF color); void calculateCharacterWidths(); - std::wstring getShortenedFilePath(const std::wstring& path, int maxLength, HDC hDC); + std::wstring MultiReplace::getShortenedFilePath(const std::wstring& path, int maxLength, HDC hDC = nullptr); void showListFilePath(); void displayResultCentered(size_t posStart, size_t posEnd, bool isDownwards); std::wstring getSelectedText(); @@ -582,6 +582,7 @@ class MultiReplace : public StaticDialog bool saveListToCsvSilent(const std::wstring& filePath, const std::vector& list); void saveListToCsv(const std::wstring& filePath, const std::vector& list); void loadListFromCsvSilent(const std::wstring& filePath, std::vector& list); + void checkForFileChangesAtStartup(); std::wstring escapeCsvValue(const std::wstring& value); std::wstring unescapeCsvValue(const std::wstring& value); diff --git a/src/language_mapping.cpp b/src/language_mapping.cpp index 1da5480..fc59e37 100644 --- a/src/language_mapping.cpp +++ b/src/language_mapping.cpp @@ -118,10 +118,11 @@ std::unordered_map languageMap = { { L"status_saved_items_to_csv", L"$REPLACE_STRING items saved to CSV." }, { L"status_no_valid_items_in_csv", L"No valid items found in the CSV file." }, { L"status_list_exported_to_bash", L"List exported to BASH script." }, -{ L"status_unable_to_open_file", L"Failed to open the file." }, -{ L"status_invalid_column_count", L"Invalid number of columns in CSV file." }, -{ L"status_invalid_data_in_columns", L"Invalid data found in CSV columns." }, +{ L"status_invalid_column_count", L"File not loaded! Invalid number of columns in CSV file." }, +{ L"status_invalid_data_in_columns", L"File not loaded! Invalid data found in CSV columns." }, { L"status_no_find_replace_list_input", L"No 'Find' or 'Replace' string provided. Please enter a value." }, +{ L"status_found_in_list", L"Entry found in the list." }, +{ L"status_not_found_in_list", L"No entry found in the list based on input fields." }, // Dynamic Status message entries { L"status_rows_shifted", L"$REPLACE_STRING rows successfully shifted." }, @@ -141,9 +142,7 @@ std::unordered_map languageMap = { { L"status_wrapped_find", L"Wrapped '$REPLACE_STRING1'. Position: $REPLACE_STRING2" }, { L"status_wrapped_no_find", L"Wrapped. Position: $REPLACE_STRING" }, { L"status_line_and_column_position", L" (Line: $REPLACE_STRING, Column: $REPLACE_STRING1)" }, -{ L"status_no_find_replace_list_input", L"No 'Find' or 'Replace' string provided. Please enter a value." }, -{ L"status_found_in_list", L"Entry found in the list." }, -{ L"status_not_found_in_list", L"No entry found in the list based on input fields." }, +{ L"status_unable_to_open_file", L"Failed to open the file: $REPLACE_STRING1" }, // MessageBox Titles { L"msgbox_title_error", L"Error" }, @@ -151,6 +150,7 @@ std::unordered_map languageMap = { { L"msgbox_title_use_variables_syntax_error", L"Use Variables: Syntax Error" }, { L"msgbox_title_use_variables_execution_error", L"Use Variables: Execution Error" }, { L"msgbox_title_save_list", L"Save list" }, +{ L"msgbox_title_reload", L"Reload" }, // MessageBox Messages { L"msgbox_failed_create_control", L"Failed to create control with ID: $REPLACE_STRING1, GetLastError returned: $REPLACE_STRING2" }, @@ -162,6 +162,7 @@ std::unordered_map languageMap = { { L"msgbox_confirm_delete_multiple", L"Are you sure you want to delete $REPLACE_STRING lines?" }, { L"msgbox_save_list_file", L"Save list: $REPLACE_STRING?" }, { L"msgbox_save_list", L"Do you want to save the list?" }, +{ L"msgbox_file_modified_prompt", L"$REPLACE_STRING

The file has been modified by another program.
Do you want to load the changes and lose unsaved modifications?" }, // Context Menu Strings { L"ctxmenu_transfer_to_input_fields", L"&Transfer to Input Fields\tAlt+Up" }, From 24325281729b928ee2af301c46965d307b1c6100 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Fri, 27 Sep 2024 12:40:47 +0200 Subject: [PATCH 11/51] update Window Size based on Use List --- languages.ini | 22 +++--- src/MultiReplacePanel.cpp | 130 +++++++++++++++++++++++------------- src/MultiReplacePanel.h | 1 + src/StaticDialog/resource.h | 1 + src/language_mapping.cpp | 4 +- 5 files changed, 97 insertions(+), 61 deletions(-) diff --git a/languages.ini b/languages.ini index f3407a0..7a2d485 100644 --- a/languages.ini +++ b/languages.ini @@ -160,9 +160,9 @@ msgbox_error_saving_settings="An error occurred while saving the settings:
$ msgbox_use_variables_execution_error="Execution halted due to execution failure in:
$REPLACE_STRING" msgbox_confirm_delete_single="Are you sure you want to delete this line?" msgbox_confirm_delete_multiple="Are you sure you want to delete $REPLACE_STRING lines?" -msgbox_save_list_file="Save list: $REPLACE_STRING?" +msgbox_save_list_file="Save list: '$REPLACE_STRING' ?" msgbox_save_list="Do you want to save the list?" -msgbox_file_modified_prompt="$REPLACE_STRING

The file has been modified by another program.
Do you want to load the changes and lose unsaved modifications?" +msgbox_file_modified_prompt="'$REPLACE_STRING'

The file has been modified by another program.
Do you want to load the changes and lose unsaved modifications?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transfer to Input Fields Alt+Up" @@ -338,9 +338,9 @@ msgbox_error_saving_settings="Fehler beim Speichern der Einstellungen:
$REPL msgbox_use_variables_execution_error="Ausführung wegen Fehler angehalten:
$REPLACE_STRING" msgbox_confirm_delete_single="Sind Sie sicher, dass Sie diese Zeile löschen möchten?" msgbox_confirm_delete_multiple="Sind Sie sicher, dass Sie $REPLACE_STRING Zeilen löschen möchten?" -msgbox_save_list_file="Liste speichern: $REPLACE_STRING?" +msgbox_save_list_file="Liste speichern: '$REPLACE_STRING' ?" msgbox_save_list="Möchten Sie die Liste speichern?" -msgbox_file_modified_prompt="$REPLACE_STRING

Die Datei wurde von einem anderen Programm geändert.
Möchten Sie die Änderungen laden und ungespeicherte Änderungen verlieren?" +msgbox_file_modified_prompt="'$REPLACE_STRING'

Die Datei wurde von einem anderen Programm geändert.
Möchten Sie die Änderungen laden und ungespeicherte Änderungen verlieren?" ; Context Menu ctxmenu_transfer_to_input_fields="&In Eingabefelder übertragen Alt+Hoch" @@ -517,9 +517,9 @@ msgbox_error_saving_settings="Si è verificato un errore durante il salvataggio msgbox_use_variables_execution_error="Esecuzione interrotta per errore di esecuzione in:
$REPLACE_STRING" msgbox_confirm_delete_single="Vuoi cancellare questa riga?" msgbox_confirm_delete_multiple="Vuoi cancellare la riga $REPLACE_STRING?" -msgbox_save_list_file="Salva elenco: $REPLACE_STRING?" +msgbox_save_list_file="Salva elenco: '$REPLACE_STRING' ?" msgbox_save_list="Vuoi salvare l'elenco?" -msgbox_file_modified_prompt="$REPLACE_STRING

Il file è stato modificato da un altro programma.
Vuoi caricare le modifiche e perdere le modifiche non salvate?" +msgbox_file_modified_prompt="'$REPLACE_STRING'

Il file è stato modificato da un altro programma.
Vuoi caricare le modifiche e perdere le modifiche non salvate?" ; Context Menu ctxmenu_transfer_to_input_fields="&Transferisci nei campi di input Alt+Up" @@ -696,9 +696,9 @@ msgbox_error_saving_settings="Hiba történt a beállítások mentése közben:< msgbox_use_variables_execution_error="Végrehajtás megszakadt a következő miatt:
$REPLACE_STRING" msgbox_confirm_delete_single="Biztosan törölni szeretné ezt a sort?" msgbox_confirm_delete_multiple="Biztosan törölni szeretné $REPLACE_STRING sorokat?" -msgbox_save_list_file="Lista mentése: $REPLACE_STRING?" +msgbox_save_list_file="Lista mentése: '$REPLACE_STRING' ?" msgbox_save_list="Szeretné menteni a listát?" -msgbox_file_modified_prompt="$REPLACE_STRING

A fájlt egy másik program módosította.
Szeretné betölteni a változtatásokat és elveszíteni a nem mentett módosításokat?" +msgbox_file_modified_prompt="'$REPLACE_STRING'

A fájlt egy másik program módosította.
Szeretné betölteni a változtatásokat és elveszíteni a nem mentett módosításokat?" ; Context Menu ctxmenu_transfer_to_input_fields="&Átvitel a bemeneti mezőkbe Alt+Up" @@ -875,9 +875,9 @@ msgbox_error_saving_settings="Произошла ошибка при сохра msgbox_use_variables_execution_error="Выполнение приостановлено из-за сбоя выполнения в:
$REPLACE_STRING" msgbox_confirm_delete_single="Вы уверены, что хотите удалить эту строку?" msgbox_confirm_delete_multiple="Вы уверены, что хотите удалить строки $REPLACE_STRING?" -msgbox_save_list_file="Сохранить список: $REPLACE_STRING?" +msgbox_save_list_file="Сохранить список: '$REPLACE_STRING' ?" msgbox_save_list="Хотите сохранить список?" -msgbox_file_modified_prompt="$REPLACE_STRING

Файл был изменен другой программой.
Вы хотите загрузить изменения и потерять несохраненные данные?" +msgbox_file_modified_prompt="'$REPLACE_STRING'

Файл был изменен другой программой.
Вы хотите загрузить изменения и потерять несохраненные данные?" ; Context Menu ctxmenu_transfer_to_input_fields="Переместить в поля ввода Alt+Up" @@ -1053,7 +1053,7 @@ msgbox_error_saving_settings="Ocurrió un error al guardar la configuración:
selectedIndices; int i = -1; while ((i = ListView_GetNextItem(listView, i, LVNI_SELECTED)) != -1) { @@ -949,7 +980,7 @@ void MultiReplace::handleCopyToListButton() { // Enable the ListView accordingly SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, BST_CHECKED, 0); - EnableWindow(_replaceListView, TRUE); + adjustWindowSize(); } void MultiReplace::resetCountColumns() { @@ -1727,7 +1758,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializeDragAndDrop(); loadSettings(); checkForFileChangesAtStartup(); - updateButtonVisibilityBasedOnMode(); + adjustWindowSize(); updateStatisticsColumnButtonIcon(); // Activate Dark Mode @@ -1740,10 +1771,24 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l MINMAXINFO* pMMI = reinterpret_cast(lParam); RECT adjustedSize = calculateMinWindowFrame(_hSelf); pMMI->ptMinTrackSize.x = adjustedSize.right; - pMMI->ptMinTrackSize.y = adjustedSize.bottom; + //pMMI->ptMaxTrackSize.x = MAXLONG; + + // Determine whether the checkbox is checked + BOOL listCheckboxChecked = (IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED); + + if (listCheckboxChecked) { + pMMI->ptMinTrackSize.y = adjustedSize.bottom; + //pMMI->ptMaxTrackSize.y = MAXLONG; + } + else { + pMMI->ptMinTrackSize.y = adjustedSize.bottom; + pMMI->ptMaxTrackSize.y = adjustedSize.bottom; // Fix the height + } + return 0; } + case WM_ACTIVATE: { if (LOWORD(wParam) == WA_INACTIVE) { @@ -2271,11 +2316,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l case IDC_USE_LIST_CHECKBOX: { - // Check if the Use List Checkbox is enabled - bool listCheckboxChecked = (IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED); - - // Enable or disable the ListView accordingly - EnableWindow(_replaceListView, listCheckboxChecked); + // Adjust the window size based on the current Use List state + adjustWindowSize(); } break; @@ -6352,10 +6394,6 @@ void MultiReplace::saveListToCsv(const std::wstring& filePath, const std::vector showStatusMessage(getLangStr(L"status_saved_items_to_csv", { std::to_wstring(list.size()) }), RGB(0, 128, 0)); - // Enable the ListView accordingly - SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, BST_CHECKED, 0); - EnableWindow(_replaceListView, TRUE); - // Update the file path and original hash after a successful save listFilePath = filePath; originalListHash = computeListHash(list); @@ -6700,9 +6738,6 @@ void MultiReplace::exportToBashScript(const std::wstring& fileName) { showStatusMessage(getLangStr(L"status_list_exported_to_bash"), RGB(0, 128, 0)); - // Enable the ListView accordingly - SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, BST_CHECKED, 0); - EnableWindow(_replaceListView, TRUE); } std::string MultiReplace::escapeSpecialChars(const std::string& input, bool extended) { @@ -7021,7 +7056,6 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { bool useList = readBoolFromIniFile(iniFilePath, L"Options", L"UseList", false); SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, useList ? BST_CHECKED : BST_UNCHECKED, 0); - EnableWindow(_replaceListView, useList); // Loading and setting the scope with enabled state check int selection = readIntFromIniFile(iniFilePath, L"Scope", L"Selection", 0); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 1970b84..8a325db 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -443,6 +443,7 @@ class MultiReplace : public StaticDialog void updateStatisticsColumnButtonIcon(); void drawGripper(); void SetWindowTransparency(HWND hwnd, BYTE alpha); + void adjustWindowSize(); //ListView HWND CreateHeaderTooltip(HWND hwndParent); diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index 72a5aaa..cb9e9cf 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -132,6 +132,7 @@ #define POS_Y 40 #define MIN_WIDTH 1023 #define MIN_HEIGHT 500 +#define SHRUNK_HEIGHT 280 #define IDC_WEBSITE_LINK_VALUE TEXT("https://github.com/daddel80/notepadpp-multireplace/issues") diff --git a/src/language_mapping.cpp b/src/language_mapping.cpp index fc59e37..72c7108 100644 --- a/src/language_mapping.cpp +++ b/src/language_mapping.cpp @@ -160,9 +160,9 @@ std::unordered_map languageMap = { { L"msgbox_use_variables_execution_error", L"Execution halted due to execution failure in:
$REPLACE_STRING" }, { L"msgbox_confirm_delete_single", L"Are you sure you want to delete this line?" }, { L"msgbox_confirm_delete_multiple", L"Are you sure you want to delete $REPLACE_STRING lines?" }, -{ L"msgbox_save_list_file", L"Save list: $REPLACE_STRING?" }, +{ L"msgbox_save_list_file", L"Save list: '$REPLACE_STRING' ?" }, { L"msgbox_save_list", L"Do you want to save the list?" }, -{ L"msgbox_file_modified_prompt", L"$REPLACE_STRING

The file has been modified by another program.
Do you want to load the changes and lose unsaved modifications?" }, +{ L"msgbox_file_modified_prompt", L"'$REPLACE_STRING'

The file has been modified by another program.
Do you want to load the changes and lose unsaved modifications?" }, // Context Menu Strings { L"ctxmenu_transfer_to_input_fields", L"&Transfer to Input Fields\tAlt+Up" }, From 3dd56785024e27f880a5e6ac194d68fe15aee553 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Fri, 27 Sep 2024 16:23:19 +0200 Subject: [PATCH 12/51] save Window Size --- src/MultiReplacePanel.cpp | 141 ++++++++++++++++++++++++++++---------- src/MultiReplacePanel.h | 1 + 2 files changed, 104 insertions(+), 38 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index ffc59c1..37c31b7 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -88,30 +88,30 @@ void MultiReplace::initializeWindowSize() { } RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { + // Use local variables to avoid modifying windowRect + RECT tempWindowRect; + GetWindowRect(hwnd, &tempWindowRect); + // Measure the window's borders and title bar RECT clientRect; GetClientRect(hwnd, &clientRect); - GetWindowRect(hwnd, &windowRect); - int borderWidth = ((windowRect.right - windowRect.left) - (clientRect.right - clientRect.left)) / 2; - int titleBarHeight = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top) - borderWidth; + int borderWidth = ((tempWindowRect.right - tempWindowRect.left) - (clientRect.right - clientRect.left)) / 2; + int titleBarHeight = (tempWindowRect.bottom - tempWindowRect.top) - (clientRect.bottom - clientRect.top) - borderWidth; - // Determine whether the checkbox is checked - BOOL listCheckboxChecked = (IsDlgButtonChecked(hwnd, IDC_USE_LIST_CHECKBOX) == BST_CHECKED); + // Determine whether the Use List checkbox is checked + BOOL useListChecked = IsDlgButtonChecked(hwnd, IDC_USE_LIST_CHECKBOX) == BST_CHECKED; - int adjustedHeight = 0; - int adjustedWidth = MIN_WIDTH + 2 * borderWidth; + int minHeight = useListChecked ? MIN_HEIGHT : SHRUNK_HEIGHT; + int minWidth = MIN_WIDTH; - if (listCheckboxChecked) { - adjustedHeight = MIN_HEIGHT + borderWidth + titleBarHeight; - } - else { - adjustedHeight = SHRUNK_HEIGHT + borderWidth + titleBarHeight; - } + // Adjust for window borders and title bar + minHeight += borderWidth + titleBarHeight; + minWidth += 2 * borderWidth; - RECT adjustedSize = { 0, 0, adjustedWidth, adjustedHeight }; + RECT minSize = { 0, 0, minWidth, minHeight }; - return adjustedSize; + return minSize; } void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) { @@ -525,12 +525,42 @@ void MultiReplace::SetWindowTransparency(HWND hwnd, BYTE alpha) { } void MultiReplace::adjustWindowSize() { - // Adjust the window size based on the current Use List state - RECT adjustedSize = calculateMinWindowFrame(_hSelf); - GetWindowRect(_hSelf, &windowRect); - int newWidth = windowRect.right - windowRect.left; - int newHeight = adjustedSize.bottom; - SetWindowPos(_hSelf, NULL, 0, 0, newWidth, newHeight, SWP_NOMOVE | SWP_NOZORDER); + // Determine whether the Use List checkbox is checked + BOOL useListChecked = IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED; + + // Get the minimum allowed window size + RECT minSize = calculateMinWindowFrame(_hSelf); + int minHeight = minSize.bottom; // minSize.bottom is minHeight + + // Get the current window position and size + RECT currentRect; + GetWindowRect(_hSelf, ¤tRect); + int currentWidth = currentRect.right - currentRect.left; + int currentHeight = currentRect.bottom - currentRect.top; + int currentX = currentRect.left; + int currentY = currentRect.top; + + int newHeight = currentHeight; + + if (useListChecked) { + // If we have a stored previous height, restore it + if (previousWindowHeight > 0) { + newHeight = std::max(previousWindowHeight, minHeight); + } + else { + // No previous height stored; keep the current height or ensure minimum height + newHeight = std::max(currentHeight, minHeight); + } + } + else { + // Store the current height before shrinking + previousWindowHeight = currentHeight; + // Shrink the window to the minimum height + newHeight = minHeight; + } + + // Adjust the window size while keeping the current position and width + SetWindowPos(_hSelf, NULL, currentX, currentY, currentWidth, newHeight, SWP_NOZORDER); // Update the visibility of UI elements based on the current modes updateButtonVisibilityBasedOnMode(); @@ -1767,28 +1797,34 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l } break; - case WM_GETMINMAXINFO: { + case WM_GETMINMAXINFO: + { MINMAXINFO* pMMI = reinterpret_cast(lParam); RECT adjustedSize = calculateMinWindowFrame(_hSelf); + + // Set minimum window width pMMI->ptMinTrackSize.x = adjustedSize.right; - //pMMI->ptMaxTrackSize.x = MAXLONG; + // Allow horizontal resizing up to a maximum value + pMMI->ptMaxTrackSize.x = MAXLONG; - // Determine whether the checkbox is checked - BOOL listCheckboxChecked = (IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED); + // Determine whether the Use List checkbox is checked + BOOL useListChecked = IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED; - if (listCheckboxChecked) { + if (useListChecked) { + // Set minimum window height pMMI->ptMinTrackSize.y = adjustedSize.bottom; - //pMMI->ptMaxTrackSize.y = MAXLONG; + // Allow vertical resizing up to a maximum value + pMMI->ptMaxTrackSize.y = MAXLONG; } else { + // Set fixed window height pMMI->ptMinTrackSize.y = adjustedSize.bottom; - pMMI->ptMaxTrackSize.y = adjustedSize.bottom; // Fix the height + pMMI->ptMaxTrackSize.y = adjustedSize.bottom; // Fix the height when Use List is unchecked } return 0; } - case WM_ACTIVATE: { if (LOWORD(wParam) == WA_INACTIVE) { @@ -6877,12 +6913,13 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { throw std::runtime_error("Could not open settings file for writing."); } - // Store window size and position from the global windowRect - GetWindowRect(_hSelf, &windowRect); - int width = windowRect.right - windowRect.left; - int height = windowRect.bottom - windowRect.top; - int posX = windowRect.left; - int posY = windowRect.top; + // Get the current window rectangle + RECT currentRect; + GetWindowRect(_hSelf, ¤tRect); + int width = currentRect.right - currentRect.left; + int height = currentRect.bottom - currentRect.top; + int posX = currentRect.left; + int posY = currentRect.top; outFile << wstringToString(L"[Window]\n"); outFile << wstringToString(L"Width=" + std::to_wstring(width) + L"\n"); @@ -7118,11 +7155,39 @@ void MultiReplace::loadSettings() { void MultiReplace::loadUIConfigFromIni() { auto [iniFilePath, _] = generateConfigFilePaths(); // Generating config file paths - // Load window position and size + // Load window position windowRect.left = readIntFromIniFile(iniFilePath, L"Window", L"PosX", POS_X); windowRect.top = readIntFromIniFile(iniFilePath, L"Window", L"PosY", POS_Y); - windowRect.right = windowRect.left + std::max(readIntFromIniFile(iniFilePath, L"Window", L"Width", MIN_WIDTH), MIN_WIDTH); - windowRect.bottom = windowRect.top + std::max(readIntFromIniFile(iniFilePath, L"Window", L"Height", MIN_HEIGHT), MIN_HEIGHT); + + // Load the state of the Use List checkbox from the ini file + int useList = readIntFromIniFile(iniFilePath, L"Options", L"UseList", 1); // Default to 1 if not found + // Set the checkbox state + CheckDlgButton(_hSelf, IDC_USE_LIST_CHECKBOX, useList ? BST_CHECKED : BST_UNCHECKED); + + // Determine whether the Use List checkbox is checked + BOOL useListChecked = (useList == 1); + + int defaultHeight = useListChecked ? MIN_HEIGHT : SHRUNK_HEIGHT; + + // Load window size + int savedWidth = readIntFromIniFile(iniFilePath, L"Window", L"Width", MIN_WIDTH); + int savedHeight = readIntFromIniFile(iniFilePath, L"Window", L"Height", defaultHeight); + + // Ensure the saved size meets the minimum requirements + int minWidth = MIN_WIDTH; + int minHeight = defaultHeight; + + int width = std::max(savedWidth, minWidth); + int height = std::max(savedHeight, minHeight); + + // Set windowRect + windowRect.right = windowRect.left + width; + windowRect.bottom = windowRect.top + height; + + // Initialize previousWindowHeight if Use List is checked + if (useListChecked) { + previousWindowHeight = height; + } // Read column widths findCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"FindCountWidth", findCountColumnWidth); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 8a325db..e9f1e97 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -413,6 +413,7 @@ class MultiReplace : public StaticDialog std::wstring listFilePath = L""; //to store the file path of loaded list const std::size_t golden_ratio_constant = 0x9e3779b9; // 2^32 / φ /uused for Hashing std::size_t originalListHash = 0; + int previousWindowHeight = 0; // GUI control-related constants const std::vector selectionRadioDisabledButtons = { From 2d9a7b9c29d5879a3eadf984ecb5bbc502a7d152 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sat, 28 Sep 2024 10:23:18 +0200 Subject: [PATCH 13/51] update of saving Window Height for enabled Use List --- src/MultiReplacePanel.cpp | 80 +++++++++++++++++++++------------------ src/MultiReplacePanel.h | 3 +- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 37c31b7..df672f0 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -536,27 +536,18 @@ void MultiReplace::adjustWindowSize() { RECT currentRect; GetWindowRect(_hSelf, ¤tRect); int currentWidth = currentRect.right - currentRect.left; - int currentHeight = currentRect.bottom - currentRect.top; int currentX = currentRect.left; int currentY = currentRect.top; - int newHeight = currentHeight; + int newHeight = 0; if (useListChecked) { - // If we have a stored previous height, restore it - if (previousWindowHeight > 0) { - newHeight = std::max(previousWindowHeight, minHeight); - } - else { - // No previous height stored; keep the current height or ensure minimum height - newHeight = std::max(currentHeight, minHeight); - } + // Use the stored useListOnHeight, ensuring it's at least the minimum height + newHeight = std::max(useListOnHeight, minHeight); } else { - // Store the current height before shrinking - previousWindowHeight = currentHeight; - // Shrink the window to the minimum height - newHeight = minHeight; + // Set the height to useListOffHeight (SHRUNK_HEIGHT) + newHeight = useListOffHeight; } // Adjust the window size while keeping the current position and width @@ -1898,7 +1889,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l case WM_SIZE: { - if (isWindowOpen) { + if (isWindowOpen) { // Force the edit control of the right mouse click to lose focus by setting focus to the main window if (isWindowOpen && hwndEdit && GetFocus() == hwndEdit) { HWND hwndListView = GetDlgItem(_hSelf, IDC_REPLACE_LIST); @@ -1919,9 +1910,28 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Refresh UI and gripper by invalidating window InvalidateRect(_hSelf, NULL, TRUE); + + // Determine whether the Use List checkbox is checked + BOOL useListChecked = IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED; + + if (useListChecked) { + // Update useListOnHeight with the new height + RECT currentRect; + GetWindowRect(_hSelf, ¤tRect); + int currentHeight = currentRect.bottom - currentRect.top; + + // Get the minimum allowed window height + RECT minSize = calculateMinWindowFrame(_hSelf); + int minHeight = minSize.bottom; + + // Ensure useListOnHeight is at least the minimum height + useListOnHeight = std::max(currentHeight, minHeight); + } + } return 0; } + break; case WM_NOTIFY: @@ -6921,11 +6931,19 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { int posX = currentRect.left; int posY = currentRect.top; + // Determine whether the Use List checkbox is checked + BOOL useListChecked = IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED; + + // Update useListOnHeight if Use List is checked + if (useListChecked) { + useListOnHeight = height; + } + outFile << wstringToString(L"[Window]\n"); outFile << wstringToString(L"Width=" + std::to_wstring(width) + L"\n"); - outFile << wstringToString(L"Height=" + std::to_wstring(height) + L"\n"); outFile << wstringToString(L"PosX=" + std::to_wstring(posX) + L"\n"); outFile << wstringToString(L"PosY=" + std::to_wstring(posY) + L"\n"); + outFile << wstringToString(L"UseListOnHeight=" + std::to_wstring(useListOnHeight) + L"\n"); // Save transparency settings outFile << wstringToString(L"ForegroundTransparency=" + std::to_wstring(foregroundTransparency) + L"\n"); @@ -6956,7 +6974,7 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { int wrapAround = IsDlgButtonChecked(_hSelf, IDC_WRAP_AROUND_CHECKBOX) == BST_CHECKED ? 1 : 0; int useVariables = IsDlgButtonChecked(_hSelf, IDC_USE_VARIABLES_CHECKBOX) == BST_CHECKED ? 1 : 0; int ButtonsMode = IsDlgButtonChecked(_hSelf, IDC_2_BUTTONS_MODE) == BST_CHECKED ? 1 : 0; - int useList = IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED ? 1 : 0; + int useList = useListChecked ? 1 : 0; outFile << wstringToString(L"[Options]\n"); outFile << wstringToString(L"WholeWord=" + std::to_wstring(wholeWord) + L"\n"); @@ -7091,7 +7109,7 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { bool replaceButtonsMode = readBoolFromIniFile(iniFilePath, L"Options", L"ButtonsMode", false); SendMessage(GetDlgItem(_hSelf, IDC_2_BUTTONS_MODE), BM_SETCHECK, replaceButtonsMode ? BST_CHECKED : BST_UNCHECKED, 0); - bool useList = readBoolFromIniFile(iniFilePath, L"Options", L"UseList", false); + bool useList = readBoolFromIniFile(iniFilePath, L"Options", L"UseList", true); SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, useList ? BST_CHECKED : BST_UNCHECKED, 0); // Loading and setting the scope with enabled state check @@ -7164,31 +7182,21 @@ void MultiReplace::loadUIConfigFromIni() { // Set the checkbox state CheckDlgButton(_hSelf, IDC_USE_LIST_CHECKBOX, useList ? BST_CHECKED : BST_UNCHECKED); - // Determine whether the Use List checkbox is checked - BOOL useListChecked = (useList == 1); - - int defaultHeight = useListChecked ? MIN_HEIGHT : SHRUNK_HEIGHT; - - // Load window size + // Load window width int savedWidth = readIntFromIniFile(iniFilePath, L"Window", L"Width", MIN_WIDTH); - int savedHeight = readIntFromIniFile(iniFilePath, L"Window", L"Height", defaultHeight); + int width = std::max(savedWidth, MIN_WIDTH); - // Ensure the saved size meets the minimum requirements - int minWidth = MIN_WIDTH; - int minHeight = defaultHeight; + // Load useListOnHeight from INI file + useListOnHeight = readIntFromIniFile(iniFilePath, L"Window", L"UseListOnHeight", MIN_HEIGHT); + useListOnHeight = std::max(useListOnHeight, MIN_HEIGHT); // Ensure minimum height - int width = std::max(savedWidth, minWidth); - int height = std::max(savedHeight, minHeight); + // Set windowRect based on Use List state + BOOL useListChecked = (useList == 1); + int height = useListChecked ? useListOnHeight : useListOffHeight; - // Set windowRect windowRect.right = windowRect.left + width; windowRect.bottom = windowRect.top + height; - // Initialize previousWindowHeight if Use List is checked - if (useListChecked) { - previousWindowHeight = height; - } - // Read column widths findCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"FindCountWidth", findCountColumnWidth); replaceCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"ReplaceCountWidth", replaceCountColumnWidth); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index e9f1e97..260a885 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -413,7 +413,8 @@ class MultiReplace : public StaticDialog std::wstring listFilePath = L""; //to store the file path of loaded list const std::size_t golden_ratio_constant = 0x9e3779b9; // 2^32 / φ /uused for Hashing std::size_t originalListHash = 0; - int previousWindowHeight = 0; + int useListOnHeight = MIN_HEIGHT; // Default height when "Use List" is on + const int useListOffHeight = SHRUNK_HEIGHT; // Height when "Use List" is off (constant) // GUI control-related constants const std::vector selectionRadioDisabledButtons = { From c82daaebebd6e75667b4bdae21833d8bc91dbc26 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sat, 28 Sep 2024 11:18:08 +0200 Subject: [PATCH 14/51] moved checkForFileChangesAtStartup into WM_POST_INIT to show in opened Window --- src/MultiReplacePanel.cpp | 16 +++++++++++++++- src/StaticDialog/resource.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index df672f0..dda2e8b 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -1778,16 +1778,26 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializeListView(); initializeDragAndDrop(); loadSettings(); - checkForFileChangesAtStartup(); adjustWindowSize(); updateStatisticsColumnButtonIcon(); // Activate Dark Mode ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, static_cast(NppDarkMode::dmfInit), reinterpret_cast(_hSelf)); + + // Post a custom message to perform post-initialization tasks after the dialog is shown + PostMessage(_hSelf, WM_POST_INIT, 0, 0); + return TRUE; } break; + case WM_POST_INIT: + { + checkForFileChangesAtStartup(); + + return 0; + } + case WM_GETMINMAXINFO: { MINMAXINFO* pMMI = reinterpret_cast(lParam); @@ -6627,6 +6637,10 @@ void MultiReplace::checkForFileChangesAtStartup() { } else { showStatusMessage(getLangStr(L"status_items_loaded_from_csv", { std::to_wstring(replaceListData.size()) }), RGB(0, 128, 0)); + + // Update the list view control, if necessary + ListView_SetItemCountEx(_replaceListView, replaceListData.size(), LVSICF_NOINVALIDATEALL); + InvalidateRect(_replaceListView, NULL, TRUE); } } diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index cb9e9cf..3d6b94d 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -134,6 +134,9 @@ #define MIN_HEIGHT 500 #define SHRUNK_HEIGHT 280 +// Custom message used to perform initial actions after the window has been fully opened +#define WM_POST_INIT (WM_APP + 1) + #define IDC_WEBSITE_LINK_VALUE TEXT("https://github.com/daddel80/notepadpp-multireplace/issues") #endif // RESOURCE_H From d79a4009485eccfb83c9418e8f43624ecdcc9553 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sat, 28 Sep 2024 12:02:57 +0200 Subject: [PATCH 15/51] updated returns in Dialog --- src/MultiReplacePanel.cpp | 187 ++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 97 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index dda2e8b..60b165a 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -1765,7 +1765,6 @@ int MultiReplace::searchInListData(int startIdx, const std::wstring& findText, c INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) { case WM_INITDIALOG: @@ -1779,23 +1778,22 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializeDragAndDrop(); loadSettings(); adjustWindowSize(); - updateStatisticsColumnButtonIcon(); - + updateStatisticsColumnButtonIcon(); + // Activate Dark Mode - ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, static_cast(NppDarkMode::dmfInit), reinterpret_cast(_hSelf)); + ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, + static_cast(NppDarkMode::dmfInit), reinterpret_cast(_hSelf)); - // Post a custom message to perform post-initialization tasks after the dialog is shown - PostMessage(_hSelf, WM_POST_INIT, 0, 0); + // Post a custom message to perform post-initialization tasks after the dialog is shown + PostMessage(_hSelf, WM_POST_INIT, 0, 0); - return TRUE; + return TRUE; } - break; case WM_POST_INIT: { checkForFileChangesAtStartup(); - - return 0; + return TRUE; } case WM_GETMINMAXINFO: @@ -1844,15 +1842,14 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l HDC hdcStatic = reinterpret_cast(wParam); HWND hwndStatic = reinterpret_cast(lParam); - if (hwndStatic == GetDlgItem(_hSelf, IDC_STATUS_MESSAGE) ) { + if (hwndStatic == GetDlgItem(_hSelf, IDC_STATUS_MESSAGE)) { SetTextColor(hdcStatic, _statusMessageColor); SetBkMode(hdcStatic, TRANSPARENT); - return (LRESULT)GetStockObject(NULL_BRUSH); + return (LRESULT)GetStockObject(NULL_BRUSH); // Return a brush handle } - break; + return FALSE; } - break; case WM_DESTROY: { @@ -1894,8 +1891,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Post a quit message to ensure the application terminates cleanly PostQuitMessage(0); + + return 0; } - break; case WM_SIZE: { @@ -1937,13 +1935,10 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Ensure useListOnHeight is at least the minimum height useListOnHeight = std::max(currentHeight, minHeight); } - } return 0; } - break; - case WM_NOTIFY: { NMHDR* pnmh = reinterpret_cast(lParam); @@ -1978,20 +1973,20 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleDeletion(pnmia); } if (pnmia->iSubItem == 3) { // Select button column - // get current selection status of the item + // Get current selection status of the item bool currentSelectionStatus = replaceListData[pnmia->iItem].isEnabled; - // set the selection status to its opposite + // Set the selection status to its opposite setSelections(!currentSelectionStatus, true); } + return TRUE; } - break; case NM_DBLCLK: { NMITEMACTIVATE* pnmia = reinterpret_cast(lParam); handleCopyBack(pnmia); + return TRUE; } - break; case LVN_GETDISPINFO: { @@ -2058,8 +2053,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l plvdi->item.pszText = L"\u2716"; break; } + return TRUE; } - break; case LVN_COLUMNCLICK: { @@ -2078,7 +2073,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l lastColumn = pnmv->iSubItem; sortReplaceListData(lastColumn, newDirection); // Now correctly passing SortDirection } - break; + return TRUE; } case LVN_KEYDOWN: @@ -2134,7 +2129,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l performItemAction(_contextMenuClickPoint, ItemAction::Delete); break; case VK_F12: // F12 key - GetClientRect(_hSelf, &windowRect); + { + RECT sizeWindowRect; + GetClientRect(_hSelf, &sizeWindowRect); hDC = GetDC(_hSelf); if (hDC) @@ -2154,8 +2151,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l int baseUnitY = tm.tmHeight; // Calculate the window size in dialog units - int duWidth = MulDiv(windowRect.right, 4, baseUnitX); - int duHeight = MulDiv(windowRect.bottom, 8, baseUnitY); + int duWidth = MulDiv(sizeWindowRect.right, 4, baseUnitX); + int duHeight = MulDiv(sizeWindowRect.bottom, 8, baseUnitY); wchar_t sizeText[100]; wsprintfW(sizeText, L"Window Size: %ld x %ld DUs", duWidth, duHeight); @@ -2167,24 +2164,24 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l ReleaseDC(_hSelf, hDC); } break; + } case VK_SPACE: // Spacebar key iItem = ListView_GetNextItem(_replaceListView, -1, LVNI_SELECTED); if (iItem >= 0) { - // get current selection status of the item + // Get current selection status of the item bool currentSelectionStatus = replaceListData[iItem].isEnabled; - // set the selection status to its opposite + // Set the selection status to its opposite setSelections(!currentSelectionStatus, true); } break; } } - + return TRUE; } - break; } } - } - break; + return FALSE; + } case WM_CONTEXTMENU: { @@ -2213,8 +2210,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l createContextMenu(_hSelf, ptScreen, state); // Show context menu return TRUE; } + return FALSE; } - break; case WM_SHOWWINDOW: { @@ -2230,21 +2227,19 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleClearTextMarksButton(); handleClearDelimiterState(); } + return 0; } - break; case WM_PAINT: { drawGripper(); + return 0; } - break; case WM_COMMAND: { - switch (LOWORD(wParam)) { - case IDC_USE_VARIABLES_HELP: { auto n = SendMessage(nppData._nppHandle, NPPM_GETPLUGINHOMEPATH, 0, 0); @@ -2255,17 +2250,16 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l std::wstring filename = isDarkMode ? L"\\help_use_variables_dark.html" : L"\\help_use_variables_light.html"; path += filename; ShellExecute(NULL, L"open", path.c_str(), NULL, NULL, SW_SHOWNORMAL); + return TRUE; } - break; case IDCANCEL: { CloseDebugWindow(); // Close the Lua debug window if it is open EndDialog(_hSelf, 0); _MultiReplace.display(false); - + return TRUE; } - break; case IDC_2_BUTTONS_MODE: { @@ -2273,47 +2267,48 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (HIWORD(wParam) == BN_CLICKED) { updateButtonVisibilityBasedOnMode(); + return TRUE; } + return FALSE; } - break; case IDC_REGEX_RADIO: { setUIElementVisibility(); + return TRUE; } - break; case IDC_NORMAL_RADIO: case IDC_EXTENDED_RADIO: { EnableWindow(GetDlgItem(_hSelf, IDC_WHOLE_WORD_CHECKBOX), TRUE); setUIElementVisibility(); + return TRUE; } - break; case IDC_ALL_TEXT_RADIO: { setUIElementVisibility(); handleClearDelimiterState(); + return TRUE; } - break; case IDC_SELECTION_RADIO: { setUIElementVisibility(); handleClearDelimiterState(); + return TRUE; } - break; case IDC_COLUMN_NUM_EDIT: case IDC_DELIMITER_EDIT: case IDC_QUOTECHAR_EDIT: case IDC_COLUMN_MODE_RADIO: { - CheckRadioButton(_hSelf, IDC_ALL_TEXT_RADIO, IDC_COLUMN_MODE_RADIO, IDC_COLUMN_MODE_RADIO); + CheckRadioButton(_hSelf, IDC_ALL_TEXT_RADIO, IDC_COLUMN_MODE_RADIO, IDC_COLUMN_MODE_RADIO); setUIElementVisibility(); + return TRUE; } - break; case IDC_COLUMN_SORT_ASC_BUTTON: { @@ -2321,9 +2316,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (columnDelimiterData.isValid()) { handleSortStateAndSort(SortDirection::Ascending); UpdateSortButtonSymbols(); - } + } + return TRUE; } - break; case IDC_COLUMN_SORT_DESC_BUTTON: { @@ -2332,8 +2327,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleSortStateAndSort(SortDirection::Descending); UpdateSortButtonSymbols(); } + return TRUE; } - break; case IDC_COLUMN_DROP_BUTTON: { @@ -2343,8 +2338,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleDeleteColumns(); } } + return TRUE; } - break; case IDC_COLUMN_COPY_BUTTON: { @@ -2352,8 +2347,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (columnDelimiterData.isValid()) { handleCopyColumnsToClipboard(); } + return TRUE; } - break; case IDC_COLUMN_HIGHLIGHT_BUTTON: { @@ -2367,16 +2362,15 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleClearColumnMarks(); showStatusMessage(getLangStr(L"status_column_marks_cleared"), RGB(0, 128, 0)); } + return TRUE; } - break; case IDC_USE_LIST_CHECKBOX: { // Adjust the window size based on the current Use List state adjustWindowSize(); - + return TRUE; } - break; case IDC_SWAP_BUTTON: { @@ -2386,14 +2380,14 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Swap the content of the two text fields SetDlgItemTextW(_hSelf, IDC_FIND_EDIT, replaceText.c_str()); SetDlgItemTextW(_hSelf, IDC_REPLACE_EDIT, findText.c_str()); + return TRUE; } - break; case IDC_COPY_TO_LIST_BUTTON: { handleCopyToListButton(); + return TRUE; } - break; case IDC_FIND_BUTTON: case IDC_FIND_NEXT_BUTTON: @@ -2402,17 +2396,17 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l resetCountColumns(); handleDelimiterPositions(DelimiterOperation::LoadAll); handleFindNextButton(); + return TRUE; } - break; case IDC_FIND_PREV_BUTTON: { CloseDebugWindow(); // Close the Lua debug window if it is open resetCountColumns(); handleDelimiterPositions(DelimiterOperation::LoadAll); - handleFindPrevButton(); + handleFindPrevButton(); + return TRUE; } - break; case IDC_REPLACE_BUTTON: { @@ -2420,8 +2414,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l resetCountColumns(); handleDelimiterPositions(DelimiterOperation::LoadAll); handleReplaceButton(); + return TRUE; } - break; case IDC_REPLACE_ALL_SMALL_BUTTON: { @@ -2429,8 +2423,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l resetCountColumns(); handleDelimiterPositions(DelimiterOperation::LoadAll); handleReplaceAllButton(); + return TRUE; } - break; case IDC_REPLACE_ALL_BUTTON: { @@ -2463,7 +2457,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Process documents in the main view if it's visible if (visibleMain) { for (LRESULT i = 0; i < docCountMain; ++i) { - ::SendMessage(nppData._nppHandle, NPPM_ACTIVATEDOC, MAIN_VIEW, i); + ::SendMessage(nppData._nppHandle, NPPM_ACTIVATEDOC, MAIN_VIEW, i); handleDelimiterPositions(DelimiterOperation::LoadAll); handleReplaceAllButton(); } @@ -2489,8 +2483,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleDelimiterPositions(DelimiterOperation::LoadAll); handleReplaceAllButton(); } + return TRUE; } - break; case IDC_MARK_MATCHES_BUTTON: case IDC_MARK_BUTTON: @@ -2499,22 +2493,22 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l handleDelimiterPositions(DelimiterOperation::LoadAll); handleClearTextMarksButton(); handleMarkMatchesButton(); + return TRUE; } - break; case IDC_CLEAR_MARKS_BUTTON: { resetCountColumns(); handleClearTextMarksButton(); showStatusMessage(getLangStr(L"status_all_marks_cleared"), RGB(0, 128, 0)); + return TRUE; } - break; case IDC_COPY_MARKED_TEXT_BUTTON: { handleCopyMarkedTextToClipboardButton(); + return TRUE; } - break; case IDC_SAVE_AS_BUTTON: case IDC_SAVE_TO_CSV_BUTTON: @@ -2525,8 +2519,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (!filePath.empty()) { saveListToCsv(filePath, replaceListData); } + return TRUE; } - break; case IDC_SAVE_BUTTON: { @@ -2541,8 +2535,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l saveListToCsv(filePath, replaceListData); } } + return TRUE; } - break; case IDC_LOAD_LIST_BUTTON: case IDC_LOAD_FROM_CSV_BUTTON: @@ -2561,26 +2555,26 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (!filePath.empty()) { loadListFromCsv(filePath); } + return TRUE; } - break; case IDC_NEW_LIST_BUTTON: { clearList(); + return TRUE; } - break; case IDC_UP_BUTTON: { shiftListItem(_replaceListView, Direction::Up); + return TRUE; } - break; case IDC_DOWN_BUTTON: { shiftListItem(_replaceListView, Direction::Down); + return TRUE; } - break; case IDC_EXPORT_BASH_BUTTON: { @@ -2604,102 +2598,101 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l if (!filePath.empty()) { exportToBashScript(filePath); } + return TRUE; } - break; case ID_REPLACE_ALL_OPTION: { SetDlgItemText(_hSelf, IDC_REPLACE_ALL_BUTTON, getLangStrLPWSTR(L"split_button_replace_all")); isReplaceAllInDocs = false; + return TRUE; } - break; case ID_REPLACE_IN_ALL_DOCS_OPTION: { SetDlgItemText(_hSelf, IDC_REPLACE_ALL_BUTTON, getLangStrLPWSTR(L"split_button_replace_all_in_docs")); isReplaceAllInDocs = true; + return TRUE; } - break; case ID_STATISTICS_COLUMNS: { isStatisticsColumnsExpanded = !isStatisticsColumnsExpanded; resizeCountColumns(); updateStatisticsColumnButtonIcon(); + return TRUE; } - break; case IDM_SEARCH_IN_LIST: { performItemAction(_contextMenuClickPoint, ItemAction::Search); + return TRUE; } - break; case IDM_COPY_DATA_TO_FIELDS: { NMITEMACTIVATE nmia = {}; nmia.iItem = ListView_HitTest(_replaceListView, &_contextMenuClickPoint); - handleCopyBack(&nmia); + handleCopyBack(&nmia); + return TRUE; } - break; case IDM_CUT_LINES_TO_CLIPBOARD: { performItemAction(_contextMenuClickPoint, ItemAction::Cut); + return TRUE; } - break; - case IDM_COPY_LINES_TO_CLIPBOARD: + case IDM_COPY_LINES_TO_CLIPBOARD: { performItemAction(_contextMenuClickPoint, ItemAction::Copy); + return TRUE; } - break; - case IDM_PASTE_LINES_FROM_CLIPBOARD: + case IDM_PASTE_LINES_FROM_CLIPBOARD: { performItemAction(_contextMenuClickPoint, ItemAction::Paste); + return TRUE; } - break; case IDM_EDIT_VALUE: { performItemAction(_contextMenuClickPoint, ItemAction::Edit); + return TRUE; } - break; case IDM_DELETE_LINES: { performItemAction(_contextMenuClickPoint, ItemAction::Delete); + return TRUE; } - break; - case IDM_SELECT_ALL: + case IDM_SELECT_ALL: { ListView_SetItemState(_replaceListView, -1, LVIS_SELECTED, LVIS_SELECTED); + return TRUE; } - break; - case IDM_ENABLE_LINES: + case IDM_ENABLE_LINES: { setSelections(true, ListView_GetSelectedCount(_replaceListView) > 0); + return TRUE; } - break; - case IDM_DISABLE_LINES: + case IDM_DISABLE_LINES: { setSelections(false, ListView_GetSelectedCount(_replaceListView) > 0); + return TRUE; } - break; default: return FALSE; } - } - break; + default: + return FALSE; } - return FALSE; } #pragma endregion From c1729e930931abe75379d28e3376e2b173c85b48 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sat, 28 Sep 2024 13:31:51 +0200 Subject: [PATCH 16/51] fixed issue with Multi-Selected Text when All Text or CSV is enabled --- src/MultiReplacePanel.cpp | 64 +++++++++++++++++++++++++++------------ src/MultiReplacePanel.h | 2 +- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 60b165a..2087ffa 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -2778,7 +2778,7 @@ void MultiReplace::handleReplaceButton() { // First check if the document is read-only LRESULT isReadOnly = ::SendMessage(_hScintilla, SCI_GETREADONLY, 0, 0); if (isReadOnly) { - showStatusMessage(getLangStrLPWSTR(L"status_cannot_replace_read_only"), RGB(255, 0, 0)); + showStatusMessage(getLangStr(L"status_cannot_replace_read_only"), RGB(255, 0, 0)); return; } @@ -2788,13 +2788,20 @@ void MultiReplace::handleReplaceButton() { bool useListEnabled = (IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED); bool wrapAroundEnabled = (IsDlgButtonChecked(_hSelf, IDC_WRAP_AROUND_CHECKBOX) == BST_CHECKED); + // Determine the scope + bool isSelectionScope = (IsDlgButtonChecked(_hSelf, IDC_SELECTION_RADIO) == BST_CHECKED); + + SelectionInfo selection = getSelectionInfo(); + + Sci_Position startPos = isSelectionScope ? selection.startPos : 0; + size_t matchIndex = std::numeric_limits::max(); + SearchResult searchResult; searchResult.pos = -1; searchResult.length = 0; searchResult.foundText = ""; Sci_Position newPos = ::SendMessage(_hScintilla, SCI_GETCURRENTPOS, 0, 0); - size_t matchIndex = std::numeric_limits::max(); if (useListEnabled) { if (replaceListData.empty()) { @@ -2807,11 +2814,9 @@ void MultiReplace::handleReplaceButton() { return; } - SelectionInfo selection = getSelectionInfo(); - int replacements = 0; // Counter for replacements for (size_t i = 0; i < replaceListData.size(); ++i) { - if (replaceListData[i].isEnabled && replaceOne(replaceListData[i], selection, searchResult, newPos)) { + if (replaceListData[i].isEnabled && replaceOne(replaceListData[i], selection, searchResult, newPos, startPos)) { replacements++; updateCountColumns(i, -1, 1); } @@ -2855,8 +2860,7 @@ void MultiReplace::handleReplaceButton() { std::string findTextUtf8 = convertAndExtend(replaceItem.findText, replaceItem.extended); int searchFlags = (replaceItem.wholeWord * SCFIND_WHOLEWORD) | (replaceItem.matchCase * SCFIND_MATCHCASE) | (replaceItem.regex * SCFIND_REGEXP); - SelectionInfo selection = getSelectionInfo(); - bool wasReplaced = replaceOne(replaceItem, selection, searchResult, newPos); + bool wasReplaced = replaceOne(replaceItem, selection, searchResult, newPos, startPos); // Add the entered text to the combo box history addStringToComboBoxHistory(GetDlgItem(_hSelf, IDC_FIND_EDIT), replaceItem.findText); @@ -2888,13 +2892,23 @@ void MultiReplace::handleReplaceButton() { } } -bool MultiReplace::replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos) +bool MultiReplace::replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos, Sci_Position startPos) { std::string findTextUtf8 = convertAndExtend(itemData.findText, itemData.extended); int searchFlags = (itemData.wholeWord * SCFIND_WHOLEWORD) | (itemData.matchCase * SCFIND_MATCHCASE) | (itemData.regex * SCFIND_REGEXP); - searchResult = performSearchForward(findTextUtf8, searchFlags, true, selection.startPos); + searchResult = performSearchForward(findTextUtf8, searchFlags, true, startPos); + + // Modify the condition to check if the found position matches the selection (only if scope is "Selection") + bool isSelectionScope = (IsDlgButtonChecked(_hSelf, IDC_SELECTION_RADIO) == BST_CHECKED); - if (searchResult.pos == selection.startPos && searchResult.length == selection.length) { + if (isSelectionScope) { + // If the scope is "Selection" and the found text does not match the selection, do not replace + if (searchResult.pos != selection.startPos || searchResult.length != selection.length) { + return false; + } + } + + if (searchResult.pos >= 0) { bool skipReplace = false; std::string replaceTextUtf8 = convertAndExtend(itemData.replaceText, itemData.extended); std::string localReplaceTextUtf8 = wstringToString(itemData.replaceText); @@ -3095,16 +3109,28 @@ bool MultiReplace::preProcessListForReplace() { } SelectionInfo MultiReplace::getSelectionInfo() { - // Get selected text - Sci_Position selectionStart = ::SendMessage(_hScintilla, SCI_GETSELECTIONSTART, 0, 0); - Sci_Position selectionEnd = ::SendMessage(_hScintilla, SCI_GETSELECTIONEND, 0, 0); - std::vector buffer(selectionEnd - selectionStart + 1); - ::SendMessage(_hScintilla, SCI_GETSELTEXT, 0, reinterpret_cast(&buffer[0])); - std::string selectedText(&buffer[0]); - - // Calculate the length of the selected text - Sci_Position selectionLength = selectionEnd - selectionStart; + // Get the number of selections + int selectionCount = static_cast(::SendMessage(_hScintilla, SCI_GETSELECTIONS, 0, 0)); + + Sci_Position selectionStart = 0; + Sci_Position selectionEnd = 0; + std::string selectedText = ""; + + if (selectionCount == 1) { + // Single selection + selectionStart = ::SendMessage(_hScintilla, SCI_GETSELECTIONSTART, 0, 0); + selectionEnd = ::SendMessage(_hScintilla, SCI_GETSELECTIONEND, 0, 0); + Sci_Position selectionLength = selectionEnd - selectionStart; + if (selectionLength > 0) { + std::vector buffer(static_cast(selectionLength) + 1); + ::SendMessage(_hScintilla, SCI_GETSELTEXT, 0, reinterpret_cast(&buffer[0])); + selectedText = std::string(&buffer[0]); + } + } + + // For multiple selections or no selection, return empty selection info + Sci_Position selectionLength = selectionEnd - selectionStart; return SelectionInfo{ selectedText, selectionStart, selectionLength }; } diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 260a885..d8680e1 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -486,7 +486,7 @@ class MultiReplace : public StaticDialog void handleReplaceAllButton(); void handleReplaceButton(); void replaceAll(const ReplaceItemData& itemData, int& findCount, int& replaceCount); - bool replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos); + bool replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos, Sci_Position startPos); Sci_Position performReplace(const std::string& replaceTextUtf8, Sci_Position pos, Sci_Position length); Sci_Position performRegexReplace(const std::string& replaceTextUtf8, Sci_Position pos, Sci_Position length); bool MultiReplace::preProcessListForReplace(); From f74f5126745e5408fdb09aebb1580154fbfb8fc2 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sat, 28 Sep 2024 17:16:20 +0200 Subject: [PATCH 17/51] updated MesageBox to nppData._nppHandle --- src/MultiReplacePanel.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 2087ffa..613b5bd 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -269,7 +269,7 @@ bool MultiReplace::createAndShowWindows() { { DWORD dwError = GetLastError(); std::wstring errorMsg = getLangStr(L"msgbox_failed_create_control", { std::to_wstring(pair.first), std::to_wstring(dwError) }); - MessageBox(NULL, errorMsg.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR); + MessageBox(nppData._nppHandle, errorMsg.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR); return false; } @@ -1475,7 +1475,7 @@ void MultiReplace::performItemAction(POINT pt, ItemAction action) { confirmationMessage = getLangStr(L"msgbox_confirm_delete_multiple", { std::to_wstring(selectedCount) }); } - int msgBoxID = MessageBox(NULL, confirmationMessage.c_str(), getLangStr(L"msgbox_title_confirm").c_str(), MB_ICONWARNING | MB_YESNO); + int msgBoxID = MessageBox(nppData._nppHandle, confirmationMessage.c_str(), getLangStr(L"msgbox_title_confirm").c_str(), MB_ICONWARNING | MB_YESNO); if (msgBoxID == IDYES) { deleteSelectedLines(_replaceListView); } @@ -2432,7 +2432,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l { CloseDebugWindow(); // Close the Lua debug window if it is open int msgboxID = MessageBox( - NULL, + nppData._nppHandle, getLangStr(L"msgbox_confirm_replace_all").c_str(), getLangStr(L"msgbox_title_confirm").c_str(), MB_OKCANCEL @@ -4568,7 +4568,7 @@ bool MultiReplace::confirmColumnDeletion() { // Display a message box with Yes/No options and a question mark icon int msgboxID = MessageBox( - _hSelf, + nppData._nppHandle, confirmMessage.c_str(), getLangStr(L"msgbox_title_confirm").c_str(), MB_ICONQUESTION | MB_YESNO @@ -6495,7 +6495,7 @@ int MultiReplace::checkForUnsavedChanges() { // Show the MessageBox with the appropriate message int result = MessageBox( - _hSelf, + nppData._nppHandle, message.c_str(), getLangStr(L"msgbox_title_save_list").c_str(), MB_YESNOCANCEL | MB_ICONQUESTION @@ -6637,7 +6637,7 @@ void MultiReplace::checkForFileChangesAtStartup() { if (newFileHash != originalListHash) { std::wstring message = getLangStr(L"msgbox_file_modified_prompt", { shortenedFilePath }); - int response = MessageBox(_hSelf, message.c_str(), getLangStr(L"msgbox_title_reload").c_str(), MB_YESNO | MB_ICONQUESTION); + int response = MessageBox(nppData._nppHandle, message.c_str(), getLangStr(L"msgbox_title_reload").c_str(), MB_YESNO | MB_ICONQUESTION); if (response == IDYES) { replaceListData = tempListFromFile; @@ -7084,7 +7084,7 @@ void MultiReplace::saveSettings() { catch (const std::exception& ex) { // If an error occurs while writing to the INI file, we show an error message std::wstring errorMessage = getLangStr(L"msgbox_error_saving_settings", { std::wstring(ex.what(), ex.what() + strlen(ex.what())) }); - MessageBox(NULL, errorMessage.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR); + MessageBox(nppData._nppHandle, errorMessage.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR); } settingsSaved = true; } From 9cdaacee19de5cf7ff62578ad8b0e5a584d5f447 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 29 Sep 2024 11:41:10 +0200 Subject: [PATCH 18/51] adapted MessageBox Icons --- src/MultiReplacePanel.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 613b5bd..d15f234 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -269,7 +269,7 @@ bool MultiReplace::createAndShowWindows() { { DWORD dwError = GetLastError(); std::wstring errorMsg = getLangStr(L"msgbox_failed_create_control", { std::to_wstring(pair.first), std::to_wstring(dwError) }); - MessageBox(nppData._nppHandle, errorMsg.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR); + MessageBox(nppData._nppHandle, errorMsg.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); return false; } @@ -2157,7 +2157,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l wchar_t sizeText[100]; wsprintfW(sizeText, L"Window Size: %ld x %ld DUs", duWidth, duHeight); - MessageBoxW(_hSelf, sizeText, L"Window Size", MB_OK); + MessageBox(nppData._nppHandle, sizeText, L"Window Size", MB_OK); // Cleanup SelectObject(hDC, hOldFont); @@ -2435,7 +2435,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l nppData._nppHandle, getLangStr(L"msgbox_confirm_replace_all").c_str(), getLangStr(L"msgbox_title_confirm").c_str(), - MB_OKCANCEL + MB_ICONWARNING | MB_OKCANCEL ); if (msgboxID == IDOK) @@ -3442,7 +3442,7 @@ bool MultiReplace::resolveLuaSyntax(std::string& inputString, const LuaVariables lua_pop(L, 1); if (isLuaErrorDialogEnabled) { std::wstring error_message = utf8ToWString(cstr); - MessageBoxW(NULL, error_message.c_str(), getLangStr(L"msgbox_title_use_variables_syntax_error").c_str(), MB_OK | MB_SETFOREGROUND | MB_SYSTEMMODAL); + MessageBox(nppData._nppHandle, error_message.c_str(), getLangStr(L"msgbox_title_use_variables_syntax_error").c_str(), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); } lua_close(L); return false; @@ -3476,7 +3476,7 @@ bool MultiReplace::resolveLuaSyntax(std::string& inputString, const LuaVariables if (isLuaErrorDialogEnabled) { std::wstring errorMsg = getLangStr(L"msgbox_use_variables_execution_error", { utf8ToWString(inputString.c_str()) }); std::wstring errorTitle = getLangStr(L"msgbox_title_use_variables_execution_error"); - MessageBoxW(NULL, errorMsg.c_str(), errorTitle.c_str(), MB_OK); + MessageBox(nppData._nppHandle, errorMsg.c_str(), errorTitle.c_str(), MB_OK); } lua_close(L); return false; @@ -3592,7 +3592,7 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { // Convert the message from UTF-8 to UTF-16 int result = MultiByteToWideChar(CP_UTF8, 0, message.c_str(), -1, wMessage, buffer_size); if (result == 0) { - MessageBoxW(NULL, L"Error converting message", L"Error", MB_OK | MB_ICONERROR); + MessageBox(nppData._nppHandle, L"Error converting message", L"Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); return -1; } @@ -3608,7 +3608,7 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { wc.hCursor = LoadCursor(NULL, IDC_ARROW); if (!RegisterClass(&wc)) { - MessageBoxW(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); + MessageBoxW(nppData._nppHandle, L"Error registering class", L"Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); return -1; } @@ -3627,11 +3627,11 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { L"Debug Information", WS_OVERLAPPEDWINDOW, x, y, width, height, - NULL, NULL, hInstance, (LPVOID)wMessage + nppData._nppHandle, NULL, hInstance, (LPVOID)wMessage ); if (hwnd == NULL) { - MessageBoxW(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); + MessageBox(nppData._nppHandle, L"Error creating window", L"Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); return -1; } @@ -4571,7 +4571,7 @@ bool MultiReplace::confirmColumnDeletion() { nppData._nppHandle, confirmMessage.c_str(), getLangStr(L"msgbox_title_confirm").c_str(), - MB_ICONQUESTION | MB_YESNO + MB_ICONWARNING | MB_YESNO ); return (msgboxID == IDYES); // Return true if user confirmed, else false @@ -6498,7 +6498,7 @@ int MultiReplace::checkForUnsavedChanges() { nppData._nppHandle, message.c_str(), getLangStr(L"msgbox_title_save_list").c_str(), - MB_YESNOCANCEL | MB_ICONQUESTION + MB_ICONWARNING | MB_YESNOCANCEL ); if (result == IDYES) { @@ -6637,7 +6637,12 @@ void MultiReplace::checkForFileChangesAtStartup() { if (newFileHash != originalListHash) { std::wstring message = getLangStr(L"msgbox_file_modified_prompt", { shortenedFilePath }); - int response = MessageBox(nppData._nppHandle, message.c_str(), getLangStr(L"msgbox_title_reload").c_str(), MB_YESNO | MB_ICONQUESTION); + int response = MessageBox( + nppData._nppHandle, + message.c_str(), + getLangStr(L"msgbox_title_reload").c_str(), + MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND + ); if (response == IDYES) { replaceListData = tempListFromFile; @@ -7084,7 +7089,7 @@ void MultiReplace::saveSettings() { catch (const std::exception& ex) { // If an error occurs while writing to the INI file, we show an error message std::wstring errorMessage = getLangStr(L"msgbox_error_saving_settings", { std::wstring(ex.what(), ex.what() + strlen(ex.what())) }); - MessageBox(nppData._nppHandle, errorMessage.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR); + MessageBox(nppData._nppHandle, errorMessage.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); } settingsSaved = true; } From d585c6087d347036e2e101635280d6be1eec80d6 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 29 Sep 2024 13:22:16 +0200 Subject: [PATCH 19/51] MessagBox updates to MB_SETFOREGROUND --- src/MultiReplacePanel.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index d15f234..a940498 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -269,7 +269,7 @@ bool MultiReplace::createAndShowWindows() { { DWORD dwError = GetLastError(); std::wstring errorMsg = getLangStr(L"msgbox_failed_create_control", { std::to_wstring(pair.first), std::to_wstring(dwError) }); - MessageBox(nppData._nppHandle, errorMsg.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + MessageBox(nppData._nppHandle, errorMsg.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR | MB_SETFOREGROUND); return false; } @@ -3442,7 +3442,7 @@ bool MultiReplace::resolveLuaSyntax(std::string& inputString, const LuaVariables lua_pop(L, 1); if (isLuaErrorDialogEnabled) { std::wstring error_message = utf8ToWString(cstr); - MessageBox(nppData._nppHandle, error_message.c_str(), getLangStr(L"msgbox_title_use_variables_syntax_error").c_str(), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + MessageBox(nppData._nppHandle, error_message.c_str(), getLangStr(L"msgbox_title_use_variables_syntax_error").c_str(), MB_OK | MB_ICONERROR | MB_SETFOREGROUND); } lua_close(L); return false; @@ -3592,7 +3592,7 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { // Convert the message from UTF-8 to UTF-16 int result = MultiByteToWideChar(CP_UTF8, 0, message.c_str(), -1, wMessage, buffer_size); if (result == 0) { - MessageBox(nppData._nppHandle, L"Error converting message", L"Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + MessageBox(nppData._nppHandle, L"Error converting message", L"Error", MB_OK | MB_ICONERROR | MB_SETFOREGROUND); return -1; } @@ -3608,7 +3608,7 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { wc.hCursor = LoadCursor(NULL, IDC_ARROW); if (!RegisterClass(&wc)) { - MessageBoxW(nppData._nppHandle, L"Error registering class", L"Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + MessageBox(nppData._nppHandle, L"Error registering class", L"Error", MB_OK | MB_ICONERROR | MB_SETFOREGROUND); return -1; } @@ -3631,7 +3631,7 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { ); if (hwnd == NULL) { - MessageBox(nppData._nppHandle, L"Error creating window", L"Error", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + MessageBoxW(nppData._nppHandle, L"Error creating window", L"Error", MB_OK | MB_ICONERROR | MB_SETFOREGROUND); return -1; } @@ -7089,7 +7089,7 @@ void MultiReplace::saveSettings() { catch (const std::exception& ex) { // If an error occurs while writing to the INI file, we show an error message std::wstring errorMessage = getLangStr(L"msgbox_error_saving_settings", { std::wstring(ex.what(), ex.what() + strlen(ex.what())) }); - MessageBox(nppData._nppHandle, errorMessage.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR | MB_SYSTEMMODAL); + MessageBox(nppData._nppHandle, errorMessage.c_str(), getLangStr(L"msgbox_title_error").c_str(), MB_OK | MB_ICONERROR | MB_SETFOREGROUND); } settingsSaved = true; } From feac2414f3ce390e3e51ce53053c8c9d55f93d8b Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 29 Sep 2024 14:01:54 +0200 Subject: [PATCH 20/51] updated Tab order --- src/StaticDialog/resource.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index 3d6b94d..6995b39 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -35,12 +35,12 @@ #define IDC_REPLACE_ALL_SMALL_BUTTON 5009 #define IDC_2_BUTTONS_MODE 5010 #define IDC_FIND_BUTTON 5011 -#define IDC_FIND_NEXT_BUTTON 5012 -#define IDC_FIND_PREV_BUTTON 5013 +#define IDC_FIND_PREV_BUTTON 5012 +#define IDC_FIND_NEXT_BUTTON 5013 #define IDC_MARK_BUTTON 5014 #define IDC_MARK_MATCHES_BUTTON 5015 -#define IDC_CLEAR_MARKS_BUTTON 5016 -#define IDC_COPY_MARKED_TEXT_BUTTON 5017 +#define IDC_COPY_MARKED_TEXT_BUTTON 5016 +#define IDC_CLEAR_MARKS_BUTTON 5017 #define IDC_REPLACE_ALL_IN_LIST_BUTTON 5018 #define IDC_DELETE_REPLACE_ITEM_BUTTON 5019 #define IDC_LOAD_FROM_CSV_BUTTON 5020 From 0e226e38906f7a664d85cdd0cc521350b9d2fc99 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 30 Sep 2024 19:41:14 +0200 Subject: [PATCH 21/51] updated ini Parameter back to 'Height' --- src/MultiReplacePanel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index a940498..be3abea 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -6978,10 +6978,10 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { } outFile << wstringToString(L"[Window]\n"); - outFile << wstringToString(L"Width=" + std::to_wstring(width) + L"\n"); outFile << wstringToString(L"PosX=" + std::to_wstring(posX) + L"\n"); outFile << wstringToString(L"PosY=" + std::to_wstring(posY) + L"\n"); - outFile << wstringToString(L"UseListOnHeight=" + std::to_wstring(useListOnHeight) + L"\n"); + outFile << wstringToString(L"Width=" + std::to_wstring(width) + L"\n"); + outFile << wstringToString(L"Height=" + std::to_wstring(useListOnHeight) + L"\n"); // Save transparency settings outFile << wstringToString(L"ForegroundTransparency=" + std::to_wstring(foregroundTransparency) + L"\n"); @@ -7225,7 +7225,7 @@ void MultiReplace::loadUIConfigFromIni() { int width = std::max(savedWidth, MIN_WIDTH); // Load useListOnHeight from INI file - useListOnHeight = readIntFromIniFile(iniFilePath, L"Window", L"UseListOnHeight", MIN_HEIGHT); + useListOnHeight = readIntFromIniFile(iniFilePath, L"Window", L"Height", MIN_HEIGHT); useListOnHeight = std::max(useListOnHeight, MIN_HEIGHT); // Ensure minimum height // Set windowRect based on Use List state From ffcf5893a86dc16930904f3e84fdb54e8ba496b0 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Tue, 1 Oct 2024 20:22:05 +0200 Subject: [PATCH 22/51] fixed tooltip repositioning on column resize --- src/MultiReplacePanel.cpp | 74 ++++++++++++++++++++++++++++----------- src/MultiReplacePanel.h | 3 +- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index be3abea..f9469c6 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -562,24 +562,24 @@ void MultiReplace::adjustWindowSize() { #pragma region ListView -HWND MultiReplace::CreateHeaderTooltip(HWND hwndParent) { - HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, +HWND MultiReplace::CreateHeaderTooltip(HWND hwndParent) +{ + HWND hwndTT = CreateWindowEx( + WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, NULL, GetModuleHandle(NULL), - NULL); + NULL + ); - SetWindowPos(hwndTT, - HWND_TOPMOST, - 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + if (hwndTT) + { + SendMessage(hwndTT, TTM_ACTIVATE, TRUE, 0); + } return hwndTT; } @@ -594,9 +594,10 @@ void MultiReplace::AddHeaderTooltip(HWND hwndTT, HWND hwndHeader, int columnInde ti.hwnd = hwndHeader; ti.hinst = GetModuleHandle(NULL); ti.uId = columnIndex; - ti.lpszText = (LPWSTR)pszText; + ti.lpszText = const_cast(pszText); ti.rect = rect; + SendMessage(hwndTT, TTM_DELTOOL, 0, (LPARAM)&ti); SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti); } @@ -680,13 +681,13 @@ void MultiReplace::createListViewColumns(HWND listView) { //Adding Tooltips HWND hwndHeader = ListView_GetHeader(listView); - HWND hwndTT = CreateHeaderTooltip(hwndHeader); + _hHeaderTooltip = CreateHeaderTooltip(hwndHeader); - AddHeaderTooltip(hwndTT, hwndHeader, 6, getLangStrLPWSTR(L"tooltip_header_whole_word")); - AddHeaderTooltip(hwndTT, hwndHeader, 7, getLangStrLPWSTR(L"tooltip_header_match_case")); - AddHeaderTooltip(hwndTT, hwndHeader, 8, getLangStrLPWSTR(L"tooltip_header_use_variables")); - AddHeaderTooltip(hwndTT, hwndHeader, 9, getLangStrLPWSTR(L"tooltip_header_extended")); - AddHeaderTooltip(hwndTT, hwndHeader, 10, getLangStrLPWSTR(L"tooltip_header_regex")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 6, getLangStrLPWSTR(L"tooltip_header_whole_word")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 7, getLangStrLPWSTR(L"tooltip_header_match_case")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 8, getLangStrLPWSTR(L"tooltip_header_use_variables")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 9, getLangStrLPWSTR(L"tooltip_header_extended")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 10, getLangStrLPWSTR(L"tooltip_header_regex")); } @@ -768,7 +769,20 @@ void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { SendMessage(widths.listView, WM_SETREDRAW, TRUE, 0); //InvalidateRect(widths.listView, NULL, TRUE); //UpdateWindow(widths.listView); +} + +void MultiReplace::updateListViewTooltips() +{ + HWND hwndHeader = ListView_GetHeader(_replaceListView); + if (!hwndHeader || !_hHeaderTooltip) + return; + // Re-add tooltips for columns 6 to 10 + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 6, getLangStrLPWSTR(L"tooltip_header_whole_word")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 7, getLangStrLPWSTR(L"tooltip_header_match_case")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 8, getLangStrLPWSTR(L"tooltip_header_use_variables")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 9, getLangStrLPWSTR(L"tooltip_header_extended")); + AddHeaderTooltip(_hHeaderTooltip, hwndHeader, 10, getLangStrLPWSTR(L"tooltip_header_regex")); } void MultiReplace::handleCopyBack(NMITEMACTIVATE* pnmia) { @@ -1306,17 +1320,37 @@ LRESULT CALLBACK MultiReplace::ListViewSubclassProc(HWND hwnd, UINT msg, WPARAM case WM_NOTIFY: { NMHDR* pnmhdr = reinterpret_cast(lParam); if (pnmhdr->hwndFrom == ListView_GetHeader(hwnd)) { - UINT code = static_cast(pnmhdr->code); - if (code == HDN_ITEMCHANGEDW || code == HDN_ITEMCHANGEDA) { + int code = static_cast(static_cast(pnmhdr->code)); + + // These values are derived from the HDN_ITEMCHANGEDW and HDN_ITEMCHANGEDA constants: + // HDN_ITEMCHANGEDW = 0U - 300U - 21 = -321 + // HDN_ITEMCHANGEDA = 0U - 300U - 1 = -301 + // The constants are not used directly to avoid unsigned arithmetic overflow warnings. + if (code == (int(0) - 300 - 21) || code == (int(0) - 300 - 1)) { + // If there is an active edit control, destroy it when the header is changed if (pThis->hwndEdit && IsWindow(pThis->hwndEdit)) { DestroyWindow(pThis->hwndEdit); pThis->hwndEdit = NULL; } + + // Set a timer to defer the tooltip update, ensuring the column resize is complete + SetTimer(hwnd, 1, 100, NULL); // Timer ID 1, 100ms delay + } } break; } + case WM_TIMER: { + if (wParam == 1) { // Check if it's the timer we set + KillTimer(hwnd, 1); // Stop the timer + + // Update the tooltips now that the columns have been resized + pThis->updateListViewTooltips(); + } + break; + } + default: // No special handling needed break; diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index d8680e1..9db69bf 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -358,6 +358,7 @@ class MultiReplace : public StaticDialog HWND _hStatusMessage; HFONT _hFont; COLORREF _statusMessageColor; + HWND _hHeaderTooltip; // Handle to the tooltip for the ListView header // ContextMenuInfo structure instance POINT _contextMenuClickPoint; @@ -454,6 +455,7 @@ class MultiReplace : public StaticDialog void insertReplaceListItem(const ReplaceItemData& itemData); int calcDynamicColWidth(const CountColWidths& widths); void updateListViewAndColumns(HWND listView, LPARAM lParam); + void updateListViewTooltips(); void handleCopyBack(NMITEMACTIVATE* pnmia); void shiftListItem(HWND listView, const Direction& direction); void handleDeletion(NMITEMACTIVATE* pnmia); @@ -561,7 +563,6 @@ class MultiReplace : public StaticDialog void updateHeaderSelection(); void updateHeaderSortDirection(); void showStatusMessage(const std::wstring& messageText, COLORREF color); - void calculateCharacterWidths(); std::wstring MultiReplace::getShortenedFilePath(const std::wstring& path, int maxLength, HDC hDC = nullptr); void showListFilePath(); void displayResultCentered(size_t posStart, size_t posEnd, bool isDownwards); From 7e89959925a7c47bd28d320674414ebbc4db0ca4 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Thu, 3 Oct 2024 19:59:03 +0200 Subject: [PATCH 23/51] Added DPI Manager and adjusted panel scaling --- src/DPIManager.cpp | 95 ++++++++ src/DPIManager.h | 53 ++++ src/MultiReplacePanel.cpp | 257 ++++++++++++++------ src/MultiReplacePanel.h | 6 + src/StaticDialog/MultiReplacePanel.manifest | 8 + src/StaticDialog/resource.rc | 4 +- vs.proj/MultiReplace.vcxproj | 2 + vs.proj/MultiReplace.vcxproj.filters | 2 + 8 files changed, 346 insertions(+), 81 deletions(-) create mode 100644 src/DPIManager.cpp create mode 100644 src/DPIManager.h create mode 100644 src/StaticDialog/MultiReplacePanel.manifest diff --git a/src/DPIManager.cpp b/src/DPIManager.cpp new file mode 100644 index 0000000..e60ac27 --- /dev/null +++ b/src/DPIManager.cpp @@ -0,0 +1,95 @@ +// This file is part of Notepad++ project +// Copyright (C)2023 Thomas Knoefel + +#include "DPIManager.h" + +// Constructor: Initializes DPI values. +DPIManager::DPIManager(HWND hwnd) + : _hwnd(hwnd), _dpiX(96), _dpiY(96) // Default DPI is 96. +{ + init(); +} + +// Destructor: Currently not used, but can be expanded for resource management. +DPIManager::~DPIManager() +{ + // No dynamic resources to release at this time. +} + +// Initializes the DPI values using Win32 APIs. +void DPIManager::init() +{ + UINT dpiX = 96, dpiY = 96; // Default DPI. + + // Attempt to load Shcore.dll for modern DPI functions. + HMODULE hShcore = LoadLibrary(TEXT("Shcore.dll")); + if (hShcore) + { + typedef HRESULT(WINAPI* GetDpiForMonitorFunc)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); + GetDpiForMonitorFunc pGetDpiForMonitor = (GetDpiForMonitorFunc)GetProcAddress(hShcore, "GetDpiForMonitor"); + + if (pGetDpiForMonitor) + { + HMONITOR hMonitor = MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST); + if (SUCCEEDED(pGetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) + { + // Successfully retrieved DPI values. + } + } + + FreeLibrary(hShcore); + } + else + { + // Fallback for older Windows versions. + HDC hdc = GetDC(_hwnd); + if (hdc) + { + dpiX = GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(_hwnd, hdc); + } + } + + _dpiX = dpiX; + _dpiY = dpiY; +} + +// Updates the DPI values, typically called when DPI changes. +void DPIManager::updateDPI(HWND hwnd) +{ + _hwnd = hwnd; // Update window handle in case it has changed. + init(); // Reinitialize DPI values. +} + +// Scales a RECT structure. +void DPIManager::scaleRect(RECT* pRect) const +{ + if (pRect) + { + pRect->left = scaleX(pRect->left); + pRect->right = scaleX(pRect->right); + pRect->top = scaleY(pRect->top); + pRect->bottom = scaleY(pRect->bottom); + } +} + +// Scales a POINT structure. +void DPIManager::scalePoint(POINT* pPoint) const +{ + if (pPoint) + { + pPoint->x = scaleX(pPoint->x); + pPoint->y = scaleY(pPoint->y); + } +} + +// Scales a SIZE structure. +void DPIManager::scaleSize(SIZE* pSize) const +{ + if (pSize) + { + pSize->cx = scaleX(pSize->cx); + pSize->cy = scaleY(pSize->cy); + } +} diff --git a/src/DPIManager.h b/src/DPIManager.h new file mode 100644 index 0000000..6cbb1f4 --- /dev/null +++ b/src/DPIManager.h @@ -0,0 +1,53 @@ +// This file is part of Notepad++ project +// Copyright (C)2023 Thomas Knoefel + +#ifndef DPIMANAGER_H +#define DPIMANAGER_H + +#include +#include + +// DPIManager class handles DPI awareness and scaling for UI elements. +class DPIManager +{ +public: + // Constructor initializes DPI values based on the provided window handle. + DPIManager(HWND hwnd); + + // Destructor (if needed for resource management). + ~DPIManager(); + + // Retrieves the current DPI values. + int getDPIX() const { return _dpiX; } + int getDPIY() const { return _dpiY; } + + // Converts raw pixels to scaled pixels. + int scaleX(int x) const { return MulDiv(x, _dpiX, 96); } + int scaleY(int y) const { return MulDiv(y, _dpiY, 96); } + + // Converts scaled pixels back to raw pixels. + int unscaleX(int x) const { return MulDiv(x, 96, _dpiX); } + int unscaleY(int y) const { return MulDiv(y, 96, _dpiY); } + + // Scales a RECT structure. + void scaleRect(RECT* pRect) const; + + // Scales a POINT structure. + void scalePoint(POINT* pPoint) const; + + // Scales a SIZE structure. + void scaleSize(SIZE* pSize) const; + + // Updates DPI values (e.g., after a DPI change event). + void updateDPI(HWND hwnd); + +private: + HWND _hwnd; // Handle to the window. + int _dpiX; // Horizontal DPI. + int _dpiY; // Vertical DPI. + + // Initializes the DPI values. + void init(); +}; + +#endif // DPIMANAGER_H diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index f9469c6..e013076 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -21,6 +21,7 @@ #include "Notepad_plus_msgs.h" #include "PluginDefinition.h" #include "Scintilla.h" +#include "DPIManager.h" #include #include @@ -76,6 +77,59 @@ HWND MultiReplace::hDebugWnd = NULL; #pragma warning(disable: 6262) +void MultiReplace::applyFonts() +{ + if (!dpiMgr) return; + + // Create a standard scaled font + HFONT hStandardFont = CreateFont( + dpiMgr->scaleY(12), // Font height + 0, // Font width (0 means default) + 0, // Escapement + 0, // Orientation + FW_NORMAL, // Weight + FALSE, // Italic + FALSE, // Underline + FALSE, // Strikeout + DEFAULT_CHARSET, // Char set + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, + L"Segoe UI" // Font name + ); + + // Apply the standard font to all controls + for (const auto& pair : ctrlMap) + { + SendMessage(GetDlgItem(_hSelf, pair.first), WM_SETFONT, (WPARAM)hStandardFont, TRUE); + } + + // Create and apply a bold font for specific controls + HFONT hBoldFont = CreateFont( + dpiMgr->scaleY(14), // Larger font height + 0, + 0, + 0, + FW_BOLD, // Bold weight + FALSE, + FALSE, + FALSE, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, + L"Courier New" // Font name + ); + + // Apply the bold font to specific controls + SendMessage(GetDlgItem(_hSelf, IDC_SWAP_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_COLUMN_COPY_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); +} + #pragma region Initialization void MultiReplace::initializeWindowSize() { @@ -114,86 +168,88 @@ RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { return minSize; } -void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) { - int buttonX = windowWidth - 45 - 160; - int checkbox2X = buttonX + 173; - int swapButtonX = windowWidth - 45 - 160 - 33; - int comboWidth = windowWidth - 365; - int frameX = windowWidth - 320; - int listWidth = windowWidth - 260; - int listHeight = windowHeight - 310; - int checkboxX = buttonX - 105; - - // Static positions and sizes - ctrlMap[IDC_STATIC_FIND] = { 14, 19, 100, 24, WC_STATIC, getLangStrLPCWSTR(L"panel_find_what"), SS_RIGHT, NULL }; - ctrlMap[IDC_STATIC_REPLACE] = { 14, 54, 100, 24, WC_STATIC, getLangStrLPCWSTR(L"panel_replace_with"), SS_RIGHT }; - - ctrlMap[IDC_WHOLE_WORD_CHECKBOX] = { 20, 95, 198, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_match_whole_word_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_MATCH_CASE_CHECKBOX] = { 20, 126, 198, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_match_case"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_USE_VARIABLES_CHECKBOX] = { 20, 157, 167, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_use_variables"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_USE_VARIABLES_HELP] = { 190, 158, 25, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_help"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_FIRST_CHECKBOX] = { 20, 189, 198, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_first_match_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_WRAP_AROUND_CHECKBOX] = { 20, 220, 198, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_wrap_around"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - - ctrlMap[IDC_SEARCH_MODE_GROUP] = { 225, 99, 200, 130, WC_BUTTON, getLangStrLPCWSTR(L"panel_search_mode"), BS_GROUPBOX, NULL }; - ctrlMap[IDC_NORMAL_RADIO] = { 235, 126, 180, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_normal"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; - ctrlMap[IDC_EXTENDED_RADIO] = { 235, 157, 180, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_extended"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REGEX_RADIO] = { 235, 188, 180, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_regular_expression"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - - ctrlMap[IDC_SCOPE_GROUP] = { 440, 99, 247, 163, WC_BUTTON, getLangStrLPCWSTR(L"panel_scope"), BS_GROUPBOX, NULL }; - ctrlMap[IDC_ALL_TEXT_RADIO] = { 450, 126, 230, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; - ctrlMap[IDC_SELECTION_RADIO] = { 450, 157, 230, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_MODE_RADIO] = { 450, 188, 50, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_NUM_STATIC] = { 450, 227, 30, 25, WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL}; - ctrlMap[IDC_COLUMN_NUM_EDIT] = { 482, 227, 50, 20, WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL , getLangStrLPCWSTR(L"tooltip_columns") }; - ctrlMap[IDC_DELIMITER_STATIC] = { 538, 227, 40, 25, WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; - ctrlMap[IDC_DELIMITER_EDIT] = { 580, 227, 30, 20, WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL , getLangStrLPCWSTR(L"tooltip_delimiter") }; - ctrlMap[IDC_QUOTECHAR_STATIC] = { 616, 227, 40, 25, WC_STATIC, getLangStrLPCWSTR(L"panel_quote"), SS_RIGHT, NULL }; - ctrlMap[IDC_QUOTECHAR_EDIT] = { 658, 227, 15, 20, WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL , getLangStrLPCWSTR(L"tooltip_quote") }; - - ctrlMap[IDC_COLUMN_SORT_DESC_BUTTON] = { 508, 186, 17, 25, WC_BUTTON, symbolSortDesc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_descending") }; - ctrlMap[IDC_COLUMN_SORT_ASC_BUTTON] = { 526, 186, 17, 25, WC_BUTTON, symbolSortAsc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_ascending") }; - ctrlMap[IDC_COLUMN_DROP_BUTTON] = { 554, 186, 25, 25, WC_BUTTON, L"✖", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_drop_columns") }; - ctrlMap[IDC_COLUMN_COPY_BUTTON] = { 590, 186, 25, 25, WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_columns") }; - ctrlMap[IDC_COLUMN_HIGHLIGHT_BUTTON] = { 626, 186, 50, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_show"), BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_column_highlight") }; - - ctrlMap[IDC_STATUS_MESSAGE] = { 14, 260, 630, 24, WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; +void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) +{ + // Calculate dimensions without scaling + int buttonX = windowWidth - dpiMgr->scaleX(36 + 128); + int checkbox2X = buttonX + dpiMgr->scaleX(138); + int swapButtonX = windowWidth - dpiMgr->scaleX(36 + 128 + 26); + int comboWidth = windowWidth - dpiMgr->scaleX(292); + int frameX = windowWidth - dpiMgr->scaleX(256); + int listWidth = windowWidth - dpiMgr->scaleX(260); + int listHeight = windowHeight - dpiMgr->scaleX(310); + int checkboxX = buttonX - dpiMgr->scaleX(84); + + // Apply scaling only when assigning to ctrlMap + ctrlMap[IDC_STATIC_FIND] = { dpiMgr->scaleX(11), dpiMgr->scaleY(15), dpiMgr->scaleX(80), dpiMgr->scaleY(19), WC_STATIC, getLangStrLPCWSTR(L"panel_find_what"), SS_RIGHT, NULL }; + ctrlMap[IDC_STATIC_REPLACE] = { dpiMgr->scaleX(11), dpiMgr->scaleY(43), dpiMgr->scaleX(80), dpiMgr->scaleY(19), WC_STATIC, getLangStrLPCWSTR(L"panel_replace_with"), SS_RIGHT }; + + ctrlMap[IDC_WHOLE_WORD_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(76), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_whole_word_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_MATCH_CASE_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(101), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_case"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_USE_VARIABLES_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(126), dpiMgr->scaleX(134), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_variables"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_USE_VARIABLES_HELP] = { dpiMgr->scaleX(152), dpiMgr->scaleY(126), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_help"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_FIRST_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(151), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_first_match_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_WRAP_AROUND_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(176), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_wrap_around"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + + ctrlMap[IDC_SEARCH_MODE_GROUP] = { dpiMgr->scaleX(180), dpiMgr->scaleY(79), dpiMgr->scaleX(160), dpiMgr->scaleY(104), WC_BUTTON, getLangStrLPCWSTR(L"panel_search_mode"), BS_GROUPBOX, NULL }; + ctrlMap[IDC_NORMAL_RADIO] = { dpiMgr->scaleX(188), dpiMgr->scaleY(101), dpiMgr->scaleX(144), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_normal"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; + ctrlMap[IDC_EXTENDED_RADIO] = { dpiMgr->scaleX(188), dpiMgr->scaleY(126), dpiMgr->scaleX(144), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_extended"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REGEX_RADIO] = { dpiMgr->scaleX(188), dpiMgr->scaleY(150), dpiMgr->scaleX(144), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_regular_expression"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + + ctrlMap[IDC_SCOPE_GROUP] = { dpiMgr->scaleX(352), dpiMgr->scaleY(79), dpiMgr->scaleX(198), dpiMgr->scaleY(130), WC_BUTTON, getLangStrLPCWSTR(L"panel_scope"), BS_GROUPBOX, NULL }; + ctrlMap[IDC_ALL_TEXT_RADIO] = { dpiMgr->scaleX(360), dpiMgr->scaleY(101), dpiMgr->scaleX(184), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; + ctrlMap[IDC_SELECTION_RADIO] = { dpiMgr->scaleX(360), dpiMgr->scaleY(126), dpiMgr->scaleX(184), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_MODE_RADIO] = { dpiMgr->scaleX(360), dpiMgr->scaleY(150), dpiMgr->scaleX(40), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_NUM_STATIC] = { dpiMgr->scaleX(360), dpiMgr->scaleY(182), dpiMgr->scaleX(24), dpiMgr->scaleY(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; + ctrlMap[IDC_COLUMN_NUM_EDIT] = { dpiMgr->scaleX(386), dpiMgr->scaleY(182), dpiMgr->scaleX(40), dpiMgr->scaleY(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; + ctrlMap[IDC_DELIMITER_STATIC] = { dpiMgr->scaleX(430), dpiMgr->scaleY(182), dpiMgr->scaleX(32), dpiMgr->scaleY(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; + ctrlMap[IDC_DELIMITER_EDIT] = { dpiMgr->scaleX(464), dpiMgr->scaleY(182), dpiMgr->scaleX(24), dpiMgr->scaleY(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_delimiter") }; + ctrlMap[IDC_QUOTECHAR_STATIC] = { dpiMgr->scaleX(493), dpiMgr->scaleY(182), dpiMgr->scaleX(32), dpiMgr->scaleY(20), WC_STATIC, getLangStrLPCWSTR(L"panel_quote"), SS_RIGHT, NULL }; + ctrlMap[IDC_QUOTECHAR_EDIT] = { dpiMgr->scaleX(526), dpiMgr->scaleY(182), dpiMgr->scaleX(12), dpiMgr->scaleY(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_quote") }; + + ctrlMap[IDC_COLUMN_SORT_DESC_BUTTON] = { dpiMgr->scaleX(406), dpiMgr->scaleY(149), dpiMgr->scaleX(14), dpiMgr->scaleY(20), WC_BUTTON, symbolSortDesc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_descending") }; + ctrlMap[IDC_COLUMN_SORT_ASC_BUTTON] = { dpiMgr->scaleX(421), dpiMgr->scaleY(149), dpiMgr->scaleX(14), dpiMgr->scaleY(20), WC_BUTTON, symbolSortAsc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_ascending") }; + ctrlMap[IDC_COLUMN_DROP_BUTTON] = { dpiMgr->scaleX(443), dpiMgr->scaleY(149), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, L"✖", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_drop_columns") }; + ctrlMap[IDC_COLUMN_COPY_BUTTON] = { dpiMgr->scaleX(472), dpiMgr->scaleY(149), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_columns") }; + ctrlMap[IDC_COLUMN_HIGHLIGHT_BUTTON] = { dpiMgr->scaleX(501), dpiMgr->scaleY(149), dpiMgr->scaleX(40), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_show"), BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_column_highlight") }; + + ctrlMap[IDC_STATUS_MESSAGE] = { dpiMgr->scaleX(11), dpiMgr->scaleY(208), dpiMgr->scaleX(504), dpiMgr->scaleY(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; // Dynamic positions and sizes - ctrlMap[IDC_FIND_EDIT] = { 120, 19, comboWidth, 200, WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_EDIT] = { 120, 54, comboWidth, 200, WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; - ctrlMap[IDC_SWAP_BUTTON] = { swapButtonX, 33, 28, 34, WC_BUTTON, L"⇅", BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COPY_TO_LIST_BUTTON] = { buttonX, 19, 160, 60, WC_BUTTON, getLangStrLPCWSTR(L"panel_add_into_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_STATIC_FRAME] = { frameX, 99, 285, 163, WC_BUTTON, L"", BS_GROUPBOX, NULL }; - ctrlMap[IDC_REPLACE_ALL_BUTTON] = { buttonX, 118, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_all"), BS_SPLITBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_BUTTON] = { buttonX, 118, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_replace"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_ALL_SMALL_BUTTON] = { buttonX + 125, 118, 35, 30, WC_BUTTON, L"↻", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_replace_all")}; - ctrlMap[IDC_2_BUTTONS_MODE] = { checkbox2X, 118, 25, 25, WC_BUTTON, L"", BS_AUTOCHECKBOX | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_2_buttons_mode") }; - ctrlMap[IDC_FIND_BUTTON] = { buttonX, 153, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_find_next"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_FIND_EDIT] = { dpiMgr->scaleX(96), dpiMgr->scaleY(15), comboWidth, dpiMgr->scaleY(160), WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_EDIT] = { dpiMgr->scaleX(96), dpiMgr->scaleY(43), comboWidth, dpiMgr->scaleY(160), WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; + ctrlMap[IDC_SWAP_BUTTON] = { swapButtonX, dpiMgr->scaleY(26), dpiMgr->scaleX(22), dpiMgr->scaleY(27), WC_BUTTON, L"⇅", BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COPY_TO_LIST_BUTTON] = { buttonX, dpiMgr->scaleY(15), dpiMgr->scaleX(128), dpiMgr->scaleY(48), WC_BUTTON, getLangStrLPCWSTR(L"panel_add_into_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_STATIC_FRAME] = { frameX, dpiMgr->scaleY(79), dpiMgr->scaleX(228), dpiMgr->scaleY(130), WC_BUTTON, L"", BS_GROUPBOX, NULL }; + ctrlMap[IDC_REPLACE_ALL_BUTTON] = { buttonX, dpiMgr->scaleY(94), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_all"), BS_SPLITBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_BUTTON] = { buttonX, dpiMgr->scaleY(94), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_ALL_SMALL_BUTTON] = { buttonX + dpiMgr->scaleX(100), dpiMgr->scaleY(94), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"↻", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_replace_all") }; + ctrlMap[IDC_2_BUTTONS_MODE] = { checkbox2X, dpiMgr->scaleY(94), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, L"", BS_AUTOCHECKBOX | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_2_buttons_mode") }; + ctrlMap[IDC_FIND_BUTTON] = { buttonX, dpiMgr->scaleY(122), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_find_next"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[ID_STATISTICS_COLUMNS] = { dpiMgr->scaleX(2), dpiMgr->scaleY(228), dpiMgr->scaleX(14), dpiMgr->scaleY(19), WC_BUTTON, L"▶", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, getLangStrLPCWSTR(L"tooltip_display_statistics_columns") }; findNextButtonText = L"▼ " + getLangStr(L"panel_find_next_small"); - ctrlMap[IDC_FIND_NEXT_BUTTON] = ControlInfo{ buttonX + 40, 153, 120, 30, WC_BUTTON, findNextButtonText.c_str(), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - - ctrlMap[IDC_FIND_PREV_BUTTON] = { buttonX, 153, 35, 30, WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_MARK_BUTTON] = { buttonX, 188, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_MARK_MATCHES_BUTTON] = { buttonX, 188, 120, 30, WC_BUTTON,getLangStrLPCWSTR(L"panel_mark_matches_small"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COPY_MARKED_TEXT_BUTTON] = { buttonX + 125, 188, 35, 30, WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_marked_text") }; - ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, 223, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, 284, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_LOAD_LIST_BUTTON] = { buttonX, 284, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_NEW_LIST_BUTTON] = { buttonX + 125, 284, 35, 30, WC_BUTTON, L"➕", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_new_list") }; - ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, 319, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_SAVE_BUTTON] = { buttonX, 319, 35, 30, WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; - ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + 40, 319, 120, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_EXPORT_BASH_BUTTON] = { buttonX, 354, 160, 30, WC_BUTTON, getLangStrLPCWSTR(L"panel_export_to_bash"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_UP_BUTTON] = { buttonX + 5, 404, 30, 30, WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; - ctrlMap[IDC_DOWN_BUTTON] = { buttonX + 5, 404 + 30 + 5, 30, 30, WC_BUTTON, L"▼", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; - ctrlMap[IDC_SHIFT_FRAME] = { buttonX, 404 - 14, 165, 85, WC_BUTTON, L"", BS_GROUPBOX, NULL }; - ctrlMap[IDC_SHIFT_TEXT] = { buttonX + 38, 404 + 20, 120, 20, WC_STATIC, getLangStrLPCWSTR(L"panel_shift_lines"), SS_LEFT, NULL }; - ctrlMap[IDC_REPLACE_LIST] = { 20, 284, listWidth, listHeight, WC_LISTVIEW, NULL, LVS_REPORT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_VSCROLL | LVS_SHOWSELALWAYS, NULL }; - ctrlMap[IDC_PATH_DISPLAY] = { 20, 284 + listHeight + 5, listWidth, 24, WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; - ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, 175, 95, 25, WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[ID_STATISTICS_COLUMNS] = { 2, 285, 17, 24, WC_BUTTON, L"▶", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, getLangStrLPCWSTR(L"tooltip_display_statistics_columns") }; + ctrlMap[IDC_FIND_NEXT_BUTTON] = ControlInfo{ buttonX + dpiMgr->scaleX(32), dpiMgr->scaleY(122), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, findNextButtonText.c_str(), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + + ctrlMap[IDC_FIND_PREV_BUTTON] = { buttonX, dpiMgr->scaleY(122), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_MARK_BUTTON] = { buttonX, dpiMgr->scaleY(150), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_MARK_MATCHES_BUTTON] = { buttonX, dpiMgr->scaleY(150), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches_small"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COPY_MARKED_TEXT_BUTTON] = { buttonX + dpiMgr->scaleX(100), dpiMgr->scaleY(150), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_marked_text") }; + ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, dpiMgr->scaleY(178), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, dpiMgr->scaleY(227), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_LOAD_LIST_BUTTON] = { buttonX, dpiMgr->scaleY(227), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_NEW_LIST_BUTTON] = { buttonX + dpiMgr->scaleX(100), dpiMgr->scaleY(227), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"➕", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_new_list") }; + ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, dpiMgr->scaleY(255), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_SAVE_BUTTON] = { buttonX, dpiMgr->scaleY(255), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; + ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + dpiMgr->scaleX(32), dpiMgr->scaleY(255), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_EXPORT_BASH_BUTTON] = { buttonX, dpiMgr->scaleY(283), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_export_to_bash"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_UP_BUTTON] = { buttonX + dpiMgr->scaleX(4), dpiMgr->scaleY(323), dpiMgr->scaleX(24), dpiMgr->scaleY(24), WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; + ctrlMap[IDC_DOWN_BUTTON] = { buttonX + dpiMgr->scaleX(4), dpiMgr->scaleY(323 + 24 + 4), dpiMgr->scaleX(24), dpiMgr->scaleY(24), WC_BUTTON, L"▼", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; + ctrlMap[IDC_SHIFT_FRAME] = { buttonX, dpiMgr->scaleY(323 - 11), dpiMgr->scaleX(132), dpiMgr->scaleY(68), WC_BUTTON, L"", BS_GROUPBOX, NULL }; + ctrlMap[IDC_SHIFT_TEXT] = { buttonX + dpiMgr->scaleX(30), dpiMgr->scaleY(323 + 16), dpiMgr->scaleX(96), dpiMgr->scaleY(16), WC_STATIC, getLangStrLPCWSTR(L"panel_shift_lines"), SS_LEFT, NULL }; + ctrlMap[IDC_REPLACE_LIST] = { dpiMgr->scaleX(20), dpiMgr->scaleY(227), listWidth, listHeight, WC_LISTVIEW, NULL, LVS_REPORT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_VSCROLL | LVS_SHOWSELALWAYS, NULL }; + ctrlMap[IDC_PATH_DISPLAY] = { dpiMgr->scaleX(18), windowHeight - dpiMgr->scaleY(15), listWidth, dpiMgr->scaleY(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL}; + ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, dpiMgr->scaleY(140), dpiMgr->scaleX(76), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; } void MultiReplace::initializeCtrlMap() { @@ -342,7 +398,7 @@ void MultiReplace::moveAndResizeControls() { IDC_FIND_EDIT, IDC_REPLACE_EDIT, IDC_SWAP_BUTTON, IDC_STATIC_FRAME, IDC_COPY_TO_LIST_BUTTON, IDC_REPLACE_ALL_BUTTON, IDC_REPLACE_BUTTON, IDC_REPLACE_ALL_SMALL_BUTTON, IDC_2_BUTTONS_MODE, IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_FIND_PREV_BUTTON, IDC_MARK_BUTTON, IDC_MARK_MATCHES_BUTTON, IDC_CLEAR_MARKS_BUTTON, IDC_COPY_MARKED_TEXT_BUTTON, - IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_LOAD_LIST_BUTTON, IDC_NEW_LIST_BUTTON, IDC_SAVE_TO_CSV_BUTTON, + IDC_USE_LIST_CHECKBOX, IDC_LOAD_FROM_CSV_BUTTON, IDC_LOAD_LIST_BUTTON, IDC_NEW_LIST_BUTTON, IDC_SAVE_TO_CSV_BUTTON, IDC_SAVE_BUTTON, IDC_SAVE_AS_BUTTON, IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, IDC_SHIFT_TEXT, IDC_EXPORT_BASH_BUTTON, IDC_PATH_DISPLAY }; @@ -386,7 +442,7 @@ void MultiReplace::moveAndResizeControls() { IDC_2_BUTTONS_MODE, IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_FIND_PREV_BUTTON, IDC_MARK_BUTTON, IDC_MARK_MATCHES_BUTTON, IDC_CLEAR_MARKS_BUTTON, IDC_COPY_MARKED_TEXT_BUTTON, IDC_SHIFT_FRAME, IDC_UP_BUTTON, IDC_DOWN_BUTTON, IDC_SHIFT_TEXT }; - + // Redraw controls using stored HWNDs for (int ctrlId : redrawIds) { InvalidateRect(hwndMap[ctrlId], NULL, TRUE); @@ -742,7 +798,7 @@ int MultiReplace::calcDynamicColWidth(const CountColWidths& widths) { } void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { - int newWidth = LOWORD(lParam); // calculate ListWidth as lParam return WindowWidth + int newWidth = LOWORD(lParam); // calculate ListWidth as lParam return WindowWidth int newHeight = HIWORD(lParam); CountColWidths widths = { @@ -1803,6 +1859,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l { case WM_INITDIALOG: { + // Instantiate DPIManager + dpiMgr = new DPIManager(_hSelf); loadLanguage(); initializeWindowSize(); pointerToScintilla(); @@ -1812,7 +1870,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializeDragAndDrop(); loadSettings(); adjustWindowSize(); - updateStatisticsColumnButtonIcon(); + updateStatisticsColumnButtonIcon(); // Activate Dark Mode ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, @@ -1824,6 +1882,36 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l return TRUE; } + case WM_DPICHANGED: + { + // Extract the new window size and position from lParam + RECT* pRect = (RECT*)lParam; + SetWindowPos(_hSelf, NULL, pRect->left, pRect->top, + pRect->right - pRect->left, pRect->bottom - pRect->top, + SWP_NOZORDER | SWP_NOACTIVATE); + + // Update DPI values + if (dpiMgr) + { + dpiMgr->updateDPI(_hSelf); + } + + // Rescale and reposition UI elements + RECT rcClient; + GetClientRect(_hSelf, &rcClient); + positionAndResizeControls(rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); + + // Move and resize controls accordingly + moveAndResizeControls(); + + // Apply scaled fonts after resizing controls + applyFonts(); + + // Refresh the UI + InvalidateRect(_hSelf, NULL, TRUE); + return 0; + } + case WM_POST_INIT: { checkForFileChangesAtStartup(); @@ -1887,6 +1975,12 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l case WM_DESTROY: { + // Clean up DPIManager + if (dpiMgr) { + delete dpiMgr; + dpiMgr = nullptr; + } + if (_replaceListView && originalListViewProc) { SetWindowLongPtr(_replaceListView, GWLP_WNDPROC, (LONG_PTR)originalListViewProc); } @@ -1950,6 +2044,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Move all Elements moveAndResizeControls(); + // Apply fonts after resizing controls + applyFonts(); + // Refresh UI and gripper by invalidating window InvalidateRect(_hSelf, NULL, TRUE); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 9db69bf..f9f8628 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -20,6 +20,7 @@ #include "StaticDialog/resource.h" #include "PluginInterface.h" #include "DropTarget.h" +#include "DPIManager.h" #include #include @@ -339,6 +340,8 @@ class MultiReplace : public StaticDialog static constexpr wchar_t* symbolSortDescUnsorted = L"△"; static constexpr int MAX_CAP_GROUPS = 9; // Maximum number of capture groups supported by Notepad++ + DPIManager* dpiMgr; // Pointer to DPIManager instance + // Static variables related to GUI static HWND s_hScintilla; static HWND s_hDlg; @@ -432,6 +435,9 @@ class MultiReplace : public StaticDialog BYTE foregroundTransparency = 255; // Default to fully opaque BYTE backgroundTransparency = 190; // Default to semi-transparent + + void applyFonts(); + //Initialization void initializeWindowSize(); RECT calculateMinWindowFrame(HWND hwnd); diff --git a/src/StaticDialog/MultiReplacePanel.manifest b/src/StaticDialog/MultiReplacePanel.manifest new file mode 100644 index 0000000..a3b3848 --- /dev/null +++ b/src/StaticDialog/MultiReplacePanel.manifest @@ -0,0 +1,8 @@ + + + + + true/pm + + + diff --git a/src/StaticDialog/resource.rc b/src/StaticDialog/resource.rc index a39a10f..1f73dbf 100644 --- a/src/StaticDialog/resource.rc +++ b/src/StaticDialog/resource.rc @@ -43,4 +43,6 @@ END IDR_MR_BMP BITMAP "resources\multireplace_light.bmp" IDI_MR_ICON ICON "resources\multireplace_ico_black.ico" -IDI_MR_DM_ICON ICON "resources\multireplace_ico_white.ico" \ No newline at end of file +IDI_MR_DM_ICON ICON "resources\multireplace_ico_white.ico" + +1 RT_MANIFEST "MultiReplacePanel.manifest" \ No newline at end of file diff --git a/vs.proj/MultiReplace.vcxproj b/vs.proj/MultiReplace.vcxproj index b7e8384..1e2cfed 100644 --- a/vs.proj/MultiReplace.vcxproj +++ b/vs.proj/MultiReplace.vcxproj @@ -28,6 +28,7 @@
+ @@ -64,6 +65,7 @@ + diff --git a/vs.proj/MultiReplace.vcxproj.filters b/vs.proj/MultiReplace.vcxproj.filters index 351c10d..b39a76f 100644 --- a/vs.proj/MultiReplace.vcxproj.filters +++ b/vs.proj/MultiReplace.vcxproj.filters @@ -110,6 +110,7 @@ + @@ -203,6 +204,7 @@ + From 9da25d8c98061caae726b4e2fbd57f5b5db989a4 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Fri, 4 Oct 2024 00:29:35 +0200 Subject: [PATCH 24/51] updated DPI Window Size --- src/DPIManager.h | 6 ++-- src/MultiReplacePanel.cpp | 65 ++++++++++++++++++++++--------------- src/MultiReplacePanel.h | 8 +++-- src/StaticDialog/resource.h | 6 ++-- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/DPIManager.h b/src/DPIManager.h index 6cbb1f4..13bb370 100644 --- a/src/DPIManager.h +++ b/src/DPIManager.h @@ -41,11 +41,13 @@ class DPIManager // Updates DPI values (e.g., after a DPI change event). void updateDPI(HWND hwnd); -private: - HWND _hwnd; // Handle to the window. int _dpiX; // Horizontal DPI. int _dpiY; // Vertical DPI. +private: + HWND _hwnd; // Handle to the window. + + // Initializes the DPI values. void init(); }; diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index e013076..6f1a457 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -77,6 +77,21 @@ HWND MultiReplace::hDebugWnd = NULL; #pragma warning(disable: 6262) +#pragma region Initialization + +void MultiReplace::initializeWindowSize() { + MIN_WIDTH_scaled = dpiMgr->scaleX(MIN_WIDTH); // MIN_WIDTH from resource.rc + MIN_HEIGHT_scaled = dpiMgr->scaleY(MIN_HEIGHT); // MIN_HEIGHT from resource.rc + SHRUNK_HEIGHT_scaled = dpiMgr->scaleY(SHRUNK_HEIGHT); // SHRUNK_HEIGHT from resource.rc + + loadUIConfigFromIni(); // Loads the UI configuration, including window size and position + + // Set the window position and size based on the loaded settings + SetWindowPos(_hSelf, NULL, windowRect.left, windowRect.top, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, SWP_NOZORDER); +} + void MultiReplace::applyFonts() { if (!dpiMgr) return; @@ -130,17 +145,6 @@ void MultiReplace::applyFonts() SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); } -#pragma region Initialization - -void MultiReplace::initializeWindowSize() { - loadUIConfigFromIni(); // Loads the UI configuration, including window size and position - - // Set the window position and size based on the loaded settings - SetWindowPos(_hSelf, NULL, windowRect.left, windowRect.top, - windowRect.right - windowRect.left, - windowRect.bottom - windowRect.top, SWP_NOZORDER); -} - RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { // Use local variables to avoid modifying windowRect RECT tempWindowRect; @@ -156,8 +160,8 @@ RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { // Determine whether the Use List checkbox is checked BOOL useListChecked = IsDlgButtonChecked(hwnd, IDC_USE_LIST_CHECKBOX) == BST_CHECKED; - int minHeight = useListChecked ? MIN_HEIGHT : SHRUNK_HEIGHT; - int minWidth = MIN_WIDTH; + int minHeight = useListChecked ? MIN_HEIGHT_scaled : SHRUNK_HEIGHT_scaled; + int minWidth = MIN_WIDTH_scaled; // Adjust for window borders and title bar minHeight += borderWidth + titleBarHeight; @@ -176,8 +180,8 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) int swapButtonX = windowWidth - dpiMgr->scaleX(36 + 128 + 26); int comboWidth = windowWidth - dpiMgr->scaleX(292); int frameX = windowWidth - dpiMgr->scaleX(256); - int listWidth = windowWidth - dpiMgr->scaleX(260); - int listHeight = windowHeight - dpiMgr->scaleX(310); + int listWidth = windowWidth - dpiMgr->scaleX(208); + int listHeight = windowHeight - dpiMgr->scaleX(248); int checkboxX = buttonX - dpiMgr->scaleX(84); // Apply scaling only when assigning to ctrlMap @@ -799,15 +803,18 @@ int MultiReplace::calcDynamicColWidth(const CountColWidths& widths) { void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { int newWidth = LOWORD(lParam); // calculate ListWidth as lParam return WindowWidth - int newHeight = HIWORD(lParam); + //int newHeight = HIWORD(lParam); + newWidth += newWidth; + + // Retrieve the control information for IDC_REPLACE_LIST from ctrlMap + const ControlInfo& listCtrlInfo = ctrlMap[IDC_REPLACE_LIST]; CountColWidths widths = { listView, - newWidth - 282, // Direct use of newWidth for listViewWidth - false, // This is not used for current calculation. + listCtrlInfo.cx, // Direct use of newWidth for listViewWidth ListView_GetColumnWidth(listView, 1), // Current Find Count Width ListView_GetColumnWidth(listView, 2), // Current Replace Count Width - 0 // No margin as already precalculated in newWidth + GetSystemMetrics(SM_CXVSCROLL) // Width of Scrollbar }; // Calculate width available for each dynamic column. @@ -820,7 +827,8 @@ void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { ListView_SetColumnWidth(listView, 4, perColumnWidth); // Find Text ListView_SetColumnWidth(listView, 5, perColumnWidth); // Replace Text - MoveWindow(listHwnd, 20, 284, newWidth - 260, newHeight - 310, TRUE); + //MoveWindow(listHwnd, 20, dpiMgr->scaleY(227), newWidth - dpiMgr->scaleX(208), newHeight - dpiMgr->scaleY(248), TRUE); + MoveWindow(listHwnd, listCtrlInfo.x, listCtrlInfo.y, listCtrlInfo.cx, listCtrlInfo.cy, TRUE); SendMessage(widths.listView, WM_SETREDRAW, TRUE, 0); //InvalidateRect(widths.listView, NULL, TRUE); @@ -1127,12 +1135,12 @@ void MultiReplace::resizeCountColumns() { LONG style = GetWindowLong(listView, GWL_STYLE); bool hasVerticalScrollbar = (style & WS_VSCROLL) != 0; - int margin = hasVerticalScrollbar ? 0 : 20; + int scrollbarWidth = GetSystemMetrics(SM_CXVSCROLL); + int margin = hasVerticalScrollbar ? 0 : scrollbarWidth; CountColWidths widths = { listView, listViewWidth, - hasVerticalScrollbar, ListView_GetColumnWidth(_replaceListView, 1), // Current Find Count Width ListView_GetColumnWidth(_replaceListView, 2), // Current Replace Count Width margin @@ -1861,6 +1869,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l { // Instantiate DPIManager dpiMgr = new DPIManager(_hSelf); + // For debugging only + // dpiMgr->_dpiX = 70; + // dpiMgr->_dpiY = 70; loadLanguage(); initializeWindowSize(); pointerToScintilla(); @@ -2035,12 +2046,12 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l int newWidth = LOWORD(lParam); int newHeight = HIWORD(lParam); - // Move and resize the List - updateListViewAndColumns(GetDlgItem(_hSelf, IDC_REPLACE_LIST), lParam); - // Calculate Position for all Elements positionAndResizeControls(newWidth, newHeight); + // Move and resize the List + updateListViewAndColumns(GetDlgItem(_hSelf, IDC_REPLACE_LIST), lParam); + // Move all Elements moveAndResizeControls(); @@ -7352,8 +7363,8 @@ void MultiReplace::loadUIConfigFromIni() { CheckDlgButton(_hSelf, IDC_USE_LIST_CHECKBOX, useList ? BST_CHECKED : BST_UNCHECKED); // Load window width - int savedWidth = readIntFromIniFile(iniFilePath, L"Window", L"Width", MIN_WIDTH); - int width = std::max(savedWidth, MIN_WIDTH); + int savedWidth = readIntFromIniFile(iniFilePath, L"Window", L"Width", MIN_WIDTH_scaled); + int width = std::max(savedWidth, MIN_WIDTH_scaled); // Load useListOnHeight from INI file useListOnHeight = readIntFromIniFile(iniFilePath, L"Window", L"Height", MIN_HEIGHT); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index f9f8628..71e8691 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -157,7 +157,6 @@ struct ColumnInfo { struct CountColWidths { HWND listView; int listViewWidth; - bool hasVerticalScrollbar; int findCountWidth; int replaceCountWidth; int margin; @@ -435,12 +434,15 @@ class MultiReplace : public StaticDialog BYTE foregroundTransparency = 255; // Default to fully opaque BYTE backgroundTransparency = 190; // Default to semi-transparent - - void applyFonts(); + // Window DPI scaled size + int MIN_WIDTH_scaled; + int MIN_HEIGHT_scaled; + int SHRUNK_HEIGHT_scaled; //Initialization void initializeWindowSize(); RECT calculateMinWindowFrame(HWND hwnd); + void applyFonts(); void positionAndResizeControls(int windowWidth, int windowHeight); void initializeCtrlMap(); bool createAndShowWindows(); diff --git a/src/StaticDialog/resource.h b/src/StaticDialog/resource.h index 6995b39..b67b4b7 100644 --- a/src/StaticDialog/resource.h +++ b/src/StaticDialog/resource.h @@ -130,9 +130,9 @@ // Default window position and dimensions #define POS_X 92 #define POS_Y 40 -#define MIN_WIDTH 1023 -#define MIN_HEIGHT 500 -#define SHRUNK_HEIGHT 280 +#define MIN_WIDTH 818 +#define MIN_HEIGHT 400 +#define SHRUNK_HEIGHT 224 // Custom message used to perform initial actions after the window has been fully opened #define WM_POST_INIT (WM_APP + 1) From 045646c90b02927c36d5de844357963ada2066e2 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Fri, 4 Oct 2024 11:02:07 +0200 Subject: [PATCH 25/51] adapted column size based on DPI --- src/MultiReplacePanel.cpp | 53 +++++++++++++-------------------------- src/MultiReplacePanel.h | 6 ++--- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 6f1a457..4164ad8 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -92,7 +92,7 @@ void MultiReplace::initializeWindowSize() { windowRect.bottom - windowRect.top, SWP_NOZORDER); } -void MultiReplace::applyFonts() +void MultiReplace::initializeFontStyles() { if (!dpiMgr) return; @@ -276,27 +276,9 @@ void MultiReplace::initializeCtrlMap() { return; } - // Create the font - _hFont = CreateFont(FONT_SIZE, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, 0, 0, FONT_NAME); - - // Set the font for each control in ctrlMap - for (auto& pair : ctrlMap) - { - SendMessage(GetDlgItem(_hSelf, pair.first), WM_SETFONT, (WPARAM)_hFont, TRUE); - } - // Limit the input for IDC_QUOTECHAR_EDIT to one character SendMessage(GetDlgItem(_hSelf, IDC_QUOTECHAR_EDIT), EM_SETLIMITTEXT, (WPARAM)1, 0); - // Set the larger, bolder font for the swap, copy and refresh button - HFONT hLargerBolderFont = CreateFont(28, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, 0, 0, TEXT("Courier New")); - SendMessage(GetDlgItem(_hSelf, IDC_SWAP_BUTTON), WM_SETFONT, (WPARAM)hLargerBolderFont, TRUE); - - HFONT hLargerBolderFont1 = CreateFont(29, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, 0, 0, TEXT("Courier New")); - SendMessage(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), WM_SETFONT, (WPARAM)hLargerBolderFont1, TRUE); - SendMessage(GetDlgItem(_hSelf, IDC_COLUMN_COPY_BUTTON), WM_SETFONT, (WPARAM)hLargerBolderFont1, TRUE); - SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)hLargerBolderFont1, TRUE); - // Enable IDC_SELECTION_RADIO based on text selection Sci_Position start = ::SendMessage(MultiReplace::getScintillaHandle(), SCI_GETSELECTIONSTART, 0, 0); Sci_Position end = ::SendMessage(MultiReplace::getScintillaHandle(), SCI_GETSELECTIONEND, 0, 0); @@ -675,16 +657,16 @@ void MultiReplace::createListViewColumns(HWND listView) { int windowWidth = rcClient.right - rcClient.left; // Calculate the remaining width for the first two columns - int adjustedWidth = windowWidth - 282; + int adjustedWidth = windowWidth - dpiMgr->scaleX(225); // Calculate the total width of columns 5 to 10 (Options and Delete Button) - int columns5to10Width = 30 * 7; + int columns5to10Width = dpiMgr->scaleX(24) * 7; // Calculate the remaining width after subtracting the widths of the specified columns int remainingWidth = adjustedWidth - findCountColumnWidth - replaceCountColumnWidth - columns5to10Width; // Ensure remainingWidth is not less than the minimum width - remainingWidth = std::max(remainingWidth, 60); + remainingWidth = std::max(remainingWidth, dpiMgr->scaleX(MIN_COLUMN_WIDTH)); // Minimum size of Find and Replace Column lvc.iSubItem = 0; lvc.pszText = L""; @@ -706,7 +688,7 @@ void MultiReplace::createListViewColumns(HWND listView) { // Column for Selection lvc.iSubItem = 3; lvc.pszText = L"\u2610"; - lvc.cx = 30; + lvc.cx = dpiMgr->scaleX(24); lvc.fmt = LVCFMT_CENTER | LVCFMT_FIXED_WIDTH; ListView_InsertColumn(listView, 3, &lvc); @@ -728,7 +710,7 @@ void MultiReplace::createListViewColumns(HWND listView) { for (int i = 0; i < 5; i++) { lvc.iSubItem = 6 + i; lvc.pszText = getLangStrLPWSTR(options[i]); - lvc.cx = 30; + lvc.cx = dpiMgr->scaleX(24); lvc.fmt = LVCFMT_CENTER | LVCFMT_FIXED_WIDTH; ListView_InsertColumn(listView, 6 + i, &lvc); } @@ -736,7 +718,7 @@ void MultiReplace::createListViewColumns(HWND listView) { // Column for Delete Button lvc.iSubItem = 11; lvc.pszText = L""; - lvc.cx = 30; + lvc.cx = dpiMgr->scaleX(24); ListView_InsertColumn(listView, 11, &lvc); //Adding Tooltips @@ -793,7 +775,7 @@ void MultiReplace::insertReplaceListItem(const ReplaceItemData& itemData) { } int MultiReplace::calcDynamicColWidth(const CountColWidths& widths) { - int columns5to10Width = 210; // Simplified calculation (30 * 7). + int columns5to10Width = dpiMgr->scaleX(24) * 7; // Simplified calculation // Directly calculate the width available for each dynamic column. int totalRemainingWidth = widths.listViewWidth - widths.margin - columns5to10Width - widths.findCountWidth - widths.replaceCountWidth; @@ -1132,6 +1114,7 @@ void MultiReplace::resizeCountColumns() { RECT listRect; GetClientRect(listView, &listRect); int listViewWidth = listRect.right - listRect.left; + int COUNT_COLUMN_WIDTH_scaled = dpiMgr->scaleX(COUNT_COLUMN_WIDTH); LONG style = GetWindowLong(listView, GWL_STYLE); bool hasVerticalScrollbar = (style & WS_VSCROLL) != 0; @@ -1147,15 +1130,15 @@ void MultiReplace::resizeCountColumns() { }; // Determine the direction of the adjustment - bool expandColumns = widths.findCountWidth < COUNT_COLUMN_WIDTH && widths.replaceCountWidth < COUNT_COLUMN_WIDTH; + bool expandColumns = widths.findCountWidth < COUNT_COLUMN_WIDTH_scaled && widths.replaceCountWidth < COUNT_COLUMN_WIDTH_scaled; // Perform the adjustment in steps - while ((expandColumns && (widths.findCountWidth < COUNT_COLUMN_WIDTH || widths.replaceCountWidth < COUNT_COLUMN_WIDTH)) || + while ((expandColumns && (widths.findCountWidth < COUNT_COLUMN_WIDTH_scaled || widths.replaceCountWidth < COUNT_COLUMN_WIDTH_scaled)) || (!expandColumns && (widths.findCountWidth > 0 || widths.replaceCountWidth > 0))) { if (expandColumns) { - widths.findCountWidth = std::min(widths.findCountWidth + STEP_SIZE, COUNT_COLUMN_WIDTH); - widths.replaceCountWidth = std::min(widths.replaceCountWidth + STEP_SIZE, COUNT_COLUMN_WIDTH); + widths.findCountWidth = std::min(widths.findCountWidth + STEP_SIZE, COUNT_COLUMN_WIDTH_scaled); + widths.replaceCountWidth = std::min(widths.replaceCountWidth + STEP_SIZE, COUNT_COLUMN_WIDTH_scaled); } else { widths.findCountWidth = std::max(widths.findCountWidth - STEP_SIZE, 0); @@ -1870,8 +1853,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Instantiate DPIManager dpiMgr = new DPIManager(_hSelf); // For debugging only - // dpiMgr->_dpiX = 70; - // dpiMgr->_dpiY = 70; + dpiMgr->_dpiX = 70; + dpiMgr->_dpiY = 70; loadLanguage(); initializeWindowSize(); pointerToScintilla(); @@ -1916,7 +1899,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l moveAndResizeControls(); // Apply scaled fonts after resizing controls - applyFonts(); + initializeFontStyles(); // Refresh the UI InvalidateRect(_hSelf, NULL, TRUE); @@ -2056,7 +2039,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l moveAndResizeControls(); // Apply fonts after resizing controls - applyFonts(); + initializeFontStyles(); // Refresh UI and gripper by invalidating window InvalidateRect(_hSelf, NULL, TRUE); @@ -7381,7 +7364,7 @@ void MultiReplace::loadUIConfigFromIni() { findCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"FindCountWidth", findCountColumnWidth); replaceCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"ReplaceCountWidth", replaceCountColumnWidth); - isStatisticsColumnsExpanded = (findCountColumnWidth >= COUNT_COLUMN_WIDTH && replaceCountColumnWidth >= COUNT_COLUMN_WIDTH); + isStatisticsColumnsExpanded = (findCountColumnWidth >= dpiMgr->scaleX(COUNT_COLUMN_WIDTH) && replaceCountColumnWidth >= dpiMgr->scaleX(COUNT_COLUMN_WIDTH)); // Load transparency settings with defaults foregroundTransparency = readByteFromIniFile(iniFilePath, L"Window", L"ForegroundTransparency", foregroundTransparency); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 71e8691..f9b3cce 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -330,8 +330,8 @@ class MultiReplace : public StaticDialog static constexpr long MARKER_COLOR = 0x007F00; // Color for non-list Marker static constexpr LRESULT PROGRESS_THRESHOLD = 50000; // Will show progress bar if total exceeds defined threshold bool isReplaceAllInDocs = false; // True if replacing in all open documents, false for current document only. - static constexpr int COUNT_COLUMN_WIDTH = 50; // Initial Size for Count Column - static constexpr int MIN_COLUMN_WIDTH = 60; // Minimum size of Find and Replace Column + static constexpr int COUNT_COLUMN_WIDTH = 40; // Initial Size for Count Column + static constexpr int MIN_COLUMN_WIDTH = 48; // Minimum size of Find and Replace Column static constexpr int STEP_SIZE = 5; // Speed for opening and closing Count Columns static constexpr wchar_t* symbolSortAsc = L"▼"; static constexpr wchar_t* symbolSortDesc = L"▲"; @@ -442,7 +442,7 @@ class MultiReplace : public StaticDialog //Initialization void initializeWindowSize(); RECT calculateMinWindowFrame(HWND hwnd); - void applyFonts(); + void initializeFontStyles(); void positionAndResizeControls(int windowWidth, int windowHeight); void initializeCtrlMap(); bool createAndShowWindows(); From 5a9d6b56f10f6aa2f8be954195d046b510f814b0 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Fri, 4 Oct 2024 17:29:40 +0200 Subject: [PATCH 26/51] addes scaleFactor for indivisual Sizing --- src/DPIManager.cpp | 2 +- src/DPIManager.h | 19 ++++++++++--- src/MultiReplacePanel.cpp | 57 ++++++++++++++++++++++++++------------- src/MultiReplacePanel.h | 1 + 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/DPIManager.cpp b/src/DPIManager.cpp index e60ac27..91b203e 100644 --- a/src/DPIManager.cpp +++ b/src/DPIManager.cpp @@ -5,7 +5,7 @@ // Constructor: Initializes DPI values. DPIManager::DPIManager(HWND hwnd) - : _hwnd(hwnd), _dpiX(96), _dpiY(96) // Default DPI is 96. + : _hwnd(hwnd), _dpiX(120), _dpiY(120), _customScaleFactor(1.0f) { init(); } diff --git a/src/DPIManager.h b/src/DPIManager.h index 13bb370..fe75504 100644 --- a/src/DPIManager.h +++ b/src/DPIManager.h @@ -6,6 +6,8 @@ #include #include +#include + // DPIManager class handles DPI awareness and scaling for UI elements. class DPIManager @@ -17,13 +19,23 @@ class DPIManager // Destructor (if needed for resource management). ~DPIManager(); + // Getter for custom scale factor + float getCustomScaleFactor() const { return _customScaleFactor; } + + // Setter for custom scale factor + void DPIManager::setCustomScaleFactor(float scaleFactor) { + _customScaleFactor = std::clamp(scaleFactor, 0.5f, 2.0f); + } + // Retrieves the current DPI values. int getDPIX() const { return _dpiX; } int getDPIY() const { return _dpiY; } - // Converts raw pixels to scaled pixels. - int scaleX(int x) const { return MulDiv(x, _dpiX, 96); } - int scaleY(int y) const { return MulDiv(y, _dpiY, 96); } + // Converts raw pixels to scaled pixels, includes the custom scale factor. + //int scaleX(int x) const { return static_cast(MulDiv(x, _dpiX, 120) * _customScaleFactor); } + //int scaleY(int y) const { return static_cast(MulDiv(y, _dpiY, 120) * _customScaleFactor); } + int scaleX(int x) const { return static_cast(MulDiv(x, _dpiX, 96) * _customScaleFactor); } + int scaleY(int y) const { return static_cast(MulDiv(y, _dpiY, 96) * _customScaleFactor); } // Converts scaled pixels back to raw pixels. int unscaleX(int x) const { return MulDiv(x, 96, _dpiX); } @@ -43,6 +55,7 @@ class DPIManager int _dpiX; // Horizontal DPI. int _dpiY; // Vertical DPI. + float _customScaleFactor; // Custom scaling factor stored INI file. private: HWND _hwnd; // Handle to the window. diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 4164ad8..c981d1c 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -80,9 +80,6 @@ HWND MultiReplace::hDebugWnd = NULL; #pragma region Initialization void MultiReplace::initializeWindowSize() { - MIN_WIDTH_scaled = dpiMgr->scaleX(MIN_WIDTH); // MIN_WIDTH from resource.rc - MIN_HEIGHT_scaled = dpiMgr->scaleY(MIN_HEIGHT); // MIN_HEIGHT from resource.rc - SHRUNK_HEIGHT_scaled = dpiMgr->scaleY(SHRUNK_HEIGHT); // SHRUNK_HEIGHT from resource.rc loadUIConfigFromIni(); // Loads the UI configuration, including window size and position @@ -1852,9 +1849,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l { // Instantiate DPIManager dpiMgr = new DPIManager(_hSelf); - // For debugging only - dpiMgr->_dpiX = 70; - dpiMgr->_dpiY = 70; + // For DPI debugging only + //dpiMgr->_dpiX = 70; + //dpiMgr->_dpiY = 70; loadLanguage(); initializeWindowSize(); pointerToScintilla(); @@ -1864,7 +1861,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l initializeDragAndDrop(); loadSettings(); adjustWindowSize(); - updateStatisticsColumnButtonIcon(); + updateStatisticsColumnButtonIcon(); + initializeFontStyles(); // Activate Dark Mode ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, @@ -1969,11 +1967,6 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l case WM_DESTROY: { - // Clean up DPIManager - if (dpiMgr) { - delete dpiMgr; - dpiMgr = nullptr; - } if (_replaceListView && originalListViewProc) { SetWindowLongPtr(_replaceListView, GWLP_WNDPROC, (LONG_PTR)originalListViewProc); @@ -2009,6 +2002,12 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l hDebugWnd = NULL; // Reset the handle after closing } + // Clean up DPIManager + if (dpiMgr) { + delete dpiMgr; + dpiMgr = nullptr; + } + DestroyWindow(_hSelf); // Destroy the main window // Post a quit message to ensure the application terminates cleanly @@ -2038,9 +2037,6 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Move all Elements moveAndResizeControls(); - // Apply fonts after resizing controls - initializeFontStyles(); - // Refresh UI and gripper by invalidating window InvalidateRect(_hSelf, NULL, TRUE); @@ -7107,6 +7103,7 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { outFile << wstringToString(L"PosY=" + std::to_wstring(posY) + L"\n"); outFile << wstringToString(L"Width=" + std::to_wstring(width) + L"\n"); outFile << wstringToString(L"Height=" + std::to_wstring(useListOnHeight) + L"\n"); + outFile << wstringToString(L"ScaleFactor=" + std::to_wstring(dpiMgr->getCustomScaleFactor()).substr(0, std::to_wstring(dpiMgr->getCustomScaleFactor()).find(L'.') + 2) + L"\n"); // Save transparency settings outFile << wstringToString(L"ForegroundTransparency=" + std::to_wstring(foregroundTransparency) + L"\n"); @@ -7205,7 +7202,7 @@ void MultiReplace::saveSettings() { // Generate the paths to the configuration files auto [iniFilePath, csvFilePath] = generateConfigFilePaths(); - + // Try to save the settings in the INI file try { saveSettingsToIni(iniFilePath); @@ -7336,6 +7333,15 @@ void MultiReplace::loadSettings() { void MultiReplace::loadUIConfigFromIni() { auto [iniFilePath, _] = generateConfigFilePaths(); // Generating config file paths + // Load DPI Scaling factor from INI file + float customScaleFactor = readFloatFromIniFile(iniFilePath, L"Window", L"ScaleFactor", 1.0f); + dpiMgr->setCustomScaleFactor(customScaleFactor); + + // Scale Window Size after loading ScaleFactor + MIN_WIDTH_scaled = dpiMgr->scaleX(MIN_WIDTH); // MIN_WIDTH from resource.rc + MIN_HEIGHT_scaled = dpiMgr->scaleY(MIN_HEIGHT); // MIN_HEIGHT from resource.rc + SHRUNK_HEIGHT_scaled = dpiMgr->scaleY(SHRUNK_HEIGHT); // SHRUNK_HEIGHT from resource.rc + // Load window position windowRect.left = readIntFromIniFile(iniFilePath, L"Window", L"PosX", POS_X); windowRect.top = readIntFromIniFile(iniFilePath, L"Window", L"PosY", POS_Y); @@ -7350,8 +7356,9 @@ void MultiReplace::loadUIConfigFromIni() { int width = std::max(savedWidth, MIN_WIDTH_scaled); // Load useListOnHeight from INI file - useListOnHeight = readIntFromIniFile(iniFilePath, L"Window", L"Height", MIN_HEIGHT); - useListOnHeight = std::max(useListOnHeight, MIN_HEIGHT); // Ensure minimum height + useListOnHeight = readIntFromIniFile(iniFilePath, L"Window", L"Height", MIN_HEIGHT_scaled); + useListOnHeight = std::max(useListOnHeight, MIN_HEIGHT_scaled); // Ensure minimum height + // Set windowRect based on Use List state BOOL useListChecked = (useList == 1); @@ -7453,6 +7460,20 @@ BYTE MultiReplace::readByteFromIniFile(const std::wstring& iniFilePath, const st return static_cast(intValue); } +float MultiReplace::readFloatFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, float defaultValue) { + WCHAR buffer[256] = { 0 }; + std::wstring defaultStr = std::to_wstring(defaultValue); + + GetPrivateProfileStringW(section.c_str(), key.c_str(), defaultStr.c_str(), buffer, sizeof(buffer) / sizeof(WCHAR), iniFilePath.c_str()); + + try { + return std::stof(buffer); + } + catch (...) { + return defaultValue; + } +} + void MultiReplace::setTextInDialogItem(HWND hDlg, int itemID, const std::wstring& text) { ::SetDlgItemTextW(hDlg, itemID, text.c_str()); } diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index f9b3cce..8c61916 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -616,6 +616,7 @@ class MultiReplace : public StaticDialog std::wstring readStringFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, const std::wstring& defaultValue); bool readBoolFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, bool defaultValue); int readIntFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, int defaultValue); + float readFloatFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, float defaultValue); std::size_t readSizeTFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, std::size_t defaultValue); void setTextInDialogItem(HWND hDlg, int itemID, const std::wstring& text); From 7e0b4f037ef43288ee22c1ddacc7b4552c75f41b Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Fri, 4 Oct 2024 19:49:42 +0200 Subject: [PATCH 27/51] =?UTF-8?q?updated=20Column=20Size=20f=C3=BCr=20Unic?= =?UTF-8?q?ode=20Char?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MultiReplacePanel.cpp | 61 ++++++++++++++++++++++++++++++--------- src/MultiReplacePanel.h | 3 ++ 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index c981d1c..9d02235 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -89,13 +89,12 @@ void MultiReplace::initializeWindowSize() { windowRect.bottom - windowRect.top, SWP_NOZORDER); } -void MultiReplace::initializeFontStyles() -{ +void MultiReplace::initializeFontStyles() { if (!dpiMgr) return; // Create a standard scaled font HFONT hStandardFont = CreateFont( - dpiMgr->scaleY(12), // Font height + dpiMgr->scaleY(14), // Font height 0, // Font width (0 means default) 0, // Escapement 0, // Orientation @@ -112,14 +111,13 @@ void MultiReplace::initializeFontStyles() ); // Apply the standard font to all controls - for (const auto& pair : ctrlMap) - { + for (const auto& pair : ctrlMap) { SendMessage(GetDlgItem(_hSelf, pair.first), WM_SETFONT, (WPARAM)hStandardFont, TRUE); } // Create and apply a bold font for specific controls HFONT hBoldFont = CreateFont( - dpiMgr->scaleY(14), // Larger font height + dpiMgr->scaleY(19), // Larger font height 0, 0, 0, @@ -140,6 +138,17 @@ void MultiReplace::initializeFontStyles() SendMessage(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); SendMessage(GetDlgItem(_hSelf, IDC_COLUMN_COPY_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); + + // For ListView identify width of special characters + // Measure the width of the Unicode characters L"\u2714" and L"\u2716" (for columns 5 to 10) + int widthCheckMark = getCharacterWidth(IDC_REPLACE_LIST, L"\u2714"); + int widthCross = getCharacterWidth(IDC_REPLACE_LIST, L"\u2716"); + + // Store the widest width of column 5 to 10 with extra space for padding/alignment + column5to10Width_scaled = std::max(widthCheckMark, widthCross) + 15; + + // Store width of column 3 with extra space for padding/alignment + column3Width_scaled = getCharacterWidth(IDC_REPLACE_LIST, L"\u2610") + 15; } RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { @@ -657,10 +666,10 @@ void MultiReplace::createListViewColumns(HWND listView) { int adjustedWidth = windowWidth - dpiMgr->scaleX(225); // Calculate the total width of columns 5 to 10 (Options and Delete Button) - int columns5to10Width = dpiMgr->scaleX(24) * 7; + int totalWidthColumn5to10 = column5to10Width_scaled * 7; // Calculate the remaining width after subtracting the widths of the specified columns - int remainingWidth = adjustedWidth - findCountColumnWidth - replaceCountColumnWidth - columns5to10Width; + int remainingWidth = adjustedWidth - findCountColumnWidth - replaceCountColumnWidth - totalWidthColumn5to10; // Ensure remainingWidth is not less than the minimum width remainingWidth = std::max(remainingWidth, dpiMgr->scaleX(MIN_COLUMN_WIDTH)); // Minimum size of Find and Replace Column @@ -685,7 +694,7 @@ void MultiReplace::createListViewColumns(HWND listView) { // Column for Selection lvc.iSubItem = 3; lvc.pszText = L"\u2610"; - lvc.cx = dpiMgr->scaleX(24); + lvc.cx = column3Width_scaled; lvc.fmt = LVCFMT_CENTER | LVCFMT_FIXED_WIDTH; ListView_InsertColumn(listView, 3, &lvc); @@ -707,7 +716,7 @@ void MultiReplace::createListViewColumns(HWND listView) { for (int i = 0; i < 5; i++) { lvc.iSubItem = 6 + i; lvc.pszText = getLangStrLPWSTR(options[i]); - lvc.cx = dpiMgr->scaleX(24); + lvc.cx = column5to10Width_scaled; lvc.fmt = LVCFMT_CENTER | LVCFMT_FIXED_WIDTH; ListView_InsertColumn(listView, 6 + i, &lvc); } @@ -715,7 +724,7 @@ void MultiReplace::createListViewColumns(HWND listView) { // Column for Delete Button lvc.iSubItem = 11; lvc.pszText = L""; - lvc.cx = dpiMgr->scaleX(24); + lvc.cx = column5to10Width_scaled; ListView_InsertColumn(listView, 11, &lvc); //Adding Tooltips @@ -772,14 +781,37 @@ void MultiReplace::insertReplaceListItem(const ReplaceItemData& itemData) { } int MultiReplace::calcDynamicColWidth(const CountColWidths& widths) { - int columns5to10Width = dpiMgr->scaleX(24) * 7; // Simplified calculation + + int totalWidthColumn5to10 = column5to10Width_scaled * 7; // Simplified calculation // Directly calculate the width available for each dynamic column. - int totalRemainingWidth = widths.listViewWidth - widths.margin - columns5to10Width - widths.findCountWidth - widths.replaceCountWidth; + int totalRemainingWidth = widths.listViewWidth - widths.margin - totalWidthColumn5to10 - widths.findCountWidth - widths.replaceCountWidth; int perColumnWidth = std::max(totalRemainingWidth, MIN_COLUMN_WIDTH * 2) / 2; // Ensure total min width is 120, then divide by 2. return perColumnWidth; // Return width for a single column. } +int MultiReplace::getCharacterWidth(int elementID, const wchar_t* character) { + // Get the HWND of the element by its ID + HWND hwndElement = GetDlgItem(_hSelf, elementID); + + // Get the font used by the element + HFONT hFont = (HFONT)SendMessage(hwndElement, WM_GETFONT, 0, 0); + + // Get the device context for measuring text + HDC hdc = GetDC(hwndElement); + SelectObject(hdc, hFont); // Use the font of the given element + + // Measure the width of the character + SIZE size; + GetTextExtentPoint32W(hdc, character, 1, &size); + + // Release the device context + ReleaseDC(hwndElement, hdc); + + // Return the width of the character + return size.cx; +} + void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { int newWidth = LOWORD(lParam); // calculate ListWidth as lParam return WindowWidth //int newHeight = HIWORD(lParam); @@ -1857,12 +1889,13 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l pointerToScintilla(); initializePluginStyle(); initializeCtrlMap(); + initializeFontStyles(); initializeListView(); initializeDragAndDrop(); loadSettings(); adjustWindowSize(); updateStatisticsColumnButtonIcon(); - initializeFontStyles(); + // Activate Dark Mode ::SendMessage(nppData._nppHandle, NPPM_DARKMODESUBCLASSANDTHEME, diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 8c61916..6b2246e 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -418,6 +418,8 @@ class MultiReplace : public StaticDialog std::size_t originalListHash = 0; int useListOnHeight = MIN_HEIGHT; // Default height when "Use List" is on const int useListOffHeight = SHRUNK_HEIGHT; // Height when "Use List" is off (constant) + int column5to10Width_scaled = 0; // Global variable for columns 5 to 10 + int column3Width_scaled = 0; // Global variable for column 3 // GUI control-related constants const std::vector selectionRadioDisabledButtons = { @@ -580,6 +582,7 @@ class MultiReplace : public StaticDialog sptr_t send(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0, bool useDirect = true); bool normalizeAndValidateNumber(std::string& str); std::vector createFilterString(const std::vector>& filters); + int getCharacterWidth(int elementID, const wchar_t* character); //StringHandling std::wstring stringToWString(const std::string& encodedInput) const; From 4597cba4c504bcecbff645354ee7cd9b023e2e14 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sat, 5 Oct 2024 20:31:11 +0200 Subject: [PATCH 28/51] smaller panel fixes; Hight of 'Use List Checkbox, margin in resizeCountColumns() --- src/DPIManager.cpp | 4 +- src/MultiReplacePanel.cpp | 232 +++++++++++++++++++------------------- src/MultiReplacePanel.h | 15 ++- 3 files changed, 129 insertions(+), 122 deletions(-) diff --git a/src/DPIManager.cpp b/src/DPIManager.cpp index 91b203e..a96a5c4 100644 --- a/src/DPIManager.cpp +++ b/src/DPIManager.cpp @@ -5,7 +5,7 @@ // Constructor: Initializes DPI values. DPIManager::DPIManager(HWND hwnd) - : _hwnd(hwnd), _dpiX(120), _dpiY(120), _customScaleFactor(1.0f) + : _hwnd(hwnd), _dpiX(96), _dpiY(96), _customScaleFactor(1.0f) { init(); } @@ -19,7 +19,7 @@ DPIManager::~DPIManager() // Initializes the DPI values using Win32 APIs. void DPIManager::init() { - UINT dpiX = 96, dpiY = 96; // Default DPI. + UINT dpiX = 120, dpiY = 120; // Default DPI. // Attempt to load Shcore.dll for modern DPI functions. HMODULE hShcore = LoadLibrary(TEXT("Shcore.dll")); diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 9d02235..ea5fc62 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -140,15 +140,11 @@ void MultiReplace::initializeFontStyles() { SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); // For ListView identify width of special characters - // Measure the width of the Unicode characters L"\u2714" and L"\u2716" (for columns 5 to 10) - int widthCheckMark = getCharacterWidth(IDC_REPLACE_LIST, L"\u2714"); - int widthCross = getCharacterWidth(IDC_REPLACE_LIST, L"\u2716"); + // Add 15 units of padding to the widths of checkmark, cross, and box characters + checkMarkWidth_scaled = getCharacterWidth(IDC_REPLACE_LIST, L"\u2714") + 15; + crossWidth_scaled = getCharacterWidth(IDC_REPLACE_LIST, L"\u2716") + 15; + boxWidth_scaled = getCharacterWidth(IDC_REPLACE_LIST, L"\u2610") + 15; - // Store the widest width of column 5 to 10 with extra space for padding/alignment - column5to10Width_scaled = std::max(widthCheckMark, widthCross) + 15; - - // Store width of column 3 with extra space for padding/alignment - column3Width_scaled = getCharacterWidth(IDC_REPLACE_LIST, L"\u2610") + 15; } RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { @@ -180,86 +176,90 @@ RECT MultiReplace::calculateMinWindowFrame(HWND hwnd) { void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) { + // Helper functions sx() and sy() are used to scale values for DPI awareness. + // sx(value): scales the x-axis value based on DPI settings. + // sy(value): scales the y-axis value based on DPI settings. + // Calculate dimensions without scaling - int buttonX = windowWidth - dpiMgr->scaleX(36 + 128); - int checkbox2X = buttonX + dpiMgr->scaleX(138); - int swapButtonX = windowWidth - dpiMgr->scaleX(36 + 128 + 26); - int comboWidth = windowWidth - dpiMgr->scaleX(292); - int frameX = windowWidth - dpiMgr->scaleX(256); - int listWidth = windowWidth - dpiMgr->scaleX(208); - int listHeight = windowHeight - dpiMgr->scaleX(248); - int checkboxX = buttonX - dpiMgr->scaleX(84); + int buttonX = windowWidth - sx(36 + 128); + int checkbox2X = buttonX + sx(138); + int swapButtonX = windowWidth - sx(36 + 128 + 26); + int comboWidth = windowWidth - sx(292); + int frameX = windowWidth - sx(256); + int listWidth = windowWidth - sx(208); + int listHeight = windowHeight - sy(248); + int checkboxX = buttonX - sx(84); // Apply scaling only when assigning to ctrlMap - ctrlMap[IDC_STATIC_FIND] = { dpiMgr->scaleX(11), dpiMgr->scaleY(15), dpiMgr->scaleX(80), dpiMgr->scaleY(19), WC_STATIC, getLangStrLPCWSTR(L"panel_find_what"), SS_RIGHT, NULL }; - ctrlMap[IDC_STATIC_REPLACE] = { dpiMgr->scaleX(11), dpiMgr->scaleY(43), dpiMgr->scaleX(80), dpiMgr->scaleY(19), WC_STATIC, getLangStrLPCWSTR(L"panel_replace_with"), SS_RIGHT }; - - ctrlMap[IDC_WHOLE_WORD_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(76), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_whole_word_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_MATCH_CASE_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(101), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_case"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_USE_VARIABLES_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(126), dpiMgr->scaleX(134), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_variables"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_USE_VARIABLES_HELP] = { dpiMgr->scaleX(152), dpiMgr->scaleY(126), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_help"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_FIRST_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(151), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_first_match_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_WRAP_AROUND_CHECKBOX] = { dpiMgr->scaleX(16), dpiMgr->scaleY(176), dpiMgr->scaleX(158), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_wrap_around"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - - ctrlMap[IDC_SEARCH_MODE_GROUP] = { dpiMgr->scaleX(180), dpiMgr->scaleY(79), dpiMgr->scaleX(160), dpiMgr->scaleY(104), WC_BUTTON, getLangStrLPCWSTR(L"panel_search_mode"), BS_GROUPBOX, NULL }; - ctrlMap[IDC_NORMAL_RADIO] = { dpiMgr->scaleX(188), dpiMgr->scaleY(101), dpiMgr->scaleX(144), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_normal"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; - ctrlMap[IDC_EXTENDED_RADIO] = { dpiMgr->scaleX(188), dpiMgr->scaleY(126), dpiMgr->scaleX(144), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_extended"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REGEX_RADIO] = { dpiMgr->scaleX(188), dpiMgr->scaleY(150), dpiMgr->scaleX(144), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_regular_expression"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - - ctrlMap[IDC_SCOPE_GROUP] = { dpiMgr->scaleX(352), dpiMgr->scaleY(79), dpiMgr->scaleX(198), dpiMgr->scaleY(130), WC_BUTTON, getLangStrLPCWSTR(L"panel_scope"), BS_GROUPBOX, NULL }; - ctrlMap[IDC_ALL_TEXT_RADIO] = { dpiMgr->scaleX(360), dpiMgr->scaleY(101), dpiMgr->scaleX(184), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; - ctrlMap[IDC_SELECTION_RADIO] = { dpiMgr->scaleX(360), dpiMgr->scaleY(126), dpiMgr->scaleX(184), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_MODE_RADIO] = { dpiMgr->scaleX(360), dpiMgr->scaleY(150), dpiMgr->scaleX(40), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_NUM_STATIC] = { dpiMgr->scaleX(360), dpiMgr->scaleY(182), dpiMgr->scaleX(24), dpiMgr->scaleY(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; - ctrlMap[IDC_COLUMN_NUM_EDIT] = { dpiMgr->scaleX(386), dpiMgr->scaleY(182), dpiMgr->scaleX(40), dpiMgr->scaleY(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; - ctrlMap[IDC_DELIMITER_STATIC] = { dpiMgr->scaleX(430), dpiMgr->scaleY(182), dpiMgr->scaleX(32), dpiMgr->scaleY(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; - ctrlMap[IDC_DELIMITER_EDIT] = { dpiMgr->scaleX(464), dpiMgr->scaleY(182), dpiMgr->scaleX(24), dpiMgr->scaleY(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_delimiter") }; - ctrlMap[IDC_QUOTECHAR_STATIC] = { dpiMgr->scaleX(493), dpiMgr->scaleY(182), dpiMgr->scaleX(32), dpiMgr->scaleY(20), WC_STATIC, getLangStrLPCWSTR(L"panel_quote"), SS_RIGHT, NULL }; - ctrlMap[IDC_QUOTECHAR_EDIT] = { dpiMgr->scaleX(526), dpiMgr->scaleY(182), dpiMgr->scaleX(12), dpiMgr->scaleY(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_quote") }; - - ctrlMap[IDC_COLUMN_SORT_DESC_BUTTON] = { dpiMgr->scaleX(406), dpiMgr->scaleY(149), dpiMgr->scaleX(14), dpiMgr->scaleY(20), WC_BUTTON, symbolSortDesc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_descending") }; - ctrlMap[IDC_COLUMN_SORT_ASC_BUTTON] = { dpiMgr->scaleX(421), dpiMgr->scaleY(149), dpiMgr->scaleX(14), dpiMgr->scaleY(20), WC_BUTTON, symbolSortAsc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_ascending") }; - ctrlMap[IDC_COLUMN_DROP_BUTTON] = { dpiMgr->scaleX(443), dpiMgr->scaleY(149), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, L"✖", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_drop_columns") }; - ctrlMap[IDC_COLUMN_COPY_BUTTON] = { dpiMgr->scaleX(472), dpiMgr->scaleY(149), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_columns") }; - ctrlMap[IDC_COLUMN_HIGHLIGHT_BUTTON] = { dpiMgr->scaleX(501), dpiMgr->scaleY(149), dpiMgr->scaleX(40), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_show"), BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_column_highlight") }; - - ctrlMap[IDC_STATUS_MESSAGE] = { dpiMgr->scaleX(11), dpiMgr->scaleY(208), dpiMgr->scaleX(504), dpiMgr->scaleY(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; + ctrlMap[IDC_STATIC_FIND] = { sx(11), sy(15), sx(80), sy(19), WC_STATIC, getLangStrLPCWSTR(L"panel_find_what"), SS_RIGHT, NULL }; + ctrlMap[IDC_STATIC_REPLACE] = { sx(11), sy(43), sx(80), sy(19), WC_STATIC, getLangStrLPCWSTR(L"panel_replace_with"), SS_RIGHT }; + + ctrlMap[IDC_WHOLE_WORD_CHECKBOX] = { sx(16), sy(76), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_whole_word_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_MATCH_CASE_CHECKBOX] = { sx(16), sy(101), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_case"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_USE_VARIABLES_CHECKBOX] = { sx(16), sy(126), sx(134), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_variables"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_USE_VARIABLES_HELP] = { sx(152), sy(126), sx(20), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_help"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_FIRST_CHECKBOX] = { sx(16), sy(151), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_first_match_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_WRAP_AROUND_CHECKBOX] = { sx(16), sy(176), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_wrap_around"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + + ctrlMap[IDC_SEARCH_MODE_GROUP] = { sx(180), sy(79), sx(160), sy(104), WC_BUTTON, getLangStrLPCWSTR(L"panel_search_mode"), BS_GROUPBOX, NULL }; + ctrlMap[IDC_NORMAL_RADIO] = { sx(188), sy(101), sx(144), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_normal"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; + ctrlMap[IDC_EXTENDED_RADIO] = { sx(188), sy(126), sx(144), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_extended"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REGEX_RADIO] = { sx(188), sy(150), sx(144), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_regular_expression"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + + ctrlMap[IDC_SCOPE_GROUP] = { sx(352), sy(79), sx(198), sy(130), WC_BUTTON, getLangStrLPCWSTR(L"panel_scope"), BS_GROUPBOX, NULL }; + ctrlMap[IDC_ALL_TEXT_RADIO] = { sx(360), sy(101), sx(184), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; + ctrlMap[IDC_SELECTION_RADIO] = { sx(360), sy(126), sx(184), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_MODE_RADIO] = { sx(360), sy(150), sx(40), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_NUM_STATIC] = { sx(360), sy(182), sx(24), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; + ctrlMap[IDC_COLUMN_NUM_EDIT] = { sx(386), sy(182), sx(40), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; + ctrlMap[IDC_DELIMITER_STATIC] = { sx(430), sy(182), sx(32), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; + ctrlMap[IDC_DELIMITER_EDIT] = { sx(464), sy(182), sx(24), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_delimiter") }; + ctrlMap[IDC_QUOTECHAR_STATIC] = { sx(493), sy(182), sx(32), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_quote"), SS_RIGHT, NULL }; + ctrlMap[IDC_QUOTECHAR_EDIT] = { sx(526), sy(182), sx(12), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_quote") }; + + ctrlMap[IDC_COLUMN_SORT_DESC_BUTTON] = { sx(406), sy(149), sx(14), sy(20), WC_BUTTON, symbolSortDesc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_descending") }; + ctrlMap[IDC_COLUMN_SORT_ASC_BUTTON] = { sx(421), sy(149), sx(14), sy(20), WC_BUTTON, symbolSortAsc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_ascending") }; + ctrlMap[IDC_COLUMN_DROP_BUTTON] = { sx(443), sy(149), sx(20), sy(20), WC_BUTTON, L"✖", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_drop_columns") }; + ctrlMap[IDC_COLUMN_COPY_BUTTON] = { sx(472), sy(149), sx(20), sy(20), WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_columns") }; + ctrlMap[IDC_COLUMN_HIGHLIGHT_BUTTON] = { sx(501), sy(149), sx(40), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_show"), BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_column_highlight") }; + + ctrlMap[IDC_STATUS_MESSAGE] = { sx(11), sy(208), sx(504), sy(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; // Dynamic positions and sizes - ctrlMap[IDC_FIND_EDIT] = { dpiMgr->scaleX(96), dpiMgr->scaleY(15), comboWidth, dpiMgr->scaleY(160), WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_EDIT] = { dpiMgr->scaleX(96), dpiMgr->scaleY(43), comboWidth, dpiMgr->scaleY(160), WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; - ctrlMap[IDC_SWAP_BUTTON] = { swapButtonX, dpiMgr->scaleY(26), dpiMgr->scaleX(22), dpiMgr->scaleY(27), WC_BUTTON, L"⇅", BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COPY_TO_LIST_BUTTON] = { buttonX, dpiMgr->scaleY(15), dpiMgr->scaleX(128), dpiMgr->scaleY(48), WC_BUTTON, getLangStrLPCWSTR(L"panel_add_into_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_STATIC_FRAME] = { frameX, dpiMgr->scaleY(79), dpiMgr->scaleX(228), dpiMgr->scaleY(130), WC_BUTTON, L"", BS_GROUPBOX, NULL }; - ctrlMap[IDC_REPLACE_ALL_BUTTON] = { buttonX, dpiMgr->scaleY(94), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_all"), BS_SPLITBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_BUTTON] = { buttonX, dpiMgr->scaleY(94), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_ALL_SMALL_BUTTON] = { buttonX + dpiMgr->scaleX(100), dpiMgr->scaleY(94), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"↻", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_replace_all") }; - ctrlMap[IDC_2_BUTTONS_MODE] = { checkbox2X, dpiMgr->scaleY(94), dpiMgr->scaleX(20), dpiMgr->scaleY(20), WC_BUTTON, L"", BS_AUTOCHECKBOX | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_2_buttons_mode") }; - ctrlMap[IDC_FIND_BUTTON] = { buttonX, dpiMgr->scaleY(122), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_find_next"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[ID_STATISTICS_COLUMNS] = { dpiMgr->scaleX(2), dpiMgr->scaleY(228), dpiMgr->scaleX(14), dpiMgr->scaleY(19), WC_BUTTON, L"▶", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, getLangStrLPCWSTR(L"tooltip_display_statistics_columns") }; + ctrlMap[IDC_FIND_EDIT] = { sx(96), sy(15), comboWidth, sy(160), WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_EDIT] = { sx(96), sy(43), comboWidth, sy(160), WC_COMBOBOX, NULL, CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP, NULL }; + ctrlMap[IDC_SWAP_BUTTON] = { swapButtonX, sy(26), sx(22), sy(27), WC_BUTTON, L"⇅", BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COPY_TO_LIST_BUTTON] = { buttonX, sy(15), sx(128), sy(48), WC_BUTTON, getLangStrLPCWSTR(L"panel_add_into_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_STATIC_FRAME] = { frameX, sy(79), sx(228), sy(130), WC_BUTTON, L"", BS_GROUPBOX, NULL }; + ctrlMap[IDC_REPLACE_ALL_BUTTON] = { buttonX, sy(94), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_all"), BS_SPLITBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_BUTTON] = { buttonX, sy(94), sx(96), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_ALL_SMALL_BUTTON] = { buttonX + sx(100), sy(94), sx(28), sy(24), WC_BUTTON, L"↻", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_replace_all") }; + ctrlMap[IDC_2_BUTTONS_MODE] = { checkbox2X, sy(94), sx(20), sy(20), WC_BUTTON, L"", BS_AUTOCHECKBOX | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_2_buttons_mode") }; + ctrlMap[IDC_FIND_BUTTON] = { buttonX, sy(122), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_find_next"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[ID_STATISTICS_COLUMNS] = { sx(2), sy(228), sx(14), sy(19), WC_BUTTON, L"▶", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, getLangStrLPCWSTR(L"tooltip_display_statistics_columns") }; findNextButtonText = L"▼ " + getLangStr(L"panel_find_next_small"); - ctrlMap[IDC_FIND_NEXT_BUTTON] = ControlInfo{ buttonX + dpiMgr->scaleX(32), dpiMgr->scaleY(122), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, findNextButtonText.c_str(), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - - ctrlMap[IDC_FIND_PREV_BUTTON] = { buttonX, dpiMgr->scaleY(122), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_MARK_BUTTON] = { buttonX, dpiMgr->scaleY(150), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_MARK_MATCHES_BUTTON] = { buttonX, dpiMgr->scaleY(150), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches_small"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COPY_MARKED_TEXT_BUTTON] = { buttonX + dpiMgr->scaleX(100), dpiMgr->scaleY(150), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_marked_text") }; - ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, dpiMgr->scaleY(178), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, dpiMgr->scaleY(227), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_LOAD_LIST_BUTTON] = { buttonX, dpiMgr->scaleY(227), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_NEW_LIST_BUTTON] = { buttonX + dpiMgr->scaleX(100), dpiMgr->scaleY(227), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"➕", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_new_list") }; - ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, dpiMgr->scaleY(255), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_SAVE_BUTTON] = { buttonX, dpiMgr->scaleY(255), dpiMgr->scaleX(28), dpiMgr->scaleY(24), WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; - ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + dpiMgr->scaleX(32), dpiMgr->scaleY(255), dpiMgr->scaleX(96), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_EXPORT_BASH_BUTTON] = { buttonX, dpiMgr->scaleY(283), dpiMgr->scaleX(128), dpiMgr->scaleY(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_export_to_bash"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_UP_BUTTON] = { buttonX + dpiMgr->scaleX(4), dpiMgr->scaleY(323), dpiMgr->scaleX(24), dpiMgr->scaleY(24), WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; - ctrlMap[IDC_DOWN_BUTTON] = { buttonX + dpiMgr->scaleX(4), dpiMgr->scaleY(323 + 24 + 4), dpiMgr->scaleX(24), dpiMgr->scaleY(24), WC_BUTTON, L"▼", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; - ctrlMap[IDC_SHIFT_FRAME] = { buttonX, dpiMgr->scaleY(323 - 11), dpiMgr->scaleX(132), dpiMgr->scaleY(68), WC_BUTTON, L"", BS_GROUPBOX, NULL }; - ctrlMap[IDC_SHIFT_TEXT] = { buttonX + dpiMgr->scaleX(30), dpiMgr->scaleY(323 + 16), dpiMgr->scaleX(96), dpiMgr->scaleY(16), WC_STATIC, getLangStrLPCWSTR(L"panel_shift_lines"), SS_LEFT, NULL }; - ctrlMap[IDC_REPLACE_LIST] = { dpiMgr->scaleX(20), dpiMgr->scaleY(227), listWidth, listHeight, WC_LISTVIEW, NULL, LVS_REPORT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_VSCROLL | LVS_SHOWSELALWAYS, NULL }; - ctrlMap[IDC_PATH_DISPLAY] = { dpiMgr->scaleX(18), windowHeight - dpiMgr->scaleY(15), listWidth, dpiMgr->scaleY(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL}; - ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, dpiMgr->scaleY(140), dpiMgr->scaleX(76), dpiMgr->scaleY(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_FIND_NEXT_BUTTON] = ControlInfo{ buttonX + sx(32), sy(122), sx(96), sy(24), WC_BUTTON, findNextButtonText.c_str(), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + + ctrlMap[IDC_FIND_PREV_BUTTON] = { buttonX, sy(122), sx(28), sy(24), WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_MARK_BUTTON] = { buttonX, sy(150), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_MARK_MATCHES_BUTTON] = { buttonX, sy(150), sx(96), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_mark_matches_small"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COPY_MARKED_TEXT_BUTTON] = { buttonX + sx(100), sy(150), sx(28), sy(24), WC_BUTTON, L"🗍", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_copy_marked_text") }; + ctrlMap[IDC_CLEAR_MARKS_BUTTON] = { buttonX, sy(178), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_clear_all_marks"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_LOAD_FROM_CSV_BUTTON] = { buttonX, sy(227), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_LOAD_LIST_BUTTON] = { buttonX, sy(227), sx(96), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_load_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_NEW_LIST_BUTTON] = { buttonX + sx(100), sy(227), sx(28), sy(24), WC_BUTTON, L"➕", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_new_list") }; + ctrlMap[IDC_SAVE_TO_CSV_BUTTON] = { buttonX, sy(255), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_save_list"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_SAVE_BUTTON] = { buttonX, sy(255), sx(28), sy(24), WC_BUTTON, L"💾", BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_save") }; + ctrlMap[IDC_SAVE_AS_BUTTON] = { buttonX + sx(32), sy(255), sx(96), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_save_as"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_EXPORT_BASH_BUTTON] = { buttonX, sy(283), sx(128), sy(24), WC_BUTTON, getLangStrLPCWSTR(L"panel_export_to_bash"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_UP_BUTTON] = { buttonX + sx(4), sy(323), sx(24), sy(24), WC_BUTTON, L"▲", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; + ctrlMap[IDC_DOWN_BUTTON] = { buttonX + sx(4), sy(323 + 24 + 4), sx(24), sy(24), WC_BUTTON, L"▼", BS_PUSHBUTTON | WS_TABSTOP | BS_CENTER, NULL }; + ctrlMap[IDC_SHIFT_FRAME] = { buttonX, sy(323 - 11), sx(132), sy(68), WC_BUTTON, L"", BS_GROUPBOX, NULL }; + ctrlMap[IDC_SHIFT_TEXT] = { buttonX + sx(30), sy(323 + 16), sx(96), sy(16), WC_STATIC, getLangStrLPCWSTR(L"panel_shift_lines"), SS_LEFT, NULL }; + ctrlMap[IDC_REPLACE_LIST] = { sx(20), sy(227), listWidth, listHeight, WC_LISTVIEW, NULL, LVS_REPORT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_VSCROLL | LVS_SHOWSELALWAYS, NULL }; + ctrlMap[IDC_PATH_DISPLAY] = { sx(18), sy(227) + listHeight + sy(5), listWidth, sy(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; + ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, sy(140), sx(76), sy(14), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; } void MultiReplace::initializeCtrlMap() { @@ -662,17 +662,18 @@ void MultiReplace::createListViewColumns(HWND listView) { // Extract width from the RECT int windowWidth = rcClient.right - rcClient.left; - // Calculate the remaining width for the first two columns - int adjustedWidth = windowWidth - dpiMgr->scaleX(225); + // Calculate the remaining width for the first two columns + // 225 equals (rcList.right - rcList.left); is fixed and has not to be calculated + int adjustedWidth = windowWidth - sx(225); // sx() Function to scale values for DPI awareness // Calculate the total width of columns 5 to 10 (Options and Delete Button) - int totalWidthColumn5to10 = column5to10Width_scaled * 7; + int totalWidthColumn5to10 = (checkMarkWidth_scaled * 6) + crossWidth_scaled; // Calculate the remaining width after subtracting the widths of the specified columns int remainingWidth = adjustedWidth - findCountColumnWidth - replaceCountColumnWidth - totalWidthColumn5to10; // Ensure remainingWidth is not less than the minimum width - remainingWidth = std::max(remainingWidth, dpiMgr->scaleX(MIN_COLUMN_WIDTH)); // Minimum size of Find and Replace Column + remainingWidth = std::max(remainingWidth, MIN_COLUMN_WIDTH_scaled); // Minimum size of Find and Replace Column lvc.iSubItem = 0; lvc.pszText = L""; @@ -694,7 +695,7 @@ void MultiReplace::createListViewColumns(HWND listView) { // Column for Selection lvc.iSubItem = 3; lvc.pszText = L"\u2610"; - lvc.cx = column3Width_scaled; + lvc.cx = boxWidth_scaled; lvc.fmt = LVCFMT_CENTER | LVCFMT_FIXED_WIDTH; ListView_InsertColumn(listView, 3, &lvc); @@ -716,7 +717,7 @@ void MultiReplace::createListViewColumns(HWND listView) { for (int i = 0; i < 5; i++) { lvc.iSubItem = 6 + i; lvc.pszText = getLangStrLPWSTR(options[i]); - lvc.cx = column5to10Width_scaled; + lvc.cx = checkMarkWidth_scaled; lvc.fmt = LVCFMT_CENTER | LVCFMT_FIXED_WIDTH; ListView_InsertColumn(listView, 6 + i, &lvc); } @@ -724,7 +725,7 @@ void MultiReplace::createListViewColumns(HWND listView) { // Column for Delete Button lvc.iSubItem = 11; lvc.pszText = L""; - lvc.cx = column5to10Width_scaled; + lvc.cx = crossWidth_scaled; ListView_InsertColumn(listView, 11, &lvc); //Adding Tooltips @@ -782,11 +783,12 @@ void MultiReplace::insertReplaceListItem(const ReplaceItemData& itemData) { int MultiReplace::calcDynamicColWidth(const CountColWidths& widths) { - int totalWidthColumn5to10 = column5to10Width_scaled * 7; // Simplified calculation + int totalWidthColumn5to10 = (checkMarkWidth_scaled * 6) + crossWidth_scaled; // Calculation for fixed Option columns // Directly calculate the width available for each dynamic column. int totalRemainingWidth = widths.listViewWidth - widths.margin - totalWidthColumn5to10 - widths.findCountWidth - widths.replaceCountWidth; - int perColumnWidth = std::max(totalRemainingWidth, MIN_COLUMN_WIDTH * 2) / 2; // Ensure total min width is 120, then divide by 2. + int perColumnWidth = std::max(totalRemainingWidth, MIN_COLUMN_WIDTH_scaled * 2) / 2; // Ensure total min width is 120, then divide by 2. + return perColumnWidth; // Return width for a single column. } @@ -812,14 +814,14 @@ int MultiReplace::getCharacterWidth(int elementID, const wchar_t* character) { return size.cx; } -void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { - int newWidth = LOWORD(lParam); // calculate ListWidth as lParam return WindowWidth - //int newHeight = HIWORD(lParam); - newWidth += newWidth; +void MultiReplace::updateListViewAndColumns() { // Retrieve the control information for IDC_REPLACE_LIST from ctrlMap const ControlInfo& listCtrlInfo = ctrlMap[IDC_REPLACE_LIST]; + // Get the ListView handle directly + HWND listView = GetDlgItem(_hSelf, IDC_REPLACE_LIST); + CountColWidths widths = { listView, listCtrlInfo.cx, // Direct use of newWidth for listViewWidth @@ -831,21 +833,19 @@ void MultiReplace::updateListViewAndColumns(HWND listView, LPARAM lParam) { // Calculate width available for each dynamic column. int perColumnWidth = calcDynamicColWidth(widths); - HWND listHwnd = GetDlgItem(_hSelf, IDC_REPLACE_LIST); - SendMessage(widths.listView, WM_SETREDRAW, FALSE, 0); + SendMessage(listView, WM_SETREDRAW, FALSE, 0); // Disable redraw during resize // Set column widths directly using calculated width. ListView_SetColumnWidth(listView, 4, perColumnWidth); // Find Text ListView_SetColumnWidth(listView, 5, perColumnWidth); // Replace Text - //MoveWindow(listHwnd, 20, dpiMgr->scaleY(227), newWidth - dpiMgr->scaleX(208), newHeight - dpiMgr->scaleY(248), TRUE); - MoveWindow(listHwnd, listCtrlInfo.x, listCtrlInfo.y, listCtrlInfo.cx, listCtrlInfo.cy, TRUE); + // Move the window with the correct dimensions + MoveWindow(listView, listCtrlInfo.x, listCtrlInfo.y, listCtrlInfo.cx, listCtrlInfo.cy, TRUE); - SendMessage(widths.listView, WM_SETREDRAW, TRUE, 0); - //InvalidateRect(widths.listView, NULL, TRUE); - //UpdateWindow(widths.listView); + SendMessage(listView, WM_SETREDRAW, TRUE, 0); // Enable redraw after resize } + void MultiReplace::updateListViewTooltips() { HWND hwndHeader = ListView_GetHeader(_replaceListView); @@ -1142,9 +1142,11 @@ void MultiReplace::resizeCountColumns() { HWND listView = GetDlgItem(_hSelf, IDC_REPLACE_LIST); RECT listRect; GetClientRect(listView, &listRect); + + // Calculate the total width of the ListView int listViewWidth = listRect.right - listRect.left; - int COUNT_COLUMN_WIDTH_scaled = dpiMgr->scaleX(COUNT_COLUMN_WIDTH); + // Determine Scrollbar width LONG style = GetWindowLong(listView, GWL_STYLE); bool hasVerticalScrollbar = (style & WS_VSCROLL) != 0; int scrollbarWidth = GetSystemMetrics(SM_CXVSCROLL); @@ -1155,7 +1157,7 @@ void MultiReplace::resizeCountColumns() { listViewWidth, ListView_GetColumnWidth(_replaceListView, 1), // Current Find Count Width ListView_GetColumnWidth(_replaceListView, 2), // Current Replace Count Width - margin + margin - 2 // -2 as the Listview borders are not part of the columns }; // Determine the direction of the adjustment @@ -1225,7 +1227,6 @@ std::size_t MultiReplace::computeListHash(const std::vector& li return combinedHash; } - #pragma endregion @@ -1881,9 +1882,6 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l { // Instantiate DPIManager dpiMgr = new DPIManager(_hSelf); - // For DPI debugging only - //dpiMgr->_dpiX = 70; - //dpiMgr->_dpiY = 70; loadLanguage(); initializeWindowSize(); pointerToScintilla(); @@ -1921,6 +1919,9 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l dpiMgr->updateDPI(_hSelf); } + // Apply scaled fonts after resizing controls + initializeFontStyles(); + // Rescale and reposition UI elements RECT rcClient; GetClientRect(_hSelf, &rcClient); @@ -1929,9 +1930,6 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l // Move and resize controls accordingly moveAndResizeControls(); - // Apply scaled fonts after resizing controls - initializeFontStyles(); - // Refresh the UI InvalidateRect(_hSelf, NULL, TRUE); return 0; @@ -2065,7 +2063,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l positionAndResizeControls(newWidth, newHeight); // Move and resize the List - updateListViewAndColumns(GetDlgItem(_hSelf, IDC_REPLACE_LIST), lParam); + updateListViewAndColumns(); // Move all Elements moveAndResizeControls(); @@ -7370,10 +7368,12 @@ void MultiReplace::loadUIConfigFromIni() { float customScaleFactor = readFloatFromIniFile(iniFilePath, L"Window", L"ScaleFactor", 1.0f); dpiMgr->setCustomScaleFactor(customScaleFactor); - // Scale Window Size after loading ScaleFactor - MIN_WIDTH_scaled = dpiMgr->scaleX(MIN_WIDTH); // MIN_WIDTH from resource.rc - MIN_HEIGHT_scaled = dpiMgr->scaleY(MIN_HEIGHT); // MIN_HEIGHT from resource.rc - SHRUNK_HEIGHT_scaled = dpiMgr->scaleY(SHRUNK_HEIGHT); // SHRUNK_HEIGHT from resource.rc + // Scale Window and List Size after loading ScaleFactor + MIN_WIDTH_scaled = sx(MIN_WIDTH); // MIN_WIDTH from resource.rc + MIN_HEIGHT_scaled = sy(MIN_HEIGHT); // MIN_HEIGHT from resource.rc + SHRUNK_HEIGHT_scaled = sy(SHRUNK_HEIGHT); // SHRUNK_HEIGHT from resource.rc + COUNT_COLUMN_WIDTH_scaled = sx(COUNT_COLUMN_WIDTH); // Scaled Size of Count Columns + MIN_COLUMN_WIDTH_scaled = sx(MIN_COLUMN_WIDTH); // Scaled Size of Minimum Size for Find and Replace // Load window position windowRect.left = readIntFromIniFile(iniFilePath, L"Window", L"PosX", POS_X); @@ -7404,7 +7404,7 @@ void MultiReplace::loadUIConfigFromIni() { findCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"FindCountWidth", findCountColumnWidth); replaceCountColumnWidth = readIntFromIniFile(iniFilePath, L"ListColumns", L"ReplaceCountWidth", replaceCountColumnWidth); - isStatisticsColumnsExpanded = (findCountColumnWidth >= dpiMgr->scaleX(COUNT_COLUMN_WIDTH) && replaceCountColumnWidth >= dpiMgr->scaleX(COUNT_COLUMN_WIDTH)); + isStatisticsColumnsExpanded = (findCountColumnWidth >= COUNT_COLUMN_WIDTH_scaled && replaceCountColumnWidth >= COUNT_COLUMN_WIDTH_scaled); // Load transparency settings with defaults foregroundTransparency = readByteFromIniFile(iniFilePath, L"Window", L"ForegroundTransparency", foregroundTransparency); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 6b2246e..79a9de3 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -264,6 +264,10 @@ class MultiReplace : public StaticDialog static MultiReplace* instance; // Static instance of the class + // Helper functions for scaling + inline int sx(int value) { return dpiMgr->scaleX(value); } + inline int sy(int value) { return dpiMgr->scaleY(value); } + static inline void setInstance(MultiReplace* inst) { instance = inst; } @@ -418,8 +422,9 @@ class MultiReplace : public StaticDialog std::size_t originalListHash = 0; int useListOnHeight = MIN_HEIGHT; // Default height when "Use List" is on const int useListOffHeight = SHRUNK_HEIGHT; // Height when "Use List" is off (constant) - int column5to10Width_scaled = 0; // Global variable for columns 5 to 10 - int column3Width_scaled = 0; // Global variable for column 3 + int checkMarkWidth_scaled = 0; + int crossWidth_scaled = 0; + int boxWidth_scaled = 0; // GUI control-related constants const std::vector selectionRadioDisabledButtons = { @@ -428,7 +433,7 @@ class MultiReplace : public StaticDialog const std::vector columnRadioDependentElements = { IDC_COLUMN_SORT_DESC_BUTTON, IDC_COLUMN_SORT_ASC_BUTTON, IDC_COLUMN_DROP_BUTTON, IDC_COLUMN_COPY_BUTTON, IDC_COLUMN_HIGHLIGHT_BUTTON }; - + // Window related settings RECT windowRect; // Structure to store window position and size int findCountColumnWidth = 0; // Width of the "Find Count" column @@ -440,6 +445,8 @@ class MultiReplace : public StaticDialog int MIN_WIDTH_scaled; int MIN_HEIGHT_scaled; int SHRUNK_HEIGHT_scaled; + int COUNT_COLUMN_WIDTH_scaled; + int MIN_COLUMN_WIDTH_scaled; //Initialization void initializeWindowSize(); @@ -464,7 +471,7 @@ class MultiReplace : public StaticDialog void createListViewColumns(HWND listView); void insertReplaceListItem(const ReplaceItemData& itemData); int calcDynamicColWidth(const CountColWidths& widths); - void updateListViewAndColumns(HWND listView, LPARAM lParam); + void updateListViewAndColumns(); void updateListViewTooltips(); void handleCopyBack(NMITEMACTIVATE* pnmia); void shiftListItem(HWND listView, const Direction& direction); From a9b1018780442da327f06fb0a6d59d9516f51c83 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 6 Oct 2024 14:21:17 +0200 Subject: [PATCH 29/51] updated Checkbox and Radiobutton heigt related to DPI --- src/DPIManager.cpp | 21 ++++++++++++ src/DPIManager.h | 13 ++++---- src/MultiReplacePanel.cpp | 68 +++++++++++++++++++++++++++------------ src/MultiReplacePanel.h | 7 ++-- 4 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/DPIManager.cpp b/src/DPIManager.cpp index a96a5c4..6d56901 100644 --- a/src/DPIManager.cpp +++ b/src/DPIManager.cpp @@ -51,10 +51,31 @@ void DPIManager::init() } } + // Check if GetSystemMetricsForDpi is supported + HMODULE hUser32 = LoadLibrary(TEXT("User32.dll")); + if (hUser32) + { + _pGetSystemMetricsForDpi = (decltype(GetSystemMetricsForDpi)*)GetProcAddress(hUser32, "GetSystemMetricsForDpi"); + _isSystemMetricsForDpiSupported = (_pGetSystemMetricsForDpi != nullptr); + FreeLibrary(hUser32); + } + _dpiX = dpiX; _dpiY = dpiY; } +// Function for custom metrics with fallback. +int DPIManager::getCustomMetricOrFallback(int nIndex, UINT dpi, int fallbackValue) const +{ + if (_isSystemMetricsForDpiSupported && _pGetSystemMetricsForDpi) + { + return _pGetSystemMetricsForDpi(nIndex, dpi); + } + + // Return the fallback value if not supported + return fallbackValue; +} + // Updates the DPI values, typically called when DPI changes. void DPIManager::updateDPI(HWND hwnd) { diff --git a/src/DPIManager.h b/src/DPIManager.h index fe75504..45c74da 100644 --- a/src/DPIManager.h +++ b/src/DPIManager.h @@ -32,8 +32,6 @@ class DPIManager int getDPIY() const { return _dpiY; } // Converts raw pixels to scaled pixels, includes the custom scale factor. - //int scaleX(int x) const { return static_cast(MulDiv(x, _dpiX, 120) * _customScaleFactor); } - //int scaleY(int y) const { return static_cast(MulDiv(y, _dpiY, 120) * _customScaleFactor); } int scaleX(int x) const { return static_cast(MulDiv(x, _dpiX, 96) * _customScaleFactor); } int scaleY(int y) const { return static_cast(MulDiv(y, _dpiY, 96) * _customScaleFactor); } @@ -53,13 +51,16 @@ class DPIManager // Updates DPI values (e.g., after a DPI change event). void updateDPI(HWND hwnd); + // Function for custom metrics with fallback. + int getCustomMetricOrFallback(int nIndex, UINT dpi, int fallbackValue) const; + +private: + HWND _hwnd; // Handle to the window. int _dpiX; // Horizontal DPI. int _dpiY; // Vertical DPI. float _customScaleFactor; // Custom scaling factor stored INI file. - -private: - HWND _hwnd; // Handle to the window. - + bool _isSystemMetricsForDpiSupported; // To check if GetSystemMetricsForDpi is supported + decltype(GetSystemMetricsForDpi)* _pGetSystemMetricsForDpi; // Pointer to the function // Initializes the DPI values. void init(); diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index ea5fc62..3c96c79 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -93,7 +93,7 @@ void MultiReplace::initializeFontStyles() { if (!dpiMgr) return; // Create a standard scaled font - HFONT hStandardFont = CreateFont( + _hStandardFont = CreateFont( dpiMgr->scaleY(14), // Font height 0, // Font width (0 means default) 0, // Escapement @@ -112,11 +112,11 @@ void MultiReplace::initializeFontStyles() { // Apply the standard font to all controls for (const auto& pair : ctrlMap) { - SendMessage(GetDlgItem(_hSelf, pair.first), WM_SETFONT, (WPARAM)hStandardFont, TRUE); + SendMessage(GetDlgItem(_hSelf, pair.first), WM_SETFONT, (WPARAM)_hStandardFont, TRUE); } // Create and apply a bold font for specific controls - HFONT hBoldFont = CreateFont( + _hBoldFont = CreateFont( dpiMgr->scaleY(19), // Larger font height 0, 0, @@ -134,10 +134,10 @@ void MultiReplace::initializeFontStyles() { ); // Apply the bold font to specific controls - SendMessage(GetDlgItem(_hSelf, IDC_SWAP_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); - SendMessage(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); - SendMessage(GetDlgItem(_hSelf, IDC_COLUMN_COPY_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); - SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_SWAP_BUTTON), WM_SETFONT, (WPARAM)_hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_COPY_MARKED_TEXT_BUTTON), WM_SETFONT, (WPARAM)_hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_COLUMN_COPY_BUTTON), WM_SETFONT, (WPARAM)_hBoldFont, TRUE); + SendMessage(GetDlgItem(_hSelf, IDC_REPLACE_ALL_SMALL_BUTTON), WM_SETFONT, (WPARAM)_hBoldFont, TRUE); // For ListView identify width of special characters // Add 15 units of padding to the widths of checkmark, cross, and box characters @@ -180,6 +180,20 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) // sx(value): scales the x-axis value based on DPI settings. // sy(value): scales the y-axis value based on DPI settings. + // DPI Aware System Metrics for Checkboxes and Radio Buttons + UINT dpi = dpiMgr->getDPIX(); // Get the DPI from DPIManager + + // Calculate appropriate height for checkboxes and radiobuttons + int checkboxBaseHeight = dpiMgr->getCustomMetricOrFallback(SM_CYMENUCHECK, dpi, 14); + int radioButtonBaseHeight = dpiMgr->getCustomMetricOrFallback(SM_CYMENUCHECK, dpi, 14); + + // Get the font height from the standard font + int fontHeight = getFontHeight(_hSelf, _hStandardFont); + + // Choose the larger value between the font height and the base height + int checkboxHeight = std::max(checkboxBaseHeight, fontHeight); + int radioButtonHeight = std::max(radioButtonBaseHeight, fontHeight); + // Calculate dimensions without scaling int buttonX = windowWidth - sx(36 + 128); int checkbox2X = buttonX + sx(138); @@ -194,22 +208,22 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_STATIC_FIND] = { sx(11), sy(15), sx(80), sy(19), WC_STATIC, getLangStrLPCWSTR(L"panel_find_what"), SS_RIGHT, NULL }; ctrlMap[IDC_STATIC_REPLACE] = { sx(11), sy(43), sx(80), sy(19), WC_STATIC, getLangStrLPCWSTR(L"panel_replace_with"), SS_RIGHT }; - ctrlMap[IDC_WHOLE_WORD_CHECKBOX] = { sx(16), sy(76), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_whole_word_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_MATCH_CASE_CHECKBOX] = { sx(16), sy(101), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_match_case"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_USE_VARIABLES_CHECKBOX] = { sx(16), sy(126), sx(134), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_variables"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_WHOLE_WORD_CHECKBOX] = { sx(16), sy(76), sx(158), checkboxHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_match_whole_word_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_MATCH_CASE_CHECKBOX] = { sx(16), sy(101), sx(158), checkboxHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_match_case"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_USE_VARIABLES_CHECKBOX] = { sx(16), sy(126), sx(134), checkboxHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_use_variables"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; ctrlMap[IDC_USE_VARIABLES_HELP] = { sx(152), sy(126), sx(20), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_help"), BS_PUSHBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REPLACE_FIRST_CHECKBOX] = { sx(16), sy(151), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_first_match_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; - ctrlMap[IDC_WRAP_AROUND_CHECKBOX] = { sx(16), sy(176), sx(158), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_wrap_around"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_REPLACE_FIRST_CHECKBOX] = { sx(16), sy(151), sx(158), checkboxHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_replace_first_match_only"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_WRAP_AROUND_CHECKBOX] = { sx(16), sy(176), sx(158), checkboxHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_wrap_around"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; ctrlMap[IDC_SEARCH_MODE_GROUP] = { sx(180), sy(79), sx(160), sy(104), WC_BUTTON, getLangStrLPCWSTR(L"panel_search_mode"), BS_GROUPBOX, NULL }; - ctrlMap[IDC_NORMAL_RADIO] = { sx(188), sy(101), sx(144), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_normal"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; - ctrlMap[IDC_EXTENDED_RADIO] = { sx(188), sy(126), sx(144), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_extended"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_REGEX_RADIO] = { sx(188), sy(150), sx(144), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_regular_expression"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_NORMAL_RADIO] = { sx(188), sy(101), sx(144), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_normal"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; + ctrlMap[IDC_EXTENDED_RADIO] = { sx(188), sy(126), sx(144), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_extended"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_REGEX_RADIO] = { sx(188), sy(150), sx(144), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_regular_expression"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_SCOPE_GROUP] = { sx(352), sy(79), sx(198), sy(130), WC_BUTTON, getLangStrLPCWSTR(L"panel_scope"), BS_GROUPBOX, NULL }; - ctrlMap[IDC_ALL_TEXT_RADIO] = { sx(360), sy(101), sx(184), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; - ctrlMap[IDC_SELECTION_RADIO] = { sx(360), sy(126), sx(184), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_MODE_RADIO] = { sx(360), sy(150), sx(40), sy(20), WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_ALL_TEXT_RADIO] = { sx(360), sy(101), sx(184), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; + ctrlMap[IDC_SELECTION_RADIO] = { sx(360), sy(126), sx(184), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_MODE_RADIO] = { sx(360), sy(150), sx(40), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_COLUMN_NUM_STATIC] = { sx(360), sy(182), sx(24), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; ctrlMap[IDC_COLUMN_NUM_EDIT] = { sx(386), sy(182), sx(40), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; ctrlMap[IDC_DELIMITER_STATIC] = { sx(430), sy(182), sx(32), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; @@ -259,7 +273,7 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_SHIFT_TEXT] = { buttonX + sx(30), sy(323 + 16), sx(96), sy(16), WC_STATIC, getLangStrLPCWSTR(L"panel_shift_lines"), SS_LEFT, NULL }; ctrlMap[IDC_REPLACE_LIST] = { sx(20), sy(227), listWidth, listHeight, WC_LISTVIEW, NULL, LVS_REPORT | LVS_OWNERDATA | WS_BORDER | WS_TABSTOP | WS_VSCROLL | LVS_SHOWSELALWAYS, NULL }; ctrlMap[IDC_PATH_DISPLAY] = { sx(18), sy(227) + listHeight + sy(5), listWidth, sy(19), WC_STATIC, L"", WS_VISIBLE | SS_LEFT, NULL }; - ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, sy(140), sx(76), sy(14), WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; + ctrlMap[IDC_USE_LIST_CHECKBOX] = { checkboxX, sy(140), sx(76), checkboxHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_use_list"), BS_AUTOCHECKBOX | WS_TABSTOP, NULL }; } void MultiReplace::initializeCtrlMap() { @@ -845,7 +859,6 @@ void MultiReplace::updateListViewAndColumns() { SendMessage(listView, WM_SETREDRAW, TRUE, 0); // Enable redraw after resize } - void MultiReplace::updateListViewTooltips() { HWND hwndHeader = ListView_GetHeader(_replaceListView); @@ -2016,7 +2029,8 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l DestroyWindow(hwndEdit); } - DeleteObject(_hFont); + DeleteObject(_hStandardFont); + DeleteObject(_hBoldFont); // Close the debug window if open if (hDebugWnd != NULL) { @@ -6418,6 +6432,18 @@ std::vector MultiReplace::createFilterString(const std::vector createFilterString(const std::vector>& filters); int getCharacterWidth(int elementID, const wchar_t* character); + int getFontHeight(HWND hwnd, HFONT hFont); //StringHandling std::wstring stringToWString(const std::string& encodedInput) const; From 49f93355495907c4be59aca31dba0a0096365199 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Sun, 6 Oct 2024 17:46:19 +0200 Subject: [PATCH 30/51] updated Width for CSV Static Text --- src/MultiReplacePanel.cpp | 111 ++++++++++++++++++++++++-------------- src/MultiReplacePanel.h | 1 + 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 3c96c79..8f38a76 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -223,13 +223,13 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_SCOPE_GROUP] = { sx(352), sy(79), sx(198), sy(130), WC_BUTTON, getLangStrLPCWSTR(L"panel_scope"), BS_GROUPBOX, NULL }; ctrlMap[IDC_ALL_TEXT_RADIO] = { sx(360), sy(101), sx(184), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; ctrlMap[IDC_SELECTION_RADIO] = { sx(360), sy(126), sx(184), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_MODE_RADIO] = { sx(360), sy(150), sx(40), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; - ctrlMap[IDC_COLUMN_NUM_STATIC] = { sx(360), sy(182), sx(24), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; - ctrlMap[IDC_COLUMN_NUM_EDIT] = { sx(386), sy(182), sx(40), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; - ctrlMap[IDC_DELIMITER_STATIC] = { sx(430), sy(182), sx(32), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; + ctrlMap[IDC_COLUMN_MODE_RADIO] = { sx(361), sy(150), sx(40), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_NUM_STATIC] = { sx(358), sy(182), sx(25), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; + ctrlMap[IDC_COLUMN_NUM_EDIT] = { sx(385), sy(182), sx(40), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; + ctrlMap[IDC_DELIMITER_STATIC] = { sx(427), sy(182), sx(35), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; ctrlMap[IDC_DELIMITER_EDIT] = { sx(464), sy(182), sx(24), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_delimiter") }; - ctrlMap[IDC_QUOTECHAR_STATIC] = { sx(493), sy(182), sx(32), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_quote"), SS_RIGHT, NULL }; - ctrlMap[IDC_QUOTECHAR_EDIT] = { sx(526), sy(182), sx(12), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_quote") }; + ctrlMap[IDC_QUOTECHAR_STATIC] = { sx(490), sy(182), sx(35), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_quote"), SS_RIGHT, NULL }; + ctrlMap[IDC_QUOTECHAR_EDIT] = { sx(527), sy(182), sx(12), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_quote") }; ctrlMap[IDC_COLUMN_SORT_DESC_BUTTON] = { sx(406), sy(149), sx(14), sy(20), WC_BUTTON, symbolSortDesc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_descending") }; ctrlMap[IDC_COLUMN_SORT_ASC_BUTTON] = { sx(421), sy(149), sx(14), sy(20), WC_BUTTON, symbolSortAsc, BS_PUSHBUTTON | WS_TABSTOP, getLangStrLPCWSTR(L"tooltip_sort_ascending") }; @@ -2245,7 +2245,6 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l case LVN_KEYDOWN: { LPNMLVKEYDOWN pnkd = reinterpret_cast(pnmh); - HDC hDC = NULL; int iItem = -1; PostMessage(_replaceListView, WM_SETFOCUS, 0, 0); @@ -2296,39 +2295,7 @@ INT_PTR CALLBACK MultiReplace::run_dlgProc(UINT message, WPARAM wParam, LPARAM l break; case VK_F12: // F12 key { - RECT sizeWindowRect; - GetClientRect(_hSelf, &sizeWindowRect); - - hDC = GetDC(_hSelf); - if (hDC) - { - // Get the current font of the window - HFONT currentFont = (HFONT)SendMessage(_hSelf, WM_GETFONT, 0, 0); - HFONT hOldFont = (HFONT)SelectObject(hDC, currentFont); - - // Get the text metrics for the current font - TEXTMETRIC tm; - GetTextMetrics(hDC, &tm); - - // Calculate the base units - SIZE size; - GetTextExtentPoint32W(hDC, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); - int baseUnitX = (size.cx / 26 + 1) / 2; - int baseUnitY = tm.tmHeight; - - // Calculate the window size in dialog units - int duWidth = MulDiv(sizeWindowRect.right, 4, baseUnitX); - int duHeight = MulDiv(sizeWindowRect.bottom, 8, baseUnitY); - - wchar_t sizeText[100]; - wsprintfW(sizeText, L"Window Size: %ld x %ld DUs", duWidth, duHeight); - - MessageBox(nppData._nppHandle, sizeText, L"Window Size", MB_OK); - - // Cleanup - SelectObject(hDC, hOldFont); - ReleaseDC(_hSelf, hDC); - } + showDPIAndFontInfo(); // Show the DPI and font information break; } case VK_SPACE: // Spacebar key @@ -6443,6 +6410,70 @@ int MultiReplace::getFontHeight(HWND hwnd, HFONT hFont) { return fontHeight; // Return the font height } +void MultiReplace::showDPIAndFontInfo() +{ + RECT sizeWindowRect; + GetClientRect(_hSelf, &sizeWindowRect); // Get the dimensions of the client area of the window + + HDC hDC = GetDC(_hSelf); // Get the device context (DC) for the current window + if (hDC) + { + // Get the current font of the window + HFONT currentFont = (HFONT)SendMessage(_hSelf, WM_GETFONT, 0, 0); + HFONT hOldFont = (HFONT)SelectObject(hDC, currentFont); // Select the current font into the DC + + // Get the text metrics for the current font + TEXTMETRIC tmCurrent; + GetTextMetrics(hDC, &tmCurrent); // Retrieve text metrics for the selected font + + // Now get the standard font metrics (_hStandardFont) + SelectObject(hDC, _hStandardFont); // Select the standard font into the DC + TEXTMETRIC tmStandard; + GetTextMetrics(hDC, &tmStandard); // Retrieve text metrics for the standard font + + // Calculate the base units + SIZE size; + GetTextExtentPoint32W(hDC, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); + int baseUnitX = (size.cx / 26 + 1) / 2; + int baseUnitY = tmCurrent.tmHeight; + + // Calculate the window size in dialog units + int duWidth = MulDiv(sizeWindowRect.right, 4, baseUnitX); + int duHeight = MulDiv(sizeWindowRect.bottom, 8, baseUnitY); + + // Get DPI values from the DPIManager + int dpix = dpiMgr->getDPIX(); + int dpiy = dpiMgr->getDPIY(); + float customScaleFactor = dpiMgr->getCustomScaleFactor(); + + // Calculate scaled DPI values + int scaledDpiX = dpiMgr->scaleX(96); // example using 96 for standard DPI + int scaledDpiY = dpiMgr->scaleY(96); + + // Create a message box to display the window size, DPI, and font information + wchar_t sizeText[500]; + swprintf(sizeText, 500, L"Window Size: %ld x %ld DUs\n" + L"DPI X: %d\nDPI Y: %d\nCustom Scale Factor: %.2f\n" + L"Scaled DPI X: %d\nScaled DPI Y: %d\n\n" + L"Current Font: Height = %d, Ascent = %d, Descent = %d, Weight = %d\n" + L"Standard Font: Height = %d, Ascent = %d, Descent = %d, Weight = %d", + duWidth, duHeight, dpix, dpiy, customScaleFactor, scaledDpiX, scaledDpiY, + tmCurrent.tmHeight, tmCurrent.tmAscent, tmCurrent.tmDescent, tmCurrent.tmWeight, + tmStandard.tmHeight, tmStandard.tmAscent, tmStandard.tmDescent, tmStandard.tmWeight); + + MessageBox(nppData._nppHandle, sizeText, L"Window, DPI, and Font Info", MB_OK); + + // Cleanup + SelectObject(hDC, hOldFont); // Restore the previous font in the DC + ReleaseDC(_hSelf, hDC); // Release the device context + } + else + { + MessageBox(_hSelf, L"Failed to retrieve device context (HDC).", L"Error", MB_OK | MB_ICONERROR); + } +} + + #pragma endregion diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 96f1a7f..11cf60b 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -592,6 +592,7 @@ class MultiReplace : public StaticDialog bool normalizeAndValidateNumber(std::string& str); std::vector createFilterString(const std::vector>& filters); int getCharacterWidth(int elementID, const wchar_t* character); + void showDPIAndFontInfo(); int getFontHeight(HWND hwnd, HFONT hFont); //StringHandling From 28308a5fa124ad98a7289f7f5319b49ae8fac5f9 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 7 Oct 2024 21:03:30 +0200 Subject: [PATCH 31/51] higligt list enry for found item --- src/MultiReplacePanel.cpp | 20 ++++++++++++++++++++ src/MultiReplacePanel.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 8f38a76..075d60b 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -2952,6 +2952,7 @@ void MultiReplace::handleReplaceButton() { if (replaceListData[i].isEnabled && replaceOne(replaceListData[i], selection, searchResult, newPos, startPos)) { replacements++; updateCountColumns(i, -1, 1); + selectListItem(matchIndex); } } @@ -2965,6 +2966,7 @@ void MultiReplace::handleReplaceButton() { if (replacements > 0) { if (searchResult.pos >= 0) { updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); showStatusMessage(getLangStr(L"status_replace_next_found", { std::to_wstring(replacements) }), RGB(0, 128, 0)); } else { @@ -4022,6 +4024,7 @@ void MultiReplace::handleFindNextButton() { result = performListSearchForward(replaceListData, 0, matchIndex); if (result.pos >= 0) { updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); // Highlight the matched item in the list showStatusMessage(getLangStr(L"status_wrapped"), RGB(0, 128, 0)); return; } @@ -4030,6 +4033,7 @@ void MultiReplace::handleFindNextButton() { if (result.pos >= 0) { showStatusMessage(L"", RGB(0, 128, 0)); updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); // Highlight the matched item in the list } else { showStatusMessage(getLangStr(L"status_no_matches_found"), RGB(255, 0, 0)); @@ -4084,6 +4088,7 @@ void MultiReplace::handleFindPrevButton() { if (result.pos >= 0) { updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); // Highlight the matched item in the list showStatusMessage(L"" + addLineAndColumnMessage(result.pos), RGB(0, 128, 0)); } else if (wrapAroundEnabled) @@ -4091,6 +4096,7 @@ void MultiReplace::handleFindPrevButton() { result = performListSearchBackward(replaceListData, ::SendMessage(_hScintilla, SCI_GETLENGTH, 0, 0), matchIndex); if (result.pos >= 0) { updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); // Highlight the matched item in the list showStatusMessage(getLangStr(L"status_wrapped_position", { addLineAndColumnMessage(result.pos) }), RGB(0, 128, 0)); } else { @@ -4461,6 +4467,20 @@ void MultiReplace::displayResultCentered(size_t posStart, size_t posEnd, bool is } +void MultiReplace::selectListItem(size_t matchIndex) { + HWND hListView = GetDlgItem(_hSelf, IDC_REPLACE_LIST); + if (hListView && matchIndex != std::numeric_limits::max()) { + // Deselect all items + ListView_SetItemState(hListView, -1, 0, LVIS_SELECTED); + + // Select the item at matchIndex + ListView_SetItemState(hListView, static_cast(matchIndex), LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + + // Ensure the item is visible + ListView_EnsureVisible(hListView, static_cast(matchIndex), FALSE); + } +} + #pragma endregion diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 11cf60b..e80fb78 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -532,6 +532,7 @@ class MultiReplace : public StaticDialog SearchResult performSearchBackward(const std::string& findTextUtf8, int searchFlags, LRESULT start); SearchResult performListSearchForward(const std::vector& list, LRESULT cursorPos, size_t& closestMatchIndex); SearchResult performListSearchBackward(const std::vector& list, LRESULT cursorPos, size_t& closestMatchIndex); + void MultiReplace::selectListItem(size_t matchIndex); //Mark void handleMarkMatchesButton(); From 87dba2ab7d82332fd59613c456ba6734ab63f81b Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 7 Oct 2024 21:19:39 +0200 Subject: [PATCH 32/51] rolled back handleReplaceButton() and replaceOne() from previous changes --- src/MultiReplacePanel.cpp | 36 ++++++++++-------------------------- src/MultiReplacePanel.h | 2 +- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 075d60b..96f029d 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -2911,7 +2911,7 @@ void MultiReplace::handleReplaceButton() { // First check if the document is read-only LRESULT isReadOnly = ::SendMessage(_hScintilla, SCI_GETREADONLY, 0, 0); if (isReadOnly) { - showStatusMessage(getLangStr(L"status_cannot_replace_read_only"), RGB(255, 0, 0)); + showStatusMessage(getLangStrLPWSTR(L"status_cannot_replace_read_only"), RGB(255, 0, 0)); return; } @@ -2921,20 +2921,13 @@ void MultiReplace::handleReplaceButton() { bool useListEnabled = (IsDlgButtonChecked(_hSelf, IDC_USE_LIST_CHECKBOX) == BST_CHECKED); bool wrapAroundEnabled = (IsDlgButtonChecked(_hSelf, IDC_WRAP_AROUND_CHECKBOX) == BST_CHECKED); - // Determine the scope - bool isSelectionScope = (IsDlgButtonChecked(_hSelf, IDC_SELECTION_RADIO) == BST_CHECKED); - - SelectionInfo selection = getSelectionInfo(); - - Sci_Position startPos = isSelectionScope ? selection.startPos : 0; - size_t matchIndex = std::numeric_limits::max(); - SearchResult searchResult; searchResult.pos = -1; searchResult.length = 0; searchResult.foundText = ""; Sci_Position newPos = ::SendMessage(_hScintilla, SCI_GETCURRENTPOS, 0, 0); + size_t matchIndex = std::numeric_limits::max(); if (useListEnabled) { if (replaceListData.empty()) { @@ -2947,12 +2940,13 @@ void MultiReplace::handleReplaceButton() { return; } + SelectionInfo selection = getSelectionInfo(); + int replacements = 0; // Counter for replacements for (size_t i = 0; i < replaceListData.size(); ++i) { - if (replaceListData[i].isEnabled && replaceOne(replaceListData[i], selection, searchResult, newPos, startPos)) { + if (replaceListData[i].isEnabled && replaceOne(replaceListData[i], selection, searchResult, newPos)) { replacements++; updateCountColumns(i, -1, 1); - selectListItem(matchIndex); } } @@ -2966,7 +2960,6 @@ void MultiReplace::handleReplaceButton() { if (replacements > 0) { if (searchResult.pos >= 0) { updateCountColumns(matchIndex, 1); - selectListItem(matchIndex); showStatusMessage(getLangStr(L"status_replace_next_found", { std::to_wstring(replacements) }), RGB(0, 128, 0)); } else { @@ -2995,7 +2988,8 @@ void MultiReplace::handleReplaceButton() { std::string findTextUtf8 = convertAndExtend(replaceItem.findText, replaceItem.extended); int searchFlags = (replaceItem.wholeWord * SCFIND_WHOLEWORD) | (replaceItem.matchCase * SCFIND_MATCHCASE) | (replaceItem.regex * SCFIND_REGEXP); - bool wasReplaced = replaceOne(replaceItem, selection, searchResult, newPos, startPos); + SelectionInfo selection = getSelectionInfo(); + bool wasReplaced = replaceOne(replaceItem, selection, searchResult, newPos); // Add the entered text to the combo box history addStringToComboBoxHistory(GetDlgItem(_hSelf, IDC_FIND_EDIT), replaceItem.findText); @@ -3027,23 +3021,13 @@ void MultiReplace::handleReplaceButton() { } } -bool MultiReplace::replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos, Sci_Position startPos) +bool MultiReplace::replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos) { std::string findTextUtf8 = convertAndExtend(itemData.findText, itemData.extended); int searchFlags = (itemData.wholeWord * SCFIND_WHOLEWORD) | (itemData.matchCase * SCFIND_MATCHCASE) | (itemData.regex * SCFIND_REGEXP); - searchResult = performSearchForward(findTextUtf8, searchFlags, true, startPos); - - // Modify the condition to check if the found position matches the selection (only if scope is "Selection") - bool isSelectionScope = (IsDlgButtonChecked(_hSelf, IDC_SELECTION_RADIO) == BST_CHECKED); - - if (isSelectionScope) { - // If the scope is "Selection" and the found text does not match the selection, do not replace - if (searchResult.pos != selection.startPos || searchResult.length != selection.length) { - return false; - } - } + searchResult = performSearchForward(findTextUtf8, searchFlags, true, selection.startPos); - if (searchResult.pos >= 0) { + if (searchResult.pos == selection.startPos && searchResult.length == selection.length) { bool skipReplace = false; std::string replaceTextUtf8 = convertAndExtend(itemData.replaceText, itemData.extended); std::string localReplaceTextUtf8 = wstringToString(itemData.replaceText); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index e80fb78..1ec0f47 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -507,7 +507,7 @@ class MultiReplace : public StaticDialog void handleReplaceAllButton(); void handleReplaceButton(); void replaceAll(const ReplaceItemData& itemData, int& findCount, int& replaceCount); - bool replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos, Sci_Position startPos); + bool MultiReplace::replaceOne(const ReplaceItemData& itemData, const SelectionInfo& selection, SearchResult& searchResult, Sci_Position& newPos); Sci_Position performReplace(const std::string& replaceTextUtf8, Sci_Position pos, Sci_Position length); Sci_Position performRegexReplace(const std::string& replaceTextUtf8, Sci_Position pos, Sci_Position length); bool MultiReplace::preProcessListForReplace(); From 5e5d8e4904d9630ed56e645c0014f47278744281 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Mon, 7 Oct 2024 22:14:11 +0200 Subject: [PATCH 33/51] added list ntry higlighting for Replace --- src/MultiReplacePanel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 96f029d..677a93b 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -2960,6 +2960,7 @@ void MultiReplace::handleReplaceButton() { if (replacements > 0) { if (searchResult.pos >= 0) { updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); // Highlight the matched item in the list showStatusMessage(getLangStr(L"status_replace_next_found", { std::to_wstring(replacements) }), RGB(0, 128, 0)); } else { @@ -2971,6 +2972,8 @@ void MultiReplace::handleReplaceButton() { showStatusMessage(getLangStr(L"status_no_occurrence_found"), RGB(255, 0, 0)); } else { + updateCountColumns(matchIndex, 1); + selectListItem(matchIndex); // Highlight the matched item in the list showStatusMessage(getLangStr(L"status_found_text_not_replaced"), RGB(255, 0, 0)); } } From 717a4fc1fde01790bd580364eb00e8352e2ad0cf Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Tue, 8 Oct 2024 13:43:48 +0200 Subject: [PATCH 34/51] updated getSelectionInfo() --- src/MultiReplacePanel.cpp | 39 ++++++++++++++++----------------------- src/MultiReplacePanel.h | 8 ++++---- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 677a93b..f88cf93 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -224,6 +224,7 @@ void MultiReplace::positionAndResizeControls(int windowWidth, int windowHeight) ctrlMap[IDC_ALL_TEXT_RADIO] = { sx(360), sy(101), sx(184), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_all_text"), BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, NULL }; ctrlMap[IDC_SELECTION_RADIO] = { sx(360), sy(126), sx(184), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_selection"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; ctrlMap[IDC_COLUMN_MODE_RADIO] = { sx(361), sy(150), sx(40), radioButtonHeight, WC_BUTTON, getLangStrLPCWSTR(L"panel_csv"), BS_AUTORADIOBUTTON | WS_TABSTOP, NULL }; + ctrlMap[IDC_COLUMN_NUM_STATIC] = { sx(358), sy(182), sx(25), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_cols"), SS_RIGHT, NULL }; ctrlMap[IDC_COLUMN_NUM_EDIT] = { sx(385), sy(182), sx(40), sy(16), WC_EDIT, NULL, ES_LEFT | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, getLangStrLPCWSTR(L"tooltip_columns") }; ctrlMap[IDC_DELIMITER_STATIC] = { sx(427), sy(182), sx(35), sy(20), WC_STATIC, getLangStrLPCWSTR(L"panel_delim"), SS_RIGHT, NULL }; @@ -2926,7 +2927,11 @@ void MultiReplace::handleReplaceButton() { searchResult.length = 0; searchResult.foundText = ""; - Sci_Position newPos = ::SendMessage(_hScintilla, SCI_GETCURRENTPOS, 0, 0); + SelectionInfo selection = getSelectionInfo(); + + // If there is a selection, set newPos to the start of the selection; otherwise, use the current cursor position + Sci_Position newPos = (selection.length > 0) ? selection.startPos : ::SendMessage(_hScintilla, SCI_GETCURRENTPOS, 0, 0); + size_t matchIndex = std::numeric_limits::max(); if (useListEnabled) { @@ -2940,8 +2945,6 @@ void MultiReplace::handleReplaceButton() { return; } - SelectionInfo selection = getSelectionInfo(); - int replacements = 0; // Counter for replacements for (size_t i = 0; i < replaceListData.size(); ++i) { if (replaceListData[i].isEnabled && replaceOne(replaceListData[i], selection, searchResult, newPos)) { @@ -2991,7 +2994,6 @@ void MultiReplace::handleReplaceButton() { std::string findTextUtf8 = convertAndExtend(replaceItem.findText, replaceItem.extended); int searchFlags = (replaceItem.wholeWord * SCFIND_WHOLEWORD) | (replaceItem.matchCase * SCFIND_MATCHCASE) | (replaceItem.regex * SCFIND_REGEXP); - SelectionInfo selection = getSelectionInfo(); bool wasReplaced = replaceOne(replaceItem, selection, searchResult, newPos); // Add the entered text to the combo box history @@ -3232,28 +3234,19 @@ bool MultiReplace::preProcessListForReplace() { SelectionInfo MultiReplace::getSelectionInfo() { // Get the number of selections - int selectionCount = static_cast(::SendMessage(_hScintilla, SCI_GETSELECTIONS, 0, 0)); - + LRESULT selectionCount = ::SendMessage(_hScintilla, SCI_GETSELECTIONS, 0, 0); Sci_Position selectionStart = 0; Sci_Position selectionEnd = 0; - std::string selectedText = ""; - if (selectionCount == 1) { - // Single selection - selectionStart = ::SendMessage(_hScintilla, SCI_GETSELECTIONSTART, 0, 0); - selectionEnd = ::SendMessage(_hScintilla, SCI_GETSELECTIONEND, 0, 0); - Sci_Position selectionLength = selectionEnd - selectionStart; - - if (selectionLength > 0) { - std::vector buffer(static_cast(selectionLength) + 1); - ::SendMessage(_hScintilla, SCI_GETSELTEXT, 0, reinterpret_cast(&buffer[0])); - selectedText = std::string(&buffer[0]); - } + if (selectionCount > 0) { + // Take the first selection in the list + selectionStart = ::SendMessage(_hScintilla, SCI_GETSELECTIONNSTART, 0, 0); + selectionEnd = ::SendMessage(_hScintilla, SCI_GETSELECTIONNEND, 0, 0); } - // For multiple selections or no selection, return empty selection info + // Calculate the selection length Sci_Position selectionLength = selectionEnd - selectionStart; - return SelectionInfo{ selectedText, selectionStart, selectionLength }; + return SelectionInfo{ selectionStart, selectionLength }; } void MultiReplace::captureLuaGlobals(lua_State* L) { @@ -4181,11 +4174,14 @@ SearchResult MultiReplace::performSearchForward(const std::string& findTextUtf8, SearchResult result; SelectionRange targetRange; + // If selectMatch is true, highlight the found text + // Check if IDC_SELECTION_RADIO is enabled and selectMatch is false if (!selectMatch && IsDlgButtonChecked(_hSelf, IDC_SELECTION_RADIO) == BST_CHECKED) { LRESULT selectionCount = ::SendMessage(_hScintilla, SCI_GETSELECTIONS, 0, 0); std::vector selections(selectionCount); + // Extract all Positions of detected Selections for (int i = 0; i < selectionCount; i++) { selections[i].start = ::SendMessage(_hScintilla, SCI_GETSELECTIONNSTART, i, 0); selections[i].end = ::SendMessage(_hScintilla, SCI_GETSELECTIONNEND, i, 0); @@ -7800,9 +7796,6 @@ void MultiReplace::onSelectionChanged() { } static bool wasTextSelected = false; // This stores the previous state - const std::vector selectionRadioDisabledButtons = { - IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_FIND_PREV_BUTTON, IDC_REPLACE_BUTTON - }; // Get the start and end of the selection Sci_Position start = ::SendMessage(MultiReplace::getScintillaHandle(), SCI_GETSELECTIONSTART, 0, 0); diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 1ec0f47..0976b45 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -107,7 +107,7 @@ struct SearchResult { }; struct SelectionInfo { - std::string text; + Sci_Position startPos; Sci_Position length; }; @@ -430,12 +430,12 @@ class MultiReplace : public StaticDialog // GUI control-related constants const std::vector selectionRadioDisabledButtons = { - IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON, IDC_REPLACE_BUTTON + IDC_FIND_BUTTON, IDC_FIND_NEXT_BUTTON , IDC_REPLACE_BUTTON }; const std::vector columnRadioDependentElements = { IDC_COLUMN_SORT_DESC_BUTTON, IDC_COLUMN_SORT_ASC_BUTTON, IDC_COLUMN_DROP_BUTTON, IDC_COLUMN_COPY_BUTTON, IDC_COLUMN_HIGHLIGHT_BUTTON }; - + // Window related settings RECT windowRect; // Structure to store window position and size int findCountColumnWidth = 0; // Width of the "Find Count" column @@ -601,7 +601,7 @@ class MultiReplace : public StaticDialog std::string wstringToString(const std::wstring& input) const; std::wstring utf8ToWString(const char* cstr) const; std::string utf8ToCodepage(const std::string& utf8Str, int codepage) const; - std::wstring trim(const std::wstring& str); + std::wstring trim(const std::wstring& str); //FileOperations std::wstring promptSaveListToCsv(); From fce32498156a7ce3a1ceb871c7205aa91d2f1cb4 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Tue, 8 Oct 2024 17:13:17 +0200 Subject: [PATCH 35/51] new Option for auto selecting list entry when match --- src/MultiReplacePanel.cpp | 14 +++++++++++--- src/MultiReplacePanel.h | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index f88cf93..79f877c 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -4451,6 +4451,10 @@ void MultiReplace::displayResultCentered(size_t posStart, size_t posEnd, bool is } void MultiReplace::selectListItem(size_t matchIndex) { + if (!highlightMatchEnabled) { + return; + } + HWND hListView = GetDlgItem(_hSelf, IDC_REPLACE_LIST); if (hListView && matchIndex != std::numeric_limits::max()) { // Deselect all items @@ -4459,8 +4463,8 @@ void MultiReplace::selectListItem(size_t matchIndex) { // Select the item at matchIndex ListView_SetItemState(hListView, static_cast(matchIndex), LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - // Ensure the item is visible - ListView_EnsureVisible(hListView, static_cast(matchIndex), FALSE); + // Ensure the item is visible, but only scroll if absolutely necessary + ListView_EnsureVisible(hListView, static_cast(matchIndex), TRUE); } } @@ -7237,6 +7241,7 @@ void MultiReplace::saveSettingsToIni(const std::wstring& iniFilePath) { outFile << wstringToString(L"UseVariables=" + std::to_wstring(useVariables) + L"\n"); outFile << wstringToString(L"ButtonsMode=" + std::to_wstring(ButtonsMode) + L"\n"); outFile << wstringToString(L"UseList=" + std::to_wstring(useList) + L"\n"); + outFile << wstringToString(L"HighlightMatch=" + std::to_wstring(highlightMatchEnabled ? 1 : 0) + L"\n"); // Convert and Store the scope options int selection = IsDlgButtonChecked(_hSelf, IDC_SELECTION_RADIO) == BST_CHECKED ? 1 : 0; @@ -7363,6 +7368,8 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { bool useList = readBoolFromIniFile(iniFilePath, L"Options", L"UseList", true); SendMessage(GetDlgItem(_hSelf, IDC_USE_LIST_CHECKBOX), BM_SETCHECK, useList ? BST_CHECKED : BST_UNCHECKED, 0); + highlightMatchEnabled = readBoolFromIniFile(iniFilePath, L"Options", L"HighlightMatch", true); + // Loading and setting the scope with enabled state check int selection = readIntFromIniFile(iniFilePath, L"Scope", L"Selection", 0); int columnMode = readIntFromIniFile(iniFilePath, L"Scope", L"ColumnMode", 0); @@ -7389,6 +7396,7 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { if (selection) { CheckRadioButton(_hSelf, IDC_ALL_TEXT_RADIO, IDC_COLUMN_MODE_RADIO, IDC_SELECTION_RADIO); + onSelectionChanged(); // check selection for IDC_SELECTION_RADIO } else if (columnMode) { CheckRadioButton(_hSelf, IDC_ALL_TEXT_RADIO, IDC_COLUMN_MODE_RADIO, IDC_COLUMN_MODE_RADIO); @@ -7396,7 +7404,7 @@ void MultiReplace::loadSettingsFromIni(const std::wstring& iniFilePath) { else { CheckRadioButton(_hSelf, IDC_ALL_TEXT_RADIO, IDC_COLUMN_MODE_RADIO, IDC_ALL_TEXT_RADIO); } - + setUIElementVisibility(); } diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index 0976b45..f57cb0d 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -427,6 +427,8 @@ class MultiReplace : public StaticDialog int checkMarkWidth_scaled = 0; int crossWidth_scaled = 0; int boxWidth_scaled = 0; + bool highlightMatchEnabled = false; // HighlightMatch + // GUI control-related constants const std::vector selectionRadioDisabledButtons = { From 87eb1f5981373f83b889bbbbdfd6574a4c474cef Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Wed, 9 Oct 2024 18:24:55 +0200 Subject: [PATCH 36/51] enhanced DPI Debug Information; DPI scaling Debug Window --- src/MultiReplacePanel.cpp | 176 +++++++++++++++++++++++--------------- src/MultiReplacePanel.h | 12 ++- 2 files changed, 120 insertions(+), 68 deletions(-) diff --git a/src/MultiReplacePanel.cpp b/src/MultiReplacePanel.cpp index 79f877c..7e32a10 100644 --- a/src/MultiReplacePanel.cpp +++ b/src/MultiReplacePanel.cpp @@ -3731,8 +3731,8 @@ int MultiReplace::ShowDebugWindow(const std::string& message) { } // Use the saved position and size if set, otherwise use default position and size - int width = debugWindowSizeSet ? debugWindowSize.cx : 400; // Set initial width - int height = debugWindowSizeSet ? debugWindowSize.cy : 500; // Set initial height + int width = debugWindowSizeSet ? debugWindowSize.cx : sx(260); // Set initial width + int height = debugWindowSizeSet ? debugWindowSize.cy : sy(400); // Set initial height int x = debugWindowPositionSet ? debugWindowPosition.x : (GetSystemMetrics(SM_CXSCREEN) - width) / 2; // Center horizontally int y = debugWindowPositionSet ? debugWindowPosition.y : (GetSystemMetrics(SM_CYSCREEN) - height) / 2; // Center vertically @@ -6417,71 +6417,6 @@ int MultiReplace::getFontHeight(HWND hwnd, HFONT hFont) { return fontHeight; // Return the font height } -void MultiReplace::showDPIAndFontInfo() -{ - RECT sizeWindowRect; - GetClientRect(_hSelf, &sizeWindowRect); // Get the dimensions of the client area of the window - - HDC hDC = GetDC(_hSelf); // Get the device context (DC) for the current window - if (hDC) - { - // Get the current font of the window - HFONT currentFont = (HFONT)SendMessage(_hSelf, WM_GETFONT, 0, 0); - HFONT hOldFont = (HFONT)SelectObject(hDC, currentFont); // Select the current font into the DC - - // Get the text metrics for the current font - TEXTMETRIC tmCurrent; - GetTextMetrics(hDC, &tmCurrent); // Retrieve text metrics for the selected font - - // Now get the standard font metrics (_hStandardFont) - SelectObject(hDC, _hStandardFont); // Select the standard font into the DC - TEXTMETRIC tmStandard; - GetTextMetrics(hDC, &tmStandard); // Retrieve text metrics for the standard font - - // Calculate the base units - SIZE size; - GetTextExtentPoint32W(hDC, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); - int baseUnitX = (size.cx / 26 + 1) / 2; - int baseUnitY = tmCurrent.tmHeight; - - // Calculate the window size in dialog units - int duWidth = MulDiv(sizeWindowRect.right, 4, baseUnitX); - int duHeight = MulDiv(sizeWindowRect.bottom, 8, baseUnitY); - - // Get DPI values from the DPIManager - int dpix = dpiMgr->getDPIX(); - int dpiy = dpiMgr->getDPIY(); - float customScaleFactor = dpiMgr->getCustomScaleFactor(); - - // Calculate scaled DPI values - int scaledDpiX = dpiMgr->scaleX(96); // example using 96 for standard DPI - int scaledDpiY = dpiMgr->scaleY(96); - - // Create a message box to display the window size, DPI, and font information - wchar_t sizeText[500]; - swprintf(sizeText, 500, L"Window Size: %ld x %ld DUs\n" - L"DPI X: %d\nDPI Y: %d\nCustom Scale Factor: %.2f\n" - L"Scaled DPI X: %d\nScaled DPI Y: %d\n\n" - L"Current Font: Height = %d, Ascent = %d, Descent = %d, Weight = %d\n" - L"Standard Font: Height = %d, Ascent = %d, Descent = %d, Weight = %d", - duWidth, duHeight, dpix, dpiy, customScaleFactor, scaledDpiX, scaledDpiY, - tmCurrent.tmHeight, tmCurrent.tmAscent, tmCurrent.tmDescent, tmCurrent.tmWeight, - tmStandard.tmHeight, tmStandard.tmAscent, tmStandard.tmDescent, tmStandard.tmWeight); - - MessageBox(nppData._nppHandle, sizeText, L"Window, DPI, and Font Info", MB_OK); - - // Cleanup - SelectObject(hDC, hOldFont); // Restore the previous font in the DC - ReleaseDC(_hSelf, hDC); // Release the device context - } - else - { - MessageBox(_hSelf, L"Failed to retrieve device context (HDC).", L"Error", MB_OK | MB_ICONERROR); - } -} - - - #pragma endregion @@ -7845,4 +7780,111 @@ void MultiReplace::onCaretPositionChanged() } +#pragma endregion + + +#pragma region Debug DPI Information + +BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + (void)hdcMonitor; // Mark hdcMonitor as unused + (void)lprcMonitor; // Mark lprcMonitor as unused + + MonitorEnumData* pData = reinterpret_cast(dwData); + + MONITORINFOEX monitorInfoEx; + monitorInfoEx.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(hMonitor, &monitorInfoEx); + + int screenWidth = monitorInfoEx.rcMonitor.right - monitorInfoEx.rcMonitor.left; + int screenHeight = monitorInfoEx.rcMonitor.bottom - monitorInfoEx.rcMonitor.top; + BOOL isPrimary = (monitorInfoEx.dwFlags & MONITORINFOF_PRIMARY) != 0; + + // Add monitor info to the buffer + pData->monitorInfo += L"Monitor " + std::to_wstring(pData->monitorCount + 1) + L": " + + (isPrimary ? L"Primary, " : L"Secondary, ") + + std::to_wstring(screenWidth) + L"x" + std::to_wstring(screenHeight) + L"\n"; + + // Increment the monitor counter + pData->monitorCount++; + + // Check if this is the primary monitor + if (isPrimary) { + pData->primaryMonitorIndex = pData->monitorCount; + } + + // Check if this is the current monitor where the window is + if (MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST) == hMonitor) { + pData->currentMonitor = pData->monitorCount; + } + + return TRUE; // Continue enumerating monitors +} + +void MultiReplace::showDPIAndFontInfo() +{ + RECT sizeWindowRect; + GetClientRect(_hSelf, &sizeWindowRect); // Get window dimensions + + HDC hDC = GetDC(_hSelf); // Get the device context (DC) for the current window + if (hDC) + { + // Get current font of the window + HFONT currentFont = (HFONT)SendMessage(_hSelf, WM_GETFONT, 0, 0); + SelectObject(hDC, currentFont); // Select current font into the DC + + TEXTMETRIC tmCurrent; + GetTextMetrics(hDC, &tmCurrent); // Retrieve text metrics for current font + + SelectObject(hDC, _hStandardFont); // Select standard font into the DC + TEXTMETRIC tmStandard; + GetTextMetrics(hDC, &tmStandard); // Retrieve text metrics for standard font + + SIZE size; + GetTextExtentPoint32W(hDC, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); + int baseUnitX = (size.cx / 26 + 1) / 2; + int baseUnitY = tmCurrent.tmHeight; + int duWidth = MulDiv(sizeWindowRect.right, 4, baseUnitX); + int duHeight = MulDiv(sizeWindowRect.bottom, 8, baseUnitY); + + // Get DPI values from the DPIManager + int dpix = dpiMgr->getDPIX(); + int dpiy = dpiMgr->getDPIY(); + float customScaleFactor = dpiMgr->getCustomScaleFactor(); + int scaledDpiX = dpiMgr->scaleX(96); + int scaledDpiY = dpiMgr->scaleY(96); + + wchar_t scaleBuffer[10]; + swprintf(scaleBuffer, 10, L"%.1f", customScaleFactor); + + MonitorEnumData monitorData = {}; + monitorData.monitorCount = 0; + monitorData.currentMonitor = 0; + monitorData.primaryMonitorIndex = 0; + + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, reinterpret_cast(&monitorData)); + + std::wstring message = + L"On Monitor " + std::to_wstring(monitorData.currentMonitor) + L"\n" + + + monitorData.monitorInfo + L"\n" + + L"Window Size DUs: " + std::to_wstring(duWidth) + L"x" + std::to_wstring(duHeight) + L"\n" + L"Scaled DPI: " + std::to_wstring(dpix) + L"x" + std::to_wstring(dpiy) + L" * " + scaleBuffer + L" = " + + std::to_wstring(scaledDpiX) + L"x" + std::to_wstring(scaledDpiY) + L"\n\n" + + L"Font Current: Height=" + std::to_wstring(tmCurrent.tmHeight) + L", Ascent=" + std::to_wstring(tmCurrent.tmAscent) + + L", Descent=" + std::to_wstring(tmCurrent.tmDescent) + L", Weight=" + std::to_wstring(tmCurrent.tmWeight) + L"\n" + L"Font Standard: Height=" + std::to_wstring(tmStandard.tmHeight) + L", Ascent=" + std::to_wstring(tmStandard.tmAscent) + + L", Descent=" + std::to_wstring(tmStandard.tmDescent) + L", Weight=" + std::to_wstring(tmStandard.tmWeight); + + MessageBox(_hSelf, message.c_str(), L"Window, Monitor, DPI, and Font Info", MB_ICONINFORMATION | MB_OK); + + ReleaseDC(_hSelf, hDC); + } + else + { + MessageBox(_hSelf, L"Failed to retrieve device context (HDC).", L"Error", MB_OK); + } +} + #pragma endregion \ No newline at end of file diff --git a/src/MultiReplacePanel.h b/src/MultiReplacePanel.h index f57cb0d..840e300 100644 --- a/src/MultiReplacePanel.h +++ b/src/MultiReplacePanel.h @@ -178,6 +178,13 @@ struct MenuState { bool allDisabled = false;; }; +struct MonitorEnumData { + std::wstring monitorInfo; + int monitorCount; + int currentMonitor; + int primaryMonitorIndex; +}; + enum class ItemAction { Search, Edit, @@ -595,7 +602,6 @@ class MultiReplace : public StaticDialog bool normalizeAndValidateNumber(std::string& str); std::vector createFilterString(const std::vector>& filters); int getCharacterWidth(int elementID, const wchar_t* character); - void showDPIAndFontInfo(); int getFontHeight(HWND hwnd, HFONT hFont); //StringHandling @@ -645,6 +651,10 @@ class MultiReplace : public StaticDialog LPCWSTR getLangStrLPCWSTR(const std::wstring& id); LPWSTR getLangStrLPWSTR(const std::wstring& id); BYTE readByteFromIniFile(const std::wstring& iniFilePath, const std::wstring& section, const std::wstring& key, BYTE defaultValue); + + // Debug DPI Information + void showDPIAndFontInfo(); + }; extern std::unordered_map languageMap; From 8dcaf29000b20562f1c02a63a7c81e6870f30f67 Mon Sep 17 00:00:00 2001 From: Thomas Knoefel Date: Thu, 10 Oct 2024 18:22:23 +0200 Subject: [PATCH 37/51] update of help html;; update of DPIManager --- help_use_variables_dark.html | 96 +++++++++++++++++++++++++++-------- help_use_variables_light.html | 95 ++++++++++++++++++++++++++-------- src/DPIManager.cpp | 24 ++++++--- src/MultiReplacePanel.cpp | 44 +++------------- 4 files changed, 174 insertions(+), 85 deletions(-) diff --git a/help_use_variables_dark.html b/help_use_variables_dark.html index 0ec5749..9b95527 100644 --- a/help_use_variables_dark.html +++ b/help_use_variables_dark.html @@ -1,7 +1,7 @@ -MultiRpelace Use Variables Option +MultiReplace Use Variables Option @@ -289,18 +336,22 @@
-
- -