diff options
Diffstat (limited to 'plugins/TabSRMM/src/chat_window.cpp')
-rw-r--r-- | plugins/TabSRMM/src/chat_window.cpp | 3258 |
1 files changed, 3258 insertions, 0 deletions
diff --git a/plugins/TabSRMM/src/chat_window.cpp b/plugins/TabSRMM/src/chat_window.cpp new file mode 100644 index 0000000000..da5e408c66 --- /dev/null +++ b/plugins/TabSRMM/src/chat_window.cpp @@ -0,0 +1,3258 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// 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 +// +// This implements the group chat dialog window + +#include "stdafx.h" + +// externs... +extern LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +extern HRESULT(WINAPI *MyCloseThemeData)(HANDLE); + +int g_cLinesPerPage = 0; +int g_iWheelCarryover = 0; + +extern HMENU g_hMenu; + +static HKL hkl = NULL; +char szIndicators[] = { 0, '+', '%', '@', '!', '*' }; + +struct MESSAGESUBDATA +{ + time_t lastEnterTime; + wchar_t *szSearchQuery; + wchar_t *szSearchResult; + BOOL iSavedSpaces; + SESSION_INFO *lastSession; +}; + +const CLSID IID_ITextDocument = { 0x8CC497C0, 0xA1DF, 0x11CE, { 0x80, 0x98, 0x00, 0xAA, 0x00, 0x47, 0xBE, 0x5D } }; + +///////////////////////////////////////////////////////////////////////////////////////// +// checking if theres's protected text at the point +// emulates EN_LINK WM_NOTIFY to parent to process links + +static BOOL CheckCustomLink(HWND hwndDlg, POINT *ptClient, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bUrlNeeded) +{ + long res = 0, cnt = 0; + long cpMin = 0, cpMax = 0; + POINT ptEnd = { 0 }; + IRichEditOle *RichEditOle = NULL; + ITextDocument *TextDocument = NULL; + ITextRange *TextRange = NULL; + ITextFont *TextFont = NULL; + BOOL bIsCustomLink = FALSE; + + POINT pt = *ptClient; + ClientToScreen(hwndDlg, &pt); + + do { + if (!SendMessage(hwndDlg, EM_GETOLEINTERFACE, 0, (LPARAM)&RichEditOle)) break; + if (RichEditOle->QueryInterface(IID_ITextDocument, (void**)&TextDocument) != S_OK) break; + if (TextDocument->RangeFromPoint(pt.x, pt.y, &TextRange) != S_OK) break; + + TextRange->GetStart(&cpMin); + cpMax = cpMin + 1; + TextRange->SetEnd(cpMax); + + if (TextRange->GetFont(&TextFont) != S_OK) + break; + + TextFont->GetProtected(&res); + if (res != tomTrue) + break; + + TextRange->GetPoint(tomEnd + TA_BOTTOM + TA_RIGHT, &ptEnd.x, &ptEnd.y); + if (pt.x > ptEnd.x || pt.y > ptEnd.y) + break; + + if (bUrlNeeded) { + TextRange->GetStoryLength(&cnt); + for (; cpMin > 0; cpMin--) { + res = tomTrue; + TextRange->SetIndex(tomCharacter, cpMin + 1, tomTrue); + TextFont->GetProtected(&res); + if (res != tomTrue) { cpMin++; break; } + } + for (cpMax--; cpMax < cnt; cpMax++) { + res = tomTrue; + TextRange->SetIndex(tomCharacter, cpMax + 1, tomTrue); + TextFont->GetProtected(&res); + if (res != tomTrue) + break; + } + } + + bIsCustomLink = (cpMin < cpMax); + } while (FALSE); + + if (TextFont) TextFont->Release(); + if (TextRange) TextRange->Release(); + if (TextDocument) TextDocument->Release(); + if (RichEditOle) RichEditOle->Release(); + + if (bIsCustomLink) { + ENLINK enlink = { 0 }; + enlink.nmhdr.hwndFrom = hwndDlg; + enlink.nmhdr.idFrom = IDC_LOG; + enlink.nmhdr.code = EN_LINK; + enlink.msg = uMsg; + enlink.wParam = wParam; + enlink.lParam = lParam; + enlink.chrg.cpMin = cpMin; + enlink.chrg.cpMax = cpMax; + SendMessage(GetParent(hwndDlg), WM_NOTIFY, IDC_LOG, (LPARAM)&enlink); + } + return bIsCustomLink; +} + +bool IsStringValidLink(wchar_t *pszText) +{ + if (pszText == NULL) + return false; + + if (mir_wstrlen(pszText) < 5 || wcschr(pszText, '"')) + return false; + + if (towlower(pszText[0]) == 'w' && towlower(pszText[1]) == 'w' && towlower(pszText[2]) == 'w' && pszText[3] == '.' && iswalnum(pszText[4])) + return true; + + return wcsstr(pszText, L"://") != NULL; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// called whenever a group chat tab becomes active(either by switching tabs or activating a +// container window + +void CChatRoomDlg::UpdateWindowState(UINT msg) +{ + if (si == NULL) + return; + + if (msg == WM_ACTIVATE) { + if (m_pContainer->dwFlags & CNT_TRANSPARENCY) { + DWORD trans = LOWORD(m_pContainer->settings->dwTransparency); + SetLayeredWindowAttributes(m_pContainer->hwnd, CSkin::m_ContainerColorKey, (BYTE)trans, (m_pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); + } + } + + if (m_hwndFilter) { + POINT pt; + GetCursorPos(&pt); + + RECT rcFilter; + GetWindowRect(m_hwndFilter, &rcFilter); + if (!PtInRect(&rcFilter, pt)) { + SendMessage(m_hwndFilter, WM_CLOSE, 1, 1); + m_hwndFilter = 0; + } + } + + if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) { + m_iInputAreaHeight = 0; + m_message.SendMsg(EM_REQUESTRESIZE, 0, 0); + } + + m_pPanel.dismissConfig(); + m_dwUnread = 0; + if (m_pWnd) { + m_pWnd->activateTab(); + m_pWnd->setOverlayIcon(0, true); + } + + if (m_pContainer->hwndSaved == m_hwnd || m_bWasDeleted) + return; + + m_pContainer->hwndSaved = m_hwnd; + + pci->SetActiveSession(si->ptszID, si->pszModule); + m_hTabIcon = m_hTabStatusIcon; + + if (m_iTabID >= 0) { + if (db_get_w(si->hContact, si->pszModule, "ApparentMode", 0) != 0) + db_set_w(si->hContact, si->pszModule, "ApparentMode", 0); + if (pcli->pfnGetEvent(si->hContact, 0)) + pcli->pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); + + SendMessage(m_hwnd, GC_UPDATETITLE, 0, 1); + m_dwTickLastEvent = 0; + m_dwFlags &= ~MWF_DIVIDERSET; + if (KillTimer(m_hwnd, TIMERID_FLASHWND) || m_iFlashIcon) { + FlashTab(false); + m_bCanFlashTab = FALSE; + m_iFlashIcon = 0; + } + if (m_pContainer->dwFlashingStarted != 0) { + FlashContainer(m_pContainer, 0, 0); + m_pContainer->dwFlashingStarted = 0; + } + m_pContainer->dwFlags &= ~CNT_NEED_UPDATETITLE; + + if (m_dwFlags & MWF_NEEDCHECKSIZE) + PostMessage(m_hwnd, DM_SAVESIZE, 0, 0); + + if (PluginConfig.m_bAutoLocaleSupport) { + if (hkl == 0) + DM_LoadLocale(); + else + SendMessage(m_hwnd, DM_SETLOCALE, 0, 0); + } + SetFocus(GetDlgItem(m_hwnd, IDC_MESSAGE)); + m_dwLastActivity = GetTickCount(); + m_pContainer->dwLastActivity = m_dwLastActivity; + m_pContainer->MenuBar->configureMenu(); + UpdateTrayMenuState(this, FALSE); + DM_SetDBButtonStates(); + + if (m_dwFlagsEx & MWF_EX_DELAYEDSPLITTER) { + m_dwFlagsEx &= ~MWF_EX_DELAYEDSPLITTER; + ShowWindow(m_pContainer->hwnd, SW_RESTORE); + PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam); + PostMessage(m_hwnd, WM_SIZE, 0, 0); + m_wParam = m_lParam = 0; + } + } + BB_SetButtonsPos(); + if (M.isAero()) + InvalidateRect(m_hwndParent, NULL, FALSE); + if (m_pContainer->dwFlags & CNT_SIDEBAR) + m_pContainer->SideBar->setActiveItem(this); + + if (m_pWnd) + m_pWnd->Invalidate(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// resizer callback for the group chat session window.Called from Mirandas dialog +// resizing service + +int CChatRoomDlg::Resizer(UTILRESIZECONTROL *urc) +{ + bool bToolbar = !(m_pContainer->dwFlags & CNT_HIDETOOLBAR); + bool bBottomToolbar = (m_pContainer->dwFlags & CNT_BOTTOMTOOLBAR) != 0; + bool bNick = si->iType != GCW_SERVER && si->bNicklistEnabled; + bool bInfoPanel = m_pPanel.isActive(); + int panelHeight = m_pPanel.getHeight() + 1; + int iSplitterX = m_pContainer->settings->iSplitterX; + + RECT rcTabs; + GetClientRect(m_hwnd, &rcTabs); + + if (m_bIsAutosizingInput) + Utils::showDlgControl(m_hwnd, IDC_SPLITTERY, SW_HIDE); + + if (si->iType != GCW_SERVER) { + Utils::showDlgControl(m_hwnd, IDC_LIST, si->bNicklistEnabled ? SW_SHOW : SW_HIDE); + Utils::showDlgControl(m_hwnd, IDC_SPLITTERX, si->bNicklistEnabled ? SW_SHOW : SW_HIDE); + + m_btnNickList.Enable(true); + m_btnFilter.Enable(true); + if (si->iType == GCW_CHATROOM) { + MODULEINFO *tmp = pci->MM_FindModule(si->pszModule); + if (tmp) + m_btnChannelMgr.Enable(tmp->bChanMgr); + } + } + else { + Utils::showDlgControl(m_hwnd, IDC_LIST, SW_HIDE); + Utils::showDlgControl(m_hwnd, IDC_SPLITTERX, SW_HIDE); + } + + if (si->iType == GCW_SERVER) { + m_btnNickList.Enable(false); + m_btnFilter.Enable(false); + m_btnChannelMgr.Enable(false); + } + + switch (urc->wId) { + case IDC_PANELSPLITTER: + urc->rcItem.bottom = panelHeight; + urc->rcItem.top = panelHeight - 2; + return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; + + case IDC_LOG: + urc->rcItem.top = 0; + urc->rcItem.left = 0; + urc->rcItem.right = bNick ? urc->dlgNewSize.cx - iSplitterX : urc->dlgNewSize.cx; + urc->rcItem.bottom = urc->dlgNewSize.cy - m_iSplitterY; + if (!bToolbar || bBottomToolbar) + urc->rcItem.bottom += DPISCALEY_S(21); + if (bInfoPanel) + urc->rcItem.top += panelHeight; + if (CSkin::m_skinEnabled) { + CSkinItem *item = &SkinItems[ID_EXTBKHISTORY]; + if (!item->IGNORED) { + urc->rcItem.left += item->MARGIN_LEFT; + urc->rcItem.right -= item->MARGIN_RIGHT; + urc->rcItem.top += item->MARGIN_TOP; + urc->rcItem.bottom -= item->MARGIN_BOTTOM; + } + } + return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; + + case IDC_LIST: + urc->rcItem.top = 0; + urc->rcItem.right = urc->dlgNewSize.cx; + urc->rcItem.left = urc->dlgNewSize.cx - iSplitterX + 2; + urc->rcItem.bottom = urc->dlgNewSize.cy - m_iSplitterY; + if (!bToolbar || bBottomToolbar) + urc->rcItem.bottom += DPISCALEY_S(21); + if (bInfoPanel) + urc->rcItem.top += panelHeight; + if (CSkin::m_skinEnabled) { + CSkinItem *item = &SkinItems[ID_EXTBKUSERLIST]; + if (!item->IGNORED) { + urc->rcItem.left += item->MARGIN_LEFT; + urc->rcItem.right -= item->MARGIN_RIGHT; + urc->rcItem.top += item->MARGIN_TOP; + urc->rcItem.bottom -= item->MARGIN_BOTTOM; + } + } + return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; + + case IDC_SPLITTERX: + urc->rcItem.right = urc->dlgNewSize.cx - iSplitterX + 2; + urc->rcItem.left = urc->dlgNewSize.cx - iSplitterX; + urc->rcItem.bottom = urc->dlgNewSize.cy - m_iSplitterY; + if (!bToolbar || bBottomToolbar) + urc->rcItem.bottom += DPISCALEY_S(21); + urc->rcItem.top = 0; + if (bInfoPanel) + urc->rcItem.top += panelHeight; + return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; + + case IDC_SPLITTERY: + urc->rcItem.right = urc->dlgNewSize.cx; + urc->rcItem.top = urc->dlgNewSize.cy - m_iSplitterY + DPISCALEY_S(23); + urc->rcItem.bottom = urc->rcItem.top + DPISCALEY_S(2); + urc->rcItem.left = 0; + urc->rcItem.bottom++; + urc->rcItem.top++; + return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; + + case IDC_MESSAGE: + urc->rcItem.right = urc->dlgNewSize.cx; + urc->rcItem.top = urc->dlgNewSize.cy - m_iSplitterY + 3 + DPISCALEY_S(23); + urc->rcItem.bottom = urc->dlgNewSize.cy; + if (bBottomToolbar && bToolbar) + urc->rcItem.bottom -= DPISCALEY_S(22); + + if (m_bIsAutosizingInput) + urc->rcItem.top -= DPISCALEY_S(1); + + if (CSkin::m_skinEnabled) { + CSkinItem *item = &SkinItems[ID_EXTBKINPUTAREA]; + if (!item->IGNORED) { + urc->rcItem.left += item->MARGIN_LEFT; + urc->rcItem.right -= item->MARGIN_RIGHT; + urc->rcItem.top += item->MARGIN_TOP; + urc->rcItem.bottom -= item->MARGIN_BOTTOM; + } + } + return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclassing for the message input control(a richedit text control) + +static bool TabAutoComplete(HWND hwnd, MESSAGESUBDATA *dat, SESSION_INFO *si) +{ + LRESULT lResult = (LRESULT)SendMessage(hwnd, EM_GETSEL, 0, 0); + int start = LOWORD(lResult), end = HIWORD(lResult); + SendMessage(hwnd, EM_SETSEL, end, end); + + GETTEXTEX gt = { 0 }; + gt.codepage = 1200; + gt.flags = GTL_DEFAULT | GTL_PRECISE; + int iLen = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0); + if (iLen <= 0) + return false; + + bool isTopic = false, isRoom = false; + wchar_t *pszName = NULL; + wchar_t *pszText = (wchar_t*)mir_calloc((iLen + 10) * sizeof(wchar_t)); + + gt.flags = GT_DEFAULT; + gt.cb = (iLen + 9) * sizeof(wchar_t); + SendMessage(hwnd, EM_GETTEXTEX, (WPARAM)>, (LPARAM)pszText); + + if (start > 1 && pszText[start - 1] == ' ' && pszText[start - 2] == ':') + start -= 2; + + if (dat->szSearchResult != NULL) { + int cbResult = (int)mir_wstrlen(dat->szSearchResult); + if (start >= cbResult && !wcsnicmp(dat->szSearchResult, pszText + start - cbResult, cbResult)) { + start -= cbResult; + goto LBL_SkipEnd; + } + } + + while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB) + start--; + +LBL_SkipEnd: + while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB) + end++; + + if (pszText[start] == '#') + isRoom = TRUE; + else { + int topicStart = start; + while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB)) + topicStart--; + if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6]) + isTopic = TRUE; + } + if (dat->szSearchQuery == NULL) { + dat->szSearchQuery = mir_wstrndup(pszText + start, end - start); + dat->szSearchResult = mir_wstrdup(dat->szSearchQuery); + dat->lastSession = NULL; + } + if (isTopic) + pszName = si->ptszTopic; + else if (isRoom) { + dat->lastSession = SM_FindSessionAutoComplete(si->pszModule, si, dat->lastSession, dat->szSearchQuery, dat->szSearchResult); + if (dat->lastSession != NULL) + pszName = dat->lastSession->ptszName; + } + else pszName = pci->UM_FindUserAutoComplete(si->pUsers, dat->szSearchQuery, dat->szSearchResult); + + replaceStrW(dat->szSearchResult, NULL); + + if (pszName != NULL) { + dat->szSearchResult = mir_wstrdup(pszName); + if (end != start) { + ptrW szReplace; + if (!isRoom && !isTopic && g_Settings.bAddColonToAutoComplete && start == 0) { + szReplace = (wchar_t*)mir_alloc((mir_wstrlen(pszName) + 4) * sizeof(wchar_t)); + mir_wstrcpy(szReplace, pszName); + mir_wstrcat(szReplace, g_Settings.bUseCommaAsColon ? L", " : L": "); + pszName = szReplace; + } + SendMessage(hwnd, EM_SETSEL, start, end); + SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM)pszName); + } + return true; + } + + if (end != start) { + SendMessage(hwnd, EM_SETSEL, start, end); + SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM)dat->szSearchQuery); + } + replaceStrW(dat->szSearchQuery, NULL); + return false; +} + +static LRESULT CALLBACK MessageSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND hwndParent = GetParent(hwnd); + CChatRoomDlg *mwdat = (CChatRoomDlg*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); + MESSAGESUBDATA *dat = (MESSAGESUBDATA*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + if (mwdat != nullptr) + if (mwdat->m_fkeyProcessed && (msg == WM_KEYUP)) { + GetKeyboardState(mwdat->kstate); + if (!(mwdat->kstate[VK_CONTROL] & 0x80) && !(mwdat->kstate[VK_SHIFT] & 0x80)) + mwdat->m_fkeyProcessed = false; + return 0; + } + + switch (msg) { + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, MessageSubclassProc); + + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, MessageSubclassProc); + + case EM_SUBCLASSED: + dat = (MESSAGESUBDATA*)mir_calloc(sizeof(MESSAGESUBDATA)); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)dat); + return 0; + + case WM_CONTEXTMENU: + POINT pt; + GetCursorPos(&pt); + { + HMENU hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); + HMENU hSubMenu = GetSubMenu(hMenu, 2); + RemoveMenu(hSubMenu, 9, MF_BYPOSITION); + RemoveMenu(hSubMenu, 8, MF_BYPOSITION); + RemoveMenu(hSubMenu, 4, MF_BYPOSITION); + + MODULEINFO *mi = pci->MM_FindModule(mwdat->si->pszModule); + EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, MF_BYCOMMAND | ((mi && mi->bBold) ? MF_ENABLED : MF_GRAYED)); + TranslateMenu(hSubMenu); + + CHARRANGE sel, all = { 0, -1 }; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) { + EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(hSubMenu, IDM_CUT, MF_BYCOMMAND | MF_GRAYED); + } + + MessageWindowPopupData mwpd = { sizeof(mwpd) }; + mwpd.uType = MSG_WINDOWPOPUP_SHOWING; + mwpd.uFlags = MSG_WINDOWPOPUP_INPUT; + mwpd.hContact = mwdat->m_hContact; + mwpd.hwnd = hwnd; + mwpd.hMenu = hSubMenu; + mwpd.pt = pt; + NotifyEventHooks(PluginConfig.m_event_MsgPopup, 0, (LPARAM)&mwpd); + + int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, GetParent(hwnd), NULL); + + mwpd.selection = iSelection; + mwpd.uType = MSG_WINDOWPOPUP_SELECTED; + NotifyEventHooks(PluginConfig.m_event_MsgPopup, 0, (LPARAM)&mwpd); + + switch (iSelection) { + case IDM_COPY: + SendMessage(hwnd, WM_COPY, 0, 0); + break; + case IDM_CUT: + SendMessage(hwnd, WM_CUT, 0, 0); + break; + case IDM_PASTE: + case IDM_PASTEFORMATTED: + SendMessage(hwnd, EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0); + break; + case IDM_COPYALL: + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&all); + SendMessage(hwnd, WM_COPY, 0, 0); + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); + break; + case IDM_SELECTALL: + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&all); + break; + } + DestroyMenu(hMenu); + } + return TRUE; + + case WM_MOUSEWHEEL: + if (mwdat->DM_MouseWheelHandler(wParam, lParam) == 0) + return 0; + + dat->lastEnterTime = 0; + break; + + case WM_SYSKEYUP: + if (wParam == VK_MENU) { + ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_MESSAGE); + return 0; + } + break; + + case WM_SYSKEYDOWN: + mwdat->m_fkeyProcessed = false; + if (ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_MESSAGE)) { + mwdat->m_fkeyProcessed = true; + return 0; + } + break; + + case WM_SYSCHAR: + if (mwdat->m_fkeyProcessed) { + mwdat->m_fkeyProcessed = false; // preceeding key event has been processed by miranda hotkey service + return 0; + } + + if ((wParam >= '0' && wParam <= '9') && (GetKeyState(VK_MENU) & 0x8000)) { // ALT-1 -> ALT-0 direct tab selection + BYTE bChar = (BYTE)wParam; + int iIndex = (bChar == '0') ? 10 : bChar - (BYTE)'0'; + SendMessage(mwdat->m_pContainer->hwnd, DM_SELECTTAB, DM_SELECT_BY_INDEX, (LPARAM)iIndex); + return 0; + } + break; + + case WM_CHAR: + bool isShift, isAlt, isCtrl; + mwdat->KbdState(isShift, isCtrl, isAlt); + + if (PluginConfig.m_bSoundOnTyping && !isAlt &&!isCtrl&&!(mwdat->m_pContainer->dwFlags & CNT_NOSOUND) && wParam != VK_ESCAPE&&!(wParam == VK_TAB && PluginConfig.m_bAllowTab)) + SkinPlaySound("SoundOnTyping"); + + if (isCtrl && !isAlt && !isShift) { + MODULEINFO *mi = pci->MM_FindModule(mwdat->si->pszModule); + if (mi == NULL) + return 0; + + switch (wParam) { + case 0x09: // ctrl-i (italics) + if (mi->bItalics) { + CheckDlgButton(hwndParent, IDC_ITALICS, IsDlgButtonChecked(hwndParent, IDC_ITALICS) == BST_UNCHECKED ? BST_CHECKED : BST_UNCHECKED); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_ITALICS, 0), 0); + } + return 0; + case 0x02: // ctrl-b (bold) + if (mi->bBold) { + CheckDlgButton(hwndParent, IDC_BOLD, IsDlgButtonChecked(hwndParent, IDC_BOLD) == BST_UNCHECKED ? BST_CHECKED : BST_UNCHECKED); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_BOLD, 0), 0); + } + return 0; + case 0x20: // ctrl-space clear formatting + if (mi->bBold && mi->bItalics && mi->bUnderline) { + CheckDlgButton(hwndParent, IDC_BKGCOLOR, BST_UNCHECKED); + CheckDlgButton(hwndParent, IDC_COLOR, BST_UNCHECKED); + CheckDlgButton(hwndParent, IDC_BOLD, BST_UNCHECKED); + CheckDlgButton(hwndParent, IDC_UNDERLINE, BST_UNCHECKED); + CheckDlgButton(hwndParent, IDC_ITALICS, BST_UNCHECKED); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_BKGCOLOR, 0), 0); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_COLOR, 0), 0); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_BOLD, 0), 0); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_UNDERLINE, 0), 0); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_ITALICS, 0), 0); + } + return 0; + case 0x0c: // ctrl-l background color + if (mi->bBkgColor) { + CheckDlgButton(hwndParent, IDC_BKGCOLOR, IsDlgButtonChecked(hwndParent, IDC_BKGCOLOR) == BST_UNCHECKED ? BST_CHECKED : BST_UNCHECKED); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_BKGCOLOR, 0), 0); + } + return 0; + case 0x15: // ctrl-u underlined + if (mi->bUnderline) { + CheckDlgButton(hwndParent, IDC_UNDERLINE, IsDlgButtonChecked(hwndParent, IDC_UNDERLINE) == BST_UNCHECKED ? BST_CHECKED : BST_UNCHECKED); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_UNDERLINE, 0), 0); + } + return 0; // ctrl-k color + case 0x0b: + if (mi->bColor) { + CheckDlgButton(hwndParent, IDC_COLOR, IsDlgButtonChecked(hwndParent, IDC_COLOR) == BST_UNCHECKED ? BST_CHECKED : BST_UNCHECKED); + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_COLOR, 0), 0); + } + return 0; + case 0x17: + PostMessage(hwndParent, WM_CLOSE, 0, 1); + return 0; + } + } + break; + + case WM_KEYDOWN: + mwdat->KbdState(isShift, isCtrl, isAlt); + + // sound on typing.. + if (PluginConfig.m_bSoundOnTyping && !isAlt && wParam == VK_DELETE) + SkinPlaySound("SoundOnTyping"); + + if (wParam == VK_INSERT && !isShift && !isCtrl && !isAlt) { + mwdat->m_fInsertMode = !mwdat->m_fInsertMode; + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), EN_CHANGE), (LPARAM)hwnd); + } + if (wParam == VK_CAPITAL || wParam == VK_NUMLOCK) + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), EN_CHANGE), (LPARAM)hwnd); + + if (isCtrl && isAlt && !isShift) { + switch (wParam) { + case VK_UP: + case VK_DOWN: + case VK_PRIOR: + case VK_NEXT: + case VK_HOME: + case VK_END: + WPARAM wp = 0; + + if (wParam == VK_UP) + wp = MAKEWPARAM(SB_LINEUP, 0); + else if (wParam == VK_PRIOR) + wp = MAKEWPARAM(SB_PAGEUP, 0); + else if (wParam == VK_NEXT) + wp = MAKEWPARAM(SB_PAGEDOWN, 0); + else if (wParam == VK_HOME) + wp = MAKEWPARAM(SB_TOP, 0); + else if (wParam == VK_END) { + mwdat->DM_ScrollToBottom(0, 0); + return 0; + } + else if (wParam == VK_DOWN) + wp = MAKEWPARAM(SB_LINEDOWN, 0); + + SendDlgItemMessage(hwndParent, IDC_LOG, WM_VSCROLL, wp, 0); + return 0; + } + } + + if (wParam == VK_RETURN) { + if (isShift) { + if (PluginConfig.m_bSendOnShiftEnter) { + PostMessage(hwndParent, WM_COMMAND, IDOK, 0); + return 0; + } + break; + } + if ((isCtrl && !isShift) ^ (0 != PluginConfig.m_bSendOnEnter)) { + PostMessage(hwndParent, WM_COMMAND, IDOK, 0); + return 0; + } + if (!PluginConfig.m_bSendOnEnter && !PluginConfig.m_bSendOnDblEnter) + break; + if (isCtrl) + break; + + if (PluginConfig.m_bSendOnDblEnter) { + if (dat->lastEnterTime + 2 < time(NULL)) { + dat->lastEnterTime = time(NULL); + break; + } + + SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 0); + SendMessage(hwnd, WM_KEYUP, VK_BACK, 0); + PostMessage(hwndParent, WM_COMMAND, IDOK, 0); + return 0; + } + PostMessage(hwndParent, WM_COMMAND, IDOK, 0); + return 0; + } + else dat->lastEnterTime = 0; + + if ((wParam == VK_NEXT && isCtrl && !isShift) || (wParam == VK_TAB && isCtrl && !isShift)) { // CTRL-TAB (switch tab/window) + SendMessage(mwdat->m_pContainer->hwnd, DM_SELECTTAB, DM_SELECT_NEXT, 0); + return TRUE; + } + + if ((wParam == VK_PRIOR && isCtrl && !isShift) || (wParam == VK_TAB && isCtrl && isShift)) { // CTRL_SHIFT-TAB (switch tab/window) + SendMessage(mwdat->m_pContainer->hwnd, DM_SELECTTAB, DM_SELECT_PREV, 0); + return TRUE; + } + if (wParam == VK_TAB && !isCtrl && !isShift) { //tab-autocomplete + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + bool fCompleted = TabAutoComplete(hwnd, dat, mwdat->si); + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE); + if (!fCompleted && !PluginConfig.m_bAllowTab) { + if ((GetSendButtonState(mwdat->GetHwnd()) != PBS_DISABLED)) + SetFocus(GetDlgItem(mwdat->GetHwnd(), IDOK)); + else + SetFocus(GetDlgItem(mwdat->GetHwnd(), IDC_LOG)); + } + return 0; + } + if (wParam != VK_RIGHT && wParam != VK_LEFT) { + mir_free(dat->szSearchQuery); + dat->szSearchQuery = NULL; + mir_free(dat->szSearchResult); + dat->szSearchResult = NULL; + } + + if (wParam == VK_F4 && isCtrl && !isAlt) { // ctrl-F4 (close tab) + SendMessage(hwndParent, WM_COMMAND, MAKEWPARAM(IDC_CLOSE, BN_CLICKED), 0); + return 0; + } + + if (wParam == VK_NEXT || wParam == VK_PRIOR) { + HWND htemp = hwndParent; + SendDlgItemMessage(htemp, IDC_LOG, msg, wParam, lParam); + dat->lastEnterTime = 0; + return 0; + } + + if (wParam == VK_UP && isCtrl && !isAlt) { + char *lpPrevCmd = pci->SM_GetPrevCommand(mwdat->si->ptszID, mwdat->si->pszModule); + + if (!mwdat->si->lpCurrentCommand || !mwdat->si->lpCurrentCommand->last) { + // Next command is not defined. It means currently entered text is not saved in the history and it + // need to be saved in the window context. + char *enteredText = Message_GetFromStream(hwndParent); + if (mwdat->m_enteredText) + mir_free(mwdat->m_enteredText); + + mwdat->m_enteredText = enteredText; + } + + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + + LOGFONTA lf; + LoadLogfont(MSGFONTID_MESSAGEAREA, &lf, NULL, FONTMODULE); + + SETTEXTEX ste; + ste.flags = ST_DEFAULT; + ste.codepage = CP_ACP; + if (lpPrevCmd) + SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)lpPrevCmd); + else + SetWindowText(hwnd, L""); + + GETTEXTLENGTHEX gtl = { 0 }; + gtl.flags = GTL_PRECISE; + gtl.codepage = CP_ACP; + int iLen = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + SendMessage(hwnd, EM_SCROLLCARET, 0, 0); + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE); + SendMessage(hwnd, EM_SETSEL, iLen, iLen); + dat->lastEnterTime = 0; + return 0; + } + + if (wParam == VK_DOWN && isCtrl && !isAlt) { + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + + SETTEXTEX ste; + ste.flags = ST_DEFAULT; + ste.codepage = CP_ACP; + + char *lpPrevCmd = pci->SM_GetNextCommand(mwdat->si->ptszID, mwdat->si->pszModule); + if (lpPrevCmd) + SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)lpPrevCmd); + else if (mwdat->m_enteredText) { + // If we cannot load the message from history, load the last edited text. + SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&ste, (LPARAM)mwdat->m_enteredText); + mir_free(mwdat->m_enteredText); + mwdat->m_enteredText = NULL; + } + + GETTEXTLENGTHEX gtl = { 0 }; + gtl.flags = GTL_PRECISE; + gtl.codepage = CP_ACP; + int iLen = SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + SendMessage(hwnd, EM_SCROLLCARET, 0, 0); + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE); + SendMessage(hwnd, EM_SETSEL, iLen, iLen); + dat->lastEnterTime = 0; + return 0; + } + // fall through + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_KILLFOCUS: + dat->lastEnterTime = 0; + break; + + case WM_KEYUP: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + COLORREF cr; + LoadLogfont(MSGFONTID_MESSAGEAREA, NULL, &cr, FONTMODULE); + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR | CFM_UNDERLINETYPE; + cf.dwEffects = 0; + SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + { + MODULEINFO *mi = pci->MM_FindModule(mwdat->si->pszModule); + if (mi == NULL) + break; + + if (mi->bColor) { + int index = Chat_GetColorIndex(mwdat->si->pszModule, cf.crTextColor); + UINT u = IsDlgButtonChecked(GetParent(hwnd), IDC_COLOR); + + if (index >= 0) { + mwdat->si->bFGSet = TRUE; + mwdat->si->iFG = index; + } + + if (u == BST_UNCHECKED && cf.crTextColor != cr) + CheckDlgButton(hwndParent, IDC_COLOR, BST_CHECKED); + else if (u == BST_CHECKED && cf.crTextColor == cr) + CheckDlgButton(hwndParent, IDC_COLOR, BST_UNCHECKED); + } + + if (mi->bBkgColor) { + int index = Chat_GetColorIndex(mwdat->si->pszModule, cf.crBackColor); + COLORREF crB = (COLORREF)M.GetDword(FONTMODULE, "inputbg", SRMSGDEFSET_BKGCOLOUR); + UINT u = IsDlgButtonChecked(hwndParent, IDC_BKGCOLOR); + + if (index >= 0) { + mwdat->si->bBGSet = TRUE; + mwdat->si->iBG = index; + } + + if (u == BST_UNCHECKED && cf.crBackColor != crB) + CheckDlgButton(hwndParent, IDC_BKGCOLOR, BST_CHECKED); + else if (u == BST_CHECKED && cf.crBackColor == crB) + CheckDlgButton(hwndParent, IDC_BKGCOLOR, BST_UNCHECKED); + } + + if (mi->bBold) { + UINT u = IsDlgButtonChecked(hwndParent, IDC_BOLD); + UINT u2 = cf.dwEffects; + u2 &= CFE_BOLD; + if (u == BST_UNCHECKED && u2) + CheckDlgButton(hwndParent, IDC_BOLD, BST_CHECKED); + else if (u == BST_CHECKED && u2 == 0) + CheckDlgButton(hwndParent, IDC_BOLD, BST_UNCHECKED); + } + + if (mi->bItalics) { + UINT u = IsDlgButtonChecked(hwndParent, IDC_ITALICS); + UINT u2 = cf.dwEffects; + u2 &= CFE_ITALIC; + if (u == BST_UNCHECKED && u2) + CheckDlgButton(hwndParent, IDC_ITALICS, BST_CHECKED); + else if (u == BST_CHECKED && u2 == 0) + CheckDlgButton(hwndParent, IDC_ITALICS, BST_UNCHECKED); + } + + if (mi->bUnderline) { + UINT u = IsDlgButtonChecked(hwndParent, IDC_UNDERLINE); + if (cf.dwEffects & CFE_UNDERLINE && (cf.bUnderlineType & CFU_UNDERLINE || cf.bUnderlineType & CFU_UNDERLINEWORD)) { + if (u == BST_UNCHECKED) + CheckDlgButton(hwndParent, IDC_UNDERLINE, BST_CHECKED); + } + else { + if (u == BST_CHECKED) + CheckDlgButton(hwndParent, IDC_UNDERLINE, BST_UNCHECKED); + } + } + } + break; + + case WM_INPUTLANGCHANGE: + if (PluginConfig.m_bAutoLocaleSupport && GetFocus() == hwnd && mwdat->m_pContainer->hwndActive == hwndParent && GetForegroundWindow() == mwdat->m_pContainer->hwnd && GetActiveWindow() == mwdat->m_pContainer->hwnd) { + mwdat->DM_SaveLocale(wParam, lParam); + SendMessage(hwnd, EM_SETLANGOPTIONS, 0, (LPARAM)SendMessage(hwnd, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD); + return 1; + } + break; + + case WM_ERASEBKGND: + return !CSkin::m_skinEnabled; + + case WM_DESTROY: + mir_free(dat); + } + + return mir_callNextSubclass(hwnd, MessageSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclassing for the message filter dialog (set and configure event filters for the +// current session + +static UINT _eventorder[] = +{ + GC_EVENT_ACTION, + GC_EVENT_MESSAGE, + GC_EVENT_NICK, + GC_EVENT_JOIN, + GC_EVENT_PART, + GC_EVENT_TOPIC, + GC_EVENT_ADDSTATUS, + GC_EVENT_INFORMATION, + GC_EVENT_QUIT, + GC_EVENT_KICK, + GC_EVENT_NOTICE +}; + +static INT_PTR CALLBACK FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + SESSION_INFO *si = (SESSION_INFO*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + switch (uMsg) { + case WM_INITDIALOG: + si = (SESSION_INFO*)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)si); + { + DWORD dwMask = db_get_dw(si->hContact, CHAT_MODULE, "FilterMask", 0); + DWORD dwFlags = db_get_dw(si->hContact, CHAT_MODULE, "FilterFlags", 0); + + DWORD dwPopupMask = db_get_dw(si->hContact, CHAT_MODULE, "PopupMask", 0); + DWORD dwPopupFlags = db_get_dw(si->hContact, CHAT_MODULE, "PopupFlags", 0); + + DWORD dwTrayMask = db_get_dw(si->hContact, CHAT_MODULE, "TrayIconMask", 0); + DWORD dwTrayFlags = db_get_dw(si->hContact, CHAT_MODULE, "TrayIconFlags", 0); + + for (int i = 0; i < _countof(_eventorder); i++) { + CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE); + CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE); + CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE); + } + } + return FALSE; + + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + SetTextColor((HDC)wParam, RGB(60, 60, 150)); + SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); + + case WM_CLOSE: + if (wParam == 1 && lParam == 1) { + int iFlags = 0, i; + DWORD dwMask = 0; + + for (i = 0; i < _countof(_eventorder); i++) { + int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i); + dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0); + iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0); + } + + if (iFlags&GC_EVENT_ADDSTATUS) + iFlags |= GC_EVENT_REMOVESTATUS; + + if (si) { + if (dwMask == 0) { + db_unset(si->hContact, CHAT_MODULE, "FilterFlags"); + db_unset(si->hContact, CHAT_MODULE, "FilterMask"); + } + else { + db_set_dw(si->hContact, CHAT_MODULE, "FilterFlags", iFlags); + db_set_dw(si->hContact, CHAT_MODULE, "FilterMask", dwMask); + } + } + + dwMask = iFlags = 0; + + for (i = 0; i < _countof(_eventorder); i++) { + int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i); + dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0); + iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0); + } + + if (iFlags & GC_EVENT_ADDSTATUS) + iFlags |= GC_EVENT_REMOVESTATUS; + + if (si) { + if (dwMask == 0) { + db_unset(si->hContact, CHAT_MODULE, "PopupFlags"); + db_unset(si->hContact, CHAT_MODULE, "PopupMask"); + } + else { + db_set_dw(si->hContact, CHAT_MODULE, "PopupFlags", iFlags); + db_set_dw(si->hContact, CHAT_MODULE, "PopupMask", dwMask); + } + } + + dwMask = iFlags = 0; + + for (i = 0; i < _countof(_eventorder); i++) { + int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i); + dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0); + iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0); + } + if (iFlags & GC_EVENT_ADDSTATUS) + iFlags |= GC_EVENT_REMOVESTATUS; + + if (si) { + if (dwMask == 0) { + db_unset(si->hContact, CHAT_MODULE, "TrayIconFlags"); + db_unset(si->hContact, CHAT_MODULE, "TrayIconMask"); + } + else { + db_set_dw(si->hContact, CHAT_MODULE, "TrayIconFlags", iFlags); + db_set_dw(si->hContact, CHAT_MODULE, "TrayIconMask", dwMask); + } + Chat_SetFilters(si); + SendMessage(si->hWnd, GC_CHANGEFILTERFLAG, 0, iFlags); + if (si->bFilterEnabled) + SendMessage(si->hWnd, GC_REDRAWLOG, 0, 0); + } + } + DestroyWindow(hwndDlg); + break; + + case WM_DESTROY: + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + break; + } + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclass for some tool bar buttons which must perform special actions +// on right click. + +static LRESULT CALLBACK ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_RBUTTONUP: + if (M.GetByte(CHAT_MODULE, "RightClickFilter", 0) != 0) { + HWND hwndParent = GetParent(hwnd); + switch (GetDlgCtrlID(hwnd)) { + case IDC_FILTER: + SendMessage(hwndParent, GC_SHOWFILTERMENU, 0, 0); + break; + case IDC_COLOR: + SendMessage(hwndParent, GC_SHOWCOLORCHOOSER, 0, IDC_COLOR); + break; + case IDC_BKGCOLOR: + SendMessage(hwndParent, GC_SHOWCOLORCHOOSER, 0, IDC_BKGCOLOR); + } + } + break; + } + + return mir_callNextSubclass(hwnd, ButtonSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclassing for the message history display(rich edit control in which the chat history appears) + +static LRESULT CALLBACK LogSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND hwndParent = GetParent(hwnd); + CChatRoomDlg *mwdat = (CChatRoomDlg*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); + + switch (msg) { + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, LogSubclassProc); + + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, LogSubclassProc); + + case WM_COPY: + return Utils::WMCopyHandler(hwnd, LogSubclassProc, msg, wParam, lParam); + + case WM_SETCURSOR: + if (g_Settings.bClickableNicks && (LOWORD(lParam) == HTCLIENT)) { + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hwnd, &pt); + if (CheckCustomLink(hwnd, &pt, msg, wParam, lParam, FALSE)) return TRUE; + } + break; + + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + if (g_Settings.bClickableNicks) { + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + CheckCustomLink(hwnd, &pt, msg, wParam, lParam, TRUE); + } + break; + + case WM_LBUTTONUP: + if (g_Settings.bClickableNicks) { + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + CheckCustomLink(hwnd, &pt, msg, wParam, lParam, TRUE); + } + if (M.GetByte("autocopy", 1)) { + CHARRANGE sel; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) { + SendMessage(hwnd, WM_COPY, 0, 0); + sel.cpMin = sel.cpMax; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); + SetFocus(GetDlgItem(hwndParent, IDC_MESSAGE)); + } + } + break; + + case WM_KEYDOWN: + if (wParam == 0x57 && GetKeyState(VK_CONTROL) & 0x8000) { // ctrl-w (close window) + PostMessage(hwndParent, WM_CLOSE, 0, 1); + return TRUE; + } + if (wParam == VK_INSERT && GetKeyState(VK_CONTROL) & 0x8000) + return Utils::WMCopyHandler(hwnd, LogSubclassProc, msg, wParam, lParam); + break; + + case WM_SYSKEYUP: + if (wParam == VK_MENU) { + ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_LOG); + return 0; + } + break; + + case WM_SYSKEYDOWN: + mwdat->m_fkeyProcessed = false; + if (ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_LOG)) { + mwdat->m_fkeyProcessed = true; + return 0; + } + break; + + case WM_SYSCHAR: + if (mwdat->m_fkeyProcessed) { + mwdat->m_fkeyProcessed = false; + return 0; + } + break; + + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) { + CHARRANGE sel; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) { + sel.cpMin = sel.cpMax; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel); + } + } + break; + + case WM_CHAR: + bool isCtrl, isShift, isAlt; + mwdat->KbdState(isShift, isCtrl, isAlt); + if (wParam == 0x03 && isCtrl) // Ctrl+C + return Utils::WMCopyHandler(hwnd, LogSubclassProc, msg, wParam, lParam); + + SetFocus(GetDlgItem(hwndParent, IDC_MESSAGE)); + SendDlgItemMessage(hwndParent, IDC_MESSAGE, WM_CHAR, wParam, lParam); + break; + } + + return mir_callNextSubclass(hwnd, LogSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclassing for the nickname list control.It is an ownerdrawn listbox + +static LRESULT CALLBACK NicklistSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND hwndParent = GetParent(hwnd); + CChatRoomDlg *dat = (CChatRoomDlg*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); + + switch (msg) { + case WM_NCCALCSIZE: + if (CSkin::m_DisableScrollbars) { + RECT lpRect; + GetClientRect(hwnd, &lpRect); + LONG itemHeight = SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0); + g_cLinesPerPage = (lpRect.bottom - lpRect.top) / itemHeight; + } + return CSkin::NcCalcRichEditFrame(hwnd, dat, ID_EXTBKUSERLIST, msg, wParam, lParam, NicklistSubclassProc); + + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, dat, ID_EXTBKUSERLIST, msg, wParam, lParam, NicklistSubclassProc); + + case WM_ERASEBKGND: + { + HDC dc = (HDC)wParam; + if (dc) { + int index = SendMessage(hwnd, LB_GETTOPINDEX, 0, 0); + if (index == LB_ERR || dat->si->nUsersInNicklist <= 0) + return 0; + + int items = dat->si->nUsersInNicklist - index; + int height = SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0); + + if (height != LB_ERR) { + RECT rc = { 0 }; + GetClientRect(hwnd, &rc); + + if (rc.bottom - rc.top > items * height) { + rc.top = items * height; + FillRect(dc, &rc, pci->hListBkgBrush); + } + } + } + } + return 1; + + case WM_MOUSEWHEEL: + if (CSkin::m_DisableScrollbars) { + UINT uScroll; + short zDelta = (short)HIWORD(wParam); + if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uScroll, 0)) + uScroll = 3; /* default value */ + + if (uScroll == WHEEL_PAGESCROLL) + uScroll = g_cLinesPerPage; + if (uScroll == 0) + return 0; + + zDelta += g_iWheelCarryover; /* Accumulate wheel motion */ + + int dLines = zDelta * (int)uScroll / WHEEL_DELTA; + + //Record the unused portion as the next carryover. + g_iWheelCarryover = zDelta - dLines * WHEEL_DELTA / (int)uScroll; + + // scrolling. + while (abs(dLines)) { + if (dLines > 0) { + SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); + dLines--; + } + else { + SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); + dLines++; + } + } + return 0; + } + break; + + case WM_KEYDOWN: + if (wParam == 0x57 && GetKeyState(VK_CONTROL) & 0x8000) { // ctrl-w (close window) + PostMessage(hwndParent, WM_CLOSE, 0, 1); + return TRUE; + } + + if (wParam == VK_ESCAPE || wParam == VK_UP || wParam == VK_DOWN || wParam == VK_NEXT || + wParam == VK_PRIOR || wParam == VK_TAB || wParam == VK_HOME || wParam == VK_END) { + if (dat) { + dat->m_wszSearch[0] = 0; + dat->m_iSearchItem = -1; + } + } + break; + + case WM_SETFOCUS: + case WM_KILLFOCUS: + if (dat) { // set/kill focus invalidates incremental search status + dat->m_wszSearch[0] = 0; + dat->m_iSearchItem = -1; + } + break; + + case WM_CHAR: + case WM_UNICHAR: + // simple incremental search for the user (nick) - list control + // typing esc or movement keys will clear the current search string + if (dat) { + if (wParam == 27 && dat->m_wszSearch[0]) { // escape - reset everything + dat->m_wszSearch[0] = 0; + dat->m_iSearchItem = -1; + break; + } + if (wParam == '\b' && dat->m_wszSearch[0]) // backspace + dat->m_wszSearch[mir_wstrlen(dat->m_wszSearch) - 1] = '\0'; + else if (wParam < ' ') + break; + else { + if (mir_wstrlen(dat->m_wszSearch) >= _countof(dat->m_wszSearch) - 2) { + MessageBeep(MB_OK); + break; + } + wchar_t szNew[2]; + szNew[0] = (wchar_t)wParam; + szNew[1] = '\0'; + mir_wstrcat(dat->m_wszSearch, szNew); + } + if (dat->m_wszSearch[0]) { + // iterate over the (sorted) list of nicknames and search for the + // string we have + int i, iItems = SendMessage(hwnd, LB_GETCOUNT, 0, 0); + for (i = 0; i < iItems; i++) { + USERINFO *ui = pci->UM_FindUserFromIndex(dat->si->pUsers, i); + if (ui) { + if (!wcsnicmp(ui->pszNick, dat->m_wszSearch, mir_wstrlen(dat->m_wszSearch))) { + SendMessage(hwnd, LB_SETSEL, FALSE, -1); + SendMessage(hwnd, LB_SETSEL, TRUE, i); + dat->m_iSearchItem = i; + InvalidateRect(hwnd, NULL, FALSE); + return 0; + } + } + } + if (i == iItems) { + MessageBeep(MB_OK); + dat->m_wszSearch[mir_wstrlen(dat->m_wszSearch) - 1] = '\0'; + return 0; + } + } + } + break; + + case WM_RBUTTONDOWN: + { + int iCounts = SendMessage(hwnd, LB_GETSELCOUNT, 0, 0); + if (iCounts != LB_ERR && iCounts > 1) + return 0; + SendMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam); + } + break; + + case WM_RBUTTONUP: + SendMessage(hwnd, WM_LBUTTONUP, wParam, lParam); + break; + + case WM_MEASUREITEM: + { + MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; + if (mis->CtlType == ODT_MENU) + return Menu_MeasureItem(lParam); + } + return FALSE; + + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; + if (dis->CtlType == ODT_MENU) + return Menu_DrawItem(lParam); + } + return FALSE; + + case WM_CONTEXTMENU: + { + SESSION_INFO *si = dat->si; + if (si == NULL) + break; + + int height = 0; + TVHITTESTINFO hti; + hti.pt.x = GET_X_LPARAM(lParam); + hti.pt.y = GET_Y_LPARAM(lParam); + if (hti.pt.x == -1 && hti.pt.y == -1) { + int index = SendMessage(hwnd, LB_GETCURSEL, 0, 0); + int top = SendMessage(hwnd, LB_GETTOPINDEX, 0, 0); + height = SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0); + hti.pt.x = 4; + hti.pt.y = (index - top) * height + 1; + } + else ScreenToClient(hwnd, &hti.pt); + + int item = (DWORD)(SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y))); + if (HIWORD(item) == 1) + item = (DWORD)(-1); + else + item &= 0xFFFF; + + USERINFO *ui = pci->SM_GetUserFromIndex(si->ptszID, si->pszModule, item); + if (ui) { + HMENU hMenu = 0; + USERINFO uinew; + memcpy(&uinew, ui, sizeof(USERINFO)); + if (hti.pt.x == -1 && hti.pt.y == -1) + hti.pt.y += height - 4; + ClientToScreen(hwnd, &hti.pt); + + UINT uID = CreateGCMenu(hwnd, &hMenu, 0, hti.pt, si, uinew.pszUID, uinew.pszNick); + switch (uID) { + case 0: + break; + + case 20020: // add to highlight... + { + THighLightEdit the = { THighLightEdit::CMD_ADD, si, ui }; + HWND hwndDlg = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_ADDHIGHLIGHT), dat->m_pContainer->hwnd, CMUCHighlight::dlgProcAdd, (LPARAM)&the); + TranslateDialogDefault(hwndDlg); + + RECT rc, rcWnd; + GetClientRect(dat->m_pContainer->hwnd, &rcWnd); + GetWindowRect(hwndDlg, &rc); + SetWindowPos(hwndDlg, HWND_TOP, (rcWnd.right - (rc.right - rc.left)) / 2, (rcWnd.bottom - (rc.bottom - rc.top)) / 2, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + } + break; + + case ID_MESS: + pci->DoEventHookAsync(GetParent(hwnd), si->ptszID, si->pszModule, GC_USER_PRIVMESS, ui, nullptr, 0); + break; + + default: + int iCount = SendMessage(hwnd, LB_GETCOUNT, 0, 0); + if (iCount != LB_ERR) { + int iSelectedItems = SendMessage(hwnd, LB_GETSELCOUNT, 0, 0); + if (iSelectedItems != LB_ERR) { + int *pItems = (int *)mir_alloc(sizeof(int) * (iSelectedItems + 1)); + if (pItems) { + if (SendMessage(hwnd, LB_GETSELITEMS, iSelectedItems, (LPARAM)pItems) != LB_ERR) { + for (int i = 0; i < iSelectedItems; i++) { + USERINFO *ui1 = pci->SM_GetUserFromIndex(si->ptszID, si->pszModule, pItems[i]); + if (ui1) + pci->DoEventHookAsync(hwndParent, si->ptszID, si->pszModule, GC_USER_NICKLISTMENU, ui1, nullptr, (LPARAM)uID); + } + } + mir_free(pItems); + } + } + } + break; + } + DestroyGCMenu(&hMenu, 1); + return TRUE; + } + } + break; + + case WM_MOUSEMOVE: + Chat_HoverMouse(dat->si, hwnd, lParam, M.GetByte("adv_TipperTooltip", 1) && ServiceExists("mToolTip/HideTip")); + break; + } + return mir_callNextSubclass(hwnd, NicklistSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// calculate the required rectangle for a string using the given font. This is more +// precise than using GetTextExtentPoint...() + +int GetTextPixelSize(wchar_t* pszText, HFONT hFont, bool bWidth) +{ + if (!pszText || !hFont) + return 0; + + HDC hdc = GetDC(NULL); + HFONT hOldFont = (HFONT)SelectObject(hdc, hFont); + + RECT rc = { 0 }; + DrawText(hdc, pszText, -1, &rc, DT_CALCRECT); + + SelectObject(hdc, hOldFont); + ReleaseDC(NULL, hdc); + return bWidth ? rc.right - rc.left : rc.bottom - rc.top; +} + +static void __cdecl phase2(void * lParam) +{ + Thread_SetName("TabSRMM: phase2"); + + SESSION_INFO *si = (SESSION_INFO*)lParam; + Sleep(30); + if (si && si->hWnd) + PostMessage(si->hWnd, GC_REDRAWLOG2, 0, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// the actual group chat session window procedure.Handles the entire chat session window +// which is usually a (tabbed) child of a container class window. + +CChatRoomDlg::CChatRoomDlg(SESSION_INFO *_si) + : CTabBaseDlg(IDD_CHANNEL), + m_btnOk(this, IDOK), + m_list(this, IDC_LIST), + m_btnBold(this, IDC_BOLD), + m_btnColor(this, IDC_COLOR), + m_btnFilter(this, IDC_FILTER), + m_btnItalic(this, IDC_ITALICS), + m_btnHistory(this, IDC_HISTORY), + m_btnBkColor(this, IDC_BKGCOLOR), + m_btnChannelMgr(this, IDC_CHANMGR), + m_btnUnderline(this, IDC_UNDERLINE), + m_btnNickList(this, IDC_SHOWNICKLIST) +{ + m_bType = SESSIONTYPE_CHAT; + + si = _si; + m_hContact = si->hContact; + m_szProto = GetContactProto(si->hContact); + + m_btnOk.OnClick = Callback(this, &CChatRoomDlg::OnClick_OK); + m_btnFilter.OnClick = Callback(this, &CChatRoomDlg::OnClick_Filter); + m_btnHistory.OnClick = Callback(this, &CChatRoomDlg::OnClick_History); + m_btnNickList.OnClick = Callback(this, &CChatRoomDlg::OnClick_ShowNickList); + m_btnChannelMgr.OnClick = Callback(this, &CChatRoomDlg::OnClick_ChanMgr); + + m_btnColor.OnClick = Callback(this, &CChatRoomDlg::OnClick_Color); + m_btnBkColor.OnClick = Callback(this, &CChatRoomDlg::OnClick_BkColor); + m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CChatRoomDlg::OnClick_BIU); + + m_message.OnChange = Callback(this, &CChatRoomDlg::OnChange_Message); + + m_list.OnDblClick = Callback(this, &CChatRoomDlg::OnDblClick_List); +} + +CThumbBase* CChatRoomDlg::CreateThumb(CProxyWindow *pProxy) const +{ + return new CThumbMUC(pProxy, si); +} + +void CChatRoomDlg::ClearLog() +{ + SESSION_INFO *s = pci->SM_FindSession(si->ptszID, si->pszModule); + if (s) { + m_log.SetText(L""); + pci->LM_RemoveAll(&s->pLog, &s->pLogEnd); + s->iEventCount = 0; + s->LastTime = 0; + si->iEventCount = 0; + si->LastTime = 0; + si->pLog = s->pLog; + si->pLogEnd = s->pLogEnd; + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + } +} + +void CChatRoomDlg::OnInitDialog() +{ + SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this); + + m_cache = CContactCache::getContactCache(m_hContact); + m_cache->updateNick(); + m_cache->updateUIN(); + + si->hWnd = m_hwnd; + si->dat = this; + m_bIsAutosizingInput = IsAutoSplitEnabled(); + m_fLimitedUpdate = false; + m_iInputAreaHeight = -1; + + m_iSplitterY = m_pContainer->settings->iSplitterY; + if (m_bIsAutosizingInput) + m_iSplitterY = GetDefaultMinimumInputHeight(); + + CProxyWindow::add(this); + + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS) { + m_nTypeMode = PROTOTYPE_SELFTYPING_OFF; + SetTimer(m_hwnd, TIMERID_TYPE, 1000, NULL); + } + + m_pPanel.setActive(false); + m_pPanel.Configure(); + M.AddWindow(m_hwnd, m_hContact); + BroadCastContainer(m_pContainer, DM_REFRESHTABINDEX, 0, 0); + + m_log.SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)&reOleCallback); + m_log.SendMsg(EM_AUTOURLDETECT, 1, 0); + SetWindowLongPtr(GetDlgItem(m_hwnd, IDC_PANELSPLITTER), GWLP_WNDPROC, (LONG_PTR)SplitterSubclassProc); + TABSRMM_FireEvent(m_hContact, m_hwnd, MSG_WINDOW_EVT_OPENING, 0); + + m_log.SendMsg(EM_SETEVENTMASK, 0, m_log.SendMsg(EM_GETEVENTMASK, 0, 0) | ENM_LINK | ENM_MOUSEEVENTS | ENM_KEYEVENTS); + m_log.SendMsg(EM_LIMITTEXT, 0x7FFFFFFF, 0); + m_log.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3)); + + m_message.SendMsg(EM_SETEVENTMASK, 0, ENM_REQUESTRESIZE | ENM_MOUSEEVENTS | ENM_SCROLL | ENM_KEYEVENTS | ENM_CHANGE); + m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3)); + + m_pPanel.loadHeight(); + + if (PluginConfig.g_hMenuTrayUnread != 0 && m_hContact != 0 && m_szProto != NULL) + UpdateTrayMenu(0, m_wStatus, m_szProto, m_wszStatus, m_hContact, FALSE); + + DM_ThemeChanged(); + m_log.SendMsg(EM_HIDESELECTION, TRUE, 0); + + GetMYUIN(); + GetMyNick(); + + HWND hwndBtn = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 6, DPISCALEY_S(20), m_hwnd, (HMENU)IDC_TOGGLESIDEBAR, g_hInst, NULL); + CustomizeButton(hwndBtn); + SendMessage(hwndBtn, BUTTONSETASTHEMEDBTN, 1, 0); + SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)m_pContainer, 0); + SendMessage(hwndBtn, BUTTONSETASFLATBTN, FALSE, 0); + SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, TRUE, 0); + SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Expand or collapse the side bar"), BATF_UNICODE); + + DM_InitTip(); + BB_InitDlgButtons(); + SendMessage(m_hwnd, WM_CBD_LOADICONS, 0, 0); + + mir_subclassWindow(GetDlgItem(m_hwnd, IDC_SPLITTERX), SplitterSubclassProc); + mir_subclassWindow(GetDlgItem(m_hwnd, IDC_SPLITTERY), SplitterSubclassProc); + mir_subclassWindow(m_list.GetHwnd(), NicklistSubclassProc); + mir_subclassWindow(m_log.GetHwnd(), LogSubclassProc); + mir_subclassWindow(m_btnFilter.GetHwnd(), ButtonSubclassProc); + mir_subclassWindow(m_btnColor.GetHwnd(), ButtonSubclassProc); + mir_subclassWindow(m_btnBkColor.GetHwnd(), ButtonSubclassProc); + + mir_subclassWindow(m_message.GetHwnd(), MessageSubclassProc); + m_message.SendMsg(EM_SUBCLASSED, 0, 0); + + SendMessage(m_hwnd, GC_SETWNDPROPS, 0, 0); + SendMessage(m_hwnd, GC_UPDATESTATUSBAR, 0, 0); + SendMessage(m_hwnd, GC_UPDATETITLE, 0, 1); + + RECT rc; + SendMessage(m_pContainer->hwnd, DM_QUERYCLIENTAREA, 0, (LPARAM)&rc); + SetWindowPos(m_hwnd, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), 0); + ShowWindow(m_hwnd, SW_SHOW); + PostMessage(m_hwnd, GC_UPDATENICKLIST, 0, 0); + m_pContainer->hwndActive = m_hwnd; + TABSRMM_FireEvent(m_hContact, m_hwnd, MSG_WINDOW_EVT_OPEN, 0); +} + +void CChatRoomDlg::OnDestroy() +{ + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS) + if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON) + DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF); + + if (pcli->pfnGetEvent(si->hContact, 0)) + pcli->pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); + si->wState &= ~STATE_TALK; + si->hWnd = NULL; + si->dat = NULL; + si = NULL; + + TABSRMM_FireEvent(m_hContact, m_hwnd, MSG_WINDOW_EVT_CLOSING, 0); + + DM_FreeTheme(); + + UpdateTrayMenuState(this, FALSE); // remove me from the tray menu (if still there) + if (PluginConfig.g_hMenuTrayUnread) + DeleteMenu(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)m_hContact, MF_BYCOMMAND); + + if (m_hSmileyIcon) + DestroyIcon(m_hSmileyIcon); + + if (m_hwndTip) + DestroyWindow(m_hwndTip); + + int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd); + if (i >= 0) { + SendMessage(m_hwndParent, WM_USER + 100, 0, 0); // clean up tooltip + TabCtrl_DeleteItem(m_hwndParent, i); + BroadCastContainer(m_pContainer, DM_REFRESHTABINDEX, 0, 0); + m_iTabID = -1; + } + if (m_pWnd) { + delete m_pWnd; + m_pWnd = 0; + } + if (m_sbCustom) { + delete m_sbCustom; + m_sbCustom = 0; + } + + M.RemoveWindow(m_hwnd); + + TABSRMM_FireEvent(m_hContact, m_hwnd, MSG_WINDOW_EVT_CLOSE, 0); + + memset((void*)&m_pContainer->mOld, -1000, sizeof(MARGINS)); + PostMessage(m_pContainer->hwnd, WM_SIZE, 0, 1); + + if (m_pContainer->dwFlags & CNT_SIDEBAR) + m_pContainer->SideBar->removeSession(this); + mir_free(m_enteredText); + SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); +} + +void CChatRoomDlg::OnClick_OK(CCtrlButton*) +{ + if (GetSendButtonState(m_hwnd) == PBS_DISABLED) + return; + + MODULEINFO *mi = pci->MM_FindModule(si->pszModule); + if (mi == NULL) + return; + + ptrA pszRtf(Message_GetFromStream(m_message.GetHwnd())); + pci->SM_AddCommand(si->ptszID, si->pszModule, pszRtf); + + CMStringW ptszText(ptrW(mir_utf8decodeW(pszRtf))); + if (ptszText.IsEmpty()) + return; + + DoRtfToTags(ptszText, mi->nColorCount, mi->crColors); + ptszText.Trim(); + ptszText.Replace(L"%", L"%%"); + + if (mi->bAckMsg) { + Utils::enableDlgControl(m_hwnd, IDC_MESSAGE, false); + m_message.SendMsg(EM_SETREADONLY, TRUE, 0); + } + else SetDlgItemText(m_hwnd, IDC_MESSAGE, L""); + + Utils::enableDlgControl(m_hwnd, IDOK, false); + + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS) + if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON) + DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF); + + bool fSound = true; + if (ptszText[0] == '/' || si->iType == GCW_SERVER) + fSound = false; + pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_MESSAGE, NULL, ptszText, 0); + mi->idleTimeStamp = time(0); + mi->lastIdleCheck = 0; + pci->SM_BroadcastMessage(si->pszModule, GC_UPDATESTATUSBAR, 0, 1, TRUE); + if (m_pContainer) + if (fSound && !nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) + SkinPlaySound("ChatSent"); + + SetFocus(m_message.GetHwnd()); +} + +void CChatRoomDlg::OnClick_Filter(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + if (si->iLogFilterFlags == 0 && !si->bFilterEnabled) { + MessageBox(0, TranslateT("The filter cannot be enabled, because there are no event types selected either global or for this chat room"), TranslateT("Event filter error"), MB_OK); + si->bFilterEnabled = 0; + } + else si->bFilterEnabled = !si->bFilterEnabled; + + m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, + (LPARAM)(si->bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0); + + if (si->bFilterEnabled && M.GetByte(CHAT_MODULE, "RightClickFilter", 0) == 0) { + SendMessage(m_hwnd, GC_SHOWFILTERMENU, 0, 0); + return; + } + SendMessage(m_hwnd, GC_REDRAWLOG, 0, 0); + SendMessage(m_hwnd, GC_UPDATETITLE, 0, 0); + db_set_b(si->hContact, CHAT_MODULE, "FilterEnabled", (BYTE)si->bFilterEnabled); +} + +void CChatRoomDlg::OnClick_History(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + MODULEINFO *pInfo = pci->MM_FindModule(si->pszModule); + if (ServiceExists("MSP/HTMLlog/ViewLog") && strstr(si->pszModule, "IRC")) { + char szName[MAX_PATH]; + WideCharToMultiByte(CP_ACP, 0, si->ptszName, -1, szName, MAX_PATH, 0, 0); + szName[MAX_PATH - 1] = 0; + CallService("MSP/HTMLlog/ViewLog", (WPARAM)si->pszModule, (LPARAM)szName); + } + else if (pInfo) + ShellExecute(m_hwnd, NULL, pci->GetChatLogsFilename(si, 0), NULL, NULL, SW_SHOW); +} + +void CChatRoomDlg::OnClick_ShowNickList(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + if (si->iType == GCW_SERVER) + return; + + si->bNicklistEnabled = !si->bNicklistEnabled; + + SendMessage(m_hwnd, WM_SIZE, 0, 0); + if (CSkin::m_skinEnabled) + InvalidateRect(m_hwnd, NULL, TRUE); + PostMessage(m_hwnd, GC_SCROLLTOBOTTOM, 0, 0); +} + +void CChatRoomDlg::OnClick_ChanMgr(CCtrlButton *pButton) +{ + if (pButton->Enabled()) + pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_CHANMGR, NULL, NULL, 0); +} + +void CChatRoomDlg::OnClick_BIU(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; + cf.dwEffects = 0; + + if (IsDlgButtonChecked(m_hwnd, IDC_BOLD)) + cf.dwEffects |= CFE_BOLD; + if (IsDlgButtonChecked(m_hwnd, IDC_ITALICS)) + cf.dwEffects |= CFE_ITALIC; + if (IsDlgButtonChecked(m_hwnd, IDC_UNDERLINE)) + cf.dwEffects |= CFE_UNDERLINE; + + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); +} + +void CChatRoomDlg::OnClick_Color(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwEffects = 0; + + if (IsDlgButtonChecked(m_hwnd, IDC_COLOR)) { + if (M.GetByte(CHAT_MODULE, "RightClickFilter", 0) == 0) + SendMessage(m_hwnd, GC_SHOWCOLORCHOOSER, 0, IDC_COLOR); + else if (si->bFGSet) { + cf.dwMask = CFM_COLOR; + cf.crTextColor = pci->MM_FindModule(si->pszModule)->crColors[si->iFG]; + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + } + } + else { + COLORREF cr; + LoadLogfont(MSGFONTID_MESSAGEAREA, NULL, &cr, FONTMODULE); + cf.dwMask = CFM_COLOR; + cf.crTextColor = cr; + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + } +} + +void CChatRoomDlg::OnClick_BkColor(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwEffects = 0; + + if (IsDlgButtonChecked(m_hwnd, IDC_BKGCOLOR)) { + if (M.GetByte(CHAT_MODULE, "RightClickFilter", 0) == 0) + SendMessage(m_hwnd, GC_SHOWCOLORCHOOSER, 0, IDC_BKGCOLOR); + else if (si->bBGSet) { + cf.dwMask = CFM_BACKCOLOR; + cf.crBackColor = pci->MM_FindModule(si->pszModule)->crColors[si->iBG]; + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + } + } + else { + cf.dwMask = CFM_BACKCOLOR; + cf.crBackColor = (COLORREF)M.GetDword(FONTMODULE, "inputbg", SRMSGDEFSET_BKGCOLOUR); + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + } +} + +void CChatRoomDlg::OnChange_Message(CCtrlEdit*) +{ + if (m_pContainer->hwndActive == m_hwnd) + UpdateReadChars(); + m_dwLastActivity = GetTickCount(); + m_pContainer->dwLastActivity = m_dwLastActivity; + m_btnOk.SendMsg(BUTTONSETASNORMAL, GetRichTextLength(m_message.GetHwnd()) != 0, 0); + m_btnOk.Enable(GetRichTextLength(m_message.GetHwnd()) != 0); + + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS) { + if (!(GetKeyState(VK_CONTROL) & 0x8000)) { + m_nLastTyping = GetTickCount(); + if (GetWindowTextLength(m_message.GetHwnd())) { + if (m_nTypeMode == PROTOTYPE_SELFTYPING_OFF) { + if (!(m_dwFlags & MWF_INITMODE)) + DM_NotifyTyping(PROTOTYPE_SELFTYPING_ON); + } + } + else if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON) + DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF); + } + } +} + +void CChatRoomDlg::OnDblClick_List(CCtrlListBox*) +{ + TVHITTESTINFO hti; + hti.pt.x = (short)LOWORD(GetMessagePos()); + hti.pt.y = (short)HIWORD(GetMessagePos()); + ScreenToClient(m_list.GetHwnd(), &hti.pt); + + int item = LOWORD(m_list.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y))); + USERINFO *ui = pci->UM_FindUserFromIndex(si->pUsers, item); + if (ui == nullptr) + return; + + bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; + if (g_Settings.bDoubleClick4Privat ? bShift : !bShift) { + int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0)); + CMStringW tszName(ui->pszNick); + if (selStart == 0) + tszName.AppendChar(':'); + tszName.AppendChar(' '); + + m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString()); + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + SetFocus(m_message.GetHwnd()); + } + else pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_PRIVMESS, ui, NULL, 0); +} + +INT_PTR CChatRoomDlg::DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (si == NULL && (uMsg == WM_ACTIVATE || uMsg == WM_SETFOCUS)) + return 0; + + POINT pt, tmp, cur; + RECT rc; + MODULEINFO *pInfo; + + switch (uMsg) { + case WM_SETFOCUS: + if (CMimAPI::m_shutDown) + break; + + UpdateWindowState(WM_SETFOCUS); + SetFocus(m_message.GetHwnd()); + return 1; + + case WM_TIMECHANGE: + PostMessage(m_hwnd, GC_REDRAWLOG, 0, 0); + break; + + case WM_CBD_LOADICONS: + Srmm_UpdateToolbarIcons(m_hwnd); + return 0; + + case GC_SETWNDPROPS: + pInfo = si ? pci->MM_FindModule(si->pszModule) : NULL; + if (pInfo) { + m_btnBold.Enable(pInfo->bBold); + m_btnItalic.Enable(pInfo->bItalics); + m_btnUnderline.Enable(pInfo->bUnderline); + m_btnColor.Enable(pInfo->bColor); + m_btnBkColor.Enable(pInfo->bBkgColor); + if (si->iType == GCW_CHATROOM) + m_btnChannelMgr.Enable(pInfo->bChanMgr); + } + m_log.SendMsg(EM_SETBKGNDCOLOR, 0, M.GetDword(FONTMODULE, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR)); + + DM_InitRichEdit(); + m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0); + + m_list.SetItemHeight(0, g_Settings.iNickListFontHeight); + InvalidateRect(m_list.GetHwnd(), NULL, TRUE); + + m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(si->bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0); + SendMessage(m_hwnd, WM_SIZE, 0, 0); + SendMessage(m_hwnd, GC_REDRAWLOG2, 0, 0); + break; + + case DM_UPDATETITLE: + case GC_UPDATETITLE: + if (!m_bWasDeleted) { + m_wStatus = si->wStatus; + + const wchar_t *szNick = m_cache->getNick(); + if (mir_wstrlen(szNick) > 0) { + if (M.GetByte("cuttitle", 0)) + CutContactName(szNick, m_wszTitle, _countof(m_wszTitle)); + else + wcsncpy_s(m_wszTitle, szNick, _TRUNCATE); + } + + wchar_t szTemp[100]; + HICON hIcon = 0; + + switch (si->iType) { + case GCW_CHATROOM: + hIcon = Skin_LoadProtoIcon(si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus); + mir_snwprintf(szTemp, + (si->nUsersInNicklist == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"), + szNick, si->nUsersInNicklist, si->bFilterEnabled ? TranslateT(", event filter active") : L""); + break; + case GCW_PRIVMESS: + hIcon = Skin_LoadProtoIcon(si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus); + if (si->nUsersInNicklist == 1) + mir_snwprintf(szTemp, TranslateT("%s: message session"), szNick); + else + mir_snwprintf(szTemp, TranslateT("%s: message session (%u users)"), szNick, si->nUsersInNicklist); + break; + case GCW_SERVER: + mir_snwprintf(szTemp, L"%s: Server", szNick); + hIcon = LoadIconEx("window"); + break; + } + + if (m_pWnd) { + m_pWnd->updateTitle(m_wszTitle); + m_pWnd->updateIcon(hIcon); + } + m_hTabStatusIcon = hIcon; + + if (lParam) + m_hTabIcon = m_hTabStatusIcon; + + if (m_cache->getStatus() != m_cache->getOldStatus()) { + wcsncpy_s(m_wszStatus, pcli->pfnGetStatusModeDescription(m_wStatus, 0), _TRUNCATE); + + TCITEM item = {}; + item.mask = TCIF_TEXT; + item.pszText = m_wszTitle; + TabCtrl_SetItem(m_hwndParent, m_iTabID, &item); + } + SetWindowText(m_hwnd, szTemp); + if (m_pContainer->hwndActive == m_hwnd) { + SendMessage(m_pContainer->hwnd, DM_UPDATETITLE, (WPARAM)m_hwnd, 1); + SendMessage(m_hwnd, GC_UPDATESTATUSBAR, 0, 0); + } + } + break; + + case GC_UPDATESTATUSBAR: + if (m_bWasDeleted) + return 0; + + if (m_pContainer->hwndActive != m_hwnd || m_pContainer->hwndStatus == 0 || CMimAPI::m_shutDown || m_wszStatusBar[0]) + break; + + if (si->pszModule != NULL) { + wchar_t szFinalStatusBarText[512]; + + //Mad: strange rare crash here... + MODULEINFO *mi = pci->MM_FindModule(si->pszModule); + if (!mi) + break; + + if (!mi->ptszModDispName) + break; + + int x = 12; + x += GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->hwndStatus, WM_GETFONT, 0, 0), TRUE); + x += GetSystemMetrics(SM_CXSMICON); + + if (m_pPanel.isActive()) { + time_t now = time(0); + DWORD diff = (now - mi->idleTimeStamp) / 60; + + if ((diff >= 1 && diff != mi->lastIdleCheck) || lParam) { + mi->lastIdleCheck = diff; + if (diff == 0) + mi->tszIdleMsg[0] = 0; + else if (diff > 59) { + DWORD hours = diff / 60; + DWORD minutes = diff % 60; + mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"), hours, hours > 1 ? + TranslateT("hours") : TranslateT("hour"), + minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute")); + } + else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute")); + } + mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg); + } + else { + if (si->ptszStatusbarText) + mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, si->ptszStatusbarText); + else + wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE); + } + SendMessage(m_pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText); + UpdateStatusBar(); + m_pPanel.Invalidate(); + if (m_pWnd) + m_pWnd->Invalidate(); + return TRUE; + } + break; + + case WM_SIZE: + if (m_ipFieldHeight == 0) + m_ipFieldHeight = CInfoPanel::m_ipConfig.height1; + + if (wParam == SIZE_MAXIMIZED) + PostMessage(m_hwnd, GC_SCROLLTOBOTTOM, 0, 0); + + if (!IsIconic(m_hwnd)) { + int panelHeight = m_pPanel.getHeight() + 1; + + GetClientRect(m_hwnd, &rc); + int cx = rc.right; + + CTabBaseDlg::DlgProc(uMsg, 0, 0); // call basic window resizer + + BB_SetButtonsPos(); + + rc.left = panelHeight <= CInfoPanel::LEFT_OFFSET_LOGO ? panelHeight : CInfoPanel::LEFT_OFFSET_LOGO; + rc.right = cx; + rc.top = 1; + rc.bottom = (panelHeight > CInfoPanel::DEGRADE_THRESHOLD ? rc.top + m_ipFieldHeight - 2 : panelHeight - 1); + m_rcNick = rc; + + rc.left = panelHeight <= CInfoPanel::LEFT_OFFSET_LOGO ? panelHeight : CInfoPanel::LEFT_OFFSET_LOGO; + rc.right = cx; + rc.bottom = panelHeight - 2; + rc.top = m_rcNick.bottom + 1; + m_rcUIN = rc; + + if (m_hwndIEView || m_hwndHPP) + ResizeIeView(); + DetermineMinHeight(); + } + return 0; + + case GC_REDRAWWINDOW: + InvalidateRect(m_hwnd, NULL, TRUE); + break; + + case GC_REDRAWLOG: + si->LastTime = 0; + if (si->pLog) { + LOGINFO * pLog = si->pLog; + if (si->iEventCount > 60) { + int index = 0; + while (index < 59) { + if (pLog->next == NULL) + break; + pLog = pLog->next; + if ((si->iType != GCW_CHATROOM && si->iType != GCW_PRIVMESS) || !si->bFilterEnabled || (si->iLogFilterFlags&pLog->iType) != 0) + index++; + } + StreamInEvents(pLog, si, TRUE); + mir_forkthread(phase2, si); + } + else StreamInEvents(si->pLogEnd, si, TRUE); + } + else SendMessage(m_hwnd, GC_CONTROL_MSG, WINDOW_CLEARLOG, 0); + break; + + case GC_REDRAWLOG2: + si->LastTime = 0; + if (si->pLog) + StreamInEvents(si->pLogEnd, si, TRUE); + break; + + case GC_ADDLOG: + if (PluginConfig.m_bUseDividers) { + if (PluginConfig.m_bDividersUsePopupConfig) { + if (!MessageWindowOpened(0, (LPARAM)m_hwnd)) + DM_AddDivider(); + } + else { + bool bInactive = (GetForegroundWindow() != m_pContainer->hwnd || GetActiveWindow() != m_pContainer->hwnd); + if (bInactive) + DM_AddDivider(); + else if (m_pContainer->hwndActive != m_hwnd) + DM_AddDivider(); + } + } + + if (si->pLogEnd) + StreamInEvents(si->pLog, si, FALSE); + else + SendMessage(m_hwnd, GC_CONTROL_MSG, WINDOW_CLEARLOG, 0); + break; + + case DM_TYPING: + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS) { + int preTyping = m_nTypeSecs != 0; + m_nTypeSecs = (int)lParam > 0 ? (int)lParam : 0; + + if (m_nTypeSecs) + m_bShowTyping = 0; + + SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, preTyping); + } + return TRUE; + + case WM_CTLCOLORLISTBOX: + SetBkColor((HDC)wParam, g_Settings.crUserListBGColor); + return (INT_PTR)pci->hListBkgBrush; + + case WM_MEASUREITEM: + { + MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; + if (mis->CtlType == ODT_MENU) { + if (m_pPanel.isHovered()) { + mis->itemHeight = 0; + mis->itemWidth = 6; + return TRUE; + } + return Menu_MeasureItem(lParam); + } + mis->itemHeight = g_Settings.iNickListFontHeight; + } + return TRUE; + + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; + if (dis->CtlType == ODT_MENU) { + if (m_pPanel.isHovered()) { + DrawMenuItem(dis, (HICON)dis->itemData, 0); + return TRUE; + } + return Menu_DrawItem(lParam); + } + + if (dis->CtlID == IDC_LIST) { + int x_offset = 0; + int index = dis->itemID; + + USERINFO *ui = pci->UM_FindUserFromIndex(si->pUsers, index); + if (ui == NULL) + return TRUE; + + int height = dis->rcItem.bottom - dis->rcItem.top; + if (height & 1) + height++; + int offset = (height == 10) ? 0 : height / 2; + + HICON hIcon = pci->SM_GetStatusIcon(si, ui); + HFONT hFont = g_Settings.UserListFonts[ui->iStatusEx]; + HFONT hOldFont = (HFONT)SelectObject(dis->hDC, hFont); + SetBkMode(dis->hDC, TRANSPARENT); + + int nickIndex = 0; + for (int i = 0; i < STATUSICONCOUNT; i++) { + if (hIcon == pci->hIcons[ICON_STATUS0 + i]) { + nickIndex = i; + break; + } + } + + if (dis->itemState & ODS_SELECTED) { + FillRect(dis->hDC, &dis->rcItem, g_Settings.SelectionBGBrush); + SetTextColor(dis->hDC, g_Settings.nickColors[6]); + } + else { + FillRect(dis->hDC, &dis->rcItem, pci->hListBkgBrush); + if (g_Settings.bColorizeNicks && nickIndex != 0) + SetTextColor(dis->hDC, g_Settings.nickColors[nickIndex - 1]); + else + SetTextColor(dis->hDC, g_Settings.UserListColors[ui->iStatusEx]); + } + x_offset = 2; + + if (g_Settings.bShowContactStatus && g_Settings.bContactStatusFirst && ui->ContactStatus) { + HICON icon = Skin_LoadProtoIcon(si->pszModule, ui->ContactStatus); + DrawIconEx(dis->hDC, x_offset, dis->rcItem.top + offset - 8, icon, 16, 16, 0, NULL, DI_NORMAL); + IcoLib_ReleaseIcon(icon); + x_offset += 18; + } + + if (g_Settings.bClassicIndicators) { + char szTemp[3]; + szTemp[1] = 0; + szTemp[0] = szIndicators[nickIndex]; + if (szTemp[0]) { + SIZE szUmode; + GetTextExtentPoint32A(dis->hDC, szTemp, 1, &szUmode); + TextOutA(dis->hDC, x_offset, dis->rcItem.top, szTemp, 1); + x_offset += szUmode.cx + 2; + } + else x_offset += 8; + } + else { + DrawIconEx(dis->hDC, x_offset, dis->rcItem.top + offset - 5, hIcon, 10, 10, 0, NULL, DI_NORMAL); + x_offset += 12; + } + + if (g_Settings.bShowContactStatus && !g_Settings.bContactStatusFirst && ui->ContactStatus) { + HICON icon = Skin_LoadProtoIcon(si->pszModule, ui->ContactStatus); + DrawIconEx(dis->hDC, x_offset, dis->rcItem.top + offset - 8, icon, 16, 16, 0, NULL, DI_NORMAL); + IcoLib_ReleaseIcon(icon); + x_offset += 18; + } + + SIZE sz; + if (m_iSearchItem != -1 && m_iSearchItem == index && m_wszSearch[0]) { + COLORREF clr_orig = GetTextColor(dis->hDC); + GetTextExtentPoint32(dis->hDC, ui->pszNick, (int)mir_wstrlen(m_wszSearch), &sz); + SetTextColor(dis->hDC, RGB(250, 250, 0)); + TextOut(dis->hDC, x_offset, (dis->rcItem.top + dis->rcItem.bottom - sz.cy) / 2, ui->pszNick, (int)mir_wstrlen(m_wszSearch)); + SetTextColor(dis->hDC, clr_orig); + x_offset += sz.cx; + TextOut(dis->hDC, x_offset, (dis->rcItem.top + dis->rcItem.bottom - sz.cy) / 2, ui->pszNick + mir_wstrlen(m_wszSearch), int(mir_wstrlen(ui->pszNick) - mir_wstrlen(m_wszSearch))); + } + else { + GetTextExtentPoint32(dis->hDC, ui->pszNick, (int)mir_wstrlen(ui->pszNick), &sz); + TextOut(dis->hDC, x_offset, (dis->rcItem.top + dis->rcItem.bottom - sz.cy) / 2, ui->pszNick, (int)mir_wstrlen(ui->pszNick)); + SelectObject(dis->hDC, hOldFont); + } + return TRUE; + } + } + break; + + case WM_CONTEXTMENU: + { + DWORD idFrom = GetDlgCtrlID((HWND)wParam); + if (idFrom >= MIN_CBUTTONID && idFrom <= MAX_CBUTTONID) + Srmm_ClickToolbarIcon(m_hContact, idFrom, (HWND)wParam, 1); + } + break; + + case GC_UPDATENICKLIST: + { + int i = m_list.SendMsg(LB_GETTOPINDEX, 0, 0); + m_list.SendMsg(LB_SETCOUNT, si->nUsersInNicklist, 0); + m_list.SendMsg(LB_SETTOPINDEX, i, 0); + SendMessage(m_hwnd, GC_UPDATETITLE, 0, 0); + } + break; + + case GC_CONTROL_MSG: + switch (wParam) { + case SESSION_OFFLINE: + SendMessage(m_hwnd, GC_UPDATESTATUSBAR, 0, 0); + SendMessage(si->hWnd, GC_UPDATENICKLIST, 0, 0); + return TRUE; + + case SESSION_ONLINE: + SendMessage(m_hwnd, GC_UPDATESTATUSBAR, 0, 0); + return TRUE; + + case WINDOW_HIDDEN: + SendMessage(m_hwnd, GC_CLOSEWINDOW, 0, 1); + return TRUE; + + case WINDOW_CLEARLOG: + SetDlgItemText(m_hwnd, IDC_LOG, L""); + return TRUE; + + case SESSION_TERMINATE: + if (pcli->pfnGetEvent(si->hContact, 0)) + pcli->pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); + + si->wState &= ~STATE_TALK; + m_bWasDeleted = true; + db_set_w(si->hContact, si->pszModule, "ApparentMode", 0); + SendMessage(m_hwnd, GC_CLOSEWINDOW, 0, lParam == 2 ? lParam : 1); + return TRUE; + + case WINDOW_MINIMIZE: + ShowWindow(m_hwnd, SW_MINIMIZE); +LABEL_SHOWWINDOW: + SendMessage(m_hwnd, WM_SIZE, 0, 0); + SendMessage(m_hwnd, GC_REDRAWLOG, 0, 0); + SendMessage(m_hwnd, GC_UPDATENICKLIST, 0, 0); + SendMessage(m_hwnd, GC_UPDATESTATUSBAR, 0, 0); + ShowWindow(m_hwnd, SW_SHOW); + SendMessage(m_hwnd, WM_SIZE, 0, 0); + SetForegroundWindow(m_hwnd); + return TRUE; + + case WINDOW_MAXIMIZE: + ShowWindow(m_hwnd, SW_MAXIMIZE); + goto LABEL_SHOWWINDOW; + + case SESSION_INITDONE: + if (M.GetByte(CHAT_MODULE, "PopupOnJoin", 0) != 0) + return TRUE; + + // fall through + case WINDOW_VISIBLE: + if (IsIconic(m_hwnd)) + ShowWindow(m_hwnd, SW_NORMAL); + goto LABEL_SHOWWINDOW; + } + break; + + case DM_SPLITTERMOVED: + RECT rcLog; + { + GetWindowRect(m_log.GetHwnd(), &rcLog); + if ((HWND)lParam == GetDlgItem(m_hwnd, IDC_SPLITTERX)) { + GetClientRect(m_hwnd, &rc); + pt.x = wParam, pt.y = 0; + ScreenToClient(m_hwnd, &pt); + + int iSplitterX = rc.right - pt.x + 1; + if (iSplitterX < 35) + iSplitterX = 35; + if (iSplitterX > rc.right - rc.left - 35) + iSplitterX = rc.right - rc.left - 35; + m_pContainer->settings->iSplitterX = iSplitterX; + SendMessage(m_hwnd, WM_SIZE, 0, 0); + } + else if ((HWND)lParam == GetDlgItem(m_hwnd, IDC_SPLITTERY) || lParam == -1) { + GetClientRect(m_hwnd, &rc); + rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30); + pt.x = 0, pt.y = wParam; + ScreenToClient(m_hwnd, &pt); + + m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(1); + if (m_iSplitterY < DPISCALEY_S(23)) + m_iSplitterY = DPISCALEY_S(23); + if (m_iSplitterY > rc.bottom - rc.top - DPISCALEY_S(40)) + m_iSplitterY = rc.bottom - rc.top - DPISCALEY_S(40); + m_pContainer->settings->iSplitterY = m_iSplitterY; + UpdateToolbarBG(); + SendMessage(m_hwnd, WM_SIZE, 0, 0); + } + else if ((HWND)lParam == GetDlgItem(m_hwnd, IDC_PANELSPLITTER)) { + pt.x = 0, pt.y = wParam; + ScreenToClient(m_hwnd, &pt); + GetClientRect(m_log.GetHwnd(), &rc); + if ((pt.y + 2 >= MIN_PANELHEIGHT + 2) && (pt.y + 2 < 100) && (pt.y + 2 < rc.bottom - 30)) + m_pPanel.setHeight(pt.y + 2); + RedrawWindow(m_hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + if (M.isAero()) + InvalidateRect(m_hwndParent, NULL, FALSE); + SendMessage(m_hwnd, WM_SIZE, DM_SPLITTERMOVED, 0); + break; + } + } + break; + + case GC_FIREHOOK: + if (lParam) { + GCHOOK *gch = (GCHOOK*)lParam; + NotifyEventHooks(pci->hSendEvent, 0, (WPARAM)gch); + if (gch->pDest) { + mir_free((void*)gch->pDest->ptszID); + mir_free((void*)gch->pDest->pszModule); + mir_free(gch->pDest); + } + mir_free(gch->ptszText); + mir_free(gch->ptszUID); + mir_free(gch); + } + break; + + case GC_CHANGEFILTERFLAG: + if (si->iLogFilterFlags == 0 && si->bFilterEnabled) + SendMessage(m_hwnd, WM_COMMAND, IDC_FILTER, 0); + break; + + case GC_SHOWFILTERMENU: + m_hwndFilter = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_FILTER), m_pContainer->hwnd, FilterWndProc, (LPARAM)si); + TranslateDialogDefault(m_hwndFilter); + + RECT rcFilter; + GetClientRect(m_hwndFilter, &rcFilter); + GetWindowRect(m_log.GetHwnd(), &rcLog); + + pt.x = rcLog.right; pt.y = rcLog.bottom; + ScreenToClient(m_pContainer->hwnd, &pt); + + SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); + break; + + case GC_SHOWCOLORCHOOSER: + pci->ColorChooser(si, lParam == IDC_COLOR, m_hwnd, m_message.GetHwnd(), GetDlgItem(m_hwnd, lParam)); + break; + + case GC_SCROLLTOBOTTOM: + DM_ScrollToBottom(wParam, lParam); + return 0; + + case WM_TIMER: + if (wParam == TIMERID_FLASHWND) + if (m_bCanFlashTab) + FlashTab(true); + + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS && wParam == TIMERID_TYPE) + DM_Typing(false); + break; + + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_ACTIVE) { + m_pContainer->hwndSaved = 0; + break; + } + // fall through + + case WM_MOUSEACTIVATE: + UpdateWindowState(WM_ACTIVATE); + return 1; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->code) { + case EN_MSGFILTER: + bool isShift, isCtrl, isMenu; + { + UINT msg = ((MSGFILTER *)lParam)->msg; + WPARAM wp = ((MSGFILTER *)lParam)->wParam; + LPARAM lp = ((MSGFILTER *)lParam)->lParam; + + KbdState(isShift, isCtrl, isMenu); + + MSG message; + message.hwnd = m_hwnd; + message.message = msg; + message.lParam = lp; + message.wParam = wp; + + if (msg == WM_SYSKEYUP) { + if (wp == VK_MENU) + if (!m_fkeyProcessed && !(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000) && !(lp & (1 << 24))) + m_pContainer->MenuBar->autoShow(); + + return _dlgReturn(m_hwnd, 0); + } + + if (msg == WM_MOUSEMOVE) { + GetCursorPos(&pt); + DM_DismissTip(pt); + m_pPanel.trackMouse(pt); + break; + } + if (msg == WM_KEYDOWN) { + if ((wp == VK_INSERT && isShift && !isCtrl && !isMenu) || (wp == 'V' && !isShift && !isMenu && isCtrl)) { + m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0); + ((MSGFILTER*)lParam)->msg = WM_NULL; + ((MSGFILTER*)lParam)->wParam = 0; + ((MSGFILTER*)lParam)->lParam = 0; + return _dlgReturn(m_hwnd, 1); + } + } + + if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) + m_pContainer->MenuBar->Cancel(); + + if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && !(GetKeyState(VK_RMENU) & 0x8000)) { + if (DM_GenericHotkeysCheck(&message)) { + m_fkeyProcessed = true; + return _dlgReturn(m_hwnd, 1); + } + + LRESULT mim_hotkey_check = CallService(MS_HOTKEY_CHECK, (WPARAM)&message, (LPARAM)(TABSRMM_HK_SECTION_GC)); + if (mim_hotkey_check) + m_fkeyProcessed = true; + + switch (mim_hotkey_check) { + case TABSRMM_HK_CHANNELMGR: + OnClick_ChanMgr(&m_btnChannelMgr); + return _dlgReturn(m_hwnd, 1); + case TABSRMM_HK_FILTERTOGGLE: + OnClick_Filter(&m_btnFilter); + InvalidateRect(m_btnFilter.GetHwnd(), NULL, TRUE); + return _dlgReturn(m_hwnd, 1); + case TABSRMM_HK_LISTTOGGLE: + OnClick_ShowNickList(&m_btnNickList); + return _dlgReturn(m_hwnd, 1); + case TABSRMM_HK_MUC_SHOWSERVER: + if (si->iType != GCW_SERVER) + pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_MESSAGE, NULL, L"/servershow", 0); + return _dlgReturn(m_hwnd, 1); + } + } + + if (msg == WM_KEYDOWN && ((NMHDR*)lParam)->idFrom != IDC_MESSAGE) { + if ((wp == VK_NEXT && isCtrl && !isShift) || (wp == VK_TAB && isCtrl && !isShift)) // CTRL-TAB (switch tab/window) + SendMessage(m_pContainer->hwnd, DM_SELECTTAB, DM_SELECT_NEXT, 0); + else if ((wp == VK_PRIOR && isCtrl && !isShift) || (wp == VK_TAB && isCtrl && isShift)) // CTRL_SHIFT-TAB (switch tab/window) + SendMessage(m_pContainer->hwnd, DM_SELECTTAB, DM_SELECT_PREV, 0); + } + + if (msg == WM_KEYDOWN && wp == VK_TAB) { + if (((NMHDR*)lParam)->idFrom == IDC_LOG) { + SetFocus(m_message.GetHwnd()); + return _dlgReturn(m_hwnd, 1); + } + } + + if (((LPNMHDR)lParam)->idFrom == IDC_LOG && ((MSGFILTER *)lParam)->msg == WM_RBUTTONUP) { + CHARRANGE sel, all = { 0, -1 }; + + pt.x = LOWORD(((ENLINK*)lParam)->lParam), pt.y = HIWORD(((ENLINK*)lParam)->lParam); + ClientToScreen(((LPNMHDR)lParam)->hwndFrom, &pt); + + // fixing stuff for searches + wchar_t *pszWord = (wchar_t*)_alloca(8192); + pszWord[0] = '\0'; + POINTL ptl = { pt.x, pt.y }; + ScreenToClient(m_log.GetHwnd(), (LPPOINT)&ptl); + int iCharIndex = m_log.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl); + if (iCharIndex < 0) + break; + + int start = m_log.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex); + int end = m_log.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex); + + if (end - start > 0) { + static char szTrimString[] = ":;,.!?\'\"><()[]- \r\n"; + + CHARRANGE cr; + cr.cpMin = start; + cr.cpMax = end; + + TEXTRANGE tr = { 0 }; + tr.chrg = cr; + tr.lpstrText = (wchar_t*)pszWord; + int iRes = m_log.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); + + if (iRes > 0) { + size_t iLen = mir_wstrlen(pszWord) - 1; + while (strchr(szTrimString, pszWord[iLen])) { + pszWord[iLen] = '\0'; + iLen--; + } + } + } + + HMENU hMenu = 0; + UINT uID = CreateGCMenu(m_hwnd, &hMenu, 1, pt, si, NULL, pszWord); + switch (uID) { + case 0: + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + case ID_COPYALL: + SendMessage(((LPNMHDR)lParam)->hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel); + SendMessage(((LPNMHDR)lParam)->hwndFrom, EM_EXSETSEL, 0, (LPARAM)&all); + SendMessage(((LPNMHDR)lParam)->hwndFrom, WM_COPY, 0, 0); + SendMessage(((LPNMHDR)lParam)->hwndFrom, EM_EXSETSEL, 0, (LPARAM)&sel); + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + case ID_CLEARLOG: + ClearLog(); + break; + + case ID_SEARCH_GOOGLE: + if (pszWord[0]) + Utils_OpenUrlW(CMStringW(FORMAT, L"http://www.google.com/search?q=%s", pszWord)); + + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + case ID_SEARCH_WIKIPEDIA: + if (pszWord[0]) + Utils_OpenUrlW(CMStringW(FORMAT, L"http://en.wikipedia.org/wiki/%s", pszWord)); + + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + default: + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_LOGMENU, NULL, NULL, (LPARAM)uID); + break; + } + + DestroyGCMenu(&hMenu, 5); + } + } + break; + + case EN_REQUESTRESIZE: + if (((LPNMHDR)lParam)->idFrom == IDC_MESSAGE) + DM_HandleAutoSizeRequest((REQRESIZE *)lParam); + break; + + case EN_LINK: + if (((LPNMHDR)lParam)->idFrom == IDC_LOG) { + switch (((ENLINK*)lParam)->msg) { + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + CHARRANGE sel; + SendMessage(((LPNMHDR)lParam)->hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) { + UINT msg = ((ENLINK*)lParam)->msg; + m_pContainer->MenuBar->Cancel(); + + TEXTRANGE tr; + tr.lpstrText = NULL; + tr.chrg = ((ENLINK*)lParam)->chrg; + tr.lpstrText = (wchar_t*)mir_alloc(sizeof(wchar_t) * (tr.chrg.cpMax - tr.chrg.cpMin + 2)); + SendMessage(((LPNMHDR)lParam)->hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&tr); + + BOOL isLink = IsStringValidLink(tr.lpstrText); + if (isLink) // handled by core + break; + + // clicked a nick name + if (g_Settings.bClickableNicks) { + if (msg == WM_RBUTTONDOWN) { + HMENU hMenu = 0; + USERINFO uiNew; + for (USERINFO *ui = si->pUsers; ui; ui = ui->next) { + if (mir_wstrcmp(ui->pszNick, tr.lpstrText)) + continue; + + pt.x = (short)LOWORD(((ENLINK*)lParam)->lParam); + pt.y = (short)HIWORD(((ENLINK*)lParam)->lParam); + ClientToScreen(((NMHDR*)lParam)->hwndFrom, &pt); + memcpy(&uiNew, ui, sizeof(USERINFO)); + UINT uID = CreateGCMenu(m_hwnd, &hMenu, 0, pt, si, uiNew.pszUID, uiNew.pszNick); + switch (uID) { + case 0: + break; + + case ID_MESS: + pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_PRIVMESS, ui, NULL, 0); + break; + + default: + pci->DoEventHookAsync(m_hwnd, si->ptszID, si->pszModule, GC_USER_NICKLISTMENU, ui, NULL, (LPARAM)uID); + break; + } + DestroyGCMenu(&hMenu, 1); + return TRUE; + } + return TRUE; + } + + if (msg == WM_LBUTTONUP) { + CHARRANGE chr; + m_message.SendMsg(EM_EXGETSEL, 0, (LPARAM)&chr); + + wchar_t tszAplTmpl[] = L"%s:"; + size_t bufSize = mir_wstrlen(tr.lpstrText) + mir_wstrlen(tszAplTmpl) + 3; + wchar_t *tszTmp = (wchar_t*)mir_alloc(bufSize * sizeof(wchar_t)), *tszAppeal = tszTmp; + + TEXTRANGE tr2; + tr2.lpstrText = (LPTSTR)mir_alloc(sizeof(wchar_t) * 2); + if (chr.cpMin) { + // prepend nick with space if needed + tr2.chrg.cpMin = chr.cpMin - 1; + tr2.chrg.cpMax = chr.cpMin; + m_message.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr2); + if (!iswspace(*tr2.lpstrText)) + *tszTmp++ = ' '; + mir_wstrcpy(tszTmp, tr.lpstrText); + } + else // in the beginning of the message window + mir_snwprintf(tszAppeal, bufSize, tszAplTmpl, tr.lpstrText); + + size_t st = mir_wstrlen(tszAppeal); + if (chr.cpMax != -1) { + tr2.chrg.cpMin = chr.cpMax; + tr2.chrg.cpMax = chr.cpMax + 1; + // if there is no space after selection, + // or there is nothing after selection at all... + if (!m_message.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr2) || !iswspace(*tr2.lpstrText)) { + tszAppeal[st++] = ' '; + tszAppeal[st++] = '\0'; + } + } + else { + tszAppeal[st++] = ' '; + tszAppeal[st++] = '\0'; + } + m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszAppeal); + mir_free((void*)tr2.lpstrText); + mir_free((void*)tszAppeal); + } + } + SetFocus(m_message.GetHwnd()); + mir_free(tr.lpstrText); + return TRUE; + } + } + break; + } + break; + } + break; + + case WM_LBUTTONDOWN: + GetCursorPos(&tmp); + if (!m_pPanel.isHovered()) { + cur.x = (SHORT)tmp.x; + cur.y = (SHORT)tmp.y; + SendMessage(m_pContainer->hwnd, WM_NCLBUTTONDOWN, HTCAPTION, *((LPARAM*)(&cur))); + } + break; + + case WM_LBUTTONUP: + GetCursorPos(&tmp); + if (m_pPanel.isHovered()) + m_pPanel.handleClick(tmp); + else { + cur.x = (SHORT)tmp.x; + cur.y = (SHORT)tmp.y; + SendMessage(m_pContainer->hwnd, WM_NCLBUTTONUP, HTCAPTION, *((LPARAM*)(&cur))); + } + break; + + case WM_MOUSEMOVE: + GetCursorPos(&pt); + DM_DismissTip(pt); + m_pPanel.trackMouse(pt); + break; + + case WM_APPCOMMAND: + { + DWORD cmd = GET_APPCOMMAND_LPARAM(lParam); + if (cmd == APPCOMMAND_BROWSER_BACKWARD || cmd == APPCOMMAND_BROWSER_FORWARD) { + SendMessage(m_pContainer->hwnd, DM_SELECTTAB, cmd == APPCOMMAND_BROWSER_BACKWARD ? DM_SELECT_PREV : DM_SELECT_NEXT, 0); + return 1; + } + } + break; + + case WM_COMMAND: + if (LOWORD(wParam) >= MIN_CBUTTONID && LOWORD(wParam) <= MAX_CBUTTONID){ + Srmm_ClickToolbarIcon(m_hContact, LOWORD(wParam), GetDlgItem(m_hwnd, LOWORD(wParam)), 0); + break; + } + + switch (LOWORD(wParam)) { + case IDC_TOGGLESIDEBAR: + SendMessage(m_pContainer->hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0); + break; + + case IDCANCEL: + ShowWindow(m_pContainer->hwnd, SW_MINIMIZE); + return FALSE; + + case IDC_CLOSE: + SendMessage(m_hwnd, WM_CLOSE, 0, 1); + break; + + case IDC_SELFTYPING: + // Typing support for GCW_PRIVMESS sessions + if (si->iType == GCW_PRIVMESS) { + if (m_hContact) { + int iCurrentTypingMode = db_get_b(m_hContact, SRMSGMOD, SRMSGSET_TYPING, M.GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW)); + + if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) { + DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF); + m_nTypeMode = PROTOTYPE_SELFTYPING_OFF; + } + db_set_b(m_hContact, SRMSGMOD, SRMSGSET_TYPING, (BYTE)!iCurrentTypingMode); + } + } + break; + } + break; + + case WM_KEYDOWN: + SetFocus(m_message.GetHwnd()); + break; + + case WM_ERASEBKGND: + RECT rcClient, rcWindow; + { + HDC hdc = (HDC)wParam; + UINT item_ids[3] = { ID_EXTBKUSERLIST, ID_EXTBKHISTORY, ID_EXTBKINPUTAREA }; + UINT ctl_ids[3] = { IDC_LIST, IDC_LOG, IDC_MESSAGE }; + HANDLE hbp = 0; + HDC hdcMem = 0; + HBITMAP hbm, hbmOld; + + GetClientRect(m_hwnd, &rcClient); + LONG cx = rcClient.right - rcClient.left; + LONG cy = rcClient.bottom - rcClient.top; + + if (CMimAPI::m_haveBufferedPaint) { + hbp = CSkin::InitiateBufferedPaint(hdc, rcClient, hdcMem); + hbm = hbmOld = 0; + } + else { + hdcMem = CreateCompatibleDC(hdc); + hbm = CSkin::CreateAeroCompatibleBitmap(rcClient, hdc); + hbmOld = (HBITMAP)SelectObject(hdcMem, hbm); + } + + if (CSkin::m_skinEnabled && !M.isAero()) { + CSkin::SkinDrawBG(m_hwnd, m_pContainer->hwnd, m_pContainer, &rcClient, hdcMem); + for (int i = 0; i < 3; i++) { + CSkinItem *item = &SkinItems[item_ids[i]]; + if (!item->IGNORED) { + GetWindowRect(GetDlgItem(m_hwnd, ctl_ids[i]), &rcWindow); + pt.x = rcWindow.left; + pt.y = rcWindow.top; + ScreenToClient(m_hwnd, &pt); + rc.left = pt.x - item->MARGIN_LEFT; + rc.top = pt.y - item->MARGIN_TOP; + rc.right = rc.left + item->MARGIN_RIGHT + (rcWindow.right - rcWindow.left) + item->MARGIN_LEFT; + rc.bottom = rc.top + item->MARGIN_BOTTOM + (rcWindow.bottom - rcWindow.top) + item->MARGIN_TOP; + CSkin::DrawItem(hdcMem, &rc, item); + } + } + } + else { + CSkin::FillBack(hdcMem, &rcClient); + + if (M.isAero()) { + LONG temp = rcClient.bottom; + rcClient.bottom = m_pPanel.isActive() ? m_pPanel.getHeight() + 5 : 5; + FillRect(hdcMem, &rcClient, (HBRUSH)GetStockObject(BLACK_BRUSH)); + rcClient.bottom = temp; + } + } + + GetClientRect(m_hwnd, &rc); + m_pPanel.renderBG(hdcMem, rc, &SkinItems[ID_EXTBKINFOPANELBG], M.isAero()); + m_pPanel.renderContent(hdcMem); + + if (!CSkin::m_skinEnabled) + RenderToolbarBG(hdcMem, rcClient); + + if (hbp) + CSkin::FinalizeBufferedPaint(hbp, &rcClient); + else { + BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hbmOld); + DeleteObject(hbm); + DeleteDC(hdcMem); + } + if (!m_fLimitedUpdate) + SetAeroMargins(m_pContainer); + } + return TRUE; + + case WM_NCPAINT: + if (CSkin::m_skinEnabled) + return 0; + break; + + case WM_PAINT: + PAINTSTRUCT ps; + BeginPaint(m_hwnd, &ps); + EndPaint(m_hwnd, &ps); + return 0; + + case WM_RBUTTONUP: + GetCursorPos(&pt); + if (!m_pPanel.invokeConfigDialog(pt)) { + HMENU subMenu = GetSubMenu(PluginConfig.g_hMenuContext, 0); + + MsgWindowUpdateMenu(subMenu, MENU_TABCONTEXT); + + int iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, NULL); + if (iSelection >= IDM_CONTAINERMENU) { + char szIndex[10]; + _itoa_s(iSelection - IDM_CONTAINERMENU, szIndex, 10); + ptrW wszContainer(db_get_wsa(NULL, "TAB_ContainersW", szIndex)); + if (wszContainer != nullptr) + SendMessage(m_hwnd, DM_CONTAINERSELECTED, 0, wszContainer); + break; + } + MsgWindowMenuHandler(iSelection, MENU_TABCONTEXT); + } + break; + + case WM_LBUTTONDBLCLK: + if (LOWORD(lParam) < 30) + PostMessage(m_hwnd, GC_SCROLLTOBOTTOM, 0, 0); + break; + + case WM_CLOSE: + if (wParam == 0 && lParam == 0) { + if (PluginConfig.m_EscapeCloses == 1) { + SendMessage(m_pContainer->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); + return TRUE; + } + if (PluginConfig.m_bHideOnClose && PluginConfig.m_EscapeCloses == 2) { + ShowWindow(m_pContainer->hwnd, SW_HIDE); + return TRUE; + } + _dlgReturn(m_hwnd, TRUE); + } + SendMessage(m_hwnd, GC_CLOSEWINDOW, 0, 1); + return 0; + + case GC_CLOSEWINDOW: + { + bool bForced = (lParam == 2); + + int iTabs = TabCtrl_GetItemCount(m_hwndParent); + if (iTabs == 1 && CMimAPI::m_shutDown == 0) { + SendMessage(m_pContainer->hwnd, WM_CLOSE, 0, 1); + return 1; + } + + m_pContainer->iChilds--; + int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd); + + // after closing a tab, we need to activate the tab to the left side of + // the previously open tab. + // normally, this tab has the same index after the deletion of the formerly active tab + // unless, of course, we closed the last (rightmost) tab. + if (!m_pContainer->bDontSmartClose && iTabs > 1 && !bForced) { + if (i == iTabs - 1) + i--; + else + i++; + TabCtrl_SetCurSel(m_hwndParent, i); + + TCITEM item = {}; + item.mask = TCIF_PARAM; + TabCtrl_GetItem(m_hwndParent, i, &item); // retrieve dialog hwnd for the now active tab... + m_pContainer->hwndActive = (HWND)item.lParam; + + SendMessage(m_pContainer->hwnd, DM_QUERYCLIENTAREA, 0, (LPARAM)&rc); + SetWindowPos(m_pContainer->hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW); + ShowWindow((HWND)item.lParam, SW_SHOW); + SetForegroundWindow(m_pContainer->hwndActive); + SetFocus(m_pContainer->hwndActive); + SendMessage(m_pContainer->hwnd, WM_SIZE, 0, 0); + } + + if (iTabs == 1) + SendMessage(m_pContainer->hwnd, WM_CLOSE, 0, 1); + else { + PostMessage(m_pContainer->hwnd, WM_SIZE, 0, 0); + DestroyWindow(m_hwnd); + } + } + return 0; + + case DM_SAVESIZE: + if (m_dwFlags & MWF_NEEDCHECKSIZE) + lParam = 0; + + m_dwFlags &= ~MWF_NEEDCHECKSIZE; + if (m_dwFlags & MWF_WASBACKGROUNDCREATE) + m_dwFlags &= ~MWF_INITMODE; + + SendMessage(m_pContainer->hwnd, DM_QUERYCLIENTAREA, 0, (LPARAM)&rcClient); + MoveWindow(m_hwnd, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), TRUE); + + if (m_dwFlags & MWF_WASBACKGROUNDCREATE) { + m_dwFlags &= ~MWF_WASBACKGROUNDCREATE; + SendMessage(m_hwnd, WM_SIZE, 0, 0); + + pt.x = pt.y = 0; + m_log.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&pt); + if (PluginConfig.m_bAutoLocaleSupport) { + if (hkl == 0) + DM_LoadLocale(); + else + PostMessage(m_hwnd, DM_SETLOCALE, 0, 0); + } + } + else { + SendMessage(m_hwnd, WM_SIZE, 0, 0); + if (lParam == 0) + PostMessage(m_hwnd, GC_SCROLLTOBOTTOM, 1, 1); + } + return 0; + + case DM_REFRESHTABINDEX: + m_iTabID = GetTabIndexFromHWND(m_hwndParent, m_hwnd); + return 0; + + case WM_CBD_UPDATED: + if (lParam) + CB_ChangeButton((CustomButtonData*)lParam); + else + BB_InitDlgButtons(); + + BB_SetButtonsPos(); + return 0; + + case WM_CBD_REMOVED: + if (lParam) + CB_DestroyButton((DWORD)wParam, (DWORD)lParam); + else + CB_DestroyAllButtons(); + break; + + case DM_CONFIGURETOOLBAR: + SendMessage(m_hwnd, WM_SIZE, 0, 0); + break; + + case DM_SMILEYOPTIONSCHANGED: + SendMessage(m_hwnd, GC_REDRAWLOG, 0, 1); + break; + + case EM_THEMECHANGED: + DM_FreeTheme(); + DM_ThemeChanged(); + return 0; + + case WM_DWMCOMPOSITIONCHANGED: + BB_RefreshTheme(); + memset(&m_pContainer->mOld, -1000, sizeof(MARGINS)); + CProxyWindow::verify(this); + break; + } + return CTabBaseDlg::DlgProc(uMsg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// chat session creator + +void ShowRoom(SESSION_INFO *si) +{ + if (si == nullptr) + return; + + if (si->hWnd != nullptr) { + ActivateExistingTab(si->dat->m_pContainer, si->hWnd); + return; + } + + wchar_t szName[CONTAINER_NAMELEN + 2]; szName[0] = 0; + TContainerData *pContainer = nullptr; + if (si->dat != nullptr) + pContainer = si->dat->m_pContainer; + if (pContainer == nullptr) { + GetContainerNameForContact(si->hContact, szName, CONTAINER_NAMELEN); + if (!g_Settings.bOpenInDefault && !mir_wstrcmp(szName, L"default")) + wcsncpy(szName, L"Chat Rooms", CONTAINER_NAMELEN); + szName[CONTAINER_NAMELEN] = 0; + pContainer = FindContainerByName(szName); + } + if (pContainer == nullptr) + pContainer = CreateContainer(szName, FALSE, si->hContact); + if (pContainer == nullptr) + return; // smth went wrong, nothing to do here + + MCONTACT hContact = si->hContact; + if (M.FindWindow(hContact) != 0) + return; + + if (hContact != 0 && M.GetByte("limittabs", 0) && !wcsncmp(pContainer->szName, L"default", 6)) { + if ((pContainer = FindMatchingContainer(L"default")) == NULL) { + wchar_t szName[CONTAINER_NAMELEN + 1]; + mir_snwprintf(szName, L"default"); + if ((pContainer = CreateContainer(szName, CNT_CREATEFLAG_CLONED, hContact)) == NULL) + return; + } + } + + wchar_t *contactName = pcli->pfnGetContactDisplayName(hContact, 0); + + // cut nickname if larger than x chars... + wchar_t newcontactname[128]; + if (mir_wstrlen(contactName) > 0) { + if (M.GetByte("cuttitle", 0)) + CutContactName(contactName, newcontactname, _countof(newcontactname)); + else + wcsncpy_s(newcontactname, contactName, _TRUNCATE); + } + else wcsncpy_s(newcontactname, L"_U_", _TRUNCATE); + + HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS); + + // hide the active tab + if (pContainer->hwndActive) + ShowWindow(pContainer->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 = newcontactname; + item.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM; + int iTabId = TabCtrl_InsertItem(hwndTab, pContainer->iTabIndex, &item); + + SendMessage(hwndTab, EM_REFRESHWITHOUTCLIP, 0, 0); + TabCtrl_SetCurSel(hwndTab, iTabId); + pContainer->iChilds++; + + CChatRoomDlg *pDlg = new CChatRoomDlg(si); + pDlg->m_iTabID = iTabId; + pDlg->m_pContainer = pContainer; + pDlg->SetParent(hwndTab); + pDlg->Create(); + + HWND hwndNew = pDlg->GetHwnd(); + item.lParam = (LPARAM)hwndNew; + TabCtrl_SetItem(hwndTab, iTabId, &item); + + if (pContainer->dwFlags & CNT_SIDEBAR) + pContainer->SideBar->addSession(pDlg, pContainer->iTabIndex); + + SendMessage(pContainer->hwnd, WM_SIZE, 0, 0); + // if the container is minimized, then pop it up... + if (IsIconic(pContainer->hwnd)) { + SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); + SetFocus(pContainer->hwndActive); + } + + if (PluginConfig.m_bHideOnClose && !IsWindowVisible(pContainer->hwnd)) { + WINDOWPLACEMENT wp = { 0 }; + wp.length = sizeof(wp); + GetWindowPlacement(pContainer->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->hwnd, SW_SHOWMAXIMIZED); + else { + ShowWindow(pContainer->hwnd, SW_SHOWNORMAL); + } + SendMessage(pContainer->hwndActive, WM_SIZE, 0, 0); + SetFocus(hwndNew); + } + else { + SetFocus(hwndNew); + RedrawWindow(pContainer->hwnd, NULL, NULL, RDW_INVALIDATE); + UpdateWindow(pContainer->hwnd); + if (GetForegroundWindow() != pContainer->hwnd) + SetForegroundWindow(pContainer->hwnd); + } + + if (PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek && CSkin::m_skinEnabled && !M.GetByte("forceAeroPeek", 0)) + CWarning::show(CWarning::WARN_AEROPEEK_SKIN, MB_ICONWARNING | MB_OK); +} |