diff options
Diffstat (limited to 'plugins/NotesAndReminders/src')
| -rw-r--r-- | plugins/NotesAndReminders/src/globals.h | 147 | ||||
| -rw-r--r-- | plugins/NotesAndReminders/src/hotkeys.cpp | 137 | ||||
| -rw-r--r-- | plugins/NotesAndReminders/src/main.cpp | 389 | ||||
| -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 | 637 | ||||
| -rw-r--r-- | plugins/NotesAndReminders/src/reminders.cpp | 2890 | ||||
| -rw-r--r-- | plugins/NotesAndReminders/src/resource.h | 65 | 
9 files changed, 6738 insertions, 0 deletions
diff --git a/plugins/NotesAndReminders/src/globals.h b/plugins/NotesAndReminders/src/globals.h new file mode 100644 index 0000000000..f8cc794e4a --- /dev/null +++ b/plugins/NotesAndReminders/src/globals.h @@ -0,0 +1,147 @@ +#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 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 PLUGINLINK *pluginLink;
 +
 +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..a8bd8d3c53 --- /dev/null +++ b/plugins/NotesAndReminders/src/hotkeys.cpp @@ -0,0 +1,137 @@ +#include "globals.h"
 +
 +#define MSG_WND_CLASS "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.pszSection = SECTIONNAME;
 +
 +	desc.pszName = MODULENAME"/NewNote";
 +	desc.pszDescription = "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.pszDescription = "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.pszDescription = "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.pszDescription = "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;
 +}*/
 +
 +
 +int 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 = (WNDPROC)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..2165a8e49b --- /dev/null +++ b/plugins/NotesAndReminders/src/main.cpp @@ -0,0 +1,389 @@ +#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 OpenTriggeredReminder(WPARAM w, LPARAM l);
 +void BringAllNotesToFront(STICKYNOTE *pActive);
 +void CloseNotesList();
 +void CloseReminderList();
 +
 +int PluginMenuCommandAddNew(WPARAM w,LPARAM l)
 +{
 +	STICKYNOTE* PSN;
 +	PSN = NewNote(0,0,0,0,NULL,NULL,TRUE,TRUE,0);
 +	SetFocus(PSN->REHwnd);
 +	return 0;
 +}
 +
 +int PluginMenuCommandDeleteAll(WPARAM w,LPARAM l)
 +{
 +	if (g_Stickies && MessageBox(NULL, Translate("Are you sure you want to delete all notes?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
 +		DeleteNotes();
 +	return 0;
 +}
 +
 +int PluginMenuCommandShowHide(WPARAM w,LPARAM l)
 +{
 +	ShowHideNotes();
 +	return 0;
 +}
 +
 +int PluginMenuCommandViewNotes(WPARAM w,LPARAM l)
 +{
 +	ListNotes();
 +	return 0;
 +}
 +
 +int PluginMenuCommandAllBringFront(WPARAM w,LPARAM l)
 +{
 +	BringAllNotesToFront(NULL);
 +	return 0;
 +}
 +
 +int PluginMenuCommandNewReminder(WPARAM w,LPARAM l)
 +{
 +	NewReminder();
 +	return 0;
 +}
 +
 +int PluginMenuCommandViewReminders(WPARAM w,LPARAM l)
 +{
 +	ListReminders();
 +	return 0;
 +}
 +
 +int PluginMenuCommandDeleteReminders(WPARAM w,LPARAM l)
 +{
 +	if (RemindersList && MessageBox(NULL, Translate("Are you sure you want to delete all reminders?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
 +		DeleteReminders();
 +	return 0;
 +}
 +
 +struct
 +{
 +	char* szDescr;
 +	char* szName;
 +	int   defIconID;
 +}
 +static const iconList[] =
 +{
 +	{"New Reminder", "AddReminder", IDI_ADDREMINDER},
 +	{"Delete All Notes", "DeleteIcon", IDI_DELETEICON},
 +	{"New Note", "NoteIcon", IDI_NOTEICON},
 +	{"Show/Hide Notes", "ShowHide", IDI_SHOWHIDE},
 +	{"On Top Caption Icon", "CaptionIcon", IDI_CAPTIONICON},
 +	{"Delete All Reminders", "DeleteReminder", IDI_DELETEREMINDER},
 +	{"View Reminders", "ViewReminders", IDI_VIEWREMINDERS},
 +	{"Not on Top Caption Icon", "CaptionIconNotTop", IDI_CAPTIONICONNOTTOP},
 +	{"Hide Note Icon", "HideNote", IDI_HIDENOTE},
 +	{"Remove Note Icon", "RemoveNote", IDI_REMOVENOTE},
 +	{"Reminder Icon", "Reminder", IDI_REMINDER},
 +	{"Bring All to Front", "BringFront", IDI_BRINGFRONT},
 +	{"Play Sound Icon", "PlaySound", IDI_PLAYSOUND},
 +	{"View Notes", "ViewNotes", IDI_VIEWNOTES},
 +	{"New Note", "NewNote", IDI_NOTEICON},
 +	{"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.pszSection = MODULENAME;
 +	sid.flags = SIDF_PATH_TCHAR;
 +
 +	for (i = 0; i < SIZEOF(iconList); i++) 
 +	{
 +		mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", MODULENAME, iconList[i].szName);
 +
 +		sid.pszDescription = 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.pszTitle = _T(SECTIONNAME);
 +	odp.pszGroup = _T("Plugins");
 +	odp.pfnDlgProc = DlgProcOptions;
 +	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
 +
 +	SkinAddNewSoundEx("AlertReminder", "Alerts", "Reminder triggered");
 +	SkinAddNewSoundEx("AlertReminder2", "Alerts", "Reminder triggered (Alternative 1)");
 +	SkinAddNewSoundEx("AlertReminder3", "Alerts", "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.pszPopupName = "Notes && Reminders";
 +	cmi.flags = CMIM_ICON | CMIF_ICONFROMICOLIB;
 +
 +	cmi.position = 1600000000;
 +	cmi.icolibItem = hIconLibItem[2];
 +	cmi.pszName = "New &Note";
 +	cmi.pszService = MODULENAME"/MenuCommandAddNew";
 +	if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
 +	Menu_AddMainMenuItem(&cmi);
 +
 +	cmi.position = 1600000001;
 +	cmi.icolibItem = hIconLibItem[0];
 +	cmi.pszName = "New &Reminder";
 +	cmi.pszService = MODULENAME"/MenuCommandNewReminder";
 +	if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
 +	Menu_AddMainMenuItem(&cmi);
 +
 +	//
 +
 +	cmi.position = 1600100000;
 +	cmi.icolibItem = hIconLibItem[3];
 +	cmi.pszName = "&Show / Hide Notes";
 +	cmi.pszService = MODULENAME"/MenuCommandShowHide";
 +	if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
 +	Menu_AddMainMenuItem(&cmi);
 +
 +	cmi.position = 1600100001;
 +	cmi.icolibItem = hIconLibItem[13];
 +	cmi.pszName = "Vie&w Notes";
 +	cmi.pszService = MODULENAME"/MenuCommandViewNotes";
 +	if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
 +	Menu_AddMainMenuItem(&cmi);
 +
 +	cmi.position = 1600100002;
 +	cmi.icolibItem = hIconLibItem[1];
 +	cmi.pszName = "&Delete All Notes";
 +	cmi.pszService = MODULENAME"/MenuCommandDeleteAll";
 +	if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
 +	Menu_AddMainMenuItem(&cmi);
 +
 +	cmi.position = 1600100003;
 +	cmi.icolibItem = hIconLibItem[11];
 +	cmi.pszName = "&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.pszName = "&View Reminders";
 +	cmi.pszService = MODULENAME"/MenuCommandViewReminders";
 +	if (g_AddContListMI) Menu_AddContactMenuItem(&cmi);
 +	Menu_AddMainMenuItem(&cmi);
 +
 +	cmi.position = 1600200001;
 +	cmi.icolibItem = hIconLibItem[5];
 +	cmi.pszName = "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("RICHED20.DLL");
 +	if (!hRichedDll)
 +	{
 +		if (MessageBox(0, Translate("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."), SECTIONNAME, MB_YESNO | MB_ICONINFORMATION) != IDYES)
 +			return 1;
 +		return 0;
 +	}
 +
 +	hUserDll = LoadLibrary("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("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..5f98768d8b --- /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 "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[] =
 +{
 +	{"Black", RGB(0,0,0)},
 +	{"Maroon", RGB(128,0,0)},
 +	{"Green", RGB(0,128,0)},
 +	{"Olive", RGB(128,128,0)},
 +	{"Navy", RGB(0,0,128)},
 +	{"Purple", RGB(128,0,128)},
 +	{"Teal", RGB(0,128,128)},
 +	{"Gray", RGB(128,128,128)},
 +	{"Silver", RGB(192,192,192)},
 +	{"Red", RGB(255,0,0)},
 +	{"Orange", RGB(255,155,0)},
 +	{"Lime", RGB(0,255,0)},
 +	{"Yellow", RGB(255,255,0)},
 +	{"Blue", RGB(0,0,255)},
 +	{"Fuchsia", RGB(255,0,255)},
 +	{"Aqua", RGB(0,255,255)},
 +	{"White", RGB(255,255,255)}
 +};
 +
 +
 +TREEELEMENT *g_Stickies = NULL;
 +
 +
 +int CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,
 +								  WPARAM wParam,LPARAM lParam);
 +int CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,
 +								  LPARAM lParam);
 +void JustSaveNotes(void);
 +int PluginMenuCommandAddNew(WPARAM w,LPARAM l);
 +int 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, Translate("Are you sure you want to delete this note?"), 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, TranslateT(clrPresets[i].szName));
 +
 +			for (i=0; i<SIZEOF(clrPresets); i++)
 +				InsertMenu(hFg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_FG+i, TranslateT(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 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 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;
 +			CallService(MS_SKIN2_RELEASEICONBIG, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_BIG, (LPARAM)NULL), 0);
 +			CallService(MS_SKIN2_RELEASEICON, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_SMALL, (LPARAM)NULL), 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..8e15946db9 --- /dev/null +++ b/plugins/NotesAndReminders/src/options.cpp @@ -0,0 +1,637 @@ +#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));
 +
 +		FontRegister(&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));
 +
 +		ColourRegister(&colorid);
 +	}
 +
 +	hkFontChange = HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
 +	hkColorChange = HookEvent(ME_COLOUR_RELOAD, FS_ColorChanged);
 +}
 +
 +void LoadNRFont(int i, LOGFONT *lf, COLORREF *colour)
 +{
 +	COLORREF col;
 +    FontIDT fontid = {0};
 +
 +    fontid.cbSize = sizeof(fontid);
 +    _tcsncpy(fontid.group, LPGENT(SECTIONNAME), SIZEOF(fontid.group));
 +    _tcsncpy(fontid.name, fontOptionsList[i].szDescr, SIZEOF(fontid.name));
 +
 +    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 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 = "Executable Files\0*.exe\0All Files\0*.*\0\0";
 +				ofn.lpstrFile = s;
 +				ofn.nMaxFile = SIZEOF(s);
 +				ofn.lpstrTitle = Translate("Select Executable");
 +				ofn.lpstrInitialDir = ".";
 +				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,"");
 +				if (g_lpszAltBrowser)
 +				{
 +					mir_free(g_lpszAltBrowser);
 +					g_lpszAltBrowser = NULL;
 +				}
 +				SetDlgItemText(hdlg,IDC_EDIT_ALTBROWSER,"");
 +				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..0d545056f9 --- /dev/null +++ b/plugins/NotesAndReminders/src/reminders.cpp @@ -0,0 +1,2890 @@ +#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 CALLBACK DlgProcNotifyReminder(HWND Dialog,UINT Message,
 +								   WPARAM wParam,LPARAM lParam);
 +int CALLBACK DlgProcNewReminder(HWND Dialog,UINT Message,WPARAM wParam,
 +								LPARAM lParam);
 +int 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 OpenTriggeredReminder(WPARAM w, LPARAM l)
 +{
 +	REMINDERDATA *pReminder;
 +
 +	if (!l)
 +		return 0;
 +
 +	l = ((CLISTEVENT*)l)->lParam;
 +
 +	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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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, (WPARAM)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, (WPARAM)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, (WPARAM)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, (WPARAM)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, (WPARAM)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 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,(WPARAM)n,0) * 60;
 +
 +									if (TT >= 24*3600)
 +									{
 +										// selection is 1 day or more, take daylight saving boundaries into consideration
 +										// (ie. 24 hour might actually be 23 or 25, in order for reminder to pop up at the
 +										// same time tomorrow)
 +										SYSTEMTIME tm2, tm3;
 +										ULARGE_INTEGER li2 = li;
 +										FileTimeToTzLocalST((FILETIME*)&li2, &tm2);
 +										li2.QuadPart += (TT * FILETIME_TICKS_PER_SEC);
 +										FileTimeToTzLocalST((FILETIME*)&li2, &tm3);
 +										if (tm2.wHour != tm3.wHour || tm2.wMinute != tm3.wMinute)
 +										{
 +											// boundary crossed
 +
 +											// do a quick and dirty sanity check that times not more than 2 hours apart
 +											if (abs((int)(tm3.wHour*60+tm3.wMinute)-(int)(tm2.wHour*60+tm2.wMinute)) <= 120)
 +											{
 +												// adjust TT so that same HH:MM is set
 +												tm3.wHour = tm2.wHour;
 +												tm3.wMinute = tm2.wMinute;
 +												TzLocalSTToFileTime(&tm3, (FILETIME*)&li2);
 +												TT = (li2.QuadPart - li.QuadPart) / FILETIME_TICKS_PER_SEC;
 +											}
 +										}
 +									}
 +								}
 +								else
 +								{
 +									// parse user input
 +									char s[32];
 +									int h = 0, m = 0;
 +									GetDlgItemText(Dialog, IDC_REMINDAGAININ, s, 30);
 +									ParseTime(s, &h, &m, TRUE, TRUE);
 +									m += h * 60;
 +									if (!m)
 +									{
 +										MessageBox(Dialog, Translate("The specified time offset is invalid."), SECTIONNAME, MB_OK|MB_ICONWARNING);
 +										return TRUE;
 +									}
 +
 +									TT = m * 60;
 +								}
 +
 +								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 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, (WPARAM)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,(WPARAM)n,(LPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)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,(WPARAM)n,(LPARAM)0);
 +				n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("Alternative 1"));
 +				SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)1);
 +				n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("Alternative 2"));
 +				SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)n,(LPARAM)2);
 +				n = SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_ADDSTRING,0,(LPARAM)Translate("None"));
 +				SendDlgItemMessage(Dialog,IDC_COMBO_SOUND,CB_SETITEMDATA,(WPARAM)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, (WPARAM)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, (WPARAM)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;
 +    HMENU hMenuLoad,FhMenu;
 +	MENUITEMINFO mii;
 +
 +	hwndListView = (HWND)wParam;
 +	if (hwndListView != GetDlgItem(AhWnd,IDC_LISTREMINDERS)) return FALSE;
 +	hMenuLoad = LoadMenu(hinstance,"MNU_REMINDERPOPUP");
 +	FhMenu = GetSubMenu(hMenuLoad,0);
 +
 +	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 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,"");
 +			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:
 +		{
 +			HWND H;
 +
 +			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,"");
 +			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, Translate("Are you sure you want to delete all reminders?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
 +				{
 +					SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
 +					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, Translate("Are you sure you want to delete this reminder?"), SECTIONNAME, MB_OKCANCEL) == IDOK)
 +						{
 +							SetDlgItemText(Dialog,IDC_REMINDERDATA,"");
 +							DeleteReminder((REMINDERDATA*)TreeGetAt(RemindersList, I));
 +							JustSaveReminders();
 +							InitListView(H);
 +						}
 +					}
 +					return TRUE;
 +				}
 +			}
 +		}
 +	case WM_DESTROY:
 +		{
 +			CallService(MS_SKIN2_RELEASEICONBIG, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_BIG, (LPARAM)NULL), 0);
 +			CallService(MS_SKIN2_RELEASEICON, (WPARAM)SendMessage(Dialog, WM_SETICON, ICON_SMALL, (LPARAM)NULL), 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
  | 
