summaryrefslogtreecommitdiff
path: root/plugins/UserInfoEx/src/svc_refreshci.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/UserInfoEx/src/svc_refreshci.cpp')
-rw-r--r--plugins/UserInfoEx/src/svc_refreshci.cpp936
1 files changed, 936 insertions, 0 deletions
diff --git a/plugins/UserInfoEx/src/svc_refreshci.cpp b/plugins/UserInfoEx/src/svc_refreshci.cpp
new file mode 100644
index 0000000000..bddc9e60ca
--- /dev/null
+++ b/plugins/UserInfoEx/src/svc_refreshci.cpp
@@ -0,0 +1,936 @@
+/*
+UserinfoEx plugin for Miranda IM
+
+Copyright:
+ฉ 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+===============================================================================
+
+File name : $HeadURL: https://userinfoex.googlecode.com/svn/trunk/svc_refreshci.cpp $
+Revision : $Revision: 187 $
+Last change on : $Date: 2010-09-08 16:05:54 +0400 (ะกั€, 08 ัะตะฝ 2010) $
+Last change by : $Author: ing.u.horn $
+
+===============================================================================
+*/
+#include "commonheaders.h"
+#include "mir_contactqueue.h"
+#include "mir_menuitems.h"
+#include "svc_refreshci.h"
+
+#define HM_PROTOACK (WM_USER+100)
+
+typedef INT_PTR (*PUpdCallback) (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, PVOID UserData);
+
+/***********************************************************************************************************
+ * class CUpdProgress
+ ***********************************************************************************************************/
+
+class CUpdProgress
+{
+protected:
+ BOOLEAN _bBBCode; // TRUE if text renderer can handle BBCodes
+ BOOLEAN _bIsCanceled; // is set to TRUE uppon click on the CANCEL button
+ PUpdCallback _pFnCallBack; // a pointer to a callback function, which can be used
+ // to catch several messages by the caller.
+ PVOID _pData; // application defined data
+ HWND _hWnd; // window handle of the progress dialog/popup
+
+ /**
+ * This is the default window procedure, which is called for both progress dialog and popup.
+ * It handles some common messages and calls the user defined callback function if defined.
+ *
+ * @param pProgress - This is the pointer to the object of CUpdProgress.
+ * @param hWnd - HWND window handle of the progress dialog
+ * @param uMsg - message sent to the dialog
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return This method returns 0.
+ **/
+ static INT_PTR CALLBACK DefWndProc(CUpdProgress *pProgress, HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ __try
+ {
+ if (PtrIsValid(pProgress))
+ {
+ switch (uMsg)
+ {
+ case UM_POPUPACTION:
+ case WM_COMMAND:
+ {
+ if (wParam == MAKEWORD(IDSKIP, BN_CLICKED))
+ {
+ pProgress->Destroy();
+ }
+ else
+ if (wParam == MAKEWORD(IDCANCEL, BN_CLICKED))
+ {
+ pProgress->_bIsCanceled = TRUE;
+ }
+ }
+ }
+ if (PtrIsValid(pProgress->_pFnCallBack))
+ {
+ pProgress->_pFnCallBack(hWnd, uMsg, wParam, lParam, pProgress->_pData);
+ }
+ }
+ }
+ __except(GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ { // code to handle exception
+ puts("Exception Occurred");
+ }
+ return 0;
+ }
+
+public:
+
+ virtual HWND Create (LPCTSTR szTitle, PUpdCallback pFnCallBack) = 0;
+ virtual VOID Destroy (VOID) {};
+ virtual VOID SetTitle (LPCTSTR szText) = 0;
+ virtual VOID SetText (LPCTSTR szText) = 0;
+
+ BOOLEAN IsVisible() const
+ {
+ return _hWnd != NULL;
+ }
+ /**
+ *
+ *
+ **/
+ BOOLEAN IsCanceled() const
+ {
+ return _bIsCanceled;
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID SetTitleParam(LPCTSTR szText, ...)
+ {
+ if (szText)
+ {
+ TCHAR buf[MAXDATASIZE];
+ va_list vl;
+
+ va_start(vl, szText);
+ if (mir_vsntprintf(buf, SIZEOF(buf), szText, vl) != -1)
+ {
+ SetTitle(buf);
+ }
+ va_end(vl);
+ }
+ }
+
+ /**
+ * This method is used to set the popups or dialogs message text.
+ * It takes text with parameters as sprintf does. If bbcodes are
+ * disabled this method automatically deletes them from the text.
+ *
+ * @param szText - the text to display. Can contain formats like
+ * sprintf does.
+ * @param ... - a list of parameters, which depends on the
+ * format of szText.
+ *
+ * @return nothing
+ **/
+ VOID SetTextParam(LPCTSTR szText, ...)
+ {
+ if (szText)
+ {
+ INT_PTR cch = mir_tcslen(szText);
+ LPTSTR fmt = (LPTSTR) mir_alloc((cch + 1) * sizeof(TCHAR));
+
+ if (fmt)
+ {
+ TCHAR buf[MAXDATASIZE];
+ va_list vl;
+
+ _tcscpy(fmt, szText);
+
+ // delete bbcodes
+ if (!_bBBCode)
+ {
+ LPTSTR s, e;
+
+ for (s = fmt, e = fmt + cch; s[0] != 0; s++)
+ {
+ if (s[0] == '[')
+ {
+ // leading bbcode tag (e.g.: [b], [u], [i])
+ if ((s[1] == 'b' || s[1] == 'u' || s[1] == 'i') && s[2] == ']')
+ {
+ memmove(s, s + 3, (e - s - 2) * sizeof(TCHAR));
+ e -= 3;
+ }
+ // ending bbcode tag (e.g.: [/b], [/u], [/i])
+ else if (s[1] == '/' && (s[2] == 'b' || s[2] == 'u' || s[2] == 'i') && s[3] == ']')
+ {
+ memmove(s, s + 4, (e - s - 3) * sizeof(TCHAR));
+ e -= 4;
+ }
+ }
+ }
+ }
+
+ va_start(vl, szText);
+ if (mir_vsntprintf(buf, SIZEOF(buf), fmt, vl) != -1)
+ {
+ SetText(buf);
+ }
+ va_end(vl);
+ mir_free(fmt);
+ }
+ }
+ }
+
+ /**
+ *
+ *
+ **/
+ CUpdProgress()
+ {
+ _hWnd = NULL;
+ _pFnCallBack = NULL;
+ _pData = NULL;
+ _bIsCanceled = FALSE;
+ _bBBCode = FALSE;
+ }
+
+ /**
+ *
+ *
+ **/
+ CUpdProgress(PVOID data)
+ {
+ _hWnd = NULL;
+ _pFnCallBack = NULL;
+ _pData = data;
+ _bIsCanceled = FALSE;
+ _bBBCode = FALSE;
+ }
+
+ ~CUpdProgress()
+ {
+ }
+
+};
+
+/***********************************************************************************************************
+ * class CDlgUpdProgress
+ ***********************************************************************************************************/
+
+class CDlgUpdProgress : public CUpdProgress
+{
+ /**
+ *
+ *
+ **/
+ static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ const ICONCTRL idIcon[] = {
+ { ICO_BTN_UPDATE, WM_SETICON, NULL },
+ { ICO_BTN_DOWNARROW, BM_SETIMAGE, IDSKIP },
+ { ICO_BTN_CANCEL, BM_SETIMAGE, IDCANCEL }
+ };
+ IcoLib_SetCtrlIcons(hWnd, idIcon, DB::Setting::GetByte(SET_ICONS_BUTTONS, 1) ? 2 : 1);
+
+ SendDlgItemMessage(hWnd, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
+ SendDlgItemMessage(hWnd, IDSKIP, BUTTONTRANSLATE, NULL, NULL);
+ SetUserData(hWnd, lParam);
+
+ TranslateDialogDefault(hWnd);
+ }
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID))
+ {
+ case STATIC_WHITERECT:
+ case IDC_INFO:
+ {
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return GetSysColor(COLOR_WINDOW);
+ }
+ }
+ }
+ return FALSE;
+
+ }
+ return CUpdProgress::DefWndProc((CUpdProgress *) GetUserData(hWnd), hWnd, uMsg, wParam, lParam);
+ }
+
+public:
+
+ /**
+ *
+ *
+ **/
+ CDlgUpdProgress(PVOID data)
+ : CUpdProgress(data)
+ {
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
+ {
+ _pFnCallBack = pFnCallBack;
+ _hWnd = CreateDialogParam(ghInst,
+ MAKEINTRESOURCE(IDD_REFRESHDETAILS),
+ 0,
+ (DLGPROC)CDlgUpdProgress::WndProc,
+ (LPARAM) this);
+ if (_hWnd)
+ {
+ SetTitle(szTitle);
+ ShowWindow(_hWnd, SW_SHOW);
+ }
+ return _hWnd;
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID Destroy()
+ {
+ if (_hWnd)
+ {
+ SetUserData(_hWnd, NULL);
+ EndDialog(_hWnd, IDOK);
+ _hWnd = NULL;
+ }
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetTitle(LPCTSTR szText)
+ {
+ SetWindowText(_hWnd, szText);
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetText(LPCTSTR szText)
+ {
+ SetDlgItemText(_hWnd, IDC_INFO, szText);
+ }
+
+};
+
+/***********************************************************************************************************
+ * class CPopupUpdProgress
+ ***********************************************************************************************************/
+
+class CPopupUpdProgress : public CUpdProgress
+{
+ LPCTSTR _szText;
+ POPUPACTION _popupButtons[2];
+
+ /**
+ *
+ *
+ **/
+ VOID UpdateText()
+ {
+ if (_szText)
+ {
+ INT_PTR cb = mir_tcslen(_szText) + 8;
+ LPTSTR pb = (LPTSTR) mir_alloc(cb * sizeof(TCHAR));
+
+ if (pb)
+ {
+ mir_tcscpy(pb, _szText);
+
+ SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TITLET, (LPARAM) pb);
+ }
+ }
+ }
+
+ /**
+ * This static member is the window procedure, which is used to modify the behaviour of
+ * a popup dialog, so that it can act as a replacement for the progress dialog.
+ * The major task of this method is to filter out some messages, who would lead to a crash,
+ * if passed to the default windows procedure.
+ *
+ **/
+ static INT_PTR CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+ {
+ // Filter out messages, which must not be passed to default windows procedure or even
+ // to the callback function!
+ switch (uMsg)
+ {
+ case UM_INITPOPUP:
+ case UM_CHANGEPOPUP:
+ case UM_FREEPLUGINDATA:
+ break;
+ default:
+ CUpdProgress::DefWndProc((CUpdProgress *) PUGetPluginData(hWnd), hWnd, uMsg, wParam, lParam);
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+ }
+
+public:
+
+ /**
+ *
+ *
+ **/
+ CPopupUpdProgress(PVOID data)
+ : CUpdProgress(data)
+ {
+ _szText = NULL;
+ _bBBCode = DB::Setting::GetByte("PopUp", "UseMText", FALSE);
+
+ _popupButtons[0].cbSize = sizeof(POPUPACTION);
+ _popupButtons[0].flags = PAF_ENABLED;
+ _popupButtons[0].lchIcon = IcoLib_GetIcon(ICO_BTN_DOWNARROW);
+ _popupButtons[0].wParam = MAKEWORD(IDSKIP, BN_CLICKED);
+ _popupButtons[0].lParam = NULL;
+ strcpy(_popupButtons[0].lpzTitle, MODNAME"/Hide");
+
+ // cancel button
+ _popupButtons[1].cbSize = sizeof(POPUPACTION);
+ _popupButtons[1].flags = PAF_ENABLED;
+ _popupButtons[1].lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ _popupButtons[1].wParam = MAKEWORD(IDCANCEL, BN_CLICKED);
+ _popupButtons[1].lParam = NULL;
+ strcpy(_popupButtons[1].lpzTitle, MODNAME"/Cancel");
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual HWND Create(LPCTSTR szTitle, PUpdCallback pFnCallBack)
+ {
+ POPUPDATAT_V2 pd;
+
+ ZeroMemory(&pd, sizeof(POPUPDATAT_V2));
+ pd.cbSize = sizeof(POPUPDATAT_V2);
+ pd.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ pd.iSeconds = -1;
+ pd.PluginData = this;
+ pd.PluginWindowProc = (WNDPROC)CPopupUpdProgress::WndProc;
+ pd.actionCount = SIZEOF(_popupButtons);
+ pd.lpActions = _popupButtons;
+
+ // dummy text
+ _szText = mir_tcsdup(szTitle);
+ mir_tcscpy(pd.lptzContactName, _szText);
+
+ _tcscpy(pd.lptzText, _T(" "));
+
+ _pFnCallBack = pFnCallBack;
+ _hWnd = (HWND) CallService(MS_POPUP_ADDPOPUPT, (WPARAM) &pd, APF_RETURN_HWND|APF_NEWDATA);
+ return _hWnd;
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID Destroy()
+ {
+ if (_hWnd)
+ {
+ PUDeletePopUp(_hWnd);
+ _hWnd = NULL;
+ }
+ MIR_FREE(_szText);
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetTitle(LPCTSTR szText)
+ {
+ MIR_FREE(_szText);
+ _szText = mir_tcsdup(szText);
+ UpdateText();
+ }
+
+ /**
+ *
+ *
+ **/
+ virtual VOID SetText(LPCTSTR szText)
+ {
+ SendMessage(_hWnd, UM_CHANGEPOPUP, CPT_TEXTT, (LPARAM) mir_tcsdup(szText));
+ }
+};
+
+
+/***********************************************************************************************************
+ * class CContactUpdater
+ ***********************************************************************************************************/
+
+class CContactUpdater : public CContactQueue
+{
+ CUpdProgress* _pProgress; // pointer to the progress dialog/popup
+ HANDLE _hProtoAckEvent; // handle to protocol ack notification
+ HANDLE _hContact; // current contact being refreshed
+ PBYTE _hContactAcks; // array of acknoledgements for the current contact to wait for
+ INT_PTR _nContactAcks; // number of acknoledgements for the current contact to wait for
+
+ MIRANDA_CPP_PLUGIN_API(CContactUpdater);
+
+ /**
+ * This is a callback dialog procedure, which is assigned the update progress dialog to
+ * gain control over certain messages.
+ *
+ * @param hWnd - HWND window handle of the progress dialog
+ * @param uMsg - message sent to the dialog
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ * @param u - This is a parameter assigned by the CUpdProgress' constructur.
+ * In this case it is a pointer to this class's object.
+ *
+ * @return This method returns 0.
+ **/
+ static INT DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, CContactUpdater* u)
+ {
+ switch (uMsg)
+ {
+
+ /**
+ * User has clicked on the skip or cancel button.
+ **/
+ case UM_POPUPACTION:
+ case WM_COMMAND:
+ {
+ if (PtrIsValid(u))
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ {
+ if (HIWORD(wParam) == BN_CLICKED)
+ {
+ u->Cancel();
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * This method handles ack broadcasts from the protocols to determine,
+ * whether a contact's information set's update is complete to continue
+ * with the next faster. By default the queue is configured to wait
+ * about 15s between contacts. If the protocol sends a complete ack,
+ * the time is shortend to 4s.
+ *
+ * @param wParam - not used
+ * @param ack - pointer to an ACKDATA structure containing all
+ * data for the acknoledgement.
+ *
+ * @return nothing
+ **/
+ INT __cdecl OnProtoAck(WPARAM wParam, ACKDATA *ack)
+ {
+ if (ack && ack->cbSize == sizeof(ACKDATA) && ack->hContact == _hContact && ack->type == ACKTYPE_GETINFO)
+ {
+ if (ack->hProcess || ack->lParam)
+ {
+ if (!_hContactAcks)
+ {
+ _nContactAcks = (INT_PTR)ack->hProcess;
+ _hContactAcks = (PBYTE)mir_calloc(sizeof(BYTE) * (INT_PTR)ack->hProcess);
+ }
+ if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED)
+ {
+ _hContactAcks[ack->lParam] = 1;
+ }
+
+ for (INT i = 0; i < _nContactAcks; i++)
+ {
+ if (_hContactAcks[i] == 0)
+ {
+ return 0;
+ }
+ }
+ }
+ // don't wait the full time, but continue immitiatly
+ ContinueWithNext();
+ }
+ return 0;
+ }
+
+ /**
+ * This method is called just before the worker thread is going to suspend,
+ * if the queue is empty.
+ *
+ * @param none
+ *
+ * @return nothing
+ **/
+ virtual VOID OnEmpty()
+ {
+ // This was the last contact, so destroy the progress window.
+ if (_hProtoAckEvent)
+ {
+ UnhookEvent(_hProtoAckEvent);
+ _hProtoAckEvent = NULL;
+ }
+
+ // free up last ackresult array
+ MIR_FREE(_hContactAcks);
+ _nContactAcks = 0;
+ _hContact = NULL;
+
+ // close progress bar
+ if (_pProgress)
+ {
+ _pProgress->Destroy();
+
+ delete _pProgress;
+ _pProgress = NULL;
+ }
+
+ // reset menu
+ if (hMenuItemRefresh)
+ {
+ CLISTMENUITEM clmi;
+
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME|CMIM_ICON;
+ clmi.pszName = LPGEN("Refresh Contact Details");
+ clmi.hIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM)&clmi);
+ }
+ }
+
+ /**
+ * This virtual method is called by the derived CContactQueue class,
+ * if an action is requested for a queue item.
+ *
+ * @param hContact - the handle of the contact an action is requested for
+ * @param param - not used here
+ *
+ * @return nothing
+ **/
+ virtual VOID Callback(HANDLE hContact, PVOID param)
+ {
+ LPSTR pszProto = DB::Contact::Proto(hContact);
+
+ if (pszProto && pszProto[0])
+ {
+ MIR_FREE(_hContactAcks);
+ _nContactAcks = 0;
+ _hContact = hContact;
+
+ if (!_hProtoAckEvent)
+ {
+ _hProtoAckEvent = (HANDLE) ThisHookEvent(ME_PROTO_ACK, (EVENTHOOK) &CContactUpdater::OnProtoAck);
+ }
+
+ if (_pProgress)
+ {
+ _pProgress->SetTextParam(TranslateT("[b]%s (%S)...[/b]\n%d Contacts remaning"),
+ DB::Contact::DisplayName(_hContact), pszProto, Size());
+ }
+ if (IsProtoOnline(pszProto))
+ {
+ INT i;
+ for (i = 0; i < 3 && CallContactService(hContact, PSS_GETINFO, 0, 0); i++)
+ {
+ Sleep(3000);
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ * This is the default constructor
+ *
+ **/
+ CContactUpdater() : CContactQueue()
+ {
+ _hContactAcks = NULL;
+ _nContactAcks = 0;
+ _hContact = NULL;
+ _pProgress = NULL;
+ _hProtoAckEvent = NULL;
+ }
+
+ /**
+ *
+ *
+ **/
+ ~CContactUpdater()
+ {
+ RemoveAll();
+ OnEmpty();
+ }
+
+ /**
+ *
+ *
+ **/
+ BOOL QueueAddRefreshContact(HANDLE hContact, INT iWait)
+ {
+ LPSTR pszProto = DB::Contact::Proto(hContact);
+
+ if ((mir_strcmp(pszProto, "Weather")!=0) &&
+ (mir_strcmp(pszProto, "MetaContacts")!=0) &&
+ IsProtoOnline(pszProto))
+ {
+ return Add(iWait, hContact);
+ }
+ return 0;
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID RefreshAll()
+ {
+ HANDLE hContact;
+ INT iWait;
+
+ for (hContact = DB::Contact::FindFirst(), iWait = 100;
+ hContact != NULL;
+ hContact = DB::Contact::FindNext(hContact))
+ {
+ if (QueueAddRefreshContact(hContact, iWait))
+ {
+ iWait += 5000;
+ }
+ }
+ if (Size() && !_pProgress)
+ {
+ if (ServiceExists(MS_POPUP_CHANGETEXTT) && DB::Setting::GetByte("PopupProgress", FALSE))
+ {
+ _pProgress = new CPopupUpdProgress(this);
+ }
+ else
+ {
+ _pProgress = new CDlgUpdProgress(this);
+ }
+
+ _pProgress->Create(TranslateT("Refresh Contact Details"), (PUpdCallback) CContactUpdater::DlgProc);
+ _pProgress->SetText(TranslateT("Preparing..."));
+ }
+
+ // if there are contacts in the queue, change the main menu item to indicate it is meant for canceling.
+ if (hMenuItemRefresh && Size() > 0)
+ {
+ CLISTMENUITEM clmi;
+
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_NAME|CMIM_ICON;
+ clmi.pszName = LPGEN("Abort Refreshing Contact Details");
+ clmi.hIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM) hMenuItemRefresh, (LPARAM) &clmi);
+ }
+ }
+
+ /**
+ *
+ *
+ **/
+ VOID Cancel()
+ {
+ RemoveAll();
+ ContinueWithNext();
+ }
+
+};
+
+static CContactUpdater *ContactUpdater = NULL;
+
+/***********************************************************************************************************
+ * common helper functions
+ ***********************************************************************************************************/
+
+/**
+ * This function checks, whether at least one protocol is online!
+ *
+ * @param none
+ *
+ * @retval TRUE - At least one protocol is online.
+ * @retval FALSE - All protocols are offline.
+ **/
+static BOOL IsMirandaOnline()
+{
+ PROTOACCOUNT **pAcc;
+ INT i, nAccCount;
+ BOOL bIsOnline = FALSE;
+
+ if (MIRSUCCEEDED(ProtoEnumAccounts(&nAccCount, &pAcc)))
+ {
+ for (i = 0; (i < nAccCount) && !bIsOnline; i++)
+ {
+ bIsOnline |= (IsProtoAccountEnabled(pAcc[i]) && IsProtoOnline(pAcc[i]->szModuleName));
+ }
+ }
+ return bIsOnline;
+}
+
+/***********************************************************************************************************
+ * services
+ ***********************************************************************************************************/
+
+/**
+ * This is the service function being called by MS_USERINFO_REFRESH.
+ * It adds each contact, whose protocol is online, to the queue of contacts to refresh.
+ * The queue is running a separate thread, which is responsible for requesting the contact information
+ * one after another with a certain time to wait in between.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return This service function always returns 0.
+ **/
+static INT_PTR RefreshService(WPARAM wParam, LPARAM lParam)
+{
+ try
+ {
+ if (IsMirandaOnline())
+ {
+ if (!ContactUpdater)
+ {
+ ContactUpdater = new CContactUpdater();
+ }
+
+ if (ContactUpdater->Size() == 0)
+ {
+ ContactUpdater->RefreshAll();
+ }
+ else if (IDYES == MsgBox(NULL, MB_YESNO|MB_ICON_QUESTION, LPGENT("Refresh Contact Details"), NULL,
+ LPGENT("Do you want to cancel the current refresh procedure?")))
+ {
+ ContactUpdater->Cancel();
+ }
+ }
+ else
+ {
+ MsgErr(NULL, LPGENT("Miranda must be online for refreshing contact information!"));
+ }
+ }
+ catch(...)
+ {
+ MsgErr(NULL, LPGENT("The function caused an exception!"));
+ }
+ return 0;
+}
+
+/***********************************************************************************************************
+ * events
+ ***********************************************************************************************************/
+
+/**
+ *
+ *
+ **/
+static INT OnContactAdded(WPARAM wParam, LPARAM lParam)
+{
+ try
+ {
+ DWORD dwStmp;
+
+ dwStmp = DB::Setting::GetDWord((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME, 0);
+ if (!dwStmp)
+ {
+ MTime mt;
+
+ mt.GetLocalTime();
+ mt.DBWriteStamp((HANDLE)wParam, USERINFO, SET_CONTACT_ADDEDTIME);
+
+ // create updater, if not yet exists
+ if (!ContactUpdater)
+ {
+ ContactUpdater = new CContactUpdater();
+ }
+
+ // add to the end of the queue
+ ContactUpdater->AddIfDontHave(
+ (ContactUpdater->Size() > 0)
+ ? max(ContactUpdater->Get(ContactUpdater->Size() - 1)->check_time + 15000, 4000)
+ : 4000,
+ (HANDLE)wParam);
+ }
+ }
+ catch(...)
+ {
+ MsgErr(NULL, LPGENT("The function caused an exception!"));
+ }
+ return 0;
+}
+
+/**
+ * Miranda is going to shutdown soon, so any panding contact information refresh is to be terminated
+ * and the queue object must be deleted. Further refresh requests will be ignored.
+ *
+ * @param wParam - not used
+ * @param lParam - not used
+ *
+ * @return This function always returns 0.
+ **/
+static INT OnPreShutdown(WPARAM, LPARAM)
+{
+ if(ContactUpdater) {
+ delete ContactUpdater;
+ ContactUpdater = 0;
+ }
+ //MIR_DELETE(ContactUpdater);
+ return 0;
+}
+
+/***********************************************************************************************************
+ * initialization
+ ***********************************************************************************************************/
+
+/**
+ * This function initially loads the module uppon startup.
+ **/
+VOID SvcRefreshContactInfoLoadModule(VOID)
+{
+ CreateServiceFunction(MS_USERINFO_REFRESH, RefreshService);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown);
+ HookEvent(ME_DB_CONTACT_ADDED, OnContactAdded);
+
+ HOTKEYDESC hk = { 0 };
+ hk.cbSize = sizeof(HOTKEYDESC);
+ hk.pszSection = MODNAME;
+ hk.pszName = "RefreshContactDetails";
+ hk.pszDescription = LPGEN("Refresh Contact Details");
+ hk.pszService = MS_USERINFO_REFRESH;
+ Hotkey_Register(&hk);
+}