From f43319173cc6e40a2208da5728ca20181f2611e0 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Fri, 24 Apr 2026 12:03:04 +0200 Subject: [PATCH 1/2] add: PasteBoard: opt-out 'Don't ask again' on closing modified untitled documents --- .github/copilot-instructions.md | 8 +- Build/Notepad3.ini | 1 + CLAUDE.md | 4 +- language/np3_de_de/strings_de_de.rc | 2 +- language/np3_en_gb/strings_en_gb.rc | 2 +- language/np3_en_us/strings_en_us.rc | 2 +- readme/config/Configuration.md | 6 ++ src/Config/Config.cpp | 12 ++- src/Dialogs.c | 121 ++++++++++++++++++++-------- src/Edit.c | 42 +++++----- src/MuiLanguage.c | 6 +- src/Notepad3.c | 65 ++++++++++----- src/Notepad3Util.c | 2 +- src/TypeDefs.h | 31 +++++++ 14 files changed, 219 insertions(+), 85 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index fdde3ac31..9428a63fa 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -46,6 +46,8 @@ Each language is one `styleLexXXX.c` defining an `EDITLEXER` struct (see existin 27+ locales under `np3_LANG_COUNTRY\`. Language packs build as separate DLLs. +**Invariant:** every `IDS_MUI_*` defined in `common_res.h` must exist in **all** `strings_*.rc` files. A missing entry breaks the corresponding language DLL build. For bulk insertions across locales, use a `.venv/Scripts/python.exe` script — `sed`/`perl` `\n` escaping is unreliable in Cygwin. + ### Adding a string resource 1. `#define IDS_MUI_XXX ` in `language\common_res.h` (13xxx errors/warnings, 14xxx info/prompts). @@ -128,6 +130,10 @@ Vendored 5.5.8 / 5.4.6 with NP3 patches under `np3_patches\`. Versions in `scint Use existing structs (`Globals`, `Settings`, `Settings2`, `Flags`, `Paths`). Don't add new globals. +### Dialog return values (`InfoBoxLng`) + +`InfoBoxLng()` returns `MAKELONG(button, mode)` — never compare/switch on the raw return when the suppression key is non-NULL: a saved-answer replay sets HIWORD too, so `result == IDNO` is false. Use `IsYesOkay()` / `IsRetryContinue()` / `IsNoCancelClose()` (in `src\Dialogs.h`), or extract via `INFOBOX_ANSW(r)` (LOWORD) and `INFOBOX_MODE(r)` (HIWORD). Switching on the raw value is a latent bug if a suppression key is ever added. + ### Portable INI design - INI beside the exe. No registry. Runs on defaults if none exists (user creates via "Save Settings Now"). @@ -138,7 +144,7 @@ Use existing structs (`Globals`, `Settings`, `Settings2`, `Flags`, `Paths`). Don - **`bIniFileFromScratch`** is set when INI is 0 bytes, cleared after `SaveAllSettings()`. While set, `MuiLanguage.c` suppresses writing `PreferredLanguageLocaleName`. - **Empty lexer sections are pruned** in `Style_ToIniSection()` via `IniSectionGetKeyCount()` — only sections with non-default styles persist. - MiniPath uses the same pattern (`minipath\src\Config.cpp`). -- **New `Settings2` (or other INI) params must be documented as commented entries in `Build\Notepad3.ini`.** +- **New `Settings2` (or other INI) params must be documented in BOTH places**: a commented entry under `[Settings2]` in `Build\Notepad3.ini` AND a `#### \`Name=default\`` heading + prose paragraph in the matching topical section of `readme\config\Configuration.md` (the user-facing reference). Updating only the INI is incomplete. ### Save/Load macros (`src\Config\Config.cpp`) diff --git a/Build/Notepad3.ini b/Build/Notepad3.ini index 1e64febc7..e80c4d5de 100644 --- a/Build/Notepad3.ini +++ b/Build/Notepad3.ini @@ -16,6 +16,7 @@ SettingsVersion=5 ;DefaultDirectory= ;DefaultExtension=txt ;DenyVirtualSpaceAccess=0 +;DiscardOnClosingUntitledPasteBoard=0 ;(0/1) {when set, the close-modified prompt for an Untitled (no file path) document in PasteBoard / clipboard-monitoring mode (/B) offers a "Don't show this dialog again" opt-out checkbox; after opting out, the chosen action repeats silently on subsequent closes; has no effect when PasteBoard mode is not active} ;filebrowser.exe=minipath.exe ;FileCheckInterval=2000 ;(min: 200[msec] - if equal or less, notify immediately) ;FileWatchingMethod=0 ;(0=both[default], 1=poll-only, 2=push-only) diff --git a/CLAUDE.md b/CLAUDE.md index 7ce297b0b..036c2565c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,6 +79,7 @@ External tool, **not built from source**. Pre-built exes under `grepWin\portable ## Localization - 26 locales under `language\np3_LANG_COUNTRY\`. Language DLLs are separate projects in the solution. +- **Invariant: every `IDS_MUI_*` defined in `common_res.h` must exist in all 26 `strings_*.rc` files.** A missing entry breaks the corresponding language DLL build. For bulk insertions across locales, use a `.venv/Scripts/python.exe` script — `sed`/`perl` `\n` escaping is unreliable in Cygwin. - **`.rc` files are UTF-8 WITHOUT BOM, CRLF line endings.** Never write with BOM. Use `Build\rc_to_utf8.cmd` to strip accidental BOMs. In PowerShell use `[System.Text.UTF8Encoding]::new($false)`; in Python write `\r\n` explicitly. - **`Build\Notepad3.ini`, `Build\minipath.ini` are UTF-8 WITH BOM** (`EF BB BF`). Preserve it. @@ -99,7 +100,7 @@ External tool, **not built from source**. Pre-built exes under `grepWin\portable - **`bIniFileFromScratch`** is set when INI is 0 bytes, cleared after `SaveAllSettings()`. While set, `MuiLanguage.c` suppresses writing `PreferredLanguageLocaleName` to keep fresh INIs clean. - **Empty lexer sections are pruned** in `Style_ToIniSection()` via `IniSectionGetKeyCount()` after `Style_CanonicalSectionToIniCache()` establishes order — only sections with non-default styles persist. - MiniPath follows the same pattern (`minipath\src\Config.cpp`). -- **New `Settings2` (or other INI) params must be documented as commented entries in `Build\Notepad3.ini`.** +- **New `Settings2` (or other INI) params must be documented in BOTH places**: a commented entry under `[Settings2]` in `Build\Notepad3.ini` AND a `#### \`Name=default\`` heading + prose paragraph in the matching topical section of `readme\config\Configuration.md` (the user-facing reference). Updating only the INI is incomplete. ### Save/Load macros (`src\Config\Config.cpp`) @@ -167,6 +168,7 @@ Supported: Win32 (x86), x64, x64_AVX2, ARM64. **ARM 32-bit is not supported** - **Scintilla**: always use `SciCall.h` wrappers. Add missing wrappers there. Naming: `DeclareSciCall{V|R}{0|01|1|2}` — V=void, R=return; 0/1/2 = param count; `01` = lParam-only. The `msg` arg is the suffix after `SCI_`. - **Global state**: use existing structs (`Globals`, `Settings`, `Settings2`, `Flags`, `Paths`); don't add new globals. - **Undo/redo**: use `_BEGIN_UNDO_ACTION_` / `_END_UNDO_ACTION_` macros (`Notepad3.h`) for grouping; they also throttle notifications during bulk edits. +- **`InfoBoxLng()` returns `MAKELONG(button, mode)`** — never compare/switch on the raw return when the suppression key is non-NULL: a saved-answer replay sets HIWORD too, so `result == IDNO` is false. Use `IsYesOkay()` / `IsRetryContinue()` / `IsNoCancelClose()` (in `src/Dialogs.h`), or extract via `INFOBOX_ANSW(r)` (LOWORD) and `INFOBOX_MODE(r)` (HIWORD). Switching on the raw value is a latent bug if a suppression key is ever added. ### WriteAccessBuf — dangling pointer anti-pattern diff --git a/language/np3_de_de/strings_de_de.rc b/language/np3_de_de/strings_de_de.rc index 0ab3f033f..eb91be3ab 100644 --- a/language/np3_de_de/strings_de_de.rc +++ b/language/np3_de_de/strings_de_de.rc @@ -100,7 +100,7 @@ END STRINGTABLE BEGIN IDS_MUI_APPTITLE_ELEVATED "%s : Erhöhte Rechte" - IDS_MUI_APPTITLE_PASTEBOARD "%s : Zwischenablage" + IDS_MUI_APPTITLE_PASTEBOARD "%s : PASTEBOARD" IDS_MUI_UNTITLED "Kein Titel" IDS_MUI_TITLEEXCERPT """%s""" IDS_MUI_READONLY "(Nur Lesen)" diff --git a/language/np3_en_gb/strings_en_gb.rc b/language/np3_en_gb/strings_en_gb.rc index 8115e160c..56b9c676d 100644 --- a/language/np3_en_gb/strings_en_gb.rc +++ b/language/np3_en_gb/strings_en_gb.rc @@ -100,7 +100,7 @@ END STRINGTABLE BEGIN IDS_MUI_APPTITLE_ELEVATED "%s : Elevated" - IDS_MUI_APPTITLE_PASTEBOARD "%s : Clipboard" + IDS_MUI_APPTITLE_PASTEBOARD "%s : PASTEBOARD" IDS_MUI_UNTITLED "Untitled" IDS_MUI_TITLEEXCERPT """%s""" IDS_MUI_READONLY "(Read Only)" diff --git a/language/np3_en_us/strings_en_us.rc b/language/np3_en_us/strings_en_us.rc index 97db81b25..e65bbc67a 100644 --- a/language/np3_en_us/strings_en_us.rc +++ b/language/np3_en_us/strings_en_us.rc @@ -100,7 +100,7 @@ END STRINGTABLE BEGIN IDS_MUI_APPTITLE_ELEVATED "%s : Elevated" - IDS_MUI_APPTITLE_PASTEBOARD "%s : Clipboard" + IDS_MUI_APPTITLE_PASTEBOARD "%s : PASTEBOARD" IDS_MUI_UNTITLED "Untitled" IDS_MUI_TITLEEXCERPT """%s""" IDS_MUI_READONLY "(Read Only)" diff --git a/readme/config/Configuration.md b/readme/config/Configuration.md index d42689039..7a2e2aaa4 100644 --- a/readme/config/Configuration.md +++ b/readme/config/Configuration.md @@ -276,6 +276,12 @@ Managed by Notepad3 (Menu → Settings → Window → Reuse Window, **Ctrl+Shift Whether to prompt for save when closing an untitled document that contains only whitespace. +#### `DiscardOnClosingUntitledPasteBoard=0` + +Set to `1` to enable an opt-out variant of the close-modified prompt for **Untitled** documents (documents with no associated file path) **while clipboard-monitoring (PasteBoard) mode is active** — i.e. when Notepad3 was launched with `/B` or PasteBoard mode was toggled on via `Edit → Toggle Clipboard Monitoring`. With the default (`0`), or whenever PasteBoard mode is inactive, the standard Save / Discard / Cancel prompt is shown without a "Don't show this dialog again" checkbox. + +When the gate matches (flag set + Untitled + PasteBoard active), closing a modified untitled document shows the same Save / Discard / Cancel prompt with a **"Don't show this dialog again"** checkbox and Discard pre-selected as the default button. If the box is checked alongside a Save or Discard answer, that choice is persisted under `[Suppressed Messages] MsgDiscardUntitled` in the INI and replays silently on subsequent close-untitled events — useful when Notepad3 is regularly used as a clipboard scratchpad. Cancel is never persisted. To re-enable the prompt, delete the `MsgDiscardUntitled` line from the `[Suppressed Messages]` section, or simply set `DiscardOnClosingUntitledPasteBoard=0` (the loader auto-clears the suppression entry on next start). Has no effect on documents that have a file path or when PasteBoard mode is not active. + #### `SciFontQuality=3` Scintilla font rendering quality: diff --git a/src/Config/Config.cpp b/src/Config/Config.cpp index 9c5e23472..b4753bda6 100644 --- a/src/Config/Config.cpp +++ b/src/Config/Config.cpp @@ -1376,6 +1376,16 @@ void LoadSettings() Settings2.SubWrappedLineSelectOnMarginClick = IniSectionGetBool(IniSecSettings2, L"SubWrappedLineSelectOnMarginClick", false); + Settings2.DiscardOnClosingUntitledPasteBoard = IniSectionGetBool(IniSecSettings2, L"DiscardOnClosingUntitledPasteBoard", false); + if (!Settings2.DiscardOnClosingUntitledPasteBoard) { + // Drop any stale opt-out so re-enabling the feature later starts with a fresh prompt. + // Use the cache-aware IniSectionDelete (not IniFileDelete) — LoadSettings runs inside + // an OpenSettingsFile/CloseSettingsFile bracket that flushes the cache back at the end; + // a direct file write here would be overwritten. Mark dirty so the flush happens. + IniSectionDelete(Constants.SectionSuppressedMessages, Constants.SuppressKey.MsgDiscardUntitled, false); + bDirtyFlag = true; + } + int const iAnsiCPBonusSet = clampi(IniSectionGetInt(IniSecSettings2, L"LocaleAnsiCodePageAnalysisBonus", 33), 0, 100); Settings2.LocaleAnsiCodePageAnalysisBonus = (float)iAnsiCPBonusSet / 100.0f; @@ -2533,7 +2543,7 @@ void CmdSaveSettingsNow() } } if (Globals.bCanSaveIniFile && SaveAllSettings(true)) { - InfoBoxLng(MB_ICONINFORMATION, L"MsgSaveSettingsInfo", IDS_MUI_SAVEDSETTINGS); + InfoBoxLng(MB_ICONINFORMATION, Constants.SuppressKey.MsgSaveSettingsInfo, IDS_MUI_SAVEDSETTINGS); if ((dwFileAttributes != 0) && (dwFileAttributes != INVALID_FILE_ATTRIBUTES)) { Path_SetFileAttributes(Paths.IniFile, dwFileAttributes); // reset } diff --git a/src/Dialogs.c b/src/Dialogs.c index 79a3a6d65..3e17211ed 100644 --- a/src/Dialogs.c +++ b/src/Dialogs.c @@ -17,13 +17,10 @@ #include #include -#include -#include #include #include #include -#include -#include +#include #include @@ -34,7 +31,6 @@ #include "VersionEx.h" #include "PathLib.h" -#include "Edit.h" #include "Dlapi.h" #include "Encoding.h" #include "Styles.h" @@ -44,9 +40,6 @@ #include "DarkMode/DarkMode.h" #include "tinyexprcpp/tinyexpr_cif.h" #include "Resample.h" -#include "PathLib.h" - -#include "SciCall.h" #include "Dialogs.h" @@ -95,6 +88,44 @@ typedef struct _infbox { bool bDisableCheckBox; } INFOBOXLNG, *LPINFOBOXLNG; +// Map (MB_TYPEMASK | MB_DEFMASK) → control ID of the requested default button per InfoBox dialog template (IDD_MUI_INFOBOX..INFOBOX7). Returns 0 for unmapped styles (e.g. MB_FILECHANGEDNOTIFY) so the caller can fall back to the template's resource default. +static int _InfoBoxLng_GetDefaultBtnId(UINT uType) +{ + UINT const btnType = uType & MB_TYPEMASK; + UINT const defBtn = uType & MB_DEFMASK; + + switch (btnType) { + case MB_OK: + return IDOK; + case MB_YESNO: + return (defBtn == MB_DEFBUTTON2) ? IDNO : IDYES; + case MB_OKCANCEL: + return (defBtn == MB_DEFBUTTON2) ? IDCANCEL : IDOK; + case MB_YESNOCANCEL: + switch (defBtn) { + case MB_DEFBUTTON2: return IDNO; + case MB_DEFBUTTON3: return IDCANCEL; + default: return IDOK; // template uses IDOK for "Yes" + } + case MB_RETRYCANCEL: + return (defBtn == MB_DEFBUTTON2) ? IDCANCEL : IDRETRY; + case MB_ABORTRETRYIGNORE: + switch (defBtn) { + case MB_DEFBUTTON2: return IDRETRY; + case MB_DEFBUTTON3: return IDIGNORE; + default: return IDABORT; + } + case MB_CANCELTRYCONTINUE: + switch (defBtn) { + case MB_DEFBUTTON2: return IDRETRY; // template uses IDRETRY for "Try Again" + case MB_DEFBUTTON3: return IDCONTINUE; + default: return IDCANCEL; + } + default: + return 0; + } +} + static INT_PTR CALLBACK _InfoBoxLngDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) { static HBITMAP hIconBmp = NULL; @@ -239,6 +270,19 @@ static INT_PTR CALLBACK _InfoBoxLngDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, CenterDlgInParent(hwnd, true); AttentionBeep(lpMsgBox->uType); + + // Custom dialog templates don't honor MB_DEFBUTTON* automatically: DM_SETDEFID relocates the BS_DEFPUSHBUTTON border + Enter target + // SetFocus moves keyboard focus. Helper maps (style, defbutton) → control ID + // returns 0 for unknown styles so we fall through to the template's resource default. + int const defFocusId = _InfoBoxLng_GetDefaultBtnId(lpMsgBox->uType); + if (defFocusId) { + HWND const hDefBtn = GetDlgItem(hwnd, defFocusId); + if (hDefBtn) { + SendMessage(hwnd, DM_SETDEFID, (WPARAM)defFocusId, 0); + SetFocus(hDefBtn); + return FALSE; + } + } } return TRUE; @@ -302,17 +346,19 @@ CASE_WM_CTLCOLOR_SET: case WM_COMMAND: { LPINFOBOXLNG const lpMsgBox = (LPINFOBOXLNG)GetWindowLongPtr(hwnd, DWLP_USER); switch (LOWORD(wParam)) { - case IDOK: + // Persistable button set (WRITE side) — must agree with InfoBoxLng() READ switch and IDC_INFOBOXCHECK ENABLE list + // divergence yields "saved but never replayed" answers. case IDYES: + case IDNO: + case IDOK: case IDRETRY: - case IDIGNORE: case IDTRYAGAIN: + case IDIGNORE: case IDCONTINUE: if (IsButtonChecked(hwnd, IDC_INFOBOXCHECK) && StrIsNotEmpty(lpMsgBox->lpstrSetting) && Globals.bCanSaveIniFile) { IniFileSetLong(Paths.IniFile, Constants.SectionSuppressedMessages, lpMsgBox->lpstrSetting, LOWORD(wParam)); } - //[FallThrough] - case IDNO: + //[FallThrough] - buttons to be disabled case IDABORT: case IDCLOSE: case IDCANCEL: @@ -320,14 +366,14 @@ CASE_WM_CTLCOLOR_SET: break; case IDC_INFOBOXCHECK: { + // Persistable button set (ENABLE side) — disable only non-persistable controls + // so the user picks an answer the WRITE/READ sides round-trip. + // No WM_NEXTDLGCTL: moving focus onto a pushbutton would override DM_SETDEFID's + // visual default border. Leaving focus on the checkbox preserves it. bool const isChecked = IsButtonChecked(hwnd, IDC_INFOBOXCHECK); - DialogEnableControl(hwnd, IDNO, !isChecked); DialogEnableControl(hwnd, IDABORT, !isChecked); - DialogEnableControl(hwnd, IDIGNORE, !isChecked); - DialogEnableControl(hwnd, IDCONTINUE, !isChecked); DialogEnableControl(hwnd, IDCLOSE, !isChecked); DialogEnableControl(hwnd, IDCANCEL, !isChecked); - SendMessage(hwnd, WM_NEXTDLGCTL, 0, FALSE); } break; @@ -355,9 +401,15 @@ LONG InfoBoxLng(UINT uType, LPCWSTR lpstrSetting, UINT uidMsg, ...) uType |= MB_RTLREADING; } + // Persistable button set (READ side) — must include every value the WM_COMMAND WRITE list can store + // saved values not listed here fall into `default:` and are silently deleted on next call. switch (iMode) { case IDOK: case IDYES: + case IDNO: + case IDRETRY: + case IDTRYAGAIN: + case IDIGNORE: case IDCONTINUE: return MAKELONG(iMode, iMode); @@ -436,7 +488,8 @@ LONG InfoBoxLng(UINT uType, LPCWSTR lpstrSetting, UINT uidMsg, ...) msgBox.lpstrSetting = (LPWSTR)lpstrSetting; msgBox.bDisableCheckBox = (!Globals.bCanSaveIniFile || StrIsEmpty(lpstrSetting) || (iMode < 0)) ? true : false; - int idDlg; + int idDlg = IDD_MUI_INFOBOX; + switch (uType & MB_TYPEMASK) { case MB_OK: // one push button : OK. This is the default. @@ -1208,7 +1261,7 @@ CASE_WM_CTLCOLOR_SET: WCHAR wchBuf2[128] = { L'\0' }; WCHAR wchVerInfo[2048] = { L'\0' }; - int ResX, ResY; + int ResX = 0, ResY = 0; GetCurrentMonitorResolution(Globals.hwndMain, &ResX, &ResY); // -------------------------------------------------------------------- @@ -1601,11 +1654,10 @@ static INT_PTR CALLBACK OpenWithDlgProc(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM case WM_SIZE: { - int dx, dy; + int dx = 0, dy = 0; ResizeDlg_Size(hwnd,lParam,&dx,&dy); - HDWP hdwp; - hdwp = BeginDeferWindowPos(5); + HDWP hdwp = BeginDeferWindowPos(5); hdwp = DeferCtlPos(hdwp,hwnd,IDOK,dx,dy,SWP_NOSIZE); hdwp = DeferCtlPos(hdwp,hwnd,IDCANCEL,dx,dy,SWP_NOSIZE); hdwp = DeferCtlPos(hdwp,hwnd,IDC_OPENWITHDIR,dx,dy,SWP_NOMOVE); @@ -1861,11 +1913,10 @@ static INT_PTR CALLBACK FavoritesDlgProc(HWND hwnd,UINT umsg,WPARAM wParam,LPARA case WM_SIZE: { - int dx, dy; + int dx = 0, dy = 0; ResizeDlg_Size(hwnd,lParam,&dx,&dy); - HDWP hdwp; - hdwp = BeginDeferWindowPos(5); + HDWP hdwp = BeginDeferWindowPos(5); hdwp = DeferCtlPos(hdwp,hwnd,IDOK,dx,dy,SWP_NOSIZE); hdwp = DeferCtlPos(hdwp,hwnd,IDCANCEL,dx,dy,SWP_NOSIZE); hdwp = DeferCtlPos(hdwp,hwnd,IDC_FAVORITESDIR,dx,dy,SWP_NOMOVE); @@ -2074,7 +2125,7 @@ static INT_PTR CALLBACK AddToFavDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, LPA case WM_SIZE: { - int dx; + int dx = 0; ResizeDlg_Size(hwnd, lParam, &dx, NULL); HDWP hdwp = BeginDeferWindowPos(4); hdwp = DeferCtlPos(hdwp, hwnd, IDOK, dx, 0, SWP_NOSIZE); @@ -2358,7 +2409,7 @@ static INT_PTR CALLBACK FileMRUDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, LPAR return FALSE; case WM_SIZE: { - int dx, dy; + int dx = 0, dy = 0; ResizeDlg_Size(hwnd, lParam, &dx, &dy); HDWP hdwp = BeginDeferWindowPos(8); hdwp = DeferCtlPos(hdwp, hwnd, IDOK, dx, dy, SWP_NOSIZE); @@ -2922,7 +2973,7 @@ CASE_WM_CTLCOLOR_SET: switch (LOWORD(wParam)) { case IDOK: { - BOOL fTranslated; + BOOL fTranslated = FALSE; UINT const iNewNumber = GetDlgItemInt(hwnd, IDC_COLUMNWRAP, &fTranslated, FALSE); if (fTranslated) { UINT* piNumber = (UINT*)GetWindowLongPtr(hwnd, DWLP_USER); @@ -3144,7 +3195,7 @@ static INT_PTR CALLBACK LongLineSettingsDlgProc(HWND hwnd, UINT umsg, WPARAM wPa SetDlgItemText(hwnd, IDC_MULTIEDGELINE, pszColumnList); SendDlgItemMessage(hwnd, IDC_MULTIEDGELINE, EM_LIMITTEXT, MIDSZ_BUFFER, 0); - BOOL fTranslated; + BOOL fTranslated = FALSE; /*UINT const iCol = */ GetDlgItemInt(hwnd, IDC_MULTIEDGELINE, &fTranslated, FALSE); if (fTranslated) { switch (Settings.LongLineMode) { @@ -3206,7 +3257,7 @@ CASE_WM_CTLCOLOR_SET: switch (LOWORD(wParam)) { case IDC_MULTIEDGELINE: { - BOOL fTranslated; + BOOL fTranslated = FALSE; /*UINT const iCol = */ GetDlgItemInt(hwnd, IDC_MULTIEDGELINE, &fTranslated, FALSE); if (fTranslated) { DialogEnableControl(hwnd, IDC_SHOWEDGELINE, true); @@ -3364,7 +3415,7 @@ CASE_WM_CTLCOLOR_SET: switch(LOWORD(wParam)) { case IDOK: { - BOOL fTranslated1, fTranslated2; + BOOL fTranslated1 = FALSE, fTranslated2 = FALSE; int const _iNewTabWidth = GetDlgItemInt(hwnd, IDC_TAB_WIDTH, &fTranslated1, FALSE); int const _iNewIndentWidth = GetDlgItemInt(hwnd, IDC_INDENT_DEPTH, &fTranslated2, FALSE); @@ -3734,7 +3785,7 @@ static INT_PTR CALLBACK SelectEncodingDlgProc(HWND hwnd,UINT umsg,WPARAM wParam, case WM_SIZE: { - int dx, dy; + int dx = 0, dy = 0; ResizeDlg_Size(hwnd,lParam,&dx,&dy); HDWP hdwp = BeginDeferWindowPos(3); @@ -5263,7 +5314,7 @@ void DialogAdminExe(HWND hwnd, bool bExecInstaller) sei.nShow = SW_SHOWNORMAL; if (bExecInstaller) { ShellExecuteExW(&sei); - if (IsYesOkay(InfoBoxLng(MB_OKCANCEL, L"NoAdminTool", IDS_MUI_ERR_ADMINEXE))) { + if (IsYesOkay(InfoBoxLng(MB_OKCANCEL, Constants.SuppressKey.NoAdminTool, IDS_MUI_ERR_ADMINEXE))) { sei.lpFile = VERSION_UPDATE_CHECK; ShellExecuteExW(&sei); } @@ -6214,7 +6265,7 @@ int Toolbar_SetButtons(HANDLE hwnd, int cmdBase, LPCWSTR lpszButtons, LPCTBBUTTO } p = tchButtons; while (*p) { - int iCmd; + int iCmd = 0; //if (swscanf_s(p, L"%i", &iCmd) == 1) { if (StrToIntEx(p, STIF_DEFAULT, &iCmd)) { iCmd = (iCmd == 0) ? 0 : iCmd + cmdBase - 1; @@ -6259,7 +6310,7 @@ static inline bool IsChineseTraditionalSubLang(LANGID subLang) bool GetLocaleDefaultUIFont(LANGID lang, LPWSTR lpFaceName, WORD* wSize) { - LPCWSTR font; + LPCWSTR font = lpFaceName; LANGID const subLang = SUBLANGID(lang); switch (PRIMARYLANGID(lang)) { default: @@ -6360,7 +6411,7 @@ static inline BYTE* DialogTemplate_GetFontSizeField(const DLGTEMPLATE* pTemplate { bool bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - WORD* pw; + WORD* pw = NULL; if (bDialogEx) { pw = (WORD*)((DLGTEMPLATEEX*)pTemplate + 1); diff --git a/src/Edit.c b/src/Edit.c index ec2b0c45a..f37a359dc 100644 --- a/src/Edit.c +++ b/src/Edit.c @@ -572,7 +572,7 @@ bool EditSetNewEncoding(HWND hwnd, cpi_enc_t iNewEncoding, bool bSupressWarning) if (Sci_IsDocEmpty()) { bool const doNewEncoding = (Sci_HaveUndoRedoHistory() && !bSupressWarning) ? - IsYesOkay(InfoBoxLng(MB_YESNO, L"MsgConv2", IDS_MUI_ASK_ENCODING2)) : true; + IsYesOkay(InfoBoxLng(MB_YESNO, Constants.SuppressKey.MsgConv2, IDS_MUI_ASK_ENCODING2)) : true; if (doNewEncoding) { return EditConvertText(hwnd, iCurrentEncoding, iNewEncoding); @@ -585,7 +585,7 @@ bool EditSetNewEncoding(HWND hwnd, cpi_enc_t iNewEncoding, bool bSupressWarning) bSupressWarning = bIsCurANSI && bIsTargetUTF; } - bool const doNewEncoding = (!bSupressWarning) ? IsYesOkay(InfoBoxLng(MB_YESNO, L"MsgConv1", IDS_MUI_ASK_ENCODING)) : true; + bool const doNewEncoding = (!bSupressWarning) ? IsYesOkay(InfoBoxLng(MB_YESNO, Constants.SuppressKey.MsgConv1, IDS_MUI_ASK_ENCODING)) : true; if (doNewEncoding) { return EditConvertText(hwnd, iCurrentEncoding, iNewEncoding); } @@ -1301,7 +1301,7 @@ bool EditLoadFile( WCHAR sizeWarnStr[64] = { L'\0' }; StrFormatByteSizeEx(fileSizeWarning, SFBS_FLAGS_ROUND_TO_NEAREST_DISPLAYED_DIGIT, sizeWarnStr, COUNTOF(sizeWarnStr)); Flags.bHugeFileLoadState = true; - if (!IsYesOkay(InfoBoxLng(MB_YESNO, L"MsgFileSizeWarning", IDS_MUI_WARN_LOAD_BIG_FILE, sizeStr, sizeWarnStr))) { + if (!IsYesOkay(InfoBoxLng(MB_YESNO, Constants.SuppressKey.MsgFileSizeWarning, IDS_MUI_WARN_LOAD_BIG_FILE, sizeStr, sizeWarnStr))) { CloseHandle(hFile); Encoding_Forced(CPI_NONE); goto observe; @@ -1319,7 +1319,7 @@ bool EditLoadFile( // check for unknown file/extension status->bUnknownExt = false; if (!Style_HasLexerForExt(hfile_pth)) { - if (!IsYesOkay(InfoBoxLng(MB_YESNO, L"MsgFileUnknownExt", IDS_MUI_WARN_UNKNOWN_EXT, Path_FindFileName(hfile_pth)))) { + if (!IsYesOkay(InfoBoxLng(MB_YESNO, Constants.SuppressKey.MsgFileUnknownExt, IDS_MUI_WARN_UNKNOWN_EXT, Path_FindFileName(hfile_pth)))) { CloseHandle(hFile); Encoding_Forced(CPI_NONE); status->bUnknownExt = true; @@ -1346,7 +1346,7 @@ bool EditLoadFile( bReadSuccess = ((readFlag & DECRYPT_FATAL_ERROR) || (readFlag & DECRYPT_FREAD_FAILED)) ? false : true; if ((readFlag & DECRYPT_CANCELED_NO_PASS) || (readFlag & DECRYPT_WRONG_PASS)) { - bReadSuccess = IsYesOkay(InfoBoxLng(MB_OKCANCEL, L"MsgNoOrWrongPassphrase", IDS_MUI_NOPASS)); + bReadSuccess = IsYesOkay(InfoBoxLng(MB_OKCANCEL, Constants.SuppressKey.MsgNoOrWrongPassphrase, IDS_MUI_NOPASS)); if (!bReadSuccess) { Encoding_Forced(CPI_NONE); FreeMem(lpData); @@ -1414,7 +1414,7 @@ bool EditLoadFile( } status->iEOLMode = Settings.DefaultEOLMode; FreeMem(lpData); - InfoBoxLng(MB_ICONWARNING, L"MsgUTF32Unsupported", IDS_MUI_ERR_ENCODINGNA); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgUTF32Unsupported, IDS_MUI_ERR_ENCODINGNA); goto observe; } @@ -1798,7 +1798,7 @@ bool EditSaveFile( FreeMem(lpDataWide); - if (!bCancelDataLoss || IsYesOkay(InfoBoxLng(MB_OKCANCEL, L"MsgConv3", IDS_MUI_ERR_UNICODE2))) { + if (!bCancelDataLoss || IsYesOkay(InfoBoxLng(MB_OKCANCEL, Constants.SuppressKey.MsgConv3, IDS_MUI_ERR_UNICODE2))) { SetEndOfFile(hFile); if (cbDataConverted != 0) { bWriteSuccess = EncryptAndWriteFile(hwnd, hFile, (BYTE *)lpData, cbDataConverted, &bytesWritten); @@ -7548,7 +7548,7 @@ bool EditFindNext(HWND hwnd, const LPEDITFINDREPLACE lpefr, bool bExtendSelectio DocPos iPos = _FindInTarget(wchFind, sFlags, &start, &end, true, FRMOD_NORM); if ((iPos < NOT_FOUND) && bIsRegExpr) { - InfoBoxLng(MB_ICONWARNING, L"MsgInvalidRegex", IDS_MUI_REGEX_INVALID); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgInvalidRegex, IDS_MUI_REGEX_INVALID); bSuppressNotFound = true; } else if ((iPos < 0LL) && (start >= 0LL) && !bExtendSelection) { UpdateStatusbar(false); @@ -7562,11 +7562,11 @@ bool EditFindNext(HWND hwnd, const LPEDITFINDREPLACE lpefr, bool bExtendSelectio if ((iPos < 0LL) || (end == _start)) { if ((iPos < -1) && bIsRegExpr) { - InfoBoxLng(MB_ICONWARNING, L"MsgInvalidRegex", IDS_MUI_REGEX_INVALID); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgInvalidRegex, IDS_MUI_REGEX_INVALID); bSuppressNotFound = true; } } else { - LONG const result = InfoBoxLng(MB_OKCANCEL, L"MsgFindWrap1", IDS_MUI_FIND_WRAPFW); + LONG const result = InfoBoxLng(MB_OKCANCEL, Constants.SuppressKey.MsgFindWrap1, IDS_MUI_FIND_WRAPFW); if (!IsYesOkay(result)) { iPos = NOT_FOUND; bSuppressNotFound = true; @@ -7578,7 +7578,7 @@ bool EditFindNext(HWND hwnd, const LPEDITFINDREPLACE lpefr, bool bExtendSelectio if (iPos < 0LL) { if (!bSuppressNotFound) { - InfoBoxLng(MB_OK, L"MsgNotFound", IDS_MUI_NOTFOUND); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgNotFound, IDS_MUI_NOTFOUND); } return false; } @@ -7649,7 +7649,7 @@ bool EditFindPrev(HWND hwnd, LPEDITFINDREPLACE lpefr, bool bExtendSelection, boo DocPos iPos = _FindInTarget(wchFind, sFlags, &start, &end, true, FRMOD_NORM); if ((iPos < NOT_FOUND) && bIsRegExpr) { - InfoBoxLng(MB_ICONWARNING, L"MsgInvalidRegex", IDS_MUI_REGEX_INVALID); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgInvalidRegex, IDS_MUI_REGEX_INVALID); bSuppressNotFound = true; } else if ((iPos < 0LL) && (start <= iDocEndPos) && !bExtendSelection) { UpdateStatusbar(false); @@ -7663,11 +7663,11 @@ bool EditFindPrev(HWND hwnd, LPEDITFINDREPLACE lpefr, bool bExtendSelection, boo if ((iPos < 0LL) || (start == _start)) { if ((iPos < NOT_FOUND) && bIsRegExpr) { - InfoBoxLng(MB_ICONWARNING, L"MsgInvalidRegex", IDS_MUI_REGEX_INVALID); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgInvalidRegex, IDS_MUI_REGEX_INVALID); bSuppressNotFound = true; } } else { - LONG const result = InfoBoxLng(MB_OKCANCEL, L"MsgFindWrap2", IDS_MUI_FIND_WRAPRE); + LONG const result = InfoBoxLng(MB_OKCANCEL, Constants.SuppressKey.MsgFindWrap2, IDS_MUI_FIND_WRAPRE); if (!IsYesOkay(result)) { iPos = NOT_FOUND; bSuppressNotFound = true; @@ -7679,7 +7679,7 @@ bool EditFindPrev(HWND hwnd, LPEDITFINDREPLACE lpefr, bool bExtendSelection, boo if (iPos < 0LL) { if (!bSuppressNotFound) { - InfoBoxLng(MB_OK, L"MsgNotFound", IDS_MUI_NOTFOUND); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgNotFound, IDS_MUI_NOTFOUND); } return false; } @@ -7906,7 +7906,7 @@ DocPosU EditReplaceAllInRange(HWND hwnd, LPEDITFINDREPLACE lpefr, DocPos iStartP DocPos iPos = _FindInTarget(wchFind, sFlags, &start, &end, false, FRMOD_NORM); if ((iPos < NOT_FOUND) && bIsRegExpr) { - InfoBoxLng(MB_ICONWARNING, L"MsgInvalidRegex", IDS_MUI_REGEX_INVALID); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgInvalidRegex, IDS_MUI_REGEX_INVALID); return 0; } @@ -7970,9 +7970,9 @@ bool EditReplaceAll(HWND hwnd, LPEDITFINDREPLACE lpefr, bool bShowInfo) if (bShowInfo) { if (Globals.iReplacedOccurrences) { - InfoBoxLng(MB_OK, L"MsgReplaceCount", IDS_MUI_REPLCOUNT, wchOcc); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgReplaceCount, IDS_MUI_REPLCOUNT, wchOcc); } else { - InfoBoxLng(MB_OK, L"MsgNotFound", IDS_MUI_NOTFOUND); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgNotFound, IDS_MUI_NOTFOUND); } } @@ -8011,11 +8011,11 @@ bool EditReplaceAllInSelection(HWND hwnd, LPEDITFINDREPLACE lpefr, bool bShowInf WCHAR wchOcc[64] = { L'\0' }; StringCchPrintf(wchOcc, COUNTOF(wchOcc), DOCPOSFMTW, Globals.iReplacedOccurrences); FormatNumberStr(wchOcc, COUNTOF(wchOcc), 0); - InfoBoxLng(MB_OK, L"MsgReplaceCount", IDS_MUI_REPLCOUNT, wchOcc); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgReplaceCount, IDS_MUI_REPLCOUNT, wchOcc); } } else if (bShowInfo) { - InfoBoxLng(MB_OK, L"MsgNotFound", IDS_MUI_NOTFOUND); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgNotFound, IDS_MUI_NOTFOUND); } return (bool)Globals.iReplacedOccurrences; @@ -8702,7 +8702,7 @@ void EditBookMarkLineRange(HWND hwnd) } } if (marker >= MARKER_NP3_BOOKMARK) { - InfoBoxLng(MB_ICONWARNING, L"OutOfOccurrenceMarkers", IDS_MUI_OUT_OFF_OCCMRK); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.OutOfOccurrenceMarkers, IDS_MUI_OUT_OFF_OCCMRK); return; } diff --git a/src/MuiLanguage.c b/src/MuiLanguage.c index 8f54f6735..a93a13c2c 100644 --- a/src/MuiLanguage.c +++ b/src/MuiLanguage.c @@ -87,6 +87,7 @@ unsigned grepWinLang_CountOf() { return COUNTOF(grepWinLangFileName); }; +// ----------------------------------------------------------------------------- grepWinLng_t grepWinLangFileNamePortableApps[] = { { L"en-US", L"" }, // build-in language @@ -456,9 +457,8 @@ unsigned LoadLanguageResources(LPCWSTR pLocaleName) { MUI_LanguageDLLs[iInternalLngIndex].bIsActive = true; iLngIndex = iInternalLngIndex; - const WCHAR *const suprMsg = L"MsgPrefLanguageNotAvailable"; - InfoBoxLng(MB_ICONWARNING, suprMsg, IDS_WARN_PREF_LNG_NOT_AVAIL, pLocaleName); - int const noMsg = IniFileGetLong(Paths.IniFile, Constants.SectionSuppressedMessages, suprMsg, 0); + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.MsgPrefLanguageNotAvailable, IDS_WARN_PREF_LNG_NOT_AVAIL, pLocaleName); + int const noMsg = IniFileGetLong(Paths.IniFile, Constants.SectionSuppressedMessages, Constants.SuppressKey.MsgPrefLanguageNotAvailable, 0); if (noMsg && Globals.bCanSaveIniFile) { IniFileSetString(Paths.IniFile, Constants.Settings2_Section, L"PreferredLanguageLocaleName", MUI_LanguageDLLs[iInternalLngIndex].LocaleName); } diff --git a/src/Notepad3.c b/src/Notepad3.c index 34bae714b..fb808f036 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -30,9 +30,7 @@ #include #include #include -#include #include -//#include #include "PathLib.h" #include "Edit.h" @@ -93,6 +91,33 @@ CONSTANTS_T const Constants = { , L"Window" // Inifile Section "Window" , L"Styles" // Inifile Section "Styles" , L"Suppressed Messages" // Inifile Section "SuppressedMessages" + , { // SuppressKey — keys under [Suppressed Messages] + L"AllowClearUndoHistory" + , L"InfoInstanceExist" + , L"MsgConv1" + , L"MsgConv2" + , L"MsgConv3" + , L"MsgDiscardUntitled" + , L"MsgFileSizeWarning" + , L"MsgFileUnknownExt" + , L"MsgFindWrap1" + , L"MsgFindWrap2" + , L"MsgInvalidRegex" + , L"MsgNoOrWrongPassphrase" + , L"MsgNotFound" + , L"MsgPrefLanguageNotAvailable" + , L"MsgReplaceCount" + , L"MsgResetScheme" + , L"MsgSaveSettingsInfo" + , L"MsgStickyWinPos" + , L"MsgUTF32Unsupported" + , L"NoAdminTool" + , L"NotSuitableToolbarDim" + , L"OutOfOccurrenceMarkers" + , L"PreserveFileModTime" + , L"QuietKeepReadonlyLock" + , L"ReloadExSavedCfg" + } }; @@ -453,7 +478,7 @@ static void CALLBACK MQ_ExecuteNext(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWOR UNREFERENCED_PARAMETER(idEvent); // must be IDT_TIMER_MRKALL UNREFERENCED_PARAMETER(dwTime); // This is the value returned by the GetTickCount() function - CmdMessageQueue_t* pmqc; + CmdMessageQueue_t* pmqc = NULL; DL_FOREACH(MessageQueue, pmqc) { if (pmqc->delay >= 0) { --(pmqc->delay); // count down @@ -860,7 +885,7 @@ static void _CleanUpResources(const HWND hwnd, bool bIsInitialized) } CmdMessageQueue_t* pmqc = NULL; - CmdMessageQueue_t* dummy; + CmdMessageQueue_t* dummy = NULL; DL_FOREACH_SAFE(MessageQueue, pmqc, dummy) { DL_DELETE(MessageQueue, pmqc); FreeMem(pmqc); @@ -4163,7 +4188,7 @@ LRESULT MsgInitMenu(HWND hwnd, WPARAM wParam, LPARAM lParam) EnableCmd(hmenu, IDM_LINEENDINGS_LF, !ro); EnableCmd(hmenu, IDM_LINEENDINGS_CR, !ro); - int i; + int i = 0; if (Encoding_IsUNICODE_REVERSE(Encoding_GetCurrent())) { i = IDM_ENCODING_UNICODEREV; @@ -4592,7 +4617,7 @@ static void _ApplyChangeHistoryMode() int const iChgHist = SciCall_GetChangeHistory(); if (iChgHist == Settings.ChangeHistoryMode) { return; } if ((!iChgHist && Settings.ChangeHistoryMode) || !Settings.ChangeHistoryMode) { - if (IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONWARNING, L"AllowClearUndoHistory", IDS_MUI_ASK_CLEAR_UNDO))) { + if (IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONWARNING, Constants.SuppressKey.AllowClearUndoHistory, IDS_MUI_ASK_CLEAR_UNDO))) { UndoRedoReset(); } else { @@ -4673,7 +4698,7 @@ static bool _HandleFileCommands(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lPar case IDM_FILE_PRESERVE_FILEMODTIME: { if (!Flags.bPreserveFileModTime) { - InfoBoxLng(MB_OK, L"PreserveFileModTime", IDS_MUI_INF_PRSVFILEMODTM); + InfoBoxLng(MB_OK, Constants.SuppressKey.PreserveFileModTime, IDS_MUI_INF_PRSVFILEMODTM); } Flags.bPreserveFileModTime = true; FileSave(FSF_SaveAlways); @@ -6408,7 +6433,7 @@ static bool _HandleViewAndSettingsCommands(HWND hwnd, UINT umsg, WPARAM wParam, break; case IDM_VIEW_CHGHIST_CLEAR_UNDOREDO: - if (IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONWARNING, L"AllowClearUndoHistory", IDS_MUI_ASK_CLEAR_UNDO))) { + if (IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONWARNING, Constants.SuppressKey.AllowClearUndoHistory, IDS_MUI_ASK_CLEAR_UNDO))) { UndoRedoReset(); UpdateToolbar(); UpdateMargins(true); @@ -6566,7 +6591,7 @@ static bool _HandleViewAndSettingsCommands(HWND hwnd, UINT umsg, WPARAM wParam, Flags.bStickyWindowPosition = !Flags.bStickyWindowPosition; // toggle if (Flags.bStickyWindowPosition) { - InfoBoxLng(MB_OK, L"MsgStickyWinPos", IDS_MUI_STICKYWINPOS); + InfoBoxLng(MB_OK, Constants.SuppressKey.MsgStickyWinPos, IDS_MUI_STICKYWINPOS); } if (OpenSettingsFile("IDM_VIEW_STICKYWINPOS")) { @@ -6685,7 +6710,7 @@ static bool _HandleViewAndSettingsCommands(HWND hwnd, UINT umsg, WPARAM wParam, case IDM_VIEW_WIN_DARK_MODE: { - if (!IsYesOkay(InfoBoxLng(MB_OKCANCEL | MB_ICONWARNING, L"MsgResetScheme", IDS_MUI_WARN_STYLE_RESET))) { + if (!IsYesOkay(InfoBoxLng(MB_OKCANCEL | MB_ICONWARNING, Constants.SuppressKey.MsgResetScheme, IDS_MUI_WARN_STYLE_RESET))) { break; } @@ -7590,7 +7615,7 @@ LRESULT MsgCommand(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) bool const bIsThemesMenuCmd = ((iLoWParam >= IDM_THEMES_FACTORY_RESET) && (iLoWParam < (int)(IDM_THEMES_FACTORY_RESET + ThemeItems_CountOf()))); if (bIsThemesMenuCmd) { if (iLoWParam == IDM_THEMES_FACTORY_RESET) { - if (!IsYesOkay(InfoBoxLng(MB_OKCANCEL | MB_ICONWARNING, L"MsgResetScheme", IDS_MUI_WARN_STYLE_RESET))) { + if (!IsYesOkay(InfoBoxLng(MB_OKCANCEL | MB_ICONWARNING, Constants.SuppressKey.MsgResetScheme, IDS_MUI_WARN_STYLE_RESET))) { return FALSE; } } @@ -8813,7 +8838,7 @@ inline static LRESULT _MsgNotifyLean(const SCNotification* const scn, bool* bMod EditToggleView(Globals.hwndEdit); } else { - if (!FileWatching.MonitoringLog && !IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONINFORMATION, L"QuietKeepReadonlyLock", IDS_MUI_DOCUMENT_READONLY))) { + if (!FileWatching.MonitoringLog && !IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONINFORMATION, Constants.SuppressKey.QuietKeepReadonlyLock, IDS_MUI_DOCUMENT_READONLY))) { SendWMCommand(Globals.hwndMain, IDM_VIEW_READONLY); } else { @@ -10938,7 +10963,7 @@ bool FileIO(bool fLoad, const HPATHL hfile_pth, EditFileIOStatus* status, SciCall_SetReadOnly(Settings.DocReadOnlyMode || FileWatching.MonitoringLog); } else { - int idx; + int idx = 0; if (MRU_FindPath(Globals.pFileMRU, hfile_pth, &idx)) { Globals.pFileMRU->iEncoding[idx] = status->iEncoding; Globals.pFileMRU->iCaretPos[idx] = (Settings.PreserveCaretPos ? SciCall_GetCurrentPos() : -1); @@ -11111,7 +11136,7 @@ bool FileLoad(const HPATHL hfile_pth, const FileLoadFlags fLoadFlags, const DocP HWND hwnd = NULL; if (FindOtherInstance(&hwnd, hopen_file)) { - if (!s_bInitAppDone || IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONQUESTION, L"InfoInstanceExist", IDS_MUI_ASK_INSTANCE_EXISTS))) { + if (!s_bInitAppDone || IsYesOkay(InfoBoxLng(MB_YESNO | MB_ICONQUESTION, Constants.SuppressKey.InfoInstanceExist, IDS_MUI_ASK_INSTANCE_EXISTS))) { if (IsIconic(hwnd)) { ShowWindowAsync(hwnd, SW_RESTORE); } @@ -11176,7 +11201,7 @@ bool FileLoad(const HPATHL hfile_pth, const FileLoadFlags fLoadFlags, const DocP } } else { - int idx; + int idx = 0; if (!bReloadFile && MRU_FindPath(Globals.pFileMRU, hopen_file, &idx)) { fioStatus.iEncoding = Globals.pFileMRU->iEncoding[idx]; if (Encoding_IsValid(fioStatus.iEncoding)) { @@ -11586,9 +11611,11 @@ bool FileSave(FileSaveFlags fSaveFlags) GetLngString(IDS_MUI_UNTITLED, wchFileName, COUNTOF(wchFileName)); Path_GetDisplayName(wchFileName, COUNTOF(wchFileName), Paths.CurrentFile, NULL, false); - - INT_PTR const answer = InfoBoxLng(MB_YESNOCANCEL | MB_ICONWARNING, NULL, IDS_MUI_ASK_SAVE, wchFileName); - switch (answer) + bool const bDiscardOptOut = Settings2.DiscardOnClosingUntitledPasteBoard && Path_IsEmpty(Paths.CurrentFile) && IsPasteBoardActive(); + UINT const uMsgType = MB_YESNOCANCEL | MB_ICONWARNING | (bDiscardOptOut ? MB_DEFBUTTON2 : 0L); + LPCWSTR const lpSuppressKey = bDiscardOptOut ? Constants.SuppressKey.MsgDiscardUntitled : NULL; + INT_PTR const answer = InfoBoxLng(uMsgType, lpSuppressKey, IDS_MUI_ASK_SAVE, wchFileName); + switch (LOWORD(answer)) // InfoBoxLng packs suppression-mode in HIWORD; LOWORD holds the actual button ID { case IDCANCEL: return false; @@ -11693,7 +11720,7 @@ bool FileSave(FileSaveFlags fSaveFlags) WCHAR tch[256] = { L'\0' }; if (Settings.SaveSettings) { LoadLngStringW(IDS_MUI_RELOADCFGSEX, tch, COUNTOF(tch)); } UINT const typ = Settings.SaveSettings ? (MB_YESNO | MB_ICONWARNING) : (MB_YESNO | MB_ICONINFORMATION); - LONG const answer = InfoBoxLng(typ, L"ReloadExSavedCfg", IDS_MUI_RELOADSETTINGS, tch); + LONG const answer = InfoBoxLng(typ, Constants.SuppressKey.ReloadExSavedCfg, IDS_MUI_RELOADSETTINGS, tch); if (IsYesOkay(answer)) { ///~SaveAllSettings(true); ~ already saved (CurrentFile) DialogNewWindow(Globals.hwndMain, true, Paths.CurrentFile, NULL); diff --git a/src/Notepad3Util.c b/src/Notepad3Util.c index f7c226dcf..b317687e3 100644 --- a/src/Notepad3Util.c +++ b/src/Notepad3Util.c @@ -53,7 +53,7 @@ HBITMAP NP3Util_LoadBitmapFile(const HPATHL hpath) bDimOK = (bmp.bmWidth >= (height * NUMTOOLBITMAPS)); } if (!bDimOK) { - InfoBoxLng(MB_ICONWARNING, L"NotSuitableToolbarDim", IDS_MUI_ERR_BITMAP, Path_Get(hpath), + InfoBoxLng(MB_ICONWARNING, Constants.SuppressKey.NotSuitableToolbarDim, IDS_MUI_ERR_BITMAP, Path_Get(hpath), (height * NUMTOOLBITMAPS), height, NUMTOOLBITMAPS); } } diff --git a/src/TypeDefs.h b/src/TypeDefs.h index ef6582bd4..22d3057f9 100644 --- a/src/TypeDefs.h +++ b/src/TypeDefs.h @@ -478,6 +478,36 @@ typedef struct CONSTANTS_T { const WCHAR* const Styles_Section; const WCHAR* const SectionSuppressedMessages; + // Keys under [Suppressed Messages] — pass to InfoBoxLng() / IniFile{Get,Set,Delete}Long(). + // Add a field here when introducing a new suppressible dialog; never inline a literal at the call site. + struct { + const WCHAR* const AllowClearUndoHistory; + const WCHAR* const InfoInstanceExist; + const WCHAR* const MsgConv1; + const WCHAR* const MsgConv2; + const WCHAR* const MsgConv3; + const WCHAR* const MsgDiscardUntitled; + const WCHAR* const MsgFileSizeWarning; + const WCHAR* const MsgFileUnknownExt; + const WCHAR* const MsgFindWrap1; + const WCHAR* const MsgFindWrap2; + const WCHAR* const MsgInvalidRegex; + const WCHAR* const MsgNoOrWrongPassphrase; + const WCHAR* const MsgNotFound; + const WCHAR* const MsgPrefLanguageNotAvailable; + const WCHAR* const MsgReplaceCount; + const WCHAR* const MsgResetScheme; + const WCHAR* const MsgSaveSettingsInfo; + const WCHAR* const MsgStickyWinPos; + const WCHAR* const MsgUTF32Unsupported; + const WCHAR* const NoAdminTool; + const WCHAR* const NotSuitableToolbarDim; + const WCHAR* const OutOfOccurrenceMarkers; + const WCHAR* const PreserveFileModTime; + const WCHAR* const QuietKeepReadonlyLock; + const WCHAR* const ReloadExSavedCfg; + } SuppressKey; + } CONSTANTS_T, *PCONSTANTS_T; extern CONSTANTS_T const Constants; @@ -792,6 +822,7 @@ typedef struct SETTINGS2_T { bool SubWrappedLineSelectOnMarginClick; bool LexerSQLNumberSignAsComment; bool AtomicFileSave; + bool DiscardOnClosingUntitledPasteBoard; int ExitOnESCSkipLevel; int ZoomTooltipTimeout; int WrapAroundTooltipTimeout; From fe91c428168d66bde94947ce2235bca830d16b08 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Fri, 24 Apr 2026 12:29:18 +0200 Subject: [PATCH 2/2] PasteBoard: defer minimize on /B + /I startup so auto-pasted clipboard is visible briefly --- Build/Notepad3.ini | 1 + readme/config/Configuration.md | 4 +++ src/Config/Config.cpp | 1 + src/Notepad3.c | 65 +++++++++++++++++++++++++--------- src/Notepad3.h | 1 + src/TypeDefs.h | 1 + 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/Build/Notepad3.ini b/Build/Notepad3.ini index e80c4d5de..349f8e38e 100644 --- a/Build/Notepad3.ini +++ b/Build/Notepad3.ini @@ -32,6 +32,7 @@ SettingsVersion=5 ;PasteBoardSeparator= ;(-> ) {separator pre-pended before each new clipboard entry (pasted at caret) in pasteboard mode; suppressed on first paste after enable and when caret is at a line start; empty=no separator; supports \r\n, \n, \t, \xHH; include newlines explicitly e.g. "\r\n---\r\n" for a dashed separator line} ;PasteBoardDebounceMs=200 ;(min: 0, max: 5000[msec]) {debounce interval for clipboard monitoring} ;PasteBoardAddTimestamp=0 ;(0/1) {prepend [HH:MM:SS] timestamp to each pasted entry in pasteboard mode} +;PasteBoardInitialShowMs=1500 ;(min: 500, max: 5000[msec]) {when launched with /B and /I together: show the window normally for this duration so the auto-pasted clipboard is visible, then minimize. Has no effect unless both /B and /I are passed.} ;NoFadeHidden=0 ;NoFileVariables=0 ;NoHTMLGuess=0 diff --git a/readme/config/Configuration.md b/readme/config/Configuration.md index 7a2e2aaa4..aa99837bc 100644 --- a/readme/config/Configuration.md +++ b/readme/config/Configuration.md @@ -226,6 +226,10 @@ PasteBoardSeparator=\r\n\r\n\r\n ; two blank lines between entries Set to `1` to prepend a `[HH:MM:SS]` timestamp to each pasted entry. +#### `PasteBoardInitialShowMs=1500` + +When Notepad3 is launched with **both `/B` (clipboard monitoring) and `/I` (start minimized)**, the immediate minimize is deferred so the user can see the window populate with the one-shot auto-pasted clipboard content. The window stays visible for this many milliseconds, then minimizes (to the tray or taskbar, per `Settings.MinimizeToTray`). Range: `500`–`5000` ms (clamped at load time). Has no effect unless both `/B` and `/I` are passed on the command line — `/I` alone still minimizes immediately as before. + #### `NoFadeHidden=0` Set to `1` to disable fading of hidden objects in file lists (Favorites, etc.). diff --git a/src/Config/Config.cpp b/src/Config/Config.cpp index b4753bda6..3b6b8fccd 100644 --- a/src/Config/Config.cpp +++ b/src/Config/Config.cpp @@ -1480,6 +1480,7 @@ void LoadSettings() } Settings2.PasteBoardDebounceMs = clampi(IniSectionGetInt(IniSecSettings2, L"PasteBoardDebounceMs", 200), 0, 5000); Settings2.PasteBoardAddTimestamp = IniSectionGetBool(IniSecSettings2, L"PasteBoardAddTimestamp", false); + Settings2.PasteBoardInitialShowMs = clampi(IniSectionGetInt(IniSecSettings2, L"PasteBoardInitialShowMs", 1500), 500, 5000); for (int i = 0; i < COUNTOF(Settings2.CodeFontPrefPrioList); ++i) { if (i < COUNTOF(g_CodeFontPrioList)) diff --git a/src/Notepad3.c b/src/Notepad3.c index fb808f036..16a5933e7 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -1849,6 +1849,35 @@ bool InitWndClass(const HINSTANCE hInstance, LPCWSTR lpszWndClassName, LPCWSTR l #endif +//============================================================================= +// +// _StartupMinimizeMainWnd() / _DeferMinimizeTimerProc() +// Hoisted from InitInstance() so the same minimize sequence runs either immediately +// or after the deferred-minimize timer fires (used for the /B + /I startup combo). +// +static void _StartupMinimizeMainWnd(HWND hwndMain) +{ + SetWindowPos(hwndMain, Settings.AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (!Settings.ShowTitlebar) { + SetWindowLong(hwndMain, GWL_STYLE, GetWindowLong(hwndMain, GWL_STYLE) & ~WS_CAPTION); + } + if (Settings.MinimizeToTray) { + MinimizeWndToTray(hwndMain); + } + else { + MinimizeWndToTaskbar(hwndMain); + } +} + +static VOID CALLBACK _DeferMinimizeTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(dwTime); + KillTimer(hwnd, idEvent); // one-shot + _StartupMinimizeMainWnd(hwnd); +} + + //============================================================================= // // InitInstance() - DarkMode already initialized ! @@ -1930,15 +1959,21 @@ HWND InitInstance(const HINSTANCE hInstance, int nCmdShow) // Determine if starting minimized/tray (don't show window early in that case) bool const bStartMinimized = s_flagStartAsTrayIcon || (nCmdShow == SW_MINIMIZE) || (nCmdShow == SW_SHOWMINIMIZED); + // /B + /I together: show the window long enough for the one-shot auto-paste to land, + // then minimize via timer (fired below after PasteBoard_Start). Captures s_flagPasteBoard + // before the PasteBoard activation block clears it. + bool const bDeferMinimizeForPasteBoard = bStartMinimized && s_flagPasteBoard; // Show window frame early for faster perceived startup — the user sees - // the window (with initial toolbar from WM_CREATE) while we re-create bars - if (!bStartMinimized) { + // the window (with initial toolbar from WM_CREATE) while we re-create bars. + // For the /B + /I deferred-minimize case, force SW_SHOWNORMAL: nCmdShow could be + // SW_SHOWMINIMIZED (shortcut "Start minimized") which would defeat the purpose. + if (!bStartMinimized || bDeferMinimizeForPasteBoard) { if (!Settings.ShowTitlebar) { SetWindowLong(hwndMain, GWL_STYLE, GetWindowLong(hwndMain, GWL_STYLE) & ~WS_CAPTION); } SetWindowPos(hwndMain, Settings.AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - ShowWindow(hwndMain, nCmdShow); + ShowWindow(hwndMain, bDeferMinimizeForPasteBoard ? SW_SHOWNORMAL : nCmdShow); UpdateWindow(hwndMain); } @@ -1949,7 +1984,7 @@ HWND InitInstance(const HINSTANCE hInstance, int nCmdShow) // Force layout recalculation after toolbar/statusbar re-creation // (early ShowWindow already triggered WM_SIZE with old child windows) - if (!bStartMinimized) { + if (!bStartMinimized || bDeferMinimizeForPasteBoard) { RECT rc; GetClientRect(hwndMain, &rc); SendMessage(hwndMain, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rc.right, rc.bottom)); @@ -2010,21 +2045,13 @@ HWND InitInstance(const HINSTANCE hInstance, int nCmdShow) ShowWindowAsync(s_hwndEditFrame, SW_SHOWDEFAULT); ShowWindowAsync(Globals.hwndEdit, SW_SHOWDEFAULT); - if (bStartMinimized) { + if (bStartMinimized && !bDeferMinimizeForPasteBoard) { //~SnapToWinInfoPos(hwndMain, g_IniWinInfo, SCR_NORMAL, SW_HIDE); - SetWindowPos(hwndMain, Settings.AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - if (!Settings.ShowTitlebar) { - SetWindowLong(hwndMain, GWL_STYLE, GetWindowLong(hwndMain, GWL_STYLE) & ~WS_CAPTION); - } - if (Settings.MinimizeToTray) { - MinimizeWndToTray(hwndMain); - } - else { - MinimizeWndToTaskbar(hwndMain); - } + _StartupMinimizeMainWnd(hwndMain); } else { - // Window was already shown above; ensure children are painted + // Either not minimizing, or deferring minimize until /B auto-paste lands — + // the window must paint normally so the user sees the pasted content briefly. UpdateWindow(hwndMain); } @@ -2186,6 +2213,12 @@ HWND InitInstance(const HINSTANCE hInstance, int nCmdShow) } } + // /B + /I: arm the deferred-minimize timer now that PasteBoard is started. + // PasteBoardInitialShowMs is clamped to 500..5000 at load time. + if (bDeferMinimizeForPasteBoard) { + SetTimer(Globals.hwndMain, ID_DEFERMINIMIZETIMER, (UINT)Settings2.PasteBoardInitialShowMs, _DeferMinimizeTimerProc); + } + // check if a lexer was specified from the command line if (s_flagLexerSpecified) { if (s_lpSchemeArg) { diff --git a/src/Notepad3.h b/src/Notepad3.h index 28932943a..1dbba8887 100644 --- a/src/Notepad3.h +++ b/src/Notepad3.h @@ -70,6 +70,7 @@ np3params, *LPnp3params; #define ID_LOGROTATETIMER (0xA003) // Log Rotation Retry #define ID_AUTOSCROLLTIMER (0xA004) // Middle-Click Auto-Scroll #define ID_ATOMICSAVETIMER (0xA005) // Atomic Save Detection +#define ID_DEFERMINIMIZETIMER (0xA006) // Deferred minimize on /B + /I startup //==== Reuse Window Lock Timeout ============================================== diff --git a/src/TypeDefs.h b/src/TypeDefs.h index 22d3057f9..a81715e87 100644 --- a/src/TypeDefs.h +++ b/src/TypeDefs.h @@ -878,6 +878,7 @@ typedef struct SETTINGS2_T { WCHAR PasteBoardSeparator[MICRO_BUFFER]; int PasteBoardDebounceMs; bool PasteBoardAddTimestamp; + int PasteBoardInitialShowMs; const WCHAR* CodeFontPrefPrioList[MICRO_BUFFER]; const WCHAR* TextFontPrefPrioList[MICRO_BUFFER];