diff options
Diffstat (limited to 'plugins/NotesAndReminders/src')
-rw-r--r-- | plugins/NotesAndReminders/src/globals.h | 146 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/hotkeys.cpp | 138 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/main.cpp | 390 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/miscutils.cpp | 246 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/miscutils.h | 47 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/notes.cpp | 2180 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/options.cpp | 636 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/reminders.cpp | 2881 | ||||
-rw-r--r-- | plugins/NotesAndReminders/src/resource.h | 65 |
9 files changed, 6729 insertions, 0 deletions
diff --git a/plugins/NotesAndReminders/src/globals.h b/plugins/NotesAndReminders/src/globals.h new file mode 100644 index 0000000000..ecdae3cf1c --- /dev/null +++ b/plugins/NotesAndReminders/src/globals.h @@ -0,0 +1,146 @@ +#define _CRT_SECURE_NO_WARNINGS
+
+#include <windows.h>
+#include <commctrl.h>
+#include <memory.h>
+#include <time.h>
+#include <richedit.h>
+#include <stdio.h>
+#include <WinSock.h>
+#include <mmsystem.h>
+
+#include "win2k.h"
+#include "newpluginapi.h"
+#include "m_system.h"
+#include "m_database.h"
+#include "m_clist.h"
+#include "m_langpack.h"
+#include "m_options.h"
+#include "m_skin.h"
+#include "m_fontservice.h"
+#include "m_hotkeys.h"
+#include "m_icolib.h"
+
+#include "m_toptoolbar.h"
+
+#include "miscutils.h"
+#include "resource.h"
+
+// {842A6668-F9DA-4968-BFD7-D2BD9DF848EE}
+#define MIID_NNR {0x842a6668, 0xf9da, 0x4968, {0xbf, 0xd7, 0xd2, 0xbd, 0x9d, 0xf8, 0x48, 0xee}}
+
+#define MODULENAME "StickyNotes"
+#define SECTIONNAME "Notes & Reminders"
+
+// 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 Visible,OnTop;
+ 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 OnTop,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 hinstance;
+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 BOOL g_isWin2kPlus;
+
+extern TCHAR *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 HANDLE hIconLibItem[];
+
+// 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 new file mode 100644 index 0000000000..d324d5fdd7 --- /dev/null +++ b/plugins/NotesAndReminders/src/hotkeys.cpp @@ -0,0 +1,138 @@ +#include "globals.h"
+
+#define MSG_WND_CLASS _T("MIM_SNMsgWindow")
+
+
+HWND HKHwnd;
+
+
+enum KB_ACTIONS {KB_NEW_NOTE = 1, KB_TOGGLE_NOTES, KB_NEW_REMINDER};
+
+
+void RegisterKeyBindings()
+{
+ HOTKEYDESC desc;
+
+ ZeroMemory(&desc, sizeof(desc));
+ desc.cbSize = sizeof(desc);
+ desc.ptszSection = _T(SECTIONNAME);
+ desc.dwFlags = HKD_TCHAR;
+
+ desc.pszName = MODULENAME"/NewNote";
+ desc.ptszDescription = LPGENT("New Note");
+ desc.lParam = KB_NEW_NOTE;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_INSERT);
+ desc.pszService = MODULENAME"/MenuCommandAddNew";
+ Hotkey_Register(&desc);
+
+ desc.pszName = MODULENAME"/ToggleNotesVis";
+ desc.ptszDescription = LPGENT("Toggle Notes Visibility");
+ desc.lParam = KB_TOGGLE_NOTES;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_ADD);
+ desc.pszService = MODULENAME"/MenuCommandShowHide";
+ Hotkey_Register(&desc);
+
+ desc.pszName = MODULENAME"/BringNotesFront";
+ desc.ptszDescription = LPGENT("Bring All Notes to Front");
+ desc.lParam = KB_TOGGLE_NOTES;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_HOME);
+ desc.pszService = MODULENAME"/MenuCommandBringAllFront";
+ Hotkey_Register(&desc);
+
+ desc.pszName = MODULENAME"/NewReminder";
+ desc.ptszDescription = LPGENT("New Reminder");
+ desc.lParam = KB_NEW_REMINDER;
+ desc.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, VK_SUBTRACT);
+ desc.pszService = MODULENAME"/MenuCommandNewReminder";
+ Hotkey_Register(&desc);
+}
+
+
+/*int HandleNRShortcuts(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ STICKYNOTE* PSN;
+
+ BOOL isShift = GetKeyState(VK_SHIFT) & 0x8000;
+ BOOL isAlt = GetKeyState(VK_MENU) & 0x8000;
+ BOOL isCtrl = (GetKeyState(VK_CONTROL) & 0x8000) && !isAlt;
+
+ int action;
+ MSG amsg;
+ amsg.hwnd = hwnd;
+ amsg.message = msg;
+ amsg.wParam = wParam;
+ amsg.lParam = lParam;
+
+ switch ( (action = CallService(MS_HOTKEY_CHECK, (WPARAM)&amsg, (LPARAM)SECTIONNAME)) )
+ {
+ case KB_NEW_NOTE:
+ PSN = NewNote(0,0,0,0,NULL,NULL,TRUE,TRUE,0);
+ SetFocus(PSN->REHwnd);
+ return FALSE;
+ case KB_TOGGLE_NOTES:
+ ShowHideNotes();
+ return FALSE;
+ case KB_NEW_REMINDER:
+ NewReminder();
+ return FALSE;
+ }
+
+ return -1;
+}*/
+
+
+LRESULT CALLBACK NotifyHotKeyWndProc(HWND AHwnd,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ BOOL b;
+
+ switch (Message)
+ {
+ case WM_TIMER:
+ {
+ KillTimer(HKHwnd,1026);
+ b = CheckRemindersAndStart();
+ SetTimer(HKHwnd,1026,b ? REMINDER_UPDATE_INTERVAL_SHORT : REMINDER_UPDATE_INTERVAL,0);
+
+ return FALSE;
+ }
+ }
+
+ return DefWindowProc(AHwnd,Message,wParam,lParam);
+}
+
+void CreateMsgWindow(void)
+{
+ HWND hParent = NULL;
+ WNDCLASSEX TWC = {0};
+
+ if (!GetClassInfoEx(hmiranda,MSG_WND_CLASS,&TWC))
+ {
+ TWC.style = 0;
+ TWC.cbClsExtra = 0;
+ TWC.cbWndExtra = 0;
+ TWC.hInstance = hmiranda;
+ TWC.hIcon = 0;
+ TWC.hCursor = 0;
+ TWC.hbrBackground = 0;
+ TWC.lpszMenuName = NULL;
+ TWC.lpszClassName = MSG_WND_CLASS;
+ TWC.cbSize = sizeof(TWC);
+ TWC.lpfnWndProc = NotifyHotKeyWndProc;
+ RegisterClassEx(&TWC);
+ }
+
+ if ( IsWinVer2000Plus() )
+ {
+ // win2k+ has special message-only windows support
+ hParent = HWND_MESSAGE;
+ }
+
+ HKHwnd = CreateWindowEx(WS_EX_TOOLWINDOW,MSG_WND_CLASS,_T("StickyNotes"),0,0,0,0,0,hParent,NULL,hmiranda,NULL);
+ SetTimer(HKHwnd,1026,REMINDER_UPDATE_INTERVAL,0);
+}
+
+void DestroyMsgWindow(void)
+{
+ KillTimer(HKHwnd,1026);
+ DestroyWindow(HKHwnd);
+}
diff --git a/plugins/NotesAndReminders/src/main.cpp b/plugins/NotesAndReminders/src/main.cpp new file mode 100644 index 0000000000..505aa21fbb --- /dev/null +++ b/plugins/NotesAndReminders/src/main.cpp @@ -0,0 +1,390 @@ +#include "globals.h"
+
+HINSTANCE hinstance = NULL;
+HINSTANCE hmiranda = NULL;
+int hLangpack;
+
+HANDLE hkOptInit = NULL;
+HANDLE hkTopToolbarInit = NULL;
+HANDLE hkModulesLoaded = NULL;
+HANDLE hkFontChange = NULL;
+HANDLE hkColorChange = NULL;
+HMODULE hUserDll = NULL;
+HMODULE hRichedDll = NULL;
+HMODULE hKernelDll = NULL;
+
+extern TREEELEMENT *g_Stickies;
+extern TREEELEMENT *RemindersList;
+
+static PLUGININFOEX pluginInfo =
+{
+ sizeof(PLUGININFOEX),
+ "Sticky Notes & Reminders",
+ PLUGIN_MAKE_VERSION(0,0,5,1),
+ "Sticky Notes & Reminders Implementation for Miranda IM.",
+ "Joe Kucera, Lubomir Kolev Ivanov, Georg Fischer",
+ "jokusoftware@users.sourceforge.net; d00mEr@dir.bg",
+ "(C) 2003,2005 Joe Kucera, Lubomir Ivanov",
+ "http://d00mer.freeshell.org/miranda/",
+ UNICODE_AWARE,
+ MIID_NNR
+};
+
+
+void RegisterFontServiceFonts();
+void RegisterKeyBindings();
+INT_PTR OpenTriggeredReminder(WPARAM w, LPARAM l);
+void BringAllNotesToFront(STICKYNOTE *pActive);
+void CloseNotesList();
+void CloseReminderList();
+
+INT_PTR PluginMenuCommandAddNew(WPARAM w,LPARAM l)
+{
+ STICKYNOTE* PSN;
+ PSN = NewNote(0,0,0,0,NULL,NULL,TRUE,TRUE,0);
+ SetFocus(PSN->REHwnd);
+ return 0;
+}
+
+INT_PTR PluginMenuCommandDeleteAll(WPARAM w,LPARAM l)
+{
+ if (g_Stickies && MessageBox(NULL, TranslateT("Are you sure you want to delete all notes?"), _T(SECTIONNAME), MB_OKCANCEL) == IDOK)
+ DeleteNotes();
+ return 0;
+}
+
+INT_PTR PluginMenuCommandShowHide(WPARAM w,LPARAM l)
+{
+ ShowHideNotes();
+ return 0;
+}
+
+INT_PTR PluginMenuCommandViewNotes(WPARAM w,LPARAM l)
+{
+ ListNotes();
+ return 0;
+}
+
+INT_PTR PluginMenuCommandAllBringFront(WPARAM w,LPARAM l)
+{
+ BringAllNotesToFront(NULL);
+ return 0;
+}
+
+INT_PTR PluginMenuCommandNewReminder(WPARAM w,LPARAM l)
+{
+ NewReminder();
+ return 0;
+}
+
+INT_PTR PluginMenuCommandViewReminders(WPARAM w,LPARAM l)
+{
+ ListReminders();
+ return 0;
+}
+
+INT_PTR PluginMenuCommandDeleteReminders(WPARAM w,LPARAM l)
+{
+ if (RemindersList && MessageBox(NULL, TranslateT("Are you sure you want to delete all reminders?"), _T(SECTIONNAME), MB_OKCANCEL) == IDOK)
+ DeleteReminders();
+ return 0;
+}
+
+struct
+{
+ TCHAR* szDescr;
+ char* szName;
+ int defIconID;
+}
+static const iconList[] =
+{
+ {LPGENT("New Reminder"), "AddReminder", IDI_ADDREMINDER},
+ {LPGENT("Delete All Notes"), "DeleteIcon", IDI_DELETEICON},
+ {LPGENT("New Note"), "NoteIcon", IDI_NOTEICON},
+ {LPGENT("Show/Hide Notes"), "ShowHide", IDI_SHOWHIDE},
+ {LPGENT("On Top Caption Icon"), "CaptionIcon", IDI_CAPTIONICON},
+ {LPGENT("Delete All Reminders"), "DeleteReminder", IDI_DELETEREMINDER},
+ {LPGENT("View Reminders"), "ViewReminders", IDI_VIEWREMINDERS},
+ {LPGENT("Not on Top Caption Icon"), "CaptionIconNotTop", IDI_CAPTIONICONNOTTOP},
+ {LPGENT("Hide Note Icon"), "HideNote", IDI_HIDENOTE},
+ {LPGENT("Remove Note Icon"), "RemoveNote", IDI_REMOVENOTE},
+ {LPGENT("Reminder Icon"), "Reminder", IDI_REMINDER},
+ {LPGENT("Bring All to Front"), "BringFront", IDI_BRINGFRONT},
+ {LPGENT("Play Sound Icon"), "PlaySound", IDI_PLAYSOUND},
+ {LPGENT("View Notes"), "ViewNotes", IDI_VIEWNOTES},
+ {LPGENT("New Note"), "NewNote", IDI_NOTEICON},
+ {LPGENT("New Reminder"), "NewReminder", IDI_ADDREMINDER}
+};
+
+HANDLE hIconLibItem[SIZEOF(iconList)];
+
+void InitIcons(void)
+{
+ int i;
+ char szSettingName[100];
+ SKINICONDESC sid = {0};
+ TCHAR szFile[MAX_PATH];
+
+ GetModuleFileName(hinstance, szFile, SIZEOF(szFile));
+
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszDefaultFile = szFile;
+ sid.pszName = szSettingName;
+ sid.ptszSection = _T(MODULENAME);
+ sid.flags = SIDF_ALL_TCHAR;
+
+ for (i = 0; i < SIZEOF(iconList); i++)
+ {
+ mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", MODULENAME, iconList[i].szName);
+
+ sid.ptszDescription = iconList[i].szDescr;
+ sid.iDefaultIndex = -iconList[i].defIconID;
+ hIconLibItem[i] = Skin_AddIcon(&sid);
+ }
+}
+
+int OnOptInitialise(WPARAM w, LPARAM L)
+{
+ OPTIONSDIALOGPAGE odp = {0};
+ odp.cbSize = sizeof(odp);
+ odp.position = 900002000;
+ odp.hInstance = hinstance;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_STNOTEOPTIONS);
+ odp.ptszTitle = _T(SECTIONNAME);
+ odp.ptszGroup = _T("Plugins");
+ odp.pfnDlgProc = DlgProcOptions;
+ odp.flags = ODPF_TCHAR;
+ Options_AddPage(w, &odp);
+ return 0;
+}
+
+int OnTopToolBarInit(WPARAM w,LPARAM L)
+{
+ TTBButton ttb = {0};
+ ttb.cbSize = sizeof(TTBButton);
+ ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP;
+
+ ttb.hIconHandleUp = hIconLibItem[14];
+ ttb.pszService = MODULENAME"/MenuCommandAddNew";
+ ttb.name = ttb.pszTooltipUp = LPGEN("Add New Note");
+ TopToolbar_AddButton(&ttb);
+
+ ttb.hIconHandleUp = hIconLibItem[15];
+ ttb.pszService = MODULENAME"/MenuCommandNewReminder";
+ ttb.name = ttb.pszTooltipUp = LPGEN("Add New Reminder");
+ TopToolbar_AddButton(&ttb);
+
+ UnhookEvent(hkTopToolbarInit);
+ return 0;
+}
+
+static void InitServices()
+{
+ // register sounds
+
+ SkinAddNewSoundExT("AlertReminder", LPGENT("Alerts"), LPGENT("Reminder triggered"));
+ SkinAddNewSoundExT("AlertReminder2", LPGENT("Alerts"), LPGENT("Reminder triggered (Alternative 1)"));
+ SkinAddNewSoundExT("AlertReminder3", LPGENT("Alerts"), LPGENT("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);
+}
+
+int OnModulesLoaded(WPARAM wparam,LPARAM lparam)
+{
+ CLISTMENUITEM cmi = {0};
+
+ // register fonts and hotkeys
+
+ RegisterFontServiceFonts();
+ RegisterKeyBindings();
+
+ // register menus
+
+ cmi.cbSize = sizeof(cmi);
+ cmi.pszContactOwner = NULL;
+ cmi.ptszPopupName = LPGENT("Notes && Reminders");
+ cmi.flags = CMIF_TCHAR | CMIF_ICONFROMICOLIB;
+
+ cmi.position = 1600000000;
+ cmi.icolibItem = hIconLibItem[2];
+ cmi.ptszName = LPGENT("New &Note");
+ cmi.pszService = MODULENAME"/MenuCommandAddNew";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600000001;
+ cmi.icolibItem = hIconLibItem[0];
+ cmi.ptszName = LPGENT("New &Reminder");
+ cmi.pszService = MODULENAME"/MenuCommandNewReminder";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ //
+
+ cmi.position = 1600100000;
+ cmi.icolibItem = hIconLibItem[3];
+ cmi.ptszName = LPGENT("&Show / Hide Notes");
+ cmi.pszService = MODULENAME"/MenuCommandShowHide";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600100001;
+ cmi.icolibItem = hIconLibItem[13];
+ cmi.ptszName = LPGENT("Vie&w Notes");
+ cmi.pszService = MODULENAME"/MenuCommandViewNotes";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600100002;
+ cmi.icolibItem = hIconLibItem[1];
+ cmi.ptszName = LPGENT("&Delete All Notes");
+ cmi.pszService = MODULENAME"/MenuCommandDeleteAll";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600100003;
+ cmi.icolibItem = hIconLibItem[11];
+ cmi.ptszName = LPGENT("&Bring All to Front");
+ cmi.pszService = MODULENAME"/MenuCommandBringAllFront";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ //
+
+ cmi.position = 1600200000;
+ cmi.icolibItem = hIconLibItem[6];
+ cmi.ptszName = LPGENT("&View Reminders");
+ cmi.pszService = MODULENAME"/MenuCommandViewReminders";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ cmi.position = 1600200001;
+ cmi.icolibItem = hIconLibItem[5];
+ cmi.ptszName = LPGENT("D&elete All Reminders");
+ cmi.pszService = MODULENAME"/MenuCommandDeleteReminders";
+ if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
+ Menu_AddMainMenuItem(&cmi);
+
+ // register misc
+
+ hkOptInit = HookEvent(ME_OPT_INITIALISE, OnOptInitialise);
+ hkTopToolbarInit = HookEvent("TopToolBar/ModuleLoaded", OnTopToolBarInit);
+ UnhookEvent(hkModulesLoaded);
+
+ // init vars and load all data
+
+ InitSettings();
+ CreateMsgWindow();
+ LoadNotes(TRUE);
+ LoadReminders();
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ CloseNotesList();
+ CloseReminderList();
+ SaveNotes();
+ SaveReminders();
+ DestroyMsgWindow();
+ WS_CleanUp();
+ TermSettings();
+
+ UnhookEvent(hkFontChange);
+ UnhookEvent(hkColorChange);
+
+ UnhookEvent(hkOptInit);
+
+ Skin_ReleaseIcon(g_hReminderIcon);
+ DeleteObject(hBodyFont);
+ DeleteObject(hCaptionFont);
+
+ if (hRichedDll)
+ FreeLibrary(hRichedDll);
+ if (hUserDll)
+ FreeLibrary(hUserDll);
+ if (hKernelDll)
+ FreeLibrary(hKernelDll);
+
+ return 0;
+}
+
+BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpvReserved)
+{
+ hinstance = hinst;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ mir_getLP(&pluginInfo);
+
+ INITCOMMONCONTROLSEX ctrls = {0};
+ ctrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ ctrls.dwICC = ICC_DATE_CLASSES;
+ InitCommonControlsEx(&ctrls);
+
+ g_isWin2kPlus = IsWinVer2000Plus();
+
+ hRichedDll = LoadLibrary(_T("RICHED20.DLL"));
+ if (!hRichedDll)
+ {
+ if (MessageBox(0, TranslateT("Miranda could not load the Note & Reminders plugin, RICHED20.DLL is missing. If you are using Windows 95 or WINE please make sure you have riched20.dll installed. Press 'Yes' to continue loading Miranda."), _T(SECTIONNAME), MB_YESNO | MB_ICONINFORMATION) != IDYES)
+ return 1;
+ return 0;
+ }
+
+ hUserDll = LoadLibrary(_T("user32.dll"));
+ if (hUserDll)
+ {
+ MySetLayeredWindowAttributes = (BOOL (WINAPI *)(HWND,COLORREF,BYTE,DWORD))GetProcAddress(hUserDll,"SetLayeredWindowAttributes");
+ MyMonitorFromWindow = (HANDLE (WINAPI*)(HWND,DWORD))GetProcAddress(hUserDll,"MonitorFromWindow");
+ }
+ else
+ {
+ MySetLayeredWindowAttributes = NULL;
+ MyMonitorFromWindow = NULL;
+ }
+
+ hKernelDll = LoadLibrary(_T("kernel32.dll"));
+ if (hKernelDll)
+ {
+ MyTzSpecificLocalTimeToSystemTime = (BOOL (WINAPI*)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME))GetProcAddress(hKernelDll,"TzSpecificLocalTimeToSystemTime");
+ MySystemTimeToTzSpecificLocalTime = (BOOL (WINAPI*)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME))GetProcAddress(hKernelDll,"SystemTimeToTzSpecificLocalTime");
+ }
+ else
+ {
+ MyTzSpecificLocalTimeToSystemTime = NULL;
+ MySystemTimeToTzSpecificLocalTime = NULL;
+ }
+
+ InitServices();
+ WS_Init();
+
+ hkModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED,OnModulesLoaded);
+ InitIcons();
+
+ return 0;
+}
\ No newline at end of file diff --git a/plugins/NotesAndReminders/src/miscutils.cpp b/plugins/NotesAndReminders/src/miscutils.cpp new file mode 100644 index 0000000000..b4de98404c --- /dev/null +++ b/plugins/NotesAndReminders/src/miscutils.cpp @@ -0,0 +1,246 @@ +#include "globals.h"
+
+BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
+HANDLE (WINAPI *MyMonitorFromWindow)(HWND,DWORD);
+BOOL (WINAPI *MyTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+BOOL (WINAPI *MySystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+
+
+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;
+}
+
+void WriteSettingInt(HANDLE hContact,char *ModuleName,char *SettingName,int Value)
+{
+ DBCONTACTWRITESETTING cws = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_DWORD;
+ dbv.dVal = Value;
+ cws.szModule = ModuleName;
+ cws.szSetting = SettingName;
+ cws.value = dbv;
+ CallService(MS_DB_CONTACT_WRITESETTING,(DWORD)hContact,(DWORD)&cws);
+}
+
+int ReadSettingInt(HANDLE hContact,char *ModuleName,char *SettingName,int Default)
+{
+ DBCONTACTGETSETTING cws = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_DWORD;
+ dbv.dVal = Default;
+ cws.szModule = ModuleName;
+ cws.szSetting = SettingName;
+ cws.pValue = &dbv;
+ if (CallService(MS_DB_CONTACT_GETSETTING,(DWORD)hContact,(DWORD)&cws))
+ return Default;
+ else
+ return dbv.dVal;
+}
+
+void DeleteSetting(HANDLE hContact,char *ModuleName,char *SettingName)
+{
+ DBCONTACTGETSETTING dbcgs = {0};
+ dbcgs.szModule = ModuleName;
+ dbcgs.szSetting = SettingName;
+ dbcgs.pValue = NULL;
+ CallService(MS_DB_CONTACT_DELETESETTING,(DWORD)hContact,(DWORD)&dbcgs);
+}
+
+void FreeSettingBlob(WORD pSize,void * pbBlob)
+{
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_BLOB;
+ dbv.cpbVal = pSize;
+ dbv.pbVal = (BYTE*)pbBlob;
+ CallService(MS_DB_CONTACT_FREEVARIANT,0,(DWORD)&dbv);
+}
+
+void WriteSettingBlob(HANDLE hContact,char *ModuleName,char *SettingName,WORD pSize,void *pbBlob)
+{
+ DBCONTACTWRITESETTING cgs = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_BLOB;
+ dbv.cpbVal = pSize;
+ dbv.pbVal = (BYTE*)pbBlob;
+ cgs.szModule = ModuleName;
+ cgs.szSetting = SettingName;
+ cgs.value = dbv;
+ CallService(MS_DB_CONTACT_WRITESETTING,(DWORD)hContact,(DWORD)&cgs);
+}
+
+void ReadSettingBlob(HANDLE hContact, char *ModuleName, char *SettingName, WORD *pSize, void **pbBlob)
+{
+ DBCONTACTGETSETTING cgs = {0};
+ DBVARIANT dbv = {0};
+ dbv.type = DBVT_BLOB;
+// dbv.cpbVal = (LOBYTE(pSize) | HIBYTE((BYTE)pbBlob)); // this is not used anyway
+ cgs.szModule = ModuleName;
+ cgs.szSetting = SettingName;
+ cgs.pValue = &dbv;
+ if (CallService(MS_DB_CONTACT_GETSETTING,(DWORD)hContact,(DWORD)&cgs))
+ {
+ pSize = 0; // tohle asi ve 4.2 neni
+ pbBlob = NULL;
+ }
+ else
+ {
+ *pSize = LOWORD(dbv.cpbVal);
+ *pbBlob = (int*)dbv.pbVal;
+ }
+}
+
+void WriteSettingIntArray(HANDLE hContact,char *ModuleName,char *SettingName,const int *Value, int Size)
+{
+ WriteSettingBlob(hContact,ModuleName,SettingName,sizeof(int)*Size,(void*)Value);
+}
+
+BOOL ReadSettingIntArray(HANDLE hContact,char *ModuleName,char *SettingName,int *Value, int Size)
+{
+ WORD sz = 4096;
+ int *pData;
+ BOOL bResult;
+
+ ReadSettingBlob(hContact, ModuleName, SettingName, &sz, (void**)&pData);
+
+ if (!pData)
+ return FALSE;
+
+ bResult = FALSE;
+
+ if (sz == sizeof(int)*Size)
+ {
+ memcpy(Value, pData, sizeof(int)*Size);
+ bResult = TRUE;
+ }
+
+ FreeSettingBlob(sz,pData);
+
+ return bResult;
+}
+
+void TreeAdd(TREEELEMENT **root, void *Data)
+{
+ TREEELEMENT *NTE;
+ NTE = (TREEELEMENT*)malloc(sizeof(TREEELEMENT));
+ if (NTE)
+ {
+ NTE->ptrdata = Data;
+ NTE->next = *root;
+ *root = NTE;
+ }
+}
+
+void TreeAddSorted(TREEELEMENT **root,void *Data,int (*CompareCb)(TREEELEMENT*,TREEELEMENT*))
+{
+ TREEELEMENT *TTE,*Prev;
+ TREEELEMENT *NTE;
+
+ if (!*root)
+ {
+ // list empty, just add normally
+ TreeAdd(root,Data);
+ return;
+ }
+
+ NTE = (TREEELEMENT*)malloc(sizeof(TREEELEMENT));
+ if (!NTE)
+ return;
+
+ NTE->ptrdata = Data;
+ NTE->next = NULL;
+
+ // insert sorted
+
+ Prev = NULL;
+ 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;
+}
+
+void TreeDelete(TREEELEMENT **root,void *Item)
+{
+ TREEELEMENT *TTE,*Prev = NULL;
+ TTE = *root;
+ if (!TTE) return;
+ while ((TTE) && (TTE->ptrdata != Item))
+ {
+ Prev = TTE;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+ if (TTE)
+ {
+ if (Prev)
+ Prev->next = TTE->next;
+ else
+ *root = (TREEELEMENT*)TTE->next;
+ SAFE_FREE((void**)&TTE);
+ }
+}
+
+void *TreeGetAt(TREEELEMENT *root,int Item)
+{
+ TREEELEMENT *TTE;
+ int I = 0;
+ if (!root) return NULL;
+ TTE = root;
+ while ((TTE) && (I != Item))
+ {
+ TTE = (TREEELEMENT*)TTE->next;
+ I++;
+ }
+ if (!TTE)
+ return NULL;
+ else
+ return TTE->ptrdata;
+}
+
+int TreeGetCount(TREEELEMENT *root)
+{
+ int I = 0;
+ TREEELEMENT *TTE;
+ if (!root) return 0;
+ TTE = root;
+ while (TTE)
+ {
+ TTE = (TREEELEMENT*)TTE->next;
+ I++;
+ }
+ return I;
+}
diff --git a/plugins/NotesAndReminders/src/miscutils.h b/plugins/NotesAndReminders/src/miscutils.h new file mode 100644 index 0000000000..104de5313c --- /dev/null +++ b/plugins/NotesAndReminders/src/miscutils.h @@ -0,0 +1,47 @@ +void WriteSettingInt(HANDLE hContact,char *ModuleName,
+ char *SettingName,int Value);
+int ReadSettingInt(HANDLE hContact,char *ModuleName,
+ char *SettingName,int Default);
+void ReadSettingBlob(HANDLE hContact, char *ModuleName,
+ char *SettingName, WORD *pSize, void **pbBlob);
+void WriteSettingBlob(HANDLE hContact,char *ModuleName,
+ char *SettingName,WORD pSize,void *pbBlob);
+void FreeSettingBlob(WORD pSize,void * pbBlob);
+BOOL ReadSettingBool(HANDLE hContact,char *ModuleName,
+ char *SettingName,BOOL Default);
+void WriteSettingBool(HANDLE hContact,char *ModuleName,
+ char *SettingName,BOOL Value);
+void DeleteSetting(HANDLE hContact,char *ModuleName,
+ char *SettingName);
+void WriteSettingIntArray(HANDLE hContact,char *ModuleName,
+ char *SettingName,const int *Value, int Size);
+BOOL ReadSettingIntArray(HANDLE hContact,char *ModuleName,
+ char *SettingName,int *Value, int Size);
+
+extern BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
+extern HANDLE (WINAPI *MyMonitorFromWindow)(HWND,DWORD);
+extern BOOL (WINAPI *MyTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+extern BOOL (WINAPI *MySystemTimeToTzSpecificLocalTime)(LPTIME_ZONE_INFORMATION,LPSYSTEMTIME,LPSYSTEMTIME);
+
+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)
+ {
+ free(*p);
+ *p = NULL;
+ }
+}
diff --git a/plugins/NotesAndReminders/src/notes.cpp b/plugins/NotesAndReminders/src/notes.cpp new file mode 100644 index 0000000000..389bd0e904 --- /dev/null +++ b/plugins/NotesAndReminders/src/notes.cpp @@ -0,0 +1,2180 @@ +#include "globals.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)
+#define DATATAG_BGCOL 3 // %x (custom background color)
+#define DATATAG_FGCOL 4 // %x (custom text/fg colors)
+#define DATATAG_TITLE 5 // %s (custom note title)
+#define DATATAG_FONT 6 // %d:%u:%u:%s (custom font)
+
+
+#define MAX_TITLE_LEN 63
+#define MAX_NOTE_LEN 16384
+
+// delay before saving note changes (ms)
+#define NOTE_CHANGE_COMMIT_DELAY 1000
+
+
+#ifndef WS_EX_NOACTIVATE
+#define WS_EX_NOACTIVATE 0x08000000
+#endif
+#define IDM_REMOVENOTE 40001
+#define IDM_HIDENOTE 40002
+#define IDM_TOGGLEONTOP 40003
+#define IDM_UNDO 40004
+#define IDM_COPY 40005
+#define IDM_PASTE 40006
+#define IDM_CUT 40007
+#define IDM_CLEAR 40008
+#define WS_EX_LAYERED 0x00080000
+#define LWA_ALPHA 0x00000002
+
+#define IDC_LISTREMINDERS 1000
+#define IDC_LISTREMINDERS_HEADER 2000
+#define IDC_REMINDERDATA 1001
+#define IDC_ADDNEWREMINDER 1002
+#define IDC_CLOSE 1003
+#define WM_RELOAD (WM_USER + 100)
+
+#define NOTIFY_LIST() if (ListNotesVisible) PostMessage(LV,WM_RELOAD,0,0)
+
+#define PENLINK ENLINK *
+
+#define NOTE_WND_CLASS _T("MIM_StickyNote")
+
+
+#define IDM_COLORPRESET_BG 41000
+#define IDM_COLORPRESET_FG 41100
+
+
+static BOOL ListNotesVisible = FALSE;
+static HWND LV;
+
+
+struct ColorPreset
+{
+ TCHAR *szName;
+ COLORREF color;
+};
+
+static struct ColorPreset clrPresets[] =
+{
+ {LPGENT("Black"), RGB(0,0,0)},
+ {LPGENT("Maroon"), RGB(128,0,0)},
+ {LPGENT("Green"), RGB(0,128,0)},
+ {LPGENT("Olive"), RGB(128,128,0)},
+ {LPGENT("Navy"), RGB(0,0,128)},
+ {LPGENT("Purple"), RGB(128,0,128)},
+ {LPGENT("Teal"), RGB(0,128,128)},
+ {LPGENT("Gray"), RGB(128,128,128)},
+ {LPGENT("Silver"), RGB(192,192,192)},
+ {LPGENT("Red"), RGB(255,0,0)},
+ {LPGENT("Orange"), RGB(255,155,0)},
+ {LPGENT("Lime"), RGB(0,255,0)},
+ {LPGENT("Yellow"), RGB(255,255,0)},
+ {LPGENT("Blue"), RGB(0,0,255)},
+ {LPGENT("Fuchsia"), RGB(255,0,255)},
+ {LPGENT("Aqua"), RGB(0,255,255)},
+ {LPGENT("White"), RGB(255,255,255)}
+};
+
+
+TREEELEMENT *g_Stickies = NULL;
+
+
+INT_PTR CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,
+ WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,
+ LPARAM lParam);
+void JustSaveNotes(void);
+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 OnListResize(HWND Dialog);
+void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols);
+void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal);
+
+
+COLORREF GetCaptionColor(COLORREF bodyClr)
+{
+ const DWORD r = ((bodyClr & 0xff) * 4) / 5;
+ const DWORD g = (((bodyClr & 0xff00) * 4) / 5) & 0xff00;
+ const DWORD b = (((bodyClr & 0xff0000) * 4) / 5) & 0xff0000;
+
+ return (COLORREF)(r|g|b);
+}
+
+
+static void EnsureUniqueID(STICKYNOTE *TSN)
+{
+ TREEELEMENT *TTE;
+
+ if (!g_Stickies)
+ return;
+
+try_next:
+
+ // check existing notes if id is in use
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ if (((STICKYNOTE*)TTE->ptrdata)->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++;
+ goto try_next;
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+}
+
+
+static void InitNoteTitle(STICKYNOTE *TSN)
+{
+ if (g_NoteTitleDate)
+ {
+ char TempStr[MAX_PATH];
+ SYSTEMTIME tm;
+ LCID lc = GetUserDefaultLCID();
+
+ TempStr[0] = 0;
+
+ memset(&tm, 0, sizeof(tm));
+ FileTimeToTzLocalST((FILETIME*)&TSN->ID, &tm);
+
+ if ( GetDateFormat(lc, 0, &tm, GetDateFormatStr(), TempStr, MAX_PATH) )
+ {
+ // append time if requested
+ if (g_NoteTitleTime)
+ {
+ int n = strlen(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->CustomTitle = FALSE;
+}
+
+
+static void InitStickyNoteLogFont(STICKYNOTEFONT *pCustomFont, LOGFONT *lf)
+{
+ if (!pCustomFont->size)
+ {
+ HDC hdc;
+
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE);
+ lf->lfHeight = 10;
+ hdc = GetDC(0);
+ lf->lfHeight = -MulDiv(lf->lfHeight,GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ ReleaseDC(0, hdc);
+ }
+ else
+ {
+ lf->lfHeight = pCustomFont->size;
+ }
+
+ _tcscpy(lf->lfFaceName, pCustomFont->szFace);
+
+ lf->lfWidth = lf->lfEscapement = lf->lfOrientation = 0;
+ lf->lfWeight = pCustomFont->style & DBFONTF_BOLD ? FW_BOLD : FW_NORMAL;
+ lf->lfItalic = (pCustomFont->style & DBFONTF_ITALIC) != 0;
+ lf->lfUnderline = (pCustomFont->style & DBFONTF_UNDERLINE) != 0;
+ lf->lfStrikeOut = (pCustomFont->style & DBFONTF_STRIKEOUT) != 0;
+ lf->lfCharSet = pCustomFont->charset;
+ lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = DEFAULT_QUALITY;
+ lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+}
+
+static BOOL CreateStickyNoteFont(STICKYNOTEFONT *pCustomFont, LOGFONT *plf)
+{
+ LOGFONT lf = {0};
+
+ if (!plf)
+ {
+ InitStickyNoteLogFont(pCustomFont, &lf);
+ plf = &lf;
+ }
+
+ if (pCustomFont->hFont)
+ DeleteObject(pCustomFont->hFont);
+
+ pCustomFont->hFont = CreateFontIndirect(plf);
+
+ return pCustomFont->hFont != NULL;
+}
+
+
+STICKYNOTE* NewNoteEx(int Ax,int Ay,int Aw,int Ah,char *Data,ULARGE_INTEGER *ID,BOOL Visible,BOOL OnTop,int scrollV,COLORREF bgClr,COLORREF fgClr,char *Title,STICKYNOTEFONT *pCustomFont,BOOL bLoading)
+{
+ STICKYNOTE* TSN;
+ WNDCLASSEX TWC = {0};
+ WINDOWPLACEMENT TWP;
+ DWORD L1,L2;
+ SYSTEMTIME tm;
+ char TempStr[MAX_PATH] = {0};
+ char *TData;
+
+ const BOOL bIsStartup = Visible & 0x10000;
+ Visible &= ~0x10000;
+
+ if (Data) TData = Data; else TData = NULL;
+
+ 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(0,IDI_APPLICATION);
+ TWC.hCursor = LoadCursor(0,IDC_ARROW);
+ TWC.hbrBackground = 0;
+ TWC.lpszMenuName = 0;
+ TWC.lpszClassName = NOTE_WND_CLASS;
+ TWC.cbSize = sizeof(WNDCLASSEX);
+ TWC.lpfnWndProc = (WNDPROC)StickyNoteWndProc;
+ if (!RegisterClassEx(&TWC)) return NULL;
+ }
+
+ 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);
+ }
+
+ TSN = (STICKYNOTE*)malloc(sizeof(STICKYNOTE));
+
+ if (ID)
+ {
+ TSN->ID = *ID;
+ }
+ else
+ {
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&TSN->ID);
+ }
+
+ EnsureUniqueID(TSN);
+
+ TreeAdd(&g_Stickies,TSN);
+
+ if (!TData)
+ {
+ TData = _strdup("");
+ TSN->data = TData;
+ }
+ else
+ TSN->data = TData;
+
+ // init note title (time-stamp)
+ if (Title)
+ {
+ TSN->title = Title;
+ TSN->CustomTitle = TRUE;
+ }
+ else
+ {
+ TSN->title = NULL;
+ InitNoteTitle(TSN);
+ }
+
+ TSN->Visible = Visible;
+ TSN->OnTop = OnTop;
+
+ TSN->BgColor = bgClr;
+ TSN->FgColor = fgClr;
+
+ TSN->pCustomFont = pCustomFont;
+
+ L1 = WS_EX_TOOLWINDOW;
+ if (MySetLayeredWindowAttributes && g_Transparency < 255) L1 |= WS_EX_LAYERED;
+ if (OnTop) L1 |= WS_EX_TOPMOST;
+
+ L2 = WS_POPUP | WS_THICKFRAME | WS_CAPTION;
+
+ // NOTE: loaded note positions stem from GetWindowPlacement, which normally have a different coord space than
+ // CreateWindow/SetWindowPos, BUT since we now use WS_EX_TOOLWINDOW they use the same coord space so
+ // we don't have to worry about notes "drifting" between sessions
+ TSN->SNHwnd = CreateWindowEx(L1, NOTE_WND_CLASS, _T("StickyNote"), L2, Ax,Ay,Aw,Ah, NULL, 0, hmiranda, TSN);
+
+ if (MySetLayeredWindowAttributes && g_Transparency < 255)
+ MySetLayeredWindowAttributes(TSN->SNHwnd,0,(BYTE)g_Transparency,LWA_ALPHA);
+
+ // ensure that window is not placed off-screen (if previous session had different monitor count or resolution)
+ // NOTE: SetWindowPlacement should do this, but it's extremly flakey
+ if (Data)
+ {
+ if (MyMonitorFromWindow && !MyMonitorFromWindow(TSN->SNHwnd, MONITOR_DEFAULTTONULL) )
+ {
+ TWP.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(GetDesktopWindow(), &TWP);
+
+ if (Aw > 500) Aw = 500;
+ if (Ay < TWP.rcNormalPosition.left+10 || Ax > TWP.rcNormalPosition.right-120)
+ Ax = ((TWP.rcNormalPosition.right - TWP.rcNormalPosition.left) / 2) - (Aw / 2) + (rand() & 0x3f);
+ if (Ay < TWP.rcNormalPosition.top+50 || Ay > TWP.rcNormalPosition.bottom-50)
+ Ay = ((TWP.rcNormalPosition.bottom - TWP.rcNormalPosition.top) / 4) + (rand() & 0x1f);
+
+ SetWindowPos(TSN->SNHwnd, NULL, Ax, Ay, Aw, Ah, SWP_NOZORDER|SWP_NOACTIVATE);
+ }
+ }
+
+ if (Visible)
+ {
+ ShowWindow(TSN->SNHwnd, SW_SHOWNA);
+
+ // when loading notes (only at startup), place all non-top notes at the bottom so they don't cover other windows
+ if (Data && !OnTop && bIsStartup)
+ SetWindowPos(TSN->SNHwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_ASYNCWINDOWPOS);
+ }
+
+ if (scrollV)
+ {
+ SendMessage(TSN->REHwnd, EM_LINESCROLL, 0, scrollV);
+ }
+
+ // make sure that any event triggered by init doesn't cause a meaningless save
+ KillTimer(TSN->SNHwnd, 1025);
+
+ if (!bLoading)
+ {
+ NOTIFY_LIST();
+ }
+
+ return TSN;
+}
+
+STICKYNOTE* NewNote(int Ax,int Ay,int Aw,int Ah,char *Data,ULARGE_INTEGER *ID,BOOL Visible,BOOL OnTop,int scrollV)
+{
+ return NewNoteEx(Ax,Ay,Aw,Ah,Data,ID,Visible,OnTop,scrollV,0,0,NULL,NULL,FALSE);
+}
+
+void LoadNotes(BOOL bIsStartup)
+{
+ int I;
+ int NotesCount;
+ WORD Size;
+ char *Value = NULL, *TVal = NULL;
+ char ValueName[32];
+
+ g_Stickies = NULL;
+
+ NotesCount = ReadSettingInt(0,MODULENAME,"NotesData",0);
+
+ for (I = 0; I < NotesCount; I++)
+ {
+ char *DelPos;
+
+ sprintf(ValueName, "NotesData%d", I);
+
+ if (Value)
+ {
+ FreeSettingBlob(Size, Value);
+ Value = NULL;
+ }
+
+ Size = 65535; // does not get used
+
+ ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value);
+
+ if (!Size || !Value)
+ continue; // the setting could not be read from DB -> skip
+
+ if (Value[0] == 'X')
+ {
+ // new eXtended/fleXible data format
+
+ STICKYNOTE note = {0};
+ int i, rect[4];
+ int scrollV = 0;
+ STICKYNOTEFONT *pCustomFont = NULL;
+ DWORD flags;
+
+ DelPos = strchr(Value+1,0x1B);
+ if (DelPos)
+ *DelPos = 0;
+
+ // id:x:y:w:h:flags
+
+ TVal = strchr(Value+1, ':');
+ if (!TVal || (DelPos && TVal > DelPos))
+ continue;
+ *TVal++ = 0;
+
+ note.ID.QuadPart = _strtoui64(Value+1, NULL, 16);
+
+ for (i=0; i<4; i++)
+ {
+ char *sep = strchr(TVal, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+
+ rect[i] = strtol(TVal, NULL, 10);
+
+ TVal = sep;
+ }
+
+ flags = strtoul(TVal, NULL, 16);
+
+ if (flags & 1)
+ note.Visible = TRUE;
+ if (flags & 2)
+ note.OnTop = TRUE;
+
+ // optional \033 separated params
+ while (DelPos)
+ {
+ char *sep;
+ UINT tag;
+
+ TVal = DelPos + 1;
+ // find param end and make sure it's null-terminated (if end of data then it's already null-terminated)
+ DelPos = strchr(TVal, 0x1B);
+ if (DelPos)
+ *DelPos = 0;
+
+ // tag:<data>
+
+ sep = strchr(TVal, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+
+ tag = strtoul(TVal, NULL, 10);
+ TVal = sep + 1;
+
+ switch (tag)
+ {
+ case DATATAG_TEXT:
+ note.data = _strdup(TVal);
+ break;
+
+ case DATATAG_SCROLLPOS:
+ scrollV = (int)strtoul(TVal, NULL, 10);
+ break;
+
+ case DATATAG_BGCOL:
+ note.BgColor = strtoul(TVal, NULL, 16) | 0xff000000;
+ break;
+
+ case DATATAG_FGCOL:
+ note.FgColor = strtoul(TVal, NULL, 16) | 0xff000000;
+ break;
+
+ case DATATAG_TITLE:
+ if (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, NULL, 10);
+ TVal2 = sep;
+
+ sep = strchr(TVal2, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+ fstyle = strtoul(TVal2, NULL, 10);
+ TVal2 = sep;
+
+ sep = strchr(TVal2, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+ *sep++ = 0;
+ fcharset = strtoul(TVal2, NULL, 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;
+ _tcscpy(pCustomFont->szFace, TVal2);
+ pCustomFont->hFont = NULL;
+
+ if ( !CreateStickyNoteFont(pCustomFont, NULL) )
+ {
+ free(pCustomFont);
+ pCustomFont = NULL;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!note.data)
+ note.data = _strdup("");
+
+ note.Visible = note.Visible && (!bIsStartup || g_ShowNotesAtStart);
+ if (bIsStartup)
+ note.Visible |= 0x10000;
+
+ NewNoteEx(rect[0],rect[1],rect[2],rect[3],note.data,¬e.ID,note.Visible,note.OnTop,scrollV,note.BgColor,note.FgColor,note.title,pCustomFont,TRUE);
+ }
+ else
+ {
+ // old format (for DB backward compatibility)
+
+ int Tx,Ty,Tw,Th,TV,OT;
+ BOOL V;
+ char *Data,*ID;
+ ULARGE_INTEGER newid;
+
+ OT = 1; TV = 1;
+ Tx = 100; Ty = 100;
+ Tw = 179; Th = 35;
+ Data = NULL; ID = NULL;
+
+ if (DelPos = strchr(Value,0x1B))
+ { // get first delimiter
+ int PartLen = DelPos - TVal;
+
+ Data = NULL;
+ ID = NULL;
+ TVal = Value;
+ DelPos[0] = 0x0;
+ Tx = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Ty = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Tw = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Th = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ TV = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ OT = strtol(TVal, NULL, 10);
+
+ TVal = DelPos + 1;
+ DelPos = strchr(TVal, 0x1B);
+ if (!DelPos) continue; // setting is broken, do not crash
+ DelPos[0] = 0x0;
+ Data = _strdup(TVal);
+
+ TVal = DelPos + 1;
+ ID = TVal;
+
+ V = (BOOL)TV && (!bIsStartup || g_ShowNotesAtStart);
+
+ if (bIsStartup)
+ V |= 0x10000;
+
+ // convert old ID format to new
+ if ( strchr(ID, '-') )
+ {
+ // validate format (otherwise create new)
+ if (strlen(ID) < 19 || ID[2] != '-' || ID[5] != '-' || ID[10] != ' ' || ID[13] != ':' || ID[16] != ':')
+ {
+ ID = NULL;
+ }
+ else
+ {
+ SYSTEMTIME tm;
+
+ ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.wDay = (WORD)strtoul(ID, NULL, 10);
+ tm.wMonth = (WORD)strtoul(ID+3, NULL, 10);
+ tm.wYear = (WORD)strtoul(ID+6, NULL, 10);
+ tm.wHour = (WORD)strtoul(ID+11, NULL, 10);
+ tm.wMinute = (WORD)strtoul(ID+14, NULL, 10);
+ tm.wSecond = (WORD)strtoul(ID+17, NULL, 10);
+
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&newid);
+ }
+ }
+ else
+ {
+ ID = NULL;
+ }
+
+ NewNoteEx(Tx,Ty,Tw,Th,Data,ID?&newid:NULL,V,(BOOL)OT,0,0,0,NULL,NULL,TRUE);
+ }
+ }
+skip:;
+ }
+
+ if (Value)
+ FreeSettingBlob(Size, Value); // we do not leak on bad setting
+
+ NOTIFY_LIST();
+}
+
+void CloseNotesList()
+{
+ if (ListNotesVisible)
+ {
+ DestroyWindow(LV);
+ ListNotesVisible = FALSE;
+ }
+}
+
+static void PurgeNotesTree()
+{
+ STICKYNOTE *pt;
+
+ while (g_Stickies) // empty whole tree
+ {
+ 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 = NULL;
+}
+
+void SaveNotes(void)
+{
+ JustSaveNotes();
+ PurgeNotesTree();
+}
+
+void PurgeNotes(void)
+{
+ int NotesCount, I;
+ char ValueName[16];
+
+ NotesCount = ReadSettingInt(0,MODULENAME,"NotesData",0);
+ for(I = 0; I < NotesCount; I++)
+ {
+ sprintf(ValueName, "NotesData%d", I);
+ DBDeleteContactSetting(0,MODULENAME,ValueName);
+ }
+}
+
+void OnDeleteNote(HWND hdlg, STICKYNOTE *SN)
+{
+ if (MessageBox(hdlg, TranslateT("Are you sure you want to delete this note?"), _T(SECTIONNAME), MB_OKCANCEL) == IDOK)
+ {
+ DestroyWindow(hdlg);
+ TreeDelete(&g_Stickies,SN);
+ SAFE_FREE((void**)&SN->data);
+ if (SN->pCustomFont)
+ {
+ DeleteObject(SN->pCustomFont->hFont);
+ free(SN->pCustomFont);
+ }
+ SAFE_FREE((void**)&SN);
+ JustSaveNotes();
+ }
+}
+
+void DeleteNotes(void)
+{
+ PurgeNotes();
+ WriteSettingInt(0, MODULENAME, "NotesData", 0);
+ PurgeNotesTree();
+}
+
+void ShowHideNotes(void)
+{
+ TREEELEMENT *TTE;
+ int bShow;
+ UINT nHideCount, nVisCount;
+ BOOL Visible;
+
+ if (!g_Stickies)
+ return;
+
+ // 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
+
+ nHideCount = nVisCount = 0;
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ if (((STICKYNOTE*)TTE->ptrdata)->Visible)
+ nVisCount++;
+ else
+ nHideCount++;
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ if (!nVisCount)
+ Visible = TRUE;
+ else if (!nHideCount)
+ Visible = FALSE;
+ else
+ Visible = TRUE;
+
+ bShow = Visible ? SW_SHOWNA : SW_HIDE;
+
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata;
+
+ if ((!Visible) != (!SN->Visible))
+ {
+ ShowWindow(SN->SNHwnd, bShow);
+ SN->Visible = Visible;
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ JustSaveNotes();
+}
+
+void BringAllNotesToFront(STICKYNOTE *pActive)
+{
+ TREEELEMENT *TTE;
+
+ 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
+
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TTE->ptrdata;
+
+ if (SN->Visible && pActive != SN)
+ {
+ SetWindowPos(SN->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ if (!SN->OnTop)
+ SetWindowPos(SN->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ if (pActive)
+ {
+ SetWindowPos(pActive->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ if (!pActive->OnTop)
+ SetWindowPos(pActive->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ }
+}
+
+static void JustSaveNotesEx(STICKYNOTE *pModified)
+{
+ // pModified optionally points to the modified note that invoked the JustSaveNotesEx call
+
+ TREEELEMENT *TTE;
+ int I, 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, *Value;
+
+ const int OldNotesCount = ReadSettingInt(0, MODULENAME, "NotesData", 0);
+
+ WriteSettingInt(0, MODULENAME, "NotesData", NotesCount);
+
+ for (TTE = g_Stickies, I = 0; TTE; TTE = (TREEELEMENT*)TTE->next, I++)
+ {
+ STICKYNOTE *pNote = (STICKYNOTE*)TTE->ptrdata;
+ BOOL bDeleteTData = TRUE;
+ scrollV = 0;
+ tData = NULL;
+
+ // 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;
+
+ // set flags
+ flags = 0;
+ if (pNote->Visible) flags |= 1;
+ if (pNote->OnTop) flags |= 2;
+
+ // get note text
+ SzT = SendMessage(pNote->REHwnd, WM_GETTEXTLENGTH, 0, 0);
+ 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)
+ SendMessage(pNote->REHwnd, WM_GETTEXT, SzT+1, (LPARAM)tData);
+ }
+
+ if (pNote == pModified)
+ {
+ // update the data of the modified note
+ if (pNote->data)
+ free(pNote->data);
+ pNote->data = tData ? tData : _strdup("");
+ bDeleteTData = FALSE;
+ }
+
+ if (!tData)
+ // empty note
+ SzT = 0;
+ else
+ // get current scroll position
+ scrollV = SendMessage(pNote->REHwnd, EM_GETFIRSTVISIBLELINE, 0, 0);
+
+ //
+
+ Value = (char*)malloc(SzT + 512);
+
+ if (!Value)
+ {
+ if (bDeleteTData)
+ SAFE_FREE((void**)&tData);
+ continue;
+ }
+
+ n = 0;
+
+ // data header
+ l = sprintf(Value, "X%I64x:%d:%d:%d:%d:%x", pNote->ID.QuadPart, TX, TY, TW, TH, flags);
+ if (l > 0) n += l;
+
+ // scroll pos
+ if (scrollV > 0)
+ {
+ l = sprintf(Value+n, "\033""%u:%u", DATATAG_SCROLLPOS, (UINT)scrollV);
+ if (l > 0) n += l;
+ }
+
+ // custom bg color
+ if (pNote->BgColor)
+ {
+ l = sprintf(Value+n, "\033""%u:%x", DATATAG_BGCOL, (UINT)(pNote->BgColor&0xffffff));
+ if (l > 0) n += l;
+ }
+
+ // custom fg color
+ if (pNote->FgColor)
+ {
+ l = sprintf(Value+n, "\033""%u:%x", DATATAG_FGCOL, (UINT)(pNote->FgColor&0xffffff));
+ if (l > 0) n += l;
+ }
+
+ if (pNote->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;
+ }
+
+ // custom title
+ if (pNote->CustomTitle && pNote->title)
+ {
+ l = sprintf(Value+n, "\033""%u:%s", DATATAG_TITLE, pNote->title);
+ if (l > 0) n += l;
+ }
+
+ // note text (ALWAYS PUT THIS PARAM LAST)
+ if (tData)
+ {
+ l = sprintf(Value+n, "\033""%u:%s", DATATAG_TEXT, tData);
+ 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;
+ ValueName[0xffff] = 0;
+ }
+
+ sprintf(ValueName, "NotesData%d", NotesCount - I - 1); // we do not reverse notes in DB
+
+ WriteSettingBlob(0, MODULENAME, ValueName, (WORD)(n+1), Value);
+
+ SAFE_FREE((void**)&Value);
+ if (bDeleteTData)
+ SAFE_FREE((void**)&tData);
+
+ // make no save is queued for the note
+ if (pNote->SNHwnd)
+ KillTimer(pNote->SNHwnd, 1025);
+ }
+
+ // delete any left over DB note entries
+ for(; I < OldNotesCount; I++)
+ {
+ sprintf(ValueName, "NotesData%d", I);
+ DBDeleteContactSetting(0,MODULENAME,ValueName);
+ }
+
+ NOTIFY_LIST();
+}
+
+__inline void JustSaveNotes(void)
+{
+ JustSaveNotesEx(NULL);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Note Window
+
+static int FindMenuItem(HMENU h, LPTSTR lpszName)
+{
+ int n;
+ UINT i;
+ TCHAR s[128];
+
+ 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 (i=0; i<(UINT)n; i++)
+ {
+ if ( GetMenuString(h, i, s, 128, MF_BYPOSITION) )
+ {
+ if ( !_tcscmp(s, lpszName) )
+ {
+ return (int)i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static BOOL DoContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam)
+{
+ int n, i;
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(AhWnd,"ctrldata");
+
+ HMENU hMenuLoad, FhMenu, hSub;
+ hMenuLoad = LoadMenu(hinstance,"MNU_NOTEPOPUP");
+ FhMenu = GetSubMenu(hMenuLoad,0);
+
+ if (SN->OnTop)
+ CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED|MF_BYCOMMAND);
+
+ EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_PASTETITLE, MF_BYCOMMAND | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
+
+ if (!SN->CustomTitle)
+ EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_RESETTITLE, MF_BYCOMMAND | MF_GRAYED);
+
+ // NOTE: names used for FindMenuItem would need to include & chars if such shortcuts are added to the menus
+
+ // color preset menu items uses features that require win2k or later, I'm too lazy to make a fallback path
+ // for obsolete OS versions (win95/98/ME users can still use the "Custom" menu option)
+ if (g_isWin2kPlus)
+ {
+ n = FindMenuItem(FhMenu, _T("Appearance"));
+ if (n >= 0 && (hSub = GetSubMenu(FhMenu, n)))
+ {
+ HMENU hBg = GetSubMenu(hSub, FindMenuItem(hSub, _T("Background Color")));
+ HMENU hFg = GetSubMenu(hSub, FindMenuItem(hSub, _T("Text Color")));
+
+ for (i=0; i<SIZEOF(clrPresets); i++)
+ InsertMenu(hBg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_BG+i, TranslateTS(clrPresets[i].szName));
+
+ for (i=0; i<SIZEOF(clrPresets); i++)
+ InsertMenu(hFg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_FG+i, TranslateTS(clrPresets[i].szName));
+ }
+ }
+
+ CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
+ TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
+ DestroyMenu(hMenuLoad);
+
+ return TRUE;
+}
+
+static void MeasureColorPresetMenuItem(HWND hdlg, LPMEASUREITEMSTRUCT lpMeasureItem, struct ColorPreset *clrPresets)
+{
+ HDC hdc = GetDC(hdlg);
+ LPTSTR lpsz = TranslateT(clrPresets->szName);
+ SIZE sz;
+ GetTextExtentPoint32(hdc, lpsz, _tcslen(lpsz), &sz);
+ ReleaseDC(hdlg, hdc);
+
+ lpMeasureItem->itemWidth = 50 + sz.cx;
+ lpMeasureItem->itemHeight = (sz.cy+2)>18 ? sz.cy+2 : 18;
+}
+
+static void PaintColorPresetMenuItem(LPDRAWITEMSTRUCT lpDrawItem, struct ColorPreset *clrPresets)
+{
+ 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;
+
+ if (lpDrawItem->itemState & ODS_SELECTED)
+ {
+ SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUHILIGHT));
+ FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH));
+
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else
+ {
+ SetDCBrushColor(lpDrawItem->hDC, GetSysColor(COLOR_MENU));
+ FillRect(lpDrawItem->hDC, &lpDrawItem->rcItem, (HBRUSH)GetStockObject(DC_BRUSH));
+
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_MENUTEXT));
+ }
+
+ SetBkMode(lpDrawItem->hDC, TRANSPARENT);
+ DrawText(lpDrawItem->hDC,clrPresets->szName,-1,&rect,DT_LEFT | DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER);
+
+ {
+ 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;
+
+ FrameRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ rect.left++; rect.top++;
+ rect.right--; rect.bottom--;
+ SetDCBrushColor(lpDrawItem->hDC, clrPresets->color);
+ FillRect(lpDrawItem->hDC, &rect, (HBRUSH)GetStockObject(DC_BRUSH));
+ }
+}
+
+static BOOL GetClipboardText_Title(char *pOut, int size)
+{
+ BOOL bResult = FALSE;
+
+ if ( OpenClipboard(NULL) )
+ {
+ HANDLE hData = GetClipboardData(CF_TEXT);
+ LPCSTR buffer;
+
+ if (hData && (buffer = (LPCSTR)GlobalLock(hData)))
+ {
+ // trim initial white spaces
+ while (*buffer && isspace(*buffer))
+ buffer++;
+
+ if (buffer)
+ {
+ char *p;
+
+ int n = (int)strlen(buffer);
+ if (n >= size)
+ n = size-1;
+ memcpy(pOut, buffer, n);
+ pOut[n] = 0;
+
+ // end string on line break and convert tabs to spaces
+ p = pOut;
+ while (*p)
+ {
+ if (*p == '\r' || *p == '\n')
+ {
+ *p = 0;
+ n = strlen(pOut);
+ break;
+ }
+ else if (*p == '\t')
+ {
+ *p = ' ';
+ }
+ p++;
+ }
+
+ // trim trailing white spaces
+ while (n && isspace(pOut[n-1]))
+ pOut[--n] = 0;
+
+ if (n)
+ bResult = TRUE;
+ }
+
+ GlobalUnlock(hData);
+ }
+
+ 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
+ SendMessage(SN->REHwnd, WM_SETTEXT, 0, (LPARAM)(SN->data));
+}
+
+
+static UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_INITDIALOG)
+ {
+ // hide color selector
+ ShowWindow(GetDlgItem(hdlg,0x443), SW_HIDE);
+ ShowWindow(GetDlgItem(hdlg,0x473), SW_HIDE);
+ TranslateDialogDefault(hdlg);
+ }
+
+ return 0;
+}
+
+
+INT_PTR CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CLOSE:
+ return TRUE;
+
+ case WM_SIZE:
+ {
+ HWND H;
+ RECT SZ;
+
+ GetClientRect(hdlg,&SZ);
+ H = GetDlgItem(hdlg,1);
+ MoveWindow(H, 0, 0, SZ.right,SZ.bottom, TRUE);
+
+ KillTimer(hdlg, 1025);
+ SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);
+
+ return TRUE;
+ }
+ case WM_TIMER:
+ if (wParam == 1025)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ KillTimer(hdlg, 1025);
+ JustSaveNotesEx(SN);
+ }
+ break;
+ case WM_MOVE:
+ {
+ KillTimer(hdlg, 1025);
+ SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);
+ return TRUE;
+ }
+ case WM_CREATE:
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ CREATESTRUCT *CS = (CREATESTRUCT *)lParam;
+ HWND H;
+ DWORD mystyle;
+
+ 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;
+ H = CreateWindow(RICHEDIT_CLASS, 0, mystyle, 0, 0, CS->cx-3-3, CS->cy-3-(3+14), hdlg, (HMENU)1, hmiranda, 0);
+ 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;
+ }
+ 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;
+ case WM_ERASEBKGND:
+ // no BG needed as edit control takes up entire client area
+ return TRUE;
+ case WM_NCPAINT:
+ // make window borders have the same color as caption
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ HBRUSH hBkBrush;
+ RECT rect, wr, r;
+ //HDC hdc = GetDCEx(hdlg, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
+ HDC hdc = GetWindowDC(hdlg);
+
+ GetWindowRect(hdlg, &wr);
+ if (wParam && wParam != 1)
+ {
+ SelectClipRgn(hdc, (HRGN)wParam);
+ OffsetClipRgn(hdc, -wr.left, -wr.top);
+ }
+
+ rect = wr;
+ OffsetRect(&rect, -wr.left, -wr.top);
+
+ if (g_isWin2kPlus)
+ {
+ hBkBrush = (HBRUSH)GetStockObject(DC_BRUSH);
+ SetDCBrushColor(hdc, GetCaptionColor((SN && SN->BgColor) ? SN->BgColor : BodyColor));
+ }
+ else
+ {
+ hBkBrush = (HBRUSH)CreateSolidBrush( GetCaptionColor((SN && SN->BgColor) ? SN->BgColor : BodyColor) );
+ }
+
+ //FillRect(hdc, &rect, hBkBrush);
+ // 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);
+ }
+ if (hBkBrush && !g_isWin2kPlus) DeleteObject(hBkBrush);
+
+ // paint title bar contents (time stamp and buttons)
+
+ 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;
+
+ 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 (g_ShowNoteButtons)
+ {
+ HICON hcIcon;
+ if (SN->OnTop)
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[4]);
+ else
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[7]);
+ DrawIcon(hdc, wr.right - wr.left - 16, 0 + 3, hcIcon);
+ Skin_ReleaseIcon(hcIcon);
+
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[9]);
+ DrawIcon(hdc, wr.right - wr.left - 32, 1 + 3, hcIcon);
+ Skin_ReleaseIcon(hcIcon);
+
+ hcIcon = Skin_GetIconByHandle(hIconLibItem[8]);
+ DrawIcon(hdc, wr.right - wr.left - 48, 1 + 3, hcIcon);
+ Skin_ReleaseIcon(hcIcon);
+ }
+
+ if (wParam && wParam != 1)
+ {
+ SelectClipRgn(hdc, NULL);
+ }
+
+ ReleaseDC(hdlg, hdc);
+ return TRUE;
+ }
+ 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;
+ }
+ case WM_NCACTIVATE:
+ // update window (so that parts that potentially became visible through activation get redrawn immediately)
+ RedrawWindow(hdlg, NULL, NULL, RDW_UPDATENOW);
+ return TRUE;
+ case WM_NOTIFY:
+ if (LOWORD(wParam) == 1)
+ {
+ char *Buff;
+ PENLINK PEnLnk = (PENLINK)lParam;
+
+ 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;
+ }
+ 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;
+ }
+ case WM_NCLBUTTONDOWN:
+ if (wParam == HTCAPTION && g_ShowNoteButtons)
+ {
+ long X,Y;
+ RECT rect;
+ int Tw;
+
+ GetWindowRect(hdlg, &rect);
+ Tw = rect.right - rect.left;
+
+ X = LOWORD(lParam) - rect.left;
+ Y = HIWORD(lParam) - rect.top;
+
+ 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);
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpMeasureItem = (LPMEASUREITEMSTRUCT)lParam;
+
+ if (lpMeasureItem->CtlType != ODT_MENU)
+ break;
+
+ if (lpMeasureItem->itemID >= IDM_COLORPRESET_BG && lpMeasureItem->itemID <= IDM_COLORPRESET_BG+SIZEOF(clrPresets))
+ {
+ MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_BG));
+ return TRUE;
+ }
+ else if (lpMeasureItem->itemID >= IDM_COLORPRESET_FG && lpMeasureItem->itemID <= IDM_COLORPRESET_FG+SIZEOF(clrPresets))
+ {
+ MeasureColorPresetMenuItem(hdlg, lpMeasureItem, clrPresets + (lpMeasureItem->itemID - IDM_COLORPRESET_FG));
+ return TRUE;
+ }
+ }
+ break;
+ case WM_DRAWITEM:
+ if (!wParam)
+ {
+ LPDRAWITEMSTRUCT lpDrawItem = (LPDRAWITEMSTRUCT)lParam;
+
+ if (lpDrawItem->CtlType != ODT_MENU)
+ break;
+
+ if (lpDrawItem->itemID >= IDM_COLORPRESET_BG && lpDrawItem->itemID <= IDM_COLORPRESET_BG+SIZEOF(clrPresets))
+ {
+ PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_BG));
+ return TRUE;
+ }
+ else if (lpDrawItem->itemID >= IDM_COLORPRESET_FG && lpDrawItem->itemID <= IDM_COLORPRESET_FG+SIZEOF(clrPresets))
+ {
+ PaintColorPresetMenuItem(lpDrawItem, clrPresets + (lpDrawItem->itemID - IDM_COLORPRESET_FG));
+ return TRUE;
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,"ctrldata");
+
+ HWND H;
+ UINT id;
+
+ switch ( HIWORD(wParam) )
+ {
+ case EN_CHANGE:
+ case EN_VSCROLL:
+ case EN_HSCROLL:
+ {
+ KillTimer(hdlg,1025);
+ SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);
+ }
+ break;
+ }
+
+ id = (UINT) LOWORD(wParam);
+
+ H = SN->REHwnd;
+
+ if (id >= IDM_COLORPRESET_BG && id <= IDM_COLORPRESET_BG+SIZEOF(clrPresets))
+ {
+ SN->BgColor = clrPresets[id-IDM_COLORPRESET_BG].color | 0xff000000;
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)(SN->BgColor&0xffffff));
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ return FALSE;
+ }
+ else if (id >= IDM_COLORPRESET_FG && id <= IDM_COLORPRESET_FG+SIZEOF(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, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ return FALSE;
+ }
+
+ switch (id)
+ {
+ case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE:
+ {
+ PluginMenuCommandAddNew(0,0);
+ }
+ break;
+ case ID_APPEARANCE_CUSTOMBG:
+ {
+ COLORREF custclr[16] = {0};
+ CHOOSECOLOR cc = {0};
+ COLORREF orgclr = SN->BgColor ? (COLORREF)(SN->BgColor&0xffffff) : (COLORREF)(BodyColor&0xffffff);
+ cc.lStructSize = sizeof(cc);
+ cc.hwndOwner = SN->SNHwnd;
+ cc.rgbResult = orgclr;
+ cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT | CC_SOLIDCOLOR;
+ cc.lpCustColors = custclr;
+
+ if (ChooseColor(&cc) && cc.rgbResult != orgclr)
+ {
+ SN->BgColor = cc.rgbResult | 0xff000000;
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)(SN->BgColor&0xffffff));
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ 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, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_APPEARANCE_CUSTOMFONT:
+ {
+ CHOOSEFONT cf = {0};
+ LOGFONT lf = {0};
+
+ if (SN->pCustomFont)
+ InitStickyNoteLogFont(SN->pCustomFont, &lf);
+ else
+ LoadNRFont(NR_FONTID_BODY, &lf, NULL);
+
+ 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 = NULL;
+ }
+
+ 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;
+ _tcscpy(SN->pCustomFont->szFace, lf.lfFaceName);
+
+ if ( !CreateStickyNoteFont(SN->pCustomFont, &lf) )
+ {
+ // failed
+ free(SN->pCustomFont);
+ SN->pCustomFont = NULL;
+ }
+
+ // clear text first to force a reformatting w.r.t scrollbar
+ SendMessage(H, WM_SETTEXT, 0, (LPARAM)"");
+ SendMessage(H, WM_SETFONT, (WPARAM)(SN->pCustomFont ? SN->pCustomFont->hFont : hBodyFont), FALSE);
+ SetNoteTextControl(SN);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_BACKGROUNDCOLOR_RESET:
+ {
+ SN->BgColor = 0;
+ SendMessage(H, EM_SETBKGNDCOLOR, 0, (LPARAM)BodyColor);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ 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, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case ID_FONT_RESET:
+ {
+ if (SN->pCustomFont)
+ {
+ DeleteObject(SN->pCustomFont->hFont);
+ free(SN->pCustomFont);
+ SN->pCustomFont = NULL;
+
+ // clear text first to force a reformatting w.r.t scrollbar
+ SendMessage(H, WM_SETTEXT, 0, (LPARAM)"");
+ SendMessage(H, WM_SETFONT, (WPARAM)hBodyFont, FALSE);
+ SetNoteTextControl(SN);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_PASTETITLE:
+ {
+ char s[MAX_TITLE_LEN+1];
+ if ( GetClipboardText_Title(s, sizeof(s)) )
+ {
+ if (SN->title)
+ free(SN->title);
+ SN->title = _strdup(s);
+ SN->CustomTitle = TRUE;
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_RESETTITLE:
+ if (SN->CustomTitle)
+ {
+ if (SN->title)
+ {
+ free(SN->title);
+ SN->title = NULL;
+ }
+ InitNoteTitle(SN);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_INVALIDATE|RDW_FRAME|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case IDM_REMOVENOTE:
+ OnDeleteNote(hdlg, SN);
+ break;
+ case IDM_HIDENOTE:
+ {
+ SN->Visible = FALSE;
+ ShowWindow(hdlg,SW_HIDE);
+ JustSaveNotes();
+ }
+ break;
+ case IDM_COPY: SendMessage(H,WM_COPY,0,0); break;
+ case IDM_PASTE: SendMessage(H,WM_PASTE,0,0); break;
+ case IDM_CUT: SendMessage(H,WM_CUT,0,0); break;
+ case IDM_CLEAR: SendMessage(H,WM_CLEAR,0,0); break;
+ case IDM_UNDO: SendMessage(H,WM_UNDO,0,0); break;
+ case IDM_TOGGLEONTOP:
+ {
+ SN->OnTop = !SN->OnTop;
+ SetWindowPos(hdlg, SN->OnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
+ RedrawWindow(hdlg, NULL, NULL, RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES:
+ ListNotes();
+ break;
+ case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP:
+ BringAllNotesToFront(SN);
+ break;
+ }
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ {
+ RemoveProp(hdlg, "ctrldata");
+ }
+ break;
+ case WM_CONTEXTMENU:
+ if (DoContextMenu(hdlg,wParam,lParam)) return FALSE;
+
+ default:
+ return DefWindowProc(hdlg,message,wParam,lParam);
+ }
+ return FALSE;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Notes List Dialog (uses same dialog template as reminder list)
+
+void ListNotes(void)
+{
+ if (!ListNotesVisible)
+ {
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_LISTREMINDERS), 0, DlgProcViewNotes);
+ ListNotesVisible = TRUE;
+ }
+ else
+ {
+ BringWindowToTop(LV);
+ }
+}
+
+static void EditNote(STICKYNOTE *SN)
+{
+ if (!SN)
+ return;
+
+ if (!SN->Visible)
+ {
+ SN->Visible = TRUE;
+ JustSaveNotes();
+ }
+
+ SetWindowPos(SN->SNHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+ if (!SN->OnTop)
+ SetWindowPos(SN->SNHwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+
+ SetFocus(SN->REHwnd);
+}
+
+char* GetPreviewString(const char *lpsz)
+{
+ int l;
+ char *p;
+ const int MaxLen = 80;
+ static char s[80+8];
+
+ if (!lpsz)
+ return "";
+
+ // trim leading spaces
+ while ( iswspace(*lpsz) )
+ lpsz++;
+
+ l = strlen(lpsz);
+
+ if (!l)
+ return "";
+
+ if (l <= MaxLen)
+ {
+ strcpy(s, lpsz);
+ }
+ else
+ {
+ memcpy(s, lpsz, MaxLen);
+ s[MaxLen] = '.';
+ s[MaxLen+1] = '.';
+ s[MaxLen+2] = '.';
+ s[MaxLen+3] = 0;
+ }
+
+ if (!s)
+ return NULL;
+
+ // convert line breaks and tabs to spaces
+
+ p = s;
+
+ while (*p)
+ {
+ if ( iswspace(*p) )
+ *p = ' ';
+ p++;
+ }
+
+ return s;
+}
+
+static void InitListView(HWND AHLV)
+{
+ LV_ITEM lvTIt;
+ int I;
+ char *S;
+ char S1[128];
+ STICKYNOTE *pNote;
+ TREEELEMENT *TTE;
+
+ char *V = Translate("V");
+ char *T = Translate("T");
+
+ ListView_SetHoverTime(AHLV,700);
+ ListView_SetExtendedListViewStyle(AHLV,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_TRACKSELECT);
+ ListView_DeleteAllItems(AHLV);
+
+ I = 0;
+ TTE = g_Stickies;
+ while (TTE)
+ {
+ pNote = (STICKYNOTE*)TTE->ptrdata;
+
+ lvTIt.mask = LVIF_TEXT;
+
+ if (!pNote->CustomTitle || !pNote->title)
+ GetTriggerTimeString(&pNote->ID, S1, sizeof(S1), TRUE);
+
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 0;
+ lvTIt.pszText = (pNote->CustomTitle && pNote->title) ? pNote->title : S1;
+ lvTIt.cchTextMax = strlen(S1);
+ ListView_InsertItem(AHLV,&lvTIt);
+
+ if (pNote->Visible)
+ {
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 1;
+ lvTIt.pszText = V;
+ lvTIt.cchTextMax = strlen(lvTIt.pszText);
+ ListView_SetItem(AHLV,&lvTIt);
+ }
+
+ if (pNote->OnTop)
+ {
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 2;
+ lvTIt.pszText = T;
+ lvTIt.cchTextMax = strlen(lvTIt.pszText);
+ ListView_SetItem(AHLV,&lvTIt);
+ }
+
+ S = GetPreviewString(pNote->data);
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 3;
+ lvTIt.pszText = S;
+ lvTIt.cchTextMax = strlen(S);
+ ListView_SetItem(AHLV,&lvTIt);
+
+ I++;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ ListView_SetItemState(AHLV,0,LVIS_SELECTED,LVIS_SELECTED);
+}
+
+static BOOL DoListContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam,STICKYNOTE *pNote)
+{
+ HWND hwndListView;
+ HMENU hMenuLoad,FhMenu;
+ MENUITEMINFO mii;
+
+ hwndListView = (HWND)wParam;
+ if (hwndListView != GetDlgItem(AhWnd,IDC_LISTREMINDERS)) return FALSE;
+ hMenuLoad = LoadMenu(hinstance,"MNU_NOTELISTPOPUP");
+ FhMenu = GetSubMenu(hMenuLoad,0);
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+ if (!pNote)
+ mii.fState |= MFS_GRAYED;
+ SetMenuItemInfo(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE, FALSE, &mii);
+
+ if (!pNote)
+ {
+ EnableMenuItem(FhMenu, IDM_REMOVENOTE, MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY, MF_GRAYED|MF_BYCOMMAND);
+ EnableMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_GRAYED|MF_BYCOMMAND);
+ }
+ else
+ {
+ if (pNote->Visible)
+ CheckMenuItem(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY, MF_CHECKED|MF_BYCOMMAND);
+ if (pNote->OnTop)
+ CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED|MF_BYCOMMAND);
+ }
+
+ CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
+ TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
+ DestroyMenu(hMenuLoad);
+
+ return TRUE;
+}
+
+
+INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ LV_COLUMN lvCol;
+ NMLISTVIEW *NM;
+ char *S;
+ int I;
+
+ switch (Message)
+ {
+ case WM_SIZE:
+ {
+ OnListResize(Dialog);
+ UpdateGeomFromWnd(Dialog, g_notesListGeom, NULL, 0);
+ break;
+ }
+ case WM_MOVE:
+ UpdateGeomFromWnd(Dialog, g_notesListGeom, NULL, 0);
+ break;
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *mm = (MINMAXINFO*)lParam;
+ mm->ptMinTrackSize.x = 394;
+ mm->ptMinTrackSize.y = 300;
+ }
+ return 0;
+ case WM_RELOAD:
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+ InitListView(GetDlgItem(Dialog,IDC_LISTREMINDERS));
+ return TRUE;
+ }
+ case WM_CONTEXTMENU:
+ {
+ HWND H;
+ STICKYNOTE *pNote = NULL;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ pNote = (STICKYNOTE*)TreeGetAt(g_Stickies, I);
+ }
+ }
+
+ if (DoListContextMenu(Dialog, wParam, lParam, pNote))
+ return TRUE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ HWND H;
+
+ HICON hIcon = Skin_GetIconByHandle(hIconLibItem[13], ICON_SMALL);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
+ hIcon = Skin_GetIconByHandle(hIconLibItem[13], ICON_BIG);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
+
+ SetWindowText(Dialog, _T("Notes"));
+
+ TranslateDialogDefault(Dialog);
+
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("Note text");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[3];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("T");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[2];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("V");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[1];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+
+ S = Translate("Date/Title");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_notesListColGeom[0];
+ ListView_InsertColumn(H,0,&lvCol);
+
+ InitListView(H);
+ SetWindowLong(GetDlgItem(H, 0), GWL_ID, IDC_LISTREMINDERS_HEADER);
+ LV = Dialog;
+
+ if (g_notesListGeom[1] && g_notesListGeom[2])
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(Dialog, &wp);
+ wp.rcNormalPosition.left = g_notesListGeom[0];
+ wp.rcNormalPosition.top = g_notesListGeom[1];
+ wp.rcNormalPosition.right = g_notesListGeom[2] + g_notesListGeom[0];
+ wp.rcNormalPosition.bottom = g_notesListGeom[3] + g_notesListGeom[1];
+ SetWindowPlacement(Dialog, &wp);
+ }
+ return TRUE;
+ }
+ case WM_CLOSE:
+ DestroyWindow(Dialog);
+ ListNotesVisible = FALSE;
+ return TRUE;
+
+ case WM_DESTROY:
+ ListNotesVisible = FALSE;
+ Skin_ReleaseIcon((HICON)SendMessage(Dialog, WM_SETICON, ICON_BIG, 0));
+ Skin_ReleaseIcon((HICON)SendMessage(Dialog, WM_SETICON, ICON_SMALL, 0));
+ return TRUE;
+
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_LISTREMINDERS)
+ {
+ NM = (NMLISTVIEW *)lParam;
+ switch (NM->hdr.code)
+ {
+ case LVN_ITEMCHANGED:
+ {
+ S = ((STICKYNOTE*)TreeGetAt(g_Stickies,NM->iItem))->data;
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,S);
+ }
+ break;
+ case NM_DBLCLK:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditNote((STICKYNOTE *)TreeGetAt(g_Stickies, I));
+ }
+ }
+ }
+ break;
+ }
+ }
+ else if (wParam == IDC_LISTREMINDERS_HEADER)
+ {
+ NMHEADER *NM = (NMHEADER*)lParam;
+ switch (NM->hdr.code)
+ {
+ case HDN_ENDTRACK:
+ UpdateGeomFromWnd(Dialog, NULL, g_notesListColGeom, SIZEOF(g_notesListColGeom));
+ break;
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditNote((STICKYNOTE*)TreeGetAt(g_Stickies, I));
+ }
+ }
+ }
+ return TRUE;
+ case ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies, I);
+ SN->Visible = !SN->Visible;
+ ShowWindow(SN->SNHwnd,SN->Visible?SW_SHOWNA:SW_HIDE);
+ JustSaveNotes();
+ }
+ }
+ }
+ return TRUE;
+ case IDM_TOGGLEONTOP:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies,I);
+ SN->OnTop = !SN->OnTop;
+ SetWindowPos(SN->SNHwnd, SN->OnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
+ RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW);
+ JustSaveNotes();
+ }
+ }
+ }
+ return TRUE;
+ case IDC_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListNotesVisible = FALSE;
+ return TRUE;
+ }
+ case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE:
+ case IDC_ADDNEWREMINDER:
+ {
+ PluginMenuCommandAddNew(0,0);
+ return TRUE;
+ }
+ case ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES:
+ {
+ PluginMenuCommandDeleteAll(0,0);
+ return TRUE;
+ }
+ case IDM_REMOVENOTE:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ OnDeleteNote(Dialog, (STICKYNOTE*)TreeGetAt(g_Stickies, I));
+ }
+ }
+ }
+ return TRUE;
+ case ID_CONTEXTMENUNOTELISTVIEW_SHOW:
+ {
+ ShowHideNotes();
+ return TRUE;
+ }
+ case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP:
+ {
+ BringAllNotesToFront(NULL);
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
diff --git a/plugins/NotesAndReminders/src/options.cpp b/plugins/NotesAndReminders/src/options.cpp new file mode 100644 index 0000000000..026d7e37a4 --- /dev/null +++ b/plugins/NotesAndReminders/src/options.cpp @@ -0,0 +1,636 @@ +#include "globals.h"
+
+#ifndef OFN_DONTADDTORECENT
+#define OFN_DONTADDTORECENT 0x02000000
+#endif
+
+
+// min allowed alpha (don't want 0 because it's a waste of resources as well as might confuse user)
+#define MIN_ALPHA 30
+
+
+#define IDC_RESET 1007
+#define IDC_SHOWNOTES 1010
+#define IDC_ADDCONTACTMENU 1011
+#define IDC_NOTEWIDTH 1012
+#define IDC_NOTEHEIGHT 1013
+#define IDC_TRANSTRACK 1014
+#define IDC_REMINDEMAIL 1017
+#define IDC_SHOWSCROLLBAR 1018
+#define IDC_SHOWBUTTONS 1019
+#define IDC_ADDREMINDERCLOSES 1020
+#define IDC_USEMCI 1023
+#define PSM_CHANGED (WM_USER + 104)
+
+
+extern HANDLE hkFontChange;
+extern HANDLE hkColorChange;
+
+BOOL g_CloseAfterAddReminder, g_UseDefaultPlaySound;
+HICON g_hReminderIcon = NULL;
+
+LOGFONT lfBody,lfCaption;
+HFONT hBodyFont = NULL, hCaptionFont = NULL;
+long BodyColor;
+long CaptionFontColor,BodyFontColor;
+BOOL g_ShowNotesAtStart,g_ShowScrollbar,g_AddContListMI,g_ShowNoteButtons;
+int g_NoteTitleDate, g_NoteTitleTime;
+int g_NoteWidth,g_NoteHeight;
+int g_Transparency;
+char *g_RemindSMS = NULL;
+BOOL g_isWin2kPlus = TRUE;
+
+TCHAR *g_lpszAltBrowser = NULL;
+
+int g_reminderListGeom[4] = {0};
+int g_reminderListColGeom[2] = { 150, 205 };
+int g_notesListGeom[4] = {0};
+int g_notesListColGeom[4] = { 150, 20, 20, 165 };
+
+
+#define NRCDEFAULT_BODYCLR RGB(255,255,0)
+
+
+struct DateFormat
+{
+ LPCSTR lpszUI;
+ LPCSTR 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" }
+};
+
+struct TimeFormat
+{
+ LPCSTR lpszUI;
+ LPCSTR 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" }
+};
+
+
+struct FontOptionsList
+{
+ TCHAR *szDescr;
+ COLORREF defColour;
+ TCHAR *szDefFace;
+ BYTE defStyle;
+ char defSize;
+ TCHAR *szBkgName;
+}
+static fontOptionsList[] =
+{
+ {LPGENT("Sticky Note Caption"), RGB(0,0,0), _T("Small Fonts"), 0, 7, LPGENT("Sticky Note Background Color")},
+ //{LPGENT("Sticky Note Caption"), RGB(0,0,0), _T("Terminal"), 0, 6, LPGENT("Sticky Note Background Color")},
+ //{LPGENT("Sticky Note Caption"), RGB(0,0,0), _T("MS Serif"), 0, 7, LPGENT("Sticky Note Background Color")},
+ //{LPGENT("Sticky Note Body"), RGB(0,0,0), _T("Tahoma"), 0, 8, LPGENT("Sticky Note Background Color")},
+ {LPGENT("Sticky Note Body"), RGB(0,0,0), _T("System"), DBFONTF_BOLD, 10, LPGENT("Sticky Note Background Color")},
+};
+
+
+struct ColourOptionsList
+{
+ TCHAR *szName;
+ char *szSettingName;
+ COLORREF defColour;
+}
+static colourOptionsList[] =
+{
+ {LPGENT("Sticky Note Background Color"), "BodyColor", NRCDEFAULT_BODYCLR}
+};
+
+
+LPCSTR GetDateFormatStr()
+{
+ return dateFormats[g_NoteTitleDate ? g_NoteTitleDate-1 : 0].lpszFmt;
+}
+
+LPCSTR GetTimeFormatStr()
+{
+ return timeFormats[g_NoteTitleTime ? g_NoteTitleTime-1 : 0].lpszFmt;
+}
+
+#if defined( _UNICODE )
+static BYTE MsgDlgGetFontDefaultCharset(const TCHAR* 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 *lpelfe, const TEXTMETRIC *lpntme, DWORD FontType, LPARAM lParam)
+{
+ *(int*)lParam = 1;
+ return 0;
+}
+
+// get font charset according to current CP, if available for specified font
+static BYTE MsgDlgGetFontDefaultCharset(const TCHAR* szFont)
+{
+ HDC hdc;
+ LOGFONT lf = {0};
+ int found = 0;
+
+ _tcscpy(lf.lfFaceName, szFont);
+ lf.lfCharSet = MsgDlgGetCPDefaultCharset();
+
+ // check if the font supports specified charset
+ hdc = GetDC(0);
+ EnumFontFamiliesEx(hdc, &lf, &EnumFontFamExProc, (LPARAM)&found, 0);
+ ReleaseDC(0, hdc);
+
+ if (found)
+ return lf.lfCharSet;
+ else // no, give default
+ return DEFAULT_CHARSET;
+}
+#endif
+
+
+static void InitFonts()
+{
+ ZeroMemory(&lfBody,sizeof(LOGFONT));
+ ZeroMemory(&lfCaption,sizeof(LOGFONT));
+
+ LoadNRFont(NR_FONTID_CAPTION, &lfCaption, (COLORREF*)&CaptionFontColor);
+ LoadNRFont(NR_FONTID_BODY, &lfBody, (COLORREF*)&BodyFontColor);
+
+ if (hBodyFont)
+ DeleteObject(hBodyFont);
+ if (hCaptionFont)
+ DeleteObject(hCaptionFont);
+
+ hBodyFont = CreateFontIndirect(&lfBody);
+ hCaptionFont = CreateFontIndirect(&lfCaption);
+}
+
+
+static int FS_FontsChanged(WPARAM wParam, LPARAM lParam)
+{
+ InitFonts();
+
+ SaveNotes();
+ LoadNotes(FALSE);
+
+ return 0;
+}
+
+static int FS_ColorChanged(WPARAM wParam, LPARAM lParam)
+{
+ LoadNRFont(NR_FONTID_CAPTION, &lfCaption, (COLORREF*)&CaptionFontColor);
+ LoadNRFont(NR_FONTID_BODY, &lfBody, (COLORREF*)&BodyFontColor);
+
+ BodyColor = DBGetContactSettingDword(NULL, MODULENAME, colourOptionsList[0].szSettingName, colourOptionsList[0].defColour);
+
+ SaveNotes();
+ LoadNotes(FALSE);
+
+ return 0;
+}
+
+void RegisterFontServiceFonts()
+{
+ HDC hDC;
+ int nFontScale;
+ FontIDT fontid = {0};
+ ColourIDT colorid = {0};
+ char szTemp[100];
+ int i;
+
+ fontid.cbSize = sizeof(FontIDT);
+
+ mir_sntprintf(fontid.group, SIZEOF(fontid.group), _T("%s"), LPGENT(SECTIONNAME));
+ mir_sntprintf(fontid.backgroundGroup, SIZEOF(fontid.backgroundGroup), _T("%s"), LPGENT(SECTIONNAME));
+ strncpy(fontid.dbSettingsGroup, MODULENAME, SIZEOF(fontid.dbSettingsGroup));
+ fontid.flags = FIDF_ALLOWREREGISTER | FIDF_DEFAULTVALID | FIDF_SAVEPOINTSIZE;
+
+ hDC = GetDC(NULL);
+ nFontScale = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(NULL, hDC);
+
+ for (i = 0; i < SIZEOF(fontOptionsList); i++)
+ {
+ fontid.order = i;
+ mir_snprintf(szTemp, SIZEOF(szTemp), "Font%d", i);
+ strncpy(fontid.prefix, szTemp, SIZEOF(fontid.prefix));
+ _tcsncpy(fontid.name, fontOptionsList[i].szDescr, SIZEOF(fontid.name));
+ fontid.deffontsettings.colour = fontOptionsList[i].defColour;
+
+ fontid.deffontsettings.size = (char)-MulDiv(fontOptionsList[i].defSize, nFontScale, 72);
+ //fontid.deffontsettings.size = fontOptionsList[i].defSize;
+
+ fontid.deffontsettings.style = fontOptionsList[i].defStyle;
+ fontid.deffontsettings.charset = MsgDlgGetFontDefaultCharset(fontOptionsList[i].szDefFace);
+ _tcsncpy(fontid.deffontsettings.szFace, fontOptionsList[i].szDefFace, SIZEOF(fontid.deffontsettings.szFace));
+ _tcsncpy(fontid.backgroundName, fontOptionsList[i].szBkgName, SIZEOF(fontid.backgroundName));
+
+ FontRegisterT(&fontid);
+ }
+
+ colorid.cbSize = sizeof(ColourIDT);
+
+ mir_sntprintf(colorid.group, SIZEOF(colorid.group), _T("%s"), LPGENT(SECTIONNAME));
+ strncpy(colorid.dbSettingsGroup, MODULENAME, SIZEOF(fontid.dbSettingsGroup));
+ colorid.flags = 0;
+
+ for (i = 0; i < SIZEOF(colourOptionsList); i++)
+ {
+ colorid.order = i;
+ _tcsncpy(colorid.name, colourOptionsList[i].szName, SIZEOF(colorid.name));
+ colorid.defcolour = colourOptionsList[i].defColour;
+ strncpy(colorid.setting, colourOptionsList[i].szSettingName, SIZEOF(colorid.setting));
+
+ ColourRegisterT(&colorid);
+ }
+
+ hkFontChange = HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
+ hkColorChange = HookEvent(ME_COLOUR_RELOAD, FS_ColorChanged);
+}
+
+void LoadNRFont(int i, LOGFONT *lf, COLORREF *colour)
+{
+ FontIDT fontid = {0};
+
+ fontid.cbSize = sizeof(fontid);
+ _tcsncpy(fontid.group, LPGENT(SECTIONNAME), SIZEOF(fontid.group));
+ _tcsncpy(fontid.name, fontOptionsList[i].szDescr, SIZEOF(fontid.name));
+
+ COLORREF col = CallService(MS_FONT_GETT, (WPARAM)&fontid, (LPARAM)lf);
+
+ if (colour)
+ {
+ *colour = col;
+ }
+}
+
+
+static void TrimString(TCHAR *s)
+{
+ TCHAR *start;
+ TCHAR *end;
+ UINT n;
+
+ if (!s || !*s)
+ {
+ return;
+ }
+
+ start = s;
+ n = _tcslen(s) - 1;
+
+ end = s + n;
+
+ if (!_istspace(*start) && !_istspace(*end))
+ {
+ // nothing to trim
+ return;
+ }
+
+ // scan past leading spaces
+ while (*start && _istspace(*start)) start++;
+
+ if (!*start)
+ {
+ // empty string
+ *s = 0;
+ return;
+ }
+
+ // trim trailing spaces
+ while ( _istspace(*end) ) end--;
+ end[1] = 0;
+
+ if (start > s)
+ {
+ // remove leading spaces
+ memmove(s, start, ((UINT)(end-start)+2)*sizeof(TCHAR));
+ }
+}
+
+
+INT_PTR CALLBACK DlgProcOptions(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ BOOL LB;
+ WORD SzT;
+ void *P;
+ int i;
+
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hdlg);
+ SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_SETRANGE,TRUE,MAKELONG(0,255-MIN_ALPHA));
+ SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_SETPOS,TRUE,255-g_Transparency);
+ SendDlgItemMessage(hdlg,IDC_SHOWNOTES,BM_SETCHECK,(WPARAM)!g_ShowNotesAtStart,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWBUTTONS,BM_SETCHECK,(WPARAM)g_ShowNoteButtons,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWSCROLLBAR,BM_SETCHECK,(WPARAM)g_ShowScrollbar,0); // 4.2
+ SendDlgItemMessage(hdlg,IDC_ADDCONTACTMENU,BM_SETCHECK,(WPARAM)g_AddContListMI,0);
+ SetDlgItemInt(hdlg,IDC_NOTEWIDTH,g_NoteWidth,FALSE);
+ SetDlgItemInt(hdlg,IDC_NOTEHEIGHT,g_NoteHeight,FALSE);
+ SendDlgItemMessage(hdlg,IDC_ADDREMINDERCLOSES,BM_SETCHECK,(WPARAM)g_CloseAfterAddReminder,0);
+ SendDlgItemMessage(hdlg,IDC_USEMCI,BM_SETCHECK,(WPARAM)!g_UseDefaultPlaySound,0);
+
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_RESETCONTENT,0,0);
+ SendDlgItemMessage(hdlg,IDC_COMBOTIME,CB_RESETCONTENT,0,0);
+ for (i=0; i<SIZEOF(dateFormats); i++)
+ SendDlgItemMessage(hdlg,IDC_COMBODATE,CB_ADDSTRING,0,(LPARAM)dateFormats[i].lpszUI);
+ for (i=0; i<SIZEOF(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);
+
+ if (g_RemindSMS)
+ SetDlgItemText(hdlg,IDC_REMINDEMAIL,g_RemindSMS);
+ else
+ SetDlgItemText(hdlg,IDC_REMINDEMAIL,"");
+
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,g_lpszAltBrowser ? g_lpszAltBrowser : _T(""));
+ if (!MySetLayeredWindowAttributes)
+ { // layered UI not available
+ EnableWindow(GetDlgItem(hdlg,IDC_TRANSTRACK), FALSE);
+ }
+ return TRUE;
+ }
+ case WM_HSCROLL:
+ {
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY)
+ {
+ g_ShowNotesAtStart = !(BOOL)SendDlgItemMessage(hdlg,IDC_SHOWNOTES,BM_GETCHECK,0,0);
+ g_ShowNoteButtons = (BOOL)SendDlgItemMessage(hdlg,IDC_SHOWBUTTONS,BM_GETCHECK,0,0);
+ g_ShowScrollbar = (BOOL)SendDlgItemMessage(hdlg,IDC_SHOWSCROLLBAR,BM_GETCHECK,0,0); // 4.2
+ g_AddContListMI = (BOOL)SendDlgItemMessage(hdlg,IDC_ADDCONTACTMENU,BM_GETCHECK,0,0);
+ g_NoteWidth = GetDlgItemInt(hdlg,IDC_NOTEWIDTH,&LB,FALSE);
+ g_NoteHeight = GetDlgItemInt(hdlg,IDC_NOTEHEIGHT,&LB,FALSE);
+ g_Transparency = 255-SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_GETPOS,0,0);
+ g_CloseAfterAddReminder = (BOOL)SendDlgItemMessage(hdlg,IDC_ADDREMINDERCLOSES,BM_GETCHECK,0,0);
+ g_UseDefaultPlaySound = !(BOOL)SendDlgItemMessage(hdlg,IDC_USEMCI,BM_GETCHECK,0,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)
+ {
+ g_NoteWidth = 179;
+ SetDlgItemInt(hdlg,IDC_NOTEWIDTH,g_NoteWidth,FALSE);
+ }
+ if (g_NoteHeight < 35)
+ {
+ g_NoteHeight = 35;
+ SetDlgItemInt(hdlg,IDC_NOTEHEIGHT,g_NoteHeight,FALSE);
+ }
+ SzT = (WORD)SendDlgItemMessage(hdlg,IDC_REMINDEMAIL,WM_GETTEXTLENGTH,0,0);
+ if (SzT != 0)
+ {
+ g_RemindSMS = (char*)realloc(g_RemindSMS,SzT+1);
+ SendDlgItemMessage(hdlg,IDC_REMINDEMAIL,WM_GETTEXT,SzT+1,(LPARAM)g_RemindSMS);
+ }
+ P = g_RemindSMS;
+ WriteSettingBlob(0,MODULENAME,"RemindEmail",SzT,P);
+
+ SzT = (WORD)SendDlgItemMessage(hdlg,IDC_EDIT_ALTBROWSER,WM_GETTEXTLENGTH,0,0);
+ if (SzT != 0)
+ {
+ g_lpszAltBrowser = (TCHAR*)mir_realloc(g_lpszAltBrowser,SzT+1);
+ SendDlgItemMessage(hdlg,IDC_EDIT_ALTBROWSER,WM_GETTEXT,SzT+1,(LPARAM)g_lpszAltBrowser);
+ TrimString(g_lpszAltBrowser);
+ if (!*g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+ }
+ else if (g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,g_lpszAltBrowser ? g_lpszAltBrowser : _T(""));
+ if (g_lpszAltBrowser)
+ DBWriteContactSettingString(0,MODULENAME,"AltBrowser",g_lpszAltBrowser);
+ else
+ DBDeleteContactSetting(0,MODULENAME,"AltBrowser");
+
+ WriteSettingInt(0,MODULENAME,"ShowNotesAtStart",g_ShowNotesAtStart);
+ WriteSettingInt(0,MODULENAME,"ShowNoteButtons",g_ShowNoteButtons);
+ WriteSettingInt(0,MODULENAME,"ShowScrollbar",g_ShowScrollbar); // 4.2
+ WriteSettingInt(0,MODULENAME,"AddContactMenuItems",g_AddContListMI);
+ WriteSettingInt(0,MODULENAME,"NoteWidth",g_NoteWidth);
+ WriteSettingInt(0,MODULENAME,"NoteHeight",g_NoteHeight);
+ WriteSettingInt(0,MODULENAME,"Transparency",g_Transparency);
+ WriteSettingInt(0,MODULENAME,"NoteTitleDate",g_NoteTitleDate);
+ WriteSettingInt(0,MODULENAME,"NoteTitleTime",g_NoteTitleTime);
+ WriteSettingInt(0,MODULENAME,"CloseAfterAddReminder",g_CloseAfterAddReminder);
+ WriteSettingInt(0,MODULENAME,"UseMCI",!g_UseDefaultPlaySound);
+ SaveNotes();
+ LoadNotes(FALSE);
+ return TRUE;
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_BTN_BROWSEBROWSER:
+ {
+ TCHAR s[MAX_PATH];
+
+ OPENFILENAME ofn = {0};
+#if defined(WINVER) && _WIN32_WINNT >= 0x0500
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#else
+ ofn.lStructSize = sizeof(ofn);
+#endif
+ ofn.hwndOwner = hdlg;
+ ofn.lpstrFilter = _T("Executable Files\0*.exe\0All Files\0*.*\0\0");
+ ofn.lpstrFile = s;
+ ofn.nMaxFile = SIZEOF(s);
+ ofn.lpstrTitle = TranslateT("Select Executable");
+ ofn.lpstrInitialDir = _T(".");
+ ofn.Flags = OFN_FILEMUSTEXIST|OFN_LONGNAMES;
+ if ( IsWinVer98Plus() )
+ {
+ ofn.Flags |= OFN_ENABLESIZING;
+ if (g_isWin2kPlus)
+ ofn.Flags |= OFN_DONTADDTORECENT;
+ }
+
+ SendDlgItemMessage(hdlg,IDC_EDIT_ALTBROWSER,WM_GETTEXT,(WPARAM)ofn.nMaxFile,(LPARAM)s);
+
+ if ( GetOpenFileName(&ofn) )
+ {
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,s);
+ }
+ }
+ break;
+ case IDC_RESET:
+ {
+ SAFE_FREE((void**)&g_RemindSMS);
+ SetDlgItemText(hdlg,IDC_REMINDEMAIL,_T(""));
+ if (g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+ SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,_T(""));
+ g_ShowNotesAtStart = TRUE;
+ g_AddContListMI = TRUE;
+ g_ShowScrollbar = TRUE; // 4.2
+ g_ShowNoteButtons = TRUE;
+ g_NoteTitleDate = 1;
+ g_NoteTitleTime = 1;
+ g_CloseAfterAddReminder = TRUE;
+ g_UseDefaultPlaySound = FALSE;
+ SendDlgItemMessage(hdlg,IDC_SHOWNOTES,BM_SETCHECK,!g_ShowNotesAtStart,0);
+ SendDlgItemMessage(hdlg,IDC_ADDCONTACTMENU,BM_SETCHECK,g_AddContListMI,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWSCROLLBAR,BM_SETCHECK,g_ShowScrollbar,0);
+ SendDlgItemMessage(hdlg,IDC_SHOWBUTTONS,BM_SETCHECK,(WPARAM)g_ShowNoteButtons,0);
+ SendDlgItemMessage(hdlg,IDC_ADDREMINDERCLOSES,BM_SETCHECK,g_CloseAfterAddReminder,0);
+ SendDlgItemMessage(hdlg,IDC_USEMCI,BM_SETCHECK,!g_UseDefaultPlaySound,0);
+ 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; // 4.2
+ g_NoteHeight = 35;
+ SetDlgItemInt(hdlg,IDC_NOTEWIDTH,g_NoteWidth,FALSE);
+ SetDlgItemInt(hdlg,IDC_NOTEHEIGHT,g_NoteHeight,FALSE);
+ g_Transparency = 255;
+ SendDlgItemMessage(hdlg,IDC_TRANSTRACK,TBM_SETPOS,TRUE,0);
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0); // JK optim
+ return TRUE;
+ }
+ default:
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void InitSettings(void)
+{
+ void *P = NULL;
+ short Sz1;
+
+ Sz1 = MAX_PATH; P = NULL;
+ ReadSettingBlob(0, MODULENAME, "RemindEmail", (WORD*)&Sz1, &P);
+ if (!(Sz1 && P))
+ g_RemindSMS = NULL;
+ else
+ {
+ g_RemindSMS = (char*)malloc(Sz1+1);
+ ZeroMemory(g_RemindSMS,Sz1+1);
+ memcpy(g_RemindSMS,P,Sz1);
+ FreeSettingBlob(Sz1,P);
+ }
+
+ g_lpszAltBrowser = DBGetString(0,MODULENAME,"AltBrowser");
+
+ g_ShowNotesAtStart = (BOOL)ReadSettingInt(0,MODULENAME,"ShowNotesAtStart",1);
+ g_ShowNoteButtons = (BOOL)ReadSettingInt(0,MODULENAME,"ShowNoteButtons",1);
+ g_ShowScrollbar = (BOOL)ReadSettingInt(0,MODULENAME,"ShowScrollbar",1);
+ g_AddContListMI = (BOOL)ReadSettingInt(0,MODULENAME,"AddContactMenuItems",1);
+ g_NoteWidth = ReadSettingInt(0,MODULENAME,"NoteWidth",179);
+ g_NoteHeight = ReadSettingInt(0,MODULENAME,"NoteHeight",50);
+ g_Transparency = ReadSettingInt(0,MODULENAME,"Transparency",255);
+ g_NoteTitleDate = ReadSettingInt(0,MODULENAME,"NoteTitleDate",1);
+ g_NoteTitleTime = ReadSettingInt(0,MODULENAME,"NoteTitleTime",1);
+ g_CloseAfterAddReminder = (BOOL)ReadSettingInt(0,MODULENAME,"CloseAfterAddReminder",1);
+ g_UseDefaultPlaySound = !(BOOL)ReadSettingInt(0,MODULENAME,"UseMCI",1);
+
+ ReadSettingIntArray(0,MODULENAME,"ReminderListGeom",g_reminderListGeom,SIZEOF(g_reminderListGeom));
+ ReadSettingIntArray(0,MODULENAME,"ReminderListColGeom",g_reminderListColGeom,SIZEOF(g_reminderListColGeom));
+ ReadSettingIntArray(0,MODULENAME,"NotesListGeom",g_notesListGeom,SIZEOF(g_notesListGeom));
+ ReadSettingIntArray(0,MODULENAME,"NotesListColGeom",g_notesListColGeom,SIZEOF(g_notesListColGeom));
+
+ BodyColor = DBGetContactSettingDword(NULL, MODULENAME, colourOptionsList[0].szSettingName, colourOptionsList[0].defColour);
+
+ InitFonts();
+
+ g_hReminderIcon = Skin_GetIconByHandle(hIconLibItem[10]);
+
+ if (g_Transparency < MIN_ALPHA)
+ g_Transparency = MIN_ALPHA;
+ else if (g_Transparency > 255)
+ g_Transparency = 255;
+}
+
+void TermSettings(void)
+{
+ if (g_reminderListGeom[2] > 0 && g_reminderListGeom[3] > 0)
+ {
+ WriteSettingIntArray(0,MODULENAME,"ReminderListGeom",g_reminderListGeom,SIZEOF(g_reminderListGeom));
+ WriteSettingIntArray(0,MODULENAME,"ReminderListColGeom",g_reminderListColGeom,SIZEOF(g_reminderListColGeom));
+ }
+ if (g_notesListGeom[2] > 0 && g_notesListGeom[3] > 0)
+ {
+ WriteSettingIntArray(0,MODULENAME,"NotesListGeom",g_notesListGeom,SIZEOF(g_notesListGeom));
+ WriteSettingIntArray(0,MODULENAME,"NotesListColGeom",g_notesListColGeom,SIZEOF(g_notesListColGeom));
+ }
+
+ if (g_lpszAltBrowser)
+ {
+ mir_free(g_lpszAltBrowser);
+ g_lpszAltBrowser = NULL;
+ }
+}
diff --git a/plugins/NotesAndReminders/src/reminders.cpp b/plugins/NotesAndReminders/src/reminders.cpp new file mode 100644 index 0000000000..4183892d09 --- /dev/null +++ b/plugins/NotesAndReminders/src/reminders.cpp @@ -0,0 +1,2881 @@ +#include "globals.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
+#define IDC_CLOSE 1003
+#define IDC_REMINDER 1004
+#define IDC_LISTREMINDERS 1000
+#define IDC_LISTREMINDERS_HEADER 2000
+#define IDC_REMINDERDATA 1001
+#define IDC_ADDNEWREMINDER 1002
+#define IDC_REMDATA 1000
+#define IDC_DISMISS 1001
+#define IDC_REMINDAGAIN 1002
+#define IDC_REMINDAGAININ 1003
+#define IDC_AFTER 1004
+#define IDC_ONDATE 1005
+#define IDC_DATEAGAIN 1006
+#define IDC_TIMEAGAIN 1007
+#define IDC_VIEWREMINDERS 1007
+#define IDC_NONE 1008
+#define IDC_DAILY 1009
+#define IDC_WEEKLY 1010
+#define IDC_MONTHLY 1011
+#define IDM_NEWREMINDER 40001
+#define IDM_DELETEREMINDER 40002
+#define IDM_DELETEALLREMINDERS 40003
+#define WM_RELOAD (WM_USER + 100)
+
+#define NOTIFY_LIST() if (ListReminderVisible) PostMessage(LV,WM_RELOAD,0,0)
+
+
+TREEELEMENT *RemindersList = NULL;
+static UINT QueuedReminderCount = 0;
+static BOOL ListReminderVisible = FALSE;
+static BOOL NewReminderVisible = FALSE;
+static REMINDERDATA *pEditReminder = NULL;
+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);
+
+INT_PTR CALLBACK DlgProcNotifyReminder(HWND Dialog,UINT Message,
+ WPARAM wParam,LPARAM lParam);
+INT_PTR CALLBACK DlgProcNewReminder(HWND Dialog,UINT Message,WPARAM wParam,
+ LPARAM lParam);
+INT_PTR CALLBACK DlgProcViewReminders(HWND Dialog,UINT Message,WPARAM wParam,
+ LPARAM lParam);
+
+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;
+}
+
+
+#ifndef WINXP_MINIMUM
+// TzSpecificLocalTimeToSystemTime/SystemTimeToTzSpecificLocalTime (re-)implemented to work on win2k and older
+
+static const int DaysInMonth[2][12] =
+{
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // normal year
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year
+};
+
+static __inline BOOL IsLeapYear(UINT Y)
+{
+ return !(Y & 3) && ((Y % 100) || !(Y % 400));
+}
+
+static int TZDateComp(const SYSTEMTIME *lpDate, const SYSTEMTIME *lpDateRef)
+{
+ int boundaryDay, day;
+
+ if (lpDate->wMonth < lpDateRef->wMonth)
+ return -1;
+ if (lpDate->wMonth > lpDateRef->wMonth)
+ return 1;
+
+ if (!lpDateRef->wYear)
+ {
+ const int week = (int)lpDateRef->wDay;
+ const WORD wFirst = (6 + lpDateRef->wDayOfWeek - lpDate->wDayOfWeek + lpDate->wDay) % 7 + 1;
+ boundaryDay = (int)wFirst + 7 * (week - 1);
+ if (boundaryDay > DaysInMonth[ IsLeapYear(lpDate->wYear) ][lpDate->wMonth-1])
+ boundaryDay -= 7;
+ }
+ else
+ boundaryDay = (int)lpDateRef->wDay;
+
+ boundaryDay = ((boundaryDay * 24 + (int)lpDateRef->wHour) * 60 + (int)lpDateRef->wMinute) * 60;
+ day = (((int)lpDate->wDay * 24 + (int)lpDate->wHour) * 60 + (int)lpDate->wMinute) * 60 + (int)lpDate->wSecond;
+
+ return (day < boundaryDay) ? -1 : (day > boundaryDay);
+}
+
+static UINT TZGetType(LPTIME_ZONE_INFORMATION lpTZI, ULARGE_INTEGER *lpFT, BOOL bLocal)
+{
+ if (lpTZI->DaylightDate.wMonth)
+ {
+ ULARGE_INTEGER ft = *lpFT;
+ SYSTEMTIME tm;
+ BOOL BeforeStandardDate, AfterDaylightDate;
+ UINT id;
+
+ if (!lpTZI->StandardDate.wMonth || (!lpTZI->StandardDate.wYear
+ && (!lpTZI->StandardDate.wDay || lpTZI->StandardDate.wDay > 5
+ || !lpTZI->DaylightDate.wDay || lpTZI->DaylightDate.wDay > 5)))
+ return TIME_ZONE_ID_INVALID;
+
+ if (!bLocal)
+ ft.QuadPart -= (LONGLONG)(lpTZI->Bias + lpTZI->DaylightBias) * (LONGLONG)600000000;
+
+ FileTimeToSystemTime((FILETIME*)&ft, &tm);
+
+ BeforeStandardDate = (TZDateComp(&tm, &lpTZI->StandardDate) < 0);
+
+ if (!bLocal)
+ {
+ ft.QuadPart -= (LONGLONG)(lpTZI->StandardBias - lpTZI->DaylightBias) * (LONGLONG)600000000;
+ FileTimeToSystemTime((FILETIME*)&ft, &tm);
+ }
+
+ AfterDaylightDate = (TZDateComp(&tm, &lpTZI->DaylightDate) >= 0);
+
+ id = TIME_ZONE_ID_STANDARD;
+ if (lpTZI->DaylightDate.wMonth < lpTZI->StandardDate.wMonth)
+ {
+ if (BeforeStandardDate && AfterDaylightDate)
+ id = TIME_ZONE_ID_DAYLIGHT;
+ }
+ else
+ {
+ if (BeforeStandardDate || AfterDaylightDate)
+ id = TIME_ZONE_ID_DAYLIGHT;
+ }
+
+ return id;
+ }
+
+ return TIME_ZONE_ID_UNKNOWN;
+}
+
+static BOOL TZGetBias(LPTIME_ZONE_INFORMATION lpTZI, ULARGE_INTEGER *lpFT, BOOL bLocal, LONG *pBias)
+{
+ LONG Bias = lpTZI->Bias;
+
+ switch ( TZGetType(lpTZI, lpFT, bLocal) )
+ {
+ case TIME_ZONE_ID_INVALID: return FALSE;
+ case TIME_ZONE_ID_DAYLIGHT: Bias += lpTZI->DaylightBias; break;
+ case TIME_ZONE_ID_STANDARD: Bias += lpTZI->StandardBias; break;
+ }
+
+ *pBias = Bias;
+
+ return TRUE;
+}
+
+#define TzSpecificLocalTimeToSystemTime _TzSpecificLocalTimeToSystemTime
+static BOOL _TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTZI, LPSYSTEMTIME lpLocal, LPSYSTEMTIME lpUtc)
+{
+ TIME_ZONE_INFORMATION tzi;
+ ULARGE_INTEGER ft;
+ LONG Bias;
+
+ // if possible use the real function (shouldn't be necessary, be feels more comfortable)
+ if (MyTzSpecificLocalTimeToSystemTime)
+ return MyTzSpecificLocalTimeToSystemTime(lpTZI, lpLocal, lpUtc);
+
+ if (!lpTZI)
+ {
+ if (GetTimeZoneInformation(&tzi) == TIME_ZONE_ID_INVALID)
+ return FALSE;
+ lpTZI = &tzi;
+ }
+
+ if (!SystemTimeToFileTime(lpLocal, (FILETIME*)&ft)
+ || !TZGetBias(lpTZI, &ft, TRUE, &Bias))
+ return FALSE;
+
+ ft.QuadPart += (LONGLONG)Bias * (LONGLONG)600000000;
+
+ return FileTimeToSystemTime((FILETIME*)&ft, lpUtc);
+}
+
+#define SystemTimeToTzSpecificLocalTime _SystemTimeToTzSpecificLocalTime
+static BOOL _SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTZI, LPSYSTEMTIME lpUtc, LPSYSTEMTIME lpLocal)
+{
+ TIME_ZONE_INFORMATION tzi;
+ ULARGE_INTEGER ft;
+ LONG Bias;
+
+ // if possible use the real function (shouldn't be necessary, be feels more comfortable)
+ if (MySystemTimeToTzSpecificLocalTime)
+ return MySystemTimeToTzSpecificLocalTime(lpTZI, lpUtc, lpLocal);
+
+ if (!lpTZI)
+ {
+ if (GetTimeZoneInformation(&tzi) == TIME_ZONE_ID_INVALID)
+ return FALSE;
+ lpTZI = &tzi;
+ }
+
+ if (!SystemTimeToFileTime(lpUtc, (FILETIME*)&ft)
+ || !TZGetBias(lpTZI, &ft, FALSE, &Bias))
+ return FALSE;
+
+ ft.QuadPart -= (LONGLONG)Bias * (LONGLONG)600000000;
+
+ return FileTimeToSystemTime((FILETIME*)&ft, lpLocal);
+}
+
+#endif
+
+
+// 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(NULL, &tm, &tmLocal);
+ SYSTEMTIMEtoFILETIME(&tmLocal, lpLocal);
+}
+
+void TzLocalToUtcFT(const FILETIME *lpLocal, FILETIME *lpUtc)
+{
+ SYSTEMTIME tm, tmUtc;
+ FILETIMEtoSYSTEMTIME(lpLocal, &tm);
+ TzSpecificLocalTimeToSystemTime(NULL, &tm, &tmUtc);
+ SYSTEMTIMEtoFILETIME(&tmUtc, lpUtc);
+}
+
+void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal)
+{
+ SYSTEMTIME tm;
+ FILETIMEtoSYSTEMTIME(lpUtc, &tm);
+ SystemTimeToTzSpecificLocalTime(NULL, &tm, tmLocal);
+}
+
+void TzLocalSTToFileTime(const SYSTEMTIME *tmLocal, FILETIME *lpUtc)
+{
+ SYSTEMTIME tm;
+ TzSpecificLocalTimeToSystemTime(NULL, (SYSTEMTIME*)tmLocal, &tm);
+ SYSTEMTIMEtoFILETIME(&tm, lpUtc);
+}
+
+/*void AddToTzLocalFT(FILETIME *lpLocal, UINT nSeconds)
+{
+ ULARGE_INTEGER utc;
+ TzLocalToUtcFT(lpLocal, (FILETIME*)&utc);
+ utc.QuadPart += (ULONGLONG)nSeconds * FILETIME_TICKS_PER_SEC;
+ UtcToTzLocalFT((FILETIME*)&utc, lpLocal);
+}*/
+
+/*static void AddToSystemTime(SYSTEMTIME *tm, UINT nSeconds)
+{
+ ULARGE_INTEGER li;
+ FILETIME ft;
+
+ SYSTEMTIMEtoFILETIME(tm, &ft);
+
+ li.HighPart = ft.dwHighDateTime;
+ li.LowPart = ft.dwLowDateTime;
+ li.QuadPart += (ULONGLONG)nSeconds * FILETIME_TICKS_PER_SEC;
+
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, tm);
+}*/
+
+
+static DWORD CreateUid()
+{
+ 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;
+
+try_next:;
+ }
+
+ // should never get here (unless someone has 4294967295 reminders)
+ return 0;
+}
+
+static REMINDERDATA* FindReminder(DWORD uid)
+{
+ TREEELEMENT *TTE;
+
+ if (!RemindersList)
+ return NULL;
+
+ TTE = RemindersList;
+ while (TTE)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ if (pReminder->uid == uid)
+ {
+ return pReminder;
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ return NULL;
+}
+
+
+static void RemoveReminderSystemEvent(REMINDERDATA *p)
+{
+ if (p->SystemEventQueued)
+ {
+ int i;
+
+ for (i=0; ; i++)
+ {
+ CLISTEVENT *pev;
+
+ pev = (CLISTEVENT*) CallService(MS_CLIST_GETEVENT,(WPARAM)INVALID_HANDLE_VALUE,i);
+ if (!pev)
+ break;
+
+ if ((ULONG)pev->lParam == p->uid && !pev->hContact
+ && pev->pszService && !strcmp(pev->pszService, MODULENAME"/OpenTriggeredReminder"))
+ {
+ if ( !CallService(MS_CLIST_REMOVEEVENT,(WPARAM)pev->hContact,(LPARAM)pev->hDbEvent) )
+ {
+ p->SystemEventQueued = FALSE;
+ if (QueuedReminderCount)
+ QueuedReminderCount--;
+ }
+ break;
+ }
+ }
+ }
+}
+
+void PurgeReminders(void)
+{
+ int ReminderCount,I;
+ char ValueName[32];
+
+ ReminderCount = ReadSettingInt(0,MODULENAME,"RemindersData",0);
+ for(I = 0;I < ReminderCount;I++)
+ {
+ sprintf(ValueName, "RemindersData%d", I);
+ DeleteSetting(0,MODULENAME,ValueName);
+ }
+}
+
+void JustSaveReminders(void)
+{
+ TREEELEMENT *TTE;
+ int I, n, l;
+ char *tmpReminder = NULL,*Value;
+ char ValueName[32];
+ int ReminderCount;
+ REMINDERDATA *pReminder;
+
+ const int OldReminderCount = ReadSettingInt(0, MODULENAME, "RemindersData", 0);
+
+ ReminderCount = TreeGetCount(RemindersList);
+
+ WriteSettingInt(0,MODULENAME, "RemindersData", ReminderCount);
+
+ for (TTE = RemindersList, I = 0; TTE; TTE = (TREEELEMENT*)TTE->next, I++)
+ {
+ pReminder = (REMINDERDATA*)TTE->ptrdata;
+ if (pReminder->Reminder && strlen(pReminder->Reminder))
+ tmpReminder = pReminder->Reminder;
+ else
+ tmpReminder = NULL;
+
+ if (!tmpReminder)
+ tmpReminder = "";
+
+ Value = (char*)malloc(strlen(tmpReminder) + 512);
+
+ if (!Value)
+ continue;
+
+ n = 0;
+
+ // 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;
+
+ // sound repeat
+ if (pReminder->RepeatSound)
+ {
+ l = sprintf(Value+n, "\033""%u:%u", DATATAG_SNDREPEAT, pReminder->RepeatSound);
+ if (l > 0) n += l;
+ }
+
+ // sound
+ if (pReminder->SoundSel)
+ {
+ l = sprintf(Value+n, "\033""%u:%d", DATATAG_SNDSEL, pReminder->SoundSel);
+ if (l > 0) n += l;
+ }
+
+ // 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;
+ ValueName[0xffff] = 0;
+ }
+
+ sprintf(ValueName, "RemindersData%d", ReminderCount - I - 1); // do not want to reverse in DB
+
+ WriteSettingBlob(0, MODULENAME, ValueName, (WORD)(n+1), Value);
+
+ SAFE_FREE((void**)&Value);
+ }
+
+ // delete any left over DB reminder entries
+ for(; I < OldReminderCount; I++)
+ {
+ sprintf(ValueName, "RemindersData%d", I);
+ DBDeleteContactSetting(0,MODULENAME,ValueName);
+ }
+}
+
+void LoadReminders(void)
+{
+ int I,RemindersCount;
+ char *Value;
+ WORD Size;
+ char ValueName[32];
+ BOOL GenerateUids = FALSE;
+
+ RemindersList = NULL;
+ RemindersCount = ReadSettingInt(0, MODULENAME, "RemindersData", 0);
+
+ for (I = 0; I < RemindersCount; I++)
+ {
+ Size = 65535;
+ Value = NULL;
+ sprintf(ValueName, "RemindersData%d", I);
+
+ ReadSettingBlob(0, MODULENAME, ValueName, &Size, (void**)&Value);
+
+ if (Size && Value) // was the blob found
+ {
+ REMINDERDATA rem = {0};
+ char *TVal;
+ REMINDERDATA *TempRem;
+ char *DelPos = strchr(Value, 0x1B);
+
+ // ensure that read data is null-terminated
+ Value[(UINT)Size-1] = 0;
+
+ if (Value[0] == 'X')
+ {
+ // new eXtended/fleXible data format
+
+ if (DelPos)
+ *DelPos = 0;
+
+ // uid:when
+
+ TVal = strchr(Value+1, ':');
+ if (!TVal || (DelPos && TVal > DelPos))
+ continue;
+ *TVal++ = 0;
+
+ rem.uid = strtoul(Value+1, NULL, 10);
+ rem.When.QuadPart = _strtoui64(TVal, NULL, 16) * FILETIME_TICKS_PER_SEC;
+
+ // optional \033 separated params
+ while (DelPos)
+ {
+ char *sep;
+ UINT tag;
+
+ TVal = DelPos + 1;
+ // find param end and make sure it's null-terminated (if end of data then it's already null-terminated)
+ DelPos = strchr(TVal, 0x1B);
+ if (DelPos)
+ *DelPos = 0;
+
+ // tag:<data>
+
+ sep = strchr(TVal, ':');
+ if (!sep || (DelPos && sep > DelPos))
+ goto skip;
+
+ tag = strtoul(TVal, NULL, 10);
+ TVal = sep + 1;
+
+ switch (tag)
+ {
+ case DATATAG_TEXT:
+ rem.Reminder = _strdup(TVal);
+ break;
+
+ case DATATAG_SNDREPEAT:
+ rem.RepeatSound = strtoul(TVal, NULL, 10);
+ break;
+
+ case DATATAG_SNDSEL:
+ rem.SoundSel = strtol(TVal, NULL, 10);
+ if (rem.SoundSel > 2) rem.SoundSel = 2;
+ break;
+ }
+ }
+
+ if (rem.SoundSel < 0)
+ rem.RepeatSound = 0;
+ if (!rem.Reminder)
+ rem.Reminder = _strdup("");
+ }
+ else
+ {
+ // old format (for DB backward compatibility)
+
+ if (!DelPos)
+ continue;
+
+ DelPos[0] = 0;
+ // convert time_t to (local) FILETIME
+ {
+ SYSTEMTIME tm;
+ struct tm *stm;
+ time_t tt;
+
+ tt = (time_t)strtoul(Value, NULL, 10);
+ stm = localtime(&tt);
+ tm.wDayOfWeek = 0;
+ tm.wSecond = 0;
+ tm.wMilliseconds = 0;
+ tm.wHour = stm->tm_hour;
+ tm.wMinute = stm->tm_min;
+ tm.wSecond = stm->tm_sec;
+ tm.wYear = stm->tm_year + 1900;
+ tm.wMonth = stm->tm_mon + 1;
+ tm.wDay = stm->tm_mday;
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&rem.When);
+ }
+ TVal = DelPos + 1;
+ rem.Reminder = _strdup(TVal);
+ }
+
+ // queue uid generation if invalid uid is present
+ if (!rem.uid)
+ GenerateUids = TRUE;
+
+ TempRem = (REMINDERDATA*)malloc(sizeof(REMINDERDATA));
+ if (TempRem)
+ {
+ *TempRem = rem;
+ TreeAddSorted(&RemindersList, TempRem, ReminderSortCb);
+ }
+ else if (rem.Reminder)
+ {
+ free(rem.Reminder);
+ }
+skip:;
+ }
+
+ FreeSettingBlob(Size, Value);
+ }
+
+ // generate UIDs if there are any items with an invalid UID
+ if (GenerateUids && RemindersList)
+ {
+ TREEELEMENT *TTE;
+
+ TTE = RemindersList;
+ while (TTE)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ if (!pReminder->uid)
+ pReminder->uid = CreateUid();
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ JustSaveReminders();
+ }
+}
+
+
+/*void EscapeString(LPCSTR lpszSrc, char *s, int maxLen)
+{
+ maxLen -= 3;
+
+ *s++ = '"';
+
+ while (*lpszSrc && maxLen > 1)
+ {
+ switch (*lpszSrc)
+ {
+ case '\r': *s++ = '\\'; *s++ = 'r'; break;
+ case '\n': *s++ = '\\'; *s++ = 'n'; break;
+ case '"': *s++ = '\\'; *s++ = '"'; break;
+ case '\t': *s++ = '\\'; *s++ = 't'; break;
+ case '\\': *s++ = '\\'; *s++ = '\\'; break;
+ default:
+ *s++ = *lpszSrc;
+ }
+
+ lpszSrc++;
+ maxLen--;
+ }
+
+ *s++ = '"';
+ *s = 0;
+}
+
+void ExportReminders()
+{
+ LPCSTR lpsz;
+ TREEELEMENT *TTE;
+ char s[MAX_REMINDER_LEN+512];
+
+ if (!RemindersList)
+ return NULL;
+
+ // CSV header
+ lpsz = "TimeUTC,SoundSel,SoundRepeat,Description";
+ WriteFile(hFile, lpsz, strlen(lpsz), NULL, NULL);
+
+ TTE = RemindersList;
+ while (TTE)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ sprintf(s, "%I64u,%d,%d,", (pReminder->When.QuadPart-(ULONGLONG)116444736000000000)/FILETIME_TICKS_PER_SEC, pReminder->SoundSel, pReminder->RepeatSound);
+ WriteFile(hFile, s, strlen(s), NULL, NULL);
+
+ if (pReminder->Reminder)
+ {
+ EscapeString(pReminder->Reminder, s, sizeof(s));
+ WriteFile(hFile, s, strlen(s), NULL, NULL);
+ }
+
+ WriteFile(hFile, (LPCVOID)"\r\n", 2, NULL, NULL);
+
+ TTE = TTE->next;
+ }
+
+ return NULL;
+}*/
+
+
+void NewReminder(void)
+{
+ if (!NewReminderVisible)
+ {
+ NewReminderVisible = TRUE;
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_ADDREMINDER), 0, DlgProcNewReminder);
+ }
+}
+
+void EditReminder(REMINDERDATA *p)
+{
+ if (!p)
+ return;
+
+ if (!NewReminderVisible && !p->SystemEventQueued)
+ {
+ if (!p->RemVisible)
+ {
+ p->RemVisible = TRUE;
+ NewReminderVisible = 2;
+ pEditReminder = p;
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_ADDREMINDER), 0, DlgProcNewReminder);
+ }
+ else
+ {
+ BringWindowToTop(p->handle);
+ }
+ }
+}
+
+static void DeleteReminder(REMINDERDATA *p)
+{
+ if (!p)
+ return;
+
+ if (p->SystemEventQueued)
+ {
+ // remove pending system event
+ RemoveReminderSystemEvent(p);
+ }
+
+ TreeDelete(&RemindersList, p);
+ SAFE_FREE((void**)&p->Reminder);
+ SAFE_FREE((void**)&p);
+}
+
+void CloseReminderList()
+{
+ if (ListReminderVisible)
+ {
+ DestroyWindow(LV);
+ ListReminderVisible = FALSE;
+ }
+}
+
+static void PurgeReminderTree()
+{
+ REMINDERDATA *pt;
+
+ while (RemindersList) // empty whole tree
+ {
+ pt = (REMINDERDATA*)RemindersList->ptrdata;
+ if (pt->handle) DestroyWindow(pt->handle);
+ DeleteReminder(pt);
+ }
+ RemindersList = NULL;
+}
+
+void SaveReminders(void)
+{
+ JustSaveReminders();
+ PurgeReminderTree();
+}
+
+void DeleteReminders(void)
+{
+ PurgeReminders();
+ WriteSettingInt(0,MODULENAME,"RemindersData",0);
+ PurgeReminderTree();
+}
+
+void ListReminders(void)
+{
+ if (!ListReminderVisible)
+ {
+ CreateDialog(hinstance, MAKEINTRESOURCE(IDD_LISTREMINDERS), 0, DlgProcViewReminders);
+ ListReminderVisible = TRUE;
+ }
+ else
+ {
+ BringWindowToTop(LV);
+ }
+}
+
+
+void GetTriggerTimeString(const ULARGE_INTEGER *When, char *s, UINT strSize, BOOL bUtc)
+{
+ SYSTEMTIME tm;
+ LCID lc = GetUserDefaultLCID();
+
+ *s = 0;
+
+ memset(&tm, 0, sizeof(tm));
+ if (bUtc)
+ FileTimeToTzLocalST((const FILETIME*)When, &tm);
+ else
+ FILETIMEtoSYSTEMTIME((FILETIME*)When, &tm);
+
+ if ( GetDateFormat(lc, DATE_LONGDATE, &tm, NULL, s, strSize) )
+ {
+ // append time
+ {
+ int n = strlen(s);
+ s[n++] = ' ';
+ s[n] = 0;
+
+ if ( !GetTimeFormat(lc, LOCALE_NOUSEROVERRIDE|TIME_NOSECONDS, &tm, NULL, s+n, strSize-n) )
+ {
+ mir_snprintf(s+n, strSize-n, "%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);
+ }
+}
+
+
+INT_PTR OpenTriggeredReminder(WPARAM w, LPARAM l)
+{
+ if (!l)
+ return 0;
+
+ l = ((CLISTEVENT*)l)->lParam;
+
+ REMINDERDATA *pReminder = (REMINDERDATA*)FindReminder((DWORD)l);
+ if (!pReminder || !pReminder->SystemEventQueued)
+ return 0;
+
+ pReminder->SystemEventQueued = FALSE;
+ if (QueuedReminderCount)
+ QueuedReminderCount--;
+
+ {
+ char S[MAX_PATH];
+ char S1[128];
+ HWND H;
+ GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE);
+
+ pReminder->RemVisible = TRUE;
+
+ pReminder->handle = H = CreateDialog(hinstance, MAKEINTRESOURCE(IDD_NOTIFYREMINDER), 0, DlgProcNotifyReminder);
+
+ sprintf(S, "%s! - %s", Translate("Reminder"), S1);
+ SetWindowText(H, S);
+
+ if (pReminder->Reminder)
+ SetDlgItemText(H, IDC_REMDATA, pReminder->Reminder);
+
+ BringWindowToTop(H);
+ }
+
+ return 0;
+}
+
+static void SkinPlaySoundPoly(LPCSTR pszSoundName)
+{
+ if (g_UseDefaultPlaySound)
+ {
+ SkinPlaySound(pszSoundName);
+ return;
+ }
+
+ if (DBGetContactSettingByte(NULL, "SkinSoundsOff", pszSoundName, 0)==0) {
+ DBVARIANT dbv;
+
+ if (DBGetContactSettingString(NULL, "SkinSounds", pszSoundName, &dbv)==0) {
+ char szFull[MAX_PATH];
+
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM)dbv.pszVal, (LPARAM)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, sizeof(s), "play \"%s\"", szShort);
+ mciSendStringA(s, NULL, 0, NULL);
+ }
+
+ DBFreeVariant(&dbv);
+ }
+ }
+}
+
+static void UpdateReminderEvent(REMINDERDATA *pReminder, UINT nElapsedSeconds, BOOL *pHasPlayedSound)
+{
+ DWORD dwSoundMask;
+
+ if (pReminder->RepeatSound)
+ {
+ if (nElapsedSeconds >= pReminder->RepeatSoundTTL)
+ {
+ pReminder->RepeatSoundTTL = pReminder->RepeatSound;
+
+ dwSoundMask = 1 << pReminder->SoundSel;
+
+ if ( !(*pHasPlayedSound & dwSoundMask) )
+ {
+ switch (pReminder->SoundSel)
+ {
+ case 1: SkinPlaySoundPoly("AlertReminder2"); break;
+ case 2: SkinPlaySoundPoly("AlertReminder3"); break;
+ default:
+ SkinPlaySoundPoly("AlertReminder");
+ }
+
+ *pHasPlayedSound |= dwSoundMask;
+ }
+ }
+ else
+ {
+ pReminder->RepeatSoundTTL -= nElapsedSeconds;
+ }
+ }
+}
+
+static void FireReminder(REMINDERDATA *pReminder, BOOL *pHasPlayedSound)
+{
+ DWORD dwSoundMask;
+
+ if (pReminder->SystemEventQueued)
+ return;
+
+ // add a system event
+ {
+ CLISTEVENT ev = { 0 };
+
+ ev.cbSize = sizeof(ev);
+ ev.hIcon = g_hReminderIcon;
+ ev.flags = CLEF_URGENT;
+ ev.lParam = (LPARAM)pReminder->uid;
+ ev.pszService = MODULENAME"/OpenTriggeredReminder";
+ ev.pszTooltip = Translate("Reminder");
+
+ CallService(MS_CLIST_ADDEVENT,0,(LPARAM)&ev);
+ }
+
+ pReminder->SystemEventQueued = TRUE;
+ QueuedReminderCount++;
+
+ if (pReminder->SoundSel < 0)
+ {
+ // sound disabled
+ return;
+ }
+
+ dwSoundMask = 1 << pReminder->SoundSel;
+
+ pReminder->RepeatSoundTTL = pReminder->RepeatSound;
+
+ if ( !(*pHasPlayedSound & dwSoundMask) )
+ {
+ switch (pReminder->SoundSel)
+ {
+ case 1: SkinPlaySoundPoly("AlertReminder2"); break;
+ case 2: SkinPlaySoundPoly("AlertReminder3"); break;
+ default:
+ SkinPlaySoundPoly("AlertReminder");
+ }
+
+ *pHasPlayedSound |= dwSoundMask;
+ }
+}
+
+
+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
+
+ TREEELEMENT *TTE;
+ ULARGE_INTEGER curT;
+ BOOL bHasPlayedSound;
+ BOOL bResult;
+ BOOL bHasQueuedReminders;
+
+ if (!RemindersList)
+ return FALSE;
+
+ {
+ 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;
+ }
+
+ bResult = FALSE;
+
+ // var used to avoid playing multiple alarm sounds during a single update
+ bHasPlayedSound = FALSE;
+
+ // 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
+ bHasQueuedReminders = (QueuedReminderCount != 0);
+
+ // allthough count should always be correct, it's fool proof to just count them again in the loop below
+ QueuedReminderCount = 0;
+
+ TTE = RemindersList;
+ while (TTE && (bHasQueuedReminders || ((REMINDERDATA*)TTE->ptrdata)->When.QuadPart <= curT.QuadPart))
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ if (!pReminder->RemVisible)
+ {
+ if (pReminder->SystemEventQueued)
+ {
+ UpdateReminderEvent(pReminder, REMINDER_UPDATE_INTERVAL_SHORT/1000, &bHasPlayedSound);
+
+ QueuedReminderCount++;
+ bResult = TRUE;
+ }
+ else if (((REMINDERDATA*)TTE->ptrdata)->When.QuadPart <= curT.QuadPart)
+ {
+ if (!g_RemindSMS)
+ {
+ FireReminder(pReminder, &bHasPlayedSound);
+
+ if (pReminder->SystemEventQueued)
+ bResult = TRUE;
+ }
+ else
+ {
+ char* S2 = strchr(g_RemindSMS, '@');
+ char* S1 = (char*)malloc(S2 - g_RemindSMS);
+
+ strncpy(S1, g_RemindSMS, S2 - g_RemindSMS);
+ S1[S2 - g_RemindSMS]= 0x0;
+ S2++;
+ Send(S1, S2, pReminder->Reminder ? pReminder->Reminder : "", NULL);
+ SAFE_FREE((void**)&S1);
+ DeleteReminder(pReminder);
+ JustSaveReminders();
+ NOTIFY_LIST();
+ }
+ }
+ }
+
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ return bResult;
+}
+
+
+static LRESULT CALLBACK DatePickerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_RBUTTONDBLCLK:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_CHAR:
+ case WM_INITMENUPOPUP:
+ case WM_PASTE:
+ return TRUE;
+ case WM_SYSKEYUP:
+ case WM_SYSKEYDOWN:
+ case WM_SYSCHAR:
+ return FALSE;
+ }
+
+ return CallWindowProc((WNDPROC)GetProp(hWnd, TEXT("OldWndProc")), hWnd, message, wParam, lParam);
+}
+
+static void InitDatePicker(HWND Dialog, UINT nIDDate)
+{
+ // subclass date picker to prevent user editing (should only use the dropdown calender to ensure valid dates)
+ WNDPROC pOldWndProc;
+ HWND hCtrl = GetDlgItem(Dialog, nIDDate);
+
+ // tweak style of picker
+ if ( IsWinVerVistaPlus() )
+ {
+ DWORD dw = SendDlgItemMessage(Dialog,nIDDate,DTM_GETMCSTYLE,0,0);
+ dw |= MCS_WEEKNUMBERS | MCS_NOSELCHANGEONNAV;
+ SendDlgItemMessage(Dialog,nIDDate,DTM_SETMCSTYLE,0,dw);
+ }
+
+#ifdef _WIN64
+ pOldWndProc = (WNDPROC)SetWindowLongPtr(hCtrl, GWLP_WNDPROC, (LONG_PTR)DatePickerWndProc);
+#else
+ pOldWndProc = (WNDPROC)SetWindowLong(hCtrl, GWL_WNDPROC, (LONG)DatePickerWndProc);
+#endif
+
+ SetProp(hCtrl, TEXT("OldWndProc"), pOldWndProc);
+}
+
+static BOOL ParseTime(LPCSTR s, int *hout, int *mout, BOOL bTimeOffset, BOOL bAllowOffsetOverride)
+{
+ // validate format: <WS><digit>[<digit>]<WS>[':'<WS><digit>[<digit>]]<WS>[p | P].*
+
+ // if bTimeOffset is FALSE the user may still enter a time offset by using + as the first char, the
+ // delta time will be returned in minutes (even > 60) and hout will always be -1
+
+ // if bTimeOffset is TRUE time is always interpreted as an offset (ignores PM indicator and defaults to minutes)
+
+ int h, m;
+ BOOL bOffset = bTimeOffset;
+
+ // read hour
+
+ while ( iswspace(*s) ) s++;
+
+ if (*s == '+')
+ {
+ if (!bTimeOffset)
+ {
+ if (!bAllowOffsetOverride)
+ return FALSE;
+
+ // treat value as an offset anyway
+ bOffset = TRUE;
+ }
+
+ s++;
+ while ( iswspace(*s) ) s++;
+ }
+
+ if ( !isdigit(*s) )
+ return FALSE;
+ h = (int)(*s-'0');
+ s++;
+
+ if (!bOffset)
+ {
+ if ( isdigit(*s) )
+ {
+ h = h * 10 + (int)(*s-'0');
+ s++;
+ }
+
+ if ( isdigit(*s) )
+ return FALSE;
+ }
+ else
+ {
+ // allow more than 2-digit numbers for offset
+ while ( isdigit(*s) )
+ {
+ h = h * 10 + (int)(*s-'0');
+ s++;
+ }
+ }
+
+ // find : separator
+
+ while ( iswspace(*s) ) s++;
+
+ if (*s == ':')
+ {
+ s++;
+
+ // read minutes
+
+ while ( iswspace(*s) ) s++;
+
+ if ( !isdigit(*s) )
+ return FALSE;
+ m = (int)(*s-'0');
+ s++;
+
+ if ( isdigit(*s) )
+ {
+ m = m * 10 + (int)(*s-'0');
+ s++;
+ }
+ }
+ else
+ {
+ if (bOffset)
+ {
+ // no : separator found, interpret the entered number as minutes and allow > 60
+
+ if (h < 0)
+ return FALSE;
+
+ if (bTimeOffset)
+ {
+ *hout = h / 60;
+ *mout = h % 60;
+ }
+ else
+ {
+ *mout = h;
+ *hout = -1;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ m = 0;
+ }
+ }
+
+ // validate time
+ if (bOffset)
+ {
+ if (h < 0)
+ return FALSE;
+ if (m < 0 || m > 59)
+ return FALSE;
+ }
+ else
+ {
+ if (h == 24)
+ h = 0;
+ else if (h < 0 || h > 23)
+ return FALSE;
+ if (m < 0 || m > 59)
+ return FALSE;
+ }
+
+ if (!bOffset)
+ {
+ // check for PM indicator (not strict, only checks for P char)
+
+ while ( iswspace(*s) ) s++;
+
+ if (*s == 'p' || *s == 'P')
+ {
+ if (h < 13)
+ h += 12;
+ else if (h == 12)
+ h = 0;
+ }
+ }
+ else if (!bTimeOffset)
+ {
+ // entered time is an offset
+
+ *mout = h * 60 + m;
+ *hout = -1;
+
+ return TRUE;
+ }
+
+ *hout = h;
+ *mout = m;
+
+ return TRUE;
+}
+
+// returns TRUE if combo box list displays time offsets ("23:34 (5 Minutes)" etc.)
+__inline static BOOL IsRelativeCombo(HWND Dialog, UINT nIDTime)
+{
+ return (int)SendDlgItemMessage(Dialog,nIDTime,CB_GETITEMDATA,0,0) >= 0;
+}
+
+static void PopulateTimeCombo(HWND Dialog, 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];
+ 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);
+
+ // ensure that we start on midnight local time
+ SystemTimeToTzSpecificLocalTime(NULL, (SYSTEMTIME*)tmUtc, &tm2);
+ tm2.wHour = 0;
+ tm2.wMinute = 0;
+ tm2.wSecond = 0;
+ tm2.wMilliseconds = 0;
+ TzSpecificLocalTimeToSystemTime(NULL, &tm2, &tm2);
+ SYSTEMTIMEtoFILETIME(&tm2, (FILETIME*)&li);
+
+ // from 00:00 to 23:30 in 30 minute steps
+ for (i=0; i<50; i++)
+ {
+ const int h = i>>1;
+ const int m = (i&1) ? 30 : 0;
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ sprintf(s, "%02d:%02d", (UINT)tm2.wHour, (UINT)tm2.wMinute);
+ n = SendDlgItemMessage(Dialog,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));
+
+ li.QuadPart += (ULONGLONG)30 * MinutesToFileTime;
+
+ if (tm2.wHour == 23 && tm2.wMinute >= 30)
+ break;
+ }
+
+ return;
+ }
+
+ //
+
+ SendDlgItemMessage(Dialog, nIDTime, CB_RESETCONTENT, 0, 0);
+
+ lpszMinutes = Translate("Minutes");
+ lpszHours = Translate("Hours");
+
+ SYSTEMTIMEtoFILETIME(tmUtc, (FILETIME*)&li);
+ 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, sizeof(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));
+
+ // 5 minutes
+ li.QuadPart += (ULONGLONG)5 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(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));
+
+ // 10 minutes
+ li.QuadPart += (ULONGLONG)5 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(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));
+
+ // 15 minutes
+ li.QuadPart += (ULONGLONG)5 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(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));
+
+ // 30 minutes
+ li.QuadPart += (ULONGLONG)15 * MinutesToFileTime;
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+ mir_snprintf(s, sizeof(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));
+
+ // 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++)
+ {
+ UINT dt;
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm2);
+
+ if (i > 40)
+ {
+ UINT nLastEntry = ((UINT)wCurHour * 60 + (UINT)wCurMinute) / 30;
+ if (nLastEntry)
+ nLastEntry *= 30;
+ else
+ nLastEntry = 23*60 + 30;
+
+ if (((UINT)tm2.wHour * 60 + (UINT)tm2.wMinute) == nLastEntry)
+ break;
+ }
+
+ // icq-style display 1.0, 1.5 etc. hours even though that isn't accurate due to rounding
+ //mir_snprintf(s, sizeof(s), "%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, sizeof(s), "%02d:%02d (%d %s)", (UINT)tm2.wHour, (UINT)tm2.wMinute, dt, lpszMinutes);
+ else
+ mir_snprintf(s, sizeof(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, (LPARAM)(dt*60));
+
+ li.QuadPart += (ULONGLONG)30 * MinutesToFileTime;
+ }
+}
+
+static void PopulateTimeOffsetCombo(HWND Dialog, UINT nIDCombo)
+{
+ int i, n;
+ LPCSTR lpszMinutes;
+ LPCSTR lpszHour;
+ LPCSTR lpszHours;
+ LPCSTR lpszDay;
+ LPCSTR lpszDays;
+ LPCSTR lpszWeek;
+ char s[MAX_PATH];
+
+ SendDlgItemMessage(Dialog,nIDCombo,CB_RESETCONTENT,0,0);
+
+ lpszMinutes = Translate("Minutes");
+ lpszHour = Translate("Hour");
+ lpszHours = Translate("Hours");
+ lpszDay = Translate("Day");
+ lpszDays = Translate("Days");
+ lpszWeek = Translate("Week");
+
+ // 5 - 55 minutes (in 5 minute steps)
+ for (i = 1; i < 12; i++)
+ {
+ mir_snprintf(s, sizeof(s), "%d %s", i*5, lpszMinutes);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,n, (LPARAM)(i*5));
+ }
+
+ // 1 hour
+ mir_snprintf(s, sizeof(s), "1 %s", lpszHour);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,n, (LPARAM)60);
+
+ // 2, 4, 8 hours
+ for (i = 2; i <= 8; i+=2)
+ {
+ mir_snprintf(s, sizeof(s), "%d %s", i, lpszHours);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,n, (LPARAM)(i*60));
+ }
+
+ // 1 day
+ mir_snprintf(s, sizeof(s), "1 %s", lpszDay);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,n, (LPARAM)(24*60));
+
+ // 2-4 days
+ for (i = 2; i <= 4; i++)
+ {
+ mir_snprintf(s, sizeof(s), "%d %s", i, lpszDays);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,n, (LPARAM)(i*24*60));
+ }
+
+ // 1 week
+ mir_snprintf(s, sizeof(s), "1 %s", lpszWeek);
+ n = SendDlgItemMessage(Dialog,nIDCombo,CB_ADDSTRING,0,(LPARAM)s);
+ SendDlgItemMessage(Dialog,nIDCombo,CB_SETITEMDATA,n, (LPARAM)(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)
+{
+ 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) )
+ {
+ ULONGLONG ref;
+ ULARGE_INTEGER li;
+ SYSTEMTIME tm;
+
+ // get reference time (UTC) from hidden control
+ {
+ GetDlgItemText(Dialog, nIDRefTime, buf, 30);
+ li.QuadPart = ref = _strtoui64(buf, NULL, 16);
+ }
+
+ // clamp delta time to 23.5 hours (coule be issues otherwise as relative combo only handles <24)
+ if (m > (23*60+30))
+ m = 23*60+30;
+
+ li.QuadPart += (ULONGLONG)(m * 60) * FILETIME_TICKS_PER_SEC;
+
+ FileTimeToTzLocalST((FILETIME*)&li, &tm);
+ h = (int)tm.wHour;
+ m = (int)tm.wMinute;
+
+ if (triggerRelUtcOut)
+ *triggerRelUtcOut = li;
+
+ dt = (UINT)((li.QuadPart/MinutesToFileTime) - (ref/MinutesToFileTime));
+
+ if (dt < 60)
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d %s)", h, m, dt, Translate("Minutes"));
+ else
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d.%d %s)", h, m, dt/60, ((dt%60)*10)/60, Translate("Hours"));
+
+ // search for preset
+ n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf);
+ if (n != CB_ERR)
+ {
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, n, 0);
+ return 0;
+ }
+
+ SetDlgItemText(Dialog, nIDTime, buf);
+ }
+ else
+ {
+ // should never happen
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, 0, 0);
+ }
+
+ return 0;
+ }
+
+ //
+
+ sprintf(buf, "%02d:%02d", h, m);
+
+ // search for preset first
+ n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf);
+ if (n != CB_ERR)
+ {
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, n, 0);
+ return 0;
+ }
+
+ if ( IsRelativeCombo(Dialog, 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, NULL, 16);
+ }
+
+ 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);
+ }
+
+ // 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);
+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;
+ }
+
+output_result:
+ if (triggerRelUtcOut)
+ *triggerRelUtcOut = li;
+
+ dt = (UINT)((li.QuadPart/MinutesToFileTime) - (ref/MinutesToFileTime));
+
+ if (dt < 60)
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d %s)", h, m, dt, Translate("Minutes"));
+ else
+ mir_snprintf(buf, sizeof(buf), "%02d:%02d (%d.%d %s)", h, m, dt/60, ((dt%60)*10)/60, Translate("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;
+
+ TzLocalSTToFileTime(&Date, &ft);
+ FileTimeToTzLocalST(&ft, &Date);
+
+ if ((int)Date.wHour != h || (int)Date.wMinute != m)
+ {
+ sprintf(buf, "%02d:%02d", (UINT)Date.wHour, (UINT)Date.wMinute);
+
+ // search for preset again
+ n = SendDlgItemMessage(Dialog, nIDTime, CB_FINDSTRING, (WPARAM)-1, (LPARAM)buf);
+ if (n != CB_ERR)
+ {
+ SendDlgItemMessage(Dialog, nIDTime, CB_SETCURSEL, n, 0);
+ goto invalid_dst;
+ }
+
+ SetDlgItemText(Dialog, nIDTime, buf);
+
+ goto invalid_dst;
+ }
+ }
+
+ SetDlgItemText(Dialog, nIDTime, buf);
+
+ return 0;
+}
+
+static __inline int ReformatTimeInput(HWND Dialog, UINT nIDTime, UINT nIDRefTime, int h, int m, const SYSTEMTIME *pDateLocal)
+{
+ return ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDateLocal, NULL);
+}
+
+// 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)
+{
+ ULARGE_INTEGER li;
+ char buf[32];
+ int n;
+ int h, m;
+
+ // get reference (UTC) time from hidden control
+ {
+ GetDlgItemText(Dialog, nIDRefTime, buf, 30);
+ li.QuadPart = _strtoui64(buf, NULL, 16);
+ }
+
+ if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR)
+ {
+ // use preset value
+preset_value:;
+ if ( IsRelativeCombo(Dialog, nIDTime) )
+ {
+ // time offset from ref time ("24:43 (5 Minutes)" etc.)
+
+ UINT nDeltaSeconds = (UINT)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, n, 0);
+ li.QuadPart += (ULONGLONG)nDeltaSeconds * FILETIME_TICKS_PER_SEC;
+
+ 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
+ if (nDeltaSeconds >= 10*60)
+ {
+ pDate->wSecond = 0;
+ pDate->wMilliseconds = 0;
+ }
+ }
+ else
+ {
+ // absolute time (offset from midnight on pDate)
+
+ UINT nDeltaSeconds = (UINT)((ULONG)SendDlgItemMessage(Dialog, nIDTime, CB_GETITEMDATA, n, 0) & ~0x80000000);
+ pDate->wHour = 0;
+ pDate->wMinute = 0;
+ pDate->wSecond = 0;
+ pDate->wMilliseconds = 0;
+ TzLocalSTToFileTime(pDate, (FILETIME*)&li);
+ li.QuadPart += (ULONGLONG)nDeltaSeconds * FILETIME_TICKS_PER_SEC;
+
+ 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);
+ return FALSE;
+ }
+
+ if ( IsRelativeCombo(Dialog, 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) )
+ return FALSE;
+
+ // check if reformatted value is a preset
+ if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR)
+ goto preset_value;
+
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li2, pDate);
+
+ return TRUE;
+ }
+ else
+ {
+ if ( ReformatTimeInputEx(Dialog, nIDTime, nIDRefTime, h, m, pDate, NULL) )
+ return FALSE;
+
+ // check if reformatted value is a preset
+ if ((n = SendDlgItemMessage(Dialog, nIDTime, CB_GETCURSEL, 0, 0)) != CB_ERR)
+ goto preset_value;
+ }
+
+ // absolute time (on pDate)
+
+ pDate->wHour = h;
+ pDate->wMinute = m;
+ pDate->wSecond = 0;
+ pDate->wMilliseconds = 0;
+
+ TzLocalSTToFileTime(pDate, (FILETIME*)&li);
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, pDate);
+
+ return TRUE;
+}
+
+static void OnDateChanged(HWND Dialog, UINT nDateID, UINT nTimeID, UINT nRefTimeID)
+{
+ // repopulate time combo list with regular times (not offsets like "23:32 (5 minutes)" etc.)
+
+ 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);
+
+ TzSpecificLocalTimeToSystemTime(NULL, &Date, &DateUtc);
+ PopulateTimeCombo(Dialog, nTimeID, FALSE, &DateUtc);
+
+ if (h < 0)
+ {
+ // parsing failed, default to current time
+ SYSTEMTIME tm;
+ GetLocalTime(&tm);
+ h = (UINT)tm.wHour;
+ m = (UINT)tm.wMinute;
+ }
+
+ ReformatTimeInput(Dialog, nTimeID, nRefTimeID, h, m, &Date);
+}
+
+
+INT_PTR CALLBACK DlgProcNotifyReminder(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ int I;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+ {
+ SYSTEMTIME tm;
+ ULARGE_INTEGER li;
+
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li);
+
+ TranslateDialogDefault(Dialog);
+
+ // 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];
+ sprintf(s, "%I64x", li.QuadPart);
+ SetDlgItemText(Dialog, IDC_REFTIME, s);
+ }
+
+ BringWindowToTop(Dialog);
+
+ PopulateTimeOffsetCombo(Dialog, 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);
+ SendDlgItemMessage(Dialog,IDC_AFTER,BM_SETCHECK,1,0);
+ SendDlgItemMessage(Dialog,IDC_ONDATE,BM_SETCHECK,0,0);
+ SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_SETCURSEL,0,0);
+
+ SendDlgItemMessage(Dialog, IDC_REMDATA, EM_LIMITTEXT, MAX_REMINDER_LEN, 0);
+
+ PopulateTimeCombo(Dialog, 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(Dialog,IDC_TIMEAGAIN,CB_SETCURSEL,0,0);
+
+ return TRUE;
+ }
+ case WM_NCDESTROY:
+ RemoveProp(GetDlgItem(Dialog, IDC_DATEAGAIN), TEXT("OldWndProc"));
+ return TRUE;
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_DATEAGAIN)
+ {
+ NMLISTVIEW *NM = (NMLISTVIEW*)lParam;
+
+ switch (NM->hdr.code)
+ {
+ case DTN_DATETIMECHANGE:
+ OnDateChanged(Dialog, IDC_DATEAGAIN, IDC_TIMEAGAIN, IDC_REFTIME);
+ break;
+ }
+ }
+ }
+ break;
+ case WM_CLOSE:
+ {
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (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;
+ 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);
+ }
+ }
+ 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");
+ sprintf(buf, "%d:%02d %s", h, m, lpszHours);
+ }
+ else
+ {
+ LPCSTR lpszMinutes = Translate("Minutes");
+ sprintf(buf, "%d %s", m, lpszMinutes);
+ }
+ SetDlgItemText(Dialog, IDC_REMINDAGAININ, buf);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_REMINDAGAININ,CB_SETCURSEL,0,0);
+ }
+ }
+ break;
+ }
+ break;
+
+ 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(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:
+ {
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (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;
+ }
+ case IDC_REMINDAGAIN:
+ {
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (I = 0; I < ReminderCount; I++)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I);
+
+ if (pReminder->handle == Dialog)
+ {
+ if (SendDlgItemMessage(Dialog,IDC_AFTER,BM_GETCHECK,0,0) == BST_CHECKED)
+ {
+ // delta time
+
+ ULONGLONG TT;
+ SYSTEMTIME tm;
+ ULARGE_INTEGER li;
+ int n;
+
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li);
+
+ 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, TranslateT("The specified time offset is invalid."), _T(SECTIONNAME), MB_OK|MB_ICONWARNING);
+ return TRUE;
+ }
+
+ TT = m * 60;
+ }
+
+ pReminder->When = li;
+ pReminder->When.QuadPart += (TT * FILETIME_TICKS_PER_SEC);
+ }
+ else if (SendDlgItemMessage(Dialog,IDC_ONDATE,BM_GETCHECK,0,0) == BST_CHECKED)
+ {
+ SYSTEMTIME Date;
+
+ SendDlgItemMessage(Dialog,IDC_DATEAGAIN,DTM_GETSYSTEMTIME,0,(LPARAM)&Date);
+
+ if ( !GetTriggerTime(Dialog, IDC_TIMEAGAIN, IDC_REFTIME, &Date) )
+ break;
+
+ SYSTEMTIMEtoFILETIME(&Date, (FILETIME*)&pReminder->When);
+ }
+
+ // update reminder text
+ {
+ char *ReminderText = NULL;
+ 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);
+ SendDlgItemMessage(Dialog,IDC_REMDATA,WM_GETTEXT,SzT+1,(LPARAM)ReminderText);
+ }
+ if (pReminder->Reminder)
+ free(pReminder->Reminder);
+ pReminder->Reminder = ReminderText;
+ }
+
+ pReminder->RemVisible = FALSE;
+ pReminder->handle = NULL;
+
+ // re-insert tree item sorted
+ TreeDelete(&RemindersList,pReminder);
+ TreeAddSorted(&RemindersList,pReminder,ReminderSortCb);
+ JustSaveReminders();
+ break;
+ }
+ }
+ NOTIFY_LIST();
+ DestroyWindow(Dialog);
+ return TRUE;
+ }
+ case IDC_NONE:
+ {// create note from remainder
+ int ReminderCount = TreeGetCount(RemindersList);
+ for (I = 0; I < ReminderCount; I++)
+ {
+ REMINDERDATA *pReminder = (REMINDERDATA*)TreeGetAt(RemindersList, I);
+
+ if (pReminder->handle == Dialog)
+ {
+ // get up-to-date reminder text
+ char *ReminderText = NULL;
+ 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);
+ SendDlgItemMessage(Dialog,IDC_REMDATA,WM_GETTEXT,SzT+1,(LPARAM)ReminderText);
+ }
+
+ SetFocus(NewNote(0, 0, -1, -1, ReminderText, 0, TRUE, TRUE, 0)->REHwnd);
+ break;
+ }
+ }
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+INT_PTR CALLBACK DlgProcNewReminder(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ HICON hIcon = NULL;
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+ {
+ ULARGE_INTEGER li;
+ SYSTEMTIME tm;
+
+ if (NewReminderVisible == 2)
+ {
+ // opening the edit reminder dialog (uses same dialog resource as add reminder)
+ SetWindowText(Dialog, _T("Reminder"));
+ SetDlgItemText(Dialog, IDC_ADDREMINDER, _T("&Update Reminder"));
+ ShowWindow(GetDlgItem(Dialog, IDC_VIEWREMINDERS), SW_HIDE);
+
+ li = pEditReminder->When;
+ FILETIMEtoSYSTEMTIME((FILETIME*)&li, &tm);
+ }
+ else
+ {
+ GetSystemTime(&tm);
+ SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&li);
+ }
+
+ TranslateDialogDefault(Dialog);
+
+ // 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];
+ sprintf(s, "%I64x", li.QuadPart);
+ SetDlgItemText(Dialog, IDC_REFTIME, s);
+ }
+
+ /*SendDlgItemMessage(Dialog,IDC_NONE,BM_SETCHECK,1,0);
+ SendDlgItemMessage(Dialog,IDC_DAILY,BM_SETCHECK,0,0);
+ SendDlgItemMessage(Dialog,IDC_WEEKLY,BM_SETCHECK,0,0);
+ SendDlgItemMessage(Dialog,IDC_MONTHLY,BM_SETCHECK,0,0);*/
+
+ PopulateTimeCombo(Dialog, 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(Dialog, IDC_REMINDER, EM_LIMITTEXT, MAX_REMINDER_LEN, 0);
+
+ if (NewReminderVisible == 2)
+ {
+ int n;
+ char s[32];
+ mir_snprintf(s, sizeof(s), "%02d:%02d", (UINT)tm.wHour, (UINT)tm.wMinute);
+
+ // search for preset first
+ n = SendDlgItemMessage(Dialog, IDC_TIME, CB_FINDSTRING, (WPARAM)-1, (LPARAM)s);
+ if (n != CB_ERR)
+ SendDlgItemMessage(Dialog, IDC_TIME, CB_SETCURSEL, n, 0);
+ else
+ SetDlgItemText(Dialog, IDC_TIME, s);
+
+ SetDlgItemText(Dialog, IDC_REMINDER, pEditReminder->Reminder);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_TIME,CB_SETCURSEL,0,0);
+ }
+
+ // populate sound repeat combo
+ {
+ char s[64];
+ int n;
+ LPCSTR lpszEvery = Translate("Every");
+ LPCSTR lpszSeconds = Translate("Seconds");
+
+ // NOTE: use multiples of REMINDER_UPDATE_INTERVAL_SHORT (currently 5 seconds)
+
+ n = SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_ADDSTRING,0,(LPARAM)Translate("Never"));
+ SendDlgItemMessage(Dialog,IDC_COMBO_REPEATSND,CB_SETITEMDATA, n, 0);
+
+ mir_snprintf(s, sizeof(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_snprintf(s, sizeof(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_snprintf(s, sizeof(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_snprintf(s, sizeof(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_snprintf(s, sizeof(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_snprintf(s, sizeof(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);
+
+ if (NewReminderVisible == 2 && pEditReminder->RepeatSound)
+ {
+ mir_snprintf(s, sizeof(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);
+ }
+ }
+
+ // 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, (LPARAM)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 n = pEditReminder->SoundSel<0 ? 3 : pEditReminder->SoundSel;
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETCURSEL,n,0);
+ }
+ else
+ {
+ SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETCURSEL,0,0);
+ }
+ }
+
+ hIcon = Skin_GetIconByHandle(hIconLibItem[12]);
+ SendDlgItemMessage(Dialog,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);
+ }
+
+ if (NewReminderVisible == 2)
+ SetFocus( GetDlgItem(Dialog, IDC_ADDREMINDER) );
+ else
+ SetFocus( GetDlgItem(Dialog, IDC_REMINDER) );
+
+ return FALSE;
+ }
+ case WM_NCDESTROY:
+ RemoveProp(GetDlgItem(Dialog, IDC_DATE), TEXT("OldWndProc"));
+ return TRUE;
+ case WM_CLOSE:
+ {
+ if (NewReminderVisible == 2)
+ {
+ pEditReminder->RemVisible = FALSE;
+ }
+ DestroyWindow(Dialog);
+ NewReminderVisible = FALSE;
+ pEditReminder = NULL;
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_DATE)
+ {
+ NMLISTVIEW *NM = (NMLISTVIEW*)lParam;
+
+ switch (NM->hdr.code)
+ {
+ case DTN_DATETIMECHANGE:
+ OnDateChanged(Dialog, 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);
+ }
+ }
+ 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);
+
+ EnableWindow(GetDlgItem(Dialog, IDC_BTN_PLAYSOUND), n>=0);
+ EnableWindow(GetDlgItem(Dialog, 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: SkinPlaySound("AlertReminder"); break;
+ case 1: SkinPlaySound("AlertReminder2"); break;
+ case 2: SkinPlaySound("AlertReminder3"); break;
+ }
+ }
+ return TRUE;
+ case IDC_CLOSE:
+ {
+ if (NewReminderVisible == 2)
+ {
+ pEditReminder->RemVisible = FALSE;
+ }
+ DestroyWindow(Dialog);
+ NewReminderVisible = FALSE;
+ pEditReminder = NULL;
+ return TRUE;
+ }
+ case IDC_VIEWREMINDERS:
+ {
+ ListReminders();
+ return TRUE;
+ }
+ case IDC_ADDREMINDER:
+ {
+ char *ReminderText = NULL;
+ int SzT;
+ SYSTEMTIME Date;
+ REMINDERDATA* TempRem;
+ int RepeatSound;
+
+ SendDlgItemMessage(Dialog,IDC_DATE,DTM_GETSYSTEMTIME,0,(LPARAM)&Date);
+
+ if ( !GetTriggerTime(Dialog, IDC_TIME, IDC_REFTIME, &Date) )
+ break;
+
+ 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;
+
+ SzT = SendDlgItemMessage(Dialog,IDC_REMINDER,WM_GETTEXTLENGTH,0,0);
+ if (SzT)
+ {
+ if (SzT > MAX_REMINDER_LEN) SzT = MAX_REMINDER_LEN;
+ ReminderText = (char*)malloc(SzT+1);
+ SendDlgItemMessage(Dialog,IDC_REMINDER,WM_GETTEXT,SzT+1,(LPARAM)ReminderText);
+ }
+
+ if (NewReminderVisible != 2)
+ {
+ // new reminder
+ 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
+
+ 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;
+
+ // re-insert tree item sorted
+ TreeDelete(&RemindersList,pEditReminder);
+ TreeAddSorted(&RemindersList,pEditReminder,ReminderSortCb);
+
+ pEditReminder->RemVisible = FALSE;
+ }
+ SetDlgItemText(Dialog,IDC_REMINDER,"");
+ JustSaveReminders();
+ NOTIFY_LIST();
+
+ if (g_CloseAfterAddReminder || NewReminderVisible == 2)
+ {
+ DestroyWindow(Dialog);
+ NewReminderVisible = FALSE;
+ pEditReminder = NULL;
+ }
+ }
+ }
+ }
+ case WM_DESTROY:
+ {
+ Skin_ReleaseIcon(hIcon);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static void InitListView(HWND AHLV)
+{
+ LV_ITEM lvTIt;
+ int I;
+ char *S;
+ char S1[128];
+ REMINDERDATA *pReminder;
+ TREEELEMENT *TTE;
+
+ ListView_SetHoverTime(AHLV,700);
+ ListView_SetExtendedListViewStyle(AHLV,LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_TRACKSELECT);
+ ListView_DeleteAllItems(AHLV);
+
+ I = 0;
+ TTE = RemindersList;
+ while (TTE)
+ {
+ pReminder = (REMINDERDATA*)TTE->ptrdata;
+
+ lvTIt.mask = LVIF_TEXT;
+
+ GetTriggerTimeString(&pReminder->When, S1, sizeof(S1), TRUE);
+
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 0;
+ lvTIt.pszText = S1;
+ lvTIt.cchTextMax = strlen(S1);
+ ListView_InsertItem(AHLV,&lvTIt);
+ lvTIt.mask = LVIF_TEXT;
+ S = GetPreviewString(pReminder->Reminder);
+ lvTIt.iItem = I;
+ lvTIt.iSubItem = 1;
+ lvTIt.pszText = S;
+ lvTIt.cchTextMax = strlen(S);
+ ListView_SetItem(AHLV,&lvTIt);
+
+ I++;
+ TTE = (TREEELEMENT*)TTE->next;
+ }
+
+ ListView_SetItemState(AHLV,0,LVIS_SELECTED,LVIS_SELECTED);
+}
+
+void OnListResize(HWND Dialog)
+{
+ HWND hList, hText, hBtnNew, hBtnClose;
+ RECT lr, cr, tr, clsr;
+ int th, btnh, btnw, MARGIN;
+ POINT org = {0};
+
+ hList = GetDlgItem(Dialog, IDC_LISTREMINDERS);
+ hText = GetDlgItem(Dialog, IDC_REMINDERDATA);
+ hBtnNew = GetDlgItem(Dialog, IDC_ADDNEWREMINDER);
+ hBtnClose = GetDlgItem(Dialog, IDC_CLOSE);
+
+ ClientToScreen(Dialog, &org);
+ GetClientRect(Dialog, &cr);
+
+ GetWindowRect(hList, &lr); OffsetRect(&lr, -org.x, -org.y);
+ GetWindowRect(hText, &tr); OffsetRect(&tr, -org.x, -org.y);
+ GetWindowRect(hBtnClose, &clsr); OffsetRect(&clsr, -org.x, -org.y);
+
+ MARGIN = lr.left;
+
+ th = tr.bottom - tr.top;
+ btnh = clsr.bottom - clsr.top;
+ btnw = clsr.right - clsr.left;
+
+ clsr.left = cr.right - MARGIN - btnw;
+ clsr.top = cr.bottom - MARGIN - btnh;
+
+ tr.left = MARGIN;
+ tr.right = cr.right - MARGIN;
+ tr.bottom = clsr.top - MARGIN - 2;
+ tr.top = tr.bottom - th;
+
+ lr.right = cr.right - MARGIN;
+ lr.bottom = tr.top - 5;
+
+ MoveWindow(hList, lr.left, lr.top, lr.right-lr.left, lr.bottom-lr.top, FALSE);
+ MoveWindow(hText, tr.left, tr.top, tr.right-tr.left, tr.bottom-tr.top, FALSE);
+
+ MoveWindow(hBtnClose, clsr.left, clsr.top, btnw, btnh, FALSE);
+ clsr.left -= btnw + 2;
+ MoveWindow(hBtnNew, clsr.left, clsr.top, btnw, btnh, FALSE);
+
+ RedrawWindow(Dialog, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
+ //UpdateWindow(Dialog);
+}
+
+void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols)
+{
+ if (geom)
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+
+ GetWindowPlacement(Dialog, &wp);
+
+ geom[0] = wp.rcNormalPosition.left;
+ geom[1] = wp.rcNormalPosition.top;
+ geom[2] = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+ geom[3] = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+ }
+
+ if (colgeom)
+ {
+ int i;
+ HWND H = GetDlgItem(Dialog, IDC_LISTREMINDERS);
+
+ for (i=0; i<nCols; i++)
+ {
+ colgeom[i] = ListView_GetColumnWidth(H, i);
+ }
+ }
+}
+
+static BOOL DoListContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam,REMINDERDATA *pReminder)
+{
+ HWND hwndListView = (HWND)wParam;
+ if (hwndListView != GetDlgItem(AhWnd,IDC_LISTREMINDERS)) return FALSE;
+ HMENU hMenuLoad = LoadMenu(hinstance,_T("MNU_REMINDERPOPUP"));
+ HMENU FhMenu = GetSubMenu(hMenuLoad,0);
+
+ MENUITEMINFO mii;
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DEFAULT;
+ if (!pReminder || pReminder->SystemEventQueued)
+ mii.fState |= MFS_GRAYED;
+ SetMenuItemInfo(FhMenu, ID_CONTEXTMENUREMINDERLISTVIEW_EDIT, FALSE, &mii);
+
+ if (!pReminder)
+ EnableMenuItem(FhMenu, IDM_DELETEREMINDER, MF_GRAYED|MF_BYCOMMAND);
+
+ CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
+ TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
+ DestroyMenu(hMenuLoad);
+ return TRUE;
+}
+
+INT_PTR CALLBACK DlgProcViewReminders(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
+{
+ LV_COLUMN lvCol;
+ NMLISTVIEW *NM;
+ char *S;
+ int I;
+
+ switch (Message)
+ {
+ case WM_SIZE:
+ {
+ OnListResize(Dialog);
+ UpdateGeomFromWnd(Dialog, g_reminderListGeom, NULL, 0);
+ break;
+ }
+ case WM_MOVE:
+ UpdateGeomFromWnd(Dialog, g_reminderListGeom, NULL, 0);
+ break;
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO *mm = (MINMAXINFO*)lParam;
+ mm->ptMinTrackSize.x = 394;
+ mm->ptMinTrackSize.y = 300;
+ }
+ return 0;
+ case WM_RELOAD:
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,_T(""));
+ InitListView(GetDlgItem(Dialog,IDC_LISTREMINDERS));
+ return TRUE;
+ }
+ case WM_CONTEXTMENU:
+ {
+ HWND H;
+ REMINDERDATA *pReminder = NULL;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ pReminder = (REMINDERDATA*)TreeGetAt(RemindersList,I);
+ }
+ }
+
+ if ( DoListContextMenu(Dialog, wParam, lParam, pReminder) )
+ return TRUE;
+ }
+ break;
+ case WM_INITDIALOG:
+ {
+ HICON hIcon = Skin_GetIconByHandle(hIconLibItem[6], ICON_SMALL);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
+ hIcon = Skin_GetIconByHandle(hIconLibItem[6], ICON_BIG);
+ SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);
+
+ TranslateDialogDefault(Dialog);
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,_T(""));
+ HWND H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+ S = Translate("Reminder text");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_reminderListColGeom[1];
+ ListView_InsertColumn(H,0,&lvCol);
+ lvCol.mask = LVCF_TEXT | LVCF_WIDTH;
+ S = Translate("Date of activation");
+ lvCol.pszText = S;
+ lvCol.cchTextMax = strlen(S);
+ lvCol.cx = g_reminderListColGeom[0];
+ ListView_InsertColumn(H,0,&lvCol);
+ InitListView(H);
+ SetWindowLong(GetDlgItem(H, 0), GWL_ID, IDC_LISTREMINDERS_HEADER);
+ LV = Dialog;
+
+ if (g_reminderListGeom[1] && g_reminderListGeom[2])
+ {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(Dialog, &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);
+ }
+ return TRUE;
+ }
+ case WM_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListReminderVisible = FALSE;
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ {
+ if (wParam == IDC_LISTREMINDERS)
+ {
+ NM = (NMLISTVIEW *)lParam;
+ switch (NM->hdr.code)
+ {
+ case LVN_ITEMCHANGED:
+ {
+ S = ((REMINDERDATA*)TreeGetAt(RemindersList,NM->iItem))->Reminder;
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,S);
+ }
+ break;
+ case NM_DBLCLK:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
+ }
+ }
+ }
+ break;
+ }
+ }
+ else if (wParam == IDC_LISTREMINDERS_HEADER)
+ {
+ NMHEADER *NM = (NMHEADER*)lParam;
+ switch (NM->hdr.code)
+ {
+ case HDN_ENDTRACK:
+ UpdateGeomFromWnd(Dialog, NULL, g_reminderListColGeom, SIZEOF(g_reminderListColGeom));
+ break;
+ }
+ }
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case ID_CONTEXTMENUREMINDERLISTVIEW_EDIT:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1)
+ {
+ EditReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
+ }
+ }
+ }
+ return TRUE;
+ case IDC_CLOSE:
+ {
+ DestroyWindow(Dialog);
+ ListReminderVisible = FALSE;
+ return TRUE;
+ }
+ case IDM_NEWREMINDER:
+ case IDC_ADDNEWREMINDER:
+ {
+ NewReminder();
+ return TRUE;
+ }
+ case IDM_DELETEALLREMINDERS:
+ if (RemindersList && MessageBox(Dialog, TranslateT("Are you sure you want to delete all reminders?"), _T(SECTIONNAME), MB_OKCANCEL) == IDOK)
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,_T(""));
+ DeleteReminders();
+ InitListView(GetDlgItem(Dialog,IDC_LISTREMINDERS));
+ }
+ return TRUE;
+ case IDM_DELETEREMINDER:
+ {
+ HWND H;
+
+ H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
+ if ( ListView_GetSelectedCount(H) )
+ {
+ I = ListView_GetSelectionMark(H);
+ if (I != -1
+ && MessageBox(Dialog, TranslateT("Are you sure you want to delete this reminder?"), _T(SECTIONNAME), MB_OKCANCEL) == IDOK)
+ {
+ SetDlgItemText(Dialog,IDC_REMINDERDATA,_T(""));
+ DeleteReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
+ JustSaveReminders();
+ InitListView(H);
+ }
+ }
+ return TRUE;
+ }
+ }
+ }
+ case WM_DESTROY:
+ Skin_ReleaseIcon((HICON)SendMessage(Dialog, WM_SETICON, ICON_BIG, 0));
+ Skin_ReleaseIcon((HICON)SendMessage(Dialog, WM_SETICON, ICON_SMALL, 0));
+ break;
+ }
+ return FALSE;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// 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 rlen;
+ if ((rlen = send(s,data,datalen,0)) == SOCKET_ERROR) return FALSE;
+ return TRUE;
+}
+
+unsigned long WS_ResolveName(char *name,WORD *port,int defaultPort)
+{
+ HOSTENT *lk;
+ char *pcolon,*nameCopy;
+ DWORD ret;
+ nameCopy=_strdup(name);
+ if(port != NULL) *port = defaultPort;
+ pcolon = strchr(nameCopy,':');
+ if(pcolon != NULL)
+ {
+ if(port != NULL) *port = atoi(pcolon+1);
+ *pcolon = 0;
+ }
+ if (inet_addr(nameCopy) == INADDR_NONE)
+ {
+ lk = gethostbyname(nameCopy);
+ if(lk == 0) return SOCKET_ERROR;
+ else {free(nameCopy); return *(u_long*)lk->h_addr_list[0];}
+ }
+ ret=inet_addr(nameCopy);
+ free(nameCopy);
+ return ret;
+}
+
+void Send(char *user, char *host, char *Msg, char *server)
+{
+ SOCKADDR_IN sockaddr;
+ WORD port;
+ char *ch = NULL;
+ 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(strlen(user) + 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,strlen(ch));
+ WS_Send(S,"data\r\n",6);
+ WS_Send(S,"From:<REM>\r\n\r\n",14);
+ WS_Send(S,Msg,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/resource.h b/plugins/NotesAndReminders/src/resource.h new file mode 100644 index 0000000000..0ddf8b4031 --- /dev/null +++ b/plugins/NotesAndReminders/src/resource.h @@ -0,0 +1,65 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#define IDD_STNOTEOPTIONS 101
+#define IDD_ADDREMINDER 102
+#define IDD_NOTIFYREMINDER 103
+#define IDD_LISTREMINDERS 104
+#define IDI_ADDREMINDER 110
+#define IDI_DELETEICON 111
+#define IDI_NOTEICON 112
+#define IDI_SHOWHIDE 113
+#define IDI_CAPTIONICON 114
+#define IDI_DELETEREMINDER 115
+#define IDI_VIEWREMINDERS 116
+#define IDI_CAPTIONICONNOTTOP 117
+#define IDI_HIDENOTE 118
+#define IDI_REMOVENOTE 119
+#define IDI_REMINDER 120
+#define IDI_BRINGFRONT 121
+#define IDI_PLAYSOUND 122
+#define IDI_VIEWNOTES 123
+
+#define IDC_COMBODATE 1021
+#define IDC_COMBOTIME 1022
+#define IDC_COMBOREMINDERTIME 1023
+#define IDC_REFTIME 1024
+#define IDC_STATIC_DATE 1025
+#define IDC_STATIC_TIME 1026
+#define IDC_COMBO_REPEATSND 1028
+#define IDC_COMBO_SOUND 1029
+#define IDC_BTN_PLAYSOUND 1030
+#define IDC_EDIT_ALTBROWSER 1031
+#define IDC_BTN_BROWSEBROWSER 1032
+#define ID_CONTEXTMENUNOTEPOPUP_NEWNOTE 40009
+#define ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP 40010
+#define ID_CONTEXTMENUNOTEPOPUP_APPEARANCE 40011
+#define ID_APPEARANCE_BACKGROUNDCOLOR 40013
+#define ID_APPEARANCE_TEXTCOLOR 40014
+#define ID_APPEARANCE_CUSTOMTEXT 40015
+#define ID_APPEARANCE_CUSTOMBG 40016
+#define ID_BACKGROUNDCOLOR_RESET 40017
+#define ID_TEXTCOLOR_RESET 40018
+#define ID_CONTEXTMENUNOTEPOPUP_PASTETITLE 40019
+#define ID_CONTEXTMENUNOTEPOPUP_RESETTITLE 40020
+#define ID_CONTEXTMENUREMINDERLISTVIEW_EDIT 40021
+#define ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE 40022
+#define ID_CONTEXTMENUNOTELISTVIEW_SHOW 40023
+#define ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES 40024
+#define ID_CONTEXTMENUNOTEPOPUP_VIEWNOTES 40025
+#define ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY 40026
+#define ID_APPEARANCE_FONT 40027
+#define ID_APPEARANCE_CUSTOMFONT 40028
+#define ID_FONT_RESET 40029
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 106
+#define _APS_NEXT_COMMAND_VALUE 40030
+#define _APS_NEXT_CONTROL_VALUE 1033
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
|