From 171e81205e357e0d54283a63997ed58ff97d54a9 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 24 Jul 2012 11:48:31 +0000 Subject: UserInfoEx, Variables: changed folder structure git-svn-id: http://svn.miranda-ng.org/main/trunk@1160 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/UserInfoEx/src/svc_reminder.cpp | 1216 +++++++++++++++++++++++++++++++ 1 file changed, 1216 insertions(+) create mode 100644 plugins/UserInfoEx/src/svc_reminder.cpp (limited to 'plugins/UserInfoEx/src/svc_reminder.cpp') diff --git a/plugins/UserInfoEx/src/svc_reminder.cpp b/plugins/UserInfoEx/src/svc_reminder.cpp new file mode 100644 index 0000000000..f91ba47f33 --- /dev/null +++ b/plugins/UserInfoEx/src/svc_reminder.cpp @@ -0,0 +1,1216 @@ +/* +UserinfoEx plugin for Miranda IM + +Copyright: +ฉ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +=============================================================================== + +File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_reminder.cpp $ +Revision : $Revision: 187 $ +Last change on : $Date: 2010-09-08 16:05:54 +0400 (ะกั€, 08 ัะตะฝ 2010) $ +Last change by : $Author: ing.u.horn $ + +=============================================================================== +*/ + +/** + * System Includes: + **/ +#include "commonheaders.h" +#include "m_skin.h" +#include "m_clui.h" + +#include "svc_Gender.h" +#include "svc_Reminder.h" +#include "dlg_anniversarylist.h" + +/** + * The CEvent structure describes the next anniversary to remind of. + **/ +struct CEvent +{ + enum EType { NONE, BIRTHDAY, ANNIVERSARY }; + + EType _eType; + WORD _wDaysLeft; + + CEvent(); + CEvent(EType eType, WORD wDaysLeft); + + BOOLEAN operator << (const CEvent& e); +}; + +typedef struct _REMINDEROPTIONS +{ + WORD wDaysEarlier; + BYTE bPopups; + BYTE bCListExtraIcon; + BYTE bFlashCList; + BYTE bCheckVisibleOnly; + BYTE RemindState; + CEvent evt; +} +REMINDEROPTIONS, *LPREMINDEROPTIONS; + +static HANDLE ExtraIcon = INVALID_HANDLE_VALUE; + + +static HANDLE ghCListIA = NULL; +static HANDLE ghCListIR = NULL; +static HANDLE ghSettingsChanged = NULL; + +static UINT_PTR ghRemindTimer = 0; +static UINT_PTR ghRemindDateChangeTimer = 0; + +HANDLE ghCListAnnivIcons[11]; +HANDLE ghCListBirthdayIcons[11]; + +static REMINDEROPTIONS gRemindOpts; + +static VOID UpdateTimer(BOOLEAN bStartup); + + +/*********************************************************************************************************** + * struct CEvent + ***********************************************************************************************************/ + +/** + * This is the default constructor. + * + * @param none + * + * @return nothing + **/ +CEvent::CEvent() +{ + _wDaysLeft = 0xFFFF; + _eType = NONE; +} + +/** + * This is the default constructor. + * + * @param eType - initial type + * @param wDaysLeft - initial days to event + * + * @return nothing + **/ +CEvent::CEvent(EType eType, WORD wDaysLeft) +{ + _wDaysLeft = wDaysLeft; + _eType = eType; +} + +/** + * This operator dups the attributes of the given CEvent object if + * the event comes up earlier then the one of the object. + * + * @param evt - the reference to the event object whose attributes to assign. + * + * @retval TRUE - The values of @e evt have been assigned. + * @retval FALSE - The values are not assigned. + **/ +BOOLEAN CEvent::operator << (const CEvent& evt) +{ + if (_wDaysLeft > evt._wDaysLeft) + { + _wDaysLeft = evt._wDaysLeft; + _eType = evt._eType; + return TRUE; + } + return FALSE; +} + +/*********************************************************************************************************** + * notification functions + ***********************************************************************************************************/ + +/** + * This function returns the icon for the given anniversary, + * which is the given number of days in advance. + * + * @param evt - structure specifying the next anniversary + * + * @return The function returns icolib's icon if found or NULL otherwise. + **/ +static HICON GetAnnivIcon(const CEvent &evt) +{ + HICON hIcon = NULL; + + CHAR szIcon[MAXSETTING]; + + switch (evt._eType) + { + case CEvent::BIRTHDAY: + { + if (evt._wDaysLeft > 9) + { + hIcon = IcoLib_GetIcon(ICO_RMD_DTBX); + } + else + { + mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dtb%u", evt._wDaysLeft); + hIcon = IcoLib_GetIcon(szIcon); + } + } + break; + + case CEvent::ANNIVERSARY: + { + if (evt._wDaysLeft > 9) + { + hIcon = IcoLib_GetIcon(ICO_RMD_DTAX); + } + else + { + mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dta%u", evt._wDaysLeft); + hIcon = IcoLib_GetIcon(szIcon); + } + } + } + return hIcon; +} + +/** + * This function adds the icon for the given anniversary, which is the given number of days + * in advance to the contact list's imagelist. + * + * @param evt - structure specifying the next anniversary + * + * @return The function returns the clist's extra icon handle if found and successfully added. + **/ +static HANDLE AddCListExtraIcon(const CEvent &evt) +{ + HANDLE hClistIcon; + HICON hIco = GetAnnivIcon(evt); + if (hIco) + { + hClistIcon = (HANDLE)CallService(MS_CLIST_EXTRA_ADD_ICON, (WPARAM)hIco, 0); + if (hClistIcon == (HANDLE)CALLSERVICE_NOTFOUND) + hClistIcon = INVALID_HANDLE_VALUE; + + Skin_ReleaseIcon(hIco); + } + else hClistIcon = INVALID_HANDLE_VALUE; + + return hClistIcon; +} + +/** + * This function returns the clist extra icon handle for the given anniversary. + * + * @param evt - structure specifying the next anniversary + * + * @return The function returns the clist extra icon handle for the given anniversary. + **/ +static HANDLE GetCListExtraIcon(const CEvent &evt) +{ + if (gRemindOpts.bCListExtraIcon) + { + WORD wIndex = evt._wDaysLeft; + + switch (evt._eType) + { + case CEvent::BIRTHDAY: + { + if (wIndex >= SIZEOF(ghCListBirthdayIcons)) + { + wIndex = SIZEOF(ghCListBirthdayIcons) - 1; + } + // add the icon to clists imagelist if required + if (ghCListBirthdayIcons[wIndex] == INVALID_HANDLE_VALUE) + { + ghCListBirthdayIcons[wIndex] = AddCListExtraIcon(evt); + } + } + return ghCListBirthdayIcons[wIndex]; + + case CEvent::ANNIVERSARY: + { + if (wIndex >= SIZEOF(ghCListAnnivIcons)) + { + wIndex = SIZEOF(ghCListAnnivIcons) - 1; + } + // add the icon to clists imagelist if required + if (ghCListAnnivIcons[wIndex] == INVALID_HANDLE_VALUE) + { + ghCListAnnivIcons[wIndex] = AddCListExtraIcon(evt); + } + } + return ghCListAnnivIcons[wIndex]; + } + } + return INVALID_HANDLE_VALUE; +} + +/** + * Displays an clist extra icon according to the kind of anniversary + * and the days in advance. + * + * @param evt - structure specifying the next anniversary + * + * @return nothing + **/ +static VOID NotifyWithExtraIcon(HANDLE hContact, const CEvent &evt) +{ + if (myGlobals.HaveCListExtraIcons && gRemindOpts.bCListExtraIcon) + { + if (!myGlobals.ExtraIconsServiceExist) + { + IconExtraColumn iec; + + iec.cbSize = sizeof(IconExtraColumn); + iec.ColumnType = gRemindOpts.bCListExtraIcon; + iec.hImage = GetCListExtraIcon(evt); + CallService(MS_CLIST_EXTRA_SET_ICON, (WPARAM)hContact, (LPARAM)&iec); + } + else + { + CHAR szIcon[MAXSETTING]; + EXTRAICON ico; + + ico.cbSize=sizeof(ico); + ico.hContact=hContact; + ico.hExtraIcon=ExtraIcon; + switch (evt._eType) + { + case CEvent::BIRTHDAY: + { + if (evt._wDaysLeft > 9) + { + ico.icoName=ICO_RMD_DTAX; + } + else + { + mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dtb%u", evt._wDaysLeft); + ico.icoName=szIcon; + } + break; + } + case CEvent::ANNIVERSARY: + { + if (evt._wDaysLeft > 9) + { + ico.icoName=ICO_RMD_DTAX; + } + else + { + mir_snprintf(szIcon, SIZEOF(szIcon), MODNAME"_rmd_dta%u", evt._wDaysLeft); + ico.icoName=szIcon; + } + break; + } + default: + ico.icoName=(char *)0; + } + CallService(MS_EXTRAICON_SET_ICON, (WPARAM)&ico, 0); + } + } +} + +/** + * Message procedure for popup messages + * + * @param hWnd - handle to the popupwindow + * @param uMsg - message to handle + * @param wParam - message specific parameter + * @param lParam - message specific parameter + * + * @return message specific + **/ +static INT_PTR CALLBACK PopupWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_COMMAND: + { + if (HIWORD(wParam) == STN_CLICKED) + { + PUDeletePopUp(hWnd); + return TRUE; + } + break; + } + + case WM_CONTEXTMENU: + { + PUDeletePopUp(hWnd); + return TRUE; + } + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +/** + * Displays a popup + * + * @param hContact - contact to display popup for + * @param eventType - indicates which popup settings to apply + * @param DaysToAnniv - days left until anniversary occures + * @param pszDesc - this is the headline + * @param szMsg - message to display + * + * @return return value of the popup service + **/ +static INT NotifyWithPopup(HANDLE hContact, CEvent::EType eventType, INT DaysToAnniv, LPCTSTR pszDesc, LPCTSTR pszMsg) +{ + if (gRemindOpts.bPopups) + { + POPUPDATAT_V2 ppd; + + ZeroMemory(&ppd, sizeof(POPUPDATAT_V2)); + ppd.PluginWindowProc = (WNDPROC)PopupWindowProc; + ppd.iSeconds = (INT)DB::Setting::GetByte(SET_POPUP_DELAY, 0); + + if (hContact) + { + ppd.lchContact = hContact; + mir_sntprintf(ppd.lptzContactName, SIZEOF(ppd.lptzContactName), + _T("%s - %s"), TranslateTS(pszDesc), DB::Contact::DisplayName(hContact)); + } + else + { + mir_tcsncpy(ppd.lptzContactName, TranslateT("Reminder"), SIZEOF(ppd.lptzContactName)); + } + mir_tcsncpy(ppd.lptzText, pszMsg, MAX_SECONDLINE); + + ppd.lchIcon = GetAnnivIcon(CEvent(eventType, DaysToAnniv)); + + switch (eventType) + { + case CEvent::BIRTHDAY: + switch (DB::Setting::GetByte(SET_POPUP_BIRTHDAY_COLORTYPE, POPUP_COLOR_CUSTOM)) + { + case POPUP_COLOR_WINDOWS: + ppd.colorBack = GetSysColor(COLOR_BTNFACE); + ppd.colorText = GetSysColor(COLOR_WINDOWTEXT); + break; + + case POPUP_COLOR_CUSTOM: + ppd.colorBack = DB::Setting::GetDWord(SET_POPUP_BIRTHDAY_COLOR_BACK, RGB(192,180,30)); + ppd.colorText = DB::Setting::GetDWord(SET_POPUP_BIRTHDAY_COLOR_TEXT, 0); + break; + } + break; + + case CEvent::ANNIVERSARY: + switch (DB::Setting::GetByte(SET_POPUP_ANNIVERSARY_COLORTYPE, POPUP_COLOR_CUSTOM)) + { + case POPUP_COLOR_WINDOWS: + ppd.colorBack = GetSysColor(COLOR_BTNFACE); + ppd.colorText = GetSysColor(COLOR_WINDOWTEXT); + break; + + case POPUP_COLOR_CUSTOM: + ppd.colorBack = DB::Setting::GetDWord(SET_POPUP_ANNIVERSARY_COLOR_BACK, RGB(90, 190, 130)); + ppd.colorText = DB::Setting::GetDWord(SET_POPUP_ANNIVERSARY_COLOR_TEXT, 0); + break; + } + } + return PUAddPopUpT(&ppd); + } + return 1; +} + +/** + * Flash contact list's contact icon. + * + * @param hContact - contact whose icon to flash + * @param evt - structure specifying the next anniversary + * + * @return nothing + **/ +static VOID NotifyFlashCListIcon(HANDLE hContact, const CEvent &evt) +{ + if (gRemindOpts.bFlashCList && evt._wDaysLeft == 0) + { + CLISTEVENT cle ={0}; + TCHAR szMsg[MAX_PATH]; + + cle.cbSize = sizeof(CLISTEVENT); + cle.hContact = hContact; + cle.flags = CLEF_URGENT|CLEF_TCHAR; + cle.hDbEvent = NULL; + + switch (evt._eType) { + case CEvent::BIRTHDAY: + { + mir_sntprintf(szMsg, SIZEOF(szMsg), + TranslateT("%s has %s today."), + DB::Contact::DisplayName(hContact), + TranslateT("Birthday")); + cle.hIcon = IcoLib_GetIcon(ICO_COMMON_BIRTHDAY); + } + break; + + case CEvent::ANNIVERSARY: + { + mir_sntprintf(szMsg, SIZEOF(szMsg), + TranslateT("%s has %s today."), + DB::Contact::DisplayName(hContact), + TranslateT("an anniversary")); + cle.hIcon = IcoLib_GetIcon(ICO_COMMON_ANNIVERSARY); + } + break; + + default: + szMsg[0] = NULL; + } + cle.ptszTooltip = szMsg; + + // pszService = NULL get error (crash), + // pszService = "dummy" get 'service not fount' and continue; + cle.pszService = "dummy"; + cle.lParam = NULL; + + CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle); + } +} + +/** + * Play a sound for the nearest upcoming anniversary + * + * @param evt - structure specifying the next anniversary + * + * @retval 0 if sound was played + * @retval 1 otherwise + **/ +static BYTE NotifyWithSound(const CEvent &evt) +{ + if (evt._wDaysLeft <= min(DB::Setting::GetByte(SET_REMIND_SOUNDOFFSET, DEFVAL_REMIND_SOUNDOFFSET), gRemindOpts.wDaysEarlier)) + { + switch (evt._eType) + { + case CEvent::BIRTHDAY: + SkinPlaySound(evt._wDaysLeft == 0 ? SOUND_BIRTHDAY_TODAY : SOUND_BIRTHDAY_SOON); + return 0; + + case CEvent::ANNIVERSARY: + SkinPlaySound(SOUND_ANNIVERSARY); + return 0; + } + } + return 1; +} + +/*********************************************************************************************************** + * "check for anniversary" functions + ***********************************************************************************************************/ + +static LPCTSTR ContactGender(HANDLE hContact) +{ + switch (GenderOf(hContact)) + { + case 'M': return TranslateT("He"); + case 'F': return TranslateT("She"); + } + return TranslateT("He/She"); +} + +static BOOLEAN CheckAnniversaries(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify) +{ + INT numAnniversaries = 0; + INT Diff; + MAnnivDate mta; + INT i; + TCHAR szAnniv[MAX_PATH]; + TCHAR strMsg[MAX_SECONDLINE]; + BOOLEAN bOverflow = FALSE; + WORD wDaysEarlier; + + if ((gRemindOpts.RemindState == REMIND_ANNIV) || (gRemindOpts.RemindState == REMIND_ALL)) + { + for (i = 0; i < ANID_LAST && !mta.DBGetAnniversaryDate(hContact, i); i++) + { + mta.DBGetReminderOpts(hContact); + + if (mta.RemindOption() != BST_UNCHECKED) + { + wDaysEarlier = (mta.RemindOption() == BST_CHECKED) ? mta.RemindOffset() : -1; + if (wDaysEarlier == (WORD)-1) + { + wDaysEarlier = gRemindOpts.wDaysEarlier; + } + + Diff = mta.CompareDays(Now); + if ((Diff >= 0) && (Diff <= wDaysEarlier)) + { + if (evt._wDaysLeft > Diff) + { + evt._wDaysLeft = Diff; + evt._eType = CEvent::ANNIVERSARY; + } + numAnniversaries++; + + // create displayed text for popup + if (bNotify && !bOverflow) + { + // first anniversary found + if (numAnniversaries == 1) + { + mir_sntprintf(szAnniv, MAX_PATH, + TranslateT("%s has the following anniversaries:\0"), + ContactGender(hContact)); + mir_tcsncpy(strMsg, szAnniv, mir_tcslen(szAnniv)); + } + switch (Diff) + { + case 0: + { + mir_sntprintf(szAnniv, MAX_PATH, + TranslateT("%d. %s today\0"), + mta.Age(), mta.Description()); + } + break; + + case 1: + { + mir_sntprintf(szAnniv, MAX_PATH, + TranslateT("%d. %s tomorrow\0"), + mta.Age() + 1, mta.Description()); + } + break; + + default: + { + mir_sntprintf(szAnniv, MAX_PATH, + TranslateT("%d. %s in %d days\0"), + mta.Age() + 1, mta.Description(), Diff); + } + } + if (mir_tcslen(szAnniv) >= MAX_SECONDLINE - mir_tcslen(strMsg)) + { + if (strMsg) + mir_tcsncat(strMsg, _T("\n...\0"), SIZEOF(strMsg)); + else + mir_tcsncpy(strMsg, _T("\n...\0"), mir_tcslen(_T("\n...\0"))); + bOverflow = TRUE; + } + else + { + if (strMsg) + mir_tcsncat(strMsg, _T("\n- \0"), SIZEOF(strMsg)); + else + mir_tcsncpy(strMsg, _T("\n- \0"), mir_tcslen(_T("\n- \0"))); + mir_tcsncat(strMsg, szAnniv, SIZEOF(strMsg)); + } + } + } + } + } + } + // show one popup for all anniversaries + if (numAnniversaries != 0 && bNotify) + { + NotifyWithPopup(hContact, CEvent::ANNIVERSARY, Diff, LPGENT("Anniversaries"), strMsg); + } + return numAnniversaries != 0; +} + +/** + * This function checks, whether a contact has a birthday and it is within the period of time to remind of or not. + * + * @param hContact - the contact to check + * @param Now - current time + * @param evt - the reference to a structure, which retrieves the resulting DTB + * @param bNotify - if TRUE, a popup will be displayed for a contact having birthday within the next few days. + * @param LastAnswer - this parameter is used for the automatic backup function + * + * @retval TRUE - contact has a birthday to remind of + * @retval FALSE - contact has no birthday or it is not within the desired period of time. + **/ +static BOOLEAN CheckBirthday(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify, PWORD LastAnwer) +{ + BOOLEAN result = FALSE; + + if (gRemindOpts.RemindState == REMIND_BIRTH || gRemindOpts.RemindState == REMIND_ALL) + { + MAnnivDate mtb; + + if (!mtb.DBGetBirthDate(hContact)) + { + INT Diff; + WORD wDaysEarlier; + + mtb.DBGetReminderOpts(hContact); + + // make backup of each protocol based birthday + if (DB::Setting::GetByte(SET_REMIND_SECUREBIRTHDAY, TRUE)) + { + mtb.BackupBirthday(hContact, NULL, 0, LastAnwer); + } + + if (mtb.RemindOption() != BST_UNCHECKED) + { + wDaysEarlier = (mtb.RemindOption() == BST_CHECKED) ? mtb.RemindOffset() : -1; + if (wDaysEarlier == (WORD)-1) + { + wDaysEarlier = gRemindOpts.wDaysEarlier; + } + + Diff = mtb.CompareDays(Now); + if ((Diff >= 0) && (Diff <= wDaysEarlier)) + { + if (evt._wDaysLeft > Diff) + { + evt._wDaysLeft = Diff; + evt._eType = CEvent::BIRTHDAY; + } + + if (bNotify) + { + TCHAR szMsg[MAXDATASIZE]; + WORD cchMsg = 0; + + switch (Diff) + { + case 0: + { + cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg), + TranslateT("%s has birthday today."), + DB::Contact::DisplayName(hContact)); + } + break; + + case 1: + { + cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg), + TranslateT("%s has birthday tomorrow."), + DB::Contact::DisplayName(hContact)); + } + break; + + default: + { + cchMsg = mir_sntprintf(szMsg, SIZEOF(szMsg), + TranslateT("%s has birthday in %d days."), + DB::Contact::DisplayName(hContact), Diff); + } + } + mir_sntprintf(szMsg + cchMsg, SIZEOF(szMsg) - cchMsg, + TranslateT("\n%s becomes %d years old."), + ContactGender(hContact), mtb.Age(&Now) + (Diff > 0)); + + NotifyWithPopup(hContact, CEvent::BIRTHDAY, Diff, mtb.Description(), szMsg); + } + result = TRUE; + } + } + } + } + return result; +} + +/** + * This function checks one contact. It is mainly used for clist extra icon rebuild notification handler. + * + * @param hContact - the contact to check + * @param Now - current time + * @param evt - the reference to a structure, which retrieves the resulting DTB + * @param bNotify - if TRUE, a popup will be displayed for a contact having birthday within the next few days. + * @param LastAnswer - this parameter is used for the automatic backup function + * + * @return nothing + **/ +static VOID CheckContact(HANDLE hContact, MTime &Now, CEvent &evt, BOOLEAN bNotify, PWORD LastAnwer = 0) +{ + // ignore meta subcontacts here as their birthday information are collected explicitly + if (hContact && + (!gRemindOpts.bCheckVisibleOnly || !DB::Setting::GetByte(hContact, MOD_CLIST, "Hidden", FALSE)) && + (!DB::MetaContact::IsSub(hContact))) + { + CEvent ca; + + if (CheckBirthday(hContact, Now, ca, bNotify, LastAnwer) || + CheckAnniversaries(hContact, Now, ca, bNotify)) + { + evt << ca; + + if (bNotify) + { + NotifyFlashCListIcon(hContact, ca); + } + } + NotifyWithExtraIcon(hContact, ca); + } +} + +/** + * This function checks all contacts. + * + * @param notify - notification type + * + * @return nothing + **/ +VOID SvcReminderCheckAll(const ENotify notify) +{ + if (gRemindOpts.RemindState != REMIND_OFF) + { + HANDLE hContact; + CEvent evt; + MTime now; + WORD a1 = 0; + + now.GetLocalTime(); + + //walk through all the contacts stored in the DB + for (hContact = DB::Contact::FindFirst(); + hContact != NULL; + hContact = DB::Contact::FindNext(hContact)) + { + CheckContact(hContact, now, evt, notify != NOTIFY_CLIST, &a1); + } + + if (notify != NOTIFY_CLIST) + { + // play sound for the next anniversary + NotifyWithSound(evt); + + // popup anniversary list + if (DB::Setting::GetByte(SET_ANNIVLIST_POPUP, FALSE)) + { + DlgAnniversaryListShow(0, 0); + } + + if (evt._wDaysLeft > gRemindOpts.wDaysEarlier && notify == NOTIFY_NOANNIV) + { + NotifyWithPopup(NULL, CEvent::NONE, 0, NULL, TranslateT("No anniversaries to remind of")); + } + } + UpdateTimer(FALSE); + } +} + +/*********************************************************************************************************** + * Event Handler functions + ***********************************************************************************************************/ + +/** + * This is the notification handler to tell reminder to reload required icons. + * The reminder only loads icons to clist, which are really required at the moment. + * This should help to save a bit memory. + * + * @param: wParam - not used + * @param: lParam - not used + * + * @return This function must return 0 in order to continue in the notification chain. + **/ +static INT OnCListRebuildIcons(WPARAM, LPARAM) +{ + UINT i; + + for (i = 0; i < SIZEOF(ghCListAnnivIcons); i++) + { + ghCListAnnivIcons[i] = INVALID_HANDLE_VALUE; + } + for (i = 0; i < SIZEOF(ghCListBirthdayIcons); i++) + { + ghCListBirthdayIcons[i] = INVALID_HANDLE_VALUE; + } + return 0; +} + +/** + * This function is the notification handler for clist extra icons to be applied for a contact. + * + * @param hContact - handle to the contact whose extra icon is to apply + * @param lParam - not used + * + * @return This function must return 0 in order to continue in the notification chain. + **/ +INT OnCListApplyIcon(HANDLE hContact, LPARAM) +{ + if (gRemindOpts.RemindState != REMIND_OFF) + { + CEvent evt; + MTime now; + + now.GetLocalTime(); + CheckContact(hContact, now, evt, FALSE); + } + return 0; +} + +/** + * This is a notification handler for changed contact settings. + * If any anniversary setting has changed for a meta sub contact, + * the parental meta contact is rescanned. + * + * @param hContact - handle of the contect the notification was fired for + * @param pdbcws - pointer to a DBCONTACTWRITESETTING structure + * + * @return This function must return 0 in order to continue in the notification chain. + **/ +static INT OnContactSettingChanged(HANDLE hContact, DBCONTACTWRITESETTING* pdbcws) +{ + if (hContact && // valid contact not owner! + ghCListIA && // extraicons active + pdbcws && pdbcws->szSetting && // setting structure valid + (pdbcws->value.type < DBVT_DWORD) && // anniversary datatype + (gRemindOpts.RemindState != REMIND_OFF) && // reminder active + (!strncmp(pdbcws->szSetting, "Birth", 5) || + !strncmp(pdbcws->szSetting, "Anniv", 5) || + !strncmp(pdbcws->szSetting, "DOB", 3))) + { + HANDLE hMeta = DB::MetaContact::GetMeta(hContact); + WORD LastAnswer = IDNONE; + CEvent evt; + MTime now; + + // check metacontact instead of subcontact + if (hMeta) + { + hContact = hMeta; + } + now.GetLocalTime(); + if (!strcmp(pdbcws->szModule, SvcReminderGetMyBirthdayModule())) + { + CheckContact(hContact, now, evt, FALSE, &LastAnswer); + } + else + { + CheckContact(hContact, now, evt, FALSE, 0); + } + } + return 0; +} + +#define TBB_IDBTN "CheckAnniv" +#define TBB_ICONAME TOOLBARBUTTON_ICONIDPREFIX TBB_IDBTN TOOLBARBUTTON_ICONIDPRIMARYSUFFIX + +/** + * This function is called by the ME_TTB_MODULELOADED event. + * It adds a set of buttons to the TopToolbar plugin. + * + * @param none + * + * @return nothing + **/ + +VOID SvcReminderOnTopToolBarLoaded() +{ + TTBButton ttb = {0}; + ttb.cbSize = sizeof(ttb); + + ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP; + ttb.pszService = MS_USERINFO_REMINDER_CHECK; + ttb.name = ttb.pszTooltipUp = LPGEN("Check anniversaries"); + ttb.hIconHandleUp = Skin_GetIconHandle(ICO_COMMON_BIRTHDAY); + TopToolbar_AddButton(&ttb); +} + + +/*********************************************************************************************************** + * services + ***********************************************************************************************************/ + +/** + * This is the service function for MS_USERINFO_REMINDER_CHECK. + * + * @param: wParam - not used + * @param: lParam - not used + * + * @return 0 + **/ +static INT_PTR CheckService(WPARAM, LPARAM) +{ + if (gRemindOpts.RemindState != REMIND_OFF) + { + SvcReminderCheckAll(NOTIFY_NOANNIV); + } + return 0; +} + +/** + * This is the service function for MS_USERINFO_REMINDER_AGGRASIVEBACKUP. + * + * @param hContact - handle to single contact or NULL to backup all + * @param lParam - if 1, the messagebox will not be displayed + * + * return: 0 + **/ +static INT_PTR BackupBirthdayService(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + MAnnivDate mdb; + + if (hContact) + { + if (!mdb.DBGetBirthDate(hContact)) + { + mdb.BackupBirthday(hContact, NULL, TRUE); + } + } + else + { + WORD a1 = 0; + + //walk through all the contacts stored in the DB + for (hContact = DB::Contact::FindFirst(); + hContact != NULL; + hContact = DB::Contact::FindNext(hContact)) + { + if (!DB::MetaContact::IsSub(hContact) && !mdb.DBGetBirthDate(hContact)) + { + mdb.BackupBirthday(hContact, NULL, TRUE, &a1); + } + } + } + + if (lParam != TRUE) + { + MSGBOX mBox; + + mBox.cbSize = sizeof(MSGBOX); + mBox.hParent = NULL; + mBox.hiLogo = IcoLib_GetIcon(ICO_COMMON_BIRTHDAY); + mBox.uType = MB_ICON_INFO; + mBox.ptszTitle = TranslateT("Update custom birthday"); + mBox.ptszMsg = TranslateT("Backing up and syncing all birthdays complete!"); + MsgBoxService(NULL, (LPARAM)&mBox); + } + return 0; +} + +/** + * This function returns a constant pointer to the module the date should be saved to + * + * @param none + * + * @return module to write birthday information to, MOD_MBIRTHDAY by default + **/ +LPCSTR SvcReminderGetMyBirthdayModule() +{ + return ((DB::Setting::GetByte(SET_REMIND_BIRTHMODULE, DEFVAL_REMIND_BIRTHMODULE) == 1) ? USERINFO : MOD_MBIRTHDAY); +} + + +/*********************************************************************************************************** + * timer stuff + ***********************************************************************************************************/ + +/** + * Timer procedure, called if date changed. This updates clist icons. + * + * @param hwnd - not used + * @param uMsg - not used + * @param idEvent - not used + * @param dwTime - not used + * @return nothing + **/ +static VOID CALLBACK TimerProc_DateChanged(HWND, UINT, UINT_PTR, DWORD) +{ + static MTime last; + MTime now; + + now.GetLocalTime(); + if (now.Day() > last.Day() || now.Month() > last.Month() || now.Year() > last.Year()) { + SvcReminderCheckAll(NOTIFY_CLIST); + last = now; + } +} + +/** + * Timer procedure, called again and again if the notification interval ellapsed + * + * @param hwnd - not used + * @param uMsg - not used + * @param idEvent - not used + * @param dwTime - not used + * + * @return nothing + **/ +static VOID CALLBACK TimerProc_Check(HWND, UINT, UINT_PTR, DWORD) +{ + SvcReminderCheckAll(NOTIFY_POPUP); +} + +/** + * Load timers or update them. + * + * @param bStartup - is only TRUE if module is loaded to indicate startup process + * + * @return nothing + **/ +static VOID UpdateTimer(BOOLEAN bStartup) +{ + LONG wNotifyInterval = 60 * 60 * (LONG)DB::Setting::GetWord(MODNAME, SET_REMIND_NOTIFYINTERVAL, DEFVAL_REMIND_NOTIFYINTERVAL); + MTime now, last; + + now.GetTimeUTC(); + + if (bStartup) { + last.DBGetStamp(NULL, MODNAME, SET_REMIND_LASTCHECK); + + // if last check occured at least one day before just do it on startup again + if (now.Year() > last.Year() || now.Month() > last.Month() || now.Day() > last.Day() || DB::Setting::GetByte(SET_REMIND_CHECKON_STARTUP, FALSE)) + wNotifyInterval = 5; + else + wNotifyInterval -= now.Compare(last); + + ghRemindDateChangeTimer = SetTimer(0, 0, 1000 * 60 * 5, (TIMERPROC)TimerProc_DateChanged); + } + else { + now.DBWriteStamp(NULL, MODNAME, SET_REMIND_LASTCHECK); + } + // wait at least 5 seconds before checking at startup, to give miranda a better chance to load faster + KillTimer(0, ghRemindTimer); + ghRemindTimer = SetTimer(0, 0, 1000 * wNotifyInterval, TimerProc_Check); +} + +/*********************************************************************************************************** + * module loading & unloading + ***********************************************************************************************************/ + +VOID SvcReminderEnable(BOOLEAN bEnable) +{ + if (bEnable) // Reminder is on + { + if (myGlobals.ExtraIconsServiceExist && (ExtraIcon == INVALID_HANDLE_VALUE)) + { + EXTRAICON_INFO ico = {0}; + ico.type = EXTRAICON_TYPE_ICOLIB; + ico.cbSize=sizeof(ico); + ico.name="Reminder"; + ico.description="Reminder (uinfoex)"; + ico.descIcon=ICO_COMMON_ANNIVERSARY; + ExtraIcon=(HANDLE)CallService(MS_EXTRAICON_REGISTER, (WPARAM)&ico, 0); + ZeroMemory(&ico,sizeof(ico)); + } + // init hooks + if (!ghCListIR) + { + ghCListIR = HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, (MIRANDAHOOK)OnCListRebuildIcons); + } + + if (!ghCListIA) + { + ghCListIA = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, (MIRANDAHOOK)OnCListApplyIcon); + } + if (!ghSettingsChanged && !myGlobals.UseDbxTree) + { + ghSettingsChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, (MIRANDAHOOK)OnContactSettingChanged); + } + + // reinit reminder options + gRemindOpts.RemindState = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED); + gRemindOpts.wDaysEarlier = DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET); + gRemindOpts.bCListExtraIcon = DB::Setting::GetByte(SET_REMIND_EXTRAICON, 1); + gRemindOpts.bCheckVisibleOnly = DB::Setting::GetByte(SET_REMIND_CHECKVISIBLE, DEFVAL_REMIND_CHECKVISIBLE); + gRemindOpts.bFlashCList = DB::Setting::GetByte(SET_REMIND_FLASHICON, FALSE); + gRemindOpts.bPopups = ServiceExists(MS_POPUP_ADDPOPUPT) && DB::Setting::GetByte(SET_POPUP_ENABLED, DEFVAL_POPUP_ENABLED); + + // init the timer + UpdateTimer(TRUE); + } + else // Reminder is off + { + HANDLE hContact; + + for (hContact = DB::Contact::FindFirst(); + hContact != NULL; + hContact = DB::Contact::FindNext(hContact)) + { + NotifyWithExtraIcon(hContact, CEvent()); + } + gRemindOpts.RemindState = REMIND_OFF; + SvcReminderUnloadModule(); + } +} + +/** + * This function is called by Miranda just after loading all system modules. + * + * @param none + * + * @return nothing + **/ +VOID SvcReminderOnModulesLoaded(VOID) +{ + // init clist extra icon structure + OnCListRebuildIcons(0, 0); + + SvcReminderEnable(DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) != REMIND_OFF); +} + +/** + * This function initially loads all required stuff for reminder. + * + * @param none + * + * @return nothing + **/ +VOID SvcReminderLoadModule(VOID) +{ + // init sounds + SKINSOUNDDESCEX ssd = { 0 }; + ssd.cbSize = sizeof(ssd); + ssd.pszSection = MODNAME; + + ssd.pszName = SOUND_BIRTHDAY_TODAY; + ssd.pszDescription = LPGEN("Birthday reminder"); + ssd.pszDefaultFile = "Sounds\\BirthDay.wav"; + Skin_AddSound(&ssd); + + ssd.pszName = SOUND_BIRTHDAY_SOON; + ssd.pszDescription = LPGEN("Birthday reminder: it's coming"); + ssd.pszDefaultFile = "Sounds\\BirthDayComing.wav"; + Skin_AddSound(&ssd); + + ssd.pszName = SOUND_ANNIVERSARY; + ssd.pszDescription = LPGEN("Anniversary Reminder"); + ssd.pszDefaultFile = "Sounds\\Reminder.wav"; + Skin_AddSound(&ssd); + + // create service functions + CreateServiceFunction(MS_USERINFO_REMINDER_CHECK, CheckService); + CreateServiceFunction(MS_USERINFO_REMINDER_AGGRASIVEBACKUP, BackupBirthdayService); + + // register hotkey + HOTKEYDESC hk = { 0 }; + hk.cbSize = sizeof(HOTKEYDESC); + hk.pszSection = MODNAME; + hk.pszName = "ReminderCheck"; + hk.pszDescription = LPGEN("Check anniversaries"); + hk.pszService = MS_USERINFO_REMINDER_CHECK; + Hotkey_Register(&hk); +} + +/** + * This function unloads the reminder module. + * + * @param none + * + * @return nothing + **/ +VOID SvcReminderUnloadModule(VOID) +{ + // kill timers + KillTimer(0, ghRemindTimer); + ghRemindTimer = 0; + KillTimer(0, ghRemindDateChangeTimer); + ghRemindDateChangeTimer = 0; + + // unhook event handlers + UnhookEvent(ghCListIR); + ghCListIR = 0; + UnhookEvent(ghCListIA); + ghCListIA = 0; + UnhookEvent(ghSettingsChanged); + ghSettingsChanged = 0; +} -- cgit v1.2.3