/* 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 = {0}; ttb.cbSize = sizeof(ttb); ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP; ttb.pszService = MS_USERINFO_REMINDER_LIST; ttb.hIconHandleUp = Skin_GetIconHandle(ICO_COMMON_ANNIVERSARY); ttb.name = ttb.pszTooltipUp = LPGEN("Anniversary list"); TopToolbar_AddButton(&ttb); } /** * This function initially loads all required stuff for the anniversary list. * * @param none * * @return nothing **/ VOID DlgAnniversaryListLoadModule() { CreateServiceFunction(MS_USERINFO_REMINDER_LIST, DlgAnniversaryListShow); HOTKEYDESC hk = { 0 }; hk.cbSize = sizeof(HOTKEYDESC); hk.pszSection = MODNAME; hk.pszName = "AnniversaryList"; hk.pszDescription = LPGEN("Popup Anniversary list"); hk.pszService = MS_USERINFO_REMINDER_LIST; Hotkey_Register(&hk); }