From 91d5b91fe12c9733db9fd4d8eb84bfbe674b0a2c Mon Sep 17 00:00:00 2001 From: Rozhuk Ivan Date: Tue, 25 Nov 2014 20:29:32 +0000 Subject: TabSRMM null ptr fixes and code cleanup require for review. git-svn-id: http://svn.miranda-ng.org/main/trunk@11065 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/TabSRMM/src/buttonsbar.cpp | 2835 +++++------ plugins/TabSRMM/src/chat/window.cpp | 5 +- plugins/TabSRMM/src/contactcache.cpp | 1244 ++--- plugins/TabSRMM/src/container.cpp | 4680 +++++++++--------- plugins/TabSRMM/src/controls.cpp | 2073 ++++---- plugins/TabSRMM/src/generic_msghandlers.cpp | 40 +- plugins/TabSRMM/src/msgdialog.cpp | 6902 +++++++++++++-------------- plugins/TabSRMM/src/sendqueue.cpp | 1780 +++---- plugins/TabSRMM/src/srmm.cpp | 415 +- plugins/TabSRMM/src/templates.cpp | 789 ++- plugins/TabSRMM/src/themes.cpp | 5114 ++++++++++---------- 11 files changed, 12941 insertions(+), 12936 deletions(-) (limited to 'plugins/TabSRMM/src') diff --git a/plugins/TabSRMM/src/buttonsbar.cpp b/plugins/TabSRMM/src/buttonsbar.cpp index 7af8ac71ff..3d46372ed1 100644 --- a/plugins/TabSRMM/src/buttonsbar.cpp +++ b/plugins/TabSRMM/src/buttonsbar.cpp @@ -1,1417 +1,1418 @@ -#include "commonheaders.h" - -static HANDLE hButtonsBarAddButton; -static HANDLE hButtonsBarRemoveButton; -static HANDLE hButtonsBarGetButtonState; -static HANDLE hButtonsBarSetButtonState; -static HANDLE hButtonsBarModifyButton; - -HANDLE hHookButtonPressedEvt; -HANDLE hHookToolBarLoadedEvt; - -static LIST RButtonsList(1, NumericKeySortT), LButtonsList(1, NumericKeySortT); - -DWORD LastCID = 4000; -DWORD dwSepCount = 0; - -static mir_cs ToolBarCS; - -static void wipeList(LIST &list) -{ - for (int i = list.getCount()-1; i >= 0; i--) { - delete list[i]; - list.remove(i); - } -} - -static int sstSortButtons(const void *p1, const void *p2) -{ - return (*(CustomButtonData**)p1)->dwPosition - (*(CustomButtonData**)p2)->dwPosition; -} - -void CB_InitCustomButtons() -{ - dwSepCount = M.GetDword("TabSRMM_Toolbar", "SeparatorsCount", 0); - - hButtonsBarAddButton = CreateServiceFunction(MS_BB_ADDBUTTON, CB_AddButton); - hButtonsBarRemoveButton = CreateServiceFunction(MS_BB_REMOVEBUTTON, CB_RemoveButton); - hButtonsBarModifyButton = CreateServiceFunction(MS_BB_MODIFYBUTTON, CB_ModifyButton); - hButtonsBarGetButtonState = CreateServiceFunction(MS_BB_GETBUTTONSTATE, CB_GetButtonState); - hButtonsBarSetButtonState = CreateServiceFunction(MS_BB_SETBUTTONSTATE, CB_SetButtonState); - - hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED); - hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED); -} - -void CB_DeInitCustomButtons() -{ - wipeList(LButtonsList); - wipeList(RButtonsList); - - DestroyHookableEvent(hHookToolBarLoadedEvt); - DestroyHookableEvent(hHookButtonPressedEvt); - DestroyServiceFunction(hButtonsBarAddButton); - DestroyServiceFunction(hButtonsBarRemoveButton); - DestroyServiceFunction(hButtonsBarModifyButton); - DestroyServiceFunction(hButtonsBarGetButtonState); - DestroyServiceFunction(hButtonsBarSetButtonState); -} - -void CB_DestroyAllButtons(HWND hwndDlg, TWindowData *dat) -{ - HWND hwndBtn = NULL; - for (int i=0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID); - if (hwndBtn) - DestroyWindow(hwndBtn); - } - - for (int i=0; i < RButtonsList.getCount(); i++) { - CustomButtonData *cbd = RButtonsList[i]; - hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID); - if (hwndBtn) - DestroyWindow(hwndBtn); - } -} - -void CB_DestroyButton(HWND hwndDlg, TWindowData *dat, DWORD dwButtonCID, DWORD dwFlags) -{ - HWND hwndBtn = GetDlgItem(hwndDlg, dwButtonCID); - RECT rc = { 0 }; - if (hwndBtn) { - GetClientRect(hwndBtn, &rc); - if (dwFlags & BBBF_ISLSIDEBUTTON) - dat->bbLSideWidth -= rc.right; - else if (dwFlags & BBBF_ISRSIDEBUTTON) - dat->bbRSideWidth -= rc.right; - - DestroyWindow(hwndBtn); - BB_SetButtonsPos(dat); - } -} - -void CB_ChangeButton(HWND hwndDlg, TWindowData *dat, CustomButtonData *cbd) -{ - HWND hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID); - if (hwndBtn) { - if (cbd->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); - if (cbd->ptszTooltip) - SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)cbd->ptszTooltip, BATF_TCHAR); - SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)dat->pContainer, 0); - SetWindowTextA(hwndBtn, cbd->pszModuleName); - } -} - -void CB_ReInitCustomButtons() -{ - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - - if (cbd->opFlags & BBSF_NTBSWAPED || cbd->opFlags & BBSF_NTBDESTRUCT) { - cbd->opFlags ^= BBSF_NTBSWAPED; - - if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) - RButtonsList.insert(cbd); - - LButtonsList.remove(i); - i--; - } - } - - for (int i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData* cbd = RButtonsList[i]; - if (cbd->opFlags & BBSF_NTBSWAPED || cbd->opFlags & BBSF_NTBDESTRUCT) { - cbd->opFlags ^= BBSF_NTBSWAPED; - - if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) - LButtonsList.insert(cbd); - - RButtonsList.remove(i); - i--; - } - } - M.BroadcastMessage(DM_BBNEEDUPDATE, 0, 0); - M.BroadcastMessage(DM_LOADBUTTONBARICONS, 0, 0); -} - -void CB_HardReInit() -{ - M.BroadcastMessage(DM_CBDESTROY, 0, 0); - { - mir_cslock lck(ToolBarCS); - wipeList(LButtonsList); - wipeList(RButtonsList); - } - LastCID = 4000; - dwSepCount = 0; - - CB_InitDefaultButtons(); - NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0); -} - -static INT_PTR CB_AddButton(WPARAM, LPARAM lParam) -{ - BBButton *bbdi = (BBButton *)lParam; - if (!bbdi || bbdi->cbSize != sizeof(BBButton)) - return 1; - - CustomButtonData *cbd = new CustomButtonData(); - - if (!bbdi->iButtonWidth && (bbdi->bbbFlags & BBBF_ISARROWBUTTON)) - cbd->iButtonWidth = DPISCALEX_S(34); - else if (!bbdi->iButtonWidth) - cbd->iButtonWidth = DPISCALEX_S(22); - else - cbd->iButtonWidth = DPISCALEX_S(bbdi->iButtonWidth); - - cbd->pszModuleName = mir_strdup(bbdi->pszModuleName); - - if (bbdi->ptszTooltip) { - if (bbdi->bbbFlags & BBBF_ANSITOOLTIP) - cbd->ptszTooltip = mir_a2u(bbdi->pszTooltip); - else - cbd->ptszTooltip = mir_tstrdup(bbdi->ptszTooltip); - } - else cbd->ptszTooltip = NULL; - - cbd->dwButtonOrigID = bbdi->dwButtonID; - cbd->hIcon = bbdi->hIcon; - cbd->dwPosition = bbdi->dwDefPos; - cbd->dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID; - //ugly workaround for smileys plugins - cbd->dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? (cbd->dwButtonCID == IDOK ? IDC_SENDMENU : (cbd->dwButtonCID + 1)) : 0; - cbd->bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->bLSided = (bbdi->bbbFlags & BBBF_ISLSIDEBUTTON) != 0; - cbd->bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - cbd->bSeparator = (bbdi->bbbFlags & BBBF_ISDUMMYBUTTON) != 0; - cbd->bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - cbd->bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0; - - CB_GetButtonSettings(NULL, cbd); - - if (cbd->bLSided) - LButtonsList.insert(cbd); - else if (cbd->bRSided) - RButtonsList.insert(cbd); - else return 1; - - if (cbd->dwButtonCID != cbd->dwButtonOrigID) - LastCID++; - if (cbd->dwArrowCID == LastCID) - LastCID++; - - M.BroadcastMessage(DM_BBNEEDUPDATE, 0, 0); - return 0; -} - -static INT_PTR CB_GetButtonState(WPARAM wParam, LPARAM lParam) -{ - if (wParam == 0 || lParam == 0) - return 1; - - DWORD tempCID = 0; - bool realbutton = false; - BBButton *bbdi = (BBButton *)lParam; - bbdi->bbbFlags = 0; - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { - realbutton = true; - tempCID = cbd->dwButtonCID; - } - } - if (!realbutton) - for (int i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData* cbd = RButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { - realbutton = true; - tempCID = cbd->dwButtonCID; - } - } - - if (!realbutton) - return 1; - - HWND hwndDlg = M.FindWindow(wParam); - if (!hwndDlg) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED); - return 0; -} - -static INT_PTR CB_SetButtonState(WPARAM wParam, LPARAM lParam) -{ - if (wParam == 0 || lParam == 0) - return 1; - - bool realbutton = false; - DWORD tempCID = 0; - BBButton *bbdi = (BBButton *)lParam; - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { - realbutton = true; - tempCID = cbd->dwButtonCID; - } - } - if (!realbutton) - for (int i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData* cbd = RButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { - realbutton = true; - tempCID = cbd->dwButtonCID; - } - } - - if (!realbutton) - return 1; - - HWND hwndDlg = M.FindWindow(wParam); - if (!hwndDlg) - return 1; - - SetDlgItemTextA(hwndDlg, tempCID, bbdi->pszModuleName); - if (bbdi->hIcon) - SendDlgItemMessage(hwndDlg, tempCID, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(bbdi->hIcon)); - if (bbdi->pszTooltip) - SendDlgItemMessage(hwndDlg, tempCID, BUTTONADDTOOLTIP, (WPARAM)bbdi->ptszTooltip, (bbdi->bbbFlags & BBBF_ANSITOOLTIP) ? 0 : BATF_TCHAR); - if (bbdi->bbbFlags) { - Utils::showDlgControl(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_HIDDEN) ? SW_HIDE : SW_SHOW); - Utils::enableDlgControl(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_DISABLED) ? 0 : 1); - CheckDlgButton(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_PUSHED) ? 1 : 0); - CheckDlgButton(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_RELEASED) ? 0 : 1); - } - return 0; -} - -static INT_PTR CB_RemoveButton(WPARAM, LPARAM lParam) -{ - BBButton *bbdi = (BBButton *)lParam; - if (!bbdi) - return 1; - - DWORD tempCID = 0; - DWORD dwFlags = 0; - { - mir_cslock lck(ToolBarCS); - - for (int i = LButtonsList.getCount()-1; i >= 0; i--) { - CustomButtonData *cbd = LButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && cbd->dwButtonOrigID == bbdi->dwButtonID) { - tempCID = cbd->dwButtonCID; - dwFlags = cbd->bLSided ? BBBF_ISLSIDEBUTTON : BBBF_ISRSIDEBUTTON; - LButtonsList.remove(i); - } - } - - if (!tempCID) { - for (int i = RButtonsList.getCount()-1; i >= 0; i--) { - CustomButtonData *cbd = RButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && cbd->dwButtonOrigID == bbdi->dwButtonID) { - tempCID = cbd->dwButtonCID; - dwFlags = cbd->bLSided ? BBBF_ISLSIDEBUTTON : BBBF_ISRSIDEBUTTON; - RButtonsList.remove(i); - } - } - } - } - - if (tempCID) - M.BroadcastMessage(DM_CBDESTROY, (WPARAM)tempCID, (LPARAM)dwFlags); - return 0; -} - -static INT_PTR CB_ModifyButton(WPARAM, LPARAM lParam) -{ - BBButton *bbdi = (BBButton *)lParam; - if (!bbdi) - return 1; - - bool bFound = 1; - CustomButtonData *cbd = NULL; - { - mir_cslock lck(ToolBarCS); - - for (int i = 0; i < LButtonsList.getCount(); i++) { - cbd = LButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { - bFound = true; - break; - } - } - - if (!bFound) { - for (int i = 0; i < RButtonsList.getCount(); i++) { - cbd = RButtonsList[i]; - if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { - bFound = true; - break; - } - } - } - - if (bFound) { - if (bbdi->pszTooltip) { - mir_free(cbd->ptszTooltip); - if (bbdi->bbbFlags & BBBF_ANSITOOLTIP) - cbd->ptszTooltip = mir_a2u(bbdi->pszTooltip); - else - cbd->ptszTooltip = mir_tstrdup(bbdi->ptszTooltip); - } - if (bbdi->hIcon) - cbd->hIcon = bbdi->hIcon; - if (bbdi->bbbFlags) { - cbd->bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->bLSided = (bbdi->bbbFlags & BBBF_ISLSIDEBUTTON) != 0; - cbd->bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - cbd->bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - } - } - } - - if (bFound) - M.BroadcastMessage(DM_BBNEEDUPDATE, 0, (LPARAM)cbd); - return 0; -} - -void BB_UpdateIcons(HWND hdlg, TWindowData *dat) -{ - HWND hwndBtn = NULL; - - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - if (cbd) { - if (!cbd->bSeparator) - hwndBtn = GetDlgItem(hdlg, cbd->dwButtonCID); - - if (hwndBtn && cbd->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); - } - } - - hwndBtn = NULL; - for (int i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData *cbd = RButtonsList[i]; - if (cbd) { - if (!cbd->bSeparator) - hwndBtn = GetDlgItem(hdlg, cbd->dwButtonCID); - - if (hwndBtn && cbd->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); - } - } -} - -void TSAPI BB_InitDlgButtons(TWindowData *dat) -{ - RECT rect; - int i; - int lwidth = 0, rwidth = 0; - RECT rcSplitter; - POINT ptSplitter; - int splitterY; - BYTE gap = DPISCALEX_S(PluginConfig.g_iButtonsBarGap); - BOOL isThemed = TRUE; - int cx = 0, cy = 0; - int lcount = LButtonsList.getCount(); - int rcount = RButtonsList.getCount(); - HWND hdlg = dat->hwnd; - - if (dat == 0 || hdlg == 0) { return; } - if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKBUTTONSNPRESSED].IGNORED && - !SkinItems[ID_EXTBKBUTTONSPRESSED].IGNORED && !SkinItems[ID_EXTBKBUTTONSMOUSEOVER].IGNORED) { - isThemed = FALSE; - } - - GetWindowRect(GetDlgItem(hdlg, (dat->bType == SESSIONTYPE_IM) ? IDC_SPLITTER : IDC_SPLITTERY), &rcSplitter); - ptSplitter.x = 0; - ptSplitter.y = rcSplitter.top; - ScreenToClient(hdlg, &ptSplitter); - - GetClientRect(hdlg, &rect); - splitterY = ptSplitter.y - DPISCALEY_S(1); - - HWND hwndBtn = NULL; - - for (i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData *cbd = RButtonsList[i]; - if (((dat->bType == SESSIONTYPE_IM && cbd->bIMButton) || (dat->bType == SESSIONTYPE_CHAT && cbd->bChatButton))) { - if (!cbd->bHidden) - rwidth += cbd->iButtonWidth + gap; - if (!cbd->bHidden && !cbd->bCanBeHidden) - dat->iButtonBarReallyNeeds += cbd->iButtonWidth + gap; - if (!cbd->bSeparator && !GetDlgItem(hdlg, cbd->dwButtonCID)) { - hwndBtn = CreateWindowEx(0, _T("MButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, rect.right - rwidth + gap, splitterY, cbd->iButtonWidth, DPISCALEY_S(22), hdlg, (HMENU)cbd->dwButtonCID, g_hInst, NULL); - CustomizeButton(hwndBtn); - } - if (!cbd->bSeparator && hwndBtn) { - SendMessage(hwndBtn, BUTTONSETASFLATBTN, TRUE, 0); - SendMessage(hwndBtn, BUTTONSETASTHEMEDBTN, isThemed != 0, 0); - if (cbd->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); - if (cbd->ptszTooltip) - SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), BATF_TCHAR); - SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)dat->pContainer, 0); - SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, TRUE, 0); - - if (hwndBtn && cbd->dwArrowCID) - SendMessage(hwndBtn, BUTTONSETARROW, cbd->dwArrowCID, 0); - if (hwndBtn && cbd->bPushButton) - SendMessage(hwndBtn, BUTTONSETASPUSHBTN, TRUE, 0); - } - } - else if (GetDlgItem(hdlg, cbd->dwButtonCID)) - DestroyWindow(GetDlgItem(hdlg, cbd->dwButtonCID)); - - if (cbd->bDisabled) - EnableWindow(hwndBtn, 0); - if (cbd->bHidden) - ShowWindow(hwndBtn, SW_HIDE); - - } - - hwndBtn = NULL; - - for (i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - if (((dat->bType == SESSIONTYPE_IM && cbd->bIMButton) || (dat->bType == SESSIONTYPE_CHAT && cbd->bChatButton))) { - if (!cbd->bSeparator && !GetDlgItem(hdlg, cbd->dwButtonCID)) { - hwndBtn = CreateWindowEx(0, _T("MButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, 2 + lwidth, splitterY, - cbd->iButtonWidth, DPISCALEY_S(22), hdlg, (HMENU)cbd->dwButtonCID, g_hInst, NULL); - CustomizeButton(hwndBtn); - } - if (!cbd->bHidden) - lwidth += cbd->iButtonWidth + gap; - if (!cbd->bHidden && !cbd->bCanBeHidden) - dat->iButtonBarReallyNeeds += cbd->iButtonWidth + gap; - if (!cbd->bSeparator && hwndBtn) { - SendMessage(hwndBtn, BUTTONSETASFLATBTN, TRUE, 0); - SendMessage(hwndBtn, BUTTONSETASTHEMEDBTN, isThemed != 0, 0); - if (cbd->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); - if (cbd->ptszTooltip) - SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), BATF_TCHAR); - SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)dat->pContainer, 0); - SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, TRUE, 0); - - if (hwndBtn && cbd->dwArrowCID) - SendMessage(hwndBtn, BUTTONSETARROW, cbd->dwArrowCID, 0); - if (hwndBtn && cbd->bPushButton) - SendMessage(hwndBtn, BUTTONSETASPUSHBTN, TRUE, 0); - } - } - else if (GetDlgItem(hdlg, cbd->dwButtonCID)) - DestroyWindow(GetDlgItem(hdlg, cbd->dwButtonCID)); - - if (cbd->bDisabled) - EnableWindow(hwndBtn, 0); - if (cbd->bHidden) - ShowWindow(hwndBtn, SW_HIDE); - } - - dat->bbLSideWidth = lwidth; - dat->bbRSideWidth = rwidth; -} - -void TSAPI BB_RedrawButtons(TWindowData *dat) -{ - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - HWND hwnd = GetDlgItem(dat->hwnd, cbd->dwButtonCID); - if (hwnd) - InvalidateRect(hwnd, 0, TRUE); - } - - for (int i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData *cbd = RButtonsList[i]; - HWND hwnd = GetDlgItem(dat->hwnd, cbd->dwButtonCID); - if (hwnd) - InvalidateRect(hwnd, 0, TRUE); - } - - HWND hwndToggleSideBar = GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_TOGGLESIDEBAR : IDC_CHAT_TOGGLESIDEBAR); - if (hwndToggleSideBar && IsWindow(hwndToggleSideBar)) - InvalidateRect(hwndToggleSideBar, 0, TRUE); -} - -BOOL TSAPI BB_SetButtonsPos(TWindowData *dat) -{ - HWND hwnd = dat->hwnd; - if (!dat || !IsWindowVisible(hwnd)) - return 0; - - RECT rect; - int i; - HWND hwndBtn = 0; - - BYTE gap = DPISCALEX_S(PluginConfig.g_iButtonsBarGap); - bool showToolbar = !(dat->pContainer->dwFlags & CNT_HIDETOOLBAR); - bool bBottomToolbar = (dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR) != 0; - - HDWP hdwp = BeginDeferWindowPos(LButtonsList.getCount() + RButtonsList.getCount() + 1); - - HWND hwndToggleSideBar = GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_TOGGLESIDEBAR : IDC_CHAT_TOGGLESIDEBAR); - ShowWindow(hwndToggleSideBar, (showToolbar && dat->pContainer->SideBar->isActive()) ? SW_SHOW : SW_HIDE); - - mir_cslock lck(ToolBarCS); - - RECT rcSplitter; - GetWindowRect(GetDlgItem(hwnd, (dat->bType == SESSIONTYPE_IM) ? IDC_SPLITTER : IDC_SPLITTERY), &rcSplitter); - - POINT ptSplitter = { 0, rcSplitter.top }; - ScreenToClient(hwnd, &ptSplitter); - - GetClientRect(hwnd, &rect); - - int splitterY = (!bBottomToolbar) ? ptSplitter.y - DPISCALEY_S(1) : rect.bottom; - int tempL = dat->bbLSideWidth, tempR = dat->bbRSideWidth; - int lwidth = 0, rwidth = 0; - int iOff = DPISCALEY_S((PluginConfig.g_DPIscaleY > 1.0) ? (dat->bType == SESSIONTYPE_IM ? 22 : 23) : 22); - - int foravatar = 0; - if ((rect.bottom - ptSplitter.y - (rcSplitter.bottom - rcSplitter.top) /*- DPISCALEY(2)*/ - (bBottomToolbar ? DPISCALEY_S(24) : 0) < dat->pic.cy - DPISCALEY_S(2)) && dat->bShowAvatar && !PluginConfig.m_AlwaysFullToolbarWidth) - foravatar = dat->pic.cx + gap; - - if ((dat->pContainer->dwFlags & CNT_SIDEBAR) && (dat->pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT)) { - DeferWindowPos(hdwp, hwndToggleSideBar, NULL, 4, 2 + splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - lwidth += 10; - tempL -= 10; - } - - for (i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - if (((dat->bType == SESSIONTYPE_IM) && cbd->bIMButton) || ((dat->bType == SESSIONTYPE_CHAT) && cbd->bChatButton)) { - hwndBtn = GetDlgItem(hwnd, cbd->dwButtonCID); - - if (!showToolbar) { - ShowWindow(hwndBtn, SW_HIDE); - DeferWindowPos(hdwp, hwndBtn, NULL, lwidth, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) - lwidth += cbd->iButtonWidth + gap; - if (!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden) - cbd->bAutoHidden = 1; - continue; - } - if (!cbd->bCanBeHidden && !cbd->bHidden && !(!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden)) { - ShowWindow(hwndBtn, SW_SHOW); - cbd->bAutoHidden = 0; - } - - if (!cbd->bSeparator && !IsWindowVisible(hwndBtn) && !IsWindowEnabled(hwndBtn) && !cbd->bAutoHidden) - tempL -= cbd->iButtonWidth + gap; - - if (cbd->bCanBeHidden && !cbd->bHidden && (cbd->bSeparator || !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden))) { - if (tempL + tempR > (rect.right - foravatar)) { - ShowWindow(hwndBtn, SW_HIDE); - cbd->bAutoHidden = 1; - tempL -= cbd->iButtonWidth + gap; - } - else if (cbd->bAutoHidden) { - ShowWindow(hwndBtn, SW_SHOW); - cbd->bAutoHidden = 0; - } - } - DeferWindowPos(hdwp, hwndBtn, NULL, lwidth, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE);// SWP_NOCOPYBITS); - if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) - lwidth += cbd->iButtonWidth + gap; - } - } - - if ((dat->pContainer->dwFlags & CNT_SIDEBAR) && (dat->pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_RIGHT)) { - DeferWindowPos(hdwp, hwndToggleSideBar, NULL, rect.right - foravatar - 10, 2 + splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - rwidth += 12; - tempR -= 12; - } - - for (i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData *cbd = RButtonsList[i]; - if (((dat->bType == SESSIONTYPE_IM) && cbd->bIMButton) || ((dat->bType == SESSIONTYPE_CHAT) && cbd->bChatButton)) { - hwndBtn = GetDlgItem(hwnd, cbd->dwButtonCID); - - if (!showToolbar) { - ShowWindow(hwndBtn, SW_HIDE); - if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) - rwidth += cbd->iButtonWidth + gap; - DeferWindowPos(hdwp, hwndBtn, NULL, rect.right - foravatar - rwidth + gap, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - if (!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden) - cbd->bAutoHidden = 1; - continue; - } - if (!cbd->bCanBeHidden && !cbd->bHidden && !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden)) { - ShowWindow(hwndBtn, SW_SHOW); - cbd->bAutoHidden = 0; - } - - if (!cbd->bSeparator && !IsWindowVisible(hwndBtn) && !IsWindowEnabled(hwndBtn) && !cbd->bAutoHidden) - tempR -= cbd->iButtonWidth + gap; - - if (cbd->bCanBeHidden && !cbd->bHidden && (cbd->bSeparator || !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden))) { - if (tempL + tempR > (rect.right - foravatar)) { - ShowWindow(hwndBtn, SW_HIDE); - cbd->bAutoHidden = 1; - tempR -= cbd->iButtonWidth + gap; - } - else if (cbd->bAutoHidden) { - ShowWindow(hwndBtn, SW_SHOW); - cbd->bAutoHidden = 0; - } - } - - if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) - rwidth += cbd->iButtonWidth + gap; - DeferWindowPos(hdwp, hwndBtn, NULL, rect.right - foravatar - rwidth + gap, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - } - - return EndDeferWindowPos(hdwp); -} - -void TSAPI BB_CustomButtonClick(TWindowData *dat, DWORD idFrom, HWND hwndFrom, BOOL code) -{ - RECT rc; - int i; - BOOL bFromArrow = 0; - CustomButtonClickData cbcd = { 0 }; - - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - for (i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData* cbd = LButtonsList[i]; - if (cbd->dwButtonCID == idFrom) { - cbcd.pszModule = cbd->pszModuleName; - cbcd.dwButtonId = cbd->dwButtonOrigID; - } - else if (cbd->dwArrowCID == idFrom) { - bFromArrow = 1; - cbcd.pszModule = cbd->pszModuleName; - cbcd.dwButtonId = cbd->dwButtonOrigID; - } - } - - if (!cbcd.pszModule) - for (i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData* cbd = RButtonsList[i]; - if (cbd->dwButtonCID == idFrom) { - cbcd.pszModule = cbd->pszModuleName; - cbcd.dwButtonId = cbd->dwButtonOrigID; - } - else if (cbd->dwArrowCID == idFrom) { - bFromArrow = 1; - cbcd.pszModule = cbd->pszModuleName; - cbcd.dwButtonId = cbd->dwButtonOrigID; - } - } - - cbcd.cbSize = sizeof(CustomButtonClickData); - cbcd.hwndFrom = dat->hwnd; - cbcd.hContact = dat->hContact; - cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, dat->hContact, (LPARAM)&cbcd); -} - - -void CB_GetButtonSettings(MCONTACT hContact, CustomButtonData *cbd) -{ - DBVARIANT dbv = { 0 }; - char SettingName[1024] = { '\0' }; - char* token = NULL; - - //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - - mir_snprintf(SettingName, sizeof(SettingName), "%s_%d", cbd->pszModuleName, cbd->dwButtonOrigID); - - if (!db_get_s(hContact, "TabSRMM_Toolbar", SettingName, &dbv)) { - token = strtok(dbv.pszVal, "_"); - cbd->dwPosition = (DWORD)atoi(token); - token = strtok(NULL, "_"); - cbd->bIMButton = atoi(token) != 0; - token = strtok(NULL, "_"); - cbd->bChatButton = atoi(token) != 0; - token = strtok(NULL, "_"); - cbd->bLSided = atoi(token) != 0; - token = strtok(NULL, "_"); - cbd->bRSided = atoi(token) != 0; - token = strtok(NULL, "_"); - cbd->bCanBeHidden = atoi(token) != 0; - - db_free(&dbv); - } -} - -void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd) -{ - char SettingName[1024] = { '\0' }; - char SettingParameter[1024] = { '\0' }; - - //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - - mir_snprintf(SettingName, sizeof(SettingName), "%s_%d", cbd->pszModuleName, cbd->dwButtonOrigID); - mir_snprintf(SettingParameter, sizeof(SettingParameter), "%d_%u_%u_%u_%u_%u", cbd->dwPosition, cbd->bIMButton, cbd->bChatButton, cbd->bLSided, cbd->bRSided, cbd->bCanBeHidden); - if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) - db_set_s(hContact, "TabSRMM_Toolbar", SettingName, SettingParameter); - else - db_unset(hContact, "TabSRMM_Toolbar", SettingName); -} - -void BB_RegisterSeparators() -{ - BBButton bbd = { 0 }; - DWORD i = 0; - bbd.cbSize = sizeof(BBButton); - bbd.pszModuleName = "Tabsrmm_sep"; - for (; dwSepCount > i; i++) { - bbd.bbbFlags = BBBF_ISDUMMYBUTTON | BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON; - bbd.dwButtonID = i + 1; - bbd.dwDefPos = 410 + i; - CB_AddButton(0, (LPARAM)&bbd); - } -} - -void BB_RefreshTheme(const TWindowData *dat) -{ - for (int i = 0; i < RButtonsList.getCount(); i++) { - CustomButtonData* cbd = RButtonsList[i]; - SendMessage(GetDlgItem(dat->hwnd, cbd->dwButtonCID), WM_THEMECHANGED, 0, 0); - } - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData* cbd = LButtonsList[i]; - SendMessage(GetDlgItem(dat->hwnd, cbd->dwButtonCID), WM_THEMECHANGED, 0, 0); - } -} - -void CB_InitDefaultButtons() -{ - BBButton bbd = { 0 }; - bbd.cbSize = sizeof(BBButton); - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISARROWBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_PROTOCOL; - bbd.dwDefPos = 10; - bbd.hIcon = LoadSkinnedIconHandle(SKINICON_OTHER_CONNECTING); - bbd.pszModuleName = "Tabsrmm"; - bbd.ptszTooltip = LPGENT("Protocol Button"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISARROWBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_NAME; - bbd.dwDefPos = 20; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[20]; - bbd.ptszTooltip = LPGENT("Info button"); - CB_AddButton(0, (LPARAM)&bbd); - - if (PluginConfig.g_SmileyAddAvail) { - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_SMILEYBTN; - bbd.iButtonWidth = 0; - bbd.dwDefPos = 30; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[9]; - bbd.ptszTooltip = LPGENT("Insert Emoticon"); - CB_AddButton(0, (LPARAM)&bbd); - } - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_FONTBOLD; - bbd.dwDefPos = 40; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[10]; - bbd.ptszTooltip = LPGENT("Bold text"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_FONTITALIC; - bbd.dwDefPos = 50; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[11]; - bbd.ptszTooltip = LPGENT("Italic text"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_FONTUNDERLINE; - bbd.dwDefPos = 60; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[12]; - bbd.ptszTooltip = LPGENT("Underlined text"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_FONTSTRIKEOUT; - bbd.dwDefPos = 70; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[15]; - bbd.ptszTooltip = LPGENT("Strike-through text"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_FONTFACE; - bbd.dwDefPos = 80; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[14]; - bbd.ptszTooltip = LPGENT("Select font color"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_ISARROWBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDOK; - bbd.dwDefPos = 10; - bbd.iButtonWidth = 51; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[6]; - bbd.ptszTooltip = LPGENT("Send message\nClick dropdown arrow for sending options"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_SAVE; - bbd.dwDefPos = 20; - bbd.iButtonWidth = 0; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[8]; - bbd.ptszTooltip = LPGENT("Close session"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_QUOTE; - bbd.dwDefPos = 30; - bbd.iButtonWidth = 0; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[4]; - bbd.ptszTooltip = LPGENT("Quote last message OR selected text"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_TIME; - bbd.dwDefPos = 40; - bbd.iButtonWidth = 0; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[2]; - bbd.ptszTooltip = LPGENT("Message Log Options"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_HISTORY; - bbd.dwDefPos = 50; - bbd.iButtonWidth = 0; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[0]; - bbd.ptszTooltip = LPGENT("View User's History"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_PIC; - bbd.dwDefPos = 60; - bbd.iButtonWidth = 0; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[7]; - bbd.ptszTooltip = LPGENT("Edit user notes"); - CB_AddButton(0, (LPARAM)&bbd); - - //chat buttons - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISDUMMYBUTTON; - bbd.dwButtonID = 1; - bbd.pszModuleName = "tb_splitter"; - bbd.dwDefPos = 31; - bbd.iButtonWidth = 22; - bbd.hIcon = 0; - bbd.pszTooltip = 0; - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISDUMMYBUTTON; - bbd.dwButtonID = 2; - bbd.dwDefPos = 22; - bbd.iButtonWidth = 22; - bbd.hIcon = 0; - bbd.pszTooltip = 0; - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISDUMMYBUTTON; - bbd.dwButtonID = 3; - bbd.dwDefPos = 71; - bbd.iButtonWidth = 22; - bbd.hIcon = 0; - bbd.pszTooltip = 0; - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_BKGCOLOR; - bbd.pszModuleName = "Tabsrmm"; - bbd.dwDefPos = 81; - bbd.iButtonWidth = 22; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[16]; - bbd.ptszTooltip = LPGENT("Change background color"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_SHOWNICKLIST; - bbd.dwDefPos = 22; - bbd.iButtonWidth = 22; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[19]; - bbd.ptszTooltip = LPGENT("Toggle nick list"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_FILTER; - bbd.dwDefPos = 24; - bbd.iButtonWidth = 22; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[18]; - bbd.ptszTooltip = LPGENT("Event filter - right click to setup, left click to activate/deactivate"); - CB_AddButton(0, (LPARAM)&bbd); - - bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; - bbd.dwButtonID = IDC_CHANMGR; - bbd.dwDefPos = 33; - bbd.iButtonWidth = 22; - bbd.hIcon = PluginConfig.g_buttonBarIconHandles[17]; - bbd.ptszTooltip = LPGENT("Channel manager"); - CB_AddButton(0, (LPARAM)&bbd); - - BB_RegisterSeparators(); -} - -#define MIDDLE_SEPARATOR _T(">-------M-------<") - -static int SaveTree(HWND hToolBarTree) -{ - bool RSide = false; - int count = 10; - DWORD loc_sepcout = 0; - TCHAR strbuf[128]; - - TVITEM tvi; - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE; - tvi.hItem = TreeView_GetRoot(hToolBarTree); - tvi.pszText = strbuf; - tvi.cchTextMax = sizeof(strbuf); - { - mir_cslock lck(ToolBarCS); - - while (tvi.hItem != NULL) { - TreeView_GetItem(hToolBarTree, &tvi); - - if (_tcscmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) { - RSide = true; - count = TreeView_GetCount(hToolBarTree) * 10 - count; - tvi.hItem = TreeView_GetNextSibling(hToolBarTree, tvi.hItem); - continue; - } - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd) { - if (cbd->opFlags) { - cbd->bIMButton = (cbd->opFlags & BBSF_IMBUTTON) != 0; - cbd->bChatButton = (cbd->opFlags & BBSF_CHATBUTTON) != 0; - cbd->bCanBeHidden = (cbd->opFlags & BBSF_CANBEHIDDEN) != 0; - } - if (RSide && cbd->bLSided) { - cbd->bLSided = false; - cbd->bRSided = true; - cbd->opFlags |= BBSF_NTBSWAPED; - } - else if (!RSide && cbd->bRSided) { - cbd->bLSided = true; - cbd->bRSided = false; - cbd->opFlags |= BBSF_NTBSWAPED; - } - if (!TreeView_GetCheckState(hToolBarTree, tvi.hItem)) { - cbd->bIMButton = false; - cbd->bChatButton = false; - - if (cbd->bSeparator && !strcmp(cbd->pszModuleName, "Tabsrmm_sep")) - cbd->opFlags = BBSF_NTBDESTRUCT; - } - else { - if (!cbd->bIMButton && !cbd->bChatButton) - cbd->bIMButton = true; - if (cbd->bSeparator && !strcmp(cbd->pszModuleName, "Tabsrmm_sep")) { - cbd->bHidden = 0; - cbd->opFlags &= ~BBSF_NTBDESTRUCT; - ++loc_sepcout; - } - } - - cbd->dwPosition = (DWORD)count; - CB_WriteButtonSettings(NULL, cbd); - - if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) - (RSide) ? (count -= 10) : (count += 10); - } - - HTREEITEM hItem = TreeView_GetNextSibling(hToolBarTree, tvi.hItem); - if (cbd->opFlags & BBSF_NTBDESTRUCT) - TreeView_DeleteItem(hToolBarTree, tvi.hItem); - tvi.hItem = hItem; - } - - qsort(LButtonsList.getArray(), LButtonsList.getCount(), sizeof(void*), sstSortButtons); - qsort(RButtonsList.getArray(), RButtonsList.getCount(), sizeof(void*), sstSortButtons); - } - db_set_dw(0, "TabSRMM_Toolbar", "SeparatorsCount", loc_sepcout); - dwSepCount = loc_sepcout; - return 1; -} - -HIMAGELIST himgl = NULL; - -static int BuildMenuObjectsTree(HWND hToolBarTree) -{ - HTREEITEM hti; - int iImage = 0; - - TVINSERTSTRUCT tvis; - tvis.hParent = NULL; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE; - - TreeView_DeleteAllItems(hToolBarTree); - - himgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon(himgl, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT)); - ImageList_Destroy(TreeView_GetImageList(hToolBarTree, TVSIL_NORMAL)); - TreeView_SetImageList(hToolBarTree, himgl, TVSIL_NORMAL); - - if ((RButtonsList.getCount() + LButtonsList.getCount()) == 0) - return FALSE; - - mir_cslock lck(ToolBarCS); - - for (int i = 0; i < LButtonsList.getCount(); i++) { - CustomButtonData *cbd = LButtonsList[i]; - tvis.item.lParam = (LPARAM)cbd; - - if (cbd->bSeparator) { - tvis.item.pszText = TranslateT(""); - tvis.item.iImage = tvis.item.iSelectedImage = 0; - } - else { - tvis.item.pszText = TranslateTS(cbd->ptszTooltip); - iImage = ImageList_AddIcon(himgl, Skin_GetIconByHandle(cbd->hIcon)); - tvis.item.iImage = tvis.item.iSelectedImage = iImage; - } - cbd->opFlags = 0; - hti = TreeView_InsertItem(hToolBarTree, &tvis); - - TreeView_SetCheckState(hToolBarTree, hti, (cbd->bIMButton || cbd->bChatButton)); - } - - tvis.item.lParam = 0; - tvis.item.mask |= TVIF_STATE; - tvis.item.pszText = MIDDLE_SEPARATOR; - tvis.item.stateMask = TVIS_BOLD; - tvis.item.state = TVIS_BOLD; - tvis.item.iImage = tvis.item.iSelectedImage = -1; - hti = TreeView_InsertItem(hToolBarTree, &tvis); - TreeView_SetCheckState(hToolBarTree, hti, 1); - - for (int i = RButtonsList.getCount()-1; i >= 0; i--) { - CustomButtonData *cbd = RButtonsList[i]; - tvis.item.lParam = (LPARAM)cbd; - - if (cbd->bSeparator) { - tvis.item.pszText = TranslateT(""); - tvis.item.iImage = tvis.item.iSelectedImage = -1; - } - else { - tvis.item.pszText = TranslateTS(cbd->ptszTooltip); - iImage = ImageList_AddIcon(himgl, Skin_GetIconByHandle(cbd->hIcon)); - tvis.item.iImage = tvis.item.iSelectedImage = iImage; - } - tvis.item.state = 0; - cbd->opFlags = 0; - hti = TreeView_InsertItem(hToolBarTree, &tvis); - TreeView_SetCheckState(hToolBarTree, hti, (cbd->bIMButton || cbd->bChatButton)); - } - - return 1; -} - - -BOOL drag = FALSE, bOptionsInit = TRUE; -HANDLE hDragItem = NULL; -HWND hToolBarTree = NULL; - -INT_PTR CALLBACK DlgProcToolBar(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - HTREEITEM hti; - - switch (uMsg) { - case WM_INITDIALOG: - hToolBarTree = GetDlgItem(hwndDlg, IDC_TOOLBARTREE); - { - LONG_PTR style = GetWindowLongPtr(hToolBarTree, GWL_STYLE); - style ^= TVS_CHECKBOXES; - SetWindowLongPtr(hToolBarTree, GWL_STYLE, style); - style |= TVS_CHECKBOXES; - style |= TVS_NOHSCROLL; - SetWindowLongPtr(hToolBarTree, GWL_STYLE, style); - } - { - mir_cslock lck(ToolBarCS); - BuildMenuObjectsTree(hToolBarTree); - } - - Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); - - SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_SETRANGE, 0, MAKELONG(10, 0)); - SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_SETPOS, 0, MAKELONG(PluginConfig.g_iButtonsBarGap, 0)); - TranslateDialogDefault(hwndDlg); - bOptionsInit = FALSE; - break; - - case WM_LBUTTONUP: - if (drag) { - TreeView_SetInsertMark(hToolBarTree, NULL, 0); - drag = 0; - ReleaseCapture(); - - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - ClientToScreen(hwndDlg, &hti.pt); - ScreenToClient(hToolBarTree, &hti.pt); - hti.pt.y -= TreeView_GetItemHeight(hToolBarTree) / 2; - TreeView_HitTest(hToolBarTree, &hti); - if (hDragItem == hti.hItem) break; - if (hti.flags & TVHT_ABOVE) - hti.hItem = TVI_FIRST; - - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = (HTREEITEM)hDragItem; - TreeView_GetItem(hToolBarTree, &tvi); - if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT) || (hti.hItem == TVI_FIRST)) { - TVINSERTSTRUCT tvis; - TCHAR strbuf[128]; - tvis.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = 0xFFFFFFFF; - tvis.item.pszText = strbuf; - tvis.item.cchTextMax = sizeof(strbuf); - tvis.item.hItem = (HTREEITEM)hDragItem; - TreeView_GetItem(hToolBarTree, &tvis.item); - TreeView_DeleteItem(hToolBarTree, hDragItem); - tvis.hParent = NULL; - tvis.hInsertAfter = hti.hItem; - TreeView_SelectItem(hToolBarTree, TreeView_InsertItem(hToolBarTree, &tvis)); - SendMessage((GetParent(hwndDlg)), PSM_CHANGED, 0, 0); - } - } - break; - - case WM_MOUSEMOVE: - if (drag) { - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - ClientToScreen(hwndDlg, &hti.pt); - ScreenToClient(hToolBarTree, &hti.pt); - TreeView_HitTest(hToolBarTree, &hti); - if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) { - HTREEITEM it = hti.hItem; - hti.pt.y -= TreeView_GetItemHeight(hToolBarTree) / 2; - TreeView_HitTest(hToolBarTree, &hti); - if (!(hti.flags & TVHT_ABOVE)) - TreeView_SetInsertMark(hToolBarTree, hti.hItem, 1); - else - TreeView_SetInsertMark(hToolBarTree, it, 0); - } - else { - if (hti.flags & TVHT_ABOVE) SendMessage(hToolBarTree, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); - if (hti.flags & TVHT_BELOW) SendMessage(hToolBarTree, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); - TreeView_SetInsertMark(hToolBarTree, NULL, 0); - } - } - break; - - case WM_COMMAND: - if (HIWORD(wParam) == BN_CLICKED && GetFocus() == (HWND)lParam && (HWND)lParam != hToolBarTree) - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - if ((HIWORD(wParam) == EN_CHANGE) && ((HWND)lParam == GetFocus())) - if (!bOptionsInit) - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - - switch (LOWORD(wParam)) { - case IDC_BBRESET: - CallService(MS_DB_MODULE_DELETE, NULL, LPARAM("TabSRMM_Toolbar")); - CB_HardReInit(); - BuildMenuObjectsTree(hToolBarTree); - break; - - case IDC_SEPARATOR: - hti = TreeView_GetSelection(hToolBarTree); - if (!hti) - hti = TVI_FIRST; - - CustomButtonData *cbd = new CustomButtonData(); - cbd->bSeparator = cbd->bHidden = cbd->bIMButton = cbd->bLSided = true; - cbd->dwButtonOrigID = ++dwSepCount; - cbd->pszModuleName = "Tabsrmm_sep"; - cbd->iButtonWidth = 22; - cbd->opFlags = BBSF_NTBDESTRUCT; - LButtonsList.insert(cbd); - - TVINSERTSTRUCT tvis; - tvis.hParent = NULL; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - - tvis.item.pszText = TranslateT(""); - tvis.item.iImage = tvis.item.iSelectedImage = -1; - tvis.item.lParam = (LPARAM)cbd; - hti = TreeView_InsertItem(hToolBarTree, &tvis); - - TreeView_SetCheckState(hToolBarTree, hti, (cbd->bIMButton || cbd->bChatButton)); - } - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_RESET: - CB_ReInitCustomButtons(); - dwSepCount = M.GetDword("TabSRMM_Toolbar", "SeparatorsCount", 0); - return 1; - - case PSN_APPLY: - hti = TreeView_GetSelection(hToolBarTree); - if (hti) { - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hti; - TreeView_GetItem(hToolBarTree, &tvi); - - if (tvi.lParam) { - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd) { - cbd->bIMButton = IsDlgButtonChecked(hwndDlg, IDC_IMCHECK) != 0; - cbd->bChatButton = IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK) != 0; - cbd->bCanBeHidden = IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN) != 0; - } - } - } - - SaveTree(hToolBarTree); - CB_ReInitCustomButtons(); - PluginConfig.g_iButtonsBarGap = (BYTE)SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_GETPOS, 0, 0); - - if (PluginConfig.g_iButtonsBarGap != M.GetByte("ButtonsBarGap", 1)) - M.BroadcastMessageAsync(WM_SIZE, 0, 0); - - db_set_b(0, SRMSGMOD_T, "ButtonsBarGap", PluginConfig.g_iButtonsBarGap); - - BuildMenuObjectsTree((HWND)hToolBarTree); - Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); - return 1; - } - break; - - case IDC_TOOLBARTREE: - switch (((LPNMHDR)lParam)->code) { - case TVN_BEGINDRAGA: - case TVN_BEGINDRAGW: - SetCapture(hwndDlg); - drag = 1; - hDragItem = ((LPNMTREEVIEW)lParam)->itemNew.hItem; - TreeView_SelectItem(hToolBarTree, hDragItem); - break; - - case TVN_SELCHANGINGA: - case TVN_SELCHANGINGW: - hti = TreeView_GetSelection(hToolBarTree); - if (hti != NULL) { - TCHAR strbuf[128]; - - TVITEM tvi; - tvi.hItem = hti; - tvi.pszText = strbuf; - tvi.cchTextMax = sizeof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - TreeView_GetItem(hToolBarTree, &tvi); - - if (tvi.lParam == 0 || !TreeView_GetCheckState(hToolBarTree, tvi.hItem) || !_tcscmp(tvi.pszText, MIDDLE_SEPARATOR)) - break; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd) { - cbd->opFlags = (IsDlgButtonChecked(hwndDlg, IDC_IMCHECK)) ? BBSF_IMBUTTON : 0; - cbd->opFlags |= (IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK)) ? BBSF_CHATBUTTON : 0; - cbd->opFlags |= (IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN)) ? BBSF_CANBEHIDDEN : 0; - - cbd->bIMButton = (IsDlgButtonChecked(hwndDlg, IDC_IMCHECK) ? TRUE : FALSE); - cbd->bChatButton = (IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK) ? TRUE : FALSE); - cbd->bCanBeHidden = (IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN) ? TRUE : FALSE); - } - } - break; - - case TVN_SELCHANGEDW: - case TVN_SELCHANGEDA: - hti = TreeView_GetSelection(hToolBarTree); - if (hti != NULL) { - TCHAR strbuf[128]; - - TVITEM tvi; - tvi.pszText = strbuf; - tvi.cchTextMax = sizeof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hti; - TreeView_GetItem(hToolBarTree, &tvi); - - if (!TreeView_GetCheckState(hToolBarTree, tvi.hItem) || !_tcscmp(tvi.pszText, MIDDLE_SEPARATOR)) { - Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); - break; - } - - if (tvi.lParam == 0) - break; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd) { - Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, TRUE); - CheckDlgButton(hwndDlg, IDC_IMCHECK, (cbd->bIMButton) ? 1 : 0); - CheckDlgButton(hwndDlg, IDC_CHATCHECK, (cbd->bChatButton) ? 1 : 0); - CheckDlgButton(hwndDlg, IDC_CANBEHIDDEN, (cbd->bCanBeHidden) ? 1 : 0); - } - } - break; - - case NM_CLICK: - TVHITTESTINFO hti = { 0 }; - GetCursorPos(&hti.pt); - ScreenToClient(hToolBarTree, &hti.pt); - if (TreeView_HitTest(hToolBarTree, &hti)) { - if (hti.flags&TVHT_ONITEMSTATEICON) { - SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); - if (TreeView_GetCheckState(hToolBarTree, hti.hItem)) { - Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); - CheckDlgButton(hwndDlg, IDC_IMCHECK, 1); - } - else { - Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, TRUE); - } - TreeView_SelectItem(hToolBarTree, hti.hItem); - } - } - } - } - break; - - case WM_DESTROY: - HIMAGELIST hIml = TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_TOOLBARTREE), TVSIL_NORMAL); - ImageList_Destroy(hIml); - hIml = TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_TOOLBARTREE), TVSIL_STATE); - ImageList_Destroy(hIml); - break; - } - - return FALSE; -} +#include "commonheaders.h" + +static HANDLE hButtonsBarAddButton; +static HANDLE hButtonsBarRemoveButton; +static HANDLE hButtonsBarGetButtonState; +static HANDLE hButtonsBarSetButtonState; +static HANDLE hButtonsBarModifyButton; + +HANDLE hHookButtonPressedEvt; +HANDLE hHookToolBarLoadedEvt; + +static LIST RButtonsList(1, NumericKeySortT), LButtonsList(1, NumericKeySortT); + +DWORD LastCID = 4000; +DWORD dwSepCount = 0; + +static mir_cs ToolBarCS; + +static void wipeList(LIST &list) +{ + for (int i = list.getCount()-1; i >= 0; i--) { + delete list[i]; + list.remove(i); + } +} + +static int sstSortButtons(const void *p1, const void *p2) +{ + return (*(CustomButtonData**)p1)->dwPosition - (*(CustomButtonData**)p2)->dwPosition; +} + +void CB_InitCustomButtons() +{ + dwSepCount = M.GetDword("TabSRMM_Toolbar", "SeparatorsCount", 0); + + hButtonsBarAddButton = CreateServiceFunction(MS_BB_ADDBUTTON, CB_AddButton); + hButtonsBarRemoveButton = CreateServiceFunction(MS_BB_REMOVEBUTTON, CB_RemoveButton); + hButtonsBarModifyButton = CreateServiceFunction(MS_BB_MODIFYBUTTON, CB_ModifyButton); + hButtonsBarGetButtonState = CreateServiceFunction(MS_BB_GETBUTTONSTATE, CB_GetButtonState); + hButtonsBarSetButtonState = CreateServiceFunction(MS_BB_SETBUTTONSTATE, CB_SetButtonState); + + hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED); + hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED); +} + +void CB_DeInitCustomButtons() +{ + wipeList(LButtonsList); + wipeList(RButtonsList); + + DestroyHookableEvent(hHookToolBarLoadedEvt); + DestroyHookableEvent(hHookButtonPressedEvt); + DestroyServiceFunction(hButtonsBarAddButton); + DestroyServiceFunction(hButtonsBarRemoveButton); + DestroyServiceFunction(hButtonsBarModifyButton); + DestroyServiceFunction(hButtonsBarGetButtonState); + DestroyServiceFunction(hButtonsBarSetButtonState); +} + +void CB_DestroyAllButtons(HWND hwndDlg, TWindowData *dat) +{ + HWND hwndBtn = NULL; + for (int i=0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID); + if (hwndBtn) + DestroyWindow(hwndBtn); + } + + for (int i=0; i < RButtonsList.getCount(); i++) { + CustomButtonData *cbd = RButtonsList[i]; + hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID); + if (hwndBtn) + DestroyWindow(hwndBtn); + } +} + +void CB_DestroyButton(HWND hwndDlg, TWindowData *dat, DWORD dwButtonCID, DWORD dwFlags) +{ + HWND hwndBtn = GetDlgItem(hwndDlg, dwButtonCID); + RECT rc = { 0 }; + if (hwndBtn) { + GetClientRect(hwndBtn, &rc); + if (dwFlags & BBBF_ISLSIDEBUTTON) + dat->bbLSideWidth -= rc.right; + else if (dwFlags & BBBF_ISRSIDEBUTTON) + dat->bbRSideWidth -= rc.right; + + DestroyWindow(hwndBtn); + BB_SetButtonsPos(dat); + } +} + +void CB_ChangeButton(HWND hwndDlg, TWindowData *dat, CustomButtonData *cbd) +{ + HWND hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID); + if (hwndBtn) { + if (cbd->hIcon) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); + if (cbd->ptszTooltip) + SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)cbd->ptszTooltip, BATF_TCHAR); + SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)dat->pContainer, 0); + SetWindowTextA(hwndBtn, cbd->pszModuleName); + } +} + +void CB_ReInitCustomButtons() +{ + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + + if (cbd->opFlags & BBSF_NTBSWAPED || cbd->opFlags & BBSF_NTBDESTRUCT) { + cbd->opFlags ^= BBSF_NTBSWAPED; + + if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) + RButtonsList.insert(cbd); + + LButtonsList.remove(i); + i--; + } + } + + for (int i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData* cbd = RButtonsList[i]; + if (cbd->opFlags & BBSF_NTBSWAPED || cbd->opFlags & BBSF_NTBDESTRUCT) { + cbd->opFlags ^= BBSF_NTBSWAPED; + + if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) + LButtonsList.insert(cbd); + + RButtonsList.remove(i); + i--; + } + } + M.BroadcastMessage(DM_BBNEEDUPDATE, 0, 0); + M.BroadcastMessage(DM_LOADBUTTONBARICONS, 0, 0); +} + +void CB_HardReInit() +{ + M.BroadcastMessage(DM_CBDESTROY, 0, 0); + { + mir_cslock lck(ToolBarCS); + wipeList(LButtonsList); + wipeList(RButtonsList); + } + LastCID = 4000; + dwSepCount = 0; + + CB_InitDefaultButtons(); + NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0); +} + +static INT_PTR CB_AddButton(WPARAM, LPARAM lParam) +{ + BBButton *bbdi = (BBButton *)lParam; + if (!bbdi || bbdi->cbSize != sizeof(BBButton)) + return 1; + + CustomButtonData *cbd = new CustomButtonData(); + + if (!bbdi->iButtonWidth && (bbdi->bbbFlags & BBBF_ISARROWBUTTON)) + cbd->iButtonWidth = DPISCALEX_S(34); + else if (!bbdi->iButtonWidth) + cbd->iButtonWidth = DPISCALEX_S(22); + else + cbd->iButtonWidth = DPISCALEX_S(bbdi->iButtonWidth); + + cbd->pszModuleName = mir_strdup(bbdi->pszModuleName); + + if (bbdi->ptszTooltip) { + if (bbdi->bbbFlags & BBBF_ANSITOOLTIP) + cbd->ptszTooltip = mir_a2u(bbdi->pszTooltip); + else + cbd->ptszTooltip = mir_tstrdup(bbdi->ptszTooltip); + } + else cbd->ptszTooltip = NULL; + + cbd->dwButtonOrigID = bbdi->dwButtonID; + cbd->hIcon = bbdi->hIcon; + cbd->dwPosition = bbdi->dwDefPos; + cbd->dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID; + //ugly workaround for smileys plugins + cbd->dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? (cbd->dwButtonCID == IDOK ? IDC_SENDMENU : (cbd->dwButtonCID + 1)) : 0; + cbd->bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; + cbd->bLSided = (bbdi->bbbFlags & BBBF_ISLSIDEBUTTON) != 0; + cbd->bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; + cbd->bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; + cbd->bSeparator = (bbdi->bbbFlags & BBBF_ISDUMMYBUTTON) != 0; + cbd->bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; + cbd->bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; + cbd->bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; + cbd->bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0; + + CB_GetButtonSettings(NULL, cbd); + + if (cbd->bLSided) + LButtonsList.insert(cbd); + else if (cbd->bRSided) + RButtonsList.insert(cbd); + else return 1; + + if (cbd->dwButtonCID != cbd->dwButtonOrigID) + LastCID++; + if (cbd->dwArrowCID == LastCID) + LastCID++; + + M.BroadcastMessage(DM_BBNEEDUPDATE, 0, 0); + return 0; +} + +static INT_PTR CB_GetButtonState(WPARAM wParam, LPARAM lParam) +{ + if (wParam == 0 || lParam == 0) + return 1; + + DWORD tempCID = 0; + bool realbutton = false; + BBButton *bbdi = (BBButton *)lParam; + bbdi->bbbFlags = 0; + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { + realbutton = true; + tempCID = cbd->dwButtonCID; + } + } + if (!realbutton) + for (int i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData* cbd = RButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { + realbutton = true; + tempCID = cbd->dwButtonCID; + } + } + + if (!realbutton) + return 1; + + HWND hwndDlg = M.FindWindow(wParam); + if (!hwndDlg) + return 1; + + HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); + bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED); + return 0; +} + +static INT_PTR CB_SetButtonState(WPARAM wParam, LPARAM lParam) +{ + if (wParam == 0 || lParam == 0) + return 1; + + bool realbutton = false; + DWORD tempCID = 0; + BBButton *bbdi = (BBButton *)lParam; + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { + realbutton = true; + tempCID = cbd->dwButtonCID; + } + } + if (!realbutton) + for (int i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData* cbd = RButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { + realbutton = true; + tempCID = cbd->dwButtonCID; + } + } + + if (!realbutton) + return 1; + + HWND hwndDlg = M.FindWindow(wParam); + if (!hwndDlg) + return 1; + + SetDlgItemTextA(hwndDlg, tempCID, bbdi->pszModuleName); + if (bbdi->hIcon) + SendDlgItemMessage(hwndDlg, tempCID, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(bbdi->hIcon)); + if (bbdi->pszTooltip) + SendDlgItemMessage(hwndDlg, tempCID, BUTTONADDTOOLTIP, (WPARAM)bbdi->ptszTooltip, (bbdi->bbbFlags & BBBF_ANSITOOLTIP) ? 0 : BATF_TCHAR); + if (bbdi->bbbFlags) { + Utils::showDlgControl(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_HIDDEN) ? SW_HIDE : SW_SHOW); + Utils::enableDlgControl(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_DISABLED) ? 0 : 1); + CheckDlgButton(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_PUSHED) ? 1 : 0); + CheckDlgButton(hwndDlg, tempCID, (bbdi->bbbFlags&BBSF_RELEASED) ? 0 : 1); + } + return 0; +} + +static INT_PTR CB_RemoveButton(WPARAM, LPARAM lParam) +{ + BBButton *bbdi = (BBButton *)lParam; + if (!bbdi) + return 1; + + DWORD tempCID = 0; + DWORD dwFlags = 0; + { + mir_cslock lck(ToolBarCS); + + for (int i = LButtonsList.getCount()-1; i >= 0; i--) { + CustomButtonData *cbd = LButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && cbd->dwButtonOrigID == bbdi->dwButtonID) { + tempCID = cbd->dwButtonCID; + dwFlags = cbd->bLSided ? BBBF_ISLSIDEBUTTON : BBBF_ISRSIDEBUTTON; + LButtonsList.remove(i); + } + } + + if (!tempCID) { + for (int i = RButtonsList.getCount()-1; i >= 0; i--) { + CustomButtonData *cbd = RButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && cbd->dwButtonOrigID == bbdi->dwButtonID) { + tempCID = cbd->dwButtonCID; + dwFlags = cbd->bLSided ? BBBF_ISLSIDEBUTTON : BBBF_ISRSIDEBUTTON; + RButtonsList.remove(i); + } + } + } + } + + if (tempCID) + M.BroadcastMessage(DM_CBDESTROY, (WPARAM)tempCID, (LPARAM)dwFlags); + return 0; +} + +static INT_PTR CB_ModifyButton(WPARAM, LPARAM lParam) +{ + BBButton *bbdi = (BBButton *)lParam; + if (!bbdi) + return 1; + + bool bFound = 1; + CustomButtonData *cbd = NULL; + { + mir_cslock lck(ToolBarCS); + + for (int i = 0; i < LButtonsList.getCount(); i++) { + cbd = LButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { + bFound = true; + break; + } + } + + if (!bFound) { + for (int i = 0; i < RButtonsList.getCount(); i++) { + cbd = RButtonsList[i]; + if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) { + bFound = true; + break; + } + } + } + + if (bFound) { + if (bbdi->pszTooltip) { + mir_free(cbd->ptszTooltip); + if (bbdi->bbbFlags & BBBF_ANSITOOLTIP) + cbd->ptszTooltip = mir_a2u(bbdi->pszTooltip); + else + cbd->ptszTooltip = mir_tstrdup(bbdi->ptszTooltip); + } + if (bbdi->hIcon) + cbd->hIcon = bbdi->hIcon; + if (bbdi->bbbFlags) { + cbd->bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; + cbd->bLSided = (bbdi->bbbFlags & BBBF_ISLSIDEBUTTON) != 0; + cbd->bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; + cbd->bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; + cbd->bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; + cbd->bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; + cbd->bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; + } + } + } + + if (bFound) + M.BroadcastMessage(DM_BBNEEDUPDATE, 0, (LPARAM)cbd); + return 0; +} + +void BB_UpdateIcons(HWND hdlg, TWindowData *dat) +{ + HWND hwndBtn = NULL; + + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + if (cbd) { + if (!cbd->bSeparator) + hwndBtn = GetDlgItem(hdlg, cbd->dwButtonCID); + + if (hwndBtn && cbd->hIcon) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); + } + } + + hwndBtn = NULL; + for (int i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData *cbd = RButtonsList[i]; + if (cbd) { + if (!cbd->bSeparator) + hwndBtn = GetDlgItem(hdlg, cbd->dwButtonCID); + + if (hwndBtn && cbd->hIcon) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); + } + } +} + +void TSAPI BB_InitDlgButtons(TWindowData *dat) +{ + RECT rect; + int i; + int lwidth = 0, rwidth = 0; + RECT rcSplitter; + POINT ptSplitter; + int splitterY; + BYTE gap = DPISCALEX_S(PluginConfig.g_iButtonsBarGap); + BOOL isThemed = TRUE; + int cx = 0, cy = 0; + int lcount = LButtonsList.getCount(); + int rcount = RButtonsList.getCount(); + HWND hdlg = dat->hwnd; + + if (dat == 0 || hdlg == 0) { return; } + if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKBUTTONSNPRESSED].IGNORED && + !SkinItems[ID_EXTBKBUTTONSPRESSED].IGNORED && !SkinItems[ID_EXTBKBUTTONSMOUSEOVER].IGNORED) { + isThemed = FALSE; + } + + GetWindowRect(GetDlgItem(hdlg, (dat->bType == SESSIONTYPE_IM) ? IDC_SPLITTER : IDC_SPLITTERY), &rcSplitter); + ptSplitter.x = 0; + ptSplitter.y = rcSplitter.top; + ScreenToClient(hdlg, &ptSplitter); + + GetClientRect(hdlg, &rect); + splitterY = ptSplitter.y - DPISCALEY_S(1); + + HWND hwndBtn = NULL; + + for (i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData *cbd = RButtonsList[i]; + if (((dat->bType == SESSIONTYPE_IM && cbd->bIMButton) || (dat->bType == SESSIONTYPE_CHAT && cbd->bChatButton))) { + if (!cbd->bHidden) + rwidth += cbd->iButtonWidth + gap; + if (!cbd->bHidden && !cbd->bCanBeHidden) + dat->iButtonBarReallyNeeds += cbd->iButtonWidth + gap; + if (!cbd->bSeparator && !GetDlgItem(hdlg, cbd->dwButtonCID)) { + hwndBtn = CreateWindowEx(0, _T("MButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, rect.right - rwidth + gap, splitterY, cbd->iButtonWidth, DPISCALEY_S(22), hdlg, (HMENU)cbd->dwButtonCID, g_hInst, NULL); + CustomizeButton(hwndBtn); + } + if (!cbd->bSeparator && hwndBtn) { + SendMessage(hwndBtn, BUTTONSETASFLATBTN, TRUE, 0); + SendMessage(hwndBtn, BUTTONSETASTHEMEDBTN, isThemed != 0, 0); + if (cbd->hIcon) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); + if (cbd->ptszTooltip) + SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), BATF_TCHAR); + SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)dat->pContainer, 0); + SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, TRUE, 0); + + if (hwndBtn && cbd->dwArrowCID) + SendMessage(hwndBtn, BUTTONSETARROW, cbd->dwArrowCID, 0); + if (hwndBtn && cbd->bPushButton) + SendMessage(hwndBtn, BUTTONSETASPUSHBTN, TRUE, 0); + } + } + else if (GetDlgItem(hdlg, cbd->dwButtonCID)) + DestroyWindow(GetDlgItem(hdlg, cbd->dwButtonCID)); + + if (cbd->bDisabled) + EnableWindow(hwndBtn, 0); + if (cbd->bHidden) + ShowWindow(hwndBtn, SW_HIDE); + + } + + hwndBtn = NULL; + + for (i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + if (((dat->bType == SESSIONTYPE_IM && cbd->bIMButton) || (dat->bType == SESSIONTYPE_CHAT && cbd->bChatButton))) { + if (!cbd->bSeparator && !GetDlgItem(hdlg, cbd->dwButtonCID)) { + hwndBtn = CreateWindowEx(0, _T("MButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, 2 + lwidth, splitterY, + cbd->iButtonWidth, DPISCALEY_S(22), hdlg, (HMENU)cbd->dwButtonCID, g_hInst, NULL); + CustomizeButton(hwndBtn); + } + if (!cbd->bHidden) + lwidth += cbd->iButtonWidth + gap; + if (!cbd->bHidden && !cbd->bCanBeHidden) + dat->iButtonBarReallyNeeds += cbd->iButtonWidth + gap; + if (!cbd->bSeparator && hwndBtn) { + SendMessage(hwndBtn, BUTTONSETASFLATBTN, TRUE, 0); + SendMessage(hwndBtn, BUTTONSETASTHEMEDBTN, isThemed != 0, 0); + if (cbd->hIcon) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_GetIconByHandle(cbd->hIcon)); + if (cbd->ptszTooltip) + SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), BATF_TCHAR); + SendMessage(hwndBtn, BUTTONSETCONTAINER, (LPARAM)dat->pContainer, 0); + SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, TRUE, 0); + + if (hwndBtn && cbd->dwArrowCID) + SendMessage(hwndBtn, BUTTONSETARROW, cbd->dwArrowCID, 0); + if (hwndBtn && cbd->bPushButton) + SendMessage(hwndBtn, BUTTONSETASPUSHBTN, TRUE, 0); + } + } + else if (GetDlgItem(hdlg, cbd->dwButtonCID)) + DestroyWindow(GetDlgItem(hdlg, cbd->dwButtonCID)); + + if (cbd->bDisabled) + EnableWindow(hwndBtn, 0); + if (cbd->bHidden) + ShowWindow(hwndBtn, SW_HIDE); + } + + dat->bbLSideWidth = lwidth; + dat->bbRSideWidth = rwidth; +} + +void TSAPI BB_RedrawButtons(TWindowData *dat) +{ + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + HWND hwnd = GetDlgItem(dat->hwnd, cbd->dwButtonCID); + if (hwnd) + InvalidateRect(hwnd, 0, TRUE); + } + + for (int i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData *cbd = RButtonsList[i]; + HWND hwnd = GetDlgItem(dat->hwnd, cbd->dwButtonCID); + if (hwnd) + InvalidateRect(hwnd, 0, TRUE); + } + + HWND hwndToggleSideBar = GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_TOGGLESIDEBAR : IDC_CHAT_TOGGLESIDEBAR); + if (hwndToggleSideBar && IsWindow(hwndToggleSideBar)) + InvalidateRect(hwndToggleSideBar, 0, TRUE); +} + +BOOL TSAPI BB_SetButtonsPos(TWindowData *dat) +{ + + if (!dat || !IsWindowVisible(dat->hwnd)) + return 0; + + HWND hwnd = dat->hwnd; + RECT rect; + int i; + HWND hwndBtn = 0; + + BYTE gap = DPISCALEX_S(PluginConfig.g_iButtonsBarGap); + bool showToolbar = !(dat->pContainer->dwFlags & CNT_HIDETOOLBAR); + bool bBottomToolbar = (dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR) != 0; + + HDWP hdwp = BeginDeferWindowPos(LButtonsList.getCount() + RButtonsList.getCount() + 1); + + HWND hwndToggleSideBar = GetDlgItem(hwnd, dat->bType == SESSIONTYPE_IM ? IDC_TOGGLESIDEBAR : IDC_CHAT_TOGGLESIDEBAR); + ShowWindow(hwndToggleSideBar, (showToolbar && dat->pContainer->SideBar->isActive()) ? SW_SHOW : SW_HIDE); + + mir_cslock lck(ToolBarCS); + + RECT rcSplitter; + GetWindowRect(GetDlgItem(hwnd, (dat->bType == SESSIONTYPE_IM) ? IDC_SPLITTER : IDC_SPLITTERY), &rcSplitter); + + POINT ptSplitter = { 0, rcSplitter.top }; + ScreenToClient(hwnd, &ptSplitter); + + GetClientRect(hwnd, &rect); + + int splitterY = (!bBottomToolbar) ? ptSplitter.y - DPISCALEY_S(1) : rect.bottom; + int tempL = dat->bbLSideWidth, tempR = dat->bbRSideWidth; + int lwidth = 0, rwidth = 0; + int iOff = DPISCALEY_S((PluginConfig.g_DPIscaleY > 1.0) ? (dat->bType == SESSIONTYPE_IM ? 22 : 23) : 22); + + int foravatar = 0; + if ((rect.bottom - ptSplitter.y - (rcSplitter.bottom - rcSplitter.top) /*- DPISCALEY(2)*/ - (bBottomToolbar ? DPISCALEY_S(24) : 0) < dat->pic.cy - DPISCALEY_S(2)) && dat->bShowAvatar && !PluginConfig.m_AlwaysFullToolbarWidth) + foravatar = dat->pic.cx + gap; + + if ((dat->pContainer->dwFlags & CNT_SIDEBAR) && (dat->pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT)) { + DeferWindowPos(hdwp, hwndToggleSideBar, NULL, 4, 2 + splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + lwidth += 10; + tempL -= 10; + } + + for (i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + if (((dat->bType == SESSIONTYPE_IM) && cbd->bIMButton) || ((dat->bType == SESSIONTYPE_CHAT) && cbd->bChatButton)) { + hwndBtn = GetDlgItem(hwnd, cbd->dwButtonCID); + + if (!showToolbar) { + ShowWindow(hwndBtn, SW_HIDE); + DeferWindowPos(hdwp, hwndBtn, NULL, lwidth, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) + lwidth += cbd->iButtonWidth + gap; + if (!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden) + cbd->bAutoHidden = 1; + continue; + } + if (!cbd->bCanBeHidden && !cbd->bHidden && !(!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden)) { + ShowWindow(hwndBtn, SW_SHOW); + cbd->bAutoHidden = 0; + } + + if (!cbd->bSeparator && !IsWindowVisible(hwndBtn) && !IsWindowEnabled(hwndBtn) && !cbd->bAutoHidden) + tempL -= cbd->iButtonWidth + gap; + + if (cbd->bCanBeHidden && !cbd->bHidden && (cbd->bSeparator || !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden))) { + if (tempL + tempR > (rect.right - foravatar)) { + ShowWindow(hwndBtn, SW_HIDE); + cbd->bAutoHidden = 1; + tempL -= cbd->iButtonWidth + gap; + } + else if (cbd->bAutoHidden) { + ShowWindow(hwndBtn, SW_SHOW); + cbd->bAutoHidden = 0; + } + } + DeferWindowPos(hdwp, hwndBtn, NULL, lwidth, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE);// SWP_NOCOPYBITS); + if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) + lwidth += cbd->iButtonWidth + gap; + } + } + + if ((dat->pContainer->dwFlags & CNT_SIDEBAR) && (dat->pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_RIGHT)) { + DeferWindowPos(hdwp, hwndToggleSideBar, NULL, rect.right - foravatar - 10, 2 + splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + rwidth += 12; + tempR -= 12; + } + + for (i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData *cbd = RButtonsList[i]; + if (((dat->bType == SESSIONTYPE_IM) && cbd->bIMButton) || ((dat->bType == SESSIONTYPE_CHAT) && cbd->bChatButton)) { + hwndBtn = GetDlgItem(hwnd, cbd->dwButtonCID); + + if (!showToolbar) { + ShowWindow(hwndBtn, SW_HIDE); + if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) + rwidth += cbd->iButtonWidth + gap; + DeferWindowPos(hdwp, hwndBtn, NULL, rect.right - foravatar - rwidth + gap, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + if (!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden) + cbd->bAutoHidden = 1; + continue; + } + if (!cbd->bCanBeHidden && !cbd->bHidden && !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden)) { + ShowWindow(hwndBtn, SW_SHOW); + cbd->bAutoHidden = 0; + } + + if (!cbd->bSeparator && !IsWindowVisible(hwndBtn) && !IsWindowEnabled(hwndBtn) && !cbd->bAutoHidden) + tempR -= cbd->iButtonWidth + gap; + + if (cbd->bCanBeHidden && !cbd->bHidden && (cbd->bSeparator || !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden))) { + if (tempL + tempR > (rect.right - foravatar)) { + ShowWindow(hwndBtn, SW_HIDE); + cbd->bAutoHidden = 1; + tempR -= cbd->iButtonWidth + gap; + } + else if (cbd->bAutoHidden) { + ShowWindow(hwndBtn, SW_SHOW); + cbd->bAutoHidden = 0; + } + } + + if (IsWindowVisible(hwndBtn) || (cbd->bSeparator && !(cbd->bAutoHidden || cbd->bHidden))) + rwidth += cbd->iButtonWidth + gap; + DeferWindowPos(hdwp, hwndBtn, NULL, rect.right - foravatar - rwidth + gap, splitterY - iOff, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + } + } + + return EndDeferWindowPos(hdwp); +} + +void TSAPI BB_CustomButtonClick(TWindowData *dat, DWORD idFrom, HWND hwndFrom, BOOL code) +{ + RECT rc; + int i; + BOOL bFromArrow = 0; + CustomButtonClickData cbcd = { 0 }; + + GetWindowRect(hwndFrom, &rc); + cbcd.pt.x = rc.left; + cbcd.pt.y = rc.bottom; + + for (i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData* cbd = LButtonsList[i]; + if (cbd->dwButtonCID == idFrom) { + cbcd.pszModule = cbd->pszModuleName; + cbcd.dwButtonId = cbd->dwButtonOrigID; + } + else if (cbd->dwArrowCID == idFrom) { + bFromArrow = 1; + cbcd.pszModule = cbd->pszModuleName; + cbcd.dwButtonId = cbd->dwButtonOrigID; + } + } + + if (!cbcd.pszModule) + for (i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData* cbd = RButtonsList[i]; + if (cbd->dwButtonCID == idFrom) { + cbcd.pszModule = cbd->pszModuleName; + cbcd.dwButtonId = cbd->dwButtonOrigID; + } + else if (cbd->dwArrowCID == idFrom) { + bFromArrow = 1; + cbcd.pszModule = cbd->pszModuleName; + cbcd.dwButtonId = cbd->dwButtonOrigID; + } + } + + cbcd.cbSize = sizeof(CustomButtonClickData); + cbcd.hwndFrom = dat->hwnd; + cbcd.hContact = dat->hContact; + cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0); + + NotifyEventHooks(hHookButtonPressedEvt, dat->hContact, (LPARAM)&cbcd); +} + + +void CB_GetButtonSettings(MCONTACT hContact, CustomButtonData *cbd) +{ + DBVARIANT dbv = { 0 }; + char SettingName[1024] = { '\0' }; + char* token = NULL; + + //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden + + mir_snprintf(SettingName, sizeof(SettingName), "%s_%d", cbd->pszModuleName, cbd->dwButtonOrigID); + + if (!db_get_s(hContact, "TabSRMM_Toolbar", SettingName, &dbv)) { + token = strtok(dbv.pszVal, "_"); + cbd->dwPosition = (DWORD)atoi(token); + token = strtok(NULL, "_"); + cbd->bIMButton = atoi(token) != 0; + token = strtok(NULL, "_"); + cbd->bChatButton = atoi(token) != 0; + token = strtok(NULL, "_"); + cbd->bLSided = atoi(token) != 0; + token = strtok(NULL, "_"); + cbd->bRSided = atoi(token) != 0; + token = strtok(NULL, "_"); + cbd->bCanBeHidden = atoi(token) != 0; + + db_free(&dbv); + } +} + +void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd) +{ + char SettingName[1024] = { '\0' }; + char SettingParameter[1024] = { '\0' }; + + //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden + + mir_snprintf(SettingName, sizeof(SettingName), "%s_%d", cbd->pszModuleName, cbd->dwButtonOrigID); + mir_snprintf(SettingParameter, sizeof(SettingParameter), "%d_%u_%u_%u_%u_%u", cbd->dwPosition, cbd->bIMButton, cbd->bChatButton, cbd->bLSided, cbd->bRSided, cbd->bCanBeHidden); + if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) + db_set_s(hContact, "TabSRMM_Toolbar", SettingName, SettingParameter); + else + db_unset(hContact, "TabSRMM_Toolbar", SettingName); +} + +void BB_RegisterSeparators() +{ + BBButton bbd = { 0 }; + DWORD i = 0; + bbd.cbSize = sizeof(BBButton); + bbd.pszModuleName = "Tabsrmm_sep"; + for (; dwSepCount > i; i++) { + bbd.bbbFlags = BBBF_ISDUMMYBUTTON | BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON; + bbd.dwButtonID = i + 1; + bbd.dwDefPos = 410 + i; + CB_AddButton(0, (LPARAM)&bbd); + } +} + +void BB_RefreshTheme(const TWindowData *dat) +{ + for (int i = 0; i < RButtonsList.getCount(); i++) { + CustomButtonData* cbd = RButtonsList[i]; + SendMessage(GetDlgItem(dat->hwnd, cbd->dwButtonCID), WM_THEMECHANGED, 0, 0); + } + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData* cbd = LButtonsList[i]; + SendMessage(GetDlgItem(dat->hwnd, cbd->dwButtonCID), WM_THEMECHANGED, 0, 0); + } +} + +void CB_InitDefaultButtons() +{ + BBButton bbd = { 0 }; + bbd.cbSize = sizeof(BBButton); + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISARROWBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_PROTOCOL; + bbd.dwDefPos = 10; + bbd.hIcon = LoadSkinnedIconHandle(SKINICON_OTHER_CONNECTING); + bbd.pszModuleName = "Tabsrmm"; + bbd.ptszTooltip = LPGENT("Protocol Button"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISARROWBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_NAME; + bbd.dwDefPos = 20; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[20]; + bbd.ptszTooltip = LPGENT("Info button"); + CB_AddButton(0, (LPARAM)&bbd); + + if (PluginConfig.g_SmileyAddAvail) { + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_SMILEYBTN; + bbd.iButtonWidth = 0; + bbd.dwDefPos = 30; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[9]; + bbd.ptszTooltip = LPGENT("Insert Emoticon"); + CB_AddButton(0, (LPARAM)&bbd); + } + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_FONTBOLD; + bbd.dwDefPos = 40; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[10]; + bbd.ptszTooltip = LPGENT("Bold text"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_FONTITALIC; + bbd.dwDefPos = 50; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[11]; + bbd.ptszTooltip = LPGENT("Italic text"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_FONTUNDERLINE; + bbd.dwDefPos = 60; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[12]; + bbd.ptszTooltip = LPGENT("Underlined text"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_FONTSTRIKEOUT; + bbd.dwDefPos = 70; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[15]; + bbd.ptszTooltip = LPGENT("Strike-through text"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_CANBEHIDDEN | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_FONTFACE; + bbd.dwDefPos = 80; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[14]; + bbd.ptszTooltip = LPGENT("Select font color"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_ISARROWBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDOK; + bbd.dwDefPos = 10; + bbd.iButtonWidth = 51; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[6]; + bbd.ptszTooltip = LPGENT("Send message\nClick dropdown arrow for sending options"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_SAVE; + bbd.dwDefPos = 20; + bbd.iButtonWidth = 0; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[8]; + bbd.ptszTooltip = LPGENT("Close session"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_QUOTE; + bbd.dwDefPos = 30; + bbd.iButtonWidth = 0; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[4]; + bbd.ptszTooltip = LPGENT("Quote last message OR selected text"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_TIME; + bbd.dwDefPos = 40; + bbd.iButtonWidth = 0; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[2]; + bbd.ptszTooltip = LPGENT("Message Log Options"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_HISTORY; + bbd.dwDefPos = 50; + bbd.iButtonWidth = 0; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[0]; + bbd.ptszTooltip = LPGENT("View User's History"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_PIC; + bbd.dwDefPos = 60; + bbd.iButtonWidth = 0; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[7]; + bbd.ptszTooltip = LPGENT("Edit user notes"); + CB_AddButton(0, (LPARAM)&bbd); + + //chat buttons + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISDUMMYBUTTON; + bbd.dwButtonID = 1; + bbd.pszModuleName = "tb_splitter"; + bbd.dwDefPos = 31; + bbd.iButtonWidth = 22; + bbd.hIcon = 0; + bbd.pszTooltip = 0; + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISDUMMYBUTTON; + bbd.dwButtonID = 2; + bbd.dwDefPos = 22; + bbd.iButtonWidth = 22; + bbd.hIcon = 0; + bbd.pszTooltip = 0; + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISDUMMYBUTTON; + bbd.dwButtonID = 3; + bbd.dwDefPos = 71; + bbd.iButtonWidth = 22; + bbd.hIcon = 0; + bbd.pszTooltip = 0; + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISLSIDEBUTTON | BBBF_ISPUSHBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_BKGCOLOR; + bbd.pszModuleName = "Tabsrmm"; + bbd.dwDefPos = 81; + bbd.iButtonWidth = 22; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[16]; + bbd.ptszTooltip = LPGENT("Change background color"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_SHOWNICKLIST; + bbd.dwDefPos = 22; + bbd.iButtonWidth = 22; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[19]; + bbd.ptszTooltip = LPGENT("Toggle nick list"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_FILTER; + bbd.dwDefPos = 24; + bbd.iButtonWidth = 22; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[18]; + bbd.ptszTooltip = LPGENT("Event filter - right click to setup, left click to activate/deactivate"); + CB_AddButton(0, (LPARAM)&bbd); + + bbd.bbbFlags = BBBF_ISCHATBUTTON | BBBF_ISRSIDEBUTTON | BBBF_CREATEBYID; + bbd.dwButtonID = IDC_CHANMGR; + bbd.dwDefPos = 33; + bbd.iButtonWidth = 22; + bbd.hIcon = PluginConfig.g_buttonBarIconHandles[17]; + bbd.ptszTooltip = LPGENT("Channel manager"); + CB_AddButton(0, (LPARAM)&bbd); + + BB_RegisterSeparators(); +} + +#define MIDDLE_SEPARATOR _T(">-------M-------<") + +static int SaveTree(HWND hToolBarTree) +{ + bool RSide = false; + int count = 10; + DWORD loc_sepcout = 0; + TCHAR strbuf[128]; + + TVITEM tvi; + tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE; + tvi.hItem = TreeView_GetRoot(hToolBarTree); + tvi.pszText = strbuf; + tvi.cchTextMax = sizeof(strbuf); + { + mir_cslock lck(ToolBarCS); + + while (tvi.hItem != NULL) { + TreeView_GetItem(hToolBarTree, &tvi); + + if (_tcscmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) { + RSide = true; + count = TreeView_GetCount(hToolBarTree) * 10 - count; + tvi.hItem = TreeView_GetNextSibling(hToolBarTree, tvi.hItem); + continue; + } + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + if (cbd) { + if (cbd->opFlags) { + cbd->bIMButton = (cbd->opFlags & BBSF_IMBUTTON) != 0; + cbd->bChatButton = (cbd->opFlags & BBSF_CHATBUTTON) != 0; + cbd->bCanBeHidden = (cbd->opFlags & BBSF_CANBEHIDDEN) != 0; + } + if (RSide && cbd->bLSided) { + cbd->bLSided = false; + cbd->bRSided = true; + cbd->opFlags |= BBSF_NTBSWAPED; + } + else if (!RSide && cbd->bRSided) { + cbd->bLSided = true; + cbd->bRSided = false; + cbd->opFlags |= BBSF_NTBSWAPED; + } + if (!TreeView_GetCheckState(hToolBarTree, tvi.hItem)) { + cbd->bIMButton = false; + cbd->bChatButton = false; + + if (cbd->bSeparator && !strcmp(cbd->pszModuleName, "Tabsrmm_sep")) + cbd->opFlags = BBSF_NTBDESTRUCT; + } + else { + if (!cbd->bIMButton && !cbd->bChatButton) + cbd->bIMButton = true; + if (cbd->bSeparator && !strcmp(cbd->pszModuleName, "Tabsrmm_sep")) { + cbd->bHidden = 0; + cbd->opFlags &= ~BBSF_NTBDESTRUCT; + ++loc_sepcout; + } + } + + cbd->dwPosition = (DWORD)count; + CB_WriteButtonSettings(NULL, cbd); + + if (!(cbd->opFlags & BBSF_NTBDESTRUCT)) + (RSide) ? (count -= 10) : (count += 10); + } + + HTREEITEM hItem = TreeView_GetNextSibling(hToolBarTree, tvi.hItem); + if (cbd->opFlags & BBSF_NTBDESTRUCT) + TreeView_DeleteItem(hToolBarTree, tvi.hItem); + tvi.hItem = hItem; + } + + qsort(LButtonsList.getArray(), LButtonsList.getCount(), sizeof(void*), sstSortButtons); + qsort(RButtonsList.getArray(), RButtonsList.getCount(), sizeof(void*), sstSortButtons); + } + db_set_dw(0, "TabSRMM_Toolbar", "SeparatorsCount", loc_sepcout); + dwSepCount = loc_sepcout; + return 1; +} + +HIMAGELIST himgl = NULL; + +static int BuildMenuObjectsTree(HWND hToolBarTree) +{ + HTREEITEM hti; + int iImage = 0; + + TVINSERTSTRUCT tvis; + tvis.hParent = NULL; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE; + + TreeView_DeleteAllItems(hToolBarTree); + + himgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2); + ImageList_AddIcon(himgl, LoadSkinnedIcon(SKINICON_OTHER_SMALLDOT)); + ImageList_Destroy(TreeView_GetImageList(hToolBarTree, TVSIL_NORMAL)); + TreeView_SetImageList(hToolBarTree, himgl, TVSIL_NORMAL); + + if ((RButtonsList.getCount() + LButtonsList.getCount()) == 0) + return FALSE; + + mir_cslock lck(ToolBarCS); + + for (int i = 0; i < LButtonsList.getCount(); i++) { + CustomButtonData *cbd = LButtonsList[i]; + tvis.item.lParam = (LPARAM)cbd; + + if (cbd->bSeparator) { + tvis.item.pszText = TranslateT(""); + tvis.item.iImage = tvis.item.iSelectedImage = 0; + } + else { + tvis.item.pszText = TranslateTS(cbd->ptszTooltip); + iImage = ImageList_AddIcon(himgl, Skin_GetIconByHandle(cbd->hIcon)); + tvis.item.iImage = tvis.item.iSelectedImage = iImage; + } + cbd->opFlags = 0; + hti = TreeView_InsertItem(hToolBarTree, &tvis); + + TreeView_SetCheckState(hToolBarTree, hti, (cbd->bIMButton || cbd->bChatButton)); + } + + tvis.item.lParam = 0; + tvis.item.mask |= TVIF_STATE; + tvis.item.pszText = MIDDLE_SEPARATOR; + tvis.item.stateMask = TVIS_BOLD; + tvis.item.state = TVIS_BOLD; + tvis.item.iImage = tvis.item.iSelectedImage = -1; + hti = TreeView_InsertItem(hToolBarTree, &tvis); + TreeView_SetCheckState(hToolBarTree, hti, 1); + + for (int i = RButtonsList.getCount()-1; i >= 0; i--) { + CustomButtonData *cbd = RButtonsList[i]; + tvis.item.lParam = (LPARAM)cbd; + + if (cbd->bSeparator) { + tvis.item.pszText = TranslateT(""); + tvis.item.iImage = tvis.item.iSelectedImage = -1; + } + else { + tvis.item.pszText = TranslateTS(cbd->ptszTooltip); + iImage = ImageList_AddIcon(himgl, Skin_GetIconByHandle(cbd->hIcon)); + tvis.item.iImage = tvis.item.iSelectedImage = iImage; + } + tvis.item.state = 0; + cbd->opFlags = 0; + hti = TreeView_InsertItem(hToolBarTree, &tvis); + TreeView_SetCheckState(hToolBarTree, hti, (cbd->bIMButton || cbd->bChatButton)); + } + + return 1; +} + + +BOOL drag = FALSE, bOptionsInit = TRUE; +HANDLE hDragItem = NULL; +HWND hToolBarTree = NULL; + +INT_PTR CALLBACK DlgProcToolBar(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HTREEITEM hti; + + switch (uMsg) { + case WM_INITDIALOG: + hToolBarTree = GetDlgItem(hwndDlg, IDC_TOOLBARTREE); + { + LONG_PTR style = GetWindowLongPtr(hToolBarTree, GWL_STYLE); + style ^= TVS_CHECKBOXES; + SetWindowLongPtr(hToolBarTree, GWL_STYLE, style); + style |= TVS_CHECKBOXES; + style |= TVS_NOHSCROLL; + SetWindowLongPtr(hToolBarTree, GWL_STYLE, style); + } + { + mir_cslock lck(ToolBarCS); + BuildMenuObjectsTree(hToolBarTree); + } + + Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); + + SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_SETRANGE, 0, MAKELONG(10, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_SETPOS, 0, MAKELONG(PluginConfig.g_iButtonsBarGap, 0)); + TranslateDialogDefault(hwndDlg); + bOptionsInit = FALSE; + break; + + case WM_LBUTTONUP: + if (drag) { + TreeView_SetInsertMark(hToolBarTree, NULL, 0); + drag = 0; + ReleaseCapture(); + + TVHITTESTINFO hti; + hti.pt.x = (short)LOWORD(lParam); + hti.pt.y = (short)HIWORD(lParam); + ClientToScreen(hwndDlg, &hti.pt); + ScreenToClient(hToolBarTree, &hti.pt); + hti.pt.y -= TreeView_GetItemHeight(hToolBarTree) / 2; + TreeView_HitTest(hToolBarTree, &hti); + if (hDragItem == hti.hItem) break; + if (hti.flags & TVHT_ABOVE) + hti.hItem = TVI_FIRST; + + TVITEM tvi; + tvi.mask = TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = (HTREEITEM)hDragItem; + TreeView_GetItem(hToolBarTree, &tvi); + if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT) || (hti.hItem == TVI_FIRST)) { + TVINSERTSTRUCT tvis; + TCHAR strbuf[128]; + tvis.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; + tvis.item.stateMask = 0xFFFFFFFF; + tvis.item.pszText = strbuf; + tvis.item.cchTextMax = sizeof(strbuf); + tvis.item.hItem = (HTREEITEM)hDragItem; + TreeView_GetItem(hToolBarTree, &tvis.item); + TreeView_DeleteItem(hToolBarTree, hDragItem); + tvis.hParent = NULL; + tvis.hInsertAfter = hti.hItem; + TreeView_SelectItem(hToolBarTree, TreeView_InsertItem(hToolBarTree, &tvis)); + SendMessage((GetParent(hwndDlg)), PSM_CHANGED, 0, 0); + } + } + break; + + case WM_MOUSEMOVE: + if (drag) { + TVHITTESTINFO hti; + hti.pt.x = (short)LOWORD(lParam); + hti.pt.y = (short)HIWORD(lParam); + ClientToScreen(hwndDlg, &hti.pt); + ScreenToClient(hToolBarTree, &hti.pt); + TreeView_HitTest(hToolBarTree, &hti); + if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) { + HTREEITEM it = hti.hItem; + hti.pt.y -= TreeView_GetItemHeight(hToolBarTree) / 2; + TreeView_HitTest(hToolBarTree, &hti); + if (!(hti.flags & TVHT_ABOVE)) + TreeView_SetInsertMark(hToolBarTree, hti.hItem, 1); + else + TreeView_SetInsertMark(hToolBarTree, it, 0); + } + else { + if (hti.flags & TVHT_ABOVE) SendMessage(hToolBarTree, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); + if (hti.flags & TVHT_BELOW) SendMessage(hToolBarTree, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); + TreeView_SetInsertMark(hToolBarTree, NULL, 0); + } + } + break; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && GetFocus() == (HWND)lParam && (HWND)lParam != hToolBarTree) + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + if ((HIWORD(wParam) == EN_CHANGE) && ((HWND)lParam == GetFocus())) + if (!bOptionsInit) + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + + switch (LOWORD(wParam)) { + case IDC_BBRESET: + CallService(MS_DB_MODULE_DELETE, NULL, LPARAM("TabSRMM_Toolbar")); + CB_HardReInit(); + BuildMenuObjectsTree(hToolBarTree); + break; + + case IDC_SEPARATOR: + hti = TreeView_GetSelection(hToolBarTree); + if (!hti) + hti = TVI_FIRST; + + CustomButtonData *cbd = new CustomButtonData(); + cbd->bSeparator = cbd->bHidden = cbd->bIMButton = cbd->bLSided = true; + cbd->dwButtonOrigID = ++dwSepCount; + cbd->pszModuleName = "Tabsrmm_sep"; + cbd->iButtonWidth = 22; + cbd->opFlags = BBSF_NTBDESTRUCT; + LButtonsList.insert(cbd); + + TVINSERTSTRUCT tvis; + tvis.hParent = NULL; + tvis.hInsertAfter = hti; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + + tvis.item.pszText = TranslateT(""); + tvis.item.iImage = tvis.item.iSelectedImage = -1; + tvis.item.lParam = (LPARAM)cbd; + hti = TreeView_InsertItem(hToolBarTree, &tvis); + + TreeView_SetCheckState(hToolBarTree, hti, (cbd->bIMButton || cbd->bChatButton)); + } + break; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_RESET: + CB_ReInitCustomButtons(); + dwSepCount = M.GetDword("TabSRMM_Toolbar", "SeparatorsCount", 0); + return 1; + + case PSN_APPLY: + hti = TreeView_GetSelection(hToolBarTree); + if (hti) { + TVITEM tvi; + tvi.mask = TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hti; + TreeView_GetItem(hToolBarTree, &tvi); + + if (tvi.lParam) { + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + if (cbd) { + cbd->bIMButton = IsDlgButtonChecked(hwndDlg, IDC_IMCHECK) != 0; + cbd->bChatButton = IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK) != 0; + cbd->bCanBeHidden = IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN) != 0; + } + } + } + + SaveTree(hToolBarTree); + CB_ReInitCustomButtons(); + PluginConfig.g_iButtonsBarGap = (BYTE)SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_GETPOS, 0, 0); + + if (PluginConfig.g_iButtonsBarGap != M.GetByte("ButtonsBarGap", 1)) + M.BroadcastMessageAsync(WM_SIZE, 0, 0); + + db_set_b(0, SRMSGMOD_T, "ButtonsBarGap", PluginConfig.g_iButtonsBarGap); + + BuildMenuObjectsTree((HWND)hToolBarTree); + Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); + return 1; + } + break; + + case IDC_TOOLBARTREE: + switch (((LPNMHDR)lParam)->code) { + case TVN_BEGINDRAGA: + case TVN_BEGINDRAGW: + SetCapture(hwndDlg); + drag = 1; + hDragItem = ((LPNMTREEVIEW)lParam)->itemNew.hItem; + TreeView_SelectItem(hToolBarTree, hDragItem); + break; + + case TVN_SELCHANGINGA: + case TVN_SELCHANGINGW: + hti = TreeView_GetSelection(hToolBarTree); + if (hti != NULL) { + TCHAR strbuf[128]; + + TVITEM tvi; + tvi.hItem = hti; + tvi.pszText = strbuf; + tvi.cchTextMax = sizeof(strbuf); + tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; + TreeView_GetItem(hToolBarTree, &tvi); + + if (tvi.lParam == 0 || !TreeView_GetCheckState(hToolBarTree, tvi.hItem) || !_tcscmp(tvi.pszText, MIDDLE_SEPARATOR)) + break; + + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + if (cbd) { + cbd->opFlags = (IsDlgButtonChecked(hwndDlg, IDC_IMCHECK)) ? BBSF_IMBUTTON : 0; + cbd->opFlags |= (IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK)) ? BBSF_CHATBUTTON : 0; + cbd->opFlags |= (IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN)) ? BBSF_CANBEHIDDEN : 0; + + cbd->bIMButton = (IsDlgButtonChecked(hwndDlg, IDC_IMCHECK) ? TRUE : FALSE); + cbd->bChatButton = (IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK) ? TRUE : FALSE); + cbd->bCanBeHidden = (IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN) ? TRUE : FALSE); + } + } + break; + + case TVN_SELCHANGEDW: + case TVN_SELCHANGEDA: + hti = TreeView_GetSelection(hToolBarTree); + if (hti != NULL) { + TCHAR strbuf[128]; + + TVITEM tvi; + tvi.pszText = strbuf; + tvi.cchTextMax = sizeof(strbuf); + tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hti; + TreeView_GetItem(hToolBarTree, &tvi); + + if (!TreeView_GetCheckState(hToolBarTree, tvi.hItem) || !_tcscmp(tvi.pszText, MIDDLE_SEPARATOR)) { + Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); + break; + } + + if (tvi.lParam == 0) + break; + + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + if (cbd) { + Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, TRUE); + CheckDlgButton(hwndDlg, IDC_IMCHECK, (cbd->bIMButton) ? 1 : 0); + CheckDlgButton(hwndDlg, IDC_CHATCHECK, (cbd->bChatButton) ? 1 : 0); + CheckDlgButton(hwndDlg, IDC_CANBEHIDDEN, (cbd->bCanBeHidden) ? 1 : 0); + } + } + break; + + case NM_CLICK: + TVHITTESTINFO hti = { 0 }; + GetCursorPos(&hti.pt); + ScreenToClient(hToolBarTree, &hti.pt); + if (TreeView_HitTest(hToolBarTree, &hti)) { + if (hti.flags&TVHT_ONITEMSTATEICON) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + if (TreeView_GetCheckState(hToolBarTree, hti.hItem)) { + Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, FALSE); + CheckDlgButton(hwndDlg, IDC_IMCHECK, 1); + } + else { + Utils::enableDlgControl(hwndDlg, IDC_IMCHECK, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_CHATCHECK, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_CANBEHIDDEN, TRUE); + } + TreeView_SelectItem(hToolBarTree, hti.hItem); + } + } + } + } + break; + + case WM_DESTROY: + HIMAGELIST hIml = TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_TOOLBARTREE), TVSIL_NORMAL); + ImageList_Destroy(hIml); + hIml = TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_TOOLBARTREE), TVSIL_STATE); + ImageList_Destroy(hIml); + break; + } + + return FALSE; +} diff --git a/plugins/TabSRMM/src/chat/window.cpp b/plugins/TabSRMM/src/chat/window.cpp index 8b247b793d..90ace97da2 100644 --- a/plugins/TabSRMM/src/chat/window.cpp +++ b/plugins/TabSRMM/src/chat/window.cpp @@ -182,8 +182,7 @@ static BOOL CheckCustomLink(HWND hwndDlg, POINT* ptClient, UINT uMsg, WPARAM wPa } bIsCustomLink = (cpMin < cpMax); - } - while (FALSE); + } while (FALSE); if (TextFont) TextFont->Release(); if (TextRange) TextRange->Release(); @@ -3007,7 +3006,7 @@ LABEL_SHOWWINDOW: mi->idleTimeStamp = time(0); mi->lastIdleCheck = 0; pci->SM_BroadcastMessage(si->pszModule, GC_UPDATESTATUSBAR, 0, 1, TRUE); - if (dat && dat->pContainer) + if (dat->pContainer) if (fSound && !nen_options.iNoSounds && !(dat->pContainer->dwFlags & CNT_NOSOUND)) SkinPlaySound("ChatSent"); diff --git a/plugins/TabSRMM/src/contactcache.cpp b/plugins/TabSRMM/src/contactcache.cpp index 53ef5a3884..22a445fe3d 100644 --- a/plugins/TabSRMM/src/contactcache.cpp +++ b/plugins/TabSRMM/src/contactcache.cpp @@ -1,622 +1,622 @@ -/* - * astyle --force-indent=tab=4 --brackets=linux --indent-switches - * --pad=oper --one-line=keep-blocks --unpad=paren - * - * Miranda NG: the free IM client for Microsoft* Windows* - * - * Copyright (c) 2000-09 Miranda ICQ/IM project, - * all portions of this codebase are copyrighted to the people - * listed in contributors.txt. - * - * This programm 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 - * - * contact cache implementation - * - * the contact cache provides various services to the message window(s) - * it also abstracts meta contacts. - * - */ - -#include "commonheaders.h" - -static OBJLIST arContacts(50, NumericKeySortT); - -static DBCachedContact ccInvalid = { 0 }; - -CContactCache::CContactCache(const MCONTACT hContact) -{ - m_hContact = hContact; - m_wOldStatus = m_wStatus = ID_STATUS_OFFLINE; - - if (hContact) { - if ((cc = db_get_contact(hContact)) != NULL) { - initPhaseTwo(); - return; - } - } - - cc = &ccInvalid; - m_szAccount = C_INVALID_ACCOUNT; - m_isMeta = false; - m_Valid = false; -} - -/** - * 2nd part of the object initialization that must be callable during the - * object's lifetime (not only on construction). - */ -void CContactCache::initPhaseTwo() -{ - m_szAccount = 0; - if (cc->szProto) { - PROTOACCOUNT *acc = reinterpret_cast(::CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)cc->szProto)); - if (acc && acc->tszAccountName) - m_szAccount = acc->tszAccountName; - } - - m_Valid = (cc->szProto != 0 && m_szAccount != 0) ? true : false; - if (m_Valid) { - m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here - if (m_isMeta) - updateMeta(); - updateState(); - updateFavorite(); - } - else { - cc->szProto = C_INVALID_PROTO; - m_szAccount = C_INVALID_ACCOUNT; - m_isMeta = false; - } -} - -/** - * reset meta contact information. Used when meta contacts are disabled - * on user's request. - */ -void CContactCache::resetMeta() -{ - m_isMeta = false; - m_szMetaProto = 0; - m_wMetaStatus = ID_STATUS_OFFLINE; - initPhaseTwo(); -} - -/** - * if the contact has an open message window, close it. - * window procedure will use setWindowData() to reset m_hwnd to 0. - */ -void CContactCache::closeWindow() -{ - if (m_hwnd) - ::SendMessage(m_hwnd, WM_CLOSE, 1, 2); -} - -void CContactCache::updateState() -{ - updateNick(); - updateStatus(); -} - -/** - * update private copy of the nick name. Use contact list name cache - * - * @return bool: true if nick has changed. - */ -bool CContactCache::updateNick() -{ - bool fChanged = false; - - if (m_Valid) { - TCHAR *tszNick = pcli->pfnGetContactDisplayName(getActiveContact(), 0); - if (tszNick) - fChanged = (_tcscmp(m_szNick, tszNick) ? true : false); - mir_sntprintf(m_szNick, 80, _T("%s"), tszNick ? tszNick : _T("")); - } - return fChanged; -} - -/** - * update status mode - * @return bool: true if status mode has changed, false if not. - */ -bool CContactCache::updateStatus() -{ - if (!m_Valid) - return false; - - m_wOldStatus = m_wStatus; - m_wStatus = (WORD)db_get_w(getActiveContact(), getActiveProto(), "Status", ID_STATUS_OFFLINE); - return m_wOldStatus != m_wStatus; -} - -/** - * update meta (subcontact and -protocol) status. This runs when the - * MC protocol fires one of its events OR when a relevant database value changes - * in the master contact. - */ -void CContactCache::updateMeta() -{ - if (m_Valid) { - MCONTACT hOldSub = m_hSub; - m_hSub = db_mc_getSrmmSub(cc->contactID); - m_szMetaProto = GetContactProto(m_hSub); - m_wMetaStatus = (WORD)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE); - PROTOACCOUNT *pa = ProtoGetAccount(m_szMetaProto); - if (pa) - m_szAccount = pa->tszAccountName; - - if (hOldSub != m_hSub) { - updateStatus(); - updateNick(); - updateUIN(); - } - } - else { - m_hSub = 0; - m_szMetaProto = NULL; - m_wMetaStatus = ID_STATUS_OFFLINE; - m_xStatus = 0; - } -} - -/** - * obtain the UIN. This is only maintained for open message windows - * it also run when the subcontact for a MC changes. - */ -bool CContactCache::updateUIN() -{ - m_szUIN[0] = 0; - - if (m_Valid) { - CONTACTINFO ci = { sizeof(ci) }; - ci.hContact = getActiveContact(); - ci.szProto = const_cast(getActiveProto()); - ci.dwFlag = CNF_DISPLAYUID | CNF_TCHAR; - if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) { - switch (ci.type) { - case CNFT_ASCIIZ: - mir_sntprintf(m_szUIN, SIZEOF(m_szUIN), _T("%s"), reinterpret_cast(ci.pszVal)); - mir_free((void*)ci.pszVal); - break; - case CNFT_DWORD: - mir_sntprintf(m_szUIN, SIZEOF(m_szUIN), _T("%u"), ci.dVal); - break; - } - } - } - - return false; -} - -void CContactCache::updateStats(int iType, size_t value) -{ - if (m_stats == 0) - allocStats(); - - switch(iType) { - case TSessionStats::UPDATE_WITH_LAST_RCV: - if (!m_stats->lastReceivedChars) - break; - m_stats->iReceived++; - m_stats->messageCount++; - m_stats->iReceivedBytes += m_stats->lastReceivedChars; - m_stats->lastReceivedChars = 0; - break; - case TSessionStats::INIT_TIMER: - m_stats->started = time(0); - break; - case TSessionStats::SET_LAST_RCV: - m_stats->lastReceivedChars = (unsigned int)value; - break; - case TSessionStats::BYTES_SENT: - m_stats->iSent++; - m_stats->messageCount++; - m_stats->iSentBytes += (unsigned int)value; - break; - } -} - -void CContactCache::allocStats() -{ - if (m_stats == 0) { - m_stats = new TSessionStats; - ::ZeroMemory(m_stats, sizeof(TSessionStats)); - } -} - -/** - * set the window data for this contact. The window procedure of the message - * dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache - * that a message window is open for this contact. - * - * @param hwnd: window handle - * @param dat: _MessageWindowData - window data structure - */ -void CContactCache::setWindowData(const HWND hwnd, TWindowData *dat) -{ - m_hwnd = hwnd; - m_dat = dat; - if (hwnd && dat && m_history == 0) - allocHistory(); - if (hwnd) - updateStatusMsg(); - else { - /* release memory - not needed when window isn't open */ - if (m_szStatusMsg) { - mir_free(m_szStatusMsg); - m_szStatusMsg = 0; - } - if (m_ListeningInfo) { - mir_free(m_ListeningInfo); - m_ListeningInfo = 0; - } - if (m_xStatusMsg) { - mir_free(m_xStatusMsg); - m_xStatusMsg = 0; - } - } -} - -/** - * saves message to the input history. - * it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history. - */ - -void CContactCache::saveHistory(WPARAM wParam, LPARAM lParam) -{ - size_t iLength = 0, iStreamLength = 0; - int oldTop = 0; - char* szFromStream = NULL; - - if (m_hwnd == 0 || m_dat == 0) - return; - - if (wParam) { - oldTop = m_iHistoryTop; - m_iHistoryTop = (int)wParam; - } - - szFromStream = ::Message_GetFromStream(GetDlgItem(m_hwnd, IDC_MESSAGE), m_dat, (CP_UTF8 << 16) | (SF_RTFNOOBJS | SFF_PLAINRTF | SF_USECODEPAGE | SF_NCRFORNONASCII)); - - iLength = iStreamLength = (strlen(szFromStream) + 1); - - if (iLength > 0 && m_history != NULL) { - if ((m_iHistoryTop == m_iHistorySize) && oldTop == 0) { // shift the stack down... - TInputHistory ihTemp = m_history[0]; - m_iHistoryTop--; - ::MoveMemory((void*)&m_history[0], (void*)&m_history[1], (m_iHistorySize - 1) * sizeof(TInputHistory)); - m_history[m_iHistoryTop] = ihTemp; - } - if (iLength > m_history[m_iHistoryTop].lLen) { - if (m_history[m_iHistoryTop].szText == NULL) { - if (iLength < HISTORY_INITIAL_ALLOCSIZE) - iLength = HISTORY_INITIAL_ALLOCSIZE; - m_history[m_iHistoryTop].szText = (TCHAR*)mir_alloc(iLength); - m_history[m_iHistoryTop].lLen = iLength; - } else { - if (iLength > m_history[m_iHistoryTop].lLen) { - m_history[m_iHistoryTop].szText = (TCHAR*)mir_realloc(m_history[m_iHistoryTop].szText, iLength); - m_history[m_iHistoryTop].lLen = iLength; - } - } - } - ::CopyMemory(m_history[m_iHistoryTop].szText, szFromStream, iStreamLength); - if (!oldTop) { - if (m_iHistoryTop < m_iHistorySize) { - m_iHistoryTop++; - m_iHistoryCurrent = m_iHistoryTop; - } - } - } - if (szFromStream) - mir_free(szFromStream); - if (oldTop) - m_iHistoryTop = oldTop; -} - -/** - * handle the input history scrolling for the message input area - * @param wParam: VK_ keyboard code (VK_UP or VK_DOWN) - */ -void CContactCache::inputHistoryEvent(WPARAM wParam) -{ - if (m_hwnd == 0 || m_dat == 0) - return; - - if (m_history != NULL && m_history[0].szText != NULL) { // at least one entry needs to be alloced, otherwise we get a nice infinite loop ;) - HWND hwndEdit = ::GetDlgItem(m_hwnd, IDC_MESSAGE); - SETTEXTEX stx = {ST_DEFAULT, CP_UTF8}; - - if (m_dat->dwFlags & MWF_NEEDHISTORYSAVE) { - m_iHistoryCurrent = m_iHistoryTop; - if (::GetWindowTextLengthA(hwndEdit) > 0) - saveHistory(m_iHistorySize, 0); - else - m_history[m_iHistorySize].szText[0] = (TCHAR)'\0'; - } - if (wParam == VK_UP) { - if (m_iHistoryCurrent == 0) - return; - m_iHistoryCurrent--; - } - else { - m_iHistoryCurrent++; - if (m_iHistoryCurrent > m_iHistoryTop) - m_iHistoryCurrent = m_iHistoryTop; - } - if (m_iHistoryCurrent == m_iHistoryTop) { - if (m_history[m_iHistorySize].szText != NULL) { // replace the temp buffer - ::SetWindowText(hwndEdit, _T("")); - ::SendMessage(hwndEdit, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_history[m_iHistorySize].szText); - ::SendMessage(hwndEdit, EM_SETSEL, -1, -1); - } - } - else { - if (m_history[m_iHistoryCurrent].szText != NULL) { - ::SetWindowText(hwndEdit, _T("")); - ::SendMessage(hwndEdit, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_history[m_iHistoryCurrent].szText); - ::SendMessage(hwndEdit, EM_SETSEL, -1, -1); - } - else ::SetWindowText(hwndEdit, _T("")); - } - ::SendMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(::GetDlgCtrlID(hwndEdit), EN_CHANGE), (LPARAM)hwndEdit); - m_dat->dwFlags &= ~MWF_NEEDHISTORYSAVE; - } -} - -/** - * allocate the input history (on-demand, when it is requested by - * opening a message window for this contact). - * - * note: it allocs historysize + 1 elements, because the + 1 is used - * for the temporary buffer which saves the current input line when - * using input history scrolling. - */ -void CContactCache::allocHistory() -{ - m_iHistorySize = M.GetByte("historysize", 15); - if (m_iHistorySize < 10) - m_iHistorySize = 10; - m_history = (TInputHistory *)mir_alloc(sizeof(TInputHistory) * (m_iHistorySize + 1)); - m_iHistoryCurrent = 0; - m_iHistoryTop = 0; - if (m_history) - ZeroMemory(m_history, sizeof(TInputHistory) * m_iHistorySize); - m_history[m_iHistorySize].szText = (TCHAR*)mir_alloc((HISTORY_INITIAL_ALLOCSIZE + 1) * sizeof(TCHAR)); - m_history[m_iHistorySize].lLen = HISTORY_INITIAL_ALLOCSIZE; -} - -/** - * release additional memory resources - */ -void CContactCache::releaseAlloced() -{ - if (m_stats) { - delete m_stats; - m_stats = 0; - } - - if (m_history) { - for (int i = 0; i <= m_iHistorySize; i++) - mir_free(m_history[i].szText); - - mir_free(m_history); - m_history = 0; - } - - mir_free(m_szStatusMsg); - m_szStatusMsg = NULL; -} - -/** - * when a contact is deleted, mark it as invalid in the cache and release - * all memory it has allocated. - */ -void CContactCache::deletedHandler() -{ - m_Valid = false; - if (m_hwnd) - ::SendMessage(m_hwnd, WM_CLOSE, 1, 2); - - releaseAlloced(); - m_hContact = (MCONTACT)-1; -} - -/** - * udpate favorite or recent state. runs when user manually adds - * or removes a user from that list or when database setting is - * changed from elsewhere - */ -void CContactCache::updateFavorite() -{ - m_isFavorite = db_get_b(m_hContact, SRMSGMOD_T, "isFavorite", 0) != 0; - m_isRecent = M.GetDword(m_hContact, "isRecent", 0) ? true : false; -} - -/** - * update all or only the given status message information from the database - * - * @param szKey: char* database key name or 0 to reload all messages - */ -void CContactCache::updateStatusMsg(const char *szKey) -{ - if (!m_Valid) - return; - - MCONTACT hContact = getActiveContact(); - - if (szKey == 0 || (szKey && !strcmp("StatusMsg", szKey))) { - if (m_szStatusMsg) - mir_free(m_szStatusMsg); - m_szStatusMsg = 0; - ptrT szStatus(db_get_tsa(hContact, "CList", "StatusMsg")); - if (szStatus != 0) - m_szStatusMsg = (lstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : 0); - } - if (szKey == 0 || (szKey && !strcmp("ListeningTo", szKey))) { - if (m_ListeningInfo) - mir_free(m_ListeningInfo); - m_ListeningInfo = 0; - ptrT szListeningTo(db_get_tsa(hContact, cc->szProto, "ListeningTo")); - if (szListeningTo != 0 && *szListeningTo) - m_ListeningInfo = szListeningTo.detouch(); - } - if (szKey == 0 || (szKey && !strcmp("XStatusMsg", szKey))) { - if (m_xStatusMsg) - mir_free(m_xStatusMsg); - m_xStatusMsg = 0; - ptrT szXStatusMsg(db_get_tsa(hContact, cc->szProto, "XStatusMsg")); - if (szXStatusMsg != 0 && *szXStatusMsg) - m_xStatusMsg = szXStatusMsg.detouch(); - } - m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0); -} - -/** - * retrieve contact cache entry for the given contact. It _never_ returns zero, for a hContact - * 0, it retrieves a dummy object. - * Non-existing cache entries are created on demand. - * - * @param hContact: contact handle - * @return CContactCache* pointer to the cache entry for this contact - */ - -CContactCache* CContactCache::getContactCache(MCONTACT hContact) -{ - CContactCache *cc = arContacts.find((CContactCache*)&hContact); - if (cc == NULL) { - cc = new CContactCache(hContact); - arContacts.insert(cc); - } - return cc; -} - -/** - * when the state of the meta contacts protocol changes from enabled to disabled - * (or vice versa), this updates the contact cache - * - * it is ONLY called from the DBSettingChanged() event handler when the relevant - * database value is touched. - */ -int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM) -{ - for (int i=0; i < arContacts.getCount(); i++) { - CContactCache &c = arContacts[i]; - if (c.isMeta() && !bMetaEnabled) { - c.closeWindow(); - c.resetMeta(); - } - - // meta contacts are enabled, but current contact is a subcontact - > close window - if (bMetaEnabled && c.isSubContact()) - c.closeWindow(); - - // reset meta contact information, if metacontacts protocol became avail - if (bMetaEnabled && !c.cc->IsMeta()) - c.resetMeta(); - } - return 0; -} - -/** - * normalize the status message with proper cr/lf sequences. - * @param src TCHAR*: original status message - * @param fStripAll bool: strip all cr/lf sequences and replace them with spaces (use for title bar) - * @return TCHAR*: converted status message. CALLER is responsible to mir_free it, MUST use mir_free() - */ -TCHAR* CContactCache::getNormalizedStatusMsg(const TCHAR *src, bool fStripAll) -{ - size_t k = 0, i = 0; - TCHAR* tszResult = 0; - - if (src == 0 || lstrlen(src) < 2) - return 0; - - tstring dest; - - for (i=0; i < _tcslen(src); i++) { - if (src[i] == 0x0d || src[i] == '\t') - continue; - if (i && src[i] == (TCHAR)0x0a) { - if (fStripAll) { - dest.append(_T(" ")); - continue; - } - dest.append(_T("\n")); - continue; - } - dest += src[i]; - } - - if (i) { - tszResult = (TCHAR*)mir_alloc((dest.length() + 1) * sizeof(TCHAR)); - _tcscpy(tszResult, dest.c_str()); - tszResult[dest.length()] = 0; - } - return tszResult; -} - -/** - * retrieve the tab/title icon for the corresponding session. - */ -HICON CContactCache::getIcon(int& iSize) const -{ - if (!m_dat || !m_hwnd) - return LoadSkinnedProtoIcon(cc->szProto, m_wStatus); - - if (m_dat->dwFlags & MWF_ERRORSTATE) - return PluginConfig.g_iconErr; - if (m_dat->mayFlashTab) - return m_dat->iFlashIcon; - - if (m_dat->si && m_dat->iFlashIcon) { - int sizeX, sizeY; - Utils::getIconSize(m_dat->iFlashIcon, sizeX, sizeY); - iSize = sizeX; - return m_dat->iFlashIcon; - } - if (m_dat->hTabIcon == m_dat->hTabStatusIcon && m_dat->hXStatusIcon) - return m_dat->hXStatusIcon; - return m_dat->hTabIcon; -} - -int CContactCache::getMaxMessageLength() -{ - MCONTACT hContact = getActiveContact(); - LPCSTR szProto = getActiveProto(); - if (szProto) { - m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact); - if (m_nMax) { - if (M.GetByte("autosplit", 0)) { - if (m_hwnd) - ::SendDlgItemMessage(m_hwnd, IDC_MESSAGE, EM_EXLIMITTEXT, 0, 20000); - } - else { - if (m_hwnd) - ::SendDlgItemMessage(m_hwnd, IDC_MESSAGE, EM_EXLIMITTEXT, 0, (LPARAM)m_nMax); - } - } - else { - if (m_hwnd) - ::SendDlgItemMessage(m_hwnd, IDC_MESSAGE, EM_EXLIMITTEXT, 0, 20000); - m_nMax = 20000; - } - } - return m_nMax; -} +/* + * astyle --force-indent=tab=4 --brackets=linux --indent-switches + * --pad=oper --one-line=keep-blocks --unpad=paren + * + * Miranda NG: the free IM client for Microsoft* Windows* + * + * Copyright (c) 2000-09 Miranda ICQ/IM project, + * all portions of this codebase are copyrighted to the people + * listed in contributors.txt. + * + * This programm 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 + * + * contact cache implementation + * + * the contact cache provides various services to the message window(s) + * it also abstracts meta contacts. + * + */ + +#include "commonheaders.h" + +static OBJLIST arContacts(50, NumericKeySortT); + +static DBCachedContact ccInvalid = { 0 }; + +CContactCache::CContactCache(const MCONTACT hContact) +{ + m_hContact = hContact; + m_wOldStatus = m_wStatus = ID_STATUS_OFFLINE; + + if (hContact) { + if ((cc = db_get_contact(hContact)) != NULL) { + initPhaseTwo(); + return; + } + } + + cc = &ccInvalid; + m_szAccount = C_INVALID_ACCOUNT; + m_isMeta = false; + m_Valid = false; +} + +/** + * 2nd part of the object initialization that must be callable during the + * object's lifetime (not only on construction). + */ +void CContactCache::initPhaseTwo() +{ + m_szAccount = 0; + if (cc->szProto) { + PROTOACCOUNT *acc = reinterpret_cast(::CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)cc->szProto)); + if (acc && acc->tszAccountName) + m_szAccount = acc->tszAccountName; + } + + m_Valid = (cc->szProto != 0 && m_szAccount != 0) ? true : false; + if (m_Valid) { + m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here + if (m_isMeta) + updateMeta(); + updateState(); + updateFavorite(); + } + else { + cc->szProto = C_INVALID_PROTO; + m_szAccount = C_INVALID_ACCOUNT; + m_isMeta = false; + } +} + +/** + * reset meta contact information. Used when meta contacts are disabled + * on user's request. + */ +void CContactCache::resetMeta() +{ + m_isMeta = false; + m_szMetaProto = 0; + m_wMetaStatus = ID_STATUS_OFFLINE; + initPhaseTwo(); +} + +/** + * if the contact has an open message window, close it. + * window procedure will use setWindowData() to reset m_hwnd to 0. + */ +void CContactCache::closeWindow() +{ + if (m_hwnd) + ::SendMessage(m_hwnd, WM_CLOSE, 1, 2); +} + +void CContactCache::updateState() +{ + updateNick(); + updateStatus(); +} + +/** + * update private copy of the nick name. Use contact list name cache + * + * @return bool: true if nick has changed. + */ +bool CContactCache::updateNick() +{ + bool fChanged = false; + + if (m_Valid) { + TCHAR *tszNick = pcli->pfnGetContactDisplayName(getActiveContact(), 0); + if (tszNick) + fChanged = (_tcscmp(m_szNick, tszNick) ? true : false); + mir_sntprintf(m_szNick, 80, _T("%s"), tszNick ? tszNick : _T("")); + } + return fChanged; +} + +/** + * update status mode + * @return bool: true if status mode has changed, false if not. + */ +bool CContactCache::updateStatus() +{ + if (!m_Valid) + return false; + + m_wOldStatus = m_wStatus; + m_wStatus = (WORD)db_get_w(getActiveContact(), getActiveProto(), "Status", ID_STATUS_OFFLINE); + return m_wOldStatus != m_wStatus; +} + +/** + * update meta (subcontact and -protocol) status. This runs when the + * MC protocol fires one of its events OR when a relevant database value changes + * in the master contact. + */ +void CContactCache::updateMeta() +{ + if (m_Valid) { + MCONTACT hOldSub = m_hSub; + m_hSub = db_mc_getSrmmSub(cc->contactID); + m_szMetaProto = GetContactProto(m_hSub); + m_wMetaStatus = (WORD)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE); + PROTOACCOUNT *pa = ProtoGetAccount(m_szMetaProto); + if (pa) + m_szAccount = pa->tszAccountName; + + if (hOldSub != m_hSub) { + updateStatus(); + updateNick(); + updateUIN(); + } + } + else { + m_hSub = 0; + m_szMetaProto = NULL; + m_wMetaStatus = ID_STATUS_OFFLINE; + m_xStatus = 0; + } +} + +/** + * obtain the UIN. This is only maintained for open message windows + * it also run when the subcontact for a MC changes. + */ +bool CContactCache::updateUIN() +{ + m_szUIN[0] = 0; + + if (m_Valid) { + CONTACTINFO ci = { sizeof(ci) }; + ci.hContact = getActiveContact(); + ci.szProto = const_cast(getActiveProto()); + ci.dwFlag = CNF_DISPLAYUID | CNF_TCHAR; + if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) { + switch (ci.type) { + case CNFT_ASCIIZ: + mir_sntprintf(m_szUIN, SIZEOF(m_szUIN), _T("%s"), reinterpret_cast(ci.pszVal)); + mir_free((void*)ci.pszVal); + break; + case CNFT_DWORD: + mir_sntprintf(m_szUIN, SIZEOF(m_szUIN), _T("%u"), ci.dVal); + break; + } + } + } + + return false; +} + +void CContactCache::updateStats(int iType, size_t value) +{ + if (m_stats == 0) + allocStats(); + + switch(iType) { + case TSessionStats::UPDATE_WITH_LAST_RCV: + if (!m_stats->lastReceivedChars) + break; + m_stats->iReceived++; + m_stats->messageCount++; + m_stats->iReceivedBytes += m_stats->lastReceivedChars; + m_stats->lastReceivedChars = 0; + break; + case TSessionStats::INIT_TIMER: + m_stats->started = time(0); + break; + case TSessionStats::SET_LAST_RCV: + m_stats->lastReceivedChars = (unsigned int)value; + break; + case TSessionStats::BYTES_SENT: + m_stats->iSent++; + m_stats->messageCount++; + m_stats->iSentBytes += (unsigned int)value; + break; + } +} + +void CContactCache::allocStats() +{ + if (m_stats == 0) { + m_stats = new TSessionStats; + ::ZeroMemory(m_stats, sizeof(TSessionStats)); + } +} + +/** + * set the window data for this contact. The window procedure of the message + * dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache + * that a message window is open for this contact. + * + * @param hwnd: window handle + * @param dat: _MessageWindowData - window data structure + */ +void CContactCache::setWindowData(const HWND hwnd, TWindowData *dat) +{ + m_hwnd = hwnd; + m_dat = dat; + if (hwnd && dat && m_history == 0) + allocHistory(); + if (hwnd) + updateStatusMsg(); + else { + /* release memory - not needed when window isn't open */ + if (m_szStatusMsg) { + mir_free(m_szStatusMsg); + m_szStatusMsg = 0; + } + if (m_ListeningInfo) { + mir_free(m_ListeningInfo); + m_ListeningInfo = 0; + } + if (m_xStatusMsg) { + mir_free(m_xStatusMsg); + m_xStatusMsg = 0; + } + } +} + +/** + * saves message to the input history. + * it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history. + */ + +void CContactCache::saveHistory(WPARAM wParam, LPARAM lParam) +{ + size_t iLength = 0, iStreamLength = 0; + int oldTop = 0; + char* szFromStream = NULL; + + if (m_hwnd == 0 || m_dat == 0) + return; + + if (wParam) { + oldTop = m_iHistoryTop; + m_iHistoryTop = (int)wParam; + } + + szFromStream = ::Message_GetFromStream(GetDlgItem(m_hwnd, IDC_MESSAGE), m_dat, (CP_UTF8 << 16) | (SF_RTFNOOBJS | SFF_PLAINRTF | SF_USECODEPAGE | SF_NCRFORNONASCII)); + if (szFromStream != NULL) { + iLength = iStreamLength = (strlen(szFromStream) + 1); + + if (iLength > 0 && m_history != NULL) { // XXX: iLength > 1 ? + if ((m_iHistoryTop == m_iHistorySize) && oldTop == 0) { // shift the stack down... + TInputHistory ihTemp = m_history[0]; + m_iHistoryTop--; + ::MoveMemory((void*)&m_history[0], (void*)&m_history[1], (m_iHistorySize - 1) * sizeof(TInputHistory)); + m_history[m_iHistoryTop] = ihTemp; + } + if (iLength > m_history[m_iHistoryTop].lLen) { + if (m_history[m_iHistoryTop].szText == NULL) { + if (iLength < HISTORY_INITIAL_ALLOCSIZE) + iLength = HISTORY_INITIAL_ALLOCSIZE; + m_history[m_iHistoryTop].szText = (TCHAR*)mir_alloc(iLength); + m_history[m_iHistoryTop].lLen = iLength; + } else { + if (iLength > m_history[m_iHistoryTop].lLen) { + m_history[m_iHistoryTop].szText = (TCHAR*)mir_realloc(m_history[m_iHistoryTop].szText, iLength); + m_history[m_iHistoryTop].lLen = iLength; + } + } + } + ::CopyMemory(m_history[m_iHistoryTop].szText, szFromStream, iStreamLength); + if (!oldTop) { + if (m_iHistoryTop < m_iHistorySize) { + m_iHistoryTop++; + m_iHistoryCurrent = m_iHistoryTop; + } + } + } + mir_free(szFromStream); + } + if (oldTop) + m_iHistoryTop = oldTop; +} + +/** + * handle the input history scrolling for the message input area + * @param wParam: VK_ keyboard code (VK_UP or VK_DOWN) + */ +void CContactCache::inputHistoryEvent(WPARAM wParam) +{ + if (m_hwnd == 0 || m_dat == 0) + return; + + if (m_history != NULL && m_history[0].szText != NULL) { // at least one entry needs to be alloced, otherwise we get a nice infinite loop ;) + HWND hwndEdit = ::GetDlgItem(m_hwnd, IDC_MESSAGE); + SETTEXTEX stx = {ST_DEFAULT, CP_UTF8}; + + if (m_dat->dwFlags & MWF_NEEDHISTORYSAVE) { + m_iHistoryCurrent = m_iHistoryTop; + if (::GetWindowTextLengthA(hwndEdit) > 0) + saveHistory(m_iHistorySize, 0); + else + m_history[m_iHistorySize].szText[0] = (TCHAR)'\0'; + } + if (wParam == VK_UP) { + if (m_iHistoryCurrent == 0) + return; + m_iHistoryCurrent--; + } + else { + m_iHistoryCurrent++; + if (m_iHistoryCurrent > m_iHistoryTop) + m_iHistoryCurrent = m_iHistoryTop; + } + if (m_iHistoryCurrent == m_iHistoryTop) { + if (m_history[m_iHistorySize].szText != NULL) { // replace the temp buffer + ::SetWindowText(hwndEdit, _T("")); + ::SendMessage(hwndEdit, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_history[m_iHistorySize].szText); + ::SendMessage(hwndEdit, EM_SETSEL, -1, -1); + } + } + else { + if (m_history[m_iHistoryCurrent].szText != NULL) { + ::SetWindowText(hwndEdit, _T("")); + ::SendMessage(hwndEdit, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_history[m_iHistoryCurrent].szText); + ::SendMessage(hwndEdit, EM_SETSEL, -1, -1); + } + else ::SetWindowText(hwndEdit, _T("")); + } + ::SendMessage(m_hwnd, WM_COMMAND, MAKEWPARAM(::GetDlgCtrlID(hwndEdit), EN_CHANGE), (LPARAM)hwndEdit); + m_dat->dwFlags &= ~MWF_NEEDHISTORYSAVE; + } +} + +/** + * allocate the input history (on-demand, when it is requested by + * opening a message window for this contact). + * + * note: it allocs historysize + 1 elements, because the + 1 is used + * for the temporary buffer which saves the current input line when + * using input history scrolling. + */ +void CContactCache::allocHistory() +{ + m_iHistorySize = M.GetByte("historysize", 15); + if (m_iHistorySize < 10) + m_iHistorySize = 10; + m_history = (TInputHistory *)mir_alloc(sizeof(TInputHistory) * (m_iHistorySize + 1)); + m_iHistoryCurrent = 0; + m_iHistoryTop = 0; + if (m_history) + ZeroMemory(m_history, sizeof(TInputHistory) * m_iHistorySize); + m_history[m_iHistorySize].szText = (TCHAR*)mir_alloc((HISTORY_INITIAL_ALLOCSIZE + 1) * sizeof(TCHAR)); + m_history[m_iHistorySize].lLen = HISTORY_INITIAL_ALLOCSIZE; +} + +/** + * release additional memory resources + */ +void CContactCache::releaseAlloced() +{ + if (m_stats) { + delete m_stats; + m_stats = 0; + } + + if (m_history) { + for (int i = 0; i <= m_iHistorySize; i++) + mir_free(m_history[i].szText); + + mir_free(m_history); + m_history = 0; + } + + mir_free(m_szStatusMsg); + m_szStatusMsg = NULL; +} + +/** + * when a contact is deleted, mark it as invalid in the cache and release + * all memory it has allocated. + */ +void CContactCache::deletedHandler() +{ + m_Valid = false; + if (m_hwnd) + ::SendMessage(m_hwnd, WM_CLOSE, 1, 2); + + releaseAlloced(); + m_hContact = (MCONTACT)-1; +} + +/** + * udpate favorite or recent state. runs when user manually adds + * or removes a user from that list or when database setting is + * changed from elsewhere + */ +void CContactCache::updateFavorite() +{ + m_isFavorite = db_get_b(m_hContact, SRMSGMOD_T, "isFavorite", 0) != 0; + m_isRecent = M.GetDword(m_hContact, "isRecent", 0) ? true : false; +} + +/** + * update all or only the given status message information from the database + * + * @param szKey: char* database key name or 0 to reload all messages + */ +void CContactCache::updateStatusMsg(const char *szKey) +{ + if (!m_Valid) + return; + + MCONTACT hContact = getActiveContact(); + + if (szKey == 0 || (szKey && !strcmp("StatusMsg", szKey))) { + if (m_szStatusMsg) + mir_free(m_szStatusMsg); + m_szStatusMsg = 0; + ptrT szStatus(db_get_tsa(hContact, "CList", "StatusMsg")); + if (szStatus != 0) + m_szStatusMsg = (lstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : 0); + } + if (szKey == 0 || (szKey && !strcmp("ListeningTo", szKey))) { + if (m_ListeningInfo) + mir_free(m_ListeningInfo); + m_ListeningInfo = 0; + ptrT szListeningTo(db_get_tsa(hContact, cc->szProto, "ListeningTo")); + if (szListeningTo != 0 && *szListeningTo) + m_ListeningInfo = szListeningTo.detouch(); + } + if (szKey == 0 || (szKey && !strcmp("XStatusMsg", szKey))) { + if (m_xStatusMsg) + mir_free(m_xStatusMsg); + m_xStatusMsg = 0; + ptrT szXStatusMsg(db_get_tsa(hContact, cc->szProto, "XStatusMsg")); + if (szXStatusMsg != 0 && *szXStatusMsg) + m_xStatusMsg = szXStatusMsg.detouch(); + } + m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0); +} + +/** + * retrieve contact cache entry for the given contact. It _never_ returns zero, for a hContact + * 0, it retrieves a dummy object. + * Non-existing cache entries are created on demand. + * + * @param hContact: contact handle + * @return CContactCache* pointer to the cache entry for this contact + */ + +CContactCache* CContactCache::getContactCache(MCONTACT hContact) +{ + CContactCache *cc = arContacts.find((CContactCache*)&hContact); + if (cc == NULL) { + cc = new CContactCache(hContact); + arContacts.insert(cc); + } + return cc; +} + +/** + * when the state of the meta contacts protocol changes from enabled to disabled + * (or vice versa), this updates the contact cache + * + * it is ONLY called from the DBSettingChanged() event handler when the relevant + * database value is touched. + */ +int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM) +{ + for (int i=0; i < arContacts.getCount(); i++) { + CContactCache &c = arContacts[i]; + if (c.isMeta() && !bMetaEnabled) { + c.closeWindow(); + c.resetMeta(); + } + + // meta contacts are enabled, but current contact is a subcontact - > close window + if (bMetaEnabled && c.isSubContact()) + c.closeWindow(); + + // reset meta contact information, if metacontacts protocol became avail + if (bMetaEnabled && !c.cc->IsMeta()) + c.resetMeta(); + } + return 0; +} + +/** + * normalize the status message with proper cr/lf sequences. + * @param src TCHAR*: original status message + * @param fStripAll bool: strip all cr/lf sequences and replace them with spaces (use for title bar) + * @return TCHAR*: converted status message. CALLER is responsible to mir_free it, MUST use mir_free() + */ +TCHAR* CContactCache::getNormalizedStatusMsg(const TCHAR *src, bool fStripAll) +{ + size_t k = 0, i = 0; + TCHAR* tszResult = 0; + + if (src == 0 || lstrlen(src) < 2) + return 0; + + tstring dest; + + for (i=0; i < _tcslen(src); i++) { + if (src[i] == 0x0d || src[i] == '\t') + continue; + if (i && src[i] == (TCHAR)0x0a) { + if (fStripAll) { + dest.append(_T(" ")); + continue; + } + dest.append(_T("\n")); + continue; + } + dest += src[i]; + } + + if (i) { + tszResult = (TCHAR*)mir_alloc((dest.length() + 1) * sizeof(TCHAR)); + _tcscpy(tszResult, dest.c_str()); + tszResult[dest.length()] = 0; + } + return tszResult; +} + +/** + * retrieve the tab/title icon for the corresponding session. + */ +HICON CContactCache::getIcon(int& iSize) const +{ + if (!m_dat || !m_hwnd) + return LoadSkinnedProtoIcon(cc->szProto, m_wStatus); + + if (m_dat->dwFlags & MWF_ERRORSTATE) + return PluginConfig.g_iconErr; + if (m_dat->mayFlashTab) + return m_dat->iFlashIcon; + + if (m_dat->si && m_dat->iFlashIcon) { + int sizeX, sizeY; + Utils::getIconSize(m_dat->iFlashIcon, sizeX, sizeY); + iSize = sizeX; + return m_dat->iFlashIcon; + } + if (m_dat->hTabIcon == m_dat->hTabStatusIcon && m_dat->hXStatusIcon) + return m_dat->hXStatusIcon; + return m_dat->hTabIcon; +} + +int CContactCache::getMaxMessageLength() +{ + MCONTACT hContact = getActiveContact(); + LPCSTR szProto = getActiveProto(); + if (szProto) { + m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact); + if (m_nMax) { + if (M.GetByte("autosplit", 0)) { + if (m_hwnd) + ::SendDlgItemMessage(m_hwnd, IDC_MESSAGE, EM_EXLIMITTEXT, 0, 20000); + } + else { + if (m_hwnd) + ::SendDlgItemMessage(m_hwnd, IDC_MESSAGE, EM_EXLIMITTEXT, 0, (LPARAM)m_nMax); + } + } + else { + if (m_hwnd) + ::SendDlgItemMessage(m_hwnd, IDC_MESSAGE, EM_EXLIMITTEXT, 0, 20000); + m_nMax = 20000; + } + } + return m_nMax; +} diff --git a/plugins/TabSRMM/src/container.cpp b/plugins/TabSRMM/src/container.cpp index 3784bc8abb..ebe9c4ebc0 100644 --- a/plugins/TabSRMM/src/container.cpp +++ b/plugins/TabSRMM/src/container.cpp @@ -1,2340 +1,2340 @@ -/* -// Miranda NG: the free IM client for Microsoft* Windows* -// -// 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 -// -// implements the "Container" window which acts as a toplevel window -// for message sessions. - */ - -#include "commonheaders.h" - -#define CONTAINER_KEY "TAB_ContainersW" -#define CONTAINER_SUBKEY "containerW" -#define CONTAINER_PREFIX "CNTW_" - -TContainerData *pFirstContainer = 0; // the linked list of struct ContainerWindowData -TContainerData *pLastActiveContainer = NULL; - -static bool fForceOverlayIcons = false; - -static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact) -{ - if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) { - if (hContact == 0) - return 0; - if (item->dwFlags & BUTTON_PASSHCONTACTW) - *wParam = hContact; - else if (item->dwFlags & BUTTON_PASSHCONTACTL) - *lParam = hContact; - return 1; - } - return 1; // doesn't need a paramter -} - -// Windows Vista+ -// extend the glassy area to get aero look for the status bar, tab bar, info panel -// and outer margins. - -void TSAPI SetAeroMargins(TContainerData *pContainer) -{ - if ( !M.isAero() || !pContainer || CSkin::m_skinEnabled) { - pContainer->MenuBar->setAero(false); - return; - } - - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - if (!dat) - return; - - RECT rcWnd; - if (dat->bType == SESSIONTYPE_IM) { - if (dat->Panel->isActive()) - GetWindowRect(GetDlgItem(dat->hwnd, IDC_LOG), &rcWnd); - else - GetWindowRect(dat->hwnd, &rcWnd); - } - else { - if (dat->Panel->isActive()) - GetWindowRect(GetDlgItem(dat->hwnd, IDC_CHAT_LOG), &rcWnd); - else - GetWindowRect(dat->hwnd, &rcWnd); - } - - POINT pt = { rcWnd.left, rcWnd.top }; - ScreenToClient(pContainer->hwnd, &pt); - - MARGINS m; - m.cyTopHeight = pt.y; - pContainer->MenuBar->setAero(true); - - // bottom part - GetWindowRect(dat->hwnd, &rcWnd); - pt.x = rcWnd.left; - - LONG sbar_left, sbar_right; - if (!pContainer->SideBar->isActive()) { - pt.y = rcWnd.bottom + ((pContainer->iChilds > 1 || !(pContainer->dwFlags & CNT_HIDETABS)) ? pContainer->tBorder : 0); - sbar_left = 0, sbar_right = 0; - } - else { - pt.y = rcWnd.bottom; - sbar_left = (pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT ? pContainer->SideBar->getWidth() : 0); - sbar_right = (pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_RIGHT ? pContainer->SideBar->getWidth() : 0); - } - ScreenToClient(pContainer->hwnd, &pt); - GetClientRect(pContainer->hwnd, &rcWnd); - m.cyBottomHeight = (rcWnd.bottom - pt.y); - - if (m.cyBottomHeight < 0 || m.cyBottomHeight >= rcWnd.bottom) - m.cyBottomHeight = 0; - - m.cxLeftWidth = pContainer->tBorder_outer_left; - m.cxRightWidth = pContainer->tBorder_outer_right; - m.cxLeftWidth += sbar_left; - m.cxRightWidth += sbar_right; - - if (memcmp(&m, &pContainer->mOld, sizeof(MARGINS)) != 0) { - pContainer->mOld = m; - CMimAPI::m_pfnDwmExtendFrameIntoClientArea(pContainer->hwnd, &m); - } -} - -// CreateContainer MUST allocate a ContainerWindowData and pass its address -// to CreateDialogParam() via the LPARAM. It also adds the struct to the linked list -// of containers. -// -// The WM_DESTROY handler of the container DlgProc is responsible for mir_free()'ing the -// pointer and for removing the struct from the linked list. - -TContainerData* TSAPI CreateContainer(const TCHAR *name, int iTemp, MCONTACT hContactFrom) -{ - if (CMimAPI::m_shutDown) - return NULL; - - TContainerData *pContainer = (TContainerData*)mir_calloc(sizeof(TContainerData)); - _tcsncpy(pContainer->szName, name, CONTAINER_NAMELEN + 1); - AppendToContainerList(pContainer); - - if (M.GetByte("limittabs", 0) && !_tcscmp(name, _T("default"))) - iTemp |= CNT_CREATEFLAG_CLONED; - - // save container name to the db - if (!M.GetByte("singlewinmode", 0)) { - int iFirstFree = -1, iFound = FALSE, i = 0; - do { - char szCounter[10]; - itoa(i, szCounter, 10); - ptrT tszName(db_get_tsa(NULL, CONTAINER_KEY, szCounter)); - if (tszName == NULL) { - if (iFirstFree != -1) { - pContainer->iContainerIndex = iFirstFree; - itoa(iFirstFree, szCounter, 10); - } - else pContainer->iContainerIndex = i; - - db_set_ts(NULL, CONTAINER_KEY, szCounter, name); - BuildContainerMenu(); - break; - } - - if (!_tcsncmp(tszName, name, CONTAINER_NAMELEN)) { - pContainer->iContainerIndex = i; - iFound = TRUE; - } - else if (!_tcsncmp(tszName, _T("**mir_free**"), CONTAINER_NAMELEN)) - iFirstFree = i; - } - while (++i && iFound == FALSE); - } - else { - iTemp |= CNT_CREATEFLAG_CLONED; - pContainer->iContainerIndex = 1; - } - - if (iTemp & CNT_CREATEFLAG_MINIMIZED) - pContainer->dwFlags = CNT_CREATE_MINIMIZED; - if (iTemp & CNT_CREATEFLAG_CLONED) { - pContainer->dwFlags |= CNT_CREATE_CLONED; - pContainer->hContactFrom = hContactFrom; - } - pContainer->hwnd = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGCONTAINER), NULL, DlgProcContainer, (LPARAM)pContainer); - return pContainer; -} - -static LRESULT CALLBACK ContainerWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - TContainerData *pContainer = (TContainerData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - BOOL bSkinned = CSkin::m_skinEnabled ? TRUE : FALSE; - - switch (msg) { - case WM_NCPAINT: { - PAINTSTRUCT ps; - HDC hdcReal; - RECT rcClient; - LONG width, height; - HDC hdc; - CSkinItem *item = &SkinItems[0], *item_normal, *item_pressed, *item_hot; - HICON hIcon; - HFONT hOldFont = 0; - TEXTMETRIC tm; - - if (!pContainer || !bSkinned) - break; - - if (CSkin::m_frameSkins) { - RECT rcWindow, rcClient; - HDC dcFrame = GetDCEx(hwndDlg, 0, DCX_WINDOW|/*DCX_INTERSECTRGN|*/0x10000); // GetWindowDC(hwndDlg); - POINT pt, pt1; - LONG clip_top, clip_left; - HRGN rgn = 0; - CSkinItem *item; - TCHAR szWindowText[512]; - RECT rcText; - HDC dcMem = CreateCompatibleDC(pContainer->cachedDC ? pContainer->cachedDC : dcFrame); - HBITMAP hbmMem, hbmOld; - int i; - DRAWITEMSTRUCT dis = {0}; - - GetWindowRect(hwndDlg, &rcWindow); - GetClientRect(hwndDlg, &rcClient); - pt.y = 0; - pt.x = 0; - ClientToScreen(hwndDlg, &pt); - pt1.x = rcClient.right; - pt1.y = rcClient.bottom; - ClientToScreen(hwndDlg, &pt1); - clip_top = pt.y - rcWindow.top; - clip_left = pt.x - rcWindow.left; - - rcWindow.right = rcWindow.right - rcWindow.left; - rcWindow.bottom = rcWindow.bottom - rcWindow.top; - rcWindow.left = rcWindow.top = 0; - - hbmMem = CreateCompatibleBitmap(dcFrame, rcWindow.right, rcWindow.bottom); - hbmOld = (HBITMAP)SelectObject(dcMem, hbmMem); - - ExcludeClipRect(dcFrame, clip_left, clip_top, clip_left + (pt1.x - pt.x), clip_top + (pt1.y - pt.y)); - ExcludeClipRect(dcMem, clip_left, clip_top, clip_left + (pt1.x - pt.x), clip_top + (pt1.y - pt.y)); - item = pContainer->ncActive ? &SkinItems[ID_EXTBKFRAME] : &SkinItems[ID_EXTBKFRAMEINACTIVE]; - - CSkin::DrawItem(dcMem, &rcWindow, item); - - GetWindowText(hwndDlg, szWindowText, 512); - szWindowText[511] = 0; - hOldFont = (HFONT)SelectObject(dcMem, PluginConfig.hFontCaption); - GetTextMetrics(dcMem, &tm); - SetTextColor(dcMem, CInfoPanel::m_ipConfig.clrs[IPFONTCOUNT - 1]); - SetBkMode(dcMem, TRANSPARENT); - rcText.left =20 + CSkin::m_SkinnedFrame_left + CSkin::m_bClipBorder + CSkin::m_titleBarLeftOff;//26; - rcText.right = rcWindow.right - 3 * CSkin::m_titleBarButtonSize.cx - 11 - CSkin::m_titleBarRightOff; - rcText.top = CSkin::m_captionOffset + CSkin::m_bClipBorder; - rcText.bottom = rcText.top + tm.tmHeight; - rcText.left += CSkin::m_captionPadding; - DrawText(dcMem, szWindowText, -1, &rcText, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX); - SelectObject(dcMem, hOldFont); - - // icon - hIcon = (HICON)SendMessage(hwndDlg, WM_GETICON, ICON_SMALL, 0); - DrawIconEx(dcMem, 4 + CSkin::m_SkinnedFrame_left + CSkin::m_bClipBorder + CSkin::m_titleBarLeftOff, rcText.top + (rcText.bottom - rcText.top) / 2 - 8, hIcon, 16, 16, 0, 0, DI_NORMAL); - - // title buttons - pContainer->rcClose.top = pContainer->rcMin.top = pContainer->rcMax.top = CSkin::m_titleButtonTopOff; - pContainer->rcClose.bottom = pContainer->rcMin.bottom = pContainer->rcMax.bottom = CSkin::m_titleButtonTopOff + CSkin::m_titleBarButtonSize.cy; - - pContainer->rcClose.right = rcWindow.right - 10 - CSkin::m_titleBarRightOff; - pContainer->rcClose.left = pContainer->rcClose.right - CSkin::m_titleBarButtonSize.cx; - - pContainer->rcMax.right = pContainer->rcClose.left - 2; - pContainer->rcMax.left = pContainer->rcMax.right - CSkin::m_titleBarButtonSize.cx; - - pContainer->rcMin.right = pContainer->rcMax.left - 2; - pContainer->rcMin.left = pContainer->rcMin.right - CSkin::m_titleBarButtonSize.cx; - - item_normal = &SkinItems[ID_EXTBKTITLEBUTTON]; - item_hot = &SkinItems[ID_EXTBKTITLEBUTTONMOUSEOVER]; - item_pressed = &SkinItems[ID_EXTBKTITLEBUTTONPRESSED]; - - for (i=0; i < 3; i++) { - RECT *rc = 0; - HICON hIcon; - - switch (i) { - case 0: - rc = &pContainer->rcMin; - hIcon = CSkin::m_minIcon; - break; - case 1: - rc = &pContainer->rcMax; - hIcon = CSkin::m_maxIcon; - break; - case 2: - rc = &pContainer->rcClose; - hIcon = CSkin::m_closeIcon; - break; - } - if (rc) { - item = pContainer->buttons[i].isPressed ? item_pressed : (pContainer->buttons[i].isHot ? item_hot : item_normal); - CSkin::DrawItem(dcMem, rc, item); - DrawIconEx(dcMem, rc->left + ((rc->right - rc->left) / 2 - 8), rc->top + ((rc->bottom - rc->top) / 2 - 8), hIcon, 16, 16, 0, 0, DI_NORMAL); - } - } - SetBkMode(dcMem, TRANSPARENT); - BitBlt(dcFrame, 0, 0, rcWindow.right, rcWindow.bottom, dcMem, 0, 0, SRCCOPY); - SelectObject(dcMem, hbmOld); - DeleteObject(hbmMem); - DeleteDC(dcMem); - ReleaseDC(hwndDlg, dcFrame); - } - else mir_callNextSubclass(hwndDlg, ContainerWndProc, msg, wParam, lParam); - - hdcReal = BeginPaint(hwndDlg, &ps); - - GetClientRect(hwndDlg, &rcClient); - width = rcClient.right - rcClient.left; - height = rcClient.bottom - rcClient.top; - if (width != pContainer->oldDCSize.cx || height != pContainer->oldDCSize.cy) { - CSkinItem *sbaritem = &SkinItems[ID_EXTBKSTATUSBAR]; - BOOL statusBarSkinnd = !(pContainer->dwFlags & CNT_NOSTATUSBAR) && !sbaritem->IGNORED; - LONG sbarDelta = statusBarSkinnd ? pContainer->statusBarHeight : 0; - - pContainer->oldDCSize.cx = width; - pContainer->oldDCSize.cy = height; - - if (pContainer->cachedDC) { - SelectObject(pContainer->cachedDC, pContainer->oldHBM); - DeleteObject(pContainer->cachedHBM); - DeleteDC(pContainer->cachedDC); - } - pContainer->cachedDC = CreateCompatibleDC(hdcReal); - pContainer->cachedHBM = CreateCompatibleBitmap(hdcReal, width, height); - pContainer->oldHBM = (HBITMAP)SelectObject(pContainer->cachedDC, pContainer->cachedHBM); - - hdc = pContainer->cachedDC; - - if (!CSkin::DrawItem(hdc, &rcClient, item)) - FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE)); - - if (sbarDelta) { - rcClient.top = rcClient.bottom - sbarDelta; - CSkin::DrawItem(hdc, &rcClient, sbaritem); - } - } - BitBlt(hdcReal, 0, 0, width, height, pContainer->cachedDC, 0, 0, SRCCOPY); - EndPaint(hwndDlg, &ps); - return 0; - } - case WM_NCLBUTTONDOWN: - case WM_NCLBUTTONUP: - case WM_NCMOUSEHOVER: - case WM_NCMOUSEMOVE: - if (pContainer && CSkin::m_frameSkins) { - POINT pt; - RECT rcWindow; - BOOL isMin, isMax, isClose; - int i; - - GetCursorPos(&pt); - GetWindowRect(hwndDlg, &rcWindow); - - CopyMemory(&pContainer->oldbuttons[0], &pContainer->buttons[0], sizeof(TitleBtn) * 3); - ZeroMemory(&pContainer->buttons[0], sizeof(TitleBtn) * 3); - isMin = isMax = isClose = FALSE; - - if (pt.x >= (rcWindow.left + pContainer->rcMin.left) && pt.x <= (rcWindow.left + pContainer->rcClose.right) && pt.y < rcWindow.top + 24 && wParam != HTTOPRIGHT) { - LRESULT result = 0; //DefWindowProc(hwndDlg, msg, wParam, lParam); - HDC hdc = GetWindowDC(hwndDlg); - LONG left = rcWindow.left; - - pt.y = 10; - isMin = pt.x >= left + pContainer->rcMin.left && pt.x <= left + pContainer->rcMin.right; - isMax = pt.x >= left + pContainer->rcMax.left && pt.x <= left + pContainer->rcMax.right; - isClose = pt.x >= left + pContainer->rcClose.left && pt.x <= left + pContainer->rcClose.right; - - if (msg == WM_NCMOUSEMOVE) { - if (isMax) - pContainer->buttons[BTN_MAX].isHot = TRUE; - else if (isMin) - pContainer->buttons[BTN_MIN].isHot = TRUE; - else if (isClose) - pContainer->buttons[BTN_CLOSE].isHot = TRUE; - } - else if (msg == WM_NCLBUTTONDOWN) { - if (isMax) - pContainer->buttons[BTN_MAX].isPressed = TRUE; - else if (isMin) - pContainer->buttons[BTN_MIN].isPressed = TRUE; - else if (isClose) - pContainer->buttons[BTN_CLOSE].isPressed = TRUE; - } - else if (msg == WM_NCLBUTTONUP) { - if (isMin) - SendMessage(hwndDlg, WM_SYSCOMMAND, SC_MINIMIZE, 0); - else if (isMax) { - if (IsZoomed(hwndDlg)) - PostMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0); - else - PostMessage(hwndDlg, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - } - else if (isClose) - PostMessage(hwndDlg, WM_SYSCOMMAND, SC_CLOSE, 0); - } - for (i=0; i < 3; i++) { - if (pContainer->buttons[i].isHot != pContainer->oldbuttons[i].isHot) { - RECT *rc = 0; - HICON hIcon; - - switch (i) { - case 0: - rc = &pContainer->rcMin; - hIcon = CSkin::m_minIcon; - break; - case 1: - rc = &pContainer->rcMax; - hIcon = CSkin::m_maxIcon; - break; - case 2: - rc = &pContainer->rcClose; - hIcon = CSkin::m_closeIcon; - break; - } - if (rc) { - CSkinItem *item = &SkinItems[pContainer->buttons[i].isPressed ? ID_EXTBKTITLEBUTTONPRESSED : (pContainer->buttons[i].isHot ? ID_EXTBKTITLEBUTTONMOUSEOVER : ID_EXTBKTITLEBUTTON)]; - CSkin::DrawItem(hdc, rc, item); - DrawIconEx(hdc, rc->left + ((rc->right - rc->left) / 2 - 8), rc->top + ((rc->bottom - rc->top) / 2 - 8), hIcon, 16, 16, 0, 0, DI_NORMAL); - } - } - } - ReleaseDC(hwndDlg, hdc); - return result; - } - else { - LRESULT result = DefWindowProc(hwndDlg, msg, wParam, lParam); - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_NOCHILDREN); - return result; - } - } - break; - - case WM_SETCURSOR: - if (CSkin::m_frameSkins && (HWND)wParam == hwndDlg) { - DefWindowProc(hwndDlg, msg, wParam, lParam); - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_NOCHILDREN); - return 1; - } - break; - - case WM_NCCALCSIZE: - if (!CSkin::m_frameSkins) - break; - - if (wParam) { - RECT *rc; - NCCALCSIZE_PARAMS *ncsp = (NCCALCSIZE_PARAMS *)lParam; - - DefWindowProc(hwndDlg, msg, wParam, lParam); - rc = &ncsp->rgrc[0]; - - rc->left += CSkin::m_realSkinnedFrame_left; - rc->right -= CSkin::m_realSkinnedFrame_right; - rc->bottom -= CSkin::m_realSkinnedFrame_bottom; - rc->top += CSkin::m_realSkinnedFrame_caption; - return TRUE; - } - - return DefWindowProc(hwndDlg, msg, wParam, lParam); - - case WM_NCACTIVATE: - if (pContainer) { - pContainer->ncActive = wParam; - if (bSkinned && CSkin::m_frameSkins) { - SendMessage(hwndDlg, WM_NCPAINT, 0, 0); - return 1; - } - } - break; - case WM_SETTEXT: - case WM_SETICON: - if (CSkin::m_frameSkins) { - DefWindowProc(hwndDlg, msg, wParam, lParam); - RedrawWindow(hwndDlg, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); - return 0; - } - break; - - case WM_NCHITTEST: - { - RECT r; - POINT pt; - int k = 0; - int clip = CSkin::m_bClipBorder; - - if (!pContainer) - break; - - if (!(pContainer->dwFlags & CNT_NOTITLE)) - break; - - GetWindowRect(hwndDlg, &r); - GetCursorPos(&pt); - if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6) { - if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) - return HTBOTTOM; - if (pt.x < r.left + clip + 10) - return HTBOTTOMLEFT; - if (pt.x > r.right - clip - 10) - return HTBOTTOMRIGHT; - - } - else if (pt.y >= r.top && pt.y <= r.top + 6) { - if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) - return HTTOP; - if (pt.x < r.left + clip + 10) - return HTTOPLEFT; - if (pt.x > r.right - clip - 10) - return HTTOPRIGHT; - } - else if (pt.x >= r.left && pt.x <= r.left + clip + 6) - return HTLEFT; - else if (pt.x >= r.right - clip - 6 && pt.x <= r.right) - return HTRIGHT; - } - return(DefWindowProc(hwndDlg, WM_NCHITTEST, wParam, lParam)); - - case 0xae: // must be some undocumented message - seems it messes with the title bar... - if (CSkin::m_frameSkins) - return 0; - } - return mir_callNextSubclass(hwndDlg, ContainerWndProc, msg, wParam, lParam); -} - -// container window procedure... - -static BOOL fHaveTipper = FALSE; - -static INT_PTR CALLBACK DlgProcContainer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int iItem = 0; - TCITEM item; - - TContainerData *pContainer = (TContainerData*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - BOOL bSkinned = CSkin::m_skinEnabled ? TRUE : FALSE; - HWND hwndTab = GetDlgItem(hwndDlg, IDC_MSGTABS); - - switch (msg) { - case WM_INITDIALOG: - { - bool bAero = M.isAero(); - BOOL isFlat = M.GetByte("tbflat", 1); - BOOL isThemed = !M.GetByte("nlflat", 0); - - fHaveTipper = ServiceExists("mToolTip/ShowTip"); - fForceOverlayIcons = M.GetByte("forceTaskBarStatusOverlays", 0) ? true : false; - - pContainer = (TContainerData*)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pContainer); - mir_subclassWindow(hwndDlg, ContainerWndProc); - - pContainer->hwnd = hwndDlg; - DWORD dwCreateFlags = pContainer->dwFlags; - - pContainer->isCloned = (pContainer->dwFlags & CNT_CREATE_CLONED); - pContainer->fPrivateThemeChanged = FALSE; - - SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); // set options... - pContainer->dwFlags |= dwCreateFlags; - - LoadOverrideTheme(pContainer); - DWORD ws = GetWindowLongPtr(hwndTab, GWL_STYLE); - if (pContainer->dwFlagsEx & TCF_FLAT) - ws |= TCS_BUTTONS; - - memset((void*)&pContainer->mOld, -1000, sizeof(MARGINS)); - - if (pContainer->dwFlagsEx & TCF_SINGLEROWTABCONTROL) { - ws &= ~TCS_MULTILINE; - ws |= TCS_SINGLELINE; - ws |= TCS_FIXEDWIDTH; - } - else { - ws &= ~TCS_SINGLELINE; - ws |= TCS_MULTILINE; - if (ws & TCS_BUTTONS) - ws |= TCS_FIXEDWIDTH; - } - SetWindowLongPtr(hwndTab, GWL_STYLE, ws); - - pContainer->buttonItems = g_ButtonSet.items; - - pContainer->dwFlags = ((pContainer->dwFlagsEx & (TCF_SBARLEFT | TCF_SBARRIGHT)) ? - pContainer->dwFlags | CNT_SIDEBAR : pContainer->dwFlags & ~CNT_SIDEBAR); - - pContainer->SideBar = new CSideBar(pContainer); - pContainer->MenuBar = new CMenuBar(hwndDlg, pContainer); - - SetClassLongPtr(hwndDlg, GCL_STYLE, GetClassLongPtr(hwndDlg, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); - SetClassLongPtr(hwndTab, GCL_STYLE, GetClassLongPtr(hwndTab, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); - - SetClassLongPtr(hwndDlg, GCL_STYLE, GetClassLongPtr(hwndDlg, GCL_STYLE) & ~CS_DROPSHADOW); - - // additional system menu items... - HMENU hSysmenu = GetSystemMenu(hwndDlg, FALSE); - int iMenuItems = GetMenuItemCount(hSysmenu); - - InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_SEPARATOR, 0, _T("")); - InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top")); - if (!CSkin::m_frameSkins) - InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_NOTITLE, TranslateT("Hide title bar")); - InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_SEPARATOR, 0, _T("")); - InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_MOREOPTIONS, TranslateT("Container options...")); - SetWindowText(hwndDlg, TranslateT("Message Session...")); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PluginConfig.g_iconContainer); - - // make the tab control the controlling parent window for all message dialogs - - ws = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_EXSTYLE); - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_EXSTYLE, ws | WS_EX_CONTROLPARENT); - - LONG x_pad = M.GetByte("x-pad", 3) + (pContainer->dwFlagsEx & TCF_CLOSEBUTTON ? 7 : 0); - LONG y_pad = M.GetByte("y-pad", 3) + ((pContainer->dwFlags & CNT_TABSBOTTOM) ? 1 : 0); - - if (pContainer->dwFlagsEx & TCF_FLAT) - y_pad += 1; //(pContainer->dwFlags & CNT_TABSBOTTOM ? 1 : 2); - - TabCtrl_SetPadding(GetDlgItem(hwndDlg, IDC_MSGTABS), x_pad, y_pad); - - TabCtrl_SetImageList(GetDlgItem(hwndDlg, IDC_MSGTABS), PluginConfig.g_hImageList); - - SendMessage(hwndDlg, DM_CONFIGURECONTAINER, 0, 10); - - // context menu - pContainer->hMenuContext = PluginConfig.g_hMenuContext; - - // tab tooltips... - if (!fHaveTipper || M.GetByte("d_tooltips", 0) == 0) { - pContainer->hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL, g_hInst, (LPVOID) NULL); - - if (pContainer->hwndTip) { - SetWindowPos(pContainer->hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - TabCtrl_SetToolTips(GetDlgItem(hwndDlg, IDC_MSGTABS), pContainer->hwndTip); - } - } - else pContainer->hwndTip = 0; - - if (pContainer->dwFlags & CNT_CREATE_MINIMIZED) { - WINDOWPLACEMENT wp = {0}; - wp.length = sizeof(wp); - - SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) & ~WS_VISIBLE); - ShowWindow(hwndDlg, SW_SHOWMINNOACTIVE); - SendMessage(hwndDlg, DM_RESTOREWINDOWPOS, 0, 0); - //GetClientRect(hwndDlg, &pContainer->rcSaved); - ShowWindow(hwndDlg, SW_SHOWMINNOACTIVE); - GetWindowPlacement(hwndDlg, &wp); - pContainer->rcSaved.left = pContainer->rcSaved.top = 0; - pContainer->rcSaved.right = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - pContainer->rcSaved.bottom = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; - } - else { - SendMessage(hwndDlg, DM_RESTOREWINDOWPOS, 0, 0); - ShowWindow(hwndDlg, SW_SHOWNORMAL); - } - } - - // prevent ugly back background being visible while tabbed clients are created - if (M.isAero()) { - MARGINS m = {-1}; - CMimAPI::m_pfnDwmExtendFrameIntoClientArea(hwndDlg, &m); - } - return TRUE; - - case DM_RESTOREWINDOWPOS: - // retrieve the container window geometry information from the database. - if (pContainer->isCloned && pContainer->hContactFrom != 0 && !(pContainer->dwFlags & CNT_GLOBALSIZE)) { - if (Utils_RestoreWindowPosition(hwndDlg, pContainer->hContactFrom, SRMSGMOD_T, "split")) { - if (Utils_RestoreWindowPositionNoMove(hwndDlg, pContainer->hContactFrom, SRMSGMOD_T, "split")) - if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, "split")) - if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, "split")) - SetWindowPos(hwndDlg, 0, 50, 50, 450, 300, SWP_NOZORDER | SWP_NOACTIVATE); - } - } - else { - if (pContainer->dwFlags & CNT_GLOBALSIZE) { - if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, "split")) - if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, "split")) - SetWindowPos(hwndDlg, 0, 50, 50, 450, 300, SWP_NOZORDER | SWP_NOACTIVATE); - } - else { - char szCName[CONTAINER_NAMELEN + 20]; - mir_snprintf(szCName, sizeof(szCName), "%s%d", CONTAINER_PREFIX, pContainer->iContainerIndex); - if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, szCName)) { - if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, szCName)) - if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, "split")) - if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, "split")) - SetWindowPos(hwndDlg, 0, 50, 50, 450, 300, SWP_NOZORDER | SWP_NOACTIVATE); - } - } - } - return 0; - - case WM_SIZE: - if (IsIconic(hwndDlg)) - pContainer->dwFlags |= CNT_DEFERREDSIZEREQUEST; - else { - RECT rcClient, rcUnadjusted; - TCITEM item = {0}; - POINT pt = {0}; - - GetClientRect(hwndDlg, &rcClient); - pContainer->MenuBar->getClientRect(); - - if (pContainer->hwndStatus) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - SendMessage(pContainer->hwndStatus, WM_USER + 101, 0, (LPARAM)dat); - - RECT rcs; - GetWindowRect(pContainer->hwndStatus, &rcs); - pContainer->statusBarHeight = (rcs.bottom - rcs.top) + 1; - SendMessage(pContainer->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, 0); - } - else pContainer->statusBarHeight = 0; - - CopyRect(&pContainer->rcSaved, &rcClient); - rcUnadjusted = rcClient; - - pContainer->MenuBar->Resize(LOWORD(lParam), HIWORD(lParam), FALSE); - LONG rebarHeight = pContainer->MenuBar->getHeight(); - pContainer->MenuBar->Show((pContainer->dwFlags & CNT_NOMENUBAR) ? SW_HIDE : SW_SHOW); - - LONG sbarWidth = pContainer->SideBar->getWidth(); - LONG sbarWidth_left = pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT ? sbarWidth : 0; - - if (lParam) { - DWORD dwSWPFlags = SWP_NOACTIVATE|SWP_NOZORDER |SWP_DEFERERASE | SWP_NOCOPYBITS; // | SWP_NOSENDCHANGING | SWP_ASYNCWINDOWPOS; - if (pContainer->dwFlags & CNT_TABSBOTTOM) - SetWindowPos(hwndTab, 0, pContainer->tBorder_outer_left + sbarWidth_left, pContainer->tBorder_outer_top + rebarHeight, - (rcClient.right - rcClient.left) - (pContainer->tBorder_outer_left + pContainer->tBorder_outer_right + sbarWidth), - (rcClient.bottom - rcClient.top) - pContainer->statusBarHeight - (pContainer->tBorder_outer_top + pContainer->tBorder_outer_bottom) - rebarHeight, dwSWPFlags); - else - SetWindowPos(hwndTab, 0, pContainer->tBorder_outer_left + sbarWidth_left, pContainer->tBorder_outer_top + rebarHeight, - (rcClient.right - rcClient.left) - (pContainer->tBorder_outer_left + pContainer->tBorder_outer_right + sbarWidth), - (rcClient.bottom - rcClient.top) - pContainer->statusBarHeight - (pContainer->tBorder_outer_top + pContainer->tBorder_outer_bottom) - rebarHeight, dwSWPFlags); - } - - pContainer->SideBar->resizeScrollWnd(sbarWidth_left ? pContainer->tBorder_outer_left : rcClient.right - pContainer->tBorder_outer_right - (sbarWidth - 2), - pContainer->tBorder_outer_top + rebarHeight, 0, - (rcClient.bottom - rcClient.top) - pContainer->statusBarHeight - (pContainer->tBorder_outer_top + pContainer->tBorder_outer_bottom) - rebarHeight); - - AdjustTabClientRect(pContainer, &rcClient); - - BOOL sizeChanged = (((rcClient.right - rcClient.left) != pContainer->preSIZE.cx) || ((rcClient.bottom - rcClient.top) != pContainer->preSIZE.cy)); - if (sizeChanged) { - pContainer->preSIZE.cx = rcClient.right - rcClient.left; - pContainer->preSIZE.cy = rcClient.bottom - rcClient.top; - } - - // we care about all client sessions, but we really resize only the active tab (hwndActive) - // we tell inactive tabs to resize theirselves later when they get activated (DM_CHECKSIZE - // just queues a resize request) - int nCount = TabCtrl_GetItemCount(hwndTab); - - for (int i=0; i < nCount; i++) { - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, i, &item); - if ((HWND)item.lParam == pContainer->hwndActive) { - SetWindowPos((HWND)item.lParam, 0, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), - SWP_NOSENDCHANGING|SWP_NOACTIVATE/*|SWP_NOCOPYBITS*/); - if (!pContainer->bSizingLoop && sizeChanged) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - DM_ScrollToBottom(dat, 0, 1); - } - } - else if (sizeChanged) - SendMessage((HWND)item.lParam, DM_CHECKSIZE, 0, 0); - } - pContainer->SideBar->scrollIntoView(); - - if (!M.isAero()) { // aero mode uses buffered paint, no forced redraw needed - RedrawWindow(hwndTab, NULL, NULL, RDW_INVALIDATE | (pContainer->bSizingLoop ? RDW_ERASE : 0)); - RedrawWindow(hwndDlg, NULL, NULL, (bSkinned ? RDW_FRAME : 0) | RDW_INVALIDATE | ((pContainer->bSizingLoop || wParam == SIZE_RESTORED ) ? RDW_ERASE : 0)); - } - - if (pContainer->hwndStatus) - InvalidateRect(pContainer->hwndStatus, NULL, FALSE); - - if ((CSkin::m_bClipBorder != 0 || CSkin::m_bRoundedCorner) && CSkin::m_frameSkins) { - HRGN rgn; - int clip = CSkin::m_bClipBorder; - - RECT rcWindow; - GetWindowRect(hwndDlg, &rcWindow); - - if (CSkin::m_bRoundedCorner) - rgn = CreateRoundRectRgn(clip, clip, (rcWindow.right - rcWindow.left) - clip + 1, - (rcWindow.bottom - rcWindow.top) - clip + 1, CSkin::m_bRoundedCorner + clip, CSkin::m_bRoundedCorner + clip); - else - rgn = CreateRectRgn(clip, clip, (rcWindow.right - rcWindow.left) - clip, (rcWindow.bottom - rcWindow.top) - clip); - SetWindowRgn(hwndDlg, rgn, TRUE); - } - else if (CSkin::m_frameSkins) - SetWindowRgn(hwndDlg, NULL, TRUE); - } - break; - - case WM_NOTIFY: - if (pContainer->MenuBar) { - LRESULT processed = pContainer->MenuBar->processMsg(msg, wParam, lParam); - if (processed != -1) { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, processed); - return(processed); - } - } - - if (pContainer != NULL && pContainer->hwndStatus != 0 && ((LPNMHDR)lParam)->hwndFrom == pContainer->hwndStatus) { - switch (((LPNMHDR)lParam)->code) { - case NM_CLICK: - case NM_RCLICK: - unsigned int nParts, nPanel; - NMMOUSE *nm = (NMMOUSE*)lParam; - RECT rc; - - nParts = SendMessage(pContainer->hwndStatus, SB_GETPARTS, 0, 0); - if (nm->dwItemSpec == 0xFFFFFFFE) { - nPanel = 2; - SendMessage(pContainer->hwndStatus, SB_GETRECT, nPanel, (LPARAM)&rc); - if (nm->pt.x > rc.left && nm->pt.x < rc.right) - goto panel_found; - else - return FALSE; - } - else nPanel = nm->dwItemSpec; -panel_found: - if (nPanel == 2) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - SendMessage(pContainer->hwndStatus, SB_GETRECT, nPanel, (LPARAM)&rc); - if (dat) - SI_CheckStatusIconClick(dat, pContainer->hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code); - } - else if (((LPNMHDR)lParam)->code == NM_RCLICK) { - POINT pt; - MCONTACT hContact = 0; - HMENU hMenu; - - GetCursorPos(&pt); - SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - if (hContact) { - int iSel = 0; - hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, hContact, 0); - iSel = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); - if (iSel) - CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(iSel), MPCF_CONTACTMENU), hContact); - DestroyMenu(hMenu); - } - } - return TRUE; - } - break; - } - - switch (((LPNMHDR)lParam)->code) { - case TCN_SELCHANGE: - ZeroMemory(&item, sizeof(item)); - iItem = TabCtrl_GetCurSel(hwndTab); - item.mask = TCIF_PARAM; - if (TabCtrl_GetItem(hwndTab, iItem, &item)) { - if ((HWND)item.lParam != pContainer->hwndActive) { - if (pContainer->hwndActive && IsWindow(pContainer->hwndActive)) - ShowWindow(pContainer->hwndActive, SW_HIDE); - } - pContainer->hwndActive = (HWND)item.lParam; - SendMessage((HWND)item.lParam, DM_SAVESIZE, 0, 1); - ShowWindow((HWND)item.lParam, SW_SHOW); - if (!IsIconic(hwndDlg)) - SetFocus(pContainer->hwndActive); - } - SendMessage(hwndTab, EM_VALIDATEBOTTOM, 0, 0); - return 0; - - // tooltips - case NM_RCLICK: - { - int iItem; - bool fFromSidebar = false; - TCITEM item = { 0 }; - TWindowData *dat = 0; - - POINT pt, pt1; - GetCursorPos(&pt); - pt1 = pt; - HMENU subMenu = GetSubMenu(pContainer->hMenuContext, 0); - - if (((LPNMHDR)lParam)->idFrom == IDC_MSGTABS) { - if ((iItem = GetTabItemFromMouse(hwndTab, &pt)) == -1) - break; - - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, iItem, &item); - if (item.lParam && IsWindow((HWND)item.lParam)) - dat = (TWindowData*)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA); - } - // sent from a sidebar button (RMB click) instead of the tab control - else if (((LPNMHDR)lParam)->idFrom == 5000) { - TSideBarNotify* n = reinterpret_cast(lParam); - dat = const_cast(n->dat); - fFromSidebar = true; - } - - if (dat) - MsgWindowUpdateMenu(dat, subMenu, MENU_TABCONTEXT); - - int iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt1.x, pt1.y, 0, hwndDlg, NULL); - if (iSelection >= IDM_CONTAINERMENU) { - DBVARIANT dbv = {0}; - char szIndex[10]; - itoa(iSelection - IDM_CONTAINERMENU, szIndex, 10); - if (iSelection - IDM_CONTAINERMENU >= 0) { - ptrT tszName(db_get_tsa(NULL, CONTAINER_KEY, szIndex)); - if (tszName != NULL) - SendMessage((HWND)item.lParam, DM_CONTAINERSELECTED, 0, tszName); - } - return 1; - } - switch (iSelection) { - case ID_TABMENU_CLOSETAB: - if (fFromSidebar) - SendMessage(dat->hwnd, WM_CLOSE, 1, 0); - else - SendMessage(hwndDlg, DM_CLOSETABATMOUSE, 0, (LPARAM)&pt1); - break; - case ID_TABMENU_CLOSEOTHERTABS: - CloseOtherTabs(hwndTab, *dat); - break; - case ID_TABMENU_SAVETABPOSITION: - db_set_dw(dat->hContact, SRMSGMOD_T, "tabindex", dat->iTabID * 100); - break; - case ID_TABMENU_CLEARSAVEDTABPOSITION: - db_unset(dat->hContact, SRMSGMOD_T, "tabindex"); - break; - case ID_TABMENU_LEAVECHATROOM: - if (dat && dat->bType == SESSIONTYPE_CHAT) { - SESSION_INFO *si = dat->si; - if (si && dat->hContact) { - char *szProto = GetContactProto(dat->hContact); - if (szProto) - CallProtoService( szProto, PS_LEAVECHAT, dat->hContact, 0 ); - } - } - break; - case ID_TABMENU_ATTACHTOCONTAINER: - if ((iItem = GetTabItemFromMouse(hwndTab, &pt1)) == -1) - break; - ZeroMemory(&item, sizeof(item)); - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, iItem, &item); - CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_SELECTCONTAINER), hwndDlg, SelectContainerDlgProc, (LPARAM)item.lParam); - break; - case ID_TABMENU_CONTAINEROPTIONS: - if (pContainer->hWndOptions == 0) - CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CONTAINEROPTIONS), hwndDlg, DlgProcContainerOptions, (LPARAM)pContainer); - break; - case ID_TABMENU_CLOSECONTAINER: - SendMessage(hwndDlg, WM_CLOSE, 0, 0); - break; - } - InvalidateRect(hwndTab, NULL, FALSE); - return 1; - } - } - break; - - case WM_COMMAND: - { - bool fProcessContactMenu = pContainer->MenuBar->isContactMenu(); - bool fProcessMainMenu = pContainer->MenuBar->isMainMenu(); - pContainer->MenuBar->Cancel(); - - MCONTACT hContact; - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - DWORD dwOldFlags = pContainer->dwFlags; - ButtonItem *pItem = pContainer->buttonItems; - - if (dat) { - DWORD dwOldMsgWindowFlags = dat->dwFlags; - DWORD dwOldEventIsShown = dat->dwFlagsEx; - - if (fProcessContactMenu) - return(CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)dat->hContact)); - else if (fProcessMainMenu) { - return(CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_MAINMENU), 0)); - } - else if (MsgWindowMenuHandler(dat, LOWORD(wParam), MENU_PICMENU) == 1) - break; - } - SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - if (LOWORD(wParam) == IDC_TBFIRSTUID - 1) - break; - - switch (LOWORD(wParam)) { - case IDC_TOGGLESIDEBAR: - { - RECT rc; - LONG dwNewLeft; - BOOL skinnedMode = bSkinned; - - skinnedMode |= (IsThemeActive() ? 1 : 0); - - GetWindowRect(hwndDlg, &rc); - - bool fVisible = pContainer->SideBar->isVisible(); - if (fVisible) { - dwNewLeft = pContainer->SideBar->getWidth(); - pContainer->SideBar->setVisible(false); - } - else { - pContainer->SideBar->setVisible(true); - dwNewLeft = -(pContainer->SideBar->getWidth()); - } - - pContainer->preSIZE.cx = pContainer->preSIZE.cy = 0; - pContainer->oldDCSize.cx = pContainer->oldDCSize.cy = 0; - - PostMessage(hwndDlg, WM_SIZE, 0, 1); - } - break; - - case IDC_SIDEBARDOWN: - case IDC_SIDEBARUP: - { - HWND hwnd = GetFocus(); - pContainer->SideBar->processScrollerButtons(LOWORD(wParam)); - SetFocus(hwnd); - } - break; - - default: - Utils::CmdDispatcher(Utils::CMD_CONTAINER, hwndDlg, LOWORD(wParam), wParam, lParam, 0, pContainer); - } - if (pContainer->dwFlags != dwOldFlags) - SendMessage(hwndDlg, DM_CONFIGURECONTAINER, 0, 0); - } - break; - - case WM_ENTERSIZEMOVE: - { - RECT rc; - GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc); - - SIZE sz; - sz.cx = rc.right - rc.left; - sz.cy = rc.bottom - rc.top; - pContainer->oldSize = sz; - pContainer->bSizingLoop = TRUE; - } - break; - - case WM_EXITSIZEMOVE: - { - RECT rc; - GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc); - if (!((rc.right - rc.left) == pContainer->oldSize.cx && (rc.bottom - rc.top) == pContainer->oldSize.cy)) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - DM_ScrollToBottom(dat, 0, 0); - SendMessage(pContainer->hwndActive, WM_SIZE, 0, 0); - } - pContainer->bSizingLoop = FALSE; - } - break; - - // determine minimum and maximum size limits - // 1) for maximizing the window when the "vertical maximize" option is set - // 2) to limit the minimum height when manually resizing the window - // (this avoids overlapping of controls inside the window and ensures - // that at least 2 lines of the message log are always visible). - case WM_GETMINMAXINFO: - { - RECT rc, rcWindow, rcClient = {0}; - POINT pt; - - MINMAXINFO *mmi = (MINMAXINFO *) lParam; - mmi->ptMinTrackSize.x = 275; - mmi->ptMinTrackSize.y = 130; - GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc); - if (pContainer->hwndActive) // at container creation time, there is no hwndActive yet.. - GetClientRect(pContainer->hwndActive, &rcClient); - GetWindowRect(hwndDlg, &rcWindow); - pt.y = rc.top; - TabCtrl_AdjustRect(GetDlgItem(hwndDlg, IDC_MSGTABS), FALSE, &rc); - // uChildMinHeight holds the min height for the client window only - // so let's add the container's vertical padding (title bar, tab bar, - // window border, status bar) to this value - if (pContainer->hwndActive) - mmi->ptMinTrackSize.y = pContainer->uChildMinHeight + (pContainer->hwndActive ? ((rcWindow.bottom - rcWindow.top) - rcClient.bottom) : 0); - - if (pContainer->dwFlags & CNT_VERTICALMAX || (GetKeyState(VK_CONTROL) & 0x8000)) { - RECT rcDesktop = {0}; - BOOL fDesktopValid = FALSE; - int monitorXOffset = 0; - WINDOWPLACEMENT wp = {0}; - - HMONITOR hMonitor = MonitorFromWindow(hwndDlg, 2); - if (hMonitor) { - MONITORINFO mi = { 0 }; - mi.cbSize = sizeof(mi); - GetMonitorInfoA(hMonitor, &mi); - rcDesktop = mi.rcWork; - OffsetRect(&rcDesktop, -mi.rcMonitor.left, -mi.rcMonitor.top); - monitorXOffset = mi.rcMonitor.left; - fDesktopValid = TRUE; - } - if (!fDesktopValid) - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0); - - wp.length = sizeof(wp); - GetWindowPlacement(hwndDlg, &wp); - mmi->ptMaxSize.y = rcDesktop.bottom - rcDesktop.top; - mmi->ptMaxSize.x = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - mmi->ptMaxPosition.x = wp.rcNormalPosition.left - monitorXOffset; - mmi->ptMaxPosition.y = 0; - if (IsIconic(hwndDlg)) { - mmi->ptMaxPosition.x += rcDesktop.left; - mmi->ptMaxPosition.y += rcDesktop.top; - } - - // protect against invalid values... - if (mmi->ptMinTrackSize.y < 50 || mmi->ptMinTrackSize.y > rcDesktop.bottom) - mmi->ptMinTrackSize.y = 130; - } - } - return 0; - - case DM_UPDATETITLE: - { - MCONTACT hContact = 0; - TWindowData *dat = NULL; - - if (lParam) { // lParam != 0 means sent by a chat window - TCHAR szText[512]; - dat = (TWindowData*)GetWindowLongPtr((HWND)wParam, GWLP_USERDATA); - GetWindowText((HWND)wParam, szText, SIZEOF(szText)); - szText[SIZEOF(szText)-1] = 0; - SetWindowText(hwndDlg, szText); - if (dat) - SendMessage(hwndDlg, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hTabIcon != dat->hTabStatusIcon ? dat->hTabIcon : dat->hTabStatusIcon)); - return 0; - } - if (wParam == 0) { // no hContact given - obtain the hContact for the active tab - if (pContainer->hwndActive && IsWindow(pContainer->hwndActive)) - SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - else - break; - dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - } - else { - HWND hwnd = M.FindWindow(wParam); - if (hwnd == 0) { - SESSION_INFO *si = SM_FindSessionByHCONTACT(wParam); - if (si) { - SendMessage(si->hWnd, GC_UPDATETITLE, 0, 0); - return 0; - } - } - hContact = wParam; - if (hwnd && hContact) - dat = (TWindowData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - } - if (dat) { - SendMessage(hwndDlg, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabStatusIcon)); - const TCHAR *szNewTitle = Utils::FormatTitleBar(dat, pContainer->settings->szTitleFormat); - if (szNewTitle) { - SetWindowText(hwndDlg, szNewTitle); - mir_free((void*)szNewTitle); - } - } - } - return 0; - - case WM_TIMER: - if (wParam == TIMERID_HEARTBEAT) { - TWindowData *dat = 0; - if (GetForegroundWindow() != hwndDlg && (pContainer->settings->autoCloseSeconds > 0) && !pContainer->fHidden) { - BOOL fResult = TRUE; - BroadCastContainer(pContainer, DM_CHECKAUTOHIDE, (WPARAM)pContainer->settings->autoCloseSeconds, (LPARAM)&fResult); - - if (fResult && 0 == pContainer->hWndOptions) - PostMessage(hwndDlg, WM_CLOSE, 1, 0); - } - dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - if (dat && dat->bType == SESSIONTYPE_IM) { - if (dat->idle && pContainer->hwndActive && IsWindow(pContainer->hwndActive)) - dat->Panel->Invalidate(TRUE); - } - else if (dat) - SendMessage(dat->hwnd, GC_UPDATESTATUSBAR, 0, 0); - } - else if (wParam == TIMERID_HOVER) { - RECT rcWindow; - GetWindowRect(hwndDlg, &rcWindow); - } - break; - - case WM_SYSCOMMAND: - switch (wParam) { - case IDM_STAYONTOP: - SetWindowPos(hwndDlg, (pContainer->dwFlags & CNT_STICKY) ? HWND_NOTOPMOST : HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - CheckMenuItem(GetSystemMenu(hwndDlg, FALSE), IDM_STAYONTOP, (pContainer->dwFlags & CNT_STICKY) ? MF_BYCOMMAND | MF_UNCHECKED : MF_BYCOMMAND | MF_CHECKED); - ApplyContainerSetting(pContainer, CNT_STICKY, pContainer->dwFlags & CNT_STICKY ? 0 : 1, false); - break; - case IDM_NOTITLE: - pContainer->oldSize.cx = 0; - pContainer->oldSize.cy = 0; - - CheckMenuItem(GetSystemMenu(hwndDlg, FALSE), IDM_NOTITLE, (pContainer->dwFlags & CNT_NOTITLE) ? MF_BYCOMMAND | MF_UNCHECKED : MF_BYCOMMAND | MF_CHECKED); - ApplyContainerSetting(pContainer, CNT_NOTITLE, pContainer->dwFlags & CNT_NOTITLE ? 0 : 1, false); - break; - case IDM_MOREOPTIONS: - if (IsIconic(pContainer->hwnd)) - SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); - if (pContainer->hWndOptions == 0) - CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CONTAINEROPTIONS), hwndDlg, DlgProcContainerOptions, (LPARAM)pContainer); - break; - case SC_MAXIMIZE: - pContainer->oldSize.cx = pContainer->oldSize.cy = 0; - break; - case SC_RESTORE: - pContainer->oldSize.cx = pContainer->oldSize.cy = 0; - memset((void*)&pContainer->mOld, -1000, sizeof(MARGINS)); - break; - case SC_MINIMIZE: - TWindowData *dat = reinterpret_cast(GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA)); - if (dat) { - //GetWindowRect(GetDlgItem(pContainer->hwndActive, dat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &pContainer->rcLogSaved); - GetWindowRect(pContainer->hwndActive, &pContainer->rcLogSaved); - pContainer->ptLogSaved.x = pContainer->rcLogSaved.left; - pContainer->ptLogSaved.y = pContainer->rcLogSaved.top; - ScreenToClient(hwndDlg, &pContainer->ptLogSaved); - } - } - break; - - case DM_SELECTTAB: - switch (wParam) { - int iItems, iCurrent, iNewTab; - - case DM_SELECT_BY_HWND: - ActivateTabFromHWND(hwndTab, (HWND)lParam); - break; - - case DM_SELECT_NEXT: - case DM_SELECT_PREV: - case DM_SELECT_BY_INDEX: - iItems = TabCtrl_GetItemCount(hwndTab); - iCurrent = TabCtrl_GetCurSel(hwndTab); - - if (iItems == 1) - break; - if (wParam == DM_SELECT_PREV) - iNewTab = iCurrent ? iCurrent - 1 : iItems - 1; // cycle if current is already the leftmost tab.. - else if (wParam == DM_SELECT_NEXT) - iNewTab = (iCurrent == (iItems - 1)) ? 0 : iCurrent + 1; - else if (wParam == DM_SELECT_BY_INDEX) { - if ((int)lParam > iItems) - break; - iNewTab = lParam - 1; - } - - if (iNewTab != iCurrent) { - TabControlData *tabdat = (TabControlData *)GetWindowLongPtr(hwndTab, GWLP_USERDATA); - ZeroMemory(&item, sizeof(item)); - item.mask = TCIF_PARAM; - if (TabCtrl_GetItem(hwndTab, iNewTab, &item)) { - TabCtrl_SetCurSel(hwndTab, iNewTab); - ShowWindow(pContainer->hwndActive, SW_HIDE); - pContainer->hwndActive = (HWND)item.lParam; - ShowWindow((HWND)item.lParam, SW_SHOW); - SetFocus(pContainer->hwndActive); - } - } - break; - } - break; - - case WM_INITMENUPOPUP: - pContainer->MenuBar->setActive(reinterpret_cast(wParam)); - break; - - case WM_LBUTTONDOWN: - if (pContainer->dwFlags & CNT_NOTITLE) { - POINT pt; - GetCursorPos(&pt); - return SendMessage(hwndDlg, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); - } - break; - - // pass the WM_ACTIVATE msg to the active message dialog child - case WM_NCACTIVATE: - if (IsWindowVisible(hwndDlg)) - pContainer->fHidden = false; - break; - - case WM_ACTIVATE: - if (pContainer == NULL) - break; - - if (LOWORD(wParam == WA_INACTIVE)) - BroadCastContainer(pContainer, DM_CHECKINFOTIP, wParam, lParam); - - if (LOWORD(wParam == WA_INACTIVE) && (HWND)lParam != PluginConfig.g_hwndHotkeyHandler && GetParent((HWND)lParam) != hwndDlg) { - BOOL fTransAllowed = !bSkinned || PluginConfig.m_bIsVista; - - if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) { - SetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)HIWORD(pContainer->settings->dwTransparency), (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); - } - } - pContainer->hwndSaved = 0; - - if (LOWORD(wParam) != WA_ACTIVE) { - pContainer->MenuBar->Cancel(); - break; - } - - case WM_MOUSEACTIVATE: - if (pContainer != NULL) { - TCITEM item; - int curItem = 0; - BOOL fTransAllowed = !bSkinned || PluginConfig.m_WinVerMajor >= 6; - - FlashContainer(pContainer, 0, 0); - pContainer->dwFlashingStarted = 0; - pLastActiveContainer = pContainer; - if (pContainer->dwFlags & CNT_DEFERREDTABSELECT) { - pContainer->dwFlags &= ~CNT_DEFERREDTABSELECT; - SendMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0); - - NMHDR nmhdr = { hwndTab, IDC_MSGTABS, TCN_SELCHANGE }; - SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nmhdr); // do it via a WM_NOTIFY / TCN_SELCHANGE to simulate user-activation - } - if (pContainer->dwFlags & CNT_DEFERREDSIZEREQUEST) { - pContainer->dwFlags &= ~CNT_DEFERREDSIZEREQUEST; - SendMessage(hwndDlg, WM_SIZE, 0, 0); - } - - if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) { - DWORD trans = LOWORD(pContainer->settings->dwTransparency); - SetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)trans, (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); - } - if (pContainer->dwFlags & CNT_NEED_UPDATETITLE) { - MCONTACT hContact = 0; - pContainer->dwFlags &= ~CNT_NEED_UPDATETITLE; - if (pContainer->hwndActive) { - SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - if (hContact) - SendMessage(hwndDlg, DM_UPDATETITLE, hContact, 0); - } - } - ZeroMemory(&item, sizeof(item)); - item.mask = TCIF_PARAM; - if ((curItem = TabCtrl_GetCurSel(hwndTab)) >= 0) - TabCtrl_GetItem(hwndTab, curItem, &item); - if (pContainer->dwFlags & CNT_DEFERREDCONFIGURE && curItem >= 0) { - pContainer->dwFlags &= ~CNT_DEFERREDCONFIGURE; - pContainer->hwndActive = (HWND)item.lParam; - SendMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0); - if (pContainer->hwndActive != 0 && IsWindow(pContainer->hwndActive)) { - ShowWindow(pContainer->hwndActive, SW_SHOW); - SetFocus(pContainer->hwndActive); - SendMessage(pContainer->hwndActive, WM_ACTIVATE, WA_ACTIVE, 0); - RedrawWindow(pContainer->hwndActive, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); - } - } - else if (curItem >= 0) - SendMessage((HWND)item.lParam, WM_ACTIVATE, WA_ACTIVE, 0); - } - break; - - case DM_CLOSETABATMOUSE: - { - HWND hwndCurrent = pContainer->hwndActive; - POINT *pt = (POINT *)lParam; - if ((iItem = GetTabItemFromMouse(hwndTab, pt)) == -1) - break; - - TCITEM item = {0}; - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, iItem, &item); - if (item.lParam) { - if ((HWND)item.lParam != hwndCurrent) { - pContainer->bDontSmartClose = TRUE; - SendMessage((HWND)item.lParam, WM_CLOSE, 0, 1); - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); - pContainer->bDontSmartClose = FALSE; - } - else SendMessage((HWND)item.lParam, WM_CLOSE, 0, 1); - } - } - break; - - case WM_PAINT: - if (bSkinned || M.isAero()) { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwndDlg, &ps); - EndPaint(hwndDlg, &ps); - return 0; - } - break; - - case WM_ERASEBKGND: - // avoid flickering of the menu bar when aero is active - if (pContainer) { - HDC hdc = (HDC)wParam; - RECT rc; - GetClientRect(hwndDlg, &rc); - - if (M.isAero()) { - HDC hdcMem; - HANDLE hbp = CMimAPI::m_pfnBeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, 0, &hdcMem); - FillRect(hdcMem, &rc, CSkin::m_BrushBack); - CSkin::FinalizeBufferedPaint(hbp, &rc); - } - else { - if (CSkin::m_skinEnabled) - CSkin::DrawItem(hdc, &rc, &SkinItems[ID_EXTBKCONTAINER]); - else { - CSkin::FillBack(hdc, &rc); - if (pContainer->SideBar->isActive() && pContainer->SideBar->isVisible()) { - - HPEN hPen = ::CreatePen(PS_SOLID, 1, PluginConfig.m_cRichBorders ? PluginConfig.m_cRichBorders : ::GetSysColor(COLOR_3DSHADOW)); - HPEN hOldPen = reinterpret_cast(::SelectObject(hdc, hPen)); - LONG x = (pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT ? pContainer->SideBar->getWidth() - 2 + pContainer->tBorder_outer_left : - rc.right - pContainer->SideBar->getWidth() + 1 - pContainer->tBorder_outer_right); - ::MoveToEx(hdc, x, rc.top, 0); - ::LineTo(hdc, x, rc.bottom); - ::SelectObject(hdc, hOldPen); - ::DeleteObject(hPen); - } - } - } - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); - return TRUE; - } - break; - - case DM_OPTIONSAPPLIED: - { - char szCname[40]; - TCHAR szTitleFormat[200]; - TCHAR* szThemeName = NULL; - DBVARIANT dbv = {0}; - - szTitleFormat[0] = 0; - - if (pContainer->isCloned && pContainer->hContactFrom != 0) { - pContainer->settings = &PluginConfig.globalContainerSettings; - - pContainer->szRelThemeFile[0] = pContainer->szAbsThemeFile[0] = 0; - mir_snprintf(szCname, 40, "%s_theme", CONTAINER_PREFIX); - if (!db_get_ts(pContainer->hContactFrom, SRMSGMOD_T, szCname, &dbv)) - szThemeName = dbv.ptszVal; - } - else { - Utils::ReadPrivateContainerSettings(pContainer); - if (szThemeName == NULL) { - mir_snprintf(szCname, 40, "%s%d_theme", CONTAINER_PREFIX, pContainer->iContainerIndex); - if (!db_get_ts(NULL, SRMSGMOD_T, szCname, &dbv)) - szThemeName = dbv.ptszVal; - } - } - Utils::SettingsToContainer(pContainer); - - if (szThemeName != NULL) { - PathToAbsoluteT(szThemeName, pContainer->szAbsThemeFile, M.getDataPath()); - mir_sntprintf(pContainer->szRelThemeFile, MAX_PATH, _T("%s"), szThemeName); - db_free(&dbv); - } - else pContainer->szAbsThemeFile[0] = pContainer->szRelThemeFile[0] = 0; - - pContainer->ltr_templates = pContainer->rtl_templates = 0; - } - break; - - case DM_STATUSBARCHANGED: - SendMessage(hwndDlg, WM_SIZE, 0, 0); - { - RECT rc; - GetWindowRect(hwndDlg, &rc); - SetWindowPos(hwndDlg, 0, rc.left, rc.top, rc.right - rc.left, (rc.bottom - rc.top) + 1, SWP_NOZORDER | SWP_NOACTIVATE); - SetWindowPos(hwndDlg, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE); - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); - } - if (pContainer->hwndStatus != 0 && pContainer->hwndActive != 0) - PostMessage(pContainer->hwndActive, DM_STATUSBARCHANGED, 0, 0); - return 0; - - case DM_CONFIGURECONTAINER: - { - DWORD ws, wsold, ex = 0, exold = 0; - HMENU hSysmenu = GetSystemMenu(hwndDlg, FALSE); - MCONTACT hContact = 0; - int i=0; - UINT sBarHeight; - bool bAero = M.isAero(); - - ws = wsold = GetWindowLongPtr(hwndDlg, GWL_STYLE); - if (!CSkin::m_frameSkins) { - ws = (pContainer->dwFlags & CNT_NOTITLE) ? - ((IsWindowVisible(hwndDlg) ? WS_VISIBLE : 0) | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN | WS_THICKFRAME | (CSkin::m_frameSkins ? WS_SYSMENU : WS_SYSMENU | WS_SIZEBOX)) : - ws | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; - } - - SetWindowLongPtr(hwndDlg, GWL_STYLE, ws); - - pContainer->tBorder = M.GetByte((bSkinned ? "S_tborder" : "tborder"), 2); - pContainer->tBorder_outer_left = g_ButtonSet.left + M.GetByte((bSkinned ? "S_tborder_outer_left" : "tborder_outer_left"), 2); - pContainer->tBorder_outer_right = g_ButtonSet.right + M.GetByte((bSkinned ? "S_tborder_outer_right" : "tborder_outer_right"), 2); - pContainer->tBorder_outer_top = g_ButtonSet.top + M.GetByte((bSkinned ? "S_tborder_outer_top" : "tborder_outer_top"), 2); - pContainer->tBorder_outer_bottom = g_ButtonSet.bottom + M.GetByte((bSkinned ? "S_tborder_outer_bottom" : "tborder_outer_bottom"), 2); - sBarHeight = (UINT)M.GetByte((bSkinned ? "S_sbarheight" : "sbarheight"), 0); - - if (LOBYTE(LOWORD(GetVersion())) >= 5) { - BOOL fTransAllowed = !bSkinned || PluginConfig.m_WinVerMajor >= 6; - - ex = exold = GetWindowLongPtr(hwndDlg, GWL_EXSTYLE); - ex = (pContainer->dwFlags & CNT_TRANSPARENCY && (!CSkin::m_skinEnabled || fTransAllowed)) ? ex | WS_EX_LAYERED : ex & ~(WS_EX_LAYERED); - - SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, ex); - if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) { - DWORD trans = LOWORD(pContainer->settings->dwTransparency); - SetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)trans, (/* pContainer->bSkinned ? LWA_COLORKEY : */ 0) | (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); - } - } - - if (!CSkin::m_frameSkins) - CheckMenuItem(hSysmenu, IDM_NOTITLE, (pContainer->dwFlags & CNT_NOTITLE) ? MF_BYCOMMAND | MF_CHECKED : MF_BYCOMMAND | MF_UNCHECKED); - - CheckMenuItem(hSysmenu, IDM_STAYONTOP, pContainer->dwFlags & CNT_STICKY ? MF_BYCOMMAND | MF_CHECKED : MF_BYCOMMAND | MF_UNCHECKED); - SetWindowPos(hwndDlg, (pContainer->dwFlags & CNT_STICKY) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); - if (ws != wsold) { - RECT rc; - GetWindowRect(hwndDlg, &rc); - if ((ws & WS_CAPTION) != (wsold & WS_CAPTION)) { - SetWindowPos(hwndDlg, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOCOPYBITS); - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); - if (pContainer->hwndActive != 0) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - DM_ScrollToBottom(dat, 0, 0); - } - } - } - - pContainer->dwFlags = ((pContainer->dwFlagsEx & (TCF_SBARLEFT | TCF_SBARRIGHT)) ? - pContainer->dwFlags | CNT_SIDEBAR : pContainer->dwFlags & ~CNT_SIDEBAR); - - pContainer->SideBar->Init(); - - ws = wsold = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_STYLE); - if (pContainer->dwFlags & CNT_TABSBOTTOM) - ws |= (TCS_BOTTOM); - else - ws &= ~(TCS_BOTTOM); - if ((ws & (TCS_BOTTOM | TCS_MULTILINE)) != (wsold & (TCS_BOTTOM | TCS_MULTILINE))) { - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_STYLE, ws); - RedrawWindow(GetDlgItem(hwndDlg, IDC_MSGTABS), NULL, NULL, RDW_INVALIDATE); - } - - if (pContainer->dwFlags & CNT_NOSTATUSBAR) { - if (pContainer->hwndStatus) { - DestroyWindow(pContainer->hwndStatus); - pContainer->hwndStatus = 0; - pContainer->statusBarHeight = 0; - SendMessage(hwndDlg, DM_STATUSBARCHANGED, 0, 0); - } - } - else if (pContainer->hwndStatus == 0) { - pContainer->hwndStatus = CreateWindowEx(0, _T("TSStatusBarClass"), NULL, SBT_TOOLTIPS | WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwndDlg, NULL, g_hInst, NULL); - - if (sBarHeight && bSkinned) - SendMessage(pContainer->hwndStatus, SB_SETMINHEIGHT, sBarHeight, 0); - } - if (pContainer->hwndActive != 0) { - hContact = 0; - SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - if (hContact) - SendMessage(hwndDlg, DM_UPDATETITLE, hContact, 0); - } - SendMessage(hwndDlg, WM_SIZE, 0, 1); - BroadCastContainer(pContainer, DM_CONFIGURETOOLBAR, 0, 1); - } - return 0; - - // search the first and most recent unread events in all client tabs... - // return all information via a RECENTINFO structure (tab indices, - // window handles and timestamps). - case DM_QUERYRECENT: - { - int iItems = TabCtrl_GetItemCount(hwndTab); - TCITEM item = {0}; - - DWORD dwTimestamp, dwMostRecent = 0; - - RECENTINFO *ri = (RECENTINFO *)lParam; - ri->iFirstIndex = ri->iMostRecent = -1; - ri->dwFirst = ri->dwMostRecent = 0; - ri->hwndFirst = ri->hwndMostRecent = 0; - - for (int i=0; i < iItems; i++) { - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, i, &item); - SendMessage((HWND)item.lParam, DM_QUERYLASTUNREAD, 0, (LPARAM)&dwTimestamp); - if (dwTimestamp > ri->dwMostRecent) { - ri->dwMostRecent = dwTimestamp; - ri->iMostRecent = i; - ri->hwndMostRecent = (HWND)item.lParam; - if (ri->iFirstIndex == -1) { - ri->iFirstIndex = i; - ri->dwFirst = dwTimestamp; - ri->hwndFirst = (HWND)item.lParam; - } - } - } - } - return 0; - - // search tab with either next or most recent unread message and select it - case DM_QUERYPENDING: - { - RECENTINFO ri; - SendMessage(hwndDlg, DM_QUERYRECENT, 0, (LPARAM)&ri); - - NMHDR nmhdr; - nmhdr.code = TCN_SELCHANGE; - - if (wParam == DM_QUERY_NEXT && ri.iFirstIndex != -1) { - TabCtrl_SetCurSel(hwndTab, ri.iFirstIndex); - SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nmhdr); - } - if (wParam == DM_QUERY_MOSTRECENT && ri.iMostRecent != -1) { - TabCtrl_SetCurSel(hwndTab, ri.iMostRecent); - SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nmhdr); - } - } - return 0; - - case DM_SETICON: - { - TWindowData *dat = (TWindowData*)wParam; - HICON hIconMsg = PluginConfig.g_IconMsgEvent; - HICON hIconBig = (dat && dat->cache) ? LoadSkinnedProtoIconBig(dat->cache->getProto(), dat->cache->getStatus()) : 0; - - if (Win7Taskbar->haveLargeIcons()) { - if ((HICON)lParam == PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING] || (HICON)lParam == hIconMsg) { - Win7Taskbar->setOverlayIcon(hwndDlg, lParam); - if (GetForegroundWindow() != hwndDlg) - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); - if ((HICON)lParam == hIconMsg) - pContainer->hIconTaskbarOverlay = hIconMsg; - break; - } - - if (dat) { - if (dat->hTaskbarIcon == 0) - dat->hTaskbarIcon = ((dat->pContainer->dwFlags & CNT_AVATARSONTASKBAR) ? Utils::iconFromAvatar(dat) : 0); - else { - if (!(dat->pContainer->dwFlags & CNT_AVATARSONTASKBAR)) { - DestroyIcon(dat->hTaskbarIcon); - dat->hTaskbarIcon = 0; - } - } - - if (dat->hTaskbarIcon) { - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)dat->hTaskbarIcon); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); - Win7Taskbar->setOverlayIcon(hwndDlg, (LPARAM)(dat->hTabIcon ? (LPARAM)dat->hTabIcon : lParam)); - } - else { - if (0 == hIconBig || (HICON)CALLSERVICE_NOTFOUND == hIconBig) - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)lParam); - else - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIconBig); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); - if (dat->pContainer->hIconTaskbarOverlay) - Win7Taskbar->setOverlayIcon(hwndDlg, (LPARAM)dat->pContainer->hIconTaskbarOverlay); - else if (Win7Taskbar->haveAlwaysGroupingMode() && fForceOverlayIcons) - Win7Taskbar->setOverlayIcon(hwndDlg, lParam); - else - Win7Taskbar->clearOverlayIcon(hwndDlg); - } - return 0; - } - } - - // default handling (no win7 taskbar) - if ((HICON)lParam == PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]) { // always set typing icon, but don't save it... - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PluginConfig.g_IconTypingEventBig); - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); - break; - } - if (reinterpret_cast(lParam) == hIconMsg) - hIconBig = LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE); - - if (pContainer->hIcon == STICK_ICON_MSG && (HICON)lParam != hIconMsg && pContainer->dwFlags & CNT_NEED_UPDATETITLE) { - lParam = (LPARAM)hIconMsg; - hIconBig = LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE); - } - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); - if (0 != hIconBig && reinterpret_cast(CALLSERVICE_NOTFOUND) != hIconBig) - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, LPARAM(hIconBig)); - pContainer->hIcon = (lParam == (LPARAM)hIconMsg) ? STICK_ICON_MSG : 0; - } - return 0; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - int cx = PluginConfig.m_smcxicon; - int cy = PluginConfig.m_smcyicon; - int id = LOWORD(dis->itemID); - - if (dis->hwndItem == pContainer->hwndStatus && !(pContainer->dwFlags & CNT_NOSTATUSBAR)) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - if (dat) - DrawStatusIcons(dat, dis->hDC, dis->rcItem, 2); - return TRUE; - } - } - return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam); - - case WM_MEASUREITEM: - return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); - - case DM_QUERYCLIENTAREA: - { - RECT *rc = (RECT *)lParam; - if (rc) { - if (!IsIconic(hwndDlg)) - GetClientRect(hwndDlg, rc); - else - CopyRect(rc, &pContainer->rcSaved); - AdjustTabClientRect(pContainer, rc); - } - } - return 0; - - case WM_DESTROY: - pContainer->hwnd = 0; - pContainer->hwndActive = 0; - pContainer->hMenuContext = 0; - if (pContainer->hwndStatus) - DestroyWindow(pContainer->hwndStatus); - - // mir_free private theme... - if (pContainer->theme.isPrivate) { - mir_free(pContainer->ltr_templates); - mir_free(pContainer->rtl_templates); - mir_free(pContainer->theme.logFonts); - mir_free(pContainer->theme.fontColors); - mir_free(pContainer->theme.rtfFonts); - } - - if (pContainer->hwndTip) - DestroyWindow(pContainer->hwndTip); - RemoveContainerFromList(pContainer); - SM_RemoveContainer(pContainer); - if (pContainer->cachedDC) { - SelectObject(pContainer->cachedDC, pContainer->oldHBM); - DeleteObject(pContainer->cachedHBM); - DeleteDC(pContainer->cachedDC); - } - if (pContainer->cachedToolbarDC) { - SelectObject(pContainer->cachedToolbarDC, pContainer->oldhbmToolbarBG); - DeleteObject(pContainer->hbmToolbarBG); - DeleteDC(pContainer->cachedToolbarDC); - } - return 0; - - case WM_NCDESTROY: - if (pContainer) { - delete pContainer->MenuBar; - delete pContainer->SideBar; - if (pContainer->settings != &PluginConfig.globalContainerSettings) - mir_free(pContainer->settings); - mir_free(pContainer); - } - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - - case WM_CLOSE: - if (PluginConfig.m_HideOnClose && !lParam) { - ShowWindow(hwndDlg, SW_HIDE); - pContainer->fHidden = true; - } - else { - if (TabCtrl_GetItemCount(hwndTab) > 1) { - LRESULT res = CWarning::show(CWarning::WARN_CLOSEWINDOW, MB_YESNOCANCEL | MB_ICONQUESTION); - if (IDNO == res || IDCANCEL == res) - break; - } - - // dont ask if container is empty (no tabs) - if (lParam == 0 && TabCtrl_GetItemCount(GetDlgItem(hwndDlg, IDC_MSGTABS)) > 0) { - int clients = TabCtrl_GetItemCount(hwndTab), iOpenJobs = 0; - - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - for (int i = 0; i < clients; i++) { - TabCtrl_GetItem(hwndTab, i, &item); - if (item.lParam && IsWindow((HWND)item.lParam)) - SendMessage((HWND)item.lParam, DM_CHECKQUEUEFORCLOSE, 0, (LPARAM)&iOpenJobs); - } - if (iOpenJobs && pContainer) { - if (pContainer->exFlags & CNT_EX_CLOSEWARN) - return TRUE; - - pContainer->exFlags |= CNT_EX_CLOSEWARN; - LRESULT result = SendQueue::WarnPendingJobs(iOpenJobs); - pContainer->exFlags &= ~CNT_EX_CLOSEWARN; - if (result == IDNO) - return TRUE; - } - } - - // save geometry information to the database... - if (!(pContainer->dwFlags & CNT_GLOBALSIZE)) { - WINDOWPLACEMENT wp = { 0 }; - wp.length = sizeof(wp); - if (GetWindowPlacement(hwndDlg, &wp) != 0) { - if (pContainer->isCloned && pContainer->hContactFrom != 0) { - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &item); - - MCONTACT hContact; - SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - db_set_b(hContact, SRMSGMOD_T, "splitmax", (BYTE)((wp.showCmd == SW_SHOWMAXIMIZED) ? 1 : 0)); - - for (int i = 0; i < TabCtrl_GetItemCount(hwndTab); i++) { - if (TabCtrl_GetItem(hwndTab, i, &item)) { - SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - db_set_dw(hContact, SRMSGMOD_T, "splitx", wp.rcNormalPosition.left); - db_set_dw(hContact, SRMSGMOD_T, "splity", wp.rcNormalPosition.top); - db_set_dw(hContact, SRMSGMOD_T, "splitwidth", wp.rcNormalPosition.right - wp.rcNormalPosition.left); - db_set_dw(hContact, SRMSGMOD_T, "splitheight", wp.rcNormalPosition.bottom - wp.rcNormalPosition.top); - } - } - } - else { - char szCName[40]; - mir_snprintf(szCName, 40, "%s%dx", CONTAINER_PREFIX, pContainer->iContainerIndex); - db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.left); - mir_snprintf(szCName, 40, "%s%dy", CONTAINER_PREFIX, pContainer->iContainerIndex); - db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.top); - mir_snprintf(szCName, 40, "%s%dwidth", CONTAINER_PREFIX, pContainer->iContainerIndex); - db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.right - wp.rcNormalPosition.left); - mir_snprintf(szCName, 40, "%s%dheight", CONTAINER_PREFIX, pContainer->iContainerIndex); - db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top); - - db_set_b(0, SRMSGMOD_T, "splitmax", (BYTE)((wp.showCmd == SW_SHOWMAXIMIZED) ? 1 : 0)); - } - } - } - - // clear temp flags which should NEVER be saved... - if (pContainer->isCloned && pContainer->hContactFrom != 0) { - TCITEM item = {0}; - item.mask = TCIF_PARAM; - pContainer->dwFlags &= ~(CNT_DEFERREDCONFIGURE | CNT_CREATE_MINIMIZED | CNT_DEFERREDSIZEREQUEST | CNT_CREATE_CLONED); - for (int i=0; i < TabCtrl_GetItemCount(hwndTab); i++) { - if (TabCtrl_GetItem(hwndTab, i, &item)) { - MCONTACT hContact; - SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); - - char szCName[40]; - mir_snprintf(szCName, 40, "%s_theme", CONTAINER_PREFIX); - if (lstrlen(pContainer->szRelThemeFile) > 1) { - if (pContainer->fPrivateThemeChanged == TRUE) { - PathToRelativeT(pContainer->szRelThemeFile, pContainer->szAbsThemeFile, M.getDataPath()); - db_set_ts(hContact, SRMSGMOD_T, szCName, pContainer->szRelThemeFile); - pContainer->fPrivateThemeChanged = FALSE; - } - } - else { - db_unset(hContact, SRMSGMOD_T, szCName); - pContainer->fPrivateThemeChanged = FALSE; - } - } - } - } - else Utils::SaveContainerSettings(pContainer, CONTAINER_PREFIX); - DestroyWindow(hwndDlg); - } - } - return FALSE; -} - -// search the list of tabs and return the tab (by index) which "belongs" to the given -// hwnd. The hwnd is the handle of a message dialog childwindow. At creation, -// the dialog handle is stored in the TCITEM.lParam field, because we need -// to know the owner of the tab. -// -// hwndTab: handle of the tab control itself. -// hwnd: handle of a message dialog. -// -// returns the tab index (zero based), -1 if no tab is found (which SHOULD not -// really happen, but who knows... ;)) - -int TSAPI GetTabIndexFromHWND(HWND hwndTab, HWND hwnd) -{ - int iItems = TabCtrl_GetItemCount(hwndTab); - - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - for (int i=0; i < iItems; i++) { - TabCtrl_GetItem(hwndTab, i, &item); - if ((HWND)item.lParam == hwnd) - return i; - } - return -1; -} - -// search the list of tabs and return the tab (by index) which "belongs" to the given -// hwnd. The hwnd is the handle of a message dialog childwindow. At creation, -// the dialog handle is stored in the TCITEM.lParam field, because we need -// to know the owner of the tab. -// -// hwndTab: handle of the tab control itself. -// hwnd: handle of a message dialog. -// -// returns the tab index (zero based), -1 if no tab is found (which SHOULD not -// really happen, but who knows... ;)) - -HWND TSAPI GetHWNDFromTabIndex(HWND hwndTab, int idx) -{ - int iItems = TabCtrl_GetItemCount(hwndTab); - - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - for (int i=0; i < iItems; i++) { - TabCtrl_GetItem(hwndTab, i, &item); - if (i == idx) - return (HWND)item.lParam; - } - return NULL; -} - -// activates the tab belonging to the given client HWND (handle of the actual -// message window. - -int TSAPI ActivateTabFromHWND(HWND hwndTab, HWND hwnd) -{ - int iItem = GetTabIndexFromHWND(hwndTab, hwnd); - if (iItem >= 0) { - TabCtrl_SetCurSel(hwndTab, iItem); - - NMHDR nmhdr = { 0 }; - nmhdr.code = TCN_SELCHANGE; - SendMessage(GetParent(hwndTab), WM_NOTIFY, 0, (LPARAM)&nmhdr); // do it via a WM_NOTIFY / TCN_SELCHANGE to simulate user-activation - return iItem; - } - return -1; -} - -// returns the index of the tab under the mouse pointer. Used for -// context menu popup and tooltips -// pt: mouse coordinates, obtained from GetCursorPos() - -int TSAPI GetTabItemFromMouse(HWND hwndTab, POINT *pt) -{ - ScreenToClient(hwndTab, pt); - - TCHITTESTINFO tch; - tch.pt = *pt; - tch.flags = 0; - return TabCtrl_HitTest(hwndTab, &tch); -} - -// enumerates tabs and closes all of them, but the one in dat -void TSAPI CloseOtherTabs(HWND hwndTab, TWindowData &dat) -{ - for (int idxt = 0; idxt < dat.pContainer->iChilds; ) { - HWND otherTab = GetHWNDFromTabIndex(hwndTab, idxt); - if (otherTab != NULL && otherTab != dat.hwnd) - SendMessage(otherTab, WM_CLOSE, 1, 0); - else - ++idxt; - } -} - -// cut off contact name to the option value set via Options->Tabbed messaging -// some people were requesting this, because really long contact list names -// are causing extraordinary wide tabs and these are looking ugly and wasting -// screen space. -// -// size = max length of target string - -int TSAPI CutContactName(const TCHAR *oldname, TCHAR *newname, unsigned int size) -{ - int cutMax = PluginConfig.m_CutContactNameTo; - - if ((int)lstrlen(oldname) <= cutMax) { - lstrcpyn(newname, oldname, size); - newname[size - 1] = 0; - } - else { - TCHAR fmt[20]; - mir_sntprintf(fmt, 18, _T("%%%d.%ds..."), cutMax, cutMax); - mir_sntprintf(newname, size, fmt, oldname); - newname[size - 1] = 0; - } - return 0; -} - -// functions for handling the linked list of struct ContainerWindowData *foo - -static TContainerData* TSAPI AppendToContainerList(TContainerData *pContainer) -{ - if (!pFirstContainer) { - pFirstContainer = pContainer; - pFirstContainer->pNext = NULL; - return pFirstContainer; - } - - TContainerData *p = pFirstContainer; - while (p->pNext != 0) - p = p->pNext; - p->pNext = pContainer; - pContainer->pNext = NULL; - return p; -} - -TContainerData* TSAPI FindContainerByName(const TCHAR *name) -{ - if (name == NULL || lstrlen(name) == 0) - return 0; - - if (M.GetByte("singlewinmode", 0)) // single window mode - always return 0 and force a new container - return NULL; - - for (TContainerData *p = pFirstContainer; p; p = p->pNext) - if (!_tcsncmp(p->szName, name, CONTAINER_NAMELEN)) - return p; - - // error, didn't find it. - return NULL; -} - -static TContainerData* TSAPI RemoveContainerFromList(TContainerData *pContainer) -{ - if (pContainer == pFirstContainer) { - if (pContainer->pNext != NULL) - pFirstContainer = pContainer->pNext; - else - pFirstContainer = NULL; - - if (pLastActiveContainer == pContainer) // make sure, we don't reference this container anymore - pLastActiveContainer = pFirstContainer; - - return pFirstContainer; - } - - for (TContainerData *p = pFirstContainer; p; p = p->pNext) { - if (p->pNext == pContainer) { - p->pNext = p->pNext->pNext; - - if (pLastActiveContainer == pContainer) // make sure, we don't reference this container anymore - pLastActiveContainer = pFirstContainer; - - return 0; - } - } - return NULL; -} - -// calls the TabCtrl_AdjustRect to calculate the "real" client area of the tab. -// also checks for the option "hide tabs when only one tab open" and adjusts -// geometry if necessary -// rc is the RECT obtained by GetClientRect(hwndTab) - -void TSAPI AdjustTabClientRect(TContainerData *pContainer, RECT *rc) -{ - HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS); - DWORD tBorder = pContainer->tBorder; - DWORD dwStyle = GetWindowLongPtr(hwndTab, GWL_STYLE); - - RECT rcTab, rcTabOrig; - GetClientRect(hwndTab, &rcTab); - DWORD dwBottom = rcTab.bottom; - DWORD dwTop = rcTab.top; - if (!(pContainer->dwFlags & CNT_SIDEBAR) && (pContainer->iChilds > 1 || !(pContainer->dwFlags & CNT_HIDETABS))) { - rcTabOrig = rcTab; - TabCtrl_AdjustRect(hwndTab, FALSE, &rcTab); - DWORD dwTopPad = rcTab.top - rcTabOrig.top; - - rc->left += tBorder; - rc->right -= tBorder; - - if (dwStyle & TCS_BUTTONS) { - if (pContainer->dwFlags & CNT_TABSBOTTOM) { - int nCount = TabCtrl_GetItemCount(hwndTab); - if (nCount > 0) { - RECT rcItem; - TabCtrl_GetItemRect(hwndTab, nCount - 1, &rcItem); - rc->bottom = rcItem.top; - } - } - else { - rc->top += (dwTopPad - 2); - rc->bottom = rcTabOrig.bottom; - } - } - else { - if (pContainer->dwFlags & CNT_TABSBOTTOM) - rc->bottom = rcTab.bottom + 2; - else { - rc->top += (dwTopPad - 2); - rc->bottom = rcTabOrig.bottom; - } - } - - rc->top += tBorder; - rc->bottom -= tBorder; - } - else { - rc->bottom = rcTab.bottom; - rc->top = rcTab.top; - } - rc->right -= (pContainer->tBorder_outer_left + pContainer->tBorder_outer_right); - if (pContainer->SideBar->isVisible()) - rc->right -= pContainer->SideBar->getWidth(); -} - -// retrieve the container name for the given contact handle. -// if none is assigned, return the name of the default container - -int TSAPI GetContainerNameForContact(MCONTACT hContact, TCHAR *szName, int iNameLen) -{ - // single window mode using cloned (temporary) containers - if (M.GetByte("singlewinmode", 0)) { - _tcsncpy_s(szName, iNameLen, _T("Message Session"), _TRUNCATE); - return 0; - } - - // use clist group names for containers... - if (M.GetByte("useclistgroups", 0)) { - ptrT tszGroup(db_get_tsa(hContact, "CList", "Group")); - if (tszGroup == NULL) { - _tcsncpy_s(szName, iNameLen, _T("default"), _TRUNCATE); - return 0; - } - - _tcsncpy_s(szName, iNameLen, tszGroup, _TRUNCATE); - return 1; - } - - ptrT tszContainerName(db_get_tsa(hContact, SRMSGMOD_T, CONTAINER_SUBKEY)); - if (tszContainerName == NULL) { - _tcsncpy_s(szName, iNameLen, _T("default"), _TRUNCATE); - return 0; - } - - _tcsncpy_s(szName, iNameLen, tszContainerName, _TRUNCATE); - return 1; -} - -void TSAPI DeleteContainer(int iIndex) -{ - char szIndex[10]; - itoa(iIndex, szIndex, 10); - ptrT tszContainerName(db_get_tsa(NULL, CONTAINER_KEY, szIndex)); - if (tszContainerName == NULL) - return; - - db_set_ts(NULL, CONTAINER_KEY, szIndex, _T("**mir_free**")); - - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - ptrT tszValue(db_get_tsa(hContact, SRMSGMOD_T, CONTAINER_SUBKEY)); - if (!lstrcmp(tszValue, tszContainerName)) - db_unset(hContact, SRMSGMOD_T, CONTAINER_SUBKEY); - } - - char szSetting[CONTAINER_NAMELEN + 30]; - mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%d_Flags", CONTAINER_PREFIX, iIndex); - db_unset(NULL, SRMSGMOD_T, szSetting); - mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%d_Trans", CONTAINER_PREFIX, iIndex); - db_unset(NULL, SRMSGMOD_T, szSetting); - mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dwidth", CONTAINER_PREFIX, iIndex); - db_unset(NULL, SRMSGMOD_T, szSetting); - mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dheight", CONTAINER_PREFIX, iIndex); - db_unset(NULL, SRMSGMOD_T, szSetting); - mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dx", CONTAINER_PREFIX, iIndex); - db_unset(NULL, SRMSGMOD_T, szSetting); - mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dy", CONTAINER_PREFIX, iIndex); - db_unset(NULL, SRMSGMOD_T, szSetting); -} - -void TSAPI RenameContainer(int iIndex, const TCHAR *szNew) -{ - if (lstrlen(szNew) == 0) - return; - - char szIndex[10]; - itoa(iIndex, szIndex, 10); - ptrT tszContainerName(db_get_tsa(NULL, CONTAINER_KEY, szIndex)); - if (tszContainerName == NULL) - return; - - db_set_ts(NULL, CONTAINER_KEY, szIndex, szNew); - - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - ptrT tszValue(db_get_tsa(hContact, SRMSGMOD_T, CONTAINER_SUBKEY)); - if (!lstrcmp(tszValue, tszContainerName)) - db_set_ts(hContact, SRMSGMOD_T, CONTAINER_SUBKEY, szNew); - } -} - -HMENU TSAPI BuildContainerMenu() -{ - if (PluginConfig.g_hMenuContainer != 0) { - HMENU submenu = GetSubMenu(PluginConfig.g_hMenuContext, 0); - RemoveMenu(submenu, 6, MF_BYPOSITION); - DestroyMenu(PluginConfig.g_hMenuContainer); - PluginConfig.g_hMenuContainer = 0; - } - - // no container attach menu, if we are using the "clist group mode" - if (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) - return NULL; - - HMENU hMenu = CreateMenu(); - int i = 0; - while (true) { - char szCounter[10]; - itoa(i, szCounter, 10); - ptrT tszName(db_get_tsa(NULL, CONTAINER_KEY, szCounter)); - if (tszName == NULL) - break; - - if (_tcsncmp(tszName, _T("**mir_free**"), CONTAINER_NAMELEN)) - AppendMenu(hMenu, MF_STRING, IDM_CONTAINERMENU + i, !_tcscmp(tszName, _T("default")) ? TranslateT("Default container") : tszName); - i++; - } - - InsertMenu(PluginConfig.g_hMenuContext, ID_TABMENU_ATTACHTOCONTAINER, MF_BYCOMMAND | MF_POPUP, (UINT_PTR)hMenu, TranslateT("Attach to")); - PluginConfig.g_hMenuContainer = hMenu; - return hMenu; -} - -// flashes the container -// iMode != 0: turn on flashing -// iMode == 0: turn off flashing - -void TSAPI FlashContainer(TContainerData *pContainer, int iMode, int iCount) -{ - if (pContainer->dwFlags & CNT_NOFLASH) // container should never flash - return; - - FLASHWINFO fwi; - fwi.cbSize = sizeof(fwi); - fwi.uCount = 0; - - if (iMode) { - fwi.dwFlags = FLASHW_ALL; - if (pContainer->dwFlags & CNT_FLASHALWAYS) - fwi.dwFlags |= FLASHW_TIMER; - else - fwi.uCount = (iCount == 0) ? M.GetByte("nrflash", 4) : iCount; - fwi.dwTimeout = M.GetDword("flashinterval", 1000); - - } - else fwi.dwFlags = FLASHW_STOP; - - fwi.hwnd = pContainer->hwnd; - pContainer->dwFlashingStarted = GetTickCount(); - FlashWindowEx(&fwi); -} - -void TSAPI ReflashContainer(TContainerData *pContainer) -{ - DWORD dwStartTime = pContainer->dwFlashingStarted; - - if (GetForegroundWindow() == pContainer->hwnd || GetActiveWindow() == pContainer->hwnd) // dont care about active windows - return; - - if (pContainer->dwFlags & CNT_NOFLASH || pContainer->dwFlashingStarted == 0) - return; // dont care about containers which should never flash - - if (pContainer->dwFlags & CNT_FLASHALWAYS) - FlashContainer(pContainer, 1, 0); - else { - // recalc the remaining flashes - DWORD dwInterval = M.GetDword("flashinterval", 1000); - int iFlashesElapsed = (GetTickCount() - dwStartTime) / dwInterval; - DWORD dwFlashesDesired = M.GetByte("nrflash", 4); - if (iFlashesElapsed < (int)dwFlashesDesired) - FlashContainer(pContainer, 1, dwFlashesDesired - iFlashesElapsed); - else { - BOOL isFlashed = FlashWindow(pContainer->hwnd, TRUE); - if (!isFlashed) - FlashWindow(pContainer->hwnd, TRUE); - } - } - pContainer->dwFlashingStarted = dwStartTime; -} - -// broadcasts a message to all child windows (tabs/sessions) - -void TSAPI BroadCastContainer(const TContainerData *pContainer, UINT message, WPARAM wParam, LPARAM lParam, BYTE bType) -{ - HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS); - - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - - int nCount = TabCtrl_GetItemCount(hwndTab); - for (int i = 0; i < nCount; i++) { - TabCtrl_GetItem(hwndTab, i, &item); - if (IsWindow((HWND)item.lParam)) { - if (bType == SESSIONTYPE_ANY) - SendMessage((HWND)item.lParam, message, wParam, lParam); - else { - TWindowData *dat = (TWindowData*)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA); - if (dat && dat->bType == bType) - SendMessage((HWND)item.lParam, message, wParam, lParam); - } - } - } -} - -void TSAPI CloseAllContainers() -{ - BOOL fOldHideSetting = PluginConfig.m_HideOnClose; - - while (pFirstContainer != NULL) { - if (!IsWindow(pFirstContainer->hwnd)) - pFirstContainer = pFirstContainer->pNext; - else { - PluginConfig.m_HideOnClose = FALSE; - ::SendMessage(pFirstContainer->hwnd, WM_CLOSE, 0, 1); - } - } - - PluginConfig.m_HideOnClose = fOldHideSetting; -} +/* +// Miranda NG: the free IM client for Microsoft* Windows* +// +// 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 +// +// implements the "Container" window which acts as a toplevel window +// for message sessions. + */ + +#include "commonheaders.h" + +#define CONTAINER_KEY "TAB_ContainersW" +#define CONTAINER_SUBKEY "containerW" +#define CONTAINER_PREFIX "CNTW_" + +TContainerData *pFirstContainer = 0; // the linked list of struct ContainerWindowData +TContainerData *pLastActiveContainer = NULL; + +static bool fForceOverlayIcons = false; + +static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact) +{ + if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) { + if (hContact == 0) + return 0; + if (item->dwFlags & BUTTON_PASSHCONTACTW) + *wParam = hContact; + else if (item->dwFlags & BUTTON_PASSHCONTACTL) + *lParam = hContact; + return 1; + } + return 1; // doesn't need a paramter +} + +// Windows Vista+ +// extend the glassy area to get aero look for the status bar, tab bar, info panel +// and outer margins. + +void TSAPI SetAeroMargins(TContainerData *pContainer) +{ + if ( !M.isAero() || !pContainer || CSkin::m_skinEnabled) { + pContainer->MenuBar->setAero(false); + return; + } + + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + if (!dat) + return; + + RECT rcWnd; + if (dat->bType == SESSIONTYPE_IM) { + if (dat->Panel->isActive()) + GetWindowRect(GetDlgItem(dat->hwnd, IDC_LOG), &rcWnd); + else + GetWindowRect(dat->hwnd, &rcWnd); + } + else { + if (dat->Panel->isActive()) + GetWindowRect(GetDlgItem(dat->hwnd, IDC_CHAT_LOG), &rcWnd); + else + GetWindowRect(dat->hwnd, &rcWnd); + } + + POINT pt = { rcWnd.left, rcWnd.top }; + ScreenToClient(pContainer->hwnd, &pt); + + MARGINS m; + m.cyTopHeight = pt.y; + pContainer->MenuBar->setAero(true); + + // bottom part + GetWindowRect(dat->hwnd, &rcWnd); + pt.x = rcWnd.left; + + LONG sbar_left, sbar_right; + if (!pContainer->SideBar->isActive()) { + pt.y = rcWnd.bottom + ((pContainer->iChilds > 1 || !(pContainer->dwFlags & CNT_HIDETABS)) ? pContainer->tBorder : 0); + sbar_left = 0, sbar_right = 0; + } + else { + pt.y = rcWnd.bottom; + sbar_left = (pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT ? pContainer->SideBar->getWidth() : 0); + sbar_right = (pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_RIGHT ? pContainer->SideBar->getWidth() : 0); + } + ScreenToClient(pContainer->hwnd, &pt); + GetClientRect(pContainer->hwnd, &rcWnd); + m.cyBottomHeight = (rcWnd.bottom - pt.y); + + if (m.cyBottomHeight < 0 || m.cyBottomHeight >= rcWnd.bottom) + m.cyBottomHeight = 0; + + m.cxLeftWidth = pContainer->tBorder_outer_left; + m.cxRightWidth = pContainer->tBorder_outer_right; + m.cxLeftWidth += sbar_left; + m.cxRightWidth += sbar_right; + + if (memcmp(&m, &pContainer->mOld, sizeof(MARGINS)) != 0) { + pContainer->mOld = m; + CMimAPI::m_pfnDwmExtendFrameIntoClientArea(pContainer->hwnd, &m); + } +} + +// CreateContainer MUST allocate a ContainerWindowData and pass its address +// to CreateDialogParam() via the LPARAM. It also adds the struct to the linked list +// of containers. +// +// The WM_DESTROY handler of the container DlgProc is responsible for mir_free()'ing the +// pointer and for removing the struct from the linked list. + +TContainerData* TSAPI CreateContainer(const TCHAR *name, int iTemp, MCONTACT hContactFrom) +{ + if (CMimAPI::m_shutDown) + return NULL; + + TContainerData *pContainer = (TContainerData*)mir_calloc(sizeof(TContainerData)); + _tcsncpy(pContainer->szName, name, CONTAINER_NAMELEN + 1); + AppendToContainerList(pContainer); + + if (M.GetByte("limittabs", 0) && !_tcscmp(name, _T("default"))) + iTemp |= CNT_CREATEFLAG_CLONED; + + // save container name to the db + if (!M.GetByte("singlewinmode", 0)) { + int iFirstFree = -1, iFound = FALSE, i = 0; + do { + char szCounter[10]; + itoa(i, szCounter, 10); + ptrT tszName(db_get_tsa(NULL, CONTAINER_KEY, szCounter)); + if (tszName == NULL) { + if (iFirstFree != -1) { + pContainer->iContainerIndex = iFirstFree; + itoa(iFirstFree, szCounter, 10); + } + else pContainer->iContainerIndex = i; + + db_set_ts(NULL, CONTAINER_KEY, szCounter, name); + BuildContainerMenu(); + break; + } + + if (!_tcsncmp(tszName, name, CONTAINER_NAMELEN)) { + pContainer->iContainerIndex = i; + iFound = TRUE; + } + else if (!_tcsncmp(tszName, _T("**mir_free**"), CONTAINER_NAMELEN)) + iFirstFree = i; + } + while (++i && iFound == FALSE); + } + else { + iTemp |= CNT_CREATEFLAG_CLONED; + pContainer->iContainerIndex = 1; + } + + if (iTemp & CNT_CREATEFLAG_MINIMIZED) + pContainer->dwFlags = CNT_CREATE_MINIMIZED; + if (iTemp & CNT_CREATEFLAG_CLONED) { + pContainer->dwFlags |= CNT_CREATE_CLONED; + pContainer->hContactFrom = hContactFrom; + } + pContainer->hwnd = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGCONTAINER), NULL, DlgProcContainer, (LPARAM)pContainer); + return pContainer; +} + +static LRESULT CALLBACK ContainerWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TContainerData *pContainer = (TContainerData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + BOOL bSkinned = CSkin::m_skinEnabled ? TRUE : FALSE; + + switch (msg) { + case WM_NCPAINT: { + PAINTSTRUCT ps; + HDC hdcReal; + RECT rcClient; + LONG width, height; + HDC hdc; + CSkinItem *item = &SkinItems[0], *item_normal, *item_pressed, *item_hot; + HICON hIcon; + HFONT hOldFont = 0; + TEXTMETRIC tm; + + if (!pContainer || !bSkinned) + break; + + if (CSkin::m_frameSkins) { + RECT rcWindow, rcClient; + HDC dcFrame = GetDCEx(hwndDlg, 0, DCX_WINDOW|/*DCX_INTERSECTRGN|*/0x10000); // GetWindowDC(hwndDlg); + POINT pt, pt1; + LONG clip_top, clip_left; + HRGN rgn = 0; + CSkinItem *item; + TCHAR szWindowText[512]; + RECT rcText; + HDC dcMem = CreateCompatibleDC(pContainer->cachedDC ? pContainer->cachedDC : dcFrame); + HBITMAP hbmMem, hbmOld; + int i; + DRAWITEMSTRUCT dis = {0}; + + GetWindowRect(hwndDlg, &rcWindow); + GetClientRect(hwndDlg, &rcClient); + pt.y = 0; + pt.x = 0; + ClientToScreen(hwndDlg, &pt); + pt1.x = rcClient.right; + pt1.y = rcClient.bottom; + ClientToScreen(hwndDlg, &pt1); + clip_top = pt.y - rcWindow.top; + clip_left = pt.x - rcWindow.left; + + rcWindow.right = rcWindow.right - rcWindow.left; + rcWindow.bottom = rcWindow.bottom - rcWindow.top; + rcWindow.left = rcWindow.top = 0; + + hbmMem = CreateCompatibleBitmap(dcFrame, rcWindow.right, rcWindow.bottom); + hbmOld = (HBITMAP)SelectObject(dcMem, hbmMem); + + ExcludeClipRect(dcFrame, clip_left, clip_top, clip_left + (pt1.x - pt.x), clip_top + (pt1.y - pt.y)); + ExcludeClipRect(dcMem, clip_left, clip_top, clip_left + (pt1.x - pt.x), clip_top + (pt1.y - pt.y)); + item = pContainer->ncActive ? &SkinItems[ID_EXTBKFRAME] : &SkinItems[ID_EXTBKFRAMEINACTIVE]; + + CSkin::DrawItem(dcMem, &rcWindow, item); + + GetWindowText(hwndDlg, szWindowText, 512); + szWindowText[511] = 0; + hOldFont = (HFONT)SelectObject(dcMem, PluginConfig.hFontCaption); + GetTextMetrics(dcMem, &tm); + SetTextColor(dcMem, CInfoPanel::m_ipConfig.clrs[IPFONTCOUNT - 1]); + SetBkMode(dcMem, TRANSPARENT); + rcText.left =20 + CSkin::m_SkinnedFrame_left + CSkin::m_bClipBorder + CSkin::m_titleBarLeftOff;//26; + rcText.right = rcWindow.right - 3 * CSkin::m_titleBarButtonSize.cx - 11 - CSkin::m_titleBarRightOff; + rcText.top = CSkin::m_captionOffset + CSkin::m_bClipBorder; + rcText.bottom = rcText.top + tm.tmHeight; + rcText.left += CSkin::m_captionPadding; + DrawText(dcMem, szWindowText, -1, &rcText, DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX); + SelectObject(dcMem, hOldFont); + + // icon + hIcon = (HICON)SendMessage(hwndDlg, WM_GETICON, ICON_SMALL, 0); + DrawIconEx(dcMem, 4 + CSkin::m_SkinnedFrame_left + CSkin::m_bClipBorder + CSkin::m_titleBarLeftOff, rcText.top + (rcText.bottom - rcText.top) / 2 - 8, hIcon, 16, 16, 0, 0, DI_NORMAL); + + // title buttons + pContainer->rcClose.top = pContainer->rcMin.top = pContainer->rcMax.top = CSkin::m_titleButtonTopOff; + pContainer->rcClose.bottom = pContainer->rcMin.bottom = pContainer->rcMax.bottom = CSkin::m_titleButtonTopOff + CSkin::m_titleBarButtonSize.cy; + + pContainer->rcClose.right = rcWindow.right - 10 - CSkin::m_titleBarRightOff; + pContainer->rcClose.left = pContainer->rcClose.right - CSkin::m_titleBarButtonSize.cx; + + pContainer->rcMax.right = pContainer->rcClose.left - 2; + pContainer->rcMax.left = pContainer->rcMax.right - CSkin::m_titleBarButtonSize.cx; + + pContainer->rcMin.right = pContainer->rcMax.left - 2; + pContainer->rcMin.left = pContainer->rcMin.right - CSkin::m_titleBarButtonSize.cx; + + item_normal = &SkinItems[ID_EXTBKTITLEBUTTON]; + item_hot = &SkinItems[ID_EXTBKTITLEBUTTONMOUSEOVER]; + item_pressed = &SkinItems[ID_EXTBKTITLEBUTTONPRESSED]; + + for (i=0; i < 3; i++) { + RECT *rc = 0; + HICON hIcon; + + switch (i) { + case 0: + rc = &pContainer->rcMin; + hIcon = CSkin::m_minIcon; + break; + case 1: + rc = &pContainer->rcMax; + hIcon = CSkin::m_maxIcon; + break; + case 2: + rc = &pContainer->rcClose; + hIcon = CSkin::m_closeIcon; + break; + } + if (rc) { + item = pContainer->buttons[i].isPressed ? item_pressed : (pContainer->buttons[i].isHot ? item_hot : item_normal); + CSkin::DrawItem(dcMem, rc, item); + DrawIconEx(dcMem, rc->left + ((rc->right - rc->left) / 2 - 8), rc->top + ((rc->bottom - rc->top) / 2 - 8), hIcon, 16, 16, 0, 0, DI_NORMAL); + } + } + SetBkMode(dcMem, TRANSPARENT); + BitBlt(dcFrame, 0, 0, rcWindow.right, rcWindow.bottom, dcMem, 0, 0, SRCCOPY); + SelectObject(dcMem, hbmOld); + DeleteObject(hbmMem); + DeleteDC(dcMem); + ReleaseDC(hwndDlg, dcFrame); + } + else mir_callNextSubclass(hwndDlg, ContainerWndProc, msg, wParam, lParam); + + hdcReal = BeginPaint(hwndDlg, &ps); + + GetClientRect(hwndDlg, &rcClient); + width = rcClient.right - rcClient.left; + height = rcClient.bottom - rcClient.top; + if (width != pContainer->oldDCSize.cx || height != pContainer->oldDCSize.cy) { + CSkinItem *sbaritem = &SkinItems[ID_EXTBKSTATUSBAR]; + BOOL statusBarSkinnd = !(pContainer->dwFlags & CNT_NOSTATUSBAR) && !sbaritem->IGNORED; + LONG sbarDelta = statusBarSkinnd ? pContainer->statusBarHeight : 0; + + pContainer->oldDCSize.cx = width; + pContainer->oldDCSize.cy = height; + + if (pContainer->cachedDC) { + SelectObject(pContainer->cachedDC, pContainer->oldHBM); + DeleteObject(pContainer->cachedHBM); + DeleteDC(pContainer->cachedDC); + } + pContainer->cachedDC = CreateCompatibleDC(hdcReal); + pContainer->cachedHBM = CreateCompatibleBitmap(hdcReal, width, height); + pContainer->oldHBM = (HBITMAP)SelectObject(pContainer->cachedDC, pContainer->cachedHBM); + + hdc = pContainer->cachedDC; + + if (!CSkin::DrawItem(hdc, &rcClient, item)) + FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE)); + + if (sbarDelta) { + rcClient.top = rcClient.bottom - sbarDelta; + CSkin::DrawItem(hdc, &rcClient, sbaritem); + } + } + BitBlt(hdcReal, 0, 0, width, height, pContainer->cachedDC, 0, 0, SRCCOPY); + EndPaint(hwndDlg, &ps); + return 0; + } + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCMOUSEHOVER: + case WM_NCMOUSEMOVE: + if (pContainer && CSkin::m_frameSkins) { + POINT pt; + RECT rcWindow; + BOOL isMin, isMax, isClose; + int i; + + GetCursorPos(&pt); + GetWindowRect(hwndDlg, &rcWindow); + + CopyMemory(&pContainer->oldbuttons[0], &pContainer->buttons[0], sizeof(TitleBtn) * 3); + ZeroMemory(&pContainer->buttons[0], sizeof(TitleBtn) * 3); + isMin = isMax = isClose = FALSE; + + if (pt.x >= (rcWindow.left + pContainer->rcMin.left) && pt.x <= (rcWindow.left + pContainer->rcClose.right) && pt.y < rcWindow.top + 24 && wParam != HTTOPRIGHT) { + LRESULT result = 0; //DefWindowProc(hwndDlg, msg, wParam, lParam); + HDC hdc = GetWindowDC(hwndDlg); + LONG left = rcWindow.left; + + pt.y = 10; + isMin = pt.x >= left + pContainer->rcMin.left && pt.x <= left + pContainer->rcMin.right; + isMax = pt.x >= left + pContainer->rcMax.left && pt.x <= left + pContainer->rcMax.right; + isClose = pt.x >= left + pContainer->rcClose.left && pt.x <= left + pContainer->rcClose.right; + + if (msg == WM_NCMOUSEMOVE) { + if (isMax) + pContainer->buttons[BTN_MAX].isHot = TRUE; + else if (isMin) + pContainer->buttons[BTN_MIN].isHot = TRUE; + else if (isClose) + pContainer->buttons[BTN_CLOSE].isHot = TRUE; + } + else if (msg == WM_NCLBUTTONDOWN) { + if (isMax) + pContainer->buttons[BTN_MAX].isPressed = TRUE; + else if (isMin) + pContainer->buttons[BTN_MIN].isPressed = TRUE; + else if (isClose) + pContainer->buttons[BTN_CLOSE].isPressed = TRUE; + } + else if (msg == WM_NCLBUTTONUP) { + if (isMin) + SendMessage(hwndDlg, WM_SYSCOMMAND, SC_MINIMIZE, 0); + else if (isMax) { + if (IsZoomed(hwndDlg)) + PostMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0); + else + PostMessage(hwndDlg, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + } + else if (isClose) + PostMessage(hwndDlg, WM_SYSCOMMAND, SC_CLOSE, 0); + } + for (i=0; i < 3; i++) { + if (pContainer->buttons[i].isHot != pContainer->oldbuttons[i].isHot) { + RECT *rc = 0; + HICON hIcon; + + switch (i) { + case 0: + rc = &pContainer->rcMin; + hIcon = CSkin::m_minIcon; + break; + case 1: + rc = &pContainer->rcMax; + hIcon = CSkin::m_maxIcon; + break; + case 2: + rc = &pContainer->rcClose; + hIcon = CSkin::m_closeIcon; + break; + } + if (rc) { + CSkinItem *item = &SkinItems[pContainer->buttons[i].isPressed ? ID_EXTBKTITLEBUTTONPRESSED : (pContainer->buttons[i].isHot ? ID_EXTBKTITLEBUTTONMOUSEOVER : ID_EXTBKTITLEBUTTON)]; + CSkin::DrawItem(hdc, rc, item); + DrawIconEx(hdc, rc->left + ((rc->right - rc->left) / 2 - 8), rc->top + ((rc->bottom - rc->top) / 2 - 8), hIcon, 16, 16, 0, 0, DI_NORMAL); + } + } + } + ReleaseDC(hwndDlg, hdc); + return result; + } + else { + LRESULT result = DefWindowProc(hwndDlg, msg, wParam, lParam); + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_NOCHILDREN); + return result; + } + } + break; + + case WM_SETCURSOR: + if (CSkin::m_frameSkins && (HWND)wParam == hwndDlg) { + DefWindowProc(hwndDlg, msg, wParam, lParam); + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_NOCHILDREN); + return 1; + } + break; + + case WM_NCCALCSIZE: + if (!CSkin::m_frameSkins) + break; + + if (wParam) { + RECT *rc; + NCCALCSIZE_PARAMS *ncsp = (NCCALCSIZE_PARAMS *)lParam; + + DefWindowProc(hwndDlg, msg, wParam, lParam); + rc = &ncsp->rgrc[0]; + + rc->left += CSkin::m_realSkinnedFrame_left; + rc->right -= CSkin::m_realSkinnedFrame_right; + rc->bottom -= CSkin::m_realSkinnedFrame_bottom; + rc->top += CSkin::m_realSkinnedFrame_caption; + return TRUE; + } + + return DefWindowProc(hwndDlg, msg, wParam, lParam); + + case WM_NCACTIVATE: + if (pContainer) { + pContainer->ncActive = wParam; + if (bSkinned && CSkin::m_frameSkins) { + SendMessage(hwndDlg, WM_NCPAINT, 0, 0); + return 1; + } + } + break; + case WM_SETTEXT: + case WM_SETICON: + if (CSkin::m_frameSkins) { + DefWindowProc(hwndDlg, msg, wParam, lParam); + RedrawWindow(hwndDlg, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN); + return 0; + } + break; + + case WM_NCHITTEST: + { + RECT r; + POINT pt; + int k = 0; + int clip = CSkin::m_bClipBorder; + + if (!pContainer) + break; + + if (!(pContainer->dwFlags & CNT_NOTITLE)) + break; + + GetWindowRect(hwndDlg, &r); + GetCursorPos(&pt); + if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6) { + if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) + return HTBOTTOM; + if (pt.x < r.left + clip + 10) + return HTBOTTOMLEFT; + if (pt.x > r.right - clip - 10) + return HTBOTTOMRIGHT; + + } + else if (pt.y >= r.top && pt.y <= r.top + 6) { + if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) + return HTTOP; + if (pt.x < r.left + clip + 10) + return HTTOPLEFT; + if (pt.x > r.right - clip - 10) + return HTTOPRIGHT; + } + else if (pt.x >= r.left && pt.x <= r.left + clip + 6) + return HTLEFT; + else if (pt.x >= r.right - clip - 6 && pt.x <= r.right) + return HTRIGHT; + } + return(DefWindowProc(hwndDlg, WM_NCHITTEST, wParam, lParam)); + + case 0xae: // must be some undocumented message - seems it messes with the title bar... + if (CSkin::m_frameSkins) + return 0; + } + return mir_callNextSubclass(hwndDlg, ContainerWndProc, msg, wParam, lParam); +} + +// container window procedure... + +static BOOL fHaveTipper = FALSE; + +static INT_PTR CALLBACK DlgProcContainer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + int iItem = 0; + TCITEM item; + + TContainerData *pContainer = (TContainerData*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + BOOL bSkinned = CSkin::m_skinEnabled ? TRUE : FALSE; + HWND hwndTab = GetDlgItem(hwndDlg, IDC_MSGTABS); + + switch (msg) { + case WM_INITDIALOG: + { + bool bAero = M.isAero(); + BOOL isFlat = M.GetByte("tbflat", 1); + BOOL isThemed = !M.GetByte("nlflat", 0); + + fHaveTipper = ServiceExists("mToolTip/ShowTip"); + fForceOverlayIcons = M.GetByte("forceTaskBarStatusOverlays", 0) ? true : false; + + pContainer = (TContainerData*)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pContainer); + mir_subclassWindow(hwndDlg, ContainerWndProc); + + pContainer->hwnd = hwndDlg; + DWORD dwCreateFlags = pContainer->dwFlags; + + pContainer->isCloned = (pContainer->dwFlags & CNT_CREATE_CLONED); + pContainer->fPrivateThemeChanged = FALSE; + + SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); // set options... + pContainer->dwFlags |= dwCreateFlags; + + LoadOverrideTheme(pContainer); + DWORD ws = GetWindowLongPtr(hwndTab, GWL_STYLE); + if (pContainer->dwFlagsEx & TCF_FLAT) + ws |= TCS_BUTTONS; + + memset((void*)&pContainer->mOld, -1000, sizeof(MARGINS)); + + if (pContainer->dwFlagsEx & TCF_SINGLEROWTABCONTROL) { + ws &= ~TCS_MULTILINE; + ws |= TCS_SINGLELINE; + ws |= TCS_FIXEDWIDTH; + } + else { + ws &= ~TCS_SINGLELINE; + ws |= TCS_MULTILINE; + if (ws & TCS_BUTTONS) + ws |= TCS_FIXEDWIDTH; + } + SetWindowLongPtr(hwndTab, GWL_STYLE, ws); + + pContainer->buttonItems = g_ButtonSet.items; + + pContainer->dwFlags = ((pContainer->dwFlagsEx & (TCF_SBARLEFT | TCF_SBARRIGHT)) ? + pContainer->dwFlags | CNT_SIDEBAR : pContainer->dwFlags & ~CNT_SIDEBAR); + + pContainer->SideBar = new CSideBar(pContainer); + pContainer->MenuBar = new CMenuBar(hwndDlg, pContainer); + + SetClassLongPtr(hwndDlg, GCL_STYLE, GetClassLongPtr(hwndDlg, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); + SetClassLongPtr(hwndTab, GCL_STYLE, GetClassLongPtr(hwndTab, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); + + SetClassLongPtr(hwndDlg, GCL_STYLE, GetClassLongPtr(hwndDlg, GCL_STYLE) & ~CS_DROPSHADOW); + + // additional system menu items... + HMENU hSysmenu = GetSystemMenu(hwndDlg, FALSE); + int iMenuItems = GetMenuItemCount(hSysmenu); + + InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_SEPARATOR, 0, _T("")); + InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top")); + if (!CSkin::m_frameSkins) + InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_NOTITLE, TranslateT("Hide title bar")); + InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_SEPARATOR, 0, _T("")); + InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_MOREOPTIONS, TranslateT("Container options...")); + SetWindowText(hwndDlg, TranslateT("Message Session...")); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PluginConfig.g_iconContainer); + + // make the tab control the controlling parent window for all message dialogs + + ws = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_EXSTYLE); + SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_EXSTYLE, ws | WS_EX_CONTROLPARENT); + + LONG x_pad = M.GetByte("x-pad", 3) + (pContainer->dwFlagsEx & TCF_CLOSEBUTTON ? 7 : 0); + LONG y_pad = M.GetByte("y-pad", 3) + ((pContainer->dwFlags & CNT_TABSBOTTOM) ? 1 : 0); + + if (pContainer->dwFlagsEx & TCF_FLAT) + y_pad += 1; //(pContainer->dwFlags & CNT_TABSBOTTOM ? 1 : 2); + + TabCtrl_SetPadding(GetDlgItem(hwndDlg, IDC_MSGTABS), x_pad, y_pad); + + TabCtrl_SetImageList(GetDlgItem(hwndDlg, IDC_MSGTABS), PluginConfig.g_hImageList); + + SendMessage(hwndDlg, DM_CONFIGURECONTAINER, 0, 10); + + // context menu + pContainer->hMenuContext = PluginConfig.g_hMenuContext; + + // tab tooltips... + if (!fHaveTipper || M.GetByte("d_tooltips", 0) == 0) { + pContainer->hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL, g_hInst, (LPVOID) NULL); + + if (pContainer->hwndTip) { + SetWindowPos(pContainer->hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + TabCtrl_SetToolTips(GetDlgItem(hwndDlg, IDC_MSGTABS), pContainer->hwndTip); + } + } + else pContainer->hwndTip = 0; + + if (pContainer->dwFlags & CNT_CREATE_MINIMIZED) { + WINDOWPLACEMENT wp = {0}; + wp.length = sizeof(wp); + + SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) & ~WS_VISIBLE); + ShowWindow(hwndDlg, SW_SHOWMINNOACTIVE); + SendMessage(hwndDlg, DM_RESTOREWINDOWPOS, 0, 0); + //GetClientRect(hwndDlg, &pContainer->rcSaved); + ShowWindow(hwndDlg, SW_SHOWMINNOACTIVE); + GetWindowPlacement(hwndDlg, &wp); + pContainer->rcSaved.left = pContainer->rcSaved.top = 0; + pContainer->rcSaved.right = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + pContainer->rcSaved.bottom = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + } + else { + SendMessage(hwndDlg, DM_RESTOREWINDOWPOS, 0, 0); + ShowWindow(hwndDlg, SW_SHOWNORMAL); + } + } + + // prevent ugly back background being visible while tabbed clients are created + if (M.isAero()) { + MARGINS m = {-1}; + CMimAPI::m_pfnDwmExtendFrameIntoClientArea(hwndDlg, &m); + } + return TRUE; + + case DM_RESTOREWINDOWPOS: + // retrieve the container window geometry information from the database. + if (pContainer->isCloned && pContainer->hContactFrom != 0 && !(pContainer->dwFlags & CNT_GLOBALSIZE)) { + if (Utils_RestoreWindowPosition(hwndDlg, pContainer->hContactFrom, SRMSGMOD_T, "split")) { + if (Utils_RestoreWindowPositionNoMove(hwndDlg, pContainer->hContactFrom, SRMSGMOD_T, "split")) + if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, "split")) + if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, "split")) + SetWindowPos(hwndDlg, 0, 50, 50, 450, 300, SWP_NOZORDER | SWP_NOACTIVATE); + } + } + else { + if (pContainer->dwFlags & CNT_GLOBALSIZE) { + if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, "split")) + if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, "split")) + SetWindowPos(hwndDlg, 0, 50, 50, 450, 300, SWP_NOZORDER | SWP_NOACTIVATE); + } + else { + char szCName[CONTAINER_NAMELEN + 20]; + mir_snprintf(szCName, sizeof(szCName), "%s%d", CONTAINER_PREFIX, pContainer->iContainerIndex); + if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, szCName)) { + if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, szCName)) + if (Utils_RestoreWindowPosition(hwndDlg, NULL, SRMSGMOD_T, "split")) + if (Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, SRMSGMOD_T, "split")) + SetWindowPos(hwndDlg, 0, 50, 50, 450, 300, SWP_NOZORDER | SWP_NOACTIVATE); + } + } + } + return 0; + + case WM_SIZE: + if (IsIconic(hwndDlg)) + pContainer->dwFlags |= CNT_DEFERREDSIZEREQUEST; + else { + RECT rcClient, rcUnadjusted; + TCITEM item = {0}; + POINT pt = {0}; + + GetClientRect(hwndDlg, &rcClient); + pContainer->MenuBar->getClientRect(); + + if (pContainer->hwndStatus) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + SendMessage(pContainer->hwndStatus, WM_USER + 101, 0, (LPARAM)dat); + + RECT rcs; + GetWindowRect(pContainer->hwndStatus, &rcs); + pContainer->statusBarHeight = (rcs.bottom - rcs.top) + 1; + SendMessage(pContainer->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, 0); + } + else pContainer->statusBarHeight = 0; + + CopyRect(&pContainer->rcSaved, &rcClient); + rcUnadjusted = rcClient; + + pContainer->MenuBar->Resize(LOWORD(lParam), HIWORD(lParam), FALSE); + LONG rebarHeight = pContainer->MenuBar->getHeight(); + pContainer->MenuBar->Show((pContainer->dwFlags & CNT_NOMENUBAR) ? SW_HIDE : SW_SHOW); + + LONG sbarWidth = pContainer->SideBar->getWidth(); + LONG sbarWidth_left = pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT ? sbarWidth : 0; + + if (lParam) { + DWORD dwSWPFlags = SWP_NOACTIVATE|SWP_NOZORDER |SWP_DEFERERASE | SWP_NOCOPYBITS; // | SWP_NOSENDCHANGING | SWP_ASYNCWINDOWPOS; + if (pContainer->dwFlags & CNT_TABSBOTTOM) + SetWindowPos(hwndTab, 0, pContainer->tBorder_outer_left + sbarWidth_left, pContainer->tBorder_outer_top + rebarHeight, + (rcClient.right - rcClient.left) - (pContainer->tBorder_outer_left + pContainer->tBorder_outer_right + sbarWidth), + (rcClient.bottom - rcClient.top) - pContainer->statusBarHeight - (pContainer->tBorder_outer_top + pContainer->tBorder_outer_bottom) - rebarHeight, dwSWPFlags); + else + SetWindowPos(hwndTab, 0, pContainer->tBorder_outer_left + sbarWidth_left, pContainer->tBorder_outer_top + rebarHeight, + (rcClient.right - rcClient.left) - (pContainer->tBorder_outer_left + pContainer->tBorder_outer_right + sbarWidth), + (rcClient.bottom - rcClient.top) - pContainer->statusBarHeight - (pContainer->tBorder_outer_top + pContainer->tBorder_outer_bottom) - rebarHeight, dwSWPFlags); + } + + pContainer->SideBar->resizeScrollWnd(sbarWidth_left ? pContainer->tBorder_outer_left : rcClient.right - pContainer->tBorder_outer_right - (sbarWidth - 2), + pContainer->tBorder_outer_top + rebarHeight, 0, + (rcClient.bottom - rcClient.top) - pContainer->statusBarHeight - (pContainer->tBorder_outer_top + pContainer->tBorder_outer_bottom) - rebarHeight); + + AdjustTabClientRect(pContainer, &rcClient); + + BOOL sizeChanged = (((rcClient.right - rcClient.left) != pContainer->preSIZE.cx) || ((rcClient.bottom - rcClient.top) != pContainer->preSIZE.cy)); + if (sizeChanged) { + pContainer->preSIZE.cx = rcClient.right - rcClient.left; + pContainer->preSIZE.cy = rcClient.bottom - rcClient.top; + } + + // we care about all client sessions, but we really resize only the active tab (hwndActive) + // we tell inactive tabs to resize theirselves later when they get activated (DM_CHECKSIZE + // just queues a resize request) + int nCount = TabCtrl_GetItemCount(hwndTab); + + for (int i=0; i < nCount; i++) { + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, i, &item); + if ((HWND)item.lParam == pContainer->hwndActive) { + SetWindowPos((HWND)item.lParam, 0, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), + SWP_NOSENDCHANGING|SWP_NOACTIVATE/*|SWP_NOCOPYBITS*/); + if (!pContainer->bSizingLoop && sizeChanged) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + DM_ScrollToBottom(dat, 0, 1); + } + } + else if (sizeChanged) + SendMessage((HWND)item.lParam, DM_CHECKSIZE, 0, 0); + } + pContainer->SideBar->scrollIntoView(); + + if (!M.isAero()) { // aero mode uses buffered paint, no forced redraw needed + RedrawWindow(hwndTab, NULL, NULL, RDW_INVALIDATE | (pContainer->bSizingLoop ? RDW_ERASE : 0)); + RedrawWindow(hwndDlg, NULL, NULL, (bSkinned ? RDW_FRAME : 0) | RDW_INVALIDATE | ((pContainer->bSizingLoop || wParam == SIZE_RESTORED ) ? RDW_ERASE : 0)); + } + + if (pContainer->hwndStatus) + InvalidateRect(pContainer->hwndStatus, NULL, FALSE); + + if ((CSkin::m_bClipBorder != 0 || CSkin::m_bRoundedCorner) && CSkin::m_frameSkins) { + HRGN rgn; + int clip = CSkin::m_bClipBorder; + + RECT rcWindow; + GetWindowRect(hwndDlg, &rcWindow); + + if (CSkin::m_bRoundedCorner) + rgn = CreateRoundRectRgn(clip, clip, (rcWindow.right - rcWindow.left) - clip + 1, + (rcWindow.bottom - rcWindow.top) - clip + 1, CSkin::m_bRoundedCorner + clip, CSkin::m_bRoundedCorner + clip); + else + rgn = CreateRectRgn(clip, clip, (rcWindow.right - rcWindow.left) - clip, (rcWindow.bottom - rcWindow.top) - clip); + SetWindowRgn(hwndDlg, rgn, TRUE); + } + else if (CSkin::m_frameSkins) + SetWindowRgn(hwndDlg, NULL, TRUE); + } + break; + + case WM_NOTIFY: + if (pContainer->MenuBar) { + LRESULT processed = pContainer->MenuBar->processMsg(msg, wParam, lParam); + if (processed != -1) { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, processed); + return(processed); + } + } + + if (pContainer->hwndStatus != 0 && ((LPNMHDR)lParam)->hwndFrom == pContainer->hwndStatus) { + switch (((LPNMHDR)lParam)->code) { + case NM_CLICK: + case NM_RCLICK: + unsigned int nParts, nPanel; + NMMOUSE *nm = (NMMOUSE*)lParam; + RECT rc; + + nParts = SendMessage(pContainer->hwndStatus, SB_GETPARTS, 0, 0); + if (nm->dwItemSpec == 0xFFFFFFFE) { + nPanel = 2; + SendMessage(pContainer->hwndStatus, SB_GETRECT, nPanel, (LPARAM)&rc); + if (nm->pt.x > rc.left && nm->pt.x < rc.right) + goto panel_found; + else + return FALSE; + } + else nPanel = nm->dwItemSpec; +panel_found: + if (nPanel == 2) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + SendMessage(pContainer->hwndStatus, SB_GETRECT, nPanel, (LPARAM)&rc); + if (dat) + SI_CheckStatusIconClick(dat, pContainer->hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code); + } + else if (((LPNMHDR)lParam)->code == NM_RCLICK) { + POINT pt; + MCONTACT hContact = 0; + HMENU hMenu; + + GetCursorPos(&pt); + SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + if (hContact) { + int iSel = 0; + hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, hContact, 0); + iSel = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); + if (iSel) + CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(iSel), MPCF_CONTACTMENU), hContact); + DestroyMenu(hMenu); + } + } + return TRUE; + } + break; + } + + switch (((LPNMHDR)lParam)->code) { + case TCN_SELCHANGE: + ZeroMemory(&item, sizeof(item)); + iItem = TabCtrl_GetCurSel(hwndTab); + item.mask = TCIF_PARAM; + if (TabCtrl_GetItem(hwndTab, iItem, &item)) { + if ((HWND)item.lParam != pContainer->hwndActive) { + if (pContainer->hwndActive && IsWindow(pContainer->hwndActive)) + ShowWindow(pContainer->hwndActive, SW_HIDE); + } + pContainer->hwndActive = (HWND)item.lParam; + SendMessage((HWND)item.lParam, DM_SAVESIZE, 0, 1); + ShowWindow((HWND)item.lParam, SW_SHOW); + if (!IsIconic(hwndDlg)) + SetFocus(pContainer->hwndActive); + } + SendMessage(hwndTab, EM_VALIDATEBOTTOM, 0, 0); + return 0; + + // tooltips + case NM_RCLICK: + { + int iItem; + bool fFromSidebar = false; + TCITEM item = { 0 }; + TWindowData *dat = 0; + + POINT pt, pt1; + GetCursorPos(&pt); + pt1 = pt; + HMENU subMenu = GetSubMenu(pContainer->hMenuContext, 0); + + if (((LPNMHDR)lParam)->idFrom == IDC_MSGTABS) { + if ((iItem = GetTabItemFromMouse(hwndTab, &pt)) == -1) + break; + + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, iItem, &item); + if (item.lParam && IsWindow((HWND)item.lParam)) + dat = (TWindowData*)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA); + } + // sent from a sidebar button (RMB click) instead of the tab control + else if (((LPNMHDR)lParam)->idFrom == 5000) { + TSideBarNotify* n = reinterpret_cast(lParam); + dat = const_cast(n->dat); + fFromSidebar = true; + } + + if (dat) + MsgWindowUpdateMenu(dat, subMenu, MENU_TABCONTEXT); + + int iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt1.x, pt1.y, 0, hwndDlg, NULL); + if (iSelection >= IDM_CONTAINERMENU) { + DBVARIANT dbv = {0}; + char szIndex[10]; + itoa(iSelection - IDM_CONTAINERMENU, szIndex, 10); + if (iSelection - IDM_CONTAINERMENU >= 0) { + ptrT tszName(db_get_tsa(NULL, CONTAINER_KEY, szIndex)); + if (tszName != NULL) + SendMessage((HWND)item.lParam, DM_CONTAINERSELECTED, 0, tszName); + } + return 1; + } + switch (iSelection) { + case ID_TABMENU_CLOSETAB: + if (fFromSidebar) + SendMessage(dat->hwnd, WM_CLOSE, 1, 0); + else + SendMessage(hwndDlg, DM_CLOSETABATMOUSE, 0, (LPARAM)&pt1); + break; + case ID_TABMENU_CLOSEOTHERTABS: + CloseOtherTabs(hwndTab, *dat); + break; + case ID_TABMENU_SAVETABPOSITION: + db_set_dw(dat->hContact, SRMSGMOD_T, "tabindex", dat->iTabID * 100); + break; + case ID_TABMENU_CLEARSAVEDTABPOSITION: + db_unset(dat->hContact, SRMSGMOD_T, "tabindex"); + break; + case ID_TABMENU_LEAVECHATROOM: + if (dat && dat->bType == SESSIONTYPE_CHAT) { + SESSION_INFO *si = dat->si; + if (si && dat->hContact) { + char *szProto = GetContactProto(dat->hContact); + if (szProto) + CallProtoService( szProto, PS_LEAVECHAT, dat->hContact, 0 ); + } + } + break; + case ID_TABMENU_ATTACHTOCONTAINER: + if ((iItem = GetTabItemFromMouse(hwndTab, &pt1)) == -1) + break; + ZeroMemory(&item, sizeof(item)); + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, iItem, &item); + CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_SELECTCONTAINER), hwndDlg, SelectContainerDlgProc, (LPARAM)item.lParam); + break; + case ID_TABMENU_CONTAINEROPTIONS: + if (pContainer->hWndOptions == 0) + CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CONTAINEROPTIONS), hwndDlg, DlgProcContainerOptions, (LPARAM)pContainer); + break; + case ID_TABMENU_CLOSECONTAINER: + SendMessage(hwndDlg, WM_CLOSE, 0, 0); + break; + } + InvalidateRect(hwndTab, NULL, FALSE); + return 1; + } + } + break; + + case WM_COMMAND: + { + bool fProcessContactMenu = pContainer->MenuBar->isContactMenu(); + bool fProcessMainMenu = pContainer->MenuBar->isMainMenu(); + pContainer->MenuBar->Cancel(); + + MCONTACT hContact; + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + DWORD dwOldFlags = pContainer->dwFlags; + ButtonItem *pItem = pContainer->buttonItems; + + if (dat) { + DWORD dwOldMsgWindowFlags = dat->dwFlags; + DWORD dwOldEventIsShown = dat->dwFlagsEx; + + if (fProcessContactMenu) + return(CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_CONTACTMENU), (LPARAM)dat->hContact)); + else if (fProcessMainMenu) { + return(CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam), MPCF_MAINMENU), 0)); + } + else if (MsgWindowMenuHandler(dat, LOWORD(wParam), MENU_PICMENU) == 1) + break; + } + SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + if (LOWORD(wParam) == IDC_TBFIRSTUID - 1) + break; + + switch (LOWORD(wParam)) { + case IDC_TOGGLESIDEBAR: + { + RECT rc; + LONG dwNewLeft; + BOOL skinnedMode = bSkinned; + + skinnedMode |= (IsThemeActive() ? 1 : 0); + + GetWindowRect(hwndDlg, &rc); + + bool fVisible = pContainer->SideBar->isVisible(); + if (fVisible) { + dwNewLeft = pContainer->SideBar->getWidth(); + pContainer->SideBar->setVisible(false); + } + else { + pContainer->SideBar->setVisible(true); + dwNewLeft = -(pContainer->SideBar->getWidth()); + } + + pContainer->preSIZE.cx = pContainer->preSIZE.cy = 0; + pContainer->oldDCSize.cx = pContainer->oldDCSize.cy = 0; + + PostMessage(hwndDlg, WM_SIZE, 0, 1); + } + break; + + case IDC_SIDEBARDOWN: + case IDC_SIDEBARUP: + { + HWND hwnd = GetFocus(); + pContainer->SideBar->processScrollerButtons(LOWORD(wParam)); + SetFocus(hwnd); + } + break; + + default: + Utils::CmdDispatcher(Utils::CMD_CONTAINER, hwndDlg, LOWORD(wParam), wParam, lParam, 0, pContainer); + } + if (pContainer->dwFlags != dwOldFlags) + SendMessage(hwndDlg, DM_CONFIGURECONTAINER, 0, 0); + } + break; + + case WM_ENTERSIZEMOVE: + { + RECT rc; + GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc); + + SIZE sz; + sz.cx = rc.right - rc.left; + sz.cy = rc.bottom - rc.top; + pContainer->oldSize = sz; + pContainer->bSizingLoop = TRUE; + } + break; + + case WM_EXITSIZEMOVE: + { + RECT rc; + GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc); + if (!((rc.right - rc.left) == pContainer->oldSize.cx && (rc.bottom - rc.top) == pContainer->oldSize.cy)) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + DM_ScrollToBottom(dat, 0, 0); + SendMessage(pContainer->hwndActive, WM_SIZE, 0, 0); + } + pContainer->bSizingLoop = FALSE; + } + break; + + // determine minimum and maximum size limits + // 1) for maximizing the window when the "vertical maximize" option is set + // 2) to limit the minimum height when manually resizing the window + // (this avoids overlapping of controls inside the window and ensures + // that at least 2 lines of the message log are always visible). + case WM_GETMINMAXINFO: + { + RECT rc, rcWindow, rcClient = {0}; + POINT pt; + + MINMAXINFO *mmi = (MINMAXINFO *) lParam; + mmi->ptMinTrackSize.x = 275; + mmi->ptMinTrackSize.y = 130; + GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc); + if (pContainer->hwndActive) // at container creation time, there is no hwndActive yet.. + GetClientRect(pContainer->hwndActive, &rcClient); + GetWindowRect(hwndDlg, &rcWindow); + pt.y = rc.top; + TabCtrl_AdjustRect(GetDlgItem(hwndDlg, IDC_MSGTABS), FALSE, &rc); + // uChildMinHeight holds the min height for the client window only + // so let's add the container's vertical padding (title bar, tab bar, + // window border, status bar) to this value + if (pContainer->hwndActive) + mmi->ptMinTrackSize.y = pContainer->uChildMinHeight + (pContainer->hwndActive ? ((rcWindow.bottom - rcWindow.top) - rcClient.bottom) : 0); + + if (pContainer->dwFlags & CNT_VERTICALMAX || (GetKeyState(VK_CONTROL) & 0x8000)) { + RECT rcDesktop = {0}; + BOOL fDesktopValid = FALSE; + int monitorXOffset = 0; + WINDOWPLACEMENT wp = {0}; + + HMONITOR hMonitor = MonitorFromWindow(hwndDlg, 2); + if (hMonitor) { + MONITORINFO mi = { 0 }; + mi.cbSize = sizeof(mi); + GetMonitorInfoA(hMonitor, &mi); + rcDesktop = mi.rcWork; + OffsetRect(&rcDesktop, -mi.rcMonitor.left, -mi.rcMonitor.top); + monitorXOffset = mi.rcMonitor.left; + fDesktopValid = TRUE; + } + if (!fDesktopValid) + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0); + + wp.length = sizeof(wp); + GetWindowPlacement(hwndDlg, &wp); + mmi->ptMaxSize.y = rcDesktop.bottom - rcDesktop.top; + mmi->ptMaxSize.x = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + mmi->ptMaxPosition.x = wp.rcNormalPosition.left - monitorXOffset; + mmi->ptMaxPosition.y = 0; + if (IsIconic(hwndDlg)) { + mmi->ptMaxPosition.x += rcDesktop.left; + mmi->ptMaxPosition.y += rcDesktop.top; + } + + // protect against invalid values... + if (mmi->ptMinTrackSize.y < 50 || mmi->ptMinTrackSize.y > rcDesktop.bottom) + mmi->ptMinTrackSize.y = 130; + } + } + return 0; + + case DM_UPDATETITLE: + { + MCONTACT hContact = 0; + TWindowData *dat = NULL; + + if (lParam) { // lParam != 0 means sent by a chat window + TCHAR szText[512]; + dat = (TWindowData*)GetWindowLongPtr((HWND)wParam, GWLP_USERDATA); + GetWindowText((HWND)wParam, szText, SIZEOF(szText)); + szText[SIZEOF(szText)-1] = 0; + SetWindowText(hwndDlg, szText); + if (dat) + SendMessage(hwndDlg, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hTabIcon != dat->hTabStatusIcon ? dat->hTabIcon : dat->hTabStatusIcon)); + return 0; + } + if (wParam == 0) { // no hContact given - obtain the hContact for the active tab + if (pContainer->hwndActive && IsWindow(pContainer->hwndActive)) + SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + else + break; + dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + } + else { + HWND hwnd = M.FindWindow(wParam); + if (hwnd == 0) { + SESSION_INFO *si = SM_FindSessionByHCONTACT(wParam); + if (si) { + SendMessage(si->hWnd, GC_UPDATETITLE, 0, 0); + return 0; + } + } + hContact = wParam; + if (hwnd && hContact) + dat = (TWindowData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + } + if (dat) { + SendMessage(hwndDlg, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabStatusIcon)); + const TCHAR *szNewTitle = Utils::FormatTitleBar(dat, pContainer->settings->szTitleFormat); + if (szNewTitle) { + SetWindowText(hwndDlg, szNewTitle); + mir_free((void*)szNewTitle); + } + } + } + return 0; + + case WM_TIMER: + if (wParam == TIMERID_HEARTBEAT) { + TWindowData *dat = 0; + if (GetForegroundWindow() != hwndDlg && (pContainer->settings->autoCloseSeconds > 0) && !pContainer->fHidden) { + BOOL fResult = TRUE; + BroadCastContainer(pContainer, DM_CHECKAUTOHIDE, (WPARAM)pContainer->settings->autoCloseSeconds, (LPARAM)&fResult); + + if (fResult && 0 == pContainer->hWndOptions) + PostMessage(hwndDlg, WM_CLOSE, 1, 0); + } + dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + if (dat && dat->bType == SESSIONTYPE_IM) { + if (dat->idle && pContainer->hwndActive && IsWindow(pContainer->hwndActive)) + dat->Panel->Invalidate(TRUE); + } + else if (dat) + SendMessage(dat->hwnd, GC_UPDATESTATUSBAR, 0, 0); + } + else if (wParam == TIMERID_HOVER) { + RECT rcWindow; + GetWindowRect(hwndDlg, &rcWindow); + } + break; + + case WM_SYSCOMMAND: + switch (wParam) { + case IDM_STAYONTOP: + SetWindowPos(hwndDlg, (pContainer->dwFlags & CNT_STICKY) ? HWND_NOTOPMOST : HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + CheckMenuItem(GetSystemMenu(hwndDlg, FALSE), IDM_STAYONTOP, (pContainer->dwFlags & CNT_STICKY) ? MF_BYCOMMAND | MF_UNCHECKED : MF_BYCOMMAND | MF_CHECKED); + ApplyContainerSetting(pContainer, CNT_STICKY, pContainer->dwFlags & CNT_STICKY ? 0 : 1, false); + break; + case IDM_NOTITLE: + pContainer->oldSize.cx = 0; + pContainer->oldSize.cy = 0; + + CheckMenuItem(GetSystemMenu(hwndDlg, FALSE), IDM_NOTITLE, (pContainer->dwFlags & CNT_NOTITLE) ? MF_BYCOMMAND | MF_UNCHECKED : MF_BYCOMMAND | MF_CHECKED); + ApplyContainerSetting(pContainer, CNT_NOTITLE, pContainer->dwFlags & CNT_NOTITLE ? 0 : 1, false); + break; + case IDM_MOREOPTIONS: + if (IsIconic(pContainer->hwnd)) + SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); + if (pContainer->hWndOptions == 0) + CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CONTAINEROPTIONS), hwndDlg, DlgProcContainerOptions, (LPARAM)pContainer); + break; + case SC_MAXIMIZE: + pContainer->oldSize.cx = pContainer->oldSize.cy = 0; + break; + case SC_RESTORE: + pContainer->oldSize.cx = pContainer->oldSize.cy = 0; + memset((void*)&pContainer->mOld, -1000, sizeof(MARGINS)); + break; + case SC_MINIMIZE: + TWindowData *dat = reinterpret_cast(GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA)); + if (dat) { + //GetWindowRect(GetDlgItem(pContainer->hwndActive, dat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &pContainer->rcLogSaved); + GetWindowRect(pContainer->hwndActive, &pContainer->rcLogSaved); + pContainer->ptLogSaved.x = pContainer->rcLogSaved.left; + pContainer->ptLogSaved.y = pContainer->rcLogSaved.top; + ScreenToClient(hwndDlg, &pContainer->ptLogSaved); + } + } + break; + + case DM_SELECTTAB: + switch (wParam) { + int iItems, iCurrent, iNewTab; + + case DM_SELECT_BY_HWND: + ActivateTabFromHWND(hwndTab, (HWND)lParam); + break; + + case DM_SELECT_NEXT: + case DM_SELECT_PREV: + case DM_SELECT_BY_INDEX: + iItems = TabCtrl_GetItemCount(hwndTab); + iCurrent = TabCtrl_GetCurSel(hwndTab); + + if (iItems == 1) + break; + if (wParam == DM_SELECT_PREV) + iNewTab = iCurrent ? iCurrent - 1 : iItems - 1; // cycle if current is already the leftmost tab.. + else if (wParam == DM_SELECT_NEXT) + iNewTab = (iCurrent == (iItems - 1)) ? 0 : iCurrent + 1; + else if (wParam == DM_SELECT_BY_INDEX) { + if ((int)lParam > iItems) + break; + iNewTab = lParam - 1; + } + + if (iNewTab != iCurrent) { + TabControlData *tabdat = (TabControlData *)GetWindowLongPtr(hwndTab, GWLP_USERDATA); + ZeroMemory(&item, sizeof(item)); + item.mask = TCIF_PARAM; + if (TabCtrl_GetItem(hwndTab, iNewTab, &item)) { + TabCtrl_SetCurSel(hwndTab, iNewTab); + ShowWindow(pContainer->hwndActive, SW_HIDE); + pContainer->hwndActive = (HWND)item.lParam; + ShowWindow((HWND)item.lParam, SW_SHOW); + SetFocus(pContainer->hwndActive); + } + } + break; + } + break; + + case WM_INITMENUPOPUP: + pContainer->MenuBar->setActive(reinterpret_cast(wParam)); + break; + + case WM_LBUTTONDOWN: + if (pContainer->dwFlags & CNT_NOTITLE) { + POINT pt; + GetCursorPos(&pt); + return SendMessage(hwndDlg, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); + } + break; + + // pass the WM_ACTIVATE msg to the active message dialog child + case WM_NCACTIVATE: + if (IsWindowVisible(hwndDlg)) + pContainer->fHidden = false; + break; + + case WM_ACTIVATE: + if (pContainer == NULL) + break; + + if (LOWORD(wParam == WA_INACTIVE)) + BroadCastContainer(pContainer, DM_CHECKINFOTIP, wParam, lParam); + + if (LOWORD(wParam == WA_INACTIVE) && (HWND)lParam != PluginConfig.g_hwndHotkeyHandler && GetParent((HWND)lParam) != hwndDlg) { + BOOL fTransAllowed = !bSkinned || PluginConfig.m_bIsVista; + + if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) { + SetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)HIWORD(pContainer->settings->dwTransparency), (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); + } + } + pContainer->hwndSaved = 0; + + if (LOWORD(wParam) != WA_ACTIVE) { + pContainer->MenuBar->Cancel(); + break; + } + + case WM_MOUSEACTIVATE: + if (pContainer != NULL) { + TCITEM item; + int curItem = 0; + BOOL fTransAllowed = !bSkinned || PluginConfig.m_WinVerMajor >= 6; + + FlashContainer(pContainer, 0, 0); + pContainer->dwFlashingStarted = 0; + pLastActiveContainer = pContainer; + if (pContainer->dwFlags & CNT_DEFERREDTABSELECT) { + pContainer->dwFlags &= ~CNT_DEFERREDTABSELECT; + SendMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0); + + NMHDR nmhdr = { hwndTab, IDC_MSGTABS, TCN_SELCHANGE }; + SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nmhdr); // do it via a WM_NOTIFY / TCN_SELCHANGE to simulate user-activation + } + if (pContainer->dwFlags & CNT_DEFERREDSIZEREQUEST) { + pContainer->dwFlags &= ~CNT_DEFERREDSIZEREQUEST; + SendMessage(hwndDlg, WM_SIZE, 0, 0); + } + + if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) { + DWORD trans = LOWORD(pContainer->settings->dwTransparency); + SetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)trans, (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); + } + if (pContainer->dwFlags & CNT_NEED_UPDATETITLE) { + MCONTACT hContact = 0; + pContainer->dwFlags &= ~CNT_NEED_UPDATETITLE; + if (pContainer->hwndActive) { + SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + if (hContact) + SendMessage(hwndDlg, DM_UPDATETITLE, hContact, 0); + } + } + ZeroMemory(&item, sizeof(item)); + item.mask = TCIF_PARAM; + if ((curItem = TabCtrl_GetCurSel(hwndTab)) >= 0) + TabCtrl_GetItem(hwndTab, curItem, &item); + if (pContainer->dwFlags & CNT_DEFERREDCONFIGURE && curItem >= 0) { + pContainer->dwFlags &= ~CNT_DEFERREDCONFIGURE; + pContainer->hwndActive = (HWND)item.lParam; + SendMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0); + if (pContainer->hwndActive != 0 && IsWindow(pContainer->hwndActive)) { + ShowWindow(pContainer->hwndActive, SW_SHOW); + SetFocus(pContainer->hwndActive); + SendMessage(pContainer->hwndActive, WM_ACTIVATE, WA_ACTIVE, 0); + RedrawWindow(pContainer->hwndActive, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); + } + } + else if (curItem >= 0) + SendMessage((HWND)item.lParam, WM_ACTIVATE, WA_ACTIVE, 0); + } + break; + + case DM_CLOSETABATMOUSE: + { + HWND hwndCurrent = pContainer->hwndActive; + POINT *pt = (POINT *)lParam; + if ((iItem = GetTabItemFromMouse(hwndTab, pt)) == -1) + break; + + TCITEM item = {0}; + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, iItem, &item); + if (item.lParam) { + if ((HWND)item.lParam != hwndCurrent) { + pContainer->bDontSmartClose = TRUE; + SendMessage((HWND)item.lParam, WM_CLOSE, 0, 1); + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); + pContainer->bDontSmartClose = FALSE; + } + else SendMessage((HWND)item.lParam, WM_CLOSE, 0, 1); + } + } + break; + + case WM_PAINT: + if (bSkinned || M.isAero()) { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwndDlg, &ps); + EndPaint(hwndDlg, &ps); + return 0; + } + break; + + case WM_ERASEBKGND: + // avoid flickering of the menu bar when aero is active + if (pContainer) { + HDC hdc = (HDC)wParam; + RECT rc; + GetClientRect(hwndDlg, &rc); + + if (M.isAero()) { + HDC hdcMem; + HANDLE hbp = CMimAPI::m_pfnBeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, 0, &hdcMem); + FillRect(hdcMem, &rc, CSkin::m_BrushBack); + CSkin::FinalizeBufferedPaint(hbp, &rc); + } + else { + if (CSkin::m_skinEnabled) + CSkin::DrawItem(hdc, &rc, &SkinItems[ID_EXTBKCONTAINER]); + else { + CSkin::FillBack(hdc, &rc); + if (pContainer->SideBar->isActive() && pContainer->SideBar->isVisible()) { + + HPEN hPen = ::CreatePen(PS_SOLID, 1, PluginConfig.m_cRichBorders ? PluginConfig.m_cRichBorders : ::GetSysColor(COLOR_3DSHADOW)); + HPEN hOldPen = reinterpret_cast(::SelectObject(hdc, hPen)); + LONG x = (pContainer->SideBar->getFlags() & CSideBar::SIDEBARORIENTATION_LEFT ? pContainer->SideBar->getWidth() - 2 + pContainer->tBorder_outer_left : + rc.right - pContainer->SideBar->getWidth() + 1 - pContainer->tBorder_outer_right); + ::MoveToEx(hdc, x, rc.top, 0); + ::LineTo(hdc, x, rc.bottom); + ::SelectObject(hdc, hOldPen); + ::DeleteObject(hPen); + } + } + } + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); + return TRUE; + } + break; + + case DM_OPTIONSAPPLIED: + { + char szCname[40]; + TCHAR szTitleFormat[200]; + TCHAR* szThemeName = NULL; + DBVARIANT dbv = {0}; + + szTitleFormat[0] = 0; + + if (pContainer->isCloned && pContainer->hContactFrom != 0) { + pContainer->settings = &PluginConfig.globalContainerSettings; + + pContainer->szRelThemeFile[0] = pContainer->szAbsThemeFile[0] = 0; + mir_snprintf(szCname, 40, "%s_theme", CONTAINER_PREFIX); + if (!db_get_ts(pContainer->hContactFrom, SRMSGMOD_T, szCname, &dbv)) + szThemeName = dbv.ptszVal; + } + else { + Utils::ReadPrivateContainerSettings(pContainer); + if (szThemeName == NULL) { + mir_snprintf(szCname, 40, "%s%d_theme", CONTAINER_PREFIX, pContainer->iContainerIndex); + if (!db_get_ts(NULL, SRMSGMOD_T, szCname, &dbv)) + szThemeName = dbv.ptszVal; + } + } + Utils::SettingsToContainer(pContainer); + + if (szThemeName != NULL) { + PathToAbsoluteT(szThemeName, pContainer->szAbsThemeFile, M.getDataPath()); + mir_sntprintf(pContainer->szRelThemeFile, MAX_PATH, _T("%s"), szThemeName); + db_free(&dbv); + } + else pContainer->szAbsThemeFile[0] = pContainer->szRelThemeFile[0] = 0; + + pContainer->ltr_templates = pContainer->rtl_templates = 0; + } + break; + + case DM_STATUSBARCHANGED: + SendMessage(hwndDlg, WM_SIZE, 0, 0); + { + RECT rc; + GetWindowRect(hwndDlg, &rc); + SetWindowPos(hwndDlg, 0, rc.left, rc.top, rc.right - rc.left, (rc.bottom - rc.top) + 1, SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos(hwndDlg, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE); + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); + } + if (pContainer->hwndStatus != 0 && pContainer->hwndActive != 0) + PostMessage(pContainer->hwndActive, DM_STATUSBARCHANGED, 0, 0); + return 0; + + case DM_CONFIGURECONTAINER: + { + DWORD ws, wsold, ex = 0, exold = 0; + HMENU hSysmenu = GetSystemMenu(hwndDlg, FALSE); + MCONTACT hContact = 0; + int i=0; + UINT sBarHeight; + bool bAero = M.isAero(); + + ws = wsold = GetWindowLongPtr(hwndDlg, GWL_STYLE); + if (!CSkin::m_frameSkins) { + ws = (pContainer->dwFlags & CNT_NOTITLE) ? + ((IsWindowVisible(hwndDlg) ? WS_VISIBLE : 0) | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN | WS_THICKFRAME | (CSkin::m_frameSkins ? WS_SYSMENU : WS_SYSMENU | WS_SIZEBOX)) : + ws | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; + } + + SetWindowLongPtr(hwndDlg, GWL_STYLE, ws); + + pContainer->tBorder = M.GetByte((bSkinned ? "S_tborder" : "tborder"), 2); + pContainer->tBorder_outer_left = g_ButtonSet.left + M.GetByte((bSkinned ? "S_tborder_outer_left" : "tborder_outer_left"), 2); + pContainer->tBorder_outer_right = g_ButtonSet.right + M.GetByte((bSkinned ? "S_tborder_outer_right" : "tborder_outer_right"), 2); + pContainer->tBorder_outer_top = g_ButtonSet.top + M.GetByte((bSkinned ? "S_tborder_outer_top" : "tborder_outer_top"), 2); + pContainer->tBorder_outer_bottom = g_ButtonSet.bottom + M.GetByte((bSkinned ? "S_tborder_outer_bottom" : "tborder_outer_bottom"), 2); + sBarHeight = (UINT)M.GetByte((bSkinned ? "S_sbarheight" : "sbarheight"), 0); + + if (LOBYTE(LOWORD(GetVersion())) >= 5) { + BOOL fTransAllowed = !bSkinned || PluginConfig.m_WinVerMajor >= 6; + + ex = exold = GetWindowLongPtr(hwndDlg, GWL_EXSTYLE); + ex = (pContainer->dwFlags & CNT_TRANSPARENCY && (!CSkin::m_skinEnabled || fTransAllowed)) ? ex | WS_EX_LAYERED : ex & ~(WS_EX_LAYERED); + + SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, ex); + if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) { + DWORD trans = LOWORD(pContainer->settings->dwTransparency); + SetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)trans, (/* pContainer->bSkinned ? LWA_COLORKEY : */ 0) | (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); + } + } + + if (!CSkin::m_frameSkins) + CheckMenuItem(hSysmenu, IDM_NOTITLE, (pContainer->dwFlags & CNT_NOTITLE) ? MF_BYCOMMAND | MF_CHECKED : MF_BYCOMMAND | MF_UNCHECKED); + + CheckMenuItem(hSysmenu, IDM_STAYONTOP, pContainer->dwFlags & CNT_STICKY ? MF_BYCOMMAND | MF_CHECKED : MF_BYCOMMAND | MF_UNCHECKED); + SetWindowPos(hwndDlg, (pContainer->dwFlags & CNT_STICKY) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + if (ws != wsold) { + RECT rc; + GetWindowRect(hwndDlg, &rc); + if ((ws & WS_CAPTION) != (wsold & WS_CAPTION)) { + SetWindowPos(hwndDlg, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOCOPYBITS); + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + if (pContainer->hwndActive != 0) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + DM_ScrollToBottom(dat, 0, 0); + } + } + } + + pContainer->dwFlags = ((pContainer->dwFlagsEx & (TCF_SBARLEFT | TCF_SBARRIGHT)) ? + pContainer->dwFlags | CNT_SIDEBAR : pContainer->dwFlags & ~CNT_SIDEBAR); + + pContainer->SideBar->Init(); + + ws = wsold = GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_STYLE); + if (pContainer->dwFlags & CNT_TABSBOTTOM) + ws |= (TCS_BOTTOM); + else + ws &= ~(TCS_BOTTOM); + if ((ws & (TCS_BOTTOM | TCS_MULTILINE)) != (wsold & (TCS_BOTTOM | TCS_MULTILINE))) { + SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MSGTABS), GWL_STYLE, ws); + RedrawWindow(GetDlgItem(hwndDlg, IDC_MSGTABS), NULL, NULL, RDW_INVALIDATE); + } + + if (pContainer->dwFlags & CNT_NOSTATUSBAR) { + if (pContainer->hwndStatus) { + DestroyWindow(pContainer->hwndStatus); + pContainer->hwndStatus = 0; + pContainer->statusBarHeight = 0; + SendMessage(hwndDlg, DM_STATUSBARCHANGED, 0, 0); + } + } + else if (pContainer->hwndStatus == 0) { + pContainer->hwndStatus = CreateWindowEx(0, _T("TSStatusBarClass"), NULL, SBT_TOOLTIPS | WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwndDlg, NULL, g_hInst, NULL); + + if (sBarHeight && bSkinned) + SendMessage(pContainer->hwndStatus, SB_SETMINHEIGHT, sBarHeight, 0); + } + if (pContainer->hwndActive != 0) { + hContact = 0; + SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + if (hContact) + SendMessage(hwndDlg, DM_UPDATETITLE, hContact, 0); + } + SendMessage(hwndDlg, WM_SIZE, 0, 1); + BroadCastContainer(pContainer, DM_CONFIGURETOOLBAR, 0, 1); + } + return 0; + + // search the first and most recent unread events in all client tabs... + // return all information via a RECENTINFO structure (tab indices, + // window handles and timestamps). + case DM_QUERYRECENT: + { + int iItems = TabCtrl_GetItemCount(hwndTab); + TCITEM item = {0}; + + DWORD dwTimestamp, dwMostRecent = 0; + + RECENTINFO *ri = (RECENTINFO *)lParam; + ri->iFirstIndex = ri->iMostRecent = -1; + ri->dwFirst = ri->dwMostRecent = 0; + ri->hwndFirst = ri->hwndMostRecent = 0; + + for (int i=0; i < iItems; i++) { + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, i, &item); + SendMessage((HWND)item.lParam, DM_QUERYLASTUNREAD, 0, (LPARAM)&dwTimestamp); + if (dwTimestamp > ri->dwMostRecent) { + ri->dwMostRecent = dwTimestamp; + ri->iMostRecent = i; + ri->hwndMostRecent = (HWND)item.lParam; + if (ri->iFirstIndex == -1) { + ri->iFirstIndex = i; + ri->dwFirst = dwTimestamp; + ri->hwndFirst = (HWND)item.lParam; + } + } + } + } + return 0; + + // search tab with either next or most recent unread message and select it + case DM_QUERYPENDING: + { + RECENTINFO ri; + SendMessage(hwndDlg, DM_QUERYRECENT, 0, (LPARAM)&ri); + + NMHDR nmhdr; + nmhdr.code = TCN_SELCHANGE; + + if (wParam == DM_QUERY_NEXT && ri.iFirstIndex != -1) { + TabCtrl_SetCurSel(hwndTab, ri.iFirstIndex); + SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nmhdr); + } + if (wParam == DM_QUERY_MOSTRECENT && ri.iMostRecent != -1) { + TabCtrl_SetCurSel(hwndTab, ri.iMostRecent); + SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nmhdr); + } + } + return 0; + + case DM_SETICON: + { + TWindowData *dat = (TWindowData*)wParam; + HICON hIconMsg = PluginConfig.g_IconMsgEvent; + HICON hIconBig = (dat && dat->cache) ? LoadSkinnedProtoIconBig(dat->cache->getProto(), dat->cache->getStatus()) : 0; + + if (Win7Taskbar->haveLargeIcons()) { + if ((HICON)lParam == PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING] || (HICON)lParam == hIconMsg) { + Win7Taskbar->setOverlayIcon(hwndDlg, lParam); + if (GetForegroundWindow() != hwndDlg) + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); + if ((HICON)lParam == hIconMsg) + pContainer->hIconTaskbarOverlay = hIconMsg; + break; + } + + if (dat) { + if (dat->hTaskbarIcon == 0) + dat->hTaskbarIcon = ((dat->pContainer->dwFlags & CNT_AVATARSONTASKBAR) ? Utils::iconFromAvatar(dat) : 0); + else { + if (!(dat->pContainer->dwFlags & CNT_AVATARSONTASKBAR)) { + DestroyIcon(dat->hTaskbarIcon); + dat->hTaskbarIcon = 0; + } + } + + if (dat->hTaskbarIcon) { + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)dat->hTaskbarIcon); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); + Win7Taskbar->setOverlayIcon(hwndDlg, (LPARAM)(dat->hTabIcon ? (LPARAM)dat->hTabIcon : lParam)); + } + else { + if (0 == hIconBig || (HICON)CALLSERVICE_NOTFOUND == hIconBig) + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)lParam); + else + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIconBig); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); + if (dat->pContainer->hIconTaskbarOverlay) + Win7Taskbar->setOverlayIcon(hwndDlg, (LPARAM)dat->pContainer->hIconTaskbarOverlay); + else if (Win7Taskbar->haveAlwaysGroupingMode() && fForceOverlayIcons) + Win7Taskbar->setOverlayIcon(hwndDlg, lParam); + else + Win7Taskbar->clearOverlayIcon(hwndDlg); + } + return 0; + } + } + + // default handling (no win7 taskbar) + if ((HICON)lParam == PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]) { // always set typing icon, but don't save it... + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)PluginConfig.g_IconTypingEventBig); + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); + break; + } + if (reinterpret_cast(lParam) == hIconMsg) + hIconBig = LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE); + + if (pContainer->hIcon == STICK_ICON_MSG && (HICON)lParam != hIconMsg && pContainer->dwFlags & CNT_NEED_UPDATETITLE) { + lParam = (LPARAM)hIconMsg; + hIconBig = LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE); + } + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam); + if (0 != hIconBig && reinterpret_cast(CALLSERVICE_NOTFOUND) != hIconBig) + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, LPARAM(hIconBig)); + pContainer->hIcon = (lParam == (LPARAM)hIconMsg) ? STICK_ICON_MSG : 0; + } + return 0; + + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; + int cx = PluginConfig.m_smcxicon; + int cy = PluginConfig.m_smcyicon; + int id = LOWORD(dis->itemID); + + if (dis->hwndItem == pContainer->hwndStatus && !(pContainer->dwFlags & CNT_NOSTATUSBAR)) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + if (dat) + DrawStatusIcons(dat, dis->hDC, dis->rcItem, 2); + return TRUE; + } + } + return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam); + + case WM_MEASUREITEM: + return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); + + case DM_QUERYCLIENTAREA: + { + RECT *rc = (RECT *)lParam; + if (rc) { + if (!IsIconic(hwndDlg)) + GetClientRect(hwndDlg, rc); + else + CopyRect(rc, &pContainer->rcSaved); + AdjustTabClientRect(pContainer, rc); + } + } + return 0; + + case WM_DESTROY: + pContainer->hwnd = 0; + pContainer->hwndActive = 0; + pContainer->hMenuContext = 0; + if (pContainer->hwndStatus) + DestroyWindow(pContainer->hwndStatus); + + // mir_free private theme... + if (pContainer->theme.isPrivate) { + mir_free(pContainer->ltr_templates); + mir_free(pContainer->rtl_templates); + mir_free(pContainer->theme.logFonts); + mir_free(pContainer->theme.fontColors); + mir_free(pContainer->theme.rtfFonts); + } + + if (pContainer->hwndTip) + DestroyWindow(pContainer->hwndTip); + RemoveContainerFromList(pContainer); + SM_RemoveContainer(pContainer); + if (pContainer->cachedDC) { + SelectObject(pContainer->cachedDC, pContainer->oldHBM); + DeleteObject(pContainer->cachedHBM); + DeleteDC(pContainer->cachedDC); + } + if (pContainer->cachedToolbarDC) { + SelectObject(pContainer->cachedToolbarDC, pContainer->oldhbmToolbarBG); + DeleteObject(pContainer->hbmToolbarBG); + DeleteDC(pContainer->cachedToolbarDC); + } + return 0; + + case WM_NCDESTROY: + if (pContainer) { + delete pContainer->MenuBar; + delete pContainer->SideBar; + if (pContainer->settings != &PluginConfig.globalContainerSettings) + mir_free(pContainer->settings); + mir_free(pContainer); + } + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + break; + + case WM_CLOSE: + if (PluginConfig.m_HideOnClose && !lParam) { + ShowWindow(hwndDlg, SW_HIDE); + pContainer->fHidden = true; + } + else { + if (TabCtrl_GetItemCount(hwndTab) > 1) { + LRESULT res = CWarning::show(CWarning::WARN_CLOSEWINDOW, MB_YESNOCANCEL | MB_ICONQUESTION); + if (IDNO == res || IDCANCEL == res) + break; + } + + // dont ask if container is empty (no tabs) + if (lParam == 0 && TabCtrl_GetItemCount(GetDlgItem(hwndDlg, IDC_MSGTABS)) > 0) { + int clients = TabCtrl_GetItemCount(hwndTab), iOpenJobs = 0; + + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + for (int i = 0; i < clients; i++) { + TabCtrl_GetItem(hwndTab, i, &item); + if (item.lParam && IsWindow((HWND)item.lParam)) + SendMessage((HWND)item.lParam, DM_CHECKQUEUEFORCLOSE, 0, (LPARAM)&iOpenJobs); + } + if (iOpenJobs && pContainer) { + if (pContainer->exFlags & CNT_EX_CLOSEWARN) + return TRUE; + + pContainer->exFlags |= CNT_EX_CLOSEWARN; + LRESULT result = SendQueue::WarnPendingJobs(iOpenJobs); + pContainer->exFlags &= ~CNT_EX_CLOSEWARN; + if (result == IDNO) + return TRUE; + } + } + + // save geometry information to the database... + if (!(pContainer->dwFlags & CNT_GLOBALSIZE)) { + WINDOWPLACEMENT wp = { 0 }; + wp.length = sizeof(wp); + if (GetWindowPlacement(hwndDlg, &wp) != 0) { + if (pContainer->isCloned && pContainer->hContactFrom != 0) { + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &item); + + MCONTACT hContact; + SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + db_set_b(hContact, SRMSGMOD_T, "splitmax", (BYTE)((wp.showCmd == SW_SHOWMAXIMIZED) ? 1 : 0)); + + for (int i = 0; i < TabCtrl_GetItemCount(hwndTab); i++) { + if (TabCtrl_GetItem(hwndTab, i, &item)) { + SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + db_set_dw(hContact, SRMSGMOD_T, "splitx", wp.rcNormalPosition.left); + db_set_dw(hContact, SRMSGMOD_T, "splity", wp.rcNormalPosition.top); + db_set_dw(hContact, SRMSGMOD_T, "splitwidth", wp.rcNormalPosition.right - wp.rcNormalPosition.left); + db_set_dw(hContact, SRMSGMOD_T, "splitheight", wp.rcNormalPosition.bottom - wp.rcNormalPosition.top); + } + } + } + else { + char szCName[40]; + mir_snprintf(szCName, 40, "%s%dx", CONTAINER_PREFIX, pContainer->iContainerIndex); + db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.left); + mir_snprintf(szCName, 40, "%s%dy", CONTAINER_PREFIX, pContainer->iContainerIndex); + db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.top); + mir_snprintf(szCName, 40, "%s%dwidth", CONTAINER_PREFIX, pContainer->iContainerIndex); + db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.right - wp.rcNormalPosition.left); + mir_snprintf(szCName, 40, "%s%dheight", CONTAINER_PREFIX, pContainer->iContainerIndex); + db_set_dw(0, SRMSGMOD_T, szCName, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top); + + db_set_b(0, SRMSGMOD_T, "splitmax", (BYTE)((wp.showCmd == SW_SHOWMAXIMIZED) ? 1 : 0)); + } + } + } + + // clear temp flags which should NEVER be saved... + if (pContainer->isCloned && pContainer->hContactFrom != 0) { + TCITEM item = {0}; + item.mask = TCIF_PARAM; + pContainer->dwFlags &= ~(CNT_DEFERREDCONFIGURE | CNT_CREATE_MINIMIZED | CNT_DEFERREDSIZEREQUEST | CNT_CREATE_CLONED); + for (int i=0; i < TabCtrl_GetItemCount(hwndTab); i++) { + if (TabCtrl_GetItem(hwndTab, i, &item)) { + MCONTACT hContact; + SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact); + + char szCName[40]; + mir_snprintf(szCName, 40, "%s_theme", CONTAINER_PREFIX); + if (lstrlen(pContainer->szRelThemeFile) > 1) { + if (pContainer->fPrivateThemeChanged == TRUE) { + PathToRelativeT(pContainer->szRelThemeFile, pContainer->szAbsThemeFile, M.getDataPath()); + db_set_ts(hContact, SRMSGMOD_T, szCName, pContainer->szRelThemeFile); + pContainer->fPrivateThemeChanged = FALSE; + } + } + else { + db_unset(hContact, SRMSGMOD_T, szCName); + pContainer->fPrivateThemeChanged = FALSE; + } + } + } + } + else Utils::SaveContainerSettings(pContainer, CONTAINER_PREFIX); + DestroyWindow(hwndDlg); + } + } + return FALSE; +} + +// search the list of tabs and return the tab (by index) which "belongs" to the given +// hwnd. The hwnd is the handle of a message dialog childwindow. At creation, +// the dialog handle is stored in the TCITEM.lParam field, because we need +// to know the owner of the tab. +// +// hwndTab: handle of the tab control itself. +// hwnd: handle of a message dialog. +// +// returns the tab index (zero based), -1 if no tab is found (which SHOULD not +// really happen, but who knows... ;)) + +int TSAPI GetTabIndexFromHWND(HWND hwndTab, HWND hwnd) +{ + int iItems = TabCtrl_GetItemCount(hwndTab); + + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + for (int i=0; i < iItems; i++) { + TabCtrl_GetItem(hwndTab, i, &item); + if ((HWND)item.lParam == hwnd) + return i; + } + return -1; +} + +// search the list of tabs and return the tab (by index) which "belongs" to the given +// hwnd. The hwnd is the handle of a message dialog childwindow. At creation, +// the dialog handle is stored in the TCITEM.lParam field, because we need +// to know the owner of the tab. +// +// hwndTab: handle of the tab control itself. +// hwnd: handle of a message dialog. +// +// returns the tab index (zero based), -1 if no tab is found (which SHOULD not +// really happen, but who knows... ;)) + +HWND TSAPI GetHWNDFromTabIndex(HWND hwndTab, int idx) +{ + int iItems = TabCtrl_GetItemCount(hwndTab); + + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + for (int i=0; i < iItems; i++) { + TabCtrl_GetItem(hwndTab, i, &item); + if (i == idx) + return (HWND)item.lParam; + } + return NULL; +} + +// activates the tab belonging to the given client HWND (handle of the actual +// message window. + +int TSAPI ActivateTabFromHWND(HWND hwndTab, HWND hwnd) +{ + int iItem = GetTabIndexFromHWND(hwndTab, hwnd); + if (iItem >= 0) { + TabCtrl_SetCurSel(hwndTab, iItem); + + NMHDR nmhdr = { 0 }; + nmhdr.code = TCN_SELCHANGE; + SendMessage(GetParent(hwndTab), WM_NOTIFY, 0, (LPARAM)&nmhdr); // do it via a WM_NOTIFY / TCN_SELCHANGE to simulate user-activation + return iItem; + } + return -1; +} + +// returns the index of the tab under the mouse pointer. Used for +// context menu popup and tooltips +// pt: mouse coordinates, obtained from GetCursorPos() + +int TSAPI GetTabItemFromMouse(HWND hwndTab, POINT *pt) +{ + ScreenToClient(hwndTab, pt); + + TCHITTESTINFO tch; + tch.pt = *pt; + tch.flags = 0; + return TabCtrl_HitTest(hwndTab, &tch); +} + +// enumerates tabs and closes all of them, but the one in dat +void TSAPI CloseOtherTabs(HWND hwndTab, TWindowData &dat) +{ + for (int idxt = 0; idxt < dat.pContainer->iChilds; ) { + HWND otherTab = GetHWNDFromTabIndex(hwndTab, idxt); + if (otherTab != NULL && otherTab != dat.hwnd) + SendMessage(otherTab, WM_CLOSE, 1, 0); + else + ++idxt; + } +} + +// cut off contact name to the option value set via Options->Tabbed messaging +// some people were requesting this, because really long contact list names +// are causing extraordinary wide tabs and these are looking ugly and wasting +// screen space. +// +// size = max length of target string + +int TSAPI CutContactName(const TCHAR *oldname, TCHAR *newname, unsigned int size) +{ + int cutMax = PluginConfig.m_CutContactNameTo; + + if ((int)lstrlen(oldname) <= cutMax) { + lstrcpyn(newname, oldname, size); + newname[size - 1] = 0; + } + else { + TCHAR fmt[20]; + mir_sntprintf(fmt, 18, _T("%%%d.%ds..."), cutMax, cutMax); + mir_sntprintf(newname, size, fmt, oldname); + newname[size - 1] = 0; + } + return 0; +} + +// functions for handling the linked list of struct ContainerWindowData *foo + +static TContainerData* TSAPI AppendToContainerList(TContainerData *pContainer) +{ + if (!pFirstContainer) { + pFirstContainer = pContainer; + pFirstContainer->pNext = NULL; + return pFirstContainer; + } + + TContainerData *p = pFirstContainer; + while (p->pNext != 0) + p = p->pNext; + p->pNext = pContainer; + pContainer->pNext = NULL; + return p; +} + +TContainerData* TSAPI FindContainerByName(const TCHAR *name) +{ + if (name == NULL || lstrlen(name) == 0) + return 0; + + if (M.GetByte("singlewinmode", 0)) // single window mode - always return 0 and force a new container + return NULL; + + for (TContainerData *p = pFirstContainer; p; p = p->pNext) + if (!_tcsncmp(p->szName, name, CONTAINER_NAMELEN)) + return p; + + // error, didn't find it. + return NULL; +} + +static TContainerData* TSAPI RemoveContainerFromList(TContainerData *pContainer) +{ + if (pContainer == pFirstContainer) { + if (pContainer->pNext != NULL) + pFirstContainer = pContainer->pNext; + else + pFirstContainer = NULL; + + if (pLastActiveContainer == pContainer) // make sure, we don't reference this container anymore + pLastActiveContainer = pFirstContainer; + + return pFirstContainer; + } + + for (TContainerData *p = pFirstContainer; p; p = p->pNext) { + if (p->pNext == pContainer) { + p->pNext = p->pNext->pNext; + + if (pLastActiveContainer == pContainer) // make sure, we don't reference this container anymore + pLastActiveContainer = pFirstContainer; + + return 0; + } + } + return NULL; +} + +// calls the TabCtrl_AdjustRect to calculate the "real" client area of the tab. +// also checks for the option "hide tabs when only one tab open" and adjusts +// geometry if necessary +// rc is the RECT obtained by GetClientRect(hwndTab) + +void TSAPI AdjustTabClientRect(TContainerData *pContainer, RECT *rc) +{ + HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS); + DWORD tBorder = pContainer->tBorder; + DWORD dwStyle = GetWindowLongPtr(hwndTab, GWL_STYLE); + + RECT rcTab, rcTabOrig; + GetClientRect(hwndTab, &rcTab); + DWORD dwBottom = rcTab.bottom; + DWORD dwTop = rcTab.top; + if (!(pContainer->dwFlags & CNT_SIDEBAR) && (pContainer->iChilds > 1 || !(pContainer->dwFlags & CNT_HIDETABS))) { + rcTabOrig = rcTab; + TabCtrl_AdjustRect(hwndTab, FALSE, &rcTab); + DWORD dwTopPad = rcTab.top - rcTabOrig.top; + + rc->left += tBorder; + rc->right -= tBorder; + + if (dwStyle & TCS_BUTTONS) { + if (pContainer->dwFlags & CNT_TABSBOTTOM) { + int nCount = TabCtrl_GetItemCount(hwndTab); + if (nCount > 0) { + RECT rcItem; + TabCtrl_GetItemRect(hwndTab, nCount - 1, &rcItem); + rc->bottom = rcItem.top; + } + } + else { + rc->top += (dwTopPad - 2); + rc->bottom = rcTabOrig.bottom; + } + } + else { + if (pContainer->dwFlags & CNT_TABSBOTTOM) + rc->bottom = rcTab.bottom + 2; + else { + rc->top += (dwTopPad - 2); + rc->bottom = rcTabOrig.bottom; + } + } + + rc->top += tBorder; + rc->bottom -= tBorder; + } + else { + rc->bottom = rcTab.bottom; + rc->top = rcTab.top; + } + rc->right -= (pContainer->tBorder_outer_left + pContainer->tBorder_outer_right); + if (pContainer->SideBar->isVisible()) + rc->right -= pContainer->SideBar->getWidth(); +} + +// retrieve the container name for the given contact handle. +// if none is assigned, return the name of the default container + +int TSAPI GetContainerNameForContact(MCONTACT hContact, TCHAR *szName, int iNameLen) +{ + // single window mode using cloned (temporary) containers + if (M.GetByte("singlewinmode", 0)) { + _tcsncpy_s(szName, iNameLen, _T("Message Session"), _TRUNCATE); + return 0; + } + + // use clist group names for containers... + if (M.GetByte("useclistgroups", 0)) { + ptrT tszGroup(db_get_tsa(hContact, "CList", "Group")); + if (tszGroup == NULL) { + _tcsncpy_s(szName, iNameLen, _T("default"), _TRUNCATE); + return 0; + } + + _tcsncpy_s(szName, iNameLen, tszGroup, _TRUNCATE); + return 1; + } + + ptrT tszContainerName(db_get_tsa(hContact, SRMSGMOD_T, CONTAINER_SUBKEY)); + if (tszContainerName == NULL) { + _tcsncpy_s(szName, iNameLen, _T("default"), _TRUNCATE); + return 0; + } + + _tcsncpy_s(szName, iNameLen, tszContainerName, _TRUNCATE); + return 1; +} + +void TSAPI DeleteContainer(int iIndex) +{ + char szIndex[10]; + itoa(iIndex, szIndex, 10); + ptrT tszContainerName(db_get_tsa(NULL, CONTAINER_KEY, szIndex)); + if (tszContainerName == NULL) + return; + + db_set_ts(NULL, CONTAINER_KEY, szIndex, _T("**mir_free**")); + + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + ptrT tszValue(db_get_tsa(hContact, SRMSGMOD_T, CONTAINER_SUBKEY)); + if (!lstrcmp(tszValue, tszContainerName)) + db_unset(hContact, SRMSGMOD_T, CONTAINER_SUBKEY); + } + + char szSetting[CONTAINER_NAMELEN + 30]; + mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%d_Flags", CONTAINER_PREFIX, iIndex); + db_unset(NULL, SRMSGMOD_T, szSetting); + mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%d_Trans", CONTAINER_PREFIX, iIndex); + db_unset(NULL, SRMSGMOD_T, szSetting); + mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dwidth", CONTAINER_PREFIX, iIndex); + db_unset(NULL, SRMSGMOD_T, szSetting); + mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dheight", CONTAINER_PREFIX, iIndex); + db_unset(NULL, SRMSGMOD_T, szSetting); + mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dx", CONTAINER_PREFIX, iIndex); + db_unset(NULL, SRMSGMOD_T, szSetting); + mir_snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dy", CONTAINER_PREFIX, iIndex); + db_unset(NULL, SRMSGMOD_T, szSetting); +} + +void TSAPI RenameContainer(int iIndex, const TCHAR *szNew) +{ + if (lstrlen(szNew) == 0) + return; + + char szIndex[10]; + itoa(iIndex, szIndex, 10); + ptrT tszContainerName(db_get_tsa(NULL, CONTAINER_KEY, szIndex)); + if (tszContainerName == NULL) + return; + + db_set_ts(NULL, CONTAINER_KEY, szIndex, szNew); + + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + ptrT tszValue(db_get_tsa(hContact, SRMSGMOD_T, CONTAINER_SUBKEY)); + if (!lstrcmp(tszValue, tszContainerName)) + db_set_ts(hContact, SRMSGMOD_T, CONTAINER_SUBKEY, szNew); + } +} + +HMENU TSAPI BuildContainerMenu() +{ + if (PluginConfig.g_hMenuContainer != 0) { + HMENU submenu = GetSubMenu(PluginConfig.g_hMenuContext, 0); + RemoveMenu(submenu, 6, MF_BYPOSITION); + DestroyMenu(PluginConfig.g_hMenuContainer); + PluginConfig.g_hMenuContainer = 0; + } + + // no container attach menu, if we are using the "clist group mode" + if (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) + return NULL; + + HMENU hMenu = CreateMenu(); + int i = 0; + while (true) { + char szCounter[10]; + itoa(i, szCounter, 10); + ptrT tszName(db_get_tsa(NULL, CONTAINER_KEY, szCounter)); + if (tszName == NULL) + break; + + if (_tcsncmp(tszName, _T("**mir_free**"), CONTAINER_NAMELEN)) + AppendMenu(hMenu, MF_STRING, IDM_CONTAINERMENU + i, !_tcscmp(tszName, _T("default")) ? TranslateT("Default container") : tszName); + i++; + } + + InsertMenu(PluginConfig.g_hMenuContext, ID_TABMENU_ATTACHTOCONTAINER, MF_BYCOMMAND | MF_POPUP, (UINT_PTR)hMenu, TranslateT("Attach to")); + PluginConfig.g_hMenuContainer = hMenu; + return hMenu; +} + +// flashes the container +// iMode != 0: turn on flashing +// iMode == 0: turn off flashing + +void TSAPI FlashContainer(TContainerData *pContainer, int iMode, int iCount) +{ + if (pContainer->dwFlags & CNT_NOFLASH) // container should never flash + return; + + FLASHWINFO fwi; + fwi.cbSize = sizeof(fwi); + fwi.uCount = 0; + + if (iMode) { + fwi.dwFlags = FLASHW_ALL; + if (pContainer->dwFlags & CNT_FLASHALWAYS) + fwi.dwFlags |= FLASHW_TIMER; + else + fwi.uCount = (iCount == 0) ? M.GetByte("nrflash", 4) : iCount; + fwi.dwTimeout = M.GetDword("flashinterval", 1000); + + } + else fwi.dwFlags = FLASHW_STOP; + + fwi.hwnd = pContainer->hwnd; + pContainer->dwFlashingStarted = GetTickCount(); + FlashWindowEx(&fwi); +} + +void TSAPI ReflashContainer(TContainerData *pContainer) +{ + DWORD dwStartTime = pContainer->dwFlashingStarted; + + if (GetForegroundWindow() == pContainer->hwnd || GetActiveWindow() == pContainer->hwnd) // dont care about active windows + return; + + if (pContainer->dwFlags & CNT_NOFLASH || pContainer->dwFlashingStarted == 0) + return; // dont care about containers which should never flash + + if (pContainer->dwFlags & CNT_FLASHALWAYS) + FlashContainer(pContainer, 1, 0); + else { + // recalc the remaining flashes + DWORD dwInterval = M.GetDword("flashinterval", 1000); + int iFlashesElapsed = (GetTickCount() - dwStartTime) / dwInterval; + DWORD dwFlashesDesired = M.GetByte("nrflash", 4); + if (iFlashesElapsed < (int)dwFlashesDesired) + FlashContainer(pContainer, 1, dwFlashesDesired - iFlashesElapsed); + else { + BOOL isFlashed = FlashWindow(pContainer->hwnd, TRUE); + if (!isFlashed) + FlashWindow(pContainer->hwnd, TRUE); + } + } + pContainer->dwFlashingStarted = dwStartTime; +} + +// broadcasts a message to all child windows (tabs/sessions) + +void TSAPI BroadCastContainer(const TContainerData *pContainer, UINT message, WPARAM wParam, LPARAM lParam, BYTE bType) +{ + HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS); + + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + + int nCount = TabCtrl_GetItemCount(hwndTab); + for (int i = 0; i < nCount; i++) { + TabCtrl_GetItem(hwndTab, i, &item); + if (IsWindow((HWND)item.lParam)) { + if (bType == SESSIONTYPE_ANY) + SendMessage((HWND)item.lParam, message, wParam, lParam); + else { + TWindowData *dat = (TWindowData*)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA); + if (dat && dat->bType == bType) + SendMessage((HWND)item.lParam, message, wParam, lParam); + } + } + } +} + +void TSAPI CloseAllContainers() +{ + BOOL fOldHideSetting = PluginConfig.m_HideOnClose; + + while (pFirstContainer != NULL) { + if (!IsWindow(pFirstContainer->hwnd)) + pFirstContainer = pFirstContainer->pNext; + else { + PluginConfig.m_HideOnClose = FALSE; + ::SendMessage(pFirstContainer->hwnd, WM_CLOSE, 0, 1); + } + } + + PluginConfig.m_HideOnClose = fOldHideSetting; +} diff --git a/plugins/TabSRMM/src/controls.cpp b/plugins/TabSRMM/src/controls.cpp index 022b81432c..20e9f5e3aa 100644 --- a/plugins/TabSRMM/src/controls.cpp +++ b/plugins/TabSRMM/src/controls.cpp @@ -1,1036 +1,1037 @@ -/* - * Miranda NG: the free IM client for Microsoft* Windows* - * - * 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 - * - * Menu and status bar control(s) for the container window. - * - */ - -#include "commonheaders.h" - -static WNDPROC OldStatusBarproc = 0; - -bool CMenuBar::m_buttonsInit = false; -HHOOK CMenuBar::m_hHook = 0; -TBBUTTON CMenuBar::m_TbButtons[8] = {0}; -CMenuBar *CMenuBar::m_Owner = 0; -HBITMAP CMenuBar::m_MimIcon = 0; -int CMenuBar::m_MimIconRefCount = 0; - -static int resetLP(WPARAM, LPARAM, LPARAM obj) -{ - ((CMenuBar*)obj)->resetLP(); - return 0; -} - -CMenuBar::CMenuBar(HWND hwndParent, const TContainerData *pContainer) -{ - m_pContainer = const_cast(pContainer); - - if (m_MimIcon == 0) { - HDC hdc = ::GetDC(m_pContainer->hwnd); - HANDLE hIcon = LoadSkinnedIconHandle(SKINICON_OTHER_MIRANDA); - - HDC hdcTemp = ::CreateCompatibleDC(hdc); - - RECT rc = {0,0,16,16}; - m_MimIcon = CSkin::CreateAeroCompatibleBitmap(rc, hdcTemp); - HBITMAP hbmOld = reinterpret_cast(::SelectObject(hdcTemp, m_MimIcon)); - ::DrawIconEx(hdcTemp, 0, 0, (HICON)hIcon, 16, 16, 0, 0, DI_NORMAL); - ::SelectObject(hdcTemp, hbmOld); - - ::DeleteDC(hdcTemp); - ::ReleaseDC(m_pContainer->hwnd, hdc); - } - - m_MimIconRefCount++; - - m_hwndToolbar = ::CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE|TBSTYLE_FLAT|TBSTYLE_TRANSPARENT|TBSTYLE_LIST|/*CCS_NOPARENTALIGN|*/CCS_NODIVIDER|CCS_TOP, - 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL); - - ::SendMessage(m_hwndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); - - checkButtons(); - - m_activeMenu = 0; - m_activeID = 0; - m_isAero = M.isAero(); - m_mustAutoHide = false; - m_activeSubMenu = 0; - m_fTracking = false; - m_isContactMenu = m_isMainMenu = false; - HookEventParam(ME_LANGPACK_CHANGED, &::resetLP, (LPARAM)this); - - ::SetWindowLongPtr(m_hwndToolbar, GWLP_USERDATA, (LONG_PTR)this); - mir_subclassWindow(m_hwndToolbar, wndProc); -} - -CMenuBar::~CMenuBar() -{ - ::SetWindowLongPtr(m_hwndToolbar, GWLP_USERDATA, 0); - ::DestroyWindow(m_hwndToolbar); - releaseHook(); - m_MimIconRefCount--; - if (m_MimIconRefCount == 0) { - ::DeleteObject(m_MimIcon); - m_MimIcon = 0; - } -} - -/** - * retrieves the client rectangle for the rebar control. This must be - * called once per WM_SIZE event by the parent window. getHeight() depends on it. - * - * @return RECT&: client rectangle of the rebar control - */ -const RECT& CMenuBar::getClientRect() -{ - ::GetClientRect(m_hwndToolbar, &m_rcClient); - return(m_rcClient); -} - -void CMenuBar::obtainHook() -{ - releaseHook(); - if (m_hHook == 0) - m_hHook = ::SetWindowsHookEx(WH_MSGFILTER, CMenuBar::MessageHook, 0, GetCurrentThreadId()); - m_Owner = this; -} - -void CMenuBar::releaseHook() -{ - if (m_hHook) { - ::UnhookWindowsHookEx(m_hHook); - m_hHook = 0; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Retrieve the height of the rebar control -// -// @return LONG: height of the rebar, in pixels - -LONG CMenuBar::getHeight() const -{ - return((m_pContainer->dwFlags & CNT_NOMENUBAR) ? 0 : m_size_y); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// process all relevant messages. Must be called by the parent window's -// window procedure. -// -// @param msg -// @param wParam -// @param lParam -// -// @return LRESULT: message processing result. Win32 conform. -// -1 means: nothing processed, caller should continue as usual. - -LONG_PTR CMenuBar::processMsg(const UINT msg, const WPARAM wParam, const LPARAM lParam) -{ - if (msg == WM_NOTIFY) { - NMHDR *pNMHDR = (NMHDR*)lParam; - switch(pNMHDR->code) { - case NM_CUSTOMDRAW: - { - NMCUSTOMDRAW *nm = (NMCUSTOMDRAW*)lParam; - return customDrawWorker(nm); - } - - case TBN_DROPDOWN: - { - NMTOOLBAR *mtb = (NMTOOLBAR *)lParam; - return Handle(mtb); - } - case TBN_HOTITEMCHANGE: - { - NMTBHOTITEM *nmtb = (NMTBHOTITEM *)lParam; - if (nmtb->idNew != 0 && m_fTracking && nmtb->idNew != m_activeID && m_activeID != 0) { - cancel(0); - return 0; - } - else if (m_fTracking == true && m_activeID == 0 && nmtb->idNew != 0) { - invoke(nmtb->idNew); - return 0; - } - break; - } - - default: - return -1; - } - } - else if (msg == WM_LBUTTONDOWN) { - if (m_pContainer->dwFlags & CNT_NOTITLE) { - POINT pt; - ::GetCursorPos(&pt); - return ::SendMessage(m_pContainer->hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); - } - } - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// subclass the toolbar control to handle some keyboard events and improve -// keyboard navigation - -LRESULT CALLBACK CMenuBar::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CMenuBar *menuBar = reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); - - switch(msg) { - case WM_SYSKEYUP: - if (wParam == VK_MENU) { - menuBar->Cancel(); - return 0; - } - break; - } - return ::mir_callNextSubclass(hWnd, CMenuBar::wndProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Implements NM_CUSTOMDRAW for the toolbar -// -// @param nm NMCUSTOMDRAW *: sent via NM_CUSTOMDRAW message -// -// @return LONG_PTR: see Win32 NM_CUSTOMDRAW message. The function must return a valid -// message return value to indicate how Windows should continue with the drawing process. -// -// It may return zero in which case, the caller should allow default processing for -// the NM_CUSTOMDRAW message. - -LONG_PTR CMenuBar::customDrawWorker(NMCUSTOMDRAW *nm) -{ - bool fMustDraw = true; - - if (nm->hdr.hwndFrom == m_hwndToolbar) { - NMTBCUSTOMDRAW *nmtb = (NMTBCUSTOMDRAW *)(nm); - - switch(nmtb->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - if (fMustDraw) { - if (nmtb->nmcd.dwItemSpec == 0) { - m_hdcDraw = ::CreateCompatibleDC(nmtb->nmcd.hdc); - //m_rcItem = nmtb->nmcd.rc; - ::GetClientRect(m_hwndToolbar, &m_rcItem); - m_rcItem.bottom -= 4; - m_hbmDraw = CSkin::CreateAeroCompatibleBitmap(m_rcItem, nmtb->nmcd.hdc); - m_hbmOld = reinterpret_cast(::SelectObject(m_hdcDraw, m_hbmDraw)); - m_hTheme = M.isAero() || M.isVSThemed() ? OpenThemeData(m_hwndToolbar, L"REBAR") : 0; - m_hOldFont = reinterpret_cast(::SelectObject(m_hdcDraw, reinterpret_cast(::GetStockObject(DEFAULT_GUI_FONT)))); - if (m_isAero) { - nm->rc.bottom--; - CSkin::ApplyAeroEffect(m_hdcDraw, &m_rcItem, CSkin::AERO_EFFECT_AREA_MENUBAR); - nm->rc.bottom++; - } - else if ((PluginConfig.m_fillColor || M.isVSThemed()) && !CSkin::m_skinEnabled) { - if (PluginConfig.m_fillColor && PluginConfig.m_tbBackgroundHigh && PluginConfig.m_tbBackgroundLow) { - ::DrawAlpha(m_hdcDraw, &m_rcItem, PluginConfig.m_tbBackgroundHigh, 100, PluginConfig.m_tbBackgroundLow, 0, - GRADIENT_TB, 0, 0, 0); - } - else { - m_rcItem.bottom--; - if (PluginConfig.m_fillColor) - CSkin::FillBack(m_hdcDraw, &m_rcItem); - else if (M.isVSThemed()) - DrawThemeBackground(m_hTheme, m_hdcDraw, 6, 1, &m_rcItem, &m_rcItem); - else - FillRect(m_hdcDraw, &m_rcItem, GetSysColorBrush(COLOR_3DFACE)); - } - } - else if (CSkin::m_MenuBGBrush) - ::FillRect(m_hdcDraw, &nm->rc, CSkin::m_MenuBGBrush); - else - ::FillRect(m_hdcDraw, &nm->rc, GetSysColorBrush(COLOR_3DFACE)); - } - return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYPOSTERASE; - } - - m_hdcDraw = 0; - return CDRF_DODEFAULT; - - case CDDS_ITEMPREPAINT: - if (fMustDraw) { - TCHAR *szText = 0; - bool fDraw = true; - - int iIndex = idToIndex(nmtb->nmcd.dwItemSpec); - if (iIndex >= 0 && iIndex < NR_BUTTONS) - szText = (TCHAR*)m_TbButtons[iIndex].iString; - - UINT uState = nmtb->nmcd.uItemState; - - nmtb->nmcd.rc.bottom--; - if (CSkin::m_skinEnabled) { - CSkinItem *item = 0; - - ::FillRect(m_hdcDraw, &nmtb->nmcd.rc, CSkin::m_MenuBGBrush); - - if (uState & CDIS_MARKED || uState & CDIS_CHECKED || uState & CDIS_SELECTED) - item = &SkinItems[ID_EXTBKBUTTONSPRESSED]; - else if (uState & CDIS_HOT) - item = &SkinItems[ID_EXTBKBUTTONSMOUSEOVER]; - - if (item) - fDraw = !CSkin::DrawItem(m_hdcDraw, &nmtb->nmcd.rc, item); - else - fDraw = false; - } - if (fDraw) { - COLORREF clr = ::GetSysColor(COLOR_HOTLIGHT); - COLORREF clrRev = clr; - if (uState & CDIS_MARKED || uState & CDIS_CHECKED) - ::DrawAlpha(m_hdcDraw, &nmtb->nmcd.rc, clrRev, 80, clrRev, 0, 9, 31, 4, 0); - - if (uState & CDIS_SELECTED) - ::DrawAlpha(m_hdcDraw, &nmtb->nmcd.rc, clrRev, 80, clrRev, 0, 9, 31, 4, 0); - - if (uState & CDIS_HOT) - ::DrawAlpha(m_hdcDraw, &nmtb->nmcd.rc, clrRev, 80, clrRev, 0, 9, 31, 4, 0); - } - - if (szText) { - COLORREF clr = CSkin::m_skinEnabled ? CSkin::m_DefaultFontColor : - (PluginConfig.m_fillColor ? PluginConfig.m_genericTxtColor : - (uState & (CDIS_SELECTED | CDIS_HOT | CDIS_MARKED)) ? ::GetSysColor(COLOR_HIGHLIGHTTEXT) : ::GetSysColor(COLOR_BTNTEXT)); - - ::SetBkMode(m_hdcDraw, TRANSPARENT); - CSkin::RenderText(m_hdcDraw, m_hTheme, szText, &nmtb->nmcd.rc, DT_SINGLELINE | DT_VCENTER | DT_CENTER, CSkin::m_glowSize, clr); - } - if (iIndex == 0) - ::DrawIconEx(m_hdcDraw, (nmtb->nmcd.rc.left + nmtb->nmcd.rc.right) / 2 - 8, - (nmtb->nmcd.rc.top + nmtb->nmcd.rc.bottom) / 2 - 8, LoadSkinnedIcon(SKINICON_OTHER_MIRANDA), - 16, 16, 0, 0, DI_NORMAL); - - return CDRF_SKIPDEFAULT; - } - else return CDRF_DODEFAULT; - - case CDDS_PREERASE: - case CDDS_ITEMPOSTERASE: - case CDDS_ITEMPOSTPAINT: - case CDDS_ITEMPREERASE: - return fMustDraw ? CDRF_SKIPDEFAULT : CDRF_DODEFAULT; - - case CDDS_POSTERASE: - return fMustDraw ? CDRF_SKIPDEFAULT : CDRF_DODEFAULT; - - case CDDS_POSTPAINT: - if (nmtb->nmcd.dwItemSpec == 0 && m_hdcDraw) { - ::BitBlt(nmtb->nmcd.hdc, 0, 0, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, - m_hdcDraw, 0, 0, SRCCOPY); - ::SelectObject(m_hdcDraw, m_hbmOld); - ::DeleteObject(m_hbmDraw); - ::SelectObject(m_hdcDraw, m_hOldFont); - ::DeleteDC(m_hdcDraw); - m_hdcDraw = 0; - if (m_hTheme) - CloseThemeData(m_hTheme); - return CDRF_SKIPDEFAULT; - } - return CDRF_DODEFAULT; - - default: - return CDRF_DODEFAULT; - } - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Handle the TBN_DROPDOWN notification message sent by the -// toolbar control. -// -// @param nmtb NMTOOLBAR *: notification message structure -// -// @return LONG_PTR: must be a valid return value. See Win32 API, TBN_DROPDOWN - -LONG_PTR CMenuBar::Handle(const NMTOOLBAR *nmtb) -{ - if (nmtb->hdr.hwndFrom != m_hwndToolbar) - return TBDDRET_NODEFAULT; - - const int index = idToIndex(nmtb->iItem); - invoke(nmtb->iItem); - - return TBDDRET_DEFAULT; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Invoke the dropdown menu for the button with the given control id. -// -// @param id int: the control id of the toolbar button which has been activated - -void CMenuBar::invoke(const int id) -{ - const int index = idToIndex(id); - - HMENU hMenu; - - m_isContactMenu = m_isMainMenu = false; - - TWindowData *dat = (TWindowData*)GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA); - - MCONTACT hContact = dat ? dat->hContact : 0; - - if (index == 3 && hContact != 0) { - hMenu = reinterpret_cast(::CallService(MS_CLIST_MENUBUILDCONTACT, hContact, 0)); - m_isContactMenu = true; - } else if (index == 0) { - hMenu = reinterpret_cast(::CallService(MS_CLIST_MENUBUILDMAIN, 0, 0)); - m_isMainMenu = true; - } else - hMenu = reinterpret_cast(m_TbButtons[index].dwData); - - RECT rcButton; - POINT pt; - ::SendMessage(m_hwndToolbar, TB_GETITEMRECT, (WPARAM)index, (LPARAM)&rcButton); - pt.x = rcButton.left; - pt.y = rcButton.bottom; - ::ClientToScreen(m_hwndToolbar, &pt); - - if (m_activeID) - cancel(0); - - m_activeMenu = hMenu; - m_activeSubMenu = 0; - m_activeID = id; - updateState(hMenu); - obtainHook(); - m_fTracking = true; - ::SendMessage(m_hwndToolbar, TB_SETSTATE, (WPARAM)id, TBSTATE_CHECKED | TBSTATE_ENABLED); - ::TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, m_pContainer->hwnd, 0); -} - -void CMenuBar::cancel(const int id) -{ - releaseHook(); - if (m_activeID) - ::SendMessage(m_hwndToolbar, TB_SETSTATE, (WPARAM)m_activeID, TBSTATE_ENABLED); - m_activeID = 0; - m_activeMenu = 0; - m_isContactMenu = m_isMainMenu = false; - ::EndMenu(); -} - -void CMenuBar::Cancel(void) -{ - cancel(0); - m_fTracking = false; - autoShow(0); -} - -void CMenuBar::updateState(const HMENU hMenu) const -{ - TWindowData *dat = (TWindowData*)GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA); - if (dat) { - ::CheckMenuItem(hMenu, ID_VIEW_SHOWMENUBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOMENUBAR ? MF_UNCHECKED : MF_CHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_SHOWSTATUSBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOSTATUSBAR ? MF_UNCHECKED : MF_CHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_SHOWAVATAR, MF_BYCOMMAND | dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_SHOWTITLEBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOTITLE ? MF_UNCHECKED : MF_CHECKED); - - ::EnableMenuItem(hMenu, ID_VIEW_SHOWTITLEBAR, CSkin::m_skinEnabled && CSkin::m_frameSkins ? MF_GRAYED : MF_ENABLED); - - ::CheckMenuItem(hMenu, ID_VIEW_TABSATBOTTOM, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_TABSBOTTOM ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_VERTICALMAXIMIZE, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_VERTICALMAX ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_SHOWTOOLBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_HIDETOOLBAR ? MF_UNCHECKED : MF_CHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_BOTTOMTOOLBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_BOTTOMTOOLBAR ? MF_CHECKED : MF_UNCHECKED); - - ::CheckMenuItem(hMenu, ID_VIEW_SHOWMULTISENDCONTACTLIST, MF_BYCOMMAND | (dat->sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_VIEW_STAYONTOP, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_STICKY ? MF_CHECKED : MF_UNCHECKED); - - ::EnableMenuItem(hMenu, 2, MF_BYPOSITION | (nen_options.bWindowCheck ? MF_GRAYED : MF_ENABLED)); - ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_DISABLEALLEVENTPOPUPS, MF_BYCOMMAND | m_pContainer->dwFlags & (CNT_DONTREPORT | CNT_DONTREPORTUNFOCUSED | CNT_DONTREPORTFOCUSED | CNT_ALWAYSREPORTINACTIVE) ? MF_UNCHECKED : MF_CHECKED); - ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISMINIMIZED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_DONTREPORT ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSFORALLINACTIVESESSIONS, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_ALWAYSREPORTINACTIVE ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISUNFOCUSED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_DONTREPORTUNFOCUSED ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISFOCUSED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_DONTREPORTFOCUSED ? MF_CHECKED : MF_UNCHECKED); - - ::CheckMenuItem(hMenu, ID_WINDOWFLASHING_USEDEFAULTVALUES, MF_BYCOMMAND | (m_pContainer->dwFlags & (CNT_NOFLASH | CNT_FLASHALWAYS)) ? MF_UNCHECKED : MF_CHECKED); - ::CheckMenuItem(hMenu, ID_WINDOWFLASHING_DISABLEFLASHING, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOFLASH ? MF_CHECKED : MF_UNCHECKED); - ::CheckMenuItem(hMenu, ID_WINDOWFLASHING_FLASHUNTILFOCUSED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_FLASHALWAYS ? MF_CHECKED : MF_UNCHECKED); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// this updates the container menu bar and other window elements depending on the current -// child session (IM, chat etc.). It fully supports IEView and will disable/enable the -// message log menus depending on the configuration of IEView (e.g. when template mode -// is on, the message log settin menus have no functionality, thus can be disabled to -// improve ui feedback quality). - -void CMenuBar::configureMenu() const -{ - BOOL fDisable = FALSE; - - TWindowData *dat = (TWindowData*)::GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA); - if (dat) { - bool fChat = (dat->bType == SESSIONTYPE_CHAT); - - ::SendMessage(m_hwndToolbar, TB_SETSTATE, 103, fChat ? TBSTATE_HIDDEN : TBSTATE_ENABLED); - ::SendMessage(m_hwndToolbar, TB_SETSTATE, 104, fChat ? TBSTATE_ENABLED : TBSTATE_HIDDEN); - ::SendMessage(m_hwndToolbar, TB_SETSTATE, 105, fChat ? TBSTATE_HIDDEN : TBSTATE_ENABLED); - - if (dat->bType == SESSIONTYPE_IM) - ::EnableWindow(GetDlgItem(dat->hwnd, IDC_TIME), fDisable ? FALSE : TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Automatically shows or hides the menu bar. Depends on the current state, -// used when the ALT key is hit in the message window. - -void CMenuBar::autoShow(const int showcmd) -{ - if (m_mustAutoHide && !(m_pContainer->dwFlags & CNT_NOMENUBAR)) { - m_pContainer->dwFlags |= CNT_NOMENUBAR; - m_mustAutoHide = false; - ::SendMessage(m_pContainer->hwnd, WM_SIZE, 0, 1); - releaseHook(); - } - - if (showcmd == 0) { - ::SetFocus(m_pContainer->hwndActive); - return; - } - - if (m_pContainer->dwFlags & CNT_NOMENUBAR) { - m_mustAutoHide = true; - m_pContainer->dwFlags &= ~CNT_NOMENUBAR; - ::SendMessage(m_pContainer->hwnd, WM_SIZE, 0, 1); - } - else // do nothing, already visible - m_mustAutoHide = false; - - ::SetFocus(m_hwndToolbar); -} - -void CMenuBar::checkButtons() -{ - if (!m_buttonsInit) { - ::ZeroMemory(m_TbButtons, sizeof(m_TbButtons)); - - m_TbButtons[0].iBitmap = 0; - m_TbButtons[0].iString = 0; - m_TbButtons[0].fsState = TBSTATE_ENABLED; - m_TbButtons[0].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[0].idCommand = 100; - m_TbButtons[0].dwData = 0; - - m_TbButtons[1].iBitmap = I_IMAGENONE; - m_TbButtons[1].iString = (INT_PTR)TranslateT("&File"); - m_TbButtons[1].fsState = TBSTATE_ENABLED; - m_TbButtons[1].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[1].idCommand = 101; - m_TbButtons[1].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 0)); - - m_TbButtons[2].iBitmap = I_IMAGENONE; - m_TbButtons[2].iString = (INT_PTR)TranslateT("&View"); - m_TbButtons[2].fsState = TBSTATE_ENABLED; - m_TbButtons[2].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[2].idCommand = 102; - m_TbButtons[2].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 1)); - - m_TbButtons[3].iBitmap = I_IMAGENONE; - m_TbButtons[3].iString = (INT_PTR)TranslateT("&User"); - m_TbButtons[3].fsState = TBSTATE_ENABLED; - m_TbButtons[3].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[3].idCommand = 103; - m_TbButtons[3].dwData = 0; // dynamically built by Clist service - - m_TbButtons[4].iBitmap = I_IMAGENONE; - m_TbButtons[4].iString = (INT_PTR)TranslateT("&Room"); - m_TbButtons[4].fsState = TBSTATE_ENABLED; - m_TbButtons[4].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[4].idCommand = 104; - m_TbButtons[4].dwData = 0; - - m_TbButtons[5].iBitmap = I_IMAGENONE; - m_TbButtons[5].iString = (INT_PTR)TranslateT("Message &Log"); - m_TbButtons[5].fsState = TBSTATE_ENABLED; - m_TbButtons[5].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[5].idCommand = 105; - m_TbButtons[5].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 2)); - - m_TbButtons[6].iBitmap = I_IMAGENONE; - m_TbButtons[6].iString = (INT_PTR)TranslateT("&Container"); - m_TbButtons[6].fsState = TBSTATE_ENABLED; - m_TbButtons[6].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[6].idCommand = 106; - m_TbButtons[6].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 3)); - - m_TbButtons[7].iBitmap = I_IMAGENONE; - m_TbButtons[7].iString = (INT_PTR)TranslateT("Help"); - m_TbButtons[7].fsState = TBSTATE_ENABLED; - m_TbButtons[7].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; - m_TbButtons[7].idCommand = 107; - m_TbButtons[7].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 4)); - - m_buttonsInit = true; - } - - ::SendMessage(m_hwndToolbar, TB_ADDBUTTONS, SIZEOF(m_TbButtons), (LPARAM)m_TbButtons); - - m_size_y = HIWORD(::SendMessage(m_hwndToolbar, TB_GETBUTTONSIZE, 0, 0)); - - TBADDBITMAP tb; - tb.nID = (UINT_PTR)m_MimIcon; - tb.hInst = 0; - - ::SendMessage(m_hwndToolbar, TB_ADDBITMAP, 1, (LPARAM)&tb); -} - -void CMenuBar::resetLP() -{ - while ( SendMessage(m_hwndToolbar, TB_DELETEBUTTON, 0, 0)); - - m_buttonsInit = false; - checkButtons(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Message hook function, installed by the menu handler to support -// hot-tracking and keyboard navigation for the menu bar while a modal -// popup menu is active. -// -// Hook is only active while a (modal) popup menu is processed. -// -// @params See Win32, message hooks - -LRESULT CALLBACK CMenuBar::MessageHook(int nCode, WPARAM wParam, LPARAM lParam) -{ - MSG *pMsg = reinterpret_cast(lParam); - bool fCancel = false; - POINT pt; - - if (nCode == MSGF_MENU) { - switch(pMsg->message) { - case WM_KEYDOWN: - if (pMsg->wParam == VK_ESCAPE) - fCancel = true; - break; - - case WM_SYSKEYUP: - if (pMsg->wParam == VK_MENU) - fCancel = true; - break; - - case WM_LBUTTONDOWN: - ::GetCursorPos(&pt); - if (::MenuItemFromPoint(0, m_Owner->m_activeMenu, pt) >= 0) // inside menu - break; - if (m_Owner->m_activeSubMenu && ::MenuItemFromPoint(0, m_Owner->m_activeSubMenu, pt) >= 0) - break; - else { // anywhere else, cancel the menu - ::CallNextHookEx(m_hHook, nCode, wParam, lParam); - m_Owner->Cancel(); - return 0; - } - - // allow hottracking by the toolbar control - case WM_MOUSEMOVE: - ::GetCursorPos(&pt); - ::ScreenToClient(m_Owner->m_hwndToolbar, &pt); - LPARAM newPos = MAKELONG(pt.x, pt.y); - ::SendMessage(m_Owner->m_hwndToolbar, pMsg->message, pMsg->wParam, newPos); - break; - } - - // some key event requested to cancel the menu - if (fCancel) { - int iIndex = m_Owner->idToIndex(m_Owner->m_activeID); - if (iIndex != -1) - ::SendMessage(m_Owner->m_hwndToolbar, TB_SETHOTITEM, (WPARAM)iIndex, 0); - ::SetFocus(m_Owner->m_hwndToolbar); - ::SendMessage(m_Owner->m_hwndToolbar, TB_SETSTATE, (WPARAM)m_Owner->m_activeID, TBSTATE_ENABLED | TBSTATE_PRESSED); - m_Owner->cancel(0); - m_Owner->m_fTracking = false; - } - } - return ::CallNextHookEx(m_hHook, nCode, wParam, lParam); -} - -// window procedure for the status bar class. - -static int tooltip_active = FALSE; -static POINT ptMouse = {0}; -RECT rcLastStatusBarClick; // remembers click (down event) point for status bar clicks - -LONG_PTR CALLBACK StatusBarSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - TContainerData *pContainer = (TContainerData*)GetWindowLongPtr(GetParent(hWnd), GWLP_USERDATA); - POINT pt; - - if (OldStatusBarproc == 0) { - WNDCLASSEX wc = {0}; - wc.cbSize = sizeof(wc); - GetClassInfoEx(g_hInst, STATUSCLASSNAME, &wc); - OldStatusBarproc = wc.lpfnWndProc; - } - - switch (msg) { - case WM_CREATE: - LRESULT ret; - { - CREATESTRUCT *cs = (CREATESTRUCT *)lParam; - HWND hwndParent = GetParent(hWnd); - - // dirty trick to get rid of that annoying sizing gripper - SetWindowLongPtr(hwndParent, GWL_STYLE, GetWindowLongPtr(hwndParent, GWL_STYLE) & ~WS_THICKFRAME); - SetWindowLongPtr(hwndParent, GWL_EXSTYLE, GetWindowLongPtr(hwndParent, GWL_EXSTYLE) & ~WS_EX_APPWINDOW); - cs->style &= ~SBARS_SIZEGRIP; - ret = CallWindowProc(OldStatusBarproc, hWnd, msg, wParam, lParam); - SetWindowLongPtr(hwndParent, GWL_STYLE, GetWindowLongPtr(hwndParent, GWL_STYLE) | WS_THICKFRAME); - SetWindowLongPtr(hwndParent, GWL_EXSTYLE, GetWindowLongPtr(hwndParent, GWL_EXSTYLE) | WS_EX_APPWINDOW); - } - return ret; - - case WM_NCHITTEST: - RECT r; - { - LRESULT lr = SendMessage(GetParent(hWnd), WM_NCHITTEST, wParam, lParam); - int clip = CSkin::m_bClipBorder; - - GetWindowRect(hWnd, &r); - GetCursorPos(&pt); - if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 3) { - if (pt.x > r.right - clip - 4) - return HTBOTTOMRIGHT; - } - if (lr == HTLEFT || lr == HTRIGHT || lr == HTBOTTOM || lr == HTTOP || lr == HTTOPLEFT || lr == HTTOPRIGHT - || lr == HTBOTTOMLEFT || lr == HTBOTTOMRIGHT) - return HTTRANSPARENT; - } - break; - - case WM_ERASEBKGND: - return 1; - - case WM_PAINT: - PAINTSTRUCT ps; - { - HDC hdc = BeginPaint(hWnd, &ps); - int nParts = SendMessage(hWnd, SB_GETPARTS, 0, 0); - CSkinItem *item = &SkinItems[ID_EXTBKSTATUSBARPANEL]; - - BOOL bAero = M.isAero(); - HANDLE hTheme = bAero ? OpenThemeData(hWnd, L"ButtonStyle") : 0; - - TWindowData *dat = 0; - if (pContainer) - dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - - RECT rcClient; - GetClientRect(hWnd, &rcClient); - - HBITMAP hbm, hbmOld; - HANDLE hbp = 0; - HDC hdcMem; - if (CMimAPI::m_haveBufferedPaint) - hbp = CMimAPI::m_pfnBeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, NULL, &hdcMem); - else { - hdcMem = CreateCompatibleDC(hdc); - hbm = CSkin::CreateAeroCompatibleBitmap(rcClient, hdc); - hbmOld = (HBITMAP)SelectObject(hdcMem, hbm); - } - - SetBkMode(hdcMem, TRANSPARENT); - - COLORREF clr = CSkin::m_skinEnabled ? CSkin::m_DefaultFontColor : (PluginConfig.m_fillColor ? PluginConfig.m_genericTxtColor : GetSysColor(COLOR_BTNTEXT)); - - HFONT hFontOld = (HFONT)SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT)); - - if (pContainer && CSkin::m_skinEnabled) - CSkin::SkinDrawBG(hWnd, GetParent(hWnd), pContainer, &rcClient, hdcMem); - else if (bAero) { - FillRect(hdcMem, &rcClient, CSkin::m_BrushBack); - CSkin::ApplyAeroEffect(hdcMem, &rcClient, CSkin::AERO_EFFECT_AREA_STATUSBAR); - } - else { - CSkin::FillBack(hdcMem, &rcClient); - RECT rcFrame = rcClient; - if (PluginConfig.m_fillColor == 0) { - InflateRect(&rcFrame, -2, -1); - DrawEdge(hdcMem, &rcClient, BDR_RAISEDINNER | BDR_SUNKENOUTER, BF_RECT); - } - else { - CSkin::m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, 180); - CSkin::m_switchBarItem->Render(hdcMem, &rcFrame, true); - } - } - - for (int i = 0; i < nParts; i++) { - RECT itemRect; - SendMessage(hWnd, SB_GETRECT, (WPARAM)i, (LPARAM)&itemRect); - if (!item->IGNORED && !bAero && pContainer && CSkin::m_skinEnabled) - CSkin::DrawItem(hdcMem, &itemRect, item); - - if (i == 0) - itemRect.left += 2; - - // draw visual message length indicator in the leftmost status bar field - if (PluginConfig.m_visualMessageSizeIndicator && i == 0) { - if (dat && dat->bType == SESSIONTYPE_IM) { - HBRUSH br = CreateSolidBrush(RGB(0, 255, 0)); - HBRUSH brOld = (HBRUSH)SelectObject(hdcMem, br); - - RECT rc = itemRect; - rc.top = rc.bottom - 3; - rc.left = 0; - - if (!PluginConfig.m_autoSplit) { - float fMax = (float)dat->nMax; - float uPercent = (float)dat->textLen / ((fMax / (float)100.0) ? (fMax / (float)100.0) : (float)75.0); - float fx = ((float)rc.right / (float)100.0) * uPercent; - - rc.right = (LONG)fx; - FillRect(hdcMem, &rc, br); - } - else { - float baselen = (dat->textLen <= dat->nMax) ? (float)dat->textLen : (float)dat->nMax; - float fMax = (float)dat->nMax; - float uPercent = baselen / ((fMax / (float)100.0) ? (fMax / (float)100.0) : (float)75.0); - float fx; - LONG width = rc.right - rc.left; - if (dat->textLen >= dat->nMax) - rc.right = rc.right / 3; - fx = ((float)rc.right / (float)100.0) * uPercent; - rc.right = (LONG)fx; - FillRect(hdcMem, &rc, br); - if (dat->textLen >= dat->nMax) { - SelectObject(hdcMem, brOld); - DeleteObject(br); - br = CreateSolidBrush(RGB(255, 0, 0)); - brOld = (HBRUSH)SelectObject(hdcMem, br); - rc.left = width / 3; - rc.right = width; - uPercent = (float)dat->textLen / (float)200.0; - fx = ((float)(rc.right - rc.left) / (float)100.0) * uPercent; - rc.right = rc.left + (LONG)fx; - FillRect(hdcMem, &rc, br); - } - } - SelectObject(hdcMem, brOld); - DeleteObject(br); - } - } - - int height = itemRect.bottom - itemRect.top; - int width = itemRect.right - itemRect.left; - HICON hIcon = (HICON)SendMessage(hWnd, SB_GETICON, i, 0); - - TCHAR szText[1024]; szText[0] = 0; - LRESULT result = SendMessage(hWnd, SB_GETTEXT, i, (LPARAM)szText); - if (i == 2 && pContainer) { - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - if (dat) - DrawStatusIcons(dat, hdcMem, itemRect, 2); - } - else { - if (hIcon) { - if (LOWORD(result) > 1) { // we have a text - DrawIconEx(hdcMem, itemRect.left + 3, (height / 2 - 8) + itemRect.top, hIcon, 16, 16, 0, 0, DI_NORMAL); - if (dat) { - if (dat->bShowTyping == 2) - DrawIconEx(hdcMem, itemRect.left + 3, (height / 2 - 8) + itemRect.top, PluginConfig.g_iconOverlayEnabled, 16, 16, 0, 0, DI_NORMAL); - } - itemRect.left += 20; - CSkin::RenderText(hdcMem, hTheme, szText, &itemRect, DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX, - CSkin::m_glowSize, clr); - } - else DrawIconEx(hdcMem, itemRect.left + 3, (height / 2 - 8) + itemRect.top, hIcon, 16, 16, 0, 0, DI_NORMAL); - } - else { - itemRect.left += 2; - itemRect.right -= 2; - CSkin::RenderText(hdcMem, hTheme, szText, &itemRect, DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX, CSkin::m_glowSize, clr); - } - } - } - - if (hbp) - CSkin::FinalizeBufferedPaint(hbp, &rcClient); - else { - BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, hdcMem, 0, 0, SRCCOPY); - SelectObject(hdcMem, hbmOld); - DeleteObject(hbm); - SelectObject(hdcMem, hFontOld); - DeleteDC(hdcMem); - } - - if (hTheme) - CloseThemeData(hTheme); - - EndPaint(hWnd, &ps); - } - return 0; - - // tell status bar to update the part layout (re-calculate part widths) - // needed when an icon is added to or removed from the icon area - case WM_USER + 101: - { - int list_icons = 0; - TWindowData *dat = (TWindowData*)lParam; - if (dat) - while ( Srmm_GetNthIcon(dat->hContact, list_icons)) - list_icons++; - - SendMessage(hWnd, WM_SIZE, 0, 0); - - RECT rcs; - GetWindowRect(hWnd, &rcs); - - int statwidths[5]; - statwidths[0] = (rcs.right - rcs.left) - (2 * SB_CHAR_WIDTH + 20) - (list_icons * (PluginConfig.m_smcxicon + 2)); - statwidths[1] = (rcs.right - rcs.left) - (10 + (list_icons * (PluginConfig.m_smcxicon + 2))); - statwidths[2] = -1; - SendMessage(hWnd, SB_SETPARTS, 3, (LPARAM)statwidths); - } - return 0; - - case WM_SETCURSOR: - GetCursorPos(&pt); - SendMessage(GetParent(hWnd), msg, wParam, lParam); - if (pt.x == ptMouse.x && pt.y == ptMouse.y) - return 1; - - ptMouse = pt; - if (tooltip_active) { - KillTimer(hWnd, TIMERID_HOVER); - CallService("mToolTip/HideTip", 0, 0); - tooltip_active = FALSE; - } - KillTimer(hWnd, TIMERID_HOVER); - SetTimer(hWnd, TIMERID_HOVER, 450, 0); - break; - - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - KillTimer(hWnd, TIMERID_HOVER); - CallService("mToolTip/HideTip", 0, 0); - tooltip_active = FALSE; - GetCursorPos(&pt); - rcLastStatusBarClick.left = pt.x - 2; - rcLastStatusBarClick.right = pt.x + 2; - rcLastStatusBarClick.top = pt.y - 2; - rcLastStatusBarClick.bottom = pt.y + 2; - - if (pContainer->dwFlags & CNT_NOTITLE) { - POINT pt1 = pt; - ScreenToClient(hWnd, &pt1); - - RECT rcIconpart; - SendMessage(hWnd, SB_GETRECT, 2, (LPARAM)&rcIconpart); - if (!PtInRect(&rcIconpart, pt1)) - return SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); - } - break; - - case WM_TIMER: - if (wParam == TIMERID_HOVER) { - CLCINFOTIP ti = {0}; - ti.cbSize = sizeof(ti); - - KillTimer(hWnd, TIMERID_HOVER); - GetCursorPos(&pt); - if (pt.x == ptMouse.x && pt.y == ptMouse.y) { - RECT rc; - TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); - SIZE size; - TCHAR wBuf[512]; wBuf[0] = 0; - ti.ptCursor = pt; - ScreenToClient(hWnd, &pt); - SendMessage(hWnd, SB_GETRECT, 2, (LPARAM)&rc); - if (dat && PtInRect(&rc, pt)) { - unsigned int iconNum = (pt.x - rc.left) / (PluginConfig.m_smcxicon + 2); - StatusIconData *sid = Srmm_GetNthIcon(dat->hContact, iconNum); - if (sid == NULL) - break; - - if ( !strcmp(sid->szModule, MSG_ICON_MODULE)) { - if (sid->dwId == MSG_ICON_SOUND && pContainer) - mir_sntprintf(wBuf, SIZEOF(wBuf), TranslateT("Sounds are %s. Click to toggle status, hold SHIFT and click to set for all open containers"), - pContainer->dwFlags & CNT_NOSOUND ? TranslateT("disabled") : TranslateT("enabled")); - - else if (sid->dwId == MSG_ICON_UTN && dat && (dat->bType == SESSIONTYPE_IM || dat->si->iType == GCW_PRIVMESS)) { - int mtnStatus = db_get_b(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, M.GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW)); - mir_sntprintf(wBuf, SIZEOF(wBuf), TranslateT("Sending typing notifications is %s."), - mtnStatus ? TranslateT("enabled") : TranslateT("disabled")); - } - else if (sid->dwId == MSG_ICON_SESSION) - mir_sntprintf(wBuf, SIZEOF(wBuf), _T("%s"), TranslateT("Session list.\nClick left for a list of open sessions.\nClick right to access favorites and quickly configure message window behavior")); - } - else if (sid->tszTooltip) - _tcsncpy(wBuf, sid->tszTooltip, SIZEOF(wBuf)); - - if (wBuf[0]) { - CallService("mToolTip/ShowTipW", (WPARAM)wBuf, (LPARAM)&ti); - tooltip_active = TRUE; - } - } - SendMessage(hWnd, SB_GETRECT, 1, (LPARAM)&rc); - if (dat && PtInRect(&rc, pt)) { - int iLength = 0; - GETTEXTLENGTHEX gtxl = {0}; - int iQueued = db_get_dw(dat->hContact, "SendLater", "count", 0); - gtxl.codepage = CP_UTF8; - gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES; - iLength = SendDlgItemMessage(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); - tooltip_active = TRUE; - - const TCHAR *szFormat = TranslateT("There are %d pending send jobs. Message length: %d bytes, message length limit: %d bytes\n\n%d messages are queued for later delivery"); - - mir_sntprintf(wBuf, SIZEOF(wBuf), szFormat, dat->iOpenJobs, iLength, dat->nMax ? dat->nMax : 20000, iQueued); - CallService("mToolTip/ShowTipW", (WPARAM)wBuf, (LPARAM)&ti); - } - - if (SendMessage(dat->pContainer->hwndStatus, SB_GETTEXT, 0, (LPARAM)wBuf)) { - HDC hdc; - int iLen=SendMessage(dat->pContainer->hwndStatus,SB_GETTEXTLENGTH,0,0); - SendMessage(hWnd, SB_GETRECT, 0, (LPARAM)&rc); - GetTextExtentPoint32( hdc=GetDC( dat->pContainer->hwndStatus), wBuf, iLen, &size ); - ReleaseDC (dat->pContainer->hwndStatus,hdc); - - if (dat && PtInRect(&rc,pt)&&((rc.right-rc.left)bType == SESSIONTYPE_CHAT) - db_get_ts(dat->hContact,dat->szProto,"Topic",&dbv); - - tooltip_active = TRUE; - CallService("mToolTip/ShowTipW", (WPARAM)dbv.ptszVal, (LPARAM)&ti); - if (dbv.pszVal) - db_free(&dbv); - } - } - } - } - break; - - case WM_DESTROY: - KillTimer(hWnd, TIMERID_HOVER); - } - return CallWindowProc(OldStatusBarproc, hWnd, msg, wParam, lParam); -} +/* + * Miranda NG: the free IM client for Microsoft* Windows* + * + * 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 + * + * Menu and status bar control(s) for the container window. + * + */ + +#include "commonheaders.h" + +static WNDPROC OldStatusBarproc = 0; + +bool CMenuBar::m_buttonsInit = false; +HHOOK CMenuBar::m_hHook = 0; +TBBUTTON CMenuBar::m_TbButtons[8] = {0}; +CMenuBar *CMenuBar::m_Owner = 0; +HBITMAP CMenuBar::m_MimIcon = 0; +int CMenuBar::m_MimIconRefCount = 0; + +static int resetLP(WPARAM, LPARAM, LPARAM obj) +{ + ((CMenuBar*)obj)->resetLP(); + return 0; +} + +CMenuBar::CMenuBar(HWND hwndParent, const TContainerData *pContainer) +{ + m_pContainer = const_cast(pContainer); + + if (m_MimIcon == 0) { + HDC hdc = ::GetDC(m_pContainer->hwnd); + HANDLE hIcon = LoadSkinnedIconHandle(SKINICON_OTHER_MIRANDA); + + HDC hdcTemp = ::CreateCompatibleDC(hdc); + + RECT rc = {0,0,16,16}; + m_MimIcon = CSkin::CreateAeroCompatibleBitmap(rc, hdcTemp); + HBITMAP hbmOld = reinterpret_cast(::SelectObject(hdcTemp, m_MimIcon)); + ::DrawIconEx(hdcTemp, 0, 0, (HICON)hIcon, 16, 16, 0, 0, DI_NORMAL); + ::SelectObject(hdcTemp, hbmOld); + + ::DeleteDC(hdcTemp); + ::ReleaseDC(m_pContainer->hwnd, hdc); + } + + m_MimIconRefCount++; + + m_hwndToolbar = ::CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL, WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_VISIBLE|TBSTYLE_FLAT|TBSTYLE_TRANSPARENT|TBSTYLE_LIST|/*CCS_NOPARENTALIGN|*/CCS_NODIVIDER|CCS_TOP, + 0, 0, 0, 0, hwndParent, NULL, g_hInst, NULL); + + ::SendMessage(m_hwndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); + + checkButtons(); + + m_activeMenu = 0; + m_activeID = 0; + m_isAero = M.isAero(); + m_mustAutoHide = false; + m_activeSubMenu = 0; + m_fTracking = false; + m_isContactMenu = m_isMainMenu = false; + HookEventParam(ME_LANGPACK_CHANGED, &::resetLP, (LPARAM)this); + + ::SetWindowLongPtr(m_hwndToolbar, GWLP_USERDATA, (LONG_PTR)this); + mir_subclassWindow(m_hwndToolbar, wndProc); +} + +CMenuBar::~CMenuBar() +{ + ::SetWindowLongPtr(m_hwndToolbar, GWLP_USERDATA, 0); + ::DestroyWindow(m_hwndToolbar); + releaseHook(); + m_MimIconRefCount--; + if (m_MimIconRefCount == 0) { + ::DeleteObject(m_MimIcon); + m_MimIcon = 0; + } +} + +/** + * retrieves the client rectangle for the rebar control. This must be + * called once per WM_SIZE event by the parent window. getHeight() depends on it. + * + * @return RECT&: client rectangle of the rebar control + */ +const RECT& CMenuBar::getClientRect() +{ + ::GetClientRect(m_hwndToolbar, &m_rcClient); + return(m_rcClient); +} + +void CMenuBar::obtainHook() +{ + releaseHook(); + if (m_hHook == 0) + m_hHook = ::SetWindowsHookEx(WH_MSGFILTER, CMenuBar::MessageHook, 0, GetCurrentThreadId()); + m_Owner = this; +} + +void CMenuBar::releaseHook() +{ + if (m_hHook) { + ::UnhookWindowsHookEx(m_hHook); + m_hHook = 0; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Retrieve the height of the rebar control +// +// @return LONG: height of the rebar, in pixels + +LONG CMenuBar::getHeight() const +{ + return((m_pContainer->dwFlags & CNT_NOMENUBAR) ? 0 : m_size_y); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// process all relevant messages. Must be called by the parent window's +// window procedure. +// +// @param msg +// @param wParam +// @param lParam +// +// @return LRESULT: message processing result. Win32 conform. +// -1 means: nothing processed, caller should continue as usual. + +LONG_PTR CMenuBar::processMsg(const UINT msg, const WPARAM wParam, const LPARAM lParam) +{ + if (msg == WM_NOTIFY) { + NMHDR *pNMHDR = (NMHDR*)lParam; + switch(pNMHDR->code) { + case NM_CUSTOMDRAW: + { + NMCUSTOMDRAW *nm = (NMCUSTOMDRAW*)lParam; + return customDrawWorker(nm); + } + + case TBN_DROPDOWN: + { + NMTOOLBAR *mtb = (NMTOOLBAR *)lParam; + return Handle(mtb); + } + case TBN_HOTITEMCHANGE: + { + NMTBHOTITEM *nmtb = (NMTBHOTITEM *)lParam; + if (nmtb->idNew != 0 && m_fTracking && nmtb->idNew != m_activeID && m_activeID != 0) { + cancel(0); + return 0; + } + else if (m_fTracking == true && m_activeID == 0 && nmtb->idNew != 0) { + invoke(nmtb->idNew); + return 0; + } + break; + } + + default: + return -1; + } + } + else if (msg == WM_LBUTTONDOWN) { + if (m_pContainer->dwFlags & CNT_NOTITLE) { + POINT pt; + ::GetCursorPos(&pt); + return ::SendMessage(m_pContainer->hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); + } + } + return -1; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclass the toolbar control to handle some keyboard events and improve +// keyboard navigation + +LRESULT CALLBACK CMenuBar::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CMenuBar *menuBar = reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); + + switch(msg) { + case WM_SYSKEYUP: + if (wParam == VK_MENU) { + menuBar->Cancel(); + return 0; + } + break; + } + return ::mir_callNextSubclass(hWnd, CMenuBar::wndProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Implements NM_CUSTOMDRAW for the toolbar +// +// @param nm NMCUSTOMDRAW *: sent via NM_CUSTOMDRAW message +// +// @return LONG_PTR: see Win32 NM_CUSTOMDRAW message. The function must return a valid +// message return value to indicate how Windows should continue with the drawing process. +// +// It may return zero in which case, the caller should allow default processing for +// the NM_CUSTOMDRAW message. + +LONG_PTR CMenuBar::customDrawWorker(NMCUSTOMDRAW *nm) +{ + bool fMustDraw = true; + + if (nm->hdr.hwndFrom == m_hwndToolbar) { + NMTBCUSTOMDRAW *nmtb = (NMTBCUSTOMDRAW *)(nm); + + switch(nmtb->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + if (fMustDraw) { + if (nmtb->nmcd.dwItemSpec == 0) { + m_hdcDraw = ::CreateCompatibleDC(nmtb->nmcd.hdc); + //m_rcItem = nmtb->nmcd.rc; + ::GetClientRect(m_hwndToolbar, &m_rcItem); + m_rcItem.bottom -= 4; + m_hbmDraw = CSkin::CreateAeroCompatibleBitmap(m_rcItem, nmtb->nmcd.hdc); + m_hbmOld = reinterpret_cast(::SelectObject(m_hdcDraw, m_hbmDraw)); + m_hTheme = M.isAero() || M.isVSThemed() ? OpenThemeData(m_hwndToolbar, L"REBAR") : 0; + m_hOldFont = reinterpret_cast(::SelectObject(m_hdcDraw, reinterpret_cast(::GetStockObject(DEFAULT_GUI_FONT)))); + if (m_isAero) { + nm->rc.bottom--; + CSkin::ApplyAeroEffect(m_hdcDraw, &m_rcItem, CSkin::AERO_EFFECT_AREA_MENUBAR); + nm->rc.bottom++; + } + else if ((PluginConfig.m_fillColor || M.isVSThemed()) && !CSkin::m_skinEnabled) { + if (PluginConfig.m_fillColor && PluginConfig.m_tbBackgroundHigh && PluginConfig.m_tbBackgroundLow) { + ::DrawAlpha(m_hdcDraw, &m_rcItem, PluginConfig.m_tbBackgroundHigh, 100, PluginConfig.m_tbBackgroundLow, 0, + GRADIENT_TB, 0, 0, 0); + } + else { + m_rcItem.bottom--; + if (PluginConfig.m_fillColor) + CSkin::FillBack(m_hdcDraw, &m_rcItem); + else if (M.isVSThemed()) + DrawThemeBackground(m_hTheme, m_hdcDraw, 6, 1, &m_rcItem, &m_rcItem); + else + FillRect(m_hdcDraw, &m_rcItem, GetSysColorBrush(COLOR_3DFACE)); + } + } + else if (CSkin::m_MenuBGBrush) + ::FillRect(m_hdcDraw, &nm->rc, CSkin::m_MenuBGBrush); + else + ::FillRect(m_hdcDraw, &nm->rc, GetSysColorBrush(COLOR_3DFACE)); + } + return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT | CDRF_NOTIFYPOSTERASE; + } + + m_hdcDraw = 0; + return CDRF_DODEFAULT; + + case CDDS_ITEMPREPAINT: + if (fMustDraw) { + TCHAR *szText = 0; + bool fDraw = true; + + int iIndex = idToIndex(nmtb->nmcd.dwItemSpec); + if (iIndex >= 0 && iIndex < NR_BUTTONS) + szText = (TCHAR*)m_TbButtons[iIndex].iString; + + UINT uState = nmtb->nmcd.uItemState; + + nmtb->nmcd.rc.bottom--; + if (CSkin::m_skinEnabled) { + CSkinItem *item = 0; + + ::FillRect(m_hdcDraw, &nmtb->nmcd.rc, CSkin::m_MenuBGBrush); + + if (uState & CDIS_MARKED || uState & CDIS_CHECKED || uState & CDIS_SELECTED) + item = &SkinItems[ID_EXTBKBUTTONSPRESSED]; + else if (uState & CDIS_HOT) + item = &SkinItems[ID_EXTBKBUTTONSMOUSEOVER]; + + if (item) + fDraw = !CSkin::DrawItem(m_hdcDraw, &nmtb->nmcd.rc, item); + else + fDraw = false; + } + if (fDraw) { + COLORREF clr = ::GetSysColor(COLOR_HOTLIGHT); + COLORREF clrRev = clr; + if (uState & CDIS_MARKED || uState & CDIS_CHECKED) + ::DrawAlpha(m_hdcDraw, &nmtb->nmcd.rc, clrRev, 80, clrRev, 0, 9, 31, 4, 0); + + if (uState & CDIS_SELECTED) + ::DrawAlpha(m_hdcDraw, &nmtb->nmcd.rc, clrRev, 80, clrRev, 0, 9, 31, 4, 0); + + if (uState & CDIS_HOT) + ::DrawAlpha(m_hdcDraw, &nmtb->nmcd.rc, clrRev, 80, clrRev, 0, 9, 31, 4, 0); + } + + if (szText) { + COLORREF clr = CSkin::m_skinEnabled ? CSkin::m_DefaultFontColor : + (PluginConfig.m_fillColor ? PluginConfig.m_genericTxtColor : + (uState & (CDIS_SELECTED | CDIS_HOT | CDIS_MARKED)) ? ::GetSysColor(COLOR_HIGHLIGHTTEXT) : ::GetSysColor(COLOR_BTNTEXT)); + + ::SetBkMode(m_hdcDraw, TRANSPARENT); + CSkin::RenderText(m_hdcDraw, m_hTheme, szText, &nmtb->nmcd.rc, DT_SINGLELINE | DT_VCENTER | DT_CENTER, CSkin::m_glowSize, clr); + } + if (iIndex == 0) + ::DrawIconEx(m_hdcDraw, (nmtb->nmcd.rc.left + nmtb->nmcd.rc.right) / 2 - 8, + (nmtb->nmcd.rc.top + nmtb->nmcd.rc.bottom) / 2 - 8, LoadSkinnedIcon(SKINICON_OTHER_MIRANDA), + 16, 16, 0, 0, DI_NORMAL); + + return CDRF_SKIPDEFAULT; + } + else return CDRF_DODEFAULT; + + case CDDS_PREERASE: + case CDDS_ITEMPOSTERASE: + case CDDS_ITEMPOSTPAINT: + case CDDS_ITEMPREERASE: + return fMustDraw ? CDRF_SKIPDEFAULT : CDRF_DODEFAULT; + + case CDDS_POSTERASE: + return fMustDraw ? CDRF_SKIPDEFAULT : CDRF_DODEFAULT; + + case CDDS_POSTPAINT: + if (nmtb->nmcd.dwItemSpec == 0 && m_hdcDraw) { + ::BitBlt(nmtb->nmcd.hdc, 0, 0, m_rcItem.right - m_rcItem.left, m_rcItem.bottom - m_rcItem.top, + m_hdcDraw, 0, 0, SRCCOPY); + ::SelectObject(m_hdcDraw, m_hbmOld); + ::DeleteObject(m_hbmDraw); + ::SelectObject(m_hdcDraw, m_hOldFont); + ::DeleteDC(m_hdcDraw); + m_hdcDraw = 0; + if (m_hTheme) + CloseThemeData(m_hTheme); + return CDRF_SKIPDEFAULT; + } + return CDRF_DODEFAULT; + + default: + return CDRF_DODEFAULT; + } + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Handle the TBN_DROPDOWN notification message sent by the +// toolbar control. +// +// @param nmtb NMTOOLBAR *: notification message structure +// +// @return LONG_PTR: must be a valid return value. See Win32 API, TBN_DROPDOWN + +LONG_PTR CMenuBar::Handle(const NMTOOLBAR *nmtb) +{ + if (nmtb->hdr.hwndFrom != m_hwndToolbar) + return TBDDRET_NODEFAULT; + + const int index = idToIndex(nmtb->iItem); + invoke(nmtb->iItem); + + return TBDDRET_DEFAULT; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Invoke the dropdown menu for the button with the given control id. +// +// @param id int: the control id of the toolbar button which has been activated + +void CMenuBar::invoke(const int id) +{ + const int index = idToIndex(id); + + HMENU hMenu; + + m_isContactMenu = m_isMainMenu = false; + + TWindowData *dat = (TWindowData*)GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA); + + MCONTACT hContact = dat ? dat->hContact : 0; + + if (index == 3 && hContact != 0) { + hMenu = reinterpret_cast(::CallService(MS_CLIST_MENUBUILDCONTACT, hContact, 0)); + m_isContactMenu = true; + } else if (index == 0) { + hMenu = reinterpret_cast(::CallService(MS_CLIST_MENUBUILDMAIN, 0, 0)); + m_isMainMenu = true; + } else + hMenu = reinterpret_cast(m_TbButtons[index].dwData); + + RECT rcButton; + POINT pt; + ::SendMessage(m_hwndToolbar, TB_GETITEMRECT, (WPARAM)index, (LPARAM)&rcButton); + pt.x = rcButton.left; + pt.y = rcButton.bottom; + ::ClientToScreen(m_hwndToolbar, &pt); + + if (m_activeID) + cancel(0); + + m_activeMenu = hMenu; + m_activeSubMenu = 0; + m_activeID = id; + updateState(hMenu); + obtainHook(); + m_fTracking = true; + ::SendMessage(m_hwndToolbar, TB_SETSTATE, (WPARAM)id, TBSTATE_CHECKED | TBSTATE_ENABLED); + ::TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, m_pContainer->hwnd, 0); +} + +void CMenuBar::cancel(const int id) +{ + releaseHook(); + if (m_activeID) + ::SendMessage(m_hwndToolbar, TB_SETSTATE, (WPARAM)m_activeID, TBSTATE_ENABLED); + m_activeID = 0; + m_activeMenu = 0; + m_isContactMenu = m_isMainMenu = false; + ::EndMenu(); +} + +void CMenuBar::Cancel(void) +{ + cancel(0); + m_fTracking = false; + autoShow(0); +} + +void CMenuBar::updateState(const HMENU hMenu) const +{ + TWindowData *dat = (TWindowData*)GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA); + if (dat) { + ::CheckMenuItem(hMenu, ID_VIEW_SHOWMENUBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOMENUBAR ? MF_UNCHECKED : MF_CHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_SHOWSTATUSBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOSTATUSBAR ? MF_UNCHECKED : MF_CHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_SHOWAVATAR, MF_BYCOMMAND | dat->bShowAvatar ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_SHOWTITLEBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOTITLE ? MF_UNCHECKED : MF_CHECKED); + + ::EnableMenuItem(hMenu, ID_VIEW_SHOWTITLEBAR, CSkin::m_skinEnabled && CSkin::m_frameSkins ? MF_GRAYED : MF_ENABLED); + + ::CheckMenuItem(hMenu, ID_VIEW_TABSATBOTTOM, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_TABSBOTTOM ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_VERTICALMAXIMIZE, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_VERTICALMAX ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_SHOWTOOLBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_HIDETOOLBAR ? MF_UNCHECKED : MF_CHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_BOTTOMTOOLBAR, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_BOTTOMTOOLBAR ? MF_CHECKED : MF_UNCHECKED); + + ::CheckMenuItem(hMenu, ID_VIEW_SHOWMULTISENDCONTACTLIST, MF_BYCOMMAND | (dat->sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_VIEW_STAYONTOP, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_STICKY ? MF_CHECKED : MF_UNCHECKED); + + ::EnableMenuItem(hMenu, 2, MF_BYPOSITION | (nen_options.bWindowCheck ? MF_GRAYED : MF_ENABLED)); + ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_DISABLEALLEVENTPOPUPS, MF_BYCOMMAND | m_pContainer->dwFlags & (CNT_DONTREPORT | CNT_DONTREPORTUNFOCUSED | CNT_DONTREPORTFOCUSED | CNT_ALWAYSREPORTINACTIVE) ? MF_UNCHECKED : MF_CHECKED); + ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISMINIMIZED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_DONTREPORT ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSFORALLINACTIVESESSIONS, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_ALWAYSREPORTINACTIVE ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISUNFOCUSED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_DONTREPORTUNFOCUSED ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISFOCUSED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_DONTREPORTFOCUSED ? MF_CHECKED : MF_UNCHECKED); + + ::CheckMenuItem(hMenu, ID_WINDOWFLASHING_USEDEFAULTVALUES, MF_BYCOMMAND | (m_pContainer->dwFlags & (CNT_NOFLASH | CNT_FLASHALWAYS)) ? MF_UNCHECKED : MF_CHECKED); + ::CheckMenuItem(hMenu, ID_WINDOWFLASHING_DISABLEFLASHING, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_NOFLASH ? MF_CHECKED : MF_UNCHECKED); + ::CheckMenuItem(hMenu, ID_WINDOWFLASHING_FLASHUNTILFOCUSED, MF_BYCOMMAND | m_pContainer->dwFlags & CNT_FLASHALWAYS ? MF_CHECKED : MF_UNCHECKED); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// this updates the container menu bar and other window elements depending on the current +// child session (IM, chat etc.). It fully supports IEView and will disable/enable the +// message log menus depending on the configuration of IEView (e.g. when template mode +// is on, the message log settin menus have no functionality, thus can be disabled to +// improve ui feedback quality). + +void CMenuBar::configureMenu() const +{ + BOOL fDisable = FALSE; + + TWindowData *dat = (TWindowData*)::GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA); + if (dat) { + bool fChat = (dat->bType == SESSIONTYPE_CHAT); + + ::SendMessage(m_hwndToolbar, TB_SETSTATE, 103, fChat ? TBSTATE_HIDDEN : TBSTATE_ENABLED); + ::SendMessage(m_hwndToolbar, TB_SETSTATE, 104, fChat ? TBSTATE_ENABLED : TBSTATE_HIDDEN); + ::SendMessage(m_hwndToolbar, TB_SETSTATE, 105, fChat ? TBSTATE_HIDDEN : TBSTATE_ENABLED); + + if (dat->bType == SESSIONTYPE_IM) + ::EnableWindow(GetDlgItem(dat->hwnd, IDC_TIME), fDisable ? FALSE : TRUE); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Automatically shows or hides the menu bar. Depends on the current state, +// used when the ALT key is hit in the message window. + +void CMenuBar::autoShow(const int showcmd) +{ + if (m_mustAutoHide && !(m_pContainer->dwFlags & CNT_NOMENUBAR)) { + m_pContainer->dwFlags |= CNT_NOMENUBAR; + m_mustAutoHide = false; + ::SendMessage(m_pContainer->hwnd, WM_SIZE, 0, 1); + releaseHook(); + } + + if (showcmd == 0) { + ::SetFocus(m_pContainer->hwndActive); + return; + } + + if (m_pContainer->dwFlags & CNT_NOMENUBAR) { + m_mustAutoHide = true; + m_pContainer->dwFlags &= ~CNT_NOMENUBAR; + ::SendMessage(m_pContainer->hwnd, WM_SIZE, 0, 1); + } + else // do nothing, already visible + m_mustAutoHide = false; + + ::SetFocus(m_hwndToolbar); +} + +void CMenuBar::checkButtons() +{ + if (!m_buttonsInit) { + ::ZeroMemory(m_TbButtons, sizeof(m_TbButtons)); + + m_TbButtons[0].iBitmap = 0; + m_TbButtons[0].iString = 0; + m_TbButtons[0].fsState = TBSTATE_ENABLED; + m_TbButtons[0].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[0].idCommand = 100; + m_TbButtons[0].dwData = 0; + + m_TbButtons[1].iBitmap = I_IMAGENONE; + m_TbButtons[1].iString = (INT_PTR)TranslateT("&File"); + m_TbButtons[1].fsState = TBSTATE_ENABLED; + m_TbButtons[1].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[1].idCommand = 101; + m_TbButtons[1].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 0)); + + m_TbButtons[2].iBitmap = I_IMAGENONE; + m_TbButtons[2].iString = (INT_PTR)TranslateT("&View"); + m_TbButtons[2].fsState = TBSTATE_ENABLED; + m_TbButtons[2].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[2].idCommand = 102; + m_TbButtons[2].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 1)); + + m_TbButtons[3].iBitmap = I_IMAGENONE; + m_TbButtons[3].iString = (INT_PTR)TranslateT("&User"); + m_TbButtons[3].fsState = TBSTATE_ENABLED; + m_TbButtons[3].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[3].idCommand = 103; + m_TbButtons[3].dwData = 0; // dynamically built by Clist service + + m_TbButtons[4].iBitmap = I_IMAGENONE; + m_TbButtons[4].iString = (INT_PTR)TranslateT("&Room"); + m_TbButtons[4].fsState = TBSTATE_ENABLED; + m_TbButtons[4].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[4].idCommand = 104; + m_TbButtons[4].dwData = 0; + + m_TbButtons[5].iBitmap = I_IMAGENONE; + m_TbButtons[5].iString = (INT_PTR)TranslateT("Message &Log"); + m_TbButtons[5].fsState = TBSTATE_ENABLED; + m_TbButtons[5].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[5].idCommand = 105; + m_TbButtons[5].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 2)); + + m_TbButtons[6].iBitmap = I_IMAGENONE; + m_TbButtons[6].iString = (INT_PTR)TranslateT("&Container"); + m_TbButtons[6].fsState = TBSTATE_ENABLED; + m_TbButtons[6].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[6].idCommand = 106; + m_TbButtons[6].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 3)); + + m_TbButtons[7].iBitmap = I_IMAGENONE; + m_TbButtons[7].iString = (INT_PTR)TranslateT("Help"); + m_TbButtons[7].fsState = TBSTATE_ENABLED; + m_TbButtons[7].fsStyle = BTNS_DROPDOWN|BTNS_AUTOSIZE; + m_TbButtons[7].idCommand = 107; + m_TbButtons[7].dwData = reinterpret_cast(::GetSubMenu(PluginConfig.getMenuBar(), 4)); + + m_buttonsInit = true; + } + + ::SendMessage(m_hwndToolbar, TB_ADDBUTTONS, SIZEOF(m_TbButtons), (LPARAM)m_TbButtons); + + m_size_y = HIWORD(::SendMessage(m_hwndToolbar, TB_GETBUTTONSIZE, 0, 0)); + + TBADDBITMAP tb; + tb.nID = (UINT_PTR)m_MimIcon; + tb.hInst = 0; + + ::SendMessage(m_hwndToolbar, TB_ADDBITMAP, 1, (LPARAM)&tb); +} + +void CMenuBar::resetLP() +{ + while ( SendMessage(m_hwndToolbar, TB_DELETEBUTTON, 0, 0)); + + m_buttonsInit = false; + checkButtons(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Message hook function, installed by the menu handler to support +// hot-tracking and keyboard navigation for the menu bar while a modal +// popup menu is active. +// +// Hook is only active while a (modal) popup menu is processed. +// +// @params See Win32, message hooks + +LRESULT CALLBACK CMenuBar::MessageHook(int nCode, WPARAM wParam, LPARAM lParam) +{ + MSG *pMsg = reinterpret_cast(lParam); + bool fCancel = false; + POINT pt; + + if (nCode == MSGF_MENU) { + switch(pMsg->message) { + case WM_KEYDOWN: + if (pMsg->wParam == VK_ESCAPE) + fCancel = true; + break; + + case WM_SYSKEYUP: + if (pMsg->wParam == VK_MENU) + fCancel = true; + break; + + case WM_LBUTTONDOWN: + ::GetCursorPos(&pt); + if (::MenuItemFromPoint(0, m_Owner->m_activeMenu, pt) >= 0) // inside menu + break; + if (m_Owner->m_activeSubMenu && ::MenuItemFromPoint(0, m_Owner->m_activeSubMenu, pt) >= 0) + break; + else { // anywhere else, cancel the menu + ::CallNextHookEx(m_hHook, nCode, wParam, lParam); + m_Owner->Cancel(); + return 0; + } + + // allow hottracking by the toolbar control + case WM_MOUSEMOVE: + ::GetCursorPos(&pt); + ::ScreenToClient(m_Owner->m_hwndToolbar, &pt); + LPARAM newPos = MAKELONG(pt.x, pt.y); + ::SendMessage(m_Owner->m_hwndToolbar, pMsg->message, pMsg->wParam, newPos); + break; + } + + // some key event requested to cancel the menu + if (fCancel) { + int iIndex = m_Owner->idToIndex(m_Owner->m_activeID); + if (iIndex != -1) + ::SendMessage(m_Owner->m_hwndToolbar, TB_SETHOTITEM, (WPARAM)iIndex, 0); + ::SetFocus(m_Owner->m_hwndToolbar); + ::SendMessage(m_Owner->m_hwndToolbar, TB_SETSTATE, (WPARAM)m_Owner->m_activeID, TBSTATE_ENABLED | TBSTATE_PRESSED); + m_Owner->cancel(0); + m_Owner->m_fTracking = false; + } + } + return ::CallNextHookEx(m_hHook, nCode, wParam, lParam); +} + +// window procedure for the status bar class. + +static int tooltip_active = FALSE; +static POINT ptMouse = {0}; +RECT rcLastStatusBarClick; // remembers click (down event) point for status bar clicks + +LONG_PTR CALLBACK StatusBarSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TContainerData *pContainer = (TContainerData*)GetWindowLongPtr(GetParent(hWnd), GWLP_USERDATA); + POINT pt; + + if (OldStatusBarproc == 0) { + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + GetClassInfoEx(g_hInst, STATUSCLASSNAME, &wc); + OldStatusBarproc = wc.lpfnWndProc; + } + + switch (msg) { + case WM_CREATE: + LRESULT ret; + { + CREATESTRUCT *cs = (CREATESTRUCT *)lParam; + HWND hwndParent = GetParent(hWnd); + + // dirty trick to get rid of that annoying sizing gripper + SetWindowLongPtr(hwndParent, GWL_STYLE, GetWindowLongPtr(hwndParent, GWL_STYLE) & ~WS_THICKFRAME); + SetWindowLongPtr(hwndParent, GWL_EXSTYLE, GetWindowLongPtr(hwndParent, GWL_EXSTYLE) & ~WS_EX_APPWINDOW); + cs->style &= ~SBARS_SIZEGRIP; + ret = CallWindowProc(OldStatusBarproc, hWnd, msg, wParam, lParam); + SetWindowLongPtr(hwndParent, GWL_STYLE, GetWindowLongPtr(hwndParent, GWL_STYLE) | WS_THICKFRAME); + SetWindowLongPtr(hwndParent, GWL_EXSTYLE, GetWindowLongPtr(hwndParent, GWL_EXSTYLE) | WS_EX_APPWINDOW); + } + return ret; + + case WM_NCHITTEST: + RECT r; + { + LRESULT lr = SendMessage(GetParent(hWnd), WM_NCHITTEST, wParam, lParam); + int clip = CSkin::m_bClipBorder; + + GetWindowRect(hWnd, &r); + GetCursorPos(&pt); + if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 3) { + if (pt.x > r.right - clip - 4) + return HTBOTTOMRIGHT; + } + if (lr == HTLEFT || lr == HTRIGHT || lr == HTBOTTOM || lr == HTTOP || lr == HTTOPLEFT || lr == HTTOPRIGHT + || lr == HTBOTTOMLEFT || lr == HTBOTTOMRIGHT) + return HTTRANSPARENT; + } + break; + + case WM_ERASEBKGND: + return 1; + + case WM_PAINT: + PAINTSTRUCT ps; + { + HDC hdc = BeginPaint(hWnd, &ps); + int nParts = SendMessage(hWnd, SB_GETPARTS, 0, 0); + CSkinItem *item = &SkinItems[ID_EXTBKSTATUSBARPANEL]; + + BOOL bAero = M.isAero(); + HANDLE hTheme = bAero ? OpenThemeData(hWnd, L"ButtonStyle") : 0; + + TWindowData *dat = 0; + if (pContainer) + dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + + RECT rcClient; + GetClientRect(hWnd, &rcClient); + + HBITMAP hbm, hbmOld; + HANDLE hbp = 0; + HDC hdcMem; + if (CMimAPI::m_haveBufferedPaint) + hbp = CMimAPI::m_pfnBeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, NULL, &hdcMem); + else { + hdcMem = CreateCompatibleDC(hdc); + hbm = CSkin::CreateAeroCompatibleBitmap(rcClient, hdc); + hbmOld = (HBITMAP)SelectObject(hdcMem, hbm); + } + + SetBkMode(hdcMem, TRANSPARENT); + + COLORREF clr = CSkin::m_skinEnabled ? CSkin::m_DefaultFontColor : (PluginConfig.m_fillColor ? PluginConfig.m_genericTxtColor : GetSysColor(COLOR_BTNTEXT)); + + HFONT hFontOld = (HFONT)SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT)); + + if (pContainer && CSkin::m_skinEnabled) + CSkin::SkinDrawBG(hWnd, GetParent(hWnd), pContainer, &rcClient, hdcMem); + else if (bAero) { + FillRect(hdcMem, &rcClient, CSkin::m_BrushBack); + CSkin::ApplyAeroEffect(hdcMem, &rcClient, CSkin::AERO_EFFECT_AREA_STATUSBAR); + } + else { + CSkin::FillBack(hdcMem, &rcClient); + RECT rcFrame = rcClient; + if (PluginConfig.m_fillColor == 0) { + InflateRect(&rcFrame, -2, -1); + DrawEdge(hdcMem, &rcClient, BDR_RAISEDINNER | BDR_SUNKENOUTER, BF_RECT); + } + else { + CSkin::m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, 180); + CSkin::m_switchBarItem->Render(hdcMem, &rcFrame, true); + } + } + + for (int i = 0; i < nParts; i++) { + RECT itemRect; + SendMessage(hWnd, SB_GETRECT, (WPARAM)i, (LPARAM)&itemRect); + if (!item->IGNORED && !bAero && pContainer && CSkin::m_skinEnabled) + CSkin::DrawItem(hdcMem, &itemRect, item); + + if (i == 0) + itemRect.left += 2; + + // draw visual message length indicator in the leftmost status bar field + if (PluginConfig.m_visualMessageSizeIndicator && i == 0) { + if (dat && dat->bType == SESSIONTYPE_IM) { + HBRUSH br = CreateSolidBrush(RGB(0, 255, 0)); + HBRUSH brOld = (HBRUSH)SelectObject(hdcMem, br); + + RECT rc = itemRect; + rc.top = rc.bottom - 3; + rc.left = 0; + + if (!PluginConfig.m_autoSplit) { + float fMax = (float)dat->nMax; + float uPercent = (float)dat->textLen / ((fMax / (float)100.0) ? (fMax / (float)100.0) : (float)75.0); + float fx = ((float)rc.right / (float)100.0) * uPercent; + + rc.right = (LONG)fx; + FillRect(hdcMem, &rc, br); + } + else { + float baselen = (dat->textLen <= dat->nMax) ? (float)dat->textLen : (float)dat->nMax; + float fMax = (float)dat->nMax; + float uPercent = baselen / ((fMax / (float)100.0) ? (fMax / (float)100.0) : (float)75.0); + float fx; + LONG width = rc.right - rc.left; + if (dat->textLen >= dat->nMax) + rc.right = rc.right / 3; + fx = ((float)rc.right / (float)100.0) * uPercent; + rc.right = (LONG)fx; + FillRect(hdcMem, &rc, br); + if (dat->textLen >= dat->nMax) { + SelectObject(hdcMem, brOld); + DeleteObject(br); + br = CreateSolidBrush(RGB(255, 0, 0)); + brOld = (HBRUSH)SelectObject(hdcMem, br); + rc.left = width / 3; + rc.right = width; + uPercent = (float)dat->textLen / (float)200.0; + fx = ((float)(rc.right - rc.left) / (float)100.0) * uPercent; + rc.right = rc.left + (LONG)fx; + FillRect(hdcMem, &rc, br); + } + } + SelectObject(hdcMem, brOld); + DeleteObject(br); + } + } + + int height = itemRect.bottom - itemRect.top; + int width = itemRect.right - itemRect.left; + HICON hIcon = (HICON)SendMessage(hWnd, SB_GETICON, i, 0); + + TCHAR szText[1024]; szText[0] = 0; + LRESULT result = SendMessage(hWnd, SB_GETTEXT, i, (LPARAM)szText); + if (i == 2 && pContainer) { + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + if (dat) + DrawStatusIcons(dat, hdcMem, itemRect, 2); + } + else { + if (hIcon) { + if (LOWORD(result) > 1) { // we have a text + DrawIconEx(hdcMem, itemRect.left + 3, (height / 2 - 8) + itemRect.top, hIcon, 16, 16, 0, 0, DI_NORMAL); + if (dat) { + if (dat->bShowTyping == 2) + DrawIconEx(hdcMem, itemRect.left + 3, (height / 2 - 8) + itemRect.top, PluginConfig.g_iconOverlayEnabled, 16, 16, 0, 0, DI_NORMAL); + } + itemRect.left += 20; + CSkin::RenderText(hdcMem, hTheme, szText, &itemRect, DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX, + CSkin::m_glowSize, clr); + } + else DrawIconEx(hdcMem, itemRect.left + 3, (height / 2 - 8) + itemRect.top, hIcon, 16, 16, 0, 0, DI_NORMAL); + } + else { + itemRect.left += 2; + itemRect.right -= 2; + CSkin::RenderText(hdcMem, hTheme, szText, &itemRect, DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX, CSkin::m_glowSize, clr); + } + } + } + + if (hbp) + CSkin::FinalizeBufferedPaint(hbp, &rcClient); + else { + BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hbmOld); + DeleteObject(hbm); + SelectObject(hdcMem, hFontOld); + DeleteDC(hdcMem); + } + + if (hTheme) + CloseThemeData(hTheme); + + EndPaint(hWnd, &ps); + } + return 0; + + // tell status bar to update the part layout (re-calculate part widths) + // needed when an icon is added to or removed from the icon area + case WM_USER + 101: + { + int list_icons = 0; + TWindowData *dat = (TWindowData*)lParam; + if (dat) + while ( Srmm_GetNthIcon(dat->hContact, list_icons)) + list_icons++; + + SendMessage(hWnd, WM_SIZE, 0, 0); + + RECT rcs; + GetWindowRect(hWnd, &rcs); + + int statwidths[5]; + statwidths[0] = (rcs.right - rcs.left) - (2 * SB_CHAR_WIDTH + 20) - (list_icons * (PluginConfig.m_smcxicon + 2)); + statwidths[1] = (rcs.right - rcs.left) - (10 + (list_icons * (PluginConfig.m_smcxicon + 2))); + statwidths[2] = -1; + SendMessage(hWnd, SB_SETPARTS, 3, (LPARAM)statwidths); + } + return 0; + + case WM_SETCURSOR: + GetCursorPos(&pt); + SendMessage(GetParent(hWnd), msg, wParam, lParam); + if (pt.x == ptMouse.x && pt.y == ptMouse.y) + return 1; + + ptMouse = pt; + if (tooltip_active) { + KillTimer(hWnd, TIMERID_HOVER); + CallService("mToolTip/HideTip", 0, 0); + tooltip_active = FALSE; + } + KillTimer(hWnd, TIMERID_HOVER); + SetTimer(hWnd, TIMERID_HOVER, 450, 0); + break; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + KillTimer(hWnd, TIMERID_HOVER); + CallService("mToolTip/HideTip", 0, 0); + tooltip_active = FALSE; + GetCursorPos(&pt); + rcLastStatusBarClick.left = pt.x - 2; + rcLastStatusBarClick.right = pt.x + 2; + rcLastStatusBarClick.top = pt.y - 2; + rcLastStatusBarClick.bottom = pt.y + 2; + + if (pContainer->dwFlags & CNT_NOTITLE) { + POINT pt1 = pt; + ScreenToClient(hWnd, &pt1); + + RECT rcIconpart; + SendMessage(hWnd, SB_GETRECT, 2, (LPARAM)&rcIconpart); + if (!PtInRect(&rcIconpart, pt1)) + return SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); + } + break; + + case WM_TIMER: + if (wParam != TIMERID_HOVER) + break; + KillTimer(hWnd, TIMERID_HOVER); + GetCursorPos(&pt); + if (pt.x != ptMouse.x || pt.y != ptMouse.y) + break; + TWindowData *dat = (TWindowData*)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA); + if (dat == NULL) + break; + RECT rc; + SIZE size; + TCHAR wBuf[512]; wBuf[0] = 0; + CLCINFOTIP ti = {0}; + ti.cbSize = sizeof(ti); + ti.ptCursor = pt; + ScreenToClient(hWnd, &pt); + SendMessage(hWnd, SB_GETRECT, 2, (LPARAM)&rc); + if ( PtInRect(&rc, pt)) { + unsigned int iconNum = (pt.x - rc.left) / (PluginConfig.m_smcxicon + 2); + StatusIconData *sid = Srmm_GetNthIcon(dat->hContact, iconNum); + if (sid == NULL) + break; + + if ( !strcmp(sid->szModule, MSG_ICON_MODULE)) { + if (sid->dwId == MSG_ICON_SOUND && pContainer) + mir_sntprintf(wBuf, SIZEOF(wBuf), TranslateT("Sounds are %s. Click to toggle status, hold SHIFT and click to set for all open containers"), + pContainer->dwFlags & CNT_NOSOUND ? TranslateT("disabled") : TranslateT("enabled")); + + else if (sid->dwId == MSG_ICON_UTN && dat && (dat->bType == SESSIONTYPE_IM || dat->si->iType == GCW_PRIVMESS)) { + int mtnStatus = db_get_b(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, M.GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW)); + mir_sntprintf(wBuf, SIZEOF(wBuf), TranslateT("Sending typing notifications is %s."), + mtnStatus ? TranslateT("enabled") : TranslateT("disabled")); + } + else if (sid->dwId == MSG_ICON_SESSION) + mir_sntprintf(wBuf, SIZEOF(wBuf), _T("%s"), TranslateT("Session list.\nClick left for a list of open sessions.\nClick right to access favorites and quickly configure message window behavior")); + } + else if (sid->tszTooltip) + _tcsncpy(wBuf, sid->tszTooltip, SIZEOF(wBuf)); + + if (wBuf[0]) { + CallService("mToolTip/ShowTipW", (WPARAM)wBuf, (LPARAM)&ti); + tooltip_active = TRUE; + } + } + SendMessage(hWnd, SB_GETRECT, 1, (LPARAM)&rc); + if (PtInRect(&rc, pt)) { + int iLength = 0; + GETTEXTLENGTHEX gtxl = {0}; + int iQueued = db_get_dw(dat->hContact, "SendLater", "count", 0); + gtxl.codepage = CP_UTF8; + gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES; + iLength = SendDlgItemMessage(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); + tooltip_active = TRUE; + + const TCHAR *szFormat = TranslateT("There are %d pending send jobs. Message length: %d bytes, message length limit: %d bytes\n\n%d messages are queued for later delivery"); + + mir_sntprintf(wBuf, SIZEOF(wBuf), szFormat, dat->iOpenJobs, iLength, dat->nMax ? dat->nMax : 20000, iQueued); + CallService("mToolTip/ShowTipW", (WPARAM)wBuf, (LPARAM)&ti); + } + + if (SendMessage(dat->pContainer->hwndStatus, SB_GETTEXT, 0, (LPARAM)wBuf)) { + HDC hdc; + int iLen=SendMessage(dat->pContainer->hwndStatus,SB_GETTEXTLENGTH,0,0); + SendMessage(hWnd, SB_GETRECT, 0, (LPARAM)&rc); + GetTextExtentPoint32( hdc=GetDC( dat->pContainer->hwndStatus), wBuf, iLen, &size ); + ReleaseDC (dat->pContainer->hwndStatus,hdc); + + if (PtInRect(&rc,pt)&&((rc.right-rc.left)bType == SESSIONTYPE_CHAT) + db_get_ts(dat->hContact,dat->szProto,"Topic",&dbv); + + tooltip_active = TRUE; + CallService("mToolTip/ShowTipW", (WPARAM)dbv.ptszVal, (LPARAM)&ti); + if (dbv.pszVal) + db_free(&dbv); + } + } + break; + + case WM_DESTROY: + KillTimer(hWnd, TIMERID_HOVER); + } + return CallWindowProc(OldStatusBarproc, hWnd, msg, wParam, lParam); +} diff --git a/plugins/TabSRMM/src/generic_msghandlers.cpp b/plugins/TabSRMM/src/generic_msghandlers.cpp index 34799804d3..4daa1700f3 100644 --- a/plugins/TabSRMM/src/generic_msghandlers.cpp +++ b/plugins/TabSRMM/src/generic_msghandlers.cpp @@ -1104,25 +1104,27 @@ LRESULT TSAPI DM_WMCopyHandler(HWND hwnd, WNDPROC oldWndProc, UINT msg, WPARAM w { LRESULT result = mir_callNextSubclass(hwnd, oldWndProc, msg, wParam, lParam); - if (OpenClipboard(hwnd)) { - HANDLE hClip = GetClipboardData(CF_UNICODETEXT); - if (hClip) { - TCHAR *tszText = (TCHAR*)mir_alloc((lstrlen((TCHAR*)hClip) + 2) * sizeof(TCHAR)); - - lstrcpy(tszText, (TCHAR*)hClip); - Utils::FilterEventMarkers(tszText); - EmptyClipboard(); - - HGLOBAL hgbl = GlobalAlloc(GMEM_MOVEABLE, (lstrlen(tszText) + 1) * sizeof(TCHAR)); - TCHAR *tszLocked = (TCHAR*)GlobalLock(hgbl); - lstrcpy(tszLocked, tszText); - GlobalUnlock(hgbl); - SetClipboardData(CF_UNICODETEXT, hgbl); - if (tszText) - mir_free(tszText); - } - CloseClipboard(); - } + if (!OpenClipboard(hwnd)) + return result; + HANDLE hClip = GetClipboardData(CF_UNICODETEXT); + if (!hClip) + goto err_out; + TCHAR *tszText = (TCHAR*)mir_alloc((lstrlen((TCHAR*)hClip) + 2) * sizeof(TCHAR)); + if (!tszText) + goto err_out; + lstrcpy(tszText, (TCHAR*)hClip); + Utils::FilterEventMarkers(tszText); + EmptyClipboard(); + + HGLOBAL hgbl = GlobalAlloc(GMEM_MOVEABLE, (lstrlen(tszText) + 1) * sizeof(TCHAR)); + TCHAR *tszLocked = (TCHAR*)GlobalLock(hgbl); + lstrcpy(tszLocked, tszText); + GlobalUnlock(hgbl); + SetClipboardData(CF_UNICODETEXT, hgbl); + mir_free(tszText); + +err_out: + CloseClipboard(); return result; } diff --git a/plugins/TabSRMM/src/msgdialog.cpp b/plugins/TabSRMM/src/msgdialog.cpp index 663a4383a0..ab677d6451 100644 --- a/plugins/TabSRMM/src/msgdialog.cpp +++ b/plugins/TabSRMM/src/msgdialog.cpp @@ -1,3451 +1,3451 @@ -/* - * Miranda NG: the free IM client for Microsoft* Windows* - * - * 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 message dialog window. - * - */ - -#include "commonheaders.h" - -#define MS_HTTPSERVER_ADDFILENAME "HTTPServer/AddFileName" - -bool IsStringValidLink(TCHAR* pszText); - -const TCHAR *pszIDCSAVE_close = 0, *pszIDCSAVE_save = 0; - -static const UINT sendControls[] = { IDC_MESSAGE, IDC_LOG }; -static const UINT formatControls[] = { IDC_SMILEYBTN, IDC_FONTBOLD, IDC_FONTITALIC, IDC_FONTUNDERLINE, IDC_FONTFACE,IDC_FONTSTRIKEOUT }; -static const UINT addControls[] = { IDC_ADD, IDC_CANCELADD }; -static const UINT btnControls[] = { IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER, IDC_ADD, IDC_CANCELADD }; -static const UINT errorControls[] = { IDC_STATICERRORICON, IDC_STATICTEXT, IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER}; - -static struct { - int id; - const TCHAR* text; -} -tooltips[] = -{ - { IDC_ADD, LPGENT("Add this contact permanently to your contact list") }, - { IDC_CANCELADD, LPGENT("Do not add this contact permanently") }, - { IDC_TOGGLESIDEBAR, LPGENT("Expand or collapse the side bar") } -}; - -static struct -{ - int id; - HICON *pIcon; -} -buttonicons[] = -{ - { IDC_ADD, &PluginConfig.g_buttonBarIcons[ICON_BUTTON_ADD] }, - { IDC_CANCELADD, &PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL] } -}; - -static void _clrMsgFilter(LPARAM lParam) -{ - MSGFILTER *m = reinterpret_cast(lParam); - m->msg = 0; - m->lParam = 0; - m->wParam = 0; -} - -BOOL TSAPI IsUtfSendAvailable(MCONTACT hContact) -{ - char *szProto = GetContactProto(hContact); - if (szProto == NULL) - return FALSE; - - return (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_IMSENDUTF) ? TRUE : FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// show a modified context menu for the richedit control(s) -// @param dat message window data -// @param idFrom dlg ctrl id -// @param hwndFrom src window handle -// @param pt mouse pointer position - -static void ShowPopupMenu(TWindowData *dat, int idFrom, HWND hwndFrom, POINT pt) -{ - CHARRANGE sel, all = { 0, -1}; - int oldCodepage = dat->codePage; - int iPrivateBG = M.GetByte(dat->hContact, "private_bg", 0); - HWND hwndDlg = dat->hwnd; - - HMENU hSubMenu, hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); - if (idFrom == IDC_LOG) - hSubMenu = GetSubMenu(hMenu, 0); - else { - hSubMenu = GetSubMenu(hMenu, 2); - EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, MF_BYCOMMAND | (dat->SendFormat != 0 ? MF_ENABLED : MF_GRAYED)); - EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, MF_BYCOMMAND | (PluginConfig.m_PasteAndSend ? MF_ENABLED : MF_GRAYED)); - CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, MF_BYCOMMAND | (PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED)); - EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, MF_BYCOMMAND | (dat->pContainer->hwndStatus ? MF_ENABLED : MF_GRAYED)); - } - TranslateMenu(hSubMenu); - SendMessage(hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) { - EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); - EnableMenuItem(hSubMenu, IDM_QUOTE, MF_BYCOMMAND | MF_GRAYED); - if (idFrom == IDC_MESSAGE) - EnableMenuItem(hSubMenu, IDM_CUT, MF_BYCOMMAND | MF_GRAYED); - } - - if (idFrom == IDC_LOG) { - InsertMenuA(hSubMenu, 6/*5*/, MF_BYPOSITION | MF_SEPARATOR, 0, 0); - InsertMenu(hSubMenu, 7/*6*/, MF_BYPOSITION | MF_POPUP, (UINT_PTR) PluginConfig.g_hMenuEncoding, TranslateT("Character Encoding")); - for (int i=0; i < GetMenuItemCount(PluginConfig.g_hMenuEncoding); i++) - CheckMenuItem(PluginConfig.g_hMenuEncoding, i, MF_BYPOSITION | MF_UNCHECKED); - - if (dat->codePage == CP_ACP) - CheckMenuItem(PluginConfig.g_hMenuEncoding, 0, MF_BYPOSITION | MF_CHECKED); - else - CheckMenuItem(PluginConfig.g_hMenuEncoding, dat->codePage, MF_BYCOMMAND | MF_CHECKED); - - CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, MF_BYCOMMAND | (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED ? MF_CHECKED : MF_UNCHECKED)); - } - - MessageWindowPopupData mwpd; - if (idFrom == IDC_LOG || idFrom == IDC_MESSAGE) { - // First notification - mwpd.cbSize = sizeof(mwpd); - mwpd.uType = MSG_WINDOWPOPUP_SHOWING; - mwpd.uFlags = (idFrom == IDC_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT); - mwpd.hContact = dat->hContact; - mwpd.hwnd = hwndFrom; - mwpd.hMenu = hSubMenu; - mwpd.selection = 0; - mwpd.pt = pt; - NotifyEventHooks(PluginConfig.m_event_MsgPopup, 0, (LPARAM)&mwpd); - } - - int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); - - if (idFrom == IDC_LOG || idFrom == IDC_MESSAGE) { - // Second notification - mwpd.selection = iSelection; - mwpd.uType = MSG_WINDOWPOPUP_SELECTED; - NotifyEventHooks(PluginConfig.m_event_MsgPopup, 0, (LPARAM)&mwpd); - } - - if (((iSelection > 800 && iSelection < 1400) || iSelection == 20866) && idFrom == IDC_LOG) { - dat->codePage = iSelection; - db_set_dw(dat->hContact, SRMSGMOD_T, "ANSIcodepage", dat->codePage); - } - else if (iSelection == 500 && idFrom == IDC_LOG) { - dat->codePage = CP_ACP; - db_unset(dat->hContact, SRMSGMOD_T, "ANSIcodepage"); - } - else { - switch (iSelection) { - case IDM_COPY: - SendMessage(hwndFrom, WM_COPY, 0, 0); - break; - case IDM_CUT: - SendMessage(hwndFrom, WM_CUT, 0, 0); - break; - case IDM_PASTE: - case IDM_PASTEFORMATTED: - if (idFrom == IDC_MESSAGE) - SendMessage(hwndFrom, EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0); - break; - case IDM_COPYALL: - SendMessage(hwndFrom, EM_EXSETSEL, 0, (LPARAM)&all); - SendMessage(hwndFrom, WM_COPY, 0, 0); - SendMessage(hwndFrom, EM_EXSETSEL, 0, (LPARAM)&sel); - break; - case IDM_QUOTE: - SendMessage(hwndDlg, WM_COMMAND, IDC_QUOTE, 0); - break; - case IDM_SELECTALL: - SendMessage(hwndFrom, EM_EXSETSEL, 0, (LPARAM)&all); - break; - case IDM_CLEAR: - ClearLog(dat); - break; - case ID_LOG_FREEZELOG: - SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_KEYDOWN, VK_F12, 0); - break; - case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR: - PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator; - db_set_b(0, SRMSGMOD_T, "msgsizebar", (BYTE)PluginConfig.m_visualMessageSizeIndicator); - M.BroadcastMessage(DM_CONFIGURETOOLBAR, 0, 0); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - if (dat->pContainer->hwndStatus) - RedrawWindow(dat->pContainer->hwndStatus, 0, 0, RDW_INVALIDATE | RDW_UPDATENOW); - break; - case ID_EDITOR_PASTEANDSENDIMMEDIATELY: - HandlePasteAndSend(dat); - break; - } - } - if (idFrom == IDC_LOG) - RemoveMenu(hSubMenu, 7, MF_BYPOSITION); - DestroyMenu(hMenu); - if (dat->codePage != oldCodepage) { - SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); - SendMessage(hwndDlg, DM_UPDATETITLE, 0, 1); - } -} - -static void ResizeIeView(const TWindowData *dat, DWORD px, DWORD py, DWORD cx, DWORD cy) -{ - RECT rcRichEdit; - GetWindowRect(GetDlgItem(dat->hwnd, IDC_LOG), &rcRichEdit); - - POINT pt = { rcRichEdit.left, rcRichEdit.top }; - ScreenToClient(dat->hwnd, &pt); - - IEVIEWWINDOW ieWindow = { sizeof(ieWindow) }; - ieWindow.iType = IEW_SETPOS; - ieWindow.parent = dat->hwnd; - ieWindow.hwnd = dat->hwndIEView ? dat->hwndIEView : dat->hwndHPP; - ieWindow.x = pt.x; - ieWindow.y = pt.y; - ieWindow.cx = rcRichEdit.right - rcRichEdit.left; - ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top; - if (ieWindow.cx != 0 && ieWindow.cy != 0) - CallService(dat->hwndIEView ? MS_IEVIEW_WINDOW : MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); -} - -LRESULT CALLBACK IEViewSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - - switch (msg) { - case WM_NCCALCSIZE: - return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, IEViewSubclassProc); - case WM_NCPAINT: - return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, IEViewSubclassProc); - } - return mir_callNextSubclass(hwnd, IEViewSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// sublassing procedure for the h++ based message log viewer - -LRESULT CALLBACK HPPKFSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (mwdat) { - BOOL isCtrl, isShift, isAlt; - KbdState(mwdat, isShift, isCtrl, isAlt); - - switch (msg) { - case WM_NCCALCSIZE: - return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, HPPKFSubclassProc); - case WM_NCPAINT: - return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, HPPKFSubclassProc); - - case WM_KEYDOWN: - if (!isCtrl && !isAlt && !isShift) { - if (wParam != VK_PRIOR && wParam != VK_NEXT && wParam != VK_DELETE && - wParam != VK_MENU && wParam != VK_END && wParam != VK_HOME && - wParam != VK_UP && wParam != VK_DOWN && wParam != VK_LEFT && - wParam != VK_RIGHT && wParam != VK_TAB && wParam != VK_SPACE) - { - SetFocus(GetDlgItem(mwdat->hwnd, IDC_MESSAGE)); - keybd_event((BYTE)wParam, (BYTE)MapVirtualKey(wParam, 0), KEYEVENTF_EXTENDEDKEY | 0, 0); - return 0; - } - break; - } - } - } - return mir_callNextSubclass(hwnd, HPPKFSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// update state of the container - this is called whenever a tab becomes active, no matter how and -// deals with various things like updating the title bar, removing flashing icons, updating the -// session list, switching the keyboard layout (autolocale active) and the general container status. -// -// it protects itself from being called more than once per session activation and is valid for -// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c) - -static void MsgWindowUpdateState(TWindowData *dat, UINT msg) -{ - if (dat && dat->iTabID >= 0) { - HWND hwndDlg = dat->hwnd; - HWND hwndTab = GetParent(hwndDlg); - - if (msg == WM_ACTIVATE) { - if (dat->pContainer->dwFlags & CNT_TRANSPARENCY) { - DWORD trans = LOWORD(dat->pContainer->settings->dwTransparency); - SetLayeredWindowAttributes(dat->pContainer->hwnd, 0, (BYTE)trans, (dat->pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); - } - } - - if (dat->bIsAutosizingInput && dat->iInputAreaHeight == -1) { - dat->iInputAreaHeight = 0; - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_REQUESTRESIZE, 0, 0); - } - - if (dat->pWnd) - dat->pWnd->activateTab(); - dat->Panel->dismissConfig(); - dat->dwUnread = 0; - if (dat->pContainer->hwndSaved == hwndDlg) - return; - - dat->pContainer->hwndSaved = hwndDlg; - - dat->dwTickLastEvent = 0; - dat->dwFlags &= ~MWF_DIVIDERSET; - if (KillTimer(hwndDlg, TIMERID_FLASHWND)) { - FlashTab(dat, hwndTab, dat->iTabID, &dat->bTabFlash, FALSE, dat->hTabIcon); - dat->mayFlashTab = FALSE; - } - if (dat->pContainer->dwFlashingStarted != 0) { - FlashContainer(dat->pContainer, 0, 0); - dat->pContainer->dwFlashingStarted = 0; - } - if (dat->dwFlagsEx & MWF_SHOW_FLASHCLIST) { - dat->dwFlagsEx &= ~MWF_SHOW_FLASHCLIST; - if (dat->hFlashingEvent != 0) - CallService(MS_CLIST_REMOVEEVENT, dat->hContact, (LPARAM)dat->hFlashingEvent); - dat->hFlashingEvent = 0; - } - dat->pContainer->dwFlags &= ~CNT_NEED_UPDATETITLE; - - if (dat->dwFlags & MWF_DEFERREDREMAKELOG) { - SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); - dat->dwFlags &= ~MWF_DEFERREDREMAKELOG; - } - - if (dat->dwFlags & MWF_NEEDCHECKSIZE) - PostMessage(hwndDlg, DM_SAVESIZE, 0, 0); - - if (PluginConfig.m_AutoLocaleSupport) { - if (dat->hkl == 0) - DM_LoadLocale(dat); - else - SendMessage(hwndDlg, DM_SETLOCALE, 0, 0); - } - - dat->pContainer->hIconTaskbarOverlay = 0; - SendMessage(dat->pContainer->hwnd, DM_UPDATETITLE, dat->hContact, 0); - - UpdateStatusBar(dat); - dat->dwLastActivity = GetTickCount(); - dat->pContainer->dwLastActivity = dat->dwLastActivity; - - dat->pContainer->MenuBar->configureMenu(); - UpdateTrayMenuState(dat, FALSE); - - if (dat->pContainer->hwndActive == hwndDlg) - PostMessage(hwndDlg, DM_REMOVEPOPUPS, PU_REMOVE_ON_FOCUS, 0); - - dat->Panel->Invalidate(); - - if (dat->dwFlags & MWF_DEFERREDSCROLL && dat->hwndIEView == 0 && dat->hwndHPP == 0) { - HWND hwnd = GetDlgItem(hwndDlg, IDC_LOG); - - SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); - dat->dwFlags &= ~MWF_DEFERREDSCROLL; - SendMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_TOP, 0), 0); - SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); - DM_ScrollToBottom(dat, 0, 1); - PostMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0); // XXX was post() - } - DM_SetDBButtonStates(hwndDlg, dat); - - if (dat->hwndIEView) { - RECT rcRTF; - POINT pt; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rcRTF); - rcRTF.left += 20; - rcRTF.top += 20; - pt.x = rcRTF.left; - pt.y = rcRTF.top; - if (dat->hwndIEView) { - if (M.GetByte("subclassIEView", 0)) { - mir_subclassWindow(dat->hwndIEView, IEViewSubclassProc); - SetWindowPos(dat->hwndIEView, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_DRAWFRAME); - RedrawWindow(dat->hwndIEView, 0, 0, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); - } - } - dat->hwndIWebBrowserControl = WindowFromPoint(pt); - } - - if (dat->dwFlagsEx & MWF_EX_DELAYEDSPLITTER) { - dat->dwFlagsEx &= ~MWF_EX_DELAYEDSPLITTER; - ShowWindow(dat->pContainer->hwnd, SW_RESTORE); - PostMessage(hwndDlg, DM_SPLITTERGLOBALEVENT, dat->wParam, dat->lParam); - dat->wParam = dat->lParam = 0; - } - if (dat->dwFlagsEx & MWF_EX_AVATARCHANGED) { - dat->dwFlagsEx &= ~MWF_EX_AVATARCHANGED; - PostMessage(hwndDlg, DM_UPDATEPICLAYOUT, 0, 0); - } - BB_SetButtonsPos(dat); - if (M.isAero()) - InvalidateRect(hwndTab, NULL, FALSE); - if (dat->pContainer->dwFlags & CNT_SIDEBAR) - dat->pContainer->SideBar->setActiveItem(dat); - - if (dat->pWnd) - dat->pWnd->Invalidate(); - } -} - -void TSAPI ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - Utils::showDlgControl(hwndDlg, controls[i], state); -} - -void TSAPI SetDialogToType(HWND hwndDlg) -{ - TWindowData *dat = (TWindowData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - int showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1; - - if (dat->hContact) { - if (db_get_b(dat->hContact, "CList", "NotOnList", 0)) { - dat->bNotOnList = TRUE; - ShowMultipleControls(hwndDlg, addControls, 2, SW_SHOW); - Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_SHOW); - SetWindowText(GetDlgItem(hwndDlg, IDC_LOGFROZENTEXT), TranslateT("Contact not on list. You may add it...")); - } - else { - ShowMultipleControls(hwndDlg, addControls, 2, SW_HIDE); - dat->bNotOnList = FALSE; - Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_HIDE); - } - } - - Utils::enableDlgControl(hwndDlg, IDC_TIME, TRUE); - - if (dat->hwndIEView || dat->hwndHPP) { - Utils::showDlgControl(hwndDlg, IDC_LOG, SW_HIDE); - Utils::enableDlgControl(hwndDlg, IDC_LOG, FALSE); - Utils::showDlgControl(hwndDlg, IDC_MESSAGE, SW_SHOW); - } - else ShowMultipleControls(hwndDlg, sendControls, sizeof(sendControls) / sizeof(sendControls[0]), SW_SHOW); - - ShowMultipleControls(hwndDlg, errorControls, sizeof(errorControls) / sizeof(errorControls[0]), dat->dwFlags & MWF_ERRORSTATE ? SW_SHOW : SW_HIDE); - - if (!dat->SendFormat) - ShowMultipleControls(hwndDlg, &formatControls[1], 5, SW_HIDE); - - ConfigureSmileyButton(dat); - - if (dat->pContainer->hwndActive == hwndDlg) - UpdateReadChars(dat); - - SetDlgItemText(hwndDlg, IDC_STATICTEXT, TranslateT("A message failed to send successfully.")); - - DM_RecalcPictureSize(dat); - GetAvatarVisibility(hwndDlg, dat); - - Utils::showDlgControl(hwndDlg, IDC_CONTACTPIC, dat->bShowAvatar ? SW_SHOW : SW_HIDE); - Utils::showDlgControl(hwndDlg, IDC_SPLITTER, dat->bIsAutosizingInput ? SW_HIDE : SW_SHOW); - Utils::showDlgControl(hwndDlg, IDC_MULTISPLITTER, (dat->sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); - - EnableSendButton(dat, GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)) != 0); - SendMessage(hwndDlg, DM_UPDATETITLE, 0, 1); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - - Utils::enableDlgControl(hwndDlg, IDC_CONTACTPIC, FALSE); - - dat->Panel->Configure(); -} - -static LRESULT CALLBACK MessageLogSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - HWND hwndParent = GetParent(hwnd); - TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); - BOOL isCtrl, isShift, isAlt; - KbdState(mwdat, isShift, isCtrl, isAlt); - - switch (msg) { - case WM_KILLFOCUS: - if (wParam != (WPARAM)hwnd && 0 != wParam) { - CHARRANGE cr; - SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&cr); - if (cr.cpMax != cr.cpMin) { - cr.cpMin = cr.cpMax; - SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr); - } - } - break; - - case WM_CHAR: - if (wParam == 0x03 && isCtrl) // Ctrl+C - return DM_WMCopyHandler(hwnd, MessageLogSubclassProc, msg, wParam, lParam); - if (wParam == 0x11 && isCtrl) - SendMessage(mwdat->hwnd, WM_COMMAND, IDC_QUOTE, 0); - break; - - case WM_SYSKEYUP: - if (wParam == VK_MENU) { - ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_LOG); - return 0; - } - break; - - case WM_SYSKEYDOWN: - mwdat->fkeyProcessed = false; - if (ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_LOG)) { - mwdat->fkeyProcessed = true; - return 0; - } - break; - - case WM_SYSCHAR: - if (mwdat->fkeyProcessed) { - mwdat->fkeyProcessed = false; - return 0; - } - break; - - case WM_KEYDOWN: - if (wParam == VK_INSERT && isCtrl) - return DM_WMCopyHandler(hwnd, MessageLogSubclassProc, msg, wParam, lParam); - break; - - case WM_COPY: - return DM_WMCopyHandler(hwnd, MessageLogSubclassProc, msg, wParam, lParam); - - case WM_NCCALCSIZE: - return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, MessageLogSubclassProc); - - case WM_NCPAINT: - return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, MessageLogSubclassProc); - - case WM_CONTEXTMENU: - POINT pt; - - if (lParam == 0xFFFFFFFF) { - CHARRANGE sel; - SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); - SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); - ClientToScreen(hwnd, &pt); - } - else { - pt.x = (short)LOWORD(lParam); - pt.y = (short)HIWORD(lParam); - } - - ShowPopupMenu(mwdat, IDC_LOG, hwnd, pt); - return TRUE; - } - - return mir_callNextSubclass(hwnd, MessageLogSubclassProc, msg, wParam, lParam); -} - -static LRESULT CALLBACK MessageEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - BOOL isCtrl, isShift, isAlt; - HWND hwndParent = GetParent(hwnd); - TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); - if (mwdat == NULL) - return 0; - - // prevent the rich edit from switching text direction or keyboard layout when - // using hotkeys with ctrl-shift or alt-shift modifiers - if (mwdat->fkeyProcessed && (msg == WM_KEYUP)) { - GetKeyboardState(mwdat->kstate); - if (mwdat->kstate[VK_CONTROL] & 0x80 || mwdat->kstate[VK_SHIFT] & 0x80) - return 0; - - mwdat->fkeyProcessed = false; - return 0; - } - - switch (msg) { - case WM_NCCALCSIZE: - return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, MessageEditSubclassProc); - - case WM_NCPAINT: - return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, MessageEditSubclassProc); - - case WM_DROPFILES: - SendMessage(hwndParent, WM_DROPFILES, (WPARAM)wParam, (LPARAM)lParam); - break; - - case WM_CHAR: - KbdState(mwdat, isShift, isCtrl, isAlt); - - if (PluginConfig.g_bSoundOnTyping && !isAlt && !isCtrl && !(mwdat->pContainer->dwFlags & CNT_NOSOUND) && wParam != VK_ESCAPE && !(wParam == VK_TAB && PluginConfig.m_AllowTab)) - SkinPlaySound("SoundOnTyping"); - - if (isCtrl && !isAlt) { - switch (wParam) { - case 0x02: // bold - if (mwdat->SendFormat) - SendMessage(hwndParent, WM_COMMAND, MAKELONG(IDC_FONTBOLD, IDC_MESSAGE), 0); - return 0; - case 0x09: - if (mwdat->SendFormat) - SendMessage(hwndParent, WM_COMMAND, MAKELONG(IDC_FONTITALIC, IDC_MESSAGE), 0); - return 0; - case 21: - if (mwdat->SendFormat) - SendMessage(hwndParent, WM_COMMAND, MAKELONG(IDC_FONTUNDERLINE, IDC_MESSAGE), 0); - return 0; - case 0x0b: - SetWindowText(hwnd, _T("")); - return 0; - } - } - break; - - case WM_MOUSEWHEEL: - if (DM_MouseWheelHandler(hwnd, hwndParent, mwdat, wParam, lParam) == 0) - return 0; - break; - - case WM_PASTE: - case EM_PASTESPECIAL: - if (OpenClipboard(hwnd)) { - HANDLE hClip = GetClipboardData(CF_TEXT); - if (hClip) { - if (lstrlenA((char*)hClip) > mwdat->nMax) { - TCHAR szBuffer[512]; - if (M.GetByte("autosplit", 0)) - mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("WARNING: The message you are trying to paste exceeds the message size limit for the active protocol. It will be sent in chunks of max %d characters"), mwdat->nMax - 10); - else - mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("The message you are trying to paste exceeds the message size limit for the active protocol. Only the first %d characters will be sent."), mwdat->nMax); - SendMessage(hwndParent, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer); - } - } - else if (hClip = GetClipboardData(CF_BITMAP)) - SendHBitmapAsFile(mwdat, (HBITMAP)hClip); - - CloseClipboard(); - } - break; - - case WM_KEYDOWN: - KbdState(mwdat, isShift, isCtrl, isAlt); - - if (PluginConfig.g_bSoundOnTyping && !isAlt && !(mwdat->pContainer->dwFlags & CNT_NOSOUND) && wParam == VK_DELETE) - SkinPlaySound("SoundOnTyping"); - - if (wParam == VK_INSERT && !isShift && !isCtrl && !isAlt) { - mwdat->fInsertMode = !mwdat->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 (wParam == VK_RETURN) { - if (mwdat->fEditNotesActive) - break; - - if (isShift) { - if (PluginConfig.m_SendOnShiftEnter) { - PostMessage(hwndParent, WM_COMMAND, IDOK, 0); - return 0; - } - else break; - } - if ((isCtrl && !isShift) ^ (0 != PluginConfig.m_SendOnEnter)) { - PostMessage(hwndParent, WM_COMMAND, IDOK, 0); - return 0; - } - if (PluginConfig.m_SendOnEnter || PluginConfig.m_SendOnDblEnter) { - if (isCtrl) - break; - - if (PluginConfig.m_SendOnDblEnter) { - LONG_PTR lastEnterTime = GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (lastEnterTime + 2 < time(NULL)) { - lastEnterTime = time(NULL); - SetWindowLongPtr(hwnd, GWLP_USERDATA, lastEnterTime); - break; - } - else { - 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 break; - } - else SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - - if (isCtrl && !isAlt && !isShift) { - if (!isShift && (wParam == VK_UP || wParam == VK_DOWN)) { // input history scrolling (ctrl-up / down) - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - if (mwdat) - mwdat->cache->inputHistoryEvent(wParam); - return 0; - } - } - 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; - - SetWindowLongPtr(hwnd, GWLP_USERDATA, 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) { - DM_ScrollToBottom(mwdat, 0, 0); - return 0; - } - else if (wParam == VK_DOWN) - wp = MAKEWPARAM(SB_LINEDOWN, 0); - - if (mwdat->hwndIEView == 0 && mwdat->hwndHPP == 0) - SendMessage(GetDlgItem(hwndParent, IDC_LOG), WM_VSCROLL, wp, 0); - else - SendMessage(mwdat->hwndIWebBrowserControl, WM_VSCROLL, wp, 0); - return 0; - } - } - if (wParam == VK_RETURN) - break; - - case WM_SYSKEYDOWN: - mwdat->fkeyProcessed = false; - if (ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_MESSAGE)) { - mwdat->fkeyProcessed = true; - return 0; - } - break; - - case WM_SYSKEYUP: - if (wParam == VK_MENU) { - ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_MESSAGE); - return 0; - } - break; - - case WM_SYSCHAR: - if (mwdat->fkeyProcessed) { - mwdat->fkeyProcessed = false; - return 0; - } - - KbdState(mwdat, isShift, isCtrl, isAlt); - if ((wParam >= '0' && wParam <= '9') && isAlt) { // ALT-1 -> ALT-0 direct tab selection - BYTE bChar = (BYTE)wParam; - int iIndex; - - if (bChar == '0') - iIndex = 10; - else - iIndex = bChar - (BYTE)'0'; - SendMessage(mwdat->pContainer->hwnd, DM_SELECTTAB, DM_SELECT_BY_INDEX, (LPARAM)iIndex); - return 0; - } - break; - - case WM_INPUTLANGCHANGE: - if (PluginConfig.m_AutoLocaleSupport && GetFocus() == hwnd && mwdat->pContainer->hwndActive == hwndParent && GetForegroundWindow() == mwdat->pContainer->hwnd && GetActiveWindow() == mwdat->pContainer->hwnd) { - DM_SaveLocale(mwdat, 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 ? 0 : 1); - - // sent by smileyadd when the smiley selection window dies - // just grab the focus :) - case WM_USER + 100: - SetFocus(hwnd); - break; - - case WM_CONTEXTMENU: - POINT pt; - if (lParam == 0xFFFFFFFF) { - CHARRANGE sel; - SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); - SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); - ClientToScreen(hwnd, &pt); - } - else { - pt.x = (short)LOWORD(lParam); - pt.y = (short)HIWORD(lParam); - } - - ShowPopupMenu(mwdat, IDC_MESSAGE, hwnd, pt); - return TRUE; - } - return mir_callNextSubclass(hwnd, MessageEditSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// subclasses the avatar display controls, needed for skinning and to prevent -// it from flickering during resize/move operations. - -static LRESULT CALLBACK AvatarSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_ERASEBKGND: - return TRUE; - - case WM_UPDATEUISTATE: - return TRUE; - - case WM_NOTIFY: - ProcessAvatarChange(hwnd, lParam); - break; - } - return mir_callNextSubclass(hwnd, AvatarSubclassProc, msg, wParam, lParam); -} - -LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - HWND hwndParent = GetParent(hwnd); - TWindowData *dat = (TWindowData*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); - - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - GetClientRect(hwnd, &rc); - SetCursor(rc.right > rc.bottom ? PluginConfig.hCurSplitNS : PluginConfig.hCurSplitWE); - return TRUE; - - case WM_LBUTTONDOWN: - if (hwnd == GetDlgItem(hwndParent, IDC_SPLITTER) || hwnd == GetDlgItem(hwndParent, IDC_SPLITTERY)) { - if (dat) { - GetClientRect(hwnd, &rc); - dat->savedSplitter = rc.right > rc.bottom ? (short)HIWORD(GetMessagePos()) + rc.bottom / 2 : (short)LOWORD(GetMessagePos()) + rc.right / 2; - if (dat->bType == SESSIONTYPE_IM) - dat->savedSplitY = dat->splitterY; - else - dat->savedSplitY = dat->si->iSplitterY; - - dat->savedDynaSplit = dat->dynaSplitter; - } - } - SetCapture(hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == hwnd) { - GetClientRect(hwnd, &rc); - SendMessage(hwndParent, DM_SPLITTERMOVED, rc.right > rc.bottom ? (short)HIWORD(GetMessagePos()) + rc.bottom / 2 : (short)LOWORD(GetMessagePos()) + rc.right / 2, (LPARAM)hwnd); - } - return 0; - - case WM_ERASEBKGND: - return 1; - - case WM_PAINT: - GetClientRect(hwnd, &rc); - { - PAINTSTRUCT ps; - HDC dc = BeginPaint(hwnd, &ps); - - if (dat && CSkin::m_skinEnabled) - CSkin::SkinDrawBG(hwnd, dat->pContainer->hwnd, dat->pContainer, &rc, dc); - else if (M.isAero() || M.isVSThemed()) { - if (GetDlgCtrlID(hwnd) == IDC_PANELSPLITTER) { - EndPaint(hwnd, &ps); - return 0; - } - CSkin::FillBack(dc, &rc); - } - else CSkin::FillBack(dc, &rc); - - EndPaint(hwnd, &ps); - } - return 0; - - case WM_LBUTTONUP: - HWND hwndCapture = GetCapture(); - - ReleaseCapture(); - DM_ScrollToBottom(dat, 0, 1); - if (dat && dat->bType == SESSIONTYPE_IM && hwnd == GetDlgItem(hwndParent, IDC_PANELSPLITTER)) { - SendMessage(hwndParent, WM_SIZE, 0, 0); - RedrawWindow(hwndParent, NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); - } - else if ((dat && dat->bType == SESSIONTYPE_IM && hwnd == GetDlgItem(hwndParent, IDC_SPLITTER)) || - (dat && dat->bType == SESSIONTYPE_CHAT && hwnd == GetDlgItem(hwndParent, IDC_SPLITTERY))) { - POINT pt; - int selection; - HMENU hMenu = GetSubMenu(dat->pContainer->hMenuContext, 12); - LONG messagePos = GetMessagePos(); - - GetClientRect(hwnd, &rc); - if (hwndCapture != hwnd || dat->savedSplitter == (rc.right > rc.bottom ? (short) HIWORD(messagePos) + rc.bottom / 2 : (short) LOWORD(messagePos) + rc.right / 2)) - break; - GetCursorPos(&pt); - - if (dat->bIsAutosizingInput) - selection = ID_SPLITTERCONTEXT_SETPOSITIONFORTHISSESSION; - else - selection = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndParent, NULL); - - switch (selection) { - case ID_SPLITTERCONTEXT_SAVEFORTHISCONTACTONLY: - dat->dwFlagsEx |= MWF_SHOW_SPLITTEROVERRIDE; - db_set_b(dat->hContact, SRMSGMOD_T, "splitoverride", 1); - if (dat->bType == SESSIONTYPE_IM) - SaveSplitter(dat); - break; - - case ID_SPLITTERCONTEXT_SETPOSITIONFORTHISSESSION: - if (dat->bIsAutosizingInput) { - GetWindowRect(GetDlgItem(dat->hwnd, IDC_MESSAGE), &rc); - dat->iInputAreaHeight = 0; - } - break; - - case ID_SPLITTERCONTEXT_SAVEGLOBALFORALLSESSIONS: - { - RECT rcWin; - BYTE bSync = M.GetByte(CHAT_MODULE, "SyncSplitter", 0); - DWORD dwOff_IM = 0, dwOff_CHAT = 0; - - dwOff_CHAT = -(2 + (PluginConfig.g_DPIscaleY > 1.0 ? 1 : 0)); - dwOff_IM = 2 + (PluginConfig.g_DPIscaleY > 1.0 ? 1 : 0); - - GetWindowRect(hwndParent, &rcWin); - PluginConfig.lastSPlitterPos.pSrcDat = dat; - PluginConfig.lastSPlitterPos.pSrcContainer = dat->pContainer; - PluginConfig.lastSPlitterPos.lParam = rc.bottom; - PluginConfig.lastSPlitterPos.pos = rcWin.bottom - HIWORD(messagePos); - PluginConfig.lastSPlitterPos.pos_chat = rcWin.bottom - (short)HIWORD(messagePos) + rc.bottom / 2; - PluginConfig.lastSPlitterPos.off_chat = dwOff_CHAT; - PluginConfig.lastSPlitterPos.off_im = dwOff_IM; - PluginConfig.lastSPlitterPos.bSync = bSync; - SendMessage(dat->hwnd, DM_SPLITTERGLOBALEVENT, 0, 0); - M.BroadcastMessage(DM_SPLITTERGLOBALEVENT, 0, 0); - } - break; - - default: - dat->splitterY = dat->savedSplitY; - dat->dynaSplitter = dat->savedDynaSplit; - DM_RecalcPictureSize(dat); - if (dat->bType == SESSIONTYPE_CHAT) { - SESSION_INFO *si = dat->si; - si->iSplitterY = dat->savedSplitY; - dat->splitterY = si->iSplitterY + DPISCALEY_S(22); - } - CSkin::UpdateToolbarBG(dat); - SendMessage(hwndParent, WM_SIZE, 0, 0); - DM_ScrollToBottom(dat, 0, 1); - break; - } - } - return 0; - } - return mir_callNextSubclass(hwnd, SplitterSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// resizer proc for the "new" layout. - -static int MessageDialogResize(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL * urc) -{ - TWindowData *dat = (TWindowData*)lParam; - RECT rc, rcButton; - static int uinWidth, msgTop = 0, msgBottom = 0; - - int showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1; - BOOL bBottomToolbar = dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR ? 1 : 0; - static LONG rcLogBottom; - - int panelHeight = dat->Panel->getHeight() + 1; - int s_offset = 0; - bool bInfoPanel = dat->Panel->isActive(); - bool fErrorState = (dat->dwFlags & MWF_ERRORSTATE) ? true : false; - - GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rc); - GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOCOL), &rcButton); - - if (dat->panelStatusCX == 0) - dat->panelStatusCX = 80; - - s_offset = 1; - - 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: - if (dat->dwFlags & MWF_ERRORSTATE) - urc->rcItem.bottom -= ERRORPANEL_HEIGHT; - if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED || dat->bNotOnList) - urc->rcItem.bottom -= 20; - if (dat->sendMode & SMODE_MULTIPLE) - urc->rcItem.right -= (dat->multiSplitterX + 3); - urc->rcItem.bottom -= dat->splitterY - dat->originalSplitterY; - if (!showToolbar||bBottomToolbar) - urc->rcItem.bottom += 21; - if (bInfoPanel) - urc->rcItem.top += panelHeight; - urc->rcItem.bottom += 3; - 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; - } - } - rcLogBottom = urc->rcItem.bottom; - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - - case IDC_CONTACTPIC: - GetClientRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &rc); - urc->rcItem.top -= dat->splitterY - dat->originalSplitterY; - urc->rcItem.left = urc->rcItem.right - (dat->pic.cx + 2); - if ((urc->rcItem.bottom - urc->rcItem.top) < (dat->pic.cy/* + 2*/) && dat->bShowAvatar) { - urc->rcItem.top = urc->rcItem.bottom - dat->pic.cy; - dat->bUseOffset = true; - } - else dat->bUseOffset = false; - - if (showToolbar && bBottomToolbar && (PluginConfig.m_AlwaysFullToolbarWidth || ((dat->pic.cy - DPISCALEY_S(6)) < rc.bottom))) { - urc->rcItem.bottom -= DPISCALEY_S(22); - if (dat->bIsAutosizingInput) { - urc->rcItem.left--; - urc->rcItem.top--; - } - } - - if (dat->hwndContactPic) //if Panel control was created? - SetWindowPos(dat->hwndContactPic, HWND_TOP, 1, ((urc->rcItem.bottom - urc->rcItem.top) - (dat->pic.cy)) / 2 + 1, //resizes it - dat->pic.cx - 2, dat->pic.cy - 2, SWP_SHOWWINDOW); - - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - - case IDC_SPLITTER: - urc->rcItem.right = urc->dlgNewSize.cx; - urc->rcItem.top -= dat->splitterY - dat->originalSplitterY; - urc->rcItem.bottom = urc->rcItem.top + 2; - OffsetRect(&urc->rcItem, 0, 1); - urc->rcItem.left = 0; - - if (dat->bUseOffset) - urc->rcItem.right -= (dat->pic.cx); // + DPISCALEX(2)); - return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; - - case IDC_MESSAGE: - urc->rcItem.right = urc->dlgNewSize.cx; - if (dat->bShowAvatar) - urc->rcItem.right -= dat->pic.cx + 2; - urc->rcItem.top -= dat->splitterY - dat->originalSplitterY; - if (bBottomToolbar&&showToolbar) - urc->rcItem.bottom -= DPISCALEY_S(22); - - if (dat->bIsAutosizingInput) - urc->rcItem.top -= DPISCALEY_S(1); - - msgTop = urc->rcItem.top; - msgBottom = urc->rcItem.bottom; - 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_BOTTOM; - - case IDC_MULTISPLITTER: - if (bInfoPanel) - urc->rcItem.top += panelHeight; - urc->rcItem.left -= dat->multiSplitterX; - urc->rcItem.right -= dat->multiSplitterX; - urc->rcItem.bottom = rcLogBottom; - return RD_ANCHORX_RIGHT | RD_ANCHORY_HEIGHT; - - case IDC_LOGFROZENTEXT: - urc->rcItem.right = urc->dlgNewSize.cx - 50; - urc->rcItem.bottom = msgTop - (bBottomToolbar ? 0 : 28); - urc->rcItem.top = msgTop - 16 - (bBottomToolbar ? 0 : 28); - if (!showToolbar && !bBottomToolbar) { - urc->rcItem.bottom += 21; - urc->rcItem.top += 21; - } - return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; - - case IDC_ADD: - urc->rcItem.bottom = msgTop - (bBottomToolbar ? 0 : 28); - urc->rcItem.top = msgTop - 18 - (bBottomToolbar ? 0 : 28); - urc->rcItem.right = urc->dlgNewSize.cx - 28; - urc->rcItem.left = urc->rcItem.right - 20; - if (!showToolbar && !bBottomToolbar) { - urc->rcItem.bottom += 21; - urc->rcItem.top += 21; - } - return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; - - case IDC_CANCELADD: - urc->rcItem.bottom = msgTop - (bBottomToolbar ? 0 : 28); - urc->rcItem.top = msgTop - 18 - (bBottomToolbar ? 0 : 28); - urc->rcItem.right = urc->dlgNewSize.cx - 4; - urc->rcItem.left = urc->rcItem.right - 20; - if (!showToolbar && !bBottomToolbar) { - urc->rcItem.bottom += 21; - urc->rcItem.top += 21; - } - return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; - - case IDC_TOGGLESIDEBAR: - return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; - - case IDC_RETRY: - case IDC_CANCELSEND: - case IDC_MSGSENDLATER: - if (fErrorState) { - urc->rcItem.bottom = msgTop - 5 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); - urc->rcItem.top = msgTop - 25 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); - } - if (!showToolbar && !bBottomToolbar) { - urc->rcItem.bottom += 21; - urc->rcItem.top += 21; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_STATICTEXT: - case IDC_STATICERRORICON: - if (fErrorState) { - urc->rcItem.bottom = msgTop - 28 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); - urc->rcItem.top = msgTop - 45 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); - } - if (!showToolbar && !bBottomToolbar) { - urc->rcItem.bottom += 21; - urc->rcItem.top += 21; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; -} - -INT_PTR CALLBACK DlgProcMessage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - TContainerData *m_pContainer = 0; - TWindowData *dat = (TWindowData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - HWND hwndTab = GetParent(hwndDlg), hwndContainer; - POINT pt, tmp, cur; - - if (dat == 0) { - if (msg == WM_ACTIVATE || msg == WM_SETFOCUS) - return 0; - } - else { - m_pContainer = dat->pContainer; - hwndContainer = m_pContainer->hwnd; - } - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - TNewWindowData *newData = (TNewWindowData*)lParam; - - dat = (TWindowData*)mir_calloc(sizeof(TWindowData)); - if (newData->iTabID >= 0) { - dat->pContainer = newData->pContainer; - m_pContainer = dat->pContainer; - hwndContainer = m_pContainer->hwnd; - } - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - - if (Utils::rtf_ctable == 0) - Utils::RTF_CTableInit(); - - dat->dwFlags |= MWF_INITMODE; - dat->bType = SESSIONTYPE_IM; - dat->fInsertMode = FALSE; - dat->fLimitedUpdate = false; - dat->Panel = new CInfoPanel(dat); - - newData->item.lParam = (LPARAM)hwndDlg; - TabCtrl_SetItem(hwndTab, newData->iTabID, &newData->item); - dat->iTabID = newData->iTabID; - dat->hwnd = hwndDlg; - - DM_ThemeChanged(dat); - - pszIDCSAVE_close = TranslateT("Close Session"); - pszIDCSAVE_save = TranslateT("Save and close session"); - - dat->hContact = newData->hContact; - dat->cache = CContactCache::getContactCache(dat->hContact); - dat->cache->updateState(); - dat->cache->setWindowData(hwndDlg, dat); - M.AddWindow(hwndDlg, dat->hContact); - BroadCastContainer(m_pContainer, DM_REFRESHTABINDEX, 0, 0); - dat->pWnd = 0; - dat->sbCustom = 0; - CProxyWindow::add(dat); - dat->szProto = const_cast(dat->cache->getProto()); - dat->bIsMeta = dat->cache->isMeta(); - if (dat->bIsMeta) - dat->cache->updateMeta(); - - dat->cache->updateUIN(); - - if (dat->hContact && dat->szProto != NULL) { - dat->wStatus = db_get_w(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE); - _tcsncpy_s(dat->szStatus, pcli->pfnGetStatusModeDescription(dat->szProto == NULL ? ID_STATUS_OFFLINE : dat->wStatus, 0), _TRUNCATE); - } - else dat->wStatus = ID_STATUS_OFFLINE; - - for (int i = 0; i < SIZEOF(btnControls); i++) - CustomizeButton(GetDlgItem(hwndDlg, btnControls[i])); - - GetMYUIN(dat); - GetClientIcon(dat); - - CustomizeButton(CreateWindowEx(0, _T("MButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 6, DPISCALEY_S(20), - hwndDlg, (HMENU)IDC_TOGGLESIDEBAR, g_hInst, NULL)); - dat->hwndPanelPicParent = CreateWindowEx(WS_EX_TOPMOST, _T("Static"), _T(""), SS_OWNERDRAW | WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, hwndDlg, (HMENU)6000, NULL, NULL); - mir_subclassWindow(dat->hwndPanelPicParent, CInfoPanel::avatarParentSubclass); - - dat->bShowUIElements = (m_pContainer->dwFlags & CNT_HIDETOOLBAR) == 0; - dat->sendMode |= M.GetByte(dat->hContact, "forceansi", 0) ? SMODE_FORCEANSI : 0; - dat->sendMode |= dat->hContact == 0 ? SMODE_MULTIPLE : 0; - dat->sendMode |= M.GetByte(dat->hContact, "no_ack", 0) ? SMODE_NOACK : 0; - - dat->hQueuedEvents = (HANDLE*)mir_calloc(sizeof(HANDLE)* EVENT_QUEUE_SIZE); - dat->iEventQueueSize = EVENT_QUEUE_SIZE; - dat->iCurrentQueueError = -1; - - // message history limit - // hHistoryEvents holds up to n event handles - dat->maxHistory = M.GetDword(dat->hContact, "maxhist", M.GetDword("maxhist", 0)); - dat->curHistory = 0; - if (dat->maxHistory) - dat->hHistoryEvents = (HANDLE*)mir_alloc(dat->maxHistory * sizeof(HANDLE)); - else - dat->hHistoryEvents = NULL; - - if (dat->bIsMeta) - SendMessage(hwndDlg, DM_UPDATEMETACONTACTINFO, 0, 0); - else - SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); - dat->bTabFlash = FALSE; - dat->mayFlashTab = FALSE; - GetMyNick(dat); - - dat->multiSplitterX = (int)M.GetDword(SRMSGMOD, "multisplit", 150); - dat->nTypeMode = PROTOTYPE_SELFTYPING_OFF; - SetTimer(hwndDlg, TIMERID_TYPE, 1000, NULL); - dat->iLastEventType = 0xffffffff; - - // load log option flags... - dat->dwFlags = dat->pContainer->theme.dwFlags; - - // consider per-contact message setting overrides - if (dat->hContact && M.GetDword(dat->hContact, "mwmask", 0)) - LoadLocalFlags(hwndDlg, dat); - - DM_InitTip(dat); - dat->Panel->getVisibility(); - - dat->dwFlagsEx |= M.GetByte(dat->hContact, "splitoverride", 0) ? MWF_SHOW_SPLITTEROVERRIDE : 0; - dat->bIsAutosizingInput = IsAutoSplitEnabled(dat); - dat->iInputAreaHeight = -1; - SetMessageLog(dat); - if (dat->hContact) { - dat->codePage = M.GetDword(dat->hContact, "ANSIcodepage", CP_ACP); - dat->Panel->loadHeight(); - } - - dat->bShowAvatar = GetAvatarVisibility(hwndDlg, dat); - - RECT rc; - GetWindowRect(GetDlgItem(hwndDlg, IDC_SMILEYBTN), &rc); - - Utils::showDlgControl(hwndDlg, IDC_MULTISPLITTER, SW_HIDE); - - GetWindowRect(GetDlgItem(hwndDlg, IDC_SPLITTER), &rc); - pt.y = (rc.top + rc.bottom) / 2; - pt.x = 0; - ScreenToClient(hwndDlg, &pt); - dat->originalSplitterY = pt.y; - if (dat->splitterY == -1) - dat->splitterY = dat->originalSplitterY + 60; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &rc); - dat->minEditBoxSize.cx = rc.right - rc.left; - dat->minEditBoxSize.cy = rc.bottom - rc.top; - - BB_InitDlgButtons(dat); - SendMessage(hwndDlg, DM_LOADBUTTONBARICONS, 0, 0); - - BOOL isThemed = TRUE; - if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKBUTTONSNPRESSED].IGNORED && - !SkinItems[ID_EXTBKBUTTONSPRESSED].IGNORED && !SkinItems[ID_EXTBKBUTTONSMOUSEOVER].IGNORED) - isThemed = FALSE; - - SendMessage(GetDlgItem(hwndDlg, IDC_ADD), BUTTONSETASFLATBTN, TRUE, 0); - SendMessage(GetDlgItem(hwndDlg, IDC_CANCELADD), BUTTONSETASFLATBTN, TRUE, 0); - - SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASTHEMEDBTN, isThemed, 0); - SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETCONTAINER, (LPARAM)m_pContainer, 0); - SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASTOOLBARBUTTON, TRUE, 0); - - TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPENING, 0); - - for (int i = 0; i < SIZEOF(tooltips); i++) - SendDlgItemMessage(hwndDlg, tooltips[i].id, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(tooltips[i].text), BATF_TCHAR); - - SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, dat->bNotOnList ? TranslateT("Contact not on list. You may add it...") : - TranslateT("Auto scrolling is disabled (press F12 to enable it)")); - - SendMessage(GetDlgItem(hwndDlg, IDC_SAVE), BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, BATF_TCHAR); - SendMessage(GetDlgItem(hwndDlg, IDC_PROTOCOL), BUTTONADDTOOLTIP, (WPARAM)TranslateT("Click for contact menu\nClick dropdown for window settings"), BATF_TCHAR); - - SetWindowText(GetDlgItem(hwndDlg, IDC_RETRY), TranslateT("Retry")); - - UINT _ctrls[] = { IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER }; - for (int i = 0; i < SIZEOF(_ctrls); i++) { - SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASPUSHBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASFLATBTN, FALSE, 0); - SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASTHEMEDBTN, TRUE, 0); - } - - SetWindowText(GetDlgItem(hwndDlg, IDC_CANCELSEND), TranslateT("Cancel")); - SetWindowText(GetDlgItem(hwndDlg, IDC_MSGSENDLATER), TranslateT("Send later")); - - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETUNDOLIMIT, 0, 0); - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_LINK); - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETEVENTMASK, 0, ENM_REQUESTRESIZE | ENM_MOUSEEVENTS | ENM_SCROLL | ENM_KEYEVENTS | ENM_CHANGE); - - dat->bActualHistory = M.GetByte(dat->hContact, "ActualHistory", 0); - - /* OnO: higligh lines to their end */ - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); - - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETLANGOPTIONS, 0, SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOFONTSIZEADJUST); - - // add us to the tray list (if it exists) - if (PluginConfig.g_hMenuTrayUnread != 0 && dat->hContact != 0 && dat->szProto != NULL) - UpdateTrayMenu(0, dat->wStatus, dat->szProto, dat->szStatus, dat->hContact, FALSE); - - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_AUTOURLDETECT, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXLIMITTEXT, 0, 0x80000000); - - // subclassing stuff - mir_subclassWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), MessageEditSubclassProc); - mir_subclassWindow(GetDlgItem(hwndDlg, IDC_CONTACTPIC), AvatarSubclassProc); - mir_subclassWindow(GetDlgItem(hwndDlg, IDC_SPLITTER), SplitterSubclassProc); - mir_subclassWindow(GetDlgItem(hwndDlg, IDC_MULTISPLITTER), SplitterSubclassProc); - mir_subclassWindow(GetDlgItem(hwndDlg, IDC_PANELSPLITTER), SplitterSubclassProc); - - // load old messages from history (if wanted...) - dat->cache->updateStats(TSessionStats::INIT_TIMER); - if (dat->hContact) { - FindFirstEvent(dat); - dat->nMax = dat->cache->getMaxMessageLength(); - } - LoadContactAvatar(dat); - SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); - LoadOwnAvatar(dat); - - // restore saved msg if any... - if (dat->hContact) { - ptrT tszSavedMsg(db_get_tsa(dat->hContact, SRMSGMOD, "SavedMsg")); - if (tszSavedMsg != 0) { - SETTEXTEX stx = { ST_DEFAULT, 1200 }; - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, tszSavedMsg); - SendQueue::UpdateSaveAndSendButton(dat); - if (m_pContainer->hwndActive == hwndDlg) - UpdateReadChars(dat); - } - } - if (newData->szInitialText) { - if (newData->isWchar) - SetDlgItemTextW(hwndDlg, IDC_MESSAGE, (TCHAR*)newData->szInitialText); - else - SetDlgItemTextA(hwndDlg, IDC_MESSAGE, newData->szInitialText); - int len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)); - PostMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_SETSEL, len, len); - if (len) - EnableSendButton(dat, TRUE); - } - - for (HANDLE hdbEvent = db_event_last(dat->hContact); hdbEvent; hdbEvent = db_event_prev(dat->hContact, hdbEvent)) { - DBEVENTINFO dbei = { sizeof(dbei) }; - db_event_get(hdbEvent, &dbei); - if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { - dat->lastMessage = dbei.timestamp; - DM_UpdateLastMessage(dat); - break; - } - } - - SendMessage(hwndContainer, DM_QUERYCLIENTAREA, 0, (LPARAM)&rc); - - WNDCLASS wndClass = { 0 }; - GetClassInfo(g_hInst, _T("RICHEDIT50W"), &wndClass); - mir_subclassWindowFull(GetDlgItem(hwndDlg, IDC_LOG), MessageLogSubclassProc, wndClass.lpfnWndProc); - - SetWindowPos(hwndDlg, 0, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), newData->iActivate ? 0 : SWP_NOZORDER | SWP_NOACTIVATE); - LoadSplitter(dat); - ShowPicture(dat, TRUE); - - if (m_pContainer->dwFlags & CNT_CREATE_MINIMIZED || !newData->iActivate || m_pContainer->dwFlags & CNT_DEFERREDTABSELECT) { - dat->iFlashIcon = PluginConfig.g_IconMsgEvent; - SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL); - dat->mayFlashTab = true; - - DBEVENTINFO dbei = { 0 }; - dbei.eventType = EVENTTYPE_MESSAGE; - FlashOnClist(hwndDlg, dat, dat->hDbEventFirst, &dbei); - - SendMessage(hwndContainer, DM_SETICON, (WPARAM)dat, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_MESSAGE)); - m_pContainer->dwFlags |= CNT_NEED_UPDATETITLE; - dat->dwFlags |= MWF_NEEDCHECKSIZE | MWF_WASBACKGROUNDCREATE | MWF_DEFERREDSCROLL; - } - - if (newData->iActivate) { - m_pContainer->hwndActive = hwndDlg; - ShowWindow(hwndDlg, SW_SHOW); - SetActiveWindow(hwndDlg); - SetForegroundWindow(hwndDlg); - } - else if (m_pContainer->dwFlags & CNT_CREATE_MINIMIZED) { - dat->dwFlags |= MWF_DEFERREDSCROLL; - ShowWindow(hwndDlg, SW_SHOWNOACTIVATE); - m_pContainer->hwndActive = hwndDlg; - m_pContainer->dwFlags |= CNT_DEFERREDCONFIGURE; - } - PostMessage(hwndContainer, DM_UPDATETITLE, dat->hContact, 0); - - DM_RecalcPictureSize(dat); - dat->dwLastActivity = GetTickCount() - 1000; - m_pContainer->dwLastActivity = dat->dwLastActivity; - - if (dat->hwndHPP) - mir_subclassWindow(dat->hwndHPP, HPPKFSubclassProc); - - dat->dwFlags &= ~MWF_INITMODE; - TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPEN, 0); - - // show a popup if wanted... - if (newData->bWantPopup) { - DBEVENTINFO dbei = { sizeof(dbei) }; - newData->bWantPopup = FALSE; - db_event_get(newData->hdbEvent, &dbei); - tabSRMM_ShowPopup(dat->hContact, newData->hdbEvent, dbei.eventType, 0, 0, hwndDlg, dat->cache->getActiveProto(), dat); - } - if (m_pContainer->dwFlags & CNT_CREATE_MINIMIZED) { - m_pContainer->dwFlags &= ~CNT_CREATE_MINIMIZED; - m_pContainer->hwndActive = hwndDlg; - return FALSE; - } - return newData->iActivate != 0; - } - - case WM_ERASEBKGND: - { - HDC hdc = (HDC)wParam; - - RECT rcClient, rcWindow, rc; - HDC hdcMem = 0; - HBITMAP hbm, hbmOld; - HANDLE hpb = 0; - - GetClientRect(hwndDlg, &rcClient); - DWORD cx = rcClient.right - rcClient.left; - DWORD cy = rcClient.bottom - rcClient.top; - - if (CMimAPI::m_haveBufferedPaint) - hpb = CMimAPI::m_pfnBeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, 0, &hdcMem); - else { - hdcMem = CreateCompatibleDC(hdc); - hbm = CSkin::CreateAeroCompatibleBitmap(rcClient, hdc); - hbmOld = (HBITMAP)SelectObject(hdcMem, hbm); - } - - bool bInfoPanel = dat->Panel->isActive(); - bool bAero = M.isAero(); - - if (CSkin::m_skinEnabled) { - UINT item_ids[2] = {ID_EXTBKHISTORY, ID_EXTBKINPUTAREA}; - UINT ctl_ids[2] = {IDC_LOG, IDC_MESSAGE}; - BOOL isEditNotesReason = dat->fEditNotesActive; - BOOL isSendLaterReason = (dat->sendMode & SMODE_SENDLATER); - BOOL isMultipleReason = (dat->sendMode & SMODE_MULTIPLE || dat->sendMode & SMODE_CONTAINER); - - CSkin::SkinDrawBG(hwndDlg, hwndContainer, m_pContainer, &rcClient, hdcMem); - - for (int i=0; i < 2; i++) { - CSkinItem *item = &SkinItems[item_ids[i]]; - if (!item->IGNORED) { - GetWindowRect(GetDlgItem(hwndDlg, ctl_ids[i]), &rcWindow); - pt.x = rcWindow.left; - pt.y = rcWindow.top; - ScreenToClient(hwndDlg, &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; - if (item_ids[i] == ID_EXTBKINPUTAREA && (isMultipleReason || isEditNotesReason || isSendLaterReason)) { - HBRUSH br = CreateSolidBrush(isMultipleReason ? RGB(255, 130, 130) : (isEditNotesReason ? RGB(80, 255, 80) : RGB(80, 80, 255))); - FillRect(hdcMem, &rc, br); - DeleteObject(br); - } - else CSkin::DrawItem(hdcMem, &rc, item); - } - } - } - else { - CSkin::FillBack(hdcMem, &rcClient); - - if (M.isAero()) { - LONG temp = rcClient.bottom; - rcClient.bottom = dat->Panel->isActive() ? dat->Panel->getHeight() + 5 : 5; - FillRect(hdcMem, &rcClient, (HBRUSH)GetStockObject(BLACK_BRUSH)); - rcClient.bottom = temp; - } - } - - // draw the (new) infopanel background. Use the gradient from the statusitem. - GetClientRect(hwndDlg, &rc); - dat->Panel->renderBG(hdcMem, rc, &SkinItems[ID_EXTBKINFOPANELBG], bAero); - - // draw aero related stuff - if (!CSkin::m_skinEnabled) - CSkin::RenderToolbarBG(dat, hdcMem, rcClient); - - // render info panel fields - dat->Panel->renderContent(hdcMem); - - if (hpb) - CSkin::FinalizeBufferedPaint(hpb, &rcClient); - else { - BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY); - SelectObject(hdcMem, hbmOld); - DeleteObject(hbm); - DeleteDC(hdcMem); - } - if (!dat->fLimitedUpdate) - SetAeroMargins(dat->pContainer); - } - return 1; - - case WM_NCPAINT: - return 0; - - case WM_PAINT: - // in skinned mode only, draw the background elements for the 2 richedit controls - // this allows border-less textboxes to appear "skinned" and blended with the background - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwndDlg, &ps); - EndPaint(hwndDlg, &ps); - } - return 0; - - case WM_SIZE: - if (!IsIconic(hwndDlg)) { - if (dat->ipFieldHeight == 0) - dat->ipFieldHeight = CInfoPanel::m_ipConfig.height2; - - if (dat->pContainer->uChildMinHeight > 0 && HIWORD(lParam) >= dat->pContainer->uChildMinHeight) { - if (dat->splitterY > HIWORD(lParam) - DPISCALEY_S(MINLOGHEIGHT)) { - dat->splitterY = HIWORD(lParam) - DPISCALEY_S(MINLOGHEIGHT); - dat->dynaSplitter = dat->splitterY - DPISCALEY_S(34); - DM_RecalcPictureSize(dat); - } - if (dat->splitterY < DPISCALEY_S(MINSPLITTERY)) - LoadSplitter(dat); - } - - HBITMAP hbm = ((dat->Panel->isActive()) && m_pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown); - if (hbm != 0) { - BITMAP bminfo; - GetObject(hbm, sizeof(bminfo), &bminfo); - CalcDynamicAvatarSize(dat, &bminfo); - } - - RECT rc; - GetClientRect(hwndDlg, &rc); - - UTILRESIZEDIALOG urd = { sizeof(urd) }; - urd.hInstance = g_hInst; - urd.hwndDlg = hwndDlg; - urd.lParam = (LPARAM)dat; - urd.lpTemplate = MAKEINTRESOURCEA(IDD_MSGSPLITNEW); - urd.pfnResizer = MessageDialogResize; - CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); - - BB_SetButtonsPos(dat); - - // size info panel fields - if (dat->Panel->isActive()) { - LONG cx = rc.right; - LONG panelHeight = dat->Panel->getHeight(); - - hbm = (m_pContainer->avatarMode == 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown); - double dHeight = 0, dWidth = 0; - Utils::scaleAvatarHeightLimited(hbm, dWidth, dHeight, panelHeight-2); - dat->iPanelAvatarX = (int)dWidth; - dat->iPanelAvatarY = (int)dHeight; - - rc.top = 1; - rc.left = cx - dat->iPanelAvatarX; - rc.bottom = panelHeight - (CSkin::m_bAvatarBorderType ? 2 : 0); - rc.right = cx; - dat->rcPic = rc; - - if (dat->bShowInfoAvatar) { - SetWindowPos(dat->hwndPanelPicParent, HWND_TOP, rc.left - 2, rc.top, rc.right - rc.left, rc.bottom - rc.top + 1, 0); - ShowWindow(dat->hwndPanelPicParent, (dat->iPanelAvatarX == 0) || !dat->Panel->isActive() ? SW_HIDE : SW_SHOW); - } - else { - ShowWindow(dat->hwndPanelPicParent, SW_HIDE); - dat->iPanelAvatarX = dat->iPanelAvatarY = 0; - } - - rc.right = cx - dat->iPanelAvatarX; - rc.left = rc.right - dat->panelStatusCX; - rc.bottom = panelHeight - 3; - rc.top = rc.bottom - dat->ipFieldHeight; - dat->rcStatus = rc; - - rc.left = CInfoPanel::LEFT_OFFSET_LOGO; - rc.right = cx - dat->iPanelAvatarX - (panelHeight < CInfoPanel::DEGRADE_THRESHOLD ? (dat->rcStatus.right - dat->rcStatus.left) + 3 : 0); - rc.bottom = panelHeight - (panelHeight >= CInfoPanel::DEGRADE_THRESHOLD ? dat->ipFieldHeight : 0) - 1; - rc.top = 1; - dat->rcNick = rc; - - rc.left = CInfoPanel::LEFT_OFFSET_LOGO; - rc.right = cx - (dat->iPanelAvatarX + 2) - dat->panelStatusCX; - rc.bottom = panelHeight - 3; - rc.top = rc.bottom - dat->ipFieldHeight; - dat->rcUIN = rc; - - dat->Panel->Invalidate(); - } - - if (GetDlgItem(hwndDlg, IDC_CLIST) != 0) { - RECT rc, rcClient, rcLog; - GetClientRect(hwndDlg, &rcClient); - GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rcLog); - rc.top = 0; - rc.right = rcClient.right; - rc.left = rcClient.right - dat->multiSplitterX; - rc.bottom = rcLog.bottom; - if (dat->Panel->isActive()) - rc.top += (dat->Panel->getHeight() + 1); - MoveWindow(GetDlgItem(hwndDlg, IDC_CLIST), rc.left, rc.top, rc.right - rc.left, rcLog.bottom - rcLog.top, FALSE); - } - - if (dat->hwndIEView || dat->hwndHPP) - ResizeIeView(dat, 0, 0, 0, 0); - - DetermineMinHeight(dat); - } - break; - - case WM_TIMECHANGE: - PostMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); - break; - - case WM_NOTIFY: - if (dat != 0 && ((NMHDR*)lParam)->hwndFrom == dat->hwndTip) { - if (((NMHDR*)lParam)->code == NM_CLICK) - SendMessage(dat->hwndTip, TTM_TRACKACTIVATE, FALSE, 0); - break; - } - - switch (((NMHDR*)lParam)->idFrom) { - case IDC_LOG: - case IDC_MESSAGE: - switch (((NMHDR*)lParam)->code) { - case EN_MSGFILTER: - { - DWORD msg = ((MSGFILTER *) lParam)->msg; - WPARAM wp = ((MSGFILTER *) lParam)->wParam; - LPARAM lp = ((MSGFILTER *) lParam)->lParam; - CHARFORMAT2 cf2; - BOOL isCtrl, isShift, isAlt; - KbdState(dat, isShift, isCtrl, isAlt); - - MSG message; - message.hwnd = hwndDlg; - message.message = msg; - message.lParam = lp; - message.wParam = wp; - - if (msg == WM_SYSKEYUP) { - UINT ctrlId = 0; - - if (wp == VK_MENU) - if (!dat->fkeyProcessed && !(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000) && !(lp & (1 << 24))) - m_pContainer->MenuBar->autoShow(); - - return(_dlgReturn(hwndDlg, 0)); - } - - if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && !(GetKeyState(VK_RMENU) & 0x8000)) { - LRESULT mim_hotkey_check = CallService(MS_HOTKEY_CHECK, (WPARAM)&message, (LPARAM)(TABSRMM_HK_SECTION_IM)); - if (mim_hotkey_check) - dat->fkeyProcessed = true; - - switch (mim_hotkey_check) { - case TABSRMM_HK_SETUSERPREFS: - CallService(MS_TABMSG_SETUSERPREFS, dat->hContact, 0); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_NUDGE: - SendNudge(dat); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_SENDFILE: - CallService(MS_FILE_SENDFILE, dat->hContact, 0); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_QUOTEMSG: - SendMessage(hwndDlg, WM_COMMAND, IDC_QUOTE, 0); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_USERMENU: - SendMessage(hwndDlg, WM_COMMAND, IDC_PROTOCOL, 0); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_USERDETAILS: - SendMessage(hwndDlg, WM_COMMAND, MAKELONG(IDC_NAME, BN_CLICKED), 0); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_EDITNOTES: - PostMessage(hwndDlg, WM_COMMAND, MAKELONG(IDC_PIC, BN_CLICKED), 0); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_TOGGLESENDLATER: - if (sendLater->isAvail()) { - dat->sendMode ^= SMODE_SENDLATER; - SetWindowPos(GetDlgItem(hwndDlg, IDC_MESSAGE), 0, 0, 0, 0, 0, SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_NOZORDER| - SWP_NOMOVE|SWP_NOSIZE|SWP_NOCOPYBITS); - RedrawWindow(hwndDlg, 0, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW|RDW_ALLCHILDREN); - } - else - CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK|MB_ICONINFORMATION, TranslateT("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0 and \\b1 send to multiple contacts\\b0 features depend on it.\n\nYou must enable it under \\b1Options->Message Sessions->Advanced tweaks\\b0. Changing this option requires a restart.")); - return(_dlgReturn(hwndDlg, 1)); - case TABSRMM_HK_TOGGLERTL: - { - DWORD dwGlobal = M.GetDword("mwflags", MWF_LOG_DEFAULT); - DWORD dwOldFlags = dat->dwFlags; - DWORD dwMask = M.GetDword(dat->hContact, "mwmask", 0); - DWORD dwFlags = M.GetDword(dat->hContact, "mwflags", 0); - - dat->dwFlags ^= MWF_LOG_RTL; - if ((dwGlobal & MWF_LOG_RTL) != (dat->dwFlags & MWF_LOG_RTL)) { - dwMask |= MWF_LOG_RTL; - dwFlags |= (dat->dwFlags & MWF_LOG_RTL); - } - else { - dwMask &= ~MWF_LOG_RTL; - dwFlags &= ~MWF_LOG_RTL; - } - if (dwMask) { - db_set_dw(dat->hContact, SRMSGMOD_T, "mwmask", dwMask); - db_set_dw(dat->hContact, SRMSGMOD_T, "mwflags", dwFlags); - } - else { - db_unset(dat->hContact, SRMSGMOD_T, "mwmask"); - db_unset(dat->hContact, SRMSGMOD_T, "mwflags"); - } - SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); - SendMessage(hwndDlg, DM_DEFERREDREMAKELOG, (WPARAM)hwndDlg, 0); - } - return(_dlgReturn(hwndDlg, 1)); - - case TABSRMM_HK_TOGGLEMULTISEND: - dat->sendMode ^= SMODE_MULTIPLE; - if (dat->sendMode & SMODE_MULTIPLE) - DM_CreateClist(dat); - else if (IsWindow(GetDlgItem(hwndDlg, IDC_CLIST))) - DestroyWindow(GetDlgItem(hwndDlg, IDC_CLIST)); - - HWND hwndEdit = GetDlgItem(hwndDlg, IDC_MESSAGE); - SetWindowPos(hwndEdit, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - RedrawWindow(hwndEdit, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ERASE); - DM_ScrollToBottom(dat, 0, 0); - Utils::showDlgControl(hwndDlg, IDC_MULTISPLITTER, (dat->sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); - Utils::showDlgControl(hwndDlg, IDC_CLIST, (dat->sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); - if (dat->sendMode & SMODE_MULTIPLE) - SetFocus(GetDlgItem(hwndDlg, IDC_CLIST)); - else - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - RedrawWindow(hwndDlg, 0, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW|RDW_ALLCHILDREN); - return(_dlgReturn(hwndDlg, 1)); - } - if (DM_GenericHotkeysCheck(&message, dat)) { - dat->fkeyProcessed = true; - return(_dlgReturn(hwndDlg, 1)); - } - } - if (wp == VK_BROWSER_BACK || wp == VK_BROWSER_FORWARD) - return 1; - - if (msg == WM_CHAR) { - if (isCtrl && !isShift && !isAlt) { - switch (wp) { - case 23: // ctrl - w - PostMessage(hwndDlg, WM_CLOSE, 1, 0); - break; - case 19: - PostMessage(hwndDlg, WM_COMMAND, IDC_SENDMENU, IDC_SENDMENU); - break; - case 16: - PostMessage(hwndDlg, WM_COMMAND, IDC_PROTOMENU, IDC_PROTOMENU); - break; - case 20: - PostMessage(hwndDlg, WM_COMMAND, IDC_TOGGLETOOLBAR, 1); - break; - } - return 1; - } - } - if (msg == WM_KEYDOWN) { - if ((wp == VK_INSERT && isShift && !isCtrl) || (wp == 'V' && isCtrl && !isShift && !isAlt)) { - SendMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_PASTESPECIAL, CF_UNICODETEXT, 0); - _clrMsgFilter(lParam); - return(_dlgReturn(hwndDlg, 1)); - } - if (isCtrl && isShift) { - if (wp == 0x9) { // ctrl-shift tab - SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_PREV, 0); - _clrMsgFilter(lParam); - return(_dlgReturn(hwndDlg, 1)); - } - } - if (isCtrl && !isShift && !isAlt) { - if (wp == VK_TAB) { - SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_NEXT, 0); - _clrMsgFilter(lParam); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_F4) { - PostMessage(hwndDlg, WM_CLOSE, 1, 0); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_PRIOR) { - SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_PREV, 0); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_NEXT) { - SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_NEXT, 0); - return(_dlgReturn(hwndDlg, 1)); - } - } - } - if (msg == WM_SYSKEYDOWN && isAlt) { - if (wp == 0x52) { - SendMessage(hwndDlg, DM_QUERYPENDING, DM_QUERY_MOSTRECENT, 0); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_MULTIPLY) { - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_DIVIDE) { - SetFocus(GetDlgItem(hwndDlg, IDC_LOG)); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_ADD) { - SendMessage(hwndContainer, DM_SELECTTAB, DM_SELECT_NEXT, 0); - return(_dlgReturn(hwndDlg, 1)); - } - if (wp == VK_SUBTRACT) { - SendMessage(hwndContainer, DM_SELECTTAB, DM_SELECT_PREV, 0); - return(_dlgReturn(hwndDlg, 1)); - } - } - - if (msg == WM_KEYDOWN && wp == VK_F12) { - if (isShift || isCtrl || isAlt) - return(_dlgReturn(hwndDlg, 1)); - if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) - SendMessage(hwndDlg, DM_REPLAYQUEUE, 0, 0); - dat->dwFlagsEx ^= MWF_SHOW_SCROLLINGDISABLED; - Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, (dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? SW_SHOW : SW_HIDE); - if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)) - SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, TranslateT("Contact not on list. You may add it...")); - else - SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, TranslateT("Auto scrolling is disabled (press F12 to enable it)")); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - DM_ScrollToBottom(dat, 1, 1); - return(_dlgReturn(hwndDlg, 1)); - } - //MAD: tabulation mod - if (msg == WM_KEYDOWN && wp == VK_TAB) { - if (PluginConfig.m_AllowTab) { - if (((NMHDR*)lParam)->idFrom == IDC_MESSAGE) - SendMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_REPLACESEL, FALSE, (LPARAM)"\t"); - _clrMsgFilter(lParam); - if (((NMHDR*)lParam)->idFrom != IDC_MESSAGE) - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - return(_dlgReturn(hwndDlg, 1)); - } - else { - if (((NMHDR*)lParam)->idFrom == IDC_MESSAGE) { - if (GetSendButtonState(hwndDlg) != PBS_DISABLED && !(dat->pContainer->dwFlags & CNT_HIDETOOLBAR)) - SetFocus(GetDlgItem(hwndDlg, IDOK)); - else - SetFocus(GetDlgItem(hwndDlg, IDC_LOG)); - return(_dlgReturn(hwndDlg, 1)); - } - if (((NMHDR*)lParam)->idFrom == IDC_LOG) { - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - return(_dlgReturn(hwndDlg, 1)); - } - } - return(_dlgReturn(hwndDlg, 0)); - } - - if (msg == WM_MOUSEWHEEL && (((NMHDR*)lParam)->idFrom == IDC_LOG || ((NMHDR*)lParam)->idFrom == IDC_MESSAGE)) { - RECT rc; - GetCursorPos(&pt); - GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rc); - if (PtInRect(&rc, pt)) { - short wDirection = (short)HIWORD(wp); - if (LOWORD(wp) & MK_SHIFT) { - if (wDirection < 0) - SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0); - else if (wDirection > 0) - SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0); - return 0; - } - return 0; - } - return 1; - } - - if (msg == WM_CHAR && wp == 'c') { - if (isCtrl) { - SendDlgItemMessage(hwndDlg, ((NMHDR*)lParam)->code, WM_COPY, 0, 0); - break; - } - } - if ((msg == WM_LBUTTONDOWN || msg == WM_KEYUP || msg == WM_LBUTTONUP) && ((NMHDR*)lParam)->idFrom == IDC_MESSAGE) { - int bBold = IsDlgButtonChecked(hwndDlg, IDC_FONTBOLD); - int bItalic = IsDlgButtonChecked(hwndDlg, IDC_FONTITALIC); - int bUnder = IsDlgButtonChecked(hwndDlg, IDC_FONTUNDERLINE); - int bStrikeout = IsDlgButtonChecked(hwndDlg, IDC_FONTSTRIKEOUT); - - cf2.cbSize = sizeof(CHARFORMAT2); - cf2.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_UNDERLINETYPE | CFM_STRIKEOUT; - cf2.dwEffects = 0; - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); - if (cf2.dwEffects & CFE_BOLD) { - if (bBold == BST_UNCHECKED) - CheckDlgButton(hwndDlg, IDC_FONTBOLD, BST_CHECKED); - } - else if (bBold == BST_CHECKED) - CheckDlgButton(hwndDlg, IDC_FONTBOLD, BST_UNCHECKED); - - if (cf2.dwEffects & CFE_ITALIC) { - if (bItalic == BST_UNCHECKED) - CheckDlgButton(hwndDlg, IDC_FONTITALIC, BST_CHECKED); - } - else if (bItalic == BST_CHECKED) - CheckDlgButton(hwndDlg, IDC_FONTITALIC, BST_UNCHECKED); - - if (cf2.dwEffects & CFE_UNDERLINE && (cf2.bUnderlineType & CFU_UNDERLINE || cf2.bUnderlineType & CFU_UNDERLINEWORD)) { - if (bUnder == BST_UNCHECKED) - CheckDlgButton(hwndDlg, IDC_FONTUNDERLINE, BST_CHECKED); - } - else if (bUnder == BST_CHECKED) - CheckDlgButton(hwndDlg, IDC_FONTUNDERLINE, BST_UNCHECKED); - - if (cf2.dwEffects & CFE_STRIKEOUT) { - if (bStrikeout == BST_UNCHECKED) - CheckDlgButton(hwndDlg, IDC_FONTSTRIKEOUT, BST_CHECKED); - } - else if (bStrikeout == BST_CHECKED) - CheckDlgButton(hwndDlg, IDC_FONTSTRIKEOUT, BST_UNCHECKED); - } - switch (msg) { - case WM_LBUTTONDOWN: - { - HCURSOR hCur = GetCursor(); - m_pContainer->MenuBar->Cancel(); - if (hCur == LoadCursor(NULL, IDC_SIZENS) || hCur == LoadCursor(NULL, IDC_SIZEWE) - || hCur == LoadCursor(NULL, IDC_SIZENESW) || hCur == LoadCursor(NULL, IDC_SIZENWSE)) { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); - return TRUE; - } - break; - } - - // auto-select-and-copy handling... - // if enabled, releasing the lmb with an active selection automatically copies the selection - // to the clipboard. - // holding ctrl while releasing the button pastes the selection to the input area, using plain text - // holding ctrl-alt does the same, but pastes formatted text - case WM_LBUTTONUP: - if (((NMHDR*) lParam)->idFrom == IDC_LOG) { - CHARRANGE cr; - SendMessage(GetDlgItem(hwndDlg, IDC_LOG), EM_EXGETSEL, 0, (LPARAM)&cr); - if (cr.cpMax != cr.cpMin) { - cr.cpMin = cr.cpMax; - if (isCtrl && M.GetByte("autocopy", 1)) { - SETTEXTEX stx = {ST_KEEPUNDO | ST_SELECTION, CP_UTF8}; - char *streamOut = NULL; - if (isAlt) - streamOut = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, (CP_UTF8 << 16) | (SF_RTFNOOBJS | SFF_PLAINRTF | SFF_SELECTION | SF_USECODEPAGE)); - else - streamOut = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, (CP_UTF8 << 16) | (SF_TEXT | SFF_SELECTION | SF_USECODEPAGE)); - if (streamOut) { - Utils::FilterEventMarkers(streamOut); - SendMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)streamOut); - mir_free(streamOut); - } - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - } - else if (M.GetByte("autocopy", 1) && !isShift) { - SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_COPY, 0, 0); - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - if (m_pContainer->hwndStatus) - SendMessage(m_pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM)TranslateT("Selection copied to clipboard")); - } - } - } - break; - - case WM_MOUSEMOVE: - POINT pt; - HCURSOR hCur = GetCursor(); - GetCursorPos(&pt); - DM_DismissTip(dat, pt); - dat->Panel->trackMouse(pt); - if (hCur == LoadCursor(NULL, IDC_SIZENS) || hCur == LoadCursor(NULL, IDC_SIZEWE) || hCur == LoadCursor(NULL, IDC_SIZENESW) || hCur == LoadCursor(NULL, IDC_SIZENWSE)) - SetCursor(LoadCursor(NULL, IDC_ARROW)); - break; - } - } - break; - - case EN_REQUESTRESIZE: - DM_HandleAutoSizeRequest(dat, (REQRESIZE *)lParam); - break; - - case EN_LINK: - switch (((ENLINK *)lParam)->msg) { - case WM_SETCURSOR: - SetCursor(PluginConfig.hCurHyperlinkHand); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); - return TRUE; - - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - { - CHARRANGE sel; - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) - break; - - TEXTRANGEW tr; - tr.chrg = ((ENLINK*)lParam)->chrg; - tr.lpstrText = (TCHAR*)_alloca(sizeof(TCHAR)*(tr.chrg.cpMax - tr.chrg.cpMin + 8)); - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (_tcschr(tr.lpstrText, '@') != NULL && _tcschr(tr.lpstrText, ':') == NULL && _tcschr(tr.lpstrText, '/') == NULL) { - MoveMemory(tr.lpstrText + 7, tr.lpstrText, tr.chrg.cpMax - tr.chrg.cpMin + 1); - CopyMemory(tr.lpstrText, _T("mailto:"), 7); - } - if (!IsStringValidLink(tr.lpstrText)) - break; - - if (((ENLINK*)lParam)->msg != WM_RBUTTONDOWN) { - CallService(MS_UTILS_OPENURL, OUF_TCHAR, (LPARAM)tr.lpstrText); - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - break; - } - HMENU hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); - HMENU hSubMenu = GetSubMenu(hMenu, 1); - TranslateMenu(hSubMenu); - pt.x = (short)LOWORD(((ENLINK*)lParam)->lParam); - pt.y = (short)HIWORD(((ENLINK*)lParam)->lParam); - ClientToScreen(((NMHDR*)lParam)->hwndFrom, &pt); - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL)) { - case IDM_OPENNEW: - CallService(MS_UTILS_OPENURL, 1 + OUF_TCHAR, (LPARAM)tr.lpstrText); - break; - - case IDM_OPENEXISTING: - CallService(MS_UTILS_OPENURL, OUF_TCHAR, (LPARAM)tr.lpstrText); - break; - - case IDM_COPYLINK: - if (!OpenClipboard(hwndDlg)) - break; - - EmptyClipboard(); - HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(TCHAR)*(lstrlen(tr.lpstrText) + 1)); - TCHAR *buf = (TCHAR*)GlobalLock(hData); - lstrcpy(buf, tr.lpstrText); - GlobalUnlock(hData); - SetClipboardData(CF_UNICODETEXT, hData); - CloseClipboard(); - break; - } - DestroyMenu(hMenu); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - } - } - break; - - case DM_TYPING: - { - int preTyping = dat->nTypeSecs != 0; - dat->nTypeSecs = (int)lParam > 0 ? (int)lParam : 0; - - if (dat->nTypeSecs) - dat->bShowTyping = 0; - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, preTyping); - } - return TRUE; - - case DM_UPDATEWINICON: - if (dat->hXStatusIcon) { - DestroyIcon(dat->hXStatusIcon); - dat->hXStatusIcon = 0; - } - - if (LPCSTR szProto = dat->cache->getProto()) { - dat->hTabIcon = dat->hTabStatusIcon = MY_GetContactIcon(dat, "MetaiconTab"); - if (M.GetByte("use_xicons", 1)) - dat->hXStatusIcon = GetXStatusIcon(dat); - - SendDlgItemMessage(hwndDlg, IDC_PROTOCOL, BUTTONSETASDIMMED, (dat->dwFlagsEx & MWF_SHOW_ISIDLE) != 0, 0); - SendDlgItemMessage(hwndDlg, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : MY_GetContactIcon(dat, "MetaiconBar"))); - - if (m_pContainer->hwndActive == hwndDlg) - SendMessage(m_pContainer->hwnd, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabIcon)); - - if (dat->pWnd) - dat->pWnd->updateIcon(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabIcon); - } - return 0; - - // configures the toolbar only... if lParam != 0, then it also calls - // SetDialogToType() to reconfigure the message window - case DM_CONFIGURETOOLBAR: - dat->bShowUIElements = m_pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1; - - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_SPLITTER), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_SPLITTER), GWL_EXSTYLE) & ~WS_EX_STATICEDGE); - - if (lParam == 1) { - GetSendFormat(dat, 1); - SetDialogToType(hwndDlg); - } - - if (lParam == 1) { - DM_RecalcPictureSize(dat); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - DM_ScrollToBottom(dat, 0, 1); - } - return 0; - - case DM_LOADBUTTONBARICONS: - for (int i=0; i < SIZEOF(buttonicons); i++) { - SendDlgItemMessage(hwndDlg, buttonicons[i].id, BM_SETIMAGE, IMAGE_ICON, (LPARAM)*buttonicons[i].pIcon); - SendDlgItemMessage(hwndDlg, buttonicons[i].id, BUTTONSETCONTAINER, (LPARAM)m_pContainer, 0); - } - - BB_UpdateIcons(hwndDlg, dat); - SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); - return 0; - - case DM_OPTIONSAPPLIED: - DM_OptionsApplied(dat, wParam, lParam); - return 0; - - case DM_UPDATETITLE: - DM_UpdateTitle(dat, wParam, lParam); - return 0; - - case DM_UPDATESTATUSMSG: - dat->Panel->Invalidate(); - return 0; - - case DM_OWNNICKCHANGED: - GetMyNick(dat); - return 0; - - case DM_ADDDIVIDER: - if (!(dat->dwFlags & MWF_DIVIDERSET) && PluginConfig.m_UseDividers) { - if (GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_LOG)) > 0) { - dat->dwFlags |= MWF_DIVIDERWANTED; - dat->dwFlags |= MWF_DIVIDERSET; - } - } - return 0; - - case WM_SETFOCUS: - MsgWindowUpdateState(dat, WM_SETFOCUS); - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - return 1; - - case WM_ACTIVATE: - if (LOWORD(wParam) != WA_ACTIVE) - break; - - //fall through - case WM_MOUSEACTIVATE: - MsgWindowUpdateState(dat, WM_ACTIVATE); - return 1; - - case DM_UPDATEPICLAYOUT: - LoadContactAvatar(dat); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - return 0; - - case DM_SPLITTERGLOBALEVENT: - DM_SplitterGlobalEvent(dat, wParam, lParam); - return 0; - - case DM_SPLITTERMOVED: - if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_MULTISPLITTER)) { - RECT rc; - GetClientRect(hwndDlg, &rc); - pt.x = wParam; - pt.y = 0; - ScreenToClient(hwndDlg, &pt); - int oldSplitterX = dat->multiSplitterX; - dat->multiSplitterX = rc.right - pt.x; - if (dat->multiSplitterX < 25) - dat->multiSplitterX = 25; - - if (dat->multiSplitterX > ((rc.right - rc.left) - 80)) - dat->multiSplitterX = oldSplitterX; - SendMessage(dat->hwnd, WM_SIZE, 0, 0); - } - else if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SPLITTER)) { - int oldSplitterY, oldDynaSplitter; - int bottomtoolbarH=0; - RECT rc; - GetClientRect(hwndDlg, &rc); - rc.top += (dat->Panel->isActive() ? dat->Panel->getHeight() + 40 : 30); - pt.x = 0; - pt.y = wParam; - ScreenToClient(hwndDlg, &pt); - - oldSplitterY = dat->splitterY; - oldDynaSplitter = dat->dynaSplitter; - - dat->splitterY = rc.bottom - pt.y +DPISCALEY_S(23); - - // attempt to fix splitter troubles.. - // hardcoded limits... better solution is possible, but this works for now - if (dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR) - bottomtoolbarH = 22; - - if (dat->splitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) { // min splitter size - dat->splitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH); - dat->dynaSplitter = dat->splitterY - DPISCALEY_S(34); - DM_RecalcPictureSize(dat); - } - else if (dat->splitterY > (rc.bottom - rc.top)) { - dat->splitterY = oldSplitterY; - dat->dynaSplitter = oldDynaSplitter; - DM_RecalcPictureSize(dat); - } - else { - dat->dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11); - DM_RecalcPictureSize(dat); - } - CSkin::UpdateToolbarBG(dat); - SendMessage(dat->hwnd, WM_SIZE, 0, 0); - } - else if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_PANELSPLITTER)) { - RECT rc; - GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rc); - - POINT pt = { 0, wParam }; - ScreenToClient(hwndDlg, &pt); - if ((pt.y + 2 >= MIN_PANELHEIGHT + 2) && (pt.y + 2 < 100) && (pt.y + 2 < rc.bottom - 30)) - dat->Panel->setHeight(pt.y + 2, true); - - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); - if (M.isAero()) - InvalidateRect(GetParent(hwndDlg), NULL, FALSE); - } - break; - - // queue a dm_remakelog - // wParam = hwnd of the sender, so we can directly do a DM_REMAKELOG if the msg came - // from ourself. otherwise, the dm_remakelog will be deferred until next window - // activation (focus) - case DM_DEFERREDREMAKELOG: - if ((HWND) wParam == hwndDlg) - SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); - else { - if (M.GetByte(dat->hContact, "mwoverride", 0) == 0) { - dat->dwFlags &= ~(MWF_LOG_ALL); - dat->dwFlags |= (lParam & MWF_LOG_ALL); - dat->dwFlags |= MWF_DEFERREDREMAKELOG; - } - } - return 0; - - case DM_FORCEDREMAKELOG: - if ((HWND) wParam == hwndDlg) - SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); - else { - dat->dwFlags &= ~(MWF_LOG_ALL); - dat->dwFlags |= (lParam & MWF_LOG_ALL); - dat->dwFlags |= MWF_DEFERREDREMAKELOG; - } - return 0; - - case DM_REMAKELOG: - dat->szMicroLf[0] = 0; - dat->lastEventTime = 0; - dat->iLastEventType = -1; - StreamInEvents(hwndDlg, dat->hDbEventFirst, -1, 0, NULL); - return 0; - - case DM_APPENDMCEVENT: - if (dat->hContact == db_mc_getMeta(wParam) && dat->hDbEventFirst == NULL) { - dat->hDbEventFirst = (HANDLE)lParam; - SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0); - } - else if (dat->hContact == wParam && db_mc_isSub(wParam) && db_event_getContact(HANDLE(lParam)) != wParam) - StreamInEvents(hwndDlg, (HANDLE)lParam, 1, 1, NULL); - return 0; - - case DM_APPENDTOLOG: - StreamInEvents(hwndDlg, (HANDLE)wParam, 1, 1, NULL); - return 0; - - // replays queued events after the message log has been frozen for a while - case DM_REPLAYQUEUE: - for (int i=0; i < dat->iNextQueuedEvent; i++) - if (dat->hQueuedEvents[i] != 0) - StreamInEvents(hwndDlg, dat->hQueuedEvents[i], 1, 1, NULL); - - dat->iNextQueuedEvent = 0; - SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, dat->bNotOnList ? TranslateT("Contact not on list. You may add it...") : - TranslateT("Auto scrolling is disabled (press F12 to enable it)")); - return 0; - - case DM_SCROLLIEVIEW: - { - IEVIEWWINDOW iew = { sizeof(iew) }; - iew.iType = IEW_SCROLLBOTTOM; - if (dat->hwndIEView) { - iew.hwnd = dat->hwndIEView; - CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&iew); - } - else if (dat->hwndHPP) { - iew.hwnd = dat->hwndHPP; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew); - } - } - return 0; - - case DM_FORCESCROLL: - { - SCROLLINFO *psi = (SCROLLINFO *)lParam; - POINT *ppt = (POINT *)wParam; - - HWND hwnd = GetDlgItem(hwndDlg, IDC_LOG); - int len; - - if (wParam == 0 && lParam == 0) { - DM_ScrollToBottom(dat, 0, 1); - return 0; - } - - if (dat->hwndIEView == 0 && dat->hwndHPP == 0) { - len = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_LOG)); - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETSEL, len - 1, len - 1); - } - - if (psi == NULL) { - DM_ScrollToBottom(dat, 0, 0); - return 0; - } - - if ((UINT)psi->nPos >= (UINT)psi->nMax - psi->nPage - 5 || psi->nMax - psi->nMin - psi->nPage < 50) - DM_ScrollToBottom(dat, 0, 0); - else - SendMessage((dat->hwndIEView || dat->hwndHPP) ? (dat->hwndIEView ? dat->hwndIEView : dat->hwndHPP) : hwnd, EM_SETSCROLLPOS, 0, (LPARAM)ppt); - } - return 0; - - case HM_DBEVENTADDED: - // this is called whenever a new event has been added to the database. - // this CAN be posted (some sanity checks required). - if (!dat) - return 0; - if (dat->hContact) - DM_EventAdded(dat, dat->hContact, lParam); - return 0; - - case WM_TIMER: - // timer to control info panel hovering - if (wParam == TIMERID_AWAYMSG) { - KillTimer(hwndDlg, wParam); - GetCursorPos(&pt); - - if (wParam == TIMERID_AWAYMSG && dat->Panel->hitTest(pt) != CInfoPanel::HTNIRVANA) - SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, 0, 0); - else - dat->dwFlagsEx &= ~MWF_SHOW_AWAYMSGTIMER; - break; - } - - // timer id for message timeouts is composed like: - // for single message sends: basevalue (TIMERID_MSGSEND) + send queue index - if (wParam >= TIMERID_MSGSEND) { - int iIndex = wParam - TIMERID_MSGSEND; - if (iIndex < SendQueue::NR_SENDJOBS) { // single sendjob timer - SendJob *job = sendQueue->getJobByIndex(iIndex); - KillTimer(hwndDlg, wParam); - mir_sntprintf(job->szErrorMsg, SIZEOF(job->szErrorMsg), TranslateT("Delivery failure: %s"), - TranslateT("The message send timed out")); - job->iStatus = SendQueue::SQ_ERROR; - if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) - SkinPlaySound("SendError"); - if (!(dat->dwFlags & MWF_ERRORSTATE)) - sendQueue->handleError(dat, iIndex); - break; - } - } - else if (wParam == TIMERID_FLASHWND) { - if (dat->mayFlashTab) - FlashTab(dat, hwndTab, dat->iTabID, &dat->bTabFlash, TRUE, dat->hTabIcon); - break; - } - else if (wParam == TIMERID_TYPE) { - DM_Typing(dat); - break; - } - break; - - case DM_ERRORDECIDED: - switch (wParam) { - case MSGERROR_CANCEL: - case MSGERROR_SENDLATER: - if (dat->dwFlags & MWF_ERRORSTATE) { - dat->cache->saveHistory(0, 0); - if (wParam == MSGERROR_SENDLATER) - sendQueue->doSendLater(dat->iCurrentQueueError, dat); // to be implemented at a later time - dat->iOpenJobs--; - sendQueue->dec(); - if (dat->iCurrentQueueError >= 0 && dat->iCurrentQueueError < SendQueue::NR_SENDJOBS) - sendQueue->clearJob(dat->iCurrentQueueError); - dat->iCurrentQueueError = -1; - sendQueue->showErrorControls(dat, FALSE); - if (wParam != MSGERROR_CANCEL || (wParam == MSGERROR_CANCEL && lParam == 0)) - SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); - sendQueue->checkQueue(dat); - int iNextFailed = sendQueue->findNextFailed(dat); - if (iNextFailed >= 0) - sendQueue->handleError(dat, iNextFailed); - } - break; - - case MSGERROR_RETRY: - if (dat->dwFlags & MWF_ERRORSTATE) { - int resent = 0; - - dat->cache->saveHistory(0, 0); - if (dat->iCurrentQueueError >= 0 && dat->iCurrentQueueError < SendQueue::NR_SENDJOBS) { - SendJob *job = sendQueue->getJobByIndex(dat->iCurrentQueueError); - if (job->hSendId == 0 && job->hContact == 0) - break; - - job->hSendId = (HANDLE)CallContactService(job->hContact, PSS_MESSAGE, - (dat->sendMode & SMODE_FORCEANSI) ? (job->dwFlags & ~PREF_UNICODE) : job->dwFlags, (LPARAM)job->szSendBuffer); - resent++; - } - - if (resent) { - int iNextFailed; - SendJob *job = sendQueue->getJobByIndex(dat->iCurrentQueueError); - - SetTimer(hwndDlg, TIMERID_MSGSEND + dat->iCurrentQueueError, PluginConfig.m_MsgTimeout, NULL); - job->iStatus = SendQueue::SQ_INPROGRESS; - dat->iCurrentQueueError = -1; - sendQueue->showErrorControls(dat, FALSE); - SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); - sendQueue->checkQueue(dat); - if ((iNextFailed = sendQueue->findNextFailed(dat)) >= 0) - sendQueue->handleError(dat, iNextFailed); - } - } - } - break; - - case DM_SELECTTAB: - SendMessage(hwndContainer, DM_SELECTTAB, wParam, lParam); // pass the msg to our container - return 0; - - case DM_SETLOCALE: - if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) - break; - if (m_pContainer->hwndActive == hwndDlg && PluginConfig.m_AutoLocaleSupport && hwndContainer == GetForegroundWindow() && hwndContainer == GetActiveWindow()) { - if (lParam) - dat->hkl = (HKL)lParam; - - if (dat->hkl) - ActivateKeyboardLayout(dat->hkl, 0); - } - return 0; - - // return timestamp (in ticks) of last recent message which has not been read yet. - // 0 if there is none - // lParam = pointer to a dword receiving the value. - case DM_QUERYLASTUNREAD: - { - DWORD *pdw = (DWORD *)lParam; - if (pdw) - *pdw = dat->dwTickLastEvent; - } - return 0; - - case DM_QUERYCONTAINER: - { - TContainerData **pc = (TContainerData **)lParam; - if (pc) - *pc = m_pContainer; - } - return 0; - - case DM_QUERYHCONTACT: - { - MCONTACT *phContact = (MCONTACT*)lParam; - if (phContact) - *phContact = dat->hContact; - } - return 0; - - case DM_UPDATELASTMESSAGE: - DM_UpdateLastMessage(dat); - return 0; - - case DM_SAVESIZE: - if (dat->dwFlags & MWF_NEEDCHECKSIZE) - lParam = 0; - - dat->dwFlags &= ~MWF_NEEDCHECKSIZE; - if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) { - dat->dwFlags &= ~MWF_INITMODE; - if (dat->lastMessage) - DM_UpdateLastMessage(dat); - } - - RECT rcClient; - SendMessage(hwndContainer, DM_QUERYCLIENTAREA, 0, (LPARAM)&rcClient); - MoveWindow(hwndDlg, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), TRUE); - if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) { - dat->dwFlags &= ~MWF_WASBACKGROUNDCREATE; - SendMessage(hwndDlg, WM_SIZE, 0, 0); - PostMessage(hwndDlg, DM_UPDATEPICLAYOUT, 0, 0); - if (PluginConfig.m_AutoLocaleSupport) { - if (dat->hkl == 0) - DM_LoadLocale(dat); - else - PostMessage(hwndDlg, DM_SETLOCALE, 0, 0); - } - if (dat->hwndIEView != 0) - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - if (dat->pContainer->dwFlags & CNT_SIDEBAR) - dat->pContainer->SideBar->Layout(); - } - else { - SendMessage(hwndDlg, WM_SIZE, 0, 0); - if (lParam == 0) - PostMessage(hwndDlg, DM_FORCESCROLL, 0, 0); - } - return 0; - - case DM_CHECKSIZE: - dat->dwFlags |= MWF_NEEDCHECKSIZE; - return 0; - - // sent by the message input area hotkeys. just pass it to our container - case DM_QUERYPENDING: - SendMessage(hwndContainer, DM_QUERYPENDING, wParam, lParam); - return 0; - - case WM_LBUTTONDOWN: - GetCursorPos(&tmp); - cur.x = (SHORT)tmp.x; - cur.y = (SHORT)tmp.y; - if (!dat->Panel->isHovered()) - SendMessage(hwndContainer, WM_NCLBUTTONDOWN, HTCAPTION, *((LPARAM*)(&cur))); - break; - - case WM_LBUTTONUP: - GetCursorPos(&tmp); - if (dat->Panel->isHovered()) - dat->Panel->handleClick(tmp); - else { - cur.x = (SHORT)tmp.x; - cur.y = (SHORT)tmp.y; - SendMessage(hwndContainer, WM_NCLBUTTONUP, HTCAPTION, *((LPARAM*)(&cur))); - } - break; - - case WM_RBUTTONUP: - { - RECT rcPicture, rcPanelNick = {0}; - int menuID = 0; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_CONTACTPIC), &rcPicture); - rcPanelNick.left = rcPanelNick.right - 30; - GetCursorPos(&pt); - - if (dat->Panel->invokeConfigDialog(pt)) - break; - - if (PtInRect(&rcPicture, pt)) - menuID = MENU_PICMENU; - - if ((menuID == MENU_PICMENU && ((dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown) || dat->hOwnPic) && dat->bShowAvatar != 0)) { - HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, menuID == MENU_PICMENU ? 1 : 11); - GetCursorPos(&pt); - MsgWindowUpdateMenu(dat, submenu, menuID); - int iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); - MsgWindowMenuHandler(dat, iSelection, menuID); - break; - } - - HMENU subMenu = GetSubMenu(m_pContainer->hMenuContext, 0); - MsgWindowUpdateMenu(dat, subMenu, MENU_TABCONTEXT); - - int iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); - if (iSelection >= IDM_CONTAINERMENU) { - char szIndex[10]; - char *szKey = "TAB_ContainersW"; - - mir_snprintf(szIndex, SIZEOF(szIndex), "%d", iSelection - IDM_CONTAINERMENU); - if (iSelection - IDM_CONTAINERMENU >= 0) { - ptrT val(db_get_tsa(NULL, szKey, szIndex)); - if (val) - SendMessage(hwndDlg, DM_CONTAINERSELECTED, 0, (LPARAM)val); - } - break; - } - MsgWindowMenuHandler(dat, iSelection, MENU_TABCONTEXT); - } - break; - - case WM_MOUSEMOVE: - GetCursorPos(&pt); - DM_DismissTip(dat, pt); - dat->Panel->trackMouse(pt); - break; - - case WM_MEASUREITEM: - { - LPMEASUREITEMSTRUCT lpmi = (LPMEASUREITEMSTRUCT) lParam; - if (dat->Panel->isHovered()) { - lpmi->itemHeight = 0; - lpmi->itemWidth = 6; - return TRUE; - } - } - return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); - - case WM_NCHITTEST: - SendMessage(hwndContainer, WM_NCHITTEST, wParam, lParam); - break; - - case WM_DRAWITEM: - return MsgWindowDrawHandler(wParam, lParam, dat); - - case WM_APPCOMMAND: - { - DWORD cmd = GET_APPCOMMAND_LPARAM(lParam); - if (cmd == APPCOMMAND_BROWSER_BACKWARD || cmd == APPCOMMAND_BROWSER_FORWARD) { - SendMessage(hwndContainer, DM_SELECTTAB, cmd == APPCOMMAND_BROWSER_BACKWARD ? DM_SELECT_PREV : DM_SELECT_NEXT, 0); - return 1; - } - } - break; - - case WM_COMMAND: - if (!dat) - break; - - // custom button handling - if (LOWORD(wParam) >= MIN_CBUTTONID && LOWORD(wParam) <= MAX_CBUTTONID) { - BB_CustomButtonClick(dat, LOWORD(wParam), GetDlgItem(hwndDlg, LOWORD(wParam)), 0); - break; - } - - switch (LOWORD(wParam)) { - case IDOK: - if (dat->fEditNotesActive) { - SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_PIC, (LPARAM)TranslateT("You are editing the user notes. Click the button again or use the hotkey (default: Alt-N) to save the notes and return to normal messaging mode")); - return 0; - } - else { - // don't parse text formatting when the message contains curly braces - these are used by the rtf syntax - // and the parser currently cannot handle them properly in the text - XXX needs to be fixed later. - FINDTEXTEXA fi = { 0 }; - fi.chrg.cpMin = 0; - fi.chrg.cpMax = -1; - fi.lpstrText = "{"; - int final_sendformat = SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) == -1 ? dat->SendFormat : 0; - fi.lpstrText = "}"; - final_sendformat = SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) == -1 ? final_sendformat : 0; - - if (GetSendButtonState(hwndDlg) == PBS_DISABLED) - break; - - ptrA streamOut(Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, final_sendformat ? 0 : (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE))); - if (streamOut == NULL) - break; - - ptrT decoded(mir_utf8decodeT(streamOut)); - if (decoded == NULL) - break; - - char *utfResult = NULL; - if (final_sendformat) - DoRtfToTags(decoded, dat); - rtrimt(decoded); - int bufSize = WideCharToMultiByte(dat->codePage, 0, decoded, -1, dat->sendBuffer, 0, 0, 0); - - int memRequired = 0, flags = 0; - if (!IsUtfSendAvailable(dat->hContact)) { - flags |= PREF_UNICODE; - memRequired = bufSize + ((lstrlenW(decoded) + 1) * sizeof(WCHAR)); - } - else { - flags |= PREF_UTF; - utfResult = mir_utf8encodeT(decoded); - memRequired = (int)(strlen(utfResult)) + 1; - } - - // try to detect RTL - HWND hwndEdit = GetDlgItem(hwndDlg, IDC_MESSAGE); - SendMessage(hwndEdit, WM_SETREDRAW, FALSE, 0); - - PARAFORMAT2 pf2; - ZeroMemory(&pf2, sizeof(PARAFORMAT2)); - pf2.cbSize = sizeof(pf2); - pf2.dwMask = PFM_RTLPARA; - SendMessage(hwndEdit, EM_SETSEL, 0, -1); - SendMessage(hwndEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2); - if (pf2.wEffects & PFE_RTLPARA) - if (SendQueue::RTL_Detect(decoded)) - flags |= PREF_RTL; - - SendMessage(hwndEdit, WM_SETREDRAW, TRUE, 0); - SendMessage(hwndEdit, EM_SETSEL, -1, -1); - InvalidateRect(hwndEdit, NULL, FALSE); - - if (memRequired > dat->iSendBufferSize) { - dat->sendBuffer = (char *)mir_realloc(dat->sendBuffer, memRequired); - dat->iSendBufferSize = memRequired; - } - if (utfResult) { - CopyMemory(dat->sendBuffer, utfResult, memRequired); - mir_free(utfResult); - } - else { - WideCharToMultiByte(dat->codePage, 0, decoded, -1, dat->sendBuffer, bufSize, 0, 0); - if (flags & PREF_UNICODE) - CopyMemory(&dat->sendBuffer[bufSize], decoded, (lstrlenW(decoded) + 1) * sizeof(WCHAR)); - } - - if (memRequired == 0 || dat->sendBuffer[0] == 0) - break; - - if (dat->sendMode & SMODE_CONTAINER && m_pContainer->hwndActive == hwndDlg && GetForegroundWindow() == hwndContainer) { - int tabCount = TabCtrl_GetItemCount(hwndTab); - ptrA szFromStream(Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, dat->SendFormat ? 0 : (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE))); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - for (int i=0; i < tabCount; i++) { - TabCtrl_GetItem(hwndTab, i, &tci); - // get the contact from the tabs lparam which hopefully is the tabs hwnd so we can get its userdata.... hopefully - HWND contacthwnd = (HWND)tci.lParam; - if (IsWindow(contacthwnd)) { - // if the contact hwnd is the current contact then ignore it and let the normal code deal with the msg - if (contacthwnd != hwndDlg) { - SETTEXTEX stx = {ST_DEFAULT, CP_UTF8}; - // send the buffer to the contacts msg typing area - SendDlgItemMessage(contacthwnd, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szFromStream); - SendMessage(contacthwnd, WM_COMMAND, IDOK, 0); - } - } - } - } - // END /all /MOD - if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) - DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); - - DeletePopupsForContact(dat->hContact, PU_REMOVE_ON_SEND); - if (M.GetByte("allow_sendhook", 0)) { - int result = TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CUSTOM, MAKELONG(flags, tabMSG_WINDOW_EVT_CUSTOM_BEFORESEND)); - if (result) - return TRUE; - } - sendQueue->addTo(dat, memRequired, flags); - } - return TRUE; - - case IDC_QUOTE: - { - int iCharsPerLine = M.GetDword("quoteLineLength", 64); - CHARRANGE sel; - SETTEXTEX stx = {ST_SELECTION, 1200}; - - HANDLE hDBEvent = 0; - if (dat->hwndIEView || dat->hwndHPP) { // IEView quoting support.. - TCHAR *selected = 0, *szQuoted = 0; - - IEVIEWEVENT event = { sizeof(event) }; - event.hContact = dat->hContact; - event.dwFlags = 0; - event.iType = IEE_GET_SELECTION; - - if (dat->hwndIEView) { - event.hwnd = dat->hwndIEView; - selected = (TCHAR*)CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event); - } - else { - event.hwnd = dat->hwndHPP; - selected = (TCHAR*)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - if (selected != NULL) { - szQuoted = QuoteText(selected, iCharsPerLine, 0); - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted); - if (szQuoted) - mir_free(szQuoted); - break; - } - else { - hDBEvent = db_event_last(dat->hContact); - goto quote_from_last; - } - } - hDBEvent = dat->hDbEventLast; - -quote_from_last: - if (hDBEvent == NULL) - break; - - SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) { - DBEVENTINFO dbei = { sizeof(dbei) }; - dbei.cbBlob = db_event_getBlobSize(hDBEvent); - TCHAR *szText = (TCHAR*)mir_alloc((dbei.cbBlob + 1) * sizeof(TCHAR)); //URLs are made one char bigger for crlf - dbei.pBlob = (BYTE*)szText; - db_event_get(hDBEvent, &dbei); - int iSize = int(strlen((char*)dbei.pBlob)) + 1; - - bool iAlloced = false; - TCHAR *szConverted; - if (dbei.flags & DBEF_UTF) { - szConverted = mir_utf8decodeW((char*)szText); - iAlloced = true; - } - else { - if (iSize != dbei.cbBlob) - szConverted = (TCHAR*)&dbei.pBlob[iSize]; - else { - szConverted = (TCHAR*)mir_alloc(sizeof(TCHAR) * iSize); - iAlloced = true; - MultiByteToWideChar(CP_ACP, 0, (char*)dbei.pBlob, -1, szConverted, iSize); - } - } - if (dbei.eventType == EVENTTYPE_FILE) { - int iDescr = lstrlenA((char *)(szText + sizeof(DWORD))); - MoveMemory(szText, szText + sizeof(DWORD), iDescr); - MoveMemory(szText + iDescr + 2, szText + sizeof(DWORD)+iDescr, dbei.cbBlob - iDescr - sizeof(DWORD)-1); - szText[iDescr] = '\r'; - szText[iDescr + 1] = '\n'; - szConverted = (TCHAR*)mir_alloc(sizeof(TCHAR)* (1 + lstrlenA((char *)szText))); - MultiByteToWideChar(CP_ACP, 0, (char *)szText, -1, szConverted, 1 + lstrlenA((char *)szText)); - iAlloced = true; - } - ptrT szQuoted(QuoteText(szConverted, iCharsPerLine, 0)); - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted); - mir_free(szText); - if (iAlloced) - mir_free(szConverted); - } - else { - ptrA szFromStream(Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, SF_TEXT | SF_USECODEPAGE | SFF_SELECTION)); - ptrW converted(mir_utf8decodeW(szFromStream)); - Utils::FilterEventMarkers(converted); - ptrT szQuoted(QuoteText(converted, iCharsPerLine, 0)); - SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted); - } - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - } - break; - - case IDC_ADD: - { - ADDCONTACTSTRUCT acs = { 0 }; - acs.hContact = dat->hContact; - acs.handleType = HANDLE_CONTACT; - acs.szProto = 0; - CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs); - if (!db_get_b(dat->hContact, "CList", "NotOnList", 0)) { - dat->bNotOnList = FALSE; - ShowMultipleControls(hwndDlg, addControls, 2, SW_HIDE); - if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)) - Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_HIDE); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - } - } - break; - - case IDC_CANCELADD: - dat->bNotOnList = FALSE; - ShowMultipleControls(hwndDlg, addControls, 2, SW_HIDE); - if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)) - Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_HIDE); - SendMessage(hwndDlg, WM_SIZE, 0, 0); - break; - - case IDC_MESSAGE: - if (HIWORD(wParam) == EN_CHANGE) { - if (m_pContainer->hwndActive == hwndDlg) - UpdateReadChars(dat); - dat->dwFlags |= MWF_NEEDHISTORYSAVE; - dat->dwLastActivity = GetTickCount(); - m_pContainer->dwLastActivity = dat->dwLastActivity; - SendQueue::UpdateSaveAndSendButton(dat); - if (!(GetKeyState(VK_CONTROL) & 0x8000)) { - dat->nLastTyping = GetTickCount(); - if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE))) { - if (dat->nTypeMode == PROTOTYPE_SELFTYPING_OFF) { - if (!(dat->dwFlags & MWF_INITMODE)) - DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_ON); - } - } - else if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) - DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); - } - } - break; - - default: - Utils::CmdDispatcher(Utils::CMD_MSGDIALOG, hwndDlg, LOWORD(wParam), wParam, lParam, dat, m_pContainer); - break; - } - break; - - case WM_CONTEXTMENU: - { - DWORD idFrom = GetDlgCtrlID((HWND)wParam); - if (idFrom >= MIN_CBUTTONID && idFrom <= MAX_CBUTTONID) { - BB_CustomButtonClick(dat, idFrom, (HWND)wParam, 1); - break; - } - } - break; - - // this is now *only* called from the global ME_PROTO_ACK handler (static int ProtoAck() in msgs.c) - // it receives: - // wParam = index of the sendjob in the queue in the low word, index of the found sendID in the high word - // (normally 0, but if its a multisend job, then the sendjob may contain more than one hContact/hSendId pairs.) - // lParam = the original ackdata - // - // the "per message window" ACK hook is gone, the global ack handler cares about all types of ack's (currently - // *_MESSAGE and *_AVATAR and dispatches them to the owner windows). - case HM_EVENTSENT: - sendQueue->ackMessage(dat, wParam, lParam); - return 0; - - case DM_ACTIVATEME: - ActivateExistingTab(m_pContainer, hwndDlg); - return 0; - - // sent by the select container dialog box when a container was selected... - // lParam = (TCHAR*)selected name... - case DM_CONTAINERSELECTED: - { - TCHAR *szNewName = (TCHAR*)lParam; - if (!_tcscmp(szNewName, TranslateT("Default container"))) - szNewName = CGlobals::m_default_container_name; - - int iOldItems = TabCtrl_GetItemCount(hwndTab); - if (!_tcsncmp(m_pContainer->szName, szNewName, CONTAINER_NAMELEN)) - break; - - TContainerData *pNewContainer = FindContainerByName(szNewName); - if (pNewContainer == NULL) - if ((pNewContainer = CreateContainer(szNewName, FALSE, dat->hContact)) == NULL) - break; - - db_set_ts(dat->hContact, SRMSGMOD_T, "containerW", szNewName); - dat->fIsReattach = TRUE; - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, dat->hContact); - if (iOldItems > 1) // there were more than 1 tab, container is still valid - SendMessage(m_pContainer->hwndActive, WM_SIZE, 0, 0); - SetForegroundWindow(pNewContainer->hwnd); - SetActiveWindow(pNewContainer->hwnd); - } - break; - - case DM_STATUSBARCHANGED: - UpdateStatusBar(dat); - return 0; - - case DM_UINTOCLIPBOARD: - Utils::CopyToClipBoard(dat->cache->getUIN(), hwndDlg); - return 0; - - // broadcasted when GLOBAL info panel setting changes - case DM_SETINFOPANEL: - CInfoPanel::setPanelHandler(dat, wParam, lParam); - return 0; - - // show the balloon tooltip control. - // wParam == id of the "anchor" element, defaults to the panel status field (for away msg retrieval) - // lParam == new text to show - case DM_ACTIVATETOOLTIP: - if (IsIconic(hwndContainer) || m_pContainer->hwndActive != hwndDlg) - break; - - dat->Panel->showTip(wParam, lParam); - break; - - case WM_NEXTDLGCTL: - if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) - return 1; - break; - - // save the contents of the log as rtf file - case DM_SAVEMESSAGELOG: - DM_SaveLogAsRTF(dat); - return 0; - - case DM_CHECKAUTOHIDE: - DM_CheckAutoHide(dat, wParam, lParam); - return 0; - - case DM_IEVIEWOPTIONSCHANGED: - if (dat->hwndIEView) - SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); - break; - - case DM_SMILEYOPTIONSCHANGED: - ConfigureSmileyButton(dat); - SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); - break; - - case DM_MYAVATARCHANGED: - { - const char *szProto = dat->cache->getActiveProto(); - if (!strcmp((char *)wParam, szProto) && lstrlenA(szProto) == lstrlenA((char *)wParam)) - LoadOwnAvatar(dat); - } - break; - - case DM_GETWINDOWSTATE: - { - UINT state = MSG_WINDOW_STATE_EXISTS; - if (IsWindowVisible(hwndDlg)) - state |= MSG_WINDOW_STATE_VISIBLE; - if (GetForegroundWindow() == hwndContainer) - state |= MSG_WINDOW_STATE_FOCUS; - if (IsIconic(hwndContainer)) - state |= MSG_WINDOW_STATE_ICONIC; - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, state); - } - return TRUE; - - case DM_CLIENTCHANGED: - GetClientIcon(dat); - if (dat->hClientIcon && dat->Panel->isActive()) - InvalidateRect(hwndDlg, NULL, TRUE); - return 0; - - case DM_UPDATEUIN: - if (dat->Panel->isActive()) - dat->Panel->Invalidate(); - if (dat->pContainer->dwFlags & CNT_UINSTATUSBAR) - UpdateStatusBar(dat); - return 0; - - case DM_REMOVEPOPUPS: - DeletePopupsForContact(dat->hContact, (DWORD)wParam); - return 0; - - case EM_THEMECHANGED: - DM_FreeTheme(dat); - return DM_ThemeChanged(dat); - - case DM_PLAYINCOMINGSOUND: - if (!dat) - return 0; - PlayIncomingSound(dat); - return 0; - - case DM_REFRESHTABINDEX: - dat->iTabID = GetTabIndexFromHWND(GetParent(hwndDlg), hwndDlg); - return 0; - - case DM_STATUSICONCHANGE: - if (m_pContainer->hwndStatus) { - SendMessage(dat->pContainer->hwnd, WM_SIZE, 0, 0); - SendMessage(m_pContainer->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, 0); - InvalidateRect(m_pContainer->hwndStatus, NULL, TRUE); - } - return 0; - - case DM_BBNEEDUPDATE: - if (lParam) - CB_ChangeButton(hwndDlg, dat, (CustomButtonData*)lParam); - else - BB_InitDlgButtons(dat); - - BB_SetButtonsPos(dat); - return 0; - - case DM_CBDESTROY: - if (lParam) - CB_DestroyButton(hwndDlg, dat, (DWORD)wParam, (DWORD)lParam); - else - CB_DestroyAllButtons(hwndDlg, dat); - return 0; - - case WM_DROPFILES: - { - BOOL not_sending = GetKeyState(VK_CONTROL) & 0x8000; - if (!not_sending) { - const char *szProto = dat->cache->getActiveProto(); - if (szProto == NULL) - break; - - int pcaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0); - if (!(pcaps & PF1_FILESEND)) - break; - if (dat->wStatus == ID_STATUS_OFFLINE) { - pcaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0); - if (!(pcaps & PF4_OFFLINEFILES)) { - TCHAR szBuffer[256]; - - mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("Contact is offline and this protocol does not support sending files to offline users.")); - SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer); - break; - } - } - } - - if (dat->hContact != NULL) { - TCHAR szFilename[MAX_PATH]; - HDROP hDrop = (HDROP)wParam; - int fileCount = DragQueryFile(hDrop, -1, NULL, 0), totalCount = 0, i; - TCHAR** ppFiles = NULL; - for (i = 0; i < fileCount; i++) { - DragQueryFile(hDrop, i, szFilename, SIZEOF(szFilename)); - Utils::AddToFileList(&ppFiles, &totalCount, szFilename); - } - - if (!not_sending) - CallService(MS_FILE_SENDSPECIFICFILEST, dat->hContact, (LPARAM)ppFiles); - else { - if (ServiceExists(MS_HTTPSERVER_ADDFILENAME)) { - for (int i = 0; i < totalCount; i++) { - char* szFileName = mir_t2a(ppFiles[i]); - char *szTemp = (char*)CallService(MS_HTTPSERVER_ADDFILENAME, (WPARAM)szFileName, 0); - mir_free(szFileName); - } - char *szHTTPText = "DEBUG"; - SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_REPLACESEL, TRUE, (LPARAM)szHTTPText); - SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - } - } - for (i = 0; ppFiles[i]; i++) - mir_free(ppFiles[i]); - mir_free(ppFiles); - } - } - return 0; - - case DM_CHECKQUEUEFORCLOSE: - { - int *uOpen = (int*)lParam; - if (uOpen) - *uOpen += dat->iOpenJobs; - } - return 0; - - case WM_CLOSE: - // esc handles error controls if we are in error state (error controls visible) - if (wParam == 0 && lParam == 0 && dat->dwFlags & MWF_ERRORSTATE) { - SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_CANCEL, 0); - return TRUE; - } - - if (wParam == 0 && lParam == 0) { - if (PluginConfig.m_EscapeCloses == 1) { - SendMessage(hwndContainer, WM_SYSCOMMAND, SC_MINIMIZE, 0); - return TRUE; - } - else if (PluginConfig.m_HideOnClose && PluginConfig.m_EscapeCloses == 2) { - ShowWindow(hwndContainer, SW_HIDE); - return TRUE; - } - _dlgReturn(hwndDlg, TRUE); - } - { - TContainerData *pContainer = dat->pContainer; - if (dat->iOpenJobs > 0 && lParam != 2) { - if (dat->dwFlags & MWF_ERRORSTATE) - SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_CANCEL, 1); - else if (dat) { - if (dat->dwFlagsEx & MWF_EX_WARNCLOSE) - return TRUE; - - dat->dwFlagsEx |= MWF_EX_WARNCLOSE; - LRESULT result = SendQueue::WarnPendingJobs(0); - dat->dwFlagsEx &= ~MWF_EX_WARNCLOSE; - if (result == IDNO) - return TRUE; - } - } - int iTabs = TabCtrl_GetItemCount(hwndTab); - if (iTabs == 1) { - PostMessage(hwndContainer, WM_CLOSE, 0, 1); - return 1; - } - - m_pContainer->iChilds--; - int i = GetTabIndexFromHWND(hwndTab, hwndDlg); - - // 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 && lParam != 3) { - if (i == iTabs - 1) - i--; - else - i++; - TabCtrl_SetCurSel(hwndTab, i); - - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - TabCtrl_GetItem(hwndTab, i, &item); // retrieve dialog hwnd for the now active tab... - - m_pContainer->hwndActive = (HWND)item.lParam; - - RECT rc; - SendMessage(hwndContainer, 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(hwndContainer, WM_SIZE, 0, 0); - } - - DestroyWindow(hwndDlg); - if (iTabs == 1) - PostMessage(GetParent(GetParent(hwndDlg)), WM_CLOSE, 0, 1); - else - SendMessage(pContainer->hwnd, WM_SIZE, 0, 0); - } - break; - - case WM_DESTROY: - if (!dat) - break; - - if (dat->hwndContactPic) - DestroyWindow(dat->hwndContactPic); - - if (dat->hwndPanelPic) - DestroyWindow(dat->hwndPanelPic); - - if (dat->hClientIcon) - DestroyIcon(dat->hClientIcon); - - if (dat->hwndPanelPicParent) - DestroyWindow(dat->hwndPanelPicParent); - - if (dat->cache->isValid()) { // not valid means the contact was deleted - TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CLOSING, 0); - AddContactToFavorites(dat->hContact, dat->cache->getNick(), dat->cache->getActiveProto(), dat->szStatus, dat->wStatus, - LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getActiveStatus()), 1, PluginConfig.g_hMenuRecent); - if (dat->hContact) { - if (!dat->fEditNotesActive) { - char *msg = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE)); - if (msg) { - db_set_utf(dat->hContact, SRMSGMOD, "SavedMsg", msg); - mir_free(msg); - } - else db_unset(dat->hContact, SRMSGMOD, "SavedMsg"); - } - else SendMessage(hwndDlg, WM_COMMAND, IDC_PIC, 0); - } - } - - if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) - DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); - - DM_FreeTheme(dat); - - mir_free(dat->sendBuffer); - mir_free(dat->hHistoryEvents); - - // search the sendqueue for unfinished send jobs and mir_free them. Leave unsent - // messages in the queue as they can be acked later - { - SendJob *jobs = sendQueue->getJobByIndex(0); - - for (int i = 0; i < SendQueue::NR_SENDJOBS; i++) { - if (jobs[i].hContact == dat->hContact) { - if (jobs[i].iStatus >(unsigned)SendQueue::SQ_INPROGRESS) - sendQueue->clearJob(i); - - // unfinished jobs which did not yet return anything are kept in the queue. - // the hwndOwner is set to 0 because the window handle is now no longer valid. - // Response for such a job is still silently handled by AckMessage() (sendqueue.c) - if (jobs[i].iStatus == (unsigned)SendQueue::SQ_INPROGRESS) - jobs[i].hOwnerWnd = 0; - } - } - } - - mir_free(dat->hQueuedEvents); - - if (dat->hSmileyIcon) - DestroyIcon(dat->hSmileyIcon); - - if (dat->hXStatusIcon) - DestroyIcon(dat->hXStatusIcon); - - if (dat->hwndTip) - DestroyWindow(dat->hwndTip); - - if (dat->hTaskbarIcon) - DestroyIcon(dat->hTaskbarIcon); - - UpdateTrayMenuState(dat, FALSE); // remove me from the tray menu (if still there) - if (PluginConfig.g_hMenuTrayUnread) - DeleteMenu(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)dat->hContact, MF_BYCOMMAND); - M.RemoveWindow(hwndDlg); - - if (dat->cache->isValid()) - db_set_dw(0, SRMSGMOD, "multisplit", dat->multiSplitterX); - - { - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_GETFONT, 0, 0); - if (hFont != NULL && hFont != (HFONT)SendDlgItemMessage(hwndDlg, IDOK, WM_GETFONT, 0, 0)) - DeleteObject(hFont); - - TCITEM item = { 0 }; - item.mask = TCIF_PARAM; - - int i = GetTabIndexFromHWND(hwndTab, hwndDlg); - if (i >= 0) { - SendMessage(hwndTab, WM_USER + 100, 0, 0); // remove tooltip - TabCtrl_DeleteItem(hwndTab, i); - BroadCastContainer(m_pContainer, DM_REFRESHTABINDEX, 0, 0); - dat->iTabID = -1; - } - } - - TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CLOSE, 0); - - // clean up IEView and H++ log windows - if (dat->hwndIEView != 0) { - IEVIEWWINDOW ieWindow; - ieWindow.cbSize = sizeof(IEVIEWWINDOW); - ieWindow.iType = IEW_DESTROY; - ieWindow.hwnd = dat->hwndIEView; - CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&ieWindow); - } - if (dat->hwndHPP) { - IEVIEWWINDOW ieWindow; - ieWindow.cbSize = sizeof(IEVIEWWINDOW); - ieWindow.iType = IEW_DESTROY; - ieWindow.hwnd = dat->hwndHPP; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - if (dat->pWnd) { - delete dat->pWnd; - dat->pWnd = 0; - } - if (dat->sbCustom) { - delete dat->sbCustom; - dat->sbCustom = 0; - } - break; - - case WM_DWMCOMPOSITIONCHANGED: - BB_RefreshTheme(dat); - memset((void*)&dat->pContainer->mOld, -1000, sizeof(MARGINS)); - CProxyWindow::verify(dat); - break; - - case DM_FORCEREDRAW: - RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW); - return 0; - - case DM_CHECKINFOTIP: - dat->Panel->hideTip(reinterpret_cast(lParam)); - return 0; - - case WM_NCDESTROY: - if (dat) { - memset((void*)&dat->pContainer->mOld, -1000, sizeof(MARGINS)); - PostMessage(dat->pContainer->hwnd, WM_SIZE, 0, 1); - if (m_pContainer->dwFlags & CNT_SIDEBAR) - m_pContainer->SideBar->removeSession(dat); - dat->cache->setWindowData(); - if (dat->cache->isValid() && !dat->fIsReattach && dat->hContact && M.GetByte("deletetemp", 0)) - if (db_get_b(dat->hContact, "CList", "NotOnList", 0)) - CallService(MS_DB_CONTACT_DELETE, dat->hContact, 0); - - delete dat->Panel; - mir_free(dat); - } - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} +/* + * Miranda NG: the free IM client for Microsoft* Windows* + * + * 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 message dialog window. + * + */ + +#include "commonheaders.h" + +#define MS_HTTPSERVER_ADDFILENAME "HTTPServer/AddFileName" + +bool IsStringValidLink(TCHAR* pszText); + +const TCHAR *pszIDCSAVE_close = 0, *pszIDCSAVE_save = 0; + +static const UINT sendControls[] = { IDC_MESSAGE, IDC_LOG }; +static const UINT formatControls[] = { IDC_SMILEYBTN, IDC_FONTBOLD, IDC_FONTITALIC, IDC_FONTUNDERLINE, IDC_FONTFACE,IDC_FONTSTRIKEOUT }; +static const UINT addControls[] = { IDC_ADD, IDC_CANCELADD }; +static const UINT btnControls[] = { IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER, IDC_ADD, IDC_CANCELADD }; +static const UINT errorControls[] = { IDC_STATICERRORICON, IDC_STATICTEXT, IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER}; + +static struct { + int id; + const TCHAR* text; +} +tooltips[] = +{ + { IDC_ADD, LPGENT("Add this contact permanently to your contact list") }, + { IDC_CANCELADD, LPGENT("Do not add this contact permanently") }, + { IDC_TOGGLESIDEBAR, LPGENT("Expand or collapse the side bar") } +}; + +static struct +{ + int id; + HICON *pIcon; +} +buttonicons[] = +{ + { IDC_ADD, &PluginConfig.g_buttonBarIcons[ICON_BUTTON_ADD] }, + { IDC_CANCELADD, &PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL] } +}; + +static void _clrMsgFilter(LPARAM lParam) +{ + MSGFILTER *m = reinterpret_cast(lParam); + m->msg = 0; + m->lParam = 0; + m->wParam = 0; +} + +BOOL TSAPI IsUtfSendAvailable(MCONTACT hContact) +{ + char *szProto = GetContactProto(hContact); + if (szProto == NULL) + return FALSE; + + return (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_IMSENDUTF) ? TRUE : FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// show a modified context menu for the richedit control(s) +// @param dat message window data +// @param idFrom dlg ctrl id +// @param hwndFrom src window handle +// @param pt mouse pointer position + +static void ShowPopupMenu(TWindowData *dat, int idFrom, HWND hwndFrom, POINT pt) +{ + CHARRANGE sel, all = { 0, -1}; + int oldCodepage = dat->codePage; + int iPrivateBG = M.GetByte(dat->hContact, "private_bg", 0); + HWND hwndDlg = dat->hwnd; + + HMENU hSubMenu, hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); + if (idFrom == IDC_LOG) + hSubMenu = GetSubMenu(hMenu, 0); + else { + hSubMenu = GetSubMenu(hMenu, 2); + EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, MF_BYCOMMAND | (dat->SendFormat != 0 ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, MF_BYCOMMAND | (PluginConfig.m_PasteAndSend ? MF_ENABLED : MF_GRAYED)); + CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, MF_BYCOMMAND | (PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED)); + EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, MF_BYCOMMAND | (dat->pContainer->hwndStatus ? MF_ENABLED : MF_GRAYED)); + } + TranslateMenu(hSubMenu); + SendMessage(hwndFrom, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) { + EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(hSubMenu, IDM_QUOTE, MF_BYCOMMAND | MF_GRAYED); + if (idFrom == IDC_MESSAGE) + EnableMenuItem(hSubMenu, IDM_CUT, MF_BYCOMMAND | MF_GRAYED); + } + + if (idFrom == IDC_LOG) { + InsertMenuA(hSubMenu, 6/*5*/, MF_BYPOSITION | MF_SEPARATOR, 0, 0); + InsertMenu(hSubMenu, 7/*6*/, MF_BYPOSITION | MF_POPUP, (UINT_PTR) PluginConfig.g_hMenuEncoding, TranslateT("Character Encoding")); + for (int i=0; i < GetMenuItemCount(PluginConfig.g_hMenuEncoding); i++) + CheckMenuItem(PluginConfig.g_hMenuEncoding, i, MF_BYPOSITION | MF_UNCHECKED); + + if (dat->codePage == CP_ACP) + CheckMenuItem(PluginConfig.g_hMenuEncoding, 0, MF_BYPOSITION | MF_CHECKED); + else + CheckMenuItem(PluginConfig.g_hMenuEncoding, dat->codePage, MF_BYCOMMAND | MF_CHECKED); + + CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, MF_BYCOMMAND | (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED ? MF_CHECKED : MF_UNCHECKED)); + } + + MessageWindowPopupData mwpd; + if (idFrom == IDC_LOG || idFrom == IDC_MESSAGE) { + // First notification + mwpd.cbSize = sizeof(mwpd); + mwpd.uType = MSG_WINDOWPOPUP_SHOWING; + mwpd.uFlags = (idFrom == IDC_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT); + mwpd.hContact = dat->hContact; + mwpd.hwnd = hwndFrom; + mwpd.hMenu = hSubMenu; + mwpd.selection = 0; + mwpd.pt = pt; + NotifyEventHooks(PluginConfig.m_event_MsgPopup, 0, (LPARAM)&mwpd); + } + + int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); + + if (idFrom == IDC_LOG || idFrom == IDC_MESSAGE) { + // Second notification + mwpd.selection = iSelection; + mwpd.uType = MSG_WINDOWPOPUP_SELECTED; + NotifyEventHooks(PluginConfig.m_event_MsgPopup, 0, (LPARAM)&mwpd); + } + + if (((iSelection > 800 && iSelection < 1400) || iSelection == 20866) && idFrom == IDC_LOG) { + dat->codePage = iSelection; + db_set_dw(dat->hContact, SRMSGMOD_T, "ANSIcodepage", dat->codePage); + } + else if (iSelection == 500 && idFrom == IDC_LOG) { + dat->codePage = CP_ACP; + db_unset(dat->hContact, SRMSGMOD_T, "ANSIcodepage"); + } + else { + switch (iSelection) { + case IDM_COPY: + SendMessage(hwndFrom, WM_COPY, 0, 0); + break; + case IDM_CUT: + SendMessage(hwndFrom, WM_CUT, 0, 0); + break; + case IDM_PASTE: + case IDM_PASTEFORMATTED: + if (idFrom == IDC_MESSAGE) + SendMessage(hwndFrom, EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0); + break; + case IDM_COPYALL: + SendMessage(hwndFrom, EM_EXSETSEL, 0, (LPARAM)&all); + SendMessage(hwndFrom, WM_COPY, 0, 0); + SendMessage(hwndFrom, EM_EXSETSEL, 0, (LPARAM)&sel); + break; + case IDM_QUOTE: + SendMessage(hwndDlg, WM_COMMAND, IDC_QUOTE, 0); + break; + case IDM_SELECTALL: + SendMessage(hwndFrom, EM_EXSETSEL, 0, (LPARAM)&all); + break; + case IDM_CLEAR: + ClearLog(dat); + break; + case ID_LOG_FREEZELOG: + SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_KEYDOWN, VK_F12, 0); + break; + case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR: + PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator; + db_set_b(0, SRMSGMOD_T, "msgsizebar", (BYTE)PluginConfig.m_visualMessageSizeIndicator); + M.BroadcastMessage(DM_CONFIGURETOOLBAR, 0, 0); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + if (dat->pContainer->hwndStatus) + RedrawWindow(dat->pContainer->hwndStatus, 0, 0, RDW_INVALIDATE | RDW_UPDATENOW); + break; + case ID_EDITOR_PASTEANDSENDIMMEDIATELY: + HandlePasteAndSend(dat); + break; + } + } + if (idFrom == IDC_LOG) + RemoveMenu(hSubMenu, 7, MF_BYPOSITION); + DestroyMenu(hMenu); + if (dat->codePage != oldCodepage) { + SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); + SendMessage(hwndDlg, DM_UPDATETITLE, 0, 1); + } +} + +static void ResizeIeView(const TWindowData *dat, DWORD px, DWORD py, DWORD cx, DWORD cy) +{ + RECT rcRichEdit; + GetWindowRect(GetDlgItem(dat->hwnd, IDC_LOG), &rcRichEdit); + + POINT pt = { rcRichEdit.left, rcRichEdit.top }; + ScreenToClient(dat->hwnd, &pt); + + IEVIEWWINDOW ieWindow = { sizeof(ieWindow) }; + ieWindow.iType = IEW_SETPOS; + ieWindow.parent = dat->hwnd; + ieWindow.hwnd = dat->hwndIEView ? dat->hwndIEView : dat->hwndHPP; + ieWindow.x = pt.x; + ieWindow.y = pt.y; + ieWindow.cx = rcRichEdit.right - rcRichEdit.left; + ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top; + if (ieWindow.cx != 0 && ieWindow.cy != 0) + CallService(dat->hwndIEView ? MS_IEVIEW_WINDOW : MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); +} + +LRESULT CALLBACK IEViewSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + + switch (msg) { + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, IEViewSubclassProc); + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, IEViewSubclassProc); + } + return mir_callNextSubclass(hwnd, IEViewSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// sublassing procedure for the h++ based message log viewer + +LRESULT CALLBACK HPPKFSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + if (mwdat) { + BOOL isCtrl, isShift, isAlt; + KbdState(mwdat, isShift, isCtrl, isAlt); + + switch (msg) { + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, HPPKFSubclassProc); + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, HPPKFSubclassProc); + + case WM_KEYDOWN: + if (!isCtrl && !isAlt && !isShift) { + if (wParam != VK_PRIOR && wParam != VK_NEXT && wParam != VK_DELETE && + wParam != VK_MENU && wParam != VK_END && wParam != VK_HOME && + wParam != VK_UP && wParam != VK_DOWN && wParam != VK_LEFT && + wParam != VK_RIGHT && wParam != VK_TAB && wParam != VK_SPACE) + { + SetFocus(GetDlgItem(mwdat->hwnd, IDC_MESSAGE)); + keybd_event((BYTE)wParam, (BYTE)MapVirtualKey(wParam, 0), KEYEVENTF_EXTENDEDKEY | 0, 0); + return 0; + } + break; + } + } + } + return mir_callNextSubclass(hwnd, HPPKFSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// update state of the container - this is called whenever a tab becomes active, no matter how and +// deals with various things like updating the title bar, removing flashing icons, updating the +// session list, switching the keyboard layout (autolocale active) and the general container status. +// +// it protects itself from being called more than once per session activation and is valid for +// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c) + +static void MsgWindowUpdateState(TWindowData *dat, UINT msg) +{ + if (dat && dat->iTabID >= 0) { + HWND hwndDlg = dat->hwnd; + HWND hwndTab = GetParent(hwndDlg); + + if (msg == WM_ACTIVATE) { + if (dat->pContainer->dwFlags & CNT_TRANSPARENCY) { + DWORD trans = LOWORD(dat->pContainer->settings->dwTransparency); + SetLayeredWindowAttributes(dat->pContainer->hwnd, 0, (BYTE)trans, (dat->pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0)); + } + } + + if (dat->bIsAutosizingInput && dat->iInputAreaHeight == -1) { + dat->iInputAreaHeight = 0; + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_REQUESTRESIZE, 0, 0); + } + + if (dat->pWnd) + dat->pWnd->activateTab(); + dat->Panel->dismissConfig(); + dat->dwUnread = 0; + if (dat->pContainer->hwndSaved == hwndDlg) + return; + + dat->pContainer->hwndSaved = hwndDlg; + + dat->dwTickLastEvent = 0; + dat->dwFlags &= ~MWF_DIVIDERSET; + if (KillTimer(hwndDlg, TIMERID_FLASHWND)) { + FlashTab(dat, hwndTab, dat->iTabID, &dat->bTabFlash, FALSE, dat->hTabIcon); + dat->mayFlashTab = FALSE; + } + if (dat->pContainer->dwFlashingStarted != 0) { + FlashContainer(dat->pContainer, 0, 0); + dat->pContainer->dwFlashingStarted = 0; + } + if (dat->dwFlagsEx & MWF_SHOW_FLASHCLIST) { + dat->dwFlagsEx &= ~MWF_SHOW_FLASHCLIST; + if (dat->hFlashingEvent != 0) + CallService(MS_CLIST_REMOVEEVENT, dat->hContact, (LPARAM)dat->hFlashingEvent); + dat->hFlashingEvent = 0; + } + dat->pContainer->dwFlags &= ~CNT_NEED_UPDATETITLE; + + if (dat->dwFlags & MWF_DEFERREDREMAKELOG) { + SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); + dat->dwFlags &= ~MWF_DEFERREDREMAKELOG; + } + + if (dat->dwFlags & MWF_NEEDCHECKSIZE) + PostMessage(hwndDlg, DM_SAVESIZE, 0, 0); + + if (PluginConfig.m_AutoLocaleSupport) { + if (dat->hkl == 0) + DM_LoadLocale(dat); + else + SendMessage(hwndDlg, DM_SETLOCALE, 0, 0); + } + + dat->pContainer->hIconTaskbarOverlay = 0; + SendMessage(dat->pContainer->hwnd, DM_UPDATETITLE, dat->hContact, 0); + + UpdateStatusBar(dat); + dat->dwLastActivity = GetTickCount(); + dat->pContainer->dwLastActivity = dat->dwLastActivity; + + dat->pContainer->MenuBar->configureMenu(); + UpdateTrayMenuState(dat, FALSE); + + if (dat->pContainer->hwndActive == hwndDlg) + PostMessage(hwndDlg, DM_REMOVEPOPUPS, PU_REMOVE_ON_FOCUS, 0); + + dat->Panel->Invalidate(); + + if (dat->dwFlags & MWF_DEFERREDSCROLL && dat->hwndIEView == 0 && dat->hwndHPP == 0) { + HWND hwnd = GetDlgItem(hwndDlg, IDC_LOG); + + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + dat->dwFlags &= ~MWF_DEFERREDSCROLL; + SendMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_TOP, 0), 0); + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + DM_ScrollToBottom(dat, 0, 1); + PostMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0); // XXX was post() + } + DM_SetDBButtonStates(hwndDlg, dat); + + if (dat->hwndIEView) { + RECT rcRTF; + POINT pt; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rcRTF); + rcRTF.left += 20; + rcRTF.top += 20; + pt.x = rcRTF.left; + pt.y = rcRTF.top; + if (dat->hwndIEView) { + if (M.GetByte("subclassIEView", 0)) { + mir_subclassWindow(dat->hwndIEView, IEViewSubclassProc); + SetWindowPos(dat->hwndIEView, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_DRAWFRAME); + RedrawWindow(dat->hwndIEView, 0, 0, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); + } + } + dat->hwndIWebBrowserControl = WindowFromPoint(pt); + } + + if (dat->dwFlagsEx & MWF_EX_DELAYEDSPLITTER) { + dat->dwFlagsEx &= ~MWF_EX_DELAYEDSPLITTER; + ShowWindow(dat->pContainer->hwnd, SW_RESTORE); + PostMessage(hwndDlg, DM_SPLITTERGLOBALEVENT, dat->wParam, dat->lParam); + dat->wParam = dat->lParam = 0; + } + if (dat->dwFlagsEx & MWF_EX_AVATARCHANGED) { + dat->dwFlagsEx &= ~MWF_EX_AVATARCHANGED; + PostMessage(hwndDlg, DM_UPDATEPICLAYOUT, 0, 0); + } + BB_SetButtonsPos(dat); + if (M.isAero()) + InvalidateRect(hwndTab, NULL, FALSE); + if (dat->pContainer->dwFlags & CNT_SIDEBAR) + dat->pContainer->SideBar->setActiveItem(dat); + + if (dat->pWnd) + dat->pWnd->Invalidate(); + } +} + +void TSAPI ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) +{ + for (int i = 0; i < cControls; i++) + Utils::showDlgControl(hwndDlg, controls[i], state); +} + +void TSAPI SetDialogToType(HWND hwndDlg) +{ + TWindowData *dat = (TWindowData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + int showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1; + + if (dat->hContact) { + if (db_get_b(dat->hContact, "CList", "NotOnList", 0)) { + dat->bNotOnList = TRUE; + ShowMultipleControls(hwndDlg, addControls, 2, SW_SHOW); + Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_SHOW); + SetWindowText(GetDlgItem(hwndDlg, IDC_LOGFROZENTEXT), TranslateT("Contact not on list. You may add it...")); + } + else { + ShowMultipleControls(hwndDlg, addControls, 2, SW_HIDE); + dat->bNotOnList = FALSE; + Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_HIDE); + } + } + + Utils::enableDlgControl(hwndDlg, IDC_TIME, TRUE); + + if (dat->hwndIEView || dat->hwndHPP) { + Utils::showDlgControl(hwndDlg, IDC_LOG, SW_HIDE); + Utils::enableDlgControl(hwndDlg, IDC_LOG, FALSE); + Utils::showDlgControl(hwndDlg, IDC_MESSAGE, SW_SHOW); + } + else ShowMultipleControls(hwndDlg, sendControls, sizeof(sendControls) / sizeof(sendControls[0]), SW_SHOW); + + ShowMultipleControls(hwndDlg, errorControls, sizeof(errorControls) / sizeof(errorControls[0]), dat->dwFlags & MWF_ERRORSTATE ? SW_SHOW : SW_HIDE); + + if (!dat->SendFormat) + ShowMultipleControls(hwndDlg, &formatControls[1], 5, SW_HIDE); + + ConfigureSmileyButton(dat); + + if (dat->pContainer->hwndActive == hwndDlg) + UpdateReadChars(dat); + + SetDlgItemText(hwndDlg, IDC_STATICTEXT, TranslateT("A message failed to send successfully.")); + + DM_RecalcPictureSize(dat); + GetAvatarVisibility(hwndDlg, dat); + + Utils::showDlgControl(hwndDlg, IDC_CONTACTPIC, dat->bShowAvatar ? SW_SHOW : SW_HIDE); + Utils::showDlgControl(hwndDlg, IDC_SPLITTER, dat->bIsAutosizingInput ? SW_HIDE : SW_SHOW); + Utils::showDlgControl(hwndDlg, IDC_MULTISPLITTER, (dat->sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); + + EnableSendButton(dat, GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)) != 0); + SendMessage(hwndDlg, DM_UPDATETITLE, 0, 1); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + + Utils::enableDlgControl(hwndDlg, IDC_CONTACTPIC, FALSE); + + dat->Panel->Configure(); +} + +static LRESULT CALLBACK MessageLogSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND hwndParent = GetParent(hwnd); + TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); + BOOL isCtrl, isShift, isAlt; + KbdState(mwdat, isShift, isCtrl, isAlt); + + switch (msg) { + case WM_KILLFOCUS: + if (wParam != (WPARAM)hwnd && 0 != wParam) { + CHARRANGE cr; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&cr); + if (cr.cpMax != cr.cpMin) { + cr.cpMin = cr.cpMax; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&cr); + } + } + break; + + case WM_CHAR: + if (wParam == 0x03 && isCtrl) // Ctrl+C + return DM_WMCopyHandler(hwnd, MessageLogSubclassProc, msg, wParam, lParam); + if (wParam == 0x11 && isCtrl) + SendMessage(mwdat->hwnd, WM_COMMAND, IDC_QUOTE, 0); + break; + + case WM_SYSKEYUP: + if (wParam == VK_MENU) { + ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_LOG); + return 0; + } + break; + + case WM_SYSKEYDOWN: + mwdat->fkeyProcessed = false; + if (ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_LOG)) { + mwdat->fkeyProcessed = true; + return 0; + } + break; + + case WM_SYSCHAR: + if (mwdat->fkeyProcessed) { + mwdat->fkeyProcessed = false; + return 0; + } + break; + + case WM_KEYDOWN: + if (wParam == VK_INSERT && isCtrl) + return DM_WMCopyHandler(hwnd, MessageLogSubclassProc, msg, wParam, lParam); + break; + + case WM_COPY: + return DM_WMCopyHandler(hwnd, MessageLogSubclassProc, msg, wParam, lParam); + + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, MessageLogSubclassProc); + + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, MessageLogSubclassProc); + + case WM_CONTEXTMENU: + POINT pt; + + if (lParam == 0xFFFFFFFF) { + CHARRANGE sel; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); + ClientToScreen(hwnd, &pt); + } + else { + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + } + + ShowPopupMenu(mwdat, IDC_LOG, hwnd, pt); + return TRUE; + } + + return mir_callNextSubclass(hwnd, MessageLogSubclassProc, msg, wParam, lParam); +} + +static LRESULT CALLBACK MessageEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + BOOL isCtrl, isShift, isAlt; + HWND hwndParent = GetParent(hwnd); + TWindowData *mwdat = (TWindowData*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); + if (mwdat == NULL) + return 0; + + // prevent the rich edit from switching text direction or keyboard layout when + // using hotkeys with ctrl-shift or alt-shift modifiers + if (mwdat->fkeyProcessed && (msg == WM_KEYUP)) { + GetKeyboardState(mwdat->kstate); + if (mwdat->kstate[VK_CONTROL] & 0x80 || mwdat->kstate[VK_SHIFT] & 0x80) + return 0; + + mwdat->fkeyProcessed = false; + return 0; + } + + switch (msg) { + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, MessageEditSubclassProc); + + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, MessageEditSubclassProc); + + case WM_DROPFILES: + SendMessage(hwndParent, WM_DROPFILES, (WPARAM)wParam, (LPARAM)lParam); + break; + + case WM_CHAR: + KbdState(mwdat, isShift, isCtrl, isAlt); + + if (PluginConfig.g_bSoundOnTyping && !isAlt && !isCtrl && !(mwdat->pContainer->dwFlags & CNT_NOSOUND) && wParam != VK_ESCAPE && !(wParam == VK_TAB && PluginConfig.m_AllowTab)) + SkinPlaySound("SoundOnTyping"); + + if (isCtrl && !isAlt) { + switch (wParam) { + case 0x02: // bold + if (mwdat->SendFormat) + SendMessage(hwndParent, WM_COMMAND, MAKELONG(IDC_FONTBOLD, IDC_MESSAGE), 0); + return 0; + case 0x09: + if (mwdat->SendFormat) + SendMessage(hwndParent, WM_COMMAND, MAKELONG(IDC_FONTITALIC, IDC_MESSAGE), 0); + return 0; + case 21: + if (mwdat->SendFormat) + SendMessage(hwndParent, WM_COMMAND, MAKELONG(IDC_FONTUNDERLINE, IDC_MESSAGE), 0); + return 0; + case 0x0b: + SetWindowText(hwnd, _T("")); + return 0; + } + } + break; + + case WM_MOUSEWHEEL: + if (DM_MouseWheelHandler(hwnd, hwndParent, mwdat, wParam, lParam) == 0) + return 0; + break; + + case WM_PASTE: + case EM_PASTESPECIAL: + if (OpenClipboard(hwnd)) { + HANDLE hClip = GetClipboardData(CF_TEXT); + if (hClip) { + if (lstrlenA((char*)hClip) > mwdat->nMax) { + TCHAR szBuffer[512]; + if (M.GetByte("autosplit", 0)) + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("WARNING: The message you are trying to paste exceeds the message size limit for the active protocol. It will be sent in chunks of max %d characters"), mwdat->nMax - 10); + else + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("The message you are trying to paste exceeds the message size limit for the active protocol. Only the first %d characters will be sent."), mwdat->nMax); + SendMessage(hwndParent, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer); + } + } + else if (hClip = GetClipboardData(CF_BITMAP)) + SendHBitmapAsFile(mwdat, (HBITMAP)hClip); + + CloseClipboard(); + } + break; + + case WM_KEYDOWN: + KbdState(mwdat, isShift, isCtrl, isAlt); + + if (PluginConfig.g_bSoundOnTyping && !isAlt && !(mwdat->pContainer->dwFlags & CNT_NOSOUND) && wParam == VK_DELETE) + SkinPlaySound("SoundOnTyping"); + + if (wParam == VK_INSERT && !isShift && !isCtrl && !isAlt) { + mwdat->fInsertMode = !mwdat->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 (wParam == VK_RETURN) { + if (mwdat->fEditNotesActive) + break; + + if (isShift) { + if (PluginConfig.m_SendOnShiftEnter) { + PostMessage(hwndParent, WM_COMMAND, IDOK, 0); + return 0; + } + else break; + } + if ((isCtrl && !isShift) ^ (0 != PluginConfig.m_SendOnEnter)) { + PostMessage(hwndParent, WM_COMMAND, IDOK, 0); + return 0; + } + if (PluginConfig.m_SendOnEnter || PluginConfig.m_SendOnDblEnter) { + if (isCtrl) + break; + + if (PluginConfig.m_SendOnDblEnter) { + LONG_PTR lastEnterTime = GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (lastEnterTime + 2 < time(NULL)) { + lastEnterTime = time(NULL); + SetWindowLongPtr(hwnd, GWLP_USERDATA, lastEnterTime); + break; + } + else { + 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 break; + } + else SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + + if (isCtrl && !isAlt && !isShift) { + if (!isShift && (wParam == VK_UP || wParam == VK_DOWN)) { // input history scrolling (ctrl-up / down) + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + if (mwdat) + mwdat->cache->inputHistoryEvent(wParam); + return 0; + } + } + 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; + + SetWindowLongPtr(hwnd, GWLP_USERDATA, 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) { + DM_ScrollToBottom(mwdat, 0, 0); + return 0; + } + else if (wParam == VK_DOWN) + wp = MAKEWPARAM(SB_LINEDOWN, 0); + + if (mwdat->hwndIEView == 0 && mwdat->hwndHPP == 0) + SendMessage(GetDlgItem(hwndParent, IDC_LOG), WM_VSCROLL, wp, 0); + else + SendMessage(mwdat->hwndIWebBrowserControl, WM_VSCROLL, wp, 0); + return 0; + } + } + if (wParam == VK_RETURN) + break; + + case WM_SYSKEYDOWN: + mwdat->fkeyProcessed = false; + if (ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_MESSAGE)) { + mwdat->fkeyProcessed = true; + return 0; + } + break; + + case WM_SYSKEYUP: + if (wParam == VK_MENU) { + ProcessHotkeysByMsgFilter(hwnd, msg, wParam, lParam, IDC_MESSAGE); + return 0; + } + break; + + case WM_SYSCHAR: + if (mwdat->fkeyProcessed) { + mwdat->fkeyProcessed = false; + return 0; + } + + KbdState(mwdat, isShift, isCtrl, isAlt); + if ((wParam >= '0' && wParam <= '9') && isAlt) { // ALT-1 -> ALT-0 direct tab selection + BYTE bChar = (BYTE)wParam; + int iIndex; + + if (bChar == '0') + iIndex = 10; + else + iIndex = bChar - (BYTE)'0'; + SendMessage(mwdat->pContainer->hwnd, DM_SELECTTAB, DM_SELECT_BY_INDEX, (LPARAM)iIndex); + return 0; + } + break; + + case WM_INPUTLANGCHANGE: + if (PluginConfig.m_AutoLocaleSupport && GetFocus() == hwnd && mwdat->pContainer->hwndActive == hwndParent && GetForegroundWindow() == mwdat->pContainer->hwnd && GetActiveWindow() == mwdat->pContainer->hwnd) { + DM_SaveLocale(mwdat, 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 ? 0 : 1); + + // sent by smileyadd when the smiley selection window dies + // just grab the focus :) + case WM_USER + 100: + SetFocus(hwnd); + break; + + case WM_CONTEXTMENU: + POINT pt; + if (lParam == 0xFFFFFFFF) { + CHARRANGE sel; + SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); + SendMessage(hwnd, EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); + ClientToScreen(hwnd, &pt); + } + else { + pt.x = (short)LOWORD(lParam); + pt.y = (short)HIWORD(lParam); + } + + ShowPopupMenu(mwdat, IDC_MESSAGE, hwnd, pt); + return TRUE; + } + return mir_callNextSubclass(hwnd, MessageEditSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// subclasses the avatar display controls, needed for skinning and to prevent +// it from flickering during resize/move operations. + +static LRESULT CALLBACK AvatarSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_ERASEBKGND: + return TRUE; + + case WM_UPDATEUISTATE: + return TRUE; + + case WM_NOTIFY: + ProcessAvatarChange(hwnd, lParam); + break; + } + return mir_callNextSubclass(hwnd, AvatarSubclassProc, msg, wParam, lParam); +} + +LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + RECT rc; + HWND hwndParent = GetParent(hwnd); + TWindowData *dat = (TWindowData*)GetWindowLongPtr(hwndParent, GWLP_USERDATA); + + switch (msg) { + case WM_NCHITTEST: + return HTCLIENT; + + case WM_SETCURSOR: + GetClientRect(hwnd, &rc); + SetCursor(rc.right > rc.bottom ? PluginConfig.hCurSplitNS : PluginConfig.hCurSplitWE); + return TRUE; + + case WM_LBUTTONDOWN: + if (hwnd == GetDlgItem(hwndParent, IDC_SPLITTER) || hwnd == GetDlgItem(hwndParent, IDC_SPLITTERY)) { + if (dat) { + GetClientRect(hwnd, &rc); + dat->savedSplitter = rc.right > rc.bottom ? (short)HIWORD(GetMessagePos()) + rc.bottom / 2 : (short)LOWORD(GetMessagePos()) + rc.right / 2; + if (dat->bType == SESSIONTYPE_IM) + dat->savedSplitY = dat->splitterY; + else + dat->savedSplitY = dat->si->iSplitterY; + + dat->savedDynaSplit = dat->dynaSplitter; + } + } + SetCapture(hwnd); + return 0; + + case WM_MOUSEMOVE: + if (GetCapture() == hwnd) { + GetClientRect(hwnd, &rc); + SendMessage(hwndParent, DM_SPLITTERMOVED, rc.right > rc.bottom ? (short)HIWORD(GetMessagePos()) + rc.bottom / 2 : (short)LOWORD(GetMessagePos()) + rc.right / 2, (LPARAM)hwnd); + } + return 0; + + case WM_ERASEBKGND: + return 1; + + case WM_PAINT: + GetClientRect(hwnd, &rc); + { + PAINTSTRUCT ps; + HDC dc = BeginPaint(hwnd, &ps); + + if (dat && CSkin::m_skinEnabled) + CSkin::SkinDrawBG(hwnd, dat->pContainer->hwnd, dat->pContainer, &rc, dc); + else if (M.isAero() || M.isVSThemed()) { + if (GetDlgCtrlID(hwnd) == IDC_PANELSPLITTER) { + EndPaint(hwnd, &ps); + return 0; + } + CSkin::FillBack(dc, &rc); + } + else CSkin::FillBack(dc, &rc); + + EndPaint(hwnd, &ps); + } + return 0; + + case WM_LBUTTONUP: + HWND hwndCapture = GetCapture(); + + ReleaseCapture(); + DM_ScrollToBottom(dat, 0, 1); + if (dat && dat->bType == SESSIONTYPE_IM && hwnd == GetDlgItem(hwndParent, IDC_PANELSPLITTER)) { + SendMessage(hwndParent, WM_SIZE, 0, 0); + RedrawWindow(hwndParent, NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); + } + else if ((dat && dat->bType == SESSIONTYPE_IM && hwnd == GetDlgItem(hwndParent, IDC_SPLITTER)) || + (dat && dat->bType == SESSIONTYPE_CHAT && hwnd == GetDlgItem(hwndParent, IDC_SPLITTERY))) { + POINT pt; + int selection; + HMENU hMenu = GetSubMenu(dat->pContainer->hMenuContext, 12); + LONG messagePos = GetMessagePos(); + + GetClientRect(hwnd, &rc); + if (hwndCapture != hwnd || dat->savedSplitter == (rc.right > rc.bottom ? (short) HIWORD(messagePos) + rc.bottom / 2 : (short) LOWORD(messagePos) + rc.right / 2)) + break; + GetCursorPos(&pt); + + if (dat->bIsAutosizingInput) + selection = ID_SPLITTERCONTEXT_SETPOSITIONFORTHISSESSION; + else + selection = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndParent, NULL); + + switch (selection) { + case ID_SPLITTERCONTEXT_SAVEFORTHISCONTACTONLY: + dat->dwFlagsEx |= MWF_SHOW_SPLITTEROVERRIDE; + db_set_b(dat->hContact, SRMSGMOD_T, "splitoverride", 1); + if (dat->bType == SESSIONTYPE_IM) + SaveSplitter(dat); + break; + + case ID_SPLITTERCONTEXT_SETPOSITIONFORTHISSESSION: + if (dat->bIsAutosizingInput) { + GetWindowRect(GetDlgItem(dat->hwnd, IDC_MESSAGE), &rc); + dat->iInputAreaHeight = 0; + } + break; + + case ID_SPLITTERCONTEXT_SAVEGLOBALFORALLSESSIONS: + { + RECT rcWin; + BYTE bSync = M.GetByte(CHAT_MODULE, "SyncSplitter", 0); + DWORD dwOff_IM = 0, dwOff_CHAT = 0; + + dwOff_CHAT = -(2 + (PluginConfig.g_DPIscaleY > 1.0 ? 1 : 0)); + dwOff_IM = 2 + (PluginConfig.g_DPIscaleY > 1.0 ? 1 : 0); + + GetWindowRect(hwndParent, &rcWin); + PluginConfig.lastSPlitterPos.pSrcDat = dat; + PluginConfig.lastSPlitterPos.pSrcContainer = dat->pContainer; + PluginConfig.lastSPlitterPos.lParam = rc.bottom; + PluginConfig.lastSPlitterPos.pos = rcWin.bottom - HIWORD(messagePos); + PluginConfig.lastSPlitterPos.pos_chat = rcWin.bottom - (short)HIWORD(messagePos) + rc.bottom / 2; + PluginConfig.lastSPlitterPos.off_chat = dwOff_CHAT; + PluginConfig.lastSPlitterPos.off_im = dwOff_IM; + PluginConfig.lastSPlitterPos.bSync = bSync; + SendMessage(dat->hwnd, DM_SPLITTERGLOBALEVENT, 0, 0); + M.BroadcastMessage(DM_SPLITTERGLOBALEVENT, 0, 0); + } + break; + + default: + dat->splitterY = dat->savedSplitY; + dat->dynaSplitter = dat->savedDynaSplit; + DM_RecalcPictureSize(dat); + if (dat->bType == SESSIONTYPE_CHAT) { + SESSION_INFO *si = dat->si; + si->iSplitterY = dat->savedSplitY; + dat->splitterY = si->iSplitterY + DPISCALEY_S(22); + } + CSkin::UpdateToolbarBG(dat); + SendMessage(hwndParent, WM_SIZE, 0, 0); + DM_ScrollToBottom(dat, 0, 1); + break; + } + } + return 0; + } + return mir_callNextSubclass(hwnd, SplitterSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// resizer proc for the "new" layout. + +static int MessageDialogResize(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL * urc) +{ + TWindowData *dat = (TWindowData*)lParam; + RECT rc, rcButton; + static int uinWidth, msgTop = 0, msgBottom = 0; + + int showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1; + BOOL bBottomToolbar = dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR ? 1 : 0; + static LONG rcLogBottom; + + int panelHeight = dat->Panel->getHeight() + 1; + int s_offset = 0; + bool bInfoPanel = dat->Panel->isActive(); + bool fErrorState = (dat->dwFlags & MWF_ERRORSTATE) ? true : false; + + GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rc); + GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOCOL), &rcButton); + + if (dat->panelStatusCX == 0) + dat->panelStatusCX = 80; + + s_offset = 1; + + 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: + if (dat->dwFlags & MWF_ERRORSTATE) + urc->rcItem.bottom -= ERRORPANEL_HEIGHT; + if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED || dat->bNotOnList) + urc->rcItem.bottom -= 20; + if (dat->sendMode & SMODE_MULTIPLE) + urc->rcItem.right -= (dat->multiSplitterX + 3); + urc->rcItem.bottom -= dat->splitterY - dat->originalSplitterY; + if (!showToolbar||bBottomToolbar) + urc->rcItem.bottom += 21; + if (bInfoPanel) + urc->rcItem.top += panelHeight; + urc->rcItem.bottom += 3; + 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; + } + } + rcLogBottom = urc->rcItem.bottom; + return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; + + case IDC_CONTACTPIC: + GetClientRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &rc); + urc->rcItem.top -= dat->splitterY - dat->originalSplitterY; + urc->rcItem.left = urc->rcItem.right - (dat->pic.cx + 2); + if ((urc->rcItem.bottom - urc->rcItem.top) < (dat->pic.cy/* + 2*/) && dat->bShowAvatar) { + urc->rcItem.top = urc->rcItem.bottom - dat->pic.cy; + dat->bUseOffset = true; + } + else dat->bUseOffset = false; + + if (showToolbar && bBottomToolbar && (PluginConfig.m_AlwaysFullToolbarWidth || ((dat->pic.cy - DPISCALEY_S(6)) < rc.bottom))) { + urc->rcItem.bottom -= DPISCALEY_S(22); + if (dat->bIsAutosizingInput) { + urc->rcItem.left--; + urc->rcItem.top--; + } + } + + if (dat->hwndContactPic) //if Panel control was created? + SetWindowPos(dat->hwndContactPic, HWND_TOP, 1, ((urc->rcItem.bottom - urc->rcItem.top) - (dat->pic.cy)) / 2 + 1, //resizes it + dat->pic.cx - 2, dat->pic.cy - 2, SWP_SHOWWINDOW); + + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + + case IDC_SPLITTER: + urc->rcItem.right = urc->dlgNewSize.cx; + urc->rcItem.top -= dat->splitterY - dat->originalSplitterY; + urc->rcItem.bottom = urc->rcItem.top + 2; + OffsetRect(&urc->rcItem, 0, 1); + urc->rcItem.left = 0; + + if (dat->bUseOffset) + urc->rcItem.right -= (dat->pic.cx); // + DPISCALEX(2)); + return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; + + case IDC_MESSAGE: + urc->rcItem.right = urc->dlgNewSize.cx; + if (dat->bShowAvatar) + urc->rcItem.right -= dat->pic.cx + 2; + urc->rcItem.top -= dat->splitterY - dat->originalSplitterY; + if (bBottomToolbar&&showToolbar) + urc->rcItem.bottom -= DPISCALEY_S(22); + + if (dat->bIsAutosizingInput) + urc->rcItem.top -= DPISCALEY_S(1); + + msgTop = urc->rcItem.top; + msgBottom = urc->rcItem.bottom; + 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_BOTTOM; + + case IDC_MULTISPLITTER: + if (bInfoPanel) + urc->rcItem.top += panelHeight; + urc->rcItem.left -= dat->multiSplitterX; + urc->rcItem.right -= dat->multiSplitterX; + urc->rcItem.bottom = rcLogBottom; + return RD_ANCHORX_RIGHT | RD_ANCHORY_HEIGHT; + + case IDC_LOGFROZENTEXT: + urc->rcItem.right = urc->dlgNewSize.cx - 50; + urc->rcItem.bottom = msgTop - (bBottomToolbar ? 0 : 28); + urc->rcItem.top = msgTop - 16 - (bBottomToolbar ? 0 : 28); + if (!showToolbar && !bBottomToolbar) { + urc->rcItem.bottom += 21; + urc->rcItem.top += 21; + } + return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; + + case IDC_ADD: + urc->rcItem.bottom = msgTop - (bBottomToolbar ? 0 : 28); + urc->rcItem.top = msgTop - 18 - (bBottomToolbar ? 0 : 28); + urc->rcItem.right = urc->dlgNewSize.cx - 28; + urc->rcItem.left = urc->rcItem.right - 20; + if (!showToolbar && !bBottomToolbar) { + urc->rcItem.bottom += 21; + urc->rcItem.top += 21; + } + return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; + + case IDC_CANCELADD: + urc->rcItem.bottom = msgTop - (bBottomToolbar ? 0 : 28); + urc->rcItem.top = msgTop - 18 - (bBottomToolbar ? 0 : 28); + urc->rcItem.right = urc->dlgNewSize.cx - 4; + urc->rcItem.left = urc->rcItem.right - 20; + if (!showToolbar && !bBottomToolbar) { + urc->rcItem.bottom += 21; + urc->rcItem.top += 21; + } + return RD_ANCHORX_CUSTOM | RD_ANCHORY_BOTTOM; + + case IDC_TOGGLESIDEBAR: + return RD_ANCHORX_CUSTOM | RD_ANCHORY_CUSTOM; + + case IDC_RETRY: + case IDC_CANCELSEND: + case IDC_MSGSENDLATER: + if (fErrorState) { + urc->rcItem.bottom = msgTop - 5 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); + urc->rcItem.top = msgTop - 25 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); + } + if (!showToolbar && !bBottomToolbar) { + urc->rcItem.bottom += 21; + urc->rcItem.top += 21; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; + + case IDC_STATICTEXT: + case IDC_STATICERRORICON: + if (fErrorState) { + urc->rcItem.bottom = msgTop - 28 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); + urc->rcItem.top = msgTop - 45 - (bBottomToolbar ? 0 : 28) - ((dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? 20 : 0); + } + if (!showToolbar && !bBottomToolbar) { + urc->rcItem.bottom += 21; + urc->rcItem.top += 21; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; +} + +INT_PTR CALLBACK DlgProcMessage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TContainerData *m_pContainer = 0; + TWindowData *dat = (TWindowData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + HWND hwndTab = GetParent(hwndDlg), hwndContainer; + POINT pt, tmp, cur; + + if (dat == 0) { + if (msg == WM_ACTIVATE || msg == WM_SETFOCUS) + return 0; + } + else { + m_pContainer = dat->pContainer; + hwndContainer = m_pContainer->hwnd; + } + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + TNewWindowData *newData = (TNewWindowData*)lParam; + + dat = (TWindowData*)mir_calloc(sizeof(TWindowData)); + if (newData->iTabID >= 0) { + dat->pContainer = newData->pContainer; + m_pContainer = dat->pContainer; + hwndContainer = m_pContainer->hwnd; + } + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + if (Utils::rtf_ctable == 0) + Utils::RTF_CTableInit(); + + dat->dwFlags |= MWF_INITMODE; + dat->bType = SESSIONTYPE_IM; + dat->fInsertMode = FALSE; + dat->fLimitedUpdate = false; + dat->Panel = new CInfoPanel(dat); + + newData->item.lParam = (LPARAM)hwndDlg; + TabCtrl_SetItem(hwndTab, newData->iTabID, &newData->item); + dat->iTabID = newData->iTabID; + dat->hwnd = hwndDlg; + + DM_ThemeChanged(dat); + + pszIDCSAVE_close = TranslateT("Close Session"); + pszIDCSAVE_save = TranslateT("Save and close session"); + + dat->hContact = newData->hContact; + dat->cache = CContactCache::getContactCache(dat->hContact); + dat->cache->updateState(); + dat->cache->setWindowData(hwndDlg, dat); + M.AddWindow(hwndDlg, dat->hContact); + BroadCastContainer(m_pContainer, DM_REFRESHTABINDEX, 0, 0); + dat->pWnd = 0; + dat->sbCustom = 0; + CProxyWindow::add(dat); + dat->szProto = const_cast(dat->cache->getProto()); + dat->bIsMeta = dat->cache->isMeta(); + if (dat->bIsMeta) + dat->cache->updateMeta(); + + dat->cache->updateUIN(); + + if (dat->hContact && dat->szProto != NULL) { + dat->wStatus = db_get_w(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE); + _tcsncpy_s(dat->szStatus, pcli->pfnGetStatusModeDescription(dat->szProto == NULL ? ID_STATUS_OFFLINE : dat->wStatus, 0), _TRUNCATE); + } + else dat->wStatus = ID_STATUS_OFFLINE; + + for (int i = 0; i < SIZEOF(btnControls); i++) + CustomizeButton(GetDlgItem(hwndDlg, btnControls[i])); + + GetMYUIN(dat); + GetClientIcon(dat); + + CustomizeButton(CreateWindowEx(0, _T("MButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 6, DPISCALEY_S(20), + hwndDlg, (HMENU)IDC_TOGGLESIDEBAR, g_hInst, NULL)); + dat->hwndPanelPicParent = CreateWindowEx(WS_EX_TOPMOST, _T("Static"), _T(""), SS_OWNERDRAW | WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, hwndDlg, (HMENU)6000, NULL, NULL); + mir_subclassWindow(dat->hwndPanelPicParent, CInfoPanel::avatarParentSubclass); + + dat->bShowUIElements = (m_pContainer->dwFlags & CNT_HIDETOOLBAR) == 0; + dat->sendMode |= M.GetByte(dat->hContact, "forceansi", 0) ? SMODE_FORCEANSI : 0; + dat->sendMode |= dat->hContact == 0 ? SMODE_MULTIPLE : 0; + dat->sendMode |= M.GetByte(dat->hContact, "no_ack", 0) ? SMODE_NOACK : 0; + + dat->hQueuedEvents = (HANDLE*)mir_calloc(sizeof(HANDLE)* EVENT_QUEUE_SIZE); + dat->iEventQueueSize = EVENT_QUEUE_SIZE; + dat->iCurrentQueueError = -1; + + // message history limit + // hHistoryEvents holds up to n event handles + dat->maxHistory = M.GetDword(dat->hContact, "maxhist", M.GetDword("maxhist", 0)); + dat->curHistory = 0; + if (dat->maxHistory) + dat->hHistoryEvents = (HANDLE*)mir_alloc(dat->maxHistory * sizeof(HANDLE)); + else + dat->hHistoryEvents = NULL; + + if (dat->bIsMeta) + SendMessage(hwndDlg, DM_UPDATEMETACONTACTINFO, 0, 0); + else + SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); + dat->bTabFlash = FALSE; + dat->mayFlashTab = FALSE; + GetMyNick(dat); + + dat->multiSplitterX = (int)M.GetDword(SRMSGMOD, "multisplit", 150); + dat->nTypeMode = PROTOTYPE_SELFTYPING_OFF; + SetTimer(hwndDlg, TIMERID_TYPE, 1000, NULL); + dat->iLastEventType = 0xffffffff; + + // load log option flags... + dat->dwFlags = dat->pContainer->theme.dwFlags; + + // consider per-contact message setting overrides + if (dat->hContact && M.GetDword(dat->hContact, "mwmask", 0)) + LoadLocalFlags(hwndDlg, dat); + + DM_InitTip(dat); + dat->Panel->getVisibility(); + + dat->dwFlagsEx |= M.GetByte(dat->hContact, "splitoverride", 0) ? MWF_SHOW_SPLITTEROVERRIDE : 0; + dat->bIsAutosizingInput = IsAutoSplitEnabled(dat); + dat->iInputAreaHeight = -1; + SetMessageLog(dat); + if (dat->hContact) { + dat->codePage = M.GetDword(dat->hContact, "ANSIcodepage", CP_ACP); + dat->Panel->loadHeight(); + } + + dat->bShowAvatar = GetAvatarVisibility(hwndDlg, dat); + + RECT rc; + GetWindowRect(GetDlgItem(hwndDlg, IDC_SMILEYBTN), &rc); + + Utils::showDlgControl(hwndDlg, IDC_MULTISPLITTER, SW_HIDE); + + GetWindowRect(GetDlgItem(hwndDlg, IDC_SPLITTER), &rc); + pt.y = (rc.top + rc.bottom) / 2; + pt.x = 0; + ScreenToClient(hwndDlg, &pt); + dat->originalSplitterY = pt.y; + if (dat->splitterY == -1) + dat->splitterY = dat->originalSplitterY + 60; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_MESSAGE), &rc); + dat->minEditBoxSize.cx = rc.right - rc.left; + dat->minEditBoxSize.cy = rc.bottom - rc.top; + + BB_InitDlgButtons(dat); + SendMessage(hwndDlg, DM_LOADBUTTONBARICONS, 0, 0); + + BOOL isThemed = TRUE; + if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKBUTTONSNPRESSED].IGNORED && + !SkinItems[ID_EXTBKBUTTONSPRESSED].IGNORED && !SkinItems[ID_EXTBKBUTTONSMOUSEOVER].IGNORED) + isThemed = FALSE; + + SendMessage(GetDlgItem(hwndDlg, IDC_ADD), BUTTONSETASFLATBTN, TRUE, 0); + SendMessage(GetDlgItem(hwndDlg, IDC_CANCELADD), BUTTONSETASFLATBTN, TRUE, 0); + + SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASTHEMEDBTN, isThemed, 0); + SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETCONTAINER, (LPARAM)m_pContainer, 0); + SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASTOOLBARBUTTON, TRUE, 0); + + TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPENING, 0); + + for (int i = 0; i < SIZEOF(tooltips); i++) + SendDlgItemMessage(hwndDlg, tooltips[i].id, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(tooltips[i].text), BATF_TCHAR); + + SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, dat->bNotOnList ? TranslateT("Contact not on list. You may add it...") : + TranslateT("Auto scrolling is disabled (press F12 to enable it)")); + + SendMessage(GetDlgItem(hwndDlg, IDC_SAVE), BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, BATF_TCHAR); + SendMessage(GetDlgItem(hwndDlg, IDC_PROTOCOL), BUTTONADDTOOLTIP, (WPARAM)TranslateT("Click for contact menu\nClick dropdown for window settings"), BATF_TCHAR); + + SetWindowText(GetDlgItem(hwndDlg, IDC_RETRY), TranslateT("Retry")); + + UINT _ctrls[] = { IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER }; + for (int i = 0; i < SIZEOF(_ctrls); i++) { + SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASPUSHBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASFLATBTN, FALSE, 0); + SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASTHEMEDBTN, TRUE, 0); + } + + SetWindowText(GetDlgItem(hwndDlg, IDC_CANCELSEND), TranslateT("Cancel")); + SetWindowText(GetDlgItem(hwndDlg, IDC_MSGSENDLATER), TranslateT("Send later")); + + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETUNDOLIMIT, 0, 0); + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_LINK); + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETEVENTMASK, 0, ENM_REQUESTRESIZE | ENM_MOUSEEVENTS | ENM_SCROLL | ENM_KEYEVENTS | ENM_CHANGE); + + dat->bActualHistory = M.GetByte(dat->hContact, "ActualHistory", 0); + + /* OnO: higligh lines to their end */ + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); + + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETLANGOPTIONS, 0, SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOFONTSIZEADJUST); + + // add us to the tray list (if it exists) + if (PluginConfig.g_hMenuTrayUnread != 0 && dat->hContact != 0 && dat->szProto != NULL) + UpdateTrayMenu(0, dat->wStatus, dat->szProto, dat->szStatus, dat->hContact, FALSE); + + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_AUTOURLDETECT, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXLIMITTEXT, 0, 0x80000000); + + // subclassing stuff + mir_subclassWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), MessageEditSubclassProc); + mir_subclassWindow(GetDlgItem(hwndDlg, IDC_CONTACTPIC), AvatarSubclassProc); + mir_subclassWindow(GetDlgItem(hwndDlg, IDC_SPLITTER), SplitterSubclassProc); + mir_subclassWindow(GetDlgItem(hwndDlg, IDC_MULTISPLITTER), SplitterSubclassProc); + mir_subclassWindow(GetDlgItem(hwndDlg, IDC_PANELSPLITTER), SplitterSubclassProc); + + // load old messages from history (if wanted...) + dat->cache->updateStats(TSessionStats::INIT_TIMER); + if (dat->hContact) { + FindFirstEvent(dat); + dat->nMax = dat->cache->getMaxMessageLength(); + } + LoadContactAvatar(dat); + SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); + LoadOwnAvatar(dat); + + // restore saved msg if any... + if (dat->hContact) { + ptrT tszSavedMsg(db_get_tsa(dat->hContact, SRMSGMOD, "SavedMsg")); + if (tszSavedMsg != 0) { + SETTEXTEX stx = { ST_DEFAULT, 1200 }; + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, tszSavedMsg); + SendQueue::UpdateSaveAndSendButton(dat); + if (m_pContainer->hwndActive == hwndDlg) + UpdateReadChars(dat); + } + } + if (newData->szInitialText) { + if (newData->isWchar) + SetDlgItemTextW(hwndDlg, IDC_MESSAGE, (TCHAR*)newData->szInitialText); + else + SetDlgItemTextA(hwndDlg, IDC_MESSAGE, newData->szInitialText); + int len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE)); + PostMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_SETSEL, len, len); + if (len) + EnableSendButton(dat, TRUE); + } + + for (HANDLE hdbEvent = db_event_last(dat->hContact); hdbEvent; hdbEvent = db_event_prev(dat->hContact, hdbEvent)) { + DBEVENTINFO dbei = { sizeof(dbei) }; + db_event_get(hdbEvent, &dbei); + if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) { + dat->lastMessage = dbei.timestamp; + DM_UpdateLastMessage(dat); + break; + } + } + + SendMessage(hwndContainer, DM_QUERYCLIENTAREA, 0, (LPARAM)&rc); + + WNDCLASS wndClass = { 0 }; + GetClassInfo(g_hInst, _T("RICHEDIT50W"), &wndClass); + mir_subclassWindowFull(GetDlgItem(hwndDlg, IDC_LOG), MessageLogSubclassProc, wndClass.lpfnWndProc); + + SetWindowPos(hwndDlg, 0, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), newData->iActivate ? 0 : SWP_NOZORDER | SWP_NOACTIVATE); + LoadSplitter(dat); + ShowPicture(dat, TRUE); + + if (m_pContainer->dwFlags & CNT_CREATE_MINIMIZED || !newData->iActivate || m_pContainer->dwFlags & CNT_DEFERREDTABSELECT) { + dat->iFlashIcon = PluginConfig.g_IconMsgEvent; + SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL); + dat->mayFlashTab = true; + + DBEVENTINFO dbei = { 0 }; + dbei.eventType = EVENTTYPE_MESSAGE; + FlashOnClist(hwndDlg, dat, dat->hDbEventFirst, &dbei); + + SendMessage(hwndContainer, DM_SETICON, (WPARAM)dat, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_MESSAGE)); + m_pContainer->dwFlags |= CNT_NEED_UPDATETITLE; + dat->dwFlags |= MWF_NEEDCHECKSIZE | MWF_WASBACKGROUNDCREATE | MWF_DEFERREDSCROLL; + } + + if (newData->iActivate) { + m_pContainer->hwndActive = hwndDlg; + ShowWindow(hwndDlg, SW_SHOW); + SetActiveWindow(hwndDlg); + SetForegroundWindow(hwndDlg); + } + else if (m_pContainer->dwFlags & CNT_CREATE_MINIMIZED) { + dat->dwFlags |= MWF_DEFERREDSCROLL; + ShowWindow(hwndDlg, SW_SHOWNOACTIVATE); + m_pContainer->hwndActive = hwndDlg; + m_pContainer->dwFlags |= CNT_DEFERREDCONFIGURE; + } + PostMessage(hwndContainer, DM_UPDATETITLE, dat->hContact, 0); + + DM_RecalcPictureSize(dat); + dat->dwLastActivity = GetTickCount() - 1000; + m_pContainer->dwLastActivity = dat->dwLastActivity; + + if (dat->hwndHPP) + mir_subclassWindow(dat->hwndHPP, HPPKFSubclassProc); + + dat->dwFlags &= ~MWF_INITMODE; + TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPEN, 0); + + // show a popup if wanted... + if (newData->bWantPopup) { + DBEVENTINFO dbei = { sizeof(dbei) }; + newData->bWantPopup = FALSE; + db_event_get(newData->hdbEvent, &dbei); + tabSRMM_ShowPopup(dat->hContact, newData->hdbEvent, dbei.eventType, 0, 0, hwndDlg, dat->cache->getActiveProto(), dat); + } + if (m_pContainer->dwFlags & CNT_CREATE_MINIMIZED) { + m_pContainer->dwFlags &= ~CNT_CREATE_MINIMIZED; + m_pContainer->hwndActive = hwndDlg; + return FALSE; + } + return newData->iActivate != 0; + } + + case WM_ERASEBKGND: + { + HDC hdc = (HDC)wParam; + + RECT rcClient, rcWindow, rc; + HDC hdcMem = 0; + HBITMAP hbm, hbmOld; + HANDLE hpb = 0; + + GetClientRect(hwndDlg, &rcClient); + DWORD cx = rcClient.right - rcClient.left; + DWORD cy = rcClient.bottom - rcClient.top; + + if (CMimAPI::m_haveBufferedPaint) + hpb = CMimAPI::m_pfnBeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, 0, &hdcMem); + else { + hdcMem = CreateCompatibleDC(hdc); + hbm = CSkin::CreateAeroCompatibleBitmap(rcClient, hdc); + hbmOld = (HBITMAP)SelectObject(hdcMem, hbm); + } + + bool bInfoPanel = dat->Panel->isActive(); + bool bAero = M.isAero(); + + if (CSkin::m_skinEnabled) { + UINT item_ids[2] = {ID_EXTBKHISTORY, ID_EXTBKINPUTAREA}; + UINT ctl_ids[2] = {IDC_LOG, IDC_MESSAGE}; + BOOL isEditNotesReason = dat->fEditNotesActive; + BOOL isSendLaterReason = (dat->sendMode & SMODE_SENDLATER); + BOOL isMultipleReason = (dat->sendMode & SMODE_MULTIPLE || dat->sendMode & SMODE_CONTAINER); + + CSkin::SkinDrawBG(hwndDlg, hwndContainer, m_pContainer, &rcClient, hdcMem); + + for (int i=0; i < 2; i++) { + CSkinItem *item = &SkinItems[item_ids[i]]; + if (!item->IGNORED) { + GetWindowRect(GetDlgItem(hwndDlg, ctl_ids[i]), &rcWindow); + pt.x = rcWindow.left; + pt.y = rcWindow.top; + ScreenToClient(hwndDlg, &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; + if (item_ids[i] == ID_EXTBKINPUTAREA && (isMultipleReason || isEditNotesReason || isSendLaterReason)) { + HBRUSH br = CreateSolidBrush(isMultipleReason ? RGB(255, 130, 130) : (isEditNotesReason ? RGB(80, 255, 80) : RGB(80, 80, 255))); + FillRect(hdcMem, &rc, br); + DeleteObject(br); + } + else CSkin::DrawItem(hdcMem, &rc, item); + } + } + } + else { + CSkin::FillBack(hdcMem, &rcClient); + + if (M.isAero()) { + LONG temp = rcClient.bottom; + rcClient.bottom = dat->Panel->isActive() ? dat->Panel->getHeight() + 5 : 5; + FillRect(hdcMem, &rcClient, (HBRUSH)GetStockObject(BLACK_BRUSH)); + rcClient.bottom = temp; + } + } + + // draw the (new) infopanel background. Use the gradient from the statusitem. + GetClientRect(hwndDlg, &rc); + dat->Panel->renderBG(hdcMem, rc, &SkinItems[ID_EXTBKINFOPANELBG], bAero); + + // draw aero related stuff + if (!CSkin::m_skinEnabled) + CSkin::RenderToolbarBG(dat, hdcMem, rcClient); + + // render info panel fields + dat->Panel->renderContent(hdcMem); + + if (hpb) + CSkin::FinalizeBufferedPaint(hpb, &rcClient); + else { + BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hbmOld); + DeleteObject(hbm); + DeleteDC(hdcMem); + } + if (!dat->fLimitedUpdate) + SetAeroMargins(dat->pContainer); + } + return 1; + + case WM_NCPAINT: + return 0; + + case WM_PAINT: + // in skinned mode only, draw the background elements for the 2 richedit controls + // this allows border-less textboxes to appear "skinned" and blended with the background + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwndDlg, &ps); + EndPaint(hwndDlg, &ps); + } + return 0; + + case WM_SIZE: + if (!IsIconic(hwndDlg)) { + if (dat->ipFieldHeight == 0) + dat->ipFieldHeight = CInfoPanel::m_ipConfig.height2; + + if (dat->pContainer->uChildMinHeight > 0 && HIWORD(lParam) >= dat->pContainer->uChildMinHeight) { + if (dat->splitterY > HIWORD(lParam) - DPISCALEY_S(MINLOGHEIGHT)) { + dat->splitterY = HIWORD(lParam) - DPISCALEY_S(MINLOGHEIGHT); + dat->dynaSplitter = dat->splitterY - DPISCALEY_S(34); + DM_RecalcPictureSize(dat); + } + if (dat->splitterY < DPISCALEY_S(MINSPLITTERY)) + LoadSplitter(dat); + } + + HBITMAP hbm = ((dat->Panel->isActive()) && m_pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown); + if (hbm != 0) { + BITMAP bminfo; + GetObject(hbm, sizeof(bminfo), &bminfo); + CalcDynamicAvatarSize(dat, &bminfo); + } + + RECT rc; + GetClientRect(hwndDlg, &rc); + + UTILRESIZEDIALOG urd = { sizeof(urd) }; + urd.hInstance = g_hInst; + urd.hwndDlg = hwndDlg; + urd.lParam = (LPARAM)dat; + urd.lpTemplate = MAKEINTRESOURCEA(IDD_MSGSPLITNEW); + urd.pfnResizer = MessageDialogResize; + CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); + + BB_SetButtonsPos(dat); + + // size info panel fields + if (dat->Panel->isActive()) { + LONG cx = rc.right; + LONG panelHeight = dat->Panel->getHeight(); + + hbm = (m_pContainer->avatarMode == 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown); + double dHeight = 0, dWidth = 0; + Utils::scaleAvatarHeightLimited(hbm, dWidth, dHeight, panelHeight-2); + dat->iPanelAvatarX = (int)dWidth; + dat->iPanelAvatarY = (int)dHeight; + + rc.top = 1; + rc.left = cx - dat->iPanelAvatarX; + rc.bottom = panelHeight - (CSkin::m_bAvatarBorderType ? 2 : 0); + rc.right = cx; + dat->rcPic = rc; + + if (dat->bShowInfoAvatar) { + SetWindowPos(dat->hwndPanelPicParent, HWND_TOP, rc.left - 2, rc.top, rc.right - rc.left, rc.bottom - rc.top + 1, 0); + ShowWindow(dat->hwndPanelPicParent, (dat->iPanelAvatarX == 0) || !dat->Panel->isActive() ? SW_HIDE : SW_SHOW); + } + else { + ShowWindow(dat->hwndPanelPicParent, SW_HIDE); + dat->iPanelAvatarX = dat->iPanelAvatarY = 0; + } + + rc.right = cx - dat->iPanelAvatarX; + rc.left = rc.right - dat->panelStatusCX; + rc.bottom = panelHeight - 3; + rc.top = rc.bottom - dat->ipFieldHeight; + dat->rcStatus = rc; + + rc.left = CInfoPanel::LEFT_OFFSET_LOGO; + rc.right = cx - dat->iPanelAvatarX - (panelHeight < CInfoPanel::DEGRADE_THRESHOLD ? (dat->rcStatus.right - dat->rcStatus.left) + 3 : 0); + rc.bottom = panelHeight - (panelHeight >= CInfoPanel::DEGRADE_THRESHOLD ? dat->ipFieldHeight : 0) - 1; + rc.top = 1; + dat->rcNick = rc; + + rc.left = CInfoPanel::LEFT_OFFSET_LOGO; + rc.right = cx - (dat->iPanelAvatarX + 2) - dat->panelStatusCX; + rc.bottom = panelHeight - 3; + rc.top = rc.bottom - dat->ipFieldHeight; + dat->rcUIN = rc; + + dat->Panel->Invalidate(); + } + + if (GetDlgItem(hwndDlg, IDC_CLIST) != 0) { + RECT rc, rcClient, rcLog; + GetClientRect(hwndDlg, &rcClient); + GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rcLog); + rc.top = 0; + rc.right = rcClient.right; + rc.left = rcClient.right - dat->multiSplitterX; + rc.bottom = rcLog.bottom; + if (dat->Panel->isActive()) + rc.top += (dat->Panel->getHeight() + 1); + MoveWindow(GetDlgItem(hwndDlg, IDC_CLIST), rc.left, rc.top, rc.right - rc.left, rcLog.bottom - rcLog.top, FALSE); + } + + if (dat->hwndIEView || dat->hwndHPP) + ResizeIeView(dat, 0, 0, 0, 0); + + DetermineMinHeight(dat); + } + break; + + case WM_TIMECHANGE: + PostMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); + break; + + case WM_NOTIFY: + if (dat != 0 && ((NMHDR*)lParam)->hwndFrom == dat->hwndTip) { + if (((NMHDR*)lParam)->code == NM_CLICK) + SendMessage(dat->hwndTip, TTM_TRACKACTIVATE, FALSE, 0); + break; + } + + switch (((NMHDR*)lParam)->idFrom) { + case IDC_LOG: + case IDC_MESSAGE: + switch (((NMHDR*)lParam)->code) { + case EN_MSGFILTER: + { + DWORD msg = ((MSGFILTER *) lParam)->msg; + WPARAM wp = ((MSGFILTER *) lParam)->wParam; + LPARAM lp = ((MSGFILTER *) lParam)->lParam; + CHARFORMAT2 cf2; + BOOL isCtrl, isShift, isAlt; + KbdState(dat, isShift, isCtrl, isAlt); + + MSG message; + message.hwnd = hwndDlg; + message.message = msg; + message.lParam = lp; + message.wParam = wp; + + if (msg == WM_SYSKEYUP) { + UINT ctrlId = 0; + + if (wp == VK_MENU) + if (!dat->fkeyProcessed && !(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000) && !(lp & (1 << 24))) + m_pContainer->MenuBar->autoShow(); + + return(_dlgReturn(hwndDlg, 0)); + } + + if ((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && !(GetKeyState(VK_RMENU) & 0x8000)) { + LRESULT mim_hotkey_check = CallService(MS_HOTKEY_CHECK, (WPARAM)&message, (LPARAM)(TABSRMM_HK_SECTION_IM)); + if (mim_hotkey_check) + dat->fkeyProcessed = true; + + switch (mim_hotkey_check) { + case TABSRMM_HK_SETUSERPREFS: + CallService(MS_TABMSG_SETUSERPREFS, dat->hContact, 0); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_NUDGE: + SendNudge(dat); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_SENDFILE: + CallService(MS_FILE_SENDFILE, dat->hContact, 0); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_QUOTEMSG: + SendMessage(hwndDlg, WM_COMMAND, IDC_QUOTE, 0); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_USERMENU: + SendMessage(hwndDlg, WM_COMMAND, IDC_PROTOCOL, 0); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_USERDETAILS: + SendMessage(hwndDlg, WM_COMMAND, MAKELONG(IDC_NAME, BN_CLICKED), 0); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_EDITNOTES: + PostMessage(hwndDlg, WM_COMMAND, MAKELONG(IDC_PIC, BN_CLICKED), 0); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_TOGGLESENDLATER: + if (sendLater->isAvail()) { + dat->sendMode ^= SMODE_SENDLATER; + SetWindowPos(GetDlgItem(hwndDlg, IDC_MESSAGE), 0, 0, 0, 0, 0, SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_NOZORDER| + SWP_NOMOVE|SWP_NOSIZE|SWP_NOCOPYBITS); + RedrawWindow(hwndDlg, 0, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW|RDW_ALLCHILDREN); + } + else + CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK|MB_ICONINFORMATION, TranslateT("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0 and \\b1 send to multiple contacts\\b0 features depend on it.\n\nYou must enable it under \\b1Options->Message Sessions->Advanced tweaks\\b0. Changing this option requires a restart.")); + return(_dlgReturn(hwndDlg, 1)); + case TABSRMM_HK_TOGGLERTL: + { + DWORD dwGlobal = M.GetDword("mwflags", MWF_LOG_DEFAULT); + DWORD dwOldFlags = dat->dwFlags; + DWORD dwMask = M.GetDword(dat->hContact, "mwmask", 0); + DWORD dwFlags = M.GetDword(dat->hContact, "mwflags", 0); + + dat->dwFlags ^= MWF_LOG_RTL; + if ((dwGlobal & MWF_LOG_RTL) != (dat->dwFlags & MWF_LOG_RTL)) { + dwMask |= MWF_LOG_RTL; + dwFlags |= (dat->dwFlags & MWF_LOG_RTL); + } + else { + dwMask &= ~MWF_LOG_RTL; + dwFlags &= ~MWF_LOG_RTL; + } + if (dwMask) { + db_set_dw(dat->hContact, SRMSGMOD_T, "mwmask", dwMask); + db_set_dw(dat->hContact, SRMSGMOD_T, "mwflags", dwFlags); + } + else { + db_unset(dat->hContact, SRMSGMOD_T, "mwmask"); + db_unset(dat->hContact, SRMSGMOD_T, "mwflags"); + } + SendMessage(hwndDlg, DM_OPTIONSAPPLIED, 0, 0); + SendMessage(hwndDlg, DM_DEFERREDREMAKELOG, (WPARAM)hwndDlg, 0); + } + return(_dlgReturn(hwndDlg, 1)); + + case TABSRMM_HK_TOGGLEMULTISEND: + dat->sendMode ^= SMODE_MULTIPLE; + if (dat->sendMode & SMODE_MULTIPLE) + DM_CreateClist(dat); + else if (IsWindow(GetDlgItem(hwndDlg, IDC_CLIST))) + DestroyWindow(GetDlgItem(hwndDlg, IDC_CLIST)); + + HWND hwndEdit = GetDlgItem(hwndDlg, IDC_MESSAGE); + SetWindowPos(hwndEdit, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + RedrawWindow(hwndEdit, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ERASE); + DM_ScrollToBottom(dat, 0, 0); + Utils::showDlgControl(hwndDlg, IDC_MULTISPLITTER, (dat->sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); + Utils::showDlgControl(hwndDlg, IDC_CLIST, (dat->sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE); + if (dat->sendMode & SMODE_MULTIPLE) + SetFocus(GetDlgItem(hwndDlg, IDC_CLIST)); + else + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + RedrawWindow(hwndDlg, 0, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW|RDW_ALLCHILDREN); + return(_dlgReturn(hwndDlg, 1)); + } + if (DM_GenericHotkeysCheck(&message, dat)) { + dat->fkeyProcessed = true; + return(_dlgReturn(hwndDlg, 1)); + } + } + if (wp == VK_BROWSER_BACK || wp == VK_BROWSER_FORWARD) + return 1; + + if (msg == WM_CHAR) { + if (isCtrl && !isShift && !isAlt) { + switch (wp) { + case 23: // ctrl - w + PostMessage(hwndDlg, WM_CLOSE, 1, 0); + break; + case 19: + PostMessage(hwndDlg, WM_COMMAND, IDC_SENDMENU, IDC_SENDMENU); + break; + case 16: + PostMessage(hwndDlg, WM_COMMAND, IDC_PROTOMENU, IDC_PROTOMENU); + break; + case 20: + PostMessage(hwndDlg, WM_COMMAND, IDC_TOGGLETOOLBAR, 1); + break; + } + return 1; + } + } + if (msg == WM_KEYDOWN) { + if ((wp == VK_INSERT && isShift && !isCtrl) || (wp == 'V' && isCtrl && !isShift && !isAlt)) { + SendMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_PASTESPECIAL, CF_UNICODETEXT, 0); + _clrMsgFilter(lParam); + return(_dlgReturn(hwndDlg, 1)); + } + if (isCtrl && isShift) { + if (wp == 0x9) { // ctrl-shift tab + SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_PREV, 0); + _clrMsgFilter(lParam); + return(_dlgReturn(hwndDlg, 1)); + } + } + if (isCtrl && !isShift && !isAlt) { + if (wp == VK_TAB) { + SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_NEXT, 0); + _clrMsgFilter(lParam); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_F4) { + PostMessage(hwndDlg, WM_CLOSE, 1, 0); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_PRIOR) { + SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_PREV, 0); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_NEXT) { + SendMessage(hwndDlg, DM_SELECTTAB, DM_SELECT_NEXT, 0); + return(_dlgReturn(hwndDlg, 1)); + } + } + } + if (msg == WM_SYSKEYDOWN && isAlt) { + if (wp == 0x52) { + SendMessage(hwndDlg, DM_QUERYPENDING, DM_QUERY_MOSTRECENT, 0); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_MULTIPLY) { + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_DIVIDE) { + SetFocus(GetDlgItem(hwndDlg, IDC_LOG)); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_ADD) { + SendMessage(hwndContainer, DM_SELECTTAB, DM_SELECT_NEXT, 0); + return(_dlgReturn(hwndDlg, 1)); + } + if (wp == VK_SUBTRACT) { + SendMessage(hwndContainer, DM_SELECTTAB, DM_SELECT_PREV, 0); + return(_dlgReturn(hwndDlg, 1)); + } + } + + if (msg == WM_KEYDOWN && wp == VK_F12) { + if (isShift || isCtrl || isAlt) + return(_dlgReturn(hwndDlg, 1)); + if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) + SendMessage(hwndDlg, DM_REPLAYQUEUE, 0, 0); + dat->dwFlagsEx ^= MWF_SHOW_SCROLLINGDISABLED; + Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, (dat->bNotOnList || dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED) ? SW_SHOW : SW_HIDE); + if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)) + SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, TranslateT("Contact not on list. You may add it...")); + else + SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, TranslateT("Auto scrolling is disabled (press F12 to enable it)")); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + DM_ScrollToBottom(dat, 1, 1); + return(_dlgReturn(hwndDlg, 1)); + } + //MAD: tabulation mod + if (msg == WM_KEYDOWN && wp == VK_TAB) { + if (PluginConfig.m_AllowTab) { + if (((NMHDR*)lParam)->idFrom == IDC_MESSAGE) + SendMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_REPLACESEL, FALSE, (LPARAM)"\t"); + _clrMsgFilter(lParam); + if (((NMHDR*)lParam)->idFrom != IDC_MESSAGE) + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + return(_dlgReturn(hwndDlg, 1)); + } + else { + if (((NMHDR*)lParam)->idFrom == IDC_MESSAGE) { + if (GetSendButtonState(hwndDlg) != PBS_DISABLED && !(dat->pContainer->dwFlags & CNT_HIDETOOLBAR)) + SetFocus(GetDlgItem(hwndDlg, IDOK)); + else + SetFocus(GetDlgItem(hwndDlg, IDC_LOG)); + return(_dlgReturn(hwndDlg, 1)); + } + if (((NMHDR*)lParam)->idFrom == IDC_LOG) { + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + return(_dlgReturn(hwndDlg, 1)); + } + } + return(_dlgReturn(hwndDlg, 0)); + } + + if (msg == WM_MOUSEWHEEL && (((NMHDR*)lParam)->idFrom == IDC_LOG || ((NMHDR*)lParam)->idFrom == IDC_MESSAGE)) { + RECT rc; + GetCursorPos(&pt); + GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rc); + if (PtInRect(&rc, pt)) { + short wDirection = (short)HIWORD(wp); + if (LOWORD(wp) & MK_SHIFT) { + if (wDirection < 0) + SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0); + else if (wDirection > 0) + SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0); + return 0; + } + return 0; + } + return 1; + } + + if (msg == WM_CHAR && wp == 'c') { + if (isCtrl) { + SendDlgItemMessage(hwndDlg, ((NMHDR*)lParam)->code, WM_COPY, 0, 0); + break; + } + } + if ((msg == WM_LBUTTONDOWN || msg == WM_KEYUP || msg == WM_LBUTTONUP) && ((NMHDR*)lParam)->idFrom == IDC_MESSAGE) { + int bBold = IsDlgButtonChecked(hwndDlg, IDC_FONTBOLD); + int bItalic = IsDlgButtonChecked(hwndDlg, IDC_FONTITALIC); + int bUnder = IsDlgButtonChecked(hwndDlg, IDC_FONTUNDERLINE); + int bStrikeout = IsDlgButtonChecked(hwndDlg, IDC_FONTSTRIKEOUT); + + cf2.cbSize = sizeof(CHARFORMAT2); + cf2.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_UNDERLINETYPE | CFM_STRIKEOUT; + cf2.dwEffects = 0; + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); + if (cf2.dwEffects & CFE_BOLD) { + if (bBold == BST_UNCHECKED) + CheckDlgButton(hwndDlg, IDC_FONTBOLD, BST_CHECKED); + } + else if (bBold == BST_CHECKED) + CheckDlgButton(hwndDlg, IDC_FONTBOLD, BST_UNCHECKED); + + if (cf2.dwEffects & CFE_ITALIC) { + if (bItalic == BST_UNCHECKED) + CheckDlgButton(hwndDlg, IDC_FONTITALIC, BST_CHECKED); + } + else if (bItalic == BST_CHECKED) + CheckDlgButton(hwndDlg, IDC_FONTITALIC, BST_UNCHECKED); + + if (cf2.dwEffects & CFE_UNDERLINE && (cf2.bUnderlineType & CFU_UNDERLINE || cf2.bUnderlineType & CFU_UNDERLINEWORD)) { + if (bUnder == BST_UNCHECKED) + CheckDlgButton(hwndDlg, IDC_FONTUNDERLINE, BST_CHECKED); + } + else if (bUnder == BST_CHECKED) + CheckDlgButton(hwndDlg, IDC_FONTUNDERLINE, BST_UNCHECKED); + + if (cf2.dwEffects & CFE_STRIKEOUT) { + if (bStrikeout == BST_UNCHECKED) + CheckDlgButton(hwndDlg, IDC_FONTSTRIKEOUT, BST_CHECKED); + } + else if (bStrikeout == BST_CHECKED) + CheckDlgButton(hwndDlg, IDC_FONTSTRIKEOUT, BST_UNCHECKED); + } + switch (msg) { + case WM_LBUTTONDOWN: + { + HCURSOR hCur = GetCursor(); + m_pContainer->MenuBar->Cancel(); + if (hCur == LoadCursor(NULL, IDC_SIZENS) || hCur == LoadCursor(NULL, IDC_SIZEWE) + || hCur == LoadCursor(NULL, IDC_SIZENESW) || hCur == LoadCursor(NULL, IDC_SIZENWSE)) { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + break; + } + + // auto-select-and-copy handling... + // if enabled, releasing the lmb with an active selection automatically copies the selection + // to the clipboard. + // holding ctrl while releasing the button pastes the selection to the input area, using plain text + // holding ctrl-alt does the same, but pastes formatted text + case WM_LBUTTONUP: + if (((NMHDR*) lParam)->idFrom == IDC_LOG) { + CHARRANGE cr; + SendMessage(GetDlgItem(hwndDlg, IDC_LOG), EM_EXGETSEL, 0, (LPARAM)&cr); + if (cr.cpMax != cr.cpMin) { + cr.cpMin = cr.cpMax; + if (isCtrl && M.GetByte("autocopy", 1)) { + SETTEXTEX stx = {ST_KEEPUNDO | ST_SELECTION, CP_UTF8}; + char *streamOut = NULL; + if (isAlt) + streamOut = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, (CP_UTF8 << 16) | (SF_RTFNOOBJS | SFF_PLAINRTF | SFF_SELECTION | SF_USECODEPAGE)); + else + streamOut = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, (CP_UTF8 << 16) | (SF_TEXT | SFF_SELECTION | SF_USECODEPAGE)); + if (streamOut) { + Utils::FilterEventMarkers(streamOut); + SendMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)streamOut); + mir_free(streamOut); + } + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + } + else if (M.GetByte("autocopy", 1) && !isShift) { + SendMessage(GetDlgItem(hwndDlg, IDC_LOG), WM_COPY, 0, 0); + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + if (m_pContainer->hwndStatus) + SendMessage(m_pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM)TranslateT("Selection copied to clipboard")); + } + } + } + break; + + case WM_MOUSEMOVE: + POINT pt; + HCURSOR hCur = GetCursor(); + GetCursorPos(&pt); + DM_DismissTip(dat, pt); + dat->Panel->trackMouse(pt); + if (hCur == LoadCursor(NULL, IDC_SIZENS) || hCur == LoadCursor(NULL, IDC_SIZEWE) || hCur == LoadCursor(NULL, IDC_SIZENESW) || hCur == LoadCursor(NULL, IDC_SIZENWSE)) + SetCursor(LoadCursor(NULL, IDC_ARROW)); + break; + } + } + break; + + case EN_REQUESTRESIZE: + DM_HandleAutoSizeRequest(dat, (REQRESIZE *)lParam); + break; + + case EN_LINK: + switch (((ENLINK *)lParam)->msg) { + case WM_SETCURSOR: + SetCursor(PluginConfig.hCurHyperlinkHand); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + { + CHARRANGE sel; + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) + break; + + TEXTRANGEW tr; + tr.chrg = ((ENLINK*)lParam)->chrg; + tr.lpstrText = (TCHAR*)_alloca(sizeof(TCHAR)*(tr.chrg.cpMax - tr.chrg.cpMin + 8)); + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETTEXTRANGE, 0, (LPARAM)&tr); + if (_tcschr(tr.lpstrText, '@') != NULL && _tcschr(tr.lpstrText, ':') == NULL && _tcschr(tr.lpstrText, '/') == NULL) { + MoveMemory(tr.lpstrText + 7, tr.lpstrText, tr.chrg.cpMax - tr.chrg.cpMin + 1); + CopyMemory(tr.lpstrText, _T("mailto:"), 7); + } + if (!IsStringValidLink(tr.lpstrText)) + break; + + if (((ENLINK*)lParam)->msg != WM_RBUTTONDOWN) { + CallService(MS_UTILS_OPENURL, OUF_TCHAR, (LPARAM)tr.lpstrText); + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + break; + } + HMENU hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT)); + HMENU hSubMenu = GetSubMenu(hMenu, 1); + TranslateMenu(hSubMenu); + pt.x = (short)LOWORD(((ENLINK*)lParam)->lParam); + pt.y = (short)HIWORD(((ENLINK*)lParam)->lParam); + ClientToScreen(((NMHDR*)lParam)->hwndFrom, &pt); + switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL)) { + case IDM_OPENNEW: + CallService(MS_UTILS_OPENURL, 1 + OUF_TCHAR, (LPARAM)tr.lpstrText); + break; + + case IDM_OPENEXISTING: + CallService(MS_UTILS_OPENURL, OUF_TCHAR, (LPARAM)tr.lpstrText); + break; + + case IDM_COPYLINK: + if (!OpenClipboard(hwndDlg)) + break; + + EmptyClipboard(); + HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(TCHAR)*(lstrlen(tr.lpstrText) + 1)); + TCHAR *buf = (TCHAR*)GlobalLock(hData); + lstrcpy(buf, tr.lpstrText); + GlobalUnlock(hData); + SetClipboardData(CF_UNICODETEXT, hData); + CloseClipboard(); + break; + } + DestroyMenu(hMenu); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + } + } + break; + + case DM_TYPING: + { + int preTyping = dat->nTypeSecs != 0; + dat->nTypeSecs = (int)lParam > 0 ? (int)lParam : 0; + + if (dat->nTypeSecs) + dat->bShowTyping = 0; + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, preTyping); + } + return TRUE; + + case DM_UPDATEWINICON: + if (dat->hXStatusIcon) { + DestroyIcon(dat->hXStatusIcon); + dat->hXStatusIcon = 0; + } + + if (LPCSTR szProto = dat->cache->getProto()) { + dat->hTabIcon = dat->hTabStatusIcon = MY_GetContactIcon(dat, "MetaiconTab"); + if (M.GetByte("use_xicons", 1)) + dat->hXStatusIcon = GetXStatusIcon(dat); + + SendDlgItemMessage(hwndDlg, IDC_PROTOCOL, BUTTONSETASDIMMED, (dat->dwFlagsEx & MWF_SHOW_ISIDLE) != 0, 0); + SendDlgItemMessage(hwndDlg, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : MY_GetContactIcon(dat, "MetaiconBar"))); + + if (m_pContainer->hwndActive == hwndDlg) + SendMessage(m_pContainer->hwnd, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabIcon)); + + if (dat->pWnd) + dat->pWnd->updateIcon(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabIcon); + } + return 0; + + // configures the toolbar only... if lParam != 0, then it also calls + // SetDialogToType() to reconfigure the message window + case DM_CONFIGURETOOLBAR: + dat->bShowUIElements = m_pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1; + + SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_SPLITTER), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_SPLITTER), GWL_EXSTYLE) & ~WS_EX_STATICEDGE); + + if (lParam == 1) { + GetSendFormat(dat, 1); + SetDialogToType(hwndDlg); + } + + if (lParam == 1) { + DM_RecalcPictureSize(dat); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + DM_ScrollToBottom(dat, 0, 1); + } + return 0; + + case DM_LOADBUTTONBARICONS: + for (int i=0; i < SIZEOF(buttonicons); i++) { + SendDlgItemMessage(hwndDlg, buttonicons[i].id, BM_SETIMAGE, IMAGE_ICON, (LPARAM)*buttonicons[i].pIcon); + SendDlgItemMessage(hwndDlg, buttonicons[i].id, BUTTONSETCONTAINER, (LPARAM)m_pContainer, 0); + } + + BB_UpdateIcons(hwndDlg, dat); + SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0); + return 0; + + case DM_OPTIONSAPPLIED: + DM_OptionsApplied(dat, wParam, lParam); + return 0; + + case DM_UPDATETITLE: + DM_UpdateTitle(dat, wParam, lParam); + return 0; + + case DM_UPDATESTATUSMSG: + dat->Panel->Invalidate(); + return 0; + + case DM_OWNNICKCHANGED: + GetMyNick(dat); + return 0; + + case DM_ADDDIVIDER: + if (!(dat->dwFlags & MWF_DIVIDERSET) && PluginConfig.m_UseDividers) { + if (GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_LOG)) > 0) { + dat->dwFlags |= MWF_DIVIDERWANTED; + dat->dwFlags |= MWF_DIVIDERSET; + } + } + return 0; + + case WM_SETFOCUS: + MsgWindowUpdateState(dat, WM_SETFOCUS); + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + return 1; + + case WM_ACTIVATE: + if (LOWORD(wParam) != WA_ACTIVE) + break; + + //fall through + case WM_MOUSEACTIVATE: + MsgWindowUpdateState(dat, WM_ACTIVATE); + return 1; + + case DM_UPDATEPICLAYOUT: + LoadContactAvatar(dat); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + return 0; + + case DM_SPLITTERGLOBALEVENT: + DM_SplitterGlobalEvent(dat, wParam, lParam); + return 0; + + case DM_SPLITTERMOVED: + if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_MULTISPLITTER)) { + RECT rc; + GetClientRect(hwndDlg, &rc); + pt.x = wParam; + pt.y = 0; + ScreenToClient(hwndDlg, &pt); + int oldSplitterX = dat->multiSplitterX; + dat->multiSplitterX = rc.right - pt.x; + if (dat->multiSplitterX < 25) + dat->multiSplitterX = 25; + + if (dat->multiSplitterX > ((rc.right - rc.left) - 80)) + dat->multiSplitterX = oldSplitterX; + SendMessage(dat->hwnd, WM_SIZE, 0, 0); + } + else if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SPLITTER)) { + int oldSplitterY, oldDynaSplitter; + int bottomtoolbarH=0; + RECT rc; + GetClientRect(hwndDlg, &rc); + rc.top += (dat->Panel->isActive() ? dat->Panel->getHeight() + 40 : 30); + pt.x = 0; + pt.y = wParam; + ScreenToClient(hwndDlg, &pt); + + oldSplitterY = dat->splitterY; + oldDynaSplitter = dat->dynaSplitter; + + dat->splitterY = rc.bottom - pt.y +DPISCALEY_S(23); + + // attempt to fix splitter troubles.. + // hardcoded limits... better solution is possible, but this works for now + if (dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR) + bottomtoolbarH = 22; + + if (dat->splitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) { // min splitter size + dat->splitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH); + dat->dynaSplitter = dat->splitterY - DPISCALEY_S(34); + DM_RecalcPictureSize(dat); + } + else if (dat->splitterY > (rc.bottom - rc.top)) { + dat->splitterY = oldSplitterY; + dat->dynaSplitter = oldDynaSplitter; + DM_RecalcPictureSize(dat); + } + else { + dat->dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11); + DM_RecalcPictureSize(dat); + } + CSkin::UpdateToolbarBG(dat); + SendMessage(dat->hwnd, WM_SIZE, 0, 0); + } + else if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_PANELSPLITTER)) { + RECT rc; + GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rc); + + POINT pt = { 0, wParam }; + ScreenToClient(hwndDlg, &pt); + if ((pt.y + 2 >= MIN_PANELHEIGHT + 2) && (pt.y + 2 < 100) && (pt.y + 2 < rc.bottom - 30)) + dat->Panel->setHeight(pt.y + 2, true); + + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + if (M.isAero()) + InvalidateRect(GetParent(hwndDlg), NULL, FALSE); + } + break; + + // queue a dm_remakelog + // wParam = hwnd of the sender, so we can directly do a DM_REMAKELOG if the msg came + // from ourself. otherwise, the dm_remakelog will be deferred until next window + // activation (focus) + case DM_DEFERREDREMAKELOG: + if ((HWND) wParam == hwndDlg) + SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); + else { + if (M.GetByte(dat->hContact, "mwoverride", 0) == 0) { + dat->dwFlags &= ~(MWF_LOG_ALL); + dat->dwFlags |= (lParam & MWF_LOG_ALL); + dat->dwFlags |= MWF_DEFERREDREMAKELOG; + } + } + return 0; + + case DM_FORCEDREMAKELOG: + if ((HWND) wParam == hwndDlg) + SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); + else { + dat->dwFlags &= ~(MWF_LOG_ALL); + dat->dwFlags |= (lParam & MWF_LOG_ALL); + dat->dwFlags |= MWF_DEFERREDREMAKELOG; + } + return 0; + + case DM_REMAKELOG: + dat->szMicroLf[0] = 0; + dat->lastEventTime = 0; + dat->iLastEventType = -1; + StreamInEvents(hwndDlg, dat->hDbEventFirst, -1, 0, NULL); + return 0; + + case DM_APPENDMCEVENT: + if (dat->hContact == db_mc_getMeta(wParam) && dat->hDbEventFirst == NULL) { + dat->hDbEventFirst = (HANDLE)lParam; + SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0); + } + else if (dat->hContact == wParam && db_mc_isSub(wParam) && db_event_getContact(HANDLE(lParam)) != wParam) + StreamInEvents(hwndDlg, (HANDLE)lParam, 1, 1, NULL); + return 0; + + case DM_APPENDTOLOG: + StreamInEvents(hwndDlg, (HANDLE)wParam, 1, 1, NULL); + return 0; + + // replays queued events after the message log has been frozen for a while + case DM_REPLAYQUEUE: + for (int i=0; i < dat->iNextQueuedEvent; i++) + if (dat->hQueuedEvents[i] != 0) + StreamInEvents(hwndDlg, dat->hQueuedEvents[i], 1, 1, NULL); + + dat->iNextQueuedEvent = 0; + SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, dat->bNotOnList ? TranslateT("Contact not on list. You may add it...") : + TranslateT("Auto scrolling is disabled (press F12 to enable it)")); + return 0; + + case DM_SCROLLIEVIEW: + { + IEVIEWWINDOW iew = { sizeof(iew) }; + iew.iType = IEW_SCROLLBOTTOM; + if (dat->hwndIEView) { + iew.hwnd = dat->hwndIEView; + CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&iew); + } + else if (dat->hwndHPP) { + iew.hwnd = dat->hwndHPP; + CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew); + } + } + return 0; + + case DM_FORCESCROLL: + { + SCROLLINFO *psi = (SCROLLINFO *)lParam; + POINT *ppt = (POINT *)wParam; + + HWND hwnd = GetDlgItem(hwndDlg, IDC_LOG); + int len; + + if (wParam == 0 && lParam == 0) { + DM_ScrollToBottom(dat, 0, 1); + return 0; + } + + if (dat->hwndIEView == 0 && dat->hwndHPP == 0) { + len = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_LOG)); + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETSEL, len - 1, len - 1); + } + + if (psi == NULL) { + DM_ScrollToBottom(dat, 0, 0); + return 0; + } + + if ((UINT)psi->nPos >= (UINT)psi->nMax - psi->nPage - 5 || psi->nMax - psi->nMin - psi->nPage < 50) + DM_ScrollToBottom(dat, 0, 0); + else + SendMessage((dat->hwndIEView || dat->hwndHPP) ? (dat->hwndIEView ? dat->hwndIEView : dat->hwndHPP) : hwnd, EM_SETSCROLLPOS, 0, (LPARAM)ppt); + } + return 0; + + case HM_DBEVENTADDED: + // this is called whenever a new event has been added to the database. + // this CAN be posted (some sanity checks required). + if (!dat) + return 0; + if (dat->hContact) + DM_EventAdded(dat, dat->hContact, lParam); + return 0; + + case WM_TIMER: + // timer to control info panel hovering + if (wParam == TIMERID_AWAYMSG) { + KillTimer(hwndDlg, wParam); + GetCursorPos(&pt); + + if (wParam == TIMERID_AWAYMSG && dat->Panel->hitTest(pt) != CInfoPanel::HTNIRVANA) + SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, 0, 0); + else + dat->dwFlagsEx &= ~MWF_SHOW_AWAYMSGTIMER; + break; + } + + // timer id for message timeouts is composed like: + // for single message sends: basevalue (TIMERID_MSGSEND) + send queue index + if (wParam >= TIMERID_MSGSEND) { + int iIndex = wParam - TIMERID_MSGSEND; + if (iIndex < SendQueue::NR_SENDJOBS) { // single sendjob timer + SendJob *job = sendQueue->getJobByIndex(iIndex); + KillTimer(hwndDlg, wParam); + mir_sntprintf(job->szErrorMsg, SIZEOF(job->szErrorMsg), TranslateT("Delivery failure: %s"), + TranslateT("The message send timed out")); + job->iStatus = SendQueue::SQ_ERROR; + if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) + SkinPlaySound("SendError"); + if (!(dat->dwFlags & MWF_ERRORSTATE)) + sendQueue->handleError(dat, iIndex); + break; + } + } + else if (wParam == TIMERID_FLASHWND) { + if (dat->mayFlashTab) + FlashTab(dat, hwndTab, dat->iTabID, &dat->bTabFlash, TRUE, dat->hTabIcon); + break; + } + else if (wParam == TIMERID_TYPE) { + DM_Typing(dat); + break; + } + break; + + case DM_ERRORDECIDED: + switch (wParam) { + case MSGERROR_CANCEL: + case MSGERROR_SENDLATER: + if (dat->dwFlags & MWF_ERRORSTATE) { + dat->cache->saveHistory(0, 0); + if (wParam == MSGERROR_SENDLATER) + sendQueue->doSendLater(dat->iCurrentQueueError, dat); // to be implemented at a later time + dat->iOpenJobs--; + sendQueue->dec(); + if (dat->iCurrentQueueError >= 0 && dat->iCurrentQueueError < SendQueue::NR_SENDJOBS) + sendQueue->clearJob(dat->iCurrentQueueError); + dat->iCurrentQueueError = -1; + sendQueue->showErrorControls(dat, FALSE); + if (wParam != MSGERROR_CANCEL || (wParam == MSGERROR_CANCEL && lParam == 0)) + SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); + sendQueue->checkQueue(dat); + int iNextFailed = sendQueue->findNextFailed(dat); + if (iNextFailed >= 0) + sendQueue->handleError(dat, iNextFailed); + } + break; + + case MSGERROR_RETRY: + if (dat->dwFlags & MWF_ERRORSTATE) { + int resent = 0; + + dat->cache->saveHistory(0, 0); + if (dat->iCurrentQueueError >= 0 && dat->iCurrentQueueError < SendQueue::NR_SENDJOBS) { + SendJob *job = sendQueue->getJobByIndex(dat->iCurrentQueueError); + if (job->hSendId == 0 && job->hContact == 0) + break; + + job->hSendId = (HANDLE)CallContactService(job->hContact, PSS_MESSAGE, + (dat->sendMode & SMODE_FORCEANSI) ? (job->dwFlags & ~PREF_UNICODE) : job->dwFlags, (LPARAM)job->szSendBuffer); + resent++; + } + + if (resent) { + int iNextFailed; + SendJob *job = sendQueue->getJobByIndex(dat->iCurrentQueueError); + + SetTimer(hwndDlg, TIMERID_MSGSEND + dat->iCurrentQueueError, PluginConfig.m_MsgTimeout, NULL); + job->iStatus = SendQueue::SQ_INPROGRESS; + dat->iCurrentQueueError = -1; + sendQueue->showErrorControls(dat, FALSE); + SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); + sendQueue->checkQueue(dat); + if ((iNextFailed = sendQueue->findNextFailed(dat)) >= 0) + sendQueue->handleError(dat, iNextFailed); + } + } + } + break; + + case DM_SELECTTAB: + SendMessage(hwndContainer, DM_SELECTTAB, wParam, lParam); // pass the msg to our container + return 0; + + case DM_SETLOCALE: + if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) + break; + if (m_pContainer->hwndActive == hwndDlg && PluginConfig.m_AutoLocaleSupport && hwndContainer == GetForegroundWindow() && hwndContainer == GetActiveWindow()) { + if (lParam) + dat->hkl = (HKL)lParam; + + if (dat->hkl) + ActivateKeyboardLayout(dat->hkl, 0); + } + return 0; + + // return timestamp (in ticks) of last recent message which has not been read yet. + // 0 if there is none + // lParam = pointer to a dword receiving the value. + case DM_QUERYLASTUNREAD: + { + DWORD *pdw = (DWORD *)lParam; + if (pdw) + *pdw = dat->dwTickLastEvent; + } + return 0; + + case DM_QUERYCONTAINER: + { + TContainerData **pc = (TContainerData **)lParam; + if (pc) + *pc = m_pContainer; + } + return 0; + + case DM_QUERYHCONTACT: + { + MCONTACT *phContact = (MCONTACT*)lParam; + if (phContact) + *phContact = dat->hContact; + } + return 0; + + case DM_UPDATELASTMESSAGE: + DM_UpdateLastMessage(dat); + return 0; + + case DM_SAVESIZE: + if (dat->dwFlags & MWF_NEEDCHECKSIZE) + lParam = 0; + + dat->dwFlags &= ~MWF_NEEDCHECKSIZE; + if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) { + dat->dwFlags &= ~MWF_INITMODE; + if (dat->lastMessage) + DM_UpdateLastMessage(dat); + } + + RECT rcClient; + SendMessage(hwndContainer, DM_QUERYCLIENTAREA, 0, (LPARAM)&rcClient); + MoveWindow(hwndDlg, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), TRUE); + if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) { + dat->dwFlags &= ~MWF_WASBACKGROUNDCREATE; + SendMessage(hwndDlg, WM_SIZE, 0, 0); + PostMessage(hwndDlg, DM_UPDATEPICLAYOUT, 0, 0); + if (PluginConfig.m_AutoLocaleSupport) { + if (dat->hkl == 0) + DM_LoadLocale(dat); + else + PostMessage(hwndDlg, DM_SETLOCALE, 0, 0); + } + if (dat->hwndIEView != 0) + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + if (dat->pContainer->dwFlags & CNT_SIDEBAR) + dat->pContainer->SideBar->Layout(); + } + else { + SendMessage(hwndDlg, WM_SIZE, 0, 0); + if (lParam == 0) + PostMessage(hwndDlg, DM_FORCESCROLL, 0, 0); + } + return 0; + + case DM_CHECKSIZE: + dat->dwFlags |= MWF_NEEDCHECKSIZE; + return 0; + + // sent by the message input area hotkeys. just pass it to our container + case DM_QUERYPENDING: + SendMessage(hwndContainer, DM_QUERYPENDING, wParam, lParam); + return 0; + + case WM_LBUTTONDOWN: + GetCursorPos(&tmp); + cur.x = (SHORT)tmp.x; + cur.y = (SHORT)tmp.y; + if (!dat->Panel->isHovered()) + SendMessage(hwndContainer, WM_NCLBUTTONDOWN, HTCAPTION, *((LPARAM*)(&cur))); + break; + + case WM_LBUTTONUP: + GetCursorPos(&tmp); + if (dat->Panel->isHovered()) + dat->Panel->handleClick(tmp); + else { + cur.x = (SHORT)tmp.x; + cur.y = (SHORT)tmp.y; + SendMessage(hwndContainer, WM_NCLBUTTONUP, HTCAPTION, *((LPARAM*)(&cur))); + } + break; + + case WM_RBUTTONUP: + { + RECT rcPicture, rcPanelNick = {0}; + int menuID = 0; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_CONTACTPIC), &rcPicture); + rcPanelNick.left = rcPanelNick.right - 30; + GetCursorPos(&pt); + + if (dat->Panel->invokeConfigDialog(pt)) + break; + + if (PtInRect(&rcPicture, pt)) + menuID = MENU_PICMENU; + + if ((menuID == MENU_PICMENU && ((dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown) || dat->hOwnPic) && dat->bShowAvatar != 0)) { + HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, menuID == MENU_PICMENU ? 1 : 11); + GetCursorPos(&pt); + MsgWindowUpdateMenu(dat, submenu, menuID); + int iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); + MsgWindowMenuHandler(dat, iSelection, menuID); + break; + } + + HMENU subMenu = GetSubMenu(m_pContainer->hMenuContext, 0); + MsgWindowUpdateMenu(dat, subMenu, MENU_TABCONTEXT); + + int iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); + if (iSelection >= IDM_CONTAINERMENU) { + char szIndex[10]; + char *szKey = "TAB_ContainersW"; + + mir_snprintf(szIndex, SIZEOF(szIndex), "%d", iSelection - IDM_CONTAINERMENU); + if (iSelection - IDM_CONTAINERMENU >= 0) { + ptrT val(db_get_tsa(NULL, szKey, szIndex)); + if (val) + SendMessage(hwndDlg, DM_CONTAINERSELECTED, 0, (LPARAM)val); + } + break; + } + MsgWindowMenuHandler(dat, iSelection, MENU_TABCONTEXT); + } + break; + + case WM_MOUSEMOVE: + GetCursorPos(&pt); + DM_DismissTip(dat, pt); + dat->Panel->trackMouse(pt); + break; + + case WM_MEASUREITEM: + { + LPMEASUREITEMSTRUCT lpmi = (LPMEASUREITEMSTRUCT) lParam; + if (dat->Panel->isHovered()) { + lpmi->itemHeight = 0; + lpmi->itemWidth = 6; + return TRUE; + } + } + return CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam); + + case WM_NCHITTEST: + SendMessage(hwndContainer, WM_NCHITTEST, wParam, lParam); + break; + + case WM_DRAWITEM: + return MsgWindowDrawHandler(wParam, lParam, dat); + + case WM_APPCOMMAND: + { + DWORD cmd = GET_APPCOMMAND_LPARAM(lParam); + if (cmd == APPCOMMAND_BROWSER_BACKWARD || cmd == APPCOMMAND_BROWSER_FORWARD) { + SendMessage(hwndContainer, DM_SELECTTAB, cmd == APPCOMMAND_BROWSER_BACKWARD ? DM_SELECT_PREV : DM_SELECT_NEXT, 0); + return 1; + } + } + break; + + case WM_COMMAND: + if (!dat) + break; + + // custom button handling + if (LOWORD(wParam) >= MIN_CBUTTONID && LOWORD(wParam) <= MAX_CBUTTONID) { + BB_CustomButtonClick(dat, LOWORD(wParam), GetDlgItem(hwndDlg, LOWORD(wParam)), 0); + break; + } + + switch (LOWORD(wParam)) { + case IDOK: + if (dat->fEditNotesActive) { + SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_PIC, (LPARAM)TranslateT("You are editing the user notes. Click the button again or use the hotkey (default: Alt-N) to save the notes and return to normal messaging mode")); + return 0; + } + else { + // don't parse text formatting when the message contains curly braces - these are used by the rtf syntax + // and the parser currently cannot handle them properly in the text - XXX needs to be fixed later. + FINDTEXTEXA fi = { 0 }; + fi.chrg.cpMin = 0; + fi.chrg.cpMax = -1; + fi.lpstrText = "{"; + int final_sendformat = SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) == -1 ? dat->SendFormat : 0; + fi.lpstrText = "}"; + final_sendformat = SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) == -1 ? final_sendformat : 0; + + if (GetSendButtonState(hwndDlg) == PBS_DISABLED) + break; + + ptrA streamOut(Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, final_sendformat ? 0 : (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE))); + if (streamOut == NULL) + break; + + ptrT decoded(mir_utf8decodeT(streamOut)); + if (decoded == NULL) + break; + + char *utfResult = NULL; + if (final_sendformat) + DoRtfToTags(decoded, dat); + rtrimt(decoded); + int bufSize = WideCharToMultiByte(dat->codePage, 0, decoded, -1, dat->sendBuffer, 0, 0, 0); + + int memRequired = 0, flags = 0; + if (!IsUtfSendAvailable(dat->hContact)) { + flags |= PREF_UNICODE; + memRequired = bufSize + ((lstrlenW(decoded) + 1) * sizeof(WCHAR)); + } + else { + flags |= PREF_UTF; + utfResult = mir_utf8encodeT(decoded); + memRequired = (int)(strlen(utfResult)) + 1; + } + + // try to detect RTL + HWND hwndEdit = GetDlgItem(hwndDlg, IDC_MESSAGE); + SendMessage(hwndEdit, WM_SETREDRAW, FALSE, 0); + + PARAFORMAT2 pf2; + ZeroMemory(&pf2, sizeof(PARAFORMAT2)); + pf2.cbSize = sizeof(pf2); + pf2.dwMask = PFM_RTLPARA; + SendMessage(hwndEdit, EM_SETSEL, 0, -1); + SendMessage(hwndEdit, EM_GETPARAFORMAT, 0, (LPARAM)&pf2); + if (pf2.wEffects & PFE_RTLPARA) + if (SendQueue::RTL_Detect(decoded)) + flags |= PREF_RTL; + + SendMessage(hwndEdit, WM_SETREDRAW, TRUE, 0); + SendMessage(hwndEdit, EM_SETSEL, -1, -1); + InvalidateRect(hwndEdit, NULL, FALSE); + + if (memRequired > dat->iSendBufferSize) { + dat->sendBuffer = (char *)mir_realloc(dat->sendBuffer, memRequired); + dat->iSendBufferSize = memRequired; + } + if (utfResult) { + CopyMemory(dat->sendBuffer, utfResult, memRequired); + mir_free(utfResult); + } + else { + WideCharToMultiByte(dat->codePage, 0, decoded, -1, dat->sendBuffer, bufSize, 0, 0); + if (flags & PREF_UNICODE) + CopyMemory(&dat->sendBuffer[bufSize], decoded, (lstrlenW(decoded) + 1) * sizeof(WCHAR)); + } + + if (memRequired == 0 || dat->sendBuffer[0] == 0) + break; + + if (dat->sendMode & SMODE_CONTAINER && m_pContainer->hwndActive == hwndDlg && GetForegroundWindow() == hwndContainer) { + int tabCount = TabCtrl_GetItemCount(hwndTab); + ptrA szFromStream(Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, dat->SendFormat ? 0 : (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE))); + + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM; + for (int i=0; i < tabCount; i++) { + TabCtrl_GetItem(hwndTab, i, &tci); + // get the contact from the tabs lparam which hopefully is the tabs hwnd so we can get its userdata.... hopefully + HWND contacthwnd = (HWND)tci.lParam; + if (IsWindow(contacthwnd)) { + // if the contact hwnd is the current contact then ignore it and let the normal code deal with the msg + if (contacthwnd != hwndDlg) { + SETTEXTEX stx = {ST_DEFAULT, CP_UTF8}; + // send the buffer to the contacts msg typing area + SendDlgItemMessage(contacthwnd, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szFromStream); + SendMessage(contacthwnd, WM_COMMAND, IDOK, 0); + } + } + } + } + // END /all /MOD + if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) + DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); + + DeletePopupsForContact(dat->hContact, PU_REMOVE_ON_SEND); + if (M.GetByte("allow_sendhook", 0)) { + int result = TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CUSTOM, MAKELONG(flags, tabMSG_WINDOW_EVT_CUSTOM_BEFORESEND)); + if (result) + return TRUE; + } + sendQueue->addTo(dat, memRequired, flags); + } + return TRUE; + + case IDC_QUOTE: + { + int iCharsPerLine = M.GetDword("quoteLineLength", 64); + CHARRANGE sel; + SETTEXTEX stx = {ST_SELECTION, 1200}; + + HANDLE hDBEvent = 0; + if (dat->hwndIEView || dat->hwndHPP) { // IEView quoting support.. + TCHAR *selected = 0, *szQuoted = 0; + + IEVIEWEVENT event = { sizeof(event) }; + event.hContact = dat->hContact; + event.dwFlags = 0; + event.iType = IEE_GET_SELECTION; + + if (dat->hwndIEView) { + event.hwnd = dat->hwndIEView; + selected = (TCHAR*)CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event); + } + else { + event.hwnd = dat->hwndHPP; + selected = (TCHAR*)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); + } + + if (selected != NULL) { + szQuoted = QuoteText(selected, iCharsPerLine, 0); + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted); + if (szQuoted) + mir_free(szQuoted); + break; + } + else { + hDBEvent = db_event_last(dat->hContact); + goto quote_from_last; + } + } + hDBEvent = dat->hDbEventLast; + +quote_from_last: + if (hDBEvent == NULL) + break; + + SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) { + DBEVENTINFO dbei = { sizeof(dbei) }; + dbei.cbBlob = db_event_getBlobSize(hDBEvent); + TCHAR *szText = (TCHAR*)mir_alloc((dbei.cbBlob + 1) * sizeof(TCHAR)); //URLs are made one char bigger for crlf + dbei.pBlob = (BYTE*)szText; + db_event_get(hDBEvent, &dbei); + int iSize = int(strlen((char*)dbei.pBlob)) + 1; + + bool iAlloced = false; + TCHAR *szConverted; + if (dbei.flags & DBEF_UTF) { + szConverted = mir_utf8decodeW((char*)szText); + iAlloced = true; + } + else { + if (iSize != dbei.cbBlob) + szConverted = (TCHAR*)&dbei.pBlob[iSize]; + else { + szConverted = (TCHAR*)mir_alloc(sizeof(TCHAR) * iSize); + iAlloced = true; + MultiByteToWideChar(CP_ACP, 0, (char*)dbei.pBlob, -1, szConverted, iSize); + } + } + if (dbei.eventType == EVENTTYPE_FILE) { + int iDescr = lstrlenA((char *)(szText + sizeof(DWORD))); + MoveMemory(szText, szText + sizeof(DWORD), iDescr); + MoveMemory(szText + iDescr + 2, szText + sizeof(DWORD)+iDescr, dbei.cbBlob - iDescr - sizeof(DWORD)-1); + szText[iDescr] = '\r'; + szText[iDescr + 1] = '\n'; + szConverted = (TCHAR*)mir_alloc(sizeof(TCHAR)* (1 + lstrlenA((char *)szText))); + MultiByteToWideChar(CP_ACP, 0, (char *)szText, -1, szConverted, 1 + lstrlenA((char *)szText)); + iAlloced = true; + } + ptrT szQuoted(QuoteText(szConverted, iCharsPerLine, 0)); + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted); + mir_free(szText); + if (iAlloced) + mir_free(szConverted); + } + else { + ptrA szFromStream(Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, SF_TEXT | SF_USECODEPAGE | SFF_SELECTION)); + ptrW converted(mir_utf8decodeW(szFromStream)); + Utils::FilterEventMarkers(converted); + ptrT szQuoted(QuoteText(converted, iCharsPerLine, 0)); + SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted); + } + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + } + break; + + case IDC_ADD: + { + ADDCONTACTSTRUCT acs = { 0 }; + acs.hContact = dat->hContact; + acs.handleType = HANDLE_CONTACT; + acs.szProto = 0; + CallService(MS_ADDCONTACT_SHOW, (WPARAM)hwndDlg, (LPARAM)&acs); + if (!db_get_b(dat->hContact, "CList", "NotOnList", 0)) { + dat->bNotOnList = FALSE; + ShowMultipleControls(hwndDlg, addControls, 2, SW_HIDE); + if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)) + Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_HIDE); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + } + } + break; + + case IDC_CANCELADD: + dat->bNotOnList = FALSE; + ShowMultipleControls(hwndDlg, addControls, 2, SW_HIDE); + if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)) + Utils::showDlgControl(hwndDlg, IDC_LOGFROZENTEXT, SW_HIDE); + SendMessage(hwndDlg, WM_SIZE, 0, 0); + break; + + case IDC_MESSAGE: + if (HIWORD(wParam) == EN_CHANGE) { + if (m_pContainer->hwndActive == hwndDlg) + UpdateReadChars(dat); + dat->dwFlags |= MWF_NEEDHISTORYSAVE; + dat->dwLastActivity = GetTickCount(); + m_pContainer->dwLastActivity = dat->dwLastActivity; + SendQueue::UpdateSaveAndSendButton(dat); + if (!(GetKeyState(VK_CONTROL) & 0x8000)) { + dat->nLastTyping = GetTickCount(); + if (GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE))) { + if (dat->nTypeMode == PROTOTYPE_SELFTYPING_OFF) { + if (!(dat->dwFlags & MWF_INITMODE)) + DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_ON); + } + } + else if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) + DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); + } + } + break; + + default: + Utils::CmdDispatcher(Utils::CMD_MSGDIALOG, hwndDlg, LOWORD(wParam), wParam, lParam, dat, m_pContainer); + break; + } + break; + + case WM_CONTEXTMENU: + { + DWORD idFrom = GetDlgCtrlID((HWND)wParam); + if (idFrom >= MIN_CBUTTONID && idFrom <= MAX_CBUTTONID) { + BB_CustomButtonClick(dat, idFrom, (HWND)wParam, 1); + break; + } + } + break; + + // this is now *only* called from the global ME_PROTO_ACK handler (static int ProtoAck() in msgs.c) + // it receives: + // wParam = index of the sendjob in the queue in the low word, index of the found sendID in the high word + // (normally 0, but if its a multisend job, then the sendjob may contain more than one hContact/hSendId pairs.) + // lParam = the original ackdata + // + // the "per message window" ACK hook is gone, the global ack handler cares about all types of ack's (currently + // *_MESSAGE and *_AVATAR and dispatches them to the owner windows). + case HM_EVENTSENT: + sendQueue->ackMessage(dat, wParam, lParam); + return 0; + + case DM_ACTIVATEME: + ActivateExistingTab(m_pContainer, hwndDlg); + return 0; + + // sent by the select container dialog box when a container was selected... + // lParam = (TCHAR*)selected name... + case DM_CONTAINERSELECTED: + { + TCHAR *szNewName = (TCHAR*)lParam; + if (!_tcscmp(szNewName, TranslateT("Default container"))) + szNewName = CGlobals::m_default_container_name; + + int iOldItems = TabCtrl_GetItemCount(hwndTab); + if (!_tcsncmp(m_pContainer->szName, szNewName, CONTAINER_NAMELEN)) + break; + + TContainerData *pNewContainer = FindContainerByName(szNewName); + if (pNewContainer == NULL) + if ((pNewContainer = CreateContainer(szNewName, FALSE, dat->hContact)) == NULL) + break; + + db_set_ts(dat->hContact, SRMSGMOD_T, "containerW", szNewName); + dat->fIsReattach = TRUE; + PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, dat->hContact); + if (iOldItems > 1) // there were more than 1 tab, container is still valid + SendMessage(m_pContainer->hwndActive, WM_SIZE, 0, 0); + SetForegroundWindow(pNewContainer->hwnd); + SetActiveWindow(pNewContainer->hwnd); + } + break; + + case DM_STATUSBARCHANGED: + UpdateStatusBar(dat); + return 0; + + case DM_UINTOCLIPBOARD: + Utils::CopyToClipBoard(dat->cache->getUIN(), hwndDlg); + return 0; + + // broadcasted when GLOBAL info panel setting changes + case DM_SETINFOPANEL: + CInfoPanel::setPanelHandler(dat, wParam, lParam); + return 0; + + // show the balloon tooltip control. + // wParam == id of the "anchor" element, defaults to the panel status field (for away msg retrieval) + // lParam == new text to show + case DM_ACTIVATETOOLTIP: + if (IsIconic(hwndContainer) || m_pContainer->hwndActive != hwndDlg) + break; + + dat->Panel->showTip(wParam, lParam); + break; + + case WM_NEXTDLGCTL: + if (dat->dwFlags & MWF_WASBACKGROUNDCREATE) + return 1; + break; + + // save the contents of the log as rtf file + case DM_SAVEMESSAGELOG: + DM_SaveLogAsRTF(dat); + return 0; + + case DM_CHECKAUTOHIDE: + DM_CheckAutoHide(dat, wParam, lParam); + return 0; + + case DM_IEVIEWOPTIONSCHANGED: + if (dat->hwndIEView) + SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); + break; + + case DM_SMILEYOPTIONSCHANGED: + ConfigureSmileyButton(dat); + SendMessage(hwndDlg, DM_REMAKELOG, 0, 0); + break; + + case DM_MYAVATARCHANGED: + { + const char *szProto = dat->cache->getActiveProto(); + if (!strcmp((char *)wParam, szProto) && lstrlenA(szProto) == lstrlenA((char *)wParam)) + LoadOwnAvatar(dat); + } + break; + + case DM_GETWINDOWSTATE: + { + UINT state = MSG_WINDOW_STATE_EXISTS; + if (IsWindowVisible(hwndDlg)) + state |= MSG_WINDOW_STATE_VISIBLE; + if (GetForegroundWindow() == hwndContainer) + state |= MSG_WINDOW_STATE_FOCUS; + if (IsIconic(hwndContainer)) + state |= MSG_WINDOW_STATE_ICONIC; + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, state); + } + return TRUE; + + case DM_CLIENTCHANGED: + GetClientIcon(dat); + if (dat->hClientIcon && dat->Panel->isActive()) + InvalidateRect(hwndDlg, NULL, TRUE); + return 0; + + case DM_UPDATEUIN: + if (dat->Panel->isActive()) + dat->Panel->Invalidate(); + if (dat->pContainer->dwFlags & CNT_UINSTATUSBAR) + UpdateStatusBar(dat); + return 0; + + case DM_REMOVEPOPUPS: + DeletePopupsForContact(dat->hContact, (DWORD)wParam); + return 0; + + case EM_THEMECHANGED: + DM_FreeTheme(dat); + return DM_ThemeChanged(dat); + + case DM_PLAYINCOMINGSOUND: + if (!dat) + return 0; + PlayIncomingSound(dat); + return 0; + + case DM_REFRESHTABINDEX: + dat->iTabID = GetTabIndexFromHWND(GetParent(hwndDlg), hwndDlg); + return 0; + + case DM_STATUSICONCHANGE: + if (m_pContainer->hwndStatus) { + SendMessage(dat->pContainer->hwnd, WM_SIZE, 0, 0); + SendMessage(m_pContainer->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, 0); + InvalidateRect(m_pContainer->hwndStatus, NULL, TRUE); + } + return 0; + + case DM_BBNEEDUPDATE: + if (lParam) + CB_ChangeButton(hwndDlg, dat, (CustomButtonData*)lParam); + else + BB_InitDlgButtons(dat); + + BB_SetButtonsPos(dat); + return 0; + + case DM_CBDESTROY: + if (lParam) + CB_DestroyButton(hwndDlg, dat, (DWORD)wParam, (DWORD)lParam); + else + CB_DestroyAllButtons(hwndDlg, dat); + return 0; + + case WM_DROPFILES: + { + BOOL not_sending = GetKeyState(VK_CONTROL) & 0x8000; + if (!not_sending) { + const char *szProto = dat->cache->getActiveProto(); + if (szProto == NULL) + break; + + int pcaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0); + if (!(pcaps & PF1_FILESEND)) + break; + if (dat->wStatus == ID_STATUS_OFFLINE) { + pcaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0); + if (!(pcaps & PF4_OFFLINEFILES)) { + TCHAR szBuffer[256]; + + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("Contact is offline and this protocol does not support sending files to offline users.")); + SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer); + break; + } + } + } + + if (dat->hContact != NULL) { + TCHAR szFilename[MAX_PATH]; + HDROP hDrop = (HDROP)wParam; + int fileCount = DragQueryFile(hDrop, -1, NULL, 0), totalCount = 0, i; + TCHAR** ppFiles = NULL; + for (i = 0; i < fileCount; i++) { + DragQueryFile(hDrop, i, szFilename, SIZEOF(szFilename)); + Utils::AddToFileList(&ppFiles, &totalCount, szFilename); + } + + if (!not_sending) + CallService(MS_FILE_SENDSPECIFICFILEST, dat->hContact, (LPARAM)ppFiles); + else { + if (ServiceExists(MS_HTTPSERVER_ADDFILENAME)) { + for (int i = 0; i < totalCount; i++) { + char* szFileName = mir_t2a(ppFiles[i]); + char *szTemp = (char*)CallService(MS_HTTPSERVER_ADDFILENAME, (WPARAM)szFileName, 0); + mir_free(szFileName); + } + char *szHTTPText = "DEBUG"; + SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_REPLACESEL, TRUE, (LPARAM)szHTTPText); + SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + } + } + for (i = 0; ppFiles[i]; i++) + mir_free(ppFiles[i]); + mir_free(ppFiles); + } + } + return 0; + + case DM_CHECKQUEUEFORCLOSE: + { + int *uOpen = (int*)lParam; + if (uOpen) + *uOpen += dat->iOpenJobs; + } + return 0; + + case WM_CLOSE: + // esc handles error controls if we are in error state (error controls visible) + if (wParam == 0 && lParam == 0 && dat->dwFlags & MWF_ERRORSTATE) { + SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_CANCEL, 0); + return TRUE; + } + + if (wParam == 0 && lParam == 0) { + if (PluginConfig.m_EscapeCloses == 1) { + SendMessage(hwndContainer, WM_SYSCOMMAND, SC_MINIMIZE, 0); + return TRUE; + } + else if (PluginConfig.m_HideOnClose && PluginConfig.m_EscapeCloses == 2) { + ShowWindow(hwndContainer, SW_HIDE); + return TRUE; + } + _dlgReturn(hwndDlg, TRUE); + } + { + TContainerData *pContainer = dat->pContainer; + if (dat->iOpenJobs > 0 && lParam != 2) { + if (dat->dwFlags & MWF_ERRORSTATE) { + SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_CANCEL, 1); + } else { + if (dat->dwFlagsEx & MWF_EX_WARNCLOSE) + return TRUE; + + dat->dwFlagsEx |= MWF_EX_WARNCLOSE; + LRESULT result = SendQueue::WarnPendingJobs(0); + dat->dwFlagsEx &= ~MWF_EX_WARNCLOSE; + if (result == IDNO) + return TRUE; + } + } + int iTabs = TabCtrl_GetItemCount(hwndTab); + if (iTabs == 1) { + PostMessage(hwndContainer, WM_CLOSE, 0, 1); + return 1; + } + + m_pContainer->iChilds--; + int i = GetTabIndexFromHWND(hwndTab, hwndDlg); + + // 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 && lParam != 3) { + if (i == iTabs - 1) + i--; + else + i++; + TabCtrl_SetCurSel(hwndTab, i); + + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + TabCtrl_GetItem(hwndTab, i, &item); // retrieve dialog hwnd for the now active tab... + + m_pContainer->hwndActive = (HWND)item.lParam; + + RECT rc; + SendMessage(hwndContainer, 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(hwndContainer, WM_SIZE, 0, 0); + } + + DestroyWindow(hwndDlg); + if (iTabs == 1) + PostMessage(GetParent(GetParent(hwndDlg)), WM_CLOSE, 0, 1); + else + SendMessage(pContainer->hwnd, WM_SIZE, 0, 0); + } + break; + + case WM_DESTROY: + if (!dat) + break; + + if (dat->hwndContactPic) + DestroyWindow(dat->hwndContactPic); + + if (dat->hwndPanelPic) + DestroyWindow(dat->hwndPanelPic); + + if (dat->hClientIcon) + DestroyIcon(dat->hClientIcon); + + if (dat->hwndPanelPicParent) + DestroyWindow(dat->hwndPanelPicParent); + + if (dat->cache->isValid()) { // not valid means the contact was deleted + TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CLOSING, 0); + AddContactToFavorites(dat->hContact, dat->cache->getNick(), dat->cache->getActiveProto(), dat->szStatus, dat->wStatus, + LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getActiveStatus()), 1, PluginConfig.g_hMenuRecent); + if (dat->hContact) { + if (!dat->fEditNotesActive) { + char *msg = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE)); + if (msg) { + db_set_utf(dat->hContact, SRMSGMOD, "SavedMsg", msg); + mir_free(msg); + } + else db_unset(dat->hContact, SRMSGMOD, "SavedMsg"); + } + else SendMessage(hwndDlg, WM_COMMAND, IDC_PIC, 0); + } + } + + if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) + DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF); + + DM_FreeTheme(dat); + + mir_free(dat->sendBuffer); + mir_free(dat->hHistoryEvents); + + // search the sendqueue for unfinished send jobs and mir_free them. Leave unsent + // messages in the queue as they can be acked later + { + SendJob *jobs = sendQueue->getJobByIndex(0); + + for (int i = 0; i < SendQueue::NR_SENDJOBS; i++) { + if (jobs[i].hContact == dat->hContact) { + if (jobs[i].iStatus >(unsigned)SendQueue::SQ_INPROGRESS) + sendQueue->clearJob(i); + + // unfinished jobs which did not yet return anything are kept in the queue. + // the hwndOwner is set to 0 because the window handle is now no longer valid. + // Response for such a job is still silently handled by AckMessage() (sendqueue.c) + if (jobs[i].iStatus == (unsigned)SendQueue::SQ_INPROGRESS) + jobs[i].hOwnerWnd = 0; + } + } + } + + mir_free(dat->hQueuedEvents); + + if (dat->hSmileyIcon) + DestroyIcon(dat->hSmileyIcon); + + if (dat->hXStatusIcon) + DestroyIcon(dat->hXStatusIcon); + + if (dat->hwndTip) + DestroyWindow(dat->hwndTip); + + if (dat->hTaskbarIcon) + DestroyIcon(dat->hTaskbarIcon); + + UpdateTrayMenuState(dat, FALSE); // remove me from the tray menu (if still there) + if (PluginConfig.g_hMenuTrayUnread) + DeleteMenu(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)dat->hContact, MF_BYCOMMAND); + M.RemoveWindow(hwndDlg); + + if (dat->cache->isValid()) + db_set_dw(0, SRMSGMOD, "multisplit", dat->multiSplitterX); + + { + HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_GETFONT, 0, 0); + if (hFont != NULL && hFont != (HFONT)SendDlgItemMessage(hwndDlg, IDOK, WM_GETFONT, 0, 0)) + DeleteObject(hFont); + + TCITEM item = { 0 }; + item.mask = TCIF_PARAM; + + int i = GetTabIndexFromHWND(hwndTab, hwndDlg); + if (i >= 0) { + SendMessage(hwndTab, WM_USER + 100, 0, 0); // remove tooltip + TabCtrl_DeleteItem(hwndTab, i); + BroadCastContainer(m_pContainer, DM_REFRESHTABINDEX, 0, 0); + dat->iTabID = -1; + } + } + + TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_CLOSE, 0); + + // clean up IEView and H++ log windows + if (dat->hwndIEView != 0) { + IEVIEWWINDOW ieWindow; + ieWindow.cbSize = sizeof(IEVIEWWINDOW); + ieWindow.iType = IEW_DESTROY; + ieWindow.hwnd = dat->hwndIEView; + CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&ieWindow); + } + if (dat->hwndHPP) { + IEVIEWWINDOW ieWindow; + ieWindow.cbSize = sizeof(IEVIEWWINDOW); + ieWindow.iType = IEW_DESTROY; + ieWindow.hwnd = dat->hwndHPP; + CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); + } + if (dat->pWnd) { + delete dat->pWnd; + dat->pWnd = 0; + } + if (dat->sbCustom) { + delete dat->sbCustom; + dat->sbCustom = 0; + } + break; + + case WM_DWMCOMPOSITIONCHANGED: + BB_RefreshTheme(dat); + memset((void*)&dat->pContainer->mOld, -1000, sizeof(MARGINS)); + CProxyWindow::verify(dat); + break; + + case DM_FORCEREDRAW: + RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW); + return 0; + + case DM_CHECKINFOTIP: + dat->Panel->hideTip(reinterpret_cast(lParam)); + return 0; + + case WM_NCDESTROY: + if (dat) { + memset((void*)&dat->pContainer->mOld, -1000, sizeof(MARGINS)); + PostMessage(dat->pContainer->hwnd, WM_SIZE, 0, 1); + if (m_pContainer->dwFlags & CNT_SIDEBAR) + m_pContainer->SideBar->removeSession(dat); + dat->cache->setWindowData(); + if (dat->cache->isValid() && !dat->fIsReattach && dat->hContact && M.GetByte("deletetemp", 0)) + if (db_get_b(dat->hContact, "CList", "NotOnList", 0)) + CallService(MS_DB_CONTACT_DELETE, dat->hContact, 0); + + delete dat->Panel; + mir_free(dat); + } + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + break; + } + return FALSE; +} diff --git a/plugins/TabSRMM/src/sendqueue.cpp b/plugins/TabSRMM/src/sendqueue.cpp index 6d3d7cfb01..fd5b0f1342 100644 --- a/plugins/TabSRMM/src/sendqueue.cpp +++ b/plugins/TabSRMM/src/sendqueue.cpp @@ -1,890 +1,890 @@ -/* - * astyle --force-indent=tab=4 --brackets=linux --indent-switches - * --pad=oper --one-line=keep-blocks --unpad=paren - * - * Miranda NG: the free IM client for Microsoft* Windows* - * - * 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 - * - * Implements a queued, asynchronous sending system for tabSRMM. - * - */ - -#include "commonheaders.h" - -SendQueue *sendQueue = 0; - -/* - * searches the queue for a message belonging to the given contact which has been marked - * as "failed" by either the ACKRESULT_FAILED or a timeout handler - * returns: zero-based queue index or -1 if none was found - */ - -int SendQueue::findNextFailed(const TWindowData *dat) const -{ - if (dat) - for (int i=0; i < NR_SENDJOBS; i++) - if (m_jobs[i].hContact == dat->hContact && m_jobs[i].iStatus == SQ_ERROR) - return i; - - return -1; -} - -void SendQueue::handleError(TWindowData *dat, const int iEntry) const -{ - if (!dat) return; - - TCHAR szErrorMsg[500]; - - dat->iCurrentQueueError = iEntry; - mir_sntprintf(szErrorMsg, 500, _T("%s"), m_jobs[iEntry].szErrorMsg); - logError(dat, iEntry, szErrorMsg); - recallFailed(dat, iEntry); - showErrorControls(dat, TRUE); - ::HandleIconFeedback(dat, PluginConfig.g_iconErr); -} - -/* - * add a message to the sending queue. - * iLen = required size of the memory block to hold the message - */ - -int SendQueue::addTo(TWindowData *dat, const int iLen, int dwFlags) -{ - int i; - int iFound = NR_SENDJOBS; - - if (m_currentIndex >= NR_SENDJOBS) { - _DebugPopup(dat->hContact, _T("Send queue full")); - return 0; - } - /* - * find a mir_free entry in the send queue... - */ - for (i=0; i < NR_SENDJOBS; i++) { - if (m_jobs[i].hContact != 0 || m_jobs[i].iStatus != 0) { - // this entry is used, check if it's orphaned and can be removed... - if (m_jobs[i].hOwnerWnd && IsWindow(m_jobs[i].hOwnerWnd)) // window exists, do not reuse it - continue; - if (time(NULL) - m_jobs[i].dwTime < 120) // non-acked entry, but not old enough, don't re-use it - continue; - clearJob(i); - iFound = i; - goto entry_found; - } - iFound = i; - break; - } -entry_found: - if (iFound == NR_SENDJOBS) { - _DebugPopup(dat->hContact, _T("Send queue full")); - return 0; - } - - SendJob &job = m_jobs[iFound]; - job.szSendBuffer = (char*)mir_alloc(iLen); - CopyMemory(job.szSendBuffer, dat->sendBuffer, iLen); - - job.dwFlags = dwFlags; - job.dwTime = time(NULL); - - HWND hwndDlg = dat->hwnd; - - dat->cache->saveHistory(0, 0); - ::SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); - ::SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); - - UpdateSaveAndSendButton(dat); - sendQueued(dat, iFound); - return 0; -} - -/* - * threshold for word-wrapping when sending messages in chunks - */ - -#define SPLIT_WORD_CUTOFF 20 - -static int SendChunkW(WCHAR *chunk, MCONTACT hContact, DWORD dwFlags) -{ - int wLen = lstrlenW(chunk); - DWORD memRequired = (wLen + 1) * sizeof(WCHAR); - DWORD codePage = db_get_dw(hContact, SRMSGMOD_T, "ANSIcodepage", CP_ACP); - - int mbcsSize = WideCharToMultiByte(codePage, 0, chunk, -1, NULL, 0, 0, 0); - memRequired += mbcsSize; - - ptrA pBuf((char*)mir_alloc(memRequired)); - WideCharToMultiByte(codePage, 0, chunk, -1, pBuf, mbcsSize, 0, 0); - CopyMemory(&pBuf[mbcsSize], chunk, (wLen+1) * sizeof(WCHAR)); - return CallContactService(hContact, PSS_MESSAGE, dwFlags, (LPARAM)pBuf); -} - -static int SendChunkA(char *chunk, MCONTACT hContact, char *szSvc, DWORD dwFlags) -{ - return(CallContactService(hContact, szSvc, dwFlags, (LPARAM)chunk)); -} - -static void DoSplitSendW(LPVOID param) -{ - SendJob *job = sendQueue->getJobByIndex((int)param); - BOOL fFirstSend = FALSE; - WCHAR *wszSaved, savedChar; - int iCur = 0, iSavedCur = 0, i; - BOOL fSplitting = TRUE; - MCONTACT hContact = job->hContact; - DWORD dwFlags = job->dwFlags; - int chunkSize = job->chunkSize / 2; - char *szProto = GetContactProto(hContact); - - int iLen = lstrlenA(job->szSendBuffer); - WCHAR *wszBegin = (WCHAR*) & job->szSendBuffer[iLen + 1]; - WCHAR *wszTemp = (WCHAR*)mir_alloc(sizeof(WCHAR) * (lstrlenW(wszBegin) + 1)); - CopyMemory(wszTemp, wszBegin, sizeof(WCHAR) * (lstrlenW(wszBegin) + 1)); - wszBegin = wszTemp; - - do { - iCur += chunkSize; - if (iCur > iLen) - fSplitting = FALSE; - - /* - * try to "word wrap" the chunks - split on word boundaries (space characters), if possible. - * SPLIT_WORD_CUTOFF = max length of unbreakable words, longer words may be split. - */ - - if (fSplitting) { - i = 0; - wszSaved = &wszBegin[iCur]; - iSavedCur = iCur; - while (iCur) { - if (wszBegin[iCur] == (TCHAR)' ') { - wszSaved = &wszBegin[iCur]; - break; - } - if (i == SPLIT_WORD_CUTOFF) { // no space found backwards, restore old split position - iCur = iSavedCur; - wszSaved = &wszBegin[iCur]; - break; - } - i++; - iCur--; - } - savedChar = *wszSaved; - *wszSaved = 0; - int id = SendChunkW(wszTemp, hContact, dwFlags); - if (!fFirstSend) { - job->hSendId = (HANDLE)id; - fFirstSend = TRUE; - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); - } - *wszSaved = savedChar; - wszTemp = wszSaved; - if (savedChar == (TCHAR)' ') { - wszTemp++; - iCur++; - } - } - else { - int id = SendChunkW(wszTemp, hContact, dwFlags); - if (!fFirstSend) { - job->hSendId = (HANDLE)id; - fFirstSend = TRUE; - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); - } - } - Sleep(500L); - } - while (fSplitting); - mir_free(wszBegin); -} - -static void DoSplitSendA(LPVOID param) -{ - SendJob *job = sendQueue->getJobByIndex((int)param); - int id; - BOOL fFirstSend = FALSE; - char *szBegin, *szTemp, *szSaved, savedChar; - int iLen, iCur = 0, iSavedCur = 0, i; - BOOL fSplitting = TRUE; - MCONTACT hContact = job->hContact; - DWORD dwFlags = job->dwFlags; - int chunkSize = job->chunkSize; - - iLen = lstrlenA(job->szSendBuffer); - szTemp = (char *)mir_alloc(iLen + 1); - CopyMemory(szTemp, job->szSendBuffer, iLen + 1); - szBegin = szTemp; - - do { - iCur += chunkSize; - if (iCur > iLen) - fSplitting = FALSE; - - if (fSplitting) { - i = 0; - szSaved = &szBegin[iCur]; - iSavedCur = iCur; - while (iCur) { - if (szBegin[iCur] == ' ') { - szSaved = &szBegin[iCur]; - break; - } - if (i == SPLIT_WORD_CUTOFF) { - iCur = iSavedCur; - szSaved = &szBegin[iCur]; - break; - } - i++; - iCur--; - } - savedChar = *szSaved; - *szSaved = 0; - id = SendChunkA(szTemp, hContact, PSS_MESSAGE, dwFlags); - if (!fFirstSend) { - job->hSendId = (HANDLE)id; - fFirstSend = TRUE; - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); - } - *szSaved = savedChar; - szTemp = szSaved; - if (savedChar == ' ') { - szTemp++; - iCur++; - } - } - else { - id = SendChunkA(szTemp, hContact, PSS_MESSAGE, dwFlags); - if (!fFirstSend) { - job->hSendId = (HANDLE)id; - fFirstSend = TRUE; - PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); - } - } - Sleep(500L); - } - while (fSplitting); - mir_free(szBegin); -} - -/** - * return effective length of the message in bytes (utf-8 encoded) - */ -int SendQueue::getSendLength(const int iEntry, int sendMode) -{ - if (m_jobs[iEntry].dwFlags & PREF_UNICODE && !(sendMode & SMODE_FORCEANSI)) { - int iLen = lstrlenA(m_jobs[iEntry].szSendBuffer); - WCHAR *wszBuf = (WCHAR *) & m_jobs[iEntry].szSendBuffer[iLen + 1]; - char *utf8 = mir_utf8encodeT(wszBuf); - m_jobs[iEntry].iSendLength = lstrlenA(utf8); - mir_free(utf8); - return(m_jobs[iEntry].iSendLength); - } - else { - m_jobs[iEntry].iSendLength = lstrlenA(m_jobs[iEntry].szSendBuffer); - return(m_jobs[iEntry].iSendLength); - } -} - -int SendQueue::sendQueued(TWindowData *dat, const int iEntry) -{ - CContactCache *ccActive = CContactCache::getContactCache(dat->hContact); - if (ccActive == NULL) - return 0; - - HWND hwndDlg = dat->hwnd; - - if (dat->sendMode & SMODE_MULTIPLE) { - int iJobs = 0; - int iMinLength = 0; - - m_jobs[iEntry].iStatus = SQ_INPROGRESS; - m_jobs[iEntry].hContact = ccActive->getActiveContact(); - m_jobs[iEntry].hOwnerWnd = hwndDlg; - - int iSendLength = getSendLength(iEntry, dat->sendMode); - - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, hContact, 0); - if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { - CContactCache *c = CContactCache::getContactCache(hContact); - if (c) - iMinLength = (iMinLength == 0 ? c->getMaxMessageLength() : min(c->getMaxMessageLength(), iMinLength)); - } - } - - if (iSendLength >= iMinLength) { - TCHAR tszError[256]; - mir_sntprintf(tszError, 256, TranslateT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), iMinLength); - ::SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, LPARAM(tszError)); - sendQueue->clearJob(iEntry); - return 0; - } - - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, hContact, 0); - if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { - doSendLater(iEntry, 0, hContact, false); - iJobs++; - } - } - - sendQueue->clearJob(iEntry); - if (iJobs) - sendLater->flushQueue(); // force queue processing - return 0; - } - - if (dat->hContact == NULL) - return 0; //never happens - - dat->nMax = dat->cache->getMaxMessageLength(); // refresh length info - - if (dat->sendMode & SMODE_FORCEANSI && db_get_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 1)) - db_set_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 0); - else if (!(dat->sendMode & SMODE_FORCEANSI) && !db_get_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 0)) - db_set_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 1); - - if (M.GetByte("autosplit", 0) && !(dat->sendMode & SMODE_SENDLATER)) { - // determine send buffer length - BOOL fSplit = FALSE; - if (getSendLength(iEntry, dat->sendMode) >= dat->nMax) - fSplit = true; - - if (!fSplit) - goto send_unsplitted; - - m_jobs[iEntry].hContact = ccActive->getActiveContact(); - m_jobs[iEntry].hOwnerWnd = hwndDlg; - m_jobs[iEntry].iStatus = SQ_INPROGRESS; - m_jobs[iEntry].iAcksNeeded = 1; - m_jobs[iEntry].chunkSize = dat->nMax; - - DWORD dwOldFlags = m_jobs[iEntry].dwFlags; - if (dat->sendMode & SMODE_FORCEANSI) - m_jobs[iEntry].dwFlags &= ~PREF_UNICODE; - - if (!(m_jobs[iEntry].dwFlags & PREF_UNICODE) || dat->sendMode & SMODE_FORCEANSI) - mir_forkthread(DoSplitSendA, (LPVOID)iEntry); - else - mir_forkthread(DoSplitSendW, (LPVOID)iEntry); - m_jobs[iEntry].dwFlags = dwOldFlags; - } - else { - -send_unsplitted: - - m_jobs[iEntry].hContact = ccActive->getActiveContact(); - m_jobs[iEntry].hOwnerWnd = hwndDlg; - m_jobs[iEntry].iStatus = SQ_INPROGRESS; - m_jobs[iEntry].iAcksNeeded = 1; - if (dat->sendMode & SMODE_SENDLATER) { - TCHAR tszError[256]; - - int iSendLength = getSendLength(iEntry, dat->sendMode); - if (iSendLength >= dat->nMax) { - mir_sntprintf(tszError, 256, TranslateT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), dat->nMax); - SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, LPARAM(tszError)); - clearJob(iEntry); - return 0; - } - doSendLater(iEntry, dat); - clearJob(iEntry); - return 0; - } - m_jobs[iEntry].hSendId = (HANDLE)CallContactService(dat->hContact, PSS_MESSAGE, - (dat->sendMode & SMODE_FORCEANSI) ? (m_jobs[iEntry].dwFlags & ~PREF_UNICODE) : m_jobs[iEntry].dwFlags, (LPARAM)m_jobs[iEntry].szSendBuffer); - - if (dat->sendMode & SMODE_NOACK) { // fake the ack if we are not interested in receiving real acks - ACKDATA ack = {0}; - ack.hContact = dat->hContact; - ack.hProcess = m_jobs[iEntry].hSendId; - ack.type = ACKTYPE_MESSAGE; - ack.result = ACKRESULT_SUCCESS; - SendMessage(hwndDlg, HM_EVENTSENT, (WPARAM)MAKELONG(iEntry, 0), (LPARAM)&ack); - } - else SetTimer(hwndDlg, TIMERID_MSGSEND + iEntry, PluginConfig.m_MsgTimeout, NULL); - } - - dat->iOpenJobs++; - m_currentIndex++; - - // give icon feedback... - - if (dat->pContainer->hwndActive == hwndDlg) - ::UpdateReadChars(dat); - - if (!(dat->sendMode & SMODE_NOACK)) - ::HandleIconFeedback(dat, PluginConfig.g_IconSend); - - if (M.GetByte(SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN)) - ::SendMessage(dat->pContainer->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); - return 0; -} - -void SendQueue::clearJob(const int iIndex) -{ - SendJob &job = m_jobs[iIndex]; - mir_free(job.szSendBuffer); - memset(&job, 0, sizeof(SendJob)); -} - -/* - * this is called when: - * - * ) a delivery has completed successfully - * ) user decided to cancel a failed send - * it removes the completed / canceled send job from the queue and schedules the next job to send (if any) - */ - -void SendQueue::checkQueue(const TWindowData *dat) const -{ - if (dat) { - HWND hwndDlg = dat->hwnd; - - if (dat->iOpenJobs == 0) - ::HandleIconFeedback(const_cast(dat), (HICON)INVALID_HANDLE_VALUE); - else if (!(dat->sendMode & SMODE_NOACK)) - ::HandleIconFeedback(const_cast(dat), PluginConfig.g_IconSend); - - if (dat->pContainer->hwndActive == hwndDlg) - ::UpdateReadChars(const_cast(dat)); - } -} - -/* - * logs an error message to the message window. Optionally, appends the original message - * from the given sendJob (queue index) - */ - -void SendQueue::logError(const TWindowData *dat, int iSendJobIndex, const TCHAR *szErrMsg) const -{ - if (dat == 0) - return; - - int iMsgLen; - DBEVENTINFO dbei = { sizeof(dbei) }; - dbei.eventType = EVENTTYPE_ERRMSG; - if (iSendJobIndex >= 0) { - dbei.pBlob = (BYTE *)m_jobs[iSendJobIndex].szSendBuffer; - iMsgLen = lstrlenA(m_jobs[iSendJobIndex].szSendBuffer) + 1; - } - else { - iMsgLen = 0; - dbei.pBlob = NULL; - } - if (m_jobs[iSendJobIndex].dwFlags & PREF_UTF) - dbei.flags = DBEF_UTF; - if (iSendJobIndex >= 0) { - if (m_jobs[iSendJobIndex].dwFlags & PREF_UNICODE) { - iMsgLen *= 3; - } - } - dbei.cbBlob = iMsgLen; - dbei.timestamp = time(NULL); - dbei.szModule = (char *)szErrMsg; - StreamInEvents(dat->hwnd, NULL, 1, 1, &dbei); -} - -/* - * enable or disable the sending controls in the given window - * ) input area - * ) multisend contact list instance - * ) send button - */ - -void SendQueue::EnableSending(const TWindowData *dat, const int iMode) -{ - if (dat) { - HWND hwndDlg = dat->hwnd; - ::SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, (WPARAM)iMode ? FALSE : TRUE, 0); - Utils::enableDlgControl(hwndDlg, IDC_CLIST, iMode ? TRUE : FALSE); - ::EnableSendButton(dat, iMode); - } -} - -/* - * show or hide the error control button bar on top of the window - */ - -void SendQueue::showErrorControls(TWindowData *dat, const int showCmd) const -{ - UINT myerrorControls[] = { IDC_STATICERRORICON, IDC_STATICTEXT, IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER}; - int i; - HWND hwndDlg = dat->hwnd; - - if (showCmd) { - TCITEM item = {0}; - dat->hTabIcon = PluginConfig.g_iconErr; - item.mask = TCIF_IMAGE; - item.iImage = 0; - TabCtrl_SetItem(GetDlgItem(dat->pContainer->hwnd, IDC_MSGTABS), dat->iTabID, &item); - dat->dwFlags |= MWF_ERRORSTATE; - } - else { - dat->dwFlags &= ~MWF_ERRORSTATE; - dat->hTabIcon = dat->hTabStatusIcon; - } - - for (i=0; i < 5; i++) { - if (IsWindow(GetDlgItem(hwndDlg, myerrorControls[i]))) - Utils::showDlgControl(hwndDlg, myerrorControls[i], showCmd ? SW_SHOW : SW_HIDE); - } - - SendMessage(hwndDlg, WM_SIZE, 0, 0); - DM_ScrollToBottom(dat, 0, 1); - if (m_jobs[0].hContact != 0) - EnableSending(dat, TRUE); -} - -void SendQueue::recallFailed(const TWindowData *dat, int iEntry) const -{ - int iLen = GetWindowTextLengthA(GetDlgItem(dat->hwnd, IDC_MESSAGE)); - - if (dat) { - NotifyDeliveryFailure(dat); - if (iLen == 0) { // message area is empty, so we can recall the failed message... - SETTEXTEX stx = {ST_DEFAULT, 1200}; - if (m_jobs[iEntry].dwFlags & PREF_UNICODE) - SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)&m_jobs[iEntry].szSendBuffer[lstrlenA(m_jobs[iEntry].szSendBuffer) + 1]); - else { - stx.codepage = (m_jobs[iEntry].dwFlags & PREF_UTF) ? CP_UTF8 : CP_ACP; - SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_jobs[iEntry].szSendBuffer); - } - UpdateSaveAndSendButton(const_cast(dat)); - SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETSEL, (WPARAM)- 1, (LPARAM)- 1); - } - } -} - -void SendQueue::UpdateSaveAndSendButton(TWindowData *dat) -{ - if (dat) { - HWND hwndDlg = dat->hwnd; - - GETTEXTLENGTHEX gtxl = {0}; - gtxl.codepage = CP_UTF8; - gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES; - - int len = SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); - if (len && GetSendButtonState(hwndDlg) == PBS_DISABLED) - EnableSendButton(dat, TRUE); - else if (len == 0 && GetSendButtonState(hwndDlg) != PBS_DISABLED) - EnableSendButton(dat, FALSE); - - if (len) { // looks complex but avoids flickering on the button while typing. - if (!(dat->dwFlags & MWF_SAVEBTN_SAV)) { - SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]); - SendDlgItemMessage(hwndDlg, IDC_SAVE, BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_save, BATF_TCHAR); - dat->dwFlags |= MWF_SAVEBTN_SAV; - } - } - else { - SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]); - SendDlgItemMessage(hwndDlg, IDC_SAVE, BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, BATF_TCHAR); - dat->dwFlags &= ~MWF_SAVEBTN_SAV; - } - dat->textLen = len; - } -} - -void SendQueue::NotifyDeliveryFailure(const TWindowData *dat) -{ - if (M.GetByte("adv_noErrorPopups", 0)) - return; - - if ( CallService(MS_POPUP_QUERY, PUQS_GETSTATUS, 0) != 1) - return; - - POPUPDATAT ppd = { 0 }; - ppd.lchContact = dat->hContact; - lstrcpyn(ppd.lptzContactName, dat->cache->getNick(), MAX_CONTACTNAME); - lstrcpyn(ppd.lptzText, TranslateT("A message delivery has failed.\nClick to open the message window."), MAX_SECONDLINE); - - if (!(BOOL)M.GetByte(MODULE, OPT_COLDEFAULT_ERR, TRUE)) { - ppd.colorText = (COLORREF)M.GetDword(MODULE, OPT_COLTEXT_ERR, DEFAULT_COLTEXT); - ppd.colorBack = (COLORREF)M.GetDword(MODULE, OPT_COLBACK_ERR, DEFAULT_COLBACK); - } - else ppd.colorText = ppd.colorBack = 0; - - ppd.PluginWindowProc = reinterpret_cast(Utils::PopupDlgProcError); - ppd.lchIcon = PluginConfig.g_iconErr; - ppd.PluginData = (void*)dat->hContact; - ppd.iSeconds = (int)M.GetDword(MODULE, OPT_DELAY_ERR, (DWORD)DEFAULT_DELAY); - PUAddPopupT(&ppd); -} - -/* - * searches string for characters typical for RTL text (hebrew and other RTL languages -*/ - -int SendQueue::RTL_Detect(const WCHAR *pszwText) -{ - int i, n = 0; - int iLen = lstrlenW(pszwText); - - WORD *infoTypeC2 = (WORD *)mir_alloc(sizeof(WORD) * (iLen + 2)); - if (infoTypeC2) { - ZeroMemory(infoTypeC2, sizeof(WORD) * (iLen + 2)); - - GetStringTypeW(CT_CTYPE2, pszwText, iLen, infoTypeC2); - - for (i=0; i < iLen; i++) - if (infoTypeC2[i] == C2_RIGHTTOLEFT) - n++; - - mir_free(infoTypeC2); - return(n >= 2 ? 1 : 0); - } - return 0; -} - -int SendQueue::ackMessage(TWindowData *dat, WPARAM wParam, LPARAM lParam) -{ - ACKDATA *ack = (ACKDATA *) lParam; - - TContainerData *m_pContainer = 0; - if (dat) - m_pContainer = dat->pContainer; - - int iFound = (int)(LOWORD(wParam)); - SendJob &job = m_jobs[iFound]; - - if (job.iStatus == SQ_ERROR) { // received ack for a job which is already in error state... - if (dat) { // window still open - if (dat->iCurrentQueueError == iFound) { - dat->iCurrentQueueError = -1; - showErrorControls(dat, FALSE); - } - } - // we must discard this job, because there is no message window open to handle the - // error properly. But we display a tray notification to inform the user about the problem. - else goto inform_and_discard; - } - - // failed acks are only handled when the window is still open. with no window open, they will be *silently* discarded - - if (ack->result == ACKRESULT_FAILED) { - if (dat) { - // "hard" errors are handled differently in multisend. There is no option to retry - once failed, they - // are discarded and the user is notified with a small log message. - if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) - SkinPlaySound("SendError"); - - TCHAR *szAckMsg = mir_a2t((char *)ack->lParam); - mir_sntprintf(job.szErrorMsg, SIZEOF(job.szErrorMsg), TranslateT("Delivery failure: %s"), szAckMsg); - job.iStatus = SQ_ERROR; - mir_free(szAckMsg); - KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound); - if (!(dat->dwFlags & MWF_ERRORSTATE)) - handleError(dat, iFound); - return 0; - } - -inform_and_discard: - _DebugPopup(job.hContact, TranslateT("A message delivery has failed after the contacts chat window was closed. You may want to resend the last message")); - clearJob(iFound); - return 0; - } - - DBEVENTINFO dbei = { sizeof(dbei) }; - dbei.eventType = EVENTTYPE_MESSAGE; - dbei.flags = DBEF_SENT; - dbei.szModule = GetContactProto(job.hContact); - dbei.timestamp = time(NULL); - dbei.cbBlob = lstrlenA(job.szSendBuffer) + 1; - - if (dat) - dat->cache->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1); - else { - CContactCache *cc = CContactCache::getContactCache(job.hContact); - if (cc) - cc->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1); - } - - if (job.dwFlags & PREF_UNICODE) - dbei.cbBlob *= sizeof(TCHAR) + 1; - if (job.dwFlags & PREF_RTL) - dbei.flags |= DBEF_RTL; - if (job.dwFlags & PREF_UTF) - dbei.flags |= DBEF_UTF; - dbei.pBlob = (PBYTE)job.szSendBuffer; - - MessageWindowEvent evt = { sizeof(evt), (int)job.hSendId, job.hContact, &dbei }; - NotifyEventHooks(PluginConfig.m_event_WriteEvent, 0, (LPARAM)&evt); - - job.szSendBuffer = (char*)dbei.pBlob; - HANDLE hNewEvent = db_event_add(job.hContact, &dbei); - - if (m_pContainer) - if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) - SkinPlaySound("SendMsg"); - - M.BroadcastMessage(DM_APPENDMCEVENT, job.hContact, LPARAM(hNewEvent)); - - job.hSendId = NULL; - job.iAcksNeeded--; - - if (job.iAcksNeeded == 0) { // everything sent - //if (job.hOwner != 0 && dat) - // EnableSending(dat, TRUE); - clearJob(iFound); - if (dat) { - KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound); - dat->iOpenJobs--; - } - m_currentIndex--; - } - if (dat) { - checkQueue(dat); - - int iNextFailed = findNextFailed(dat); - if (iNextFailed >= 0 && !(dat->dwFlags & MWF_ERRORSTATE)) - handleError(dat, iNextFailed); - else { - if (M.GetByte("AutoClose", 0)) { - if (M.GetByte("adv_AutoClose_2", 0)) - SendMessage(dat->hwnd, WM_CLOSE, 0, 1); - else - SendMessage(dat->pContainer->hwnd, WM_CLOSE, 0, 0); - } - } - } - return 0; -} - -LRESULT SendQueue::WarnPendingJobs(unsigned int uNrMessages) -{ - return MessageBox(0, - TranslateT("There are unsent messages waiting for confirmation.\nIf you close the window now, Miranda will try to send them but may be unable to inform you about possible delivery errors.\nDo you really want to close the window(s)?"), - TranslateT("Message window warning"), MB_YESNO | MB_ICONHAND); -} - -/** - * This just adds the message to the database for later delivery and - * adds the contact to the list of contacts that have queued messages - * - * @param iJobIndex int: index of the send job - * dat: Message window data - * fAddHeader: add the "message was sent delayed" header (default = true) - * hContact : contact to which the job should be added (default = hOwner of the send job) - * - * @return the index on success, -1 on failure - */ -int SendQueue::doSendLater(int iJobIndex, TWindowData *dat, MCONTACT hContact, bool fIsSendLater) -{ - bool fAvail = sendLater->isAvail(); - - const TCHAR *szNote = 0; - - if (fIsSendLater && dat) { - if (fAvail) - szNote = TranslateT("Message successfully queued for later delivery.\nIt will be sent as soon as possible and a popup will inform you about the result."); - else - szNote = TranslateT("The send later feature is not available on this protocol."); - - char *utfText = mir_utf8encodeT(szNote); - DBEVENTINFO dbei; - dbei.cbSize = sizeof(dbei); - dbei.eventType = EVENTTYPE_MESSAGE; - dbei.flags = DBEF_SENT | DBEF_UTF; - dbei.szModule = GetContactProto(dat->hContact); - dbei.timestamp = time(NULL); - dbei.cbBlob = lstrlenA(utfText) + 1; - dbei.pBlob = (PBYTE) utfText; - StreamInEvents(dat->hwnd, 0, 1, 1, &dbei); - if (dat->hDbEventFirst == NULL) - SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0); - dat->cache->saveHistory(0, 0); - EnableSendButton(dat, FALSE); - if (dat->pContainer->hwndActive == dat->hwnd) - UpdateReadChars(dat); - SendDlgItemMessage(dat->hwnd, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]); - SendDlgItemMessage(dat->hwnd, IDC_SAVE, BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, BATF_TCHAR); - dat->dwFlags &= ~MWF_SAVEBTN_SAV; - mir_free(utfText); - - if (!fAvail) - return 0; - } - - if (iJobIndex >= 0 && iJobIndex < NR_SENDJOBS) { - SendJob* job = &m_jobs[iJobIndex]; - char szKeyName[20]; - TCHAR tszHeader[150]; - - if (fIsSendLater) { - time_t now = time(0); - TCHAR tszTimestamp[30]; - _tcsftime(tszTimestamp, SIZEOF(tszTimestamp), _T("%Y.%m.%d - %H:%M"), _localtime32((__time32_t *)&now)); - tszTimestamp[29] = 0; - mir_snprintf(szKeyName, 20, "S%d", now); - mir_sntprintf(tszHeader, SIZEOF(tszHeader), TranslateT("\n(Sent delayed. Original timestamp %s)"), tszTimestamp); - } - else mir_sntprintf(tszHeader, SIZEOF(tszHeader), _T("M%d|"), time(0)); - - if (job->dwFlags & PREF_UTF || !(job->dwFlags & PREF_UNICODE)) { - char *utf_header = mir_utf8encodeT(tszHeader); - UINT required = lstrlenA(utf_header) + lstrlenA(job->szSendBuffer) + 10; - char *tszMsg = reinterpret_cast(mir_alloc(required)); - - if (fIsSendLater) { - mir_snprintf(tszMsg, required, "%s%s", job->szSendBuffer, utf_header); - db_set_s(hContact ? hContact : job->hContact, "SendLater", szKeyName, tszMsg); - } - else { - mir_snprintf(tszMsg, required, "%s%s", utf_header, job->szSendBuffer); - sendLater->addJob(tszMsg, hContact); - } - mir_free(utf_header); - mir_free(tszMsg); - } - else if (job->dwFlags & PREF_UNICODE) { - int iLen = lstrlenA(job->szSendBuffer); - wchar_t *wszMsg = (wchar_t *)&job->szSendBuffer[iLen + 1]; - - UINT required = sizeof(TCHAR) * (lstrlen(tszHeader) + lstrlenW(wszMsg) + 10); - - TCHAR *tszMsg = reinterpret_cast(mir_alloc(required)); - if (fIsSendLater) - mir_sntprintf(tszMsg, required, _T("%s%s"), wszMsg, tszHeader); - else - mir_sntprintf(tszMsg, required, _T("%s%s"), tszHeader, wszMsg); - char *utf = mir_utf8encodeT(tszMsg); - if (fIsSendLater) - db_set_s(hContact ? hContact : job->hContact, "SendLater", szKeyName, utf); - else - sendLater->addJob(utf, hContact); - mir_free(utf); - mir_free(tszMsg); - } - if (fIsSendLater) { - int iCount = db_get_dw(hContact ? hContact : job->hContact, "SendLater", "count", 0); - iCount++; - db_set_dw(hContact ? hContact : job->hContact, "SendLater", "count", iCount); - sendLater->addContact(hContact ? hContact : job->hContact); - } - return iJobIndex; - } - return -1; -} +/* + * astyle --force-indent=tab=4 --brackets=linux --indent-switches + * --pad=oper --one-line=keep-blocks --unpad=paren + * + * Miranda NG: the free IM client for Microsoft* Windows* + * + * 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 + * + * Implements a queued, asynchronous sending system for tabSRMM. + * + */ + +#include "commonheaders.h" + +SendQueue *sendQueue = 0; + +/* + * searches the queue for a message belonging to the given contact which has been marked + * as "failed" by either the ACKRESULT_FAILED or a timeout handler + * returns: zero-based queue index or -1 if none was found + */ + +int SendQueue::findNextFailed(const TWindowData *dat) const +{ + if (dat) + for (int i=0; i < NR_SENDJOBS; i++) + if (m_jobs[i].hContact == dat->hContact && m_jobs[i].iStatus == SQ_ERROR) + return i; + + return -1; +} + +void SendQueue::handleError(TWindowData *dat, const int iEntry) const +{ + if (!dat) return; + + TCHAR szErrorMsg[500]; + + dat->iCurrentQueueError = iEntry; + mir_sntprintf(szErrorMsg, 500, _T("%s"), m_jobs[iEntry].szErrorMsg); + logError(dat, iEntry, szErrorMsg); + recallFailed(dat, iEntry); + showErrorControls(dat, TRUE); + ::HandleIconFeedback(dat, PluginConfig.g_iconErr); +} + +/* + * add a message to the sending queue. + * iLen = required size of the memory block to hold the message + */ + +int SendQueue::addTo(TWindowData *dat, const int iLen, int dwFlags) +{ + int i; + int iFound = NR_SENDJOBS; + + if (m_currentIndex >= NR_SENDJOBS) { + _DebugPopup(dat->hContact, _T("Send queue full")); + return 0; + } + /* + * find a mir_free entry in the send queue... + */ + for (i=0; i < NR_SENDJOBS; i++) { + if (m_jobs[i].hContact != 0 || m_jobs[i].iStatus != 0) { + // this entry is used, check if it's orphaned and can be removed... + if (m_jobs[i].hOwnerWnd && IsWindow(m_jobs[i].hOwnerWnd)) // window exists, do not reuse it + continue; + if (time(NULL) - m_jobs[i].dwTime < 120) // non-acked entry, but not old enough, don't re-use it + continue; + clearJob(i); + iFound = i; + goto entry_found; + } + iFound = i; + break; + } +entry_found: + if (iFound == NR_SENDJOBS) { + _DebugPopup(dat->hContact, _T("Send queue full")); + return 0; + } + + SendJob &job = m_jobs[iFound]; + job.szSendBuffer = (char*)mir_alloc(iLen); + CopyMemory(job.szSendBuffer, dat->sendBuffer, iLen); + + job.dwFlags = dwFlags; + job.dwTime = time(NULL); + + HWND hwndDlg = dat->hwnd; + + dat->cache->saveHistory(0, 0); + ::SetDlgItemText(hwndDlg, IDC_MESSAGE, _T("")); + ::SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE)); + + UpdateSaveAndSendButton(dat); + sendQueued(dat, iFound); + return 0; +} + +/* + * threshold for word-wrapping when sending messages in chunks + */ + +#define SPLIT_WORD_CUTOFF 20 + +static int SendChunkW(WCHAR *chunk, MCONTACT hContact, DWORD dwFlags) +{ + int wLen = lstrlenW(chunk); + DWORD memRequired = (wLen + 1) * sizeof(WCHAR); + DWORD codePage = db_get_dw(hContact, SRMSGMOD_T, "ANSIcodepage", CP_ACP); + + int mbcsSize = WideCharToMultiByte(codePage, 0, chunk, -1, NULL, 0, 0, 0); + memRequired += mbcsSize; + + ptrA pBuf((char*)mir_alloc(memRequired)); + WideCharToMultiByte(codePage, 0, chunk, -1, pBuf, mbcsSize, 0, 0); + CopyMemory(&pBuf[mbcsSize], chunk, (wLen+1) * sizeof(WCHAR)); + return CallContactService(hContact, PSS_MESSAGE, dwFlags, (LPARAM)pBuf); +} + +static int SendChunkA(char *chunk, MCONTACT hContact, char *szSvc, DWORD dwFlags) +{ + return(CallContactService(hContact, szSvc, dwFlags, (LPARAM)chunk)); +} + +static void DoSplitSendW(LPVOID param) +{ + SendJob *job = sendQueue->getJobByIndex((int)param); + BOOL fFirstSend = FALSE; + WCHAR *wszSaved, savedChar; + int iCur = 0, iSavedCur = 0, i; + BOOL fSplitting = TRUE; + MCONTACT hContact = job->hContact; + DWORD dwFlags = job->dwFlags; + int chunkSize = job->chunkSize / 2; + char *szProto = GetContactProto(hContact); + + int iLen = lstrlenA(job->szSendBuffer); + WCHAR *wszBegin = (WCHAR*) & job->szSendBuffer[iLen + 1]; + WCHAR *wszTemp = (WCHAR*)mir_alloc(sizeof(WCHAR) * (lstrlenW(wszBegin) + 1)); + CopyMemory(wszTemp, wszBegin, sizeof(WCHAR) * (lstrlenW(wszBegin) + 1)); + wszBegin = wszTemp; + + do { + iCur += chunkSize; + if (iCur > iLen) + fSplitting = FALSE; + + /* + * try to "word wrap" the chunks - split on word boundaries (space characters), if possible. + * SPLIT_WORD_CUTOFF = max length of unbreakable words, longer words may be split. + */ + + if (fSplitting) { + i = 0; + wszSaved = &wszBegin[iCur]; + iSavedCur = iCur; + while (iCur) { + if (wszBegin[iCur] == (TCHAR)' ') { + wszSaved = &wszBegin[iCur]; + break; + } + if (i == SPLIT_WORD_CUTOFF) { // no space found backwards, restore old split position + iCur = iSavedCur; + wszSaved = &wszBegin[iCur]; + break; + } + i++; + iCur--; + } + savedChar = *wszSaved; + *wszSaved = 0; + int id = SendChunkW(wszTemp, hContact, dwFlags); + if (!fFirstSend) { + job->hSendId = (HANDLE)id; + fFirstSend = TRUE; + PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); + } + *wszSaved = savedChar; + wszTemp = wszSaved; + if (savedChar == (TCHAR)' ') { + wszTemp++; + iCur++; + } + } + else { + int id = SendChunkW(wszTemp, hContact, dwFlags); + if (!fFirstSend) { + job->hSendId = (HANDLE)id; + fFirstSend = TRUE; + PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); + } + } + Sleep(500L); + } + while (fSplitting); + mir_free(wszBegin); +} + +static void DoSplitSendA(LPVOID param) +{ + SendJob *job = sendQueue->getJobByIndex((int)param); + int id; + BOOL fFirstSend = FALSE; + char *szBegin, *szTemp, *szSaved, savedChar; + int iLen, iCur = 0, iSavedCur = 0, i; + BOOL fSplitting = TRUE; + MCONTACT hContact = job->hContact; + DWORD dwFlags = job->dwFlags; + int chunkSize = job->chunkSize; + + iLen = lstrlenA(job->szSendBuffer); + szTemp = (char *)mir_alloc(iLen + 1); + CopyMemory(szTemp, job->szSendBuffer, iLen + 1); + szBegin = szTemp; + + do { + iCur += chunkSize; + if (iCur > iLen) + fSplitting = FALSE; + + if (fSplitting) { + i = 0; + szSaved = &szBegin[iCur]; + iSavedCur = iCur; + while (iCur) { + if (szBegin[iCur] == ' ') { + szSaved = &szBegin[iCur]; + break; + } + if (i == SPLIT_WORD_CUTOFF) { + iCur = iSavedCur; + szSaved = &szBegin[iCur]; + break; + } + i++; + iCur--; + } + savedChar = *szSaved; + *szSaved = 0; + id = SendChunkA(szTemp, hContact, PSS_MESSAGE, dwFlags); + if (!fFirstSend) { + job->hSendId = (HANDLE)id; + fFirstSend = TRUE; + PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); + } + *szSaved = savedChar; + szTemp = szSaved; + if (savedChar == ' ') { + szTemp++; + iCur++; + } + } + else { + id = SendChunkA(szTemp, hContact, PSS_MESSAGE, dwFlags); + if (!fFirstSend) { + job->hSendId = (HANDLE)id; + fFirstSend = TRUE; + PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SPLITSENDACK, (WPARAM)param, 0); + } + } + Sleep(500L); + } + while (fSplitting); + mir_free(szBegin); +} + +/** + * return effective length of the message in bytes (utf-8 encoded) + */ +int SendQueue::getSendLength(const int iEntry, int sendMode) +{ + if (m_jobs[iEntry].dwFlags & PREF_UNICODE && !(sendMode & SMODE_FORCEANSI)) { + int iLen = lstrlenA(m_jobs[iEntry].szSendBuffer); + WCHAR *wszBuf = (WCHAR *) & m_jobs[iEntry].szSendBuffer[iLen + 1]; + char *utf8 = mir_utf8encodeT(wszBuf); + m_jobs[iEntry].iSendLength = lstrlenA(utf8); + mir_free(utf8); + return(m_jobs[iEntry].iSendLength); + } + else { + m_jobs[iEntry].iSendLength = lstrlenA(m_jobs[iEntry].szSendBuffer); + return(m_jobs[iEntry].iSendLength); + } +} + +int SendQueue::sendQueued(TWindowData *dat, const int iEntry) +{ + CContactCache *ccActive = CContactCache::getContactCache(dat->hContact); + if (ccActive == NULL) + return 0; + + HWND hwndDlg = dat->hwnd; + + if (dat->sendMode & SMODE_MULTIPLE) { + int iJobs = 0; + int iMinLength = 0; + + m_jobs[iEntry].iStatus = SQ_INPROGRESS; + m_jobs[iEntry].hContact = ccActive->getActiveContact(); + m_jobs[iEntry].hOwnerWnd = hwndDlg; + + int iSendLength = getSendLength(iEntry, dat->sendMode); + + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, hContact, 0); + if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { + CContactCache *c = CContactCache::getContactCache(hContact); + if (c) + iMinLength = (iMinLength == 0 ? c->getMaxMessageLength() : min(c->getMaxMessageLength(), iMinLength)); + } + } + + if (iSendLength >= iMinLength) { + TCHAR tszError[256]; + mir_sntprintf(tszError, 256, TranslateT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), iMinLength); + ::SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, LPARAM(tszError)); + sendQueue->clearJob(iEntry); + return 0; + } + + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + HANDLE hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, hContact, 0); + if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) { + doSendLater(iEntry, 0, hContact, false); + iJobs++; + } + } + + sendQueue->clearJob(iEntry); + if (iJobs) + sendLater->flushQueue(); // force queue processing + return 0; + } + + if (dat->hContact == NULL) + return 0; //never happens + + dat->nMax = dat->cache->getMaxMessageLength(); // refresh length info + + if (dat->sendMode & SMODE_FORCEANSI && db_get_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 1)) + db_set_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 0); + else if (!(dat->sendMode & SMODE_FORCEANSI) && !db_get_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 0)) + db_set_b(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 1); + + if (M.GetByte("autosplit", 0) && !(dat->sendMode & SMODE_SENDLATER)) { + // determine send buffer length + BOOL fSplit = FALSE; + if (getSendLength(iEntry, dat->sendMode) >= dat->nMax) + fSplit = true; + + if (!fSplit) + goto send_unsplitted; + + m_jobs[iEntry].hContact = ccActive->getActiveContact(); + m_jobs[iEntry].hOwnerWnd = hwndDlg; + m_jobs[iEntry].iStatus = SQ_INPROGRESS; + m_jobs[iEntry].iAcksNeeded = 1; + m_jobs[iEntry].chunkSize = dat->nMax; + + DWORD dwOldFlags = m_jobs[iEntry].dwFlags; + if (dat->sendMode & SMODE_FORCEANSI) + m_jobs[iEntry].dwFlags &= ~PREF_UNICODE; + + if (!(m_jobs[iEntry].dwFlags & PREF_UNICODE) || dat->sendMode & SMODE_FORCEANSI) + mir_forkthread(DoSplitSendA, (LPVOID)iEntry); + else + mir_forkthread(DoSplitSendW, (LPVOID)iEntry); + m_jobs[iEntry].dwFlags = dwOldFlags; + } + else { + +send_unsplitted: + + m_jobs[iEntry].hContact = ccActive->getActiveContact(); + m_jobs[iEntry].hOwnerWnd = hwndDlg; + m_jobs[iEntry].iStatus = SQ_INPROGRESS; + m_jobs[iEntry].iAcksNeeded = 1; + if (dat->sendMode & SMODE_SENDLATER) { + TCHAR tszError[256]; + + int iSendLength = getSendLength(iEntry, dat->sendMode); + if (iSendLength >= dat->nMax) { + mir_sntprintf(tszError, 256, TranslateT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), dat->nMax); + SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, LPARAM(tszError)); + clearJob(iEntry); + return 0; + } + doSendLater(iEntry, dat); + clearJob(iEntry); + return 0; + } + m_jobs[iEntry].hSendId = (HANDLE)CallContactService(dat->hContact, PSS_MESSAGE, + (dat->sendMode & SMODE_FORCEANSI) ? (m_jobs[iEntry].dwFlags & ~PREF_UNICODE) : m_jobs[iEntry].dwFlags, (LPARAM)m_jobs[iEntry].szSendBuffer); + + if (dat->sendMode & SMODE_NOACK) { // fake the ack if we are not interested in receiving real acks + ACKDATA ack = {0}; + ack.hContact = dat->hContact; + ack.hProcess = m_jobs[iEntry].hSendId; + ack.type = ACKTYPE_MESSAGE; + ack.result = ACKRESULT_SUCCESS; + SendMessage(hwndDlg, HM_EVENTSENT, (WPARAM)MAKELONG(iEntry, 0), (LPARAM)&ack); + } + else SetTimer(hwndDlg, TIMERID_MSGSEND + iEntry, PluginConfig.m_MsgTimeout, NULL); + } + + dat->iOpenJobs++; + m_currentIndex++; + + // give icon feedback... + + if (dat->pContainer->hwndActive == hwndDlg) + ::UpdateReadChars(dat); + + if (!(dat->sendMode & SMODE_NOACK)) + ::HandleIconFeedback(dat, PluginConfig.g_IconSend); + + if (M.GetByte(SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN)) + ::SendMessage(dat->pContainer->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); + return 0; +} + +void SendQueue::clearJob(const int iIndex) +{ + SendJob &job = m_jobs[iIndex]; + mir_free(job.szSendBuffer); + memset(&job, 0, sizeof(SendJob)); +} + +/* + * this is called when: + * + * ) a delivery has completed successfully + * ) user decided to cancel a failed send + * it removes the completed / canceled send job from the queue and schedules the next job to send (if any) + */ + +void SendQueue::checkQueue(const TWindowData *dat) const +{ + if (dat) { + HWND hwndDlg = dat->hwnd; + + if (dat->iOpenJobs == 0) + ::HandleIconFeedback(const_cast(dat), (HICON)INVALID_HANDLE_VALUE); + else if (!(dat->sendMode & SMODE_NOACK)) + ::HandleIconFeedback(const_cast(dat), PluginConfig.g_IconSend); + + if (dat->pContainer->hwndActive == hwndDlg) + ::UpdateReadChars(const_cast(dat)); + } +} + +/* + * logs an error message to the message window. Optionally, appends the original message + * from the given sendJob (queue index) + */ + +void SendQueue::logError(const TWindowData *dat, int iSendJobIndex, const TCHAR *szErrMsg) const +{ + if (dat == 0) + return; + + int iMsgLen; + DBEVENTINFO dbei = { sizeof(dbei) }; + dbei.eventType = EVENTTYPE_ERRMSG; + if (iSendJobIndex >= 0) { + dbei.pBlob = (BYTE *)m_jobs[iSendJobIndex].szSendBuffer; + iMsgLen = lstrlenA(m_jobs[iSendJobIndex].szSendBuffer) + 1; + } + else { + iMsgLen = 0; + dbei.pBlob = NULL; + } + if (m_jobs[iSendJobIndex].dwFlags & PREF_UTF) + dbei.flags = DBEF_UTF; + if (iSendJobIndex >= 0) { + if (m_jobs[iSendJobIndex].dwFlags & PREF_UNICODE) { + iMsgLen *= 3; + } + } + dbei.cbBlob = iMsgLen; + dbei.timestamp = time(NULL); + dbei.szModule = (char *)szErrMsg; + StreamInEvents(dat->hwnd, NULL, 1, 1, &dbei); +} + +/* + * enable or disable the sending controls in the given window + * ) input area + * ) multisend contact list instance + * ) send button + */ + +void SendQueue::EnableSending(const TWindowData *dat, const int iMode) +{ + if (dat) { + HWND hwndDlg = dat->hwnd; + ::SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, (WPARAM)iMode ? FALSE : TRUE, 0); + Utils::enableDlgControl(hwndDlg, IDC_CLIST, iMode ? TRUE : FALSE); + ::EnableSendButton(dat, iMode); + } +} + +/* + * show or hide the error control button bar on top of the window + */ + +void SendQueue::showErrorControls(TWindowData *dat, const int showCmd) const +{ + UINT myerrorControls[] = { IDC_STATICERRORICON, IDC_STATICTEXT, IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER}; + int i; + HWND hwndDlg = dat->hwnd; + + if (showCmd) { + TCITEM item = {0}; + dat->hTabIcon = PluginConfig.g_iconErr; + item.mask = TCIF_IMAGE; + item.iImage = 0; + TabCtrl_SetItem(GetDlgItem(dat->pContainer->hwnd, IDC_MSGTABS), dat->iTabID, &item); + dat->dwFlags |= MWF_ERRORSTATE; + } + else { + dat->dwFlags &= ~MWF_ERRORSTATE; + dat->hTabIcon = dat->hTabStatusIcon; + } + + for (i=0; i < 5; i++) { + if (IsWindow(GetDlgItem(hwndDlg, myerrorControls[i]))) + Utils::showDlgControl(hwndDlg, myerrorControls[i], showCmd ? SW_SHOW : SW_HIDE); + } + + SendMessage(hwndDlg, WM_SIZE, 0, 0); + DM_ScrollToBottom(dat, 0, 1); + if (m_jobs[0].hContact != 0) + EnableSending(dat, TRUE); +} + +void SendQueue::recallFailed(const TWindowData *dat, int iEntry) const +{ + if (dat == NULL) + return; + int iLen = GetWindowTextLengthA(GetDlgItem(dat->hwnd, IDC_MESSAGE)); + NotifyDeliveryFailure(dat); + if (iLen != 0) + return; + /* message area is empty, so we can recall the failed message... */ + SETTEXTEX stx = {ST_DEFAULT, 1200}; + if (m_jobs[iEntry].dwFlags & PREF_UNICODE) + SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)&m_jobs[iEntry].szSendBuffer[lstrlenA(m_jobs[iEntry].szSendBuffer) + 1]); + else { + stx.codepage = (m_jobs[iEntry].dwFlags & PREF_UTF) ? CP_UTF8 : CP_ACP; + SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_jobs[iEntry].szSendBuffer); + } + UpdateSaveAndSendButton(const_cast(dat)); + SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETSEL, (WPARAM)- 1, (LPARAM)- 1); +} + +void SendQueue::UpdateSaveAndSendButton(TWindowData *dat) +{ + if (dat) { + HWND hwndDlg = dat->hwnd; + + GETTEXTLENGTHEX gtxl = {0}; + gtxl.codepage = CP_UTF8; + gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES; + + int len = SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); + if (len && GetSendButtonState(hwndDlg) == PBS_DISABLED) + EnableSendButton(dat, TRUE); + else if (len == 0 && GetSendButtonState(hwndDlg) != PBS_DISABLED) + EnableSendButton(dat, FALSE); + + if (len) { // looks complex but avoids flickering on the button while typing. + if (!(dat->dwFlags & MWF_SAVEBTN_SAV)) { + SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]); + SendDlgItemMessage(hwndDlg, IDC_SAVE, BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_save, BATF_TCHAR); + dat->dwFlags |= MWF_SAVEBTN_SAV; + } + } + else { + SendDlgItemMessage(hwndDlg, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]); + SendDlgItemMessage(hwndDlg, IDC_SAVE, BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, BATF_TCHAR); + dat->dwFlags &= ~MWF_SAVEBTN_SAV; + } + dat->textLen = len; + } +} + +void SendQueue::NotifyDeliveryFailure(const TWindowData *dat) +{ + if (M.GetByte("adv_noErrorPopups", 0)) + return; + + if ( CallService(MS_POPUP_QUERY, PUQS_GETSTATUS, 0) != 1) + return; + + POPUPDATAT ppd = { 0 }; + ppd.lchContact = dat->hContact; + lstrcpyn(ppd.lptzContactName, dat->cache->getNick(), MAX_CONTACTNAME); + lstrcpyn(ppd.lptzText, TranslateT("A message delivery has failed.\nClick to open the message window."), MAX_SECONDLINE); + + if (!(BOOL)M.GetByte(MODULE, OPT_COLDEFAULT_ERR, TRUE)) { + ppd.colorText = (COLORREF)M.GetDword(MODULE, OPT_COLTEXT_ERR, DEFAULT_COLTEXT); + ppd.colorBack = (COLORREF)M.GetDword(MODULE, OPT_COLBACK_ERR, DEFAULT_COLBACK); + } + else ppd.colorText = ppd.colorBack = 0; + + ppd.PluginWindowProc = reinterpret_cast(Utils::PopupDlgProcError); + ppd.lchIcon = PluginConfig.g_iconErr; + ppd.PluginData = (void*)dat->hContact; + ppd.iSeconds = (int)M.GetDword(MODULE, OPT_DELAY_ERR, (DWORD)DEFAULT_DELAY); + PUAddPopupT(&ppd); +} + +/* + * searches string for characters typical for RTL text (hebrew and other RTL languages +*/ + +int SendQueue::RTL_Detect(const WCHAR *pszwText) +{ + int i, n = 0; + int iLen = lstrlenW(pszwText); + + WORD *infoTypeC2 = (WORD *)mir_alloc(sizeof(WORD) * (iLen + 2)); + if (infoTypeC2) { + ZeroMemory(infoTypeC2, sizeof(WORD) * (iLen + 2)); + + GetStringTypeW(CT_CTYPE2, pszwText, iLen, infoTypeC2); + + for (i=0; i < iLen; i++) + if (infoTypeC2[i] == C2_RIGHTTOLEFT) + n++; + + mir_free(infoTypeC2); + return(n >= 2 ? 1 : 0); + } + return 0; +} + +int SendQueue::ackMessage(TWindowData *dat, WPARAM wParam, LPARAM lParam) +{ + ACKDATA *ack = (ACKDATA *) lParam; + + TContainerData *m_pContainer = 0; + if (dat) + m_pContainer = dat->pContainer; + + int iFound = (int)(LOWORD(wParam)); + SendJob &job = m_jobs[iFound]; + + if (job.iStatus == SQ_ERROR) { // received ack for a job which is already in error state... + if (dat) { // window still open + if (dat->iCurrentQueueError == iFound) { + dat->iCurrentQueueError = -1; + showErrorControls(dat, FALSE); + } + } + // we must discard this job, because there is no message window open to handle the + // error properly. But we display a tray notification to inform the user about the problem. + else goto inform_and_discard; + } + + // failed acks are only handled when the window is still open. with no window open, they will be *silently* discarded + + if (ack->result == ACKRESULT_FAILED) { + if (dat) { + // "hard" errors are handled differently in multisend. There is no option to retry - once failed, they + // are discarded and the user is notified with a small log message. + if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) + SkinPlaySound("SendError"); + + TCHAR *szAckMsg = mir_a2t((char *)ack->lParam); + mir_sntprintf(job.szErrorMsg, SIZEOF(job.szErrorMsg), TranslateT("Delivery failure: %s"), szAckMsg); + job.iStatus = SQ_ERROR; + mir_free(szAckMsg); + KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound); + if (!(dat->dwFlags & MWF_ERRORSTATE)) + handleError(dat, iFound); + return 0; + } + +inform_and_discard: + _DebugPopup(job.hContact, TranslateT("A message delivery has failed after the contacts chat window was closed. You may want to resend the last message")); + clearJob(iFound); + return 0; + } + + DBEVENTINFO dbei = { sizeof(dbei) }; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.flags = DBEF_SENT; + dbei.szModule = GetContactProto(job.hContact); + dbei.timestamp = time(NULL); + dbei.cbBlob = lstrlenA(job.szSendBuffer) + 1; + + if (dat) + dat->cache->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1); + else { + CContactCache *cc = CContactCache::getContactCache(job.hContact); + if (cc) + cc->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1); + } + + if (job.dwFlags & PREF_UNICODE) + dbei.cbBlob *= sizeof(TCHAR) + 1; + if (job.dwFlags & PREF_RTL) + dbei.flags |= DBEF_RTL; + if (job.dwFlags & PREF_UTF) + dbei.flags |= DBEF_UTF; + dbei.pBlob = (PBYTE)job.szSendBuffer; + + MessageWindowEvent evt = { sizeof(evt), (int)job.hSendId, job.hContact, &dbei }; + NotifyEventHooks(PluginConfig.m_event_WriteEvent, 0, (LPARAM)&evt); + + job.szSendBuffer = (char*)dbei.pBlob; + HANDLE hNewEvent = db_event_add(job.hContact, &dbei); + + if (m_pContainer) + if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND)) + SkinPlaySound("SendMsg"); + + M.BroadcastMessage(DM_APPENDMCEVENT, job.hContact, LPARAM(hNewEvent)); + + job.hSendId = NULL; + job.iAcksNeeded--; + + if (job.iAcksNeeded == 0) { // everything sent + //if (job.hOwner != 0 && dat) + // EnableSending(dat, TRUE); + clearJob(iFound); + if (dat) { + KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound); + dat->iOpenJobs--; + } + m_currentIndex--; + } + if (dat) { + checkQueue(dat); + + int iNextFailed = findNextFailed(dat); + if (iNextFailed >= 0 && !(dat->dwFlags & MWF_ERRORSTATE)) + handleError(dat, iNextFailed); + else { + if (M.GetByte("AutoClose", 0)) { + if (M.GetByte("adv_AutoClose_2", 0)) + SendMessage(dat->hwnd, WM_CLOSE, 0, 1); + else + SendMessage(dat->pContainer->hwnd, WM_CLOSE, 0, 0); + } + } + } + return 0; +} + +LRESULT SendQueue::WarnPendingJobs(unsigned int uNrMessages) +{ + return MessageBox(0, + TranslateT("There are unsent messages waiting for confirmation.\nIf you close the window now, Miranda will try to send them but may be unable to inform you about possible delivery errors.\nDo you really want to close the window(s)?"), + TranslateT("Message window warning"), MB_YESNO | MB_ICONHAND); +} + +/** + * This just adds the message to the database for later delivery and + * adds the contact to the list of contacts that have queued messages + * + * @param iJobIndex int: index of the send job + * dat: Message window data + * fAddHeader: add the "message was sent delayed" header (default = true) + * hContact : contact to which the job should be added (default = hOwner of the send job) + * + * @return the index on success, -1 on failure + */ +int SendQueue::doSendLater(int iJobIndex, TWindowData *dat, MCONTACT hContact, bool fIsSendLater) +{ + bool fAvail = sendLater->isAvail(); + + const TCHAR *szNote = 0; + + if (fIsSendLater && dat) { + if (fAvail) + szNote = TranslateT("Message successfully queued for later delivery.\nIt will be sent as soon as possible and a popup will inform you about the result."); + else + szNote = TranslateT("The send later feature is not available on this protocol."); + + char *utfText = mir_utf8encodeT(szNote); + DBEVENTINFO dbei; + dbei.cbSize = sizeof(dbei); + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.flags = DBEF_SENT | DBEF_UTF; + dbei.szModule = GetContactProto(dat->hContact); + dbei.timestamp = time(NULL); + dbei.cbBlob = lstrlenA(utfText) + 1; + dbei.pBlob = (PBYTE) utfText; + StreamInEvents(dat->hwnd, 0, 1, 1, &dbei); + if (dat->hDbEventFirst == NULL) + SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0); + dat->cache->saveHistory(0, 0); + EnableSendButton(dat, FALSE); + if (dat->pContainer->hwndActive == dat->hwnd) + UpdateReadChars(dat); + SendDlgItemMessage(dat->hwnd, IDC_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]); + SendDlgItemMessage(dat->hwnd, IDC_SAVE, BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, BATF_TCHAR); + dat->dwFlags &= ~MWF_SAVEBTN_SAV; + mir_free(utfText); + + if (!fAvail) + return 0; + } + + if (iJobIndex >= 0 && iJobIndex < NR_SENDJOBS) { + SendJob* job = &m_jobs[iJobIndex]; + char szKeyName[20]; + TCHAR tszHeader[150]; + + if (fIsSendLater) { + time_t now = time(0); + TCHAR tszTimestamp[30]; + _tcsftime(tszTimestamp, SIZEOF(tszTimestamp), _T("%Y.%m.%d - %H:%M"), _localtime32((__time32_t *)&now)); + tszTimestamp[29] = 0; + mir_snprintf(szKeyName, 20, "S%d", now); + mir_sntprintf(tszHeader, SIZEOF(tszHeader), TranslateT("\n(Sent delayed. Original timestamp %s)"), tszTimestamp); + } + else mir_sntprintf(tszHeader, SIZEOF(tszHeader), _T("M%d|"), time(0)); + + if (job->dwFlags & PREF_UTF || !(job->dwFlags & PREF_UNICODE)) { + char *utf_header = mir_utf8encodeT(tszHeader); + UINT required = lstrlenA(utf_header) + lstrlenA(job->szSendBuffer) + 10; + char *tszMsg = reinterpret_cast(mir_alloc(required)); + + if (fIsSendLater) { + mir_snprintf(tszMsg, required, "%s%s", job->szSendBuffer, utf_header); + db_set_s(hContact ? hContact : job->hContact, "SendLater", szKeyName, tszMsg); + } + else { + mir_snprintf(tszMsg, required, "%s%s", utf_header, job->szSendBuffer); + sendLater->addJob(tszMsg, hContact); + } + mir_free(utf_header); + mir_free(tszMsg); + } + else if (job->dwFlags & PREF_UNICODE) { + int iLen = lstrlenA(job->szSendBuffer); + wchar_t *wszMsg = (wchar_t *)&job->szSendBuffer[iLen + 1]; + + UINT required = sizeof(TCHAR) * (lstrlen(tszHeader) + lstrlenW(wszMsg) + 10); + + TCHAR *tszMsg = reinterpret_cast(mir_alloc(required)); + if (fIsSendLater) + mir_sntprintf(tszMsg, required, _T("%s%s"), wszMsg, tszHeader); + else + mir_sntprintf(tszMsg, required, _T("%s%s"), tszHeader, wszMsg); + char *utf = mir_utf8encodeT(tszMsg); + if (fIsSendLater) + db_set_s(hContact ? hContact : job->hContact, "SendLater", szKeyName, utf); + else + sendLater->addJob(utf, hContact); + mir_free(utf); + mir_free(tszMsg); + } + if (fIsSendLater) { + int iCount = db_get_dw(hContact ? hContact : job->hContact, "SendLater", "count", 0); + iCount++; + db_set_dw(hContact ? hContact : job->hContact, "SendLater", "count", iCount); + sendLater->addContact(hContact ? hContact : job->hContact); + } + return iJobIndex; + } + return -1; +} diff --git a/plugins/TabSRMM/src/srmm.cpp b/plugins/TabSRMM/src/srmm.cpp index 145f6eb4de..06fc9c878b 100644 --- a/plugins/TabSRMM/src/srmm.cpp +++ b/plugins/TabSRMM/src/srmm.cpp @@ -1,206 +1,209 @@ -/* - * astyle --force-indent=tab=4 --brackets=linux --indent-switches - * --pad=oper --one-line=keep-blocks --unpad=paren - * - * Miranda NG: the free IM client for Microsoft* Windows* - * - * 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 - * - * plugin loading functions and global exports. - * - */ - -#include "commonheaders.h" - -HINSTANCE g_hInst; -LOGFONT lfDefault = {0}; - -/* - * miranda interfaces - */ - -int hLangpack; -TIME_API tmi = {0}; -CLIST_INTERFACE *pcli; - -PLUGININFOEX pluginInfo = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __AUTHOREMAIL, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {6CA5F042-7A7F-47CC-A715-FC8C46FBF434} - {0x6ca5f042, 0x7a7f, 0x47cc, {0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34}} -}; - -BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) -{ - g_hInst = hinstDLL; - return TRUE; -} - -extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) -{ - return &pluginInfo; -} - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_SRMM, MIID_CHAT, MIID_LAST}; - -extern "C" int __declspec(dllexport) Load(void) -{ - if (WinVerMajor() < 5) { - MessageBox(0, TranslateT("This version of TabSRMM requires Windows 2000 or later."), _T("tabSRMM"), MB_OK | MB_ICONERROR); - return 1; - } - - mir_getCLI(); - mir_getTMI(&tmi); - mir_getLP(&pluginInfo); - - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE); - - Chat_Load(); - - return LoadSendRecvMessageModule(); -} - -extern "C" int __declspec(dllexport) Unload(void) -{ - FreeLogFonts(); - Chat_Unload(); - int iRet = SplitmsgShutdown(); - Skin->setupTabCloseBitmap(true); - Skin->UnloadAeroTabs(); - CleanTempFiles(); - delete Skin; - DestroyServiceFunction(hTypingNotify); - delete sendLater; - delete sendQueue; - return iRet; -} - -int _DebugTraceW(const wchar_t *fmt, ...) -{ - wchar_t debug[2048]; - int ibsize = 2047; - SYSTEMTIME st; - va_list va; - char tszTime[50]; - va_start(va, fmt); - - GetLocalTime(&st); - - mir_snprintf(tszTime, 50, "%02d.%02d.%04d - %02d:%02d:%02d.%04d: ", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); - - - mir_vsntprintf(debug, ibsize - 10, fmt, va); -//#ifdef _DEBUG - OutputDebugStringW(debug); -//#else - { - char szLogFileName[MAX_PATH], szDataPath[MAX_PATH]; - FILE *f; - - CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)szDataPath); - mir_snprintf(szLogFileName, MAX_PATH, "%s\\%s", szDataPath, "tabsrmm_debug.log"); - f = fopen(szLogFileName, "a+"); - if (f) { - char *szDebug = mir_utf8encodeT(debug); - fputs(tszTime, f); - fputs(szDebug, f); - fputs("\n", f); - fclose(f); - if (szDebug) - mir_free(szDebug); - } - } -//#endif - return 0; -} - -int _DebugTraceA(const char *fmt, ...) -{ - char debug[2048]; - int ibsize = 2047; - va_list va; - va_start(va, fmt); - - lstrcpyA(debug, "TABSRMM: "); - mir_vsnprintf(&debug[9], ibsize - 10, fmt, va); -#ifdef _DEBUG - OutputDebugStringA(debug); -#else - { - char szLogFileName[MAX_PATH], szDataPath[MAX_PATH]; - CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)szDataPath); - mir_snprintf(szLogFileName, MAX_PATH, "%s\\tabsrmm_debug.log", szDataPath); - FILE *f = fopen(szLogFileName, "a+"); - if (f) { - fputs(debug, f); - fputs("\n", f); - fclose(f); - } - } -#endif - return 0; -} - -/* - * output a notification message. - * may accept a hContact to include the contacts nickname in the notification message... - * the actual message is using printf() rules for formatting and passing the arguments... - * - * can display the message either as systray notification (baloon popup) or using the - * popup plugin. - */ - -int _DebugPopup(MCONTACT hContact, const TCHAR *fmt, ...) -{ - va_list va; - TCHAR debug[1024]; - int ibsize = 1023; - - va_start(va, fmt); - mir_vsntprintf(debug, ibsize, fmt, va); - - if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) { - MIRANDASYSTRAYNOTIFY tn; - TCHAR szTitle[128]; - mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("TabSRMM Message (%s)"), - (hContact != 0) ? pcli->pfnGetContactDisplayName(hContact, 0) : TranslateT("Global")); - - tn.szProto = NULL; - tn.cbSize = sizeof(tn); - tn.tszInfoTitle = szTitle; - tn.tszInfo = debug; - tn.dwInfoFlags = NIIF_INFO; - tn.dwInfoFlags |= NIIF_INTERN_UNICODE; - tn.uTimeout = 1000 * 4; - CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&tn); - } - return 0; -} +/* + * astyle --force-indent=tab=4 --brackets=linux --indent-switches + * --pad=oper --one-line=keep-blocks --unpad=paren + * + * Miranda NG: the free IM client for Microsoft* Windows* + * + * 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 + * + * plugin loading functions and global exports. + * + */ + +#include "commonheaders.h" + +HINSTANCE g_hInst; +LOGFONT lfDefault = {0}; + +/* + * miranda interfaces + */ + +int hLangpack; +TIME_API tmi = {0}; +CLIST_INTERFACE *pcli; + +PLUGININFOEX pluginInfo = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __AUTHOREMAIL, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {6CA5F042-7A7F-47CC-A715-FC8C46FBF434} + {0x6ca5f042, 0x7a7f, 0x47cc, {0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34}} +}; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + g_hInst = hinstDLL; + return TRUE; +} + +extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_SRMM, MIID_CHAT, MIID_LAST}; + +extern "C" int __declspec(dllexport) Load(void) +{ + if (WinVerMajor() < 5) { + MessageBox(0, TranslateT("This version of TabSRMM requires Windows 2000 or later."), _T("tabSRMM"), MB_OK | MB_ICONERROR); + return 1; + } + + mir_getCLI(); + mir_getTMI(&tmi); + mir_getLP(&pluginInfo); + + SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE); + + Chat_Load(); + + return LoadSendRecvMessageModule(); +} + +extern "C" int __declspec(dllexport) Unload(void) +{ + FreeLogFonts(); + Chat_Unload(); + int iRet = SplitmsgShutdown(); + Skin->setupTabCloseBitmap(true); + Skin->UnloadAeroTabs(); + CleanTempFiles(); + delete Skin; + DestroyServiceFunction(hTypingNotify); + delete sendLater; + delete sendQueue; + return iRet; +} + +int _DebugTraceW(const wchar_t *fmt, ...) +{ + wchar_t debug[2048]; + int ibsize = 2047; + SYSTEMTIME st; + va_list va; + char tszTime[50]; + va_start(va, fmt); + + GetLocalTime(&st); + + mir_snprintf(tszTime, 50, "%02d.%02d.%04d - %02d:%02d:%02d.%04d: ", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); + + + mir_vsntprintf(debug, ibsize - 10, fmt, va); +//#ifdef _DEBUG + OutputDebugStringW(debug); +//#else + { + char szLogFileName[MAX_PATH], szDataPath[MAX_PATH]; + FILE *f; + + CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)szDataPath); + mir_snprintf(szLogFileName, MAX_PATH, "%s\\%s", szDataPath, "tabsrmm_debug.log"); + f = fopen(szLogFileName, "a+"); + if (f) { + char *szDebug = mir_utf8encodeT(debug); + fputs(tszTime, f); + if (szDebug != NULL) { + fputs(szDebug, f); + mir_free(szDebug); + } else { + fputs("mir_utf8encodeT() fail in _DebugTraceW()", f); + } + fputs("\n", f); + fclose(f); + } + } +//#endif + return 0; +} + +int _DebugTraceA(const char *fmt, ...) +{ + char debug[2048]; + int ibsize = 2047; + va_list va; + va_start(va, fmt); + + lstrcpyA(debug, "TABSRMM: "); + mir_vsnprintf(&debug[9], ibsize - 10, fmt, va); +#ifdef _DEBUG + OutputDebugStringA(debug); +#else + { + char szLogFileName[MAX_PATH], szDataPath[MAX_PATH]; + CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (LPARAM)szDataPath); + mir_snprintf(szLogFileName, MAX_PATH, "%s\\tabsrmm_debug.log", szDataPath); + FILE *f = fopen(szLogFileName, "a+"); + if (f) { + fputs(debug, f); + fputs("\n", f); + fclose(f); + } + } +#endif + return 0; +} + +/* + * output a notification message. + * may accept a hContact to include the contacts nickname in the notification message... + * the actual message is using printf() rules for formatting and passing the arguments... + * + * can display the message either as systray notification (baloon popup) or using the + * popup plugin. + */ + +int _DebugPopup(MCONTACT hContact, const TCHAR *fmt, ...) +{ + va_list va; + TCHAR debug[1024]; + int ibsize = 1023; + + va_start(va, fmt); + mir_vsntprintf(debug, ibsize, fmt, va); + + if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) { + MIRANDASYSTRAYNOTIFY tn; + TCHAR szTitle[128]; + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("TabSRMM Message (%s)"), + (hContact != 0) ? pcli->pfnGetContactDisplayName(hContact, 0) : TranslateT("Global")); + + tn.szProto = NULL; + tn.cbSize = sizeof(tn); + tn.tszInfoTitle = szTitle; + tn.tszInfo = debug; + tn.dwInfoFlags = NIIF_INFO; + tn.dwInfoFlags |= NIIF_INTERN_UNICODE; + tn.uTimeout = 1000 * 4; + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&tn); + } + return 0; +} diff --git a/plugins/TabSRMM/src/templates.cpp b/plugins/TabSRMM/src/templates.cpp index b32f300735..4bd8f52bae 100644 --- a/plugins/TabSRMM/src/templates.cpp +++ b/plugins/TabSRMM/src/templates.cpp @@ -1,395 +1,394 @@ -/* - * astyle --force-indent=tab=4 --brackets=linux --indent-switches - * --pad=oper --one-line=keep-blocks --unpad=paren - * - * Miranda NG: the free IM client for Microsoft* Windows* - * - * 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-2009 by silvercircle _at_ gmail _dot_ com and contributors - * - * Simple editor for the message log templates - * - */ - -#include "commonheaders.h" - -/* -* hardcoded default set of templates for both LTR and RTL. -* cannot be changed and may be used at any time to "revert" to a working layout -*/ - -char *TemplateNames[] = { - "Message In", - "Message Out", - "Group In (Start)", - "Group Out (Start)", - "Group In (Inner)", - "Group Out (Inner)", - "Status change", - "Error message" -}; - -TTemplateSet LTR_Default = { TRUE, - _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), - _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), - _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), - _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), - _T("%S %T%|%M"), - _T("%S %T%|%M"), - _T("%I %S %&D, %&T, %N %M%! "), - _T("%I%S %D, %T, %e%l%M"), - "Default LTR" - }; - -TTemplateSet RTL_Default = { TRUE, - _T("%I %S %N %D%n%S %T%|%M"), - _T("%I %S %N %D%n%S %T%|%M"), - _T("%I %S %N %D%n%S %T%|%M"), - _T("%I %S %N %D%n%S %T%|%M"), - _T("%S %T%|%M"), - _T("%S %T%|%M"), - _T("%I%S %D, %T, %N %M%! "), - _T("%I%S %D, %T, %e%l%M"), - "Default RTL" - }; - -TTemplateSet LTR_Active, RTL_Active; -static int helpActive = 0; - - -/* -* loads template set overrides from hContact into the given set of already existing -* templates -*/ - -static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl) -{ - DBVARIANT dbv = {0}; - int i; - - for (i=0; i <= TMPL_ERRMSG; i++) { - if (db_get_ts(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv)) - continue; - if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) - mir_sntprintf(tSet->szTemplates[i], TEMPLATE_LENGTH, _T("%s"), dbv.ptszVal); - db_free(&dbv); - } -} - -void LoadDefaultTemplates() -{ - int i; - - LTR_Active = LTR_Default; - RTL_Active = RTL_Default; - - if (M.GetByte(RTLTEMPLATES_MODULE, "setup", 0) < 2) { - for (i=0; i <= TMPL_ERRMSG; i++) - db_set_ts(NULL, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]); - db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2); - } - if (M.GetByte(TEMPLATES_MODULE, "setup", 0) < 2) { - for (i=0; i <= TMPL_ERRMSG; i++) - db_set_ts(NULL, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]); - db_set_b(0, TEMPLATES_MODULE, "setup", 2); - } - LoadTemplatesFrom(<R_Active, 0, 0); - LoadTemplatesFrom(&RTL_Active, 0, 1); -} - -INT_PTR CALLBACK DlgProcTemplateEditor(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - TWindowData *dat = 0; - TemplateEditorInfo *teInfo = 0; - TTemplateSet *tSet; - int i; - dat = (TWindowData*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - /* - * since this dialog needs a MessageWindowData * but has no container, we can store - * the extended info struct in pContainer *) - */ - if (dat) { - teInfo = (TemplateEditorInfo *)dat->pContainer; - tSet = teInfo->rtl ? dat->pContainer->rtl_templates : dat->pContainer->ltr_templates; - } - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - TemplateEditorNew *teNew = (TemplateEditorNew *)lParam; - COLORREF url_visited = RGB(128, 0, 128); - COLORREF url_unvisited = RGB(0, 0, 255); - dat = (TWindowData*)mir_calloc( sizeof(TWindowData)); - dat->pContainer = (TContainerData*)mir_alloc(sizeof(TContainerData)); - ZeroMemory(dat->pContainer, sizeof(TContainerData)); - teInfo = (TemplateEditorInfo *)dat->pContainer; - ZeroMemory(teInfo, sizeof(TemplateEditorInfo)); - teInfo->hContact = teNew->hContact; - teInfo->rtl = teNew->rtl; - teInfo->hwndParent = teNew->hwndParent; - - LoadOverrideTheme(dat->pContainer); - /* - * set hContact to the first found contact so that we can use the Preview window properly - * also, set other parameters needed by the streaming function to display events - */ - - SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_LINK); - SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); - SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_EXLIMITTEXT, 0, 0x80000000); - - dat->hContact = db_find_first(); - dat->szProto = GetContactProto(dat->hContact); - while(dat->szProto == 0 && dat->hContact != 0) { - dat->hContact = db_find_next(dat->hContact); - dat->szProto = GetContactProto(dat->hContact); - } - dat->dwFlags = dat->pContainer->theme.dwFlags; - - dat->cache = CContactCache::getContactCache(dat->hContact); - dat->cache->updateState(); - dat->cache->updateUIN(); - dat->cache->updateStats(TSessionStats::INIT_TIMER); - GetMYUIN(dat); - - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) dat); - ShowWindow(hwndDlg, SW_SHOW); - SendDlgItemMessage(hwndDlg, IDC_EDITTEMPLATE, EM_LIMITTEXT, (WPARAM)TEMPLATE_LENGTH - 1, 0); - SetWindowText(hwndDlg, TranslateT("Template Set Editor")); - Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); - for (i=0; i <= TMPL_ERRMSG; i++) { - SendDlgItemMessageA(hwndDlg, IDC_TEMPLATELIST, LB_ADDSTRING, 0, (LPARAM)Translate(TemplateNames[i])); - SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_SETITEMDATA, i, (LPARAM)i); - } - Utils::enableDlgControl(teInfo->hwndParent, IDC_MODIFY, FALSE); - Utils::enableDlgControl(teInfo->hwndParent, IDC_RTLMODIFY, FALSE); - - SendDlgItemMessage(hwndDlg, IDC_COLOR1, CPM_SETCOLOUR, 0, M.GetDword("cc1", SRMSGDEFSET_BKGCOLOUR)); - SendDlgItemMessage(hwndDlg, IDC_COLOR2, CPM_SETCOLOUR, 0, M.GetDword("cc2", SRMSGDEFSET_BKGCOLOUR)); - SendDlgItemMessage(hwndDlg, IDC_COLOR3, CPM_SETCOLOUR, 0, M.GetDword("cc3", SRMSGDEFSET_BKGCOLOUR)); - SendDlgItemMessage(hwndDlg, IDC_COLOR4, CPM_SETCOLOUR, 0, M.GetDword("cc4", SRMSGDEFSET_BKGCOLOUR)); - SendDlgItemMessage(hwndDlg, IDC_COLOR5, CPM_SETCOLOUR, 0, M.GetDword("cc5", SRMSGDEFSET_BKGCOLOUR)); - SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); - } - return TRUE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - - case IDC_RESETALLTEMPLATES: - if (MessageBox(0, TranslateT("This will reset the template set to the default built-in templates. Are you sure you want to do this?"), - TranslateT("Template Set Editor"), MB_YESNO | MB_ICONQUESTION) == IDYES) { - db_set_b(0, teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, "setup", 0); - LoadDefaultTemplates(); - MessageBox(0, TranslateT("Template set was successfully reset, please close and reopen all message windows. This template editor window will now close."), - TranslateT("Template Set Editor"), MB_OK); - DestroyWindow(hwndDlg); - } - break; - - case IDC_TEMPLATELIST: - switch (HIWORD(wParam)) { - LRESULT iIndex; - case LBN_DBLCLK: - iIndex = SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_GETCURSEL, 0, 0); - if (iIndex != LB_ERR) { - SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[iIndex]); - teInfo->inEdit = iIndex; - teInfo->changed = FALSE; - teInfo->selchanging = FALSE; - SetFocus(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE)); - SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, FALSE, 0); - } - break; - - case LBN_SELCHANGE: - iIndex = SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_GETCURSEL, 0, 0); - teInfo->selchanging = TRUE; - if (iIndex != LB_ERR) { - SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[iIndex]); - teInfo->inEdit = iIndex; - teInfo->changed = FALSE; - } - SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); - } - break; - - case IDC_VARIABLESHELP: - CallService(MS_UTILS_OPENURL, 0, (LPARAM)"http://wiki.miranda.or.at/TabSRMM/Templates"); - break; - - case IDC_EDITTEMPLATE: - if (HIWORD(wParam) == EN_CHANGE) { - if (!teInfo->selchanging) { - teInfo->changed = TRUE; - teInfo->updateInfo[teInfo->inEdit] = TRUE; - Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_FORGET, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_REVERT, TRUE); - } - InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); - } - break; - case IDC_SAVETEMPLATE: - { - TCHAR newTemplate[TEMPLATE_LENGTH + 2]; - - GetWindowText(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), newTemplate, TEMPLATE_LENGTH); - CopyMemory(tSet->szTemplates[teInfo->inEdit], newTemplate, sizeof(TCHAR) * TEMPLATE_LENGTH); - teInfo->changed = FALSE; - teInfo->updateInfo[teInfo->inEdit] = FALSE; - Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); - InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); - db_set_ts(teInfo->hContact, teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[teInfo->inEdit], newTemplate); - SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); - } - break; - - case IDC_FORGET: - teInfo->changed = FALSE; - teInfo->updateInfo[teInfo->inEdit] = FALSE; - teInfo->selchanging = TRUE; - SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[teInfo->inEdit]); - SetFocus(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE)); - InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, TRUE); - Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); - teInfo->selchanging = FALSE; - SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); - break; - - case IDC_REVERT: - teInfo->changed = FALSE; - teInfo->updateInfo[teInfo->inEdit] = FALSE; - teInfo->selchanging = TRUE; - CopyMemory(tSet->szTemplates[teInfo->inEdit], LTR_Default.szTemplates[teInfo->inEdit], sizeof(TCHAR) * TEMPLATE_LENGTH); - SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[teInfo->inEdit]); - db_unset(teInfo->hContact, teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[teInfo->inEdit]); - SetFocus(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE)); - InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); - teInfo->selchanging = FALSE; - Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); - Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, TRUE); - SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); - break; - - case IDC_UPDATEPREVIEW: - SendMessage(hwndDlg, DM_UPDATETEMPLATEPREVIEW, 0, 0); - break; - } - break; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *) lParam; - int iItem = dis->itemData; - HBRUSH bkg, oldBkg; - SetBkMode(dis->hDC, TRANSPARENT); - FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW)); - if (dis->itemState & ODS_SELECTED) { - if (teInfo->updateInfo[iItem] == TRUE) { - bkg = CreateSolidBrush(RGB(255, 0, 0)); - oldBkg = (HBRUSH)SelectObject(dis->hDC, bkg); - FillRect(dis->hDC, &dis->rcItem, bkg); - SelectObject(dis->hDC, oldBkg); - DeleteObject(bkg); - } else - FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT)); - - SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); - } else { - if (teInfo->updateInfo[iItem] == TRUE) - SetTextColor(dis->hDC, RGB(255, 0, 0)); - else - SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT)); - } - char *pszName = Translate(TemplateNames[iItem]); - TextOutA(dis->hDC, dis->rcItem.left, dis->rcItem.top, pszName, lstrlenA(pszName)); - } - return TRUE; - - case DM_UPDATETEMPLATEPREVIEW: { - DBEVENTINFO dbei = {0}; - int iIndex = SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_GETCURSEL, 0, 0); - TCHAR szTemp[TEMPLATE_LENGTH + 2]; - - if (teInfo->changed) { - CopyMemory(szTemp, tSet->szTemplates[teInfo->inEdit], TEMPLATE_LENGTH * sizeof(TCHAR)); - GetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[teInfo->inEdit], TEMPLATE_LENGTH); - } - dbei.szModule = dat->szProto; - dbei.timestamp = time(NULL); - dbei.eventType = (iIndex == 6) ? EVENTTYPE_STATUSCHANGE : EVENTTYPE_MESSAGE; - dbei.eventType = (iIndex == 7) ? EVENTTYPE_ERRMSG : dbei.eventType; - if (dbei.eventType == EVENTTYPE_ERRMSG) - dbei.szModule = "Sample error message"; - dbei.cbSize = sizeof(dbei); - dbei.pBlob = (iIndex == 6) ? (BYTE *)"is now offline (was online)" : (BYTE *)"The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."; - dbei.cbBlob = lstrlenA((char *)dbei.pBlob) + 1; - dbei.flags = (iIndex == 1 || iIndex == 3 || iIndex == 5) ? DBEF_SENT : 0; - dbei.flags |= (teInfo->rtl ? DBEF_RTL : 0); - dat->lastEventTime = (iIndex == 4 || iIndex == 5) ? time(NULL) - 1 : 0; - dat->iLastEventType = MAKELONG(dbei.flags, dbei.eventType); - SetWindowText(GetDlgItem(hwndDlg, IDC_PREVIEW), _T("")); - dat->dwFlags = MWF_LOG_ALL; - dat->dwFlags = (teInfo->rtl ? dat->dwFlags | MWF_LOG_RTL : dat->dwFlags & ~MWF_LOG_RTL); - dat->dwFlags = (iIndex == 0 || iIndex == 1) ? dat->dwFlags & ~MWF_LOG_GROUPMODE : dat->dwFlags | MWF_LOG_GROUPMODE; - mir_sntprintf(dat->szMyNickname, SIZEOF(dat->szMyNickname), _T("My Nickname")); - StreamInEvents(hwndDlg, 0, 1, 1, &dbei); - SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_SETSEL, -1, -1); - if (teInfo->changed) - CopyMemory(tSet->szTemplates[teInfo->inEdit], szTemp, TEMPLATE_LENGTH * sizeof(TCHAR)); - break; - } - case WM_DESTROY: - Utils::enableDlgControl(teInfo->hwndParent, IDC_MODIFY, TRUE); - Utils::enableDlgControl(teInfo->hwndParent, IDC_RTLMODIFY, TRUE); - if (dat->pContainer) - mir_free(dat->pContainer); - if (dat) - mir_free(dat); - - db_set_dw(0, SRMSGMOD_T, "cc1", SendDlgItemMessage(hwndDlg, IDC_COLOR1, CPM_GETCOLOUR, 0, 0)); - db_set_dw(0, SRMSGMOD_T, "cc2", SendDlgItemMessage(hwndDlg, IDC_COLOR2, CPM_GETCOLOUR, 0, 0)); - db_set_dw(0, SRMSGMOD_T, "cc3", SendDlgItemMessage(hwndDlg, IDC_COLOR3, CPM_GETCOLOUR, 0, 0)); - db_set_dw(0, SRMSGMOD_T, "cc4", SendDlgItemMessage(hwndDlg, IDC_COLOR4, CPM_GETCOLOUR, 0, 0)); - db_set_dw(0, SRMSGMOD_T, "cc5", SendDlgItemMessage(hwndDlg, IDC_COLOR5, CPM_GETCOLOUR, 0, 0)); - - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} +/* + * astyle --force-indent=tab=4 --brackets=linux --indent-switches + * --pad=oper --one-line=keep-blocks --unpad=paren + * + * Miranda NG: the free IM client for Microsoft* Windows* + * + * 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-2009 by silvercircle _at_ gmail _dot_ com and contributors + * + * Simple editor for the message log templates + * + */ + +#include "commonheaders.h" + +/* +* hardcoded default set of templates for both LTR and RTL. +* cannot be changed and may be used at any time to "revert" to a working layout +*/ + +char *TemplateNames[] = { + "Message In", + "Message Out", + "Group In (Start)", + "Group Out (Start)", + "Group In (Inner)", + "Group Out (Inner)", + "Status change", + "Error message" +}; + +TTemplateSet LTR_Default = { TRUE, + _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), + _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), + _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), + _T("%I %S %N %?&D%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M"), + _T("%S %T%|%M"), + _T("%S %T%|%M"), + _T("%I %S %&D, %&T, %N %M%! "), + _T("%I%S %D, %T, %e%l%M"), + "Default LTR" + }; + +TTemplateSet RTL_Default = { TRUE, + _T("%I %S %N %D%n%S %T%|%M"), + _T("%I %S %N %D%n%S %T%|%M"), + _T("%I %S %N %D%n%S %T%|%M"), + _T("%I %S %N %D%n%S %T%|%M"), + _T("%S %T%|%M"), + _T("%S %T%|%M"), + _T("%I%S %D, %T, %N %M%! "), + _T("%I%S %D, %T, %e%l%M"), + "Default RTL" + }; + +TTemplateSet LTR_Active, RTL_Active; +static int helpActive = 0; + + +/* +* loads template set overrides from hContact into the given set of already existing +* templates +*/ + +static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl) +{ + DBVARIANT dbv = {0}; + int i; + + for (i=0; i <= TMPL_ERRMSG; i++) { + if (db_get_ts(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv)) + continue; + if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) + mir_sntprintf(tSet->szTemplates[i], TEMPLATE_LENGTH, _T("%s"), dbv.ptszVal); + db_free(&dbv); + } +} + +void LoadDefaultTemplates() +{ + int i; + + LTR_Active = LTR_Default; + RTL_Active = RTL_Default; + + if (M.GetByte(RTLTEMPLATES_MODULE, "setup", 0) < 2) { + for (i=0; i <= TMPL_ERRMSG; i++) + db_set_ts(NULL, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]); + db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2); + } + if (M.GetByte(TEMPLATES_MODULE, "setup", 0) < 2) { + for (i=0; i <= TMPL_ERRMSG; i++) + db_set_ts(NULL, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]); + db_set_b(0, TEMPLATES_MODULE, "setup", 2); + } + LoadTemplatesFrom(<R_Active, 0, 0); + LoadTemplatesFrom(&RTL_Active, 0, 1); +} + +INT_PTR CALLBACK DlgProcTemplateEditor(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TWindowData *dat; + TemplateEditorInfo *teInfo = 0; + TTemplateSet *tSet; + int i; + dat = (TWindowData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + /* + * since this dialog needs a MessageWindowData * but has no container, we can store + * the extended info struct in pContainer *) + */ + if (dat) { + teInfo = (TemplateEditorInfo *)dat->pContainer; + tSet = teInfo->rtl ? dat->pContainer->rtl_templates : dat->pContainer->ltr_templates; + } + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + TemplateEditorNew *teNew = (TemplateEditorNew *)lParam; + COLORREF url_visited = RGB(128, 0, 128); + COLORREF url_unvisited = RGB(0, 0, 255); + dat = (TWindowData*)mir_calloc( sizeof(TWindowData)); + dat->pContainer = (TContainerData*)mir_alloc(sizeof(TContainerData)); + ZeroMemory(dat->pContainer, sizeof(TContainerData)); + teInfo = (TemplateEditorInfo *)dat->pContainer; + ZeroMemory(teInfo, sizeof(TemplateEditorInfo)); + teInfo->hContact = teNew->hContact; + teInfo->rtl = teNew->rtl; + teInfo->hwndParent = teNew->hwndParent; + + LoadOverrideTheme(dat->pContainer); + /* + * set hContact to the first found contact so that we can use the Preview window properly + * also, set other parameters needed by the streaming function to display events + */ + + SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_LINK); + SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); + SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_EXLIMITTEXT, 0, 0x80000000); + + dat->hContact = db_find_first(); + dat->szProto = GetContactProto(dat->hContact); + while(dat->szProto == 0 && dat->hContact != 0) { + dat->hContact = db_find_next(dat->hContact); + dat->szProto = GetContactProto(dat->hContact); + } + dat->dwFlags = dat->pContainer->theme.dwFlags; + + dat->cache = CContactCache::getContactCache(dat->hContact); + dat->cache->updateState(); + dat->cache->updateUIN(); + dat->cache->updateStats(TSessionStats::INIT_TIMER); + GetMYUIN(dat); + + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) dat); + ShowWindow(hwndDlg, SW_SHOW); + SendDlgItemMessage(hwndDlg, IDC_EDITTEMPLATE, EM_LIMITTEXT, (WPARAM)TEMPLATE_LENGTH - 1, 0); + SetWindowText(hwndDlg, TranslateT("Template Set Editor")); + Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); + for (i=0; i <= TMPL_ERRMSG; i++) { + SendDlgItemMessageA(hwndDlg, IDC_TEMPLATELIST, LB_ADDSTRING, 0, (LPARAM)Translate(TemplateNames[i])); + SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_SETITEMDATA, i, (LPARAM)i); + } + Utils::enableDlgControl(teInfo->hwndParent, IDC_MODIFY, FALSE); + Utils::enableDlgControl(teInfo->hwndParent, IDC_RTLMODIFY, FALSE); + + SendDlgItemMessage(hwndDlg, IDC_COLOR1, CPM_SETCOLOUR, 0, M.GetDword("cc1", SRMSGDEFSET_BKGCOLOUR)); + SendDlgItemMessage(hwndDlg, IDC_COLOR2, CPM_SETCOLOUR, 0, M.GetDword("cc2", SRMSGDEFSET_BKGCOLOUR)); + SendDlgItemMessage(hwndDlg, IDC_COLOR3, CPM_SETCOLOUR, 0, M.GetDword("cc3", SRMSGDEFSET_BKGCOLOUR)); + SendDlgItemMessage(hwndDlg, IDC_COLOR4, CPM_SETCOLOUR, 0, M.GetDword("cc4", SRMSGDEFSET_BKGCOLOUR)); + SendDlgItemMessage(hwndDlg, IDC_COLOR5, CPM_SETCOLOUR, 0, M.GetDword("cc5", SRMSGDEFSET_BKGCOLOUR)); + SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + + case IDC_RESETALLTEMPLATES: + if (MessageBox(0, TranslateT("This will reset the template set to the default built-in templates. Are you sure you want to do this?"), + TranslateT("Template Set Editor"), MB_YESNO | MB_ICONQUESTION) == IDYES) { + db_set_b(0, teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, "setup", 0); + LoadDefaultTemplates(); + MessageBox(0, TranslateT("Template set was successfully reset, please close and reopen all message windows. This template editor window will now close."), + TranslateT("Template Set Editor"), MB_OK); + DestroyWindow(hwndDlg); + } + break; + + case IDC_TEMPLATELIST: + switch (HIWORD(wParam)) { + LRESULT iIndex; + case LBN_DBLCLK: + iIndex = SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_GETCURSEL, 0, 0); + if (iIndex != LB_ERR) { + SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[iIndex]); + teInfo->inEdit = iIndex; + teInfo->changed = FALSE; + teInfo->selchanging = FALSE; + SetFocus(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE)); + SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, FALSE, 0); + } + break; + + case LBN_SELCHANGE: + iIndex = SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_GETCURSEL, 0, 0); + teInfo->selchanging = TRUE; + if (iIndex != LB_ERR) { + SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[iIndex]); + teInfo->inEdit = iIndex; + teInfo->changed = FALSE; + } + SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); + } + break; + + case IDC_VARIABLESHELP: + CallService(MS_UTILS_OPENURL, 0, (LPARAM)"http://wiki.miranda.or.at/TabSRMM/Templates"); + break; + + case IDC_EDITTEMPLATE: + if (HIWORD(wParam) == EN_CHANGE) { + if (!teInfo->selchanging) { + teInfo->changed = TRUE; + teInfo->updateInfo[teInfo->inEdit] = TRUE; + Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_FORGET, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_REVERT, TRUE); + } + InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); + } + break; + case IDC_SAVETEMPLATE: + { + TCHAR newTemplate[TEMPLATE_LENGTH + 2]; + + GetWindowText(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), newTemplate, TEMPLATE_LENGTH); + CopyMemory(tSet->szTemplates[teInfo->inEdit], newTemplate, sizeof(TCHAR) * TEMPLATE_LENGTH); + teInfo->changed = FALSE; + teInfo->updateInfo[teInfo->inEdit] = FALSE; + Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); + InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); + db_set_ts(teInfo->hContact, teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[teInfo->inEdit], newTemplate); + SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); + } + break; + + case IDC_FORGET: + teInfo->changed = FALSE; + teInfo->updateInfo[teInfo->inEdit] = FALSE; + teInfo->selchanging = TRUE; + SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[teInfo->inEdit]); + SetFocus(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE)); + InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, TRUE); + Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); + teInfo->selchanging = FALSE; + SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); + break; + + case IDC_REVERT: + teInfo->changed = FALSE; + teInfo->updateInfo[teInfo->inEdit] = FALSE; + teInfo->selchanging = TRUE; + CopyMemory(tSet->szTemplates[teInfo->inEdit], LTR_Default.szTemplates[teInfo->inEdit], sizeof(TCHAR) * TEMPLATE_LENGTH); + SetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[teInfo->inEdit]); + db_unset(teInfo->hContact, teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[teInfo->inEdit]); + SetFocus(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE)); + InvalidateRect(GetDlgItem(hwndDlg, IDC_TEMPLATELIST), NULL, FALSE); + teInfo->selchanging = FALSE; + Utils::enableDlgControl(hwndDlg, IDC_SAVETEMPLATE, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_REVERT, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_FORGET, FALSE); + Utils::enableDlgControl(hwndDlg, IDC_TEMPLATELIST, TRUE); + SendMessage(GetDlgItem(hwndDlg, IDC_EDITTEMPLATE), EM_SETREADONLY, TRUE, 0); + break; + + case IDC_UPDATEPREVIEW: + SendMessage(hwndDlg, DM_UPDATETEMPLATEPREVIEW, 0, 0); + break; + } + break; + + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *) lParam; + int iItem = dis->itemData; + HBRUSH bkg, oldBkg; + SetBkMode(dis->hDC, TRANSPARENT); + FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW)); + if (dis->itemState & ODS_SELECTED) { + if (teInfo->updateInfo[iItem] == TRUE) { + bkg = CreateSolidBrush(RGB(255, 0, 0)); + oldBkg = (HBRUSH)SelectObject(dis->hDC, bkg); + FillRect(dis->hDC, &dis->rcItem, bkg); + SelectObject(dis->hDC, oldBkg); + DeleteObject(bkg); + } else + FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT)); + + SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } else { + if (teInfo->updateInfo[iItem] == TRUE) + SetTextColor(dis->hDC, RGB(255, 0, 0)); + else + SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT)); + } + char *pszName = Translate(TemplateNames[iItem]); + TextOutA(dis->hDC, dis->rcItem.left, dis->rcItem.top, pszName, lstrlenA(pszName)); + } + return TRUE; + + case DM_UPDATETEMPLATEPREVIEW: { + DBEVENTINFO dbei = {0}; + int iIndex = SendDlgItemMessage(hwndDlg, IDC_TEMPLATELIST, LB_GETCURSEL, 0, 0); + TCHAR szTemp[TEMPLATE_LENGTH + 2]; + + if (teInfo->changed) { + CopyMemory(szTemp, tSet->szTemplates[teInfo->inEdit], TEMPLATE_LENGTH * sizeof(TCHAR)); + GetDlgItemText(hwndDlg, IDC_EDITTEMPLATE, tSet->szTemplates[teInfo->inEdit], TEMPLATE_LENGTH); + } + dbei.szModule = dat->szProto; + dbei.timestamp = time(NULL); + dbei.eventType = (iIndex == 6) ? EVENTTYPE_STATUSCHANGE : EVENTTYPE_MESSAGE; + dbei.eventType = (iIndex == 7) ? EVENTTYPE_ERRMSG : dbei.eventType; + if (dbei.eventType == EVENTTYPE_ERRMSG) + dbei.szModule = "Sample error message"; + dbei.cbSize = sizeof(dbei); + dbei.pBlob = (iIndex == 6) ? (BYTE *)"is now offline (was online)" : (BYTE *)"The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."; + dbei.cbBlob = lstrlenA((char *)dbei.pBlob) + 1; + dbei.flags = (iIndex == 1 || iIndex == 3 || iIndex == 5) ? DBEF_SENT : 0; + dbei.flags |= (teInfo->rtl ? DBEF_RTL : 0); + dat->lastEventTime = (iIndex == 4 || iIndex == 5) ? time(NULL) - 1 : 0; + dat->iLastEventType = MAKELONG(dbei.flags, dbei.eventType); + SetWindowText(GetDlgItem(hwndDlg, IDC_PREVIEW), _T("")); + dat->dwFlags = MWF_LOG_ALL; + dat->dwFlags = (teInfo->rtl ? dat->dwFlags | MWF_LOG_RTL : dat->dwFlags & ~MWF_LOG_RTL); + dat->dwFlags = (iIndex == 0 || iIndex == 1) ? dat->dwFlags & ~MWF_LOG_GROUPMODE : dat->dwFlags | MWF_LOG_GROUPMODE; + mir_sntprintf(dat->szMyNickname, SIZEOF(dat->szMyNickname), _T("My Nickname")); + StreamInEvents(hwndDlg, 0, 1, 1, &dbei); + SendDlgItemMessage(hwndDlg, IDC_PREVIEW, EM_SETSEL, -1, -1); + if (teInfo->changed) + CopyMemory(tSet->szTemplates[teInfo->inEdit], szTemp, TEMPLATE_LENGTH * sizeof(TCHAR)); + break; + } + case WM_DESTROY: + Utils::enableDlgControl(teInfo->hwndParent, IDC_MODIFY, TRUE); + Utils::enableDlgControl(teInfo->hwndParent, IDC_RTLMODIFY, TRUE); + if (dat) { + mir_free(dat->pContainer); + mir_free(dat); + } + db_set_dw(0, SRMSGMOD_T, "cc1", SendDlgItemMessage(hwndDlg, IDC_COLOR1, CPM_GETCOLOUR, 0, 0)); + db_set_dw(0, SRMSGMOD_T, "cc2", SendDlgItemMessage(hwndDlg, IDC_COLOR2, CPM_GETCOLOUR, 0, 0)); + db_set_dw(0, SRMSGMOD_T, "cc3", SendDlgItemMessage(hwndDlg, IDC_COLOR3, CPM_GETCOLOUR, 0, 0)); + db_set_dw(0, SRMSGMOD_T, "cc4", SendDlgItemMessage(hwndDlg, IDC_COLOR4, CPM_GETCOLOUR, 0, 0)); + db_set_dw(0, SRMSGMOD_T, "cc5", SendDlgItemMessage(hwndDlg, IDC_COLOR5, CPM_GETCOLOUR, 0, 0)); + + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + break; + } + return FALSE; +} diff --git a/plugins/TabSRMM/src/themes.cpp b/plugins/TabSRMM/src/themes.cpp index 8dbc963588..2653c4cb16 100644 --- a/plugins/TabSRMM/src/themes.cpp +++ b/plugins/TabSRMM/src/themes.cpp @@ -1,2557 +1,2557 @@ -/* - * Miranda NG: the free IM client for Microsoft* Windows* - * - * 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 - * - * Implements the skinning engine and most parts of the aero support in - * tabSRMM 3.x+ - * - */ - -#include "commonheaders.h" - -static SKINDESC my_default_skin[] = { - IDR_SKIN_AERO, _T("tabskin_aero.png"), - IDR_SKIN_AERO_GLOW, _T("tabskin_aero_glow.png"), - IDR_SKIN_AERO_SWITCHBAR, _T("tabskin_aero_button.png"), - IDR_SKIN_LOGO, _T("unknown.png") -}; - -CSkin* Skin = 0; - - -static void __inline gradientVertical(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, - ULONG ulBitmapHeight, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, - UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 y, UCHAR *ubAlpha); - -static void __inline gradientHorizontal(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, - ULONG ulBitmapWidth, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, - UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 x, UCHAR *ubAlpha); - - -UINT nextButtonID; -ButtonSet g_ButtonSet = {0}; - -#define NR_MAXSKINICONS 100 - -/* - * initialize static class data -*/ - -int CSkin::m_bAvatarBorderType = 0; -COLORREF CSkin::m_avatarBorderClr = 0; -COLORREF CSkin::m_tmp_tb_low = 0, CSkin::m_tmp_tb_high = 0; -COLORREF CSkin::m_sideBarContainerBG; -BLENDFUNCTION CSkin::m_default_bf = {0}; // a global blend function, used in various places - // when you use it, reset SourceConstantAlpha to 255 and - // the blending mode to AC_SRC_ALPHA. - -bool CSkin::m_bClipBorder = false, CSkin::m_DisableScrollbars = false, - CSkin::m_skinEnabled = false, CSkin::m_frameSkins = false; - -char CSkin::m_SkinnedFrame_left = 0, CSkin::m_SkinnedFrame_right = 0, - CSkin::m_SkinnedFrame_bottom = 0, CSkin::m_SkinnedFrame_caption = 0; - -char CSkin::m_realSkinnedFrame_left = 0; -char CSkin::m_realSkinnedFrame_right = 0; -char CSkin::m_realSkinnedFrame_bottom = 0; -char CSkin::m_realSkinnedFrame_caption = 0; - -int CSkin::m_titleBarLeftOff = 0, CSkin::m_titleButtonTopOff = 0, CSkin::m_captionOffset = 0, CSkin::m_captionPadding = 0, - CSkin::m_titleBarRightOff = 0, CSkin::m_sidebarTopOffset = 0, CSkin::m_sidebarBottomOffset = 0, CSkin::m_bRoundedCorner = 0; - -CImageItem* CSkin::m_switchBarItem = 0, - *CSkin::m_tabTop = 0, - *CSkin::m_tabBottom = 0, - *CSkin::m_tabGlowTop = 0, - *CSkin::m_tabGlowBottom = 0; - -bool CSkin::m_fAeroSkinsValid = false; - -SIZE CSkin::m_titleBarButtonSize = {0}; - -COLORREF CSkin::m_ContainerColorKey = 0, CSkin::m_dwmColorRGB = 0, CSkin::m_DefaultFontColor = 0; -HBRUSH CSkin::m_ContainerColorKeyBrush = 0, CSkin::m_MenuBGBrush = 0; - -HPEN CSkin::m_SkinLightShadowPen = 0, CSkin::m_SkinDarkShadowPen = 0; - -HICON CSkin::m_closeIcon = 0, CSkin::m_maxIcon = 0, CSkin::m_minIcon = 0; - -UINT CSkin::m_aeroEffect = 0; -DWORD CSkin::m_glowSize = 0; -HBRUSH CSkin::m_BrushBack = 0, CSkin::m_BrushFill = 0; - -HBITMAP CSkin::m_tabCloseBitmap = 0, CSkin::m_tabCloseOldBitmap = 0; -HDC CSkin::m_tabCloseHDC = 0; - -/* - * aero effects - */ - -AeroEffect CSkin::m_currentAeroEffect; -AeroEffect* CSkin::m_pCurrentAeroEffect = 0; - -AeroEffect CSkin::m_aeroEffects[AERO_EFFECT_LAST] = { - { - LPGENT("No effect"), 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0 - }, - { - LPGENT("Milky Glass"), - 0xf5f5f5, /* base color */ - 0xaaaaaa, /* gradient color */ - 70, /* base alpha */ - 0, /* final alpha */ - CORNER_ALL, /* corner type */ - GRADIENT_TB + 1, /* gradient type */ - 8, /* corner radius */ - 8, /* glow size (0 means no glowing text, colors can be used) */ - 0, /* background color (black = transparency) */ - 0xf0f0f0, /* toolbar first color (if 0, use custom gradient color) */ - 0, /* toolbar 2nd gradient color (0 = use aero theme color, -1 = use custom gradient color */ - AeroEffectCallback_Milk /* callback function to render the effect */ - }, - { - LPGENT("Carbon"), - 0xf0f0f0, - 0x000000, - 75, - 0, - CORNER_ALL, - GRADIENT_TB + 1, - 6, - 8, - 0, - 0xf0f0f0, - 0, - AeroEffectCallback_Carbon - }, - { - LPGENT("Semi transparent, custom colors"), - 0xffffff, - 0x444444, - 60, - 0, - CORNER_ALL, - GRADIENT_TB + 1, - 6, - 0, - 0x0, - 0xf0f0f0, - 0, - AeroEffectCallback_Solid - }, - { - LPGENT("Silver shadow"), - 0xffffff, - 0xa0a0a0, - 80, - 220, - 0, - GRADIENT_TB + 1, - 1, - 0, - 0xc0c0c0, - 0xf0f0f0, - 0x707070, - AeroEffectCallback_Solid - }, - { - LPGENT("Custom (use own gradient colors)"), - 0xffffff, - 0xa0a0a0, - 80, - 220, - 0, - GRADIENT_TB + 1, - 1, - 0, - 0xc0c0c0, - -1, - -1, - AeroEffectCallback_Solid - } -}; - -/* - * definition of the availbale skin items - */ - -CSkinItem SkinItems[] = { - {_T("Container"), "TSKIN_Container", ID_EXTBKCONTAINER, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Toolbar"), "TSKIN_Container", ID_EXTBKBUTTONBAR, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("{-}Buttonpressed"), "TSKIN_BUTTONSPRESSED", ID_EXTBKBUTTONSPRESSED, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Buttonnotpressed"), "TSKIN_BUTTONSNPRESSED", ID_EXTBKBUTTONSNPRESSED, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Buttonmouseover"), "TSKIN_BUTTONSMOUSEOVER", ID_EXTBKBUTTONSMOUSEOVER, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Infopanelfield"), "TSKIN_INFOPANELFIELD", ID_EXTBKINFOPANEL, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Titlebutton"), "TSKIN_TITLEBUTTON", ID_EXTBKTITLEBUTTON, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Titlebuttonmouseover"), "TSKIN_TITLEBUTTONHOVER", ID_EXTBKTITLEBUTTONMOUSEOVER, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Titlebuttonpressed"), "TSKIN_TITLEBUTTONPRESSED", ID_EXTBKTITLEBUTTONPRESSED, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabpage"), "TSKIN_TABPAGE", ID_EXTBKTABPAGE, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabitem"), "TSKIN_TABITEM", ID_EXTBKTABITEM, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabitem_active"), "TSKIN_TABITEMACTIVE", ID_EXTBKTABITEMACTIVE, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabitem_bottom"), "TSKIN_TABITEMBOTTOM", ID_EXTBKTABITEMBOTTOM, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabitem_active_bottom"), "TSKIN_TABITEMACTIVEBOTTOM", ID_EXTBKTABITEMACTIVEBOTTOM, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Frame"), "TSKIN_FRAME", ID_EXTBKFRAME, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("MessageLog"), "TSKIN_MLOG", ID_EXTBKHISTORY, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("InputArea"), "TSKIN_INPUT", ID_EXTBKINPUTAREA, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("FrameInactive"), "TSKIN_FRAMEINACTIVE", ID_EXTBKFRAMEINACTIVE, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabitem_hottrack"), "TSKIN_TABITEMHOTTRACK", ID_EXTBKTABITEMHOTTRACK, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Tabitem_hottrack_bottom"), "TSKIN_TABITEMHOTTRACKBOTTOM", ID_EXTBKTABITEMHOTTRACKBOTTOM, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Statusbarpanel"), "TSKIN_STATUSBARPANEL", ID_EXTBKSTATUSBARPANEL, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Statusbar"), "TSKIN_STATUSBAR", ID_EXTBKSTATUSBAR, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("Userlist"), "TSKIN_USERLIST", ID_EXTBKUSERLIST, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE - }, {_T("InfoPanelBackground"), "TSKIN_INFOPANELBG", ID_EXTBKINFOPANELBG, - 8, CLCDEFAULT_CORNER, - 0xf0f0f0, 0x42b1ff, 1, CLCDEFAULT_TEXTCOLOR, 40, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE, 0, 0 - }, {_T("Sidebar Background"), "TSKIN_SIDEBARBG", ID_EXTBKSIDEBARBG, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - 0xb2e1ff, 0xb2e1ff, 1, CLCDEFAULT_TEXTCOLOR, 40, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE, 0, 0 - } -}; - -static CSkinItem _defInfoPanel = { - _T("InfoPanelBackground"), "TSKIN_INFOPANELBG", ID_EXTBKINFOPANELBG, - 8, CLCDEFAULT_CORNER, - 0xf0f0f0, 0x62caff, 0, CLCDEFAULT_TEXTCOLOR, 255, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE, 0, 0 -}; - -static BYTE __inline percent_to_byte(UINT32 percent) -{ - return(BYTE)((FLOAT)(((FLOAT) percent) / 100) * 255); -} - -static COLORREF __inline revcolref(COLORREF colref) -{ - return RGB(GetBValue(colref), GetGValue(colref), GetRValue(colref)); -} - -static DWORD __inline argb_from_cola(COLORREF col, UINT32 alpha) -{ - return((BYTE) percent_to_byte(alpha) << 24 | col); -} - - -void TSAPI DrawAlpha(HDC hDC, PRECT rc, DWORD clr_base, int alpha, DWORD clr_dest, BYTE clr_dest_trans, BYTE bGradient, - BYTE bCorner, DWORD dwRadius, CImageItem *imageItem) -{ - HBRUSH BrMask; - HBRUSH holdbrush; - HDC hdc; - BLENDFUNCTION bf; - HBITMAP hbitmap; - HBITMAP holdbitmap; - BITMAPINFO bmi; - VOID *pvBits; - UINT32 x, y; - ULONG ulBitmapWidth, ulBitmapHeight; - UCHAR ubAlpha = 0xFF; - UCHAR ubRedFinal = 0xFF; - UCHAR ubGreenFinal = 0xFF; - UCHAR ubBlueFinal = 0xFF; - UCHAR ubRed; - UCHAR ubGreen; - UCHAR ubBlue; - UCHAR ubRed2; - UCHAR ubGreen2; - UCHAR ubBlue2; - - int realx; - - FLOAT fAlphaFactor; - LONG realHeight = (rc->bottom - rc->top); - LONG realWidth = (rc->right - rc->left); - LONG realHeightHalf = realHeight >> 1; - - if (rc == NULL) - return; - - if (imageItem) { - imageItem->Render(hDC, rc, false); - return; - } - - if (rc->right < rc->left || rc->bottom < rc->top || (realHeight <= 0) || (realWidth <= 0)) - return; - - /* - * use GDI fast gradient drawing when no corner radi exist - */ - - if (bCorner == 0 && dwRadius == 0) { - GRADIENT_RECT grect; - TRIVERTEX tvtx[2]; - int orig = 1, dest = 0; - - if (bGradient & GRADIENT_LR || bGradient & GRADIENT_TB) { - orig = 0; - dest = 1; - } - - tvtx[0].x = rc->left; - tvtx[0].y = rc->top; - tvtx[1].x = rc->right; - tvtx[1].y = rc->bottom; - - tvtx[orig].Red = (COLOR16)GetRValue(clr_base) << 8; - tvtx[orig].Blue = (COLOR16)GetBValue(clr_base) << 8; - tvtx[orig].Green = (COLOR16)GetGValue(clr_base) << 8; - tvtx[orig].Alpha = (COLOR16) alpha << 8; - - tvtx[dest].Red = (COLOR16)GetRValue(clr_dest) << 8; - tvtx[dest].Blue = (COLOR16)GetBValue(clr_dest) << 8; - tvtx[dest].Green = (COLOR16)GetGValue(clr_dest) << 8; - tvtx[dest].Alpha = (COLOR16) alpha << 8; - - grect.UpperLeft = 0; - grect.LowerRight = 1; - - GdiGradientFill(hDC, tvtx, 2, &grect, 1, (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H); - return; - } - - hdc = CreateCompatibleDC(hDC); - if (!hdc) - return; - - ZeroMemory(&bmi, sizeof(BITMAPINFO)); - - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - - if (bGradient & GRADIENT_ACTIVE && (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL)) { - bmi.bmiHeader.biWidth = ulBitmapWidth = realWidth; - bmi.bmiHeader.biHeight = ulBitmapHeight = 1; - } else if (bGradient & GRADIENT_ACTIVE && (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT)) { - bmi.bmiHeader.biWidth = ulBitmapWidth = 1; - bmi.bmiHeader.biHeight = ulBitmapHeight = realHeight; - } else { - bmi.bmiHeader.biWidth = ulBitmapWidth = 1; - bmi.bmiHeader.biHeight = ulBitmapHeight = 1; - } - - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = ulBitmapWidth * ulBitmapHeight * 4; - - if (ulBitmapWidth <= 0 || ulBitmapHeight <= 0) - return; - - hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0); - if (hbitmap == NULL || pvBits == NULL) { - DeleteDC(hdc); - return; - } - - holdbitmap = (HBITMAP)SelectObject(hdc, hbitmap); - - // convert basecolor to RGB and then merge alpha so its ARGB - clr_base = argb_from_cola(revcolref(clr_base), alpha); - clr_dest = argb_from_cola(revcolref(clr_dest), alpha); - - ubRed = (UCHAR)(clr_base >> 16); - ubGreen = (UCHAR)(clr_base >> 8); - ubBlue = (UCHAR) clr_base; - - ubRed2 = (UCHAR)(clr_dest >> 16); - ubGreen2 = (UCHAR)(clr_dest >> 8); - ubBlue2 = (UCHAR) clr_dest; - - //DRAW BASE - make corner space 100% transparent - for (y = 0; y < ulBitmapHeight; y++) { - for (x = 0 ; x < ulBitmapWidth ; x++) { - if (bGradient & GRADIENT_ACTIVE) { - if (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL) { - realx = x + realHeightHalf; - realx = (ULONG) realx > ulBitmapWidth ? ulBitmapWidth : realx; - gradientHorizontal(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapWidth, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, realx, &ubAlpha); - } else if (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) - gradientVertical(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapHeight, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, y, &ubAlpha); - - fAlphaFactor = (float) ubAlpha / (float) 0xff; - ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); - } else { - ubAlpha = percent_to_byte(alpha); - ubRedFinal = ubRed; - ubGreenFinal = ubGreen; - ubBlueFinal = ubBlue; - fAlphaFactor = (float) ubAlpha / (float) 0xff; - - ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); - } - } - } - bf.BlendOp = AC_SRC_OVER; - bf.BlendFlags = 0; - bf.SourceConstantAlpha = (UCHAR)(clr_base >> 24); - bf.AlphaFormat = AC_SRC_ALPHA; // so it will use our specified alpha value - - GdiAlphaBlend(hDC, rc->left + realHeightHalf, rc->top, (realWidth - realHeightHalf * 2), realHeight, hdc, 0, 0, ulBitmapWidth, ulBitmapHeight, bf); - - SelectObject(hdc, holdbitmap); - DeleteObject(hbitmap); - - // corners - BrMask = CreateSolidBrush(RGB(0xFF, 0x00, 0xFF)); - { - bmi.bmiHeader.biWidth = ulBitmapWidth = realHeightHalf; - bmi.bmiHeader.biHeight = ulBitmapHeight = realHeight; - bmi.bmiHeader.biSizeImage = ulBitmapWidth * ulBitmapHeight * 4; - - if (ulBitmapWidth <= 0 || ulBitmapHeight <= 0) { - DeleteDC(hdc); - DeleteObject(BrMask); - return; - } - - // TL+BL CORNER - hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0); - - if (hbitmap == 0 || pvBits == NULL) { - DeleteObject(BrMask); - DeleteDC(hdc); - return; - } - - holdbrush = (HBRUSH)SelectObject(hdc, BrMask); - holdbitmap = (HBITMAP)SelectObject(hdc, hbitmap); - RoundRect(hdc, -1, -1, ulBitmapWidth * 2 + 1, (realHeight + 1), dwRadius, dwRadius); - - for (y = 0; y < ulBitmapHeight; y++) { - for (x = 0; x < ulBitmapWidth; x++) { - if (((((UINT32 *) pvBits)[x + y * ulBitmapWidth]) << 8) == 0xFF00FF00 || (y < ulBitmapHeight >> 1 && !(bCorner & CORNER_BL && bCorner & CORNER_ACTIVE)) || (y > ulBitmapHeight >> 2 && !(bCorner & CORNER_TL && bCorner & CORNER_ACTIVE))) { - if (bGradient & GRADIENT_ACTIVE) { - if (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL) - gradientHorizontal(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, realWidth, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, x, &ubAlpha); - else if (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) - gradientVertical(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapHeight, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, y, &ubAlpha); - - fAlphaFactor = (float) ubAlpha / (float) 0xff; - ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); - } else { - ubAlpha = percent_to_byte(alpha); - ubRedFinal = ubRed; - ubGreenFinal = ubGreen; - ubBlueFinal = ubBlue; - fAlphaFactor = (float) ubAlpha / (float) 0xff; - - ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); - } - } - } - } - GdiAlphaBlend(hDC, rc->left, rc->top, ulBitmapWidth, ulBitmapHeight, hdc, 0, 0, ulBitmapWidth, ulBitmapHeight, bf); - SelectObject(hdc, holdbitmap); - DeleteObject(hbitmap); - - // TR+BR CORNER - hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0); - - //SelectObject(hdc, BrMask); // already BrMask? - holdbitmap = (HBITMAP)SelectObject(hdc, hbitmap); - RoundRect(hdc, -1 - ulBitmapWidth, -1, ulBitmapWidth + 1, (realHeight + 1), dwRadius, dwRadius); - - for (y = 0; y < ulBitmapHeight; y++) { - for (x = 0; x < ulBitmapWidth; x++) { - if (((((UINT32 *) pvBits)[x + y * ulBitmapWidth]) << 8) == 0xFF00FF00 || (y < ulBitmapHeight >> 1 && !(bCorner & CORNER_BR && bCorner & CORNER_ACTIVE)) || (y > ulBitmapHeight >> 1 && !(bCorner & CORNER_TR && bCorner & CORNER_ACTIVE))) { - if (bGradient & GRADIENT_ACTIVE) { - if (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL) { - realx = x + realWidth; - realx = realx > realWidth ? realWidth : realx; - gradientHorizontal(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, realWidth, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, realx, &ubAlpha); - } else if (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) - gradientVertical(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapHeight, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, y, &ubAlpha); - - fAlphaFactor = (float) ubAlpha / (float) 0xff; - ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); - } else { - ubAlpha = percent_to_byte(alpha); - ubRedFinal = ubRed; - ubGreenFinal = ubGreen; - ubBlueFinal = ubBlue; - fAlphaFactor = (float) ubAlpha / (float) 0xff; - - ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); - } - } - } - } - GdiAlphaBlend(hDC, rc->right - realHeightHalf, rc->top, ulBitmapWidth, ulBitmapHeight, hdc, 0, 0, ulBitmapWidth, ulBitmapHeight, bf); - } - SelectObject(hdc, holdbitmap); - DeleteObject(hbitmap); - SelectObject(hdc, holdbrush); - DeleteObject(BrMask); - DeleteDC(hdc); -} - -void __inline gradientHorizontal(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, ULONG ulBitmapWidth, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 x, UCHAR *ubAlpha) -{ - FLOAT fSolidMulti, fInvSolidMulti; - - // solid to transparent - if (transparent) { - *ubAlpha = (UCHAR)((float) x / (float) ulBitmapWidth * 255); - *ubAlpha = FLG_GRADIENT & GRADIENT_LR ? 0xFF - (*ubAlpha) : (*ubAlpha); - *ubRedFinal = ubRed; - *ubGreenFinal = ubGreen; - *ubBlueFinal = ubBlue; - } else { // solid to solid2 - if (FLG_GRADIENT & GRADIENT_LR) { - fSolidMulti = ((float) x / (float) ulBitmapWidth); - fInvSolidMulti = 1 - fSolidMulti; - } else { - fInvSolidMulti = ((float) x / (float) ulBitmapWidth); - fSolidMulti = 1 - fInvSolidMulti; - } - - *ubRedFinal = (UCHAR)((((float) ubRed * (float) fInvSolidMulti)) + (((float) ubRed2 * (float) fSolidMulti))); - *ubGreenFinal = (UCHAR)((UCHAR)(((float) ubGreen * (float) fInvSolidMulti)) + (((float) ubGreen2 * (float) fSolidMulti))); - *ubBlueFinal = (UCHAR)((((float) ubBlue * (float) fInvSolidMulti)) + (UCHAR)(((float) ubBlue2 * (float) fSolidMulti))); - - *ubAlpha = 0xFF; - } -} - -void __inline gradientVertical(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, ULONG ulBitmapHeight, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 y, UCHAR *ubAlpha) -{ - FLOAT fSolidMulti, fInvSolidMulti; - - // solid to transparent - if (transparent) { - *ubAlpha = (UCHAR)((float) y / (float) ulBitmapHeight * 255); - *ubAlpha = FLG_GRADIENT & GRADIENT_BT ? 0xFF - *ubAlpha : *ubAlpha; - *ubRedFinal = ubRed; - *ubGreenFinal = ubGreen; - *ubBlueFinal = ubBlue; - } else { // solid to solid2 - if (FLG_GRADIENT & GRADIENT_BT) { - fSolidMulti = ((float) y / (float) ulBitmapHeight); - fInvSolidMulti = 1 - fSolidMulti; - } else { - fInvSolidMulti = ((float) y / (float) ulBitmapHeight); - fSolidMulti = 1 - fInvSolidMulti; - } - - *ubRedFinal = (UCHAR)((((float) ubRed * (float) fInvSolidMulti)) + (((float) ubRed2 * (float) fSolidMulti))); - *ubGreenFinal = (UCHAR)((((float) ubGreen * (float) fInvSolidMulti)) + (((float) ubGreen2 * (float) fSolidMulti))); - *ubBlueFinal = (UCHAR)(((float) ubBlue * (float) fInvSolidMulti)) + (UCHAR)(((float) ubBlue2 * (float) fSolidMulti)); - - *ubAlpha = 0xFF; - } -} - -/** - * Renders the image item to the given target device context and rectangle - * - * @param hdc HDC: target device context - * @param rc RECT *: client rectangle inside the target DC. - * @param fIgnoreGlyph: bool: will ignore any glyph item. Set it to true when - * using this function from _outside_ a skin - */ -void __fastcall CImageItem::Render(const HDC hdc, const RECT *rc, bool fIgnoreGlyph) const -{ - BYTE l = m_bLeft, r = m_bRight, t = m_bTop, b = m_bBottom; - LONG width = rc->right - rc->left; - LONG height = rc->bottom - rc->top; - BOOL isGlyph = ((m_dwFlags & IMAGE_GLYPH) && Skin->haveGlyphItem()); - BOOL fCleanUp = TRUE; - HDC hdcSrc = 0; - HBITMAP hbmOld; - LONG srcOrigX = isGlyph ? m_glyphMetrics[0] : 0; - LONG srcOrigY = isGlyph ? m_glyphMetrics[1] : 0; - - if (m_hdc == 0) { - hdcSrc = CreateCompatibleDC(hdc); - hbmOld = (HBITMAP)SelectObject(hdcSrc, isGlyph ? Skin->getGlyphItem()->getHbm() : m_hbm); - } else { - if (fIgnoreGlyph) - hdcSrc = m_hdc; - else - hdcSrc = isGlyph ? Skin->getGlyphItem()->getDC() : m_hdc; - fCleanUp = FALSE; - } - - if (m_dwFlags & IMAGE_FLAG_DIVIDED) { - // top 3 items - - GdiAlphaBlend(hdc, rc->left, rc->top, l, t, hdcSrc, srcOrigX, srcOrigY, l, t, m_bf); - GdiAlphaBlend(hdc, rc->left + l, rc->top, width - l - r, t, hdcSrc, srcOrigX + l, srcOrigY, m_inner_width, t, m_bf); - GdiAlphaBlend(hdc, rc->right - r, rc->top, r, t, hdcSrc, srcOrigX + (m_width - r), srcOrigY, r, t, m_bf); - - // middle 3 items - - GdiAlphaBlend(hdc, rc->left, rc->top + t, l, height - t - b, hdcSrc, srcOrigX, srcOrigY + t, l, m_inner_height, m_bf); - - if ((m_dwFlags & IMAGE_FILLSOLID) && m_fillBrush) { - RECT rcFill; - rcFill.left = rc->left + l; - rcFill.top = rc->top + t; - rcFill.right = rc->right - r; - rcFill.bottom = rc->bottom - b; - FillRect(hdc, &rcFill, m_fillBrush); - } else - GdiAlphaBlend(hdc, rc->left + l, rc->top + t, width - l - r, height - t - b, hdcSrc, srcOrigX + l, srcOrigY + t, m_inner_width, m_inner_height, m_bf); - - GdiAlphaBlend(hdc, rc->right - r, rc->top + t, r, height - t - b, hdcSrc, srcOrigX + (m_width - r), srcOrigY + t, r, m_inner_height, m_bf); - - // bottom 3 items - - GdiAlphaBlend(hdc, rc->left, rc->bottom - b, l, b, hdcSrc, srcOrigX, srcOrigY + (m_height - b), l, b, m_bf); - GdiAlphaBlend(hdc, rc->left + l, rc->bottom - b, width - l - r, b, hdcSrc, srcOrigX + l, srcOrigY + (m_height - b), m_inner_width, b, m_bf); - GdiAlphaBlend(hdc, rc->right - r, rc->bottom - b, r, b, hdcSrc, srcOrigX + (m_width - r), srcOrigY + (m_height - b), r, b, m_bf); - } - else switch (m_bStretch) { - case IMAGE_STRETCH_H: - // tile image vertically, stretch to width - { - LONG top = rc->top; - - do { - if (top + m_height <= rc->bottom) { - GdiAlphaBlend(hdc, rc->left, top, width, m_height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf); - top += m_height; - } else { - GdiAlphaBlend(hdc, rc->left, top, width, rc->bottom - top, hdcSrc, srcOrigX, srcOrigY, m_width, rc->bottom - top, m_bf); - break; - } - } while (true); - break; - } - case IMAGE_STRETCH_V: - // tile horizontally, stretch to height - { - LONG left = rc->left; - - do { - if (left + m_width <= rc->right) { - GdiAlphaBlend(hdc, left, rc->top, m_width, height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf); - left += m_width; - } else { - GdiAlphaBlend(hdc, left, rc->top, rc->right - left, height, hdcSrc, srcOrigX, srcOrigY, rc->right - left, m_height, m_bf); - break; - } - } while (true); - break; - } - case IMAGE_STRETCH_B: - // stretch the image in both directions... - GdiAlphaBlend(hdc, rc->left, rc->top, width, height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf); - break; - } - - if (fCleanUp) { - SelectObject(hdcSrc, hbmOld); - DeleteDC(hdcSrc); - } -} - -static CSkinItem StatusItem_Default = { - _T("Container"), "EXBK_Offline", ID_EXTBKCONTAINER, - CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, - CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, - CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE -}; - -static struct { - TCHAR *szIniKey, *szIniName; - char *szSetting; - unsigned int size; - int defaultval; -} _tagSettings[] = { - _T("Global"), _T("SbarHeight"), "S_sbarheight", 1, 22, - _T("ClientArea"), _T("Left"), "S_tborder_outer_left", 1, 0, - _T("ClientArea"), _T("Right"), "S_tborder_outer_right", 1, 0, - _T("ClientArea"), _T("Top"), "S_tborder_outer_top", 1, 0, - _T("ClientArea"), _T("Bottom"), "S_tborder_outer_bottom", 1, 0, - _T("ClientArea"), _T("Inner"), "S_tborder", 1, 0, - _T("Global"), _T("TabTextNormal"), "S_tab_txt_normal", 5, 0, - _T("Global"), _T("TabTextActive"), "S_tab_txt_active", 5, 0, - _T("Global"), _T("TabTextUnread"), "S_tab_txt_unread", 5, 0, - _T("Global"), _T("TabTextHottrack"), "S_tab_txt_hottrack", 5, 0, - NULL, NULL, NULL, 0, 0 -}; - -void CImageItem::Create(const TCHAR *szImageFile) -{ - HBITMAP hbm = LoadPNG(szImageFile); - BITMAP bm; - - m_hdc = 0; - m_hbmOld = 0; - m_hbm = 0; - - if (hbm) { - m_hbm = hbm; - m_bf.BlendFlags = 0; - m_bf.BlendOp = AC_SRC_OVER; - m_bf.AlphaFormat = 0; - - GetObject(hbm, sizeof(bm), &bm); - if (bm.bmBitsPixel == 32 && m_dwFlags & IMAGE_PERPIXEL_ALPHA) { - CImageItem::PreMultiply(m_hbm, 1); - m_bf.AlphaFormat = AC_SRC_ALPHA; - } - m_width = bm.bmWidth; - m_height = bm.bmHeight; - m_inner_height = m_height - m_bTop - m_bBottom; - m_inner_width = m_width - m_bLeft - m_bRight; - if (m_bTop && m_bBottom && m_bLeft && m_bRight) { - m_dwFlags |= IMAGE_FLAG_DIVIDED; - if (m_inner_height <= 0 || m_inner_width <= 0) { - DeleteObject(hbm); - m_hbm = 0; - return; - } - } - } -} - -/** - * Reads the definitions for an image item from the given .tsk file - * It does _not_ create the image itself, a call to CImageItem::Create() must be done - * to read the image in memory and prepare - * - * @param szFilename char*: full path and filename to the .TSK file - * - * @return char*: full path and filename to the .png image which represents this image item. - * caller MUST delete it. - */ -TCHAR* CImageItem::Read(const TCHAR *szFilename) -{ - TCHAR buffer[501]; - TCHAR szDrive[MAX_PATH], szPath[MAX_PATH]; - TCHAR *szFinalName = 0; - - GetPrivateProfileString(m_szName, _T("Glyph"), _T("None"), buffer, 500, szFilename); - if (_tcscmp(buffer, _T("None"))) { - _stscanf(buffer, _T("%d,%d,%d,%d"), &m_glyphMetrics[0], &m_glyphMetrics[1], - &m_glyphMetrics[2], &m_glyphMetrics[3]); - if (m_glyphMetrics[2] > m_glyphMetrics[0] && m_glyphMetrics[3] > m_glyphMetrics[1]) { - m_dwFlags |= IMAGE_GLYPH; - m_glyphMetrics[2] = (m_glyphMetrics[2] - m_glyphMetrics[0]) + 1; - m_glyphMetrics[3] = (m_glyphMetrics[3] - m_glyphMetrics[1]) + 1; - } - } - GetPrivateProfileString(m_szName, _T("Image"), _T("None"), buffer, 500, szFilename); - if (_tcscmp(buffer, _T("None")) || m_dwFlags & IMAGE_GLYPH) { - szFinalName = new TCHAR[MAX_PATH]; - //strncpy(m_szName, &m_szName[1], sizeof(m_szName)); - //m_szName[sizeof(m_szName) - 1] = 0; - _tsplitpath(szFilename, szDrive, szPath, NULL, NULL); - mir_sntprintf(szFinalName, MAX_PATH, _T("%s\\%s%s"), szDrive, szPath, buffer); - if (!PathFileExists(szFinalName)) { - delete[] szFinalName; - szFinalName = 0; - } - m_alpha = GetPrivateProfileInt(m_szName, _T("Alpha"), 100, szFilename); - m_alpha = min(m_alpha, 100); - m_alpha = (BYTE)((FLOAT)(((FLOAT) m_alpha) / 100) * 255); - m_bf.SourceConstantAlpha = m_alpha; - m_bLeft = GetPrivateProfileInt(m_szName, _T("Left"), 0, szFilename); - m_bRight = GetPrivateProfileInt(m_szName, _T("Right"), 0, szFilename); - m_bTop = GetPrivateProfileInt(m_szName, _T("Top"), 0, szFilename); - m_bBottom = GetPrivateProfileInt(m_szName, _T("Bottom"), 0, szFilename); - if (m_dwFlags & IMAGE_GLYPH) { - m_width = m_glyphMetrics[2]; - m_height = m_glyphMetrics[3]; - m_inner_height = m_glyphMetrics[3] - m_bTop - m_bBottom; - m_inner_width = m_glyphMetrics[2] - m_bRight - m_bLeft; - - if (m_bTop && m_bBottom && m_bLeft && m_bRight) - m_dwFlags |= IMAGE_FLAG_DIVIDED; - m_bf.BlendFlags = 0; - m_bf.BlendOp = AC_SRC_OVER; - m_bf.AlphaFormat = 0; - m_dwFlags |= IMAGE_PERPIXEL_ALPHA; - m_bf.AlphaFormat = AC_SRC_ALPHA; - if (m_inner_height <= 0 || m_inner_width <= 0) { - if (szFinalName) { - delete[] szFinalName; - szFinalName = 0; - } - return(szFinalName); - } - } - GetPrivateProfileString(m_szName, _T("Fillcolor"), _T("None"), buffer, 500, szFilename); - if (_tcscmp(buffer, _T("None"))) { - COLORREF fillColor = CSkin::HexStringToLong(buffer); - m_fillBrush = CreateSolidBrush(fillColor); - m_dwFlags |= IMAGE_FILLSOLID; - } else - m_fillBrush = 0; - GetPrivateProfileString(m_szName, _T("Colorkey"), _T("None"), buffer, 500, szFilename); - if (_tcscmp(buffer, _T("None"))) { - CSkin::m_ContainerColorKey = CSkin::HexStringToLong(buffer); - if (CSkin::m_ContainerColorKeyBrush) - DeleteObject(CSkin::m_ContainerColorKeyBrush); - CSkin::m_ContainerColorKeyBrush = CreateSolidBrush(CSkin::m_ContainerColorKey); - } - GetPrivateProfileString(m_szName, _T("Stretch"), _T("None"), buffer, 500, szFilename); - if (buffer[0] == 'B' || buffer[0] == 'b') - m_bStretch = IMAGE_STRETCH_B; - else if (buffer[0] == 'h' || buffer[0] == 'H') - m_bStretch = IMAGE_STRETCH_V; - else if (buffer[0] == 'w' || buffer[0] == 'W') - m_bStretch = IMAGE_STRETCH_H; - m_hbm = 0; - if (GetPrivateProfileInt(m_szName, _T("Perpixel"), 0, szFilename)) - m_dwFlags |= IMAGE_PERPIXEL_ALPHA; - - return(szFinalName); - } - return 0; -} - -/** - * Free all resources allocated by an image item - */ -void CImageItem::Free() -{ - if (m_hdc ) { - ::SelectObject(m_hdc, m_hbmOld); - ::DeleteDC(m_hdc); - } - if (m_hbm) - ::DeleteObject(m_hbm); - - if (m_fillBrush) - ::DeleteObject(m_fillBrush); - - ZeroMemory(this, sizeof(CImageItem)); -} - -/** - * Set the alpha value for a 32bit RGBA bitmap to the given value - * - * @param hBitmap bitmap handle - * @param bAlpha new alpha value (0 -> fully transparent, 255 -> opaque) - * default value is 255 - */ -void CImageItem::SetBitmap32Alpha(HBITMAP hBitmap, BYTE bAlpha) -{ - BITMAP bmp; - int x, y; - BOOL fixIt = TRUE; - - GetObject(hBitmap, sizeof(bmp), &bmp); - - if (bmp.bmBitsPixel != 32) - return; - - DWORD dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); - BYTE *p = (BYTE *)mir_alloc(dwLen); - if (p == NULL) - return; - memset(p, 0, dwLen); - - GetBitmapBits(hBitmap, dwLen, p); - - for (y = 0; y < bmp.bmHeight; ++y) { - BYTE *px = p + bmp.bmWidth * 4 * y; - - for (x = 0; x < bmp.bmWidth; ++x) { - px[3] = bAlpha; - px += 4; - } - } - SetBitmapBits(hBitmap, bmp.bmWidth * bmp.bmHeight * 4, p); - mir_free(p); -} - -void CImageItem::PreMultiply(HBITMAP hBitmap, int mode) -{ - BITMAP bmp; - ::GetObject(hBitmap, sizeof(bmp), &bmp); - - int width = bmp.bmWidth; - int height = bmp.bmHeight; - DWORD dwLen = width * height * 4; - BYTE *p = (BYTE *)mir_alloc(dwLen); - if (p) { - ::GetBitmapBits(hBitmap, dwLen, p); - for (int y = 0; y < height; ++y) { - BYTE *px = p + width * 4 * y; - - for (int x = 0; x < width; ++x) { - if (mode) { - BYTE alpha = px[3]; - px[0] = px[0] * alpha / 255; - px[1] = px[1] * alpha / 255; - px[2] = px[2] * alpha / 255; - } else - px[3] = 255; - px += 4; - } - } - dwLen = ::SetBitmapBits(hBitmap, dwLen, p); - mir_free(p); - } -} - -void CImageItem::Colorize(HBITMAP hBitmap, BYTE dr, BYTE dg, BYTE db, BYTE alpha) -{ - BYTE *p = NULL; - DWORD dwLen; - int width, height, x, y; - BITMAP bmp; - - float r = (float)dr / 2.55; - float g = (float)dg / 2.55; - float b = (float)db / 2.55; - - ::GetObject(hBitmap, sizeof(bmp), &bmp); - width = bmp.bmWidth; - height = bmp.bmHeight; - dwLen = width * height * 4; - p = (BYTE *)mir_alloc(dwLen); - if (p) { - ::GetBitmapBits(hBitmap, dwLen, p); - for (y = 0; y < height; ++y) { - BYTE *px = p + width * 4 * y; - - for (x = 0; x < width; ++x) { - px[0] = (int)(px[0] + b) > 255 ? 255 : px[0] + b; - px[1] = (int)(px[1] + g) > 255 ? 255 : px[1] + g; - px[2] = (int)(px[2] + r) > 255 ? 255 : px[2] + r; - px[3] += alpha; - px += 4; - } - } - dwLen = ::SetBitmapBits(hBitmap, dwLen, p); - mir_free(p); - } -} - -/** - * load PNG image using core service (advaimg) - */ - -HBITMAP TSAPI CImageItem::LoadPNG(const TCHAR *szFilename) -{ - HBITMAP hBitmap = 0; - hBitmap = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)szFilename, IMGL_TCHAR); - return hBitmap; -} - - -/** - * set filename and load parameters from the database - * called on: - * ) init - * ) manual loading on user's request - */ -void CSkin::setFileName() -{ - DBVARIANT dbv; - if (0 == db_get_ts(0, SRMSGMOD_T, "ContainerSkin", &dbv)) { - PathToAbsoluteT(dbv.ptszVal, m_tszFileName, M.getSkinPath()); - db_free(&dbv); - } - else - m_tszFileName[0] = 0; - - m_fLoadOnStartup = M.GetByte("useskin", 0) ? true : false; -} -/** - * initialize the skin object - */ - -void CSkin::Init(bool fStartup) -{ - m_ImageItems = 0; - m_SkinItems = ::SkinItems; - m_fLoadOnStartup = false; - m_skinEnabled = m_frameSkins = false; - m_bAvatarBorderType = AVBORDER_NORMAL; - m_avatarBorderClr = ::GetSysColor(COLOR_3DDKSHADOW); - m_sideBarContainerBG = ::GetSysColor(COLOR_3DFACE); - - m_SkinItems[ID_EXTBKINFOPANELBG] = _defInfoPanel; - /* - * read current skin name from db - */ - - m_DisableScrollbars = M.GetByte("disableVScroll", 0) ? true : false; - - setFileName(); - m_aeroEffect = M.GetDword("aerostyle", AERO_EFFECT_MILK); - if (m_fLoadOnStartup && fStartup) - Load(); -} - -/** - * throws a warning to close all message windows before a skin can - * be loaded. user can cancel it - * @return: bool: true if windows were closed (or none was open) -> skin can be loaded - * - */ -bool CSkin::warnToClose() const -{ - if (::pFirstContainer == NULL) - return true; - - if (MessageBox(0, - TranslateT("All message containers need to close before the skin can be changed\nProceed?"), - TranslateT("Change skin"), MB_YESNO | MB_ICONQUESTION) != IDYES) - return false; - - CloseAllContainers(); - return true; -} - -/** - * mir_free the aero tab bitmaps - * only called on exit, NOT when a skin is unloaded as these elements - * are always needed (even without a skin) - */ -void CSkin::UnloadAeroTabs() -{ - if (m_tabTop) { - delete m_tabTop; - m_tabTop = NULL; - } - - if (m_tabBottom) { - delete m_tabBottom; - m_tabBottom = NULL; - } - - if (m_tabGlowTop) { - delete m_tabGlowTop; - m_tabGlowTop = NULL; - } - - if (m_tabGlowBottom) { - delete m_tabGlowBottom; - m_tabGlowTop = NULL; - } - - if (m_switchBarItem) { - delete m_switchBarItem; - m_switchBarItem = NULL; - } -} - -/** - * Unload the skin. Free everything allocated. - * Called when: - * * user unloads the skin from the dialog box - * * a new skin is loaded by user's request. - */ - -void CSkin::Unload() -{ - // do nothing when user decides to not close any window - if (warnToClose() == false) - return; - - /* - * delete all image items - */ - - m_skinEnabled = m_frameSkins = false; - - CImageItem *tmp = m_ImageItems, *nextItem; - while(tmp) { - nextItem = tmp->getNextItem(); - delete tmp; - tmp = nextItem; - } - - m_ImageItems = 0; - m_glyphItem.Free(); - - if (m_ContainerColorKeyBrush) - ::DeleteObject(m_ContainerColorKeyBrush); - if (m_MenuBGBrush) - ::DeleteObject(m_MenuBGBrush); - if (m_BrushBack) - ::DeleteObject(m_BrushBack); - - m_ContainerColorKeyBrush = m_MenuBGBrush = 0; - - if (m_SkinLightShadowPen) - ::DeleteObject(m_SkinLightShadowPen); - m_SkinLightShadowPen = 0; - - if (m_SkinDarkShadowPen) - ::DeleteObject(m_SkinDarkShadowPen); - m_SkinDarkShadowPen = 0; - - if (m_SkinItems) { - for (int i=0; i < ID_EXTBK_LAST; i++) { - m_SkinItems[i].IGNORED = 1; - m_SkinItems[i].imageItem = 0; - } - m_SkinItems[ID_EXTBKINFOPANELBG] = _defInfoPanel; - } - - ZeroMemory(this, sizeof(CSkin)); - - m_SkinItems = ::SkinItems; - setFileName(); - - m_bClipBorder = m_DisableScrollbars = false; - m_SkinnedFrame_left = m_SkinnedFrame_right = m_SkinnedFrame_bottom = m_SkinnedFrame_caption = 0; - m_realSkinnedFrame_left = m_realSkinnedFrame_right = m_realSkinnedFrame_bottom = m_realSkinnedFrame_caption = 0; - - m_titleBarLeftOff = m_titleButtonTopOff = m_captionOffset = m_captionPadding = - m_titleBarRightOff = m_sidebarTopOffset = m_sidebarBottomOffset = m_bRoundedCorner = 0; - - m_titleBarButtonSize.cx = m_titleBarButtonSize.cy = 0; - m_ContainerColorKey = 0; - m_ContainerColorKeyBrush = m_MenuBGBrush = 0; - m_skinEnabled = m_frameSkins = false; - - if (m_closeIcon) - ::DestroyIcon(m_closeIcon); - if (m_maxIcon) - ::DestroyIcon(m_maxIcon); - if (m_minIcon) - ::DestroyIcon(m_minIcon); - - m_closeIcon = m_maxIcon = m_minIcon = 0; - - for (int i=0; i < m_nrSkinIcons; i++) - if (m_skinIcons[i].phIcon ) - ::DestroyIcon(*(m_skinIcons[i].phIcon)); - mir_free(m_skinIcons); - - if (!g_bShutdown) { - M.getAeroState(); // refresh after unload - ::FreeTabConfig(); - ::ReloadTabConfig(); - } - - m_bAvatarBorderType = AVBORDER_NORMAL; - m_avatarBorderClr = ::GetSysColor(COLOR_3DDKSHADOW); - m_sideBarContainerBG = ::GetSysColor(COLOR_3DFACE); - - m_DisableScrollbars = M.GetByte("disableVScroll", 0) ? true : false; -} - -void CSkin::LoadIcon(const TCHAR *szSection, const TCHAR *name, HICON *hIcon) -{ - TCHAR buffer[512]; - if (*hIcon != 0) - DestroyIcon(*hIcon); - GetPrivateProfileString(szSection, name, _T("none"), buffer, 250, m_tszFileName); - buffer[500] = 0; - - if (_tcsicmp(buffer, _T("none"))) { - TCHAR szDrive[MAX_PATH], szDir[MAX_PATH], szImagePath[MAX_PATH]; - - _tsplitpath(m_tszFileName, szDrive, szDir, NULL, NULL); - mir_sntprintf(szImagePath, MAX_PATH, _T("%s\\%s\\%s"), szDrive, szDir, buffer); - if (hIcon) - *hIcon = (HICON)LoadImage(0, szImagePath, IMAGE_ICON, 16, 16, LR_LOADFROMFILE); - } -} - - -/** - * Read a skin item (not a image item - these are handled elsewhere) - * Reads all values from the .ini style .tsk file and fills the - * structure. - * - * @param id int: zero-based index into the table of predefined skin items - * @param szItem char *: the section name in the ini file which holds the definition for this - * item. - */ -void CSkin::ReadItem(const int id, const TCHAR *szItem) -{ - TCHAR buffer[512]; - TCHAR def_color[20]; - COLORREF clr; - CSkinItem *defaults = &StatusItem_Default; - - CSkinItem *this_item = &m_SkinItems[id]; - - this_item->ALPHA = (int)GetPrivateProfileInt(szItem, _T("Alpha"), defaults->ALPHA, m_tszFileName); - this_item->ALPHA = min(this_item->ALPHA, 100); - - clr = RGB(GetBValue(defaults->COLOR), GetGValue(defaults->COLOR), GetRValue(defaults->COLOR)); - mir_sntprintf(def_color, 15, _T("%6.6x"), clr); - GetPrivateProfileString(szItem, _T("Color1"), def_color, buffer, 400, m_tszFileName); - this_item->COLOR = HexStringToLong(buffer); - - clr = RGB(GetBValue(defaults->COLOR2), GetGValue(defaults->COLOR2), GetRValue(defaults->COLOR2)); - mir_sntprintf(def_color, 15, _T("%6.6x"), clr); - GetPrivateProfileString(szItem, _T("Color2"), def_color, buffer, 400, m_tszFileName); - this_item->COLOR2 = HexStringToLong(buffer); - - this_item->COLOR2_TRANSPARENT = (BYTE)GetPrivateProfileInt(szItem, _T("COLOR2_TRANSPARENT"), defaults->COLOR2_TRANSPARENT, m_tszFileName); - - this_item->CORNER = defaults->CORNER & CORNER_ACTIVE ? defaults->CORNER : 0; - GetPrivateProfileString(szItem, _T("Corner"), _T("None"), buffer, 400, m_tszFileName); - if (_tcsstr(buffer, _T("tl"))) - this_item->CORNER |= CORNER_TL; - if (_tcsstr(buffer, _T("tr"))) - this_item->CORNER |= CORNER_TR; - if (_tcsstr(buffer, _T("bl"))) - this_item->CORNER |= CORNER_BL; - if (_tcsstr(buffer, _T("br"))) - this_item->CORNER |= CORNER_BR; - if (this_item->CORNER) - this_item->CORNER |= CORNER_ACTIVE; - - this_item->GRADIENT = defaults->GRADIENT & GRADIENT_ACTIVE ? defaults->GRADIENT : 0; - GetPrivateProfileString(szItem, _T("Gradient"), _T("None"), buffer, 400, m_tszFileName); - if (_tcsstr(buffer, _T("left"))) - this_item->GRADIENT = GRADIENT_RL; - else if (_tcsstr(buffer, _T("right"))) - this_item->GRADIENT = GRADIENT_LR; - else if (_tcsstr(buffer, _T("up"))) - this_item->GRADIENT = GRADIENT_BT; - else if (_tcsstr(buffer, _T("down"))) - this_item->GRADIENT = GRADIENT_TB; - if (this_item->GRADIENT) - this_item->GRADIENT |= GRADIENT_ACTIVE; - - this_item->MARGIN_LEFT = GetPrivateProfileInt(szItem, _T("Left"), defaults->MARGIN_LEFT, m_tszFileName); - this_item->MARGIN_RIGHT = GetPrivateProfileInt(szItem, _T("Right"), defaults->MARGIN_RIGHT, m_tszFileName); - this_item->MARGIN_TOP = GetPrivateProfileInt(szItem, _T("Top"), defaults->MARGIN_TOP, m_tszFileName); - this_item->MARGIN_BOTTOM = GetPrivateProfileInt(szItem, _T("Bottom"), defaults->MARGIN_BOTTOM, m_tszFileName); - this_item->BORDERSTYLE = GetPrivateProfileInt(szItem, _T("Radius"), defaults->BORDERSTYLE, m_tszFileName); - - GetPrivateProfileString(szItem, _T("Textcolor"), _T("ffffffff"), buffer, 400, m_tszFileName); - this_item->TEXTCOLOR = HexStringToLong(buffer); - this_item->IGNORED = 0; -} - -/** - * stub to read a single item. Called from CSkin::LoadItems() - * The real work is done by the CImageItem::Read(). - * - * @param itemname char *: image item name, also section name in the .tsk file - */ -void CSkin::ReadImageItem(const TCHAR *itemname) -{ - TCHAR buffer[512], szItemNr[30]; - - CImageItem tmpItem(itemname); - - TCHAR *szImageFileName = tmpItem.Read(m_tszFileName); - - if (!_tcsicmp(itemname, _T("$glyphs")) && szImageFileName != 0) { // the glyph item MUST have a valid image - tmpItem.Create(szImageFileName); - if (tmpItem.getHbm()) { - m_glyphItem = tmpItem; - m_fHaveGlyph = true; - } - tmpItem.Clear(); - delete[] szImageFileName; - return; - } - - /* - * handle the assignments of image items to skin items - */ - - for (int n = 0;;n++) { - mir_sntprintf(szItemNr, 30, _T("Item%d"), n); - GetPrivateProfileString(itemname, szItemNr, _T("None"), buffer, 500, m_tszFileName); - if (!_tcscmp(buffer, _T("None"))) - break; - for (int i=0; i <= ID_EXTBK_LAST; i++) { - if (!_tcsicmp(SkinItems[i].szName[0] == '{' ? &SkinItems[i].szName[3] : SkinItems[i].szName, buffer)) { - if (!(tmpItem.getFlags() & IMAGE_GLYPH)) { - if (szImageFileName) - tmpItem.Create(szImageFileName); - else { - tmpItem.Clear(); - return; // no reference to the glpyh image and no valid image file name -> invalid item - } - } - if (tmpItem.getHbm() || (tmpItem.getFlags() & IMAGE_GLYPH)) { - CImageItem *newItem = new CImageItem(tmpItem); - SkinItems[i].imageItem = newItem; - if (m_ImageItems == NULL) - m_ImageItems = newItem; - else { - CImageItem *pItem = m_ImageItems; - - while (pItem->getNextItem() != 0) - pItem = pItem->getNextItem(); - pItem->setNextItem(newItem); - } - } - } - } - } - tmpItem.Clear(); - if (szImageFileName) - delete[] szImageFileName; -} - -/** - * Load the skin from the .tsk file - * It reads and initializes all static values for the skin. Afterwards - * it calls ReadItems() to read additional skin information like image items, - * buttons and icons. - */ -void CSkin::Load(void) -{ - if (warnToClose() == false) - return; - - if (m_skinEnabled) { - Unload(); - m_skinEnabled = false; - } - - m_fHaveGlyph = false; - - if ( !m_tszFileName[0] || !::PathFileExists(m_tszFileName)) - return; - - TCHAR *p; - TCHAR *szSections = (TCHAR*)mir_alloc(6004); - int i = 1, j = 0; - UINT data; - TCHAR buffer[500]; - - if (!(GetPrivateProfileInt(_T("Global"), _T("Version"), 0, m_tszFileName) >= 1 && GetPrivateProfileInt(_T("Global"), _T("Signature"), 0, m_tszFileName) == 101)) - return; - - i = 0; - while (_tagSettings[i].szIniKey != NULL) { - data = 0; - data = GetPrivateProfileInt(_tagSettings[i].szIniKey, _tagSettings[i].szIniName, - _tagSettings[i].defaultval, m_tszFileName); - switch (_tagSettings[i].size) { - case 1: - db_set_b(0, SRMSGMOD_T, _tagSettings[i].szSetting, (BYTE)data); - break; - case 4: - db_set_dw(0, SRMSGMOD_T, _tagSettings[i].szSetting, data); - break; - case 2: - db_set_w(NULL, SRMSGMOD_T, _tagSettings[i].szSetting, (WORD)data); - break; - case 5: - GetPrivateProfileString(_tagSettings[i].szIniKey, _tagSettings[i].szIniName, _T("000000"), - buffer, 10, m_tszFileName); - db_set_dw(0, SRMSGMOD_T, _tagSettings[i].szSetting, HexStringToLong(buffer)); - break; - } - i++; - } - - m_DisableScrollbars = M.GetByte("disableVScroll", 0) ? true : false; - - ZeroMemory(szSections, 6000); - p = szSections; - GetPrivateProfileSectionNames(szSections, 3000, m_tszFileName); - szSections[3001] = szSections[3000] = 0; - p = szSections; - while (lstrlen(p) > 1) { - if (p[0] != '%') { - p += (lstrlen(p) + 1); - continue; - } - for (i=0; i <= ID_EXTBK_LAST; i++) { - if (!_tcsicmp(&p[1], SkinItems[i].szName[0] == '{' ? &SkinItems[i].szName[3] : SkinItems[i].szName)) { - ReadItem(i, p); - break; - } - } - p += (lstrlen(p) + 1); - j++; - } - - if (j > 0) { - m_skinEnabled = true; - M.getAeroState(); // refresh aero state (set to false when a skin is successfully loaded and active) - } - - GetPrivateProfileString(_T("Avatars"), _T("BorderColor"), _T("000000"), buffer, 20, m_tszFileName); - m_avatarBorderClr = (COLORREF)HexStringToLong(buffer); - - GetPrivateProfileString(_T("Global"), _T("SideBarBG"), _T("None"), buffer, 20, m_tszFileName); - if (_tcscmp(buffer, _T("None"))) - m_sideBarContainerBG = (COLORREF)HexStringToLong(buffer); - else - m_sideBarContainerBG = SkinItems[ID_EXTBKSIDEBARBG].COLOR; - - m_bAvatarBorderType = GetPrivateProfileInt(_T("Avatars"), _T("BorderType"), 1, m_tszFileName); - - LoadIcon(_T("Global"), _T("CloseGlyph"), &CSkin::m_closeIcon); - LoadIcon(_T("Global"), _T("MaximizeGlyph"), &CSkin::m_maxIcon); - LoadIcon(_T("Global"), _T("MinimizeGlyph"), &CSkin::m_minIcon); - - m_frameSkins = GetPrivateProfileInt(_T("Global"), _T("framelessmode"), 0, m_tszFileName) ? true : false; - m_DisableScrollbars = GetPrivateProfileInt(_T("Global"), _T("NoScrollbars"), 0, m_tszFileName) ? true : false; - - m_SkinnedFrame_left = GetPrivateProfileInt(_T("WindowFrame"), _T("left"), 4, m_tszFileName); - m_SkinnedFrame_right = GetPrivateProfileInt(_T("WindowFrame"), _T("right"), 4, m_tszFileName); - m_SkinnedFrame_caption = GetPrivateProfileInt(_T("WindowFrame"), _T("Caption"), 24, m_tszFileName); - m_SkinnedFrame_bottom = GetPrivateProfileInt(_T("WindowFrame"), _T("bottom"), 4, m_tszFileName); - - m_titleBarButtonSize.cx = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleButtonWidth"), 24, m_tszFileName); - m_titleBarButtonSize.cy = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleButtonHeight"), 12, m_tszFileName); - m_titleButtonTopOff = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleButtonTopOffset"), 0, m_tszFileName); - - m_titleBarRightOff = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleBarRightOffset"), 0, m_tszFileName); - m_titleBarLeftOff = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleBarLeftOffset"), 0, m_tszFileName); - - m_captionOffset = GetPrivateProfileInt(_T("WindowFrame"), _T("CaptionOffset"), 3, m_tszFileName); - m_captionPadding = GetPrivateProfileInt(_T("WindowFrame"), _T("CaptionPadding"), 0, m_tszFileName); - m_sidebarTopOffset = GetPrivateProfileInt(_T("ClientArea"), _T("SidebarTop"), -1, m_tszFileName); - m_sidebarBottomOffset = GetPrivateProfileInt(_T("ClientArea"), _T("SidebarBottom"), -1, m_tszFileName); - - m_bClipBorder = GetPrivateProfileInt(_T("WindowFrame"), _T("ClipFrame"), 0, m_tszFileName) ? true : false; - - BYTE radius_tl, radius_tr, radius_bl, radius_br; - TCHAR szFinalName[MAX_PATH]; - TCHAR szDrive[MAX_PATH], szPath[MAX_PATH]; - - radius_tl = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusTL"), 0, m_tszFileName); - radius_tr = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusTR"), 0, m_tszFileName); - radius_bl = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusBL"), 0, m_tszFileName); - radius_br = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusBR"), 0, m_tszFileName); - - CSkin::m_bRoundedCorner = radius_tl; - - GetPrivateProfileString(_T("Theme"), _T("File"), _T("None"), buffer, MAX_PATH, m_tszFileName); - - _tsplitpath(m_tszFileName, szDrive, szPath, NULL, NULL); - mir_sntprintf(szFinalName, MAX_PATH, _T("%s\\%s\\%s"), szDrive, szPath, buffer); - if (PathFileExists(szFinalName)) { - ReadThemeFromINI(szFinalName, 0, FALSE, m_fLoadOnStartup ? 0 : M.GetByte("skin_loadmode", 0)); - CacheLogFonts(); - CacheMsgLogIcons(); - } - - GetPrivateProfileString(_T("Global"), _T("MenuBarBG"), _T("None"), buffer, 20, m_tszFileName); - data = HexStringToLong(buffer); - if (m_MenuBGBrush) { - DeleteObject(m_MenuBGBrush); - m_MenuBGBrush = 0; - } - if (_tcscmp(buffer, _T("None"))) - m_MenuBGBrush = CreateSolidBrush(data); - - GetPrivateProfileString(_T("Global"), _T("LightShadow"), _T("000000"), buffer, 20, m_tszFileName); - data = HexStringToLong(buffer); - CSkin::m_SkinLightShadowPen = CreatePen(PS_SOLID, 1, RGB(GetRValue(data), GetGValue(data), GetBValue(data))); - GetPrivateProfileString(_T("Global"), _T("DarkShadow"), _T("000000"), buffer, 20, m_tszFileName); - data = HexStringToLong(buffer); - CSkin::m_SkinDarkShadowPen = CreatePen(PS_SOLID, 1, RGB(GetRValue(data), GetGValue(data), GetBValue(data))); - - SkinCalcFrameWidth(); - - GetPrivateProfileString(_T("Global"), _T("FontColor"), _T("None"), buffer, 20, m_tszFileName); - if (_tcscmp(buffer, _T("None"))) - CSkin::m_DefaultFontColor = HexStringToLong(buffer); - else - CSkin::m_DefaultFontColor = GetSysColor(COLOR_BTNTEXT); - buffer[499] = 0; - mir_free(szSections); - - LoadItems(); - ::FreeTabConfig(); - ::ReloadTabConfig(); -} - -#define SECT_BUFFER_SIZE 2500 - -/** - * Load additional skin items (like image items, buttons, icons etc.) - * This is called AFTER ReadItems() has read the basic skin items - */ -void CSkin::LoadItems() -{ - TCHAR *szSections = NULL; - TCHAR *p, *p1; - TIconDesc tmpIconDesc = {0}; - - CImageItem *pItem = m_ImageItems; - - if (m_skinIcons == NULL) - m_skinIcons = (TIconDescW *)mir_calloc(sizeof(TIconDescW) * NR_MAXSKINICONS); - - m_nrSkinIcons = 0; - - szSections = (TCHAR*)mir_alloc((SECT_BUFFER_SIZE + 2) * sizeof(TCHAR)); - ZeroMemory(szSections, (SECT_BUFFER_SIZE + 2) * sizeof(TCHAR)); - - GetPrivateProfileSection(_T("Icons"), szSections, SECT_BUFFER_SIZE, m_tszFileName); - szSections[SECT_BUFFER_SIZE] = 0; - - p = szSections; - while (lstrlen(p) > 1) { - p1 = _tcschr(p, (int)'='); - if (p1) - *p1 = 0; - if (m_nrSkinIcons < NR_MAXSKINICONS && p1) { - LoadIcon(_T("Icons"), p, (HICON *)&tmpIconDesc.uId); - if (tmpIconDesc.uId) { - ZeroMemory(&m_skinIcons[m_nrSkinIcons], sizeof(TIconDesc)); - m_skinIcons[m_nrSkinIcons].uId = tmpIconDesc.uId; - m_skinIcons[m_nrSkinIcons].phIcon = (HICON *)(&m_skinIcons[m_nrSkinIcons].uId); - m_skinIcons[m_nrSkinIcons].szName = (TCHAR*)mir_alloc(sizeof(TCHAR) * (lstrlen(p) + 1)); - lstrcpy(m_skinIcons[m_nrSkinIcons].szName, p); - m_nrSkinIcons++; - } - } - if (p1) - *p1 = '='; - p += (lstrlen(p) + 1); - } - - ZeroMemory(szSections, (SECT_BUFFER_SIZE + 2) * sizeof(TCHAR)); - GetPrivateProfileSectionNames(szSections, SECT_BUFFER_SIZE, m_tszFileName); - szSections[SECT_BUFFER_SIZE] = 0; - - p = szSections; - while (lstrlen(p) > 1) { - if (p[0] == '$') - ReadImageItem(p); - p += (lstrlen(p) + 1); - } - nextButtonID = IDC_TBFIRSTUID; - - p = szSections; - /* - while (lstrlen(p) > 1) { - if (p[0] == '!') - ReadButtonItem(p); - p += (lstrlen(p) + 1); - } - */ - mir_free(szSections); - g_ButtonSet.top = GetPrivateProfileInt(_T("ButtonArea"), _T("top"), 0, m_tszFileName); - g_ButtonSet.bottom = GetPrivateProfileInt(_T("ButtonArea"), _T("bottom"), 0, m_tszFileName); - g_ButtonSet.left = GetPrivateProfileInt(_T("ButtonArea"), _T("left"), 0, m_tszFileName); - g_ButtonSet.right = GetPrivateProfileInt(_T("ButtonArea"), _T("right"), 0, m_tszFileName); -} - -/** - * setup and cache the bitmap for the close button on tabs and switch bar - * buttons. - * re-created when: - * ) theme changes - * ) icons change (via ico lib service) - * - * @param fDeleteOnly: only delete GDI resources (this is ONLY used at plugin shutdown) - */ -void CSkin::setupTabCloseBitmap(bool fDeleteOnly) -{ - if (m_tabCloseHDC || fDeleteOnly) { - if (m_tabCloseHDC) { - ::SelectObject(m_tabCloseHDC, m_tabCloseOldBitmap); - ::DeleteObject(m_tabCloseBitmap); - ::DeleteDC(m_tabCloseHDC); - } - if (fDeleteOnly) - return; - } - - bool fFree = false; - RECT rc = {0, 0, 20, 20}; - HDC dc = ::GetDC(PluginConfig.g_hwndHotkeyHandler); - m_tabCloseHDC = ::CreateCompatibleDC(dc); - - if (M.isAero()) - m_tabCloseBitmap = CreateAeroCompatibleBitmap(rc, m_tabCloseHDC); - else - m_tabCloseBitmap = ::CreateCompatibleBitmap(dc, 20, 20); - - m_tabCloseOldBitmap = reinterpret_cast(::SelectObject(m_tabCloseHDC, m_tabCloseBitmap)); - - if (M.isVSThemed() || M.isAero()) { - ::FillRect(m_tabCloseHDC, &rc, M.isAero() ? reinterpret_cast(::GetStockObject(BLACK_BRUSH)) : ::GetSysColorBrush(COLOR_3DFACE)); - - HANDLE hTheme = OpenThemeData(PluginConfig.g_hwndHotkeyHandler, L"BUTTON"); - rc.left--; rc.right++; - rc.top--; rc.bottom++; - DrawThemeParentBackground(PluginConfig.g_hwndHotkeyHandler, m_tabCloseHDC, &rc); - DrawThemeBackground(hTheme, m_tabCloseHDC, 1, 1, &rc, &rc); - CloseThemeData(hTheme); - } - else if (CSkin::m_skinEnabled) - CSkin::DrawItem(m_tabCloseHDC, &rc, &SkinItems[ID_EXTBKBUTTONSNPRESSED]); - else { - ::FillRect(m_tabCloseHDC, &rc, ::GetSysColorBrush(COLOR_3DFACE)); - ::DrawFrameControl(m_tabCloseHDC, &rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_MONO); - } - ::DrawIconEx(m_tabCloseHDC, 2, 2, PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL], 16, 16, 0, 0, DI_NORMAL); - ::SelectObject(m_tabCloseHDC, m_tabCloseOldBitmap); - - HBITMAP hbmTemp = ResizeBitmap(m_tabCloseBitmap, 16, 16, fFree); - ::DeleteObject(m_tabCloseBitmap); - m_tabCloseBitmap = hbmTemp; - CImageItem::PreMultiply(m_tabCloseBitmap, 1); - m_tabCloseOldBitmap = reinterpret_cast(::SelectObject(m_tabCloseHDC, m_tabCloseBitmap)); - - ::ReleaseDC(PluginConfig.g_hwndHotkeyHandler, dc); -} -/** - * load and setup some images which are used to draw tabs in aero mode - * there is one image for tabs (it is flipped vertically for bottom tabs) - * and one image for the glowing effect on tabs (also flipped for bottom - * tabs). - * - * support for custom images added 3.0.0.34 - * user can place images with a custom_ prefix into the images folder and - * they will be loaded instead the default ones. - * - * the 3rd image acts as background for switch bar buttons - * - * this is executed when: - * ) dwm mode changes - * ) aero effect is changed by the user - * ) glow colorization is changed by user's request - */ -void CSkin::setupAeroSkins() -{ - M.getAeroState(); - UnloadAeroTabs(); - - if (!m_fAeroSkinsValid) - return; - - TCHAR tszFilename[MAX_PATH], tszBasePath[MAX_PATH]; - _tcsncpy_s(tszBasePath, M.getDataPath(), _TRUNCATE); - if (tszBasePath[lstrlen(tszBasePath) - 1] != '\\') - _tcscat(tszBasePath, _T("\\")); - - // load unknown avatar.. - if (0 == PluginConfig.g_hbmUnknown) { - mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_unknown.png"), tszBasePath); - if (!PathFileExists(tszFilename)) - mir_sntprintf(tszFilename, MAX_PATH, _T("%sunknown.png"), tszBasePath); - PluginConfig.g_hbmUnknown = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR); - if (PluginConfig.g_hbmUnknown == 0) { - HDC dc = GetDC(0); - PluginConfig.g_hbmUnknown = CreateCompatibleBitmap(dc, 20, 20); - ReleaseDC(0, dc); - } - } - - mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_tabskin_aero.png"), tszBasePath); - if (!PathFileExists(tszFilename)) - mir_sntprintf(tszFilename, MAX_PATH, _T("%stabskin_aero.png"), tszBasePath); - - BOOL isOpaque = false; - if (CMimAPI::m_pfnDwmGetColorizationColor && M.isAero()) - CMimAPI::m_pfnDwmGetColorizationColor(&m_dwmColor, &isOpaque); - else - m_dwmColor = PluginConfig.m_fillColor; - - float fr = (float)((m_dwmColor & 0x00ff0000) >> 16); - float fg = (float)((m_dwmColor & 0x0000ff00) >> 8); - float fb = (float)((m_dwmColor & 0x000000ff)); - BYTE alphafactor = 255 - ((m_dwmColor & 0xff000000) >> 24); - - /* - * a bit tricky, because for low alpha settings (high DWM transparency), the dwm - * color is almost dark gray, so we need to intensify the strongest color a bit more than - * the others. This will give us a good match for the dwm color that can be used - * to render non transparent stuff (i.e. the tool bar). - * - * TODO: this isn't perfect yet, for some colors, the result is a bit off... - */ - - if (!isOpaque && alphafactor > 150 && !(fr == fg && fg == fb)) { - float fmax = max(max(fr,fg), fb); - - if (fmax == fr) { - fr *= (alphafactor / 100 * 2.2); - fr = min(fr, 255); - fb = min(fb * alphafactor / 100, 255); - fg = min(fg * alphafactor / 100, 255); - } else if (fmax == fg) { - fg *= (alphafactor / 100 * 2.2); - fg = min(fg, 255); - fr = min(fr * alphafactor / 100, 255); - fb = min(fb * alphafactor / 100, 255); - } else { - fb *= (alphafactor / 100 * 2.2); - fb = min(fb, 255); - fr = min(fr * alphafactor / 100, 255); - fg = min(fg * alphafactor / 100, 255); - } - } - - m_dwmColorRGB = RGB((BYTE)fr, (BYTE)fg, (BYTE)fb); - - FIBITMAP *fib = (FIBITMAP *)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR | IMGL_RETURNDIB); - - HBITMAP hbm = FIF->FI_CreateHBITMAPFromDIB(fib); - - CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB), - GetGValue(m_dwmColorRGB), - GetBValue(m_dwmColorRGB)); - - CImageItem::PreMultiply(hbm, 1); - - BITMAP bm; - GetObject(hbm, sizeof(bm), &bm); - m_tabTop = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, - 0, 255, 30, 80, 50, 100); - - m_tabTop->setAlphaFormat(AC_SRC_ALPHA, 255); - m_tabTop->setMetrics(bm.bmWidth, bm.bmHeight); - - - /* - * created inverted bitmap for bottom tabs - */ - - FIF->FI_FlipVertical(fib); - - hbm = FIF->FI_CreateHBITMAPFromDIB(fib); - - CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB), - GetGValue(m_dwmColorRGB), - GetBValue(m_dwmColorRGB)); - - CImageItem::PreMultiply(hbm, 1); - FIF->FI_Unload(fib); - - GetObject(hbm, sizeof(bm), &bm); - m_tabBottom = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, - 0, 255, 30, 80, 50, 100); - - m_tabBottom->setAlphaFormat(AC_SRC_ALPHA, 255); - m_tabBottom->setMetrics(bm.bmWidth, bm.bmHeight); - - - mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_tabskin_aero_glow.png"), tszBasePath); - if (!PathFileExists(tszFilename)) - mir_sntprintf(tszFilename, MAX_PATH, _T("%stabskin_aero_glow.png"), tszBasePath); - - fib = (FIBITMAP *)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR | IMGL_RETURNDIB); - - COLORREF glowColor = M.GetDword(FONTMODULE, "aeroGlow", RGB(40, 40, 255)); - hbm = FIF->FI_CreateHBITMAPFromDIB(fib); - CImageItem::Colorize(hbm, GetRValue(glowColor), GetGValue(glowColor), GetBValue(glowColor)); - CImageItem::PreMultiply(hbm, 1); - - GetObject(hbm, sizeof(bm), &bm); - m_tabGlowTop = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, - 0, 255, 30, 80, 50, 100); - - m_tabGlowTop->setAlphaFormat(AC_SRC_ALPHA, 255); - m_tabGlowTop->setMetrics(bm.bmWidth, bm.bmHeight); - - FIF->FI_FlipVertical(fib); - - hbm = FIF->FI_CreateHBITMAPFromDIB(fib); - CImageItem::Colorize(hbm, GetRValue(glowColor), GetGValue(glowColor), GetBValue(glowColor)); - CImageItem::PreMultiply(hbm, 1); - FIF->FI_Unload(fib); - - GetObject(hbm, sizeof(bm), &bm); - m_tabGlowBottom = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, - 0, 255, 30, 80, 50, 100); - - m_tabGlowBottom->setAlphaFormat(AC_SRC_ALPHA, 255); - m_tabGlowBottom->setMetrics(bm.bmWidth, bm.bmHeight); - - /* - * background item for the button switch bar - */ - mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_tabskin_aero_button.png"), tszBasePath); - if (!PathFileExists(tszFilename)) - mir_sntprintf(tszFilename, MAX_PATH, _T("%stabskin_aero_button.png"), tszBasePath); - - hbm = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR); - - CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB), - GetGValue(m_dwmColorRGB), - GetBValue(m_dwmColorRGB)); - - CImageItem::PreMultiply(hbm, 1); - - GetObject(hbm, sizeof(bm), &bm); - - m_switchBarItem = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, - 0, 255, 2, 12, 10, 20); - - m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, 255); - m_switchBarItem->setMetrics(bm.bmWidth, bm.bmHeight); -} - -/** - * Calculate window frame borders for a skin with the ability to paint the window frame. - * Uses system metrics to determine predefined window borders and caption bar size. - */ -void CSkin::SkinCalcFrameWidth() -{ - int xBorder, yBorder, yCaption; - - xBorder = GetSystemMetrics(SM_CXSIZEFRAME); - yBorder = GetSystemMetrics(SM_CYSIZEFRAME); - yCaption = GetSystemMetrics(SM_CYCAPTION); - - m_realSkinnedFrame_left = m_SkinnedFrame_left - xBorder; - m_realSkinnedFrame_right = m_SkinnedFrame_right - xBorder; - m_realSkinnedFrame_bottom = m_SkinnedFrame_bottom - yBorder; - m_realSkinnedFrame_caption = m_SkinnedFrame_caption - yCaption; -} - - -/** - * Draws part of the background to the foreground control - * - * @param hwndClient HWND: target window - * @param hwnd HWND: source window (usually the parent, needed for transforming client coordinates - * @param pContainer ContainerWindowData *: needed to access the cached DC of the container window - * @param rcClient RECT *: client rectangle (target area) - * @param hdcTarget HDC: device context of the target window - */ -void CSkin::SkinDrawBG(HWND hwndClient, HWND hwnd, TContainerData *pContainer, RECT *rcClient, HDC hdcTarget) -{ - RECT rcWindow; - POINT pt; - LONG width = rcClient->right - rcClient->left; - LONG height = rcClient->bottom - rcClient->top; - HDC dc; - - ::GetWindowRect(hwndClient, &rcWindow); - pt.x = rcWindow.left + rcClient->left; - pt.y = rcWindow.top + rcClient->top; - ::ScreenToClient(hwnd, &pt); - if (pContainer) - dc = pContainer->cachedDC; - else - dc = ::GetDC(hwnd); - pt.y = max(pt.y, 0); - ::BitBlt(hdcTarget, rcClient->left, rcClient->top, width, height, dc, pt.x, pt.y, SRCCOPY); - if (!pContainer) - ::ReleaseDC(hwnd, dc); -} - -/** - * Draws part of the background to the foreground control - * same as above, but can use any source DC, not just the - * container - * - * @param hwndClient HWND: target window - * @param hwnd HWND: source window (usually the parent, needed for transforming client coordinates - * @param pContainer ContainerWindowData *: needed to access the cached DC of the container window - * @param rcClient RECT *: client rectangle (target area) - * @param hdcTarget HDC: device context of the target window - */ - -void CSkin::SkinDrawBGFromDC(HWND hwndClient, HWND hwnd, RECT *rcClient, HDC hdcTarget) -{ - RECT rcWindow; - POINT pt; - LONG width = rcClient->right - rcClient->left; - LONG height = rcClient->bottom - rcClient->top; - HDC hdcSrc = ::GetDC(hwnd); - - ::GetWindowRect(hwndClient, &rcWindow); - pt.x = rcWindow.left + rcClient->left; - pt.y = rcWindow.top + rcClient->top; - ::ScreenToClient(hwnd, &pt); - ::StretchBlt(hdcTarget, rcClient->left, rcClient->top, width, height, hdcSrc, pt.x, pt.y, width, height, SRCCOPY | CAPTUREBLT); - ::ReleaseDC(hwnd, hdcSrc); -} - -/** - * draw an icon "Dimmed" (small amount of transparency applied) -*/ - -void CSkin::DrawDimmedIcon(HDC hdc, LONG left, LONG top, LONG dx, LONG dy, HICON hIcon, BYTE alpha) -{ - HDC dcMem = ::CreateCompatibleDC(hdc); - HBITMAP hbm = ::CreateCompatibleBitmap(hdc, dx, dy), hbmOld = 0; - - hbmOld = reinterpret_cast(::SelectObject(dcMem, hbm)); - ::DrawIconEx(dcMem, 0, 0, hIcon, dx, dy, 0, 0, DI_NORMAL); - m_default_bf.SourceConstantAlpha = alpha; - hbm = (HBITMAP)SelectObject(dcMem, hbmOld); - CImageItem::PreMultiply(hbm, 1); // for AlphaBlend()... - hbmOld = reinterpret_cast(::SelectObject(dcMem, hbm)); - GdiAlphaBlend(hdc, left, top, dx, dy, dcMem, 0, 0, dx, dy, m_default_bf); - m_default_bf.SourceConstantAlpha = 255; - SelectObject(dcMem, hbmOld); - DeleteObject(hbm); - DeleteDC(dcMem); -} - -UINT CSkin::NcCalcRichEditFrame(HWND hwnd, const TWindowData *mwdat, UINT skinID, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC MyWndProc) -{ - NCCALCSIZE_PARAMS *nccp = (NCCALCSIZE_PARAMS *)lParam; - BOOL bReturn = FALSE; - - if (CSkin::m_DisableScrollbars) { - SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VSCROLL); - EnableScrollBar(hwnd, SB_VERT, ESB_DISABLE_BOTH); - ShowScrollBar(hwnd, SB_VERT, FALSE); - } - - LRESULT orig = mir_callNextSubclass(hwnd, MyWndProc, msg, wParam, lParam); - if (0 == mwdat) - return orig; - - if (CSkin::m_skinEnabled) { - CSkinItem *item = &SkinItems[skinID]; - if (!item->IGNORED) - return WVR_REDRAW; - } - if (mwdat->hTheme && wParam) { - RECT rcClient; - HDC hdc = GetDC(GetParent(hwnd)); - - if (GetThemeBackgroundContentRect(mwdat->hTheme, hdc, 1, 1, &nccp->rgrc[0], &rcClient) == S_OK) { - if (EqualRect(&rcClient, &nccp->rgrc[0])) - InflateRect(&rcClient, -1, -1); - CopyRect(&nccp->rgrc[0], &rcClient); - bReturn = TRUE; - } - ReleaseDC(GetParent(hwnd), hdc); - if (bReturn) - return WVR_REDRAW; - else - return orig; - } - if ((mwdat->sendMode & SMODE_MULTIPLE || mwdat->sendMode & SMODE_CONTAINER || - mwdat->fEditNotesActive || mwdat->sendMode & SMODE_SENDLATER) && skinID == ID_EXTBKINPUTAREA) { - InflateRect(&nccp->rgrc[0], -1, -1); - return WVR_REDRAW; - } - return orig; -} - -/* - * process WM_NCPAINT for the rich edit control. Draw a visual style border and avoid classic static edge / client edge - * may also draw a colorized border around the control - */ - -UINT CSkin::DrawRichEditFrame(HWND hwnd, const TWindowData *mwdat, UINT skinID, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC OldWndProc) -{ - // do default processing (otherwise, NO scrollbar as it is painted in NC_PAINT) - LRESULT result = mir_callNextSubclass(hwnd, OldWndProc, msg, wParam, lParam); - if (0 == mwdat) - return result; - - BOOL isEditNotesReason = ((mwdat->fEditNotesActive) && (skinID == ID_EXTBKINPUTAREA)); - BOOL isSendLaterReason = ((mwdat->sendMode & SMODE_SENDLATER) && (skinID == ID_EXTBKINPUTAREA)); - BOOL isMultipleReason = ((skinID == ID_EXTBKINPUTAREA) && (mwdat->sendMode & SMODE_MULTIPLE || mwdat->sendMode & SMODE_CONTAINER)); - - HDC hdc = GetWindowDC(hwnd); - RECT rcWindow; - POINT pt; - LONG left_off, top_off, right_off, bottom_off; - LONG dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE); - LONG dwExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); - - GetWindowRect(hwnd, &rcWindow); - pt.x = pt.y = 0; - ClientToScreen(hwnd, &pt); - left_off = pt.x - rcWindow.left; - if (dwStyle & WS_VSCROLL && dwExStyle & WS_EX_RTLREADING) - left_off -= PluginConfig.m_ncm.iScrollWidth; - top_off = pt.y - rcWindow.top; - - CSkinItem *item = &SkinItems[skinID]; - if (CSkin::m_skinEnabled && !item->IGNORED) { - right_off = item->MARGIN_RIGHT; - bottom_off = item->MARGIN_BOTTOM; - } - else { - right_off = left_off; - bottom_off = top_off; - } - - rcWindow.right -= rcWindow.left; - rcWindow.bottom -= rcWindow.top; - rcWindow.left = rcWindow.top = 0; - - ExcludeClipRect(hdc, left_off, top_off, rcWindow.right - right_off, rcWindow.bottom - bottom_off); - if (CSkin::m_skinEnabled && !item->IGNORED) { - ReleaseDC(hwnd, hdc); - return result; - } - if (isMultipleReason || isEditNotesReason || isSendLaterReason) { - HBRUSH br = CreateSolidBrush(isMultipleReason ? RGB(255, 130, 130) : (isEditNotesReason ? RGB(80, 255, 80) : RGB(80, 80, 255))); - FillRect(hdc, &rcWindow, br); - DeleteObject(br); - } else { - if (PluginConfig.m_cRichBorders) { - HBRUSH br = CreateSolidBrush(PluginConfig.m_cRichBorders); - FillRect(hdc, &rcWindow, br); - DeleteObject(br); - } - else - DrawThemeBackground(mwdat->hTheme, hdc, 1, 1, &rcWindow, &rcWindow); - } - ReleaseDC(hwnd, hdc); - return result; -} - -/** - * convert a html-style color string (without the #) to a 32bit COLORREF value - * - * @param szSource TCHAR*: the color value as string. format: - * html-style without the leading #. e.g. - * "f3e355" - * - * @return COLORREF representation of the string value. - */ -DWORD __fastcall CSkin::HexStringToLong(const TCHAR *szSource) -{ - TCHAR *stopped; - COLORREF clr = _tcstol(szSource, &stopped, 16); - if (clr == -1) - return clr; - return(RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr))); -} - -/** - * Render text to the given HDC. This function is aero aware and will use uxtheme DrawThemeTextEx() when needed. - * Paramaters are pretty much comparable to GDI DrawText() API - * - * @return - */ - -int CSkin::RenderText(HDC hdc, HANDLE hTheme, const TCHAR *szText, RECT *rc, DWORD dtFlags, const int iGlowSize, COLORREF clr, bool fForceAero) -{ - if ((PluginConfig.m_bIsVista && !CSkin::m_skinEnabled && hTheme) || fForceAero) { - DTTOPTS dto = {0}; - dto.dwSize = sizeof(dto); - if (iGlowSize && (M.isAero() || fForceAero)) { - dto.iGlowSize = iGlowSize; - dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE; - } - else { - dto.dwFlags = DTT_TEXTCOLOR|DTT_COMPOSITED;//|DTT_SHADOWTYPE|DTT_SHADOWOFFSET|DTT_SHADOWCOLOR|DTT_BORDERSIZE|DTT_BORDERCOLOR; - dto.crText = clr; - } - dto.iBorderSize = 10; - return(CMimAPI::m_pfnDrawThemeTextEx(hTheme, hdc, BP_PUSHBUTTON, PBS_NORMAL, szText, -1, dtFlags, rc, &dto)); - } - else { - ::SetTextColor(hdc, clr); - return(::DrawText(hdc, szText, -1, rc, dtFlags)); - } -} - -/** - * Resize a bitmap using image service. The function does not care about the image aspect ratio. - * The caller is responsible to submit proper values for the desired height and width. - * - * @param hBmpSrc HBITMAP: the source bitmap - * @param width LONG: width of the destination bitmap - * @param height LONG: height of the new bitmap - * @param mustFree bool: indicates that the new bitmap had been - *resized and either the source or destination - *bitmap should be freed. - * - * @return HBTIAMP: handle to a bitmap with the desired size. - */ -HBITMAP CSkin::ResizeBitmap(HBITMAP hBmpSrc, LONG width, LONG height, bool &mustFree) -{ - BITMAP bm; - - GetObject(hBmpSrc, sizeof(bm), &bm); - if (bm.bmHeight != height || bm.bmWidth != width) { - ::ResizeBitmap rb; - rb.size = sizeof(rb); - rb.fit = RESIZEBITMAP_STRETCH; - rb.max_height = height; - rb.max_width = width; - rb.hBmp = hBmpSrc; - - HBITMAP hbmNew = (HBITMAP)CallService(MS_IMG_RESIZE, (WPARAM)&rb, 0); - if (hbmNew != hBmpSrc) - mustFree = true; - return(hbmNew); - } - else { - mustFree = false; - return(hBmpSrc); - } -} - -/** - * Draw the given skin item to the target rectangle and dc - * - * @param hdc HDC: device context - * @param rc RECT: target rectangle - * @param item CSkinItem*: fully initialiized skin item - * - * @return bool: true if the item has been painted, false if not - * (only reason: the ignore flag in the item is set). - */ -bool __fastcall CSkin::DrawItem(const HDC hdc, const RECT *rc, const CSkinItem *item) -{ - if (!item->IGNORED) { - ::DrawAlpha(hdc, const_cast(rc), item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, - item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); - return true; - } - return false; -} - -/** - * Create a 32bit RGBA bitmap, compatible for rendering with alpha channel. - * Required by anything which would render on a transparent aero surface. - * the image is a "bottom up" bitmap, as it has a negative - * height. This is a requirement for some UxTheme APIs (e.g. - * DrawThemeTextEx). - * - * @param rc RECT &: the rectangle describing the target area. - * @param dc The device context for which the bitmap should be created. - * - * @return HBITMAP: handle to the bitmap created. - */ -HBITMAP CSkin::CreateAeroCompatibleBitmap(const RECT &rc, HDC dc) -{ - BITMAPINFO dib = {0}; - - dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - dib.bmiHeader.biWidth = rc.right - rc.left; - dib.bmiHeader.biHeight = -(rc.bottom - rc.top); - dib.bmiHeader.biPlanes = 1; - dib.bmiHeader.biBitCount = 32; - dib.bmiHeader.biCompression = BI_RGB; - - return(CreateDIBSection(dc, &dib, DIB_RGB_COLORS, NULL, NULL, 0 )); -} - -/** - * Map a given rectangle within the window, specified by hwndClient - * to the client area of another window. - * - * @param hwndClient HWND: Client window - * @param hwndParent HWND: The window to which the coordinates should be mapped - * @param rc RECT &: Rectangular area within the client area of hwndClient. - * - *It will receive the transformed coordinates, relative to the client area of hwndParent - */ -void CSkin::MapClientToParent(HWND hwndClient, HWND hwndParent, RECT &rc) -{ - POINT pt; - - LONG cx = rc.right - rc.left; - LONG cy = rc.bottom - rc.top; - pt.x = rc.left; pt.y = rc.top; - - ::ClientToScreen(hwndClient, &pt); - ::ScreenToClient(hwndParent, &pt); - - rc.top = pt.y; - rc.left = pt.x; - rc.right = rc.left + cx; - rc.bottom = rc.top + cy; -} - -/** - * Draw the background for the message window tool bar - * - * @param dat _MessageWindowData *: structure describing the message session - * - * @param hdc HDC: handle to the device context in which painting should occur. - * @param rcWindow RECT &: The window rectangle of the message dialog window - */ -void CSkin::RenderToolbarBG(const TWindowData *dat, HDC hdc, const RECT &rcWindow) -{ - if (dat) { - if (dat->pContainer->dwFlags & CNT_HIDETOOLBAR) - return; - - bool bAero = M.isAero(); - bool fTbColorsValid = PluginConfig.m_tbBackgroundHigh && PluginConfig.m_tbBackgroundLow; - BYTE bAlphaOffset = 0; - BOOL fMustDrawNonThemed = ((bAero || fTbColorsValid) && !M.GetByte(SRMSGMOD_T, "forceThemedToolbar", 0)); - RECT rc, rcToolbar; - POINT pt; - - if (!(dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR)) { - ::GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_CHAT ? IDC_CHAT_LOG : IDC_LOG), &rc); - pt.y = rc.bottom + 0; - ::ScreenToClient(dat->hwnd, &pt); - rcToolbar.top = pt.y; - rcToolbar.left = 0; - rcToolbar.right = rcWindow.right; - - if (dat->bType == SESSIONTYPE_IM) { - if (dat->dwFlags & MWF_ERRORSTATE) - rcToolbar.top += ERRORPANEL_HEIGHT; - if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED || dat->bNotOnList) { - rcToolbar.top += 20; - RECT rcAdd; - rcAdd.left = 0; rcAdd.right = rcToolbar.right - rcToolbar.left; - rcAdd.bottom = rcToolbar.top - 1; - rcAdd.top = rcAdd.bottom - 20; - ::DrawEdge(hdc, &rcAdd, BDR_RAISEDINNER | BDR_RAISEDOUTER, BF_RECT | BF_SOFT | BF_FLAT); - } - } - - ::GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_CHAT ? IDC_CHAT_MESSAGE : IDC_MESSAGE), &rc); - pt.y = rc.top - (dat->bIsAutosizingInput ? 1 : 2); - ::ScreenToClient(dat->hwnd, &pt); - rcToolbar.bottom = pt.y; - } - else { - GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_CHAT ? IDC_CHAT_MESSAGE : IDC_MESSAGE), &rc); - pt.y = rc.bottom - (dat->bType == SESSIONTYPE_IM ? 2 : 2); - ScreenToClient(dat->hwnd, &pt); - rcToolbar.top = pt.y + 1; - rcToolbar.left = 0; - rcToolbar.right = rcWindow.right; - rcToolbar.bottom = rcWindow.bottom; - } - LONG cx = rcToolbar.right - rcToolbar.left; - LONG cy = rcToolbar.bottom - rcToolbar.top; - - RECT rcCachedToolbar = {0}; - rcCachedToolbar.right = cx; - rcCachedToolbar.bottom = cy; - - if (dat->pContainer->cachedToolbarDC == 0) - dat->pContainer->cachedToolbarDC = ::CreateCompatibleDC(hdc); - - if (dat->pContainer->szOldToolbarSize.cx != cx || dat->pContainer->szOldToolbarSize.cy != cy) { - if (dat->pContainer->oldhbmToolbarBG) { - ::SelectObject(dat->pContainer->cachedToolbarDC, dat->pContainer->oldhbmToolbarBG); - ::DeleteObject(dat->pContainer->hbmToolbarBG); - } - dat->pContainer->hbmToolbarBG = CSkin::CreateAeroCompatibleBitmap(rcCachedToolbar, hdc);// ::CreateCompatibleBitmap(hdc, cx, cy); - dat->pContainer->oldhbmToolbarBG = reinterpret_cast(::SelectObject(dat->pContainer->cachedToolbarDC, dat->pContainer->hbmToolbarBG)); - } - dat->pContainer->szOldToolbarSize.cx = cx; - dat->pContainer->szOldToolbarSize.cy = cy; - - if (!fMustDrawNonThemed && M.isVSThemed()) { - DrawThemeBackground(dat->hThemeToolbar, dat->pContainer->cachedToolbarDC, 6, 1, &rcCachedToolbar, &rcCachedToolbar); - dat->pContainer->bTBRenderingMode = 1; // tell TSButton how to render the tool bar buttons - } - else { - dat->pContainer->bTBRenderingMode = (M.isVSThemed() ? 1 : 0); - m_tmp_tb_high = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh : - ((bAero && m_pCurrentAeroEffect) ? m_pCurrentAeroEffect->m_clrToolbar : ::GetSysColor(COLOR_3DFACE)); - m_tmp_tb_low = PluginConfig.m_tbBackgroundLow ? PluginConfig.m_tbBackgroundLow : - ((bAero && m_pCurrentAeroEffect) ? m_pCurrentAeroEffect->m_clrToolbar2 : ::GetSysColor(COLOR_3DFACE)); - - bAlphaOffset = PluginConfig.m_tbBackgroundHigh ? 40 : 0; - ::DrawAlpha(dat->pContainer->cachedToolbarDC, &rcCachedToolbar, m_tmp_tb_high, 55 + bAlphaOffset, m_tmp_tb_low, 0, 9, 0, 0, 0); - } - - ::BitBlt(hdc, rcToolbar.left, rcToolbar.top, cx, cy, - dat->pContainer->cachedToolbarDC, 0, 0, SRCCOPY); - } -} - -/** - * Initiate a buffered paint operation - * - * @param hdcSrc The source device context (usually obtained by BeginPaint()) - * @param rc RECT&: the target rectangle that receives the painting - * @param hdcOut HDC& (out) receives the buffered device context handle - * - * @return - */ -HANDLE CSkin::InitiateBufferedPaint(const HDC hdcSrc, RECT& rc, HDC& hdcOut) -{ - HANDLE hbp = CMimAPI::m_pfnBeginBufferedPaint(hdcSrc, &rc, BPBF_TOPDOWNDIB, NULL, &hdcOut); - return(hbp); -} - -/** - * finalize buffered paint cycle and apply (if applicable) the global alpha value - * - * @param hbp HANDLE: handle of the buffered paint context - * @param rc RECT*: target rectangly where alpha value should be applied - */ -void CSkin::FinalizeBufferedPaint(HANDLE hbp, RECT *rc) -{ - if (m_pCurrentAeroEffect && m_pCurrentAeroEffect->m_finalAlpha > 0) - CMimAPI::m_pfnBufferedPaintSetAlpha(hbp, rc, m_pCurrentAeroEffect->m_finalAlpha); - CMimAPI::m_pfnEndBufferedPaint(hbp, TRUE); -} -/** - * Apply an effect to a aero glass area - * - * @param hdc HDC: device context - * @param rc RECT*: target rectangle - * @param iEffectArea - * int: area identifier (specifies which area we are drawing, so that allows to - * have different effects on different areas). - * Area can be the status bar, info panel, menu - * bar etc. - * @param hbp HANDLE: handle to a buffered paint identifier. - * default is none, needed forsome special - * effects. default paramenter is 0 - */ - -void CSkin::ApplyAeroEffect(const HDC hdc, const RECT *rc, int iEffectArea, HANDLE hbp) -{ - if (m_pCurrentAeroEffect == 0 || m_aeroEffect == AERO_EFFECT_NONE) - return; - - if (m_pCurrentAeroEffect->pfnEffectRenderer) - m_pCurrentAeroEffect->pfnEffectRenderer(hdc, rc, iEffectArea); -} - -/** aero effect callbacks - * - */ - -void CSkin::AeroEffectCallback_Milk(const HDC hdc, const RECT *rc, int iEffectArea) -{ - if (iEffectArea < 0x1000) { - int alpha = (iEffectArea == AERO_EFFECT_AREA_INFOPANEL) ? m_pCurrentAeroEffect->m_baseAlpha : 40; - if (iEffectArea == AERO_EFFECT_AREA_MENUBAR) - alpha = 90; - BYTE color2_trans = (iEffectArea == AERO_EFFECT_AREA_MENUBAR) ? 0 : 1; - DWORD corner = (iEffectArea == AERO_EFFECT_AREA_INFOPANEL) ? m_pCurrentAeroEffect->m_cornerRadius : 6; - - DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, alpha, m_pCurrentAeroEffect->m_gradientColor, - color2_trans, m_pCurrentAeroEffect->m_gradientType, m_pCurrentAeroEffect->m_cornerType, corner, 0); - } -} - -void CSkin::AeroEffectCallback_Carbon(const HDC hdc, const RECT *rc, int iEffectArea) -{ - if (iEffectArea < 0x1000) - DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, m_pCurrentAeroEffect->m_baseAlpha, - m_pCurrentAeroEffect->m_gradientColor, 0, m_pCurrentAeroEffect->m_gradientType, - m_pCurrentAeroEffect->m_cornerType, m_pCurrentAeroEffect->m_cornerRadius, 0); -} - -void CSkin::AeroEffectCallback_Solid(const HDC hdc, const RECT *rc, int iEffectArea) -{ - if (iEffectArea < 0x1000) { - if (iEffectArea == AERO_EFFECT_AREA_SIDEBAR_LEFT) - ::DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, m_pCurrentAeroEffect->m_baseAlpha, - m_pCurrentAeroEffect->m_gradientColor, 0, GRADIENT_TB + 1, - 0, 2, 0); - else - ::DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, m_pCurrentAeroEffect->m_baseAlpha, - m_pCurrentAeroEffect->m_gradientColor, 0, m_pCurrentAeroEffect->m_gradientType, - m_pCurrentAeroEffect->m_cornerType, m_pCurrentAeroEffect->m_cornerRadius, 0); - } - else { - BYTE bGradient = (iEffectArea & AERO_EFFECT_AREA_TAB_BOTTOM ? GRADIENT_BT : GRADIENT_TB) + 1; - ::DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, 70, - m_pCurrentAeroEffect->m_gradientColor, 1, bGradient, - m_pCurrentAeroEffect->m_cornerType, m_pCurrentAeroEffect->m_cornerRadius, 0); - } -} - -void CSkin::initAeroEffect() -{ - if (m_BrushBack) { - ::DeleteObject(m_BrushBack); - m_BrushBack = 0; - } - if (PluginConfig.m_bIsVista && m_aeroEffect > AERO_EFFECT_NONE && m_aeroEffect < AERO_EFFECT_LAST) { - m_currentAeroEffect = m_aeroEffects[m_aeroEffect]; - m_pCurrentAeroEffect = &m_currentAeroEffect; - m_glowSize = m_pCurrentAeroEffect->m_glowSize; - - if (m_pCurrentAeroEffect->m_clrToolbar == -1) - m_pCurrentAeroEffect->m_clrToolbar = PluginConfig.m_ipBackgroundGradientHigh; - - if (m_pCurrentAeroEffect->m_clrToolbar2 == -1) - m_pCurrentAeroEffect->m_clrToolbar2 = PluginConfig.m_ipBackgroundGradient; - else if (m_pCurrentAeroEffect->m_clrToolbar2 == 0) - m_pCurrentAeroEffect->m_clrToolbar2 = m_dwmColorRGB; - - if (m_aeroEffect == AERO_EFFECT_CUSTOM || m_aeroEffect == AERO_EFFECT_SOLID) { - m_pCurrentAeroEffect->m_baseColor = PluginConfig.m_ipBackgroundGradientHigh; - m_pCurrentAeroEffect->m_gradientColor = PluginConfig.m_ipBackgroundGradient; - if (m_aeroEffect == AERO_EFFECT_CUSTOM) - m_pCurrentAeroEffect->m_clrBack = PluginConfig.m_ipBackgroundGradientHigh; - } - - m_BrushBack = ::CreateSolidBrush(m_pCurrentAeroEffect->m_clrBack); - } else { - m_pCurrentAeroEffect = 0; - m_glowSize = 10; - m_BrushBack = ::CreateSolidBrush(0); - } - - for (TContainerData *p = pFirstContainer; p; p = p->pNext) { - InvalidateRect(GetDlgItem(p->hwnd, IDC_MSGTABS), NULL, TRUE); - InvalidateRect(p->hwnd, NULL, TRUE); - if (IsWindow(GetDlgItem(p->hwnd, 5000))) - InvalidateRect(GetDlgItem(p->hwnd, 5000), NULL, TRUE); - } -} - -void CSkin::setAeroEffect(LRESULT effect) -{ - if (effect == -1) - effect = static_cast(M.GetDword(SRMSGMOD_T, "aerostyle", AERO_EFFECT_NONE)); - - if (effect >= 0 && effect < AERO_EFFECT_LAST) - m_aeroEffect = (UINT)effect; - else - m_aeroEffect = AERO_EFFECT_NONE; - - initAeroEffect(); - db_set_dw(0, SRMSGMOD_T, "aerostyle", m_aeroEffect); -} - -/** - * extract the aero skin images from the DLL and store them in - * the private data folder. - * runs at every startup - */ -void CSkin::extractSkinsAndLogo(bool fForceOverwrite) const -{ - TCHAR tszBasePath[MAX_PATH]; - mir_sntprintf(tszBasePath, MAX_PATH, _T("%s"), M.getDataPath()); - if (tszBasePath[lstrlen(tszBasePath) - 1] != '\\') - _tcscat(tszBasePath, _T("\\")); - - CreateDirectoryTreeT(tszBasePath); - - m_fAeroSkinsValid = true; - - for (int i=0; i < SIZEOF(my_default_skin); i++) - if ( !Utils::extractResource(g_hInst, my_default_skin[i].ulID, _T("SKIN_GLYPH"), tszBasePath, my_default_skin[i].tszName, fForceOverwrite)) - m_fAeroSkinsValid = false; -} - -/** - * redraw the splitter area between the message input and message log - * area only - */ -void CSkin::UpdateToolbarBG(TWindowData *dat, DWORD dwRdwOptFlags) -{ - RECT rcUpdate, rcTmp; - POINT pt; - - if (dat) { - ::GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &rcTmp); - - pt.x = rcTmp.left; - pt.y = rcTmp.top; - ::ScreenToClient(dat->hwnd, &pt); - - rcUpdate.left = 0; - rcUpdate.top = pt.y; - - ::GetClientRect(dat->hwnd, &rcTmp); - rcUpdate.right = rcTmp.right; - rcUpdate.bottom = rcTmp.bottom; - - if (M.isAero() || M.isDwmActive()) - dat->fLimitedUpdate = true; // skip unrelevant window updates when we have buffered paint avail - ::RedrawWindow(dat->hwnd, &rcUpdate, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW); - ::BB_RedrawButtons(dat); - dat->fLimitedUpdate = false; - } -} - -/** - * fill a background area with the default color. This can be either the configured - * fill color or default system color. - * - * @param hdc: device context - * @param rc: area to fill. - */ -void CSkin::FillBack(const HDC hdc, RECT* rc) -{ - if (0 == CSkin::m_BrushFill) { - if (PluginConfig.m_fillColor) - CSkin::m_BrushFill = ::CreateSolidBrush(PluginConfig.m_fillColor); - } - - if (PluginConfig.m_fillColor) - ::FillRect(hdc, rc, CSkin::m_BrushFill); - else - ::FillRect(hdc, rc, GetSysColorBrush(COLOR_3DFACE)); -} +/* + * Miranda NG: the free IM client for Microsoft* Windows* + * + * 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 + * + * Implements the skinning engine and most parts of the aero support in + * tabSRMM 3.x+ + * + */ + +#include "commonheaders.h" + +static SKINDESC my_default_skin[] = { + IDR_SKIN_AERO, _T("tabskin_aero.png"), + IDR_SKIN_AERO_GLOW, _T("tabskin_aero_glow.png"), + IDR_SKIN_AERO_SWITCHBAR, _T("tabskin_aero_button.png"), + IDR_SKIN_LOGO, _T("unknown.png") +}; + +CSkin* Skin = 0; + + +static void __inline gradientVertical(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, + ULONG ulBitmapHeight, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, + UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 y, UCHAR *ubAlpha); + +static void __inline gradientHorizontal(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, + ULONG ulBitmapWidth, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, + UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 x, UCHAR *ubAlpha); + + +UINT nextButtonID; +ButtonSet g_ButtonSet = {0}; + +#define NR_MAXSKINICONS 100 + +/* + * initialize static class data +*/ + +int CSkin::m_bAvatarBorderType = 0; +COLORREF CSkin::m_avatarBorderClr = 0; +COLORREF CSkin::m_tmp_tb_low = 0, CSkin::m_tmp_tb_high = 0; +COLORREF CSkin::m_sideBarContainerBG; +BLENDFUNCTION CSkin::m_default_bf = {0}; // a global blend function, used in various places + // when you use it, reset SourceConstantAlpha to 255 and + // the blending mode to AC_SRC_ALPHA. + +bool CSkin::m_bClipBorder = false, CSkin::m_DisableScrollbars = false, + CSkin::m_skinEnabled = false, CSkin::m_frameSkins = false; + +char CSkin::m_SkinnedFrame_left = 0, CSkin::m_SkinnedFrame_right = 0, + CSkin::m_SkinnedFrame_bottom = 0, CSkin::m_SkinnedFrame_caption = 0; + +char CSkin::m_realSkinnedFrame_left = 0; +char CSkin::m_realSkinnedFrame_right = 0; +char CSkin::m_realSkinnedFrame_bottom = 0; +char CSkin::m_realSkinnedFrame_caption = 0; + +int CSkin::m_titleBarLeftOff = 0, CSkin::m_titleButtonTopOff = 0, CSkin::m_captionOffset = 0, CSkin::m_captionPadding = 0, + CSkin::m_titleBarRightOff = 0, CSkin::m_sidebarTopOffset = 0, CSkin::m_sidebarBottomOffset = 0, CSkin::m_bRoundedCorner = 0; + +CImageItem* CSkin::m_switchBarItem = 0, + *CSkin::m_tabTop = 0, + *CSkin::m_tabBottom = 0, + *CSkin::m_tabGlowTop = 0, + *CSkin::m_tabGlowBottom = 0; + +bool CSkin::m_fAeroSkinsValid = false; + +SIZE CSkin::m_titleBarButtonSize = {0}; + +COLORREF CSkin::m_ContainerColorKey = 0, CSkin::m_dwmColorRGB = 0, CSkin::m_DefaultFontColor = 0; +HBRUSH CSkin::m_ContainerColorKeyBrush = 0, CSkin::m_MenuBGBrush = 0; + +HPEN CSkin::m_SkinLightShadowPen = 0, CSkin::m_SkinDarkShadowPen = 0; + +HICON CSkin::m_closeIcon = 0, CSkin::m_maxIcon = 0, CSkin::m_minIcon = 0; + +UINT CSkin::m_aeroEffect = 0; +DWORD CSkin::m_glowSize = 0; +HBRUSH CSkin::m_BrushBack = 0, CSkin::m_BrushFill = 0; + +HBITMAP CSkin::m_tabCloseBitmap = 0, CSkin::m_tabCloseOldBitmap = 0; +HDC CSkin::m_tabCloseHDC = 0; + +/* + * aero effects + */ + +AeroEffect CSkin::m_currentAeroEffect; +AeroEffect* CSkin::m_pCurrentAeroEffect = 0; + +AeroEffect CSkin::m_aeroEffects[AERO_EFFECT_LAST] = { + { + LPGENT("No effect"), 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0 + }, + { + LPGENT("Milky Glass"), + 0xf5f5f5, /* base color */ + 0xaaaaaa, /* gradient color */ + 70, /* base alpha */ + 0, /* final alpha */ + CORNER_ALL, /* corner type */ + GRADIENT_TB + 1, /* gradient type */ + 8, /* corner radius */ + 8, /* glow size (0 means no glowing text, colors can be used) */ + 0, /* background color (black = transparency) */ + 0xf0f0f0, /* toolbar first color (if 0, use custom gradient color) */ + 0, /* toolbar 2nd gradient color (0 = use aero theme color, -1 = use custom gradient color */ + AeroEffectCallback_Milk /* callback function to render the effect */ + }, + { + LPGENT("Carbon"), + 0xf0f0f0, + 0x000000, + 75, + 0, + CORNER_ALL, + GRADIENT_TB + 1, + 6, + 8, + 0, + 0xf0f0f0, + 0, + AeroEffectCallback_Carbon + }, + { + LPGENT("Semi transparent, custom colors"), + 0xffffff, + 0x444444, + 60, + 0, + CORNER_ALL, + GRADIENT_TB + 1, + 6, + 0, + 0x0, + 0xf0f0f0, + 0, + AeroEffectCallback_Solid + }, + { + LPGENT("Silver shadow"), + 0xffffff, + 0xa0a0a0, + 80, + 220, + 0, + GRADIENT_TB + 1, + 1, + 0, + 0xc0c0c0, + 0xf0f0f0, + 0x707070, + AeroEffectCallback_Solid + }, + { + LPGENT("Custom (use own gradient colors)"), + 0xffffff, + 0xa0a0a0, + 80, + 220, + 0, + GRADIENT_TB + 1, + 1, + 0, + 0xc0c0c0, + -1, + -1, + AeroEffectCallback_Solid + } +}; + +/* + * definition of the availbale skin items + */ + +CSkinItem SkinItems[] = { + {_T("Container"), "TSKIN_Container", ID_EXTBKCONTAINER, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Toolbar"), "TSKIN_Container", ID_EXTBKBUTTONBAR, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("{-}Buttonpressed"), "TSKIN_BUTTONSPRESSED", ID_EXTBKBUTTONSPRESSED, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Buttonnotpressed"), "TSKIN_BUTTONSNPRESSED", ID_EXTBKBUTTONSNPRESSED, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Buttonmouseover"), "TSKIN_BUTTONSMOUSEOVER", ID_EXTBKBUTTONSMOUSEOVER, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Infopanelfield"), "TSKIN_INFOPANELFIELD", ID_EXTBKINFOPANEL, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Titlebutton"), "TSKIN_TITLEBUTTON", ID_EXTBKTITLEBUTTON, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Titlebuttonmouseover"), "TSKIN_TITLEBUTTONHOVER", ID_EXTBKTITLEBUTTONMOUSEOVER, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Titlebuttonpressed"), "TSKIN_TITLEBUTTONPRESSED", ID_EXTBKTITLEBUTTONPRESSED, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabpage"), "TSKIN_TABPAGE", ID_EXTBKTABPAGE, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabitem"), "TSKIN_TABITEM", ID_EXTBKTABITEM, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabitem_active"), "TSKIN_TABITEMACTIVE", ID_EXTBKTABITEMACTIVE, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabitem_bottom"), "TSKIN_TABITEMBOTTOM", ID_EXTBKTABITEMBOTTOM, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabitem_active_bottom"), "TSKIN_TABITEMACTIVEBOTTOM", ID_EXTBKTABITEMACTIVEBOTTOM, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Frame"), "TSKIN_FRAME", ID_EXTBKFRAME, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("MessageLog"), "TSKIN_MLOG", ID_EXTBKHISTORY, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("InputArea"), "TSKIN_INPUT", ID_EXTBKINPUTAREA, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("FrameInactive"), "TSKIN_FRAMEINACTIVE", ID_EXTBKFRAMEINACTIVE, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabitem_hottrack"), "TSKIN_TABITEMHOTTRACK", ID_EXTBKTABITEMHOTTRACK, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Tabitem_hottrack_bottom"), "TSKIN_TABITEMHOTTRACKBOTTOM", ID_EXTBKTABITEMHOTTRACKBOTTOM, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Statusbarpanel"), "TSKIN_STATUSBARPANEL", ID_EXTBKSTATUSBARPANEL, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Statusbar"), "TSKIN_STATUSBAR", ID_EXTBKSTATUSBAR, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("Userlist"), "TSKIN_USERLIST", ID_EXTBKUSERLIST, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE + }, {_T("InfoPanelBackground"), "TSKIN_INFOPANELBG", ID_EXTBKINFOPANELBG, + 8, CLCDEFAULT_CORNER, + 0xf0f0f0, 0x42b1ff, 1, CLCDEFAULT_TEXTCOLOR, 40, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE, 0, 0 + }, {_T("Sidebar Background"), "TSKIN_SIDEBARBG", ID_EXTBKSIDEBARBG, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + 0xb2e1ff, 0xb2e1ff, 1, CLCDEFAULT_TEXTCOLOR, 40, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE, 0, 0 + } +}; + +static CSkinItem _defInfoPanel = { + _T("InfoPanelBackground"), "TSKIN_INFOPANELBG", ID_EXTBKINFOPANELBG, + 8, CLCDEFAULT_CORNER, + 0xf0f0f0, 0x62caff, 0, CLCDEFAULT_TEXTCOLOR, 255, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE, 0, 0 +}; + +static BYTE __inline percent_to_byte(UINT32 percent) +{ + return(BYTE)((FLOAT)(((FLOAT) percent) / 100) * 255); +} + +static COLORREF __inline revcolref(COLORREF colref) +{ + return RGB(GetBValue(colref), GetGValue(colref), GetRValue(colref)); +} + +static DWORD __inline argb_from_cola(COLORREF col, UINT32 alpha) +{ + return((BYTE) percent_to_byte(alpha) << 24 | col); +} + + +void TSAPI DrawAlpha(HDC hDC, PRECT rc, DWORD clr_base, int alpha, DWORD clr_dest, BYTE clr_dest_trans, BYTE bGradient, + BYTE bCorner, DWORD dwRadius, CImageItem *imageItem) +{ + HBRUSH BrMask; + HBRUSH holdbrush; + HDC hdc; + BLENDFUNCTION bf; + HBITMAP hbitmap; + HBITMAP holdbitmap; + BITMAPINFO bmi; + VOID *pvBits; + UINT32 x, y; + ULONG ulBitmapWidth, ulBitmapHeight; + UCHAR ubAlpha = 0xFF; + UCHAR ubRedFinal = 0xFF; + UCHAR ubGreenFinal = 0xFF; + UCHAR ubBlueFinal = 0xFF; + UCHAR ubRed; + UCHAR ubGreen; + UCHAR ubBlue; + UCHAR ubRed2; + UCHAR ubGreen2; + UCHAR ubBlue2; + FLOAT fAlphaFactor; + + int realx; + + if (rc == NULL) + return; + LONG realHeight = (rc->bottom - rc->top); + LONG realWidth = (rc->right - rc->left); + LONG realHeightHalf = realHeight >> 1; + + if (imageItem) { + imageItem->Render(hDC, rc, false); + return; + } + + if (rc->right < rc->left || rc->bottom < rc->top || (realHeight <= 0) || (realWidth <= 0)) + return; + + /* + * use GDI fast gradient drawing when no corner radi exist + */ + + if (bCorner == 0 && dwRadius == 0) { + GRADIENT_RECT grect; + TRIVERTEX tvtx[2]; + int orig = 1, dest = 0; + + if (bGradient & GRADIENT_LR || bGradient & GRADIENT_TB) { + orig = 0; + dest = 1; + } + + tvtx[0].x = rc->left; + tvtx[0].y = rc->top; + tvtx[1].x = rc->right; + tvtx[1].y = rc->bottom; + + tvtx[orig].Red = (COLOR16)GetRValue(clr_base) << 8; + tvtx[orig].Blue = (COLOR16)GetBValue(clr_base) << 8; + tvtx[orig].Green = (COLOR16)GetGValue(clr_base) << 8; + tvtx[orig].Alpha = (COLOR16) alpha << 8; + + tvtx[dest].Red = (COLOR16)GetRValue(clr_dest) << 8; + tvtx[dest].Blue = (COLOR16)GetBValue(clr_dest) << 8; + tvtx[dest].Green = (COLOR16)GetGValue(clr_dest) << 8; + tvtx[dest].Alpha = (COLOR16) alpha << 8; + + grect.UpperLeft = 0; + grect.LowerRight = 1; + + GdiGradientFill(hDC, tvtx, 2, &grect, 1, (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H); + return; + } + + hdc = CreateCompatibleDC(hDC); + if (!hdc) + return; + + ZeroMemory(&bmi, sizeof(BITMAPINFO)); + + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + + if (bGradient & GRADIENT_ACTIVE && (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL)) { + bmi.bmiHeader.biWidth = ulBitmapWidth = realWidth; + bmi.bmiHeader.biHeight = ulBitmapHeight = 1; + } else if (bGradient & GRADIENT_ACTIVE && (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT)) { + bmi.bmiHeader.biWidth = ulBitmapWidth = 1; + bmi.bmiHeader.biHeight = ulBitmapHeight = realHeight; + } else { + bmi.bmiHeader.biWidth = ulBitmapWidth = 1; + bmi.bmiHeader.biHeight = ulBitmapHeight = 1; + } + + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = ulBitmapWidth * ulBitmapHeight * 4; + + if (ulBitmapWidth <= 0 || ulBitmapHeight <= 0) + return; + + hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0); + if (hbitmap == NULL || pvBits == NULL) { + DeleteDC(hdc); + return; + } + + holdbitmap = (HBITMAP)SelectObject(hdc, hbitmap); + + // convert basecolor to RGB and then merge alpha so its ARGB + clr_base = argb_from_cola(revcolref(clr_base), alpha); + clr_dest = argb_from_cola(revcolref(clr_dest), alpha); + + ubRed = (UCHAR)(clr_base >> 16); + ubGreen = (UCHAR)(clr_base >> 8); + ubBlue = (UCHAR) clr_base; + + ubRed2 = (UCHAR)(clr_dest >> 16); + ubGreen2 = (UCHAR)(clr_dest >> 8); + ubBlue2 = (UCHAR) clr_dest; + + //DRAW BASE - make corner space 100% transparent + for (y = 0; y < ulBitmapHeight; y++) { + for (x = 0 ; x < ulBitmapWidth ; x++) { + if (bGradient & GRADIENT_ACTIVE) { + if (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL) { + realx = x + realHeightHalf; + realx = (ULONG) realx > ulBitmapWidth ? ulBitmapWidth : realx; + gradientHorizontal(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapWidth, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, realx, &ubAlpha); + } else if (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) + gradientVertical(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapHeight, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, y, &ubAlpha); + + fAlphaFactor = (float) ubAlpha / (float) 0xff; + ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); + } else { + ubAlpha = percent_to_byte(alpha); + ubRedFinal = ubRed; + ubGreenFinal = ubGreen; + ubBlueFinal = ubBlue; + fAlphaFactor = (float) ubAlpha / (float) 0xff; + + ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); + } + } + } + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = (UCHAR)(clr_base >> 24); + bf.AlphaFormat = AC_SRC_ALPHA; // so it will use our specified alpha value + + GdiAlphaBlend(hDC, rc->left + realHeightHalf, rc->top, (realWidth - realHeightHalf * 2), realHeight, hdc, 0, 0, ulBitmapWidth, ulBitmapHeight, bf); + + SelectObject(hdc, holdbitmap); + DeleteObject(hbitmap); + + // corners + BrMask = CreateSolidBrush(RGB(0xFF, 0x00, 0xFF)); + { + bmi.bmiHeader.biWidth = ulBitmapWidth = realHeightHalf; + bmi.bmiHeader.biHeight = ulBitmapHeight = realHeight; + bmi.bmiHeader.biSizeImage = ulBitmapWidth * ulBitmapHeight * 4; + + if (ulBitmapWidth <= 0 || ulBitmapHeight <= 0) { + DeleteDC(hdc); + DeleteObject(BrMask); + return; + } + + // TL+BL CORNER + hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0); + + if (hbitmap == 0 || pvBits == NULL) { + DeleteObject(BrMask); + DeleteDC(hdc); + return; + } + + holdbrush = (HBRUSH)SelectObject(hdc, BrMask); + holdbitmap = (HBITMAP)SelectObject(hdc, hbitmap); + RoundRect(hdc, -1, -1, ulBitmapWidth * 2 + 1, (realHeight + 1), dwRadius, dwRadius); + + for (y = 0; y < ulBitmapHeight; y++) { + for (x = 0; x < ulBitmapWidth; x++) { + if (((((UINT32 *) pvBits)[x + y * ulBitmapWidth]) << 8) == 0xFF00FF00 || (y < ulBitmapHeight >> 1 && !(bCorner & CORNER_BL && bCorner & CORNER_ACTIVE)) || (y > ulBitmapHeight >> 2 && !(bCorner & CORNER_TL && bCorner & CORNER_ACTIVE))) { + if (bGradient & GRADIENT_ACTIVE) { + if (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL) + gradientHorizontal(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, realWidth, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, x, &ubAlpha); + else if (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) + gradientVertical(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapHeight, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, y, &ubAlpha); + + fAlphaFactor = (float) ubAlpha / (float) 0xff; + ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); + } else { + ubAlpha = percent_to_byte(alpha); + ubRedFinal = ubRed; + ubGreenFinal = ubGreen; + ubBlueFinal = ubBlue; + fAlphaFactor = (float) ubAlpha / (float) 0xff; + + ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); + } + } + } + } + GdiAlphaBlend(hDC, rc->left, rc->top, ulBitmapWidth, ulBitmapHeight, hdc, 0, 0, ulBitmapWidth, ulBitmapHeight, bf); + SelectObject(hdc, holdbitmap); + DeleteObject(hbitmap); + + // TR+BR CORNER + hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0); + + //SelectObject(hdc, BrMask); // already BrMask? + holdbitmap = (HBITMAP)SelectObject(hdc, hbitmap); + RoundRect(hdc, -1 - ulBitmapWidth, -1, ulBitmapWidth + 1, (realHeight + 1), dwRadius, dwRadius); + + for (y = 0; y < ulBitmapHeight; y++) { + for (x = 0; x < ulBitmapWidth; x++) { + if (((((UINT32 *) pvBits)[x + y * ulBitmapWidth]) << 8) == 0xFF00FF00 || (y < ulBitmapHeight >> 1 && !(bCorner & CORNER_BR && bCorner & CORNER_ACTIVE)) || (y > ulBitmapHeight >> 1 && !(bCorner & CORNER_TR && bCorner & CORNER_ACTIVE))) { + if (bGradient & GRADIENT_ACTIVE) { + if (bGradient & GRADIENT_LR || bGradient & GRADIENT_RL) { + realx = x + realWidth; + realx = realx > realWidth ? realWidth : realx; + gradientHorizontal(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, realWidth, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, realx, &ubAlpha); + } else if (bGradient & GRADIENT_TB || bGradient & GRADIENT_BT) + gradientVertical(&ubRedFinal, &ubGreenFinal, &ubBlueFinal, ulBitmapHeight, ubRed, ubGreen, ubBlue, ubRed2, ubGreen2, ubBlue2, bGradient, clr_dest_trans, y, &ubAlpha); + + fAlphaFactor = (float) ubAlpha / (float) 0xff; + ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); + } else { + ubAlpha = percent_to_byte(alpha); + ubRedFinal = ubRed; + ubGreenFinal = ubGreen; + ubBlueFinal = ubBlue; + fAlphaFactor = (float) ubAlpha / (float) 0xff; + + ((UINT32 *) pvBits)[x + y * ulBitmapWidth] = (ubAlpha << 24) | ((UCHAR)(ubRedFinal * fAlphaFactor) << 16) | ((UCHAR)(ubGreenFinal * fAlphaFactor) << 8) | ((UCHAR)(ubBlueFinal * fAlphaFactor)); + } + } + } + } + GdiAlphaBlend(hDC, rc->right - realHeightHalf, rc->top, ulBitmapWidth, ulBitmapHeight, hdc, 0, 0, ulBitmapWidth, ulBitmapHeight, bf); + } + SelectObject(hdc, holdbitmap); + DeleteObject(hbitmap); + SelectObject(hdc, holdbrush); + DeleteObject(BrMask); + DeleteDC(hdc); +} + +void __inline gradientHorizontal(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, ULONG ulBitmapWidth, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 x, UCHAR *ubAlpha) +{ + FLOAT fSolidMulti, fInvSolidMulti; + + // solid to transparent + if (transparent) { + *ubAlpha = (UCHAR)((float) x / (float) ulBitmapWidth * 255); + *ubAlpha = FLG_GRADIENT & GRADIENT_LR ? 0xFF - (*ubAlpha) : (*ubAlpha); + *ubRedFinal = ubRed; + *ubGreenFinal = ubGreen; + *ubBlueFinal = ubBlue; + } else { // solid to solid2 + if (FLG_GRADIENT & GRADIENT_LR) { + fSolidMulti = ((float) x / (float) ulBitmapWidth); + fInvSolidMulti = 1 - fSolidMulti; + } else { + fInvSolidMulti = ((float) x / (float) ulBitmapWidth); + fSolidMulti = 1 - fInvSolidMulti; + } + + *ubRedFinal = (UCHAR)((((float) ubRed * (float) fInvSolidMulti)) + (((float) ubRed2 * (float) fSolidMulti))); + *ubGreenFinal = (UCHAR)((UCHAR)(((float) ubGreen * (float) fInvSolidMulti)) + (((float) ubGreen2 * (float) fSolidMulti))); + *ubBlueFinal = (UCHAR)((((float) ubBlue * (float) fInvSolidMulti)) + (UCHAR)(((float) ubBlue2 * (float) fSolidMulti))); + + *ubAlpha = 0xFF; + } +} + +void __inline gradientVertical(UCHAR *ubRedFinal, UCHAR *ubGreenFinal, UCHAR *ubBlueFinal, ULONG ulBitmapHeight, UCHAR ubRed, UCHAR ubGreen, UCHAR ubBlue, UCHAR ubRed2, UCHAR ubGreen2, UCHAR ubBlue2, DWORD FLG_GRADIENT, BOOL transparent, UINT32 y, UCHAR *ubAlpha) +{ + FLOAT fSolidMulti, fInvSolidMulti; + + // solid to transparent + if (transparent) { + *ubAlpha = (UCHAR)((float) y / (float) ulBitmapHeight * 255); + *ubAlpha = FLG_GRADIENT & GRADIENT_BT ? 0xFF - *ubAlpha : *ubAlpha; + *ubRedFinal = ubRed; + *ubGreenFinal = ubGreen; + *ubBlueFinal = ubBlue; + } else { // solid to solid2 + if (FLG_GRADIENT & GRADIENT_BT) { + fSolidMulti = ((float) y / (float) ulBitmapHeight); + fInvSolidMulti = 1 - fSolidMulti; + } else { + fInvSolidMulti = ((float) y / (float) ulBitmapHeight); + fSolidMulti = 1 - fInvSolidMulti; + } + + *ubRedFinal = (UCHAR)((((float) ubRed * (float) fInvSolidMulti)) + (((float) ubRed2 * (float) fSolidMulti))); + *ubGreenFinal = (UCHAR)((((float) ubGreen * (float) fInvSolidMulti)) + (((float) ubGreen2 * (float) fSolidMulti))); + *ubBlueFinal = (UCHAR)(((float) ubBlue * (float) fInvSolidMulti)) + (UCHAR)(((float) ubBlue2 * (float) fSolidMulti)); + + *ubAlpha = 0xFF; + } +} + +/** + * Renders the image item to the given target device context and rectangle + * + * @param hdc HDC: target device context + * @param rc RECT *: client rectangle inside the target DC. + * @param fIgnoreGlyph: bool: will ignore any glyph item. Set it to true when + * using this function from _outside_ a skin + */ +void __fastcall CImageItem::Render(const HDC hdc, const RECT *rc, bool fIgnoreGlyph) const +{ + BYTE l = m_bLeft, r = m_bRight, t = m_bTop, b = m_bBottom; + LONG width = rc->right - rc->left; + LONG height = rc->bottom - rc->top; + BOOL isGlyph = ((m_dwFlags & IMAGE_GLYPH) && Skin->haveGlyphItem()); + BOOL fCleanUp = TRUE; + HDC hdcSrc = 0; + HBITMAP hbmOld; + LONG srcOrigX = isGlyph ? m_glyphMetrics[0] : 0; + LONG srcOrigY = isGlyph ? m_glyphMetrics[1] : 0; + + if (m_hdc == 0) { + hdcSrc = CreateCompatibleDC(hdc); + hbmOld = (HBITMAP)SelectObject(hdcSrc, isGlyph ? Skin->getGlyphItem()->getHbm() : m_hbm); + } else { + if (fIgnoreGlyph) + hdcSrc = m_hdc; + else + hdcSrc = isGlyph ? Skin->getGlyphItem()->getDC() : m_hdc; + fCleanUp = FALSE; + } + + if (m_dwFlags & IMAGE_FLAG_DIVIDED) { + // top 3 items + + GdiAlphaBlend(hdc, rc->left, rc->top, l, t, hdcSrc, srcOrigX, srcOrigY, l, t, m_bf); + GdiAlphaBlend(hdc, rc->left + l, rc->top, width - l - r, t, hdcSrc, srcOrigX + l, srcOrigY, m_inner_width, t, m_bf); + GdiAlphaBlend(hdc, rc->right - r, rc->top, r, t, hdcSrc, srcOrigX + (m_width - r), srcOrigY, r, t, m_bf); + + // middle 3 items + + GdiAlphaBlend(hdc, rc->left, rc->top + t, l, height - t - b, hdcSrc, srcOrigX, srcOrigY + t, l, m_inner_height, m_bf); + + if ((m_dwFlags & IMAGE_FILLSOLID) && m_fillBrush) { + RECT rcFill; + rcFill.left = rc->left + l; + rcFill.top = rc->top + t; + rcFill.right = rc->right - r; + rcFill.bottom = rc->bottom - b; + FillRect(hdc, &rcFill, m_fillBrush); + } else + GdiAlphaBlend(hdc, rc->left + l, rc->top + t, width - l - r, height - t - b, hdcSrc, srcOrigX + l, srcOrigY + t, m_inner_width, m_inner_height, m_bf); + + GdiAlphaBlend(hdc, rc->right - r, rc->top + t, r, height - t - b, hdcSrc, srcOrigX + (m_width - r), srcOrigY + t, r, m_inner_height, m_bf); + + // bottom 3 items + + GdiAlphaBlend(hdc, rc->left, rc->bottom - b, l, b, hdcSrc, srcOrigX, srcOrigY + (m_height - b), l, b, m_bf); + GdiAlphaBlend(hdc, rc->left + l, rc->bottom - b, width - l - r, b, hdcSrc, srcOrigX + l, srcOrigY + (m_height - b), m_inner_width, b, m_bf); + GdiAlphaBlend(hdc, rc->right - r, rc->bottom - b, r, b, hdcSrc, srcOrigX + (m_width - r), srcOrigY + (m_height - b), r, b, m_bf); + } + else switch (m_bStretch) { + case IMAGE_STRETCH_H: + // tile image vertically, stretch to width + { + LONG top = rc->top; + + do { + if (top + m_height <= rc->bottom) { + GdiAlphaBlend(hdc, rc->left, top, width, m_height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf); + top += m_height; + } else { + GdiAlphaBlend(hdc, rc->left, top, width, rc->bottom - top, hdcSrc, srcOrigX, srcOrigY, m_width, rc->bottom - top, m_bf); + break; + } + } while (true); + break; + } + case IMAGE_STRETCH_V: + // tile horizontally, stretch to height + { + LONG left = rc->left; + + do { + if (left + m_width <= rc->right) { + GdiAlphaBlend(hdc, left, rc->top, m_width, height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf); + left += m_width; + } else { + GdiAlphaBlend(hdc, left, rc->top, rc->right - left, height, hdcSrc, srcOrigX, srcOrigY, rc->right - left, m_height, m_bf); + break; + } + } while (true); + break; + } + case IMAGE_STRETCH_B: + // stretch the image in both directions... + GdiAlphaBlend(hdc, rc->left, rc->top, width, height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf); + break; + } + + if (fCleanUp) { + SelectObject(hdcSrc, hbmOld); + DeleteDC(hdcSrc); + } +} + +static CSkinItem StatusItem_Default = { + _T("Container"), "EXBK_Offline", ID_EXTBKCONTAINER, + CLCDEFAULT_GRADIENT, CLCDEFAULT_CORNER, + CLCDEFAULT_COLOR, CLCDEFAULT_COLOR2, CLCDEFAULT_COLOR2_TRANSPARENT, CLCDEFAULT_TEXTCOLOR, CLCDEFAULT_ALPHA, CLCDEFAULT_MRGN_LEFT, + CLCDEFAULT_MRGN_TOP, CLCDEFAULT_MRGN_RIGHT, CLCDEFAULT_MRGN_BOTTOM, CLCDEFAULT_IGNORE +}; + +static struct { + TCHAR *szIniKey, *szIniName; + char *szSetting; + unsigned int size; + int defaultval; +} _tagSettings[] = { + _T("Global"), _T("SbarHeight"), "S_sbarheight", 1, 22, + _T("ClientArea"), _T("Left"), "S_tborder_outer_left", 1, 0, + _T("ClientArea"), _T("Right"), "S_tborder_outer_right", 1, 0, + _T("ClientArea"), _T("Top"), "S_tborder_outer_top", 1, 0, + _T("ClientArea"), _T("Bottom"), "S_tborder_outer_bottom", 1, 0, + _T("ClientArea"), _T("Inner"), "S_tborder", 1, 0, + _T("Global"), _T("TabTextNormal"), "S_tab_txt_normal", 5, 0, + _T("Global"), _T("TabTextActive"), "S_tab_txt_active", 5, 0, + _T("Global"), _T("TabTextUnread"), "S_tab_txt_unread", 5, 0, + _T("Global"), _T("TabTextHottrack"), "S_tab_txt_hottrack", 5, 0, + NULL, NULL, NULL, 0, 0 +}; + +void CImageItem::Create(const TCHAR *szImageFile) +{ + HBITMAP hbm = LoadPNG(szImageFile); + BITMAP bm; + + m_hdc = 0; + m_hbmOld = 0; + m_hbm = 0; + + if (hbm) { + m_hbm = hbm; + m_bf.BlendFlags = 0; + m_bf.BlendOp = AC_SRC_OVER; + m_bf.AlphaFormat = 0; + + GetObject(hbm, sizeof(bm), &bm); + if (bm.bmBitsPixel == 32 && m_dwFlags & IMAGE_PERPIXEL_ALPHA) { + CImageItem::PreMultiply(m_hbm, 1); + m_bf.AlphaFormat = AC_SRC_ALPHA; + } + m_width = bm.bmWidth; + m_height = bm.bmHeight; + m_inner_height = m_height - m_bTop - m_bBottom; + m_inner_width = m_width - m_bLeft - m_bRight; + if (m_bTop && m_bBottom && m_bLeft && m_bRight) { + m_dwFlags |= IMAGE_FLAG_DIVIDED; + if (m_inner_height <= 0 || m_inner_width <= 0) { + DeleteObject(hbm); + m_hbm = 0; + return; + } + } + } +} + +/** + * Reads the definitions for an image item from the given .tsk file + * It does _not_ create the image itself, a call to CImageItem::Create() must be done + * to read the image in memory and prepare + * + * @param szFilename char*: full path and filename to the .TSK file + * + * @return char*: full path and filename to the .png image which represents this image item. + * caller MUST delete it. + */ +TCHAR* CImageItem::Read(const TCHAR *szFilename) +{ + TCHAR buffer[501]; + TCHAR szDrive[MAX_PATH], szPath[MAX_PATH]; + TCHAR *szFinalName = 0; + + GetPrivateProfileString(m_szName, _T("Glyph"), _T("None"), buffer, 500, szFilename); + if (_tcscmp(buffer, _T("None"))) { + _stscanf(buffer, _T("%d,%d,%d,%d"), &m_glyphMetrics[0], &m_glyphMetrics[1], + &m_glyphMetrics[2], &m_glyphMetrics[3]); + if (m_glyphMetrics[2] > m_glyphMetrics[0] && m_glyphMetrics[3] > m_glyphMetrics[1]) { + m_dwFlags |= IMAGE_GLYPH; + m_glyphMetrics[2] = (m_glyphMetrics[2] - m_glyphMetrics[0]) + 1; + m_glyphMetrics[3] = (m_glyphMetrics[3] - m_glyphMetrics[1]) + 1; + } + } + GetPrivateProfileString(m_szName, _T("Image"), _T("None"), buffer, 500, szFilename); + if (_tcscmp(buffer, _T("None")) || m_dwFlags & IMAGE_GLYPH) { + szFinalName = new TCHAR[MAX_PATH]; + //strncpy(m_szName, &m_szName[1], sizeof(m_szName)); + //m_szName[sizeof(m_szName) - 1] = 0; + _tsplitpath(szFilename, szDrive, szPath, NULL, NULL); + mir_sntprintf(szFinalName, MAX_PATH, _T("%s\\%s%s"), szDrive, szPath, buffer); + if (!PathFileExists(szFinalName)) { + delete[] szFinalName; + szFinalName = 0; + } + m_alpha = GetPrivateProfileInt(m_szName, _T("Alpha"), 100, szFilename); + m_alpha = min(m_alpha, 100); + m_alpha = (BYTE)((FLOAT)(((FLOAT) m_alpha) / 100) * 255); + m_bf.SourceConstantAlpha = m_alpha; + m_bLeft = GetPrivateProfileInt(m_szName, _T("Left"), 0, szFilename); + m_bRight = GetPrivateProfileInt(m_szName, _T("Right"), 0, szFilename); + m_bTop = GetPrivateProfileInt(m_szName, _T("Top"), 0, szFilename); + m_bBottom = GetPrivateProfileInt(m_szName, _T("Bottom"), 0, szFilename); + if (m_dwFlags & IMAGE_GLYPH) { + m_width = m_glyphMetrics[2]; + m_height = m_glyphMetrics[3]; + m_inner_height = m_glyphMetrics[3] - m_bTop - m_bBottom; + m_inner_width = m_glyphMetrics[2] - m_bRight - m_bLeft; + + if (m_bTop && m_bBottom && m_bLeft && m_bRight) + m_dwFlags |= IMAGE_FLAG_DIVIDED; + m_bf.BlendFlags = 0; + m_bf.BlendOp = AC_SRC_OVER; + m_bf.AlphaFormat = 0; + m_dwFlags |= IMAGE_PERPIXEL_ALPHA; + m_bf.AlphaFormat = AC_SRC_ALPHA; + if (m_inner_height <= 0 || m_inner_width <= 0) { + if (szFinalName) { + delete[] szFinalName; + szFinalName = 0; + } + return(szFinalName); + } + } + GetPrivateProfileString(m_szName, _T("Fillcolor"), _T("None"), buffer, 500, szFilename); + if (_tcscmp(buffer, _T("None"))) { + COLORREF fillColor = CSkin::HexStringToLong(buffer); + m_fillBrush = CreateSolidBrush(fillColor); + m_dwFlags |= IMAGE_FILLSOLID; + } else + m_fillBrush = 0; + GetPrivateProfileString(m_szName, _T("Colorkey"), _T("None"), buffer, 500, szFilename); + if (_tcscmp(buffer, _T("None"))) { + CSkin::m_ContainerColorKey = CSkin::HexStringToLong(buffer); + if (CSkin::m_ContainerColorKeyBrush) + DeleteObject(CSkin::m_ContainerColorKeyBrush); + CSkin::m_ContainerColorKeyBrush = CreateSolidBrush(CSkin::m_ContainerColorKey); + } + GetPrivateProfileString(m_szName, _T("Stretch"), _T("None"), buffer, 500, szFilename); + if (buffer[0] == 'B' || buffer[0] == 'b') + m_bStretch = IMAGE_STRETCH_B; + else if (buffer[0] == 'h' || buffer[0] == 'H') + m_bStretch = IMAGE_STRETCH_V; + else if (buffer[0] == 'w' || buffer[0] == 'W') + m_bStretch = IMAGE_STRETCH_H; + m_hbm = 0; + if (GetPrivateProfileInt(m_szName, _T("Perpixel"), 0, szFilename)) + m_dwFlags |= IMAGE_PERPIXEL_ALPHA; + + return(szFinalName); + } + return 0; +} + +/** + * Free all resources allocated by an image item + */ +void CImageItem::Free() +{ + if (m_hdc ) { + ::SelectObject(m_hdc, m_hbmOld); + ::DeleteDC(m_hdc); + } + if (m_hbm) + ::DeleteObject(m_hbm); + + if (m_fillBrush) + ::DeleteObject(m_fillBrush); + + ZeroMemory(this, sizeof(CImageItem)); +} + +/** + * Set the alpha value for a 32bit RGBA bitmap to the given value + * + * @param hBitmap bitmap handle + * @param bAlpha new alpha value (0 -> fully transparent, 255 -> opaque) + * default value is 255 + */ +void CImageItem::SetBitmap32Alpha(HBITMAP hBitmap, BYTE bAlpha) +{ + BITMAP bmp; + int x, y; + BOOL fixIt = TRUE; + + GetObject(hBitmap, sizeof(bmp), &bmp); + + if (bmp.bmBitsPixel != 32) + return; + + DWORD dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8); + BYTE *p = (BYTE *)mir_alloc(dwLen); + if (p == NULL) + return; + memset(p, 0, dwLen); + + GetBitmapBits(hBitmap, dwLen, p); + + for (y = 0; y < bmp.bmHeight; ++y) { + BYTE *px = p + bmp.bmWidth * 4 * y; + + for (x = 0; x < bmp.bmWidth; ++x) { + px[3] = bAlpha; + px += 4; + } + } + SetBitmapBits(hBitmap, bmp.bmWidth * bmp.bmHeight * 4, p); + mir_free(p); +} + +void CImageItem::PreMultiply(HBITMAP hBitmap, int mode) +{ + BITMAP bmp; + ::GetObject(hBitmap, sizeof(bmp), &bmp); + + int width = bmp.bmWidth; + int height = bmp.bmHeight; + DWORD dwLen = width * height * 4; + BYTE *p = (BYTE *)mir_alloc(dwLen); + if (p) { + ::GetBitmapBits(hBitmap, dwLen, p); + for (int y = 0; y < height; ++y) { + BYTE *px = p + width * 4 * y; + + for (int x = 0; x < width; ++x) { + if (mode) { + BYTE alpha = px[3]; + px[0] = px[0] * alpha / 255; + px[1] = px[1] * alpha / 255; + px[2] = px[2] * alpha / 255; + } else + px[3] = 255; + px += 4; + } + } + dwLen = ::SetBitmapBits(hBitmap, dwLen, p); + mir_free(p); + } +} + +void CImageItem::Colorize(HBITMAP hBitmap, BYTE dr, BYTE dg, BYTE db, BYTE alpha) +{ + BYTE *p = NULL; + DWORD dwLen; + int width, height, x, y; + BITMAP bmp; + + float r = (float)dr / 2.55; + float g = (float)dg / 2.55; + float b = (float)db / 2.55; + + ::GetObject(hBitmap, sizeof(bmp), &bmp); + width = bmp.bmWidth; + height = bmp.bmHeight; + dwLen = width * height * 4; + p = (BYTE *)mir_alloc(dwLen); + if (p) { + ::GetBitmapBits(hBitmap, dwLen, p); + for (y = 0; y < height; ++y) { + BYTE *px = p + width * 4 * y; + + for (x = 0; x < width; ++x) { + px[0] = (int)(px[0] + b) > 255 ? 255 : px[0] + b; + px[1] = (int)(px[1] + g) > 255 ? 255 : px[1] + g; + px[2] = (int)(px[2] + r) > 255 ? 255 : px[2] + r; + px[3] += alpha; + px += 4; + } + } + dwLen = ::SetBitmapBits(hBitmap, dwLen, p); + mir_free(p); + } +} + +/** + * load PNG image using core service (advaimg) + */ + +HBITMAP TSAPI CImageItem::LoadPNG(const TCHAR *szFilename) +{ + HBITMAP hBitmap = 0; + hBitmap = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)szFilename, IMGL_TCHAR); + return hBitmap; +} + + +/** + * set filename and load parameters from the database + * called on: + * ) init + * ) manual loading on user's request + */ +void CSkin::setFileName() +{ + DBVARIANT dbv; + if (0 == db_get_ts(0, SRMSGMOD_T, "ContainerSkin", &dbv)) { + PathToAbsoluteT(dbv.ptszVal, m_tszFileName, M.getSkinPath()); + db_free(&dbv); + } + else + m_tszFileName[0] = 0; + + m_fLoadOnStartup = M.GetByte("useskin", 0) ? true : false; +} +/** + * initialize the skin object + */ + +void CSkin::Init(bool fStartup) +{ + m_ImageItems = 0; + m_SkinItems = ::SkinItems; + m_fLoadOnStartup = false; + m_skinEnabled = m_frameSkins = false; + m_bAvatarBorderType = AVBORDER_NORMAL; + m_avatarBorderClr = ::GetSysColor(COLOR_3DDKSHADOW); + m_sideBarContainerBG = ::GetSysColor(COLOR_3DFACE); + + m_SkinItems[ID_EXTBKINFOPANELBG] = _defInfoPanel; + /* + * read current skin name from db + */ + + m_DisableScrollbars = M.GetByte("disableVScroll", 0) ? true : false; + + setFileName(); + m_aeroEffect = M.GetDword("aerostyle", AERO_EFFECT_MILK); + if (m_fLoadOnStartup && fStartup) + Load(); +} + +/** + * throws a warning to close all message windows before a skin can + * be loaded. user can cancel it + * @return: bool: true if windows were closed (or none was open) -> skin can be loaded + * + */ +bool CSkin::warnToClose() const +{ + if (::pFirstContainer == NULL) + return true; + + if (MessageBox(0, + TranslateT("All message containers need to close before the skin can be changed\nProceed?"), + TranslateT("Change skin"), MB_YESNO | MB_ICONQUESTION) != IDYES) + return false; + + CloseAllContainers(); + return true; +} + +/** + * mir_free the aero tab bitmaps + * only called on exit, NOT when a skin is unloaded as these elements + * are always needed (even without a skin) + */ +void CSkin::UnloadAeroTabs() +{ + if (m_tabTop) { + delete m_tabTop; + m_tabTop = NULL; + } + + if (m_tabBottom) { + delete m_tabBottom; + m_tabBottom = NULL; + } + + if (m_tabGlowTop) { + delete m_tabGlowTop; + m_tabGlowTop = NULL; + } + + if (m_tabGlowBottom) { + delete m_tabGlowBottom; + m_tabGlowTop = NULL; + } + + if (m_switchBarItem) { + delete m_switchBarItem; + m_switchBarItem = NULL; + } +} + +/** + * Unload the skin. Free everything allocated. + * Called when: + * * user unloads the skin from the dialog box + * * a new skin is loaded by user's request. + */ + +void CSkin::Unload() +{ + // do nothing when user decides to not close any window + if (warnToClose() == false) + return; + + /* + * delete all image items + */ + + m_skinEnabled = m_frameSkins = false; + + CImageItem *tmp = m_ImageItems, *nextItem; + while(tmp) { + nextItem = tmp->getNextItem(); + delete tmp; + tmp = nextItem; + } + + m_ImageItems = 0; + m_glyphItem.Free(); + + if (m_ContainerColorKeyBrush) + ::DeleteObject(m_ContainerColorKeyBrush); + if (m_MenuBGBrush) + ::DeleteObject(m_MenuBGBrush); + if (m_BrushBack) + ::DeleteObject(m_BrushBack); + + m_ContainerColorKeyBrush = m_MenuBGBrush = 0; + + if (m_SkinLightShadowPen) + ::DeleteObject(m_SkinLightShadowPen); + m_SkinLightShadowPen = 0; + + if (m_SkinDarkShadowPen) + ::DeleteObject(m_SkinDarkShadowPen); + m_SkinDarkShadowPen = 0; + + if (m_SkinItems) { + for (int i=0; i < ID_EXTBK_LAST; i++) { + m_SkinItems[i].IGNORED = 1; + m_SkinItems[i].imageItem = 0; + } + m_SkinItems[ID_EXTBKINFOPANELBG] = _defInfoPanel; + } + + ZeroMemory(this, sizeof(CSkin)); + + m_SkinItems = ::SkinItems; + setFileName(); + + m_bClipBorder = m_DisableScrollbars = false; + m_SkinnedFrame_left = m_SkinnedFrame_right = m_SkinnedFrame_bottom = m_SkinnedFrame_caption = 0; + m_realSkinnedFrame_left = m_realSkinnedFrame_right = m_realSkinnedFrame_bottom = m_realSkinnedFrame_caption = 0; + + m_titleBarLeftOff = m_titleButtonTopOff = m_captionOffset = m_captionPadding = + m_titleBarRightOff = m_sidebarTopOffset = m_sidebarBottomOffset = m_bRoundedCorner = 0; + + m_titleBarButtonSize.cx = m_titleBarButtonSize.cy = 0; + m_ContainerColorKey = 0; + m_ContainerColorKeyBrush = m_MenuBGBrush = 0; + m_skinEnabled = m_frameSkins = false; + + if (m_closeIcon) + ::DestroyIcon(m_closeIcon); + if (m_maxIcon) + ::DestroyIcon(m_maxIcon); + if (m_minIcon) + ::DestroyIcon(m_minIcon); + + m_closeIcon = m_maxIcon = m_minIcon = 0; + + for (int i=0; i < m_nrSkinIcons; i++) + if (m_skinIcons[i].phIcon ) + ::DestroyIcon(*(m_skinIcons[i].phIcon)); + mir_free(m_skinIcons); + + if (!g_bShutdown) { + M.getAeroState(); // refresh after unload + ::FreeTabConfig(); + ::ReloadTabConfig(); + } + + m_bAvatarBorderType = AVBORDER_NORMAL; + m_avatarBorderClr = ::GetSysColor(COLOR_3DDKSHADOW); + m_sideBarContainerBG = ::GetSysColor(COLOR_3DFACE); + + m_DisableScrollbars = M.GetByte("disableVScroll", 0) ? true : false; +} + +void CSkin::LoadIcon(const TCHAR *szSection, const TCHAR *name, HICON *hIcon) +{ + TCHAR buffer[512]; + if (hIcon) + return; + if (*hIcon != 0) + DestroyIcon(*hIcon); + GetPrivateProfileString(szSection, name, _T("none"), buffer, 250, m_tszFileName); + buffer[500] = 0; + + if (_tcsicmp(buffer, _T("none"))) { + TCHAR szDrive[MAX_PATH], szDir[MAX_PATH], szImagePath[MAX_PATH]; + + _tsplitpath(m_tszFileName, szDrive, szDir, NULL, NULL); + mir_sntprintf(szImagePath, MAX_PATH, _T("%s\\%s\\%s"), szDrive, szDir, buffer); + *hIcon = (HICON)LoadImage(0, szImagePath, IMAGE_ICON, 16, 16, LR_LOADFROMFILE); + } +} + + +/** + * Read a skin item (not a image item - these are handled elsewhere) + * Reads all values from the .ini style .tsk file and fills the + * structure. + * + * @param id int: zero-based index into the table of predefined skin items + * @param szItem char *: the section name in the ini file which holds the definition for this + * item. + */ +void CSkin::ReadItem(const int id, const TCHAR *szItem) +{ + TCHAR buffer[512]; + TCHAR def_color[20]; + COLORREF clr; + CSkinItem *defaults = &StatusItem_Default; + + CSkinItem *this_item = &m_SkinItems[id]; + + this_item->ALPHA = (int)GetPrivateProfileInt(szItem, _T("Alpha"), defaults->ALPHA, m_tszFileName); + this_item->ALPHA = min(this_item->ALPHA, 100); + + clr = RGB(GetBValue(defaults->COLOR), GetGValue(defaults->COLOR), GetRValue(defaults->COLOR)); + mir_sntprintf(def_color, 15, _T("%6.6x"), clr); + GetPrivateProfileString(szItem, _T("Color1"), def_color, buffer, 400, m_tszFileName); + this_item->COLOR = HexStringToLong(buffer); + + clr = RGB(GetBValue(defaults->COLOR2), GetGValue(defaults->COLOR2), GetRValue(defaults->COLOR2)); + mir_sntprintf(def_color, 15, _T("%6.6x"), clr); + GetPrivateProfileString(szItem, _T("Color2"), def_color, buffer, 400, m_tszFileName); + this_item->COLOR2 = HexStringToLong(buffer); + + this_item->COLOR2_TRANSPARENT = (BYTE)GetPrivateProfileInt(szItem, _T("COLOR2_TRANSPARENT"), defaults->COLOR2_TRANSPARENT, m_tszFileName); + + this_item->CORNER = defaults->CORNER & CORNER_ACTIVE ? defaults->CORNER : 0; + GetPrivateProfileString(szItem, _T("Corner"), _T("None"), buffer, 400, m_tszFileName); + if (_tcsstr(buffer, _T("tl"))) + this_item->CORNER |= CORNER_TL; + if (_tcsstr(buffer, _T("tr"))) + this_item->CORNER |= CORNER_TR; + if (_tcsstr(buffer, _T("bl"))) + this_item->CORNER |= CORNER_BL; + if (_tcsstr(buffer, _T("br"))) + this_item->CORNER |= CORNER_BR; + if (this_item->CORNER) + this_item->CORNER |= CORNER_ACTIVE; + + this_item->GRADIENT = defaults->GRADIENT & GRADIENT_ACTIVE ? defaults->GRADIENT : 0; + GetPrivateProfileString(szItem, _T("Gradient"), _T("None"), buffer, 400, m_tszFileName); + if (_tcsstr(buffer, _T("left"))) + this_item->GRADIENT = GRADIENT_RL; + else if (_tcsstr(buffer, _T("right"))) + this_item->GRADIENT = GRADIENT_LR; + else if (_tcsstr(buffer, _T("up"))) + this_item->GRADIENT = GRADIENT_BT; + else if (_tcsstr(buffer, _T("down"))) + this_item->GRADIENT = GRADIENT_TB; + if (this_item->GRADIENT) + this_item->GRADIENT |= GRADIENT_ACTIVE; + + this_item->MARGIN_LEFT = GetPrivateProfileInt(szItem, _T("Left"), defaults->MARGIN_LEFT, m_tszFileName); + this_item->MARGIN_RIGHT = GetPrivateProfileInt(szItem, _T("Right"), defaults->MARGIN_RIGHT, m_tszFileName); + this_item->MARGIN_TOP = GetPrivateProfileInt(szItem, _T("Top"), defaults->MARGIN_TOP, m_tszFileName); + this_item->MARGIN_BOTTOM = GetPrivateProfileInt(szItem, _T("Bottom"), defaults->MARGIN_BOTTOM, m_tszFileName); + this_item->BORDERSTYLE = GetPrivateProfileInt(szItem, _T("Radius"), defaults->BORDERSTYLE, m_tszFileName); + + GetPrivateProfileString(szItem, _T("Textcolor"), _T("ffffffff"), buffer, 400, m_tszFileName); + this_item->TEXTCOLOR = HexStringToLong(buffer); + this_item->IGNORED = 0; +} + +/** + * stub to read a single item. Called from CSkin::LoadItems() + * The real work is done by the CImageItem::Read(). + * + * @param itemname char *: image item name, also section name in the .tsk file + */ +void CSkin::ReadImageItem(const TCHAR *itemname) +{ + TCHAR buffer[512], szItemNr[30]; + + CImageItem tmpItem(itemname); + + TCHAR *szImageFileName = tmpItem.Read(m_tszFileName); + + if (!_tcsicmp(itemname, _T("$glyphs")) && szImageFileName != 0) { // the glyph item MUST have a valid image + tmpItem.Create(szImageFileName); + if (tmpItem.getHbm()) { + m_glyphItem = tmpItem; + m_fHaveGlyph = true; + } + tmpItem.Clear(); + delete[] szImageFileName; + return; + } + + /* + * handle the assignments of image items to skin items + */ + + for (int n = 0;;n++) { + mir_sntprintf(szItemNr, 30, _T("Item%d"), n); + GetPrivateProfileString(itemname, szItemNr, _T("None"), buffer, 500, m_tszFileName); + if (!_tcscmp(buffer, _T("None"))) + break; + for (int i=0; i <= ID_EXTBK_LAST; i++) { + if (!_tcsicmp(SkinItems[i].szName[0] == '{' ? &SkinItems[i].szName[3] : SkinItems[i].szName, buffer)) { + if (!(tmpItem.getFlags() & IMAGE_GLYPH)) { + if (szImageFileName) + tmpItem.Create(szImageFileName); + else { + tmpItem.Clear(); + return; // no reference to the glpyh image and no valid image file name -> invalid item + } + } + if (tmpItem.getHbm() || (tmpItem.getFlags() & IMAGE_GLYPH)) { + CImageItem *newItem = new CImageItem(tmpItem); + SkinItems[i].imageItem = newItem; + if (m_ImageItems == NULL) + m_ImageItems = newItem; + else { + CImageItem *pItem = m_ImageItems; + + while (pItem->getNextItem() != 0) + pItem = pItem->getNextItem(); + pItem->setNextItem(newItem); + } + } + } + } + } + tmpItem.Clear(); + if (szImageFileName) + delete[] szImageFileName; +} + +/** + * Load the skin from the .tsk file + * It reads and initializes all static values for the skin. Afterwards + * it calls ReadItems() to read additional skin information like image items, + * buttons and icons. + */ +void CSkin::Load(void) +{ + if (warnToClose() == false) + return; + + if (m_skinEnabled) { + Unload(); + m_skinEnabled = false; + } + + m_fHaveGlyph = false; + + if ( !m_tszFileName[0] || !::PathFileExists(m_tszFileName)) + return; + + TCHAR *p; + TCHAR *szSections = (TCHAR*)mir_alloc(6004); + int i = 1, j = 0; + UINT data; + TCHAR buffer[500]; + + if (!(GetPrivateProfileInt(_T("Global"), _T("Version"), 0, m_tszFileName) >= 1 && GetPrivateProfileInt(_T("Global"), _T("Signature"), 0, m_tszFileName) == 101)) + return; + + i = 0; + while (_tagSettings[i].szIniKey != NULL) { + data = 0; + data = GetPrivateProfileInt(_tagSettings[i].szIniKey, _tagSettings[i].szIniName, + _tagSettings[i].defaultval, m_tszFileName); + switch (_tagSettings[i].size) { + case 1: + db_set_b(0, SRMSGMOD_T, _tagSettings[i].szSetting, (BYTE)data); + break; + case 4: + db_set_dw(0, SRMSGMOD_T, _tagSettings[i].szSetting, data); + break; + case 2: + db_set_w(NULL, SRMSGMOD_T, _tagSettings[i].szSetting, (WORD)data); + break; + case 5: + GetPrivateProfileString(_tagSettings[i].szIniKey, _tagSettings[i].szIniName, _T("000000"), + buffer, 10, m_tszFileName); + db_set_dw(0, SRMSGMOD_T, _tagSettings[i].szSetting, HexStringToLong(buffer)); + break; + } + i++; + } + + m_DisableScrollbars = M.GetByte("disableVScroll", 0) ? true : false; + + ZeroMemory(szSections, 6000); + p = szSections; + GetPrivateProfileSectionNames(szSections, 3000, m_tszFileName); + szSections[3001] = szSections[3000] = 0; + p = szSections; + while (lstrlen(p) > 1) { + if (p[0] != '%') { + p += (lstrlen(p) + 1); + continue; + } + for (i=0; i <= ID_EXTBK_LAST; i++) { + if (!_tcsicmp(&p[1], SkinItems[i].szName[0] == '{' ? &SkinItems[i].szName[3] : SkinItems[i].szName)) { + ReadItem(i, p); + break; + } + } + p += (lstrlen(p) + 1); + j++; + } + + if (j > 0) { + m_skinEnabled = true; + M.getAeroState(); // refresh aero state (set to false when a skin is successfully loaded and active) + } + + GetPrivateProfileString(_T("Avatars"), _T("BorderColor"), _T("000000"), buffer, 20, m_tszFileName); + m_avatarBorderClr = (COLORREF)HexStringToLong(buffer); + + GetPrivateProfileString(_T("Global"), _T("SideBarBG"), _T("None"), buffer, 20, m_tszFileName); + if (_tcscmp(buffer, _T("None"))) + m_sideBarContainerBG = (COLORREF)HexStringToLong(buffer); + else + m_sideBarContainerBG = SkinItems[ID_EXTBKSIDEBARBG].COLOR; + + m_bAvatarBorderType = GetPrivateProfileInt(_T("Avatars"), _T("BorderType"), 1, m_tszFileName); + + LoadIcon(_T("Global"), _T("CloseGlyph"), &CSkin::m_closeIcon); + LoadIcon(_T("Global"), _T("MaximizeGlyph"), &CSkin::m_maxIcon); + LoadIcon(_T("Global"), _T("MinimizeGlyph"), &CSkin::m_minIcon); + + m_frameSkins = GetPrivateProfileInt(_T("Global"), _T("framelessmode"), 0, m_tszFileName) ? true : false; + m_DisableScrollbars = GetPrivateProfileInt(_T("Global"), _T("NoScrollbars"), 0, m_tszFileName) ? true : false; + + m_SkinnedFrame_left = GetPrivateProfileInt(_T("WindowFrame"), _T("left"), 4, m_tszFileName); + m_SkinnedFrame_right = GetPrivateProfileInt(_T("WindowFrame"), _T("right"), 4, m_tszFileName); + m_SkinnedFrame_caption = GetPrivateProfileInt(_T("WindowFrame"), _T("Caption"), 24, m_tszFileName); + m_SkinnedFrame_bottom = GetPrivateProfileInt(_T("WindowFrame"), _T("bottom"), 4, m_tszFileName); + + m_titleBarButtonSize.cx = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleButtonWidth"), 24, m_tszFileName); + m_titleBarButtonSize.cy = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleButtonHeight"), 12, m_tszFileName); + m_titleButtonTopOff = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleButtonTopOffset"), 0, m_tszFileName); + + m_titleBarRightOff = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleBarRightOffset"), 0, m_tszFileName); + m_titleBarLeftOff = GetPrivateProfileInt(_T("WindowFrame"), _T("TitleBarLeftOffset"), 0, m_tszFileName); + + m_captionOffset = GetPrivateProfileInt(_T("WindowFrame"), _T("CaptionOffset"), 3, m_tszFileName); + m_captionPadding = GetPrivateProfileInt(_T("WindowFrame"), _T("CaptionPadding"), 0, m_tszFileName); + m_sidebarTopOffset = GetPrivateProfileInt(_T("ClientArea"), _T("SidebarTop"), -1, m_tszFileName); + m_sidebarBottomOffset = GetPrivateProfileInt(_T("ClientArea"), _T("SidebarBottom"), -1, m_tszFileName); + + m_bClipBorder = GetPrivateProfileInt(_T("WindowFrame"), _T("ClipFrame"), 0, m_tszFileName) ? true : false; + + BYTE radius_tl, radius_tr, radius_bl, radius_br; + TCHAR szFinalName[MAX_PATH]; + TCHAR szDrive[MAX_PATH], szPath[MAX_PATH]; + + radius_tl = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusTL"), 0, m_tszFileName); + radius_tr = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusTR"), 0, m_tszFileName); + radius_bl = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusBL"), 0, m_tszFileName); + radius_br = GetPrivateProfileInt(_T("WindowFrame"), _T("RadiusBR"), 0, m_tszFileName); + + CSkin::m_bRoundedCorner = radius_tl; + + GetPrivateProfileString(_T("Theme"), _T("File"), _T("None"), buffer, MAX_PATH, m_tszFileName); + + _tsplitpath(m_tszFileName, szDrive, szPath, NULL, NULL); + mir_sntprintf(szFinalName, MAX_PATH, _T("%s\\%s\\%s"), szDrive, szPath, buffer); + if (PathFileExists(szFinalName)) { + ReadThemeFromINI(szFinalName, 0, FALSE, m_fLoadOnStartup ? 0 : M.GetByte("skin_loadmode", 0)); + CacheLogFonts(); + CacheMsgLogIcons(); + } + + GetPrivateProfileString(_T("Global"), _T("MenuBarBG"), _T("None"), buffer, 20, m_tszFileName); + data = HexStringToLong(buffer); + if (m_MenuBGBrush) { + DeleteObject(m_MenuBGBrush); + m_MenuBGBrush = 0; + } + if (_tcscmp(buffer, _T("None"))) + m_MenuBGBrush = CreateSolidBrush(data); + + GetPrivateProfileString(_T("Global"), _T("LightShadow"), _T("000000"), buffer, 20, m_tszFileName); + data = HexStringToLong(buffer); + CSkin::m_SkinLightShadowPen = CreatePen(PS_SOLID, 1, RGB(GetRValue(data), GetGValue(data), GetBValue(data))); + GetPrivateProfileString(_T("Global"), _T("DarkShadow"), _T("000000"), buffer, 20, m_tszFileName); + data = HexStringToLong(buffer); + CSkin::m_SkinDarkShadowPen = CreatePen(PS_SOLID, 1, RGB(GetRValue(data), GetGValue(data), GetBValue(data))); + + SkinCalcFrameWidth(); + + GetPrivateProfileString(_T("Global"), _T("FontColor"), _T("None"), buffer, 20, m_tszFileName); + if (_tcscmp(buffer, _T("None"))) + CSkin::m_DefaultFontColor = HexStringToLong(buffer); + else + CSkin::m_DefaultFontColor = GetSysColor(COLOR_BTNTEXT); + buffer[499] = 0; + mir_free(szSections); + + LoadItems(); + ::FreeTabConfig(); + ::ReloadTabConfig(); +} + +#define SECT_BUFFER_SIZE 2500 + +/** + * Load additional skin items (like image items, buttons, icons etc.) + * This is called AFTER ReadItems() has read the basic skin items + */ +void CSkin::LoadItems() +{ + TCHAR *szSections = NULL; + TCHAR *p, *p1; + TIconDesc tmpIconDesc = {0}; + + CImageItem *pItem = m_ImageItems; + + if (m_skinIcons == NULL) + m_skinIcons = (TIconDescW *)mir_calloc(sizeof(TIconDescW) * NR_MAXSKINICONS); + + m_nrSkinIcons = 0; + + szSections = (TCHAR*)mir_alloc((SECT_BUFFER_SIZE + 2) * sizeof(TCHAR)); + ZeroMemory(szSections, (SECT_BUFFER_SIZE + 2) * sizeof(TCHAR)); + + GetPrivateProfileSection(_T("Icons"), szSections, SECT_BUFFER_SIZE, m_tszFileName); + szSections[SECT_BUFFER_SIZE] = 0; + + p = szSections; + while (lstrlen(p) > 1) { + p1 = _tcschr(p, (int)'='); + if (p1) + *p1 = 0; + if (m_nrSkinIcons < NR_MAXSKINICONS && p1) { + LoadIcon(_T("Icons"), p, (HICON *)&tmpIconDesc.uId); + if (tmpIconDesc.uId) { + ZeroMemory(&m_skinIcons[m_nrSkinIcons], sizeof(TIconDesc)); + m_skinIcons[m_nrSkinIcons].uId = tmpIconDesc.uId; + m_skinIcons[m_nrSkinIcons].phIcon = (HICON *)(&m_skinIcons[m_nrSkinIcons].uId); + m_skinIcons[m_nrSkinIcons].szName = (TCHAR*)mir_alloc(sizeof(TCHAR) * (lstrlen(p) + 1)); + lstrcpy(m_skinIcons[m_nrSkinIcons].szName, p); + m_nrSkinIcons++; + } + } + if (p1) + *p1 = '='; + p += (lstrlen(p) + 1); + } + + ZeroMemory(szSections, (SECT_BUFFER_SIZE + 2) * sizeof(TCHAR)); + GetPrivateProfileSectionNames(szSections, SECT_BUFFER_SIZE, m_tszFileName); + szSections[SECT_BUFFER_SIZE] = 0; + + p = szSections; + while (lstrlen(p) > 1) { + if (p[0] == '$') + ReadImageItem(p); + p += (lstrlen(p) + 1); + } + nextButtonID = IDC_TBFIRSTUID; + + p = szSections; + /* + while (lstrlen(p) > 1) { + if (p[0] == '!') + ReadButtonItem(p); + p += (lstrlen(p) + 1); + } + */ + mir_free(szSections); + g_ButtonSet.top = GetPrivateProfileInt(_T("ButtonArea"), _T("top"), 0, m_tszFileName); + g_ButtonSet.bottom = GetPrivateProfileInt(_T("ButtonArea"), _T("bottom"), 0, m_tszFileName); + g_ButtonSet.left = GetPrivateProfileInt(_T("ButtonArea"), _T("left"), 0, m_tszFileName); + g_ButtonSet.right = GetPrivateProfileInt(_T("ButtonArea"), _T("right"), 0, m_tszFileName); +} + +/** + * setup and cache the bitmap for the close button on tabs and switch bar + * buttons. + * re-created when: + * ) theme changes + * ) icons change (via ico lib service) + * + * @param fDeleteOnly: only delete GDI resources (this is ONLY used at plugin shutdown) + */ +void CSkin::setupTabCloseBitmap(bool fDeleteOnly) +{ + if (m_tabCloseHDC || fDeleteOnly) { + if (m_tabCloseHDC) { + ::SelectObject(m_tabCloseHDC, m_tabCloseOldBitmap); + ::DeleteObject(m_tabCloseBitmap); + ::DeleteDC(m_tabCloseHDC); + } + if (fDeleteOnly) + return; + } + + bool fFree = false; + RECT rc = {0, 0, 20, 20}; + HDC dc = ::GetDC(PluginConfig.g_hwndHotkeyHandler); + m_tabCloseHDC = ::CreateCompatibleDC(dc); + + if (M.isAero()) + m_tabCloseBitmap = CreateAeroCompatibleBitmap(rc, m_tabCloseHDC); + else + m_tabCloseBitmap = ::CreateCompatibleBitmap(dc, 20, 20); + + m_tabCloseOldBitmap = reinterpret_cast(::SelectObject(m_tabCloseHDC, m_tabCloseBitmap)); + + if (M.isVSThemed() || M.isAero()) { + ::FillRect(m_tabCloseHDC, &rc, M.isAero() ? reinterpret_cast(::GetStockObject(BLACK_BRUSH)) : ::GetSysColorBrush(COLOR_3DFACE)); + + HANDLE hTheme = OpenThemeData(PluginConfig.g_hwndHotkeyHandler, L"BUTTON"); + rc.left--; rc.right++; + rc.top--; rc.bottom++; + DrawThemeParentBackground(PluginConfig.g_hwndHotkeyHandler, m_tabCloseHDC, &rc); + DrawThemeBackground(hTheme, m_tabCloseHDC, 1, 1, &rc, &rc); + CloseThemeData(hTheme); + } + else if (CSkin::m_skinEnabled) + CSkin::DrawItem(m_tabCloseHDC, &rc, &SkinItems[ID_EXTBKBUTTONSNPRESSED]); + else { + ::FillRect(m_tabCloseHDC, &rc, ::GetSysColorBrush(COLOR_3DFACE)); + ::DrawFrameControl(m_tabCloseHDC, &rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_MONO); + } + ::DrawIconEx(m_tabCloseHDC, 2, 2, PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL], 16, 16, 0, 0, DI_NORMAL); + ::SelectObject(m_tabCloseHDC, m_tabCloseOldBitmap); + + HBITMAP hbmTemp = ResizeBitmap(m_tabCloseBitmap, 16, 16, fFree); + ::DeleteObject(m_tabCloseBitmap); + m_tabCloseBitmap = hbmTemp; + CImageItem::PreMultiply(m_tabCloseBitmap, 1); + m_tabCloseOldBitmap = reinterpret_cast(::SelectObject(m_tabCloseHDC, m_tabCloseBitmap)); + + ::ReleaseDC(PluginConfig.g_hwndHotkeyHandler, dc); +} +/** + * load and setup some images which are used to draw tabs in aero mode + * there is one image for tabs (it is flipped vertically for bottom tabs) + * and one image for the glowing effect on tabs (also flipped for bottom + * tabs). + * + * support for custom images added 3.0.0.34 + * user can place images with a custom_ prefix into the images folder and + * they will be loaded instead the default ones. + * + * the 3rd image acts as background for switch bar buttons + * + * this is executed when: + * ) dwm mode changes + * ) aero effect is changed by the user + * ) glow colorization is changed by user's request + */ +void CSkin::setupAeroSkins() +{ + M.getAeroState(); + UnloadAeroTabs(); + + if (!m_fAeroSkinsValid) + return; + + TCHAR tszFilename[MAX_PATH], tszBasePath[MAX_PATH]; + _tcsncpy_s(tszBasePath, M.getDataPath(), _TRUNCATE); + if (tszBasePath[lstrlen(tszBasePath) - 1] != '\\') + _tcscat(tszBasePath, _T("\\")); + + // load unknown avatar.. + if (0 == PluginConfig.g_hbmUnknown) { + mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_unknown.png"), tszBasePath); + if (!PathFileExists(tszFilename)) + mir_sntprintf(tszFilename, MAX_PATH, _T("%sunknown.png"), tszBasePath); + PluginConfig.g_hbmUnknown = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR); + if (PluginConfig.g_hbmUnknown == 0) { + HDC dc = GetDC(0); + PluginConfig.g_hbmUnknown = CreateCompatibleBitmap(dc, 20, 20); + ReleaseDC(0, dc); + } + } + + mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_tabskin_aero.png"), tszBasePath); + if (!PathFileExists(tszFilename)) + mir_sntprintf(tszFilename, MAX_PATH, _T("%stabskin_aero.png"), tszBasePath); + + BOOL isOpaque = false; + if (CMimAPI::m_pfnDwmGetColorizationColor && M.isAero()) + CMimAPI::m_pfnDwmGetColorizationColor(&m_dwmColor, &isOpaque); + else + m_dwmColor = PluginConfig.m_fillColor; + + float fr = (float)((m_dwmColor & 0x00ff0000) >> 16); + float fg = (float)((m_dwmColor & 0x0000ff00) >> 8); + float fb = (float)((m_dwmColor & 0x000000ff)); + BYTE alphafactor = 255 - ((m_dwmColor & 0xff000000) >> 24); + + /* + * a bit tricky, because for low alpha settings (high DWM transparency), the dwm + * color is almost dark gray, so we need to intensify the strongest color a bit more than + * the others. This will give us a good match for the dwm color that can be used + * to render non transparent stuff (i.e. the tool bar). + * + * TODO: this isn't perfect yet, for some colors, the result is a bit off... + */ + + if (!isOpaque && alphafactor > 150 && !(fr == fg && fg == fb)) { + float fmax = max(max(fr,fg), fb); + + if (fmax == fr) { + fr *= (alphafactor / 100 * 2.2); + fr = min(fr, 255); + fb = min(fb * alphafactor / 100, 255); + fg = min(fg * alphafactor / 100, 255); + } else if (fmax == fg) { + fg *= (alphafactor / 100 * 2.2); + fg = min(fg, 255); + fr = min(fr * alphafactor / 100, 255); + fb = min(fb * alphafactor / 100, 255); + } else { + fb *= (alphafactor / 100 * 2.2); + fb = min(fb, 255); + fr = min(fr * alphafactor / 100, 255); + fg = min(fg * alphafactor / 100, 255); + } + } + + m_dwmColorRGB = RGB((BYTE)fr, (BYTE)fg, (BYTE)fb); + + FIBITMAP *fib = (FIBITMAP *)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR | IMGL_RETURNDIB); + + HBITMAP hbm = FIF->FI_CreateHBITMAPFromDIB(fib); + + CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB), + GetGValue(m_dwmColorRGB), + GetBValue(m_dwmColorRGB)); + + CImageItem::PreMultiply(hbm, 1); + + BITMAP bm; + GetObject(hbm, sizeof(bm), &bm); + m_tabTop = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, + 0, 255, 30, 80, 50, 100); + + m_tabTop->setAlphaFormat(AC_SRC_ALPHA, 255); + m_tabTop->setMetrics(bm.bmWidth, bm.bmHeight); + + + /* + * created inverted bitmap for bottom tabs + */ + + FIF->FI_FlipVertical(fib); + + hbm = FIF->FI_CreateHBITMAPFromDIB(fib); + + CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB), + GetGValue(m_dwmColorRGB), + GetBValue(m_dwmColorRGB)); + + CImageItem::PreMultiply(hbm, 1); + FIF->FI_Unload(fib); + + GetObject(hbm, sizeof(bm), &bm); + m_tabBottom = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, + 0, 255, 30, 80, 50, 100); + + m_tabBottom->setAlphaFormat(AC_SRC_ALPHA, 255); + m_tabBottom->setMetrics(bm.bmWidth, bm.bmHeight); + + + mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_tabskin_aero_glow.png"), tszBasePath); + if (!PathFileExists(tszFilename)) + mir_sntprintf(tszFilename, MAX_PATH, _T("%stabskin_aero_glow.png"), tszBasePath); + + fib = (FIBITMAP *)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR | IMGL_RETURNDIB); + + COLORREF glowColor = M.GetDword(FONTMODULE, "aeroGlow", RGB(40, 40, 255)); + hbm = FIF->FI_CreateHBITMAPFromDIB(fib); + CImageItem::Colorize(hbm, GetRValue(glowColor), GetGValue(glowColor), GetBValue(glowColor)); + CImageItem::PreMultiply(hbm, 1); + + GetObject(hbm, sizeof(bm), &bm); + m_tabGlowTop = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, + 0, 255, 30, 80, 50, 100); + + m_tabGlowTop->setAlphaFormat(AC_SRC_ALPHA, 255); + m_tabGlowTop->setMetrics(bm.bmWidth, bm.bmHeight); + + FIF->FI_FlipVertical(fib); + + hbm = FIF->FI_CreateHBITMAPFromDIB(fib); + CImageItem::Colorize(hbm, GetRValue(glowColor), GetGValue(glowColor), GetBValue(glowColor)); + CImageItem::PreMultiply(hbm, 1); + FIF->FI_Unload(fib); + + GetObject(hbm, sizeof(bm), &bm); + m_tabGlowBottom = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, + 0, 255, 30, 80, 50, 100); + + m_tabGlowBottom->setAlphaFormat(AC_SRC_ALPHA, 255); + m_tabGlowBottom->setMetrics(bm.bmWidth, bm.bmHeight); + + /* + * background item for the button switch bar + */ + mir_sntprintf(tszFilename, MAX_PATH, _T("%scustom_tabskin_aero_button.png"), tszBasePath); + if (!PathFileExists(tszFilename)) + mir_sntprintf(tszFilename, MAX_PATH, _T("%stabskin_aero_button.png"), tszBasePath); + + hbm = (HBITMAP)CallService(MS_IMG_LOAD, (WPARAM)tszFilename, IMGL_TCHAR); + + CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB), + GetGValue(m_dwmColorRGB), + GetBValue(m_dwmColorRGB)); + + CImageItem::PreMultiply(hbm, 1); + + GetObject(hbm, sizeof(bm), &bm); + + m_switchBarItem = new CImageItem(4, 4, 4, 4, 0, hbm, IMAGE_FLAG_DIVIDED | IMAGE_PERPIXEL_ALPHA, + 0, 255, 2, 12, 10, 20); + + m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, 255); + m_switchBarItem->setMetrics(bm.bmWidth, bm.bmHeight); +} + +/** + * Calculate window frame borders for a skin with the ability to paint the window frame. + * Uses system metrics to determine predefined window borders and caption bar size. + */ +void CSkin::SkinCalcFrameWidth() +{ + int xBorder, yBorder, yCaption; + + xBorder = GetSystemMetrics(SM_CXSIZEFRAME); + yBorder = GetSystemMetrics(SM_CYSIZEFRAME); + yCaption = GetSystemMetrics(SM_CYCAPTION); + + m_realSkinnedFrame_left = m_SkinnedFrame_left - xBorder; + m_realSkinnedFrame_right = m_SkinnedFrame_right - xBorder; + m_realSkinnedFrame_bottom = m_SkinnedFrame_bottom - yBorder; + m_realSkinnedFrame_caption = m_SkinnedFrame_caption - yCaption; +} + + +/** + * Draws part of the background to the foreground control + * + * @param hwndClient HWND: target window + * @param hwnd HWND: source window (usually the parent, needed for transforming client coordinates + * @param pContainer ContainerWindowData *: needed to access the cached DC of the container window + * @param rcClient RECT *: client rectangle (target area) + * @param hdcTarget HDC: device context of the target window + */ +void CSkin::SkinDrawBG(HWND hwndClient, HWND hwnd, TContainerData *pContainer, RECT *rcClient, HDC hdcTarget) +{ + RECT rcWindow; + POINT pt; + LONG width = rcClient->right - rcClient->left; + LONG height = rcClient->bottom - rcClient->top; + HDC dc; + + ::GetWindowRect(hwndClient, &rcWindow); + pt.x = rcWindow.left + rcClient->left; + pt.y = rcWindow.top + rcClient->top; + ::ScreenToClient(hwnd, &pt); + if (pContainer) + dc = pContainer->cachedDC; + else + dc = ::GetDC(hwnd); + pt.y = max(pt.y, 0); + ::BitBlt(hdcTarget, rcClient->left, rcClient->top, width, height, dc, pt.x, pt.y, SRCCOPY); + if (!pContainer) + ::ReleaseDC(hwnd, dc); +} + +/** + * Draws part of the background to the foreground control + * same as above, but can use any source DC, not just the + * container + * + * @param hwndClient HWND: target window + * @param hwnd HWND: source window (usually the parent, needed for transforming client coordinates + * @param pContainer ContainerWindowData *: needed to access the cached DC of the container window + * @param rcClient RECT *: client rectangle (target area) + * @param hdcTarget HDC: device context of the target window + */ + +void CSkin::SkinDrawBGFromDC(HWND hwndClient, HWND hwnd, RECT *rcClient, HDC hdcTarget) +{ + RECT rcWindow; + POINT pt; + LONG width = rcClient->right - rcClient->left; + LONG height = rcClient->bottom - rcClient->top; + HDC hdcSrc = ::GetDC(hwnd); + + ::GetWindowRect(hwndClient, &rcWindow); + pt.x = rcWindow.left + rcClient->left; + pt.y = rcWindow.top + rcClient->top; + ::ScreenToClient(hwnd, &pt); + ::StretchBlt(hdcTarget, rcClient->left, rcClient->top, width, height, hdcSrc, pt.x, pt.y, width, height, SRCCOPY | CAPTUREBLT); + ::ReleaseDC(hwnd, hdcSrc); +} + +/** + * draw an icon "Dimmed" (small amount of transparency applied) +*/ + +void CSkin::DrawDimmedIcon(HDC hdc, LONG left, LONG top, LONG dx, LONG dy, HICON hIcon, BYTE alpha) +{ + HDC dcMem = ::CreateCompatibleDC(hdc); + HBITMAP hbm = ::CreateCompatibleBitmap(hdc, dx, dy), hbmOld = 0; + + hbmOld = reinterpret_cast(::SelectObject(dcMem, hbm)); + ::DrawIconEx(dcMem, 0, 0, hIcon, dx, dy, 0, 0, DI_NORMAL); + m_default_bf.SourceConstantAlpha = alpha; + hbm = (HBITMAP)SelectObject(dcMem, hbmOld); + CImageItem::PreMultiply(hbm, 1); // for AlphaBlend()... + hbmOld = reinterpret_cast(::SelectObject(dcMem, hbm)); + GdiAlphaBlend(hdc, left, top, dx, dy, dcMem, 0, 0, dx, dy, m_default_bf); + m_default_bf.SourceConstantAlpha = 255; + SelectObject(dcMem, hbmOld); + DeleteObject(hbm); + DeleteDC(dcMem); +} + +UINT CSkin::NcCalcRichEditFrame(HWND hwnd, const TWindowData *mwdat, UINT skinID, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC MyWndProc) +{ + NCCALCSIZE_PARAMS *nccp = (NCCALCSIZE_PARAMS *)lParam; + BOOL bReturn = FALSE; + + if (CSkin::m_DisableScrollbars) { + SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VSCROLL); + EnableScrollBar(hwnd, SB_VERT, ESB_DISABLE_BOTH); + ShowScrollBar(hwnd, SB_VERT, FALSE); + } + + LRESULT orig = mir_callNextSubclass(hwnd, MyWndProc, msg, wParam, lParam); + if (0 == mwdat) + return orig; + + if (CSkin::m_skinEnabled) { + CSkinItem *item = &SkinItems[skinID]; + if (!item->IGNORED) + return WVR_REDRAW; + } + if (mwdat->hTheme && wParam) { + RECT rcClient; + HDC hdc = GetDC(GetParent(hwnd)); + + if (GetThemeBackgroundContentRect(mwdat->hTheme, hdc, 1, 1, &nccp->rgrc[0], &rcClient) == S_OK) { + if (EqualRect(&rcClient, &nccp->rgrc[0])) + InflateRect(&rcClient, -1, -1); + CopyRect(&nccp->rgrc[0], &rcClient); + bReturn = TRUE; + } + ReleaseDC(GetParent(hwnd), hdc); + if (bReturn) + return WVR_REDRAW; + else + return orig; + } + if ((mwdat->sendMode & SMODE_MULTIPLE || mwdat->sendMode & SMODE_CONTAINER || + mwdat->fEditNotesActive || mwdat->sendMode & SMODE_SENDLATER) && skinID == ID_EXTBKINPUTAREA) { + InflateRect(&nccp->rgrc[0], -1, -1); + return WVR_REDRAW; + } + return orig; +} + +/* + * process WM_NCPAINT for the rich edit control. Draw a visual style border and avoid classic static edge / client edge + * may also draw a colorized border around the control + */ + +UINT CSkin::DrawRichEditFrame(HWND hwnd, const TWindowData *mwdat, UINT skinID, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC OldWndProc) +{ + // do default processing (otherwise, NO scrollbar as it is painted in NC_PAINT) + LRESULT result = mir_callNextSubclass(hwnd, OldWndProc, msg, wParam, lParam); + if (0 == mwdat) + return result; + + BOOL isEditNotesReason = ((mwdat->fEditNotesActive) && (skinID == ID_EXTBKINPUTAREA)); + BOOL isSendLaterReason = ((mwdat->sendMode & SMODE_SENDLATER) && (skinID == ID_EXTBKINPUTAREA)); + BOOL isMultipleReason = ((skinID == ID_EXTBKINPUTAREA) && (mwdat->sendMode & SMODE_MULTIPLE || mwdat->sendMode & SMODE_CONTAINER)); + + HDC hdc = GetWindowDC(hwnd); + RECT rcWindow; + POINT pt; + LONG left_off, top_off, right_off, bottom_off; + LONG dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + LONG dwExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + + GetWindowRect(hwnd, &rcWindow); + pt.x = pt.y = 0; + ClientToScreen(hwnd, &pt); + left_off = pt.x - rcWindow.left; + if (dwStyle & WS_VSCROLL && dwExStyle & WS_EX_RTLREADING) + left_off -= PluginConfig.m_ncm.iScrollWidth; + top_off = pt.y - rcWindow.top; + + CSkinItem *item = &SkinItems[skinID]; + if (CSkin::m_skinEnabled && !item->IGNORED) { + right_off = item->MARGIN_RIGHT; + bottom_off = item->MARGIN_BOTTOM; + } + else { + right_off = left_off; + bottom_off = top_off; + } + + rcWindow.right -= rcWindow.left; + rcWindow.bottom -= rcWindow.top; + rcWindow.left = rcWindow.top = 0; + + ExcludeClipRect(hdc, left_off, top_off, rcWindow.right - right_off, rcWindow.bottom - bottom_off); + if (CSkin::m_skinEnabled && !item->IGNORED) { + ReleaseDC(hwnd, hdc); + return result; + } + if (isMultipleReason || isEditNotesReason || isSendLaterReason) { + HBRUSH br = CreateSolidBrush(isMultipleReason ? RGB(255, 130, 130) : (isEditNotesReason ? RGB(80, 255, 80) : RGB(80, 80, 255))); + FillRect(hdc, &rcWindow, br); + DeleteObject(br); + } else { + if (PluginConfig.m_cRichBorders) { + HBRUSH br = CreateSolidBrush(PluginConfig.m_cRichBorders); + FillRect(hdc, &rcWindow, br); + DeleteObject(br); + } + else + DrawThemeBackground(mwdat->hTheme, hdc, 1, 1, &rcWindow, &rcWindow); + } + ReleaseDC(hwnd, hdc); + return result; +} + +/** + * convert a html-style color string (without the #) to a 32bit COLORREF value + * + * @param szSource TCHAR*: the color value as string. format: + * html-style without the leading #. e.g. + * "f3e355" + * + * @return COLORREF representation of the string value. + */ +DWORD __fastcall CSkin::HexStringToLong(const TCHAR *szSource) +{ + TCHAR *stopped; + COLORREF clr = _tcstol(szSource, &stopped, 16); + if (clr == -1) + return clr; + return(RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr))); +} + +/** + * Render text to the given HDC. This function is aero aware and will use uxtheme DrawThemeTextEx() when needed. + * Paramaters are pretty much comparable to GDI DrawText() API + * + * @return + */ + +int CSkin::RenderText(HDC hdc, HANDLE hTheme, const TCHAR *szText, RECT *rc, DWORD dtFlags, const int iGlowSize, COLORREF clr, bool fForceAero) +{ + if ((PluginConfig.m_bIsVista && !CSkin::m_skinEnabled && hTheme) || fForceAero) { + DTTOPTS dto = {0}; + dto.dwSize = sizeof(dto); + if (iGlowSize && (M.isAero() || fForceAero)) { + dto.iGlowSize = iGlowSize; + dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE; + } + else { + dto.dwFlags = DTT_TEXTCOLOR|DTT_COMPOSITED;//|DTT_SHADOWTYPE|DTT_SHADOWOFFSET|DTT_SHADOWCOLOR|DTT_BORDERSIZE|DTT_BORDERCOLOR; + dto.crText = clr; + } + dto.iBorderSize = 10; + return(CMimAPI::m_pfnDrawThemeTextEx(hTheme, hdc, BP_PUSHBUTTON, PBS_NORMAL, szText, -1, dtFlags, rc, &dto)); + } + else { + ::SetTextColor(hdc, clr); + return(::DrawText(hdc, szText, -1, rc, dtFlags)); + } +} + +/** + * Resize a bitmap using image service. The function does not care about the image aspect ratio. + * The caller is responsible to submit proper values for the desired height and width. + * + * @param hBmpSrc HBITMAP: the source bitmap + * @param width LONG: width of the destination bitmap + * @param height LONG: height of the new bitmap + * @param mustFree bool: indicates that the new bitmap had been + *resized and either the source or destination + *bitmap should be freed. + * + * @return HBTIAMP: handle to a bitmap with the desired size. + */ +HBITMAP CSkin::ResizeBitmap(HBITMAP hBmpSrc, LONG width, LONG height, bool &mustFree) +{ + BITMAP bm; + + GetObject(hBmpSrc, sizeof(bm), &bm); + if (bm.bmHeight != height || bm.bmWidth != width) { + ::ResizeBitmap rb; + rb.size = sizeof(rb); + rb.fit = RESIZEBITMAP_STRETCH; + rb.max_height = height; + rb.max_width = width; + rb.hBmp = hBmpSrc; + + HBITMAP hbmNew = (HBITMAP)CallService(MS_IMG_RESIZE, (WPARAM)&rb, 0); + if (hbmNew != hBmpSrc) + mustFree = true; + return(hbmNew); + } + else { + mustFree = false; + return(hBmpSrc); + } +} + +/** + * Draw the given skin item to the target rectangle and dc + * + * @param hdc HDC: device context + * @param rc RECT: target rectangle + * @param item CSkinItem*: fully initialiized skin item + * + * @return bool: true if the item has been painted, false if not + * (only reason: the ignore flag in the item is set). + */ +bool __fastcall CSkin::DrawItem(const HDC hdc, const RECT *rc, const CSkinItem *item) +{ + if (!item->IGNORED) { + ::DrawAlpha(hdc, const_cast(rc), item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, + item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); + return true; + } + return false; +} + +/** + * Create a 32bit RGBA bitmap, compatible for rendering with alpha channel. + * Required by anything which would render on a transparent aero surface. + * the image is a "bottom up" bitmap, as it has a negative + * height. This is a requirement for some UxTheme APIs (e.g. + * DrawThemeTextEx). + * + * @param rc RECT &: the rectangle describing the target area. + * @param dc The device context for which the bitmap should be created. + * + * @return HBITMAP: handle to the bitmap created. + */ +HBITMAP CSkin::CreateAeroCompatibleBitmap(const RECT &rc, HDC dc) +{ + BITMAPINFO dib = {0}; + + dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + dib.bmiHeader.biWidth = rc.right - rc.left; + dib.bmiHeader.biHeight = -(rc.bottom - rc.top); + dib.bmiHeader.biPlanes = 1; + dib.bmiHeader.biBitCount = 32; + dib.bmiHeader.biCompression = BI_RGB; + + return(CreateDIBSection(dc, &dib, DIB_RGB_COLORS, NULL, NULL, 0 )); +} + +/** + * Map a given rectangle within the window, specified by hwndClient + * to the client area of another window. + * + * @param hwndClient HWND: Client window + * @param hwndParent HWND: The window to which the coordinates should be mapped + * @param rc RECT &: Rectangular area within the client area of hwndClient. + * + *It will receive the transformed coordinates, relative to the client area of hwndParent + */ +void CSkin::MapClientToParent(HWND hwndClient, HWND hwndParent, RECT &rc) +{ + POINT pt; + + LONG cx = rc.right - rc.left; + LONG cy = rc.bottom - rc.top; + pt.x = rc.left; pt.y = rc.top; + + ::ClientToScreen(hwndClient, &pt); + ::ScreenToClient(hwndParent, &pt); + + rc.top = pt.y; + rc.left = pt.x; + rc.right = rc.left + cx; + rc.bottom = rc.top + cy; +} + +/** + * Draw the background for the message window tool bar + * + * @param dat _MessageWindowData *: structure describing the message session + * + * @param hdc HDC: handle to the device context in which painting should occur. + * @param rcWindow RECT &: The window rectangle of the message dialog window + */ +void CSkin::RenderToolbarBG(const TWindowData *dat, HDC hdc, const RECT &rcWindow) +{ + if (dat) { + if (dat->pContainer->dwFlags & CNT_HIDETOOLBAR) + return; + + bool bAero = M.isAero(); + bool fTbColorsValid = PluginConfig.m_tbBackgroundHigh && PluginConfig.m_tbBackgroundLow; + BYTE bAlphaOffset = 0; + BOOL fMustDrawNonThemed = ((bAero || fTbColorsValid) && !M.GetByte(SRMSGMOD_T, "forceThemedToolbar", 0)); + RECT rc, rcToolbar; + POINT pt; + + if (!(dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR)) { + ::GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_CHAT ? IDC_CHAT_LOG : IDC_LOG), &rc); + pt.y = rc.bottom + 0; + ::ScreenToClient(dat->hwnd, &pt); + rcToolbar.top = pt.y; + rcToolbar.left = 0; + rcToolbar.right = rcWindow.right; + + if (dat->bType == SESSIONTYPE_IM) { + if (dat->dwFlags & MWF_ERRORSTATE) + rcToolbar.top += ERRORPANEL_HEIGHT; + if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED || dat->bNotOnList) { + rcToolbar.top += 20; + RECT rcAdd; + rcAdd.left = 0; rcAdd.right = rcToolbar.right - rcToolbar.left; + rcAdd.bottom = rcToolbar.top - 1; + rcAdd.top = rcAdd.bottom - 20; + ::DrawEdge(hdc, &rcAdd, BDR_RAISEDINNER | BDR_RAISEDOUTER, BF_RECT | BF_SOFT | BF_FLAT); + } + } + + ::GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_CHAT ? IDC_CHAT_MESSAGE : IDC_MESSAGE), &rc); + pt.y = rc.top - (dat->bIsAutosizingInput ? 1 : 2); + ::ScreenToClient(dat->hwnd, &pt); + rcToolbar.bottom = pt.y; + } + else { + GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_CHAT ? IDC_CHAT_MESSAGE : IDC_MESSAGE), &rc); + pt.y = rc.bottom - (dat->bType == SESSIONTYPE_IM ? 2 : 2); + ScreenToClient(dat->hwnd, &pt); + rcToolbar.top = pt.y + 1; + rcToolbar.left = 0; + rcToolbar.right = rcWindow.right; + rcToolbar.bottom = rcWindow.bottom; + } + LONG cx = rcToolbar.right - rcToolbar.left; + LONG cy = rcToolbar.bottom - rcToolbar.top; + + RECT rcCachedToolbar = {0}; + rcCachedToolbar.right = cx; + rcCachedToolbar.bottom = cy; + + if (dat->pContainer->cachedToolbarDC == 0) + dat->pContainer->cachedToolbarDC = ::CreateCompatibleDC(hdc); + + if (dat->pContainer->szOldToolbarSize.cx != cx || dat->pContainer->szOldToolbarSize.cy != cy) { + if (dat->pContainer->oldhbmToolbarBG) { + ::SelectObject(dat->pContainer->cachedToolbarDC, dat->pContainer->oldhbmToolbarBG); + ::DeleteObject(dat->pContainer->hbmToolbarBG); + } + dat->pContainer->hbmToolbarBG = CSkin::CreateAeroCompatibleBitmap(rcCachedToolbar, hdc);// ::CreateCompatibleBitmap(hdc, cx, cy); + dat->pContainer->oldhbmToolbarBG = reinterpret_cast(::SelectObject(dat->pContainer->cachedToolbarDC, dat->pContainer->hbmToolbarBG)); + } + dat->pContainer->szOldToolbarSize.cx = cx; + dat->pContainer->szOldToolbarSize.cy = cy; + + if (!fMustDrawNonThemed && M.isVSThemed()) { + DrawThemeBackground(dat->hThemeToolbar, dat->pContainer->cachedToolbarDC, 6, 1, &rcCachedToolbar, &rcCachedToolbar); + dat->pContainer->bTBRenderingMode = 1; // tell TSButton how to render the tool bar buttons + } + else { + dat->pContainer->bTBRenderingMode = (M.isVSThemed() ? 1 : 0); + m_tmp_tb_high = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh : + ((bAero && m_pCurrentAeroEffect) ? m_pCurrentAeroEffect->m_clrToolbar : ::GetSysColor(COLOR_3DFACE)); + m_tmp_tb_low = PluginConfig.m_tbBackgroundLow ? PluginConfig.m_tbBackgroundLow : + ((bAero && m_pCurrentAeroEffect) ? m_pCurrentAeroEffect->m_clrToolbar2 : ::GetSysColor(COLOR_3DFACE)); + + bAlphaOffset = PluginConfig.m_tbBackgroundHigh ? 40 : 0; + ::DrawAlpha(dat->pContainer->cachedToolbarDC, &rcCachedToolbar, m_tmp_tb_high, 55 + bAlphaOffset, m_tmp_tb_low, 0, 9, 0, 0, 0); + } + + ::BitBlt(hdc, rcToolbar.left, rcToolbar.top, cx, cy, + dat->pContainer->cachedToolbarDC, 0, 0, SRCCOPY); + } +} + +/** + * Initiate a buffered paint operation + * + * @param hdcSrc The source device context (usually obtained by BeginPaint()) + * @param rc RECT&: the target rectangle that receives the painting + * @param hdcOut HDC& (out) receives the buffered device context handle + * + * @return + */ +HANDLE CSkin::InitiateBufferedPaint(const HDC hdcSrc, RECT& rc, HDC& hdcOut) +{ + HANDLE hbp = CMimAPI::m_pfnBeginBufferedPaint(hdcSrc, &rc, BPBF_TOPDOWNDIB, NULL, &hdcOut); + return(hbp); +} + +/** + * finalize buffered paint cycle and apply (if applicable) the global alpha value + * + * @param hbp HANDLE: handle of the buffered paint context + * @param rc RECT*: target rectangly where alpha value should be applied + */ +void CSkin::FinalizeBufferedPaint(HANDLE hbp, RECT *rc) +{ + if (m_pCurrentAeroEffect && m_pCurrentAeroEffect->m_finalAlpha > 0) + CMimAPI::m_pfnBufferedPaintSetAlpha(hbp, rc, m_pCurrentAeroEffect->m_finalAlpha); + CMimAPI::m_pfnEndBufferedPaint(hbp, TRUE); +} +/** + * Apply an effect to a aero glass area + * + * @param hdc HDC: device context + * @param rc RECT*: target rectangle + * @param iEffectArea + * int: area identifier (specifies which area we are drawing, so that allows to + * have different effects on different areas). + * Area can be the status bar, info panel, menu + * bar etc. + * @param hbp HANDLE: handle to a buffered paint identifier. + * default is none, needed forsome special + * effects. default paramenter is 0 + */ + +void CSkin::ApplyAeroEffect(const HDC hdc, const RECT *rc, int iEffectArea, HANDLE hbp) +{ + if (m_pCurrentAeroEffect == 0 || m_aeroEffect == AERO_EFFECT_NONE) + return; + + if (m_pCurrentAeroEffect->pfnEffectRenderer) + m_pCurrentAeroEffect->pfnEffectRenderer(hdc, rc, iEffectArea); +} + +/** aero effect callbacks + * + */ + +void CSkin::AeroEffectCallback_Milk(const HDC hdc, const RECT *rc, int iEffectArea) +{ + if (iEffectArea < 0x1000) { + int alpha = (iEffectArea == AERO_EFFECT_AREA_INFOPANEL) ? m_pCurrentAeroEffect->m_baseAlpha : 40; + if (iEffectArea == AERO_EFFECT_AREA_MENUBAR) + alpha = 90; + BYTE color2_trans = (iEffectArea == AERO_EFFECT_AREA_MENUBAR) ? 0 : 1; + DWORD corner = (iEffectArea == AERO_EFFECT_AREA_INFOPANEL) ? m_pCurrentAeroEffect->m_cornerRadius : 6; + + DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, alpha, m_pCurrentAeroEffect->m_gradientColor, + color2_trans, m_pCurrentAeroEffect->m_gradientType, m_pCurrentAeroEffect->m_cornerType, corner, 0); + } +} + +void CSkin::AeroEffectCallback_Carbon(const HDC hdc, const RECT *rc, int iEffectArea) +{ + if (iEffectArea < 0x1000) + DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, m_pCurrentAeroEffect->m_baseAlpha, + m_pCurrentAeroEffect->m_gradientColor, 0, m_pCurrentAeroEffect->m_gradientType, + m_pCurrentAeroEffect->m_cornerType, m_pCurrentAeroEffect->m_cornerRadius, 0); +} + +void CSkin::AeroEffectCallback_Solid(const HDC hdc, const RECT *rc, int iEffectArea) +{ + if (iEffectArea < 0x1000) { + if (iEffectArea == AERO_EFFECT_AREA_SIDEBAR_LEFT) + ::DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, m_pCurrentAeroEffect->m_baseAlpha, + m_pCurrentAeroEffect->m_gradientColor, 0, GRADIENT_TB + 1, + 0, 2, 0); + else + ::DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, m_pCurrentAeroEffect->m_baseAlpha, + m_pCurrentAeroEffect->m_gradientColor, 0, m_pCurrentAeroEffect->m_gradientType, + m_pCurrentAeroEffect->m_cornerType, m_pCurrentAeroEffect->m_cornerRadius, 0); + } + else { + BYTE bGradient = (iEffectArea & AERO_EFFECT_AREA_TAB_BOTTOM ? GRADIENT_BT : GRADIENT_TB) + 1; + ::DrawAlpha(hdc, const_cast(rc), m_pCurrentAeroEffect->m_baseColor, 70, + m_pCurrentAeroEffect->m_gradientColor, 1, bGradient, + m_pCurrentAeroEffect->m_cornerType, m_pCurrentAeroEffect->m_cornerRadius, 0); + } +} + +void CSkin::initAeroEffect() +{ + if (m_BrushBack) { + ::DeleteObject(m_BrushBack); + m_BrushBack = 0; + } + if (PluginConfig.m_bIsVista && m_aeroEffect > AERO_EFFECT_NONE && m_aeroEffect < AERO_EFFECT_LAST) { + m_currentAeroEffect = m_aeroEffects[m_aeroEffect]; + m_pCurrentAeroEffect = &m_currentAeroEffect; + m_glowSize = m_pCurrentAeroEffect->m_glowSize; + + if (m_pCurrentAeroEffect->m_clrToolbar == -1) + m_pCurrentAeroEffect->m_clrToolbar = PluginConfig.m_ipBackgroundGradientHigh; + + if (m_pCurrentAeroEffect->m_clrToolbar2 == -1) + m_pCurrentAeroEffect->m_clrToolbar2 = PluginConfig.m_ipBackgroundGradient; + else if (m_pCurrentAeroEffect->m_clrToolbar2 == 0) + m_pCurrentAeroEffect->m_clrToolbar2 = m_dwmColorRGB; + + if (m_aeroEffect == AERO_EFFECT_CUSTOM || m_aeroEffect == AERO_EFFECT_SOLID) { + m_pCurrentAeroEffect->m_baseColor = PluginConfig.m_ipBackgroundGradientHigh; + m_pCurrentAeroEffect->m_gradientColor = PluginConfig.m_ipBackgroundGradient; + if (m_aeroEffect == AERO_EFFECT_CUSTOM) + m_pCurrentAeroEffect->m_clrBack = PluginConfig.m_ipBackgroundGradientHigh; + } + + m_BrushBack = ::CreateSolidBrush(m_pCurrentAeroEffect->m_clrBack); + } else { + m_pCurrentAeroEffect = 0; + m_glowSize = 10; + m_BrushBack = ::CreateSolidBrush(0); + } + + for (TContainerData *p = pFirstContainer; p; p = p->pNext) { + InvalidateRect(GetDlgItem(p->hwnd, IDC_MSGTABS), NULL, TRUE); + InvalidateRect(p->hwnd, NULL, TRUE); + if (IsWindow(GetDlgItem(p->hwnd, 5000))) + InvalidateRect(GetDlgItem(p->hwnd, 5000), NULL, TRUE); + } +} + +void CSkin::setAeroEffect(LRESULT effect) +{ + if (effect == -1) + effect = static_cast(M.GetDword(SRMSGMOD_T, "aerostyle", AERO_EFFECT_NONE)); + + if (effect >= 0 && effect < AERO_EFFECT_LAST) + m_aeroEffect = (UINT)effect; + else + m_aeroEffect = AERO_EFFECT_NONE; + + initAeroEffect(); + db_set_dw(0, SRMSGMOD_T, "aerostyle", m_aeroEffect); +} + +/** + * extract the aero skin images from the DLL and store them in + * the private data folder. + * runs at every startup + */ +void CSkin::extractSkinsAndLogo(bool fForceOverwrite) const +{ + TCHAR tszBasePath[MAX_PATH]; + mir_sntprintf(tszBasePath, MAX_PATH, _T("%s"), M.getDataPath()); + if (tszBasePath[lstrlen(tszBasePath) - 1] != '\\') + _tcscat(tszBasePath, _T("\\")); + + CreateDirectoryTreeT(tszBasePath); + + m_fAeroSkinsValid = true; + + for (int i=0; i < SIZEOF(my_default_skin); i++) + if ( !Utils::extractResource(g_hInst, my_default_skin[i].ulID, _T("SKIN_GLYPH"), tszBasePath, my_default_skin[i].tszName, fForceOverwrite)) + m_fAeroSkinsValid = false; +} + +/** + * redraw the splitter area between the message input and message log + * area only + */ +void CSkin::UpdateToolbarBG(TWindowData *dat, DWORD dwRdwOptFlags) +{ + RECT rcUpdate, rcTmp; + POINT pt; + + if (dat) { + ::GetWindowRect(::GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &rcTmp); + + pt.x = rcTmp.left; + pt.y = rcTmp.top; + ::ScreenToClient(dat->hwnd, &pt); + + rcUpdate.left = 0; + rcUpdate.top = pt.y; + + ::GetClientRect(dat->hwnd, &rcTmp); + rcUpdate.right = rcTmp.right; + rcUpdate.bottom = rcTmp.bottom; + + if (M.isAero() || M.isDwmActive()) + dat->fLimitedUpdate = true; // skip unrelevant window updates when we have buffered paint avail + ::RedrawWindow(dat->hwnd, &rcUpdate, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW); + ::BB_RedrawButtons(dat); + dat->fLimitedUpdate = false; + } +} + +/** + * fill a background area with the default color. This can be either the configured + * fill color or default system color. + * + * @param hdc: device context + * @param rc: area to fill. + */ +void CSkin::FillBack(const HDC hdc, RECT* rc) +{ + if (0 == CSkin::m_BrushFill) { + if (PluginConfig.m_fillColor) + CSkin::m_BrushFill = ::CreateSolidBrush(PluginConfig.m_fillColor); + } + + if (PluginConfig.m_fillColor) + ::FillRect(hdc, rc, CSkin::m_BrushFill); + else + ::FillRect(hdc, rc, GetSysColorBrush(COLOR_3DFACE)); +} -- cgit v1.2.3