/* * astyle --force-indent=tab=4 --brackets=linux --indent-switches * --pad=oper --one-line=keep-blocks --unpad=paren * * Miranda IM: the free IM client for Microsoft* Windows* * * Copyright 2000-2009 Miranda ICQ/IM project, * all portions of this codebase are copyrighted to the people * listed in contributors.txt. * * 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. * * part of tabSRMM messaging plugin for Miranda. * * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors * * $Id: globals.cpp 13447 2011-03-14 19:55:07Z george.hazan $ * * Plugin configuration variables and functions. Implemented as a class * though there will always be only a single instance. * */ #include "commonheaders.h" extern PLUGININFOEX pluginInfo; CGlobals PluginConfig; CGlobals* pConfig = &PluginConfig; static TContainerSettings _cnt_default = { false, CNT_FLAGS_DEFAULT, CNT_FLAGSEX_DEFAULT, 255, CInfoPanel::DEGRADE_THRESHOLD, 60, _T("%n (%s)"), 1, 0 }; HANDLE CGlobals::m_event_ModulesLoaded = 0, CGlobals::m_event_PrebuildMenu = 0, CGlobals::m_event_SettingChanged = 0; HANDLE CGlobals::m_event_ContactDeleted = 0, CGlobals::m_event_Dispatch = 0, CGlobals::m_event_EventAdded = 0; HANDLE CGlobals::m_event_IconsChanged = 0, CGlobals::m_event_TypingEvent = 0, CGlobals::m_event_ProtoAck = 0; HANDLE CGlobals::m_event_PreShutdown = 0, CGlobals::m_event_OkToExit = 0; HANDLE CGlobals::m_event_IcoLibChanged = 0, CGlobals::m_event_AvatarChanged = 0, CGlobals::m_event_MyAvatarChanged = 0, CGlobals::m_event_FontsChanged = 0; HANDLE CGlobals::m_event_SmileyAdd = 0, CGlobals::m_event_IEView = 0, CGlobals::m_event_FoldersChanged = 0; HANDLE CGlobals::m_event_ME_MC_SUBCONTACTSCHANGED = 0, CGlobals::m_event_ME_MC_FORCESEND = 0, CGlobals::m_event_ME_MC_UNFORCESEND = 0; TCHAR* CGlobals::m_default_container_name = _T("default"); extern HANDLE hHookButtonPressedEvt; extern HANDLE hHookToolBarLoadedEvt; EXCEPTION_RECORD CGlobals::m_exRecord = {0}; CONTEXT CGlobals::m_exCtx = {0}; LRESULT CGlobals::m_exLastResult = 0; char CGlobals::m_exSzFile[MAX_PATH] = "\0"; wchar_t CGlobals::m_exReason[256] = L"\0"; int CGlobals::m_exLine = 0; bool CGlobals::m_exAllowContinue = false; #if defined(_WIN64) static char szCurrentVersion[30]; static char *szVersionUrl = "http://download.miranda.or.at/tabsrmm/3/version.txt"; static char *szUpdateUrl = "http://nightly.miranda.im/x64/tabsrmm.zip"; static char *szFLVersionUrl = "http://nightly.miranda.im/"; static char *szFLUpdateurl = "http://nightly.miranda.im/"; #else static char szCurrentVersion[30]; static char *szVersionUrl = "http://download.miranda.or.at/tabsrmm/3/version.txt"; static char *szUpdateUrl = "http://nightly.miranda.im/x32/tabsrmm.zip"; static char *szFLVersionUrl = "http://nightly.miranda.im/"; static char *szFLUpdateurl = "http://nightly.miranda.im/"; #endif static char *szPrefix = "tabsrmm "; CRTException::CRTException(const char *szMsg, const TCHAR *szParam) : std::runtime_error(std::string(szMsg)) { mir_sntprintf(m_szParam, MAX_PATH, szParam); } void CRTException::display() const { TCHAR* tszMsg = mir_a2t(what()); TCHAR tszBoxMsg[500]; mir_sntprintf(tszBoxMsg, 500, _T("%s\n\n(%s)"), tszMsg, m_szParam); ::MessageBox(0, tszBoxMsg, _T("TabSRMM runtime error"), MB_OK | MB_ICONERROR); mir_free(tszMsg); } /** * reload system values. These are read ONCE and are not allowed to change * without a restart */ void CGlobals::reloadSystemStartup() { HDC hScrnDC; DBVARIANT dbv = {0}; m_WinVerMajor = WinVerMajor(); m_WinVerMinor = WinVerMinor(); m_bIsXP = IsWinVerXPPlus(); m_bIsVista = IsWinVerVistaPlus(); m_bIsWin7 = IsWinVer7Plus(); ::LoadTSButtonModule(); ::RegisterTabCtrlClass(); CTip::registerClass(); dwThreadID = GetCurrentThreadId(); PluginConfig.g_hMenuContext = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_TABCONTEXT)); TranslateMenu(g_hMenuContext); SkinAddNewSoundEx("RecvMsgActive", LPGEN("Instant messages"), LPGEN("Incoming (Focused Window)")); SkinAddNewSoundEx("RecvMsgInactive", LPGEN("Instant messages"), LPGEN("Incoming (Unfocused Window)")); SkinAddNewSoundEx("AlertMsg", LPGEN("Instant messages"), LPGEN("Incoming (New Session)")); SkinAddNewSoundEx("SendMsg", LPGEN("Instant messages"), LPGEN("Outgoing")); SkinAddNewSoundEx("SendError", LPGEN("Instant messages"), LPGEN("Message send error")); hCurSplitNS = LoadCursor(NULL, IDC_SIZENS); hCurSplitWE = LoadCursor(NULL, IDC_SIZEWE); hCurHyperlinkHand = LoadCursor(NULL, IDC_HAND); if (hCurHyperlinkHand == NULL) hCurHyperlinkHand = LoadCursor(g_hInst, MAKEINTRESOURCE(IDC_HYPERLINKHAND)); hScrnDC = GetDC(0); g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0; g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0; ReleaseDC(0, hScrnDC); reloadSettings(false); reloadAdv(); hookSystemEvents(); } /** * this runs ONCE at startup when the Modules Loaded event is fired * by the core. all plugins are loaded and ready to use. * * any initialation for 3rd party plugins must go here. */ void CGlobals::reloadSystemModulesChanged() { BOOL bIEView = FALSE; CLISTMENUITEM mi = { 0 }; m_MathModAvail = ServiceExists(MATH_RTF_REPLACE_FORMULAE); /* * smiley add */ if (ServiceExists(MS_SMILEYADD_REPLACESMILEYS)) { PluginConfig.g_SmileyAddAvail = 1; m_event_SmileyAdd = HookEvent(ME_SMILEYADD_OPTIONSCHANGED, ::SmileyAddOptionsChanged); } else m_event_SmileyAdd = 0; /* * Flashavatars */ g_FlashAvatarAvail = (ServiceExists(MS_FAVATAR_GETINFO) ? 1 : 0); /* * ieView */ bIEView = ServiceExists(MS_IEVIEW_WINDOW); if (bIEView) { BOOL bOldIEView = M->GetByte("ieview_installed", 0); if (bOldIEView != bIEView) M->WriteByte(SRMSGMOD_T, "default_ieview", 1); M->WriteByte(SRMSGMOD_T, "ieview_installed", 1); m_event_IEView = HookEvent(ME_IEVIEW_OPTIONSCHANGED, ::IEViewOptionsChanged); } else { M->WriteByte(SRMSGMOD_T, "ieview_installed", 0); m_event_IEView = 0; } g_iButtonsBarGap = M->GetByte("ButtonsBarGap", 1); m_hwndClist = (HWND)CallService(MS_CLUI_GETHWND, 0, 0); m_MathModAvail = (ServiceExists(MATH_RTF_REPLACE_FORMULAE) ? 1 : 0); if (m_MathModAvail) { char *szDelim = (char *)CallService(MATH_GET_STARTDELIMITER, 0, 0); if (szDelim) { MultiByteToWideChar(CP_ACP, 0, szDelim, -1, PluginConfig.m_MathModStartDelimiter, safe_sizeof(PluginConfig.m_MathModStartDelimiter)); CallService(MTH_FREE_MATH_BUFFER, 0, (LPARAM)szDelim); } } else PluginConfig.m_MathModStartDelimiter[0] = 0; g_MetaContactsAvail = (ServiceExists(MS_MC_GETDEFAULTCONTACT) ? 1 : 0); if (g_MetaContactsAvail) { mir_snprintf(szMetaName, 256, "%s", (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0)); bMetaEnabled = abs(M->GetByte(0, szMetaName, "Enabled", -1)); } else { szMetaName[0] = 0; bMetaEnabled = 0; } g_PopupAvail = (ServiceExists(MS_POPUP_ADDPOPUPEX) ? 1 : 0); g_PopupWAvail = (ServiceExists(MS_POPUP_ADDPOPUPW) ? 1 : 0); mi.cbSize = sizeof(mi); mi.position = -2000090000; if (ServiceExists(MS_SKIN2_GETICONBYHANDLE)) { mi.flags = CMIF_ICONFROMICOLIB | CMIF_DEFAULT; mi.icolibItem = LoadSkinnedIconHandle(SKINICON_EVENT_MESSAGE); } else { mi.flags = CMIF_DEFAULT; mi.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE); } mi.pszName = LPGEN("&Message"); mi.pszService = MS_MSG_SENDMESSAGE; PluginConfig.m_hMenuItem = Menu_AddContactMenuItem(&mi); m_useAeroPeek = M->GetByte("useAeroPeek", 1); } /** * reload plugin settings on startup and runtime. Most of these setttings can be * changed while plugin is running. */ void CGlobals::reloadSettings(bool fReloadSkins) { m_ncm.cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &m_ncm, 0); DWORD dwFlags = M->GetDword("mwflags", MWF_LOG_DEFAULT); m_SendOnShiftEnter = (int)M->GetByte("sendonshiftenter", 0); m_SendOnEnter = (int)M->GetByte(SRMSGSET_SENDONENTER, SRMSGDEFSET_SENDONENTER); m_SendOnDblEnter = (int)M->GetByte("SendOnDblEnter", 0); m_AutoLocaleSupport = (int)M->GetByte("al", 0); m_AutoSwitchTabs = (int)M->GetByte("autoswitchtabs", 1); m_CutContactNameTo = (int) DBGetContactSettingWord(NULL, SRMSGMOD_T, "cut_at", 15); m_CutContactNameOnTabs = (int)M->GetByte("cuttitle", 0); m_StatusOnTabs = (int)M->GetByte("tabstatus", 1); m_LogStatusChanges = (int)dwFlags & MWF_LOG_STATUSCHANGES; m_UseDividers = (int)M->GetByte("usedividers", 0); m_DividersUsePopupConfig = (int)M->GetByte("div_popupconfig", 0); m_MsgTimeout = (int)M->GetDword(SRMSGMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT); if (m_MsgTimeout < SRMSGSET_MSGTIMEOUT_MIN) m_MsgTimeout = SRMSGSET_MSGTIMEOUT_MIN; m_EscapeCloses = (int)M->GetByte("escmode", 0); m_HideOnClose = (int) M->GetByte("hideonclose", 0); m_AllowTab = (int) M->GetByte("tabmode", 0); m_FlashOnClist = (int)M->GetByte("flashcl", 0); m_AlwaysFullToolbarWidth = (int)M->GetByte("alwaysfulltoolbar", 1); m_LimitStaticAvatarHeight = (int)M->GetDword("avatarheight", 96); m_SendFormat = (int)M->GetByte("sendformat", 0); m_FormatWholeWordsOnly = 1; m_RTLDefault = (int)M->GetByte("rtldefault", 0); m_TabAppearance = (int)M->GetDword("tabconfig", TCF_FLASHICON | TCF_SINGLEROWTABCONTROL); m_panelHeight = (DWORD)M->GetDword("panelheight", CInfoPanel::DEGRADE_THRESHOLD); m_MUCpanelHeight = M->GetDword("Chat", "panelheight", CInfoPanel::DEGRADE_THRESHOLD); m_IdleDetect = (int)M->GetByte("dimIconsForIdleContacts", 1); m_smcxicon = 16; m_smcyicon = 16; m_PasteAndSend = (int)M->GetByte("pasteandsend", 1); m_szNoStatus = TranslateT("No status message"); m_LangPackCP = ServiceExists(MS_LANGPACK_GETCODEPAGE) ? CallService(MS_LANGPACK_GETCODEPAGE, 0, 0) : CP_ACP; m_visualMessageSizeIndicator = M->GetByte("msgsizebar", 0); m_autoSplit = M->GetByte("autosplit", 0); m_FlashOnMTN = M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINFLASH, SRMSGDEFSET_SHOWTYPINGWINFLASH); if (m_MenuBar == 0) { m_MenuBar = ::LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENUBAR)); TranslateMenu(m_MenuBar); } m_ipBackgroundGradient = M->GetDword(FONTMODULE, "ipfieldsbg", 0x62caff); if (0 == m_ipBackgroundGradient) m_ipBackgroundGradient = 0x62caff; m_ipBackgroundGradientHigh = M->GetDword(FONTMODULE, "ipfieldsbgHigh", 0xf0f0f0); if (0 == m_ipBackgroundGradientHigh) m_ipBackgroundGradientHigh = 0xf0f0f0; m_tbBackgroundHigh = M->GetDword(FONTMODULE, "tbBgHigh", 0); m_tbBackgroundLow = M->GetDword(FONTMODULE, "tbBgLow", 0); m_fillColor = M->GetDword(FONTMODULE, "fillColor", 0); if (CSkin::m_BrushFill) { ::DeleteObject(CSkin::m_BrushFill); CSkin::m_BrushFill = 0; } m_genericTxtColor = M->GetDword(FONTMODULE, "genericTxtClr", GetSysColor(COLOR_BTNTEXT)); m_cRichBorders = M->GetDword(FONTMODULE, "cRichBorders", 0); ::CopyMemory(&globalContainerSettings, &_cnt_default, sizeof(TContainerSettings)); Utils::ReadContainerSettingsFromDB(0, &globalContainerSettings); globalContainerSettings.fPrivate = false; if (fReloadSkins) Skin->setupAeroSkins(); } /** * reload "advanced tweaks" that can be applied w/o a restart */ void CGlobals::reloadAdv() { g_bDisableAniAvatars = M->GetByte("adv_DisableAniAvatars", 0); g_bSoundOnTyping = M->GetByte("adv_soundontyping", 0); m_dontUseDefaultKbd = M->GetByte("adv_leaveKeyboardAlone", 1); g_bClientInStatusBar = M->GetByte("adv_ClientIconInStatusBar", 0); if (g_bSoundOnTyping && m_TypingSoundAdded == false) { SkinAddNewSoundEx("SoundOnTyping", LPGEN("Other"), LPGEN("TABSRMM: Typing")); m_TypingSoundAdded = true; } m_AllowOfflineMultisend = M->GetByte("AllowOfflineMultisend", 0); } const HMENU CGlobals::getMenuBar() { if (m_MenuBar == 0) { m_MenuBar = ::LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENUBAR)); TranslateMenu(m_MenuBar); } return(m_MenuBar); } /** * hook core events. This runs in LoadModule() * only core events and services are guaranteed to exist at this time */ void CGlobals::hookSystemEvents() { m_event_ModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); m_event_IconsChanged = HookEvent(ME_SKIN_ICONSCHANGED, ::IconsChanged); m_event_TypingEvent = HookEvent(ME_PROTO_CONTACTISTYPING, CMimAPI::TypingMessage); m_event_ProtoAck = HookEvent(ME_PROTO_ACK, CMimAPI::ProtoAck); m_event_PreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreshutdownSendRecv); m_event_OkToExit = HookEvent(ME_SYSTEM_OKTOEXIT, OkToExit); m_event_PrebuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, CMimAPI::PrebuildContactMenu); m_event_IcoLibChanged = HookEvent(ME_SKIN2_ICONSCHANGED, ::IcoLibIconsChanged); m_event_AvatarChanged = HookEvent(ME_AV_AVATARCHANGED, ::AvatarChanged); m_event_MyAvatarChanged = HookEvent(ME_AV_MYAVATARCHANGED, ::MyAvatarChanged); } /** * second part of the startup initialisation. All plugins are now fully loaded */ int CGlobals::ModulesLoaded(WPARAM wParam, LPARAM lParam) { int i; MENUITEMINFOA mii = {0}; HMENU submenu; CLISTMENUITEM mi = { 0 }; ::UnhookEvent(m_event_ModulesLoaded); M->configureCustomFolders(); Skin->Init(true); CSkin::initAeroEffect(); for (i = 0; i < NR_BUTTONBARICONS; i++) PluginConfig.g_buttonBarIcons[i] = 0; ::LoadIconTheme(); ::CreateImageList(TRUE); mii.cbSize = sizeof(mii); mii.fMask = MIIM_BITMAP; mii.hbmpItem = HBMMENU_CALLBACK; submenu = GetSubMenu(PluginConfig.g_hMenuContext, 7); for (i = 0; i <= 8; i++) SetMenuItemInfoA(submenu, (UINT_PTR)i, TRUE, &mii); PluginConfig.reloadSystemModulesChanged(); ::BuildContainerMenu(); ::CB_InitDefaultButtons(); ::ModPlus_Init(wParam, lParam); ::NotifyEventHooks(hHookToolBarLoadedEvt, (WPARAM)0, (LPARAM)0); // if (M->GetByte("avatarmode", -1) == -1) M->WriteByte(SRMSGMOD_T, "avatarmode", 2); PluginConfig.g_hwndHotkeyHandler = CreateWindowEx(0, _T("TSHK"), _T(""), WS_POPUP, 0, 0, 40, 40, 0, 0, g_hInst, NULL); ::CreateTrayMenus(TRUE); if (nen_options.bTraySupport) ::CreateSystrayIcon(TRUE); mi.cbSize = sizeof(mi); mi.position = -500050005; mi.hIcon = PluginConfig.g_iconContainer; mi.pszContactOwner = NULL; mi.pszName = LPGEN("&Messaging settings..."); mi.pszService = MS_TABMSG_SETUSERPREFS; PluginConfig.m_UserMenuItem = Menu_AddContactMenuItem(&mi); if (sendLater->isAvail()) { mi.cbSize = sizeof(mi); mi.position = -500050006; mi.hIcon = 0; mi.pszContactOwner = NULL; mi.pszName = LPGEN("&Send later job list..."); mi.pszService = MS_TABMSG_SLQMGR; PluginConfig.m_UserMenuItem = Menu_AddMainMenuItem(&mi); } RestoreUnreadMessageAlerts(); ::RegisterFontServiceFonts(); ::CacheLogFonts(); ::Chat_ModulesLoaded(wParam, lParam); if (PluginConfig.g_PopupWAvail||PluginConfig.g_PopupAvail) TN_ModuleInit(); m_event_SettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DBSettingChanged); m_event_ContactDeleted = HookEvent(ME_DB_CONTACT_DELETED, DBContactDeleted); m_event_Dispatch = HookEvent(ME_DB_EVENT_ADDED, CMimAPI::DispatchNewEvent); m_event_EventAdded = HookEvent(ME_DB_EVENT_ADDED, CMimAPI::MessageEventAdded); if (PluginConfig.g_MetaContactsAvail) { m_event_ME_MC_SUBCONTACTSCHANGED = HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaContactEvent); m_event_ME_MC_FORCESEND = HookEvent(ME_MC_FORCESEND, MetaContactEvent); m_event_ME_MC_UNFORCESEND = HookEvent(ME_MC_UNFORCESEND, MetaContactEvent); } m_event_FontsChanged = HookEvent(ME_FONT_RELOAD, ::FontServiceFontsChanged); return 0; } /** * watches various important database settings and reacts accordingly * needed to catch status, nickname and other changes in order to update open message * sessions. */ int CGlobals::DBSettingChanged(WPARAM wParam, LPARAM lParam) { DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam; const char *szProto = NULL; const char *setting = cws->szSetting; HWND hwnd = 0; CContactCache* c = 0; bool fChanged = false, fNickChanged = false, fExtendedStatusChange = false; hwnd = M->FindWindow((HANDLE)wParam); if (hwnd == 0 && wParam != 0) { // we are not interested in this event if there is no open message window/tab if (!strcmp(setting, "Status") || !strcmp(setting, "MyHandle") || !strcmp(setting, "Nick") || !strcmp(cws->szModule, SRMSGMOD_T)) { c = CContactCache::getContactCache((HANDLE)wParam); if (c) { fChanged = c->updateStatus(); if (strcmp(setting, "Status")) c->updateNick(); if (!strcmp(setting, "isFavorite") || !strcmp(setting, "isRecent")) c->updateFavorite(); } } return 0; } if (wParam == 0 && !strcmp("Nick", setting)) { M->BroadcastMessage(DM_OWNNICKCHANGED, 0, (LPARAM)cws->szModule); return 0; } if (wParam) { c = CContactCache::getContactCache((HANDLE)wParam); if (c) { szProto = c->getProto(); if (!strcmp(cws->szModule, SRMSGMOD_T)) { // catch own relevant settings if (!strcmp(setting, "isFavorite") || !strcmp(setting, "isRecent")) c->updateFavorite(); } } } if (wParam == 0 && !lstrcmpA(setting, "Enabled")) { if (PluginConfig.g_MetaContactsAvail && !lstrcmpA(cws->szModule, PluginConfig.szMetaName)) { // catch the disabled meta contacts PluginConfig.bMetaEnabled = abs(M->GetByte(0, PluginConfig.szMetaName, "Enabled", -1)); cacheUpdateMetaChanged(); } } if (lstrcmpA(cws->szModule, "CList") && (szProto == NULL || lstrcmpA(cws->szModule, szProto))) return 0; if (PluginConfig.g_MetaContactsAvail && !lstrcmpA(cws->szModule, PluginConfig.szMetaName)) { if (wParam != 0 && !lstrcmpA(setting, "Nick")) // filter out this setting to avoid infinite loops while trying to obtain the most online contact return 0; } if (hwnd) { if (c) { fChanged = c->updateStatus(); fNickChanged = c->updateNick(); } if (lstrlenA(setting) > 6 && lstrlenA(setting) < 9 && !strncmp(setting, "Status", 6)) { fChanged = true; if (c) { c->updateMeta(true); c->updateUIN(); } } else if (!strcmp(setting, "MirVer")) PostMessage(hwnd, DM_CLIENTCHANGED, 0, 0); else if (!strcmp(setting, "display_uid")) { if (c) c->updateUIN(); PostMessage(hwnd, DM_UPDATEUIN, 0, 0); } else if (lstrlenA(setting) > 6 && strstr("StatusMsg,XStatusMsg,XStatusName,XStatusId,ListeningTo", setting)) { if (c) { c->updateStatusMsg(setting); fExtendedStatusChange = true; } } if (fChanged || fNickChanged || fExtendedStatusChange) PostMessage(hwnd, DM_UPDATETITLE, 0, 1); if (fExtendedStatusChange) PostMessage(hwnd, DM_UPDATESTATUSMSG, 0, 0); if (fChanged) { if (c && c->getStatus() == ID_STATUS_OFFLINE) { // clear typing notification in the status bar when contact goes offline TWindowData* dat = c->getDat(); if (dat) { dat->nTypeSecs = 0; dat->showTyping = 0; dat->szStatusBar[0] = 0; PostMessage(c->getHwnd(), DM_UPDATELASTMESSAGE, 0, 0); } } if (c) PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_LOGSTATUSCHANGE, MAKELONG(c->getStatus(), c->getOldStatus()), (LPARAM)c); } } return 0; } /** * event fired when a contact has been deleted. Make sure to close its message session */ int CGlobals::DBContactDeleted(WPARAM wParam, LPARAM lParam) { if (wParam) { CContactCache *c = CContactCache::getContactCache((HANDLE)wParam); if (c) c->deletedHandler(); } return 0; } /** * Handle events from metacontacts protocol. Basically, just update * our contact cache and, if a message window exists, tell it to update * relevant information. */ int CGlobals::MetaContactEvent(WPARAM wParam, LPARAM lParam) { if (wParam) { CContactCache *c = CContactCache::getContactCache((HANDLE)wParam); if (c) { c->updateMeta(true); if (c->getHwnd()) { c->updateUIN(); // only do this for open windows, not needed normally ::PostMessage(c->getHwnd(), DM_UPDATETITLE, 0, 0); } } } return 0; } int CGlobals::PreshutdownSendRecv(WPARAM wParam, LPARAM lParam) { HANDLE hContact; int i; #if defined(__USE_EX_HANDLERS) __try { #endif if (PluginConfig.m_chat_enabled) ::Chat_PreShutdown(); ::TN_ModuleDeInit(); while(pFirstContainer){ if (PluginConfig.m_HideOnClose) PluginConfig.m_HideOnClose = FALSE; ::SendMessage(pFirstContainer->hwnd, WM_CLOSE, 0, 1); } hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); while (hContact) { M->WriteDword(hContact, SRMSGMOD_T, "messagecount", 0); hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); } for (i = 0; i < SERVICE_LAST; i++) { if (PluginConfig.hSvc[i]) DestroyServiceFunction(PluginConfig.hSvc[i]); } ::SI_DeinitStatusIcons(); ::CB_DeInitCustomButtons(); /* * the event API */ DestroyHookableEvent(PluginConfig.m_event_MsgWin); DestroyHookableEvent(PluginConfig.m_event_MsgPopup); ::NEN_WriteOptions(&nen_options); ::DestroyWindow(PluginConfig.g_hwndHotkeyHandler); ::UnregisterClass(_T("TSStatusBarClass"), g_hInst); ::UnregisterClass(_T("SideBarClass"), g_hInst); ::UnregisterClassA("TSTabCtrlClass", g_hInst); ::UnregisterClass(_T("RichEditTipClass"), g_hInst); ::UnregisterClass(_T("TSHK"), g_hInst); #if defined(__USE_EX_HANDLERS) } __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"SHUTDOWN_STAGE2", false)) { return 0; } #endif return 0; } int CGlobals::OkToExit(WPARAM wParam, LPARAM lParam) { UnhookEvent(m_event_OkToExit); #if defined(__USE_EX_HANDLERS) __try { #endif ::CreateSystrayIcon(0); ::CreateTrayMenus(0); CWarning::destroyAll(); CMimAPI::m_shutDown = true; UnhookEvent(m_event_EventAdded); UnhookEvent(m_event_Dispatch); UnhookEvent(m_event_PrebuildMenu); UnhookEvent(m_event_SettingChanged); UnhookEvent(m_event_ContactDeleted); UnhookEvent(m_event_AvatarChanged); UnhookEvent(m_event_MyAvatarChanged); UnhookEvent(m_event_ProtoAck); UnhookEvent(m_event_TypingEvent); UnhookEvent(m_event_FontsChanged); UnhookEvent(m_event_IcoLibChanged); UnhookEvent(m_event_IconsChanged); if (m_event_SmileyAdd) UnhookEvent(m_event_SmileyAdd); if (m_event_IEView) UnhookEvent(m_event_IEView); if (m_event_FoldersChanged) UnhookEvent(m_event_FoldersChanged); if (m_event_ME_MC_FORCESEND) { UnhookEvent(m_event_ME_MC_FORCESEND); UnhookEvent(m_event_ME_MC_SUBCONTACTSCHANGED); UnhookEvent(m_event_ME_MC_UNFORCESEND); } ::ModPlus_PreShutdown(wParam, lParam); PluginConfig.globalContainerSettings.fPrivate = false; ::DBWriteContactSettingBlob(0, SRMSGMOD_T, CNT_KEYNAME, &PluginConfig.globalContainerSettings, sizeof(TContainerSettings)); #if defined(__USE_EX_HANDLERS) } __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"SHUTDOWN_STAGE1", false)) { return 0; } #endif return 0; } /** * used on startup to restore flashing tray icon if one or more messages are * still "unread" */ void CGlobals::RestoreUnreadMessageAlerts(void) { CLISTEVENT cle = { 0 }; DBEVENTINFO dbei = { 0 }; TCHAR toolTip[256]; int windowAlreadyExists; int usingReadNext = 0; int autoPopup = M->GetByte(SRMSGMOD, SRMSGSET_AUTOPOPUP, SRMSGDEFSET_AUTOPOPUP); HANDLE hDbEvent, hContact; dbei.cbSize = sizeof(dbei); cle.cbSize = sizeof(cle); cle.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE); cle.pszService = "SRMsg/ReadMessage"; cle.flags = CLEF_TCHAR; hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); while (hContact) { if (M->GetDword(hContact, "SendLater", "count", 0)) sendLater->addContact(hContact); hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDFIRSTUNREAD, (WPARAM) hContact, 0); while (hDbEvent) { dbei.cbBlob = 0; CallService(MS_DB_EVENT_GET, (WPARAM) hDbEvent, (LPARAM) & dbei); if (!(dbei.flags & (DBEF_SENT | DBEF_READ)) && dbei.eventType == EVENTTYPE_MESSAGE) { windowAlreadyExists = M->FindWindow(hContact) != NULL; if (!usingReadNext && windowAlreadyExists) continue; cle.hContact = hContact; cle.hDbEvent = hDbEvent; mir_sntprintf(toolTip, safe_sizeof(toolTip), TranslateT("Message from %s"), (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR)); cle.ptszTooltip = toolTip; CallService(MS_CLIST_ADDEVENT, 0, (LPARAM) & cle); } hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) hDbEvent, 0); } hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0); } } void CGlobals::logStatusChange(WPARAM wParam, const CContactCache *c) { if (c == 0) return; HANDLE hContact = c->getContact(); bool fGlobal = PluginConfig.m_LogStatusChanges ? true : false; DWORD dwMask = M->GetDword(hContact, SRMSGMOD_T, "mwmask", 0); DWORD dwFlags = M->GetDword(hContact, SRMSGMOD_T, "mwflags", 0); bool fLocal = ((dwMask & MWF_LOG_STATUSCHANGES) ? (dwFlags & MWF_LOG_STATUSCHANGES ? true : false) : false); if (fGlobal || fLocal) { /* * don't log them if WE are logging off */ if (CallProtoService(c->getProto(), PS_GETSTATUS, 0, 0) == ID_STATUS_OFFLINE) return; WORD wStatus, wOldStatus; wStatus = LOWORD(wParam); wOldStatus = HIWORD(wParam); if (wStatus == wOldStatus) return; DBEVENTINFO dbei; TCHAR buffer[450]; HANDLE hNewEvent; TCHAR* szOldStatus = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)wOldStatus, GSMDF_TCHAR); TCHAR* szNewStatus = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)wStatus, GSMDF_TCHAR); if (szOldStatus == 0 || szNewStatus == 0) return; if (c->isValid()) { if (wStatus == ID_STATUS_OFFLINE) mir_sntprintf(buffer, safe_sizeof(buffer), TranslateT("signed off.")); else if (wOldStatus == ID_STATUS_OFFLINE) mir_sntprintf(buffer, safe_sizeof(buffer), TranslateT("signed on and is now %s."), szNewStatus); else mir_sntprintf(buffer, safe_sizeof(buffer), TranslateT("changed status from %s to %s."), szOldStatus, szNewStatus); } char *szMsg = mir_utf8encodeT(buffer); dbei.pBlob = (PBYTE)szMsg; dbei.cbBlob = lstrlenA(szMsg) + 1; dbei.flags = DBEF_UTF | DBEF_READ; dbei.cbSize = sizeof(dbei); dbei.eventType = EVENTTYPE_STATUSCHANGE; dbei.timestamp = time(NULL); dbei.szModule = const_cast(c->getProto()); hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) hContact, (LPARAM) & dbei); mir_free(szMsg); } } /** * when the state of the meta contacts protocol changes from enabled to disabled * (or vice versa), this updates the contact cache * * it is ONLY called from the DBSettingChanged() event handler when the relevant * database value is touched. */ void CGlobals::cacheUpdateMetaChanged() { CContactCache* c = CContactCache::m_cCache; bool fMetaActive = (PluginConfig.g_MetaContactsAvail && PluginConfig.bMetaEnabled) ? true : false; while(c) { if (c->isMeta() && PluginConfig.bMetaEnabled == false) { c->closeWindow(); c->resetMeta(); } // meta contacts are enabled, but current contact is a subcontact - > close window if (fMetaActive && c->isSubContact()) c->closeWindow(); // reset meta contact information, if metacontacts protocol became avail if (fMetaActive && !strcmp(c->getProto(), PluginConfig.szMetaName)) c->resetMeta(); c = c->m_next; } } /** * on Windows 7, when using new task bar features (grouping mode and per tab * previews), autoswitching does not work relieably, so it is disabled. * * @return: true if configuration dictates autoswitch */ bool CGlobals::haveAutoSwitch() { if (m_bIsWin7) { if (m_useAeroPeek && !CSkin::m_skinEnabled) return(false); } return(m_AutoSwitchTabs ? true : false); } /** * exception handling - copy error message to clip board * @param hWnd: window handle of the edit control containing the error message */ void CGlobals::Ex_CopyEditToClipboard(HWND hWnd) { SendMessage(hWnd, EM_SETSEL, 0, 65535L); SendMessage(hWnd, WM_COPY, 0 , 0); SendMessage(hWnd, EM_SETSEL, 0, 0); } INT_PTR CALLBACK CGlobals::Ex_DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { WORD wNotifyCode, wID; switch(uMsg) { case WM_INITDIALOG: { char szBuffer[2048]; #ifdef _WIN64 sprintf(szBuffer, "Exception %16.16X at address %16.16X occured in %s at line %d.\r\n\r\nEAX=%16.16X EBX=%16.16X ECX=%16.16X\r\nEDX=%16.16X ESI=%16.16X EDI=%16.16X\r\nEBP=%16.16X ESP=%16.16X EIP=%16.16X", m_exRecord.ExceptionCode, m_exRecord.ExceptionAddress, m_exSzFile, m_exLine, m_exCtx.Rax,m_exCtx.Rbx, m_exCtx.Rcx, m_exCtx.Rdx, m_exCtx.Rsi, m_exCtx.Rdi, m_exCtx.Rbp, m_exCtx.Rsp, m_exCtx.Rip); #else sprintf(szBuffer, "Exception %8.8X at address %8.8X occured in %s at line %d.\r\n\r\nEAX=%8.8X EBX=%8.8X ECX=%8.8X\r\nEDX=%8.8X ESI=%8.8X EDI=%8.8X\r\nEBP=%8.8X ESP=%8.8X EIP=%8.8X", m_exRecord.ExceptionCode, m_exRecord.ExceptionAddress, m_exSzFile, m_exLine, m_exCtx.Eax,m_exCtx.Ebx, m_exCtx.Ecx, m_exCtx.Edx, m_exCtx.Esi, m_exCtx.Edi, m_exCtx.Ebp, m_exCtx.Esp, m_exCtx.Eip); #endif SetDlgItemTextA(hwndDlg, IDC_EXCEPTION_DETAILS, szBuffer); SetFocus(GetDlgItem(hwndDlg, IDC_EXCEPTION_DETAILS)); SendDlgItemMessage(hwndDlg, IDC_EXCEPTION_DETAILS, WM_SETFONT, (WPARAM)GetStockObject(OEM_FIXED_FONT), 0); SetDlgItemTextW(hwndDlg, IDC_EX_REASON, m_exReason); Utils::enableDlgControl(hwndDlg, IDOK, m_exAllowContinue ? TRUE : FALSE); } break; case WM_COMMAND: wNotifyCode = HIWORD(wParam); wID = LOWORD(wParam); if (wNotifyCode == BN_CLICKED) { if (wID == IDOK || wID == IDCANCEL) EndDialog(hwndDlg, wID); if (wID == IDC_COPY_EXCEPTION) Ex_CopyEditToClipboard(GetDlgItem(hwndDlg, IDC_EXCEPTION_DETAILS)); } break; } return FALSE; } void CGlobals::Ex_Handler() { if (m_exLastResult == IDCANCEL) ExitProcess(1); } int CGlobals::Ex_ShowDialog(EXCEPTION_POINTERS *ep, const char *szFile, int line, wchar_t* szReason, bool fAllowContinue) { char szDrive[MAX_PATH], szDir[MAX_PATH], szName[MAX_PATH], szExt[MAX_PATH]; _splitpath(szFile, szDrive, szDir, szName, szExt); memcpy(&m_exRecord, ep->ExceptionRecord, sizeof(EXCEPTION_RECORD)); memcpy(&m_exCtx, ep->ContextRecord, sizeof(CONTEXT)); _snprintf(m_exSzFile, MAX_PATH, "%s%s", szName, szExt); mir_sntprintf(m_exReason, 256, L"An application error has occured: %s", szReason); m_exLine = line; m_exLastResult = DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_EXCEPTION), 0, CGlobals::Ex_DlgProc, 0); m_exAllowContinue = fAllowContinue; if (IDCANCEL == m_exLastResult) ExitProcess(1); return 1; }