Add warning tip for the max length text on search (Find/Replace)

Fix #16749, close #16834
This commit is contained in:
Don Ho 2025-07-19 15:33:33 +02:00
parent 0d98bd24f1
commit d6ad51a022
8 changed files with 162 additions and 17 deletions

View File

@ -1889,6 +1889,7 @@ If you select advanced mode but do not edit files in the aforementioned language
<statusbar-Sel value="Sel: "/>
<statusbar-Sel-number value="Sel"/>
<toolbar-accent-tip value="This option makes your toolbar icons follow Windows system accent color. Accent color is the highlight color used in buttons, borders, and Start menu tiles in Windows. To change it, go to Settings &gt; Personalization &gt; Colors, then select your preferred accent color."/>
<max-len-on-search-tip value="Only 2046 bytes are allowed for the find/replace text length - your input could be truncated."/>
</MiscStrings>
</Native-Langue>
</NotepadPlus>

View File

@ -1889,6 +1889,7 @@ If you select advanced mode but do not edit files in the aforementioned language
<statusbar-Sel value="Sel: "/>
<statusbar-Sel-number value="Sel"/>
<toolbar-accent-tip value="This option makes your toolbar icons follow Windows system accent color. Accent color is the highlight color used in buttons, borders, and Start menu tiles in Windows. To change it, go to Settings &gt; Personalization &gt; Colors, then select your preferred accent color."/>
<max-len-on-search-tip value="Only 2046 bytes are allowed for the find/replace text length - your input could be truncated."/>
</MiscStrings>
</Native-Langue>
</NotepadPlus>

View File

@ -1884,6 +1884,7 @@ Si vous sélectionnez le mode avancé sans modifier les fichiers des langues men
<statusbar-Sel value="Sel : "/>
<statusbar-Sel-number value="Sel"/>
<toolbar-accent-tip value="Cette option permet aux icônes de votre barre d'outils d'adopter la couleur d'accentuation du système Windows. La couleur d'accentuation est la couleur de surbrillance utilisée pour les boutons, les bordures et les tuiles du menu Démarrer sous Windows. Pour la modifier, accédez à Paramètres &gt; Personnalisation &gt; Couleurs, puis sélectionnez votre couleur d'accentuation préférée."/>
<max-len-on-search-tip value="Seuls 2046 octets sont autorisés pour la longueur du texte de recherche/remplacement - votre entrée pourrait être tronquée."/>
</MiscStrings>
</Native-Langue>
</NotepadPlus>

View File

@ -1708,6 +1708,7 @@ C、C++、Java、C#、Objective-C、PHP、JavaScript、JSP、CSS、Perl、Rust
<statusbar-Sel value="選取:"/>
<statusbar-Sel-number value="選取"/>
<toolbar-accent-tip value="此選項可讓您的工具列圖示遵循 Windows 系統強調色。強調色是 Windows 中的按鈕、邊框和開始功能表圖塊所使用的反白顯示顏色。要更改它,請轉到「設定」&gt;「個性化」&gt;「顏色」,然後選擇您喜歡的強調色。"/>
<max-len-on-search-tip value="尋找/取代文字長度僅允許 2046 個位元組 - 您的輸入可能會被截斷。"/>
</MiscStrings>
</Native-Langue>
</NotepadPlus>

View File

@ -2094,4 +2094,61 @@ bool isCoreWindows()
return isCoreWindows;
}
bool ControlInfoTip::init(HINSTANCE hInst, HWND ctrl2attached, HWND ctrl2attachedParent, const wstring& tipStr, bool isRTL)
{
_hWndInfoTip = CreateWindowEx(isRTL ? WS_EX_LAYOUTRTL : 0, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
ctrl2attachedParent, NULL,
hInst, NULL);
if (!_hWndInfoTip)
return false;
_toolInfo.cbSize = sizeof(_toolInfo);
_toolInfo.hwnd = ctrl2attachedParent;
_toolInfo.uFlags = TTF_IDISHWND | TTF_TRACK;
_toolInfo.uId = (UINT_PTR)ctrl2attached;
_toolInfo.lpszText = const_cast<PTSTR>(tipStr.c_str());
if (!SendMessage(_hWndInfoTip, TTM_ADDTOOL, 0, (LPARAM)&_toolInfo))
{
::DestroyWindow(_hWndInfoTip);
_hWndInfoTip = nullptr;
return false;
}
SendMessage(_hWndInfoTip, TTM_SETMAXTIPWIDTH, 0, 200);
SendMessage(_hWndInfoTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, MAKELPARAM((15000), (0)));
SendMessage(_hWndInfoTip, TTM_ACTIVATE, TRUE, 0); // Activates the tooltip control globally
return true;
}
void ControlInfoTip::show() const
{
if (!isValid()) return;
RECT rcComboBox;
GetWindowRect(reinterpret_cast<HWND>(_toolInfo.uId), &rcComboBox);
int xPos = rcComboBox.left + (rcComboBox.right - rcComboBox.left) / 2;
int yPos = rcComboBox.top + 25;
SendMessage(_hWndInfoTip, TTM_TRACKPOSITION, 0, MAKELPARAM(xPos, yPos));
SendMessage(_hWndInfoTip, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&_toolInfo);
}
void ControlInfoTip::hide()
{
if (_hWndInfoTip)
{
SendMessage(_hWndInfoTip, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&_toolInfo);
DestroyWindow(_hWndInfoTip);
_hWndInfoTip = nullptr;
}
}
#pragma warning(default:4996)

View File

@ -19,6 +19,7 @@
#include <string>
#include <sstream>
#include <windows.h>
#include <commctrl.h>
#include <iso646.h>
#include <cstdint>
#include <unordered_set>
@ -296,3 +297,34 @@ bool doesPathExist(const wchar_t* path, DWORD milliSec2wait = 0, bool* isTimeout
bool isWindowVisibleOnAnyMonitor(const RECT& rectWndIn);
bool isCoreWindows();
class ControlInfoTip final
{
public:
ControlInfoTip() {};
~ControlInfoTip() {
if (_hWndInfoTip) {
hide();
}
};
bool init(HINSTANCE hInst, HWND ctrl2attached, HWND ctrl2attachedParent, const std::wstring& tipStr, bool isRTL);
bool isValid() const {
return _hWndInfoTip != nullptr;
};
HWND getTipHandle() const {
return _hWndInfoTip;
};
void show() const;
void hide();
private:
HWND _hWndInfoTip = nullptr;
TOOLINFO _toolInfo = {};
ControlInfoTip(const ControlInfoTip&) = delete;
ControlInfoTip& operator=(const ControlInfoTip&) = delete;
};

View File

@ -1550,6 +1550,10 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
HWND hFindCombo = ::GetDlgItem(_hSelf, IDFINDWHAT);
HWND hReplaceCombo = ::GetDlgItem(_hSelf, IDREPLACEWITH);
::SendMessage(hFindCombo, CB_LIMITTEXT, FINDREPLACE_MAXLENGTH - 2, 0);
::SendMessage(hReplaceCombo, CB_LIMITTEXT, FINDREPLACE_MAXLENGTH - 2, 0);
HWND hFiltersCombo = ::GetDlgItem(_hSelf, IDD_FINDINFILES_FILTERS_COMBO);
HWND hDirCombo = ::GetDlgItem(_hSelf, IDD_FINDINFILES_DIR_COMBO);
@ -1918,6 +1922,14 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
return TRUE;
}
case WM_NCLBUTTONDOWN:
{
if (_maxLenOnSearchTip.isValid())
{
_maxLenOnSearchTip.hide();
}
return FALSE;
}
case WM_COMMAND:
{
@ -1926,6 +1938,47 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
FindHistory & findHistory = nppParamInst.getFindHistory();
switch (LOWORD(wParam))
{
case IDFINDWHAT:
case IDREPLACEWITH:
{
if (HIWORD(wParam) == CBN_EDITUPDATE)
{
HWND hComboBox = ::GetDlgItem(_hSelf, LOWORD(wParam));
LRESULT length = ::GetWindowTextLength(hComboBox);
if (length >= FINDREPLACE_MAXLENGTH - 2)
{
if (!_maxLenOnSearchTip.isValid()) // Create the tooltip and add the tool ONLY ONCE
{
NativeLangSpeaker* pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
static wstring maxLenOnSearchTip = pNativeSpeaker->getLocalizedStrFromID("max-len-on-search-tip", L"Only 2046 bytes are allowed for the find/replace text length - your input could be truncated.");
bool isSuccessful = _maxLenOnSearchTip.init(_hInst, hComboBox, _hSelf, maxLenOnSearchTip.c_str(), _isRTL);
if (!isSuccessful)
{
return FALSE;
}
NppDarkMode::setDarkTooltips(_maxLenOnSearchTip.getTipHandle(), NppDarkMode::ToolTipsType::tooltip);
}
_maxLenOnSearchTip.show();
}
else
{
if (_maxLenOnSearchTip.isValid())
{
_maxLenOnSearchTip.hide();
}
}
}
else if (HIWORD(wParam) == CBN_KILLFOCUS || HIWORD(wParam) == CBN_SELCHANGE)
{
if (_maxLenOnSearchTip.isValid())
{
_maxLenOnSearchTip.hide();
}
}
return TRUE;
}
//Single actions
case IDC_2_BUTTONS_MODE:
{
@ -2007,7 +2060,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
FindStatus findStatus = FSFound;
processFindNext(_options._str2Search.c_str(), _env, &findStatus);
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
NativeLangSpeaker *pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
if (findStatus == FSEndReached)
{
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-end-reached", FIND_STATUS_END_REACHED_TEXT);
@ -2362,8 +2415,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
if (_currentStatus == REPLACE_DLG)
{
NppParameters& nppParam = NppParameters::getInstance();
const NppGUI& nppGui = nppParam.getNppGUI();
const NppGUI& nppGui = nppParamInst.getNppGUI();
if (!nppGui._confirmReplaceInAllOpenDocs || replaceInOpenDocsConfirmCheck())
{
setStatusbarMessage(L"", FSNoMessage);
@ -2391,7 +2443,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
setStatusbarMessage(L"", FSNoMessage);
if ((*_ppEditView)->getCurrentBuffer()->isReadOnly())
{
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
NativeLangSpeaker *pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replace-readonly", L"Replace: Cannot replace text. The current document is read only.");
setStatusbarMessage(msg, FSNotFound);
return TRUE;
@ -2411,7 +2463,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
nppParamInst._isFindReplacing = false;
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
NativeLangSpeaker *pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
if (nbReplaced == FIND_INVALID_REGULAR_EXPRESSION)
{
setStatusbarMessageWithRegExprErr(*_ppEditView);
@ -2456,7 +2508,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
int nbCounted = processAll(ProcessCountAll, &_options);
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
NativeLangSpeaker *pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
if (nbCounted == FIND_INVALID_REGULAR_EXPRESSION)
{
setStatusbarMessageWithRegExprErr(*_ppEditView);
@ -2509,7 +2561,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
nppParamInst._isFindReplacing = false;
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
NativeLangSpeaker *pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
if (nbMarked == FIND_INVALID_REGULAR_EXPRESSION)
{
setStatusbarMessageWithRegExprErr(*_ppEditView);
@ -2675,7 +2727,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
{
::SendDlgItemMessage(_hSelf, IDC_TRANSPARENT_LOSSFOCUS_RADIO, BM_SETCHECK, BST_UNCHECKED, 0);
::SendDlgItemMessage(_hSelf, IDC_TRANSPARENT_ALWAYS_RADIO, BM_SETCHECK, BST_UNCHECKED, 0);
(NppParameters::getInstance()).removeTransparent(_hSelf);
nppParamInst.removeTransparent(_hSelf);
findHistory._transparencyMode = FindHistory::none;
}
@ -2685,14 +2737,14 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
case IDC_TRANSPARENT_ALWAYS_RADIO :
{
int percent = static_cast<int32_t>(::SendDlgItemMessage(_hSelf, IDC_PERCENTAGE_SLIDER, TBM_GETPOS, 0, 0));
(NppParameters::getInstance()).SetTransparent(_hSelf, percent);
nppParamInst.SetTransparent(_hSelf, percent);
findHistory._transparencyMode = FindHistory::persistent;
}
return TRUE;
case IDC_TRANSPARENT_LOSSFOCUS_RADIO :
{
(NppParameters::getInstance()).removeTransparent(_hSelf);
nppParamInst.removeTransparent(_hSelf);
findHistory._transparencyMode = FindHistory::onLosingFocus;
}
return TRUE;
@ -2761,7 +2813,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
{
if (_currentStatus == FINDINFILES_DLG)
{
NativeLangSpeaker* pNativeSpeaker = NppParameters::getInstance().getNativeLangSpeaker();
NativeLangSpeaker* pNativeSpeaker = nppParamInst.getNativeLangSpeaker();
const wstring title = pNativeSpeaker->getLocalizedStrFromID("find-in-files-select-folder", L"Select a folder to search from");
folderBrowser(_hSelf, title, IDD_FINDINFILES_DIR_COMBO, _options._directory.c_str());
}
@ -2773,7 +2825,7 @@ intptr_t CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARA
RECT rc{};
getWindowRect(rc);
LONG w = rc.right - rc.left;
bool& isLessModeOn = NppParameters::getInstance().getNppGUI()._findWindowLessMode;
bool& isLessModeOn = nppParamInst.getNppGUI()._findWindowLessMode;
isLessModeOn = !isLessModeOn;
long dlgH = (isLessModeOn ? _lesssModeHeight : _szMinDialog.cy) + _szBorder.cy;
@ -2895,6 +2947,7 @@ bool FindReplaceDlg::processFindNext(const wchar_t *txt2find, const FindOption *
(*_ppEditView)->execute(SCI_SETSEARCHFLAGS, flags);
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
posFind = (*_ppEditView)->searchInTarget(pText, stringSizeFind, startPosition, endPosition);
if (posFind == -1) //no match found in target, check if a new target should be used
@ -2928,7 +2981,6 @@ bool FindReplaceDlg::processFindNext(const wchar_t *txt2find, const FindOption *
//failed, or failed twice with wrap
if (pOptions->_incrementalType == NotIncremental) //incremental search doesn't trigger messages
{
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
wstring warningMsg = pNativeSpeaker->getLocalizedStrFromID("find-status-cannot-find", L"Find: Can't find the text \"$STR_REPLACE$\"");
wstring newTxt2find = stringReplace(txt2find, L"&", L"&&");
@ -2983,7 +3035,6 @@ bool FindReplaceDlg::processFindNext(const wchar_t *txt2find, const FindOption *
// Show a calltip for a zero length match
if (start == end)
{
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-regex-zero-length-match", L"zero length match");
msg = L"^ " + msg;
(*_ppEditView)->showCallTip(start, msg.c_str());
@ -3022,6 +3073,8 @@ bool FindReplaceDlg::processReplace(const wchar_t *txt2find, const wchar_t *txt2
FindStatus status;
moreMatches = processFindNext(txt2find, &replaceOptions, &status, FINDNEXTTYPE_FINDNEXTFORREPLACE);
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
if (moreMatches)
{
Sci_CharacterRangeFull nextFind = (*_ppEditView)->getSelection();
@ -3056,8 +3109,6 @@ bool FindReplaceDlg::processReplace(const wchar_t *txt2find, const wchar_t *txt2
}
(*_ppEditView)->execute(SCI_SETSEL, start + replacedLen, start + replacedLen);
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
NppParameters& nppParam = NppParameters::getInstance();
const NppGUI& nppGui = nppParam.getNppGUI();
@ -3098,7 +3149,6 @@ bool FindReplaceDlg::processReplace(const wchar_t *txt2find, const wchar_t *txt2
{
if (_statusbarTooltipMsg.empty()) // Tooltip message non-empty means there's a find problem - so we keep the message as it is and not erase it
{
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replace-not-found", L"Replace: no occurrence was found");
msg += L" ";

View File

@ -484,6 +484,8 @@ private:
std::vector<int> _reduce2hide_fip = { IDD_FINDINFILES_FILTERS_STATIC, IDD_FINDINFILES_FILTERS_COMBO, IDCANCEL };
std::vector<int> _reduce2hide_mark = { IDC_MARKLINE_CHECK, IDC_PURGE_CHECK, IDC_IN_SELECTION_CHECK, IDC_REPLACEINSELECTION, IDC_COPY_MARKED_TEXT, IDCANCEL };
ControlInfoTip _maxLenOnSearchTip;
void enableFindDlgItem(int dlgItemID, bool isEnable = true);
void showFindDlgItem(int dlgItemID, bool isShow = true);