From 48540940b6c28bb4378abfeb500ec45a625b37b6 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 15 May 2012 10:38:20 +0000 Subject: initial commit git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/UserInfoEx/dlg_anniversarylist.cpp | 1161 ++++++++++++++++++++++++++++ 1 file changed, 1161 insertions(+) create mode 100644 plugins/UserInfoEx/dlg_anniversarylist.cpp (limited to 'plugins/UserInfoEx/dlg_anniversarylist.cpp') diff --git a/plugins/UserInfoEx/dlg_anniversarylist.cpp b/plugins/UserInfoEx/dlg_anniversarylist.cpp new file mode 100644 index 0000000000..a2daefd45d --- /dev/null +++ b/plugins/UserInfoEx/dlg_anniversarylist.cpp @@ -0,0 +1,1161 @@ +/* +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/dlg_anniversarylist.cpp $ +Revision : $Revision: 202 $ +Last change on : $Date: 2010-09-24 23:46:57 +0400 (Пт, 24 сен 2010) $ +Last change by : $Author: ing.u.horn $ + +=============================================================================== +*/ + +/** + * System Includes: + **/ +#include "commonheaders.h" +#include "svc_gender.h" +#include "svc_reminder.h" +#include "dlg_anniversarylist.h" + +#include "m_message.h" +#include "m_email.h" + +#define IsLeap(wYear) (!(((wYear) % 4 != 0) || (((wYear) % 100 == 0) && ((wYear) % 400 != 0)))) + +class CAnnivList; + +static CAnnivList *gpDlg = NULL; + +/*********************************************************************************************************** + * class CAnnivList + ***********************************************************************************************************/ + +class CAnnivList +{ + HWND _hDlg; + HWND _hList; + SIZE _sizeMin; + RECT _rcWin; + SHORT _sortOrder; + INT _sortHeader; + INT _curSel; + INT _numRows; + BYTE _bRemindEnable; + HANDLE _mHookExit; + bool _wmINIT; + + typedef INT (CALLBACK* CMPPROC)(LPARAM, LPARAM LPARAM); + + enum EColumn + { + COLUMN_ETA = 0, + COLUMN_CONTACT, + COLUMN_PROTO, + COLUMN_AGE, + COLUMN_DESC, + COLUMN_DATE, + }; + + enum EFilter + { + FILTER_ALL = 0, + FILTER_BIRTHDAY, + FILTER_ANNIV, + FILTER_DISABLED_REMINDER + }; + + struct CFilter + { + WORD wDaysBefore; + BYTE bFilterIndex; + LPSTR pszProto; + LPTSTR pszAnniv; + + CFilter() + { + wDaysBefore = (WORD)-1; + bFilterIndex = 0; + pszProto = NULL; + pszAnniv = NULL; + } + } _filter; + + struct CItemData + { + HANDLE _hContact; + MAnnivDate* _pDate; + WORD _wDaysBefore; + BYTE _wReminderState; + + CItemData(HANDLE hContact, MAnnivDate &date) + { + _hContact = hContact; + _wReminderState = date.RemindOption(); + _wDaysBefore = date.RemindOffset(); + _pDate = new MAnnivDate(date); + } + + ~CItemData() + { + if (_pDate) + { + // save changes + if (_wReminderState != _pDate->RemindOption() || _wDaysBefore != _pDate->RemindOffset()) + { + _pDate->RemindOffset(_wDaysBefore); + _pDate->RemindOption(_wReminderState); + _pDate->DBWriteReminderOpts(_hContact); + } + delete _pDate; + _pDate = NULL; + } + } + }; + + /** + * This class handles the movement of child controls on size change. + **/ + class CAnchor + { + public: + enum EAnchor + { + ANCHOR_LEFT = 1, + ANCHOR_RIGHT = 2, + ANCHOR_TOP = 4, + ANCHOR_BOTTOM = 8, + ANCHOR_ALL = ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_TOP | ANCHOR_BOTTOM + }; + + private: + WINDOWPOS* _wndPos; + HDWP _hdWnds; + RECT _rcParent; + + VOID _ScreenToClient(HWND hWnd, LPRECT rc) + { + POINT pt = { rc->left, rc->top }; + + ScreenToClient(hWnd, &pt); + rc->right += pt.x - rc->left; + rc->bottom += pt.y - rc->top; + rc->left = pt.x; + rc->top = pt.y; + } + + VOID _MoveWindow(HWND hWnd, INT anchors) + { + if (!(_wndPos->flags & SWP_NOSIZE)) + { + RECT rcc = _CalcPos(hWnd, anchors); + MoveWindow(hWnd, rcc.left, rcc.top, rcc.right - rcc.left, rcc.bottom - rcc.top, FALSE); + } + } + + RECT _CalcPos(HWND hWnd, INT anchors) + { + RECT rcc; + + GetWindowRect(hWnd, &rcc); + _ScreenToClient(_wndPos->hwnd, &rcc); + if (!(_wndPos->flags & SWP_NOSIZE)) + { + // calculate difference between new and old size + const INT cx = _wndPos->cx - _rcParent.right + _rcParent.left; + const INT cy = _wndPos->cy - _rcParent.bottom + _rcParent.top; + + if (cx != 0 || cy != 0) + { + // move client rect points to the desired new position + if (!(anchors & ANCHOR_LEFT) || (anchors & ANCHOR_RIGHT)) + { + rcc.right += cx; + } + if (!(anchors & ANCHOR_TOP) || (anchors & ANCHOR_BOTTOM)) + { + rcc.bottom += cy; + } + if ((anchors & ANCHOR_RIGHT) && (!(anchors & ANCHOR_LEFT))) + { + rcc.left += cx; + } + if ((anchors & ANCHOR_BOTTOM) && (!(anchors & ANCHOR_TOP))) + { + rcc.top += cy; + } + } + } + return rcc; + } + + public: + CAnchor(WINDOWPOS* wndPos, SIZE minSize) + { + GetWindowRect(wndPos->hwnd, &_rcParent); + if (wndPos->cx < minSize.cx) { + wndPos->cx = minSize.cx; + } + if (wndPos->cy < minSize.cy) { + wndPos->cy = minSize.cy; + } + _wndPos = wndPos; + _hdWnds = BeginDeferWindowPos(2); + } + + ~CAnchor() + { + EndDeferWindowPos(_hdWnds); + } + + VOID MoveCtrl(WORD idCtrl, INT anchors) + { + if (!(_wndPos->flags & SWP_NOSIZE)) + { + HWND hCtrl = GetDlgItem(_wndPos->hwnd, idCtrl); + RECT rcc = _CalcPos(hCtrl, anchors); + _hdWnds = DeferWindowPos( + _hdWnds, //HDWP hWinPosInfo + hCtrl, //HWND hWnd + HWND_NOTOPMOST, //hWndInsertAfter + rcc.left, //int x + rcc.top, //int y + rcc.right - rcc.left, + rcc.bottom - rcc.top, + SWP_NOZORDER //UINT uFlags + ); + } + } + }; + + /** + * This compare function is used by ListView_SortItemsEx to sort the listview. + * + * @param iItem1 - index of the first item + * @param iItem2 - index of the second item + * @param pDlg - pointer to the class' object + * + * @return This function returns a number indicating comparison result. + **/ + static INT CALLBACK cmpProc(INT iItem1, INT iItem2, CAnnivList* pDlg) + { + INT result; + + if (pDlg) { + TCHAR szText1[MAX_PATH]; + TCHAR szText2[MAX_PATH]; + + szText1[0] = szText2[0] = 0; + switch (pDlg->_sortHeader) { + case COLUMN_CONTACT: + case COLUMN_PROTO: + case COLUMN_DESC: + { + ListView_GetItemText(pDlg->_hList, iItem1, pDlg->_sortHeader, szText1, MAX_PATH); + ListView_GetItemText(pDlg->_hList, iItem2, pDlg->_sortHeader, szText2, MAX_PATH); + result = pDlg->_sortOrder * mir_tcscmp(szText1, szText2); + } break; + + case COLUMN_AGE: + case COLUMN_ETA: + { + ListView_GetItemText(pDlg->_hList, iItem1, pDlg->_sortHeader, szText1, MAX_PATH); + ListView_GetItemText(pDlg->_hList, iItem2, pDlg->_sortHeader, szText2, MAX_PATH); + result = pDlg->_sortOrder * (_ttoi(szText1) - _ttoi(szText2)); + } break; + + case COLUMN_DATE: + { + CItemData *id1 = pDlg->ItemData(iItem1), + *id2 = pDlg->ItemData(iItem2); + + if (PtrIsValid(id1) && PtrIsValid(id2)) { + result = pDlg->_sortOrder * id1->_pDate->Compare(*id2->_pDate); + break; + } + } + default: + { + result = 0; + } + } + } + else { + result = 0; + } + return result; + } + + /** + * This static method is the window procedure for the dialog. + * + * @param hDlg - handle of the dialog window + * @param uMsg - message to handle + * @param wParam - message dependend parameter + * @param lParam - message dependend parameter + * + * @return depends on message + **/ + static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + CAnnivList *pDlg = (CAnnivList*)GetUserData(hDlg); + + switch (uMsg) + { + case WM_INITDIALOG: + { + INT i = 0; + HWND hCtrl; + HICON hIcon; + RECT rc; + + // link the class to the window handle + pDlg = (CAnnivList*)lParam; + if (!pDlg) { + break; + } + SetUserData(hDlg, lParam); + pDlg->_hDlg = hDlg; + + // init pointer listview control + pDlg->_hList = GetDlgItem(hDlg, EDIT_ANNIVERSARY_DATE); + if (!pDlg->_hList) { + break; + } + + // set icons + hIcon = IcoLib_GetIcon(ICO_DLG_ANNIVERSARY); + SendDlgItemMessage(hDlg, IDC_HEADERBAR, WM_SETICON, 0, (LPARAM)hIcon); + SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); + + // insert columns into the listboxes + ListView_SetExtendedListViewStyle(pDlg->_hList, LVS_EX_FULLROWSELECT); + + // add columns + if (pDlg->AddColumn(CAnnivList::COLUMN_ETA, LPGENT("ETA"), 40) || + pDlg->AddColumn(CAnnivList::COLUMN_CONTACT, LPGENT("Contact"), 160) || + pDlg->AddColumn(CAnnivList::COLUMN_PROTO, LPGENT("Proto"), 50) || + pDlg->AddColumn(CAnnivList::COLUMN_AGE, LPGENT("Age/Nr."), 40) || + pDlg->AddColumn(CAnnivList::COLUMN_DESC, LPGENT("Anniversary"), 100) || + pDlg->AddColumn(CAnnivList::COLUMN_DATE, LPGENT("Date"), 80)) + { + break; + } + + TranslateDialogDefault(hDlg); + + // save minimal size + GetWindowRect(hDlg, &rc); + pDlg->_sizeMin.cx = rc.right - rc.left; + pDlg->_sizeMin.cy = rc.bottom - rc.top; + + // restore position and size + Utils_RestoreWindowPosition(hDlg, NULL, MODNAME, "AnnivDlg_"); + + //save win pos + GetWindowRect(hDlg, &pDlg->_rcWin); + + // add filter strings + if (hCtrl = GetDlgItem(hDlg, COMBO_VIEW)) { + ComboBox_AddString(hCtrl, TranslateT("All contacts")); + ComboBox_AddString(hCtrl, TranslateT("Birthdays only")); + ComboBox_AddString(hCtrl, TranslateT("Anniversaries only")); + ComboBox_AddString(hCtrl, TranslateT("Disabled reminder")); + ComboBox_SetCurSel(hCtrl, pDlg->_filter.bFilterIndex); + } + + // init reminder groups + pDlg->_bRemindEnable = DB::Setting::GetByte(SET_REMIND_ENABLED, DEFVAL_REMIND_ENABLED) != REMIND_OFF; + if (hCtrl = GetDlgItem(hDlg, CHECK_REMIND)) { + Button_SetCheck(hCtrl, pDlg->_bRemindEnable ? BST_INDETERMINATE : BST_UNCHECKED); + EnableWindow(hCtrl, pDlg->_bRemindEnable); + } + + CheckDlgButton(hDlg, CHECK_POPUP, DB::Setting::GetByte(SET_ANNIVLIST_POPUP, FALSE)); + // set number of days to show contact in advance + SetDlgItemInt(hDlg, EDIT_DAYS, pDlg->_filter.wDaysBefore , FALSE); + if (hCtrl = GetDlgItem(hDlg, CHECK_DAYS)) { + Button_SetCheck(hCtrl, DB::Setting::GetByte(SET_ANNIVLIST_FILTER_DAYSENABLED, FALSE)); + DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_DAYS, BN_CLICKED), (LPARAM)hCtrl); + } + + pDlg->_wmINIT = false; + } + return TRUE; + + /** + * set propertysheet page's background white in aero mode + **/ + case WM_CTLCOLORSTATIC: + case WM_CTLCOLORDLG: + if (IsAeroMode()) + return (INT_PTR)GetStockBrush(WHITE_BRUSH); + break; + + case WM_NOTIFY: + { + switch (((LPNMHDR)lParam)->idFrom) + { + case EDIT_ANNIVERSARY_DATE: + { + switch (((LPNMHDR)lParam)->code) + { + /* + * handle changed selection + */ + case LVN_ITEMCHANGED: + { + CItemData* pid; + HWND hCheck; + + pDlg->_curSel = ((LPNMLISTVIEW)lParam)->iItem; + pid = pDlg->ItemData(pDlg->_curSel); + if (pid && pDlg->_bRemindEnable && (hCheck = GetDlgItem(hDlg, CHECK_REMIND))) { + SetDlgItemInt(hDlg, EDIT_REMIND, pid->_wDaysBefore, FALSE); + Button_SetCheck(hCheck, pid->_wReminderState); + DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(CHECK_REMIND, BN_CLICKED), (LPARAM)hCheck); + } + } break; + + /* + * resort the list + */ + case LVN_COLUMNCLICK: + { + LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; + + if (pDlg->_sortHeader == pnmv->iSubItem) { + pDlg->_sortOrder *= -1; + } + else { + pDlg->_sortOrder = 1; + pDlg->_sortHeader = pnmv->iSubItem; + } + ListView_SortItemsEx(pDlg->_hList, (CMPPROC)cmpProc, pDlg); + } break; + + /* + * show contact menu + */ + case NM_RCLICK: + { + CItemData* pid = pDlg->ItemData(pDlg->_curSel); + if (pid) { + HMENU hPopup = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) pid->_hContact, 0); + if (hPopup) { + POINT pt; + GetCursorPos(&pt); + TrackPopupMenu(hPopup, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hDlg, NULL); + DestroyMenu(hPopup); + } + } + } break; + + /* + * handle double click on contact: show message dialog + */ + case NM_DBLCLK: + { + CItemData* pid = pDlg->ItemData(((LPNMITEMACTIVATE)lParam)->iItem); + if (pid) { + CallService(MS_MSG_SENDMESSAGE,(WPARAM)pid->_hContact, NULL); + } + } + } + } + } + } break; + + case WM_COMMAND: + { + if (PtrIsValid(pDlg)) { + CItemData* pid = pDlg->ItemData(pDlg->_curSel); + + // process contact menu command + if (pid && CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)pid->_hContact)) { + break; + } + + switch (LOWORD(wParam)) + { + + /* + * enable/disable reminder checkbox is clicked + */ + case CHECK_REMIND: + { + if (pDlg->_bRemindEnable && HIWORD(wParam) == BN_CLICKED) { + BOOLEAN checkState = Button_GetCheck((HWND)lParam); + + EnableWindow(GetDlgItem(hDlg, EDIT_REMIND), checkState == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, SPIN_REMIND), checkState == BST_CHECKED); + EnableWindow(GetDlgItem(hDlg, TXT_REMIND5), checkState == BST_CHECKED); + if (pid && pid->_wReminderState != checkState) { + pid->_wReminderState = checkState; + } + } + } break; + + /* + * number of days to remind in advance is edited + */ + case EDIT_REMIND: + { + if (pid && pDlg->_bRemindEnable && HIWORD(wParam) == EN_CHANGE) { + WORD wDaysBefore = GetDlgItemInt(hDlg, LOWORD(wParam), NULL, FALSE); + if (pid->_wReminderState == BST_CHECKED && pid->_wDaysBefore != wDaysBefore) { + pid->_wDaysBefore = wDaysBefore; + } + } + } break; + + /* + * the filter to display only contacts which have an anniversary in a certain + * period of time is enabled/disabled + */ + case CHECK_DAYS: + { + if (HIWORD(wParam) == BN_CLICKED) { + BOOLEAN isChecked = Button_GetCheck((HWND)lParam); + EnableWindow(GetDlgItem(hDlg, EDIT_DAYS), isChecked); + EnableWindow(GetDlgItem(hDlg, TXT_DAYS), isChecked); + pDlg->_filter.wDaysBefore = isChecked ? GetDlgItemInt(hDlg, EDIT_DAYS, NULL, FALSE) : (WORD)-1; + pDlg->RebuildList(); + } + } break; + + /* + * the number of days a contact must have an anniversary in advance to be displayed is edited + */ + case EDIT_DAYS: + { + if (HIWORD(wParam) == EN_CHANGE) { + WORD wNewDays = GetDlgItemInt(hDlg, LOWORD(wParam), NULL, FALSE); + if (wNewDays != pDlg->_filter.wDaysBefore) { + pDlg->_filter.wDaysBefore = wNewDays; + pDlg->RebuildList(); + } + } + } break; + + /* + * the filter selection of the filter combobox has changed + */ + case COMBO_VIEW: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + pDlg->_filter.bFilterIndex = ComboBox_GetCurSel((HWND)lParam); + pDlg->RebuildList(); + } + } + } + } + } break; + + case WM_DRAWITEM: + return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam); + + case WM_MEASUREITEM: + return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); + + case WM_WINDOWPOSCHANGING: + { + if (PtrIsValid(pDlg)) { + WINDOWPOS* wndPos = (WINDOWPOS*)lParam; + if(!pDlg->_wmINIT && (wndPos->cx != 0 || wndPos->cy != 0)) { + //win pos change + if( (wndPos->cx == pDlg->_rcWin.right - pDlg->_rcWin.left) && + (wndPos->cy == pDlg->_rcWin.bottom - pDlg->_rcWin.top) ) { + //win pos change (store new pos) + GetWindowRect(hDlg, &pDlg->_rcWin); + } + //win size change + else { + // l change + if( (wndPos->cx < pDlg->_sizeMin.cx) && (wndPos->x > pDlg->_rcWin.left) ){ + wndPos->x = wndPos->x + wndPos->cx - pDlg->_sizeMin.cx; + wndPos->cx = pDlg->_sizeMin.cx; + } + // r change + else if (wndPos->cx < pDlg->_sizeMin.cx) { + wndPos->cx = pDlg->_sizeMin.cx; + } + + // t change + if( (wndPos->cy < pDlg->_sizeMin.cy) && (wndPos->y > pDlg->_rcWin.top) ) { + wndPos->y = wndPos->y + wndPos->cy - pDlg->_sizeMin.cy; + wndPos->cy = pDlg->_sizeMin.cy; + } + // b change + else if (wndPos->cy < pDlg->_sizeMin.cy) { + wndPos->cy = pDlg->_sizeMin.cy; + } + + pDlg->_rcWin.left = wndPos->x; + pDlg->_rcWin.right = wndPos->x + wndPos->cx; + pDlg->_rcWin.top = wndPos->y; + pDlg->_rcWin.bottom = wndPos->y + wndPos->cy; + } + } + + CAnchor anchor(wndPos, pDlg->_sizeMin); + INT anchorPos = CAnchor::ANCHOR_LEFT | CAnchor::ANCHOR_RIGHT | CAnchor::ANCHOR_TOP; + + anchor.MoveCtrl(IDC_HEADERBAR, anchorPos); + anchor.MoveCtrl(GROUP_STATS, anchorPos); + + // birthday list + anchor.MoveCtrl(EDIT_ANNIVERSARY_DATE, CAnchor::ANCHOR_ALL); + + anchorPos = CAnchor::ANCHOR_RIGHT | CAnchor::ANCHOR_BOTTOM; + + // filter group + anchor.MoveCtrl(GROUP_FILTER, anchorPos); + anchor.MoveCtrl(COMBO_VIEW, anchorPos); + anchor.MoveCtrl(CHECK_DAYS, anchorPos); + anchor.MoveCtrl(EDIT_DAYS, anchorPos); + anchor.MoveCtrl(TXT_DAYS, anchorPos); + + // filter group + anchor.MoveCtrl(GROUP_REMINDER, anchorPos); + anchor.MoveCtrl(CHECK_REMIND, anchorPos); + anchor.MoveCtrl(EDIT_REMIND, anchorPos); + anchor.MoveCtrl(SPIN_REMIND, anchorPos); + anchor.MoveCtrl(TXT_REMIND6, anchorPos); + anchor.MoveCtrl(CHECK_POPUP, anchorPos); + } + } break; + + /** + * This message is sent if eighter the user clicked on the close button or + * Miranda fires the ME_SYSTEM_SHUTDOWN event. + **/ + case WM_CLOSE: + { + DestroyWindow(hDlg); + } break; + + /** + * If the anniversary list is destroyed somehow, the data class must be + * deleted, too. + **/ + case WM_DESTROY: + { + if (PtrIsValid(pDlg)) { + SetUserData(hDlg, NULL); + delete pDlg; + } + MagneticWindows_RemoveWindow(hDlg); + } break; + } + return FALSE; + } + + /** + * This method adds a column to the listview. + * + * @param iSubItem - desired column index + * @param pszText - the header text + * @param defaultWidth - the default witdth + * + * @retval 0 if successful + * @retval 1 if failed + **/ + BOOLEAN AddColumn(INT iSubItem, LPCTSTR pszText, INT defaultWidth) + { + LVCOLUMN lvc; + CHAR pszSetting[MAXSETTING]; + + mir_snprintf(pszSetting, SIZEOF(pszSetting), "AnnivDlg_Col%d", iSubItem); + lvc.cx = DB::Setting::GetWord(pszSetting, defaultWidth); + lvc.mask = LVCF_WIDTH|LVCF_TEXT; + lvc.iSubItem = iSubItem; + lvc.pszText = TranslateTS(pszText); + return ListView_InsertColumn(_hList, lvc.iSubItem++, &lvc) == -1; + } + + /** + * This method sets the subitem text for the current contact + * + * @param iItem - index of the current row + * @param iSubItem - column to set text for + * @param pszText - text to insert + * + * @retval TRUE if successful + * @retval FALSE if failed + **/ + BOOLEAN AddSubItem(INT iItem, INT iSubItem, LPTSTR pszText) + { + LVITEM lvi; + if (iSubItem > 0) + { + lvi.iItem = iItem; + lvi.iSubItem = iSubItem; + lvi.pszText = pszText; + lvi.mask = LVIF_TEXT; + return ListView_SetItem(_hList, &lvi); + } + return FALSE; + } + + /** + * This method adds a row to the listview. + * + * @param pszText - text to insert + * @param lParam - pointer to the rows data + * + * @retval TRUE if successful + * @retval FALSE if failed + **/ + BOOLEAN AddItem(LPTSTR pszText, LPARAM lParam) + { + LVITEM lvi; + + if (!pszText) { + return FALSE; + } + lvi.iItem = 0; + lvi.iSubItem = 0; + lvi.pszText = pszText; + lvi.mask = LVIF_TEXT|TVIF_PARAM; + lvi.lParam = lParam; + return ListView_InsertItem(_hList, &lvi); + } + + /** + * This method adds a row to the listview. + * + * @param hContact - contact to add the line for + * @param pszProto - contact's protocol + * @param ad - anniversary to add + * @param mtNow - current time + * @param wDaysBefore - number of days in advance to remind the user of the anniversary + * + * @retval TRUE if successful + * @retval FALSE if failed + **/ + BOOLEAN AddRow(HANDLE hContact, LPCSTR pszProto, MAnnivDate &ad, MTime &mtNow, WORD wDaysBefore) + { + TCHAR szText[MAX_PATH]; + INT diff, iItem = -1; + CItemData *pdata; + + // + // first column: ETA + // + diff = ad.CompareDays(mtNow); + if (diff < 0) + { + diff += IsLeap(mtNow.Year() + 1) ? 366 : 365; + } + // is filtered + if (diff <= _filter.wDaysBefore) + { + // read reminder options for the contact + ad.DBGetReminderOpts(hContact); + if ((_filter.bFilterIndex != FILTER_DISABLED_REMINDER) || (ad.RemindOption() == BST_UNCHECKED)) + { + // set default offset if required + if (ad.RemindOffset() == (WORD)-1) + { + ad.RemindOffset(wDaysBefore); + + // create data object + pdata = new CItemData(hContact, ad); + if (!pdata) + { + return FALSE; + } + // add item + iItem = AddItem(_itot(diff, szText, 10), (LPARAM)pdata); + if (iItem == -1) + { + delete pdata; + return FALSE; + } + + // second column: contact name + AddSubItem(iItem, COLUMN_CONTACT, DB::Contact::DisplayName(hContact)); + + // third column: protocol + TCHAR* ptszProto = mir_a2t(pszProto); + AddSubItem(iItem, COLUMN_PROTO, ptszProto); + mir_free(ptszProto); + + // forth line: age + AddSubItem(iItem, COLUMN_AGE, _itot(ad.Age(&mtNow), szText, 10)); + + // fifth line: anniversary + AddSubItem(iItem, COLUMN_DESC, (LPTSTR)ad.Description()); + + // sixth line: date + ad.DateFormat(szText, SIZEOF(szText)); + AddSubItem(iItem, COLUMN_DATE, szText); + + _numRows++; + } + } + } + return TRUE; + } + + /** + * This method clears the list and adds contacts again, according to the current filter settings. + **/ + VOID RebuildList() + { + HANDLE hContact; + LPSTR pszProto; + MTime mtNow; + MAnnivDate ad; + INT i = 0; + DWORD age = 0; + WORD wDaysBefore = DB::Setting::GetWord(SET_REMIND_OFFSET, DEFVAL_REMIND_OFFSET); + WORD numMale = 0; + WORD numFemale = 0; + WORD numContacts = 0; + WORD numBirthContacts = 0; + + ShowWindow(_hList, SW_HIDE); + DeleteAllItems(); + mtNow.GetLocalTime(); + + // insert the items into the list + for (hContact = DB::Contact::FindFirst(); + hContact; + hContact = DB::Contact::FindNext(hContact)) + { + // ignore meta subcontacts here, as they are not interesting. + if (!DB::MetaContact::IsSub(hContact)){ + // filter protocol + pszProto = DB::Contact::Proto(hContact); + if (pszProto) { + numContacts++; + switch (GenderOf(hContact, pszProto)) + { + case 'M': { + numMale++; + } break; + + case 'F': { + numFemale++; + } + } + + if (!ad.DBGetBirthDate(hContact, pszProto)) { + age += ad.Age(&mtNow); + numBirthContacts++; + + // add birthday + if ((_filter.bFilterIndex != FILTER_ANNIV) && (!_filter.pszProto || !strcmpi(pszProto, _filter.pszProto))) { + AddRow(hContact, pszProto, ad, mtNow, wDaysBefore); + } + } + + // add anniversaries + if ( _filter.bFilterIndex != FILTER_BIRTHDAY && + (!_filter.pszProto || !strcmpi(pszProto, _filter.pszProto))) + { + for (i = 0; !ad.DBGetAnniversaryDate(hContact, i); i++) { + if (!_filter.pszAnniv || !_tcsicmp(_filter.pszAnniv, ad.Description())) { + AddRow(hContact, pszProto, ad, mtNow, wDaysBefore); + } + } + } + } + } + } + ListView_SortItemsEx(_hList, (CMPPROC)cmpProc, this); + ShowWindow(_hList, SW_SHOW); + + // display statistics + SetDlgItemInt(_hDlg, TXT_NUMBIRTH, numBirthContacts, FALSE); + SetDlgItemInt(_hDlg, TXT_NUMCONTACT, numContacts, FALSE); + SetDlgItemInt(_hDlg, TXT_FEMALE, numFemale, FALSE); + SetDlgItemInt(_hDlg, TXT_MALE, numMale, FALSE); + SetDlgItemInt(_hDlg, TXT_AGE, numBirthContacts > 0 ? max(0, (age - (age % numBirthContacts)) / numBirthContacts) : 0, FALSE); + } + + /** + * This method deletes all items from the listview + **/ + VOID DeleteAllItems() + { + CItemData *pid; + + for (INT i = 0; i < _numRows; i++) + { + pid = ItemData(i); + if (pid) + { + delete pid; + } + } + ListView_DeleteAllItems(_hList); + _numRows = 0; + } + + /** + * This method returns the data structure accociated with a list item. + * + * @param iItem - index of the desired item + * + * @return pointer to the data strucutre on success or NULL otherwise. + **/ + CItemData* ItemData(INT iItem) + { + if (_hList && iItem >= 0 && iItem < _numRows) + { + LVITEM lvi; + + lvi.mask = LVIF_PARAM; + lvi.iItem = iItem; + lvi.iSubItem = 0; + if (ListView_GetItem(_hList, &lvi) && PtrIsValid(lvi.lParam)) + { + return (CItemData*)lvi.lParam; + } + } + return NULL; + } + + /** + * This method loads all filter settings from db + **/ + VOID LoadFilter() + { + _filter.wDaysBefore = DB::Setting::GetWord(SET_ANNIVLIST_FILTER_DAYS, 9); + _filter.bFilterIndex = DB::Setting::GetByte(SET_ANNIVLIST_FILTER_INDEX, 0); + } + + /** + * This method saves all filter settings to db + **/ + VOID SaveFilter() + { + if (_hDlg) { + DB::Setting::WriteWord(SET_ANNIVLIST_FILTER_DAYS, (WORD)GetDlgItemInt(_hDlg, EDIT_DAYS, NULL, FALSE)); + DB::Setting::WriteByte(SET_ANNIVLIST_FILTER_DAYSENABLED, (BYTE)Button_GetCheck(GetDlgItem(_hDlg, CHECK_DAYS))); + DB::Setting::WriteByte(SET_ANNIVLIST_FILTER_INDEX, (BYTE)ComboBox_GetCurSel(GetDlgItem(_hDlg, EDIT_DAYS))); + } + } + +public: + + /** + * This is the default constructor. + **/ + CAnnivList() + { + _hList = NULL; + _sortHeader = 0; + _sortOrder = 1; + _curSel = -1; + _numRows = 0; + _wmINIT = true; + _rcWin.left = _rcWin.right = _rcWin.top = _rcWin.bottom = 0; + LoadFilter(); + + _hDlg = CreateDialogParam(ghInst, MAKEINTRESOURCE(IDD_ANNIVERSARY_LIST), NULL, (DLGPROC)DlgProc, (LPARAM)this); + if (_hDlg) { + MagneticWindows_AddWindow(_hDlg); + _mHookExit = HookEventMessage(ME_SYSTEM_PRESHUTDOWN, _hDlg, WM_CLOSE); + } + else { + _mHookExit = NULL; + delete this; + } + } + + /** + * This is the default destructor. + **/ + ~CAnnivList() + { + // delete the shutdown hook + if (_mHookExit) { + UnhookEvent(_mHookExit); + _mHookExit = NULL; + } + + // close window if required + if (_hDlg) { + // save list state + if (_hList) { + CHAR pszSetting[MAXSETTING]; + INT c, cc = Header_GetItemCount(ListView_GetHeader(_hList)); + + for (c = 0; c < cc; c++) { + mir_snprintf(pszSetting, MAXSETTING, "AnnivDlg_Col%d", c); + DB::Setting::WriteWord(pszSetting, (WORD)ListView_GetColumnWidth(_hList, c)); + } + DeleteAllItems(); + } + // remember popup setting + DB::Setting::WriteByte(SET_ANNIVLIST_POPUP, (BYTE)IsDlgButtonChecked(_hDlg, CHECK_POPUP)); + // save window position, size and column widths + Utils_SaveWindowPosition(_hDlg, NULL, MODNAME, "AnnivDlg_"); + SaveFilter(); + + // if the window did not yet retrieve a WM_DESTROY message, do it right now. + if (PtrIsValid(GetUserData(_hDlg))) { + SetUserData(_hDlg, NULL); + DestroyWindow(_hDlg); + } + } + gpDlg = NULL; + } + + /** + * name: BringToFront + * class: CAnnivList + * desc: brings anniversary list to the top + * param: none + * return: nothing + **/ + VOID BringToFront() + { + ShowWindow(_hDlg, SW_RESTORE); + SetForegroundWindow(_hDlg); + SetFocus(_hDlg); + } + +}; // class CAnnivList + +/*********************************************************************************************************** + * service handlers + ***********************************************************************************************************/ + +/** + * This is the service function that is called list all anniversaries. + * + * @param wParam - not used + * @param lParam - not used + * + * @return always 0 + **/ +INT_PTR DlgAnniversaryListShow(WPARAM wParam, LPARAM lParam) +{ + if (!gpDlg) { + try + { + myGlobals.WantAeroAdaption = DB::Setting::GetByte(SET_PROPSHEET_AEROADAPTION, TRUE); + gpDlg = new CAnnivList(); + } + catch(...) + { + delete gpDlg; + gpDlg = NULL; + } + } + else { + gpDlg->BringToFront(); + } + return 0; +} + +/*********************************************************************************************************** + * loading and unloading module + ***********************************************************************************************************/ + +#define TBB_IDBTN "AnnivList" +#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 wParam - none + * + * @return nothing + **/ +VOID DlgAnniversaryListOnTopToolBarLoaded() +{ + TTBButton ttb; + HICON hIcon; + ICONINFO ii; + + hIcon = IcoLib_RegisterIcon(TBB_ICONAME, LPGEN("Anniversary list"), + SECT_TOOLBAR, IDI_ANNIVERSARY, 0); + if (hIcon) { + GetIconInfo(hIcon, &ii); + + ZeroMemory(&ttb, sizeof(TTBButton)); + ttb.cbSize = sizeof(TTBButton); + ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP; + ttb.pszServiceDown = MS_USERINFO_REMINDER_LIST; + ttb.hbBitmapUp = ttb.hbBitmapDown = ii.hbmColor; + ttb.name = Translate("Anniversary list"); + + CallService(MS_TTB_ADDBUTTON, (WPARAM) &ttb, 0); + } +} + +/** + * This function is called by the ME_TB_MODULELOADED event. + * It adds a set of buttons to the Toolbar of the Modern Contact List. + * + * @param wParam - none + * + * @return nothing + **/ +VOID DlgAnniversaryListOnToolBarLoaded() +{ + TBButton tbb; + + ZeroMemory(&tbb, sizeof(tbb)); + tbb.cbSize = sizeof(tbb); + tbb.tbbFlags = TBBF_VISIBLE | TBBF_SHOWTOOLTIP; + tbb.defPos = 2100; + tbb.pszButtonName = + tbb.pszButtonID = TBB_IDBTN; + tbb.pszServiceName = MS_USERINFO_REMINDER_LIST; + tbb.pszTooltipDn = + tbb.pszTooltipUp = LPGEN("Anniversary list"); + tbb.hPrimaryIconHandle = + tbb.hSecondaryIconHandle = + IcoLib_RegisterIconHandle(TBB_ICONAME, tbb.pszTooltipUp, + SECT_TOOLBAR, IDI_ANNIVERSARY, 0); + + CallService(MS_TB_ADDBUTTON, 0, (LPARAM) &tbb); +} + +/** + * This function initially loads all required stuff for the anniversary list. + * + * @param none + * + * @return nothing + **/ +VOID DlgAnniversaryListLoadModule() +{ + HOTKEYDESC hk; + + myCreateServiceFunction(MS_USERINFO_REMINDER_LIST, DlgAnniversaryListShow); + + ZeroMemory(&hk, sizeof(HOTKEYDESC)); + hk.cbSize = sizeof(HOTKEYDESC); + hk.pszSection = MODNAME; + hk.pszName = "AnniversaryList"; + hk.pszDescription = LPGEN("Popup Anniversary list"); + hk.pszService = MS_USERINFO_REMINDER_LIST; + CallService(MS_HOTKEY_REGISTER, NULL, (LPARAM)&hk); +} -- cgit v1.2.3