diff options
author | George Hazan <ghazan@miranda.im> | 2019-03-18 18:26:23 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-03-18 18:26:23 +0300 |
commit | 69b001f36e1df9bfc80effd0e7fb5333335c2d87 (patch) | |
tree | b4e27eacfca1e812104b3a81d47a75d580f111bc /plugins/NotesAndReminders/src/notes.cpp | |
parent | e69e0ee825baad1b09849eeb126df9a08501208b (diff) |
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
Diffstat (limited to 'plugins/NotesAndReminders/src/notes.cpp')
-rw-r--r-- | plugins/NotesAndReminders/src/notes.cpp | 1726 |
1 files changed, 827 insertions, 899 deletions
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) } }; -TREEELEMENT *g_Stickies = nullptr; +///////////////////////////////////////////////////////////////////////////////////////// -LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam); +struct STICKYNOTEFONT : public MZeroedObject +{ + HFONT hFont; + char size; + BYTE style; // see the DBFONTF_* flags + BYTE charset; + char szFace[LF_FACESIZE]; +}; -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); +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); + } + } +}; + +static OBJLIST<STICKYNOTE> g_arStickies(1, PtrKeySortT); + +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,413 +215,11 @@ 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) -{ - WNDCLASSEX TWC = { 0 }; - WINDOWPLACEMENT TWP; - DWORD L1, L2; - SYSTEMTIME tm; - - const BOOL bIsStartup = bVisible & 0x10000; - bVisible &= ~0x10000; - - char *TData = Data; - - 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; - } - - 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); - } - - STICKYNOTE *TSN = (STICKYNOTE*)malloc(sizeof(STICKYNOTE)); - - if (ID) { - TSN->ID = *ID; - } - else { - GetSystemTime(&tm); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&TSN->ID); - } - - EnsureUniqueID(TSN); - - TreeAdd(&g_Stickies, TSN); - - if (!TData) { - TData = _strdup(""); - TSN->data = TData; - } - else - TSN->data = TData; - - // init note title (time-stamp) - if (Title) { - TSN->title = Title; - TSN->CustomTitle = TRUE; - } - else { - TSN->title = nullptr; - InitNoteTitle(TSN); - } - - TSN->bVisible = bVisible; - TSN->bOnTop = bOnTop; - - TSN->BgColor = bgClr; - TSN->FgColor = fgClr; - - TSN->pCustomFont = pCustomFont; - - L1 = WS_EX_TOOLWINDOW; - if (g_Transparency < 255) L1 |= WS_EX_LAYERED; - if (bOnTop) L1 |= WS_EX_TOPMOST; - - L2 = WS_POPUP | WS_THICKFRAME | WS_CAPTION; - - // 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); - - if (g_Transparency < 255) - SetLayeredWindowAttributes(TSN->SNHwnd, 0, (BYTE)g_Transparency, LWA_ALPHA); - - // 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); - - 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); - - SetWindowPos(TSN->SNHwnd, nullptr, Ax, Ay, Aw, Ah, SWP_NOZORDER | SWP_NOACTIVATE); - } - } - - if (bVisible) { - ShowWindow(TSN->SNHwnd, SW_SHOWNA); - - // 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); - } - - if (scrollV) - SendMessage(TSN->REHwnd, EM_LINESCROLL, 0, scrollV); - - // make sure that any event triggered by init doesn't cause a meaningless save - KillTimer(TSN->SNHwnd, 1025); - - if (!bLoading) { - NOTIFY_LIST(); - } - - return TSN; -} - -STICKYNOTE* NewNote(int Ax, int Ay, int Aw, int Ah, char *Data, ULARGE_INTEGER *ID, BOOL bVisible, BOOL bOnTop, int scrollV) -{ - return NewNoteEx(Ax, Ay, Aw, Ah, Data, ID, bVisible, bOnTop, scrollV, 0, 0, nullptr, nullptr, FALSE); -} - -void LoadNotes(BOOL bIsStartup) -{ - WORD Size = 0; - char *Value = nullptr, *TVal = nullptr; - char ValueName[32]; - - g_Stickies = nullptr; - - int NotesCount = g_plugin.getDword("NotesData", 0); - - for (int I = 0; I < NotesCount; I++) { - char *DelPos; - - mir_snprintf(ValueName, "NotesData%d", I); - - if (Value) { - FreeSettingBlob(Size, Value); - Value = nullptr; - } - - Size = 65535; // does not get used - - ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value); - - if (!Size || !Value) - continue; // the setting could not be read from DB -> skip - - if (Value[0] == 'X') { - // new eXtended/fleXible data format - - STICKYNOTE note = {}; - int i, rect[4]; - int scrollV = 0; - STICKYNOTEFONT *pCustomFont = nullptr; - DWORD flags; - - DelPos = strchr(Value + 1, 0x1B); - if (DelPos) - *DelPos = 0; - - // id:x:y:w:h:flags - - TVal = strchr(Value + 1, ':'); - if (!TVal || (DelPos && TVal > DelPos)) - continue; - *TVal++ = 0; - - note.ID.QuadPart = _strtoui64(Value + 1, nullptr, 16); - - for (i = 0; i<4; i++) { - char *sep = strchr(TVal, ':'); - if (!sep || (DelPos && sep > DelPos)) - goto skip; - *sep++ = 0; - - rect[i] = strtol(TVal, nullptr, 10); - - 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:<data> - - 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(""); - - note.bVisible = note.bVisible && (!bIsStartup || g_ShowNotesAtStart); - if (bIsStartup) - note.bVisible |= 0x10000; - - 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 Tx, Ty, Tw, Th, TV, OT; - BOOL V; - char *Data, *ID; - ULARGE_INTEGER newid; - - OT = 1; TV = 1; - Tx = 100; Ty = 100; - Tw = 179; Th = 35; - Data = nullptr; ID = nullptr; - - if (DelPos = strchr(Value, 0x1B)) { // get first delimiter - // int PartLen = DelPos - TVal; - - Data = nullptr; - ID = nullptr; - TVal = Value; - DelPos[0] = 0x0; - Tx = strtol(TVal, nullptr, 10); - - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Ty = strtol(TVal, nullptr, 10); - - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Tw = strtol(TVal, nullptr, 10); - - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Th = strtol(TVal, nullptr, 10); - - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - TV = strtol(TVal, nullptr, 10); - - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - OT = strtol(TVal, nullptr, 10); - - TVal = DelPos + 1; - DelPos = strchr(TVal, 0x1B); - if (!DelPos) continue; // setting is broken, do not crash - DelPos[0] = 0x0; - Data = _strdup(TVal); - - TVal = DelPos + 1; - ID = TVal; - - V = (BOOL)TV && (!bIsStartup || g_ShowNotesAtStart); - - if (bIsStartup) - V |= 0x10000; - - // 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; - - ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0; - - 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); - - 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:; - } - - if (Value) - FreeSettingBlob(Size, Value); // we do not leak on bad setting - - NOTIFY_LIST(); -} - void CloseNotesList() { if (ListNotesVisible) { @@ -604,30 +228,13 @@ void CloseNotesList() } } -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; -} - void PurgeNotes(void) { char ValueName[16]; int NotesCount = g_plugin.getDword("NotesData", 0); - for (int I = 0; I < NotesCount; I++) { - mir_snprintf(ValueName, "NotesData%d", I); + for (int i = 0; i < NotesCount; i++) { + mir_snprintf(ValueName, "NotesData%d", i); g_plugin.delSetting(ValueName); } } @@ -636,22 +243,20 @@ void DeleteNotes(void) { PurgeNotes(); g_plugin.setDword("NotesData", 0); - PurgeNotesTree(); + g_arStickies.destroy(); NOTIFY_LIST(); } void BringAllNotesToFront(STICKYNOTE *pActive) { - if (!g_Stickies) + if (!g_arStickies.getCount()) 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; - + 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) @@ -669,47 +274,40 @@ void BringAllNotesToFront(STICKYNOTE *pActive) // pModified optionally points to the modified note that invoked the JustSaveNotesEx call static void JustSaveNotesEx(STICKYNOTE *pModified = nullptr) { - int I = 0, NotesCount = TreeGetCount(g_Stickies); - int n, l; + int i = 0, NotesCount = g_arStickies.getCount(); char ValueName[32]; - WINDOWPLACEMENT wp; - int TX, TY, TW, TH; - DWORD flags; - int SzT; - int scrollV; - char *tData; const int OldNotesCount = g_plugin.getDword("NotesData", 0); g_plugin.setDword("NotesData", NotesCount); - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next, I++) { - STICKYNOTE *pNote = (STICKYNOTE*)TTE->ptrdata; + for (auto &pNote : g_arStickies) { BOOL bDeleteTData = TRUE; - scrollV = 0; - tData = nullptr; + int scrollV = 0; + char *tData = nullptr; // window pos and size + WINDOWPLACEMENT wp; 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; + 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; // set flags - flags = 0; + DWORD flags = 0; if (pNote->bVisible) flags |= 1; if (pNote->bOnTop) flags |= 2; // get note text - SzT = GetWindowTextLength(pNote->REHwnd); + 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) - GetWindowText(pNote->REHwnd, tData, SzT + 1); + GetWindowTextA(pNote->REHwnd, tData, SzT + 1); } if (pNote == pModified) { @@ -725,68 +323,39 @@ static void JustSaveNotesEx(STICKYNOTE *pModified = nullptr) else // get current scroll position scrollV = SendMessage(pNote->REHwnd, EM_GETFIRSTVISIBLELINE, 0, 0); - char *Value = (char*)malloc(SzT + 512); - if (!Value) { - if (bDeleteTData) - SAFE_FREE((void**)&tData); - continue; - } - - 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; + CMStringA szValue; + szValue.AppendFormat("X%I64x:%d:%d:%d:%d:%x", pNote->ID.QuadPart, TX, TY, TW, TH, flags); // scroll pos - if (scrollV > 0) { - l = sprintf(Value + n, "\033""%u:%u", DATATAG_SCROLLPOS, (UINT)scrollV); //!!!!!!!!!! - if (l > 0) n += l; - } + if (scrollV > 0) + szValue.AppendFormat("\033""%u:%u", DATATAG_SCROLLPOS, (UINT)scrollV); // custom bg color - if (pNote->BgColor) { - l = sprintf(Value + n, "\033""%u:%x", DATATAG_BGCOL, (UINT)(pNote->BgColor & 0xffffff)); //!!!!!!!!!!!!! - if (l > 0) n += l; - } + if (pNote->BgColor) + szValue.AppendFormat("\033""%u:%x", DATATAG_BGCOL, (UINT)(pNote->BgColor & 0xffffff)); // custom fg color - if (pNote->FgColor) { - l = sprintf(Value + n, "\033""%u:%x", DATATAG_FGCOL, (UINT)(pNote->FgColor & 0xffffff)); //!!!!!!!!!!!!! - if (l > 0) n += l; - } + if (pNote->FgColor) + szValue.AppendFormat("\033""%u:%x", DATATAG_FGCOL, (UINT)(pNote->FgColor & 0xffffff)); if (pNote->pCustomFont) { - l = sprintf(Value + n, "\033""%u:%d:%u:%u:%s", DATATAG_FONT, + 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 (l > 0) n += l; + pNote->pCustomFont->szFace); } // custom title - if (pNote->CustomTitle && pNote->title) { - l = sprintf(Value + n, "\033""%u:%s", DATATAG_TITLE, pNote->title); //!!!!!!!!!!!!! - if (l > 0) n += l; - } + if (pNote->CustomTitle && pNote->title) + szValue.AppendFormat("\033""%u:%s", DATATAG_TITLE, pNote->title); // note text (ALWAYS PUT THIS PARAM LAST) - if (tData) { - l = sprintf(Value + n, "\033""%u:%s", DATATAG_TEXT, tData); //!!!!!!!!!!!! - if (l > 0) n += l; - } + if (tData) + szValue.AppendFormat("\033""%u:%s", DATATAG_TEXT, tData); - // 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; - } - - mir_snprintf(ValueName, "NotesData%d", NotesCount - I - 1); // we do not reverse notes in DB - - db_set_blob(0, MODULENAME, ValueName, Value, n + 1); + mir_snprintf(ValueName, "NotesData%d", i++); // we do not reverse notes in DB + db_set_blob(0, MODULENAME, ValueName, szValue.GetBuffer(), szValue.GetLength() + 1); - SAFE_FREE((void**)&Value); if (bDeleteTData) SAFE_FREE((void**)&tData); @@ -796,8 +365,8 @@ static void JustSaveNotesEx(STICKYNOTE *pModified = nullptr) } // delete any left over DB note entries - for (; I < OldNotesCount; I++) { - mir_snprintf(ValueName, "NotesData%d", I); + for (; i < OldNotesCount; i++) { + mir_snprintf(ValueName, "NotesData%d", i); g_plugin.delSetting(ValueName); } @@ -807,15 +376,7 @@ static void JustSaveNotesEx(STICKYNOTE *pModified = nullptr) 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); - } - SAFE_FREE((void**)&SN); + g_arStickies.remove(SN); JustSaveNotesEx(); NOTIFY_LIST(); } @@ -823,7 +384,7 @@ void OnDeleteNote(HWND hdlg, STICKYNOTE *SN) void ShowHideNotes(void) { - if (!g_Stickies) + if (!g_arStickies.getCount()) return; // if some notes are hidden but others visible then first make all visible @@ -831,27 +392,23 @@ void ShowHideNotes(void) UINT nHideCount = 0, nVisCount = 0; - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - if (((STICKYNOTE*)TTE->ptrdata)->bVisible) + for (auto &SN : g_arStickies) { + if (SN->bVisible) nVisCount++; else nHideCount++; } - BOOL bVisible; + bool bVisible; if (!nVisCount) - bVisible = TRUE; + bVisible = true; else if (!nHideCount) - bVisible = FALSE; + bVisible = false; else - bVisible = TRUE; + bVisible = true; int bShow = bVisible ? SW_SHOWNA : SW_HIDE; - - - for (TREEELEMENT *TTE = g_Stickies; TTE; TTE = (TREEELEMENT*)TTE->next) { - STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata; - + for (auto &SN : g_arStickies) { if ((!bVisible) != (!SN->bVisible)) { ShowWindow(SN->SNHwnd, bShow); SN->bVisible = bVisible; @@ -864,30 +421,25 @@ void ShowHideNotes(void) void SaveNotes(void) { JustSaveNotesEx(); - PurgeNotesTree(); + g_arStickies.destroy(); } - -///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// // Note Window -static int FindMenuItem(HMENU h, LPTSTR lpszName) +static int FindMenuItem(HMENU h, LPSTR lpszName) { int n = GetMenuItemCount(h); - - if (n <= 0) { + 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++) { + for (int i = 0; i < n; i++) { char s[128]; - if (GetMenuString(h, i, s, 128, MF_BYPOSITION)) { - if (!mir_strcmp(s, lpszName)) { + if (GetMenuStringA(h, i, s, 128, MF_BYPOSITION)) + if (!mir_strcmp(s, lpszName)) return (int)i; - } - } } return -1; @@ -895,10 +447,10 @@ static int FindMenuItem(HMENU h, LPTSTR lpszName) static BOOL DoContextMenu(HWND AhWnd, WPARAM, LPARAM lParam) { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(AhWnd, "ctrldata"); + STICKYNOTE *SN = (STICKYNOTE*)GetPropA(AhWnd, "ctrldata"); HMENU hMenuLoad, FhMenu, hSub; - hMenuLoad = LoadMenu(g_plugin.getInst(), "MNU_NOTEPOPUP"); + hMenuLoad = LoadMenuA(g_plugin.getInst(), "MNU_NOTEPOPUP"); FhMenu = GetSubMenu(hMenuLoad, 0); if (SN->bOnTop) @@ -917,10 +469,10 @@ static BOOL DoContextMenu(HWND AhWnd, WPARAM, LPARAM lParam) HMENU hFg = GetSubMenu(hSub, FindMenuItem(hSub, "Text Color")); for (int i = 0; i < _countof(clrPresets); i++) - InsertMenu(hBg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_BG + i, Translate(clrPresets[i].szName)); + InsertMenu(hBg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_BG + i, TranslateW(clrPresets[i].szName)); for (int i = 0; i < _countof(clrPresets); i++) - InsertMenu(hFg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_FG + i, Translate(clrPresets[i].szName)); + InsertMenu(hFg, i, MF_BYPOSITION | MF_OWNERDRAW, IDM_COLORPRESET_FG + i, TranslateW(clrPresets[i].szName)); } TranslateMenu(FhMenu); @@ -932,13 +484,13 @@ static BOOL DoContextMenu(HWND AhWnd, WPARAM, LPARAM lParam) static void MeasureColorPresetMenuItem(HWND hdlg, LPMEASUREITEMSTRUCT lpMeasureItem, struct ColorPreset *clrPresets2) { HDC hdc = GetDC(hdlg); - LPSTR lpsz = Translate(clrPresets2->szName); + wchar_t *lpsz = TranslateW(clrPresets2->szName); SIZE sz; - GetTextExtentPoint32(hdc, lpsz, (int)mir_strlen(lpsz), &sz); + GetTextExtentPoint32(hdc, lpsz, (int)mir_wstrlen(lpsz), &sz); ReleaseDC(hdlg, hdc); lpMeasureItem->itemWidth = 50 + sz.cx; - lpMeasureItem->itemHeight = (sz.cy + 2)>18 ? sz.cy + 2 : 18; + lpMeasureItem->itemHeight = (sz.cy + 2) > 18 ? sz.cy + 2 : 18; } static void PaintColorPresetMenuItem(LPDRAWITEMSTRUCT lpDrawItem, struct ColorPreset *clrPresets2) @@ -979,31 +531,30 @@ static void PaintColorPresetMenuItem(LPDRAWITEMSTRUCT lpDrawItem, struct ColorPr FillRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH)); } -static BOOL GetClipboardText_Title(char *pOut, int size) +static BOOL GetClipboardText_Title(wchar_t *pOut, int size) { BOOL bResult = FALSE; if (OpenClipboard(nullptr)) { - HANDLE hData = GetClipboardData(CF_TEXT); - LPCSTR buffer; + HANDLE hData = GetClipboardData(CF_UNICODETEXT); + wchar_t *buffer; - if (hData && (buffer = (LPCSTR)GlobalLock(hData))) { + if (hData && (buffer = (wchar_t*)GlobalLock(hData))) { // trim initial white spaces - while (*buffer && isspace(*buffer)) + while (*buffer && iswspace(*buffer)) buffer++; - size_t n = mir_strlen(buffer); + size_t n = mir_wstrlen(buffer); if (n >= size) n = size - 1; - memcpy(pOut, buffer, n); - pOut[n] = 0; + wcsncpy_s(pOut, size, buffer, _TRUNCATE); // end string on line break and convert tabs to spaces - char *p = pOut; + wchar_t *p = pOut; while (*p) { if (*p == '\r' || *p == '\n') { *p = 0; - n = mir_strlen(pOut); + n = mir_wstrlen(pOut); break; } else if (*p == '\t') { @@ -1013,7 +564,7 @@ static BOOL GetClipboardText_Title(char *pOut, int size) } // trim trailing white spaces - rtrim(pOut); + rtrimw(pOut); if (pOut[0]) bResult = TRUE; @@ -1028,14 +579,14 @@ static BOOL GetClipboardText_Title(char *pOut, int size) static void SetNoteTextControl(STICKYNOTE *SN) { - CHARFORMAT CF = { 0 }; + 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); + SetWindowTextA(SN->REHwnd, SN->data); } @@ -1051,31 +602,26 @@ static UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT msg, WPARAM, LPARAM) return 0; } - LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { + STICKYNOTE *SN = (STICKYNOTE*)GetPropA(hdlg, "ctrldata"); + switch (message) { case WM_CLOSE: return TRUE; case WM_SIZE: - { - RECT SZ; + RECT SZ; + GetClientRect(hdlg, &SZ); - GetClientRect(hdlg, &SZ); - HWND H = GetDlgItem(hdlg, 1); - MoveWindow(H, 0, 0, SZ.right, SZ.bottom, TRUE); - - KillTimer(hdlg, 1025); - SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); + MoveWindow(GetDlgItem(hdlg, 1), 0, 0, SZ.right, SZ.bottom, TRUE); - return TRUE; - } + KillTimer(hdlg, 1025); + SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, nullptr); + return TRUE; case WM_TIMER: if (wParam == 1025) { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); - KillTimer(hdlg, 1025); JustSaveNotesEx(SN); } @@ -1088,16 +634,15 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA case WM_CREATE: { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); - CREATESTRUCT *CS = (CREATESTRUCT *)lParam; DWORD mystyle; SN = (STICKYNOTE*)CS->lpCreateParams; - SetProp(hdlg, "ctrldata", (HANDLE)SN); + SetPropA(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; + 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); @@ -1127,10 +672,7 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA case WM_NCPAINT: // make window borders have the same color as caption { - STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg, "ctrldata"); - RECT rect, wr, r; - //HDC hdc = GetDCEx(hdlg, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN); HDC hdc = GetWindowDC(hdlg); GetWindowRect(hdlg, &wr); @@ -1169,15 +711,15 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA 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) + if (g_plugin.bShowNoteButtons) R.right -= 48; 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); + DrawTextW(hdc, SN->title, -1, &R, DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER); } - if (g_ShowNoteButtons) { + if (g_plugin.bShowNoteButtons) { HICON hcIcon; if (SN->bOnTop) hcIcon = IcoLib_GetIconByHandle(iconList[4].hIcolib); @@ -1227,11 +769,11 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA 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); + ShellExecuteA(hdlg, "open", "iexplore", Buff, "", SW_SHOWNORMAL); else if (g_lpszAltBrowser && *g_lpszAltBrowser) - ShellExecute(hdlg, "open", g_lpszAltBrowser, Buff, "", SW_SHOWNORMAL); + ShellExecuteA(hdlg, "open", g_lpszAltBrowser, Buff, "", SW_SHOWNORMAL); else - ShellExecute(hdlg, "open", Buff, "", "", SW_SHOWNORMAL); + ShellExecuteA(hdlg, "open", Buff, "", "", SW_SHOWNORMAL); SAFE_FREE((void**)&Buff); return TRUE; } @@ -1254,7 +796,7 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA } case WM_NCLBUTTONDOWN: - if (wParam == HTCAPTION && g_ShowNoteButtons) { + if (wParam == HTCAPTION && g_plugin.bShowNoteButtons) { long X, Y; RECT rect; int Tw; @@ -1317,243 +859,237 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA 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; - } + 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; + } - UINT id = (UINT)LOWORD(wParam); + 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; + } - HWND H = SN->REHwnd; + switch (id) { + case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE: + PluginMenuCommandAddNew(0, 0); + break; - 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; + 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(); + } } - 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; + 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; - switch (id) { - case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE: - PluginMenuCommandAddNew(0, 0); - break; - - 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; + case ID_APPEARANCE_CUSTOMFONT: + { + CHOOSEFONTA cf = {0}; + LOGFONTA lf = {0}; - 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(); + 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; } - } - 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; + 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); - 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; - - case ID_FONT_RESET: - if (SN->pCustomFont) { - DeleteObject(SN->pCustomFont->hFont); - free(SN->pCustomFont); - SN->pCustomFont = nullptr; + 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)hBodyFont, FALSE); + 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; + } + break; - 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; + 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; - case ID_CONTEXTMENUNOTEPOPUP_RESETTITLE: - if (SN->CustomTitle) { - if (SN->title) { + 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; + + 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 = nullptr; - } - InitNoteTitle(SN); + SN->title = _wcsdup(s); + SN->CustomTitle = TRUE; RedrawWindow(SN->SNHwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); JustSaveNotesEx(); } - break; - - case IDM_REMOVENOTE: - OnDeleteNote(hdlg, SN); - break; + } + break; - case IDM_HIDENOTE: - SN->bVisible = FALSE; - ShowWindow(hdlg, SW_HIDE); + 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; + } + break; - case IDM_COPY: - SendMessage(H, WM_COPY, 0, 0); - break; + case IDM_REMOVENOTE: + OnDeleteNote(hdlg, SN); + break; - case IDM_PASTE: - SendMessage(H, WM_PASTE, 0, 0); - break; + case IDM_HIDENOTE: + SN->bVisible = FALSE; + ShowWindow(hdlg, SW_HIDE); + JustSaveNotesEx(); + break; - case IDM_CUT: - SendMessage(H, WM_CUT, 0, 0); - break; + case IDM_COPY: + SendMessage(SN->REHwnd, WM_COPY, 0, 0); + break; - case IDM_CLEAR: - SendMessage(H, WM_CLEAR, 0, 0); - break; + case IDM_PASTE: + SendMessage(SN->REHwnd, WM_PASTE, 0, 0); + break; - case IDM_UNDO: - SendMessage(H, WM_UNDO, 0, 0); - break; + case IDM_CUT: + SendMessage(SN->REHwnd, WM_CUT, 0, 0); + break; - 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; + case IDM_CLEAR: + SendMessage(SN->REHwnd, WM_CLEAR, 0, 0); + break; - case ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES: - ListNotes(); - break; + case IDM_UNDO: + SendMessage(SN->REHwnd, WM_UNDO, 0, 0); + break; - case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP: - BringAllNotesToFront(SN); - break; - } - return TRUE; + 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; + + case ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES: + PluginMenuCommandViewNotes(0, 0); + break; + + case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP: + BringAllNotesToFront(SN); + break; } + return TRUE; case WM_NCDESTROY: - RemoveProp(hdlg, "ctrldata"); + RemovePropA(hdlg, "ctrldata"); break; case WM_CONTEXTMENU: @@ -1566,6 +1102,395 @@ LRESULT CALLBACK StickyNoteWndProc(HWND hdlg, UINT message, WPARAM wParam, LPARA return FALSE; } +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) +{ + WNDCLASSEX TWC = {0}; + WINDOWPLACEMENT TWP; + DWORD L1, L2; + SYSTEMTIME tm; + + const BOOL bIsStartup = bVisible & 0x10000; + bVisible &= ~0x10000; + + char *TData = Data; + + 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; + } + + 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); + } + + STICKYNOTE *TSN = new STICKYNOTE(); + + if (ID) + TSN->ID = *ID; + else { + GetSystemTime(&tm); + SystemTimeToFileTime(&tm, (FILETIME*)&TSN->ID); + } + + EnsureUniqueID(TSN); + + g_arStickies.insert(TSN); + + if (!TData) + TSN->data = _strdup(""); + else + TSN->data = TData; + + // init note title (time-stamp) + if (Title) { + TSN->title = Title; + TSN->CustomTitle = TRUE; + } + else { + TSN->title = nullptr; + InitNoteTitle(TSN); + } + + TSN->bVisible = bVisible; + TSN->bOnTop = bOnTop; + TSN->BgColor = bgClr; + TSN->FgColor = fgClr; + TSN->pCustomFont = pCustomFont; + + L1 = WS_EX_TOOLWINDOW; + if (g_Transparency < 255) L1 |= WS_EX_LAYERED; + if (bOnTop) L1 |= WS_EX_TOPMOST; + + L2 = WS_POPUP | WS_THICKFRAME | WS_CAPTION; + + // 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); + + if (g_Transparency < 255) + SetLayeredWindowAttributes(TSN->SNHwnd, 0, (BYTE)g_Transparency, LWA_ALPHA); + + // 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); + + 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); + + SetWindowPos(TSN->SNHwnd, nullptr, Ax, Ay, Aw, Ah, SWP_NOZORDER | SWP_NOACTIVATE); + } + } + + if (bVisible) { + ShowWindow(TSN->SNHwnd, SW_SHOWNA); + + // 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); + } + + if (scrollV) + SendMessage(TSN->REHwnd, EM_LINESCROLL, 0, scrollV); + + // make sure that any event triggered by init doesn't cause a meaningless save + KillTimer(TSN->SNHwnd, 1025); + + if (!bLoading) { + NOTIFY_LIST(); + } + + return TSN; +} + +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); +} + +void LoadNotes(BOOL bIsStartup) +{ + WORD Size = 0; + char *Value = nullptr, *TVal = nullptr; + char ValueName[32]; + + g_arStickies.destroy(); + + int NotesCount = g_plugin.getDword("NotesData", 0); + + for (int i = 0; i < NotesCount; i++) { + mir_snprintf(ValueName, "NotesData%d", i); + + if (Value) { + FreeSettingBlob(Size, Value); + Value = nullptr; + } + + Size = 65535; // does not get used + + ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value); + + if (!Size || !Value) + continue; // the setting could not be read from DB -> skip + + if (Value[0] == 'X') { + // new eXtended/fleXible data format + + int scrollV = 0; + STICKYNOTEFONT *pCustomFont = nullptr; + + char *DelPos = strchr(Value + 1, 0x1B); + if (DelPos) + *DelPos = 0; + + // id:x:y:w:h:flags + + TVal = strchr(Value + 1, ':'); + if (!TVal || (DelPos && TVal > DelPos)) + continue; + + *TVal++ = 0; + ULARGE_INTEGER id; + id.QuadPart = _strtoui64(Value + 1, nullptr, 16); + + int rect[4]; + for (auto &it : rect) { + char *sep = strchr(TVal, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; + *sep++ = 0; + + it = strtol(TVal, nullptr, 10); + TVal = sep; + } + + BOOL bVisible = 0, bOnTop = 0; + DWORD flags = strtoul(TVal, nullptr, 16); + if (flags & 1) + bVisible = TRUE; + if (flags & 2) + bOnTop = TRUE; + + // optional \033 separated params + char *data = 0; + wchar_t *title = 0; + COLORREF BgColor = 0, FgColor = 0; + + 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; + + // tag:<data> + char *sep = strchr(TVal, ':'); + if (!sep || (DelPos && sep > DelPos)) + goto skip; + + UINT tag = strtoul(TVal, nullptr, 10); + TVal = sep + 1; + + switch (tag) { + case DATATAG_TEXT: + data = _strdup(TVal); + break; + + 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; + } + } + + if (!data) + data = _strdup(""); + + bVisible = bVisible && (!bIsStartup || g_plugin.bShowNotesAtStart); + if (bIsStartup) + bVisible |= 0x10000; + + 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) + + int Tx, Ty, Tw, Th, TV, OT; + BOOL V; + char *Data, *ID; + ULARGE_INTEGER newid; + + OT = 1; TV = 1; + Tx = 100; Ty = 100; + Tw = 179; Th = 35; + Data = nullptr; ID = nullptr; + + if (char *DelPos = strchr(Value, 0x1B)) { // get first delimiter + Data = nullptr; + ID = nullptr; + TVal = Value; + DelPos[0] = 0x0; + Tx = strtol(TVal, nullptr, 10); + + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Ty = strtol(TVal, nullptr, 10); + + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Tw = strtol(TVal, nullptr, 10); + + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Th = strtol(TVal, nullptr, 10); + + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + TV = strtol(TVal, nullptr, 10); + + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + OT = strtol(TVal, nullptr, 10); + + TVal = DelPos + 1; + DelPos = strchr(TVal, 0x1B); + if (!DelPos) continue; // setting is broken, do not crash + DelPos[0] = 0x0; + Data = _strdup(TVal); + + TVal = DelPos + 1; + ID = TVal; + + V = (BOOL)TV && (!bIsStartup || g_plugin.bShowNotesAtStart); + + if (bIsStartup) + V |= 0x10000; + + // 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; + + ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0; + + 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); + + 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:; + } + + 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; } |