/* UserinfoEx plugin for Miranda IM Copyright: � 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol part of this code based on: Miranda IM Country Flags Plugin Copyright �2006-2007 H. Herkenrath 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. */ #include "..\commonheaders.h" #define M_ENABLE_SUBCTLS (WM_APP+1) FLAGSOPTIONS gFlagsOpts; /* Misc */ int nCountriesCount; struct CountryListEntry *countries; static HANDLE hExtraIconSvc = INVALID_HANDLE_VALUE; /* hook */ static HANDLE hRebuildIconsHook = NULL; static HANDLE hApplyIconHook = NULL; static HANDLE hMsgWndEventHook = NULL; static HANDLE hIconsChangedHook = NULL; static HANDLE hSettingChangedHook = NULL; static HANDLE hOptInitHook = NULL; static int OnContactSettingChanged(WPARAM wParam,LPARAM lParam); /*********************************************************************************************************** * Buffered functions ***********************************************************************************************************/ struct BufferedCallData { DWORD startTick; UINT uElapse; BUFFEREDPROC pfnBuffProc; LPARAM lParam; #ifdef _DEBUG const char *pszProcName; #endif }; static UINT_PTR idBufferedTimer; static struct BufferedCallData *callList; static int nCallListCount; // always gets called in main message loop static void CALLBACK BufferedProcTimer(HWND hwnd, UINT msg, UINT idTimer, DWORD currentTick) { int i; struct BufferedCallData *buf; UINT uElapsed, uElapseNext = USER_TIMER_MAXIMUM; BUFFEREDPROC pfnBuffProc; LPARAM lParam; #ifdef _DEBUG char szDbgLine[256]; const char *pszProcName; #endif UNREFERENCED_PARAMETER(msg); for(i=0;i=callList[i].uElapse) { /* call elapsed proc */ pfnBuffProc=callList[i].pfnBuffProc; lParam=callList[i].lParam; #ifdef _DEBUG pszProcName=callList[i].pszProcName; #endif /* resize storage array */ if ((i+1)startTick=GetTickCount(); data->uElapse=uElapse; data->lParam=lParam; data->pfnBuffProc=pfnBuffProc; #ifdef _DEBUG { char szDbgLine[256]; data->pszProcName=pszProcName; mir_snprintf(szDbgLine,sizeof(szDbgLine),"buffered queue: %s(0x%X)\n",pszProcName,lParam); /* all ascii */ OutputDebugStringA(szDbgLine); if (!idBufferedTimer) { mir_snprintf(szDbgLine,sizeof(szDbgLine),"next buffered timeout: %ums\n",uElapse); /* all ascii */ OutputDebugStringA(szDbgLine); } } #endif /* set next timer */ if (idBufferedTimer) uElapse=USER_TIMER_MINIMUM; /* will get recalculated */ idBufferedTimer=SetTimer(NULL,idBufferedTimer,uElapse,(TIMERPROC)BufferedProcTimer); } // assumes to be called in context of main thread void PrepareBufferedFunctions() { idBufferedTimer=0; nCallListCount=0; callList=NULL; } // assumes to be called in context of main thread void KillBufferedFunctions() { if (idBufferedTimer) KillTimer(NULL,idBufferedTimer); nCallListCount=0; mir_free(callList); /* does NULL check */ } /*********************************************************************************************************** * service functions ***********************************************************************************************************/ static INT_PTR ServiceDetectContactOriginCountry(WPARAM wParam,LPARAM lParam) { WORD countryNumber; char *pszProto = GetContactProto((HANDLE)wParam); /* UserinfoEx */ if (countryNumber = db_get_w((HANDLE)wParam, USERINFO, SET_CONTACT_ORIGIN_COUNTRY, 0)) return (INT_PTR)countryNumber; else if (countryNumber = db_get_w((HANDLE)wParam, USERINFO, SET_CONTACT_COUNTRY, 0)) return (INT_PTR)countryNumber; else if (countryNumber = db_get_w((HANDLE)wParam, USERINFO, SET_CONTACT_COMPANY_COUNTRY, 0)) return (INT_PTR)countryNumber; /* fallback proto settings */ else if (countryNumber = db_get_w((HANDLE)wParam, pszProto, "Country", 0)) return (INT_PTR)countryNumber; else if (countryNumber = db_get_w((HANDLE)wParam, pszProto, "CompanyCountry", 0)) return (INT_PTR)countryNumber; /* fallback ip detect else if (countryNumber==0xFFFF && db_get_b(NULL,"Flags","UseIpToCountry",SETTING_USEIPTOCOUNTRY_DEFAULT)) { countryNumber=ServiceIpToCountry(db_get_dw((HANDLE)wParam,pszProto,"RealIP",0),0); }*/ return (INT_PTR)0xFFFF; } /*********************************************************************************************************** * Clist Extra Image functions ***********************************************************************************************************/ static void CALLBACK SetExtraImage(LPARAM lParam) { /* get contact's country */ int countryNumber = ServiceDetectContactOriginCountry((WPARAM)lParam,0); ExtraIcon_SetIcon(hExtraIconSvc, (HANDLE)lParam, (countryNumber != 0xFFFF || gFlagsOpts.bUseUnknownFlag) ? LoadFlagHandle(countryNumber) : NULL); } static void CALLBACK RemoveExtraImages(LPARAM lParam) { /* enum all contacts */ for (HANDLE hContact = db_find_first(); hContact != NULL; hContact = db_find_next(hContact)) ExtraIcon_Clear(hExtraIconSvc, hContact); } // always call in context of main thread void EnsureExtraImages() { for (HANDLE hContact = db_find_first(); hContact != NULL; hContact = db_find_next(hContact)) CallFunctionBuffered(SetExtraImage,(LPARAM)hContact,TRUE,EXTRAIMAGE_REFRESHDELAY); } static void CALLBACK UpdateExtraImages(LPARAM lParam) { if (!lParam) RemoveExtraImages(0); else EnsureExtraImages(); } static int OnCListApplyIcons(WPARAM wParam,LPARAM lParam) { SetExtraImage((LPARAM)wParam); /* unbuffered */ return 0; } //hookProc (ME_DB_CONTACT_SETTINGCHANGED) - workaround for missing event from ExtraIconSvc static int OnExtraIconSvcChanged(WPARAM wParam,LPARAM lParam) { DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam; if ((HANDLE)wParam != NULL) return 0; if (!lstrcmpA(dbcws->szModule, "ExtraIcons") && !lstrcmpA(dbcws->szSetting,"Slot_Flags")) { BOOL bEnable; switch (dbcws->value.type) { case DBVT_BYTE: bEnable = dbcws->value.bVal != (BYTE)-1; break; case DBVT_WORD: bEnable = dbcws->value.wVal != (WORD)-1; break; case DBVT_DWORD: bEnable = dbcws->value.dVal != (DWORD)-1; break; default: bEnable = -1; break; } if (bEnable == -1) return 0; if (bEnable && !hApplyIconHook) hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons); else if (!bEnable && hApplyIconHook) UnhookEvent(hApplyIconHook); hApplyIconHook = NULL; CallFunctionBuffered(UpdateExtraImages,(LPARAM)bEnable,FALSE,EXTRAIMAGE_REFRESHDELAY); } return 0; } void SvcFlagsEnableExtraIcons(BYTE bColumn, BYTE bUpdateDB) { gFlagsOpts.bShowExtraImgFlag = (bColumn!=((BYTE)-1)); if (bUpdateDB) db_set_b(NULL, MODNAMEFLAGS, "ShowExtraImgFlag", bColumn!=(BYTE)-1); // Flags is on if (gFlagsOpts.bShowExtraImgFlag) { if (hExtraIconSvc == INVALID_HANDLE_VALUE) { //get local langID for descIcon (try to use user local Flag as icon) DWORD langid = 0; int r = GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ICOUNTRY | LOCALE_RETURN_NUMBER , (LPTSTR)&langid, sizeof(langid)/sizeof(TCHAR)); if (!CallService(MS_UTILS_GETCOUNTRYBYNUMBER,langid,0)) langid = 1; char szId[20]; mir_snprintf(szId, SIZEOF(szId), (langid==0xFFFF)?"%s_0x%X":"%s_%i","flags",langid); /* buffer safe */ hExtraIconSvc = ExtraIcon_Register("Flags", LPGEN("Flags (uinfoex)"), szId); if (hExtraIconSvc) HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnExtraIconSvcChanged); } //init hooks if (!hApplyIconHook) hApplyIconHook = HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, OnCListApplyIcons); if (!hSettingChangedHook) hSettingChangedHook = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged); } } /*********************************************************************************************************** * message winsow status icon functions ***********************************************************************************************************/ MsgWndData::MsgWndData(HWND hwnd, HANDLE hContact) { m_hwnd = hwnd; m_hContact = hContact; m_countryID = (int)ServiceDetectContactOriginCountry((WPARAM)m_hContact,0); StatusIconData sid = { sizeof(sid) }; sid.szModule = MODNAMEFLAGS; sid.flags = MBF_HIDDEN; Srmm_AddIcon(&sid); FlagsIconUpdate(); } MsgWndData::~MsgWndData() { FlagsIconUnset(); //check if realy need } void MsgWndData::FlagsIconSet() { /* ensure status icon is registered */ if (m_countryID != 0xFFFF || gFlagsOpts.bUseUnknownFlag) { StatusIconData sid = { sizeof(sid) }; sid.szModule = MODNAMEFLAGS; sid.hIconDisabled = sid.hIcon = LoadFlagIcon(m_countryID); sid.szTooltip = Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,m_countryID,0)); Srmm_ModifyIcon(m_hContact, &sid); } } void MsgWndData::FlagsIconUnset() { StatusIconData sid = { sizeof(sid) }; sid.szModule = MODNAMEFLAGS; sid.flags = MBF_HIDDEN; Srmm_ModifyIcon(m_hContact, &sid); } static int CompareMsgWndData(const MsgWndData* p1, const MsgWndData* p2) { return (int)((INT_PTR)p1->m_hContact - (INT_PTR)p2->m_hContact); } static LIST gMsgWndList(10, CompareMsgWndData); static int CompareIconListData(const IconList* p1, const IconList* p2) { return (int)((INT_PTR)p1->m_ID - (INT_PTR)p2->m_ID); } static OBJLIST gIListMW(10, CompareIconListData); IconList::IconList(StatusIconData *sid) { m_StatusIconData.cbSize = sid->cbSize; m_StatusIconData.szModule = mir_strdup(sid->szModule); m_StatusIconData.dwId = sid->dwId; m_StatusIconData.hIcon = CopyIcon(sid->hIcon); m_StatusIconData.hIconDisabled = sid->hIconDisabled; m_StatusIconData.flags = sid->flags; m_StatusIconData.szTooltip = mir_strdup(sid->szTooltip); m_ID = sid->dwId; Srmm_AddIcon(sid); } IconList::~IconList() { mir_free(m_StatusIconData.szModule); mir_free(m_StatusIconData.szTooltip); } // const char *pszName; // [Optional] Name of an icon registered with icolib to be used in GUI. static __inline int MessageAPI_AddIcon(const char* pszName, const char* szModul/*StatusIconData *sid*/,int ID, int flags, const char* szTooltip) { HICON hIcon = Skin_GetIcon(pszName); StatusIconData sid = { sizeof(sid) }; sid.szModule = (char*)szModul; sid.dwId = (DWORD)ID; sid.hIcon = (hIcon!=NULL)?CopyIcon(hIcon):NULL; sid.szTooltip = Translate((char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER,ID,0)); Skin_ReleaseIcon(hIcon); /* does NULL check */ int res = -1; IconList* p = new IconList(&sid); if (!p->m_ID)delete p; else res = gIListMW.insert(p); if (res == -1)delete p; return res; } void CALLBACK UpdateStatusIcons(LPARAM lParam) { if (!lParam) { /* enum all opened message windows */ for (int i = 0; i < gMsgWndList.getCount(); i++) gMsgWndList[i]->FlagsIconUpdate(); } else { int i = gMsgWndList.getIndex((MsgWndData*)&lParam); if (i!= -1) gMsgWndList[i]->FlagsIconUpdate(); } } //hookProc ME_MSG_WINDOWEVENT static int OnMsgWndEvent(WPARAM wParam,LPARAM lParam) { MessageWindowEventData *msgwe=(MessageWindowEventData*)lParam; /* sanity check */ if (msgwe->hContact==NULL) return 0; switch(msgwe->uType) { case MSG_WINDOW_EVT_OPENING: { MsgWndData* msgwnd = gMsgWndList.find((MsgWndData*)&msgwe->hContact); if (msgwnd == NULL) { msgwnd = new MsgWndData(msgwe->hwndWindow, msgwe->hContact); gMsgWndList.insert(msgwnd); } } break; case MSG_WINDOW_EVT_CLOSE: { int i = gMsgWndList.getIndex((MsgWndData*)&msgwe->hContact); if (i != -1) { delete gMsgWndList[i]; gMsgWndList.remove(i); } } break; } return 0; } //hookProc ME_SKIN2_ICONSCHANGED static int OnStatusIconsChanged(WPARAM wParam,LPARAM lParam) { if (gFlagsOpts.bShowStatusIconFlag) CallFunctionBuffered(UpdateStatusIcons,0,FALSE,STATUSICON_REFRESHDELAY); return 0; } /*********************************************************************************************************** * option page (not used yet) ***********************************************************************************************************/ static INT_PTR CALLBACK ExtraImgOptDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam) { return FALSE; } //hookProc ME_OPT_INITIALISE static int ExtraImgOptInit(WPARAM wParam,LPARAM lParam) { return 0; } /*********************************************************************************************************** * misc functions ***********************************************************************************************************/ //hookProc ME_DB_CONTACT_SETTINGCHANGED static int OnContactSettingChanged(WPARAM wParam,LPARAM lParam) { if ((HANDLE)wParam==NULL) return 0; DBCONTACTWRITESETTING *dbcws=(DBCONTACTWRITESETTING*)lParam; /* user details update */ if (/*!lstrcmpA(dbcws->szSetting,"RealIP") || */ !lstrcmpA(dbcws->szSetting,SET_CONTACT_COUNTRY) || !lstrcmpA(dbcws->szSetting,SET_CONTACT_ORIGIN_COUNTRY) || !lstrcmpA(dbcws->szSetting,SET_CONTACT_COMPANY_COUNTRY)) { /* Extra Image */ CallFunctionBuffered(SetExtraImage,(LPARAM)wParam,TRUE,EXTRAIMAGE_REFRESHDELAY); /* Status Icon */ if (hMsgWndEventHook) { int i = gMsgWndList.getIndex((MsgWndData*)&wParam); if (i != -1) { gMsgWndList[i]->ContryIDchange((int)ServiceDetectContactOriginCountry(wParam,0)); gMsgWndList[i]->FlagsIconUpdate(); } } } return 0; } /*********************************************************************************************************** * module loading & unloading ***********************************************************************************************************/ /** * This function initially loads all required stuff for Flags. * * @param none * * @return nothing **/ void SvcFlagsLoadModule() { PrepareBufferedFunctions(); if (CallService(MS_UTILS_GETCOUNTRYLIST,(WPARAM)&nCountriesCount,(LPARAM)&countries)) nCountriesCount=0; InitIcons(); /* load in iconlib */ //InitIpToCountry(); /* not implementet */ CreateServiceFunction(MS_FLAGS_DETECTCONTACTORIGINCOUNTRY,ServiceDetectContactOriginCountry); //init settings gFlagsOpts.bShowExtraImgFlag = db_get_b(NULL, MODNAMEFLAGS, "ShowExtraImgFlag", SETTING_SHOWEXTRAIMGFLAG_DEFAULT); gFlagsOpts.bUseUnknownFlag = db_get_b(NULL, MODNAMEFLAGS, "UseUnknownFlag", SETTING_USEUNKNOWNFLAG_DEFAULT); gFlagsOpts.bShowStatusIconFlag = db_get_b(NULL, MODNAMEFLAGS, "ShowStatusIconFlag", SETTING_SHOWSTATUSICONFLAG_DEFAULT); hOptInitHook = HookEvent(ME_OPT_INITIALISE,ExtraImgOptInit); hIconsChangedHook = HookEvent(ME_SKIN2_ICONSCHANGED,OnStatusIconsChanged); } /** * This function is called by Miranda just after loading all system modules. * * @param none * * @return nothing **/ void SvcFlagsOnModulesLoaded() { SvcFlagsEnableExtraIcons(db_get_b(NULL, MODNAME, SET_CLIST_EXTRAICON_FLAGS2, 0), FALSE); /* Status Icon */ hMsgWndEventHook = HookEvent(ME_MSG_WINDOWEVENT, OnMsgWndEvent); } /** * This function unloads the module. * * @param none * * @return nothing **/ void SvcFlagsUnloadModule() { KillBufferedFunctions(); //Uninit ExtraImg UnhookEvent(hRebuildIconsHook); UnhookEvent(hApplyIconHook); UnhookEvent(hIconsChangedHook); //Uninit message winsow UnhookEvent(hMsgWndEventHook); for(int i = 0; i < gMsgWndList.getCount(); i++) { //this should not happen delete gMsgWndList[i]; gMsgWndList.remove(i); } gMsgWndList.destroy(); gIListMW.destroy(); //Uninit misc UnhookEvent(hSettingChangedHook); UnhookEvent(hOptInitHook); UninitIcons(); //UninitIpToCountry(); /* not implementet */ }