///////////////////////////////////////////////////////////////////////////////////////// // Miranda NG: the free IM client for Microsoft* Windows* // // Copyright (ñ) 2012-17 Miranda NG project, // Copyright (c) 2000-09 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 // // Load, setup and shutdown the plugin // core plugin messaging services (single IM chats only). #include "stdafx.h" #define IDI_CORE_LOAD 132 // icon id for the "connecting" icon NEN_OPTIONS nen_options; static HANDLE hUserPrefsWindowLis = 0; HMODULE g_hIconDLL = 0, g_hMsftedit; static void UnloadIcons(); void Chat_AddIcons(); ///////////////////////////////////////////////////////////////////////////////////////// // fired event when user changes IEView plugin options. Apply them to all open tabs int IEViewOptionsChanged(WPARAM, LPARAM) { Srmm_Broadcast(DM_IEVIEWOPTIONSCHANGED, 0, 0); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // fired event when user changes smileyadd options. Notify all open tabs about the changes int SmileyAddOptionsChanged(WPARAM, LPARAM) { Srmm_Broadcast(DM_SMILEYOPTIONSCHANGED, 0, 0); pci->SM_BroadcastMessage(nullptr, DM_SMILEYOPTIONSCHANGED, 0, 0, FALSE); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // basic window class CTabBaseDlg::CTabBaseDlg(int iResource, SESSION_INFO *si) : CSrmmBaseDialog(g_hInst, iResource, si), m_pPanel(this), m_dwFlags(MWF_INITMODE), m_iInputAreaHeight(-1) { m_autoClose = CLOSE_ON_CANCEL; m_forceResizable = true; } CTabBaseDlg::~CTabBaseDlg() { delete m_pWnd; mir_free(m_sendBuffer); mir_free(m_hHistoryEvents); mir_free(m_hQueuedEvents); if (m_hClientIcon) DestroyIcon(m_hClientIcon); if (m_hSmileyIcon) DestroyIcon(m_hSmileyIcon); if (m_hXStatusIcon) DestroyIcon(m_hXStatusIcon); if (m_hTaskbarIcon) DestroyIcon(m_hTaskbarIcon); } void CTabBaseDlg::LoadSettings() { m_clrInputBG = m_pContainer->theme.inputbg; LoadLogfont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG, FONTMODULE); } void CTabBaseDlg::OnInitDialog() { CSrmmBaseDialog::OnInitDialog(); // m_hwnd is valid, pass it to the tab control TCITEM tci; tci.mask = TCIF_PARAM; tci.lParam = (LPARAM)m_hwnd; TabCtrl_SetItem(m_hwndParent, m_iTabID, &tci); // update another tab ids m_pContainer->UpdateTabs(); // add this window to window list & proxy if (PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek) m_pWnd = new CProxyWindow(this); else m_pWnd = nullptr; // set up Windows themes DM_ThemeChanged(); // refresh cache data for this contact m_cache = CContactCache::getContactCache(m_hContact); m_cache->updateNick(); m_cache->updateUIN(); m_bIsAutosizingInput = IsAutoSplitEnabled(); } INT_PTR CTabBaseDlg::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case DM_SETINFOPANEL: // broadcasted when global info panel setting changes if (wParam == 0 && lParam == 0) { m_pPanel.getVisibility(); m_pPanel.loadHeight(); m_pPanel.showHide(); } else { CTabBaseDlg *srcDat = (CTabBaseDlg*)wParam; if (lParam == 0) m_pPanel.loadHeight(); else { if (srcDat && lParam && this != srcDat && !m_pPanel.isPrivateHeight()) { if (srcDat->isChat() != isChat() && M.GetByte("syncAllPanels", 0) == 0) return 0; if (m_pContainer->settings->fPrivate && srcDat->m_pContainer != m_pContainer) return 0; m_pPanel.setHeight((LONG)lParam); } } Resize(); } return 0; case DM_ACTIVATEME: // the child window will activate itself ActivateExistingTab(m_pContainer, m_hwnd); return 0; case DM_SETLOCALE: if (m_dwFlags & MWF_WASBACKGROUNDCREATE) break; if (m_pContainer->m_hwndActive == m_hwnd && PluginConfig.m_bAutoLocaleSupport && m_pContainer->m_hwnd == GetForegroundWindow() && m_pContainer->m_hwnd == GetActiveWindow()) { if (lParam) m_hkl = (HKL)lParam; if (m_hkl) ActivateKeyboardLayout(m_hkl, 0); } return 0; case DM_QUERYCONTAINER: // container API support functions if (lParam) *(TContainerData**)lParam = m_pContainer; return 0; case DM_QUERYHCONTACT: if (lParam) *(MCONTACT*)lParam = m_hContact; return 0; case DM_CHECKSIZE: m_dwFlags |= MWF_NEEDCHECKSIZE; return 0; case DM_CONTAINERSELECTED: // sent by the select container dialog box when a container was selected... // lParam = (wchar_t*)selected name... { wchar_t *szNewName = (wchar_t*)lParam; if (!mir_wstrcmp(szNewName, TranslateT("Default container"))) szNewName = CGlobals::m_default_container_name; int iOldItems = TabCtrl_GetItemCount(m_hwndParent); if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN)) break; TContainerData *pNewContainer = FindContainerByName(szNewName); if (pNewContainer == nullptr) if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr) break; db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName); PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact); if (iOldItems > 1) // there were more than 1 tab, container is still valid SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0); SetForegroundWindow(pNewContainer->m_hwnd); SetActiveWindow(pNewContainer->m_hwnd); } return 0; case DM_ACTIVATETOOLTIP: // show the balloon tooltip control. // wParam == id of the "anchor" element, defaults to the panel status field (for away msg retrieval) // lParam == new text to show if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd) m_pPanel.showTip(wParam, lParam); return 0; case DM_STATUSBARCHANGED: tabUpdateStatusBar(); break; case DM_CHECKAUTOHIDE: // This is broadcasted by the container to all child windows to check if the // container can be autohidden or -closed. // // wParam is the autohide timeout (in seconds) // lParam points to a BOOL and a session which wants to prevent auto-hiding // the container must set it to FALSE. // // If no session in the container disagrees, the container will be hidden. if (lParam) { BOOL *fResult = (BOOL*)lParam; // text entered in the input area -> prevent autohide/cose if (GetWindowTextLength(m_message.GetHwnd()) > 0) *fResult = FALSE; // unread events, do not hide or close the container else if (m_dwUnread) *fResult = FALSE; // time since last activity did not yet reach the threshold. else if (((GetTickCount() - m_dwLastActivity) / 1000) <= wParam) *fResult = FALSE; } return 0; case DM_SPLITTERGLOBALEVENT: DM_SplitterGlobalEvent(wParam, lParam); return 0; } return CSrmmBaseDialog::DlgProc(msg, wParam, lParam); } void CTabBaseDlg::NotifyDeliveryFailure() const { if (M.GetByte("adv_noErrorPopups", 0)) return; if (CallService(MS_POPUP_QUERY, PUQS_GETSTATUS, 0) != 1) return; POPUPDATAT ppd = { 0 }; ppd.lchContact = m_hContact; wcsncpy_s(ppd.lptzContactName, m_cache->getNick(), _TRUNCATE); wcsncpy_s(ppd.lptzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE); if (!(BOOL)M.GetByte(MODULE, OPT_COLDEFAULT_ERR, TRUE)) { ppd.colorText = (COLORREF)M.GetDword(MODULE, OPT_COLTEXT_ERR, DEFAULT_COLTEXT); ppd.colorBack = (COLORREF)M.GetDword(MODULE, OPT_COLBACK_ERR, DEFAULT_COLBACK); } else ppd.colorText = ppd.colorBack = 0; ppd.PluginWindowProc = Utils::PopupDlgProcError; ppd.lchIcon = PluginConfig.g_iconErr; ppd.PluginData = 0; ppd.iSeconds = (int)M.GetDword(MODULE, OPT_DELAY_ERR, (DWORD)DEFAULT_DELAY); PUAddPopupT(&ppd); } ///////////////////////////////////////////////////////////////////////////////////////// // Sets a status bar text for a contact void CTabBaseDlg::SetStatusText(const wchar_t *wszText, HICON hIcon) { if (wszText != nullptr) { m_bStatusSet = true; m_szStatusText = wszText; m_szStatusIcon = hIcon; } else { m_bStatusSet = false; m_szStatusText.Empty(); m_szStatusIcon = nullptr; } tabUpdateStatusBar(); } ///////////////////////////////////////////////////////////////////////////////////////// // service function. Invoke the user preferences dialog for the contact given (by handle) in wParam static INT_PTR SetUserPrefs(WPARAM wParam, LPARAM) { HWND hWnd = WindowList_Find(PluginConfig.hUserPrefsWindowList, wParam); if (hWnd) { SetForegroundWindow(hWnd); // already open, bring it to front return 0; } CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_USERPREFS_FRAME), 0, DlgProcUserPrefsFrame, (LPARAM)wParam); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // service function - open the tray menu from the TTB button static INT_PTR Service_OpenTrayMenu(WPARAM, LPARAM lParam) { SendMessage(PluginConfig.g_hwndHotkeyHandler, DM_TRAYICONNOTIFY, 101, lParam == 0 ? WM_LBUTTONUP : WM_RBUTTONUP); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // service function finds a message session // wParam = contact handle for which we want the window handle // thanks to bio for the suggestion of this service // if wParam == 0, then lParam is considered to be a valid window handle and // the function tests the popup mode of the target container // // returns the hwnd if there is an open window or tab for the given hcontact (wParam), // or (if lParam was specified) the hwnd if the window exists. // 0 if there is none (or the popup mode of the target container was configured to "hide" // the window.. int TSAPI MessageWindowOpened(MCONTACT hContact, HWND _hwnd) { HWND hwnd = 0; TContainerData *pContainer = nullptr; if (hContact) hwnd = Srmm_FindWindow(hContact); else if (_hwnd) hwnd = _hwnd; else return 0; if (!hwnd) return 0; SendMessage(hwnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer); if (pContainer) { if (pContainer->dwFlags & CNT_DONTREPORT) { if (IsIconic(pContainer->m_hwnd)) return 0; } if (pContainer->dwFlags & CNT_DONTREPORTUNFOCUSED) { if (!IsIconic(pContainer->m_hwnd) && GetForegroundWindow() != pContainer->m_hwnd && GetActiveWindow() != pContainer->m_hwnd) return 0; } if (pContainer->dwFlags & CNT_ALWAYSREPORTINACTIVE) { if (pContainer->dwFlags & CNT_DONTREPORTFOCUSED) return 0; return pContainer->m_hwndActive == hwnd; } } return 1; } ///////////////////////////////////////////////////////////////////////////////////////// // ReadMessageCommand is executed whenever the user wants to manually open a window. // This can happen when double clicking a contact on the clist OR when opening a new // message (clicking on a popup, clicking the flashing tray icon and so on). static INT_PTR ReadMessageCommand(WPARAM, LPARAM lParam) { MCONTACT hContact = ((CLISTEVENT *)lParam)->hContact; HWND hwndExisting = Srmm_FindWindow(hContact); if (hwndExisting != 0) SendMessage(hwndExisting, DM_ACTIVATEME, 0, 0); else { wchar_t szName[CONTAINER_NAMELEN + 1]; GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN); TContainerData *pContainer = FindContainerByName(szName); if (pContainer == nullptr) pContainer = CreateContainer(szName, FALSE, hContact); if (pContainer) CreateNewTabForContact(pContainer, hContact, true, true, false, 0); } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // the SendMessageCommand() invokes a message session window for the given contact. // e.g. it is called when user double clicks a contact on the contact list // it is implemented as a service, so external plugins can use it to open a message window. // contacts handle must be passed in wParam. INT_PTR SendMessageCommand_Worker(MCONTACT hContact, LPCSTR pszMsg, bool isWchar) { // make sure that only the main UI thread will handle window creation if (GetCurrentThreadId() != PluginConfig.dwThreadID) { if (pszMsg) { wchar_t *tszText = (isWchar) ? mir_wstrdup((wchar_t*)pszMsg) : mir_a2u(pszMsg); PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SENDMESSAGECOMMANDW, hContact, (LPARAM)tszText); } else PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SENDMESSAGECOMMANDW, hContact, 0); return 0; } // does the MCONTACT's protocol support IM messages? char *szProto = GetContactProto(hContact); if (szProto == nullptr) return 0; // unknown contact if (!CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND) return 0; HWND hwnd = Srmm_FindWindow(hContact); if (hwnd) { if (pszMsg) { HWND hEdit = GetDlgItem(hwnd, IDC_SRMM_MESSAGE); SendMessage(hEdit, EM_SETSEL, -1, GetWindowTextLength(hEdit)); if (isWchar) SendMessageW(hEdit, EM_REPLACESEL, FALSE, (LPARAM)pszMsg); else SendMessageA(hEdit, EM_REPLACESEL, FALSE, (LPARAM)pszMsg); } SendMessage(hwnd, DM_ACTIVATEME, 0, 0); } else { wchar_t szName[CONTAINER_NAMELEN + 1]; GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN); TContainerData *pContainer = FindContainerByName(szName); if (pContainer == nullptr) pContainer = CreateContainer(szName, FALSE, hContact); if (pContainer) CreateNewTabForContact(pContainer, hContact, true, true, false, 0, isWchar, pszMsg); } return 0; } INT_PTR SendMessageCommand(WPARAM hContact, LPARAM lParam) { return SendMessageCommand_Worker(hContact, LPCSTR(lParam), false); } INT_PTR SendMessageCommand_W(WPARAM hContact, LPARAM lParam) { return SendMessageCommand_Worker(hContact, LPCSTR(lParam), true); } ///////////////////////////////////////////////////////////////////////////////////////// // open a window when user clicks on the flashing "typing message" tray icon. // just calls SendMessageCommand() for the given contact. static INT_PTR TypingMessageCommand(WPARAM, LPARAM lParam) { CLISTEVENT *cle = (CLISTEVENT*)lParam; if (cle) SendMessageCommand((WPARAM)cle->hContact, 0); return 0; } int SplitmsgShutdown(void) { WindowList_Destroy(PluginConfig.hUserPrefsWindowList); DestroyCursor(PluginConfig.hCurSplitNS); DestroyCursor(PluginConfig.hCurSplitWE); FreeLibrary(g_hMsftedit); if (g_hIconDLL) { FreeLibrary(g_hIconDLL); g_hIconDLL = nullptr; } ImageList_RemoveAll(PluginConfig.g_hImageList); ImageList_Destroy(PluginConfig.g_hImageList); delete Win7Taskbar; DestroyMenu(PluginConfig.g_hMenuContext); if (PluginConfig.g_hMenuContainer) DestroyMenu(PluginConfig.g_hMenuContainer); UnloadIcons(); FreeTabConfig(); if (Utils::rtf_ctable) mir_free(Utils::rtf_ctable); UnloadTSButtonModule(); return 0; } int MyAvatarChanged(WPARAM wParam, LPARAM lParam) { if (wParam == 0 || IsBadReadPtr((void*)wParam, 4)) return 0; for (TContainerData *p = pFirstContainer; p; p = p->pNext) BroadCastContainer(p, DM_MYAVATARCHANGED, wParam, lParam); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // tabbed mode support functions... // (C) by Nightwish // // this function searches and activates the tab belonging to the given hwnd (which is the // hwnd of a message dialog window) int TSAPI ActivateExistingTab(TContainerData *pContainer, HWND hwndChild) { CSrmmWindow *dat = (CSrmmWindow*)GetWindowLongPtr(hwndChild, GWLP_USERDATA); // needed to obtain the hContact for the message window if (!dat || !pContainer) return FALSE; NMHDR nmhdr = { 0 }; nmhdr.code = TCN_SELCHANGE; if (TabCtrl_GetItemCount(GetDlgItem(pContainer->m_hwnd, IDC_MSGTABS)) > 1 && !(pContainer->dwFlags & CNT_DEFERREDTABSELECT)) { TabCtrl_SetCurSel(GetDlgItem(pContainer->m_hwnd, IDC_MSGTABS), GetTabIndexFromHWND(GetDlgItem(pContainer->m_hwnd, IDC_MSGTABS), hwndChild)); SendMessage(pContainer->m_hwnd, WM_NOTIFY, 0, (LPARAM)&nmhdr); // just select the tab and let WM_NOTIFY do the rest } if (!dat->isChat()) pContainer->UpdateTitle(dat->m_hContact); if (IsIconic(pContainer->m_hwnd)) { SendMessage(pContainer->m_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); SetForegroundWindow(pContainer->m_hwnd); } // hide on close feature if (!IsWindowVisible(pContainer->m_hwnd)) { WINDOWPLACEMENT wp = { 0 }; wp.length = sizeof(wp); GetWindowPlacement(pContainer->m_hwnd, &wp); // all tabs must re-check the layout on activation because adding a tab while // the container was hidden can make this necessary BroadCastContainer(pContainer, DM_CHECKSIZE, 0, 0); if (wp.showCmd == SW_SHOWMAXIMIZED) ShowWindow(pContainer->m_hwnd, SW_SHOWMAXIMIZED); else { ShowWindow(pContainer->m_hwnd, SW_SHOWNA); SetForegroundWindow(pContainer->m_hwnd); } SendMessage(pContainer->m_hwndActive, WM_SIZE, 0, 0); // make sure the active tab resizes its layout properly } else if (GetForegroundWindow() != pContainer->m_hwnd) SetForegroundWindow(pContainer->m_hwnd); if (!dat->isChat()) SetFocus(GetDlgItem(hwndChild, IDC_SRMM_MESSAGE)); return TRUE; } ///////////////////////////////////////////////////////////////////////////////////////// // this function creates and activates a new tab within the given container. // bActivateTab: make the new tab the active one // bPopupContainer: restore container if it was minimized, otherwise flash it... HWND TSAPI CreateNewTabForContact(TContainerData *pContainer, MCONTACT hContact, bool bActivateTab, bool bPopupContainer, bool bWantPopup, MEVENT hdbEvent, bool bIsUnicode, const char *pszInitialText) { if (Srmm_FindWindow(hContact) != 0) { _DebugPopup(hContact, L"Warning: trying to create duplicate window"); return 0; } // if we have a max # of tabs/container set and want to open something in the default container... if (hContact != 0 && M.GetByte("limittabs", 0) && !wcsncmp(pContainer->m_wszName, L"default", 6)) if ((pContainer = FindMatchingContainer(L"default")) == nullptr) if ((pContainer = CreateContainer(L"default", CNT_CREATEFLAG_CLONED, hContact)) == nullptr) return 0; char *szProto = GetContactProto(hContact); // obtain various status information about the contact wchar_t *contactName = pcli->pfnGetContactDisplayName(hContact, 0); // cut nickname if larger than x chars... wchar_t newcontactname[128], tabtitle[128]; if (contactName && mir_wstrlen(contactName) > 0) { if (M.GetByte("cuttitle", 0)) CutContactName(contactName, newcontactname, _countof(newcontactname)); else wcsncpy_s(newcontactname, contactName, _TRUNCATE); Utils::DoubleAmpersands(newcontactname, _countof(newcontactname)); } else wcsncpy_s(newcontactname, L"_U_", _TRUNCATE); wchar_t *szStatus = pcli->pfnGetStatusModeDescription(szProto == nullptr ? ID_STATUS_OFFLINE : db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE), 0); if (PluginConfig.m_bStatusOnTabs) mir_snwprintf(tabtitle, L"%s (%s)", newcontactname, szStatus); else mir_snwprintf(tabtitle, L"%s", newcontactname); HWND hwndTab = GetDlgItem(pContainer->m_hwnd, IDC_MSGTABS); // hide the active tab if (pContainer->m_hwndActive && bActivateTab) ShowWindow(pContainer->m_hwndActive, SW_HIDE); int iTabIndex_wanted = M.GetDword(hContact, "tabindex", pContainer->iChilds * 100); int iCount = TabCtrl_GetItemCount(hwndTab); pContainer->iTabIndex = iCount; if (iCount > 0) { TCITEM item = {}; for (int i = iCount - 1; i >= 0; i--) { item.mask = TCIF_PARAM; TabCtrl_GetItem(hwndTab, i, &item); HWND hwnd = (HWND)item.lParam; CSrmmWindow *dat = (CSrmmWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (dat) { int relPos = M.GetDword(dat->m_hContact, "tabindex", i * 100); if (iTabIndex_wanted <= relPos) pContainer->iTabIndex = i; } } } TCITEM item = {}; item.pszText = tabtitle; item.mask = TCIF_TEXT | TCIF_IMAGE; item.iImage = 0; item.cchTextMax = _countof(tabtitle); int iTabId = TabCtrl_InsertItem(hwndTab, pContainer->iTabIndex, &item); SendMessage(hwndTab, EM_REFRESHWITHOUTCLIP, 0, 0); if (bActivateTab) TabCtrl_SetCurSel(hwndTab, iTabId); CSrmmWindow *pWindow = new CSrmmWindow(); pWindow->m_hContact = hContact; pWindow->m_iTabID = iTabId; pWindow->m_pContainer = pContainer; pContainer->iChilds++; pWindow->m_bActivate = bActivateTab; pWindow->m_bWantPopup = bWantPopup; pWindow->m_hDbEventFirst = hdbEvent; if (pszInitialText) pWindow->wszInitialText = (bIsUnicode) ? mir_wstrdup((const wchar_t*)pszInitialText) : mir_a2u(pszInitialText); pWindow->SetParent(hwndTab); pWindow->Create(); HWND hwndNew = pWindow->GetHwnd(); // switchbar support if (pContainer->dwFlags & CNT_SIDEBAR) pContainer->SideBar->addSession(pWindow, pContainer->iTabIndex); SendMessage(pContainer->m_hwnd, WM_SIZE, 0, 0); // if the container is minimized, then pop it up... if (IsIconic(pContainer->m_hwnd)) { if (bPopupContainer) { SendMessage(pContainer->m_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); SetFocus(pContainer->m_hwndActive); } else { if (pContainer->dwFlags & CNT_NOFLASH) SendMessage(pContainer->m_hwnd, DM_SETICON, 0, (LPARAM)Skin_LoadIcon(SKINICON_EVENT_MESSAGE)); else FlashContainer(pContainer, 1, 0); } } if (bActivateTab) { ActivateExistingTab(pContainer, hwndNew); SetFocus(hwndNew); RedrawWindow(pContainer->m_hwnd, nullptr, nullptr, RDW_ERASENOW); UpdateWindow(pContainer->m_hwnd); if (GetForegroundWindow() != pContainer->m_hwnd && bPopupContainer == TRUE) SetForegroundWindow(pContainer->m_hwnd); } else if (!IsIconic(pContainer->m_hwnd) && IsWindowVisible(pContainer->m_hwnd)) { SendMessage(pContainer->m_hwndActive, WM_SIZE, 0, 0); RedrawWindow(pContainer->m_hwndActive, nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW); RedrawWindow(pContainer->m_hwndActive, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW); } if (PluginConfig.m_bHideOnClose&&!IsWindowVisible(pContainer->m_hwnd)) { WINDOWPLACEMENT wp = { 0 }; wp.length = sizeof(wp); GetWindowPlacement(pContainer->m_hwnd, &wp); BroadCastContainer(pContainer, DM_CHECKSIZE, 0, 0); // make sure all tabs will re-check layout on activation if (wp.showCmd == SW_SHOWMAXIMIZED) ShowWindow(pContainer->m_hwnd, SW_SHOWMAXIMIZED); else { if (bPopupContainer) ShowWindow(pContainer->m_hwnd, SW_SHOWNORMAL); else ShowWindow(pContainer->m_hwnd, SW_SHOWMINNOACTIVE); } SendMessage(pContainer->m_hwndActive, WM_SIZE, 0, 0); } if (PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek && CSkin::m_skinEnabled) CWarning::show(CWarning::WARN_AEROPEEK_SKIN, MB_ICONWARNING | MB_OK); if (ServiceExists(MS_HPP_EG_EVENT) && ServiceExists(MS_IEVIEW_EVENT) && db_get_b(0, "HistoryPlusPlus", "IEViewAPI", 0)) if (IDYES == CWarning::show(CWarning::WARN_HPP_APICHECK, MB_ICONWARNING | MB_YESNO)) db_set_b(0, "HistoryPlusPlus", "IEViewAPI", 0); return hwndNew; // return handle of the new dialog } ///////////////////////////////////////////////////////////////////////////////////////// // this is used by the 2nd containermode (limit tabs on default containers). // it searches a container with "room" for the new tabs or otherwise creates // a new (cloned) one. TContainerData* TSAPI FindMatchingContainer(const wchar_t *szName) { int iMaxTabs = M.GetDword("maxtabs", 0); if (iMaxTabs > 0 && M.GetByte("limittabs", 0) && !wcsncmp(szName, L"default", 6)) { // search a "default" with less than iMaxTabs opened... for (TContainerData *p = pFirstContainer; p; p = p->pNext) if (!wcsncmp(p->m_wszName, L"default", 6) && p->iChilds < iMaxTabs) return p; return nullptr; } return FindContainerByName(szName); } ///////////////////////////////////////////////////////////////////////////////////////// // load some global icons. void TSAPI CreateImageList(BOOL bInitial) { // the imagelist is now a fake. It is still needed to provide the tab control with a // image list handle. This will make sure that the tab control will reserve space for // an icon on each tab. This is a blank and empty icon if (bInitial) { PluginConfig.g_hImageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 0); HICON hIcon = CreateIcon(g_hInst, 16, 16, 1, 4, nullptr, nullptr); ImageList_AddIcon(PluginConfig.g_hImageList, hIcon); DestroyIcon(hIcon); } PluginConfig.g_IconFileEvent = Skin_LoadIcon(SKINICON_EVENT_FILE); PluginConfig.g_IconMsgEvent = Skin_LoadIcon(SKINICON_EVENT_MESSAGE); PluginConfig.g_IconMsgEventBig = Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true); if ((HICON)CALLSERVICE_NOTFOUND == PluginConfig.g_IconMsgEventBig) PluginConfig.g_IconMsgEventBig = 0; PluginConfig.g_IconTypingEventBig = Skin_LoadIcon(SKINICON_OTHER_TYPING, true); if ((HICON)CALLSERVICE_NOTFOUND == PluginConfig.g_IconTypingEventBig) PluginConfig.g_IconTypingEventBig = 0; PluginConfig.g_IconSend = PluginConfig.g_buttonBarIcons[9]; PluginConfig.g_IconTypingEvent = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]; } ///////////////////////////////////////////////////////////////////////////////////////// // standard icon definitions static TIconDesc _toolbaricons[] = { { "tabSRMM_mlog", LPGEN("Message Log Options"), &PluginConfig.g_buttonBarIcons[2], -IDI_MSGLOGOPT, 1 }, { "tabSRMM_multi", LPGEN("Image tag"), &PluginConfig.g_buttonBarIcons[3], -IDI_IMAGETAG, 1 }, { "tabSRMM_quote", LPGEN("Quote text"), &PluginConfig.g_buttonBarIcons[8], -IDI_QUOTE, 1 }, { "tabSRMM_save", LPGEN("Save and close"), &PluginConfig.g_buttonBarIcons[7], -IDI_SAVE, 1 }, { "tabSRMM_send", LPGEN("Send message"), &PluginConfig.g_buttonBarIcons[9], -IDI_SEND, 1 }, { "tabSRMM_avatar", LPGEN("Edit user notes"), &PluginConfig.g_buttonBarIcons[10], -IDI_CONTACTPIC, 1 }, { "tabSRMM_close", LPGEN("Close"), &PluginConfig.g_buttonBarIcons[6], -IDI_CLOSEMSGDLG, 1 } }; static TIconDesc _exttoolbaricons[] = { { "tabSRMM_emoticon", LPGEN("Smiley button"), &PluginConfig.g_buttonBarIcons[11], -IDI_SMILEYICON, 1 }, { "tabSRMM_bold", LPGEN("Format bold"), &PluginConfig.g_buttonBarIcons[17], -IDI_FONTBOLD, 1 }, { "tabSRMM_italic", LPGEN("Format italic"), &PluginConfig.g_buttonBarIcons[18], -IDI_FONTITALIC, 1 }, { "tabSRMM_underline", LPGEN("Format underline"), &PluginConfig.g_buttonBarIcons[19], -IDI_FONTUNDERLINE, 1 }, { "tabSRMM_face", LPGEN("Font face"), &PluginConfig.g_buttonBarIcons[20], -IDI_FONTFACE, 1 }, { "tabSRMM_color", LPGEN("Font color"), &PluginConfig.g_buttonBarIcons[21], -IDI_FONTCOLOR, 1 }, { "tabSRMM_strikeout", LPGEN("Format strike-through"), &PluginConfig.g_buttonBarIcons[30], -IDI_STRIKEOUT, 1 } }; static TIconDesc _chattoolbaricons[] = { { "chat_bkgcol", LPGEN("Background color"), &PluginConfig.g_buttonBarIcons[31], -IDI_BKGCOLOR, 1 }, { "chat_settings", LPGEN("Room settings"), &PluginConfig.g_buttonBarIcons[32], -IDI_TOPICBUT, 1 }, { "chat_filter", LPGEN("Event filter"), &PluginConfig.g_buttonBarIcons[33], -IDI_FILTER2, 1 }, { "chat_shownicklist", LPGEN("Nick list"), &PluginConfig.g_buttonBarIcons[35], -IDI_SHOWNICKLIST, 1 } }; static TIconDesc _logicons[] = { { "tabSRMM_error", LPGEN("Message delivery error"), &PluginConfig.g_iconErr, -IDI_MSGERROR, 1 }, { "tabSRMM_in", LPGEN("Incoming message"), &PluginConfig.g_iconIn, -IDI_ICONIN, 0 }, { "tabSRMM_out", LPGEN("Outgoing message"), &PluginConfig.g_iconOut, -IDI_ICONOUT, 0 }, { "tabSRMM_status", LPGEN("Status change"), &PluginConfig.g_iconStatus, -IDI_STATUSCHANGE, 0 } }; static TIconDesc _deficons[] = { { "tabSRMM_container", LPGEN("Static container icon"), &PluginConfig.g_iconContainer, -IDI_CONTAINER, 1 }, { "tabSRMM_sounds_on", LPGEN("Sounds (status bar)"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS], -IDI_SOUNDSON, 1 }, { "tabSRMM_pulldown", LPGEN("Pulldown Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_PULLDOWN], -IDI_PULLDOWNARROW, 1 }, { "tabSRMM_Leftarrow", LPGEN("Left Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_LEFT], -IDI_LEFTARROW, 1 }, { "tabSRMM_Rightarrow", LPGEN("Right Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_RIGHT], -IDI_RIGHTARROW, 1 }, { "tabSRMM_Pulluparrow", LPGEN("Up Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_UP], -IDI_PULLUPARROW, 1 }, { "tabSRMM_sb_slist", LPGEN("Session List"), &PluginConfig.g_sideBarIcons[0], -IDI_SESSIONLIST, 1 }, }; static TIconDesc _trayIcon[] = { { "tabSRMM_frame1", LPGEN("Frame 1"), &PluginConfig.m_AnimTrayIcons[0], -IDI_TRAYANIM1, 1 }, { "tabSRMM_frame2", LPGEN("Frame 2"), &PluginConfig.m_AnimTrayIcons[1], -IDI_TRAYANIM2, 1 }, { "tabSRMM_frame3", LPGEN("Frame 3"), &PluginConfig.m_AnimTrayIcons[2], -IDI_TRAYANIM3, 1 }, { "tabSRMM_frame4", LPGEN("Frame 4"), &PluginConfig.m_AnimTrayIcons[3], -IDI_TRAYANIM4, 1 }, }; struct { char *szSection; TIconDesc *idesc; int nItems; } static ICONBLOCKS[] = { { LPGEN("Message Sessions") "/" LPGEN("Default"), _deficons, _countof(_deficons) }, { LPGEN("Message Sessions") "/" LPGEN("Toolbar"), _toolbaricons, _countof(_toolbaricons) }, { LPGEN("Message Sessions") "/" LPGEN("Toolbar"), _exttoolbaricons, _countof(_exttoolbaricons) }, { LPGEN("Message Sessions") "/" LPGEN("Toolbar"), _chattoolbaricons, _countof(_chattoolbaricons) }, { LPGEN("Message Sessions") "/" LPGEN("Message Log"), _logicons, _countof(_logicons) }, { LPGEN("Message Sessions") "/" LPGEN("Animated Tray"), _trayIcon, _countof(_trayIcon) } }; static int GetIconPackVersion(HMODULE hDLL) { char szIDString[256]; int version = 0; if (LoadStringA(hDLL, IDS_IDENTIFY, szIDString, sizeof(szIDString)) == 0) version = 0; else if (!mir_strcmp(szIDString, "__tabSRMM_ICONPACK 1.0__")) version = 1; else if (!mir_strcmp(szIDString, "__tabSRMM_ICONPACK 2.0__")) version = 2; else if (!mir_strcmp(szIDString, "__tabSRMM_ICONPACK 3.0__")) version = 3; else if (!mir_strcmp(szIDString, "__tabSRMM_ICONPACK 3.5__")) version = 4; else if (!mir_strcmp(szIDString, "__tabSRMM_ICONPACK 5.0__")) version = 5; if (version < 5) CWarning::show(CWarning::WARN_ICONPACK_VERSION, MB_OK | MB_ICONERROR); return version; } ///////////////////////////////////////////////////////////////////////////////////////// // setup default icons for the IcoLib service. This needs to be done every time the // plugin is loaded default icons are taken from the icon pack in either \icons or \plugins static int TSAPI SetupIconLibConfig() { int j = 2, version = 0; wchar_t szFilename[MAX_PATH]; wcsncpy(szFilename, L"icons\\tabsrmm_icons.dll", MAX_PATH); g_hIconDLL = LoadLibrary(szFilename); if (g_hIconDLL == 0) { CWarning::show(CWarning::WARN_ICONPACKMISSING, CWarning::CWF_NOALLOWHIDE | MB_ICONERROR | MB_OK); return 0; } GetModuleFileName(g_hIconDLL, szFilename, MAX_PATH); Chat_AddIcons(); version = GetIconPackVersion(g_hIconDLL); FreeLibrary(g_hIconDLL); g_hIconDLL = 0; SKINICONDESC sid = { 0 }; sid.defaultFile.w = szFilename; sid.flags = SIDF_PATH_UNICODE; for (int n = 0; n < _countof(ICONBLOCKS); n++) { sid.section.a = ICONBLOCKS[n].szSection; for (int i = 0; i < ICONBLOCKS[n].nItems; i++) { sid.pszName = ICONBLOCKS[n].idesc[i].szName; sid.description.a = ICONBLOCKS[n].idesc[i].szDesc; sid.iDefaultIndex = ICONBLOCKS[n].idesc[i].uId == -IDI_HISTORY ? 0 : ICONBLOCKS[n].idesc[i].uId; // workaround problem /w icoLib and a resource id of 1 (actually, a Windows problem) if (n > 0 && n < 4) PluginConfig.g_buttonBarIconHandles[j++] = IcoLib_AddIcon(&sid); else IcoLib_AddIcon(&sid); } } sid.section.a = LPGEN("Message Sessions") "/" LPGEN("Default"); sid.pszName = "tabSRMM_clock_symbol"; sid.description.a = LPGEN("Clock symbol (for the info panel clock)"); sid.iDefaultIndex = -IDI_CLOCK; IcoLib_AddIcon(&sid); wcsncpy(szFilename, L"plugins\\tabsrmm.dll", MAX_PATH); sid.pszName = "tabSRMM_overlay_disabled"; sid.description.a = LPGEN("Feature disabled (used as overlay)"); sid.iDefaultIndex = -IDI_FEATURE_DISABLED; IcoLib_AddIcon(&sid); sid.pszName = "tabSRMM_overlay_enabled"; sid.description.a = LPGEN("Feature enabled (used as overlay)"); sid.iDefaultIndex = -IDI_FEATURE_ENABLED; IcoLib_AddIcon(&sid); return 1; } // load the icon theme from IconLib - check if it exists... static int TSAPI LoadFromIconLib() { for (int n = 0; n < _countof(ICONBLOCKS); n++) for (int i = 0; i < ICONBLOCKS[n].nItems; i++) *(ICONBLOCKS[n].idesc[i].phIcon) = IcoLib_GetIcon(ICONBLOCKS[n].idesc[i].szName); PluginConfig.g_buttonBarIcons[0] = Skin_LoadIcon(SKINICON_OTHER_ADDCONTACT); PluginConfig.g_buttonBarIcons[1] = Skin_LoadIcon(SKINICON_OTHER_HISTORY); PluginConfig.g_buttonBarIconHandles[0] = Skin_GetIconHandle(SKINICON_OTHER_HISTORY); PluginConfig.g_buttonBarIconHandles[1] = Skin_GetIconHandle(SKINICON_OTHER_ADDCONTACT); PluginConfig.g_buttonBarIconHandles[20] = Skin_GetIconHandle(SKINICON_OTHER_USERDETAILS); PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING] = PluginConfig.g_buttonBarIcons[12] = Skin_LoadIcon(SKINICON_OTHER_TYPING); PluginConfig.g_IconChecked = Skin_LoadIcon(SKINICON_OTHER_TICK); PluginConfig.g_IconUnchecked = Skin_LoadIcon(SKINICON_OTHER_NOTICK); PluginConfig.g_IconGroupOpen = Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN); PluginConfig.g_IconGroupClose = Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT); PluginConfig.g_iconOverlayEnabled = IcoLib_GetIcon("tabSRMM_overlay_enabled"); PluginConfig.g_iconOverlayDisabled = IcoLib_GetIcon("tabSRMM_overlay_disabled"); PluginConfig.g_iconClock = IcoLib_GetIcon("tabSRMM_clock_symbol"); CacheMsgLogIcons(); Srmm_Broadcast(WM_CBD_LOADICONS, 0, 0); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // load icon theme from either icon pack or IcoLib void TSAPI LoadIconTheme() { if (SetupIconLibConfig() == 0) return; else LoadFromIconLib(); Skin->setupTabCloseBitmap(); } static void UnloadIcons() { for (int n = 0; n < _countof(ICONBLOCKS); n++) for (int i = 0; i < ICONBLOCKS[n].nItems; i++) if (*(ICONBLOCKS[n].idesc[i].phIcon) != 0) { DestroyIcon(*(ICONBLOCKS[n].idesc[i].phIcon)); *(ICONBLOCKS[n].idesc[i].phIcon) = 0; } if (PluginConfig.g_hbmUnknown) DeleteObject(PluginConfig.g_hbmUnknown); for (int i = 0; i < 4; i++) if (PluginConfig.m_AnimTrayIcons[i]) DestroyIcon(PluginConfig.m_AnimTrayIcons[i]); } ///////////////////////////////////////////////////////////////////////////////////////// int IcoLibIconsChanged(WPARAM, LPARAM) { LoadFromIconLib(); CacheMsgLogIcons(); return 0; } int IconsChanged(WPARAM, LPARAM) { CreateImageList(FALSE); CacheMsgLogIcons(); Srmm_Broadcast(DM_OPTIONSAPPLIED, 0, 0); Srmm_Broadcast(DM_UPDATEWINICON, 0, 0); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // services needed for assembly management static INT_PTR ReloadSkin(WPARAM, LPARAM) { Skin->setFileName(); Skin->Load(); return 0; } static INT_PTR ReloadSettings(WPARAM, LPARAM lParam) { PluginConfig.reloadSettings(lParam != 0); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // initialises the internal API, services, events etc... static void TSAPI InitAPI() { CreateServiceFunction(MS_MSG_READMESSAGE, ReadMessageCommand); CreateServiceFunction(MS_MSG_SENDMESSAGE, SendMessageCommand); CreateServiceFunction(MS_MSG_SENDMESSAGEW, SendMessageCommand_W); CreateServiceFunction(MS_MSG_TYPINGMESSAGE, TypingMessageCommand); CreateServiceFunction("TabSRMsg/ReloadSkin", ReloadSkin); CreateServiceFunction("TabSRMsg/ReloadSettings", ReloadSettings); CreateServiceFunction(MS_TABMSG_SETUSERPREFS, SetUserPrefs); CreateServiceFunction(MS_TABMSG_TRAYSUPPORT, Service_OpenTrayMenu); CreateServiceFunction(MS_TABMSG_SLQMGR, CSendLater::svcQMgr); SI_InitStatusIcons(); } int LoadSendRecvMessageModule(void) { if (FIF == 0) { MessageBox(0, TranslateT("The image service plugin (AdvaImg) is not properly installed.\n\nTabSRMM is disabled."), TranslateT("TabSRMM fatal error"), MB_OK | MB_ICONERROR); return 1; } INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES | ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&icex); g_hMsftedit = Utils::loadSystemLibrary(L"\\Msftedit.dll"); Win7Taskbar = new CTaskbarInteract; Win7Taskbar->updateMetrics(); memset(&nen_options, 0, sizeof(nen_options)); PluginConfig.hUserPrefsWindowList = WindowList_Create(); sendQueue = new SendQueue; Skin = new CSkin; sendLater = new CSendLater; InitOptions(); InitAPI(); PluginConfig.reloadSystemStartup(); ReloadTabConfig(); NEN_ReadOptions(&nen_options); db_set_b(0, TEMPLATES_MODULE, "setup", 2); LoadDefaultTemplates(); return 0; }