#include "globals.h"

#ifndef MONITOR_DEFAULTTONULL
#define MONITOR_DEFAULTTONULL       0x00000000
#endif


// NotesData DB data params
#define DATATAG_TEXT		1	// %s
#define DATATAG_SCROLLPOS	2	// %u (specifies rich edit controls scroll post as first visible line)
#define DATATAG_BGCOL		3	// %x (custom background color)
#define DATATAG_FGCOL		4	// %x (custom text/fg colors)
#define DATATAG_TITLE		5	// %s (custom note title)
#define DATATAG_FONT		6	// %d:%u:%u:%s (custom font)


#define MAX_TITLE_LEN	63
#define MAX_NOTE_LEN	16384

// delay before saving note changes (ms)
#define NOTE_CHANGE_COMMIT_DELAY 1000


#ifndef WS_EX_NOACTIVATE
#define WS_EX_NOACTIVATE 0x08000000
#endif
#define IDM_REMOVENOTE 40001
#define IDM_HIDENOTE 40002
#define IDM_TOGGLEONTOP 40003
#define IDM_UNDO 40004
#define IDM_COPY 40005
#define IDM_PASTE 40006
#define IDM_CUT 40007
#define IDM_CLEAR 40008
#define WS_EX_LAYERED 0x00080000
#define LWA_ALPHA 0x00000002

#define IDC_LISTREMINDERS 1000
#define IDC_LISTREMINDERS_HEADER 2000
#define IDC_REMINDERDATA 1001
#define IDC_ADDNEWREMINDER 1002
#define IDC_CLOSE 1003
#define WM_RELOAD (WM_USER + 100)

#define NOTIFY_LIST() if (ListNotesVisible) PostMessage(LV,WM_RELOAD,0,0)

#define PENLINK ENLINK *

#define NOTE_WND_CLASS _T("MIM_StickyNote")


#define IDM_COLORPRESET_BG 41000
#define IDM_COLORPRESET_FG 41100


static BOOL ListNotesVisible = FALSE;
static HWND LV;


struct ColorPreset
{
	TCHAR *szName;
	COLORREF color;
};

static struct ColorPreset clrPresets[] =
{
	{LPGENT("Black"), RGB(0,0,0)},
	{LPGENT("Maroon"), RGB(128,0,0)},
	{LPGENT("Green"), RGB(0,128,0)},
	{LPGENT("Olive"), RGB(128,128,0)},
	{LPGENT("Navy"), RGB(0,0,128)},
	{LPGENT("Purple"), RGB(128,0,128)},
	{LPGENT("Teal"), RGB(0,128,128)},
	{LPGENT("Gray"), RGB(128,128,128)},
	{LPGENT("Silver"), RGB(192,192,192)},
	{LPGENT("Red"), RGB(255,0,0)},
	{LPGENT("Orange"), RGB(255,155,0)},
	{LPGENT("Lime"), RGB(0,255,0)},
	{LPGENT("Yellow"), RGB(255,255,0)},
	{LPGENT("Blue"), RGB(0,0,255)},
	{LPGENT("Fuchsia"), RGB(255,0,255)},
	{LPGENT("Aqua"), RGB(0,255,255)},
	{LPGENT("White"), RGB(255,255,255)}
};


TREEELEMENT *g_Stickies = NULL;


INT_PTR CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,
								  WPARAM wParam,LPARAM lParam);
INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,
								  LPARAM lParam);
void JustSaveNotes(void);
INT_PTR PluginMenuCommandAddNew(WPARAM w,LPARAM l);
INT_PTR PluginMenuCommandDeleteAll(WPARAM w,LPARAM l);
void GetTriggerTimeString(const ULARGE_INTEGER *When, char *s, UINT strSize, BOOL bUtc);
void OnListResize(HWND Dialog);
void UpdateGeomFromWnd(HWND Dialog, int *geom, int *colgeom, int nCols);
void FileTimeToTzLocalST(const FILETIME *lpUtc, SYSTEMTIME *tmLocal);


COLORREF GetCaptionColor(COLORREF bodyClr)
{
	const DWORD r = ((bodyClr & 0xff) * 4) / 5;
	const DWORD g = (((bodyClr & 0xff00) * 4) / 5) & 0xff00;
	const DWORD b = (((bodyClr & 0xff0000) * 4) / 5) & 0xff0000;

	return (COLORREF)(r|g|b);
}


static void EnsureUniqueID(STICKYNOTE *TSN)
{
	TREEELEMENT *TTE;

	if (!g_Stickies)
		return;

try_next:

	// check existing notes if id is in use
	TTE = g_Stickies;
	while (TTE)
	{
		if (((STICKYNOTE*)TTE->ptrdata)->ID.QuadPart == TSN->ID.QuadPart)
		{
			// id in use, try new (increases the ID/time stamp by 100 nanosecond steps until an unused time is found,
			// allthough it's very unlikely that there will be duplicated id's it's better to make 100% sure)
			TSN->ID.QuadPart++;
			goto try_next;
		}

		TTE = (TREEELEMENT*)TTE->next;
	}
}


static void InitNoteTitle(STICKYNOTE *TSN)
{
	if (g_NoteTitleDate)
	{
		char TempStr[MAX_PATH];
		SYSTEMTIME tm;
		LCID lc = GetUserDefaultLCID();

		TempStr[0] = 0;

		memset(&tm, 0, sizeof(tm));
		FileTimeToTzLocalST((FILETIME*)&TSN->ID, &tm);

		if ( GetDateFormat(lc, 0, &tm, GetDateFormatStr(), TempStr, MAX_PATH) )
		{
			// append time if requested
			if (g_NoteTitleTime)
			{
				int n = (int)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)
	{
		SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE);
		lf->lfHeight = 10;
		HDC 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,&note.ID,note.Visible,note.OnTop,scrollV,note.BgColor,note.FgColor,note.title,pCustomFont,TRUE);
		}
		else
		{
			// old format (for DB backward compatibility)

			int Tx,Ty,Tw,Th,TV,OT;
			BOOL V;
			char *Data,*ID;
			ULARGE_INTEGER newid;

			OT = 1; TV = 1;
			Tx = 100; Ty = 100;
			Tw = 179; Th = 35;
			Data = NULL; ID = NULL;

			if (DelPos = strchr(Value,0x1B))
			{	// get first delimiter
				int	PartLen = DelPos - TVal;

				Data = NULL;
				ID = NULL;
				TVal = Value;
				DelPos[0] = 0x0;
				Tx = strtol(TVal, NULL, 10);

				TVal = DelPos + 1;
				DelPos = strchr(TVal, 0x1B);
				if (!DelPos) continue; // setting is broken, do not crash
				DelPos[0] = 0x0;
				Ty = strtol(TVal, NULL, 10);

				TVal = DelPos + 1;
				DelPos = strchr(TVal, 0x1B);
				if (!DelPos) continue; // setting is broken, do not crash
				DelPos[0] = 0x0;
				Tw = strtol(TVal, NULL, 10);

				TVal = DelPos + 1;
				DelPos = strchr(TVal, 0x1B);
				if (!DelPos) continue; // setting is broken, do not crash
				DelPos[0] = 0x0;
				Th = strtol(TVal, NULL, 10);

				TVal = DelPos + 1;
				DelPos = strchr(TVal, 0x1B);
				if (!DelPos) continue; // setting is broken, do not crash
				DelPos[0] = 0x0;
				TV = strtol(TVal, NULL, 10);

				TVal = DelPos + 1;
				DelPos = strchr(TVal, 0x1B);
				if (!DelPos) continue; // setting is broken, do not crash
				DelPos[0] = 0x0;
				OT = strtol(TVal, NULL, 10);

				TVal = DelPos + 1;
				DelPos = strchr(TVal, 0x1B);
				if (!DelPos) continue; // setting is broken, do not crash
				DelPos[0] = 0x0;
				Data = _strdup(TVal);

				TVal = DelPos + 1;
				ID = TVal;

				V = (BOOL)TV && (!bIsStartup || g_ShowNotesAtStart);

				if (bIsStartup)
					V |= 0x10000;

				// convert old ID format to new
				if ( strchr(ID, '-') )
				{
					// validate format (otherwise create new)
					if (strlen(ID) < 19 || ID[2] != '-' || ID[5] != '-' || ID[10] != ' ' || ID[13] != ':' || ID[16] != ':')
					{
						ID = NULL;
					}
					else
					{
						SYSTEMTIME tm;

						ID[2] = ID[5] = ID[10] = ID[13] = ID[16] = 0;

						memset(&tm, 0, sizeof(tm));
						tm.wDay = (WORD)strtoul(ID, NULL, 10);
						tm.wMonth = (WORD)strtoul(ID+3, NULL, 10);
						tm.wYear = (WORD)strtoul(ID+6, NULL, 10);
						tm.wHour = (WORD)strtoul(ID+11, NULL, 10);
						tm.wMinute = (WORD)strtoul(ID+14, NULL, 10);
						tm.wSecond = (WORD)strtoul(ID+17, NULL, 10);

						SYSTEMTIMEtoFILETIME(&tm, (FILETIME*)&newid);
					}
				}
				else
				{
					ID = NULL;
				}

				NewNoteEx(Tx,Ty,Tw,Th,Data,ID?&newid:NULL,V,(BOOL)OT,0,0,0,NULL,NULL,TRUE);
			}
		}
skip:;
	}

	if (Value)
		FreeSettingBlob(Size, Value); // we do not leak on bad setting

	NOTIFY_LIST();
}

void CloseNotesList()
{
	if (ListNotesVisible)
	{
		DestroyWindow(LV);
		ListNotesVisible = FALSE;
	}
}

static void PurgeNotesTree()
{
	STICKYNOTE *pt;

	while (g_Stickies) // empty whole tree
	{
		pt = (STICKYNOTE*)g_Stickies->ptrdata;
		if (pt->SNHwnd) DestroyWindow(pt->SNHwnd);
		SAFE_FREE((void**)&pt->title);
		SAFE_FREE((void**)&pt->data);
		if (pt->pCustomFont)
		{
			DeleteObject(pt->pCustomFont->hFont);
			free(pt->pCustomFont);
		}
		TreeDelete(&g_Stickies,pt);
		SAFE_FREE((void**)&pt);
	}
	g_Stickies = NULL;
}

void SaveNotes(void)
{
	JustSaveNotes();
	PurgeNotesTree();
}

void PurgeNotes(void)
{
	int NotesCount, I;
	char ValueName[16];

	NotesCount = ReadSettingInt(0,MODULENAME,"NotesData",0);
	for(I = 0; I < NotesCount; I++)
	{
		sprintf(ValueName, "NotesData%d", I);
		DBDeleteContactSetting(0,MODULENAME,ValueName);
	}
}

void OnDeleteNote(HWND hdlg, STICKYNOTE *SN)
{
	if (MessageBox(hdlg, TranslateT("Are you sure you want to delete this note?"), TranslateT(SECTIONNAME), MB_OKCANCEL) == IDOK)
	{
		if (SN->SNHwnd)
			DestroyWindow(SN->SNHwnd);
		TreeDelete(&g_Stickies,SN);
		SAFE_FREE((void**)&SN->data);
		if (SN->pCustomFont)
		{
			DeleteObject(SN->pCustomFont->hFont);
			free(SN->pCustomFont);
		}
		SAFE_FREE((void**)&SN);
		JustSaveNotes();
		NOTIFY_LIST();
	}
}

void DeleteNotes(void)
{
	PurgeNotes();
	WriteSettingInt(0, MODULENAME, "NotesData", 0);
	PurgeNotesTree();
	NOTIFY_LIST();
}

void ShowHideNotes(void)
{
	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

	UINT nHideCount  = 0, nVisCount = 0;
	TREEELEMENT *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;

	int 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;
			Value[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)
{
	UINT i;
	TCHAR s[128];

	int n = GetMenuItemCount(h);

	if (n <= 0)
	{
		return -1;
	}

	// searches for a menu item based on name (used to avoid hardcoding item indices for sub-menus)
	for (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,_T("ctrldata"));

	HMENU hMenuLoad, FhMenu, hSub;
	hMenuLoad = LoadMenu(hinstance,_T("MNU_NOTEPOPUP"));
	FhMenu = GetSubMenu(hMenuLoad,0);

	if (SN->OnTop)
		CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED|MF_BYCOMMAND);

	EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_PASTETITLE, MF_BYCOMMAND | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));

	if (!SN->CustomTitle)
		EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTEPOPUP_RESETTITLE, MF_BYCOMMAND | MF_GRAYED);

	// NOTE: names used for FindMenuItem would need to include & chars if such shortcuts are added to the menus

	// color preset menu items uses features that require win2k or later, I'm too lazy to make a fallback path
	// for obsolete OS versions (win95/98/ME users can still use the "Custom" menu option)
	if (g_isWin2kPlus)
	{
		n = FindMenuItem(FhMenu, _T("Appearance"));
		if (n >= 0 && (hSub = GetSubMenu(FhMenu, n)))
		{
			HMENU hBg = GetSubMenu(hSub, FindMenuItem(hSub, _T("Background Color")));
			HMENU hFg = GetSubMenu(hSub, FindMenuItem(hSub, _T("Text Color")));

			for (i=0; i<SIZEOF(clrPresets); i++)
				InsertMenu(hBg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_BG+i, TranslateTS(clrPresets[i].szName));

			for (i=0; i<SIZEOF(clrPresets); i++)
				InsertMenu(hFg, i, MF_BYPOSITION|MF_OWNERDRAW, IDM_COLORPRESET_FG+i, TranslateTS(clrPresets[i].szName));
		}
	}

    CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
	TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
	DestroyMenu(hMenuLoad);

	return TRUE;
}

static void MeasureColorPresetMenuItem(HWND hdlg, LPMEASUREITEMSTRUCT lpMeasureItem, struct ColorPreset *clrPresets)
{
	HDC hdc = GetDC(hdlg);
	LPTSTR lpsz = TranslateTS(clrPresets->szName);
	SIZE sz;
	GetTextExtentPoint32(hdc, lpsz, (int)_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 = (int)strlen(pOut);
						break;
					}
					else if (*p == '\t')
					{
						*p = ' ';
					}
					p++;
				}

				// trim trailing white spaces
				while  (n && isspace(pOut[n-1]))
					pOut[--n] = 0;

				if (n)
					bResult = TRUE;
			}

			GlobalUnlock(hData);
		}

		CloseClipboard();
	}

	return bResult;
}

static void SetNoteTextControl(STICKYNOTE *SN)
{
    CHARFORMAT CF = {0};
	CF.cbSize = sizeof(CHARFORMAT);
	CF.dwMask = CFM_COLOR;
	CF.crTextColor = SN->FgColor ? (SN->FgColor&0xffffff) : BodyFontColor;
	SendMessage(SN->REHwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CF);

	if (SN->data) // TODO: use EM_STREAMIN
		SendMessage(SN->REHwnd, WM_SETTEXT, 0, (LPARAM)(SN->data));
}


static UINT_PTR CALLBACK CFHookProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == WM_INITDIALOG)
	{
		// hide color selector
		ShowWindow(GetDlgItem(hdlg,0x443), SW_HIDE);
		ShowWindow(GetDlgItem(hdlg,0x473), SW_HIDE);
		TranslateDialogDefault(hdlg);
	}

	return 0;
}


INT_PTR CALLBACK StickyNoteWndProc(HWND hdlg,UINT message,WPARAM wParam,LPARAM lParam)
{
	switch (message)
	{
	case WM_CLOSE:
		return TRUE;

	case WM_SIZE:
		{
			RECT SZ;

			GetClientRect(hdlg,&SZ);
			HWND H = GetDlgItem(hdlg,1);
			MoveWindow(H, 0, 0, SZ.right,SZ.bottom, TRUE);

			KillTimer(hdlg, 1025);
			SetTimer(hdlg, 1025, NOTE_CHANGE_COMMIT_DELAY, 0);

			return TRUE;
		}
    case WM_TIMER:
		if (wParam == 1025)
		{
			STICKYNOTE *SN = (STICKYNOTE*)GetProp(hdlg,_T("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,_T("ctrldata"));

			CREATESTRUCT *CS = (CREATESTRUCT *)lParam;
			HWND H;
			DWORD mystyle;

			SN = (STICKYNOTE*)CS->lpCreateParams;
			SetProp(hdlg,_T("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(iconList[4].hIcolib);
				else
					hcIcon = Skin_GetIconByHandle(iconList[7].hIcolib);
				DrawIcon(hdc, wr.right - wr.left - 16, 0 + 3, hcIcon);
				Skin_ReleaseIcon(hcIcon);

				hcIcon = Skin_GetIconByHandle(iconList[9].hIcolib);
				DrawIcon(hdc, wr.right - wr.left - 32, 1 + 3, hcIcon);
				Skin_ReleaseIcon(hcIcon);

				hcIcon = Skin_GetIconByHandle(iconList[8].hIcolib);
				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,_T("open"),_T("iexplore"),Buff,_T(""),SW_SHOWNORMAL);
				else if (g_lpszAltBrowser && *g_lpszAltBrowser)
					ShellExecute(hdlg,_T("open"),g_lpszAltBrowser,Buff,_T(""),SW_SHOWNORMAL);
				else
					ShellExecute(hdlg,_T("open"),Buff,_T(""),_T(""),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,_T("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, _T("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 = (int)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 = (int)strlen(S1);
		ListView_InsertItem(AHLV,&lvTIt);

		if (pNote->Visible)
		{
			lvTIt.iItem = I;
			lvTIt.iSubItem = 1;
			lvTIt.pszText = V;
			lvTIt.cchTextMax = (int)strlen(lvTIt.pszText);
			ListView_SetItem(AHLV,&lvTIt);
		}

		if (pNote->OnTop)
		{
			lvTIt.iItem = I;
			lvTIt.iSubItem = 2;
			lvTIt.pszText = T;
			lvTIt.cchTextMax = (int)strlen(lvTIt.pszText);
			ListView_SetItem(AHLV,&lvTIt);
		}

		S = GetPreviewString(pNote->data);
		lvTIt.iItem = I;
		lvTIt.iSubItem = 3;
		lvTIt.pszText = S;
		lvTIt.cchTextMax = (int)strlen(S);
		ListView_SetItem(AHLV,&lvTIt);

		I++;
		TTE = (TREEELEMENT*)TTE->next;
	}

	ListView_SetItemState(AHLV,0,LVIS_SELECTED,LVIS_SELECTED);
}

static BOOL DoListContextMenu(HWND AhWnd,WPARAM wParam,LPARAM lParam,STICKYNOTE *pNote)
{
	HWND hwndListView;
    HMENU hMenuLoad,FhMenu;
	MENUITEMINFO mii;

	hwndListView = (HWND)wParam;
	if (hwndListView != GetDlgItem(AhWnd,IDC_LISTREMINDERS)) return FALSE;
	hMenuLoad = LoadMenu(hinstance,"MNU_NOTELISTPOPUP");
	FhMenu = GetSubMenu(hMenuLoad,0);

	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE;
	mii.fState = MFS_DEFAULT;
	if (!pNote)
		mii.fState |= MFS_GRAYED;
	SetMenuItemInfo(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE, FALSE, &mii);

	if (!pNote)
	{
		EnableMenuItem(FhMenu, IDM_REMOVENOTE, MF_GRAYED|MF_BYCOMMAND);
		EnableMenuItem(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY, MF_GRAYED|MF_BYCOMMAND);
		EnableMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_GRAYED|MF_BYCOMMAND);
	}
	else
	{
		if (pNote->Visible)
			CheckMenuItem(FhMenu, ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY, MF_CHECKED|MF_BYCOMMAND);
		if (pNote->OnTop)
			CheckMenuItem(FhMenu, IDM_TOGGLEONTOP, MF_CHECKED|MF_BYCOMMAND);
	}

    CallService(MS_LANGPACK_TRANSLATEMENU,(DWORD)FhMenu,0);
	TrackPopupMenu(FhMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON,LOWORD(lParam),HIWORD(lParam),0,AhWnd,0);
	DestroyMenu(hMenuLoad);

	return TRUE;
}


INT_PTR CALLBACK DlgProcViewNotes(HWND Dialog,UINT Message,WPARAM wParam,LPARAM lParam)
{
    LV_COLUMN lvCol;
    NMLISTVIEW *NM;
    TCHAR *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(iconList[13].hIcolib, ICON_SMALL);
			SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
			hIcon = Skin_GetIconByHandle(iconList[13].hIcolib, ICON_BIG);
			SendMessage(Dialog, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hIcon);

			SetWindowText(Dialog, _T("Notes"));

			TranslateDialogDefault(Dialog);

			SetDlgItemText(Dialog,IDC_REMINDERDATA,_T(""));

			H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
			lvCol.mask = LVCF_TEXT | LVCF_WIDTH;

			S = TranslateT("Note text");
			lvCol.pszText = S;
			lvCol.cchTextMax = (int)_tcslen(S);
			lvCol.cx = g_notesListColGeom[3];
			ListView_InsertColumn(H,0,&lvCol);
			lvCol.mask = LVCF_TEXT | LVCF_WIDTH;

			S = TranslateT("T");
			lvCol.pszText = S;
			lvCol.cchTextMax = (int)_tcslen(S);
			lvCol.cx = g_notesListColGeom[2];
			ListView_InsertColumn(H,0,&lvCol);
			lvCol.mask = LVCF_TEXT | LVCF_WIDTH;

			S = TranslateT("V");
			lvCol.pszText = S;
			lvCol.cchTextMax = (int)strlen(S);
			lvCol.cx = g_notesListColGeom[1];
			ListView_InsertColumn(H,0,&lvCol);
			lvCol.mask = LVCF_TEXT | LVCF_WIDTH;

			S = TranslateT("Date/Title");
			lvCol.pszText = S;
			lvCol.cchTextMax = (int)_tcslen(S);
			lvCol.cx = g_notesListColGeom[0];
			ListView_InsertColumn(H,0,&lvCol);

			InitListView(H);
			SetWindowLong(GetDlgItem(H, 0), GWL_ID, IDC_LISTREMINDERS_HEADER);
			LV = Dialog;

			if (g_notesListGeom[1] && g_notesListGeom[2])
			{
				WINDOWPLACEMENT wp;
				wp.length = sizeof(WINDOWPLACEMENT);
				GetWindowPlacement(Dialog, &wp);
				wp.rcNormalPosition.left = g_notesListGeom[0];
				wp.rcNormalPosition.top = g_notesListGeom[1];
				wp.rcNormalPosition.right = g_notesListGeom[2] + g_notesListGeom[0];
				wp.rcNormalPosition.bottom = g_notesListGeom[3] + g_notesListGeom[1];
				SetWindowPlacement(Dialog, &wp);
			}
			return TRUE;
		}
	case WM_CLOSE:
		DestroyWindow(Dialog);
		ListNotesVisible = FALSE;
		return TRUE;

	case WM_DESTROY:
		ListNotesVisible = FALSE;
		Skin_ReleaseIcon((HICON)SendMessage(Dialog, WM_SETICON, ICON_BIG, 0));
		Skin_ReleaseIcon((HICON)SendMessage(Dialog, WM_SETICON, ICON_SMALL, 0));
		return TRUE;

	case WM_NOTIFY:
		{
			if (wParam == IDC_LISTREMINDERS)
			{
				NM = (NMLISTVIEW *)lParam;
				switch (NM->hdr.code)
				{
				case LVN_ITEMCHANGED:
					{
						S = ((STICKYNOTE*)TreeGetAt(g_Stickies,NM->iItem))->data;
					    SetDlgItemText(Dialog,IDC_REMINDERDATA,S);
					}
					break;
				case NM_DBLCLK:
					{
						HWND H;

						H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
						if ( ListView_GetSelectedCount(H) )
						{
							I = ListView_GetSelectionMark(H);
							if (I != -1)
							{
								EditNote((STICKYNOTE *)TreeGetAt(g_Stickies, I));
							}
						}
					}
					break;
				}
			}
			else if (wParam == IDC_LISTREMINDERS_HEADER)
			{
				NMHEADER *NM = (NMHEADER*)lParam;
				switch (NM->hdr.code)
				{
				case HDN_ENDTRACK:
					UpdateGeomFromWnd(Dialog, NULL, g_notesListColGeom, SIZEOF(g_notesListColGeom));
					break;
				}
			}
		}
		break;
	case WM_COMMAND:
		{
			switch(LOWORD(wParam))
			{
			case ID_CONTEXTMENUNOTELISTVIEW_EDITNOTE:
				{
					HWND H;

					H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
					if ( ListView_GetSelectedCount(H) )
					{
						I = ListView_GetSelectionMark(H);
						if (I != -1)
						{
							EditNote((STICKYNOTE*)TreeGetAt(g_Stickies, I));
						}
					}
				}
				return TRUE;
			case ID_CONTEXTMENUNOTELISTVIEW_TOGGLEVISIBILITY:
				{
					HWND H;

					H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
					if ( ListView_GetSelectedCount(H) )
					{
						I = ListView_GetSelectionMark(H);
						if (I != -1)
						{
							STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies, I);
							SN->Visible = !SN->Visible;
							ShowWindow(SN->SNHwnd,SN->Visible?SW_SHOWNA:SW_HIDE);
							JustSaveNotes();
						}
					}
				}
				return TRUE;
			case IDM_TOGGLEONTOP:
				{
					HWND H;

					H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
					if ( ListView_GetSelectedCount(H) )
					{
						I = ListView_GetSelectionMark(H);
						if (I != -1)
						{
							STICKYNOTE *SN = (STICKYNOTE*)TreeGetAt(g_Stickies,I);
							SN->OnTop = !SN->OnTop;
							SetWindowPos(SN->SNHwnd, SN->OnTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0,0,0,0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
							RedrawWindow(SN->SNHwnd, NULL, NULL, RDW_FRAME|RDW_INVALIDATE|RDW_UPDATENOW);
							JustSaveNotes();
						}
					}
				}
				return TRUE;
			case IDC_CLOSE:
				{
					DestroyWindow(Dialog);
					ListNotesVisible = FALSE;
					return TRUE;
				}
			case ID_CONTEXTMENUNOTEPOPUP_NEWNOTE:
			case IDC_ADDNEWREMINDER:
				{
					PluginMenuCommandAddNew(0,0);
					return TRUE;
				}
			case ID_CONTEXTMENUNOTELISTVIEW_DELETEALLNOTES:
				{
					PluginMenuCommandDeleteAll(0,0);
					return TRUE;
				}
			case IDM_REMOVENOTE:
				{
					HWND H;

					H = GetDlgItem(Dialog,IDC_LISTREMINDERS);
					if ( ListView_GetSelectedCount(H) )
					{
						I = ListView_GetSelectionMark(H);
						if (I != -1)
						{
							OnDeleteNote(Dialog, (STICKYNOTE*)TreeGetAt(g_Stickies, I));
						}
					}
				}
				return TRUE;
			case ID_CONTEXTMENUNOTELISTVIEW_SHOW:
				{
					ShowHideNotes();
					return TRUE;
				}
			case ID_CONTEXTMENUNOTEPOPUP_BRINGALLTOTOP:
				{
					BringAllNotesToFront(NULL);
					return TRUE;
				}
			}
		}
	}
	return FALSE;
}