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/globals.h | 145 -- plugins/NotesAndReminders/src/hotkeys.cpp | 33 +- plugins/NotesAndReminders/src/main.cpp | 138 +- plugins/NotesAndReminders/src/miscutils.cpp | 155 +- plugins/NotesAndReminders/src/miscutils.h | 22 +- plugins/NotesAndReminders/src/notes.cpp | 2434 +++++++++++++-------------- plugins/NotesAndReminders/src/options.cpp | 299 ++-- plugins/NotesAndReminders/src/reminders.cpp | 1857 +++++++++----------- plugins/NotesAndReminders/src/stdafx.cpp | 18 - plugins/NotesAndReminders/src/stdafx.cxx | 18 + plugins/NotesAndReminders/src/stdafx.h | 113 ++ plugins/NotesAndReminders/src/version.h | 2 +- 12 files changed, 2328 insertions(+), 2906 deletions(-) delete mode 100644 plugins/NotesAndReminders/src/globals.h delete mode 100644 plugins/NotesAndReminders/src/stdafx.cpp create mode 100644 plugins/NotesAndReminders/src/stdafx.cxx create mode 100644 plugins/NotesAndReminders/src/stdafx.h (limited to 'plugins/NotesAndReminders/src') diff --git a/plugins/NotesAndReminders/src/globals.h b/plugins/NotesAndReminders/src/globals.h deleted file mode 100644 index c774dcffb7..0000000000 --- a/plugins/NotesAndReminders/src/globals.h +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#define _CRT_SECURE_NO_WARNINGS - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "miscutils.h" -#include "resource.h" -#include "version.h" - -#define MODULENAME "StickyNotes" -#define SECTIONNAME LPGEN("Notes & Reminders") - -struct CMPlugin : public PLUGIN -{ - CMPlugin(); - - int Load() override; - int Unload() override; -}; - -// normal timer interval for reminder update processing -#define REMINDER_UPDATE_INTERVAL 10000 -// short timer interval for reminder updates used as long as there are pending alarams in the event queue -#define REMINDER_UPDATE_INTERVAL_SHORT 5000 - - -// font IDs used with LoadNRFont -#define NR_FONTID_CAPTION 0 -#define NR_FONTID_BODY 1 -#define NR_FONTID_MAX NR_FONTID_BODY - -typedef struct { - HFONT hFont; - char size; - BYTE style; // see the DBFONTF_* flags - BYTE charset; - char szFace[LF_FACESIZE]; -} STICKYNOTEFONT; - -typedef struct { - HWND SNHwnd,REHwnd; - BOOL bVisible, bOnTop; - char *data; - ULARGE_INTEGER ID; // FILETIME in UTC - char *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; - -typedef struct { - HWND handle; - BOOL RemVisible; - DWORD uid; - char *Reminder; - ULARGE_INTEGER When; // FILETIME in UTC - UINT RepeatSound; - UINT RepeatSoundTTL; - int SoundSel; // -1 if sound disabled - BOOL SystemEventQueued; -} REMINDERDATA; - - -extern void CreateMsgWindow(void); -extern void DestroyMsgWindow(void); - -extern STICKYNOTE* NewNote(int Ax,int Ay,int Aw,int Ah,char *Data, ULARGE_INTEGER *ID,BOOL Visible,BOOL bOnTop,int scrollV); - -extern void LoadNotes(BOOL bIsStartup); -extern void SaveNotes(void); -extern void DeleteNotes(void); -extern void ShowHideNotes(void); -extern void ListNotes(void); - -extern void NewReminder(void); -extern void LoadReminders(void); -extern void SaveReminders(void); -extern void DeleteReminders(void); -extern void ListReminders(void); -extern BOOL CheckRemindersAndStart(void); - -extern void InitSettings(void); -extern void TermSettings(void); -extern INT_PTR CALLBACK DlgProcOptions(HWND hdlg,UINT message, WPARAM wParam,LPARAM lParam); -extern void LoadNRFont(int i, LOGFONT *lf, COLORREF *colour); - -extern BOOL WS_Init(); -extern void WS_CleanUp(); - -extern LPCSTR GetDateFormatStr(); -extern LPCSTR GetTimeFormatStr(); - -extern HINSTANCE hmiranda; - -extern BOOL g_CloseAfterAddReminder, g_UseDefaultPlaySound; -extern HICON g_hReminderIcon; - -extern LOGFONT lfBody,lfCaption; -extern HFONT hBodyFont,hCaptionFont; - -extern long BodyColor; -extern long CaptionFontColor,BodyFontColor; - -extern BOOL g_ShowNotesAtStart,g_ShowScrollbar,g_AddContListMI,g_ShowNoteButtons; -extern int g_NoteTitleDate, g_NoteTitleTime; - -extern int g_NoteWidth,g_NoteHeight; - -extern int g_Transparency; - -extern char *g_RemindSMS; - -extern char *g_lpszAltBrowser; - -extern int g_reminderListGeom[4]; -extern int g_reminderListColGeom[2]; -extern int g_notesListGeom[4]; -extern int g_notesListColGeom[4]; - -extern HWND HKHwnd; -extern IconItem iconList[]; - -// these defs are only used to emphasize that SYSTEMTIMEtoFILETIME/FILETIMEtoSYSTEMTIME only convert the data type, -// it does not apply any time conversion/correction like UTC to local etc. (if input is local, then output is local too) -#define SYSTEMTIMEtoFILETIME SystemTimeToFileTime -#define FILETIMEtoSYSTEMTIME FileTimeToSystemTime diff --git a/plugins/NotesAndReminders/src/hotkeys.cpp b/plugins/NotesAndReminders/src/hotkeys.cpp index 6e17e03f55..905faca305 100644 --- a/plugins/NotesAndReminders/src/hotkeys.cpp +++ b/plugins/NotesAndReminders/src/hotkeys.cpp @@ -1,6 +1,6 @@ -#include "globals.h" +#include "stdafx.h" -#define MSG_WND_CLASS "MIM_SNMsgWindow" +#define MSG_WND_CLASS L"MIM_SNMsgWindow" HWND HKHwnd; @@ -57,27 +57,14 @@ LRESULT CALLBACK NotifyHotKeyWndProc(HWND AHwnd, UINT Message, WPARAM wParam, LP void CreateMsgWindow(void) { - HWND hParent = nullptr; - WNDCLASSEX TWC = { 0 }; - - if (!GetClassInfoEx(hmiranda, MSG_WND_CLASS, &TWC)) { - TWC.style = 0; - TWC.cbClsExtra = 0; - TWC.cbWndExtra = 0; - TWC.hInstance = hmiranda; - TWC.hIcon = nullptr; - TWC.hCursor = nullptr; - TWC.hbrBackground = nullptr; - TWC.lpszMenuName = nullptr; - TWC.lpszClassName = MSG_WND_CLASS; - TWC.cbSize = sizeof(TWC); - TWC.lpfnWndProc = NotifyHotKeyWndProc; - RegisterClassEx(&TWC); - } - - hParent = HWND_MESSAGE; - - HKHwnd = CreateWindowEx(WS_EX_TOOLWINDOW, MSG_WND_CLASS, "StickyNotes", 0, 0, 0, 0, 0, hParent, nullptr, hmiranda, nullptr); + WNDCLASSEX TWC = {}; + TWC.cbSize = sizeof(TWC); + TWC.hInstance = hmiranda; + TWC.lpszClassName = MSG_WND_CLASS; + TWC.lpfnWndProc = NotifyHotKeyWndProc; + RegisterClassEx(&TWC); + + HKHwnd = CreateWindowExW(WS_EX_TOOLWINDOW, MSG_WND_CLASS, L"StickyNotes", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, hmiranda, nullptr); SetTimer(HKHwnd, 1026, REMINDER_UPDATE_INTERVAL, nullptr); } diff --git a/plugins/NotesAndReminders/src/main.cpp b/plugins/NotesAndReminders/src/main.cpp index 66f764a94f..28009ca443 100644 --- a/plugins/NotesAndReminders/src/main.cpp +++ b/plugins/NotesAndReminders/src/main.cpp @@ -1,13 +1,10 @@ -#include "globals.h" +#include "stdafx.h" CMPlugin g_plugin; HINSTANCE hmiranda = nullptr; HMODULE hRichedDll = nullptr; -extern TREEELEMENT *g_Stickies; -extern TREEELEMENT *RemindersList; - ///////////////////////////////////////////////////////////////////////////////////////// static PLUGININFOEX pluginInfoEx = @@ -23,71 +20,14 @@ static PLUGININFOEX pluginInfoEx = {0x842a6668, 0xf9da, 0x4968, {0xbf, 0xd7, 0xd2, 0xbd, 0x9d, 0xf8, 0x48, 0xee}} // {842A6668-F9DA-4968-BFD7-D2BD9DF848EE} }; -CMPlugin::CMPlugin() : - PLUGIN(MODULENAME, pluginInfoEx) -{} - ///////////////////////////////////////////////////////////////////////////////////////// void RegisterFontServiceFonts(); void RegisterKeyBindings(); INT_PTR OpenTriggeredReminder(WPARAM w, LPARAM l); -void BringAllNotesToFront(STICKYNOTE *pActive); void CloseNotesList(); void CloseReminderList(); -INT_PTR PluginMenuCommandAddNew(WPARAM, LPARAM) -{ - STICKYNOTE *PSN = NewNote(0,0,0,0,nullptr,nullptr,TRUE,TRUE,0); - if(PSN) - SetFocus(PSN->REHwnd); - return 0; -} - -INT_PTR PluginMenuCommandDeleteAll(WPARAM, LPARAM) -{ - if (g_Stickies && MessageBox(nullptr, Translate("Are you sure you want to delete all notes?"), Translate(SECTIONNAME), MB_OKCANCEL) == IDOK) - DeleteNotes(); - return 0; -} - -static INT_PTR PluginMenuCommandShowHide(WPARAM, LPARAM) -{ - ShowHideNotes(); - return 0; -} - -static INT_PTR PluginMenuCommandViewNotes(WPARAM, LPARAM) -{ - ListNotes(); - return 0; -} - -static INT_PTR PluginMenuCommandAllBringFront(WPARAM, LPARAM) -{ - BringAllNotesToFront(nullptr); - return 0; -} - -static INT_PTR PluginMenuCommandNewReminder(WPARAM, LPARAM) -{ - NewReminder(); - return 0; -} - -static INT_PTR PluginMenuCommandViewReminders(WPARAM, LPARAM) -{ - ListReminders(); - return 0; -} - -static INT_PTR PluginMenuCommandDeleteReminders(WPARAM, LPARAM) -{ - if (RemindersList && MessageBox(nullptr, Translate("Are you sure you want to delete all reminders?"), Translate(SECTIONNAME), MB_OKCANCEL) == IDOK) - DeleteReminders(); - return 0; -} - IconItem iconList[] = { { LPGEN("New Reminder"), "AddReminder", IDI_ADDREMINDER }, @@ -113,18 +53,6 @@ void InitIcons(void) g_plugin.registerIcon(LPGEN("Sticky Notes"), iconList, MODULENAME); } -static int OnOptInitialise(WPARAM w, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 900002000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_STNOTEOPTIONS); - odp.szTitle.a = SECTIONNAME; - odp.szGroup.a = LPGEN("Plugins"); - odp.pfnDlgProc = DlgProcOptions; - g_plugin.addOptions(w, &odp); - return 0; -} - int OnTopToolBarInit(WPARAM, LPARAM) { TTBButton ttb = {}; @@ -142,38 +70,11 @@ int OnTopToolBarInit(WPARAM, LPARAM) return 0; } -static void InitServices() -{ - // register sounds - - g_plugin.addSound("AlertReminder", LPGENW("Alerts"), LPGENW("Reminder triggered")); - g_plugin.addSound("AlertReminder2", LPGENW("Alerts"), LPGENW("Reminder triggered (Alternative 1)")); - g_plugin.addSound("AlertReminder3", LPGENW("Alerts"), LPGENW("Reminder triggered (Alternative 2)")); - - // register menu command services - - CreateServiceFunction(MODULENAME"/MenuCommandAddNew", PluginMenuCommandAddNew); - CreateServiceFunction(MODULENAME"/MenuCommandShowHide", PluginMenuCommandShowHide); - CreateServiceFunction(MODULENAME"/MenuCommandViewNotes", PluginMenuCommandViewNotes); - CreateServiceFunction(MODULENAME"/MenuCommandDeleteAll", PluginMenuCommandDeleteAll); - CreateServiceFunction(MODULENAME"/MenuCommandBringAllFront", PluginMenuCommandAllBringFront); - - // - - CreateServiceFunction(MODULENAME"/MenuCommandNewReminder", PluginMenuCommandNewReminder); - CreateServiceFunction(MODULENAME"/MenuCommandViewReminders", PluginMenuCommandViewReminders); - CreateServiceFunction(MODULENAME"/MenuCommandDeleteReminders", PluginMenuCommandDeleteReminders); - - // register misc - - CreateServiceFunction(MODULENAME"/OpenTriggeredReminder", OpenTriggeredReminder); -} - ///////////////////////////////////////////////////////////////////////////////////////// static void addMenuItem(CMenuItem &mi) { - if (g_AddContListMI) { + if (g_plugin.bAddContListMI) { HGENMENU save = mi.root; mi.root = nullptr; Menu_AddContactMenuItem(&mi); mi.root = save; @@ -187,8 +88,6 @@ int OnModulesLoaded(WPARAM, LPARAM) RegisterFontServiceFonts(); RegisterKeyBindings(); - g_AddContListMI = (BOOL)g_plugin.getDword("AddContactMenuItems", 1); - // register menus CMenuItem mi(&g_plugin); mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("Notes && Reminders"), 1600000000); @@ -200,48 +99,56 @@ int OnModulesLoaded(WPARAM, LPARAM) mi.name.w = LPGENW("New &Note"); mi.pszService = MODULENAME"/MenuCommandAddNew"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandAddNew); mi.position = 1600000001; mi.hIcolibItem = iconList[0].hIcolib; mi.name.w = LPGENW("New &Reminder"); mi.pszService = MODULENAME"/MenuCommandNewReminder"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandNewReminder); mi.position = 1600100000; mi.hIcolibItem = iconList[3].hIcolib; mi.name.w = LPGENW("&Show / Hide Notes"); mi.pszService = MODULENAME"/MenuCommandShowHide"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandShowHide); mi.position = 1600100001; mi.hIcolibItem = iconList[13].hIcolib; mi.name.w = LPGENW("Vie&w Notes"); mi.pszService = MODULENAME"/MenuCommandViewNotes"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandViewNotes); mi.position = 1600100002; mi.hIcolibItem = iconList[1].hIcolib; mi.name.w = LPGENW("&Delete All Notes"); mi.pszService = MODULENAME"/MenuCommandDeleteAll"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandDeleteNotes); mi.position = 1600100003; mi.hIcolibItem = iconList[11].hIcolib; mi.name.w = LPGENW("&Bring All to Front"); mi.pszService = MODULENAME"/MenuCommandBringAllFront"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandAllBringFront); mi.position = 1600200000; mi.hIcolibItem = iconList[6].hIcolib; mi.name.w = LPGENW("&View Reminders"); mi.pszService = MODULENAME"/MenuCommandViewReminders"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandViewReminders); mi.position = 1600200001; mi.hIcolibItem = iconList[5].hIcolib; mi.name.w = LPGENW("D&elete All Reminders"); mi.pszService = MODULENAME"/MenuCommandDeleteReminders"; addMenuItem(mi); + CreateServiceFunction(mi.pszService, PluginMenuCommandDeleteReminders); // register misc HookEvent("TopToolBar/ModuleLoaded", OnTopToolBarInit); @@ -256,6 +163,19 @@ int OnModulesLoaded(WPARAM, LPARAM) ///////////////////////////////////////////////////////////////////////////////////////// +CMPlugin::CMPlugin() : + PLUGIN(MODULENAME, pluginInfoEx), + bAddContListMI(MODULENAME, "AddContactMenuItems", true), + bShowNotesAtStart(MODULENAME, "ShowNotesAtStart", true), + bShowNoteButtons(MODULENAME, "ShowNoteButtons", true), + bShowScrollbar(MODULENAME, "ShowScrollbar", true), + bCloseAfterAddReminder(MODULENAME, "CloseAfterAddReminder", true), + bUseMSI(MODULENAME, "UseMCI", true) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + int CMPlugin::Load() { hmiranda = GetModuleHandle(nullptr); @@ -265,14 +185,20 @@ int CMPlugin::Load() ctrls.dwICC = ICC_DATE_CLASSES; InitCommonControlsEx(&ctrls); - hRichedDll = LoadLibrary("Msftedit.dll"); + hRichedDll = LoadLibraryA("Msftedit.dll"); if (!hRichedDll) { - if (MessageBox(nullptr, Translate("Miranda could not load the Notes & Reminders plugin, Msftedit.dll is missing. If you are using WINE, please make sure you have Msftedit.dll installed. Press 'Yes' to continue loading Miranda."), SECTIONNAME, MB_YESNO | MB_ICONINFORMATION) != IDYES) + if (MessageBox(nullptr, TranslateT("Miranda could not load the Notes & Reminders plugin, Msftedit.dll is missing. If you are using WINE, please make sure you have Msftedit.dll installed. Press 'Yes' to continue loading Miranda."), _A2W(SECTIONNAME), MB_YESNO | MB_ICONINFORMATION) != IDYES) return 1; return 0; } - InitServices(); + CreateServiceFunction(MODULENAME"/OpenTriggeredReminder", OpenTriggeredReminder); + + // register sounds + g_plugin.addSound("AlertReminder", LPGENW("Alerts"), LPGENW("Reminder triggered")); + g_plugin.addSound("AlertReminder2", LPGENW("Alerts"), LPGENW("Reminder triggered (Alternative 1)")); + g_plugin.addSound("AlertReminder3", LPGENW("Alerts"), LPGENW("Reminder triggered (Alternative 2)")); + WS_Init(); HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); diff --git a/plugins/NotesAndReminders/src/miscutils.cpp b/plugins/NotesAndReminders/src/miscutils.cpp index 04d08f00ed..3b86ad8207 100644 --- a/plugins/NotesAndReminders/src/miscutils.cpp +++ b/plugins/NotesAndReminders/src/miscutils.cpp @@ -1,22 +1,4 @@ -#include "globals.h" - -WORD ConvertHotKeyToControl(WORD HK) -{ - WORD R = 0; - if ((HK & MOD_CONTROL) == MOD_CONTROL) R = R | HOTKEYF_CONTROL; - if ((HK & MOD_ALT) == MOD_ALT) R = R | HOTKEYF_ALT; - if ((HK & MOD_SHIFT) == MOD_SHIFT) R = R | HOTKEYF_SHIFT; - return R; -} - -WORD ConvertControlToHotKey(WORD HK) -{ - WORD R = 0; - if ((HK & HOTKEYF_CONTROL) == HOTKEYF_CONTROL) R = R | MOD_CONTROL; - if ((HK & HOTKEYF_ALT) == HOTKEYF_ALT) R = R | MOD_ALT; - if ((HK & HOTKEYF_SHIFT) == HOTKEYF_SHIFT) R = R | MOD_SHIFT; - return R; -} +#include "stdafx.h" void FreeSettingBlob(WORD pSize, void *pbBlob) { @@ -65,101 +47,72 @@ bool ReadSettingIntArray(MCONTACT hContact, char *ModuleName, char *SettingName, return bResult; } -void TreeAdd(TREEELEMENT **root, void *Data) +///////////////////////////////////////////////////////////////////// +// Email/SMS and WinSock functions + +BOOL WS_Init() { - TREEELEMENT *NTE = (TREEELEMENT*)malloc(sizeof(TREEELEMENT)); - if (NTE) { - NTE->ptrdata = Data; - NTE->next = *root; - *root = NTE; - } + WSADATA wsd; + if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) return FALSE; + return TRUE; } -void TreeAddSorted(TREEELEMENT **root, void *Data, int(*CompareCb)(TREEELEMENT*, TREEELEMENT*)) +void WS_CleanUp() { - if (!*root) { - // list empty, just add normally - TreeAdd(root, Data); - return; - } - - TREEELEMENT *NTE = (TREEELEMENT*)malloc(sizeof(TREEELEMENT)); - if (!NTE) - return; - - NTE->ptrdata = Data; - NTE->next = nullptr; - - // insert sorted - - TREEELEMENT *Prev = nullptr; - TREEELEMENT *TTE = *root; - - while (TTE) { - if (CompareCb(NTE, TTE) < 0) { - if (Prev) { - Prev->next = NTE; - NTE->next = TTE; - } - else { - // first in list - NTE->next = TTE; - *root = NTE; - } - return; - } - - Prev = TTE; - TTE = (TREEELEMENT*)TTE->next; - } - - // add last - Prev->next = NTE; + WSACleanup(); } -void TreeDelete(TREEELEMENT **root, void *iItem) +int WS_Send(SOCKET s, char *data, int datalen) { - TREEELEMENT *TTE = *root, *Prev = nullptr; - if (!TTE) - return; - - while ((TTE) && (TTE->ptrdata != iItem)) { - Prev = TTE; - TTE = (TREEELEMENT*)TTE->next; - } - if (TTE) { - if (Prev) - Prev->next = TTE->next; - else - *root = (TREEELEMENT*)TTE->next; - SAFE_FREE((void**)&TTE); - } + int rlen; + if ((rlen = send(s, data, datalen, 0)) == SOCKET_ERROR) return FALSE; + return TRUE; } -void *TreeGetAt(TREEELEMENT *root, int iItem) +unsigned long WS_ResolveName(char *name, WORD *port, int defaultPort) { - if (!root) - return nullptr; + char *nameCopy = _strdup(name); + if (port != nullptr) + *port = defaultPort; + char *pcolon = strchr(nameCopy, ':'); + if (pcolon != nullptr) { + if (port != nullptr) *port = atoi(pcolon + 1); + *pcolon = 0; + } + if (inet_addr(nameCopy) == INADDR_NONE) { + HOSTENT *lk = gethostbyname(nameCopy); + if (lk == nullptr) + return SOCKET_ERROR; - TREEELEMENT *TTE = root; - int i = 0; - while ((TTE) && (i != iItem)) { - TTE = (TREEELEMENT*)TTE->next; - i++; + free(nameCopy); + return *(u_long*)lk->h_addr_list[0]; } - return (!TTE) ? nullptr : TTE->ptrdata; + DWORD ret = inet_addr(nameCopy); + free(nameCopy); + return ret; } -int TreeGetCount(TREEELEMENT *root) +void Send(char *user, char *host, char *Msg, char *server) { - if (!root) - return 0; - - int i = 0; - TREEELEMENT *TTE = root; - while (TTE) { - TTE = (TREEELEMENT*)TTE->next; - i++; - } - return i; + SOCKADDR_IN sockaddr; + WORD port; + char *ch = nullptr; + SOCKET S = socket(AF_INET, SOCK_STREAM, 0); + if (!server) server = host; + if ((sockaddr.sin_addr.S_un.S_addr = WS_ResolveName(server, + &port, 25)) == SOCKET_ERROR) return; + sockaddr.sin_port = htons(port); + sockaddr.sin_family = AF_INET; + if (connect(S, (SOCKADDR*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) return; + ch = (char*)malloc(mir_strlen(user) + mir_strlen(host) + 16); + ch = (char*)realloc(ch, sprintf(ch, "rcpt to:%s@%s\r\n", user, host)); //!!!!!!!!!! + WS_Send(S, "mail from: \r\n", 13); + WS_Send(S, ch, (int)mir_strlen(ch)); + WS_Send(S, "data\r\n", 6); + WS_Send(S, "From:\r\n\r\n", 14); + WS_Send(S, Msg, (int)mir_strlen(Msg)); + WS_Send(S, "\r\n.\r\n", 5); + WS_Send(S, "quit\r\n", 6); + SAFE_FREE((void**)&ch); + closesocket(S); } diff --git a/plugins/NotesAndReminders/src/miscutils.h b/plugins/NotesAndReminders/src/miscutils.h index 692de31a1f..0d9dace18d 100644 --- a/plugins/NotesAndReminders/src/miscutils.h +++ b/plugins/NotesAndReminders/src/miscutils.h @@ -3,33 +3,15 @@ void ReadSettingBlob(MCONTACT hContact, char *ModuleName, char *SettingName, WORD *pSize, void **pbBlob); void FreeSettingBlob(WORD pSize,void * pbBlob); -BOOL ReadSettingBool(MCONTACT hContact,char *ModuleName, - char *SettingName,BOOL Default); -void WriteSettingBool(MCONTACT hContact,char *ModuleName, - char *SettingName,BOOL Value); + void WriteSettingIntArray(MCONTACT hContact,char *ModuleName, char *SettingName,const int *Value, int Size); bool ReadSettingIntArray(MCONTACT hContact,char *ModuleName, char *SettingName,int *Value, int Size); -WORD ConvertHotKeyToControl(WORD HK); -WORD ConvertControlToHotKey(WORD HK); - -typedef struct { - void *ptrdata; - void *next; -} TREEELEMENT; - -void TreeAdd(TREEELEMENT **root,void *Data); -void TreeAddSorted(TREEELEMENT **root,void *Data,int (*CompareCb)(TREEELEMENT*,TREEELEMENT*)); -void TreeDelete(TREEELEMENT **root,void *Item); -void *TreeGetAt(TREEELEMENT *root,int Item); -int TreeGetCount(TREEELEMENT *root); - static void __inline SAFE_FREE(void** p) { - if (*p) - { + if (*p) { free(*p); *p = nullptr; } 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; } diff --git a/plugins/NotesAndReminders/src/options.cpp b/plugins/NotesAndReminders/src/options.cpp index c088db9257..928d9e6d3f 100644 --- a/plugins/NotesAndReminders/src/options.cpp +++ b/plugins/NotesAndReminders/src/options.cpp @@ -1,71 +1,67 @@ -#include "globals.h" +#include "stdafx.h" // min allowed alpha (don't want 0 because it's a waste of resources as well as might confuse user) #define MIN_ALPHA 30 -BOOL g_CloseAfterAddReminder, g_UseDefaultPlaySound; HICON g_hReminderIcon = nullptr; -LOGFONT lfBody,lfCaption; +LOGFONTA lfBody, lfCaption; HFONT hBodyFont = nullptr, hCaptionFont = nullptr; long BodyColor; -long CaptionFontColor,BodyFontColor; -BOOL g_ShowNotesAtStart,g_ShowScrollbar,g_AddContListMI,g_ShowNoteButtons; +long CaptionFontColor, BodyFontColor; int g_NoteTitleDate, g_NoteTitleTime; -int g_NoteWidth,g_NoteHeight; +int g_NoteWidth, g_NoteHeight; int g_Transparency; char *g_RemindSMS = nullptr; char *g_lpszAltBrowser = nullptr; int g_reminderListGeom[4] = {0}; -int g_reminderListColGeom[2] = { 150, 205 }; +int g_reminderListColGeom[2] = {150, 205}; int g_notesListGeom[4] = {0}; -int g_notesListColGeom[4] = { 150, 20, 20, 165 }; - +int g_notesListColGeom[4] = {150, 20, 20, 165}; #define NRCDEFAULT_BODYCLR RGB(255,255,0) struct DateFormat { - LPCSTR lpszUI; - LPCSTR lpszFmt; + wchar_t *lpszUI; + wchar_t *lpszFmt; } static dateFormats[] = { - { "1981-12-31", "yyyy'-'MM'-'dd" }, - { "31-12-1981", "dd'-'MM'-'yyyy" }, - { "12-31-1981", "MM'-'dd'-'yyyy" }, - { "1981-dec-31", "yyyy'-'MMM'-'dd" }, - { "31-dec-1981", "dd'-'MMM'-'yyyy" }, - { "dec-31-1981", "MMM'-'dd'-'yyyy" }, - { "1981/12/31", "yyyy'/'MM'/'dd" }, - { "31/12/1981", "dd'/'MM'/'yyyy" }, - { "12/31/1981", "MM'/'dd'/'yyyy" }, - { "1981/dec/31", "yyyy'/'MMM'/'dd" }, - { "31/dec/1981", "dd'/'MMM'/'yyyy" }, - { "dec/31/1981", "MMM'/'dd'/'yyyy" }, - { "1981 dec 31", "yyyy MMM dd" }, - { "31 dec 1981", "dd MMM yyyy" }, - { "dec 31 1981", "MMM dd yyyy" } + { L"1981-12-31", L"yyyy'-'MM'-'dd" }, + { L"31-12-1981", L"dd'-'MM'-'yyyy" }, + { L"12-31-1981", L"MM'-'dd'-'yyyy" }, + { L"1981-dec-31", L"yyyy'-'MMM'-'dd" }, + { L"31-dec-1981", L"dd'-'MMM'-'yyyy" }, + { L"dec-31-1981", L"MMM'-'dd'-'yyyy" }, + { L"1981/12/31", L"yyyy'/'MM'/'dd" }, + { L"31/12/1981", L"dd'/'MM'/'yyyy" }, + { L"12/31/1981", L"MM'/'dd'/'yyyy" }, + { L"1981/dec/31", L"yyyy'/'MMM'/'dd" }, + { L"31/dec/1981", L"dd'/'MMM'/'yyyy" }, + { L"dec/31/1981", L"MMM'/'dd'/'yyyy" }, + { L"1981 dec 31", L"yyyy MMM dd" }, + { L"31 dec 1981", L"dd MMM yyyy" }, + { L"dec 31 1981", L"MMM dd yyyy" } }; struct TimeFormat { - LPCSTR lpszUI; - LPCSTR lpszFmt; + wchar_t *lpszUI; + wchar_t *lpszFmt; } static timeFormats[] = { - { "19:30:00", "HH':'mm':'ss" }, - { "19:30", "HH':'mm'" }, - { "7:30:00 PM", "hh':'mm':'ss tt" }, - { "7:30 PM", "hh':'mm tt" }, - { "7:30:00P", "hh':'mm':'sst" }, - { "7:30P", "hh':'mmt" } + { L"19:30:00", L"HH':'mm':'ss" }, + { L"19:30", L"HH':'mm'" }, + { L"7:30:00 PM", L"hh':'mm':'ss tt" }, + { L"7:30 PM", L"hh':'mm tt" }, + { L"7:30:00P", L"hh':'mm':'sst" }, + { L"7:30P", L"hh':'mmt" } }; - struct FontOptionsList { char *szDescr; @@ -78,9 +74,6 @@ struct FontOptionsList static fontOptionsList[] = { { LPGEN("Sticky Note Caption"), RGB(0, 0, 0), "Small Fonts", 0, 7, LPGEN("Sticky Note Background Color") }, - // { LPGEN("Sticky Note Caption"), RGB(0,0,0), L"Terminal", 0, 6, LPGEN("Sticky Note Background Color")}, - // { LPGEN("Sticky Note Caption"), RGB(0,0,0), L"MS Serif", 0, 7, LPGEN("Sticky Note Background Color")}, - // { LPGEN("Sticky Note Body"), RGB(0,0,0), L"Tahoma", 0, 8, LPGEN("Sticky Note Background Color")}, { LPGEN("Sticky Note Body"), RGB(0, 0, 0), "System", DBFONTF_BOLD, 10, LPGEN("Sticky Note Background Color") }, }; @@ -97,91 +90,20 @@ static colourOptionsList[] = }; -LPCSTR GetDateFormatStr() +wchar_t* GetDateFormatStr() { return dateFormats[g_NoteTitleDate ? g_NoteTitleDate - 1 : 0].lpszFmt; } -LPCSTR GetTimeFormatStr() +wchar_t* GetTimeFormatStr() { return timeFormats[g_NoteTitleTime ? g_NoteTitleTime - 1 : 0].lpszFmt; } -#if defined( _UNICODE ) -static BYTE MsgDlgGetFontDefaultCharset(const wchar_t* szFont) -{ - return DEFAULT_CHARSET; -} -#else -// get font charset according to current CP -static BYTE MsgDlgGetCPDefaultCharset() -{ - switch (GetACP()) { - case 1250: - return EASTEUROPE_CHARSET; - case 1251: - return RUSSIAN_CHARSET; - case 1252: - return ANSI_CHARSET; - case 1253: - return GREEK_CHARSET; - case 1254: - return TURKISH_CHARSET; - case 1255: - return HEBREW_CHARSET; - case 1256: - return ARABIC_CHARSET; - case 1257: - return BALTIC_CHARSET; - case 1361: - return JOHAB_CHARSET; - case 874: - return THAI_CHARSET; - case 932: - return SHIFTJIS_CHARSET; - case 936: - return GB2312_CHARSET; - case 949: - return HANGEUL_CHARSET; - case 950: - return CHINESEBIG5_CHARSET; - default: - return DEFAULT_CHARSET; - } -} - -static int CALLBACK EnumFontFamExProc(const LOGFONT *, const TEXTMETRIC *, DWORD, LPARAM lParam) -{ - *(int*)lParam = 1; - return 0; -} - -// get font charset according to current CP, if available for specified font -static BYTE MsgDlgGetFontDefaultCharset(const char *szFont) -{ - LOGFONT lf = { 0 }; - int found = 0; - - mir_strcpy(lf.lfFaceName, szFont); - lf.lfCharSet = MsgDlgGetCPDefaultCharset(); - - // check if the font supports specified charset - HDC hdc = GetDC(nullptr); - EnumFontFamiliesEx(hdc, &lf, &EnumFontFamExProc, (LPARAM)&found, 0); - ReleaseDC(nullptr, hdc); - - if (found) - return lf.lfCharSet; - else // no, give default - return DEFAULT_CHARSET; -} -#endif - - static void InitFonts() { - memset(&lfBody, 0, sizeof(LOGFONT)); - memset(&lfCaption, 0, sizeof(LOGFONT)); + memset(&lfBody, 0, sizeof(lfBody)); + memset(&lfCaption, 0, sizeof(lfCaption)); LoadNRFont(NR_FONTID_CAPTION, &lfCaption, (COLORREF*)&CaptionFontColor); LoadNRFont(NR_FONTID_BODY, &lfBody, (COLORREF*)&BodyFontColor); @@ -191,8 +113,8 @@ static void InitFonts() if (hCaptionFont) DeleteObject(hCaptionFont); - hBodyFont = CreateFontIndirect(&lfBody); - hCaptionFont = CreateFontIndirect(&lfCaption); + hBodyFont = CreateFontIndirectA(&lfBody); + hCaptionFont = CreateFontIndirectA(&lfCaption); } @@ -244,7 +166,7 @@ void RegisterFontServiceFonts() //fontid.deffontsettings.size = fontOptionsList[i].defSize; fontid.deffontsettings.style = fontOptionsList[i].defStyle; - fontid.deffontsettings.charset = MsgDlgGetFontDefaultCharset(fontOptionsList[i].szDefFace); + fontid.deffontsettings.charset = DEFAULT_CHARSET; strncpy(fontid.deffontsettings.szFace, fontOptionsList[i].szDefFace, _countof(fontid.deffontsettings.szFace)); strncpy(fontid.backgroundName, fontOptionsList[i].szBkgName, _countof(fontid.backgroundName)); @@ -269,7 +191,7 @@ void RegisterFontServiceFonts() HookEvent(ME_COLOUR_RELOAD, FS_ColorChanged); } -void LoadNRFont(int i, LOGFONT *lf, COLORREF *colour) +void LoadNRFont(int i, LOGFONTA *lf, COLORREF *colour) { COLORREF col = Font_Get(SECTIONNAME, fontOptionsList[i].szDescr, lf); if (colour) @@ -311,42 +233,48 @@ static void TrimString(char *s) } } +static void FillValues(HWND hdlg) +{ + CheckDlgButton(hdlg, IDC_CHECK_HIDENOTES, g_plugin.bShowNotesAtStart ? BST_UNCHECKED : BST_CHECKED); // reversed + CheckDlgButton(hdlg, IDC_CHECK_MENUS, g_plugin.bAddContListMI ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_CHECK_BUTTONS, g_plugin.bShowNoteButtons ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_CHECK_SCROLLBARS, g_plugin.bShowScrollbar ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_CHECK_CLOSE, g_plugin.bCloseAfterAddReminder ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hdlg, IDC_CHECK_MSI, g_plugin.bUseMSI ? BST_CHECKED : BST_UNCHECKED); -INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) + SetDlgItemInt(hdlg, IDC_EDIT_WIDTH, g_NoteWidth, FALSE); + SetDlgItemInt(hdlg, IDC_EDIT_HEIGHT, g_NoteHeight, FALSE); + + SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_SETCURSEL, (WPARAM)(g_NoteTitleDate ? g_NoteTitleDate - 1 : SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_GETCOUNT, 0, 0) - 1), 0); + SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_SETCURSEL, (WPARAM)(g_NoteTitleTime ? g_NoteTitleTime - 1 : SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_GETCOUNT, 0, 0) - 1), 0); + + SendDlgItemMessage(hdlg, IDC_SLIDER_TRANSPARENCY, TBM_SETPOS, TRUE, 255 - g_Transparency); +} + +static INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: TranslateDialogDefault(hdlg); SendDlgItemMessage(hdlg, IDC_SLIDER_TRANSPARENCY, TBM_SETRANGE, TRUE, MAKELONG(0, 255 - MIN_ALPHA)); - SendDlgItemMessage(hdlg, IDC_SLIDER_TRANSPARENCY, TBM_SETPOS, TRUE, 255 - g_Transparency); - CheckDlgButton(hdlg, IDC_CHECK_HIDENOTES, !g_ShowNotesAtStart ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_MENUS, g_AddContListMI ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_BUTTONS, g_ShowNoteButtons ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_SCROLLBARS, g_ShowScrollbar ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_CLOSE, g_CloseAfterAddReminder ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_MSI, !g_UseDefaultPlaySound ? BST_CHECKED : BST_UNCHECKED); - SetDlgItemInt(hdlg, IDC_EDIT_WIDTH, g_NoteWidth, FALSE); - SetDlgItemInt(hdlg, IDC_EDIT_HEIGHT, g_NoteHeight, FALSE); + FillValues(hdlg); SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_RESETCONTENT, 0, 0); SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_RESETCONTENT, 0, 0); - for (int i = 0; i < _countof(dateFormats); i++) - SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_ADDSTRING, 0, (LPARAM)dateFormats[i].lpszUI); - for (int i = 0; i < _countof(timeFormats); i++) - SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_ADDSTRING, 0, (LPARAM)timeFormats[i].lpszUI); - SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_ADDSTRING, 0, (LPARAM)Translate("None")); - SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_ADDSTRING, 0, (LPARAM)Translate("None")); - - SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_SETCURSEL, (WPARAM)(g_NoteTitleDate ? g_NoteTitleDate - 1 : SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_GETCOUNT, 0, 0) - 1), 0); - SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_SETCURSEL, (WPARAM)(g_NoteTitleTime ? g_NoteTitleTime - 1 : SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_GETCOUNT, 0, 0) - 1), 0); + for (auto &it : dateFormats) + SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_ADDSTRING, 0, (LPARAM)it.lpszUI); + for (auto &it : timeFormats) + SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_ADDSTRING, 0, (LPARAM)it.lpszUI); + SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_ADDSTRING, 0, (LPARAM)TranslateT("None")); + SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_ADDSTRING, 0, (LPARAM)TranslateT("None")); if (g_RemindSMS) - SetDlgItemText(hdlg, IDC_EDIT_EMAILSMS, g_RemindSMS); + SetDlgItemTextA(hdlg, IDC_EDIT_EMAILSMS, g_RemindSMS); else - SetDlgItemText(hdlg, IDC_EDIT_EMAILSMS, ""); + SetDlgItemTextA(hdlg, IDC_EDIT_EMAILSMS, ""); - SetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, g_lpszAltBrowser ? g_lpszAltBrowser : ""); + SetDlgItemTextA(hdlg, IDC_EDIT_ALTBROWSER, g_lpszAltBrowser ? g_lpszAltBrowser : ""); return TRUE; case WM_HSCROLL: @@ -355,16 +283,19 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l case WM_NOTIFY: if (((LPNMHDR)lParam)->code == PSN_APPLY) { - g_ShowNotesAtStart = !(BOOL)IsDlgButtonChecked(hdlg, IDC_CHECK_HIDENOTES); - g_ShowNoteButtons = (BOOL)IsDlgButtonChecked(hdlg, IDC_CHECK_BUTTONS); - g_ShowScrollbar = (BOOL)IsDlgButtonChecked(hdlg, IDC_CHECK_SCROLLBARS); - g_AddContListMI = (BOOL)IsDlgButtonChecked(hdlg, IDC_CHECK_MENUS); + g_plugin.bShowNotesAtStart = IsDlgButtonChecked(hdlg, IDC_CHECK_HIDENOTES) == 0; // reversed + g_plugin.bShowNoteButtons = IsDlgButtonChecked(hdlg, IDC_CHECK_BUTTONS) != 0; + g_plugin.bShowScrollbar = IsDlgButtonChecked(hdlg, IDC_CHECK_SCROLLBARS) != 0; + g_plugin.bAddContListMI = IsDlgButtonChecked(hdlg, IDC_CHECK_MENUS) != 0; + BOOL LB; g_NoteWidth = GetDlgItemInt(hdlg, IDC_EDIT_WIDTH, &LB, FALSE); g_NoteHeight = GetDlgItemInt(hdlg, IDC_EDIT_HEIGHT, &LB, FALSE); g_Transparency = 255 - SendDlgItemMessage(hdlg, IDC_SLIDER_TRANSPARENCY, TBM_GETPOS, 0, 0); - g_CloseAfterAddReminder = (BOOL)IsDlgButtonChecked(hdlg, IDC_CHECK_CLOSE); - g_UseDefaultPlaySound = !(BOOL)IsDlgButtonChecked(hdlg, IDC_CHECK_MSI); + + g_plugin.bCloseAfterAddReminder = IsDlgButtonChecked(hdlg, IDC_CHECK_CLOSE) != 0; + g_plugin.bUseMSI = IsDlgButtonChecked(hdlg, IDC_CHECK_MSI) != 0; + g_NoteTitleDate = (SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_GETCURSEL, 0, 0) + 1) % SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_GETCOUNT, 0, 0); g_NoteTitleTime = (SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_GETCURSEL, 0, 0) + 1) % SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_GETCOUNT, 0, 0); if (g_NoteWidth < 179) { @@ -378,7 +309,7 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l WORD SzT = (WORD)SendDlgItemMessage(hdlg, IDC_EDIT_EMAILSMS, WM_GETTEXTLENGTH, 0, 0); if (SzT != 0) { g_RemindSMS = (char*)realloc(g_RemindSMS, SzT + 1); - GetDlgItemText(hdlg, IDC_EDIT_EMAILSMS, g_RemindSMS, SzT + 1); + GetDlgItemTextA(hdlg, IDC_EDIT_EMAILSMS, g_RemindSMS, SzT + 1); } char *P = g_RemindSMS; db_set_blob(0, MODULENAME, "RemindEmail", P, SzT); @@ -386,7 +317,7 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l SzT = (WORD)SendDlgItemMessage(hdlg, IDC_EDIT_ALTBROWSER, WM_GETTEXTLENGTH, 0, 0); if (SzT != 0) { g_lpszAltBrowser = (char*)mir_realloc(g_lpszAltBrowser, SzT + 1); - GetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, g_lpszAltBrowser, SzT + 1); + GetDlgItemTextA(hdlg, IDC_EDIT_ALTBROWSER, g_lpszAltBrowser, SzT + 1); TrimString(g_lpszAltBrowser); if (!*g_lpszAltBrowser) { mir_free(g_lpszAltBrowser); @@ -397,23 +328,18 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l mir_free(g_lpszAltBrowser); g_lpszAltBrowser = nullptr; } - SetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, g_lpszAltBrowser ? g_lpszAltBrowser : ""); + SetDlgItemTextA(hdlg, IDC_EDIT_ALTBROWSER, g_lpszAltBrowser ? g_lpszAltBrowser : ""); if (g_lpszAltBrowser) g_plugin.setString("AltBrowser", g_lpszAltBrowser); else g_plugin.delSetting("AltBrowser"); - g_plugin.setDword("ShowNotesAtStart", g_ShowNotesAtStart); - g_plugin.setDword("ShowNoteButtons", g_ShowNoteButtons); - g_plugin.setDword("ShowScrollbar", g_ShowScrollbar); - g_plugin.setDword("AddContactMenuItems", g_AddContListMI); g_plugin.setDword("NoteWidth", g_NoteWidth); g_plugin.setDword("NoteHeight", g_NoteHeight); g_plugin.setDword("Transparency", g_Transparency); g_plugin.setDword("NoteTitleDate", g_NoteTitleDate); g_plugin.setDword("NoteTitleTime", g_NoteTitleTime); - g_plugin.setDword("CloseAfterAddReminder", g_CloseAfterAddReminder); - g_plugin.setDword("UseMCI", !g_UseDefaultPlaySound); + SaveNotes(); LoadNotes(FALSE); return TRUE; @@ -424,20 +350,19 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l switch (LOWORD(wParam)) { case IDC_BTN_BROWSEBROWSER: { - char s[MAX_PATH]; + wchar_t s[MAX_PATH]; + GetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, s, _countof(s)); - OPENFILENAME ofn = { 0 }; + OPENFILENAME ofn = {0}; ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofn.hwndOwner = hdlg; - ofn.lpstrFilter = Translate("Executable Files\0*.exe\0All Files\0*.*\0\0"); + ofn.lpstrFilter = TranslateT("Executable Files\0*.exe\0All Files\0*.*\0\0"); ofn.lpstrFile = s; ofn.nMaxFile = _countof(s); - ofn.lpstrTitle = Translate("Select Executable"); - ofn.lpstrInitialDir = "."; + ofn.lpstrTitle = TranslateT("Select Executable"); + ofn.lpstrInitialDir = L"."; ofn.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_ENABLESIZING | OFN_DONTADDTORECENT; - GetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, s, ofn.nMaxFile); - if (GetOpenFileName(&ofn)) { SetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, s); SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0); @@ -447,34 +372,20 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l case IDC_BUTTON_RESET: SAFE_FREE((void**)&g_RemindSMS); - SetDlgItemText(hdlg, IDC_EDIT_EMAILSMS, ""); - if (g_lpszAltBrowser) { - mir_free(g_lpszAltBrowser); - g_lpszAltBrowser = nullptr; - } - SetDlgItemText(hdlg, IDC_EDIT_ALTBROWSER, ""); - g_ShowNotesAtStart = TRUE; - g_AddContListMI = TRUE; - g_ShowScrollbar = TRUE; - g_ShowNoteButtons = TRUE; + SetDlgItemTextA(hdlg, IDC_EDIT_EMAILSMS, ""); + SetDlgItemTextA(hdlg, IDC_EDIT_ALTBROWSER, ""); + + replaceStr(g_lpszAltBrowser, nullptr); + + g_plugin.bShowNotesAtStart = g_plugin.bAddContListMI = g_plugin.bShowScrollbar = g_plugin.bShowNoteButtons = true; + g_plugin.bCloseAfterAddReminder = g_plugin.bUseMSI = true; + g_NoteTitleDate = 1; g_NoteTitleTime = 1; - g_CloseAfterAddReminder = TRUE; - g_UseDefaultPlaySound = FALSE; - CheckDlgButton(hdlg, IDC_CHECK_HIDENOTES, !g_ShowNotesAtStart ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_MENUS, g_AddContListMI ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_SCROLLBARS, g_ShowScrollbar ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_BUTTONS, g_ShowNoteButtons ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_CLOSE, g_CloseAfterAddReminder ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hdlg, IDC_CHECK_MSI, !g_UseDefaultPlaySound ? BST_CHECKED : BST_UNCHECKED); - SendDlgItemMessage(hdlg, IDC_COMBODATE, CB_SETCURSEL, (WPARAM)(g_NoteTitleDate - 1), 0); - SendDlgItemMessage(hdlg, IDC_COMBOTIME, CB_SETCURSEL, (WPARAM)(g_NoteTitleTime - 1), 0); g_NoteWidth = 179; g_NoteHeight = 35; - SetDlgItemInt(hdlg, IDC_EDIT_WIDTH, g_NoteWidth, FALSE); - SetDlgItemInt(hdlg, IDC_EDIT_HEIGHT, g_NoteHeight, FALSE); g_Transparency = 255; - SendDlgItemMessage(hdlg, IDC_SLIDER_TRANSPARENCY, TBM_SETPOS, TRUE, 0); + FillValues(hdlg); SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0); // JK optim return TRUE; @@ -506,6 +417,20 @@ INT_PTR CALLBACK DlgProcOptions(HWND hdlg, UINT message, WPARAM wParam, LPARAM l return FALSE; } +int OnOptInitialise(WPARAM w, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = 900002000; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_STNOTEOPTIONS); + odp.szTitle.a = SECTIONNAME; + odp.szGroup.a = LPGEN("Plugins"); + odp.pfnDlgProc = DlgProcOptions; + g_plugin.addOptions(w, &odp); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + void InitSettings(void) { void *P = nullptr; @@ -523,17 +448,11 @@ void InitSettings(void) g_lpszAltBrowser = g_plugin.getStringA("AltBrowser"); - g_ShowNotesAtStart = (BOOL)g_plugin.getDword("ShowNotesAtStart", 1); - g_ShowNoteButtons = (BOOL)g_plugin.getDword("ShowNoteButtons", 1); - g_ShowScrollbar = (BOOL)g_plugin.getDword("ShowScrollbar", 1); - g_AddContListMI = (BOOL)g_plugin.getDword("AddContactMenuItems", 1); g_NoteWidth = g_plugin.getDword("NoteWidth", 179); g_NoteHeight = g_plugin.getDword("NoteHeight", 50); g_Transparency = g_plugin.getDword("Transparency", 255); g_NoteTitleDate = g_plugin.getDword("NoteTitleDate", 1); g_NoteTitleTime = g_plugin.getDword("NoteTitleTime", 1); - g_CloseAfterAddReminder = (BOOL)g_plugin.getDword("CloseAfterAddReminder", 1); - g_UseDefaultPlaySound = !(BOOL)g_plugin.getDword("UseMCI", 1); ReadSettingIntArray(0, MODULENAME, "ReminderListGeom", g_reminderListGeom, _countof(g_reminderListGeom)); ReadSettingIntArray(0, MODULENAME, "ReminderListColGeom", g_reminderListColGeom, _countof(g_reminderListColGeom)); diff --git a/plugins/NotesAndReminders/src/reminders.cpp b/plugins/NotesAndReminders/src/reminders.cpp index e8a021da1d..1990714449 100644 --- a/plugins/NotesAndReminders/src/reminders.cpp +++ b/plugins/NotesAndReminders/src/reminders.cpp @@ -1,16 +1,14 @@ -#include "globals.h" +#include "stdafx.h" #define FILETIME_TICKS_PER_SEC ((ULONGLONG)10000000) #define MAX_REMINDER_LEN 16384 - // RemindersData DB data params #define DATATAG_TEXT 1 // %s #define DATATAG_SNDREPEAT 2 // %u (specifies seconds to wait between sound repeats, 0 if repeat is disabled) #define DATATAG_SNDSEL 3 // %d (which sound to use, default, alt1, alt2, -1 means no sound at all) - #define IDC_DATE 1000 #define IDC_TIME IDC_COMBOREMINDERTIME #define IDC_ADDREMINDER 1002 @@ -40,50 +38,59 @@ #define NOTIFY_LIST() if (ListReminderVisible) PostMessage(LV,WM_RELOAD,0,0) +///////////////////////////////////////////////////////////////////////////////////////// + +static void RemoveReminderSystemEvent(struct REMINDERDATA *p); + +struct REMINDERDATA +{ + HWND handle; + DWORD uid; + char* Reminder; + ULARGE_INTEGER When; // FILETIME in UTC + UINT RepeatSound; + UINT RepeatSoundTTL; + int SoundSel; // -1 if sound disabled + bool RemVisible; + bool SystemEventQueued; + + ~REMINDERDATA() + { + // remove pending system event + if (SystemEventQueued) + RemoveReminderSystemEvent(this); + } +}; + +static int ReminderSortCb(const REMINDERDATA *v1, const REMINDERDATA *v2) +{ + if (v1->When.QuadPart == v2->When.QuadPart) + return 0; + + return (v1->When.QuadPart < v2->When.QuadPart) ? -1 : 1; +} + +static LIST arReminders(1, ReminderSortCb); -TREEELEMENT *RemindersList = nullptr; static UINT QueuedReminderCount = 0; -static BOOL ListReminderVisible = FALSE; -static BOOL NewReminderVisible = FALSE; +static bool ListReminderVisible = false; +static int NewReminderVisible = 0; static REMINDERDATA *pEditReminder = nullptr; static HWND LV; -static SOCKET S; int WS_Send(SOCKET s, char *data, int datalen); unsigned long WS_ResolveName(char *name, WORD *port, int defaultPort); void Send(char *user, char *host, char *Msg, char* server); -char* GetPreviewString(const char *lpsz); - - -static int ReminderSortCb(TREEELEMENT *v1, TREEELEMENT *v2) -{ - return (((REMINDERDATA*)v1->ptrdata)->When.QuadPart < ((REMINDERDATA*)v2->ptrdata)->When.QuadPart) ? -1 : 1; -} +wchar_t* GetPreviewString(const char *lpsz); // time convertsion routines that take local time-zone specific daylight saving configuration into account // (unlike the standard FileTimeToLocalFileTime functions) -void UtcToTzLocalFT(const FILETIME *lpUtc, FILETIME *lpLocal) -{ - SYSTEMTIME tm, tmLocal; - FILETIMEtoSYSTEMTIME(lpUtc, &tm); - SystemTimeToTzSpecificLocalTime(nullptr, &tm, &tmLocal); - SYSTEMTIMEtoFILETIME(&tmLocal, lpLocal); -} - -void TzLocalToUtcFT(const FILETIME *lpLocal, FILETIME *lpUtc) -{ - SYSTEMTIME tm, tmUtc; - FILETIMEtoSYSTEMTIME(lpLocal, &tm); - TzSpecificLocalTimeToSystemTime(nullptr, &tm, &tmUtc); - SYSTEMTIMEtoFILETIME(&tmUtc, lpUtc); -} - void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal) { SYSTEMTIME tm; - FILETIMEtoSYSTEMTIME(lpUtc, &tm); + FileTimeToSystemTime(lpUtc, &tm); SystemTimeToTzSpecificLocalTime(nullptr, &tm, tmLocal); } @@ -91,50 +98,38 @@ void TzLocalSTToFileTime(const SYSTEMTIME *tmLocal, FILETIME *lpUtc) { SYSTEMTIME tm; TzSpecificLocalTimeToSystemTime(nullptr, (SYSTEMTIME*)tmLocal, &tm); - SYSTEMTIMEtoFILETIME(&tm, lpUtc); + SystemTimeToFileTime(&tm, lpUtc); } -static DWORD CreateUid() +static REMINDERDATA* FindReminder(DWORD uid) { - DWORD uid; - TREEELEMENT *TTE; - - if (!RemindersList) - return 1; - - for (uid = 1;; uid++) { - // check existing reminders if uid is in use - TTE = RemindersList; - while (TTE) { - if (((REMINDERDATA*)TTE->ptrdata)->uid == uid) - // uid in use - goto try_next; - - TTE = (TREEELEMENT*)TTE->next; - } - - return uid; + for (auto &pReminder : arReminders) + if (pReminder->uid == uid) + return pReminder; -try_next:; - } + return nullptr; } -static REMINDERDATA* FindReminder(DWORD uid) +static REMINDERDATA* FindReminder(HWND hwndDlg) { - if (!RemindersList) - return nullptr; - - for (TREEELEMENT *TTE = RemindersList; TTE; TTE = (TREEELEMENT *)TTE->next) { - REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata; - - if (pReminder->uid == uid) { + for (auto pReminder : arReminders) + if (pReminder->handle == hwndDlg) return pReminder; - } - } return nullptr; } +static DWORD CreateUid() +{ + if (!arReminders.getCount()) + return 1; + + // check existing reminders if uid is in use + for (DWORD uid = 1;; uid++) { + if (!FindReminder(uid)) // uid in use + return uid; + } +} static void RemoveReminderSystemEvent(REMINDERDATA *p) { @@ -161,82 +156,52 @@ void PurgeReminders(void) char ValueName[32]; int ReminderCount = g_plugin.getDword("RemindersData", 0); - for (int I = 0; I < ReminderCount; I++) { - mir_snprintf(ValueName, "RemindersData%d", I); + for (int i = 0; i < ReminderCount; i++) { + mir_snprintf(ValueName, "RemindersData%d", i); g_plugin.delSetting(ValueName); } } void JustSaveReminders(void) { - int I = 0, n, l; - char *tmpReminder = nullptr, *Value; - char ValueName[32]; - int ReminderCount; - - const int OldReminderCount = g_plugin.getDword("RemindersData", 0); - - ReminderCount = TreeGetCount(RemindersList); + int OldReminderCount = g_plugin.getDword("RemindersData", 0); + g_plugin.setDword("RemindersData", arReminders.getCount()); - g_plugin.setDword("RemindersData", ReminderCount); - - for (TREEELEMENT *TTE = RemindersList; TTE; TTE = (TREEELEMENT *)TTE->next, I++) { - REMINDERDATA *pReminder = (REMINDERDATA *)TTE->ptrdata; - if (pReminder->Reminder && mir_strlen(pReminder->Reminder)) + int i = 0; + for (auto &pReminder : arReminders) { + char *tmpReminder; + if (mir_strlen(pReminder->Reminder)) tmpReminder = pReminder->Reminder; else - tmpReminder = nullptr; - - if (!tmpReminder) tmpReminder = ""; - Value = (char*)malloc(mir_strlen(tmpReminder) + 512); - - if (!Value) - continue; - - n = 0; + CMStringA szValue; // data header (save 'When' with 1-second resolution, it's just a waste to have 100-nanosecond resolution // which results in larger DB strings with no use) - l = sprintf(Value, "X%u:%I64x", pReminder->uid, pReminder->When.QuadPart / FILETIME_TICKS_PER_SEC); //!!!!!!!!! - if (l > 0) n += l; + szValue.AppendFormat("X%u:%I64x", pReminder->uid, pReminder->When.QuadPart / FILETIME_TICKS_PER_SEC); // sound repeat - if (pReminder->RepeatSound) { - l = sprintf(Value + n, "\033""%u:%u", DATATAG_SNDREPEAT, pReminder->RepeatSound); //!!!!!!!!!! - if (l > 0) n += l; - } + if (pReminder->RepeatSound) + szValue.AppendFormat("\033""%u:%u", DATATAG_SNDREPEAT, pReminder->RepeatSound); // sound - if (pReminder->SoundSel) { - l = sprintf(Value + n, "\033""%u:%d", DATATAG_SNDSEL, pReminder->SoundSel); //!!!!!!!!! - if (l > 0) n += l; - } + if (pReminder->SoundSel) + szValue.AppendFormat("\033""%u:%d", DATATAG_SNDSEL, pReminder->SoundSel); // reminder text/note (ALWAYS PUT THIS PARAM LAST) - if (tmpReminder && *tmpReminder) { - l = sprintf(Value + n, "\033""%u:%s", DATATAG_TEXT, tmpReminder); //!!!!!!!!!!! - if (l > 0) n += l; - } - - // 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, "RemindersData%d", ReminderCount - I - 1); // do not want to reverse in DB - - db_set_blob(0, MODULENAME, ValueName, Value, n + 1); + if (tmpReminder && *tmpReminder) + szValue.AppendFormat("\033""%u:%s", DATATAG_TEXT, tmpReminder); - SAFE_FREE((void**)&Value); + char ValueName[32]; + mir_snprintf(ValueName, "RemindersData%d", i++); + db_set_blob(0, MODULENAME, ValueName, szValue.GetBuffer(), szValue.GetLength() + 1); } // delete any left over DB reminder entries - for (; I < OldReminderCount; I++) { - mir_snprintf(ValueName, "RemindersData%d", I); + while (i < OldReminderCount) { + char ValueName[32]; + mir_snprintf(ValueName, "RemindersData%d", i++); g_plugin.delSetting(ValueName); } } @@ -248,13 +213,13 @@ void LoadReminders(void) char ValueName[32]; BOOL GenerateUids = FALSE; - RemindersList = nullptr; + arReminders.destroy(); int RemindersCount = g_plugin.getDword("RemindersData", 0); - for (int I = 0; I < RemindersCount; I++) { + for (int i = 0; i < RemindersCount; i++) { Size = 65535; Value = nullptr; - mir_snprintf(ValueName, "RemindersData%d", I); + mir_snprintf(ValueName, "RemindersData%d", i); ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value); @@ -349,7 +314,7 @@ void LoadReminders(void) tm.wYear = stm->tm_year + 1900; tm.wMonth = stm->tm_mon + 1; tm.wDay = stm->tm_mday; - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&rem.When); + SystemTimeToFileTime(&tm, (FILETIME*)&rem.When); } TVal = DelPos + 1; rem.Reminder = _strdup(TVal); @@ -357,16 +322,15 @@ void LoadReminders(void) // queue uid generation if invalid uid is present if (!rem.uid) - GenerateUids = TRUE; + GenerateUids = true; TempRem = (REMINDERDATA*)malloc(sizeof(REMINDERDATA)); if (TempRem) { *TempRem = rem; - TreeAddSorted(&RemindersList, TempRem, ReminderSortCb); + arReminders.insert(TempRem); } - else if (rem.Reminder) { + else if (rem.Reminder) free(rem.Reminder); - } skip:; } @@ -374,13 +338,10 @@ skip:; } // generate UIDs if there are any items with an invalid UID - if (GenerateUids && RemindersList) { - for (TREEELEMENT *TTE = RemindersList; TTE; TTE = (TREEELEMENT*)TTE->next) { - REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata; - + if (GenerateUids && arReminders.getCount()) { + for (auto &pReminder : arReminders) if (!pReminder->uid) pReminder->uid = CreateUid(); - } JustSaveReminders(); } @@ -388,36 +349,26 @@ skip:; static void DeleteReminder(REMINDERDATA *p) { - if (!p) - return; - - if (p->SystemEventQueued) { - // remove pending system event - RemoveReminderSystemEvent(p); + if (p) { + arReminders.remove(p); + delete p; } - - TreeDelete(&RemindersList, p); - SAFE_FREE((void**)&p->Reminder); - SAFE_FREE((void**)&p); } void CloseReminderList() { if (ListReminderVisible) { DestroyWindow(LV); - ListReminderVisible = FALSE; + ListReminderVisible = false; } } static void PurgeReminderTree() { - while (RemindersList) // empty whole tree - { - REMINDERDATA *pt = (REMINDERDATA*)RemindersList->ptrdata; - if (pt->handle) DestroyWindow(pt->handle); - DeleteReminder(pt); - } - RemindersList = nullptr; + for (auto &pt : arReminders) // empty whole tree + delete pt; + + arReminders.destroy(); } void SaveReminders(void) @@ -433,10 +384,9 @@ void DeleteReminders(void) PurgeReminderTree(); } - -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) { - SYSTEMTIME tm = { 0 }; + SYSTEMTIME tm = {0}; LCID lc = GetUserDefaultLCID(); *s = 0; @@ -444,52 +394,48 @@ void GetTriggerTimeString(const ULARGE_INTEGER *When, char *s, UINT strSize, BOO if (bUtc) FileTimeToTzLocalST((const FILETIME*)When, &tm); else - FILETIMEtoSYSTEMTIME((FILETIME*)When, &tm); + FileTimeToSystemTime((FILETIME*)When, &tm); - if (GetDateFormat(lc, DATE_LONGDATE, &tm, nullptr, s, strSize)) { + if (GetDateFormat(lc, DATE_LONGDATE, &tm, nullptr, s, (int)strSize)) { // append time - int n = (int)mir_strlen(s); + size_t n = mir_wstrlen(s); s[n++] = ' '; s[n] = 0; - if (!GetTimeFormat(lc, LOCALE_NOUSEROVERRIDE | TIME_NOSECONDS, &tm, nullptr, s + n, strSize - n)) - mir_snprintf(s + n, strSize - n, "%02d:%02d", tm.wHour, tm.wMinute); + if (!GetTimeFormat(lc, LOCALE_NOUSEROVERRIDE | TIME_NOSECONDS, &tm, nullptr, s + n, int(strSize - n))) + mir_snwprintf(s + n, strSize - n, L"%02d:%02d", tm.wHour, tm.wMinute); } - else mir_snprintf(s, strSize, "%d-%02d-%02d %02d:%02d", tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute); + else mir_snwprintf(s, strSize, L"%d-%02d-%02d %02d:%02d", tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute); } static void Skin_PlaySoundPoly(LPCSTR pszSoundName) { - if (g_UseDefaultPlaySound) { + if (!g_plugin.bUseMSI) { Skin_PlaySound(pszSoundName); return; } if (db_get_b(0, "SkinSoundsOff", pszSoundName, 0) == 0) { DBVARIANT dbv; - if (db_get_s(0, "SkinSounds", pszSoundName, &dbv) == 0) { char szFull[MAX_PATH]; PathToAbsolute(dbv.pszVal, szFull); - //NotifyEventHooks(hPlayEvent, 0, (LPARAM)szFull); - { - // use MCI device which allows multiple sounds playing at once - // NOTE: mciSendString does not like long paths names, must convert to short - char szShort[MAX_PATH]; - char s[512]; - GetShortPathNameA(szFull, szShort, sizeof(szShort)); - mir_snprintf(s, "play \"%s\"", szShort); - mciSendStringA(s, nullptr, 0, nullptr); - } + // use MCI device which allows multiple sounds playing at once + // NOTE: mciSendString does not like long paths names, must convert to short + char szShort[MAX_PATH]; + char s[512]; + GetShortPathNameA(szFull, szShort, sizeof(szShort)); + mir_snprintf(s, "play \"%s\"", szShort); + mciSendStringA(s, nullptr, 0, nullptr); db_free(&dbv); } } } -static void UpdateReminderEvent(REMINDERDATA *pReminder, UINT nElapsedSeconds, BOOL *pHasPlayedSound) +static void UpdateReminderEvent(REMINDERDATA *pReminder, UINT nElapsedSeconds, DWORD *pHasPlayedSound) { DWORD dwSoundMask; @@ -510,35 +456,29 @@ static void UpdateReminderEvent(REMINDERDATA *pReminder, UINT nElapsedSeconds, B *pHasPlayedSound |= dwSoundMask; } } - else { - pReminder->RepeatSoundTTL -= nElapsedSeconds; - } + else pReminder->RepeatSoundTTL -= nElapsedSeconds; } } -static void FireReminder(REMINDERDATA *pReminder, BOOL *pHasPlayedSound) +static void FireReminder(REMINDERDATA *pReminder, DWORD *pHasPlayedSound) { if (pReminder->SystemEventQueued) return; // add a system event - { - CLISTEVENT ev = {}; - ev.hIcon = g_hReminderIcon; - ev.flags = CLEF_URGENT; - ev.lParam = (LPARAM)pReminder->uid; - ev.pszService = MODULENAME"/OpenTriggeredReminder"; - ev.szTooltip.a = Translate("Reminder"); - g_clistApi.pfnAddEvent(&ev); - } + CLISTEVENT ev = {}; + ev.hIcon = g_hReminderIcon; + ev.flags = CLEF_URGENT; + ev.lParam = (LPARAM)pReminder->uid; + ev.pszService = MODULENAME"/OpenTriggeredReminder"; + ev.szTooltip.a = Translate("Reminder"); + g_clistApi.pfnAddEvent(&ev); pReminder->SystemEventQueued = TRUE; QueuedReminderCount++; - if (pReminder->SoundSel < 0) { - // sound disabled + if (pReminder->SoundSel < 0) // sound disabled return; - } DWORD dwSoundMask = 1 << pReminder->SoundSel; @@ -557,64 +497,58 @@ static void FireReminder(REMINDERDATA *pReminder, BOOL *pHasPlayedSound) } -BOOL CheckRemindersAndStart(void) +bool CheckRemindersAndStart(void) { // returns TRUE if there are any triggered reminder with SystemEventQueued, this will shorten the update interval // allowing sound repeats with shorter intervals - ULARGE_INTEGER curT; - BOOL bResult; - - if (!RemindersList) - return FALSE; + if (!arReminders.getCount()) + return false; - { - SYSTEMTIME tm; - GetSystemTime(&tm); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&curT); - } + ULARGE_INTEGER curT; + SYSTEMTIME tm; + GetSystemTime(&tm); + SystemTimeToFileTime(&tm, (FILETIME*)&curT); // NOTE: reminder list is sorted by trigger time, so we can early out on the first reminder > cur time - // quick check for normal case with no reminder ready to be triggered and no queued triggered reminders // (happens 99.99999999999% of the time) - if (curT.QuadPart < ((REMINDERDATA*)RemindersList->ptrdata)->When.QuadPart && !QueuedReminderCount) { - return FALSE; - } + if (curT.QuadPart < arReminders[0]->When.QuadPart && !QueuedReminderCount) + return false; - bResult = FALSE; + bool bResult = false; // var used to avoid playing multiple alarm sounds during a single update - BOOL bHasPlayedSound = FALSE; + DWORD bHasPlayedSound = 0; // if there are queued (triggered) reminders then iterate through entire list, becaue of WM_TIMECHANGE events // and for example daylight saving changes it's possible for an already triggered event to end up with When>curT - BOOL bHasQueuedReminders = (QueuedReminderCount != 0); + bool bHasQueuedReminders = (QueuedReminderCount != 0); // allthough count should always be correct, it's fool proof to just count them again in the loop below QueuedReminderCount = 0; - - for (TREEELEMENT *TTE = RemindersList; TTE && (bHasQueuedReminders || ((REMINDERDATA*)TTE->ptrdata)->When.QuadPart <= curT.QuadPart); TTE = (TREEELEMENT *)TTE->next) { - REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata; + for (auto &pReminder : arReminders) { + if (!bHasQueuedReminders && pReminder->When.QuadPart > curT.QuadPart) + break; if (!pReminder->RemVisible) { if (pReminder->SystemEventQueued) { UpdateReminderEvent(pReminder, REMINDER_UPDATE_INTERVAL_SHORT / 1000, &bHasPlayedSound); QueuedReminderCount++; - bResult = TRUE; + bResult = true; } - else if (((REMINDERDATA*)TTE->ptrdata)->When.QuadPart <= curT.QuadPart) { + else if (pReminder->When.QuadPart <= curT.QuadPart) { if (!g_RemindSMS) { FireReminder(pReminder, &bHasPlayedSound); if (pReminder->SystemEventQueued) - bResult = TRUE; + bResult = true; } else { - char* S2 = strchr(g_RemindSMS, '@'); - char* S1 = (char*)malloc(S2 - g_RemindSMS); + char *S2 = strchr(g_RemindSMS, '@'); + char *S1 = (char*)malloc(S2 - g_RemindSMS); strncpy(S1, g_RemindSMS, S2 - g_RemindSMS); S1[S2 - g_RemindSMS] = 0x0; @@ -632,7 +566,6 @@ BOOL CheckRemindersAndStart(void) return bResult; } - static LRESULT CALLBACK DatePickerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { @@ -654,22 +587,22 @@ static LRESULT CALLBACK DatePickerWndProc(HWND hWnd, UINT message, WPARAM wParam return mir_callNextSubclass(hWnd, DatePickerWndProc, message, wParam, lParam); } -static void InitDatePicker(HWND Dialog, UINT nIDDate) +static void InitDatePicker(HWND hwndDlg, UINT nIDDate) { // subclass date picker to prevent user editing (should only use the dropdown calender to ensure valid dates) - HWND hCtrl = GetDlgItem(Dialog, nIDDate); + HWND hCtrl = GetDlgItem(hwndDlg, nIDDate); // tweak style of picker if (IsWinVerVistaPlus()) { - DWORD dw = SendDlgItemMessage(Dialog, nIDDate, DTM_GETMCSTYLE, 0, 0); + DWORD dw = SendDlgItemMessage(hwndDlg, nIDDate, DTM_GETMCSTYLE, 0, 0); dw |= MCS_WEEKNUMBERS | MCS_NOSELCHANGEONNAV; - SendDlgItemMessage(Dialog, nIDDate, DTM_SETMCSTYLE, 0, dw); + SendDlgItemMessage(hwndDlg, nIDDate, DTM_SETMCSTYLE, 0, dw); } mir_subclassWindow(hCtrl, DatePickerWndProc); } -static BOOL ParseTime(LPCSTR s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAllowOffsetOverride) +static BOOL ParseTime(const wchar_t *s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAllowOffsetOverride) { // validate format: [][':'[]][p | P].* @@ -698,31 +631,31 @@ static BOOL ParseTime(LPCSTR s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAl while (iswspace(*s)) s++; } - if (!isdigit(*s)) + if (!iswdigit(*s)) return FALSE; h = (int)(*s - '0'); s++; if (!bOffset) { - if (isdigit(*s)) { + if (iswdigit(*s)) { h = h * 10 + (int)(*s - '0'); s++; } - if (isdigit(*s)) + if (iswdigit(*s)) return FALSE; } else { // allow more than 2-digit numbers for offset - while (isdigit(*s)) { + while (iswdigit(*s)) { h = h * 10 + (int)(*s - '0'); s++; } } // find : separator - - while (iswspace(*s)) s++; + while (iswspace(*s)) + s++; if (*s == ':') { s++; @@ -736,7 +669,7 @@ static BOOL ParseTime(LPCSTR s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAl m = (int)(*s - '0'); s++; - if (isdigit(*s)) { + if (iswdigit(*s)) { m = m * 10 + (int)(*s - '0'); s++; } @@ -808,28 +741,23 @@ static BOOL ParseTime(LPCSTR s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAl } // returns TRUE if combo box list displays time offsets ("23:34 (5 Minutes)" etc.) -__inline static BOOL IsRelativeCombo(HWND Dialog, UINT nIDTime) +__inline static BOOL IsRelativeCombo(HWND hwndDlg, UINT nIDTime) { - return (((int)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, 0, 0)) >= 0); + return (((int)SendDlgItemMessage(hwndDlg, nIDTime, CB_GETITEMDATA, 0, 0)) >= 0); } -static void PopulateTimeCombo(HWND Dialog, UINT nIDTime, BOOL bRelative, const SYSTEMTIME *tmUtc) +static void PopulateTimeCombo(HWND hwndDlg, UINT nIDTime, BOOL bRelative, const SYSTEMTIME *tmUtc) { // NOTE: may seem like a bit excessive time converstion and handling, but this is done in order // to gracefully handle crossing daylight saving boundaries SYSTEMTIME tm2; ULARGE_INTEGER li; - ULONGLONG ref; - int i, n; - char s[64]; + wchar_t s[64]; const ULONGLONG MinutesToFileTime = (ULONGLONG)60 * FILETIME_TICKS_PER_SEC; - LPCSTR lpszMinutes; - LPCSTR lpszHours; - WORD wCurHour, wCurMinute; if (!bRelative) { - SendDlgItemMessage(Dialog, nIDTime, CB_RESETCONTENT, 0, 0); + SendDlgItemMessage(hwndDlg, nIDTime, CB_RESETCONTENT, 0, 0); // ensure that we start on midnight local time SystemTimeToTzSpecificLocalTime(nullptr, (SYSTEMTIME*)tmUtc, &tm2); @@ -838,19 +766,19 @@ static void PopulateTimeCombo(HWND Dialog, UINT nIDTime, BOOL bRelative, const S tm2.wSecond = 0; tm2.wMilliseconds = 0; TzSpecificLocalTimeToSystemTime(nullptr, &tm2, &tm2); - SYSTEMTIMEtoFILETIME(&tm2, (FILETIME*)&li); + SystemTimeToFileTime(&tm2, (FILETIME*)&li); // from 00:00 to 23:30 in 30 minute steps - for (i = 0; i < 50; i++) { + for (int i = 0; i < 50; i++) { const int h = i >> 1; const int m = (i & 1) ? 30 : 0; FileTimeToTzLocalST((FILETIME*)&li, &tm2); - mir_snprintf(s, "%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + mir_snwprintf(s, L"%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute); + int n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); // item data contains time offset from midnight in seconds (bit 31 is set to flag that // combo box items are absolute times and not relative times like below - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, (LPARAM)((ULONG)((h * 60 + m) * 60) | 0x80000000)); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, (LPARAM)((ULONG)((h * 60 + m) * 60) | 0x80000000)); li.QuadPart += (ULONGLONG)30 * MinutesToFileTime; @@ -861,60 +789,57 @@ static void PopulateTimeCombo(HWND Dialog, UINT nIDTime, BOOL bRelative, const S return; } - // + //////////////////////////////////////////////////////////////////////////////////////// - SendDlgItemMessage(Dialog, nIDTime, CB_RESETCONTENT, 0, 0); + SendDlgItemMessage(hwndDlg, nIDTime, CB_RESETCONTENT, 0, 0); - lpszMinutes = Translate("Minutes"); - lpszHours = Translate("Hours"); - - SYSTEMTIMEtoFILETIME(tmUtc, (FILETIME*)&li); - ref = li.QuadPart; + SystemTimeToFileTime(tmUtc, (FILETIME*)&li); + ULONGLONG ref = li.QuadPart; // NOTE: item data contains offset from reference time (tmUtc) in seconds // cur time FileTimeToTzLocalST((FILETIME*)&li, &tm2); - wCurHour = tm2.wHour; - wCurMinute = tm2.wMinute; - mir_snprintf(s, "%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); + WORD wCurHour = tm2.wHour; + WORD wCurMinute = tm2.wMinute; + mir_snwprintf(s, L"%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute); + int n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); // 5 minutes li.QuadPart += (ULONGLONG)5 * MinutesToFileTime; FileTimeToTzLocalST((FILETIME*)&li, &tm2); - mir_snprintf(s, "%02d:%02d (5 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); + mir_snwprintf(s, L"%02d:%02d (5 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, TranslateT("Minutes")); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); // 10 minutes li.QuadPart += (ULONGLONG)5 * MinutesToFileTime; FileTimeToTzLocalST((FILETIME*)&li, &tm2); - mir_snprintf(s, "%02d:%02d (10 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); + mir_snwprintf(s, L"%02d:%02d (10 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, TranslateT("Minutes")); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); // 15 minutes li.QuadPart += (ULONGLONG)5 * MinutesToFileTime; FileTimeToTzLocalST((FILETIME*)&li, &tm2); - mir_snprintf(s, "%02d:%02d (15 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); + mir_snwprintf(s, L"%02d:%02d (15 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, TranslateT("Minutes")); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); // 30 minutes li.QuadPart += (ULONGLONG)15 * MinutesToFileTime; FileTimeToTzLocalST((FILETIME*)&li, &tm2); - mir_snprintf(s, "%02d:%02d (30 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, lpszMinutes); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); + mir_snwprintf(s, L"%02d:%02d (30 %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, TranslateT("Minutes")); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, (LPARAM)((li.QuadPart - ref) / FILETIME_TICKS_PER_SEC)); // round +1h time to nearest even or half hour li.QuadPart += (ULONGLONG)30 * MinutesToFileTime; li.QuadPart = (li.QuadPart / (30 * MinutesToFileTime)) * (30 * MinutesToFileTime); // add from +1 to +23.5 (in half hour steps) if crossing daylight saving boundary it may be 22.5 or 24.5 hours - for (i = 0; i < 50; i++) { + for (int i = 0; i < 50; i++) { UINT dt; FileTimeToTzLocalST((FILETIME*)&li, &tm2); @@ -931,93 +856,81 @@ static void PopulateTimeCombo(HWND Dialog, UINT nIDTime, BOOL bRelative, const S } // icq-style display 1.0, 1.5 etc. hours even though that isn't accurate due to rounding - //mir_snprintf(s, "%02d:%02d (%d.%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, 1+(i>>1), (i&1) ? 5 : 0, lpszHours); + //mir_snwprintf(s, L"%02d:%02d (%d.%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, 1+(i>>1), (i&1) ? 5 : 0, lpszHours); // display delta time more accurately to match reformatting (that icq doesn't do) dt = (UINT)((li.QuadPart / MinutesToFileTime) - (ref / MinutesToFileTime)); if (dt < 60) - mir_snprintf(s, "%02d:%02d (%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt, lpszMinutes); + mir_snwprintf(s, L"%02d:%02d (%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt, TranslateT("Minutes")); else - mir_snprintf(s, "%02d:%02d (%d.%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt / 60, ((dt % 60) * 10) / 60, lpszHours); - n = SendDlgItemMessage(Dialog, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDTime, CB_SETITEMDATA, n, dt * 60); + mir_snwprintf(s, L"%02d:%02d (%d.%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt / 60, ((dt % 60) * 10) / 60, TranslateT("Hours")); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETITEMDATA, n, dt * 60); li.QuadPart += (ULONGLONG)30 * MinutesToFileTime; } } -static void PopulateTimeOffsetCombo(HWND Dialog, UINT nIDCombo) +static void PopulateTimeOffsetCombo(HWND hwndDlg, UINT nIDCombo) { - int n; - char s[MAX_PATH]; - - SendDlgItemMessage(Dialog, nIDCombo, CB_RESETCONTENT, 0, 0); - - LPCSTR lpszMinutes = Translate("Minutes"); - LPCSTR lpszHour = Translate("Hour"); - LPCSTR lpszHours = Translate("Hours"); - LPCSTR lpszDay = Translate("Day"); - LPCSTR lpszDays = Translate("Days"); - LPCSTR lpszWeek = Translate("Week"); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_RESETCONTENT, 0, 0); // 5 - 55 minutes (in 5 minute steps) + wchar_t s[MAX_PATH]; for (int i = 1; i < 12; i++) { - mir_snprintf(s, "%d %s", i * 5, lpszMinutes); - n = SendDlgItemMessage(Dialog, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDCombo, CB_SETITEMDATA, n, i * 5); + mir_snwprintf(s, L"%d %s", i * 5, TranslateT("Minutes")); + int n = SendDlgItemMessage(hwndDlg, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_SETITEMDATA, n, i * 5); } // 1 hour - mir_snprintf(s, "1 %s", lpszHour); - n = SendDlgItemMessage(Dialog, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDCombo, CB_SETITEMDATA, n, 60); + mir_snwprintf(s, L"1 %s", TranslateT("Hour")); + int n = SendDlgItemMessage(hwndDlg, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_SETITEMDATA, n, 60); // 2, 4, 8 hours for (int i = 2; i <= 8; i += 2) { - mir_snprintf(s, "%d %s", i, lpszHours); - n = SendDlgItemMessage(Dialog, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDCombo, CB_SETITEMDATA, n, i * 60); + mir_snwprintf(s, L"%d %s", i, TranslateT("Hours")); + n = SendDlgItemMessage(hwndDlg, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_SETITEMDATA, n, i * 60); } // 1 day - mir_snprintf(s, "1 %s", lpszDay); - n = SendDlgItemMessage(Dialog, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDCombo, CB_SETITEMDATA, n, 24 * 60); + mir_snwprintf(s, L"1 %s", TranslateT("Day")); + n = SendDlgItemMessage(hwndDlg, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_SETITEMDATA, n, 24 * 60); // 2-4 days for (int i = 2; i <= 4; i++) { - mir_snprintf(s, "%d %s", i, lpszDays); - n = SendDlgItemMessage(Dialog, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDCombo, CB_SETITEMDATA, n, i * 24 * 60); + mir_snwprintf(s, L"%d %s", i, TranslateT("Days")); + n = SendDlgItemMessage(hwndDlg, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_SETITEMDATA, n, i * 24 * 60); } // 1 week - mir_snprintf(s, "1 %s", lpszWeek); - n = SendDlgItemMessage(Dialog, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, nIDCombo, CB_SETITEMDATA, n, 7 * 24 * 60); + mir_snwprintf(s, L"1 %s", TranslateT("Week")); + n = SendDlgItemMessage(hwndDlg, nIDCombo, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, nIDCombo, CB_SETITEMDATA, n, 7 * 24 * 60); } // returns non-zero if specified time was inside "missing" hour of daylight saving // IMPORTANT: triggerRelUtcOut is only initialized if IsRelativeCombo() is TRUE and return value is 0 -static int ReformatTimeInputEx(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal, ULARGE_INTEGER *triggerRelUtcOut) +static int ReformatTimeInputEx(HWND hwndDlg, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal, ULARGE_INTEGER *triggerRelUtcOut) { int n; UINT dt; - char buf[64]; const ULONGLONG MinutesToFileTime = (ULONGLONG)60 * FILETIME_TICKS_PER_SEC; if (h < 0) { // time value is an offset ('m' holds the offset in minutes) - if (IsRelativeCombo(Dialog, nIDTime)) { + if (IsRelativeCombo(hwndDlg, nIDTime)) { + // get reference time (UTC) from hidden control + wchar_t buf[64]; + GetDlgItemText(hwndDlg, nIDRefTime, buf, 30); + ULONGLONG ref; ULARGE_INTEGER li; - SYSTEMTIME tm; - - // get reference time (UTC) from hidden control - { - GetDlgItemText(Dialog, nIDRefTime, buf, 30); - li.QuadPart = ref = _strtoui64(buf, nullptr, 16); - } + li.QuadPart = ref = _wcstoui64(buf, nullptr, 16); // clamp delta time to 23.5 hours (coule be issues otherwise as relative combo only handles <24) if (m > (23 * 60 + 30)) @@ -1025,6 +938,7 @@ static int ReformatTimeInputEx(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h li.QuadPart += (ULONGLONG)(m * 60) * FILETIME_TICKS_PER_SEC; + SYSTEMTIME tm; FileTimeToTzLocalST((FILETIME*)&li, &tm); h = (int)tm.wHour; m = (int)tm.wMinute; @@ -1035,179 +949,168 @@ static int ReformatTimeInputEx(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h dt = (UINT)((li.QuadPart / MinutesToFileTime) - (ref / MinutesToFileTime)); if (dt < 60) - mir_snprintf(buf, "%02d:%02d (%d %s)", h, m, dt, Translate("Minutes")); + mir_snwprintf(buf, L"%02d:%02d (%d %s)", h, m, dt, TranslateT("Minutes")); else - mir_snprintf(buf, "%02d:%02d (%d.%d %s)", h, m, dt / 60, ((dt % 60) * 10) / 60, Translate("Hours")); + mir_snwprintf(buf, L"%02d:%02d (%d.%d %s)", h, m, dt / 60, ((dt % 60) * 10) / 60, TranslateT("Hours")); // search for preset - n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf); if (n != CB_ERR) { - SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, n, 0); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETCURSEL, n, 0); return 0; } - SetDlgItemText(Dialog, nIDTime, buf); + SetDlgItemText(hwndDlg, nIDTime, buf); } else { // should never happen - SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, 0, 0); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETCURSEL, 0, 0); } return 0; } - // - - mir_snprintf(buf, "%02d:%02d", h, m); + wchar_t buf[64]; + mir_snwprintf(buf, L"%02d:%02d", h, m); // search for preset first - n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf); if (n != CB_ERR) { - SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, n, 0); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETCURSEL, n, 0); return 0; } - if (IsRelativeCombo(Dialog, nIDTime)) { + if (IsRelativeCombo(hwndDlg, nIDTime)) { // date format is a time offset ("24:43 (5 Minutes)" etc.) - ULONGLONG ref; - SYSTEMTIME tmRefLocal; SYSTEMTIME tmTriggerLocal, tmTriggerLocal2; // get reference time (UTC) from hidden control - { - GetDlgItemText(Dialog, nIDRefTime, buf, 30); - ref = _strtoui64(buf, nullptr, 16); - } + GetDlgItemText(hwndDlg, nIDRefTime, buf, 30); + ULONGLONG ref = _wcstoui64(buf, nullptr, 16); + SYSTEMTIME tmRefLocal; FileTimeToTzLocalST((FILETIME*)&ref, &tmRefLocal); - { - ULARGE_INTEGER li; - const UINT nRefT = (UINT)tmRefLocal.wHour * 60 + (UINT)tmRefLocal.wMinute; - const UINT nT = h * 60 + m; - - tmTriggerLocal = tmRefLocal; - tmTriggerLocal.wHour = (WORD)h; - tmTriggerLocal.wMinute = (WORD)m; - tmTriggerLocal.wSecond = 0; - tmTriggerLocal.wMilliseconds = 0; - - if (nT < nRefT) { - // (this special case only works correctly if time can be returned in triggerRelUtcOut) - if (tmRefLocal.wHour == tmTriggerLocal.wHour && triggerRelUtcOut) { - // check for special case if daylight saving ends in this hour, then interpret as within the next hour - TzLocalSTToFileTime(&tmTriggerLocal, (FILETIME*)&li); - li.QuadPart += (ULONGLONG)3600 * FILETIME_TICKS_PER_SEC; - FileTimeToTzLocalST((FILETIME*)&li, &tmTriggerLocal2); - if ((tmTriggerLocal2.wHour * 60 + tmTriggerLocal2.wMinute) == (tmTriggerLocal.wHour * 60 + tmTriggerLocal.wMinute)) - // special case detected - goto output_result; - } - - // tomorrow (add 24h to local time) - SYSTEMTIMEtoFILETIME(&tmTriggerLocal, (FILETIME*)&li); - li.QuadPart += (ULONGLONG)(24 * 3600)*FILETIME_TICKS_PER_SEC; - FILETIMEtoSYSTEMTIME((FILETIME*)&li, &tmTriggerLocal); + ULARGE_INTEGER li; + const UINT nRefT = (UINT)tmRefLocal.wHour * 60 + (UINT)tmRefLocal.wMinute; + const UINT nT = h * 60 + m; + + tmTriggerLocal = tmRefLocal; + tmTriggerLocal.wHour = (WORD)h; + tmTriggerLocal.wMinute = (WORD)m; + tmTriggerLocal.wSecond = 0; + tmTriggerLocal.wMilliseconds = 0; + + if (nT < nRefT) { + // (this special case only works correctly if time can be returned in triggerRelUtcOut) + if (tmRefLocal.wHour == tmTriggerLocal.wHour && triggerRelUtcOut) { + // check for special case if daylight saving ends in this hour, then interpret as within the next hour + TzLocalSTToFileTime(&tmTriggerLocal, (FILETIME*)&li); + li.QuadPart += (ULONGLONG)3600 * FILETIME_TICKS_PER_SEC; + FileTimeToTzLocalST((FILETIME*)&li, &tmTriggerLocal2); + if ((tmTriggerLocal2.wHour * 60 + tmTriggerLocal2.wMinute) == (tmTriggerLocal.wHour * 60 + tmTriggerLocal.wMinute)) + // special case detected + goto output_result; } - // clean up value for potential daylight saving boundary - TzLocalSTToFileTime(&tmTriggerLocal, (FILETIME*)&li); - FileTimeToTzLocalST((FILETIME*)&li, &tmTriggerLocal2); - - // NOTE: win32 time functions will round hour downward if supplied hour does not exist due to daylight saving - // for example if supplied time is 02:30 on the night the clock is turned forward at 02:00 to 03:00, thus - // there never is a 02:30, the time functions will convert it to 01:30 (and not 03:30 as one might think) - // (02:00 would return 01:00) - - // check for special case when the current time and requested time is inside the "missing" hour - // standard->daylight switch, so that the cleaned up time ends up being earlier than the current - // time even though it originally wasn't (see note above) - if ((tmTriggerLocal2.wHour * 60 + tmTriggerLocal2.wMinute) < (tmTriggerLocal.wHour * 60 + tmTriggerLocal.wMinute)) { - // special case detected, fall back to current time so at least the reminder won't be missed - // due to ending up at an undesired time (this way the user immediately notices something was wrong) - SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, 0, 0); + // tomorrow (add 24h to local time) + SystemTimeToFileTime(&tmTriggerLocal, (FILETIME*)&li); + li.QuadPart += (ULONGLONG)(24 * 3600)*FILETIME_TICKS_PER_SEC; + FileTimeToSystemTime((FILETIME*)&li, &tmTriggerLocal); + } + + // clean up value for potential daylight saving boundary + TzLocalSTToFileTime(&tmTriggerLocal, (FILETIME*)&li); + FileTimeToTzLocalST((FILETIME*)&li, &tmTriggerLocal2); + + // NOTE: win32 time functions will round hour downward if supplied hour does not exist due to daylight saving + // for example if supplied time is 02:30 on the night the clock is turned forward at 02:00 to 03:00, thus + // there never is a 02:30, the time functions will convert it to 01:30 (and not 03:30 as one might think) + // (02:00 would return 01:00) + + // check for special case when the current time and requested time is inside the "missing" hour + // standard->daylight switch, so that the cleaned up time ends up being earlier than the current + // time even though it originally wasn't (see note above) + if ((tmTriggerLocal2.wHour * 60 + tmTriggerLocal2.wMinute) < (tmTriggerLocal.wHour * 60 + tmTriggerLocal.wMinute)) { + // special case detected, fall back to current time so at least the reminder won't be missed + // due to ending up at an undesired time (this way the user immediately notices something was wrong) + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETCURSEL, 0, 0); invalid_dst: - MessageBox(Dialog, Translate("The specified time is invalid due to begin of daylight saving (summer time)."), SECTIONNAME, MB_OK | MB_ICONWARNING); - return 1; - } + MessageBox(hwndDlg, TranslateT("The specified time is invalid due to begin of daylight saving (summer time)."), _A2W(SECTIONNAME), MB_OK | MB_ICONWARNING); + return 1; + } output_result: - if (triggerRelUtcOut) - *triggerRelUtcOut = li; + if (triggerRelUtcOut) + *triggerRelUtcOut = li; - dt = (UINT)((li.QuadPart / MinutesToFileTime) - (ref / MinutesToFileTime)); + dt = (UINT)((li.QuadPart / MinutesToFileTime) - (ref / MinutesToFileTime)); - if (dt < 60) - mir_snprintf(buf, "%02d:%02d (%d %s)", h, m, dt, Translate("Minutes")); - else - mir_snprintf(buf, "%02d:%02d (%d.%d %s)", h, m, dt / 60, ((dt % 60) * 10) / 60, Translate("Hours")); - } + if (dt < 60) + mir_snwprintf(buf, L"%02d:%02d (%d %s)", h, m, dt, TranslateT("Minutes")); + else + mir_snwprintf(buf, L"%02d:%02d (%d.%d %s)", h, m, dt / 60, ((dt % 60) * 10) / 60, TranslateT("Hours")); } else { // absolute time (00:00 to 23:59), clean up time to make sure it's not inside "missing" hour (will be rounded downard) - - FILETIME ft; SYSTEMTIME Date = *pDateLocal; Date.wHour = h; Date.wMinute = m; Date.wSecond = 0; Date.wMilliseconds = 0; + FILETIME ft; TzLocalSTToFileTime(&Date, &ft); FileTimeToTzLocalST(&ft, &Date); if ((int)Date.wHour != h || (int)Date.wMinute != m) { - mir_snprintf(buf, "%02d:%02d", (UINT)Date.wHour, (UINT)Date.wMinute); + mir_snwprintf(buf, L"%02d:%02d", Date.wHour, Date.wMinute); // search for preset again - n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf); + n = SendDlgItemMessage(hwndDlg, nIDTime, CB_FINDSTRING, -1, (LPARAM)buf); if (n != CB_ERR) { - SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, n, 0); + SendDlgItemMessage(hwndDlg, nIDTime, CB_SETCURSEL, n, 0); goto invalid_dst; } - SetDlgItemText(Dialog, nIDTime, buf); - + SetDlgItemText(hwndDlg, nIDTime, buf); goto invalid_dst; } } - SetDlgItemText(Dialog, nIDTime, buf); - + SetDlgItemText(hwndDlg, nIDTime, buf); return 0; } -static __inline int ReformatTimeInput(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal) +static __inline int ReformatTimeInput(HWND hwndDlg, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal) { - return ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDateLocal, nullptr); + return ReformatTimeInputEx(hwndDlg, nIDTime, nIDRefTime, h, m, pDateLocal, nullptr); } // in: pDate contains the desired trigger date in LOCAL time // out: pDate contains the resulting trigger time and date in UTC -static BOOL GetTriggerTime(HWND Dialog, UINT nIDTime, UINT nIDRefTime, SYSTEMTIME *pDate) +static BOOL GetTriggerTime(HWND hwndDlg, UINT nIDTime, UINT nIDRefTime, SYSTEMTIME *pDate) { - ULARGE_INTEGER li; - char buf[32]; int h, m; // get reference (UTC) time from hidden control - { - GetDlgItemText(Dialog, nIDRefTime, buf, 30); - li.QuadPart = _strtoui64(buf, nullptr, 16); - } + wchar_t buf[32]; + GetDlgItemText(hwndDlg, nIDRefTime, buf, 30); + ULARGE_INTEGER li; + li.QuadPart = _wcstoui64(buf, nullptr, 16); - int n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0); + int n = SendDlgItemMessage(hwndDlg, nIDTime, CB_GETCURSEL, 0, 0); if (n != CB_ERR) { // use preset value preset_value:; - if (IsRelativeCombo(Dialog, nIDTime)) { + if (IsRelativeCombo(hwndDlg, nIDTime)) { // time offset from ref time ("24:43 (5 Minutes)" etc.) - UINT nDeltaSeconds = (UINT)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, n, 0); + UINT nDeltaSeconds = (UINT)SendDlgItemMessage(hwndDlg, nIDTime, CB_GETITEMDATA, n, 0); li.QuadPart += (ULONGLONG)nDeltaSeconds * FILETIME_TICKS_PER_SEC; - FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate); + FileTimeToSystemTime((FILETIME*)&li, pDate); // if specified time is a small offset (< 10 Minutes) then retain current second count for better accuracy // otherwise try to match specified time (which never contains seconds only even minutes) as close as possible @@ -1219,7 +1122,7 @@ preset_value:; else { // absolute time (offset from midnight on pDate) - UINT nDeltaSeconds = (UINT)((ULONG)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, n, 0) & ~0x80000000); + UINT nDeltaSeconds = (UINT)((ULONG)SendDlgItemMessage(hwndDlg, nIDTime, CB_GETITEMDATA, n, 0) & ~0x80000000); pDate->wHour = 0; pDate->wMinute = 0; pDate->wSecond = 0; @@ -1227,43 +1130,41 @@ preset_value:; TzLocalSTToFileTime(pDate, (FILETIME*)&li); li.QuadPart += (ULONGLONG)nDeltaSeconds * FILETIME_TICKS_PER_SEC; - FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate); + FileTimeToSystemTime((FILETIME*)&li, pDate); } return TRUE; } // user entered a custom value - - GetDlgItemText(Dialog, nIDTime, buf, 30); - - if (!ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(Dialog, nIDTime))) { - MessageBox(Dialog, Translate("The specified time is invalid."), SECTIONNAME, MB_OK | MB_ICONWARNING); + GetDlgItemText(hwndDlg, nIDTime, buf, 30); + if (!ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(hwndDlg, nIDTime))) { + MessageBox(hwndDlg, TranslateT("The specified time is invalid."), _A2W(SECTIONNAME), MB_OK | MB_ICONWARNING); return FALSE; } - if (IsRelativeCombo(Dialog, nIDTime)) { + if (IsRelativeCombo(hwndDlg, nIDTime)) { // date has not been changed, the specified time is a time between reftime and reftime+24h ULARGE_INTEGER li2; - if (ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDate, &li2)) + if (ReformatTimeInputEx(hwndDlg, nIDTime, nIDRefTime, h, m, pDate, &li2)) return FALSE; // check if reformatted value is a preset - if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR) + if ((n = SendDlgItemMessage(hwndDlg, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR) goto preset_value; - FILETIMEtoSYSTEMTIME((FILETIME*)&li2, pDate); + FileTimeToSystemTime((FILETIME*)&li2, pDate); return TRUE; } else { - if (ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDate, nullptr)) + if (ReformatTimeInputEx(hwndDlg, nIDTime, nIDRefTime, h, m, pDate, nullptr)) return FALSE; // check if reformatted value is a preset - if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR) + if ((n = SendDlgItemMessage(hwndDlg, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR) goto preset_value; } @@ -1275,27 +1176,25 @@ preset_value:; pDate->wMilliseconds = 0; TzLocalSTToFileTime(pDate, (FILETIME*)&li); - FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate); + FileTimeToSystemTime((FILETIME*)&li, pDate); return TRUE; } -static void OnDateChanged(HWND Dialog, UINT nDateID, UINT nTimeID, UINT nRefTimeID) +static void OnDateChanged(HWND hwndDlg, UINT nDateID, UINT nTimeID, UINT nRefTimeID) { // repopulate time combo list with regular times (not offsets like "23:32 (5 minutes)" etc.) + wchar_t s[32]; + GetDlgItemText(hwndDlg, nTimeID, s, _countof(s)); - SYSTEMTIME Date, DateUtc; int h = -1, m; - char s[32]; - - GetDlgItemText(Dialog, nTimeID, s, 30); - ParseTime(s, &h, &m, FALSE, FALSE); - SendDlgItemMessage(Dialog, nDateID, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); + SYSTEMTIME Date, DateUtc; + SendDlgItemMessage(hwndDlg, nDateID, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); TzSpecificLocalTimeToSystemTime(nullptr, &Date, &DateUtc); - PopulateTimeCombo(Dialog, nTimeID, FALSE, &DateUtc); + PopulateTimeCombo(hwndDlg, nTimeID, FALSE, &DateUtc); if (h < 0) { // parsing failed, default to current time @@ -1305,61 +1204,58 @@ static void OnDateChanged(HWND Dialog, UINT nDateID, UINT nTimeID, UINT nRefTime m = (UINT)tm.wMinute; } - ReformatTimeInput(Dialog, nTimeID, nRefTimeID, h, m, &Date); + ReformatTimeInput(hwndDlg, nTimeID, nRefTimeID, h, m, &Date); } -static INT_PTR CALLBACK DlgProcNotifyReminder(HWND Dialog, UINT Message, WPARAM wParam, LPARAM lParam) +static INT_PTR CALLBACK DlgProcNotifyReminder(HWND hwndDlg, UINT Message, WPARAM wParam, LPARAM lParam) { switch (Message) { case WM_INITDIALOG: { SYSTEMTIME tm; ULARGE_INTEGER li; - GetSystemTime(&tm); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li); + SystemTimeToFileTime(&tm, (FILETIME*)&li); - TranslateDialogDefault(Dialog); + TranslateDialogDefault(hwndDlg); // save reference time in hidden control, is needed when adding reminder to properly detect if speicifed // time wrapped around to tomorrow or not (dialog could in theory be open for a longer period of time // which could potentially mess up things otherwise) - { - char s[32]; - mir_snprintf(s, "%I64x", li.QuadPart); - SetDlgItemText(Dialog, IDC_REFTIME, s); - } + wchar_t s[32]; + mir_snwprintf(s, L"%I64x", li.QuadPart); + SetDlgItemText(hwndDlg, IDC_REFTIME, s); - BringWindowToTop(Dialog); + BringWindowToTop(hwndDlg); - PopulateTimeOffsetCombo(Dialog, IDC_REMINDAGAININ); + PopulateTimeOffsetCombo(hwndDlg, IDC_REMINDAGAININ); - ShowWindow(GetDlgItem(Dialog, IDC_REMINDAGAININ), SW_SHOW); - ShowWindow(GetDlgItem(Dialog, IDC_DATEAGAIN), SW_HIDE); - ShowWindow(GetDlgItem(Dialog, IDC_TIMEAGAIN), SW_HIDE); - ShowWindow(GetDlgItem(Dialog, IDC_STATIC_DATE), SW_HIDE); - ShowWindow(GetDlgItem(Dialog, IDC_STATIC_TIME), SW_HIDE); - CheckDlgButton(Dialog, IDC_AFTER, BST_CHECKED); - CheckDlgButton(Dialog, IDC_ONDATE, BST_UNCHECKED); - SendDlgItemMessage(Dialog, IDC_REMINDAGAININ, CB_SETCURSEL, 0, 0); + ShowWindow(GetDlgItem(hwndDlg, IDC_REMINDAGAININ), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_DATEAGAIN), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_TIMEAGAIN), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_DATE), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_TIME), SW_HIDE); + CheckDlgButton(hwndDlg, IDC_AFTER, BST_CHECKED); + CheckDlgButton(hwndDlg, IDC_ONDATE, BST_UNCHECKED); + SendDlgItemMessage(hwndDlg, IDC_REMINDAGAININ, CB_SETCURSEL, 0, 0); - SendDlgItemMessage(Dialog, IDC_REMDATA, EM_LIMITTEXT, MAX_REMINDER_LEN, 0); + SendDlgItemMessage(hwndDlg, IDC_REMDATA, EM_LIMITTEXT, MAX_REMINDER_LEN, 0); - PopulateTimeCombo(Dialog, IDC_TIMEAGAIN, TRUE, &tm); + PopulateTimeCombo(hwndDlg, IDC_TIMEAGAIN, TRUE, &tm); FileTimeToTzLocalST((FILETIME*)&li, &tm); // make sure date picker uses reference time - SendDlgItemMessage(Dialog, IDC_DATEAGAIN, DTM_SETSYSTEMTIME, 0, (LPARAM)&tm); - InitDatePicker(Dialog, IDC_DATEAGAIN); + SendDlgItemMessage(hwndDlg, IDC_DATEAGAIN, DTM_SETSYSTEMTIME, 0, (LPARAM)&tm); + InitDatePicker(hwndDlg, IDC_DATEAGAIN); - SendDlgItemMessage(Dialog, IDC_TIMEAGAIN, CB_SETCURSEL, 0, 0); + SendDlgItemMessage(hwndDlg, IDC_TIMEAGAIN, CB_SETCURSEL, 0, 0); return TRUE; } case WM_NCDESTROY: - RemoveProp(GetDlgItem(Dialog, IDC_DATEAGAIN), TEXT("OldWndProc")); + RemoveProp(GetDlgItem(hwndDlg, IDC_DATEAGAIN), TEXT("OldWndProc")); return TRUE; case WM_NOTIFY: @@ -1368,248 +1264,210 @@ static INT_PTR CALLBACK DlgProcNotifyReminder(HWND Dialog, UINT Message, WPARAM switch (NM->hdr.code) { case DTN_DATETIMECHANGE: - OnDateChanged(Dialog, IDC_DATEAGAIN, IDC_TIMEAGAIN, IDC_REFTIME); + OnDateChanged(hwndDlg, IDC_DATEAGAIN, IDC_TIMEAGAIN, IDC_REFTIME); break; } } break; case WM_CLOSE: - { - int ReminderCount = TreeGetCount(RemindersList); - for (int I = 0; I < ReminderCount; I++) { - REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I); - - if (pReminder->handle == Dialog) { - DeleteReminder(pReminder); - JustSaveReminders(); - break; - } - } - NOTIFY_LIST(); + if (auto *pReminder = FindReminder(hwndDlg)) { + DeleteReminder(pReminder); + JustSaveReminders(); } - DestroyWindow(Dialog); + + NOTIFY_LIST(); + DestroyWindow(hwndDlg); return TRUE; case WM_COMMAND: - { - switch (LOWORD(wParam)) { - case IDC_TIMEAGAIN: - switch (HIWORD(wParam)) { - case CBN_KILLFOCUS: - // reformat displayed value - if (SendDlgItemMessage(Dialog, IDC_TIMEAGAIN, CB_GETCURSEL, 0, 0) == CB_ERR) { - char buf[64]; - int h, m; - GetDlgItemText(Dialog, IDC_TIMEAGAIN, buf, 30); - - if (ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(Dialog, IDC_TIMEAGAIN))) { - SYSTEMTIME Date; - SendDlgItemMessage(Dialog, IDC_DATEAGAIN, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); - - ReformatTimeInput(Dialog, IDC_TIMEAGAIN, IDC_REFTIME, h, m, &Date); - } - else { - SendDlgItemMessage(Dialog, IDC_TIMEAGAIN, CB_SETCURSEL, 0, 0); - } + switch (LOWORD(wParam)) { + case IDC_TIMEAGAIN: + switch (HIWORD(wParam)) { + case CBN_KILLFOCUS: + // reformat displayed value + if (SendDlgItemMessage(hwndDlg, IDC_TIMEAGAIN, CB_GETCURSEL, 0, 0) == CB_ERR) { + wchar_t buf[64]; + GetDlgItemText(hwndDlg, IDC_TIMEAGAIN, buf, _countof(buf)); + + int h, m; + if (ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(hwndDlg, IDC_TIMEAGAIN))) { + SYSTEMTIME Date; + SendDlgItemMessage(hwndDlg, IDC_DATEAGAIN, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); + + ReformatTimeInput(hwndDlg, IDC_TIMEAGAIN, IDC_REFTIME, h, m, &Date); + } + else { + SendDlgItemMessage(hwndDlg, IDC_TIMEAGAIN, CB_SETCURSEL, 0, 0); } - break; } break; - - case IDC_REMINDAGAININ: - switch (HIWORD(wParam)) { - case CBN_KILLFOCUS: - // reformat displayed value if it has been edited - if (SendDlgItemMessage(Dialog, IDC_REMINDAGAININ, CB_GETCURSEL, 0, 0) == CB_ERR) { - char buf[64]; - int h, m; - GetDlgItemText(Dialog, IDC_REMINDAGAININ, buf, 30); - - if (ParseTime(buf, &h, &m, TRUE, TRUE) && h + m) { - if (h) { - LPCSTR lpszHours = Translate("Hours"); - mir_snprintf(buf, "%d:%02d %s", h, m, lpszHours); - } - else { - LPCSTR lpszMinutes = Translate("Minutes"); - mir_snprintf(buf, "%d %s", m, lpszMinutes); - } - SetDlgItemText(Dialog, IDC_REMINDAGAININ, buf); - } - else { - SendDlgItemMessage(Dialog, IDC_REMINDAGAININ, CB_SETCURSEL, 0, 0); - } + } + break; + + case IDC_REMINDAGAININ: + switch (HIWORD(wParam)) { + case CBN_KILLFOCUS: + // reformat displayed value if it has been edited + if (SendDlgItemMessage(hwndDlg, IDC_REMINDAGAININ, CB_GETCURSEL, 0, 0) == CB_ERR) { + wchar_t buf[64]; + GetDlgItemText(hwndDlg, IDC_REMINDAGAININ, buf, 30); + + int h, m; + if (ParseTime(buf, &h, &m, TRUE, TRUE) && h + m) { + if (h) + mir_snwprintf(buf, L"%d:%02d %s", h, m, TranslateT("Hours")); + else + mir_snwprintf(buf, L"%d %s", m, TranslateT("Minutes")); + + SetDlgItemText(hwndDlg, IDC_REMINDAGAININ, buf); } - break; + else SendDlgItemMessage(hwndDlg, IDC_REMINDAGAININ, CB_SETCURSEL, 0, 0); } break; + } + break; + + case IDC_AFTER: + ShowWindow(GetDlgItem(hwndDlg, IDC_REMINDAGAININ), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_DATEAGAIN), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_TIMEAGAIN), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_DATE), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_TIME), SW_HIDE); + return TRUE; - case IDC_AFTER: - ShowWindow(GetDlgItem(Dialog, IDC_REMINDAGAININ), SW_SHOW); - ShowWindow(GetDlgItem(Dialog, IDC_DATEAGAIN), SW_HIDE); - ShowWindow(GetDlgItem(Dialog, IDC_TIMEAGAIN), SW_HIDE); - ShowWindow(GetDlgItem(Dialog, IDC_STATIC_DATE), SW_HIDE); - ShowWindow(GetDlgItem(Dialog, IDC_STATIC_TIME), SW_HIDE); - return TRUE; + case IDC_ONDATE: + ShowWindow(GetDlgItem(hwndDlg, IDC_DATEAGAIN), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_TIMEAGAIN), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_DATE), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_TIME), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_REMINDAGAININ), SW_HIDE); + return TRUE; - case IDC_ONDATE: - ShowWindow(GetDlgItem(Dialog, IDC_DATEAGAIN), SW_SHOW); - ShowWindow(GetDlgItem(Dialog, IDC_TIMEAGAIN), SW_SHOW); - ShowWindow(GetDlgItem(Dialog, IDC_STATIC_DATE), SW_SHOW); - ShowWindow(GetDlgItem(Dialog, IDC_STATIC_TIME), SW_SHOW); - ShowWindow(GetDlgItem(Dialog, IDC_REMINDAGAININ), SW_HIDE); - return TRUE; + case IDC_DISMISS: + if (auto *pReminder = FindReminder(hwndDlg)) { + DeleteReminder(pReminder); + JustSaveReminders(); + } - case IDC_DISMISS: - { - int ReminderCount = TreeGetCount(RemindersList); - for (int I = 0; I < ReminderCount; I++) { - REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I); - - if (pReminder->handle == Dialog) { - DeleteReminder(pReminder); - JustSaveReminders(); - break; - } - } - NOTIFY_LIST(); - DestroyWindow(Dialog); - return TRUE; - } + NOTIFY_LIST(); + DestroyWindow(hwndDlg); + return TRUE; - case IDC_REMINDAGAIN: - { - int ReminderCount = TreeGetCount(RemindersList); - for (int I = 0; I < ReminderCount; I++) { - REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I); - - if (pReminder->handle == Dialog) { - if (IsDlgButtonChecked(Dialog, IDC_AFTER) == BST_CHECKED) { - // delta time - - ULONGLONG TT; - SYSTEMTIME tm; - ULARGE_INTEGER li; - - GetSystemTime(&tm); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li); - - int n = SendDlgItemMessage(Dialog, IDC_REMINDAGAININ, CB_GETCURSEL, 0, 0); - if (n != CB_ERR) { - TT = SendDlgItemMessage(Dialog, IDC_REMINDAGAININ, CB_GETITEMDATA, n, 0) * 60; - - if (TT >= 24 * 3600) { - // selection is 1 day or more, take daylight saving boundaries into consideration - // (ie. 24 hour might actually be 23 or 25, in order for reminder to pop up at the - // same time tomorrow) - SYSTEMTIME tm2, tm3; - ULARGE_INTEGER li2 = li; - FileTimeToTzLocalST((FILETIME*)&li2, &tm2); - li2.QuadPart += (TT * FILETIME_TICKS_PER_SEC); - FileTimeToTzLocalST((FILETIME*)&li2, &tm3); - if (tm2.wHour != tm3.wHour || tm2.wMinute != tm3.wMinute) { - // boundary crossed - - // do a quick and dirty sanity check that times not more than 2 hours apart - if (abs((int)(tm3.wHour * 60 + tm3.wMinute) - (int)(tm2.wHour * 60 + tm2.wMinute)) <= 120) { - // adjust TT so that same HH:MM is set - tm3.wHour = tm2.wHour; - tm3.wMinute = tm2.wMinute; - TzLocalSTToFileTime(&tm3, (FILETIME*)&li2); - TT = (li2.QuadPart - li.QuadPart) / FILETIME_TICKS_PER_SEC; - } - } - } - } - else { - // parse user input - char s[32]; - int h = 0, m = 0; - GetDlgItemText(Dialog, IDC_REMINDAGAININ, s, 30); - ParseTime(s, &h, &m, TRUE, TRUE); - m += h * 60; - if (!m) { - MessageBox(Dialog, Translate("The specified time offset is invalid."), SECTIONNAME, MB_OK | MB_ICONWARNING); - return TRUE; - } - - TT = m * 60; + case IDC_REMINDAGAIN: + if (auto *pReminder = FindReminder(hwndDlg)) { + if (IsDlgButtonChecked(hwndDlg, IDC_AFTER) == BST_CHECKED) { + // delta time + ULONGLONG TT; + SYSTEMTIME tm; + ULARGE_INTEGER li; + + GetSystemTime(&tm); + SystemTimeToFileTime(&tm, (FILETIME*)&li); + + int n = SendDlgItemMessage(hwndDlg, IDC_REMINDAGAININ, CB_GETCURSEL, 0, 0); + if (n != CB_ERR) { + TT = SendDlgItemMessage(hwndDlg, IDC_REMINDAGAININ, CB_GETITEMDATA, n, 0) * 60; + if (TT >= 24 * 3600) { + // selection is 1 day or more, take daylight saving boundaries into consideration + // (ie. 24 hour might actually be 23 or 25, in order for reminder to pop up at the + // same time tomorrow) + SYSTEMTIME tm2, tm3; + ULARGE_INTEGER li2 = li; + FileTimeToTzLocalST((FILETIME*)&li2, &tm2); + li2.QuadPart += (TT * FILETIME_TICKS_PER_SEC); + FileTimeToTzLocalST((FILETIME*)&li2, &tm3); + if (tm2.wHour != tm3.wHour || tm2.wMinute != tm3.wMinute) { + // boundary crossed + + // do a quick and dirty sanity check that times not more than 2 hours apart + if (abs((int)(tm3.wHour * 60 + tm3.wMinute) - (int)(tm2.wHour * 60 + tm2.wMinute)) <= 120) { + // adjust TT so that same HH:MM is set + tm3.wHour = tm2.wHour; + tm3.wMinute = tm2.wMinute; + TzLocalSTToFileTime(&tm3, (FILETIME*)&li2); + TT = (li2.QuadPart - li.QuadPart) / FILETIME_TICKS_PER_SEC; } - - pReminder->When = li; - pReminder->When.QuadPart += (TT * FILETIME_TICKS_PER_SEC); } - else if (IsDlgButtonChecked(Dialog, IDC_ONDATE) == BST_CHECKED) { - SYSTEMTIME Date; + } + } + else { + // parse user input + wchar_t s[32]; + GetDlgItemText(hwndDlg, IDC_REMINDAGAININ, s, _countof(s)); + + int h = 0, m = 0; + ParseTime(s, &h, &m, TRUE, TRUE); + m += h * 60; + if (!m) { + MessageBox(hwndDlg, TranslateT("The specified time offset is invalid."), _A2W(SECTIONNAME), MB_OK | MB_ICONWARNING); + return TRUE; + } - SendDlgItemMessage(Dialog, IDC_DATEAGAIN, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); + TT = m * 60; + } - if (!GetTriggerTime(Dialog, IDC_TIMEAGAIN, IDC_REFTIME, &Date)) - break; + pReminder->When = li; + pReminder->When.QuadPart += (TT * FILETIME_TICKS_PER_SEC); + } + else if (IsDlgButtonChecked(hwndDlg, IDC_ONDATE) == BST_CHECKED) { + SYSTEMTIME Date; - SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&pReminder->When); - } + SendDlgItemMessage(hwndDlg, IDC_DATEAGAIN, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); - // update reminder text - { - char *ReminderText = nullptr; - int SzT = SendDlgItemMessage(Dialog, IDC_REMDATA, WM_GETTEXTLENGTH, 0, 0); - if (SzT) { - if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN; - ReminderText = (char*)malloc(SzT + 1); - GetDlgItemText(Dialog, IDC_REMDATA, ReminderText, SzT + 1); - } - if (pReminder->Reminder) - free(pReminder->Reminder); - pReminder->Reminder = ReminderText; - } + if (!GetTriggerTime(hwndDlg, IDC_TIMEAGAIN, IDC_REFTIME, &Date)) + break; - pReminder->RemVisible = FALSE; - pReminder->handle = nullptr; + SystemTimeToFileTime(&Date, (FILETIME*)&pReminder->When); + } - // re-insert tree item sorted - TreeDelete(&RemindersList, pReminder); - TreeAddSorted(&RemindersList, pReminder, ReminderSortCb); - JustSaveReminders(); - break; - } - } - NOTIFY_LIST(); - DestroyWindow(Dialog); - return TRUE; + // update reminder text + char *ReminderText = nullptr; + int SzT = SendDlgItemMessage(hwndDlg, IDC_REMDATA, WM_GETTEXTLENGTH, 0, 0); + if (SzT) { + if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN; + ReminderText = (char*)malloc(SzT + 1); + GetDlgItemTextA(hwndDlg, IDC_REMDATA, ReminderText, SzT + 1); } - case IDC_NONE: - { - // create note from remainder - int ReminderCount = TreeGetCount(RemindersList); - for (int I = 0; I < ReminderCount; I++) { - REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I); - - if (pReminder->handle == Dialog) { - // get up-to-date reminder text - char *ReminderText = nullptr; - int SzT = SendDlgItemMessage(Dialog, IDC_REMDATA, WM_GETTEXTLENGTH, 0, 0); - if (SzT) { - if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN; - ReminderText = (char*)malloc(SzT + 1); - GetDlgItemText(Dialog, IDC_REMDATA, ReminderText, SzT + 1); - } + if (pReminder->Reminder) + free(pReminder->Reminder); + pReminder->Reminder = ReminderText; - SetFocus(NewNote(0, 0, -1, -1, ReminderText, nullptr, TRUE, TRUE, 0)->REHwnd); - break; - } - } - return TRUE; + pReminder->RemVisible = FALSE; + pReminder->handle = nullptr; + + // re-insert tree item sorted + arReminders.remove(pReminder); + arReminders.insert(pReminder); + JustSaveReminders(); + } + + NOTIFY_LIST(); + DestroyWindow(hwndDlg); + return TRUE; + + case IDC_NONE: + // create note from remainder + if (auto *pReminder = FindReminder(hwndDlg)) { + // get up-to-date reminder text + char *ReminderText = nullptr; + int SzT = SendDlgItemMessage(hwndDlg, IDC_REMDATA, WM_GETTEXTLENGTH, 0, 0); + if (SzT) { + if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN; + ReminderText = (char*)malloc(SzT + 1); + GetDlgItemTextA(hwndDlg, IDC_REMDATA, ReminderText, SzT + 1); } + + NewNote(0, 0, -1, -1, ReminderText, nullptr, TRUE, TRUE, 0); } + return TRUE; } } return FALSE; } -static INT_PTR CALLBACK DlgProcNewReminder(HWND Dialog, UINT Message, WPARAM wParam, LPARAM lParam) +static INT_PTR CALLBACK DlgProcNewReminder(HWND hwndDlg, UINT Message, WPARAM wParam, LPARAM lParam) { HICON hIcon = nullptr; switch (Message) { @@ -1620,151 +1478,130 @@ static INT_PTR CALLBACK DlgProcNewReminder(HWND Dialog, UINT Message, WPARAM wPa if (NewReminderVisible == 2) { // opening the edit reminder dialog (uses same dialog resource as add reminder) - SetWindowText(Dialog, Translate("Reminder")); - SetDlgItemText(Dialog, IDC_ADDREMINDER, Translate("&Update Reminder")); - ShowWindow(GetDlgItem(Dialog, IDC_VIEWREMINDERS), SW_HIDE); + SetWindowText(hwndDlg, TranslateT("Reminder")); + SetDlgItemText(hwndDlg, IDC_ADDREMINDER, TranslateT("&Update Reminder")); + ShowWindow(GetDlgItem(hwndDlg, IDC_VIEWREMINDERS), SW_HIDE); li = pEditReminder->When; - FILETIMEtoSYSTEMTIME((FILETIME*)&li, &tm); + FileTimeToSystemTime((FILETIME*)&li, &tm); } else { GetSystemTime(&tm); - SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li); + SystemTimeToFileTime(&tm, (FILETIME*)&li); } - TranslateDialogDefault(Dialog); + TranslateDialogDefault(hwndDlg); // save reference time in hidden control, is needed when adding reminder to properly detect if speicifed // time wrapped around to tomorrow or not (dialog could in theory be open for a longer period of time // which could potentially mess up things otherwise) - { - char s[32]; - mir_snprintf(s, "%I64x", li.QuadPart); - SetDlgItemText(Dialog, IDC_REFTIME, s); - } + wchar_t s[64]; + mir_snwprintf(s, L"%I64x", li.QuadPart); + SetDlgItemText(hwndDlg, IDC_REFTIME, s); - /*CheckDlgButton(Dialog, IDC_NONE, BST_CHECKED); - CheckDlgButton(Dialog, IDC_DAILY, BST_UNCHECKED); - CheckDlgButton(Dialog, IDC_WEEKLY, BST_UNCHECKED); - CheckDlgButton(Dialog, IDC_MONTHLY, BST_UNCHECKED);*/ - - PopulateTimeCombo(Dialog, IDC_TIME, NewReminderVisible != 2, &tm); + PopulateTimeCombo(hwndDlg, IDC_TIME, NewReminderVisible != 2, &tm); FileTimeToTzLocalST((FILETIME*)&li, &tm); // make sure date picker uses reference time - SendDlgItemMessage(Dialog, IDC_DATE, DTM_SETSYSTEMTIME, 0, (LPARAM)&tm); - InitDatePicker(Dialog, IDC_DATE); + SendDlgItemMessage(hwndDlg, IDC_DATE, DTM_SETSYSTEMTIME, 0, (LPARAM)&tm); + InitDatePicker(hwndDlg, IDC_DATE); - SendDlgItemMessage(Dialog, IDC_REMINDER, EM_LIMITTEXT, MAX_REMINDER_LEN, 0); + SendDlgItemMessage(hwndDlg, IDC_REMINDER, EM_LIMITTEXT, MAX_REMINDER_LEN, 0); if (NewReminderVisible == 2) { - char s[32]; - mir_snprintf(s, "%02d:%02d", (UINT)tm.wHour, (UINT)tm.wMinute); + mir_snwprintf(s, L"%02d:%02d", tm.wHour, tm.wMinute); // search for preset first - int n = SendDlgItemMessage(Dialog, IDC_TIME, CB_FINDSTRING, (WPARAM)-1, (LPARAM)s); + int n = SendDlgItemMessageA(hwndDlg, IDC_TIME, CB_FINDSTRING, (WPARAM)-1, (LPARAM)s); if (n != CB_ERR) - SendDlgItemMessage(Dialog, IDC_TIME, CB_SETCURSEL, n, 0); + SendDlgItemMessage(hwndDlg, IDC_TIME, CB_SETCURSEL, n, 0); else - SetDlgItemText(Dialog, IDC_TIME, s); + SetDlgItemText(hwndDlg, IDC_TIME, s); - SetDlgItemText(Dialog, IDC_REMINDER, pEditReminder->Reminder); - } - else { - SendDlgItemMessage(Dialog, IDC_TIME, CB_SETCURSEL, 0, 0); + SetDlgItemTextA(hwndDlg, IDC_REMINDER, pEditReminder->Reminder); } + else SendDlgItemMessage(hwndDlg, IDC_TIME, CB_SETCURSEL, 0, 0); // populate sound repeat combo - { - char s[64]; - LPCSTR lpszEvery = Translate("Every"); - LPCSTR lpszSeconds = Translate("Seconds"); - - // NOTE: use multiples of REMINDER_UPDATE_INTERVAL_SHORT (currently 5 seconds) + wchar_t *lpszEvery = TranslateT("Every"); + wchar_t *lpszSeconds = TranslateT("Seconds"); - int n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)Translate("Never")); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, 0); + // NOTE: use multiples of REMINDER_UPDATE_INTERVAL_SHORT (currently 5 seconds) + int n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)TranslateT("Never")); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, 0); - mir_snprintf(s, "%s 5 %s", lpszEvery, lpszSeconds); - n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)5); + mir_snwprintf(s, L"%s 5 %s", lpszEvery, lpszSeconds); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)5); - mir_snprintf(s, "%s 10 %s", lpszEvery, lpszSeconds); - n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)10); + mir_snwprintf(s, L"%s 10 %s", lpszEvery, lpszSeconds); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)10); - mir_snprintf(s, "%s 15 %s", lpszEvery, lpszSeconds); - n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)15); + mir_snwprintf(s, L"%s 15 %s", lpszEvery, lpszSeconds); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)15); - mir_snprintf(s, "%s 20 %s", lpszEvery, lpszSeconds); - n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)20); + mir_snwprintf(s, L"%s 20 %s", lpszEvery, lpszSeconds); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)20); - mir_snprintf(s, "%s 30 %s", lpszEvery, lpszSeconds); - n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)30); + mir_snwprintf(s, L"%s 30 %s", lpszEvery, lpszSeconds); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)30); - mir_snprintf(s, "%s 60 %s", lpszEvery, lpszSeconds); - n = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)60); + mir_snwprintf(s, L"%s 60 %s", lpszEvery, lpszSeconds); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_ADDSTRING, 0, (LPARAM)s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETITEMDATA, n, (LPARAM)60); - if (NewReminderVisible == 2 && pEditReminder->RepeatSound) { - mir_snprintf(s, "%s %d %s", lpszEvery, pEditReminder->RepeatSound, lpszSeconds); - SetDlgItemText(Dialog, IDC_COMBO_REPEATSND, s); - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETCURSEL, SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)s), 0); - } - else { - SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_SETCURSEL, 0, 0); - } + if (NewReminderVisible == 2 && pEditReminder->RepeatSound) { + mir_snwprintf(s, L"%s %d %s", lpszEvery, pEditReminder->RepeatSound, lpszSeconds); + SetDlgItemText(hwndDlg, IDC_COMBO_REPEATSND, s); + SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETCURSEL, SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)s), 0); } + else SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_SETCURSEL, 0, 0); // populate sound selection combo - { - int n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)Translate("Default")); - SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_SETITEMDATA, n, 0); - n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)Translate("Alternative 1")); - SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_SETITEMDATA, n, 1); - n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)Translate("Alternative 2")); - SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_SETITEMDATA, n, (LPARAM)2); - n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)Translate("None")); - SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_SETITEMDATA, n, (LPARAM)-1); - - if (NewReminderVisible == 2 && pEditReminder->SoundSel) { - const UINT n2 = pEditReminder->SoundSel < 0 ? 3 : pEditReminder->SoundSel; - SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_SETCURSEL, n2, 0); - } - else { - SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_SETCURSEL, 0, 0); - } + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)TranslateT("Default")); + SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_SETITEMDATA, n, 0); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)TranslateT("Alternative 1")); + SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_SETITEMDATA, n, 1); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)TranslateT("Alternative 2")); + SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_SETITEMDATA, n, (LPARAM)2); + n = SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_ADDSTRING, 0, (LPARAM)TranslateT("None")); + SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_SETITEMDATA, n, (LPARAM)-1); + + if (NewReminderVisible == 2 && pEditReminder->SoundSel) { + const UINT n2 = pEditReminder->SoundSel < 0 ? 3 : pEditReminder->SoundSel; + SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_SETCURSEL, n2, 0); } + else SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_SETCURSEL, 0, 0); hIcon = IcoLib_GetIconByHandle(iconList[12].hIcolib); - SendDlgItemMessage(Dialog, IDC_BTN_PLAYSOUND, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon); + SendDlgItemMessage(hwndDlg, IDC_BTN_PLAYSOUND, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hIcon); if (NewReminderVisible == 2 && pEditReminder->SoundSel) { - EnableWindow(GetDlgItem(Dialog, IDC_BTN_PLAYSOUND), pEditReminder->SoundSel >= 0); - EnableWindow(GetDlgItem(Dialog, IDC_COMBO_REPEATSND), pEditReminder->SoundSel >= 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PLAYSOUND), pEditReminder->SoundSel >= 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_COMBO_REPEATSND), pEditReminder->SoundSel >= 0); } if (NewReminderVisible == 2) - SetFocus(GetDlgItem(Dialog, IDC_ADDREMINDER)); + SetFocus(GetDlgItem(hwndDlg, IDC_ADDREMINDER)); else - SetFocus(GetDlgItem(Dialog, IDC_REMINDER)); - - return FALSE; + SetFocus(GetDlgItem(hwndDlg, IDC_REMINDER)); } + return FALSE; case WM_NCDESTROY: - RemoveProp(GetDlgItem(Dialog, IDC_DATE), TEXT("OldWndProc")); + RemoveProp(GetDlgItem(hwndDlg, IDC_DATE), TEXT("OldWndProc")); return TRUE; case WM_CLOSE: - if (NewReminderVisible == 2) { + if (NewReminderVisible == 2) pEditReminder->RemVisible = FALSE; - } - DestroyWindow(Dialog); + + DestroyWindow(hwndDlg); NewReminderVisible = FALSE; pEditReminder = nullptr; return TRUE; @@ -1772,143 +1609,134 @@ static INT_PTR CALLBACK DlgProcNewReminder(HWND Dialog, UINT Message, WPARAM wPa case WM_NOTIFY: if (wParam == IDC_DATE) { LPNMLISTVIEW NM = (LPNMLISTVIEW)lParam; - switch (NM->hdr.code) { case DTN_DATETIMECHANGE: - OnDateChanged(Dialog, IDC_DATE, IDC_TIME, IDC_REFTIME); + OnDateChanged(hwndDlg, IDC_DATE, IDC_TIME, IDC_REFTIME); break; } } break; case WM_COMMAND: - { - switch (LOWORD(wParam)) { - case IDC_TIME: - switch (HIWORD(wParam)) { - case CBN_KILLFOCUS: - // reformat displayed value - if (SendDlgItemMessage(Dialog, IDC_TIME, CB_GETCURSEL, 0, 0) == CB_ERR) { - char buf[64]; - int h, m; - GetDlgItemText(Dialog, IDC_TIME, buf, 30); - - if (ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(Dialog, IDC_TIME))) { - SYSTEMTIME Date; - SendDlgItemMessage(Dialog, IDC_DATE, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); - ReformatTimeInput(Dialog, IDC_TIME, IDC_REFTIME, h, m, &Date); - } - else { - SendDlgItemMessage(Dialog, IDC_TIME, CB_SETCURSEL, 0, 0); - } + switch (LOWORD(wParam)) { + case IDC_TIME: + switch (HIWORD(wParam)) { + case CBN_KILLFOCUS: + // reformat displayed value + if (SendDlgItemMessage(hwndDlg, IDC_TIME, CB_GETCURSEL, 0, 0) == CB_ERR) { + wchar_t buf[64]; + GetDlgItemText(hwndDlg, IDC_TIME, buf, 30); + + int h, m; + if (ParseTime(buf, &h, &m, FALSE, IsRelativeCombo(hwndDlg, IDC_TIME))) { + SYSTEMTIME Date; + SendDlgItemMessage(hwndDlg, IDC_DATE, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); + ReformatTimeInput(hwndDlg, IDC_TIME, IDC_REFTIME, h, m, &Date); } - break; + else SendDlgItemMessage(hwndDlg, IDC_TIME, CB_SETCURSEL, 0, 0); } break; + } + break; - case IDC_COMBO_SOUND: - switch (HIWORD(wParam)) { - case CBN_SELENDOK: - { - int n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0); - n = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, n, 0); + case IDC_COMBO_SOUND: + switch (HIWORD(wParam)) { + case CBN_SELENDOK: + { + int n = SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0); + n = (int)SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETITEMDATA, n, 0); - EnableWindow(GetDlgItem(Dialog, IDC_BTN_PLAYSOUND), n >= 0); - EnableWindow(GetDlgItem(Dialog, IDC_COMBO_REPEATSND), n >= 0); - } - break; + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PLAYSOUND), n >= 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_COMBO_REPEATSND), n >= 0); } break; + } + break; - case IDC_BTN_PLAYSOUND: - { - int n = SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0); - n = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, n, 0); - switch (n) { - case 0: Skin_PlaySound("AlertReminder"); break; - case 1: Skin_PlaySound("AlertReminder2"); break; - case 2: Skin_PlaySound("AlertReminder3"); break; - } - } - return TRUE; - - case IDC_CLOSE: - if (NewReminderVisible == 2) { - pEditReminder->RemVisible = FALSE; + case IDC_BTN_PLAYSOUND: + { + int n = SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0); + n = (int)SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETITEMDATA, n, 0); + switch (n) { + case 0: Skin_PlaySound("AlertReminder"); break; + case 1: Skin_PlaySound("AlertReminder2"); break; + case 2: Skin_PlaySound("AlertReminder3"); break; } - DestroyWindow(Dialog); - NewReminderVisible = FALSE; - pEditReminder = nullptr; - return TRUE; - - case IDC_VIEWREMINDERS: - ListReminders(); - return TRUE; - - case IDC_ADDREMINDER: - { - SYSTEMTIME Date; - - SendDlgItemMessage(Dialog, IDC_DATE, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); - - if (!GetTriggerTime(Dialog, IDC_TIME, IDC_REFTIME, &Date)) - break; - - int RepeatSound = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_GETCURSEL, 0, 0); - if (RepeatSound != CB_ERR) - RepeatSound = SendDlgItemMessage(Dialog, IDC_COMBO_REPEATSND, CB_GETITEMDATA, (WPARAM)RepeatSound, 0); - else - RepeatSound = 0; - - int SzT = SendDlgItemMessage(Dialog, IDC_REMINDER, WM_GETTEXTLENGTH, 0, 0); - char *ReminderText = nullptr; - if (SzT) { - if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN; - ReminderText = (char*)malloc(SzT + 1); - GetDlgItemText(Dialog, IDC_REMINDER, ReminderText, SzT + 1); - } + } + return TRUE; - if (NewReminderVisible != 2) { - // new reminder - REMINDERDATA *TempRem = (REMINDERDATA*)malloc(sizeof(REMINDERDATA)); - TempRem->uid = CreateUid(); - SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&TempRem->When); - TempRem->Reminder = ReminderText; - TempRem->RemVisible = FALSE; - TempRem->SystemEventQueued = 0; - TempRem->RepeatSoundTTL = 0; - TempRem->SoundSel = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0), 0); - TempRem->RepeatSound = TempRem->SoundSel < 0 ? 0 : (UINT)RepeatSound; - TreeAddSorted(&RemindersList, TempRem, ReminderSortCb); - } - else { - // update existing reminder + case IDC_CLOSE: + if (NewReminderVisible == 2) + pEditReminder->RemVisible = FALSE; - SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&pEditReminder->When); - if (pEditReminder->Reminder) - free(pEditReminder->Reminder); - pEditReminder->Reminder = ReminderText; - pEditReminder->SoundSel = (int)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)SendDlgItemMessage(Dialog, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0), 0); - pEditReminder->RepeatSound = pEditReminder->SoundSel < 0 ? 0 : (UINT)RepeatSound; + DestroyWindow(hwndDlg); + NewReminderVisible = FALSE; + pEditReminder = nullptr; + return TRUE; - // re-insert tree item sorted - TreeDelete(&RemindersList, pEditReminder); - TreeAddSorted(&RemindersList, pEditReminder, ReminderSortCb); + case IDC_VIEWREMINDERS: + PluginMenuCommandViewReminders(0, 0); + return TRUE; - pEditReminder->RemVisible = FALSE; - } - SetDlgItemText(Dialog, IDC_REMINDER, ""); - JustSaveReminders(); - NOTIFY_LIST(); + case IDC_ADDREMINDER: + SYSTEMTIME Date; + SendDlgItemMessage(hwndDlg, IDC_DATE, DTM_GETSYSTEMTIME, 0, (LPARAM)&Date); + if (GetTriggerTime(hwndDlg, IDC_TIME, IDC_REFTIME, &Date)) { + int RepeatSound = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_GETCURSEL, 0, 0); + if (RepeatSound != CB_ERR) + RepeatSound = SendDlgItemMessage(hwndDlg, IDC_COMBO_REPEATSND, CB_GETITEMDATA, (WPARAM)RepeatSound, 0); + else + RepeatSound = 0; + + int SzT = SendDlgItemMessage(hwndDlg, IDC_REMINDER, WM_GETTEXTLENGTH, 0, 0); + char *ReminderText = nullptr; + if (SzT) { + if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN; + ReminderText = (char*)malloc(SzT + 1); + GetDlgItemTextA(hwndDlg, IDC_REMINDER, ReminderText, SzT + 1); + } - if (g_CloseAfterAddReminder || NewReminderVisible == 2) { - DestroyWindow(Dialog); - NewReminderVisible = FALSE; - pEditReminder = nullptr; - } + if (NewReminderVisible != 2) { + // new reminder + REMINDERDATA *TempRem = (REMINDERDATA*)malloc(sizeof(REMINDERDATA)); + TempRem->uid = CreateUid(); + SystemTimeToFileTime(&Date, (FILETIME*)&TempRem->When); + TempRem->Reminder = ReminderText; + TempRem->RemVisible = false; + TempRem->SystemEventQueued = 0; + TempRem->RepeatSoundTTL = 0; + TempRem->SoundSel = (int)SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0), 0); + TempRem->RepeatSound = TempRem->SoundSel < 0 ? 0 : (UINT)RepeatSound; + arReminders.insert(TempRem); + } + else { + // update existing reminder + SystemTimeToFileTime(&Date, (FILETIME*)&pEditReminder->When); + if (pEditReminder->Reminder) + free(pEditReminder->Reminder); + pEditReminder->Reminder = ReminderText; + pEditReminder->SoundSel = (int)SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETITEMDATA, (WPARAM)SendDlgItemMessage(hwndDlg, IDC_COMBO_SOUND, CB_GETCURSEL, 0, 0), 0); + pEditReminder->RepeatSound = pEditReminder->SoundSel < 0 ? 0 : (UINT)RepeatSound; + + // re-insert tree item sorted + arReminders.remove(pEditReminder); + arReminders.insert(pEditReminder); + + pEditReminder->RemVisible = false; + } + SetDlgItemTextA(hwndDlg, IDC_REMINDER, ""); + JustSaveReminders(); + NOTIFY_LIST(); + + if (g_plugin.bCloseAfterAddReminder || NewReminderVisible == 2) { + DestroyWindow(hwndDlg); + NewReminderVisible = false; + pEditReminder = nullptr; } } } + break; + case WM_DESTROY: IcoLib_ReleaseIcon(hIcon); break; @@ -1916,7 +1744,6 @@ static INT_PTR CALLBACK DlgProcNewReminder(HWND Dialog, UINT Message, WPARAM wPa return FALSE; } - INT_PTR OpenTriggeredReminder(WPARAM, LPARAM l) { if (!l) @@ -1932,35 +1759,24 @@ INT_PTR OpenTriggeredReminder(WPARAM, LPARAM l) if (QueuedReminderCount) QueuedReminderCount--; - { - char S1[128], S2[MAX_PATH]; - GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE); - - pReminder->RemVisible = TRUE; + wchar_t S1[128], S2[MAX_PATH]; + GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE); - HWND H = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NOTIFYREMINDER), nullptr, DlgProcNotifyReminder); - pReminder->handle = H; + pReminder->RemVisible = TRUE; - mir_snprintf(S2, "%s! - %s", Translate("Reminder"), S1); - SetWindowText(H, S2); + HWND H = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NOTIFYREMINDER), nullptr, DlgProcNotifyReminder); + pReminder->handle = H; - if (pReminder->Reminder) - SetDlgItemText(H, IDC_REMDATA, pReminder->Reminder); + mir_snwprintf(S2, L"%s! - %s", TranslateT("Reminder"), S1); + SetWindowText(H, S2); - BringWindowToTop(H); - } + if (pReminder->Reminder) + SetDlgItemTextA(H, IDC_REMDATA, pReminder->Reminder); + BringWindowToTop(H); return 0; } -void NewReminder(void) -{ - if (!NewReminderVisible) { - NewReminderVisible = TRUE; - CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_ADDREMINDER), nullptr, DlgProcNewReminder); - } -} - void EditReminder(REMINDERDATA *p) { if (!p) @@ -1981,52 +1797,50 @@ void EditReminder(REMINDERDATA *p) static void InitListView(HWND AHLV) { - int I = 0; - char S1[128]; - ListView_SetHoverTime(AHLV, 700); ListView_SetExtendedListViewStyle(AHLV, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_TRACKSELECT); ListView_DeleteAllItems(AHLV); - for (TREEELEMENT *TTE = RemindersList; TTE; TTE = (TREEELEMENT*)TTE->next) { - REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata; - + int i = 0; + for (auto &pReminder : arReminders) { LV_ITEM lvTIt; lvTIt.mask = LVIF_TEXT; - GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE); + wchar_t S1[128]; + GetTriggerTimeString(&pReminder->When, S1, _countof(S1), TRUE); - lvTIt.iItem = I; + lvTIt.iItem = i; lvTIt.iSubItem = 0; lvTIt.pszText = S1; ListView_InsertItem(AHLV, &lvTIt); lvTIt.mask = LVIF_TEXT; - char *S2 = GetPreviewString(pReminder->Reminder); - lvTIt.iItem = I; + + wchar_t *S2 = GetPreviewString(pReminder->Reminder); + lvTIt.iItem = i; lvTIt.iSubItem = 1; lvTIt.pszText = S2; ListView_SetItem(AHLV, &lvTIt); - I++; + i++; } ListView_SetItemState(AHLV, 0, LVIS_SELECTED, LVIS_SELECTED); } -void OnListResize(HWND Dialog) +void OnListResize(HWND hwndDlg) { HWND hList, hText, hBtnNew, hBtnClose; RECT lr, cr, tr, clsr; int th, btnh, btnw, MARGIN; - POINT org = { 0 }; + POINT org = {0}; - hList = GetDlgItem(Dialog, IDC_LISTREMINDERS); - hText = GetDlgItem(Dialog, IDC_REMINDERDATA); - hBtnNew = GetDlgItem(Dialog, IDC_ADDNEWREMINDER); - hBtnClose = GetDlgItem(Dialog, IDC_CLOSE); + hList = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); + hText = GetDlgItem(hwndDlg, IDC_REMINDERDATA); + hBtnNew = GetDlgItem(hwndDlg, IDC_ADDNEWREMINDER); + hBtnClose = GetDlgItem(hwndDlg, IDC_CLOSE); - ClientToScreen(Dialog, &org); - GetClientRect(Dialog, &cr); + ClientToScreen(hwndDlg, &org); + GetClientRect(hwndDlg, &cr); GetWindowRect(hList, &lr); OffsetRect(&lr, -org.x, -org.y); GetWindowRect(hText, &tr); OffsetRect(&tr, -org.x, -org.y); @@ -2056,17 +1870,17 @@ void OnListResize(HWND Dialog) clsr.left -= btnw + 2; MoveWindow(hBtnNew, clsr.left, clsr.top, btnw, btnh, FALSE); - RedrawWindow(Dialog, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); - //UpdateWindow(Dialog); + RedrawWindow(hwndDlg, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + //UpdateWindow(hwndDlg); } -void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols) +void UpdateGeomFromWnd(HWND hwndDlg, int *geom, int *colgeom, int nCols) { if (geom) { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(Dialog, &wp); + GetWindowPlacement(hwndDlg, &wp); geom[0] = wp.rcNormalPosition.left; geom[1] = wp.rcNormalPosition.top; @@ -2075,7 +1889,7 @@ void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols) } if (colgeom) { - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); + HWND H = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); for (int i = 0; i < nCols; i++) { colgeom[i] = ListView_GetColumnWidth(H, i); @@ -2087,10 +1901,10 @@ static BOOL DoListContextMenu(HWND AhWnd, WPARAM wParam, LPARAM lParam, REMINDER { HWND hwndListView = (HWND)wParam; if (hwndListView != GetDlgItem(AhWnd, IDC_LISTREMINDERS)) return FALSE; - HMENU hMenuLoad = LoadMenu(g_plugin.getInst(), "MNU_REMINDERPOPUP"); + HMENU hMenuLoad = LoadMenuA(g_plugin.getInst(), "MNU_REMINDERPOPUP"); HMENU FhMenu = GetSubMenu(hMenuLoad, 0); - MENUITEMINFO mii = { 0 }; + MENUITEMINFO mii = {0}; mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; mii.fState = MFS_DEFAULT; @@ -2107,18 +1921,18 @@ static BOOL DoListContextMenu(HWND AhWnd, WPARAM wParam, LPARAM lParam, REMINDER return TRUE; } -static INT_PTR CALLBACK DlgProcViewReminders(HWND Dialog, UINT Message, WPARAM wParam, LPARAM lParam) +static INT_PTR CALLBACK DlgProcViewReminders(HWND hwndDlg, UINT Message, WPARAM wParam, LPARAM lParam) { LV_COLUMN lvCol; switch (Message) { case WM_SIZE: - OnListResize(Dialog); - UpdateGeomFromWnd(Dialog, g_reminderListGeom, nullptr, 0); + OnListResize(hwndDlg); + UpdateGeomFromWnd(hwndDlg, g_reminderListGeom, nullptr, 0); break; case WM_MOVE: - UpdateGeomFromWnd(Dialog, g_reminderListGeom, nullptr, 0); + UpdateGeomFromWnd(hwndDlg, g_reminderListGeom, nullptr, 0); break; case WM_GETMINMAXINFO: @@ -2130,63 +1944,63 @@ static INT_PTR CALLBACK DlgProcViewReminders(HWND Dialog, UINT Message, WPARAM w return 0; case WM_RELOAD: - SetDlgItemText(Dialog, IDC_REMINDERDATA, ""); - InitListView(GetDlgItem(Dialog, IDC_LISTREMINDERS)); + SetDlgItemTextA(hwndDlg, IDC_REMINDERDATA, ""); + InitListView(GetDlgItem(hwndDlg, IDC_LISTREMINDERS)); return TRUE; case WM_CONTEXTMENU: { REMINDERDATA *pReminder = nullptr; - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); + HWND H = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I); - } + int i = ListView_GetSelectionMark(H); + if (i != -1) + pReminder = arReminders[i]; } - if (DoListContextMenu(Dialog, wParam, lParam, pReminder)) + if (DoListContextMenu(hwndDlg, wParam, lParam, pReminder)) return TRUE; } break; case WM_INITDIALOG: - Window_SetIcon_IcoLib(Dialog, iconList[6].hIcolib); + Window_SetIcon_IcoLib(hwndDlg, iconList[6].hIcolib); - TranslateDialogDefault(Dialog); - SetDlgItemText(Dialog, IDC_REMINDERDATA, ""); + TranslateDialogDefault(hwndDlg); + SetDlgItemTextA(hwndDlg, IDC_REMINDERDATA, ""); { - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); + HWND H = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); lvCol.mask = LVCF_TEXT | LVCF_WIDTH; - char *S2 = Translate("Reminder text"); - lvCol.pszText = S2; + lvCol.pszText = TranslateT("Reminder text"); lvCol.cx = g_reminderListColGeom[1]; ListView_InsertColumn(H, 0, &lvCol); + lvCol.mask = LVCF_TEXT | LVCF_WIDTH; - S2 = Translate("Date of activation"); - lvCol.pszText = S2; + lvCol.pszText = TranslateT("Date of activation"); lvCol.cx = g_reminderListColGeom[0]; ListView_InsertColumn(H, 0, &lvCol); + InitListView(H); + SetWindowLongPtr(GetDlgItem(H, 0), GWL_ID, IDC_LISTREMINDERS_HEADER); - LV = Dialog; + LV = hwndDlg; if (g_reminderListGeom[1] && g_reminderListGeom[2]) { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(Dialog, &wp); + GetWindowPlacement(hwndDlg, &wp); wp.rcNormalPosition.left = g_reminderListGeom[0]; wp.rcNormalPosition.top = g_reminderListGeom[1]; wp.rcNormalPosition.right = g_reminderListGeom[2] + g_reminderListGeom[0]; wp.rcNormalPosition.bottom = g_reminderListGeom[3] + g_reminderListGeom[1]; - SetWindowPlacement(Dialog, &wp); + SetWindowPlacement(hwndDlg, &wp); } } return TRUE; case WM_CLOSE: - DestroyWindow(Dialog); + DestroyWindow(hwndDlg); ListReminderVisible = FALSE; return TRUE; @@ -2197,18 +2011,17 @@ static INT_PTR CALLBACK DlgProcViewReminders(HWND Dialog, UINT Message, WPARAM w switch (NM->hdr.code) { case LVN_ITEMCHANGED: { - char *S2 = ((REMINDERDATA*)TreeGetAt(RemindersList, NM->iItem))->Reminder; - SetDlgItemText(Dialog, IDC_REMINDERDATA, S2); + char *S2 = arReminders[NM->iItem]->Reminder; + SetDlgItemTextA(hwndDlg, IDC_REMINDERDATA, S2); } break; case NM_DBLCLK: { - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); + HWND H = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - EditReminder((REMINDERDATA*)TreeGetAt(RemindersList, I)); - } + int i = ListView_GetSelectionMark(H); + if (i != -1) + EditReminder(arReminders[i]); } } break; @@ -2218,147 +2031,93 @@ static INT_PTR CALLBACK DlgProcViewReminders(HWND Dialog, UINT Message, WPARAM w LPNMHEADER NM = (LPNMHEADER)lParam; switch (NM->hdr.code) { case HDN_ENDTRACK: - UpdateGeomFromWnd(Dialog, nullptr, g_reminderListColGeom, _countof(g_reminderListColGeom)); + UpdateGeomFromWnd(hwndDlg, nullptr, g_reminderListColGeom, _countof(g_reminderListColGeom)); break; } } } break; + case WM_COMMAND: - { - switch (LOWORD(wParam)) { - case ID_CONTEXTMENUREMINDERLISTVIEW_EDIT: - { - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); - if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1) { - EditReminder((REMINDERDATA*)TreeGetAt(RemindersList, I)); - } - } + switch (LOWORD(wParam)) { + case ID_CONTEXTMENUREMINDERLISTVIEW_EDIT: + { + HWND H = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); + if (ListView_GetSelectedCount(H)) { + int i = ListView_GetSelectionMark(H); + if (i != -1) + EditReminder(arReminders[i]); } - return TRUE; + } + return TRUE; - case IDC_CLOSE: - DestroyWindow(Dialog); - ListReminderVisible = FALSE; - return TRUE; + case IDC_CLOSE: + DestroyWindow(hwndDlg); + ListReminderVisible = false; + return TRUE; - case IDM_NEWREMINDER: - case IDC_ADDNEWREMINDER: - NewReminder(); - return TRUE; + case IDM_NEWREMINDER: + case IDC_ADDNEWREMINDER: + PluginMenuCommandNewReminder(0, 0); + return TRUE; - case IDM_DELETEALLREMINDERS: - if (RemindersList && MessageBox(Dialog, Translate("Are you sure you want to delete all reminders?"), Translate(SECTIONNAME), MB_OKCANCEL) == IDOK) { - SetDlgItemText(Dialog, IDC_REMINDERDATA, ""); - DeleteReminders(); - InitListView(GetDlgItem(Dialog, IDC_LISTREMINDERS)); - } - return TRUE; + case IDM_DELETEALLREMINDERS: + if (arReminders.getCount() && IDOK == MessageBox(hwndDlg, TranslateT("Are you sure you want to delete all reminders?"), _A2W(SECTIONNAME), MB_OKCANCEL)) { + SetDlgItemTextA(hwndDlg, IDC_REMINDERDATA, ""); + DeleteReminders(); + InitListView(GetDlgItem(hwndDlg, IDC_LISTREMINDERS)); + } + return TRUE; - case IDM_DELETEREMINDER: - { - HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS); - if (ListView_GetSelectedCount(H)) { - int I = ListView_GetSelectionMark(H); - if (I != -1 - && MessageBox(Dialog, Translate("Are you sure you want to delete this reminder?"), Translate(SECTIONNAME), MB_OKCANCEL) == IDOK) { - SetDlgItemText(Dialog, IDC_REMINDERDATA, ""); - DeleteReminder((REMINDERDATA*)TreeGetAt(RemindersList, I)); - JustSaveReminders(); - InitListView(H); - } + case IDM_DELETEREMINDER: + { + HWND H = GetDlgItem(hwndDlg, IDC_LISTREMINDERS); + if (ListView_GetSelectedCount(H)) { + int i = ListView_GetSelectionMark(H); + if (i != -1 && IDOK == MessageBox(hwndDlg, TranslateT("Are you sure you want to delete this reminder?"), _A2W(SECTIONNAME), MB_OKCANCEL)) { + SetDlgItemTextA(hwndDlg, IDC_REMINDERDATA, ""); + DeleteReminder(arReminders[i]); + JustSaveReminders(); + InitListView(H); } - return TRUE; } + return TRUE; } } + break; + case WM_DESTROY: - Window_FreeIcon_IcoLib(Dialog); + Window_FreeIcon_IcoLib(hwndDlg); break; } return FALSE; } -void ListReminders(void) -{ - if (!ListReminderVisible) { - CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_LISTREMINDERS), nullptr, DlgProcViewReminders); - ListReminderVisible = TRUE; - } - else { - BringWindowToTop(LV); - } -} - - -///////////////////////////////////////////////////////////////////// -// Email/SMS and WinSock functions - -BOOL WS_Init() -{ - WSADATA wsd; - if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) return FALSE; - return TRUE; -} - -void WS_CleanUp() -{ - WSACleanup(); -} +///////////////////////////////////////////////////////////////////////////////////////// -int WS_Send(SOCKET s, char *data, int datalen) +INT_PTR PluginMenuCommandNewReminder(WPARAM, LPARAM) { - int rlen; - if ((rlen = send(s, data, datalen, 0)) == SOCKET_ERROR) return FALSE; - return TRUE; + if (!NewReminderVisible) { + NewReminderVisible = TRUE; + CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_ADDREMINDER), nullptr, DlgProcNewReminder); + } + return 0; } -unsigned long WS_ResolveName(char *name, WORD *port, int defaultPort) +INT_PTR PluginMenuCommandViewReminders(WPARAM, LPARAM) { - char *nameCopy = _strdup(name); - if (port != nullptr) - *port = defaultPort; - char *pcolon = strchr(nameCopy, ':'); - if (pcolon != nullptr) { - if (port != nullptr) *port = atoi(pcolon + 1); - *pcolon = 0; - } - if (inet_addr(nameCopy) == INADDR_NONE) { - HOSTENT *lk = gethostbyname(nameCopy); - if (lk == nullptr) - return SOCKET_ERROR; - - free(nameCopy); - return *(u_long*)lk->h_addr_list[0]; + if (!ListReminderVisible) { + CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_LISTREMINDERS), nullptr, DlgProcViewReminders); + ListReminderVisible = true; } - DWORD ret = inet_addr(nameCopy); - free(nameCopy); - return ret; + else BringWindowToTop(LV); + return 0; } -void Send(char *user, char *host, char *Msg, char *server) +INT_PTR PluginMenuCommandDeleteReminders(WPARAM, LPARAM) { - SOCKADDR_IN sockaddr; - WORD port; - char *ch = nullptr; - S = socket(AF_INET, SOCK_STREAM, 0); - if (!server) server = host; - if ((sockaddr.sin_addr.S_un.S_addr = WS_ResolveName(server, - &port, 25)) == SOCKET_ERROR) return; - sockaddr.sin_port = htons(port); - sockaddr.sin_family = AF_INET; - if (connect(S, (SOCKADDR*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) return; - ch = (char*)malloc(mir_strlen(user) + mir_strlen(host) + 16); - ch = (char*)realloc(ch, sprintf(ch, "rcpt to:%s@%s\r\n", user, host)); //!!!!!!!!!! - WS_Send(S, "mail from: \r\n", 13); - WS_Send(S, ch, (int)mir_strlen(ch)); - WS_Send(S, "data\r\n", 6); - WS_Send(S, "From:\r\n\r\n", 14); - WS_Send(S, Msg, (int)mir_strlen(Msg)); - WS_Send(S, "\r\n.\r\n", 5); - WS_Send(S, "quit\r\n", 6); - SAFE_FREE((void**)&ch); - closesocket(S); + if (arReminders.getCount()) + if (IDOK == MessageBox(nullptr, TranslateT("Are you sure you want to delete all reminders?"), TranslateT(SECTIONNAME), MB_OKCANCEL)) + DeleteReminders(); + return 0; } diff --git a/plugins/NotesAndReminders/src/stdafx.cpp b/plugins/NotesAndReminders/src/stdafx.cpp deleted file mode 100644 index 184538e1f7..0000000000 --- a/plugins/NotesAndReminders/src/stdafx.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "globals.h" \ No newline at end of file diff --git a/plugins/NotesAndReminders/src/stdafx.cxx b/plugins/NotesAndReminders/src/stdafx.cxx new file mode 100644 index 0000000000..1b563fc866 --- /dev/null +++ b/plugins/NotesAndReminders/src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "stdafx.h" \ No newline at end of file diff --git a/plugins/NotesAndReminders/src/stdafx.h b/plugins/NotesAndReminders/src/stdafx.h new file mode 100644 index 0000000000..16da30b03a --- /dev/null +++ b/plugins/NotesAndReminders/src/stdafx.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "miscutils.h" +#include "resource.h" +#include "version.h" + +#define MODULENAME "StickyNotes" +#define SECTIONNAME LPGEN("Notes & Reminders") + +struct CMPlugin : public PLUGIN +{ + CMPlugin(); + + CMOption bShowNotesAtStart, bShowScrollbar, bAddContListMI, bShowNoteButtons; + CMOption bCloseAfterAddReminder, bUseMSI; + + int Load() override; + int Unload() override; +}; + +// font IDs used with LoadNRFont +#define NR_FONTID_CAPTION 0 +#define NR_FONTID_BODY 1 +#define NR_FONTID_MAX NR_FONTID_BODY + +// normal timer interval for reminder update processing +#define REMINDER_UPDATE_INTERVAL 10000 + +// short timer interval for reminder updates used as long as there are pending alarams in the event queue +#define REMINDER_UPDATE_INTERVAL_SHORT 5000 + +extern void CreateMsgWindow(void); +extern void DestroyMsgWindow(void); + +void NewNote(int Ax, int Ay, int Aw, int Ah, char *Data, ULARGE_INTEGER *ID, BOOL Visible, BOOL bOnTop, int scrollV); +void LoadNotes(BOOL bIsStartup); +void SaveNotes(void); +void DeleteNotes(void); +void ShowHideNotes(void); + +void LoadReminders(void); +void SaveReminders(void); +void DeleteReminders(void); +bool CheckRemindersAndStart(void); + +void InitSettings(void); +void TermSettings(void); +void LoadNRFont(int i, LOGFONTA *lf, COLORREF *colour); + +BOOL WS_Init(); +void WS_CleanUp(); + +wchar_t* GetDateFormatStr(); +wchar_t* GetTimeFormatStr(); + +extern HINSTANCE hmiranda; + +extern HICON g_hReminderIcon; + +extern LOGFONTA lfBody, lfCaption; +extern HFONT hBodyFont, hCaptionFont; + +extern long BodyColor; +extern long CaptionFontColor, BodyFontColor; + +extern int g_NoteTitleDate, g_NoteTitleTime; + +extern int g_NoteWidth, g_NoteHeight; + +extern int g_Transparency; + +extern char *g_RemindSMS; + +extern char *g_lpszAltBrowser; + +extern int g_reminderListGeom[4]; +extern int g_reminderListColGeom[2]; +extern int g_notesListGeom[4]; +extern int g_notesListColGeom[4]; + +extern IconItem iconList[]; + +INT_PTR PluginMenuCommandAddNew(WPARAM, LPARAM); +INT_PTR PluginMenuCommandShowHide(WPARAM, LPARAM); +INT_PTR PluginMenuCommandViewNotes(WPARAM, LPARAM); +INT_PTR PluginMenuCommandAllBringFront(WPARAM, LPARAM); +INT_PTR PluginMenuCommandDeleteNotes(WPARAM, LPARAM); + +INT_PTR PluginMenuCommandNewReminder(WPARAM, LPARAM); +INT_PTR PluginMenuCommandViewReminders(WPARAM, LPARAM); +INT_PTR PluginMenuCommandDeleteReminders(WPARAM, LPARAM); + +int OnOptInitialise(WPARAM, LPARAM); diff --git a/plugins/NotesAndReminders/src/version.h b/plugins/NotesAndReminders/src/version.h index 8ab090eb82..f154f6bd1e 100644 --- a/plugins/NotesAndReminders/src/version.h +++ b/plugins/NotesAndReminders/src/version.h @@ -1,5 +1,5 @@ #define __MAJOR_VERSION 0 -#define __MINOR_VERSION 1 +#define __MINOR_VERSION 2 #define __RELEASE_NUM 0 #define __BUILD_NUM 1 -- cgit v1.2.3