Add Change History markers for saved/unsaved/undone modification

Implement Change History by using the new abilities of Scintilla v5.3.0.

Fix  #12164, close #12165
This commit is contained in:
Don Ho 2022-09-13 15:32:16 +02:00
parent 3295cc8d2c
commit fc32fbdcce
17 changed files with 112 additions and 29 deletions

View File

@ -964,6 +964,7 @@ The comments are here for explanation, it's not necessary to translate them.
<Item id="6292" name="Dynamic width"/>
<Item id="6293" name="Constant width"/>
<Item id="6207" name="Display bookmark"/>
<Item id="6223" name="Display Change History"/>
<Item id="6211" name="Vertical Edge Settings"/>
<Item id="6213" name="Background mode"/>
<Item id="6237" name="Add your column marker by indicating its position with a decimal number.
@ -1361,6 +1362,7 @@ Do you want to launch Notepad++ in Administrator mode?"/>
Notepad++ will be restarted after all the operations are terminated.
Continue?"/>
<NeedToRestartToLoadPlugins title="Notepad++ need to be relaunched" message="You have to restart Notepad++ to load plugins you installed."/> <!-- HowToReproduce: Import a plugin via menu "Settings->Import->Import Plugin(s)...". -->
<ChangeHistoryEnabledWarning title="Notepad++ need to be relaunched" message="You have to restart Notepad++ to enable Change History."/> <!-- HowToReproduce: uncheck "Display Change History" via Preferences dialog "Marges/Border/Edge. -->
</MessageBox>
<ClipboardHistory>
<PanelTitle name="Clipboard History"/>

View File

@ -961,6 +961,7 @@ The comments are here for explanation, it's not necessary to translate them.
<Item id="6292" name="Largeur dynamique"/>
<Item id="6293" name="Largeur constante"/>
<Item id="6207" name="Afficher la marge de signet"/>
<Item id="6223" name="Afficher l'Historique de Modification"/>
<Item id="6211" name="Marqueur de colonne"/>
<Item id="6213" name="Colorer larrière-plan"/>
<Item id="6237" name="Ajoutez votre marqueur de colonne en indiquant sa position avec un nombre entier.
@ -1356,6 +1357,7 @@ Voulez-vous lancer Notepad++ en mode &quot;Administrateur&quot; ?"/>
Notepad++ sera relancé après.
Continuer ?"/>
<NeedToRestartToLoadPlugins title="Notepad++ a besoin d'être redémarré" message="Vous devez redémarrer Notepad++ pour charger les modules d'extension installés."/> <!-- HowToReproduce: Import a plugin via menu "Settings->Import->Import Plugin(s)...". -->
<ChangeHistoryEnabledWarning title="Notepad++ a besoin d'être redémarré" message="Vous devez redémarrer Notepad++ pour activer Historique de Modification."/>
</MessageBox>
<ClipboardHistory>
<PanelTitle name="Historique du presse-papier"/>

View File

@ -952,6 +952,7 @@
<Item id="6292" name="動態寬度"/>
<Item id="6293" name="恆定寬度"/>
<Item id="6207" name="顯示標記"/>
<Item id="6223" name="顯示文件修改紀錄"/>
<Item id="6211" name="行界線設定"/>
<Item id="6213" name="背景色模式"/>
<Item id="6237" name="如欲添加列標記可以在下面以數字指示其列位置。
@ -1319,6 +1320,7 @@
在所有後續作業完成後Notepad++ 將重新啟動。
繼續嗎?"/>
<NeedToRestartToLoadPlugins title="重新啟動 Notepad++" message="你必須重新啟動 Notepad++ 才能載入已安裝的外掛模組。"/>
<ChangeHistoryEnabledWarning title="重新啟動 Notepad++" message="你必須重新啟動 Notepad++ 才能啟動文件修改紀錄。"/>
</MessageBox>
<ClipboardHistory>
<PanelTitle name="剪貼簿記錄"/>

View File

@ -250,8 +250,8 @@ LRESULT Notepad_plus::init(HWND hwnd)
// Configuration of 2 scintilla views
_mainEditView.showMargin(ScintillaEditView::_SC_MARGE_LINENUMBER, svp._lineNumberMarginShow);
_subEditView.showMargin(ScintillaEditView::_SC_MARGE_LINENUMBER, svp._lineNumberMarginShow);
_mainEditView.showMargin(ScintillaEditView::_SC_MARGE_SYBOLE, svp._bookMarkMarginShow);
_subEditView.showMargin(ScintillaEditView::_SC_MARGE_SYBOLE, svp._bookMarkMarginShow);
_mainEditView.showMargin(ScintillaEditView::_SC_MARGE_SYMBOL, svp._bookMarkMarginShow);
_subEditView.showMargin(ScintillaEditView::_SC_MARGE_SYMBOL, svp._bookMarkMarginShow);
_mainEditView.showIndentGuideLine(svp._indentGuideLineShow);
_subEditView.showIndentGuideLine(svp._indentGuideLineShow);

View File

@ -279,6 +279,8 @@ void Notepad_plus_Window::init(HINSTANCE hInst, HWND parent, const TCHAR *cmdLin
::SendMessage(_hSelf, NPPM_INTERNAL_CRLFFORMCHANGED, 0, 0);
::SendMessage(_hSelf, NPPM_INTERNAL_ENABLECHANGEHISTORY, 0, 0);
// Notify plugins that Notepad++ is ready
SCNotification scnN;
scnN.nmhdr.code = NPPN_READY;

View File

@ -2662,6 +2662,19 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
return TRUE;
}
case NPPM_INTERNAL_ENABLECHANGEHISTORY:
{
const ScintillaViewParams& svp = nppParam.getSVP();
int enabledCH = svp._isChangeHistoryEnabled ? (SC_CHANGE_HISTORY_ENABLED | SC_CHANGE_HISTORY_MARKERS) : SC_CHANGE_HISTORY_DISABLED;
_mainEditView.execute(SCI_SETCHANGEHISTORY, enabledCH);
_subEditView.execute(SCI_SETCHANGEHISTORY, enabledCH);
_mainEditView.showChangeHistoryMargin(svp._isChangeHistoryEnabled);
_subEditView.showChangeHistoryMargin(svp._isChangeHistoryEnabled);
return TRUE;
}
case NPPM_INTERNAL_CRLFLAUNCHSTYLECONF:
{
// Launch _configStyleDlg (create or display it)

View File

@ -2955,7 +2955,7 @@ void Notepad_plus::command(int id)
{
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
pNativeSpeaker->messageBox("NeedToRestartToLoadPlugins",
NULL,
_pPublicInterface->getHSelf(),
TEXT("You have to restart Notepad++ to load plugins you installed."),
TEXT("Notepad++ need to be relaunched"),
MB_OK | MB_APPLMODAL);
@ -3747,7 +3747,7 @@ void Notepad_plus::command(int id)
if (id == IDM_VIEW_LINENUMBER)
margin = ScintillaEditView::_SC_MARGE_LINENUMBER;
else //if (id == IDM_VIEW_SYMBOLMARGIN)
margin = ScintillaEditView::_SC_MARGE_SYBOLE;
margin = ScintillaEditView::_SC_MARGE_SYMBOL;
if (_mainEditView.hasMarginShowed(margin))
{

View File

@ -616,7 +616,7 @@ BOOL Notepad_plus::notify(SCNotification *notification)
_smartHighlighter.highlightView(_pEditView, unfocusView);
}
else if ((notification->margin == ScintillaEditView::_SC_MARGE_SYBOLE) && !notification->modifiers)
else if ((notification->margin == ScintillaEditView::_SC_MARGE_SYMBOL) && !notification->modifiers)
{
if (!_pEditView->markerMarginClick(lineClick))
bookmarkToggle(lineClick);
@ -631,7 +631,7 @@ BOOL Notepad_plus::notify(SCNotification *notification)
else if (notification->nmhdr.hwndFrom == _subEditView.getHSelf())
switchEditViewTo(SUB_VIEW);
if ((notification->margin == ScintillaEditView::_SC_MARGE_SYBOLE) && !notification->modifiers)
if ((notification->margin == ScintillaEditView::_SC_MARGE_SYMBOL) && !notification->modifiers)
{
POINT p;
::GetCursorPos(&p);

View File

@ -5607,13 +5607,28 @@ void NppParameters::feedScintillaParam(TiXmlNode *node)
nm = element->Attribute(TEXT("bookMarkMargin"));
if (nm)
{
if (!lstrcmp(nm, TEXT("show")))
_svp._bookMarkMarginShow = true;
else if (!lstrcmp(nm, TEXT("hide")))
_svp._bookMarkMarginShow = false;
}
// Bookmark Margin
nm = element->Attribute(TEXT("isChangeHistoryEnabled"));
if (nm)
{
if (!lstrcmp(nm, TEXT("yes")))
{
_svp._isChangeHistoryEnabled = true;
_svp._isChangeHistoryEnabled4NextSession = true;
}
else if (!lstrcmp(nm, TEXT("no")))
{
_svp._isChangeHistoryEnabled = false;
_svp._isChangeHistoryEnabled4NextSession = false;
}
}
// Indent GuideLine
nm = element->Attribute(TEXT("indentGuideLine"));
if (nm)
@ -6079,7 +6094,7 @@ bool NppParameters::writeScintillaParams()
(_svp._folderStyle == FOLDER_STYLE_CIRCLE)?TEXT("circle"):
(_svp._folderStyle == FOLDER_STYLE_NONE)?TEXT("none"):TEXT("box");
(scintNode->ToElement())->SetAttribute(TEXT("folderMarkStyle"), pFolderStyleStr);
(scintNode->ToElement())->SetAttribute(TEXT("isChangeHistoryEnabled"), _svp._isChangeHistoryEnabled4NextSession ? TEXT("yes") : TEXT("no"));
const TCHAR *pWrapMethodStr = (_svp._lineWrapMethod == LINEWRAP_ALIGNED)?TEXT("aligned"):
(_svp._lineWrapMethod == LINEWRAP_INDENT)?TEXT("indent"):TEXT("default");
(scintNode->ToElement())->SetAttribute(TEXT("lineWrapMethod"), pWrapMethodStr);

View File

@ -897,6 +897,8 @@ struct ScintillaViewParams
bool _lineNumberMarginShow = true;
bool _lineNumberMarginDynamicWidth = true;
bool _bookMarkMarginShow = true;
bool _isChangeHistoryEnabled = true;
bool _isChangeHistoryEnabled4NextSession = true;
folderStyle _folderStyle = FOLDER_STYLE_BOX; //"simple", "arrow", "circle", "box" and "none"
lineWrapMethod _lineWrapMethod = LINEWRAP_ALIGNED;
bool _foldMarginShow = true;

View File

@ -3066,7 +3066,7 @@ void FindReplaceDlg::findAllIn(InWhat op)
::GetWindowRect(_pFinder->getHSelf(), &findRect);
// overwrite some default settings
_pFinder->_scintView.showMargin(ScintillaEditView::_SC_MARGE_SYBOLE, false);
_pFinder->_scintView.showMargin(ScintillaEditView::_SC_MARGE_SYMBOL, false);
_pFinder->_scintView.setMakerStyle(FOLDER_STYLE_SIMPLE);
_pFinder->_scintView.display();
@ -3196,7 +3196,7 @@ Finder * FindReplaceDlg::createFinder()
::GetWindowRect(pFinder->getHSelf(), &findRect);
// overwrite some default settings
pFinder->_scintView.showMargin(ScintillaEditView::_SC_MARGE_SYBOLE, false);
pFinder->_scintView.showMargin(ScintillaEditView::_SC_MARGE_SYMBOL, false);
pFinder->_scintView.setMakerStyle(FOLDER_STYLE_SIMPLE);
pFinder->_scintView.display();

View File

@ -33,8 +33,9 @@ int ScintillaEditView::_refCount = 0;
UserDefineDialog ScintillaEditView::_userDefineDlg;
const int ScintillaEditView::_SC_MARGE_LINENUMBER = 0;
const int ScintillaEditView::_SC_MARGE_SYBOLE = 1;
const int ScintillaEditView::_SC_MARGE_FOLDER = 2;
const int ScintillaEditView::_SC_MARGE_SYMBOL = 1;
const int ScintillaEditView::_SC_MARGE_CHANGEHISTORY = 2;
const int ScintillaEditView::_SC_MARGE_FOLDER = 3;
WNDPROC ScintillaEditView::_scintillaDefaultProc = NULL;
string ScintillaEditView::_defaultCharList = "";
@ -154,10 +155,6 @@ LanguageNameInfo ScintillaEditView::_langNameInfoArray[L_EXTERNAL + 1] = {
{TEXT("ext"), TEXT("External"), TEXT("External"), L_EXTERNAL, "null"}
};
//const int MASK_RED = 0xFF0000;
//const int MASK_GREEN = 0x00FF00;
//const int MASK_BLUE = 0x0000FF;
int getNbDigits(int aNum, int base)
{
@ -229,7 +226,17 @@ void ScintillaEditView::init(HINSTANCE hInst, HWND hPere)
execute(SCI_SETMARGINMASKN, _SC_MARGE_FOLDER, SC_MASK_FOLDERS);
showMargin(_SC_MARGE_FOLDER, true);
execute(SCI_SETMARGINMASKN, _SC_MARGE_SYBOLE, (1<<MARK_BOOKMARK) | (1<<MARK_HIDELINESBEGIN) | (1<<MARK_HIDELINESEND) | (1<<MARK_HIDELINESUNDERLINE));
execute(SCI_SETMARGINMASKN, _SC_MARGE_SYMBOL, (1 << MARK_BOOKMARK) | (1 << MARK_HIDELINESBEGIN) | (1 << MARK_HIDELINESEND) | (1 << MARK_HIDELINESUNDERLINE));
execute(SCI_SETMARGINMASKN, _SC_MARGE_CHANGEHISTORY, (1 << SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN) | (1 << SC_MARKNUM_HISTORY_SAVED) | (1 << SC_MARKNUM_HISTORY_MODIFIED) | (1 << SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED));
COLORREF modifiedColor = RGB(255, 128, 0);
//COLORREF savedColor = RGB(0, 255, 0);
//COLORREF revertedToModifiedColor = RGB(255, 255, 0);
//COLORREF revertedToOriginColor = RGB(0, 0, 255);
execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_MODIFIED, modifiedColor);
//execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_SAVED, savedColor);
//execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_MODIFIED, revertedToModifiedColor);
//execute(SCI_MARKERSETBACK, SC_MARKNUM_HISTORY_REVERTED_TO_ORIGIN, revertedToOriginColor);
execute(SCI_MARKERSETALPHA, MARK_BOOKMARK, 70);
@ -253,10 +260,10 @@ void ScintillaEditView::init(HINSTANCE hInst, HWND hPere)
execute(SCI_MARKERDEFINERGBAIMAGE, MARK_HIDELINESEND, reinterpret_cast<LPARAM>(hidelines_end14));
}
execute(SCI_SETMARGINSENSITIVEN, _SC_MARGE_FOLDER, true);
execute(SCI_SETMARGINSENSITIVEN, _SC_MARGE_SYBOLE, true);
execute(SCI_SETMARGINSENSITIVEN, _SC_MARGE_FOLDER, true); // Make margin sensitive for getting notification on mouse click
execute(SCI_SETMARGINSENSITIVEN, _SC_MARGE_SYMBOL, true); // Make margin sensitive for getting notification on mouse click
execute(SCI_SETFOLDFLAGS, 16);
execute(SCI_SETFOLDFLAGS, SC_FOLDFLAG_LINEAFTER_CONTRACTED);
execute(SCI_SETSCROLLWIDTHTRACKING, true);
execute(SCI_SETSCROLLWIDTH, 1); //default empty document: override default width of 2000
@ -2001,6 +2008,11 @@ void ScintillaEditView::activateBuffer(BufferID buffer, bool force)
setCRLF();
NppParameters& nppParam = NppParameters::getInstance();
const ScintillaViewParams& svp = nppParam.getSVP();
int enabledCH = svp._isChangeHistoryEnabled ? (SC_CHANGE_HISTORY_ENABLED | SC_CHANGE_HISTORY_MARKERS) : SC_CHANGE_HISTORY_DISABLED;
execute(SCI_SETCHANGEHISTORY, enabledCH);
return; //all done
}
@ -2528,14 +2540,22 @@ void ScintillaEditView::showMargin(int whichMarge, bool willBeShowed)
{
DPIManager& dpiManager = NppParameters::getInstance()._dpiManager;
int width = dpiManager.scaleX(3);
if (whichMarge == _SC_MARGE_SYBOLE)
if (whichMarge == _SC_MARGE_SYMBOL)
width = dpiManager.scaleX(16);
else if (whichMarge == _SC_MARGE_FOLDER)
width = dpiManager.scaleX(14);
execute(SCI_SETMARGINWIDTHN, whichMarge, willBeShowed ? width : 0);
}
}
void ScintillaEditView::showChangeHistoryMargin(bool willBeShowed)
{
DPIManager& dpiManager = NppParameters::getInstance()._dpiManager;
int width = dpiManager.scaleX(9);
execute(SCI_SETMARGINWIDTHN, _SC_MARGE_CHANGEHISTORY, willBeShowed ? width : 0);
}
void ScintillaEditView::updateBeginEndSelectPosition(bool is_insert, size_t position, size_t length)
{
if (_beginSelectPosition != -1 && static_cast<intptr_t>(position) < _beginSelectPosition - 1)
@ -2708,8 +2728,8 @@ void ScintillaEditView::performGlobalStyles()
pStyle = stylers.findByName(TEXT("Bookmark margin"));
if (!pStyle)
{
pStyle = stylers.findByName(TEXT("Line number margin"));
if (pStyle)
pStyle = stylers.findByName(TEXT("Line number margin")); // "Line number margin" is used only for getting the bg color for _SC_MARGE_SYMBOL.
if (pStyle) // "Line number margin" has its own style (styleID="33") for setting its bg & fg color
{
bookmarkMarginColor = pStyle->_bgColor;
}
@ -2718,8 +2738,8 @@ void ScintillaEditView::performGlobalStyles()
{
bookmarkMarginColor = pStyle->_bgColor;
}
execute(SCI_SETMARGINTYPEN, _SC_MARGE_SYBOLE, SC_MARGIN_COLOUR);
execute(SCI_SETMARGINBACKN, _SC_MARGE_SYBOLE, bookmarkMarginColor);
execute(SCI_SETMARGINTYPEN, _SC_MARGE_SYMBOL, SC_MARGIN_COLOUR);
execute(SCI_SETMARGINBACKN, _SC_MARGE_SYMBOL, bookmarkMarginColor);
COLORREF urlHoveredFG = grey;
pStyle = stylers.findByName(TEXT("URL hovered"));

View File

@ -304,11 +304,12 @@ public:
//Marge member and method
static const int _SC_MARGE_LINENUMBER;
static const int _SC_MARGE_SYBOLE;
static const int _SC_MARGE_SYMBOL;
static const int _SC_MARGE_FOLDER;
//static const int _SC_MARGE_MODIFMARKER;
static const int _SC_MARGE_CHANGEHISTORY;
void showMargin(int whichMarge, bool willBeShowed = true);
void showChangeHistoryMargin(bool willBeShowed = true);
bool hasMarginShowed(int witchMarge) {
return (execute(SCI_GETMARGINWIDTHN, witchMarge, 0) != 0);

View File

@ -154,6 +154,7 @@ BEGIN
CONTROL "Display",IDC_CHECK_LINENUMBERMARGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,281,35,85,10
CONTROL "Dynamic width",IDC_RADIO_DYNAMIC,"Button",BS_AUTORADIOBUTTON | WS_GROUP,293,51,110,10
CONTROL "Constant width",IDC_RADIO_CONSTANT,"Button",BS_AUTORADIOBUTTON,293,65,108,10
CONTROL "Display Change History",IDC_CHECK_CHANGHISTORYMARGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,163,150,10
CONTROL "Display bookmark",IDC_CHECK_BOOKMARKMARGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,281,163,150,10
GROUPBOX "Padding",IDC_PADDING_STATIC,274,94,135,63,BS_CENTER
RTEXT "Left",IDC_PADDINGLEFT_STATIC,280,108,45,8

View File

@ -1578,7 +1578,7 @@ void MarginsBorderEdgeSubDlg::initScintParam()
::EnableWindow(::GetDlgItem(_hSelf, IDC_RADIO_CONSTANT), svp._lineNumberMarginShow);
::SendDlgItemMessage(_hSelf, IDC_CHECK_BOOKMARKMARGE, BM_SETCHECK, svp._bookMarkMarginShow, 0);
::SendDlgItemMessage(_hSelf, IDC_CHECK_CHANGHISTORYMARGE, BM_SETCHECK, svp._isChangeHistoryEnabled, 0);
::SendDlgItemMessage(_hSelf, IDC_CHECK_NOEDGE, BM_SETCHECK, !svp._showBorderEdge, 0);
bool canBeBg = svp._edgeMultiColumnPos.size() == 1;
@ -1731,6 +1731,28 @@ intptr_t CALLBACK MarginsBorderEdgeSubDlg::run_dlgProc(UINT message, WPARAM wPar
::SendMessage(_hParent, WM_COMMAND, IDM_VIEW_SYMBOLMARGIN, 0);
return TRUE;
case IDC_CHECK_CHANGHISTORYMARGE:
{
bool isChangeHistoryEnabled = (BST_CHECKED == ::SendDlgItemMessage(_hSelf, IDC_CHECK_CHANGHISTORYMARGE, BM_GETCHECK, 0, 0));
if (isChangeHistoryEnabled)
{
NativeLangSpeaker* pNativeSpeaker = nppParam.getNativeLangSpeaker();
pNativeSpeaker->messageBox("ChangeHistoryEnabledWarning",
_hSelf,
TEXT("You have to restart Notepad++ to enable Change History."),
TEXT("Notepad++ need to be relaunched"),
MB_OK | MB_APPLMODAL);
svp._isChangeHistoryEnabled4NextSession = true;
}
else
{
svp._isChangeHistoryEnabled = false;
svp._isChangeHistoryEnabled4NextSession = false;
::SendMessage(::GetParent(_hParent), NPPM_INTERNAL_ENABLECHANGEHISTORY, 0, 0);
}
return TRUE;
}
case IDC_CHECK_NOEDGE:
svp._showBorderEdge = !(BST_CHECKED == ::SendDlgItemMessage(_hSelf, IDC_CHECK_NOEDGE, BM_GETCHECK, 0, 0));
::SendMessage(::GetParent(_hParent), NPPM_SETEDITORBORDEREDGE, 0, svp._showBorderEdge ? TRUE : FALSE);

View File

@ -109,7 +109,7 @@
#define IDC_CARETBLINKRATE_SLIDER (IDD_PREFERENCE_SUB_EDITING + 20)
#define IDC_CARETBLINKRATE_F_STATIC (IDD_PREFERENCE_SUB_EDITING + 21)
#define IDC_CARETBLINKRATE_S_STATIC (IDD_PREFERENCE_SUB_EDITING + 22)
//#define IDC_CHECK_DOCCHANGESTATEMARGE (IDD_PREFERENCE_SUB_EDITING + 23)
#define IDC_CHECK_CHANGHISTORYMARGE (IDD_PREFERENCE_SUB_EDITING + 23)
#define IDC_DISTRACTIONFREE_SLIDER (IDD_PREFERENCE_SUB_EDITING + 24)
#define IDC_CHECK_MULTISELECTION (IDD_PREFERENCE_SUB_EDITING + 25)

View File

@ -640,6 +640,7 @@
#define NPPM_INTERNAL_CRLFFORMCHANGED (NOTEPADPLUS_USER_INTERNAL + 64)
#define NPPM_INTERNAL_CRLFLAUNCHSTYLECONF (NOTEPADPLUS_USER_INTERNAL + 65)
#define NPPM_INTERNAL_LAUNCHPREFERENCES (NOTEPADPLUS_USER_INTERNAL + 66)
#define NPPM_INTERNAL_ENABLECHANGEHISTORY (NOTEPADPLUS_USER_INTERNAL + 67)
// See Notepad_plus_msgs.h
//#define NOTEPADPLUS_USER (WM_USER + 1000)