From 69b001f36e1df9bfc80effd0e7fb5333335c2d87 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 18 Mar 2019 18:26:23 +0300 Subject: Notes & Reminders: - ANSI plugin became Unicode and therefore went under standard project templates; - globals.h renamed to stdafx.h, stdafx.cxx added to a project; - old pascal-style lists replaced with LIST<> templates; - massive code cleaning; - version bump --- plugins/NotesAndReminders/src/notes.cpp | 2434 +++++++++++++++---------------- 1 file changed, 1181 insertions(+), 1253 deletions(-) (limited to 'plugins/NotesAndReminders/src/notes.cpp') diff --git a/plugins/NotesAndReminders/src/notes.cpp b/plugins/NotesAndReminders/src/notes.cpp index 493d47c6ee..a958988b7d 100644 --- a/plugins/NotesAndReminders/src/notes.cpp +++ b/plugins/NotesAndReminders/src/notes.cpp @@ -1,10 +1,9 @@ -#include "globals.h" +#include "stdafx.h" #ifndef MONITOR_DEFAULTTONULL #define MONITOR_DEFAULTTONULL 0x00000000 #endif - // NotesData DB data params #define DATATAG_TEXT 1 // %s #define DATATAG_SCROLLPOS 2 // %u (specifies rich edit controls scroll post as first visible line) @@ -44,51 +43,80 @@ #define PENLINK ENLINK * -#define NOTE_WND_CLASS "MIM_StickyNote" - +#define NOTE_WND_CLASS L"MIM_StickyNote" #define IDM_COLORPRESET_BG 41000 #define IDM_COLORPRESET_FG 41100 - -static BOOL ListNotesVisible = FALSE; +static bool ListNotesVisible = FALSE; static HWND LV; - struct ColorPreset { - char *szName; + wchar_t *szName; COLORREF color; }; static struct ColorPreset clrPresets[] = { - { LPGEN("Black"), RGB(0,0,0) }, - { LPGEN("Maroon"), RGB(128,0,0) }, - { LPGEN("Green"), RGB(0,128,0) }, - { LPGEN("Olive"), RGB(128,128,0) }, - { LPGEN("Navy"), RGB(0,0,128) }, - { LPGEN("Purple"), RGB(128,0,128) }, - { LPGEN("Teal"), RGB(0,128,128) }, - { LPGEN("Gray"), RGB(128,128,128) }, - { LPGEN("Silver"), RGB(192,192,192) }, - { LPGEN("Red"), RGB(255,0,0) }, - { LPGEN("Orange"), RGB(255,155,0) }, - { LPGEN("Lime"), RGB(0,255,0) }, - { LPGEN("Yellow"), RGB(255,255,0) }, - { LPGEN("Blue"), RGB(0,0,255) }, - { LPGEN("Fuchsia"), RGB(255,0,255) }, - { LPGEN("Aqua"), RGB(0,255,255) }, - { LPGEN("White"), RGB(255,255,255) } + { LPGENW("Black"), RGB(0,0,0) }, + { LPGENW("Maroon"), RGB(128,0,0) }, + { LPGENW("Green"), RGB(0,128,0) }, + { LPGENW("Olive"), RGB(128,128,0) }, + { LPGENW("Navy"), RGB(0,0,128) }, + { LPGENW("Purple"), RGB(128,0,128) }, + { LPGENW("Teal"), RGB(0,128,128) }, + { LPGENW("Gray"), RGB(128,128,128) }, + { LPGENW("Silver"), RGB(192,192,192) }, + { LPGENW("Red"), RGB(255,0,0) }, + { LPGENW("Orange"), RGB(255,155,0) }, + { LPGENW("Lime"), RGB(0,255,0) }, + { LPGENW("Yellow"), RGB(255,255,0) }, + { LPGENW("Blue"), RGB(0,0,255) }, + { LPGENW("Fuchsia"), RGB(255,0,255) }, + { LPGENW("Aqua"), RGB(0,255,255) }, + { LPGENW("White"), RGB(255,255,255) } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +struct STICKYNOTEFONT : public MZeroedObject +{ + HFONT hFont; + char size; + BYTE style; // see the DBFONTF_* flags + BYTE charset; + char szFace[LF_FACESIZE]; }; -TREEELEMENT *g_Stickies = nullptr; +struct STICKYNOTE : public MZeroedObject +{ + HWND SNHwnd, REHwnd; + BOOL bVisible, bOnTop; + char *data; + ULARGE_INTEGER ID; // FILETIME in UTC + wchar_t *title; + BOOL CustomTitle; + DWORD BgColor; // custom bg color override (only valid if non-zero) + DWORD FgColor; // custom fg/text color override (only valid if non-zero) + STICKYNOTEFONT *pCustomFont;// custom (body) font override (NULL if default font is used) + + ~STICKYNOTE() + { + if (SNHwnd) + DestroyWindow(SNHwnd); + SAFE_FREE((void**)&title); + SAFE_FREE((void**)&data); + if (pCustomFont) { + DeleteObject(pCustomFont->hFont); + free(pCustomFont); + } + } +}; -LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam); +static OBJLIST g_arStickies(1, PtrKeySortT); -INT_PTR PluginMenuCommandAddNew(WPARAM w, LPARAM l); -INT_PTR PluginMenuCommandDeleteAll(WPARAM w, LPARAM l); -void GetTriggerTimeString(const ULARGE_INTEGER *When, char *s, UINT strSize, BOOL bUtc); +void GetTriggerTimeString(const ULARGE_INTEGER *When, wchar_t *s, size_t strSize, BOOL bUtc); void OnListResize(HWND Dialog); void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols); void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal); @@ -102,17 +130,15 @@ COLORREF GetCaptionColor(COLORREF bodyClr) return (COLORREF)(r | g | b); } - static void EnsureUniqueID(STICKYNOTE *TSN) { - if (!g_Stickies) + if (!g_arStickies.getCount()) return; try_next: - // check existing notes if id is in use - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - if (((STICKYNOTE*)TTE->ptrdata)->ID.QuadPart == TSN->ID.QuadPart) { + for (auto &it : g_arStickies) { + if (it->ID.QuadPart == TSN->ID.QuadPart) { // id in use, try new (increases the ID/time stamp by 100 nanosecond steps until an unused time is found, // allthough it's very unlikely that there will be duplicated id's it's better to make 100% sure) TSN->ID.QuadPart++; @@ -124,7 +150,7 @@ try_next: static void InitNoteTitle(STICKYNOTE *TSN) { if (g_NoteTitleDate) { - char TempStr[MAX_PATH]; + wchar_t TempStr[MAX_PATH]; SYSTEMTIME tm; LCID lc = GetUserDefaultLCID(); @@ -133,24 +159,24 @@ static void InitNoteTitle(STICKYNOTE *TSN) memset(&tm, 0, sizeof(tm)); FileTimeToTzLocalST((FILETIME*)&TSN->ID, &tm); - if (GetDateFormat(lc, 0, &tm, GetDateFormatStr(), TempStr, MAX_PATH)) { + if (GetDateFormatW(lc, 0, &tm, GetDateFormatStr(), TempStr, MAX_PATH)) { // append time if requested if (g_NoteTitleTime) { - int n = (int)mir_strlen(TempStr); + int n = (int)mir_wstrlen(TempStr); TempStr[n++] = ' '; TempStr[n] = 0; GetTimeFormat(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 0), 0, &tm, GetTimeFormatStr(), TempStr + n, MAX_PATH - n); } - TSN->title = _strdup(TempStr); + TSN->title = _wcsdup(TempStr); } } TSN->CustomTitle = FALSE; } -static void InitStickyNoteLogFont(STICKYNOTEFONT *pCustomFont, LOGFONT *lf) +static void InitStickyNoteLogFont(STICKYNOTEFONT *pCustomFont, LOGFONTA *lf) { if (!pCustomFont->size) { SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE); @@ -177,9 +203,9 @@ static void InitStickyNoteLogFont(STICKYNOTEFONT *pCustomFont, LOGFONT *lf) lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; } -static BOOL CreateStickyNoteFont(STICKYNOTEFONT *pCustomFont, LOGFONT *plf) +static BOOL CreateStickyNoteFont(STICKYNOTEFONT *pCustomFont, LOGFONTA *plf) { - LOGFONT lf = { 0 }; + LOGFONTA lf = {0}; if (!plf) { InitStickyNoteLogFont(pCustomFont, &lf); @@ -189,1383 +215,1282 @@ static BOOL CreateStickyNoteFont(STICKYNOTEFONT *pCustomFont, LOGFONT *plf) if (pCustomFont->hFont) DeleteObject(pCustomFont->hFont); - pCustomFont->hFont = CreateFontIndirect(plf); + pCustomFont->hFont = CreateFontIndirectA(plf); return pCustomFont->hFont != nullptr; } - -STICKYNOTE* NewNoteEx(int Ax, int Ay, int Aw, int Ah, char *Data, ULARGE_INTEGER *ID, BOOL bVisible, BOOL bOnTop, int scrollV, COLORREF bgClr, COLORREF fgClr, char *Title, STICKYNOTEFONT *pCustomFont, BOOL bLoading) +void CloseNotesList() { - WNDCLASSEX TWC = { 0 }; - WINDOWPLACEMENT TWP; - DWORD L1, L2; - SYSTEMTIME tm; - - const BOOL bIsStartup = bVisible & 0x10000; - bVisible &= ~0x10000; + if (ListNotesVisible) { + DestroyWindow(LV); + ListNotesVisible = FALSE; + } +} - char *TData = Data; +void PurgeNotes(void) +{ + char ValueName[16]; - if (!GetClassInfoEx(hmiranda, NOTE_WND_CLASS, &TWC)) { - TWC.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; - TWC.cbClsExtra = 0; - TWC.cbWndExtra = 0; - TWC.hInstance = hmiranda; - TWC.hIcon = LoadIcon(nullptr, IDI_APPLICATION); - TWC.hCursor = LoadCursor(nullptr, IDC_ARROW); - TWC.hbrBackground = nullptr; - TWC.lpszMenuName = nullptr; - TWC.lpszClassName = NOTE_WND_CLASS; - TWC.cbSize = sizeof(WNDCLASSEX); - TWC.lpfnWndProc = StickyNoteWndProc; - if (!RegisterClassEx(&TWC)) return nullptr; + int NotesCount = g_plugin.getDword("NotesData", 0); + for (int i = 0; i < NotesCount; i++) { + mir_snprintf(ValueName, "NotesData%d", i); + g_plugin.delSetting(ValueName); } +} - if (!TData || Aw < 0 || Ah < 0) { - TWP.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(GetDesktopWindow(), &TWP); - Aw = g_NoteWidth; Ah = g_NoteHeight; - Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2); - Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 2) - (Ah / 2); - } +void DeleteNotes(void) +{ + PurgeNotes(); + g_plugin.setDword("NotesData", 0); + g_arStickies.destroy(); + NOTIFY_LIST(); +} - STICKYNOTE *TSN = (STICKYNOTE*)malloc(sizeof(STICKYNOTE)); +void BringAllNotesToFront(STICKYNOTE *pActive) +{ + if (!g_arStickies.getCount()) + return; - if (ID) { - TSN->ID = *ID; + // NOTE: for some reason there are issues when bringing to top through hotkey while another app (like Explorer) + // is active, it refuses to move notes to top like it should with HWND_TOP. as a workaround still doesn't + // work 100% of the time, but at least more often, we first move not to top-most then for non-always-on-top + // notes we demote them back as a non top-most window + for (auto &SN : g_arStickies) { + if (SN->bVisible && pActive != SN) { + SetWindowPos(SN->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (!SN->bOnTop) + SetWindowPos(SN->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } } - else { - GetSystemTime(&tm); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&TSN->ID); + + if (pActive) { + SetWindowPos(pActive->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (!pActive->bOnTop) + SetWindowPos(pActive->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } +} - EnsureUniqueID(TSN); +// pModified optionally points to the modified note that invoked the JustSaveNotesEx call +static void JustSaveNotesEx(STICKYNOTE *pModified = nullptr) +{ + int i = 0, NotesCount = g_arStickies.getCount(); + char ValueName[32]; - TreeAdd(&g_Stickies, TSN); + const int OldNotesCount = g_plugin.getDword("NotesData", 0); - if (!TData) { - TData = _strdup(""); - TSN->data = TData; - } - else - TSN->data = TData; + g_plugin.setDword("NotesData", NotesCount); - // init note title (time-stamp) - if (Title) { - TSN->title = Title; - TSN->CustomTitle = TRUE; - } - else { - TSN->title = nullptr; - InitNoteTitle(TSN); - } + for (auto &pNote : g_arStickies) { + BOOL bDeleteTData = TRUE; + int scrollV = 0; + char *tData = nullptr; - TSN->bVisible = bVisible; - TSN->bOnTop = bOnTop; + // window pos and size + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(pNote->SNHwnd, &wp); + int TX = wp.rcNormalPosition.left; + int TY = wp.rcNormalPosition.top; + int TW = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + int TH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; - TSN->BgColor = bgClr; - TSN->FgColor = fgClr; + // set flags + DWORD flags = 0; + if (pNote->bVisible) flags |= 1; + if (pNote->bOnTop) flags |= 2; - TSN->pCustomFont = pCustomFont; + // get note text + int SzT = GetWindowTextLength(pNote->REHwnd); + if (SzT) { // TODO: change to support unicode and rtf, use EM_STREAMOUT + if (SzT > MAX_NOTE_LEN) + SzT = MAX_NOTE_LEN; // we want to be far below the 64k limit + tData = (char*)malloc(SzT + 1); + if (tData) + GetWindowTextA(pNote->REHwnd, tData, SzT + 1); + } - L1 = WS_EX_TOOLWINDOW; - if (g_Transparency < 255) L1 |= WS_EX_LAYERED; - if (bOnTop) L1 |= WS_EX_TOPMOST; + if (pNote == pModified) { + // update the data of the modified note + if (pNote->data) + free(pNote->data); + pNote->data = tData ? tData : _strdup(""); + bDeleteTData = FALSE; + } - L2 = WS_POPUP | WS_THICKFRAME | WS_CAPTION; + if (!tData) // empty note + SzT = 0; + else // get current scroll position + scrollV = SendMessage(pNote->REHwnd, EM_GETFIRSTVISIBLELINE, 0, 0); - // NOTE: loaded note positions stem from GetWindowPlacement, which normally have a different coord space than - // CreateWindow/SetWindowPos, BUT since we now use WS_EX_TOOLWINDOW they use the same coord space so - // we don't have to worry about notes "drifting" between sessions - TSN->SNHwnd = CreateWindowEx(L1, NOTE_WND_CLASS, "StickyNote", L2, Ax, Ay, Aw, Ah, nullptr, nullptr, hmiranda, TSN); + // data header + CMStringA szValue; + szValue.AppendFormat("X%I64x:%d:%d:%d:%d:%x", pNote->ID.QuadPart, TX, TY, TW, TH, flags); - if (g_Transparency < 255) - SetLayeredWindowAttributes(TSN->SNHwnd, 0, (BYTE)g_Transparency, LWA_ALPHA); + // scroll pos + if (scrollV > 0) + szValue.AppendFormat("\033""%u:%u", DATATAG_SCROLLPOS, (UINT)scrollV); - // ensure that window is not placed off-screen (if previous session had different monitor count or resolution) - // NOTE: SetWindowPlacement should do this, but it's extremly flakey - if (Data) { - if (!MonitorFromWindow(TSN->SNHwnd, MONITOR_DEFAULTTONULL)) { - TWP.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(GetDesktopWindow(), &TWP); + // custom bg color + if (pNote->BgColor) + szValue.AppendFormat("\033""%u:%x", DATATAG_BGCOL, (UINT)(pNote->BgColor & 0xffffff)); - if (Aw > 500) Aw = 500; - if (Ay < TWP.rcNormalPosition.left + 10 || Ax > TWP.rcNormalPosition.right - 120) - Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2) + (rand() & 0x3f); - if (Ay < TWP.rcNormalPosition.top + 50 || Ay > TWP.rcNormalPosition.bottom - 50) - Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 4) + (rand() & 0x1f); + // custom fg color + if (pNote->FgColor) + szValue.AppendFormat("\033""%u:%x", DATATAG_FGCOL, (UINT)(pNote->FgColor & 0xffffff)); - SetWindowPos(TSN->SNHwnd, nullptr, Ax, Ay, Aw, Ah, SWP_NOZORDER | SWP_NOACTIVATE); + if (pNote->pCustomFont) { + szValue.AppendFormat("\033""%u:%d:%u:%u:%s", DATATAG_FONT, + (int)pNote->pCustomFont->size, (UINT)pNote->pCustomFont->style, (UINT)pNote->pCustomFont->charset, + pNote->pCustomFont->szFace); } - } - if (bVisible) { - ShowWindow(TSN->SNHwnd, SW_SHOWNA); + // custom title + if (pNote->CustomTitle && pNote->title) + szValue.AppendFormat("\033""%u:%s", DATATAG_TITLE, pNote->title); - // when loading notes (only at startup), place all non-top notes at the bottom so they don't cover other windows - if (Data && !bOnTop && bIsStartup) - SetWindowPos(TSN->SNHwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS); - } + // note text (ALWAYS PUT THIS PARAM LAST) + if (tData) + szValue.AppendFormat("\033""%u:%s", DATATAG_TEXT, tData); - if (scrollV) - SendMessage(TSN->REHwnd, EM_LINESCROLL, 0, scrollV); + mir_snprintf(ValueName, "NotesData%d", i++); // we do not reverse notes in DB + db_set_blob(0, MODULENAME, ValueName, szValue.GetBuffer(), szValue.GetLength() + 1); - // make sure that any event triggered by init doesn't cause a meaningless save - KillTimer(TSN->SNHwnd, 1025); + if (bDeleteTData) + SAFE_FREE((void**)&tData); - if (!bLoading) { - NOTIFY_LIST(); + // make no save is queued for the note + if (pNote->SNHwnd) + KillTimer(pNote->SNHwnd, 1025); } - return TSN; + // delete any left over DB note entries + for (; i < OldNotesCount; i++) { + mir_snprintf(ValueName, "NotesData%d", i); + g_plugin.delSetting(ValueName); + } + + NOTIFY_LIST(); } -STICKYNOTE* NewNote(int Ax, int Ay, int Aw, int Ah, char *Data, ULARGE_INTEGER *ID, BOOL bVisible, BOOL bOnTop, int scrollV) +void OnDeleteNote(HWND hdlg, STICKYNOTE *SN) { - return NewNoteEx(Ax, Ay, Aw, Ah, Data, ID, bVisible, bOnTop, scrollV, 0, 0, nullptr, nullptr, FALSE); + if (MessageBoxW(hdlg, TranslateT("Are you sure you want to delete this note?"), TranslateT(SECTIONNAME), MB_OKCANCEL) == IDOK) { + g_arStickies.remove(SN); + JustSaveNotesEx(); + NOTIFY_LIST(); + } } -void LoadNotes(BOOL bIsStartup) +void ShowHideNotes(void) { - WORD Size = 0; - char *Value = nullptr, *TVal = nullptr; - char ValueName[32]; + if (!g_arStickies.getCount()) + return; - g_Stickies = nullptr; + // if some notes are hidden but others visible then first make all visible + // only toggle vis state if all are hidden or all are visible - int NotesCount = g_plugin.getDword("NotesData", 0); + UINT nHideCount = 0, nVisCount = 0; - for (int I = 0; I < NotesCount; I++) { - char *DelPos; + for (auto &SN : g_arStickies) { + if (SN->bVisible) + nVisCount++; + else + nHideCount++; + } - mir_snprintf(ValueName, "NotesData%d", I); + bool bVisible; + if (!nVisCount) + bVisible = true; + else if (!nHideCount) + bVisible = false; + else + bVisible = true; - if (Value) { - FreeSettingBlob(Size, Value); - Value = nullptr; + int bShow = bVisible ? SW_SHOWNA : SW_HIDE; + for (auto &SN : g_arStickies) { + if ((!bVisible) != (!SN->bVisible)) { + ShowWindow(SN->SNHwnd, bShow); + SN->bVisible = bVisible; } + } - Size = 65535; // does not get used + JustSaveNotesEx(); +} - ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value); +void SaveNotes(void) +{ + JustSaveNotesEx(); + g_arStickies.destroy(); +} - if (!Size || !Value) - continue; // the setting could not be read from DB -> skip +///////////////////////////////////////////////////////////////////////////////////////// +// Note Window - if (Value[0] == 'X') { - // new eXtended/fleXible data format +static int FindMenuItem(HMENU h, LPSTR lpszName) +{ + int n = GetMenuItemCount(h); + if (n <= 0) + return -1; - STICKYNOTE note = {}; - int i, rect[4]; - int scrollV = 0; - STICKYNOTEFONT *pCustomFont = nullptr; - DWORD flags; + // searches for a menu item based on name (used to avoid hardcoding item indices for sub-menus) + for (int i = 0; i < n; i++) { + char s[128]; - DelPos = strchr(Value + 1, 0x1B); - if (DelPos) - *DelPos = 0; + if (GetMenuStringA(h, i, s, 128, MF_BYPOSITION)) + if (!mir_strcmp(s, lpszName)) + return (int)i; + } - // id:x:y:w:h:flags + return -1; +} - TVal = strchr(Value + 1, ':'); - if (!TVal || (DelPos && TVal > DelPos)) - continue; - *TVal++ = 0; +static BOOL DoContextMenu(HWND AhWnd, WPARAM, LPARAM lParam) +{ + STICKYNOTE *SN = (STICKYNOTE*)GetPropA(AhWnd, "ctrldata"); - note.ID.QuadPart = _strtoui64(Value + 1, nullptr, 16); + HMENU hMenuLoad, FhMenu, hSub; + hMenuLoad = LoadMenuA(g_plugin.getInst(), "MNU_NOTEPOPUP"); + FhMenu = GetSubMenu(hMenuLoad, 0); - for (i = 0; i<4; i++) { - char *sep = strchr(TVal, ':'); - if (!sep || (DelPos && sep > DelPos)) - goto skip; - *sep++ = 0; + if (SN->bOnTop) + CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED | MF_BYCOMMAND); - rect[i] = strtol(TVal, nullptr, 10); + EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_PASTETITLE, MF_BYCOMMAND | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED)); - TVal = sep; - } - - flags = strtoul(TVal, nullptr, 16); - - if (flags & 1) - note.bVisible = TRUE; - if (flags & 2) - note.bOnTop = TRUE; - - // optional \033 separated params - while (DelPos) { - char *sep; - UINT tag; - - TVal = DelPos + 1; - // find param end and make sure it's null-terminated (if end of data then it's already null-terminated) - DelPos = strchr(TVal, 0x1B); - if (DelPos) - *DelPos = 0; - - // tag: - - sep = strchr(TVal, ':'); - if (!sep || (DelPos && sep > DelPos)) - goto skip; - - tag = strtoul(TVal, nullptr, 10); - TVal = sep + 1; - - switch (tag) { - case DATATAG_TEXT: - note.data = _strdup(TVal); - break; - - case DATATAG_SCROLLPOS: - scrollV = (int)strtoul(TVal, nullptr, 10); - break; - - case DATATAG_BGCOL: - note.BgColor = strtoul(TVal, nullptr, 16) | 0xff000000; - break; - - case DATATAG_FGCOL: - note.FgColor = strtoul(TVal, nullptr, 16) | 0xff000000; - break; - - case DATATAG_TITLE: - if (mir_strlen(TVal) > MAX_TITLE_LEN) - TVal[MAX_TITLE_LEN] = 0; - note.title = _strdup(TVal); - note.CustomTitle = TRUE; - break; - - case DATATAG_FONT: - { - int fsize; - UINT fstyle, fcharset; - - char *TVal2 = TVal; - sep = strchr(TVal2, ':'); - if (!sep || (DelPos && sep > DelPos)) - goto skip; - *sep++ = 0; - fsize = strtol(TVal2, nullptr, 10); - TVal2 = sep; - - sep = strchr(TVal2, ':'); - if (!sep || (DelPos && sep > DelPos)) - goto skip; - *sep++ = 0; - fstyle = strtoul(TVal2, nullptr, 10); - TVal2 = sep; - - sep = strchr(TVal2, ':'); - if (!sep || (DelPos && sep > DelPos)) - goto skip; - *sep++ = 0; - fcharset = strtoul(TVal2, nullptr, 10); - TVal2 = sep; - - if (TVal2 >= DelPos) - goto skip; - - pCustomFont = (STICKYNOTEFONT*)malloc(sizeof(STICKYNOTEFONT)); - pCustomFont->size = (char)fsize; - pCustomFont->style = (BYTE)fstyle; - pCustomFont->charset = (BYTE)fcharset; - mir_strcpy(pCustomFont->szFace, TVal2); - pCustomFont->hFont = nullptr; - - if (!CreateStickyNoteFont(pCustomFont, nullptr)) { - free(pCustomFont); - pCustomFont = nullptr; - } - } - break; - } - } - - if (!note.data) - note.data = _strdup(""); + if (!SN->CustomTitle) + EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_RESETTITLE, MF_BYCOMMAND | MF_GRAYED); - note.bVisible = note.bVisible && (!bIsStartup || g_ShowNotesAtStart); - if (bIsStartup) - note.bVisible |= 0x10000; + // NOTE: names used for FindMenuItem would need to include & chars if such shortcuts are added to the menus - NewNoteEx(rect[0], rect[1], rect[2], rect[3], note.data, ¬e.ID, note.bVisible, note.bOnTop, scrollV, note.BgColor, note.FgColor, note.title, pCustomFont, TRUE); - } - else { - // old format (for DB backward compatibility) + int n = FindMenuItem(FhMenu, "Appearance"); + if (n >= 0 && (hSub = GetSubMenu(FhMenu, n))) { + HMENU hBg = GetSubMenu(hSub, FindMenuItem(hSub, "Background Color")); + HMENU hFg = GetSubMenu(hSub, FindMenuItem(hSub, "Text Color")); - int Tx, Ty, Tw, Th, TV, OT; - BOOL V; - char *Data, *ID; - ULARGE_INTEGER newid; + for (int i = 0; i < _countof(clrPresets); i++) + InsertMenu(hBg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_BG + i, TranslateW(clrPresets[i].szName)); - OT = 1; TV = 1; - Tx = 100; Ty = 100; - Tw = 179; Th = 35; - Data = nullptr; ID = nullptr; + for (int i = 0; i < _countof(clrPresets); i++) + InsertMenu(hFg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_FG + i, TranslateW(clrPresets[i].szName)); + } - if (DelPos = strchr(Value, 0x1B)) { // get first delimiter - // int PartLen = DelPos - TVal; + TranslateMenu(FhMenu); + TrackPopupMenu(FhMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, LOWORD(lParam), HIWORD(lParam), 0, AhWnd, nullptr); + DestroyMenu(hMenuLoad); + return TRUE; +} - Data = nullptr; - ID = nullptr; - TVal = Value; - DelPos[0] = 0x0; - Tx = strtol(TVal, nullptr, 10); +static void MeasureColorPresetMenuItem(HWND hdlg, LPMEASUREITEMSTRUCT lpMeasureItem, struct ColorPreset *clrPresets2) +{ + HDC hdc = GetDC(hdlg); + wchar_t *lpsz = TranslateW(clrPresets2->szName); + SIZE sz; + GetTextExtentPoint32(hdc, lpsz, (int)mir_wstrlen(lpsz), &sz); + ReleaseDC(hdlg, hdc); - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Ty = strtol(TVal, nullptr, 10); + lpMeasureItem->itemWidth = 50 + sz.cx; + lpMeasureItem->itemHeight = (sz.cy + 2) > 18 ? sz.cy + 2 : 18; +} - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Tw = strtol(TVal, nullptr, 10); +static void PaintColorPresetMenuItem(LPDRAWITEMSTRUCT lpDrawItem, struct ColorPreset *clrPresets2) +{ + // UINT n = lpDrawItem->itemID - IDM_COLORPRESET_BG; + RECT rect; + rect.left = lpDrawItem->rcItem.left + 50; + rect.top = lpDrawItem->rcItem.top; + rect.right = lpDrawItem->rcItem.right; + rect.bottom = lpDrawItem->rcItem.bottom; - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Th = strtol(TVal, nullptr, 10); + if (lpDrawItem->itemState & ODS_SELECTED) { + SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUHILIGHT)); + FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH)); - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - TV = strtol(TVal, nullptr, 10); + SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } + else { + SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENU)); + FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH)); - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - OT = strtol(TVal, nullptr, 10); + SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUTEXT)); + } - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Data = _strdup(TVal); + SetBkMode(lpDrawItem->hDC, TRANSPARENT); + DrawText(lpDrawItem->hDC, clrPresets2->szName, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER); - TVal = DelPos + 1; - ID = TVal; + int h = lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top; + rect.left = lpDrawItem->rcItem.left + 5; + rect.top = lpDrawItem->rcItem.top + ((h - 14) >> 1); + rect.right = rect.left + 40; + rect.bottom = rect.top + 14; - V = (BOOL)TV && (!bIsStartup || g_ShowNotesAtStart); + FrameRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); + rect.left++; rect.top++; + rect.right--; rect.bottom--; + SetDCBrushColor(lpDrawItem->hDC, clrPresets2->color); + FillRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH)); +} - if (bIsStartup) - V |= 0x10000; +static BOOL GetClipboardText_Title(wchar_t *pOut, int size) +{ + BOOL bResult = FALSE; - // convert old ID format to new - if (strchr(ID, '-')) { - // validate format (otherwise create new) - if (mir_strlen(ID) < 19 || ID[2] != '-' || ID[5] != '-' || ID[10] != ' ' || ID[13] != ':' || ID[16] != ':') { - ID = nullptr; - } - else { - SYSTEMTIME tm; + if (OpenClipboard(nullptr)) { + HANDLE hData = GetClipboardData(CF_UNICODETEXT); + wchar_t *buffer; - ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0; + if (hData && (buffer = (wchar_t*)GlobalLock(hData))) { + // trim initial white spaces + while (*buffer && iswspace(*buffer)) + buffer++; - memset(&tm, 0, sizeof(tm)); - tm.wDay = (WORD)strtoul(ID, nullptr, 10); - tm.wMonth = (WORD)strtoul(ID + 3, nullptr, 10); - tm.wYear = (WORD)strtoul(ID + 6, nullptr, 10); - tm.wHour = (WORD)strtoul(ID + 11, nullptr, 10); - tm.wMinute = (WORD)strtoul(ID + 14, nullptr, 10); - tm.wSecond = (WORD)strtoul(ID + 17, nullptr, 10); + size_t n = mir_wstrlen(buffer); + if (n >= size) + n = size - 1; + wcsncpy_s(pOut, size, buffer, _TRUNCATE); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&newid); - } + // end string on line break and convert tabs to spaces + wchar_t *p = pOut; + while (*p) { + if (*p == '\r' || *p == '\n') { + *p = 0; + n = mir_wstrlen(pOut); + break; } - else { - ID = nullptr; + else if (*p == '\t') { + *p = ' '; } - - NewNoteEx(Tx, Ty, Tw, Th, Data, ID ? &newid : nullptr, V, (BOOL)OT, 0, 0, 0, nullptr, nullptr, TRUE); + p++; } - } -skip:; - } - if (Value) - FreeSettingBlob(Size, Value); // we do not leak on bad setting + // trim trailing white spaces + rtrimw(pOut); + if (pOut[0]) + bResult = TRUE; - NOTIFY_LIST(); -} + GlobalUnlock(hData); + } -void CloseNotesList() -{ - if (ListNotesVisible) { - DestroyWindow(LV); - ListNotesVisible = FALSE; + CloseClipboard(); } -} -static void PurgeNotesTree() -{ - while (g_Stickies) { // empty whole tree - STICKYNOTE *pt = (STICKYNOTE*)g_Stickies->ptrdata; - if (pt->SNHwnd) DestroyWindow(pt->SNHwnd); - SAFE_FREE((void**)&pt->title); - SAFE_FREE((void**)&pt->data); - if (pt->pCustomFont) { - DeleteObject(pt->pCustomFont->hFont); - free(pt->pCustomFont); - } - TreeDelete(&g_Stickies, pt); - SAFE_FREE((void**)&pt); - } - g_Stickies = nullptr; + return bResult; } -void PurgeNotes(void) +static void SetNoteTextControl(STICKYNOTE *SN) { - char ValueName[16]; + CHARFORMAT CF = {0}; + CF.cbSize = sizeof(CHARFORMAT); + CF.dwMask = CFM_COLOR; + CF.crTextColor = SN->FgColor ? (SN->FgColor & 0xffffff) : BodyFontColor; + SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); - int NotesCount = g_plugin.getDword("NotesData", 0); - for (int I = 0; I < NotesCount; I++) { - mir_snprintf(ValueName, "NotesData%d", I); - g_plugin.delSetting(ValueName); - } + if (SN->data) // TODO: use EM_STREAMIN + SetWindowTextA(SN->REHwnd, SN->data); } -void DeleteNotes(void) -{ - PurgeNotes(); - g_plugin.setDword("NotesData", 0); - PurgeNotesTree(); - NOTIFY_LIST(); -} -void BringAllNotesToFront(STICKYNOTE *pActive) +static UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT msg, WPARAM, LPARAM) { - if (!g_Stickies) - return; - - // NOTE: for some reason there are issues when bringing to top through hotkey while another app (like Explorer) - // is active, it refuses to move notes to top like it should with HWND_TOP. as a workaround still doesn't - // work 100% of the time, but at least more often, we first move not to top-most then for non-always-on-top - // notes we demote them back as a non top-most window - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata; - - if (SN->bVisible && pActive != SN) { - SetWindowPos(SN->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - if (!SN->bOnTop) - SetWindowPos(SN->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } + if (msg == WM_INITDIALOG) { + // hide color selector + ShowWindow(GetDlgItem(hdlg, 0x443), SW_HIDE); + ShowWindow(GetDlgItem(hdlg, 0x473), SW_HIDE); + TranslateDialogDefault(hdlg); } - if (pActive) { - SetWindowPos(pActive->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - if (!pActive->bOnTop) - SetWindowPos(pActive->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } + return 0; } -// pModified optionally points to the modified note that invoked the JustSaveNotesEx call -static void JustSaveNotesEx(STICKYNOTE *pModified = nullptr) +LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { - int I = 0, NotesCount = TreeGetCount(g_Stickies); - int n, l; - char ValueName[32]; - WINDOWPLACEMENT wp; - int TX, TY, TW, TH; - DWORD flags; - int SzT; - int scrollV; - char *tData; + STICKYNOTE *SN = (STICKYNOTE*)GetPropA(hdlg, "ctrldata"); - const int OldNotesCount = g_plugin.getDword("NotesData", 0); - - g_plugin.setDword("NotesData", NotesCount); + switch (message) { + case WM_CLOSE: + return TRUE; - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next, I++) { - STICKYNOTE *pNote = (STICKYNOTE*)TTE->ptrdata; - BOOL bDeleteTData = TRUE; - scrollV = 0; - tData = nullptr; + case WM_SIZE: + RECT SZ; + GetClientRect(hdlg, &SZ); - // window pos and size - wp.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(pNote->SNHwnd, &wp); - TX = wp.rcNormalPosition.left; - TY = wp.rcNormalPosition.top; - TW = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - TH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + MoveWindow(GetDlgItem(hdlg, 1), 0, 0, SZ.right, SZ.bottom, TRUE); - // set flags - flags = 0; - if (pNote->bVisible) flags |= 1; - if (pNote->bOnTop) flags |= 2; + KillTimer(hdlg, 1025); + SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); + return TRUE; - // get note text - SzT = GetWindowTextLength(pNote->REHwnd); - if (SzT) { // TODO: change to support unicode and rtf, use EM_STREAMOUT - if (SzT > MAX_NOTE_LEN) - SzT = MAX_NOTE_LEN; // we want to be far below the 64k limit - tData = (char*)malloc(SzT + 1); - if (tData) - GetWindowText(pNote->REHwnd, tData, SzT + 1); + case WM_TIMER: + if (wParam == 1025) { + KillTimer(hdlg, 1025); + JustSaveNotesEx(SN); } + break; - if (pNote == pModified) { - // update the data of the modified note - if (pNote->data) - free(pNote->data); - pNote->data = tData ? tData : _strdup(""); - bDeleteTData = FALSE; - } + case WM_MOVE: + KillTimer(hdlg, 1025); + SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); + return TRUE; - if (!tData) // empty note - SzT = 0; - else // get current scroll position - scrollV = SendMessage(pNote->REHwnd, EM_GETFIRSTVISIBLELINE, 0, 0); + case WM_CREATE: + { + CREATESTRUCT *CS = (CREATESTRUCT *)lParam; + DWORD mystyle; - char *Value = (char*)malloc(SzT + 512); - if (!Value) { - if (bDeleteTData) - SAFE_FREE((void**)&tData); - continue; + SN = (STICKYNOTE*)CS->lpCreateParams; + SetPropA(hdlg, "ctrldata", (HANDLE)SN); + BringWindowToTop(hdlg); + mystyle = WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN; + if (g_plugin.bShowScrollbar) + mystyle |= WS_VSCROLL; + HWND H = CreateWindowW(MSFTEDIT_CLASS, nullptr, mystyle, 0, 0, CS->cx - 3 - 3, CS->cy - 3 - (3 + 14), hdlg, (HMENU)1, hmiranda, nullptr); + SN->REHwnd = H; + SendMessage(H, EM_SETTEXTMODE, TM_PLAINTEXT, 0); + SendMessage(H, EM_LIMITTEXT, MAX_NOTE_LEN, 0); + SendMessage(H, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), 1); + SendMessage(H, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_LINK); + SendMessage(H, EM_SETBKGNDCOLOR, 0, SN->BgColor ? (SN->BgColor & 0xffffff) : BodyColor); + SendMessage(H, EM_AUTOURLDETECT, 1, 0); + SetNoteTextControl(SN); } + return TRUE; - n = 0; - - // data header - l = sprintf(Value, "X%I64x:%d:%d:%d:%d:%x", pNote->ID.QuadPart, TX, TY, TW, TH, flags); //!!!!!!!!!!!! - if (l > 0) n += l; - - // scroll pos - if (scrollV > 0) { - l = sprintf(Value + n, "\033""%u:%u", DATATAG_SCROLLPOS, (UINT)scrollV); //!!!!!!!!!! - if (l > 0) n += l; + case WM_GETMINMAXINFO: + { + MINMAXINFO *mm = (MINMAXINFO*)lParam; + // min width accomodates frame, buttons and some extra space for sanity + mm->ptMinTrackSize.x = 48 + 3 + 3 + 8 + 40; + // min height allows collapsing entire client area, only leaving frame and caption + mm->ptMinTrackSize.y = 3 + 3 + 14; } + return 0; - // custom bg color - if (pNote->BgColor) { - l = sprintf(Value + n, "\033""%u:%x", DATATAG_BGCOL, (UINT)(pNote->BgColor & 0xffffff)); //!!!!!!!!!!!!! - if (l > 0) n += l; - } + case WM_ERASEBKGND: + // no BG needed as edit control takes up entire client area + return TRUE; - // custom fg color - if (pNote->FgColor) { - l = sprintf(Value + n, "\033""%u:%x", DATATAG_FGCOL, (UINT)(pNote->FgColor & 0xffffff)); //!!!!!!!!!!!!! - if (l > 0) n += l; - } + case WM_NCPAINT: + // make window borders have the same color as caption + { + RECT rect, wr, r; + HDC hdc = GetWindowDC(hdlg); - if (pNote->pCustomFont) { - l = sprintf(Value + n, "\033""%u:%d:%u:%u:%s", DATATAG_FONT, - (int)pNote->pCustomFont->size, (UINT)pNote->pCustomFont->style, (UINT)pNote->pCustomFont->charset, - pNote->pCustomFont->szFace); //!!!!!!!!!!!!!!! - if (l > 0) n += l; - } + GetWindowRect(hdlg, &wr); + if (wParam && wParam != 1) { + SelectClipRgn(hdc, (HRGN)wParam); + OffsetClipRgn(hdc, -wr.left, -wr.top); + } - // custom title - if (pNote->CustomTitle && pNote->title) { - l = sprintf(Value + n, "\033""%u:%s", DATATAG_TITLE, pNote->title); //!!!!!!!!!!!!! - if (l > 0) n += l; - } + rect = wr; + OffsetRect(&rect, -wr.left, -wr.top); - // note text (ALWAYS PUT THIS PARAM LAST) - if (tData) { - l = sprintf(Value + n, "\033""%u:%s", DATATAG_TEXT, tData); //!!!!!!!!!!!! - if (l > 0) n += l; - } + HBRUSH hBkBrush = (HBRUSH)GetStockObject(DC_BRUSH); + SetDCBrushColor(hdc, GetCaptionColor((SN && SN->BgColor) ? SN->BgColor : BodyColor)); - // clamp data size to WORD (including null terminator) - if (n >= 0xffff) { - // huston, we have a problem, strip some reminder text - n = 0xfffe; - Value[0xffff] = 0; - } + // draw all frame sides separately to avoid filling client area (which flickers) + { + // top + r.left = rect.left; r.right = rect.right; + r.top = rect.top; r.bottom = r.top + 3 + 14; + FillRect(hdc, &r, hBkBrush); + // bottom + r.top = rect.bottom - 3; r.bottom = rect.bottom; + FillRect(hdc, &r, hBkBrush); + // left + r.left = rect.left; r.right = r.left + 3; + r.top = rect.top + 3 + 14; r.bottom = rect.bottom - 3; + FillRect(hdc, &r, hBkBrush); + // right + r.left = rect.right - 3; r.right = rect.right; + FillRect(hdc, &r, hBkBrush); + } - mir_snprintf(ValueName, "NotesData%d", NotesCount - I - 1); // we do not reverse notes in DB + // paint title bar contents (time stamp and buttons) - db_set_blob(0, MODULENAME, ValueName, Value, n + 1); + if (SN && SN->title) { + RECT R; + SelectObject(hdc, hCaptionFont); + R.top = 3 + 1; R.bottom = 3 + 11; R.left = 3 + 2; R.right = rect.right - 3 - 1; + if (g_plugin.bShowNoteButtons) + R.right -= 48; - SAFE_FREE((void**)&Value); - if (bDeleteTData) - SAFE_FREE((void**)&tData); + SetTextColor(hdc, SN->FgColor ? (SN->FgColor & 0xffffff) : CaptionFontColor); + SetBkMode(hdc, TRANSPARENT); + DrawTextW(hdc, SN->title, -1, &R, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER); + } - // make no save is queued for the note - if (pNote->SNHwnd) - KillTimer(pNote->SNHwnd, 1025); - } + if (g_plugin.bShowNoteButtons) { + HICON hcIcon; + if (SN->bOnTop) + hcIcon = IcoLib_GetIconByHandle(iconList[4].hIcolib); + else + hcIcon = IcoLib_GetIconByHandle(iconList[7].hIcolib); + DrawIcon(hdc, wr.right - wr.left - 16, 0 + 3, hcIcon); + IcoLib_ReleaseIcon(hcIcon); - // delete any left over DB note entries - for (; I < OldNotesCount; I++) { - mir_snprintf(ValueName, "NotesData%d", I); - g_plugin.delSetting(ValueName); - } + hcIcon = IcoLib_GetIconByHandle(iconList[9].hIcolib); + DrawIcon(hdc, wr.right - wr.left - 32, 1 + 3, hcIcon); + IcoLib_ReleaseIcon(hcIcon); - NOTIFY_LIST(); -} + hcIcon = IcoLib_GetIconByHandle(iconList[8].hIcolib); + DrawIcon(hdc, wr.right - wr.left - 48, 1 + 3, hcIcon); + IcoLib_ReleaseIcon(hcIcon); + } -void OnDeleteNote(HWND hdlg, STICKYNOTE *SN) -{ - if (MessageBoxW(hdlg, TranslateT("Are you sure you want to delete this note?"), TranslateT(SECTIONNAME), MB_OKCANCEL) == IDOK) { - if (SN->SNHwnd) - DestroyWindow(SN->SNHwnd); - TreeDelete(&g_Stickies, SN); - SAFE_FREE((void**)&SN->data); - if (SN->pCustomFont) { - DeleteObject(SN->pCustomFont->hFont); - free(SN->pCustomFont); + if (wParam && wParam != 1) + SelectClipRgn(hdc, nullptr); + + ReleaseDC(hdlg, hdc); } - SAFE_FREE((void**)&SN); - JustSaveNotesEx(); - NOTIFY_LIST(); - } -} + return TRUE; -void ShowHideNotes(void) -{ - if (!g_Stickies) - return; + case WM_NCCALCSIZE: + { + RECT *pRect = wParam ? &((NCCALCSIZE_PARAMS*)lParam)->rgrc[0] : (RECT*)lParam; + pRect->bottom -= 3; + pRect->right -= 3; + pRect->left += 3; + pRect->top += 3 + 14; + } + return WVR_REDRAW; - // if some notes are hidden but others visible then first make all visible - // only toggle vis state if all are hidden or all are visible + case WM_NCACTIVATE: + // update window (so that parts that potentially became visible through activation get redrawn immediately) + RedrawWindow(hdlg, nullptr, nullptr, RDW_UPDATENOW); + return TRUE; - UINT nHideCount = 0, nVisCount = 0; + case WM_NOTIFY: + if (LOWORD(wParam) == 1) { + char *Buff; + PENLINK PEnLnk = (PENLINK)lParam; - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - if (((STICKYNOTE*)TTE->ptrdata)->bVisible) - nVisCount++; - else - nHideCount++; - } + if (PEnLnk->msg == WM_LBUTTONDOWN) { + SendDlgItemMessage(hdlg, 1, EM_EXSETSEL, 0, (LPARAM)&(PEnLnk->chrg)); + Buff = (char*)malloc(PEnLnk->chrg.cpMax - PEnLnk->chrg.cpMin + 1); + SendDlgItemMessage(hdlg, 1, EM_GETSELTEXT, 0, (LPARAM)Buff); + if ((GetAsyncKeyState(VK_CONTROL) >> 15) != 0) + ShellExecuteA(hdlg, "open", "iexplore", Buff, "", SW_SHOWNORMAL); + else if (g_lpszAltBrowser && *g_lpszAltBrowser) + ShellExecuteA(hdlg, "open", g_lpszAltBrowser, Buff, "", SW_SHOWNORMAL); + else + ShellExecuteA(hdlg, "open", Buff, "", "", SW_SHOWNORMAL); + SAFE_FREE((void**)&Buff); + return TRUE; + } + return FALSE; + } + break; - BOOL bVisible; - if (!nVisCount) - bVisible = TRUE; - else if (!nHideCount) - bVisible = FALSE; - else - bVisible = TRUE; + case WM_NCHITTEST: + { + int r = DefWindowProc(hdlg, message, wParam, lParam); + // filter out potential hits on windows default title bar buttons + switch (r) { + case HTSYSMENU: + case HTCLOSE: + case HTMINBUTTON: + case HTMAXBUTTON: + return HTCAPTION; + } + return r; + } - int bShow = bVisible ? SW_SHOWNA : SW_HIDE; + case WM_NCLBUTTONDOWN: + if (wParam == HTCAPTION && g_plugin.bShowNoteButtons) { + long X, Y; + RECT rect; + int Tw; + GetWindowRect(hdlg, &rect); + Tw = rect.right - rect.left; - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata; + X = LOWORD(lParam) - rect.left; + Y = HIWORD(lParam) - rect.top; - if ((!bVisible) != (!SN->bVisible)) { - ShowWindow(SN->SNHwnd, bShow); - SN->bVisible = bVisible; + if (X > Tw - 16) { + SendMessage(hdlg, WM_COMMAND, IDM_TOGGLEONTOP, 0); + return TRUE; + } + else if (X > Tw - 31 && X < Tw - 16) { + SendMessage(hdlg, WM_COMMAND, IDM_REMOVENOTE, 0); + return TRUE; + } + else if (X > Tw - 48 && X < Tw - 32) { + SendMessage(hdlg, WM_COMMAND, IDM_HIDENOTE, 0); + return TRUE; + } } - } + return DefWindowProc(hdlg, message, wParam, lParam); - JustSaveNotesEx(); -} + case WM_MEASUREITEM: + { + LPMEASUREITEMSTRUCT lpMeasureItem = (LPMEASUREITEMSTRUCT)lParam; -void SaveNotes(void) -{ - JustSaveNotesEx(); - PurgeNotesTree(); -} + if (lpMeasureItem->CtlType != ODT_MENU) + break; - -///////////////////////////////////////////////////////////////////// -// Note Window - -static int FindMenuItem(HMENU h, LPTSTR lpszName) -{ - int n = GetMenuItemCount(h); - - if (n <= 0) { - return -1; - } - - // searches for a menu item based on name (used to avoid hardcoding item indices for sub-menus) - for (UINT i = 0; i < (UINT)n; i++) { - char s[128]; - - if (GetMenuString(h, i, s, 128, MF_BYPOSITION)) { - if (!mir_strcmp(s, lpszName)) { - return (int)i; + if (lpMeasureItem->itemID >= IDM_COLORPRESET_BG && lpMeasureItem->itemID <= IDM_COLORPRESET_BG + _countof(clrPresets)) { + MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_BG)); + return TRUE; + } + else if (lpMeasureItem->itemID >= IDM_COLORPRESET_FG && lpMeasureItem->itemID <= IDM_COLORPRESET_FG + _countof(clrPresets)) { + MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_FG)); + return TRUE; } } - } - - return -1; -} + break; -static BOOL DoContextMenu(HWND AhWnd, WPARAM, LPARAM lParam) -{ - STICKYNOTE *SN = (STICKYNOTE*)GetProp(AhWnd, "ctrldata"); + case WM_DRAWITEM: + if (!wParam) { + LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)lParam; - HMENU hMenuLoad, FhMenu, hSub; - hMenuLoad = LoadMenu(g_plugin.getInst(), "MNU_NOTEPOPUP"); - FhMenu = GetSubMenu(hMenuLoad, 0); + if (lpDrawItem->CtlType != ODT_MENU) + break; - if (SN->bOnTop) - CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED | MF_BYCOMMAND); + if (lpDrawItem->itemID >= IDM_COLORPRESET_BG && lpDrawItem->itemID <= IDM_COLORPRESET_BG + _countof(clrPresets)) { + PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_BG)); + return TRUE; + } + else if (lpDrawItem->itemID >= IDM_COLORPRESET_FG && lpDrawItem->itemID <= IDM_COLORPRESET_FG + _countof(clrPresets)) { + PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_FG)); + return TRUE; + } + } + break; - EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_PASTETITLE, MF_BYCOMMAND | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED)); + case WM_COMMAND: + UINT id; + switch (HIWORD(wParam)) { + case EN_CHANGE: + case EN_VSCROLL: + case EN_HSCROLL: + KillTimer(hdlg, 1025); + SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); + break; + } - if (!SN->CustomTitle) - EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_RESETTITLE, MF_BYCOMMAND | MF_GRAYED); + id = (UINT)LOWORD(wParam); + if (id >= IDM_COLORPRESET_BG && id <= IDM_COLORPRESET_BG + _countof(clrPresets)) { + SN->BgColor = clrPresets[id - IDM_COLORPRESET_BG].color | 0xff000000; + SendMessage(SN->REHwnd, EM_SETBKGNDCOLOR, 0, SN->BgColor & 0xffffff); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + return FALSE; + } + else if (id >= IDM_COLORPRESET_FG && id <= IDM_COLORPRESET_FG + _countof(clrPresets)) { + CHARFORMAT CF = {0}; + SN->FgColor = clrPresets[id - IDM_COLORPRESET_FG].color | 0xff000000; + CF.cbSize = sizeof(CHARFORMAT); + CF.dwMask = CFM_COLOR; + CF.crTextColor = SN->FgColor & 0xffffff; + SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + return FALSE; + } - // NOTE: names used for FindMenuItem would need to include & chars if such shortcuts are added to the menus + switch (id) { + case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE: + PluginMenuCommandAddNew(0, 0); + break; - int n = FindMenuItem(FhMenu, "Appearance"); - if (n >= 0 && (hSub = GetSubMenu(FhMenu, n))) { - HMENU hBg = GetSubMenu(hSub, FindMenuItem(hSub, "Background Color")); - HMENU hFg = GetSubMenu(hSub, FindMenuItem(hSub, "Text Color")); + case ID_APPEARANCE_CUSTOMBG: + { + COLORREF custclr[16] = {0}; + CHOOSECOLOR cc = {0}; + COLORREF orgclr = SN->BgColor ? (COLORREF)(SN->BgColor & 0xffffff) : (COLORREF)(BodyColor & 0xffffff); + cc.lStructSize = sizeof(cc); + cc.hwndOwner = SN->SNHwnd; + cc.rgbResult = orgclr; + cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR; + cc.lpCustColors = custclr; + + if (ChooseColor(&cc) && cc.rgbResult != orgclr) { + SN->BgColor = cc.rgbResult | 0xff000000; + SendMessage(SN->REHwnd, EM_SETBKGNDCOLOR, 0, SN->BgColor & 0xffffff); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + } + } + break; + case ID_APPEARANCE_CUSTOMTEXT: + { + COLORREF custclr[16] = {0}; + CHOOSECOLOR cc = {0}; + COLORREF orgclr = SN->FgColor ? (COLORREF)(SN->FgColor & 0xffffff) : (COLORREF)(BodyFontColor & 0xffffff); + cc.lStructSize = sizeof(cc); + cc.hwndOwner = SN->SNHwnd; + cc.rgbResult = orgclr; + cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR; + cc.lpCustColors = custclr; + + if (ChooseColor(&cc) && cc.rgbResult != orgclr) { + CHARFORMAT CF = {0}; + SN->FgColor = cc.rgbResult | 0xff000000; + CF.cbSize = sizeof(CHARFORMAT); + CF.dwMask = CFM_COLOR; + CF.crTextColor = SN->FgColor & 0xffffff; + SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + } + } + break; - for (int i = 0; i < _countof(clrPresets); i++) - InsertMenu(hBg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_BG + i, Translate(clrPresets[i].szName)); + case ID_APPEARANCE_CUSTOMFONT: + { + CHOOSEFONTA cf = {0}; + LOGFONTA lf = {0}; - for (int i = 0; i < _countof(clrPresets); i++) - InsertMenu(hFg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_FG + i, Translate(clrPresets[i].szName)); - } + if (SN->pCustomFont) + InitStickyNoteLogFont(SN->pCustomFont, &lf); + else + LoadNRFont(NR_FONTID_BODY, &lf, nullptr); + + cf.lStructSize = sizeof(cf); + cf.hwndOwner = SN->SNHwnd; + cf.lpLogFont = &lf; + cf.Flags = CF_EFFECTS | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_ENABLEHOOK; + cf.lpfnHook = CFHookProc; + + if (ChooseFontA(&cf)) { + if (!SN->pCustomFont) { + SN->pCustomFont = (STICKYNOTEFONT*)malloc(sizeof(STICKYNOTEFONT)); + SN->pCustomFont->hFont = nullptr; + } - TranslateMenu(FhMenu); - TrackPopupMenu(FhMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, LOWORD(lParam), HIWORD(lParam), 0, AhWnd, nullptr); - DestroyMenu(hMenuLoad); - return TRUE; -} + SN->pCustomFont->size = (char)lf.lfHeight; + SN->pCustomFont->style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0) | (lf.lfUnderline ? DBFONTF_UNDERLINE : 0) | (lf.lfStrikeOut ? DBFONTF_STRIKEOUT : 0); + SN->pCustomFont->charset = lf.lfCharSet; + mir_strcpy(SN->pCustomFont->szFace, lf.lfFaceName); -static void MeasureColorPresetMenuItem(HWND hdlg, LPMEASUREITEMSTRUCT lpMeasureItem, struct ColorPreset *clrPresets2) -{ - HDC hdc = GetDC(hdlg); - LPSTR lpsz = Translate(clrPresets2->szName); - SIZE sz; - GetTextExtentPoint32(hdc, lpsz, (int)mir_strlen(lpsz), &sz); - ReleaseDC(hdlg, hdc); + if (!CreateStickyNoteFont(SN->pCustomFont, &lf)) { + // failed + free(SN->pCustomFont); + SN->pCustomFont = nullptr; + } - lpMeasureItem->itemWidth = 50 + sz.cx; - lpMeasureItem->itemHeight = (sz.cy + 2)>18 ? sz.cy + 2 : 18; -} + // clear text first to force a reformatting w.r.w scrollbar + SetWindowTextA(SN->REHwnd, ""); + SendMessage(SN->REHwnd, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), FALSE); + SetNoteTextControl(SN); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + } + } + break; -static void PaintColorPresetMenuItem(LPDRAWITEMSTRUCT lpDrawItem, struct ColorPreset *clrPresets2) -{ - // UINT n = lpDrawItem->itemID - IDM_COLORPRESET_BG; - RECT rect; - rect.left = lpDrawItem->rcItem.left + 50; - rect.top = lpDrawItem->rcItem.top; - rect.right = lpDrawItem->rcItem.right; - rect.bottom = lpDrawItem->rcItem.bottom; + case ID_BACKGROUNDCOLOR_RESET: + SN->BgColor = 0; + SendMessage(SN->REHwnd, EM_SETBKGNDCOLOR, 0, (LPARAM)BodyColor); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + break; - if (lpDrawItem->itemState & ODS_SELECTED) { - SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUHILIGHT)); - FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH)); + case ID_TEXTCOLOR_RESET: + { + CHARFORMAT CF = {0}; + SN->FgColor = 0; + CF.cbSize = sizeof(CHARFORMAT); + CF.dwMask = CFM_COLOR; + CF.crTextColor = BodyFontColor; + SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + } + JustSaveNotesEx(); + break; + + case ID_FONT_RESET: + if (SN->pCustomFont) { + DeleteObject(SN->pCustomFont->hFont); + free(SN->pCustomFont); + SN->pCustomFont = nullptr; + + // clear text first to force a reformatting w.r.w scrollbar + SetWindowTextA(SN->REHwnd, ""); + SendMessage(SN->REHwnd, WM_SETFONT, (WPARAM)hBodyFont, FALSE); + SetNoteTextControl(SN); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + } + break; - SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - } - else { - SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENU)); - FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH)); + case ID_CONTEXTMENUNOTEPOPUP_PASTETITLE: + { + wchar_t s[MAX_TITLE_LEN + 1]; + if (GetClipboardText_Title(s, _countof(s))) { + if (SN->title) + free(SN->title); + SN->title = _wcsdup(s); + SN->CustomTitle = TRUE; + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + } + } + break; - SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUTEXT)); - } + case ID_CONTEXTMENUNOTEPOPUP_RESETTITLE: + if (SN->CustomTitle) { + if (SN->title) { + free(SN->title); + SN->title = nullptr; + } + InitNoteTitle(SN); + RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + JustSaveNotesEx(); + } + break; - SetBkMode(lpDrawItem->hDC, TRANSPARENT); - DrawText(lpDrawItem->hDC, clrPresets2->szName, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER); + case IDM_REMOVENOTE: + OnDeleteNote(hdlg, SN); + break; - int h = lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top; - rect.left = lpDrawItem->rcItem.left + 5; - rect.top = lpDrawItem->rcItem.top + ((h - 14) >> 1); - rect.right = rect.left + 40; - rect.bottom = rect.top + 14; + case IDM_HIDENOTE: + SN->bVisible = FALSE; + ShowWindow(hdlg, SW_HIDE); + JustSaveNotesEx(); + break; - FrameRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); - rect.left++; rect.top++; - rect.right--; rect.bottom--; - SetDCBrushColor(lpDrawItem->hDC, clrPresets2->color); - FillRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH)); -} + case IDM_COPY: + SendMessage(SN->REHwnd, WM_COPY, 0, 0); + break; -static BOOL GetClipboardText_Title(char *pOut, int size) -{ - BOOL bResult = FALSE; + case IDM_PASTE: + SendMessage(SN->REHwnd, WM_PASTE, 0, 0); + break; - if (OpenClipboard(nullptr)) { - HANDLE hData = GetClipboardData(CF_TEXT); - LPCSTR buffer; + case IDM_CUT: + SendMessage(SN->REHwnd, WM_CUT, 0, 0); + break; - if (hData && (buffer = (LPCSTR)GlobalLock(hData))) { - // trim initial white spaces - while (*buffer && isspace(*buffer)) - buffer++; + case IDM_CLEAR: + SendMessage(SN->REHwnd, WM_CLEAR, 0, 0); + break; - size_t n = mir_strlen(buffer); - if (n >= size) - n = size - 1; - memcpy(pOut, buffer, n); - pOut[n] = 0; + case IDM_UNDO: + SendMessage(SN->REHwnd, WM_UNDO, 0, 0); + break; - // end string on line break and convert tabs to spaces - char *p = pOut; - while (*p) { - if (*p == '\r' || *p == '\n') { - *p = 0; - n = mir_strlen(pOut); - break; - } - else if (*p == '\t') { - *p = ' '; - } - p++; - } + case IDM_TOGGLEONTOP: + SN->bOnTop = !SN->bOnTop; + SetWindowPos(hdlg, SN->bOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + RedrawWindow(hdlg, nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); + JustSaveNotesEx(); + break; - // trim trailing white spaces - rtrim(pOut); - if (pOut[0]) - bResult = TRUE; + case ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES: + PluginMenuCommandViewNotes(0, 0); + break; - GlobalUnlock(hData); + case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP: + BringAllNotesToFront(SN); + break; } + return TRUE; - CloseClipboard(); - } - - return bResult; -} - -static void SetNoteTextControl(STICKYNOTE *SN) -{ - CHARFORMAT CF = { 0 }; - CF.cbSize = sizeof(CHARFORMAT); - CF.dwMask = CFM_COLOR; - CF.crTextColor = SN->FgColor ? (SN->FgColor & 0xffffff) : BodyFontColor; - SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); - - if (SN->data) // TODO: use EM_STREAMIN - SetWindowText(SN->REHwnd, SN->data); -} + case WM_NCDESTROY: + RemovePropA(hdlg, "ctrldata"); + break; + case WM_CONTEXTMENU: + if (DoContextMenu(hdlg, wParam, lParam)) + return FALSE; -static UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT msg, WPARAM, LPARAM) -{ - if (msg == WM_INITDIALOG) { - // hide color selector - ShowWindow(GetDlgItem(hdlg, 0x443), SW_HIDE); - ShowWindow(GetDlgItem(hdlg, 0x473), SW_HIDE); - TranslateDialogDefault(hdlg); + default: + return DefWindowProc(hdlg, message, wParam, lParam); } - - return 0; + return FALSE; } - -LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) +STICKYNOTE* NewNoteEx(int Ax, int Ay, int Aw, int Ah, char *Data, ULARGE_INTEGER *ID, BOOL bVisible, BOOL bOnTop, int scrollV, COLORREF bgClr, COLORREF fgClr, wchar_t *Title, STICKYNOTEFONT *pCustomFont, BOOL bLoading) { - switch (message) { - case WM_CLOSE: - return TRUE; + WNDCLASSEX TWC = {0}; + WINDOWPLACEMENT TWP; + DWORD L1, L2; + SYSTEMTIME tm; - case WM_SIZE: - { - RECT SZ; + const BOOL bIsStartup = bVisible & 0x10000; + bVisible &= ~0x10000; - GetClientRect(hdlg, &SZ); - HWND H = GetDlgItem(hdlg, 1); - MoveWindow(H, 0, 0, SZ.right, SZ.bottom, TRUE); + char *TData = Data; - KillTimer(hdlg, 1025); - SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); + if (!GetClassInfoEx(hmiranda, NOTE_WND_CLASS, &TWC)) { + TWC.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + TWC.cbClsExtra = 0; + TWC.cbWndExtra = 0; + TWC.hInstance = hmiranda; + TWC.hIcon = LoadIcon(nullptr, IDI_APPLICATION); + TWC.hCursor = LoadCursor(nullptr, IDC_ARROW); + TWC.hbrBackground = nullptr; + TWC.lpszMenuName = nullptr; + TWC.lpszClassName = NOTE_WND_CLASS; + TWC.cbSize = sizeof(WNDCLASSEX); + TWC.lpfnWndProc = StickyNoteWndProc; + if (!RegisterClassEx(&TWC)) return nullptr; + } - return TRUE; - } + if (!TData || Aw < 0 || Ah < 0) { + TWP.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(GetDesktopWindow(), &TWP); + Aw = g_NoteWidth; Ah = g_NoteHeight; + Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2); + Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 2) - (Ah / 2); + } - case WM_TIMER: - if (wParam == 1025) { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); + STICKYNOTE *TSN = new STICKYNOTE(); - KillTimer(hdlg, 1025); - JustSaveNotesEx(SN); - } - break; + if (ID) + TSN->ID = *ID; + else { + GetSystemTime(&tm); + SystemTimeToFileTime(&tm, (FILETIME*)&TSN->ID); + } - case WM_MOVE: - KillTimer(hdlg, 1025); - SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); - return TRUE; + EnsureUniqueID(TSN); - case WM_CREATE: - { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); + g_arStickies.insert(TSN); - CREATESTRUCT *CS = (CREATESTRUCT *)lParam; - DWORD mystyle; + if (!TData) + TSN->data = _strdup(""); + else + TSN->data = TData; - SN = (STICKYNOTE*)CS->lpCreateParams; - SetProp(hdlg, "ctrldata", (HANDLE)SN); - BringWindowToTop(hdlg); - mystyle = WS_CHILD | WS_VISIBLE | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN; - if (g_ShowScrollbar) mystyle |= WS_VSCROLL; - HWND H = CreateWindowW(MSFTEDIT_CLASS, nullptr, mystyle, 0, 0, CS->cx - 3 - 3, CS->cy - 3 - (3 + 14), hdlg, (HMENU)1, hmiranda, nullptr); - SN->REHwnd = H; - SendMessage(H, EM_SETTEXTMODE, TM_PLAINTEXT, 0); - SendMessage(H, EM_LIMITTEXT, MAX_NOTE_LEN, 0); - SendMessage(H, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), 1); - SendMessage(H, EM_SETEVENTMASK, 0, ENM_CHANGE | ENM_LINK); - SendMessage(H, EM_SETBKGNDCOLOR, 0, SN->BgColor ? (SN->BgColor & 0xffffff) : BodyColor); - SendMessage(H, EM_AUTOURLDETECT, 1, 0); - SetNoteTextControl(SN); - } - return TRUE; + // init note title (time-stamp) + if (Title) { + TSN->title = Title; + TSN->CustomTitle = TRUE; + } + else { + TSN->title = nullptr; + InitNoteTitle(TSN); + } - case WM_GETMINMAXINFO: - { - MINMAXINFO *mm = (MINMAXINFO*)lParam; - // min width accomodates frame, buttons and some extra space for sanity - mm->ptMinTrackSize.x = 48 + 3 + 3 + 8 + 40; - // min height allows collapsing entire client area, only leaving frame and caption - mm->ptMinTrackSize.y = 3 + 3 + 14; - } - return 0; + TSN->bVisible = bVisible; + TSN->bOnTop = bOnTop; + TSN->BgColor = bgClr; + TSN->FgColor = fgClr; + TSN->pCustomFont = pCustomFont; - case WM_ERASEBKGND: - // no BG needed as edit control takes up entire client area - return TRUE; + L1 = WS_EX_TOOLWINDOW; + if (g_Transparency < 255) L1 |= WS_EX_LAYERED; + if (bOnTop) L1 |= WS_EX_TOPMOST; - case WM_NCPAINT: - // make window borders have the same color as caption - { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); + L2 = WS_POPUP | WS_THICKFRAME | WS_CAPTION; - RECT rect, wr, r; - //HDC hdc = GetDCEx(hdlg, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN); - HDC hdc = GetWindowDC(hdlg); + // NOTE: loaded note positions stem from GetWindowPlacement, which normally have a different coord space than + // CreateWindow/SetWindowPos, BUT since we now use WS_EX_TOOLWINDOW they use the same coord space so + // we don't have to worry about notes "drifting" between sessions + TSN->SNHwnd = CreateWindowEx(L1, NOTE_WND_CLASS, L"StickyNote", L2, Ax, Ay, Aw, Ah, nullptr, nullptr, hmiranda, TSN); - GetWindowRect(hdlg, &wr); - if (wParam && wParam != 1) { - SelectClipRgn(hdc, (HRGN)wParam); - OffsetClipRgn(hdc, -wr.left, -wr.top); - } + if (g_Transparency < 255) + SetLayeredWindowAttributes(TSN->SNHwnd, 0, (BYTE)g_Transparency, LWA_ALPHA); - rect = wr; - OffsetRect(&rect, -wr.left, -wr.top); + // ensure that window is not placed off-screen (if previous session had different monitor count or resolution) + // NOTE: SetWindowPlacement should do this, but it's extremly flakey + if (Data) { + if (!MonitorFromWindow(TSN->SNHwnd, MONITOR_DEFAULTTONULL)) { + TWP.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(GetDesktopWindow(), &TWP); - HBRUSH hBkBrush = (HBRUSH)GetStockObject(DC_BRUSH); - SetDCBrushColor(hdc, GetCaptionColor((SN && SN->BgColor) ? SN->BgColor : BodyColor)); + if (Aw > 500) Aw = 500; + if (Ay < TWP.rcNormalPosition.left + 10 || Ax > TWP.rcNormalPosition.right - 120) + Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2) + (rand() & 0x3f); + if (Ay < TWP.rcNormalPosition.top + 50 || Ay > TWP.rcNormalPosition.bottom - 50) + Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 4) + (rand() & 0x1f); - // draw all frame sides separately to avoid filling client area (which flickers) - { - // top - r.left = rect.left; r.right = rect.right; - r.top = rect.top; r.bottom = r.top + 3 + 14; - FillRect(hdc, &r, hBkBrush); - // bottom - r.top = rect.bottom - 3; r.bottom = rect.bottom; - FillRect(hdc, &r, hBkBrush); - // left - r.left = rect.left; r.right = r.left + 3; - r.top = rect.top + 3 + 14; r.bottom = rect.bottom - 3; - FillRect(hdc, &r, hBkBrush); - // right - r.left = rect.right - 3; r.right = rect.right; - FillRect(hdc, &r, hBkBrush); - } + SetWindowPos(TSN->SNHwnd, nullptr, Ax, Ay, Aw, Ah, SWP_NOZORDER | SWP_NOACTIVATE); + } + } - // paint title bar contents (time stamp and buttons) + if (bVisible) { + ShowWindow(TSN->SNHwnd, SW_SHOWNA); - if (SN && SN->title) { - RECT R; - SelectObject(hdc, hCaptionFont); - R.top = 3 + 1; R.bottom = 3 + 11; R.left = 3 + 2; R.right = rect.right - 3 - 1; - if (g_ShowNoteButtons) - R.right -= 48; + // when loading notes (only at startup), place all non-top notes at the bottom so they don't cover other windows + if (Data && !bOnTop && bIsStartup) + SetWindowPos(TSN->SNHwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS); + } - SetTextColor(hdc, SN->FgColor ? (SN->FgColor & 0xffffff) : CaptionFontColor); - SetBkMode(hdc, TRANSPARENT); - DrawText(hdc, SN->title, -1, &R, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER); - } + if (scrollV) + SendMessage(TSN->REHwnd, EM_LINESCROLL, 0, scrollV); - if (g_ShowNoteButtons) { - HICON hcIcon; - if (SN->bOnTop) - hcIcon = IcoLib_GetIconByHandle(iconList[4].hIcolib); - else - hcIcon = IcoLib_GetIconByHandle(iconList[7].hIcolib); - DrawIcon(hdc, wr.right - wr.left - 16, 0 + 3, hcIcon); - IcoLib_ReleaseIcon(hcIcon); + // make sure that any event triggered by init doesn't cause a meaningless save + KillTimer(TSN->SNHwnd, 1025); - hcIcon = IcoLib_GetIconByHandle(iconList[9].hIcolib); - DrawIcon(hdc, wr.right - wr.left - 32, 1 + 3, hcIcon); - IcoLib_ReleaseIcon(hcIcon); + if (!bLoading) { + NOTIFY_LIST(); + } - hcIcon = IcoLib_GetIconByHandle(iconList[8].hIcolib); - DrawIcon(hdc, wr.right - wr.left - 48, 1 + 3, hcIcon); - IcoLib_ReleaseIcon(hcIcon); - } + return TSN; +} - if (wParam && wParam != 1) - SelectClipRgn(hdc, nullptr); +void NewNote(int Ax, int Ay, int Aw, int Ah, char *Data, ULARGE_INTEGER *ID, BOOL bVisible, BOOL bOnTop, int scrollV) +{ + auto *PSN = NewNoteEx(Ax, Ay, Aw, Ah, Data, ID, bVisible, bOnTop, scrollV, 0, 0, nullptr, nullptr, FALSE); + if (PSN) + SetFocus(PSN->REHwnd); +} - ReleaseDC(hdlg, hdc); - } - return TRUE; +void LoadNotes(BOOL bIsStartup) +{ + WORD Size = 0; + char *Value = nullptr, *TVal = nullptr; + char ValueName[32]; - case WM_NCCALCSIZE: - { - RECT *pRect = wParam ? &((NCCALCSIZE_PARAMS*)lParam)->rgrc[0] : (RECT*)lParam; - pRect->bottom -= 3; - pRect->right -= 3; - pRect->left += 3; - pRect->top += 3 + 14; - } - return WVR_REDRAW; + g_arStickies.destroy(); - case WM_NCACTIVATE: - // update window (so that parts that potentially became visible through activation get redrawn immediately) - RedrawWindow(hdlg, nullptr, nullptr, RDW_UPDATENOW); - return TRUE; + int NotesCount = g_plugin.getDword("NotesData", 0); - case WM_NOTIFY: - if (LOWORD(wParam) == 1) { - char *Buff; - PENLINK PEnLnk = (PENLINK)lParam; + for (int i = 0; i < NotesCount; i++) { + mir_snprintf(ValueName, "NotesData%d", i); - if (PEnLnk->msg == WM_LBUTTONDOWN) { - SendDlgItemMessage(hdlg, 1, EM_EXSETSEL, 0, (LPARAM)&(PEnLnk->chrg)); - Buff = (char*)malloc(PEnLnk->chrg.cpMax - PEnLnk->chrg.cpMin + 1); - SendDlgItemMessage(hdlg, 1, EM_GETSELTEXT, 0, (LPARAM)Buff); - if ((GetAsyncKeyState(VK_CONTROL) >> 15) != 0) - ShellExecute(hdlg, "open", "iexplore", Buff, "", SW_SHOWNORMAL); - else if (g_lpszAltBrowser && *g_lpszAltBrowser) - ShellExecute(hdlg, "open", g_lpszAltBrowser, Buff, "", SW_SHOWNORMAL); - else - ShellExecute(hdlg, "open", Buff, "", "", SW_SHOWNORMAL); - SAFE_FREE((void**)&Buff); - return TRUE; - } - return FALSE; + if (Value) { + FreeSettingBlob(Size, Value); + Value = nullptr; } - break; - case WM_NCHITTEST: - { - int r = DefWindowProc(hdlg, message, wParam, lParam); - // filter out potential hits on windows default title bar buttons - switch (r) { - case HTSYSMENU: - case HTCLOSE: - case HTMINBUTTON: - case HTMAXBUTTON: - return HTCAPTION; - } - return r; - } + Size = 65535; // does not get used - case WM_NCLBUTTONDOWN: - if (wParam == HTCAPTION && g_ShowNoteButtons) { - long X, Y; - RECT rect; - int Tw; + ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value); - GetWindowRect(hdlg, &rect); - Tw = rect.right - rect.left; + if (!Size || !Value) + continue; // the setting could not be read from DB -> skip - X = LOWORD(lParam) - rect.left; - Y = HIWORD(lParam) - rect.top; + if (Value[0] == 'X') { + // new eXtended/fleXible data format - if (X > Tw - 16) { - SendMessage(hdlg, WM_COMMAND, IDM_TOGGLEONTOP, 0); - return TRUE; - } - else if (X > Tw - 31 && X < Tw - 16) { - SendMessage(hdlg, WM_COMMAND, IDM_REMOVENOTE, 0); - return TRUE; - } - else if (X > Tw - 48 && X < Tw - 32) { - SendMessage(hdlg, WM_COMMAND, IDM_HIDENOTE, 0); - return TRUE; - } - } - return DefWindowProc(hdlg, message, wParam, lParam); + int scrollV = 0; + STICKYNOTEFONT *pCustomFont = nullptr; - case WM_MEASUREITEM: - { - LPMEASUREITEMSTRUCT lpMeasureItem = (LPMEASUREITEMSTRUCT)lParam; + char *DelPos = strchr(Value + 1, 0x1B); + if (DelPos) + *DelPos = 0; - if (lpMeasureItem->CtlType != ODT_MENU) - break; + // id:x:y:w:h:flags - if (lpMeasureItem->itemID >= IDM_COLORPRESET_BG && lpMeasureItem->itemID <= IDM_COLORPRESET_BG + _countof(clrPresets)) { - MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_BG)); - return TRUE; - } - else if (lpMeasureItem->itemID >= IDM_COLORPRESET_FG && lpMeasureItem->itemID <= IDM_COLORPRESET_FG + _countof(clrPresets)) { - MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_FG)); - return TRUE; - } - } - break; + TVal = strchr(Value + 1, ':'); + if (!TVal || (DelPos && TVal > DelPos)) + continue; - case WM_DRAWITEM: - if (!wParam) { - LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)lParam; + *TVal++ = 0; + ULARGE_INTEGER id; + id.QuadPart = _strtoui64(Value + 1, nullptr, 16); - if (lpDrawItem->CtlType != ODT_MENU) - break; + int rect[4]; + for (auto &it : rect) { + char *sep = strchr(TVal, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; + *sep++ = 0; - if (lpDrawItem->itemID >= IDM_COLORPRESET_BG && lpDrawItem->itemID <= IDM_COLORPRESET_BG + _countof(clrPresets)) { - PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_BG)); - return TRUE; - } - else if (lpDrawItem->itemID >= IDM_COLORPRESET_FG && lpDrawItem->itemID <= IDM_COLORPRESET_FG + _countof(clrPresets)) { - PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_FG)); - return TRUE; + it = strtol(TVal, nullptr, 10); + TVal = sep; } - } - break; - case WM_COMMAND: - { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); - - switch (HIWORD(wParam)) { - case EN_CHANGE: - case EN_VSCROLL: - case EN_HSCROLL: - KillTimer(hdlg, 1025); - SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); - break; - } + BOOL bVisible = 0, bOnTop = 0; + DWORD flags = strtoul(TVal, nullptr, 16); + if (flags & 1) + bVisible = TRUE; + if (flags & 2) + bOnTop = TRUE; - UINT id = (UINT)LOWORD(wParam); + // optional \033 separated params + char *data = 0; + wchar_t *title = 0; + COLORREF BgColor = 0, FgColor = 0; - HWND H = SN->REHwnd; + while (DelPos) { + TVal = DelPos + 1; + // find param end and make sure it's null-terminated (if end of data then it's already null-terminated) + DelPos = strchr(TVal, 0x1B); + if (DelPos) + *DelPos = 0; - if (id >= IDM_COLORPRESET_BG && id <= IDM_COLORPRESET_BG + _countof(clrPresets)) { - SN->BgColor = clrPresets[id - IDM_COLORPRESET_BG].color | 0xff000000; - SendMessage(H, EM_SETBKGNDCOLOR, 0, SN->BgColor & 0xffffff); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - return FALSE; - } - else if (id >= IDM_COLORPRESET_FG && id <= IDM_COLORPRESET_FG + _countof(clrPresets)) { - CHARFORMAT CF = { 0 }; - SN->FgColor = clrPresets[id - IDM_COLORPRESET_FG].color | 0xff000000; - CF.cbSize = sizeof(CHARFORMAT); - CF.dwMask = CFM_COLOR; - CF.crTextColor = SN->FgColor & 0xffffff; - SendMessage(H, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - return FALSE; - } + // tag: + char *sep = strchr(TVal, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; - switch (id) { - case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE: - PluginMenuCommandAddNew(0, 0); - break; + UINT tag = strtoul(TVal, nullptr, 10); + TVal = sep + 1; - case ID_APPEARANCE_CUSTOMBG: - { - COLORREF custclr[16] = { 0 }; - CHOOSECOLOR cc = { 0 }; - COLORREF orgclr = SN->BgColor ? (COLORREF)(SN->BgColor & 0xffffff) : (COLORREF)(BodyColor & 0xffffff); - cc.lStructSize = sizeof(cc); - cc.hwndOwner = SN->SNHwnd; - cc.rgbResult = orgclr; - cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR; - cc.lpCustColors = custclr; - - if (ChooseColor(&cc) && cc.rgbResult != orgclr) { - SN->BgColor = cc.rgbResult | 0xff000000; - SendMessage(H, EM_SETBKGNDCOLOR, 0, SN->BgColor & 0xffffff); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - } - } - break; - case ID_APPEARANCE_CUSTOMTEXT: - { - COLORREF custclr[16] = { 0 }; - CHOOSECOLOR cc = { 0 }; - COLORREF orgclr = SN->FgColor ? (COLORREF)(SN->FgColor & 0xffffff) : (COLORREF)(BodyFontColor & 0xffffff); - cc.lStructSize = sizeof(cc); - cc.hwndOwner = SN->SNHwnd; - cc.rgbResult = orgclr; - cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR; - cc.lpCustColors = custclr; - - if (ChooseColor(&cc) && cc.rgbResult != orgclr) { - CHARFORMAT CF = { 0 }; - SN->FgColor = cc.rgbResult | 0xff000000; - CF.cbSize = sizeof(CHARFORMAT); - CF.dwMask = CFM_COLOR; - CF.crTextColor = SN->FgColor & 0xffffff; - SendMessage(H, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - } - } - break; + switch (tag) { + case DATATAG_TEXT: + data = _strdup(TVal); + break; - case ID_APPEARANCE_CUSTOMFONT: - { - CHOOSEFONT cf = { 0 }; - LOGFONT lf = { 0 }; - - if (SN->pCustomFont) - InitStickyNoteLogFont(SN->pCustomFont, &lf); - else - LoadNRFont(NR_FONTID_BODY, &lf, nullptr); - - cf.lStructSize = sizeof(cf); - cf.hwndOwner = SN->SNHwnd; - cf.lpLogFont = &lf; - cf.Flags = CF_EFFECTS | CF_FORCEFONTEXIST | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_ENABLEHOOK; - cf.lpfnHook = CFHookProc; - - if (ChooseFont(&cf)) { - if (!SN->pCustomFont) { - SN->pCustomFont = (STICKYNOTEFONT*)malloc(sizeof(STICKYNOTEFONT)); - SN->pCustomFont->hFont = nullptr; - } - - SN->pCustomFont->size = (char)lf.lfHeight; - SN->pCustomFont->style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0) | (lf.lfUnderline ? DBFONTF_UNDERLINE : 0) | (lf.lfStrikeOut ? DBFONTF_STRIKEOUT : 0); - SN->pCustomFont->charset = lf.lfCharSet; - mir_strcpy(SN->pCustomFont->szFace, lf.lfFaceName); - - if (!CreateStickyNoteFont(SN->pCustomFont, &lf)) { - // failed - free(SN->pCustomFont); - SN->pCustomFont = nullptr; - } - - // clear text first to force a reformatting w.r.w scrollbar - SetWindowText(H, ""); - SendMessage(H, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), FALSE); - SetNoteTextControl(SN); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); + case DATATAG_SCROLLPOS: + scrollV = (int)strtoul(TVal, nullptr, 10); + break; + + case DATATAG_BGCOL: + BgColor = strtoul(TVal, nullptr, 16) | 0xff000000; + break; + + case DATATAG_FGCOL: + FgColor = strtoul(TVal, nullptr, 16) | 0xff000000; + break; + + case DATATAG_TITLE: + if (mir_strlen(TVal) > MAX_TITLE_LEN) + TVal[MAX_TITLE_LEN] = 0; + title = _wcsdup(_A2T(TVal)); + break; + + case DATATAG_FONT: + int fsize; + UINT fstyle, fcharset; + + char *TVal2 = TVal; + sep = strchr(TVal2, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; + *sep++ = 0; + fsize = strtol(TVal2, nullptr, 10); + TVal2 = sep; + + sep = strchr(TVal2, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; + *sep++ = 0; + fstyle = strtoul(TVal2, nullptr, 10); + TVal2 = sep; + + sep = strchr(TVal2, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; + *sep++ = 0; + fcharset = strtoul(TVal2, nullptr, 10); + TVal2 = sep; + + if (TVal2 >= DelPos) + goto skip; + + pCustomFont = (STICKYNOTEFONT*)malloc(sizeof(STICKYNOTEFONT)); + pCustomFont->size = (char)fsize; + pCustomFont->style = (BYTE)fstyle; + pCustomFont->charset = (BYTE)fcharset; + mir_strcpy(pCustomFont->szFace, TVal2); + pCustomFont->hFont = nullptr; + + if (!CreateStickyNoteFont(pCustomFont, nullptr)) { + free(pCustomFont); + pCustomFont = nullptr; } + break; } - break; + } - case ID_BACKGROUNDCOLOR_RESET: - SN->BgColor = 0; - SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)BodyColor); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - break; + if (!data) + data = _strdup(""); - case ID_TEXTCOLOR_RESET: - { - CHARFORMAT CF = { 0 }; - SN->FgColor = 0; - CF.cbSize = sizeof(CHARFORMAT); - CF.dwMask = CFM_COLOR; - CF.crTextColor = BodyFontColor; - SendMessage(H, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - } - JustSaveNotesEx(); - break; + bVisible = bVisible && (!bIsStartup || g_plugin.bShowNotesAtStart); + if (bIsStartup) + bVisible |= 0x10000; - case ID_FONT_RESET: - if (SN->pCustomFont) { - DeleteObject(SN->pCustomFont->hFont); - free(SN->pCustomFont); - SN->pCustomFont = nullptr; + NewNoteEx(rect[0], rect[1], rect[2], rect[3], data, &id, bVisible, bOnTop, scrollV, BgColor, FgColor, title, pCustomFont, TRUE); + } + else { + // old format (for DB backward compatibility) - // clear text first to force a reformatting w.r.w scrollbar - SetWindowText(H, ""); - SendMessage(H, WM_SETFONT, (WPARAM)hBodyFont, FALSE); - SetNoteTextControl(SN); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - } - break; + int Tx, Ty, Tw, Th, TV, OT; + BOOL V; + char *Data, *ID; + ULARGE_INTEGER newid; - case ID_CONTEXTMENUNOTEPOPUP_PASTETITLE: - { - char s[MAX_TITLE_LEN + 1]; - if (GetClipboardText_Title(s, sizeof(s))) { - if (SN->title) - free(SN->title); - SN->title = _strdup(s); - SN->CustomTitle = TRUE; - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - } - } - break; + OT = 1; TV = 1; + Tx = 100; Ty = 100; + Tw = 179; Th = 35; + Data = nullptr; ID = nullptr; - case ID_CONTEXTMENUNOTEPOPUP_RESETTITLE: - if (SN->CustomTitle) { - if (SN->title) { - free(SN->title); - SN->title = nullptr; - } - InitNoteTitle(SN); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - JustSaveNotesEx(); - } - break; + if (char *DelPos = strchr(Value, 0x1B)) { // get first delimiter + Data = nullptr; + ID = nullptr; + TVal = Value; + DelPos[0] = 0x0; + Tx = strtol(TVal, nullptr, 10); - case IDM_REMOVENOTE: - OnDeleteNote(hdlg, SN); - break; + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Ty = strtol(TVal, nullptr, 10); - case IDM_HIDENOTE: - SN->bVisible = FALSE; - ShowWindow(hdlg, SW_HIDE); - JustSaveNotesEx(); - break; + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Tw = strtol(TVal, nullptr, 10); - case IDM_COPY: - SendMessage(H, WM_COPY, 0, 0); - break; + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Th = strtol(TVal, nullptr, 10); - case IDM_PASTE: - SendMessage(H, WM_PASTE, 0, 0); - break; + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + TV = strtol(TVal, nullptr, 10); - case IDM_CUT: - SendMessage(H, WM_CUT, 0, 0); - break; + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + OT = strtol(TVal, nullptr, 10); - case IDM_CLEAR: - SendMessage(H, WM_CLEAR, 0, 0); - break; + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Data = _strdup(TVal); - case IDM_UNDO: - SendMessage(H, WM_UNDO, 0, 0); - break; + TVal = DelPos + 1; + ID = TVal; - case IDM_TOGGLEONTOP: - SN->bOnTop = !SN->bOnTop; - SetWindowPos(hdlg, SN->bOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); - RedrawWindow(hdlg, nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); - JustSaveNotesEx(); - break; + V = (BOOL)TV && (!bIsStartup || g_plugin.bShowNotesAtStart); - case ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES: - ListNotes(); - break; + if (bIsStartup) + V |= 0x10000; - case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP: - BringAllNotesToFront(SN); - break; - } - return TRUE; - } + // convert old ID format to new + if (strchr(ID, '-')) { + // validate format (otherwise create new) + if (mir_strlen(ID) < 19 || ID[2] != '-' || ID[5] != '-' || ID[10] != ' ' || ID[13] != ':' || ID[16] != ':') { + ID = nullptr; + } + else { + SYSTEMTIME tm; - case WM_NCDESTROY: - RemoveProp(hdlg, "ctrldata"); - break; + ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0; - case WM_CONTEXTMENU: - if (DoContextMenu(hdlg, wParam, lParam)) - return FALSE; + memset(&tm, 0, sizeof(tm)); + tm.wDay = (WORD)strtoul(ID, nullptr, 10); + tm.wMonth = (WORD)strtoul(ID + 3, nullptr, 10); + tm.wYear = (WORD)strtoul(ID + 6, nullptr, 10); + tm.wHour = (WORD)strtoul(ID + 11, nullptr, 10); + tm.wMinute = (WORD)strtoul(ID + 14, nullptr, 10); + tm.wSecond = (WORD)strtoul(ID + 17, nullptr, 10); - default: - return DefWindowProc(hdlg, message, wParam, lParam); + SystemTimeToFileTime(&tm, (FILETIME*)&newid); + } + } + else ID = nullptr; + + NewNoteEx(Tx, Ty, Tw, Th, Data, ID ? &newid : nullptr, V, (BOOL)OT, 0, 0, 0, nullptr, nullptr, TRUE); + } + } +skip:; } - return FALSE; + + if (Value) + FreeSettingBlob(Size, Value); // we do not leak on bad setting + + NOTIFY_LIST(); } +///////////////////////////////////////////////////////////////////////////////////////// + static void EditNote(STICKYNOTE *SN) { if (!SN) @@ -1583,43 +1508,35 @@ static void EditNote(STICKYNOTE *SN) SetFocus(SN->REHwnd); } -char* GetPreviewString(const char *lpsz) +wchar_t* GetPreviewString(const char *lpsz) { - int l; - char *p; const int MaxLen = 80; - static char s[80 + 8]; + static wchar_t s[MaxLen + 8]; if (!lpsz) - return ""; + return L""; // trim leading spaces - while (iswspace(*lpsz)) + while (isspace(*lpsz)) lpsz++; - l = (int)mir_strlen(lpsz); - + size_t l = mir_strlen(lpsz); if (!l) - return ""; + return L""; if (l <= MaxLen) { - mir_strcpy(s, lpsz); + mir_wstrcpy(s, _A2T(lpsz)); } else { - memcpy(s, lpsz, MaxLen); + mir_wstrncpy(s, _A2T(lpsz), MaxLen); s[MaxLen] = '.'; s[MaxLen + 1] = '.'; s[MaxLen + 2] = '.'; s[MaxLen + 3] = 0; } - if (!s) - return nullptr; - // convert line breaks and tabs to spaces - - p = s; - + wchar_t *p = s; while (*p) { if (iswspace(*p)) *p = ' '; @@ -1631,52 +1548,48 @@ char* GetPreviewString(const char *lpsz) static void InitListView(HWND AHLV) { - int I = 0; - char *S; - char S1[128]; + int i = 0; - char *V = Translate("Visible"); - char *T = Translate("Top"); + wchar_t S1[128]; + wchar_t *V = TranslateT("Visible"); + wchar_t *T = TranslateT("Top"); ListView_SetHoverTime(AHLV, 700); ListView_SetExtendedListViewStyle(AHLV, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_TRACKSELECT); ListView_DeleteAllItems(AHLV); - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - STICKYNOTE *pNote = (STICKYNOTE*)TTE->ptrdata; - + for (auto &pNote : g_arStickies) { LV_ITEM lvTIt; lvTIt.mask = LVIF_TEXT; if (!pNote->CustomTitle || !pNote->title) - GetTriggerTimeString(&pNote->ID, S1, sizeof(S1), TRUE); + GetTriggerTimeString(&pNote->ID, S1, _countof(S1), TRUE); - lvTIt.iItem = I; + lvTIt.iItem = i; lvTIt.iSubItem = 0; lvTIt.pszText = (pNote->CustomTitle && pNote->title) ? pNote->title : S1; ListView_InsertItem(AHLV, &lvTIt); if (pNote->bVisible) { - lvTIt.iItem = I; + lvTIt.iItem = i; lvTIt.iSubItem = 1; lvTIt.pszText = V; ListView_SetItem(AHLV, &lvTIt); } if (pNote->bOnTop) { - lvTIt.iItem = I; + lvTIt.iItem = i; lvTIt.iSubItem = 2; lvTIt.pszText = T; ListView_SetItem(AHLV, &lvTIt); } - S = GetPreviewString(pNote->data); - lvTIt.iItem = I; + lvTIt.iItem = i; lvTIt.iSubItem = 3; - lvTIt.pszText = S; + lvTIt.pszText = GetPreviewString(pNote->data); ListView_SetItem(AHLV, &lvTIt); - I++; + i++; } ListView_SetItemState(AHLV, 0, LVIS_SELECTED, LVIS_SELECTED); @@ -1688,10 +1601,10 @@ static BOOL DoListContextMenu(HWND AhWnd, WPARAM wParam, LPARAM lParam, STICKYNO if (hwndListView != GetDlgItem(AhWnd, IDC_LISTREMINDERS)) return FALSE; - HMENU hMenuLoad = LoadMenu(g_plugin.getInst(), "MNU_NOTELISTPOPUP"); + HMENU hMenuLoad = LoadMenuA(g_plugin.getInst(), "MNU_NOTELISTPOPUP"); HMENU FhMenu = GetSubMenu(hMenuLoad, 0); - MENUITEMINFO mii = { 0 }; + MENUITEMINFO mii = {0}; mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; mii.fState = MFS_DEFAULT; @@ -1739,7 +1652,7 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara return 0; case WM_RELOAD: - SetDlgItemText(Dialog, IDC_REMINDERDATA, ""); + SetDlgItemTextA(Dialog, IDC_REMINDERDATA, ""); InitListView(GetDlgItem(Dialog, IDC_LISTREMINDERS)); return TRUE; @@ -1749,10 +1662,9 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - pNote = (STICKYNOTE*)TreeGetAt(g_Stickies, I); - } + int i = ListView_GetSelectionMark(H); + if (i != -1) + pNote = &g_arStickies[i]; } if (DoListContextMenu(Dialog, wParam, lParam, pNote)) @@ -1763,37 +1675,33 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara case WM_INITDIALOG: Window_SetIcon_IcoLib(Dialog, iconList[13].hIcolib); - SetWindowText(Dialog, LPGEN("Notes")); + SetWindowText(Dialog, LPGENW("Notes")); TranslateDialogDefault(Dialog); - SetDlgItemText(Dialog, IDC_REMINDERDATA, ""); + SetDlgItemText(Dialog, IDC_REMINDERDATA, L""); { HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); LV_COLUMN lvCol; lvCol.mask = LVCF_TEXT | LVCF_WIDTH; - char *S = Translate("Note text"); - lvCol.pszText = S; + lvCol.pszText = TranslateT("Note text"); lvCol.cx = g_notesListColGeom[3]; ListView_InsertColumn(H, 0, &lvCol); lvCol.mask = LVCF_TEXT | LVCF_WIDTH; - S = Translate("Top"); - lvCol.pszText = S; + lvCol.pszText = TranslateT("Top"); lvCol.cx = g_notesListColGeom[2]; ListView_InsertColumn(H, 0, &lvCol); lvCol.mask = LVCF_TEXT | LVCF_WIDTH; - S = Translate("Visible"); - lvCol.pszText = S; + lvCol.pszText = TranslateT("Visible"); lvCol.cx = g_notesListColGeom[1]; ListView_InsertColumn(H, 0, &lvCol); lvCol.mask = LVCF_TEXT | LVCF_WIDTH; - S = Translate("Date/Title"); - lvCol.pszText = S; + lvCol.pszText = TranslateT("Date/Title"); lvCol.cx = g_notesListColGeom[0]; ListView_InsertColumn(H, 0, &lvCol); @@ -1829,20 +1737,14 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara LPNMLISTVIEW NM = (LPNMLISTVIEW)lParam; switch (NM->hdr.code) { case LVN_ITEMCHANGED: - { - char *S = ((STICKYNOTE*)TreeGetAt(g_Stickies, NM->iItem))->data; - SetDlgItemText(Dialog, IDC_REMINDERDATA, S); - } + SetDlgItemTextA(Dialog, IDC_REMINDERDATA, g_arStickies[NM->iItem].data); break; + case NM_DBLCLK: - { - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); - if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - EditNote((STICKYNOTE *)TreeGetAt(g_Stickies, I)); - } - } + if (ListView_GetSelectedCount(NM->hdr.hwndFrom)) { + int i = ListView_GetSelectionMark(NM->hdr.hwndFrom); + if (i != -1) + EditNote(&g_arStickies[i]); } break; } @@ -1863,9 +1765,9 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara { HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - EditNote((STICKYNOTE*)TreeGetAt(g_Stickies, I)); + int i = ListView_GetSelectionMark(H); + if (i != -1) { + EditNote(&g_arStickies[i]); } } } @@ -1875,11 +1777,11 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara { HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies, I); - SN->bVisible = !SN->bVisible; - ShowWindow(SN->SNHwnd, SN->bVisible ? SW_SHOWNA : SW_HIDE); + int i = ListView_GetSelectionMark(H); + if (i != -1) { + auto &SN = g_arStickies[i]; + SN.bVisible = !SN.bVisible; + ShowWindow(SN.SNHwnd, SN.bVisible ? SW_SHOWNA : SW_HIDE); JustSaveNotesEx(); } } @@ -1890,12 +1792,12 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara { HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies, I); - SN->bOnTop = !SN->bOnTop; - SetWindowPos(SN->SNHwnd, SN->bOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); - RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); + int i = ListView_GetSelectionMark(H); + if (i != -1) { + auto &SN = g_arStickies[i]; + SN.bOnTop = !SN.bOnTop; + SetWindowPos(SN.SNHwnd, SN.bOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + RedrawWindow(SN.SNHwnd, nullptr, nullptr, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); JustSaveNotesEx(); } } @@ -1913,17 +1815,16 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara return TRUE; case ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES: - PluginMenuCommandDeleteAll(0, 0); + PluginMenuCommandDeleteNotes(0, 0); return TRUE; case IDM_REMOVENOTE: { HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - OnDeleteNote(Dialog, (STICKYNOTE*)TreeGetAt(g_Stickies, I)); - } + int i = ListView_GetSelectionMark(H); + if (i != -1) + OnDeleteNote(Dialog, &g_arStickies[i]); } } return TRUE; @@ -1944,11 +1845,38 @@ static INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog, UINT Message, WPARAM wPara ///////////////////////////////////////////////////////////////////// // Notes List Dialog (uses same dialog template as reminder list) -void ListNotes(void) +INT_PTR PluginMenuCommandAddNew(WPARAM, LPARAM) +{ + NewNote(0, 0, 0, 0, nullptr, nullptr, TRUE, TRUE, 0); + return 0; +} + +INT_PTR PluginMenuCommandShowHide(WPARAM, LPARAM) +{ + ShowHideNotes(); + return 0; +} + +INT_PTR PluginMenuCommandViewNotes(WPARAM, LPARAM) { if (!ListNotesVisible) { CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_LISTREMINDERS), nullptr, DlgProcViewNotes); ListNotesVisible = TRUE; } else BringWindowToTop(LV); + return 0; +} + +INT_PTR PluginMenuCommandAllBringFront(WPARAM, LPARAM) +{ + BringAllNotesToFront(nullptr); + return 0; +} + +INT_PTR PluginMenuCommandDeleteNotes(WPARAM, LPARAM) +{ + if (g_arStickies.getCount()) + if (IDOK == MessageBox(nullptr, TranslateT("Are you sure you want to delete all notes?"), TranslateT(SECTIONNAME), MB_OKCANCEL)) + DeleteNotes(); + return 0; } -- cgit v1.2.3