/* 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. */ /** * System & local includes: **/ #include "commonheaders.h" #include "dlg_propsheet.h" #include "psp_base.h" #include "ex_import\svc_ExImport.h" #include "svc_reminder.h" #define OPTIONPAGE_OLD_SIZE (offsetof(OPTIONSDIALOGPAGE, hLangpack)) #define UPDATEANIMFRAMES 20 // internal dialog message handler #define M_CHECKONLINE (WM_USER+10) #define HM_PROTOACK (WM_USER+11) #define HM_SETTING_CHANGED (WM_USER+12) #define HM_RELOADICONS (WM_USER+13) #define HM_SETWINDOWTITLE (WM_USER+14) #define TIMERID_UPDATING 1 #ifndef TIMERID_RENAME #define TIMERID_RENAME 2 #endif // flags for the PS structure #define PSF_CHANGED 0x00000100 #define PSF_LOCKED 0x00000200 #define PSF_INITIALIZED 0x00000400 #define INIT_ICONS_NONE 0 #define INIT_ICONS_OWNER 1 #define INIT_ICONS_CONTACT 2 #define INIT_ICONS_ALL (INIT_ICONS_OWNER|INIT_ICONS_CONTACT) /*********************************************************************************************************** * internal variables ***********************************************************************************************************/ static BYTE bInitIcons = INIT_ICONS_NONE; static HANDLE ghWindowList = NULL; static HANDLE ghDetailsInitEvent = NULL; static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); CPsHdr::CPsHdr() : _ignore(10, (LIST::FTSortFunc)_tcscmp) { _dwSize = sizeof(*this); _hContact = NULL; _pszProto = NULL; _pszPrefix = NULL; _pPages = NULL; _numPages = 0; _dwFlags = 0; _hImages = NULL; } CPsHdr::~CPsHdr() { int i; // delete data for (i = 0 ; i < _ignore.getCount(); i++) { if (_ignore[i]) mir_free(_ignore[i]); } // delete the list _ignore.destroy(); } void CPsHdr::Free_pPages() { for ( int i = 0 ; i < _numPages; i++) if (_pPages[i]) delete _pPages[i]; _numPages = 0; MIR_FREE(_pPages); } /*********************************************************************************************************** * class CPsUpload ***********************************************************************************************************/ class CPsUpload { public: enum EPsUpReturn { UPLOAD_CONTINUE = 0, UPLOAD_FINISH = 1, UPLOAD_FINISH_CLOSE = 2 }; private: PROTOACCOUNT **_pPd; int _numProto; BYTE _bExitAfterUploading; HANDLE _hUploading; LPPS _pPs; /** * @class Upload * @class CPsUpload * @desc start upload process for the current protocol * @param none * @return 0 on success * @return 1 otherwise **/ int Upload() { CPsTreeItem *pti; // check if icq is online if (!IsProtoOnline((*_pPd)->szModuleName)) { TCHAR szMsg[MAX_PATH]; LPTSTR ptszProto; ptszProto = mir_a2t((*_pPd)->szModuleName); mir_sntprintf(szMsg, SIZEOF(szMsg), TranslateT("Protocol '%s' is offline"), ptszProto); mir_free(ptszProto); MsgBox(_pPs->hDlg, MB_ICON_WARNING, TranslateT("Upload Details"), szMsg, TranslateT("You are not currently connected to the ICQ network.\nYou must be online in order to update your information on the server.\n\nYour changes will be saved to database only.")); } // start uploading process else { _hUploading = (HANDLE)CallProtoService((*_pPd)->szModuleName, PS_CHANGEINFOEX, CIXT_FULL, NULL); if (_hUploading && _hUploading != (HANDLE)CALLSERVICE_NOTFOUND) { EnableWindow(_pPs->pTree->Window(), FALSE); if (pti = _pPs->pTree->CurrentItem()) { EnableWindow(pti->Wnd(), FALSE); } EnableWindow(GetDlgItem(_pPs->hDlg, IDOK), FALSE); EnableWindow(GetDlgItem(_pPs->hDlg, IDAPPLY), FALSE); mir_snprintf(_pPs->szUpdating, SIZEOF(_pPs->szUpdating), "%s (%s)", Translate("Uploading"), (*_pPd)->szModuleName); ShowWindow(GetDlgItem(_pPs->hDlg, TXT_UPDATING), SW_SHOW); SetTimer(_pPs->hDlg, TIMERID_UPDATING, 100, NULL); return 0; } } return 1; } public: /** * @name CPsUpload * @class CPsUpload * @desc retrieves the list of installed protocols and initializes the class * @param pPs - the owning propertysheet * @param bExitAfter - whether the dialog is to close after upload or not * @return nothing **/ CPsUpload(LPPS pPs, BYTE bExitAfter) { _pPs = pPs; _pPd = NULL; _numProto = 0; _hUploading = NULL; _bExitAfterUploading = bExitAfter; } int UploadFirst() { // create a list of all protocols which support uploading contact information if ( ProtoEnumAccounts(&_numProto, &_pPd)) return _bExitAfterUploading ? UPLOAD_FINISH_CLOSE : UPLOAD_FINISH; return UploadNext(); } /** * @name ~CPsUpload * @class CPsUpload * @desc clear pointer to the upload object * @param none * @return nothing **/ ~CPsUpload() { _pPs->pUpload = NULL; } /** * @name Handle * @class CPsUpload * @desc returns the handle of the current upload process * @param none * @return handle of the current upload process **/ __inline HANDLE Handle() const { return _hUploading; }; /** * @name UploadNext * @class CPsUpload * @desc Search the next protocol which supports uploading contact information * and start uploading. Delete the object if ready * @param none * @return nothing **/ int UploadNext() { CHAR str[MAXMODULELABELLENGTH]; while (_pPd && *_pPd && _numProto-- > 0) { mir_strncpy(str, (*_pPd)->szModuleName, MAXMODULELABELLENGTH); mir_strncat(str, PS_CHANGEINFOEX, MAXMODULELABELLENGTH); if (ServiceExists(str) && !Upload()) { _pPd++; return UPLOAD_CONTINUE; } _pPd++; } return _bExitAfterUploading ? UPLOAD_FINISH_CLOSE : UPLOAD_FINISH; } }; /*********************************************************************************************************** * propertysheet ***********************************************************************************************************/ /** * @name SortProc() * @desc used for sorting the tab pages * * @return -1 or 0 or 1 **/ static int SortProc(CPsTreeItem** item1, CPsTreeItem** item2) { if (*item1 && *item2) { if ((*item2)->Pos() > (*item1)->Pos()) return -1; if ((*item2)->Pos() < (*item1)->Pos()) return 1; } return 0; } /** * This service routine creates the DetailsDialog * @param wParam - handle to contact * @param lParam - not used * * @retval 0 on success * @retval 1 on failure **/ static INT_PTR ShowDialog(WPARAM wParam, LPARAM lParam) { HWND hWnd; // update some cached settings myGlobals.ShowPropsheetColours = db_get_b(NULL, MODNAME, SET_PROPSHEET_SHOWCOLOURS, TRUE); myGlobals.WantAeroAdaption = db_get_b(NULL, MODNAME, SET_PROPSHEET_AEROADAPTION, TRUE); // allow only one dialog per user if (hWnd = WindowList_Find(ghWindowList, (HANDLE)wParam)) { SetForegroundWindow(hWnd); SetFocus(hWnd); } else { CPsHdr psh; POINT metrics; BYTE bScanMetaSubContacts = FALSE; HICON hDefIcon; // init the treeview options if (db_get_b(NULL, MODNAME, SET_PROPSHEET_SORTITEMS, FALSE)) psh._dwFlags |= PSTVF_SORTTREE; if (db_get_b(NULL, MODNAME, SET_PROPSHEET_GROUPS, TRUE)) psh._dwFlags |= PSTVF_GROUPS; // create imagelist metrics.x = GetSystemMetrics(SM_CXSMICON); metrics.y = GetSystemMetrics(SM_CYSMICON); if ((psh._hImages = ImageList_Create(metrics.x, metrics.y, (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 0, 1)) == NULL) { MsgErr(NULL, LPGENT("Creating the imagelist failed!")); return 1; } hDefIcon = IcoLib_GetIcon(ICO_TREE_DEFAULT); if (!hDefIcon) { hDefIcon = (HICON) LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, metrics.x, metrics.y, 0); } // add the default icon to imagelist ImageList_AddIcon(psh._hImages, hDefIcon); // init contact psh._hContact = (HANDLE)wParam; if (psh._hContact == NULL) { // mark owner icons as initiated bInitIcons |= INIT_ICONS_OWNER; psh._pszProto = NULL; psh._pszPrefix = NULL; } else { // get contact's protocol psh._pszPrefix = psh._pszProto = DB::Contact::Proto((HANDLE)wParam); if (psh._pszProto == NULL) { MsgErr(NULL, LPGENT("Could not find contact's protocol. Maybe it is not active!")); return 1; } // prepare scanning for metacontact's subcontact's pages if (bScanMetaSubContacts = DB::Module::IsMetaAndScan(psh._pszProto)) psh._dwFlags |= PSF_PROTOPAGESONLY_INIT; } // add the pages NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, wParam); if (!psh._pPages || !psh._numPages) { MsgErr(NULL, LPGENT("No pages have been added. Canceling dialog creation!")); return 1; } // metacontacts sub pages if (bScanMetaSubContacts) { int numSubs = DB::MetaContact::SubCount((HANDLE)wParam); psh._dwFlags &= ~PSF_PROTOPAGESONLY_INIT; psh._dwFlags |= PSF_PROTOPAGESONLY; for (int i = 0; i < numSubs; i++) { psh._hContact = DB::MetaContact::Sub((HANDLE)wParam, i); psh._nSubContact = i; if (psh._hContact) { psh._pszProto = DB::Contact::Proto(psh._hContact); if ((INT_PTR)psh._pszProto != CALLSERVICE_NOTFOUND) NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact); } } psh._hContact = (HANDLE)wParam; } // sort the pages by the position read from database if (!(psh._dwFlags & PSTVF_SORTTREE)) { qsort(psh._pPages, psh._numPages, sizeof(CPsTreeItem*), (int (*)(const void*, const void*))SortProc); } // create the dialog itself hWnd = CreateDialogParam(ghInst, MAKEINTRESOURCE(IDD_DETAILS), NULL, DlgProc, (LPARAM)&psh); if (!hWnd) MsgErr(NULL, LPGENT("Details dialog failed to be created. Returning error is %d."), GetLastError()); } return 0; } /** * @name AddPage() * @desc this adds a new pages * @param wParam - The List of pages we want to add the new one to * @param lParam - it's the page to add * * @return 0 **/ static INT_PTR AddPage(WPARAM wParam, LPARAM lParam) { CPsHdr *pPsh = (CPsHdr*)wParam; OPTIONSDIALOGPAGE *odp = (OPTIONSDIALOGPAGE*)lParam; // check size of the handled structures if (pPsh == NULL || odp == NULL || pPsh->_dwSize != sizeof(CPsHdr)) return 1; if (odp->cbSize != sizeof(OPTIONSDIALOGPAGE) && odp->cbSize != OPTIONPAGE_OLD_SIZE) { MsgErr(NULL, LPGENT("The Page to add has invalid size %d bytes!"), odp->cbSize); return 1; } // try to check whether the flag member is initialized or not odp->flags = odp->flags > (ODPF_UNICODE|ODPF_BOLDGROUPS|ODPF_ICON|PSPF_PROTOPREPENDED) ? 0 : odp->flags; if (pPsh->_dwFlags & (PSF_PROTOPAGESONLY|PSF_PROTOPAGESONLY_INIT)) { BYTE bIsUnicode = (odp->flags & ODPF_UNICODE) == ODPF_UNICODE; TCHAR* ptszTitle = bIsUnicode ? mir_tstrdup(odp->ptszTitle) : mir_a2t(odp->pszTitle); // avoid adding pages for a meta subcontact, which have been added for a metacontact. if (pPsh->_dwFlags & PSF_PROTOPAGESONLY) { if (pPsh->_ignore.getIndex(ptszTitle) != -1) { mir_free(ptszTitle); return 0; } } // init ignore list with pages added by metacontact else if (pPsh->_dwFlags & PSF_PROTOPAGESONLY_INIT) pPsh->_ignore.insert(mir_tstrdup(ptszTitle)); mir_free(ptszTitle); } // create the new tree item CPsTreeItem *pNew = new CPsTreeItem(); if (pNew) { if (pNew->Create(pPsh, odp)) { MIR_DELETE(pNew); return 1; } // resize the array pPsh->_pPages = (CPsTreeItem**)mir_realloc(pPsh->_pPages, (pPsh->_numPages + 1) * sizeof(CPsTreeItem*)); if (pPsh->_pPages != NULL) { pPsh->_pPages[pPsh->_numPages++] = pNew; return 0; } pPsh->_numPages = 0; } return 1; } /** * @name OnDeleteContact() * @desc a user was deleted, so need to close its details dialog, if one open * * @return 0 **/ static int OnDeleteContact(WPARAM wParam, LPARAM lParam) { HWND hWnd = WindowList_Find(ghWindowList, (HANDLE)wParam); if (hWnd != NULL) DestroyWindow(hWnd); return 0; } /** * @name OnShutdown() * @desc we need to emptify the windowlist * * @return 0 **/ static int OnShutdown(WPARAM wParam, LPARAM lParam) { WindowList_BroadcastAsync(ghWindowList, WM_DESTROY, 0, 0); return 0; } /** * @name AddProtocolPages() * @desc is called by Miranda if user selects to display the userinfo dialog * @param odp - optiondialogpage structure to use * @param wParam - the propertysheet init structure to pass * @param pszProto - the protocol name to prepend as item name (can be NULL) * * @return 0 **/ static int AddProtocolPages(OPTIONSDIALOGPAGE& odp, WPARAM wParam, LPSTR pszProto = NULL) { TCHAR szTitle[MAX_PATH]; const BYTE ofs = (pszProto) ? mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%S\\"), pszProto) : 0; odp.ptszTitle = szTitle; odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_GENERAL); odp.position = 0x8000000; odp.pfnDlgProc = PSPProcGeneral; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_GENERAL); mir_tcsncpy(szTitle + ofs, LPGENT("General"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ADDRESS); odp.position = 0x8000001; odp.pfnDlgProc = PSPProcContactHome; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ADDRESS); mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Contact (private)"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ORIGIN); odp.position = 0x8000002; odp.pfnDlgProc = PSPProcOrigin; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ADVANCED); mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Origin"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ANNIVERSARY); odp.position = 0x8000003; odp.pfnDlgProc = PSPProcAnniversary; odp.hIcon = (HICON)ICONINDEX(IDI_BIRTHDAY); mir_tcsncpy(szTitle + ofs, LPGENT("General") _T("\\") LPGENT("Anniversaries"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_COMPANY); odp.position = 0x8000004; odp.pfnDlgProc = PSPProcCompany; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_COMPANY); mir_tcsncpy(szTitle + ofs, LPGENT("Work"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ADDRESS); odp.position = 0x8000005; odp.pfnDlgProc = PSPProcContactWork; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ADDRESS); mir_tcsncpy(szTitle + ofs, LPGENT("Work") _T("\\") LPGENT("Contact (Work)"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ABOUT); odp.position = 0x8000006; odp.pfnDlgProc = PSPProcAbout; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_ABOUT); mir_tcsncpy(szTitle + ofs, LPGENT("About"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_PROFILE); odp.position = 0x8000007; odp.pfnDlgProc = PSPProcContactProfile; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_PROFILE); mir_tcsncpy(szTitle + ofs, LPGENT("About") _T("\\") LPGENT("Profile"), SIZEOF(szTitle) - ofs); AddPage(wParam, (LPARAM)&odp); return 0; } /** * @name InitDetails * @desc is called by Miranda if user selects to display the userinfo dialog * @param wParam - the propertysheet init structure to pass * @param lParam - handle to contact whose information are read * * @return 0 **/ static int InitDetails(WPARAM wParam, LPARAM lParam) { CPsHdr* pPsh = (CPsHdr*)wParam; if (!(pPsh->_dwFlags & PSF_PROTOPAGESONLY)) { OPTIONSDIALOGPAGE odp; BYTE bChangeDetailsEnabled = myGlobals.CanChangeDetails && db_get_b(NULL, MODNAME, SET_PROPSHEET_CHANGEMYDETAILS, FALSE); // important to avoid craches!! ZeroMemory(&odp, sizeof(odp)); if (lParam || bChangeDetailsEnabled) { odp.cbSize = sizeof(odp); odp.hInstance = ghInst; odp.flags = ODPF_ICON | ODPF_TCHAR; odp.ptszGroup = IcoLib_GetDefaultIconFileName(); if (lParam) { // ignore common pages for weather contacts if (!pPsh->_pszProto || _stricmp(pPsh->_pszProto, "weather")) { AddProtocolPages(odp, wParam); odp.ptszTitle = LPGENT("About") _T("\\") LPGENT("Notes"); } else { odp.ptszTitle = LPGENT("Notes"); } odp.pszTemplate = MAKEINTRESOURCEA(IDD_CONTACT_ABOUT); odp.position = 0x8000008; odp.pfnDlgProc = PSPProcMyNotes; odp.hIcon = (HICON)ICONINDEX(IDI_TREE_NOTES); AddPage(wParam, (LPARAM)&odp); } } } return 0; } /** * @name InitTreeIcons() * @desc initalize all treeview icons * @param none * * @return nothing **/ void DlgContactInfoInitTreeIcons() { // make sure this is run only once if (!(bInitIcons & INIT_ICONS_ALL)) { CPsHdr psh; POINT metrics = {0}; int i = 0; psh._dwFlags = PSTVF_INITICONS; metrics.x = GetSystemMetrics(SM_CXSMICON); metrics.y = GetSystemMetrics(SM_CYSMICON); if (psh._hImages = ImageList_Create(metrics.x, metrics.y, (IsWinVerXPPlus() ? ILC_COLOR32 : ILC_COLOR16) | ILC_MASK, 0, 1)) { HICON hDefIcon = IcoLib_GetIcon(ICO_TREE_DEFAULT); if (!hDefIcon) { hDefIcon = (HICON) LoadImage(ghInst, MAKEINTRESOURCE(IDI_DEFAULT), IMAGE_ICON, metrics.x, metrics.y, 0); } // add the default icon to imagelist ImageList_AddIcon(psh._hImages, hDefIcon); } // avoid pages from loading doubled if (!(bInitIcons & INIT_ICONS_CONTACT)) { LPCSTR pszContactProto = NULL; PROTOACCOUNT **pd; int ProtoCount = 0; psh._dwFlags |= PSF_PROTOPAGESONLY_INIT; // enumerate all protocols if ( !ProtoEnumAccounts(&ProtoCount, &pd)) { for (i = 0; i < ProtoCount; i++) { // enumerate all contacts for (psh._hContact = db_find_first(); psh._hContact != NULL; psh._hContact = db_find_next(psh._hContact)) { // compare contact's protocol to the current one, to add pszContactProto = DB::Contact::Proto(psh._hContact); if ((INT_PTR)pszContactProto != CALLSERVICE_NOTFOUND && !mir_strcmp(pd[i]->szModuleName, pszContactProto)) { // call a notification for the contact to retrieve all protocol specific tree items NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact); if (psh._pPages) { psh.Free_pPages(); psh._dwFlags = PSTVF_INITICONS|PSF_PROTOPAGESONLY; } break; } } } } bInitIcons |= INIT_ICONS_CONTACT; } // load all treeitems for owner contact if (!(bInitIcons & INIT_ICONS_OWNER)) { psh._hContact = NULL; psh._pszProto = NULL; NotifyEventHooks(ghDetailsInitEvent, (WPARAM)&psh, (LPARAM)psh._hContact); if (psh._pPages) { psh.Free_pPages(); } bInitIcons |= INIT_ICONS_OWNER; } ImageList_Destroy(psh._hImages); } } /** * @name UnLoadModule() * @desc unload the UserInfo Module * * @return nothing **/ void DlgContactInfoUnLoadModule() { DestroyHookableEvent(ghDetailsInitEvent); } /** * @name LoadModule() * @desc load the UserInfo Module * * @return nothing **/ void DlgContactInfoLoadModule() { ghDetailsInitEvent = CreateHookableEvent(ME_USERINFO_INITIALISE); CreateServiceFunction(MS_USERINFO_SHOWDIALOG, ShowDialog); CreateServiceFunction("UserInfo/AddPage", AddPage); HookEvent(ME_DB_CONTACT_DELETED, OnDeleteContact); HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown); HookEvent(ME_USERINFO_INITIALISE, InitDetails); ghWindowList = (HANDLE)CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0); // check whether changing my details via UserInfoEx is basically possible { PROTOACCOUNT **pAcc; int i, nAccCount; myGlobals.CanChangeDetails = FALSE; if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc))) { for (i = 0; (i < nAccCount) && !myGlobals.CanChangeDetails; i++) { if (IsProtoAccountEnabled(pAcc[i])) { // update my contact information on icq server myGlobals.CanChangeDetails = MIREXISTS(CallProtoService(pAcc[i]->szModuleName, PS_CHANGEINFOEX, NULL, NULL)); } } } } } static void ResetUpdateInfo(LPPS pPs) { int i; // free the array of accomblished acks for (i = 0; i < (int)pPs->nSubContacts; i++) MIR_FREE(pPs->infosUpdated[i].acks); MIR_FREE(pPs->infosUpdated); pPs->nSubContacts = 0; } /* ============================================================================================ PropertySheet's Dialog Procedures ============================================================================================ */ /** * @name DlgProc() * @desc dialog procedure for the main propertysheet dialog box * * @return 0 or 1 **/ static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPPS pPs = (LPPS)GetUserData(hDlg); // do not process any message if pPs is no longer existent if (!PtrIsValid(pPs) && uMsg != WM_INITDIALOG) return FALSE; switch (uMsg) { /** * @class WM_INITDIALOG * @desc initiates all dialog controls * @param wParam - not used * lParam - pointer to a PSHDR structure, which contains all information to create the dialog * * @return TRUE if everything is ok, FALSE if dialog creation should fail **/ case WM_INITDIALOG: { CPsHdr* pPsh = (CPsHdr*)lParam; WORD needWidth = 0; RECT rc; if (!pPsh || pPsh->_dwSize != sizeof(CPsHdr)) return FALSE; TranslateDialogDefault(hDlg); // // create data structures // if (!(pPs = (LPPS)mir_alloc(sizeof(PS)))) return FALSE; ZeroMemory(pPs, sizeof(PS)); if (!(pPs->pTree = new CPsTree(pPs))) return FALSE; if (!(pPs->pTree->Create(GetDlgItem(hDlg, STATIC_TREE), pPsh))) { return FALSE; } SetUserData(hDlg, pPs); pPs->hDlg = hDlg; pPs->dwFlags |= PSF_LOCKED; pPs->hContact = pPsh->_hContact; pPs->hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hDlg, HM_PROTOACK); pPs->hSettingChanged = HookEventMessage(ME_DB_CONTACT_SETTINGCHANGED, hDlg, HM_SETTING_CHANGED); pPs->hIconsChanged = HookEventMessage(ME_SKIN2_ICONSCHANGED, hDlg, HM_RELOADICONS); ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG),IsAeroMode()); ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG2),!IsAeroMode()); // // set icons // SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)IcoLib_GetIcon(ICO_COMMON_MAIN)); DlgProc(hDlg, HM_RELOADICONS, NULL, NULL); // // load basic protocol for current contact (for faster load later on and better handling for owner protocol) // if (pPs->hContact) mir_strncpy(pPs->pszProto, pPsh->_pszPrefix, MAXMODULELABELLENGTH); // set the windowtitle DlgProc(hDlg, HM_SETWINDOWTITLE, NULL, NULL); // translate Userinfo buttons { SendDlgItemMessage(hDlg, BTN_UPDATE, BUTTONTRANSLATE, NULL, NULL); SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL); SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL); SendDlgItemMessage(hDlg, IDAPPLY, BUTTONTRANSLATE, NULL, NULL); SendDlgItemMessage(hDlg, BTN_EXPORT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Export to file"), MBBF_TCHAR); SendDlgItemMessage(hDlg, BTN_IMPORT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Import from file"), MBBF_TCHAR); } // // set bold font for name in description area // { LOGFONT lf; HFONT hNormalFont = (HFONT)SendMessage(hDlg, WM_GETFONT, 0, 0); GetObject(hNormalFont, sizeof(lf), &lf); lf.lfHeight = 22; mir_tcscpy(lf.lfFaceName, _T("Segoe UI")); pPs->hCaptionFont = CreateFontIndirect(&lf); SendDlgItemMessage(hDlg, IDC_PAGETITLE, WM_SETFONT, (WPARAM)pPs->hCaptionFont, 0); GetObject(hNormalFont, sizeof(lf), &lf); lf.lfWeight = FW_BOLD; pPs->hBoldFont = CreateFontIndirect(&lf); } // // initialize the optionpages and tree control // if (!pPs->pTree->InitTreeItems((LPWORD)&needWidth)) return FALSE; // // move and resize dialog and its controls // { HWND hCtrl; RECT rcTree; POINT pt = { 0, 0 }; int addWidth = 0; // at least add width of scrollbar needWidth += 8 + GetSystemMetrics(SM_CXVSCROLL); // get tree rectangle GetWindowRect(hDlg, &pPs->rcDisplay); GetWindowRect(pPs->pTree->Window(), &rcTree); ClientToScreen(hDlg, &pt); OffsetRect(&rcTree, -pt.x, -pt.y); // calculate the amout of pixels to resize the dialog by? if (needWidth > rcTree.right - rcTree.left) { RECT rcMax = { 0, 0, 0, 0 }; rcMax.right = 280; MapDialogRect(hDlg, &rcMax); addWidth = min(needWidth, rcMax.right) - rcTree.right + rcTree.left; rcTree.right += addWidth; // resize tree MoveWindow(pPs->pTree->Window(), rcTree.left, rcTree.top, rcTree.right - rcTree.left, rcTree.bottom - rcTree.top, FALSE); pPs->rcDisplay.right += addWidth; MoveWindow(hDlg, pPs->rcDisplay.left, pPs->rcDisplay.top, pPs->rcDisplay.right - pPs->rcDisplay.left, pPs->rcDisplay.bottom - pPs->rcDisplay.top, FALSE); } GetClientRect(GetDlgItem(hDlg, IDC_PAGETITLEBG), &rc); // calculate dislpay area for pages OffsetRect(&pPs->rcDisplay, -pt.x, -pt.y); pPs->rcDisplay.bottom = rcTree.bottom; pPs->rcDisplay.left = rcTree.right + 2; pPs->rcDisplay.top = rcTree.top+rc.bottom; pPs->rcDisplay.right -= 2; // move and resize the rest of the controls if (addWidth > 0) { const WORD idResize[] = { IDC_HEADERBAR, STATIC_LINE2 }; const WORD idMove[] = { IDC_PAGETITLE, IDC_PAGETITLEBG, IDC_PAGETITLEBG2, IDOK, IDCANCEL, IDAPPLY }; WORD i; for (i = 0; i < SIZEOF(idResize); i++) { if (hCtrl = GetDlgItem(hDlg, idResize[i])) { GetWindowRect(hCtrl, &rc); OffsetRect(&rc, -pt.x, -pt.y); MoveWindow(hCtrl, rc.left, rc.top, rc.right - rc.left + addWidth, rc.bottom - rc.top, FALSE); } } for (i = 0; i < SIZEOF(idMove); i++) { if (hCtrl = GetDlgItem(hDlg, idMove[i])) { GetWindowRect(hCtrl, &rc); OffsetRect(&rc, -pt.x, -pt.y); MoveWindow(hCtrl, rc.left + addWidth, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE); } } } // restore window position and add required size Utils_RestoreWindowPositionNoSize(hDlg, NULL, MODNAME, "DetailsDlg"); } // // show the first propsheetpage // // finally add the dialog to the window list WindowList_Add(ghWindowList, hDlg, pPs->hContact); // show the dialog pPs->dwFlags &= ~PSF_LOCKED; pPs->dwFlags |= PSF_INITIALIZED; // // initialize the "updating" button and statustext and check for online status // pPs->updateAnimFrame = 0; if (pPs->hContact && *pPs->pszProto) { GetDlgItemTextA(hDlg, TXT_UPDATING, pPs->szUpdating, SIZEOF(pPs->szUpdating)); ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE); if (DlgProc(hDlg, M_CHECKONLINE, NULL, NULL)) { DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(BTN_UPDATE, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, BTN_UPDATE)); } } { int nPage = pPs->pTree->CurrentItemIndex(); if (!pPs->pTree->IsIndexValid(nPage)) nPage = 0; TreeView_Select(pPs->pTree->Window(), NULL, TVGN_CARET); TreeView_Select(pPs->pTree->Window(), pPs->pTree->TreeItemHandle(nPage), TVGN_CARET); ShowWindow(hDlg, SW_SHOW); } } return TRUE; /** * @class WM_TIMER * @desc is called to display the "updating" text in the status area * @param wParam - not used * lParam - not used * * @return always FALSE **/ case WM_TIMER: switch (wParam) { case TIMERID_UPDATING: { CHAR str[84]; mir_snprintf(str, SIZEOF(str), "%.*s%s%.*s", pPs->updateAnimFrame%10, ".........", pPs->szUpdating, pPs->updateAnimFrame%10, "........."); SetDlgItemTextA(hDlg, TXT_UPDATING, str); if (++pPs->updateAnimFrame == UPDATEANIMFRAMES) pPs->updateAnimFrame = 0; return FALSE; } } break; case 0x031E: /*WM_DWMCOMPOSITIONCHANGED:*/ { ShowWindow(GetDlgItem(hDlg, IDC_PAGETITLEBG),IsAeroMode()); InvalidateRect(hDlg, NULL, TRUE); } break; /** * @class WM_CTLCOLORSTATIC * @desc sets the colour of some of the dialog's static controls * @param wParam - HWND of the contrls * lParam - HDC for drawing * * @return StockObject **/ case WM_CTLCOLORSTATIC: switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) { case TXT_UPDATING: { COLORREF textCol, bgCol, newCol; int ratio; textCol = GetSysColor(COLOR_BTNTEXT); bgCol = GetSysColor(COLOR_3DFACE); ratio = abs(UPDATEANIMFRAMES/2 - pPs->updateAnimFrame) * 510 / UPDATEANIMFRAMES; newCol = RGB(GetRValue(bgCol) + (GetRValue(textCol) - GetRValue(bgCol)) * ratio / 256, GetGValue(bgCol) + (GetGValue(textCol) - GetGValue(bgCol)) * ratio / 256, GetBValue(bgCol) + (GetBValue(textCol) - GetBValue(bgCol)) * ratio / 256); SetTextColor((HDC)wParam, newCol); SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE)); } return (INT_PTR)GetSysColorBrush(COLOR_3DFACE); case IDC_PAGETITLE: case IDC_PAGETITLEBG: { if (IsAeroMode()) { SetTextColor((HDC)wParam, RGB(0,90,180)); SetBkColor((HDC)wParam, RGB(255, 255, 255)); return (INT_PTR)GetStockObject(WHITE_BRUSH); } else { SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE)); return (INT_PTR)GetSysColorBrush(COLOR_3DFACE); } } } SetBkMode((HDC)wParam, TRANSPARENT); return (INT_PTR)GetStockObject(NULL_BRUSH); /** * @class PSM_CHANGED * @desc indicates the propertysheet and the current selected page as changed * @param wParam - not used * lParam - not used * * @return TRUE if successful FALSE if the dialog is locked at the moment **/ case PSM_CHANGED: if (!(pPs->dwFlags & PSF_LOCKED)) { pPs->dwFlags |= PSF_CHANGED; pPs->pTree->CurrentItem()->AddFlags(PSPF_CHANGED); EnableWindow(GetDlgItem(hDlg, IDAPPLY), TRUE); return TRUE; } break; /** * @class PSM_GETBOLDFONT * @desc returns the bold font * @param wParam - not used * lParam - pointer to a HFONT, which takes the boldfont * * @return TRUE if successful, FALSE otherwise **/ case PSM_GETBOLDFONT: if (pPs->hBoldFont && lParam) { *(HFONT*)lParam = pPs->hBoldFont; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->hBoldFont); return TRUE; } *(HFONT*)lParam = NULL; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0l); break; /** * @class PSM_GETCONTACT * @desc returns the handle to the contact, associated with this propertysheet * @param wParam - index or -1 for current item * lParam - pointer to a HANDLE, which takes the contact handle * * @return TRUE if successful, FALSE otherwise **/ case PSM_GETCONTACT: if (lParam) { CPsTreeItem *pti = ((int)wParam != -1) ? pPs->pTree->TreeItem((int)wParam) : pPs->pTree->CurrentItem(); // prefer to return the contact accociated with the current page if (pti && pti->hContact() != INVALID_HANDLE_VALUE) { *(HANDLE*)lParam = pti->hContact(); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->hContact()); return TRUE; } // return contact who owns the details dialog if (pPs->hContact != INVALID_HANDLE_VALUE) { *(HANDLE*)lParam = pPs->hContact; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->hContact); return TRUE; } *(HANDLE*)lParam = NULL; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, NULL); } break; /** * @class PSM_GETBASEPROTO * @desc returns the basic protocol module for the associated contact * @param wParam - index or -1 for current item * lParam - pointer to a LPCSTR which takes the protocol string pointer * * @return TRUE if successful, FALSE otherwise **/ case PSM_GETBASEPROTO: if (lParam) { CPsTreeItem *pti = ((int)wParam != -1) ? pPs->pTree->TreeItem((int)wParam) : pPs->pTree->CurrentItem(); if (pti && pti->Proto()) { // return custom protocol for the current page *(LPCSTR*)lParam = pti->Proto(); SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->Proto()); return TRUE; } if (*pPs->pszProto) { // return global protocol *(LPSTR*)lParam = pPs->pszProto; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pPs->pszProto); return TRUE; } } *(LPCSTR*)lParam = NULL; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0l); break; /** * @class PSM_ISLOCKED * @desc returns the lock state of the propertysheetpage * @param wParam - not used * lParam - not used * * @return TRUE if propertysheet is locked, FALSE if not **/ case PSM_ISLOCKED: { BYTE bLocked = (pPs->dwFlags & PSF_LOCKED) == PSF_LOCKED; SetWindowLongPtr(hDlg, DWLP_MSGRESULT, bLocked); return bLocked; } /** * @class PSM_FORCECHANGED * @desc force all propertysheetpages to update their controls with new values from the database * @param wParam - whether to replace changed settings too or not * lParam - not used * * @return always FALSE **/ case PSM_FORCECHANGED: if (!(pPs->dwFlags & PSF_LOCKED)) { BYTE bChanged; pPs->dwFlags |= PSF_LOCKED; if (bChanged = pPs->pTree->OnInfoChanged()) pPs->dwFlags |= PSF_CHANGED; else pPs->dwFlags &= ~PSF_CHANGED; pPs->dwFlags &= ~PSF_LOCKED; EnableWindow(GetDlgItem(hDlg, IDAPPLY), bChanged); } break; /** * @class PSM_DLGMESSAGE * @desc Sends a message to a specified propertysheetpage * @param wParam - not used * lParam - LPDLGCOMMAND structure, which contains information about the message to forward * * @return E_FAIL if the page was not found **/ case PSM_DLGMESSAGE: { LPDLGCOMMAND pCmd = (LPDLGCOMMAND)lParam; CPsTreeItem *pti; if (pCmd && (pti = pPs->pTree->FindItemByResource(pCmd->hInst, pCmd->idDlg)) && pti->Wnd()) { if (!pCmd->idDlgItem) return SendMessage(pti->Wnd(), pCmd->uMsg, pCmd->wParam, pCmd->lParam); else return SendDlgItemMessage(pti->Wnd(), pCmd->idDlgItem, pCmd->uMsg, pCmd->wParam, pCmd->lParam); } return E_FAIL; } /** * @class PSM_GETPAGEHWND * @desc get the window handle for a specified propertysheetpage * @param wParam - recource id of the dialog recource * lParam - hinstance of the plugin, which created the dialog box * * @return TRUE if handle was found and dialog was created before, false otherwise **/ case PSM_GETPAGEHWND: { CPsTreeItem *pti; if (pti = pPs->pTree->FindItemByResource((HINSTANCE)lParam, wParam)) { SetWindowLongPtr(hDlg, DWLP_MSGRESULT, (LONG_PTR)pti->Wnd()); return (pti->Wnd() != NULL); } } return FALSE; case PSM_ISAEROMODE: { BYTE bIsAeroMode = IsAeroMode(); if (lParam) { *(BYTE*)lParam = bIsAeroMode; } return (INT_PTR)bIsAeroMode; } /** * @class HM_SETWINDOWTITLE * @desc set the window title and text of the infobar * @param wParam - not used * lParam - DBCONTACTWRITESETTING structure if called by HM_SETTING_CHANGED message handler * * @return FALSE **/ case HM_SETWINDOWTITLE: { LPCTSTR pszName = NULL; TCHAR newTitle[MAX_PATH]; RECT rc; POINT pt = { 0, 0 }; HWND hName = GetDlgItem(hDlg, TXT_NAME); DBCONTACTWRITESETTING* pdbcws = (DBCONTACTWRITESETTING*)lParam; if (!pPs->hContact) pszName = TranslateT("Owner"); else if (pdbcws && pdbcws->value.type == DBVT_TCHAR) pszName = pdbcws->value.ptszVal; else pszName = DB::Contact::DisplayName(pPs->hContact); SetWindowText(hName, pszName); mir_sntprintf(newTitle, MAX_PATH, _T("%s - %s"), pszName, TranslateT("Edit Contact Information")); SetWindowText(hDlg, newTitle); mir_sntprintf(newTitle, MAX_PATH, _T("%s\n%s"), TranslateT("Edit Contact Information"), pszName); SetDlgItemText(hDlg, IDC_HEADERBAR, newTitle); // redraw the name control ScreenToClient(hDlg, &pt); GetWindowRect(hName, &rc); OffsetRect(&rc, pt.x, pt.y); InvalidateRect(hDlg, &rc, TRUE); break; } /** * @class HM_RELOADICONS * @desc handles the changed icon event from the icolib plugin and reloads all icons * @param wParam - not used * lParam - not used * * @return FALSE **/ case HM_RELOADICONS: { HWND hCtrl; HICON hIcon; const ICONCTRL idIcon[] = { { ICO_DLG_DETAILS, STM_SETIMAGE, ICO_DLGLOGO }, { ICO_BTN_UPDATE, BM_SETIMAGE, BTN_UPDATE }, { ICO_BTN_OK, BM_SETIMAGE, IDOK }, { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL }, { ICO_BTN_APPLY, BM_SETIMAGE, IDAPPLY } }; const int numIconsToSet = db_get_b(NULL, MODNAME, SET_ICONS_BUTTONS, 1) ? SIZEOF(idIcon) : 1; IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet); if (hCtrl = GetDlgItem(hDlg, BTN_IMPORT)) { hIcon = IcoLib_GetIcon(ICO_BTN_IMPORT); SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); SetWindowText(hCtrl, hIcon ? _T("") : _T("I")); } if (hCtrl = GetDlgItem(hDlg, BTN_EXPORT)) { hIcon = IcoLib_GetIcon(ICO_BTN_EXPORT); SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); SetWindowText(hCtrl, hIcon ? _T("") : _T("E")); } // update page icons if (PtrIsValid(pPs) && (pPs->dwFlags & PSF_INITIALIZED)) pPs->pTree->OnIconsChanged(); break; } /** * @class M_CHECKONLINE * @desc determines whether miranda is online or not * @param wParam - not used * lParam - not used * * @return TRUE if online, FALSE if offline **/ case M_CHECKONLINE: { if (IsProtoOnline(pPs->pszProto)) { EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), !IsWindowVisible(GetDlgItem(hDlg, TXT_UPDATING))); return TRUE; } EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE); EnableWindow(GetDlgItem(hDlg, TXT_UPDATING), FALSE); break; } /** * @class HM_PROTOACK * @desc handles all acks from the protocol plugin * @param wParam - not used * lParam - pointer to a ACKDATA structure * * @return FALSE **/ case HM_PROTOACK: { ACKDATA *ack = (ACKDATA*)lParam; int i, iSubContact; CPsTreeItem *pti; if (!ack->hContact && ack->type == ACKTYPE_STATUS) return DlgProc(hDlg, M_CHECKONLINE, NULL, NULL); switch (ack->type) { case ACKTYPE_SETINFO: { if (ack->hContact != pPs->hContact || !pPs->pUpload || pPs->pUpload->Handle() != ack->hProcess) break; if (ack->result == ACKRESULT_SUCCESS) { ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE); KillTimer(hDlg, TIMERID_UPDATING); // upload next protocols contact information switch (pPs->pUpload->UploadNext()) { case CPsUpload::UPLOAD_FINISH_CLOSE: MIR_DELETE(pPs->pUpload); DestroyWindow(hDlg); case CPsUpload::UPLOAD_CONTINUE: return FALSE; case CPsUpload::UPLOAD_FINISH: MIR_DELETE(pPs->pUpload); break; } DlgProc(hDlg, M_CHECKONLINE, NULL, NULL); EnableWindow(pPs->pTree->Window(), TRUE); if (pti = pPs->pTree->CurrentItem()) { EnableWindow(pti->Wnd(), TRUE); } EnableWindow(GetDlgItem(hDlg, IDOK), TRUE); pPs->dwFlags &= ~PSF_LOCKED; } else if (ack->result == ACKRESULT_FAILED) { MsgBox(hDlg, MB_ICON_WARNING, LPGENT("Upload ICQ Details"), LPGENT("Upload failed"), LPGENT("Your details were not uploaded successfully.\nThey were written to database only.")); KillTimer(hDlg, TIMERID_UPDATING); ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE); DlgProc(hDlg, M_CHECKONLINE, NULL, NULL); // upload next protocols contact information switch (pPs->pUpload->UploadNext()) { case CPsUpload::UPLOAD_FINISH_CLOSE: MIR_DELETE(pPs->pUpload); DestroyWindow(hDlg); case CPsUpload::UPLOAD_CONTINUE: return 0; case CPsUpload::UPLOAD_FINISH: MIR_DELETE(pPs->pUpload); break; } if (pti = pPs->pTree->CurrentItem()) { EnableWindow(pti->Wnd(), TRUE); } // activate all controls again EnableWindow(pPs->pTree->Window(), TRUE); EnableWindow(GetDlgItem(hDlg, IDOK), TRUE); pPs->dwFlags &= ~PSF_LOCKED; } break; } case ACKTYPE_GETINFO: // is contact the owner of the dialog or any metasubcontact of the owner? skip handling otherwise! if (ack->hContact != pPs->hContact) { if (!myGlobals.szMetaProto) break; if (!db_get_b(NULL, MODNAME, SET_META_SCAN, TRUE)) break; for (i = 0; i < pPs->nSubContacts; i++) { if (pPs->infosUpdated[i].hContact == ack->hContact) { iSubContact = i; break; } } if (i == pPs->nSubContacts) break; } else { iSubContact = 0; } // if they're not gonna send any more ACK's don't let that mean we should crash if (!pPs->infosUpdated || (!ack->hProcess && !ack->lParam)) { ResetUpdateInfo(pPs); ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE); KillTimer(hDlg, TIMERID_UPDATING); DlgProc(hDlg, M_CHECKONLINE, NULL, NULL); break; } if (iSubContact < pPs->nSubContacts) { // init the acks structure for a sub contact if (pPs->infosUpdated[iSubContact].acks == NULL) { pPs->infosUpdated[iSubContact].acks = (LPINT)mir_calloc(sizeof(int) * (int)(INT_PTR)ack->hProcess); pPs->infosUpdated[iSubContact].count = (int)(INT_PTR)ack->hProcess; } if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) pPs->infosUpdated[iSubContact].acks[ack->lParam] = 1; // check for pending tasks for (iSubContact = 0; iSubContact < pPs->nSubContacts; iSubContact++) { for (i = 0; i < pPs->infosUpdated[iSubContact].count; i++) { if (pPs->infosUpdated[iSubContact].acks[i] == 0) break; } if (i < pPs->infosUpdated[iSubContact].count) break; } } // all acks are done, finish updating if (iSubContact >= pPs->nSubContacts) { ResetUpdateInfo(pPs); ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_HIDE); KillTimer(hDlg, TIMERID_UPDATING); DlgProc(hDlg, M_CHECKONLINE, NULL, NULL); } } break; } /** * @class HM_SETTING_CHANGED * @desc This message is called by the ME_DB_CONTACT_SETTINGCHANGED event and forces all * unedited settings in the propertysheetpages to be updated * @param wParam - handle to the contact whose settings are to be changed * lParam - DBCONTACTWRITESETTING structure that identifies the changed setting * @return FALSE **/ case HM_SETTING_CHANGED: if (!(pPs->dwFlags & PSF_LOCKED)) { HANDLE hContact = (HANDLE)wParam; DBCONTACTWRITESETTING* pdbcws = (DBCONTACTWRITESETTING*)lParam; if (hContact != pPs->hContact) { if (!myGlobals.szMetaProto) break; if (pPs->hContact != (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, NULL)) break; if (!db_get_b(NULL, MODNAME, SET_META_SCAN, TRUE)) break; } if ( !lstrcmpA(pdbcws->szSetting, SET_CONTACT_MYHANDLE) || !lstrcmpA(pdbcws->szSetting, SET_CONTACT_NICK)) { // force the update of all propertysheetpages DlgProc(hDlg, PSM_FORCECHANGED, NULL, NULL); // update the windowtitle DlgProc(hDlg, HM_SETWINDOWTITLE, NULL, lParam); } else if ( !lstrcmpA(pdbcws->szModule, USERINFO) || !lstrcmpA(pdbcws->szModule, pPs->pszProto) || !lstrcmpA(pdbcws->szModule, MOD_MBIRTHDAY)) { // force the update of all propertysheetpages DlgProc(hDlg, PSM_FORCECHANGED, NULL, NULL); } } break; case WM_NOTIFY: switch (wParam) { // // Notification Messages sent by the TreeView // case STATIC_TREE: switch (((LPNMHDR)lParam)->code) { case TVN_SELCHANGING: { pPs->dwFlags |= PSF_LOCKED; pPs->pTree->OnSelChanging(); pPs->dwFlags &= ~PSF_LOCKED; break; } case TVN_SELCHANGED: if (pPs->dwFlags & PSF_INITIALIZED) { pPs->dwFlags |= PSF_LOCKED; pPs->pTree->OnSelChanged((LPNMTREEVIEW)lParam); if (pPs->pTree->CurrentItem()) { RECT rc; POINT pt = { 0, 0 }; GetWindowRect(GetDlgItem(hDlg, IDC_PAGETITLE), &rc); ScreenToClient(hDlg, &pt); OffsetRect(&rc, pt.x, pt.y); SetDlgItemText(hDlg, IDC_PAGETITLE, pPs->pTree->CurrentItem()->Label()); InvalidateRect(GetDlgItem(hDlg, IDC_PAGETITLEBG), &rc, TRUE); InvalidateRect(hDlg, &rc, TRUE); } pPs->dwFlags &= ~PSF_LOCKED; } break; case TVN_BEGINDRAG: { LPNMTREEVIEW nmtv = (LPNMTREEVIEW)lParam; if (nmtv->itemNew.hItem == TreeView_GetSelection(nmtv->hdr.hwndFrom)) { SetCapture(hDlg); pPs->pTree->BeginDrag(nmtv->itemNew.hItem); } TreeView_SelectItem(nmtv->hdr.hwndFrom, nmtv->itemNew.hItem); break; } case TVN_ITEMEXPANDED: pPs->pTree->AddFlags(PSTVF_STATE_CHANGED); break; case NM_KILLFOCUS: KillTimer(hDlg, TIMERID_RENAME); break; case NM_CLICK: { TVHITTESTINFO hti; GetCursorPos(&hti.pt); ScreenToClient(pPs->pTree->Window(), &hti.pt); TreeView_HitTest(pPs->pTree->Window(), &hti); if ((hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) && hti.hItem == TreeView_GetSelection(pPs->pTree->Window())) SetTimer(hDlg, TIMERID_RENAME, 500, NULL); break; } case NM_RCLICK: pPs->pTree->PopupMenu(); return 0; } break; } break; case WM_MOUSEMOVE: if (pPs->pTree->IsDragging()) { TVHITTESTINFO hti; hti.pt.x = (SHORT)LOWORD(lParam); hti.pt.y = (SHORT)HIWORD(lParam); MapWindowPoints(hDlg, pPs->pTree->Window(), &hti.pt, 1); TreeView_HitTest(pPs->pTree->Window(), &hti); if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) { RECT rc; BYTE height; // check where over the item, the pointer is if (TreeView_GetItemRect(pPs->pTree->Window(), hti.hItem, &rc, FALSE)) { height = (BYTE)(rc.bottom - rc.top); if (hti.pt.y - (height / 3) < rc.top) { SetCursor(LoadCursor(NULL, IDC_ARROW)); TreeView_SetInsertMark(pPs->pTree->Window(), hti.hItem, 0); } else if (hti.pt.y + (height / 3) > rc.bottom) { SetCursor(LoadCursor(NULL, IDC_ARROW)); TreeView_SetInsertMark(pPs->pTree->Window(), hti.hItem, 1); } else { TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0); SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(CURSOR_ADDGROUP))); } } } else { if (hti.flags & TVHT_ABOVE) SendMessage(pPs->pTree->Window(), WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); if (hti.flags & TVHT_BELOW) SendMessage(pPs->pTree->Window(), WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0); } } break; case WM_LBUTTONUP: // drop item if (pPs->pTree->IsDragging()) { TVHITTESTINFO hti; RECT rc; BYTE height; BYTE bAsChild = FALSE; TreeView_SetInsertMark(pPs->pTree->Window(), NULL, 0); ReleaseCapture(); SetCursor(LoadCursor(NULL, IDC_ARROW)); hti.pt.x = (SHORT)LOWORD(lParam); hti.pt.y = (SHORT)HIWORD(lParam); MapWindowPoints(hDlg, pPs->pTree->Window(), &hti.pt, 1); TreeView_HitTest(pPs->pTree->Window(), &hti); if (hti.hItem == pPs->pTree->DragItem()) { pPs->pTree->EndDrag(); break; } if (hti.flags & TVHT_ABOVE) { hti.hItem = TVI_FIRST; } else if (hti.flags & (TVHT_NOWHERE|TVHT_BELOW)) { hti.hItem = TVI_LAST; } else if (hti.flags & (TVHT_ONITEM|TVHT_ONITEMRIGHT)) { // check where over the item, the pointer is if (!TreeView_GetItemRect(pPs->pTree->Window(), hti.hItem, &rc, FALSE)) { pPs->pTree->EndDrag(); break; } height = (BYTE)(rc.bottom - rc.top); if (hti.pt.y - (height / 3) < rc.top) { HTREEITEM hItem = hti.hItem; if (!(hti.hItem = TreeView_GetPrevSibling(pPs->pTree->Window(), hItem))) { if (!(hti.hItem = TreeView_GetParent(pPs->pTree->Window(), hItem))) hti.hItem = TVI_FIRST; else bAsChild = TRUE; } } else if (hti.pt.y + (height / 3) <= rc.bottom) { bAsChild = TRUE; } } pPs->pTree->MoveItem(pPs->pTree->DragItem(), hti.hItem, bAsChild); pPs->pTree->EndDrag(); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: { pPs->pTree->OnCancel(); DestroyWindow(hDlg); break; } /** * name: IDOK / IDAPPLY * desc: user clicked on apply or ok button in order to save changes **/ case IDOK: case IDAPPLY: if (pPs->dwFlags & PSF_CHANGED) { // kill focus from children to make sure all data can be saved (ComboboxEx) SetFocus(hDlg); pPs->dwFlags |= PSF_LOCKED; if (pPs->pTree->OnApply()) { pPs->dwFlags &= ~(PSF_LOCKED|PSF_CHANGED); break; } pPs->dwFlags &= ~PSF_CHANGED; EnableWindow(GetDlgItem(hDlg, IDAPPLY), FALSE); CallService(MS_CLIST_INVALIDATEDISPLAYNAME, (WPARAM)pPs->hContact, NULL); // need to upload owners settings if (!pPs->hContact && myGlobals.CanChangeDetails && db_get_b(NULL, MODNAME, SET_PROPSHEET_CHANGEMYDETAILS, FALSE)) { if (pPs->pUpload = new CPsUpload(pPs, LOWORD(wParam) == IDOK)) { if (pPs->pUpload->UploadFirst() == CPsUpload::UPLOAD_CONTINUE) break; MIR_DELETE(pPs->pUpload); } } pPs->dwFlags &= ~PSF_LOCKED; } if (LOWORD(wParam) == IDOK) DestroyWindow(hDlg); break; case BTN_UPDATE: { if (pPs->hContact != NULL) { ResetUpdateInfo(pPs); mir_snprintf(pPs->szUpdating, SIZEOF(pPs->szUpdating), "%s (%s)", Translate("Updating"), pPs->pszProto); // need meta contact's subcontact information if (DB::Module::IsMetaAndScan(pPs->pszProto)) { HANDLE hSubContact; int i, numSubs; numSubs = DB::MetaContact::SubCount(pPs->hContact); // count valid subcontacts whose protocol supports the PSS_GETINFO service to update the information for (i = 0; i < numSubs; i++) { hSubContact = DB::MetaContact::Sub(pPs->hContact, i); if (hSubContact != NULL) { if ( ProtoServiceExists(DB::Contact::Proto(hSubContact), PSS_GETINFO)) { pPs->infosUpdated = (TAckInfo*)mir_realloc(pPs->infosUpdated, sizeof(TAckInfo) * (pPs->nSubContacts + 1)); pPs->infosUpdated[pPs->nSubContacts].hContact = hSubContact; pPs->infosUpdated[pPs->nSubContacts].acks = NULL; pPs->infosUpdated[pPs->nSubContacts].count = 0; pPs->nSubContacts++; } } } if (pPs->nSubContacts != 0) { BYTE bDo = FALSE; // call the services for (i = 0; i < pPs->nSubContacts; i++) { if (!CallContactService(pPs->infosUpdated[pPs->nSubContacts].hContact, PSS_GETINFO, NULL, NULL)) { bDo = TRUE; } } if (bDo) { EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE); ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_SHOW); SetTimer(hDlg, TIMERID_UPDATING, 100, NULL); } } } else if (!CallContactService(pPs->hContact, PSS_GETINFO, NULL, NULL)) { pPs->infosUpdated = (TAckInfo*)mir_calloc(sizeof(TAckInfo)); pPs->infosUpdated[0].hContact = pPs->hContact; pPs->nSubContacts = 1; EnableWindow(GetDlgItem(hDlg, BTN_UPDATE), FALSE); ShowWindow(GetDlgItem(hDlg, TXT_UPDATING), SW_SHOW); SetTimer(hDlg, TIMERID_UPDATING, 100, NULL); } } } break; case BTN_IMPORT: svcExIm_ContactImport_Service((WPARAM) pPs->hContact, 0); break; case BTN_EXPORT: // save changes before exporting data DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(IDAPPLY, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDAPPLY)); // do the exporting stuff svcExIm_ContactExport_Service((WPARAM) pPs->hContact, 0); break; } break; case WM_CLOSE: DlgProc(hDlg, WM_COMMAND, MAKEWPARAM(IDCANCEL, BN_CLICKED), (LPARAM)GetDlgItem(hDlg, IDCANCEL)); break; case WM_DESTROY: { int i = 0; // hide before destroy ShowWindow(hDlg, SW_HIDE); ResetUpdateInfo(pPs); // avoid any further message processing for this dialog page WindowList_Remove(ghWindowList, hDlg); SetUserData(hDlg, NULL); // unhook events and stop timers KillTimer(hDlg, TIMERID_RENAME); UnhookEvent(pPs->hProtoAckEvent); UnhookEvent(pPs->hSettingChanged); UnhookEvent(pPs->hIconsChanged); // save my window position Utils_SaveWindowPosition(hDlg, NULL, MODNAME, "DetailsDlg"); // save current tree and destroy it if (pPs->pTree != NULL) { // save tree's current look pPs->pTree->SaveState(); delete pPs->pTree; pPs->pTree = NULL; } DeleteObject(pPs->hCaptionFont); DeleteObject(pPs->hBoldFont); mir_free(pPs); pPs = NULL; } } return FALSE; }