diff options
Diffstat (limited to 'plugins/TabSRMM/src')
34 files changed, 40492 insertions, 0 deletions
diff --git a/plugins/TabSRMM/src/ImageDataObject.cpp b/plugins/TabSRMM/src/ImageDataObject.cpp new file mode 100644 index 0000000000..762dbba537 --- /dev/null +++ b/plugins/TabSRMM/src/ImageDataObject.cpp @@ -0,0 +1,215 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * *
+ * $Id: ImageDataObject.cpp 11611 2010-04-22 12:36:29Z silvercircle $
+ *
+ * This inserts a bitmap into a rich edit control using OLE
+ * code partially taken from public example on the internet, source unknown.
+ *
+ * originally part of the smileyadd plugin for Miranda IM
+ *
+ */
+
+#include "commonheaders.h"
+#include "../include/ImageDataObject.h"
+
+extern void ReleaseRichEditOle(IRichEditOle *ole)
+{
+ ole->Release();
+}
+
+extern void ImageDataInsertBitmap(IRichEditOle *ole, HBITMAP hBm)
+{
+ CImageDataObject::InsertBitmap(ole, hBm);
+}
+
+int CacheIconToBMP(struct TLogIcon *theIcon, HICON hIcon, COLORREF backgroundColor, int sizeX, int sizeY)
+{
+ bool succeeded = false;
+
+ int IconSizeX = sizeX;
+ int IconSizeY = sizeY;
+
+ if ((IconSizeX == 0) || (IconSizeY == 0)) {
+ Utils::getIconSize(hIcon, IconSizeX, IconSizeY);
+ if (sizeX != 0)
+ IconSizeX = sizeX;
+ if (sizeY != 0)
+ IconSizeY = sizeY;
+ }
+ RECT rc;
+ BITMAPINFOHEADER bih = {0};
+ int widthBytes;
+ theIcon->hBkgBrush = CreateSolidBrush(backgroundColor);
+ bih.biSize = sizeof(bih);
+ bih.biBitCount = 24;
+ bih.biPlanes = 1;
+ bih.biCompression = BI_RGB;
+ bih.biHeight = IconSizeY;
+ bih.biWidth = IconSizeX;
+ widthBytes = ((bih.biWidth * bih.biBitCount + 31) >> 5) * 4;
+ rc.top = rc.left = 0;
+ rc.right = bih.biWidth;
+ rc.bottom = bih.biHeight;
+ theIcon->hdc = GetDC(0);
+ theIcon->hBmp = CreateCompatibleBitmap(theIcon->hdc, bih.biWidth, bih.biHeight);
+ theIcon->hdcMem = CreateCompatibleDC(theIcon->hdc);
+ theIcon->hoBmp = (HBITMAP)SelectObject(theIcon->hdcMem, theIcon->hBmp);
+ FillRect(theIcon->hdcMem, &rc, theIcon->hBkgBrush);
+ DrawIconEx(theIcon->hdcMem, 0, 0, hIcon, bih.biWidth, bih.biHeight, 0, NULL, DI_NORMAL);
+ SelectObject(theIcon->hdcMem, theIcon->hoBmp);
+ return TRUE;
+}
+
+void DeleteCachedIcon(struct TLogIcon *theIcon)
+{
+ DeleteDC(theIcon->hdcMem);
+ DeleteObject(theIcon->hBmp);
+ ReleaseDC(NULL, theIcon->hdc);
+ DeleteObject(theIcon->hBkgBrush);
+}
+
+// returns true on success, false on failure
+bool CImageDataObject::InsertBitmap(IRichEditOle* pRichEditOle, HBITMAP hBitmap)
+{
+ SCODE sc;
+ BITMAP bminfo;
+
+ // Get the image data object
+ //
+ CImageDataObject *pods = new CImageDataObject;
+ LPDATAOBJECT lpDataObject;
+ pods->QueryInterface(IID_IDataObject, (void **)&lpDataObject);
+
+ GetObject(hBitmap, sizeof(bminfo), &bminfo);
+ pods->SetBitmap(hBitmap);
+
+ // Get the RichEdit container site
+ //
+ IOleClientSite *pOleClientSite;
+ pRichEditOle->GetClientSite(&pOleClientSite);
+
+ // Initialize a Storage Object
+ //
+ IStorage *pStorage;
+
+ LPLOCKBYTES lpLockBytes = NULL;
+ sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
+ if (sc != S_OK) {
+ pOleClientSite->Release();
+ return false;
+ }
+ sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
+ STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_READWRITE, 0, &pStorage);
+ if (sc != S_OK) {
+ lpLockBytes = NULL;
+ pOleClientSite->Release();
+ return false;
+ }
+ // The final ole object which will be inserted in the richedit control
+ //
+ IOleObject *pOleObject;
+ pOleObject = pods->GetOleObject(pOleClientSite, pStorage);
+ if (pOleObject == NULL) {
+ pStorage->Release();
+ pOleClientSite->Release();
+ return false;
+ }
+
+ // all items are "contained" -- this makes our reference to this object
+ // weak -- which is needed for links to embedding silent update.
+ OleSetContainedObject(pOleObject, TRUE);
+
+ // Now Add the object to the RichEdit
+ //
+ REOBJECT reobject;
+ ZeroMemory(&reobject, sizeof(REOBJECT));
+ reobject.cbStruct = sizeof(REOBJECT);
+
+ CLSID clsid;
+ sc = pOleObject->GetUserClassID(&clsid);
+ if (sc != S_OK) {
+ pOleObject->Release();
+ pStorage->Release();
+ pOleClientSite->Release();
+ return false;
+ }
+
+ reobject.clsid = clsid;
+ reobject.cp = REO_CP_SELECTION ;
+ reobject.dvaspect = DVASPECT_CONTENT;
+ reobject.poleobj = pOleObject;
+ reobject.polesite = pOleClientSite;
+ reobject.pstg = pStorage;
+ reobject.dwFlags = bminfo.bmHeight <= 12 ? 0 : REO_BELOWBASELINE;
+
+ // Insert the bitmap at the current location in the richedit control
+ //
+ sc = pRichEditOle->InsertObject(&reobject);
+
+ // Release all unnecessary interfaces
+ //
+ pOleObject->Release();
+ pOleClientSite->Release();
+ lpLockBytes->Release();
+ pStorage->Release();
+ lpDataObject->Release();
+ if (sc != S_OK)
+ return false;
+ else
+ return true;
+}
+
+
+void CImageDataObject::SetBitmap(HBITMAP hBitmap)
+{
+ STGMEDIUM stgm;
+ stgm.tymed = TYMED_GDI; // Storage medium = HBITMAP handle
+ stgm.hBitmap = hBitmap;
+ stgm.pUnkForRelease = NULL; // Use ReleaseStgMedium
+
+ FORMATETC fm;
+ fm.cfFormat = CF_BITMAP; // Clipboard format = CF_BITMAP
+ fm.ptd = NULL; // Target Device = Screen
+ fm.dwAspect = DVASPECT_CONTENT; // Level of detail = Full content
+ fm.lindex = -1; // Index = Not applicaple
+ fm.tymed = TYMED_GDI; // Storage medium = HBITMAP handle
+
+ this->SetData(&fm, &stgm, TRUE);
+}
+
+
+IOleObject *CImageDataObject::GetOleObject(IOleClientSite *pOleClientSite, IStorage *pStorage)
+{
+ SCODE sc;
+ IOleObject *pOleObject;
+ sc = ::OleCreateStaticFromData(this, IID_IOleObject, OLERENDER_FORMAT,
+ &m_format, pOleClientSite, pStorage, (void **) & pOleObject);
+ if (sc != S_OK)
+ pOleObject = NULL;
+ return pOleObject;
+}
diff --git a/plugins/TabSRMM/src/TSButton.cpp b/plugins/TabSRMM/src/TSButton.cpp new file mode 100644 index 0000000000..57c3221f38 --- /dev/null +++ b/plugins/TabSRMM/src/TSButton.cpp @@ -0,0 +1,738 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: TSButton.cpp 11848 2010-05-27 14:57:22Z silvercircle $
+ *
+ * A skinnable button class for tabSRMM.
+ *
+ */
+
+#include "commonheaders.h"
+#include <ctype.h>
+
+#define PBS_PUSHDOWNPRESSED 6
+
+static LRESULT CALLBACK TSButtonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// External theme methods and properties
+
+static CRITICAL_SECTION csTips;
+static HWND hwndToolTips = NULL;
+static BLENDFUNCTION bf_buttonglyph;
+static HDC hdc_buttonglyph = 0;
+static HBITMAP hbm_buttonglyph, hbm_buttonglyph_old;
+
+// Used for our own cheap TrackMouseEvent
+#define BUTTON_POLLID 100
+#define BUTTON_POLLDELAY 50
+
+#define MGPROC(x) GetProcAddress(themeAPIHandle,x)
+
+int TSAPI UnloadTSButtonModule()
+{
+ DeleteCriticalSection(&csTips);
+ if (hdc_buttonglyph) {
+ SelectObject(hdc_buttonglyph, hbm_buttonglyph_old);
+ DeleteObject(hbm_buttonglyph);
+ DeleteDC(hdc_buttonglyph);
+ }
+ return 0;
+}
+
+int TSAPI LoadTSButtonModule(void)
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = _T("TSButtonClass");
+ wc.lpfnWndProc = TSButtonWndProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(MButtonCtrl*);
+ wc.hbrBackground = 0;
+ wc.style = CS_GLOBALCLASS | CS_PARENTDC;
+ RegisterClassEx(&wc);
+ InitializeCriticalSection(&csTips);
+ return 0;
+}
+
+static void TSAPI DestroyTheme(MButtonCtrl *ctl)
+{
+ if (M->isVSAPIState()) {
+ if (ctl->hThemeButton) {
+ CMimAPI::m_pfnCloseThemeData(ctl->hThemeButton);
+ ctl->hThemeButton = NULL;
+ }
+ if (ctl->hThemeToolbar) {
+ CMimAPI::m_pfnCloseThemeData(ctl->hThemeToolbar);
+ ctl->hThemeToolbar = NULL;
+ }
+ }
+}
+
+static void TSAPI LoadTheme(MButtonCtrl *ctl)
+{
+ if (M->isVSAPIState()) {
+ DestroyTheme(ctl);
+ ctl->hThemeButton = CMimAPI::m_pfnOpenThemeData(ctl->hwnd, L"BUTTON");
+ ctl->hThemeToolbar = (M->isAero() || IsWinVerVistaPlus()) ? CMimAPI::m_pfnOpenThemeData(ctl->hwnd, L"MENU") : CMimAPI::m_pfnOpenThemeData(ctl->hwnd, L"TOOLBAR");
+ ctl->bThemed = TRUE;
+ }
+}
+
+int TSAPI TBStateConvert2Flat(int state)
+{
+ switch (state) {
+ case PBS_NORMAL:
+ return TS_NORMAL;
+ case PBS_HOT:
+ return TS_HOT;
+ case PBS_PRESSED:
+ return TS_PRESSED;
+ case PBS_DISABLED:
+ return TS_DISABLED;
+ case PBS_DEFAULTED:
+ return TS_NORMAL;
+ }
+ return TS_NORMAL;
+}
+
+
+/**
+ * convert button state (hot, pressed, normal) to REBAR part state ids
+ *
+ * @param state int: button state
+ *
+ * @return int: state item id
+ */
+int TSAPI RBStateConvert2Flat(int state)
+{
+ switch (state) {
+ case PBS_NORMAL:
+ return 1;
+ case PBS_HOT:
+ return 2;
+ case PBS_PRESSED:
+ return 3;
+ case PBS_DISABLED:
+ return 1;
+ case PBS_DEFAULTED:
+ return 1;
+ }
+ return 1;
+}
+
+static void PaintWorker(MButtonCtrl *ctl, HDC hdcPaint)
+{
+ if (hdc_buttonglyph == 0) {
+ hdc_buttonglyph = CreateCompatibleDC(hdcPaint);
+ hbm_buttonglyph = CreateCompatibleBitmap(hdcPaint, 16, 16);
+ hbm_buttonglyph_old = (HBITMAP)SelectObject(hdc_buttonglyph, hbm_buttonglyph);
+ bf_buttonglyph.BlendFlags = 0;
+ bf_buttonglyph.SourceConstantAlpha = 120;
+ bf_buttonglyph.BlendOp = AC_SRC_OVER;
+ bf_buttonglyph.AlphaFormat = 0;
+ }
+ if (hdcPaint) {
+ HDC hdcMem;
+ HBITMAP hbmMem, hOld;
+ RECT rcClient;
+ RECT rcContent;
+ bool fAero = M->isAero();
+ bool fVSThemed = (!CSkin::m_skinEnabled && M->isVSThemed());
+ HANDLE hbp = 0;
+
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(GetParent(ctl->hwnd), GWLP_USERDATA);
+ GetClientRect(ctl->hwnd, const_cast<RECT *>(&rcClient));
+ CopyRect(&rcContent, &rcClient);
+
+ if(CMimAPI::m_haveBufferedPaint)
+ hbp = CMimAPI::m_pfnBeginBufferedPaint(hdcPaint, &rcContent, BPBF_TOPDOWNDIB, NULL, &hdcMem);
+ else {
+ hdcMem = CreateCompatibleDC(hdcPaint);
+ hbmMem = CreateCompatibleBitmap(hdcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
+ hOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
+ }
+
+ CSkin::FillBack(hdcMem, &rcContent);
+
+ if (ctl->pushBtn && ctl->pbState)
+ ctl->stateId = PBS_PRESSED;
+
+ if (ctl->flatBtn) {
+ if (ctl->pContainer && CSkin::m_skinEnabled) {
+ CSkinItem *item, *realItem = 0;
+ if (ctl->bTitleButton)
+ item = &SkinItems[ctl->stateId == PBS_NORMAL ? ID_EXTBKTITLEBUTTON : (ctl->stateId == PBS_HOT ? ID_EXTBKTITLEBUTTONMOUSEOVER : ID_EXTBKTITLEBUTTONPRESSED)];
+ else {
+ item = &SkinItems[(ctl->stateId == PBS_NORMAL || ctl->stateId == PBS_DISABLED) ? ID_EXTBKBUTTONSNPRESSED : (ctl->stateId == PBS_HOT ? ID_EXTBKBUTTONSMOUSEOVER : ID_EXTBKBUTTONSPRESSED)];
+ realItem = item;
+ }
+ CSkin::SkinDrawBG(ctl->hwnd, ctl->pContainer->hwnd, ctl->pContainer, &rcContent, hdcMem);
+ if (!item->IGNORED) {
+ RECT rc1 = rcClient;
+ rc1.left += item->MARGIN_LEFT;
+ rc1.right -= item->MARGIN_RIGHT;
+ rc1.top += item->MARGIN_TOP;
+ rc1.bottom -= item->MARGIN_BOTTOM;
+ CSkin::DrawItem(hdcMem, &rc1, item);
+ } else
+ goto flat_themed;
+ } else {
+flat_themed:
+ int state = IsWindowEnabled(ctl->hwnd) ? (ctl->stateId == PBS_NORMAL && ctl->defbutton ? PBS_DEFAULTED : ctl->stateId) : PBS_DISABLED;
+
+ if (ctl->bToolbarButton) {
+ if(dat) {
+ RECT rcWin;
+ GetWindowRect(ctl->hwnd, &rcWin);
+ POINT pt;
+ pt.x = rcWin.left;
+ ScreenToClient(dat->hwnd, &pt);
+ BitBlt(hdcMem, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
+ dat->pContainer->cachedToolbarDC, pt.x, 1, SRCCOPY);
+ }
+ }
+ if (ctl->hThemeToolbar && ctl->bThemed && 1 == dat->pContainer->bTBRenderingMode) {
+ if(fAero || PluginConfig.m_WinVerMajor >= 6)
+ CMimAPI::m_pfnDrawThemeBackground(ctl->hThemeToolbar, hdcMem, 8, RBStateConvert2Flat(state), &rcClient, &rcClient);
+ else
+ CMimAPI::m_pfnDrawThemeBackground(ctl->hThemeToolbar, hdcMem, TP_BUTTON, TBStateConvert2Flat(state), &rcClient, &rcClient);
+ } else {
+ CSkin::m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, state == PBS_HOT ? 220 : 180);
+ if(state == PBS_HOT || state == PBS_PRESSED) {
+ if(state == PBS_PRESSED) {
+ RECT rc = rcClient;
+ InflateRect(&rc, -1, -1);
+ HBRUSH bBack = CreateSolidBrush(PluginConfig.m_tbBackgroundLow ? PluginConfig.m_tbBackgroundLow : GetSysColor(COLOR_3DDKSHADOW));
+ FillRect(hdcMem, &rc, bBack);
+ DeleteObject(bBack);
+ }
+ CSkin::m_switchBarItem->Render(hdcMem, &rcClient, true);
+ }
+ }
+ }
+ } else {
+ if (ctl->pContainer && CSkin::m_skinEnabled) {
+ CSkinItem *item, *realItem = 0;
+ if (ctl->bTitleButton)
+ item = &SkinItems[ctl->stateId == PBS_NORMAL ? ID_EXTBKTITLEBUTTON : (ctl->stateId == PBS_HOT ? ID_EXTBKTITLEBUTTONMOUSEOVER : ID_EXTBKTITLEBUTTONPRESSED)];
+ else {
+ item = &SkinItems[(ctl->stateId == PBS_NORMAL || ctl->stateId == PBS_DISABLED) ? ID_EXTBKBUTTONSNPRESSED : (ctl->stateId == PBS_HOT ? ID_EXTBKBUTTONSMOUSEOVER : ID_EXTBKBUTTONSPRESSED)];
+ realItem = item;
+ }
+ CSkin::SkinDrawBG(ctl->hwnd, ctl->pContainer->hwnd, ctl->pContainer, &rcClient, hdcMem);
+ if (!item->IGNORED) {
+ RECT rc1 = rcClient;
+ rc1.left += item->MARGIN_LEFT;
+ rc1.right -= item->MARGIN_RIGHT;
+ rc1.top += item->MARGIN_TOP;
+ rc1.bottom -= item->MARGIN_BOTTOM;
+ CSkin::DrawItem(hdcMem, &rc1, item);
+ } else
+ goto nonflat_themed;
+ } else {
+nonflat_themed:
+ int state = IsWindowEnabled(ctl->hwnd) ? (ctl->stateId == PBS_NORMAL && ctl->defbutton ? PBS_DEFAULTED : ctl->stateId) : PBS_DISABLED;
+
+ if (ctl->hThemeButton && ctl->bThemed && 0 == PluginConfig.m_fillColor) {
+ CMimAPI::m_pfnDrawThemeBackground(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, state, &rcClient, &rcClient);
+ CMimAPI::m_pfnGetThemeBackgroundContentRect(ctl->hThemeToolbar, hdcMem, BP_PUSHBUTTON, PBS_NORMAL, &rcClient, &rcContent);
+ } else {
+ CSkin::m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, state == PBS_NORMAL ? 140 : 240);
+ if(state == PBS_PRESSED) {
+ RECT rc = rcClient;
+ InflateRect(&rc, -1, -1);
+ HBRUSH bBack = CreateSolidBrush(PluginConfig.m_tbBackgroundLow ? PluginConfig.m_tbBackgroundLow : GetSysColor(COLOR_3DDKSHADOW));
+ FillRect(hdcMem, &rc, bBack);
+ DeleteObject(bBack);
+ }
+ CSkin::m_switchBarItem->Render(hdcMem, &rcClient, true);
+ }
+
+ // Draw focus rectangle if button has focus
+ if (ctl->focus) {
+ RECT focusRect = rcClient;
+ InflateRect(&focusRect, -3, -3);
+ DrawFocusRect(hdcMem, &focusRect);
+ }
+ }
+ }
+
+ /*
+ * render content + */
+ if (ctl->arrow) {
+ rcContent.top += 2;
+ rcContent.bottom -= 2;
+ rcContent.left = rcClient.right - 12;
+ rcContent.right = rcContent.left;
+
+ DrawIconEx(hdcMem, rcClient.right - 15, (rcClient.bottom - rcClient.top) / 2 - (PluginConfig.m_smcyicon / 2),
+ PluginConfig.g_buttonBarIcons[ICON_DEFAULT_PULLDOWN], 16, 16, 0, 0, DI_NORMAL);
+ }
+
+ if (ctl->hIcon || ctl->hIconPrivate) {
+ int ix = (rcClient.right - rcClient.left) / 2 - 8;
+ int iy = (rcClient.bottom - rcClient.top) / 2 - 8;
+ HICON hIconNew = ctl->hIconPrivate != 0 ? ctl->hIconPrivate : ctl->hIcon;
+
+ if (ctl->stateId == PBS_PRESSED) {
+ ix++;
+ iy++;
+ }
+
+ if (ctl->arrow)
+ ix -= 4;
+
+ if (ctl->dimmed && PluginConfig.m_IdleDetect)
+ CSkin::DrawDimmedIcon(hdcMem, ix, iy, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIconNew, 180);
+ else {
+ if (ctl->stateId != PBS_DISABLED || CMimAPI::m_MyAlphaBlend == 0) {
+ DrawIconEx(hdcMem, ix, iy, hIconNew, 16, 16, 0, 0, DI_NORMAL);
+ if(ctl->overlay)
+ DrawIconEx(hdcMem, ix, iy, ctl->overlay, 16, 16, 0, 0, DI_NORMAL);
+ }
+ else {
+ BitBlt(hdc_buttonglyph, 0, 0, 16, 16, hdcMem, ix, iy, SRCCOPY);
+ DrawIconEx(hdc_buttonglyph, 0, 0, hIconNew, 16, 16, 0, 0, DI_NORMAL);
+ if(ctl->overlay)
+ DrawIconEx(hdc_buttonglyph, 0, 0, ctl->overlay, 16, 16, 0, 0, DI_NORMAL);
+ CMimAPI::m_MyAlphaBlend(hdcMem, ix, iy, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hdc_buttonglyph, 0, 0, 16, 16, bf_buttonglyph);
+ }
+ }
+ } else if (GetWindowTextLength(ctl->hwnd)) {
+ // Draw the text and optinally the arrow
+ TCHAR szText[MAX_PATH];
+ SIZE sz;
+ RECT rcText;
+ HFONT hOldFont;
+
+ CopyRect(&rcText, &rcClient);
+ GetWindowText(ctl->hwnd, szText, MAX_PATH - 1);
+ SetBkMode(hdcMem, TRANSPARENT);
+ hOldFont = (HFONT)SelectObject(hdcMem, ctl->hFont);
+ if (ctl->pContainer && CSkin::m_skinEnabled)
+ SetTextColor(hdcMem, IsWindowEnabled(ctl->hwnd) ? CSkin::m_DefaultFontColor : GetSysColor(COLOR_GRAYTEXT));
+ else {
+ if(PluginConfig.m_genericTxtColor)
+ SetTextColor(hdcMem, PluginConfig.m_genericTxtColor);
+ else
+ SetTextColor(hdcMem, IsWindowEnabled(ctl->hwnd) || !ctl->hThemeButton ? GetSysColor(COLOR_BTNTEXT) : GetSysColor(COLOR_GRAYTEXT));
+ }
+ GetTextExtentPoint32(hdcMem, szText, lstrlen(szText), &sz);
+ if (ctl->cHot) {
+ SIZE szHot;
+
+ GetTextExtentPoint32A(hdcMem, "&", 1, &szHot);
+ sz.cx -= szHot.cx;
+ }
+ if (ctl->arrow)
+ DrawState(hdcMem, NULL, NULL, (LPARAM)ctl->arrow, 0, rcClient.right - rcClient.left - 5 - PluginConfig.m_smcxicon + (!ctl->hThemeButton && ctl->stateId == PBS_PRESSED ? 1 : 0), (rcClient.bottom - rcClient.top) / 2 - PluginConfig.m_smcyicon / 2 + (!ctl->hThemeButton && ctl->stateId == PBS_PRESSED ? 1 : 0), PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, IsWindowEnabled(ctl->hwnd) ? DST_ICON : DST_ICON | DSS_DISABLED);
+ SelectObject(hdcMem, ctl->hFont);
+ DrawState(hdcMem, NULL, NULL, (LPARAM)szText, lstrlen(szText), (rcText.right - rcText.left - sz.cx) / 2 + (!ctl->hThemeButton && ctl->stateId == PBS_PRESSED ? 1 : 0), ctl->hThemeButton ? (rcText.bottom - rcText.top - sz.cy) / 2 : (rcText.bottom - rcText.top - sz.cy) / 2 - (ctl->stateId == PBS_PRESSED ? 0 : 1), sz.cx, sz.cy, IsWindowEnabled(ctl->hwnd) || ctl->hThemeButton ? DST_PREFIXTEXT | DSS_NORMAL : DST_PREFIXTEXT | DSS_DISABLED);
+ SelectObject(hdcMem, hOldFont);
+ }
+ if(hbp)
+ CMimAPI::m_pfnEndBufferedPaint(hbp, TRUE);
+ else {
+ BitBlt(hdcPaint, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hOld);
+ DeleteObject(hbmMem);
+ DeleteDC(hdcMem);
+ }
+
+ }
+}
+
+static LRESULT CALLBACK TSButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MButtonCtrl* bct = (MButtonCtrl *)GetWindowLongPtr(hwndDlg, 0);
+ switch (msg) {
+ case WM_NCCREATE: {
+ SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW);
+ bct = (MButtonCtrl *)malloc(sizeof(MButtonCtrl));
+ if (bct == NULL)
+ return FALSE;
+ ZeroMemory(bct, sizeof(MButtonCtrl));
+ bct->hwnd = hwndDlg;
+ bct->stateId = PBS_NORMAL;
+ bct->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+ LoadTheme(bct);
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)bct);
+ if (((CREATESTRUCT *)lParam)->lpszName)
+ SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName);
+ return TRUE;
+ }
+ case WM_DESTROY: {
+ if (bct) {
+ EnterCriticalSection(&csTips);
+ if (hwndToolTips) {
+ TOOLINFO ti;
+
+ ZeroMemory(&ti, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ }
+ if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
+ DestroyWindow(hwndToolTips);
+ hwndToolTips = NULL;
+ }
+ }
+ if (bct->hIconPrivate)
+ DestroyIcon(bct->hIconPrivate);
+ LeaveCriticalSection(&csTips);
+ DestroyTheme(bct);
+ }
+ break; // DONT! fall thru
+ }
+
+ case WM_NCDESTROY:
+ free(bct);
+ SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)NULL);
+ break;
+
+ case WM_SETTEXT: {
+ bct->cHot = 0;
+ if ((TCHAR *)lParam) {
+ TCHAR *tmp = (TCHAR *)lParam;
+ while (*tmp) {
+ if (*tmp == (TCHAR)'&' && *(tmp + 1)) {
+ bct->cHot = _totlower(*(tmp + 1));
+ break;
+ }
+ tmp++;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+ }
+ case WM_KEYUP:
+ if (bct->stateId != PBS_DISABLED && wParam == VK_SPACE && !(GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000)) {
+ if (bct->pushBtn) {
+ if (bct->pbState) bct->pbState = 0;
+ else bct->pbState = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+ case WM_SYSKEYUP:
+ if (bct->stateId != PBS_DISABLED && bct->cHot && bct->cHot == tolower((int)wParam)) {
+ if (bct->pushBtn) {
+ if (bct->pbState) bct->pbState = 0;
+ else bct->pbState = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM)hwndDlg);
+ return 0;
+ }
+ break;
+ case WM_THEMECHANGED: {
+ // themed changed, reload theme object
+ if (bct->bThemed)
+ LoadTheme(bct);
+ InvalidateRect(bct->hwnd, NULL, TRUE); // repaint it
+ break;
+ }
+ case WM_SETFONT: { // remember the font so we can use it later
+ bct->hFont = (HFONT)wParam; // maybe we should redraw?
+ break;
+ }
+ case WM_NCPAINT:
+ return(0);
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hdcPaint;
+
+ hdcPaint = BeginPaint(hwndDlg, &ps);
+ if (hdcPaint) {
+ if(bct->sitem)
+ bct->sitem->RenderThis(hdcPaint);
+ else
+ PaintWorker(bct, hdcPaint);
+ EndPaint(hwndDlg, &ps);
+ }
+ return(0);
+ }
+ case BM_SETIMAGE:
+ if (wParam == IMAGE_ICON) {
+ ICONINFO ii;
+ BITMAP bm;
+
+ if (bct->hIconPrivate)
+ DestroyIcon(bct->hIconPrivate);
+
+ GetIconInfo((HICON)lParam, &ii);
+ GetObject(ii.hbmColor, sizeof(bm), &bm);
+ if (bm.bmWidth != PluginConfig.m_smcxicon || bm.bmHeight != PluginConfig.m_smcyicon) {
+ HIMAGELIST hImageList;
+ hImageList = ImageList_Create(PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.m_bIsXP ? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, 1, 0);
+ ImageList_AddIcon(hImageList, (HICON)lParam);
+ bct->hIconPrivate = ImageList_GetIcon(hImageList, 0, ILD_NORMAL);
+ ImageList_RemoveAll(hImageList);
+ ImageList_Destroy(hImageList);
+ bct->hIcon = 0;
+ } else {
+ bct->hIcon = (HICON)lParam;
+ bct->hIconPrivate = 0;
+ }
+
+ DeleteObject(ii.hbmMask);
+ DeleteObject(ii.hbmColor);
+ bct->hBitmap = NULL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ } else if (wParam == IMAGE_BITMAP) {
+ bct->hBitmap = (HBITMAP)lParam;
+ if (bct->hIconPrivate)
+ DestroyIcon(bct->hIconPrivate);
+ bct->hIcon = bct->hIconPrivate = NULL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+ case BM_SETCHECK:
+ if (!bct->pushBtn) break;
+ if (wParam == BST_CHECKED) {
+ bct->pbState = 1;
+ bct->stateId = PBS_PRESSED;
+ } else if (wParam == BST_UNCHECKED) {
+ bct->pbState = 0;
+ bct->stateId = PBS_NORMAL;
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BM_GETCHECK:
+ if (bct->pushBtn) {
+ return bct->pbState ? BST_CHECKED : BST_UNCHECKED;
+ }
+ return 0;
+ case BUTTONSETARROW: // turn arrow on/off
+ bct->arrow = (HICON)wParam;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETDEFAULT:
+ bct->defbutton = wParam ? 1 : 0;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETASPUSHBTN:
+ bct->pushBtn = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETASFLATBTN:
+ bct->flatBtn = lParam == 0 ? 1 : 0;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case BUTTONSETASFLATBTN + 10:
+ bct->bThemed = lParam ? TRUE : FALSE;
+ break;
+ case BUTTONSETASFLATBTN + 11:
+ bct->dimmed = lParam ? TRUE : FALSE;
+ break;
+ case BUTTONSETASFLATBTN + 12:
+ bct->pContainer = (struct TContainerData *)lParam;
+ break;
+ case BUTTONSETASFLATBTN + 13:
+ bct->bTitleButton = TRUE;
+ break;
+ case BUTTONSETASFLATBTN + 14:
+ bct->stateId = (wParam) ? PBS_NORMAL : PBS_DISABLED;
+ InvalidateRect(bct->hwnd, NULL, FALSE);
+ break;
+ case BUTTONSETASFLATBTN + 15:
+ return bct->stateId;
+ case BUTTONSETASTOOLBARBUTTON:
+ bct->bToolbarButton = lParam;
+ break;
+ case BUTTONSETASSIDEBARBUTTON:
+ bct->sitem = reinterpret_cast<CSideBarButton *>(lParam);
+ break;
+ case BUTTONSETOVERLAYICON:
+ bct->overlay = (HICON)lParam;
+ break;
+ case BUTTONADDTOOLTIP: {
+ TOOLINFO ti;
+
+ if (!(char*)wParam)
+ break;
+ EnterCriticalSection(&csTips);
+ if (!hwndToolTips) {
+ hwndToolTips = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, _T(""), WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
+ }
+ ZeroMemory(&ti, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ }
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ ti.lpszText = (TCHAR *)wParam;
+ SendMessage(hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
+ SendMessage(hwndToolTips, TTM_SETMAXTIPWIDTH, 0, 300);
+ LeaveCriticalSection(&csTips);
+ break;
+ }
+ case WM_SETFOCUS: // set keybord focus and redraw
+ bct->focus = 1;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case WM_KILLFOCUS: // kill focus and redraw
+ bct->focus = 0;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ case WM_ENABLE: { // windows tells us to enable/disable
+ bct->stateId = wParam ? PBS_NORMAL : PBS_DISABLED;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ break;
+ }
+ case WM_MOUSELEAVE: { // faked by the WM_TIMER
+ if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
+ bct->stateId = PBS_NORMAL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ if(bct->sitem)
+ bct->sitem->invokeContextMenu();
+ break;
+
+ case WM_MBUTTONUP:
+ if(bct->sitem) {
+ if(bct->sitem->getDat())
+ SendMessage(bct->sitem->getDat()->hwnd, WM_CLOSE, 1, 0);
+ }
+ break;
+
+ case WM_LBUTTONDOWN: {
+ RECT rc;
+
+ if(bct->sitem) {
+ if(bct->sitem->testCloseButton() != -1)
+ return(TRUE);
+ bct->stateId = PBS_PRESSED;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ bct->sitem->activateSession();
+ }
+
+ if (bct->arrow) {
+ GetClientRect(bct->hwnd, &rc);
+ if (LOWORD(lParam) < rc.right - 12 && bct->stateId != PBS_DISABLED)
+ bct->stateId = PBS_PRESSED;
+ else if(LOWORD(lParam) > rc.right - 12) {
+ if(GetDlgCtrlID(hwndDlg) == IDOK || bct->stateId != PBS_DISABLED) {
+ WORD w = (WORD)((int)bct->arrow & 0x0000ffff);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(w, BN_CLICKED), (LPARAM)hwndDlg);
+ }
+ }
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ } else if (bct->stateId != PBS_DISABLED) {
+ bct->stateId = PBS_PRESSED;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ break;
+ }
+ case WM_LBUTTONUP: {
+ int showClick = 0;
+
+ if (bct->sitem) {
+ if(bct->sitem->testCloseButton() != -1) {
+ SendMessage(bct->sitem->getDat()->hwnd, WM_CLOSE, 1, 0);
+ return(TRUE);
+ }
+ }
+ if (bct->pushBtn) {
+ if (bct->pbState) bct->pbState = 0;
+ else bct->pbState = 1;
+ }
+ if (bct->stateId != PBS_DISABLED) { // don't change states if disabled
+ if(bct->stateId == PBS_PRESSED)
+ showClick = 1;
+ if (msg == WM_LBUTTONUP)
+ bct->stateId = PBS_HOT;
+ else
+ bct->stateId = PBS_NORMAL;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ if(showClick)
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM)hwndDlg);
+ break;
+ }
+ case WM_MOUSEMOVE:
+ if (bct->stateId == PBS_NORMAL) {
+ bct->stateId = PBS_HOT;
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ } else if (bct->arrow && bct->stateId == PBS_HOT) {
+ InvalidateRect(bct->hwnd, NULL, TRUE);
+ }
+ // Call timer, used to start cheesy TrackMouseEvent faker
+ SetTimer(hwndDlg, BUTTON_POLLID, BUTTON_POLLDELAY, NULL);
+ if(bct->sitem) {
+ if(bct->sitem->testCloseButton() != -1) {
+ if(bct->sitem->m_sideBar->getHoveredClose() != bct->sitem) {
+ bct->sitem->m_sideBar->setHoveredClose(bct->sitem);
+ InvalidateRect(hwndDlg, 0, FALSE);
+ }
+ }
+ else {
+ bct->sitem->m_sideBar->setHoveredClose(0);
+ InvalidateRect(hwndDlg, 0, FALSE);
+ }
+ }
+ break;
+ case WM_TIMER: { // use a timer to check if they have did a mouseout
+ if (wParam == BUTTON_POLLID) {
+ RECT rc;
+ POINT pt;
+ GetWindowRect(hwndDlg, &rc);
+ GetCursorPos(&pt);
+ if (!PtInRect(&rc, pt)) { // mouse must be gone, trigger mouse leave
+ PostMessage(hwndDlg, WM_MOUSELEAVE, 0, 0L);
+ KillTimer(hwndDlg, BUTTON_POLLID);
+ if(bct->sitem) {
+ bct->sitem->m_sideBar->setHoveredClose(0);
+ InvalidateRect(hwndDlg, 0, FALSE);
+ }
+ }
+ }
+ break;
+ }
+ case WM_ERASEBKGND:
+ return(1);
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
diff --git a/plugins/TabSRMM/src/buttonsbar.cpp b/plugins/TabSRMM/src/buttonsbar.cpp new file mode 100644 index 0000000000..fb50f7b576 --- /dev/null +++ b/plugins/TabSRMM/src/buttonsbar.cpp @@ -0,0 +1,1622 @@ +#include "commonheaders.h"
+#pragma hdrstop
+
+static HANDLE hButtonsBarAddButton;
+static HANDLE hButtonsBarRemoveButton;
+static HANDLE hButtonsBarGetButtonState;
+static HANDLE hButtonsBarSetButtonState;
+static HANDLE hButtonsBarModifyButton;
+
+HANDLE hHookButtonPressedEvt;
+HANDLE hHookToolBarLoadedEvt;
+
+static SortedList * RButtonsList;
+static SortedList * LButtonsList;
+
+DWORD LastCID = 4000;
+DWORD dwSepCount = 0;
+BOOL bNeedResort = FALSE;
+
+CRITICAL_SECTION ToolBarCS;
+
+typedef void (*ItemDestuctor)(void*);
+
+
+//////////////////////////////////////////////////////////////////////////////////
+//
+//some code parts from ClistModern toolbar by FYR
+//
+/////////////////////////////////////////////////////////////////////////////////
+static int sstSortButtons(const void * vmtbi1, const void * vmtbi2)
+{
+ CustomButtonData * mtbi1 = (CustomButtonData *) * ((CustomButtonData **)vmtbi1);
+ CustomButtonData * mtbi2 = (CustomButtonData *) * ((CustomButtonData **)vmtbi2);
+ if (mtbi1 == NULL || mtbi2 == NULL) return (mtbi1 - mtbi2);
+ return mtbi1->dwPosition - mtbi2->dwPosition;
+}
+
+
+static void li_ListDestruct(SortedList *pList, ItemDestuctor pItemDestructor)
+{
+ int i = 0;
+ if (!pList) return;
+ for (i = 0; i < pList->realCount; i++) pItemDestructor(pList->items[i]);
+ li.List_Destroy(pList);
+ mir_free(pList);
+}
+
+static void li_RemoveDestruct(SortedList *pList, int index, ItemDestuctor pItemDestructor)
+{
+ if (index >= 0 && index < pList->realCount) {
+ pItemDestructor(pList->items[index]);
+ li.List_Remove(pList, index);
+ }
+}
+
+static void li_RemovePtrDestruct(SortedList *pList, void * ptr, ItemDestuctor pItemDestructor)
+{
+ if (li.List_RemovePtr(pList, ptr))
+ pItemDestructor(ptr);
+}
+
+static void li_SortList(SortedList *pList, FSortFunc pSortFunct)
+{
+ FSortFunc pOldSort = pList->sortFunc;
+ int i;
+ if (!pSortFunct) pSortFunct = pOldSort;
+ pList->sortFunc = NULL;
+ for (i = 0; i < pList->realCount - 1; i++)
+ if (pOldSort(pList->items[i], pList->items[i+1]) < 0) {
+ void * temp = pList->items[i];
+ pList->items[i] = pList->items[i+1];
+ pList->items[i+1] = temp;
+ i--;
+ if (i > 0) i--;
+ }
+ pList->sortFunc = pOldSort;
+}
+
+static void listdestructor(void * input)
+{
+ CustomButtonData * cbdi = (CustomButtonData *)input;
+ if (cbdi->pszModuleName) mir_free(cbdi->pszModuleName);
+ if (cbdi->ptszTooltip) mir_free(cbdi->ptszTooltip);
+ mir_free(cbdi);
+}
+
+//from "advanced auto away" plugin by P. Boon
+struct RemoveSettings {
+ char *szPrefix;
+ int count;
+ char **szSettings;
+};
+
+static int DBRemoveEnumProc(const char *szSetting, LPARAM lParam)
+{
+ struct RemoveSettings *rs = (struct RemoveSettings *)lParam;
+
+ if (!rs->szPrefix || !strncmp(szSetting, rs->szPrefix, strlen(rs->szPrefix))) {
+ rs->szSettings = (char **)realloc(rs->szSettings, (rs->count + 1) * sizeof(char *));
+ rs->szSettings[rs->count] = _strdup(szSetting);
+ rs->count += 1;
+ }
+ return 0;
+}
+
+static int Hlp_RemoveDatabaseSettings(HANDLE hContact, char *szModule, char *szPrefix)
+{
+ DBCONTACTENUMSETTINGS dbces;
+ struct RemoveSettings rs;
+ int i, count;
+
+ ZeroMemory(&rs, sizeof(struct RemoveSettings));
+ rs.szPrefix = szPrefix;
+ ZeroMemory(&dbces, sizeof(DBCONTACTENUMSETTINGS));
+ dbces.pfnEnumProc = DBRemoveEnumProc;
+ dbces.lParam = (LPARAM) & rs;
+ dbces.szModule = szModule;
+ if (CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)(HANDLE)hContact, (LPARAM)&dbces) == -1) {
+
+ return -1;
+ }
+ count = 0;
+ if (rs.szSettings != NULL) {
+ for (i = 0; i < rs.count; i++) {
+ if (rs.szSettings[i] != NULL) {
+ if (!DBDeleteContactSetting(hContact, szModule, rs.szSettings[i])) {
+ count += 1;
+ }
+ free(rs.szSettings[i]);
+ }
+ }
+ free(rs.szSettings);
+ }
+
+ return count;
+}
+
+void CB_InitCustomButtons()
+{
+ LButtonsList = li.List_Create(0, 1);
+ RButtonsList = li.List_Create(0, 1);
+ InitializeCriticalSection(&ToolBarCS);
+ dwSepCount = M->GetDword("TabSRMM_Toolbar", "SeparatorsCount", 0);
+
+ //dwSepCount = DBGetContactSettingDword(NULL, "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()
+{
+ DeleteCriticalSection(&ToolBarCS);
+ li_ListDestruct(LButtonsList, listdestructor);
+ li_ListDestruct(RButtonsList, listdestructor);
+ DestroyHookableEvent(hHookToolBarLoadedEvt);
+ DestroyHookableEvent(hHookButtonPressedEvt);
+ DestroyServiceFunction(hButtonsBarAddButton);
+ DestroyServiceFunction(hButtonsBarRemoveButton);
+ DestroyServiceFunction(hButtonsBarModifyButton);
+ DestroyServiceFunction(hButtonsBarGetButtonState);
+ DestroyServiceFunction(hButtonsBarSetButtonState);
+}
+
+void CB_DestroyAllButtons(HWND hwndDlg, struct TWindowData *dat)
+{
+ int i;
+ HWND hwndBtn = NULL;
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ {
+ hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID);
+ if (hwndBtn) DestroyWindow(hwndBtn);
+ }
+ }
+
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ {
+ hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID);
+ if (hwndBtn) DestroyWindow(hwndBtn);
+ }
+ }
+}
+
+void CB_DestroyButton(HWND hwndDlg, struct 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, struct TWindowData *dat, CustomButtonData* cbd)
+{
+ HWND hwndBtn = GetDlgItem(hwndDlg, cbd->dwButtonCID);
+ if (hwndBtn) {
+ if (cbd->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)cbd->hIcon));
+ if (cbd->ptszTooltip)
+ SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), 0);
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN + 12, 0, (LPARAM)dat->pContainer);
+ }
+}
+
+void CB_ReInitCustomButtons()
+{
+ int i;
+
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ //GetButtonSettings(NULL,cbd);
+ if (cbd->opFlags&BBSF_NTBSWAPED || cbd->opFlags&BBSF_NTBDESTRUCT) {
+ cbd->opFlags ^= BBSF_NTBSWAPED;
+
+ if (!(cbd->opFlags&BBSF_NTBDESTRUCT))
+ li.List_InsertPtr(RButtonsList, cbd);
+
+ li.List_Remove(LButtonsList, i);
+ i--;
+ }
+ }
+
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ if (cbd->opFlags&BBSF_NTBSWAPED || cbd->opFlags&BBSF_NTBDESTRUCT) {
+ cbd->opFlags ^= BBSF_NTBSWAPED;
+
+ if (!(cbd->opFlags&BBSF_NTBDESTRUCT))
+ li.List_InsertPtr(LButtonsList, cbd);
+
+ li.List_Remove(RButtonsList, i);
+ i--;
+ }
+ }
+ M->BroadcastMessage(DM_BBNEEDUPDATE, 0, 0);
+ M->BroadcastMessage(DM_LOADBUTTONBARICONS, 0, 0);
+}
+
+void CB_HardReInit()
+{
+ M->BroadcastMessage(DM_CBDESTROY, 0, 0);
+ EnterCriticalSection(&ToolBarCS);
+ li_ListDestruct(LButtonsList, listdestructor);
+ li_ListDestruct(RButtonsList, listdestructor);
+ LButtonsList = li.List_Create(0, 1);
+ RButtonsList = li.List_Create(0, 1);
+ LeaveCriticalSection(&ToolBarCS);
+ LastCID = 4000;
+ dwSepCount = 0;
+
+ CB_InitDefaultButtons();
+ NotifyEventHooks(hHookToolBarLoadedEvt, (WPARAM)0, (LPARAM)0);
+}
+
+static INT_PTR CB_AddButton(WPARAM wParam, LPARAM lParam)
+{
+ BBButton * bbdi = (BBButton *)lParam;
+ bNeedResort = TRUE;
+ if (bbdi->cbSize != sizeof(BBButton))
+ return 1;
+ {
+ CustomButtonData *cbd = (CustomButtonData *)mir_alloc(sizeof(CustomButtonData));
+ memset(cbd, 0, sizeof(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) ? 1 : 0;
+ cbd->bLSided = (bbdi->bbbFlags & BBBF_ISLSIDEBUTTON) ? 1 : 0;
+ cbd->bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) ? 1 : 0;
+ cbd->bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) ? 1 : 0;
+ cbd->bDummy = (bbdi->bbbFlags & BBBF_ISDUMMYBUTTON) ? 1 : 0;
+ cbd->bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) ? 1 : 0;
+ cbd->bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) ? 1 : 0;
+ cbd->bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) ? 1 : 0;
+ cbd->bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) ? 1 : 0;
+
+ CB_GetButtonSettings(NULL, cbd);
+
+ if (cbd->bLSided)
+ li.List_InsertPtr(LButtonsList, cbd);
+ else if (cbd->bRSided)
+ li.List_InsertPtr(RButtonsList, 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)
+{
+ HWND hwndDlg = NULL;
+ int i;
+ DWORD tempCID = 0;
+ BOOL realbutton = 0;
+ BBButton * bbdi = (BBButton *)lParam;
+ bbdi->bbbFlags = 0;
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ realbutton = 1;
+ tempCID = cbd->dwButtonCID;
+ }
+ }
+ if (!realbutton)
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ realbutton = 1;
+ tempCID = cbd->dwButtonCID;
+ }
+ }
+
+ if (!realbutton) return 1;
+ hwndDlg = M->FindWindow((HANDLE)wParam);
+ bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(GetDlgItem(hwndDlg, tempCID)) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(GetDlgItem(hwndDlg, tempCID)) ? 0 : BBSF_DISABLED);
+ return 0;
+}
+
+static INT_PTR CB_SetButtonState(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndDlg;
+ int i;
+ BOOL realbutton = 0;
+ DWORD tempCID = 0;
+ BBButton * bbdi = (BBButton *)lParam;
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ realbutton = 1;
+ tempCID = cbd->dwButtonCID;
+ }
+ }
+ if (!realbutton)
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ realbutton = 1;
+ tempCID = cbd->dwButtonCID;
+ }
+ }
+
+ if (!realbutton || !wParam) return 1;
+
+
+ hwndDlg = M->FindWindow((HANDLE)wParam);
+ if (hwndDlg && realbutton && bbdi->hIcon)
+ SendMessage(GetDlgItem(hwndDlg, tempCID), BM_SETIMAGE, IMAGE_ICON, (LPARAM)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)bbdi->hIcon));
+ if (hwndDlg && realbutton && bbdi->pszTooltip) {
+ if (bbdi->bbbFlags&BBBF_ANSITOOLTIP)
+ SendMessage(GetDlgItem(hwndDlg, tempCID), BUTTONADDTOOLTIP, (WPARAM)mir_a2u(bbdi->pszTooltip), 0);
+ else
+ SendMessage(GetDlgItem(hwndDlg, tempCID), BUTTONADDTOOLTIP, (WPARAM)bbdi->ptszTooltip, 0);
+ }
+ if (hwndDlg && realbutton && 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 wParam, LPARAM lParam)
+{
+ int i;
+ DWORD tempCID = 0;
+ DWORD dwFlags = 0;
+ BBButton * bbdi = (BBButton *)lParam;
+
+ EnterCriticalSection(&ToolBarCS);
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ tempCID = cbd->dwButtonCID;
+ dwFlags = cbd->bLSided ? BBBF_ISLSIDEBUTTON : BBBF_ISRSIDEBUTTON;
+ li.List_Remove(LButtonsList, i);
+ i--;
+ }
+ }
+ if (tempCID) qsort(LButtonsList->items, LButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+
+ if (!tempCID) {
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ tempCID = cbd->dwButtonCID;
+ dwFlags = cbd->bLSided ? BBBF_ISLSIDEBUTTON : BBBF_ISRSIDEBUTTON;
+ li.List_Remove(RButtonsList, i);
+ i--;
+ }
+ }
+ if (tempCID) qsort(RButtonsList->items, RButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+ }
+
+ LeaveCriticalSection(&ToolBarCS);
+ if (tempCID)
+ M->BroadcastMessage(DM_CBDESTROY, (WPARAM)tempCID, (LPARAM)dwFlags);
+ return 0;
+}
+
+static INT_PTR CB_ModifyButton(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ BOOL bFound = 0;
+ CustomButtonData* cbd = NULL;
+ BBButton * bbdi = (BBButton *)lParam;
+
+ EnterCriticalSection(&ToolBarCS);
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ cbd = (CustomButtonData *)LButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ bFound = 1;
+ break;
+ }
+ }
+
+ if (!bFound) {
+ cbd = NULL;
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ cbd = (CustomButtonData *)RButtonsList->items[i];
+ if (!strcmp(cbd->pszModuleName, bbdi->pszModuleName) && (cbd->dwButtonOrigID == bbdi->dwButtonID)) {
+ bFound = 1;
+ break;
+ }
+ }
+ }
+
+ if (bFound) {
+ if (bbdi->pszTooltip) {
+ if (cbd->ptszTooltip) 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) ? 1 : 0;
+ cbd->bLSided = (bbdi->bbbFlags & BBBF_ISLSIDEBUTTON) ? 1 : 0;
+ cbd->bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) ? 1 : 0;
+ cbd->bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) ? 1 : 0;
+ cbd->bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) ? 1 : 0;
+ cbd->bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) ? 1 : 0;
+ cbd->bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) ? 1 : 0;
+ }
+ }
+ LeaveCriticalSection(&ToolBarCS);
+ if (bFound)
+ M->BroadcastMessage(DM_BBNEEDUPDATE, 0, (LPARAM)cbd);
+ return 0;
+}
+
+void BB_UpdateIcons(HWND hdlg, struct TWindowData *dat)
+{
+ int i;
+ HWND hwndBtn = NULL;
+
+ qsort(LButtonsList->items, LButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ if (cbd) {
+ if (!cbd->bDummy)
+ hwndBtn = GetDlgItem(hdlg, cbd->dwButtonCID);
+
+ if (hwndBtn && cbd->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)cbd->hIcon));
+ }
+ }
+
+ hwndBtn = NULL;
+ qsort(RButtonsList->items, RButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ if (cbd) {
+ if (!cbd->bDummy)
+ hwndBtn = GetDlgItem(hdlg, cbd->dwButtonCID);
+
+ if (hwndBtn && cbd->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)cbd->hIcon));
+ }
+
+ }
+}
+
+void TSAPI BB_InitDlgButtons(TWindowData *dat)
+{
+ RECT rect;
+ int i;
+ int lwidth = 0, rwidth = 0;
+ HWND hwndBtn = NULL;
+ RECT rcSplitter;
+ POINT ptSplitter;
+ int splitterY;
+ BYTE gap = DPISCALEX_S(PluginConfig.g_iButtonsBarGap);
+ BOOL isThemed = PluginConfig.m_bIsXP;
+ int cx = 0, cy = 0;
+ int lcount = LButtonsList->realCount;
+ int rcount = RButtonsList->realCount;
+ 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);
+
+ hwndBtn = NULL;
+ qsort(RButtonsList->items, RButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[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->bDummy && !GetDlgItem(hdlg, cbd->dwButtonCID))
+ hwndBtn = CreateWindowEx(0, _T("TSButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, rect.right - rwidth + gap, splitterY, cbd->iButtonWidth, DPISCALEY_S(22), hdlg, (HMENU) cbd->dwButtonCID, g_hInst, NULL);
+ if (!cbd->bDummy && hwndBtn) {
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN + 10, 0, isThemed ? 1 : 0);
+ if (cbd->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)cbd->hIcon));
+ if (cbd->ptszTooltip)
+ SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), 0);
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN + 12, 0, (LPARAM)dat->pContainer);
+ SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, 0, 1);
+
+ if (hwndBtn && cbd->dwArrowCID)
+ SendMessage(hwndBtn, BUTTONSETARROW, cbd->dwArrowCID, 0);
+ if (hwndBtn && cbd->bPushButton)
+ SendMessage(hwndBtn, BUTTONSETASPUSHBTN, 0, 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;
+ qsort(LButtonsList->items, LButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[i];
+ if (((dat->bType == SESSIONTYPE_IM && cbd->bIMButton)
+ || (dat->bType == SESSIONTYPE_CHAT && cbd->bChatButton))) {
+ if (!cbd->bDummy && !GetDlgItem(hdlg, cbd->dwButtonCID))
+ hwndBtn = CreateWindowEx(0, _T("TSButtonClass"), _T(""), WS_CHILD | WS_VISIBLE | WS_TABSTOP, 2 + lwidth, splitterY,
+ cbd->iButtonWidth, DPISCALEY_S(22), hdlg, (HMENU) cbd->dwButtonCID, g_hInst, NULL);
+ if (!cbd->bHidden)
+ lwidth += cbd->iButtonWidth + gap;
+ if (!cbd->bHidden && !cbd->bCanBeHidden)
+ dat->iButtonBarReallyNeeds += cbd->iButtonWidth + gap;
+ if (!cbd->bDummy && hwndBtn) {
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN + 10, 0, isThemed ? 1 : 0);
+ if (cbd->hIcon)
+ SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)cbd->hIcon));
+ if (cbd->ptszTooltip)
+ SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)TranslateTS(cbd->ptszTooltip), 0);
+ SendMessage(hwndBtn, BUTTONSETASFLATBTN + 12, 0, (LPARAM)dat->pContainer);
+ SendMessage(hwndBtn, BUTTONSETASTOOLBARBUTTON, 0, 1);
+
+ if (hwndBtn && cbd->dwArrowCID)
+ SendMessage(hwndBtn, BUTTONSETARROW, cbd->dwArrowCID, 0);
+ if (hwndBtn && cbd->bPushButton)
+ SendMessage(hwndBtn, BUTTONSETASPUSHBTN, 0, 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)
+{
+ int i;
+ CustomButtonData* cbd;
+ HWND hwnd;
+
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ cbd = reinterpret_cast<CustomButtonData *>(LButtonsList->items[i]);
+ if(cbd) {
+ hwnd = GetDlgItem(dat->hwnd, cbd->dwButtonCID);
+ if(hwnd)
+ InvalidateRect(hwnd, 0, TRUE);
+ }
+ }
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ cbd = reinterpret_cast<CustomButtonData *>(RButtonsList->items[i]);
+ if(cbd) {
+ 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)
+{
+ RECT rect;
+ int i;
+ HWND hwndBtn = 0;
+ int lwidth = 0, rwidth = 0;
+ RECT rcSplitter;
+ POINT ptSplitter;
+ int splitterY, iOff;
+ BYTE gap = DPISCALEX_S(PluginConfig.g_iButtonsBarGap);
+ int foravatar = 0;
+ BOOL showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1;
+ BOOL bBottomToolbar = dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR ? 1 : 0;
+ int tempL = dat->bbLSideWidth;
+ int tempR = dat->bbRSideWidth;
+ HWND hwnd = dat->hwnd;
+ HDWP hdwp = BeginDeferWindowPos(LButtonsList->realCount + RButtonsList->realCount + 1);
+ HWND hwndToggleSideBar = GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_TOGGLESIDEBAR : IDC_CHAT_TOGGLESIDEBAR);
+
+ if (!dat || !IsWindowVisible(hwnd))
+ return 0;
+
+ ShowWindow(hwndToggleSideBar, (showToolbar && dat->pContainer->SideBar->isActive()) ? SW_SHOW : SW_HIDE);
+
+ EnterCriticalSection(&ToolBarCS);
+
+ GetWindowRect(GetDlgItem(hwnd, (dat->bType == SESSIONTYPE_IM) ? IDC_SPLITTER : IDC_SPLITTERY), &rcSplitter);
+ ptSplitter.x = 0;
+ ptSplitter.y = rcSplitter.top;
+ ScreenToClient(hwnd, &ptSplitter);
+ if (PluginConfig.g_DPIscaleY > 1.0)
+ iOff = dat->bType == SESSIONTYPE_IM ? DPISCALEY_S(22) : DPISCALEY_S(23);
+ else
+ iOff = DPISCALEY_S(22);
+
+ GetClientRect(hwnd, &rect);
+
+ if (!bBottomToolbar) splitterY = ptSplitter.y - DPISCALEY_S(1);
+ else splitterY = rect.bottom;
+
+ if ((rect.bottom - ptSplitter.y - (rcSplitter.bottom - rcSplitter.top) /*- DPISCALEY(2)*/ - (bBottomToolbar ? DPISCALEY_S(24) : 0) < dat->pic.cy - DPISCALEY_S(2)) && dat->showPic && !PluginConfig.m_AlwaysFullToolbarWidth)
+ foravatar = dat->pic.cx + gap;
+
+ if (bNeedResort)
+ qsort(LButtonsList->items, LButtonsList->realCount, sizeof(BBButton *), sstSortButtons);
+
+ 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);// | SWP_NOCOPYBITS);
+ lwidth += 10;
+ tempL -= 10;
+ }
+
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[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);// | SWP_NOCOPYBITS);
+ if (IsWindowVisible(hwndBtn) || (cbd->bDummy && !(cbd->bAutoHidden || cbd->bHidden)))
+ lwidth += cbd->iButtonWidth + gap;
+ if (!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden)
+ cbd->bAutoHidden = 1;
+ continue;
+ } else if (!cbd->bCanBeHidden && !cbd->bHidden && !(!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden)) {
+ ShowWindow(hwndBtn, SW_SHOW);
+ cbd->bAutoHidden = 0;
+ }
+
+ if (!cbd->bDummy && !IsWindowVisible(hwndBtn) && !IsWindowEnabled(hwndBtn) && !cbd->bAutoHidden)
+ tempL -= cbd->iButtonWidth + gap;
+
+ if (cbd->bCanBeHidden && !cbd->bHidden && (cbd->bDummy || !((!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->bDummy && !(cbd->bAutoHidden || cbd->bHidden)))
+ lwidth += cbd->iButtonWidth + gap;
+ }
+ }
+
+ if (bNeedResort) {
+ qsort(RButtonsList->items, RButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+ bNeedResort = FALSE;
+ }
+
+ 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);// | SWP_NOCOPYBITS);
+ rwidth += 12;
+ tempR -= 12;
+ }
+
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[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->bDummy && !(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);// SWP_NOCOPYBITS);
+ if (!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn) && !cbd->bAutoHidden)
+ cbd->bAutoHidden = 1;
+ continue;
+ } else if (!cbd->bCanBeHidden && !cbd->bHidden && !((!IsWindowEnabled(hwndBtn) && !IsWindowVisible(hwndBtn)) && !cbd->bAutoHidden)) {
+ ShowWindow(hwndBtn, SW_SHOW);
+ cbd->bAutoHidden = 0;
+ }
+
+ if (!cbd->bDummy && !IsWindowVisible(hwndBtn) && !IsWindowEnabled(hwndBtn) && !cbd->bAutoHidden)
+ tempR -= cbd->iButtonWidth + gap;
+
+ if (cbd->bCanBeHidden && !cbd->bHidden && (cbd->bDummy || !((!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->bDummy && !(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);// SWP_NOCOPYBITS);
+ }
+ }
+
+ LeaveCriticalSection(&ToolBarCS);
+
+ return (EndDeferWindowPos(hdwp));
+}
+
+void TSAPI BB_CustomButtonClick(struct 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->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[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->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[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, (WPARAM)dat->hContact, (LPARAM)&cbcd);
+}
+
+
+void CB_GetButtonSettings(HANDLE 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 (!DBGetContactSettingString(hContact, "TabSRMM_Toolbar", SettingName, &dbv)) {
+ token = strtok(dbv.pszVal, "_");
+ cbd->dwPosition = (DWORD)atoi(token);
+ token = strtok(NULL, "_");
+ cbd->bIMButton = (BOOL)atoi(token);
+ token = strtok(NULL, "_");
+ cbd->bChatButton = (BOOL)atoi(token);
+ token = strtok(NULL, "_");
+ cbd->bLSided = (BOOL)atoi(token);
+ token = strtok(NULL, "_");
+ cbd->bRSided = (BOOL)atoi(token);
+ token = strtok(NULL, "_");
+ cbd->bCanBeHidden = (BOOL)atoi(token);
+
+ DBFreeVariant(&dbv);
+ }
+}
+
+void CB_WriteButtonSettings(HANDLE 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))
+ DBWriteContactSettingString(hContact, "TabSRMM_Toolbar", SettingName, SettingParameter);
+ else
+ DBDeleteContactSetting(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)
+{
+ int i;
+
+ for (i = 0; i < RButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)RButtonsList->items[i];
+ SendMessage(GetDlgItem(dat->hwnd, cbd->dwButtonCID), WM_THEMECHANGED, 0, 0);
+ }
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData* cbd = (CustomButtonData *)LButtonsList->items[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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("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 = _T("Channel manager");
+
+ CB_AddButton(0, (LPARAM)&bbd);
+
+
+ BB_RegisterSeparators();
+}
+
+#define MIDDLE_SEPARATOR _T(">-------M-------<")
+
+static int SaveTree(HWND hToolBarTree)
+{
+ TVITEM tvi;
+ HTREEITEM hItem = NULL;
+ BOOL RSide = FALSE;
+ int count = 10;
+ DWORD loc_sepcout = 0;
+ TCHAR strbuf[128];
+ CustomButtonData* cbd = NULL;
+
+ tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE;
+ tvi.hItem = TreeView_GetRoot(hToolBarTree);
+ tvi.pszText = strbuf;
+ tvi.cchTextMax = sizeof(strbuf);
+ EnterCriticalSection(&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;
+ }
+ cbd = (CustomButtonData*)tvi.lParam;
+ if (cbd) {
+ if (cbd->opFlags) {
+ cbd->bIMButton = (cbd->opFlags & BBSF_IMBUTTON) ? 1 : 0;
+ cbd->bChatButton = (cbd->opFlags & BBSF_CHATBUTTON) ? 1 : 0;
+ cbd->bCanBeHidden = (cbd->opFlags & BBSF_CANBEHIDDEN) ? 1 : 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->bDummy && !strcmp(cbd->pszModuleName, "Tabsrmm_sep"))
+ cbd->opFlags = BBSF_NTBDESTRUCT;
+ } else {
+ if (!cbd->bIMButton && !cbd->bChatButton)
+ cbd->bIMButton = TRUE;
+ if (cbd->bDummy && !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);
+ }
+ hItem = TreeView_GetNextSibling(hToolBarTree, tvi.hItem);
+ if (cbd->opFlags&BBSF_NTBDESTRUCT)
+ TreeView_DeleteItem(hToolBarTree, tvi.hItem);
+ tvi.hItem = hItem;
+ }
+ LeaveCriticalSection(&ToolBarCS);
+ M->WriteDword("TabSRMM_Toolbar", "SeparatorsCount", loc_sepcout);
+ dwSepCount = loc_sepcout;
+ bNeedResort = TRUE;
+ return 1;
+}
+
+HIMAGELIST himgl = NULL;
+
+static int BuildMenuObjectsTree(HWND hToolBarTree)
+{
+ TVINSERTSTRUCT tvis;
+ HTREEITEM hti;
+ int iImage = 0;
+ int i;
+ 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, reinterpret_cast<HICON>(CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_24")));
+ HIMAGELIST himl = TreeView_GetImageList(hToolBarTree, TVSIL_NORMAL);
+ ImageList_Destroy(himl);
+ TreeView_SetImageList(hToolBarTree, himgl, TVSIL_NORMAL);
+
+
+ if ((RButtonsList->realCount + LButtonsList->realCount) == 0)
+ return FALSE;
+ EnterCriticalSection(&ToolBarCS);
+ qsort(LButtonsList->items, LButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+
+ for (i = 0; i < LButtonsList->realCount; i++) {
+ CustomButtonData * cbd = (CustomButtonData *)LButtonsList->items[i];
+ tvis.item.lParam = (LPARAM)cbd;
+
+ if (cbd->bDummy) {
+ tvis.item.pszText = TranslateTS(_T("<Separator>"));
+ tvis.item.iImage = tvis.item.iSelectedImage = 0;
+ } else {
+ tvis.item.pszText = TranslateTS(cbd->ptszTooltip);
+ iImage = ImageList_AddIcon(himgl, (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)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));
+ }
+ if (TRUE) {
+ 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);
+ }
+
+ qsort(RButtonsList->items, RButtonsList->realCount, sizeof(CustomButtonData *), sstSortButtons);
+
+ for (i = RButtonsList->realCount; i > 0; i--) {
+ CustomButtonData * cbd = (CustomButtonData *)RButtonsList->items[i-1];
+ tvis.item.lParam = (LPARAM)cbd;
+
+ if (cbd->bDummy) {
+ tvis.item.pszText = TranslateTS(_T("<Separator>"));
+ tvis.item.iImage = tvis.item.iSelectedImage = -1;
+ } else {
+ tvis.item.pszText = TranslateTS(cbd->ptszTooltip);
+ iImage = ImageList_AddIcon(himgl, (HICON)CallService(MS_SKIN2_GETICONBYHANDLE, 0, (LPARAM)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));
+ }
+ LeaveCriticalSection(&ToolBarCS);
+ 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)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG: {
+ LONG_PTR style;
+ hToolBarTree = GetDlgItem(hwndDlg, IDC_TOOLBARTREE);
+
+ style = GetWindowLongPtr(hToolBarTree, GWL_STYLE);
+ style ^= TVS_CHECKBOXES;
+ SetWindowLongPtr(hToolBarTree, GWL_STYLE, style);
+ style |= TVS_CHECKBOXES;
+ style |= TVS_NOHSCROLL;
+ SetWindowLongPtr(hToolBarTree, GWL_STYLE, style);
+
+ EnterCriticalSection(&ToolBarCS);
+
+ BuildMenuObjectsTree((HWND)hToolBarTree);
+
+ LeaveCriticalSection(&ToolBarCS);
+
+ 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)
+ break;
+
+ TreeView_SetInsertMark(hToolBarTree, NULL, 0);
+ drag = 0;
+ ReleaseCapture();
+ {
+ TVHITTESTINFO hti;
+ TVITEM tvi;
+ 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;
+ 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: {
+ TVHITTESTINFO hti;
+ if (!drag) break;
+
+ 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: {
+ Hlp_RemoveDatabaseSettings(NULL, "TabSRMM_Toolbar", 0);
+ CB_HardReInit();
+ BuildMenuObjectsTree(hToolBarTree);
+ }
+ break;
+
+ case IDC_SEPARATOR: {
+ int i = 0;
+ HTREEITEM hti;
+ TVINSERTSTRUCT tvis;
+ CustomButtonData* cbd;
+
+ hti = TreeView_GetSelection(hToolBarTree);
+ if (!hti)
+ hti = TVI_FIRST;
+ cbd = (CustomButtonData *)mir_alloc(sizeof(CustomButtonData));
+ ZeroMemory(cbd, sizeof(CustomButtonData));
+
+ cbd->bDummy = 1;
+ cbd->bHidden = 1;
+ cbd->bIMButton = 1;
+ cbd->bLSided = 1;
+ cbd->dwButtonOrigID = ++dwSepCount;
+ cbd->pszModuleName = "Tabsrmm_sep";
+ cbd->iButtonWidth = 22;
+ cbd->opFlags = BBSF_NTBDESTRUCT;
+ li.List_InsertPtr(LButtonsList, cbd);
+
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = hti;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+
+ tvis.item.pszText = TranslateTS(_T("<Separator>"));
+ 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;
+ }
+ break;
+
+ case WM_NOTIFY: {
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY: {
+ TVITEM tvi;
+ HTREEITEM hti;
+ CustomButtonData* cbd;
+
+ hti = TreeView_GetSelection(hToolBarTree);
+
+ if (hti) {
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hti;
+ TreeView_GetItem(hToolBarTree, &tvi);
+
+ if (tvi.lParam) {
+ cbd = (CustomButtonData*)tvi.lParam;
+ if (cbd) {
+ cbd->bIMButton = IsDlgButtonChecked(hwndDlg, IDC_IMCHECK);
+ cbd->bChatButton = IsDlgButtonChecked(hwndDlg, IDC_CHATCHECK);
+ cbd->bCanBeHidden = IsDlgButtonChecked(hwndDlg, IDC_CANBEHIDDEN);
+ }
+ }
+ }
+
+ 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);
+
+ M->WriteByte(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;
+ }
+
+ case PSN_RESET: {
+ CB_ReInitCustomButtons();
+ dwSepCount = M->GetDword("TabSRMM_Toolbar", "SeparatorsCount", 0);
+ 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:
+ {
+ TVITEM tvi;
+ HTREEITEM hti;
+ TCHAR strbuf[128];
+ CustomButtonData* cbd;
+
+ hti = TreeView_GetSelection(hToolBarTree);
+
+ if (hti == NULL)
+ break;
+
+ 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;
+
+ 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: {
+ TVITEM tvi;
+ HTREEITEM hti;
+ TCHAR strbuf[128];
+ CustomButtonData* cbd;
+
+ hti = TreeView_GetSelection(hToolBarTree);
+
+ if (hti == NULL)
+ break;
+
+ 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;
+
+ 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;
+
+ }
+ break;
+ }
+ }
+ 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;
+ }
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/plugins/TabSRMM/src/commonheaders.h b/plugins/TabSRMM/src/commonheaders.h new file mode 100644 index 0000000000..7b40ad1117 --- /dev/null +++ b/plugins/TabSRMM/src/commonheaders.h @@ -0,0 +1,291 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: commonheaders.h 13596 2011-04-15 19:07:23Z george.hazan $
+ *
+ * global include file, used to build the precompiled header.
+ *
+ */
+
+
+#ifndef __COMMONHEADERS_H
+#define __COMMONHEADERS_H
+
+#define _UNICODE 1
+
+//#define __LOGDEBUG_ 1 // log some stuff to %profile_dir%/tabsrmm_debug.log
+//#define __FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER 1
+
+//#define __USE_EX_HANDLERS 1
+
+// #define __DELAYED_FOR_3_1 1 // features not going into 3.0.x and will be completed later
+
+#if !defined __DELAYED_FOR_3_1
+ #define __FEAT_EXP_AUTOSPLITTER 1
+#endif
+
+#define WINVER 0x0600
+#define _WIN32_WINNT 0x0600
+#define _WIN32_IE 0x0501
+#define WIN32_LEAN_AND_MEAN
+
+#define MIRANDA_VER 0x0A00
+
+#include <m_stdhdr.h>
+
+#include <windows.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <shellapi.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+
+#define TSAPI __stdcall
+#define FASTCALL __fastcall
+
+ /*
+ * text shadow types (DrawThemeTextEx() / Vista+ uxtheme)
+ */
+ #define TST_NONE 0
+ #define TST_SINGLE 1
+ #define TST_CONTINUOUS 2
+
+ typedef struct _DWM_THUMBNAIL_PROPERTIES
+ {
+ DWORD dwFlags;
+ RECT rcDestination;
+ RECT rcSource;
+ BYTE opacity;
+ BOOL fVisible;
+ BOOL fSourceClientAreaOnly;
+ } DWM_THUMBNAIL_PROPERTIES, *PDWM_THUMBNAIL_PROPERTIES;
+
+ enum DWMWINDOWATTRIBUTE
+ {
+ DWMWA_NCRENDERING_ENABLED = 1, // [get] Is non-client rendering enabled/disabled
+ DWMWA_NCRENDERING_POLICY, // [set] Non-client rendering policy
+ DWMWA_TRANSITIONS_FORCEDISABLED, // [set] Potentially enable/forcibly disable transitions
+ DWMWA_ALLOW_NCPAINT, // [set] Allow contents rendered in the non-client area to be visible on the DWM-drawn frame.
+ DWMWA_CAPTION_BUTTON_BOUNDS, // [get] Bounds of the caption button area in window-relative space.
+ DWMWA_NONCLIENT_RTL_LAYOUT, // [set] Is non-client content RTL mirrored
+ DWMWA_FORCE_ICONIC_REPRESENTATION, // [set] Force this window to display iconic thumbnails.
+ DWMWA_FLIP3D_POLICY, // [set] Designates how Flip3D will treat the window.
+ DWMWA_EXTENDED_FRAME_BOUNDS, // [get] Gets the extended frame bounds rectangle in screen space
+ DWMWA_HAS_ICONIC_BITMAP, // [set] Indicates an available bitmap when there is no better thumbnail representation.
+ DWMWA_DISALLOW_PEEK, // [set] Don't invoke Peek on the window.
+ DWMWA_EXCLUDED_FROM_PEEK, // [set] LivePreview exclusion information
+ DWMWA_LAST
+ };
+
+ #define DWM_TNP_RECTDESTINATION 0x00000001
+ #define DWM_TNP_RECTSOURCE 0x00000002
+ #define DWM_TNP_OPACITY 0x00000004
+ #define DWM_TNP_VISIBLE 0x00000008
+ #define DWM_TNP_SOURCECLIENTAREAONLY 0x00000010
+
+ #define DWM_SIT_DISPLAYFRAME 0x00000001 // Display a window frame around the provided bitmap
+
+ typedef HANDLE HTHUMBNAIL;
+ typedef HTHUMBNAIL* PHTHUMBNAIL;
+
+#ifndef BPPF_ERASE
+ typedef enum _BP_BUFFERFORMAT
+ {
+ BPBF_COMPATIBLEBITMAP, // Compatible bitmap
+ BPBF_DIB, // Device-independent bitmap
+ BPBF_TOPDOWNDIB, // Top-down device-independent bitmap
+ BPBF_TOPDOWNMONODIB // Top-down monochrome device-independent bitmap
+ } BP_BUFFERFORMAT;
+
+
+ typedef struct _BP_PAINTPARAMS
+ {
+ DWORD cbSize;
+ DWORD dwFlags; // BPPF_ flags
+ const RECT * prcExclude;
+ const BLENDFUNCTION * pBlendFunction;
+ } BP_PAINTPARAMS, *PBP_PAINTPARAMS;
+
+ #define BPPF_ERASE 1
+ #define BPPF_NOCLIP 2
+ #define BPPF_NONCLIENT 4
+#endif
+
+ typedef struct _DWM_BLURBEHIND
+ {
+ DWORD dwFlags;
+ BOOL fEnable;
+ HRGN hRgnBlur;
+ BOOL fTransitionOnMaximized;
+ } DWM_BLURBEHIND, *PDWM_BLURBEHIND;
+
+ #define DWM_BB_ENABLE 1
+
+#ifndef LOCALE_SISO3166CTRYNAME2
+ #define LOCALE_SISO3166CTRYNAME2 0x00000068 // 3 character ISO country name, eg "USA Vista+
+ #define LOCALE_SISO639LANGNAME2 0x00000067 // 3 character ISO abbreviated language name, eg "eng"
+#endif
+
+#ifndef WM_DWMCOMPOSITIONCHANGED
+ #define WM_DWMCOMPOSITIONCHANGED 0x031E
+ #define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
+#endif
+
+#ifndef WM_DWMSENDICONICTHUMBNAIL
+ #define WM_DWMSENDICONICTHUMBNAIL 0x0323
+ #define WM_DWMSENDICONICLIVEPREVIEWBITMAP 0x0326
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <stddef.h>
+#include <process.h>
+#include <shlwapi.h>
+#include <shlobj.h>
+#include <richedit.h>
+#include <limits.h>
+#include <ctype.h>
+#include <string>
+#include <vector>
+#include <assert.h>
+
+#include "../include/resource.h"
+
+/* State of icon with such flag will not be saved, and you must set it manually */
+#define MBF_OWNERSTATE 0x04
+
+#include <win2k.h>
+#include <newpluginapi.h>
+#include <m_imgsrvc.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_button.h>
+#include <m_clist.h>
+#include <m_options.h>
+#include <m_protosvc.h>
+#include <m_utils.h>
+#include <m_skin.h>
+#include <m_contacts.h>
+#include <m_icolib.h>
+#include <m_clc.h>
+#include <m_clui.h>
+#include <m_userinfo.h>
+#include <m_history.h>
+#include <m_addcontact.h>
+#include <m_file.h>
+#include <m_fontservice.h>
+#include <m_acc.h>
+#include <m_chat.h>
+#include <m_protomod.h>
+#include <m_hotkeys.h>
+#include <m_genmenu.h>
+#include <m_popup.h>
+#include <m_timezones.h>
+
+extern struct LIST_INTERFACE li;
+
+#define safe_sizeof(a) (unsigned int)((sizeof((a)) / sizeof((a)[0])))
+
+#include "../include/version.h"
+#include "m_ieview.h"
+#include "m_popup2.h"
+#include "m_metacontacts.h"
+#include "m_fingerprint.h"
+#include "m_nudge.h"
+#include "m_folders.h"
+#include "m_msg_buttonsbar.h"
+#include "m_cln_skinedit.h"
+#include "m_flash.h"
+#include "m_spellchecker.h"
+#include "m_mathmodule.h"
+#include "m_historyevents.h"
+#include "m_buttonbar.h"
+#include "m_updater.h"
+#include "m_smileyadd.h"
+
+#include "../include/msgs.h"
+#include "../include/msgdlgutils.h"
+#include "../include/typingnotify.h"
+#include "../include/generic_msghandlers.h"
+#include "../include/nen.h"
+extern NEN_OPTIONS nen_options;
+#include "../include/functions.h"
+#include "../chat/chat.h"
+
+#include "../include/contactcache.h"
+#include "../include/translator.h"
+#include "../include/themes.h"
+#include "../include/globals.h"
+#include "../include/mim.h"
+#include "../include/sendqueue.h"
+#include "../include/taskbar.h"
+#include "../include/controls.h"
+#include "../include/infopanel.h"
+#include "../include/sidebar.h"
+#include "../include/utils.h"
+#include "../include/sendlater.h"
+
+#include "../chat/muchighlight.h"
+
+#if !defined(_WIN64) && !defined(_USE_32BIT_TIME_T)
+ #define _USE_32BIT_TIME_T
+#else
+ #undef _USE_32BIT_TIME_T
+#endif
+
+#if defined(__GNUWIN32__)
+ #define wEffects wReserved
+#endif
+
+#if defined(__GNUG__)
+#define __except(x) if(x)
+#define __try
+#define __finally
+
+EXCEPTION_POINTERS* GetExceptionInformation()
+{
+ EXCEPTION_POINTERS e;
+ return(&e);
+}
+#endif
+
+/*
+ * tchar-like std::string
+ */
+
+typedef std::basic_string<TCHAR> tstring;
+
+extern HINSTANCE g_hInst;
+extern CSkinItem SkinItems[];
+extern TContainerData *pFirstContainer, *pLastActiveContainer;
+extern HANDLE hTypingNotify;
+
+#define IS_EXTKEY(a) (a & (1 << 24))
+
+#endif /* __COMMONHEADERS_H */
diff --git a/plugins/TabSRMM/src/contactcache.cpp b/plugins/TabSRMM/src/contactcache.cpp new file mode 100644 index 0000000000..b30290d447 --- /dev/null +++ b/plugins/TabSRMM/src/contactcache.cpp @@ -0,0 +1,655 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This 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
+ *
+ * $Id: contactcache.cpp 13336 2011-01-27 20:02:17Z george.hazan $
+ *
+ * contact cache implementation
+ *
+ * the contact cache provides various services to the message window(s)
+ * it also abstracts meta contacts.
+ *
+ */
+
+#include "commonheaders.h"
+
+CContactCache* CContactCache::m_cCache = 0;
+
+CContactCache::CContactCache(const HANDLE hContact)
+{
+ ZeroMemory(this, sizeof(CContactCache));
+
+ m_Valid = m_isMeta = false;
+ m_hContact = hContact;
+ m_wOldStatus = m_wStatus = m_wMetaStatus = ID_STATUS_OFFLINE;
+
+ m_szStatusMsg = m_ListeningInfo = m_xStatusMsg = 0;
+ m_nMax = 0;
+
+ if(hContact) {
+ m_szProto = reinterpret_cast<char *>(::CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)m_hContact, 0));
+ if(m_szProto)
+ m_tszProto = mir_a2t(m_szProto);
+ initPhaseTwo();
+ }
+ else {
+ m_szProto = C_INVALID_PROTO;
+ m_tszProto = C_INVALID_PROTO_T;
+ 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()
+{
+ PROTOACCOUNT* acc = 0;
+
+ m_szAccount = 0;
+ if(m_szProto) {
+ acc = reinterpret_cast<PROTOACCOUNT *>(::CallService(MS_PROTO_GETACCOUNT, (WPARAM)0, (LPARAM)m_szProto));
+ if(acc && acc->tszAccountName)
+ m_szAccount = acc->tszAccountName;
+ }
+
+ m_Valid = (m_szProto != 0 && m_szAccount != 0) ? true : false;
+ if(m_Valid) {
+ m_isMeta = (PluginConfig.bMetaEnabled && !strcmp(m_szProto, PluginConfig.szMetaName)) ? true : false;
+ m_isSubcontact = (M->GetByte(m_hContact, PluginConfig.szMetaName, "IsSubcontact", 0) ? true : false);
+ if(m_isMeta)
+ updateMeta(true);
+ updateState();
+ updateFavorite();
+ }
+ else {
+ m_szProto = C_INVALID_PROTO;
+ m_tszProto = C_INVALID_PROTO_T;
+ 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_hSubContact = 0;
+ m_tszMetaProto[0] = 0;
+ 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 = reinterpret_cast<TCHAR *>(::CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)m_hContact, GCDNF_TCHAR));
+ if(tszNick)
+ fChanged = (_tcscmp(m_szNick, tszNick) ? true : false);
+ mir_sntprintf(m_szNick, 80, _T("%s"), tszNick ? tszNick : _T("<undef>"));
+ }
+ return(fChanged);
+}
+
+/**
+ * update status mode
+ * @return bool: true if status mode has changed, false if not.
+ */
+bool CContactCache::updateStatus()
+{
+ if(m_Valid) {
+ m_wOldStatus = m_wStatus;
+ m_wStatus = (WORD)DBGetContactSettingWord(m_hContact, m_szProto, "Status", ID_STATUS_OFFLINE);
+
+ return(m_wOldStatus != m_wStatus);
+ }
+ else
+ return(false);
+}
+
+/**
+ * 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(bool fForce)
+{
+ if(m_Valid) {
+ HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)m_hContact, 0);
+ if (hSubContact && (hSubContact != m_hSubContact || fForce)) {
+ m_hSubContact = hSubContact;
+ m_szMetaProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)m_hSubContact, 0);
+ if (m_szMetaProto) {
+ PROTOACCOUNT *acc = reinterpret_cast<PROTOACCOUNT *>(::CallService(MS_PROTO_GETACCOUNT, (WPARAM)0, (LPARAM)m_szMetaProto));
+ if(acc && acc->tszAccountName)
+ m_szAccount = acc->tszAccountName;
+ m_wMetaStatus = DBGetContactSettingWord(m_hSubContact, m_szMetaProto, "Status", ID_STATUS_OFFLINE);
+ MultiByteToWideChar(CP_ACP, 0, m_szMetaProto, -1, m_tszMetaProto, 40);
+ m_tszMetaProto[39] = 0;
+ }
+ else {
+ m_wMetaStatus = ID_STATUS_OFFLINE;
+ m_tszMetaProto[0] = 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()
+{
+ bool fChanged = false;
+
+ if(m_Valid) {
+ CONTACTINFO ci = {0};
+
+ ci.hContact = getActiveContact();
+ ci.szProto = const_cast<char *>(getActiveProto());
+ ci.cbSize = sizeof(ci);
+
+ ci.dwFlag = CNF_DISPLAYUID | CNF_TCHAR;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) {
+ switch (ci.type) {
+ case CNFT_ASCIIZ:
+ mir_sntprintf(m_szUIN, safe_sizeof(m_szUIN), _T("%s"), reinterpret_cast<TCHAR *>(ci.pszVal));
+ mir_free((void *)ci.pszVal);
+ break;
+ case CNFT_DWORD:
+ mir_sntprintf(m_szUIN, safe_sizeof(m_szUIN), _T("%u"), ci.dVal);
+ break;
+ default:
+ m_szUIN[0] = 0;
+ break;
+ }
+ } else
+ m_szUIN[0] = 0;
+
+ }
+ else
+ m_szUIN[0] = 0;
+
+ return(fChanged);
+}
+
+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) {
+ m_stats->iReceived++;
+ m_stats->messageCount++;
+ }
+ else
+ return;
+ m_stats->iReceivedBytes += m_stats->lastReceivedChars;
+ m_stats->lastReceivedChars = 0;
+ break;
+ case TSessionStats::INIT_TIMER:
+ m_stats->started = time(0);
+ return;
+ case TSessionStats::SET_LAST_RCV:
+ m_stats->lastReceivedChars = (unsigned int)value;
+ return;
+ 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));
+
+ 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 *)malloc(iLength);
+ m_history[m_iHistoryTop].lLen = iLength;
+ } else {
+ if (iLength > m_history[m_iHistoryTop].lLen) {
+ m_history[m_iHistoryTop].szText = (TCHAR *)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)
+ 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((WPARAM)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, (WPARAM) - 1, (LPARAM) - 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, (WPARAM) - 1, (LPARAM) - 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 *)malloc(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 *)malloc((HISTORY_INITIAL_ALLOCSIZE + 1) * sizeof(TCHAR));
+ m_history[m_iHistorySize].lLen = HISTORY_INITIAL_ALLOCSIZE;
+}
+
+/**
+ * release additional memory resources
+ */
+void CContactCache::releaseAlloced()
+{
+ int i;
+
+ if(m_stats) {
+ delete m_stats;
+ m_stats = 0;
+ }
+
+ if (m_history) {
+ for (i = 0; i <= m_iHistorySize; i++) {
+ if (m_history[i].szText != 0) {
+ free(m_history[i].szText);
+ }
+ }
+ free(m_history);
+ m_history = 0;
+ }
+ if ( lstrcmp( m_tszProto, C_INVALID_PROTO_T ))
+ mir_free(m_tszProto);
+ m_tszProto = NULL;
+ 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 = (HANDLE)-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 = M->GetByte(m_hContact, SRMSGMOD_T, "isFavorite", 0) ? true : false;
+ 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)
+{
+ DBVARIANT dbv = {0};
+ BYTE bStatusMsgValid = 0;
+ INT_PTR res = 0;
+
+ if(!m_Valid)
+ return;
+
+ if(szKey == 0 || (szKey && !strcmp("StatusMsg", szKey))) {
+ if(m_szStatusMsg)
+ mir_free(m_szStatusMsg);
+ m_szStatusMsg = 0;
+ res = M->GetTString(m_hContact, "CList", "StatusMsg", &dbv);
+ if(res == 0) {
+ m_szStatusMsg = (lstrlen(dbv.ptszVal) > 0 ? getNormalizedStatusMsg(dbv.ptszVal) : 0);
+ DBFreeVariant(&dbv);
+ }
+ }
+ if(szKey == 0 || (szKey && !strcmp("ListeningTo", szKey))) {
+ if(m_ListeningInfo)
+ mir_free(m_ListeningInfo);
+ m_ListeningInfo = 0;
+ res = M->GetTString(m_hContact, m_szProto, "ListeningTo", &dbv);
+ if(res == 0) {
+ m_ListeningInfo = (lstrlen(dbv.ptszVal) > 0 ? mir_tstrdup(dbv.ptszVal) : 0);
+ DBFreeVariant(&dbv);
+ }
+ }
+ if(szKey == 0 || (szKey && !strcmp("XStatusMsg", szKey))) {
+ if(m_xStatusMsg)
+ mir_free(m_xStatusMsg);
+ m_xStatusMsg = 0;
+ res = M->GetTString(m_hContact, m_szProto, "XStatusMsg", &dbv);
+ if(res == 0) {
+ m_xStatusMsg = (lstrlen(dbv.ptszVal) > 0 ? mir_tstrdup(dbv.ptszVal) : 0);
+ DBFreeVariant(&dbv);
+ }
+ }
+ m_xStatus = M->GetByte(m_hContact, m_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(const HANDLE hContact)
+{
+ CContactCache *c = m_cCache, *cTemp;
+
+ cTemp = c;
+
+ while(c) {
+ cTemp = c;
+ if(c->m_hContact == hContact) {
+ c->inc();
+ return(c);
+ }
+ c = c->m_next;
+ }
+ CContactCache* _c = new CContactCache(hContact);
+ if(cTemp) {
+ cTemp->m_next = _c;
+ return(_c);
+ }
+ m_cCache = _c;
+ return(_c);
+}
+
+/**
+ * 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 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
+{
+ HICON hIcon;
+
+ if(m_dat && m_hwnd) {
+ if (m_dat->dwFlags & MWF_ERRORSTATE)
+ hIcon = PluginConfig.g_iconErr;
+ else if (m_dat->mayFlashTab)
+ hIcon = m_dat->iFlashIcon;
+ else {
+ if (m_dat->si && m_dat->iFlashIcon) {
+ int sizeX, sizeY;
+
+ hIcon = m_dat->iFlashIcon;
+ Utils::getIconSize(hIcon, sizeX, sizeY);
+ iSize = sizeX;
+ } else if (m_dat->hTabIcon == m_dat->hTabStatusIcon && m_dat->hXStatusIcon)
+ hIcon = m_dat->hXStatusIcon;
+ else
+ hIcon = m_dat->hTabIcon;
+ }
+ }
+ else
+ hIcon = LoadSkinnedProtoIcon(m_szProto, m_wStatus);
+ return(hIcon);
+}
+
+int CContactCache::getMaxMessageLength()
+{
+ HANDLE hContact;
+ const char* szProto;
+
+ hContact = getActiveContact();
+ szProto = getActiveProto();
+
+ if (szProto) {
+
+ m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, reinterpret_cast<LPARAM>(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 new file mode 100644 index 0000000000..2edbca9281 --- /dev/null +++ b/plugins/TabSRMM/src/container.cpp @@ -0,0 +1,2734 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: container.cpp 13447 2011-03-14 19:55:07Z george.hazan $
+ *
+ * implements the "Container" window which acts as a toplevel window
+ * for message sessions.
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+extern SESSION_INFO* m_WndList;
+extern ButtonSet g_ButtonSet;
+
+TContainerData *pFirstContainer = 0; // the linked list of struct ContainerWindowData
+TContainerData *pLastActiveContainer = NULL;
+
+static WNDPROC OldContainerWndProc = 0;
+static bool fForceOverlayIcons = false;
+
+static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, HANDLE 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 = (WPARAM)hContact;
+ else if (item->dwFlags & BUTTON_PASSHCONTACTL)
+ *lParam = (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) {
+ MARGINS m;
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+ RECT rcWnd;
+ POINT pt;
+ LONG sbar_left = 0, sbar_right = 0;
+
+ if(dat) {
+ 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);
+ }
+
+ pt.x = rcWnd.left;
+ pt.y = rcWnd.top;
+ ScreenToClient(pContainer->hwnd, &pt);
+ m.cyTopHeight = pt.y;
+ pContainer->MenuBar->setAero(true);
+
+ /*
+ * bottom part
+ */
+
+ GetWindowRect(dat->hwnd, &rcWnd);
+ pt.x = rcWnd.left;
+ if(!pContainer->SideBar->isActive())
+ pt.y = rcWnd.bottom + ((pContainer->iChilds > 1 || !(pContainer->dwFlags & CNT_HIDETABS)) ? pContainer->tBorder : 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);
+ }
+ }
+ }
+ else
+ pContainer->MenuBar->setAero(false);
+}
+
+/*
+ * CreateContainer MUST malloc() a struct 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 free()'ing the
+ * pointer and for removing the struct from the linked list.
+ */
+
+struct TContainerData* TSAPI CreateContainer(const TCHAR *name, int iTemp, HANDLE hContactFrom) {
+ DBVARIANT dbv;
+ char szCounter[10];
+ char *szKey = "TAB_ContainersW";
+ int i, iFirstFree = -1, iFound = FALSE;
+
+ struct TContainerData *pContainer = (struct TContainerData *)malloc(sizeof(struct TContainerData));
+ if (pContainer) {
+ ZeroMemory((void *)pContainer, sizeof(struct 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
+ */
+ i = 0;
+ if (!M->GetByte("singlewinmode", 0)) {
+ do {
+ _snprintf(szCounter, 8, "%d", i);
+ if (M->GetTString(NULL, szKey, szCounter, &dbv)) {
+ if (iFirstFree != -1) {
+ pContainer->iContainerIndex = iFirstFree;
+ _snprintf(szCounter, 8, "%d", iFirstFree);
+ }
+ else {
+ pContainer->iContainerIndex = i;
+ }
+ M->WriteTString(NULL, szKey, szCounter, name);
+ BuildContainerMenu();
+ break;
+ }
+ else {
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) {
+ if (!_tcsncmp(dbv.ptszVal, name, CONTAINER_NAMELEN)) {
+ pContainer->iContainerIndex = i;
+ iFound = TRUE;
+ }
+ else if (!_tcsncmp(dbv.ptszVal, _T("**free**"), CONTAINER_NAMELEN))
+ iFirstFree = i;
+ }
+ DBFreeVariant(&dbv);
+ }
+ }
+ 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;
+ }
+ return NULL;
+}
+
+static BOOL CALLBACK ContainerWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL bSkinned;
+
+ struct TContainerData *pContainer = (struct TContainerData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ 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
+ CallWindowProc(OldContainerWndProc, hwndDlg, 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(struct TitleBtn) * 3);
+ ZeroMemory(&pContainer->buttons[0], sizeof(struct 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;
+ }
+ else {
+ 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;
+ default:
+ break;
+ }
+ return CallWindowProc(OldContainerWndProc, hwndDlg, msg, wParam, lParam);
+}
+/*
+ * container window procedure...
+ */
+
+static BOOL fHaveTipper = FALSE;
+
+static INT_PTR CALLBACK DlgProcContainer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TContainerData *pContainer = 0; // pointer to our struct ContainerWindowData
+ int iItem = 0;
+ TCITEM item;
+ HWND hwndTab;
+ BOOL bSkinned;
+
+ pContainer = (struct TContainerData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ bSkinned = CSkin::m_skinEnabled ? TRUE : FALSE;
+ hwndTab = GetDlgItem(hwndDlg, IDC_MSGTABS);
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ DWORD ws;
+ HMENU hSysmenu;
+ DWORD dwCreateFlags;
+ int iMenuItems;
+ int i = 0;
+ ButtonItem *pbItem;
+ HWND hwndButton = 0;
+ bool fAero = 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;
+
+ OldContainerWndProc = (WNDPROC)SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)ContainerWndProc);
+
+ pContainer = (struct TContainerData *) lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) pContainer);
+
+ pContainer->hwnd = hwndDlg;
+ 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);
+ 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);
+
+ pbItem = pContainer->buttonItems;
+
+ 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...
+ */
+
+ hSysmenu = GetSystemMenu(hwndDlg, FALSE);
+ iMenuItems = GetMenuItemCount(hSysmenu);
+
+ InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_SEPARATOR, 0, _T(""));
+ InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, CTranslator::get(CTranslator::CNT_MENU_STAYONTOP));
+ if (!CSkin::m_frameSkins)
+ InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_NOTITLE, CTranslator::get(CTranslator::CNT_MENU_HIDETITLEBAR));
+ InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_SEPARATOR, 0, _T(""));
+ InsertMenu(hSysmenu, iMenuItems++ - 2, MF_BYPOSITION | MF_STRING, IDM_MOREOPTIONS, CTranslator::get(CTranslator::CNT_MENU_CONTAINEROPTIONS));
+ SetWindowText(hwndDlg, CTranslator::get(CTranslator::CNT_TITLE_DEFAULT));
+ 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: {
+ char *szSetting = "CNTW_";
+ char szCName[CONTAINER_NAMELEN + 20];
+ /*
+ * 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 {
+ mir_snprintf(szCName, sizeof(szCName), "%s%d", szSetting, 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: {
+ RECT rcClient, rcUnadjusted;
+ int i = 0;
+ TCITEM item = {0};
+ POINT pt = {0};
+ LONG sbarWidth, sbarWidth_left;
+ BOOL sizeChanged = FALSE;
+
+ if (IsIconic(hwndDlg)) {
+ pContainer->dwFlags |= CNT_DEFERREDSIZEREQUEST;
+ break;
+ }
+
+ GetClientRect(hwndDlg, &rcClient);
+ pContainer->MenuBar->getClientRect();
+
+ if (pContainer->hwndStatus) {
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+ RECT rcs;
+
+ SendMessage(pContainer->hwndStatus, WM_USER + 101, 0, (LPARAM)dat);
+ GetWindowRect(pContainer->hwndStatus, &rcs);
+
+ pContainer->statusBarHeight = (rcs.bottom - rcs.top) + 1;
+ SendMessage(pContainer->hwndStatus, SB_SETTEXT, (WPARAM)(SBT_OWNERDRAW) | 2, (LPARAM)0);
+
+ }
+ else
+ pContainer->statusBarHeight = 0;
+
+ CopyRect(&pContainer->rcSaved, &rcClient);
+ rcUnadjusted = rcClient;
+
+ pContainer->MenuBar->Resize(LOWORD(lParam), HIWORD(lParam), sizeChanged ? TRUE : FALSE);
+ LONG rebarHeight = pContainer->MenuBar->getHeight();
+ pContainer->MenuBar->Show((pContainer->dwFlags & CNT_NOMENUBAR) ? SW_HIDE : SW_SHOW);
+
+ sbarWidth = pContainer->SideBar->getWidth();
+ 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);
+
+ 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 (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 (PluginConfig.m_MathModAvail) {
+ TMathWindowInfo mathWndInfo;
+
+ RECT windRect;
+ GetWindowRect(hwndDlg, &windRect);
+ mathWndInfo.top = windRect.top;
+ mathWndInfo.left = windRect.left;
+ mathWndInfo.right = windRect.right;
+ mathWndInfo.bottom = windRect.bottom;
+ CallService(MTH_RESIZE, 0, (LPARAM) &mathWndInfo);
+ }
+ if ((CSkin::m_bClipBorder != 0 || CSkin::m_bRoundedCorner) && CSkin::m_frameSkins) {
+ HRGN rgn;
+ RECT rcWindow;
+ int clip = CSkin::m_bClipBorder;
+
+ 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);
+ }
+ }
+ NMHDR* pNMHDR = (NMHDR*) lParam;
+ 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) {
+ struct TWindowData *dat = (struct 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;
+ HANDLE 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, (WPARAM) 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), (LPARAM) hContact);
+ DestroyMenu(hMenu);
+ }
+ }
+ return TRUE;
+ }
+ }
+ break;
+ }
+ switch (pNMHDR->code) {
+ case TCN_SELCHANGE: {
+ ZeroMemory((void *)&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: {
+ HMENU subMenu;
+ POINT pt, pt1;
+ int iSelection, iItem;
+ TCITEM item = {0};
+ struct TWindowData *dat = 0;
+ bool fFromSidebar = false;
+
+ GetCursorPos(&pt);
+ pt1 = pt;
+ subMenu = GetSubMenu(pContainer->hMenuContext, 0);
+
+ if(pNMHDR->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 = (struct TWindowData *)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA);
+ }
+ /*
+ * sent from a sidebar button (RMB click) instead of the tab control
+ */
+ else if(pNMHDR->idFrom == 5000) {
+ TSideBarNotify* n = reinterpret_cast<TSideBarNotify *>(lParam);
+ dat = const_cast<TWindowData *>(n->dat);
+ fFromSidebar = true;
+ }
+
+ if (dat)
+ MsgWindowUpdateMenu(dat, subMenu, MENU_TABCONTEXT);
+
+ iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt1.x, pt1.y, 0, hwndDlg, NULL);
+ if (iSelection >= IDM_CONTAINERMENU) {
+ DBVARIANT dbv = {0};
+ char szIndex[10];
+ char *szKey = "TAB_ContainersW";
+ mir_snprintf(szIndex, 8, "%d", iSelection - IDM_CONTAINERMENU);
+ if (iSelection - IDM_CONTAINERMENU >= 0) {
+ if (!M->GetTString(NULL, szKey, szIndex, &dbv)) {
+ SendMessage((HWND)item.lParam, DM_CONTAINERSELECTED, 0, (LPARAM) dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ 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_SAVETABPOSITION:
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "tabindex", dat->iTabID * 100);
+ break;
+ case ID_TABMENU_CLEARSAVEDTABPOSITION:
+ DBDeleteContactSetting(dat->hContact, SRMSGMOD_T, "tabindex");
+ break;
+ case ID_TABMENU_LEAVECHATROOM: {
+ if (dat && dat->bType == SESSIONTYPE_CHAT) {
+ SESSION_INFO *si = (SESSION_INFO *)dat->si;
+ if (si && dat->hContact) {
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) dat->hContact, 0);
+ if ( szProto )
+ CallProtoService( szProto, PS_LEAVECHAT, (WPARAM)dat->hContact, 0 );
+ }
+ }
+ break;
+ }
+ case ID_TABMENU_ATTACHTOCONTAINER:
+ if ((iItem = GetTabItemFromMouse(hwndTab, &pt1)) == -1)
+ break;
+ ZeroMemory((void *)&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();
+
+ HANDLE hContact;
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+ DWORD dwOldFlags = pContainer->dwFlags;
+ int i = 0;
+ 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;
+ /*
+ else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling
+ ButtonItem *item = pContainer->buttonItems;
+ WPARAM wwParam = 0;
+ LPARAM llParam = 0;
+ HANDLE hContact = dat ? dat->hContact : 0;
+ int serviceFailure = FALSE;
+
+ while (item) {
+ if (item->uId == (DWORD)LOWORD(wParam)) {
+ int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
+
+ if (item->dwFlags & BUTTON_ISSERVICE) {
+ if (ServiceExists(item->szService) && contactOK)
+ CallService(item->szService, wwParam, llParam);
+ else if (contactOK)
+ serviceFailure = TRUE;
+ }
+ else if (item->dwFlags & BUTTON_ISPROTOSERVICE) {
+ if (contactOK) {
+ char szFinalService[512];
+
+ mir_snprintf(szFinalService, 512, "%s/%s", (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0), item->szService);
+ if (ServiceExists(szFinalService))
+ CallService(szFinalService, wwParam, llParam);
+ else
+ serviceFailure = TRUE;
+ }
+ }
+ else if (item->dwFlags & BUTTON_ISDBACTION) {
+ BYTE *pValue;
+ char *szModule = item->szModule;
+ char *szSetting = item->szSetting;
+ HANDLE finalhContact = 0;
+
+ if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) {
+ contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
+ if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ finalhContact = hContact;
+ }
+ else
+ contactOK = 1;
+
+ if (contactOK) {
+ BOOL fDelete = FALSE;
+
+ if (item->dwFlags & BUTTON_ISTOGGLE) {
+ BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED);
+
+ pValue = fChecked ? item->bValueRelease : item->bValuePush;
+ if (fChecked && pValue[0] == 0)
+ fDelete = TRUE;
+ }
+ else
+ pValue = item->bValuePush;
+
+ if (fDelete)
+ DBDeleteContactSetting(finalhContact, szModule, szSetting);
+ else {
+ switch (item->type) {
+ case DBVT_BYTE:
+ M->WriteByte(finalhContact, szModule, szSetting, pValue[0]);
+ break;
+ case DBVT_WORD:
+ DBWriteContactSettingWord(finalhContact, szModule, szSetting, *((WORD *)&pValue[0]));
+ break;
+ case DBVT_DWORD:
+ M->WriteDword(finalhContact, szModule, szSetting, *((DWORD *)&pValue[0]));
+ break;
+ case DBVT_ASCIIZ:
+ DBWriteContactSettingString(finalhContact, szModule, szSetting, (char *)pValue);
+ break;
+ }
+ }
+ }
+ else if (item->dwFlags & BUTTON_ISTOGGLE)
+ SendMessage(item->hWnd, BM_SETCHECK, 0, 0);
+ }
+ if (!contactOK)
+ MessageBox(0, _T("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat"), _T("Parameter mismatch"), MB_OK);
+ if (serviceFailure) {
+ char szError[512];
+
+ mir_snprintf(szError, 512, "The service %s specified by the %s button definition was not found. You may need to install additional plugins", item->szService, item->szName);
+ MessageBoxA(0, szError, "Service failure", MB_OK);
+ }
+ goto buttons_done;
+ }
+ item = item->nextItem;
+ }
+ }
+ while (pItem) {
+ if (LOWORD(wParam) == pItem->uId) {
+ if (pItem->pfnAction != NULL)
+ pItem->pfnAction(pItem, pContainer->hwndActive, dat, GetDlgItem(hwndDlg, pItem->uId));
+ }
+ pItem = pItem->nextItem;
+ }
+buttons_done:
+ */
+ switch (LOWORD(wParam)) {
+ case IDC_TOGGLESIDEBAR: {
+ RECT rc;
+ LONG dwNewLeft;
+ BOOL skinnedMode = bSkinned;
+
+ if (CMimAPI::m_pfnIsThemeActive)
+ skinnedMode |= (CMimAPI::m_pfnIsThemeActive() ? 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));
+ //if(lParam)
+ //SetFocus(GetDlgItem(pContainer->hwndActive, lParam));
+ 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;
+ SIZE sz;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_MSGTABS), &rc);
+ 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};
+
+ if (CMimAPI::m_pfnMonitorFromWindow && CMimAPI::m_pfnGetMonitorInfoA) {
+ HMONITOR hMonitor = CMimAPI::m_pfnMonitorFromWindow(hwndDlg, 2);
+ if (hMonitor) {
+ MONITORINFO mi = { 0 };
+ mi.cbSize = sizeof(mi);
+ CMimAPI::m_pfnGetMonitorInfoA(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;
+
+ if (PluginConfig.m_MathModAvail) {
+ if (CallService(MTH_GET_PREVIEW_SHOWN, 0, 0)) {
+ RECT rc;
+ HWND hwndMath = FindWindowA("TfrmPreview", "Preview");
+ GetWindowRect(hwndMath, &rc);
+ mmi->ptMaxSize.y -= (rc.bottom - rc.top);
+ }
+ }
+ }
+ return 0;
+ }
+ case WM_MOVE:
+ if (PluginConfig.m_MathModAvail) {
+ TMathWindowInfo mathWndInfo;
+ RECT windRect;
+ GetWindowRect(hwndDlg, &windRect);
+ mathWndInfo.top = windRect.top;
+ mathWndInfo.left = windRect.left;
+ mathWndInfo.right = windRect.right;
+ mathWndInfo.bottom = windRect.bottom;
+ CallService(MTH_RESIZE, 0, (LPARAM) &mathWndInfo);
+ }
+ break;
+ case DM_UPDATETITLE: {
+ HANDLE hContact = 0;
+ const TCHAR *szNewTitle = NULL;
+ TWindowData *dat = NULL;
+
+ if (lParam) { // lParam != 0 means sent by a chat window
+ TCHAR szText[512];
+ dat = (struct 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 = (struct TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+ }
+ else {
+ HWND hwnd = M->FindWindow((HANDLE)wParam);
+ if (hwnd == 0) {
+ SESSION_INFO *si = SM_FindSessionByHCONTACT((HANDLE)wParam);
+ if (si) {
+ SendMessage(si->hWnd, GC_UPDATETITLE, 0, 0);
+ return 0;
+ }
+ }
+ hContact = (HANDLE)wParam;
+ if (hwnd && hContact)
+ dat = (struct TWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ }
+ if (dat) {
+ SendMessage(hwndDlg, DM_SETICON, (WPARAM)dat, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabStatusIcon));
+ szNewTitle = Utils::FormatTitleBar(dat, pContainer->settings->szTitleFormat);
+ if (szNewTitle) {
+ SetWindowText(hwndDlg, szNewTitle);
+ free((void *)szNewTitle);
+ }
+ }
+ return 0;
+ }
+
+
+ case WM_TIMER:
+ if (wParam == TIMERID_HEARTBEAT) {
+ /*
+ int i;
+ TCITEM item = {0};
+ DWORD dwTimeout;
+ */
+ struct TWindowData *dat = 0;
+ /*
+ item.mask = TCIF_PARAM;
+ if ((dwTimeout = PluginConfig.m_TabAutoClose) > 0) {
+ int clients = TabCtrl_GetItemCount(GetDlgItem(hwndDlg, IDC_MSGTABS));
+ HWND *hwndClients = (HWND *)mir_alloc(sizeof(HWND) * (clients + 1));
+ for (i = 0; i < clients; i++) {
+ TabCtrl_GetItem(hwndTab, i, &item);
+ hwndClients[i] = (HWND)item.lParam;
+ }
+ for (i = 0; i < clients; i++) {
+ if (IsWindow(hwndClients[i])) {
+ if ((HWND)hwndClients[i] != pContainer->hwndActive)
+ pContainer->bDontSmartClose = TRUE;
+ SendMessage((HWND)hwndClients[i], DM_CHECKAUTOCLOSE, (WPARAM)(dwTimeout * 60), 0);
+ pContainer->bDontSmartClose = FALSE;
+ }
+ }
+ mir_free(hwndClients);
+ }
+ */
+ 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<TWindowData *>(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;
+ }
+ }
+ break;
+ case DM_SELECTTAB: {
+ switch (wParam) {
+ int iItems, iCurrent, iNewTab;
+ TCITEM item;
+
+ 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) {
+ struct TabControlData *tabdat = (struct TabControlData *)GetWindowLongPtr(hwndTab, GWLP_USERDATA);
+ ZeroMemory((void *)&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<HMENU>(wParam));
+ break;
+
+ case WM_LBUTTONDOWN: {
+ POINT pt;
+
+ if (pContainer->dwFlags & CNT_NOTITLE) {
+ 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(PluginConfig.m_MathModAvail)
+ CallService(MTH_HIDE, 0, 0);
+ }
+
+ 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 && CMimAPI::m_pSetLayeredWindowAttributes != NULL && fTransAllowed) {
+ CMimAPI::m_pSetLayeredWindowAttributes(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: {
+ TCITEM item;
+ int curItem = 0;
+ BOOL fTransAllowed = !bSkinned || PluginConfig.m_WinVerMajor >= 6;
+
+ if (pContainer == NULL)
+ break;
+
+ FlashContainer(pContainer, 0, 0);
+ pContainer->dwFlashingStarted = 0;
+ pLastActiveContainer = pContainer;
+ if (pContainer->dwFlags & CNT_DEFERREDTABSELECT) {
+ NMHDR nmhdr;
+
+ pContainer->dwFlags &= ~CNT_DEFERREDTABSELECT;
+ SendMessage(hwndDlg, WM_SYSCOMMAND, SC_RESTORE, 0);
+ ZeroMemory((void *)&nmhdr, sizeof(nmhdr));
+ nmhdr.code = TCN_SELCHANGE;
+ nmhdr.hwndFrom = hwndTab;
+ nmhdr.idFrom = IDC_MSGTABS;
+ 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 && CMimAPI::m_pSetLayeredWindowAttributes != NULL && fTransAllowed) {
+ DWORD trans = LOWORD(pContainer->settings->dwTransparency);
+ CMimAPI::m_pSetLayeredWindowAttributes(hwndDlg, Skin->getColorKey(), (BYTE)trans, (pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0));
+ }
+ if (pContainer->dwFlags & CNT_NEED_UPDATETITLE) {
+ HANDLE hContact = 0;
+ pContainer->dwFlags &= ~CNT_NEED_UPDATETITLE;
+ if (pContainer->hwndActive) {
+ SendMessage(pContainer->hwndActive, DM_QUERYHCONTACT, 0, (LPARAM)&hContact);
+ if (hContact)
+ SendMessage(hwndDlg, DM_UPDATETITLE, (WPARAM)hContact, 0);
+ }
+ }
+ ZeroMemory((void *)&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;
+ POINT *pt = (POINT *)lParam;
+ int iItem;
+ TCITEM item = {0};
+
+ hwndCurrent = pContainer->hwndActive;
+ if ((iItem = GetTabItemFromMouse(hwndTab, pt)) == -1)
+ break;
+ ZeroMemory((void *)&item, sizeof(item));
+ 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: {
+ bool fAero = M->isAero();
+
+ if (bSkinned || fAero) {
+ 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)
+ break;
+
+ HDC hdc = (HDC)wParam;
+ RECT rc;
+ GetClientRect(hwndDlg, &rc);
+
+ if (M->isAero()) {
+ HDC hdcMem;
+ HANDLE hbp;
+
+ 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<HPEN>(::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;
+ }
+ case DM_OPTIONSAPPLIED: {
+ char szCname[40];
+ TCHAR szTitleFormat[200];
+ TCHAR* szThemeName = NULL;
+ DBVARIANT dbv = {0};
+ char *szSetting = "CNTW_";
+
+ szTitleFormat[0] = 0;
+
+ if (pContainer->isCloned && pContainer->hContactFrom != 0) {
+ //if(pContainer->settings == 0)
+ // pContainer->settings = (TContainerSettings *)malloc(sizeof(TContainerSettings));
+
+ //CopyMemory((void *)pContainer->settings, (void *)&PluginConfig.globalContainerSettings, sizeof(TContainerSettings));
+ //Utils::ReadContainerSettingsFromDB(pContainer->hContactFrom, pContainer->settings);
+
+ pContainer->settings = &PluginConfig.globalContainerSettings;
+
+ pContainer->szRelThemeFile[0] = pContainer->szAbsThemeFile[0] = 0;
+ mir_snprintf(szCname, 40, "%s_theme", szSetting);
+ if (!M->GetTString(pContainer->hContactFrom, SRMSGMOD_T, szCname, &dbv))
+ szThemeName = dbv.ptszVal;
+ }
+ else {
+ Utils::ReadPrivateContainerSettings(pContainer);
+ if (szThemeName == NULL) {
+ mir_snprintf(szCname, 40, "%s%d_theme", szSetting, pContainer->iContainerIndex);
+ if (!M->GetTString(NULL, SRMSGMOD_T, szCname, &dbv))
+ szThemeName = dbv.ptszVal;
+ }
+ }
+ Utils::SettingsToContainer(pContainer);
+
+ if (szThemeName != NULL) {
+ M->pathToAbsolute(szThemeName, pContainer->szAbsThemeFile);
+ mir_sntprintf(pContainer->szRelThemeFile, MAX_PATH, _T("%s"), szThemeName);
+ DBFreeVariant(&dbv);
+ }
+ else
+ pContainer->szAbsThemeFile[0] = pContainer->szRelThemeFile[0] = 0;
+
+ pContainer->ltr_templates = pContainer->rtl_templates = 0;
+ break;
+ }
+ case DM_STATUSBARCHANGED: {
+ RECT rc;
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ 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);
+ HANDLE hContact = 0;
+ int i = 0;
+ UINT sBarHeight;
+ bool fAero = 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 && CMimAPI::m_pSetLayeredWindowAttributes != NULL) {
+ BOOL fTransAllowed = !bSkinned || PluginConfig.m_WinVerMajor >= 6;
+ DWORD exold;
+
+ ex = exold = GetWindowLongPtr(hwndDlg, GWL_EXSTYLE);
+ ex = (pContainer->dwFlags & CNT_TRANSPARENCY && (!CSkin::m_skinEnabled || fTransAllowed)) ? ex | WS_EX_LAYERED : ex & ~(WS_EX_LAYERED);
+ //if(fAero && !pContainer->bSkinned && IsWinVerVistaPlus())
+ // ex = ex | (WS_EX_COMPOSITED);//|WS_EX_LAYERED); // | WS_EX_COMPOSITED); // faster/smoother redrawing on Vista+, especially with skins
+
+ SetWindowLongPtr(hwndDlg, GWL_EXSTYLE, ex);
+ if (pContainer->dwFlags & CNT_TRANSPARENCY && fTransAllowed) {
+ DWORD trans = LOWORD(pContainer->settings->dwTransparency);
+ CMimAPI::m_pSetLayeredWindowAttributes(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) {
+ //SetWindowLongPtr(pContainer->hwndStatus, GWLP_WNDPROC, (LONG_PTR)OldStatusBarproc);
+ 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, (WPARAM)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 i;
+ int iItems = TabCtrl_GetItemCount(hwndTab);
+ RECENTINFO *ri = (RECENTINFO *)lParam;
+ TCITEM item = {0};
+
+ DWORD dwTimestamp, dwMostRecent = 0;
+
+ ri->iFirstIndex = ri->iMostRecent = -1;
+ ri->dwFirst = ri->dwMostRecent = 0;
+ ri->hwndFirst = ri->hwndMostRecent = 0;
+
+ for (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: {
+ NMHDR nmhdr;
+ RECENTINFO ri;
+
+ SendMessage(hwndDlg, DM_QUERYRECENT, 0, (LPARAM)&ri);
+ 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: {
+ HICON hIconMsg = PluginConfig.g_IconMsgEvent;
+ TWindowData* dat = (TWindowData *)wParam;
+ HICON hIconBig = (dat && dat->cache) ? LoadSkinnedProtoIconBig(dat->cache->getActiveProto(), dat->cache->getActiveStatus()) : 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);
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, lParam);
+ break;
+ }
+ if(reinterpret_cast<HICON>(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<HICON>(CALLSERVICE_NOTFOUND) != hIconBig)
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIconBig));
+ pContainer->hIcon = (lParam == (LPARAM)hIconMsg) ? STICK_ICON_MSG : 0;
+ return(0);
+ }
+ case WM_DRAWITEM: {
+ int cx = PluginConfig.m_smcxicon;
+ int cy = PluginConfig.m_smcyicon;
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+ int id = LOWORD(dis->itemID);
+
+ if (dis->hwndItem == pContainer->hwndStatus && !(pContainer->dwFlags & CNT_NOSTATUSBAR)) {
+ struct TWindowData *dat = (struct 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: {
+ int i = 0;
+ TCITEM item;
+ SESSION_INFO *node = m_WndList;
+
+ if (PluginConfig.g_FlashAvatarAvail) { // destroy own flash avatar
+ FLASHAVATAR fa = {0};
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+
+ fa.id = 25367;
+ fa.cProto = dat ? dat->szProto : NULL;
+ CallService(MS_FAVATAR_DESTROY, (WPARAM)&fa, 0);
+ }
+ ZeroMemory((void *)&item, sizeof(item));
+ pContainer->hwnd = 0;
+ pContainer->hwndActive = 0;
+ pContainer->hMenuContext = 0;
+ if (pContainer->hwndStatus)
+ DestroyWindow(pContainer->hwndStatus);
+
+ // free private theme...
+ if(pContainer->theme.isPrivate) {
+ free(pContainer->ltr_templates);
+ free(pContainer->rtl_templates);
+ free(pContainer->theme.logFonts);
+ free(pContainer->theme.fontColors);
+ free(pContainer->theme.rtfFonts);
+ }
+
+ if (pContainer->hwndTip)
+ DestroyWindow(pContainer->hwndTip);
+ RemoveContainerFromList(pContainer);
+ if (PluginConfig.m_MathModAvail)
+ CallService(MTH_HIDE, 0, 0);
+ while (node) {
+ if (node->pContainer == pContainer) {
+ node->pContainer = 0;
+ }
+ node = node->next;
+ }
+ 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);
+ }
+ SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)OldContainerWndProc);
+ return 0;
+ }
+
+ case WM_NCDESTROY:
+ if (pContainer) {
+ delete pContainer->MenuBar;
+ delete pContainer->SideBar;
+ if(pContainer->settings != &PluginConfig.globalContainerSettings)
+ free(pContainer->settings);
+ free(pContainer);
+ }
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ case WM_CLOSE: {
+ //mad
+ if (PluginConfig.m_HideOnClose && !lParam) {
+ ShowWindow(hwndDlg, SW_HIDE);
+ pContainer->fHidden = true;
+ }
+ else {
+ WINDOWPLACEMENT wp;
+ char szCName[40];
+ char *szSetting = "CNTW_";
+
+ if(TabCtrl_GetItemCount(hwndTab) > 1) {
+ LRESULT res = CWarning::show(CWarning::WARN_CLOSEWINDOW, MB_YESNOCANCEL|MB_ICONQUESTION);
+ if(IDNO == res || IDCANCEL == res)
+ break;
+ }
+
+ if (lParam == 0 && TabCtrl_GetItemCount(GetDlgItem(hwndDlg, IDC_MSGTABS)) > 0) { // dont ask if container is empty (no tabs)
+ int clients = TabCtrl_GetItemCount(hwndTab), i;
+ TCITEM item = {0};
+ int iOpenJobs = 0;
+
+ item.mask = TCIF_PARAM;
+ for (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) {
+ LRESULT result;
+
+ if (pContainer->exFlags & CNT_EX_CLOSEWARN)
+ return TRUE;
+
+ pContainer->exFlags |= CNT_EX_CLOSEWARN;
+ result = SendQueue::WarnPendingJobs(iOpenJobs);
+ pContainer->exFlags &= ~CNT_EX_CLOSEWARN;
+ if (result == IDNO)
+ return TRUE;
+ }
+ }
+
+ ZeroMemory((void *)&wp, sizeof(wp));
+ wp.length = sizeof(wp);
+ /*
+ * save geometry information to the database...
+ */
+ if (!(pContainer->dwFlags & CNT_GLOBALSIZE)) {
+ if (GetWindowPlacement(hwndDlg, &wp) != 0) {
+ if (pContainer->isCloned && pContainer->hContactFrom != 0) {
+ HANDLE hContact;
+ int i;
+ TCITEM item = {0};
+
+ item.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwndTab, TabCtrl_GetCurSel(hwndTab), &item);
+ SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact);
+ M->WriteByte(hContact, SRMSGMOD_T, "splitmax", (BYTE)((wp.showCmd==SW_SHOWMAXIMIZED)?1:0));
+
+ for (i = 0; i < TabCtrl_GetItemCount(hwndTab); i++) {
+ if (TabCtrl_GetItem(hwndTab, i, &item)) {
+ SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact);
+ M->WriteDword(hContact, SRMSGMOD_T, "splitx", wp.rcNormalPosition.left);
+ M->WriteDword(hContact, SRMSGMOD_T, "splity", wp.rcNormalPosition.top);
+ M->WriteDword(hContact, SRMSGMOD_T, "splitwidth", wp.rcNormalPosition.right - wp.rcNormalPosition.left);
+ M->WriteDword(hContact, SRMSGMOD_T, "splitheight", wp.rcNormalPosition.bottom - wp.rcNormalPosition.top);
+ }
+ }
+ }
+ else {
+ _snprintf(szCName, 40, "%s%dx", szSetting, pContainer->iContainerIndex);
+ M->WriteDword(SRMSGMOD_T, szCName, wp.rcNormalPosition.left);
+ _snprintf(szCName, 40, "%s%dy", szSetting, pContainer->iContainerIndex);
+ M->WriteDword(SRMSGMOD_T, szCName, wp.rcNormalPosition.top);
+ _snprintf(szCName, 40, "%s%dwidth", szSetting, pContainer->iContainerIndex);
+ M->WriteDword(SRMSGMOD_T, szCName, wp.rcNormalPosition.right - wp.rcNormalPosition.left);
+ _snprintf(szCName, 40, "%s%dheight", szSetting, pContainer->iContainerIndex);
+ M->WriteDword(SRMSGMOD_T, szCName, wp.rcNormalPosition.bottom - wp.rcNormalPosition.top);
+
+ M->WriteByte(SRMSGMOD_T, "splitmax", (BYTE)((wp.showCmd==SW_SHOWMAXIMIZED)?1:0));
+ }
+ }
+ }
+ // clear temp flags which should NEVER be saved...
+
+ if (pContainer->isCloned && pContainer->hContactFrom != 0) {
+ HANDLE hContact;
+ int i;
+ TCITEM item = {0};
+
+ item.mask = TCIF_PARAM;
+ pContainer->dwFlags &= ~(CNT_DEFERREDCONFIGURE | CNT_CREATE_MINIMIZED | CNT_DEFERREDSIZEREQUEST | CNT_CREATE_CLONED);
+ for (i = 0; i < TabCtrl_GetItemCount(hwndTab); i++) {
+ if (TabCtrl_GetItem(hwndTab, i, &item)) {
+ SendMessage((HWND)item.lParam, DM_QUERYHCONTACT, 0, (LPARAM)&hContact);
+ //Utils::WriteContainerSettingsToDB(hContact, pContainer->settings);
+
+ mir_snprintf(szCName, 40, "%s_theme", szSetting);
+ if (lstrlen(pContainer->szRelThemeFile) > 1) {
+ if(pContainer->fPrivateThemeChanged == TRUE) {
+ M->pathToRelative(pContainer->szRelThemeFile, pContainer->szAbsThemeFile);
+ M->WriteTString(hContact, SRMSGMOD_T, szCName, pContainer->szRelThemeFile);
+ pContainer->fPrivateThemeChanged = FALSE;
+ }
+ }
+ else {
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, szCName);
+ pContainer->fPrivateThemeChanged = FALSE;
+ }
+
+ }
+ }
+ }
+ else
+ Utils::SaveContainerSettings(pContainer, szSetting);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+ }
+ default:
+ return FALSE;
+ }
+ 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)
+{
+ TCITEM item;
+ int i = 0;
+ int iItems;
+
+ iItems = TabCtrl_GetItemCount(hwndTab);
+
+ ZeroMemory((void *)&item, sizeof(item));
+ item.mask = TCIF_PARAM;
+
+ for (i = 0; i < iItems; i++) {
+ TabCtrl_GetItem(hwndTab, i, &item);
+ if ((HWND)item.lParam == hwnd) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * activates the tab belonging to the given client HWND (handle of the actual
+ * message window.
+ */
+
+int TSAPI ActivateTabFromHWND(HWND hwndTab, HWND hwnd)
+{
+ NMHDR nmhdr;
+
+ int iItem = GetTabIndexFromHWND(hwndTab, hwnd);
+ if (iItem >= 0) {
+ TabCtrl_SetCurSel(hwndTab, iItem);
+ ZeroMemory((void *)&nmhdr, sizeof(nmhdr));
+ 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)
+{
+ TCHITTESTINFO tch;
+
+ ScreenToClient(hwndTab, pt);
+ tch.pt = *pt;
+ tch.flags = 0;
+ return TabCtrl_HitTest(hwndTab, &tch);
+}
+
+/*
+ * 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];
+ _sntprintf(fmt, 18, _T("%%%d.%ds..."), cutMax, cutMax);
+ _sntprintf(newname, size, fmt, oldname);
+ newname[size - 1] = 0;
+ }
+ return 0;
+}
+
+/*
+ * functions for handling the linked list of struct ContainerWindowData *foo
+ */
+
+static struct TContainerData* TSAPI AppendToContainerList(struct TContainerData *pContainer) {
+ struct TContainerData *pCurrent = 0;
+
+ if (!pFirstContainer) {
+ pFirstContainer = pContainer;
+ pFirstContainer->pNextContainer = NULL;
+ return pFirstContainer;
+ } else {
+ pCurrent = pFirstContainer;
+ while (pCurrent->pNextContainer != 0)
+ pCurrent = pCurrent->pNextContainer;
+ pCurrent->pNextContainer = pContainer;
+ pContainer->pNextContainer = NULL;
+ return pCurrent;
+ }
+}
+
+struct TContainerData* TSAPI FindContainerByName(const TCHAR *name) {
+ struct TContainerData *pCurrent = pFirstContainer;
+
+ 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;
+ }
+
+ while (pCurrent) {
+ if (!_tcsncmp(pCurrent->szName, name, CONTAINER_NAMELEN))
+ return pCurrent;
+ pCurrent = pCurrent->pNextContainer;
+ }
+ // error, didn't find it.
+ return NULL;
+}
+
+static struct TContainerData* TSAPI RemoveContainerFromList(struct TContainerData *pContainer) {
+ struct TContainerData *pCurrent = pFirstContainer;
+
+ if (pContainer == pFirstContainer) {
+ if (pContainer->pNextContainer != NULL)
+ pFirstContainer = pContainer->pNextContainer;
+ else
+ pFirstContainer = NULL;
+
+ if (pLastActiveContainer == pContainer) // make sure, we don't reference this container anymore
+ pLastActiveContainer = pFirstContainer;
+
+ return pFirstContainer;
+ }
+
+ do {
+ if (pCurrent->pNextContainer == pContainer) {
+ pCurrent->pNextContainer = pCurrent->pNextContainer->pNextContainer;
+
+ if (pLastActiveContainer == pContainer) // make sure, we don't reference this container anymore
+ pLastActiveContainer = pFirstContainer;
+
+ return 0;
+ }
+ } while (pCurrent = pCurrent->pNextContainer);
+ 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(struct TContainerData *pContainer, RECT *rc)
+{
+ HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS);
+ RECT rcTab, rcTabOrig;
+ DWORD dwBottom, dwTop;
+ DWORD tBorder = pContainer->tBorder;
+ DWORD dwStyle = GetWindowLongPtr(hwndTab, GWL_STYLE);
+
+ GetClientRect(hwndTab, &rcTab);
+ dwBottom = rcTab.bottom;
+ dwTop = rcTab.top;
+ if (!(pContainer->dwFlags & CNT_SIDEBAR) && (pContainer->iChilds > 1 || !(pContainer->dwFlags & CNT_HIDETABS))) {
+ DWORD dwTopPad;
+ rcTabOrig = rcTab;
+ TabCtrl_AdjustRect(hwndTab, FALSE, &rcTab);
+ 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);
+ RECT rcItem;
+
+ if (nCount > 0) {
+ TabCtrl_GetItemRect(hwndTab, nCount - 1, &rcItem);
+ //rc->top = pContainer->tBorder_outer_top;
+ 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(HANDLE hContact, TCHAR *szName, int iNameLen)
+{
+ DBVARIANT dbv;
+
+ if (M->GetByte("singlewinmode", 0)) { // single window mode using cloned (temporary) containers
+ _tcsncpy(szName, _T("Message Session"), iNameLen);
+ return 0;
+ }
+
+ if (M->GetByte("useclistgroups", 0)) { // use clist group names for containers...
+ if (M->GetTString(hContact, "CList", "Group", &dbv)) {
+ _tcsncpy(szName, _T("default"), iNameLen);
+ return 0;
+ }
+ else {
+ if (lstrlen(dbv.ptszVal) > CONTAINER_NAMELEN)
+ dbv.ptszVal[CONTAINER_NAMELEN] = '\0';
+ _tcsncpy(szName, dbv.ptszVal, iNameLen);
+ szName[iNameLen] = '\0';
+ DBFreeVariant(&dbv);
+ return dbv.cchVal;
+ }
+ }
+ if (M->GetTString(hContact, SRMSGMOD_T, "containerW", &dbv)) {
+ _tcsncpy(szName, _T("default"), iNameLen);
+ return 0;
+ }
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) {
+ _tcsncpy(szName, dbv.ptszVal, iNameLen);
+ szName[iNameLen] = 0;
+ DBFreeVariant(&dbv);
+ return dbv.cpbVal;
+ }
+ DBFreeVariant(&dbv);
+ return 0;
+}
+
+void TSAPI DeleteContainer(int iIndex) {
+ DBVARIANT dbv;
+ char szIndex[10], szSetting[CONTAINER_NAMELEN + 30];
+ char *szKey = "TAB_ContainersW";
+ char *szSettingP = "CNTW_";
+ char *szSubKey = "containerW";
+ HANDLE hhContact;
+ _snprintf(szIndex, 8, "%d", iIndex);
+
+
+ if (!M->GetTString(NULL, szKey, szIndex, &dbv)) {
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) {
+ TCHAR *wszContainerName = dbv.ptszVal;
+ M->WriteTString(NULL, szKey, szIndex, _T("**free**"));
+
+ hhContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hhContact) {
+ DBVARIANT dbv_c;
+ if (!M->GetTString(hhContact, SRMSGMOD_T, szSubKey, &dbv_c)) {
+ TCHAR *wszString = dbv_c.ptszVal;
+ if (_tcscmp(wszString, wszContainerName) && lstrlen(wszString) == lstrlen(wszContainerName))
+ DBDeleteContactSetting(hhContact, SRMSGMOD_T, "containerW");
+ DBFreeVariant(&dbv_c);
+ }
+ hhContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hhContact, 0);
+ }
+ _snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%d_Flags", szSettingP, iIndex);
+ DBDeleteContactSetting(NULL, SRMSGMOD_T, szSetting);
+ _snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%d_Trans", szSettingP, iIndex);
+ DBDeleteContactSetting(NULL, SRMSGMOD_T, szSetting);
+ _snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dwidth", szSettingP, iIndex);
+ DBDeleteContactSetting(NULL, SRMSGMOD_T, szSetting);
+ _snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dheight", szSettingP, iIndex);
+ DBDeleteContactSetting(NULL, SRMSGMOD_T, szSetting);
+ _snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dx", szSettingP, iIndex);
+ DBDeleteContactSetting(NULL, SRMSGMOD_T, szSetting);
+ _snprintf(szSetting, CONTAINER_NAMELEN + 15, "%s%dy", szSettingP, iIndex);
+ DBDeleteContactSetting(NULL, SRMSGMOD_T, szSetting);
+ }
+ DBFreeVariant(&dbv);
+ }
+}
+
+void TSAPI RenameContainer(int iIndex, const TCHAR *szNew) {
+ DBVARIANT dbv;
+ char *szKey = "TAB_ContainersW";
+ char *szSettingP = "CNTW_";
+ char *szSubKey = "containerW";
+ char szIndex[10];
+ HANDLE hhContact;
+
+ _snprintf(szIndex, 8, "%d", iIndex);
+ if (!M->GetTString(NULL, szKey, szIndex, &dbv)) {
+ if (szNew != NULL) {
+ if (lstrlen(szNew) != 0)
+ M->WriteTString(NULL, szKey, szIndex, szNew);
+ }
+ hhContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hhContact) {
+ DBVARIANT dbv_c;
+ if (!M->GetTString(hhContact, SRMSGMOD_T, szSubKey, &dbv_c)) {
+ if (!_tcscmp(dbv.ptszVal, dbv_c.ptszVal) && lstrlen(dbv_c.ptszVal) == lstrlen(dbv.ptszVal)) {
+ if (szNew != NULL) {
+ if (lstrlen(szNew) != 0)
+ M->WriteTString(hhContact, SRMSGMOD_T, szSubKey, szNew);
+ }
+ }
+ DBFreeVariant(&dbv_c);
+ }
+ hhContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hhContact, 0);
+ }
+ DBFreeVariant(&dbv);
+ }
+}
+
+HMENU TSAPI BuildContainerMenu()
+{
+ char *szKey = "TAB_ContainersW";
+ char szCounter[10];
+ int i = 0;
+ DBVARIANT dbv = { 0 };
+ HMENU hMenu;
+ MENUITEMINFO mii = {0};
+
+ 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 = CreateMenu();
+ do {
+ _snprintf(szCounter, 8, "%d", i);
+ if (M->GetTString(NULL, szKey, szCounter, &dbv))
+ break;
+
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) {
+ if (_tcsncmp(dbv.ptszVal, _T("**free**"), CONTAINER_NAMELEN))
+ AppendMenu(hMenu, MF_STRING, IDM_CONTAINERMENU + i, !_tcscmp(dbv.ptszVal, _T("default")) ?
+ CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME) : dbv.ptszVal);
+ }
+ DBFreeVariant(&dbv);
+ i++;
+ }
+ while (TRUE);
+
+ InsertMenu(PluginConfig.g_hMenuContext, ID_TABMENU_ATTACHTOCONTAINER, MF_BYCOMMAND | MF_POPUP, (UINT_PTR) hMenu, CTranslator::get(CTranslator::CNT_ATTACH_TO));
+ PluginConfig.g_hMenuContainer = hMenu;
+ return hMenu;
+}
+
+HMENU TSAPI BuildMCProtocolMenu(HWND hwndDlg) {
+ HMENU hMCContextMenu = 0, hMCSubForce = 0, hMCSubDefault = 0, hMenu = 0;
+ DBVARIANT dbv;
+ int iNumProtos = 0, i = 0, iDefaultProtoByNum = 0;
+ char szTemp[50], *szProtoMostOnline = NULL;
+ TCHAR szMenuLine[128], *nick = NULL, *szStatusText = NULL;
+ char *tzProtoName = NULL;
+ HANDLE hContactMostOnline, handle;
+ int iChecked, isForced;
+ WORD wStatus;
+
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (dat == NULL)
+ return (HMENU) 0;
+
+ if (!dat->cache->isMeta())
+ return (HMENU) 0;
+
+ hMenu = CreatePopupMenu();
+ hMCContextMenu = GetSubMenu(hMenu, 0);
+ hMCSubForce = CreatePopupMenu();
+ hMCSubDefault = CreatePopupMenu();
+
+ AppendMenu(hMenu, MF_STRING | MF_DISABLED | MF_GRAYED | MF_CHECKED, 1, CTranslator::get(CTranslator::GEN_META_CONTACT));
+ AppendMenu(hMenu, MF_SEPARATOR, 1, _T(""));
+
+ iNumProtos = (int)CallService(MS_MC_GETNUMCONTACTS, (WPARAM)dat->hContact, 0);
+ iDefaultProtoByNum = (int)CallService(MS_MC_GETDEFAULTCONTACTNUM, (WPARAM)dat->hContact, 0);
+ hContactMostOnline = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)dat->hContact, 0);
+ szProtoMostOnline = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContactMostOnline, 0);
+ isForced = M->GetDword(dat->hContact, "tabSRMM_forced", -1);
+
+ for (i = 0; i < iNumProtos; i++) {
+ mir_snprintf(szTemp, sizeof(szTemp), "Protocol%d", i);
+ if (DBGetContactSettingString(dat->hContact, PluginConfig.szMetaName, szTemp, &dbv))
+ continue;
+
+ tzProtoName = dbv.pszVal;
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)CallService(MS_PROTO_GETACCOUNT, (WPARAM)0, (LPARAM)tzProtoName);
+
+ if(acc && acc->tszAccountName) {
+ mir_snprintf(szTemp, sizeof(szTemp), "Handle%d", i);
+ if ((handle = (HANDLE)M->GetDword(dat->hContact, PluginConfig.szMetaName, szTemp, 0)) != 0) {
+ nick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)handle, GCDNF_TCHAR);
+ mir_snprintf(szTemp, sizeof(szTemp), "Status%d", i);
+ wStatus = (WORD)DBGetContactSettingWord(dat->hContact, PluginConfig.szMetaName, szTemp, 0);
+ szStatusText = (TCHAR *) CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, wStatus, GSMDF_TCHAR);
+ }
+ mir_sntprintf(szMenuLine, safe_sizeof(szMenuLine), _T("%s: %s [%s] %s"), acc->tszAccountName, nick, szStatusText,
+ i == isForced ? CTranslator::get(CTranslator::GEN_META_FORCED) : _T(""));
+ iChecked = MF_UNCHECKED;
+ if (hContactMostOnline != 0 && hContactMostOnline == handle)
+ iChecked = MF_CHECKED;
+ AppendMenu(hMCSubForce, MF_STRING | iChecked, 100 + i, szMenuLine);
+ AppendMenu(hMCSubDefault, MF_STRING | (i == iDefaultProtoByNum ? MF_CHECKED : MF_UNCHECKED), 1000 + i, szMenuLine);
+ }
+ DBFreeVariant(&dbv);
+ }
+ AppendMenu(hMCSubForce, MF_SEPARATOR, 900, _T(""));
+ AppendMenu(hMCSubForce, MF_STRING | ((isForced == -1) ? MF_CHECKED : MF_UNCHECKED), 999, CTranslator::get(CTranslator::GEN_META_AUTOSELECT));
+ InsertMenu(hMenu, 2, MF_BYPOSITION | MF_POPUP, (UINT_PTR) hMCSubForce, CTranslator::get(CTranslator::GEN_META_USEPROTO));
+ InsertMenu(hMenu, 2, MF_BYPOSITION | MF_POPUP, (UINT_PTR) hMCSubDefault, CTranslator::get(CTranslator::GEN_META_SETDEFAULT));
+
+ return hMenu;
+}
+
+/*
+ * flashes the container
+ * iMode != 0: turn on flashing
+ * iMode == 0: turn off flashing
+ */
+
+void TSAPI FlashContainer(struct TContainerData *pContainer, int iMode, int iCount) {
+ FLASHWINFO fwi;
+
+ if (CMimAPI::m_MyFlashWindowEx == NULL)
+ return;
+
+ if (pContainer->dwFlags & CNT_NOFLASH) // container should never flash
+ return;
+
+ 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();
+ CMimAPI::m_MyFlashWindowEx(&fwi);
+}
+
+void TSAPI ReflashContainer(struct 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) {
+ int i;
+ TCITEM item;
+
+ HWND hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS);
+ ZeroMemory((void *)&item, sizeof(item));
+
+ item.mask = TCIF_PARAM;
+
+ int nCount = TabCtrl_GetItemCount(hwndTab);
+ for (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);
+ }
+ }
+ }
+}
diff --git a/plugins/TabSRMM/src/containeroptions.cpp b/plugins/TabSRMM/src/containeroptions.cpp new file mode 100644 index 0000000000..dd87ef56d2 --- /dev/null +++ b/plugins/TabSRMM/src/containeroptions.cpp @@ -0,0 +1,586 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: containeroptions.cpp 13631 2011-04-24 08:44:57Z silvercircle $
+ *
+ * The dialog to customize per container options
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+static void MY_CheckDlgButton(HWND hWnd, UINT id, int iCheck)
+{
+ CheckDlgButton(hWnd, id, iCheck ? BST_CHECKED : BST_UNCHECKED);
+}
+
+static void ReloadGlobalContainerSettings(bool fForceReconfig)
+{
+ struct TContainerData *pC = pFirstContainer;
+
+ while (pC) {
+ if (!pC->settings->fPrivate) {
+ Utils::SettingsToContainer(pC);
+ if(fForceReconfig)
+ SendMessage(pC->hwnd, DM_CONFIGURECONTAINER, 0, 0);
+ else
+ SendMessage(pC->hwnd, WM_SIZE, 0, 1);
+ BroadCastContainer(pC, DM_SETINFOPANEL, 0, 0);
+ }
+ pC = pC->pNextContainer;
+ }
+}
+
+/**
+ * Apply a container setting
+ *
+ * @param pContainer ContainerWindowData *: the container
+ * @param flags DWORD: the flag values to set or clear
+ * @param mode int: bit #0 set/clear, any bit from 16-31 indicates that dwFlagsEx should be affected
+ * @param fForceResize
+ */
+void TSAPI ApplyContainerSetting(TContainerData *pContainer, DWORD flags, UINT mode, bool fForceResize)
+{
+ DWORD dwOld = pContainer->dwFlags;
+ bool isEx = (mode & 0xffff0000) ? true : false;
+ bool set = (mode & 0x01) ? true : false;
+
+ if (!pContainer->settings->fPrivate) {
+ if(!isEx)
+ pContainer->dwFlags = (set ? pContainer->dwFlags | flags : pContainer->dwFlags & ~flags);
+ else
+ pContainer->dwFlagsEx = (set ? pContainer->dwFlagsEx | flags : pContainer->dwFlagsEx & ~flags);
+
+ Utils::ContainerToSettings(pContainer);
+ if (flags & CNT_INFOPANEL)
+ BroadCastContainer(pContainer, DM_SETINFOPANEL, 0, 0);
+ if (flags & CNT_SIDEBAR) {
+ struct TContainerData *pC = pFirstContainer;
+ while (pC) {
+ if (!pC->settings->fPrivate) {
+ SendMessage(pC->hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ }
+ pC = pC->pNextContainer;
+ }
+ }
+ else
+ ReloadGlobalContainerSettings(fForceResize);
+ }
+ else {
+ if(!isEx)
+ pContainer->dwFlags = (set ? pContainer->dwFlags | flags : pContainer->dwFlags & ~flags);
+ else
+ pContainer->dwFlagsEx = (set ? pContainer->dwFlagsEx | flags : pContainer->dwFlagsEx & ~flags);
+ Utils::ContainerToSettings(pContainer);
+ if (flags & CNT_SIDEBAR)
+ SendMessage(pContainer->hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ else
+ SendMessage(pContainer->hwnd, DM_CONFIGURECONTAINER, 0, 0);
+ if (flags & CNT_INFOPANEL)
+ BroadCastContainer(pContainer, DM_SETINFOPANEL, 0, 0);
+ }
+
+ if(fForceResize)
+ SendMessage(pContainer->hwnd, WM_SIZE, 0, 1);
+
+ BroadCastContainer(pContainer, DM_BBNEEDUPDATE, 0, 0);
+}
+
+#define NR_O_PAGES 10
+#define NR_O_OPTIONSPERPAGE 10
+
+static struct _tagPages {
+ UINT idTitle;
+ UINT idDesc;
+ UINT uIds[10];
+} o_pages[] = {
+ { CTranslator::CNT_OPT_TITLE_GEN, CTranslator::STR_LAST, IDC_O_NOTABS, IDC_O_STICKY, IDC_VERTICALMAX, IDC_AUTOSPLITTER, IDC_O_AUTOHIDE, IDC_AUTOCLOSETABTIME, IDC_AUTOCLOSETABSPIN, IDC_O_AUTOHIDESECONDS, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_LAYOUT, CTranslator::STR_LAST, IDC_CNTNOSTATUSBAR, IDC_HIDEMENUBAR, IDC_UIDSTATUSBAR, IDC_HIDETOOLBAR, IDC_INFOPANEL, IDC_BOTTOMTOOLBAR, 0, 0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_TABS, CTranslator::CNT_OPT_DESC_TABS, IDC_TABMODE, IDC_O_TABMODE, IDC_O_SBARLAYOUT, IDC_SBARLAYOUT, IDC_FLASHICON, IDC_FLASHLABEL, IDC_SINGLEROWTAB, IDC_BUTTONTABS, IDC_CLOSEBUTTONONTABS, 0},
+ { CTranslator::CNT_OPT_TITLE_NOTIFY, CTranslator::CNT_OPT_DESC_NOTIFY, IDC_O_DONTREPORT, IDC_DONTREPORTUNFOCUSED2, IDC_DONTREPORTFOCUSED2, IDC_ALWAYSPOPUPSINACTIVE, IDC_O_EXPLAINGLOBALNOTIFY, 0, 0, 0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_FLASHING, CTranslator::STR_LAST, IDC_O_FLASHDEFAULT, IDC_O_FLASHALWAYS, IDC_O_FLASHNEVER, 0, 0, 0, 0, 0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_TITLEBAR, CTranslator::STR_LAST, IDC_O_HIDETITLE, IDC_TITLEFORMAT, IDC_O_TITLEBARFORMAT, IDC_O_HELP_TITLEFORMAT, 0, 0, 0, 0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_THEME, CTranslator::CNT_OPT_DESC_THEME, IDC_THEME, IDC_SELECTTHEME, IDC_USEGLOBALSIZE, IDC_SAVESIZEASGLOBAL, IDC_LABEL_PRIVATETHEME, IDC_TSLABEL_EXPLAINTHEME, 0, 0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_TRANS, CTranslator::CNT_OPT_DESC_TRANS, IDC_TRANSPARENCY, IDC_TRANSPARENCY_ACTIVE, IDC_TRANSPARENCY_INACTIVE, IDC_TSLABEL_ACTIVE, IDC_TSLABEL_INACTIVE, 0, 0,0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_AVATARS, CTranslator::STR_LAST, IDC_O_STATIC_AVATAR, IDC_O_STATIC_OWNAVATAR, IDC_AVATARMODE, IDC_OWNAVATARMODE, IDC_AVATARSONTASKBAR, 0, 0, 0, 0, 0},
+ { CTranslator::CNT_OPT_TITLE_SOUNDS, CTranslator::STR_LAST, IDC_O_ENABLESOUNDS, IDC_O_SOUNDSMINIMIZED, IDC_O_SOUNDSUNFOCUSED, IDC_O_SOUNDSINACTIVE, IDC_O_SOUNDSFOCUSED, 0, 0, 0, 0, 0},
+};
+
+static void ShowPage(HWND hwndDlg, int iPage, BOOL fShow)
+{
+ if (iPage >= 0 && iPage < NR_O_PAGES) {
+ for (int i = 0; i < NR_O_OPTIONSPERPAGE && o_pages[iPage].uIds[i] != 0; i++)
+ Utils::showDlgControl(hwndDlg, o_pages[iPage].uIds[i], fShow ? SW_SHOW : SW_HIDE);
+ }
+ if (fShow) {
+ SetDlgItemText(hwndDlg, IDC_TITLEBOX, CTranslator::get(o_pages[iPage].idTitle));
+ if (o_pages[iPage].idDesc != CTranslator::STR_LAST)
+ SetDlgItemText(hwndDlg, IDC_DESC, CTranslator::get(o_pages[iPage].idDesc));
+ else
+ SetDlgItemText(hwndDlg, IDC_DESC, _T(""));
+ }
+ Utils::showDlgControl(hwndDlg, IDC_O_EXPLAINGLOBALNOTIFY, (iPage == 3 && nen_options.bWindowCheck) ? SW_SHOW : SW_HIDE);
+
+#if !defined(__FEAT_EXP_AUTOSPLITTER)
+ if(iPage == 0)
+ Utils::showDlgControl(hwndDlg, IDC_AUTOSPLITTER, SW_HIDE);
+#endif
+ if(iPage == 8 && !IsWinVer7Plus())
+ Utils::showDlgControl(hwndDlg, IDC_AVATARSONTASKBAR, SW_HIDE);
+}
+
+INT_PTR CALLBACK DlgProcContainerOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TContainerData *pContainer = 0;
+ HWND hwndTree = GetDlgItem(hwndDlg, IDC_SECTIONTREE);
+ pContainer = (struct TContainerData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ wchar_t szNewTitle[128];
+ TContainerData *pContainer = 0;
+ int i, j;
+ TVINSERTSTRUCT tvis = {0};
+ HTREEITEM hItem;
+ int nr_layouts = 0;
+ const TSideBarLayout* sblayouts = CSideBar::getLayouts(nr_layouts);
+
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) lParam);
+ pContainer = (TContainerData *) lParam;
+ pContainer->hWndOptions = hwndDlg;
+ TranslateDialogDefault(hwndDlg);
+ SetWindowText(hwndDlg, CTranslator::get(CTranslator::CNT_OPT_TITLE));
+ mir_sntprintf(szNewTitle, SIZEOF(szNewTitle), CTranslator::get(CTranslator::CNT_OPT_HEADERBAR), !_tcscmp(pContainer->szName, _T("default")) ?
+ CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME) : pContainer->szName);
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, szNewTitle);
+ Utils::enableDlgControl(hwndDlg, IDC_O_HIDETITLE, CSkin::m_frameSkins ? FALSE : TRUE);
+ CheckDlgButton(hwndDlg, IDC_CNTPRIVATE, pContainer->settings->fPrivate ? BST_CHECKED : BST_UNCHECKED);
+
+ SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::get(CTranslator::CNT_OPT_TABSTOP));
+ SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::get(CTranslator::CNT_OPT_TABSBOTTOM));
+ SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::get(CTranslator::CNT_OPT_TABSLEFT));
+ SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::get(CTranslator::CNT_OPT_TABSRIGHT));
+
+ SendDlgItemMessage(hwndDlg, IDC_AVATARMODE, CB_INSERTSTRING, -1,
+ (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_GLOBALLY_ON));
+ SendDlgItemMessage(hwndDlg, IDC_AVATARMODE, CB_INSERTSTRING, -1,
+ (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_ON_IF_PRESENT));
+ SendDlgItemMessage(hwndDlg, IDC_AVATARMODE, CB_INSERTSTRING, -1,
+ (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_GLOBALLY_OFF));
+ SendDlgItemMessage(hwndDlg, IDC_AVATARMODE, CB_INSERTSTRING, -1,
+ (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_ON_ALWAYS_BOTTOM));
+
+ SendDlgItemMessage(hwndDlg, IDC_OWNAVATARMODE, CB_INSERTSTRING, -1,
+ (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_ON_IF_PRESENT));
+ SendDlgItemMessage(hwndDlg, IDC_OWNAVATARMODE, CB_INSERTSTRING, -1,
+ (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_DONT_SHOW));
+
+ for(i = 0; i < nr_layouts; i++)
+ SendDlgItemMessage(hwndDlg, IDC_SBARLAYOUT, CB_INSERTSTRING, -1, (LPARAM)TranslateTS(sblayouts[i].szName));
+
+ /* bits 24 - 31 of dwFlagsEx hold the side bar layout id */
+ SendDlgItemMessage(hwndDlg, IDC_SBARLAYOUT, CB_SETCURSEL, (WPARAM)((pContainer->settings->dwFlagsEx & 0xff000000) >> 24), 0);
+
+
+ SendMessage(hwndDlg, DM_SC_INITDIALOG, (WPARAM)0, (LPARAM)pContainer->settings);
+ SendDlgItemMessage(hwndDlg, IDC_TITLEFORMAT, EM_LIMITTEXT, TITLE_FORMATLEN - 1, 0);
+ SetDlgItemText(hwndDlg, IDC_TITLEFORMAT, pContainer->settings->szTitleFormat);
+ SetDlgItemText(hwndDlg, IDC_THEME, pContainer->szRelThemeFile);
+ for (i = 0; i < NR_O_PAGES; i++) {
+ tvis.hParent = NULL;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_TEXT | TVIF_PARAM;
+ tvis.item.pszText = const_cast<TCHAR *>(CTranslator::get(o_pages[i].idTitle));
+ tvis.item.lParam = i;
+ hItem = TreeView_InsertItem(hwndTree, &tvis);
+ if (i == 0)
+ SendMessage(hwndTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem);
+ for (j = 0; j < NR_O_OPTIONSPERPAGE && o_pages[i].uIds[j] != 0; j++)
+ Utils::showDlgControl(hwndDlg, o_pages[i].uIds[j], SW_HIDE);
+ ShowPage(hwndDlg, i, FALSE);
+ }
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_MESSAGE));
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE));
+ ShowPage(hwndDlg, 0, TRUE);
+ SetFocus(hwndTree);
+ Utils::enableDlgControl(hwndDlg, IDC_APPLY, FALSE);
+
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_DESC, WM_GETFONT, 0, 0);
+ LOGFONT lf = {0};
+
+ GetObject(hFont, sizeof(lf), &lf);
+ lf.lfHeight = (int)(lf.lfHeight * 1.2);
+ hFont = CreateFontIndirect(&lf);
+
+ SendDlgItemMessage(hwndDlg, IDC_TITLEBOX, WM_SETFONT, (WPARAM)hFont, TRUE);
+
+ if(pContainer->isCloned && pContainer->hContactFrom != 0) {
+ Utils::showDlgControl(hwndDlg, IDC_CNTPRIVATE, SW_HIDE);
+ Utils::showDlgControl(hwndDlg, IDC_O_CNTPRIVATE, SW_HIDE);
+ }
+ return FALSE;
+ }
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ {
+ HWND hwndChild = (HWND)lParam;
+ UINT id = GetDlgCtrlID(hwndChild);
+
+ if(hwndChild == GetDlgItem(hwndDlg, IDC_TITLEBOX)) {
+ ::SetTextColor((HDC)wParam, RGB(60, 60, 150));
+ } else if(hwndChild == GetDlgItem(hwndDlg, IDC_DESC))
+ ::SetTextColor((HDC)wParam, RGB(160, 50, 50));
+ if(id == IDC_TSLABEL_REOPENWARN)
+ break;
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+ }
+
+ case WM_NOTIFY:
+ if (wParam == IDC_SECTIONTREE && ((LPNMHDR)lParam)->code == TVN_SELCHANGED) {
+ NMTREEVIEW *pmtv = (NMTREEVIEW *)lParam;
+
+ ShowPage(hwndDlg, pmtv->itemOld.lParam, 0);
+ ShowPage(hwndDlg, pmtv->itemNew.lParam, 1);
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_CNTPRIVATE: {
+
+ if(IsDlgButtonChecked(hwndDlg, IDC_CNTPRIVATE)) {
+ Utils::ReadPrivateContainerSettings(pContainer, true);
+ pContainer->settings->fPrivate = true;
+ }
+ else {
+ if(pContainer->settings != &PluginConfig.globalContainerSettings) {
+ char szCname[40];
+ mir_snprintf(szCname, 40, "%s%d_Blob", CNT_BASEKEYNAME, pContainer->iContainerIndex);
+ pContainer->settings->fPrivate = false;
+ DBWriteContactSettingBlob(0, SRMSGMOD_T, szCname, pContainer->settings, sizeof(TContainerSettings));
+ free(pContainer->settings);
+ }
+ pContainer->settings = &PluginConfig.globalContainerSettings;
+ pContainer->settings->fPrivate = false;
+ }
+ SendMessage(hwndDlg, DM_SC_INITDIALOG, 0, (LPARAM)pContainer->settings);
+ goto do_apply;
+ }
+ case IDC_TRANSPARENCY: {
+ int isTrans = IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENCY);
+
+ Utils::enableDlgControl(hwndDlg, IDC_TRANSPARENCY_ACTIVE, isTrans ? TRUE : FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_TRANSPARENCY_INACTIVE, isTrans ? TRUE : FALSE);
+ goto do_apply;
+ }
+ case IDC_SECTIONTREE:
+ case IDC_DESC:
+ return 0;
+ case IDC_SAVESIZEASGLOBAL: {
+ WINDOWPLACEMENT wp = {0};
+
+ wp.length = sizeof(wp);
+ if (GetWindowPlacement(pContainer->hwnd, &wp)) {
+ M->WriteDword(SRMSGMOD_T, "splitx", wp.rcNormalPosition.left);
+ M->WriteDword(SRMSGMOD_T, "splity", wp.rcNormalPosition.top);
+ M->WriteDword(SRMSGMOD_T, "splitwidth", wp.rcNormalPosition.right - wp.rcNormalPosition.left);
+ M->WriteDword(SRMSGMOD_T, "splitheight", wp.rcNormalPosition.bottom - wp.rcNormalPosition.top);
+ }
+ break;
+ }
+ case IDC_O_ENABLESOUNDS:
+ SendMessage(hwndDlg, DM_SC_CONFIG, 0, 0);
+ break;
+ case IDC_TITLEFORMAT:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ goto do_apply;
+ case IDC_SELECTTHEME: {
+ const wchar_t *szFileName = GetThemeFileName(0);
+
+ if (PathFileExists(szFileName)) {
+ SetDlgItemText(hwndDlg, IDC_THEME, szFileName);
+ goto do_apply;
+ }
+ break;
+ }
+
+ case IDC_O_HELP_TITLEFORMAT:
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://miranda.or.at/TabSRMM/TitleBarFormatting");
+ break;
+
+ case IDOK:
+ case IDC_APPLY: {
+
+ SendMessage(hwndDlg, DM_SC_BUILDLIST, 0, (LPARAM)pContainer->settings);
+
+ pContainer->settings->dwTransparency = MAKELONG((WORD)SendDlgItemMessage(hwndDlg, IDC_TRANSPARENCY_ACTIVE, TBM_GETPOS, 0, 0),
+ (WORD)SendDlgItemMessage(hwndDlg, IDC_TRANSPARENCY_INACTIVE, TBM_GETPOS, 0, 0));
+
+ pContainer->settings->avatarMode = (WORD)SendDlgItemMessage(hwndDlg, IDC_AVATARMODE, CB_GETCURSEL, 0, 0);
+ pContainer->settings->ownAvatarMode = (WORD)SendDlgItemMessage(hwndDlg, IDC_OWNAVATARMODE, CB_GETCURSEL, 0, 0);
+
+ GetDlgItemText(hwndDlg, IDC_TITLEFORMAT, pContainer->settings->szTitleFormat, TITLE_FORMATLEN);
+ pContainer->settings->szTitleFormat[TITLE_FORMATLEN - 1] = 0;
+
+ pContainer->szRelThemeFile[0] = pContainer->szAbsThemeFile[0] = 0;
+
+ if (GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_THEME)) > 0) {
+ wchar_t szFinalThemeFile[MAX_PATH], szFilename[MAX_PATH];
+
+ GetDlgItemText(hwndDlg, IDC_THEME, szFilename, MAX_PATH);
+ szFilename[MAX_PATH - 1] = 0;
+ M->pathToAbsolute(szFilename, szFinalThemeFile);
+
+ if(_tcscmp(szFilename, pContainer->szRelThemeFile))
+ pContainer->fPrivateThemeChanged = TRUE;
+
+ if (PathFileExists(szFinalThemeFile))
+ mir_sntprintf(pContainer->szRelThemeFile, MAX_PATH, _T("%s"), szFilename);
+ else
+ pContainer->szRelThemeFile[0] = 0;
+ }
+ else {
+ pContainer->szRelThemeFile[0] = 0;
+ pContainer->fPrivateThemeChanged = TRUE;
+ }
+
+ Utils::SettingsToContainer(pContainer);
+
+ if (!IsDlgButtonChecked(hwndDlg, IDC_CNTPRIVATE)) {
+ ReloadGlobalContainerSettings(true);
+ ::DBWriteContactSettingBlob(0, SRMSGMOD_T, CNT_KEYNAME, &PluginConfig.globalContainerSettings, sizeof(TContainerSettings));
+ }
+ else {
+ char *szSetting = "CNTW_";
+ Utils::SaveContainerSettings(pContainer, szSetting);
+ }
+
+ SendMessage(pContainer->hwnd, DM_CONFIGURECONTAINER, 0, 0);
+ BroadCastContainer(pContainer, DM_SETINFOPANEL, 0, 0);
+
+ ShowWindow(pContainer->hwnd, SW_HIDE);
+ {
+ RECT rc;
+
+ GetWindowRect(pContainer->hwnd, &rc);
+ SetWindowPos(pContainer->hwnd, 0, rc.left, rc.top, (rc.right - rc.left) - 1, (rc.bottom - rc.top) - 1, SWP_NOZORDER | SWP_DRAWFRAME | SWP_FRAMECHANGED);
+ SetWindowPos(pContainer->hwnd, 0, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_NOZORDER | SWP_DRAWFRAME | SWP_SHOWWINDOW);
+ }
+
+ if (LOWORD(wParam) == IDOK)
+ DestroyWindow(hwndDlg);
+ else
+ Utils::enableDlgControl(hwndDlg, IDC_APPLY, FALSE);
+
+ break;
+ }
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ default:
+do_apply:
+ Utils::enableDlgControl(hwndDlg, IDC_APPLY, TRUE);
+ break;
+ }
+ break;
+ case DM_SC_INITDIALOG: {
+ TContainerSettings* cs = (TContainerSettings *)lParam;
+
+ DWORD dwFlags = cs->dwFlags;
+ DWORD dwTransparency = cs->dwTransparency;
+ DWORD dwFlagsEx = cs->dwFlagsEx;
+ int isTrans;
+ BOOL fAllowTrans = FALSE;
+
+ if(PluginConfig.m_WinVerMajor >= 6)
+ fAllowTrans = TRUE;
+ else
+ fAllowTrans = (!CSkin::m_skinEnabled);
+
+ MY_CheckDlgButton(hwndDlg, IDC_O_HIDETITLE, dwFlags & CNT_NOTITLE);
+ MY_CheckDlgButton(hwndDlg, IDC_O_DONTREPORT, dwFlags & CNT_DONTREPORT);
+ MY_CheckDlgButton(hwndDlg, IDC_O_NOTABS, dwFlags & CNT_HIDETABS);
+ MY_CheckDlgButton(hwndDlg, IDC_O_STICKY, dwFlags & CNT_STICKY);
+ MY_CheckDlgButton(hwndDlg, IDC_O_FLASHNEVER, dwFlags & CNT_NOFLASH);
+ MY_CheckDlgButton(hwndDlg, IDC_O_FLASHALWAYS, dwFlags & CNT_FLASHALWAYS);
+ MY_CheckDlgButton(hwndDlg, IDC_O_FLASHDEFAULT, !((dwFlags & CNT_NOFLASH) || (dwFlags & CNT_FLASHALWAYS)));
+ MY_CheckDlgButton(hwndDlg, IDC_TRANSPARENCY, dwFlags & CNT_TRANSPARENCY);
+ MY_CheckDlgButton(hwndDlg, IDC_DONTREPORTUNFOCUSED2, dwFlags & CNT_DONTREPORTUNFOCUSED);
+ MY_CheckDlgButton(hwndDlg, IDC_DONTREPORTFOCUSED2, dwFlags & CNT_DONTREPORTFOCUSED);
+ MY_CheckDlgButton(hwndDlg, IDC_ALWAYSPOPUPSINACTIVE, dwFlags & CNT_ALWAYSREPORTINACTIVE);
+ MY_CheckDlgButton(hwndDlg, IDC_CNTNOSTATUSBAR, dwFlags & CNT_NOSTATUSBAR);
+ MY_CheckDlgButton(hwndDlg, IDC_HIDEMENUBAR, dwFlags & CNT_NOMENUBAR);
+ MY_CheckDlgButton(hwndDlg, IDC_HIDETOOLBAR, dwFlags & CNT_HIDETOOLBAR);
+ MY_CheckDlgButton(hwndDlg, IDC_BOTTOMTOOLBAR, dwFlags & CNT_BOTTOMTOOLBAR);
+ MY_CheckDlgButton(hwndDlg, IDC_UIDSTATUSBAR, dwFlags & CNT_UINSTATUSBAR);
+ MY_CheckDlgButton(hwndDlg, IDC_VERTICALMAX, dwFlags & CNT_VERTICALMAX);
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ MY_CheckDlgButton(hwndDlg, IDC_AUTOSPLITTER, dwFlags & CNT_AUTOSPLITTER);
+#endif
+ MY_CheckDlgButton(hwndDlg, IDC_AVATARSONTASKBAR, dwFlags & CNT_AVATARSONTASKBAR);
+ MY_CheckDlgButton(hwndDlg, IDC_INFOPANEL, dwFlags & CNT_INFOPANEL);
+ MY_CheckDlgButton(hwndDlg, IDC_USEGLOBALSIZE, dwFlags & CNT_GLOBALSIZE);
+
+ MY_CheckDlgButton(hwndDlg, IDC_FLASHICON, dwFlagsEx & TCF_FLASHICON);
+ MY_CheckDlgButton(hwndDlg, IDC_FLASHLABEL, dwFlagsEx & TCF_FLASHLABEL);
+ MY_CheckDlgButton(hwndDlg, IDC_CLOSEBUTTONONTABS, dwFlagsEx & TCF_CLOSEBUTTON);
+
+ MY_CheckDlgButton(hwndDlg, IDC_SINGLEROWTAB, dwFlagsEx & TCF_SINGLEROWTABCONTROL);
+ MY_CheckDlgButton(hwndDlg, IDC_BUTTONTABS, dwFlagsEx & TCF_FLAT);
+
+ MY_CheckDlgButton(hwndDlg, IDC_O_ENABLESOUNDS, !(dwFlags & CNT_NOSOUND));
+ MY_CheckDlgButton(hwndDlg, IDC_O_SOUNDSMINIMIZED, dwFlagsEx & CNT_EX_SOUNDS_MINIMIZED);
+ MY_CheckDlgButton(hwndDlg, IDC_O_SOUNDSUNFOCUSED, dwFlagsEx & CNT_EX_SOUNDS_UNFOCUSED);
+ MY_CheckDlgButton(hwndDlg, IDC_O_SOUNDSINACTIVE, dwFlagsEx & CNT_EX_SOUNDS_INACTIVETABS);
+ MY_CheckDlgButton(hwndDlg, IDC_O_SOUNDSFOCUSED, dwFlagsEx & CNT_EX_SOUNDS_FOCUSED);
+
+ SendMessage(hwndDlg, DM_SC_CONFIG, 0, 0);
+
+ if(!(dwFlagsEx & (TCF_SBARLEFT | TCF_SBARRIGHT)))
+ SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_SETCURSEL, dwFlags & CNT_TABSBOTTOM ? 1 : 0, 0);
+ else
+ SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_SETCURSEL, dwFlagsEx & TCF_SBARLEFT ? 2 : 3, 0);
+
+ if (LOBYTE(LOWORD(GetVersion())) >= 5 && fAllowTrans)
+ CheckDlgButton(hwndDlg, IDC_TRANSPARENCY, dwFlags & CNT_TRANSPARENCY);
+ else
+ CheckDlgButton(hwndDlg, IDC_TRANSPARENCY, FALSE);
+
+ Utils::enableDlgControl(hwndDlg, IDC_TRANSPARENCY, PluginConfig.m_WinVerMajor >= 5 && fAllowTrans ? TRUE : FALSE);
+
+ isTrans = IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENCY);
+ Utils::enableDlgControl(hwndDlg, IDC_TRANSPARENCY_ACTIVE, isTrans ? TRUE : FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_TRANSPARENCY_INACTIVE, isTrans ? TRUE : FALSE);
+
+ SendDlgItemMessage(hwndDlg, IDC_TRANSPARENCY_ACTIVE, TBM_SETRANGE, 0, (LPARAM)MAKELONG(50, 255));
+ SendDlgItemMessage(hwndDlg, IDC_TRANSPARENCY_INACTIVE, TBM_SETRANGE, 0, (LPARAM)MAKELONG(50, 255));
+
+ SendDlgItemMessage(hwndDlg, IDC_TRANSPARENCY_ACTIVE, TBM_SETPOS, TRUE, (LPARAM) LOWORD(dwTransparency));
+ SendDlgItemMessage(hwndDlg, IDC_TRANSPARENCY_INACTIVE, TBM_SETPOS, TRUE, (LPARAM) HIWORD(dwTransparency));
+
+ Utils::enableDlgControl(hwndDlg, IDC_O_DONTREPORT, nen_options.bWindowCheck == 0);
+ Utils::enableDlgControl(hwndDlg, IDC_DONTREPORTUNFOCUSED2, nen_options.bWindowCheck == 0);
+ Utils::enableDlgControl(hwndDlg, IDC_DONTREPORTFOCUSED2, nen_options.bWindowCheck == 0);
+ Utils::enableDlgControl(hwndDlg, IDC_ALWAYSPOPUPSINACTIVE, nen_options.bWindowCheck == 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_AVATARMODE, CB_SETCURSEL, (WPARAM)cs->avatarMode, 0);
+ SendDlgItemMessage(hwndDlg, IDC_OWNAVATARMODE, CB_SETCURSEL, (WPARAM)cs->ownAvatarMode, 0);
+
+ Utils::showDlgControl(hwndDlg, IDC_O_EXPLAINGLOBALNOTIFY, nen_options.bWindowCheck ? SW_SHOW : SW_HIDE);
+
+ SendDlgItemMessage(hwndDlg, IDC_AUTOCLOSETABSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 0));
+ SendDlgItemMessage(hwndDlg, IDC_AUTOCLOSETABSPIN, UDM_SETPOS, 0, (LPARAM)cs->autoCloseSeconds);
+ break;
+ }
+
+ case DM_SC_CONFIG: {
+ LRESULT enable = (IsDlgButtonChecked(hwndDlg, IDC_O_ENABLESOUNDS) ? BST_CHECKED : BST_UNCHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_O_SOUNDSINACTIVE, enable);
+ Utils::enableDlgControl(hwndDlg, IDC_O_SOUNDSUNFOCUSED, enable);
+ Utils::enableDlgControl(hwndDlg, IDC_O_SOUNDSMINIMIZED, enable);
+ Utils::enableDlgControl(hwndDlg, IDC_O_SOUNDSFOCUSED, enable);
+ return(0);
+ }
+
+ case DM_SC_BUILDLIST: {
+ DWORD dwNewFlags = 0, dwNewFlagsEx = 0;
+ TContainerSettings* cs = (TContainerSettings *)lParam;
+
+ dwNewFlags = (IsDlgButtonChecked(hwndDlg, IDC_O_HIDETITLE) ? CNT_NOTITLE : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_DONTREPORT) ? CNT_DONTREPORT : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_NOTABS) ? CNT_HIDETABS : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_STICKY) ? CNT_STICKY : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_FLASHALWAYS) ? CNT_FLASHALWAYS : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_FLASHNEVER) ? CNT_NOFLASH : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_TRANSPARENCY) ? CNT_TRANSPARENCY : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_DONTREPORTUNFOCUSED2) ? CNT_DONTREPORTUNFOCUSED : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_DONTREPORTFOCUSED2) ? CNT_DONTREPORTFOCUSED : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_ALWAYSPOPUPSINACTIVE) ? CNT_ALWAYSREPORTINACTIVE : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_CNTNOSTATUSBAR) ? CNT_NOSTATUSBAR : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_HIDEMENUBAR) ? CNT_NOMENUBAR : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_HIDETOOLBAR) ? CNT_HIDETOOLBAR : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_BOTTOMTOOLBAR) ? CNT_BOTTOMTOOLBAR : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_UIDSTATUSBAR) ? CNT_UINSTATUSBAR : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_USEGLOBALSIZE) ? CNT_GLOBALSIZE : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_INFOPANEL) ? CNT_INFOPANEL : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_ENABLESOUNDS) ? 0 : CNT_NOSOUND) |
+ (IsDlgButtonChecked(hwndDlg, IDC_AVATARSONTASKBAR) ? CNT_AVATARSONTASKBAR : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_VERTICALMAX) ? CNT_VERTICALMAX : 0) |
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ (IsDlgButtonChecked(hwndDlg, IDC_AUTOSPLITTER) ? CNT_AUTOSPLITTER : 0) |
+#endif
+ (CNT_NEWCONTAINERFLAGS);
+
+ LRESULT iTabMode = SendDlgItemMessage(hwndDlg, IDC_TABMODE, CB_GETCURSEL, 0, 0);
+ LRESULT iTabLayout = SendDlgItemMessage(hwndDlg, IDC_SBARLAYOUT, CB_GETCURSEL, 0, 0);
+
+ dwNewFlagsEx = 0;
+
+ if(iTabMode < 2)
+ dwNewFlags = ((iTabMode == 1) ? (dwNewFlags | CNT_TABSBOTTOM) : (dwNewFlags & ~CNT_TABSBOTTOM));
+ else {
+ dwNewFlags &= ~CNT_TABSBOTTOM;
+ dwNewFlagsEx = iTabMode == 2 ? TCF_SBARLEFT : TCF_SBARRIGHT;
+ }
+
+ dwNewFlagsEx |= ((IsDlgButtonChecked(hwndDlg, IDC_FLASHICON) ? TCF_FLASHICON : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_FLASHLABEL) ? TCF_FLASHLABEL : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_BUTTONTABS) ? TCF_FLAT : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_CLOSEBUTTONONTABS) ? TCF_CLOSEBUTTON : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_SINGLEROWTAB) ? TCF_SINGLEROWTABCONTROL : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_SOUNDSMINIMIZED) ? CNT_EX_SOUNDS_MINIMIZED : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_SOUNDSUNFOCUSED) ? CNT_EX_SOUNDS_UNFOCUSED : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_SOUNDSFOCUSED) ? CNT_EX_SOUNDS_FOCUSED : 0) |
+ (IsDlgButtonChecked(hwndDlg, IDC_O_SOUNDSINACTIVE) ? CNT_EX_SOUNDS_INACTIVETABS : 0)
+ );
+
+ /* bits 24 - 31 of dwFlagsEx hold the sidebar layout id */
+ dwNewFlagsEx |= ((int)((iTabLayout << 24) & 0xff000000));
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_O_FLASHDEFAULT))
+ dwNewFlags &= ~(CNT_FLASHALWAYS | CNT_NOFLASH);
+
+ cs->dwFlags = dwNewFlags;
+ cs->dwFlagsEx = dwNewFlagsEx;
+ cs->autoCloseSeconds = (WORD)SendDlgItemMessage(hwndDlg, IDC_AUTOCLOSETABSPIN, UDM_GETPOS, 0, 0);
+ break;
+ }
+ case WM_DESTROY: {
+ pContainer->hWndOptions = 0;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+
+ HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_TITLEBOX, WM_GETFONT, 0, 0);
+ DeleteObject(hFont);
+ break;
+ }
+ }
+ return FALSE;
+}
diff --git a/plugins/TabSRMM/src/controls.cpp b/plugins/TabSRMM/src/controls.cpp new file mode 100644 index 0000000000..f524de5f1a --- /dev/null +++ b/plugins/TabSRMM/src/controls.cpp @@ -0,0 +1,1157 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: controls.cpp 13587 2011-04-12 13:54:26Z george.hazan $
+ *
+ * Menu and status bar control(s) for the container window.
+ *
+ */
+
+#include "commonheaders.h"
+static WNDPROC OldStatusBarproc = 0;
+
+extern int status_icon_list_size;
+extern TStatusBarIconNode *status_icon_list;
+
+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;
+
+CMenuBar::CMenuBar(HWND hwndParent, const TContainerData *pContainer)
+{
+ m_pContainer = const_cast<TContainerData *>(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<HBITMAP>(::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);
+
+ if(m_buttonsInit == false) {
+ ::ZeroMemory(m_TbButtons, sizeof(m_TbButtons));
+
+ m_TbButtons[0].iBitmap = 0;//I_IMAGENONE;
+ m_TbButtons[0].iString = 0;//(INT_PTR)TranslateT("&Main");
+ 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)CTranslator::get(CTranslator::GEN_MENUBAR_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<DWORD_PTR>(::GetSubMenu(PluginConfig.getMenuBar(), 0));
+
+ m_TbButtons[2].iBitmap = I_IMAGENONE;
+ m_TbButtons[2].iString = (INT_PTR)CTranslator::get(CTranslator::GEN_MENUBAR_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<DWORD_PTR>(::GetSubMenu(PluginConfig.getMenuBar(), 1));
+
+ m_TbButtons[3].iBitmap = I_IMAGENONE;
+ m_TbButtons[3].iString = (INT_PTR)CTranslator::get(CTranslator::GEN_MENUBAR_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)CTranslator::get(CTranslator::GEN_MENUBAR_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)CTranslator::get(CTranslator::GEN_MENUBAR_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<DWORD_PTR>(::GetSubMenu(PluginConfig.getMenuBar(), 2));
+
+ m_TbButtons[6].iBitmap = I_IMAGENONE;
+ m_TbButtons[6].iString = (INT_PTR)CTranslator::get(CTranslator::GEN_MENUBAR_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<DWORD_PTR>(::GetSubMenu(PluginConfig.getMenuBar(), 3));
+
+ m_TbButtons[7].iBitmap = I_IMAGENONE;
+ m_TbButtons[7].iString = (INT_PTR)CTranslator::get(CTranslator::GEN_MENUBAR_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<DWORD_PTR>(::GetSubMenu(PluginConfig.getMenuBar(), 4));
+
+ m_buttonsInit = true;
+ }
+
+ ::SendMessage(m_hwndToolbar, TB_ADDBUTTONS, sizeof(m_TbButtons)/sizeof(TBBUTTON), (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);
+
+ m_activeMenu = 0;
+ m_activeID = 0;
+ m_isAero = M->isAero();
+ m_mustAutoHide = false;
+ m_activeSubMenu = 0;
+ m_fTracking = false;
+ m_isContactMenu = m_isMainMenu = false;
+
+ m_oldWndProc = (WNDPROC)::GetWindowLongPtr(m_hwndToolbar, GWLP_WNDPROC);
+ ::SetWindowLongPtr(m_hwndToolbar, GWLP_USERDATA, (UINT_PTR)this);
+ ::SetWindowLongPtr(m_hwndToolbar, GWLP_WNDPROC, (UINT_PTR)wndProc);
+}
+
+CMenuBar::~CMenuBar()
+{
+ ::SetWindowLongPtr(m_hwndToolbar, GWLP_WNDPROC, (UINT_PTR)m_oldWndProc);
+ ::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, g_hInst, 0);
+ 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;
+
+ LRESULT result = Handle(mtb);
+ return(result);
+ }
+ 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<CMenuBar *>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
+
+ switch(msg) {
+ case WM_SYSKEYUP: {
+ if(wParam == VK_MENU) {
+ menuBar->Cancel();
+ return(0);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return(::CallWindowProc(menuBar->m_oldWndProc, hWnd, 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<HBITMAP>(::SelectObject(m_hdcDraw, m_hbmDraw));
+ m_hTheme = M->isAero() || M->isVSThemed() ? CMimAPI::m_pfnOpenThemeData(m_hwndToolbar, L"REBAR") : 0;
+ m_hOldFont = reinterpret_cast<HFONT>(::SelectObject(m_hdcDraw, reinterpret_cast<HFONT>(::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())
+ M->m_pfnDrawThemeBackground(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);
+ }
+ else {
+ 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 = reinterpret_cast<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)
+ CMimAPI::m_pfnCloseThemeData(m_hTheme);
+ return(CDRF_SKIPDEFAULT);
+ }
+ else
+ return(CDRF_DODEFAULT);
+
+ default:
+ return(CDRF_DODEFAULT);
+ }
+
+ return(0);
+ }
+ 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);
+
+ HANDLE hContact = dat ? dat->hContact : 0;
+
+ if(index == 3 && hContact != 0) {
+ hMenu = reinterpret_cast<HMENU>(::CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM)hContact, 0));
+ m_isContactMenu = true;
+ } else if(index == 0) {
+ hMenu = reinterpret_cast<HMENU>(::CallService(MS_CLIST_MENUBUILDMAIN, 0, 0));
+ m_isMainMenu = true;
+ } else
+ hMenu = reinterpret_cast<HMENU>(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();
+}
+
+/**
+ * Cancel menu tracking completely
+ */
+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->showPic ? 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
+{
+ TWindowData *dat = (TWindowData *)::GetWindowLongPtr(m_pContainer->hwndActive, GWLP_USERDATA);
+
+ BOOL fDisable = FALSE;
+
+ 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;
+ //obtainHook();
+ ::SetFocus(m_hwndToolbar);
+ //::SendMessage(m_hwndToolbar, TB_SETHOTITEM, 0, HICF_ACCELERATOR);
+}
+
+/**
+ * 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
+ *
+ * @return
+ */
+LRESULT CALLBACK CMenuBar::MessageHook(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ if (nCode < 0)
+ return(::CallNextHookEx(m_hHook, nCode, wParam, lParam));
+
+ MSG *pMsg = reinterpret_cast<MSG *>(lParam);
+ bool fCancel = false;
+
+ if(nCode == MSGF_MENU) {
+ switch(pMsg->message) {
+ case WM_KEYDOWN:
+ switch(pMsg->wParam) {
+ case VK_ESCAPE: {
+ fCancel = true;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case WM_SYSKEYUP:
+ if(pMsg->wParam == VK_MENU)
+ fCancel = true;
+ break;
+
+ case WM_LBUTTONDOWN: {
+ POINT pt;
+
+ ::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: {
+ POINT pt;
+
+ ::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;
+ }
+ default:
+ 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);
+
+ if (OldStatusBarproc == 0) {
+ WNDCLASSEX wc = {0};
+ wc.cbSize = sizeof(wc);
+ GetClassInfoEx(g_hInst, STATUSCLASSNAME, &wc);
+ OldStatusBarproc = wc.lpfnWndProc;
+ }
+ switch (msg) {
+ case WM_CREATE: {
+ CREATESTRUCT *cs = (CREATESTRUCT *)lParam;
+ LRESULT ret;
+ 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;
+ POINT pt;
+ 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;
+ TCHAR szText[1024];
+ int i;
+ RECT itemRect;
+ HICON hIcon;
+ LONG height, width;
+ HDC hdc = BeginPaint(hWnd, &ps);
+ HFONT hFontOld = 0;
+ UINT nParts = SendMessage(hWnd, SB_GETPARTS, 0, 0);
+ LRESULT result;
+ RECT rcClient;
+ HDC hdcMem;
+ HBITMAP hbm, hbmOld;
+ HANDLE hbp = 0;
+ CSkinItem * item = &SkinItems[ID_EXTBKSTATUSBARPANEL];
+ COLORREF clr = 0;
+
+ BOOL fAero = M->isAero();
+ HANDLE hTheme = fAero ? CMimAPI::m_pfnOpenThemeData(hWnd, L"ButtonStyle") : 0;
+ TWindowData* dat = 0;
+
+ if(pContainer)
+ dat = (TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+
+ GetClientRect(hWnd, &rcClient);
+
+ 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);
+
+ clr = CSkin::m_skinEnabled ? CSkin::m_DefaultFontColor : (PluginConfig.m_fillColor ? PluginConfig.m_genericTxtColor : GetSysColor(COLOR_BTNTEXT));
+
+ hFontOld = (HFONT)SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT));
+
+ if (pContainer && CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(hWnd, GetParent(hWnd), pContainer, &rcClient, hdcMem);
+ else if(fAero) {
+ 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 (i = 0; i < (int)nParts; i++) {
+ SendMessage(hWnd, SB_GETRECT, (WPARAM)i, (LPARAM)&itemRect);
+ if (!item->IGNORED && !fAero && 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);
+ }
+ }
+ DeleteObject(br);
+ }
+ }
+
+ height = itemRect.bottom - itemRect.top;
+ width = itemRect.right - itemRect.left;
+ hIcon = (HICON)SendMessage(hWnd, SB_GETICON, i, 0);
+ szText[0] = 0;
+ 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->showTyping == 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)
+ CMimAPI::m_pfnCloseThemeData(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: {
+ struct TWindowData *dat = (struct TWindowData *)lParam;
+ RECT rcs;
+ int statwidths[5];
+ struct TStatusBarIconNode *current = status_icon_list;
+ int list_icons = 0;
+ char buff[100];
+ DWORD flags;
+
+ while (current && dat) {
+ flags=current->sid.flags;
+ if(current->sid.flags&MBF_OWNERSTATE){
+ struct TStatusBarIconNode *currentSIN = dat->pSINod;
+
+ while (currentSIN) {
+ if (strcmp(currentSIN->sid.szModule, current->sid.szModule) == 0 && currentSIN->sid.dwId == current->sid.dwId) {
+ flags=currentSIN->sid.flags;
+ break;
+ }
+ currentSIN = currentSIN->next;
+ }
+ }
+ else {
+ sprintf(buff, "SRMMStatusIconFlags%d", (int)current->sid.dwId);
+ flags = M->GetByte(dat->hContact, current->sid.szModule, buff, current->sid.flags);
+ }
+ if (!(flags & MBF_HIDDEN))
+ list_icons++;
+ current = current->next;
+ }
+
+ SendMessage(hWnd, WM_SIZE, 0, 0);
+ GetWindowRect(hWnd, &rcs);
+
+ statwidths[0] = (rcs.right - rcs.left) - (2 * SB_CHAR_WIDTH + 20) - (52 + ((list_icons) * (PluginConfig.m_smcxicon + 2)));
+ statwidths[1] = (rcs.right - rcs.left) - (62 + ((list_icons) * (PluginConfig.m_smcxicon + 2)));
+ statwidths[2] = -1;
+ SendMessage(hWnd, SB_SETPARTS, 3, (LPARAM) statwidths);
+ return 0;
+ }
+
+ case WM_SETCURSOR: {
+ POINT pt;
+
+ 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: {
+ POINT pt;
+
+ 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;
+ RECT rcIconpart;
+
+ ScreenToClient(hWnd, &pt1);
+ 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) {
+ POINT pt;
+ char *szTTService = "mToolTip/ShowTipW";
+ CLCINFOTIP ti = {0};
+ ti.cbSize = sizeof(ti);
+
+ KillTimer(hWnd, TIMERID_HOVER);
+ GetCursorPos(&pt);
+ if (pt.x == ptMouse.x && pt.y == ptMouse.y) {
+ RECT rc;
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+//mad
+ SIZE size;
+ TCHAR szStatusBarText[512];
+//mad_
+ ti.ptCursor = pt;
+ ScreenToClient(hWnd, &pt);
+ SendMessage(hWnd, SB_GETRECT, 2, (LPARAM)&rc);
+ if (dat && PtInRect(&rc, pt)) {
+ int gap = 2;
+ TStatusBarIconNode *current = status_icon_list;
+ TStatusBarIconNode *clicked = NULL;
+ TStatusBarIconNode *currentSIN = NULL;
+
+ unsigned int iconNum = (pt.x - rc.left) / (PluginConfig.m_smcxicon + gap);
+ unsigned int list_icons = 0;
+ char buff[100];
+ DWORD flags;
+
+ while (current) {
+ if(current->sid.flags&MBF_OWNERSTATE&&dat->pSINod){
+ TStatusBarIconNode *currentSIN = dat->pSINod;
+ flags=current->sid.flags;
+ while (currentSIN) {
+ if (strcmp(currentSIN->sid.szModule, current->sid.szModule) == 0 && currentSIN->sid.dwId == current->sid.dwId) {
+ flags=currentSIN->sid.flags;
+ break;
+ }
+ currentSIN = currentSIN->next;
+ }
+ }
+ else {
+ sprintf(buff, "SRMMStatusIconFlags%d", (int)current->sid.dwId);
+ flags = M->GetByte(dat->hContact, current->sid.szModule, buff, current->sid.flags);
+ }
+ if (!(flags & MBF_HIDDEN)) {
+ if (list_icons++ == iconNum)
+ clicked = current;
+ }
+ current = current->next;
+ }
+
+ if(clicked&&clicked->sid.flags&MBF_OWNERSTATE) {
+ currentSIN=dat->pSINod;
+ while (currentSIN) {
+ if (strcmp(currentSIN->sid.szModule, clicked->sid.szModule) == 0 && currentSIN->sid.dwId == clicked->sid.dwId) {
+ clicked=currentSIN;
+ break;
+ }
+ currentSIN = currentSIN->next;
+ }
+ }
+
+ if ((int)iconNum == list_icons && pContainer) {
+ TCHAR wBuf[512];
+
+ mir_sntprintf(wBuf, safe_sizeof(wBuf), CTranslator::get(CTranslator::CNT_SBAR_SOUNDS),
+ pContainer->dwFlags & CNT_NOSOUND ? CTranslator::get(CTranslator::GEN_DISABLED) : CTranslator::get(CTranslator::GEN_ENABLED));
+ CallService(szTTService, (WPARAM)wBuf, (LPARAM)&ti);
+ tooltip_active = TRUE;
+ }
+ else if ((int)iconNum == list_icons + 1 && dat && dat->bType == SESSIONTYPE_IM) {
+ int mtnStatus = (int)M->GetByte(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, M->GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW));
+ TCHAR wBuf[512];
+
+ mir_sntprintf(wBuf, safe_sizeof(wBuf), CTranslator::get(CTranslator::CNT_SBAR_MTN),
+ mtnStatus ? CTranslator::get(CTranslator::GEN_ENABLED) : CTranslator::get(CTranslator::GEN_DISABLED));
+ CallService(szTTService, (WPARAM)wBuf, (LPARAM)&ti);
+ tooltip_active = TRUE;
+ }
+ else if((int)iconNum == list_icons + 2) {
+ TCHAR wBuf[512];
+
+ mir_sntprintf(wBuf, safe_sizeof(wBuf), _T("%s"), CTranslator::get(CTranslator::CNT_SBAR_SLIST));
+
+ CallService(szTTService, (WPARAM)wBuf, (LPARAM)&ti);
+ tooltip_active = TRUE;
+ }
+ else {
+ if (clicked) {
+ CallService("mToolTip/ShowTip", (WPARAM)clicked->sid.szTooltip, (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 = M->GetDword(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) & gtxl, 0);
+ tooltip_active = TRUE;
+
+ TCHAR wBuf[512];
+ const TCHAR *szFormat = CTranslator::get(CTranslator::GEN_SBAR_TIP_MSGLENGTH);
+
+ mir_sntprintf(wBuf, safe_sizeof(wBuf), szFormat, dat->iOpenJobs, iLength, dat->nMax ? dat->nMax : 20000, iQueued);
+ CallService(szTTService, (WPARAM)wBuf, (LPARAM)&ti);
+ }
+ //MAD
+ if(SendMessage(dat->pContainer->hwndStatus, SB_GETTEXT, 0, (LPARAM)szStatusBarText)) {
+ 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), szStatusBarText, iLen, &size );
+ ReleaseDC (dat->pContainer->hwndStatus,hdc);
+
+ if(dat && PtInRect(&rc,pt)&&((rc.right-rc.left)<size.cx)) {
+ DBVARIANT dbv={0};
+
+ if(dat->bType == SESSIONTYPE_CHAT)
+ M->GetTString(dat->hContact,dat->szProto,"Topic",&dbv);
+
+ tooltip_active = TRUE;
+ CallService(szTTService, (WPARAM)dbv.ptszVal, (LPARAM)&ti);
+ if(dbv.pszVal)
+ DBFreeVariant(&dbv);
+ }
+ }
+ // MAD_
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ KillTimer(hWnd, TIMERID_HOVER);
+ }
+ return CallWindowProc(OldStatusBarproc, hWnd, msg, wParam, lParam);
+}
+
diff --git a/plugins/TabSRMM/src/eventpopups.cpp b/plugins/TabSRMM/src/eventpopups.cpp new file mode 100644 index 0000000000..ea1df5a209 --- /dev/null +++ b/plugins/TabSRMM/src/eventpopups.cpp @@ -0,0 +1,997 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: eventpopups.cpp 13750 2011-08-03 20:10:43Z george.hazan $
+ *
+ * This implements the event notification module for tabSRMM. The code
+ * is largely based on the NewEventNotify plugin for Miranda IM. See
+ * notices below.
+ *
+ * Name: NewEventNotify - Plugin for Miranda ICQ
+ * Description: Notifies you when you receive a message
+ * Author: icebreaker, <icebreaker@newmail.net>
+ * Date: 18.07.02 13:59 / Update: 16.09.02 17:45
+ * Copyright: (C) 2002 Starzinger Michael
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+extern INT_PTR CALLBACK DlgProcSetupStatusModes(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+extern HIMAGELIST CreateStateImageList();
+extern HANDLE g_hEvent;
+
+typedef std::vector<PLUGIN_DATAT *>::iterator PopupListIterator;
+static std::vector<PLUGIN_DATAT *> PopupList;
+
+BOOL bWmNotify = TRUE;
+
+static const PLUGIN_DATAT* PU_GetByContact(const HANDLE hContact)
+{
+ if(PopupList.size()) {
+ PopupListIterator it = PopupList.begin();
+ while(it != PopupList.end()) {
+ if((*it)->hContact == hContact)
+ return(*it);
+ it++;
+ }
+ }
+ return(0);
+}
+
+/**
+ * remove stale popup data which has been marked for removal by the popup
+ * window procedure.
+ *
+ */
+static void PU_CleanUp()
+{
+ if(PopupList.size()) {
+ PopupListIterator it = PopupList.begin();
+ while(it != PopupList.end()) {
+ if(PopupList.size() == 0)
+ break;
+ if((*it)->hContact == 0) {
+ //_DebugTraceW(_T("found stale popup %s"), (*it)->eventData->szText);
+ if((*it)->eventData)
+ free((*it)->eventData);
+ free(*it);
+ it = PopupList.erase(it);
+ continue;
+ }
+ it++;
+ }
+ }
+}
+
+static void CheckForRemoveMask()
+{
+ if (!M->GetByte(MODULE, "firsttime", 0) && (nen_options.maskActL & MASK_REMOVE || nen_options.maskActR & MASK_REMOVE || nen_options.maskActTE & MASK_REMOVE)) {
+ MessageBoxA(0, Translate("One of your popup actions is set to DISMISS EVENT.\nNote that this options may have unwanted side effects as it REMOVES the event from the unread queue.\nThis may lead to events not showing up as \"new\". If you don't want this behaviour, please review the Event Notifications settings page."), "tabSRMM Warning Message", MB_OK | MB_ICONSTOP);
+ M->WriteByte(MODULE, "firsttime", 1);
+ }
+}
+
+
+int TSAPI NEN_ReadOptions(NEN_OPTIONS *options)
+{
+ options->bPreview = (BOOL)M->GetByte(MODULE, OPT_PREVIEW, TRUE);
+ options->bDefaultColorMsg = (BOOL)M->GetByte(MODULE, OPT_COLDEFAULT_MESSAGE, TRUE);
+ options->bDefaultColorOthers = (BOOL)M->GetByte(MODULE, OPT_COLDEFAULT_OTHERS, TRUE);
+ options->bDefaultColorErr = (BOOL)M->GetByte(MODULE, OPT_COLDEFAULT_ERR, TRUE);
+ options->colBackMsg = (COLORREF)M->GetDword(MODULE, OPT_COLBACK_MESSAGE, DEFAULT_COLBACK);
+ options->colTextMsg = (COLORREF)M->GetDword(MODULE, OPT_COLTEXT_MESSAGE, DEFAULT_COLTEXT);
+ options->colBackOthers = (COLORREF)M->GetDword(MODULE, OPT_COLBACK_OTHERS, DEFAULT_COLBACK);
+ options->colTextOthers = (COLORREF)M->GetDword(MODULE, OPT_COLTEXT_OTHERS, DEFAULT_COLTEXT);
+ options->colBackErr = (COLORREF)M->GetDword(MODULE, OPT_COLBACK_ERR, DEFAULT_COLBACK);
+ options->colTextErr = (COLORREF)M->GetDword(MODULE, OPT_COLTEXT_ERR, DEFAULT_COLTEXT);
+ options->maskActL = (UINT)M->GetByte(MODULE, OPT_MASKACTL, DEFAULT_MASKACTL);
+ options->maskActR = (UINT)M->GetByte(MODULE, OPT_MASKACTR, DEFAULT_MASKACTR);
+ options->maskActTE = (UINT)M->GetByte(MODULE, OPT_MASKACTTE, DEFAULT_MASKACTR) & (MASK_OPEN | MASK_DISMISS);
+ options->bMergePopup = (BOOL)M->GetByte(MODULE, OPT_MERGEPOPUP, 0);
+ options->iDelayMsg = (int)M->GetDword(MODULE, OPT_DELAY_MESSAGE, (DWORD)DEFAULT_DELAY);
+ options->iDelayOthers = (int)M->GetDword(MODULE, OPT_DELAY_OTHERS, (DWORD)DEFAULT_DELAY);
+ options->iDelayErr = (int)M->GetDword(MODULE, OPT_DELAY_ERR, (DWORD)DEFAULT_DELAY);
+ options->iDelayDefault = (int)DBGetContactSettingRangedWord(NULL, "PopUp", "Seconds", SETTING_LIFETIME_DEFAULT, SETTING_LIFETIME_MIN, SETTING_LIFETIME_MAX);
+ options->bShowHeaders = (BYTE)M->GetByte(MODULE, OPT_SHOW_HEADERS, FALSE);
+ options->bNoRSS = (BOOL)M->GetByte(MODULE, OPT_NORSS, FALSE);
+ options->iDisable = (BYTE)M->GetByte(MODULE, OPT_DISABLE, 0);
+ options->iMUCDisable = (BYTE)M->GetByte(MODULE, OPT_MUCDISABLE, 0);
+ options->dwStatusMask = (DWORD)M->GetDword(MODULE, "statusmask", (DWORD) - 1);
+ options->bTraySupport = (BOOL)M->GetByte(MODULE, "traysupport", 0);
+ options->bWindowCheck = (BOOL)M->GetByte(MODULE, OPT_WINDOWCHECK, 0);
+ options->bNoRSS = (BOOL)M->GetByte(MODULE, OPT_NORSS, 0);
+ options->iLimitPreview = (int)M->GetDword(MODULE, OPT_LIMITPREVIEW, 0);
+ options->wMaxFavorites = 15;
+ options->wMaxRecent = 15;
+ options->dwRemoveMask = M->GetDword(MODULE, OPT_REMOVEMASK, 0);
+ options->bDisableNonMessage = M->GetByte(MODULE, "disablenonmessage", 0);
+ CheckForRemoveMask();
+ return 0;
+}
+
+int TSAPI NEN_WriteOptions(NEN_OPTIONS *options)
+{
+ M->WriteByte(MODULE, OPT_PREVIEW, (BYTE)options->bPreview);
+ M->WriteByte(MODULE, OPT_COLDEFAULT_MESSAGE, (BYTE)options->bDefaultColorMsg);
+ M->WriteByte(MODULE, OPT_COLDEFAULT_OTHERS, (BYTE)options->bDefaultColorOthers);
+ M->WriteByte(MODULE, OPT_COLDEFAULT_ERR, (BYTE)options->bDefaultColorErr);
+ M->WriteDword(MODULE, OPT_COLBACK_MESSAGE, (DWORD)options->colBackMsg);
+ M->WriteDword(MODULE, OPT_COLTEXT_MESSAGE, (DWORD)options->colTextMsg);
+ M->WriteDword(MODULE, OPT_COLBACK_OTHERS, (DWORD)options->colBackOthers);
+ M->WriteDword(MODULE, OPT_COLTEXT_OTHERS, (DWORD)options->colTextOthers);
+ M->WriteDword(MODULE, OPT_COLBACK_ERR, (DWORD)options->colBackErr);
+ M->WriteDword(MODULE, OPT_COLTEXT_ERR, (DWORD)options->colTextErr);
+ M->WriteByte(MODULE, OPT_MASKACTL, (BYTE)options->maskActL);
+ M->WriteByte(MODULE, OPT_MASKACTR, (BYTE)options->maskActR);
+ M->WriteByte(MODULE, OPT_MASKACTTE, (BYTE)options->maskActTE);
+ M->WriteByte(MODULE, OPT_MERGEPOPUP, (BYTE)options->bMergePopup);
+ M->WriteDword(MODULE, OPT_DELAY_MESSAGE, (DWORD)options->iDelayMsg);
+ M->WriteDword(MODULE, OPT_DELAY_OTHERS, (DWORD)options->iDelayOthers);
+ M->WriteDword(MODULE, OPT_DELAY_ERR, (DWORD)options->iDelayErr);
+ M->WriteByte(MODULE, OPT_SHOW_HEADERS, (BYTE)options->bShowHeaders);
+ M->WriteByte(MODULE, OPT_DISABLE, (BYTE)options->iDisable);
+ M->WriteByte(MODULE, OPT_MUCDISABLE, (BYTE)options->iMUCDisable);
+ M->WriteByte(MODULE, "traysupport", (BYTE)options->bTraySupport);
+ M->WriteByte(MODULE, OPT_WINDOWCHECK, (BYTE)options->bWindowCheck);
+ M->WriteByte(MODULE, OPT_NORSS, (BYTE)options->bNoRSS);
+ M->WriteDword(MODULE, OPT_LIMITPREVIEW, options->iLimitPreview);
+ M->WriteDword(MODULE, OPT_REMOVEMASK, options->dwRemoveMask);
+ M->WriteByte(MODULE, "disablenonmessage", options->bDisableNonMessage);
+ return 0;
+}
+
+INT_PTR CALLBACK DlgProcPopupOpts(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ NEN_OPTIONS *options = &nen_options;
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TVINSERTSTRUCT tvi = {0};
+ int i = 0;
+ SetWindowLongPtr(GetDlgItem(hWnd, IDC_EVENTOPTIONS), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hWnd, IDC_EVENTOPTIONS), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+ TranslateDialogDefault(hWnd);
+ HIMAGELIST himl = (HIMAGELIST)SendDlgItemMessage(hWnd, IDC_EVENTOPTIONS, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)CreateStateImageList());
+ ImageList_Destroy(himl);
+
+ if(!PluginConfig.g_PopupAvail) {
+ HWND hwndChild = FindWindowEx(hWnd, 0, 0, 0);
+ while(hwndChild) {
+ ShowWindow(hwndChild, SW_HIDE);
+ hwndChild = FindWindowEx(hWnd, hwndChild, 0, 0);
+ }
+ Utils::showDlgControl(hWnd, IDC_NOPOPUPAVAIL, SW_SHOW);
+ }
+ else
+ Utils::showDlgControl(hWnd, IDC_NOPOPUPAVAIL, SW_HIDE);
+ /*
+ * fill the tree view
+ */
+
+ TOptionListGroup *lGroups = CTranslator::getGroupTree(CTranslator::TREE_NEN);
+
+ while (lGroups[i].szName != NULL) {
+ tvi.hParent = 0;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE;
+ tvi.item.pszText = lGroups[i].szName;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_EXPANDED | TVIS_BOLD;
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_EXPANDED | TVIS_BOLD;
+ lGroups[i++].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hWnd, IDC_EVENTOPTIONS), &tvi);
+ }
+ i = 0;
+
+ TOptionListItem *defaultItems = CTranslator::getTree(CTranslator::TREE_NEN);
+
+ while (defaultItems[i].szName != 0) {
+ tvi.hParent = (HTREEITEM)lGroups[defaultItems[i].uGroup].handle;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.pszText = defaultItems[i].szName;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+ tvi.item.lParam = i;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK;
+ if (defaultItems[i].uType == LOI_TYPE_SETTING)
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(*((BOOL *)defaultItems[i].lParam) ? 3 : 2);//2 : 1);
+ else if (defaultItems[i].uType == LOI_TYPE_FLAG) {
+ UINT uVal = *((UINT *)defaultItems[i].lParam);
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(uVal & defaultItems[i].id ? 3 : 2);//2 : 1);
+ }
+ defaultItems[i].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hWnd, IDC_EVENTOPTIONS), &tvi);
+ i++;
+ }
+ SendDlgItemMessage(hWnd, IDC_COLBACK_MESSAGE, CPM_SETCOLOUR, 0, options->colBackMsg);
+ SendDlgItemMessage(hWnd, IDC_COLTEXT_MESSAGE, CPM_SETCOLOUR, 0, options->colTextMsg);
+ SendDlgItemMessage(hWnd, IDC_COLBACK_OTHERS, CPM_SETCOLOUR, 0, options->colBackOthers);
+ SendDlgItemMessage(hWnd, IDC_COLTEXT_OTHERS, CPM_SETCOLOUR, 0, options->colTextOthers);
+ SendDlgItemMessage(hWnd, IDC_COLBACK_ERR, CPM_SETCOLOUR, 0, options->colBackErr);
+ SendDlgItemMessage(hWnd, IDC_COLTEXT_ERR, CPM_SETCOLOUR, 0, options->colTextErr);
+ CheckDlgButton(hWnd, IDC_CHKDEFAULTCOL_MESSAGE, options->bDefaultColorMsg ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hWnd, IDC_CHKDEFAULTCOL_OTHERS, options->bDefaultColorOthers ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hWnd, IDC_CHKDEFAULTCOL_ERR, options->bDefaultColorErr ? BST_CHECKED : BST_UNCHECKED);
+
+ SendDlgItemMessage(hWnd, IDC_COLTEXT_MUC, CPM_SETCOLOUR, 0, g_Settings.crPUTextColour);
+ SendDlgItemMessage(hWnd, IDC_COLBACK_MUC, CPM_SETCOLOUR, 0, g_Settings.crPUBkgColour);
+ CheckDlgButton(hWnd, IDC_CHKDEFAULTCOL_MUC, g_Settings.iPopupStyle == 2 ? BST_CHECKED : BST_UNCHECKED);
+
+ SendDlgItemMessage(hWnd, IDC_DELAY_MESSAGE_SPIN, UDM_SETRANGE, 0, MAKELONG(3600, -1));
+ SendDlgItemMessage(hWnd, IDC_DELAY_OTHERS_SPIN, UDM_SETRANGE, 0, MAKELONG(3600, -1));
+ SendDlgItemMessage(hWnd, IDC_DELAY_MESSAGE_MUC_SPIN, UDM_SETRANGE, 0, MAKELONG(3600, -1));
+ SendDlgItemMessage(hWnd, IDC_DELAY_ERR_SPIN, UDM_SETRANGE, 0, MAKELONG(3600, -1));
+
+ SendDlgItemMessage(hWnd, IDC_DELAY_MESSAGE_SPIN, UDM_SETPOS, 0, (LPARAM)options->iDelayMsg);
+ SendDlgItemMessage(hWnd, IDC_DELAY_OTHERS_SPIN, UDM_SETPOS, 0, (LPARAM)options->iDelayOthers);
+ SendDlgItemMessage(hWnd, IDC_DELAY_ERR_SPIN, UDM_SETPOS, 0, (LPARAM)options->iDelayErr);
+ SendDlgItemMessage(hWnd, IDC_DELAY_MESSAGE_MUC_SPIN, UDM_SETPOS, 0, (LPARAM)g_Settings.iPopupTimeout);
+
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_MESSAGE, !options->bDefaultColorMsg);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_MESSAGE, !options->bDefaultColorMsg);
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_OTHERS, !options->bDefaultColorOthers);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_OTHERS, !options->bDefaultColorOthers);
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_ERR, !options->bDefaultColorErr);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_ERR, !options->bDefaultColorErr);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_MUC, (g_Settings.iPopupStyle == 3) ? TRUE : FALSE);
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_MUC, (g_Settings.iPopupStyle == 3) ? TRUE : FALSE);
+
+ CheckDlgButton(hWnd, IDC_MUC_LOGCOLORS, g_Settings.iPopupStyle < 2 ? TRUE : FALSE);
+ Utils::enableDlgControl(hWnd, IDC_MUC_LOGCOLORS, g_Settings.iPopupStyle != 2 ? TRUE : FALSE);
+
+ SetDlgItemInt(hWnd, IDC_MESSAGEPREVIEWLIMIT, options->iLimitPreview, FALSE);
+ CheckDlgButton(hWnd, IDC_LIMITPREVIEW, (options->iLimitPreview > 0) ? 1 : 0);
+ SendDlgItemMessage(hWnd, IDC_MESSAGEPREVIEWLIMITSPIN, UDM_SETRANGE, 0, MAKELONG(2048, options->iLimitPreview > 0 ? 50 : 0));
+ SendDlgItemMessage(hWnd, IDC_MESSAGEPREVIEWLIMITSPIN, UDM_SETPOS, 0, (LPARAM)options->iLimitPreview);
+ Utils::enableDlgControl(hWnd, IDC_MESSAGEPREVIEWLIMIT, IsDlgButtonChecked(hWnd, IDC_LIMITPREVIEW));
+ Utils::enableDlgControl(hWnd, IDC_MESSAGEPREVIEWLIMITSPIN, IsDlgButtonChecked(hWnd, IDC_LIMITPREVIEW));
+
+ bWmNotify = FALSE;
+ return TRUE;
+ }
+ case DM_STATUSMASKSET:
+ M->WriteDword(MODULE, "statusmask", (DWORD)lParam);
+ options->dwStatusMask = (int)lParam;
+ break;
+ case WM_COMMAND:
+ if (!bWmNotify) {
+ switch (LOWORD(wParam)) {
+ case IDC_PREVIEW:
+ PopupPreview(options);
+ break;
+ case IDC_POPUPSTATUSMODES: {
+ HWND hwndNew = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CHOOSESTATUSMODES), hWnd, DlgProcSetupStatusModes, M->GetDword(MODULE, "statusmask", (DWORD) - 1));
+ SendMessage(hwndNew, DM_SETPARENTDIALOG, 0, (LPARAM)hWnd);
+ break;
+ }
+ default: {
+
+ if(IsDlgButtonChecked(hWnd, IDC_CHKDEFAULTCOL_MUC))
+ g_Settings.iPopupStyle = 2;
+ else if(IsDlgButtonChecked(hWnd, IDC_MUC_LOGCOLORS))
+ g_Settings.iPopupStyle = 1;
+ else
+ g_Settings.iPopupStyle = 3;
+
+ Utils::enableDlgControl(hWnd, IDC_MUC_LOGCOLORS, g_Settings.iPopupStyle != 2 ? TRUE : FALSE);
+
+ options->bDefaultColorMsg = IsDlgButtonChecked(hWnd, IDC_CHKDEFAULTCOL_MESSAGE);
+ options->bDefaultColorOthers = IsDlgButtonChecked(hWnd, IDC_CHKDEFAULTCOL_OTHERS);
+ options->bDefaultColorErr = IsDlgButtonChecked(hWnd, IDC_CHKDEFAULTCOL_ERR);
+
+ options->iDelayMsg = SendDlgItemMessage(hWnd, IDC_DELAY_MESSAGE_SPIN, UDM_GETPOS, 0, 0);
+ options->iDelayOthers = SendDlgItemMessage(hWnd, IDC_DELAY_OTHERS_SPIN, UDM_GETPOS, 0, 0);
+ options->iDelayErr = SendDlgItemMessage(hWnd, IDC_DELAY_ERR_SPIN, UDM_GETPOS, 0, 0);
+
+ g_Settings.iPopupTimeout = SendDlgItemMessage(hWnd, IDC_DELAY_MESSAGE_MUC_SPIN, UDM_GETPOS, 0, 0);
+
+ if (IsDlgButtonChecked(hWnd, IDC_LIMITPREVIEW))
+ options->iLimitPreview = GetDlgItemInt(hWnd, IDC_MESSAGEPREVIEWLIMIT, NULL, FALSE);
+ else
+ options->iLimitPreview = 0;
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_MESSAGE, !options->bDefaultColorMsg);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_MESSAGE, !options->bDefaultColorMsg);
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_OTHERS, !options->bDefaultColorOthers);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_OTHERS, !options->bDefaultColorOthers);
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_ERR, !options->bDefaultColorErr);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_ERR, !options->bDefaultColorErr);
+ Utils::enableDlgControl(hWnd, IDC_COLTEXT_MUC, (g_Settings.iPopupStyle == 3) ? TRUE : FALSE);
+ Utils::enableDlgControl(hWnd, IDC_COLBACK_MUC, (g_Settings.iPopupStyle == 3) ? TRUE : FALSE);
+
+ Utils::enableDlgControl(hWnd, IDC_MESSAGEPREVIEWLIMIT, IsDlgButtonChecked(hWnd, IDC_LIMITPREVIEW));
+ Utils::enableDlgControl(hWnd, IDC_MESSAGEPREVIEWLIMITSPIN, IsDlgButtonChecked(hWnd, IDC_LIMITPREVIEW));
+ //disable delay textbox when infinite is checked
+
+ Utils::enableDlgControl(hWnd, IDC_DELAY_MESSAGE, options->iDelayMsg != -1);
+ Utils::enableDlgControl(hWnd, IDC_DELAY_OTHERS, options->iDelayOthers != -1);
+ Utils::enableDlgControl(hWnd, IDC_DELAY_ERR, options->iDelayErr != -1);
+ Utils::enableDlgControl(hWnd, IDC_DELAY_MUC, g_Settings.iPopupTimeout != -1);
+
+ if (HIWORD(wParam) == CPN_COLOURCHANGED) {
+ options->colBackMsg = SendDlgItemMessage(hWnd, IDC_COLBACK_MESSAGE, CPM_GETCOLOUR, 0, 0);
+ options->colTextMsg = SendDlgItemMessage(hWnd, IDC_COLTEXT_MESSAGE, CPM_GETCOLOUR, 0, 0);
+ options->colBackOthers = SendDlgItemMessage(hWnd, IDC_COLBACK_OTHERS, CPM_GETCOLOUR, 0, 0);
+ options->colTextOthers = SendDlgItemMessage(hWnd, IDC_COLTEXT_OTHERS, CPM_GETCOLOUR, 0, 0);
+ options->colBackErr = SendDlgItemMessage(hWnd, IDC_COLBACK_ERR, CPM_GETCOLOUR, 0, 0);
+ options->colTextErr = SendDlgItemMessage(hWnd, IDC_COLTEXT_ERR, CPM_GETCOLOUR, 0, 0);
+ g_Settings.crPUBkgColour = SendDlgItemMessage(hWnd, IDC_COLBACK_MUC, CPM_GETCOLOUR, 0, 0);
+ g_Settings.crPUTextColour = SendDlgItemMessage(hWnd, IDC_COLTEXT_MUC, CPM_GETCOLOUR, 0, 0);
+ }
+ SendMessage(GetParent(hWnd), PSM_CHANGED, 0, 0);
+ break;
+ }
+ }
+ }
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case IDC_EVENTOPTIONS:
+ if (((LPNMHDR)lParam)->code == NM_CLICK) {
+ TVHITTESTINFO hti;
+ TVITEM item = {0};
+
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.stateMask = TVIS_STATEIMAGEMASK | TVIS_BOLD;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti)) {
+ item.hItem = (HTREEITEM)hti.hItem;
+ SendDlgItemMessageA(hWnd, IDC_EVENTOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (item.state & TVIS_BOLD && hti.flags & TVHT_ONITEMSTATEICON) {
+ item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_BOLD;
+ SendDlgItemMessageA(hWnd, IDC_EVENTOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ }
+ else if (hti.flags&TVHT_ONITEMSTATEICON) {
+ if (((item.state & TVIS_STATEIMAGEMASK) >> 12) == 3) {
+ item.state = INDEXTOSTATEIMAGEMASK(1);
+ SendDlgItemMessageA(hWnd, IDC_EVENTOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ }
+ SendMessage(GetParent(hWnd), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ }
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY: {
+ int i = 0;
+ TVITEM item = {0};
+ struct TContainerData *pContainer = pFirstContainer;
+
+ TOptionListItem *defaultItems = CTranslator::getTree(CTranslator::TREE_NEN);
+
+ while (defaultItems[i].szName != NULL) {
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.hItem = (HTREEITEM)defaultItems[i].handle;
+ item.stateMask = TVIS_STATEIMAGEMASK;
+
+ SendDlgItemMessageA(hWnd, IDC_EVENTOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (defaultItems[i].uType == LOI_TYPE_SETTING) {
+ BOOL *ptr = (BOOL *)defaultItems[i].lParam;
+ *ptr = (item.state >> 12) == 3/*2*/ ? TRUE : FALSE;
+ }
+ else if (defaultItems[i].uType == LOI_TYPE_FLAG) {
+ UINT *uVal = (UINT *)defaultItems[i].lParam;
+ *uVal = ((item.state >> 12) == 3/*2*/) ? *uVal | defaultItems[i].id : *uVal & ~defaultItems[i].id;
+ }
+ i++;
+ }
+ M->WriteByte("Chat", "PopupStyle", (BYTE)g_Settings.iPopupStyle);
+ DBWriteContactSettingWord(NULL, "Chat", "PopupTimeout", g_Settings.iPopupTimeout);
+
+ g_Settings.crPUBkgColour = SendDlgItemMessage(hWnd, IDC_COLBACK_MUC, CPM_GETCOLOUR, 0, 0);
+ M->WriteDword("Chat", "PopupColorBG", (DWORD)g_Settings.crPUBkgColour);
+ g_Settings.crPUTextColour = SendDlgItemMessage(hWnd, IDC_COLTEXT_MUC, CPM_GETCOLOUR, 0, 0);
+ M->WriteDword("Chat", "PopupColorText", (DWORD)g_Settings.crPUTextColour);
+
+ NEN_WriteOptions(&nen_options);
+ CheckForRemoveMask();
+ CreateSystrayIcon(nen_options.bTraySupport);
+ SetEvent(g_hEvent); // wake up the thread which cares about the floater and tray
+ break;
+ }
+ case PSN_RESET:
+ NEN_ReadOptions(&nen_options);
+ break;
+ }
+ break;
+ case WM_DESTROY: {
+ //ImageList_Destroy((HIMAGELIST)SendDlgItemMessage(hWnd, IDC_EVENTOPTIONS, TVM_GETIMAGELIST, 0, 0));
+ bWmNotify = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static int PopupAct(HWND hWnd, UINT mask, PLUGIN_DATAT* pdata)
+{
+ pdata->iActionTaken = TRUE;
+ if (mask & MASK_OPEN) {
+ int i;
+
+ for (i = 0; i < pdata->nrMerged; i++) {
+ if (pdata->eventData[i].hEvent != 0) {
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_HANDLECLISTEVENT, (WPARAM)pdata->hContact, (LPARAM)pdata->eventData[i].hEvent);
+ pdata->eventData[i].hEvent = 0;
+ }
+ }
+ }
+ if (mask & MASK_REMOVE) {
+ int i;
+
+ for (i = 0; i < pdata->nrMerged; i++) {
+ if (pdata->eventData[i].hEvent != 0) {
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_REMOVECLISTEVENT, (WPARAM)pdata->hContact, (LPARAM)pdata->eventData[i].hEvent);
+ pdata->eventData[i].hEvent = 0;
+ }
+ }
+ }
+ if (mask & MASK_DISMISS)
+ {
+ PUDeletePopUp(hWnd);
+ if (pdata->hContainer)
+ {
+ FLASHWINFO fwi;
+ fwi.cbSize = sizeof(fwi);
+ fwi.uCount = 0;
+ fwi.dwFlags = FLASHW_STOP;
+ fwi.hwnd = pdata->hContainer;
+ fwi.dwTimeout = 0;
+ FlashWindowEx(&fwi);
+ }
+ }
+ return 0;
+}
+
+static BOOL CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PLUGIN_DATAT* pdata = NULL;
+
+ pdata = (PLUGIN_DATAT *)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hWnd, (LPARAM)pdata);
+ if (!pdata) return FALSE;
+
+ switch (message) {
+ case WM_COMMAND:
+ PopupAct(hWnd, pdata->pluginOptions->maskActL, pdata);
+ break;
+ case WM_CONTEXTMENU:
+ PopupAct(hWnd, pdata->pluginOptions->maskActR, pdata);
+ break;
+ case UM_FREEPLUGINDATA:
+ pdata->hContact = 0; // mark as removeable
+ pdata->hWnd = 0;
+ return TRUE;
+ case UM_INITPOPUP:
+ pdata->hWnd = hWnd;
+ if (pdata->iSeconds > 0)
+ SetTimer(hWnd, TIMER_TO_ACTION, pdata->iSeconds * 1000, NULL);
+ break;
+ case WM_MOUSEWHEEL:
+ break;
+ case WM_SETCURSOR:
+ break;
+ case WM_TIMER: {
+ POINT pt;
+ RECT rc;
+
+ if (wParam != TIMER_TO_ACTION)
+ break;
+
+ GetCursorPos(&pt);
+ GetWindowRect(hWnd, &rc);
+ if(PtInRect(&rc, pt))
+ break;
+
+ if (pdata->iSeconds > 0)
+ KillTimer(hWnd, TIMER_TO_ACTION);
+ PopupAct(hWnd, pdata->pluginOptions->maskActTE, pdata);
+ break;
+ }
+ default:
+ break;
+ }
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+/**
+ * Get a preview for the message
+ * caller must always mir_free() the return value
+ *
+ * @param eventType the event type
+ * @param dbe DBEVENTINFO *: database event structure
+ *
+ * @return
+ */
+static TCHAR *GetPreviewT(WORD eventType, DBEVENTINFO* dbe)
+{
+ TCHAR *commentFix = NULL;
+ char *pBlob = (char *)dbe->pBlob;
+ bool fAddEllipsis = false;
+
+ int iPreviewLimit = nen_options.iLimitPreview;
+
+ if(iPreviewLimit > 500 || iPreviewLimit == 0)
+ iPreviewLimit = 500;
+
+ switch (eventType) {
+ case EVENTTYPE_MESSAGE:
+ if (pBlob) {
+ if(nen_options.bPreview) {
+ TCHAR* buf = DbGetEventTextT(dbe, CP_ACP);
+ if(lstrlen(buf) > iPreviewLimit) {
+ fAddEllipsis = true;
+ int iIndex = iPreviewLimit;
+ int iWordThreshold = 20;
+ while(iIndex && buf[iIndex] != ' ' && iWordThreshold--) {
+ buf[iIndex--] = 0;
+ }
+ buf[iIndex] = 0;
+ }
+ buf = (TCHAR *)mir_realloc(buf, (lstrlen(buf) + 5) * sizeof(TCHAR));
+ if(fAddEllipsis)
+ _tcscat(buf, _T("..."));
+ return(buf);
+ }
+ }
+ commentFix = mir_tstrdup(CTranslator::get(CTranslator::GEN_POPUPS_MESSAGE));
+ break;
+ case EVENTTYPE_FILE:
+ if(pBlob) {
+ if(!nen_options.bPreview) {
+ commentFix = mir_tstrdup(CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE));
+ break;
+ }
+ if(dbe->cbBlob > 5) { // min valid size = (sizeof(DWORD) + 1 character file name + terminating 0)
+ char* szFileName = (char *)dbe->pBlob + sizeof(DWORD);
+ char* szDescr = 0;
+ size_t namelength = Utils::safe_strlen(szFileName, dbe->cbBlob - sizeof(DWORD));
+
+ if(dbe->cbBlob > (sizeof(DWORD) + namelength + 1))
+ szDescr = szFileName + namelength + 1;
+
+ TCHAR* tszFileName = DbGetEventStringT(dbe, szFileName );
+ TCHAR* buf = 0;
+
+ if (szDescr && Utils::safe_strlen(szDescr, dbe->cbBlob - sizeof(DWORD) - namelength - 1) > 0) {
+ TCHAR* tszDescr = DbGetEventStringT(dbe, szDescr);
+
+ if(tszFileName && tszDescr) {
+ size_t uRequired = sizeof(TCHAR) * (_tcslen(CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE)) + namelength + _tcslen(tszDescr) + 10);
+ buf = (TCHAR *)mir_alloc(uRequired);
+ mir_sntprintf(buf, uRequired, _T("%s: %s (%s)"), CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE),
+ tszFileName, tszDescr);
+ mir_free(tszDescr);
+ mir_free(tszFileName);
+ return(buf);
+ }
+ }
+
+ if(tszFileName) {
+ size_t uRequired = sizeof(TCHAR) * (_tcslen(CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE)) + namelength +
+ _tcslen(CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE_NODESC)) + 10);
+ buf = (TCHAR *)mir_alloc(uRequired);
+ mir_sntprintf(buf, uRequired, _T("%s: %s (%s)"), CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE),
+ tszFileName, CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE_NODESC));
+ mir_free(tszFileName);
+ }
+ if(buf)
+ return(buf);
+ }
+ }
+ commentFix = mir_tstrdup(CTranslator::get(CTranslator::GEN_STRING_EVENT_FILE_INVALID));
+ break;
+ default:
+ commentFix = mir_tstrdup(CTranslator::get(CTranslator::GEN_POPUPS_UNKNOWN));
+ break;
+ }
+ return commentFix;
+}
+
+static int PopupUpdateT(HANDLE hContact, HANDLE hEvent)
+{
+ PLUGIN_DATAT *pdata = 0;
+ DBEVENTINFO dbe;
+ TCHAR lpzText[MAX_SECONDLINE] = _T("");
+ TCHAR timestamp[MAX_DATASIZE] = _T("\0");
+ TCHAR formatTime[MAX_DATASIZE] = _T("\0");
+ int iEvent = 0;
+ TCHAR *p = lpzText;
+ int available = 0, i;
+ TCHAR *szPreview = NULL;
+
+ pdata = const_cast<PLUGIN_DATAT *>(PU_GetByContact(hContact));
+
+ if(!pdata)
+ return(1);
+
+ ZeroMemory((void *)&dbe, sizeof(dbe));
+
+ if (hEvent) {
+ if (pdata->pluginOptions->bShowHeaders) {
+ mir_sntprintf(pdata->szHeader, safe_sizeof(pdata->szHeader), _T("%s %d\n"),
+ CTranslator::get(CTranslator::GEN_POPUPS_NEW), pdata->nrMerged + 1);
+ pdata->szHeader[255] = 0;
+ }
+ ZeroMemory(&dbe, sizeof(dbe));
+ dbe.cbSize = sizeof(dbe);
+ if (pdata->pluginOptions->bPreview && hContact) {
+ dbe.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hEvent, 0);
+ dbe.pBlob = (PBYTE)malloc(dbe.cbBlob);
+ }
+ CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&dbe);
+
+ formatTime[0] = 0;
+ _tcsncpy(formatTime, _T("%Y.%m.%d %H:%M"), MAX_DATASIZE);
+ _tcsftime(timestamp, MAX_DATASIZE, formatTime, _localtime32((__time32_t *)&dbe.timestamp));
+ mir_sntprintf(pdata->eventData[pdata->nrMerged].szText, MAX_SECONDLINE, _T("\n\n%s\n"), timestamp);
+
+ szPreview = GetPreviewT(dbe.eventType, &dbe);
+ if (szPreview) {
+ _tcsncat(pdata->eventData[pdata->nrMerged].szText, szPreview, MAX_SECONDLINE);
+ mir_free(szPreview);
+ } else
+ _tcsncat(pdata->eventData[pdata->nrMerged].szText, _T(" "), MAX_SECONDLINE);
+
+ pdata->eventData[pdata->nrMerged].szText[MAX_SECONDLINE - 1] = 0;
+
+ /*
+ * now, reassemble the popup text, make sure the *last* event is shown, and then show the most recent events
+ * for which there is enough space in the popup text
+ */
+
+ available = MAX_SECONDLINE - 1;
+ if (pdata->pluginOptions->bShowHeaders) {
+ _tcsncpy(lpzText, pdata->szHeader, MAX_SECONDLINE);
+ available -= lstrlen(pdata->szHeader);
+ }
+ for (i = pdata->nrMerged; i >= 0; i--) {
+ available -= lstrlen(pdata->eventData[i].szText);
+ if (available <= 0)
+ break;
+ }
+ i = (available > 0) ? i + 1 : i + 2;
+ for (; i <= pdata->nrMerged; i++) {
+ _tcsncat(lpzText, pdata->eventData[i].szText, MAX_SECONDLINE);
+ }
+ pdata->eventData[pdata->nrMerged].hEvent = hEvent;
+ pdata->eventData[pdata->nrMerged].timestamp = dbe.timestamp;
+ pdata->nrMerged++;
+ if (pdata->nrMerged >= pdata->nrEventsAlloced) {
+ pdata->nrEventsAlloced += 5;
+ pdata->eventData = (EVENT_DATAT *)realloc(pdata->eventData, pdata->nrEventsAlloced * sizeof(EVENT_DATAT));
+ }
+ if (dbe.pBlob)
+ free(dbe.pBlob);
+
+ CallService(MS_POPUP_CHANGETEXTT, (WPARAM)pdata->hWnd, (LPARAM)lpzText);
+ }
+ return(0);
+}
+
+static int PopupShowT(NEN_OPTIONS *pluginOptions, HANDLE hContact, HANDLE hEvent, UINT eventType, HWND hContainer)
+{
+ POPUPDATAT_V2 pud = {0};
+ PLUGIN_DATAT *pdata;
+ DBEVENTINFO dbe;
+ long iSeconds = 0;
+ TCHAR *szPreview = NULL;
+
+ //there has to be a maximum number of popups shown at the same time
+ if(PopupList.size() >= MAX_POPUPS)
+ return(2);
+
+ if (!PluginConfig.g_PopupAvail)
+ return 0;
+
+ switch (eventType) {
+ case EVENTTYPE_MESSAGE:
+ pud.lchIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ pud.colorBack = pluginOptions->bDefaultColorMsg ? 0 : pluginOptions->colBackMsg;
+ pud.colorText = pluginOptions->bDefaultColorMsg ? 0 : pluginOptions->colTextMsg;
+ iSeconds = pluginOptions->iDelayMsg;
+ break;
+ case EVENTTYPE_FILE:
+ pud.lchIcon = LoadSkinnedIcon(SKINICON_EVENT_FILE);
+ pud.colorBack = pluginOptions->bDefaultColorOthers ? 0 : pluginOptions->colBackOthers;
+ pud.colorText = pluginOptions->bDefaultColorOthers ? 0 : pluginOptions->colTextOthers;
+ iSeconds = pluginOptions->iDelayOthers;
+ break;
+ default:
+ return 1;
+ }
+
+ ZeroMemory(&dbe, sizeof(dbe));
+ dbe.pBlob = NULL;
+ dbe.cbSize = sizeof(dbe);
+
+ // fix for a crash
+ if (hEvent && (pluginOptions->bPreview || hContact == 0)) {
+ dbe.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hEvent, 0);
+ dbe.pBlob = (PBYTE)malloc(dbe.cbBlob);
+ } else
+ dbe.cbBlob = 0;
+ CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&dbe);
+
+ if(hEvent == 0 && hContact == 0)
+ dbe.szModule = Translate("Unknown module or contact");
+
+ pdata = (PLUGIN_DATAT *)malloc(sizeof(PLUGIN_DATAT));
+ ZeroMemory((void *)pdata, sizeof(PLUGIN_DATAT));
+
+ pdata->eventType = eventType;
+ pdata->hContact = hContact;
+ pdata->pluginOptions = pluginOptions;
+ pdata->pud = &pud;
+ pdata->iSeconds = iSeconds; // ? iSeconds : pluginOptions->iDelayDefault;
+ pdata->hContainer = hContainer;
+ pud.iSeconds = pdata->iSeconds ? -1 : 0;
+
+ //finally create the popup
+ pud.lchContact = hContact;
+ pud.PluginWindowProc = (WNDPROC)PopupDlgProc;
+ pud.PluginData = pdata;
+
+ if (hContact)
+ mir_sntprintf(pud.lptzContactName, MAX_CONTACTNAME, _T("%s"), (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+ else {
+ TCHAR *szModule = mir_a2t(dbe.szModule);
+ mir_sntprintf(pud.lptzContactName, MAX_CONTACTNAME, _T("%s"), szModule);
+ mir_free(szModule);
+ }
+
+ szPreview = GetPreviewT((WORD)eventType, &dbe);
+ if (szPreview) {
+ mir_sntprintf(pud.lptzText, MAX_SECONDLINE, _T("%s"), szPreview);
+ mir_free(szPreview);
+ } else
+ mir_sntprintf(pud.lptzText, MAX_SECONDLINE, _T(" "));
+
+ pdata->eventData = (EVENT_DATAT *)malloc(NR_MERGED * sizeof(EVENT_DATAT));
+ pdata->eventData[0].hEvent = hEvent;
+ pdata->eventData[0].timestamp = dbe.timestamp;
+ _tcsncpy(pdata->eventData[0].szText, pud.lptzText, MAX_SECONDLINE);
+ pdata->eventData[0].szText[MAX_SECONDLINE - 1] = 0;
+ pdata->nrEventsAlloced = NR_MERGED;
+ pdata->nrMerged = 1;
+
+ // fix for broken popups -- process failures
+ if (CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&pud, 0) < 0) {
+ // failed to display, perform cleanup
+ if (pdata->eventData)
+ free(pdata->eventData);
+ free(pdata);
+ }
+ else
+ PopupList.push_back(pdata);
+
+ if (dbe.pBlob)
+ free(dbe.pBlob);
+
+ return 0;
+}
+
+static int TSAPI PopupPreview(NEN_OPTIONS *pluginOptions)
+{
+ PopupShowT(pluginOptions, NULL, NULL, EVENTTYPE_MESSAGE, NULL);
+ return 0;
+}
+
+/*
+ * updates the menu entry...
+ * bForced is used to only update the status, nickname etc. and does NOT update the unread count
+ */
+void TSAPI UpdateTrayMenuState(struct TWindowData *dat, BOOL bForced)
+{
+ MENUITEMINFO mii = {0};
+ TCHAR szMenuEntry[80];
+
+ if (PluginConfig.g_hMenuTrayUnread == 0)
+ return;
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA | MIIM_BITMAP;
+
+ if (dat->hContact != 0) {
+ const TCHAR *tszProto = dat->cache->getRealAccount();
+
+ assert(tszProto != 0);
+
+ GetMenuItemInfo(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)dat->hContact, FALSE, &mii);
+ if (!bForced)
+ PluginConfig.m_UnreadInTray -= (mii.dwItemData & 0x0000ffff);
+ if (mii.dwItemData > 0 || bForced) {
+ if (!bForced)
+ mii.dwItemData = 0;
+ mii.fMask |= MIIM_STRING;
+ mir_sntprintf(szMenuEntry, safe_sizeof(szMenuEntry), _T("%s: %s (%s) [%d]"), tszProto, dat->cache->getNick(), dat->szStatus[0] ? dat->szStatus : _T("(undef)"), mii.dwItemData & 0x0000ffff);
+ mii.dwTypeData = (LPTSTR)szMenuEntry;
+ mii.cch = lstrlen(szMenuEntry) + 1;
+ }
+ mii.hbmpItem = HBMMENU_CALLBACK;
+ SetMenuItemInfo(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)dat->hContact, FALSE, &mii);
+ }
+}
+/*
+ * if we want tray support, add the contact to the list of unread sessions in the tray menu
+ */
+
+int TSAPI UpdateTrayMenu(const TWindowData *dat, WORD wStatus, const char *szProto, const TCHAR *szStatus, HANDLE hContact, DWORD fromEvent)
+{
+ if (PluginConfig.g_hMenuTrayUnread != 0 && hContact != 0 && szProto != NULL) {
+ TCHAR szMenuEntry[80], *tszFinalProto = NULL;
+ MENUITEMINFO mii = {0};
+ WORD wMyStatus;
+ const TCHAR *szMyStatus;
+ const TCHAR *szNick = NULL;
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA | MIIM_ID | MIIM_BITMAP;
+
+ if (szProto == NULL)
+ return 0; // should never happen...
+
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)CallService(MS_PROTO_GETACCOUNT, (WPARAM)0, (LPARAM)szProto);
+
+ tszFinalProto = (acc && acc->tszAccountName ? acc->tszAccountName : 0);
+
+ if(tszFinalProto == 0)
+ return(0); // should also NOT happen
+
+ wMyStatus = (wStatus == 0) ? DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE) : wStatus;
+ szMyStatus = (szStatus == NULL) ? (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)wMyStatus, GSMDF_TCHAR) : szStatus;
+ mii.wID = (UINT)hContact;
+ mii.hbmpItem = HBMMENU_CALLBACK;
+
+ if (dat != 0) {
+ szNick = dat->cache->getNick();
+ GetMenuItemInfo(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)hContact, FALSE, &mii);
+ mii.dwItemData++;
+ if (fromEvent == 2) // from chat...
+ mii.dwItemData |= 0x10000000;
+ DeleteMenu(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)hContact, MF_BYCOMMAND);
+ mir_sntprintf(szMenuEntry, safe_sizeof(szMenuEntry), _T("%s: %s (%s) [%d]"), tszFinalProto, szNick, szMyStatus, mii.dwItemData & 0x0000ffff);
+ AppendMenu(PluginConfig.g_hMenuTrayUnread, MF_BYCOMMAND | MF_STRING, (UINT_PTR)hContact, szMenuEntry);
+ PluginConfig.m_UnreadInTray++;
+ if (PluginConfig.m_UnreadInTray)
+ SetEvent(g_hEvent);
+ SetMenuItemInfo(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)hContact, FALSE, &mii);
+ } else {
+ szNick = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
+ if (CheckMenuItem(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)hContact, MF_BYCOMMAND | MF_UNCHECKED) == -1) {
+ mir_sntprintf(szMenuEntry, safe_sizeof(szMenuEntry), _T("%s: %s (%s) [%d]"), tszFinalProto, szNick, szMyStatus, fromEvent ? 1 : 0);
+ AppendMenu(PluginConfig.g_hMenuTrayUnread, MF_BYCOMMAND | MF_STRING, (UINT_PTR)hContact, szMenuEntry);
+ mii.dwItemData = fromEvent ? 1 : 0;
+ PluginConfig.m_UnreadInTray += (mii.dwItemData & 0x0000ffff);
+ if (PluginConfig.m_UnreadInTray)
+ SetEvent(g_hEvent);
+ if (fromEvent == 2)
+ mii.dwItemData |= 0x10000000;
+ } else {
+ GetMenuItemInfo(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)hContact, FALSE, &mii);
+ mii.dwItemData += (fromEvent ? 1 : 0);
+ PluginConfig.m_UnreadInTray += (fromEvent ? 1 : 0);
+ if (PluginConfig.m_UnreadInTray)
+ SetEvent(g_hEvent);
+ mii.fMask |= MIIM_STRING;
+ if (fromEvent == 2)
+ mii.dwItemData |= 0x10000000;
+ mir_sntprintf(szMenuEntry, safe_sizeof(szMenuEntry), _T("%s: %s (%s) [%d]"), tszFinalProto, szNick, szMyStatus, mii.dwItemData & 0x0000ffff);
+ mii.cch = lstrlen(szMenuEntry) + 1;
+ mii.dwTypeData = (LPTSTR)szMenuEntry;
+ }
+ SetMenuItemInfo(PluginConfig.g_hMenuTrayUnread, (UINT_PTR)hContact, FALSE, &mii);
+ }
+ }
+ return 0;
+}
+
+
+int tabSRMM_ShowPopup(WPARAM wParam, LPARAM lParam, WORD eventType, int windowOpen, struct TContainerData *pContainer, HWND hwndChild, const char *szProto, struct TWindowData *dat)
+{
+ int heFlags;
+
+ if (nen_options.iDisable) // no popups at all. Period
+ return(0);
+
+ PU_CleanUp();
+
+ heFlags = HistoryEvents_GetFlags(eventType);
+ if (heFlags != -1 && !(heFlags & HISTORYEVENTS_FLAG_DEFAULT)) // Filter history events popups
+ return 0;
+
+ if (nen_options.bDisableNonMessage && eventType != EVENTTYPE_MESSAGE)
+ return(0);
+
+ /*
+ * check the status mode against the status mask
+ */
+
+ if (nen_options.dwStatusMask != -1) {
+ DWORD dwStatus = 0;
+ if (szProto != NULL) {
+ dwStatus = (DWORD)CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ if (!(dwStatus == 0 || dwStatus <= ID_STATUS_OFFLINE || ((1 << (dwStatus - ID_STATUS_ONLINE)) & nen_options.dwStatusMask))) // should never happen, but...
+ return 0;
+ }
+ }
+ if (nen_options.bNoRSS && szProto != NULL && !strncmp(szProto, "RSS", 3))
+ return 0; // filter out RSS popups
+ //
+ if (windowOpen && pContainer != 0) { // message window is open, need to check the container config if we want to see a popup nonetheless
+ if (nen_options.bWindowCheck && windowOpen) // no popups at all for open windows... no exceptions
+ return(0);
+ if (pContainer->dwFlags & CNT_DONTREPORT && (IsIconic(pContainer->hwnd))) // in tray counts as "minimised"
+ goto passed;
+ if (pContainer->dwFlags & CNT_DONTREPORTUNFOCUSED) {
+ if (!IsIconic(pContainer->hwnd) && GetForegroundWindow() != pContainer->hwnd && GetActiveWindow() != pContainer->hwnd)
+ goto passed;
+ }
+ if (pContainer->dwFlags & CNT_ALWAYSREPORTINACTIVE) {
+ if (pContainer->dwFlags & CNT_DONTREPORTFOCUSED)
+ goto passed;
+
+ if (pContainer->hwndActive == hwndChild)
+ return 0;
+ else
+ goto passed;
+ }
+ return 0;
+ }
+passed:
+ if (!(PluginConfig.g_PopupAvail && PluginConfig.g_PopupWAvail))
+ return 0;
+
+ if(PU_GetByContact((HANDLE)wParam) && nen_options.bMergePopup && eventType == EVENTTYPE_MESSAGE) {
+ if(PopupUpdateT((HANDLE)wParam, (HANDLE)lParam) != 0)
+ PopupShowT(&nen_options, (HANDLE)wParam, (HANDLE)lParam, (UINT)eventType, pContainer ? pContainer->hwnd : 0);
+ }
+ else
+ PopupShowT(&nen_options, (HANDLE)wParam, (HANDLE)lParam, (UINT)eventType, pContainer ? pContainer->hwnd : 0);
+
+ return 0;
+}
+
+/**
+ * remove all popups for hContact, but only if the mask matches the current "mode"
+ */
+
+void TSAPI DeletePopupsForContact(HANDLE hContact, DWORD dwMask)
+{
+ int i = 0;
+ PLUGIN_DATAT* _T = 0;
+
+ if (!(dwMask & nen_options.dwRemoveMask) || nen_options.iDisable || !PluginConfig.g_PopupAvail)
+ return;
+
+ while ((_T = const_cast<PLUGIN_DATAT *>(PU_GetByContact(hContact))) != 0) {
+ _T->hContact = 0; // make sure, it never "comes back"
+ if (_T->hWnd != 0 && IsWindow(_T->hWnd))
+ PUDeletePopUp(_T->hWnd);
+ }
+}
diff --git a/plugins/TabSRMM/src/generic_msghandlers.cpp b/plugins/TabSRMM/src/generic_msghandlers.cpp new file mode 100644 index 0000000000..d507076eae --- /dev/null +++ b/plugins/TabSRMM/src/generic_msghandlers.cpp @@ -0,0 +1,2419 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: generic_msghandlers.cpp 13587 2011-04-12 13:54:26Z george.hazan $
+ *
+ * these are generic message handlers which are used by the message dialog window procedure.
+ * calling them directly instead of using SendMessage() is faster.
+ * also contains various callback functions for custom buttons
+ */
+
+
+#include "commonheaders.h"
+
+extern RECT rcLastStatusBarClick;
+
+
+/**
+ * Save message log for given session as RTF document
+ */
+void TSAPI DM_SaveLogAsRTF(const TWindowData* dat)
+{
+ TCHAR szFilename[MAX_PATH];
+ OPENFILENAME ofn = {0};
+ EDITSTREAM stream = { 0 };
+ TCHAR szFilter[MAX_PATH];
+
+ if (dat && dat->hwndIEView != 0) {
+ IEVIEWEVENT event = {0};
+
+ event.cbSize = sizeof(IEVIEWEVENT);
+ event.hwnd = dat->hwndIEView;
+ event.hContact = dat->hContact;
+ event.iType = IEE_SAVE_DOCUMENT;
+ event.dwFlags = 0;
+ event.count = 0;
+ event.codepage = 0;
+ CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+ } else if(dat) {
+ TCHAR szInitialDir[MAX_PATH + 2];
+
+ mir_sntprintf(szFilter, SIZEOF(szFilter), _T("%s%c*.rtf%c%c"), TranslateT("Rich Edit file"), 0, 0, 0);
+ mir_sntprintf(szFilename, MAX_PATH, _T("%s.rtf"), dat->cache->getNick());
+
+ Utils::sanitizeFilename(szFilename);
+
+ mir_sntprintf(szInitialDir, MAX_PATH, _T("%s%s\\"), M->getDataPath(), _T("\\Saved message logs"));
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szInitialDir);
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = dat->hwnd;
+ ofn.lpstrFile = szFilename;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrInitialDir = szInitialDir;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.lpstrDefExt = _T("rtf");
+ if (GetSaveFileName(&ofn)) {
+ stream.dwCookie = (DWORD_PTR)szFilename;
+ stream.dwError = 0;
+ stream.pfnCallback = Utils::StreamOut;
+ SendDlgItemMessage(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG, EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM) & stream);
+ }
+ }
+}
+
+/**
+ * This is broadcasted by the container to all child windows to check if the
+ * container can be autohidden or -closed.
+ *
+ * wParam is the autohide timeout (in seconds)
+ * lParam points to a BOOL and a session which wants to prevent auto-hiding
+ * the container must set it to FALSE.
+ *
+ * If no session in the container disagrees, the container will be hidden.
+ */
+void TSAPI DM_CheckAutoHide(const TWindowData* dat, WPARAM wParam, LPARAM lParam)
+{
+ if(dat && lParam) {
+ BOOL *fResult = (BOOL *)lParam;
+
+ if(GetWindowTextLengthA(GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE)) > 0) {
+ *fResult = FALSE;
+ return; // text entered in the input area -> prevent autohide/cose
+ }
+ if(dat->dwUnread) {
+ *fResult = FALSE;
+ return; // unread events, do not hide or close the container
+ }
+ if(((GetTickCount() - dat->dwLastActivity) / 1000) <= wParam)
+ *fResult = FALSE; // time since last activity did not yet reach the threshold.
+ }
+}
+/**
+ * checks if the balloon tooltip can be dismissed (usually called by
+ * WM_MOUSEMOVE events
+ */
+
+void TSAPI DM_DismissTip(TWindowData *dat, const POINT& pt)
+{
+ RECT rc;
+
+ if(!IsWindowVisible(dat->hwndTip))
+ return;
+
+ GetWindowRect(dat->hwndTip, &rc);
+ if(PtInRect(&rc, pt))
+ return;
+
+ if(abs(pt.x - dat->ptTipActivation.x) > 5 || abs(pt.y - dat->ptTipActivation.y) > 5) {
+ SendMessage(dat->hwndTip, TTM_TRACKACTIVATE, FALSE, 0);
+ dat->ptTipActivation.x = dat->ptTipActivation.y = 0;
+ }
+}
+
+/**
+ * initialize the balloon tooltip for message window notifications
+ */
+void TSAPI DM_InitTip(TWindowData *dat)
+{
+ dat->hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, dat->hwnd, NULL, g_hInst, (LPVOID) NULL);
+
+ ZeroMemory((void *)&dat->ti, sizeof(dat->ti));
+ dat->ti.cbSize = sizeof(dat->ti);
+ dat->ti.lpszText = PluginConfig.m_szNoStatus;
+ dat->ti.hinst = g_hInst;
+ dat->ti.hwnd = dat->hwnd;
+ dat->ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT;
+ dat->ti.uId = (UINT_PTR)dat->hwnd;
+ SendMessageA(dat->hwndTip, TTM_ADDTOOLA, 0, (LPARAM)&dat->ti);
+ SetWindowPos(dat->hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+/**
+ * checks generic hotkeys valid for both IM and MUC sessions
+ *
+ * returns 1 for handled hotkeys, 0 otherwise.
+ */
+LRESULT TSAPI DM_GenericHotkeysCheck(MSG *message, TWindowData *dat)
+{
+ LRESULT mim_hotkey_check = CallService(MS_HOTKEY_CHECK, (WPARAM)message, (LPARAM)(TABSRMM_HK_SECTION_GENERIC));
+ HWND hwndDlg = dat->hwnd;
+
+ switch(mim_hotkey_check) {
+ case TABSRMM_HK_PASTEANDSEND:
+ HandlePasteAndSend(dat);
+ return(1);
+ case TABSRMM_HK_HISTORY:
+ SendMessage(hwndDlg, WM_COMMAND, IDC_HISTORY, 0);
+ return(1);
+ case TABSRMM_HK_CONTAINEROPTIONS:
+ if (dat->pContainer->hWndOptions == 0)
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CONTAINEROPTIONS), dat->pContainer->hwnd,
+ DlgProcContainerOptions, (LPARAM)dat->pContainer);
+ return(1);
+ case TABSRMM_HK_SEND:
+ if (!(GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MESSAGE), GWL_STYLE) & ES_READONLY)) {
+ PostMessage(hwndDlg, WM_COMMAND, IDOK, 0);
+ return(1);
+ }
+ break;
+ case TABSRMM_HK_TOGGLEINFOPANEL:
+ dat->Panel->setActive(dat->Panel->isActive() ? FALSE : TRUE);
+ dat->Panel->showHide();
+ return(1);
+ case TABSRMM_HK_EMOTICONS:
+ SendMessage(hwndDlg, WM_COMMAND, IDC_SMILEYBTN, 0);
+ return(1);
+ case TABSRMM_HK_TOGGLETOOLBAR:
+ SendMessage(hwndDlg, WM_COMMAND, IDC_TOGGLETOOLBAR, 0);
+ return(1);
+ case TABSRMM_HK_CLEARLOG:
+ ClearLog(dat);
+ return(1);
+ case TABSRMM_HK_TOGGLESIDEBAR:
+ if(dat->pContainer->SideBar->isActive())
+ SendMessage(hwndDlg, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ return(1);
+ default:
+ break;
+ }
+ return(0);
+}
+
+LRESULT TSAPI DM_MsgWindowCmdHandler(HWND hwndDlg, TContainerData *m_pContainer, TWindowData *dat, UINT cmd, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndContainer = m_pContainer->hwnd;
+
+ switch(cmd) {
+ case IDC_FONTBOLD:
+ case IDC_FONTITALIC:
+ case IDC_FONTUNDERLINE:
+ case IDC_FONTSTRIKEOUT: {
+ CHARFORMAT2 cf, cfOld;
+ int cmd = LOWORD(wParam);
+ BOOL isBold, isItalic, isUnderline, isStrikeout;
+
+ if (dat->SendFormat == 0) // dont use formatting if disabled
+ break;
+
+ ZeroMemory(&cf, sizeof(CHARFORMAT2));
+ ZeroMemory(&cfOld, sizeof(CHARFORMAT2));
+ cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2);
+ cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld);
+ isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD);
+ isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC);
+ isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE);
+ isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT);
+
+ if (cmd == IDC_FONTBOLD && !IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FONTBOLD)))
+ break;
+ if (cmd == IDC_FONTITALIC && !IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FONTITALIC)))
+ break;
+ if (cmd == IDC_FONTUNDERLINE && !IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FONTUNDERLINE)))
+ break;
+ if (cmd == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FONTSTRIKEOUT)))
+ break;
+ if (cmd == IDC_FONTBOLD) {
+ cf.dwEffects = isBold ? 0 : CFE_BOLD;
+ cf.dwMask = CFM_BOLD;
+ CheckDlgButton(hwndDlg, IDC_FONTBOLD, !isBold);
+ } else if (cmd == IDC_FONTITALIC) {
+ cf.dwEffects = isItalic ? 0 : CFE_ITALIC;
+ cf.dwMask = CFM_ITALIC;
+ CheckDlgButton(hwndDlg, IDC_FONTITALIC, !isItalic);
+ } else if (cmd == IDC_FONTUNDERLINE) {
+ cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE;
+ cf.dwMask = CFM_UNDERLINE;
+ CheckDlgButton(hwndDlg, IDC_FONTUNDERLINE, !isUnderline);
+ } else if (cmd == IDC_FONTSTRIKEOUT) {
+ cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT;
+ cf.dwMask = CFM_STRIKEOUT;
+ CheckDlgButton(hwndDlg, IDC_FONTSTRIKEOUT, !isStrikeout);
+ }
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ break;
+ }
+ case IDC_FONTFACE: {
+ HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, 7);
+ RECT rc;
+ int iSelection, i;
+ CHARFORMAT2 cf;
+
+ ZeroMemory(&cf, sizeof(CHARFORMAT2));
+ cf.cbSize = sizeof(CHARFORMAT2);
+ cf.dwMask = CFM_COLOR;
+ cf.dwEffects = 0;
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_FONTFACE), &rc);
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ if (iSelection == ID_FONT_CLEARALLFORMATTING) {
+ cf.dwMask = CFM_BOLD | CFM_COLOR | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
+ cf.crTextColor = M->GetDword(FONTMODULE, "Font16Col", 0);
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ break;
+ }
+ if (iSelection == ID_FONT_DEFAULTCOLOR) {
+ int i = 0;
+ cf.crTextColor = M->GetDword(FONTMODULE, "Font16Col", 0);
+ for (i = 0; i < Utils::rtf_ctable_size; i++) {
+ if (Utils::rtf_ctable[i].clr == cf.crTextColor)
+ cf.crTextColor = RGB(GetRValue(cf.crTextColor), GetGValue(cf.crTextColor), GetBValue(cf.crTextColor) == 0 ? GetBValue(cf.crTextColor) + 1 : GetBValue(cf.crTextColor) - 1);
+ }
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ break;
+ }
+ for (i = 0; i < RTF_CTABLE_DEFSIZE; i++) {
+ if (Utils::rtf_ctable[i].menuid == iSelection) {
+ cf.crTextColor = Utils::rtf_ctable[i].clr;
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ }
+ }
+ break;
+ }
+
+ case IDCANCEL: {
+ ShowWindow(hwndContainer, SW_MINIMIZE);
+ return FALSE;
+ }
+
+ case IDC_SAVE:
+ SendMessage(hwndDlg, WM_CLOSE, 1, 0);
+ break;
+
+ case IDC_NAME: {
+ if (GetKeyState(VK_SHIFT) & 0x8000) // copy UIN
+ SendMessage(hwndDlg, DM_UINTOCLIPBOARD, 0, 0);
+ else {
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM) (dat->cache->getActiveContact()), 0);
+ }
+ break;
+ }
+
+ case IDC_HISTORY:
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, (WPARAM) dat->hContact, 0);
+ break;
+
+ case IDC_SMILEYBTN:
+ if (dat->doSmileys && PluginConfig.g_SmileyAddAvail) {
+ RECT rc;
+ HANDLE hContact = dat->cache->getActiveContact();
+
+ if (CheckValidSmileyPack(dat->cache->getActiveProto(), hContact) != 0) {
+ SMADD_SHOWSEL3 smaddInfo = {0};
+
+ if (lParam == 0)
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_SMILEYBTN), &rc);
+ else
+ GetWindowRect((HWND)lParam, &rc);
+ smaddInfo.cbSize = sizeof(SMADD_SHOWSEL3);
+ smaddInfo.hwndTarget = GetDlgItem(hwndDlg, IDC_MESSAGE);
+ smaddInfo.targetMessage = EM_REPLACESEL;
+ smaddInfo.targetWParam = TRUE;
+ smaddInfo.Protocolname = const_cast<char *>(dat->cache->getActiveProto());
+ smaddInfo.Direction = 0;
+ smaddInfo.xPosition = rc.left;
+ smaddInfo.yPosition = rc.top + 24;
+ smaddInfo.hwndParent = hwndContainer;
+ smaddInfo.hContact = hContact;
+ CallService(MS_SMILEYADD_SHOWSELECTION, (WPARAM)hwndContainer, (LPARAM) &smaddInfo);
+ }
+ }
+ break;
+ case IDC_TIME: {
+ RECT rc;
+ HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, 2);
+ int iSelection, isHandled;
+ DWORD dwOldFlags = dat->dwFlags;
+ DWORD dwOldEventIsShown = dat->dwFlagsEx;
+
+ MsgWindowUpdateMenu(dat, submenu, MENU_LOGMENU);
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_TIME), &rc);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ isHandled = MsgWindowMenuHandler(dat, iSelection, MENU_LOGMENU);
+ return(isHandled);
+ }
+ case IDC_PROTOMENU: {
+ RECT rc;
+ HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, 4);
+ int iSelection;
+ int iOldGlobalSendFormat = PluginConfig.m_SendFormat;
+
+ if (dat->hContact) {
+ int iLocalFormat = M->GetDword(dat->hContact, "sendformat", 0);
+ int iNewLocalFormat = iLocalFormat;
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_PROTOCOL), &rc);
+
+ CheckMenuItem(submenu, ID_MODE_GLOBAL, MF_BYCOMMAND | (!(dat->dwFlagsEx & MWF_SHOW_SPLITTEROVERRIDE) ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_MODE_PRIVATE, MF_BYCOMMAND | (dat->dwFlagsEx & MWF_SHOW_SPLITTEROVERRIDE ? MF_CHECKED : MF_UNCHECKED));
+
+ /*
+ * formatting menu..
+ */
+
+ CheckMenuItem(submenu, ID_GLOBAL_BBCODE, MF_BYCOMMAND | ((PluginConfig.m_SendFormat) ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_GLOBAL_OFF, MF_BYCOMMAND | ((PluginConfig.m_SendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED));
+
+ CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, MF_BYCOMMAND | ((iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, MF_BYCOMMAND | ((iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_THISCONTACT_OFF, MF_BYCOMMAND | ((iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED));
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ switch (iSelection) {
+ case ID_MODE_GLOBAL:
+ dat->dwFlagsEx &= ~(MWF_SHOW_SPLITTEROVERRIDE);
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "splitoverride", 0);
+ LoadSplitter(dat);
+ AdjustBottomAvatarDisplay(dat);
+ DM_RecalcPictureSize(dat);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ break;
+ case ID_MODE_PRIVATE:
+ dat->dwFlagsEx |= MWF_SHOW_SPLITTEROVERRIDE;
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "splitoverride", 1);
+ LoadSplitter(dat);
+ AdjustBottomAvatarDisplay(dat);
+ DM_RecalcPictureSize(dat);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ break;
+ case ID_GLOBAL_BBCODE:
+ PluginConfig.m_SendFormat = SENDFORMAT_BBCODE;
+ break;
+ case ID_GLOBAL_OFF:
+ PluginConfig.m_SendFormat = SENDFORMAT_NONE;
+ break;
+ case ID_THISCONTACT_GLOBALSETTING:
+ iNewLocalFormat = 0;
+ break;
+ case ID_THISCONTACT_BBCODE:
+ iNewLocalFormat = SENDFORMAT_BBCODE;
+ break;
+ case ID_THISCONTACT_OFF:
+ iNewLocalFormat = -1;
+ break;
+ }
+ if (iNewLocalFormat == 0)
+ DBDeleteContactSetting(dat->hContact, SRMSGMOD_T, "sendformat");
+ else if (iNewLocalFormat != iLocalFormat)
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat);
+
+ if (PluginConfig.m_SendFormat != iOldGlobalSendFormat)
+ M->WriteByte(SRMSGMOD_T, "sendformat", (BYTE)PluginConfig.m_SendFormat);
+ if (iNewLocalFormat != iLocalFormat || PluginConfig.m_SendFormat != iOldGlobalSendFormat) {
+ dat->SendFormat = M->GetDword(dat->hContact, "sendformat", PluginConfig.m_SendFormat);
+ if (dat->SendFormat == -1) // per contact override to disable it..
+ dat->SendFormat = 0;
+ M->BroadcastMessage(DM_CONFIGURETOOLBAR, 0, 1);
+ }
+ }
+ break;
+ }
+ case IDC_TOGGLETOOLBAR:
+ if (lParam == 1)
+ ApplyContainerSetting(m_pContainer, CNT_NOMENUBAR, m_pContainer->dwFlags & CNT_NOMENUBAR ? 0 : 1, true);
+ else
+ ApplyContainerSetting(m_pContainer, CNT_HIDETOOLBAR, m_pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1, true);
+ break;
+ case IDC_INFOPANELMENU: {
+ RECT rc;
+ int iSelection;
+
+ HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, 9);
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_NAME), &rc);
+
+ EnableMenuItem(submenu, ID_FAVORITES_ADDCONTACTTOFAVORITES, !dat->cache->isFavorite() ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(submenu, ID_FAVORITES_REMOVECONTACTFROMFAVORITES, !dat->cache->isFavorite() ? MF_GRAYED : MF_ENABLED);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+
+ switch(iSelection) {
+ case ID_FAVORITES_ADDCONTACTTOFAVORITES:
+ DBWriteContactSettingByte(dat->hContact, SRMSGMOD_T, "isFavorite", 1);
+ AddContactToFavorites(dat->hContact, dat->cache->getNick(), dat->cache->getActiveProto(), dat->szStatus, dat->wStatus, LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getActiveStatus()), 1, PluginConfig.g_hMenuFavorites);
+ break;
+ case ID_FAVORITES_REMOVECONTACTFROMFAVORITES:
+ DBWriteContactSettingByte(dat->hContact, SRMSGMOD_T, "isFavorite", 0);
+ DeleteMenu(PluginConfig.g_hMenuFavorites, (UINT_PTR)dat->hContact, MF_BYCOMMAND);
+ break;
+ default:
+ break;
+ }
+ dat->cache->updateFavorite();
+ break;
+ }
+ case IDC_SENDMENU: {
+ RECT rc;
+ HMENU submenu = GetSubMenu(m_pContainer->hMenuContext, 3);
+ int iSelection;
+
+ GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rc);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, MF_BYCOMMAND | (dat->sendMode & SMODE_MULTIPLE ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, MF_BYCOMMAND | (dat->sendMode == 0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, MF_BYCOMMAND | (dat->sendMode & SMODE_CONTAINER ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_SENDMENU_FORCEANSISEND, MF_BYCOMMAND | (dat->sendMode & SMODE_FORCEANSI ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, MF_BYCOMMAND | (dat->sendMode & SMODE_SENDLATER ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, MF_BYCOMMAND | (dat->sendMode & SMODE_NOACK ? MF_CHECKED : MF_UNCHECKED));
+ {
+ const char *szFinalProto = dat->cache->getActiveProto();
+ char szServiceName[128];
+
+ mir_snprintf(szServiceName, 128, "%s/SendNudge", szFinalProto);
+ EnableMenuItem(submenu, ID_SENDMENU_SENDNUDGE, MF_BYCOMMAND | ((ServiceExists(szServiceName) && ServiceExists(MS_NUDGE_SEND)) ? MF_ENABLED : MF_GRAYED));
+ }
+ if (lParam)
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+ else
+ iSelection = HIWORD(wParam);
+
+ switch (iSelection) {
+ case ID_SENDMENU_SENDTOMULTIPLEUSERS:
+ dat->sendMode ^= SMODE_MULTIPLE;
+ if (dat->sendMode & SMODE_MULTIPLE)
+ HWND hwndClist = DM_CreateClist(dat);
+ else {
+ if (IsWindow(GetDlgItem(hwndDlg, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(hwndDlg, IDC_CLIST));
+ }
+ break;
+ case ID_SENDMENU_SENDNUDGE:
+ SendNudge(dat);
+ break;
+ case ID_SENDMENU_SENDDEFAULT:
+ dat->sendMode = 0;
+ break;
+ case ID_SENDMENU_SENDTOCONTAINER:
+ dat->sendMode ^= SMODE_CONTAINER;
+ RedrawWindow(hwndDlg, 0, 0, RDW_ERASENOW|RDW_UPDATENOW);
+ break;
+ case ID_SENDMENU_FORCEANSISEND:
+ dat->sendMode ^= SMODE_FORCEANSI;
+ break;
+ case ID_SENDMENU_SENDLATER:
+ if(sendLater->isAvail())
+ dat->sendMode ^= SMODE_SENDLATER;
+ else
+ CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK|MB_ICONINFORMATION, CTranslator::get(CTranslator::QMGR_ERROR_NOMULTISEND));
+ break;
+ case ID_SENDMENU_SENDWITHOUTTIMEOUTS:
+ dat->sendMode ^= SMODE_NOACK;
+ if (dat->sendMode & SMODE_NOACK)
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "no_ack", 1);
+ else
+ DBDeleteContactSetting(dat->hContact, SRMSGMOD_T, "no_ack");
+ break;
+ }
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "no_ack", (BYTE)(dat->sendMode & SMODE_NOACK ? 1 : 0));
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "forceansi", (BYTE)(dat->sendMode & SMODE_FORCEANSI ? 1 : 0));
+ SetWindowPos(GetDlgItem(hwndDlg, IDC_MESSAGE), 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
+ if (dat->sendMode & SMODE_MULTIPLE || dat->sendMode & SMODE_CONTAINER) {
+ 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 {
+ if (IsWindow(GetDlgItem(hwndDlg, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(hwndDlg, IDC_CLIST));
+ 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);
+ }
+ SendMessage(hwndContainer, DM_QUERYCLIENTAREA, 0, (LPARAM)&rc);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ DM_ScrollToBottom(dat, 1, 1);
+ 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);
+ break;
+ }
+ case IDC_TOGGLESIDEBAR: {
+ SendMessage(m_pContainer->hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ break;
+ }
+ case IDC_PIC: {
+ RECT rc;
+ GetClientRect(hwndDlg, &rc);
+
+ dat->fEditNotesActive = !dat->fEditNotesActive;
+ if(dat->fEditNotesActive) {
+ int iLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ if(iLen != 0) {
+ SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)CTranslator::get(CTranslator::GEN_MSG_NO_EDIT_NOTES));
+ dat->fEditNotesActive = false;
+ break;
+ }
+
+ if(!dat->fIsAutosizingInput) {
+ dat->iSplitterSaved = dat->splitterY;
+ dat->splitterY = rc.bottom / 2;
+ SendMessage(hwndDlg, WM_SIZE, 1, 1);
+ }
+
+ DBVARIANT dbv = {0};
+
+ if(0 == M->GetTString(dat->hContact, "UserInfo", "MyNotes", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_MESSAGE, dbv.ptszVal);
+ mir_free(dbv.ptszVal);
+ }
+ }
+ else {
+ int iLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE));
+
+ TCHAR *buf = (TCHAR *)mir_alloc((iLen + 2) * sizeof(TCHAR));
+ GetDlgItemText(hwndDlg, IDC_MESSAGE, buf, iLen + 1);
+ M->WriteTString(dat->hContact, "UserInfo", "MyNotes", buf);
+ SetDlgItemText(hwndDlg, IDC_MESSAGE, _T(""));
+
+ if(!dat->fIsAutosizingInput) {
+ dat->splitterY = dat->iSplitterSaved;
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ DM_ScrollToBottom(dat, 0, 1);
+ }
+ }
+ 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_FRAME|RDW_UPDATENOW|RDW_ALLCHILDREN);
+
+ if(dat->fEditNotesActive)
+ CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK|MB_ICONINFORMATION);
+ break;
+ }
+ case IDM_CLEAR:
+ ClearLog(dat);
+ break;
+ case IDC_PROTOCOL: {
+ RECT rc;
+ int iSel = 0;
+
+ HMENU hMenu = (HMENU) CallService(MS_CLIST_MENUBUILDCONTACT, (WPARAM) dat->hContact, 0);
+ if(lParam == 0)
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_PROTOCOL/*IDC_NAME*/), &rc);
+ else
+ GetWindowRect((HWND)lParam, &rc);
+ iSel = TrackPopupMenu(hMenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, NULL);
+
+ if(iSel)
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(iSel), MPCF_CONTACTMENU), (LPARAM) dat->hContact);
+
+ DestroyMenu(hMenu);
+ break;
+ }
+ // error control
+ case IDC_CANCELSEND:
+ SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_CANCEL, 0);
+ break;
+ case IDC_RETRY:
+ SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_RETRY, 0);
+ break;
+ case IDC_MSGSENDLATER:
+ SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_SENDLATER, 0);
+ break;
+ case IDC_SELFTYPING:
+ if (dat->hContact) {
+ int iCurrentTypingMode = M->GetByte(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, M->GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW));
+
+ if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) {
+ DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF);
+ dat->nTypeMode = PROTOTYPE_SELFTYPING_OFF;
+ }
+ M->WriteByte(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, (BYTE)!iCurrentTypingMode);
+ }
+ break;
+ default:
+ return(0);
+ }
+ return(1);
+}
+
+LRESULT TSAPI DM_ContainerCmdHandler(TContainerData *pContainer, UINT cmd, WPARAM wParam, LPARAM lParam)
+{
+ if(!pContainer)
+ return(0);
+
+ HWND hwndDlg = pContainer->hwnd;
+
+ switch(cmd) {
+ case IDC_CLOSE:
+ SendMessage(hwndDlg, WM_SYSCOMMAND, SC_CLOSE, 0);
+ break;
+ case IDC_MINIMIZE:
+ PostMessage(hwndDlg, WM_SYSCOMMAND, SC_MINIMIZE, 0);
+ break;
+ case IDC_MAXIMIZE:
+ SendMessage(hwndDlg, WM_SYSCOMMAND, IsZoomed(hwndDlg) ? SC_RESTORE : SC_MAXIMIZE, 0);
+ break;
+ case IDOK:
+ SendMessage(pContainer->hwndActive, WM_COMMAND, wParam, lParam); // pass the IDOK command to the active child - fixes the "enter not working
+ break;
+ case ID_FILE_SAVEMESSAGELOGAS:
+ SendMessage(pContainer->hwndActive, DM_SAVEMESSAGELOG, 0, 0);
+ break;
+ case ID_FILE_CLOSEMESSAGESESSION:
+ PostMessage(pContainer->hwndActive, WM_CLOSE, 0, 1);
+ break;
+ case ID_FILE_CLOSE:
+ PostMessage(hwndDlg, WM_CLOSE, 0, 1);
+ break;
+ case ID_VIEW_SHOWSTATUSBAR:
+ ApplyContainerSetting(pContainer, CNT_NOSTATUSBAR, pContainer->dwFlags & CNT_NOSTATUSBAR ? 0 : 1, true);
+ break;
+ case ID_VIEW_VERTICALMAXIMIZE:
+ ApplyContainerSetting(pContainer, CNT_VERTICALMAX, pContainer->dwFlags & CNT_VERTICALMAX ? 0 : 1, false);
+ break;
+ case ID_VIEW_BOTTOMTOOLBAR:
+ ApplyContainerSetting(pContainer, CNT_BOTTOMTOOLBAR, pContainer->dwFlags & CNT_BOTTOMTOOLBAR ? 0 : 1, false);
+ M->BroadcastMessage(DM_CONFIGURETOOLBAR, 0, 1);
+ return 0;
+ case ID_VIEW_SHOWTOOLBAR:
+ ApplyContainerSetting(pContainer, CNT_HIDETOOLBAR, pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1, false);
+ M->BroadcastMessage(DM_CONFIGURETOOLBAR, 0, 1);
+ return 0;
+ case ID_VIEW_SHOWMENUBAR:
+ ApplyContainerSetting(pContainer, CNT_NOMENUBAR, pContainer->dwFlags & CNT_NOMENUBAR ? 0 : 1, true);
+ break;
+ case ID_VIEW_SHOWTITLEBAR:
+ ApplyContainerSetting(pContainer, CNT_NOTITLE, pContainer->dwFlags & CNT_NOTITLE ? 0 : 1, true);
+ break;
+ case ID_VIEW_TABSATBOTTOM:
+ ApplyContainerSetting(pContainer, CNT_TABSBOTTOM, pContainer->dwFlags & CNT_TABSBOTTOM ? 0 : 1, false);
+ break;
+ case ID_VIEW_SHOWMULTISENDCONTACTLIST:
+ SendMessage(pContainer->hwndActive, WM_COMMAND, MAKEWPARAM(IDC_SENDMENU, ID_SENDMENU_SENDTOMULTIPLEUSERS), 0);
+ break;
+ case ID_VIEW_STAYONTOP:
+ SendMessage(hwndDlg, WM_SYSCOMMAND, IDM_STAYONTOP, 0);
+ break;
+ case ID_CONTAINER_CONTAINEROPTIONS:
+ SendMessage(hwndDlg, WM_SYSCOMMAND, IDM_MOREOPTIONS, 0);
+ break;
+ case ID_EVENTPOPUPS_DISABLEALLEVENTPOPUPS:
+ ApplyContainerSetting(pContainer, (CNT_DONTREPORT | CNT_DONTREPORTUNFOCUSED | CNT_DONTREPORTFOCUSED | CNT_ALWAYSREPORTINACTIVE), 0, false);
+ return 0;
+ case ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISMINIMIZED:
+ ApplyContainerSetting(pContainer, CNT_DONTREPORT, pContainer->dwFlags & CNT_DONTREPORT ? 0 : 1, false);
+ return 0;
+ case ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISUNFOCUSED:
+ ApplyContainerSetting(pContainer, CNT_DONTREPORTUNFOCUSED, pContainer->dwFlags & CNT_DONTREPORTUNFOCUSED ? 0 : 1, false);
+ return 0;
+ case ID_EVENTPOPUPS_SHOWPOPUPSIFWINDOWISFOCUSED:
+ ApplyContainerSetting(pContainer, CNT_DONTREPORTFOCUSED, pContainer->dwFlags & CNT_DONTREPORTFOCUSED ? 0 : 1, false);
+ return 0;
+ case ID_EVENTPOPUPS_SHOWPOPUPSFORALLINACTIVESESSIONS:
+ ApplyContainerSetting(pContainer, CNT_ALWAYSREPORTINACTIVE, pContainer->dwFlags & CNT_ALWAYSREPORTINACTIVE ? 0 : 1, false);
+ return 0;
+ case ID_WINDOWFLASHING_DISABLEFLASHING:
+ ApplyContainerSetting(pContainer, CNT_NOFLASH, 1, false);
+ ApplyContainerSetting(pContainer, CNT_FLASHALWAYS, 0, false);
+ return 0;
+ case ID_WINDOWFLASHING_FLASHUNTILFOCUSED:
+ ApplyContainerSetting(pContainer, CNT_NOFLASH, 0, false);
+ ApplyContainerSetting(pContainer, CNT_FLASHALWAYS, 1, false);
+ return 0;
+ case ID_WINDOWFLASHING_USEDEFAULTVALUES:
+ ApplyContainerSetting(pContainer, (CNT_NOFLASH | CNT_FLASHALWAYS), 0, false);
+ return 0;
+ case ID_OPTIONS_SAVECURRENTWINDOWPOSITIONASDEFAULT: {
+ WINDOWPLACEMENT wp = {0};
+
+ wp.length = sizeof(wp);
+ if (GetWindowPlacement(hwndDlg, &wp)) {
+ M->WriteDword(SRMSGMOD_T, "splitx", wp.rcNormalPosition.left);
+ M->WriteDword(SRMSGMOD_T, "splity", wp.rcNormalPosition.top);
+ M->WriteDword(SRMSGMOD_T, "splitwidth", wp.rcNormalPosition.right - wp.rcNormalPosition.left);
+ M->WriteDword(SRMSGMOD_T, "splitheight", wp.rcNormalPosition.bottom - wp.rcNormalPosition.top);
+ }
+ return 0;
+ }
+ case ID_VIEW_INFOPANEL: {
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+ if(dat) {
+ RECT rc;
+ POINT pt;
+ GetWindowRect(pContainer->hwndActive, &rc);
+ pt.x = rc.left + 10;
+ pt.y = rc.top + dat->Panel->getHeight() - 10;
+ dat->Panel->invokeConfigDialog(pt);
+ }
+ return(0);
+ }
+ /*
+ * commands from the message log popup will be routed to the
+ * message log menu handler
+ */
+ case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
+ case ID_MESSAGELOGSETTINGS_GLOBAL: {
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(pContainer->hwndActive, GWLP_USERDATA);
+
+ if(dat) {
+ MsgWindowMenuHandler(dat, (int)LOWORD(wParam), MENU_LOGMENU);
+ return(1);
+ }
+ break;
+ }
+ case ID_HELP_ABOUTTABSRMM:
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), 0, DlgProcAbout, 0);
+ break;
+ default:
+ return(0); // not handled
+ }
+ return(1); // handled
+}
+
+/**
+ * initialize rich edit control (log and edit control) for both MUC and
+ * standard IM session windows.
+ */
+void TSAPI DM_InitRichEdit(TWindowData *dat)
+
+{
+ char* szStreamOut = NULL;
+ SETTEXTEX stx = {ST_DEFAULT, CP_UTF8};
+ COLORREF colour;
+ COLORREF inputcharcolor;
+ CHARFORMAT2A cf2;
+ LOGFONTA lf;
+ int i = 0;
+ bool fIsChat = ((dat->bType == SESSIONTYPE_CHAT) ? true : false);
+ HWND hwndLog = GetDlgItem(dat->hwnd, !fIsChat ? IDC_LOG : IDC_CHAT_LOG);
+ HWND hwndEdit= GetDlgItem(dat->hwnd, !fIsChat ? IDC_MESSAGE : IDC_CHAT_MESSAGE);
+ HWND hwndDlg = dat->hwnd;
+
+ ZeroMemory(&cf2, sizeof(CHARFORMAT2A));
+
+ dat->inputbg = fIsChat ? M->GetDword(FONTMODULE, "inputbg", SRMSGDEFSET_BKGCOLOUR) : dat->pContainer->theme.inputbg;
+ colour = fIsChat ? M->GetDword(FONTMODULE, SRMSGSET_BKGCOLOUR_MUC, SRMSGDEFSET_BKGCOLOUR) : dat->pContainer->theme.bg;
+
+ if(!fIsChat) {
+ if (GetWindowTextLengthA(hwndEdit) > 0)
+ szStreamOut = Message_GetFromStream(hwndEdit, dat, (CP_UTF8 << 16) | (SF_RTFNOOBJS | SFF_PLAINRTF | SF_USECODEPAGE));
+ }
+
+ SendMessage(hwndLog, EM_SETBKGNDCOLOR, 0, colour);
+ SendMessage(hwndEdit, EM_SETBKGNDCOLOR, 0, dat->inputbg);
+
+ if(fIsChat)
+ LoadLogfont(MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor, FONTMODULE);
+ else {
+ lf = dat->pContainer->theme.logFonts[MSGFONTID_MESSAGEAREA];
+ inputcharcolor = dat->pContainer->theme.fontColors[MSGFONTID_MESSAGEAREA];
+ }
+ /*
+ * correct the input area text color to avoid a color from the table of usable bbcode colors
+ */
+ if(!fIsChat) {
+ for (i = 0; i < Utils::rtf_ctable_size; i++) {
+ if (Utils::rtf_ctable[i].clr == inputcharcolor)
+ inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1);
+ }
+ }
+ if(fIsChat) {
+ cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR;
+ cf2.cbSize = sizeof(cf2);
+ cf2.crTextColor = inputcharcolor;
+ cf2.bCharSet = lf.lfCharSet;
+ cf2.crBackColor = dat->inputbg;
+ strncpy(cf2.szFaceName, lf.lfFaceName, LF_FACESIZE);
+ cf2.dwEffects = 0;
+ cf2.wWeight = (WORD)lf.lfWeight;
+ cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+ cf2.yHeight = abs(lf.lfHeight) * 15;
+ SetWindowText(hwndEdit, _T(""));
+ SendMessage(hwndEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
+ }
+ else {
+ cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC;
+ cf2.cbSize = sizeof(cf2);
+ cf2.crTextColor = inputcharcolor;
+ cf2.bCharSet = lf.lfCharSet;
+ strncpy(cf2.szFaceName, lf.lfFaceName, LF_FACESIZE);
+ cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0)|(lf.lfUnderline ? CFE_UNDERLINE : 0)|(lf.lfStrikeOut ? CFE_STRIKEOUT : 0);
+ cf2.wWeight = (WORD)lf.lfWeight;
+ cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+ cf2.yHeight = abs(lf.lfHeight) * 15;
+ SendMessageA(hwndEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf2);
+ }
+
+ /*
+ * setup the rich edit control(s)
+ * LOG is always set to RTL, because this is needed for proper bidirectional operation later.
+ * The real text direction is then enforced by the streaming code which adds appropiate paragraph
+ * and textflow formatting commands to the
+ */
+
+ PARAFORMAT2 pf2;
+ ZeroMemory(&pf2, sizeof(PARAFORMAT2));
+
+ pf2.cbSize = sizeof(pf2);
+
+ pf2.wEffects = PFE_RTLPARA;
+ pf2.dwMask = PFM_RTLPARA;
+ if (Utils::FindRTLLocale(dat))
+ SendMessage(hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ if (!(dat->dwFlags & MWF_LOG_RTL)) {
+ pf2.wEffects = 0;
+ SendMessage(hwndEdit, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ }
+ SendMessage(hwndEdit, EM_SETLANGOPTIONS, 0, (LPARAM) SendMessage(hwndEdit, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
+ pf2.wEffects = PFE_RTLPARA;
+ pf2.dwMask |= PFM_OFFSET;
+ if (dat->dwFlags & MWF_INITMODE) {
+ pf2.dwMask |= (PFM_RIGHTINDENT | PFM_OFFSETINDENT);
+ pf2.dxStartIndent = 30;
+ pf2.dxRightIndent = 30;
+ }
+ pf2.dxOffset = dat->pContainer->theme.left_indent + 30;
+ if(!fIsChat) {
+ SetWindowText(hwndLog, _T(""));
+ SendMessage(hwndLog, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ SendMessage(hwndLog, EM_SETLANGOPTIONS, 0, (LPARAM) SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
+ }
+
+ /*
+ * set the scrollbars etc to RTL/LTR (only for manual RTL mode)
+ */
+
+ if(!fIsChat) {
+ if (dat->dwFlags & MWF_LOG_RTL) {
+ SetWindowLongPtr(hwndEdit, GWL_EXSTYLE, GetWindowLongPtr(hwndEdit, GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
+ SetWindowLongPtr(hwndLog, GWL_EXSTYLE, GetWindowLongPtr(hwndLog, GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
+ } else {
+ SetWindowLongPtr(hwndEdit, GWL_EXSTYLE, GetWindowLongPtr(hwndEdit, GWL_EXSTYLE) &~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
+ SetWindowLongPtr(hwndLog, GWL_EXSTYLE, GetWindowLongPtr(hwndLog, GWL_EXSTYLE) &~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
+ }
+ SetWindowText(hwndEdit, _T(""));
+ }
+ if (szStreamOut != NULL) {
+ SendMessage(hwndEdit, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut);
+ free(szStreamOut);
+ }
+}
+
+/*
+* action and callback procedures for the stock button objects
+*/
+
+static void BTN_StockAction(ButtonItem *item, HWND hwndDlg, struct TWindowData *dat, HWND hwndBtn)
+{
+ if (item->dwStockFlags & SBI_HANDLEBYCLIENT && IsWindow(hwndDlg) && dat)
+ SendMessage(hwndDlg, WM_COMMAND, MAKELONG(item->uId, BN_CLICKED), (LPARAM)hwndBtn);
+ else {
+ switch (item->uId) {
+ case IDC_SBAR_CANCEL:
+ PostMessage(hwndDlg, WM_COMMAND, MAKELONG(IDC_SAVE, BN_CLICKED), (LPARAM)hwndBtn);
+ break;
+ case IDC_SBAR_SLIST:
+ SendMessage(PluginConfig.g_hwndHotkeyHandler, DM_TRAYICONNOTIFY, 101, WM_LBUTTONUP);
+ break;
+ case IDC_SBAR_FAVORITES: {
+ POINT pt;
+ int iSelection;
+ GetCursorPos(&pt);
+ iSelection = TrackPopupMenu(PluginConfig.g_hMenuFavorites, TPM_RETURNCMD, pt.x, pt.y, 0, PluginConfig.g_hwndHotkeyHandler, NULL);
+ HandleMenuEntryFromhContact(iSelection);
+ break;
+ }
+ case IDC_SBAR_RECENT: {
+ POINT pt;
+ int iSelection;
+ GetCursorPos(&pt);
+ iSelection = TrackPopupMenu(PluginConfig.g_hMenuRecent, TPM_RETURNCMD, pt.x, pt.y, 0, PluginConfig.g_hwndHotkeyHandler, NULL);
+ HandleMenuEntryFromhContact(iSelection);
+ break;
+ }
+ case IDC_SBAR_USERPREFS: {
+ HANDLE hContact = 0;
+ SendMessage(hwndDlg, DM_QUERYHCONTACT, 0, (LPARAM)&hContact);
+ if (hContact != 0)
+ CallService(MS_TABMSG_SETUSERPREFS, (WPARAM)hContact, 0);
+ break;
+ }
+ case IDC_SBAR_TOGGLEFORMAT: {
+ if (dat) {
+ if (IsDlgButtonChecked(hwndDlg, IDC_SBAR_TOGGLEFORMAT) == BST_UNCHECKED) {
+ dat->SendFormat = 0;
+ GetSendFormat(dat, 0);
+ } else {
+ dat->SendFormat = SENDFORMAT_BBCODE;
+ GetSendFormat(dat, 0);
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+static void BTN_StockCallback(ButtonItem *item, HWND hwndDlg, struct TWindowData *dat, HWND hwndBtn)
+{
+}
+
+/*
+* predefined button objects for customizeable buttons
+*/
+
+static struct SIDEBARITEM sbarItems[] = {
+ 0, 0, 0, 0, 0, _T(""), NULL, NULL, _T("")
+};
+
+int TSAPI BTN_GetStockItem(ButtonItem *item, const TCHAR *szName)
+{
+ int i = 0;
+
+ while (sbarItems[i].uId) {
+ if (!_tcsicmp(sbarItems[i].szName, szName)) {
+ item->uId = sbarItems[i].uId;
+ //item->dwFlags |= BUTTON_ISSIDEBAR;
+ //myGlobals.m_SideBarEnabled = TRUE;
+ if (item->dwFlags & BUTTON_ISSIDEBAR) {
+ if (sbarItems[i].dwFlags & SBI_TOP)
+ item->yOff = 0;
+ else if (sbarItems[i].dwFlags & SBI_BOTTOM)
+ item->yOff = -1;
+ }
+ item->dwStockFlags = sbarItems[i].dwFlags;
+ item->dwFlags = sbarItems[i].dwFlags & SBI_TOGGLE ? item->dwFlags | BUTTON_ISTOGGLE : item->dwFlags & ~BUTTON_ISTOGGLE;
+ item->pfnAction = sbarItems[i].pfnAction;
+ item->pfnCallback = sbarItems[i].pfnCallback;
+ lstrcpyn(item->szTip, sbarItems[i].tszTip, 256);
+ item->szTip[255] = 0;
+ if (sbarItems[i].hIcon) {
+ item->normalGlyphMetrics[0] = (LONG_PTR)sbarItems[i].hIcon;
+ item->dwFlags |= BUTTON_NORMALGLYPHISICON;
+ }
+ if (sbarItems[i].hIconPressed) {
+ item->pressedGlyphMetrics[0] = (LONG_PTR)sbarItems[i].hIconPressed;
+ item->dwFlags |= BUTTON_PRESSEDGLYPHISICON;
+ }
+ if (sbarItems[i].hIconHover) {
+ item->hoverGlyphMetrics[0] = (LONG_PTR)sbarItems[i].hIconHover;
+ item->dwFlags |= BUTTON_HOVERGLYPHISICON;
+ }
+ return 1;
+ }
+ i++;
+ }
+ return 0;
+}
+
+/*
+* set the states of defined database action buttons (only if button is a toggle)
+*/
+
+void TSAPI DM_SetDBButtonStates(HWND hwndChild, struct TWindowData *dat)
+{
+ ButtonItem *buttonItem = dat->pContainer->buttonItems;
+ HANDLE hContact = dat->hContact, hFinalContact = 0;
+ char *szModule, *szSetting;
+ HWND hwndContainer = dat->pContainer->hwnd;;
+
+ while (buttonItem) {
+ BOOL result = FALSE;
+ HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId);
+
+ if (buttonItem->pfnCallback)
+ buttonItem->pfnCallback(buttonItem, hwndChild, dat, hWnd);
+
+ if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+ szModule = buttonItem->szModule;
+ szSetting = buttonItem->szSetting;
+ if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (hContact == 0) {
+ SendMessage(hWnd, BM_SETCHECK, BST_UNCHECKED, 0);
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+ if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ hFinalContact = hContact;
+ } else
+ hFinalContact = 0;
+
+ if (buttonItem->type == DBVT_ASCIIZ) {
+ DBVARIANT dbv = {0};
+
+ if (!DBGetContactSettingString(hFinalContact, szModule, szSetting, &dbv)) {
+ result = !strcmp((char *)buttonItem->bValuePush, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ } else {
+ switch (buttonItem->type) {
+ case DBVT_BYTE: {
+ BYTE val = M->GetByte(hFinalContact, szModule, szSetting, 0);
+ result = (val == buttonItem->bValuePush[0]);
+ break;
+ }
+ case DBVT_WORD: {
+ WORD val = DBGetContactSettingWord(hFinalContact, szModule, szSetting, 0);
+ result = (val == *((WORD *) & buttonItem->bValuePush));
+ break;
+ }
+ case DBVT_DWORD: {
+ DWORD val = M->GetDword(hFinalContact, szModule, szSetting, 0);
+ result = (val == *((DWORD *) & buttonItem->bValuePush));
+ break;
+ }
+ }
+ }
+ SendMessage(hWnd, BM_SETCHECK, (WPARAM)result, 0);
+ buttonItem = buttonItem->nextItem;
+ }
+}
+
+LRESULT TSAPI DM_ScrollToBottom(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ if (dat) {
+
+ if (dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED)
+ return 0;
+
+ if (IsIconic(dat->pContainer->hwnd))
+ dat->dwFlags |= MWF_DEFERREDSCROLL;
+
+ if (dat->hwndIEView) {
+ PostMessage(dat->hwnd, DM_SCROLLIEVIEW, 0, 0);
+ return 0;
+ } else if (dat->hwndHPP) {
+ SendMessage(dat->hwnd, DM_SCROLLIEVIEW, 0, 0);
+ return 0;
+ } else {
+ HWND hwnd = GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG);
+ if (lParam)
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+
+ if (wParam == 1 && lParam == 1) {
+ RECT rc;
+ int len;
+
+ GetClientRect(hwnd, &rc);
+ len = GetWindowTextLengthA(hwnd);
+ SendMessage(hwnd, EM_SETSEL, len - 1, len - 1);
+ }
+ if (wParam)
+ SendMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0);
+ else
+ PostMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0);
+ if (lParam)
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ }
+ return 0;
+}
+
+static unsigned __stdcall LoadKLThread(LPVOID vParam)
+{
+ HANDLE hContact = reinterpret_cast<HANDLE>(vParam);
+ DBVARIANT dbv = {0};
+
+ LRESULT res = M->GetTString(hContact, SRMSGMOD_T, "locale", &dbv);
+ if (res == 0) {
+ HKL hkl = LoadKeyboardLayout(dbv.ptszVal, 0);
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SETLOCALE, (WPARAM)hContact, (LPARAM)hkl);
+ DBFreeVariant(&dbv);
+ }
+ return(0);
+}
+
+
+LRESULT TSAPI DM_LoadLocale(TWindowData *dat)
+{
+ /*
+ * set locale if saved to contact
+ */
+ if (dat) {
+ if (dat->dwFlags & MWF_WASBACKGROUNDCREATE)
+ return 0;
+
+ if (PluginConfig.m_AutoLocaleSupport) {
+ DBVARIANT dbv;
+ int res;
+ TCHAR szKLName[KL_NAMELENGTH+1];
+ UINT flags = KLF_ACTIVATE;
+
+ res = DBGetContactSettingTString(dat->hContact, SRMSGMOD_T, "locale", &dbv);
+ if (res == 0) {
+
+ /*
+ dat->hkl = LoadKeyboardLayout(dbv.ptszVal, KLF_REPLACELANG | KLF_NOTELLSHELL);
+ GetLocaleID(dat, dbv.ptszVal);
+ PostMessage(dat->hwnd, DM_SETLOCALE, 0, 0);*/
+ DBFreeVariant(&dbv);
+ CloseHandle((HANDLE)mir_forkthreadex(LoadKLThread, reinterpret_cast<void *>(dat->hContact), 16000, NULL));
+ } else {
+ if(!PluginConfig.m_dontUseDefaultKbd) {
+ TCHAR szBuf[20];
+
+ GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_ILANGUAGE, szBuf, 20);
+ mir_sntprintf(szKLName, KL_NAMELENGTH, _T("0000%s"), szBuf);
+ M->WriteTString(dat->hContact, SRMSGMOD_T, "locale", szKLName);
+ }
+ else {
+ GetKeyboardLayoutName(szKLName);
+ M->WriteTString(dat->hContact, SRMSGMOD_T, "locale", szKLName);
+ }
+ /*dat->hkl = LoadKeyboardLayout(szKLName, KLF_NOTELLSHELL | KLF_REPLACELANG);
+ GetLocaleID(dat, szKLName);
+ PostMessage(dat->hwnd, DM_SETLOCALE, 0, 0);*/
+ CloseHandle((HANDLE)mir_forkthreadex(LoadKLThread, reinterpret_cast<void *>(dat->hContact), 16000, NULL));
+ }
+ }
+ }
+ return 0;
+}
+
+LRESULT TSAPI DM_RecalcPictureSize(TWindowData *dat)
+{
+ BITMAP bminfo;
+ HBITMAP hbm;
+
+ if (dat) {
+ hbm = ((dat->Panel->isActive()) && dat->pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown);
+
+ if (hbm == 0) {
+ dat->pic.cy = dat->pic.cx = 60;
+ return 0;
+ }
+ GetObject(hbm, sizeof(bminfo), &bminfo);
+ CalcDynamicAvatarSize(dat, &bminfo);
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+ return 0;
+}
+
+LRESULT TSAPI DM_UpdateLastMessage(const TWindowData *dat)
+{
+ if (dat) {
+ if (dat->pContainer->hwndStatus == 0)
+ return 0;
+ if (dat->pContainer->hwndActive != dat->hwnd)
+ return 0;
+ if (dat->showTyping) {
+ TCHAR szBuf[80];
+
+ mir_sntprintf(szBuf, safe_sizeof(szBuf), CTranslator::get(CTranslator::GEN_MTN_STARTWITHNICK), dat->cache->getNick());
+ SendMessage(dat->pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM) szBuf);
+ SendMessage(dat->pContainer->hwndStatus, SB_SETICON, 0, (LPARAM) PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ return 0;
+ }
+ else
+ SendMessage(dat->pContainer->hwndStatus, SB_SETICON, 0, 0);
+
+ if (dat->lastMessage || dat->pContainer->dwFlags & CNT_UINSTATUSBAR) {
+ TCHAR date[64], time[64];
+
+ if (!(dat->pContainer->dwFlags & CNT_UINSTATUSBAR)) {
+ tmi.printTimeStamp(NULL, dat->lastMessage, _T("d"), date, safe_sizeof(date), 0);
+ if (dat->pContainer->dwFlags & CNT_UINSTATUSBAR && lstrlen(date) > 6)
+ date[lstrlen(date) - 5] = 0;
+ tmi.printTimeStamp(NULL, dat->lastMessage, _T("t"), time, safe_sizeof(time), 0);
+ }
+ if (dat->pContainer->dwFlags & CNT_UINSTATUSBAR) {
+ TCHAR fmt[100];
+ mir_sntprintf(fmt, safe_sizeof(fmt), _T("UID: %s"), dat->cache->getUIN());
+ SendMessage(dat->pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM)fmt);
+ } else {
+ TCHAR fmt[100];
+ mir_sntprintf(fmt, safe_sizeof(fmt), CTranslator::get(CTranslator::GEN_SBAR_LASTRECEIVED), date, time);
+ SendMessage(dat->pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM) fmt);
+ }
+ } else
+ SendMessageA(dat->pContainer->hwndStatus, SB_SETTEXTA, 0, (LPARAM) "");
+ }
+ return 0;
+}
+
+/*
+* save current keyboard layout for the given contact
+*/
+
+LRESULT TSAPI DM_SaveLocale(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ if (dat) {
+ if (PluginConfig.m_AutoLocaleSupport && dat->hContact && dat->pContainer->hwndActive == dat->hwnd) {
+ TCHAR szKLName[KL_NAMELENGTH + 1];
+ if ((HKL)lParam != dat->hkl) {
+ dat->hkl = (HKL)lParam;
+ ActivateKeyboardLayout(dat->hkl, 0);
+ GetKeyboardLayoutName(szKLName);
+ M->WriteTString(dat->hContact, SRMSGMOD_T, "locale", szKLName);
+ GetLocaleID(dat, szKLName);
+ UpdateReadChars(dat);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+* generic handler for the WM_COPY message in message log/chat history richedit control(s).
+* it filters out the invisible event boundary markers from the text copied to the clipboard.
+*/
+
+LRESULT TSAPI DM_WMCopyHandler(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT result = CallWindowProc(oldWndProc, hwnd, WM_COPY, wParam, lParam);
+
+ if (OpenClipboard(hwnd)) {
+ HANDLE hClip = GetClipboardData(CF_UNICODETEXT);
+ if (hClip) {
+ HGLOBAL hgbl;
+ TCHAR *tszLocked;
+ TCHAR *tszText = (TCHAR *)malloc((lstrlen((TCHAR *)hClip) + 2) * sizeof(TCHAR));
+
+ lstrcpy(tszText, (TCHAR *)hClip);
+ Utils::FilterEventMarkers(tszText);
+ EmptyClipboard();
+
+ hgbl = GlobalAlloc(GMEM_MOVEABLE, (lstrlen(tszText) + 1) * sizeof(TCHAR));
+ tszLocked = (TCHAR *)GlobalLock(hgbl);
+ lstrcpy(tszLocked, tszText);
+ GlobalUnlock(hgbl);
+ SetClipboardData(CF_UNICODETEXT, hgbl);
+ if (tszText)
+ free(tszText);
+ }
+ CloseClipboard();
+ }
+ return result;
+}
+
+/*
+* create embedded contact list control
+*/
+
+HWND TSAPI DM_CreateClist(TWindowData *dat)
+{
+ if(!sendLater->isAvail()) {
+ CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK|MB_ICONINFORMATION, CTranslator::get(CTranslator::QMGR_ERROR_NOMULTISEND));
+ dat->sendMode &= ~SMODE_MULTIPLE;
+ return(0);
+ }
+ HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248,
+ 184, 0, 30, 30, dat->hwnd, (HMENU)IDC_CLIST, g_hInst, NULL);
+
+ //MAD: fix for little bug, when following code didn't work (another hack :) )
+ HANDLE hItem;
+ SendMessage(hwndClist, WM_TIMER, 14, 0);
+ //
+ hItem = (HANDLE) SendMessage(hwndClist, CLM_FINDCONTACT, (WPARAM) dat->hContact, 0);
+
+ SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT);
+ SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL));
+ //MAD: show offline contacts in multi-send
+ if (!PluginConfig.m_AllowOfflineMultisend)
+ SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE);
+ //
+ if (hItem)
+ SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM) hItem, 1);
+
+ if (CallService(MS_CLUI_GETCAPS, 0, 0) & CLUIF_DISABLEGROUPS && !M->GetByte("CList", "UseGroups", SETTING_USEGROUPS_DEFAULT))
+ SendMessage(hwndClist, CLM_SETUSEGROUPS, (WPARAM) FALSE, 0);
+ else
+ SendMessage(hwndClist, CLM_SETUSEGROUPS, (WPARAM) TRUE, 0);
+ if (CallService(MS_CLUI_GETCAPS, 0, 0) & CLUIF_HIDEEMPTYGROUPS && M->GetByte("CList", "HideEmptyGroups", SETTING_USEGROUPS_DEFAULT))
+ SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, (WPARAM) TRUE, 0);
+ else
+ SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, (WPARAM) FALSE, 0);
+ SendMessage(hwndClist, CLM_FIRST + 106, 0, 1);
+ SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0);
+ if(hwndClist)
+ RedrawWindow(hwndClist, 0, 0, RDW_INVALIDATE|RDW_ERASE|RDW_UPDATENOW);
+ return hwndClist;
+}
+
+LRESULT TSAPI DM_MouseWheelHandler(HWND hwnd, HWND hwndParent, struct TWindowData *mwdat, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc, rc1;
+ POINT pt;
+ TCHITTESTINFO hti;
+ HWND hwndTab;
+ UINT uID = mwdat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG;
+ UINT uIDMsg = mwdat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE;
+
+ GetCursorPos(&pt);
+ GetWindowRect(hwnd, &rc);
+ if (PtInRect(&rc, pt))
+ return 1;
+ if (mwdat->pContainer->dwFlags & CNT_SIDEBAR) {
+ GetWindowRect(GetDlgItem(mwdat->pContainer->hwnd, IDC_SIDEBARUP), &rc);
+ GetWindowRect(GetDlgItem(mwdat->pContainer->hwnd, IDC_SIDEBARDOWN), &rc1);
+ rc.bottom = rc1.bottom;
+ if (PtInRect(&rc, pt)) {
+ short amount = (short)(HIWORD(wParam));
+ SendMessage(mwdat->pContainer->hwnd, WM_COMMAND, MAKELONG(amount > 0 ? IDC_SIDEBARUP : IDC_SIDEBARDOWN, 0), (LPARAM)uIDMsg);
+ return 0;
+ }
+ }
+ if (mwdat->bType == SESSIONTYPE_CHAT) { // scroll nick list by just hovering it
+ RECT rcNicklist;
+ GetWindowRect(GetDlgItem(mwdat->hwnd, IDC_LIST), &rcNicklist);
+ if (PtInRect(&rcNicklist, pt)) {
+ SendMessage(GetDlgItem(mwdat->hwnd, IDC_LIST), WM_MOUSEWHEEL, wParam, lParam);
+ return(0);
+ }
+ }
+ if (mwdat->hwndIEView)
+ GetWindowRect(mwdat->hwndIEView, &rc);
+ else if (mwdat->hwndHPP)
+ GetWindowRect(mwdat->hwndHPP, &rc);
+ else
+ GetWindowRect(GetDlgItem(hwndParent, uID), &rc);
+ if (PtInRect(&rc, pt)) {
+ HWND hwnd = (mwdat->hwndIEView || mwdat->hwndHPP) ? mwdat->hwndIWebBrowserControl : GetDlgItem(hwndParent, uID);
+ short wDirection = (short)HIWORD(wParam);
+
+ if (hwnd == 0)
+ hwnd = WindowFromPoint(pt);
+
+ if (LOWORD(wParam) & MK_SHIFT || M->GetByte("fastscroll", 0)) {
+ if (wDirection < 0)
+ SendMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
+ else if (wDirection > 0)
+ SendMessage(hwnd, WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
+ } else
+ SendMessage(hwnd, WM_MOUSEWHEEL, wParam, lParam);
+ return 0;
+ }
+ hwndTab = GetDlgItem(mwdat->pContainer->hwnd, IDC_MSGTABS);
+ GetCursorPos(&hti.pt);
+ ScreenToClient(hwndTab, &hti.pt);
+ hti.flags = 0;
+ if (TabCtrl_HitTest(hwndTab, &hti) != -1) {
+ SendMessage(hwndTab, WM_MOUSEWHEEL, wParam, -1);
+ return 0;
+ }
+ return 1;
+}
+
+void TSAPI DM_FreeTheme(TWindowData *dat)
+{
+ if(dat) {
+ if (CMimAPI::m_pfnCloseThemeData) {
+ if(dat->hTheme) {
+ CMimAPI::m_pfnCloseThemeData(dat->hTheme);
+ dat->hTheme = 0;
+ }
+ if(dat->hThemeIP) {
+ CMimAPI::m_pfnCloseThemeData(dat->hThemeIP);
+ dat->hThemeIP = 0;
+ }
+ if(dat->hThemeToolbar) {
+ CMimAPI::m_pfnCloseThemeData(dat->hThemeToolbar);
+ dat->hThemeToolbar = 0;
+ }
+ }
+ }
+}
+
+LRESULT TSAPI DM_ThemeChanged(TWindowData *dat)
+{
+ CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY];
+ CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA];
+
+ HWND hwnd = dat->hwnd;
+
+ dat->hTheme = (M->isVSAPIState() && CMimAPI::m_pfnOpenThemeData) ? CMimAPI::m_pfnOpenThemeData(hwnd, L"EDIT") : 0;
+
+ if (dat->bType == SESSIONTYPE_IM) {
+ if (dat->hTheme != 0 || (CSkin::m_skinEnabled && !item_log->IGNORED))
+ SetWindowLongPtr(GetDlgItem(hwnd, IDC_LOG), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwnd, IDC_LOG), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+ if (dat->hTheme != 0 || (CSkin::m_skinEnabled && !item_msg->IGNORED))
+ SetWindowLongPtr(GetDlgItem(hwnd, IDC_MESSAGE), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwnd, IDC_MESSAGE), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+ } else {
+ if (dat->hTheme != 0 || (CSkin::m_skinEnabled && !item_log->IGNORED)) {
+ SetWindowLongPtr(GetDlgItem(hwnd, IDC_CHAT_LOG), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwnd, IDC_CHAT_LOG), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+ SetWindowLongPtr(GetDlgItem(hwnd, IDC_LIST), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwnd, IDC_LIST), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+ }
+ if (dat->hTheme != 0 || (CSkin::m_skinEnabled && !item_msg->IGNORED))
+ SetWindowLongPtr(GetDlgItem(hwnd, IDC_CHAT_MESSAGE), GWL_EXSTYLE, GetWindowLongPtr(GetDlgItem(hwnd, IDC_CHAT_MESSAGE), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+ }
+ dat->hThemeIP = M->isAero() ? CMimAPI::m_pfnOpenThemeData(hwnd, L"ButtonStyle") : 0;
+ dat->hThemeToolbar = (M->isAero() || (!CSkin::m_skinEnabled && M->isVSThemed())) ? CMimAPI::m_pfnOpenThemeData(hwnd, L"REBAR") : 0;
+
+ return 0;
+}
+
+/**
+ * send out message typing notifications (MTN) when the
+ * user is typing/editing text in the messgae input area.
+ */
+void TSAPI DM_NotifyTyping(struct TWindowData *dat, int mode)
+{
+ DWORD protoStatus;
+ DWORD protoCaps;
+ DWORD typeCaps;
+ const char* szProto = 0;
+ HANDLE hContact = 0;
+
+ if (dat && dat->hContact) {
+ DeletePopupsForContact(dat->hContact, PU_REMOVE_ON_TYPE);
+
+ if(dat->bIsMeta){
+ szProto = dat->cache->getActiveProto();
+ hContact = dat->cache->getActiveContact();
+ }
+ else {
+ szProto = dat->szProto;
+ hContact = dat->hContact;
+ }
+
+ /*
+ * editing user notes or preparing a message for queued delivery -> don't send MTN
+ */
+ if(dat->fEditNotesActive || dat->sendMode & SMODE_SENDLATER)
+ return;
+
+ /*
+ * allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle)
+ */
+ if (!M->GetByte(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, M->GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW)))
+ return;
+
+ if (!dat->szProto) // should not, but who knows...
+ return;
+
+ /*
+ * check status and capabilities of the protocol
+ */
+ protoStatus = CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+
+ if (!(typeCaps & PF4_SUPPORTTYPING))
+ return;
+ if (protoStatus < ID_STATUS_ONLINE)
+ return;
+
+ /*
+ * check visibility/invisibility lists to not "accidentially" send MTN to contacts who
+ * should not see them (privacy issue)
+ */
+ if (protoCaps & PF1_VISLIST && DBGetContactSettingWord(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+ return;
+
+ if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && DBGetContactSettingWord(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+ return;
+
+ /*
+ * don't send to contacts which are not permanently added to the contact list,
+ * unless the option to ignore added status is set.
+ */
+ if (M->GetByte(dat->hContact, "CList", "NotOnList", 0)
+ && !M->GetByte(SRMSGMOD, SRMSGSET_TYPINGUNKNOWN, SRMSGDEFSET_TYPINGUNKNOWN))
+ return;
+ // End user check
+ dat->nTypeMode = mode;
+ CallService(MS_PROTO_SELFISTYPING, (WPARAM) hContact, dat->nTypeMode);
+ }
+}
+
+void TSAPI DM_OptionsApplied(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ if(dat == 0)
+ return;
+
+ HWND hwndDlg = dat->hwnd;
+ TContainerData *m_pContainer = dat->pContainer;
+
+ dat->szMicroLf[0] = 0;
+ if (!(dat->pContainer->theme.isPrivate)) {
+ LoadThemeDefaults(dat->pContainer);
+ dat->dwFlags = dat->pContainer->theme.dwFlags;
+ }
+ LoadLocalFlags(hwndDlg, dat);
+
+ LoadTimeZone(dat);
+
+ if (dat->hContact && dat->szProto != NULL && dat->bIsMeta) {
+ DWORD dwForcedContactNum = 0;
+ CallService(MS_MC_GETFORCESTATE, (WPARAM)dat->hContact, (LPARAM)&dwForcedContactNum);
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "tabSRMM_forced", dwForcedContactNum);
+ }
+
+ dat->showUIElements = m_pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1;
+
+ dat->dwFlagsEx = M->GetByte(SRMSGSET_SHOWURLS, SRMSGDEFSET_SHOWURLS) ? MWF_SHOW_URLEVENTS : 0;
+ dat->dwFlagsEx |= M->GetByte(SRMSGSET_SHOWFILES, SRMSGDEFSET_SHOWFILES) ? MWF_SHOW_FILEEVENTS : 0;
+ dat->dwFlagsEx |= M->GetByte(dat->hContact, "splitoverride", 0) ? MWF_SHOW_SPLITTEROVERRIDE : 0;
+ dat->Panel->getVisibility();
+
+ // small inner margins (padding) for the text areas
+
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
+
+ GetSendFormat(dat, 1);
+ SetDialogToType(hwndDlg);
+ SendMessage(hwndDlg, DM_CONFIGURETOOLBAR, 0, 0);
+
+ DM_InitRichEdit(dat);
+ if (hwndDlg == m_pContainer->hwndActive)
+ SendMessage(m_pContainer->hwnd, WM_SIZE, 0, 0);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_MESSAGE), NULL, FALSE);
+ if (!lParam)
+ SendMessage(hwndDlg, DM_REMAKELOG, 0, 0);
+
+ ShowWindow(dat->hwndPanelPicParent, PluginConfig.g_bDisableAniAvatars ? SW_HIDE : SW_SHOW);
+ EnableWindow(dat->hwndPanelPicParent, PluginConfig.g_bDisableAniAvatars ? FALSE : TRUE);
+
+ SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0);
+}
+
+
+void TSAPI DM_Typing(TWindowData *dat, bool fForceOff)
+{
+ if(dat == 0)
+ return;
+
+ HWND hwndDlg = dat->hwnd;
+ HWND hwndContainer = dat->pContainer->hwnd;
+ HWND hwndStatus = dat->pContainer->hwndStatus;
+
+ if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - dat->nLastTyping > TIMEOUT_TYPEOFF) {
+ DM_NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF);
+ }
+ if (dat->showTyping == 1) {
+ if (dat->nTypeSecs > 0) {
+ dat->nTypeSecs--;
+ if (GetForegroundWindow() == hwndContainer)
+ SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0);
+ } else {
+ struct TWindowData *dat_active = NULL;
+
+ if(!fForceOff) {
+ dat->showTyping = 2;
+ dat->nTypeSecs = 86400;
+
+ mir_sntprintf(dat->szStatusBar, safe_sizeof(dat->szStatusBar),
+ CTranslator::get(CTranslator::GEN_MTN_STOPPED), dat->cache->getNick());
+ if(hwndStatus && dat->pContainer->hwndActive == hwndDlg)
+ SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM) dat->szStatusBar);
+ }
+ SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0);
+ HandleIconFeedback(dat, (HICON) - 1);
+ dat_active = (struct TWindowData *)GetWindowLongPtr(dat->pContainer->hwndActive, GWLP_USERDATA);
+ if (dat_active && dat_active->bType == SESSIONTYPE_IM)
+ SendMessage(hwndContainer, DM_UPDATETITLE, 0, 0);
+ else
+ SendMessage(hwndContainer, DM_UPDATETITLE, (WPARAM)dat->pContainer->hwndActive, (LPARAM)1);
+ if (!(dat->pContainer->dwFlags & CNT_NOFLASH) && PluginConfig.m_FlashOnMTN)
+ ReflashContainer(dat->pContainer);
+ }
+ } else if(dat->showTyping == 2) {
+ if (dat->nTypeSecs > 0)
+ dat->nTypeSecs--;
+ else {
+ dat->szStatusBar[0] = 0;
+ dat->showTyping = 0;
+ }
+ UpdateStatusBar(dat);
+ }
+ else {
+ if (dat->nTypeSecs > 0) {
+ mir_sntprintf(dat->szStatusBar, safe_sizeof(dat->szStatusBar), CTranslator::get(CTranslator::GEN_MTN_STARTWITHNICK), dat->cache->getNick());
+
+ dat->nTypeSecs--;
+ if (hwndStatus && dat->pContainer->hwndActive == hwndDlg) {
+ SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM) dat->szStatusBar);
+ SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM) PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ }
+ if (IsIconic(hwndContainer) || GetForegroundWindow() != hwndContainer || GetActiveWindow() != hwndContainer) {
+ SetWindowText(hwndContainer, dat->szStatusBar);
+ dat->pContainer->dwFlags |= CNT_NEED_UPDATETITLE;
+ if (!(dat->pContainer->dwFlags & CNT_NOFLASH) && PluginConfig.m_FlashOnMTN)
+ ReflashContainer(dat->pContainer);
+ }
+ if (dat->pContainer->hwndActive != hwndDlg) {
+ if (dat->mayFlashTab)
+ dat->iFlashIcon = PluginConfig.g_IconTypingEvent;
+ HandleIconFeedback(dat, PluginConfig.g_IconTypingEvent);
+ } else { // active tab may show icon if status bar is disabled
+ if (!hwndStatus) {
+ if (TabCtrl_GetItemCount(GetParent(hwndDlg)) > 1 || !(dat->pContainer->dwFlags & CNT_HIDETABS)) {
+ HandleIconFeedback(dat, PluginConfig.g_IconTypingEvent);
+ }
+ }
+ }
+ if ((GetForegroundWindow() != hwndContainer) || (dat->pContainer->hwndStatus == 0) || (dat->pContainer->hwndActive != hwndDlg))
+ SendMessage(hwndContainer, DM_SETICON, (WPARAM)dat, (LPARAM) PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ dat->showTyping = 1;
+ }
+ }
+}
+
+/**
+ * sync splitter position for all open sessions.
+ * This cares about private / per container / MUC <> IM splitter syncing and everything.
+ * called from IM and MUC windows via DM_SPLITTERGLOBALEVENT
+ */
+int TSAPI DM_SplitterGlobalEvent(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ RECT rcWin;
+ short newMessagePos;
+ LONG newPos;
+ TWindowData* srcDat = PluginConfig.lastSPlitterPos.pSrcDat;
+ TContainerData* srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer;
+ bool fCntGlobal = (!dat->pContainer->settings->fPrivate ? true : false);
+
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ if(dat->fIsAutosizingInput)
+ return(0);
+#endif
+
+ GetWindowRect(dat->hwnd, &rcWin);
+
+ if(wParam == 0 && lParam == 0) {
+ if((dat->dwFlagsEx & MWF_SHOW_SPLITTEROVERRIDE) && dat != srcDat)
+ return(0);
+
+ if(srcDat->bType == dat->bType)
+ newPos = PluginConfig.lastSPlitterPos.pos;
+ else if(srcDat->bType == SESSIONTYPE_IM && dat->bType == SESSIONTYPE_CHAT)
+ newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+ else if(srcDat->bType == SESSIONTYPE_CHAT && dat->bType == SESSIONTYPE_IM)
+ newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+
+ if(dat == srcDat) {
+ if(dat->bType == SESSIONTYPE_IM) {
+ dat->pContainer->settings->splitterPos = dat->splitterY;
+ if(fCntGlobal) {
+ SaveSplitter(dat);
+ if(PluginConfig.lastSPlitterPos.bSync)
+ g_Settings.iSplitterY = dat->splitterY - DPISCALEY_S(23);
+ }
+ }
+ if(dat->bType == SESSIONTYPE_CHAT) {
+ SESSION_INFO *si = dat->si;
+ if(si) {
+ dat->pContainer->settings->splitterPos = si->iSplitterY + DPISCALEY_S(23);
+ if(fCntGlobal) {
+ g_Settings.iSplitterY = si->iSplitterY;
+ if(PluginConfig.lastSPlitterPos.bSync)
+ M->WriteDword(SRMSGMOD_T, "splitsplity", (DWORD)si->iSplitterY + DPISCALEY_S(23));
+ }
+ }
+ }
+ return(0);
+ }
+
+ if(!fCntGlobal && dat->pContainer != srcCnt)
+ return(0);
+ if(srcCnt->settings->fPrivate && dat->pContainer != srcCnt)
+ return(0);
+
+ if(!PluginConfig.lastSPlitterPos.bSync && dat->bType != srcDat->bType)
+ return(0);
+
+ /*
+ * for inactive sessions, delay the splitter repositioning until they become
+ * active (faster, avoid redraw/resize problems for minimized windows)
+ */
+ if (IsIconic(dat->pContainer->hwnd) || dat->pContainer->hwndActive != dat->hwnd) {
+ dat->dwFlagsEx |= MWF_EX_DELAYEDSPLITTER;
+ dat->wParam = newPos;
+ dat->lParam = PluginConfig.lastSPlitterPos.lParam;
+ return(0);
+ }
+ }
+ else
+ newPos = wParam;
+
+ newMessagePos = (short)rcWin.bottom - (short)newPos;
+
+ if(dat->bType == SESSIONTYPE_IM) {
+ LoadSplitter(dat);
+ AdjustBottomAvatarDisplay(dat);
+ DM_RecalcPictureSize(dat);
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ DM_ScrollToBottom(dat, 1,1);
+ if(dat != srcDat)
+ CSkin::UpdateToolbarBG(dat);
+ }
+ else {
+ SESSION_INFO *si = dat->si;
+ if(si) {
+ si->iSplitterY = g_Settings.iSplitterY;
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+ }
+ return(0);
+}
+
+/**
+ * incoming event handler
+ */
+
+void TSAPI DM_EventAdded(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ TContainerData *m_pContainer = dat->pContainer;
+ DBEVENTINFO dbei = {0};
+ DWORD dwTimestamp = 0;
+ BOOL fIsStatusChangeEvent = FALSE, fIsNotifyEvent = FALSE;
+ HWND hwndDlg = dat->hwnd, hwndContainer = m_pContainer->hwnd, hwndTab = GetParent(dat->hwnd);
+
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = 0;
+
+ CallService(MS_DB_EVENT_GET, lParam, (LPARAM) & dbei);
+ if (dat->hDbEventFirst == NULL)
+ dat->hDbEventFirst = (HANDLE) lParam;
+
+ fIsStatusChangeEvent = IsStatusEvent(dbei.eventType);
+ fIsNotifyEvent = (dbei.eventType == EVENTTYPE_MESSAGE || dbei.eventType == EVENTTYPE_FILE);
+
+ if (!fIsStatusChangeEvent) {
+ int heFlags = HistoryEvents_GetFlags(dbei.eventType);
+ if (heFlags != -1 && !(heFlags & HISTORYEVENTS_FLAG_DEFAULT) && !(heFlags & HISTORYEVENTS_FLAG_FLASH_MSG_WINDOW))
+ fIsStatusChangeEvent = TRUE;
+ }
+
+ if (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ))
+ return;
+
+ if (DbEventIsShown(dat, &dbei)) {
+ if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
+ dat->lastMessage = dbei.timestamp;
+ dat->szStatusBar[0] = 0;
+ if(dat->showTyping) {
+ dat->nTypeSecs = 0;
+ DM_Typing(dat, true);
+ dat->showTyping = 0;
+ }
+ HandleIconFeedback(dat, (HICON)-1);
+ if(m_pContainer->hwndStatus)
+ PostMessage(hwndDlg, DM_UPDATELASTMESSAGE, 0, 0);
+ }
+ /*
+ * set the message log divider to mark new (maybe unseen) messages, if the container has
+ * been minimized or in the background.
+ */
+ if (!(dbei.flags & DBEF_SENT) && !fIsStatusChangeEvent) {
+
+ if (PluginConfig.m_DividersUsePopupConfig && PluginConfig.m_UseDividers) {
+ if (!MessageWindowOpened((WPARAM)dat->hContact, 0))
+ SendMessage(hwndDlg, DM_ADDDIVIDER, 0, 0);
+ } else if (PluginConfig.m_UseDividers) {
+ if ((GetForegroundWindow() != hwndContainer || GetActiveWindow() != hwndContainer))
+ SendMessage(hwndDlg, DM_ADDDIVIDER, 0, 0);
+ else {
+ if (m_pContainer->hwndActive != hwndDlg)
+ SendMessage(hwndDlg, DM_ADDDIVIDER, 0, 0);
+ }
+ }
+ tabSRMM_ShowPopup(wParam, lParam, dbei.eventType, m_pContainer->fHidden ? 0 : 1, m_pContainer, hwndDlg, dat->cache->getActiveProto(), dat);
+ if(IsWindowVisible(m_pContainer->hwnd))
+ m_pContainer->fHidden = false;
+ }
+ dat->cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0);
+
+ if ((HANDLE) lParam != dat->hDbEventFirst) {
+ HANDLE nextEvent = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, lParam, 0);
+ if (1 || nextEvent == 0) {
+ if (!(dat->dwFlagsEx & MWF_SHOW_SCROLLINGDISABLED))
+ SendMessage(hwndDlg, DM_APPENDTOLOG, lParam, 0);
+ else {
+ TCHAR szBuf[100];
+
+ if (dat->iNextQueuedEvent >= dat->iEventQueueSize) {
+ dat->hQueuedEvents = (HANDLE *)realloc(dat->hQueuedEvents, (dat->iEventQueueSize + 10) * sizeof(HANDLE));
+ dat->iEventQueueSize += 10;
+ }
+ dat->hQueuedEvents[dat->iNextQueuedEvent++] = (HANDLE)lParam;
+ mir_sntprintf(szBuf, safe_sizeof(szBuf), CTranslator::get(CTranslator::GEN_MSG_LOGFROZENQUEUED),
+ dat->iNextQueuedEvent);
+ SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, szBuf);
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_LOGFROZENTEXT), NULL, NULL, RDW_INVALIDATE);
+ }
+ } else
+ SendMessage(hwndDlg, DM_REMAKELOG, 0, 0);
+ } else
+ SendMessage(hwndDlg, DM_REMAKELOG, 0, 0);
+
+ // handle tab flashing
+
+ if ((TabCtrl_GetCurSel(hwndTab) != dat->iTabID) && !(dbei.flags & DBEF_SENT) && !fIsStatusChangeEvent) {
+ switch (dbei.eventType) {
+ case EVENTTYPE_MESSAGE:
+ dat->iFlashIcon = PluginConfig.g_IconMsgEvent;
+ break;
+ case EVENTTYPE_FILE:
+ dat->iFlashIcon = PluginConfig.g_IconFileEvent;
+ break;
+ default:
+ dat->iFlashIcon = PluginConfig.g_IconMsgEvent;
+ break;
+ }
+ SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL);
+ dat->mayFlashTab = TRUE;
+ }
+ /*
+ * try to flash the contact list...
+ */
+
+ FlashOnClist(hwndDlg, dat, (HANDLE)lParam, &dbei);
+ /*
+ * autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch)
+ * never switch for status changes...
+ */
+ if (!(dbei.flags & DBEF_SENT) && !fIsStatusChangeEvent) {
+ if(PluginConfig.haveAutoSwitch() && m_pContainer->hwndActive != hwndDlg) {
+ if ((IsIconic(hwndContainer) && !IsZoomed(hwndContainer)) || (PluginConfig.m_HideOnClose && !IsWindowVisible(m_pContainer->hwnd))) {
+ int iItem = GetTabIndexFromHWND(GetParent(hwndDlg), hwndDlg);
+ if (iItem >= 0) {
+ TabCtrl_SetCurSel(GetParent(hwndDlg), iItem);
+ ShowWindow(m_pContainer->hwndActive, SW_HIDE);
+ m_pContainer->hwndActive = hwndDlg;
+ SendMessage(hwndContainer, DM_UPDATETITLE, (WPARAM)dat->hContact, 0);
+ m_pContainer->dwFlags |= CNT_DEFERREDTABSELECT;
+ }
+ }
+ }
+ }
+ /*
+ * flash window if it is not focused
+ */
+ if ((GetActiveWindow() != hwndContainer || GetForegroundWindow() != hwndContainer || dat->pContainer->hwndActive != hwndDlg) && !(dbei.flags & DBEF_SENT) && !fIsStatusChangeEvent) {
+ if (!(m_pContainer->dwFlags & CNT_NOFLASH) && (GetActiveWindow() != hwndContainer || GetForegroundWindow() != hwndContainer))
+ FlashContainer(m_pContainer, 1, 0);
+ SendMessage(hwndContainer, DM_SETICON, (WPARAM)dat, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_MESSAGE));
+ m_pContainer->dwFlags |= CNT_NEED_UPDATETITLE;
+ }
+ /*
+ * play a sound
+ */
+ if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT)))
+ PostMessage(hwndDlg, DM_PLAYINCOMINGSOUND, 0, 0);
+
+ if(dat->pWnd)
+ dat->pWnd->Invalidate();
+ }
+}
+
+void TSAPI DM_HandleAutoSizeRequest(TWindowData *dat, REQRESIZE* rr)
+{
+ if(dat && rr && GetForegroundWindow() == dat->pContainer->hwnd) {
+ if(dat->fIsAutosizingInput && dat->iInputAreaHeight != -1) {
+ LONG heightLimit = M->GetDword("autoSplitMinLimit", 0);
+ LONG iNewHeight = rr->rc.bottom - rr->rc.top;
+
+ if(CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+ iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2);
+
+ if(heightLimit && iNewHeight < heightLimit)
+ iNewHeight = heightLimit;
+
+ if(iNewHeight != dat->iInputAreaHeight) {
+ RECT rc;
+
+ GetClientRect(dat->hwnd, &rc);
+ LONG cy = rc.bottom - rc.top;
+ LONG panelHeight = (dat->Panel->isActive() ? dat->Panel->getHeight() : 0);
+
+ if(iNewHeight > (cy - panelHeight) / 2)
+ iNewHeight = (cy - panelHeight) / 2;
+
+ if(dat->bType == SESSIONTYPE_IM) {
+ dat->dynaSplitter = rc.bottom - (rc.bottom - iNewHeight + DPISCALEY_S(2));
+ if(dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR)
+ dat->dynaSplitter += DPISCALEY_S(22);
+ dat->splitterY = dat->dynaSplitter + DPISCALEY_S(34);
+ DM_RecalcPictureSize(dat);
+ }
+ else if (dat->si) {
+ dat->si->iSplitterY = (rc.bottom - (rc.bottom - iNewHeight + DPISCALEY_S(3))) + DPISCALEY_S(34);
+ if(!(dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR))
+ dat->si->iSplitterY -= DPISCALEY_S(22);
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+ dat->iInputAreaHeight = iNewHeight;
+ CSkin::UpdateToolbarBG(dat);
+ DM_ScrollToBottom(dat, 1, 0);
+ }
+ }
+ }
+}
+
+void TSAPI DM_UpdateTitle(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ TCHAR newtitle[128];
+ TCHAR* pszNewTitleEnd;
+ TCHAR newcontactname[128];
+ TCITEM item;
+ DWORD dwOldIdle = dat->idle;
+ const char* szActProto = 0;
+ HANDLE hActContact = 0;
+
+ HWND hwndDlg = dat->hwnd;
+ HWND hwndTab = GetParent(hwndDlg);
+ HWND hwndContainer = dat->pContainer->hwnd;
+ TContainerData* m_pContainer = dat->pContainer;
+
+ ZeroMemory((void *)newcontactname, sizeof(newcontactname));
+ dat->szStatus[0] = 0;
+
+ pszNewTitleEnd = _T("Message Session");
+
+ if (dat->iTabID == -1)
+ return;
+
+ ZeroMemory((void *)&item, sizeof(item));
+ if (dat->hContact) {
+ int iHasName;
+ TCHAR fulluin[256];
+ const TCHAR* szNick = dat->cache->getNick();
+
+ if (dat->szProto) {
+
+ szActProto = dat->cache->getActiveProto();
+ hActContact = dat->hContact;
+
+ iHasName = (dat->cache->getUIN()[0] != 0);
+ dat->idle = dat->cache->getIdleTS();
+ dat->dwFlagsEx = dat->idle ? dat->dwFlagsEx | MWF_SHOW_ISIDLE : dat->dwFlagsEx & ~MWF_SHOW_ISIDLE;
+
+ dat->wStatus = dat->cache->getStatus();
+ mir_sntprintf(dat->szStatus, safe_sizeof(dat->szStatus), _T("%s"), (char *) CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, dat->szProto == NULL ? ID_STATUS_OFFLINE : dat->wStatus, GSMDF_TCHAR));
+
+ if (lParam != 0) {
+ if (PluginConfig.m_CutContactNameOnTabs)
+ CutContactName(szNick, newcontactname, safe_sizeof(newcontactname));
+ else
+ lstrcpyn(newcontactname, szNick, safe_sizeof(newcontactname));
+
+ Utils::DoubleAmpersands(newcontactname);
+
+ if (lstrlen(newcontactname) != 0 && dat->szStatus != NULL) {
+ if (PluginConfig.m_StatusOnTabs)
+ mir_sntprintf(newtitle, 127, _T("%s (%s)"), newcontactname, dat->szStatus);
+ else
+ mir_sntprintf(newtitle, 127, _T("%s"), newcontactname);
+ } else
+ mir_sntprintf(newtitle, 127, _T("%s"), _T("Forward"));
+
+ item.mask |= TCIF_TEXT;
+ }
+ SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0);
+ if (dat->bIsMeta)
+ mir_sntprintf(fulluin, safe_sizeof(fulluin),
+ CTranslator::get(CTranslator::GEN_MSG_UINCOPY),
+ iHasName ? dat->cache->getUIN() : CTranslator::get(CTranslator::GEN_MSG_NOUIN));
+ else
+ mir_sntprintf(fulluin, safe_sizeof(fulluin),
+ CTranslator::get(CTranslator::GEN_MSG_UINCOPY_NOMC),
+ iHasName ? dat->cache->getUIN() : CTranslator::get(CTranslator::GEN_MSG_NOUIN));
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_NAME), BUTTONADDTOOLTIP, /*iHasName ?*/ (WPARAM)fulluin /*: (WPARAM)_T("")*/, 0);
+ }
+ } else
+ lstrcpyn(newtitle, pszNewTitleEnd, safe_sizeof(newtitle));
+
+ if (dat->idle != dwOldIdle || lParam != 0) {
+
+ if (item.mask & TCIF_TEXT) {
+ item.pszText = newtitle;
+ _tcsncpy(dat->newtitle, newtitle, safe_sizeof(dat->newtitle));
+ dat->newtitle[127] = 0;
+ item.cchTextMax = 127;
+ if(dat->pWnd)
+ dat->pWnd->updateTitle(dat->cache->getNick());
+ }
+ if (dat->iTabID >= 0) {
+ TabCtrl_SetItem(hwndTab, dat->iTabID, &item);
+ if(m_pContainer->dwFlags & CNT_SIDEBAR)
+ m_pContainer->SideBar->updateSession(dat);
+ }
+ if (m_pContainer->hwndActive == hwndDlg && lParam)
+ SendMessage(hwndContainer, DM_UPDATETITLE, (WPARAM)dat->hContact, 0);
+
+ UpdateTrayMenuState(dat, TRUE);
+ if (dat->cache->isFavorite())
+ AddContactToFavorites(dat->hContact, dat->cache->getNick(), szActProto, dat->szStatus, dat->wStatus,
+ LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getActiveStatus()), 0, PluginConfig.g_hMenuFavorites);
+ if (dat->cache->isRecent()) {
+ AddContactToFavorites(dat->hContact, dat->cache->getNick(), szActProto, dat->szStatus, dat->wStatus,
+ LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getActiveStatus()), 0, PluginConfig.g_hMenuRecent);
+ }
+
+ dat->Panel->Invalidate();
+ if(dat->pWnd)
+ dat->pWnd->Invalidate();
+
+ if (PluginConfig.g_FlashAvatarAvail) {
+ FLASHAVATAR fa = {0};
+
+ fa.hContact = dat->hContact;
+ fa.hWindow = 0;
+ fa.id = 25367;
+ fa.cProto = dat->szProto;
+
+ CallService(MS_FAVATAR_GETINFO, (WPARAM)&fa, 0);
+ dat->hwndFlash = fa.hWindow;
+ if (dat->hwndFlash) {
+ bool isInfoPanel = dat->Panel->isActive();
+ SetParent(dat->hwndFlash, isInfoPanel ? dat->hwndPanelPicParent : GetDlgItem(hwndDlg, IDC_CONTACTPIC));
+ }
+ }
+ }
+ // care about MetaContacts and update the statusbar icon with the currently "most online" contact...
+ if (dat->bIsMeta) {
+ PostMessage(hwndDlg, DM_UPDATEMETACONTACTINFO, 0, 0);
+ PostMessage(hwndDlg, DM_OWNNICKCHANGED, 0, 0);
+ if (m_pContainer->dwFlags & CNT_UINSTATUSBAR)
+ DM_UpdateLastMessage(dat);
+ }
+}
+
+/*
+* status icon stuff (by sje, used for indicating encryption status in the status bar
+* this is now part of the message window api
+*/
+
+static HANDLE hHookIconPressedEvt;
+struct TStatusBarIconNode *status_icon_list = 0;
+int status_icon_list_size = 0;
+
+static INT_PTR SI_AddStatusIcon(WPARAM wParam, LPARAM lParam)
+{
+ StatusIconData *sid = (StatusIconData *)lParam;
+ struct TStatusBarIconNode *siln = (struct TStatusBarIconNode *)mir_alloc(sizeof(struct TStatusBarIconNode));
+
+ siln->sid.cbSize = sid->cbSize;
+ siln->sid.szModule = mir_strdup(sid->szModule);
+ siln->sid.dwId = sid->dwId;
+ siln->sid.hIcon = sid->hIcon;
+ siln->sid.hIconDisabled = sid->hIconDisabled;
+ siln->sid.flags = sid->flags;
+ if (sid->szTooltip) siln->sid.szTooltip = mir_strdup(sid->szTooltip);
+ else siln->sid.szTooltip = 0;
+
+ siln->next = status_icon_list;
+ status_icon_list = siln;
+ status_icon_list_size++;
+
+ M->BroadcastMessage(DM_STATUSICONCHANGE, 0, 0);
+ return 0;
+}
+
+static INT_PTR SI_RemoveStatusIcon(WPARAM wParam, LPARAM lParam)
+{
+ StatusIconData *sid = (StatusIconData *)lParam;
+ struct TStatusBarIconNode *current = status_icon_list, *prev = 0;
+
+ while (current) {
+ if (strcmp(current->sid.szModule, sid->szModule) == 0 && current->sid.dwId == sid->dwId) {
+ if (prev) prev->next = current->next;
+ else status_icon_list = current->next;
+
+ status_icon_list_size--;
+
+ mir_free(current->sid.szModule);
+ DestroyIcon(current->sid.hIcon);
+ if (current->sid.hIconDisabled) DestroyIcon(current->sid.hIconDisabled);
+ if (current->sid.szTooltip) mir_free(current->sid.szTooltip);
+ mir_free(current);
+ M->BroadcastMessage(DM_STATUSICONCHANGE, 0, 0);
+ return 0;
+ }
+
+ prev = current;
+ current = current->next;
+ }
+ return 1;
+}
+
+static void SI_RemoveAllStatusIcons(void)
+{
+ struct TStatusBarIconNode *current;
+
+ while (status_icon_list) {
+ current = status_icon_list;
+ status_icon_list = status_icon_list->next;
+ status_icon_list_size--;
+
+ mir_free(current->sid.szModule);
+ DestroyIcon(current->sid.hIcon);
+ if (current->sid.hIconDisabled) DestroyIcon(current->sid.hIconDisabled);
+ if (current->sid.szTooltip) mir_free(current->sid.szTooltip);
+ mir_free(current);
+ }
+ M->BroadcastMessage(DM_STATUSICONCHANGE, 0, 0);
+}
+
+static INT_PTR SI_ModifyStatusIcon(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+
+ StatusIconData *sid = (StatusIconData *)lParam;
+ struct TStatusBarIconNode *current = status_icon_list;
+
+ while (current) {
+ if (strcmp(current->sid.szModule, sid->szModule) == 0 && current->sid.dwId == sid->dwId) {
+ if (!hContact) {
+ current->sid.flags = sid->flags;
+ if (sid->hIcon) {
+ DestroyIcon(current->sid.hIcon);
+ current->sid.hIcon = sid->hIcon;
+ }
+ if (sid->hIconDisabled) {
+ DestroyIcon(current->sid.hIconDisabled);
+ current->sid.hIconDisabled = sid->hIconDisabled;
+ }
+ if (sid->szTooltip) {
+ if (current->sid.szTooltip) mir_free(current->sid.szTooltip);
+ current->sid.szTooltip = mir_strdup(sid->szTooltip);
+ }
+
+ M->BroadcastMessage(DM_STATUSICONCHANGE, 0, 0);
+ } else {
+ char buff[256];
+ HWND hwnd;
+ if (!(sid->flags&MBF_OWNERSTATE)) {
+ sprintf(buff, "SRMMStatusIconFlags%d", (int)sid->dwId);
+ M->WriteByte(hContact, sid->szModule, buff, (BYTE)sid->flags);
+ }
+ if ((hwnd = M->FindWindow(hContact))) {
+ if (sid->flags&MBF_OWNERSTATE) {
+
+ struct TStatusBarIconNode *siln = NULL;
+ struct TWindowData *dat = (struct TWindowData *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ struct TStatusBarIconNode *psi = dat->pSINod;
+ while (psi) {
+ if (strcmp(psi->sid.szModule, sid->szModule) == 0 && psi->sid.dwId == sid->dwId) {
+ siln = psi;
+ break;
+ }
+ psi = psi->next;
+ }
+ if (!siln) {
+ siln = (struct TStatusBarIconNode *)mir_alloc(sizeof(struct TStatusBarIconNode));
+ siln->sid.szModule = mir_strdup(sid->szModule);
+ siln->sid.dwId = sid->dwId;
+ siln->sid.hIcon = sid->hIcon;
+ siln->sid.hIconDisabled = sid->hIconDisabled;
+ siln->sid.flags = sid->flags;
+
+ if (sid->szTooltip) siln->sid.szTooltip = mir_strdup(sid->szTooltip);
+ else siln->sid.szTooltip = 0;
+
+ siln->next = dat->pSINod;
+ dat->pSINod = siln;
+ } else {
+ siln->sid.hIcon = sid->hIcon;
+ siln->sid.hIconDisabled = sid->hIconDisabled;
+ siln->sid.flags = sid->flags;
+ if (siln->sid.szTooltip) mir_free(siln->sid.szTooltip);
+
+ if (sid->szTooltip) siln->sid.szTooltip = mir_strdup(sid->szTooltip);
+ else siln->sid.szTooltip = 0;
+
+ }
+
+
+ PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
+ } else
+ PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
+ }
+ }
+ return 0;
+ }
+ current = current->next;
+ }
+ return 1;
+}
+
+void DrawStatusIcons(struct TWindowData *dat, HDC hDC, RECT r, int gap)
+{
+ TStatusBarIconNode* current = status_icon_list;
+ HICON hIcon = NULL;
+ char buff[256];
+ int flags;
+ int x = r.left;
+ LONG cx_icon = PluginConfig.m_smcxicon;
+ LONG cy_icon = PluginConfig.m_smcyicon;
+ LONG y = (r.top + r.bottom - cx_icon) >> 1;
+
+ SetBkMode(hDC, TRANSPARENT);
+ while (current) {
+ if (current->sid.flags&MBF_OWNERSTATE) {
+ struct TStatusBarIconNode *currentSIN = dat->pSINod;
+ flags = current->sid.flags;
+ hIcon = current->sid.hIcon;
+ while (currentSIN) {
+ if (strcmp(currentSIN->sid.szModule, current->sid.szModule) == 0 && currentSIN->sid.dwId == current->sid.dwId) {
+ flags = currentSIN->sid.flags;
+ hIcon = currentSIN->sid.hIcon;
+ break;
+ }
+ currentSIN = currentSIN->next;
+ }
+ } else {
+ sprintf(buff, "SRMMStatusIconFlags%d", (int)current->sid.dwId);
+ flags = M->GetByte(dat->hContact, current->sid.szModule, buff, current->sid.flags);
+ }
+
+ if (!(flags & MBF_HIDDEN)) {
+ if (!(flags&MBF_OWNERSTATE) && (flags & MBF_DISABLED) && current->sid.hIconDisabled)
+ hIcon = current->sid.hIconDisabled;
+ else if (!(flags&MBF_OWNERSTATE))
+ hIcon = current->sid.hIcon;
+
+ if (flags & MBF_DISABLED && current->sid.hIconDisabled == (HICON)0)
+ CSkin::DrawDimmedIcon(hDC, x, y, cx_icon, cy_icon, hIcon, 50);
+ else
+ DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, NULL, DI_NORMAL);
+
+ x += 16 + gap;
+ }
+ current = current->next;
+ }
+ DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS],
+ cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+
+ DrawIconEx(hDC, x, y, dat->pContainer->dwFlags & CNT_NOSOUND ?
+ PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled,
+ cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+
+ x += (cx_icon + gap);
+
+ if (dat->bType == SESSIONTYPE_IM) {
+ DrawIconEx(hDC, x, y,
+ PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+ DrawIconEx(hDC, x, y, M->GetByte(dat->hContact, SRMSGMOD, SRMSGSET_TYPING, M->GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW)) ?
+ PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+ }
+ else
+ CSkin::DrawDimmedIcon(hDC, x, y, cx_icon, cy_icon,
+ PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50);
+
+ x += (cx_icon + gap);
+ DrawIconEx(hDC, x, y, PluginConfig.g_sideBarIcons[0], cx_icon, cy_icon, 0, NULL, DI_NORMAL);
+}
+
+void SI_CheckStatusIconClick(struct TWindowData *dat, HWND hwndFrom, POINT pt, RECT r, int gap, int code)
+{
+ StatusIconClickData sicd;
+ struct TStatusBarIconNode *current = status_icon_list;
+ struct TStatusBarIconNode *clicked = NULL;
+
+ unsigned int iconNum = (pt.x - (r.left + 0)) / (PluginConfig.m_smcxicon + gap);
+ unsigned int list_icons = 0;
+ char buff[100];
+ DWORD flags;
+
+ if (dat && (code == NM_CLICK || code == NM_RCLICK)) {
+ POINT ptScreen;
+
+ GetCursorPos(&ptScreen);
+ if (!PtInRect(&rcLastStatusBarClick, ptScreen))
+ return;
+ }
+ while (current && dat) {
+ if (current->sid.flags&MBF_OWNERSTATE) {
+ struct TStatusBarIconNode *currentSIN = dat->pSINod;
+ flags = current->sid.flags;
+ while (currentSIN) {
+ if (strcmp(currentSIN->sid.szModule, current->sid.szModule) == 0 && currentSIN->sid.dwId == current->sid.dwId) {
+ flags = currentSIN->sid.flags;
+ break;
+ }
+ currentSIN = currentSIN->next;
+ }
+ } else {
+ sprintf(buff, "SRMMStatusIconFlags%d", (int)current->sid.dwId);
+ flags = M->GetByte(dat->hContact, current->sid.szModule, buff, current->sid.flags);
+ }
+ if (!(flags & MBF_HIDDEN)) {
+ if (list_icons++ == iconNum)
+ clicked = current;
+ }
+ current = current->next;
+ }
+
+ if ((int)iconNum == list_icons && code != NM_RCLICK) {
+ if (GetKeyState(VK_SHIFT) & 0x8000) {
+ struct TContainerData *piContainer = pFirstContainer;
+
+ while (piContainer) {
+ piContainer->dwFlags = ((dat->pContainer->dwFlags & CNT_NOSOUND) ? piContainer->dwFlags | CNT_NOSOUND : piContainer->dwFlags & ~CNT_NOSOUND);
+ InvalidateRect(dat->pContainer->hwndStatus, NULL, TRUE);
+ piContainer = piContainer->pNextContainer;
+ }
+ } else {
+ dat->pContainer->dwFlags ^= CNT_NOSOUND;
+ InvalidateRect(dat->pContainer->hwndStatus, NULL, TRUE);
+ }
+ } else if ((int)iconNum == list_icons + 1 && code != NM_RCLICK && dat->bType == SESSIONTYPE_IM) {
+ SendMessage(dat->pContainer->hwndActive, WM_COMMAND, IDC_SELFTYPING, 0);
+ InvalidateRect(dat->pContainer->hwndStatus, NULL, TRUE);
+ } else if ((int)iconNum == list_icons + 2) {
+ if(code == NM_CLICK)
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_TRAYICONNOTIFY, 101, WM_LBUTTONUP);
+ else if(code == NM_RCLICK)
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_TRAYICONNOTIFY, 101, WM_RBUTTONUP);
+ } else if (clicked) {
+ sicd.cbSize = sizeof(StatusIconClickData);
+ GetCursorPos(&sicd.clickLocation);
+ sicd.dwId = clicked->sid.dwId;
+ sicd.szModule = clicked->sid.szModule;
+ sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
+ NotifyEventHooks(hHookIconPressedEvt, (WPARAM)dat->hContact, (LPARAM)&sicd);
+ InvalidateRect(dat->pContainer->hwndStatus, NULL, TRUE);
+ }
+}
+
+static HANDLE SI_hServiceIcon[3];
+
+int SI_InitStatusIcons()
+{
+ SI_hServiceIcon[0] = CreateServiceFunction(MS_MSG_ADDICON, SI_AddStatusIcon);
+ SI_hServiceIcon[1] = CreateServiceFunction(MS_MSG_REMOVEICON, SI_RemoveStatusIcon);
+ SI_hServiceIcon[2] = CreateServiceFunction(MS_MSG_MODIFYICON, SI_ModifyStatusIcon);
+ hHookIconPressedEvt = CreateHookableEvent(ME_MSG_ICONPRESSED);
+
+ return 0;
+}
+
+
+int SI_DeinitStatusIcons()
+{
+ int i;
+ DestroyHookableEvent(hHookIconPressedEvt);
+ for (i = 0; i < 3; i++)
+ DestroyServiceFunction(SI_hServiceIcon[i]);
+ SI_RemoveAllStatusIcons();
+ return 0;
+}
+
+int SI_GetStatusIconsCount()
+{
+ return status_icon_list_size;
+}
diff --git a/plugins/TabSRMM/src/globals.cpp b/plugins/TabSRMM/src/globals.cpp new file mode 100644 index 0000000000..689b4cb53b --- /dev/null +++ b/plugins/TabSRMM/src/globals.cpp @@ -0,0 +1,967 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: globals.cpp 13447 2011-03-14 19:55:07Z george.hazan $
+ *
+ * Plugin configuration variables and functions. Implemented as a class
+ * though there will always be only a single instance.
+ *
+ */
+
+#include "commonheaders.h"
+extern PLUGININFOEX pluginInfo;
+
+CGlobals PluginConfig;
+CGlobals* pConfig = &PluginConfig;
+
+static TContainerSettings _cnt_default = {
+ false,
+ CNT_FLAGS_DEFAULT,
+ CNT_FLAGSEX_DEFAULT,
+ 255,
+ CInfoPanel::DEGRADE_THRESHOLD,
+ 60,
+ _T("%n (%s)"),
+ 1,
+ 0
+};
+
+HANDLE CGlobals::m_event_ModulesLoaded = 0, CGlobals::m_event_PrebuildMenu = 0, CGlobals::m_event_SettingChanged = 0;
+HANDLE CGlobals::m_event_ContactDeleted = 0, CGlobals::m_event_Dispatch = 0, CGlobals::m_event_EventAdded = 0;
+HANDLE CGlobals::m_event_IconsChanged = 0, CGlobals::m_event_TypingEvent = 0, CGlobals::m_event_ProtoAck = 0;
+HANDLE CGlobals::m_event_PreShutdown = 0, CGlobals::m_event_OkToExit = 0;
+HANDLE CGlobals::m_event_IcoLibChanged = 0, CGlobals::m_event_AvatarChanged = 0, CGlobals::m_event_MyAvatarChanged = 0, CGlobals::m_event_FontsChanged = 0;
+HANDLE CGlobals::m_event_SmileyAdd = 0, CGlobals::m_event_IEView = 0, CGlobals::m_event_FoldersChanged = 0;
+HANDLE CGlobals::m_event_ME_MC_SUBCONTACTSCHANGED = 0, CGlobals::m_event_ME_MC_FORCESEND = 0, CGlobals::m_event_ME_MC_UNFORCESEND = 0;
+TCHAR* CGlobals::m_default_container_name = _T("default");
+
+extern HANDLE hHookButtonPressedEvt;
+extern HANDLE hHookToolBarLoadedEvt;
+
+EXCEPTION_RECORD CGlobals::m_exRecord = {0};
+CONTEXT CGlobals::m_exCtx = {0};
+LRESULT CGlobals::m_exLastResult = 0;
+char CGlobals::m_exSzFile[MAX_PATH] = "\0";
+wchar_t CGlobals::m_exReason[256] = L"\0";
+int CGlobals::m_exLine = 0;
+bool CGlobals::m_exAllowContinue = false;
+
+#if defined(_WIN64)
+ static char szCurrentVersion[30];
+ static char *szVersionUrl = "http://download.miranda.or.at/tabsrmm/3/version.txt";
+ static char *szUpdateUrl = "http://silvercircle.googlecode.com/files/tabsrmm-3_x64.zip";
+ static char *szFLVersionUrl = "http://addons.miranda-im.org/details.php?action=viewfile&id=3699";
+ static char *szFLUpdateurl = "http://addons.miranda-im.org/feed.php?dlfile=3699";
+#else
+ static char szCurrentVersion[30];
+ static char *szVersionUrl = "http://download.miranda.or.at/tabsrmm/3/version.txt";
+ static char *szUpdateUrl = "http://silvercircle.googlecode.com/files/tabsrmm-3_x86.zip";
+ static char *szFLVersionUrl = ADDONS_UPDATE_URL;
+ static char *szFLUpdateurl = ADDONS_DL_URL;
+#endif
+ static char *szPrefix = "tabsrmm ";
+
+
+CRTException::CRTException(const char *szMsg, const TCHAR *szParam) : std::runtime_error(std::string(szMsg))
+{
+ mir_sntprintf(m_szParam, MAX_PATH, szParam);
+}
+
+void CRTException::display() const
+{
+ TCHAR* tszMsg = mir_a2t(what());
+ TCHAR tszBoxMsg[500];
+
+ mir_sntprintf(tszBoxMsg, 500, _T("%s\n\n(%s)"), tszMsg, m_szParam);
+ ::MessageBox(0, tszBoxMsg, _T("TabSRMM runtime error"), MB_OK | MB_ICONERROR);
+ mir_free(tszMsg);
+}
+
+void CGlobals::RegisterWithUpdater()
+{
+ Update upd = {0};
+
+ if (!ServiceExists(MS_UPDATE_REGISTER))
+ return;
+
+ upd.cbSize = sizeof(upd);
+ upd.szComponentName = pluginInfo.shortName;
+ upd.pbVersion = (BYTE *)CreateVersionString(pluginInfo.version, szCurrentVersion);
+ upd.cpbVersion = (int)(strlen((char *)upd.pbVersion));
+ upd.szVersionURL = szFLVersionUrl;
+ upd.szUpdateURL = szFLUpdateurl;
+ upd.pbVersionPrefix = (BYTE *)"<span class=\"fileNameHeader\">tabSRMM Unicode 2.0 ";
+ upd.cpbVersionPrefix = (int)(strlen((char *)upd.pbVersionPrefix));
+
+ upd.szBetaUpdateURL = szUpdateUrl;
+ upd.szBetaVersionURL = szVersionUrl;
+ upd.pbVersion = (unsigned char *)szCurrentVersion;
+ upd.cpbVersion = lstrlenA(szCurrentVersion);
+ upd.pbBetaVersionPrefix = (BYTE *)szPrefix;
+ upd.cpbBetaVersionPrefix= (int)(strlen((char *)upd.pbBetaVersionPrefix));
+ upd.szBetaChangelogURL = "http://blog.miranda.or.at/tabsrmm-articles/tabsrmm-version-3-changelog";
+
+ CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&upd);
+}
+/**
+ * reload system values. These are read ONCE and are not allowed to change
+ * without a restart
+ */
+void CGlobals::reloadSystemStartup()
+{
+ HDC hScrnDC;
+ DBVARIANT dbv = {0};
+
+ m_WinVerMajor = WinVerMajor();
+ m_WinVerMinor = WinVerMinor();
+ m_bIsXP = IsWinVerXPPlus();
+ m_bIsVista = IsWinVerVistaPlus();
+ m_bIsWin7 = IsWinVer7Plus();
+
+ ::LoadTSButtonModule();
+ ::RegisterTabCtrlClass();
+ CTip::registerClass();
+
+ dwThreadID = GetCurrentThreadId();
+
+ PluginConfig.g_hMenuContext = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_TABCONTEXT));
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM)g_hMenuContext, 0);
+
+ SkinAddNewSoundEx("RecvMsgActive", "Instant messages", "Incoming (Focused Window)");
+ SkinAddNewSoundEx("RecvMsgInactive", "Instant messages", "Incoming (Unfocused Window)");
+ SkinAddNewSoundEx("AlertMsg", "Instant messages", "Incoming (New Session)");
+ SkinAddNewSoundEx("SendMsg", "Instant messages", "Outgoing");
+ SkinAddNewSoundEx("SendError", "Instant messages", "Message send error");
+
+ hCurSplitNS = LoadCursor(NULL, IDC_SIZENS);
+ hCurSplitWE = LoadCursor(NULL, IDC_SIZEWE);
+ hCurHyperlinkHand = LoadCursor(NULL, IDC_HAND);
+ if (hCurHyperlinkHand == NULL)
+ hCurHyperlinkHand = LoadCursor(g_hInst, MAKEINTRESOURCE(IDC_HYPERLINKHAND));
+
+ hScrnDC = GetDC(0);
+ g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0;
+ g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0;
+ ReleaseDC(0, hScrnDC);
+
+ reloadSettings(false);
+ reloadAdv();
+ hookSystemEvents();
+}
+
+/**
+ * this runs ONCE at startup when the Modules Loaded event is fired
+ * by the core. all plugins are loaded and ready to use.
+ *
+ * any initialation for 3rd party plugins must go here.
+ */
+void CGlobals::reloadSystemModulesChanged()
+{
+ BOOL bIEView = FALSE;
+ CLISTMENUITEM mi = { 0 };
+
+ m_MathModAvail = ServiceExists(MATH_RTF_REPLACE_FORMULAE);
+
+ /*
+ * smiley add
+ */
+ if (ServiceExists(MS_SMILEYADD_REPLACESMILEYS)) {
+ PluginConfig.g_SmileyAddAvail = 1;
+ m_event_SmileyAdd = HookEvent(ME_SMILEYADD_OPTIONSCHANGED, ::SmileyAddOptionsChanged);
+ }
+ else
+ m_event_SmileyAdd = 0;
+
+ /*
+ * Flashavatars
+ */
+
+ g_FlashAvatarAvail = (ServiceExists(MS_FAVATAR_GETINFO) ? 1 : 0);
+
+ /*
+ * ieView
+ */
+
+ bIEView = ServiceExists(MS_IEVIEW_WINDOW);
+ if (bIEView) {
+ BOOL bOldIEView = M->GetByte("ieview_installed", 0);
+ if (bOldIEView != bIEView)
+ M->WriteByte(SRMSGMOD_T, "default_ieview", 1);
+ M->WriteByte(SRMSGMOD_T, "ieview_installed", 1);
+ m_event_IEView = HookEvent(ME_IEVIEW_OPTIONSCHANGED, ::IEViewOptionsChanged);
+ } else {
+ M->WriteByte(SRMSGMOD_T, "ieview_installed", 0);
+ m_event_IEView = 0;
+ }
+
+ g_iButtonsBarGap = M->GetByte("ButtonsBarGap", 1);
+ m_hwndClist = (HWND)CallService(MS_CLUI_GETHWND, 0, 0);
+ m_MathModAvail = (ServiceExists(MATH_RTF_REPLACE_FORMULAE) ? 1 : 0);
+ if (m_MathModAvail) {
+ char *szDelim = (char *)CallService(MATH_GET_STARTDELIMITER, 0, 0);
+ if (szDelim) {
+ MultiByteToWideChar(CP_ACP, 0, szDelim, -1, PluginConfig.m_MathModStartDelimiter, safe_sizeof(PluginConfig.m_MathModStartDelimiter));
+ CallService(MTH_FREE_MATH_BUFFER, 0, (LPARAM)szDelim);
+ }
+ }
+ else
+ PluginConfig.m_MathModStartDelimiter[0] = 0;
+
+ g_MetaContactsAvail = (ServiceExists(MS_MC_GETDEFAULTCONTACT) ? 1 : 0);
+
+
+ if(g_MetaContactsAvail) {
+ mir_snprintf(szMetaName, 256, "%s", (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0));
+ bMetaEnabled = abs(M->GetByte(0, szMetaName, "Enabled", -1));
+ }
+ else {
+ szMetaName[0] = 0;
+ bMetaEnabled = 0;
+ }
+
+ g_PopupAvail = (ServiceExists(MS_POPUP_ADDPOPUPEX) ? 1 : 0);
+ g_PopupWAvail = (ServiceExists(MS_POPUP_ADDPOPUPW) ? 1 : 0);
+
+ mi.cbSize = sizeof(mi);
+ mi.position = -2000090000;
+ if (ServiceExists(MS_SKIN2_GETICONBYHANDLE)) {
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_DEFAULT;
+ mi.icolibItem = LoadSkinnedIconHandle(SKINICON_EVENT_MESSAGE);
+ } else {
+ mi.flags = CMIF_DEFAULT;
+ mi.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ }
+ mi.pszName = LPGEN("&Message");
+ mi.pszService = MS_MSG_SENDMESSAGE;
+ PluginConfig.m_hMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) & mi);
+
+ m_useAeroPeek = M->GetByte("useAeroPeek", 1);
+}
+
+/**
+ * reload plugin settings on startup and runtime. Most of these setttings can be
+ * changed while plugin is running.
+ */
+void CGlobals::reloadSettings(bool fReloadSkins)
+{
+ m_ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &m_ncm, 0);
+
+ DWORD dwFlags = M->GetDword("mwflags", MWF_LOG_DEFAULT);
+
+ m_SendOnShiftEnter = (int)M->GetByte("sendonshiftenter", 0);
+ m_SendOnEnter = (int)M->GetByte(SRMSGSET_SENDONENTER, SRMSGDEFSET_SENDONENTER);
+ m_SendOnDblEnter = (int)M->GetByte("SendOnDblEnter", 0);
+ m_AutoLocaleSupport = (int)M->GetByte("al", 0);
+ m_AutoSwitchTabs = (int)M->GetByte("autoswitchtabs", 1);
+ m_CutContactNameTo = (int) DBGetContactSettingWord(NULL, SRMSGMOD_T, "cut_at", 15);
+ m_CutContactNameOnTabs = (int)M->GetByte("cuttitle", 0);
+ m_StatusOnTabs = (int)M->GetByte("tabstatus", 1);
+ m_LogStatusChanges = (int)dwFlags & MWF_LOG_STATUSCHANGES;
+ m_UseDividers = (int)M->GetByte("usedividers", 0);
+ m_DividersUsePopupConfig = (int)M->GetByte("div_popupconfig", 0);
+ m_MsgTimeout = (int)M->GetDword(SRMSGMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT);
+
+ if (m_MsgTimeout < SRMSGSET_MSGTIMEOUT_MIN)
+ m_MsgTimeout = SRMSGSET_MSGTIMEOUT_MIN;
+
+ m_EscapeCloses = (int)M->GetByte("escmode", 0);
+
+ m_HideOnClose = (int) M->GetByte("hideonclose", 0);
+ m_AllowTab = (int) M->GetByte("tabmode", 0);
+
+ m_FlashOnClist = (int)M->GetByte("flashcl", 0);
+ m_AlwaysFullToolbarWidth = (int)M->GetByte("alwaysfulltoolbar", 1);
+ m_LimitStaticAvatarHeight = (int)M->GetDword("avatarheight", 96);
+ m_SendFormat = (int)M->GetByte("sendformat", 0);
+ m_FormatWholeWordsOnly = 1;
+ m_RTLDefault = (int)M->GetByte("rtldefault", 0);
+ m_TabAppearance = (int)M->GetDword("tabconfig", TCF_FLASHICON | TCF_SINGLEROWTABCONTROL);
+ m_panelHeight = (DWORD)M->GetDword("panelheight", CInfoPanel::DEGRADE_THRESHOLD);
+ m_MUCpanelHeight = M->GetDword("Chat", "panelheight", CInfoPanel::DEGRADE_THRESHOLD);
+ m_IdleDetect = (int)M->GetByte("dimIconsForIdleContacts", 1);
+ m_smcxicon = 16;
+ m_smcyicon = 16;
+ m_PasteAndSend = (int)M->GetByte("pasteandsend", 1);
+ m_szNoStatus = const_cast<TCHAR *>(CTranslator::get(CTranslator::GEN_NO_STATUS));
+ m_LangPackCP = ServiceExists(MS_LANGPACK_GETCODEPAGE) ? CallService(MS_LANGPACK_GETCODEPAGE, 0, 0) : CP_ACP;
+ m_visualMessageSizeIndicator = M->GetByte("msgsizebar", 0);
+ m_autoSplit = M->GetByte("autosplit", 0);
+ m_FlashOnMTN = M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINFLASH, SRMSGDEFSET_SHOWTYPINGWINFLASH);
+ if(m_MenuBar == 0) {
+ m_MenuBar = ::LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENUBAR));
+ CallService(MS_LANGPACK_TRANSLATEMENU, WPARAM(m_MenuBar), 0);
+ }
+
+ m_ipBackgroundGradient = M->GetDword(FONTMODULE, "ipfieldsbg", 0x62caff);
+ if(0 == m_ipBackgroundGradient)
+ m_ipBackgroundGradient = 0x62caff;
+
+ m_ipBackgroundGradientHigh = M->GetDword(FONTMODULE, "ipfieldsbgHigh", 0xf0f0f0);
+ if(0 == m_ipBackgroundGradientHigh)
+ m_ipBackgroundGradientHigh = 0xf0f0f0;
+
+ m_tbBackgroundHigh = M->GetDword(FONTMODULE, "tbBgHigh", 0);
+ m_tbBackgroundLow = M->GetDword(FONTMODULE, "tbBgLow", 0);
+ m_fillColor = M->GetDword(FONTMODULE, "fillColor", 0);
+ if(CSkin::m_BrushFill) {
+ ::DeleteObject(CSkin::m_BrushFill);
+ CSkin::m_BrushFill = 0;
+ }
+ m_genericTxtColor = M->GetDword(FONTMODULE, "genericTxtClr", GetSysColor(COLOR_BTNTEXT));
+ m_cRichBorders = M->GetDword(FONTMODULE, "cRichBorders", 0);
+
+ ::CopyMemory(&globalContainerSettings, &_cnt_default, sizeof(TContainerSettings));
+ Utils::ReadContainerSettingsFromDB(0, &globalContainerSettings);
+ globalContainerSettings.fPrivate = false;
+ if(fReloadSkins)
+ Skin->setupAeroSkins();
+}
+
+/**
+ * reload "advanced tweaks" that can be applied w/o a restart
+ */
+void CGlobals::reloadAdv()
+{
+ g_bDisableAniAvatars= M->GetByte("adv_DisableAniAvatars", 0);
+ g_bSoundOnTyping = M->GetByte("adv_soundontyping", 0);
+ m_dontUseDefaultKbd= M->GetByte("adv_leaveKeyboardAlone", 1);
+ g_bClientInStatusBar = M->GetByte("adv_ClientIconInStatusBar", 0);
+
+ if(g_bSoundOnTyping && m_TypingSoundAdded == false) {
+ SkinAddNewSoundEx("SoundOnTyping", "Other", "TABSRMM: Typing");
+ m_TypingSoundAdded = true;
+ }
+ m_AllowOfflineMultisend = M->GetByte("AllowOfflineMultisend", 0);
+}
+
+const HMENU CGlobals::getMenuBar()
+{
+ if(m_MenuBar == 0) {
+ m_MenuBar = ::LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENUBAR));
+ CallService(MS_LANGPACK_TRANSLATEMENU, WPARAM(m_MenuBar), 0);
+ }
+ return(m_MenuBar);
+}
+
+/**
+ * hook core events. This runs in LoadModule()
+ * only core events and services are guaranteed to exist at this time
+ */
+void CGlobals::hookSystemEvents()
+{
+ m_event_ModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ m_event_IconsChanged = HookEvent(ME_SKIN_ICONSCHANGED, ::IconsChanged);
+ m_event_TypingEvent = HookEvent(ME_PROTO_CONTACTISTYPING, CMimAPI::TypingMessage);
+ m_event_ProtoAck = HookEvent(ME_PROTO_ACK, CMimAPI::ProtoAck);
+ m_event_PreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreshutdownSendRecv);
+ m_event_OkToExit = HookEvent(ME_SYSTEM_OKTOEXIT, OkToExit);
+
+ m_event_PrebuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, CMimAPI::PrebuildContactMenu);
+
+ m_event_IcoLibChanged = HookEvent(ME_SKIN2_ICONSCHANGED, ::IcoLibIconsChanged);
+ m_event_AvatarChanged = HookEvent(ME_AV_AVATARCHANGED, ::AvatarChanged);
+ m_event_MyAvatarChanged = HookEvent(ME_AV_MYAVATARCHANGED, ::MyAvatarChanged);
+}
+
+/**
+ * second part of the startup initialisation. All plugins are now fully loaded
+ */
+
+int CGlobals::ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ int i;
+ MENUITEMINFOA mii = {0};
+ HMENU submenu;
+ CLISTMENUITEM mi = { 0 };
+
+ ::UnhookEvent(m_event_ModulesLoaded);
+
+ M->configureCustomFolders();
+
+ Skin->Init(true);
+ CSkin::initAeroEffect();
+
+ for (i = 0; i < NR_BUTTONBARICONS; i++)
+ PluginConfig.g_buttonBarIcons[i] = 0;
+ ::LoadIconTheme();
+ ::CreateImageList(TRUE);
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_BITMAP;
+ mii.hbmpItem = HBMMENU_CALLBACK;
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 7);
+ for (i = 0; i <= 8; i++)
+ SetMenuItemInfoA(submenu, (UINT_PTR)i, TRUE, &mii);
+
+ PluginConfig.reloadSystemModulesChanged();
+
+ ::BuildContainerMenu();
+
+ ::CB_InitDefaultButtons();
+ ::ModPlus_Init(wParam, lParam);
+ ::NotifyEventHooks(hHookToolBarLoadedEvt, (WPARAM)0, (LPARAM)0);
+ //
+
+ if (M->GetByte("avatarmode", -1) == -1)
+ M->WriteByte(SRMSGMOD_T, "avatarmode", 2);
+
+ PluginConfig.g_hwndHotkeyHandler = CreateWindowEx(0, _T("TSHK"), _T(""), WS_POPUP,
+ 0, 0, 40, 40, 0, 0, g_hInst, NULL);
+
+ ::CreateTrayMenus(TRUE);
+ if (nen_options.bTraySupport)
+ ::CreateSystrayIcon(TRUE);
+
+ mi.cbSize = sizeof(mi);
+ mi.position = -500050005;
+ mi.hIcon = PluginConfig.g_iconContainer;
+ mi.pszContactOwner = NULL;
+ mi.pszName = LPGEN("&Messaging settings...");
+ mi.pszService = MS_TABMSG_SETUSERPREFS;
+ PluginConfig.m_UserMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) & mi);
+
+ if(sendLater->isAvail()) {
+ mi.cbSize = sizeof(mi);
+ mi.position = -500050006;
+ mi.hIcon = 0;
+ mi.pszContactOwner = NULL;
+ mi.pszName = LPGEN("&Send later job list...");
+ mi.pszService = MS_TABMSG_SLQMGR;
+ PluginConfig.m_UserMenuItem = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM) & mi);
+ }
+ RestoreUnreadMessageAlerts();
+
+ RegisterWithUpdater();
+
+ ::RegisterFontServiceFonts();
+ ::CacheLogFonts();
+ ::Chat_ModulesLoaded(wParam, lParam);
+ if(PluginConfig.g_PopupWAvail||PluginConfig.g_PopupAvail)
+ TN_ModuleInit();
+
+ m_event_SettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, DBSettingChanged);
+ m_event_ContactDeleted = HookEvent(ME_DB_CONTACT_DELETED, DBContactDeleted);
+
+ m_event_Dispatch = HookEvent(ME_DB_EVENT_ADDED, CMimAPI::DispatchNewEvent);
+ m_event_EventAdded = HookEvent(ME_DB_EVENT_ADDED, CMimAPI::MessageEventAdded);
+ if(PluginConfig.g_MetaContactsAvail) {
+ m_event_ME_MC_SUBCONTACTSCHANGED = HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaContactEvent);
+ m_event_ME_MC_FORCESEND = HookEvent(ME_MC_FORCESEND, MetaContactEvent);
+ m_event_ME_MC_UNFORCESEND = HookEvent(ME_MC_UNFORCESEND, MetaContactEvent);
+ }
+ m_event_FontsChanged = HookEvent(ME_FONT_RELOAD, ::FontServiceFontsChanged);
+ return 0;
+}
+
+/**
+ * watches various important database settings and reacts accordingly
+ * needed to catch status, nickname and other changes in order to update open message
+ * sessions.
+ */
+
+int CGlobals::DBSettingChanged(WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam;
+ const char *szProto = NULL;
+ const char *setting = cws->szSetting;
+ HWND hwnd = 0;
+ CContactCache* c = 0;
+ bool fChanged = false, fNickChanged = false, fExtendedStatusChange = false;
+
+ hwnd = M->FindWindow((HANDLE)wParam);
+
+ if (hwnd == 0 && wParam != 0) { // we are not interested in this event if there is no open message window/tab
+ if(!strcmp(setting, "Status") || !strcmp(setting, "MyHandle") || !strcmp(setting, "Nick") || !strcmp(cws->szModule, SRMSGMOD_T)) {
+ c = CContactCache::getContactCache((HANDLE)wParam);
+ if(c) {
+ fChanged = c->updateStatus();
+ if(strcmp(setting, "Status"))
+ c->updateNick();
+ if(!strcmp(setting, "isFavorite") || !strcmp(setting, "isRecent"))
+ c->updateFavorite();
+ }
+ }
+ return(0);
+ }
+
+ if (wParam == 0 && !strcmp("Nick", setting)) {
+ M->BroadcastMessage(DM_OWNNICKCHANGED, 0, (LPARAM)cws->szModule);
+ return(0);
+ }
+
+ if(wParam) {
+ c = CContactCache::getContactCache((HANDLE)wParam);
+ if(c) {
+ szProto = c->getProto();
+ if(!strcmp(cws->szModule, SRMSGMOD_T)) { // catch own relevant settings
+ if(!strcmp(setting, "isFavorite") || !strcmp(setting, "isRecent"))
+ c->updateFavorite();
+ }
+ }
+ }
+
+ if(wParam == 0 && !lstrcmpA(setting, "Enabled")) {
+ if(PluginConfig.g_MetaContactsAvail && !lstrcmpA(cws->szModule, PluginConfig.szMetaName)) { // catch the disabled meta contacts
+ PluginConfig.bMetaEnabled = abs(M->GetByte(0, PluginConfig.szMetaName, "Enabled", -1));
+ cacheUpdateMetaChanged();
+ }
+ }
+
+ if (lstrcmpA(cws->szModule, "CList") && (szProto == NULL || lstrcmpA(cws->szModule, szProto)))
+ return(0);
+
+ if (PluginConfig.g_MetaContactsAvail && !lstrcmpA(cws->szModule, PluginConfig.szMetaName)) {
+ if(wParam != 0 && !lstrcmpA(setting, "Nick")) // filter out this setting to avoid infinite loops while trying to obtain the most online contact
+ return(0);
+ }
+
+ if (hwnd) {
+ if(c) {
+ fChanged = c->updateStatus();
+ fNickChanged = c->updateNick();
+ }
+ if (lstrlenA(setting) > 6 && lstrlenA(setting) < 9 && !strncmp(setting, "Status", 6)) {
+ fChanged = true;
+ if(c) {
+ c->updateMeta(true);
+ c->updateUIN();
+ }
+ }
+ else if (!strcmp(setting, "MirVer"))
+ PostMessage(hwnd, DM_CLIENTCHANGED, 0, 0);
+ else if (!strcmp(setting, "display_uid")) {
+ if(c)
+ c->updateUIN();
+ PostMessage(hwnd, DM_UPDATEUIN, 0, 0);
+ }
+ else if(lstrlenA(setting) > 6 && strstr("StatusMsg,XStatusMsg,XStatusName,XStatusId,ListeningTo", setting)) {
+ if(c) {
+ c->updateStatusMsg(setting);
+ fExtendedStatusChange = true;
+ }
+ }
+ if(fChanged || fNickChanged || fExtendedStatusChange)
+ PostMessage(hwnd, DM_UPDATETITLE, 0, 1);
+ if(fExtendedStatusChange)
+ PostMessage(hwnd, DM_UPDATESTATUSMSG, 0, 0);
+ if(fChanged) {
+ if(c && c->getStatus() == ID_STATUS_OFFLINE) { // clear typing notification in the status bar when contact goes offline
+ TWindowData* dat = c->getDat();
+ if(dat) {
+ dat->nTypeSecs = 0;
+ dat->showTyping = 0;
+ dat->szStatusBar[0] = 0;
+ PostMessage(c->getHwnd(), DM_UPDATELASTMESSAGE, 0, 0);
+ }
+ }
+ if(c)
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_LOGSTATUSCHANGE, MAKELONG(c->getStatus(), c->getOldStatus()), (LPARAM)c);
+ }
+ }
+ return(0);
+}
+
+/**
+ * event fired when a contact has been deleted. Make sure to close its message session
+ */
+
+int CGlobals::DBContactDeleted(WPARAM wParam, LPARAM lParam)
+{
+ if(wParam) {
+ CContactCache *c = CContactCache::getContactCache((HANDLE)wParam);
+ if(c)
+ c->deletedHandler();
+ }
+ return 0;
+}
+
+/**
+ * Handle events from metacontacts protocol. Basically, just update
+ * our contact cache and, if a message window exists, tell it to update
+ * relevant information.
+ */
+int CGlobals::MetaContactEvent(WPARAM wParam, LPARAM lParam)
+{
+ if(wParam) {
+ CContactCache *c = CContactCache::getContactCache((HANDLE)wParam);
+ if(c) {
+ c->updateMeta(true);
+ if(c->getHwnd()) {
+ c->updateUIN(); // only do this for open windows, not needed normally
+ ::PostMessage(c->getHwnd(), DM_UPDATETITLE, 0, 0);
+ }
+ }
+ }
+ return(0);
+}
+
+int CGlobals::PreshutdownSendRecv(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact;
+ int i;
+
+#if defined(__USE_EX_HANDLERS)
+ __try {
+#endif
+ if (PluginConfig.m_chat_enabled)
+ ::Chat_PreShutdown();
+
+ ::TN_ModuleDeInit();
+
+ while(pFirstContainer){
+ if (PluginConfig.m_HideOnClose)
+ PluginConfig.m_HideOnClose = FALSE;
+ ::SendMessage(pFirstContainer->hwnd, WM_CLOSE, 0, 1);
+ }
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact) {
+ M->WriteDword(hContact, SRMSGMOD_T, "messagecount", 0);
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+ for(i = 0; i < SERVICE_LAST; i++) {
+ if(PluginConfig.hSvc[i])
+ DestroyServiceFunction(PluginConfig.hSvc[i]);
+ }
+
+ ::SI_DeinitStatusIcons();
+ ::CB_DeInitCustomButtons();
+ /*
+ * the event API
+ */
+
+ DestroyHookableEvent(PluginConfig.m_event_MsgWin);
+ DestroyHookableEvent(PluginConfig.m_event_MsgPopup);
+
+ ::NEN_WriteOptions(&nen_options);
+ ::DestroyWindow(PluginConfig.g_hwndHotkeyHandler);
+
+ ::UnregisterClass(_T("TSStatusBarClass"), g_hInst);
+ ::UnregisterClass(_T("SideBarClass"), g_hInst);
+ ::UnregisterClassA("TSTabCtrlClass", g_hInst);
+ ::UnregisterClass(_T("RichEditTipClass"), g_hInst);
+ ::UnregisterClass(_T("TSHK"), g_hInst);
+#if defined(__USE_EX_HANDLERS)
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"SHUTDOWN_STAGE2", false)) {
+ return(0);
+ }
+#endif
+ return 0;
+}
+
+int CGlobals::OkToExit(WPARAM wParam, LPARAM lParam)
+{
+ UnhookEvent(m_event_OkToExit);
+#if defined(__USE_EX_HANDLERS)
+ __try {
+#endif
+ ::CreateSystrayIcon(0);
+ ::CreateTrayMenus(0);
+
+ CWarning::destroyAll();
+
+ CMimAPI::m_shutDown = true;
+ UnhookEvent(m_event_EventAdded);
+ UnhookEvent(m_event_Dispatch);
+ UnhookEvent(m_event_PrebuildMenu);
+ UnhookEvent(m_event_SettingChanged);
+ UnhookEvent(m_event_ContactDeleted);
+ UnhookEvent(m_event_AvatarChanged);
+ UnhookEvent(m_event_MyAvatarChanged);
+ UnhookEvent(m_event_ProtoAck);
+ UnhookEvent(m_event_TypingEvent);
+ UnhookEvent(m_event_FontsChanged);
+ UnhookEvent(m_event_IcoLibChanged);
+ UnhookEvent(m_event_IconsChanged);
+
+ if(m_event_SmileyAdd)
+ UnhookEvent(m_event_SmileyAdd);
+
+ if(m_event_IEView)
+ UnhookEvent(m_event_IEView);
+
+ if(m_event_FoldersChanged)
+ UnhookEvent(m_event_FoldersChanged);
+
+ if(m_event_ME_MC_FORCESEND) {
+ UnhookEvent(m_event_ME_MC_FORCESEND);
+ UnhookEvent(m_event_ME_MC_SUBCONTACTSCHANGED);
+ UnhookEvent(m_event_ME_MC_UNFORCESEND);
+ }
+ ::ModPlus_PreShutdown(wParam, lParam);
+ PluginConfig.globalContainerSettings.fPrivate = false;
+ ::DBWriteContactSettingBlob(0, SRMSGMOD_T, CNT_KEYNAME, &PluginConfig.globalContainerSettings, sizeof(TContainerSettings));
+#if defined(__USE_EX_HANDLERS)
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"SHUTDOWN_STAGE1", false)) {
+ return(0);
+ }
+#endif
+ return 0;
+}
+
+/**
+ * used on startup to restore flashing tray icon if one or more messages are
+ * still "unread"
+ */
+
+void CGlobals::RestoreUnreadMessageAlerts(void)
+{
+ CLISTEVENT cle = { 0 };
+ DBEVENTINFO dbei = { 0 };
+ TCHAR toolTip[256];
+ int windowAlreadyExists;
+ int usingReadNext = 0;
+
+ int autoPopup = M->GetByte(SRMSGMOD, SRMSGSET_AUTOPOPUP, SRMSGDEFSET_AUTOPOPUP);
+ HANDLE hDbEvent, hContact;
+
+ dbei.cbSize = sizeof(dbei);
+ cle.cbSize = sizeof(cle);
+ cle.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = "SRMsg/ReadMessage";
+ cle.flags = CLEF_TCHAR;
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact) {
+
+ if(M->GetDword(hContact, "SendLater", "count", 0))
+ sendLater->addContact(hContact);
+
+ hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDFIRSTUNREAD, (WPARAM) hContact, 0);
+ while (hDbEvent) {
+ dbei.cbBlob = 0;
+ CallService(MS_DB_EVENT_GET, (WPARAM) hDbEvent, (LPARAM) & dbei);
+ if (!(dbei.flags & (DBEF_SENT | DBEF_READ)) && dbei.eventType == EVENTTYPE_MESSAGE) {
+ windowAlreadyExists = M->FindWindow(hContact) != NULL;
+ if (!usingReadNext && windowAlreadyExists)
+ continue;
+
+ cle.hContact = hContact;
+ cle.hDbEvent = hDbEvent;
+ mir_sntprintf(toolTip, safe_sizeof(toolTip), CTranslator::get(CTranslator::GEN_STRING_MESSAGEFROM),
+ (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, GCDNF_TCHAR));
+ cle.ptszTooltip = toolTip;
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM) & cle);
+ }
+ hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) hDbEvent, 0);
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+}
+
+void CGlobals::logStatusChange(WPARAM wParam, const CContactCache *c)
+{
+ if(c == 0)
+ return;
+
+ HANDLE hContact = c->getContact();
+
+ bool fGlobal = PluginConfig.m_LogStatusChanges ? true : false;
+ DWORD dwMask = M->GetDword(hContact, SRMSGMOD_T, "mwmask", 0);
+ DWORD dwFlags = M->GetDword(hContact, SRMSGMOD_T, "mwflags", 0);
+
+ bool fLocal = ((dwMask & MWF_LOG_STATUSCHANGES) ? (dwFlags & MWF_LOG_STATUSCHANGES ? true : false) : false);
+
+ if(fGlobal || fLocal) {
+ /*
+ * don't log them if WE are logging off
+ */
+ if(CallProtoService(c->getProto(), PS_GETSTATUS, 0, 0) == ID_STATUS_OFFLINE)
+ return;
+
+ WORD wStatus, wOldStatus;
+
+ wStatus = LOWORD(wParam);
+ wOldStatus = HIWORD(wParam);
+
+ if(wStatus == wOldStatus)
+ return;
+
+ DBEVENTINFO dbei;
+ TCHAR buffer[450];
+ HANDLE hNewEvent;
+
+ TCHAR* szOldStatus = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)wOldStatus, GSMDF_TCHAR);
+ TCHAR* szNewStatus = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)wStatus, GSMDF_TCHAR);
+
+ if(szOldStatus == 0 || szNewStatus == 0)
+ return;
+
+ if (c->isValid()) {
+ if (wStatus == ID_STATUS_OFFLINE)
+ mir_sntprintf(buffer, safe_sizeof(buffer), CTranslator::get(CTranslator::GEN_MSG_SIGNEDOFF));
+ else if (wOldStatus == ID_STATUS_OFFLINE)
+ mir_sntprintf(buffer, safe_sizeof(buffer), CTranslator::get(CTranslator::GEN_MSG_SIGNEDON), szNewStatus);
+ else
+ mir_sntprintf(buffer, safe_sizeof(buffer), CTranslator::get(CTranslator::GEN_MSG_CHANGEDSTATUS), szOldStatus, szNewStatus);
+ }
+
+ char *szMsg = M->utf8_encodeT(buffer);
+
+ dbei.pBlob = (PBYTE)szMsg;
+ dbei.cbBlob = lstrlenA(szMsg) + 1;
+ dbei.flags = DBEF_UTF | DBEF_READ;
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_STATUSCHANGE;
+ dbei.timestamp = time(NULL);
+ dbei.szModule = const_cast<char *>(c->getProto());
+ hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) hContact, (LPARAM) & dbei);
+
+ mir_free(szMsg);
+ }
+}
+
+/**
+ * when the state of the meta contacts protocol changes from enabled to disabled
+ * (or vice versa), this updates the contact cache
+ *
+ * it is ONLY called from the DBSettingChanged() event handler when the relevant
+ * database value is touched.
+ */
+void CGlobals::cacheUpdateMetaChanged()
+{
+ CContactCache* c = CContactCache::m_cCache;
+ bool fMetaActive = (PluginConfig.g_MetaContactsAvail && PluginConfig.bMetaEnabled) ? true : false;
+
+ while(c) {
+ if(c->isMeta() && PluginConfig.bMetaEnabled == false) {
+ c->closeWindow();
+ c->resetMeta();
+ }
+
+ // meta contacts are enabled, but current contact is a subcontact - > close window
+
+ if(fMetaActive && c->isSubContact())
+ c->closeWindow();
+
+ // reset meta contact information, if metacontacts protocol became avail
+
+ if(fMetaActive && !strcmp(c->getProto(), PluginConfig.szMetaName))
+ c->resetMeta();
+
+ c = c->m_next;
+ }
+}
+
+/**
+ * on Windows 7, when using new task bar features (grouping mode and per tab
+ * previews), autoswitching does not work relieably, so it is disabled.
+ *
+ * @return: true if configuration dictates autoswitch
+ */
+bool CGlobals::haveAutoSwitch()
+{
+ if(m_bIsWin7) {
+ if(m_useAeroPeek && !CSkin::m_skinEnabled)
+ return(false);
+ }
+ return(m_AutoSwitchTabs ? true : false);
+}
+/**
+ * exception handling - copy error message to clip board
+ * @param hWnd: window handle of the edit control containing the error message
+ */
+void CGlobals::Ex_CopyEditToClipboard(HWND hWnd)
+{
+ SendMessage(hWnd, EM_SETSEL, 0, 65535L);
+ SendMessage(hWnd, WM_COPY, 0 , 0);
+ SendMessage(hWnd, EM_SETSEL, 0, 0);
+}
+
+INT_PTR CALLBACK CGlobals::Ex_DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ WORD wNotifyCode, wID;
+
+ switch(uMsg) {
+ case WM_INITDIALOG: {
+ char szBuffer[2048];
+#ifdef _WIN64
+ sprintf(szBuffer,
+ "Exception %16.16X at address %16.16X occured in %s at line %d.\r\n\r\nEAX=%16.16X EBX=%16.16X ECX=%16.16X\r\nEDX=%16.16X ESI=%16.16X EDI=%16.16X\r\nEBP=%16.16X ESP=%16.16X EIP=%16.16X",
+ m_exRecord.ExceptionCode, m_exRecord.ExceptionAddress, m_exSzFile, m_exLine,
+ m_exCtx.Rax,m_exCtx.Rbx, m_exCtx.Rcx, m_exCtx.Rdx,
+ m_exCtx.Rsi, m_exCtx.Rdi, m_exCtx.Rbp, m_exCtx.Rsp, m_exCtx.Rip);
+#else
+ sprintf(szBuffer,
+ "Exception %8.8X at address %8.8X occured in %s at line %d.\r\n\r\nEAX=%8.8X EBX=%8.8X ECX=%8.8X\r\nEDX=%8.8X ESI=%8.8X EDI=%8.8X\r\nEBP=%8.8X ESP=%8.8X EIP=%8.8X",
+ m_exRecord.ExceptionCode, m_exRecord.ExceptionAddress, m_exSzFile, m_exLine,
+ m_exCtx.Eax,m_exCtx.Ebx, m_exCtx.Ecx, m_exCtx.Edx,
+ m_exCtx.Esi, m_exCtx.Edi, m_exCtx.Ebp, m_exCtx.Esp, m_exCtx.Eip);
+#endif
+ SetDlgItemTextA(hwndDlg, IDC_EXCEPTION_DETAILS, szBuffer);
+ SetFocus(GetDlgItem(hwndDlg, IDC_EXCEPTION_DETAILS));
+ SendDlgItemMessage(hwndDlg, IDC_EXCEPTION_DETAILS, WM_SETFONT, (WPARAM)GetStockObject(OEM_FIXED_FONT), 0);
+ SetDlgItemTextW(hwndDlg, IDC_EX_REASON, m_exReason);
+ Utils::enableDlgControl(hwndDlg, IDOK, m_exAllowContinue ? TRUE : FALSE);
+ }
+ break;
+
+ case WM_COMMAND:
+ wNotifyCode = HIWORD(wParam);
+ wID = LOWORD(wParam);
+ if (wNotifyCode == BN_CLICKED)
+ {
+ if (wID == IDOK || wID == IDCANCEL)
+ EndDialog(hwndDlg, wID);
+
+ if (wID == IDC_COPY_EXCEPTION)
+ Ex_CopyEditToClipboard(GetDlgItem(hwndDlg, IDC_EXCEPTION_DETAILS));
+ }
+
+ break;
+ }
+ return FALSE;
+}
+
+void CGlobals::Ex_Handler()
+{
+ if (m_exLastResult == IDCANCEL)
+ ExitProcess(1);
+}
+
+int CGlobals::Ex_ShowDialog(EXCEPTION_POINTERS *ep, const char *szFile, int line, wchar_t* szReason, bool fAllowContinue)
+{
+ char szDrive[MAX_PATH], szDir[MAX_PATH], szName[MAX_PATH], szExt[MAX_PATH];
+
+ _splitpath(szFile, szDrive, szDir, szName, szExt);
+ memcpy(&m_exRecord, ep->ExceptionRecord, sizeof(EXCEPTION_RECORD));
+ memcpy(&m_exCtx, ep->ContextRecord, sizeof(CONTEXT));
+
+ _snprintf(m_exSzFile, MAX_PATH, "%s%s", szName, szExt);
+ mir_sntprintf(m_exReason, 256, L"An application error has occured: %s", szReason);
+ m_exLine = line;
+ m_exLastResult = DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_EXCEPTION), 0, CGlobals::Ex_DlgProc, 0);
+ m_exAllowContinue = fAllowContinue;
+ if(IDCANCEL == m_exLastResult)
+ ExitProcess(1);
+ return 1;
+}
diff --git a/plugins/TabSRMM/src/hotkeyhandler.cpp b/plugins/TabSRMM/src/hotkeyhandler.cpp new file mode 100644 index 0000000000..d203e2fd4d --- /dev/null +++ b/plugins/TabSRMM/src/hotkeyhandler.cpp @@ -0,0 +1,688 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: hotkeyhandler.cpp 13596 2011-04-15 19:07:23Z george.hazan $
+ *
+ * The hotkeyhandler is a small, invisible window which handles the following things:
+
+ a) event notify stuff, messages posted from the popups to avoid threading
+ issues.
+
+ b) tray icon handling
+
+ c) send later job management. Periodically process the queue of open
+ deferred send jobs.
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+extern HICON hIcons[];
+extern INT_PTR SendMessageCommand(WPARAM wParam, LPARAM lParam);
+extern INT_PTR SendMessageCommand_W(WPARAM wParam, LPARAM lParam);
+
+static UINT WM_TASKBARCREATED;
+static HANDLE hSvcHotkeyProcessor = 0;
+
+static HOTKEYDESC _hotkeydescs[] = {
+ { 0, "tabsrmm_mostrecent", "Most recent unread session", TABSRMM_HK_SECTION_IM, MS_TABMSG_HOTKEYPROCESS, HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'R'), TABSRMM_HK_LASTUNREAD },
+ { 0, "tabsrmm_paste_and_send", "Paste and send", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'D'), TABSRMM_HK_PASTEANDSEND },
+ { 0, "tabsrmm_uprefs", "Contact's messaging prefs", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'C'), TABSRMM_HK_SETUSERPREFS },
+ { 0, "tabsrmm_copts", "Container options", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_CONTROL, 'O'), TABSRMM_HK_CONTAINEROPTIONS },
+ { 0, "tabsrmm_nudge", "Send nudge", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_CONTROL, 'N'), TABSRMM_HK_NUDGE },
+ { 0, "tabsrmm_sendfile", "Send a file", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_ALT, 'F'), TABSRMM_HK_SENDFILE },
+ { 0, "tabsrmm_quote", "Quote message", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_ALT, 'Q'), TABSRMM_HK_QUOTEMSG },
+ { 0, "tabsrmm_sendlater", "Toggle send later", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_CONTROL|HOTKEYF_SHIFT, 'S'), TABSRMM_HK_TOGGLESENDLATER },
+
+ { 0, "tabsrmm_send", "Send message", TABSRMM_HK_SECTION_GENERIC, 0, 0, TABSRMM_HK_SEND },
+ { 0, "tabsrmm_emot", "Smiley selector", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_ALT, 'E'), TABSRMM_HK_EMOTICONS },
+ { 0, "tabsrmm_hist", "Show message history", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_ALT, 'H'), TABSRMM_HK_HISTORY },
+ { 0, "tabsrmm_umenu", "Show user menu", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_ALT, 'D'), TABSRMM_HK_USERMENU },
+ { 0, "tabsrmm_udet", "Show user details", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_ALT, 'U'), TABSRMM_HK_USERDETAILS },
+ { 0, "tabsrmm_tbar", "Toggle tool bar", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_ALT|HOTKEYF_SHIFT, 'T'), TABSRMM_HK_TOGGLETOOLBAR },
+ { 0, "tabsrmm_ipanel", "Toggle info panel", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_ALT|HOTKEYF_CONTROL, 'I'), TABSRMM_HK_TOGGLEINFOPANEL },
+ { 0, "tabsrmm_rtl", "Toggle text direction", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_ALT|HOTKEYF_CONTROL, 'B'), TABSRMM_HK_TOGGLERTL },
+ { 0, "tabsrmm_msend", "Toggle multi send", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_ALT|HOTKEYF_CONTROL, 'M'), TABSRMM_HK_TOGGLEMULTISEND },
+ { 0, "tabsrmm_clearlog", "Clear message log", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(HOTKEYF_CONTROL, 'L'), TABSRMM_HK_CLEARLOG },
+ { 0, "tabsrmm_notes", "Edit user notes", TABSRMM_HK_SECTION_IM, 0, HOTKEYCODE(HOTKEYF_SHIFT | HOTKEYF_CONTROL, 'N'), TABSRMM_HK_EDITNOTES },
+ { 0, "tabsrmm_sbar", "Collapse side bar", TABSRMM_HK_SECTION_GENERIC, 0, HOTKEYCODE(0, VK_F9), TABSRMM_HK_TOGGLESIDEBAR },
+ { 0, "tabsrmm_muc_cmgr", "Channel manager", TABSRMM_HK_SECTION_GC, 0, HOTKEYCODE(HOTKEYF_SHIFT | HOTKEYF_CONTROL, 'C'), TABSRMM_HK_CHANNELMGR },
+ { 0, "tabsrmm_muc_filter", "Toggle filter", TABSRMM_HK_SECTION_GC, 0, HOTKEYCODE(HOTKEYF_SHIFT | HOTKEYF_CONTROL, 'F'), TABSRMM_HK_FILTERTOGGLE },
+ { 0, "tabsrmm_muc_nick", "Toggle nick list", TABSRMM_HK_SECTION_GC, 0, HOTKEYCODE(HOTKEYF_SHIFT | HOTKEYF_CONTROL, 'N'), TABSRMM_HK_LISTTOGGLE },
+ { 0, "tabsrmm_muc_server_show", "Show server window", TABSRMM_HK_SECTION_GC, 0, HOTKEYCODE(HOTKEYF_SHIFT | HOTKEYF_CONTROL, '1'), TABSRMM_HK_MUC_SHOWSERVER }
+};
+
+static SendLaterJobIterator g_jobs;
+
+LRESULT ProcessHotkeysByMsgFilter(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR ctrlId)
+{
+ MSGFILTER mf;
+ mf.nmhdr.code = EN_MSGFILTER;
+ mf.nmhdr.hwndFrom = hwnd;
+ mf.nmhdr.idFrom = ctrlId;
+
+ mf.lParam = lParam;
+ mf.wParam = wParam;
+ mf.msg = msg;
+
+ return(SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)&mf));
+}
+static INT_PTR HotkeyProcessor(WPARAM wParam, LPARAM lParam)
+{
+ switch(lParam) {
+ case TABSRMM_HK_LASTUNREAD:
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_TRAYICONNOTIFY, 101, WM_MBUTTONDOWN);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void TSAPI HandleMenuEntryFromhContact(int iSelection)
+{
+ HWND hWnd = M->FindWindow((HANDLE)iSelection);
+ SESSION_INFO *si = NULL;
+
+ if (iSelection == 0)
+ return;
+
+ if (hWnd && IsWindow(hWnd)) {
+ struct TContainerData *pContainer = 0;
+ SendMessage(hWnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if (pContainer) {
+ ActivateExistingTab(pContainer, hWnd);
+ pContainer->hwndSaved = 0;
+ SetForegroundWindow(pContainer->hwnd);
+ } else
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)iSelection, 0);
+ } else if ((si = SM_FindSessionByHCONTACT((HANDLE)iSelection)) != NULL) {
+ if (si->hWnd) { // session does exist, but no window is open for it
+ struct TContainerData *pContainer = 0;
+
+ SendMessage(si->hWnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if (pContainer) {
+ ActivateExistingTab(pContainer, si->hWnd);
+ if (GetForegroundWindow() != pContainer->hwnd)
+ SetForegroundWindow(pContainer->hwnd);
+ SetFocus(GetDlgItem(pContainer->hwndActive, IDC_CHAT_MESSAGE));
+ } else
+ goto nothing_open;
+ } else
+ goto nothing_open;
+ } else {
+nothing_open:
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)iSelection, 0);
+ }
+}
+
+void TSAPI DrawMenuItem(DRAWITEMSTRUCT *dis, HICON hIcon, DWORD dwIdle)
+{
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_MENU));
+ if (dwIdle) {
+ CSkin::DrawDimmedIcon(dis->hDC, 2, (dis->rcItem.bottom + dis->rcItem.top - 16) / 2, 16, 16, hIcon, 180);
+ } else
+ DrawIconEx(dis->hDC, 2, (dis->rcItem.bottom + dis->rcItem.top - 16) / 2, hIcon, 16, 16, 0, 0, DI_NORMAL | DI_COMPAT);
+}
+
+LONG_PTR CALLBACK HotkeyHandlerDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static POINT ptLast;
+ static int iMousedown;
+
+ if (msg == WM_TASKBARCREATED) {
+ CreateSystrayIcon(FALSE);
+ if (nen_options.bTraySupport)
+ CreateSystrayIcon(TRUE);
+ return 0;
+ }
+ switch (msg) {
+ case WM_CREATE:
+ int i;
+
+ for(i = 0; i < safe_sizeof(_hotkeydescs); i++) {
+ _hotkeydescs[i].cbSize = sizeof(HOTKEYDESC);
+ CallService(MS_HOTKEY_REGISTER, 0, (LPARAM)&_hotkeydescs[i]);
+ }
+
+ WM_TASKBARCREATED = RegisterWindowMessageA("TaskbarCreated");
+ ShowWindow(hwndDlg, SW_HIDE);
+ hSvcHotkeyProcessor = CreateServiceFunction(MS_TABMSG_HOTKEYPROCESS, HotkeyProcessor);
+ SetTimer(hwndDlg, TIMERID_SENDLATER, TIMEOUT_SENDLATER, NULL);
+ break;
+ case WM_HOTKEY: {
+ CLISTEVENT *cli = 0;
+
+ cli = (CLISTEVENT *)CallService(MS_CLIST_GETEVENT, (WPARAM)INVALID_HANDLE_VALUE, (LPARAM)0);
+ if (cli != NULL) {
+ if (strncmp(cli->pszService, "SRMsg/TypingMessage", strlen(cli->pszService))) {
+ CallService(cli->pszService, 0, (LPARAM)cli);
+ break;
+ }
+ }
+ if (wParam == 0xc001)
+ SendMessage(hwndDlg, DM_TRAYICONNOTIFY, 101, WM_MBUTTONDOWN);
+
+ break;
+ }
+ /*
+ * handle the popup menus (session list, favorites, recents...
+ * just draw some icons, nothing more :)
+ */
+ case WM_MEASUREITEM: {
+ LPMEASUREITEMSTRUCT lpmi = (LPMEASUREITEMSTRUCT) lParam;
+ lpmi->itemHeight = 0;
+ lpmi->itemWidth = 6;
+ return TRUE;
+ }
+ case WM_DRAWITEM: {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam;
+ struct TWindowData *dat = 0;
+ if (dis->CtlType == ODT_MENU && (dis->hwndItem == (HWND)PluginConfig.g_hMenuFavorites || dis->hwndItem == (HWND)PluginConfig.g_hMenuRecent)) {
+ HICON hIcon = (HICON)dis->itemData;
+
+ DrawMenuItem(dis, hIcon, 0);
+ return TRUE;
+ } else if (dis->CtlType == ODT_MENU) {
+ HWND hWnd = M->FindWindow((HANDLE)dis->itemID);
+ DWORD idle = 0;
+
+ if (hWnd == NULL) {
+ SESSION_INFO *si = SM_FindSessionByHCONTACT((HANDLE)dis->itemID);
+
+ hWnd = si ? si->hWnd : 0;
+ }
+
+ if (hWnd)
+ dat = (struct TWindowData *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+
+ if (dis->itemData >= 0) {
+ HICON hIcon;
+ BOOL fNeedFree = FALSE;
+
+ if (dis->itemData > 0)
+ hIcon = dis->itemData & 0x10000000 ? hIcons[ICON_HIGHLIGHT] : PluginConfig.g_IconMsgEvent;
+ else if (dat != NULL) {
+ hIcon = MY_GetContactIcon(dat);
+ idle = dat->idle;
+ } else
+ hIcon = PluginConfig.g_iconContainer;
+
+ DrawMenuItem(dis, hIcon, idle);
+ if (fNeedFree)
+ DestroyIcon(hIcon);
+
+ return TRUE;
+ }
+ }
+ }
+ break;
+ case DM_TRAYICONNOTIFY: {
+ int iSelection;
+
+ if (wParam == 100 || wParam == 101) {
+ switch (lParam) {
+ case WM_LBUTTONUP: {
+ POINT pt;
+ GetCursorPos(&pt);
+ if (wParam == 100)
+ SetForegroundWindow(hwndDlg);
+ if (GetMenuItemCount(PluginConfig.g_hMenuTrayUnread) > 0) {
+ iSelection = TrackPopupMenu(PluginConfig.g_hMenuTrayUnread, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ HandleMenuEntryFromhContact(iSelection);
+ } else
+ TrackPopupMenu(GetSubMenu(PluginConfig.g_hMenuContext, 8), TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ if (wParam == 100)
+ PostMessage(hwndDlg, WM_NULL, 0, 0);
+ break;
+ }
+ case WM_MBUTTONDOWN: {
+ MENUITEMINFOA mii = {0};
+ int i, iCount = GetMenuItemCount(PluginConfig.g_hMenuTrayUnread);
+
+ if (wParam == 100)
+ SetForegroundWindow(hwndDlg);
+
+ if (iCount > 0) {
+ UINT uid = 0;
+ mii.fMask = MIIM_DATA;
+ mii.cbSize = sizeof(mii);
+ i = iCount - 1;
+ do {
+ GetMenuItemInfoA(PluginConfig.g_hMenuTrayUnread, i, TRUE, &mii);
+ if (mii.dwItemData > 0) {
+ uid = GetMenuItemID(PluginConfig.g_hMenuTrayUnread, i);
+ HandleMenuEntryFromhContact(uid);
+ break;
+ }
+ } while (--i >= 0);
+ if (uid == 0 && pLastActiveContainer != NULL) { // no session found, restore last active container
+ if (IsIconic(pLastActiveContainer->hwnd) || !IsWindowVisible(pLastActiveContainer->hwnd)) {
+ SendMessage(pLastActiveContainer->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+ SetForegroundWindow(pLastActiveContainer->hwnd);
+ SetFocus(GetDlgItem(pLastActiveContainer->hwndActive, IDC_MESSAGE));
+ } else if (GetForegroundWindow() != pLastActiveContainer->hwnd) {
+ SetForegroundWindow(pLastActiveContainer->hwnd);
+ SetFocus(GetDlgItem(pLastActiveContainer->hwndActive, IDC_MESSAGE));
+ } else {
+ if(PluginConfig.m_HideOnClose)
+ ShowWindow(pLastActiveContainer->hwnd, SW_HIDE);
+ else
+ SendMessage(pLastActiveContainer->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
+ }
+ }
+ }
+ if (wParam == 100)
+ PostMessage(hwndDlg, WM_NULL, 0, 0);
+ break;
+ }
+ case WM_RBUTTONUP: {
+ HMENU submenu = PluginConfig.g_hMenuTrayContext;
+ POINT pt;
+
+ if (wParam == 100)
+ SetForegroundWindow(hwndDlg);
+ GetCursorPos(&pt);
+ CheckMenuItem(submenu, ID_TRAYCONTEXT_DISABLEALLPOPUPS, MF_BYCOMMAND | (nen_options.iDisable ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_TRAYCONTEXT_DON40223, MF_BYCOMMAND | (nen_options.iNoSounds ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(submenu, ID_TRAYCONTEXT_DON, MF_BYCOMMAND | (nen_options.iNoAutoPopup ? MF_CHECKED : MF_UNCHECKED));
+ EnableMenuItem(submenu, ID_TRAYCONTEXT_HIDEALLMESSAGECONTAINERS, MF_BYCOMMAND | (nen_options.bTraySupport) ? MF_ENABLED : MF_GRAYED);
+ CheckMenuItem(submenu, ID_TRAYCONTEXT_SHOWTHETRAYICON, MF_BYCOMMAND | (nen_options.bTraySupport ? MF_CHECKED : MF_UNCHECKED));
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+
+ if (iSelection) {
+ MENUITEMINFO mii = {0};
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA | MIIM_ID;
+ GetMenuItemInfo(submenu, (UINT_PTR)iSelection, FALSE, &mii);
+ if (mii.dwItemData != 0) { // this must be an itm of the fav or recent menu
+ HandleMenuEntryFromhContact(iSelection);
+ } else {
+ switch (iSelection) {
+ case ID_TRAYCONTEXT_SHOWTHETRAYICON:
+ nen_options.bTraySupport = !nen_options.bTraySupport;
+ CreateSystrayIcon(nen_options.bTraySupport ? TRUE : FALSE);
+ break;
+ case ID_TRAYCONTEXT_DISABLEALLPOPUPS:
+ nen_options.iDisable ^= 1;
+ break;
+ case ID_TRAYCONTEXT_DON40223:
+ nen_options.iNoSounds ^= 1;
+ break;
+ case ID_TRAYCONTEXT_DON:
+ nen_options.iNoAutoPopup ^= 1;
+ break;
+ case ID_TRAYCONTEXT_HIDEALLMESSAGECONTAINERS: {
+ struct TContainerData *pContainer = pFirstContainer;
+
+ while (pContainer) {
+ ShowWindow(pContainer->hwnd, SW_HIDE);
+ pContainer = pContainer->pNextContainer;
+ }
+ break;
+ }
+ case ID_TRAYCONTEXT_RESTOREALLMESSAGECONTAINERS: {
+ struct TContainerData *pContainer = pFirstContainer;
+
+ while (pContainer) {
+ ShowWindow(pContainer->hwnd, SW_SHOW);
+ pContainer = pContainer->pNextContainer;
+ }
+ break;
+ }
+ case ID_TRAYCONTEXT_BE: {
+ struct TContainerData *pContainer = pFirstContainer;
+
+ nen_options.iDisable = 1;
+ nen_options.iNoSounds = 1;
+ nen_options.iNoAutoPopup = 1;
+
+ while (pContainer) {
+ SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 1);
+ pContainer = pContainer->pNextContainer;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (wParam == 100)
+ PostMessage(hwndDlg, WM_NULL, 0, 0);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ /*
+ * handle an event from the popup module (mostly window activation). Since popups may run in different threads, the message
+ * is posted to our invisible hotkey handler which does always run within the main thread.
+ * wParam is the hContact
+ * lParam the event handle
+ */
+ case DM_HANDLECLISTEVENT: {
+ CLISTEVENT *cle = (CLISTEVENT *)CallService(MS_CLIST_GETEVENT, wParam, 0);
+
+ /*
+ * if lParam == NULL, don't consider clist events, just open the message tab
+ */
+
+ if(lParam == 0) {
+ HandleMenuEntryFromhContact((int)wParam);
+ break;
+ }
+
+ /*
+ * first try, if the clist returned an event...
+ */
+ if (cle) {
+ if (ServiceExists(cle->pszService)) {
+ CallService(cle->pszService, (WPARAM)NULL, (LPARAM)cle);
+ CallService(MS_CLIST_REMOVEEVENT, (WPARAM)cle->hContact, (LPARAM)cle->hDbEvent);
+ }
+ } else { // still, we got that message posted.. the event may be waiting in tabSRMMs tray...
+ HandleMenuEntryFromhContact((int)wParam);
+ }
+ break;
+ }
+ case DM_DOCREATETAB: {
+ HWND hWnd = M->FindWindow((HANDLE)lParam);
+ if (hWnd && IsWindow(hWnd)) {
+ struct TContainerData *pContainer = 0;
+
+ SendMessage(hWnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if (pContainer) {
+ int iTabs = TabCtrl_GetItemCount(GetDlgItem(pContainer->hwnd, IDC_MSGTABS));
+ if (iTabs == 1)
+ SendMessage(pContainer->hwnd, WM_CLOSE, 0, 1);
+ else
+ SendMessage(hWnd, WM_CLOSE, 0, 1);
+
+ CreateNewTabForContact((struct TContainerData *)wParam, (HANDLE)lParam, 0, NULL, TRUE, TRUE, FALSE, 0);
+ }
+ }
+ break;
+ }
+ case DM_DOCREATETAB_CHAT: {
+ SESSION_INFO *si = SM_FindSessionByHWND((HWND)lParam);
+
+ if (si && IsWindow(si->hWnd)) {
+ struct TContainerData *pContainer = 0;
+
+ SendMessage(si->hWnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if (pContainer) {
+ int iTabs = TabCtrl_GetItemCount(GetDlgItem(pContainer->hwnd, 1159));
+ if (iTabs == 1)
+ SendMessage(pContainer->hwnd, WM_CLOSE, 0, 1);
+ else
+ SendMessage(si->hWnd, WM_CLOSE, 0, 1);
+
+ si->hWnd = CreateNewRoom((struct TContainerData *)wParam, si, TRUE, 0, 0);
+ }
+ }
+ break;
+ }
+ case DM_SENDMESSAGECOMMANDW:
+ SendMessageCommand_W(wParam, lParam);
+ if (lParam)
+ free((void *)lParam);
+ return(0);
+
+ case DM_SENDMESSAGECOMMAND:
+ SendMessageCommand(wParam, lParam);
+ if (lParam)
+ free((void *)lParam);
+ return(0);
+ /*
+ * sent from the popup to "dismiss" the event. we should do this in the main thread
+ */
+ case DM_REMOVECLISTEVENT:
+ CallService(MS_CLIST_REMOVEEVENT, wParam, lParam);
+ CallService(MS_DB_EVENT_MARKREAD, wParam, lParam);
+ return(0);
+
+ case DM_SETLOCALE: {
+ HKL hkl = (HKL)lParam;
+ HANDLE hContact = (HANDLE)wParam;
+
+ HWND hWnd = M->FindWindow(hContact);
+
+ if(hWnd) {
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ if(dat) {
+ DBVARIANT dbv;
+
+ if(hkl) {
+ dat->hkl = hkl;
+ PostMessage(dat->hwnd, DM_SETLOCALE, 0, 0);
+ }
+ if(0 == M->GetTString(hContact, SRMSGMOD_T, "locale", &dbv)) {
+ GetLocaleID(dat, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ UpdateReadChars(dat);
+ }
+ }
+ }
+ return(0);
+ }
+ /*
+ * react to changes in the desktop composition state
+ * (enable/disable DWM, change to a non-aero visual style
+ * or classic Windows theme
+ */
+ case WM_DWMCOMPOSITIONCHANGED: {
+ bool fNewAero = M->getAeroState(); // refresh dwm state
+ SendMessage(hwndDlg, WM_THEMECHANGED, 0, 0);
+ TContainerData *pContainer = pFirstContainer;
+
+ while (pContainer) {
+ if(fNewAero)
+ SetAeroMargins(pContainer);
+ else {
+ MARGINS m = {0};
+
+ if(M->m_pfnDwmExtendFrameIntoClientArea)
+ M->m_pfnDwmExtendFrameIntoClientArea(pContainer->hwnd, &m);
+ }
+ if(pContainer->SideBar->isActive())
+ RedrawWindow(GetDlgItem(pContainer->hwnd, 5000), NULL, NULL, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW); // the container for the sidebar buttons
+ RedrawWindow(pContainer->hwnd, NULL, NULL, RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+ pContainer = pContainer->pNextContainer;
+ }
+ M->BroadcastMessage(WM_DWMCOMPOSITIONCHANGED, 0, 0);
+ break;
+ }
+
+ /*
+ * this message is fired when the user changes desktop color
+ * settings (Desktop->personalize)
+ * the handler reconfigures the aero-related skin images for
+ * tabs and buttons to match the new desktop color theme.
+ */
+ case WM_DWMCOLORIZATIONCOLORCHANGED: {
+ M->getAeroState();
+ Skin->setupAeroSkins();
+ CSkin::initAeroEffect();
+ break;
+ }
+
+ /*
+ * user has changed the visual style or switched to/from
+ * classic Windows theme
+ */
+ case WM_THEMECHANGED: {
+ struct TContainerData *pContainer = pFirstContainer;
+
+ M->getAeroState();
+ Skin->setupTabCloseBitmap();
+ CSkin::initAeroEffect();
+ PluginConfig.m_ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &PluginConfig.m_ncm, 0);
+ FreeTabConfig();
+ ReloadTabConfig();
+ while (pContainer) {
+ SendMessage(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), EM_THEMECHANGED, 0, 0);
+ BroadCastContainer(pContainer, EM_THEMECHANGED, 0, 0);
+ pContainer = pContainer->pNextContainer;
+ }
+ break;
+ }
+
+ case DM_SPLITSENDACK: {
+ ACKDATA ack = {0};
+ struct SendJob *job = sendQueue->getJobByIndex((int)wParam);
+
+ ack.hContact = job->hOwner;
+ ack.hProcess = job->hSendId;
+ ack.type = ACKTYPE_MESSAGE;
+ ack.result = ACKRESULT_SUCCESS;
+
+ if (job->hOwner && job->iAcksNeeded && job->hOwner && job->iStatus == SendQueue::SQ_INPROGRESS) {
+ if (IsWindow(job->hwndOwner))
+ ::SendMessage(job->hwndOwner, HM_EVENTSENT, (WPARAM)MAKELONG(wParam, 0), (LPARAM)&ack);
+ else
+ sendQueue->ackMessage(0, (WPARAM)MAKELONG(wParam, 0), (LPARAM)&ack);
+ }
+ return 0;
+ }
+
+ case DM_LOGSTATUSCHANGE:
+ CGlobals::logStatusChange(wParam, reinterpret_cast<CContactCache *>(lParam));
+ return(0);
+
+ case DM_MUCFLASHWORKER: {
+ FLASH_PARAMS *p = reinterpret_cast<FLASH_PARAMS*>(lParam);
+
+ if(1 == wParam) {
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED, (WPARAM)p->hContact, 1);
+ p->bActiveTab = TRUE;
+ p->bInactive = FALSE;
+ p->bMustAutoswitch = p->bMustFlash = FALSE;
+ }
+
+ if(2 == wParam) {
+ p->bActiveTab = TRUE;
+ p->bInactive = FALSE;
+ p->bMustAutoswitch = p->bMustFlash = FALSE;
+ SendMessage(p->hWnd, DM_ACTIVATEME, 0, 0);
+ }
+ DoFlashAndSoundWorker(p);
+ return(0);
+ }
+
+ case WM_POWERBROADCAST:
+ case WM_DISPLAYCHANGE: {
+ struct TContainerData *pContainer = pFirstContainer;
+
+ while (pContainer) {
+ if (CSkin::m_skinEnabled) { // invalidate cached background DCs for skinned containers
+ pContainer->oldDCSize.cx = pContainer->oldDCSize.cy = 0;
+ SelectObject(pContainer->cachedDC, pContainer->oldHBM);
+ DeleteObject(pContainer->cachedHBM);
+ DeleteDC(pContainer->cachedDC);
+ pContainer->cachedDC = 0;
+ RedrawWindow(pContainer->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME);
+ }
+ pContainer = pContainer->pNextContainer;
+ }
+ break;
+ }
+
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) != WA_ACTIVE)
+ SetWindowPos(hwndDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
+ return 0;
+
+ case WM_CLOSE:
+ return 0;
+
+ case WM_TIMER:
+ if(wParam == TIMERID_SENDLATER) {
+ /*
+ * send heartbeat to each open container (to manage autoclose
+ * feature)
+ */
+ TContainerData *pContainer = pFirstContainer;
+ /*
+ * send heartbeat to each container, they use this to update
+ * dynamic content (i.e. local time in the info panel).
+ */
+ while(pContainer) {
+ SendMessage(pContainer->hwnd, WM_TIMER, TIMERID_HEARTBEAT, 0);
+ pContainer = pContainer->pNextContainer;
+ }
+ /*
+ * process send later contacts and jobs, if enough time has elapsed
+ */
+ if(sendLater->isAvail() && !sendLater->isInteractive() && (time(0) - sendLater->lastProcessed()) > CSendLater::SENDLATER_PROCESS_INTERVAL) {
+ sendLater->setLastProcessed(time(0));
+
+ /*
+ * check the list of contacts that may have new send later jobs
+ * (added on user's request)
+ */
+ sendLater->processContacts();
+
+ /*
+ * start processing the job list
+ */
+ if(!sendLater->isJobListEmpty()) {
+ KillTimer(hwndDlg, wParam);
+ sendLater->startJobListProcess();
+ SetTimer(hwndDlg, TIMERID_SENDLATER_TICK, TIMEOUT_SENDLATER_TICK, 0);
+ }
+ }
+ }
+ /*
+ * process one entry per tick (default: 200ms)
+ * TODO better timings, possibly slow down when many jobs are in the
+ * queue.
+ */
+ else if(wParam == TIMERID_SENDLATER_TICK) {
+ if(!sendLater->haveJobs()) {
+ KillTimer(hwndDlg, wParam);
+ SetTimer(hwndDlg, TIMERID_SENDLATER, TIMEOUT_SENDLATER, 0);
+ sendLater->qMgrUpdate(true);
+ }
+ else
+ sendLater->processCurrentJob();
+ }
+ break;
+
+ case WM_DESTROY: {
+ KillTimer(hwndDlg, TIMERID_SENDLATER_TICK);
+ KillTimer(hwndDlg, TIMERID_SENDLATER);
+ DestroyServiceFunction(hSvcHotkeyProcessor);
+ break;
+ }
+ }
+ return(DefWindowProc(hwndDlg, msg, wParam, lParam));
+}
diff --git a/plugins/TabSRMM/src/infopanel.cpp b/plugins/TabSRMM/src/infopanel.cpp new file mode 100644 index 0000000000..1f6b978493 --- /dev/null +++ b/plugins/TabSRMM/src/infopanel.cpp @@ -0,0 +1,1696 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: infopanel.cpp 12702 2010-09-16 02:36:17Z borkra $
+ *
+ * the info area for both im and chat sessions
+ */
+
+#include "commonheaders.h"
+
+TCHAR *xStatusDescr[] = { _T("Angry"), _T("Duck"), _T("Tired"), _T("Party"), _T("Beer"), _T("Thinking"), _T("Eating"),
+ _T("TV"), _T("Friends"), _T("Coffee"), _T("Music"), _T("Business"), _T("Camera"), _T("Funny"),
+ _T("Phone"), _T("Games"), _T("College"), _T("Shopping"), _T("Sick"), _T("Sleeping"),
+ _T("Surfing"), _T("@Internet"), _T("Engineering"), _T("Typing"), _T("Eating... yummy"),
+ _T("Having fun"), _T("Chit chatting"), _T("Crashing"), _T("Going to toilet"), _T("<undef>"),
+ _T("<undef>"), _T("<undef>")
+ };
+
+
+TInfoPanelConfig CInfoPanel::m_ipConfig = {0};
+WNDPROC CTip::m_OldMessageEditProc = 0;
+
+int CInfoPanel::setPanelHandler(TWindowData *dat, WPARAM wParam, LPARAM lParam)
+{
+ if(wParam == 0 && lParam == 0) {
+ dat->Panel->getVisibility();
+ dat->Panel->loadHeight();
+ dat->Panel->showHide();
+ }
+ else {
+ TWindowData *srcDat = (TWindowData *)wParam;
+ if(lParam == 0)
+ dat->Panel->loadHeight();
+ else {
+ if(srcDat && lParam && dat != srcDat && !dat->Panel->isPrivateHeight()) {
+ if(srcDat->bType != dat->bType && M->GetByte("syncAllPanels", 0) == 0)
+ return(0);
+
+ if(dat->pContainer->settings->fPrivate && srcDat->pContainer != dat->pContainer)
+ return(0);
+ dat->panelWidth = -1;
+ dat->Panel->setHeight((LONG)lParam);
+ }
+ }
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+ return(0);
+}
+
+void CInfoPanel::setActive(const int newActive)
+{
+ m_active = newActive ? true : false;
+}
+
+/**
+ * Load height. Private panel height is indicated by 0xffff for the high word
+ */
+void CInfoPanel::loadHeight()
+{
+ BYTE bSync = M->GetByte("syncAllPanels", 0); // sync muc <> im panels
+
+ m_height = M->GetDword(m_dat->hContact, "panelheight", -1);
+
+ if(m_height == -1 || HIWORD(m_height) == 0) {
+ if(m_dat->pContainer->settings->fPrivate)
+ m_height = m_dat->pContainer->settings->panelheight;
+ else
+ m_height = bSync ? m_defaultHeight : (m_isChat ? m_defaultMUCHeight : m_defaultHeight);
+ m_fPrivateHeight = false;
+ }
+ else {
+ m_fPrivateHeight = true;
+ m_height &= 0x0000ffff;
+ }
+
+ if (m_height <= 0 || m_height > 120) // ensure, corrupted values don't stand a chance
+ m_height = DEGRADE_THRESHOLD; // standard height for 2 lines
+}
+
+/**
+ * Save current panel height to the database
+ *
+ * @param fFlush bool: flush values to database (usually only requested by destructor)
+ */
+void CInfoPanel::saveHeight(bool fFlush)
+{
+ BYTE bSync = M->GetByte("syncAllPanels", 0);
+
+ if (m_height < 110 && m_height >= MIN_PANELHEIGHT) { // only save valid panel splitter positions
+ if(!m_fPrivateHeight) {
+ if(!m_isChat || bSync) {
+ if(m_dat->pContainer->settings->fPrivate)
+ m_dat->pContainer->settings->panelheight = m_height;
+ else {
+ PluginConfig.m_panelHeight = m_height;
+ m_defaultHeight = m_height;
+ if(fFlush)
+ M->WriteDword(SRMSGMOD_T, "panelheight", m_height);
+ }
+ }
+ else if(m_isChat && !bSync) {
+ if(m_dat->pContainer->settings->fPrivate)
+ m_dat->pContainer->settings->panelheight = m_height;
+ else {
+ PluginConfig.m_MUCpanelHeight = m_height;
+ m_defaultMUCHeight = m_height;
+ if(fFlush)
+ M->WriteDword("Chat", "panelheight", m_height);
+ }
+ }
+ }
+ else
+ M->WriteDword(m_dat->hContact, SRMSGMOD_T, "panelheight", MAKELONG(m_height, 0xffff));
+ }
+}
+
+/**
+ * Sets the new height of the panel and broadcasts it to all
+ * open sessions
+ *
+ * @param newHeight LONG: the new height.
+ * @param fBroadcast bool: broadcast the new height to all open sessions, respect
+ * container's private setting flag.
+ */
+void CInfoPanel::setHeight(LONG newHeight, bool fBroadcast)
+{
+ if(newHeight < MIN_PANELHEIGHT || newHeight > 100)
+ return;
+
+ m_height = newHeight;
+
+ if(fBroadcast) {
+ if(!m_fPrivateHeight) {
+ if(!m_dat->pContainer->settings->fPrivate)
+ M->BroadcastMessage(DM_SETINFOPANEL, (WPARAM)m_dat, (LPARAM)newHeight);
+ else
+ ::BroadCastContainer(m_dat->pContainer, DM_SETINFOPANEL, (WPARAM)m_dat, (LPARAM)newHeight);
+ }
+ saveHeight();
+ }
+}
+
+void CInfoPanel::Configure() const
+{
+ Utils::showDlgControl(m_dat->hwnd, IDC_PANELSPLITTER, m_active ? SW_SHOW : SW_HIDE);
+}
+
+void CInfoPanel::showHide() const
+{
+ HBITMAP hbm = (m_active && m_dat->pContainer->avatarMode != 3) ? m_dat->hOwnPic : (m_dat->ace ? m_dat->ace->hbmPic : PluginConfig.g_hbmUnknown);
+ BITMAP bm;
+ HWND hwndDlg = m_dat->hwnd;
+
+ if(!m_isChat) {
+ ::ShowWindow(m_dat->hwndPanelPicParent, m_active && (m_dat->hwndPanelPic || m_dat->hwndFlash) ? SW_SHOW : SW_HIDE);
+ //
+ m_dat->iRealAvatarHeight = 0;
+ ::AdjustBottomAvatarDisplay(m_dat);
+ ::GetObject(hbm, sizeof(bm), &bm);
+ ::CalcDynamicAvatarSize(m_dat, &bm);
+
+ if (m_active) {
+ if(m_dat->hwndContactPic) {
+ ::DestroyWindow(m_dat->hwndContactPic);
+ m_dat->hwndContactPic=NULL;
+ }
+ ::GetAvatarVisibility(hwndDlg, m_dat);
+ Configure();
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ }
+ Utils::showDlgControl(hwndDlg, IDC_PANELSPLITTER, m_active ? SW_SHOW : SW_HIDE);
+ ::SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ ::InvalidateRect(GetDlgItem(hwndDlg, IDC_CONTACTPIC), NULL, TRUE);
+ ::SetAeroMargins(m_dat->pContainer);
+ if(M->isAero())
+ ::InvalidateRect(GetParent(hwndDlg), NULL, FALSE);
+ ::DM_ScrollToBottom(m_dat, 0, 1);
+ }
+ else {
+ Utils::showDlgControl(hwndDlg, IDC_PANELSPLITTER, m_active ? SW_SHOW : SW_HIDE);
+
+ if (m_active) {
+ Configure();
+ ::InvalidateRect(hwndDlg, NULL, FALSE);
+ }
+
+ ::SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ ::SetAeroMargins(m_dat->pContainer);
+ if(M->isAero())
+ ::InvalidateRect(GetParent(hwndDlg), NULL, FALSE);
+ ::DM_ScrollToBottom(m_dat, 0, 1);
+ }
+}
+
+/**
+ * Decide if info panel must be visible for this session. Uses container setting and,
+ * if applicable, local (per contact) override.
+ *
+ * @return bool: panel is visible for this session
+ */
+bool CInfoPanel::getVisibility()
+{
+ if (m_dat->hContact == 0) {
+ setActive(false); // no info panel, if no hcontact
+ return(false);
+ }
+
+ BYTE bDefault = (m_dat->pContainer->dwFlags & CNT_INFOPANEL) ? 1 : 0;
+ BYTE bContact = M->GetByte(m_dat->hContact, "infopanel", 0);
+
+ BYTE visible = (bContact == 0 ? bDefault : (bContact == (BYTE)-1 ? 0 : 1));
+ setActive(visible);
+ return(m_active);
+}
+
+void CInfoPanel::mapRealRect(const RECT& rcSrc, RECT& rcDest, const SIZE& sz)
+{
+ rcDest.left = rcSrc.left;
+ rcDest.right = rcDest.left + sz.cx;
+ rcDest.top = rcSrc.top + (((rcSrc.bottom - rcSrc.top) - sz.cy) / 2);
+ rcDest.bottom = rcDest.top + sz.cy;
+}
+
+/**
+ * create an underlined version of the original font and select it
+ * in the given device context
+ *
+ * returns the previosuly selected font
+ *
+ * caller should not forget to delete the font!
+ */
+HFONT CInfoPanel::setUnderlinedFont(const HDC hdc, HFONT hFontOrig)
+{
+ LOGFONT lf;
+
+ ::GetObject(hFontOrig, sizeof(lf), &lf);
+ lf.lfUnderline = 1;
+
+ HFONT hFontNew = ::CreateFontIndirect(&lf);
+ return(reinterpret_cast<HFONT>(::SelectObject(hdc, hFontNew)));
+}
+/**
+ * Render the info panel background.
+ *
+ * @param hdc HDC: target device context
+ * @param rc RECT&: target rectangle
+ * @param item CSkinItem *: The item to render in non-aero mode
+ * @param fAero bool: aero active
+ */
+void CInfoPanel::renderBG(const HDC hdc, RECT& rc, CSkinItem *item, bool fAero, bool fAutoCalc) const
+{
+ if(m_active) {
+
+ if(fAutoCalc)
+ rc.bottom = m_height + 1;
+ if(fAero) {
+ RECT rcBlack = rc;
+ rc.bottom -= 2;
+ ::FillRect(hdc, &rc, CSkin::m_BrushBack);
+ CSkin::ApplyAeroEffect(hdc, &rc, CSkin::AERO_EFFECT_AREA_INFOPANEL);
+ rcBlack.top = rc.bottom;// + 1;
+ rcBlack.bottom = rcBlack.top + 2;
+ if(CSkin::m_pCurrentAeroEffect && CSkin::m_pCurrentAeroEffect->m_clrBack != 0)
+ ::DrawAlpha(hdc, &rcBlack, CSkin::m_pCurrentAeroEffect->m_clrBack, 90, CSkin::m_pCurrentAeroEffect->m_clrBack, 0,
+ 0, 0, 1, 0);
+ }
+ else {
+ if(CSkin::m_skinEnabled) {
+ rc.bottom -= 2;
+ CSkin::SkinDrawBG(m_dat->hwnd, m_dat->pContainer->hwnd, m_dat->pContainer, &rc, hdc);
+ item = &SkinItems[ID_EXTBKINFOPANELBG];
+ /*
+ * if new (= tabsrmm 3.x) skin item is not defined, use the old info panel
+ * field background items. That should break less skins
+ */
+ if(!item->IGNORED)
+ CSkin::DrawItem(hdc, &rc, item);
+ } else {
+ rc.bottom -= 2;
+ ::DrawAlpha(hdc, &rc, PluginConfig.m_ipBackgroundGradient, 100, PluginConfig.m_ipBackgroundGradientHigh, 0, 17,
+ 0, 0, 0);
+ if(fAutoCalc) {
+ rc.top = rc.bottom - 1;
+ rc.left--; rc.right++;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * render the content of the info panel. The target area is derived from the
+ * precalculated RECT structures in _MessageWindowData (calculated in the
+ * message window's WM_SIZE handler).
+ *
+ * @param hdc HDC: target device context
+ */
+void CInfoPanel::renderContent(const HDC hdc)
+{
+ if(m_active) {
+ if(!m_isChat) {
+ RECT rc;
+
+ /*
+ * panel picture
+ */
+
+ DRAWITEMSTRUCT dis = {0};
+
+ dis.rcItem = m_dat->rcPic;
+ dis.hDC = hdc;
+ dis.hwndItem = m_dat->hwnd;
+ if(::MsgWindowDrawHandler(0, (LPARAM)&dis, m_dat) == 0) {
+ ::PostMessage(m_dat->hwnd, WM_SIZE, 0, 1);
+ ::PostMessage(m_dat->hwnd, DM_FORCEREDRAW, 0, 0);
+ }
+
+ rc = m_dat->rcNick;
+ if(m_height >= DEGRADE_THRESHOLD) {
+ rc.top -= 2;// rc.bottom += 6;
+ }
+ RenderIPNickname(hdc, rc);
+ if(m_height >= DEGRADE_THRESHOLD) {
+ rc = m_dat->rcUIN;
+ RenderIPUIN(hdc, rc);
+ }
+ rc = m_dat->rcStatus;
+ RenderIPStatus(hdc, rc);
+ }
+ else {
+ RECT rc;
+ rc = m_dat->rcNick;
+
+ if(m_height >= DEGRADE_THRESHOLD)
+ rc.top -= 2; rc.bottom -= 2;
+
+ Chat_RenderIPNickname(hdc, rc);
+ if(m_height >= DEGRADE_THRESHOLD) {
+ rc = m_dat->rcUIN;
+ Chat_RenderIPSecondLine(hdc, rc);
+ }
+ }
+ }
+}
+
+/**
+ * Render the nickname in the info panel.
+ * This will also show the status message (if one is available)
+ * The field will dynamically adjust itself to the available info panel space. If
+ * the info panel is too small to show both nick and UIN fields, this field will show
+ * the UIN _instead_ of the nickname (most people have the nickname in the title
+ * bar anyway).
+ *
+ * @param hdc HDC: target DC for drawing
+ *
+ * @param rcItem RECT &: target rectangle
+ */
+void CInfoPanel::RenderIPNickname(const HDC hdc, RECT& rcItem)
+{
+ const TCHAR* szStatusMsg = NULL;
+ CSkinItem* item = &SkinItems[ID_EXTBKINFOPANEL];
+ const TCHAR* szTextToShow = 0;
+ bool fShowUin = false;
+ COLORREF clr = 0;
+
+ if(m_height < DEGRADE_THRESHOLD) {
+ szTextToShow = m_dat->cache->getUIN();
+ fShowUin = true;
+ } else
+ szTextToShow = m_dat->cache->getNick();
+
+ szStatusMsg = m_dat->cache->getStatusMsg();
+
+ ::SetBkMode(hdc, TRANSPARENT);
+
+ rcItem.left += 2;
+ if (szTextToShow[0]) {
+ HFONT hOldFont = 0;
+ HICON xIcon = 0;
+
+ xIcon = ::GetXStatusIcon(m_dat);
+
+ if (xIcon) {
+ ::DrawIconEx(hdc, rcItem.left, (rcItem.bottom + rcItem.top - PluginConfig.m_smcyicon) / 2, xIcon, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, 0, DI_NORMAL | DI_COMPAT);
+ ::DestroyIcon(xIcon);
+ rcItem.left += 21;
+ }
+
+ if(fShowUin) {
+ hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+ clr = m_ipConfig.clrs[IPFONTID_UIN];
+ }
+ else {
+ hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_NICK]));
+ clr = m_ipConfig.clrs[IPFONTID_NICK];
+ }
+
+ m_szNick.cx = m_szNick.cy = 0;
+
+ if (szStatusMsg) {
+ SIZE sStatusMsg, sMask;
+ DWORD dtFlags, dtFlagsNick;
+
+ ::GetTextExtentPoint32(hdc, szTextToShow, lstrlen(szTextToShow), &m_szNick);
+ ::GetTextExtentPoint32(hdc, _T("A"), 1, &sMask);
+ ::GetTextExtentPoint32(hdc, szStatusMsg, lstrlen(szStatusMsg), &sStatusMsg);
+ dtFlagsNick = DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_NOPREFIX;
+ if ((m_szNick.cx + sStatusMsg.cx + 6) < (rcItem.right - rcItem.left) || (rcItem.bottom - rcItem.top) < (2 * sMask.cy))
+ dtFlagsNick |= DT_VCENTER;
+ mapRealRect(rcItem, m_rcNick, m_szNick);
+
+ if(m_hoverFlags & HOVER_NICK)
+ setUnderlinedFont(hdc, fShowUin ? m_ipConfig.hFonts[IPFONTID_UIN] : m_ipConfig.hFonts[IPFONTID_NICK]);
+
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szTextToShow, &rcItem, dtFlagsNick, CSkin::m_glowSize, clr);
+
+ HFONT hFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_STATUS]));
+ if(m_hoverFlags & HOVER_NICK)
+ ::DeleteObject(hFont);
+
+ clr = m_ipConfig.clrs[IPFONTID_STATUS];
+
+ rcItem.left += (m_szNick.cx + 10);
+
+ if (!(dtFlagsNick & DT_VCENTER))
+ dtFlags = DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX;
+ else
+ dtFlags = DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX | DT_VCENTER;
+
+
+ rcItem.right -= 3;
+ if (rcItem.left + 30 < rcItem.right)
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szStatusMsg, &rcItem, dtFlags, CSkin::m_glowSize, clr);
+ } else {
+ GetTextExtentPoint32(hdc, szTextToShow, lstrlen(szTextToShow), &m_szNick);
+ mapRealRect(rcItem, m_rcNick, m_szNick);
+ if(m_hoverFlags & HOVER_NICK)
+ setUnderlinedFont(hdc, fShowUin ? m_ipConfig.hFonts[IPFONTID_UIN] : m_ipConfig.hFonts[IPFONTID_NICK]);
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szTextToShow, &rcItem, DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | DT_NOPREFIX, CSkin::m_glowSize, clr);
+ if(m_hoverFlags & HOVER_NICK)
+ ::DeleteObject(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+ }
+ if (hOldFont)
+ ::SelectObject(hdc, hOldFont);
+ }
+}
+
+/**
+ * Draws the UIN field for the info panel.
+ *
+ * @param hdc HDC: device context for drawing.
+ * @param rcItem RECT &: target rectangle for drawing
+ */
+void CInfoPanel::RenderIPUIN(const HDC hdc, RECT& rcItem)
+{
+ TCHAR szBuf[256];
+ HFONT hOldFont = 0;
+ CSkinItem* item = &SkinItems[ID_EXTBKINFOPANEL];
+ const TCHAR* tszUin = m_dat->cache->getUIN();
+ COLORREF clr = 0;
+
+ ::SetBkMode(hdc, TRANSPARENT);
+
+ rcItem.left += 2;
+
+ if(m_hoverFlags & HOVER_UIN)
+ hOldFont = setUnderlinedFont(hdc, m_ipConfig.hFonts[IPFONTID_UIN]);
+ else
+ hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+ clr = m_ipConfig.clrs[IPFONTID_UIN];
+
+ if (tszUin[0]) {
+ SIZE sUIN;
+ if (m_dat->idle) {
+ time_t diff = time(NULL) - m_dat->idle;
+ int i_hrs = diff / 3600;
+ int i_mins = (diff - i_hrs * 3600) / 60;
+ mir_sntprintf(szBuf, safe_sizeof(szBuf), CTranslator::get(CTranslator::GEN_IP_IDLENOTICE), tszUin, i_hrs, i_mins);
+ ::GetTextExtentPoint32(hdc, szBuf, lstrlen(szBuf), &sUIN);
+ mapRealRect(rcItem, m_rcUIN, sUIN);
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szBuf, &rcItem, DT_SINGLELINE | DT_VCENTER, CSkin::m_glowSize, clr);
+ } else {
+ ::GetTextExtentPoint32(hdc, tszUin, lstrlen(tszUin), &sUIN);
+ mapRealRect(rcItem, m_rcUIN, sUIN);
+ CSkin::RenderText(hdc, m_dat->hThemeIP, tszUin, &rcItem, DT_SINGLELINE | DT_VCENTER, CSkin::m_glowSize, clr);
+ }
+ }
+ if(m_hoverFlags & HOVER_UIN)
+ ::DeleteObject(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+
+ if (hOldFont)
+ ::SelectObject(hdc, hOldFont);
+}
+
+/**
+ * Render the info panel status field. Usually in the 2nd line, right aligned
+ * @param hdc : target device context
+ */
+void CInfoPanel::RenderIPStatus(const HDC hdc, RECT& rcItem)
+{
+ const char* szProto = m_dat->cache->getActiveProto();
+ SIZE sProto = {0}, sStatus = {0}, sTime = {0};
+ DWORD oldPanelStatusCX = m_dat->panelStatusCX;
+ RECT rc;
+ HFONT hOldFont = 0;
+ CSkinItem *item = &SkinItems[ID_EXTBKINFOPANEL];
+ const TCHAR *szFinalProto = NULL;
+ TCHAR szResult[80];
+ COLORREF clr = 0;
+
+ szResult[0] = 0;
+
+ if (m_dat->szStatus[0])
+ GetTextExtentPoint32(hdc, m_dat->szStatus, lstrlen(m_dat->szStatus), &sStatus);
+
+ /*
+ * figure out final account name
+ */
+ szFinalProto = m_dat->cache->getRealAccount();
+
+ if (szFinalProto) {
+ SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_PROTO]);
+ GetTextExtentPoint32(hdc, szFinalProto, lstrlen(szFinalProto), &sProto);
+ }
+
+ if (m_dat->hTimeZone) {
+ tmi.printDateTime(m_dat->hTimeZone, _T("t"), szResult, SIZEOF(szResult), 0);
+ GetTextExtentPoint32(hdc, szResult, lstrlen(szResult), &sTime);
+ }
+
+ m_dat->panelStatusCX = 3 + sStatus.cx + sProto.cx + 14 + (m_dat->hClientIcon ? 20 : 0) + sTime.cx + 13;;
+
+ if(m_dat->panelStatusCX != oldPanelStatusCX) {
+ SendMessage(m_dat->hwnd, WM_SIZE, 0, 0);
+ rcItem = m_dat->rcStatus;
+ }
+
+ SetBkMode(hdc, TRANSPARENT);
+ rc = rcItem;
+ rc.left += 2;
+ rc.right -=3;
+
+ if(szResult[0]) {
+ HFONT oldFont = 0;
+
+ ::DrawIconEx(hdc, rcItem.left, (rcItem.bottom - rcItem.top) / 2 - 8 + rcItem.top, PluginConfig.g_iconClock, 16, 16, 0, 0, DI_NORMAL);
+
+ oldFont = (HFONT)SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_TIME]);
+
+ clr = m_ipConfig.clrs[IPFONTID_TIME];
+ rcItem.left += 16;
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szResult, &rcItem, DT_SINGLELINE | DT_VCENTER, CSkin::m_glowSize, clr);
+ SelectObject(hdc, oldFont);
+ rc.left += (sTime.cx + 20);
+ }
+
+ hOldFont = (HFONT)SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_STATUS]);
+
+ if (m_dat->szStatus[0]) {
+ SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_STATUS]);
+ clr = m_ipConfig.clrs[IPFONTID_STATUS];
+ mapRealRect(rc, m_rcStatus, sStatus);
+ if(m_hoverFlags & HOVER_STATUS)
+ setUnderlinedFont(hdc, m_ipConfig.hFonts[IPFONTID_STATUS]);
+ CSkin::RenderText(hdc, m_dat->hThemeIP, m_dat->szStatus, &rc, DT_SINGLELINE | DT_VCENTER, CSkin::m_glowSize, clr);
+ if(m_hoverFlags & HOVER_STATUS)
+ ::DeleteObject(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_STATUS]));
+ }
+ if (szFinalProto) {
+ rc.left = rc.right - sProto.cx - 3 - (m_dat->hClientIcon ? 20 : 0);
+ SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_PROTO]);
+ clr = m_ipConfig.clrs[IPFONTID_PROTO];
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szFinalProto, &rc, DT_SINGLELINE | DT_VCENTER, CSkin::m_glowSize, clr);
+ }
+
+ if (m_dat->hClientIcon)
+ DrawIconEx(hdc, rc.right - 19, (rc.bottom + rc.top - 16) / 2, m_dat->hClientIcon, 16, 16, 0, 0, DI_NORMAL);
+
+ if (hOldFont)
+ SelectObject(hdc, hOldFont);
+}
+
+/**
+ * Draws the Nickname field (first line) in a MUC window
+ *
+ * @param hdc HDC: device context for drawing.
+ * @param rcItem RECT &: target rectangle for drawing
+ */
+void CInfoPanel::Chat_RenderIPNickname(const HDC hdc, RECT& rcItem)
+{
+ SESSION_INFO *si = reinterpret_cast<SESSION_INFO *>(m_dat->si);
+
+ HFONT hOldFont = 0;
+
+ if(si == 0)
+ return;
+
+ ::SetBkMode(hdc, TRANSPARENT);
+ m_szNick.cx = m_szNick.cy = 0;
+
+ if(m_height < DEGRADE_THRESHOLD) {
+ TCHAR tszText[2048];
+
+ mir_sntprintf(tszText, safe_sizeof(tszText), CTranslator::get(CTranslator::GEN_MUC_TOPIC_IS), si->ptszTopic ? si->ptszTopic :
+ CTranslator::get(CTranslator::GEN_MUC_NO_TOPIC));
+
+ hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+ CSkin::RenderText(hdc, m_dat->hThemeIP, tszText, &rcItem, DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX | DT_VCENTER,
+ CSkin::m_glowSize, m_ipConfig.clrs[IPFONTID_UIN]);
+ } else {
+ const TCHAR *tszNick = m_dat->cache->getNick();
+
+ hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_NICK]));
+ ::GetTextExtentPoint32(hdc, tszNick, lstrlen(tszNick), &m_szNick);
+ mapRealRect(rcItem, m_rcNick, m_szNick);
+
+ if(m_hoverFlags & HOVER_NICK)
+ setUnderlinedFont(hdc, m_ipConfig.hFonts[IPFONTID_NICK]);
+
+ CSkin::RenderText(hdc, m_dat->hThemeIP, tszNick, &rcItem, DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER,
+ CSkin::m_glowSize, m_ipConfig.clrs[IPFONTID_NICK]);
+
+ if(m_hoverFlags & HOVER_NICK)
+ ::DeleteObject(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_NICK]));
+
+ rcItem.left += (m_szNick.cx + 4);
+
+ ::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_STATUS]);
+ if(si->ptszStatusbarText) {
+ TCHAR *pTmp = _tcschr(si->ptszStatusbarText, ']');
+ pTmp += 2;
+ TCHAR tszTemp[30];
+ if(si->ptszStatusbarText[0] == '[' && pTmp > si->ptszStatusbarText && ((pTmp - si->ptszStatusbarText) < (size_t)30)) {
+ mir_sntprintf(tszTemp, pTmp - si->ptszStatusbarText, _T("%s"), si->ptszStatusbarText);
+ CSkin::RenderText(hdc, m_dat->hThemeIP, tszTemp, &rcItem, DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX | DT_VCENTER,
+ CSkin::m_glowSize, m_ipConfig.clrs[IPFONTID_STATUS]);
+ }
+ }
+ }
+ if (hOldFont)
+ ::SelectObject(hdc, hOldFont);
+}
+/**
+ * Draw 2nd line of text in the info panel.
+ * @param hdc : target device context
+ * @param rcItem : target rectangle
+ */
+void CInfoPanel::Chat_RenderIPSecondLine(const HDC hdc, RECT& rcItem)
+{
+ HFONT hOldFont = 0;
+ SIZE szTitle;
+ TCHAR szPrefix[100];
+ COLORREF clr = 0;
+
+ SESSION_INFO *si = reinterpret_cast<SESSION_INFO *>(m_dat->si);
+
+ if(si == 0)
+ return;
+
+ hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+ clr = m_ipConfig.clrs[IPFONTID_UIN];
+
+ const TCHAR *szTopicTitle = CTranslator::get(CTranslator::GEN_MUC_TOPIC_IS);
+ mir_sntprintf(szPrefix, 100, szTopicTitle, _T(""));
+ ::GetTextExtentPoint32(hdc, szPrefix, lstrlen(szPrefix), &szTitle);
+ mapRealRect(rcItem, m_rcUIN, szTitle);
+ if(m_hoverFlags & HOVER_UIN)
+ setUnderlinedFont(hdc, m_ipConfig.hFonts[IPFONTID_UIN]);
+ rcItem.right -= 3;
+ CSkin::RenderText(hdc, m_dat->hThemeIP, szPrefix, &rcItem, DT_SINGLELINE | DT_NOPREFIX | DT_TOP, CSkin::m_glowSize, clr);
+ rcItem.left += (szTitle.cx + 4);
+ if(m_hoverFlags & HOVER_UIN)
+ ::DeleteObject(::SelectObject(hdc, m_ipConfig.hFonts[IPFONTID_UIN]));
+ if(si->ptszTopic && lstrlen(si->ptszTopic) > 1)
+ CSkin::RenderText(hdc, m_dat->hThemeIP, si->ptszTopic, &rcItem, DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX | DT_TOP, CSkin::m_glowSize, clr);
+ else
+ CSkin::RenderText(hdc, m_dat->hThemeIP, CTranslator::get(CTranslator::GEN_MUC_NO_TOPIC), &rcItem, DT_TOP| DT_SINGLELINE | DT_NOPREFIX, CSkin::m_glowSize, clr);
+
+ if(hOldFont)
+ ::SelectObject(hdc, hOldFont);
+}
+/**
+ * Invalidate the info panel rectangle
+ */
+void CInfoPanel::Invalidate(BOOL fErase) const
+{
+ RECT rc;
+
+ if(m_active) {
+ ::GetClientRect(m_dat->hwnd, &rc);
+ rc.bottom = m_height;
+ ::InvalidateRect(m_dat->hwnd, &rc, fErase);
+ }
+}
+
+/**
+ * build the left click contextual menu for the info panel
+ * @return HMENU: menu handle for the fully prepared menu
+ */
+HMENU CInfoPanel::constructContextualMenu() const
+{
+ MENUITEMINFO mii = {0};
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_DATA | MIIM_ID | MIIM_BITMAP | MIIM_STRING;
+ mii.hbmpItem = HBMMENU_CALLBACK;
+
+ if(!(m_hoverFlags & HOVER_NICK))
+ return(0);
+
+ HMENU m = ::CreatePopupMenu();
+
+ if(m_hoverFlags & HOVER_NICK) {
+ Utils::addMenuItem(m, mii, ::LoadSkinnedIcon(SKINICON_OTHER_USERDETAILS), CTranslator::get(CTranslator::GEN_IP_MENU_USER_DETAILS),
+ IDC_NAME, 0);
+ Utils::addMenuItem(m, mii, ::LoadSkinnedIcon(SKINICON_OTHER_HISTORY), CTranslator::get(CTranslator::GEN_IP_MENU_HISTORY),
+ m_isChat ? IDC_CHAT_HISTORY : IDC_HISTORY, 0);
+ if(!m_isChat)
+ Utils::addMenuItem(m, mii, PluginConfig.g_iconContainer, CTranslator::get(CTranslator::GEN_IP_MENU_MSGPREFS),
+ ID_MESSAGELOGSETTINGS_FORTHISCONTACT, 1);
+ else {
+ ::AppendMenu(m, MF_STRING, IDC_CHANMGR, CTranslator::get(CTranslator::GEN_IP_MENU_ROOMPREFS));
+ if(GCW_SERVER & m_dat->si->iType)
+ ::EnableMenuItem(m, IDC_CHANMGR, MF_BYCOMMAND | MF_GRAYED);
+ }
+ ::AppendMenu(m, MF_SEPARATOR, 1000, 0);
+ Utils::addMenuItem(m, mii, PluginConfig.g_buttonBarIcons[6], CTranslator::get(CTranslator::GEN_MSG_CLOSE), IDC_SAVE, 4);
+ }
+ ::AppendMenu(m, MF_SEPARATOR, 1000, 0);
+ ::AppendMenu(m, MF_STRING, CMD_IP_COPY, CTranslator::get(CTranslator::GEN_IP_MENU_COPY));
+
+ return(m);
+}
+
+/**
+ * process internal menu commands from info panel fields
+ * if this does not handle the selected command, Utils::CmdDispatcher() will be called
+ * to chain the command through message window command handlers.
+ *
+ * @param cmd command id
+ * @return 0 if command was processed, != 0 otherwise
+ */
+
+LRESULT CInfoPanel::cmdHandler(UINT cmd)
+{
+ switch(cmd) {
+ case CMD_IP_COPY:
+ if(m_hoverFlags & HOVER_NICK) {
+ Utils::CopyToClipBoard(const_cast<wchar_t *>(m_dat->cache->getNick()), m_dat->hwnd);
+ return(S_OK);
+ }
+ else if(m_hoverFlags & HOVER_UIN) {
+ Utils::CopyToClipBoard(m_isChat ? m_dat->si->ptszTopic : const_cast<wchar_t *>(m_dat->cache->getUIN()), m_dat->hwnd);
+ return(S_OK);
+ }
+ break;
+ case IDC_CHAT_HISTORY:
+ case IDC_CHANMGR:
+ if(m_isChat) {
+ SendMessage(m_dat->hwnd, WM_COMMAND, cmd, 0);
+ return(S_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ return(S_FALSE); // not handled
+}
+
+/**
+ * handle mouse clicks on the info panel.
+ *
+ * @param pt: mouse cursor pos
+ */
+void CInfoPanel::handleClick(const POINT& pt)
+{
+ if(!m_active || m_hoverFlags == 0)
+ return;
+
+ if(!m_isChat) {
+ ::KillTimer(m_dat->hwnd, TIMERID_AWAYMSG);
+ m_dat->dwFlagsEx &= ~MWF_SHOW_AWAYMSGTIMER;
+ }
+ HMENU m = constructContextualMenu();
+ if(m) {
+ LRESULT r = ::TrackPopupMenu(m, TPM_RETURNCMD, pt.x, pt.y, 0, m_dat->hwnd, NULL);
+
+ ::DestroyMenu(m);
+ if(S_OK != cmdHandler(r))
+ Utils::CmdDispatcher(Utils::CMD_INFOPANEL, m_dat->hwnd, r, 0, 0, m_dat, m_dat->pContainer);
+ }
+ m_hoverFlags = 0;
+ Invalidate(TRUE);
+}
+
+/**
+ * peforms a hit test on the given position. returns 0, if cursor is NOT
+ * inside any of the 3 relevant hovering areas.
+ *
+ * @param pt POINT (in screen coordinates)
+ * @return Hit test result or 0 if none applies.
+ */
+int CInfoPanel::hitTest(POINT pt)
+{
+ ::ScreenToClient(m_dat->hwnd, &pt);
+
+ if(!m_isChat && ::PtInRect(&m_rcStatus, pt))
+ return(HTSTATUS);
+ else if(::PtInRect(&m_rcNick, pt))
+ return(HTNICK);
+ else if(::PtInRect(&m_rcUIN, pt))
+ return(HTUIN);
+
+ return(HTNIRVANA);
+}
+/**
+ * track mouse movements inside the panel. Needed for tooltip activation
+ * and to hover the info panel fields.
+ *
+ * @param pt : mouse coordinates (screen)
+ */
+void CInfoPanel::trackMouse(POINT& pt)
+{
+ if(!m_active)
+ return;
+
+ int result = hitTest(pt);
+
+ DWORD dwOldHovering = m_hoverFlags;
+ m_hoverFlags = 0;
+
+ switch(result) {
+ case HTSTATUS:
+ m_hoverFlags |= HOVER_STATUS;
+ ::SetCursor(LoadCursor(0, IDC_HAND));
+ break;
+
+ case HTNICK:
+ m_hoverFlags |= HOVER_NICK;
+ ::SetCursor(LoadCursor(0, IDC_HAND));
+ break;
+
+ case HTUIN:
+ ::SetCursor(LoadCursor(0, IDC_HAND));
+ m_hoverFlags |= HOVER_UIN;
+ break;
+ }
+
+ if(m_hoverFlags) {
+ if (!(m_dat->dwFlagsEx & MWF_SHOW_AWAYMSGTIMER)) {
+ ::SetTimer(m_dat->hwnd, TIMERID_AWAYMSG, 1000, 0);
+ m_dat->dwFlagsEx |= MWF_SHOW_AWAYMSGTIMER;
+ }
+ }
+ if(dwOldHovering != m_hoverFlags)
+ Invalidate(TRUE);
+ if(m_hoverFlags == 0)
+ m_dat->dwFlagsEx &= ~MWF_SHOW_AWAYMSGTIMER;
+}
+
+/**
+ * activate a tooltip
+ * @param ctrlId : control id
+ * @param lParam : typically a TCHAR * for the tooltip text
+ */
+void CInfoPanel::showTip(UINT ctrlId, const LPARAM lParam)
+{
+ if (m_active && m_dat->hwndTip) {
+ RECT rc;
+ TCHAR szTitle[256];
+ HWND hwndDlg = m_dat->hwnd;
+
+ ::GetWindowRect(GetDlgItem(hwndDlg, ctrlId), &rc);
+
+ ::SendMessage(m_dat->hwndTip, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(rc.left, rc.bottom));
+ if (lParam)
+ m_dat->ti.lpszText = reinterpret_cast<TCHAR *>(lParam);
+ else {
+ TCHAR temp[1024];
+ DBVARIANT dbv = {0};
+ size_t pos;
+ BYTE xStatus = 0;
+
+ if(m_hwndConfig)
+ return;
+
+ mir_sntprintf(temp, 1024, RTF_DEFAULT_HEADER, 0, 0, 0, 30*15);
+
+ tstring *str = new tstring(temp);
+
+ mir_sntprintf(temp, 1024, CTranslator::get(CTranslator::GEN_INFOTIP_STATUSMSG),
+ m_dat->cache->getStatusMsg() ? m_dat->cache->getStatusMsg() : CTranslator::get(CTranslator::GEN_NO_STATUS));
+ str->append(temp);
+
+ if((xStatus = m_dat->cache->getXStatusId())) {
+ TCHAR *tszXStatusName = 0;
+ if(0 == M->GetTString(m_dat->cache->getContact(), m_dat->cache->getProto(), "XStatusName", &dbv))
+ tszXStatusName = dbv.ptszVal;
+ else if(xStatus > 0 && xStatus <= 31)
+ tszXStatusName = xStatusDescr[xStatus - 1];
+
+ if(tszXStatusName) {
+ str->append(CTranslator::get(CTranslator::GEN_INFOTIP_XSTATUS));
+ mir_sntprintf(temp, 1024, _T("%s%s%s"), tszXStatusName, m_dat->cache->getXStatusMsg() ? _T(" / ") : _T(""),
+ m_dat->cache->getXStatusMsg() ? m_dat->cache->getXStatusMsg() : _T(""));
+ str->append(temp);
+ if(dbv.ptszVal)
+ mir_free(dbv.ptszVal);
+ }
+ }
+
+ if(m_dat->cache->getListeningInfo()) {
+ mir_sntprintf(temp, 1024, CTranslator::get(CTranslator::GEN_INFOTIP_LISTENING), m_dat->cache->getListeningInfo());
+ str->append(temp);
+ }
+
+ if(0 == M->GetTString(m_dat->cache->getActiveContact(), m_dat->cache->getActiveProto(), "MirVer", &dbv)) {
+ mir_sntprintf(temp, 1024, CTranslator::get(CTranslator::GEN_INFOTIP_CLIENT), dbv.ptszVal);
+ ::DBFreeVariant(&dbv);
+ str->append(temp);
+ }
+ str->append(_T("}"));
+
+ /*
+ * convert line breaks to rtf
+ */
+ /*
+ while((pos = str.find(_T("\r\n"))) != str.npos) {
+ str.erase(pos, 2);
+ str.insert(pos, _T("\\line "));
+ }
+ */
+ while((pos = str->find(_T("\n"))) != str->npos) {
+ str->erase(pos, 1);
+ str->insert(pos, _T("\\line "));
+ }
+
+ POINT pt;
+ RECT rc = {0, 0, 400, 600};
+ GetCursorPos(&pt);
+ m_tip = new CTip(m_dat->hwnd, m_dat->hContact, str->c_str(), this);
+ delete str;
+ m_tip->show(rc, pt, m_dat->hTabIcon, m_dat->szStatus);
+ return;
+ }
+ mir_sntprintf(szTitle, safe_sizeof(szTitle), CTranslator::get(CTranslator::GEN_IP_TIP_TITLE));
+ ::SendMessage(m_dat->hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&m_dat->ti);
+ ::SendMessage(m_dat->hwndTip, TTM_SETMAXTIPWIDTH, 0, 350);
+
+ ::SendMessage(m_dat->hwndTip, TTM_SETTITLE, 1, (LPARAM)szTitle);
+ ::SendMessage(m_dat->hwndTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&m_dat->ti);
+ ::GetCursorPos(&m_dat->ptTipActivation);
+ }
+}
+
+/**
+ * hide a tooltip (if it was created)
+ * this is only used from outside (i.e. container window dialog)
+ *
+ * hwndNew = window to become active (as reported by WM_ACTIVATE).
+ */
+void CInfoPanel::hideTip(const HWND hwndNew)
+{
+ if(m_tip) {
+ if(hwndNew == m_tip->getHwnd())
+ return;
+ if(::IsWindow(m_tip->getHwnd()))
+ ::DestroyWindow(m_tip->getHwnd());
+ m_tip = 0;
+ }
+}
+
+/**
+ * draw the background (and border) of the parent control that holds the avs-based avatar display
+ * (ACC window class). Only required when support for animated avatars is enabled because
+ * native avatar rendering does not support animated images.
+ * To avoid clipping issues, this is done during WM_ERASEBKGND.
+ */
+INT_PTR CALLBACK CInfoPanel::avatarParentSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_ERASEBKGND:
+ {
+ /*
+ * parent window of the infopanel ACC control
+ */
+ RECT rc, rcItem;
+ TWindowData* dat = (TWindowData *)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+
+ if(dat == 0)
+ break;
+
+ GetClientRect(hwnd, &rcItem);
+ rc = rcItem;
+ if(!IsWindowEnabled(hwnd) || !dat->Panel->isActive() || dat->showInfoPic == 0)
+ return(TRUE);
+
+ HDC dcWin = (HDC)wParam;
+
+ if(M->isAero()) {
+ HDC hdc;
+ HBITMAP hbm, hbmOld;
+ LONG cx = rcItem.right - rcItem.left;
+ LONG cy = rcItem.bottom - rcItem.top;
+
+ rc.left -= 3; rc.right += 3;
+ rc.bottom += 2;
+
+ hdc = CreateCompatibleDC(dcWin);
+ hbm = CSkin::CreateAeroCompatibleBitmap(rc, dcWin);
+ hbmOld = (HBITMAP)SelectObject(hdc, hbm);
+
+ if(CSkin::m_pCurrentAeroEffect == 0)
+ FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ else {
+ if(CSkin::m_pCurrentAeroEffect->m_finalAlpha == 0)
+ CSkin::ApplyAeroEffect(hdc, &rc, CSkin::AERO_EFFECT_AREA_INFOPANEL, 0);
+ else {
+ FillRect(hdc, &rc, CSkin::m_BrushBack);
+ CSkin::ApplyAeroEffect(hdc, &rc, CSkin::AERO_EFFECT_AREA_INFOPANEL, 0);
+ }
+ }
+ BitBlt(dcWin, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
+ SelectObject(hdc, hbmOld);
+ DeleteObject(hbm);
+ DeleteDC(hdc);
+ }
+ else {
+ rc.bottom += 2;
+ rc.left -= 3; rc.right += 3;
+ dat->Panel->renderBG(dcWin, rc, &SkinItems[ID_EXTBKINFOPANELBG], M->isAero(), false);
+ }
+ if(CSkin::m_bAvatarBorderType == 1) {
+ HRGN clipRgn = 0;
+
+ if(dat->hwndPanelPic) {
+ RECT rcPic;
+ GetClientRect(dat->hwndPanelPic, &rcPic);
+ LONG ix = ((rcItem.right - rcItem.left) - rcPic.right) / 2 - 1;
+ LONG iy = ((rcItem.bottom - rcItem.top) - rcPic.bottom) / 2 - 1;
+
+ clipRgn = CreateRectRgn(ix, iy, ix + rcPic.right + 2, iy + rcPic.bottom + 2);
+ }
+ else
+ clipRgn = CreateRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
+ HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
+ FrameRgn(dcWin, clipRgn, hbr, 1, 1);
+ DeleteObject(hbr);
+ DeleteObject(clipRgn);
+ }
+ return(TRUE);
+ }
+ default:
+ break;
+ }
+ return(DefWindowProc(hwnd, msg, wParam, lParam));
+}
+
+/**
+ * Stub for the dialog procedure. Just handles INITDIALOG and sets
+ * our userdata. Real processing is done by ConfigDlgProc()
+ *
+ * @params Like a normal dialog procedure
+ */
+INT_PTR CALLBACK CInfoPanel::ConfigDlgProcStub(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CInfoPanel *infoPanel = reinterpret_cast<CInfoPanel *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+
+ if(infoPanel)
+ return(infoPanel->ConfigDlgProc(hwnd, msg, wParam, lParam));
+
+ switch(msg) {
+ case WM_INITDIALOG: {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ infoPanel = reinterpret_cast<CInfoPanel *>(lParam);
+ return(infoPanel->ConfigDlgProc(hwnd, msg, wParam, lParam));
+ }
+ default:
+ break;
+ }
+ return(FALSE);
+}
+
+/**
+ * dialog procedure for the info panel config popup
+ */
+INT_PTR CALLBACK CInfoPanel::ConfigDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG: {
+ TCHAR tszTitle[100];
+
+ mir_sntprintf(tszTitle, 100, CTranslator::getOpt(CTranslator::OPT_IPANEL_VISBILITY_TITLE),
+ m_isChat ? CTranslator::getOpt(CTranslator::OPT_IPANEL_VISIBILTY_CHAT) : CTranslator::getOpt(CTranslator::OPT_IPANEL_VISIBILTY_IM));
+ ::SetDlgItemText(hwnd, IDC_STATIC_VISIBILTY, tszTitle);
+
+ mir_sntprintf(tszTitle, 100, m_isChat ? CTranslator::getOpt(CTranslator::OPT_IPANEL_SYNC_TITLE_IM) :
+ CTranslator::getOpt(CTranslator::OPT_IPANEL_SYNC_TITLE_MUC));
+
+ ::SetDlgItemText(hwnd, IDC_NOSYNC, tszTitle);
+
+ ::SendDlgItemMessage(hwnd, IDC_PANELVISIBILITY, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_IPANEL_VIS_INHERIT));
+ ::SendDlgItemMessage(hwnd, IDC_PANELVISIBILITY, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_IPANEL_VIS_OFF));
+ ::SendDlgItemMessage(hwnd, IDC_PANELVISIBILITY, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_IPANEL_VIS_ON));
+
+ BYTE v = M->GetByte(m_dat->hContact, "infopanel", 0);
+ ::SendDlgItemMessage(hwnd, IDC_PANELVISIBILITY, CB_SETCURSEL, (WPARAM)(v == 0 ? 0 : (v == (BYTE)-1 ? 1 : 2)), 0);
+
+ ::SendDlgItemMessage(hwnd, IDC_PANELSIZE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_IPANEL_SIZE_GLOBAL));
+ ::SendDlgItemMessage(hwnd, IDC_PANELSIZE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_IPANEL_SIZE_PRIVATE));
+
+ ::SendDlgItemMessage(hwnd, IDC_PANELSIZE, CB_SETCURSEL, (WPARAM)(m_fPrivateHeight ? 1 : 0), 0);
+
+ ::CheckDlgButton(hwnd, IDC_NOSYNC, M->GetByte("syncAllPanels", 0) ? BST_UNCHECKED : BST_CHECKED);
+
+ Utils::showDlgControl(hwnd, IDC_IPCONFIG_PRIVATECONTAINER, m_dat->pContainer->settings->fPrivate ? SW_SHOW : SW_HIDE);
+
+ if(!m_isChat) {
+ v = M->GetByte(m_dat->hContact, SRMSGMOD_T, "hideavatar", -1);
+ ::SendDlgItemMessage(hwnd, IDC_PANELPICTUREVIS, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPGLOBAL));
+ ::SendDlgItemMessage(hwnd, IDC_PANELPICTUREVIS, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_AVON));
+ ::SendDlgItemMessage(hwnd, IDC_PANELPICTUREVIS, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_AVOFF));
+ ::SendDlgItemMessage(hwnd, IDC_PANELPICTUREVIS, CB_SETCURSEL, (v == (BYTE)-1 ? 0 : (v == 1 ? 1 : 2)), 0);
+ }
+ else
+ Utils::enableDlgControl(hwnd, IDC_PANELPICTUREVIS, FALSE);
+
+ return(FALSE);
+ }
+
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ {
+ HWND hwndChild = (HWND)lParam;
+ UINT id = ::GetDlgCtrlID(hwndChild);
+
+ if(m_configDlgFont == 0) {
+ HFONT hFont = (HFONT)::SendDlgItemMessage(hwnd, IDC_IPCONFIG_TITLE, WM_GETFONT, 0, 0);
+ LOGFONT lf = {0};
+
+ ::GetObject(hFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ m_configDlgBoldFont = ::CreateFontIndirect(&lf);
+
+ lf.lfHeight = (int)(lf.lfHeight * 1.2);
+ m_configDlgFont = ::CreateFontIndirect(&lf);
+ ::SendDlgItemMessage(hwnd, IDC_IPCONFIG_TITLE, WM_SETFONT, (WPARAM)m_configDlgFont, FALSE);
+ }
+
+ if(hwndChild == ::GetDlgItem(hwnd, IDC_IPCONFIG_TITLE)) {
+ ::SetTextColor((HDC)wParam, RGB(60, 60, 150));
+ ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_configDlgFont, FALSE);
+ } else if(id == IDC_IPCONFIG_FOOTER || id == IDC_SIZE_TIP || id == IDC_IPCONFIG_PRIVATECONTAINER)
+ ::SetTextColor((HDC)wParam, RGB(160, 50, 50));
+ else if(id == IDC_GROUP_SIZE || id == IDC_GROUP_SCOPE || id == IDC_GROUP_OTHER)
+ ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_configDlgBoldFont, FALSE);
+
+ ::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
+ return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
+ }
+
+ case WM_COMMAND: {
+ LONG lOldHeight = m_height;
+
+ switch(LOWORD(wParam)) {
+ case IDC_PANELSIZE: {
+ LRESULT iResult = ::SendDlgItemMessage(hwnd, IDC_PANELSIZE, CB_GETCURSEL, 0, 0);
+
+ if(iResult == 0) {
+ if(m_fPrivateHeight) {
+ M->WriteDword(m_dat->hContact, SRMSGMOD_T, "panelheight", m_height);
+ loadHeight();
+ }
+ }
+ else if(iResult == 1) {
+ M->WriteDword(m_dat->hContact, SRMSGMOD_T, "panelheight",
+ MAKELONG(M->GetDword(m_dat->hContact, "panelheight", m_height), 0xffff));
+ loadHeight();
+ }
+ break;
+ }
+
+ case IDC_PANELPICTUREVIS: {
+ BYTE vOld = M->GetByte(m_dat->hContact, SRMSGMOD_T, "hideavatar", -1);
+ LRESULT iResult = ::SendDlgItemMessage(hwnd, IDC_PANELPICTUREVIS, CB_GETCURSEL, 0, 0);
+
+ BYTE vNew = (iResult == 0 ? (BYTE)-1 : (iResult == 1 ? 1 : 0));
+ if(vNew != vOld) {
+ if(vNew == (BYTE)-1)
+ DBDeleteContactSetting(m_dat->hContact, SRMSGMOD_T, "hideavatar");
+ else
+ M->WriteByte(m_dat->hContact, SRMSGMOD_T, "hideavatar", vNew);
+ m_dat->panelWidth = -1;
+ ::ShowPicture(m_dat, FALSE);
+ ::SendMessage(m_dat->hwnd, WM_SIZE, 0, 0);
+ ::DM_ScrollToBottom(m_dat, 0, 1);
+ }
+ break;
+ }
+
+ case IDC_PANELVISIBILITY: {
+ BYTE vOld = M->GetByte(m_dat->hContact, SRMSGMOD_T, "infopanel", 0);
+ LRESULT iResult = ::SendDlgItemMessage(hwnd, IDC_PANELVISIBILITY, CB_GETCURSEL, 0, 0);
+
+ BYTE vNew = (iResult == 0 ? 0 : (iResult == 1 ? (BYTE)-1 : 1));
+ if(vNew != vOld) {
+ M->WriteByte(m_dat->hContact, SRMSGMOD_T, "infopanel", vNew);
+ getVisibility();
+ showHide();
+ }
+ break;
+ }
+
+ case IDC_SIZECOMPACT:
+ setHeight(MIN_PANELHEIGHT + 2, true);
+ break;
+
+ case IDC_SIZENORMAL:
+ setHeight(DEGRADE_THRESHOLD, true);
+ break;
+
+ case IDC_SIZELARGE:
+ setHeight(51, true);
+ break;
+
+ case IDC_NOSYNC:
+ M->WriteByte(SRMSGMOD_T, "syncAllPanels", ::IsDlgButtonChecked(hwnd, IDC_NOSYNC) ? 0 : 1);
+ if(!IsDlgButtonChecked(hwnd, IDC_NOSYNC)) {
+ loadHeight();
+ if(!m_dat->pContainer->settings->fPrivate)
+ M->BroadcastMessage(DM_SETINFOPANEL, (WPARAM)m_dat, (LPARAM)m_defaultHeight);
+ else
+ ::BroadCastContainer(m_dat->pContainer, DM_SETINFOPANEL, (WPARAM)m_dat, (LPARAM)m_defaultHeight);
+ } else {
+ if(!m_dat->pContainer->settings->fPrivate)
+ M->BroadcastMessage(DM_SETINFOPANEL, (WPARAM)m_dat, 0);
+ else
+ ::BroadCastContainer(m_dat->pContainer,DM_SETINFOPANEL, (WPARAM)m_dat, 0);
+ }
+ break;
+ }
+ if(m_height != lOldHeight) {
+ ::SendMessage(m_dat->hwnd, WM_SIZE, 0, 0);
+ m_dat->panelWidth = -1;
+ ::SetAeroMargins(m_dat->pContainer);
+ ::RedrawWindow(m_dat->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
+ ::RedrawWindow(GetParent(m_dat->hwnd), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
+ }
+ break;
+ }
+
+ case WM_CLOSE:
+ if(wParam == 1 && lParam == 1) {
+ ::DestroyWindow(hwnd);
+ }
+ break;
+
+ case WM_DESTROY: {
+ ::DeleteObject(m_configDlgBoldFont);
+ ::DeleteObject(m_configDlgFont);
+
+ m_configDlgBoldFont = m_configDlgFont = 0;
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0L);
+ break;
+ }
+ }
+ return(FALSE);
+}
+
+/**
+ * invoke info panel config popup dialog
+ * @param pt : mouse coordinates (screen)
+ * @return : always 0
+ */
+int CInfoPanel::invokeConfigDialog(const POINT& pt)
+{
+ RECT rc;
+ POINT ptTest = pt;
+
+ if(!m_active)
+ return(0);
+
+ ::GetWindowRect(m_dat->hwnd, &rc);
+ rc.bottom = rc.top + m_height;
+ rc.right -= m_dat->panelWidth;
+
+ if(!::PtInRect(&rc, ptTest))
+ return(0);
+
+ if(m_hwndConfig == 0) {
+ m_configDlgBoldFont = m_configDlgFont = 0;
+ m_hwndConfig = ::CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_INFOPANEL), 0 /*m_dat->pContainer->hwnd */,
+ ConfigDlgProcStub, (LPARAM)this);
+ if(m_hwndConfig) {
+ RECT rc, rcLog;
+ POINT pt;
+
+ TranslateDialogDefault(m_hwndConfig);
+
+ ::GetClientRect(m_hwndConfig, &rc);
+ ::GetWindowRect(GetDlgItem(m_dat->hwnd, m_isChat ? IDC_CHAT_LOG : IDC_LOG), &rcLog);
+ pt.x = rcLog.left;
+ pt.y = rcLog.top;
+ //::ScreenToClient(m_dat->pContainer->hwnd, &pt);
+
+ m_fDialogCreated = true;
+ ::SetWindowPos(m_hwndConfig, HWND_TOP, pt.x + 10, pt.y - (m_active ? 10 : 0), 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/**
+ * remove the info panel configuration dialog
+ * @param fForced: bool, if true, dismiss it under any circumstances, even
+ * with the pointer still inside the dialog.
+ */
+void CInfoPanel::dismissConfig(bool fForced)
+{
+ if(m_hwndConfig == 0)
+ return;
+
+ POINT pt;
+ RECT rc;
+
+ if(!m_fDialogCreated) {
+ ::GetCursorPos(&pt);
+ ::GetWindowRect(m_hwndConfig, &rc);
+ if(fForced || !PtInRect(&rc, pt)) {
+ SendMessage(m_hwndConfig, WM_CLOSE, 1, 1);
+ m_hwndConfig = 0;
+ }
+ }
+ m_fDialogCreated = false;
+}
+
+/**
+ * construct a richedit tooltip object.
+ *
+ * @param hwndParent HWND owner (used only for position calculation)
+ * @param hContact HANDLE contact handle
+ * @param pszText TCHAR* the content of the rich edit control
+ * @param panel CInfoPanel* the panel which owns it
+ */
+CTip::CTip(const HWND hwndParent, const HANDLE hContact, const TCHAR *pszText, const CInfoPanel* panel)
+{
+ m_hwnd = ::CreateWindowEx(WS_EX_TOOLWINDOW, _T("RichEditTipClass"), _T(""), (M->isAero() ? WS_THICKFRAME : WS_BORDER)|WS_POPUPWINDOW|WS_TABSTOP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ 0, 0, 40, 40, 0, 0, g_hInst, this);
+
+ m_hRich = ::CreateWindowEx(0, RICHEDIT_CLASS, _T(""), WS_CHILD | ES_MULTILINE | ES_AUTOVSCROLL | ES_NOHIDESEL | ES_READONLY | WS_VSCROLL | WS_TABSTOP,
+ 0, 0, 40, 40, m_hwnd, reinterpret_cast<HMENU>(1000), g_hInst, NULL);
+
+ ::SendMessage(m_hRich, EM_AUTOURLDETECT, (WPARAM) TRUE, 0);
+ ::SendMessage(m_hRich, EM_SETEVENTMASK, 0, ENM_LINK);
+ ::SendMessage(m_hRich, WM_SETFONT, (WPARAM)CInfoPanel::m_ipConfig.hFonts[IPFONTID_STATUS], 0);
+
+ m_hContact = hContact;
+ if(pszText)
+ m_pszText = M->utf8_encodeT(pszText);
+ else
+ m_pszText = 0;
+ m_panel = panel;
+ m_hwndParent = hwndParent;
+ m_OldMessageEditProc = (WNDPROC)SetWindowLongPtr(m_hRich, GWLP_WNDPROC, (LONG_PTR)RichEditProc);
+}
+
+/**
+ * Show the tooltip at the given position (the position can be adjusted to keep it on screen and
+ * inside its parent window.
+ *
+ * it will auto-adjust the size (height only) of the richedit control to fit the m_pszText
+ *
+ * @param rc dimensions of the tip (left and top should be 0)
+ * @param pt point in screen coordinates
+ * @param hIcon optional icon to display in the tip header
+ * @param szTitle optional title to display in the tip header
+ */
+void CTip::show(const RECT& rc, POINT& pt, const HICON hIcon, const TCHAR *szTitle)
+{
+ HDC hdc = ::GetDC(m_hwnd);
+ FORMATRANGE fr = {0};
+ RECT rcPage = {0, 0, 0, 0};
+ RECT rcParent;
+ SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
+ int twips = (int)(15.0f / PluginConfig.g_DPIscaleY);
+
+ int xBorder, yBorder;
+ m_leftWidth = (m_panel->getDat()->hClientIcon || m_panel->getDat()->hXStatusIcon ? LEFT_BAR_WIDTH : 0);
+
+ xBorder = M->isAero() ? GetSystemMetrics(SM_CXSIZEFRAME) : 1;
+ yBorder = M->isAero() ? GetSystemMetrics(SM_CYSIZEFRAME) : 1;
+
+ m_hIcon = hIcon;
+ m_szTitle = szTitle;
+
+ ::SendMessage(m_hRich, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(4, 4));
+ ::SendMessage(m_hRich, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)m_pszText);
+
+ if (PluginConfig.g_SmileyAddAvail) {
+ SMADD_RICHEDIT3 smadd;
+ CContactCache* c = CContactCache::getContactCache(m_hContact);
+ ::SendMessage(m_hRich, EM_SETBKGNDCOLOR, 0, (LPARAM)PluginConfig.m_ipBackgroundGradientHigh);
+ if(c) {
+ ZeroMemory(&smadd, sizeof(smadd));
+
+ smadd.cbSize = sizeof(smadd);
+ smadd.hwndRichEditControl = m_hRich;
+ smadd.Protocolname = const_cast<char *>(c->getActiveProto());
+ smadd.hContact = c->getActiveContact();
+ smadd.flags = 0;
+ smadd.rangeToReplace = NULL;
+ smadd.disableRedraw = TRUE;
+ CallService(MS_SMILEYADD_REPLACESMILEYS, TABSRMM_SMILEYADD_BKGCOLORMODE, (LPARAM)&smadd);
+ }
+ }
+
+ ::GetWindowRect(m_hwndParent, &rcParent);
+ if(pt.x + rc.right > rcParent.right)
+ pt.x = rcParent.right - rc.right - 5;
+
+ m_rcRich = rc;
+
+ m_rcRich.bottom = 800;
+ m_rcRich.left = LEFT_BORDER + m_leftWidth; m_rcRich.top = TOP_BORDER;
+ m_rcRich.right -= (LEFT_BORDER + RIGHT_BORDER + m_leftWidth);
+
+ m_rcRich.right = m_rcRich.left + (twips * (m_rcRich.right - m_rcRich.left)) - 10 * twips;
+ m_rcRich.bottom = m_rcRich.top + (twips * (m_rcRich.bottom - m_rcRich.top));
+
+ fr.hdc = hdc;
+ fr.hdcTarget = hdc;
+ fr.rc = m_rcRich;
+ fr.rcPage = rcPage;
+ fr.chrg.cpMax = -1;
+ fr.chrg.cpMin = 0;
+ LRESULT lr = ::SendMessage(m_hRich, EM_FORMATRANGE, 0, (LPARAM)&fr);
+ m_szRich.cx = ((fr.rc.right - fr.rc.left) / twips) + 8;
+ m_szRich.cy = ((fr.rc.bottom - fr.rc.top) / twips) + 3;
+
+ m_rcRich.right = m_rcRich.left + m_szRich.cx;
+ m_rcRich.bottom = m_rcRich.top + m_szRich.cy;
+
+ ::SendMessage(m_hRich, EM_FORMATRANGE, 0, (LPARAM)NULL); // required, clear cached painting data in the richedit
+
+ ::SetWindowPos(m_hwnd, HWND_TOP, pt.x - 5, pt.y - 5, m_szRich.cx + m_leftWidth + LEFT_BORDER + RIGHT_BORDER + 2 * xBorder,
+ m_szRich.cy + TOP_BORDER + BOTTOM_BORDER + 2 * yBorder, SWP_NOACTIVATE|SWP_SHOWWINDOW);
+
+ ::SetWindowPos(m_hRich, 0, LEFT_BORDER + m_leftWidth, TOP_BORDER, m_szRich.cx, m_szRich.cy, SWP_SHOWWINDOW);
+
+ ::ReleaseDC(m_hwnd, hdc);
+}
+
+/**
+ * register richedit tooltip window class
+ */
+void CTip::registerClass()
+{
+ WNDCLASSEX wc;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = _T("RichEditTipClass");
+ wc.lpfnWndProc = (WNDPROC)CTip::WndProcStub;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(CTip *);
+ wc.hbrBackground = 0;
+ wc.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_PARENTDC;
+ RegisterClassEx(&wc);
+}
+
+/**
+ * subclass the rich edit control inside the tip. Needed to hide the blinking
+ * caret and prevent all scrolling actions.
+ */
+INT_PTR CALLBACK CTip::RichEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_SETCURSOR:
+ ::HideCaret(hwnd);
+ break;
+
+ case WM_ERASEBKGND:
+ return(1);
+
+ case WM_NCCALCSIZE:
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VSCROLL);
+ EnableScrollBar(hwnd, SB_VERT, ESB_DISABLE_BOTH);
+ ShowScrollBar(hwnd, SB_VERT, FALSE);
+ break;
+
+ case WM_VSCROLL:
+ return(0);
+ }
+ return(::CallWindowProc(m_OldMessageEditProc, hwnd, msg, wParam, lParam));
+}
+
+/**
+ * stub for the tip control window procedure. Just handle WM_CREATE and set the
+ * this pointer.
+ */
+INT_PTR CALLBACK CTip::WndProcStub(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CTip *tip = reinterpret_cast<CTip *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+
+ if(tip)
+ return(tip->WndProc(hwnd, msg, wParam, lParam));
+
+ switch(msg) {
+ case WM_CREATE: {
+ CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT *>(lParam);
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
+ }
+ default:
+ break;
+ }
+ return(::DefWindowProc(hwnd, msg, wParam, lParam));
+}
+
+/**
+ * the window procedure for the tooltip window.
+ */
+INT_PTR CALLBACK CTip::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_ACTIVATE:
+ case WM_SETCURSOR:
+ ::KillTimer(hwnd, 1000);
+ ::SetTimer(hwnd, 1000, 200, 0);
+
+ if(msg == WM_ACTIVATE && LOWORD(wParam) == WA_INACTIVE)
+ ::DestroyWindow(hwnd);
+ break;
+
+ /* prevent resizing */
+ case WM_NCHITTEST:
+ return(HTCLIENT);
+ break;
+
+ case WM_ERASEBKGND: {
+ HDC hdc = (HDC) wParam;
+ RECT rc;
+ TCHAR szTitle[128];
+ COLORREF clr = CInfoPanel::m_ipConfig.clrs[IPFONTID_NICK];
+ GetClientRect(hwnd, &rc);
+ CContactCache* c = CContactCache::getContactCache(m_hContact);
+ RECT rcText = {0, 0, rc.right, TOP_BORDER};
+ LONG cx = rc.right;
+ LONG cy = rc.bottom;
+ HANDLE hTheme = 0;
+
+ mir_sntprintf(szTitle, 128, m_szTitle ? _T("%s (%s)") : _T("%s%s"), c->getNick(), m_szTitle ? m_szTitle : _T(""));
+
+ if(m_panel) {
+ HDC hdcMem = ::CreateCompatibleDC(hdc);
+ HBITMAP hbm = ::CSkin::CreateAeroCompatibleBitmap(rc, hdc);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(hdcMem, hbm));
+ HFONT hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdcMem, CInfoPanel::m_ipConfig.hFonts[IPFONTID_NICK]));
+
+
+ ::SetBkMode(hdcMem, TRANSPARENT);
+ rc.bottom += 2;
+ rc.left -= 4;rc.right += 4;
+ HBRUSH br = ::CreateSolidBrush(PluginConfig.m_ipBackgroundGradientHigh);
+ if(M->isAero()) {
+ ::FillRect(hdcMem, &rc, reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)));
+ CSkin::ApplyAeroEffect(hdcMem, &rcText, CSkin::AERO_EFFECT_AREA_MENUBAR, 0);
+ ::FillRect(hdcMem, &m_rcRich, br);
+
+ hTheme = CMimAPI::m_pfnOpenThemeData(m_hwnd, L"BUTTON");
+ MARGINS m;
+ m.cxLeftWidth = LEFT_BORDER + m_leftWidth;
+ m.cxRightWidth = RIGHT_BORDER;
+ m.cyBottomHeight = BOTTOM_BORDER;
+ m.cyTopHeight = TOP_BORDER;
+ CMimAPI::m_pfnDwmExtendFrameIntoClientArea(m_hwnd, &m);
+ }
+ else {
+ ::FillRect(hdcMem, &rc, br);
+ ::DrawAlpha(hdcMem, &rcText, PluginConfig.m_ipBackgroundGradientHigh, 100, PluginConfig.m_ipBackgroundGradient,
+ 0, GRADIENT_TB + 1, 0, 2, 0);
+ }
+ ::DeleteObject(br);
+ rcText.left = 20;
+
+ LONG dy = 4;
+
+ if(m_hIcon) {
+ ::DrawIconEx(hdcMem, 2, dy, m_hIcon, 16, 16, 0, 0, DI_NORMAL);
+ dy = TOP_BORDER + 4;
+ }
+ if(m_panel->getDat()->hXStatusIcon) {
+ ::DrawIconEx(hdcMem, 2, dy, m_panel->getDat()->hXStatusIcon, 16, 16, 0, 0, DI_NORMAL);
+ dy += 18;
+ }
+ if(m_panel->getDat()->hClientIcon)
+ ::DrawIconEx(hdcMem, 2, dy, m_panel->getDat()->hClientIcon, 16, 16, 0, 0, DI_NORMAL);
+
+ CSkin::RenderText(hdcMem, hTheme, szTitle, &rcText, DT_SINGLELINE|DT_END_ELLIPSIS|DT_VCENTER, CSkin::m_glowSize, clr);
+ if(hTheme)
+ CMimAPI::m_pfnCloseThemeData(hTheme);
+ ::SelectObject(hdcMem, hOldFont);
+ ::BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY);
+ ::SelectObject(hdcMem, hbmOld);
+ ::DeleteObject(hbm);
+ ::DeleteDC(hdcMem);
+ }
+ return(1);
+ }
+
+ case WM_NOTIFY: {
+ switch (((NMHDR *) lParam)->code) {
+ case EN_LINK:
+ ::SetFocus(m_hRich);
+ switch (((ENLINK *) lParam)->msg) {
+ case WM_LBUTTONUP: {
+ ENLINK* e = reinterpret_cast<ENLINK *>(lParam);
+
+ const TCHAR* tszUrl = Utils::extractURLFromRichEdit(e, m_hRich);
+ if(tszUrl) {
+ char* szUrl = mir_t2a(tszUrl);
+
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ mir_free(szUrl);
+ mir_free(const_cast<TCHAR *>(tszUrl));
+ }
+ ::DestroyWindow(hwnd);
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case WM_COMMAND: {
+ if((HWND)lParam == m_hRich && HIWORD(wParam) == EN_SETFOCUS)
+ ::HideCaret(m_hRich);
+ break;
+ }
+
+ case WM_TIMER:
+ if(wParam == 1000) {
+ POINT pt;
+ RECT rc;
+
+ ::KillTimer(hwnd, 1000);
+ ::GetCursorPos(&pt);
+ ::GetWindowRect(hwnd, &rc);
+ if(!PtInRect(&rc, pt))
+ ::DestroyWindow(hwnd);
+ else
+ break;
+ if(::GetActiveWindow() != hwnd)
+ ::DestroyWindow(hwnd);
+ }
+ break;
+
+ case WM_DESTROY:
+ ::SetWindowLongPtr(m_hRich, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_OldMessageEditProc));
+ break;
+
+ case WM_NCDESTROY: {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ delete this;
+ }
+ }
+ return(::DefWindowProc(hwnd, msg, wParam, lParam));
+}
diff --git a/plugins/TabSRMM/src/mim.cpp b/plugins/TabSRMM/src/mim.cpp new file mode 100644 index 0000000000..4dc9128a92 --- /dev/null +++ b/plugins/TabSRMM/src/mim.cpp @@ -0,0 +1,935 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: mim.cpp 12842 2010-09-28 04:32:57Z borkra $
+ *
+ * wraps some parts of Miranda API
+ * Also, OS dependent stuff (visual styles api etc.)
+ *
+ */
+
+
+#include "commonheaders.h"
+extern PLUGININFOEX pluginInfo;
+
+PITA CMimAPI::m_pfnIsThemeActive = 0;
+POTD CMimAPI::m_pfnOpenThemeData = 0;
+PDTB CMimAPI::m_pfnDrawThemeBackground = 0;
+PCTD CMimAPI::m_pfnCloseThemeData = 0;
+PDTT CMimAPI::m_pfnDrawThemeText = 0;
+PDTTE CMimAPI::m_pfnDrawThemeTextEx = 0;
+PITBPT CMimAPI::m_pfnIsThemeBackgroundPartiallyTransparent = 0;
+PDTPB CMimAPI::m_pfnDrawThemeParentBackground = 0;
+PGTBCR CMimAPI::m_pfnGetThemeBackgroundContentRect = 0;
+ETDT CMimAPI::m_pfnEnableThemeDialogTexture = 0;
+PSLWA CMimAPI::m_pSetLayeredWindowAttributes = 0;
+PFWEX CMimAPI::m_MyFlashWindowEx = 0;
+PAB CMimAPI::m_MyAlphaBlend = 0;
+PGF CMimAPI::m_MyGradientFill = 0;
+DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = 0;
+DICE CMimAPI::m_pfnDwmIsCompositionEnabled = 0;
+MMFW CMimAPI::m_pfnMonitorFromWindow = 0;
+GMIA CMimAPI::m_pfnGetMonitorInfoA = 0;
+DRT CMimAPI::m_pfnDwmRegisterThumbnail = 0;
+BPI CMimAPI::m_pfnBufferedPaintInit = 0;
+BPU CMimAPI::m_pfnBufferedPaintUninit = 0;
+BBP CMimAPI::m_pfnBeginBufferedPaint = 0;
+EBP CMimAPI::m_pfnEndBufferedPaint = 0;
+BBW CMimAPI::m_pfnDwmBlurBehindWindow = 0;
+DGC CMimAPI::m_pfnDwmGetColorizationColor = 0;
+BPSA CMimAPI::m_pfnBufferedPaintSetAlpha = 0;
+GLIX CMimAPI::m_pfnGetLocaleInfoEx = 0;
+DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = 0;
+DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = 0;
+DWMUT CMimAPI::m_pfnDwmUpdateThumbnailProperties = 0;
+DURT CMimAPI::m_pfnDwmUnregisterThumbnail = 0;
+DSIT CMimAPI::m_pfnDwmSetIconicThumbnail = 0;
+DSILP CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = 0;
+bool CMimAPI::m_shutDown = 0;
+TCHAR CMimAPI::m_userDir[] = _T("\0");
+
+bool CMimAPI::m_haveBufferedPaint = false;
+DWORD CMimAPI::m_MimVersion = 0;
+
+void CMimAPI::timerMsg(const char *szMsg)
+{
+ mir_snprintf(m_timerMsg, 256, "%s: %d ticks = %f msec", szMsg, (int)(m_tStop - m_tStart), 1000 * ((double)(m_tStop - m_tStart) * m_dFreq));
+ _DebugTraceA(m_timerMsg);
+}
+/*
+ * read a setting for a contact
+ */
+
+DWORD CMimAPI::GetDword(const HANDLE hContact = 0, const char *szModule = 0, const char *szSetting = 0, DWORD uDefault = 0) const
+{
+ return((DWORD)DBGetContactSettingDword(hContact, szModule, szSetting, uDefault));
+}
+
+/*
+ * read a setting from our default module (Tab_SRMSG)
+ */
+
+DWORD CMimAPI::GetDword(const char *szSetting = 0, DWORD uDefault = 0) const
+{
+ return((DWORD)DBGetContactSettingDword(0, SRMSGMOD_T, szSetting, uDefault));
+}
+
+/*
+ * read a contact setting with our default module name (Tab_SRMSG)
+ */
+
+DWORD CMimAPI::GetDword(const HANDLE hContact = 0, const char *szSetting = 0, DWORD uDefault = 0) const
+{
+ return((DWORD)DBGetContactSettingDword(hContact, SRMSGMOD_T, szSetting, uDefault));
+}
+
+/*
+ * read a setting from module only
+ */
+
+DWORD CMimAPI::GetDword(const char *szModule, const char *szSetting, DWORD uDefault) const
+{
+ return((DWORD)DBGetContactSettingDword(0, szModule, szSetting, uDefault));
+}
+
+/*
+ * same for bytes now
+ */
+int CMimAPI::GetByte(const HANDLE hContact = 0, const char *szModule = 0, const char *szSetting = 0, int uDefault = 0) const
+{
+ return(DBGetContactSettingByte(hContact, szModule, szSetting, uDefault));
+}
+
+int CMimAPI::GetByte(const char *szSetting = 0, int uDefault = 0) const
+{
+ return(DBGetContactSettingByte(0, SRMSGMOD_T, szSetting, uDefault));
+}
+
+int CMimAPI::GetByte(const HANDLE hContact = 0, const char *szSetting = 0, int uDefault = 0) const
+{
+ return(DBGetContactSettingByte(hContact, SRMSGMOD_T, szSetting, uDefault));
+}
+
+int CMimAPI::GetByte(const char *szModule, const char *szSetting, int uDefault) const
+{
+ return(DBGetContactSettingByte(0, szModule, szSetting, uDefault));
+}
+
+INT_PTR CMimAPI::GetTString(const HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *dbv) const
+{
+ return(DBGetContactSettingTString(hContact, szModule, szSetting, dbv));
+}
+
+INT_PTR CMimAPI::GetString(const HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *dbv) const
+{
+ return(DBGetContactSettingString(hContact, szModule, szSetting, dbv));
+}
+
+/*
+ * writer functions
+ */
+
+INT_PTR CMimAPI::WriteDword(const HANDLE hContact = 0, const char *szModule = 0, const char *szSetting = 0, DWORD value = 0) const
+{
+ return(DBWriteContactSettingDword(hContact, szModule, szSetting, value));
+}
+
+/*
+ * write non-contact setting
+*/
+
+INT_PTR CMimAPI::WriteDword(const char *szModule = 0, const char *szSetting = 0, DWORD value = 0) const
+{
+ return(DBWriteContactSettingDword(0, szModule, szSetting, value));
+}
+
+INT_PTR CMimAPI::WriteByte(const HANDLE hContact = 0, const char *szModule = 0, const char *szSetting = 0, BYTE value = 0) const
+{
+ return(DBWriteContactSettingByte(hContact, szModule, szSetting, value));
+}
+
+INT_PTR CMimAPI::WriteByte(const char *szModule = 0, const char *szSetting = 0, BYTE value = 0) const
+{
+ return(DBWriteContactSettingByte(0, szModule, szSetting, value));
+}
+
+INT_PTR CMimAPI::WriteTString(const HANDLE hContact, const char *szModule = 0, const char *szSetting = 0, const TCHAR *str = 0) const
+{
+ return(DBWriteContactSettingTString(hContact, szModule, szSetting, str));
+}
+
+void CMimAPI::GetUTFI()
+{
+ mir_getUTFI(&m_utfi);
+}
+char *CMimAPI::utf8_decode(char* str, wchar_t** ucs2) const
+{
+ return(m_utfi.utf8_decode(str, ucs2));
+}
+char *CMimAPI::utf8_decodecp(char* str, int codepage, wchar_t** ucs2 ) const
+{
+ return(m_utfi.utf8_decodecp(str, codepage, ucs2));
+}
+char *CMimAPI::utf8_encode(const char* src) const
+{
+ return(m_utfi.utf8_encode(src));
+}
+
+char *CMimAPI::utf8_encodecp(const char* src, int codepage) const
+{
+ return(m_utfi.utf8_encodecp(src, codepage));
+}
+
+char *CMimAPI::utf8_encodeW(const wchar_t* src) const
+{
+ return(m_utfi.utf8_encodeW(src));
+}
+
+char *CMimAPI::utf8_encodeT(const TCHAR* src) const
+{
+ return(m_utfi.utf8_encodeW(src));
+}
+
+TCHAR *CMimAPI::utf8_decodeT(const char* src) const
+{
+ return(m_utfi.utf8_decodeW(src));
+}
+
+wchar_t *CMimAPI::utf8_decodeW(const char* str) const
+{
+ return(m_utfi.utf8_decodeW(str));
+}
+
+/**
+ * Case insensitive _tcsstr
+ *
+ * @param szString TCHAR *: String to be searched
+ * @param szSearchFor
+ * TCHAR *: String that should be found in szString
+ *
+ * @return TCHAR *: found position of szSearchFor in szString. 0 if szSearchFor was not found
+ */
+const TCHAR* CMimAPI::StriStr(const TCHAR *szString, const TCHAR *szSearchFor)
+{
+ assert(szString != 0 && szSearchFor != 0);
+
+ if(szString && *szString) {
+ if (0 == szSearchFor || 0 == *szSearchFor)
+ return(szString);
+
+ for(; *szString; ++szString) {
+ if(_totupper(*szString) == _totupper(*szSearchFor)) {
+ const TCHAR *h, *n;
+ for(h = szString, n = szSearchFor; *h && *n; ++h, ++n) {
+ if(_totupper(*h) != _totupper(*n))
+ break;
+ }
+ if(!*n)
+ return(szString);
+ }
+ }
+ return(0);
+ }
+ else
+ return(0);
+}
+
+int CMimAPI::pathIsAbsolute(const TCHAR *path) const
+{
+ if (!path || !(lstrlen(path) > 2))
+ return 0;
+ if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\'))
+ return 1;
+ return 0;
+}
+
+size_t CMimAPI::pathToRelative(const TCHAR *pSrc, TCHAR *pOut, const TCHAR *szBase) const
+{
+ const TCHAR *tszBase = szBase ? szBase : m_szProfilePath;
+
+ pOut[0] = 0;
+ if (!pSrc || !lstrlen(pSrc) || lstrlen(pSrc) > MAX_PATH)
+ return 0;
+ if (!pathIsAbsolute(pSrc)) {
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc);
+ return lstrlen(pOut);
+ } else {
+ TCHAR szTmp[MAX_PATH];
+
+ mir_sntprintf(szTmp, SIZEOF(szTmp), _T("%s"), pSrc);
+ if (StriStr(szTmp, tszBase)) {
+ if(tszBase[lstrlen(tszBase) - 1] == '\\')
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc + lstrlen(tszBase));
+ else {
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc + lstrlen(tszBase) + 1 );
+ //pOut[0]='.';
+ }
+ return(lstrlen(pOut));
+ } else {
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc);
+ return(lstrlen(pOut));
+ }
+ }
+}
+
+/**
+ * Translate a relativ path to an absolute, using the current profile
+ * data directory.
+ *
+ * @param pSrc TCHAR *: input path + filename (relative)
+ * @param pOut TCHAR *: the result
+ * @param szBase TCHAR *: (OPTIONAL) base path for the translation. Can be 0 in which case
+ * the function will use m_szProfilePath (usually \tabSRMM below %miranda_userdata%
+ *
+ * @return
+ */
+size_t CMimAPI::pathToAbsolute(const TCHAR *pSrc, TCHAR *pOut, const TCHAR *szBase) const
+{
+ const TCHAR *tszBase = szBase ? szBase : m_szProfilePath;
+
+ pOut[0] = 0;
+ if (!pSrc || !lstrlen(pSrc) || lstrlen(pSrc) > MAX_PATH)
+ return 0;
+ if (pathIsAbsolute(pSrc) && pSrc[0]!='.')
+ mir_sntprintf(pOut, MAX_PATH, _T("%s"), pSrc);
+ else if (pSrc[0]=='.')
+ mir_sntprintf(pOut, MAX_PATH, _T("%s\\%s"), tszBase, pSrc + 1);
+ else
+ mir_sntprintf(pOut, MAX_PATH, _T("%s\\%s"), tszBase, pSrc);
+
+ return lstrlen(pOut);
+}
+
+/*
+ * window list functions
+ */
+
+void CMimAPI::BroadcastMessage(UINT msg = 0, WPARAM wParam = 0, LPARAM lParam = 0)
+{
+ WindowList_Broadcast(m_hMessageWindowList, msg, wParam, lParam);
+}
+
+void CMimAPI::BroadcastMessageAsync(UINT msg = 0, WPARAM wParam = 0, LPARAM lParam = 0)
+{
+ WindowList_BroadcastAsync(m_hMessageWindowList, msg, wParam, lParam);
+}
+
+HWND CMimAPI::FindWindow(HANDLE h = 0) const
+{
+ return(WindowList_Find(m_hMessageWindowList, h));
+}
+
+INT_PTR CMimAPI::AddWindow(HWND hWnd = 0, HANDLE h = 0)
+{
+ return(WindowList_Add(m_hMessageWindowList, hWnd, h));
+}
+
+INT_PTR CMimAPI::RemoveWindow(HWND hWnd = 0)
+{
+ return(WindowList_Remove(m_hMessageWindowList, hWnd));
+}
+
+int CMimAPI::FoldersPathChanged(WPARAM wParam, LPARAM lParam)
+{
+ return(M->foldersPathChanged());
+}
+
+void CMimAPI::configureCustomFolders()
+{
+ m_haveFolders = false;
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) {
+ m_hDataPath = (HANDLE)FoldersRegisterCustomPathT("TabSRMM", "Data path", const_cast<TCHAR *>(getDataPath()));
+ m_hSkinsPath = (HANDLE)FoldersRegisterCustomPathT("TabSRMM", "Skins root", const_cast<TCHAR *>(getSkinPath()));
+ m_hAvatarsPath = (HANDLE)FoldersRegisterCustomPathT("TabSRMM", "Saved Avatars", const_cast<TCHAR *>(getSavedAvatarPath()));
+ m_hChatLogsPath = (HANDLE)FoldersRegisterCustomPathT("TabSRMM", "Group chat logs root", const_cast<TCHAR *>(getChatLogPath()));
+ CGlobals::m_event_FoldersChanged = HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged);
+ m_haveFolders = true;
+ }
+ else
+ m_hDataPath = m_hSkinsPath = m_hAvatarsPath = m_hChatLogsPath = 0;
+
+ foldersPathChanged();
+}
+
+INT_PTR CMimAPI::foldersPathChanged()
+{
+ TCHAR szTemp[MAX_PATH + 2] = {'\0'};
+
+ if(m_hDataPath) {
+ FoldersGetCustomPathT(m_hDataPath, szTemp, MAX_PATH, const_cast<TCHAR *>(getDataPath()));
+ mir_sntprintf(m_szProfilePath, MAX_PATH, _T("%s"), szTemp);
+
+ FoldersGetCustomPathT(m_hSkinsPath, szTemp, MAX_PATH, const_cast<TCHAR *>(getSkinPath()));
+ mir_sntprintf(m_szSkinsPath, MAX_PATH - 1, _T("%s"), szTemp);
+
+ /*
+ * make sure skins root path always ends with a '\' - this is assumed by the skin
+ * selection code.
+ */
+
+ Utils::ensureTralingBackslash(m_szSkinsPath);
+
+ FoldersGetCustomPathT(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<TCHAR *>(getSavedAvatarPath()));
+ mir_sntprintf(m_szSavedAvatarsPath, MAX_PATH, _T("%s"), szTemp);
+
+ FoldersGetCustomPathT(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<TCHAR *>(getChatLogPath()));
+ mir_sntprintf(m_szChatLogsPath, MAX_PATH, _T("%s"), szTemp);
+
+ Utils::ensureTralingBackslash(m_szChatLogsPath);
+ }
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)m_szProfilePath);
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)m_szSkinsPath);
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)m_szSavedAvatarsPath);
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)m_szChatLogsPath);
+
+#if defined(_FOLDER_LOCKING)
+ mir_sntprintf(szTemp, MAX_PATH, L"%sfolder.lck", m_szChatLogsPath);
+
+ if(m_hChatLogLock != INVALID_HANDLE_VALUE)
+ CloseHandle(m_hChatLogLock);
+
+ m_hChatLogLock = CreateFile(szTemp, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, 0);
+#endif
+
+ Skin->extractSkinsAndLogo(true);
+ Skin->setupAeroSkins();
+ return 0;
+}
+
+const TCHAR* CMimAPI::getUserDir()
+{
+ if(m_userDir[0] == 0) {
+ wchar_t* userdata = ::Utils_ReplaceVarsT(L"%miranda_userdata%");
+ mir_sntprintf(m_userDir, MAX_PATH, userdata);
+ Utils::ensureTralingBackslash(m_userDir);
+ mir_free(userdata);
+ }
+ return(m_userDir);
+}
+
+void CMimAPI::InitPaths()
+{
+ m_szProfilePath[0] = 0;
+ m_szSkinsPath[0] = 0;
+ m_szSavedAvatarsPath[0] = 0;
+
+ const TCHAR *szUserdataDir = getUserDir();
+
+ mir_sntprintf(m_szProfilePath, MAX_PATH, _T("%stabSRMM"), szUserdataDir);
+ mir_sntprintf(m_szChatLogsPath, MAX_PATH, _T("%sLogs\\"), szUserdataDir);
+
+ mir_sntprintf(m_szSkinsPath, MAX_PATH, _T("%s\\skins\\"), m_szProfilePath);
+ mir_sntprintf(m_szSavedAvatarsPath, MAX_PATH, _T("%s\\Saved Contact Pictures"), m_szProfilePath);
+}
+
+bool CMimAPI::getAeroState()
+{
+ BOOL result = FALSE;
+ m_isAero = m_DwmActive = false;
+ if(IsWinVerVistaPlus()) {
+ m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result) ? true : false;
+ m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive;
+
+ }
+ m_isVsThemed = (m_VsAPI && m_pfnIsThemeActive && m_pfnIsThemeActive());
+ return(m_isAero);
+}
+
+/**
+ * Initialize various Win32 API functions which are not common to all versions of Windows.
+ * We have to work with functions pointers here.
+ */
+
+void CMimAPI::InitAPI()
+{
+ m_hUxTheme = 0;
+ m_VsAPI = false;
+
+ HMODULE hDLL = GetModuleHandleA("user32");
+ m_pSetLayeredWindowAttributes = (PSLWA) GetProcAddress(hDLL, "SetLayeredWindowAttributes");
+ m_MyFlashWindowEx = (PFWEX) GetProcAddress(hDLL, "FlashWindowEx");
+
+ m_MyAlphaBlend = (PAB) GetProcAddress(GetModuleHandleA("gdi32"), "GdiAlphaBlend");
+ if (m_MyAlphaBlend == 0)
+ m_MyAlphaBlend = (PAB) GetProcAddress(LoadLibraryA("msimg32.dll"), "AlphaBlend");
+
+ m_MyGradientFill = (PGF) GetProcAddress(GetModuleHandleA("gdi32"), "GdiGradientFill");
+ if (m_MyGradientFill == 0)
+ m_MyGradientFill = (PGF) GetProcAddress(GetModuleHandleA("msimg32"), "GradientFill");
+
+ m_pfnMonitorFromWindow = (MMFW)GetProcAddress(GetModuleHandleA("USER32"), "MonitorFromWindow");
+ m_pfnGetMonitorInfoA = (GMIA)GetProcAddress(GetModuleHandleA("USER32"), "GetMonitorInfoA");
+
+ if (IsWinVerXPPlus()) {
+ if ((m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll")) != 0) {
+ m_pfnIsThemeActive = (PITA)GetProcAddress(m_hUxTheme, "IsThemeActive");
+ m_pfnOpenThemeData = (POTD)GetProcAddress(m_hUxTheme, "OpenThemeData");
+ m_pfnDrawThemeBackground = (PDTB)GetProcAddress(m_hUxTheme, "DrawThemeBackground");
+ m_pfnCloseThemeData = (PCTD)GetProcAddress(m_hUxTheme, "CloseThemeData");
+ m_pfnDrawThemeText = (PDTT)GetProcAddress(m_hUxTheme, "DrawThemeText");
+ m_pfnIsThemeBackgroundPartiallyTransparent = (PITBPT)GetProcAddress(m_hUxTheme, "IsThemeBackgroundPartiallyTransparent");
+ m_pfnDrawThemeParentBackground = (PDTPB)GetProcAddress(m_hUxTheme, "DrawThemeParentBackground");
+ m_pfnGetThemeBackgroundContentRect = (PGTBCR)GetProcAddress(m_hUxTheme, "GetThemeBackgroundContentRect");
+ m_pfnEnableThemeDialogTexture = (ETDT)GetProcAddress(m_hUxTheme, "EnableThemeDialogTexture");
+
+ if (m_pfnIsThemeActive != 0 && m_pfnOpenThemeData != 0 && m_pfnDrawThemeBackground != 0 && m_pfnCloseThemeData != 0
+ && m_pfnDrawThemeText != 0 && m_pfnIsThemeBackgroundPartiallyTransparent != 0 && m_pfnDrawThemeParentBackground != 0
+ && m_pfnGetThemeBackgroundContentRect != 0) {
+ m_VsAPI = true;
+ }
+ }
+ }
+
+ /*
+ * vista+ DWM API
+ */
+
+ m_hDwmApi = 0;
+ if (IsWinVerVistaPlus()) {
+ m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll");
+ if (m_hDwmApi) {
+ m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi,"DwmExtendFrameIntoClientArea");
+ m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi,"DwmIsCompositionEnabled");
+ m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail");
+ m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow");
+ m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor");
+ m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps");
+ m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute");
+ m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties");
+ m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail");
+ m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail");
+ m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap");
+ }
+ /*
+ * additional uxtheme APIs (Vista+)
+ */
+ if(m_hUxTheme) {
+ m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx");
+ m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint");
+ m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint");
+ m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit");
+ m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit");
+ m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha");
+ m_haveBufferedPaint = (m_pfnBeginBufferedPaint != 0 && m_pfnEndBufferedPaint != 0) ? true : false;
+ if(m_haveBufferedPaint)
+ m_pfnBufferedPaintInit();
+ }
+ m_pfnGetLocaleInfoEx = (GLIX)GetProcAddress(GetModuleHandleA("kernel32"), "GetLocaleInfoEx");
+ }
+ else
+ m_haveBufferedPaint = false;
+}
+
+/**
+ * hook subscriber function for incoming message typing events
+ */
+
+int CMimAPI::TypingMessage(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd = 0;
+ int issplit = 1, foundWin = 0, preTyping = 0;
+ struct TContainerData *pContainer = NULL;
+ BOOL fShowOnClist = TRUE;
+
+ if(wParam) {
+
+ if ((hwnd = M->FindWindow((HANDLE) wParam)) && M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING))
+ preTyping = SendMessage(hwnd, DM_TYPING, 0, lParam);
+
+ if (hwnd && IsWindowVisible(hwnd))
+ foundWin = MessageWindowOpened(0, (LPARAM)hwnd);
+ else
+ foundWin = 0;
+
+
+ if(hwnd) {
+ SendMessage(hwnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if(pContainer == NULL)
+ return 0; // should never happen
+ }
+
+ if(M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) {
+ if(!hwnd && !M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGNOWINOPEN, 1))
+ fShowOnClist = FALSE;
+ if(hwnd && !M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINOPEN, 1))
+ fShowOnClist = FALSE;
+ }
+ else
+ fShowOnClist = FALSE;
+
+ if((!foundWin || !(pContainer->dwFlags&CNT_NOSOUND)) && preTyping != (lParam != 0)){
+ if (lParam)
+ SkinPlaySound("TNStart");
+ else
+ SkinPlaySound("TNStop");
+ }
+
+ if(M->GetByte(SRMSGMOD, "ShowTypingPopup", 0)) {
+ BOOL fShow = FALSE;
+ int iMode = M->GetByte("MTN_PopupMode", 0);
+
+ switch(iMode) {
+ case 0:
+ fShow = TRUE;
+ break;
+ case 1:
+ if(!foundWin || !(pContainer && pContainer->hwndActive == hwnd && GetForegroundWindow() == pContainer->hwnd))
+ fShow = TRUE;
+ break;
+ case 2:
+ if(hwnd == 0)
+ fShow = TRUE;
+ else {
+ if(PluginConfig.m_HideOnClose) {
+ struct TContainerData *pContainer = 0;
+ SendMessage(hwnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if(pContainer && pContainer->fHidden)
+ fShow = TRUE;
+ }
+ }
+ break;
+ }
+ if(fShow)
+ TN_TypingMessage(wParam, lParam);
+ }
+
+ if ((int) lParam) {
+ TCHAR szTip[256];
+
+ _sntprintf(szTip, SIZEOF(szTip), CTranslator::get(CTranslator::GEN_MTN_STARTWITHNICK), (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, wParam, GCDNF_TCHAR));
+ if (fShowOnClist && ServiceExists(MS_CLIST_SYSTRAY_NOTIFY) && M->GetByte(SRMSGMOD, "ShowTypingBalloon", 0)) {
+ MIRANDASYSTRAYNOTIFY tn;
+ tn.szProto = NULL;
+ tn.cbSize = sizeof(tn);
+ tn.tszInfoTitle = const_cast<TCHAR *>(CTranslator::get(CTranslator::GEN_MTN_TTITLE));
+ tn.tszInfo = szTip;
+ tn.dwInfoFlags = NIIF_INFO | NIIF_INTERN_UNICODE;
+ tn.uTimeout = 1000 * 4;
+ CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM) & tn);
+ }
+ if(fShowOnClist) {
+ CLISTEVENT cle;
+
+ ZeroMemory(&cle, sizeof(cle));
+ cle.cbSize = sizeof(cle);
+ cle.hContact = (HANDLE) wParam;
+ cle.hDbEvent = (HANDLE) 1;
+ cle.flags = CLEF_ONLYAFEW | CLEF_TCHAR;
+ cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
+ cle.pszService = "SRMsg/TypingMessage";
+ cle.ptszTooltip = szTip;
+ CallServiceSync(MS_CLIST_REMOVEEVENT, wParam, (LPARAM) 1);
+ CallServiceSync(MS_CLIST_ADDEVENT, wParam, (LPARAM) & cle);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * this is the global ack dispatcher. It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events
+ * for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches
+ * it to the owners window
+ *
+ * ACKTYPE_AVATAR no longer handled here, because we have avs services now.
+ */
+
+int CMimAPI::ProtoAck(WPARAM wParam, LPARAM lParam)
+{
+ ACKDATA *pAck = (ACKDATA *) lParam;
+ HWND hwndDlg = 0;
+ int i = 0, j, iFound = SendQueue::NR_SENDJOBS;
+
+ if (lParam == 0)
+ return 0;
+
+ SendJob *jobs = sendQueue->getJobByIndex(0);
+
+ if (pAck->type == ACKTYPE_MESSAGE) {
+ for (j = 0; j < SendQueue::NR_SENDJOBS; j++) {
+ if (pAck->hProcess == jobs[j].hSendId && pAck->hContact == jobs[j].hOwner) {
+ TWindowData *dat = jobs[j].hwndOwner ? (TWindowData *)GetWindowLongPtr(jobs[j].hwndOwner, GWLP_USERDATA) : NULL;
+ if (dat) {
+ if (dat->hContact == jobs[j].hOwner) {
+ iFound = j;
+ break;
+ }
+ } else { // ack message w/o an open window...
+ sendQueue->ackMessage(NULL, (WPARAM)MAKELONG(j, i), lParam);
+ return 0;
+ }
+ }
+ if (iFound == SendQueue::NR_SENDJOBS) // no mathing entry found in this queue entry.. continue
+ continue;
+ else
+ break;
+ }
+ if (iFound == SendQueue::NR_SENDJOBS) { // no matching send info found in the queue
+ sendLater->processAck(pAck); //
+ return 0; // try to find the process handle in the list of open send later jobs
+ } else { // the job was found
+ SendMessage(jobs[iFound].hwndOwner, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int CMimAPI::PrebuildContactMenu(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ if ( hContact ) {
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof( CLISTMENUITEM );
+ clmi.flags = CMIM_FLAGS | CMIF_DEFAULT | CMIF_HIDDEN;
+
+ if ( szProto ) {
+ // leave this menu item hidden for chats
+ if ( !M->GetByte(hContact, szProto, "ChatRoom", 0 ))
+ if ( CallProtoService( szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND )
+ clmi.flags &= ~CMIF_HIDDEN;
+ }
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )PluginConfig.m_hMenuItem, ( LPARAM )&clmi );
+ }
+ return 0;
+}
+
+/**
+ * this handler is called first in the message window chain - it will handle events for which a message window
+ * is already open. if not, it will do nothing and the 2nd handler (MessageEventAdded) will perform all
+ * the needed actions.
+ *
+ * this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will
+ * improve the overall responsiveness when receiving messages.
+ */
+
+int CMimAPI::DispatchNewEvent(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam) {
+ HWND h = M->FindWindow((HANDLE)wParam);
+ if (h)
+ PostMessage(h, HM_DBEVENTADDED, wParam, lParam); // was SENDMESSAGE !!! XXX
+ }
+ return 0;
+}
+
+/**
+ * Message event added is called when a new message is added to the database
+ * if no session is open for the contact, this function will determine if and how a new message
+ * session (tab) must be created.
+ *
+ * if a session is already created, it just does nothing and DispatchNewEvent() will take care.
+ */
+
+int CMimAPI::MessageEventAdded(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd;
+ CLISTEVENT cle;
+ DBEVENTINFO dbei;
+ BYTE bAutoPopup = FALSE, bAutoCreate = FALSE, bAutoContainer = FALSE, bAllowAutoCreate = 0;
+ struct TContainerData *pContainer = 0;
+ TCHAR szName[CONTAINER_NAMELEN + 1];
+ DWORD dwStatusMask = 0;
+ struct TWindowData *mwdat=NULL;
+
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = 0;
+ CallService(MS_DB_EVENT_GET, lParam, (LPARAM) & dbei);
+
+ hwnd = M->FindWindow((HANDLE) wParam);
+
+ if (dbei.flags & DBEF_SENT || !(dbei.eventType == EVENTTYPE_MESSAGE || dbei.eventType == EVENTTYPE_FILE) || dbei.flags & DBEF_READ)
+ return 0;
+
+ CallServiceSync(MS_CLIST_REMOVEEVENT, wParam, (LPARAM) 1);
+ //MaD: hide on close mod, simulating standard behavior for hidden container
+ if (hwnd) {
+ struct TContainerData *pTargetContainer = 0;
+ WINDOWPLACEMENT wp={0};
+ wp.length = sizeof(wp);
+ SendMessage(hwnd, DM_QUERYCONTAINER, 0, (LPARAM)&pTargetContainer);
+
+ if(pTargetContainer && PluginConfig.m_HideOnClose && !IsWindowVisible(pTargetContainer->hwnd)) {
+ GetWindowPlacement(pTargetContainer->hwnd, &wp);
+ GetContainerNameForContact((HANDLE) wParam, szName, CONTAINER_NAMELEN);
+
+ bAutoPopup = M->GetByte(SRMSGSET_AUTOPOPUP, SRMSGDEFSET_AUTOPOPUP);
+ bAutoCreate = M->GetByte("autotabs", 1);
+ bAutoContainer = M->GetByte("autocontainer", 1);
+ dwStatusMask = M->GetDword("autopopupmask", -1);
+
+ bAllowAutoCreate = FALSE;
+
+ if (bAutoPopup || bAutoCreate) {
+ BOOL bActivate = TRUE, bPopup = TRUE;
+ if(bAutoPopup) {
+ if(wp.showCmd == SW_SHOWMAXIMIZED)
+ ShowWindow(pTargetContainer->hwnd, SW_SHOWMAXIMIZED);
+ else
+ ShowWindow(pTargetContainer->hwnd, SW_SHOWNOACTIVATE);
+ return 0;
+ }
+ else {
+ bActivate = FALSE;
+ bPopup = (BOOL) M->GetByte("cpopup", 0);
+ pContainer = FindContainerByName(szName);
+ if (pContainer != NULL) {
+ if(bAutoContainer) {
+ ShowWindow(pTargetContainer->hwnd, SW_SHOWMINNOACTIVE);
+ return 0;
+ }
+ else goto nowindowcreate;
+ }
+ else {
+ if(bAutoContainer) {
+ ShowWindow(pTargetContainer->hwnd, SW_SHOWMINNOACTIVE);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ else
+ return 0;
+ } else {
+ if(dbei.eventType == EVENTTYPE_FILE) {
+ tabSRMM_ShowPopup(wParam, lParam, dbei.eventType, 0, 0, 0, dbei.szModule, 0);
+ return(0);
+ }
+ }
+
+ /*
+ * if no window is open, we are not interested in anything else but unread message events
+ */
+
+ /* new message */
+ if (!nen_options.iNoSounds)
+ SkinPlaySound("AlertMsg");
+
+ if (nen_options.iNoAutoPopup)
+ goto nowindowcreate;
+
+ GetContainerNameForContact((HANDLE) wParam, szName, CONTAINER_NAMELEN);
+
+ bAutoPopup = M->GetByte(SRMSGSET_AUTOPOPUP, SRMSGDEFSET_AUTOPOPUP);
+ bAutoCreate = M->GetByte("autotabs", 1);
+ bAutoContainer = M->GetByte("autocontainer", 1);
+ dwStatusMask = M->GetDword("autopopupmask", -1);
+
+ bAllowAutoCreate = FALSE;
+
+ if (dwStatusMask == -1)
+ bAllowAutoCreate = TRUE;
+ else {
+ char *szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)wParam, 0);
+ DWORD dwStatus = 0;
+
+ if (PluginConfig.g_MetaContactsAvail && szProto && !strcmp(szProto, (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0))) {
+ HANDLE hSubconttact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, wParam, 0);
+
+ szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hSubconttact, 0);
+ }
+ if (szProto) {
+ dwStatus = (DWORD)CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ if (dwStatus == 0 || dwStatus <= ID_STATUS_OFFLINE || ((1 << (dwStatus - ID_STATUS_ONLINE)) & dwStatusMask)) // should never happen, but...
+ bAllowAutoCreate = TRUE;
+ }
+ }
+ if (bAllowAutoCreate && (bAutoPopup || bAutoCreate)) {
+ BOOL bActivate = TRUE, bPopup = TRUE;
+ if (bAutoPopup) {
+ bActivate = bPopup = TRUE;
+ if ((pContainer = FindContainerByName(szName)) == NULL)
+ pContainer = CreateContainer(szName, FALSE, (HANDLE)wParam);
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 0, NULL, bActivate, bPopup, FALSE, 0);
+ return 0;
+ } else {
+ bActivate = FALSE;
+ bPopup = (BOOL) M->GetByte("cpopup", 0);
+ pContainer = FindContainerByName(szName);
+ if (pContainer != NULL) {
+ //if ((IsIconic(pContainer->hwnd)) && PluginConfig.haveAutoSwitch())
+ // pContainer->dwFlags |= CNT_DEFERREDTABSELECT;
+ if (M->GetByte("limittabs", 0) && !wcsncmp(pContainer->szName, L"default", 6)) {
+ if ((pContainer = FindMatchingContainer(L"default", (HANDLE)wParam)) != NULL) {
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 0, NULL, bActivate, bPopup, TRUE, (HANDLE)lParam);
+ return 0;
+ } else if (bAutoContainer) {
+ pContainer = CreateContainer(szName, CNT_CREATEFLAG_MINIMIZED, (HANDLE)wParam); // 2 means create minimized, don't popup...
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 0, NULL, bActivate, bPopup, TRUE, (HANDLE)lParam);
+ SendMessageW(pContainer->hwnd, WM_SIZE, 0, 0);
+ return 0;
+ }
+ } else {
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 0, NULL, bActivate, bPopup, TRUE, (HANDLE)lParam);
+ return 0;
+ }
+
+ } else {
+ if (bAutoContainer) {
+ pContainer = CreateContainer(szName, CNT_CREATEFLAG_MINIMIZED, (HANDLE)wParam); // 2 means create minimized, don't popup...
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 0, NULL, bActivate, bPopup, TRUE, (HANDLE)lParam);
+ SendMessageW(pContainer->hwnd, WM_SIZE, 0, 0);
+ return 0;
+ }
+ }
+ }
+ }
+
+ /*
+ * for tray support, we add the event to the tray menu. otherwise we send it back to
+ * the contact list for flashing
+ */
+nowindowcreate:
+ if (!(dbei.flags & DBEF_READ)) {
+ UpdateTrayMenu(0, 0, dbei.szModule, NULL, (HANDLE)wParam, 1);
+ if (!nen_options.bTraySupport) {
+ TCHAR toolTip[256], *contactName;
+ ZeroMemory(&cle, sizeof(cle));
+ cle.cbSize = sizeof(cle);
+ cle.hContact = (HANDLE) wParam;
+ cle.hDbEvent = (HANDLE) lParam;
+ cle.flags = CLEF_TCHAR;
+ cle.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = "SRMsg/ReadMessage";
+ contactName = (TCHAR*) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, wParam, GCDNF_TCHAR);
+ mir_sntprintf(toolTip, SIZEOF(toolTip), CTranslator::get(CTranslator::GEN_MSG_TTITLE), contactName);
+ cle.ptszTooltip = toolTip;
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM) & cle);
+ }
+ tabSRMM_ShowPopup(wParam, lParam, dbei.eventType, 0, 0, 0, dbei.szModule, 0);
+ }
+ return 0;
+}
+
+CMimAPI *M = 0;
+FI_INTERFACE *FIF = 0;
diff --git a/plugins/TabSRMM/src/msgdialog.cpp b/plugins/TabSRMM/src/msgdialog.cpp new file mode 100644 index 0000000000..33b8234ae7 --- /dev/null +++ b/plugins/TabSRMM/src/msgdialog.cpp @@ -0,0 +1,3895 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: msgdialog.cpp 13447 2011-03-14 19:55:07Z george.hazan $
+ *
+ * This implements the message dialog window.
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+#define MS_HTTPSERVER_ADDFILENAME "HTTPServer/AddFileName"
+
+extern TTemplateSet RTL_Active, LTR_Active;
+const TCHAR* pszIDCSAVE_close = 0, *pszIDCSAVE_save = 0;
+
+static WNDPROC OldMessageEditProc=0, OldAvatarWndProc=0, OldMessageLogProc=0, oldAvatarParentWndProc=0;
+ WNDPROC OldIEViewProc = 0;
+
+WNDPROC OldSplitterProc = 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 errorControls[] = { IDC_STATICERRORICON, IDC_STATICTEXT, IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER};
+
+static struct _tooltips {
+ int id;
+ int translate_id;
+} tooltips[] = {
+ IDC_ADD, CTranslator::GEN_TOOLTIP_ADDCONTACT,
+ IDC_CANCELADD, CTranslator::GEN_TOOLTIP_DONTADD,
+ IDC_TOGGLESIDEBAR, CTranslator::GEN_TOOLTIP_EXPANDSIDEBAR,
+ -1, NULL
+};
+
+static struct _buttonicons {
+ int id;
+ HICON *pIcon;
+} buttonicons[] = {
+ IDC_ADD, &PluginConfig.g_buttonBarIcons[ICON_BUTTON_ADD],
+ IDC_CANCELADD, &PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL],
+ -1, NULL
+};
+
+static void _clrMsgFilter(LPARAM lParam)
+{
+ MSGFILTER *m = reinterpret_cast<MSGFILTER *>(lParam);
+
+ m->msg = 0;
+ m->lParam = 0;
+ m->wParam = 0;
+}
+
+static BOOL IsStringValidLinkA(char* pszText)
+{
+ char *p = pszText;
+
+ if (pszText == NULL)
+ return FALSE;
+ if (lstrlenA(pszText) < 5)
+ return FALSE;
+
+ while (*p) {
+ if (*p == '"')
+ return FALSE;
+ p++;
+ }
+ if (tolower(pszText[0]) == 'w' && tolower(pszText[1]) == 'w' && tolower(pszText[2]) == 'w' && pszText[3] == '.' && isalnum(pszText[4]))
+ return TRUE;
+
+ return(strstr(pszText, "://") == NULL ? FALSE : TRUE);
+}
+
+BOOL TSAPI IsUtfSendAvailable(HANDLE hContact)
+{
+ char* szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ 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)
+{
+ HMENU hMenu, hSubMenu;
+ CHARRANGE sel, all = { 0, -1};
+ int iSelection;
+ unsigned oldCodepage = dat->codePage;
+ int iPrivateBG = M->GetByte(dat->hContact, "private_bg", 0);
+ MessageWindowPopupData mwpd;
+ HWND hwndDlg = dat->hwnd;
+
+ 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));
+ }
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hSubMenu, 0);
+ SendMessage(hwndFrom, EM_EXGETSEL, 0, (LPARAM) & sel);
+ if (sel.cpMin == sel.cpMax) {
+ EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED);
+ //MAD
+ EnableMenuItem(hSubMenu, IDM_QUOTE, MF_BYCOMMAND | MF_GRAYED);
+ //MAD_
+ if (idFrom == IDC_MESSAGE)
+ EnableMenuItem(hSubMenu, IDM_CUT, MF_BYCOMMAND | MF_GRAYED);
+ }
+
+ if (idFrom == IDC_LOG) {
+ int i;
+ //MAD: quote mod
+ InsertMenuA(hSubMenu, 6/*5*/, MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+ InsertMenu(hSubMenu, 7/*6*/, MF_BYPOSITION | MF_POPUP, (UINT_PTR) PluginConfig.g_hMenuEncoding, CTranslator::get(CTranslator::GEN_MSG_ENCODING));
+ for (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));
+ }
+
+ 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);
+ }
+
+ 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;
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "ANSIcodepage", dat->codePage);
+ } else if (iSelection == 500 && idFrom == IDC_LOG) {
+ dat->codePage = CP_ACP;
+ DBDeleteContactSetting(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_TEXTT : 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;
+ //MAD
+ case IDM_QUOTE:
+ SendMessage(hwndDlg,WM_COMMAND, IDC_QUOTE, 0);
+ break;
+ //MAD_
+ 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;
+ M->WriteByte(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;
+ POINT pt;
+ IEVIEWWINDOW ieWindow;
+ int iMode = dat->hwndIEView ? 1 : 2;
+ HWND hwndDlg = dat->hwnd;
+
+ ZeroMemory(&ieWindow, sizeof(ieWindow));
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_LOG), &rcRichEdit);
+ pt.x = rcRichEdit.left;
+ pt.y = rcRichEdit.top;
+ ScreenToClient(hwndDlg, &pt);
+ ieWindow.cbSize = sizeof(IEVIEWWINDOW);
+ ieWindow.iType = IEW_SETPOS;
+ ieWindow.parent = hwndDlg;
+ ieWindow.hwnd = iMode == 1 ? 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(iMode == 1 ? MS_IEVIEW_WINDOW : MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+}
+
+LRESULT CALLBACK IEViewSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TWindowData *mwdat = (struct TWindowData *)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_NCCALCSIZE:
+ return(CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, OldIEViewProc));
+ case WM_NCPAINT:
+ return(CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, OldIEViewProc));
+ }
+ return CallWindowProc(OldIEViewProc, hwnd, msg, wParam, lParam);
+}
+
+/*
+ * sublassing procedure for the h++ based message log viewer
+ */
+
+LRESULT CALLBACK HPPKFSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+
+ struct TWindowData *mwdat = (struct 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, mwdat->oldIEViewProc));
+ case WM_NCPAINT:
+ return(CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, mwdat->oldIEViewProc));
+
+ 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 CallWindowProc(mwdat->oldIEViewProc, hwnd, 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 && CMimAPI::m_pSetLayeredWindowAttributes != NULL) {
+ DWORD trans = LOWORD(dat->pContainer->settings->dwTransparency);
+ CMimAPI::m_pSetLayeredWindowAttributes(dat->pContainer->hwnd, 0, (BYTE)trans, (dat->pContainer->dwFlags & CNT_TRANSPARENCY ? LWA_ALPHA : 0));
+ }
+ }
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ if(dat->fIsAutosizingInput && dat->iInputAreaHeight == -1) {
+ dat->iInputAreaHeight = 0;
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_REQUESTRESIZE, 0, 0);
+ }
+#endif
+ 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, (WPARAM)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, (WPARAM)dat->hContact, 0);
+
+ UpdateStatusBar(dat);
+ dat->dwLastActivity = GetTickCount();
+ dat->pContainer->dwLastActivity = dat->dwLastActivity;
+
+ dat->pContainer->MenuBar->configureMenu();
+ UpdateTrayMenuState(dat, FALSE);
+
+ if (PluginConfig.m_MathModAvail) {
+ CallService(MTH_Set_ToolboxEditHwnd, 0, (LPARAM)GetDlgItem(hwndDlg, IDC_MESSAGE));
+ MTH_updateMathWindow(dat);
+ }
+ 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) && dat->oldIEViewProc == 0) {
+ WNDPROC wndProc = (WNDPROC)SetWindowLongPtr(dat->hwndIEView, GWLP_WNDPROC, (LONG_PTR)IEViewSubclassProc);
+ if (OldIEViewProc == 0)
+ OldIEViewProc = wndProc;
+ dat->oldIEViewProc = wndProc;
+ 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)
+{
+ int i;
+ for (i = 0; i < cControls; i++)
+ Utils::showDlgControl(hwndDlg, controls[i], state);
+}
+
+void TSAPI SetDialogToType(HWND hwndDlg)
+{
+ struct TWindowData *dat;
+ int showToolbar = 0;
+
+ dat = (struct TWindowData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1;
+
+ if (dat->hContact) {
+ if (M->GetByte(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), CTranslator::get(CTranslator::GEN_MSG_CONTACT_NOT_ON_LIST));
+ } 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, CTranslator::get(CTranslator::GEN_MSG_FAILEDSEND));
+
+ DM_RecalcPictureSize(dat);
+ GetAvatarVisibility(hwndDlg, dat);
+
+ Utils::showDlgControl(hwndDlg, IDC_CONTACTPIC, dat->showPic ? SW_SHOW : SW_HIDE);
+ Utils::showDlgControl(hwndDlg, IDC_SPLITTER, dat->fIsAutosizingInput ? 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);
+
+ if (!PluginConfig.g_FlashAvatarAvail)
+ 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;
+ }
+ //MAD
+ case WM_CHAR:
+ if (wParam == 0x03 &&isCtrl)
+ return SendMessage(hwnd, WM_COPY, 0, 0);
+ 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(!isCtrl && !isAlt&&!isShift)
+ {
+ if (/*wParam != VK_ESCAPE&&*/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_SPACE)
+ {
+ // TODO causes issues when pressing keys in the log
+ //SetFocus(GetDlgItem(mwdat->hwnd,IDC_MESSAGE));
+ //keybd_event((BYTE)wParam, (BYTE)MapVirtualKey(wParam,0), KEYEVENTF_EXTENDEDKEY | 0, 0);
+
+ //return 0;
+ }
+ }
+ break;
+ //MAD_
+ case WM_COPY: {
+ return(DM_WMCopyHandler(hwnd, OldMessageLogProc, wParam, lParam));
+ }
+ case WM_NCCALCSIZE:
+ return(CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, OldMessageLogProc));
+ case WM_NCPAINT:
+ return(CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKHISTORY, msg, wParam, lParam, OldMessageLogProc));
+ 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;
+ }
+ case WM_NCDESTROY:
+ if(OldMessageLogProc)
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) OldMessageLogProc);
+ break;
+
+ }
+ return CallWindowProc(OldMessageLogProc, hwnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK MessageEditSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LONG lastEnterTime = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ HWND hwndParent = GetParent(hwnd);
+ struct TWindowData *mwdat = (struct TWindowData *)GetWindowLongPtr(hwndParent, GWLP_USERDATA);
+
+ /*
+ * 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);
+ else {
+ mwdat->fkeyProcessed = false;
+ return(0);
+ }
+ }
+ switch (msg) {
+ case WM_NCCALCSIZE:
+ return(CSkin::NcCalcRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, OldMessageEditProc));
+ case WM_NCPAINT:
+ return(CSkin::DrawRichEditFrame(hwnd, mwdat, ID_EXTBKINPUTAREA, msg, wParam, lParam, OldMessageEditProc));
+ case WM_DROPFILES:
+ SendMessage(hwndParent, WM_DROPFILES, (WPARAM)wParam, (LPARAM)lParam);
+ break;
+ case WM_CHAR: {
+ BOOL isCtrl, isShift, isAlt;
+ KbdState(mwdat, isShift, isCtrl, isAlt);
+ //MAD: sound on typing..
+ if(PluginConfig.g_bSoundOnTyping&&!isAlt&&!isCtrl&&!(mwdat->pContainer->dwFlags&CNT_NOSOUND)&&wParam!=VK_ESCAPE&&!(wParam==VK_TAB&&PluginConfig.m_AllowTab))
+ SkinPlaySound("SoundOnTyping");
+ //MAD
+ if (wParam == 0x0d && isCtrl && PluginConfig.m_MathModAvail) {
+ TCHAR toInsert[100];
+ BYTE keyState[256];
+ size_t i;
+ size_t iLen = lstrlen(PluginConfig.m_MathModStartDelimiter);
+ ZeroMemory(keyState, 256);
+ _tcsncpy(toInsert, PluginConfig.m_MathModStartDelimiter, 30);
+ _tcsncat(toInsert, PluginConfig.m_MathModStartDelimiter, 30);
+ SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM)toInsert);
+ SetKeyboardState(keyState);
+ for (i = 0; i < iLen; i++)
+ SendMessage(hwnd, WM_KEYDOWN, mwdat->dwFlags & MWF_LOG_RTL ? VK_RIGHT : VK_LEFT, 0);
+ return 0;
+ }
+ 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;
+ }
+ break;
+ }
+ case WM_MOUSEWHEEL: {
+ LRESULT result = DM_MouseWheelHandler(hwnd, hwndParent, mwdat, wParam, lParam);
+
+ if (result == 0)
+ return 0;
+ break;
+ }
+ case WM_PASTE:
+ case EM_PASTESPECIAL: {
+ if (OpenClipboard(hwnd)) {
+ HANDLE hClip;
+ if(hClip = GetClipboardData(CF_TEXT)) {
+ if (lstrlenA((char *)hClip) > mwdat->nMax) {
+ TCHAR szBuffer[512];
+ if (M->GetByte("autosplit", 0))
+ _sntprintf(szBuffer, 512, CTranslator::get(CTranslator::GEN_MSG_TOO_LONG_SPLIT), mwdat->nMax - 10);
+ else
+ _sntprintf(szBuffer, 512, CTranslator::get(CTranslator::GEN_MSG_TOO_LONG_NOSPLIT), mwdat->nMax);
+ SendMessage(hwndParent, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer);
+ }
+ }
+ else if(hClip = GetClipboardData(CF_BITMAP))
+ SendHBitmapAsFile(mwdat, (HBITMAP)hClip);
+
+ CloseClipboard();
+ }
+ return CallWindowProc(OldMessageEditProc, hwnd, msg, wParam, lParam);
+ }
+ case WM_KEYDOWN: {
+ BOOL isCtrl, isShift, isAlt;
+ KbdState(mwdat, isShift, isCtrl, isAlt);
+
+ //MAD: sound on typing..
+ 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;
+ else {
+ if (PluginConfig.m_SendOnDblEnter) {
+ 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);
+ }
+ HWND hwndDlg = hwndParent;
+ BOOL isCtrl, isShift, isAlt;
+
+ 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_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ break;
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ break;
+ case WM_INPUTLANGCHANGEREQUEST:
+ return CallWindowProc(OldMessageEditProc, hwnd, WM_INPUTLANGCHANGEREQUEST, wParam, lParam);
+ 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;
+ }
+ case WM_NCDESTROY:
+ if(OldMessageEditProc)
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) OldMessageEditProc);
+ break;
+
+
+ }
+ return CallWindowProc(OldMessageEditProc, hwnd, 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;
+ }
+ return CallWindowProc(OldAvatarWndProc, hwnd, msg, wParam, lParam);
+}
+
+LRESULT CALLBACK SplitterSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndParent = GetParent(hwnd);
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(hwndParent, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_NCHITTEST:
+ return HTCLIENT;
+ case WM_SETCURSOR: {
+ RECT rc;
+ 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)) {
+ RECT rc;
+
+ 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 {
+ SESSION_INFO *si = (SESSION_INFO *)dat->si;
+ dat->savedSplitY = si->iSplitterY;
+ }
+ dat->savedDynaSplit = dat->dynaSplitter;
+ }
+ }
+ SetCapture(hwnd);
+ return 0;
+ }
+ case WM_MOUSEMOVE:
+ if (GetCapture() == hwnd) {
+ RECT rc;
+ 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: {
+ RECT rc;
+ PAINTSTRUCT ps;
+ HDC dc = BeginPaint(hwnd, &ps);
+ int ctrlId = GetDlgCtrlID(hwnd);
+
+ GetClientRect(hwnd, &rc);
+
+ if (dat && CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(hwnd, dat->pContainer->hwnd, dat->pContainer, &rc, dc);
+ else if(M->isAero() || M->isVSThemed()) {
+ if(ctrlId == 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);
+ dat->panelWidth = -1;
+ 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))) {
+ RECT rc;
+ 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 defined(__FEAT_EXP_AUTOSPLITTER)
+ if(dat->fIsAutosizingInput)
+ selection = ID_SPLITTERCONTEXT_SETPOSITIONFORTHISSESSION;
+ else
+ selection = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndParent, NULL);
+#else
+ selection = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndParent, NULL);
+#endif
+ switch (selection) {
+ case ID_SPLITTERCONTEXT_SAVEFORTHISCONTACTONLY: {
+ HWND hwndParent = GetParent(hwnd);
+
+ dat->dwFlagsEx |= MWF_SHOW_SPLITTEROVERRIDE;
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "splitoverride", 1);
+ if (dat->bType == SESSIONTYPE_IM)
+ SaveSplitter(dat);
+ break;
+ }
+ case ID_SPLITTERCONTEXT_SETPOSITIONFORTHISSESSION:
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ if(dat->fIsAutosizingInput) {
+ RECT rc;
+ GetWindowRect(GetDlgItem(dat->hwnd, IDC_MESSAGE), &rc);
+ dat->iInputAreaHeight = 0;
+ }
+#endif
+ break;
+ case ID_SPLITTERCONTEXT_SAVEGLOBALFORALLSESSIONS: {
+
+ RECT rcWin;
+ BYTE bSync = M->GetByte("Chat", "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 = (SESSION_INFO *)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 CallWindowProc(OldSplitterProc, hwnd, 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 fInfoPanel = 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 (fInfoPanel)
+ 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:{
+ RECT rc;
+ 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->showPic) {
+ urc->rcItem.top = urc->rcItem.bottom - dat->pic.cy;
+ dat->fMustOffset = TRUE;
+ } else
+ dat->fMustOffset = FALSE;
+
+ if(showToolbar && bBottomToolbar && (PluginConfig.m_AlwaysFullToolbarWidth || ((dat->pic.cy - DPISCALEY_S(6)) < rc.bottom))) {
+ urc->rcItem.bottom -= DPISCALEY_S(22);
+ if(dat->fIsAutosizingInput) {
+ urc->rcItem.left--;
+ urc->rcItem.top--;
+ }
+ }
+
+ //Bolshevik: resizes avatar control _FIXED
+ 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);
+ //Bolshevik_
+ if (PluginConfig.g_FlashAvatarAvail) {
+ RECT rc = { urc->rcItem.left, urc->rcItem.top, urc->rcItem.right, urc->rcItem.bottom };
+ FLASHAVATAR fa = {0};
+
+ fa.hContact = !fInfoPanel ? dat->hContact : NULL;
+ fa.id = 25367;
+ fa.cProto = dat->szProto;
+ CallService(MS_FAVATAR_RESIZE, (WPARAM)&fa, (LPARAM)&rc);
+ }
+ 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->fMustOffset)
+ 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->showPic)
+ 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->fIsAutosizingInput)
+ 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 (fInfoPanel)
+ 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)
+{
+ struct TWindowData *dat = 0;
+ HWND hwndTab, hwndContainer;
+ struct TContainerData *m_pContainer = 0;
+
+ dat = (struct TWindowData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ hwndTab = GetParent(hwndDlg);
+
+ 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: {
+ RECT rc;
+ POINT pt;
+ int i;
+ BOOL isThemed = PluginConfig.m_bIsXP;
+ int dwLocalSmAdd = 0;
+ DBVARIANT dbv = {0};
+
+ struct TNewWindowData *newData = (struct TNewWindowData *) lParam;
+
+ dat = (struct TWindowData *) malloc(sizeof(struct TWindowData));
+ ZeroMemory((void *) dat, sizeof(struct 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 = CTranslator::get(CTranslator::GEN_MSG_CLOSE);
+ pszIDCSAVE_save = CTranslator::get(CTranslator::GEN_MSG_SAVEANDCLOSE);
+
+ 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;
+ CProxyWindow::add(dat);
+ dat->szProto = const_cast<char *>(dat->cache->getProto());
+ dat->bIsMeta = dat->cache->isMeta() ? TRUE : FALSE;
+ if(dat->bIsMeta)
+ dat->cache->updateMeta(true);
+ dat->cache->updateUIN();
+
+ if (dat->hContact && dat->szProto != NULL) {
+ dat->wStatus = DBGetContactSettingWord(dat->hContact, dat->szProto, "Status", ID_STATUS_OFFLINE);
+ mir_sntprintf(dat->szStatus, safe_sizeof(dat->szStatus), _T("%s"), (char *) CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, dat->szProto == NULL ? ID_STATUS_OFFLINE : dat->wStatus, GSMDF_TCHAR));
+ } else
+ dat->wStatus = ID_STATUS_OFFLINE;
+
+ GetMYUIN(dat);
+ GetClientIcon(dat);
+
+ CreateWindowEx(0, _T("TSButtonClass"), _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);
+ oldAvatarParentWndProc = (WNDPROC)SetWindowLongPtr(dat->hwndPanelPicParent, GWLP_WNDPROC, (INT_PTR)CInfoPanel::avatarParentSubclass);
+
+ dat->showUIElements = m_pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1;
+ 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 *)malloc(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 *)malloc(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 (M->GetDword(dat->hContact, "mwmask", 0)) {
+ if (dat->hContact)
+ LoadLocalFlags(hwndDlg, dat);
+ }
+
+ /*
+ * allow disabling emoticons per contact (note: currently unused feature)
+ */
+
+ dwLocalSmAdd = (int)M->GetByte(dat->hContact, "doSmileys", 0xff);
+ if (dwLocalSmAdd != 0xffffffff)
+ dat->doSmileys = dwLocalSmAdd;
+
+ DM_InitTip(dat);
+ dat->Panel->getVisibility();
+
+ dat->dwFlagsEx |= M->GetByte(dat->hContact, "splitoverride", 0) ? MWF_SHOW_SPLITTEROVERRIDE : 0;
+ dat->fIsAutosizingInput = IsAutoSplitEnabled(dat);
+ dat->iInputAreaHeight = -1;
+ SetMessageLog(dat);
+ dat->panelWidth = -1;
+ if (dat->hContact) {
+ dat->codePage = M->GetDword(dat->hContact, "ANSIcodepage", CP_ACP);
+ dat->Panel->loadHeight();
+ }
+
+ dat->showPic = GetAvatarVisibility(hwndDlg, dat);
+ 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);
+
+ if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKBUTTONSNPRESSED].IGNORED &&
+ !SkinItems[ID_EXTBKBUTTONSPRESSED].IGNORED && !SkinItems[ID_EXTBKBUTTONSMOUSEOVER].IGNORED) {
+ isThemed = FALSE;
+ }
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_ADD), BUTTONSETASFLATBTN, 0, 0);
+ SendMessage(GetDlgItem(hwndDlg, IDC_CANCELADD), BUTTONSETASFLATBTN, 0, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASFLATBTN + 10, 0, isThemed ? 1 : 0);
+ SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASFLATBTN + 12, 0, (LPARAM)m_pContainer);
+ SendDlgItemMessage(hwndDlg, IDC_TOGGLESIDEBAR, BUTTONSETASTOOLBARBUTTON, 0, 1);
+
+ TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPENING, 0);
+
+ for (i = 0;;i++) {
+ if (tooltips[i].id == -1)
+ break;
+ SendDlgItemMessage(hwndDlg, tooltips[i].id, BUTTONADDTOOLTIP, (WPARAM)CTranslator::get(tooltips[i].translate_id), 0);
+ }
+ SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, dat->bNotOnList ? CTranslator::get(CTranslator::GEN_MSG_CONTACT_NOT_ON_LIST) :
+ CTranslator::get(CTranslator::GEN_MSG_LOGFROZENSTATIC));
+
+ SendMessage(GetDlgItem(hwndDlg, IDC_SAVE), BUTTONADDTOOLTIP, (WPARAM)pszIDCSAVE_close, 0);
+ SendMessage(GetDlgItem(hwndDlg, IDC_PROTOCOL), BUTTONADDTOOLTIP, (WPARAM) CTranslator::get(CTranslator::GEN_MSG_TIP_CONTACTMENU), 0);
+
+ SetWindowText(GetDlgItem(hwndDlg, IDC_RETRY), CTranslator::get(CTranslator::GEN_MSG_BUTTON_RETRY));
+
+ {
+ UINT _ctrls[] = {IDC_RETRY, IDC_CANCELSEND, IDC_MSGSENDLATER};
+ for(i = 0; i < 3; i++) {
+ SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASPUSHBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASFLATBTN, 0, 1);
+ SendDlgItemMessage(hwndDlg, _ctrls[i], BUTTONSETASFLATBTN + 10, 0, 1);
+
+ }
+ }
+
+ SetWindowText(GetDlgItem(hwndDlg, IDC_CANCELSEND), CTranslator::get(CTranslator::GEN_MSG_BUTTON_CANCEL));
+ SetWindowText(GetDlgItem(hwndDlg, IDC_MSGSENDLATER), CTranslator::get(CTranslator::GEN_MSG_BUTTON_SENDLATER));
+
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETUNDOLIMIT, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_LINK);
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETEVENTMASK, 0, ENM_REQUESTRESIZE | ENM_MOUSEEVENTS | ENM_SCROLL | ENM_KEYEVENTS | ENM_CHANGE);
+#else
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_SCROLL | ENM_KEYEVENTS | ENM_CHANGE);
+#endif
+ 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, (WPARAM) TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXLIMITTEXT, 0, 0x80000000);
+ /*
+ * subclassing stuff
+ */
+
+ OldMessageEditProc = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MESSAGE), GWLP_WNDPROC, (LONG_PTR) MessageEditSubclassProc);
+ OldAvatarWndProc = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CONTACTPIC), GWLP_WNDPROC, (LONG_PTR) AvatarSubclassProc);
+ OldSplitterProc = (WNDPROC) SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_SPLITTER), GWLP_WNDPROC, (LONG_PTR) SplitterSubclassProc);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MULTISPLITTER), GWLP_WNDPROC, (LONG_PTR) SplitterSubclassProc);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PANELSPLITTER), GWLP_WNDPROC, (LONG_PTR) 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) {
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(dat->hContact, SRMSGMOD, "SavedMsg", &dbv)) {
+ SETTEXTEX stx = {ST_DEFAULT, CP_UTF8};
+
+ if (dbv.type == DBVT_ASCIIZ && lstrlenA(dbv.pszVal) > 0)
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)dbv.pszVal);
+ DBFreeVariant(&dbv);
+ SendQueue::UpdateSaveAndSendButton(dat);
+ if (m_pContainer->hwndActive == hwndDlg)
+ UpdateReadChars(dat);
+ }
+ }
+ if (newData->szInitialText) {
+ int len;
+ if (newData->isWchar)
+ SetDlgItemTextW(hwndDlg, IDC_MESSAGE, (TCHAR *)newData->szInitialText);
+ else
+ SetDlgItemTextA(hwndDlg, IDC_MESSAGE, newData->szInitialText);
+ len = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ PostMessage(GetDlgItem(hwndDlg, IDC_MESSAGE), EM_SETSEL, len, len);
+ if (len)
+ EnableSendButton(dat, TRUE);
+ }
+ //dat->dwFlags &= ~MWF_INITMODE;
+ {
+ DBEVENTINFO dbei = { 0};
+ HANDLE hdbEvent;
+
+ dbei.cbSize = sizeof(dbei);
+ hdbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) dat->hContact, 0);
+ if (hdbEvent) {
+ do {
+ ZeroMemory(&dbei, sizeof(dbei));
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET, (WPARAM) hdbEvent, (LPARAM) & dbei);
+ if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & DBEF_SENT)) {
+ dat->lastMessage = dbei.timestamp;
+ DM_UpdateLastMessage(dat);
+ break;
+ }
+ } while (hdbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) hdbEvent, 0));
+ }
+
+ }
+ SendMessage(hwndContainer, DM_QUERYCLIENTAREA, 0, (LPARAM)&rc);
+
+ {
+ WNDCLASSA wndClass;
+
+ ZeroMemory(&wndClass, sizeof(wndClass));
+ GetClassInfoA(g_hInst, "RichEdit20A", &wndClass);
+ OldMessageLogProc = wndClass.lpfnWndProc;
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_LOG), GWLP_WNDPROC, (LONG_PTR) MessageLogSubclassProc);
+ }
+ 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) {
+ DBEVENTINFO dbei = {0};
+
+ dbei.flags = 0;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dat->iFlashIcon = PluginConfig.g_IconMsgEvent;
+ SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL);
+ dat->mayFlashTab = TRUE;
+ 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);
+ dat->dwFlags |= MWF_DEFERREDSCROLL;
+ }
+ if (newData->iActivate) {
+ m_pContainer->hwndActive = hwndDlg;
+ ShowWindow(hwndDlg, SW_SHOW);
+ SetActiveWindow(hwndDlg);
+ SetForegroundWindow(hwndDlg);
+ //SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ PostMessage(hwndContainer, DM_UPDATETITLE, (WPARAM)dat->hContact, 0);
+ } 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, (WPARAM)dat->hContact, 0);
+ }
+
+ DM_RecalcPictureSize(dat);
+ dat->dwLastActivity = GetTickCount() - 1000;
+ m_pContainer->dwLastActivity = dat->dwLastActivity;
+
+ if (dat->hwndHPP) {
+ WNDPROC wndProc = (WNDPROC)SetWindowLongPtr(dat->hwndHPP, GWLP_WNDPROC, (LONG_PTR)HPPKFSubclassProc);
+ dat->oldIEViewProc = wndProc;
+ }
+
+ dat->dwFlags &= ~MWF_INITMODE;
+ TABSRMM_FireEvent(dat->hContact, hwndDlg, MSG_WINDOW_EVT_OPEN, 0);
+
+ if(PluginConfig.g_bClientInStatusBar)
+ ChangeClientIconInStatusBar(dat);
+
+ /*
+ * show a popup if wanted...
+ */
+ if (newData->bWantPopup) {
+ DBEVENTINFO dbei = {0};
+ newData->bWantPopup = FALSE;
+ CallService(MS_DB_EVENT_GET, (WPARAM)newData->hdbEvent, (LPARAM)&dbei);
+ tabSRMM_ShowPopup((WPARAM)dat->hContact, (LPARAM)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 ? TRUE : FALSE;
+ }
+ case WM_ERASEBKGND: {
+ HDC hdc = (HDC)wParam;
+
+ RECT rcClient, rcWindow, rc;
+ HDC hdcMem = 0;
+ HBITMAP hbm, hbmOld;
+ DWORD cx, cy;
+ HANDLE hpb = 0;
+
+ GetClientRect(hwndDlg, &rcClient);
+ cx = rcClient.right - rcClient.left;
+ 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 fInfoPanel = dat->Panel->isActive();
+ bool fAero = M->isAero();
+
+ if (CSkin::m_skinEnabled) {
+ CSkinItem *item;
+ POINT pt;
+ UINT item_ids[2] = {ID_EXTBKHISTORY, ID_EXTBKINPUTAREA};
+ UINT ctl_ids[2] = {IDC_LOG, IDC_MESSAGE};
+ int i;
+ 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 (i = 0; i < 2; i++) {
+ 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], fAero);
+
+ /*
+ * 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: {
+ UTILRESIZEDIALOG urd;
+ BITMAP bminfo;
+ RECT rc;
+ int saved = 0;
+ HBITMAP hbm = ((dat->Panel->isActive()) && m_pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown);
+
+ if (IsIconic(hwndDlg))
+ break;
+ ZeroMemory(&urd, sizeof(urd));
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = g_hInst;
+ urd.hwndDlg = hwndDlg;
+ urd.lParam = (LPARAM) dat;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_MSGSPLITNEW);
+ urd.pfnResizer = MessageDialogResize;
+
+ 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);
+ }
+
+ if (hbm != 0) {
+ GetObject(hbm, sizeof(bminfo), &bminfo);
+ CalcDynamicAvatarSize(dat, &bminfo);
+ }
+
+ GetClientRect(hwndDlg, &rc);
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM) & urd);
+ BB_SetButtonsPos(dat);
+
+ /*
+ * size info panel fields
+ */
+
+ LONG cx = rc.right;
+ LONG panelHeight = dat->Panel->getHeight();
+ LONG panelWidth = (dat->panelWidth != -1 ? dat->panelWidth : 0);
+
+ rc.top = 1;
+ rc.left = cx - (panelWidth > 0 ? panelWidth : panelHeight);
+ rc.bottom = rc.top + (panelHeight - 3);
+ rc.right = cx;
+ rc.bottom--;
+
+ if (PluginConfig.g_FlashAvatarAvail) {
+ RECT rc1 = { 0, 0, rc.right - rc.left, rc.bottom - rc.top };
+ if (dat->Panel->isActive()) {
+ FLASHAVATAR fa = {0};
+
+ fa.hContact = dat->hContact;
+ fa.id = 25367;
+ fa.cProto = dat->szProto;
+ CallService(MS_FAVATAR_RESIZE, (WPARAM)&fa, (LPARAM)&rc1);
+ }
+ }
+ if(dat->showInfoPic && (dat->hwndPanelPic || dat->hwndFlash)) {
+ SetWindowPos(dat->hwndPanelPicParent, HWND_TOP, rc.left - 2, rc.top, rc.right - rc.left, (rc.bottom - rc.top) + 1, 0);
+ ShowWindow(dat->hwndPanelPicParent, (dat->panelWidth == -1) || !dat->Panel->isActive() ? SW_HIDE : SW_SHOW);
+ }
+ else if(dat->hwndPanelPicParent)
+ ShowWindow(dat->hwndPanelPicParent, SW_HIDE);
+
+ dat->rcPic = rc;
+
+ rc.right = cx - panelWidth;
+ rc.left = cx - panelWidth - 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->panelWidth - (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->panelWidth + 2) - dat->panelStatusCX;
+ rc.bottom = panelHeight - 3;
+ rc.top = rc.bottom - dat->ipFieldHeight;
+ dat->rcUIN = rc;
+
+ 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);
+
+ dat->Panel->Invalidate();
+ 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_CLIST:
+ switch (((NMHDR *) lParam)->code) {
+ case CLN_OPTIONSCHANGED:
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETGREYOUTFLAGS, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETLEFTMARGIN, 2, 0);
+ break;
+ }
+ break;
+ 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, (WPARAM)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, (WPARAM)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, CTranslator::get(CTranslator::QMGR_ERROR_NOMULTISEND));
+ 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) {
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "mwmask", dwMask);
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "mwflags", dwFlags);
+ }
+ else {
+ DBDeleteContactSetting(dat->hContact, SRMSGMOD_T, "mwmask");
+ DBDeleteContactSetting(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:
+ {
+ HWND hwndEdit = GetDlgItem(hwndDlg, IDC_MESSAGE);
+
+ dat->sendMode ^= SMODE_MULTIPLE;
+ if (dat->sendMode & SMODE_MULTIPLE) {
+ HWND hwndClist = DM_CreateClist(dat);
+ } else {
+ if (IsWindow(GetDlgItem(hwndDlg, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(hwndDlg, IDC_CLIST));
+ }
+ 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));
+ }
+ default:
+ break;
+ }
+ 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_TEXTT, 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, CTranslator::get(CTranslator::GEN_MSG_CONTACT_NOT_ON_LIST));
+ else
+ SetDlgItemText(hwndDlg, IDC_LOGFROZENTEXT, CTranslator::get(CTranslator::GEN_MSG_LOGFROZENSTATIC));
+ 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, (WPARAM)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));
+ return(_dlgReturn(hwndDlg, 1));
+ }
+ 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));
+ }
+ //MAD_
+ if (msg == WM_MOUSEWHEEL && (((NMHDR *)lParam)->idFrom == IDC_LOG || ((NMHDR *)lParam)->idFrom == IDC_MESSAGE)) {
+ RECT rc;
+ POINT pt;
+
+ 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);
+ //MAD
+ 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", 0)) {
+ 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);
+ free(streamOut);
+ }
+ SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ } else if (M->GetByte("autocopy", 0) && !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)CTranslator::get(CTranslator::GEN_MSG_SEL_COPIED));
+ }
+ }
+ }
+ 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;
+ }
+
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ case EN_REQUESTRESIZE: {
+ REQRESIZE *rr = (REQRESIZE *)lParam;
+ DM_HandleAutoSizeRequest(dat, rr);
+ break;
+ }
+#endif
+ 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: {
+ TEXTRANGEA tr;
+ CHARRANGE sel;
+
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM) & sel);
+ if (sel.cpMin != sel.cpMax)
+ break;
+ tr.chrg = ((ENLINK *) lParam)->chrg;
+ tr.lpstrText = (char *)mir_alloc(tr.chrg.cpMax - tr.chrg.cpMin + 8);
+ SendDlgItemMessageA(hwndDlg, IDC_LOG, EM_GETTEXTRANGE, 0, (LPARAM) & tr);
+ if (strchr(tr.lpstrText, '@') != NULL && strchr(tr.lpstrText, ':') == NULL && strchr(tr.lpstrText, '/') == NULL) {
+ MoveMemory(tr.lpstrText + 7, tr.lpstrText, tr.chrg.cpMax - tr.chrg.cpMin + 1);
+ CopyMemory(tr.lpstrText, _T("mailto:"), 7);
+ }
+ if (IsStringValidLinkA(tr.lpstrText)) {
+ if (((ENLINK *) lParam)->msg == WM_RBUTTONDOWN) {
+ HMENU hMenu, hSubMenu;
+ POINT pt;
+
+ hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_CONTEXT));
+ hSubMenu = GetSubMenu(hMenu, 1);
+ CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM) hSubMenu, 0);
+ 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, (LPARAM) tr.lpstrText);
+ break;
+ case IDM_OPENEXISTING:
+ CallService(MS_UTILS_OPENURL, 0, (LPARAM) tr.lpstrText);
+ break;
+ case IDM_COPYLINK: {
+ HGLOBAL hData;
+ if (!OpenClipboard(hwndDlg))
+ break;
+ EmptyClipboard();
+ hData = GlobalAlloc(GMEM_MOVEABLE, lstrlenA(tr.lpstrText) + 1);
+ lstrcpyA((char *)GlobalLock(hData), tr.lpstrText);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_TEXT, hData);
+ CloseClipboard();
+ break;
+ }
+ }
+ mir_free(tr.lpstrText);
+ DestroyMenu(hMenu);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ } else {
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM) tr.lpstrText);
+ SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ }
+ }
+ mir_free(tr.lpstrText);
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case DM_TYPING: {
+ int preTyping = dat->nTypeSecs != 0;
+ dat->nTypeSecs = (int) lParam > 0 ? (int) lParam : 0;
+
+ if(dat->nTypeSecs)
+ dat->showTyping = 0;
+
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, preTyping);
+ return TRUE;
+ }
+ case DM_UPDATEWINICON: {
+ HWND t_hwnd;
+ const char* szProto = dat->cache->getActiveProto();
+ WORD wStatus = dat->cache->getActiveStatus();
+
+ t_hwnd = m_pContainer->hwnd;
+
+ if (dat->hXStatusIcon) {
+ DestroyIcon(dat->hXStatusIcon);
+ dat->hXStatusIcon = 0;
+ }
+
+ if (szProto) {
+ dat->hTabIcon = dat->hTabStatusIcon = MY_GetContactIcon(dat);
+ if (M->GetByte("use_xicons", 1))
+ dat->hXStatusIcon = GetXStatusIcon(dat);
+ SendDlgItemMessage(hwndDlg, IDC_PROTOCOL, BUTTONSETASFLATBTN + 11, 0, dat->dwFlagsEx & MWF_SHOW_ISIDLE ? 1 : 0);
+ SendDlgItemMessage(hwndDlg, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(dat->hXStatusIcon ? dat->hXStatusIcon : dat->hTabIcon));
+
+ if (m_pContainer->hwndActive == hwndDlg)
+ SendMessage(t_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->showUIElements = 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: {
+ int i;
+ for (i = 0;;i++) {
+ if (buttonicons[i].id == -1)
+ break;
+ SendDlgItemMessage(hwndDlg, buttonicons[i].id, BM_SETIMAGE, IMAGE_ICON, (LPARAM)*buttonicons[i].pIcon);
+ SendDlgItemMessage(hwndDlg, buttonicons[i].id, BUTTONSETASFLATBTN + 12, 0, (LPARAM)m_pContainer);
+ }
+ 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:
+ if (PluginConfig.g_FlashAvatarAvail) { // own avatar draw
+ FLASHAVATAR fa = { 0 };
+ fa.cProto = dat->szProto;
+ fa.id = 25367;
+
+ CallService(MS_FAVATAR_GETINFO, (WPARAM)&fa, 0);
+ if (fa.hWindow) {
+ if (dat->Panel->isActive()) {
+ SetParent(fa.hWindow, GetDlgItem(hwndDlg, IDC_CONTACTPIC));
+ ShowWindow(fa.hWindow, SW_SHOW);
+ } else {
+ ShowWindow(fa.hWindow, SW_HIDE);
+ }
+ }
+ }
+ MsgWindowUpdateState(dat, WM_SETFOCUS);
+ SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ return 1;
+
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) != WA_ACTIVE) {
+ //m_pContainer->hwndSaved = 0;
+ 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: {
+ POINT pt;
+ RECT rc;
+
+ if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_MULTISPLITTER)) {
+ int oldSplitterX;
+ GetClientRect(hwndDlg, &rc);
+ pt.x = wParam;
+ pt.y = 0;
+ ScreenToClient(hwndDlg, &pt);
+ 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;
+ 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
+ */
+ //mad
+ 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;
+ POINT pt;
+
+ GetClientRect(GetDlgItem(hwndDlg, IDC_LOG), &rc);
+ pt.x = 0;
+ pt.y = 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);
+ dat->panelWidth = -1;
+ //SetAeroMargins(dat->pContainer);
+ RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
+ if(M->isAero())
+ InvalidateRect(GetParent(hwndDlg), NULL, FALSE);
+ break;
+ }
+ return 0;
+ }
+ /*
+ * 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_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: {
+ int i;
+
+ for (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 ? CTranslator::get(CTranslator::GEN_MSG_CONTACT_NOT_ON_LIST) :
+ CTranslator::get(CTranslator::GEN_MSG_LOGFROZENSTATIC));
+ return 0;
+ }
+ case DM_SCROLLIEVIEW: {
+ IEVIEWWINDOW iew = {0};
+
+ iew.cbSize = sizeof(IEVIEWWINDOW);
+ 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)
+ return(DM_ScrollToBottom(dat, 0, 1));
+
+ 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)
+ return(DM_ScrollToBottom(dat, 0, 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;
+ }
+ /*
+ * this is called whenever a new event has been added to the database.
+ * this CAN be posted (some sanity checks required).
+ */
+ case HM_DBEVENTADDED:
+ if (!dat)
+ return 0;
+ if ((HANDLE)wParam != dat->hContact)
+ return 0;
+ if (dat->hContact == NULL)
+ return 0;
+ DM_EventAdded(dat, wParam, lParam);
+ return(0);
+ case WM_TIMER:
+ /*
+ * timer to control info panel hovering
+ */
+ if (wParam == TIMERID_AWAYMSG) {
+ POINT pt;
+
+ 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, safe_sizeof(job->szErrorMsg), CTranslator::get(CTranslator::GEN_MSG_DELIVERYFAILURE),
+ CTranslator::get(CTranslator::GEN_MSG_SENDTIMEOUT));
+ 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: {
+ int iNextFailed;
+
+ if (!(dat->dwFlags & MWF_ERRORSTATE))
+ break;
+
+ 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);
+ if ((iNextFailed = sendQueue->findNextFailed(dat)) >= 0)
+ sendQueue->handleError(dat, iNextFailed);
+ break;
+ }
+ case MSGERROR_RETRY: {
+ int resent = 0;;
+
+ if (!(dat->dwFlags & MWF_ERRORSTATE))
+ break;
+
+ 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->hOwner == 0)
+ break;
+ job->hSendId = (HANDLE) CallContactService(job->hOwner,
+ SendQueue::MsgServiceName(job->hOwner, dat, job->dwFlags), (dat->sendMode & SMODE_FORCEANSI) ? (job->dwFlags & ~PREF_UNICODE) : job->dwFlags, (LPARAM) job->sendBuffer);
+ 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;
+ }
+ 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: {
+ struct TContainerData **pc = (struct TContainerData **) lParam;
+ if (pc)
+ *pc = m_pContainer;
+ return 0;
+ }
+
+ case DM_QUERYHCONTACT: {
+ HANDLE *phContact = (HANDLE *) lParam;
+ if (phContact)
+ *phContact = dat->hContact;
+ return 0;
+ }
+
+ case DM_UPDATELASTMESSAGE:
+ DM_UpdateLastMessage(dat);
+ return 0;
+
+ case DM_SAVESIZE: {
+ RECT rcClient;
+
+ 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);
+ }
+ 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: {
+ POINT tmp; //+ Protogenes
+ POINTS cur; //+ Protogenes
+ GetCursorPos(&tmp); //+ Protogenes
+ cur.x = (SHORT)tmp.x; //+ Protogenes
+ cur.y = (SHORT)tmp.y; //+ Protogenes
+ if(!dat->Panel->isHovered())
+ SendMessage(hwndContainer, WM_NCLBUTTONDOWN, HTCAPTION, *((LPARAM*)(&cur))); //+ Protogenes
+ break;
+ }
+ case WM_LBUTTONUP: {
+ POINT tmp; //+ Protogenes
+ POINTS cur; //+ Protogenes
+ GetCursorPos(&tmp); //+ Protogenes
+ if(dat->Panel->isHovered())
+ dat->Panel->handleClick(tmp);
+ else {
+ cur.x = (SHORT)tmp.x; //+ Protogenes
+ cur.y = (SHORT)tmp.y; //+ Protogenes
+ SendMessage(hwndContainer, WM_NCLBUTTONUP, HTCAPTION, *((LPARAM*)(&cur))); //+ Protogenes
+ }
+ break;
+ }
+
+ case WM_RBUTTONUP: {
+ POINT pt;
+ int iSelection;
+ HMENU subMenu;
+ int isHandled;
+ 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->showPic != 0)) {
+ int iSelection, isHandled;
+ HMENU submenu = 0;
+
+ submenu = GetSubMenu(m_pContainer->hMenuContext, menuID == MENU_PICMENU ? 1 : 11);
+ GetCursorPos(&pt);
+ MsgWindowUpdateMenu(dat, submenu, menuID);
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ isHandled = MsgWindowMenuHandler(dat, iSelection, menuID);
+ break;
+ }
+ subMenu = GetSubMenu(m_pContainer->hMenuContext, 0);
+
+ MsgWindowUpdateMenu(dat, subMenu, MENU_TABCONTEXT);
+
+ iSelection = TrackPopupMenu(subMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ if (iSelection >= IDM_CONTAINERMENU) {
+ DBVARIANT dbv = {0};
+ char szIndex[10];
+ char *szKey = "TAB_ContainersW";
+
+ _snprintf(szIndex, 8, "%d", iSelection - IDM_CONTAINERMENU);
+ if (iSelection - IDM_CONTAINERMENU >= 0) {
+ if (!M->GetTString(NULL, szKey, szIndex, &dbv)) {
+ SendMessage(hwndDlg, DM_CONTAINERSELECTED, 0, (LPARAM)dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ break;
+ }
+ isHandled = MsgWindowMenuHandler(dat, iSelection, MENU_TABCONTEXT);
+ break;
+ }
+ case WM_MOUSEMOVE: {
+ POINT pt;
+ 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)CTranslator::get(CTranslator::GEN_MSG_EDIT_NOTES_TIP));
+ return(0);
+ }
+ int bufSize = 0, memRequired = 0, flags = 0;
+ char *streamOut = NULL;
+ TCHAR *decoded = NULL, *converted = NULL;
+ FINDTEXTEXA fi = {0};
+ int final_sendformat = dat->SendFormat;
+ HWND hwndEdit = GetDlgItem(hwndDlg, IDC_MESSAGE);
+ PARAFORMAT2 pf2;
+
+ // 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.
+
+ ZeroMemory(&pf2, sizeof(PARAFORMAT2));
+ fi.chrg.cpMin = 0;
+ fi.chrg.cpMax = -1;
+ fi.lpstrText = "{";
+ final_sendformat = SendDlgItemMessageA(hwndDlg, IDC_MESSAGE, EM_FINDTEXTEX, FR_DOWN, (LPARAM) & fi) == -1 ? final_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;
+
+ streamOut = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, final_sendformat ? 0 : (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE));
+ if (streamOut != NULL) {
+ decoded = M->utf8_decodeW(streamOut);
+ if (decoded != NULL) {
+ char* utfResult = NULL;
+ if (final_sendformat)
+ DoRtfToTags(decoded, dat);
+ DoTrimMessage(decoded);
+ bufSize = WideCharToMultiByte(dat->codePage, 0, decoded, -1, dat->sendBuffer, 0, 0, 0);
+
+ if (!IsUtfSendAvailable(dat->hContact)) {
+ flags |= PREF_UNICODE;
+ memRequired = bufSize + ((lstrlenW(decoded) + 1) * sizeof(WCHAR));
+ } else {
+ flags |= PREF_UTF;
+ utfResult = M->utf8_encodeW(decoded);
+ memRequired = (int)(strlen(utfResult)) + 1;
+ }
+
+ /*
+ * try to detect RTL
+ */
+
+ SendMessage(hwndEdit, WM_SETREDRAW, FALSE, 0);
+ 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 *) 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));
+ }
+ mir_free(decoded);
+ }
+ free(streamOut);
+ }
+ if (memRequired == 0 || dat->sendBuffer[0] == 0)
+ break;
+
+ if (dat->sendMode & SMODE_CONTAINER && m_pContainer->hwndActive == hwndDlg && GetForegroundWindow() == hwndContainer) {
+ HWND contacthwnd;
+ TCITEM tci;
+ int tabCount = TabCtrl_GetItemCount(hwndTab), i;
+ char *szFromStream = NULL;
+
+ szFromStream = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_MESSAGE), dat, dat->SendFormat ? 0 : (CP_UTF8 << 16) | (SF_TEXT | SF_USECODEPAGE));
+ ZeroMemory((void *)&tci, sizeof(tci));
+ tci.mask = TCIF_PARAM;
+
+ for (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
+ 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);
+ }
+ }
+ }
+ if (szFromStream)
+ free(szFromStream);
+ }
+// 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: {
+ CHARRANGE sel;
+ TCHAR* szQuoted, *szText;
+ char* szFromStream = NULL;
+ HANDLE hDBEvent = 0;
+ int iCharsPerLine = M->GetDword("quoteLineLength", 64);
+ TCHAR *szConverted;
+ int iAlloced = 0;
+ unsigned int iSize = 0;
+ SETTEXTEX stx = {ST_SELECTION, 1200};
+
+ if (dat->hwndIEView || dat->hwndHPP) { // IEView quoting support..
+ TCHAR *selected = 0, *szQuoted = 0;
+ IEVIEWEVENT event;
+ ZeroMemory((void *)&event, sizeof(event));
+ event.cbSize = sizeof(IEVIEWEVENT);
+ 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)
+ free(szQuoted);
+ break;
+ } else {
+ hDBEvent = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)dat->hContact, 0);
+ goto quote_from_last;
+ }
+ }
+ if (dat->hDbEventLast == NULL)
+ break;
+ else
+ hDBEvent = dat->hDbEventLast;
+quote_from_last:
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM)&sel);
+ if (sel.cpMin == sel.cpMax) {
+ DBEVENTINFO dbei = {0};
+ int iDescr;
+
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM)hDBEvent, 0);
+ szText = (TCHAR *)malloc((dbei.cbBlob + 1) * sizeof(TCHAR)); //URLs are made one char bigger for crlf
+ dbei.pBlob = (BYTE *)szText;
+ CallService(MS_DB_EVENT_GET, (WPARAM)hDBEvent, (LPARAM)&dbei);
+ iSize = (int)(strlen((char *)dbei.pBlob)) + 1;
+ if (dbei.flags & DBEF_UTF) {
+ szConverted = M->utf8_decodeW((char*)szText);
+ iAlloced = TRUE;
+ } else {
+ if (iSize != dbei.cbBlob)
+ szConverted = (TCHAR *) & dbei.pBlob[iSize];
+ else {
+ szConverted = (TCHAR *)malloc(sizeof(TCHAR) * iSize);
+ iAlloced = TRUE;
+ MultiByteToWideChar(CP_ACP, 0, (char *) dbei.pBlob, -1, szConverted, iSize);
+ }
+ }
+ if (dbei.eventType == EVENTTYPE_FILE) {
+ 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 *)malloc(sizeof(TCHAR) * (1 + lstrlenA((char *)szText)));
+ MultiByteToWideChar(CP_ACP, 0, (char *) szText, -1, szConverted, 1 + lstrlenA((char *)szText));
+ iAlloced = TRUE;
+ }
+ szQuoted = QuoteText(szConverted, iCharsPerLine, 0);
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted);
+ free(szText);
+ free(szQuoted);
+ if (iAlloced)
+ mir_free(szConverted);
+ } else {
+ wchar_t *converted = 0;
+ szFromStream = Message_GetFromStream(GetDlgItem(hwndDlg, IDC_LOG), dat, SF_TEXT | SF_USECODEPAGE | SFF_SELECTION);
+ converted = M->utf8_decodeW(szFromStream);
+ Utils::FilterEventMarkers(converted);
+ szQuoted = QuoteText(converted, iCharsPerLine, 0);
+ SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szQuoted);
+ free(szQuoted);
+ mir_free(converted);
+ free(szFromStream);
+ }
+ SetFocus(GetDlgItem(hwndDlg, IDC_MESSAGE));
+ break;
+ }
+
+ case IDC_ADD: {
+ ADDCONTACTSTRUCT acs = {0};
+
+ acs.handle = dat->hContact;
+ acs.handleType = HANDLE_CONTACT;
+ acs.szProto = 0;
+ CallService(MS_ADDCONTACT_SHOW, (WPARAM) hwndDlg, (LPARAM) & acs);
+ if (!M->GetByte(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 (PluginConfig.m_MathModAvail && HIWORD(wParam) == EN_CHANGE)
+ MTH_updateMathWindow(dat);
+
+ 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: {
+ //mad
+ DWORD idFrom=GetDlgCtrlID((HWND)wParam);
+
+ if(idFrom>=MIN_CBUTTONID&&idFrom<=MAX_CBUTTONID) {
+ BB_CustomButtonClick(dat,idFrom,(HWND) wParam,1);
+ break;
+ }
+ //
+ if ((HWND)wParam == GetDlgItem(hwndDlg,IDC_NAME/* IDC_PROTOCOL*/) && dat->hContact != 0) {
+ POINT pt;
+ HMENU hMC;
+
+ GetCursorPos(&pt);
+ hMC = BuildMCProtocolMenu(hwndDlg);
+ if (hMC) {
+ int iSelection = 0;
+ iSelection = TrackPopupMenu(hMC, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ if (iSelection < 1000 && iSelection >= 100) { // the "force" submenu...
+ if (iSelection == 999) { // un-force
+ if (CallService(MS_MC_UNFORCESENDCONTACT, (WPARAM)dat->hContact, 0) == 0)
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "tabSRMM_forced", -1);
+ else
+ _DebugPopup(dat->hContact, TranslateT("Unforce failed"));
+ } else {
+ if (CallService(MS_MC_FORCESENDCONTACTNUM, (WPARAM)dat->hContact, (LPARAM)(iSelection - 100)) == 0)
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "tabSRMM_forced", (DWORD)(iSelection - 100));
+ else
+ _DebugPopup(dat->hContact, TranslateT("The selected protocol cannot be forced at this time"));
+ }
+ } else if (iSelection >= 1000) { // the "default" menu...
+ CallService(MS_MC_SETDEFAULTCONTACTNUM, (WPARAM)dat->hContact, (LPARAM)(iSelection - 1000));
+ }
+ DestroyMenu(hMC);
+ InvalidateRect(GetParent(hwndDlg), NULL, FALSE);
+ return TRUE;
+ }
+ }
+ 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: {
+ struct TContainerData *pNewContainer = 0;
+ TCHAR *szNewName = (TCHAR *)lParam;
+
+ if(!_tcscmp(szNewName, CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME)))
+ szNewName = CGlobals::m_default_container_name;
+
+ int iOldItems = TabCtrl_GetItemCount(hwndTab);
+ if (!_tcsncmp(m_pContainer->szName, szNewName, CONTAINER_NAMELEN))
+ break;
+ pNewContainer = FindContainerByName(szNewName);
+ if (pNewContainer == NULL)
+ pNewContainer = CreateContainer(szNewName, FALSE, dat->hContact);
+ M->WriteTString(dat->hContact, SRMSGMOD_T, "containerW", szNewName);
+ dat->fIsReattach = TRUE;
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, (LPARAM)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(const_cast<TCHAR *>(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);
+
+ /*
+ * sent from the containers heartbeat timer
+ * wParam = inactivity timer in seconds
+ */
+ /*
+ case DM_CHECKAUTOCLOSE: {
+ if (GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MESSAGE)) > 0)
+ break; // don't autoclose if message input area contains text
+ if (M->GetByte(dat->hContact, "NoAutoClose", 0))
+ break;
+ if (dat->dwTickLastEvent >= dat->dwLastActivity)
+ break; // don't autoclose if possibly unread message is waiting
+ if (((GetTickCount() - dat->dwLastActivity) / 1000) >= wParam) {
+ if (TabCtrl_GetItemCount(GetParent(hwndDlg)) > 1 || M->GetByte("autocloselast", 0))
+ SendMessage(hwndDlg, WM_CLOSE, 0, 1);
+ }
+ break;
+ }
+ */
+ case DM_CHECKAUTOHIDE:
+ DM_CheckAutoHide(dat, wParam, lParam);
+ return(0);
+
+ // metacontact support
+
+ case DM_UPDATEMETACONTACTINFO: { // update the icon in the statusbar for the "most online" protocol
+ DWORD isForced;
+ if ((isForced = M->GetDword(dat->hContact, "tabSRMM_forced", -1)) >= 0) {
+ char szTemp[64];
+ mir_snprintf(szTemp, sizeof(szTemp), "Status%d", isForced);
+ if (DBGetContactSettingWord(dat->hContact, PluginConfig.szMetaName, szTemp, 0) == ID_STATUS_OFFLINE) {
+ TCHAR szBuffer[200];
+ mir_sntprintf(szBuffer, 200, CTranslator::get(CTranslator::GEN_MSG_MC_OFFLINEPROTOCOL));
+ SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer);
+ }
+ }
+ SendMessage(hwndDlg, DM_UPDATEWINICON, 0, 0);
+ break;
+ }
+ 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_PROTOAVATARCHANGED:
+ dat->ace = Utils::loadAvatarFromAVS(dat->hContact);
+ dat->panelWidth = -1; // force new size calculations
+ ShowPicture(dat, TRUE);
+ if (dat->Panel->isActive()) {
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ }
+ return 0;
+ 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 = 0;
+
+ 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);
+ if(PluginConfig.g_bClientInStatusBar)
+ ChangeClientIconInStatusBar(dat);
+ 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, (LPARAM)0);
+ InvalidateRect(m_pContainer->hwndStatus, NULL, TRUE);
+ }
+ return 0;
+//mad: bb-api
+ 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();
+ int pcaps;
+
+ if (szProto == NULL)
+ break;
+
+ 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];
+
+ _sntprintf(szBuffer, safe_sizeof(szBuffer), CTranslator::get(CTranslator::GEN_MSG_OFFLINE_NO_FILE));
+ SendMessage(hwndDlg, DM_ACTIVATETOOLTIP, IDC_MESSAGE, (LPARAM)szBuffer);
+ break;
+ }
+ }
+ }
+ if (dat->hContact != NULL) {
+ if (CMimAPI::m_MimVersion >= PLUGIN_MAKE_VERSION(0, 9, 0, 0)) {
+
+ 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, (WPARAM)dat->hContact, (LPARAM)ppFiles);
+ } else {
+ if (ServiceExists(MS_HTTPSERVER_ADDFILENAME)) {
+ char *szHTTPText;
+ int i;
+
+ for (i = 0;i < totalCount;i++) {
+ char* szFileName = mir_t2a( ppFiles[i] );
+ char *szTemp = (char*)CallService(MS_HTTPSERVER_ADDFILENAME, (WPARAM)szFileName, 0);
+ mir_free( szFileName );
+ }
+ 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);
+ }
+ else {
+ TCHAR szFilename[MAX_PATH];
+ HDROP hDrop = (HDROP)wParam;
+ int fileCount = DragQueryFile(hDrop, -1, NULL, 0), totalCount = 0, i;
+ char** 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_SENDSPECIFICFILES, (WPARAM)dat->hContact, (LPARAM)ppFiles);
+ } else {
+ if (ServiceExists(MS_HTTPSERVER_ADDFILENAME)) {
+ char *szHTTPText;
+ int i;
+
+ for (i = 0;i < totalCount;i++) {
+ char* szFileName = ppFiles[i];
+ char *szTemp = (char*)CallService(MS_HTTPSERVER_ADDFILENAME, (WPARAM)szFileName, 0);
+ }
+ 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: {
+ int iTabs, i;
+ TCITEM item = {0};
+ RECT rc;
+ TContainerData *pContainer = dat->pContainer;
+
+ // 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);
+ }
+
+ if (dat->iOpenJobs > 0 && lParam != 2) {
+ if (dat->dwFlags & MWF_ERRORSTATE)
+ SendMessage(hwndDlg, DM_ERRORDECIDED, MSGERROR_CANCEL, 1);
+ else if (dat) {
+ LRESULT result;
+
+ if (dat->dwFlagsEx & MWF_EX_WARNCLOSE)
+ return TRUE;
+
+ dat->dwFlagsEx |= MWF_EX_WARNCLOSE;
+ result = SendQueue::WarnPendingJobs(0);
+ dat->dwFlagsEx &= ~MWF_EX_WARNCLOSE;
+ if (result == IDNO)
+ return TRUE;
+ }
+ }
+ iTabs = TabCtrl_GetItemCount(hwndTab);
+ if (iTabs == 1) {
+ PostMessage(hwndContainer, WM_CLOSE, 0, 1);
+ return 1;
+ }
+
+ TStatusBarIconNode *current;
+
+ while (dat->pSINod) {
+ current = dat->pSINod;
+ dat->pSINod = dat->pSINod->next;
+
+ mir_free(current->sid.szModule);
+ DestroyIcon(current->sid.hIcon);
+ if (current->sid.hIconDisabled) DestroyIcon(current->sid.hIconDisabled);
+ if (current->sid.szTooltip) mir_free(current->sid.szTooltip);
+ mir_free(current);
+ }
+
+ m_pContainer->iChilds--;
+ 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);
+ item.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwndTab, i, &item); // retrieve dialog hwnd for the now active tab...
+
+ m_pContainer->hwndActive = (HWND) item.lParam;
+ 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 (PluginConfig.g_FlashAvatarAvail) {
+ FLASHAVATAR fa = {0};
+
+ fa.hContact = dat->hContact;
+ fa.id = 25367;
+ fa.cProto = dat->szProto;
+ CallService(MS_FAVATAR_DESTROY, (WPARAM)&fa, 0);
+ }
+
+ if(dat->hwndContactPic)
+ DestroyWindow(dat->hwndContactPic);
+
+ if(dat->hwndPanelPic)
+ DestroyWindow(dat->hwndPanelPic);
+
+ if(dat->hClientIcon)
+ DestroyIcon(dat->hClientIcon);
+
+ if(dat->hwndPanelPicParent) {
+ if(oldAvatarParentWndProc)
+ SetWindowLongPtr(dat->hwndPanelPicParent, GWLP_WNDPROC, (LONG_PTR)oldAvatarParentWndProc);
+ 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) {
+ DBWriteContactSettingString(dat->hContact, SRMSGMOD, "SavedMsg", msg);
+ free(msg);
+ } else
+ DBWriteContactSettingString(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);
+
+ if (dat->sendBuffer != NULL)
+ free(dat->sendBuffer);
+ if (dat->hHistoryEvents)
+ free(dat->hHistoryEvents);
+ {
+ int i;
+ /*
+ * search the sendqueue for unfinished send jobs and free them. Leave unsent
+ * messages in the queue as they can be acked later
+ */
+ SendJob *jobs = sendQueue->getJobByIndex(0);
+
+ for (i = 0; i < SendQueue::NR_SENDJOBS; i++) {
+ if (jobs[i].hOwner == 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].hwndOwner = 0;
+ }
+ }
+ if (dat->hQueuedEvents)
+ 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()) {
+ M->WriteDword(SRMSGMOD, "multisplit", dat->multiSplitterX);
+ WriteStatsOnClose(dat);
+ }
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_MULTISPLITTER), GWLP_WNDPROC, (LONG_PTR) OldSplitterProc);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PANELSPLITTER), GWLP_WNDPROC, (LONG_PTR) OldSplitterProc);
+
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_SPLITTER), GWLP_WNDPROC, (LONG_PTR) OldSplitterProc);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CONTACTPIC), GWLP_WNDPROC, (LONG_PTR) OldAvatarWndProc);
+
+ {
+ HFONT hFont;
+ TCITEM item;
+ int i;
+
+ hFont = (HFONT) SendDlgItemMessage(hwndDlg, IDC_MESSAGE, WM_GETFONT, 0, 0);
+ if (hFont != NULL && hFont != (HFONT) SendDlgItemMessage(hwndDlg, IDOK, WM_GETFONT, 0, 0))
+ DeleteObject(hFont);
+
+ ZeroMemory((void *)&item, sizeof(item));
+ item.mask = TCIF_PARAM;
+
+ 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;
+ if (dat->oldIEViewProc) {
+ SetWindowLongPtr(dat->hwndIEView, GWLP_WNDPROC, (LONG_PTR)dat->oldIEViewProc);
+ dat->oldIEViewProc = 0;
+ }
+ CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+ if (dat->hwndHPP) {
+ IEVIEWWINDOW ieWindow;
+ ieWindow.cbSize = sizeof(IEVIEWWINDOW);
+ ieWindow.iType = IEW_DESTROY;
+ ieWindow.hwnd = dat->hwndHPP;
+ if (dat->oldIEViewProc) {
+ SetWindowLongPtr(dat->hwndHPP, GWLP_WNDPROC, (LONG_PTR)dat->oldIEViewProc);
+ dat->oldIEViewProc = 0;
+ }
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ }
+ if(dat->pWnd) {
+ delete dat->pWnd;
+ dat->pWnd = 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<HWND>(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 (M->GetByte(dat->hContact, "CList", "NotOnList", 0)) {
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)dat->hContact, 0);
+ }
+ }
+ delete dat->Panel;
+ free(dat);
+ }
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
diff --git a/plugins/TabSRMM/src/msgdlgutils.cpp b/plugins/TabSRMM/src/msgdlgutils.cpp new file mode 100644 index 0000000000..caefdcffc3 --- /dev/null +++ b/plugins/TabSRMM/src/msgdlgutils.cpp @@ -0,0 +1,2555 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: msgdlgutils.cpp 13650 2011-05-30 11:53:13Z silvercircle@gmail.com $
+ *
+ * Helper functions for the message dialog.
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+//#include "m_MathModule.h"
+
+extern NEN_OPTIONS nen_options;
+extern LOGFONTA logfonts[MSGDLGFONTCOUNT + 2];
+extern COLORREF fontcolors[MSGDLGFONTCOUNT + 2];
+extern TTemplateSet LTR_Active, RTL_Active;
+
+#ifndef SHVIEW_THUMBNAIL
+#define SHVIEW_THUMBNAIL 0x702D
+#endif
+
+#define EVENTTYPE_NICKNAME_CHANGE 9001
+#define EVENTTYPE_STATUSMESSAGE_CHANGE 9002
+#define EVENTTYPE_AVATAR_CHANGE 9003
+#define EVENTTYPE_CONTACTLEFTCHANNEL 9004
+#define EVENTTYPE_WAT_ANSWER 9602
+#define EVENTTYPE_JABBER_CHATSTATES 2000
+#define EVENTTYPE_JABBER_PRESENCE 2001
+
+static int g_status_events[] = {
+ EVENTTYPE_STATUSCHANGE,
+ EVENTTYPE_CONTACTLEFTCHANNEL,
+ EVENTTYPE_WAT_ANSWER,
+ EVENTTYPE_JABBER_CHATSTATES,
+ EVENTTYPE_JABBER_PRESENCE
+};
+
+static int g_status_events_size = 0;
+#define MAX_REGS(_A_) ( sizeof(_A_) / sizeof(_A_[0]) )
+
+BOOL TSAPI IsStatusEvent(int eventType)
+{
+ int i;
+
+ if (g_status_events_size == 0)
+ g_status_events_size = MAX_REGS(g_status_events);
+
+ for (i = 0; i < g_status_events_size; i++) {
+ if (eventType == g_status_events[i])
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * reorder tabs within a container. fSavePos indicates whether the new position should be saved to the
+ * contacts db record (if so, the container will try to open the tab at the saved position later)
+ */
+
+void TSAPI RearrangeTab(HWND hwndDlg, const TWindowData *dat, int iMode, BOOL fSavePos)
+{
+ TCITEM item = {0};
+ HWND hwndTab = GetParent(hwndDlg);
+ int newIndex;
+ TCHAR oldText[512];
+ item.mask = TCIF_IMAGE | TCIF_TEXT | TCIF_PARAM;
+ item.pszText = oldText;
+ item.cchTextMax = 500;
+
+ if (dat == NULL || !IsWindow(hwndDlg))
+ return;
+
+ TabCtrl_GetItem(hwndTab, dat->iTabID, &item);
+ newIndex = LOWORD(iMode);
+
+ if (newIndex >= 0 && newIndex <= TabCtrl_GetItemCount(hwndTab)) {
+ TabCtrl_DeleteItem(hwndTab, dat->iTabID);
+ item.lParam = (LPARAM)hwndDlg;
+ TabCtrl_InsertItem(hwndTab, newIndex, &item);
+ BroadCastContainer(dat->pContainer, DM_REFRESHTABINDEX, 0, 0);
+ ActivateTabFromHWND(hwndTab, hwndDlg);
+ if (fSavePos)
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "tabindex", newIndex * 100);
+ }
+}
+
+/*
+ * subclassing for the save as file dialog (needed to set it to thumbnail view on Windows 2000
+ * or later
+ */
+
+static BOOL CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG: {
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)lParam);
+ break;
+ }
+ case WM_NOTIFY: {
+ OPENFILENAMEA *ofn = (OPENFILENAMEA *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ HWND hwndParent = GetParent(hwnd);
+ HWND hwndLv = FindWindowEx(hwndParent, NULL, _T("SHELLDLL_DefView"), NULL) ;
+
+ if (hwndLv != NULL && *((DWORD *)(ofn->lCustData))) {
+ SendMessage(hwndLv, WM_COMMAND, SHVIEW_THUMBNAIL, 0);
+ *((DWORD *)(ofn->lCustData)) = 0;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * saves a contact picture to disk
+ * takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar)
+ * requires AVS and ADVAIMG services (Miranda 0.7+)
+ */
+
+static void SaveAvatarToFile(TWindowData *dat, HBITMAP hbm, int isOwnPic)
+{
+ TCHAR szFinalPath[MAX_PATH];
+ TCHAR szFinalFilename[MAX_PATH];
+ TCHAR szBaseName[MAX_PATH];
+ TCHAR szTimestamp[100];
+ OPENFILENAME ofn = {0};
+ time_t t = time(NULL);
+ struct tm *lt = localtime(&t);
+ DWORD setView = 1;
+
+ mir_sntprintf(szTimestamp, 100, _T("%04u %02u %02u_%02u%02u"), lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min);
+
+ TCHAR *szProto = mir_a2t(dat->cache->getActiveProto());
+
+ mir_sntprintf(szFinalPath, MAX_PATH, _T("%s\\%s"), M->getSavedAvatarPath(), szProto);
+ mir_free(szProto);
+
+ if (CreateDirectory(szFinalPath, 0) == 0) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS) {
+ MessageBox(0, CTranslator::get(CTranslator::GEN_MSG_SAVE_NODIR),
+ CTranslator::get(CTranslator::GEN_MSG_SAVE), MB_OK | MB_ICONSTOP);
+ return;
+ }
+ }
+ if (isOwnPic)
+ mir_sntprintf(szBaseName, MAX_PATH,_T("My Avatar_%s"), szTimestamp);
+ else
+ mir_sntprintf(szBaseName, MAX_PATH, _T("%s_%s"), dat->cache->getNick(), szTimestamp);
+
+ mir_sntprintf(szFinalFilename, MAX_PATH, _T("%s.png"), szBaseName);
+ ofn.lpstrDefExt = _T("png");
+
+ /*
+ * do not allow / or \ or % in the filename
+ */
+ Utils::sanitizeFilename(szFinalFilename);
+
+ TCHAR filter[MAX_PATH];
+ mir_sntprintf(filter, SIZEOF(filter), _T("%s%c*.bmp;*.png;*.jpg;*.gif%c%c"), TranslateT("Image files"), 0, 0, 0);
+ ofn.lpstrFilter = filter;
+ if (IsWinVer2000Plus()) {
+ ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
+ ofn.lpfnHook = (LPOFNHOOKPROC)OpenFileSubclass;
+ ofn.lStructSize = sizeof(ofn);
+ } else {
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.Flags = OFN_HIDEREADONLY;
+ }
+ ofn.hwndOwner = 0;
+ ofn.lpstrFile = szFinalFilename;
+ ofn.lpstrInitialDir = szFinalPath;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lCustData = (LPARAM) & setView;
+ if (GetSaveFileName(&ofn)) {
+ if (PathFileExists(szFinalFilename)) {
+ if (MessageBox(0, CTranslator::get(CTranslator::GEN_MSG_SAVE_FILE_EXISTS),
+ CTranslator::get(CTranslator::GEN_MSG_SAVE), MB_YESNO | MB_ICONQUESTION) == IDNO)
+ return;
+ }
+ IMGSRVC_INFO ii;
+ ii.cbSize = sizeof(ii);
+ ii.wszName = szFinalFilename;
+ ii.hbm = hbm;
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_UNKNOWN; // get the format from the filename extension. png is default.
+ CallService(MS_IMG_SAVE, (WPARAM)&ii, IMGL_TCHAR);
+ }
+}
+
+/*
+ * flash a tab icon if mode = true, otherwise restore default icon
+ * store flashing state into bState
+ */
+
+void TSAPI FlashTab(struct TWindowData *dat, HWND hwndTab, int iTabindex, BOOL *bState, BOOL mode, HICON origImage)
+{
+ TCITEM item;
+
+ ZeroMemory((void *)&item, sizeof(item));
+ item.mask = TCIF_IMAGE;
+
+ if (mode)
+ *bState = !(*bState);
+ else
+ dat->hTabIcon = origImage;
+ item.iImage = 0;
+ TabCtrl_SetItem(hwndTab, iTabindex, &item);
+ if(dat->pContainer->dwFlags & CNT_SIDEBAR)
+ dat->pContainer->SideBar->updateSession(dat);
+}
+
+/*
+ * calculates avatar layouting, based on splitter position to find the optimal size
+ * for the avatar w/o disturbing the toolbar too much.
+ */
+
+void TSAPI CalcDynamicAvatarSize(TWindowData *dat, BITMAP *bminfo)
+{
+ RECT rc;
+ double aspect = 0, newWidth = 0, picAspect = 0;
+ double picProjectedWidth = 0;
+ BOOL bBottomToolBar=dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR;
+ BOOL bToolBar=dat->pContainer->dwFlags & CNT_HIDETOOLBAR?0:1;
+ bool fInfoPanel = dat->Panel->isActive();
+ int iSplitOffset = dat->fIsAutosizingInput ? 1 : 0;
+
+ if (PluginConfig.g_FlashAvatarAvail) {
+ FLASHAVATAR fa = {0};
+ fa.cProto = dat->szProto;
+ fa.id = 25367;
+ fa.hContact = fInfoPanel ? NULL : dat->hContact;
+ CallService(MS_FAVATAR_GETINFO, (WPARAM)&fa, 0);
+ if (fa.hWindow) {
+ bminfo->bmHeight = FAVATAR_HEIGHT;
+ bminfo->bmWidth = FAVATAR_WIDTH;
+ }
+ }
+ GetClientRect(dat->hwnd, &rc);
+
+ if (dat->dwFlags & MWF_WASBACKGROUNDCREATE || dat->pContainer->dwFlags & CNT_DEFERREDCONFIGURE || dat->pContainer->dwFlags & CNT_CREATE_MINIMIZED || IsIconic(dat->pContainer->hwnd))
+ return; // at this stage, the layout is not yet ready...
+
+ if (bminfo->bmWidth == 0 || bminfo->bmHeight == 0)
+ picAspect = 1.0;
+ else
+ picAspect = (double)(bminfo->bmWidth / (double)bminfo->bmHeight);
+ picProjectedWidth = (double)((dat->dynaSplitter-((bBottomToolBar && bToolBar)? DPISCALEX_S(24):0) + ((dat->showUIElements != 0) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect;
+
+ if ((rc.right - (int)picProjectedWidth) > (dat->iButtonBarReallyNeeds) && !PluginConfig.m_AlwaysFullToolbarWidth && bToolBar)
+ dat->iRealAvatarHeight = dat->dynaSplitter + 3 + (dat->showUIElements ? DPISCALEY_S(28):DPISCALEY_S(2));
+ else
+ dat->iRealAvatarHeight = dat->dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset);
+
+ dat->iRealAvatarHeight-=((bBottomToolBar&&bToolBar) ? DPISCALEY_S(22) : 0);
+
+ if (PluginConfig.m_LimitStaticAvatarHeight > 0)
+ dat->iRealAvatarHeight = min(dat->iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight);
+
+ if (M->GetByte(dat->hContact, "dontscaleavatars", M->GetByte("dontscaleavatars", 0)))
+ dat->iRealAvatarHeight = min(bminfo->bmHeight, dat->iRealAvatarHeight);
+
+ if (bminfo->bmHeight != 0)
+ aspect = (double)dat->iRealAvatarHeight / (double)bminfo->bmHeight;
+ else
+ aspect = 1;
+
+ newWidth = (double)bminfo->bmWidth * aspect;
+ if (newWidth > (double)(rc.right) * 0.8)
+ newWidth = (double)(rc.right) * 0.8;
+ dat->pic.cy = dat->iRealAvatarHeight + 2;
+ dat->pic.cx = (int)newWidth + 2;
+}
+
+void TSAPI WriteStatsOnClose(TWindowData *dat)
+{
+ /*
+ DBEVENTINFO dbei;
+ char buffer[450];
+ HANDLE hNewEvent;
+ time_t now = time(NULL);
+ now = now - dat->stats.started;
+
+ return;
+
+ if (dat->hContact != 0 &&(PluginConfig.m_LogStatusChanges != 0) && dat->dwFlags & MWF_LOG_STATUSCHANGES) {
+ mir_snprintf(buffer, sizeof(buffer), "Session close - active for: %d:%02d:%02d, Sent: %d (%d), Rcvd: %d (%d)", now / 3600, now / 60, now % 60, dat->stats.iSent, dat->stats.iSentBytes, dat->stats.iReceived, dat->stats.iReceivedBytes);
+ dbei.cbSize = sizeof(dbei);
+ dbei.pBlob = (PBYTE) buffer;
+ dbei.cbBlob = (int)(strlen(buffer)) + 1;
+ dbei.eventType = EVENTTYPE_STATUSCHANGE;
+ dbei.flags = DBEF_READ;
+ dbei.timestamp = time(NULL);
+ dbei.szModule = dat->szProto;
+ hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) dat->hContact, (LPARAM) & dbei);
+ if (dat->hDbEventFirst == NULL) {
+ dat->hDbEventFirst = hNewEvent;
+ SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0);
+ }
+ }
+ */
+}
+
+int TSAPI MsgWindowUpdateMenu(TWindowData *dat, HMENU submenu, int menuID)
+{
+ HWND hwndDlg = dat->hwnd;
+ bool fInfoPanel = dat->Panel->isActive();
+
+ if (menuID == MENU_TABCONTEXT) {
+ SESSION_INFO *si = (SESSION_INFO *)dat->si;
+ int iTabs = TabCtrl_GetItemCount(GetParent(hwndDlg));
+
+ EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, M->GetByte("useclistgroups", 0) || M->GetByte("singlewinmode", 0) ? MF_GRAYED : MF_ENABLED);
+ EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M->GetDword(dat->hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED);
+ } else if (menuID == MENU_PICMENU) {
+ MENUITEMINFO mii = {0};
+ TCHAR *szText = NULL;
+ char avOverride = (char)M->GetByte(dat->hContact, "hideavatar", -1);
+ HMENU visMenu = GetSubMenu(submenu, 0);
+ BOOL picValid = fInfoPanel ? (dat->hOwnPic != 0) : (dat->ace && dat->ace->hbmPic && dat->ace->hbmPic != PluginConfig.g_hbmUnknown);
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STRING;
+
+ EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, MF_BYCOMMAND | (picValid ? MF_ENABLED : MF_GRAYED));
+
+ CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, MF_BYCOMMAND | (avOverride == -1 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, MF_BYCOMMAND | (avOverride == 0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, MF_BYCOMMAND | (avOverride == 1 ? MF_CHECKED : MF_UNCHECKED));
+
+ CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, MF_BYCOMMAND | (PluginConfig.m_AlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED));
+ if (!fInfoPanel) {
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, MF_BYCOMMAND | (ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED));
+ szText = const_cast<TCHAR *>(CTranslator::get(CTranslator::GEN_AVATAR_SETTINGS));
+ EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED);
+ } else {
+ EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED);
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, MF_BYCOMMAND | ((ServiceExists(MS_AV_SETMYAVATAR) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(dat->cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED));
+ szText = const_cast<TCHAR *>(CTranslator::get(CTranslator::GEN_AVATAR_SETOWN));
+ }
+ mii.dwTypeData = szText;
+ mii.cch = lstrlen(szText) + 1;
+ SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii);
+ } else if (menuID == MENU_PANELPICMENU) {
+ HMENU visMenu = GetSubMenu(submenu, 0);
+ char avOverride = (char)M->GetByte(dat->hContact, "hideavatar", -1);
+
+ CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, MF_BYCOMMAND | (avOverride == -1 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, MF_BYCOMMAND | (avOverride == 0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, MF_BYCOMMAND | (avOverride == 1 ? MF_CHECKED : MF_UNCHECKED));
+
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, MF_BYCOMMAND | (ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED));
+ EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, MF_BYCOMMAND | ((ServiceExists(MS_AV_SAVEBITMAP) && dat->ace && dat->ace->hbmPic && dat->ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED));
+ }
+ return 0;
+}
+
+int TSAPI MsgWindowMenuHandler(TWindowData *dat, int selection, int menuId)
+{
+ if(dat == 0)
+ return(0);
+
+ HWND hwndDlg = dat->hwnd;
+
+ if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) {
+ switch (selection) {
+ case ID_TABMENU_ATTACHTOCONTAINER:
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_SELECTCONTAINER), hwndDlg, SelectContainerDlgProc, (LPARAM) hwndDlg);
+ return 1;
+ case ID_TABMENU_CONTAINEROPTIONS:
+ if (dat->pContainer->hWndOptions == 0)
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CONTAINEROPTIONS), hwndDlg, DlgProcContainerOptions, (LPARAM) dat->pContainer);
+ return 1;
+ case ID_TABMENU_CLOSECONTAINER:
+ SendMessage(dat->pContainer->hwnd, WM_CLOSE, 0, 0);
+ return 1;
+ case ID_TABMENU_CLOSETAB:
+ SendMessage(hwndDlg, WM_CLOSE, 1, 0);
+ return 1;
+ case ID_TABMENU_SAVETABPOSITION:
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "tabindex", dat->iTabID * 100);
+ break;
+ case ID_TABMENU_CLEARSAVEDTABPOSITION:
+ DBDeleteContactSetting(dat->hContact, SRMSGMOD_T, "tabindex");
+ break;
+ case ID_TABMENU_LEAVECHATROOM: {
+ if (dat && dat->bType == SESSIONTYPE_CHAT) {
+ SESSION_INFO *si = (SESSION_INFO *)dat->si;
+ if ( (si != NULL) && (dat->hContact != NULL) ) {
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) dat->hContact, 0);
+ if ( szProto )
+ CallProtoService( szProto, PS_LEAVECHAT, (WPARAM)dat->hContact, 0 );
+ }
+ }
+ return 1;
+ }
+ case ID_VISIBILITY_DEFAULT:
+ case ID_VISIBILITY_HIDDENFORTHISCONTACT:
+ case ID_VISIBILITY_VISIBLEFORTHISCONTACT: {
+ BYTE avOverrideMode;
+
+ if (selection == ID_VISIBILITY_DEFAULT)
+ avOverrideMode = -1;
+ else if (selection == ID_VISIBILITY_HIDDENFORTHISCONTACT)
+ avOverrideMode = 0;
+ else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT)
+ avOverrideMode = 1;
+ M->WriteByte(dat->hContact, SRMSGMOD_T, "hideavatar", avOverrideMode);
+ dat->panelWidth = -1;
+ ShowPicture(dat, FALSE);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ DM_ScrollToBottom(dat, 0, 1);
+ return 1;
+ }
+ case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH:
+ PluginConfig.m_AlwaysFullToolbarWidth = !PluginConfig.m_AlwaysFullToolbarWidth;
+ M->WriteByte(SRMSGMOD_T, "alwaysfulltoolbar", (BYTE)PluginConfig.m_AlwaysFullToolbarWidth);
+ M->BroadcastMessage(DM_CONFIGURETOOLBAR, 0, 1);
+ break;
+ case ID_PICMENU_SAVETHISPICTUREAS:
+ if (dat->Panel->isActive()) {
+ if (dat)
+ SaveAvatarToFile(dat, dat->hOwnPic, 1);
+ } else {
+ if (dat && dat->ace)
+ SaveAvatarToFile(dat, dat->ace->hbmPic, 0);
+ }
+ break;
+ case ID_PANELPICMENU_SAVETHISPICTUREAS:
+ if (dat && dat->ace)
+ SaveAvatarToFile(dat, dat->ace->hbmPic, 0);
+ break;
+ case ID_PICMENU_SETTINGS: {
+ if (menuId == MENU_PANELPICMENU)
+ CallService(MS_AV_CONTACTOPTIONS, (WPARAM)dat->hContact, 0);
+ else if (menuId == MENU_PICMENU) {
+ if (dat->Panel->isActive()) {
+ if (ServiceExists(MS_AV_SETMYAVATAR) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(dat->cache->getActiveProto()), 0))
+ CallService(MS_AV_SETMYAVATAR, (WPARAM)(dat->cache->getActiveProto()), 0);
+ } else
+ CallService(MS_AV_CONTACTOPTIONS, (WPARAM)dat->hContact, 0);
+ }
+ }
+ return 1;
+ }
+ } else if (menuId == MENU_LOGMENU) {
+ int iLocalTime = 0;
+ int iRtl = (PluginConfig.m_RTLDefault == 0 ? M->GetByte(dat->hContact, "RTL", 0) : M->GetByte(dat->hContact, "RTL", 1));
+ int iLogStatus = (PluginConfig.m_LogStatusChanges != 0) && dat->dwFlags & MWF_LOG_STATUSCHANGES;
+
+ DWORD dwOldFlags = dat->dwFlags;
+
+ switch (selection) {
+ case ID_MESSAGELOGSETTINGS_GLOBAL: {
+ OPENOPTIONSDIALOG ood = {0};
+
+ ood.cbSize = sizeof(OPENOPTIONSDIALOG);
+ ood.pszGroup = NULL;
+ ood.pszPage = "Message Sessions";
+ ood.pszTab = NULL;
+ M->WriteByte(SRMSGMOD_T, "opage", 3); // force 3th tab to appear
+ CallService (MS_OPT_OPENOPTIONS, 0, (LPARAM)&ood);
+ return 1;
+ }
+
+ case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
+ CallService(MS_TABMSG_SETUSERPREFS, (WPARAM)dat->hContact, (LPARAM)0);
+ return 1;
+
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * update the status bar field which displays the number of characters in the input area
+ * and various indicators (caps lock, num lock, insert mode).
+ */
+
+void TSAPI UpdateReadChars(const TWindowData *dat)
+{
+
+ if(dat && (dat->pContainer->hwndStatus && dat->pContainer->hwndActive == dat->hwnd)) {
+ TCHAR buf[128];
+ int len;
+ TCHAR szIndicators[20];
+ BOOL fCaps, fNum;
+
+ szIndicators[0] = 0;
+ if (dat->bType == SESSIONTYPE_CHAT)
+ len = GetWindowTextLength(GetDlgItem(dat->hwnd, IDC_CHAT_MESSAGE));
+ else {
+ /*
+ * retrieve text length in UTF8 bytes, because this is the relevant length for most protocols
+ */
+ GETTEXTLENGTHEX gtxl = {0};
+ gtxl.codepage = CP_UTF8;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+ len = SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM) & gtxl, 0);
+ }
+
+ fCaps = (GetKeyState(VK_CAPITAL) & 1);
+ fNum = (GetKeyState(VK_NUMLOCK) & 1);
+
+ if (dat->fInsertMode)
+ lstrcat(szIndicators, _T("O"));
+ if (fCaps)
+ lstrcat(szIndicators, _T("C"));
+ if (fNum)
+ lstrcat(szIndicators, _T("N"));
+ if (dat->fInsertMode || fCaps || fNum)
+ lstrcat(szIndicators, _T(" | "));
+
+ mir_sntprintf(buf, safe_sizeof(buf), _T("%s%s %d/%d"), szIndicators, dat->lcID, dat->iOpenJobs, len);
+ SendMessage(dat->pContainer->hwndStatus, SB_SETTEXT, 1, (LPARAM) buf);
+ if(PluginConfig.m_visualMessageSizeIndicator)
+ InvalidateRect(dat->pContainer->hwndStatus, NULL, FALSE);
+ }
+}
+
+/*
+ * update all status bar fields and force a redraw of the status bar.
+ */
+
+void TSAPI UpdateStatusBar(const TWindowData *dat)
+{
+ if (dat && dat->pContainer->hwndStatus && dat->pContainer->hwndActive == dat->hwnd) {
+ if (dat->bType == SESSIONTYPE_IM) {
+ if(dat->szStatusBar[0]) {
+ SendMessage(dat->pContainer->hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ SendMessage(dat->pContainer->hwndStatus, SB_SETTEXT, 0, (LPARAM)dat->szStatusBar);
+ }
+ else {
+ SendMessage(dat->pContainer->hwndStatus, SB_SETICON, 0, 0);
+ DM_UpdateLastMessage(dat);
+ }
+ }
+ else
+ SendMessage(dat->pContainer->hwndStatus, SB_SETICON, 0, 0);
+ UpdateReadChars(dat);
+ InvalidateRect(dat->pContainer->hwndStatus, NULL, TRUE);
+ SendMessage(dat->pContainer->hwndStatus, WM_USER + 101, 0, (LPARAM)dat);
+ }
+}
+
+/*
+ * provide user feedback via icons on tabs. Used to indicate "send in progress" or
+ * any error state.
+ *
+ * NOT used for typing notification feedback as this is handled directly from the
+ * MTN handler.
+ */
+
+void TSAPI HandleIconFeedback(TWindowData *dat, HICON iIcon)
+{
+ TCITEM item = {0};
+ HICON iOldIcon = dat->hTabIcon;
+
+ if (iIcon == (HICON) - 1) { // restore status image
+ if (dat->dwFlags & MWF_ERRORSTATE) {
+ dat->hTabIcon = PluginConfig.g_iconErr;
+ } else {
+ dat->hTabIcon = dat->hTabStatusIcon;
+ }
+ } else
+ dat->hTabIcon = iIcon;
+ item.iImage = 0;
+ item.mask = TCIF_IMAGE;
+ if(dat->pContainer->dwFlags & CNT_SIDEBAR)
+ dat->pContainer->SideBar->updateSession(dat);
+ else
+ TabCtrl_SetItem(GetDlgItem(dat->pContainer->hwnd, IDC_MSGTABS), dat->iTabID, &item);
+}
+
+/*
+ * retrieve the visiblity of the avatar window, depending on the global setting
+ * and local mode
+ */
+
+int TSAPI GetAvatarVisibility(HWND hwndDlg, struct TWindowData *dat)
+{
+ BYTE bAvatarMode = dat->pContainer->avatarMode;
+ BYTE bOwnAvatarMode = dat->pContainer->ownAvatarMode;
+ char hideOverride = (char)M->GetByte(dat->hContact, "hideavatar", -1);
+ // infopanel visible, consider own avatar display
+
+ dat->showPic = 0;
+
+ if (dat->Panel->isActive() && bAvatarMode != 3) {
+ if (bOwnAvatarMode)
+ dat->showPic = FALSE;
+ else {
+ FLASHAVATAR fa = {0};
+ if (PluginConfig.g_FlashAvatarAvail) {
+ fa.cProto = dat->szProto;
+ fa.id = 25367;
+ fa.hParentWindow = GetDlgItem(hwndDlg, IDC_CONTACTPIC);
+
+ CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
+ if (fa.hWindow != NULL&&dat->hwndContactPic) {
+ DestroyWindow(dat->hwndContactPic);
+ dat->hwndContactPic = NULL;
+ }
+ dat->showPic = ((dat->hOwnPic && dat->hOwnPic != PluginConfig.g_hbmUnknown) || (fa.hWindow != NULL)) ? 1 : 0;
+ } else
+ dat->showPic = (dat->hOwnPic && dat->hOwnPic != PluginConfig.g_hbmUnknown) ? 1 : 0;
+
+ if (!PluginConfig.g_bDisableAniAvatars && fa.hWindow == NULL && !dat->hwndContactPic)
+ dat->hwndContactPic =CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, _T(""), WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(hwndDlg, IDC_CONTACTPIC), (HMENU)0, NULL, NULL);
+
+ }
+ switch (bAvatarMode) {
+ case 2:
+ dat->showInfoPic = 0;
+ break;
+ case 0:
+ dat->showInfoPic = 1;
+ case 1: {
+ FLASHAVATAR fa = {0};
+ HBITMAP hbm = ((dat->ace && !(dat->ace->dwFlags & AVS_HIDEONCLIST)) ? dat->ace->hbmPic : 0);
+
+ if(0 == hbm && 0 == bAvatarMode && !PluginConfig.g_bDisableAniAvatars) {
+ dat->showInfoPic = 0;
+ break;
+ }
+
+ if (PluginConfig.g_FlashAvatarAvail) {
+ fa.cProto = dat->szProto;
+ fa.id = 25367;
+ fa.hContact = dat->hContact;
+ fa.hParentWindow = dat->hwndPanelPicParent;
+
+ CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
+ if (fa.hWindow != NULL && dat->hwndPanelPic) {
+ DestroyWindow(dat->hwndPanelPic);
+ dat->hwndPanelPic = NULL;
+ ShowWindow(dat->hwndPanelPicParent, SW_SHOW);
+ EnableWindow(dat->hwndPanelPicParent, TRUE);
+ }
+ }
+ if (!PluginConfig.g_bDisableAniAvatars && fa.hWindow == NULL && !dat->hwndPanelPic) {
+ dat->hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, _T(""), WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, dat->hwndPanelPicParent, (HMENU)7000, NULL, NULL);
+ if(dat->hwndPanelPic)
+ SendMessage(dat->hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE);
+ }
+ if(bAvatarMode != 0) {
+ if ((hbm && hbm != PluginConfig.g_hbmUnknown) || (fa.hWindow != NULL))
+ dat->showInfoPic = 1;
+ else
+ dat->showInfoPic = 0;
+ }
+ break;
+ }
+ }
+ if (dat->showInfoPic)
+ dat->showInfoPic = hideOverride == 0 ? 0 : dat->showInfoPic;
+ else
+ dat->showInfoPic = hideOverride == 1 ? 1 : dat->showInfoPic;
+ //Bolshevik: reloads avatars
+ if (dat->showInfoPic) {
+ /** panel and contact is shown, reloads contact's avatar -> panel
+ * user avatar -> bottom picture
+ */
+ SendMessage(dat->hwndPanelPic, AVATAR_SETCONTACT, (WPARAM)0, (LPARAM)dat->hContact);
+ if (dat->hwndContactPic)
+ SendMessage(dat->hwndContactPic, AVATAR_SETPROTOCOL, (WPARAM)0, (LPARAM)dat->szProto);
+ }
+ else {
+ //show only user picture(contact is unaccessible)
+ if (dat->hwndContactPic)
+ SendMessage(dat->hwndContactPic, AVATAR_SETPROTOCOL, (WPARAM)0, (LPARAM)dat->szProto);
+ }
+//Bolshevik_
+ } else {
+ dat->showInfoPic = 0;
+
+ switch (bAvatarMode) {
+ case 0: // globally on
+ dat->showPic = 1;
+ break;
+ case 2: // globally OFF
+ dat->showPic = 0;
+ break;
+ case 3: // on, if present
+ case 1: {
+ FLASHAVATAR fa = {0};
+ HBITMAP hbm = (dat->ace && !(dat->ace->dwFlags & AVS_HIDEONCLIST)) ? dat->ace->hbmPic : 0;
+
+ if (PluginConfig.g_FlashAvatarAvail) {
+ fa.cProto = dat->szProto;
+ fa.id = 25367;
+ fa.hContact = dat->hContact;
+ fa.hParentWindow = GetDlgItem(hwndDlg, IDC_CONTACTPIC);
+
+ CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
+
+ if (fa.hWindow != NULL&&dat->hwndContactPic) {
+ DestroyWindow(dat->hwndContactPic);
+ dat->hwndContactPic = NULL;
+ }
+ }
+ if (!PluginConfig.g_bDisableAniAvatars&&fa.hWindow == NULL&&!dat->hwndContactPic)
+ dat->hwndContactPic =CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, _T(""), WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(hwndDlg, IDC_CONTACTPIC), (HMENU)0, NULL, NULL);
+
+ if ((hbm && hbm != PluginConfig.g_hbmUnknown) || (fa.hWindow != NULL))
+ dat->showPic = 1;
+ else
+ dat->showPic = 0;
+ break;
+ }
+ }
+ if (dat->showPic)
+ dat->showPic = hideOverride == 0 ? 0 : dat->showPic;
+ else
+ dat->showPic = hideOverride == 1 ? 1 : dat->showPic;
+ //Bolshevik: reloads avatars
+ if (dat->showPic)
+ //shows contact or user picture, depending on panel visibility
+ if (dat->hwndPanelPic)
+ SendMessage(dat->hwndContactPic, AVATAR_SETPROTOCOL, (WPARAM)0, (LPARAM)dat->szProto);
+ if (dat->hwndContactPic)
+ SendMessage(dat->hwndContactPic, AVATAR_SETCONTACT, (WPARAM)0, (LPARAM)dat->hContact);
+
+ }
+ return dat->showPic;
+}
+
+/*
+ * checks, if there is a valid smileypack installed for the given protocol
+ */
+
+int TSAPI CheckValidSmileyPack(const char *szProto, HANDLE hContact)
+{
+ SMADD_INFO2 smainfo = {0};
+
+ if (PluginConfig.g_SmileyAddAvail) {
+ smainfo.cbSize = sizeof(smainfo);
+ smainfo.Protocolname = const_cast<char *>(szProto);
+ smainfo.hContact = hContact;
+ CallService(MS_SMILEYADD_GETINFO2, 0, (LPARAM)&smainfo);
+ if(smainfo.ButtonIcon)
+ DestroyIcon(smainfo.ButtonIcon);
+ return smainfo.NumberOfVisibleSmileys;
+ } else
+ return 0;
+}
+
+/*
+ * return value MUST be free()'d by caller.
+ */
+
+TCHAR* TSAPI QuoteText(const TCHAR *text, int charsPerLine, int removeExistingQuotes)
+{
+ int inChar, outChar, lineChar;
+ int justDoneLineBreak, bufSize;
+ TCHAR *strout;
+
+ bufSize = lstrlenW(text) + 23;
+ strout = (TCHAR*)malloc(bufSize * sizeof(TCHAR));
+ inChar = 0;
+ justDoneLineBreak = 1;
+ for (outChar = 0, lineChar = 0;text[inChar];) {
+ if (outChar >= bufSize - 8) {
+ bufSize += 20;
+ strout = (TCHAR *)realloc(strout, bufSize * sizeof(TCHAR));
+ }
+ if (justDoneLineBreak && text[inChar] != '\r' && text[inChar] != '\n') {
+ if (removeExistingQuotes)
+ if (text[inChar] == '>') {
+ while (text[++inChar] != '\n');
+ inChar++;
+ continue;
+ }
+ strout[outChar++] = '>';
+ strout[outChar++] = ' ';
+ lineChar = 2;
+ }
+ if (lineChar == charsPerLine && text[inChar] != '\r' && text[inChar] != '\n') {
+ int decreasedBy;
+ for (decreasedBy = 0;lineChar > 10;lineChar--, inChar--, outChar--, decreasedBy++)
+ if (strout[outChar] == ' ' || strout[outChar] == '\t' || strout[outChar] == '-') break;
+ if (lineChar <= 10) {
+ lineChar += decreasedBy;
+ inChar += decreasedBy;
+ outChar += decreasedBy;
+ } else inChar++;
+ strout[outChar++] = '\r';
+ strout[outChar++] = '\n';
+ justDoneLineBreak = 1;
+ continue;
+ }
+ strout[outChar++] = text[inChar];
+ lineChar++;
+ if (text[inChar] == '\n' || text[inChar] == '\r') {
+ if (text[inChar] == '\r' && text[inChar+1] != '\n')
+ strout[outChar++] = '\n';
+ justDoneLineBreak = 1;
+ lineChar = 0;
+ } else justDoneLineBreak = 0;
+ inChar++;
+ }
+ strout[outChar++] = '\r';
+ strout[outChar++] = '\n';
+ strout[outChar] = 0;
+ return strout;
+}
+
+
+void TSAPI AdjustBottomAvatarDisplay(TWindowData *dat)
+{
+ if(dat) {
+ bool fInfoPanel = dat->Panel->isActive();
+ HBITMAP hbm = (fInfoPanel && dat->pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown);
+ HWND hwndDlg = dat->hwnd;
+
+ if (PluginConfig.g_FlashAvatarAvail) {
+ FLASHAVATAR fa = {0};
+
+ fa.hContact = dat->hContact;
+ fa.hWindow = 0;
+ fa.id = 25367;
+ fa.cProto = dat->szProto;
+ CallService(MS_FAVATAR_GETINFO, (WPARAM)&fa, 0);
+ if (fa.hWindow) {
+ dat->hwndFlash = fa.hWindow;
+ SetParent(dat->hwndFlash, fInfoPanel ? dat->hwndPanelPicParent : GetDlgItem(dat->hwnd, IDC_CONTACTPIC));
+ }
+ fa.hContact = 0;
+ fa.hWindow = 0;
+ if (fInfoPanel) {
+ fa.hParentWindow = GetDlgItem(hwndDlg, IDC_CONTACTPIC);
+ CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
+ if (fa.hWindow) {
+ SetParent(fa.hWindow, GetDlgItem(hwndDlg, IDC_CONTACTPIC));
+ ShowWindow(fa.hWindow, SW_SHOW);
+ }
+ } else {
+ CallService(MS_FAVATAR_GETINFO, (WPARAM)&fa, 0);
+ if (fa.hWindow)
+ ShowWindow(fa.hWindow, SW_HIDE);
+ }
+ }
+
+ if (hbm) {
+ dat->showPic = GetAvatarVisibility(hwndDlg, dat);
+ if (dat->dynaSplitter == 0 || dat->splitterY == 0)
+ LoadSplitter(dat);
+ dat->dynaSplitter = dat->splitterY - DPISCALEY_S(34);
+ DM_RecalcPictureSize(dat);
+ Utils::showDlgControl(hwndDlg, IDC_CONTACTPIC, dat->showPic ? SW_SHOW : SW_HIDE);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_CONTACTPIC), NULL, TRUE);
+ } else {
+ dat->showPic = GetAvatarVisibility(hwndDlg, dat);
+ Utils::showDlgControl(hwndDlg, IDC_CONTACTPIC, dat->showPic ? SW_SHOW : SW_HIDE);
+ dat->pic.cy = dat->pic.cx = DPISCALEY_S(60);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_CONTACTPIC), NULL, TRUE);
+ }
+ }
+}
+
+void TSAPI ShowPicture(TWindowData *dat, BOOL showNewPic)
+{
+ DBVARIANT dbv = {0};
+ RECT rc;
+ HWND hwndDlg = dat->hwnd;
+
+ if (!dat->Panel->isActive())
+ dat->pic.cy = dat->pic.cx = DPISCALEY_S(60);
+
+ if (showNewPic) {
+ if (dat->Panel->isActive() && dat->pContainer->avatarMode != 3) {
+ if (!dat->hwndPanelPic) {
+ dat->panelWidth = -1;
+ InvalidateRect(dat->hwnd, NULL, TRUE);
+ UpdateWindow(dat->hwnd);
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+ return;
+ }
+ AdjustBottomAvatarDisplay(dat);
+ } else {
+ dat->showPic = dat->showPic ? 0 : 1;
+ DBWriteContactSettingByte(dat->hContact, SRMSGMOD_T, "MOD_ShowPic", (BYTE)dat->showPic);
+ }
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_CONTACTPIC), &rc);
+ if (dat->minEditBoxSize.cy + DPISCALEY_S(3)> dat->splitterY)
+ SendMessage(hwndDlg, DM_SPLITTERMOVED, (WPARAM)rc.bottom - dat->minEditBoxSize.cy, (LPARAM)GetDlgItem(hwndDlg, IDC_SPLITTER));
+ if (!showNewPic)
+ SetDialogToType(hwndDlg);
+ else
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+
+}
+
+void TSAPI FlashOnClist(HWND hwndDlg, struct TWindowData *dat, HANDLE hEvent, DBEVENTINFO *dbei)
+{
+ CLISTEVENT cle;
+
+ dat->dwTickLastEvent = GetTickCount();
+
+ if ((GetForegroundWindow() != dat->pContainer->hwnd || dat->pContainer->hwndActive != hwndDlg) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) {
+ dat->dwUnread++;
+ UpdateTrayMenu(dat, (WORD)(dat->cache->getActiveStatus()), dat->cache->getActiveProto(), dat->szStatus, dat->hContact, 0L);
+ if (nen_options.bTraySupport)
+ return;
+ }
+ if (hEvent == 0)
+ return;
+
+ if (!PluginConfig.m_FlashOnClist)
+ return;
+ if ((GetForegroundWindow() != dat->pContainer->hwnd || dat->pContainer->hwndActive != hwndDlg) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !(dat->dwFlagsEx & MWF_SHOW_FLASHCLIST)) {
+ ZeroMemory(&cle, sizeof(cle));
+ cle.cbSize = sizeof(cle);
+ cle.hContact = (HANDLE) dat->hContact;
+ cle.hDbEvent = hEvent;
+ cle.hIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = "SRMsg/ReadMessage";
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM) & cle);
+ dat->dwFlagsEx |= MWF_SHOW_FLASHCLIST;
+ dat->hFlashingEvent = hEvent;
+ }
+}
+
+/*
+ * callback function for text streaming
+ */
+
+static DWORD CALLBACK Message_StreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
+{
+ static DWORD dwRead;
+ char ** ppText = (char **) dwCookie;
+
+ if (*ppText == NULL) {
+ *ppText = (char *)malloc(cb + 2);
+ CopyMemory(*ppText, pbBuff, cb);
+ *pcb = cb;
+ dwRead = cb;
+ *(*ppText + cb) = '\0';
+ } else {
+ char *p = (char *)realloc(*ppText, dwRead + cb + 2);
+ CopyMemory(p + dwRead, pbBuff, cb);
+ *ppText = p;
+ *pcb = cb;
+ dwRead += cb;
+ *(*ppText + dwRead) = '\0';
+ }
+ return 0;
+}
+
+/*
+ * retrieve contents of the richedit control by streaming. Used to get the
+ * typed message before sending it.
+ * caller must free the returned pointer.
+ * UNICODE version returns UTF-8 encoded string.
+ */
+
+char* TSAPI Message_GetFromStream(HWND hwndRtf, const TWindowData* dat, DWORD dwPassedFlags)
+{
+ EDITSTREAM stream;
+ char *pszText = NULL;
+ DWORD dwFlags = 0;
+
+ if (hwndRtf == 0 || dat == 0)
+ return NULL;
+
+ ZeroMemory(&stream, sizeof(stream));
+ stream.pfnCallback = Message_StreamCallback;
+ stream.dwCookie = (DWORD_PTR) & pszText; // pass pointer to pointer
+ if (dwPassedFlags == 0)
+ dwFlags = (CP_UTF8 << 16) | (SF_RTFNOOBJS | SFF_PLAINRTF | SF_USECODEPAGE);
+ else
+ dwFlags = (CP_UTF8 << 16) | dwPassedFlags;
+ SendMessage(hwndRtf, EM_STREAMOUT, (WPARAM)dwFlags, (LPARAM) & stream);
+
+ return pszText; // pszText contains the text
+}
+
+/*
+ * convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting
+ * tags and return plain text
+ */
+
+BOOL TSAPI DoRtfToTags(TCHAR * pszText, const TWindowData *dat)
+{
+ TCHAR * p1;
+ BOOL bJustRemovedRTF = TRUE;
+ BOOL bTextHasStarted = FALSE;
+ LOGFONTA lf;
+ COLORREF color;
+ static int inColor = 0;
+
+ if (!pszText)
+ return FALSE;
+
+ /*
+ * used to filter out attributes which are already set for the default message input area
+ * font
+ */
+
+ lf = dat->pContainer->theme.logFonts[MSGFONTID_MESSAGEAREA];
+ color = dat->pContainer->theme.fontColors[MSGFONTID_MESSAGEAREA];
+
+ // create an index of colors in the module and map them to
+ // corresponding colors in the RTF color table
+
+ Utils::CreateColorMap(pszText);
+ // scan the file for rtf commands and remove or parse them
+ inColor = 0;
+ p1 = _tcsstr(pszText, _T("\\pard"));
+ if (p1) {
+ size_t iRemoveChars;
+ TCHAR InsertThis[50];
+ p1 += 5;
+
+ MoveMemory(pszText, p1, (lstrlen(p1) + 1) * sizeof(TCHAR));
+ p1 = pszText;
+ // iterate through all characters, if rtf control character found then take action
+ while (*p1 != (TCHAR) '\0') {
+ _sntprintf(InsertThis, 50, _T(""));
+ iRemoveChars = 0;
+
+ switch (*p1) {
+ case (TCHAR) '\\':
+ if (p1 == _tcsstr(p1, _T("\\cf"))) { // foreground color
+ TCHAR szTemp[20];
+ int iCol = _ttoi(p1 + 3);
+ int iInd = Utils::RTFColorToIndex(iCol);
+ bJustRemovedRTF = TRUE;
+
+ _sntprintf(szTemp, 20, _T("%d"), iCol);
+ iRemoveChars = 3 + lstrlen(szTemp);
+ if (bTextHasStarted || iCol)
+ _sntprintf(InsertThis, sizeof(InsertThis) / sizeof(TCHAR), (iInd > 0) ? (inColor ? _T("[/color][color=%s]") : _T("[color=%s]")) : (inColor ? _T("[/color]") : _T("")), Utils::rtf_ctable[iInd - 1].szName);
+ inColor = iInd > 0 ? 1 : 0;
+ } else if (p1 == _tcsstr(p1, _T("\\highlight"))) { //background color
+ TCHAR szTemp[20];
+ int iCol = _ttoi(p1 + 10);
+ bJustRemovedRTF = TRUE;
+
+ _sntprintf(szTemp, 20, _T("%d"), iCol);
+ iRemoveChars = 10 + lstrlen(szTemp);
+ } else if (p1 == _tcsstr(p1, _T("\\par"))) { // newline
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 4;
+ } else if (p1 == _tcsstr(p1, _T("\\line"))) { // soft line break;
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 5;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("\n"));
+ } else if (p1 == _tcsstr(p1, _T("\\endash"))) {
+ bTextHasStarted = bJustRemovedRTF = TRUE;
+ iRemoveChars = 7;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x2013);
+ } else if (p1 == _tcsstr(p1, _T("\\emdash"))) {
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 7;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x2014);
+ } else if (p1 == _tcsstr(p1, _T("\\bullet"))) {
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 7;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x2022);
+ } else if (p1 == _tcsstr(p1, _T("\\ldblquote"))) {
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 10;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x201C);
+ } else if (p1 == _tcsstr(p1, _T("\\rdblquote"))) {
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 10;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x201D);
+ } else if (p1 == _tcsstr(p1, _T("\\lquote"))) {
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 7;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x2018);
+ } else if (p1 == _tcsstr(p1, _T("\\rquote"))) {
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 7;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x2019);
+ } else if (p1 == _tcsstr(p1, _T("\\b"))) { //bold
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = (p1[2] != (TCHAR) '0') ? 2 : 3;
+ if (!(lf.lfWeight == FW_BOLD)) { // only allow bold if the font itself isn't a bold one, otherwise just strip it..
+ if (dat->SendFormat)
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), (p1[2] != (TCHAR) '0') ? _T("[b]") : _T("[/b]"));
+ }
+
+ } else if (p1 == _tcsstr(p1, _T("\\i"))) { // italics
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = (p1[2] != (TCHAR) '0') ? 2 : 3;
+ if (!lf.lfItalic) { // same as for bold
+ if (dat->SendFormat)
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), (p1[2] != (TCHAR) '0') ? _T("[i]") : _T("[/i]"));
+ }
+
+ } else if (p1 == _tcsstr(p1, _T("\\strike"))) { // strike-out
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = (p1[7] != (TCHAR) '0') ? 7 : 8;
+ if (!lf.lfStrikeOut) { // same as for bold
+ if (dat->SendFormat)
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), (p1[7] != (TCHAR) '0') ? _T("[s]") : _T("[/s]"));
+ }
+
+ } else if (p1 == _tcsstr(p1, _T("\\ul"))) { // underlined
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ if (p1[3] == (TCHAR) 'n')
+ iRemoveChars = 7;
+ else if (p1[3] == (TCHAR) '0')
+ iRemoveChars = 4;
+ else
+ iRemoveChars = 3;
+ if (!lf.lfUnderline) { // same as for bold
+ if (dat->SendFormat)
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), (p1[3] != (TCHAR) '0' && p1[3] != (TCHAR) 'n') ? _T("[u]") : _T("[/u]"));
+ }
+
+ } else if (p1 == _tcsstr(p1, _T("\\tab"))) { // tab
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = TRUE;
+ iRemoveChars = 4;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), 0x09);
+ } else if (p1[1] == (TCHAR) '\\' || p1[1] == (TCHAR) '{' || p1[1] == (TCHAR) '}') { // escaped characters
+ bTextHasStarted = TRUE;
+ //bJustRemovedRTF = TRUE;
+ iRemoveChars = 2;
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), p1[1]);
+ } else if (p1[1] == (TCHAR) '\'') { // special character
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = FALSE;
+ if (p1[2] != (TCHAR) ' ' && p1[2] != (TCHAR) '\\') {
+ int iLame = 0;
+ TCHAR * p3;
+ TCHAR *stoppedHere;
+
+ if (p1[3] != (TCHAR) ' ' && p1[3] != (TCHAR) '\\') {
+ _tcsncpy(InsertThis, p1 + 2, 3);
+ iRemoveChars = 4;
+ InsertThis[2] = 0;
+ } else {
+ _tcsncpy(InsertThis, p1 + 2, 2);
+ iRemoveChars = 3;
+ InsertThis[2] = 0;
+ }
+ // convert string containing char in hex format to int.
+ p3 = InsertThis;
+ iLame = _tcstol(p3, &stoppedHere, 16);
+ _sntprintf(InsertThis, safe_sizeof(InsertThis), _T("%c"), (TCHAR) iLame);
+
+ } else
+ iRemoveChars = 2;
+ } else { // remove unknown RTF command
+ int j = 1;
+ bJustRemovedRTF = TRUE;
+ while (!_tcschr(_T(" !$%()#*\"'"), p1[j]) && p1[j] != (TCHAR) '§' && p1[j] != (TCHAR) '\\' && p1[j] != (TCHAR) '\0')
+ // while(!_tcschr(_T(" !§$%&()#*"), p1[j]) && p1[j] != (TCHAR)'\\' && p1[j] != (TCHAR)'\0')
+ j++;
+ // while(p1[j] != (TCHAR)' ' && p1[j] != (TCHAR)'\\' && p1[j] != (TCHAR)'\0')
+ // j++;
+ iRemoveChars = j;
+ }
+ break;
+
+ case (TCHAR) '{': // other RTF control characters
+ case (TCHAR) '}':
+ iRemoveChars = 1;
+ break;
+
+ case (TCHAR) ' ': // remove spaces following a RTF command
+ if (bJustRemovedRTF)
+ iRemoveChars = 1;
+ bJustRemovedRTF = FALSE;
+ bTextHasStarted = TRUE;
+ break;
+
+ default: // other text that should not be touched
+ bTextHasStarted = TRUE;
+ bJustRemovedRTF = FALSE;
+
+ break;
+ }
+
+ // move the memory and paste in new commands instead of the old RTF
+ if (lstrlen(InsertThis) || iRemoveChars) {
+ MoveMemory(p1 + lstrlen(InsertThis), p1 + iRemoveChars, (lstrlen(p1) - iRemoveChars + 1) * sizeof(TCHAR));
+ CopyMemory(p1, InsertThis, lstrlen(InsertThis) * sizeof(TCHAR));
+ p1 += lstrlen(InsertThis);
+ } else
+ p1++;
+ }
+
+ } else {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * trims the output from DoRtfToTags(), removes trailing newlines and whitespaces...
+ */
+
+void TSAPI DoTrimMessage(TCHAR *msg)
+{
+ size_t iLen = lstrlen(msg);
+ size_t i = iLen;
+
+ while (i && (msg[i-1] == '\r' || msg[i-1] == '\n') || msg[i-1] == ' ') {
+ i--;
+ }
+ if (i < iLen)
+ msg[i] = '\0';
+}
+
+/*
+ * retrieve both buddys and my own UIN for a message session and store them in the message window *dat
+ * respects metacontacts and uses the current protocol if the contact is a MC
+ */
+
+void TSAPI GetMYUIN(TWindowData *dat)
+{
+ CONTACTINFO ci;
+ ZeroMemory((void *)&ci, sizeof(ci));
+
+ /*
+ * get my uin
+ */
+
+ ci.cbSize = sizeof(ci);
+ ci.hContact = 0;
+ ci.szProto = const_cast<char *>(dat->cache->getActiveProto());
+ ci.dwFlag = CNF_TCHAR | CNF_DISPLAYUID;
+
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM) & ci)) {
+ switch (ci.type) {
+ case CNFT_ASCIIZ:
+ mir_sntprintf(dat->myUin, safe_sizeof(dat->myUin), _T("%s"), reinterpret_cast<TCHAR *>(ci.pszVal));
+ mir_free((void *)ci.pszVal);
+ break;
+ case CNFT_DWORD:
+ mir_sntprintf(dat->myUin, safe_sizeof(dat->myUin), _T("%u"), ci.dVal);
+ break;
+ default:
+ dat->myUin[0] = 0;
+ break;
+ }
+ } else
+ dat->myUin[0] = 0;
+}
+
+static int g_IEViewAvail = -1;
+static int g_HPPAvail = -1;
+
+UINT TSAPI GetIEViewMode(HWND hwndDlg, HANDLE hContact)
+{
+ int iWantIEView = 0, iWantHPP = 0;
+
+ if (g_IEViewAvail == -1)
+ g_IEViewAvail = ServiceExists(MS_IEVIEW_WINDOW);
+
+ if (g_HPPAvail == -1)
+ g_HPPAvail = ServiceExists("History++/ExtGrid/NewWindow");
+
+ PluginConfig.g_WantIEView = g_IEViewAvail && M->GetByte("default_ieview", 0);
+ PluginConfig.g_WantHPP = g_HPPAvail && M->GetByte("default_hpp", 0);
+
+ iWantIEView = (PluginConfig.g_WantIEView) || (M->GetByte(hContact, "ieview", 0) == 1 && g_IEViewAvail);
+ iWantIEView = (M->GetByte(hContact, "ieview", 0) == (BYTE) - 1) ? 0 : iWantIEView;
+
+ iWantHPP = (PluginConfig.g_WantHPP) || (M->GetByte(hContact, "hpplog", 0) == 1 && g_HPPAvail);
+ iWantHPP = (M->GetByte(hContact, "hpplog", 0) == (BYTE) - 1) ? 0 : iWantHPP;
+
+ return iWantHPP ? WANT_HPP_LOG : (iWantIEView ? WANT_IEVIEW_LOG : 0);
+}
+
+#if defined(__FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER)
+static void CheckAndDestroyHPP(struct TWindowData *dat)
+{
+ if (dat->hwndHPP) {
+ IEVIEWWINDOW ieWindow;
+ ieWindow.cbSize = sizeof(IEVIEWWINDOW);
+ ieWindow.iType = IEW_DESTROY;
+ ieWindow.hwnd = dat->hwndHPP;
+ if(dat->oldIEViewProc) {
+ SetWindowLongPtr(dat->hwndHPP, GWLP_WNDPROC, (LONG_PTR)dat->oldIEViewProc);
+ dat->oldIEViewProc = 0;
+ }
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ dat->hwndHPP = 0;
+ }
+}
+
+void TSAPI CheckAndDestroyIEView(TWindowData *dat)
+{
+ if (dat->hwndIEView) {
+ IEVIEWWINDOW ieWindow;
+ ieWindow.cbSize = sizeof(IEVIEWWINDOW);
+ ieWindow.iType = IEW_DESTROY;
+ ieWindow.hwnd = dat->hwndIEView;
+ if (dat->oldIEViewProc){
+ SetWindowLongPtr(dat->hwndIEView, GWLP_WNDPROC, (LONG_PTR)dat->oldIEViewProc);
+ dat->oldIEViewProc =0;
+ }
+ CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&ieWindow);
+ dat->oldIEViewProc = 0;
+ dat->hwndIEView = 0;
+ }
+}
+#endif
+
+void TSAPI SetMessageLog(TWindowData *dat)
+{
+ HWND hwndDlg = dat->hwnd;
+ unsigned int iLogMode = GetIEViewMode(hwndDlg, dat->hContact);
+
+ if (iLogMode == WANT_IEVIEW_LOG && dat->hwndIEView == 0) {
+ IEVIEWWINDOW ieWindow;
+
+ ZeroMemory(&ieWindow, sizeof(ieWindow));
+#if defined(__FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER)
+ CheckAndDestroyHPP(dat);
+#endif
+ ieWindow.cbSize = sizeof(IEVIEWWINDOW);
+ ieWindow.iType = IEW_CREATE;
+ ieWindow.dwFlags = 0;
+ ieWindow.dwMode = IEWM_TABSRMM;
+ ieWindow.parent = hwndDlg;
+ ieWindow.x = 0;
+ ieWindow.y = 0;
+ ieWindow.cx = 200;
+ ieWindow.cy = 200;
+ CallService(MS_IEVIEW_WINDOW, 0, (LPARAM)&ieWindow);
+ dat->hwndIEView = ieWindow.hwnd;
+ Utils::showDlgControl(hwndDlg, IDC_LOG, SW_HIDE);
+ Utils::enableDlgControl(hwndDlg, IDC_LOG, FALSE);
+ } else if (iLogMode == WANT_HPP_LOG && dat->hwndHPP == 0) {
+ IEVIEWWINDOW ieWindow;
+#if defined(__FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER)
+ CheckAndDestroyIEView(dat);
+#endif
+ ieWindow.cbSize = sizeof(IEVIEWWINDOW);
+ ieWindow.iType = IEW_CREATE;
+ ieWindow.dwFlags = 0;
+ ieWindow.dwMode = IEWM_TABSRMM;
+ ieWindow.parent = hwndDlg;
+ ieWindow.x = 0;
+ ieWindow.y = 0;
+ ieWindow.cx = 10;
+ ieWindow.cy = 10;
+ CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow);
+ dat->hwndHPP = ieWindow.hwnd;
+ Utils::showDlgControl(hwndDlg, IDC_LOG, SW_HIDE);
+ Utils::enableDlgControl(hwndDlg, IDC_LOG, FALSE);
+ } else {
+#if defined(__FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER)
+ if (iLogMode != WANT_IEVIEW_LOG)
+ CheckAndDestroyIEView(dat);
+ if (iLogMode != WANT_HPP_LOG)
+ CheckAndDestroyHPP(dat);
+ Utils::showDlgControl(hwndDlg, IDC_LOG, SW_SHOW);
+ Utils::enableDlgControl(hwndDlg, IDC_LOG, TRUE);
+ dat->hwndIEView = 0;
+ dat->hwndIWebBrowserControl = 0;
+ dat->hwndHPP = 0;
+#endif
+ }
+}
+
+#if defined(__FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER)
+void TSAPI SwitchMessageLog(TWindowData *dat, int iMode)
+{
+ HWND hwndDlg = dat->hwnd;
+
+ if (iMode) { // switch from rtf to IEview or hpp
+ SetDlgItemText(hwndDlg, IDC_LOG, _T(""));
+ Utils::enableDlgControl(hwndDlg, IDC_LOG, FALSE);
+ Utils::showDlgControl(hwndDlg, IDC_LOG, SW_HIDE);
+ SetMessageLog(dat);
+ } else // switch from IEView or hpp to rtf
+ SetMessageLog(dat);
+
+ SetDialogToType(hwndDlg);
+ SendMessage(hwndDlg, DM_REMAKELOG, 0, 0);
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+
+ if (dat->hwndIEView) {
+ if (M->GetByte("subclassIEView", 0)&&dat->oldIEViewProc == 0) {
+ WNDPROC wndProc = (WNDPROC)SetWindowLongPtr(dat->hwndIEView, GWLP_WNDPROC, (LONG_PTR)IEViewSubclassProc);
+ dat->oldIEViewProc = wndProc;
+ }
+ } else if (dat->hwndHPP) {
+ if (dat->oldIEViewProc == 0) {
+ WNDPROC wndProc = (WNDPROC)SetWindowLongPtr(dat->hwndHPP, GWLP_WNDPROC, (LONG_PTR)HPPKFSubclassProc);
+ dat->oldIEViewProc = wndProc;
+ }
+ }
+}
+#endif
+
+void TSAPI FindFirstEvent(TWindowData *dat)
+{
+ int historyMode = (int)M->GetByte(dat->hContact, SRMSGMOD, SRMSGSET_LOADHISTORY, -1);
+ if (historyMode == -1)
+ historyMode = (int)M->GetByte(SRMSGMOD, SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY);
+
+ dat->hDbEventFirst = (HANDLE) CallService(MS_DB_EVENT_FINDFIRSTUNREAD, (WPARAM) dat->hContact, 0);
+
+ if(dat->bActualHistory)
+ historyMode = LOADHISTORY_COUNT;
+
+ switch (historyMode) {
+ case LOADHISTORY_COUNT: {
+ int i;
+ HANDLE hPrevEvent;
+ DBEVENTINFO dbei = { 0};
+ dbei.cbSize = sizeof(dbei);
+ //MAD: ability to load only current session's history
+ if (dat->bActualHistory)
+ i = dat->cache->getSessionMsgCount();
+ else
+ i = DBGetContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT);
+ //
+ for (; i > 0; i--) {
+ if (dat->hDbEventFirst == NULL)
+ hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) dat->hContact, 0);
+ else
+ hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) dat->hDbEventFirst, 0);
+ if (hPrevEvent == NULL)
+ break;
+ dbei.cbBlob = 0;
+ dat->hDbEventFirst = hPrevEvent;
+ CallService(MS_DB_EVENT_GET, (WPARAM) dat->hDbEventFirst, (LPARAM) & dbei);
+ if (!DbEventIsShown(dat, &dbei))
+ i++;
+ }
+ break;
+ }
+ case LOADHISTORY_TIME: {
+ HANDLE hPrevEvent;
+ DBEVENTINFO dbei = { 0};
+ DWORD firstTime;
+
+ dbei.cbSize = sizeof(dbei);
+ if (dat->hDbEventFirst == NULL)
+ dbei.timestamp = time(NULL);
+ else
+ CallService(MS_DB_EVENT_GET, (WPARAM) dat->hDbEventFirst, (LPARAM) & dbei);
+ firstTime = dbei.timestamp - 60 * DBGetContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME);
+ for (;;) {
+ if (dat->hDbEventFirst == NULL)
+ hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDLAST, (WPARAM) dat->hContact, 0);
+ else
+ hPrevEvent = (HANDLE) CallService(MS_DB_EVENT_FINDPREV, (WPARAM) dat->hDbEventFirst, 0);
+ if (hPrevEvent == NULL)
+ break;
+ dbei.cbBlob = 0;
+ CallService(MS_DB_EVENT_GET, (WPARAM) hPrevEvent, (LPARAM) & dbei);
+ if (dbei.timestamp < firstTime)
+ break;
+ dat->hDbEventFirst = hPrevEvent;
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+void TSAPI SaveSplitter(TWindowData *dat)
+{
+ /*
+ * group chats save their normal splitter position independently
+ */
+
+ if(dat->bType == SESSIONTYPE_CHAT)
+ return;
+
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ if(dat->fIsAutosizingInput)
+ return;
+#endif
+ if (dat->splitterY < DPISCALEY_S(MINSPLITTERY) || dat->splitterY < 0)
+ dat->splitterY = DPISCALEY_S(MINSPLITTERY);
+
+ if (dat->dwFlagsEx & MWF_SHOW_SPLITTEROVERRIDE)
+ M->WriteDword(dat->hContact, SRMSGMOD_T, "splitsplity", dat->splitterY);
+ else {
+ if(dat->pContainer->settings->fPrivate)
+ dat->pContainer->settings->splitterPos = dat->splitterY;
+ else
+ M->WriteDword(SRMSGMOD_T, "splitsplity", dat->splitterY);
+ }
+}
+
+void TSAPI LoadSplitter(TWindowData *dat)
+{
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ if (dat->fIsAutosizingInput) {
+ dat->splitterY = GetDefaultMinimumInputHeight(dat);
+ return;
+ }
+#endif
+ if (!(dat->dwFlagsEx & MWF_SHOW_SPLITTEROVERRIDE))
+ if(!dat->pContainer->settings->fPrivate)
+ dat->splitterY = (int)M->GetDword("splitsplity", (DWORD) 60);
+ else
+ dat->splitterY = dat->pContainer->settings->splitterPos;
+ else
+ dat->splitterY = (int)M->GetDword(dat->hContact, "splitsplity", M->GetDword("splitsplity", (DWORD) 70));
+
+ if (dat->splitterY < MINSPLITTERY || dat->splitterY < 0)
+ dat->splitterY = 150;
+}
+
+void TSAPI PlayIncomingSound(const TWindowData *dat)
+{
+ int iPlay = Utils::mustPlaySound(dat);
+
+ if (iPlay) {
+ if (GetForegroundWindow() == dat->pContainer->hwnd && dat->pContainer->hwndActive == dat->hwnd)
+ SkinPlaySound("RecvMsgActive");
+ else
+ SkinPlaySound("RecvMsgInactive");
+ }
+}
+
+/*
+ * reads send format and configures the toolbar buttons
+ * if mode == 0, int only configures the buttons and does not change send format
+ */
+
+void TSAPI GetSendFormat(TWindowData *dat, int mode)
+{
+ UINT controls[5] = {IDC_FONTBOLD, IDC_FONTITALIC, IDC_FONTUNDERLINE,IDC_FONTSTRIKEOUT, IDC_FONTFACE};
+ int i;
+
+ if (mode) {
+ dat->SendFormat = M->GetDword(dat->hContact, "sendformat", PluginConfig.m_SendFormat);
+ if (dat->SendFormat == -1) // per contact override to disable it..
+ dat->SendFormat = 0;
+ else if (dat->SendFormat == 0)
+ dat->SendFormat = PluginConfig.m_SendFormat ? 1 : 0;
+ }
+ for (i = 0; i < 5; i++)
+ Utils::enableDlgControl(dat->hwnd, controls[i], dat->SendFormat != 0 ? TRUE : FALSE);
+ return;
+}
+
+/*
+ * get user-readable locale information for the currently selected
+ * keyboard layout.
+ *
+ * GetLocaleInfo() should no longer be used on Vista and later
+ */
+
+void TSAPI GetLocaleID(TWindowData *dat, const TCHAR *szKLName)
+{
+ TCHAR szLI[256], *stopped = NULL;
+ USHORT langID;
+ WORD wCtype2[3];
+ PARAFORMAT2 pf2;
+ BOOL fLocaleNotSet;
+ char szTest[4] = {(char)0xe4, (char)0xf6, (char)0xfc, 0 };
+
+ szLI[0] = szLI[1] = 0;
+
+ ZeroMemory(&pf2, sizeof(PARAFORMAT2));
+ langID = (USHORT)_tcstol(szKLName, &stopped, 16);
+ dat->lcid = MAKELCID(langID, 0);
+ /*
+ * Vista+: read ISO locale names from the registry
+ */
+ if(PluginConfig.m_bIsVista) {
+ HKEY hKey = 0;
+ TCHAR szKey[20];
+ DWORD dwLID = _tcstoul(szKLName, &stopped, 16);
+
+ mir_sntprintf(szKey, 20, _T("%04.04x"), LOWORD(dwLID));
+ if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, _T("MIME\\Database\\Rfc1766"), 0, KEY_READ, &hKey)) {
+ DWORD dwLength = 255;
+ if(ERROR_SUCCESS == RegQueryValueEx(hKey, szKey, 0, 0, (unsigned char *)szLI, &dwLength)) {
+ TCHAR* p;
+
+ szLI[255] = 0;
+ if((p = _tcschr(szLI, ';')) != 0)
+ *p = 0;
+ }
+ RegCloseKey(hKey);
+ }
+ szLI[0] = _totupper(szLI[0]);
+ szLI[1] = _totupper(szLI[1]);
+ }
+ else {
+ GetLocaleInfo(dat->lcid, LOCALE_SISO639LANGNAME, szLI, 10);
+ _tcsupr(szLI);
+ }
+ fLocaleNotSet = (dat->lcID[0] == 0 && dat->lcID[1] == 0);
+ mir_sntprintf(dat->lcID, safe_sizeof(dat->lcID), szLI);
+ GetStringTypeA(dat->lcid, CT_CTYPE2, szTest, 3, wCtype2);
+ pf2.cbSize = sizeof(pf2);
+ pf2.dwMask = PFM_RTLPARA;
+ SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_GETPARAFORMAT, 0, (LPARAM)&pf2);
+ if (Utils::FindRTLLocale(dat) && fLocaleNotSet) {
+ if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT) {
+ ZeroMemory(&pf2, sizeof(pf2));
+ pf2.dwMask = PFM_RTLPARA;
+ pf2.cbSize = sizeof(pf2);
+ pf2.wEffects = PFE_RTLPARA;
+ SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ } else {
+ ZeroMemory(&pf2, sizeof(pf2));
+ pf2.dwMask = PFM_RTLPARA;
+ pf2.cbSize = sizeof(pf2);
+ pf2.wEffects = 0;
+ SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ }
+ SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETLANGOPTIONS, 0, (LPARAM) SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
+ }
+}
+
+void TSAPI LoadContactAvatar(TWindowData *dat)
+{
+ if(dat)
+ dat->ace = Utils::loadAvatarFromAVS(dat->hContact);
+
+ if (dat && (!(dat->Panel->isActive()) || dat->pContainer->avatarMode == 3)) {
+ BITMAP bm;
+ dat->iRealAvatarHeight = 0;
+
+ if (dat->ace && dat->ace->hbmPic) {
+ AdjustBottomAvatarDisplay(dat);
+ GetObject(dat->ace->hbmPic, sizeof(bm), &bm);
+ CalcDynamicAvatarSize(dat, &bm);
+ PostMessage(dat->hwnd, WM_SIZE, 0, 0);
+ } else if (dat->ace == NULL) {
+ AdjustBottomAvatarDisplay(dat);
+ GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm);
+ CalcDynamicAvatarSize(dat, &bm);
+ PostMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+ }
+}
+
+void TSAPI LoadOwnAvatar(TWindowData *dat)
+{
+ AVATARCACHEENTRY *ace = NULL;
+
+ if (ServiceExists(MS_AV_GETMYAVATAR))
+ ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(dat->cache->getActiveProto()));
+
+ if (ace) {
+ dat->hOwnPic = ace->hbmPic;
+ dat->ownAce = ace;
+ } else {
+ dat->hOwnPic = PluginConfig.g_hbmUnknown;
+ dat->ownAce = NULL;
+ }
+ if (dat->Panel->isActive() && dat->pContainer->avatarMode != 3) {
+ BITMAP bm;
+
+ dat->iRealAvatarHeight = 0;
+ AdjustBottomAvatarDisplay(dat);
+ GetObject(dat->hOwnPic, sizeof(bm), &bm);
+ CalcDynamicAvatarSize(dat, &bm);
+ SendMessage(dat->hwnd, WM_SIZE, 0, 0);
+ }
+}
+
+void TSAPI LoadTimeZone(TWindowData *dat)
+{
+ if (dat)
+ dat->hTimeZone = tmi.createByContact ? tmi.createByContact(dat->hContact, TZF_KNOWNONLY) : 0;
+}
+
+/*
+ * paste contents of the clipboard into the message input area and send it immediately
+ */
+
+void TSAPI HandlePasteAndSend(const TWindowData *dat)
+{
+ UINT ctrlID = dat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE;
+
+ if (!PluginConfig.m_PasteAndSend) {
+ SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, ctrlID, (LPARAM)CTranslator::get(CTranslator::GEN_WARNING_PASTEANDSEND_DISABLED));
+ return; // feature disabled
+ }
+
+ SendMessage(GetDlgItem(dat->hwnd, ctrlID), EM_PASTESPECIAL, CF_TEXTT, 0);
+ if (GetWindowTextLengthA(GetDlgItem(dat->hwnd, ctrlID)) > 0)
+ SendMessage(dat->hwnd, WM_COMMAND, IDOK, 0);
+}
+
+/*
+ * draw various elements of the message window, like avatar(s), info panel fields
+ * and the color formatting menu
+ */
+
+int TSAPI MsgWindowDrawHandler(WPARAM wParam, LPARAM lParam, TWindowData *dat)
+{
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT) lParam;
+
+ if (!dat)
+ return 0;
+
+ bool fAero = M->isAero();
+ HWND hwndDlg = dat->hwnd;
+
+ if (dis->CtlType == ODT_MENU && dis->hwndItem == (HWND)GetSubMenu(PluginConfig.g_hMenuContext, 7)) {
+ RECT rc = { 0 };
+ HBRUSH old, col;
+ COLORREF clr;
+ switch (dis->itemID) {
+ case ID_FONT_RED:
+ clr = RGB(255, 0, 0);
+ break;
+ case ID_FONT_BLUE:
+ clr = RGB(0, 0, 255);
+ break;
+ case ID_FONT_GREEN:
+ clr = RGB(0, 255, 0);
+ break;
+ case ID_FONT_MAGENTA:
+ clr = RGB(255, 0, 255);
+ break;
+ case ID_FONT_YELLOW:
+ clr = RGB(255, 255, 0);
+ break;
+ case ID_FONT_WHITE:
+ clr = RGB(255, 255, 255);
+ break;
+ case ID_FONT_DEFAULTCOLOR:
+ clr = GetSysColor(COLOR_MENU);
+ break;
+ case ID_FONT_CYAN:
+ clr = RGB(0, 255, 255);
+ break;
+ case ID_FONT_BLACK:
+ clr = RGB(0, 0, 0);
+ break;
+ default:
+ clr = 0;
+ }
+ col = (HBRUSH)CreateSolidBrush(clr);
+ old = (HBRUSH)SelectObject(dis->hDC, col);
+ rc.left = 2;
+ rc.top = dis->rcItem.top - 5;
+ rc.right = 15;
+ rc.bottom = dis->rcItem.bottom + 4;
+ Rectangle(dis->hDC, rc.left - 1, rc.top - 1, rc.right + 1, rc.bottom + 1);
+ FillRect(dis->hDC, &rc, col);
+ SelectObject(dis->hDC, old);
+ DeleteObject(col);
+ return TRUE;
+ }
+ else if ((dis->hwndItem == GetDlgItem(hwndDlg, IDC_CONTACTPIC) && (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown) && dat->showPic) || (dis->hwndItem == hwndDlg && dat->Panel->isActive() && (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown))) {
+ HBRUSH hOldBrush;
+ BITMAP bminfo;
+ double dAspect = 0, dNewWidth = 0, dNewHeight = 0;
+ DWORD iMaxHeight = 0, top, cx, cy;
+ RECT rc, rcClient, rcFrame;
+ HDC hdcDraw;
+ HBITMAP hbmDraw, hbmOld;
+ BOOL bPanelPic = dis->hwndItem == hwndDlg;
+ DWORD aceFlags = 0;
+ HPEN hPenBorder = 0, hPenOld = 0;
+ HRGN clipRgn = 0;
+ int iRad = PluginConfig.m_WinVerMajor >= 5 ? 4 : 6;
+ BOOL flashAvatar = FALSE;
+ bool fInfoPanel = dat->Panel->isActive();
+
+ if (PluginConfig.g_FlashAvatarAvail && (!bPanelPic || (bPanelPic && dat->showInfoPic == 1))) {
+ FLASHAVATAR fa = {0};
+
+ fa.id = 25367;
+ fa.cProto = dat->szProto;
+ if (!bPanelPic && fInfoPanel) {
+ fa.hParentWindow = GetDlgItem(hwndDlg, IDC_CONTACTPIC);
+ CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
+ Utils::enableDlgControl(hwndDlg, IDC_CONTACTPIC, fa.hWindow != 0);
+ } else {
+ fa.hContact = dat->hContact;
+ fa.hParentWindow = fInfoPanel ? dat->hwndPanelPicParent : GetDlgItem(hwndDlg, IDC_CONTACTPIC);
+ CallService(MS_FAVATAR_MAKE, (WPARAM)&fa, 0);
+ if (fInfoPanel) {
+ if (fa.hWindow != NULL&&dat->hwndPanelPic) {
+ DestroyWindow(dat->hwndPanelPic);
+ dat->hwndPanelPic = NULL;
+ }
+ if (!PluginConfig.g_bDisableAniAvatars && fa.hWindow == 0 && dat->hwndPanelPic == 0) {
+ dat->hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, _T(""), WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, dat->hwndPanelPicParent, (HMENU)7000, NULL, NULL);
+ SendMessage(dat->hwndPanelPic, AVATAR_SETCONTACT, (WPARAM)0, (LPARAM)dat->hContact);
+ }
+ } else {
+ if (fa.hWindow != NULL && dat->hwndContactPic) {
+ DestroyWindow(dat->hwndContactPic);
+ dat->hwndContactPic = NULL;
+ }
+ if(!PluginConfig.g_bDisableAniAvatars && fa.hWindow == 0 && dat->hwndContactPic == 0) {
+ dat->hwndContactPic =CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, _T(""), WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(hwndDlg, IDC_CONTACTPIC), (HMENU)0, NULL, NULL);
+ SendMessage(dat->hwndContactPic, AVATAR_SETCONTACT, (WPARAM)0, (LPARAM)dat->hContact);
+ }
+ }
+ dat->hwndFlash = fa.hWindow;
+ }
+ if (fa.hWindow != 0) {
+ bminfo.bmHeight = FAVATAR_HEIGHT;
+ bminfo.bmWidth = FAVATAR_WIDTH;
+ CallService(MS_FAVATAR_SETBKCOLOR, (WPARAM)&fa, (LPARAM)GetSysColor(COLOR_3DFACE));
+ flashAvatar = TRUE;
+ }
+ }
+
+ if (bPanelPic) {
+ if (!flashAvatar) {
+ GetObject(dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown, sizeof(bminfo), &bminfo);
+ }
+ if ((dat->ace && dat->showInfoPic && !(dat->ace->dwFlags & AVS_HIDEONCLIST)) || dat->showInfoPic)
+ aceFlags = dat->ace ? dat->ace->dwFlags : 0;
+ else {
+ if (dat->panelWidth) {
+ dat->panelWidth = -1;
+ if(!CSkin::m_skinEnabled)
+ SendMessage(hwndDlg, WM_SIZE, 0, 0);
+ }
+ return TRUE;
+ }
+ } else {
+ if (!fInfoPanel || dat->pContainer->avatarMode == 3) {
+ if (dat->ace)
+ aceFlags = dat->ace->dwFlags;
+ } else if (dat->ownAce)
+ aceFlags = dat->ownAce->dwFlags;
+
+ GetObject((fInfoPanel && dat->pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown), sizeof(bminfo), &bminfo);
+ }
+
+ GetClientRect(hwndDlg, &rc);
+ if(bPanelPic) {
+ rcClient = dis->rcItem;
+ cx = (rcClient.right - rcClient.left);
+ cy = (rcClient.bottom - rcClient.top) + 1;
+ } else {
+ GetClientRect(dis->hwndItem, &rcClient);
+ cx = rcClient.right;
+ cy = rcClient.bottom;
+ }
+
+ if (cx < 5 || cy < 5)
+ return TRUE;
+
+ if (bPanelPic) {
+ if (bminfo.bmHeight > bminfo.bmWidth) {
+ if (bminfo.bmHeight > 0)
+ dAspect = (double)(cy /*- 2*/) / (double)bminfo.bmHeight;
+ else
+ dAspect = 1.0;
+ dNewWidth = (double)bminfo.bmWidth * dAspect;
+ dNewHeight = cy;// - 2;
+ } else {
+ if (bminfo.bmWidth > 0)
+ dAspect = (double)(cy /*- 2*/) / (double)bminfo.bmWidth;
+ else
+ dAspect = 1.0;
+ dNewHeight = (double)bminfo.bmHeight * dAspect;
+ dNewWidth = cy;// - 2;
+ }
+ if (dat->panelWidth == -1) {
+ dat->panelWidth = (int)dNewWidth;
+ return(0);
+ }
+ } else {
+
+ if (bminfo.bmHeight > 0)
+ dAspect = (double)dat->iRealAvatarHeight / (double)bminfo.bmHeight;
+ else
+ dAspect = 1.0;
+ dNewWidth = (double)bminfo.bmWidth * dAspect;
+ if (dNewWidth > (double)(rc.right) * 0.8)
+ dNewWidth = (double)(rc.right) * 0.8;
+ iMaxHeight = dat->iRealAvatarHeight;
+ }
+
+ if (flashAvatar) {
+ SetWindowPos(dat->hwndPanelPicParent, HWND_TOP, rcClient.left, rcClient.top,
+ (int)dNewWidth, (int)dNewHeight, SWP_SHOWWINDOW | SWP_NOCOPYBITS);
+ return TRUE;
+ }
+
+ hdcDraw = CreateCompatibleDC(dis->hDC);
+ hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy);
+ hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw);
+
+ bool fAero = M->isAero();
+
+ hOldBrush = (HBRUSH)SelectObject(hdcDraw, fAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE));
+ rcFrame = rcClient;
+
+ if (!bPanelPic) {
+ top = (cy - dat->pic.cy) / 2;
+ RECT rcEdge = {0, top, dat->pic.cx, top + dat->pic.cy};
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(dis->hwndItem, dat->pContainer->hwnd, dat->pContainer, &dis->rcItem, hdcDraw);
+ else if (PluginConfig.m_fillColor) {
+ HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor);
+ FillRect(hdcDraw, &rcFrame, br);
+ DeleteObject(br);
+ }
+ else {
+ if(fAero && CSkin::m_pCurrentAeroEffect) {
+ COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh :
+ (CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0);
+
+ HBRUSH br = CreateSolidBrush(clr);
+ FillRect(hdcDraw, &rcFrame, br);
+ DeleteObject(br);
+ }
+ else
+ FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE));
+ }
+
+ hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr);
+ hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder);
+
+ if (CSkin::m_bAvatarBorderType == 1)
+ Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom);
+ else if (CSkin::m_bAvatarBorderType == 2) {
+ clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, iRad, iRad);
+ SelectClipRgn(hdcDraw, clipRgn);
+ }
+ }
+
+ if ((((fInfoPanel && dat->pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown)) && dat->showPic) || bPanelPic) {
+ HDC hdcMem = CreateCompatibleDC(dis->hDC);
+ HBITMAP hbmAvatar = bPanelPic ? (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown) : ((fInfoPanel && dat->pContainer->avatarMode != 3) ? dat->hOwnPic : (dat->ace ? dat->ace->hbmPic : PluginConfig.g_hbmUnknown));
+ HBITMAP hbmMem = 0;
+ if (bPanelPic) {
+ bool bBorder = (CSkin::m_bAvatarBorderType ? true : false);
+
+ LONG height_off = 0;
+ LONG border_off = bBorder ? 1 : 0;
+
+ ResizeBitmap rb;
+ rb.size = sizeof(rb);
+ rb.fit = RESIZEBITMAP_STRETCH;
+ rb.max_height = (int)(dNewHeight - (bBorder ? 2 : 0));
+ rb.max_width = (int)(dNewWidth - (bBorder ? 2 : 0));
+ rb.hBmp = hbmAvatar;
+
+ HBITMAP hbmNew = (HBITMAP)CallService("IMG/ResizeBitmap", (WPARAM)&rb, 0);
+ hbmMem = (HBITMAP)SelectObject(hdcMem, hbmNew);
+
+ rcFrame.left = rcFrame.top = 0;
+ rcFrame.right = (rcClient.right - rcClient.left);
+ rcFrame.bottom = (rcClient.bottom - rcClient.top);
+
+ rcFrame.left = rcFrame.right - (LONG)dNewWidth;
+ rcFrame.bottom = (LONG)dNewHeight;
+
+ height_off = ((cy) - (rb.max_height + (bBorder ? 2 : 0))) / 2;
+ rcFrame.top += height_off;
+ rcFrame.bottom += height_off;
+
+ /*
+ * prepare border drawing (if avatar is rendered by ACC, the parent control will be responsible for
+ * the border, so skip it here)
+ */
+ if(dat->hwndPanelPic == 0) {
+ OffsetRect(&rcClient, -2, 0);
+ if (CSkin::m_bAvatarBorderType == 1)
+ clipRgn = CreateRectRgn(rcClient.left + rcFrame.left, rcClient.top + rcFrame.top, rcClient.left + rcFrame.right,
+ rcClient.top + rcFrame.bottom);
+ else if (CSkin::m_bAvatarBorderType == 2) {
+ clipRgn = CreateRoundRectRgn(rcClient.left + rcFrame.left, rcClient.top + rcFrame.top, rcClient.left + rcFrame.right + 1,
+ rcClient.top + rcFrame.bottom + 1, iRad, iRad);
+ SelectClipRgn(dis->hDC, clipRgn);
+ }
+ }
+
+ if(dat->hwndPanelPic) {
+ /*
+ * paint avatar using ACC
+ */
+ SendMessage(dat->hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, fAero ? TRUE : FALSE);
+ SetWindowPos(dat->hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off,
+ rb.max_width, rb.max_height, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING);
+ }
+ else {
+ if(CMimAPI::m_MyAlphaBlend) {
+ CMimAPI::m_MyAlphaBlend(dis->hDC, rcClient.left + rcFrame.left + border_off, rcClient.top + rcFrame.top + border_off,
+ rb.max_width, rb.max_height, hdcMem, 0, 0,
+ rb.max_width, rb.max_height, CSkin::m_default_bf);
+ }
+ else
+ CSkin::MY_AlphaBlend(dis->hDC, rcClient.left + rcFrame.left + border_off, rcClient.top + rcFrame.top + border_off,
+ rb.max_width, rb.max_height, rb.max_width, rb.max_height, hdcMem);
+ }
+ SelectObject(hdcMem, hbmMem);
+ //DeleteObject(hbmMem);
+ DeleteDC(hdcMem);
+ if(hbmNew != hbmAvatar)
+ DeleteObject(hbmNew);
+ } else {
+ hbmMem = (HBITMAP)SelectObject(hdcMem, hbmAvatar);
+ LONG xy_off = 1; //CSkin::m_bAvatarBorderType ? 1 : 0;
+ LONG width_off = 0; //= CSkin::m_bAvatarBorderType ? 0 : 2;
+
+ SetStretchBltMode(hdcDraw, HALFTONE);
+ if (aceFlags & AVS_PREMULTIPLIED)
+ CSkin::MY_AlphaBlend(hdcDraw, xy_off, top + xy_off, (int)dNewWidth + width_off,
+ iMaxHeight + width_off, bminfo.bmWidth, bminfo.bmHeight, hdcMem);
+ else
+ StretchBlt(hdcDraw, xy_off, top + xy_off, (int)dNewWidth + width_off,
+ iMaxHeight + width_off, hdcMem, 0, 0, bminfo.bmWidth, bminfo.bmHeight, SRCCOPY);
+ //_DebugTraceA("metrics: %d, %d %d, %d", (int)dNewWidth + width_off, iMaxHeight + width_off, bminfo.bmWidth, bminfo.bmHeight);
+ SelectObject(hdcMem, hbmMem);
+ DeleteObject(hbmMem);
+ DeleteDC(hdcMem);
+ }
+ if (clipRgn) {
+ HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
+ if(bPanelPic)
+ FrameRgn(dis->hDC, clipRgn, hbr, 1, 1);
+ else
+ FrameRgn(hdcDraw, clipRgn, hbr, 1, 1);
+ DeleteObject(hbr);
+ DeleteObject(clipRgn);
+ }
+ }
+ SelectObject(hdcDraw, hPenOld);
+ SelectObject(hdcDraw, hOldBrush);
+ DeleteObject(hPenBorder);
+ if(!bPanelPic)
+ BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY);
+ SelectObject(hdcDraw, hbmOld);
+ DeleteObject(hbmDraw);
+ DeleteDC(hdcDraw);
+ return TRUE;
+ } else if (dis->hwndItem == GetDlgItem(hwndDlg, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(hwndDlg, IDC_LOGFROZENTEXT)) {
+ TCHAR szWindowText[256];
+ if (CSkin::m_skinEnabled) {
+ SetTextColor(dis->hDC, CSkin::m_DefaultFontColor);
+ CSkin::SkinDrawBG(dis->hwndItem, dat->pContainer->hwnd, dat->pContainer, &dis->rcItem, dis->hDC);
+ } else {
+ SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
+ CSkin::FillBack(dis->hDC, &dis->rcItem);
+ }
+ GetWindowText(dis->hwndItem, szWindowText, 255);
+ szWindowText[255] = 0;
+ SetBkMode(dis->hDC, TRANSPARENT);
+ DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS);
+ return TRUE;
+ } else if (dis->hwndItem == GetDlgItem(hwndDlg, IDC_STATICERRORICON)) {
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(dis->hwndItem, dat->pContainer->hwnd, dat->pContainer, &dis->rcItem, dis->hDC);
+ else
+ CSkin::FillBack(dis->hDC, &dis->rcItem);
+ DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8,
+ PluginConfig.g_iconErr, 16, 16, 0, 0, DI_NORMAL);
+ return TRUE;
+ }
+ else if (dis->CtlType == ODT_MENU && dat->Panel->isHovered()) {
+ DrawMenuItem(dis, (HICON)dis->itemData, 0);
+ return(TRUE);
+ }
+ return CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam);
+}
+
+void TSAPI LoadThemeDefaults(TContainerData *pContainer)
+{
+ int i;
+ char szTemp[40];
+ COLORREF colour;
+ ZeroMemory(&pContainer->theme, sizeof(TLogTheme));
+
+ pContainer->theme.bg = M->GetDword(FONTMODULE, SRMSGSET_BKGCOLOUR, GetSysColor(COLOR_WINDOW));
+ pContainer->theme.statbg = PluginConfig.crStatus;
+ pContainer->theme.oldinbg = PluginConfig.crOldIncoming;
+ pContainer->theme.oldoutbg = PluginConfig.crOldOutgoing;
+ pContainer->theme.inbg = PluginConfig.crIncoming;
+ pContainer->theme.outbg = PluginConfig.crOutgoing;
+ pContainer->theme.hgrid = M->GetDword(FONTMODULE, "hgrid", RGB(224, 224, 224));
+ pContainer->theme.left_indent = M->GetDword("IndentAmount", 20) * 15;
+ pContainer->theme.right_indent = M->GetDword("RightIndent", 20) * 15;
+ pContainer->theme.inputbg = M->GetDword(FONTMODULE, "inputbg", SRMSGDEFSET_BKGCOLOUR);
+
+ for (i = 1; i <= 5; i++) {
+ _snprintf(szTemp, 10, "cc%d", i);
+ colour = M->GetDword(szTemp, RGB(224, 224, 224));
+ if (colour == 0)
+ colour = RGB(1, 1, 1);
+ pContainer->theme.custom_colors[i - 1] = colour;
+ }
+ pContainer->theme.logFonts = logfonts;
+ pContainer->theme.fontColors = fontcolors;
+ pContainer->theme.rtfFonts = NULL;
+ pContainer->ltr_templates = <R_Active;
+ pContainer->rtl_templates = &RTL_Active;
+ pContainer->theme.dwFlags = (M->GetDword("mwflags", MWF_LOG_DEFAULT) & MWF_LOG_ALL);
+ pContainer->theme.isPrivate = false;
+}
+
+void TSAPI LoadOverrideTheme(TContainerData *pContainer)
+{
+ ZeroMemory(&pContainer->theme, sizeof(TLogTheme));
+
+ BOOL bReadTemplates = TRUE; //((pContainer->ltr_templates == NULL) || (pContainer->rtl_templates == NULL) ||
+ //(pContainer->logFonts == NULL) || (pContainer->fontColors == NULL));
+
+ if (lstrlen(pContainer->szAbsThemeFile) > 1) {
+ if (PathFileExists(pContainer->szAbsThemeFile)) {
+ if (CheckThemeVersion(pContainer->szAbsThemeFile) == 0) {
+ LoadThemeDefaults(pContainer);
+ return;
+ }
+ if (pContainer->ltr_templates == NULL) {
+ pContainer->ltr_templates = (TTemplateSet *)malloc(sizeof(TTemplateSet));
+ CopyMemory(pContainer->ltr_templates, <R_Active, sizeof(TTemplateSet));
+ }
+ if (pContainer->rtl_templates == NULL) {
+ pContainer->rtl_templates = (TTemplateSet *)malloc(sizeof(TTemplateSet));
+ CopyMemory(pContainer->rtl_templates, &RTL_Active, sizeof(TTemplateSet));
+ }
+
+ pContainer->theme.logFonts = (LOGFONTA *)malloc(sizeof(LOGFONTA) * (MSGDLGFONTCOUNT + 2));
+ pContainer->theme.fontColors = (COLORREF *)malloc(sizeof(COLORREF) * (MSGDLGFONTCOUNT + 2));
+ pContainer->theme.rtfFonts = (char *)malloc((MSGDLGFONTCOUNT + 2) * RTFCACHELINESIZE);
+
+ ReadThemeFromINI(pContainer->szAbsThemeFile, pContainer, bReadTemplates ? 0 : 1, THEME_READ_ALL);
+ pContainer->theme.left_indent *= 15;
+ pContainer->theme.right_indent *= 15;
+ pContainer->theme.isPrivate = true;
+ if(CSkin::m_skinEnabled)
+ pContainer->theme.bg = SkinItems[ID_EXTBKCONTAINER].COLOR;
+ else
+ pContainer->theme.bg = PluginConfig.m_fillColor ? PluginConfig.m_fillColor : GetSysColor(COLOR_WINDOW);
+ return;
+ }
+ }
+ LoadThemeDefaults(pContainer);
+}
+
+void TSAPI ConfigureSmileyButton(TWindowData *dat)
+{
+ HWND hwndDlg = dat->hwnd;
+ int nrSmileys = 0;
+ int showToolbar = dat->pContainer->dwFlags & CNT_HIDETOOLBAR ? 0 : 1;
+ int iItemID = IDC_SMILEYBTN;
+
+ if (PluginConfig.g_SmileyAddAvail) {
+ nrSmileys = CheckValidSmileyPack(dat->cache->getActiveProto(), dat->cache->getActiveContact());
+ dat->doSmileys = 1;
+ }
+
+ if (nrSmileys == 0 || dat->hContact == 0)
+ dat->doSmileys = 0;
+
+ Utils::showDlgControl(hwndDlg, iItemID, (dat->doSmileys && showToolbar) ? SW_SHOW : SW_HIDE);
+ Utils::enableDlgControl(hwndDlg, iItemID, dat->doSmileys ? TRUE : FALSE);
+}
+
+HICON TSAPI GetXStatusIcon(const TWindowData *dat)
+{
+ char szServiceName[128];
+ BYTE xStatus = dat->cache->getXStatusId();
+
+ mir_snprintf(szServiceName, 128, "%s/GetXStatusIcon", dat->cache->getActiveProto());
+
+ if (ServiceExists(szServiceName) && xStatus > 0 && xStatus <= 32)
+ return (HICON)(CallProtoService(dat->cache->getActiveProto(), "/GetXStatusIcon", xStatus, 0));
+ return 0;
+}
+
+LRESULT TSAPI GetSendButtonState(HWND hwnd)
+{
+ HWND hwndIDok=GetDlgItem(hwnd, IDOK);
+
+ if(hwndIDok)
+ return(SendMessage(hwndIDok, BUTTONSETASFLATBTN + 15, 0, 0));
+ else
+ return 0;
+}
+
+void TSAPI EnableSendButton(const TWindowData *dat, int iMode)
+{
+ HWND hwndOK;
+ SendMessage(GetDlgItem(dat->hwnd, IDOK), BUTTONSETASFLATBTN + 14, iMode, 0);
+ SendMessage(GetDlgItem(dat->hwnd, IDC_PIC), BUTTONSETASFLATBTN + 14, dat->fEditNotesActive ? TRUE : (!iMode && dat->iOpenJobs == 0) ? TRUE : FALSE, 0);
+
+ hwndOK = GetDlgItem(GetParent(GetParent(dat->hwnd)), IDOK);
+
+ if (IsWindow(hwndOK))
+ SendMessage(hwndOK, BUTTONSETASFLATBTN + 14, iMode, 0);
+}
+
+void TSAPI SendNudge(const TWindowData *dat)
+{
+ char szServiceName[128];
+
+ mir_snprintf(szServiceName, 128, "%s/SendNudge", dat->cache->getActiveProto());
+ if (ServiceExists(szServiceName) && ServiceExists(MS_NUDGE_SEND))
+ CallService(MS_NUDGE_SEND, (WPARAM)dat->cache->getActiveContact(), 0);
+ else
+ SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE,
+ (LPARAM)CTranslator::get(CTranslator::GEN_WARNING_NUDGE_DISABLED));
+}
+
+void TSAPI GetClientIcon(TWindowData *dat)
+{
+ DBVARIANT dbv = {0};
+
+ if(dat->hClientIcon)
+ DestroyIcon(dat->hClientIcon);
+
+ dat->hClientIcon = 0;
+ if (ServiceExists(MS_FP_GETCLIENTICON)) {
+ if (!DBGetContactSettingString(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "MirVer", &dbv)) {
+ dat->hClientIcon = (HICON)CallService(MS_FP_GETCLIENTICON, (WPARAM)dbv.pszVal, 1);
+ DBFreeVariant(&dbv);
+ }
+ }
+}
+
+void TSAPI GetMyNick(TWindowData *dat)
+{
+ CONTACTINFO ci;
+
+ ZeroMemory(&ci, sizeof(ci));
+ ci.cbSize = sizeof(ci);
+ ci.hContact = NULL;
+ ci.szProto = const_cast<char *>(dat->cache->getActiveProto());
+ ci.dwFlag = CNF_TCHAR | CNF_NICK;
+
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) {
+ if (ci.type == CNFT_ASCIIZ) {
+ if (lstrlen(reinterpret_cast<TCHAR *>(ci.pszVal)) < 1 || !_tcscmp(reinterpret_cast<TCHAR *>(ci.pszVal),
+ CTranslator::get(CTranslator::GEN_UNKNOWN_CONTACT))) {
+ mir_sntprintf(dat->szMyNickname, safe_sizeof(dat->szMyNickname), _T("%s"), dat->myUin[0] ? dat->myUin : CTranslator::get(CTranslator::GEN_UNKNOWN_CONTACT));
+ if (ci.pszVal) {
+ mir_free(ci.pszVal);
+ ci.pszVal = NULL;
+ }
+ } else {
+ _tcsncpy(dat->szMyNickname, reinterpret_cast<TCHAR *>(ci.pszVal), 110);
+ dat->szMyNickname[109] = 0;
+ if (ci.pszVal) {
+ mir_free(ci.pszVal);
+ ci.pszVal = NULL;
+ }
+ }
+ } else if (ci.type == CNFT_DWORD)
+ _ltot(ci.dVal, dat->szMyNickname, 10);
+ else
+ _tcsncpy(dat->szMyNickname, _T("<undef>"), 110); // that really should *never* happen
+ } else
+ _tcsncpy(dat->szMyNickname, _T("<undef>"), 110); // same here
+ if (ci.pszVal)
+ mir_free(ci.pszVal);
+}
+
+HICON TSAPI MY_GetContactIcon(const TWindowData *dat)
+{
+ return(LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getActiveStatus()));
+ //return(LoadSkinnedProtoIcon(dat->cache->getActiveProto(), dat->cache->getStatus()));
+}
+
+static void TSAPI MTH_updatePreview(const TWindowData *dat)
+{
+ TMathWindowInfo mathWndInfo;
+ HWND hwndEdit = GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE);
+ int len = GetWindowTextLengthA(hwndEdit);
+ RECT windRect;
+ char * thestr = (char *)malloc(len + 5);
+
+ GetWindowTextA(hwndEdit, thestr, len + 1);
+ GetWindowRect(dat->pContainer->hwnd, &windRect);
+ mathWndInfo.top = windRect.top;
+ mathWndInfo.left = windRect.left;
+ mathWndInfo.right = windRect.right;
+ mathWndInfo.bottom = windRect.bottom;
+
+ CallService(MTH_SETFORMULA, 0, (LPARAM) thestr);
+ CallService(MTH_RESIZE, 0, (LPARAM) &mathWndInfo);
+ free(thestr);
+}
+
+void TSAPI MTH_updateMathWindow(const TWindowData *dat)
+{
+ WINDOWPLACEMENT cWinPlace;
+
+ if (!PluginConfig.m_MathModAvail)
+ return;
+
+ MTH_updatePreview(dat);
+ CallService(MTH_SHOW, 0, 0);
+ cWinPlace.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(dat->pContainer->hwnd, &cWinPlace);
+ return;
+}
+
+/**
+ * read keyboard state and return the state of the modifier keys
+ */
+void TSAPI KbdState(TWindowData *dat, BOOL& isShift, BOOL& isControl, BOOL& isAlt)
+{
+ GetKeyboardState(dat->kstate);
+ isShift = (dat->kstate[VK_SHIFT] & 0x80);
+ isControl = (dat->kstate[VK_CONTROL] & 0x80);
+ isAlt = (dat->kstate[VK_MENU] & 0x80);
+}
+
+/**
+ * clear the message log
+ * code needs to distuingish between IM and MUC sessions.
+ */
+void TSAPI ClearLog(TWindowData *dat)
+{
+ if(dat && dat->bType == SESSIONTYPE_IM) {
+ if (dat->hwndIEView || dat->hwndHPP) {
+ IEVIEWEVENT event;
+ event.cbSize = sizeof(IEVIEWEVENT);
+ event.iType = IEE_CLEAR_LOG;
+ event.dwFlags = (dat->dwFlags & MWF_LOG_RTL) ? IEEF_RTL : 0;
+ event.hContact = dat->hContact;
+ if (dat->hwndIEView) {
+ event.hwnd = dat->hwndIEView;
+ CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+ } else {
+ event.hwnd = dat->hwndHPP;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+ }
+ SetDlgItemText(dat->hwnd, IDC_LOG, _T(""));
+ dat->hDbEventFirst = NULL;
+ }
+ else if(dat && dat->bType == SESSIONTYPE_CHAT && dat->si) {
+ SESSION_INFO* si = reinterpret_cast<SESSION_INFO *>(dat->si);
+ SESSION_INFO* s = SM_FindSession(si->ptszID, si->pszModule);
+ if (s) {
+ SetDlgItemText(dat->hwnd, IDC_CHAT_LOG, _T(""));
+ LM_RemoveAll(&s->pLog, &s->pLogEnd);
+ s->iEventCount = 0;
+ s->LastTime = 0;
+ si->iEventCount = 0;
+ si->LastTime = 0;
+ si->pLog = s->pLog;
+ si->pLogEnd = s->pLogEnd;
+ PostMessage(dat->hwnd, WM_MOUSEACTIVATE, 0, 0);
+ }
+ }
+}
+
+/**
+ * calculate the minimum required client height for the given message
+ * window layout
+
+ * the container will use this in its WM_GETMINMAXINFO handler to set
+ * minimum tracking height.
+ */
+void TSAPI DetermineMinHeight(TWindowData* dat)
+{
+ if(dat) {
+ RECT rc;
+
+ LONG height = (dat->Panel->isActive() ? dat->Panel->getHeight() + 2 : 0);
+ if(!(dat->pContainer->dwFlags & CNT_HIDETOOLBAR))
+ height += DPISCALEY_S(24); // toolbar
+ GetClientRect(GetDlgItem(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE), &rc);
+ height += rc.bottom; // input area
+ height += 40; // min space for log area and some padding
+
+ dat->pContainer->uChildMinHeight = height;
+ }
+}
+
+bool TSAPI IsAutoSplitEnabled(const TWindowData* dat)
+{
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+ return((dat && (dat->pContainer->dwFlags & CNT_AUTOSPLITTER) && !(dat->dwFlagsEx & MWF_SHOW_SPLITTEROVERRIDE)) ? true : false);
+#else
+ return(false);
+#endif
+}
+
+#if defined(__FEAT_EXP_AUTOSPLITTER)
+LONG TSAPI GetDefaultMinimumInputHeight(const TWindowData* dat)
+{
+ LONG height = MINSPLITTERY;
+
+ if(dat) {
+ if(dat->bType == SESSIONTYPE_IM)
+ height = ((dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46));
+ else
+ height = ((dat->pContainer->dwFlags & CNT_BOTTOMTOOLBAR) ? DPISCALEY_S(22 + 46) : DPISCALEY_S(46)) - DPISCALEY_S(23);
+
+ if(CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+ height += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2);
+ }
+ return(height);
+}
+#endif
+
+static std::vector<TCHAR *> vTempFilenames;
+
+/**
+ * send a pasted bitmap by file transfer.
+ */
+void TSAPI SendHBitmapAsFile(const TWindowData* dat, HBITMAP hbmp)
+{
+ const wchar_t* mirandatempdir = L"Miranda";
+ const wchar_t* filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg";
+ TCHAR filename[MAX_PATH];
+ size_t tempdirlen = GetTempPath(MAX_PATH, filename);
+ bool fSend = true;
+
+ const char* szProto = dat->cache->getActiveProto();
+ WORD wMyStatus = (WORD)CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+
+ DWORD protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ DWORD typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+
+ /*
+ * check protocol capabilities, status modes and visibility lists (privacy)
+ * to determine whether the file can be sent. Throw a warning if any of
+ * these checks fails.
+ */
+ if(!(protoCaps & PF1_FILESEND))
+ fSend = false;
+
+ if((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == dat->cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES)))
+ fSend = false;
+
+ if (protoCaps & PF1_VISLIST && DBGetContactSettingWord(dat->cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+ fSend = false;
+
+ if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && DBGetContactSettingWord(dat->cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+ fSend = false;
+
+ if(!fSend) {
+ CWarning::show(CWarning::WARN_SENDFILE, MB_OK|MB_ICONEXCLAMATION|CWarning::CWF_NOALLOWHIDE);
+ return;
+ }
+ if (tempdirlen <=0 || tempdirlen >= MAX_PATH-_tcslen(mirandatempdir)-_tcslen(filenametemplate)-2) // -2 is because %Y takes 4 symbols
+ filename[0] = 0; // prompt for a new name
+ else {
+ _tcscpy(filename+tempdirlen, mirandatempdir);
+ if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, NULL)==0)
+ filename[0] = 0;
+ else {
+ tempdirlen = _tcslen(filename);
+
+ time_t rawtime;
+ time(&rawtime);
+ const tm* timeinfo;
+ timeinfo = _localtime32((__time32_t *)&rawtime);
+ _tcsftime(filename + tempdirlen, MAX_PATH-tempdirlen, filenametemplate, timeinfo);
+ size_t firstnumberpos = tempdirlen+14;
+ size_t lastnumberpos = tempdirlen+20;
+ while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) { // while it exists
+ for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--)
+ if (filename[pos]++ != '9')
+ break;
+ else
+ if (pos == firstnumberpos)
+ filename[0] = 0; // all filenames exist => prompt for a new name
+ else
+ filename[pos] = '0';
+ }
+ }
+ }
+ if (filename[0] == 0) { // prompting to save
+ OPENFILENAME dlg;
+ dlg.lStructSize = sizeof(dlg);
+ dlg.hwndOwner = NULL; //dat->hwnd;
+ TCHAR filter[MAX_PATH];
+ mir_sntprintf(filter, SIZEOF(filter), _T("%s%c*.jpg%c%c"), TranslateT("JPEG-compressed images"), 0, 0, 0);
+ dlg.lpstrFilter = filter;
+ dlg.nFilterIndex = 1;
+ dlg.lpstrFile = filename;
+ dlg.nMaxFile = MAX_PATH;
+ dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+ dlg.lpstrDefExt = _T("jpg");
+ if (!GetSaveFileName(&dlg))
+ return;
+ }
+ IMGSRVC_INFO ii;
+ ii.cbSize = sizeof(ii);
+ ii.hbm = hbmp;
+ ii.wszName = filename;
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_JPEG;
+ CallService(MS_IMG_SAVE, (WPARAM)&ii, IMGL_TCHAR);
+
+ int fileCount = 1, totalCount = 0;
+ TCHAR** ppFiles = NULL;
+ Utils::AddToFileList(&ppFiles, &totalCount, filename);
+
+ wchar_t* _t = mir_tstrdup(filename);
+ vTempFilenames.push_back(_t);
+
+ CallService(MS_FILE_SENDSPECIFICFILEST, (WPARAM)dat->cache->getActiveContact(), (LPARAM)ppFiles);
+
+ mir_free(ppFiles[0]);
+ mir_free(ppFiles);
+}
+
+/**
+ * remove all temporary files created by the "send clipboard as file" feature.
+ */
+void TSAPI CleanTempFiles()
+{
+ std::vector<wchar_t *>::iterator it = vTempFilenames.begin();
+
+ while(it != vTempFilenames.end()) {
+ DeleteFileW(*it);
+ mir_free(*it++);
+ }
+}
diff --git a/plugins/TabSRMM/src/msglog.cpp b/plugins/TabSRMM/src/msglog.cpp new file mode 100644 index 0000000000..eb030bba9d --- /dev/null +++ b/plugins/TabSRMM/src/msglog.cpp @@ -0,0 +1,1621 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: msglog.cpp 13642 2011-05-27 10:26:12Z silvercircle $
+ *
+ * implements the richedit based message log and the template parser
+ *
+ */
+
+#include "commonheaders.h"
+#include <mbstring.h>
+
+#pragma hdrstop
+
+extern void ReleaseRichEditOle(IRichEditOle *ole);
+extern void ImageDataInsertBitmap(IRichEditOle *ole, HBITMAP hBm);
+extern int CacheIconToBMP(struct TLogIcon *theIcon, HICON hIcon, COLORREF backgroundColor, int sizeX, int sizeY);
+extern void DeleteCachedIcon(struct TLogIcon *theIcon);
+
+struct TCpTable cpTable[] = {
+ { 874, _T("Thai") },
+ { 932, _T("Japanese") },
+ { 936, _T("Simplified Chinese") },
+ { 949, _T("Korean") },
+ { 950, _T("Traditional Chinese") },
+ { 1250, _T("Central European") },
+ { 1251, _T("Cyrillic") },
+ { 20866, _T("Cyrillic KOI8-R") },
+ { 1252, _T("Latin I") },
+ { 1253, _T("Greek") },
+ { 1254, _T("Turkish") },
+ { 1255, _T("Hebrew") },
+ { 1256, _T("Arabic") },
+ { 1257, _T("Baltic") },
+ { 1258, _T("Vietnamese") },
+ { 1361, _T("Korean (Johab)") },
+ { -1, NULL}
+};
+
+static TCHAR *Template_MakeRelativeDate(struct TWindowData *dat, HANDLE hTimeZone, time_t check, int groupBreak, TCHAR code);
+static void ReplaceIcons(HWND hwndDlg, struct TWindowData *dat, LONG startAt, int fAppend, BOOL isSent);
+
+static time_t today;
+
+int g_groupBreak = TRUE;
+static TCHAR *szMyName = NULL;
+static TCHAR *szYourName = NULL;
+
+static int logPixelSY;
+static TCHAR szToday[22], szYesterday[22];
+char rtfFontsGlobal[MSGDLGFONTCOUNT + 2][RTFCACHELINESIZE];
+char *rtfFonts;
+
+LOGFONTA logfonts[MSGDLGFONTCOUNT + 2];
+COLORREF fontcolors[MSGDLGFONTCOUNT + 2];
+
+#define LOGICON_MSG 0
+#define LOGICON_URL 1
+#define LOGICON_FILE 2
+#define LOGICON_OUT 3
+#define LOGICON_IN 4
+#define LOGICON_STATUS 5
+#define LOGICON_ERROR 6
+
+static HICON Logicons[NR_LOGICONS];
+
+#define STREAMSTAGE_HEADER 0
+#define STREAMSTAGE_EVENTS 1
+#define STREAMSTAGE_TAIL 2
+#define STREAMSTAGE_STOP 3
+struct LogStreamData {
+ int stage;
+ HANDLE hContact;
+ HANDLE hDbEvent, hDbEventLast;
+ char *buffer;
+ int bufferOffset, bufferLen;
+ int eventsToInsert;
+ int isEmpty;
+ int isAppend;
+ struct TWindowData *dlgDat;
+ DBEVENTINFO *dbei;
+};
+
+__forceinline char *GetRTFFont(DWORD dwIndex)
+{
+ return rtfFonts + (dwIndex * RTFCACHELINESIZE);
+}
+
+/*
+ * remove any empty line at the end of a message to avoid some RichEdit "issues" with
+ * the highlight code (individual background colors).
+ * Doesn't touch the message for sure, but empty lines at the end are ugly anyway.
+ */
+
+static void TrimMessage(TCHAR *msg)
+{
+ size_t iLen = lstrlen(msg) - 1;
+ size_t i = iLen;
+
+ while (i && (msg[i] == '\r' || msg[i] == '\n')) {
+ i--;
+ }
+ if (i < iLen)
+ msg[i+1] = '\0';
+}
+
+void TSAPI CacheLogFonts()
+{
+ int i;
+ HDC hdc = GetDC(NULL);
+ logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(NULL, hdc);
+
+ ZeroMemory((void *)logfonts, sizeof(LOGFONTA) * MSGDLGFONTCOUNT + 2);
+ for (i = 0; i < MSGDLGFONTCOUNT; i++) {
+ LoadLogfont(i, &logfonts[i], &fontcolors[i], FONTMODULE);
+ wsprintfA(rtfFontsGlobal[i], "\\f%u\\cf%u\\b%d\\i%d\\ul%d\\fs%u", i, i, logfonts[i].lfWeight >= FW_BOLD ? 1 : 0, logfonts[i].lfItalic,logfonts[i].lfUnderline, 2 * abs(logfonts[i].lfHeight) * 74 / logPixelSY);
+ }
+ wsprintfA(rtfFontsGlobal[MSGDLGFONTCOUNT], "\\f%u\\cf%u\\b%d\\i%d\\fs%u", MSGDLGFONTCOUNT, MSGDLGFONTCOUNT, 0, 0, 0);
+
+ _tcsncpy(szToday, CTranslator::get(CTranslator::GEN_LOG_TODAY), 20);
+ _tcsncpy(szYesterday, CTranslator::get(CTranslator::GEN_LOG_YESTERDAY), 20);
+ szToday[19] = szYesterday[19] = 0;
+
+ /*
+ * cache/create the info panel fonts
+ */
+
+ COLORREF clr;
+ LOGFONTA lf;
+
+ for (i = 0; i < IPFONTCOUNT; i++) {
+ if (CInfoPanel::m_ipConfig.hFonts[i])
+ DeleteObject(CInfoPanel::m_ipConfig.hFonts[i]);
+ LoadLogfont(i + 100, &lf, &clr, FONTMODULE);
+ //lf.lfHeight =-MulDiv(lf.lfHeight, logPixelSY, 72);
+ lf.lfUnderline = 0;
+ CInfoPanel::m_ipConfig.hFonts[i] = CreateFontIndirectA(&lf);
+ CInfoPanel::m_ipConfig.clrs[i] = clr;
+ }
+
+ hdc = GetDC(PluginConfig.g_hwndHotkeyHandler);
+
+ HFONT hOldFont = (HFONT)SelectObject(hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_NICK]);
+ SIZE sz;
+
+ GetTextExtentPoint32(hdc, _T("WMA"), 3, &sz);
+ CInfoPanel::m_ipConfig.height1 = sz.cy;
+ SelectObject(hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_UIN]);
+ GetTextExtentPoint32(hdc, _T("WMA"), 3, &sz);
+ CInfoPanel::m_ipConfig.height2 = sz.cy;
+
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(PluginConfig.g_hwndHotkeyHandler, hdc);
+ PluginConfig.hFontCaption = CInfoPanel::m_ipConfig.hFonts[IPFONTCOUNT - 1];
+
+ PluginConfig.crIncoming = M->GetDword(FONTMODULE, "inbg", SRMSGDEFSET_BKGINCOLOUR);
+ PluginConfig.crOutgoing = M->GetDword(FONTMODULE, "outbg", SRMSGDEFSET_BKGOUTCOLOUR);
+ PluginConfig.crStatus = M->GetDword(FONTMODULE, "statbg", SRMSGDEFSET_BKGCOLOUR);
+ PluginConfig.crOldIncoming = M->GetDword(FONTMODULE, "oldinbg", SRMSGDEFSET_BKGINCOLOUR);
+ PluginConfig.crOldOutgoing = M->GetDword(FONTMODULE, "oldoutbg", SRMSGDEFSET_BKGOUTCOLOUR);
+}
+
+void FreeLogFonts()
+{
+ int i;
+
+ for (i = 0; i < IPFONTCOUNT; i++)
+ if (CInfoPanel::m_ipConfig.hFonts[i])
+ DeleteObject(CInfoPanel::m_ipConfig.hFonts[i]);
+
+}
+
+void TSAPI CacheMsgLogIcons()
+{
+ Logicons[0] = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ Logicons[1] = LoadSkinnedIcon(SKINICON_EVENT_URL);
+ Logicons[2] = LoadSkinnedIcon(SKINICON_EVENT_FILE);
+ Logicons[3] = PluginConfig.g_iconOut;
+ Logicons[4] = PluginConfig.g_iconIn;
+ Logicons[5] = PluginConfig.g_iconStatus;
+ Logicons[6] = PluginConfig.g_iconErr;
+}
+
+static int TSAPI GetColorIndex(char *rtffont)
+{
+ char *p;
+
+ if ((p = strstr(rtffont, "\\cf")) != NULL)
+ return atoi(p + 3);
+ return 0;
+}
+
+static void AppendToBuffer(char **buffer, int *cbBufferEnd, int *cbBufferAlloced, const char *fmt, ...)
+{
+ va_list va;
+ int charsDone;
+
+ va_start(va, fmt);
+ for (;;) {
+ charsDone = mir_vsnprintf(*buffer + *cbBufferEnd, *cbBufferAlloced - *cbBufferEnd, fmt, va);
+ if (charsDone >= 0)
+ break;
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ va_end(va);
+ *cbBufferEnd += charsDone;
+}
+
+static int AppendUnicodeToBuffer(char **buffer, int *cbBufferEnd, int *cbBufferAlloced, TCHAR * line, int mode)
+{
+ DWORD textCharsCount = 0;
+ char *d;
+
+ int lineLen = (int)(wcslen(line)) * 9 + 8;
+ if (*cbBufferEnd + lineLen > *cbBufferAlloced) {
+ cbBufferAlloced[0] += (lineLen + 1024UL - lineLen % 1024UL);
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+
+ d = *buffer + *cbBufferEnd;
+ strcpy(d, "{\\uc1 ");
+ d += 6;
+
+ for (; *line; line++, textCharsCount++) {
+
+ if (1) {
+ if (*line == 127 && line[1] != 0) {
+ TCHAR code = line[2];
+ if (((code == '0' || code == '1') && line[3] == ' ') || (line[1] == 'c' && code == 'x')) {
+ int begin = (code == '1');
+ switch (line[1]) {
+ case 'b':
+ CopyMemory(d, begin ? "\\b " : "\\b0 ", begin ? 3 : 4);
+ d += (begin ? 3 : 4);
+ line += 3;
+ continue;
+ case 'i':
+ CopyMemory(d, begin ? "\\i " : "\\i0 ", begin ? 3 : 4);
+ d += (begin ? 3 : 4);
+ line += 3;
+ continue;
+ case 'u':
+ CopyMemory(d, begin ? "\\ul " : "\\ul0 ", begin ? 4 : 5);
+ d += (begin ? 4 : 5);
+ line += 3;
+ continue;
+ case 's':
+ CopyMemory(d, begin ? "\\strike " : "\\strike0 ", begin ? 8 : 9);
+ d += (begin ? 8 : 9);
+ line += 3;
+ continue;
+ case 'c':
+ begin = (code == 'x');
+ CopyMemory(d, "\\cf", 3);
+ if (begin) {
+ d[3] = (char)line[3];
+ d[4] = (char)line[4];
+ d[5] = ' ';
+ } else {
+ char szTemp[10];
+ int colindex = GetColorIndex(GetRTFFont(LOWORD(mode) ? (MSGFONTID_MYMSG + (HIWORD(mode) ? 8 : 0)) : (MSGFONTID_YOURMSG + (HIWORD(mode) ? 8 : 0))));
+ _snprintf(szTemp, 4, "%02d", colindex);
+ d[3] = szTemp[0];
+ d[4] = szTemp[1];
+ d[5] = ' ';
+ }
+ d += 6;
+ line += (begin ? 6 : 3);
+ continue;
+ }
+ }
+ }
+ }
+ if (*line == '\r' && line[1] == '\n') {
+ CopyMemory(d, "\\line ", 6);
+ line++;
+ d += 6;
+ } else if (*line == '\n') {
+ CopyMemory(d, "\\line ", 6);
+ d += 6;
+ } else if (*line == '\t') {
+ CopyMemory(d, "\\tab ", 5);
+ d += 5;
+ } else if (*line == '\\' || *line == '{' || *line == '}') {
+ *d++ = '\\';
+ *d++ = (char) * line;
+ } else if (*line < 128) {
+ *d++ = (char) * line;
+ } else
+ d += sprintf(d, "\\u%d ?", *line);
+ }
+
+ strcpy(d, "}");
+ d++;
+
+ *cbBufferEnd = (int)(d - *buffer);
+ return textCharsCount;
+}
+
+/*
+ * same as above but does "\r\n"->"\\par " and "\t"->"\\tab " too
+ */
+
+static int AppendToBufferWithRTF(int mode, char **buffer, int *cbBufferEnd, int *cbBufferAlloced, const char *fmt, ...)
+{
+ va_list va;
+ int charsDone, i;
+
+ va_start(va, fmt);
+ for (;;) {
+ charsDone = mir_vsnprintf(*buffer + *cbBufferEnd, *cbBufferAlloced - *cbBufferEnd, fmt, va);
+ if (charsDone >= 0)
+ break;
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ va_end(va);
+ *cbBufferEnd += charsDone;
+ for (i = *cbBufferEnd - charsDone; (*buffer)[i]; i++) {
+
+ if (1) {
+ if ((*buffer)[i] == '' && (*buffer)[i + 1] != 0) {
+ char code = (*buffer)[i + 2];
+ char tag = (*buffer)[i + 1];
+
+ if (((code == '0' || code == '1') && (*buffer)[i + 3] == ' ') || (tag == 'c' && (code == 'x' || code == '0'))) {
+ int begin = (code == '1');
+
+ if (*cbBufferEnd + 5 > *cbBufferAlloced) {
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ switch (tag) {
+ case 'b':
+ CopyMemory(*buffer + i, begin ? "\\b1 " : "\\b0 ", 4);
+ continue;
+ case 'i':
+ CopyMemory(*buffer + i, begin ? "\\i1 " : "\\i0 ", 4);
+ continue;
+ case 'u':
+ MoveMemory(*buffer + i + 2, *buffer + i + 1, *cbBufferEnd - i);
+ CopyMemory(*buffer + i, begin ? "\\ul1 " : "\\ul0 ", 5);
+ *cbBufferEnd += 1;
+ continue;
+ case 's':
+ *cbBufferAlloced += 20;
+ *buffer = (char *)realloc(*buffer, *cbBufferAlloced);
+ MoveMemory(*buffer + i + 6, *buffer + i + 1, (*cbBufferEnd - i) + 1);
+ CopyMemory(*buffer + i, begin ? "\\strike1 " : "\\strike0 ", begin ? 9 : 9);
+ *cbBufferEnd += 5;
+ continue;
+ case 'c':
+ begin = (code == 'x');
+ CopyMemory(*buffer + i, "\\cf", 3);
+ if (begin) {
+ } else {
+ char szTemp[10];
+ int colindex = GetColorIndex(GetRTFFont(LOWORD(mode) ? (MSGFONTID_MYMSG + (HIWORD(mode) ? 8 : 0)) : (MSGFONTID_YOURMSG + (HIWORD(mode) ? 8 : 0))));
+ _snprintf(szTemp, 4, "%02d", colindex);
+ (*buffer)[i + 3] = szTemp[0];
+ (*buffer)[i + 4] = szTemp[1];
+ }
+ continue;
+ }
+ }
+ }
+ }
+
+ if ((*buffer)[i] == '\r' && (*buffer)[i + 1] == '\n') {
+ if (*cbBufferEnd + 5 > *cbBufferAlloced) {
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ MoveMemory(*buffer + i + 6, *buffer + i + 2, *cbBufferEnd - i - 1);
+ CopyMemory(*buffer + i, "\\line ", 6);
+ *cbBufferEnd += 4;
+ } else if ((*buffer)[i] == '\n') {
+ if (*cbBufferEnd + 6 > *cbBufferAlloced) {
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ MoveMemory(*buffer + i + 6, *buffer + i + 1, *cbBufferEnd - i);
+ CopyMemory(*buffer + i, "\\line ", 6);
+ *cbBufferEnd += 5;
+ } else if ((*buffer)[i] == '\t') {
+ if (*cbBufferEnd + 5 > *cbBufferAlloced) {
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ MoveMemory(*buffer + i + 5, *buffer + i + 1, *cbBufferEnd - i);
+ CopyMemory(*buffer + i, "\\tab ", 5);
+ *cbBufferEnd += 4;
+ } else if ((*buffer)[i] == '\\' || (*buffer)[i] == '{' || (*buffer)[i] == '}') {
+ if (*cbBufferEnd + 2 > *cbBufferAlloced) {
+ *cbBufferAlloced += 1024;
+ *buffer = (char *) realloc(*buffer, *cbBufferAlloced);
+ }
+ MoveMemory(*buffer + i + 1, *buffer + i, *cbBufferEnd - i + 1);
+ (*buffer)[i] = '\\';
+ ++*cbBufferEnd;
+ i++;
+ }
+ }
+ return (int)(_mbslen((unsigned char *)*buffer + *cbBufferEnd));
+}
+
+static void Build_RTF_Header(char **buffer, int *bufferEnd, int *bufferAlloced, struct TWindowData *dat)
+{
+ COLORREF colour;
+ int i;
+ char szTemp[30];
+ LOGFONTA* logFonts = dat->pContainer->theme.logFonts;
+ COLORREF* fontColors = dat->pContainer->theme.fontColors;
+ TLogTheme *theme = &dat->pContainer->theme;
+
+ // rtl
+ if (dat->dwFlags & MWF_LOG_RTL)
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "{\\rtf1\\ansi\\deff0{\\fonttbl");
+ else
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "{\\rtf1\\ansi\\deff0{\\fonttbl");
+
+ for (i = 0; i < MSGDLGFONTCOUNT; i++)
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "{\\f%u\\fnil\\fcharset%u %s;}", i, logFonts[i].lfCharSet, logFonts[i].lfFaceName);
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "{\\f%u\\fnil\\fcharset%u %s;}", MSGDLGFONTCOUNT, logFonts[i].lfCharSet, "Arial");
+
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "}{\\colortbl ");
+ for (i = 0; i < MSGDLGFONTCOUNT; i++)
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(fontColors[i]), GetGValue(fontColors[i]), GetBValue(fontColors[i]));
+ if (GetSysColorBrush(COLOR_HOTLIGHT) == NULL)
+ colour = RGB(0, 0, 255);
+ else
+ colour = GetSysColor(COLOR_HOTLIGHT);
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+
+ /* OnO: Create incoming and outcoming colours */
+ colour = theme->inbg;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ colour = theme->outbg;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ colour = theme->bg;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ colour = theme->hgrid;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ colour = theme->oldinbg;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ colour = theme->oldoutbg;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ colour = theme->statbg;
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+
+ // custom template colors...
+
+ for (i = 1; i <= 5; i++) {
+ _snprintf(szTemp, 10, "cc%d", i);
+ colour = theme->custom_colors[i - 1];
+ if (colour == 0)
+ colour = RGB(1, 1, 1);
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(colour), GetGValue(colour), GetBValue(colour));
+ }
+
+ // bbcode colors...
+
+ for (i = 0; i < Utils::rtf_ctable_size; i++)
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(Utils::rtf_ctable[i].clr), GetGValue(Utils::rtf_ctable[i].clr), GetBValue(Utils::rtf_ctable[i].clr));
+
+ /*
+ * paragraph header
+ */
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "}");
+
+ /*
+ * indent:
+ * real indent is set in msgdialog.c (DM_OPTIONSAPPLIED)
+ */
+
+ if (!(dat->dwFlags & MWF_LOG_INDENT))
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "\\li%u\\ri%u\\fi%u\\tx%u", 2*15, 2*15, 0, 70 * 15);
+}
+
+
+//free() the return value
+static char *CreateRTFHeader(struct TWindowData *dat)
+{
+ char *buffer;
+ int bufferAlloced, bufferEnd;
+
+ bufferEnd = 0;
+ bufferAlloced = 1024;
+ buffer = (char *) malloc(bufferAlloced);
+ buffer[0] = '\0';
+
+ Build_RTF_Header(&buffer, &bufferEnd, &bufferAlloced, dat);
+ return buffer;
+}
+
+static void AppendTimeStamp(TCHAR *szFinalTimestamp, int isSent, char **buffer, int *bufferEnd, int *bufferAlloced, int skipFont,
+ struct TWindowData *dat, int iFontIDOffset)
+{
+ if (skipFont)
+ AppendUnicodeToBuffer(buffer, bufferEnd, bufferAlloced, szFinalTimestamp, MAKELONG(isSent, dat->isHistory));
+ else {
+ AppendToBuffer(buffer, bufferEnd, bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset));
+ AppendUnicodeToBuffer(buffer, bufferEnd, bufferAlloced, szFinalTimestamp, MAKELONG(isSent, dat->isHistory));
+ }
+}
+
+//free() the return value
+static char *CreateRTFTail(struct TWindowData *dat)
+{
+ char *buffer;
+ int bufferAlloced, bufferEnd;
+
+ bufferEnd = 0;
+ bufferAlloced = 1024;
+ buffer = (char *) malloc(bufferAlloced);
+ buffer[0] = '\0';
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "}");
+ return buffer;
+}
+
+int TSAPI DbEventIsShown(struct TWindowData *dat, DBEVENTINFO * dbei)
+{
+ int heFlags;
+
+ switch (dbei->eventType) {
+ case EVENTTYPE_MESSAGE:
+ return 1;
+ case EVENTTYPE_FILE:
+ return(dat->dwFlagsEx & MWF_SHOW_FILEEVENTS);
+ }
+ if (IsStatusEvent(dbei->eventType))
+ return 1;
+
+ heFlags = HistoryEvents_GetFlags(dbei->eventType);
+ if (heFlags != -1)
+ return (heFlags & HISTORYEVENTS_FLAG_SHOW_IM_SRMM) == HISTORYEVENTS_FLAG_SHOW_IM_SRMM;
+
+ return 0;
+}
+
+static int DbEventIsForMsgWindow(DBEVENTINFO *dbei)
+{
+ DBEVENTTYPEDESCR* et = ( DBEVENTTYPEDESCR* )CallService( MS_DB_EVENT_GETTYPE, ( WPARAM )dbei->szModule, ( LPARAM )dbei->eventType );
+ return et && ( et->flags & DETF_MSGWINDOW );
+}
+
+static char *Template_CreateRTFFromDbEvent(struct TWindowData *dat, HANDLE hContact, HANDLE hDbEvent, int prefixParaBreak, struct LogStreamData *streamData)
+{
+ char *buffer, c;
+ TCHAR ci, cc;
+ TCHAR *szFinalTimestamp;
+ int bufferAlloced, bufferEnd;
+ size_t iTemplateLen, i = 0;
+ DBEVENTINFO dbei = { 0 };
+ int isSent = 0;
+ int iFontIDOffset = 0;
+ TCHAR *szTemplate;
+ HANDLE hTimeZone;
+ BOOL skipToNext = FALSE, showTime = TRUE, showDate = TRUE, skipFont = FALSE;
+ struct tm event_time;
+ TTemplateSet *this_templateset;
+ BOOL isBold = FALSE, isItalic = FALSE, isUnderline = FALSE;
+ DWORD dwEffectiveFlags;
+ DWORD dwFormattingParams = MAKELONG(PluginConfig.m_FormatWholeWordsOnly, 0);
+ BOOL fIsStatusChangeEvent = FALSE;
+ TCHAR *msg, *formatted = NULL;
+ int heFlags = -1;
+ char *rtfMessage = NULL;
+
+ bufferEnd = 0;
+ bufferAlloced = 1024;
+ buffer = (char *) malloc(bufferAlloced);
+ buffer[0] = '\0';
+
+ if (streamData->dbei != 0)
+ dbei = *(streamData->dbei);
+ else {
+ dbei.cbSize = sizeof(dbei);
+ dbei.cbBlob = CallService(MS_DB_EVENT_GETBLOBSIZE, (WPARAM) hDbEvent, 0);
+ if (dbei.cbBlob == -1) {
+ free(buffer);
+ return NULL;
+ }
+ dbei.pBlob = (PBYTE) malloc(dbei.cbBlob);
+ CallService(MS_DB_EVENT_GET, (WPARAM) hDbEvent, (LPARAM) & dbei);
+ if (!DbEventIsShown(dat, &dbei)) {
+ free(dbei.pBlob);
+ free(buffer);
+ return NULL;
+ }
+ }
+
+ if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT | DBEF_READ)))
+ dat->cache->updateStats(TSessionStats::SET_LAST_RCV, lstrlenA((char *) dbei.pBlob));
+
+ if (dbei.eventType != EVENTTYPE_MESSAGE && dbei.eventType != EVENTTYPE_FILE && !IsStatusEvent(dbei.eventType))
+ heFlags = HistoryEvents_GetFlags(dbei.eventType);
+ if (heFlags & HISTORYEVENTS_FLAG_DEFAULT)
+ heFlags = -1;
+
+ if (heFlags != -1)
+ rtfMessage = HistoryEvents_GetRichText(hDbEvent, &dbei);
+ if (rtfMessage == NULL) {
+ msg = DbGetEventTextT(&dbei, dat->codePage);
+ if (!msg) {
+ free(dbei.pBlob);
+ free(buffer);
+ return NULL;
+ }
+ TrimMessage(msg);
+ formatted = const_cast<TCHAR *>(Utils::FormatRaw(dat, msg, dwFormattingParams, isSent));
+ mir_free(msg);
+ }
+
+ fIsStatusChangeEvent = (heFlags != -1 || IsStatusEvent(dbei.eventType));
+
+ if (dat->isAutoRTL & 2) { // means: last \\par was deleted to avoid new line at end of log
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\par");
+ dat->isAutoRTL &= ~2;
+ }
+
+ if (dat->dwFlags & MWF_LOG_RTL)
+ dbei.flags |= DBEF_RTL;
+
+ if (dbei.flags & DBEF_RTL)
+ dat->isAutoRTL |= 1;
+
+ dwEffectiveFlags = dat->dwFlags;
+
+ dat->isHistory = (dbei.timestamp < dat->cache->getSessionStart() && (dbei.flags & DBEF_READ || dbei.flags & DBEF_SENT));
+ iFontIDOffset = dat->isHistory ? 8 : 0; // offset into the font table for either history (old) or new events... (# of fonts per configuration set)
+ isSent = (dbei.flags & DBEF_SENT);
+
+ if (!isSent && (fIsStatusChangeEvent || dbei.eventType == EVENTTYPE_MESSAGE || DbEventIsForMsgWindow(&dbei))) {
+ CallService(MS_DB_EVENT_MARKREAD, (WPARAM)hContact, (LPARAM)hDbEvent);
+ CallService(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hDbEvent);
+ }
+
+ g_groupBreak = TRUE;
+
+ if (dwEffectiveFlags & MWF_DIVIDERWANTED) {
+ static char szStyle_div[128] = "\0";
+ if (szStyle_div[0] == 0)
+ mir_snprintf(szStyle_div, 128, "\\f%u\\cf%u\\ul0\\b%d\\i%d\\fs%u", H_MSGFONTID_DIVIDERS, H_MSGFONTID_DIVIDERS, 0, 0, 5);
+
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\sl-1\\slmult0\\highlight%d\\cf%d\\-\\par\\sl0", H_MSGFONTID_DIVIDERS, H_MSGFONTID_DIVIDERS);
+ dat->dwFlags &= ~MWF_DIVIDERWANTED;
+ }
+ if (dwEffectiveFlags & MWF_LOG_GROUPMODE && ((dbei.flags & (DBEF_SENT | DBEF_READ | DBEF_RTL)) == LOWORD(dat->iLastEventType)) && dbei.eventType == EVENTTYPE_MESSAGE && HIWORD(dat->iLastEventType) == EVENTTYPE_MESSAGE && (dbei.timestamp - dat->lastEventTime) < 86400) {
+ g_groupBreak = FALSE;
+ if ((time_t)dbei.timestamp > today && dat->lastEventTime < today) {
+ g_groupBreak = TRUE;
+ }
+ }
+ if (!streamData->isEmpty && g_groupBreak && (dwEffectiveFlags & MWF_LOG_GRID))
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\sl-1\\slmult0\\highlight%d\\cf%d\\-\\par\\sl0", MSGDLGFONTCOUNT + 4, MSGDLGFONTCOUNT + 4);
+
+ if (dbei.flags & DBEF_RTL)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\rtlpar");
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\ltrpar");
+
+ /* OnO: highlight start */
+ if(fIsStatusChangeEvent)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\highlight%d\\cf%d", MSGDLGFONTCOUNT + 7, MSGDLGFONTCOUNT + 7);
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\highlight%d\\cf%d", MSGDLGFONTCOUNT + (dat->isHistory?5:1) + ((isSent) ? 1 : 0), MSGDLGFONTCOUNT + (dat->isHistory?5:1) + ((isSent) ? 1 : 0));
+
+ streamData->isEmpty = FALSE;
+
+ if (dat->isAutoRTL & 1) {
+ if (dbei.flags & DBEF_RTL) {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\ltrch\\rtlch");
+ } else {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\rtlch\\ltrch");
+ }
+ }
+
+ /*
+ * templated code starts here
+ */
+ if (dwEffectiveFlags & MWF_LOG_SHOWTIME) {
+ hTimeZone = ((dat->dwFlags & MWF_LOG_LOCALTIME) && !isSent) ? dat->hTimeZone : NULL;
+ time_t local_time = tmi.timeStampToTimeZoneTimeStamp(hTimeZone, dbei.timestamp);
+ event_time = *gmtime(&local_time);
+ }
+ this_templateset = dbei.flags & DBEF_RTL ? dat->pContainer->rtl_templates : dat->pContainer->ltr_templates;
+
+ if (fIsStatusChangeEvent)
+ szTemplate = this_templateset->szTemplates[TMPL_STATUSCHG];
+ else if (dbei.eventType == EVENTTYPE_ERRMSG)
+ szTemplate = this_templateset->szTemplates[TMPL_ERRMSG];
+ else {
+ if (dwEffectiveFlags & MWF_LOG_GROUPMODE)
+ szTemplate = isSent ? (g_groupBreak ? this_templateset->szTemplates[TMPL_GRPSTARTOUT] : this_templateset->szTemplates[TMPL_GRPINNEROUT]) :
+ (g_groupBreak ? this_templateset->szTemplates[TMPL_GRPSTARTIN] : this_templateset->szTemplates[TMPL_GRPINNERIN]);
+ else
+ szTemplate = isSent ? this_templateset->szTemplates[TMPL_MSGOUT] : this_templateset->szTemplates[TMPL_MSGIN];
+ }
+
+ iTemplateLen = lstrlen(szTemplate);
+ showTime = dwEffectiveFlags & MWF_LOG_SHOWTIME;
+ showDate = dwEffectiveFlags & MWF_LOG_SHOWDATES;
+
+ if (dat->hHistoryEvents) {
+ if (dat->curHistory == dat->maxHistory) {
+ MoveMemory(dat->hHistoryEvents, &dat->hHistoryEvents[1], sizeof(HANDLE) * (dat->maxHistory - 1));
+ dat->curHistory--;
+ }
+ dat->hHistoryEvents[dat->curHistory++] = hDbEvent;
+ }
+
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\ul0\\b0\\i0 ");
+
+ while (i < iTemplateLen) {
+ ci = szTemplate[i];
+ if (ci == '%') {
+ cc = szTemplate[i + 1];
+ skipToNext = FALSE;
+ skipFont = FALSE;
+ /*
+ * handle modifiers
+ */
+ while (cc == '#' || cc == '$' || cc == '&' || cc == '?' || cc == '\\') {
+ switch (cc) {
+ case '#':
+ if (!dat->isHistory) {
+ skipToNext = TRUE;
+ goto skip;
+ } else {
+ i++;
+ cc = szTemplate[i + 1];
+ continue;
+ }
+ case '$':
+ if (dat->isHistory) {
+ skipToNext = TRUE;
+ goto skip;
+ } else {
+ i++;
+ cc = szTemplate[i + 1];
+ continue;
+ }
+ case '&':
+ i++;
+ cc = szTemplate[i + 1];
+ skipFont = TRUE;
+ break;
+ case '?':
+ if (dwEffectiveFlags & MWF_LOG_NORMALTEMPLATES) {
+ i++;
+ cc = szTemplate[i + 1];
+ continue;
+ } else {
+ i++;
+ skipToNext = TRUE;
+ goto skip;
+ }
+ case '\\':
+ if (!(dwEffectiveFlags & MWF_LOG_NORMALTEMPLATES)) {
+ i++;
+ cc = szTemplate[i + 1];
+ continue;
+ } else {
+ i++;
+ skipToNext = TRUE;
+ goto skip;
+ }
+ }
+ }
+ switch (cc) {
+ case 'V':
+ //AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\fs0\\\expnd-40 ~-%d-~", hDbEvent);
+ break;
+ case 'I': {
+ if (dwEffectiveFlags & MWF_LOG_SHOWICONS) {
+ int icon;
+ if ((dwEffectiveFlags & MWF_LOG_INOUTICONS) && dbei.eventType == EVENTTYPE_MESSAGE)
+ icon = isSent ? LOGICON_OUT : LOGICON_IN;
+ else {
+ switch (dbei.eventType) {
+ case EVENTTYPE_FILE:
+ icon = LOGICON_FILE;
+ break;
+ case EVENTTYPE_ERRMSG:
+ icon = LOGICON_ERROR;
+ break;
+ default:
+ icon = LOGICON_MSG;
+ break;
+ }
+ if (fIsStatusChangeEvent)
+ icon = LOGICON_STATUS;
+ }
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s\\fs1 #~#%01d%c%s ", GetRTFFont(MSGFONTID_SYMBOLS_IN), icon, isSent ? '>' : '<', GetRTFFont(isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset));
+ } else
+ skipToNext = TRUE;
+ break;
+ }
+ case 'D': // long date
+ if (showTime && showDate) {
+ szFinalTimestamp = Template_MakeRelativeDate(dat, hTimeZone, dbei.timestamp, g_groupBreak, (TCHAR)'D');
+ AppendTimeStamp(szFinalTimestamp, isSent, &buffer, &bufferEnd, &bufferAlloced, skipFont, dat, iFontIDOffset);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'E': // short date...
+ if (showTime && showDate) {
+ szFinalTimestamp = Template_MakeRelativeDate(dat, hTimeZone, dbei.timestamp, g_groupBreak, (TCHAR)'E');
+ AppendTimeStamp(szFinalTimestamp, isSent, &buffer, &bufferEnd, &bufferAlloced, skipFont, dat, iFontIDOffset);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'a': // 12 hour
+ case 'h': // 24 hour
+ if (showTime) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, cc == 'h' ? "%02d" : "%2d", cc == 'h' ? event_time.tm_hour : (event_time.tm_hour > 12 ? event_time.tm_hour - 12 : event_time.tm_hour));
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, cc == 'h' ? "%s %02d" : "%s %2d", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), cc == 'h' ? event_time.tm_hour : (event_time.tm_hour > 12 ? event_time.tm_hour - 12 : event_time.tm_hour));
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'm': // minute
+ if (showTime) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%02d", event_time.tm_min);
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %02d", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), event_time.tm_min);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 's': //second
+ if (showTime && dwEffectiveFlags & MWF_LOG_SHOWSECONDS) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%02d", event_time.tm_sec);
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %02d", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), event_time.tm_sec);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'p': // am/pm symbol
+ if (showTime) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s", event_time.tm_hour > 11 ? "PM" : "AM");
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %s", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), event_time.tm_hour > 11 ? "PM" : "AM");
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'o': // month
+ if (showTime && showDate) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%02d", event_time.tm_mon + 1);
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %02d", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), event_time.tm_mon + 1);
+ } else
+ skipToNext = TRUE;
+ break;
+ case'O': // month (name)
+ if (showTime && showDate) {
+ if (skipFont)
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, const_cast<TCHAR *>(CTranslator::getMonth(event_time.tm_mon)),
+ MAKELONG(isSent, dat->isHistory));
+ else {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset));
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, const_cast<TCHAR *>(CTranslator::getMonth(event_time.tm_mon)),
+ MAKELONG(isSent, dat->isHistory));
+ }
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'd': // day of month
+ if (showTime && showDate) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%02d", event_time.tm_mday);
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %02d", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), event_time.tm_mday);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'w': // day of week
+ if (showTime && showDate) {
+ if (skipFont)
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, const_cast<TCHAR *>(CTranslator::getWeekday(event_time.tm_wday)), MAKELONG(isSent, dat->isHistory));
+ else {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset));
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, const_cast<TCHAR *>(CTranslator::getWeekday(event_time.tm_wday)), MAKELONG(isSent, dat->isHistory));
+ }
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'y': // year
+ if (showTime && showDate) {
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%04d", event_time.tm_year + 1900);
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %04d", GetRTFFont(isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset), event_time.tm_year + 1900);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'R':
+ case 'r': // long date
+ if (showTime && showDate) {
+ szFinalTimestamp = Template_MakeRelativeDate(dat, hTimeZone, dbei.timestamp, g_groupBreak, cc);
+ AppendTimeStamp(szFinalTimestamp, isSent, &buffer, &bufferEnd, &bufferAlloced, skipFont, dat, iFontIDOffset);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 't':
+ case 'T':
+ if (showTime) {
+ szFinalTimestamp = Template_MakeRelativeDate(dat, hTimeZone, dbei.timestamp, g_groupBreak, (TCHAR)((dwEffectiveFlags & MWF_LOG_SHOWSECONDS) ? cc : (TCHAR)'t'));
+ AppendTimeStamp(szFinalTimestamp, isSent, &buffer, &bufferEnd, &bufferAlloced, skipFont, dat, iFontIDOffset);
+ } else
+ skipToNext = TRUE;
+ break;
+ case 'S': { // symbol
+ if (dwEffectiveFlags & MWF_LOG_SYMBOLS) {
+ if ((dwEffectiveFlags & MWF_LOG_INOUTICONS) && dbei.eventType == EVENTTYPE_MESSAGE)
+ c = isSent ? 0x37 : 0x38;
+ else {
+ switch (dbei.eventType) {
+ case EVENTTYPE_MESSAGE:
+ c = (char)0xaa;
+ break;
+ case EVENTTYPE_FILE:
+ c = (char)0xcd;
+ break;
+ case EVENTTYPE_ERRMSG:
+ c = (char)0x72;;
+ break;
+ default:
+ c = (char)0xaa;
+ break;
+ }
+ if (fIsStatusChangeEvent)
+ c = 0x4e;
+ }
+ if (skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%c%s ", c, GetRTFFont(isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset));
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s %c%s ", isSent ? GetRTFFont(MSGFONTID_SYMBOLS_OUT) : GetRTFFont(MSGFONTID_SYMBOLS_IN), c, GetRTFFont(isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset));
+ } else
+ skipToNext = TRUE;
+ break;
+ }
+ case 'n': // hard line break
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, dbei.flags & DBEF_RTL ? "\\rtlpar\\par\\rtlpar" : "\\par\\ltrpar");
+ break;
+ case 'l': // soft line break
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\line");
+ break;
+ case 'N': { // nickname
+ if (heFlags != -1 && !(heFlags & HISTORYEVENTS_FLAG_EXPECT_CONTACT_NAME_BEFORE))
+ break;
+
+ if (!skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYNAME + iFontIDOffset : MSGFONTID_YOURNAME + iFontIDOffset));
+ if (isSent)
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, szMyName, MAKELONG(isSent, dat->isHistory));
+ else
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, szYourName, MAKELONG(isSent, dat->isHistory));
+ break;
+ }
+ case 'U': // UIN
+ if (!skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYNAME + iFontIDOffset : MSGFONTID_YOURNAME + iFontIDOffset));
+ if(!isSent)
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, (wchar_t *)dat->cache->getUIN(), MAKELONG(isSent, dat->isHistory));
+ else
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, (wchar_t *)dat->myUin, MAKELONG(isSent, dat->isHistory));
+ break;
+ case 'e': // error message
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(MSGFONTID_ERROR));
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, (wchar_t *)dbei.szModule, MAKELONG(isSent, dat->isHistory));
+ break;
+ case 'M': { // message
+ if (fIsStatusChangeEvent)
+ dbei.eventType = EVENTTYPE_STATUSCHANGE;
+ switch (dbei.eventType) {
+ case EVENTTYPE_MESSAGE:
+ case EVENTTYPE_ERRMSG:
+ case EVENTTYPE_STATUSCHANGE: {
+ if (fIsStatusChangeEvent || dbei.eventType == EVENTTYPE_ERRMSG) {
+ if (dbei.eventType == EVENTTYPE_ERRMSG && dbei.cbBlob == 0)
+ break;
+ if (dbei.eventType == EVENTTYPE_ERRMSG) {
+ if (!skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\line%s ", GetRTFFont(fIsStatusChangeEvent ? H_MSGFONTID_STATUSCHANGES : MSGFONTID_MYMSG));
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\line ");
+ } else {
+ if (!skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(fIsStatusChangeEvent ? H_MSGFONTID_STATUSCHANGES : MSGFONTID_MYMSG));
+ }
+ } else {
+ if (!skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset));
+ }
+
+ if (rtfMessage != NULL) {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s", rtfMessage);
+ } else {
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, formatted, MAKELONG(isSent, dat->isHistory));
+ }
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s", "\\b0\\ul0\\i0 ");
+ break;
+ }
+ case EVENTTYPE_FILE:
+ if (!skipFont)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(isSent ? MSGFONTID_MYMISC + iFontIDOffset : MSGFONTID_YOURMISC + iFontIDOffset));
+ {
+ char* szFileName = (char *)dbei.pBlob + sizeof(DWORD);
+ char* szDescr = szFileName + lstrlenA(szFileName) + 1;
+ TCHAR* tszFileName = DbGetEventStringT( &dbei, szFileName );
+ if ( *szDescr != 0 ) {
+ TCHAR* tszDescr = DbGetEventStringT( &dbei, szDescr );
+ TCHAR buf[1000];
+ mir_sntprintf( buf, SIZEOF(buf), _T("%s (%s)"), tszFileName, tszDescr );
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, buf, 0 );
+ mir_free( tszDescr );
+ }
+ else {
+ AppendUnicodeToBuffer(&buffer, &bufferEnd, &bufferAlloced, tszFileName, 0 );
+ }
+ mir_free( tszFileName );
+ }
+ break;
+ }
+ break;
+ }
+ case '*': // bold
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, isBold ? "\\b0 " : "\\b ");
+ isBold = !isBold;
+ break;
+ case '/': // italic
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, isItalic ? "\\i0 " : "\\i ");
+ isItalic = !isItalic;
+ break;
+ case '_': // italic
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, isUnderline ? "\\ul0 " : "\\ul ");
+ isUnderline = !isUnderline;
+ break;
+ case '-': { // grid line
+ TCHAR color = szTemplate[i + 2];
+ if (color >= '0' && color <= '4') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\par\\sl-1\\slmult0\\highlight%d\\cf%d\\-\\par\\sl0", MSGDLGFONTCOUNT + 8 + (color - '0'), MSGDLGFONTCOUNT + 7 + (color - '0'));
+ i++;
+ } else {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\par\\sl-1\\slmult0\\highlight%d\\cf%d\\-\\par\\sl0", MSGDLGFONTCOUNT + 4, MSGDLGFONTCOUNT + 4);
+ }
+ break;
+ }
+ case '~': // font break (switch to default font...)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, GetRTFFont(isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset));
+ break;
+ case 'H': { // highlight
+ TCHAR color = szTemplate[i + 2];
+
+ if (color >= '0' && color <= '4') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\highlight%d", MSGDLGFONTCOUNT + 8 + (color - '0'));
+ i++;
+ } else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\highlight%d", (MSGDLGFONTCOUNT + (dat->isHistory?5:1) + ((isSent) ? 1 : 0)));
+ break;
+ }
+ case '|': // tab
+ if (dwEffectiveFlags & MWF_LOG_INDENT)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\tab");
+ else
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, " ");
+ break;
+ case 'f': { // font tag...
+ TCHAR code = szTemplate[i + 2];
+ int fontindex = -1;
+ switch (code) {
+ case 'd':
+ fontindex = isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset;
+ break;
+ case 'n':
+ fontindex = isSent ? MSGFONTID_MYNAME + iFontIDOffset : MSGFONTID_YOURNAME + iFontIDOffset;
+ break;
+ case 'm':
+ fontindex = isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset;
+ break;
+ case 'M':
+ fontindex = isSent ? MSGFONTID_MYMISC + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset;
+ break;
+ case 's':
+ fontindex = isSent ? MSGFONTID_SYMBOLS_OUT : MSGFONTID_SYMBOLS_IN;
+ break;
+ }
+ if (fontindex != -1) {
+ i++;
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "%s ", GetRTFFont(fontindex));
+ } else
+ skipToNext = TRUE;
+ break;
+ }
+ case 'c': { // font color (using one of the predefined 5 colors) or one of the standard font colors (m = message, d = date/time, n = nick)
+ TCHAR color = szTemplate[i + 2];
+ if (color >= '0' && color <= '4') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\cf%d ", MSGDLGFONTCOUNT + 8 + (color - '0'));
+ i++;
+ } else if (color == (TCHAR)'d') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\cf%d ", isSent ? MSGFONTID_MYTIME + iFontIDOffset : MSGFONTID_YOURTIME + iFontIDOffset);
+ i++;
+ } else if (color == (TCHAR)'m') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\cf%d ", isSent ? MSGFONTID_MYMSG + iFontIDOffset : MSGFONTID_YOURMSG + iFontIDOffset);
+ i++;
+ } else if (color == (TCHAR)'n') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\cf%d ", isSent ? MSGFONTID_MYNAME + iFontIDOffset : MSGFONTID_YOURNAME + iFontIDOffset);
+ i++;
+ } else if (color == (TCHAR)'s') {
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\cf%d ", isSent ? MSGFONTID_SYMBOLS_OUT : MSGFONTID_SYMBOLS_IN);
+ i++;
+ } else
+ skipToNext = TRUE;
+ break;
+ }
+ case '<': // bidi tag
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\rtlmark\\rtlch ");
+ break;
+ case '>': // bidi tag
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\ltrmark\\ltrch ");
+ break;
+ }
+ skip:
+ if (skipToNext) {
+ i++;
+ while (szTemplate[i] != '%' && i < iTemplateLen) i++;
+ } else
+ i += 2;
+ } else {
+ char temp[24];
+ mir_snprintf(temp, 24, "{\\uc1\\u%d?}", (int)ci);
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, temp);
+ i++;
+ }
+ }
+
+ if (dat->hHistoryEvents)
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, dat->szMicroLf, MSGDLGFONTCOUNT + 1 + ((isSent) ? 1 : 0), hDbEvent);
+
+ AppendToBuffer(&buffer, &bufferEnd, &bufferAlloced, "\\par");
+
+ if (streamData->dbei == 0)
+ free(dbei.pBlob);
+ HistoryEvents_ReleaseText(rtfMessage);
+
+ dat->iLastEventType = MAKELONG((dbei.flags & (DBEF_SENT | DBEF_READ | DBEF_RTL)), dbei.eventType);
+ dat->lastEventTime = dbei.timestamp;
+ return buffer;
+}
+
+static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
+{
+ struct LogStreamData *dat = (struct LogStreamData *) dwCookie;
+
+ if (dat->buffer == NULL) {
+ dat->bufferOffset = 0;
+ switch (dat->stage) {
+ case STREAMSTAGE_HEADER:
+ if (dat->buffer) free(dat->buffer);
+ dat->buffer = CreateRTFHeader(dat->dlgDat);
+ dat->stage = STREAMSTAGE_EVENTS;
+ break;
+ case STREAMSTAGE_EVENTS:
+ if (dat->eventsToInsert) {
+ do {
+ if (dat->buffer) free(dat->buffer);
+ dat->buffer = Template_CreateRTFFromDbEvent(dat->dlgDat, dat->hContact, dat->hDbEvent, !dat->isEmpty, dat);
+ if (dat->buffer)
+ dat->hDbEventLast = dat->hDbEvent;
+ dat->hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) dat->hDbEvent, 0);
+ if (--dat->eventsToInsert == 0)
+ break;
+ } while (dat->buffer == NULL && dat->hDbEvent);
+ if (dat->buffer) {
+ //dat->isEmpty = 0;
+ break;
+ }
+ }
+ dat->stage = STREAMSTAGE_TAIL;
+ //fall through
+ case STREAMSTAGE_TAIL: {
+ if (dat->buffer) free(dat->buffer);
+ dat->buffer = CreateRTFTail(dat->dlgDat);
+ dat->stage = STREAMSTAGE_STOP;
+ break;
+ }
+ case STREAMSTAGE_STOP:
+ *pcb = 0;
+ return 0;
+ }
+ dat->bufferLen = lstrlenA(dat->buffer);
+ }
+ *pcb = min(cb, dat->bufferLen - dat->bufferOffset);
+ CopyMemory(pbBuff, dat->buffer + dat->bufferOffset, *pcb);
+ dat->bufferOffset += *pcb;
+ if (dat->bufferOffset == dat->bufferLen) {
+ free(dat->buffer);
+ dat->buffer = NULL;
+ }
+ return 0;
+}
+
+static void SetupLogFormatting(struct TWindowData *dat)
+{
+ if (dat->hHistoryEvents) {
+ mir_snprintf(dat->szMicroLf, sizeof(dat->szMicroLf), "%s", "\\v\\cf%d \\ ~-+%d+-~\\v0 ");
+ } else {
+ mir_snprintf(dat->szMicroLf, sizeof(dat->szMicroLf), "%s\\par\\ltrpar\\sl-1%s ", GetRTFFont(MSGDLGFONTCOUNT), GetRTFFont(MSGDLGFONTCOUNT));
+ }
+}
+
+void TSAPI StreamInEvents(HWND hwndDlg, HANDLE hDbEventFirst, int count, int fAppend, DBEVENTINFO *dbei_s)
+{
+ EDITSTREAM stream = { 0 };
+ struct LogStreamData streamData = { 0 };
+ struct TWindowData *dat = (struct TWindowData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ CHARRANGE oldSel, sel;
+ HWND hwndrtf;
+ LONG startAt = 0;
+ FINDTEXTEXA fi;
+ struct tm tm_now, tm_today;
+ time_t now;
+ SCROLLINFO si = {0}, *psi = &si;
+ POINT pt = {0};
+ BOOL wasFirstAppend = (dat->isAutoRTL & 2) ? TRUE : FALSE;
+ BOOL isSent;
+
+
+ /*
+ * calc time limit for grouping
+ */
+
+ hwndrtf = dat->hwndIEView ? dat->hwndIWebBrowserControl : GetDlgItem(hwndDlg, IDC_LOG);
+
+ si.cbSize = sizeof(si);
+ /*
+ if (IsWindow(hwndrtf)) {
+ si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;;
+ GetScrollInfo(hwndrtf, SB_VERT, &si);
+ SendMessage(hwndrtf, EM_GETSCROLLPOS, 0, (LPARAM) &pt);
+
+ if (GetWindowLongPtr(hwndrtf, GWL_STYLE) & WS_VSCROLL)
+ psi = &si;
+ else
+ psi = NULL;
+ }
+ */
+
+ rtfFonts = dat->pContainer->theme.rtfFonts ? dat->pContainer->theme.rtfFonts : &(rtfFontsGlobal[0][0]);
+ now = time(NULL);
+
+ tm_now = *localtime(&now);
+ tm_today = tm_now;
+ tm_today.tm_hour = tm_today.tm_min = tm_today.tm_sec = 0;
+ today = mktime(&tm_today);
+
+ if (dat->hwndIEView != 0) {
+ IEVIEWEVENT event;
+
+ ZeroMemory(&event, sizeof(event));
+ event.cbSize = sizeof(IEVIEWEVENT);
+ event.hwnd = dat->hwndIEView;
+ event.hContact = dat->hContact;
+ event.dwFlags = (dat->dwFlags & MWF_LOG_RTL) ? IEEF_RTL : 0;
+ if (dat->sendMode & SMODE_FORCEANSI) {
+ event.dwFlags |= IEEF_NO_UNICODE;
+ event.codepage = dat->codePage;
+ } else
+ event.codepage = 0;
+ if (!fAppend) {
+ event.iType = IEE_CLEAR_LOG;
+ CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+ }
+ event.iType = IEE_LOG_DB_EVENTS;
+ event.hDbEventFirst = hDbEventFirst;
+ event.count = count;
+ event.pszProto = dat->szProto;
+ CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+ DM_ScrollToBottom(dat, 0, 0);
+ if (fAppend)
+ dat->hDbEventLast = hDbEventFirst;
+ else
+ dat->hDbEventLast = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)dat->hContact, 0);
+ return;
+ }
+ if (dat->hwndHPP != 0) {
+ IEVIEWEVENT event;
+
+ event.cbSize = sizeof(IEVIEWEVENT);
+ event.hwnd = dat->hwndHPP;
+ event.hContact = dat->hContact;
+ event.dwFlags = (dat->dwFlags & MWF_LOG_RTL) ? IEEF_RTL : 0;
+ if (dat->sendMode & SMODE_FORCEANSI) {
+ event.dwFlags |= IEEF_NO_UNICODE;
+ event.codepage = dat->codePage;
+ } else
+ event.codepage = 0;
+ if (!fAppend) {
+ event.iType = IEE_CLEAR_LOG;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ }
+ event.iType = IEE_LOG_DB_EVENTS;
+ event.hDbEventFirst = hDbEventFirst;
+ event.count = count;
+ CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event);
+ //SendMessage(hwndDlg, DM_FORCESCROLL, (WPARAM)&pt, (LPARAM)&si);
+ DM_ScrollToBottom(dat, 0, 0);
+ if (fAppend)
+ dat->hDbEventLast = hDbEventFirst;
+ else
+ dat->hDbEventLast = (HANDLE)CallService(MS_DB_EVENT_FINDLAST, (WPARAM)dat->hContact, 0);
+ return;
+ }
+
+ // separator strings used for grid lines, message separation and so on...
+
+ dat->clr_added = FALSE;
+
+ if (dat->szMicroLf[0] == 0)
+ SetupLogFormatting(dat);
+
+ szYourName = const_cast<TCHAR *>(dat->cache->getNick());
+ szMyName = dat->szMyNickname;
+
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_HIDESELECTION, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM) & oldSel);
+ streamData.hContact = dat->hContact;
+ streamData.hDbEvent = hDbEventFirst;
+ streamData.dlgDat = dat;
+ streamData.eventsToInsert = count;
+ streamData.isEmpty = fAppend ? GetWindowTextLength(GetDlgItem(hwndDlg, IDC_LOG)) == 0 : 1;
+ streamData.dbei = dbei_s;
+ stream.pfnCallback = LogStreamInEvents;
+ stream.dwCookie = (DWORD_PTR) & streamData;
+ streamData.isAppend = fAppend;
+
+ if (fAppend) {
+ GETTEXTLENGTHEX gtxl = {0};
+ gtxl.codepage = 1200;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMCHARS;
+ fi.chrg.cpMin = SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETTEXTLENGTHEX, (WPARAM) & gtxl, 0);
+ sel.cpMin = sel.cpMax = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_LOG));
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & sel);
+ } else {
+ SetDlgItemText(hwndDlg, IDC_LOG, _T(""));
+ sel.cpMin = 0;
+ sel.cpMax = GetWindowTextLength(hwndrtf);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) &sel);
+ fi.chrg.cpMin = 0;
+ dat->isAutoRTL = 0;
+ }
+ startAt = fi.chrg.cpMin;
+
+ SendMessage(hwndrtf, WM_SETREDRAW, FALSE, 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_STREAMIN, fAppend ? SFF_SELECTION | SF_RTF : SFF_SELECTION | SF_RTF, (LPARAM) & stream);
+ //SendDlgItemMessage(hwndDlg, IDC_LOG, EM_STREAMIN, fAppend ? SFF_SELECTION | SF_RTF : SF_RTF, (LPARAM) & stream);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & oldSel);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_HIDESELECTION, FALSE, 0);
+ dat->hDbEventLast = streamData.hDbEventLast;
+
+ if (dat->isAutoRTL & 1) {
+ SendMessage(hwndrtf, EM_SETBKGNDCOLOR, 0, LOWORD(dat->iLastEventType) & DBEF_SENT ? (fAppend?dat->pContainer->theme.outbg : dat->pContainer->theme.oldoutbg) :
+ (fAppend?dat->pContainer->theme.inbg : dat->pContainer->theme.oldinbg));
+ }
+
+ if (!(dat->isAutoRTL & 1)) {
+ GETTEXTLENGTHEX gtxl = {0};
+ PARAFORMAT2 pf2;
+
+ gtxl.codepage = 1200;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMCHARS;
+ ZeroMemory(&pf2, sizeof(PARAFORMAT2));
+ sel.cpMax = SendDlgItemMessage(hwndDlg, IDC_LOG, EM_GETTEXTLENGTHEX, (WPARAM) & gtxl, 0);
+ sel.cpMin = sel.cpMax - 1;
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & sel);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, EM_REPLACESEL, FALSE, (LPARAM)_T(""));
+ dat->isAutoRTL |= 2;
+ }
+
+ if (streamData.dbei != 0)
+ isSent = (streamData.dbei->flags & DBEF_SENT) != 0;
+ else {
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ CallService(MS_DB_EVENT_GET, (WPARAM) hDbEventFirst, (LPARAM)&dbei);
+ isSent = (dbei.flags & DBEF_SENT) != 0;
+ }
+
+ ReplaceIcons(hwndDlg, dat, startAt, fAppend, isSent);
+ dat->clr_added = FALSE;
+
+ SendMessage(hwndDlg, DM_FORCESCROLL, (WPARAM)&pt, (LPARAM)psi);
+ SendDlgItemMessage(hwndDlg, IDC_LOG, WM_SETREDRAW, TRUE, 0);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_LOG), NULL, FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_QUOTE), dat->hDbEventLast != NULL);
+ if (streamData.buffer) free(streamData.buffer);
+}
+
+static void ReplaceIcons(HWND hwndDlg, struct TWindowData *dat, LONG startAt, int fAppend, BOOL isSent)
+{
+ FINDTEXTEXA fi;
+ CHARFORMAT2 cf2;
+ HWND hwndrtf;
+ IRichEditOle *ole;
+ TEXTRANGEA tr;
+ COLORREF crDefault;
+ struct TLogIcon theIcon;
+ char trbuffer[40];
+ DWORD dwScale = M->GetDword("iconscale", 0);
+ tr.lpstrText = trbuffer;
+
+ hwndrtf = GetDlgItem(hwndDlg, IDC_LOG);
+ fi.chrg.cpMin = startAt;
+ if (dat->clr_added) {
+ unsigned int length;
+ int index;
+ CHARRANGE cr;
+ fi.lpstrText = "##col##";
+ fi.chrg.cpMax = -1;
+ ZeroMemory((void *)&cf2, sizeof(cf2));
+ cf2.cbSize = sizeof(cf2);
+ cf2.dwMask = CFM_COLOR;
+ while (SendMessageA(hwndrtf, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) > -1) {
+ tr.chrg.cpMin = fi.chrgText.cpMin;
+ tr.chrg.cpMax = tr.chrg.cpMin + 18;
+ trbuffer[0] = 0;
+ SendMessageA(hwndrtf, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ trbuffer[18] = 0;
+ cr.cpMin = fi.chrgText.cpMin;
+ cr.cpMax = cr.cpMin + 18;
+ SendMessage(hwndrtf, EM_EXSETSEL, 0, (LPARAM)&cr);
+ SendMessageA(hwndrtf, EM_REPLACESEL, FALSE, (LPARAM)"");
+ length = (unsigned int)atol(&trbuffer[7]);
+ index = atol(&trbuffer[14]);
+ if (length > 0 && length < 20000 && index >= RTF_CTABLE_DEFSIZE && index < Utils::rtf_ctable_size) {
+ cf2.crTextColor = Utils::rtf_ctable[index].clr;
+ cr.cpMin = fi.chrgText.cpMin;
+ cr.cpMax = cr.cpMin + length;
+ SendMessage(hwndrtf, EM_EXSETSEL, 0, (LPARAM)&cr);
+ SendMessage(hwndrtf, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
+ }
+ }
+ }
+ fi.chrg.cpMin = startAt;
+ if (dat->dwFlags & MWF_LOG_SHOWICONS) {
+ BYTE bIconIndex = 0;
+ char bDirection = 0;
+ CHARRANGE cr;
+ fi.lpstrText = "#~#";
+ fi.chrg.cpMax = -1;
+ ZeroMemory((void *)&cf2, sizeof(cf2));
+ cf2.cbSize = sizeof(cf2);
+ cf2.dwMask = CFM_BACKCOLOR;
+
+ SendMessage(hwndrtf, EM_GETOLEINTERFACE, 0, (LPARAM)&ole);
+ while (SendMessageA(hwndrtf, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) > -1) {
+ cr.cpMin = fi.chrgText.cpMin;
+ cr.cpMax = fi.chrgText.cpMax + 2;
+ SendMessage(hwndrtf, EM_EXSETSEL, 0, (LPARAM)&cr);
+
+ tr.chrg.cpMin = fi.chrgText.cpMin + 3;
+ tr.chrg.cpMax = fi.chrgText.cpMin + 5;
+ SendMessageA(hwndrtf, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ bIconIndex = ((BYTE)trbuffer[0] - (BYTE)'0');
+ if (bIconIndex >= NR_LOGICONS) {
+ fi.chrg.cpMin = fi.chrgText.cpMax + 6;
+ continue;
+ }
+ bDirection = trbuffer[1];
+ SendMessage(hwndrtf, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
+ crDefault = cf2.crBackColor == 0 ? (true ? (bDirection == '>' ? (fAppend ? dat->pContainer->theme.outbg : dat->pContainer->theme.oldoutbg) :
+ (fAppend ? dat->pContainer->theme.inbg : dat->pContainer->theme.oldinbg)) : dat->pContainer->theme.bg) : cf2.crBackColor;
+ CacheIconToBMP(&theIcon, Logicons[bIconIndex], crDefault, dwScale, dwScale);
+ ImageDataInsertBitmap(ole, theIcon.hBmp);
+ DeleteCachedIcon(&theIcon);
+ fi.chrg.cpMin = cr.cpMax + 6;
+ }
+ ReleaseRichEditOle(ole);
+ }
+ /*
+ * do smiley replacing, using the service
+ */
+
+ if (PluginConfig.g_SmileyAddAvail) {
+ CHARRANGE sel;
+ SMADD_RICHEDIT3 smadd;
+
+ sel.cpMin = startAt;
+ sel.cpMax = -1;
+
+ ZeroMemory(&smadd, sizeof(smadd));
+
+ smadd.cbSize = sizeof(smadd);
+ smadd.hwndRichEditControl = GetDlgItem(hwndDlg, IDC_LOG);
+ smadd.Protocolname = const_cast<char *>(dat->cache->getActiveProto());
+ smadd.hContact = dat->cache->getActiveContact();
+ smadd.flags = isSent ? SAFLRE_OUTGOING : 0;
+
+ if (startAt > 0)
+ smadd.rangeToReplace = &sel;
+ else
+ smadd.rangeToReplace = NULL;
+ smadd.disableRedraw = TRUE;
+ if (dat->doSmileys)
+ CallService(MS_SMILEYADD_REPLACESMILEYS, TABSRMM_SMILEYADD_BKGCOLORMODE, (LPARAM)&smadd);
+ }
+
+ if (PluginConfig.m_MathModAvail) {
+ TMathRicheditInfo mathReplaceInfo;
+ CHARRANGE mathNewSel;
+ mathNewSel.cpMin = startAt;
+ mathNewSel.cpMax = -1;
+ mathReplaceInfo.hwndRichEditControl = GetDlgItem(hwndDlg, IDC_LOG);
+ if (startAt > 0) mathReplaceInfo.sel = & mathNewSel;
+ else mathReplaceInfo.sel = 0;
+ mathReplaceInfo.disableredraw = TRUE;
+ CallService(MATH_RTF_REPLACE_FORMULAE, 0, (LPARAM)&mathReplaceInfo);
+ }
+
+ if (dat->hHistoryEvents && dat->curHistory == dat->maxHistory) {
+ char szPattern[50];
+ FINDTEXTEXA fi;
+
+ _snprintf(szPattern, 40, "~-+%d+-~", (INT_PTR)dat->hHistoryEvents[0]);
+ fi.lpstrText = szPattern;
+ fi.chrg.cpMin = 0;
+ fi.chrg.cpMax = -1;
+ if (SendMessageA(hwndrtf, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) != 0) {
+ CHARRANGE sel;
+ sel.cpMin = 0;
+ sel.cpMax = 20;
+ SendMessage(hwndrtf, EM_SETSEL, 0, fi.chrgText.cpMax + 1);
+ SendMessageA(hwndrtf, EM_REPLACESEL, TRUE, (LPARAM)"");
+ }
+ }
+}
+
+/*
+ * NLS functions (for unicode version only) encoding stuff..
+ */
+
+static BOOL CALLBACK LangAddCallback(LPTSTR str)
+{
+ int i, count;
+ UINT cp;
+
+ cp = _ttoi(str);
+ count = sizeof(cpTable) / sizeof(cpTable[0]);
+ for (i = 0; i < count && cpTable[i].cpId != cp; i++);
+ if (i < count) {
+ AppendMenu(PluginConfig.g_hMenuEncoding, MF_STRING, cp, TranslateTS(cpTable[i].cpName));
+ }
+ return TRUE;
+}
+
+void TSAPI BuildCodePageList()
+{
+ PluginConfig.g_hMenuEncoding = CreateMenu();
+ AppendMenu(PluginConfig.g_hMenuEncoding, MF_STRING, 500, CTranslator::get(CTranslator::GEN_LOG_USEDEFAULTCP));
+ AppendMenuA(PluginConfig.g_hMenuEncoding, MF_SEPARATOR, 0, 0);
+ EnumSystemCodePages(LangAddCallback, CP_INSTALLED);
+}
+
+static TCHAR *Template_MakeRelativeDate(struct TWindowData *dat, HANDLE hTimeZone, time_t check, int groupBreak, TCHAR code)
+{
+ static TCHAR szResult[100];
+ const TCHAR *szFormat;
+
+ if ((code == (TCHAR)'R' || code == (TCHAR)'r') && check >= today) {
+ _tcscpy(szResult, szToday);
+ } else if ((code == (TCHAR)'R' || code == (TCHAR)'r') && check > (today - 86400)) {
+ _tcscpy(szResult, szYesterday);
+ } else {
+ if (code == 'D' || code == 'R')
+ szFormat = _T("D");
+ else if (code == 'T')
+ szFormat = _T("s");
+ else if (code == 't')
+ szFormat = _T("t");
+ else
+ szFormat = _T("d");
+
+ tmi.printTimeStamp(hTimeZone, check, szFormat, szResult, safe_sizeof(szResult), 0);
+ }
+ return szResult;
+}
+
diff --git a/plugins/TabSRMM/src/msgoptions.cpp b/plugins/TabSRMM/src/msgoptions.cpp new file mode 100644 index 0000000000..7438925df1 --- /dev/null +++ b/plugins/TabSRMM/src/msgoptions.cpp @@ -0,0 +1,1787 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: msgoptions.cpp 13750 2011-08-03 20:10:43Z george.hazan $
+ *
+ * Implementation of the option pages
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+#include <m_modernopt.h>
+
+#define DM_GETSTATUSMASK (WM_USER + 10)
+
+extern INT_PTR CALLBACK DlgProcPopupOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK DlgProcTabConfig(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK DlgProcTemplateEditor(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK DlgProcToolBar(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK PlusOptionsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+extern INT_PTR CALLBACK DlgProcOptions1(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK DlgProcOptions2(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK DlgProcOptions3(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+INT_PTR CALLBACK DlgProcSetupStatusModes(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+struct FontOptionsList {
+ COLORREF defColour;
+ char *szDefFace;
+ BYTE defStyle;
+ char defSize;
+}
+
+static fontOptionsList[] = {
+ {RGB(0, 0, 0), "Tahoma", 0, -10}
+};
+
+
+static HIMAGELIST g_himlStates = 0;
+HIMAGELIST CreateStateImageList()
+{
+ if (g_himlStates == 0) {
+ g_himlStates = ImageList_Create(16, 16, PluginConfig.m_bIsXP ? ILC_COLOR32 | ILC_MASK : ILC_COLOR8 | ILC_MASK, 4, 0);
+ ImageList_AddIcon(g_himlStates, PluginConfig.g_IconFolder);
+ ImageList_AddIcon(g_himlStates, PluginConfig.g_IconFolder);
+ ImageList_AddIcon(g_himlStates, PluginConfig.g_IconUnchecked);
+ ImageList_AddIcon(g_himlStates, PluginConfig.g_IconChecked);
+ }
+ return g_himlStates;
+}
+
+
+static BYTE MsgDlgGetFontDefaultCharset(const char* szFont)
+{
+ return DEFAULT_CHARSET;
+}
+
+void TSAPI LoadLogfont(int i, LOGFONTA * lf, COLORREF * colour, char *szModule)
+{
+ LOGFONT lfResult;
+ LoadMsgDlgFont((i < 100) ? FONTSECTION_IM : FONTSECTION_IP, i, &lfResult, colour, szModule);
+ if (lf)
+ {
+ lf->lfHeight = lfResult.lfHeight;
+ lf->lfWidth = lfResult.lfWidth;
+ lf->lfEscapement = lfResult.lfEscapement;
+ lf->lfOrientation = lfResult.lfOrientation;
+ lf->lfWeight = lfResult.lfWeight;
+ lf->lfItalic = lfResult.lfItalic;
+ lf->lfUnderline = lfResult.lfUnderline;
+ lf->lfStrikeOut = lfResult.lfStrikeOut;
+ lf->lfCharSet = lfResult.lfCharSet;
+ lf->lfOutPrecision = lfResult.lfOutPrecision;
+ lf->lfClipPrecision = lfResult.lfClipPrecision;
+ lf->lfQuality = lfResult.lfQuality;
+ lf->lfPitchAndFamily = lfResult.lfPitchAndFamily;
+ wsprintfA(lf->lfFaceName, "%S", lfResult.lfFaceName);
+ }
+}
+
+HIMAGELIST g_himlOptions;
+
+static HWND hwndTabConfig = 0;
+
+/**
+ * scan a single skin directory and find the .TSK file. Fill the combobox and set the
+ * relative path name as item extra data.
+ *
+ * If available, read the Name property from the [Global] section and use it in the
+ * combo box. If such property is not found, the base filename (without .tsk extension)
+ * will be used as the name of the skin.
+ *
+ * [Global]/Name property is new in TabSRMM version 3.
+ */
+static int TSAPI ScanSkinDir(const TCHAR* tszFolder, HWND hwndCombobox)
+{
+ WIN32_FIND_DATA fd = {0};
+ bool fValid = false;
+ TCHAR tszMask[MAX_PATH];
+
+ mir_sntprintf(tszMask, MAX_PATH, _T("%s*.*"), tszFolder);
+
+ HANDLE h = FindFirstFile(tszMask, &fd);
+
+ while(h != INVALID_HANDLE_VALUE) {
+ if(lstrlen(fd.cFileName) >= 5 && !_tcsnicmp(fd.cFileName + lstrlen(fd.cFileName) - 4, _T(".tsk"), 4)) {
+ fValid = true;
+ break;
+ }
+ if(FindNextFile(h, &fd) == 0)
+ break;
+ }
+ if(h != INVALID_HANDLE_VALUE)
+ FindClose(h);
+
+ if(fValid) {
+ TCHAR tszFinalName[MAX_PATH], tszRel[MAX_PATH];
+ LRESULT lr;
+ TCHAR szBuf[255];
+
+ mir_sntprintf(tszFinalName, MAX_PATH, _T("%s%s"), tszFolder, fd.cFileName);
+
+ GetPrivateProfileString(_T("Global"), _T("Name"), _T("None"), szBuf, 500, tszFinalName);
+ if(!_tcscmp(szBuf, _T("None"))) {
+ fd.cFileName[lstrlen(fd.cFileName) - 4] = 0;
+ mir_sntprintf(szBuf, 255, _T("%s"), fd.cFileName);
+ }
+
+ M->pathToRelative(tszFinalName, tszRel, M->getSkinPath());
+ if((lr = SendMessage(hwndCombobox, CB_INSERTSTRING, -1, (LPARAM)szBuf)) != CB_ERR) {
+ TCHAR *idata = (TCHAR *)malloc((lstrlen(tszRel) + 1) * sizeof(TCHAR));
+
+ _tcscpy(idata, tszRel);
+ SendMessage(hwndCombobox, CB_SETITEMDATA, lr, (LPARAM)idata);
+ }
+ }
+ return(0);
+}
+
+/**
+ * scan the skin root folder for subfolder(s). Each folder is supposed to contain a single
+ * skin. This function won't dive deeper into the folder structure, so the folder
+ * structure for any VALID skin should be:
+ * $SKINS_ROOT/skin_folder/skin_name.tsk
+ *
+ * By default, $SKINS_ROOT is set to %miranda_userdata% or custom folder
+ * selected by the folders plugin.
+ */
+static int TSAPI RescanSkins(HWND hwndCombobox)
+{
+ WIN32_FIND_DATA fd = {0};
+ TCHAR tszSkinRoot[MAX_PATH], tszFindMask[MAX_PATH];
+ DBVARIANT dbv = {0};
+
+
+ mir_sntprintf(tszSkinRoot, MAX_PATH, _T("%s"), M->getSkinPath());
+
+ SetDlgItemText(GetParent(hwndCombobox), IDC_SKINROOTFOLDER, tszSkinRoot);
+ mir_sntprintf(tszFindMask, MAX_PATH, _T("%s*.*"), tszSkinRoot);
+
+ SendMessage(hwndCombobox, CB_RESETCONTENT, 0, 0);
+ SendMessage(hwndCombobox, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_SKIN_NOSKINSELECT));
+
+ HANDLE h = FindFirstFile(tszFindMask, &fd);
+ while (h != INVALID_HANDLE_VALUE) {
+ if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && fd.cFileName[0] != '.') {
+ TCHAR tszSubDir[MAX_PATH];
+ mir_sntprintf(tszSubDir, MAX_PATH, _T("%s%s\\"), tszSkinRoot, fd.cFileName);
+ ScanSkinDir(tszSubDir, hwndCombobox);
+ }
+ if(FindNextFile(h, &fd) == 0)
+ break;
+ }
+ if(h != INVALID_HANDLE_VALUE)
+ FindClose(h);
+
+
+ SendMessage(hwndCombobox, CB_SETCURSEL, 0, 0);
+ if(0 == M->GetTString(0, SRMSGMOD_T, "ContainerSkin", &dbv)) {
+ LRESULT lr = SendMessage(hwndCombobox, CB_GETCOUNT, 0, 0);
+ for(int i = 1; i < lr; i++) {
+
+ TCHAR* idata = (TCHAR *)SendMessage(hwndCombobox, CB_GETITEMDATA, i, 0);
+ if(idata && idata != (TCHAR *)CB_ERR) {
+ if(!_tcsicmp(dbv.ptszVal, idata)) {
+ SendMessage(hwndCombobox, CB_SETCURSEL, i, 0);
+ break;
+ }
+ }
+ }
+ DBFreeVariant(&dbv);
+ }
+ return(0);
+}
+
+/**
+ * free the item extra data (used to store the skin filenames for
+ * each entry).
+ */
+static void TSAPI FreeComboData(HWND hwndCombobox)
+{
+ LRESULT lr = SendMessage(hwndCombobox, CB_GETCOUNT, 0, 0);
+
+ for(int i = 1; i < lr; i++) {
+ void *idata = (void *)SendMessage(hwndCombobox, CB_GETITEMDATA, i, 0);
+
+ if(idata && idata != (void *)CB_ERR)
+ free(idata);
+ }
+}
+
+/*
+ * controls to disable when loading or unloading a skin is not possible (because
+ * of at least one message window being open).
+ */
+static UINT _ctrls[] = { IDC_SKINNAME, IDC_RESCANSKIN, IDC_RESCANSKIN, IDC_RELOADSKIN, 0 };
+
+static INT_PTR CALLBACK DlgProcSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG: {
+ RescanSkins(GetDlgItem(hwndDlg, IDC_SKINNAME));
+ BYTE loadMode = M->GetByte("skin_loadmode", 0);
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_USESKIN, M->GetByte("useskin", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SKIN_LOADFONTS, loadMode & THEME_READ_FONTS);
+ CheckDlgButton(hwndDlg, IDC_SKIN_LOADTEMPLATES, loadMode & THEME_READ_TEMPLATES);
+
+ SendMessage(hwndDlg, WM_USER + 100, 0, 0);
+ SetTimer(hwndDlg, 1000, 100, 0);
+ return TRUE;
+ }
+
+ case WM_CTLCOLORSTATIC:
+ if((HWND)lParam == GetDlgItem(hwndDlg, IDC_SKIN_WARN)) {
+ SetTextColor((HDC)wParam, RGB(255, 50, 50));
+ return(0);
+ }
+ break;
+
+ /*
+ * self - configure the dialog, don't let the user load or unload
+ * a skin while a message window is open. Show the warning that all
+ * windows must be closed.
+ */
+ case WM_USER + 100: {
+ bool fWindowsOpen = (pFirstContainer != 0 ? true : false);
+ UINT i = 0;
+
+ while(_ctrls[i]) {
+ Utils::enableDlgControl(hwndDlg, _ctrls[i], fWindowsOpen ? FALSE : TRUE);
+ i++;
+ }
+ Utils::showDlgControl(hwndDlg, IDC_SKIN_WARN, fWindowsOpen ? SW_SHOW : SW_HIDE);
+ Utils::showDlgControl(hwndDlg, IDC_SKIN_CLOSENOW, fWindowsOpen ? SW_SHOW : SW_HIDE);
+ return(0);
+ }
+
+ case WM_TIMER:
+ if(wParam == 1000)
+ SendMessage(hwndDlg, WM_USER + 100, 0, 0);
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_USESKIN:
+ M->WriteByte(SRMSGMOD_T, "useskin", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_USESKIN) ? 1 : 0));
+ break;
+
+ case IDC_SKIN_LOADFONTS: {
+ BYTE loadMode = M->GetByte("skin_loadmode", 0);
+ loadMode = IsDlgButtonChecked(hwndDlg, IDC_SKIN_LOADFONTS) ? loadMode | THEME_READ_FONTS : loadMode & ~THEME_READ_FONTS;
+ M->WriteByte(SRMSGMOD_T, "skin_loadmode", loadMode);
+ break;
+ }
+
+ case IDC_SKIN_LOADTEMPLATES: {
+ BYTE loadMode = M->GetByte("skin_loadmode", 0);
+ loadMode = IsDlgButtonChecked(hwndDlg, IDC_SKIN_LOADTEMPLATES) ? loadMode | THEME_READ_TEMPLATES : loadMode & ~THEME_READ_TEMPLATES;
+ M->WriteByte(SRMSGMOD_T, "skin_loadmode", loadMode);
+ break;
+ }
+
+ case IDC_UNLOAD:
+ Skin->Unload();
+ SendMessage(hwndTabConfig, WM_USER + 100, 0, 0);
+ break;
+
+ case IDC_RELOADSKIN:
+ Skin->setFileName();
+ Skin->Load();
+ SendMessage(hwndTabConfig, WM_USER + 100, 0, 0);
+ break;
+
+ case IDC_RESCANSKIN:
+ FreeComboData(GetDlgItem(hwndDlg, IDC_SKINNAME));
+ RescanSkins(GetDlgItem(hwndDlg, IDC_SKINNAME));
+ break;
+
+ case IDC_THEMEEXPORT: {
+ const TCHAR *szFilename = GetThemeFileName(1);
+ if (szFilename != NULL)
+ WriteThemeToINI(szFilename, 0);
+ break;
+ }
+
+ case IDC_THEMEIMPORT: {
+ LRESULT r;
+
+ if(CSkin::m_skinEnabled) {
+ r = CWarning::show(CWarning::WARN_THEME_OVERWRITE, MB_YESNOCANCEL|MB_ICONQUESTION);
+ if(r == IDNO || r == IDCANCEL)
+ return(0);
+ }
+
+ r = CWarning::show(CWarning::WARN_OPTION_CLOSE, MB_YESNOCANCEL|MB_ICONQUESTION);
+ if(r == IDNO || r == IDCANCEL)
+ return(0);
+
+ const wchar_t* szFilename = GetThemeFileName(0);
+ DWORD dwFlags = THEME_READ_FONTS;
+ int result;
+
+ if (szFilename != NULL) {
+ result = MessageBox(0, CTranslator::get(CTranslator::GEN_WARNING_LOADTEMPLATES),
+ CTranslator::get(CTranslator::GEN_TITLE_LOADTHEME), MB_YESNOCANCEL);
+ if (result == IDCANCEL)
+ return 1;
+ else if (result == IDYES)
+ dwFlags |= THEME_READ_TEMPLATES;
+ ReadThemeFromINI(szFilename, 0, 0, dwFlags);
+ CacheLogFonts();
+ CacheMsgLogIcons();
+ PluginConfig.reloadSettings();
+ CSkin::setAeroEffect(-1);
+ M->BroadcastMessage(DM_OPTIONSAPPLIED, 1, 0);
+ M->BroadcastMessage(DM_FORCEDREMAKELOG, 0, 0);
+ SendMessage(GetParent(hwndDlg), WM_COMMAND, IDCANCEL, 0);
+ }
+ break;
+ }
+
+ case IDC_HELP_GENERAL:
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://blog.miranda.or.at/tabsrmm/skin-selection-changes/");
+ break;
+
+ case IDC_SKIN_CLOSENOW: {
+ BOOL fOldHideSetting = PluginConfig.m_HideOnClose;
+
+ PluginConfig.m_HideOnClose = FALSE;
+
+ while(pFirstContainer) {
+ if(pFirstContainer->hwnd)
+ SendMessage(pFirstContainer->hwnd, WM_CLOSE, 0, 1);
+ }
+
+ PluginConfig.m_HideOnClose = fOldHideSetting;
+ break;
+ }
+ case IDC_SKINNAME: {
+ if(HIWORD(wParam) == CBN_SELCHANGE) {
+ LRESULT lr = SendDlgItemMessage(hwndDlg, IDC_SKINNAME, CB_GETCURSEL, 0 ,0);
+ if(lr != CB_ERR && lr > 0) {
+ TCHAR *tszRelPath = (TCHAR *)SendDlgItemMessage(hwndDlg, IDC_SKINNAME, CB_GETITEMDATA, lr, 0);
+ if(tszRelPath && tszRelPath != (TCHAR *)CB_ERR)
+ M->WriteTString(0, SRMSGMOD_T, "ContainerSkin", tszRelPath);
+ SendMessage(hwndDlg, WM_COMMAND, IDC_RELOADSKIN, 0);
+ }
+ else if(lr == 0) { // selected the <no skin> entry
+ DBDeleteContactSetting(0, SRMSGMOD_T, "ContainerSkin");
+ Skin->Unload();
+ SendMessage(hwndTabConfig, WM_USER + 100, 0, 0);
+ }
+ return(0);
+ }
+ break;
+ }
+ }
+ if ((LOWORD(wParam) == IDC_SKINNAME)
+ && (HIWORD(wParam) != CBN_SELCHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ KillTimer(hwndDlg, 1000);
+ FreeComboData(GetDlgItem(hwndDlg, IDC_SKINNAME));
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProcOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG: {
+ BOOL translated;
+ TVINSERTSTRUCT tvi = {0};
+ int i = 0;
+
+ DWORD dwFlags = DBGetContactSettingDword(NULL, SRMSGMOD_T, "mwflags", MWF_LOG_DEFAULT);
+
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_WINDOWOPTIONS), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_WINDOWOPTIONS), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+
+ g_himlOptions = (HIMAGELIST)SendDlgItemMessage(hwndDlg, IDC_WINDOWOPTIONS, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)CreateStateImageList());
+ ImageList_Destroy(g_himlOptions);
+
+ /*
+ * fill the list box, create groups first, then add items
+ */
+
+ TOptionListGroup *defaultGroups = CTranslator::getGroupTree(CTranslator::TREE_MSG);
+
+ while (defaultGroups[i].szName != NULL) {
+ tvi.hParent = 0;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE;
+ tvi.item.pszText = defaultGroups[i].szName;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_EXPANDED | TVIS_BOLD;
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_EXPANDED | TVIS_BOLD;
+ defaultGroups[i++].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hwndDlg, IDC_WINDOWOPTIONS), &tvi);
+ }
+
+ i = 0;
+
+ TOptionListItem *defaultItems = CTranslator::getTree(CTranslator::TREE_MSG);
+
+ while (defaultItems[i].szName != 0) {
+ tvi.hParent = (HTREEITEM)defaultGroups[defaultItems[i].uGroup].handle;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.pszText = defaultItems[i].szName;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+ tvi.item.lParam = i;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK;
+ if (defaultItems[i].uType == LOI_TYPE_SETTING)
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(M->GetByte((char *)defaultItems[i].lParam, (BYTE)defaultItems[i].id) ? 3 : 2);
+ defaultItems[i].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hwndDlg, IDC_WINDOWOPTIONS), &tvi);
+ i++;
+ }
+
+ SetDlgItemInt(hwndDlg, IDC_MAXAVATARHEIGHT, M->GetDword("avatarheight", 100), FALSE);
+ CheckDlgButton(hwndDlg, IDC_PRESERVEAVATARSIZE, M->GetByte("dontscaleavatars", 0) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_AVATARSPIN, UDM_SETRANGE, 0, MAKELONG(150, 0));
+ SendDlgItemMessage(hwndDlg, IDC_AVATARSPIN, UDM_SETPOS, 0, GetDlgItemInt(hwndDlg, IDC_MAXAVATARHEIGHT, &translated, FALSE));
+ return TRUE;
+ }
+ case WM_DESTROY:
+ //ImageList_Destroy((HIMAGELIST)SendDlgItemMessage(hwndDlg, IDC_WINDOWOPTIONS, TVM_GETIMAGELIST, 0, 0));
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_MAXAVATARHEIGHT: {
+
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ break;
+ }
+ case IDC_HELP_GENERAL:
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://wiki.miranda.or.at/TabSRMM/GeneralSettings");
+ break;
+ case IDC_RESETWARNINGS:
+ M->WriteDword(SRMSGMOD_T, "cWarningsL", 0);
+ M->WriteDword(SRMSGMOD_T, "cWarningsH", 0);
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case IDC_WINDOWOPTIONS:
+ if (((LPNMHDR)lParam)->code == NM_CLICK || (((LPNMHDR)lParam)->code == TVN_KEYDOWN && ((LPNMTVKEYDOWN)lParam)->wVKey == VK_SPACE)) {
+ TVHITTESTINFO hti;
+ TVITEM item = {0};
+
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.stateMask = TVIS_STATEIMAGEMASK | TVIS_BOLD;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti) || ((LPNMHDR)lParam)->code == TVN_KEYDOWN) {
+ if (((LPNMHDR)lParam)->code == TVN_KEYDOWN) {
+ hti.flags |= TVHT_ONITEMSTATEICON;
+ item.hItem = TreeView_GetSelection(((LPNMHDR)lParam)->hwndFrom);
+ } else
+ item.hItem = (HTREEITEM)hti.hItem;
+ SendDlgItemMessageA(hwndDlg, IDC_WINDOWOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (item.state & TVIS_BOLD && hti.flags & TVHT_ONITEMSTATEICON) {
+ item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_BOLD;
+ SendDlgItemMessageA(hwndDlg, IDC_WINDOWOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ } else if (hti.flags & TVHT_ONITEMSTATEICON) {
+
+ if (((item.state & TVIS_STATEIMAGEMASK) >> 12) == 3) {
+ item.state = INDEXTOSTATEIMAGEMASK(1);
+ SendDlgItemMessageA(hwndDlg, IDC_WINDOWOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY: {
+ BOOL translated;
+ TVITEM item = {0};
+ int i = 0;
+
+ M->WriteDword(SRMSGMOD_T, "avatarheight", GetDlgItemInt(hwndDlg, IDC_MAXAVATARHEIGHT, &translated, FALSE));
+
+ M->WriteByte(SRMSGMOD_T, "dontscaleavatars", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_PRESERVEAVATARSIZE) ? 1 : 0));
+
+ /*
+ * scan the tree view and obtain the options...
+ */
+
+ TOptionListItem *defaultItems = CTranslator::getTree(CTranslator::TREE_MSG);
+
+ while (defaultItems[i].szName != NULL) {
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.hItem = (HTREEITEM)defaultItems[i].handle;
+ item.stateMask = TVIS_STATEIMAGEMASK;
+
+ SendDlgItemMessageA(hwndDlg, IDC_WINDOWOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (defaultItems[i].uType == LOI_TYPE_SETTING)
+ M->WriteByte(SRMSGMOD_T, (char *)defaultItems[i].lParam, (BYTE)((item.state >> 12) == 3 ? 1 : 0));
+ //pMim->WriteByte(SRMSGMOD_T, (char *)defaultItems[i].lParam, (BYTE)((item.state >> 12) == 2 ? 1 : 0));
+ i++;
+ }
+ PluginConfig.reloadSettings();
+ M->BroadcastMessage(DM_OPTIONSAPPLIED, 1, 0);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+static int have_ieview = 0, have_hpp = 0;
+
+static UINT __ctrls[] = { IDC_INDENTSPIN, IDC_RINDENTSPIN, IDC_INDENTAMOUNT, IDC_RIGHTINDENT,
+ IDC_MODIFY, IDC_RTLMODIFY };
+
+static INT_PTR CALLBACK DlgProcLogOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL translated;
+ DWORD dwFlags = M->GetDword("mwflags", MWF_LOG_DEFAULT);
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TVINSERTSTRUCT tvi = {0};
+ int i = 0;
+ DWORD maxhist = M->GetDword("maxhist", 0);
+
+ TranslateDialogDefault(hwndDlg);
+ switch (M->GetByte(SRMSGMOD, SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY)) {
+ case LOADHISTORY_UNREAD:
+ CheckDlgButton(hwndDlg, IDC_LOADUNREAD, BST_CHECKED);
+ break;
+ case LOADHISTORY_COUNT:
+ CheckDlgButton(hwndDlg, IDC_LOADCOUNT, BST_CHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTN, TRUE);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTSPIN, TRUE);
+ break;
+ case LOADHISTORY_TIME:
+ CheckDlgButton(hwndDlg, IDC_LOADTIME, BST_CHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMEN, TRUE);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMESPIN, TRUE);
+ Utils::enableDlgControl(hwndDlg, IDC_STMINSOLD, TRUE);
+ break;
+ }
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_LOGOPTIONS), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_LOGOPTIONS), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ g_himlOptions = (HIMAGELIST)SendDlgItemMessage(hwndDlg, IDC_LOGOPTIONS, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)CreateStateImageList());
+ ImageList_Destroy(g_himlOptions);
+
+ /*
+ * fill the list box, create groups first, then add items
+ */
+
+ TOptionListGroup *lvGroups = CTranslator::getGroupTree(CTranslator::TREE_LOG);
+
+ while (lvGroups[i].szName != NULL) {
+ tvi.hParent = 0;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE;
+ tvi.item.pszText = lvGroups[i].szName;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_EXPANDED | TVIS_BOLD;
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_EXPANDED | TVIS_BOLD;
+ lvGroups[i++].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hwndDlg, IDC_LOGOPTIONS), &tvi);
+ }
+
+ i = 0;
+
+ TOptionListItem *lvItems = CTranslator::getTree(CTranslator::TREE_LOG);
+
+ while (lvItems[i].szName != 0) {
+ tvi.hParent = (HTREEITEM)lvGroups[lvItems[i].uGroup].handle;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.pszText = lvItems[i].szName;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+ tvi.item.lParam = i;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK;
+ if (lvItems[i].uType == LOI_TYPE_FLAG)
+ tvi.item.state = INDEXTOSTATEIMAGEMASK((dwFlags & (UINT)lvItems[i].lParam) ? 3 : 2);
+ else if (lvItems[i].uType == LOI_TYPE_SETTING)
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(M->GetByte((char *)lvItems[i].lParam, lvItems[i].id) ? 3 : 2); // NOTE: was 2 : 1 without state image mask
+ lvItems[i].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hwndDlg, IDC_LOGOPTIONS), &tvi);
+ i++;
+ }
+ SendDlgItemMessage(hwndDlg, IDC_LOADCOUNTSPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_LOADCOUNTSPIN, UDM_SETPOS, 0, DBGetContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT));
+ SendDlgItemMessage(hwndDlg, IDC_LOADTIMESPIN, UDM_SETRANGE, 0, MAKELONG(24 * 60, 0));
+ SendDlgItemMessage(hwndDlg, IDC_LOADTIMESPIN, UDM_SETPOS, 0, DBGetContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME));
+
+ SetDlgItemInt(hwndDlg, IDC_INDENTAMOUNT, M->GetDword("IndentAmount", 20), FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_INDENTSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 0));
+ SendDlgItemMessage(hwndDlg, IDC_INDENTSPIN, UDM_SETPOS, 0, GetDlgItemInt(hwndDlg, IDC_INDENTAMOUNT, &translated, FALSE));
+
+ SetDlgItemInt(hwndDlg, IDC_RIGHTINDENT, M->GetDword("RightIndent", 20), FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_RINDENTSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 0));
+ SendDlgItemMessage(hwndDlg, IDC_RINDENTSPIN, UDM_SETPOS, 0, GetDlgItemInt(hwndDlg, IDC_RIGHTINDENT, &translated, FALSE));
+ SendMessage(hwndDlg, WM_COMMAND, MAKELONG(IDC_INDENT, 0), 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 5));
+ SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_SETPOS, 0, maxhist);
+ Utils::enableDlgControl(hwndDlg, IDC_TRIMSPIN, maxhist != 0);
+ Utils::enableDlgControl(hwndDlg, IDC_TRIM, maxhist != 0);
+ CheckDlgButton(hwndDlg, IDC_ALWAYSTRIM, maxhist != 0);
+
+ have_ieview = ServiceExists(MS_IEVIEW_WINDOW);
+ have_hpp = ServiceExists("History++/ExtGrid/NewWindow");
+
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_LOG_DEFAULT));
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_SETCURSEL, 0, 0);
+
+ if (have_ieview) {
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_LOG_IEVIEW));
+ if (M->GetByte("default_ieview", 0))
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_SETCURSEL, 1, 0);
+ }
+ if (have_hpp) {
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_LOG_HPP));
+ if (M->GetByte("default_ieview", 0))
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_SETCURSEL, 1, 0);
+ else if (M->GetByte("default_hpp", 0))
+ SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_SETCURSEL, have_ieview ? 2 : 1, 0);
+ }
+ SetDlgItemText(hwndDlg, IDC_EXPLAINMSGLOGSETTINGS, CTranslator::getOpt(CTranslator::OPT_MSGLOG_EXPLAINSETTINGS));
+ SendMessage(hwndDlg, WM_USER + 100, 0, 0);
+ return TRUE;
+ }
+ /*
+ * configure the option page - hide most of the settings here when either IEView
+ * or H++ is set as the global message log viewer. Showing these options may confuse
+ * the user, because they are not working and the user needs to configure the 3rd
+ * party plugin.
+ */
+ case WM_USER + 100: {
+ int i;
+
+ LRESULT r = SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_GETCURSEL, 0, 0);
+ Utils::showDlgControl(hwndDlg, IDC_EXPLAINMSGLOGSETTINGS, r == 0 ? SW_HIDE : SW_SHOW);
+ Utils::showDlgControl(hwndDlg, IDC_LOGOPTIONS, r == 0 ? SW_SHOW : SW_HIDE);
+ for(i = 0; i < safe_sizeof(__ctrls); i++)
+ Utils::enableDlgControl(hwndDlg, __ctrls[i], r == 0 ? TRUE : FALSE);
+ return(0);
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_ALWAYSTRIM:
+ Utils::enableDlgControl(hwndDlg, IDC_TRIMSPIN, IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM));
+ Utils::enableDlgControl(hwndDlg, IDC_TRIM, IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM));
+ break;
+ case IDC_LOADUNREAD:
+ case IDC_LOADCOUNT:
+ case IDC_LOADTIME:
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTN, IsDlgButtonChecked(hwndDlg, IDC_LOADCOUNT));
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTSPIN, IsDlgButtonChecked(hwndDlg, IDC_LOADCOUNT));
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMEN, IsDlgButtonChecked(hwndDlg, IDC_LOADTIME));
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMESPIN, IsDlgButtonChecked(hwndDlg, IDC_LOADTIME));
+ Utils::enableDlgControl(hwndDlg, IDC_STMINSOLD, IsDlgButtonChecked(hwndDlg, IDC_LOADTIME));
+ break;
+ case IDC_INDENTAMOUNT:
+ case IDC_LOADCOUNTN:
+ case IDC_LOADTIMEN:
+ case IDC_RIGHTINDENT:
+ case IDC_TRIM:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ break;
+ case IDC_MODIFY: {
+ TemplateEditorNew teNew = {0, 0, hwndDlg};
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_TEMPLATEEDIT), hwndDlg, DlgProcTemplateEditor, (LPARAM)&teNew);
+ break;
+ }
+ case IDC_RTLMODIFY: {
+ TemplateEditorNew teNew = {0, TRUE, hwndDlg};
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_TEMPLATEEDIT), hwndDlg, DlgProcTemplateEditor, (LPARAM)&teNew);
+ break;
+ }
+ case IDC_MSGLOGDIDSPLAY:
+ SendMessage(hwndDlg, WM_USER + 100, 0, 0);
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case IDC_LOGOPTIONS:
+ if (((LPNMHDR)lParam)->code == NM_CLICK || (((LPNMHDR)lParam)->code == TVN_KEYDOWN && ((LPNMTVKEYDOWN)lParam)->wVKey == VK_SPACE)) {
+ TVHITTESTINFO hti;
+ TVITEM item = {0};
+
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.stateMask = TVIS_STATEIMAGEMASK | TVIS_BOLD;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti) || ((LPNMHDR)lParam)->code == TVN_KEYDOWN) {
+ if (((LPNMHDR)lParam)->code == TVN_KEYDOWN) {
+ hti.flags |= TVHT_ONITEMSTATEICON;
+ item.hItem = TreeView_GetSelection(((LPNMHDR)lParam)->hwndFrom);
+ } else
+ item.hItem = (HTREEITEM)hti.hItem;
+ SendDlgItemMessageA(hwndDlg, IDC_LOGOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (item.state & TVIS_BOLD && hti.flags & TVHT_ONITEMSTATEICON) {
+ item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_BOLD;
+ SendDlgItemMessageA(hwndDlg, IDC_LOGOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ } else if (hti.flags & TVHT_ONITEMSTATEICON) {
+
+ if (((item.state & TVIS_STATEIMAGEMASK) >> 12) == 3) {
+ item.state = INDEXTOSTATEIMAGEMASK(1);
+ SendDlgItemMessageA(hwndDlg, IDC_LOGOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ default:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY: {
+ int i = 0;
+ TVITEM item = {0};
+ LRESULT msglogmode = SendDlgItemMessage(hwndDlg, IDC_MSGLOGDIDSPLAY, CB_GETCURSEL, 0, 0);
+
+ dwFlags &= ~(MWF_LOG_ALL);
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_LOADCOUNT))
+ M->WriteByte(SRMSGMOD, SRMSGSET_LOADHISTORY, LOADHISTORY_COUNT);
+ else if (IsDlgButtonChecked(hwndDlg, IDC_LOADTIME))
+ M->WriteByte(SRMSGMOD, SRMSGSET_LOADHISTORY, LOADHISTORY_TIME);
+ else
+ M->WriteByte(SRMSGMOD, SRMSGSET_LOADHISTORY, LOADHISTORY_UNREAD);
+ DBWriteContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADCOUNT, (WORD) SendDlgItemMessage(hwndDlg, IDC_LOADCOUNTSPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADTIME, (WORD) SendDlgItemMessage(hwndDlg, IDC_LOADTIMESPIN, UDM_GETPOS, 0, 0));
+
+ M->WriteDword(SRMSGMOD_T, "IndentAmount", (DWORD) GetDlgItemInt(hwndDlg, IDC_INDENTAMOUNT, &translated, FALSE));
+ M->WriteDword(SRMSGMOD_T, "RightIndent", (DWORD) GetDlgItemInt(hwndDlg, IDC_RIGHTINDENT, &translated, FALSE));
+
+ M->WriteByte(SRMSGMOD_T, "default_ieview", 0);
+ M->WriteByte(SRMSGMOD_T, "default_hpp", 0);
+ switch(msglogmode) {
+ case 0:
+ break;
+ case 1:
+ if (have_ieview)
+ M->WriteByte(SRMSGMOD_T, "default_ieview", 1);
+ else
+ M->WriteByte(SRMSGMOD_T, "default_hpp", 1);
+ break;
+ case 2:
+ M->WriteByte(SRMSGMOD_T, "default_hpp", 1);
+ break;
+ }
+
+ /*
+ * scan the tree view and obtain the options...
+ */
+ TOptionListItem *lvItems = CTranslator::getTree(CTranslator::TREE_LOG);
+
+ while (lvItems[i].szName != NULL) {
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.hItem = (HTREEITEM)lvItems[i].handle;
+ item.stateMask = TVIS_STATEIMAGEMASK;
+
+ SendDlgItemMessageA(hwndDlg, IDC_LOGOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (lvItems[i].uType == LOI_TYPE_FLAG)
+ dwFlags |= (item.state >> 12) == 3/*2*/ ? lvItems[i].lParam : 0;
+ else if (lvItems[i].uType == LOI_TYPE_SETTING)
+ M->WriteByte(SRMSGMOD_T, (char *)lvItems[i].lParam, (BYTE)((item.state >> 12) == 3/*2*/ ? 1 : 0)); // NOTE: state image masks changed
+ i++;
+ }
+
+ M->WriteDword(SRMSGMOD_T, "mwflags", dwFlags);
+ if (IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM))
+ M->WriteDword(SRMSGMOD_T, "maxhist", (DWORD)SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_GETPOS, 0, 0));
+ else
+ M->WriteDword(SRMSGMOD_T, "maxhist", 0);
+ PluginConfig.reloadSettings();
+ M->BroadcastMessage(DM_OPTIONSAPPLIED, 1, 0);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static void ResetCList(HWND hwndDlg)
+{
+ int i;
+
+ if (CallService(MS_CLUI_GETCAPS, 0, 0) & CLUIF_DISABLEGROUPS && !M->GetByte("CList", "UseGroups", SETTING_USEGROUPS_DEFAULT))
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETUSEGROUPS, (WPARAM) FALSE, 0);
+ else
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETUSEGROUPS, (WPARAM) TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETHIDEEMPTYGROUPS, 1, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETGREYOUTFLAGS, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETLEFTMARGIN, 2, 0);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETBKBITMAP, 0, (LPARAM)(HBITMAP) NULL);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW), 0);
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETINDENT, 10, 0);
+ for (i = 0; i <= FONTID_MAX; i++)
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+}
+
+static void RebuildList(HWND hwndDlg, HANDLE hItemNew, HANDLE hItemUnknown)
+{
+ HANDLE hContact, hItem;
+ BYTE defType = M->GetByte(SRMSGMOD, SRMSGSET_TYPINGNEW, SRMSGDEFSET_TYPINGNEW);
+
+ if (hItemNew && defType) {
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETCHECKMARK, (WPARAM) hItemNew, 1);
+ }
+ if (hItemUnknown && M->GetByte(SRMSGMOD, SRMSGSET_TYPINGUNKNOWN, SRMSGDEFSET_TYPINGUNKNOWN)) {
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETCHECKMARK, (WPARAM) hItemUnknown, 1);
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ do {
+ hItem = (HANDLE) SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM) hContact, 0);
+ if (hItem && M->GetByte(hContact, SRMSGMOD, SRMSGSET_TYPING, defType)) {
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_SETCHECKMARK, (WPARAM) hItem, 1);
+ }
+ } while (hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0));
+}
+
+static void SaveList(HWND hwndDlg, HANDLE hItemNew, HANDLE hItemUnknown)
+{
+ HANDLE hContact, hItem;
+
+ if (hItemNew) {
+ M->WriteByte(SRMSGMOD, SRMSGSET_TYPINGNEW, (BYTE)(SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM) hItemNew, 0) ? 1 : 0));
+ }
+ if (hItemUnknown) {
+ M->WriteByte(SRMSGMOD, SRMSGSET_TYPINGUNKNOWN, (BYTE)(SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM) hItemUnknown, 0) ? 1 : 0));
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ do {
+ hItem = (HANDLE) SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM) hContact, 0);
+ if (hItem) {
+ DBWriteContactSettingByte(hContact, SRMSGMOD, SRMSGSET_TYPING, (BYTE)(SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM) hItem, 0) ? 1 : 0));
+ }
+ } while (hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0));
+}
+
+static INT_PTR CALLBACK DlgProcTypeOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static HANDLE hItemNew, hItemUnknown;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ CLCINFOITEM cii = { 0 };
+ cii.cbSize = sizeof(cii);
+ cii.flags = CLCIIF_GROUPFONT | CLCIIF_CHECKBOX;
+ cii.pszText = CTranslator::getOpt(CTranslator::OPT_MTN_NEW);
+ hItemNew = (HANDLE) SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_ADDINFOITEM, 0, (LPARAM) & cii);
+ cii.pszText = CTranslator::getOpt(CTranslator::OPT_MTN_UNKNOWN);
+ hItemUnknown = (HANDLE) SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_ADDINFOITEM, 0, (LPARAM) & cii);
+ }
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CLIST), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_CLIST), GWL_STYLE) | (CLS_SHOWHIDDEN));
+ ResetCList(hwndDlg);
+ RebuildList(hwndDlg, hItemNew, hItemUnknown);
+
+ CheckDlgButton(hwndDlg, IDC_SHOWNOTIFY, M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING));
+ CheckDlgButton(hwndDlg, IDC_TYPEFLASHWIN, M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINFLASH, SRMSGDEFSET_SHOWTYPINGWINFLASH));
+
+ CheckDlgButton(hwndDlg, IDC_TYPENOWIN, M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGNOWINOPEN, 1));
+ CheckDlgButton(hwndDlg, IDC_TYPEWIN, M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINOPEN, 1));
+ CheckDlgButton(hwndDlg, IDC_NOTIFYTRAY, M->GetByte(SRMSGMOD, SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST));
+ CheckDlgButton(hwndDlg, IDC_NOTIFYBALLOON, M->GetByte(SRMSGMOD, "ShowTypingBalloon", 0));
+
+ CheckDlgButton(hwndDlg, IDC_NOTIFYPOPUP, M->GetByte(SRMSGMOD, "ShowTypingPopup", 0));
+
+ Utils::enableDlgControl(hwndDlg, IDC_TYPEWIN, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY));
+ Utils::enableDlgControl(hwndDlg, IDC_TYPENOWIN, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY));
+ Utils::enableDlgControl(hwndDlg, IDC_NOTIFYBALLOON, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY) &&
+ (IsDlgButtonChecked(hwndDlg, IDC_TYPEWIN) || IsDlgButtonChecked(hwndDlg, IDC_TYPENOWIN)));
+
+ Utils::enableDlgControl(hwndDlg, IDC_TYPEFLASHWIN, IsDlgButtonChecked(hwndDlg, IDC_SHOWNOTIFY));
+ Utils::enableDlgControl(hwndDlg, IDC_MTN_POPUPMODE, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYPOPUP));
+
+ if (!ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) {
+ Utils::enableDlgControl(hwndDlg, IDC_NOTIFYBALLOON, FALSE);
+ SetWindowText(GetDlgItem(hwndDlg, IDC_NOTIFYBALLOON), CTranslator::getOpt(CTranslator::OPT_MTN_UNSUPPORTED));
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_MTN_POPUPMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_GEN_ALWAYS));
+ SendDlgItemMessage(hwndDlg, IDC_MTN_POPUPMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_MTN_NOTFOCUSED));
+ SendDlgItemMessage(hwndDlg, IDC_MTN_POPUPMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_MTN_ONLYCLOSED));
+
+ SendDlgItemMessage(hwndDlg, IDC_MTN_POPUPMODE, CB_SETCURSEL, (WPARAM)M->GetByte("MTN_PopupMode", 0), 0);
+
+ if(!PluginConfig.g_PopupWAvail) {
+ Utils::showDlgControl(hwndDlg, IDC_NOTIFYPOPUP, SW_HIDE);
+ Utils::showDlgControl(hwndDlg, IDC_STATIC111, SW_HIDE);
+ Utils::showDlgControl(hwndDlg, IDC_MTN_POPUPMODE, SW_HIDE);
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_NOTIFYTRAY:
+ Utils::enableDlgControl(hwndDlg, IDC_TYPEWIN, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY));
+ Utils::enableDlgControl(hwndDlg, IDC_TYPENOWIN, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY));
+ Utils::enableDlgControl(hwndDlg, IDC_NOTIFYBALLOON, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY));
+ break;
+ case IDC_SHOWNOTIFY:
+ Utils::enableDlgControl(hwndDlg, IDC_TYPEFLASHWIN, IsDlgButtonChecked(hwndDlg, IDC_SHOWNOTIFY));
+ break;
+ case IDC_NOTIFYPOPUP:
+ Utils::enableDlgControl(hwndDlg, IDC_MTN_POPUPMODE, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYPOPUP));
+ break;
+ case IDC_TYPEWIN:
+ case IDC_TYPENOWIN:
+ Utils::enableDlgControl(hwndDlg, IDC_NOTIFYBALLOON, IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY) &&
+ (IsDlgButtonChecked(hwndDlg, IDC_TYPEWIN) || IsDlgButtonChecked(hwndDlg, IDC_TYPENOWIN)));
+ break;
+ case IDC_MTN_HELP:
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://wiki.miranda.or.at/TabSRMM/TypingNotifications");
+ return 0;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((NMHDR *) lParam)->idFrom) {
+ case IDC_CLIST:
+ switch (((NMHDR *) lParam)->code) {
+ case CLN_OPTIONSCHANGED:
+ ResetCList(hwndDlg);
+ break;
+ case CLN_CHECKCHANGED:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY: {
+ SaveList(hwndDlg, hItemNew, hItemUnknown);
+ M->WriteByte(SRMSGMOD, SRMSGSET_SHOWTYPING, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWNOTIFY));
+ M->WriteByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINFLASH, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_TYPEFLASHWIN));
+ M->WriteByte(SRMSGMOD, SRMSGSET_SHOWTYPINGNOWINOPEN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_TYPENOWIN));
+ M->WriteByte(SRMSGMOD, SRMSGSET_SHOWTYPINGWINOPEN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_TYPEWIN));
+ M->WriteByte(SRMSGMOD, SRMSGSET_SHOWTYPINGCLIST, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_NOTIFYTRAY));
+ M->WriteByte(SRMSGMOD, "ShowTypingBalloon", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_NOTIFYBALLOON));
+ M->WriteByte(SRMSGMOD, "ShowTypingPopup",(BYTE) IsDlgButtonChecked(hwndDlg, IDC_NOTIFYPOPUP));
+ M->WriteByte(SRMSGMOD_T, "MTN_PopupMode", (BYTE)SendDlgItemMessage(hwndDlg, IDC_MTN_POPUPMODE, CB_GETCURSEL, 0, 0));
+ PluginConfig.reloadSettings();
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/*
+ * options for tabbed messaging got their own page.. finally :)
+ */
+
+static INT_PTR CALLBACK DlgProcTabbedOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TVINSERTSTRUCT tvi = {0};
+ int i = 0;
+
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TABMSGOPTIONS), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_TABMSGOPTIONS), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ g_himlOptions = (HIMAGELIST)SendDlgItemMessage(hwndDlg, IDC_TABMSGOPTIONS, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)CreateStateImageList());
+ ImageList_Destroy(g_himlOptions);
+
+ /*
+ * fill the list box, create groups first, then add items
+ */
+
+ TOptionListGroup *tabGroups = CTranslator::getGroupTree(CTranslator::TREE_TAB);
+
+ while (tabGroups[i].szName != NULL) {
+ tvi.hParent = 0;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE;
+ tvi.item.pszText = tabGroups[i].szName;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_EXPANDED | TVIS_BOLD;
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_EXPANDED | TVIS_BOLD;
+ tabGroups[i++].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hwndDlg, IDC_TABMSGOPTIONS), &tvi);
+ }
+
+ i = 0;
+
+ TOptionListItem *tabItems = CTranslator::getTree(CTranslator::TREE_TAB);
+
+ while (tabItems[i].szName != 0) {
+ tvi.hParent = (HTREEITEM)tabGroups[tabItems[i].uGroup].handle;
+ tvi.hInsertAfter = TVI_LAST;
+ tvi.item.pszText = tabItems[i].szName;
+ tvi.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_PARAM;
+ tvi.item.lParam = i;
+ tvi.item.stateMask = TVIS_STATEIMAGEMASK;
+ if (tabItems[i].uType == LOI_TYPE_SETTING)
+ tvi.item.state = INDEXTOSTATEIMAGEMASK(M->GetByte((char *)tabItems[i].lParam, (BYTE)tabItems[i].id) ? 3 : 2/*2 : 1*/);
+ tabItems[i].handle = (LRESULT)TreeView_InsertItem(GetDlgItem(hwndDlg, IDC_TABMSGOPTIONS), &tvi);
+ i++;
+ }
+ CheckDlgButton(hwndDlg, IDC_CUT_TABTITLE, M->GetByte("cuttitle", 0));
+ SendDlgItemMessage(hwndDlg, IDC_CUT_TITLEMAXSPIN, UDM_SETRANGE, 0, MAKELONG(20, 5));
+ SendDlgItemMessage(hwndDlg, IDC_CUT_TITLEMAXSPIN, UDM_SETPOS, 0, (WPARAM)DBGetContactSettingWord(NULL, SRMSGMOD_T, "cut_at", 15));
+
+ Utils::enableDlgControl(hwndDlg, IDC_CUT_TITLEMAX, IsDlgButtonChecked(hwndDlg, IDC_CUT_TABTITLE));
+ Utils::enableDlgControl(hwndDlg, IDC_CUT_TITLEMAXSPIN, IsDlgButtonChecked(hwndDlg, IDC_CUT_TABTITLE));
+
+ SendDlgItemMessage(hwndDlg, IDC_ESCMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_CNT_ESCNORMAL));
+ SendDlgItemMessage(hwndDlg, IDC_ESCMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_CNT_ESCMINIMIZE));
+ SendDlgItemMessage(hwndDlg, IDC_ESCMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_CNT_ESCCLOS));
+ SendDlgItemMessage(hwndDlg, IDC_ESCMODE, CB_SETCURSEL, (WPARAM)PluginConfig.m_EscapeCloses, 0);
+ break;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_CUT_TITLEMAX:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ break;
+ case IDC_SETUPAUTOCREATEMODES: {
+ HWND hwndNew = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CHOOSESTATUSMODES), hwndDlg, DlgProcSetupStatusModes, M->GetDword("autopopupmask", -1));
+ SendMessage(hwndNew, DM_SETPARENTDIALOG, 0, (LPARAM)hwndDlg);
+ break;
+ }
+ case IDC_CUT_TABTITLE:
+ Utils::enableDlgControl(hwndDlg, IDC_CUT_TITLEMAX, IsDlgButtonChecked(hwndDlg, IDC_CUT_TABTITLE));
+ Utils::enableDlgControl(hwndDlg, IDC_CUT_TITLEMAXSPIN, IsDlgButtonChecked(hwndDlg, IDC_CUT_TABTITLE));
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case DM_STATUSMASKSET:
+ M->WriteDword(SRMSGMOD_T, "autopopupmask", (DWORD)lParam);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case IDC_TABMSGOPTIONS:
+ if (((LPNMHDR)lParam)->code == NM_CLICK || (((LPNMHDR)lParam)->code == TVN_KEYDOWN && ((LPNMTVKEYDOWN)lParam)->wVKey == VK_SPACE)) {
+ TVHITTESTINFO hti;
+ TVITEM item = {0};
+
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.stateMask = TVIS_STATEIMAGEMASK | TVIS_BOLD;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti) || ((LPNMHDR)lParam)->code == TVN_KEYDOWN) {
+ if (((LPNMHDR)lParam)->code == TVN_KEYDOWN) {
+ hti.flags |= TVHT_ONITEMSTATEICON;
+ item.hItem = TreeView_GetSelection(((LPNMHDR)lParam)->hwndFrom);
+ } else
+ item.hItem = (HTREEITEM)hti.hItem;
+ SendDlgItemMessageA(hwndDlg, IDC_TABMSGOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (item.state & TVIS_BOLD && hti.flags & TVHT_ONITEMSTATEICON) {
+ item.state = INDEXTOSTATEIMAGEMASK(0) | TVIS_BOLD;
+ SendDlgItemMessageA(hwndDlg, IDC_TABMSGOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ } else if (hti.flags & TVHT_ONITEMSTATEICON) {
+
+ if (((item.state & TVIS_STATEIMAGEMASK) >> 12) == 3) {
+ item.state = INDEXTOSTATEIMAGEMASK(1);
+ SendDlgItemMessageA(hwndDlg, IDC_TABMSGOPTIONS, TVM_SETITEMA, 0, (LPARAM)&item);
+ }
+
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY: {
+ TVITEM item = {0};
+ int i = 0;
+ M->WriteByte(SRMSGMOD_T, "cuttitle", (BYTE) IsDlgButtonChecked(hwndDlg, IDC_CUT_TABTITLE));
+ DBWriteContactSettingWord(NULL, SRMSGMOD_T, "cut_at", (WORD)SendDlgItemMessage(hwndDlg, IDC_CUT_TITLEMAXSPIN, UDM_GETPOS, 0, 0));
+
+ /*
+ * scan the tree view and obtain the options...
+ */
+
+ TOptionListItem *tabItems = CTranslator::getTree(CTranslator::TREE_TAB);
+
+ while (tabItems[i].szName != NULL) {
+ item.mask = TVIF_HANDLE | TVIF_STATE;
+ item.hItem = (HTREEITEM)tabItems[i].handle;
+ item.stateMask = TVIS_STATEIMAGEMASK;
+
+ SendDlgItemMessageA(hwndDlg, IDC_TABMSGOPTIONS, TVM_GETITEMA, 0, (LPARAM)&item);
+ if (tabItems[i].uType == LOI_TYPE_SETTING)
+ M->WriteByte(SRMSGMOD_T, (char *)tabItems[i].lParam, (BYTE)((item.state >> 12) == 3/*2*/ ? 1 : 0));
+ i++;
+ }
+
+ PluginConfig.m_EscapeCloses = (int)SendDlgItemMessage(hwndDlg, IDC_ESCMODE, CB_GETCURSEL, 0, 0);
+ M->WriteByte(SRMSGMOD_T, "escmode", (BYTE)PluginConfig.m_EscapeCloses);
+ PluginConfig.reloadSettings();
+ M->BroadcastMessage(DM_OPTIONSAPPLIED, 0, 0);
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProcContainerSettings(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG: {
+ DBVARIANT dbv = {0};
+
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_CONTAINERGROUPMODE, M->GetByte("useclistgroups", 0));
+ CheckDlgButton(hwndDlg, IDC_LIMITTABS, M->GetByte("limittabs", 0));
+
+ SendDlgItemMessage(hwndDlg, IDC_TABLIMITSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 1));
+ SendDlgItemMessage(hwndDlg, IDC_TABLIMITSPIN, UDM_SETPOS, 0, (int)M->GetDword("maxtabs", 1));
+ SetDlgItemInt(hwndDlg, IDC_TABLIMIT, (int)M->GetDword("maxtabs", 1), FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_TABLIMIT, IsDlgButtonChecked(hwndDlg, IDC_LIMITTABS));
+ CheckDlgButton(hwndDlg, IDC_SINGLEWINDOWMODE, M->GetByte("singlewinmode", 0));
+ CheckDlgButton(hwndDlg, IDC_DEFAULTCONTAINERMODE, !(IsDlgButtonChecked(hwndDlg, IDC_CONTAINERGROUPMODE) || IsDlgButtonChecked(hwndDlg, IDC_LIMITTABS) || IsDlgButtonChecked(hwndDlg, IDC_SINGLEWINDOWMODE)));
+
+ SetDlgItemInt(hwndDlg, IDC_NRFLASH, M->GetByte("nrflash", 4), FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_NRFLASHSPIN, UDM_SETRANGE, 0, MAKELONG(255, 1));
+ SendDlgItemMessage(hwndDlg, IDC_NRFLASHSPIN, UDM_SETPOS, 0, (int)M->GetByte("nrflash", 4));
+
+ SetDlgItemInt(hwndDlg, IDC_FLASHINTERVAL, M->GetDword("flashinterval", 1000), FALSE);
+ SendDlgItemMessage(hwndDlg, IDC_FLASHINTERVALSPIN, UDM_SETRANGE, 0, MAKELONG(10000, 500));
+ SendDlgItemMessage(hwndDlg, IDC_FLASHINTERVALSPIN, UDM_SETPOS, 0, (int)M->GetDword("flashinterval", 1000));
+ SendDlgItemMessage(hwndDlg, IDC_FLASHINTERVALSPIN, UDM_SETACCEL, 0, (int)M->GetDword("flashinterval", 1000));
+ CheckDlgButton(hwndDlg, IDC_USEAERO, M->GetByte("useAero", 1));
+ CheckDlgButton(hwndDlg, IDC_USEAEROPEEK, M->GetByte("useAeroPeek", 1));
+ for(int i = 0; i < CSkin::AERO_EFFECT_LAST; i++)
+ SendDlgItemMessage(hwndDlg, IDC_AEROEFFECT, CB_INSERTSTRING, -1, (LPARAM)TranslateTS(CSkin::m_aeroEffects[i].tszName));
+
+ SendDlgItemMessage(hwndDlg, IDC_AEROEFFECT, CB_SETCURSEL, (WPARAM)CSkin::m_aeroEffect, 0);
+ Utils::enableDlgControl(hwndDlg, IDC_AEROEFFECT, PluginConfig.m_bIsVista ? TRUE : FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_USEAERO, PluginConfig.m_bIsVista ? TRUE : FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_USEAEROPEEK, PluginConfig.m_bIsWin7 ? TRUE : FALSE);
+ if(PluginConfig.m_bIsVista)
+ Utils::enableDlgControl(hwndDlg, IDC_AEROEFFECT, IsDlgButtonChecked(hwndDlg, IDC_USEAERO) ? 1 : 0);
+
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_TABLIMIT:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ break;
+ case IDC_USEAERO:
+ Utils::enableDlgControl(hwndDlg, IDC_AEROEFFECT, IsDlgButtonChecked(hwndDlg, IDC_USEAERO) ? 1 : 0);
+ break;
+ case IDC_LIMITTABS:
+ case IDC_SINGLEWINDOWMODE:
+ case IDC_CONTAINERGROUPMODE:
+ case IDC_DEFAULTCONTAINERMODE:
+ Utils::enableDlgControl(hwndDlg, IDC_TABLIMIT, IsDlgButtonChecked(hwndDlg, IDC_LIMITTABS));
+ break;
+ case IDC_HELP_CONTAINERS:
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://wiki.miranda.or.at/TabSRMM/Containers");
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY: {
+ BOOL translated;
+
+ bool fOldAeroState = M->getAeroState();
+ M->WriteByte(SRMSGMOD_T, "useclistgroups", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_CONTAINERGROUPMODE)));
+ M->WriteByte(SRMSGMOD_T, "limittabs", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_LIMITTABS)));
+ M->WriteDword(SRMSGMOD_T, "maxtabs", GetDlgItemInt(hwndDlg, IDC_TABLIMIT, &translated, FALSE));
+ M->WriteByte(SRMSGMOD_T, "singlewinmode", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_SINGLEWINDOWMODE)));
+ M->WriteDword(SRMSGMOD_T, "flashinterval", GetDlgItemInt(hwndDlg, IDC_FLASHINTERVAL, &translated, FALSE));
+ M->WriteByte(SRMSGMOD_T, "nrflash", (BYTE)(GetDlgItemInt(hwndDlg, IDC_NRFLASH, &translated, FALSE)));
+ M->WriteByte(0, SRMSGMOD_T, "useAero", IsDlgButtonChecked(hwndDlg, IDC_USEAERO) ? 1 : 0);
+ M->WriteByte(0, SRMSGMOD_T, "useAeroPeek", IsDlgButtonChecked(hwndDlg, IDC_USEAEROPEEK) ? 1 : 0);
+ CSkin::setAeroEffect(SendDlgItemMessage(hwndDlg, IDC_AEROEFFECT, CB_GETCURSEL, 0, 0));
+
+ if(M->getAeroState() != fOldAeroState) {
+ SendMessage(PluginConfig.g_hwndHotkeyHandler, WM_DWMCOMPOSITIONCHANGED, 0, 0); // simulate aero state change
+ SendMessage(PluginConfig.g_hwndHotkeyHandler, WM_DWMCOLORIZATIONCOLORCHANGED, 0, 0); // simulate aero state change
+ }
+ BuildContainerMenu();
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+#define DBFONTF_BOLD 1
+#define DBFONTF_ITALIC 2
+#define DBFONTF_UNDERLINE 4
+
+#define FONTS_TO_CONFIG MSGDLGFONTCOUNT
+
+#define SAMEASF_FACE 1
+#define SAMEASF_SIZE 2
+#define SAMEASF_STYLE 4
+#define SAMEASF_COLOUR 8
+#include <pshpack1.h>
+
+struct {
+ BYTE sameAsFlags, sameAs;
+ COLORREF colour;
+ char size;
+ BYTE style;
+ BYTE charset;
+ char szFace[LF_FACESIZE];
+} static fontSettings[MSGDLGFONTCOUNT + 1];
+
+#include <poppack.h>
+
+#define SRFONTSETTINGMODULE FONTMODULE
+
+static int OptInitialise(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+
+ if(PluginConfig.g_PopupWAvail||PluginConfig.g_PopupAvail)
+ TN_OptionsInitialize(wParam, lParam);
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 910000000;
+ odp.hInstance = g_hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_MSGDLG);
+ odp.ptszTitle = LPGENT("Message Sessions");
+ odp.pfnDlgProc = DlgProcOptions;
+ odp.ptszGroup = NULL;
+ odp.nIDBottomSimpleControl = 0;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR;
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_GENERAL));
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_TABS));
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_TABBEDMSG);
+ odp.pfnDlgProc = DlgProcTabbedOptions;
+ CallService(MS_OPT_ADDPAGE, wParam,(LPARAM)&odp);
+
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_CONTAINERS));
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CONTAINERS);
+ odp.pfnDlgProc = DlgProcContainerSettings;
+ CallService(MS_OPT_ADDPAGE, wParam,(LPARAM)&odp);
+
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_LOG));
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_MSGLOG);
+ odp.pfnDlgProc = DlgProcLogOptions;
+ CallService(MS_OPT_ADDPAGE, wParam,(LPARAM)&odp);
+
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_TOOLBAR));
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_TOOLBAR);
+ odp.pfnDlgProc = DlgProcToolBar;
+ CallService(MS_OPT_ADDPAGE, wParam,(LPARAM)&odp);
+
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_ADVANCED));
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PLUS);
+ odp.pfnDlgProc = PlusOptionsProc;
+ CallService(MS_OPT_ADDPAGE, wParam,(LPARAM)&odp);
+
+
+ odp.ptszGroup = LPGENT("Message Sessions");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_MSGTYPE);
+ odp.ptszTitle = LPGENT("Typing Notify");
+ odp.pfnDlgProc = DlgProcTypeOptions;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUP_OPT);
+ odp.ptszTitle = LPGENT("Event notifications");
+ odp.ptszGroup = LPGENT("PopUps");
+ odp.pfnDlgProc = DlgProcPopupOpts;
+ odp.nIDBottomSimpleControl = 0;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SKIN);
+ odp.ptszTitle = LPGENT("Message window");
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TAB_SKINLOAD));
+ odp.pfnDlgProc = DlgProcSkinOpts;
+ odp.nIDBottomSimpleControl = 0;
+ odp.ptszGroup = LPGENT("Skins");
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_TABCONFIG);
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TAB_LAYOUTTWEAKS));
+ odp.pfnDlgProc = DlgProcTabConfig;
+ odp.nIDBottomSimpleControl = 0;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) &odp);
+
+ /* group chats */
+
+ odp.ptszGroup = LPGENT("Message Sessions");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS1);
+ odp.ptszTitle = LPGENT("Group Chats");
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_MUC_SETTINGS));
+ odp.pfnDlgProc = DlgProcOptions1;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS2);
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_MUC_LOG));
+ odp.pfnDlgProc = DlgProcOptions2;
+ odp.nIDBottomSimpleControl = 0;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS3);
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_MUC_EVENTS));
+ odp.pfnDlgProc = DlgProcOptions3;
+ odp.nIDBottomSimpleControl = 0;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS4);
+ odp.ptszTab = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_TABS_MUC_HIGHLIGHT));
+ odp.pfnDlgProc = CMUCHighlight::dlgProc;
+ odp.nIDBottomSimpleControl = 0;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+ return 0;
+}
+
+enum
+{
+ CBVT_NONE,
+ CBVT_CHAR,
+ CBVT_INT,
+ CBVT_BYTE,
+ CBVT_DWORD,
+ CBVT_BOOL,
+};
+
+struct OptCheckBox
+{
+ UINT idc;
+
+ DWORD defValue; // should be full combined value for masked items!
+ DWORD dwBit;
+
+ BYTE dbType;
+ char *dbModule;
+ char *dbSetting;
+
+ BYTE valueType;
+ union
+ {
+ void *pValue;
+
+ char *charValue;
+ int *intValue;
+ BYTE *byteValue;
+ DWORD *dwordValue;
+ BOOL *boolValue;
+ };
+};
+
+DWORD OptCheckBox_LoadValue(struct OptCheckBox *cb)
+{
+ switch (cb->valueType)
+ {
+ case CBVT_NONE:
+ switch (cb->dbType)
+ {
+ case DBVT_BYTE:
+ return M->GetByte(cb->dbModule, cb->dbSetting, cb->defValue);
+ case DBVT_WORD:
+ return DBGetContactSettingWord(NULL, cb->dbModule, cb->dbSetting, cb->defValue);
+ case DBVT_DWORD:
+ return M->GetDword(cb->dbModule, cb->dbSetting, cb->defValue);
+ }
+ break;
+
+ case CBVT_CHAR:
+ return *cb->charValue;
+ case CBVT_INT:
+ return *cb->intValue;
+ case CBVT_BYTE:
+ return *cb->byteValue;
+ case CBVT_DWORD:
+ return *cb->dwordValue;
+ case CBVT_BOOL:
+ return *cb->boolValue;
+ }
+
+ return cb->defValue;
+}
+
+void OptCheckBox_Load(HWND hwnd, struct OptCheckBox *cb)
+{
+ DWORD value = OptCheckBox_LoadValue(cb);
+ if (cb->dwBit) value &= cb->dwBit;
+ CheckDlgButton(hwnd, cb->idc, value ? BST_CHECKED : BST_UNCHECKED);
+}
+
+void OptCheckBox_Save(HWND hwnd, struct OptCheckBox *cb)
+{
+ DWORD value = IsDlgButtonChecked(hwnd, cb->idc) == BST_CHECKED;
+
+ if (cb->dwBit)
+ {
+ DWORD curValue = OptCheckBox_LoadValue(cb);
+ value = value ? (curValue | cb->dwBit) : (curValue & ~cb->dwBit);
+ }
+
+ switch (cb->dbType)
+ {
+ case DBVT_BYTE:
+ M->WriteByte(cb->dbModule, cb->dbSetting, (BYTE)value);
+ break;
+ case DBVT_WORD:
+ DBWriteContactSettingWord(NULL, cb->dbModule, cb->dbSetting, (WORD)value);
+ break;
+ case DBVT_DWORD:
+ M->WriteDword(cb->dbModule, cb->dbSetting, (DWORD)value);
+ break;
+ }
+
+ switch (cb->valueType)
+ {
+ case CBVT_CHAR:
+ *cb->charValue = (char)value;
+ break;
+ case CBVT_INT:
+ *cb->intValue = (int)value;
+ break;
+ case CBVT_BYTE:
+ *cb->byteValue = (BYTE)value;
+ break;
+ case CBVT_DWORD:
+ *cb->dwordValue = (DWORD)value;
+ break;
+ case CBVT_BOOL:
+ *cb->boolValue = (BOOL)value;
+ break;
+ }
+}
+
+static INT_PTR CALLBACK DlgProcTabSrmmModernOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct OptCheckBox opts[] =
+ {
+ //{IDC_, def, bit, dbtype, dbmodule, dbsetting, valtype, pval},
+ {IDC_CLOSEONESC, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, "escmode"},
+ {IDC_ALWAYSPOPUP, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, SRMSGSET_AUTOPOPUP},
+ {IDC_CREATEMIN, TRUE, 0, DBVT_BYTE, SRMSGMOD_T, "autocontainer"},
+ //{IDC_USETABS, , 0, DBVT_BYTE, SRMSGMOD_T, },
+ {IDC_CREATENOACTIVATE, TRUE, 0, DBVT_BYTE, SRMSGMOD_T, "autotabs"},
+ {IDC_POPUPONCREATE, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, "cpopup"},
+ {IDC_AUTOSWITCHTABS, TRUE, 0, DBVT_BYTE, SRMSGMOD_T, "autoswitchtabs"},
+ //{IDC_SENDCTRLENTER, , 0, DBVT_BYTE, SRMSGMOD_T, },
+ {IDC_SENDSHIFTENTER, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, "sendonshiftenter"},
+ {IDC_SENDENTER, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, SRMSGSET_SENDONENTER},
+ {IDC_SENDDBLENTER, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, "SendOnDblEnter"},
+ {IDC_MINSEND, FALSE, 0, DBVT_BYTE, SRMSGMOD_T, SRMSGSET_AUTOMIN},
+ {IDC_NOOPENNOTIFY, FALSE, 0, DBVT_BYTE, "tabSRMM_NEN", OPT_WINDOWCHECK, CBVT_BOOL, &nen_options.bWindowCheck},
+ };
+
+ static BOOL bInit = TRUE;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ int i = 0;
+ DWORD maxhist = M->GetDword("maxhist", 0);
+
+ bInit = TRUE;
+
+ TranslateDialogDefault(hwndDlg);
+
+ for (i = 0; i < SIZEOF(opts); ++i)
+ OptCheckBox_Load(hwndDlg, opts+i);
+
+ // Always on!
+ CheckDlgButton(hwndDlg, IDC_SENDCTRLENTER, BST_CHECKED);
+
+ switch (M->GetByte(SRMSGMOD, SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY)) {
+ case LOADHISTORY_UNREAD:
+ CheckDlgButton(hwndDlg, IDC_LOADUNREAD, BST_CHECKED);
+ break;
+ case LOADHISTORY_COUNT:
+ CheckDlgButton(hwndDlg, IDC_LOADCOUNT, BST_CHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTN, TRUE);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTSPIN, TRUE);
+ break;
+ case LOADHISTORY_TIME:
+ CheckDlgButton(hwndDlg, IDC_LOADTIME, BST_CHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMEN, TRUE);
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMESPIN, TRUE);
+ Utils::enableDlgControl(hwndDlg, IDC_STMINSOLD, TRUE);
+ break;
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_LOADCOUNTSPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_LOADCOUNTSPIN, UDM_SETPOS, 0, DBGetContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT));
+ SendDlgItemMessage(hwndDlg, IDC_LOADTIMESPIN, UDM_SETRANGE, 0, MAKELONG(24 * 60, 0));
+ SendDlgItemMessage(hwndDlg, IDC_LOADTIMESPIN, UDM_SETPOS, 0, DBGetContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME));
+
+ SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 5));
+ SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_SETPOS, 0, maxhist);
+ Utils::enableDlgControl(hwndDlg, IDC_TRIMSPIN, maxhist != 0);
+ Utils::enableDlgControl(hwndDlg, IDC_TRIM, maxhist != 0);
+ CheckDlgButton(hwndDlg, IDC_ALWAYSTRIM, maxhist != 0);
+
+ {
+ BOOL bTabOptGroups = M->GetByte("useclistgroups", 0);
+ BOOL bTabOptLimit = M->GetByte("limittabs", 0);
+ BOOL bTabOptSingle = M->GetByte("singlewinmode", 0);
+
+ if (bTabOptSingle && !bTabOptGroups && !bTabOptLimit)
+ CheckDlgButton(hwndDlg, IDC_USETABS, BST_UNCHECKED);
+ else if (!bTabOptSingle && !bTabOptGroups && !bTabOptLimit)
+ CheckDlgButton(hwndDlg, IDC_USETABS, BST_CHECKED);
+ else
+ {
+ LONG s = (GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_USETABS), GWL_STYLE) & ~BS_TYPEMASK) | BS_AUTO3STATE;
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_USETABS), GWL_STYLE, s);
+ CheckDlgButton(hwndDlg, IDC_USETABS, BST_INDETERMINATE);
+ }
+ }
+
+ bInit = FALSE;
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ bInit = TRUE;
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_LOADUNREAD:
+ case IDC_LOADCOUNT:
+ case IDC_LOADTIME:
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTN, IsDlgButtonChecked(hwndDlg, IDC_LOADCOUNT));
+ Utils::enableDlgControl(hwndDlg, IDC_LOADCOUNTSPIN, IsDlgButtonChecked(hwndDlg, IDC_LOADCOUNT));
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMEN, IsDlgButtonChecked(hwndDlg, IDC_LOADTIME));
+ Utils::enableDlgControl(hwndDlg, IDC_LOADTIMESPIN, IsDlgButtonChecked(hwndDlg, IDC_LOADTIME));
+ Utils::enableDlgControl(hwndDlg, IDC_STMINSOLD, IsDlgButtonChecked(hwndDlg, IDC_LOADTIME));
+ break;
+ case IDC_ALWAYSTRIM:
+ Utils::enableDlgControl(hwndDlg, IDC_TRIMSPIN, IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM));
+ Utils::enableDlgControl(hwndDlg, IDC_TRIM, IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM));
+ break;
+ case IDC_TRIM:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ break;
+ }
+ if (!bInit)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR) lParam)->idFrom)
+ {
+ case 0:
+ switch (((LPNMHDR) lParam)->code)
+ {
+ case PSN_APPLY:
+ {
+ int i;
+ for (i = 0; i < SIZEOF(opts); ++i)
+ OptCheckBox_Save(hwndDlg, opts+i);
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_LOADCOUNT))
+ M->WriteByte(SRMSGMOD, SRMSGSET_LOADHISTORY, LOADHISTORY_COUNT);
+ else if (IsDlgButtonChecked(hwndDlg, IDC_LOADTIME))
+ M->WriteByte(SRMSGMOD, SRMSGSET_LOADHISTORY, LOADHISTORY_TIME);
+ else
+ M->WriteByte(SRMSGMOD, SRMSGSET_LOADHISTORY, LOADHISTORY_UNREAD);
+ DBWriteContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADCOUNT, (WORD) SendDlgItemMessage(hwndDlg, IDC_LOADCOUNTSPIN, UDM_GETPOS, 0, 0));
+ DBWriteContactSettingWord(NULL, SRMSGMOD, SRMSGSET_LOADTIME, (WORD) SendDlgItemMessage(hwndDlg, IDC_LOADTIMESPIN, UDM_GETPOS, 0, 0));
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM))
+ M->WriteDword(SRMSGMOD_T, "maxhist", (DWORD)SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_GETPOS, 0, 0));
+ else
+ M->WriteDword(SRMSGMOD_T, "maxhist", 0);
+
+ switch (IsDlgButtonChecked(hwndDlg, IDC_USETABS))
+ {
+ case BST_UNCHECKED:
+ M->WriteByte(SRMSGMOD_T, "useclistgroups", 0);
+ M->WriteByte(SRMSGMOD_T, "limittabs", 0);
+ M->WriteByte(SRMSGMOD_T, "singlewinmode", 1);
+ break;
+ case BST_CHECKED:
+ M->WriteByte(SRMSGMOD_T, "useclistgroups", 0);
+ M->WriteByte(SRMSGMOD_T, "limittabs", 0);
+ M->WriteByte(SRMSGMOD_T, "singlewinmode", 0);
+ break;
+ }
+ PluginConfig.reloadSettings();
+ M->BroadcastMessage(DM_OPTIONSAPPLIED, 1, 0);
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+static int ModernOptInitialise(WPARAM wParam, LPARAM lParam)
+{
+ static int iBoldControls[] =
+ {
+ IDC_TXT_TITLE1, IDC_TXT_TITLE2,
+ IDC_TXT_TITLE3, IDC_TXT_TITLE4,
+ IDC_TXT_TITLE5,
+ MODERNOPT_CTRL_LAST
+ };
+
+ MODERNOPTOBJECT obj = {0};
+
+ obj.cbSize = sizeof(obj);
+ obj.dwFlags = MODEROPT_FLG_TCHAR|MODEROPT_FLG_NORESIZE;
+ obj.hIcon = LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ obj.hInstance = g_hInst;
+ obj.iSection = MODERNOPT_PAGE_MSGS;
+ obj.iType = MODERNOPT_TYPE_SECTIONPAGE;
+ obj.iBoldControls = iBoldControls;
+ obj.lpzClassicGroup = NULL;
+ obj.lpzClassicPage = "Message Sessions";
+ obj.lpzHelpUrl = "http://wiki.miranda-im.org/";
+
+ obj.lpzTemplate = MAKEINTRESOURCEA(IDD_MODERNOPTS);
+ obj.pfnDlgProc = DlgProcTabSrmmModernOptions;
+ CallService(MS_MODERNOPT_ADDOBJECT, wParam, (LPARAM)&obj);
+ return 0;
+}
+
+int TSAPI InitOptions(void)
+{
+ HookEvent(ME_OPT_INITIALISE, OptInitialise);
+ HookEvent(ME_MODERNOPT_INITIALIZE, ModernOptInitialise);
+ return 0;
+}
+
+INT_PTR CALLBACK DlgProcSetupStatusModes(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DWORD dwStatusMask = GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ static DWORD dwNewStatusMask = 0;
+ static HWND hwndParent = 0;
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ int i;
+
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ dwStatusMask = lParam;
+
+ SetWindowText(hwndDlg, CTranslator::getOpt(CTranslator::OPT_SMODE_CHOOSE));
+ for (i = ID_STATUS_ONLINE; i <= ID_STATUS_OUTTOLUNCH; i++) {
+ SetWindowText(GetDlgItem(hwndDlg, i), (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)i, GSMDF_TCHAR));
+ if (dwStatusMask != -1 && (dwStatusMask & (1 << (i - ID_STATUS_ONLINE))))
+ CheckDlgButton(hwndDlg, i, TRUE);
+ Utils::enableDlgControl(hwndDlg, i, dwStatusMask != -1);
+ }
+ if (dwStatusMask == -1)
+ CheckDlgButton(hwndDlg, IDC_ALWAYS, TRUE);
+ ShowWindow(hwndDlg, SW_SHOWNORMAL);
+ return TRUE;
+ }
+ case DM_SETPARENTDIALOG:
+ hwndParent = (HWND)lParam;
+ break;
+ case DM_GETSTATUSMASK: {
+ if (IsDlgButtonChecked(hwndDlg, IDC_ALWAYS))
+ dwNewStatusMask = -1;
+ else {
+ int i;
+ dwNewStatusMask = 0;
+ for (i = ID_STATUS_ONLINE; i <= ID_STATUS_OUTTOLUNCH; i++)
+ dwNewStatusMask |= (IsDlgButtonChecked(hwndDlg, i) ? (1 << (i - ID_STATUS_ONLINE)) : 0);
+ }
+ break;
+ }
+ case WM_COMMAND: {
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ if (LOWORD(wParam) == IDOK) {
+ SendMessage(hwndDlg, DM_GETSTATUSMASK, 0, 0);
+ SendMessage(hwndParent, DM_STATUSMASKSET, 0, (LPARAM)dwNewStatusMask);
+ }
+ DestroyWindow(hwndDlg);
+ break;
+ case IDC_ALWAYS: {
+ int i;
+ for (i = ID_STATUS_ONLINE; i <= ID_STATUS_OUTTOLUNCH; i++)
+ Utils::enableDlgControl(hwndDlg, i, !IsDlgButtonChecked(hwndDlg, IDC_ALWAYS));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ case WM_DESTROY:
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
diff --git a/plugins/TabSRMM/src/msgs.cpp b/plugins/TabSRMM/src/msgs.cpp new file mode 100644 index 0000000000..5c67a6a797 --- /dev/null +++ b/plugins/TabSRMM/src/msgs.cpp @@ -0,0 +1,1180 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: msgs.cpp 13587 2011-04-12 13:54:26Z george.hazan $
+ *
+ * Load, setup and shutdown the plugin
+ * core plugin messaging services (single IM chats only).
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+#define IDI_CORE_LOAD 132 // icon id for the "connecting" icon
+
+REOLECallback* mREOLECallback;
+NEN_OPTIONS nen_options;
+extern PLUGININFOEX pluginInfo;
+extern HANDLE hHookToolBarLoadedEvt;
+static HANDLE hUserPrefsWindowLis = 0;
+HMODULE g_hIconDLL = 0;
+
+static void UnloadIcons();
+
+extern INT_PTR CALLBACK DlgProcUserPrefsFrame(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+extern struct TLogIcon msgLogIcons[NR_LOGICONS * 3];
+extern int CacheIconToBMP (struct TLogIcon *theIcon, HICON hIcon, COLORREF backgroundColor, int sizeX, int sizeY);
+extern void DeleteCachedIcon(struct TLogIcon *theIcon);
+
+int Chat_IconsChanged(WPARAM wp, LPARAM lp);
+void Chat_AddIcons(void);
+int Chat_PreShutdown(WPARAM wParam, LPARAM lParam);
+
+/*
+ * fired event when user changes IEView plugin options. Apply them to all open tabs
+ */
+
+int IEViewOptionsChanged(WPARAM wParam, LPARAM lParam)
+{
+ M->BroadcastMessage(DM_IEVIEWOPTIONSCHANGED, 0, 0);
+ return 0;
+}
+
+/*
+ * fired event when user changes smileyadd options. Notify all open tabs about the changes
+ */
+
+int SmileyAddOptionsChanged(WPARAM wParam, LPARAM lParam)
+{
+ M->BroadcastMessage(DM_SMILEYOPTIONSCHANGED, 0, 0);
+ if (PluginConfig.m_chat_enabled)
+ SM_BroadcastMessage(NULL, DM_SMILEYOPTIONSCHANGED, 0, 0, FALSE);
+ return 0;
+}
+
+/*
+ * Message API 0.0.0.3 services
+ */
+
+static INT_PTR GetWindowClass(WPARAM wParam, LPARAM lParam)
+
+{
+ char *szBuf = (char*)wParam;
+ int size = (int)lParam;
+ mir_snprintf(szBuf, size, "tabSRMM");
+ return 0;
+}
+
+/*
+ * service function. retrieves the message window data for a given hcontact or window
+ * wParam == hContact of the window to find
+ * lParam == window handle (set it to 0 if you want search for hcontact, otherwise it
+ * is directly used as the handle for the target window
+ */
+
+static INT_PTR GetWindowData(WPARAM wParam, LPARAM lParam)
+{
+ MessageWindowInputData *mwid = (MessageWindowInputData*)wParam;
+ MessageWindowOutputData *mwod = (MessageWindowOutputData*)lParam;
+ HWND hwnd;
+ SESSION_INFO *si = NULL;
+
+ if (mwid == NULL || mwod == NULL)
+ return 1;
+ if (mwid->cbSize != sizeof(MessageWindowInputData) || mwod->cbSize != sizeof(MessageWindowOutputData))
+ return 1;
+ if (mwid->hContact == NULL)
+ return 1;
+ if (mwid->uFlags != MSG_WINDOW_UFLAG_MSG_BOTH)
+ return 1;
+ hwnd = M->FindWindow(mwid->hContact);
+ if (hwnd) {
+ mwod->uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwod->hwndWindow = hwnd;
+ mwod->local = GetParent(GetParent(hwnd));
+ SendMessage(hwnd, DM_GETWINDOWSTATE, 0, 0);
+ mwod->uState = (int)GetWindowLongPtr(hwnd, DWLP_MSGRESULT);
+ return 0;
+ }
+ else if ((si = SM_FindSessionByHCONTACT(mwid->hContact)) != NULL && si->hWnd != 0) {
+ mwod->uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwod->hwndWindow = si->hWnd;
+ mwod->local = GetParent(GetParent(si->hWnd));
+ SendMessage(si->hWnd, DM_GETWINDOWSTATE, 0, 0);
+ mwod->uState = (int)GetWindowLongPtr(si->hWnd, DWLP_MSGRESULT);
+ return 0;
+ }
+ else {
+ mwod->uState = 0;
+ mwod->hContact = 0;
+ mwod->hwndWindow = 0;
+ mwod->uFlags = 0;
+ }
+ return 1;
+}
+
+/*
+ * service function. Invoke the user preferences dialog for the contact given (by handle) in wParam
+ */
+
+static INT_PTR SetUserPrefs(WPARAM wParam, LPARAM lParam)
+{
+ HWND hWnd = WindowList_Find(PluginConfig.hUserPrefsWindowList, (HANDLE)wParam);
+ if (hWnd) {
+ SetForegroundWindow(hWnd); // already open, bring it to front
+ return 0;
+ }
+ CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_USERPREFS_FRAME), 0, DlgProcUserPrefsFrame, (LPARAM)wParam);
+ return 0;
+}
+
+/*
+ * service function - open the tray menu from the TTB button
+ */
+
+static INT_PTR Service_OpenTrayMenu(WPARAM wParam, LPARAM lParam)
+{
+ SendMessage(PluginConfig.g_hwndHotkeyHandler, DM_TRAYICONNOTIFY, 101, lParam == 0 ? WM_LBUTTONUP : WM_RBUTTONUP);
+ return 0;
+}
+
+/*
+ * service function. retrieves the message window flags for a given hcontact or window
+ * wParam == hContact of the window to find
+ * lParam == window handle (set it to 0 if you want search for hcontact, otherwise it
+ * is directly used as the handle for the target window
+ */
+
+static INT_PTR GetMessageWindowFlags(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndTarget = (HWND)lParam;
+
+ if (hwndTarget == 0)
+ hwndTarget = M->FindWindow((HANDLE)wParam);
+
+ if (hwndTarget) {
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(hwndTarget, GWLP_USERDATA);
+ if (dat)
+ return (dat->dwFlags);
+ else
+ return 0;
+ } else
+ return 0;
+}
+
+/*
+ * return the version of the window api supported
+ */
+
+static INT_PTR GetWindowAPI(WPARAM wParam, LPARAM lParam)
+{
+ return PLUGIN_MAKE_VERSION(0, 0, 0, 2);
+}
+
+/*
+ * service function finds a message session
+ * wParam = contact handle for which we want the window handle
+ * thanks to bio for the suggestion of this service
+ * if wParam == 0, then lParam is considered to be a valid window handle and
+ * the function tests the popup mode of the target container
+
+ * returns the hwnd if there is an open window or tab for the given hcontact (wParam),
+ * or (if lParam was specified) the hwnd if the window exists.
+ * 0 if there is none (or the popup mode of the target container was configured to "hide"
+ * the window..
+ */
+
+INT_PTR MessageWindowOpened(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd = 0;
+ struct TContainerData *pContainer = NULL;
+
+ if (wParam)
+ hwnd = M->FindWindow((HANDLE)wParam);
+ else if (lParam)
+ hwnd = (HWND) lParam;
+ else
+ hwnd = NULL;
+
+ if (hwnd) {
+ SendMessage(hwnd, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if (pContainer) {
+ if (pContainer->dwFlags & CNT_DONTREPORT) {
+ if (IsIconic(pContainer->hwnd))
+ return 0;
+ }
+ if (pContainer->dwFlags & CNT_DONTREPORTUNFOCUSED) {
+ if (!IsIconic(pContainer->hwnd) && GetForegroundWindow() != pContainer->hwnd && GetActiveWindow() != pContainer->hwnd)
+ return 0;
+ }
+ if (pContainer->dwFlags & CNT_ALWAYSREPORTINACTIVE) {
+ if (pContainer->dwFlags & CNT_DONTREPORTFOCUSED)
+ return 0;
+
+ if (pContainer->hwndActive == hwnd)
+ return 1;
+ else
+ return 0;
+ }
+ }
+ return 1;
+ } else
+ return 0;
+}
+
+/*
+ * ReadMessageCommand is executed whenever the user wants to manually open a window.
+ * This can happen when double clicking a contact on the clist OR when opening a new
+ * message (clicking on a popup, clicking the flashing tray icon and so on).
+ */
+
+static INT_PTR ReadMessageCommand(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndExisting;
+ HANDLE hContact = ((CLISTEVENT *) lParam)->hContact;
+ struct TContainerData *pContainer = 0;
+
+ hwndExisting = M->FindWindow(hContact);
+
+ if (hwndExisting != 0)
+ SendMessage(hwndExisting, DM_ACTIVATEME, 0, 0);
+ else {
+ TCHAR szName[CONTAINER_NAMELEN + 1];
+ GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN);
+ pContainer = FindContainerByName(szName);
+ if (pContainer == NULL)
+ pContainer = CreateContainer(szName, FALSE, hContact);
+ CreateNewTabForContact(pContainer, hContact, 0, NULL, TRUE, TRUE, FALSE, 0);
+ }
+ return 0;
+}
+
+
+/*
+ * this is the Unicode version of the SendMessageCommand handler. It accepts wchar_t strings
+ * for filling the message input box with a passed message
+ */
+
+INT_PTR SendMessageCommand_W(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd;
+ char *szProto;
+ struct TNewWindowData newData = {
+ 0
+ };
+ struct TContainerData *pContainer = 0;
+ int isSplit = 1;
+
+ /*
+ * make sure that only the main UI thread will handle window creation
+ */
+ if (GetCurrentThreadId() != PluginConfig.dwThreadID) {
+ if (lParam) {
+ unsigned iLen = lstrlenW((wchar_t *)lParam);
+ wchar_t *tszText = (wchar_t *)malloc((iLen + 1) * sizeof(wchar_t));
+ wcsncpy(tszText, (wchar_t *)lParam, iLen + 1);
+ tszText[iLen] = 0;
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SENDMESSAGECOMMANDW, wParam, (LPARAM)tszText);
+ } else
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SENDMESSAGECOMMANDW, wParam, 0);
+ return 0;
+ }
+
+ /* does the HCONTACT's protocol support IM messages? */
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto) {
+ if (!CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
+ return 0;
+ } else {
+ /* unknown contact */
+ return 0;
+ }
+
+ if (hwnd = M->FindWindow((HANDLE) wParam)) {
+ if (lParam) {
+ HWND hEdit;
+ hEdit = GetDlgItem(hwnd, IDC_MESSAGE);
+ SendMessage(hEdit, EM_SETSEL, -1, SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0));
+ SendMessage(hEdit, EM_REPLACESEL, FALSE, (LPARAM)(TCHAR *) lParam);
+ }
+ SendMessage(hwnd, DM_ACTIVATEME, 0, (LPARAM) 0);
+ } else {
+ TCHAR szName[CONTAINER_NAMELEN + 1];
+
+ GetContainerNameForContact((HANDLE) wParam, szName, CONTAINER_NAMELEN);
+ pContainer = FindContainerByName(szName);
+ if (pContainer == NULL)
+ pContainer = CreateContainer(szName, FALSE, (HANDLE)wParam);
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 1, (const char *)lParam, TRUE, TRUE, FALSE, 0);
+ }
+ return 0;
+}
+
+/*
+ * the SendMessageCommand() invokes a message session window for the given contact.
+ * e.g. it is called when user double clicks a contact on the contact list
+ * it is implemented as a service, so external plugins can use it to open a message window.
+ * contacts handle must be passed in wParam.
+ */
+
+INT_PTR SendMessageCommand(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd;
+ char *szProto;
+ struct TNewWindowData newData = {
+ 0
+ };
+ struct TContainerData *pContainer = 0;
+ int isSplit = 1;
+
+ if (GetCurrentThreadId() != PluginConfig.dwThreadID) {
+ if (lParam) {
+ unsigned iLen = lstrlenA((char *)lParam);
+ char *szText = (char *)malloc(iLen + 1);
+ strncpy(szText, (char *)lParam, iLen + 1);
+ szText[iLen] = 0;
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SENDMESSAGECOMMAND, wParam, (LPARAM)szText);
+ } else
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_SENDMESSAGECOMMAND, wParam, 0);
+ return 0;
+ }
+
+ /* does the HCONTACT's protocol support IM messages? */
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, wParam, 0);
+ if (szProto) {
+ if (!CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
+ return 0;
+ } else {
+ /* unknown contact */
+ return 0;
+ }
+
+ if (hwnd = M->FindWindow((HANDLE) wParam)) {
+ if (lParam) {
+ HWND hEdit = GetDlgItem(hwnd, IDC_MESSAGE);
+ SendMessage(hEdit, EM_SETSEL, -1, SendMessage(hEdit, WM_GETTEXTLENGTH, 0, 0));
+ SendMessageA(hEdit, EM_REPLACESEL, FALSE, (LPARAM)(char *) lParam);
+ }
+ SendMessage(hwnd, DM_ACTIVATEME, 0, 0); // ask the message window about its parent...
+ } else {
+ TCHAR szName[CONTAINER_NAMELEN + 1];
+ GetContainerNameForContact((HANDLE) wParam, szName, CONTAINER_NAMELEN);
+ pContainer = FindContainerByName(szName);
+ if (pContainer == NULL)
+ pContainer = CreateContainer(szName, FALSE, (HANDLE)wParam);
+ CreateNewTabForContact(pContainer, (HANDLE) wParam, 0, (const char *) lParam, TRUE, TRUE, FALSE, 0);
+ }
+ return 0;
+}
+
+/*
+ * open a window when user clicks on the flashing "typing message" tray icon.
+ * just calls SendMessageCommand() for the given contact.
+ */
+static INT_PTR TypingMessageCommand(WPARAM wParam, LPARAM lParam)
+{
+ CLISTEVENT *cle = (CLISTEVENT *) lParam;
+
+ if (!cle)
+ return 0;
+ SendMessageCommand((WPARAM) cle->hContact, 0);
+ return 0;
+}
+
+int SplitmsgShutdown(void)
+{
+#if defined(__USE_EX_HANDLERS)
+ __try {
+#endif
+ DestroyCursor(PluginConfig.hCurSplitNS);
+ DestroyCursor(PluginConfig.hCurHyperlinkHand);
+ DestroyCursor(PluginConfig.hCurSplitWE);
+ FreeLibrary(GetModuleHandleA("riched20"));
+ if (g_hIconDLL)
+ FreeLibrary(g_hIconDLL);
+
+ ImageList_RemoveAll(PluginConfig.g_hImageList);
+ ImageList_Destroy(PluginConfig.g_hImageList);
+
+ delete Win7Taskbar;
+ delete mREOLECallback;
+
+ OleUninitialize();
+ DestroyMenu(PluginConfig.g_hMenuContext);
+ if (PluginConfig.g_hMenuContainer)
+ DestroyMenu(PluginConfig.g_hMenuContainer);
+ if (PluginConfig.g_hMenuEncoding)
+ DestroyMenu(PluginConfig.g_hMenuEncoding);
+
+ UnloadIcons();
+ FreeTabConfig();
+
+ if (Utils::rtf_ctable)
+ free(Utils::rtf_ctable);
+
+ UnloadTSButtonModule();
+
+ if (g_hIconDLL) {
+ FreeLibrary(g_hIconDLL);
+ g_hIconDLL = 0;
+ }
+#if defined(__USE_EX_HANDLERS)
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"SHUTDOWN_STAGE3", false)) {
+ return(0);
+ }
+#endif
+ return 0;
+}
+
+int MyAvatarChanged(WPARAM wParam, LPARAM lParam)
+{
+ struct TContainerData *pContainer = pFirstContainer;
+
+ if (wParam == 0 || IsBadReadPtr((void *)wParam, 4))
+ return 0;
+
+ while (pContainer) {
+ BroadCastContainer(pContainer, DM_MYAVATARCHANGED, wParam, lParam);
+ pContainer = pContainer->pNextContainer;
+ }
+ return 0;
+}
+
+int AvatarChanged(WPARAM wParam, LPARAM lParam)
+{
+ struct avatarCacheEntry *ace = (struct avatarCacheEntry *)lParam;
+ HWND hwnd = M->FindWindow((HANDLE)wParam);
+
+ if (wParam == 0) { // protocol picture has changed...
+ M->BroadcastMessage(DM_PROTOAVATARCHANGED, wParam, lParam);
+ return 0;
+ }
+ if (hwnd) {
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (dat) {
+ dat->ace = ace;
+ if(dat->hTaskbarIcon)
+ DestroyIcon(dat->hTaskbarIcon);
+ dat->hTaskbarIcon = 0;
+ DM_RecalcPictureSize(dat);
+ if (dat->showPic == 0 || dat->showInfoPic == 0)
+ GetAvatarVisibility(hwnd, dat);
+ if(dat->hwndPanelPic) {
+ dat->panelWidth = -1; // force new size calculations (not for flash avatars)
+ SendMessage(dat->hwnd, WM_SIZE, 0, 1);
+ }
+ dat->panelWidth = -1; // force new size calculations (not for flash avatars)
+ RedrawWindow(dat->hwnd, NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ALLCHILDREN);
+ SendMessage(dat->hwnd, WM_SIZE, 0, 1);
+ ShowPicture(dat, TRUE);
+ dat->dwFlagsEx |= MWF_EX_AVATARCHANGED;
+ dat->pContainer->SideBar->updateSession(dat);
+ }
+ }
+ return 0;
+}
+
+int IcoLibIconsChanged(WPARAM wParam, LPARAM lParam)
+{
+ LoadFromIconLib();
+ CacheMsgLogIcons();
+ if (PluginConfig.m_chat_enabled)
+ Chat_IconsChanged(wParam, lParam);
+ return 0;
+}
+
+int IconsChanged(WPARAM wParam, LPARAM lParam)
+{
+ CreateImageList(FALSE);
+ CacheMsgLogIcons();
+ M->BroadcastMessage(DM_OPTIONSAPPLIED, 0, 0);
+ M->BroadcastMessage(DM_UPDATEWINICON, 0, 0);
+ if (PluginConfig.m_chat_enabled)
+ Chat_IconsChanged(wParam, lParam);
+
+ return 0;
+}
+
+/**
+ * initialises the internal API, services, events etc...
+ */
+
+static struct _svcdef {
+ char *szName;
+ INT_PTR (*pfnService)(WPARAM wParam, LPARAM lParam);
+ HANDLE *h;
+} SERVICES[] = {
+ MS_MSG_SENDMESSAGE, SendMessageCommand, &PluginConfig.hSvc[CGlobals::H_MS_MSG_SENDMESSAGE],
+ MS_MSG_GETWINDOWAPI, GetWindowAPI, &PluginConfig.hSvc[CGlobals::H_MS_MSG_GETWINDOWAPI],
+ MS_MSG_GETWINDOWCLASS, GetWindowClass, &PluginConfig.hSvc[CGlobals::H_MS_MSG_GETWINDOWCLASS],
+ MS_MSG_GETWINDOWDATA, GetWindowData, &PluginConfig.hSvc[CGlobals::H_MS_MSG_GETWINDOWDATA],
+ "SRMsg/ReadMessage", ReadMessageCommand, &PluginConfig.hSvc[CGlobals::H_MS_MSG_READMESSAGE],
+ "SRMsg/TypingMessage", TypingMessageCommand, &PluginConfig.hSvc[CGlobals::H_MS_MSG_TYPINGMESSAGE],
+ MS_MSG_MOD_MESSAGEDIALOGOPENED, MessageWindowOpened, &PluginConfig.hSvc[CGlobals::H_MS_MSG_MOD_MESSAGEDIALOGOPENED],
+ MS_TABMSG_SETUSERPREFS, SetUserPrefs, &PluginConfig.hSvc[CGlobals::H_MS_TABMSG_SETUSERPREFS],
+ MS_TABMSG_TRAYSUPPORT, Service_OpenTrayMenu, &PluginConfig.hSvc[CGlobals::H_MS_TABMSG_TRAYSUPPORT],
+ MS_MSG_MOD_GETWINDOWFLAGS, GetMessageWindowFlags, &PluginConfig.hSvc[CGlobals::H_MSG_MOD_GETWINDOWFLAGS],
+ MS_TABMSG_SLQMGR, CSendLater::svcQMgr, &PluginConfig.hSvc[CGlobals::H_MS_TABMSG_SLQMGR]
+};
+
+static void TSAPI InitAPI()
+{
+ int i;
+
+ ZeroMemory(PluginConfig.hSvc, sizeof(HANDLE) * CGlobals::SERVICE_LAST);
+
+ for(i = 0; i < safe_sizeof(SERVICES); i++)
+ *(SERVICES[i].h) = CreateServiceFunction(SERVICES[i].szName, SERVICES[i].pfnService);
+
+ *(SERVICES[CGlobals::H_MS_MSG_SENDMESSAGEW].h) = CreateServiceFunction(MS_MSG_SENDMESSAGE "W", SendMessageCommand_W);
+ SI_InitStatusIcons();
+ CB_InitCustomButtons();
+
+ /*
+ * the event API
+ */
+
+ PluginConfig.m_event_MsgWin = CreateHookableEvent(ME_MSG_WINDOWEVENT);
+ PluginConfig.m_event_MsgPopup = CreateHookableEvent(ME_MSG_WINDOWPOPUP);
+}
+
+int LoadSendRecvMessageModule(void)
+{
+ INITCOMMONCONTROLSEX icex;
+
+ if(FIF == 0) {
+ MessageBox(0, TranslateT("The image service plugin (advaimg.dll) is not properly installed.\n\nTabSRMM is disabled."), TranslateT("TabSRMM fatal error"), MB_OK | MB_ICONERROR);
+ return(1);
+ }
+ icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES | ICC_LISTVIEW_CLASSES;;
+ InitCommonControlsEx(&icex);
+
+ Utils::loadSystemLibrary(L"\\riched20.dll");
+
+ OleInitialize(NULL);
+ mREOLECallback = new REOLECallback;
+ Win7Taskbar = new CTaskbarInteract;
+ Win7Taskbar->updateMetrics();
+
+ ZeroMemory((void *)&nen_options, sizeof(nen_options));
+ M->m_hMessageWindowList = (HANDLE) CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+ PluginConfig.hUserPrefsWindowList = (HANDLE) CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0);
+ sendQueue = new SendQueue;
+ Skin = new CSkin;
+ sendLater = new CSendLater;
+
+ InitOptions();
+
+ InitAPI();
+
+ PluginConfig.reloadSystemStartup();
+ ReloadTabConfig();
+ NEN_ReadOptions(&nen_options);
+
+ M->WriteByte(TEMPLATES_MODULE, "setup", 2);
+ LoadDefaultTemplates();
+
+ BuildCodePageList();
+
+ return 0;
+}
+
+STDMETHODIMP REOLECallback::GetNewStorage(LPSTORAGE FAR *lplpstg)
+{
+ LPLOCKBYTES lpLockBytes = NULL;
+ SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
+ if (sc != S_OK)
+ return sc;
+ sc = ::StgCreateDocfileOnILockBytes(lpLockBytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, lplpstg);
+ if (sc != S_OK)
+ lpLockBytes->Release();
+ return sc;
+}
+
+
+/*
+ * tabbed mode support functions...
+ * (C) by Nightwish
+ *
+ * this function searches and activates the tab belonging to the given hwnd (which is the
+ * hwnd of a message dialog window)
+ */
+
+int TSAPI ActivateExistingTab(TContainerData *pContainer, HWND hwndChild)
+{
+ struct TWindowData *dat = 0;
+ NMHDR nmhdr;
+
+ dat = (struct TWindowData *) GetWindowLongPtr(hwndChild, GWLP_USERDATA); // needed to obtain the hContact for the message window
+ if (dat && pContainer) {
+ ZeroMemory((void *)&nmhdr, sizeof(nmhdr));
+ nmhdr.code = TCN_SELCHANGE;
+ if (TabCtrl_GetItemCount(GetDlgItem(pContainer->hwnd, IDC_MSGTABS)) > 1 && !(pContainer->dwFlags & CNT_DEFERREDTABSELECT)) {
+ TabCtrl_SetCurSel(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), GetTabIndexFromHWND(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), hwndChild));
+ SendMessage(pContainer->hwnd, WM_NOTIFY, 0, (LPARAM) &nmhdr); // just select the tab and let WM_NOTIFY do the rest
+ }
+ if (dat->bType == SESSIONTYPE_IM)
+ SendMessage(pContainer->hwnd, DM_UPDATETITLE, (WPARAM)dat->hContact, 0);
+ if (IsIconic(pContainer->hwnd)) {
+ SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+ SetForegroundWindow(pContainer->hwnd);
+ }
+ //MaD - hide on close feature
+ if (!IsWindowVisible(pContainer->hwnd)) {
+ WINDOWPLACEMENT wp={0};
+ wp.length = sizeof(wp);
+ GetWindowPlacement(pContainer->hwnd, &wp);
+
+ /*
+ * all tabs must re-check the layout on activation because adding a tab while
+ * the container was hidden can make this necessary
+ */
+ BroadCastContainer(pContainer, DM_CHECKSIZE, 0, 0);
+ if(wp.showCmd == SW_SHOWMAXIMIZED)
+ ShowWindow(pContainer->hwnd, SW_SHOWMAXIMIZED);
+ else {
+ ShowWindow(pContainer->hwnd, SW_SHOWNA);
+ SetForegroundWindow(pContainer->hwnd);
+ }
+ SendMessage(pContainer->hwndActive, WM_SIZE, 0, 0); // make sure the active tab resizes its layout properly
+ }
+ //MaD_
+ else if (GetForegroundWindow() != pContainer->hwnd)
+ SetForegroundWindow(pContainer->hwnd);
+ if (dat->bType == SESSIONTYPE_IM)
+ SetFocus(GetDlgItem(hwndChild, IDC_MESSAGE));
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+/*
+ * this function creates and activates a new tab within the given container.
+ * bActivateTab: make the new tab the active one
+ * bPopupContainer: restore container if it was minimized, otherwise flash it...
+ */
+
+HWND TSAPI CreateNewTabForContact(struct TContainerData *pContainer, HANDLE hContact, int isSend, const char *pszInitialText, BOOL bActivateTab, BOOL bPopupContainer, BOOL bWantPopup, HANDLE hdbEvent)
+{
+ TCHAR *contactName = NULL, newcontactname[128], *szStatus, tabtitle[128];
+ char *szProto = NULL;
+ WORD wStatus;
+ int newItem;
+ HWND hwndNew = 0;
+ HWND hwndTab;
+ struct TNewWindowData newData = {0};
+ DBVARIANT dbv = {0};
+
+ if (M->FindWindow(hContact) != 0) {
+ _DebugPopup(hContact, _T("Warning: trying to create duplicate window"));
+ return 0;
+ }
+ // if we have a max # of tabs/container set and want to open something in the default container...
+ if (hContact != 0 && M->GetByte("limittabs", 0) && !_tcsncmp(pContainer->szName, _T("default"), 6)) {
+ if ((pContainer = FindMatchingContainer(_T("default"), hContact)) == NULL) {
+ TCHAR szName[CONTAINER_NAMELEN + 1];
+
+ _sntprintf(szName, CONTAINER_NAMELEN, _T("default"));
+ pContainer = CreateContainer(szName, CNT_CREATEFLAG_CLONED, hContact);
+ }
+ }
+
+ newData.hContact = hContact;
+ newData.isWchar = isSend;
+ newData.szInitialText = pszInitialText;
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) newData.hContact, 0);
+
+ ZeroMemory((void *)&newData.item, sizeof(newData.item));
+
+ // obtain various status information about the contact
+ contactName = (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) newData.hContact, GCDNF_TCHAR);
+
+ /*
+ * cut nickname if larger than x chars...
+ */
+
+ if (contactName && lstrlen(contactName) > 0) {
+ if (M->GetByte("cuttitle", 0))
+ CutContactName(contactName, newcontactname, safe_sizeof(newcontactname));
+ else {
+ lstrcpyn(newcontactname, contactName, safe_sizeof(newcontactname));
+ newcontactname[127] = 0;
+ }
+ //Mad: to fix tab width for nicknames with ampersands
+ Utils::DoubleAmpersands(newcontactname);
+ } else
+ lstrcpyn(newcontactname, _T("_U_"), safe_sizeof(newcontactname));
+
+ wStatus = szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord((HANDLE) newData.hContact, szProto, "Status", ID_STATUS_OFFLINE);
+ szStatus = (TCHAR *) CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, szProto == NULL ? ID_STATUS_OFFLINE : DBGetContactSettingWord((HANDLE)newData.hContact, szProto, "Status", ID_STATUS_OFFLINE), GSMDF_TCHAR);
+
+ if (M->GetByte("tabstatus", 1))
+ mir_sntprintf(tabtitle, safe_sizeof(tabtitle), _T("%s (%s) "), newcontactname, szStatus);
+ else
+ mir_sntprintf(tabtitle, safe_sizeof(tabtitle), _T("%s "), newcontactname);
+
+ newData.item.pszText = tabtitle;
+ newData.item.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
+ newData.item.iImage = 0;
+ newData.item.cchTextMax = 255;
+
+ hwndTab = GetDlgItem(pContainer->hwnd, IDC_MSGTABS);
+ // hide the active tab
+ if (pContainer->hwndActive && bActivateTab)
+ ShowWindow(pContainer->hwndActive, SW_HIDE);
+
+ {
+ int iTabIndex_wanted = M->GetDword(hContact, "tabindex", pContainer->iChilds * 100);
+ int iCount = TabCtrl_GetItemCount(hwndTab);
+ TCITEM item = {0};
+ HWND hwnd;
+ struct TWindowData *dat;
+ int relPos;
+ int i;
+
+ pContainer->iTabIndex = iCount;
+ if (iCount > 0) {
+ for (i = iCount - 1; i >= 0; i--) {
+ item.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwndTab, i, &item);
+ hwnd = (HWND)item.lParam;
+ dat = (struct TWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (dat) {
+ relPos = M->GetDword(dat->hContact, "tabindex", i * 100);
+ if (iTabIndex_wanted <= relPos)
+ pContainer->iTabIndex = i;
+ }
+ }
+ }
+ }
+ newItem = TabCtrl_InsertItem(hwndTab, pContainer->iTabIndex, &newData.item);
+ SendMessage(hwndTab, EM_REFRESHWITHOUTCLIP, 0, 0);
+ if (bActivateTab)
+ TabCtrl_SetCurSel(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), newItem);
+ newData.iTabID = newItem;
+ newData.iTabImage = newData.item.iImage;
+ newData.pContainer = pContainer;
+ newData.iActivate = (int) bActivateTab;
+ pContainer->iChilds++;
+ newData.bWantPopup = bWantPopup;
+ newData.hdbEvent = hdbEvent;
+ hwndNew = CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGSPLITNEW), GetDlgItem(pContainer->hwnd, IDC_MSGTABS), DlgProcMessage, (LPARAM) & newData);
+ /*
+ * switchbar support
+ */
+ if(pContainer->dwFlags & CNT_SIDEBAR) {
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(hwndNew, GWLP_USERDATA);
+ if(dat)
+ pContainer->SideBar->addSession(dat, pContainer->iTabIndex);
+ }
+ SendMessage(pContainer->hwnd, WM_SIZE, 0, 0);
+
+ // if the container is minimized, then pop it up...
+
+ if (IsIconic(pContainer->hwnd)) {
+ if (bPopupContainer) {
+ SendMessage(pContainer->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+ SetFocus(pContainer->hwndActive);
+ } else {
+ if (pContainer->dwFlags & CNT_NOFLASH)
+ SendMessage(pContainer->hwnd, DM_SETICON, 0, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_MESSAGE));
+ else
+ FlashContainer(pContainer, 1, 0);
+ }
+ }
+ if (bActivateTab) {
+ ActivateExistingTab(pContainer, hwndNew);
+ SetFocus(hwndNew);
+ RedrawWindow(pContainer->hwnd, NULL, NULL, RDW_ERASENOW);
+ UpdateWindow(pContainer->hwnd);
+ if (GetForegroundWindow() != pContainer->hwnd && bPopupContainer == TRUE)
+ SetForegroundWindow(pContainer->hwnd);
+ }
+ else if(!IsIconic(pContainer->hwnd) && IsWindowVisible(pContainer->hwnd)){
+ SendMessage(pContainer->hwndActive, WM_SIZE, 0, 0);
+ RedrawWindow(pContainer->hwndActive, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW);
+ RedrawWindow(pContainer->hwndActive, NULL, NULL, RDW_ERASENOW | RDW_UPDATENOW);
+ }
+
+ //MaD
+ if (PluginConfig.m_HideOnClose&&!IsWindowVisible(pContainer->hwnd)){
+ WINDOWPLACEMENT wp={0};
+ wp.length = sizeof(wp);
+ GetWindowPlacement(pContainer->hwnd, &wp);
+
+ BroadCastContainer(pContainer, DM_CHECKSIZE, 0, 0); // make sure all tabs will re-check layout on activation
+ if(wp.showCmd == SW_SHOWMAXIMIZED)
+ ShowWindow(pContainer->hwnd, SW_SHOWMAXIMIZED);
+ else {
+ if(bPopupContainer)
+ ShowWindow(pContainer->hwnd, SW_SHOWNORMAL);
+ else
+ ShowWindow(pContainer->hwnd, SW_SHOWMINNOACTIVE);
+ }
+ SendMessage(pContainer->hwndActive, WM_SIZE, 0, 0);
+ }
+ if(PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek && CSkin::m_skinEnabled) // && !M->GetByte("forceAeroPeek", 0))
+ CWarning::show(CWarning::WARN_AEROPEEK_SKIN, MB_ICONWARNING|MB_OK);
+
+ if(ServiceExists(MS_HPP_EG_EVENT) && ServiceExists(MS_IEVIEW_EVENT) && M->GetByte(0, "HistoryPlusPlus", "IEViewAPI", 0)) {
+ if(IDYES == CWarning::show(CWarning::WARN_HPP_APICHECK, MB_ICONWARNING|MB_YESNO))
+ M->WriteByte(0, "HistoryPlusPlus", "IEViewAPI", 0);
+ }
+ return hwndNew; // return handle of the new dialog
+}
+
+/*
+ * this is used by the 2nd containermode (limit tabs on default containers).
+ * it searches a container with "room" for the new tabs or otherwise creates
+ * a new (cloned) one.
+ */
+
+struct TContainerData* TSAPI FindMatchingContainer(const TCHAR *szName, HANDLE hContact)
+{
+ struct TContainerData *pDesired = 0;
+ int iMaxTabs = M->GetDword("maxtabs", 0);
+
+ if (iMaxTabs > 0 && M->GetByte("limittabs", 0) && !_tcsncmp(szName, _T("default"), 6)) {
+ struct TContainerData *pCurrent = pFirstContainer;
+ // search a "default" with less than iMaxTabs opened...
+ while (pCurrent) {
+ if (!_tcsncmp(pCurrent->szName, _T("default"), 6) && pCurrent->iChilds < iMaxTabs) {
+ pDesired = pCurrent;
+ break;
+ }
+ pCurrent = pCurrent->pNextContainer;
+ }
+ return(pDesired != NULL ? pDesired : NULL);
+ } else
+ return FindContainerByName(szName);
+}
+/*
+ * load some global icons.
+ */
+
+void TSAPI CreateImageList(BOOL bInitial)
+{
+ HICON hIcon;
+ int cxIcon = GetSystemMetrics(SM_CXSMICON);
+ int cyIcon = GetSystemMetrics(SM_CYSMICON);
+
+ /*
+ * the imagelist is now a fake. It is still needed to provide the tab control with a
+ * image list handle. This will make sure that the tab control will reserve space for
+ * an icon on each tab. This is a blank and empty icon
+ */
+
+ if (bInitial) {
+ PluginConfig.g_hImageList = ImageList_Create(16, 16, PluginConfig.m_bIsXP ? ILC_COLOR32 | ILC_MASK : ILC_COLOR8 | ILC_MASK, 2, 0);
+ hIcon = CreateIcon(g_hInst, 16, 16, 1, 4, NULL, NULL);
+ ImageList_AddIcon(PluginConfig.g_hImageList, hIcon);
+ DestroyIcon(hIcon);
+ }
+
+ PluginConfig.g_IconFileEvent = LoadSkinnedIcon(SKINICON_EVENT_FILE);
+ PluginConfig.g_IconMsgEvent = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ PluginConfig.g_IconMsgEventBig = LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE);
+ if((HICON)CALLSERVICE_NOTFOUND == PluginConfig.g_IconMsgEventBig)
+ PluginConfig.g_IconMsgEventBig = 0;
+ PluginConfig.g_IconTypingEventBig = LoadSkinnedIconBig(SKINICON_OTHER_TYPING);
+ if((HICON)CALLSERVICE_NOTFOUND == PluginConfig.g_IconTypingEventBig)
+ PluginConfig.g_IconTypingEventBig = 0;
+ PluginConfig.g_IconSend = PluginConfig.g_buttonBarIcons[9];
+ PluginConfig.g_IconTypingEvent = PluginConfig.g_buttonBarIcons[5];
+}
+
+int TABSRMM_FireEvent(HANDLE hContact, HWND hwnd, unsigned int type, unsigned int subType)
+{
+ MessageWindowEventData mwe = { 0 };
+ struct TABSRMM_SessionInfo se = { 0 };
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ BYTE bType = dat ? dat->bType : SESSIONTYPE_IM;
+
+ if (hContact == NULL || hwnd == NULL)
+ return 0;
+
+ if (!M->GetByte("_eventapi", 1))
+ return 0;
+ mwe.cbSize = sizeof(mwe);
+ mwe.hContact = hContact;
+ mwe.hwndWindow = hwnd;
+ mwe.szModule = "tabSRMsgW";
+ mwe.uType = type;
+ mwe.hwndInput = GetDlgItem(hwnd, bType == SESSIONTYPE_IM ? IDC_MESSAGE : IDC_CHAT_MESSAGE);
+ mwe.hwndLog = GetDlgItem(hwnd, bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG);
+
+ if (type == MSG_WINDOW_EVT_CUSTOM) {
+ se.cbSize = sizeof(se);
+ se.evtCode = HIWORD(subType);
+ se.hwnd = hwnd;
+ se.extraFlags = (unsigned int)(LOWORD(subType));
+ se.local = (void *)dat->sendBuffer;
+ mwe.local = (void *) & se;
+ } else
+ mwe.local = NULL;
+ return(NotifyEventHooks(PluginConfig.m_event_MsgWin, 0, (LPARAM)&mwe));
+}
+
+/*
+ * standard icon definitions
+ */
+
+static TIconDesc _toolbaricons[] = {
+ "tabSRMM_mlog", LPGEN("Message Log Options"), &PluginConfig.g_buttonBarIcons[2], -IDI_MSGLOGOPT, 1,
+ "tabSRMM_multi", LPGEN("Image tag"), &PluginConfig.g_buttonBarIcons[3], -IDI_IMAGETAG, 1,
+ "tabSRMM_quote", LPGEN("Quote text"), &PluginConfig.g_buttonBarIcons[8], -IDI_QUOTE, 1,
+ "tabSRMM_save", LPGEN("Save and close"), &PluginConfig.g_buttonBarIcons[7], -IDI_SAVE, 1,
+ "tabSRMM_send", LPGEN("Send message"), &PluginConfig.g_buttonBarIcons[9], -IDI_SEND, 1,
+ "tabSRMM_avatar", LPGEN("Edit user notes"), &PluginConfig.g_buttonBarIcons[10], -IDI_CONTACTPIC, 1,
+ "tabSRMM_close", LPGEN("Close"), &PluginConfig.g_buttonBarIcons[6], -IDI_CLOSEMSGDLG, 1,
+ NULL, NULL, NULL, 0, 0
+};
+
+static TIconDesc _exttoolbaricons[] = {
+ "tabSRMM_emoticon", LPGEN("Smiley button"), &PluginConfig.g_buttonBarIcons[11], -IDI_SMILEYICON, 1,
+ "tabSRMM_bold", LPGEN("Format bold"), &PluginConfig.g_buttonBarIcons[17], -IDI_FONTBOLD, 1,
+ "tabSRMM_italic", LPGEN("Format italic"), &PluginConfig.g_buttonBarIcons[18], -IDI_FONTITALIC, 1,
+ "tabSRMM_underline", LPGEN("Format underline"), &PluginConfig.g_buttonBarIcons[19], -IDI_FONTUNDERLINE, 1,
+ "tabSRMM_face", LPGEN("Font face"), &PluginConfig.g_buttonBarIcons[20], -IDI_FONTFACE, 1,
+ "tabSRMM_color", LPGEN("Font color"), &PluginConfig.g_buttonBarIcons[21], -IDI_FONTCOLOR, 1,
+ "tabSRMM_strikeout", LPGEN("Format strike-through"), &PluginConfig.g_buttonBarIcons[30], -IDI_STRIKEOUT, 1,
+ NULL, NULL, NULL, 0, 0
+};
+//MAD
+static TIconDesc _chattoolbaricons[] = {
+ "chat_bkgcol",LPGEN("Background colour"), &PluginConfig.g_buttonBarIcons[31] ,-IDI_BKGCOLOR, 1,
+ "chat_settings",LPGEN("Room settings"), &PluginConfig.g_buttonBarIcons[32],-IDI_TOPICBUT, 1,
+ "chat_filter",LPGEN("Event filter"), &PluginConfig.g_buttonBarIcons[33] ,-IDI_FILTER2, 1,
+ "chat_shownicklist",LPGEN("Nick list"),&PluginConfig.g_buttonBarIcons[35] ,-IDI_SHOWNICKLIST, 1,
+ NULL, NULL, NULL, 0, 0
+ };
+//
+static TIconDesc _logicons[] = {
+ "tabSRMM_error", LPGEN("Message delivery error"), &PluginConfig.g_iconErr, -IDI_MSGERROR, 1,
+ "tabSRMM_in", LPGEN("Incoming message"), &PluginConfig.g_iconIn, -IDI_ICONIN, 0,
+ "tabSRMM_out", LPGEN("Outgoing message"), &PluginConfig.g_iconOut, -IDI_ICONOUT, 0,
+ "tabSRMM_status", LPGEN("Statuschange"), &PluginConfig.g_iconStatus, -IDI_STATUSCHANGE, 0,
+ NULL, NULL, NULL, 0, 0
+};
+static TIconDesc _deficons[] = {
+ "tabSRMM_container", LPGEN("Static container icon"), &PluginConfig.g_iconContainer, -IDI_CONTAINER, 1,
+ "tabSRMM_sounds_on", LPGEN("Sounds (status bar)"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS], -IDI_SOUNDSON, 1,
+ "tabSRMM_pulldown", LPGEN("Pulldown Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_PULLDOWN], -IDI_PULLDOWNARROW, 1,
+ "tabSRMM_Leftarrow", LPGEN("Left Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_LEFT], -IDI_LEFTARROW, 1,
+ "tabSRMM_Rightarrow", LPGEN("Right Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_RIGHT], -IDI_RIGHTARROW, 1,
+ "tabSRMM_Pulluparrow", LPGEN("Up Arrow"), &PluginConfig.g_buttonBarIcons[ICON_DEFAULT_UP], -IDI_PULLUPARROW, 1,
+ "tabSRMM_sb_slist", LPGEN("Session List"), &PluginConfig.g_sideBarIcons[0], -IDI_SESSIONLIST, 1,
+ NULL, NULL, NULL, 0, 0
+};
+static TIconDesc _trayIcon[] = {
+ "tabSRMM_frame1", LPGEN("Frame 1"), &PluginConfig.m_AnimTrayIcons[0], -IDI_TRAYANIM1, 1,
+ "tabSRMM_frame2", LPGEN("Frame 2"), &PluginConfig.m_AnimTrayIcons[1], -IDI_TRAYANIM2, 1,
+ "tabSRMM_frame3", LPGEN("Frame 3"), &PluginConfig.m_AnimTrayIcons[2], -IDI_TRAYANIM3, 1,
+ "tabSRMM_frame4", LPGEN("Frame 4"), &PluginConfig.m_AnimTrayIcons[3], -IDI_TRAYANIM4, 1,
+ NULL, NULL, NULL, 0, 0
+};
+
+static struct _iconblocks {
+ char *szSection;
+ TIconDesc *idesc;
+} ICONBLOCKS[] = {
+ "TabSRMM/Default", _deficons,
+ "TabSRMM/Toolbar", _toolbaricons,
+ "TabSRMM/Toolbar", _exttoolbaricons,
+ "TabSRMM/Toolbar", _chattoolbaricons,
+ "TabSRMM/Message Log", _logicons,
+ "TabSRMM/Animated Tray", _trayIcon,
+ NULL, 0
+};
+
+static int GetIconPackVersion(HMODULE hDLL)
+{
+ char szIDString[256];
+ int version = 0;
+
+ if (LoadStringA(hDLL, IDS_IDENTIFY, szIDString, sizeof(szIDString)) == 0)
+ version = 0;
+ else {
+ if (!strcmp(szIDString, "__tabSRMM_ICONPACK 1.0__"))
+ version = 1;
+ else if (!strcmp(szIDString, "__tabSRMM_ICONPACK 2.0__"))
+ version = 2;
+ else if (!strcmp(szIDString, "__tabSRMM_ICONPACK 3.0__"))
+ version = 3;
+ else if (!strcmp(szIDString, "__tabSRMM_ICONPACK 3.5__"))
+ version = 4;
+ else if (!strcmp(szIDString, "__tabSRMM_ICONPACK 5.0__"))
+ version = 5;
+ }
+
+ if (version < 5)
+ CWarning::show(CWarning::WARN_ICONPACK_VERSION, MB_OK|MB_ICONERROR);
+ return version;
+}
+/*
+ * setup default icons for the IcoLib service. This needs to be done every time the plugin is loaded
+ * default icons are taken from the icon pack in either \icons or \plugins
+ */
+
+static int TSAPI SetupIconLibConfig()
+{
+ SKINICONDESC sid = { 0 };
+ char szFilename[MAX_PATH];
+ int i = 0, j = 2, version = 0, n = 0;
+
+ strncpy(szFilename, "icons\\tabsrmm_icons.dll", MAX_PATH);
+ g_hIconDLL = LoadLibraryA(szFilename);
+ if (g_hIconDLL == 0) {
+ CWarning::show(CWarning::WARN_ICONPACKMISSING, CWarning::CWF_NOALLOWHIDE|MB_ICONERROR|MB_OK);
+ return 0;
+ }
+
+ GetModuleFileNameA(g_hIconDLL, szFilename, MAX_PATH);
+ if (PluginConfig.m_chat_enabled)
+ Chat_AddIcons();
+ version = GetIconPackVersion(g_hIconDLL);
+ FreeLibrary(g_hIconDLL);
+ g_hIconDLL = 0;
+
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.pszDefaultFile = szFilename;
+
+ while (ICONBLOCKS[n].szSection) {
+ i = 0;
+ sid.pszSection = ICONBLOCKS[n].szSection;
+ while (ICONBLOCKS[n].idesc[i].szDesc) {
+ sid.pszName = ICONBLOCKS[n].idesc[i].szName;
+ sid.pszDescription = ICONBLOCKS[n].idesc[i].szDesc;
+ sid.iDefaultIndex = ICONBLOCKS[n].idesc[i].uId == -IDI_HISTORY ? 0 : ICONBLOCKS[n].idesc[i].uId; // workaround problem /w icoLib and a resource id of 1 (actually, a Windows problem)
+ i++;
+ if(n>0&&n<4)
+ PluginConfig.g_buttonBarIconHandles[j++]=(HANDLE)CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ else
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+ n++;
+ }
+
+ sid.pszSection = "TabSRMM/Default";
+
+ sid.pszName = "tabSRMM_clock_symbol";
+ sid.pszDescription = "Clock symbol (for the info panel clock)";
+ sid.iDefaultIndex = -IDI_CLOCK;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+ strncpy(szFilename, "plugins\\tabsrmm.dll", MAX_PATH);
+
+ sid.pszName = "tabSRMM_overlay_disabled";
+ sid.pszDescription = "Feature disabled (used as overlay)";
+ sid.iDefaultIndex = -IDI_FEATURE_DISABLED;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+ sid.pszName = "tabSRMM_overlay_enabled";
+ sid.pszDescription = "Feature enabled (used as overlay)";
+ sid.iDefaultIndex = -IDI_FEATURE_ENABLED;
+ CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+
+
+ return 1;
+}
+
+// load the icon theme from IconLib - check if it exists...
+
+static int TSAPI LoadFromIconLib()
+{
+ int i = 0, n = 0;
+
+ while (ICONBLOCKS[n].szSection) {
+ i = 0;
+ while (ICONBLOCKS[n].idesc[i].szDesc) {
+ *(ICONBLOCKS[n].idesc[i].phIcon) = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)ICONBLOCKS[n].idesc[i].szName);
+ i++;
+ }
+ n++;
+ }
+ PluginConfig.g_buttonBarIcons[0] = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_8");
+ PluginConfig.g_buttonBarIcons[1] = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_10");
+ PluginConfig.g_buttonBarIconHandles[0] = (HICON)CallService(MS_SKIN2_GETICONHANDLE, 0, (LPARAM)"core_main_10");
+ PluginConfig.g_buttonBarIconHandles[1] = (HICON)CallService(MS_SKIN2_GETICONHANDLE, 0, (LPARAM)"core_main_8");
+ PluginConfig.g_buttonBarIconHandles[20] = (HICON)CallService(MS_SKIN2_GETICONHANDLE, 0, (LPARAM)"core_main_9");
+
+ PluginConfig.g_buttonBarIcons[5] = PluginConfig.g_buttonBarIcons[12] = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_23");
+ PluginConfig.g_IconChecked = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_19");
+ PluginConfig.g_IconUnchecked = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_20");
+ PluginConfig.g_IconFolder = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"core_main_5");
+
+ PluginConfig.g_iconOverlayEnabled = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"tabSRMM_overlay_enabled");
+ PluginConfig.g_iconOverlayDisabled = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"tabSRMM_overlay_disabled");
+
+ PluginConfig.g_iconClock = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"tabSRMM_clock_symbol");
+
+ CacheMsgLogIcons();
+ M->BroadcastMessage(DM_LOADBUTTONBARICONS, 0, 0);
+ return 0;
+}
+
+/*
+ * load icon theme from either icon pack or IcoLib
+ */
+
+void TSAPI LoadIconTheme()
+{
+ if (SetupIconLibConfig() == 0)
+ return;
+ else
+ LoadFromIconLib();
+
+ Skin->setupTabCloseBitmap();
+ return;
+}
+
+static void UnloadIcons()
+{
+ int i = 0, n = 0;
+
+ while (ICONBLOCKS[n].szSection) {
+ i = 0;
+ while (ICONBLOCKS[n].idesc[i].szDesc) {
+ if (*(ICONBLOCKS[n].idesc[i].phIcon) != 0) {
+ DestroyIcon(*(ICONBLOCKS[n].idesc[i].phIcon));
+ *(ICONBLOCKS[n].idesc[i].phIcon) = 0;
+ }
+ i++;
+ }
+ n++;
+ }
+ if (PluginConfig.g_hbmUnknown)
+ DeleteObject(PluginConfig.g_hbmUnknown);
+ for (i = 0; i < 4; i++) {
+ if (PluginConfig.m_AnimTrayIcons[i])
+ DestroyIcon(PluginConfig.m_AnimTrayIcons[i]);
+ }
+}
diff --git a/plugins/TabSRMM/src/selectcontainer.cpp b/plugins/TabSRMM/src/selectcontainer.cpp new file mode 100644 index 0000000000..975f7496a4 --- /dev/null +++ b/plugins/TabSRMM/src/selectcontainer.cpp @@ -0,0 +1,226 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: selectcontainer.cpp 12351 2010-08-21 21:44:54Z Michael.Kunz@s2005.TU-Chemnitz.de $
+ *
+ * dialog to manage containers (attaching sessions to containers etc.)
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+INT_PTR CALLBACK SelectContainerDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndMsgDlg = 0;
+
+ hwndMsgDlg = (HWND) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TCHAR szNewTitle[128];
+ RECT rc, rcParent;
+ struct TContainerData *pContainer = 0;
+
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR) lParam);
+ hwndMsgDlg = (HWND) lParam;
+
+ TranslateDialogDefault(hwndDlg);
+
+ if (lParam) {
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr((HWND)lParam, GWLP_USERDATA);
+ if (dat) {
+ mir_sntprintf(szNewTitle, safe_sizeof(szNewTitle), CTranslator::get(CTranslator::CNT_SELECT_FOR), dat->cache->getNick());
+ SetWindowText(hwndDlg, szNewTitle);
+ }
+ }
+
+ SendMessage(hwndDlg, DM_SC_BUILDLIST, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_NEWCONTAINERNAME, EM_LIMITTEXT, (WPARAM)CONTAINER_NAMELEN, 0);
+ SendDlgItemMessage(hwndDlg, IDC_NEWCONTAINER, EM_LIMITTEXT, (WPARAM)CONTAINER_NAMELEN, 0);
+
+ GetWindowRect(hwndDlg, &rc);
+ GetWindowRect(GetParent(hwndDlg), &rcParent);
+ SetWindowPos(hwndDlg, 0, (rcParent.left + rcParent.right - (rc.right - rc.left)) / 2, (rcParent.top + rcParent.bottom - (rc.bottom - rc.top)) / 2, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK: {
+ TCHAR szName[CONTAINER_NAMELEN];
+ LRESULT iItem;
+
+ if ((iItem = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETCURSEL, 0, 0)) != LB_ERR) {
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETTEXT, (WPARAM) iItem, (LPARAM) szName);
+ if (IsWindow(hwndMsgDlg))
+ SendMessage(hwndMsgDlg, DM_CONTAINERSELECTED, 0, (LPARAM) szName);
+ }
+ if (IsWindow(hwndDlg))
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ case IDC_DELETECONTAINER: {
+ TCHAR szName[CONTAINER_NAMELEN + 1];
+ LRESULT iItem;
+
+ if ((iItem = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETCURSEL, 0, 0)) != LB_ERR) {
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETTEXT, (WPARAM) iItem, (LPARAM) szName);
+ if (!_tcsncmp(szName, _T("default"), CONTAINER_NAMELEN) || !_tcsncmp(szName, CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME), CONTAINER_NAMELEN))
+ MessageBox(hwndDlg, CTranslator::get(CTranslator::CNT_SELECT_DELETEERROR), _T("Error"), MB_OK | MB_ICONERROR);
+ else {
+ int iIndex = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETITEMDATA, (WPARAM)iItem, 0);
+ DeleteContainer(iIndex);
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_RESETCONTENT, 0, 0);
+ SendMessage(hwndDlg, DM_SC_BUILDLIST, 0, 0);
+ BuildContainerMenu();
+ }
+ }
+ break;
+ }
+ case IDC_RENAMECONTAINER: {
+ TCHAR szNewName[CONTAINER_NAMELEN], szName[CONTAINER_NAMELEN + 1];
+ int iLen, iItem;
+ struct TContainerData *pCurrent = pFirstContainer;
+
+ iLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_NEWCONTAINERNAME));
+ if (iLen) {
+ GetWindowText(GetDlgItem(hwndDlg, IDC_NEWCONTAINERNAME), szNewName, CONTAINER_NAMELEN);
+ if(!_tcsncmp(szNewName, CGlobals::m_default_container_name, CONTAINER_NAMELEN) || !_tcsncmp(szNewName, CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME), CONTAINER_NAMELEN)) {
+ MessageBox(hwndDlg, CTranslator::get(CTranslator::CNT_SELECT_RENAMEERROR), _T("Error"), MB_OK | MB_ICONERROR);
+ break;
+ }
+
+ iItem = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_FINDSTRING, (WPARAM) - 1, (LPARAM) szNewName);
+ if (iItem != LB_ERR) {
+ TCHAR szOldName[CONTAINER_NAMELEN + 1];
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETTEXT, (WPARAM) iItem, (LPARAM) szOldName);
+ if (lstrlen(szOldName) == lstrlen(szNewName)) {
+ MessageBox(0, CTranslator::get(CTranslator::CNT_SELECT_INUSE), _T("Error"), MB_OK | MB_ICONERROR);
+ SetFocus(GetDlgItem(hwndDlg, IDC_NEWCONTAINERNAME));
+ break;
+ }
+ }
+ if ((iItem = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETCURSEL, 0, 0)) != LB_ERR) {
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETTEXT, (WPARAM) iItem, (LPARAM) szName);
+ if (!_tcsncmp(szName, _T("default"), CONTAINER_NAMELEN) || !_tcsncmp(szName, CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME), CONTAINER_NAMELEN))
+ MessageBox(hwndDlg, CTranslator::get(CTranslator::CNT_SELECT_RENAMEERROR), _T("Error"), MB_OK | MB_ICONERROR);
+ else {
+ int iIndex = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETITEMDATA, (WPARAM)iItem, 0);
+ RenameContainer(iIndex, szNewName);
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_RESETCONTENT, 0, 0);
+ while (pCurrent) {
+ if (!_tcsncmp(pCurrent->szName, szName, CONTAINER_NAMELEN) && lstrlen(pCurrent->szName) == lstrlen(szName)) {
+ _tcsncpy(pCurrent->szName, szNewName, CONTAINER_NAMELEN);
+ SendMessage(pCurrent->hwnd, DM_CONFIGURECONTAINER, 0, 0);
+ }
+ pCurrent = pCurrent->pNextContainer;
+ }
+ SendMessage(hwndDlg, DM_SC_BUILDLIST, 0, 0);
+ BuildContainerMenu();
+ }
+ }
+ }
+ break;
+ }
+ case IDC_CREATENEW: {
+ int iLen, iItem;
+ TCHAR szNewName[CONTAINER_NAMELEN], szName[CONTAINER_NAMELEN + 1];
+
+ iLen = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_NEWCONTAINER));
+ if (iLen) {
+ GetWindowText(GetDlgItem(hwndDlg, IDC_NEWCONTAINER), szNewName, CONTAINER_NAMELEN);
+ iItem = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_FINDSTRING, (WPARAM) - 1, (LPARAM) szNewName);
+ if (iItem != LB_ERR || !_tcsncmp(szNewName, CGlobals::m_default_container_name, CONTAINER_NAMELEN)) {
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_GETTEXT, (WPARAM)iItem, (LPARAM)szName);
+ if (lstrlen(szName) == lstrlen(szNewName) || !_tcsncmp(szNewName, CGlobals::m_default_container_name, CONTAINER_NAMELEN)) {
+ MessageBox(0, CTranslator::get(CTranslator::CNT_SELECT_INUSE), _T("Error"), MB_OK | MB_ICONERROR);
+ SetFocus(GetDlgItem(hwndDlg, IDC_NEWCONTAINER));
+ break;
+ }
+ }
+ if (IsWindow(hwndMsgDlg)) {
+ SendMessage(hwndMsgDlg, DM_CONTAINERSELECTED, 0, (LPARAM) szNewName);
+ if (IsWindow(hwndDlg))
+ DestroyWindow(hwndDlg);
+ }
+ }
+ break;
+ }
+ case IDC_CNTLIST:
+ if (HIWORD(wParam) == LBN_DBLCLK)
+ SendMessage(hwndDlg, WM_COMMAND, IDOK, 0);
+ break;
+ }
+ break;
+ /*
+ * fill the list box...
+ */
+ case DM_SC_BUILDLIST: {
+ DBVARIANT dbv;
+ int iCounter = 0, iItemNew;
+ char *szKey = "TAB_ContainersW";
+ char szValue[10];
+ struct TContainerData *pContainer = 0;
+ do {
+ _snprintf(szValue, 8, "%d", iCounter);
+ if (M->GetTString(NULL, szKey, szValue, &dbv))
+ break; // end of list
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR) {
+ if (_tcsncmp(dbv.ptszVal, _T("**free**"), CONTAINER_NAMELEN)) {
+ iItemNew = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_ADDSTRING, 0, (LPARAM)(!_tcscmp(dbv.ptszVal, _T("default")) ?
+ CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME) : dbv.ptszVal));
+ if (iItemNew != LB_ERR)
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_SETITEMDATA, (WPARAM)iItemNew, (LPARAM)iCounter);
+ }
+ DBFreeVariant(&dbv);
+ }
+ } while (++iCounter);
+
+ /*
+ * highlight the name of the container to which the message window currently is assigned
+ */
+
+ SendMessage(hwndMsgDlg, DM_QUERYCONTAINER, 0, (LPARAM)&pContainer);
+ if (pContainer) {
+ LRESULT iItem;
+
+ iItem = SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_FINDSTRING, (WPARAM) - 1, (LPARAM)(!_tcscmp(pContainer->szName, _T("default")) ?
+ CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME) : pContainer->szName));
+ if (iItem != LB_ERR)
+ SendDlgItemMessage(hwndDlg, IDC_CNTLIST, LB_SETCURSEL, (WPARAM) iItem, 0);
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
diff --git a/plugins/TabSRMM/src/sendlater.cpp b/plugins/TabSRMM/src/sendlater.cpp new file mode 100644 index 0000000000..2cbf0b7237 --- /dev/null +++ b/plugins/TabSRMM/src/sendlater.cpp @@ -0,0 +1,989 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: sendlater.cpp 14225 2012-05-08 19:49:18Z george.hazan $
+ *
+ * the sendlater class implementation
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+#define U_PREF_UNICODE PREF_UNICODE
+/*
+ * implementation of the CSendLaterJob class
+ */
+
+CSendLaterJob::CSendLaterJob()
+{
+ ZeroMemory(this, sizeof(CSendLaterJob));
+ fSuccess = false;
+}
+
+/**
+ * return true if this job is persistent (saved to the database).
+ * such a job will survive a restart of Miranda
+ */
+bool CSendLaterJob::isPersistentJob()
+{
+ return(szId[0] == 'S' ? true : false);
+}
+
+/**
+ * check conditions for deletion
+ */
+bool CSendLaterJob::mustDelete()
+{
+ if(fSuccess)
+ return(true);
+ else if(fFailed) {
+ if(bCode == JOB_REMOVABLE)
+ return(true);
+ }
+ return(false);
+}
+
+/**
+ * clean database entries for a persistent job (currently: manual send later jobs)
+ */
+void CSendLaterJob::cleanDB()
+{
+ if(isPersistentJob()) {
+ char szKey[100];
+
+ DBDeleteContactSetting(hContact, "SendLater", szId);
+ int iCount = M->GetDword(hContact, "SendLater", "count", 0);
+ if(iCount)
+ iCount--;
+ M->WriteDword(hContact, "SendLater", "count", iCount);
+ /*
+ * delete flags
+ */
+ mir_snprintf(szKey, 100, "$%s", szId);
+ DBDeleteContactSetting(hContact, "SendLater", szKey);
+ }
+}
+
+/**
+ * read flags for a persistent jobs from the db
+ * flag key name is the job id with a "$" prefix.
+ */
+void CSendLaterJob::readFlags()
+{
+ if(isPersistentJob()) {
+ char szKey[100];
+ DWORD localFlags;
+
+ mir_snprintf(szKey, 100, "$%s", szId);
+ localFlags = M->GetDword(hContact, "SendLater", szKey, 0);
+
+ if(localFlags & SLF_SUSPEND)
+ bCode = JOB_HOLD;
+ }
+}
+
+/**
+ * write flags for a persistent jobs from the db
+ * flag key name is the job id with a "$" prefix.
+ */
+void CSendLaterJob::writeFlags()
+{
+ if(isPersistentJob()) {
+ DWORD localFlags = (bCode == JOB_HOLD ? SLF_SUSPEND : 0);
+ char szKey[100];
+
+ mir_snprintf(szKey, 100, "$%s", szId);
+ M->WriteDword(hContact, "SendLater", szKey, localFlags);
+ }
+}
+
+/**
+ * delete a send later job
+ */
+CSendLaterJob::~CSendLaterJob()
+{
+ if(fSuccess || fFailed) {
+ POPUPDATAT_V2 ppd = {0};
+
+ if((sendLater->haveErrorPopups() && fFailed) || (sendLater->haveSuccessPopups() && fSuccess)) {
+ bool fShowPopup = true;
+
+ if(fFailed && bCode == JOB_REMOVABLE) // no popups for jobs removed on user's request
+ fShowPopup = false;
+ /*
+ * show a popup notification, unless they are disabled
+ */
+ if (PluginConfig.g_PopupAvail && fShowPopup) {
+ TCHAR *tszName = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR);
+
+ ZeroMemory((void *)&ppd, sizeof(ppd));
+ ppd.lchContact = hContact;
+ ppd.cbSize = sizeof(ppd);
+ mir_sntprintf(ppd.lptzContactName, MAX_CONTACTNAME, _T("%s"), tszName ? tszName : CTranslator::get(CTranslator::GEN_UNKNOWN_CONTACT));
+ TCHAR *msgPreview = Utils::GetPreviewWithEllipsis(reinterpret_cast<TCHAR *>(&pBuf[lstrlenA((char *)pBuf) + 1]), 100);
+ if(fSuccess) {
+ mir_sntprintf(ppd.lptzText, MAX_SECONDLINE, CTranslator::get(CTranslator::GEN_SQ_SENDLATER_SUCCESS_POPUP),
+ msgPreview);
+ mir_free(msgPreview);
+ }
+ else if(fFailed) {
+ mir_sntprintf(ppd.lptzText, MAX_SECONDLINE, CTranslator::get(CTranslator::GEN_SQ_SENDLATER_FAILED_POPUP),
+ msgPreview);
+ mir_free(msgPreview);
+ }
+ /*
+ * use message settings (timeout/colors) for success popups
+ */
+ ppd.colorText = fFailed ? RGB(255, 245, 225) : nen_options.colTextMsg;
+ ppd.colorBack = fFailed ? RGB(191, 0, 0) : nen_options.colBackMsg;
+ ppd.PluginWindowProc = reinterpret_cast<WNDPROC>(Utils::PopupDlgProcError);
+ ppd.lchIcon = fFailed ? PluginConfig.g_iconErr : PluginConfig.g_IconMsgEvent;
+ ppd.PluginData = (void *)hContact;
+ ppd.iSeconds = fFailed ? -1 : nen_options.iDelayMsg;
+ CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&ppd, 0);
+ }
+ }
+ if(fFailed && (bCode == JOB_AGE || bCode == JOB_REMOVABLE) && szId[0] == 'S')
+ cleanDB();
+ mir_free(sendBuffer);
+ mir_free(pBuf);
+ }
+}
+
+CSendLater::CSendLater()
+{
+ m_sendLaterContactList.clear();
+ m_sendLaterJobList.clear();
+ m_fAvail = M->GetByte("sendLaterAvail", 0) ? true : false;
+ m_last_sendlater_processed = time(0);
+ m_hwndDlg = 0;
+ m_fIsInteractive = false;
+ m_fErrorPopups = M->GetByte(0, SRMSGMOD_T, "qmgrErrorPopups", 0) ? true : false;
+ m_fSuccessPopups = M->GetByte(0, SRMSGMOD_T, "qmgrSuccessPopups", 0) ? true : false;
+}
+
+/**
+ * clear all open send jobs. Only called on system shutdown to remove
+ * the jobs from memory. Must _NOT_ delete any sendlater related stuff from
+ * the database (only successful sends may do this).
+ */
+CSendLater::~CSendLater()
+{
+ if(m_hwndDlg)
+ ::DestroyWindow(m_hwndDlg);
+
+ if(m_sendLaterJobList.empty())
+ return;
+
+ SendLaterJobIterator it = m_sendLaterJobList.begin();
+
+ while(it != m_sendLaterJobList.end()) {
+ mir_free((*it)->sendBuffer);
+ mir_free((*it)->pBuf);
+ (*it)->fSuccess = false; // avoid clearing jobs from the database
+ delete *it;
+ it++;
+ }
+}
+
+void CSendLater::startJobListProcess()
+{
+ m_jobIterator = m_sendLaterJobList.begin();
+
+ if (m_hwndDlg)
+ Utils::enableDlgControl(m_hwndDlg, IDC_QMGR_LIST, false);
+}
+
+/**
+ * checks if the current job in the timer-based process queue is subject
+ * for deletion (that is, it has failed or succeeded)
+ *
+ * if not, it will send the job and increment the list iterator.
+ *
+ * this method is called once per tick from the timer based scheduler in
+ * hotkeyhandler.cpp.
+ *
+ * returns true if more jobs are awaiting processing, false otherwise.
+ */
+bool CSendLater::processCurrentJob()
+{
+ if(m_sendLaterJobList.empty() || m_jobIterator == m_sendLaterJobList.end())
+ return(false);
+
+ if((*m_jobIterator)->fSuccess || (*m_jobIterator)->fFailed) {
+ if((*m_jobIterator)->mustDelete()) {
+ delete *m_jobIterator;
+ m_jobIterator = m_sendLaterJobList.erase(m_jobIterator);
+ }
+ else
+ m_jobIterator++;
+ return(m_jobIterator == m_sendLaterJobList.end() ? false : true);
+ }
+ sendIt(*m_jobIterator);
+ m_jobIterator++;
+ return(m_jobIterator == m_sendLaterJobList.end() ? false : true);
+}
+
+/**
+ * stub used as enum proc for the database enumeration, collecting
+ * all entries in the SendLater module
+ * (static function)
+ */
+int _cdecl CSendLater::addStub(const char *szSetting, LPARAM lParam)
+{
+ return(sendLater->addJob(szSetting, lParam));
+}
+
+/**
+ * Process a single contact from the list of contacts with open send later jobs
+ * enum the "SendLater" module and add all jobs to the list of open jobs.
+ * addJob() will deal with possible duplicates
+ * @param hContact HANDLE: contact's handle
+ */
+void CSendLater::processSingleContact(const HANDLE hContact)
+{
+ int iCount = M->GetDword(hContact, "SendLater", "count", 0);
+
+ if(iCount) {
+ DBCONTACTENUMSETTINGS ces = {0};
+
+ ces.pfnEnumProc = CSendLater::addStub;
+ ces.szModule = "SendLater";
+ ces.lParam = (LPARAM)hContact;
+
+ CallService(MS_DB_CONTACT_ENUMSETTINGS, (WPARAM)hContact, (LPARAM)&ces);
+ }
+}
+
+/**
+ * called periodically from a timer, check if new contacts were added
+ * and process them
+ */
+void CSendLater::processContacts()
+{
+ if(m_fAvail && !m_sendLaterContactList.empty()) {
+ std::vector<HANDLE>::iterator it = m_sendLaterContactList.begin();
+ while(it != m_sendLaterContactList.end()) {
+ processSingleContact(*it);
+ it++;
+ }
+ m_sendLaterContactList.clear();
+ }
+}
+
+/**
+ * This function adds a new job to the list of messages to send unattended
+ * used by the send later feature and multisend
+ *
+ * @param szSetting is either the name of the database key for a send later
+ * job OR the utf-8 encoded message for a multisend job prefixed with
+ * a 'M+timestamp'. Send later job ids start with "S".
+ *
+ * @param lParam: a contact handle for which the job should be scheduled
+ * @return 0 on failure, 1 otherwise
+ */
+int CSendLater::addJob(const char *szSetting, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)lParam;
+ DBVARIANT dbv = {0};
+ char *szOrig_Utf = 0;
+
+ if(!m_fAvail || !szSetting || !strcmp(szSetting, "count") || lstrlenA(szSetting) < 8)
+ return(0);
+
+ if(szSetting[0] != 'S' && szSetting[0] != 'M')
+ return(0);
+
+ SendLaterJobIterator it = m_sendLaterJobList.begin();
+
+ /*
+ * check for possible dupes
+ */
+ while(it != m_sendLaterJobList.end()) {
+ if((*it)->hContact == hContact && !strcmp((*it)->szId, szSetting)) {
+ return(0);
+ }
+ it++;
+ }
+
+ if(szSetting[0] == 'S') {
+ if(0 == DBGetContactSettingString(hContact, "SendLater", szSetting, &dbv))
+ szOrig_Utf = dbv.pszVal;
+ else
+ return(0);
+ }
+ else if(szSetting[0] == 'M') {
+ char *szSep = strchr(const_cast<char *>(szSetting), '|');
+ if(!szSep)
+ return(0);
+ *szSep = 0;
+ szOrig_Utf = szSep + 1;
+ }
+ else
+ return(0);
+
+ CSendLaterJob *job = new CSendLaterJob;
+
+ strncpy(job->szId, szSetting, 20);
+ job->szId[19] = 0;
+ job->hContact = hContact;
+ job->created = atol(&szSetting[1]);
+
+ char *szAnsi = 0;
+ wchar_t *szWchar = 0;
+ UINT required = 0;
+
+ int iLen = lstrlenA(szOrig_Utf);
+ job->sendBuffer = reinterpret_cast<char *>(mir_alloc(iLen + 1));
+ strncpy(job->sendBuffer, szOrig_Utf, iLen);
+ job->sendBuffer[iLen] = 0;
+
+ /*
+ * construct conventional send buffer
+ */
+
+ szAnsi = M->utf8_decodecp(szOrig_Utf, CP_ACP, &szWchar);
+ iLen = lstrlenA(szAnsi);
+ if(szWchar)
+ required = iLen + 1 + ((lstrlenW(szWchar) + 1) * sizeof(wchar_t));
+ else
+ required = iLen + 1;
+
+ job->pBuf = (PBYTE)mir_calloc(required);
+
+ strncpy((char *)job->pBuf, szAnsi, iLen);
+ job->pBuf[iLen] = 0;
+ if(szWchar)
+ wcsncpy((wchar_t *)&job->pBuf[iLen + 1], szWchar, lstrlenW(szWchar));
+
+ if(szSetting[0] == 'S')
+ DBFreeVariant(&dbv);
+
+ mir_free(szWchar);
+ job->readFlags();
+ m_sendLaterJobList.push_back(job);
+ qMgrUpdate();
+ return(1);
+}
+
+/**
+ * Try to send an open job from the job list
+ * this is ONLY called from the WM_TIMER handler and should never be executed
+ * directly.
+ */
+int CSendLater::sendIt(CSendLaterJob *job)
+{
+ HANDLE hContact = job->hContact;
+ time_t now = time(0);
+ DWORD dwFlags = 0;
+ DBVARIANT dbv = {0};
+ const char* szProto = 0;
+
+
+ if(job->bCode == CSendLaterJob::JOB_HOLD || job->bCode == CSendLaterJob::JOB_DEFERRED || job->fSuccess || job->fFailed || job->lastSent > now)
+ return(0); // this one is frozen or done (will be removed soon), don't process it now.
+
+ if(now - job->created > SENDLATER_AGE_THRESHOLD) { // too old, this will be discarded and user informed by popup
+ job->fFailed = true;
+ job->bCode = CSendLaterJob::JOB_AGE;
+ return(0);
+ }
+
+ /*
+ * mark job as deferred (5 unsuccessful sends). Job will not be removed, but
+ * the user must manually reset it in order to trigger a new send attempt.
+ */
+ if(job->iSendCount == 5) {
+ job->bCode = CSendLaterJob::JOB_DEFERRED;
+ return(0);
+ }
+
+ if(job->iSendCount > 0 && (now - job->lastSent < SENDLATER_RESEND_THRESHOLD)) {
+ //_DebugTraceA("Send it: message %s for %s RESEND but not old enough", job->szId, (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0));
+ return(0); // this one was sent, but probably failed. Resend it after a while
+ }
+
+ CContactCache *c = CContactCache::getContactCache(hContact);
+ if(!c)
+ return(0); // should not happen
+
+ if(!c->isValid()) {
+ job->fFailed = true;
+ job->bCode = CSendLaterJob::INVALID_CONTACT;
+ return(0); // can happen (contact has been deleted). mark the job as failed
+ }
+
+ hContact = c->getActiveContact();
+ szProto = c->getActiveProto();
+
+ if(!hContact || szProto == 0)
+ return(0);
+
+ WORD wMyStatus = (WORD)CallProtoService(szProto, PS_GETSTATUS, 0, 0);
+ WORD wContactStatus = c->getActiveStatus();
+
+ /*
+ * status mode checks
+ */
+ if(wMyStatus == ID_STATUS_OFFLINE) {
+ job->bCode = CSendLaterJob::JOB_MYSTATUS;
+ return(0);
+ }
+ if(job->szId[0] == 'S') {
+ if(!(wMyStatus == ID_STATUS_ONLINE || wMyStatus == ID_STATUS_FREECHAT)) {
+ job->bCode = CSendLaterJob::JOB_MYSTATUS;
+ return(0);
+ }
+ }
+ if(wContactStatus == ID_STATUS_OFFLINE) {
+ job->bCode = CSendLaterJob::JOB_STATUS;
+ return(0);
+ }
+
+ dwFlags = IsUtfSendAvailable(hContact) ? PREF_UTF : U_PREF_UNICODE;
+
+ char *svcName = SendQueue::MsgServiceName(hContact, 0, dwFlags);
+
+ job->lastSent = now;
+ job->iSendCount++;
+ job->hTargetContact = hContact;
+ job->bCode = CSendLaterJob::JOB_WAITACK;
+
+ if(dwFlags & PREF_UTF)
+ job->hProcess = (HANDLE)CallContactService(hContact, svcName, dwFlags, (LPARAM)job->sendBuffer);
+ else
+ job->hProcess = (HANDLE)CallContactService(hContact, svcName, dwFlags, (LPARAM)job->pBuf);
+ return(0);
+}
+
+/*
+ * add a contact to the list of contacts having open send later jobs.
+ * This ist is periodically checked for new additions (processContacts())
+ * and new jobs are created.
+ */
+
+void CSendLater::addContact(const HANDLE hContact)
+{
+ if(!m_fAvail)
+ return;
+
+ std::vector<HANDLE>::iterator it = m_sendLaterContactList.begin();
+
+ if(m_sendLaterContactList.empty()) {
+ m_sendLaterContactList.push_back(hContact);
+ m_last_sendlater_processed = 0; // force processing at next tick
+ return;
+ }
+
+ /*
+ * this list should not have duplicate entries
+ */
+
+ while(it != m_sendLaterContactList.end()) {
+ if(*it == hContact)
+ return;
+ it++;
+ }
+ m_sendLaterContactList.push_back(hContact);
+ m_last_sendlater_processed = 0; // force processing at next tick
+}
+
+/**
+ * process ACK messages for the send later job list. Called from the proto ack
+ * handler when it does not find a match in the normal send queue
+ *
+ * Add the message to the database and mark it as successful. The job will be
+ * removed later by the job list processing code.
+ */
+HANDLE CSendLater::processAck(const ACKDATA *ack)
+{
+ if(m_sendLaterJobList.empty() || !m_fAvail)
+ return(0);
+
+ SendLaterJobIterator it = m_sendLaterJobList.begin();
+
+ while(it != m_sendLaterJobList.end()) {
+ if((*it)->hProcess == ack->hProcess && (*it)->hTargetContact == ack->hContact && !((*it)->fSuccess || (*it)->fFailed)) {
+ DBEVENTINFO dbei = {0};
+
+ if(!(*it)->fSuccess) {
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = DBEF_SENT;
+ dbei.szModule = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)((*it)->hContact), 0);
+ dbei.timestamp = time(NULL);
+ dbei.cbBlob = lstrlenA((*it)->sendBuffer) + 1;
+ dbei.flags |= DBEF_UTF;
+ dbei.pBlob = (PBYTE)((*it)->sendBuffer);
+ HANDLE hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM)((*it)->hContact), (LPARAM)&dbei);
+
+ (*it)->cleanDB();
+ }
+ (*it)->fSuccess = true; // mark as successful, job list processing code will remove it later
+ (*it)->hProcess = (HANDLE)-1;
+ (*it)->bCode = '-';
+ qMgrUpdate();
+ return(0);
+ }
+ it++;
+ }
+ return(0);
+}
+
+/*
+ * UI stuff (dialog procedures for the queue manager dialog
+ */
+
+void CSendLater::qMgrUpdate(bool fReEnable)
+{
+ if(m_hwndDlg) {
+ if(fReEnable)
+ Utils::enableDlgControl(m_hwndDlg, IDC_QMGR_LIST, true);
+ ::SendMessage(m_hwndDlg, WM_USER + 100, 0, 0); // if qmgr is open, tell it to update
+ }
+}
+
+LRESULT CSendLater::qMgrAddFilter(const HANDLE hContact, const TCHAR* tszNick)
+{
+ LRESULT lr;
+
+ lr = ::SendMessage(m_hwndFilter, CB_FINDSTRING, 0, reinterpret_cast<LPARAM>(tszNick));
+ if(lr == CB_ERR) {
+ lr = ::SendMessage(m_hwndFilter, CB_INSERTSTRING, -1, reinterpret_cast<LPARAM>(tszNick));
+ ::SendMessage(m_hwndFilter, CB_SETITEMDATA, lr, reinterpret_cast<LPARAM>(hContact));
+ if(hContact == m_hFilter)
+ m_sel = lr;
+ }
+ return(m_sel);
+}
+
+/**
+ * fills the list of jobs with current contents of the job queue
+ * filters by m_hFilter (contact handle)
+ *
+ */
+void CSendLater::qMgrFillList(bool fClear)
+{
+ LVITEM lvItem = {0};
+ unsigned uIndex = 0;
+ CContactCache* c = 0;
+ TCHAR* formatTime = _T("%Y.%m.%d - %H:%M");
+ TCHAR tszTimestamp[30];
+ TCHAR tszStatus[20];
+ const TCHAR* tszStatusText = 0;
+ BYTE bCode = '-';
+
+ if(fClear) {
+ ::SendMessage(m_hwndList, LVM_DELETEALLITEMS, 0, 0);
+ ::SendMessage(m_hwndFilter, CB_RESETCONTENT, 0, 0);
+ }
+
+ m_sel = 0;
+ ::SendMessage(m_hwndFilter, CB_INSERTSTRING, -1,
+ reinterpret_cast<LPARAM>(CTranslator::get(CTranslator::QMGR_FILTER_ALLCONTACTS)));
+ ::SendMessage(m_hwndFilter, CB_SETITEMDATA, 0, 0);
+
+ lvItem.cchTextMax = 255;
+
+ SendLaterJobIterator it = m_sendLaterJobList.begin();
+
+ while(it != m_sendLaterJobList.end()) {
+
+ c = CContactCache::getContactCache((*it)->hContact);
+ if(c) {
+ const TCHAR* tszNick = c->getNick();
+ TCHAR tszBuf[255];
+
+ if(m_hFilter && m_hFilter != (*it)->hContact) {
+ qMgrAddFilter(c->getContact(), tszNick);
+ it++;
+ continue;
+ }
+ lvItem.mask = LVIF_TEXT|LVIF_PARAM;
+ mir_sntprintf(tszBuf, 255, _T("%s [%s]"), tszNick, c->getRealAccount());
+ lvItem.pszText = tszBuf;
+ lvItem.iItem = uIndex++;
+ lvItem.iSubItem = 0;
+ lvItem.lParam = reinterpret_cast<LPARAM>(*it);
+ ::SendMessage(m_hwndList, LVM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
+ qMgrAddFilter(c->getContact(), tszNick);
+
+ lvItem.mask = LVIF_TEXT;
+ _tcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&(*it)->created));
+ tszTimestamp[29] = 0;
+ lvItem.pszText = tszTimestamp;
+ lvItem.iSubItem = 1;
+ ::SendMessage(m_hwndList, LVM_SETITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
+
+ TCHAR* msg = M->utf8_decodeT((*it)->sendBuffer);
+ TCHAR* preview = Utils::GetPreviewWithEllipsis(msg, 255);
+ lvItem.pszText = preview;
+ lvItem.iSubItem = 2;
+ ::SendMessage(m_hwndList, LVM_SETITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
+ mir_free(preview);
+ mir_free(msg);
+
+ if((*it)->fFailed) {
+ tszStatusText = (*it)->bCode == CSendLaterJob::JOB_REMOVABLE ?
+ CTranslator::get(CTranslator::QMGR_STATUS_REMOVED) : CTranslator::get(CTranslator::QMGR_STATUS_FAILED);
+ }
+ else if((*it)->fSuccess)
+ tszStatusText = CTranslator::get(CTranslator::QMGR_STATUS_SENTOK);
+ else {
+ switch((*it)->bCode) {
+ case CSendLaterJob::JOB_DEFERRED:
+ tszStatusText = CTranslator::get(CTranslator::QMGR_STATUS_DEFERRED);
+ break;
+ case CSendLaterJob::JOB_AGE:
+ tszStatusText = CTranslator::get(CTranslator::QMGR_STATUS_FAILED);
+ break;
+ case CSendLaterJob::JOB_HOLD:
+ tszStatusText = CTranslator::get(CTranslator::QMGR_STATUS_HOLD);
+ break;
+ default:
+ tszStatusText = CTranslator::get(CTranslator::QMGR_STATUS_PENDING);
+ break;
+ }
+ }
+ if((*it)->bCode)
+ bCode = (*it)->bCode;
+ mir_sntprintf(tszStatus, 20, _T("X/%s[%c] (%d)"), tszStatusText, bCode, (*it)->iSendCount);
+ tszStatus[0] = static_cast<TCHAR>((*it)->szId[0]);
+ lvItem.pszText = tszStatus;
+ lvItem.iSubItem = 3;
+ ::SendMessage(m_hwndList, LVM_SETITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
+
+ if((*it)->lastSent == 0)
+ mir_sntprintf(tszTimestamp, 30, _T("%s"), _T("Never"));
+ else {
+ _tcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&(*it)->lastSent));
+ tszTimestamp[29] = 0;
+ }
+ lvItem.pszText = tszTimestamp;
+ lvItem.iSubItem = 4;
+ ::SendMessage(m_hwndList, LVM_SETITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
+ }
+ it++;
+ }
+ if(m_hFilter == 0)
+ ::SendMessage(m_hwndFilter, CB_SETCURSEL, 0, 0);
+ else
+ ::SendMessage(m_hwndFilter, CB_SETCURSEL, m_sel, 0);
+}
+
+/*
+ * set the column headers
+ */
+#define QMGR_LIST_NRCOLUMNS 5
+
+static char* szColFormat = "%d;%d;%d;%d;%d";
+static char* szColDefault = "100;120;80;120;120";
+
+void CSendLater::qMgrSetupColumns()
+{
+ LVCOLUMN col = {0};
+ int nWidths[QMGR_LIST_NRCOLUMNS];
+ DBVARIANT dbv = {0};
+ RECT rcList;
+ LONG cxList;
+
+ ::GetWindowRect(m_hwndList, &rcList);
+ cxList = rcList.right - rcList.left;
+
+ if(0 == M->GetString(0, SRMSGMOD_T, "qmgrListColumns", &dbv)) {
+ sscanf(dbv.pszVal, szColFormat, &nWidths[0], &nWidths[1], &nWidths[2], &nWidths[3], &nWidths[4]);
+ DBFreeVariant(&dbv);
+ }
+ else
+ sscanf(szColDefault, szColFormat, &nWidths[0], &nWidths[1], &nWidths[2], &nWidths[3], &nWidths[4]);
+
+ col.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM;
+ col.cx = max(nWidths[0], 10);
+ col.pszText = const_cast<TCHAR *>(CTranslator::get(CTranslator::GEN_CONTACT));
+
+ ::SendMessage(m_hwndList, LVM_INSERTCOLUMN, 0, reinterpret_cast<LPARAM>(&col));
+
+ col.pszText = const_cast<TCHAR *>(CTranslator::get(CTranslator::QMGR_COL_ODATE));
+ col.cx = max(nWidths[1], 10);
+ ::SendMessage(m_hwndList, LVM_INSERTCOLUMN, 1, reinterpret_cast<LPARAM>(&col));
+
+ col.pszText = const_cast<TCHAR *>(CTranslator::get(CTranslator::QMGR_COL_MESSAGETEXT));
+ col.cx = max((cxList - nWidths[0] - nWidths[1] - nWidths[3] - nWidths[4] - 10), 10);
+ ::SendMessage(m_hwndList, LVM_INSERTCOLUMN, 2, reinterpret_cast<LPARAM>(&col));
+
+ col.pszText = const_cast<TCHAR *>(CTranslator::get(CTranslator::QMGR_COL_STATUS));
+ col.cx = max(nWidths[3], 10);
+ ::SendMessage(m_hwndList, LVM_INSERTCOLUMN, 3, reinterpret_cast<LPARAM>(&col));
+
+ col.pszText = const_cast<TCHAR *>(CTranslator::get(CTranslator::QMGR_COL_LASTSENDINFO));
+ col.cx = max(nWidths[4], 10);
+ ::SendMessage(m_hwndList, LVM_INSERTCOLUMN, 4, reinterpret_cast<LPARAM>(&col));
+
+}
+
+/**
+ * save user defined column widths to the database
+ */
+void CSendLater::qMgrSaveColumns()
+{
+ char szColFormatNew[100];
+ int nWidths[QMGR_LIST_NRCOLUMNS], i;
+ LVCOLUMN col = {0};
+
+ col.mask = LVCF_WIDTH;
+ for(i = 0; i < QMGR_LIST_NRCOLUMNS; i++) {
+ ::SendMessage(m_hwndList, LVM_GETCOLUMN, i, reinterpret_cast<LPARAM>(&col));
+ nWidths[i] = max(col.cx, 10);
+ }
+ mir_snprintf(szColFormatNew, 100, "%d;%d;%d;%d;%d", nWidths[0], nWidths[1], nWidths[2], nWidths[3], nWidths[4]);
+ ::DBWriteContactSettingString(0, SRMSGMOD_T, "qmgrListColumns", szColFormatNew);
+}
+
+INT_PTR CALLBACK CSendLater::DlgProcStub(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CSendLater *s = reinterpret_cast<CSendLater *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+
+ if(s)
+ return(s->DlgProc(hwnd, msg, wParam, lParam));
+
+ switch(msg) {
+ case WM_INITDIALOG: {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ s = reinterpret_cast<CSendLater *>(lParam);
+ return(s->DlgProc(hwnd, msg, wParam, lParam));
+ }
+ default:
+ break;
+ }
+ return(FALSE);
+}
+
+INT_PTR CALLBACK CSendLater::DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG:
+ m_hwndDlg = hwnd;
+ TranslateDialogDefault(hwnd);
+ m_hwndList = ::GetDlgItem(m_hwndDlg, IDC_QMGR_LIST);
+ m_hwndFilter = ::GetDlgItem(m_hwndDlg, IDC_QMGR_FILTER);
+ m_hFilter = reinterpret_cast<HANDLE>(M->GetDword(0, SRMSGMOD_T, "qmgrFilterContact", 0));
+
+ ::SetWindowLongPtr(m_hwndList, GWL_STYLE, ::GetWindowLongPtr(m_hwndList, GWL_STYLE) | LVS_SHOWSELALWAYS);
+ ::SendMessage(m_hwndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_LABELTIP|LVS_EX_DOUBLEBUFFER);
+ qMgrSetupColumns();
+ qMgrFillList();
+ if(PluginConfig.g_PopupAvail) {
+ ::CheckDlgButton(m_hwndDlg, IDC_QMGR_SUCCESSPOPUPS, m_fSuccessPopups ? BST_CHECKED : BST_UNCHECKED);
+ ::CheckDlgButton(m_hwndDlg, IDC_QMGR_ERRORPOPUPS, m_fErrorPopups ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else {
+ Utils::showDlgControl(m_hwndDlg, IDC_QMGR_ERRORPOPUPS, SW_HIDE);
+ Utils::showDlgControl(m_hwndDlg, IDC_QMGR_SUCCESSPOPUPS, SW_HIDE);
+ }
+ ::ShowWindow(hwnd, SW_NORMAL);
+ return(FALSE);
+
+ case WM_NOTIFY: {
+ NMHDR* pNMHDR = reinterpret_cast<NMHDR *>(lParam);
+
+ if(pNMHDR->hwndFrom == m_hwndList) {
+ switch(pNMHDR->code) {
+ case NM_RCLICK: {
+ HMENU hMenu = ::LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_TABCONTEXT));
+ HMENU hSubMenu = ::GetSubMenu(hMenu, 13);
+ ::TranslateMenu(hSubMenu);
+
+ POINT pt;
+ ::GetCursorPos(&pt);
+ /*
+ * copy to clipboard only allowed with a single selection
+ */
+ if(::SendMessage(m_hwndList, LVM_GETSELECTEDCOUNT, 0, 0) == 1)
+ ::EnableMenuItem(hSubMenu, ID_QUEUEMANAGER_COPYMESSAGETOCLIPBOARD, MF_ENABLED);
+
+ m_fIsInteractive = true;
+ int selection = ::TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwndDlg, NULL);
+ if(selection == ID_QUEUEMANAGER_CANCELALLMULTISENDJOBS) {
+ SendLaterJobIterator it = m_sendLaterJobList.begin();
+ while(it != m_sendLaterJobList.end()) {
+ if((*it)->szId[0] == 'M') {
+ (*it)->fFailed = true;
+ (*it)->bCode = CSendLaterJob::JOB_REMOVABLE;
+ }
+ it++;
+ }
+ }
+ else if(selection != 0) {
+ ::SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_QMGR_REMOVE, LOWORD(selection)), 0);
+ m_last_sendlater_processed = 0; // force a queue check
+ }
+ ::DestroyMenu(hMenu);
+ m_fIsInteractive = false;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ case WM_COMMAND:
+ if(HIWORD(wParam) == CBN_SELCHANGE && reinterpret_cast<HWND>(lParam) == m_hwndFilter) {
+ LRESULT lr = ::SendMessage(m_hwndFilter, CB_GETCURSEL, 0, 0);
+ if(lr != CB_ERR) {
+ m_hFilter = reinterpret_cast<HANDLE>(::SendMessage(m_hwndFilter, CB_GETITEMDATA, lr, 0));
+ qMgrFillList();
+ }
+ break;
+ }
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ qMgrSaveColumns();
+ ::DestroyWindow(hwnd);
+ break;
+
+ case IDC_QMGR_SUCCESSPOPUPS:
+ m_fSuccessPopups = ::IsDlgButtonChecked(m_hwndDlg, IDC_QMGR_SUCCESSPOPUPS) ? true : false;
+ M->WriteByte(0, SRMSGMOD_T, "qmgrSuccessPopups", m_fSuccessPopups ? 1 : 0);
+ break;
+
+ case IDC_QMGR_ERRORPOPUPS:
+ m_fErrorPopups = ::IsDlgButtonChecked(m_hwndDlg, IDC_QMGR_ERRORPOPUPS) ? true : false;
+ M->WriteByte(0, SRMSGMOD_T, "qmgrErrorPopups", m_fErrorPopups ? 1 : 0);
+ break;
+
+ case IDC_QMGR_HELP:
+ CallService(MS_UTILS_OPENURL, 0, reinterpret_cast<LPARAM>("http://wiki.miranda.or.at/TabSRMM/SendLater"));
+ break;
+ /*
+ * this handles all commands sent by the context menu
+ * mark jobs for removal/reset/hold/unhold
+ * exception: kill all open multisend jobs is directly handled from the context menu
+ */
+ case IDC_QMGR_REMOVE: {
+ if(::SendMessage(m_hwndList, LVM_GETSELECTEDCOUNT, 0, 0) != 0) {
+ LVITEM item = {0};
+ LRESULT items = ::SendMessage(m_hwndList, LVM_GETITEMCOUNT, 0, 0);
+ item.mask = LVIF_STATE|LVIF_PARAM;
+ item.stateMask = LVIS_SELECTED;
+
+ if(HIWORD(wParam) != ID_QUEUEMANAGER_COPYMESSAGETOCLIPBOARD) {
+ if(MessageBox(0, CTranslator::get(CTranslator::QMGR_WARNING_REMOVAL), CTranslator::get(CTranslator::QMGR_TITLE),
+ MB_ICONQUESTION | MB_OKCANCEL) == IDCANCEL)
+ break;
+ }
+ for(LRESULT i = 0; i < items; i++) {
+ item.iItem = i;
+ ::SendMessage(m_hwndList, LVM_GETITEM, 0, reinterpret_cast<LPARAM>(&item));
+ if(item.state & LVIS_SELECTED) {
+ CSendLaterJob* job = reinterpret_cast<CSendLaterJob *>(item.lParam);
+ if(job) {
+ switch(HIWORD(wParam)) {
+ case ID_QUEUEMANAGER_MARKSELECTEDFORREMOVAL:
+ job->bCode = CSendLaterJob::JOB_REMOVABLE;
+ job->fFailed = true;
+ break;
+ case ID_QUEUEMANAGER_HOLDSELECTED:
+ job->bCode = CSendLaterJob::JOB_HOLD;
+ job->writeFlags();
+ break;
+ case ID_QUEUEMANAGER_RESUMESELECTED:
+ job->bCode = 0;
+ job->writeFlags();
+ break;
+ case ID_QUEUEMANAGER_COPYMESSAGETOCLIPBOARD: {
+ TCHAR *msg = M->utf8_decodeT(job->sendBuffer);
+ Utils::CopyToClipBoard(msg, m_hwndDlg);
+ mir_free(msg);
+ break;
+ }
+ /*
+ * reset deferred and aged jobs
+ * so they can be resent at next processing
+ */
+ case ID_QUEUEMANAGER_RESETSELECTED: {
+ if(job->bCode == CSendLaterJob::JOB_DEFERRED) {
+ job->iSendCount = 0;
+ job->bCode = '-';
+ }
+ else if(job->bCode == CSendLaterJob::JOB_AGE) {
+ job->fFailed = false;
+ job->bCode = '-';
+ job->created = time(0);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ }
+ qMgrFillList();
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_USER + 100:
+ qMgrFillList();
+ break;
+ case WM_NCDESTROY: {
+ m_hwndDlg = 0;
+ M->WriteDword(0, SRMSGMOD_T, "qmgrFilterContact", reinterpret_cast<DWORD>(m_hFilter));
+ break;
+ }
+ }
+ return(FALSE);
+}
+
+/**
+ * invoke queue manager dialog - do nothing if this dialog is already open
+ */
+void CSendLater::invokeQueueMgrDlg()
+{
+ if(m_hwndDlg == 0)
+ m_hwndDlg = ::CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_SENDLATER_QMGR), 0, CSendLater::DlgProcStub,
+ reinterpret_cast<LPARAM>(this));
+}
+
+/*
+ * service function to invoke the queue manager
+ */
+
+INT_PTR CSendLater::svcQMgr(WPARAM wParam, LPARAM lParam)
+{
+ sendLater->invokeQueueMgrDlg();
+ return(0);
+}
+
+
+CSendLater* sendLater = 0;
diff --git a/plugins/TabSRMM/src/sendqueue.cpp b/plugins/TabSRMM/src/sendqueue.cpp new file mode 100644 index 0000000000..b4e90359e9 --- /dev/null +++ b/plugins/TabSRMM/src/sendqueue.cpp @@ -0,0 +1,989 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: sendqueue.cpp 13750 2011-08-03 20:10:43Z george.hazan $
+ *
+ * Implements a queued, asynchronous sending system for tabSRMM.
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+SendQueue *sendQueue = 0;
+
+extern const TCHAR *pszIDCSAVE_save, *pszIDCSAVE_close;
+
+static char *pss_msg = "/SendMsg";
+static char *pss_msgw = "/SendMsgW";
+
+/**
+ * get the service name to send a message
+ *
+ * @param hContact (HANDLE) contact's handle
+ * @param dat _MessageWindowData
+ * @param dwFlags
+ *
+ * @return (char *) name of the service to send a message to this contact
+ */
+char *SendQueue::MsgServiceName(const HANDLE hContact = 0, const TWindowData *dat = 0, int dwFlags = 0)
+{
+ char szServiceName[100];
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (szProto == NULL)
+ return pss_msg;
+
+ if(dat) {
+ if (dat->sendMode & SMODE_FORCEANSI || !(dwFlags & PREF_UNICODE))
+ return pss_msg;
+ }
+
+ _snprintf(szServiceName, sizeof(szServiceName), "%s%sW", szProto, PSS_MESSAGE);
+ if (ServiceExists(szServiceName))
+ return pss_msgw;
+
+ return pss_msg;
+}
+
+/*
+ * 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) {
+ int i;
+
+ for (i = 0; i < NR_SENDJOBS; i++) {
+ if (m_jobs[i].hOwner == dat->hContact && m_jobs[i].iStatus == SQ_ERROR)
+ return i;
+ }
+ return -1;
+ }
+ return -1;
+}
+void SendQueue::handleError(TWindowData *dat, const int iEntry) const
+{
+ if(dat) {
+ 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 iLength = 0, i;
+ int iFound = NR_SENDJOBS;
+
+ if (m_currentIndex >= NR_SENDJOBS) {
+ _DebugPopup(dat->hContact, _T("Send queue full"));
+ return 0;
+ }
+ /*
+ * find a free entry in the send queue...
+ */
+ for (i = 0; i < NR_SENDJOBS; i++) {
+ if (m_jobs[i].hOwner != 0 || m_jobs[i].iStatus != 0) {
+ // this entry is used, check if it's orphaned and can be removed...
+ if (m_jobs[i].hwndOwner && IsWindow(m_jobs[i].hwndOwner)) // 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;
+ }
+ iLength = iLen;
+ if (iLength > 0) {
+ if (m_jobs[iFound].sendBuffer == NULL) {
+ if (iLength < HISTORY_INITIAL_ALLOCSIZE)
+ iLength = HISTORY_INITIAL_ALLOCSIZE;
+ m_jobs[iFound].sendBuffer = (char *)malloc(iLength);
+ m_jobs[iFound].dwLen = iLength;
+ }
+ else {
+ if (iLength > m_jobs[iFound].dwLen) {
+ m_jobs[iFound].sendBuffer = (char *)realloc(m_jobs[iFound].sendBuffer, iLength);
+ m_jobs[iFound].dwLen = iLength;
+ }
+ }
+ CopyMemory(m_jobs[iFound].sendBuffer, dat->sendBuffer, iLen);
+ }
+ m_jobs[iFound].dwFlags = dwFlags;
+ m_jobs[iFound].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, HANDLE hContact, char *szSvc, DWORD dwFlags)
+{
+ BYTE *pBuf = NULL;
+ int wLen = lstrlenW(chunk), id;
+ DWORD memRequired = (wLen + 1) * sizeof(WCHAR);
+ DWORD codePage = DBGetContactSettingDword(hContact, SRMSGMOD_T, "ANSIcodepage", CP_ACP);
+ int mbcsSize = WideCharToMultiByte(codePage, 0, chunk, -1, (char *)pBuf, 0, 0, 0);
+
+ memRequired += mbcsSize;
+ pBuf = (BYTE *)mir_alloc(memRequired);
+ WideCharToMultiByte(codePage, 0, chunk, -1, (char *)pBuf, mbcsSize, 0, 0);
+ CopyMemory(&pBuf[mbcsSize], chunk, (wLen + 1) * sizeof(WCHAR));
+ id = CallContactService(hContact, szSvc, dwFlags, (LPARAM)pBuf);
+ mir_free(pBuf);
+ return id;
+}
+
+static int SendChunkA(char *chunk, HANDLE hContact, char *szSvc, DWORD dwFlags)
+{
+ return(CallContactService(hContact, szSvc, dwFlags, (LPARAM)chunk));
+}
+
+static void DoSplitSendW(LPVOID param)
+{
+ struct SendJob *job = sendQueue->getJobByIndex((int)param);
+ int id;
+ BOOL fFirstSend = FALSE;
+ WCHAR *wszBegin, *wszTemp, *wszSaved, savedChar;
+ int iLen, iCur = 0, iSavedCur = 0, i;
+ BOOL fSplitting = TRUE;
+ char szServiceName[100], *svcName;
+ HANDLE hContact = job->hOwner;
+ DWORD dwFlags = job->dwFlags;
+ int chunkSize = job->chunkSize / 2;
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+
+ if (szProto == NULL)
+ svcName = pss_msg;
+ else {
+ _snprintf(szServiceName, sizeof(szServiceName), "%s%sW", szProto, PSS_MESSAGE);
+ if (ServiceExists(szServiceName))
+ svcName = pss_msgw;
+ else
+ svcName = pss_msg;
+ }
+
+ iLen = lstrlenA(job->sendBuffer);
+ wszBegin = (WCHAR *) & job->sendBuffer[iLen + 1];
+ 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;
+ id = SendChunkW(wszTemp, hContact, svcName, 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 {
+ id = SendChunkW(wszTemp, hContact, svcName, 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)
+{
+ struct 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;
+ char *svcName;
+ HANDLE hContact = job->hOwner;
+ DWORD dwFlags = job->dwFlags;
+ int chunkSize = job->chunkSize;
+
+ svcName = pss_msg;
+
+ iLen = lstrlenA(job->sendBuffer);
+ szTemp = (char *)mir_alloc(iLen + 1);
+ CopyMemory(szTemp, job->sendBuffer, 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;
+ WCHAR *wszBuf;
+ char *utf8;
+ iLen = lstrlenA(m_jobs[iEntry].sendBuffer);
+ wszBuf = (WCHAR *) & m_jobs[iEntry].sendBuffer[iLen + 1];
+ utf8 = M->utf8_encodeW(wszBuf);
+ m_jobs[iEntry].iSendLength = lstrlenA(utf8);
+ mir_free(utf8);
+ return(m_jobs[iEntry].iSendLength);
+ }
+ else {
+ m_jobs[iEntry].iSendLength = lstrlenA(m_jobs[iEntry].sendBuffer);
+ return(m_jobs[iEntry].iSendLength);
+ }
+}
+
+int SendQueue::sendQueued(TWindowData *dat, const int iEntry)
+{
+ HWND hwndDlg = dat->hwnd;
+
+ if (dat->sendMode & SMODE_MULTIPLE) {
+ HANDLE hContact, hItem;
+ int iJobs = 0;
+ int iMinLength = 0;
+ CContactCache* c = 0;
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+
+ m_jobs[iEntry].hOwner = dat->hContact;
+ m_jobs[iEntry].iStatus = SQ_INPROGRESS;
+ m_jobs[iEntry].hwndOwner = hwndDlg;
+
+ int iSendLength = getSendLength(iEntry, dat->sendMode);
+
+ do {
+ hItem = (HANDLE) SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM) hContact, 0);
+ if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM) hItem, 0)) {
+ c = CContactCache::getContactCache(hContact);
+ if(c)
+ iMinLength = (iMinLength == 0 ? c->getMaxMessageLength() : min(c->getMaxMessageLength(), iMinLength));
+ }
+ } while (hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0));
+
+ if(iSendLength >= iMinLength) {
+ TCHAR tszError[256];
+
+ mir_sntprintf(tszError, 256, CTranslator::get(CTranslator::GEN_SQ_SENDLATER_ERROR_MSG_TOO_LONG), iMinLength);
+ ::SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, reinterpret_cast<LPARAM>(tszError));
+ sendQueue->clearJob(iEntry);
+ return(0);
+ }
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ do {
+ hItem = (HANDLE) SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM) hContact, 0);
+ if (hItem && SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_GETCHECKMARK, (WPARAM) hItem, 0)) {
+ doSendLater(iEntry, 0, hContact, false);
+ iJobs++;
+ }
+ } while (hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0));
+
+ sendQueue->clearJob(iEntry);
+ if(iJobs)
+ sendLater->flushQueue(); // force queue processing
+ return(0);
+ }
+ else {
+ if (dat->hContact == NULL)
+ return 0; //never happens
+
+ dat->nMax = dat->cache->getMaxMessageLength(); // refresh length info
+
+ if (dat->sendMode & SMODE_FORCEANSI && M->GetByte(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 1))
+ M->WriteByte(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 0);
+ else if (!(dat->sendMode & SMODE_FORCEANSI) && !M->GetByte(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 0))
+ M->WriteByte(dat->cache->getActiveContact(), dat->cache->getActiveProto(), "UnicodeSend", 1);
+
+ if (M->GetByte("autosplit", 0) && !(dat->sendMode & SMODE_SENDLATER)) {
+ BOOL fSplit = FALSE;
+ DWORD dwOldFlags;
+
+ /*
+ * determine send buffer length
+ */
+ if(getSendLength(iEntry, dat->sendMode) >= dat->nMax)
+ fSplit = true;
+
+ if (!fSplit)
+ goto send_unsplitted;
+
+ m_jobs[iEntry].hOwner = dat->hContact;
+ m_jobs[iEntry].hwndOwner = hwndDlg;
+ m_jobs[iEntry].iStatus = SQ_INPROGRESS;
+ m_jobs[iEntry].iAcksNeeded = 1;
+ m_jobs[iEntry].chunkSize = dat->nMax;
+
+ 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].hOwner = dat->hContact;
+ m_jobs[iEntry].hwndOwner = 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, CTranslator::get(CTranslator::GEN_SQ_SENDLATER_ERROR_MSG_TOO_LONG), dat->nMax);
+ SendMessage(dat->hwnd, DM_ACTIVATETOOLTIP, IDC_MESSAGE, reinterpret_cast<LPARAM>(tszError));
+ clearJob(iEntry);
+ return(0);
+ }
+ doSendLater(iEntry, dat);
+ clearJob(iEntry);
+ return(0);
+ }
+ m_jobs[iEntry].hSendId = (HANDLE) CallContactService(dat->hContact, MsgServiceName(dat->hContact, dat, m_jobs[iEntry].dwFlags), (dat->sendMode & SMODE_FORCEANSI) ? (m_jobs[iEntry].dwFlags & ~PREF_UNICODE) : m_jobs[iEntry].dwFlags, (LPARAM) m_jobs[iEntry].sendBuffer);
+
+ 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)
+{
+ m_jobs[iIndex].hOwner = 0;
+ m_jobs[iIndex].hwndOwner = 0;
+ m_jobs[iIndex].iStatus = 0;
+ m_jobs[iIndex].iAcksNeeded = 0;
+ m_jobs[iIndex].dwFlags = 0;
+ m_jobs[iIndex].chunkSize = 0;
+ m_jobs[iIndex].dwTime = 0;
+ m_jobs[iIndex].hSendId = 0;
+ m_jobs[iIndex].iSendLength = 0;
+}
+
+/*
+ * 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<TWindowData *>(dat), (HICON) - 1);
+ }
+ else if (!(dat->sendMode & SMODE_NOACK))
+ ::HandleIconFeedback(const_cast<TWindowData *>(dat), PluginConfig.g_IconSend);
+
+ if (dat->pContainer->hwndActive == hwndDlg)
+ ::UpdateReadChars(const_cast<TWindowData *>(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
+{
+ DBEVENTINFO dbei = {0};
+ int iMsgLen;
+
+ if(dat == 0)
+ return;
+
+ dbei.eventType = EVENTTYPE_ERRMSG;
+ dbei.cbSize = sizeof(dbei);
+ if (iSendJobIndex >= 0) {
+ dbei.pBlob = (BYTE *)m_jobs[iSendJobIndex].sendBuffer;
+ iMsgLen = lstrlenA(m_jobs[iSendJobIndex].sendBuffer) + 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].hOwner != 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].sendBuffer[lstrlenA(m_jobs[iEntry].sendBuffer) + 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].sendBuffer);
+ }
+ UpdateSaveAndSendButton(const_cast<TWindowData *>(dat));
+ SendDlgItemMessage(dat->hwnd, IDC_MESSAGE, EM_SETSEL, (WPARAM) - 1, (LPARAM) - 1);
+ }
+ }
+}
+
+void SendQueue::UpdateSaveAndSendButton(TWindowData *dat)
+{
+ if(dat) {
+ int len;
+ HWND hwndDlg = dat->hwnd;
+
+ GETTEXTLENGTHEX gtxl = {0};
+ gtxl.codepage = CP_UTF8;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+ len = SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM) & gtxl, 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, 0);
+ 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, 0);
+ dat->dwFlags &= ~MWF_SAVEBTN_SAV;
+ }
+ dat->textLen = len;
+ }
+}
+
+void SendQueue::NotifyDeliveryFailure(const TWindowData *dat)
+{
+ POPUPDATAT_V2 ppd = {0};
+ int ibsize = 1023;
+
+ if(M->GetByte("adv_noErrorPopups", 0))
+ return;
+
+ if (CallService(MS_POPUP_QUERY, PUQS_GETSTATUS, 0) == 1) {
+ ZeroMemory((void *)&ppd, sizeof(ppd));
+ ppd.lchContact = dat->hContact;
+ mir_sntprintf(ppd.lptzContactName, MAX_CONTACTNAME, _T("%s"), dat->cache->getNick());
+ mir_sntprintf(ppd.lptzText, MAX_SECONDLINE, _T("%s"), CTranslator::get(CTranslator::GEN_SQ_DELIVERYFAILED));
+ 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<WNDPROC>(Utils::PopupDlgProcError);
+ ppd.lchIcon = PluginConfig.g_iconErr;
+ ppd.PluginData = (void *)dat->hContact;
+ ppd.iSeconds = (int)M->GetDword(MODULE, OPT_DELAY_ERR, (DWORD)DEFAULT_DELAY);
+ CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, 0);
+ }
+}
+
+/*
+ * searches string for characters typical for RTL text (hebrew and other RTL languages
+*/
+
+int SendQueue::RTL_Detect(const WCHAR *pszwText)
+{
+ WORD *infoTypeC2;
+ int i, n = 0;
+ int iLen = lstrlenW(pszwText);
+
+ 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;
+ DBEVENTINFO dbei = { 0};
+ HANDLE hNewEvent;
+ int iFound = SendQueue::NR_SENDJOBS, iNextFailed;
+ TContainerData *m_pContainer = 0;
+ if (dat)
+ m_pContainer = dat->pContainer;
+
+ iFound = (int)(LOWORD(wParam));
+ //i = (int)(HIWORD(wParam));
+
+ if (m_jobs[iFound].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(m_jobs[iFound].szErrorMsg, safe_sizeof(m_jobs[iFound].szErrorMsg),
+ CTranslator::get(CTranslator::GEN_MSG_DELIVERYFAILURE), szAckMsg);
+ m_jobs[iFound].iStatus = SQ_ERROR;
+ mir_free(szAckMsg);
+ KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound);
+ if (!(dat->dwFlags & MWF_ERRORSTATE))
+ handleError(dat, iFound);
+ return 0;
+ }
+ else {
+inform_and_discard:
+ _DebugPopup(m_jobs[iFound].hOwner, CTranslator::get(CTranslator::GEN_SQ_DELIVERYFAILEDLATE));
+ clearJob(iFound);
+ return 0;
+ }
+ }
+
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = DBEF_SENT;
+ dbei.szModule = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) m_jobs[iFound].hOwner, 0);
+ dbei.timestamp = time(NULL);
+ dbei.cbBlob = lstrlenA(m_jobs[iFound].sendBuffer) + 1;
+
+ if (dat)
+ dat->cache->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1);
+ else {
+ CContactCache *c = CContactCache::getContactCache(m_jobs[iFound].hOwner);
+ if(c)
+ c->updateStats(TSessionStats::BYTES_SENT, dbei.cbBlob - 1);
+ }
+
+ if (m_jobs[iFound].dwFlags & PREF_UNICODE)
+ dbei.cbBlob *= sizeof(TCHAR) + 1;
+ if (m_jobs[iFound].dwFlags & PREF_RTL)
+ dbei.flags |= DBEF_RTL;
+ if (m_jobs[iFound].dwFlags & PREF_UTF)
+ dbei.flags |= DBEF_UTF;
+ dbei.pBlob = (PBYTE) m_jobs[iFound].sendBuffer;
+ hNewEvent = (HANDLE) CallService(MS_DB_EVENT_ADD, (WPARAM) m_jobs[iFound].hOwner, (LPARAM) & dbei);
+
+ if (m_pContainer) {
+ if (!nen_options.iNoSounds && !(m_pContainer->dwFlags & CNT_NOSOUND))
+ SkinPlaySound("SendMsg");
+ }
+
+ if (dat && (m_jobs[iFound].hOwner == dat->hContact)) {
+ if (dat->hDbEventFirst == NULL) {
+ dat->hDbEventFirst = hNewEvent;
+ SendMessage(dat->hwnd, DM_REMAKELOG, 0, 0);
+ }
+ }
+
+ m_jobs[iFound].hSendId = NULL;
+ m_jobs[iFound].iAcksNeeded--;
+
+ if (m_jobs[iFound].iAcksNeeded == 0) { // everything sent
+ //if (m_jobs[iFound].hOwner != 0 && dat)
+ // EnableSending(dat, TRUE);
+ clearJob(iFound);
+ if (dat) {
+ KillTimer(dat->hwnd, TIMERID_MSGSEND + iFound);
+ dat->iOpenJobs--;
+ }
+ m_currentIndex--;
+ }
+ if (dat) {
+ checkQueue(dat);
+ if ((iNextFailed = findNextFailed(dat)) >= 0 && !(dat->dwFlags & MWF_ERRORSTATE))
+ handleError(dat, iNextFailed);
+ //MAD: close on send mode
+ 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);
+ }
+ }
+ //MAD_
+ }
+ return 0;
+}
+
+LRESULT SendQueue::WarnPendingJobs(unsigned int uNrMessages)
+{
+ return(MessageBox(0, CTranslator::get(CTranslator::GEN_SQ_WARNING),
+ CTranslator::get(CTranslator::GEN_SQ_WARNING_TITLE), 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, HANDLE hContact, bool fIsSendLater)
+{
+ bool fAvail = sendLater->isAvail();
+
+ const TCHAR *szNote = 0;
+
+ if(fIsSendLater && dat) {
+ if(fAvail)
+ szNote = CTranslator::get(CTranslator::GEN_SQ_QUEUED_MESSAGE);
+ else
+ szNote = CTranslator::get(CTranslator::GEN_SQ_QUEUING_NOT_AVAIL);
+
+ char *utfText = M->utf8_encodeT(szNote);
+ DBEVENTINFO dbei;
+ dbei.cbSize = sizeof(dbei);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.flags = DBEF_SENT | DBEF_UTF;
+ dbei.szModule = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) dat->hContact, 0);
+ 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, 0);
+ 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 tszTimestamp[30], tszHeader[150];
+ time_t now = time(0);
+
+ if(fIsSendLater) {
+ TCHAR *formatTime = _T("%Y.%m.%d - %H:%M");
+ _tcsftime(tszTimestamp, 30, formatTime, _localtime32((__time32_t *)&now));
+ tszTimestamp[29] = 0;
+ mir_snprintf(szKeyName, 20, "S%d", now);
+ mir_sntprintf(tszHeader, safe_sizeof(tszHeader), CTranslator::get(CTranslator::GEN_SQ_SENDLATER_HEADER), tszTimestamp);
+ }
+ else
+ mir_sntprintf(tszHeader, safe_sizeof(tszHeader), _T("M%d|"), time(0));
+
+ if(job->dwFlags & PREF_UTF || !(job->dwFlags & PREF_UNICODE)) {
+ char *utf_header = M->utf8_encodeT(tszHeader);
+ UINT required = lstrlenA(utf_header) + lstrlenA(job->sendBuffer) + 10;
+ char *tszMsg = reinterpret_cast<char *>(mir_alloc(required));
+
+ if(fIsSendLater) {
+ mir_snprintf(tszMsg, required, "%s%s", job->sendBuffer, utf_header);
+ DBWriteContactSettingString(hContact ? hContact : job->hOwner, "SendLater", szKeyName, tszMsg);
+ }
+ else {
+ mir_snprintf(tszMsg, required, "%s%s", utf_header, job->sendBuffer);
+ sendLater->addJob(tszMsg, (LPARAM)hContact);
+ }
+ mir_free(utf_header);
+ mir_free(tszMsg);
+ }
+ else if(job->dwFlags & PREF_UNICODE) {
+ int iLen = lstrlenA(job->sendBuffer);
+ wchar_t *wszMsg = (wchar_t *)&job->sendBuffer[iLen + 1];
+
+ UINT required = sizeof(TCHAR) * (lstrlen(tszHeader) + lstrlenW(wszMsg) + 10);
+
+ TCHAR *tszMsg = reinterpret_cast<TCHAR *>(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 = M->utf8_encodeT(tszMsg);
+ if(fIsSendLater)
+ DBWriteContactSettingString(hContact ? hContact : job->hOwner, "SendLater", szKeyName, utf);
+ else
+ sendLater->addJob(utf, (LPARAM)hContact);
+ mir_free(utf);
+ mir_free(tszMsg);
+ }
+ if(fIsSendLater) {
+ int iCount = M->GetDword(hContact ? hContact : job->hOwner, "SendLater", "count", 0);
+ iCount++;
+ M->WriteDword(hContact ? hContact : job->hOwner, "SendLater", "count", iCount);
+ sendLater->addContact(hContact ? hContact : job->hOwner);
+ }
+ return(iJobIndex);
+ }
+ return(-1);
+}
diff --git a/plugins/TabSRMM/src/sidebar.cpp b/plugins/TabSRMM/src/sidebar.cpp new file mode 100644 index 0000000000..85dbf3e6c3 --- /dev/null +++ b/plugins/TabSRMM/src/sidebar.cpp @@ -0,0 +1,1232 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: sidebar.cpp 12647 2010-09-09 22:17:49Z silvercircle $
+ *
+ * the contact switch bar on the left (or right) side
+ *
+ */
+
+#include "commonheaders.h"
+
+extern int TSAPI TBStateConvert2Flat(int state);
+extern int TSAPI RBStateConvert2Flat(int state);
+extern void TSAPI FillTabBackground(const HDC hdc, int iStateId, const TWindowData* dat, RECT* rc);
+
+TSideBarLayout CSideBar::m_layouts[CSideBar::NR_LAYOUTS] = {
+ {
+ _T("Like tabs, vertical text orientation"),
+ 26, 30,
+ SIDEBARLAYOUT_DYNHEIGHT | SIDEBARLAYOUT_VERTICALORIENTATION,
+ CSideBar::m_DefaultContentRenderer,
+ CSideBar::m_DefaultBackgroundRenderer,
+ NULL,
+ NULL,
+ SIDEBARLAYOUT_VERTICAL
+ },
+ {
+ _T("Compact layout, horizontal buttons"),
+ 100, 24,
+ 0,
+ CSideBar::m_DefaultContentRenderer,
+ CSideBar::m_DefaultBackgroundRenderer,
+ NULL,
+ NULL,
+ SIDEBARLAYOUT_NORMAL
+ },
+ {
+ _T("Advanced layout with avatars"),
+ 140, 40,
+ SIDEBARLAYOUT_NOCLOSEBUTTONS,
+ CSideBar::m_AdvancedContentRenderer,
+ CSideBar::m_DefaultBackgroundRenderer,
+ NULL,
+ NULL,
+ SIDEBARLAYOUT_NORMAL
+ },
+ {
+ _T("Advanced with avatars, vertical orientation"),
+ 40, 40,
+ SIDEBARLAYOUT_DYNHEIGHT | SIDEBARLAYOUT_VERTICALORIENTATION | SIDEBARLAYOUT_NOCLOSEBUTTONS,
+ CSideBar::m_AdvancedContentRenderer,
+ CSideBar::m_DefaultBackgroundRenderer,
+ CSideBar::m_measureAdvancedVertical,
+ NULL,
+ SIDEBARLAYOUT_VERTICAL
+ }
+};
+
+CSideBarButton::CSideBarButton(const TWindowData *dat, CSideBar *sideBar)
+{
+ m_dat = dat;
+ m_id = reinterpret_cast<UINT>(dat->hContact); // set the control id
+ m_sideBar = sideBar;
+ _create();
+}
+
+CSideBarButton::CSideBarButton(const UINT id, CSideBar *sideBar)
+{
+ m_dat = 0;
+ m_id = id;
+ m_sideBar = sideBar;
+ _create();
+}
+
+/**
+ * Internal method to create the button item and configure the associated button control
+ */
+void CSideBarButton::_create()
+{
+ m_hwnd = 0;
+ m_isTopAligned = true;
+ m_sz.cx = m_sz.cy = 0;
+
+ m_hwnd = ::CreateWindowEx(0, _T("TSButtonClass"), _T(""), WS_CHILD | WS_TABSTOP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ 0, 0, 40, 40, m_sideBar->getScrollWnd(), reinterpret_cast<HMENU>(m_id), g_hInst, NULL);
+
+ if(m_hwnd) {
+ ::SendMessage(m_hwnd, BUTTONSETASSIDEBARBUTTON, 0, (LPARAM)this);
+ ::SendMessage(m_hwnd, BUTTONSETASFLATBTN, 1, 1);
+ ::SendMessage(m_hwnd, BUTTONSETASFLATBTN + 10, 1, 1);
+ ::SendMessage(m_hwnd, BUTTONSETASFLATBTN + 12, 0, (LPARAM)m_sideBar->getContainer());
+ m_buttonControl = (MButtonCtrl *)::GetWindowLongPtr(m_hwnd, 0);
+ }
+ else
+ delete this;
+
+ if(m_id == IDC_SIDEBARUP || m_id == IDC_SIDEBARDOWN)
+ ::SetParent(m_hwnd, m_sideBar->getContainer()->hwnd);
+}
+
+CSideBarButton::~CSideBarButton()
+{
+ if(m_hwnd) {
+ ::SendMessage(m_hwnd, BUTTONSETASSIDEBARBUTTON, 0, 0); // make sure, the button will no longer call us back
+ ::DestroyWindow(m_hwnd);
+ }
+}
+
+void CSideBarButton::Show(const int showCmd) const
+{
+ ::ShowWindow(m_hwnd, showCmd);
+}
+
+/**
+ * Measure the metrics for the current item. The side bar layouting will call this
+ * whenever a layout with a dynamic height is active). For fixed dimension layouts,
+ * m_elementWidth and m_elementHeight will be used.
+ *
+ * @return SIZE&: reference to the item's size member. The caller may use cx and cy values
+ * to determine further layouting actions.
+ */
+const SIZE& CSideBarButton::measureItem()
+{
+ SIZE sz;
+
+ if(m_sideBarLayout->pfnMeasureItem)
+ m_sideBarLayout->pfnMeasureItem(this); // use the current layout's function, if available, else use default
+ else {
+ HDC dc = ::GetDC(m_hwnd);
+ TCHAR tszLabel[255];
+
+ HFONT oldFont = reinterpret_cast<HFONT>(::SelectObject(dc, ::GetStockObject(DEFAULT_GUI_FONT)));
+
+ mir_sntprintf(tszLabel, 255, _T("%s"), m_dat->newtitle);
+ ::GetTextExtentPoint32(dc, tszLabel, lstrlen(tszLabel), &sz);
+
+ sz.cx += 28;
+ if(m_dat->pContainer->dwFlagsEx & TCF_CLOSEBUTTON)
+ sz.cx += 20;
+
+ if(m_sideBarLayout->dwFlags & CSideBar::SIDEBARLAYOUT_VERTICALORIENTATION)
+ m_sz.cy = sz.cx;
+ else
+ m_sz.cx = sz.cx;
+
+ ::SelectObject(dc, oldFont);
+ ::ReleaseDC(m_hwnd, dc);
+ }
+ return(m_sz);
+}
+/**
+ * Render the button item. Callback from the button window procedure
+ *
+ * @param ctl MButtonCtrl *: pointer to the private button data structure
+ * @param hdc HDC: device context for painting
+ */
+void CSideBarButton::RenderThis(const HDC hdc) const
+{
+ RECT rc;
+ LONG cx, cy;
+ HDC hdcMem = 0;
+ bool fVertical = (m_sideBarLayout->dwFlags & CSideBar::SIDEBARLAYOUT_VERTICALORIENTATION) ? true : false;
+ HBITMAP hbmMem, hbmOld;
+ HANDLE hbp = 0;
+
+ ::GetClientRect(m_hwnd, &rc);
+
+ if(m_id == IDC_SIDEBARUP || m_id == IDC_SIDEBARDOWN)
+ fVertical = false;
+
+ if(fVertical) {
+ cx = rc.bottom;
+ cy = rc.right;
+ }
+ else {
+ cx = rc.right;
+ cy = rc.bottom;
+ }
+
+ hdcMem = ::CreateCompatibleDC(hdc);
+
+ if(fVertical) {
+ RECT rcFlipped = {0,0,cx,cy};
+ hbmMem = CSkin::CreateAeroCompatibleBitmap(rcFlipped, hdcMem);
+ rc = rcFlipped;
+ }
+ else
+ hbmMem = CSkin::CreateAeroCompatibleBitmap(rc, hdcMem);
+
+ hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(hdcMem, hbmMem));
+
+ HFONT hFontOld = reinterpret_cast<HFONT>(::SelectObject(hdcMem, ::GetStockObject(DEFAULT_GUI_FONT)));
+
+ m_sideBarLayout->pfnBackgroundRenderer(hdcMem, &rc, this);
+ m_sideBarLayout->pfnContentRenderer(hdcMem, &rc, this);
+
+ ::SelectObject(hdcMem, hFontOld);
+
+ /*
+ * for vertical tabs, we did draw to a rotated rectangle, so we now must rotate the
+ * final bitmap back to it's original orientation
+ */
+
+ if(fVertical) {
+ ::SelectObject(hdcMem, hbmOld);
+
+ FIBITMAP *fib = FIF->FI_CreateDIBFromHBITMAP(hbmMem);
+ FIBITMAP *fib_new = FIF->FI_RotateClassic(fib, 90.0f);
+ FIF->FI_Unload(fib);
+ ::DeleteObject(hbmMem);
+ hbmMem = FIF->FI_CreateHBITMAPFromDIB(fib_new);
+ FIF->FI_Unload(fib_new);
+ hbmOld =reinterpret_cast<HBITMAP>(::SelectObject(hdcMem, hbmMem));
+ ::BitBlt(hdc, 0, 0, cy, cx, hdcMem, 0, 0, SRCCOPY);
+ ::SelectObject(hdcMem, hbmOld);
+ ::DeleteObject(hbmMem);
+ ::DeleteDC(hdcMem);
+ }
+ else {
+ ::BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY);
+ ::SelectObject(hdcMem, hbmOld);
+ ::DeleteObject(hbmMem);
+ ::DeleteDC(hdcMem);
+ }
+ return;
+}
+
+/**
+ * render basic button content like nickname and icon. Used for the basic layouts
+ * only. Pretty much the same code as used for the tabs.
+ *
+ * @param hdc : target device context
+ * @param rcItem : rectangle to render into
+ */
+void CSideBarButton::renderIconAndNick(const HDC hdc, const RECT *rcItem) const
+{
+ HICON hIcon;
+ RECT rc = *rcItem;
+ DWORD dwTextFlags = DT_SINGLELINE | DT_VCENTER;
+ int stateId = m_buttonControl->stateId;
+ int iSize = 16;
+ const TContainerData *pContainer = m_sideBar->getContainer();
+
+ if(m_dat && pContainer) {
+ hIcon = m_dat->cache->getIcon(iSize);
+
+ if (m_dat->mayFlashTab == FALSE || (m_dat->mayFlashTab == TRUE && m_dat->bTabFlash != 0) || !(pContainer->dwFlagsEx & TCF_FLASHICON)) {
+ DWORD ix = rc.left + 4;
+ DWORD iy = (rc.bottom + rc.top - iSize) / 2;
+ if (m_dat->dwFlagsEx & MWF_SHOW_ISIDLE && PluginConfig.m_IdleDetect)
+ CSkin::DrawDimmedIcon(hdc, ix, iy, iSize, iSize, hIcon, 180);
+ else
+ ::DrawIconEx(hdc, ix, iy, hIcon, iSize, iSize, 0, NULL, DI_NORMAL | DI_COMPAT);
+ }
+
+ rc.left += (iSize + 7);
+
+ /*
+ * draw the close button if enabled
+ */
+ if(m_sideBar->getContainer()->dwFlagsEx & TCF_CLOSEBUTTON) {
+ if(m_sideBar->getHoveredClose() != this)
+ CSkin::m_default_bf.SourceConstantAlpha = 150;
+
+ CMimAPI::m_MyAlphaBlend(hdc, rc.right - 20, (rc.bottom + rc.top - 16) / 2, 16, 16, CSkin::m_tabCloseHDC,
+ 0, 0, 16, 16, CSkin::m_default_bf);
+
+ rc.right -= 19;
+ CSkin::m_default_bf.SourceConstantAlpha = 255;
+ }
+
+ ::SetBkMode(hdc, TRANSPARENT);
+
+ if (m_dat->mayFlashTab == FALSE || (m_dat->mayFlashTab == TRUE && m_dat->bTabFlash != 0) || !(pContainer->dwFlagsEx & TCF_FLASHLABEL)) {
+ bool fIsActive = (m_sideBar->getActiveItem() == this ? true : false);
+ COLORREF clr = 0;
+ dwTextFlags |= DT_WORD_ELLIPSIS;
+
+ if (fIsActive || stateId == PBS_PRESSED)
+ clr = PluginConfig.tabConfig.colors[1];
+ else if (stateId == PBS_HOT)
+ clr = PluginConfig.tabConfig.colors[3];
+ else
+ clr = PluginConfig.tabConfig.colors[0];
+
+ CSkin::RenderText(hdc, m_buttonControl->hThemeButton, m_dat->newtitle, &rc, dwTextFlags, CSkin::m_glowSize, clr);
+ }
+ }
+}
+
+/**
+ * test if we have the mouse pointer over our close button.
+ * @return: 1 if the pointer is inside the button's rect, -1 otherwise
+ */
+int CSideBarButton::testCloseButton() const
+{
+ if(m_id == IDC_SIDEBARUP || m_id == IDC_SIDEBARDOWN) // scroller buttons don't have a close button
+ return(-1);
+
+ if(m_sideBar->getContainer()->dwFlagsEx & TCF_CLOSEBUTTON && !(getLayout()->dwFlags & CSideBar::SIDEBARLAYOUT_NOCLOSEBUTTONS)) {
+ POINT pt;
+ ::GetCursorPos(&pt);
+ ::ScreenToClient(m_hwnd, &pt);
+ RECT rc;
+
+ ::GetClientRect(m_hwnd, &rc);
+ if(getLayout()->dwFlags & CSideBar::SIDEBARLAYOUT_VERTICALORIENTATION) {
+ rc.bottom = rc.top + 18; rc.top += 2;
+ rc.left += 2; rc.right -= 2;
+ if(::PtInRect(&rc, pt))
+ return(1);
+ }
+ else {
+ rc.bottom -= 4; rc.top += 4;
+ rc.right -= 3; rc.left = rc.right - 16;
+ if(::PtInRect(&rc, pt))
+ return(1);
+ }
+ }
+ return(-1);
+}
+/**
+ * call back from the button window procedure. Activate my session
+ */
+void CSideBarButton::activateSession() const
+{
+ if(m_dat)
+ ::SendMessage(m_dat->hwnd, DM_ACTIVATEME, 0, 0); // the child window will activate itself
+}
+
+/**
+ * show the context menu (same as on tabs + */
+void CSideBarButton::invokeContextMenu()
+{
+ const TContainerData* pContainer = m_sideBar->getContainer();
+
+ if(pContainer) {
+ TSideBarNotify tsn = {0};
+ tsn.nmHdr.code = NM_RCLICK;
+ tsn.nmHdr.idFrom = 5000;
+ tsn.nmHdr.hwndFrom = ::GetDlgItem(pContainer->hwnd, 5000);
+ tsn.dat = m_dat;
+ ::SendMessage(pContainer->hwnd, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&tsn));
+ }
+}
+
+CSideBar::CSideBar(TContainerData *pContainer)
+{
+ m_pContainer = pContainer;
+ m_up = m_down = 0;
+ m_hwndScrollWnd = 0;
+ m_buttonlist.clear();
+ m_activeItem = 0;
+ m_isVisible = true;
+
+ Init(true);
+}
+
+CSideBar::~CSideBar()
+{
+ destroyScroller();
+ m_buttonlist.clear();
+
+ if(m_hwndScrollWnd)
+ ::DestroyWindow(m_hwndScrollWnd);
+}
+
+void CSideBar::Init(const bool fForce)
+{
+ m_iTopButtons = m_iBottomButtons = 0;
+ m_topHeight = m_bottomHeight = 0;
+ m_firstVisibleOffset = 0;
+ m_totalItemHeight = 0;
+
+ m_uLayout = (m_pContainer->dwFlagsEx & 0xff000000) >> 24;
+ m_uLayout = ((m_uLayout >= 0 && m_uLayout < NR_LAYOUTS) ? m_uLayout : 0);
+
+ m_currentLayout = &m_layouts[m_uLayout];
+
+ m_dwFlags = m_currentLayout->dwFlags;
+
+ m_dwFlags = (m_pContainer->dwFlagsEx & TCF_SBARLEFT ? m_dwFlags | SIDEBARORIENTATION_LEFT : m_dwFlags & ~SIDEBARORIENTATION_LEFT);
+ m_dwFlags = (m_pContainer->dwFlagsEx & TCF_SBARRIGHT ? m_dwFlags | SIDEBARORIENTATION_RIGHT : m_dwFlags & ~SIDEBARORIENTATION_RIGHT);
+
+ if(m_pContainer->dwFlags & CNT_SIDEBAR) {
+ if(m_hwndScrollWnd == 0)
+ m_hwndScrollWnd = ::CreateWindowEx(0, _T("TS_SideBarClass"), _T(""), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | WS_CHILD,
+ 0, 0, m_width, 40, m_pContainer->hwnd, reinterpret_cast<HMENU>(5000), g_hInst, this);
+
+ m_isActive = true;
+ m_isVisible = m_isActive ? m_isVisible : true;
+ createScroller();
+ m_elementHeight = m_currentLayout->height;
+ m_elementWidth = m_currentLayout->width;
+ m_width = m_elementWidth + 4;
+ populateAll();
+ if(m_activeItem)
+ setActiveItem(m_activeItem);
+ }
+ else {
+ destroyScroller();
+ m_width = 0;
+ m_isActive = m_isVisible = false;
+ m_activeItem = 0;
+
+ removeAll();
+ if(m_hwndScrollWnd)
+ ::DestroyWindow(m_hwndScrollWnd);
+ m_hwndScrollWnd = 0;
+ }
+}
+
+/**
+ * sets visibility status for the sidebar. An invisible sidebar (collapsed
+ * by the button in the message dialog) will remain active, it will, however
+ * not do any drawing or other things that are only visually important.
+ *
+ * @param fNewVisible : set the new visibility status
+ */
+void CSideBar::setVisible(bool fNewVisible)
+{
+ m_isVisible = fNewVisible;
+
+ /*
+ * only needed on hiding. Layout() will do it when showing it
+ */
+
+ if(!m_isVisible)
+ showAll(SW_HIDE);
+ else {
+ m_up->Show(SW_SHOW);
+ m_down->Show(SW_SHOW);
+ }
+}
+
+/**
+ * Create both scrollbar buttons which can be used to scroll the switchbar
+ * up and down.
+ */
+void CSideBar::createScroller()
+{
+ if(m_up == 0)
+ m_up = new CSideBarButton(IDC_SIDEBARUP, this);
+ if(m_down == 0)
+ m_down = new CSideBarButton(IDC_SIDEBARDOWN, this);
+
+ m_up->setLayout(m_currentLayout);
+ m_down->setLayout(m_currentLayout);
+}
+
+/**
+ * Destroy the scroller buttons.
+ */
+void CSideBar::destroyScroller()
+{
+ if(m_up) {
+ delete m_up;
+ m_up = 0;
+ }
+ if(m_down) {
+ delete m_down;
+ m_down = 0;
+ }
+}
+
+/**
+ * remove all buttons from the current list
+ * Does not remove the sessions. This is basically only used when switching
+ * from a sidebar to a tabbed interface
+ */
+void CSideBar::removeAll()
+{
+ ButtonIterator item = m_buttonlist.begin();
+
+ while(item != m_buttonlist.end()) {
+ delete *item;
+ item++;
+ }
+ m_buttonlist.clear();
+}
+
+/**
+ * popuplate the side bar with all sessions inside the current window. Information
+ * is gathered from the tab control, which remains active (but invisible) when the
+ * switch bar is in use.
+ *
+ * This is needed when the user switches from tabs to a switchbar layout while a
+ * window is open.
+ */
+void CSideBar::populateAll()
+{
+ HWND hwndTab = ::GetDlgItem(m_pContainer->hwnd, IDC_MSGTABS);
+
+ if(hwndTab) {
+ int iItems = (int)TabCtrl_GetItemCount(hwndTab);
+ TCITEM item = {0};
+
+ item.mask = TCIF_PARAM;
+ std::vector<CSideBarButton *>::iterator b_item;
+
+ m_iTopButtons = 0;
+
+ for(int i = 0; i < iItems; i++) {
+ TabCtrl_GetItem(hwndTab, i, &item);
+ if(item.lParam && ::IsWindow((HWND)item.lParam)) {
+ TWindowData *dat = (TWindowData *)::GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA);
+ if(dat) {
+ if((b_item = findSession(dat)) == m_buttonlist.end())
+ addSession(dat, i);
+ else {
+ (*b_item)->setLayout(m_currentLayout);
+ if(m_dwFlags & SIDEBARLAYOUT_VERTICALORIENTATION) {
+ (*b_item)->measureItem();
+ m_topHeight += ((*b_item)->getHeight() + 1);
+ }
+ else
+ m_topHeight += (m_elementHeight + 1);
+ }
+ }
+ }
+ }
+ }
+}
+/**
+ * Add a new session to the switchbar.
+ *
+ * @param dat _MessageWindowData *: session data for a client session. Must be fully initialized
+ * (that is, it can only be used after WM_INITIALOG completed).
+ * position: -1 = append, otherwise insert it at the given position
+ */
+void CSideBar::addSession(const TWindowData *dat, int position)
+{
+ if(!m_isActive)
+ return;
+
+ CSideBarButton *item = new CSideBarButton(dat, this);
+
+ item->setLayout(m_currentLayout);
+
+ if(m_dwFlags & SIDEBARLAYOUT_DYNHEIGHT) {
+ SIZE sz = item->measureItem();
+ m_topHeight += (sz.cy + 1);
+ }
+ else
+ m_topHeight += (m_elementHeight + 1);
+
+ m_iTopButtons++;
+ if(position == -1 || (size_t)position >= m_buttonlist.size())
+ m_buttonlist.push_back(item);
+ else {
+ ButtonIterator it = m_buttonlist.begin() + position;
+ m_buttonlist.insert(it, item);
+ }
+ SendDlgItemMessage(dat->hwnd, dat->bType == SESSIONTYPE_IM ? IDC_TOGGLESIDEBAR : IDC_CHAT_TOGGLESIDEBAR, BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)(m_dwFlags & SIDEBARORIENTATION_LEFT ? PluginConfig.g_buttonBarIcons[ICON_DEFAULT_LEFT] : PluginConfig.g_buttonBarIcons[ICON_DEFAULT_RIGHT]));
+
+ Invalidate();
+}
+
+/**
+ * Remove a new session from the switchbar.
+ *
+ * @param dat _MessageWindowData *: session data for a client session.
+ */
+HRESULT CSideBar::removeSession(const TWindowData *dat)
+{
+ if(dat) {
+ std::vector<CSideBarButton *>::iterator item = findSession(dat);
+
+ if(item != m_buttonlist.end()) {
+ m_iTopButtons--;
+ if(m_dwFlags & SIDEBARLAYOUT_DYNHEIGHT) {
+ SIZE sz = (*item)->getSize();
+ m_topHeight -= (sz.cy + 1);
+ }
+ else
+ m_topHeight -= (m_elementHeight + 1);
+ delete *item;
+ m_buttonlist.erase(item);
+ Invalidate();
+ return(S_OK);
+ }
+ }
+ return(S_FALSE);
+}
+
+/**
+ * make sure the given item is visible in a scrolled switch bar
+ *
+ * @param item CSideBarButtonItem*: the item which must be visible in the switch bar
+ */
+void CSideBar::scrollIntoView(const CSideBarButton *item)
+{
+ LONG spaceUsed = 0, itemHeight;
+ bool fNeedLayout = false;
+
+ if(!m_isActive)
+ return;
+
+ if(item == 0)
+ item = m_activeItem;
+
+ ButtonIterator it = m_buttonlist.begin();
+
+ while(it != m_buttonlist.end()) {
+ itemHeight = (*it)->getHeight();
+ spaceUsed += (itemHeight + 1);
+ if(*it == item )
+ break;
+ it++;
+ }
+
+ RECT rc;
+ GetClientRect(m_hwndScrollWnd, &rc);
+
+ if(m_topHeight <= rc.bottom) {
+ m_firstVisibleOffset = 0;
+ Layout();
+ return;
+ }
+ if(it == m_buttonlist.end() || (it == m_buttonlist.begin() && m_firstVisibleOffset == 0)) {
+ Layout();
+ return; // do nothing for the first item and .end() should not really happen
+ }
+
+ if(spaceUsed <= rc.bottom && spaceUsed - (itemHeight + 1) >= m_firstVisibleOffset) {
+ Layout();
+ return; // item fully visible, do nothing
+ }
+
+ /*
+ * button partially or not at all visible at the top
+ */
+ if(spaceUsed < m_firstVisibleOffset || spaceUsed - (itemHeight + 1) < m_firstVisibleOffset) {
+ m_firstVisibleOffset = spaceUsed - (itemHeight + 1);
+ fNeedLayout = true;
+ } else {
+ if(spaceUsed > rc.bottom) { // partially or not at all visible at the bottom
+ fNeedLayout = true;
+ m_firstVisibleOffset = spaceUsed - rc.bottom;
+ }
+ }
+
+ //if(fNeedLayout)
+ Layout();
+}
+/**
+ * Invalidate the button associated with the given session.
+ *
+ * @param dat _MessageWindowData*: Session data
+ */
+void CSideBar::updateSession(const TWindowData *dat)
+{
+ if(!m_isVisible || !m_isActive)
+ return;
+
+ ButtonIterator item = findSession(dat);
+ if(item != m_buttonlist.end()) {
+ if(m_dwFlags & SIDEBARLAYOUT_DYNHEIGHT) {
+ LONG oldHeight = (*item)->getHeight();
+ m_topHeight -= (oldHeight + 1);
+ SIZE sz = (*item)->measureItem();
+ m_topHeight += (sz.cy + 1);
+ if(sz.cy != oldHeight) {
+ Invalidate();
+ ::InvalidateRect((*item)->getHwnd(), NULL, TRUE);
+ }
+ else
+ ::InvalidateRect((*item)->getHwnd(), NULL, FALSE);
+ }
+ else
+ ::InvalidateRect((*item)->getHwnd(), NULL, FALSE);
+ }
+}
+
+/**
+ * Sets the active session item
+ * called from the global update handler in msgdialog/group room window
+ * on every tab activation to ensure consistency
+ *
+ * @param dat _MessageWindowData*: session data
+ *
+ * @return The previously active item (that can be zero)
+ */
+const CSideBarButton* CSideBar::setActiveItem(const TWindowData *dat)
+{
+ ButtonIterator item = findSession(dat);
+ if(item != m_buttonlist.end()) {
+ return(setActiveItem(*item));
+ }
+ return(0);
+}
+
+/**
+ * Layout the buttons within the available space... ensure that buttons are
+ * set to invisible if there is not enough space. Also, update the state of
+ * the scroller buttons
+ *
+ * @param rc RECT*:the window rectangle
+ *
+ * @param fOnlyCalc bool: if false (default), window positions will be updated, otherwise,
+ * the method will only calculate the layout parameters. A final call to
+ * Layout() with the parameter set to false is required to perform the
+ * position update.
+ */
+void CSideBar::Layout(const RECT *rc, bool fOnlyCalc)
+{
+ if(!m_isVisible)
+ return;
+
+ RECT rcWnd;
+
+ rc = &rcWnd;
+ ::GetClientRect(m_hwndScrollWnd, &rcWnd);
+
+ if(m_currentLayout->pfnLayout) {
+ m_currentLayout->pfnLayout(this, const_cast<RECT *>(rc));
+ return;
+ }
+
+ HDWP hdwp = ::BeginDeferWindowPos(1);
+
+ int topCount = 0, bottomCount = 0;
+ size_t i = 0, j = 0;
+ BOOL topEnabled = FALSE, bottomEnabled = FALSE;
+ HWND hwnd;
+ LONG spaceUsed = 0;
+ DWORD dwFlags = SWP_NOZORDER|SWP_NOACTIVATE;
+ LONG iSpaceAvail = rc->bottom;
+
+ m_firstVisibleOffset = max(0, m_firstVisibleOffset);
+
+ ButtonIterator item = m_buttonlist.begin();
+
+ m_totalItemHeight = 0;
+
+ LONG height = m_elementHeight;
+
+ while (item != m_buttonlist.end()) {
+ hwnd = (*item)->getHwnd();
+
+ if(m_dwFlags & SIDEBARLAYOUT_DYNHEIGHT)
+ height = (*item)->getHeight();
+
+ if(spaceUsed > iSpaceAvail || m_totalItemHeight + height < m_firstVisibleOffset) {
+ ::ShowWindow(hwnd, SW_HIDE);
+ item++;
+ m_totalItemHeight += (height + 1);
+ continue;
+ }
+
+ if ((*item)->isTopAligned()) {
+ if(m_totalItemHeight <= m_firstVisibleOffset) { // partially visible
+ if(!fOnlyCalc)
+ hdwp = ::DeferWindowPos(hdwp, hwnd, 0, 2, -(m_firstVisibleOffset - m_totalItemHeight),
+ m_elementWidth, height, SWP_SHOWWINDOW | dwFlags);
+ spaceUsed += ((height + 1) - (m_firstVisibleOffset - m_totalItemHeight));
+ m_totalItemHeight += (height + 1);
+ }
+ else {
+ if(!fOnlyCalc)
+ hdwp = ::DeferWindowPos(hdwp, hwnd, 0, 2, spaceUsed,
+ m_elementWidth, height, SWP_SHOWWINDOW | dwFlags);
+ spaceUsed += (height + 1);
+ m_totalItemHeight += (height + 1);
+ }
+ }/*
+ else {
+ }*/
+ item++;
+ }
+ topEnabled = m_firstVisibleOffset > 0;
+ bottomEnabled = (m_totalItemHeight - m_firstVisibleOffset > rc->bottom);
+ ::EndDeferWindowPos(hdwp);
+
+ //_DebugTraceA("layout: total %d, used: %d, first visible: %d", m_totalItemHeight, spaceUsed, m_firstVisibleOffset);
+ if(!fOnlyCalc) {
+ RECT rcContainer;
+ ::GetClientRect(m_pContainer->hwnd, &rcContainer);
+
+ LONG dx = m_dwFlags & SIDEBARORIENTATION_LEFT ? m_pContainer->tBorder_outer_left :
+ rcContainer.right - m_pContainer->tBorder_outer_right - (m_elementWidth + 4);
+
+ ::SetWindowPos(m_up->getHwnd(), 0, dx, m_pContainer->tBorder_outer_top + m_pContainer->MenuBar->getHeight(),
+ m_elementWidth + 4, 14, dwFlags | SWP_SHOWWINDOW);
+ ::SetWindowPos(m_down->getHwnd(), 0, dx, (rcContainer.bottom - 14 - m_pContainer->statusBarHeight - 1),
+ m_elementWidth + 4, 14, dwFlags | SWP_SHOWWINDOW);
+ ::EnableWindow(m_up->getHwnd(), topEnabled);
+ ::EnableWindow(m_down->getHwnd(), bottomEnabled);
+ ::InvalidateRect(m_up->getHwnd(), NULL, FALSE);
+ ::InvalidateRect(m_down->getHwnd(), NULL, FALSE);
+ }
+}
+
+inline void CSideBar::Invalidate()
+{
+ Layout(0);
+}
+
+void CSideBar::showAll(int showCmd)
+{
+ ::ShowWindow(m_up->getHwnd(), showCmd);
+ ::ShowWindow(m_down->getHwnd(), showCmd);
+
+ std::vector<CSideBarButton *>::iterator item = m_buttonlist.begin();
+
+ while(item != m_buttonlist.end())
+ ::ShowWindow((*item++)->getHwnd(), showCmd);
+}
+
+/**
+ * Helper function: find a button item associated to the given
+ * session data
+ *
+ * @param dat _MessageWindowData*: session information
+ *
+ * @return CSideBarButtonItem*: pointer to the found item. Zero, if none was found
+ */
+ButtonIterator CSideBar::findSession(const TWindowData *dat)
+{
+ if(dat) {
+ std::vector<CSideBarButton *>::iterator item = m_buttonlist.begin();
+
+ if(m_buttonlist.size() > 0) {
+ while(item != m_buttonlist.end()) {
+ if((*item)->getDat() == dat)
+ return(item);
+ item++;
+ }
+ }
+ }
+ return(m_buttonlist.end());
+}
+
+/**
+ * Helper function: find a button item associated to the given
+ * contact handle
+ *
+ * @param hContact HANDLE: contact's handle to look for
+ *
+ * @return CSideBarButtonItem*: pointer to the found item. Zero, if none was found
+ */
+ButtonIterator CSideBar::findSession(const HANDLE hContact)
+{
+ if(hContact) {
+ std::vector<CSideBarButton *>::iterator item = m_buttonlist.begin();
+
+ if(m_buttonlist.size() > 0) {
+ while(item != m_buttonlist.end()) {
+ if((*item)->getContactHandle() == hContact)
+ return(item);
+ item++;
+ }
+ }
+ }
+ return(m_buttonlist.end());
+}
+
+void CSideBar::processScrollerButtons(UINT commandID)
+{
+ if(!m_isActive || m_down == 0)
+ return;
+
+ if(commandID == IDC_SIDEBARDOWN && ::IsWindowEnabled(m_down->getHwnd()))
+ m_firstVisibleOffset += 10;
+ else if(commandID == IDC_SIDEBARUP && ::IsWindowEnabled(m_up->getHwnd()))
+ m_firstVisibleOffset = max(0, m_firstVisibleOffset - 10);
+
+ Layout(0);
+}
+
+void CSideBar::resizeScrollWnd(LONG x, LONG y, LONG width, LONG height) const
+{
+ if(!m_isVisible || !m_isActive) {
+ ::ShowWindow(m_hwndScrollWnd, SW_HIDE);
+ return;
+ }
+ ::SetWindowPos(m_hwndScrollWnd, 0, x, y + 15, m_width, height - 30,
+ SWP_NOCOPYBITS | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_NOSENDCHANGING | SWP_DEFERERASE | SWP_ASYNCWINDOWPOS);
+}
+
+void CSideBar::invalidateButton(const TWindowData* dat)
+{
+ if(m_isActive && m_isVisible) {
+ ButtonIterator it = findSession(dat);
+
+ if(it != this->m_buttonlist.end())
+ RedrawWindow((*it)->m_buttonControl->hwnd, 0, 0, RDW_INVALIDATE | RDW_UPDATENOW);
+ }
+}
+
+/**
+ * the window procedure for the sidebar container window (the window which
+ * acts as a parent for the actual buttons). itself, this window is a child
+ * of the container window.
+ */
+LRESULT CALLBACK CSideBar::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_SIZE:
+ return(TRUE);
+ case WM_ERASEBKGND: {
+ HDC hdc = (HDC)wParam;
+ RECT rc;
+ ::GetClientRect(hwnd, &rc);
+ if (CSkin::m_skinEnabled) {
+ CSkinItem *item = &SkinItems[ID_EXTBKSIDEBARBG];
+
+ if(item->IGNORED)
+ CSkin::SkinDrawBG(hwnd, m_pContainer->hwnd, m_pContainer, &rc, hdc);
+ else
+ CSkin::DrawItem(hdc, &rc, item);
+ }
+ else if (M->isAero() || M->isVSThemed()) {
+ HDC hdcMem;
+ HANDLE hbp = 0;
+ HBITMAP hbm, hbmOld;
+
+ if(CMimAPI::m_haveBufferedPaint)
+ hbp = CSkin::InitiateBufferedPaint(hdc, rc, hdcMem);
+ else {
+ hdcMem = ::CreateCompatibleDC(hdc);
+ hbm = CSkin::CreateAeroCompatibleBitmap(rc, hdcMem);
+ hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(hdcMem, hbm));
+ }
+
+ if(M->isAero())
+ ::FillRect(hdcMem, &rc, CSkin::m_BrushBack);
+ else
+ CSkin::FillBack(hdcMem, &rc);
+
+ if(hbp)
+ CSkin::FinalizeBufferedPaint(hbp, &rc);
+ else {
+ ::BitBlt(hdc, 0, 0, rc.right, rc.bottom, hdcMem, 0, 0, SRCCOPY);
+ ::SelectObject(hdcMem, hbmOld);
+ ::DeleteObject(hbm);
+ ::DeleteDC(hdcMem);
+ }
+ }
+ else
+ ::FillRect(hdc, &rc, ::GetSysColorBrush(COLOR_3DFACE));
+ return(1);
+ }
+ default:
+ break;
+ }
+ return(DefWindowProc(hwnd, msg, wParam, lParam));
+}
+
+LRESULT CALLBACK CSideBar::wndProcStub(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CSideBar *sideBar = (CSideBar *)::GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ if(sideBar)
+ return(sideBar->wndProc(hwnd, msg, wParam, lParam));
+
+ switch(msg) {
+ case WM_NCCREATE: {
+ CREATESTRUCT *cs = (CREATESTRUCT *)lParam;
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
+ return(TRUE);
+ }
+ default:
+ break;
+ }
+ return(::DefWindowProc(hwnd, msg, wParam, lParam));
+}
+/**
+ * paints the background for a switchbar item. It can paint aero, visual styles, skins or
+ * classic buttons (depending on os and current plugin settings).
+ *
+ * @param hdc HDC: target device context
+ * @param rc RECT*: target rectangle
+ * @param stateId the state identifier (normal, pressed, hot, disabled etc.)
+ */
+void __fastcall CSideBar::m_DefaultBackgroundRenderer(const HDC hdc, const RECT *rc,
+ const CSideBarButton *item)
+{
+ UINT id = item->getID();
+ int stateId = item->m_buttonControl->stateId;
+ bool fIsActiveItem = (item->m_sideBar->getActiveItem() == item);
+
+ if(CSkin::m_skinEnabled) {
+ TContainerData* pContainer = const_cast<TContainerData *>(item->m_sideBar->getContainer());
+ int id = stateId == PBS_PRESSED || fIsActiveItem ? ID_EXTBKBUTTONSPRESSED : (stateId == PBS_HOT ? ID_EXTBKBUTTONSMOUSEOVER : ID_EXTBKBUTTONSNPRESSED);
+ CSkinItem* skinItem = &SkinItems[id];
+ HWND hwnd;
+
+ if(id == IDC_SIDEBARUP)
+ hwnd = item->m_sideBar->getScrollUp()->m_buttonControl->hwnd;
+ else if(id == IDC_SIDEBARDOWN)
+ hwnd = item->m_sideBar->getScrollDown()->m_buttonControl->hwnd;
+ else
+ hwnd = item->m_buttonControl->hwnd;
+
+ CSkin::SkinDrawBG(hwnd, pContainer->hwnd, pContainer, const_cast<RECT *>(rc), hdc);
+ CSkin::DrawItem(hdc, rc, skinItem);
+ }
+ else if(M->isAero() || PluginConfig.m_fillColor) {
+ if(id == IDC_SIDEBARUP || id == IDC_SIDEBARDOWN) {
+ if(M->isAero())
+ ::FillRect(hdc, const_cast<RECT *>(rc), CSkin::m_BrushBack);
+ else
+ CSkin::FillBack(hdc, const_cast<RECT *>(rc));
+
+ if(stateId == PBS_HOT || stateId == PBS_PRESSED)
+ DrawAlpha(hdc, const_cast<RECT *>(rc), 0xf0f0f0, 70, 0x000000, 0, 9,
+ 31, 4, 0);
+ else
+ DrawAlpha(hdc, const_cast<RECT *>(rc), 0xf0f0f0, 30, 0x707070, 0, 9,
+ 31, 4, 0);
+ }
+ else {
+ if(PluginConfig.m_fillColor)
+ FillTabBackground(hdc, stateId, item->getDat(), const_cast<RECT *>(rc));
+
+ CSkin::m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA,
+ (stateId == PBS_HOT && !fIsActiveItem) ? 250 : (fIsActiveItem || stateId == PBS_PRESSED ? 250 : 230));
+ CSkin::m_switchBarItem->Render(hdc, rc, true);
+ if(stateId == PBS_HOT || stateId == PBS_PRESSED || fIsActiveItem) {
+ RECT rcGlow = *rc;
+ rcGlow.top += 1;
+ rcGlow.bottom -= 2;
+
+ CSkin::m_tabGlowTop->setAlphaFormat(AC_SRC_ALPHA, (stateId == PBS_PRESSED || fIsActiveItem) ? 180 : 100);
+ CSkin::m_tabGlowTop->Render(hdc, &rcGlow, true);
+ }
+ }
+ }
+ else if(M->isVSThemed()) {
+ RECT *rcDraw = const_cast<RECT *>(rc);
+ if(id == IDC_SIDEBARUP || id == IDC_SIDEBARDOWN) {
+ ::FillRect(hdc, rc, stateId == PBS_HOT ? ::GetSysColorBrush(COLOR_HOTLIGHT) : ::GetSysColorBrush(COLOR_3DFACE));
+ ::InflateRect(rcDraw, -2, 0);
+ ::DrawEdge(hdc, rcDraw, EDGE_ETCHED, BF_SOFT|BF_RECT|BF_FLAT);
+ }
+ else {
+ CSkin::FillBack(hdc, rcDraw);
+
+ if(CMimAPI::m_pfnIsThemeBackgroundPartiallyTransparent(item->m_buttonControl->hThemeToolbar, TP_BUTTON, stateId))
+ CMimAPI::m_pfnDrawThemeParentBackground(item->getHwnd(), hdc, rcDraw);
+
+ if(M->isAero() || PluginConfig.m_WinVerMajor >= 6) {
+ stateId = (fIsActiveItem ? PBS_PRESSED : PBS_HOT);
+ CMimAPI::m_pfnDrawThemeBackground(item->m_buttonControl->hThemeToolbar, hdc, 8, RBStateConvert2Flat(stateId), rcDraw, rcDraw);
+ }
+ else {
+ stateId = (fIsActiveItem ? PBS_PRESSED : PBS_HOT);
+ CMimAPI::m_pfnDrawThemeBackground(item->m_buttonControl->hThemeToolbar, hdc, TP_BUTTON, TBStateConvert2Flat(stateId), rcDraw, rcDraw);
+ }
+ }
+ }
+ else {
+ RECT *rcDraw = const_cast<RECT *>(rc);
+ if(!(id == IDC_SIDEBARUP || id == IDC_SIDEBARDOWN)) {
+ HBRUSH br = (stateId == PBS_HOT && !fIsActiveItem) ? ::GetSysColorBrush(COLOR_BTNSHADOW) : (fIsActiveItem || stateId == PBS_PRESSED ? ::GetSysColorBrush(COLOR_HOTLIGHT) : ::GetSysColorBrush(COLOR_3DFACE));
+ ::FillRect(hdc, rc, br);
+ ::DrawEdge(hdc, rcDraw, (stateId == PBS_HOT && !fIsActiveItem) ? EDGE_ETCHED : (fIsActiveItem || stateId == PBS_PRESSED) ? EDGE_BUMP : EDGE_ETCHED, BF_RECT | BF_SOFT | BF_FLAT);
+ }
+ else {
+ ::FillRect(hdc, rc, stateId == PBS_HOT ? ::GetSysColorBrush(COLOR_HOTLIGHT) : ::GetSysColorBrush(COLOR_3DFACE));
+ ::InflateRect(rcDraw, -2, 0);
+ ::DrawEdge(hdc, rcDraw, EDGE_ETCHED, BF_SOFT|BF_RECT|BF_FLAT);
+ }
+ }
+}
+
+void __fastcall CSideBar::m_DefaultContentRenderer(const HDC hdc, const RECT *rcBox,
+ const CSideBarButton *item)
+{
+ UINT id = item->getID();
+ const TWindowData* dat = item->getDat();
+ int stateID = item->m_buttonControl->stateId;
+
+ LONG cx = rcBox->right - rcBox->left;
+ LONG cy = rcBox->bottom - rcBox->top;
+
+ if(id == IDC_SIDEBARUP || id == IDC_SIDEBARDOWN) {
+ ::DrawIconEx(hdc, (rcBox->left + rcBox->right) / 2 - 8, (rcBox->top + rcBox->bottom) / 2 - 8, id == IDC_SIDEBARUP ? PluginConfig.g_buttonBarIcons[26] : PluginConfig.g_buttonBarIcons[16],
+ 16, 16, 0, 0, DI_NORMAL);
+ if(!M->isAero() && stateID == PBS_HOT)
+ ::DrawEdge(hdc, const_cast<RECT *>(rcBox), BDR_INNER, BF_RECT | BF_SOFT | BF_FLAT);
+ }
+ else if(dat)
+ item->renderIconAndNick(hdc, rcBox);
+}
+
+/**
+ * content renderer for the advanced side bar button layout. includes
+ * avatars
+ */
+void __fastcall CSideBar::m_AdvancedContentRenderer(const HDC hdc, const RECT *rcBox,
+ const CSideBarButton *item)
+{
+ UINT id = item->getID();
+ const TWindowData* dat = item->getDat();
+ int stateID = item->m_buttonControl->stateId;
+
+ LONG cx = rcBox->right - rcBox->left;
+ LONG cy = rcBox->bottom - rcBox->top;
+ SIZE szFirstLine, szSecondLine;
+
+ if(id == IDC_SIDEBARUP || id == IDC_SIDEBARDOWN)
+ m_DefaultContentRenderer(hdc, rcBox, item);
+ else if(dat) {
+ RECT rc = *rcBox;
+
+ if(dat->ace && dat->ace->hbmPic) { // we have an avatar
+ double dNewHeight, dNewWidth;
+ LONG maxHeight = cy - 8;
+ bool fFree = false;
+
+ Utils::scaleAvatarHeightLimited(dat->ace->hbmPic, dNewWidth, dNewHeight, maxHeight);
+
+ HBITMAP hbmResized = CSkin::ResizeBitmap(dat->ace->hbmPic, dNewWidth, dNewHeight, fFree);
+ HDC dc = CreateCompatibleDC(hdc);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmResized));
+
+ LONG xOff = (cx - maxHeight) + (maxHeight - (LONG)dNewWidth) / 2 - 4;
+ LONG yOff = (cy - (LONG)dNewHeight) / 2;
+
+ CMimAPI::m_MyAlphaBlend(hdc, xOff, yOff, (LONG)dNewWidth, (LONG)dNewHeight, dc, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
+ ::SelectObject(dc, hbmOld);
+ if(hbmResized != dat->ace->hbmPic)
+ ::DeleteObject(hbmResized);
+ ::DeleteDC(dc);
+ rc.right -= (maxHeight + 6);
+ }
+
+ /*
+ * calculate metrics based on font configuration. Determine if we have enough
+ * space for both lines
+ */
+
+ rc.left += 3;
+ HFONT hOldFont = reinterpret_cast<HFONT>(::SelectObject(hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_NICK]));
+ ::GetTextExtentPoint32A(hdc, "A", 1, &szFirstLine);
+ ::SelectObject(hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_STATUS]);
+ ::GetTextExtentPoint32A(hdc, "A", 1, &szSecondLine);
+ szSecondLine.cy = max(szSecondLine.cy, 18);
+
+ LONG required = szFirstLine.cy + szSecondLine.cy;
+ bool fSecondLine = (required < cy ? true : false);
+
+ DWORD dtFlags = DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | (!fSecondLine ? DT_VCENTER : 0);
+
+ ::SelectObject(hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_NICK]);
+ rc.top++;
+ ::SetBkMode(hdc, TRANSPARENT);
+ CSkin::RenderText(hdc, dat->hThemeIP, dat->cache->getNick(), &rc,
+ dtFlags, CSkin::m_glowSize, CInfoPanel::m_ipConfig.clrs[IPFONTID_NICK]);
+
+ if(fSecondLine) {
+ int iSize;
+ HICON hIcon = dat->cache->getIcon(iSize);
+
+ /*
+ * TODO support larger icons at a later time. This side bar button
+ * could use 32x32 icons as well.
+ */
+
+ rc.top = rc.bottom - szSecondLine.cy - 2;
+ ::DrawIconEx(hdc, rc.left, rc.top + (rc.bottom - rc.top) / 2 - 8, hIcon, 16, 16, 0, 0, DI_NORMAL);
+ rc.left += 18;
+ ::SelectObject(hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_STATUS]);
+ CSkin::RenderText(hdc, dat->hThemeIP, dat->szStatus, &rc,
+ dtFlags | DT_VCENTER, CSkin::m_glowSize, CInfoPanel::m_ipConfig.clrs[IPFONTID_STATUS]);
+ }
+ ::SelectObject(hdc, hOldFont);
+ }
+}
+
+/**
+ * measure callback for the advanced sidebar button layout (vertical mode
+ * with variable height buttons)
+ */
+const SIZE& __fastcall CSideBar::m_measureAdvancedVertical(CSideBarButton* item)
+{
+ const TWindowData* dat = item->getDat();
+
+ SIZE sz = {0};
+
+ if(dat) {
+ SIZE szFirstLine, szSecondLine;
+
+ if(dat->ace && dat->ace->hbmPic)
+ sz.cy = item->getLayout()->width;
+
+ HDC dc = ::GetDC(dat->hwnd);
+
+ HFONT hOldFont = reinterpret_cast<HFONT>(::SelectObject(dc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_NICK]));
+ ::GetTextExtentPoint32(dc, dat->cache->getNick(), lstrlen(dat->cache->getNick()), &szFirstLine);
+ ::SelectObject(dc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_STATUS]);
+ ::GetTextExtentPoint32(dc, dat->szStatus, lstrlen(dat->szStatus), &szSecondLine);
+ ::SelectObject(dc, hOldFont);
+ ReleaseDC(dat->hwnd, dc);
+
+ szSecondLine.cx += 18; // icon space
+
+ sz.cy += max(szFirstLine.cx + 4, szSecondLine.cx + 4);
+ sz.cy += 2;
+ }
+ item->setSize(sz);
+ return(item->getSize());
+}
diff --git a/plugins/TabSRMM/src/srmm.cpp b/plugins/TabSRMM/src/srmm.cpp new file mode 100644 index 0000000000..60af12edfd --- /dev/null +++ b/plugins/TabSRMM/src/srmm.cpp @@ -0,0 +1,316 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: srmm.cpp 13596 2011-04-15 19:07:23Z george.hazan $
+ *
+ * plugin loading functions and global exports.
+ *
+ */
+
+#include "commonheaders.h"
+
+extern int LoadSendRecvMessageModule(void);
+extern int SplitmsgShutdown(void);
+extern void LogErrorMessage(HWND hwndDlg, struct TWindowData *dat, int i, TCHAR *szMsg);
+extern int Chat_Load(PLUGINLINK *link), Chat_Unload();
+extern void FreeLogFonts();
+
+PLUGINLINK *pluginLink;
+HINSTANCE g_hInst;
+LOGFONT lfDefault = {0};
+
+/*
+ * miranda interfaces
+ */
+
+struct LIST_INTERFACE li;
+struct MM_INTERFACE mmi;
+int hLangpack;
+TIME_API tmi = {0};
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+#ifdef __GNUWIN32__
+ "TabSRMM (MINGW32)",
+#else
+#ifdef _WIN64
+ "TabSRMM (x64, Unicode)",
+#else
+#ifdef _UNICODE
+ "TabSRMM (Unicode)",
+#else
+ "TabSRMM",
+#endif
+#endif
+#endif
+ PLUGIN_MAKE_VERSION(_VER_MAJOR, _VER_MINOR, _VER_REVISION, _VER_BUILD),
+ "IM and group chat module for Miranda IM.",
+ "The Miranda developers team and contributors",
+ "silvercircle _at_ gmail _dot_ com",
+ "2000-2010 Miranda Project and contributors. See readme.txt for more.",
+ "http://miranda.or.at",
+ UNICODE_AWARE,
+ DEFMOD_SRMESSAGE, // replace internal version (if any)
+ {0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 }} //{6CA5F042-7A7F-47cc-A715-FC8C46FBF434}
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ g_hInst = hinstDLL;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ CMimAPI::m_MimVersion = mirandaVersion;
+
+ if(WinVerMajor() < 5) {
+ MessageBox(0, _T("This version of tabSRMM requires Windows 2000 or later."), _T("tabSRMM"), MB_OK | MB_ICONERROR);
+ return(0);
+ }
+ if (mirandaVersion < PLUGIN_MAKE_VERSION(0, 8, 6, 0)) {
+ MessageBox(0, _T("This version of tabSRMM requires Miranda 0.8.5 or later. The plugin cannot be loaded."), _T("tabSRMM"), MB_OK | MB_ICONERROR);
+ return(0);
+ }
+ return &pluginInfo;
+}
+
+static const MUUID interfaces[] = {MIID_SRMM, MIID_CHAT, MIID_LAST};
+
+extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+extern "C" int __declspec(dllexport) Load(PLUGINLINK * link)
+{
+ pluginLink = link;
+
+ mir_getMMI(&mmi);
+ mir_getLI(&li);
+ mir_getTMI(&tmi);
+ mir_getLP(&pluginInfo);
+
+ CTranslator::preTranslateAll();
+
+ M = new CMimAPI();
+
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
+
+ Chat_Load(pluginLink);
+
+ return LoadSendRecvMessageModule();
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ int iRet;
+#if defined(__USE_EX_HANDLERS)
+ __try {
+#endif
+ FreeLogFonts();
+ Chat_Unload();
+ iRet = SplitmsgShutdown();
+ Skin->setupTabCloseBitmap(true);
+ Skin->UnloadAeroTabs();
+ CleanTempFiles();
+ delete Skin;
+ DestroyServiceFunction(hTypingNotify);
+ delete sendLater;
+ delete sendQueue;
+ delete M;
+#if defined(__USE_EX_HANDLERS)
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"SHUTDOWN_STAGE_UNLOAD", false)) {
+ return(0);
+ }
+#endif
+ 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);
+
+
+ _vsnwprintf(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 = M->utf8_encodeW(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: ");
+ _vsnprintf(&debug[9], ibsize - 10, fmt, va);
+#ifdef _DEBUG
+ OutputDebugStringA(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) {
+ 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(HANDLE hContact, const TCHAR *fmt, ...)
+{
+ va_list va;
+ TCHAR debug[1024];
+ int ibsize = 1023;
+
+ va_start(va, fmt);
+ _vsntprintf(debug, ibsize, fmt, va);
+
+ if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) {
+ MIRANDASYSTRAYNOTIFY tn;
+ TCHAR szTitle[128];
+
+ tn.szProto = NULL;
+ tn.cbSize = sizeof(tn);
+ mir_sntprintf(szTitle, safe_sizeof(szTitle), TranslateT("tabSRMM Message (%s)"), (hContact != 0) ? (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR) : TranslateT("Global"));
+ 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;
+}
+
+INT_PTR CALLBACK DlgProcAbout(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ COLORREF url_visited = RGB(128, 0, 128);
+ COLORREF url_unvisited = RGB(0, 0, 255);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ char str[64];
+ TCHAR tStr[80];
+ char szVersion[512], *found = NULL, buildstr[50] = "";
+ UINT build_nr = 0;
+ DWORD v = pluginInfo.version;
+
+ mir_snprintf(str, sizeof(str), Translate("Built %s %s"), __DATE__, __TIME__);
+ SetDlgItemTextA(hwndDlg, IDC_BUILDTIME, str);
+
+ CallService(MS_SYSTEM_GETVERSIONTEXT, 500, (LPARAM)szVersion);
+ if ((found = strchr(szVersion, '#')) != NULL) {
+ build_nr = atoi(found + 1);
+ mir_snprintf(buildstr, 50, "[Build #%d]", build_nr);
+ }
+ TCHAR *szBuildstr = mir_a2t(buildstr);
+ mir_sntprintf(tStr, safe_sizeof(tStr), _T("TabSRMM\n%s %d.%d.%d.%d (Unicode) %s"),
+ _T("Version"), HIBYTE(HIWORD(v)), LOBYTE(HIWORD(v)), HIBYTE(LOWORD(v)), LOBYTE(LOWORD(v)),
+ szBuildstr);
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, tStr);
+ mir_free(szBuildstr);
+ }
+ SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadSkinnedIcon(SKINICON_EVENT_MESSAGE));
+ SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE));
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ case IDC_SUPPORT:
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://miranda.or.at/");
+ break;
+ case IDC_RESETWARNINGS:
+ M->WriteDword(SRMSGMOD_T, "cWarningsL", 0);
+ M->WriteDword(SRMSGMOD_T, "cWarningsH", 0);
+ break;
+ }
+ break;
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, RGB(60, 60, 150));
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+ default:
+ break;
+ }
+ return FALSE;
+}
diff --git a/plugins/TabSRMM/src/tabctrl.cpp b/plugins/TabSRMM/src/tabctrl.cpp new file mode 100644 index 0000000000..2aadbd91f5 --- /dev/null +++ b/plugins/TabSRMM/src/tabctrl.cpp @@ -0,0 +1,1592 @@ +/* astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2009 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: tabctrl.cpp 12643 2010-09-09 03:57:16Z silvercircle $
+ *
+ * a custom tab control, skinable, aero support, single/multi row, button
+ * tabs support, proper rendering for bottom row tabs and more.
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+static WNDPROC OldTabControlClassProc;
+extern ButtonSet g_ButtonSet;
+
+static LRESULT CALLBACK TabControlSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#define FIXED_TAB_SIZE 100
+
+/*
+ * register the new tab control as a window class (TSTabCtrlClass)
+ */
+
+int TSAPI RegisterTabCtrlClass(void)
+{
+ WNDCLASSEXA wc;
+ WNDCLASSEX wce;
+
+ ZeroMemory(&wc, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = "TSTabCtrlClass";
+ wc.lpfnWndProc = TabControlSubclassProc;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.cbWndExtra = sizeof(struct TabControlData *);
+ wc.hbrBackground = 0;
+ wc.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_PARENTDC;
+ RegisterClassExA(&wc);
+
+ ZeroMemory(&wce, sizeof(wce));
+ wce.cbSize = sizeof(wce);
+ wce.lpszClassName = _T("TSStatusBarClass");
+ wce.lpfnWndProc = StatusBarSubclassProc;
+ wce.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wce.cbWndExtra = sizeof(void*);
+ wce.hbrBackground = 0;
+ wce.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_PARENTDC;
+ RegisterClassEx(&wce);
+
+ ZeroMemory(&wce, sizeof(wce));
+ wce.cbSize = sizeof(wce);
+ wce.lpszClassName = _T("TS_SideBarClass");
+ wce.lpfnWndProc = CSideBar::wndProcStub;;
+ wce.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wce.cbWndExtra = sizeof(void*);
+ wce.hbrBackground = 0;
+ wce.style = CS_GLOBALCLASS;// | CS_DBLCLKS; // | CS_PARENTDC;
+ RegisterClassEx(&wce);
+
+ ZeroMemory(&wce, sizeof(wce));
+ wce.cbSize = sizeof(wce);
+ wce.lpszClassName = _T("TSHK");
+ wce.lpfnWndProc = HotkeyHandlerDlgProc;
+ wce.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wce.cbWndExtra = sizeof(void*);
+ wce.hbrBackground = 0;
+ wce.style = CS_GLOBALCLASS;// | CS_DBLCLKS; // | CS_PARENTDC;
+ RegisterClassEx(&wce);
+
+ return 0;
+}
+
+static int TabCtrl_TestForCloseButton(const TabControlData *tabdat, HWND hwnd, POINT& pt)
+{
+ TCHITTESTINFO tci = {0};
+ tci.pt.x = pt.x;
+ tci.pt.y = pt.y;
+ int iTab;
+
+ ScreenToClient(hwnd, &tci.pt);
+ iTab = TabCtrl_HitTest(hwnd, &tci);
+ if(iTab != -1) {
+ RECT rcTab;
+
+ if(tci.flags & TCHT_NOWHERE)
+ return(-1);
+
+ TabCtrl_GetItemRect(hwnd, iTab, &rcTab);
+ if(tabdat->dwStyle & TCS_BUTTONS) {
+ rcTab.right -= 1;
+ rcTab.left = rcTab.right - 18;
+ }
+ else {
+ rcTab.left = rcTab.right - 18;
+ rcTab.right -= 5;
+ }
+ rcTab.bottom -= 4;
+ rcTab.top += 4;
+ if(PtInRect(&rcTab, tci.pt))
+ return(iTab);
+ }
+ return(-1);
+}
+
+/*
+ * tabctrl helper function
+ * Finds leftmost down item.
+ */
+
+static UINT FindLeftDownItem(HWND hwnd)
+{
+ RECT rctLeft = {100000, 0, 0, 0}, rctCur;
+ int nCount = TabCtrl_GetItemCount(hwnd) - 1;
+ UINT nItem = 0;
+ int i;
+
+ for (i = 0;i < nCount;i++) {
+ TabCtrl_GetItemRect(hwnd, i, &rctCur);
+ if (rctCur.left > 0 && rctCur.left <= rctLeft.left) {
+ if (rctCur.bottom > rctLeft.bottom) {
+ rctLeft = rctCur;
+ nItem = i;
+ }
+ }
+ }
+ return nItem;
+}
+
+/*
+ * tab control color definitions, including the database setting key names
+ */
+
+static struct colOptions {
+ UINT defclr;
+ char *szKey;
+ char *szSkinnedKey;
+} tabcolors[] = {
+ COLOR_BTNTEXT, "tab_txt_normal", "S_tab_txt_normal",
+ COLOR_BTNTEXT, "tab_txt_active", "S_tab_txt_active",
+ COLOR_HOTLIGHT, "tab_txt_hottrack", "S_tab_txt_hottrack",
+ COLOR_HOTLIGHT, "tab_txt_unread", "S_tab_txt_unread",
+ COLOR_3DFACE, "tab_bg_normal", "tab_bg_normal",
+ COLOR_3DFACE, "tab_bg_active", "tab_bg_active",
+ COLOR_3DFACE, "tab_bg_hottrack", "tab_bg_hottrack",
+ COLOR_3DFACE, "tab_bg_unread", "tab_bg_unread",
+ 0, 0, NULL, NULL
+};
+
+/*
+ * hints for drawing functions
+ */
+
+#define HINT_ACTIVATE_RIGHT_SIDE 1
+#define HINT_ACTIVE_ITEM 2
+#define FLOAT_ITEM_HEIGHT_SHIFT 2
+#define ACTIVE_ITEM_HEIGHT_SHIFT 2
+#define SHIFT_FROM_CUT_TO_SPIN 4
+#define HINT_TRANSPARENT 16
+#define HINT_HOTTRACK 32
+
+static void TSAPI DrawCustomTabPage(HDC hdc, RECT& rcClient)
+{
+ HBRUSH brOld = reinterpret_cast<HBRUSH>(::SelectObject(hdc, CSkin::m_BrushFill));
+ HPEN hPen = ::CreatePen(PS_SOLID, 1, PluginConfig.m_cRichBorders);
+ HPEN hPenOld = reinterpret_cast<HPEN>(::SelectObject(hdc, hPen));
+ ::Rectangle(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
+ ::SelectObject(hdc, hPenOld);
+ ::SelectObject(hdc, brOld);
+ ::DeleteObject(hPen);
+}
+
+void TSAPI FillTabBackground(const HDC hdc, int iStateId, const TWindowData* dat, RECT* rc)
+{
+ unsigned clrIndex;
+
+ if(dat && dat->mayFlashTab)
+ clrIndex = 7;
+ else
+ clrIndex = (iStateId == PBS_PRESSED ? 5 : (iStateId == PBS_HOT ? 6 : 4));
+
+ if(PluginConfig.tabConfig.colors[clrIndex] != PluginConfig.m_fillColor)
+ FillRect(hdc, rc, PluginConfig.tabConfig.m_brushes[clrIndex - 4]);
+ else
+ CSkin::FillBack(hdc, rc);
+}
+
+/*
+ * draws the item contents (icon and label)
+ * it obtains the label and icon handle directly from the message window data
+ * no image list is used and necessary, the message window dialog procedure has to provide a valid
+ * icon handle in dat->hTabIcon
+ */
+
+static void DrawItem(TabControlData *tabdat, HDC dc, RECT *rcItem, int nHint, int nItem, TWindowData* dat)
+{
+ int iSize = 16;
+ DWORD dwTextFlags = DT_SINGLELINE | DT_VCENTER/* | DT_NOPREFIX*/;
+ BOOL leftMost = FALSE;
+
+ if (dat) {
+ HICON hIcon;
+ COLORREF clr = 0;
+ HFONT oldFont;
+ DWORD dwStyle = tabdat->dwStyle;
+ int oldMode = 0;
+ unsigned clrIndex = 0;
+
+ InflateRect(rcItem, -2, -2);
+
+ if(dat->mayFlashTab)
+ clrIndex = 3;
+ else
+ clrIndex = (nHint & HINT_ACTIVE_ITEM ? 1 : (nHint & HINT_HOTTRACK ? 2 : 0));
+
+ clr = PluginConfig.tabConfig.colors[clrIndex];
+
+ oldMode = SetBkMode(dc, TRANSPARENT);
+
+ if (!(dwStyle & TCS_BOTTOM))
+ OffsetRect(rcItem, 0, 1);
+
+ if (dat->dwFlags & MWF_ERRORSTATE)
+ hIcon = PluginConfig.g_iconErr;
+ else if (dat->mayFlashTab)
+ hIcon = dat->iFlashIcon;
+ else {
+ if (dat->si && dat->iFlashIcon) {
+ int sizeX, sizeY;
+
+ hIcon = dat->iFlashIcon;
+ Utils::getIconSize(hIcon, sizeX, sizeY);
+ iSize = sizeX;
+ } else if (dat->hTabIcon == dat->hTabStatusIcon && dat->hXStatusIcon)
+ hIcon = dat->hXStatusIcon;
+ else
+ hIcon = dat->hTabIcon;
+ }
+
+
+ if (dat->mayFlashTab == FALSE || (dat->mayFlashTab == TRUE && dat->bTabFlash != 0) || !(dat->pContainer->dwFlagsEx & TCF_FLASHICON)) {
+ DWORD ix = rcItem->left + tabdat->m_xpad - 1;
+ DWORD iy = (rcItem->bottom + rcItem->top - iSize) / 2;
+ if (dat->dwFlagsEx & MWF_SHOW_ISIDLE && PluginConfig.m_IdleDetect)
+ CSkin::DrawDimmedIcon(dc, ix, iy, iSize, iSize, hIcon, 180);
+ else
+ DrawIconEx(dc, ix, iy, hIcon, iSize, iSize, 0, NULL, DI_NORMAL | DI_COMPAT);
+ }
+
+ rcItem->left += (iSize + 2 + tabdat->m_xpad);
+
+ if(tabdat->fCloseButton) {
+ if(tabdat->iHoveredCloseIcon != nItem)
+ CSkin::m_default_bf.SourceConstantAlpha = 150;
+
+ CMimAPI::m_MyAlphaBlend(dc, rcItem->right - 16 - tabdat->m_xpad, (rcItem->bottom + rcItem->top - 16) / 2, 16, 16, CSkin::m_tabCloseHDC,
+ 0, 0, 16, 16, CSkin::m_default_bf);
+
+ rcItem->right -= (18 + tabdat->m_xpad);
+ CSkin::m_default_bf.SourceConstantAlpha = 255;
+ }
+
+ if (dat->mayFlashTab == FALSE || (dat->mayFlashTab == TRUE && dat->bTabFlash != 0) || !(dat->pContainer->dwFlagsEx & TCF_FLASHLABEL)) {
+ oldFont = (HFONT)SelectObject(dc, (HFONT)SendMessage(tabdat->hwnd, WM_GETFONT, 0, 0));
+ if (tabdat->dwStyle & TCS_BUTTONS || !(tabdat->dwStyle & TCS_MULTILINE)) { // || (tabdat->m_moderntabs && leftMost)) {
+ rcItem->right -= tabdat->m_xpad;
+ dwTextFlags |= DT_WORD_ELLIPSIS;
+ }
+ CSkin::RenderText(dc, dwStyle & TCS_BUTTONS ? tabdat->hThemeButton : tabdat->hTheme, dat->newtitle, rcItem, dwTextFlags, CSkin::m_glowSize, clr);
+ SelectObject(dc, oldFont);
+ }
+ if (oldMode)
+ SetBkMode(dc, oldMode);
+ }
+}
+
+/*
+ * draws the item rect (the "tab") in *classic* style (no visual themes
+ */
+
+static RECT rcTabPage = {0};
+
+static void DrawItemRect(struct TabControlData *tabdat, HDC dc, RECT *rcItem, int nHint, int iItem, const TWindowData* dat)
+{
+ POINT pt;
+ DWORD dwStyle = tabdat->dwStyle;
+
+ rcItem->bottom -= 1;
+ if (rcItem->left >= 0) {
+
+ /*
+ * draw "button style" tabs... raised edge for hottracked, sunken edge for active (pushed)
+ * otherwise, they get a normal border
+ */
+
+ if (dwStyle & TCS_BUTTONS) {
+ BOOL bClassicDraw = (tabdat->m_VisualStyles == FALSE);
+
+ // draw frame controls for button or bottom tabs
+ if (dwStyle & TCS_BOTTOM)
+ rcItem->top++;
+
+ rcItem->right += 6;
+ if(tabdat->fAeroTabs) {
+ if(M->isAero()) {
+ InflateRect(rcItem, 2, 0);
+ FillRect(dc, rcItem, CSkin::m_BrushBack);
+ }
+ else if(dat) {
+ int iStateId = (nHint & HINT_ACTIVE_ITEM ? PBS_PRESSED : 0) | (nHint & HINT_HOTTRACK ? PBS_HOT : 0);
+
+ InflateRect(rcItem, 1, 0);
+ FillTabBackground(dc, iStateId, dat, rcItem);
+ }
+ CSkin::m_switchBarItem->setAlphaFormat(AC_SRC_ALPHA, nHint & HINT_ACTIVE_ITEM ? 255 : 200);
+ CSkin::m_switchBarItem->Render(dc, rcItem, true);
+
+
+ if(nHint & HINT_ACTIVE_ITEM || nHint & HINT_HOTTRACK) {
+ RECT rcGlow = *rcItem;
+
+ if(dwStyle & TCS_BOTTOM)
+ rcGlow.top++;
+ else
+ rcGlow.bottom--;
+
+ tabdat->helperGlowItem->setAlphaFormat(AC_SRC_ALPHA, nHint & HINT_ACTIVE_ITEM ? 200 : 150);
+ tabdat->helperGlowItem->Render(dc, &rcGlow, true);
+ }
+ }
+ else if(bClassicDraw) {
+ if (CSkin::m_skinEnabled) {
+ CSkinItem *item = nHint & HINT_ACTIVE_ITEM ? &SkinItems[ID_EXTBKBUTTONSPRESSED] : (nHint & HINT_HOTTRACK ? &SkinItems[ID_EXTBKBUTTONSMOUSEOVER] : &SkinItems[ID_EXTBKBUTTONSNPRESSED]);
+
+ if (!item->IGNORED) {
+ CSkin::SkinDrawBG(tabdat->hwnd, tabdat->pContainer->hwnd, tabdat->pContainer, rcItem, dc);
+ CSkin::DrawItem(dc, rcItem, item);
+ } else
+ goto b_nonskinned;
+ } else {
+b_nonskinned:
+ if (nHint & HINT_ACTIVE_ITEM)
+ DrawEdge(dc, rcItem, EDGE_ETCHED, BF_RECT | BF_SOFT);
+ else if (nHint & HINT_HOTTRACK)
+ DrawEdge(dc, rcItem, EDGE_BUMP, BF_RECT | BF_MONO | BF_SOFT);
+ else
+ DrawEdge(dc, rcItem, EDGE_RAISED, BF_RECT | BF_SOFT);
+ }
+ } else {
+ if(M->isAero() && !(dwStyle & TCS_BOTTOM))
+ FillRect(dc, rcItem, CSkin::m_BrushBack);
+ else
+ CSkin::FillBack(dc, rcItem);
+ CMimAPI::m_pfnDrawThemeBackground(tabdat->hThemeButton, dc, 1, nHint & HINT_ACTIVE_ITEM ? 3 : (nHint & HINT_HOTTRACK ? 2 : 1), rcItem, rcItem);
+ }
+ return;
+ }
+ SelectObject(dc, PluginConfig.tabConfig.m_hPenLight);
+
+ if (nHint & HINT_ACTIVE_ITEM) {
+ if (dwStyle & TCS_BOTTOM) {
+ if (!CSkin::m_skinEnabled)
+ CSkin::FillBack(dc, rcItem);
+ rcItem->bottom += 2;
+ } else {
+ rcItem->bottom += 2;
+ if (!CSkin::m_skinEnabled)
+ CSkin::FillBack(dc, rcItem);
+ rcItem->bottom--;
+ rcItem->top -= 2;
+ }
+ if (CSkin::m_skinEnabled) {
+ CSkinItem *item = &SkinItems[dwStyle & TCS_BOTTOM ? ID_EXTBKTABITEMACTIVEBOTTOM : ID_EXTBKTABITEMACTIVE];
+ if (!item->IGNORED) {
+ rcItem->left += item->MARGIN_LEFT;
+ rcItem->right -= item->MARGIN_RIGHT;
+ rcItem->top += item->MARGIN_TOP;
+ rcItem->bottom -= item->MARGIN_BOTTOM;
+ CSkin::DrawItem(dc, rcItem, item);
+ return;
+ }
+ }
+ }
+ if (CSkin::m_skinEnabled) {
+ CSkinItem *item = &SkinItems[dwStyle & TCS_BOTTOM ? (nHint & HINT_HOTTRACK ? ID_EXTBKTABITEMHOTTRACKBOTTOM : ID_EXTBKTABITEMBOTTOM) :
+ (nHint & HINT_HOTTRACK ? ID_EXTBKTABITEMHOTTRACK : ID_EXTBKTABITEM)];
+ if (!item->IGNORED) {
+ if (dwStyle & TCS_BOTTOM)
+ rcItem->top = (rcItem->top > rcTabPage.bottom + 5) ? --rcItem->top : rcItem->top;
+ else
+ rcItem->bottom++;
+ //rcItem->bottom = (rcItem->bottom < rcTabPage.top - 5) ? ++rcItem->bottom : rcItem->bottom;
+
+ rcItem->left += item->MARGIN_LEFT;
+ rcItem->right -= item->MARGIN_RIGHT;
+ CSkin::DrawItem(dc, rcItem, item);
+ return;
+ }
+ }
+ if (dwStyle & TCS_BOTTOM) {
+ MoveToEx(dc, rcItem->left, rcItem->top - (nHint & HINT_ACTIVE_ITEM ? 1 : 0), &pt);
+ LineTo(dc, rcItem->left, rcItem->bottom - 2);
+ LineTo(dc, rcItem->left + 2, rcItem->bottom);
+ SelectObject(dc, PluginConfig.tabConfig.m_hPenShadow);
+ LineTo(dc, rcItem->right - 3, rcItem->bottom);
+
+ LineTo(dc, rcItem->right - 1, rcItem->bottom - 2);
+ LineTo(dc, rcItem->right - 1, rcItem->top - 1);
+ MoveToEx(dc, rcItem->right - 2, rcItem->top, &pt);
+ SelectObject(dc, PluginConfig.tabConfig.m_hPenItemShadow);
+ LineTo(dc, rcItem->right - 2, rcItem->bottom - 1);
+ MoveToEx(dc, rcItem->right - 3, rcItem->bottom - 1, &pt);
+ LineTo(dc, rcItem->left + 2, rcItem->bottom - 1);
+ } else {
+ MoveToEx(dc, rcItem->left, rcItem->bottom, &pt);
+ LineTo(dc, rcItem->left, rcItem->top + 2);
+ LineTo(dc, rcItem->left + 2, rcItem->top);
+ LineTo(dc, rcItem->right - 2, rcItem->top);
+ SelectObject(dc, PluginConfig.tabConfig.m_hPenItemShadow);
+
+ MoveToEx(dc, rcItem->right - 2, rcItem->top + 1, &pt);
+ LineTo(dc, rcItem->right - 2, rcItem->bottom + 1);
+ SelectObject(dc, PluginConfig.tabConfig.m_hPenShadow);
+ MoveToEx(dc, rcItem->right - 1, rcItem->top + 2, &pt);
+ LineTo(dc, rcItem->right - 1, rcItem->bottom + 1);
+ }
+ }
+}
+
+static int DWordAlign(int n)
+{
+ int rem = n % 4;
+ if (rem)
+ n += (4 - rem);
+ return n;
+}
+
+static HRESULT DrawThemesPartWithAero(const TabControlData *tabdat, HDC hDC, int iPartId, int iStateId, LPRECT prcBox, TWindowData* dat)
+{
+ HRESULT hResult = 0;
+ bool fAero = M->isAero();
+
+ if(tabdat->fAeroTabs) {
+ if(tabdat->dwStyle & TCS_BOTTOM)
+ prcBox->top += (fAero ? 2 : iStateId == PBS_PRESSED ? (M->isVSThemed() ? 1 : -1) : 0);
+ else if (!fAero)
+ prcBox->bottom -= (iStateId == PBS_PRESSED ? (M->isVSThemed() ? 1 : -1) : 0);
+
+ if(fAero)
+ FillRect(hDC, prcBox, CSkin::m_BrushBack);
+ else if(dat)
+ FillTabBackground(hDC, iStateId, dat, prcBox);
+
+ tabdat->helperItem->setAlphaFormat(AC_SRC_ALPHA, iStateId == PBS_PRESSED ? 255 : (fAero ? 240 : 255));
+ tabdat->helperItem->Render(hDC, prcBox, true);
+ tabdat->helperGlowItem->setAlphaFormat(AC_SRC_ALPHA, iStateId == PBS_PRESSED ? 220 : 180);
+
+ if(iStateId != PBS_NORMAL)
+ tabdat->helperGlowItem->Render(hDC, prcBox, true);
+ }
+ else if(CMimAPI::m_pfnDrawThemeBackground) {
+ if (tabdat->hTheme != 0)
+ hResult = CMimAPI::m_pfnDrawThemeBackground(tabdat->hTheme, hDC, iPartId, iStateId, prcBox, NULL);
+ }
+
+ return hResult;
+}
+/*
+ * draws a theme part (identifier in uiPartNameID) using the given clipping rectangle
+ */
+
+static HRESULT DrawThemesPart(const TabControlData *tabdat, HDC hDC, int iPartId, int iStateId, LPRECT prcBox)
+{
+ HRESULT hResult = 0;
+
+ if (CMimAPI::m_pfnDrawThemeBackground == 0)
+ return 0;
+
+ if (tabdat->hTheme != 0)
+ hResult = CMimAPI::m_pfnDrawThemeBackground(tabdat->hTheme, hDC, iPartId, iStateId, prcBox, NULL);
+
+ return hResult;
+}
+
+/*
+ * draw a themed tab item. either a tab or the body pane
+ * handles image mirroring for tabs at the bottom
+ */
+
+static void DrawThemesXpTabItem(HDC pDC, int ixItem, RECT *rcItem, UINT uiFlag, struct TabControlData *tabdat, TWindowData* dat)
+{
+ BOOL bBody = (uiFlag & 1) ? TRUE : FALSE;
+ BOOL bSel = (uiFlag & 2) ? TRUE : FALSE;
+ BOOL bHot = (uiFlag & 4) ? TRUE : FALSE;
+ BOOL bBottom = (uiFlag & 8) ? TRUE : FALSE; // mirror
+ SIZE szBmp;
+ HDC dcMem;
+ HBITMAP bmpMem, pBmpOld;
+ RECT rcMem;
+ BITMAPINFO biOut;
+ BITMAPINFOHEADER *bihOut;
+ int nBmpWdtPS;
+ int nSzBuffPS;
+ LPBYTE pcImg = NULL, pcImg1 = NULL;
+ int nStart = 0, nLenSub = 0;
+ szBmp.cx = rcItem->right - rcItem->left;
+ szBmp.cy = rcItem->bottom - rcItem->top;
+
+ /*
+ * for top row tabs, it's easy. Just draw to the provided dc (it's a mem dc already)
+ */
+
+ if (!bBottom) {
+ if (bBody) {
+ if(PluginConfig.m_bIsVista) {
+ rcItem->right += 2; // hide right tab sheet shadow (only draw the actual border line)
+ rcItem->bottom += 1;
+ }
+ DrawThemesPart(tabdat, pDC, 9, 0, rcItem); // TABP_PANE id = 9
+ } else {
+ int iStateId = bSel ? 3 : (bHot ? 2 : 1); // leftmost item has different part id
+ DrawThemesPartWithAero(tabdat, pDC, rcItem->left < 20 ? 2 : 1, iStateId, rcItem, dat);
+ }
+ return;
+ }
+ else if(tabdat->fAeroTabs && !bBody) {
+ int iStateId = bSel ? 3 : (bHot ? 2 : 1); // leftmost item has different part id
+ DrawThemesPartWithAero(tabdat, pDC, rcItem->left < 20 ? 2 : 1, iStateId, rcItem, dat);
+ return;
+ }
+
+ /*
+ * remaining code is for bottom tabs only.
+ */
+
+ dcMem = CreateCompatibleDC(pDC);
+ bmpMem = CreateCompatibleBitmap(pDC, szBmp.cx, szBmp.cy);
+
+ pBmpOld = (HBITMAP)SelectObject(dcMem, bmpMem);
+
+ rcMem.left = rcMem.top = 0;
+ rcMem.right = szBmp.cx;
+ rcMem.bottom = szBmp.cy;
+
+ ZeroMemory(&biOut, sizeof(BITMAPINFO)); // Fill local pixel arrays
+ bihOut = &biOut.bmiHeader;
+
+ bihOut->biSize = sizeof(BITMAPINFOHEADER);
+ bihOut->biCompression = BI_RGB;
+ bihOut->biPlanes = 1;
+ bihOut->biBitCount = 24; // force as RGB: 3 bytes, 24 bits
+ bihOut->biWidth = szBmp.cx;
+ bihOut->biHeight = szBmp.cy;
+
+ nBmpWdtPS = DWordAlign(szBmp.cx * 3);
+ nSzBuffPS = ((nBmpWdtPS * szBmp.cy) / 8 + 2) * 8;
+
+ /*
+ * blit the background to the memory dc, so that transparent tabs will draw properly
+ * for bottom tabs, it's more complex, because the background part must not be mirrored
+ * the body part does not need that (filling with the background color is much faster
+ * and sufficient for the tab "page" part.
+ */
+
+ if (!bSel)
+ CSkin::FillBack(dcMem, &rcMem);
+ else {
+ /*
+ * mirror the background horizontally for bottom selected tabs (they can overwrite others)
+ * needed, because after drawing the theme part the images will again be mirrored
+ * to "flip" the tab item.
+ */
+ BitBlt(dcMem, 0, 0, szBmp.cx, szBmp.cy, pDC, rcItem->left, rcItem->top, SRCCOPY);
+
+ pcImg1 = (BYTE *)mir_alloc(nSzBuffPS);
+
+ if (pcImg1) {
+ GetDIBits(pDC, bmpMem, nStart, szBmp.cy - nLenSub, pcImg1, &biOut, DIB_RGB_COLORS);
+ bihOut->biHeight = -szBmp.cy; // to mirror bitmap is eough to use negative height between Get/SetDIBits
+ SetDIBits(pDC, bmpMem, nStart, szBmp.cy - nLenSub, pcImg1, &biOut, DIB_RGB_COLORS);
+ mir_free(pcImg1);
+ }
+ }
+
+ /*
+ * body may be *large* so rotating the final image can be very slow.
+ * workaround: draw the skin item (tab pane) into a small dc, rotate this (small) image and render
+ * it to the final DC with the IMG_RenderItem() routine.
+ */
+
+ if (bBody) {
+ HDC hdcTemp = CreateCompatibleDC(pDC);
+ HBITMAP hbmTemp = CreateCompatibleBitmap(pDC, 100, 50);
+ HBITMAP hbmTempOld = (HBITMAP)SelectObject(hdcTemp, hbmTemp);
+ RECT rcTemp = {0};
+
+ rcTemp.right = 100;
+ rcTemp.bottom = 50;
+
+ bihOut->biWidth = 100;
+ bihOut->biHeight = 50;
+
+ nBmpWdtPS = DWordAlign(100 * 3);
+ nSzBuffPS = ((nBmpWdtPS * 50) / 8 + 2) * 8;
+
+ CSkin::FillBack(hdcTemp, &rcTemp);
+ DrawThemesPart(tabdat, hdcTemp, 9, 0, &rcTemp); // TABP_PANE id = 9
+ pcImg = (BYTE *)mir_alloc(nSzBuffPS);
+ if (pcImg) { // get bits:
+ GetDIBits(hdcTemp, hbmTemp, nStart, 50 - nLenSub, pcImg, &biOut, DIB_RGB_COLORS);
+ bihOut->biHeight = -50;
+ SetDIBits(hdcTemp, hbmTemp, nStart, 50 - nLenSub, pcImg, &biOut, DIB_RGB_COLORS);
+ mir_free(pcImg);
+ }
+ CImageItem tempItem(10, 10, 10, 10, hdcTemp, 0, IMAGE_FLAG_DIVIDED | IMAGE_FILLSOLID,
+ GetSysColorBrush(COLOR_3DFACE), 255, 30, 80, 50, 100);
+
+ if(PluginConfig.m_bIsVista) { // hide right tab sheet shadow (only draw the actual border line)
+ rcItem->right += 2;
+ }
+
+ tempItem.Render(pDC, rcItem, true);
+ tempItem.Clear();
+ SelectObject(hdcTemp, hbmTempOld);
+ DeleteObject(hbmTemp);
+ DeleteDC(hdcTemp);
+
+ SelectObject(dcMem, pBmpOld);
+ DeleteObject(bmpMem);
+ DeleteDC(dcMem);
+ return;
+ } else {
+ int iStateId = bSel ? 3 : (bHot ? 2 : 1);
+ DrawThemesPart(tabdat, dcMem, rcItem->left < 20 ? 2 : 1, iStateId, &rcMem);
+ }
+
+ bihOut->biHeight = szBmp.cy;
+ pcImg = (BYTE *)mir_alloc(nSzBuffPS);
+
+ if (pcImg) { // get bits:
+ GetDIBits(pDC, bmpMem, nStart, szBmp.cy - nLenSub, pcImg, &biOut, DIB_RGB_COLORS);
+ bihOut->biHeight = -szBmp.cy;
+ SetDIBits(pDC, bmpMem, nStart, szBmp.cy - nLenSub, pcImg, &biOut, DIB_RGB_COLORS);
+ mir_free(pcImg);
+ }
+
+ /*
+ * finally, blit the result to the destination dc
+ */
+
+ BitBlt(pDC, rcItem->left, rcItem->top, szBmp.cx, szBmp.cy, dcMem, 0, 0, SRCCOPY);
+ SelectObject(dcMem, pBmpOld);
+ DeleteObject(bmpMem);
+ DeleteDC(dcMem);
+}
+
+static POINT ptMouseT = {0};
+
+static LRESULT CALLBACK TabControlSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TabControlData *tabdat = 0;
+
+ tabdat = (struct TabControlData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (tabdat) {
+ if (tabdat->pContainer == NULL)
+ tabdat->pContainer = (TContainerData *)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA);
+ tabdat->dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
+ }
+
+ switch (msg) {
+ case WM_NCCREATE: {
+ WNDCLASSEXA wcl = {0};
+
+ wcl.cbSize = sizeof(wcl);
+ GetClassInfoExA(g_hInst, "SysTabControl32", &wcl);
+
+ tabdat = (struct TabControlData *)mir_alloc(sizeof(struct TabControlData));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)tabdat);
+ ZeroMemory((void *)tabdat, sizeof(struct TabControlData));
+ tabdat->hwnd = hwnd;
+ tabdat->cx = GetSystemMetrics(SM_CXSMICON);
+ tabdat->cy = GetSystemMetrics(SM_CYSMICON);
+ tabdat->fTipActive = FALSE;
+ tabdat->iHoveredCloseIcon = -1;
+ SendMessage(hwnd, EM_THEMECHANGED, 0, 0);
+ OldTabControlClassProc = wcl.lpfnWndProc;
+ return TRUE;
+ }
+ case EM_THEMECHANGED:
+ tabdat->m_xpad = M->GetByte("x-pad", 3);
+ tabdat->m_VisualStyles = FALSE;
+ if (PluginConfig.m_bIsXP && M->isVSAPIState()) {
+ if (CMimAPI::m_pfnIsThemeActive != 0)
+ if (CMimAPI::m_pfnIsThemeActive()) {
+ tabdat->m_VisualStyles = TRUE;
+ if (tabdat->hTheme != 0 && CMimAPI::m_pfnCloseThemeData != 0) {
+ CMimAPI::m_pfnCloseThemeData(tabdat->hTheme);
+ CMimAPI::m_pfnCloseThemeData(tabdat->hThemeButton);
+ }
+ if (CMimAPI::m_pfnOpenThemeData != 0) {
+ if ((tabdat->hTheme = CMimAPI::m_pfnOpenThemeData(hwnd, L"TAB")) == 0 || (tabdat->hThemeButton = CMimAPI::m_pfnOpenThemeData(hwnd, L"BUTTON")) == 0)
+ tabdat->m_VisualStyles = FALSE;
+ }
+ }
+ }
+ return 0;
+ case EM_SEARCHSCROLLER: {
+ HWND hwndChild;
+ /*
+ * search the updown control (scroll arrows) to subclass it...
+ * the control is dynamically created and may not exist as long as it is
+ * not needed. So we have to search it everytime we need to paint. However,
+ * it is sufficient to search it once. So this message is called, whenever
+ * a new tab is inserted
+ */
+
+ if ((hwndChild = FindWindowEx(hwnd, 0, _T("msctls_updown32"), NULL)) != 0)
+ DestroyWindow(hwndChild);
+ return 0;
+ }
+ case EM_VALIDATEBOTTOM: {
+ BOOL bClassicDraw = (tabdat->m_VisualStyles == FALSE);
+ if ((tabdat->dwStyle & TCS_BOTTOM) && !bClassicDraw && PluginConfig.tabConfig.m_bottomAdjust != 0)
+ InvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+ case EM_REFRESHWITHOUTCLIP:
+ if (TabCtrl_GetItemCount(hwnd) > 1)
+ return 0;
+ else {
+ tabdat->bRefreshWithoutClip = TRUE;
+ RedrawWindow(hwnd, NULL, NULL, RDW_UPDATENOW | RDW_NOCHILDREN | RDW_INVALIDATE);
+ tabdat->bRefreshWithoutClip = FALSE;
+ return 0;
+ }
+ case TCM_INSERTITEM:
+ case TCM_DELETEITEM:
+ tabdat->iHoveredCloseIcon = -1;
+ if (!(tabdat->dwStyle & TCS_MULTILINE) || tabdat->dwStyle & TCS_BUTTONS) {
+ LRESULT result;
+ RECT rc;
+ int iTabs = TabCtrl_GetItemCount(hwnd);
+ if (iTabs >= 1 && msg == TCM_INSERTITEM) {
+ TabCtrl_GetItemRect(hwnd, 0, &rc);
+ TabCtrl_SetItemSize(hwnd, 10, rc.bottom - rc.top);
+ }
+ result = CallWindowProc(OldTabControlClassProc, hwnd, msg, wParam, lParam);
+ TabCtrl_GetItemRect(hwnd, 0, &rc);
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ return result;
+ }
+ break;
+ case WM_DESTROY:
+ if (tabdat) {
+ if (tabdat->hTheme != 0 && CMimAPI::m_pfnCloseThemeData != 0) {
+ CMimAPI::m_pfnCloseThemeData(tabdat->hTheme);
+ CMimAPI::m_pfnCloseThemeData(tabdat->hThemeButton);
+ }
+ }
+ break;
+ case WM_NCDESTROY:
+ if(tabdat) {
+ mir_free(tabdat);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0L);
+ }
+ break;
+ case WM_MBUTTONDOWN: {
+ POINT pt;
+ GetCursorPos(&pt);
+ SendMessage(GetParent(hwnd), DM_CLOSETABATMOUSE, 0, (LPARAM)&pt);
+ return 1;
+ }
+ case WM_SETCURSOR: {
+ POINT pt;
+
+ GetCursorPos(&pt);
+ SendMessage(GetParent(hwnd), msg, wParam, lParam);
+ if (abs(pt.x - ptMouseT.x) < 4 && abs(pt.y - ptMouseT.y) < 4)
+ return 1;
+ ptMouseT = pt;
+ if (tabdat->fTipActive) {
+ KillTimer(hwnd, TIMERID_HOVER_T);
+ CallService("mToolTip/HideTip", 0, 0);
+ tabdat->fTipActive = FALSE;
+ }
+ KillTimer(hwnd, TIMERID_HOVER_T);
+ if(tabdat->pContainer && (!tabdat->pContainer->SideBar->isActive() && (TabCtrl_GetItemCount(hwnd) > 1 || !(tabdat->pContainer->dwFlags & CNT_HIDETABS))))
+ SetTimer(hwnd, TIMERID_HOVER_T, 750, 0);
+ break;
+ }
+ case WM_SIZE: {
+ int iTabs = TabCtrl_GetItemCount(hwnd);
+
+ if (!tabdat->pContainer)
+ break;
+
+ if (!(tabdat->dwStyle & TCS_MULTILINE)) {
+ RECT rcClient, rc;
+ DWORD newItemSize;
+ if (iTabs > (tabdat->pContainer->dwFlags & CNT_HIDETABS ? 1 : 0)) {
+ GetClientRect(hwnd, &rcClient);
+ TabCtrl_GetItemRect(hwnd, iTabs - 1, &rc);
+ newItemSize = (rcClient.right - 6) - (tabdat->dwStyle & TCS_BUTTONS ? (iTabs) * 10 : 0);
+ newItemSize = newItemSize / iTabs;
+ if (newItemSize < PluginConfig.tabConfig.m_fixedwidth) {
+ TabCtrl_SetItemSize(hwnd, newItemSize, rc.bottom - rc.top);
+ } else {
+ TabCtrl_SetItemSize(hwnd, PluginConfig.tabConfig.m_fixedwidth, rc.bottom - rc.top);
+ }
+ SendMessage(hwnd, EM_SEARCHSCROLLER, 0, 0);
+ }
+ } else if (tabdat->dwStyle & TCS_BUTTONS && iTabs > 0) {
+ RECT rcClient, rcItem;
+ int nrTabsPerLine;
+ GetClientRect(hwnd, &rcClient);
+ TabCtrl_GetItemRect(hwnd, 0, &rcItem);
+ nrTabsPerLine = (rcClient.right) / PluginConfig.tabConfig.m_fixedwidth;
+ if (iTabs >= nrTabsPerLine && nrTabsPerLine > 0)
+ TabCtrl_SetItemSize(hwnd, ((rcClient.right - 6) / nrTabsPerLine) - (tabdat->dwStyle & TCS_BUTTONS ? 8 : 0), rcItem.bottom - rcItem.top);
+ else
+ TabCtrl_SetItemSize(hwnd, PluginConfig.tabConfig.m_fixedwidth, rcItem.bottom - rcItem.top);
+ }
+ break;
+ }
+ case WM_LBUTTONDBLCLK: {
+ POINT pt;
+ GetCursorPos(&pt);
+ SendMessage(GetParent(hwnd), DM_CLOSETABATMOUSE, 0, (LPARAM)&pt);
+ break;
+ }
+ case WM_RBUTTONDOWN:
+ KillTimer(hwnd, TIMERID_HOVER_T);
+ CallService("mToolTip/HideTip", 0, 0);
+ tabdat->fTipActive = FALSE;
+ break;
+
+ case WM_LBUTTONDOWN: {
+ TCHITTESTINFO tci = {0};
+
+ KillTimer(hwnd, TIMERID_HOVER_T);
+ CallService("mToolTip/HideTip", 0, 0);
+ tabdat->fTipActive = FALSE;
+
+ if (GetKeyState(VK_CONTROL) & 0x8000) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) {
+ int i;
+ tci.flags = TCHT_ONITEM;
+
+ ScreenToClient(hwnd, &tci.pt);
+ i = TabCtrl_HitTest(hwnd, &tci);
+ if (i != -1) {
+ TCITEM tc;
+ struct TWindowData *dat = NULL;
+
+ tc.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwnd, i, &tc);
+ dat = (struct TWindowData *)GetWindowLongPtr((HWND)tc.lParam, GWLP_USERDATA);
+ if (dat) {
+ tabdat->bDragging = TRUE;
+ tabdat->iBeginIndex = i;
+ tabdat->hwndDrag = (HWND)tc.lParam;
+ tabdat->dragDat = dat;
+ tabdat->fSavePos = TRUE;
+ tabdat->himlDrag = ImageList_Create(16, 16, ILC_MASK | (PluginConfig.m_bIsXP ? ILC_COLOR32 : ILC_COLOR16), 1, 0);
+ ImageList_AddIcon(tabdat->himlDrag, dat->hTabIcon);
+ ImageList_BeginDrag(tabdat->himlDrag, 0, 8, 8);
+ ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y);
+ SetCapture(hwnd);
+ }
+ return TRUE;
+ }
+ }
+ }
+
+ if (GetKeyState(VK_MENU) & 0x8000) {
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) {
+ int i;
+ tci.flags = TCHT_ONITEM;
+
+ ScreenToClient(hwnd, &tci.pt);
+ i = TabCtrl_HitTest(hwnd, &tci);
+ if (i != -1) {
+ TCITEM tc;
+ TWindowData *dat = NULL;
+
+ tc.mask = TCIF_PARAM;
+ TabCtrl_GetItem(hwnd, i, &tc);
+ dat = (TWindowData *)GetWindowLongPtr((HWND)tc.lParam, GWLP_USERDATA);
+ if (dat) {
+ tabdat->bDragging = TRUE;
+ tabdat->iBeginIndex = i;
+ tabdat->hwndDrag = (HWND)tc.lParam;
+ tabdat->dragDat = dat;
+ tabdat->himlDrag = ImageList_Create(16, 16, ILC_MASK | (PluginConfig.m_bIsXP ? ILC_COLOR32 : ILC_COLOR16), 1, 0);
+ tabdat->fSavePos = FALSE;
+ ImageList_AddIcon(tabdat->himlDrag, dat->hTabIcon);
+ ImageList_BeginDrag(tabdat->himlDrag, 0, 8, 8);
+ ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y);
+ SetCapture(hwnd);
+ }
+ return TRUE;
+ }
+ }
+ }
+ if(tabdat->fCloseButton) {
+ POINT pt;
+ GetCursorPos(&pt);
+
+ if(TabCtrl_TestForCloseButton(tabdat, hwnd, pt) != -1)
+ return(TRUE);
+ }
+ }
+ break;
+
+ case WM_CAPTURECHANGED: {
+ tabdat->bDragging = FALSE;
+ ImageList_DragLeave(hwnd);
+ ImageList_EndDrag();
+ if (tabdat->himlDrag) {
+ ImageList_RemoveAll(tabdat->himlDrag);
+ ImageList_Destroy(tabdat->himlDrag);
+ tabdat->himlDrag = 0;
+ }
+ }
+ break;
+
+ case WM_MOUSEMOVE: {
+ if (tabdat->bDragging) {
+ TCHITTESTINFO tci = {0};
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(hwnd, &tci.pt);
+ ImageList_DragMove(tci.pt.x, tci.pt.y);
+ }
+ if(tabdat->fCloseButton) {
+ POINT pt;
+
+ GetCursorPos(&pt);
+ int iOldHovered = tabdat->iHoveredCloseIcon;
+ tabdat->iHoveredCloseIcon = TabCtrl_TestForCloseButton(tabdat, hwnd, pt);
+ if(tabdat->iHoveredCloseIcon != iOldHovered)
+ InvalidateRect(hwnd, NULL, FALSE);
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP: {
+ CallWindowProc(OldTabControlClassProc, hwnd, msg, wParam, lParam);
+ if (tabdat->bDragging && ReleaseCapture()) {
+ TCHITTESTINFO tci = {0};
+ int i;
+ tci.pt.x = (short)LOWORD(GetMessagePos());
+ tci.pt.y = (short)HIWORD(GetMessagePos());
+ tci.flags = TCHT_ONITEM;
+ tabdat->bDragging = FALSE;
+ ImageList_DragLeave(hwnd);
+ ImageList_EndDrag();
+
+ ScreenToClient(hwnd, &tci.pt);
+ i = TabCtrl_HitTest(hwnd, &tci);
+ if (i != -1 && i != tabdat->iBeginIndex)
+ RearrangeTab(tabdat->hwndDrag, tabdat->dragDat, MAKELONG(i, 0xffff), tabdat->fSavePos);
+ tabdat->hwndDrag = (HWND) - 1;
+ tabdat->dragDat = NULL;
+ if (tabdat->himlDrag) {
+ ImageList_RemoveAll(tabdat->himlDrag);
+ ImageList_Destroy(tabdat->himlDrag);
+ tabdat->himlDrag = 0;
+ }
+ }
+ if(tabdat->fCloseButton) {
+ POINT pt;
+
+ GetCursorPos(&pt);
+ int iItem = TabCtrl_TestForCloseButton(tabdat, hwnd, pt);
+ if(iItem != -1)
+ SendMessage(GetParent(hwnd), DM_CLOSETABATMOUSE, 0, (LPARAM)&pt);
+ }
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ if (tabdat->pContainer && (CSkin::m_skinEnabled || M->isAero()))
+ return TRUE;
+ return(0);
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hdcreal, hdc;
+ RECT rectTemp, rctPage, rctActive, rcItem, rctClip, rctOrig;
+ RECT rectUpDn = {0, 0, 0, 0};
+ int nCount = TabCtrl_GetItemCount(hwnd), i;
+ TCITEM item = {0};
+ int iActive, hotItem;
+ POINT pt;
+ DWORD dwStyle = tabdat->dwStyle;
+ HPEN hPenOld = 0;
+ UINT uiFlags = 1;
+ UINT uiBottom = 0;
+ TCHITTESTINFO hti;
+ HBITMAP bmpMem, bmpOld;
+ DWORD cx, cy;
+ bool isAero = M->isAero();
+ HANDLE hpb = 0;
+ BOOL bClassicDraw = !isAero && (tabdat->m_VisualStyles == FALSE || CSkin::m_skinEnabled);
+ if(GetUpdateRect(hwnd, NULL, TRUE) == 0)
+ break;
+
+ item.mask = TCIF_PARAM;
+
+ tabdat->fAeroTabs = (CSkin::m_fAeroSkinsValid && (isAero || PluginConfig.m_fillColor)) ? TRUE : FALSE;
+ tabdat->fCloseButton = tabdat->pContainer ? (tabdat->pContainer->dwFlagsEx & TCF_CLOSEBUTTON ? TRUE : FALSE) : FALSE;
+
+ tabdat->helperDat = 0;
+
+ if(tabdat->fAeroTabs && tabdat->pContainer) {
+ TWindowData *dat = (TWindowData *)GetWindowLongPtr(tabdat->pContainer->hwndActive, GWLP_USERDATA);
+ if(dat) {
+ tabdat->helperDat = dat;
+ }
+ else
+ tabdat->fAeroTabs = 0;
+
+ tabdat->helperItem = (dwStyle & TCS_BOTTOM) ? CSkin::m_tabBottom : CSkin::m_tabTop;
+ tabdat->helperGlowItem = (dwStyle & TCS_BOTTOM) ? CSkin::m_tabGlowBottom : CSkin::m_tabGlowTop;
+ }
+ else
+ tabdat->fAeroTabs = FALSE;
+
+ hdcreal = BeginPaint(hwnd, &ps);
+
+ /*
+ * switchbar is active, don't paint a single pixel, the tab control won't be visible at all
+ * same when we have only ONE tab and do not want it to be visible because of the container
+ * option "Show tab bar only when needed".
+ */
+
+ if((tabdat->pContainer->dwFlags & CNT_SIDEBAR) || (nCount == 1 && tabdat->pContainer->dwFlags & CNT_HIDETABS)) {
+ if(nCount == 0)
+ FillRect(hdcreal, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE)); // avoid flickering/ugly black background during container creation
+ EndPaint(hwnd, &ps);
+ return(0);
+ }
+
+ GetClientRect(hwnd, &rctPage);
+ rctOrig = rctPage;
+ iActive = TabCtrl_GetCurSel(hwnd);
+ TabCtrl_GetItemRect(hwnd, iActive, &rctActive);
+ cx = rctPage.right - rctPage.left;
+ cy = rctPage.bottom - rctPage.top;
+
+ /*
+ * draw everything to a memory dc to avoid flickering
+ */
+
+ if(CMimAPI::m_haveBufferedPaint)
+ hpb = tabdat->hbp = CSkin::InitiateBufferedPaint(hdcreal, rctPage, hdc);
+ else {
+ hdc = CreateCompatibleDC(hdcreal);
+ bmpMem = tabdat->fAeroTabs ? CSkin::CreateAeroCompatibleBitmap(rctPage, hdcreal) : CreateCompatibleBitmap(hdcreal, cx, cy);
+ bmpOld = (HBITMAP)SelectObject(hdc, bmpMem);
+ }
+
+ if (nCount == 1 && tabdat->pContainer->dwFlags & CNT_HIDETABS)
+ rctClip = rctPage;
+
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(hwnd, tabdat->pContainer->hwnd, tabdat->pContainer, &rctPage, hdc);
+ else
+ CSkin::FillBack(hdc, &rctPage);
+
+ if (dwStyle & TCS_BUTTONS) {
+ RECT rc1;
+ TabCtrl_GetItemRect(hwnd, nCount - 1, &rc1);
+ if (dwStyle & TCS_BOTTOM) {
+ rctPage.bottom = rc1.top;
+ uiBottom = 8;
+ } else {
+ rctPage.top = rc1.bottom + 2;
+ uiBottom = 0;
+ }
+ } else {
+ if (dwStyle & TCS_BOTTOM) {
+ rctPage.bottom = rctActive.top;
+ uiBottom = 8;
+ } else {
+ rctPage.top = rctActive.bottom;
+ uiBottom = 0;
+ }
+ }
+
+ if (nCount > 1 || !(tabdat->pContainer->dwFlags & CNT_HIDETABS)) {
+ rctClip = rctPage;
+ InflateRect(&rctClip, -tabdat->pContainer->tBorder, -tabdat->pContainer->tBorder);
+ }
+ else
+ ZeroMemory(&rctClip, sizeof(RECT));
+
+ hPenOld = (HPEN)SelectObject(hdc, PluginConfig.tabConfig.m_hPenLight);
+ /*
+ * visual style support
+ */
+
+ CopyRect(&rcTabPage, &rctPage);
+ if (!tabdat->bRefreshWithoutClip)
+ ExcludeClipRect(hdc, rctClip.left, rctClip.top, rctClip.right, rctClip.bottom);
+ else
+ ZeroMemory(&rctClip, sizeof(RECT));
+ if ((!bClassicDraw || PluginConfig.m_fillColor) && IntersectRect(&rectTemp, &rctPage, &ps.rcPaint) && !CSkin::m_skinEnabled) {
+ RECT rcClient = rctPage;
+ if (dwStyle & TCS_BOTTOM) {
+ rcClient.bottom = rctPage.bottom;
+ uiFlags |= uiBottom;
+ } else
+ rcClient.top = rctPage.top;
+ if(PluginConfig.m_fillColor)
+ DrawCustomTabPage(hdc, rcClient);
+ else
+ DrawThemesXpTabItem(hdc, -1, &rcClient, uiFlags, tabdat, 0); // TABP_PANE=9,0,'TAB'
+ if (tabdat->bRefreshWithoutClip)
+ goto skip_tabs;
+ } else {
+ if (IntersectRect(&rectTemp, &rctPage, &ps.rcPaint)) {
+ if (CSkin::m_skinEnabled) {
+ CSkinItem *item = &SkinItems[ID_EXTBKTABPAGE];
+
+ if (!item->IGNORED) {
+ DrawAlpha(hdc, &rctPage, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT,
+ item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ goto page_done;
+ }
+ }
+
+ if (tabdat->bRefreshWithoutClip)
+ goto skip_tabs;
+
+ if (dwStyle & TCS_BUTTONS) {
+ rectTemp = rctPage;
+ if (dwStyle & TCS_BOTTOM) {
+ rectTemp.top--;
+ rectTemp.bottom--;
+ } else {
+ rectTemp.bottom--;
+ rectTemp.top++;
+ }
+ if(PluginConfig.m_fillColor)
+ DrawCustomTabPage(hdc, rectTemp);
+ else {
+ MoveToEx(hdc, rectTemp.left, rectTemp.bottom, &pt);
+ LineTo(hdc, rectTemp.left, rectTemp.top + 1);
+ LineTo(hdc, rectTemp.right - 1, rectTemp.top + 1);
+ SelectObject(hdc, PluginConfig.tabConfig.m_hPenShadow);
+ LineTo(hdc, rectTemp.right - 1, rectTemp.bottom);
+ LineTo(hdc, rectTemp.left, rectTemp.bottom);
+ }
+ } else {
+ rectTemp = rctPage;
+ if(PluginConfig.m_fillColor)
+ DrawCustomTabPage(hdc, rectTemp);
+ else {
+ MoveToEx(hdc, rectTemp.left, rectTemp.bottom - 1, &pt);
+ LineTo(hdc, rectTemp.left, rectTemp.top);
+
+ if (dwStyle & TCS_BOTTOM) {
+ LineTo(hdc, rectTemp.right - 1, rectTemp.top);
+ SelectObject(hdc, PluginConfig.tabConfig.m_hPenShadow);
+ LineTo(hdc, rectTemp.right - 1, rectTemp.bottom - 1);
+ LineTo(hdc, rctActive.right, rectTemp.bottom - 1);
+ MoveToEx(hdc, rctActive.left - 2, rectTemp.bottom - 1, &pt);
+ LineTo(hdc, rectTemp.left - 1, rectTemp.bottom - 1);
+ SelectObject(hdc, PluginConfig.tabConfig.m_hPenItemShadow);
+ MoveToEx(hdc, rectTemp.right - 2, rectTemp.top + 1, &pt);
+ LineTo(hdc, rectTemp.right - 2, rectTemp.bottom - 2);
+ LineTo(hdc, rctActive.right, rectTemp.bottom - 2);
+ MoveToEx(hdc, rctActive.left - 2, rectTemp.bottom - 2, &pt);
+ LineTo(hdc, rectTemp.left, rectTemp.bottom - 2);
+ } else {
+ if (rctActive.left >= 0) {
+ LineTo(hdc, rctActive.left, rctActive.bottom);
+ if (IsRectEmpty(&rectUpDn))
+ MoveToEx(hdc, rctActive.right, rctActive.bottom, &pt);
+ else {
+ if (rctActive.right >= rectUpDn.left)
+ MoveToEx(hdc, rectUpDn.left - SHIFT_FROM_CUT_TO_SPIN + 2, rctActive.bottom + 1, &pt);
+ else
+ MoveToEx(hdc, rctActive.right, rctActive.bottom, &pt);
+ }
+ LineTo(hdc, rectTemp.right - 2, rctActive.bottom);
+ } else {
+ RECT rectItemLeftmost;
+ UINT nItemLeftmost = FindLeftDownItem(hwnd);
+ TabCtrl_GetItemRect(hwnd, nItemLeftmost, &rectItemLeftmost);
+ LineTo(hdc, rectTemp.right - 2, rctActive.bottom);
+ }
+ SelectObject(hdc, PluginConfig.tabConfig.m_hPenItemShadow);
+ LineTo(hdc, rectTemp.right - 2, rectTemp.bottom - 2);
+ LineTo(hdc, rectTemp.left, rectTemp.bottom - 2);
+
+ SelectObject(hdc, PluginConfig.tabConfig.m_hPenShadow);
+ MoveToEx(hdc, rectTemp.right - 1, rctActive.bottom, &pt);
+ LineTo(hdc, rectTemp.right - 1, rectTemp.bottom - 1);
+ LineTo(hdc, rectTemp.left - 2, rectTemp.bottom - 1);
+ }
+ }
+ }
+ }
+ }
+page_done:
+ /*
+ * if aero is active _and_ the infopanel is visible in the current window, we "flatten" out the top area
+ * of the tab page by overpainting it black (thus it will appear transparent)
+ */
+ if(isAero && tabdat->helperDat) {
+ RECT rcLog, rcPage;
+ POINT pt;
+
+ GetClientRect(hwnd, &rcPage);
+ if(dwStyle & TCS_BOTTOM) {
+ GetWindowRect(tabdat->helperDat->hwnd, &rcLog);
+ pt.y = rcLog.bottom;
+ pt.x = rcLog.left;
+ ScreenToClient(hwnd, &pt);
+ rcPage.top = pt.y + ((nCount > 1 || !(tabdat->helperDat->pContainer->dwFlags & CNT_HIDETABS)) ? tabdat->helperDat->pContainer->tBorder : 0);
+ FillRect(hdc, &rcPage, CSkin::m_BrushBack);
+ rcPage.top = 0;
+ }
+ GetWindowRect(GetDlgItem(tabdat->helperDat->hwnd, tabdat->helperDat->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &rcLog);
+ pt.y = rcLog.top;
+ pt.x = rcLog.left;
+ ScreenToClient(hwnd, &pt);
+ rcPage.bottom = pt.y;
+ FillRect(hdc, &rcPage, CSkin::m_BrushBack);
+ }
+
+ uiFlags = 0;
+ /*
+ * figure out hottracked item (if any)
+ */
+
+ if (tabdat->bRefreshWithoutClip)
+ goto skip_tabs;
+
+ GetCursorPos(&hti.pt);
+ ScreenToClient(hwnd, &hti.pt);
+ hti.flags = 0;
+ hotItem = TabCtrl_HitTest(hwnd, &hti);
+ for (i = 0; i < nCount; i++) {
+ TWindowData* dat = 0;
+
+ if (i != iActive) {
+ TabCtrl_GetItem(hwnd, i, &item);
+ if(item.lParam)
+ dat = (TWindowData *)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA);
+ TabCtrl_GetItemRect(hwnd, i, &rcItem);
+ if (!bClassicDraw && uiBottom) {
+ rcItem.top -= PluginConfig.tabConfig.m_bottomAdjust;
+ rcItem.bottom -= PluginConfig.tabConfig.m_bottomAdjust;
+ }
+ if (IntersectRect(&rectTemp, &rcItem, &ps.rcPaint) || bClassicDraw) {
+ int nHint = 0;
+ if (!bClassicDraw && !(dwStyle & TCS_BUTTONS)) {
+ DrawThemesXpTabItem(hdc, i, &rcItem, uiFlags | uiBottom | (i == hotItem ? 4 : 0), tabdat, dat);
+ DrawItem(tabdat, hdc, &rcItem, nHint | (i == hotItem ? HINT_HOTTRACK : 0), i, dat);
+ } else {
+ if(tabdat->fAeroTabs && !CSkin::m_skinEnabled && !(dwStyle & TCS_BUTTONS))
+ DrawThemesPartWithAero(tabdat, hdc, 0, (i == hotItem ? PBS_HOT : PBS_NORMAL), &rcItem, dat);
+ else
+ DrawItemRect(tabdat, hdc, &rcItem, nHint | (i == hotItem ? HINT_HOTTRACK : 0), i, dat);
+ DrawItem(tabdat, hdc, &rcItem, nHint | (i == hotItem ? HINT_HOTTRACK : 0), i, dat);
+ }
+ }
+ }
+ }
+ /*
+ * draw the active item
+ */
+ if (!bClassicDraw && uiBottom) {
+ rctActive.top -= PluginConfig.tabConfig.m_bottomAdjust;
+ rctActive.bottom -= PluginConfig.tabConfig.m_bottomAdjust;
+ }
+ if (rctActive.left >= 0) {
+ TWindowData* dat = 0;
+ int nHint = 0;
+
+ rcItem = rctActive;
+ TabCtrl_GetItem(hwnd, iActive, &item);
+ if(item.lParam)
+ dat = (TWindowData *)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA);
+
+ if (!bClassicDraw && !(dwStyle & TCS_BUTTONS)) {
+ InflateRect(&rcItem, 2, 2);
+ DrawThemesXpTabItem(hdc, iActive, &rcItem, 2 | uiBottom, tabdat, dat);
+ DrawItem(tabdat, hdc, &rcItem, nHint | HINT_ACTIVE_ITEM, iActive, dat);
+ } else {
+ if (!(dwStyle & TCS_BUTTONS)) {
+ if (iActive == 0) {
+ rcItem.right += 2;
+ rcItem.left--;
+ } else
+ InflateRect(&rcItem, 2, 0);
+ }
+ if(tabdat->fAeroTabs && !CSkin::m_skinEnabled && !(dwStyle & TCS_BUTTONS)) {
+ if(dwStyle & TCS_BOTTOM)
+ rcItem.bottom+= 2;
+ else
+ rcItem.top-= 2;
+ DrawThemesPartWithAero(tabdat, hdc, 0, PBS_PRESSED, &rcItem, dat);
+ }
+ else
+ DrawItemRect(tabdat, hdc, &rcItem, HINT_ACTIVATE_RIGHT_SIDE | HINT_ACTIVE_ITEM | nHint, iActive, dat);
+ DrawItem(tabdat, hdc, &rcItem, HINT_ACTIVE_ITEM | nHint, iActive, dat);
+ }
+ }
+skip_tabs:
+ if (hPenOld)
+ SelectObject(hdc, hPenOld);
+
+ /*
+ * finally, bitblt the contents of the memory dc to the real dc
+ */
+ //if(!tabdat->pContainer->bSkinned)
+ if (!tabdat->bRefreshWithoutClip)
+ ExcludeClipRect(hdcreal, rctClip.left, rctClip.top, rctClip.right, rctClip.bottom);
+
+ if(hpb)
+ CSkin::FinalizeBufferedPaint(hpb, &rctOrig);
+ //CMimAPI::m_pfnEndBufferedPaint(hpb, TRUE);
+ else {
+ BitBlt(hdcreal, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
+ SelectObject(hdc, bmpOld);
+ DeleteObject(bmpMem);
+ DeleteDC(hdc);
+ }
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+ case WM_TIMER: {
+ if (wParam == TIMERID_HOVER_T && M->GetByte("d_tooltips", 0)) {
+ POINT pt;
+ CLCINFOTIP ti = {0};
+ ti.cbSize = sizeof(ti);
+
+ KillTimer(hwnd, TIMERID_HOVER_T);
+ GetCursorPos(&pt);
+ if (abs(pt.x - ptMouseT.x) < 5 && abs(pt.y - ptMouseT.y) < 5) {
+ TCITEM item = {0};
+ int nItem = 0;
+ struct TWindowData *dat = 0;
+
+ ti.ptCursor = pt;
+ //ScreenToClient(hwnd, &pt);
+
+ item.mask = TCIF_PARAM;
+ nItem = GetTabItemFromMouse(hwnd, &pt);
+ if (nItem >= 0 && nItem < TabCtrl_GetItemCount(hwnd)) {
+ TabCtrl_GetItem(hwnd, nItem, &item);
+ /*
+ * get the message window data for the session to which this tab item belongs
+ */
+
+ if (IsWindow((HWND)item.lParam) && item.lParam != 0)
+ dat = (struct TWindowData *)GetWindowLongPtr((HWND)item.lParam, GWLP_USERDATA);
+ if (dat) {
+ tabdat->fTipActive = TRUE;
+ ti.isGroup = 0;
+ ti.hItem = dat->hContact;
+ ti.isTreeFocused = 0;
+ CallService("mToolTip/ShowTip", 0, (LPARAM)&ti);
+ }
+ }
+ }
+ }
+ break;
+ }
+ case WM_MOUSEWHEEL: {
+ short amount = (short)(HIWORD(wParam));
+ if (lParam != -1)
+ break;
+ if (amount > 0)
+ SendMessage(GetParent(hwnd), DM_SELECTTAB, DM_SELECT_PREV, 0);
+ else if (amount < 0)
+ SendMessage(GetParent(hwnd), DM_SELECTTAB, DM_SELECT_NEXT, 0);
+ InvalidateRect(hwnd, NULL, FALSE);
+ break;
+ }
+ case WM_USER + 100: {
+ if (tabdat->fTipActive) {
+ tabdat->fTipActive = FALSE;
+ CallService("mToolTip/HideTip", 0, 0);
+ }
+ }
+ }
+ return CallWindowProc(OldTabControlClassProc, hwnd, msg, wParam, lParam);
+}
+
+/*
+ * load the tab control configuration data (colors, fonts, flags...
+ */
+
+void TSAPI ReloadTabConfig()
+{
+ NONCLIENTMETRICS nclim;
+ int i = 0;
+
+ PluginConfig.tabConfig.m_hPenLight = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
+ PluginConfig.tabConfig.m_hPenShadow = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW));
+ PluginConfig.tabConfig.m_hPenItemShadow = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
+
+ nclim.cbSize = sizeof(nclim);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nclim, 0);
+ PluginConfig.tabConfig.m_hMenuFont = CreateFontIndirect(&nclim.lfMessageFont);
+
+ while (tabcolors[i].szKey != NULL) {
+ PluginConfig.tabConfig.colors[i] = M->GetDword(CSkin::m_skinEnabled ? tabcolors[i].szSkinnedKey : tabcolors[i].szKey, GetSysColor(tabcolors[i].defclr));
+ i++;
+ }
+ PluginConfig.tabConfig.m_brushes[0] = CreateSolidBrush(PluginConfig.tabConfig.colors[4]);
+ PluginConfig.tabConfig.m_brushes[1] = CreateSolidBrush(PluginConfig.tabConfig.colors[5]);
+ PluginConfig.tabConfig.m_brushes[2] = CreateSolidBrush(PluginConfig.tabConfig.colors[6]);
+ PluginConfig.tabConfig.m_brushes[3] = CreateSolidBrush(PluginConfig.tabConfig.colors[7]);
+
+ PluginConfig.tabConfig.m_bottomAdjust = (int)M->GetDword("bottomadjust", 0);
+ PluginConfig.tabConfig.m_fixedwidth = M->GetDword("fixedwidth", FIXED_TAB_SIZE);
+
+ PluginConfig.tabConfig.m_fixedwidth = (PluginConfig.tabConfig.m_fixedwidth < 60 ? 60 : PluginConfig.tabConfig.m_fixedwidth);
+}
+
+void TSAPI FreeTabConfig()
+{
+ int i;
+
+ if(PluginConfig.tabConfig.m_hPenItemShadow)
+ DeleteObject(PluginConfig.tabConfig.m_hPenItemShadow);
+
+ if(PluginConfig.tabConfig.m_hPenLight)
+ DeleteObject(PluginConfig.tabConfig.m_hPenLight);
+
+ if(PluginConfig.tabConfig.m_hPenShadow)
+ DeleteObject(PluginConfig.tabConfig.m_hPenShadow);
+
+ if(PluginConfig.tabConfig.m_hMenuFont)
+ DeleteObject(PluginConfig.tabConfig.m_hMenuFont);
+
+ for(i = 0; i < 4; i++) {
+ if(PluginConfig.tabConfig.m_brushes[i]) {
+ DeleteObject(PluginConfig.tabConfig.m_brushes[i]);
+ PluginConfig.tabConfig.m_brushes[i] = 0;
+ }
+ }
+ ZeroMemory(&PluginConfig.tabConfig, sizeof(myTabCtrl));
+}
+
+/*
+ * options dialog for setting up tab options
+ */
+
+static bool tconfig_init = false;
+
+INT_PTR CALLBACK DlgProcTabConfig(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG: {
+ tconfig_init = false;
+ TranslateDialogDefault(hwndDlg);
+ SendMessage(hwndDlg, WM_USER + 100, 0, 0);
+ tconfig_init = true;
+ return TRUE;
+ }
+ case WM_USER + 100: {
+ DWORD dwFlags = M->GetDword("tabconfig", TCF_DEFAULT);
+ int i = 0;
+
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPIN, UDM_SETRANGE, 0, MAKELONG(10, 0));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPIN, UDM_SETPOS, 0, (int)M->GetByte(CSkin::m_skinEnabled ? "S_tborder" : "tborder", 2));
+ SetDlgItemInt(hwndDlg, IDC_TABBORDER, (int)M->GetByte(CSkin::m_skinEnabled ? "S_tborder" : "tborder", 2), FALSE);;
+
+ SendDlgItemMessage(hwndDlg, IDC_BOTTOMTABADJUSTSPIN, UDM_SETRANGE, 0, MAKELONG(3, -3));
+ SendDlgItemMessage(hwndDlg, IDC_BOTTOMTABADJUSTSPIN, UDM_SETPOS, 0, PluginConfig.tabConfig.m_bottomAdjust);
+ SetDlgItemInt(hwndDlg, IDC_BOTTOMTABADJUST, PluginConfig.tabConfig.m_bottomAdjust, TRUE);
+
+ SendDlgItemMessage(hwndDlg, IDC_TABWIDTHSPIN, UDM_SETRANGE, 0, MAKELONG(400, 50));
+ SendDlgItemMessage(hwndDlg, IDC_TABWIDTHSPIN, UDM_SETPOS, 0, PluginConfig.tabConfig.m_fixedwidth);
+ SetDlgItemInt(hwndDlg, IDC_TABWIDTH, PluginConfig.tabConfig.m_fixedwidth, TRUE);
+
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTER, UDM_SETRANGE, 0, MAKELONG(50, 0));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTERRIGHT, UDM_SETRANGE, 0, MAKELONG(50, 0));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTERTOP, UDM_SETRANGE, 0, MAKELONG(40, 0));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTERBOTTOM, UDM_SETRANGE, 0, MAKELONG(40, 0));
+
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTER, UDM_SETPOS, 0, (int)M->GetByte(CSkin::m_skinEnabled ? "S_tborder_outer_left" : "tborder_outer_left", 2));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTERRIGHT, UDM_SETPOS, 0, (int)M->GetByte(CSkin::m_skinEnabled ? "S_tborder_outer_right" : "tborder_outer_right", 2));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTERTOP, UDM_SETPOS, 0, (int)M->GetByte(CSkin::m_skinEnabled ? "S_tborder_outer_top" : "tborder_outer_top", 2));
+ SendDlgItemMessage(hwndDlg, IDC_TABBORDERSPINOUTERBOTTOM, UDM_SETPOS, 0, (int)M->GetByte(CSkin::m_skinEnabled ? "S_tborder_outer_bottom" : "tborder_outer_bottom", 2));
+
+ SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_SETRANGE, 0, MAKELONG(10, 1));
+ SendDlgItemMessage(hwndDlg, IDC_SPIN3, UDM_SETRANGE, 0, MAKELONG(10, 1));
+ SendDlgItemMessage(hwndDlg, IDC_SPIN1, UDM_SETPOS, 0, (LPARAM)M->GetByte("y-pad", 3));
+ SendDlgItemMessage(hwndDlg, IDC_SPIN3, UDM_SETPOS, 0, (LPARAM)M->GetByte("x-pad", 4));
+ SetDlgItemInt(hwndDlg, IDC_TABPADDING, (int)M->GetByte("y-pad", 3), FALSE);;
+ SetDlgItemInt(hwndDlg, IDC_HTABPADDING, (int)M->GetByte("x-pad", 4), FALSE);;
+ return 0;
+ }
+ case WM_NOTIFY:
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY: {
+ int i = 0;
+ BOOL translated;
+ int fixedWidth;
+
+ struct TContainerData *pContainer = pFirstContainer;
+
+ M->WriteByte(SRMSGMOD_T, "y-pad", (BYTE)(GetDlgItemInt(hwndDlg, IDC_TABPADDING, NULL, FALSE)));
+ M->WriteByte(SRMSGMOD_T, "x-pad", (BYTE)(GetDlgItemInt(hwndDlg, IDC_HTABPADDING, NULL, FALSE)));
+ M->WriteByte(SRMSGMOD_T, "tborder", (BYTE) GetDlgItemInt(hwndDlg, IDC_TABBORDER, &translated, FALSE));
+ M->WriteByte(SRMSGMOD_T, CSkin::m_skinEnabled ? "S_tborder_outer_left" : "tborder_outer_left", (BYTE) GetDlgItemInt(hwndDlg, IDC_TABBORDEROUTER, &translated, FALSE));
+ M->WriteByte(SRMSGMOD_T, CSkin::m_skinEnabled ? "S_tborder_outer_right" : "tborder_outer_right", (BYTE) GetDlgItemInt(hwndDlg, IDC_TABBORDEROUTERRIGHT, &translated, FALSE));
+ M->WriteByte(SRMSGMOD_T, CSkin::m_skinEnabled ? "S_tborder_outer_top" : "tborder_outer_top", (BYTE) GetDlgItemInt(hwndDlg, IDC_TABBORDEROUTERTOP, &translated, FALSE));
+ M->WriteByte(SRMSGMOD_T, CSkin::m_skinEnabled ? "S_tborder_outer_bottom" : "tborder_outer_bottom", (BYTE) GetDlgItemInt(hwndDlg, IDC_TABBORDEROUTERBOTTOM, &translated, FALSE));
+ M->WriteDword(SRMSGMOD_T, "bottomadjust", GetDlgItemInt(hwndDlg, IDC_BOTTOMTABADJUST, &translated, TRUE));
+
+ fixedWidth = GetDlgItemInt(hwndDlg, IDC_TABWIDTH, &translated, FALSE);
+ fixedWidth = (fixedWidth < 60 ? 60 : fixedWidth);
+ M->WriteDword(SRMSGMOD_T, "fixedwidth", fixedWidth);
+ FreeTabConfig();
+ ReloadTabConfig();
+ while (pContainer) {
+ TabCtrl_SetPadding(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), GetDlgItemInt(hwndDlg, IDC_HTABPADDING, NULL, FALSE), GetDlgItemInt(hwndDlg, IDC_TABPADDING, NULL, FALSE));
+ RedrawWindow(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
+ pContainer = pContainer->pNextContainer;
+ }
+ return TRUE;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_TABWIDTH:
+ case IDC_TABPADDING:
+ case IDC_HTABPADDING:
+ case IDC_TABBORDER:
+ case IDC_TABBORDEROUTER:
+ case IDC_TABBORDEROUTERBOTTOM:
+ case IDC_TABBORDEROUTERRIGHT:
+ case IDC_TABBORDEROUTERTOP:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+ break;
+
+ default:
+ break;
+
+ }
+ if(tconfig_init)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_DESTROY:
+ tconfig_init = false;
+ }
+ return FALSE;
+}
+
diff --git a/plugins/TabSRMM/src/taskbar.cpp b/plugins/TabSRMM/src/taskbar.cpp new file mode 100644 index 0000000000..dfe645bd4a --- /dev/null +++ b/plugins/TabSRMM/src/taskbar.cpp @@ -0,0 +1,956 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: taskbar.cpp 13178 2010-12-05 21:29:17Z silvercircle $
+ *
+ * Windows 7 taskbar integration
+ *
+ * - overlay icons
+ * - custom taskbar thumbnails for aero peek in tabbed containers
+ * - read Windows 7 task bar configuration from the registry.
+ */
+/**
+ * how it works:
+ *
+ * Because of the fact, the DWM does not talk to non-toplevel windows
+ * we need an invisible "proxy window" for each tab. This window is a very
+ * small and hidden toplevel tool window which is used to communicate
+ * with the dwm. Each proxy is associated with the client window (the "tab")
+ * and registers itself with the message container window via
+ * ITaskbarList3::RegisterTab().
+ *
+ * Instead of automatically created snapshots of the window content, we
+ * use custom generated thumbnails for the task bar buttons, including
+ * nickname, UID, status message and avatar. This makes the thumbnails
+ * easily recognizable.
+ *
+ * Thumbnails are generated "on request", only when the desktop window
+ * manager needs one.
+ *
+ * Each proxy window has a CThumbIM or CThumbMUC object which represents
+ * the actual thumbnail bitmap.
+ */
+
+#include "commonheaders.h"
+
+/**
+ * maps MUC event types to icon names for retrieving the "big" icons
+ * while generating task bar thumbnails.
+ * used by getMUCBigICon()
+ */
+
+/*
+struct TMUCLargeIconsMap {
+ UINT eventType;
+ char* szIconDesc;
+} MUCLargeIconMap[] = {
+ { GC_EVENT_NICK, "chat_nick" },
+ { GC_EVENT_PART, "chat_part" }
+};
+*/
+
+CTaskbarInteract* Win7Taskbar = 0;
+
+/**
+ * set the overlay icon for a task bar button. Used for typing notifications and incoming
+ * message indicator.
+ *
+ * @param hwndDlg HWND: container window handle
+ * @param lParam LPARAM: icon handle
+ * @return true if icon has been set and taskbar will accept it, false otherwise
+ */
+bool CTaskbarInteract::setOverlayIcon(HWND hwndDlg, LPARAM lParam) const
+{
+ if (m_pTaskbarInterface && m_isEnabled && m_fHaveLargeicons) {
+ m_pTaskbarInterface->SetOverlayIcon(hwndDlg,(HICON)lParam, NULL);
+ return(true);
+ }
+ return(false);
+}
+
+/**
+ * check the task bar status for "large icon mode".
+ * @return bool: true if large icons are in use, false otherwise
+ */
+bool CTaskbarInteract::haveLargeIcons()
+{
+ m_fHaveLargeicons = false;
+
+ if (m_pTaskbarInterface && m_isEnabled) {
+ HKEY hKey;
+ DWORD val = 1;
+ DWORD valGrouping = 2;
+ DWORD size = 4;
+ DWORD dwType = REG_DWORD;
+ /*
+ * check whether the taskbar is set to show large icons. This is necessary, because the method SetOverlayIcon()
+ * always returns S_OK, but the icon is simply ignored when using small taskbar icons.
+ * also, figure out the button grouping mode.
+ */
+ if(::RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hKey) == ERROR_SUCCESS) {
+ ::RegQueryValueEx(hKey, _T("TaskbarSmallIcons"), 0, &dwType, (LPBYTE)&val, &size);
+ size = 4;
+ dwType = REG_DWORD;
+ /*
+ * this is the "grouping mode" setting for the task bar. 0 = always combine, no labels
+ */
+ ::RegQueryValueEx(hKey, _T("TaskbarGlomLevel"), 0, &dwType, (LPBYTE)&valGrouping, &size);
+ ::RegCloseKey(hKey);
+ }
+ m_fHaveLargeicons = (val ? false : true); // small icons in use, revert to default icon feedback
+ m_fHaveAlwaysGrouping = (valGrouping == 0 ? true : false);
+ }
+ return(m_fHaveLargeicons);
+}
+
+/**
+ * removes the overlay icon for the given container window
+ * @param hwndDlg HWND: window handle
+ */
+void CTaskbarInteract::clearOverlayIcon(HWND hwndDlg) const
+{
+ if (m_pTaskbarInterface && m_isEnabled)
+ m_pTaskbarInterface->SetOverlayIcon(hwndDlg, NULL, NULL);
+}
+
+LONG CTaskbarInteract::updateMetrics()
+{
+ m_IconSize = 32;
+
+ return(m_IconSize);
+}
+
+/**
+ * register a new task bar button ("tab") for the button group hwndContainer
+ * (one of the top level message windows)
+ * @param hwndTab proxy window handle
+ * @param hwndContainer a message container window
+ */
+void CTaskbarInteract::registerTab(const HWND hwndTab, const HWND hwndContainer) const
+{
+ if(m_isEnabled) {
+ m_pTaskbarInterface->RegisterTab(hwndTab, hwndContainer);
+ m_pTaskbarInterface->SetTabOrder(hwndTab, 0);
+ }
+}
+
+/**
+ * remove a previously registered proxy window. The destructor of the proxy
+ * window class is using this before destroying the proxy window itself.
+ * @param hwndTab proxy window handle
+ */
+void CTaskbarInteract::unRegisterTab(const HWND hwndTab) const
+{
+ if(m_isEnabled)
+ m_pTaskbarInterface->UnregisterTab(hwndTab);
+}
+
+/**
+ * set a tab as active. The active thumbnail will appear with a slightly
+ * different background and transparency.
+ *
+ * @param hwndTab window handle of the PROXY window to activate
+ * (don't use the real window handle of the message
+ * tab, because it is not a toplevel window).
+ * @param hwndGroup window group to which it belongs (usually, the
+ * container window handle).
+ */
+void CTaskbarInteract::SetTabActive(const HWND hwndTab, const HWND hwndGroup) const
+{
+ if(m_isEnabled)
+ m_pTaskbarInterface->SetTabActive(hwndTab, hwndGroup, 0);
+}
+
+/**
+ * create a proxy window object for the given session. Do NOT call this more than once
+ * per session and not outside of WM_INITDIALOG after most things are initialized.
+ * @param dat session window data
+ *
+ * static member function. Ignored when OS is not Windows 7 or global option for
+ * Windows 7 task bar support is diabled.
+ */
+void CProxyWindow::add(TWindowData *dat)
+{
+ if(PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek) // && (!CSkin::m_skinEnabled || M->GetByte("forceAeroPeek", 0)))
+ dat->pWnd = new CProxyWindow(dat);
+ else
+ dat->pWnd = 0;
+}
+
+/**
+ * This is called from the broadcasted WM_DWMCOMPOSITIONCHANGED event by all messages
+ * sessions. It checks and, if needed, destroys or creates a proxy object, based on
+ * the status of the DWM
+ * @param dat session window data
+ *
+ * static member function
+ */
+void CProxyWindow::verify(TWindowData *dat)
+{
+ if(PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek) {
+ if(0 == dat->pWnd) {
+ dat->pWnd = new CProxyWindow(dat);
+ if(dat->pWnd) {
+ dat->pWnd->updateIcon(dat->hTabStatusIcon);
+ dat->pWnd->updateTitle(dat->cache->getNick());
+ }
+ }
+ else
+ dat->pWnd->verifyDwmState();
+ }
+ /*
+ * this should not happens, but who knows...
+ */
+ else {
+ if(dat->pWnd) {
+ delete dat->pWnd;
+ dat->pWnd = 0;
+ }
+ }
+}
+
+/**
+ * create the proxy (toplevel) window required to show per tab thumbnails
+ * and previews for a message session.
+ * each tab has one invisible proxy window
+ */
+CProxyWindow::CProxyWindow(const TWindowData *dat)
+{
+ m_dat = dat;
+ m_hBigIcon = 0;
+ m_thumb = 0;
+
+ m_hwndProxy = ::CreateWindowEx(/*WS_EX_TOOLWINDOW | */WS_EX_NOACTIVATE, PROXYCLASSNAME, _T(""),
+ WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION, -32000, -32000, 10, 10, NULL, NULL, g_hInst, (LPVOID)this);
+
+#if defined(__LOGDEBUG_)
+ _DebugTraceW(_T("create proxy object for: %s"), m_dat->cache->getNick());
+#endif
+ Win7Taskbar->registerTab(m_hwndProxy, m_dat->pContainer->hwnd);
+ if(CMimAPI::m_pfnDwmSetWindowAttribute) {
+ BOOL fIconic = TRUE;
+ BOOL fHasIconicBitmap = TRUE;
+
+ CMimAPI::m_pfnDwmSetWindowAttribute(m_hwndProxy, DWMWA_FORCE_ICONIC_REPRESENTATION, &fIconic, sizeof(fIconic));
+ CMimAPI::m_pfnDwmSetWindowAttribute(m_hwndProxy, DWMWA_HAS_ICONIC_BITMAP, &fHasIconicBitmap, sizeof(fHasIconicBitmap));
+ }
+}
+
+CProxyWindow::~CProxyWindow()
+{
+ Win7Taskbar->unRegisterTab(m_hwndProxy);
+ ::DestroyWindow(m_hwndProxy);
+
+#if defined(__LOGDEBUG_)
+ _DebugTraceW(_T("destroy proxy object for: %s"), m_dat->cache->getNick());
+#endif
+ if(m_thumb) {
+ delete m_thumb;
+ m_thumb = 0;
+ }
+}
+
+/**
+ * verify status of DWM when system broadcasts a WM_DWMCOMPOSITIONCHANGED message
+ * delete thumbnails, if no longer needed
+ */
+void CProxyWindow::verifyDwmState()
+{
+ if(!M->isDwmActive()) {
+ if(m_thumb) {
+ delete m_thumb;
+ m_thumb = 0;
+ }
+ }
+ else {
+ /*
+ * force thumbnail recreation
+ */
+ m_width = 0;
+ m_height = 0;
+ }
+}
+
+/**
+ * send a thumbnail to the DWM. If required, refresh it first.
+ * called by WM_DWMSENDICONICTHUMBNAIL handler.
+ *
+ * @param width thumbnail width as requested by DWM via lParam
+ * @param height thumbnail height as requested by DWM via lParam
+ */
+void CProxyWindow::sendThumb(LONG width, LONG height)
+{
+ if(0 == m_thumb) {
+ m_width = width;
+ m_height = height;
+ if(m_dat->bType == SESSIONTYPE_IM)
+ m_thumb = new CThumbIM(this);
+ else
+ m_thumb = new CThumbMUC(this);
+ }
+ else if(width != m_width || height != m_height || !m_thumb->isValid()) {
+ m_width = width;
+ m_height = height;
+ m_thumb->update();
+ }
+ if(m_thumb)
+ CMimAPI::m_pfnDwmSetIconicThumbnail(m_hwndProxy, m_thumb->getHBM(), DWM_SIT_DISPLAYFRAME);
+}
+
+/**
+ * send a live preview image of a given message session to the DWM.
+ * called by WM_DWMSENDICONICLIVEPREVIEWBITMAP on DWM's request.
+ *
+ * The bitmap can be deleted after submitting it, because the DWM
+ * will cache a copy of it (and re-request it when its own bitmap cache
+ * was purged).
+ */
+void CProxyWindow::sendPreview()
+{
+ POINT pt = {0};
+ RECT rcContainer;
+ HDC hdc, dc;
+ FORMATRANGE fr = {0};
+ int twips = (int)(15.0f / PluginConfig.g_DPIscaleY);
+ RECT rcTemp;
+ RECT rcRich, rcLog;
+ bool fIsChat = m_dat->bType == SESSIONTYPE_IM ? false : true;
+ TWindowData* dat_active = reinterpret_cast<TWindowData *>(::GetWindowLongPtr(m_dat->pContainer->hwndActive, GWLP_USERDATA));
+
+ if(m_thumb && dat_active) {
+ HWND hwndRich = ::GetDlgItem(m_dat->hwnd, fIsChat ? IDC_CHAT_LOG : IDC_LOG);
+ LONG cx, cy;
+ POINT ptOrigin = {0}, ptBottom;
+
+ if(m_dat->dwFlags & MWF_NEEDCHECKSIZE) {
+ RECT rcClient;
+
+ ::SendMessage(m_dat->pContainer->hwnd, DM_QUERYCLIENTAREA, 0, (LPARAM)&rcClient);
+ ::MoveWindow(m_dat->hwnd, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), FALSE);
+ ::SendMessage(m_dat->hwnd, WM_SIZE, 0, 0);
+ ::SendMessage(m_dat->hwnd, DM_FORCESCROLL, 0, 0);
+ }
+ /*
+ * a minimized container has a null rect as client area, so do not use it
+ * use the last known client area size instead.
+ */
+
+ if(!::IsIconic(m_dat->pContainer->hwnd)) {
+ ::GetWindowRect(m_dat->pContainer->hwndActive, &rcLog);
+ ::GetClientRect(m_dat->pContainer->hwnd, &rcContainer);
+ pt.x = rcLog.left;
+ pt.y = rcLog.top;
+ ::ScreenToClient(m_dat->pContainer->hwnd, &pt);
+ }
+ else {
+ rcLog = m_dat->pContainer->rcLogSaved;
+ rcContainer = m_dat->pContainer->rcSaved;
+ pt = m_dat->pContainer->ptLogSaved;
+ }
+
+ ::GetWindowRect(::GetDlgItem(m_dat->pContainer->hwndActive, dat_active->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &rcTemp);
+ ptBottom.x = rcTemp.left;
+ ptBottom.y = rcTemp.bottom;
+ ::ScreenToClient(m_dat->pContainer->hwnd, &ptBottom);
+
+ cx = rcLog.right - rcLog.left;
+ cy = rcLog.bottom - rcLog.top;
+ rcRich.left = 0;
+ rcRich.top = 0;
+ rcRich.right = cx;
+ rcRich.bottom = ptBottom.y - pt.y;
+
+ dc = ::GetDC(m_dat->hwnd);
+ hdc = ::CreateCompatibleDC(dc);
+ HBITMAP hbm = CSkin::CreateAeroCompatibleBitmap(rcContainer, hdc);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(hdc, hbm));
+
+ HBRUSH brb = ::CreateSolidBrush(RGB(20, 20, 20));
+ ::FillRect(hdc, &rcContainer, brb);
+ ::DeleteObject(brb);
+ CImageItem::SetBitmap32Alpha(hbm, 100);
+
+ LRESULT first = ::SendMessage(hwndRich, EM_CHARFROMPOS, 0, reinterpret_cast<LPARAM>(&ptOrigin));
+
+ /*
+ * paint the content of the message log control into a separate bitmap without
+ * transparency
+ */
+ HDC hdcRich = ::CreateCompatibleDC(dc);
+ HBITMAP hbmRich = CSkin::CreateAeroCompatibleBitmap(rcRich, hdcRich);
+ HBITMAP hbmRichOld = reinterpret_cast<HBITMAP>(::SelectObject(hdcRich, hbmRich));
+
+ COLORREF clr = fIsChat ? M->GetDword(FONTMODULE, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR) : m_dat->pContainer->theme.inbg;
+ HBRUSH br = ::CreateSolidBrush(clr);
+ ::FillRect(hdcRich, &rcRich, br);
+ ::DeleteObject(br);
+
+ if(m_dat->hwndIEView)
+ ::SendMessage(m_dat->hwndIEView, WM_PRINT, reinterpret_cast<WPARAM>(hdcRich), PRF_CLIENT | PRF_NONCLIENT);
+ else if(m_dat->hwndHPP) {
+ CSkin::RenderText(hdcRich, m_dat->hTheme, CTranslator::get(CTranslator::GEN_AEROPEEK_NOHPP),
+ &rcRich, DT_VCENTER | DT_CENTER | DT_WORDBREAK, 10, m_dat->pContainer->theme.fontColors[MSGFONTID_MYMSG], false);
+ }
+ else {
+ rcRich.right *= twips;
+ rcRich.bottom *= twips;
+
+ fr.hdc = hdcRich;
+ fr.hdcTarget = hdcRich;
+ fr.rc = rcRich;
+ fr.rcPage = rcRich;
+ fr.chrg.cpMax = -1;
+ fr.chrg.cpMin = first;
+
+ ::SendMessage(hwndRich, EM_FORMATRANGE, 1, reinterpret_cast<LPARAM>(&fr));
+ }
+
+ ::SelectObject(hdcRich, hbmRichOld);
+ CImageItem::SetBitmap32Alpha(hbmRich, 255);
+ ::SelectObject(hdcRich, hbmRich);
+ ::BitBlt(hdc, pt.x, pt.y, cx, cy, hdcRich, 0, 0, SRCCOPY);
+ ::SelectObject(hdcRich, hbmRichOld);
+ ::DeleteObject(hbmRich);
+ ::DeleteDC(hdcRich);
+
+ ::SelectObject(hdc, hbmOld);
+ ::DeleteDC(hdc);
+ if(CSkin::m_skinEnabled && CSkin::m_frameSkins) {
+ pt.x = CSkin::m_SkinnedFrame_left;
+ pt.y = CSkin::m_SkinnedFrame_caption + CSkin::m_SkinnedFrame_bottom;
+ }
+ else
+ pt.x = pt.y = 0;
+ CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap(m_hwndProxy, hbm, &pt, m_dat->pContainer->dwFlags & CNT_CREATE_MINIMIZED ? 0 : DWM_SIT_DISPLAYFRAME);
+ ::ReleaseDC(m_dat->hwnd, dc);
+ ::DeleteObject(hbm);
+ }
+}
+
+/**
+ * set the large icon for the thumbnail. This is mostly used by group chats
+ * to indicate last active event in the session.
+ *
+ * hIcon may be 0 to remove a custom big icon. In that case, the renderer
+ * will try to figure out a suitable one, based on session data.
+ *
+ * @param hIcon icon handle (should be a 32x32 icon)
+ * @param fInvalidate invalidate the thumbnail (default value = true)
+ */
+void CProxyWindow::setBigIcon(const HICON hIcon, bool fInvalidate)
+{
+ m_hBigIcon = hIcon;
+ if(fInvalidate)
+ Invalidate();
+}
+
+/**
+ * set a overlay icon for the thumbnail. This is mostly used by group chats
+ * to indicate last active event in the session.
+ *
+ * hIcon may be 0 to remove a custom overlay icon.
+ *
+ * @param hIcon icon handle (should be a 16x16 icon)
+ * @param fInvalidate invalidate the thumbnail (default value = true)
+ */
+void CProxyWindow::setOverlayIcon(const HICON hIcon, bool fInvalidate)
+{
+ m_hOverlayIcon = hIcon;
+ if(fInvalidate)
+ Invalidate();
+}
+
+/**
+ * update the (small) thumbnail icon in front of the title string
+ * @param hIcon new icon handle
+ */
+void CProxyWindow::updateIcon(const HICON hIcon) const
+{
+ if(m_hwndProxy && hIcon)
+ ::SendMessage(m_hwndProxy, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon));
+}
+
+/**
+ * set the task bar button ("tab") active. This activates the proxy
+ * window as a sub-window of the (top level) container window.
+ * This is called whenever the active message tab or window changes
+ */
+void CProxyWindow::activateTab() const
+{
+ Win7Taskbar->SetTabActive(m_hwndProxy, m_dat->pContainer->hwnd);
+}
+/**
+ * invalidate the thumbnail, it will be recreated at the next request
+ * by the DWM
+ *
+ * this is called from several places whenever a relevant information,
+ * represented in a thumbnail image, has changed.
+ *
+ * also tell the DWM that it must request a new thumb.
+ */
+void CProxyWindow::Invalidate() const
+{
+ if(m_thumb) {
+ m_thumb->setValid(false);
+ /*
+ * tell the DWM to request a new thumbnail for the proxy window m_hwnd
+ * when it needs one.
+ */
+ CMimAPI::m_pfnDwmInvalidateIconicBitmaps(m_hwndProxy);
+ }
+}
+
+/**
+ * update the thumb title string (usually, the nickname)
+ * @param tszTitle: new title string
+ */
+void CProxyWindow::updateTitle(const TCHAR *tszTitle) const
+{
+ if(m_hwndProxy && tszTitle)
+ ::SetWindowText(m_hwndProxy, tszTitle);
+}
+
+/**
+ * stub window procedure for the custom proxy window class
+ * just initialize GWLP_USERDATA and call the object's method
+ *
+ * static member function
+ */
+LRESULT CALLBACK CProxyWindow::stubWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CProxyWindow* pWnd = reinterpret_cast<CProxyWindow *>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
+
+ if(pWnd)
+ return(pWnd->wndProc(hWnd, msg, wParam, lParam));
+
+ switch(msg) {
+ case WM_NCCREATE: {
+ CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT *>(lParam);
+ CProxyWindow *pWnd = reinterpret_cast<CProxyWindow *>(cs->lpCreateParams);
+ ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<UINT_PTR>(pWnd));
+ return(pWnd->wndProc(hWnd, msg, wParam, lParam));
+ }
+ default:
+ break;
+ }
+ return(::DefWindowProc(hWnd, msg, wParam, lParam));
+}
+
+/**
+ * window procedure for the proxy window
+ */
+LRESULT CALLBACK CProxyWindow::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+
+#if defined(__LOGDEBUG_)
+ case WM_NCCREATE:
+ _DebugTraceW(_T("create proxy WINDOW for: %s"), m_dat->cache->getNick());
+ break;
+#endif
+ case WM_CLOSE: {
+ TContainerData* pC = m_dat->pContainer;
+
+ if(m_dat->hwnd != pC->hwndActive)
+ SendMessage(m_dat->hwnd, WM_CLOSE, 1, 3);
+ else
+ SendMessage(m_dat->hwnd, WM_CLOSE, 1, 2);
+ if(!IsIconic(pC->hwnd))
+ SetForegroundWindow(pC->hwnd);
+ return(0);
+ }
+
+ /*
+ * proxy window was activated by clicking on the thumbnail. Send this
+ * to the real message window.
+ */
+ case WM_ACTIVATE:
+ if(WA_ACTIVE == wParam) {
+ if(IsWindow(m_dat->hwnd))
+ ::PostMessage(m_dat->hwnd, DM_ACTIVATEME, 0, 0);
+ return(0); // no default processing, avoid flickering.
+ }
+ break;
+
+ case WM_NCDESTROY:
+ ::SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
+#if defined(__LOGDEBUG_)
+ _DebugTraceW(_T("destroy proxy WINDOW for: %s"), m_dat->cache->getNick());
+#endif
+ break;
+
+ case WM_DWMSENDICONICTHUMBNAIL:
+ sendThumb(HIWORD(lParam), LOWORD(lParam));
+ return(0);
+
+ case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
+ sendPreview();
+ return(0);
+
+ default:
+ break;
+ }
+ return(::DefWindowProc(hWnd, msg, wParam, lParam));
+}
+
+/**
+ * base thumbnail class. Create the background and common parts for a
+ * thumbnail
+ *
+ * @param _p owner proxy window object
+ * @return
+ */
+CThumbBase::CThumbBase(const CProxyWindow* _p)
+{
+ m_pWnd = _p;
+ m_hbmThumb = 0;
+ renderBase();
+}
+
+/**
+ * render base for a thumbnail. This creates the background, the large icon
+ * and the basic status mode text. It also divides the thumbnail rectangle
+ * into a few content rectangles used later by the content renderer.
+ */
+void CThumbBase::renderBase()
+{
+ HICON hIcon = 0;
+ HBRUSH brBack;
+ LONG lIconSize = 32;
+
+ m_width = m_pWnd->getWidth();
+ m_height = m_pWnd->getHeight();
+ m_dat = m_pWnd->getDat();
+ m_dtFlags = 0;
+ m_hOldFont = 0;
+
+#if defined(__LOGDEBUG_)
+ _DebugTraceW(_T("refresh base (background) with %d, %d"), m_width, m_height);
+#endif
+
+ m_rc.right = m_width;
+ m_rc.bottom = m_height;
+ m_rc.left = m_rc.top = 0;
+
+ if(m_hbmThumb) {
+ ::DeleteObject(m_hbmThumb);
+ m_hbmThumb = 0;
+ }
+
+ HDC dc = ::GetDC(m_pWnd->getHwnd());
+ m_hdc = ::CreateCompatibleDC(dc);
+
+ m_hbmThumb = CSkin::CreateAeroCompatibleBitmap(m_rc, m_hdc);
+ m_hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(m_hdc, m_hbmThumb));
+ ReleaseDC(m_pWnd->getHwnd(), dc);
+
+ brBack = ::CreateSolidBrush(m_dat->dwUnread ? RGB(80, 60, 60) : RGB(60, 60, 60));
+ ::FillRect(m_hdc, &m_rc, brBack);
+ ::DeleteObject(brBack);
+
+ ::SelectObject(m_hdc, m_hbmOld);
+ CImageItem::SetBitmap32Alpha(m_hbmThumb, m_dat->dwUnread ? 110 : 60);
+ m_hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(m_hdc, m_hbmThumb));
+
+ SetBkMode(m_hdc, TRANSPARENT);
+
+ m_hOldFont = reinterpret_cast<HFONT>(::SelectObject(m_hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_STATUS]));
+ ::GetTextExtentPoint32A(m_hdc, "A", 1, &m_sz);
+
+ InflateRect(&m_rc, -3, -3);
+
+ setupRect();
+ hIcon = m_pWnd->getBigIcon();
+
+ if(0 == hIcon) {
+ if(m_dat->dwUnread) {
+ if(PluginConfig.g_IconMsgEventBig)
+ hIcon = PluginConfig.g_IconMsgEventBig;
+ else {
+ hIcon = PluginConfig.g_IconMsgEvent;
+ lIconSize = 16;
+ }
+ }
+ else {
+ hIcon = reinterpret_cast<HICON>(LoadSkinnedProtoIconBig(m_dat->cache->getActiveProto(), m_dat->cache->getActiveStatus()));
+ if(0 == hIcon || reinterpret_cast<HICON>(CALLSERVICE_NOTFOUND) == hIcon) {
+ hIcon = reinterpret_cast<HICON>(LoadSkinnedProtoIcon(m_dat->cache->getActiveProto(), m_dat->cache->getActiveStatus()));
+ lIconSize = 16;
+ }
+ }
+ }
+ ::DrawIconEx(m_hdc, m_rcIcon.right / 2 - lIconSize / 2, m_rcIcon.top, hIcon, lIconSize, lIconSize, 0, 0, DI_NORMAL);
+ hIcon = m_pWnd->getOverlayIcon();
+ if(hIcon)
+ ::DrawIconEx(m_hdc, m_rcIcon.right - 16, m_rcIcon.top + 16, hIcon, 16, 16, 0, 0, DI_NORMAL);
+
+ m_rcIcon.top += (lIconSize + 3);
+ CSkin::RenderText(m_hdc, m_dat->hTheme, m_dat->szStatus, &m_rcIcon, m_dtFlags | DT_CENTER | DT_WORD_ELLIPSIS, 10, 0, true);
+ if(m_dat->dwUnread && SESSIONTYPE_IM == m_dat->bType) {
+ wchar_t tszTemp[30];
+
+ m_rcIcon.top += m_sz.cy;
+ mir_sntprintf(tszTemp, 30, CTranslator::get(CTranslator::GEN_TASKBAR_STRING_UNREAD), m_dat->dwUnread);
+ CSkin::RenderText(m_hdc, m_dat->hTheme, tszTemp, &m_rcIcon, m_dtFlags | DT_CENTER | DT_WORD_ELLIPSIS, 10, 0, true);
+ }
+ m_rcIcon= m_rcTop;
+ m_rcIcon.top += 2;
+ m_rcIcon.left = m_rc.right / 3;
+ m_cx = m_rcIcon.right - m_rcIcon.left;
+ m_cy = m_rcIcon.bottom - m_rcIcon.top;
+}
+
+/**
+ * divide space into content rectangles for normal thumbnails
+ */
+void CThumbBase::setupRect()
+{
+ if(SESSIONTYPE_IM == m_pWnd->getDat()->bType) {
+ m_rcTop = m_rc;
+ m_rcBottom = m_rc;
+ m_rcBottom.top = m_rc.bottom - ( 2 * (m_rcBottom.bottom / 5)) - 2;
+ m_rcTop.bottom = m_rcBottom.top - 2;
+
+ m_rcIcon = m_rcTop;
+ m_rcIcon.right = m_rc.right / 3;
+ }
+ else {
+ m_rcTop = m_rc;
+ m_rcBottom = m_rc;
+ m_rcBottom.top = m_rc.bottom - ( 2 * (m_rcBottom.bottom / 5)) - 2;
+ m_rcTop.bottom = m_rcBottom.top - 2;
+
+ m_rcIcon = m_rcTop;
+ m_rcIcon.right = m_rc.left + 42;
+ }
+}
+
+/**
+ * destroy the thumbnail object. Just delete the bitmap we cached
+ * @return
+ */
+CThumbBase::~CThumbBase()
+{
+ if(m_hbmThumb) {
+ ::DeleteObject(m_hbmThumb);
+ m_hbmThumb = 0;
+ m_isValid = false;
+ }
+#if defined(__LOGDEBUG_)
+ _DebugTraceW(_T("destroy CThumbBase"));
+#endif
+}
+
+/**
+ * create a IM session thumbnail. base class will create the
+ * bitmap and render the background.
+ *
+ * @param _p our owner (CProxyWindow object)
+ */
+CThumbIM::CThumbIM(const CProxyWindow* _p) : CThumbBase(_p)
+{
+ renderContent();
+ setValid(true);
+}
+
+/**
+ * update the thumbnail, render everything and set it valid
+ */
+void CThumbIM::update()
+{
+ renderBase();
+ renderContent();
+ setValid(true);
+}
+
+/**
+ * render the content for an IM chat session thumbnail
+ * m_hdc etc. must already be initialized (done by the constructor)
+ * background had been already rendered
+ */
+void CThumbIM::renderContent()
+{
+ HBITMAP hbmAvatar, hbmOldAv;
+ double dNewWidth = 0.0, dNewHeight = 0.0;
+ bool fFree = false;
+ HRGN hRgn = 0;
+ HDC dc;
+ const wchar_t* tszStatusMsg = 0;
+
+ hbmAvatar = (m_dat->ace && m_dat->ace->hbmPic) ? m_dat->ace->hbmPic : PluginConfig.g_hbmUnknown;
+ Utils::scaleAvatarHeightLimited(hbmAvatar, dNewWidth, dNewHeight, m_rcIcon.bottom - m_rcIcon.top);
+
+ HBITMAP hbmResized = CSkin::ResizeBitmap(hbmAvatar, dNewWidth, dNewHeight, fFree);
+
+ dc = CreateCompatibleDC(m_hdc);
+ hbmOldAv = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmResized));
+
+ LONG xOff = m_rcIcon.right - (LONG)dNewWidth - 2;
+ LONG yOff = (m_cy - (LONG)dNewHeight) / 2 + m_rcIcon.top;
+
+ hRgn = ::CreateRectRgn(xOff - 1, yOff - 1, xOff + (LONG)dNewWidth + 2, yOff + (LONG)dNewHeight + 2);
+ CSkin::m_default_bf.SourceConstantAlpha = 150;
+ CMimAPI::m_MyAlphaBlend(m_hdc, xOff, yOff, (LONG)dNewWidth, (LONG)dNewHeight, dc, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
+ CSkin::m_default_bf.SourceConstantAlpha = 255;
+ ::FrameRgn(m_hdc, hRgn, reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)), 1, 1);
+
+ ::DeleteObject(hRgn);
+ ::SelectObject(dc, hbmOldAv);
+
+ if(hbmResized != hbmAvatar)
+ ::DeleteObject(hbmResized);
+
+ ::DeleteDC(dc);
+ m_rcBottom.bottom -= 16;
+
+ /*
+ * status message and bottom line (either UID or nick name, depending on
+ * task bar grouping mode). For chat rooms, it is the topic.
+ */
+ if((m_rcBottom.bottom - m_rcBottom.top) < 2 * m_sz.cy)
+ m_dtFlags |= DT_SINGLELINE;
+
+ m_rcBottom.bottom -= ((m_rcBottom.bottom - m_rcBottom.top) % m_sz.cy); // adjust to a multiple of line height
+
+ if(0 == (tszStatusMsg = m_dat->cache->getStatusMsg()))
+ tszStatusMsg = CTranslator::get(CTranslator::GEN_NO_STATUS);
+
+ CSkin::RenderText(m_hdc, m_dat->hTheme, tszStatusMsg, &m_rcBottom, DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | m_dtFlags, 10, 0, true);
+ m_rcBottom.bottom = m_rc.bottom;
+ m_rcBottom.top = m_rcBottom.bottom - m_sz.cy - 2;
+ CSkin::RenderText(m_hdc, m_dat->hTheme, Win7Taskbar->haveAlwaysGroupingMode() ? m_dat->cache->getUIN() : m_dat->cache->getNick(),
+ &m_rcBottom, m_dtFlags | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS, 10, 0, true);
+
+ /*
+ * finalize it
+ * do NOT delete the bitmap, the dwm will need the handle
+ * m_hbm is deleted when a new thumbnail is generated on dwm's request.
+ * this is not a leak!
+ */
+ if(m_hOldFont)
+ ::SelectObject(m_hdc, m_hOldFont);
+
+ ::SelectObject(m_hdc, m_hbmOld);
+ ::DeleteDC(m_hdc);
+}
+
+/**
+ * create a MUC session thumbnail. base class will create the
+ * bitmap and render the background.
+ *
+ * @param _p our owner (CProxyWindow object)
+ * @return
+ */
+CThumbMUC::CThumbMUC(const CProxyWindow* _p) : CThumbBase(_p)
+{
+ renderContent();
+ setValid(true);
+}
+
+/**
+ * update an invalidated thumbnail
+ */
+void CThumbMUC::update()
+{
+ renderBase();
+ renderContent();
+ setValid(true);
+}
+
+/**
+ * render content area for a MUC thumbnail
+ */
+void CThumbMUC::renderContent()
+{
+ if(m_dat->si) {
+ const MODULEINFO* mi = MM_FindModule(m_dat->si->pszModule);
+ wchar_t szTemp[250];
+ const wchar_t* szStatusMsg = 0;
+
+ if(mi) {
+ if(m_dat->dwUnread) {
+ mir_sntprintf(szTemp, 30, CTranslator::get(CTranslator::GEN_TASKBAR_STRING_UNREAD), m_dat->dwUnread);
+ CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
+ m_rcIcon.top += m_sz.cy;
+ }
+ if(m_dat->si->iType != GCW_SERVER) {
+ wchar_t* _p = NULL;
+ if ( m_dat->si->ptszStatusbarText )
+ _p = wcschr(m_dat->si->ptszStatusbarText, ']');
+ if( _p ) {
+ _p++;
+ wchar_t _t = *_p;
+ *_p = 0;
+ mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_CHAT_ROOM), m_dat->si->ptszStatusbarText);
+ *_p = _t;
+ }
+ else
+ mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_CHAT_ROOM), L"");
+ CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
+ m_rcIcon.top += m_sz.cy;
+ mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_USERS), m_dat->si->nUsersInNicklist);
+ CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
+ }
+ else {
+ mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_SERVER_WINDOW));
+ CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
+ if(mi->tszIdleMsg[0] && _tcslen(mi->tszIdleMsg) > 2) {
+ m_rcIcon.top += m_sz.cy;
+ CSkin::RenderText(m_hdc, m_dat->hTheme, &mi->tszIdleMsg[2], &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
+ }
+ }
+ }
+
+ if((m_rcBottom.bottom - m_rcBottom.top) < 2 * m_sz.cy)
+ m_dtFlags |= DT_SINGLELINE;
+
+ m_rcBottom.bottom -= ((m_rcBottom.bottom - m_rcBottom.top) % m_sz.cy); // adjust to a multiple of line height
+
+ if(m_dat->si->iType != GCW_SERVER) {
+ if(0 == (szStatusMsg = m_dat->si->ptszTopic))
+ szStatusMsg = CTranslator::get(CTranslator::GEN_MUC_NO_TOPIC);
+ }
+ else if(mi) {
+ mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::MUC_SBAR_ON_SERVER), m_dat->szMyNickname, mi->ptszModDispName, L"");
+ szStatusMsg = szTemp;
+ }
+
+ CSkin::RenderText(m_hdc, m_dat->hTheme, szStatusMsg, &m_rcBottom, DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | m_dtFlags, 10, 0, true);
+ }
+ /*
+ * finalize it
+ * do NOT delete the bitmap, the dwm will need the handle
+ * m_hbm is deleted when a new thumbnail is generated on dwm's request.
+ * this is not a leak!
+ */
+ if(m_hOldFont)
+ ::SelectObject(m_hdc, m_hOldFont);
+
+ ::SelectObject(m_hdc, m_hbmOld);
+ ::DeleteDC(m_hdc);
+}
diff --git a/plugins/TabSRMM/src/templates.cpp b/plugins/TabSRMM/src/templates.cpp new file mode 100644 index 0000000000..74000f7e11 --- /dev/null +++ b/plugins/TabSRMM/src/templates.cpp @@ -0,0 +1,391 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2009 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: templates.cpp 13034 2010-10-24 20:39:04Z silvercircle $
+ *
+ * Simple editor for the message log templates
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+/*
+* 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, HANDLE hContact, int rtl)
+{
+ DBVARIANT dbv = {0};
+ int i;
+
+ for (i = 0; i <= TMPL_ERRMSG; i++) {
+ if (M->GetTString(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);
+ DBFreeVariant(&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++)
+ M->WriteTString(NULL, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]);
+ M->WriteByte(RTLTEMPLATES_MODULE, "setup", 2);
+ }
+ if (M->GetByte(TEMPLATES_MODULE, "setup", 0) < 2) {
+ for (i = 0; i <= TMPL_ERRMSG; i++)
+ M->WriteTString(NULL, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]);
+ M->WriteByte(TEMPLATES_MODULE, "setup", 2);
+ }
+ LoadTemplatesFrom(<R_Active, (HANDLE)0, 0);
+ LoadTemplatesFrom(&RTL_Active, (HANDLE)0, 1);
+}
+
+INT_PTR CALLBACK DlgProcTemplateEditor(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct TWindowData *dat = 0;
+ TemplateEditorInfo *teInfo = 0;
+ TTemplateSet *tSet;
+ int i;
+ dat = (struct TWindowData *) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ /*
+ * since this dialog needs a struct 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: {
+ TemplateEditorNew *teNew = (TemplateEditorNew *)lParam;
+ COLORREF url_visited = RGB(128, 0, 128);
+ COLORREF url_unvisited = RGB(0, 0, 255);
+ dat = (struct TWindowData *) malloc(sizeof(struct TWindowData));
+
+ TranslateDialogDefault(hwndDlg);
+
+ ZeroMemory((void *) dat, sizeof(struct TWindowData));
+ dat->pContainer = (struct TContainerData *)malloc(sizeof(struct TContainerData));
+ ZeroMemory((void *)dat->pContainer, sizeof(struct TContainerData));
+ teInfo = (TemplateEditorInfo *)dat->pContainer;
+ ZeroMemory((void *)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 = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ dat->szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)dat->hContact, 0);
+ while(dat->szProto == 0 && dat->hContact != 0) {
+ dat->hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)dat->hContact, 0);
+ dat->szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)dat->hContact, 0);
+ }
+ 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, CTranslator::getOpt(CTranslator::OPT_TEMP_TITLE));
+ 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, CTranslator::getOpt(CTranslator::OPT_TEMP_RESET),
+ CTranslator::getOpt(CTranslator::OPT_TEMP_TITLE), MB_YESNO | MB_ICONQUESTION) == IDYES) {
+ M->WriteByte(teInfo->rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, "setup", 0);
+ LoadDefaultTemplates();
+ MessageBox(0, CTranslator::getOpt(CTranslator::OPT_TEMP_WASRESET),
+ CTranslator::getOpt(CTranslator::OPT_TEMP_TITLE), MB_OK);
+ DestroyWindow(hwndDlg);
+ }
+ break;
+ case IDC_TEMPLATELIST:
+ switch (HIWORD(wParam)) {
+ case LBN_DBLCLK: {
+ LRESULT 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: {
+ LRESULT 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;
+ }
+ }
+ 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);
+ M->WriteTString(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]);
+ DBDeleteContactSetting(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, safe_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)
+ free(dat->pContainer);
+ if (dat)
+ free(dat);
+
+ M->WriteDword(SRMSGMOD_T, "cc1", SendDlgItemMessage(hwndDlg, IDC_COLOR1, CPM_GETCOLOUR, 0, 0));
+ M->WriteDword(SRMSGMOD_T, "cc2", SendDlgItemMessage(hwndDlg, IDC_COLOR2, CPM_GETCOLOUR, 0, 0));
+ M->WriteDword(SRMSGMOD_T, "cc3", SendDlgItemMessage(hwndDlg, IDC_COLOR3, CPM_GETCOLOUR, 0, 0));
+ M->WriteDword(SRMSGMOD_T, "cc4", SendDlgItemMessage(hwndDlg, IDC_COLOR4, CPM_GETCOLOUR, 0, 0));
+ M->WriteDword(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/themeio.cpp b/plugins/TabSRMM/src/themeio.cpp new file mode 100644 index 0000000000..a331d921c1 --- /dev/null +++ b/plugins/TabSRMM/src/themeio.cpp @@ -0,0 +1,474 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: themeio.cpp 13412 2011-03-08 19:13:11Z george.hazan $
+ *
+ * Import and export theme settings between files and the database
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+
+#define CURRENT_THEME_VERSION 5
+#define THEME_COOKIE 25099837
+
+extern char *TemplateNames[];
+extern TTemplateSet LTR_Active, RTL_Active;
+
+
+static struct _tagExtSettings {
+ char* szIniSection;
+ char* szIniName;
+ char* szDbModule;
+ char* szDbSetting;
+ DWORD dwDef;
+} _extSettings[12] = {
+ "Message Log", "BackgroundColor", FONTMODULE, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR,
+ "Message Log", "IncomingBG", FONTMODULE, "inbg", SRMSGDEFSET_BKGINCOLOUR,
+ "Message Log", "OutgoingBG", FONTMODULE, "outbg", SRMSGDEFSET_BKGOUTCOLOUR,
+ "Message Log", "OldIncomingBG", FONTMODULE, "oldinbg", SRMSGDEFSET_BKGINCOLOUR,
+ "Message Log", "OldOutgoingBG", FONTMODULE, "oldoutbg", SRMSGDEFSET_BKGOUTCOLOUR,
+ "Message Log", "StatusBG", FONTMODULE, "statbg", SRMSGDEFSET_BKGCOLOUR,
+ "Message Log", "InputBG", FONTMODULE, "inputbg", SRMSGDEFSET_BKGCOLOUR,
+ "Message Log", "HgridColor", FONTMODULE, "hgrid", SRMSGDEFSET_BKGCOLOUR,
+ "Message Log", "DWFlags", SRMSGMOD_T, "mwflags", MWF_LOG_DEFAULT,
+ "Chat", "UserListBG", "Chat", "ColorNicklistBG", SRMSGDEFSET_BKGCOLOUR,
+ "Message Log", "LeftIndent", SRMSGMOD_T, "IndentAmount", 20,
+ "Message Log", "RightIndent", SRMSGMOD_T, "RightIndent", 20,
+};
+
+/**
+ * new in TabSRMM3 / theme version 5
+ * don't read these values from themes with version < 5
+ */
+static struct _tagExtSettings_v5 {
+ char* szIniSection;
+ char* szIniName;
+ char* szDbModule;
+ char* szDbSetting;
+ DWORD dwDef;
+} _extSettings_v5[18] = {
+ "CommonClrs", "IP_High", FONTMODULE, "ipfieldsbgHigh", 0xf0f0f0,
+ "CommonClrs", "IP_Low", FONTMODULE, "ipfieldsbg", 0x62caff,
+ "CommonClrs", "TB_High", FONTMODULE, "tbBgHigh", 0,
+ "CommonClrs", "TB_Low", FONTMODULE, "tbBgLow", 0,
+ "CommonClrs", "FillColor", FONTMODULE, "fillColor", 0,
+ "CommonClrs", "RichBorders", FONTMODULE, "cRichBorders", 0,
+ "CommonClrs", "GenericTxt", FONTMODULE, "genericTxtClr", RGB(20, 20, 20),
+ "AeroMode", "Style", SRMSGMOD_T, "aerostyle", CSkin::AERO_EFFECT_MILK,
+ "AeroMode", "AeroGlowColor", FONTMODULE, "aeroGlow", RGB(40, 40, 255),
+
+ "Colored Tabs", "NormalText", SRMSGMOD_T, "tab_txt_normal", RGB(1, 1, 1),
+ "Colored Tabs", "ActiveText", SRMSGMOD_T, "tab_txt_active", RGB(1, 1, 1),
+ "Colored Tabs", "HottrackText", SRMSGMOD_T, "tab_txt_hottrack", RGB(1, 1, 1),
+ "Colored Tabs", "UnreadText", SRMSGMOD_T, "tab_txt_unread", RGB(1, 1, 1),
+
+ "Colored Tabs", "NormalBG", SRMSGMOD_T, "tab_bg_normal", 0xf0f0f0,
+ "Colored Tabs", "ActiveBG", SRMSGMOD_T, "tab_bg_active", 0xf0f0f0,
+ "Colored Tabs", "HottrackBG", SRMSGMOD_T, "tab_bg_hottrack", 0xf0f0f0,
+ "Colored Tabs", "UnreadBG", SRMSGMOD_T, "tab_bg_unread", 0xf0f0f0,
+ "Chat", "Background", FONTMODULE, SRMSGSET_BKGCOLOUR_MUC, SRMSGDEFSET_BKGCOLOUR
+};
+
+/*
+ * this loads a font definition from an INI file.
+ * i = font number
+ * szKey = ini section (e.g. [Font10])
+ * *lf = pointer to a LOGFONT structure which will receive the font definition
+ * *colour = pointer to a COLORREF which will receive the color of the font definition
+*/
+static void TSAPI LoadLogfontFromINI(int i, char *szKey, LOGFONTA *lf, COLORREF *colour, const char *szIniFilename)
+{
+ int style;
+ char bSize;
+
+ if (colour)
+ *colour = GetPrivateProfileIntA(szKey, "Color", 0, szIniFilename);
+
+ if (lf) {
+ HDC hdc = GetDC(NULL);
+ if (i == H_MSGFONTID_DIVIDERS)
+ lf->lfHeight = 5;
+ else {
+ bSize = (char)GetPrivateProfileIntA(szKey, "Size", -12, szIniFilename);
+ if (bSize > 0)
+ lf->lfHeight = -MulDiv(bSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ else
+ lf->lfHeight = bSize;
+ }
+
+ ReleaseDC(NULL, hdc);
+
+ lf->lfWidth = 0;
+ lf->lfEscapement = 0;
+ lf->lfOrientation = 0;
+ style = (int)GetPrivateProfileIntA(szKey, "Style", 0, szIniFilename);
+ lf->lfWeight = style & FONTF_BOLD ? FW_BOLD : FW_NORMAL;
+ lf->lfItalic = style & FONTF_ITALIC ? 1 : 0;
+ lf->lfUnderline = style & FONTF_UNDERLINE ? 1 : 0;
+ lf->lfStrikeOut = 0;
+ if (i == MSGFONTID_SYMBOLS_IN || i == MSGFONTID_SYMBOLS_OUT)
+ lf->lfCharSet = SYMBOL_CHARSET;
+ else
+ lf->lfCharSet = (BYTE)GetPrivateProfileIntA(szKey, "Set", DEFAULT_CHARSET, szIniFilename);
+ lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = DEFAULT_QUALITY;
+ lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ if (i == MSGFONTID_SYMBOLS_IN || i == MSGFONTID_SYMBOLS_OUT) {
+ lstrcpynA(lf->lfFaceName, "Webdings", LF_FACESIZE);
+ lf->lfCharSet = SYMBOL_CHARSET;
+ } else
+ GetPrivateProfileStringA(szKey, "Face", "Tahoma", lf->lfFaceName, LF_FACESIZE - 1, szIniFilename);
+
+ /*
+ * filter out font attributes from the message input area font
+ * (can be disabled by db tweak)
+ */
+ if(!strcmp(szKey, "Font16") && M->GetByte(0, SRMSGMOD_T, "inputFontFix", 1) == 1) {
+ lf->lfWeight = FW_NORMAL;
+ lf->lfItalic = 0;
+ lf->lfUnderline = 0;
+ lf->lfStrikeOut = 0;
+ }
+ }
+}
+
+static struct _tagFontBlocks {
+ char *szModule;
+ int iFirst;
+ int iCount;
+ char *szIniTemp;
+ char *szBLockname;
+} fontBlocks[] = {
+ FONTMODULE, 0, MSGDLGFONTCOUNT, "Font%d", "StdFonts",
+ FONTMODULE, 100, IPFONTCOUNT, "IPFont%d", "MiscFonts",
+ CHAT_FONTMODULE, 0, CHATFONTCOUNT, "ChatFont%d", "ChatFonts",
+ NULL, 0, 0, NULL
+};
+
+int TSAPI CheckThemeVersion(const TCHAR *szIniFilename)
+{
+ int cookie = GetPrivateProfileInt(_T("TabSRMM Theme"), _T("Cookie"), 0, szIniFilename);
+ int version = GetPrivateProfileInt(_T("TabSRMM Theme"), _T("Version"), 0, szIniFilename);
+
+ if (version >= CURRENT_THEME_VERSION && cookie == THEME_COOKIE)
+ return 1;
+ return 0;
+}
+
+void TSAPI WriteThemeToINI(const TCHAR *szIniFilenameT, struct TWindowData *dat)
+{
+ int i, n = 0;
+ DBVARIANT dbv;
+ char szBuf[100], szTemp[100], szAppname[100];
+ COLORREF def;
+ char *szIniFilename = mir_u2a(szIniFilenameT);
+
+ WritePrivateProfileStringA("TabSRMM Theme", "Version", _itoa(CURRENT_THEME_VERSION, szBuf, 10), szIniFilename);
+ WritePrivateProfileStringA("TabSRMM Theme", "Cookie", _itoa(THEME_COOKIE, szBuf, 10), szIniFilename);
+
+ while (fontBlocks[n].szModule) {
+ int firstIndex = fontBlocks[n].iFirst;
+ char *szModule = fontBlocks[n].szModule;
+ WritePrivateProfileStringA(fontBlocks[n].szBLockname, "Valid", "1", szIniFilename);
+ for (i = 0; i < fontBlocks[n].iCount; i++) {
+ sprintf(szTemp, "Font%d", firstIndex + i);
+ sprintf(szAppname, fontBlocks[n].szIniTemp, firstIndex + i);
+ if (!DBGetContactSettingString(NULL, szModule, szTemp, &dbv)) {
+ WritePrivateProfileStringA(szAppname, "Face", dbv.pszVal, szIniFilename);
+ DBFreeVariant(&dbv);
+ }
+ sprintf(szTemp, "Font%dCol", firstIndex + i);
+ WritePrivateProfileStringA(szAppname, "Color", _itoa(M->GetDword(szModule, szTemp, 0), szBuf, 10), szIniFilename);
+ sprintf(szTemp, "Font%dSty", firstIndex + i);
+ WritePrivateProfileStringA(szAppname, "Style", _itoa(M->GetByte(szModule, szTemp, 0), szBuf, 10), szIniFilename);
+ sprintf(szTemp, "Font%dSize", firstIndex + i);
+ WritePrivateProfileStringA(szAppname, "Size", _itoa(M->GetByte(szModule, szTemp, 0), szBuf, 10), szIniFilename);
+ sprintf(szTemp, "Font%dSet", firstIndex + i);
+ WritePrivateProfileStringA(szAppname, "Set", _itoa(M->GetByte(szModule, szTemp, 0), szBuf, 10), szIniFilename);
+ }
+ n++;
+ }
+ def = SRMSGDEFSET_BKGCOLOUR;
+
+ for(i = 0; i < safe_sizeof(_extSettings); i++) {
+ WritePrivateProfileStringA(_extSettings[i].szIniSection, _extSettings[i].szIniName,
+ _itoa(M->GetDword(_extSettings[i].szDbModule, _extSettings[i].szDbSetting, _extSettings[i].dwDef), szBuf, 10), szIniFilename);
+ }
+
+ for(i = 0; i < safe_sizeof(_extSettings_v5); i++) {
+ WritePrivateProfileStringA(_extSettings_v5[i].szIniSection, _extSettings_v5[i].szIniName,
+ _itoa(M->GetDword(_extSettings_v5[i].szDbModule, _extSettings_v5[i].szDbSetting, _extSettings_v5[i].dwDef), szBuf, 10), szIniFilename);
+ }
+
+ WritePrivateProfileStringA("Message Log", "VGrid", _itoa(M->GetByte("wantvgrid", 0), szBuf, 10), szIniFilename);
+ WritePrivateProfileStringA("Message Log", "ExtraMicroLF", _itoa(M->GetByte("extramicrolf", 0), szBuf, 10), szIniFilename);
+
+ for (i = 0; i <= TMPL_ERRMSG; i++) {
+ char *encoded;
+ if (dat == 0)
+ encoded = M->utf8_encodeW(LTR_Active.szTemplates[i]);
+ else
+ encoded = M->utf8_encodeW(dat->pContainer->ltr_templates->szTemplates[i]);
+ WritePrivateProfileStringA("Templates", TemplateNames[i], encoded, szIniFilename);
+ mir_free(encoded);
+ if (dat == 0)
+ encoded = M->utf8_encodeW(RTL_Active.szTemplates[i]);
+ else
+ encoded = M->utf8_encodeW(dat->pContainer->rtl_templates->szTemplates[i]);
+ WritePrivateProfileStringA("RTLTemplates", TemplateNames[i], encoded, szIniFilename);
+ mir_free(encoded);
+ }
+ for (i = 0; i < CUSTOM_COLORS; i++) {
+ sprintf(szTemp, "cc%d", i + 1);
+ if (dat == 0)
+ WritePrivateProfileStringA("Custom Colors", szTemp, _itoa(M->GetDword(szTemp, 0), szBuf, 10), szIniFilename);
+ else
+ WritePrivateProfileStringA("Custom Colors", szTemp, _itoa(dat->pContainer->theme.custom_colors[i], szBuf, 10), szIniFilename);
+ }
+ for (i = 0; i <= 7; i++)
+ WritePrivateProfileStringA("Nick Colors", _itoa(i, szBuf, 10), _itoa(g_Settings.nickColors[i], szTemp, 10), szIniFilename);
+
+ mir_free(szIniFilename);
+}
+
+void TSAPI ReadThemeFromINI(const TCHAR *szIniFilenameT, TContainerData *dat, int noAdvanced, DWORD dwFlags)
+{
+ char szBuf[512], szTemp[100], szAppname[100];
+ int i, n = 0;
+ int version;
+ COLORREF def;
+ char *szIniFilename = mir_u2a(szIniFilenameT);
+ char szTemplateBuffer[TEMPLATE_LENGTH * 3 + 2];
+ char bSize = 0;
+ HDC hdc;
+ int charset;
+
+ if ((version = GetPrivateProfileIntA("TabSRMM Theme", "Version", 0, szIniFilename)) == 0) // no version number.. assume 1
+ version = 1;
+
+ if (dat == 0) {
+ hdc = GetDC(NULL);
+
+ while (fontBlocks[n].szModule && (dwFlags & THEME_READ_FONTS)) {
+ char *szModule = fontBlocks[n].szModule;
+ int firstIndex = fontBlocks[n].iFirst;
+
+ if (n != 1 && !(dwFlags & THEME_READ_FONTS)) {
+ n++;
+ continue;
+ }
+ if (GetPrivateProfileIntA(fontBlocks[n].szBLockname, "Valid", 0, szIniFilename) == 0 && n != 0) {
+ n++;
+ continue;
+ }
+ for (i = 0; i < fontBlocks[n].iCount; i++) {
+ sprintf(szTemp, "Font%d", firstIndex + i);
+ sprintf(szAppname, fontBlocks[n].szIniTemp, firstIndex + i);
+ if (GetPrivateProfileStringA(szAppname, "Face", "Verdana", szBuf, sizeof(szBuf), szIniFilename) != 0) {
+ if (i == MSGFONTID_SYMBOLS_IN || i == MSGFONTID_SYMBOLS_OUT)
+ lstrcpynA(szBuf, "Arial", sizeof(szBuf));
+ DBWriteContactSettingString(NULL, szModule, szTemp, szBuf);
+ }
+
+ sprintf(szTemp, "Font%dCol", firstIndex + i);
+ M->WriteDword(szModule, szTemp, GetPrivateProfileIntA(szAppname, "Color", GetSysColor(COLOR_WINDOWTEXT), szIniFilename));
+
+ sprintf(szTemp, "Font%dSty", firstIndex + i);
+ M->WriteByte(szModule, szTemp, (BYTE)(GetPrivateProfileIntA(szAppname, "Style", 0, szIniFilename)));
+
+ sprintf(szTemp, "Font%dSize", firstIndex + i);
+ bSize = (char)GetPrivateProfileIntA(szAppname, "Size", -10, szIniFilename);
+ if (bSize > 0)
+ bSize = -MulDiv(bSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ M->WriteByte(szModule, szTemp, bSize);
+
+ sprintf(szTemp, "Font%dSet", firstIndex + i);
+ charset = GetPrivateProfileIntA(szAppname, "Set", 0, szIniFilename);
+ if (i == MSGFONTID_SYMBOLS_IN || i == MSGFONTID_SYMBOLS_OUT)
+ charset = 0;
+ M->WriteByte(szModule, szTemp, (BYTE)charset);
+ }
+ n++;
+ }
+ def = SRMSGDEFSET_BKGCOLOUR;
+ ReleaseDC(NULL, hdc);
+
+ if (dwFlags & THEME_READ_FONTS) {
+ COLORREF defclr;
+
+ for(i = 0; i < safe_sizeof(_extSettings); i++) {
+ M->WriteDword(_extSettings[i].szDbModule, _extSettings[i].szDbSetting,
+ GetPrivateProfileIntA(_extSettings[i].szIniSection, _extSettings[i].szIniName, _extSettings[i].dwDef, szIniFilename));
+ }
+
+ if(version >= 5) {
+ for(i = 0; i < safe_sizeof(_extSettings_v5); i++) {
+ M->WriteDword(_extSettings_v5[i].szDbModule, _extSettings_v5[i].szDbSetting,
+ GetPrivateProfileIntA(_extSettings_v5[i].szIniSection, _extSettings_v5[i].szIniName, _extSettings_v5[i].dwDef, szIniFilename));
+ }
+ }
+
+ M->WriteByte(SRMSGMOD_T, "wantvgrid", (BYTE)(GetPrivateProfileIntA("Message Log", "VGrid", 0, szIniFilename)));
+ M->WriteByte(SRMSGMOD_T, "extramicrolf", (BYTE)(GetPrivateProfileIntA("Message Log", "ExtraMicroLF", 0, szIniFilename)));
+
+ for (i = 0; i < CUSTOM_COLORS; i++) {
+ sprintf(szTemp, "cc%d", i + 1);
+ if (dat == 0)
+ M->WriteDword(SRMSGMOD_T, szTemp, GetPrivateProfileIntA("Custom Colors", szTemp, RGB(224, 224, 224), szIniFilename));
+ else
+ dat->theme.custom_colors[i] = GetPrivateProfileIntA("Custom Colors", szTemp, RGB(224, 224, 224), szIniFilename);
+ }
+ for (i = 0; i <= 7; i++) {
+ if (i == 5)
+ defclr = GetSysColor(COLOR_HIGHLIGHT);
+ else if (i == 6)
+ defclr = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ else
+ defclr = g_Settings.crUserListColor;
+ g_Settings.nickColors[i] = GetPrivateProfileIntA("Nick Colors", _itoa(i, szTemp, 10), defclr, szIniFilename);
+ sprintf(szTemp, "NickColor%d", i);
+ M->WriteDword("Chat", szTemp, g_Settings.nickColors[i]);
+ }
+ }
+ } else {
+ HDC hdc = GetDC(NULL);
+ int SY = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(NULL, hdc);
+ if (!noAdvanced) {
+ for (i = 0; i < MSGDLGFONTCOUNT; i++) {
+ _snprintf(szTemp, 20, "Font%d", i);
+ LoadLogfontFromINI(i, szTemp, &dat->theme.logFonts[i], &dat->theme.fontColors[i], szIniFilename);
+ wsprintfA(dat->theme.rtfFonts + (i * RTFCACHELINESIZE), "\\f%u\\cf%u\\b%d\\i%d\\ul%d\\fs%u", i, i, dat->theme.logFonts[i].lfWeight >= FW_BOLD ? 1 : 0, dat->theme.logFonts[i].lfItalic, dat->theme.logFonts[i].lfUnderline, 2 * abs(dat->theme.logFonts[i].lfHeight) * 74 / SY);
+ }
+ wsprintfA(dat->theme.rtfFonts + (MSGDLGFONTCOUNT * RTFCACHELINESIZE), "\\f%u\\cf%u\\b%d\\i%d\\ul%d\\fs%u", MSGDLGFONTCOUNT, MSGDLGFONTCOUNT, 0, 0, 0, 0);
+ }
+ dat->theme.bg = GetPrivateProfileIntA("Message Log", "BackgroundColor", RGB(224, 224, 224), szIniFilename);
+ dat->theme.inbg = GetPrivateProfileIntA("Message Log", "IncomingBG", RGB(224, 224, 224), szIniFilename);
+ dat->theme.outbg = GetPrivateProfileIntA("Message Log", "OutgoingBG", RGB(224, 224, 224), szIniFilename);
+
+ dat->theme.oldinbg = GetPrivateProfileIntA("Message Log", "OldIncomingBG", RGB(224, 224, 224), szIniFilename);
+ dat->theme.oldoutbg = GetPrivateProfileIntA("Message Log", "OldOutgoingBG", RGB(224, 224, 224), szIniFilename);
+ dat->theme.statbg = GetPrivateProfileIntA("Message Log", "StatusBG", RGB(224, 224, 224), szIniFilename);
+
+ dat->theme.inputbg = GetPrivateProfileIntA("Message Log", "InputBG", RGB(224, 224, 224), szIniFilename);
+ dat->theme.hgrid = GetPrivateProfileIntA("Message Log", "HgridColor", RGB(224, 224, 224), szIniFilename);
+ dat->theme.dwFlags = GetPrivateProfileIntA("Message Log", "DWFlags", MWF_LOG_DEFAULT, szIniFilename);
+ M->WriteByte(SRMSGMOD_T, "wantvgrid", (BYTE)(GetPrivateProfileIntA("Message Log", "VGrid", 0, szIniFilename)));
+ M->WriteByte(SRMSGMOD_T, "extramicrolf", (BYTE)(GetPrivateProfileIntA("Message Log", "ExtraMicroLF", 0, szIniFilename)));
+
+ dat->theme.left_indent = GetPrivateProfileIntA("Message Log", "LeftIndent", 0, szIniFilename);
+ dat->theme.right_indent = GetPrivateProfileIntA("Message Log", "RightIndent", 0, szIniFilename);
+
+ for (i = 0; i < CUSTOM_COLORS; i++) {
+ sprintf(szTemp, "cc%d", i + 1);
+ if (dat == 0)
+ M->WriteDword(SRMSGMOD_T, szTemp, GetPrivateProfileIntA("Custom Colors", szTemp, RGB(224, 224, 224), szIniFilename));
+ else
+ dat->theme.custom_colors[i] = GetPrivateProfileIntA("Custom Colors", szTemp, RGB(224, 224, 224), szIniFilename);
+ }
+ }
+
+ if (version >= 3) {
+ if (!noAdvanced && dwFlags & THEME_READ_TEMPLATES) {
+ for (i = 0; i <= TMPL_ERRMSG; i++) {
+ wchar_t *decoded = 0;
+
+ GetPrivateProfileStringA("Templates", TemplateNames[i], "[undef]", szTemplateBuffer, TEMPLATE_LENGTH * 3, szIniFilename);
+
+ if (strcmp(szTemplateBuffer, "[undef]")) {
+ if (dat == 0)
+ DBWriteContactSettingStringUtf(NULL, TEMPLATES_MODULE, TemplateNames[i], szTemplateBuffer);
+ decoded = M->utf8_decodeW(szTemplateBuffer);
+ if (dat == 0)
+ mir_sntprintf(LTR_Active.szTemplates[i], TEMPLATE_LENGTH, L"%s", decoded);
+ else
+ mir_sntprintf(dat->ltr_templates->szTemplates[i], TEMPLATE_LENGTH, L"%s", decoded);
+ mir_free(decoded);
+ }
+
+ GetPrivateProfileStringA("RTLTemplates", TemplateNames[i], "[undef]", szTemplateBuffer, TEMPLATE_LENGTH * 3, szIniFilename);
+
+ if (strcmp(szTemplateBuffer, "[undef]")) {
+ if (dat == 0)
+ DBWriteContactSettingStringUtf(NULL, RTLTEMPLATES_MODULE, TemplateNames[i], szTemplateBuffer);
+ decoded = M->utf8_decodeW(szTemplateBuffer);
+ if (dat == 0)
+ mir_sntprintf(RTL_Active.szTemplates[i], TEMPLATE_LENGTH, L"%s", decoded);
+ else
+ mir_sntprintf(dat->rtl_templates->szTemplates[i], TEMPLATE_LENGTH, L"%s", decoded);
+ mir_free(decoded);
+ }
+ }
+ }
+ }
+ if (PluginConfig.m_chat_enabled)
+ LoadGlobalSettings();
+ mir_free(szIniFilename);
+}
+
+/*
+ * iMode = 0 - GetOpenFilename, otherwise, GetSaveAs...
+ */
+
+const TCHAR* TSAPI GetThemeFileName(int iMode)
+{
+ static TCHAR szFilename[MAX_PATH];
+ OPENFILENAME ofn = {0};
+ TCHAR szInitialDir[MAX_PATH];
+
+ mir_sntprintf(szInitialDir, MAX_PATH, _T("%s"), M->getSkinPath());
+
+ szFilename[0] = 0;
+
+ TCHAR filter[MAX_PATH];
+ mir_sntprintf(filter, SIZEOF(filter), _T("%s%c*.tabsrmm%c%c"), TranslateT("tabSRMM themes"), 0, 0, 0);
+ ofn.lpstrFilter = filter;
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = 0;
+ ofn.lpstrFile = szFilename;
+ ofn.lpstrInitialDir = szInitialDir;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ ofn.lpstrDefExt = _T("tabsrmm");
+ if (!iMode) {
+ if (GetOpenFileName(&ofn))
+ return szFilename;
+ else
+ return NULL;
+ } else {
+ if (GetSaveFileName(&ofn))
+ return szFilename;
+ else
+ return NULL;
+ }
+}
+
diff --git a/plugins/TabSRMM/src/themes.cpp b/plugins/TabSRMM/src/themes.cpp new file mode 100644 index 0000000000..65c1ec8e2f --- /dev/null +++ b/plugins/TabSRMM/src/themes.cpp @@ -0,0 +1,2871 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: themes.cpp 12512 2010-08-28 22:03:43Z silvercircle $
+ *
+ * Implements the skinning engine and most parts of the aero support in
+ * tabSRMM 3.x+
+ *
+ */
+
+#include "commonheaders.h"
+#include <stdexcept>
+#pragma hdrstop
+
+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] = {
+ {
+ _T("No effect"), 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0
+ },
+ {
+ _T("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 */
+ },
+ {
+ _T("Carbon"),
+ 0xf0f0f0,
+ 0x000000,
+ 75,
+ 0,
+ CORNER_ALL,
+ GRADIENT_TB + 1,
+ 6,
+ 8,
+ 0,
+ 0xf0f0f0,
+ 0,
+ AeroEffectCallback_Carbon
+ },
+ {
+ _T("Semi transparent, custom colors"),
+ 0xffffff,
+ 0x444444,
+ 60,
+ 0,
+ CORNER_ALL,
+ GRADIENT_TB + 1,
+ 6,
+ 0,
+ 0x0,
+ 0xf0f0f0,
+ 0,
+ AeroEffectCallback_Solid
+ },
+ {
+ _T("Silver shadow"),
+ 0xffffff,
+ 0xa0a0a0,
+ 80,
+ 220,
+ 0,
+ GRADIENT_TB + 1,
+ 1,
+ 0,
+ 0xc0c0c0,
+ 0xf0f0f0,
+ 0x707070,
+ AeroEffectCallback_Solid
+ },
+ {
+ _T("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 || CMimAPI::m_MyGradientFill == 0 || CMimAPI::m_MyAlphaBlend == 0)
+ 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;
+
+ CMimAPI::m_MyGradientFill(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
+
+ CMimAPI::m_MyAlphaBlend(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));
+ }
+ }
+ }
+ }
+ CMimAPI::m_MyAlphaBlend(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));
+ }
+ }
+ }
+ }
+ CMimAPI::m_MyAlphaBlend(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 (CMimAPI::m_MyAlphaBlend == 0)
+ return;
+
+ 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
+
+ CMimAPI::m_MyAlphaBlend(hdc, rc->left, rc->top, l, t, hdcSrc, srcOrigX, srcOrigY, l, t, m_bf);
+ CMimAPI::m_MyAlphaBlend(hdc, rc->left + l, rc->top, width - l - r, t, hdcSrc, srcOrigX + l, srcOrigY, m_inner_width, t, m_bf);
+ CMimAPI::m_MyAlphaBlend(hdc, rc->right - r, rc->top, r, t, hdcSrc, srcOrigX + (m_width - r), srcOrigY, r, t, m_bf);
+
+ // middle 3 items
+
+ CMimAPI::m_MyAlphaBlend(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
+ CMimAPI::m_MyAlphaBlend(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);
+
+ CMimAPI::m_MyAlphaBlend(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
+
+ CMimAPI::m_MyAlphaBlend(hdc, rc->left, rc->bottom - b, l, b, hdcSrc, srcOrigX, srcOrigY + (m_height - b), l, b, m_bf);
+ CMimAPI::m_MyAlphaBlend(hdc, rc->left + l, rc->bottom - b, width - l - r, b, hdcSrc, srcOrigX + l, srcOrigY + (m_height - b), m_inner_width, b, m_bf);
+ CMimAPI::m_MyAlphaBlend(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) {
+ CMimAPI::m_MyAlphaBlend(hdc, rc->left, top, width, m_height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf);
+ top += m_height;
+ } else {
+ CMimAPI::m_MyAlphaBlend(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) {
+ CMimAPI::m_MyAlphaBlend(hdc, left, rc->top, m_width, height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf);
+ left += m_width;
+ } else {
+ CMimAPI::m_MyAlphaBlend(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...
+ CMimAPI::m_MyAlphaBlend(hdc, rc->left, rc->top, width, height, hdcSrc, srcOrigX, srcOrigY, m_width, m_height, m_bf);
+ break;
+ /*
+ case IMAGE_STRETCH_V:
+ // stretch vertically, draw 3 horizontal tiles...
+ AlphaBlend(hdc, rc->left, rc->top, l, height, item->hdc, 0, 0, l, item->height, item->bf);
+ AlphaBlend(hdc, rc->left + l, rc->top, width - l - r, height, item->hdc, l, 0, item->inner_width, item->height, item->bf);
+ AlphaBlend(hdc, rc->right - r, rc->top, r, height, item->hdc, item->width - r, 0, r, item->height, item->bf);
+ break;
+ case IMAGE_STRETCH_H:
+ // stretch horizontally, draw 3 vertical tiles...
+ AlphaBlend(hdc, rc->left, rc->top, width, t, item->hdc, 0, 0, item->width, t, item->bf);
+ AlphaBlend(hdc, rc->left, rc->top + t, width, height - t - b, item->hdc, 0, t, item->width, item->inner_height, item->bf);
+ AlphaBlend(hdc, rc->left, rc->bottom - b, width, b, item->hdc, 0, item->height - b, item->width, b, item->bf);
+ break;
+ */
+ default:
+ 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;
+ DWORD dwLen;
+ BYTE *p;
+ int x, y;
+ BOOL fixIt = TRUE;
+
+ GetObject(hBitmap, sizeof(bmp), &bmp);
+
+ if (bmp.bmBitsPixel != 32)
+ return;
+
+ dwLen = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
+ p = (BYTE *)malloc(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);
+ free(p);
+}
+
+void CImageItem::PreMultiply(HBITMAP hBitmap, int mode)
+{
+ BYTE *p = NULL;
+ DWORD dwLen;
+ int width, height, x, y;
+ BITMAP bmp;
+ BYTE alpha;
+
+ ::GetObject(hBitmap, sizeof(bmp), &bmp);
+ width = bmp.bmWidth;
+ height = bmp.bmHeight;
+ dwLen = width * height * 4;
+ p = (BYTE *)malloc(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) {
+ if (mode) {
+ 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);
+ 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 *)malloc(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);
+ 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 == M->GetTString(0, SRMSGMOD_T, "ContainerSkin", &dbv)) {
+ M->pathToAbsolute(dbv.ptszVal, m_tszFileName, M->getSkinPath());
+ m_tszFileName[MAX_PATH - 1] = 0;
+ DBFreeVariant(&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;
+ ZeroMemory(this, sizeof(CSkin));
+ 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) {
+ if (MessageBox(0, CTranslator::get(CTranslator::GEN_SKIN_WARNCLOSE),
+ CTranslator::get(CTranslator::GEN_SKIN_WARNCLOSE_TITLE), MB_YESNO | MB_ICONQUESTION) == IDYES) {
+ TContainerData *pContainer = ::pFirstContainer;
+ while (pFirstContainer)
+ SendMessage(pFirstContainer->hwnd, WM_CLOSE, 0, 1);
+ return true;
+ } else
+ return false;
+ }
+ return true;
+}
+
+/**
+ * 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()
+{
+ int i;
+ CImageItem *tmp = m_ImageItems, *nextItem = 0;
+
+ /*
+ * delete all image items
+ */
+
+ if(warnToClose() == false)
+ return; // do nothing when user decides to not close any window
+
+ m_skinEnabled = m_frameSkins = false;
+
+ 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);
+
+ m_ContainerColorKeyBrush = m_MenuBGBrush = 0;
+
+ if(m_SkinLightShadowPen)
+ ::DeleteObject(m_SkinLightShadowPen);
+ m_SkinLightShadowPen = 0;
+
+ if(m_SkinDarkShadowPen)
+ ::DeleteObject(m_SkinDarkShadowPen);
+ m_SkinDarkShadowPen = 0;
+
+ for(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(i = 0; i < m_nrSkinIcons; i++) {
+ if(m_skinIcons[i].phIcon )
+ ::DestroyIcon(*(m_skinIcons[i].phIcon));
+ }
+ 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));
+ _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));
+ _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;
+}
+
+/* DISABLED code
+
+void CSkin::ReadButtonItem(const TCHAR *itemName) const
+{
+ ButtonItem tmpItem, *newItem;
+ TCHAR szBuffer[1024];
+ char szBufferA[1024];
+ CImageItem *imgItem = m_ImageItems;
+ HICON *phIcon;
+
+ ZeroMemory(&tmpItem, sizeof(tmpItem));
+ mir_snprintf(tmpItem.szName, safe_sizeof(tmpItem.szName), "%s", &szItemNameA[1]);
+ tmpItem.width = GetPrivateProfileInt(itemName, _T("Width"), 16, m_tszFileName);
+ tmpItem.height = GetPrivateProfileInt(itemName,_T( "Height"), 16, m_tszFileName);
+ tmpItem.xOff = GetPrivateProfileInt(itemName, _T("xoff"), 0, m_tszFileName);
+ tmpItem.yOff = GetPrivateProfileInt(itemName, _T("yoff"), 0, m_tszFileName);
+
+ tmpItem.dwFlags |= GetPrivateProfileInt(itemName, _T("toggle"), 0, m_tszFileName) ? BUTTON_ISTOGGLE : 0;
+
+ GetPrivateProfileString(itemName, _T("Pressed"), _T("None"), szBuffer, 1000, m_tszFileName);
+ if (!_tcsicmp(szBuffer, _T("default")))
+ tmpItem.imgPressed = SkinItems[ID_EXTBKBUTTONSPRESSED].imageItem;
+ else {
+ while (imgItem) {
+ if (!_tcsicmp(imgItem->getName(), szBuffer)) {
+ tmpItem.imgPressed = imgItem;
+ break;
+ }
+ imgItem = imgItem->getNextItem();
+ }
+ }
+
+ imgItem = m_ImageItems;
+ GetPrivateProfileString(itemName, _T("Normal"), _T("None"), szBuffer, 1000, m_tszFileName);
+ if (!_tcsicmp(szBuffer, _T("default")))
+ tmpItem.imgNormal = SkinItems[ID_EXTBKBUTTONSNPRESSED].imageItem;
+ else {
+ while (imgItem) {
+ if (!_tcsicmp(imgItem->getName(), szBuffer)) {
+ tmpItem.imgNormal = imgItem;
+ break;
+ }
+ imgItem = imgItem->getNextItem();
+ }
+ }
+
+ imgItem = m_ImageItems;
+ GetPrivateProfileString(itemName, _T("Hover"), _T("None"), szBuffer, 1000, m_tszFileName);
+ if (!_tcsicmp(szBuffer, _T("default")))
+ tmpItem.imgHover = SkinItems[ID_EXTBKBUTTONSMOUSEOVER].imageItem;
+ else {
+ while (imgItem) {
+ if (!_tcsicmp(imgItem->getName(), szBuffer)) {
+ tmpItem.imgHover = imgItem;
+ break;
+ }
+ imgItem = imgItem->getNextItem();
+ }
+ }
+
+ tmpItem.uId = IDC_TBFIRSTUID - 1;
+ tmpItem.pfnAction = tmpItem.pfnCallback = NULL;
+
+ GetPrivateProfileString(itemName, _T("Action"), _T("Custom"), szBuffer, 1000, m_tszFileName);
+ if (!_tcsicmp(szBuffer, _T("service"))) {
+ tmpItem.szService[0] = 0;
+ GetPrivateProfileStringA(szItemNameA, "Service", "None", szBufferA, 1000, szFileNameA);
+ if (_stricmp(szBufferA, "None")) {
+ mir_snprintf(tmpItem.szService, 256, "%s", szBufferA);
+ tmpItem.dwFlags |= BUTTON_ISSERVICE;
+ tmpItem.uId = nextButtonID++;
+ }
+ } else if (!_tcsicmp(szBuffer, _T("protoservice"))) {
+ tmpItem.szService[0] = 0;
+ GetPrivateProfileStringA(szItemNameA, "Service", "None", szBufferA, 1000, szFileNameA);
+ if (_stricmp(szBufferA, "None")) {
+ mir_snprintf(tmpItem.szService, 256, "%s", szBufferA);
+ tmpItem.dwFlags |= BUTTON_ISPROTOSERVICE;
+ tmpItem.uId = nextButtonID++;
+ }
+ } else if (!_tcsicmp(szBuffer, _T("database"))) {
+ int n;
+
+ GetPrivateProfileStringA(szItemNameA, "Module", "None", szBufferA, 1000, szFileNameA);
+ if (_stricmp(szBufferA, "None"))
+ mir_snprintf(tmpItem.szModule, 256, "%s", szBufferA);
+ GetPrivateProfileStringA(szItemNameA, "Setting", "None", szBufferA, 1000, szFileNameA);
+ if (_stricmp(szBufferA, "None"))
+ mir_snprintf(tmpItem.szSetting, 256, "%s", szBufferA);
+ if (GetPrivateProfileIntA(szItemNameA, "contact", 0, szFileNameA) != 0)
+ tmpItem.dwFlags |= BUTTON_DBACTIONONCONTACT;
+
+ for (n = 0; n <= 1; n++) {
+ char szKey[20];
+ BYTE *pValue;
+
+ strcpy(szKey, n == 0 ? "dbonpush" : "dbonrelease");
+ pValue = (n == 0 ? tmpItem.bValuePush : tmpItem.bValueRelease);
+
+ GetPrivateProfileStringA(szItemNameA, szKey, "None", szBufferA, 1000, szFileNameA);
+ switch (szBufferA[0]) {
+ case 'b': {
+ BYTE value = (BYTE)atol(&szBufferA[1]);
+ pValue[0] = value;
+ tmpItem.type = DBVT_BYTE;
+ break;
+ }
+ case 'w': {
+ WORD value = (WORD)atol(&szBufferA[1]);
+ *((WORD *)&pValue[0]) = value;
+ tmpItem.type = DBVT_WORD;
+ break;
+ }
+ case 'd': {
+ DWORD value = (DWORD)atol(&szBufferA[1]);
+ *((DWORD *)&pValue[0]) = value;
+ tmpItem.type = DBVT_DWORD;
+ break;
+ }
+ case 's': {
+ mir_snprintf((char *)pValue, 256, &szBufferA[1]);
+ tmpItem.type = DBVT_ASCIIZ;
+ break;
+ }
+ }
+ }
+ if (tmpItem.szModule[0] && tmpItem.szSetting[0]) {
+ tmpItem.dwFlags |= BUTTON_ISDBACTION;
+ if (tmpItem.szModule[0] == '$' && (tmpItem.szModule[1] == 'c' || tmpItem.szModule[1] == 'C'))
+ tmpItem.dwFlags |= BUTTON_ISCONTACTDBACTION;
+ tmpItem.uId = nextButtonID++;
+ }
+ } else if (_tcsicmp(szBuffer, _T("Custom"))) {
+ if (BTN_GetStockItem(&tmpItem, szBuffer))
+ goto create_it;
+ }
+ GetPrivateProfileString(itemName, _T("PassContact"), _T("None"), szBuffer, 1000, m_tszFileName);
+ if (_tcsicmp(szBuffer, _T("None"))) {
+ if (szBuffer[0] == 'w' || szBuffer[0] == 'W')
+ tmpItem.dwFlags |= BUTTON_PASSHCONTACTW;
+ else if (szBuffer[0] == 'l' || szBuffer[0] == 'L')
+ tmpItem.dwFlags |= BUTTON_PASSHCONTACTL;
+ }
+
+ GetPrivateProfileString(itemName, _T("Tip"), _T("None"), szBuffer, 1000, m_tszFileName);
+ if (_tcsicmp(szBuffer, _T("None"))) {
+ mir_sntprintf(tmpItem.szTip, 256, _T("%s"), szBuffer);
+ } else
+ tmpItem.szTip[0] = 0;
+
+create_it:
+
+ GetPrivateProfileString(itemName, _T("Label"), _T("None"), szBuffer, 40, m_tszFileName);
+ if (_tcsicmp(szBuffer, _T("None"))) {
+ mir_sntprintf(tmpItem.tszLabel, 40, _T("%s"), szBuffer);
+ tmpItem.dwFlags |= BUTTON_HASLABEL;
+ } else
+ tmpItem.tszLabel[0] = 0;
+
+ GetPrivateProfileString(itemName, _T("NormalGlyph"), _T("0, 0, 0, 0"), szBuffer, 1000, m_tszFileName);
+ if (_tcsicmp(szBuffer, _T("default"))) {
+ tmpItem.dwFlags &= ~BUTTON_NORMALGLYPHISICON;
+ if ((phIcon = BTN_GetIcon(szBuffer)) != 0) {
+ tmpItem.dwFlags |= BUTTON_NORMALGLYPHISICON;
+ tmpItem.normalGlyphMetrics[0] = (LONG_PTR)phIcon;
+ } else {
+ _tscanf(szBuffer, _T("%d,%d,%d,%d"), &tmpItem.normalGlyphMetrics[0], &tmpItem.normalGlyphMetrics[1],
+ &tmpItem.normalGlyphMetrics[2], &tmpItem.normalGlyphMetrics[3]);
+ tmpItem.normalGlyphMetrics[2] = (tmpItem.normalGlyphMetrics[2] - tmpItem.normalGlyphMetrics[0]) + 1;
+ tmpItem.normalGlyphMetrics[3] = (tmpItem.normalGlyphMetrics[3] - tmpItem.normalGlyphMetrics[1]) + 1;
+ }
+ }
+
+ GetPrivateProfileString(itemName, _T("PressedGlyph"), _T("0, 0, 0, 0"), szBuffer, 1000, m_tszFileName);
+ if (_tcsicmp(szBuffer, _T("default"))) {
+ tmpItem.dwFlags &= ~BUTTON_PRESSEDGLYPHISICON;
+ if ((phIcon = BTN_GetIcon(szBuffer)) != 0) {
+ tmpItem.pressedGlyphMetrics[0] = (LONG_PTR)phIcon;
+ tmpItem.dwFlags |= BUTTON_PRESSEDGLYPHISICON;
+ } else {
+ _tscanf(szBuffer, _T("%d,%d,%d,%d"), &tmpItem.pressedGlyphMetrics[0], &tmpItem.pressedGlyphMetrics[1],
+ &tmpItem.pressedGlyphMetrics[2], &tmpItem.pressedGlyphMetrics[3]);
+ tmpItem.pressedGlyphMetrics[2] = (tmpItem.pressedGlyphMetrics[2] - tmpItem.pressedGlyphMetrics[0]) + 1;
+ tmpItem.pressedGlyphMetrics[3] = (tmpItem.pressedGlyphMetrics[3] - tmpItem.pressedGlyphMetrics[1]) + 1;
+ }
+ }
+
+ GetPrivateProfileString(itemName, _T("HoverGlyph"), _T("0, 0, 0, 0"), szBuffer, 1000, m_tszFileName);
+ if (_tcsicmp(szBuffer, _T("default"))) {
+ tmpItem.dwFlags &= ~BUTTON_HOVERGLYPHISICON;
+ if ((phIcon = BTN_GetIcon(szBuffer)) != 0) {
+ tmpItem.hoverGlyphMetrics[0] = (LONG_PTR)phIcon;
+ tmpItem.dwFlags |= BUTTON_HOVERGLYPHISICON;
+ } else {
+ _tscanf(szBuffer, _T("%d,%d,%d,%d"), &tmpItem.hoverGlyphMetrics[0], &tmpItem.hoverGlyphMetrics[1],
+ &tmpItem.hoverGlyphMetrics[2], &tmpItem.hoverGlyphMetrics[3]);
+ tmpItem.hoverGlyphMetrics[2] = (tmpItem.hoverGlyphMetrics[2] - tmpItem.hoverGlyphMetrics[0]) + 1;
+ tmpItem.hoverGlyphMetrics[3] = (tmpItem.hoverGlyphMetrics[3] - tmpItem.hoverGlyphMetrics[1]) + 1;
+ }
+ }
+
+ newItem = (ButtonItem *)malloc(sizeof(ButtonItem));
+ ZeroMemory(newItem, sizeof(ButtonItem));
+ if (g_ButtonSet.items == NULL) {
+ g_ButtonSet.items = newItem;
+ *newItem = tmpItem;
+ newItem->nextItem = 0;
+ } else {
+ ButtonItem *curItem = g_ButtonSet.items;
+ while (curItem->nextItem)
+ curItem = curItem->nextItem;
+ *newItem = tmpItem;
+ newItem->nextItem = 0;
+ curItem->nextItem = newItem;
+ }
+#ifdef _UNICODE
+ mir_free((void *)szItemNameA);
+#endif
+ return;
+}
+
+*/
+
+/**
+ * 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()
+{
+ if(warnToClose() == false)
+ return;
+
+ if(m_skinEnabled) {
+ Unload();
+ m_skinEnabled = false;
+ }
+
+ m_fHaveGlyph = false;
+
+ if(m_tszFileName[0]) {
+ if(::PathFileExists(m_tszFileName)) {
+ TCHAR *p;
+ TCHAR *szSections = (TCHAR *)malloc(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:
+ M->WriteByte(SRMSGMOD_T, _tagSettings[i].szSetting, (BYTE)data);
+ break;
+ case 4:
+ M->WriteDword(SRMSGMOD_T, _tagSettings[i].szSetting, data);
+ break;
+ case 2:
+ DBWriteContactSettingWord(NULL, SRMSGMOD_T, _tagSettings[i].szSetting, (WORD)data);
+ break;
+ case 5:
+ GetPrivateProfileString(_tagSettings[i].szIniKey, _tagSettings[i].szIniName, _T("000000"),
+ buffer, 10, m_tszFileName);
+ M->WriteDword(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;
+ 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 *)malloc(sizeof(TIconDescW) * NR_MAXSKINICONS);
+
+ ZeroMemory(m_skinIcons, sizeof(TIconDesc) * NR_MAXSKINICONS);
+ m_nrSkinIcons = 0;
+
+ szSections = (TCHAR *)malloc((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 *)malloc(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);
+ }
+ */
+ 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<HBITMAP>(::SelectObject(m_tabCloseHDC, m_tabCloseBitmap));
+
+ if(M->isVSThemed() || M->isAero()) {
+ ::FillRect(m_tabCloseHDC, &rc, M->isAero() ? reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)) : ::GetSysColorBrush(COLOR_3DFACE));
+
+ HANDLE hTheme = CMimAPI::m_pfnOpenThemeData(PluginConfig.g_hwndHotkeyHandler, L"BUTTON");
+ rc.left--; rc.right++;
+ rc.top--; rc.bottom++;
+ CMimAPI::m_pfnDrawThemeParentBackground(PluginConfig.g_hwndHotkeyHandler, m_tabCloseHDC, &rc);
+ CMimAPI::m_pfnDrawThemeBackground(hTheme, m_tabCloseHDC, 1, 1, &rc, &rc);
+ CMimAPI::m_pfnCloseThemeData(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<HBITMAP>(::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()
+{
+ TCHAR tszFilename[MAX_PATH], tszBasePath[MAX_PATH];
+
+ M->getAeroState();
+ UnloadAeroTabs();
+
+ BOOL isOpaque;
+ HBITMAP hbm;
+ BITMAP bm;
+
+ if(!m_fAeroSkinsValid)
+ return;
+
+ mir_sntprintf(tszBasePath, MAX_PATH, _T("%s"), M->getDataPath());
+ 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);
+
+ 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);
+
+ hbm = FIF->FI_CreateHBITMAPFromDIB(fib);
+
+ CImageItem::Colorize(hbm, GetRValue(m_dwmColorRGB),
+ GetGValue(m_dwmColorRGB),
+ GetBValue(m_dwmColorRGB));
+
+ CImageItem::PreMultiply(hbm, 1);
+
+ 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, struct 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 transparent avatar image. Get around crappy image rescaling quality of the
+ * AlphaBlend() API.
+ *
+ * hdcMem contains the bitmap to draw (must be premultiplied for proper per-pixel alpha
+ * rendering in AlphaBlend().
+ */
+
+void CSkin::MY_AlphaBlend(HDC hdcDraw, DWORD left, DWORD top, int width, int height, int bmWidth, int bmHeight, HDC hdcMem)
+{
+ HDC hdcTemp = CreateCompatibleDC(hdcDraw);
+ HBITMAP hbmTemp = CreateCompatibleBitmap(hdcMem, bmWidth, bmHeight);
+ HBITMAP hbmOld = (HBITMAP)SelectObject(hdcTemp, hbmTemp);
+
+ SetStretchBltMode(hdcTemp, HALFTONE);
+ StretchBlt(hdcTemp, 0, 0, bmWidth, bmHeight, hdcDraw, left, top, width, height, SRCCOPY);
+ if (CMimAPI::m_MyAlphaBlend)
+ CMimAPI::m_MyAlphaBlend(hdcTemp, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, bmWidth, bmHeight, CSkin::m_default_bf);
+ else {
+ SetStretchBltMode(hdcTemp, HALFTONE);
+ StretchBlt(hdcTemp, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, bmWidth, bmHeight, SRCCOPY);
+ }
+ StretchBlt(hdcDraw, left, top, width, height, hdcTemp, 0, 0, bmWidth, bmHeight, SRCCOPY);
+ SelectObject(hdcTemp, hbmOld);
+ DeleteObject(hbmTemp);
+ DeleteDC(hdcTemp);
+}
+
+/**
+ * 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<HBITMAP>(::SelectObject(dcMem, hbm));
+ ::DrawIconEx(dcMem, 0, 0, hIcon, dx, dy, 0, 0, DI_NORMAL);
+ m_default_bf.SourceConstantAlpha = alpha;
+ if (CMimAPI::m_MyAlphaBlend) {
+ HBITMAP hbm = (HBITMAP)SelectObject(dcMem, hbmOld);
+ CImageItem::PreMultiply(hbm, 1); // for AlphaBlend()...
+ hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dcMem, hbm));
+ CMimAPI::m_MyAlphaBlend(hdc, left, top, dx, dy, dcMem, 0, 0, dx, dy, m_default_bf);
+ }
+ else {
+ SetStretchBltMode(hdc, HALFTONE);
+ StretchBlt(hdc, left, top, dx, dy, dcMem, 0, 0, dx, dy, SRCCOPY);
+ }
+ 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 OldWndProc)
+{
+ LRESULT orig = 0;
+ 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);
+ }
+ if(OldWndProc)
+ orig = CallWindowProc(OldWndProc, hwnd, 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 && CMimAPI::m_pfnGetThemeBackgroundContentRect) {
+ RECT rcClient;
+ HDC hdc = GetDC(GetParent(hwnd));
+
+ if (CMimAPI::m_pfnGetThemeBackgroundContentRect(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)
+{
+ CSkinItem *item = &SkinItems[skinID];
+ LRESULT result = 0;
+
+ if(OldWndProc)
+ result = CallWindowProc(OldWndProc, hwnd, msg, wParam, lParam); // do default processing (otherwise, NO scrollbar as it is painted in NC_PAINT)
+
+ 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;
+
+ 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;
+ } else if (CMimAPI::m_pfnDrawThemeBackground) {
+ 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
+ CMimAPI::m_pfnDrawThemeBackground(mwdat->hTheme, hdc, 1, 1, &rcWindow, &rcWindow);
+ }
+ }
+ else {
+ HBRUSH br = CreateSolidBrush(PluginConfig.m_cRichBorders ? PluginConfig.m_cRichBorders : ::GetSysColor(COLOR_3DSHADOW));
+ FillRect(hdc, &rcWindow, br);
+ DeleteObject(br);
+ }
+ 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("IMG/ResizeBitmap", (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<RECT *>(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 fAero = M->isAero();
+ bool fTbColorsValid = PluginConfig.m_tbBackgroundHigh && PluginConfig.m_tbBackgroundLow;
+ BYTE bAlphaOffset = 0;
+ BOOL fMustDrawNonThemed = ((fAero || 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->fIsAutosizingInput ? 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<HBITMAP>(::SelectObject(dat->pContainer->cachedToolbarDC, dat->pContainer->hbmToolbarBG));
+ }
+ dat->pContainer->szOldToolbarSize.cx = cx;
+ dat->pContainer->szOldToolbarSize.cy = cy;
+
+ if(!fMustDrawNonThemed && M->isVSThemed()) {
+ CMimAPI::m_pfnDrawThemeBackground(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 :
+ ((fAero && m_pCurrentAeroEffect) ? m_pCurrentAeroEffect->m_clrToolbar : ::GetSysColor(COLOR_3DFACE));
+ m_tmp_tb_low = PluginConfig.m_tbBackgroundLow ? PluginConfig.m_tbBackgroundLow :
+ ((fAero && 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<RECT *>(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<RECT *>(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<RECT *>(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<RECT *>(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<RECT *>(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);
+ }
+
+ TContainerData *pContainer = pFirstContainer;
+
+ while (pContainer) {
+ InvalidateRect(GetDlgItem(pContainer->hwnd, IDC_MSGTABS), NULL, TRUE);
+ InvalidateRect(pContainer->hwnd, NULL, TRUE);
+ if(IsWindow(GetDlgItem(pContainer->hwnd, 5000)))
+ InvalidateRect(GetDlgItem(pContainer->hwnd, 5000), NULL, TRUE);
+ pContainer = pContainer->pNextContainer;
+ }
+}
+
+void CSkin::setAeroEffect(LRESULT effect)
+{
+ if(effect == -1)
+ effect = static_cast<LRESULT>(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();
+ M->WriteDword(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("\\"));
+
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)tszBasePath);
+
+ m_fAeroSkinsValid = true;
+
+ try {
+ for(int i = 0; i < safe_sizeof(my_default_skin); i++)
+ Utils::extractResource(g_hInst, my_default_skin[i].ulID, _T("SKIN_GLYPH"), tszBasePath, my_default_skin[i].tszName, fForceOverwrite);
+ }
+ catch(CRTException& ex) {
+ ex.display();
+ 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));
+}
diff --git a/plugins/TabSRMM/src/translator.cpp b/plugins/TabSRMM/src/translator.cpp new file mode 100644 index 0000000000..1e801152ea --- /dev/null +++ b/plugins/TabSRMM/src/translator.cpp @@ -0,0 +1,665 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: translator.cpp 12999 2010-10-22 07:32:53Z silvercircle $
+ *
+ * handle pretranslated strings
+ *
+ */
+
+#include "commonheaders.h"
+
+wchar_t* CTranslator::weekDays[7] = {LPGENT("Sunday"), LPGENT("Monday"),
+ LPGENT("Tuesday"), LPGENT("Wednesday"),
+ LPGENT("Thursday"), LPGENT("Friday"), LPGENT("Saturday")};
+
+wchar_t* CTranslator::months[12] = {LPGENT("January"), LPGENT("February"),
+ LPGENT("March"), LPGENT("April"), LPGENT("May"),
+ LPGENT("June"), LPGENT("July"), LPGENT("August"),
+ LPGENT("September"), LPGENT("October"), LPGENT("November"), LPGENT("December")};
+
+wchar_t* CTranslator::weekDays_translated[7];
+wchar_t* CTranslator::months_translated[12];
+
+wchar_t* CTranslator::m_strings[STR_LAST] = {
+ LPGENT("Stay on Top"), /* CNT_MENU_STAYONTOP */
+ LPGENT("Hide titlebar"), /* CNT_MENU_HIDETITLEBAR */
+ LPGENT("Container options..."), /* CNT_MENU_CONTAINEROPTIONS */
+ LPGENT("Message Session..."), /* CNT_TITLE_DEFAULT */
+ LPGENT("Attach to"), /* CNT_ATTACH_TO */
+ LPGENT("Meta Contact"), /* GEN_META_CONTACT */
+ LPGENT("(Forced)"), /* GEN_META_FORCED */
+ LPGENT("Autoselect"), /* GEN_META_AUTOSELECT */
+ LPGENT("Use Protocol"), /* GEN_META_USEPROTO */
+ LPGENT("Set Default Protocol"), /* GEN_META_SETDEFAULT */
+ LPGENT("Nick name"), /* GEN_MUC_NICKNAME */
+ LPGENT("Unique Id"), /* GEN_MUC_UID */
+ LPGENT("Status"), /* GEN_MUC_STATUS */
+ LPGENT("%s: Chat Room (%u user%s)"), /* GEN_MUC_ROOM_TITLE_USER */
+ LPGENT("%s: Chat Room (%u users%s)"), /* GEN_MUC_ROOM_TITLE_USERS */
+ LPGENT(", event filter active"), /* GEN_MUC_ROOM_TITLE_FILTER */
+ LPGENT("%s: Message Session"), /* GEN_MUC_PRIVSESSION */
+ LPGENT("%s: Message Session (%u users)"), /* GEN_MUC_PRIVSESSION_MULTI */
+ LPGENT("The filter canoot be enabled, because there are no event types selected either global or for this chat room"), /* GEN_MUC_FILTER_ERROR */
+ LPGENT("Event filter error"), /* GEN_MUC_FILTER_ERROR_TITLE */
+ LPGENT("Text color"), /* GEN_MUC_TEXTCOLOR */
+ LPGENT("Background color"), /* GEN_MUC_BGCOLOR */
+ LPGENT("Container options"), /* CNT_OPT_TITLE */
+ LPGENT("Tabs at the top"), /* CNT_OPT_TABSTOP */
+ LPGENT("Tabs at the bottom"), /* CNT_OPT_TABSBOTTOM */
+ LPGENT("Switch bar on the left side"), /* CNT_OPT_TABSLEFT */
+ LPGENT("Switch bar on the right side"), /* CNT_OPT_TABSRIGHT */
+ LPGENT("Configure container options for\n%s"), /* CNT_OPT_HEADERBAR */
+ LPGENT("&File"), /* GEN_MENUBAR_FILE */
+ LPGENT("&View"), /* GEN_MENUBAR_VIEW */
+ LPGENT("&User"), /* GEN_MENUBAR_USER */
+ LPGENT("&Room"), /* GEN_MENUBAR_FILE */
+ LPGENT("Message &Log"), /* GEN_MENUBAR_LOG */
+ LPGENT("&Container"), /* GEN_MENUBAR_CONTAINER */
+ LPGENT("Help"), /* GEN_MENUBAR_HELP */
+ LPGENT("Sounds are %s. Click to toggle status, hold SHIFT and click to set for all open containers"), /* GEN_CNT_SBAR_SOUNDS */
+ LPGENT("enabled"), /* GEN_ENABLED */
+ LPGENT("disabled"), /* GEN_DISABLED */
+ LPGENT("Sending typing notifications is %s."), /* GEN_CNT_SBAR_MTN */
+ LPGENT("Extended status for %s: %s"), /* GEN_IP_TIP_XSTATUS */
+ LPGENT("%s is using"), /* GEN_IP_TIP_CLIENT */
+ LPGENT("Status message for %s (%s)"), /* GEN_IP_TIP_STATUSMSG */
+ LPGENT("tabSRMM Information"), /* GEN_IP_TIP_TITLE */
+ LPGENT("All message containers need to close before the skin can be changed\nProceed?"), /* GEN_SKIN_WARNCLOSE */
+ LPGENT("Change skin"), /* GEN_SKIN_WARNCLOSE_TITLE */
+ LPGENT("Warning: Popup plugin not found."), /* GEN_MTN_POPUP_WARNING */
+ LPGENT("Warning: Current Popup plugin version is not supported."), /* GEN_MTN_POPUP_UNSUPPORTED */
+ LPGENT("Contact"), /* GEN_CONTACT */
+ LPGENT("...is typing a message."), /* GEN_MTN_START */
+ LPGENT("...has stopped typing."), /* GEN_MTN_STOP */
+ LPGENT("Favorites"), /* GEN_FAVORITES */
+ LPGENT("Recent Sessions"), /* GEN_RECENT_SESSIONS */
+ LPGENT("Last received: %s at %s"), /* GEN_SBAR_LASTRECEIVED */
+ LPGENT("There are %d pending send jobs. Message length: %d bytes, message length limit: %d bytes\n\n%d messages are queued for later delivery"), /* GEN_SBAR_TIP_MSGLENGTH */
+ LPGENT("General options"), /* CNT_OPT_TITLE_GEN */
+ LPGENT("Window layout"), /* CNT_OPT_TITLE_LAYOUT */
+ LPGENT("Tabs and switch bar"), /* CNT_OPT_TITLE_TABS */
+ LPGENT("Notifications"), /* CNT_OPT_TITLE_NOTIFY */
+ LPGENT("Flashing"), /* CNT_OPT_TITLE_FLASHING */
+ LPGENT("Title bar"), /* CNT_OPT_TITLE_TITLEBAR */
+ LPGENT("Window size and theme"), /* CNT_OPT_TITLE_THEME */
+ LPGENT("Transparency"), /* CNT_OPT_TITLE_TRANS */
+ LPGENT("Choose your options for the tabbed user interface. Not all options can be applied to open windows. You may need to close and re-open them."), /* CNT_OPT_DESC_TABS */
+ LPGENT("Select, when you want to see event notifications (popups) for this window. The settings apply to all tabs within this window."), /*CNT_OPT_DESC_NOTIFY */
+ LPGENT("You can select a private theme (.tabsrmm file) for this container which will then override the default message log theme. You will have to close and re-open all message windows after changing this option."), /* CNT_OPT_DESC_THEME */
+ LPGENT("This feature requires Windows 2000 or later and may be unavailable when using a container skin."), /* CNT_OPT_DESC_TRANS */
+ LPGENT("Message"), /* GEN_POPUPS_MESSAGE */
+ LPGENT("Unknown event"), /* GEN_POPUPS_UNKNOWN */
+ LPGENT("New messages: "), /* GEN_POPUPS_NEW */
+ LPGENT("No status message"), /* GEN_NO_STATUS */
+ LPGENT("%s is typing a message."), /* GEN_MTN_STARTWITHNICK */
+ LPGENT("Typing Notification"), /* GEN_MTN_TTITLE */
+ LPGENT("Message from %s"), /* GEN_MSG_TTITLE */
+ LPGENT("/"), /* GEN_ICONPACK_WARNING */ /* NOT IN USE!! */
+ LPGENT("Select container for %s"), /* CNT_SELECT_FOR */
+ LPGENT("This name is already in use"), /* CNT_SELECT_INUSE */
+ LPGENT("You cannot rename the default container"), /* CNT_SELECT_RENAMEERROR */
+ LPGENT("You cannot delete the default container"), /* CNT_SELECT_DELETEERROR */
+ LPGENT(" "), /* GEN_WARN_CLOSE */ /* UNUSED */
+ LPGENT("Error creating destination directory"), /* GEN_MSG_SAVE_NODIR */
+ LPGENT("Save contact picture"), /* GEN_MSG_SAVE */
+ LPGENT("The file exists. Do you want to overwrite it?"), /* GEN_MSG_SAVE_FILE_EXISTS */
+ LPGENT("Topic is: %s"), /* GEN_MUC_TOPIC_IS */
+ LPGENT("no topic set."), /* GEN_MUC_NO_TOPIC */
+ LPGENT("%s has entered text."), /* GEN_MTN_STOPPED */
+ LPGENT("Contact Picture Settings..."), /* GEN_AVATAR_SETTINGS */
+ LPGENT("Set Your Avatar..."), /* GEN_AVATAR_SETOWN */
+ LPGENT("Do you want to also read message templates from the theme?\nCaution: This will overwrite the stored template set which may affect the look of your message window significantly.\nSelect cancel to not load anything at all."), /* GEN_WARNING_LOADTEMPLATES */
+ LPGENT("Load theme"), /* GEN_TITLE_LOADTHEME */
+ LPGENT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending Messages' section"), /* GEN_WARNING_PASTEANDSEND_DISABELD */
+ LPGENT("Either the nudge plugin is not installed or the contact's protocol does not support sending a nudge event."), /*GEN_WARNING_NUDGE_DISABLED */
+ LPGENT("'(Unknown Contact)'"), /* GEN_UNKNOWN_CONTACT */
+ LPGENT("Today"), /* GEN_LOG_TODAY */
+ LPGENT("Yesterday"), /* GEN_LOG_YESTERDAY */
+ LPGENT("Use default codepage"), /* GEN_LOG_USEDEFAAULTCP */
+ LPGENT("UID: %s (SHIFT click -> copy to clipboard)\nClick for User's Details\nRight click for MetaContact control\nClick dropdown to add or remove user from your favorites."), /* GEN_MSG_UINCOPY */
+ LPGENT("No UID"), /* GEN_MSG_NOUIN */
+ LPGENT("UID: %s (SHIFT click -> copy to clipboard)\nClick for User's Details\nClick dropdown to change this contact's favorite status."), /* GEN_MSG_UINCOPY_NO_MC */
+ LPGENT("signed off."), /* GEN_MSG_SIGNEDOFF */
+ LPGENT("signed on and is now %s."), /* GEN_MSG_SIGNEDON */
+ LPGENT("changed status from %s to %s."), /* GEN_MSG_CHANGEDSTATUS */
+ LPGENT("There are unsent messages waiting for confirmation.\nWhen 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)?"), /* GEN_SQ_WARNING */
+ LPGENT("Message window warning"), /* GEN_SQ_WARNING_TITLE */
+ LPGENT("You haven't selected any contacts from the list. Click the checkbox box next to a name to send the message to that person."), /* GEN_SQ_MULTISEND_NOCONTACTS */
+ LPGENT("A message delivery has failed.\nClick to open the message window."), /* GEN_SQ_DELIVERYFAILED */
+ LPGENT("A message delivery has failed after the contacts chat window was closed. You may want to resend the last message"), /* GEN_SQ_DELIVERYFAILEDLATE */
+ LPGENT("Multisend: successfully sent to: %s"), /* GEN_SQ_MULTISEND_SUCCESS */
+ LPGENT("Message successfully queued for later delivery.\nIt will be sent as soon as possible and a popup will inform you about the result."), /* GEN_SQ_QUEUED_MESSAGE */
+ LPGENT("The send later feature is not available on this protocol."), /* GEN_SQ_QUEUING_NOT_AVAIL */
+ LPGENT("\n(Sent delayed. Original timestamp %s)"), /* GEN_SQ_SENDLATER_HEADER */
+ LPGENT("Session list.\nClick left for a list of open sessions.\nClick right to access favorites and quickly configure message window behavior"), /* CNT_SBAR_SLIST */
+ LPGENT("Character Encoding"), /* GEN_MSG_ENCODING */
+ LPGENT("A message failed to send successfully."), /* GEN_MSG_FAILEDSEND */
+ LPGENT("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"), /* GEN_MSG_TOO_LONG_SPLIT */
+ LPGENT("The message you are trying to paste exceeds the message size limit for the active protocol. Only the first %d characters will be sent."), /* GEN_MSG_TOO_LONG_NOSPLIT */
+ LPGENT("Close Session"), /* GEN_MSG_CLOSE */
+ LPGENT("Save and close session"), /* GEN_MSG_SAVEANDCLOSE */
+ LPGENT("Autoscrolling is disabled (press F12 to enable it)"), /* GEN_MSG_LOGFROZENSTATIC */
+ LPGENT("Click for contact menu\nClick dropdown for window settings"), /*GEN_MSG_TIP_CONTACTMENU */
+ LPGENT("Retry"), /* GEN_MSG_BUTTON_RETRY */
+ LPGENT("Cancel"), /* GEN_MSG_BUTTON_CANCEL */
+ LPGENT("Send later"), /* GEN_MSG_BUTTON_SENDLATER */
+ LPGENT("Selection copied to clipboard"), /* GEN_MSG_SEL_COPIED */
+ LPGENT("Autoscrolling is disabled, %d message(s) queued (press F12 to enable it)"), /* GEN_MSG_LOGFROZENQUEUED */
+ LPGENT("Unknown client"), /* GEN_MSG_UNKNOWNCLIENT */
+ LPGENT("No extended status message available"), /* GEN_MSG_NOXSTATUSMSG */
+ LPGENT("Delivery failure: %s"), /* GEN_MSG_DELIVERYFAILURE */
+ LPGENT("The message send timed out"), /* GEN_MSG_SENDTIMEOUT */
+ LPGENT("Show Contact Picture"), /* GEN_MSG_SHOWPICTURE */
+ LPGENT("You cannot edit user notes when there are unsent messages"), /* GEN_MSG_NO_EDIT_NOTES */
+ LPGENT("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"), /* GEN_MSG_EDIT_NOTES_TIP */
+ LPGENT("Warning: you have selected a subprotocol for sending the following messages which is currently offline"), /* GEN_MSG_MC_OFFLINEPROTOCOL */
+ LPGENT("Contact is offline and this protocol does not support sending files to offline users."), /* GEN_MSG_OFFLINE_NO_FILE */
+ LPGENT("File"), /* GEN_STRING_FILE */
+ LPGENT("Message from %s"), /* GEN_STRING_MESSAGEFROM */
+ LPGENT("Multisend: failed sending to: %s"), /* GEN_SQ_MULTISENDERROR */
+ LPGENT("Look up \'%s\':"), /* GEN_MUC_LOOKUP */
+ LPGENT("No word to look up"), /* GEN_MUC_LOOKUP_NOWORD */
+ LPGENT("&Message"), /* GEN_MUC_MESSAGEAMP */
+ LPGENT("UTF-8"), /* GEN_STRING_UTF8 */
+
+ /* MUC LOG Formatting strings*/
+
+ LPGENT("%s has joined"), /* MUC_LOG_JOINED */
+ LPGENT("You have joined %s"), /* MUC_LOG_ME_JOINED */
+ LPGENT("%s has left"), /* MUC_LOG_LEFT */
+ LPGENT("%s has disconnected"), /* MUC_LOG_DISC */
+ LPGENT("%s is now known as %s"), /* MUC_LOG_NICKCHANGE */
+ LPGENT("You are now known as %s"), /* MUC_LOG_ME_NICKCHANGE */
+ LPGENT("%s kicked %s"), /* MUC_LOG_KICK */
+ LPGENT("Notice from %s: "), /* MUC_LOG_NOTICE */
+ LPGENT("The topic is \'%s%s\'"), /* MUC_LOG_TOPICIS */
+ LPGENT(" (set by %s on %s)"), /* MUC_LOG_TOPICSETBYON */
+ LPGENT(" (set by %s)"), /* MUC_LOG_TOPICSETBY */
+ LPGENT("%s enables \'%s\' status for %s"), /* MUC_LOG_STATUSENABLE */
+ LPGENT("%s disables \'%s\' status for %s"), /* MUC_LOG_STATUSDISABLE */
+ LPGENT("Highlight User..."), /* GEN_MUC_MENU_ADDTOHIGHLIGHT */
+ LPGENT("Add user to highlight list"), /* GEN_MUC_HIGHLIGHT_ADD */
+ LPGENT("Edit user highlight list"), /* GEN_MUC_HIGHLIGHT_EDIT */
+ LPGENT("Edit Highlight List..."), /* GEN_MUC_MENU_EDITHIGHLIGHTLIST */
+ LPGENT("Contact not on list. You may add it..."), /* GEN_MSG_CONTACT_NOT_ON_LIST */
+ LPGENT("A send later job completed successfully.\nThe original message: %s"), /* GEN_SQ_SENDLATER_SUCCESS_POPUP */
+
+ LPGENT("Copy To Clipboard"), /* GEN_IP_MENU_COPY */
+ LPGENT("Open User Details..."), /* GEN_IP_MENU_USER_DETAILS */
+ LPGENT("Messaging Settings..."), /* GEN_IP_MENU_MSGPREFS */
+ LPGENT("Room Settings..."), /* GEN_IP_MENU_ROOMPREFS */
+ LPGENT("Open History..."), /* GEN_IP_MENU_HISTORY */
+
+ LPGENT("hour"), /* GEN_STRING_HOUR */
+ LPGENT("hours"), /* GEN_STRING_HOURS */
+ LPGENT("minute"), /* GEN_STRING_MINUTE */
+ LPGENT("minutes"), /* GEN_STRING_MINUTES */
+ LPGENT(", %d %s, %d %s idle"), /* MUC_SBAR_IDLEFORMAT */
+ LPGENT("%s on %s%s"), /* MUC_SBAR_ON_SERVER */
+ LPGENT(", %d %s idle"), /* MUC_SBAR_IDLEFORMAT_SHORT */
+ LPGENT("Contact avatars"), /* CNT_OPT_TITLE_AVATARS */
+
+ /*
+ * MUC tray icon notifications
+ */
+
+ LPGENT("%s wants your attention in %s"), /* GEN_MUC_TRAY_HILIGHT */
+ LPGENT("%s speaks in %s"), /* GEN_MUC_TRAY_MSG */
+ LPGENT("%s has joined %s"), /* GEN_MUC_TRAY_JOINED */
+ LPGENT("%s has left %s"), /* GEN_MUC_TRAY_LEFT */
+ LPGENT("%s has disconnected"), /* GEN_MUC_TRAY_QUIT */
+ LPGENT("%s is now known as %s"), /* GEN_MUC_TRAY_NICK */
+ LPGENT("%s kicked %s from %s"), /* GEN_MUC_TRAY_KICK */
+ LPGENT("Notice from %s"), /* GEN_MUC_TRAY_NOTICE */
+ LPGENT("Topic change in %s"), /* GEN_MUC_TRAY_TOPIC */
+ LPGENT("Information in %s"), /* GEN_MUC_TRAY_INFO */
+ LPGENT("%s enables \'%s\' status for %s in %s"), /* GEN_MUC_TRAY_STATUS_ON */
+ LPGENT("%s disables \'%s\' status for %s in %s"), /* GEN_MUC_TRAY_STATUS_OFF */
+
+ /*
+ * muc popups and disk logging
+ */
+
+ LPGENT("%s%s says:%s %s"), /* GEN_MUC_POPUP_MSG */
+ LPGENT("%s has joined"), /* GEN_MUC_POPUP_JOINED */
+ LPGENT("%s has left"), /* GEN_MUC_POPUP_LEFT */
+ LPGENT("%s has left (%s)"), /* GEN_MUC_POPUP_LEFT1 */
+ LPGENT("%s has disconnected"), /* GEN_MUC_POPUP_QUIT */
+ LPGENT("%s has disconnected (%s)"), /* GEN_MUC_POPUP_QUIT1 */
+ LPGENT("%s is now known as %s"), /* GEN_MUC_POPUP_NICK */
+ LPGENT("%s kicked %s"), /* GEN_MUC_POPUP_KICK */
+ LPGENT("%s kicked %s (%s)"), /* GEN_MUC_POPUP_KICK1 */
+ LPGENT("Notice from %s: %s"), /* GEN_MUC_POPUP_NOTICE */
+ LPGENT("The topic is \'%s\'"), /* GEN_MUC_POPUP_TOPIC */
+ LPGENT("The topic is \'%s\' (set by %s)"), /* GEN_MUC_POPUP_TOPIC1 */
+ LPGENT("%s enables \'%s\' status for %s"), /* GEN_MUC_POPUP_STATUS_ON */
+ LPGENT("%s disables \'%s\' status for %s"), /* GEN_MUC_POPUP_STATUS_OFF */
+
+ LPGENT("Sound notifications"), /* CNT_OPT_TITLE_SOUNDS */
+ LPGENT("%s Idle: %dh,%02dm"), /* GEN_IP_IDLENOTICE */
+
+ /**
+ * template strings for the richedit - based infopanel tooltip.
+ * Please DO NOT mess with the RTF formatting codes or you may destroy the layout of the
+ * tooltips, just translate the strings inside
+ */
+ LPGENT("\\tab \\ul\\b Status message:\\ul0\\b0 \\par %s"), /* GEN_INFOTIP_STATUSMSG */
+ LPGENT("\\par\\par\\tab \\ul\\b Extended status information:\\ul0\\b0 \\par "), /* GEN_INFOTIP_XSTATUS */
+ LPGENT("\\par\\par\\tab \\ul\\b Listening to:\\ul0\\b0 \\par %s"), /* GEN_INFOTIP_LISTENING */
+ LPGENT("\\par\\par\\ul\\b Client:\\ul0\\b0 %s"), /* GEN_INFOTIP_CLIENT */
+
+ LPGENT("Insert [img] tag / surround selected text with [img][/img]"), /* GEN_BB_IMGTOOLTIP */
+ LPGENT("Original timestamp"), /* QMGR_COL_ODATE */
+ LPGENT("Message text"), /* QMGR_COL_MESSAGETEXT */
+ LPGENT("Status"), /* QMGR_COL_STATUS */
+ LPGENT("Last send info"), /* QMGR_COL_LASTSENDINFO */
+ LPGENT("<All contacts>"), /* QMGR_FILTER_ALLCONTACTS */
+ LPGENT("Failed"), /* QMGR_STATUS_FAILED */
+ LPGENT("Sent OK"), /* QMGR_STATUS_SENTOK */
+ LPGENT("Pending"), /* QMGR_STATUS_PENDING */
+ LPGENT("Wait ACK"), /* QMGR_STATUS_WAITACK */
+ LPGENT("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."), /* QMGR_ERROR_NOMULTISEND */
+ LPGENT("Removed"), /* QMGR_STATUS_REMOVED */
+ LPGENT("You are about to modify the state of one or more items in the\nunattended send queue. The requested action(s) will be executed at the next scheduled queue processing.\n\nThis action cannot be made undone."), /* QMGR_WARNING_REMOVAL */
+ LPGENT("Queue manager"), /* QMGR_TITLE */
+ LPGENT("Suspended"), /* QMGR_STATUS_HOLD */
+ LPGENT("Deferred"), /* QMGR_STATUS_DEFERRED */
+ LPGENT("A send later job failed to complete.\nThe original message: %s"), /* GEN_SQ_SENDLATER_FAILED_POPUP */
+ LPGENT("The message cannot be sent delayed or to multiple contacts, because it exceeds the maximum allowed message length of %d bytes"), /* GEN_SQ_SENDLATER_ERROR_MSG_TOO_LONG */
+
+ LPGENT("Default container"), /* GEN_DEFAULT_CONTAINER_NAME */
+ /*
+ * event notification popups
+ */
+ LPGENT("No description given"), /* GEN_STRING_EVENT_FILE_NODESC */
+ LPGENT("Incoming file (invalid format"), /* GEN_STRING_EVENT_FILE_INVALID */
+ LPGENT("Incoming file"), /* GEN_STRING_EVENT_FILE */
+ /*
+ * tooltips for static message window buttons
+ */
+ LPGENT("Add this contact permanently to your contact list"), /* GEN_TOOLTIP_ADDCONTACT */
+ LPGENT("Do not add this contact permanently"), /* GEN_TOOLTIP_DONTADD */
+ LPGENT("Expand or collapse the side bar"), /* GEN_TOOLTIP_EXPANDSIDEBAR */
+
+ /*
+ * task bar support (thumbnails)
+ */
+ LPGENT("Chat room %s"), /* GEN_TASKBAR_STRING_CHAT_ROOM */
+ LPGENT("Server window"), /* GEN_TASKBAR_STRING_SERVER_WINDOW */
+ LPGENT("%d Unread"), /* GEN_TASKBAR_STRING_UNREAD */
+ LPGENT("%d User(s)"), /* GEN_TASKBAR_STRING_USERS */
+
+ LPGENT("Previews not availble when using History++ plugin for message log display."), /* GEN_AEROPEEK_NOHPP */
+ LPGENT("TabSRMM warning message"), /* GEN_STRING_WARNING_TITLE */
+};
+
+/*
+ * these strings are used by option pages ONLY
+ */
+
+wchar_t* CTranslator::m_OptStrings[OPT_LAST] = {
+ LPGENT("Use Global Setting"), /* OPT_UPREFS_IPGLOBAL */
+ LPGENT("Always On"), /* OPT_UPREFS_ON */
+ LPGENT("Always Off"), /* OPT_UPREFS_OFF */
+ LPGENT("Show always (if present)"), /* OPT_UPREFS_AVON */
+ LPGENT("Never show it at all"), /* OPT_UPREFS_AVOFF */
+ LPGENT("Force History++"), /* OPT_UPREFS_FORCEHPP */
+ LPGENT("Force IEView"), /* OPT_UPREFS_FORCEIEV */
+ LPGENT("Force Default Message Log"), /* OPT_UPREFS_FORCEDEFAULT */
+ LPGENT("Simple Tags (*/_)"), /* OPT_UPREFS_SIMPLETAGS */
+ LPGENT("BBCode"), /* OPT_UPREFS_BBCODE */
+ LPGENT("Force Off"), /* OPT_UPREFS_FORMATTING_OFF */
+ LPGENT("Use default codepage"), /* OPT_UPREFS_DEFAULTCP */
+ LPGENT("Time zone service is missing"), /* OPT_UPREFS_NOTZSVC */
+ LPGENT("Set messaging options for %s"), /* OPT_UPREFS_TITLE */
+ LPGENT("Message Log"), /* OPT_UPREFS_MSGLOG */
+ LPGENT("General"), /* OPT_UPREFS_GENERIC */
+ LPGENT(""), /* OPT_AERO_EFFECT_NONE */
+ LPGENT(""), /* OPT_AERO_EFFECT_MILK */
+ LPGENT(""), /* OPT_AERO_EFFECT_CARBON */
+ LPGENT(""), /* OPT_AERO_EFFECT_SOLID */
+ LPGENT("No border"), /* OPT_GEN_NONE */
+ LPGENT(""), /* OPT_GEN_AUTO */
+ LPGENT(""), /* OPT_GEN_SUNKEN */
+ LPGENT("1 pixel, solid"), /* OPT_GEN_1PIXEL */
+ LPGENT("Rounded (only for internal avatar drawing)"), /* OPT_GEN_ROUNDED */
+ LPGENT("Globally on"), /* OPT_GEN_GLOBALLY ON */
+ LPGENT("On, if present"), /* OPT_GEN_ON_IF_PRESENT */
+ LPGENT("Globally OFF"), /* OPT_GEN_GLOBALLY_OFF */
+ LPGENT("On, if present, always in bottom display"), /* OPT_GEN_ON_ALWAYS_BOTTOM */
+ LPGENT("Don't show them"), /* OPT_GEN_DONT_SHOW */
+ LPGENT("Window layout tweaks"), /* OPT_TAB_LAYOUTTWEAKS */
+ LPGENT("Load and apply"), /* OPT_TAB_SKINLOAD */
+ LPGENT("Set panel visibility for this %s"), /* OPT_IPANEL_VISIBILTY_TITLE */
+ LPGENT("contact"), /* OPT_IPANEL_VISIBILTY_IM */
+ LPGENT("chat room"), /* OPT_IPANEL_VISIBILTY_CHAT */
+ LPGENT("Do not synchronize the panel height with IM windows"), /* OPT_IPANEL_SYNC_TITLE_IM */
+ LPGENT("Do not synchronize the panel height with group chat windows"), /* OPT_IPANEL_SYNC_TITLE_MUC */
+ LPGENT("Inherit from container setting"), /* OPT_IPANEL_VIS_INHERIT */
+ LPGENT("Always off"), /* OPT_IPANEL_VIS_OFF */
+ LPGENT("Always on"), /* OPT_IPANEL_VIS_ON*/
+ LPGENT("Use default size"), /* OPT_IPANEL_SIZE_GLOBAL */
+ LPGENT("Use private size"), /* OPT_IPANEL_SIZE_PRIVATE */
+ LPGENT("Off"), /* OPT_GEN_OFF */
+ LPGENT("BBCode"), /* OPT_GEN_BBCODE */
+ LPGENT("Default"), /* OPT_LOG_DEFAULT */
+ LPGENT("IEView plugin"), /* OPT_LOG_IEVIEW */
+ LPGENT("History++ plugin"), /* OPT_LOG_HPP */
+ LPGENT("** New contacts **"), /* OPT_MTN_NEW */
+ LPGENT("** Unknown contacts **"), /* OPT_MTN_UNKNOWN */
+ LPGENT("Always"), /* OPT_GEN_ALWAYS */
+ LPGENT("Always, but no popup when window is focused"), /* OPT_MTN_NOTFOUCSED */
+ LPGENT("Only when no message window is open"), /* OPT_MTN_ONLYCLOSED */
+ LPGENT("Normal - close tab, if last tab is closed also close the window"), /* OPT_CNT_ESCNORMAL */
+ LPGENT("Minimize the window to the task bar"), /* OPT_CNT_ESCMINIMIZE */
+ LPGENT("Close or hide window, depends on the close button setting above"), /* OPT_CNT_ESCCLOSE */
+ LPGENT("Show balloon popup (unsupported system)"), /* OPT_MTN_UNSUPPORTED */
+ LPGENT("Choose status modes"), /* OPT_SMODE_CHOOSE */
+ LPGENT("nick of current contact (if defined)"), /* OPT_MUC_LOGTIP1 */
+ LPGENT("protocol name of current contact (if defined). Account name is used when protocol supports multiaccounts"), /* OPT_MUC_LOGTIP2 */
+ LPGENT("UserID of current contact (if defined). It is like UIN Number for ICQ, JID for Jabber, etc."), /* OPT_MUC_LOGTIP3 */
+ LPGENT("path to root miranda folder"), /* OPT_MUC_LOGTIP4 */
+ LPGENT("path to current miranda profile"), /* OPT_MUC_LOGTIP5 */
+ LPGENT("name of current miranda profile (filename, without extension)"), /* OPT_MUC_LOGTIP6 */
+ LPGENT("will return parsed string %miranda_profile%\\Profiles\\%miranda_profilename%"), /* OPT_MUC_LOGTIP7 */
+ LPGENT("same as environment variable %APPDATA% for currently logged-on Windows user"), /* OPT_MUC_LOGTIP8 */
+ LPGENT("username for currently logged-on Windows user"), /* OPT_MUC_LOGTIP9 */
+ LPGENT("\"My Documents\" folder for currently logged-on Windows user"), /* OPT_MUC_LOGTIP10 */
+ LPGENT("\"Desktop\" folder for currently logged-on Windows user"), /* OPT_MUC_LOGTIP11 */
+ LPGENT("any environment variable defined in current Windows session (like %systemroot%, %allusersprofile%, etc.)"), /* OPT_MUC_LOGTIP12 */
+ LPGENT("day of month, 1-31"), /* OPT_MUC_LOGTIP13 */
+ LPGENT("day of month, 01-31"), /* OPT_MUC_LOGTIP14 */
+ LPGENT("month number, 1-12"), /* OPT_MUC_LOGTIP15 */
+ LPGENT("month number, 01-12"), /* OPT_MUC_LOGTIP16 */
+ LPGENT("abbreviated month name"), /* OPT_MUC_LOGTIP17 */
+ LPGENT("full month name"), /* OPT_MUC_LOGTIP18 */
+ LPGENT("year without century, 01-99"), /* OPT_MUC_LOGTIP19 */
+ LPGENT("year with century, 1901-9999"), /* OPT_MUC_LOGTIP20 */
+ LPGENT("abbreviated weekday name"), /* OPT_MUC_LOGTIP21 */
+ LPGENT("full weekday name"), /* OPT_MUC_LOGTIP22 */
+ LPGENT("Appearance and functionality of chat room windows"), /* OPT_MUC_OPTHEADER1 */
+ LPGENT("Appearance of the message log"), /* OPT_MUC_OPTEHADER2 */
+ LPGENT("Variables"), /* OPT_MUC_VARIABLES */
+ LPGENT("Select Folder"), /* OPT_MUC_SELECTFOLDER */
+ LPGENT("No markers"), /* OPT_MUC_NOMARKERS */
+ LPGENT("Show as icons"), /* OPT_MUC_ASICONS */
+ LPGENT("Show as text symbols"), /* OPT_MUC_ASSYMBOLS */
+ LPGENT("Template Set Editor"), /* OPT_TEMP_TITLE */
+ LPGENT("This will reset the template set to the default built-in templates. Are you sure you want to do this?"), /* OPT_TEMP_RESET */
+ LPGENT("Template set was successfully reset, please close and reopen all message windows. This template editor window will now close."), /* OPT_TEMP_WASRESET */
+ LPGENT("Template editor help"), /* OPT_TEMP_HELPTITLE */
+ LPGENT("General"), /* OPT_TABS_GENERAL */
+ LPGENT("Tabs and layout"), /* OPT_TABS_TABS */
+ LPGENT("Containers"), /* OPT_TABS_CONTAINERS */
+ LPGENT("Message log"), /* OPT_TABS_LOG */
+ LPGENT("Tool bar"), /* OPT_TABS_TOOLBAR */
+ LPGENT("Advanced tweaks"), /* OPT_TABS_ADVANCED */
+ LPGENT("Settings"), /* OPT_TABS_MUC_SETTINGS */
+ LPGENT("Log formatting"), /* OPT_TABS_MUC_LOG */
+ LPGENT("Events and filters"), /* OPT_TABS_MUC_EVENTS */
+ LPGENT("Highlighting"), /* OPT_TABS_MUC_HIGHLIGHT */
+ LPGENT("You have chosen to use an external plugin for displaying the message history in the chat window. Most of the settings on this page are for the standard message log viewer only and will have no effect. To change the appearance of the message log, you must configure either IEView or History++."), /* OPT_MSGLOG_EXPLAINSETTINGS */
+ LPGENT("<no skin>"), /* OPT_SKIN_NOSKINSELECT */
+};
+
+/** IMPORTANT note to translators for translation of the warning dialogs:
+ *
+ * Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
+ * warning title from the actual warning text.
+ *
+ * Also, do NOT insert multiple | characters in the translated string. Not well-formatted
+ * warnings cannot be translated and the plugin will show the untranslated versions.
+ *
+ * strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
+ * will be used for important and critical error messages only.
+ *
+ * some strings are empty, this is intentional and used for error messages that share
+ * the message with other possible error notifications (popups, tool tips etc.)
+ *
+ * Entries that do not use the LPGENT() macro are NOT TRANSLATABLE, so don't bother translating them.
+ */
+wchar_t* CTranslator::m_Warnings[WARN_LAST] = {
+ LPGENT("Important release notes|A test warning message"), /* WARN_TEST */ /* reserved for important notes after upgrade - NOT translatable */
+ LPGENT("Icon pack version check|The installed icon pack is outdated and might be incompatible with TabSRMM version 3.\n\n\\b1Missing or misplaced icons are possible issues with the currently installed icon pack.\\b0"), /* WARN_ICONPACKVERSION */ /* NOT TRANSLATABLE */
+ LPGENT("Edit user notes|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"), /* WARN_EDITUSERNOTES */
+ LPGENT("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"), /* WARN_ICONPACKMISSING */ /* NOT TRANSLATABLE */
+ LPGENT("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."), /* WARN_AEROPEEKSKIN */
+ LPGENT("TabSRMM group chat module|TabSRMM could not enable its group chat module. The most likely cause is that you have installed and enabled \\b1chat.dll\\b0 or another plugin that provides groupchat services.\n\nShould I try to fix this now \\b1(a restart of Miranda is required to apply these changes)?\\b0"), /* WARN_CHAT_ENABLED */ /* NOT TRANSLATABLE */
+ L"Filetransfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibilty list.", /* WARN_IMGSVC_MISSING */ /* NOT TRANSLATABLE */
+ LPGENT("Settings problem|The option \\b1 History->Imitate IEView API\\b0 is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
+ L" ", /* WARN_NO_SENDLATER */ /*uses QMGR_ERROR_NOMULTISEND */
+ LPGENT("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"), /* WARN_CLOSEWINDOW */
+ LPGENT("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
+ LPGENT("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
+};
+
+wchar_t* CTranslator::m_translated[STR_LAST];
+wchar_t* CTranslator::m_OptTranslated[OPT_LAST];
+wchar_t* CTranslator::m_WarningsTranslated[WARN_LAST];
+
+TOptionListGroup CTranslator::m_lvGroupsModPlus[] = {
+ 0, LPGENT("Message window tweaks"),
+ 0, LPGENT("Error feedback"),
+ 0, NULL
+};
+
+TOptionListItem CTranslator::m_lvItemsModPlus[] = {
+ 0, LPGENT("Show client icon in status bar (fingerprint plugin required)"), 0, LOI_TYPE_SETTING, (UINT_PTR)"adv_ClientIconInStatusBar", 0,
+ 0, LPGENT("Show skinnable tooltip in chat (tipper plugin required)"), 1, LOI_TYPE_SETTING, (UINT_PTR)"adv_TipperTooltip", 0,
+ 0, LPGENT("Enable typing sounds"), 0, LOI_TYPE_SETTING, (UINT_PTR)"adv_soundontyping", 0,
+ 0, LPGENT("Disable animated GIF avatars (will not affect already open message windows)"), 0, LOI_TYPE_SETTING, (UINT_PTR)"adv_DisableAniAvatars", 0,
+ 0, LPGENT("Close current tab on send"), 0, LOI_TYPE_SETTING, (UINT_PTR)"adv_AutoClose_2", 0,
+ 0, LPGENT("Disable error popups on sending failures"), 0, LOI_TYPE_SETTING, (UINT_PTR)"adv_noErrorPopups", 1,
+ 0, LPGENT("Automatic keyboard layout: Do not load the system default for new contacts"), 1, LOI_TYPE_SETTING, (UINT_PTR)"adv_leaveKeyboardAlone", 0,
+ 0, LPGENT("Enable unattended send (experimental feature, required for multisend and send later) (*)"), 0, LOI_TYPE_SETTING, (UINT_PTR)"sendLaterAvail", 0,
+ 0, NULL, 0, 0, 0, 0
+};
+
+TOptionListItem CTranslator::m_lvItemsNEN [] = {
+ 0, LPGENT("Show a preview of the event"), IDC_CHKPREVIEW, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bPreview, 1,
+ 0, LPGENT("Don't announce event when message dialog is open"), IDC_CHKWINDOWCHECK, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bWindowCheck, 1,
+ 0, LPGENT("Don't announce events from RSS protocols"), IDC_NORSS, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bNoRSS, 1,
+ 0, LPGENT("Enable the system tray icon"), IDC_ENABLETRAYSUPPORT, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bTraySupport, 2,
+ 0, LPGENT("Merge new events for the same contact into existing popup"), 1, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bMergePopup, 6,
+ 0, LPGENT("Show headers"), IDC_CHKSHOWHEADERS, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bShowHeaders, 6,
+ 0, LPGENT("Dismiss popup"), MASK_DISMISS, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActL, 3,
+ 0, LPGENT("Open event"), MASK_OPEN, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActL, 3,
+ 0, LPGENT("Dismiss event"), MASK_REMOVE, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActL, 3,
+
+ 0, LPGENT("Dismiss popup"), MASK_DISMISS, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActR, 4,
+ 0, LPGENT("Open event"), MASK_OPEN, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActR, 4,
+ 0, LPGENT("Dismiss event"), MASK_REMOVE, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActR, 4,
+
+ 0, LPGENT("Dismiss popup"), MASK_DISMISS, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActTE, 5,
+ 0, LPGENT("Open event"), MASK_OPEN, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.maskActTE, 5,
+
+ 0, LPGENT("Disable event notifications for instant messages"), IDC_CHKWINDOWCHECK, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.iDisable, 0,
+ 0, LPGENT("Disable event notifications for group chats"), IDC_CHKWINDOWCHECK, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.iMUCDisable, 0,
+ 0, LPGENT("Disable notifications for non-message events"), IDC_CHKWINDOWCHECK, LOI_TYPE_SETTING, (UINT_PTR)&nen_options.bDisableNonMessage, 0,
+
+ 0, LPGENT("Remove popups for a contact when the message window is focused"), PU_REMOVE_ON_FOCUS, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.dwRemoveMask, 7,
+ 0, LPGENT("Remove popups for a contact when I start typing a reply"), PU_REMOVE_ON_TYPE, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.dwRemoveMask, 7,
+ 0, LPGENT("Remove popups for a contact when I send a reply"), PU_REMOVE_ON_SEND, LOI_TYPE_FLAG, (UINT_PTR)&nen_options.dwRemoveMask, 7,
+
+ 0, NULL, 0, 0, 0, 0
+};
+
+TOptionListGroup CTranslator::m_lvGroupsNEN[] = {
+ 0, LPGENT("Disable notifications"),
+ 0, LPGENT("General options"),
+ 0, LPGENT("System tray icon"),
+ 0, LPGENT("Left click actions (popups only)"),
+ 0, LPGENT("Right click actions (popups only)"),
+ 0, LPGENT("Timeout actions (popups only)"),
+ 0, LPGENT("Combine notifications for the same contact"),
+ 0, LPGENT("Remove popups under following conditions"),
+ 0, NULL
+};
+
+TOptionListGroup CTranslator::m_lvGroupsMsg[] = {
+ 0, LPGENT("Message window behaviour"),
+ 0, LPGENT("Sending messages"),
+ 0, LPGENT("Other options"),
+ 0, NULL
+};
+
+TOptionListItem CTranslator::m_lvItemsMsg[] = {
+ 0, LPGENT("Send on SHIFT - Enter"), 0, LOI_TYPE_SETTING, (UINT_PTR)"sendonshiftenter", 1,
+ 0, LPGENT("Send message on 'Enter'"), SRMSGDEFSET_SENDONENTER, LOI_TYPE_SETTING, (UINT_PTR)SRMSGSET_SENDONENTER, 1,
+ 0, LPGENT("Send message on double 'Enter'"), 0, LOI_TYPE_SETTING, (UINT_PTR)"SendOnDblEnter", 1,
+ 0, LPGENT("Minimize the message window on send"), SRMSGDEFSET_AUTOMIN, LOI_TYPE_SETTING, (UINT_PTR)SRMSGSET_AUTOMIN, 1,
+ //Mad
+ 0, LPGENT("Close the message window on send"), 0, LOI_TYPE_SETTING, (UINT_PTR)"AutoClose", 1,
+ //mad_
+ 0, LPGENT("Always flash contact list and tray icon for new messages"), 0, LOI_TYPE_SETTING, (UINT_PTR)"flashcl", 0,
+ 0, LPGENT("Delete temporary contacts on close"), 0, LOI_TYPE_SETTING, (UINT_PTR)"deletetemp", 0,
+ 0, LPGENT("Enable \"Paste and send\" feature"), 0, LOI_TYPE_SETTING, (UINT_PTR)"pasteandsend", 1,
+ 0, LPGENT("Allow BBCode formatting in outgoing messages"), 0, LOI_TYPE_SETTING, (UINT_PTR)"sendformat", 1,
+ 0, LPGENT("Automatically split long messages (experimental, use with care)"), 0, LOI_TYPE_SETTING, (UINT_PTR)"autosplit", 2,
+ 0, NULL, 0, 0, 0, 0
+};
+
+TOptionListGroup CTranslator::m_lvGroupsLog[] = {
+ 0, LPGENT("Message log appearance"),
+ 0, LPGENT("Support for external plugins"),
+ 0, LPGENT("Other options"),
+ 0, LPGENT("Additional events to show"),
+ 0, LPGENT("Timestamp settings (note: timstamps also depend on your templates)"),
+ 0, LPGENT("Message log icons"),
+ 0, NULL
+};
+
+TOptionListItem CTranslator::m_lvItemsLog[] = {
+ 0, LPGENT("Show file events"), 1, LOI_TYPE_SETTING, (UINT_PTR)SRMSGSET_SHOWFILES, 3,
+ 0, LPGENT("Show timestamps"), 1, LOI_TYPE_FLAG, (UINT_PTR)MWF_LOG_SHOWTIME, 4,
+ 0, LPGENT("Show dates in timestamps"), 1, LOI_TYPE_FLAG, (UINT_PTR)MWF_LOG_SHOWDATES, 4,
+ 0, LPGENT("Show seconds in timestamps"), 1, LOI_TYPE_FLAG, (UINT_PTR)MWF_LOG_SHOWSECONDS, 4,
+ 0, LPGENT("Use contacts local time (if timezone info available)"), 0, LOI_TYPE_FLAG, (UINT_PTR)MWF_LOG_LOCALTIME, 4,
+ 0, LPGENT("Draw grid lines"), 1, LOI_TYPE_FLAG, MWF_LOG_GRID, 0,
+ 0, LPGENT("Event type icons in the message log"), 1, LOI_TYPE_FLAG, MWF_LOG_SHOWICONS, 5,
+ 0, LPGENT("Text symbols as event markers"), 0, LOI_TYPE_FLAG, MWF_LOG_SYMBOLS, 5,
+ 0, LPGENT("Use Incoming/Outgoing Icons"), 1, LOI_TYPE_FLAG, MWF_LOG_INOUTICONS, 5,
+ 0, LPGENT("Use Message Grouping"), 1, LOI_TYPE_FLAG, MWF_LOG_GROUPMODE, 0,
+ 0, LPGENT("Indent message body"), 1, LOI_TYPE_FLAG, MWF_LOG_INDENT, 0,
+ 0, LPGENT("Simple text formatting (*bold* etc.)"), 0, LOI_TYPE_FLAG, MWF_LOG_TEXTFORMAT, 0,
+ 0, LPGENT("Support BBCode formatting"), 1, LOI_TYPE_FLAG, MWF_LOG_BBCODE, 0,
+ 0, LPGENT("Place a separator in the log after a window lost its foreground status"), 0, LOI_TYPE_SETTING, (UINT_PTR)"usedividers", 0,
+ 0, LPGENT("Only place a separator when an incoming event is announced with a popup"), 0, LOI_TYPE_SETTING, (UINT_PTR)"div_popupconfig", 0,
+ 0, LPGENT("RTL is default text direction"), 0, LOI_TYPE_FLAG, MWF_LOG_RTL, 0,
+ //0, LPGENT("Support Math Module plugin"), 1, LOI_TYPE_SETTING, (UINT_PTR)"wantmathmod", 1,
+//MAD:
+ 0, LPGENT("Show events at the new line (IEView Compatibility Mode)"), 1, LOI_TYPE_FLAG, MWF_LOG_NEWLINE, 1,
+ 0, LPGENT("Underline timestamp/nickname (IEView Compatibility Mode)"), 0, LOI_TYPE_FLAG, MWF_LOG_UNDERLINE, 1,
+ 0, LPGENT("Show timestamp after nickname (IEView Compatibility Mode)"), 0, LOI_TYPE_FLAG, MWF_LOG_SWAPNICK, 1,
+//
+ 0, LPGENT("Log status changes"), 1, LOI_TYPE_FLAG, MWF_LOG_STATUSCHANGES, 2,
+ 0, LPGENT("Automatically copy selected text"), 0, LOI_TYPE_SETTING, (UINT_PTR)"autocopy", 2,
+ 0, LPGENT("Use normal templates (uncheck to use simple templates if your template set supports them)"), 1, LOI_TYPE_FLAG, MWF_LOG_NORMALTEMPLATES, 0,
+ 0, NULL, 0, 0, 0, 0
+};
+
+TOptionListGroup CTranslator::m_lvGroupsTab[] = {
+ 0, LPGENT("Tab options"),
+ 0, LPGENT("How to create tabs and windows for incoming messages"),
+ 0, LPGENT("Miscellaneous options"),
+ 0, NULL
+};
+
+TOptionListItem CTranslator::m_lvItemsTab[] = {
+ 0, LPGENT("Show status text on tabs"), 1, LOI_TYPE_SETTING, (UINT_PTR)"tabstatus", 0,
+ 0, LPGENT("Prefer xStatus icons when available"), 1, LOI_TYPE_SETTING, (UINT_PTR)"use_xicons", 0,
+ 0, LPGENT("Detailed tooltip on tabs (requires mToolTip or Tipper plugin)"), 0, LOI_TYPE_SETTING, (UINT_PTR)"d_tooltips", 0,
+ 0, LPGENT("ALWAYS activate new message sessions (has PRIORITY over the options below)"), SRMSGDEFSET_AUTOPOPUP, LOI_TYPE_SETTING, (UINT_PTR)SRMSGSET_AUTOPOPUP, 1,
+ 0, LPGENT("Automatically create new message sessions without activating them"), 1, LOI_TYPE_SETTING, (UINT_PTR)"autotabs", 1,
+ 0, LPGENT("New windows are minimized (the option above MUST be active)"), 1, LOI_TYPE_SETTING, (UINT_PTR)"autocontainer", 1,
+ 0, LPGENT("Activate a minimized window when a new tab is created inside it"), 0, LOI_TYPE_SETTING, (UINT_PTR)"cpopup", 1,
+ 0, LPGENT("Automatically switch existing tabs in minimized windows on incoming messages (ignored when using Aero Peek task bar features)"), 1, LOI_TYPE_SETTING, (UINT_PTR)"autoswitchtabs", 1,
+ 0, LPGENT("Remember and set keyboard layout per contact"), 1, LOI_TYPE_SETTING, (UINT_PTR)"al", 2,
+ 0, LPGENT("Close button only hides message windows"), 0, LOI_TYPE_SETTING, (UINT_PTR)"hideonclose", 2,
+ 0, LPGENT("Allow TAB key in typing area (this will disable focus selection by TAB key)"), 0, LOI_TYPE_SETTING, (UINT_PTR)"tabmode", 2,
+ 0, LPGENT("Add offline contacts to multisend list"),0,LOI_TYPE_SETTING,(UINT_PTR) "AllowOfflineMultisend", 2,
+ 0, NULL, 0, 0, 0, 0
+};
+
+
+TOptionListItem* CTranslator::getTree(UINT id)
+{
+ switch(id) {
+ case TREE_MODPLUS:
+ return(m_lvItemsModPlus);
+ case TREE_NEN:
+ return(m_lvItemsNEN);
+ case TREE_MSG:
+ return(m_lvItemsMsg);
+ case TREE_LOG:
+ return(m_lvItemsLog);
+ case TREE_TAB:
+ return(m_lvItemsTab);
+ default:
+ return(0);
+ }
+}
+
+TOptionListGroup* CTranslator::getGroupTree(UINT id)
+{
+ switch(id) {
+ case TREE_MODPLUS:
+ return(m_lvGroupsModPlus);
+ case TREE_NEN:
+ return(m_lvGroupsNEN);
+ case TREE_MSG:
+ return(m_lvGroupsMsg);
+ case TREE_LOG:
+ return(m_lvGroupsLog);
+ case TREE_TAB:
+ return(m_lvGroupsTab);
+ default:
+ return(0);
+ }
+}
+
+void CTranslator::translateGroupTree(TOptionListGroup *lvGroup)
+{
+ UINT i = 0;
+
+ while(lvGroup[i].szName) {
+ lvGroup[i].szName = TranslateTS(lvGroup[i].szName);
+ i++;
+ }
+}
+
+void CTranslator::translateOptionTree(TOptionListItem *lvItems)
+{
+ UINT i = 0;
+
+ while(lvItems[i].szName) {
+ lvItems[i].szName = TranslateTS(lvItems[i].szName);
+ i++;
+ }
+}
diff --git a/plugins/TabSRMM/src/trayicon.cpp b/plugins/TabSRMM/src/trayicon.cpp new file mode 100644 index 0000000000..28de552468 --- /dev/null +++ b/plugins/TabSRMM/src/trayicon.cpp @@ -0,0 +1,374 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * This code is based on and still contains large parts of the the
+ * original chat module for Miranda IM, written and copyrighted
+ * by Joergen Persson in 2005.
+ *
+ * (C) 2005-2009 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: trayicon.cpp 13447 2011-03-14 19:55:07Z george.hazan $
+ *
+ * The code that creates and animates the tray icon.
+ *
+ */
+
+#include "commonheaders.h"
+#pragma hdrstop
+//#include "m_toptoolbar.h"
+
+static BOOL isAnimThreadRunning = TRUE;
+static HANDLE hTrayAnimThread = 0;
+static HICON hIconTrayCurrent = 0;
+HANDLE g_hEvent = 0;
+
+static TCHAR g_eventName[100];
+
+static unsigned __stdcall TrayAnimThread(LPVOID vParam)
+{
+ int iAnimMode = (PluginConfig.m_AnimTrayIcons[0] && PluginConfig.m_AnimTrayIcons[1] && PluginConfig.m_AnimTrayIcons[2] &&
+ PluginConfig.m_AnimTrayIcons[3]);
+ DWORD dwElapsed = 0, dwAnimStep = 0;
+ HICON hIconDefault = iAnimMode ? PluginConfig.m_AnimTrayIcons[0] : PluginConfig.g_iconContainer;
+ DWORD idleTimer = 0;
+ HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, g_eventName);
+
+ do {
+ if (isAnimThreadRunning && PluginConfig.m_UnreadInTray == 0) {
+ if (hIconTrayCurrent != hIconDefault)
+ FlashTrayIcon(hIconDefault); // restore default icon
+ PluginConfig.m_TrayFlashState = 0;
+
+ dwElapsed = 0;
+ dwAnimStep = 0;
+ WaitForSingleObject(hEvent, 30000);
+ ResetEvent(hEvent);
+ idleTimer += 2000;
+ }
+ if (!isAnimThreadRunning) {
+ if (hIconTrayCurrent != hIconDefault)
+ FlashTrayIcon(hIconDefault); // restore default icon
+ PluginConfig.m_TrayFlashState = 0;
+ break;
+ }
+ if (PluginConfig.m_UnreadInTray) {
+ if (iAnimMode) {
+ dwAnimStep++;
+ if (dwAnimStep > 3)
+ dwAnimStep = 0;
+ FlashTrayIcon(PluginConfig.m_AnimTrayIcons[dwAnimStep]); // restore default icon
+ }
+ else { // simple flashing
+ dwElapsed += 200;
+ if (dwElapsed >= 600) {
+ PluginConfig.m_TrayFlashState = !PluginConfig.m_TrayFlashState;
+ dwElapsed = 0;
+ FlashTrayIcon(PluginConfig.m_TrayFlashState ? 0 : hIconDefault); // restore default icon
+ }
+ }
+ Sleep(200);
+ idleTimer += 200;
+ }
+ if (idleTimer >= 2000) {
+ idleTimer = 0;
+ }
+ }
+ while (isAnimThreadRunning);
+ CloseHandle(hEvent);
+ return 0;
+}
+
+void TSAPI CreateTrayMenus(int mode)
+{
+ if (mode) {
+ mir_sntprintf(g_eventName, 100, _T("tsr_evt_%d"), GetCurrentThreadId());
+ g_hEvent = CreateEvent(NULL, FALSE, FALSE, g_eventName);
+ isAnimThreadRunning = TRUE;
+ hTrayAnimThread = (HANDLE)mir_forkthreadex(TrayAnimThread, NULL, 16000, NULL);
+
+ PluginConfig.g_hMenuTrayUnread = CreatePopupMenu();
+ PluginConfig.g_hMenuFavorites = CreatePopupMenu();
+ PluginConfig.g_hMenuRecent = CreatePopupMenu();
+ PluginConfig.g_hMenuTrayContext = GetSubMenu(PluginConfig.g_hMenuContext, 6);
+ ModifyMenu(PluginConfig.g_hMenuTrayContext, 0, MF_BYPOSITION | MF_POPUP,
+ (UINT_PTR)PluginConfig.g_hMenuFavorites, CTranslator::get(CTranslator::GEN_FAVORITES));
+ ModifyMenu(PluginConfig.g_hMenuTrayContext, 2, MF_BYPOSITION | MF_POPUP,
+ (UINT_PTR)PluginConfig.g_hMenuRecent, CTranslator::get(CTranslator::GEN_RECENT_SESSIONS));
+ LoadFavoritesAndRecent();
+ }
+ else {
+ isAnimThreadRunning = FALSE;
+ SetEvent(g_hEvent);
+ WaitForSingleObject(hTrayAnimThread, 5000);
+ CloseHandle(hTrayAnimThread);
+ CloseHandle(g_hEvent);
+ g_hEvent = 0;
+ hTrayAnimThread = 0;
+ if (PluginConfig.g_hMenuTrayUnread != 0) {
+ DestroyMenu(PluginConfig.g_hMenuTrayUnread);
+ PluginConfig.g_hMenuTrayUnread = 0;
+ }
+ if (PluginConfig.g_hMenuFavorites != 0) {
+ DestroyMenu(PluginConfig.g_hMenuFavorites);
+ PluginConfig.g_hMenuFavorites = 0;
+ }
+ if (PluginConfig.g_hMenuRecent != 0) {
+ DestroyMenu(PluginConfig.g_hMenuRecent);
+ PluginConfig.g_hMenuRecent = 0;
+ }
+ }
+}
+/*
+ * create a system tray icon, create all necessary submenus
+ */
+
+void TSAPI CreateSystrayIcon(int create)
+{
+ NOTIFYICONDATA nim;
+
+ nim.cbSize = sizeof(nim);
+ nim.hWnd = PluginConfig.g_hwndHotkeyHandler;
+ nim.uID = 100;
+ nim.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ nim.hIcon = PluginConfig.g_iconContainer;
+ nim.uCallbackMessage = DM_TRAYICONNOTIFY;
+ mir_sntprintf(nim.szTip, 64, _T("%s"), _T("tabSRMM"));
+ if (create && !nen_options.bTrayExist) {
+ Shell_NotifyIcon(NIM_ADD, &nim);
+ nen_options.bTrayExist = TRUE;
+ hIconTrayCurrent = 0;
+ SetEvent(g_hEvent);
+ }
+ else if (create == FALSE && nen_options.bTrayExist) {
+ Shell_NotifyIcon(NIM_DELETE, &nim);
+ nen_options.bTrayExist = FALSE;
+ }
+}
+
+static BOOL CALLBACK FindTrayWnd(HWND hwnd, LPARAM lParam)
+{
+ TCHAR szClassName[256];
+ GetClassName(hwnd, szClassName, 255);
+
+ if (_tcscmp(szClassName, _T("TrayNotifyWnd")) == 0) {
+ RECT *pRect = (RECT *) lParam;
+ GetWindowRect(hwnd, pRect);
+ return TRUE;
+ }
+ if (_tcscmp(szClassName, _T("TrayClockWClass")) == 0) {
+ RECT *pRect = (RECT *) lParam;
+ RECT rectClock;
+ GetWindowRect(hwnd, &rectClock);
+ if (rectClock.bottom < pRect->bottom - 5) // 10 = random fudge factor.
+ pRect->top = rectClock.bottom;
+ else
+ pRect->right = rectClock.left;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void GetTrayWindowRect(LPRECT lprect)
+{
+ HWND hShellTrayWnd = FindWindow(_T("Shell_TrayWnd"), NULL);
+ if (hShellTrayWnd) {
+ GetWindowRect(hShellTrayWnd, lprect);
+ EnumChildWindows(hShellTrayWnd, FindTrayWnd, (LPARAM)lprect);
+ return;
+ }
+}
+
+/*
+ * flash the tray icon
+ * mode = 0 - continue to flash
+ * mode = 1 - restore the original icon
+ */
+
+void TSAPI FlashTrayIcon(HICON hIcon)
+{
+ NOTIFYICONDATA nim;
+
+ hIconTrayCurrent = hIcon;
+
+ if (nen_options.bTraySupport) {
+ nim.cbSize = sizeof(nim);
+ nim.hWnd = PluginConfig.g_hwndHotkeyHandler;
+ nim.uID = 100;
+ nim.uFlags = NIF_ICON;
+ nim.hIcon = hIcon;
+ Shell_NotifyIcon(NIM_MODIFY, &nim);
+ }
+}
+
+/*
+ * add a contact to recent or favorites menu
+ * mode = 1, add
+ * mode = 0, only modify it..
+ * hMenu specifies the menu handle (the menus are identical...)
+ * cares about updating the menu entry. It sets the hIcon (proto status icon) in
+ * dwItemData of the the menu entry, so that the WM_DRAWITEM handler can retrieve it
+ * w/o costly service calls.
+ *
+ * Also, the function does housekeeping on the Recent Sessions menu to enforce the
+ * maximum number of allowed entries (20 at the moment). The oldest (topmost) entry
+ * is deleted, if necessary.
+ */
+
+void TSAPI AddContactToFavorites(HANDLE hContact, const TCHAR *szNickname, const char *szProto, TCHAR *szStatus, WORD wStatus, HICON hIcon, BOOL mode, HMENU hMenu)
+{
+ MENUITEMINFO mii = {0};
+ TCHAR szMenuEntry[80];
+ TCHAR szFinalNick[100];
+
+ if (szNickname == NULL) {
+ mir_sntprintf(szFinalNick, safe_sizeof(szFinalNick), _T("%s"), (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+ }
+ else {
+ _tcsncpy(szFinalNick, szNickname, 100);
+ szFinalNick[99] = 0;
+ }
+
+ if (szProto == NULL)
+ szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (szProto) {
+ if (wStatus == 0)
+ wStatus = DBGetContactSettingWord((HANDLE)hContact, szProto, "Status", ID_STATUS_OFFLINE);
+ if (szStatus == NULL)
+ szStatus = (TCHAR *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, wStatus, GSMDF_TCHAR);
+ }
+ else
+ return;
+
+ if (hIcon == 0)
+ hIcon = LoadSkinnedProtoIcon(szProto, wStatus);
+
+ PROTOACCOUNT *acc = (PROTOACCOUNT *)CallService(MS_PROTO_GETACCOUNT, (WPARAM)0, (LPARAM)szProto);
+
+ if(acc && acc->tszAccountName) {
+ mii.cbSize = sizeof(mii);
+ mir_sntprintf(szMenuEntry, safe_sizeof(szMenuEntry), _T("%s: %s (%s)"), acc->tszAccountName, szFinalNick, szStatus);
+ if (mode) {
+ if (hMenu == PluginConfig.g_hMenuRecent) {
+ if (CheckMenuItem(hMenu, (UINT_PTR)hContact, MF_BYCOMMAND | MF_UNCHECKED) == 0) {
+ DeleteMenu(hMenu, (UINT_PTR)hContact, MF_BYCOMMAND);
+ goto addnew; // move to the end of the menu...
+ }
+ if (GetMenuItemCount(PluginConfig.g_hMenuRecent) > nen_options.wMaxRecent) { // throw out oldest entry in the recent menu...
+ UINT uid = GetMenuItemID(hMenu, 0);
+ if (uid) {
+ DeleteMenu(hMenu, (UINT_PTR)0, MF_BYPOSITION);
+ M->WriteDword((HANDLE)uid, SRMSGMOD_T, "isRecent", 0);
+ }
+ }
+ addnew:
+ M->WriteDword(hContact, SRMSGMOD_T, "isRecent", time(NULL));
+ AppendMenu(hMenu, MF_BYCOMMAND, (UINT_PTR)hContact, szMenuEntry);
+ }
+ else if (hMenu == PluginConfig.g_hMenuFavorites) { // insert the item sorted...
+ MENUITEMINFO mii2 = {0};
+ TCHAR szBuffer[142];
+ int i, c = GetMenuItemCount(PluginConfig.g_hMenuFavorites);
+ mii2.fMask = MIIM_STRING;
+ mii2.cbSize = sizeof(mii2);
+ if (c == 0)
+ InsertMenu(PluginConfig.g_hMenuFavorites, 0, MF_BYPOSITION, (UINT_PTR)hContact, szMenuEntry);
+ else {
+ for (i = 0; i <= c; i++) {
+ mii2.cch = 0;
+ mii2.dwTypeData = NULL;
+ GetMenuItemInfo(PluginConfig.g_hMenuFavorites, i, TRUE, &mii2);
+ mii2.cch++;
+ mii2.dwTypeData = szBuffer;
+ GetMenuItemInfo(PluginConfig.g_hMenuFavorites, i, TRUE, &mii2);
+ if (_tcsncmp((TCHAR *)mii2.dwTypeData, szMenuEntry, 140) > 0 || i == c) {
+ InsertMenu(PluginConfig.g_hMenuFavorites, i, MF_BYPOSITION, (UINT_PTR)hContact, szMenuEntry);
+ break;
+ }
+ }
+ }
+ }
+ }
+ mii.fMask = MIIM_BITMAP | MIIM_DATA;
+ if (!mode) {
+ mii.fMask |= MIIM_STRING;
+ mii.dwTypeData = (LPTSTR)szMenuEntry;
+ mii.cch = lstrlen(szMenuEntry) + 1;
+ }
+ mii.hbmpItem = HBMMENU_CALLBACK;
+ mii.dwItemData = (ULONG_PTR)hIcon;
+ SetMenuItemInfo(hMenu, (UINT)hContact, FALSE, &mii);
+ }
+}
+
+/*
+ * called by CreateSysTrayIcon(), usually on startup or when you activate tray support
+ * at runtime.
+ * scans the contact db for favorites or recent session entries and builds the menus.
+ */
+
+typedef struct _recentEntry {
+ DWORD dwTimestamp;
+ HANDLE hContact;
+} RCENTRY;
+
+void TSAPI LoadFavoritesAndRecent()
+{
+ RCENTRY *recentEntries, rceTemp;
+ DWORD dwRecent;
+ int iIndex = 0, i, j;
+ HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ recentEntries = new RCENTRY[nen_options.wMaxRecent + 1];
+ if (recentEntries != NULL) {
+ while (hContact != 0) {
+ if (M->GetByte(hContact, SRMSGMOD_T, "isFavorite", 0))
+ AddContactToFavorites(hContact, NULL, NULL, NULL, 0, 0, 1, PluginConfig.g_hMenuFavorites);
+ if ((dwRecent = M->GetDword(hContact, "isRecent", 0)) != 0 && iIndex < nen_options.wMaxRecent) {
+ recentEntries[iIndex].dwTimestamp = dwRecent;
+ recentEntries[iIndex++].hContact = hContact;
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ if (iIndex == 0) {
+ free(recentEntries);
+ return;
+ }
+
+ for (i = 0; i < iIndex - 1; i++) {
+ for (j = 0; j < iIndex - 1; j++) {
+ if (recentEntries[j].dwTimestamp > recentEntries[j+1].dwTimestamp) {
+ rceTemp = recentEntries[j];
+ recentEntries[j] = recentEntries[j+1];
+ recentEntries[j+1] = rceTemp;
+ }
+ }
+ }
+ for (i = 0; i < iIndex; i++)
+ AddContactToFavorites(recentEntries[i].hContact, NULL, NULL, NULL, 0, 0, 1, PluginConfig.g_hMenuRecent);
+
+ delete[] recentEntries;
+ }
+}
+
diff --git a/plugins/TabSRMM/src/typingnotify.cpp b/plugins/TabSRMM/src/typingnotify.cpp new file mode 100644 index 0000000000..ac6550c01f --- /dev/null +++ b/plugins/TabSRMM/src/typingnotify.cpp @@ -0,0 +1,573 @@ +#include "commonheaders.h"
+#pragma hdrstop
+HANDLE hTypingNotify;
+
+static INT_PTR EnableDisableMenuCommand(WPARAM wParam,LPARAM lParam)
+{
+ Disabled = !(Disabled);
+
+ if (PopupService) {
+
+ CLISTMENUITEM mi = { 0 };
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_ICON | CMIM_NAME;
+
+ if (!Disabled) {
+ mi.pszName = LPGEN("Disable &typing notification");
+ mi.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ENABLED));
+ } else {
+ mi.pszName = LPGEN("Enable &typing notification");
+ mi.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_DISABLED));
+ }
+
+ CallService(MS_CLIST_MODIFYMENUITEM,(WPARAM)hDisableMenu,(LPARAM)&mi);
+ }
+
+ return 0;
+}
+
+static int CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_COMMAND:
+ if (HIWORD(wParam) == STN_CLICKED) {
+ HANDLE hContact = PUGetContact(hWnd);
+ CallService(MS_MSG_SENDMESSAGE "W",(WPARAM)hContact,0);
+ PUDeletePopUp(hWnd);
+ return 1;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ PUDeletePopUp(hWnd);
+ return 1;
+
+ case UM_INITPOPUP: {
+ HANDLE hContact = PUGetContact(hWnd);
+ WindowList_Add(hPopUpsList, hWnd, hContact);
+ }
+ return 1;
+
+ case UM_FREEPLUGINDATA:
+ WindowList_Remove(hPopUpsList, hWnd);
+ return 1;
+ default:
+ break;
+ }
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+int TN_TypingMessage(WPARAM wParam, LPARAM lParam)
+{
+ POPUPDATAT_V2 ppd = { 0 };
+ TCHAR *szContactName = NULL;
+ HWND hPopUpWnd = NULL;
+ int notyping;
+
+ // hidden & ignored contacts check
+ if (M->GetByte((HANDLE)wParam, "CList", "Hidden", 0) || (M->GetDword((HANDLE)wParam, "Ignore", "Mask1",0) & 1)) // 9 - online notification
+ return 0;
+
+ szContactName = (TCHAR*) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, wParam, GSMDF_TCHAR);
+
+ if (PopupService && !Disabled) {
+ if (OnePopUp) {
+ hPopUpWnd = WindowList_Find(hPopUpsList, (HANDLE) wParam);
+ while (hPopUpWnd) {
+ PUDeletePopUp(hPopUpWnd);
+ hPopUpWnd = WindowList_Find(hPopUpsList, (HANDLE) wParam);
+ }
+ }
+
+ switch (lParam) {
+ case PROTOTYPE_CONTACTTYPING_OFF:
+ if (StopDisabled)
+ return 0;
+ lstrcpyn(ppd.lptzContactName, szContactName, MAX_CONTACTNAME);
+ lstrcpyn(ppd.lptzText, szStop, MAX_SECONDLINE);
+ ppd.hNotification = hntfStopped;
+ notyping = 1;
+ break;
+ default:
+ if (StartDisabled)
+ return 0;
+ lstrcpyn(ppd.lptzContactName, szContactName, MAX_CONTACTNAME);
+ lstrcpyn(ppd.lptzText, szStart, MAX_SECONDLINE);
+ ppd.hNotification = hntfStarted;
+ notyping = 0;
+ break;
+ }
+
+ switch (ColorMode) {
+ case COLOR_OWN:
+ ppd.colorBack = colorPicker[2* notyping ].color;
+ ppd.colorText = colorPicker[2* notyping + 1].color;
+ break;
+ case COLOR_WINDOWS:
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ break;
+ case COLOR_POPUP:
+ default:
+ ppd.colorBack = ppd.colorText = 0;
+ break;
+ }
+
+ if (notyping)
+ switch (TimeoutMode2) {
+ case TIMEOUT_CUSTOM:
+ ppd.iSeconds = Timeout2;
+ break;
+ case TIMEOUT_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+ case TIMEOUT_POPUP:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ else
+ switch (TimeoutMode) {
+ case TIMEOUT_CUSTOM:
+ ppd.iSeconds = Timeout;
+ break;
+ case TIMEOUT_PROTO:
+ ppd.iSeconds = (DWORD) lParam;
+ break;
+ case TIMEOUT_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+ case TIMEOUT_POPUP:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+
+ ppd.lchIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
+ ppd.lchContact = (HANDLE) wParam;
+ ppd.PluginWindowProc = (WNDPROC) PopupDlgProc;
+ ppd.PluginData = NULL;
+
+ ppd.cbSize = sizeof(ppd);
+
+ CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, APF_NEWDATA);
+ }
+ return 0;
+}
+
+static INT_PTR CALLBACK DlgProcOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WORD i;
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ TranslateDialogDefault(hwndDlg);
+
+ if (!ServiceExists(MS_POPUP_ADDPOPUPT))
+ SetDlgItemText(hwndDlg, IDC_INFO, CTranslator::get(CTranslator::GEN_MTN_POPUP_WARNING));
+ else if (!PopupService)
+ SetDlgItemText(hwndDlg, IDC_INFO, CTranslator::get(CTranslator::GEN_MTN_POPUP_UNSUPPORTED));
+ if (ColorMode == COLOR_WINDOWS) {
+ CheckDlgButton(hwndDlg, IDC_USEWINCOLORS, BST_CHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_USEPOPUPCOLORS, FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_USEWINCOLORS, TRUE);
+ CheckDlgButton(hwndDlg, IDC_USEPOPUPCOLORS, BST_UNCHECKED);
+ } else if (ColorMode == COLOR_POPUP) {
+ CheckDlgButton(hwndDlg, IDC_USEWINCOLORS, BST_UNCHECKED);
+ Utils::enableDlgControl(hwndDlg, IDC_USEWINCOLORS, FALSE);
+ Utils::enableDlgControl(hwndDlg, IDC_USEPOPUPCOLORS, TRUE);
+ CheckDlgButton(hwndDlg, IDC_USEPOPUPCOLORS, BST_CHECKED);
+ }
+
+ for (i = 0; i < sizeof(colorPicker) / sizeof(colorPicker[0]); i++) {
+ SendDlgItemMessage(hwndDlg, colorPicker[i].res, CPM_SETCOLOUR, 0, colorPicker[i].color);
+ Utils::enableDlgControl(hwndDlg, colorPicker[i].res, (ColorMode == COLOR_OWN));
+ }
+
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_PERMANENT, (TimeoutMode == TIMEOUT_PERMANENT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_POPUP, (TimeoutMode == TIMEOUT_POPUP) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_PROTO, (TimeoutMode == TIMEOUT_PROTO) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_CUSTOM, (TimeoutMode == TIMEOUT_CUSTOM) ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemInt(hwndDlg, IDC_TIMEOUT_VALUE, Timeout, 0);
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE, TimeoutMode == TIMEOUT_CUSTOM);
+
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_PERMANENT2, (TimeoutMode2 == TIMEOUT_PERMANENT) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_POPUP2, (TimeoutMode2 == TIMEOUT_POPUP) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_CUSTOM2, (TimeoutMode2 == TIMEOUT_CUSTOM) ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemInt(hwndDlg, IDC_TIMEOUT_VALUE2, Timeout2, 0);
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE2, TimeoutMode2 == TIMEOUT_CUSTOM);
+
+ CheckDlgButton(hwndDlg, IDC_START, (StartDisabled) ? BST_UNCHECKED : BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_STOP, (StopDisabled) ? BST_UNCHECKED : BST_CHECKED);
+
+ CheckDlgButton(hwndDlg, IDC_ONEPOPUP, (OnePopUp) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SHOWMENU, (ShowMenu) ? BST_CHECKED : BST_UNCHECKED);
+
+ Utils::enableDlgControl(hwndDlg, IDC_ONEPOPUP, PopupService);
+ Utils::enableDlgControl(hwndDlg, IDC_SHOWMENU, PopupService);
+ Utils::enableDlgControl(hwndDlg, IDC_PREVIEW, PopupService/*&&!ServiceExists(MS_POPUP_REGISTERNOTIFICATION)*/);
+
+ newTimeout = Timeout;
+ newTimeoutMode = TimeoutMode;
+ newTimeout2 = Timeout2;
+ newTimeoutMode2 = TimeoutMode2;
+ newColorMode = ColorMode;
+ }
+ break;
+
+ case WM_COMMAND: {
+ WORD idCtrl = LOWORD(wParam), wNotifyCode = HIWORD(wParam);
+
+ if (wNotifyCode == CPN_COLOURCHANGED) {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+
+ switch (idCtrl) {
+ case IDC_USEWINCOLORS: {
+ BOOL bEnableOthers = FALSE;
+
+ if (wNotifyCode != BN_CLICKED)
+ break;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_USEWINCOLORS)) {
+ newColorMode = COLOR_WINDOWS;
+ bEnableOthers = FALSE;
+ } else {
+ newColorMode = COLOR_OWN;
+ bEnableOthers = TRUE;
+ }
+
+ for (i = 0; i < sizeof(colorPicker) / sizeof(colorPicker[0]); i++)
+ Utils::enableDlgControl(hwndDlg, colorPicker[i].res, bEnableOthers);
+
+ Utils::enableDlgControl(hwndDlg, IDC_USEPOPUPCOLORS, bEnableOthers);
+
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case IDC_USEPOPUPCOLORS: {
+ BOOL bEnableOthers = FALSE;
+ if (wNotifyCode != BN_CLICKED)
+ break;
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_USEPOPUPCOLORS)) {
+ newColorMode = COLOR_POPUP;
+ bEnableOthers = FALSE;
+ } else {
+ newColorMode = COLOR_OWN;
+ bEnableOthers = TRUE;
+ }
+
+ for (i = 0; i < sizeof(colorPicker) / sizeof(colorPicker[0]); i++)
+ Utils::enableDlgControl(hwndDlg, colorPicker[i].res, bEnableOthers);
+
+ Utils::enableDlgControl(hwndDlg, IDC_USEWINCOLORS, bEnableOthers);
+
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case IDC_ONEPOPUP:
+ case IDC_CLIST:
+ case IDC_DISABLED:
+ case IDC_SHOWMENU:
+ case IDC_START:
+ case IDC_STOP:
+ case IDC_WOCL:
+ if (wNotifyCode == BN_CLICKED)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_PREVIEW: {
+ POPUPDATAT_V2 ppd = { 0 };
+ char *szProto = NULL;
+ int i, notyping;
+
+ HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+
+ if (!PopupService)
+ break;
+
+ while (hContact) {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (szProto != NULL)
+ break;
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+
+ for (i = 0; i < 2; i++) {
+
+ switch (i) {
+ case PROTOTYPE_CONTACTTYPING_OFF:
+ lstrcpy(ppd.lptzContactName, CTranslator::get(CTranslator::GEN_CONTACT));
+ lstrcpyn(ppd.lptzText, szStop, MAX_SECONDLINE);
+ notyping = 1;
+ break;
+ default:
+ lstrcpy(ppd.lptzContactName, CTranslator::get(CTranslator::GEN_CONTACT));
+ lstrcpyn(ppd.lptzText, szStart, MAX_SECONDLINE);
+ notyping = 0;
+ break;
+ }
+
+ switch (newColorMode) {
+ case COLOR_OWN:
+ ppd.colorText = SendDlgItemMessage(hwndDlg, colorPicker[2* notyping + 1].res, CPM_GETCOLOUR, 0, 0);
+ ppd.colorBack = SendDlgItemMessage(hwndDlg, colorPicker[2* notyping ].res, CPM_GETCOLOUR, 0, 0);
+ break;
+ case COLOR_WINDOWS:
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ break;
+ case COLOR_POPUP:
+ default:
+ ppd.colorBack = ppd.colorText = 0;
+ break;
+ }
+
+ if (notyping)
+ switch (newTimeoutMode2) {
+ case TIMEOUT_CUSTOM:
+ ppd.iSeconds = newTimeout2;
+ break;
+ case TIMEOUT_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+ case TIMEOUT_POPUP:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ else
+ switch (newTimeoutMode) {
+ case TIMEOUT_CUSTOM:
+ ppd.iSeconds = newTimeout;
+ break;
+ case TIMEOUT_PROTO:
+ ppd.iSeconds = 10;
+ break;
+ case TIMEOUT_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+ case TIMEOUT_POPUP:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+
+ ppd.lchIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
+ ppd.lchContact = (HANDLE) wParam;
+ ppd.PluginWindowProc = NULL;
+ ppd.PluginData = NULL;
+
+ CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, 0);
+
+ }
+ break;
+ }
+ case IDC_TIMEOUT_POPUP2:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode2 = TIMEOUT_POPUP;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE2, 0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_CUSTOM2:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode2 = TIMEOUT_CUSTOM;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE2, 1);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_POPUP:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode = TIMEOUT_POPUP;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE, 0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_PERMANENT:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode = TIMEOUT_PERMANENT;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE, 0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_PERMANENT2:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode2 = TIMEOUT_PERMANENT;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE2, 0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_CUSTOM:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode = TIMEOUT_CUSTOM;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE, 1);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_PROTO:
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ newTimeoutMode = TIMEOUT_PROTO;
+ Utils::enableDlgControl(hwndDlg, IDC_TIMEOUT_VALUE, 0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_VALUE:
+ case IDC_TIMEOUT_VALUE2: {
+ int newValue = GetDlgItemInt(hwndDlg, idCtrl, NULL, 0);
+
+ if (wNotifyCode == EN_KILLFOCUS) {
+ int oldValue;
+
+ if (idCtrl == IDC_TIMEOUT_VALUE)
+ oldValue = newTimeout;
+ else
+ oldValue = newTimeout2;
+
+ if (newValue != oldValue)
+ SetDlgItemInt(hwndDlg, idCtrl, oldValue, 0);
+ return TRUE;
+ }
+ if (wNotifyCode != EN_CHANGE || (HWND) lParam != GetFocus())
+ return TRUE;
+
+ if (newValue > TIMEOUT_MAXVALUE)
+ newValue = TIMEOUT_MAXVALUE;
+ else if (newValue < TIMEOUT_MINVALUE)
+ newValue = TIMEOUT_MINVALUE;
+
+ if (idCtrl == IDC_TIMEOUT_VALUE)
+ newTimeout = newValue;
+ else
+ newTimeout2 = newValue;
+
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+
+ }
+ break;
+ }
+ case WM_NOTIFY: {
+ switch (((LPNMHDR) lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ for (i = 0; i < sizeof(colorPicker) / sizeof(colorPicker[0]); i++) {
+ colorPicker[i].color = SendDlgItemMessage(hwndDlg, colorPicker[i].res, CPM_GETCOLOUR, 0, 0);
+ M->WriteDword(Module, colorPicker[i].desc, colorPicker[i].color);
+ }
+
+ Timeout = newTimeout;
+ TimeoutMode = newTimeoutMode;
+ Timeout2 = newTimeout2;
+ TimeoutMode2 = newTimeoutMode2;
+ ColorMode = newColorMode;
+
+ if (Disabled != IsDlgButtonChecked(hwndDlg, IDC_DISABLED))
+ EnableDisableMenuCommand(0, 0);
+
+ StartDisabled = IsDlgButtonChecked(hwndDlg, IDC_START) ? 0 : 2;
+ StopDisabled = IsDlgButtonChecked(hwndDlg, IDC_STOP) ? 0 : 4;
+ OnePopUp = IsDlgButtonChecked(hwndDlg, IDC_ONEPOPUP);
+ ShowMenu = IsDlgButtonChecked(hwndDlg, IDC_SHOWMENU);
+
+ M->WriteByte(Module, SET_ONEPOPUP, OnePopUp);
+ M->WriteByte(Module, SET_SHOWDISABLEMENU, ShowMenu);
+ M->WriteByte(Module, SET_DISABLED, (BYTE) (StartDisabled | StopDisabled));
+ M->WriteByte(Module, SET_COLOR_MODE, ColorMode);
+ M->WriteByte(Module, SET_TIMEOUT_MODE, TimeoutMode);
+ M->WriteByte(Module, SET_TIMEOUT, (BYTE) Timeout);
+ M->WriteByte(Module, SET_TIMEOUT_MODE2, TimeoutMode2);
+ M->WriteByte(Module, SET_TIMEOUT2, (BYTE) Timeout2);
+
+ return TRUE;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+int TN_OptionsInitialize(WPARAM wParam, LPARAM lParam)
+{
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+
+ if (PluginConfig.g_PopupAvail) {
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = g_hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_TYPINGNOTIFYPOPUP);
+ odp.pszTitle = LPGEN("Typing Notify");
+ odp.pszGroup = LPGEN("PopUps");
+ odp.groupPosition = 910000000;
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.pfnDlgProc = DlgProcOpts;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ }
+ return 0;
+}
+
+int TN_ModuleInit()
+{
+ WORD i;
+
+ PopupService = (PluginConfig.g_PopupWAvail || PluginConfig.g_PopupAvail);
+
+ hPopUpsList = (HANDLE) CallService(MS_UTILS_ALLOCWINDOWLIST,0,0);
+
+ OnePopUp = M->GetByte(Module,SET_ONEPOPUP,DEF_ONEPOPUP);
+ ShowMenu = M->GetByte(Module,SET_SHOWDISABLEMENU,DEF_SHOWDISABLEMENU);
+
+ i = M->GetByte(Module,SET_DISABLED,DEF_DISABLED);
+ Disabled = i & 1;
+ StartDisabled = i & 2;
+ StopDisabled = i & 4;
+
+ ColorMode = M->GetByte(Module,SET_COLOR_MODE,DEF_COLOR_MODE);
+ TimeoutMode = M->GetByte(Module,SET_TIMEOUT_MODE,DEF_TIMEOUT_MODE);
+ Timeout = M->GetByte(Module,SET_TIMEOUT,DEF_TIMEOUT);
+ TimeoutMode2 = M->GetByte(Module,SET_TIMEOUT_MODE2,DEF_TIMEOUT_MODE2);
+ Timeout2 = M->GetByte(Module,SET_TIMEOUT2,DEF_TIMEOUT2);
+
+ if (!(M->GetDword(Module, colorPicker[0].desc, 1) && !M->GetDword(Module, colorPicker[0].desc, 0)))
+ for (i = 0; i < sizeof(colorPicker) / sizeof(colorPicker[0]); i++)
+ colorPicker[i].color = M->GetDword(Module,colorPicker[i].desc,0);
+
+ mir_sntprintf(szStart, sizeof(szStart), CTranslator::get(CTranslator::GEN_MTN_START));
+ mir_sntprintf(szStop, sizeof(szStop), CTranslator::get(CTranslator::GEN_MTN_STOP));
+
+ if (PopupService && ShowMenu) {
+ CLISTMENUITEM mi = { 0 };
+ hTypingNotify = CreateServiceFunction("TypingNotify/EnableDisableMenuCommand", EnableDisableMenuCommand);
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = 0;
+
+ if (!Disabled) {
+ mi.pszName = LPGEN("Disable &typing notification");
+ mi.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ENABLED));
+ } else {
+ mi.pszName = LPGEN("Enable &typing notification");
+ mi.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_DISABLED));
+ }
+ mi.pszService = "TypingNotify/EnableDisableMenuCommand";
+ mi.pszPopupName = LPGEN("PopUps");
+ hDisableMenu = (HANDLE) CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+ }
+ SkinAddNewSoundEx("TNStart", "Instant messages", "Contact started typing");
+ SkinAddNewSoundEx("TNStop", "Instant messages", "Contact stopped typing");
+
+ return 0;
+}
+
+int TN_ModuleDeInit()
+{
+ M->WriteByte(Module, SET_DISABLED, (BYTE) (Disabled | StartDisabled | StopDisabled));
+ return 0;
+}
diff --git a/plugins/TabSRMM/src/userprefs.cpp b/plugins/TabSRMM/src/userprefs.cpp new file mode 100644 index 0000000000..5691e71a65 --- /dev/null +++ b/plugins/TabSRMM/src/userprefs.cpp @@ -0,0 +1,593 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: userprefs.cpp 12893 2010-10-04 06:19:57Z silvercircle $
+ *
+ *
+ * global/local message log options
+ * local (per user) template overrides
+ * view mode (ieview/default)
+ * text formatting
+ *
+ */
+
+#include "commonheaders.h"
+
+#pragma hdrstop
+#include <uxtheme.h>
+
+#define UPREF_ACTION_APPLYOPTIONS 1
+#define UPREF_ACTION_REMAKELOG 2
+#define UPREF_ACTION_SWITCHLOGVIEWER 4
+
+extern HANDLE hUserPrefsWindowList;
+extern struct TCpTable cpTable[];
+
+static HWND hCpCombo;
+
+static BOOL CALLBACK FillCpCombo(LPCTSTR str)
+{
+ int i;
+ UINT cp;
+
+ cp = _ttoi(str);
+ for (i = 0; cpTable[i].cpName != NULL && cpTable[i].cpId != cp; i++);
+ if (cpTable[i].cpName != NULL) {
+ LRESULT iIndex = SendMessage(hCpCombo, CB_ADDSTRING, -1, (LPARAM) TranslateTS(cpTable[i].cpName));
+ SendMessage(hCpCombo, CB_SETITEMDATA, (WPARAM)iIndex, cpTable[i].cpId);
+ }
+ return TRUE;
+}
+
+static int have_ieview = 0, have_hpp = 0;
+
+static INT_PTR CALLBACK DlgProcUserPrefs(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG: {
+ DWORD sCodePage;
+ int i;
+ DWORD maxhist = M->GetDword((HANDLE)lParam, "maxhist", 0);
+ BYTE bIEView = M->GetByte((HANDLE)lParam, "ieview", 0);
+ BYTE bHPP = M->GetByte((HANDLE)lParam, "hpplog", 0);
+ int iLocalFormat = M->GetDword((HANDLE)lParam, "sendformat", 0);
+ BYTE bRTL = M->GetByte((HANDLE)lParam, "RTL", 0);
+ BYTE bLTR = M->GetByte((HANDLE)lParam, "RTL", 1);
+ BYTE bSplit = M->GetByte((HANDLE)lParam, "splitoverride", 0);
+ BYTE bInfoPanel = M->GetByte((HANDLE)lParam, "infopanel", 0);
+ BYTE bAvatarVisible = M->GetByte((HANDLE)lParam, "hideavatar", -1);
+ char *szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)lParam, 0);
+ int def_log_index = 1, hpp_log_index = 1, ieview_log_index = 1;
+
+ have_ieview = ServiceExists(MS_IEVIEW_WINDOW);
+ have_hpp = ServiceExists("History++/ExtGrid/NewWindow");
+
+ hContact = (HANDLE)lParam;
+
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+
+ SendDlgItemMessage(hwndDlg, IDC_INFOPANEL, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPGLOBAL));
+ SendDlgItemMessage(hwndDlg, IDC_INFOPANEL, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPON));
+ SendDlgItemMessage(hwndDlg, IDC_INFOPANEL, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPOFF));
+ SendDlgItemMessage(hwndDlg, IDC_INFOPANEL, CB_SETCURSEL, bInfoPanel == 0 ? 0 : (bInfoPanel == 1 ? 1 : 2), 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_SHOWAVATAR, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPGLOBAL));
+ SendDlgItemMessage(hwndDlg, IDC_SHOWAVATAR, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_AVON));
+ SendDlgItemMessage(hwndDlg, IDC_SHOWAVATAR, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_AVOFF));
+ SendDlgItemMessage(hwndDlg, IDC_SHOWAVATAR, CB_SETCURSEL, bAvatarVisible == 0xff ? 0 : (bAvatarVisible == 1 ? 1 : 2), 0);
+
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPGLOBAL));
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_FORCEDEFAULT));
+
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETITEMDATA, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETITEMDATA, 1, 1);
+
+ if(have_hpp) {
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_FORCEHPP));
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETITEMDATA, 2, 2);
+ }
+
+ if(have_ieview) {
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_FORCEIEV));
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETITEMDATA, have_hpp ? 3 : 2, 3);
+ }
+
+ if (bIEView == 0 && bHPP == 0)
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETCURSEL, 0, 0);
+ else if (bIEView == 0xff && bHPP == 0xff)
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETCURSEL, 1, 0);
+ else {
+ if(bHPP == 1)
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETCURSEL, have_hpp ? 2 : 0, 0);
+ if(bIEView == 1)
+ SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_SETCURSEL, (have_hpp && have_ieview) ? 3 : (have_ieview ? 2 : 0), 0);
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_TEXTFORMATTING, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_IPGLOBAL));
+ SendDlgItemMessage(hwndDlg, IDC_TEXTFORMATTING, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_BBCODE));
+ SendDlgItemMessage(hwndDlg, IDC_TEXTFORMATTING, CB_INSERTSTRING, -1, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_FORMATTING_OFF));
+
+ SendDlgItemMessage(hwndDlg, IDC_TEXTFORMATTING, CB_SETCURSEL, iLocalFormat == 0 ? 0 : (iLocalFormat == -1 ? 2 : (1)), 0);
+
+ if (CheckMenuItem(PluginConfig.g_hMenuFavorites, (UINT_PTR)lParam, MF_BYCOMMAND | MF_UNCHECKED) == -1)
+ CheckDlgButton(hwndDlg, IDC_ISFAVORITE, FALSE);
+ else
+ CheckDlgButton(hwndDlg, IDC_ISFAVORITE, TRUE);
+
+ CheckDlgButton(hwndDlg, IDC_PRIVATESPLITTER, bSplit);
+ CheckDlgButton(hwndDlg, IDC_TEMPLOVERRIDE, M->GetByte(hContact, TEMPLATES_MODULE, "enabled", 0));
+ CheckDlgButton(hwndDlg, IDC_RTLTEMPLOVERRIDE, M->GetByte(hContact, RTLTEMPLATES_MODULE, "enabled", 0));
+
+ //MAD
+ CheckDlgButton(hwndDlg, IDC_LOADONLYACTUAL, M->GetByte(hContact, "ActualHistory", 0));
+ //
+ SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_SETRANGE, 0, MAKELONG(1000, 5));
+ SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_SETPOS, 0, maxhist);
+ Utils::enableDlgControl(hwndDlg, IDC_TRIMSPIN, maxhist != 0);
+ Utils::enableDlgControl(hwndDlg, IDC_TRIM, maxhist != 0);
+ CheckDlgButton(hwndDlg, IDC_ALWAYSTRIM2, maxhist != 0);
+
+ hCpCombo = GetDlgItem(hwndDlg, IDC_CODEPAGES);
+ sCodePage = M->GetDword(hContact, "ANSIcodepage", 0);
+ EnumSystemCodePages((CODEPAGE_ENUMPROC)FillCpCombo, CP_INSTALLED);
+ SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_INSERTSTRING, 0, (LPARAM)CTranslator::getOpt(CTranslator::OPT_UPREFS_DEFAULTCP));
+ if (sCodePage == 0)
+ SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_SETCURSEL, (WPARAM)0, 0);
+ else {
+ for (i = 0; i < SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_GETCOUNT, 0, 0); i++) {
+ if (SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_GETITEMDATA, (WPARAM)i, 0) == (LRESULT)sCodePage)
+ SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_SETCURSEL, (WPARAM)i, 0);
+ }
+ }
+ CheckDlgButton(hwndDlg, IDC_FORCEANSI, M->GetByte(hContact, "forceansi", 0) ? 1 : 0);
+ CheckDlgButton(hwndDlg, IDC_IGNORETIMEOUTS, M->GetByte(hContact, "no_ack", 0));
+
+ ShowWindow(hwndDlg, SW_SHOW);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_ALWAYSTRIM2:
+ Utils::enableDlgControl(hwndDlg, IDC_TRIMSPIN, IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM2));
+ Utils::enableDlgControl(hwndDlg, IDC_TRIM, IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM2));
+ break;
+ case WM_USER + 100: {
+ struct TWindowData *dat = 0;
+ DWORD *pdwActionToTake = (DWORD *)lParam;
+ int iIndex = CB_ERR, iMode = -1;
+ DWORD newCodePage;
+ unsigned int iOldIEView;
+ HWND hWnd = M->FindWindow(hContact);
+ DWORD sCodePage = M->GetDword(hContact, "ANSIcodepage", 0);
+ BYTE bInfoPanel, bOldInfoPanel = M->GetByte(hContact, "infopanel", 0);
+ BYTE bAvatarVisible = 0;
+
+ if (hWnd) {
+ dat = (struct TWindowData *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ if (dat)
+ iOldIEView = GetIEViewMode(hWnd, dat->hContact);
+ }
+ iIndex = SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_GETCURSEL, 0, 0);
+ iMode = SendDlgItemMessage(hwndDlg, IDC_IEVIEWMODE, CB_GETITEMDATA, iIndex, 0);
+
+ if (iIndex != CB_ERR && (iMode >= 0 && iMode <= 3)) {
+ unsigned int iNewIEView;
+
+ switch (iMode) {
+ case 0:
+ M->WriteByte(hContact, SRMSGMOD_T, "ieview", 0);
+ M->WriteByte(hContact, SRMSGMOD_T, "hpplog", 0);
+ break;
+ case 1:
+ M->WriteByte(hContact, SRMSGMOD_T, "ieview", -1);
+ M->WriteByte(hContact, SRMSGMOD_T, "hpplog", -1);
+ break;
+ case 2:
+ M->WriteByte(hContact, SRMSGMOD_T, "ieview", -1);
+ M->WriteByte(hContact, SRMSGMOD_T, "hpplog", 1);
+ break;
+ case 3:
+ M->WriteByte(hContact, SRMSGMOD_T, "ieview", 1);
+ M->WriteByte(hContact, SRMSGMOD_T, "hpplog", -1);
+ break;
+ default:
+ break;
+ }
+ if (hWnd && dat) {
+ iNewIEView = GetIEViewMode(hWnd, dat->hContact);
+ if (iNewIEView != iOldIEView) {
+ if(pdwActionToTake)
+ *pdwActionToTake |= UPREF_ACTION_SWITCHLOGVIEWER;
+ }
+ }
+ }
+ if ((iIndex = SendDlgItemMessage(hwndDlg, IDC_TEXTFORMATTING, CB_GETCURSEL, 0, 0)) != CB_ERR) {
+ if (iIndex == 0)
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "sendformat");
+ else
+ M->WriteDword(hContact, SRMSGMOD_T, "sendformat", iIndex == 2 ? -1 : 1);
+ }
+ iIndex = SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_GETCURSEL, 0, 0);
+ if ((newCodePage = (DWORD)SendDlgItemMessage(hwndDlg, IDC_CODEPAGES, CB_GETITEMDATA, (WPARAM)iIndex, 0)) != sCodePage) {
+ M->WriteDword(hContact, SRMSGMOD_T, "ANSIcodepage", (DWORD)newCodePage);
+ if (hWnd && dat) {
+ dat->codePage = newCodePage;
+ SendMessage(hWnd, DM_UPDATETITLE, 0, 1);
+ }
+ }
+ if ((IsDlgButtonChecked(hwndDlg, IDC_FORCEANSI) ? 1 : 0) != M->GetByte(hContact, "forceansi", 0)) {
+ M->WriteByte(hContact, SRMSGMOD_T, "forceansi", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_FORCEANSI) ? 1 : 0));
+ if (hWnd && dat)
+ dat->sendMode = IsDlgButtonChecked(hwndDlg, IDC_FORCEANSI) ? dat->sendMode | SMODE_FORCEANSI : dat->sendMode & ~SMODE_FORCEANSI;
+ }
+ if (IsDlgButtonChecked(hwndDlg, IDC_ISFAVORITE)) {
+ if (!M->GetByte(hContact, SRMSGMOD_T, "isFavorite", 0))
+ AddContactToFavorites(hContact, NULL, NULL, NULL, 0, 0, 1, PluginConfig.g_hMenuFavorites);
+ } else
+ DeleteMenu(PluginConfig.g_hMenuFavorites, (UINT_PTR)hContact, MF_BYCOMMAND);
+
+ M->WriteByte(hContact, SRMSGMOD_T, "isFavorite", (WORD)(IsDlgButtonChecked(hwndDlg, IDC_ISFAVORITE) ? 1 : 0));
+ M->WriteByte(hContact, SRMSGMOD_T, "splitoverride", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_PRIVATESPLITTER) ? 1 : 0));
+
+ M->WriteByte(hContact, TEMPLATES_MODULE, "enabled", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_TEMPLOVERRIDE)));
+ M->WriteByte(hContact, RTLTEMPLATES_MODULE, "enabled", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_RTLTEMPLOVERRIDE)));
+
+ bAvatarVisible = (BYTE)SendDlgItemMessage(hwndDlg, IDC_SHOWAVATAR, CB_GETCURSEL, 0, 0);
+ if(bAvatarVisible == 0)
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "hideavatar");
+ else
+ M->WriteByte(hContact, SRMSGMOD_T, "hideavatar", (BYTE)(bAvatarVisible == 1 ? 1 : 0));
+
+ bInfoPanel = (BYTE)SendDlgItemMessage(hwndDlg, IDC_INFOPANEL, CB_GETCURSEL, 0, 0);
+ if (bInfoPanel != bOldInfoPanel) {
+ M->WriteByte(hContact, SRMSGMOD_T, "infopanel", (BYTE)(bInfoPanel == 0 ? 0 : (bInfoPanel == 1 ? 1 : -1)));
+ if (hWnd && dat)
+ SendMessage(hWnd, DM_SETINFOPANEL, 0, 0);
+ }
+ if (IsDlgButtonChecked(hwndDlg, IDC_ALWAYSTRIM2))
+ M->WriteDword(hContact, SRMSGMOD_T, "maxhist", (DWORD)SendDlgItemMessage(hwndDlg, IDC_TRIMSPIN, UDM_GETPOS, 0, 0));
+ else
+ M->WriteDword(hContact, SRMSGMOD_T, "maxhist", 0);
+
+ //MAD
+ if (IsDlgButtonChecked(hwndDlg, IDC_LOADONLYACTUAL)){
+ M->WriteByte(hContact, SRMSGMOD_T, "ActualHistory", 1);
+ if (hWnd && dat) dat->bActualHistory=TRUE;
+ }else{
+ M->WriteByte(hContact, SRMSGMOD_T, "ActualHistory", 0);
+ if (hWnd && dat) dat->bActualHistory=FALSE;
+ }
+ //
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_IGNORETIMEOUTS)) {
+ M->WriteByte(hContact, SRMSGMOD_T, "no_ack", 1);
+ if (hWnd && dat)
+ dat->sendMode |= SMODE_NOACK;
+ } else {
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "no_ack");
+ if (hWnd && dat)
+ dat->sendMode &= ~SMODE_NOACK;
+ }
+ if (hWnd && dat) {
+ SendMessage(hWnd, DM_CONFIGURETOOLBAR, 0, 1);
+ dat->panelWidth = -1;
+ ShowPicture(dat, FALSE);
+ SendMessage(hWnd, WM_SIZE, 0, 0);
+ DM_ScrollToBottom(dat, 0, 1);
+ }
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static struct _checkboxes {
+ UINT uId;
+ UINT uFlag;
+} checkboxes[] = {
+ IDC_UPREFS_GRID, MWF_LOG_GRID,
+ IDC_UPREFS_SHOWICONS, MWF_LOG_SHOWICONS,
+ IDC_UPREFS_SHOWSYMBOLS, MWF_LOG_SYMBOLS,
+ IDC_UPREFS_INOUTICONS, MWF_LOG_INOUTICONS,
+ IDC_UPREFS_SHOWTIMESTAMP, MWF_LOG_SHOWTIME,
+ IDC_UPREFS_SHOWDATES, MWF_LOG_SHOWDATES,
+ IDC_UPREFS_SHOWSECONDS, MWF_LOG_SHOWSECONDS,
+ IDC_UPREFS_LOCALTIME, MWF_LOG_LOCALTIME,
+ IDC_UPREFS_INDENT, MWF_LOG_INDENT,
+ IDC_UPREFS_GROUPING, MWF_LOG_GROUPMODE,
+ IDC_UPREFS_BBCODE, MWF_LOG_BBCODE,
+ IDC_UPREFS_RTL, MWF_LOG_RTL,
+ IDC_UPREFS_LOGSTATUS, MWF_LOG_STATUSCHANGES,
+ IDC_UPREFS_NORMALTEMPLATES, MWF_LOG_NORMALTEMPLATES,
+ 0, 0
+};
+
+/*
+ * loads message log and other "per contact" flags
+ * it uses the global flag value (0, mwflags) and then merges per contact settings
+ * based on the mask value.
+
+ * ALWAYS mask dat->dwFlags with MWF_LOG_ALL to only affect real flag bits and
+ * ignore temporary bits.
+ */
+
+int TSAPI LoadLocalFlags(HWND hwnd, struct TWindowData *dat)
+{
+ int i = 0;
+ DWORD dwMask = M->GetDword(dat->hContact, "mwmask", 0);
+ DWORD dwLocal = M->GetDword(dat->hContact, "mwflags", 0);
+ DWORD dwGlobal = M->GetDword("mwflags", 0);
+ DWORD maskval;
+
+ if(dat) {
+ dat->dwFlags &= ~MWF_LOG_ALL;
+ if(dat->pContainer->theme.isPrivate)
+ dat->dwFlags |= (dat->pContainer->theme.dwFlags & MWF_LOG_ALL);
+ else
+ dat->dwFlags |= (dwGlobal & MWF_LOG_ALL);
+ while(checkboxes[i].uId) {
+ maskval = checkboxes[i].uFlag;
+ if(dwMask & maskval)
+ dat->dwFlags = (dwLocal & maskval) ? dat->dwFlags | maskval : dat->dwFlags & ~maskval;
+ i++;
+ }
+ return(dat->dwFlags & MWF_LOG_ALL);
+ }
+ return 0;
+}
+
+/**
+ * dialog procedure for the user preferences dialog (2nd page,
+ * "per contact" message log options)
+ *
+ * @params: Win32 window procedure conform
+ *
+ * @return LRESULT
+ */
+static INT_PTR CALLBACK DlgProcUserPrefsLogOptions(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch(msg) {
+ case WM_INITDIALOG: {
+
+ hContact = (HANDLE)lParam;
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hContact);
+ SendMessage(hwndDlg, WM_COMMAND, WM_USER + 200, 0);
+ return TRUE;
+ }
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case WM_USER + 200: {
+ DWORD dwLocalFlags, dwLocalMask, maskval;
+ int i = 0;
+
+ dwLocalFlags = M->GetDword(hContact, "mwflags", 0);
+ dwLocalMask = M->GetDword(hContact, "mwmask", 0);
+
+ while(checkboxes[i].uId) {
+ maskval = checkboxes[i].uFlag;
+
+ if(dwLocalMask & maskval)
+ CheckDlgButton(hwndDlg, checkboxes[i].uId, (dwLocalFlags & maskval) ? BST_CHECKED : BST_UNCHECKED);
+ else
+ CheckDlgButton(hwndDlg, checkboxes[i].uId, BST_INDETERMINATE);
+ i++;
+ }
+ break;
+ }
+ case WM_USER + 100: {
+ int i = 0;
+ LRESULT state;
+ HWND hwnd = M->FindWindow(hContact);
+ struct TWindowData *dat = NULL;
+ DWORD *dwActionToTake = (DWORD *)lParam, dwMask = 0, dwFlags = 0, maskval;
+
+ if(hwnd)
+ dat = (struct TWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ while(checkboxes[i].uId) {
+ maskval = checkboxes[i].uFlag;
+
+ state = IsDlgButtonChecked(hwndDlg, checkboxes[i].uId);
+ if(state != BST_INDETERMINATE) {
+ dwMask |= maskval;
+ dwFlags = (state == BST_CHECKED) ? (dwFlags | maskval) : (dwFlags & ~maskval);
+ }
+ i++;
+ }
+ if(dwMask) {
+ M->WriteDword(hContact, SRMSGMOD_T, "mwmask", dwMask);
+ M->WriteDword(hContact, SRMSGMOD_T, "mwflags", dwFlags);
+ }
+ else {
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "mwmask");
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "mwflags");
+ }
+ if(hwnd && dat) {
+ if(dwMask)
+ *dwActionToTake |= (DWORD)UPREF_ACTION_REMAKELOG;
+ if((dat->dwFlags & MWF_LOG_RTL) != (dwFlags & MWF_LOG_RTL))
+ *dwActionToTake |= (DWORD)UPREF_ACTION_APPLYOPTIONS;
+ }
+ break;
+ }
+ case IDC_REVERTGLOBAL:
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "mwmask");
+ DBDeleteContactSetting(hContact, SRMSGMOD_T, "mwflags");
+ SendMessage(hwndDlg, WM_COMMAND, WM_USER + 200, 0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**
+ * dialog procedure for the user preferences dialog. Handles the top
+ * level window (a tab control with 2 subpages)
+ *
+ * @params: like any Win32 window procedure
+ *
+ * @return LRESULT (ignored for dialog procs, use
+ * DWLP_MSGRESULT)
+ */
+INT_PTR CALLBACK DlgProcUserPrefsFrame(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(msg) {
+ case WM_INITDIALOG: {
+ TCITEM tci = {0};
+ RECT rcClient;
+ TCHAR szBuffer[180];
+
+ hContact = (HANDLE)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hContact);
+
+ WindowList_Add(PluginConfig.hUserPrefsWindowList, hwndDlg, hContact);
+ TranslateDialogDefault(hwndDlg);
+
+ GetClientRect(hwndDlg, &rcClient);
+
+ mir_sntprintf(szBuffer, safe_sizeof(szBuffer), CTranslator::getOpt(CTranslator::OPT_UPREFS_TITLE),
+ (TCHAR *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR));
+
+ SetWindowText(hwndDlg, szBuffer);
+
+ tci.cchTextMax = 100;
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_USERPREFS), hwndDlg, DlgProcUserPrefs, (LPARAM)hContact);
+ tci.pszText = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_UPREFS_GENERIC));
+ TabCtrl_InsertItem(GetDlgItem(hwndDlg, IDC_OPTIONSTAB), 0, &tci);
+ MoveWindow((HWND)tci.lParam, 6, DPISCALEY_S(32), rcClient.right - 12, rcClient.bottom - DPISCALEY_S(80), 1);
+ ShowWindow((HWND)tci.lParam, SW_SHOW);
+ if (CMimAPI::m_pfnEnableThemeDialogTexture)
+ CMimAPI::m_pfnEnableThemeDialogTexture((HWND)tci.lParam, ETDT_ENABLETAB);
+
+
+ tci.lParam = (LPARAM)CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_USERPREFS1), hwndDlg, DlgProcUserPrefsLogOptions, (LPARAM)hContact);
+ tci.pszText = const_cast<TCHAR *>(CTranslator::getOpt(CTranslator::OPT_UPREFS_MSGLOG));
+ TabCtrl_InsertItem(GetDlgItem(hwndDlg, IDC_OPTIONSTAB), 1, &tci);
+ MoveWindow((HWND)tci.lParam, 6, DPISCALEY_S(32), rcClient.right - 12, rcClient.bottom - DPISCALEY_S(80), 1);
+ ShowWindow((HWND)tci.lParam, SW_HIDE);
+ if (CMimAPI::m_pfnEnableThemeDialogTexture)
+ CMimAPI::m_pfnEnableThemeDialogTexture((HWND)tci.lParam, ETDT_ENABLETAB);
+ TabCtrl_SetCurSel(GetDlgItem(hwndDlg, IDC_OPTIONSTAB), 0);
+ ShowWindow(hwndDlg, SW_SHOW);
+ return TRUE;
+ }
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_OPTIONSTAB:
+ switch (((LPNMHDR)lParam)->code) {
+ case TCN_SELCHANGING: {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM;
+
+ TabCtrl_GetItem(GetDlgItem(hwndDlg, IDC_OPTIONSTAB), TabCtrl_GetCurSel(GetDlgItem(hwndDlg, IDC_OPTIONSTAB)), &tci);
+ ShowWindow((HWND)tci.lParam, SW_HIDE);
+ }
+ break;
+ case TCN_SELCHANGE: {
+ TCITEM tci;
+ tci.mask = TCIF_PARAM;
+
+ TabCtrl_GetItem(GetDlgItem(hwndDlg, IDC_OPTIONSTAB), TabCtrl_GetCurSel(GetDlgItem(hwndDlg, IDC_OPTIONSTAB)), &tci);
+ ShowWindow((HWND)tci.lParam, SW_SHOW);
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND: {
+ switch(LOWORD(wParam)) {
+ case IDOK: {
+ TCITEM tci;
+ int i, count;
+ DWORD dwActionToTake = 0; // child pages request which action to take
+ HWND hwnd = M->FindWindow(hContact);
+
+ tci.mask = TCIF_PARAM;
+
+ count = TabCtrl_GetItemCount(GetDlgItem(hwndDlg, IDC_OPTIONSTAB));
+ for (i = 0;i < count;i++) {
+ TabCtrl_GetItem(GetDlgItem(hwndDlg, IDC_OPTIONSTAB), i, &tci);
+ SendMessage((HWND)tci.lParam, WM_COMMAND, WM_USER + 100, (LPARAM)&dwActionToTake);
+ }
+ if(hwnd) {
+ struct TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if(dat) {
+ DWORD dwOldFlags = (dat->dwFlags & MWF_LOG_ALL);
+
+ SetDialogToType(hwnd);
+#if defined(__FEAT_DEPRECATED_DYNAMICSWITCHLOGVIEWER)
+ if(dwActionToTake & UPREF_ACTION_SWITCHLOGVIEWER) {
+ unsigned int mode = GetIEViewMode(hwndDlg, dat->hContact);
+ SwitchMessageLog(dat, mode);
+ }
+#endif
+ LoadLocalFlags(hwnd, dat);
+ if((dat->dwFlags & MWF_LOG_ALL) != dwOldFlags) {
+ BOOL fShouldHide = TRUE;
+
+ if(IsIconic(dat->pContainer->hwnd))
+ fShouldHide = FALSE;
+ else
+ ShowWindow(dat->pContainer->hwnd, SW_HIDE);
+ SendMessage(hwnd, DM_OPTIONSAPPLIED, 0, 0);
+ SendMessage(hwnd, DM_DEFERREDREMAKELOG, (WPARAM)hwnd, 0);
+ if(fShouldHide)
+ ShowWindow(dat->pContainer->hwnd, SW_SHOWNORMAL);
+ }
+ }
+ }
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+ }
+ case WM_DESTROY:
+ WindowList_Remove(PluginConfig.hUserPrefsWindowList, hwndDlg);
+ break;
+ }
+ return FALSE;
+}
diff --git a/plugins/TabSRMM/src/utils.cpp b/plugins/TabSRMM/src/utils.cpp new file mode 100644 index 0000000000..f4bd5ab118 --- /dev/null +++ b/plugins/TabSRMM/src/utils.cpp @@ -0,0 +1,1513 @@ +/*
+ * astyle --force-indent=tab=4 --brackets=linux --indent-switches
+ * --pad=oper --one-line=keep-blocks --unpad=paren
+ *
+ * Miranda IM: the free IM client for Microsoft* Windows*
+ *
+ * Copyright 2000-2009 Miranda ICQ/IM project,
+ * all portions of this codebase are copyrighted to the people
+ * listed in contributors.txt.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * you should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * part of tabSRMM messaging plugin for Miranda.
+ *
+ * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+ *
+ * $Id: utils.cpp 13428 2011-03-10 13:15:44Z borkra $
+ *
+ * generic utility functions
+ *
+ */
+
+#include "commonheaders.h"
+#include <string>
+
+#define MWF_LOG_BBCODE 1
+#define MWF_LOG_TEXTFORMAT 0x2000000
+#define MSGDLGFONTCOUNT 22
+
+int Utils::rtf_ctable_size = 0;
+TRTFColorTable* Utils::rtf_ctable = 0;
+
+HANDLE CWarning::hWindowList = 0;
+
+static TCHAR *w_bbcodes_begin[] = { _T("[b]"), _T("[i]"), _T("[u]"), _T("[s]"), _T("[color=") };
+static TCHAR *w_bbcodes_end[] = { _T("[/b]"), _T("[/i]"), _T("[/u]"), _T("[/s]"), _T("[/color]") };
+
+static TCHAR *formatting_strings_begin[] = { _T("b1 "), _T("i1 "), _T("u1 "), _T("s1 "), _T("c1 ") };
+static TCHAR *formatting_strings_end[] = { _T("b0 "), _T("i0 "), _T("u0 "), _T("s0 "), _T("c0 ") };
+
+#define NR_CODES 5
+
+LRESULT TSAPI _dlgReturn(HWND hWnd, LRESULT result)
+{
+ SetWindowLongPtr(hWnd, DWLP_MSGRESULT, result);
+ return(result);
+}
+
+void* Utils::safeAlloc(const size_t size)
+{
+ __try {
+ unsigned char* _p = reinterpret_cast<unsigned char*>(malloc(size));
+ *_p = 0;
+
+ return(reinterpret_cast<void*>(_p));
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"MEMORY_ALLOCATION", false)) {
+ return(0);
+ }
+}
+
+void* Utils::safeCalloc(const size_t size)
+{
+ __try {
+ void* _p = safeAlloc(size);
+ ::ZeroMemory(_p, size);
+ return(_p);
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"MEMORY_ALLOCATION", false)) {
+ return(0);
+ }
+}
+
+void* Utils::safeMirAlloc(const size_t size)
+{
+ __try {
+ unsigned char* _p = reinterpret_cast<unsigned char*>(mir_alloc(size));
+ *_p = 0;
+
+ return(reinterpret_cast<void*>(_p));
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"MIR_MEMORY_ALLOCATION", false)) {
+ return(0);
+ }
+}
+
+void* Utils::safeMirCalloc(const size_t size)
+{
+ __try {
+ void* _p = safeMirAlloc(size);
+ ::ZeroMemory(_p, size);
+ return(_p);
+ }
+ __except(CGlobals::Ex_ShowDialog(GetExceptionInformation(), __FILE__, __LINE__, L"MIR_MEMORY_ALLOCATION", false)) {
+ return(0);
+ }
+}
+
+TCHAR* Utils::FilterEventMarkers(TCHAR *wszText)
+{
+ tstring text(wszText);
+ INT_PTR beginmark = 0, endmark = 0;
+
+ while (TRUE) {
+ if ((beginmark = text.find(_T("~-+"))) != text.npos) {
+ endmark = text.find(_T("+-~"), beginmark);
+ if (endmark != text.npos && (endmark - beginmark) > 5) {
+ text.erase(beginmark, (endmark - beginmark) + 3);
+ continue;
+ } else
+ break;
+ } else
+ break;
+ }
+ //mad
+
+ while (TRUE) {
+ if ((beginmark = text.find( _T("\xAA"))) != text.npos) {
+ endmark = beginmark+2;
+ if (endmark != text.npos && (endmark - beginmark) > 1) {
+ text.erase(beginmark, endmark - beginmark);
+ continue;
+ } else
+ break;
+ } else
+ break;
+ }
+ //
+
+ lstrcpy(wszText, text.c_str());
+ return wszText;
+}
+
+/**
+ * this translates formatting tags into rtf sequences...
+ * flags: loword = words only for simple * /_ formatting
+ * hiword = bbcode support (strip bbcodes if 0)
+ */
+const TCHAR* Utils::FormatRaw(TWindowData *dat, const TCHAR *msg, int flags, BOOL isSent)
+{
+ bool clr_was_added = false, was_added;
+ static tstring message(msg);
+ INT_PTR beginmark = 0, endmark = 0, tempmark = 0, index;
+ int i, endindex;
+ TCHAR endmarker;
+ DWORD dwFlags = dat->dwFlags;
+ message.assign(msg);
+ int haveMathMod = PluginConfig.m_MathModAvail;
+ TCHAR* mathModDelimiter = PluginConfig.m_MathModStartDelimiter;
+
+
+ if (haveMathMod && mathModDelimiter[0] && message.find(mathModDelimiter) != message.npos)
+ return(message.c_str());
+
+ if(dwFlags & MWF_LOG_BBCODE) {
+ if (haveMathMod && mathModDelimiter[0]) {
+ INT_PTR mark = 0;
+ int nrDelims = 0;
+ while ((mark = message.find(mathModDelimiter, mark)) != message.npos) {
+ nrDelims++;
+ mark += lstrlen(mathModDelimiter);
+ }
+ if (nrDelims > 0 && (nrDelims % 2) != 0)
+ message.append(mathModDelimiter);
+ }
+ beginmark = 0;
+ while (TRUE) {
+ for (i = 0; i < NR_CODES; i++) {
+ if ((tempmark = message.find(w_bbcodes_begin[i], 0)) != message.npos)
+ break;
+ }
+ if (i >= NR_CODES)
+ break;
+ beginmark = tempmark;
+ endindex = i;
+ endmark = message.find(w_bbcodes_end[i], beginmark);
+ if (endindex == 4) { // color
+ size_t closing = message.find_first_of(_T("]"), beginmark);
+ was_added = false;
+
+ if (closing == message.npos) { // must be an invalid [color=] tag w/o closing bracket
+ message[beginmark] = ' ';
+ continue;
+ } else {
+ tstring colorname = message.substr(beginmark + 7, 8);
+search_again:
+ bool clr_found = false;
+ int ii = 0;
+ TCHAR szTemp[5];
+ for (ii = 0; ii < rtf_ctable_size; ii++) {
+ if (!_tcsnicmp((TCHAR *)colorname.c_str(), rtf_ctable[ii].szName, lstrlen(rtf_ctable[ii].szName))) {
+ closing = beginmark + 7 + lstrlen(rtf_ctable[ii].szName);
+ if (endmark != message.npos) {
+ message.erase(endmark, 4);
+ message.replace(endmark, 4, _T("c0 "));
+ }
+ message.erase(beginmark, (closing - beginmark));
+ message.insert(beginmark, _T("cxxx "));
+ _sntprintf(szTemp, 4, _T("%02d"), MSGDLGFONTCOUNT + 13 + ii);
+ message[beginmark + 3] = szTemp[0];
+ message[beginmark + 4] = szTemp[1];
+ clr_found = true;
+ if (was_added) {
+ TCHAR wszTemp[100];
+ _sntprintf(wszTemp, 100, _T("##col##%06u:%04u"), endmark - closing, ii);
+ wszTemp[99] = 0;
+ message.insert(beginmark, wszTemp);
+ }
+ break;
+ }
+ }
+ if (!clr_found) {
+ size_t c_closing = colorname.find_first_of(_T("]"), 0);
+ if (c_closing == colorname.npos)
+ c_closing = colorname.length();
+ const TCHAR *wszColname = colorname.c_str();
+ if (endmark != message.npos && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing -1])) {
+ RTF_ColorAdd(wszColname, c_closing);
+ if (!was_added) {
+ clr_was_added = was_added = true;
+ goto search_again;
+ } else
+ goto invalid_code;
+ } else {
+invalid_code:
+ if (endmark != message.npos)
+ message.erase(endmark, 8);
+ if (closing != message.npos && closing < (size_t)endmark)
+ message.erase(beginmark, (closing - beginmark) + 1);
+ else
+ message[beginmark] = ' ';
+ }
+ }
+ continue;
+ }
+ }
+ if (endmark != message.npos)
+ message.replace(endmark, 4, formatting_strings_end[i]);
+ message.insert(beginmark, _T(" "));
+ message.replace(beginmark, 4, formatting_strings_begin[i]);
+ }
+ }
+
+ if (!(dwFlags & MWF_LOG_TEXTFORMAT) || message.find(_T("://")) != message.npos) {
+ dat->clr_added = clr_was_added ? TRUE : FALSE;
+ return(message.c_str());
+ }
+
+
+ while ((beginmark = message.find_first_of(_T("*/_"), beginmark)) != message.npos) {
+ endmarker = message[beginmark];
+ if (LOWORD(flags)) {
+ if (beginmark > 0 && !_istspace(message[beginmark - 1]) && !_istpunct(message[beginmark - 1])) {
+ beginmark++;
+ continue;
+ }
+ // search a corresponding endmarker which fulfills the criteria
+ INT_PTR tempmark = beginmark + 1;
+ while ((endmark = message.find(endmarker, tempmark)) != message.npos) {
+ if (_istpunct(message[endmark + 1]) || _istspace(message[endmark + 1]) || message[endmark + 1] == 0 || _tcschr(_T("*/_"), message[endmark + 1]) != NULL)
+ goto ok;
+ tempmark = endmark + 1;
+ }
+ break;
+ } else {
+ if ((endmark = message.find(endmarker, beginmark + 1)) == message.npos)
+ break;
+ }
+ok:
+ if ((endmark - beginmark) < 2) {
+ beginmark++;
+ continue;
+ }
+ index = 0;
+ switch (endmarker) {
+ case '*':
+ index = 0;
+ break;
+ case '/':
+ index = 1;
+ break;
+ case '_':
+ index = 2;
+ break;
+ }
+
+ /*
+ * check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if
+ * it really is one.
+ */
+
+ if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) {
+ tstring smcode;
+ smcode.assign(message, beginmark, (endmark - beginmark) + 1);
+ SMADD_BATCHPARSE2 smbp = {0};
+ SMADD_BATCHPARSERES *smbpr;
+
+ smbp.cbSize = sizeof(smbp);
+ smbp.Protocolname = dat->cache->getActiveProto();
+ smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0);
+ smbp.str = (TCHAR *)smcode.c_str();
+ smbp.hContact = dat->hContact;
+ smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM) & smbp);
+ if (smbpr) {
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr);
+ beginmark = endmark + 1;
+ continue;
+ }
+ }
+ message.insert(endmark, _T("%%%"));
+ message.replace(endmark, 4, formatting_strings_end[index]);
+ message.insert(beginmark, _T("%%%"));
+ message.replace(beginmark, 4, formatting_strings_begin[index]);
+ }
+ dat->clr_added = clr_was_added ? TRUE : FALSE;
+ return(message.c_str());
+}
+
+/**
+ * format the title bar string for IM chat sessions using placeholders.
+ * the caller must free() the returned string
+ */
+const TCHAR* Utils::FormatTitleBar(const TWindowData *dat, const TCHAR *szFormat)
+{
+ TCHAR *szResult = 0;
+ INT_PTR length = 0;
+ INT_PTR tempmark = 0;
+ TCHAR szTemp[512];
+
+ if(dat == 0)
+ return(0);
+
+ tstring title(szFormat);
+
+ for ( size_t curpos = 0; curpos < title.length(); ) {
+ if(title[curpos] != '%') {
+ curpos++;
+ continue;
+ }
+ tempmark = curpos;
+ curpos++;
+ if(title[curpos] == 0)
+ break;
+
+ switch (title[curpos]) {
+ case 'n': {
+ const TCHAR *tszNick = dat->cache->getNick();
+ if (tszNick[0])
+ title.insert(tempmark + 2, tszNick);
+ title.erase(tempmark, 2);
+ curpos = tempmark + lstrlen(tszNick);
+ break;
+ }
+ case 'p':
+ case 'a': {
+ const TCHAR *szAcc = dat->cache->getRealAccount();
+ if (szAcc)
+ title.insert(tempmark + 2, szAcc);
+ title.erase(tempmark, 2);
+ curpos = tempmark + lstrlen(szAcc);
+ break;
+ }
+ case 's': {
+ if (dat->szStatus && dat->szStatus[0])
+ title.insert(tempmark + 2, dat->szStatus);
+ title.erase(tempmark, 2);
+ curpos = tempmark + lstrlen(dat->szStatus);
+ break;
+ }
+ case 'u': {
+ const TCHAR *szUIN = dat->cache->getUIN();
+ if (szUIN[0])
+ title.insert(tempmark + 2, szUIN);
+ title.erase(tempmark, 2);
+ curpos = tempmark + lstrlen(szUIN);
+ break;
+ }
+ case 'c': {
+ TCHAR *c = (!_tcscmp(dat->pContainer->szName, _T("default")) ? const_cast<TCHAR *>(CTranslator::get(CTranslator::GEN_DEFAULT_CONTAINER_NAME)) : dat->pContainer->szName);
+ title.insert(tempmark + 2, c);
+ title.erase(tempmark, 2);
+ curpos = tempmark + lstrlen(c);
+ break;
+ }
+ case 'o': {
+ const TCHAR* szProto = dat->cache->getActiveProtoT();
+ if (szProto)
+ title.insert(tempmark + 2, szProto);
+ title.erase(tempmark, 2);
+ curpos = tempmark + (szProto ? lstrlen(szProto) : 0);
+ break;
+ }
+ case 'x': {
+ TCHAR *szFinalStatus = NULL;
+ BYTE xStatus = dat->cache->getXStatusId();
+
+ if (dat->wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+ DBVARIANT dbv = {0};
+
+ if (!M->GetTString(dat->hContact, (char *)dat->szProto, "XStatusName", &dbv)) {
+ _tcsncpy(szTemp, dbv.ptszVal, 500);
+ szTemp[500] = 0;
+ DBFreeVariant(&dbv);
+ title.insert(tempmark + 2, szTemp);
+ curpos = tempmark + lstrlen(szTemp);
+ }
+ else {
+ title.insert(tempmark + 2, xStatusDescr[xStatus - 1]);
+ curpos = tempmark + lstrlen(xStatusDescr[xStatus - 1]);
+ }
+ }
+ title.erase(tempmark, 2);
+ break;
+ }
+ case 'm': {
+ TCHAR *szFinalStatus = NULL;
+ BYTE xStatus = dat->cache->getXStatusId();
+
+ if (dat->wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+ DBVARIANT dbv = {0};
+
+ if (!M->GetTString(dat->hContact, (char *)dat->szProto, "XStatusName", &dbv)) {
+ _tcsncpy(szTemp, dbv.ptszVal, 500);
+ szTemp[500] = 0;
+ DBFreeVariant(&dbv);
+ title.insert(tempmark + 2, szTemp);
+ } else
+ szFinalStatus = xStatusDescr[xStatus - 1];
+ } else
+ szFinalStatus = (TCHAR *)(dat->szStatus && dat->szStatus[0] ? dat->szStatus : _T("(undef)"));
+
+ if (szFinalStatus) {
+ title.insert(tempmark + 2, szFinalStatus);
+ curpos = tempmark + lstrlen(szFinalStatus);
+ }
+
+ title.erase(tempmark, 2);
+ break;
+ }
+ /*
+ * status message (%T will skip the "No status message" for empty
+ * messages)
+ */
+ case 't':
+ case 'T': {
+ TCHAR *tszStatusMsg = dat->cache->getNormalizedStatusMsg(dat->cache->getStatusMsg(), true);
+
+ if(tszStatusMsg) {
+ title.insert(tempmark + 2, tszStatusMsg);
+ curpos = tempmark + lstrlen(tszStatusMsg);
+ }
+ else if(title[curpos] == 't') {
+ const TCHAR* tszStatusMsg = CTranslator::get(CTranslator::GEN_NO_STATUS);
+ title.insert(tempmark + 2, tszStatusMsg);
+ curpos = tempmark + lstrlen(tszStatusMsg);
+ }
+ title.erase(tempmark, 2);
+ if(tszStatusMsg)
+ mir_free(tszStatusMsg);
+ break;
+ }
+ default:
+ title.erase(tempmark, 1);
+ break;
+ }
+ }
+ length = title.length();
+
+ szResult = (TCHAR *)malloc((length + 2) * sizeof(TCHAR));
+ if (szResult) {
+ _tcsncpy(szResult, title.c_str(), length);
+ szResult[length] = 0;
+ }
+ return szResult;
+}
+
+char* Utils::FilterEventMarkers(char *szText)
+{
+ std::string text(szText);
+ INT_PTR beginmark = 0, endmark = 0;
+
+ while (TRUE) {
+ if ((beginmark = text.find("~-+")) != text.npos) {
+ endmark = text.find("+-~", beginmark);
+ if (endmark != text.npos && (endmark - beginmark) > 5) {
+ text.erase(beginmark, (endmark - beginmark) + 3);
+ continue;
+ } else
+ break;
+ } else
+ break;
+ }
+ //mad
+ while (TRUE) {
+ if ((beginmark = text.find( "\xAA")) != text.npos) {
+ endmark = beginmark+2;
+ if (endmark != text.npos && (endmark - beginmark) > 1) {
+ text.erase(beginmark, endmark - beginmark);
+ continue;
+ } else
+ break;
+ } else
+ break;
+ }
+ //
+ lstrcpyA(szText, text.c_str());
+ return szText;
+}
+
+const TCHAR* Utils::DoubleAmpersands(TCHAR *pszText)
+{
+ tstring text(pszText);
+
+ INT_PTR textPos = 0;
+
+ while (TRUE) {
+ if ((textPos = text.find(_T("&"),textPos)) != text.npos) {
+ text.insert(textPos,_T("%"));
+ text.replace(textPos, 2, _T("&&"));
+ textPos+=2;
+ continue;
+ } else
+ break;
+ }
+ _tcscpy(pszText, text.c_str());
+ return pszText;
+}
+
+/**
+ * Get a preview of the text with an ellipsis appended (...)
+ *
+ * @param szText source text
+ * @param iMaxLen max length of the preview
+ * @return TCHAR* result (caller must mir_free() it)
+ */
+TCHAR* Utils::GetPreviewWithEllipsis(TCHAR *szText, size_t iMaxLen)
+{
+ size_t uRequired;
+ TCHAR* p = 0, cSaved;
+ bool fEllipsis = false;
+
+ if(_tcslen(szText) <= iMaxLen)
+ uRequired = _tcslen(szText) + 4;
+ else {
+ TCHAR *p = &szText[iMaxLen - 1];
+ fEllipsis = true;
+
+ while(p >= szText && *p != ' ')
+ p--;
+ if(p == szText)
+ p = szText + iMaxLen - 1;
+
+ cSaved = *p;
+ *p = 0;
+ uRequired = (p - szText) + 6;
+ }
+ TCHAR *szResult = reinterpret_cast<TCHAR *>(mir_alloc(uRequired * sizeof(TCHAR)));
+ mir_sntprintf(szResult, uRequired, fEllipsis ? _T("%s...") : _T("%s"), szText);
+
+ if(p)
+ *p = cSaved;
+
+ return(szResult);
+}
+
+/*
+ * returns != 0 when one of the installed keyboard layouts belongs to an rtl language
+ * used to find out whether we need to configure the message input box for bidirectional mode
+ */
+
+int Utils::FindRTLLocale(TWindowData *dat)
+{
+ HKL layouts[20];
+ int i, result = 0;
+ LCID lcid;
+ WORD wCtype2[5];
+
+ if (dat->iHaveRTLLang == 0) {
+ ZeroMemory(layouts, 20 * sizeof(HKL));
+ GetKeyboardLayoutList(20, layouts);
+ for (i = 0; i < 20 && layouts[i]; i++) {
+ lcid = MAKELCID(LOWORD(layouts[i]), 0);
+ GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2);
+ if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT)
+ result = 1;
+ }
+ dat->iHaveRTLLang = (result ? 1 : -1);
+ } else
+ result = dat->iHaveRTLLang == 1 ? 1 : 0;
+
+ return result;
+}
+
+/*
+ * init default color table. the table may grow when using custom colors via bbcodes
+ */
+
+void Utils::RTF_CTableInit()
+{
+ int iSize = sizeof(TRTFColorTable) * RTF_CTABLE_DEFSIZE;
+
+ rtf_ctable = (TRTFColorTable *)malloc(iSize);
+ ZeroMemory(rtf_ctable, iSize);
+ CopyMemory(rtf_ctable, _rtf_ctable, iSize);
+ rtf_ctable_size = RTF_CTABLE_DEFSIZE;
+}
+
+/*
+ * add a color to the global rtf color table
+ */
+
+void Utils::RTF_ColorAdd(const TCHAR *tszColname, size_t length)
+{
+ TCHAR *stopped;
+ COLORREF clr;
+
+ rtf_ctable_size++;
+ rtf_ctable = (TRTFColorTable *)realloc(rtf_ctable, sizeof(TRTFColorTable) * rtf_ctable_size);
+ clr = _tcstol(tszColname, &stopped, 16);
+ mir_sntprintf(rtf_ctable[rtf_ctable_size - 1].szName, length + 1, _T("%06x"), clr);
+ rtf_ctable[rtf_ctable_size - 1].menuid = rtf_ctable[rtf_ctable_size - 1].index = 0;
+
+ clr = _tcstol(tszColname, &stopped, 16);
+ rtf_ctable[rtf_ctable_size - 1].clr = (RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr)));
+}
+
+void Utils::CreateColorMap(TCHAR *Text)
+{
+ TCHAR * pszText = Text;
+ TCHAR * p1;
+ TCHAR * p2;
+ TCHAR * pEnd;
+ int iIndex = 1, i = 0;
+ COLORREF default_color;
+
+ static const TCHAR *lpszFmt = _T("\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];");
+ TCHAR szRed[10], szGreen[10], szBlue[10];
+
+ p1 = _tcsstr(pszText, _T("\\colortbl"));
+ if (!p1)
+ return;
+
+ pEnd = _tcschr(p1, '}');
+
+ p2 = _tcsstr(p1, _T("\\red"));
+
+ for (i = 0; i < RTF_CTABLE_DEFSIZE; i++)
+ rtf_ctable[i].index = 0;
+
+ default_color = (COLORREF)M->GetDword(FONTMODULE, "Font16Col", 0);
+
+ while (p2 && p2 < pEnd) {
+ if (_stscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) {
+ int i;
+ for (i = 0; i < RTF_CTABLE_DEFSIZE; i++) {
+ if (rtf_ctable[i].clr == RGB(_ttoi(szRed), _ttoi(szGreen), _ttoi(szBlue)))
+ rtf_ctable[i].index = iIndex;
+ }
+ }
+ iIndex++;
+ p1 = p2;
+ p1 ++;
+
+ p2 = _tcsstr(p1, _T("\\red"));
+ }
+ return ;
+}
+
+int Utils::RTFColorToIndex(int iCol)
+{
+ int i = 0;
+ for (i = 0; i < RTF_CTABLE_DEFSIZE; i++) {
+ if (rtf_ctable[i].index == iCol)
+ return i + 1;
+ }
+ return 0;
+}
+
+/**
+ * generic error popup dialog procedure
+ */
+INT_PTR CALLBACK Utils::PopupDlgProcError(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hWnd, (LPARAM)&hContact);
+
+ switch (message) {
+ case WM_COMMAND:
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_HANDLECLISTEVENT, (WPARAM)hContact, 0);
+ PUDeletePopUp(hWnd);
+ break;
+ case WM_CONTEXTMENU:
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_HANDLECLISTEVENT, (WPARAM)hContact, 0);
+ PUDeletePopUp(hWnd);
+ break;
+ case WM_MOUSEWHEEL:
+ break;
+ case WM_SETCURSOR:
+ break;
+ default:
+ break;
+ }
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+/**
+ * read a blob from db into the container settings structure
+ * @param hContact: contact handle (0 = read global)
+ * @param cs TContainerSettings* target structure
+ * @return 0 on success, 1 failure (blob does not exist OR is not a valid private setting structure
+ */
+int Utils::ReadContainerSettingsFromDB(const HANDLE hContact, TContainerSettings *cs, const char *szKey)
+{
+ DBVARIANT dbv = {0};
+
+ CopyMemory(cs, &PluginConfig.globalContainerSettings, sizeof(TContainerSettings));
+
+ if(0 == DBGetContactSetting(hContact, SRMSGMOD_T, szKey ? szKey : CNT_KEYNAME, &dbv)) {
+ if(dbv.type == DBVT_BLOB && dbv.cpbVal > 0 && dbv.cpbVal <= sizeof(TContainerSettings)) {
+ ::CopyMemory((void *)cs, (void *)dbv.pbVal, dbv.cpbVal);
+ ::DBFreeVariant(&dbv);
+ if(hContact == 0 && szKey == 0)
+ cs->fPrivate = false;
+ return(0);
+ }
+ cs->fPrivate = false;
+ DBFreeVariant(&dbv);
+ return(1);
+ }
+ else {
+ cs->fPrivate = false;
+ return(1);
+ }
+}
+
+int Utils::WriteContainerSettingsToDB(const HANDLE hContact, TContainerSettings *cs, const char *szKey)
+{
+ DBWriteContactSettingBlob(hContact, SRMSGMOD_T, szKey ? szKey : CNT_KEYNAME, cs, sizeof(TContainerSettings));
+ return(0);
+}
+
+void Utils::SettingsToContainer(TContainerData *pContainer)
+{
+ pContainer->dwFlags = pContainer->settings->dwFlags;
+ pContainer->dwFlagsEx = pContainer->settings->dwFlagsEx;
+ pContainer->avatarMode = pContainer->settings->avatarMode;
+ pContainer->ownAvatarMode = pContainer->settings->ownAvatarMode;
+}
+
+void Utils::ContainerToSettings(TContainerData *pContainer)
+{
+ pContainer->settings->dwFlags = pContainer->dwFlags;
+ pContainer->settings->dwFlagsEx = pContainer->dwFlagsEx;
+ pContainer->settings->avatarMode = pContainer->avatarMode;
+ pContainer->settings->ownAvatarMode = pContainer->ownAvatarMode;
+}
+
+/**
+ * read settings for a container with private settings enabled.
+ *
+ * @param pContainer container window info struct
+ * @param fForce true -> force them private, even if they were not marked as private in the db
+ */
+void Utils::ReadPrivateContainerSettings(TContainerData *pContainer, bool fForce)
+{
+ char szCname[50];
+ TContainerSettings csTemp = {0};
+
+ mir_snprintf(szCname, 40, "%s%d_Blob", CNT_BASEKEYNAME, pContainer->iContainerIndex);
+ Utils::ReadContainerSettingsFromDB(0, &csTemp, szCname);
+ if(csTemp.fPrivate || fForce) {
+ if(pContainer->settings == 0 || pContainer->settings == &PluginConfig.globalContainerSettings)
+ pContainer->settings = (TContainerSettings *)malloc(sizeof(TContainerSettings));
+ CopyMemory((void *)pContainer->settings, (void *)&csTemp, sizeof(TContainerSettings));
+ pContainer->settings->fPrivate = true;
+ }
+ else
+ pContainer->settings = &PluginConfig.globalContainerSettings;
+}
+
+void Utils::SaveContainerSettings(TContainerData *pContainer, const char *szSetting)
+{
+ char szCName[50];
+
+ pContainer->dwFlags &= ~(CNT_DEFERREDCONFIGURE | CNT_CREATE_MINIMIZED | CNT_DEFERREDSIZEREQUEST | CNT_CREATE_CLONED);
+ if(pContainer->settings->fPrivate) {
+ _snprintf(szCName, 40, "%s%d_Blob", szSetting, pContainer->iContainerIndex);
+ WriteContainerSettingsToDB(0, pContainer->settings, szCName);
+ }
+ mir_snprintf(szCName, 40, "%s%d_theme", szSetting, pContainer->iContainerIndex);
+ if (lstrlen(pContainer->szRelThemeFile) > 1) {
+ if(pContainer->fPrivateThemeChanged == TRUE) {
+ M->pathToRelative(pContainer->szRelThemeFile, pContainer->szAbsThemeFile);
+ M->WriteTString(NULL, SRMSGMOD_T, szCName, pContainer->szAbsThemeFile);
+ pContainer->fPrivateThemeChanged = FALSE;
+ }
+ }
+ else {
+ ::DBDeleteContactSetting(NULL, SRMSGMOD_T, szCName);
+ pContainer->fPrivateThemeChanged = FALSE;
+ }
+}
+
+/**
+ * calculate new width and height values for a user picture (avatar)
+ *
+ * @param: maxHeight - determines maximum height for the picture, width will
+ * be scaled accordingly.
+ */
+void Utils::scaleAvatarHeightLimited(const HBITMAP hBm, double& dNewWidth, double& dNewHeight, LONG maxHeight)
+{
+ BITMAP bm;
+ double dAspect;
+
+ GetObject(hBm, sizeof(bm), &bm);
+
+ if (bm.bmHeight > bm.bmWidth) {
+ if (bm.bmHeight > 0)
+ dAspect = (double)(maxHeight) / (double)bm.bmHeight;
+ else
+ dAspect = 1.0;
+ dNewWidth = (double)bm.bmWidth * dAspect;
+ dNewHeight = (double)maxHeight;
+ } else {
+ if (bm.bmWidth > 0)
+ dAspect = (double)(maxHeight) / (double)bm.bmWidth;
+ else
+ dAspect = 1.0;
+ dNewHeight = (double)bm.bmHeight * dAspect;
+ dNewWidth = (double)maxHeight;
+ }
+}
+
+/**
+ * convert the avatar bitmap to icon format so that it can be used on the task bar
+ * tries to keep correct aspect ratio of the avatar image
+ *
+ * @param dat: _MessageWindowData* pointer to the window data
+ * @return HICON: the icon handle
+ */
+HICON Utils::iconFromAvatar(const TWindowData *dat)
+{
+ double dNewWidth, dNewHeight;
+ bool fFree = false;
+ HIMAGELIST hIml_c = 0;
+ HICON hIcon = 0;
+
+ if(!ServiceExists(MS_AV_GETAVATARBITMAP))
+ return(0);
+
+ if(dat) {
+ AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)dat->hContact, 0);
+ LONG lIconSize = Win7Taskbar->getIconSize();
+
+ if(ace && ace->hbmPic) {
+ scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize);
+ /*
+ * resize picture to fit it on the task bar, use an image list for converting it to
+ * 32bpp icon format
+ * dat->hTaskbarIcon will cache it until avatar is changed
+ */
+ HBITMAP hbmResized = CSkin::ResizeBitmap(ace->hbmPic, (LONG)dNewWidth, (LONG)dNewHeight, fFree);
+ hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0);
+
+ RECT rc = {0, 0, lIconSize, lIconSize};
+
+ HDC hdc = ::GetDC(dat->pContainer->hwnd);
+ HDC dc = ::CreateCompatibleDC(hdc);
+ HDC dcResized = ::CreateCompatibleDC(hdc);
+
+ ReleaseDC(dat->pContainer->hwnd, hdc);
+
+ HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew));
+ HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized));
+
+ LONG ix = (lIconSize - (LONG)dNewWidth) / 2;
+ LONG iy = (lIconSize - (LONG)dNewHeight) / 2;
+ CSkin::m_default_bf.SourceConstantAlpha = M->GetByte("taskBarIconAlpha", 255);
+ CMimAPI::m_MyAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized,
+ 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
+
+ CSkin::m_default_bf.SourceConstantAlpha = 255;
+ ::SelectObject(dc, hbmOld);
+ ::ImageList_Add(hIml_c, hbmNew, 0);
+ ::DeleteObject(hbmNew);
+ ::DeleteDC(dc);
+
+ ::SelectObject(dcResized, hbmOldResized);
+ if(hbmResized != ace->hbmPic)
+ ::DeleteObject(hbmResized);
+ ::DeleteDC(dcResized);
+ hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL);
+ ::ImageList_RemoveAll(hIml_c);
+ ::ImageList_Destroy(hIml_c);
+ }
+ }
+ return(hIcon);
+}
+
+AVATARCACHEENTRY* Utils::loadAvatarFromAVS(const HANDLE hContact)
+{
+ if(ServiceExists(MS_AV_GETAVATARBITMAP))
+ return(reinterpret_cast<AVATARCACHEENTRY *>(CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0)));
+ else
+ return(0);
+}
+
+void Utils::getIconSize(HICON hIcon, int& sizeX, int& sizeY)
+{
+ ICONINFO ii;
+ BITMAP bm;
+ ::GetIconInfo(hIcon, &ii);
+ ::GetObject(ii.hbmColor, sizeof(bm), &bm);
+ sizeX = bm.bmWidth;
+ sizeY = bm.bmHeight;
+ ::DeleteObject(ii.hbmMask);
+ ::DeleteObject(ii.hbmColor);
+}
+
+/**
+ * add a menu item to a ownerdrawn menu. mii must be pre-initialized
+ *
+ * @param m menu handle
+ * @param mii menu item info structure
+ * @param hIcon the icon (0 allowed -> no icon)
+ * @param szText menu item text (must NOT be 0)
+ * @param uID the item command id
+ * @param pos zero-based position index
+ */
+void Utils::addMenuItem(const HMENU& m, MENUITEMINFO& mii, HICON hIcon, const TCHAR *szText, UINT uID, UINT pos)
+{
+ mii.wID = uID;
+ mii.dwItemData = (ULONG_PTR)hIcon;
+ mii.dwTypeData = const_cast<TCHAR *>(szText);
+ mii.cch = lstrlen(mii.dwTypeData) + 1;
+
+ ::InsertMenuItem(m, pos, TRUE, &mii);
+}
+
+/**
+ * return != 0 when the sound effect must be played for the given
+ * session. Uses container sound settings
+ */
+int TSAPI Utils::mustPlaySound(const TWindowData *dat)
+{
+ if(!dat)
+ return(0);
+
+ if(dat->pContainer->fHidden) // hidden container is treated as closed, so play the sound
+ return(1);
+
+ if(dat->pContainer->dwFlags & CNT_NOSOUND || nen_options.iNoSounds)
+ return(0);
+
+ bool fActiveWindow = (dat->pContainer->hwnd == ::GetForegroundWindow() ? true : false);
+ bool fActiveTab = (dat->pContainer->hwndActive == dat->hwnd ? true : false);
+ bool fIconic = (::IsIconic(dat->pContainer->hwnd) ? true : false);
+
+ /*
+ * window minimized, check if sound has to be played
+ */
+ if(fIconic)
+ return(dat->pContainer->dwFlagsEx & CNT_EX_SOUNDS_MINIMIZED ? 1 : 0);
+
+ /*
+ * window in foreground
+ */
+ if(fActiveWindow) {
+ if(fActiveTab)
+ return(dat->pContainer->dwFlagsEx & CNT_EX_SOUNDS_FOCUSED ? 1 : 0);
+ else
+ return(dat->pContainer->dwFlagsEx & CNT_EX_SOUNDS_INACTIVETABS ? 1 : 0);
+ }
+ else
+ return(dat->pContainer->dwFlagsEx & CNT_EX_SOUNDS_UNFOCUSED ? 1 : 0);
+
+ return(1);
+}
+
+/**
+ * enable or disable a dialog control
+ */
+void TSAPI Utils::enableDlgControl(const HWND hwnd, UINT id, BOOL fEnable)
+{
+ ::EnableWindow(::GetDlgItem(hwnd, id), fEnable);
+}
+
+/**
+ * show or hide a dialog control
+ */
+void TSAPI Utils::showDlgControl(const HWND hwnd, UINT id, int showCmd)
+{
+ ::ShowWindow(::GetDlgItem(hwnd, id), showCmd);
+}
+
+/*
+ * stream function to write the contents of the message log to an rtf file
+ */
+DWORD CALLBACK Utils::StreamOut(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
+{
+ HANDLE hFile;
+ TCHAR *szFilename = (TCHAR *)dwCookie;
+ if ((hFile = CreateFile(szFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) {
+ SetFilePointer(hFile, 0, NULL, FILE_END);
+ FilterEventMarkers(reinterpret_cast<TCHAR *>(pbBuff));
+ WriteFile(hFile, pbBuff, cb, (DWORD *)pcb, NULL);
+ *pcb = cb;
+ CloseHandle(hFile);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * extract a resource from the given module
+ * tszPath must end with \
+ */
+void TSAPI Utils::extractResource(const HMODULE h, const UINT uID, const TCHAR *tszName, const TCHAR *tszPath,
+ const TCHAR *tszFilename, bool fForceOverwrite)
+{
+ HRSRC hRes;
+ HGLOBAL hResource;
+ TCHAR szFilename[MAX_PATH];
+
+ hRes = FindResource(h, MAKEINTRESOURCE(uID), tszName);
+
+ if(hRes) {
+ hResource = LoadResource(h, hRes);
+ if(hResource) {
+ HANDLE hFile;
+ char *pData = (char *)LockResource(hResource);
+ DWORD dwSize = SizeofResource(g_hInst, hRes), written = 0;
+ mir_sntprintf(szFilename, MAX_PATH, _T("%s%s"), tszPath, tszFilename);
+ if(!fForceOverwrite) {
+ if(PathFileExists(szFilename))
+ return;
+ }
+ if((hFile = CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)) != INVALID_HANDLE_VALUE) {
+ WriteFile(hFile, (void *)pData, dwSize, &written, NULL);
+ CloseHandle(hFile);
+ }
+ else
+ throw(CRTException("Error while extracting aero skin images, Aero mode disabled.", szFilename));
+ }
+ }
+}
+
+/**
+ * extract the clicked URL from a rich edit control. Return the URL as TCHAR*
+ * caller MUST mir_free() the returned string
+ * @param hwndRich - rich edit window handle
+ * @return wchar_t* extracted URL
+ */
+const wchar_t* Utils::extractURLFromRichEdit(const ENLINK* _e, const HWND hwndRich)
+{
+ TEXTRANGEW tr = {0};
+ CHARRANGE sel = {0};
+
+ ::SendMessageW(hwndRich, EM_EXGETSEL, 0, (LPARAM) & sel);
+ if (sel.cpMin != sel.cpMax)
+ return(0);
+
+ tr.chrg = _e->chrg;
+ tr.lpstrText = (wchar_t *)mir_alloc(2 * (tr.chrg.cpMax - tr.chrg.cpMin + 8));
+ ::SendMessageW(hwndRich, EM_GETTEXTRANGE, 0, (LPARAM) & tr);
+ if (wcschr(tr.lpstrText, '@') != NULL && wcschr(tr.lpstrText, ':') == NULL && wcschr(tr.lpstrText, '/') == NULL) {
+ ::MoveMemory(tr.lpstrText + 7, tr.lpstrText, sizeof(wchar_t) * (tr.chrg.cpMax - tr.chrg.cpMin + 1));
+ ::CopyMemory(tr.lpstrText, L"mailto:", 7 * sizeof(wchar_t));
+ }
+ return(tr.lpstrText);
+}
+
+/**
+ * generic command dispatcher
+ * used in various places (context menus, info panel menus etc.)
+ */
+LRESULT Utils::CmdDispatcher(UINT uType, HWND hwndDlg, UINT cmd, WPARAM wParam, LPARAM lParam, TWindowData *dat, TContainerData *pContainer)
+{
+ switch(uType) {
+ case CMD_CONTAINER:
+ if(pContainer && hwndDlg)
+ return(DM_ContainerCmdHandler(pContainer, cmd, wParam, lParam));
+ break;
+ case CMD_MSGDIALOG:
+ if(pContainer && hwndDlg && dat)
+ return(DM_MsgWindowCmdHandler(hwndDlg, pContainer, dat, cmd, wParam, lParam));
+ break;
+ case CMD_INFOPANEL:
+ if(MsgWindowMenuHandler(dat, cmd, MENU_LOGMENU) == 0) {
+ return(DM_MsgWindowCmdHandler(hwndDlg, pContainer, dat, cmd, wParam, lParam));
+ }
+ break;
+ }
+ return(0);
+}
+
+/**
+ * filter out invalid characters from a string used as part of a file
+ * or folder name. All invalid characters will be replaced by spaces.
+ *
+ * @param tszFilename - string to filter.
+ */
+void Utils::sanitizeFilename(wchar_t* tszFilename)
+{
+ static wchar_t *forbiddenCharacters = L"%/\\':|\"<>?";
+ int i;
+
+ for (i = 0; i < lstrlenW(forbiddenCharacters); i++) {
+ wchar_t* szFound = 0;
+
+ while ((szFound = wcschr(tszFilename, (int)forbiddenCharacters[i])) != NULL)
+ *szFound = ' ';
+ }
+}
+
+/**
+ * ensure that a path name ends on a trailing backslash
+ * @param szPathname - pathname to check
+ */
+void Utils::ensureTralingBackslash(wchar_t *szPathname)
+{
+ if(szPathname[lstrlenW(szPathname) - 1] != '\\')
+ wcscat(szPathname, L"\\");
+}
+
+/**
+ * load a system library from the Windows system path and return its module
+ * handle.
+ *
+ * return 0 and throw an exception if something goes wrong.
+ */
+HMODULE Utils::loadSystemLibrary(const wchar_t* szFilename)
+{
+ wchar_t sysPathName[MAX_PATH + 2];
+ HMODULE _h = 0;
+
+ try {
+ if(0 == ::GetSystemDirectoryW(sysPathName, MAX_PATH))
+ throw(CRTException("Error while loading system library", szFilename));
+
+ sysPathName[MAX_PATH - 1] = 0;
+ if(wcslen(sysPathName) + wcslen(szFilename) >= MAX_PATH)
+ throw(CRTException("Error while loading system library", szFilename));
+
+ lstrcatW(sysPathName, szFilename);
+ _h = LoadLibraryW(sysPathName);
+ if(0 == _h)
+ throw(CRTException("Error while loading system library", szFilename));
+ }
+ catch(CRTException& ex) {
+ ex.display();
+ return(0);
+ }
+ return(_h);
+}
+/**
+ * implementation of the CWarning class
+ */
+CWarning::CWarning(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const DWORD dwFlags)
+{
+ m_szTitle = new std::basic_string<wchar_t>(tszTitle);
+ m_szText = new std::basic_string<wchar_t>(tszText);
+ m_uId = uId;
+ m_hFontCaption = 0;
+ m_dwFlags = dwFlags;
+
+ m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
+}
+
+CWarning::~CWarning()
+{
+ delete m_szText;
+ delete m_szTitle;
+
+ if(m_hFontCaption)
+ ::DeleteObject(m_hFontCaption);
+
+#if defined(__LOGDEBUG_)
+ _DebugTraceW(L"destroy object");
+#endif
+}
+
+LRESULT CWarning::ShowDialog() const
+{
+ if(!m_fIsModal) {
+ ::CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_WARNING), 0, stubDlgProc, reinterpret_cast<LPARAM>(this));
+ return(0);
+ }
+ else {
+ LRESULT res = ::DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_WARNING), 0, stubDlgProc, reinterpret_cast<LPARAM>(this));
+ return(res);
+ }
+}
+
+__int64 CWarning::getMask()
+{
+ __int64 mask = 0;
+
+ DWORD dwLow = M->GetDword("cWarningsL", 0);
+ DWORD dwHigh = M->GetDword("cWarningsH", 0);
+
+ mask = ((((__int64)dwHigh) << 32) & 0xffffffff00000000) | dwLow;
+
+ return(mask);
+}
+
+/**
+ * send cancel message to all open warning dialogs so they are destroyed
+ * before TabSRMM is unloaded.
+ *
+ * called by the OkToExit handler in globals.cpp
+ */
+void CWarning::destroyAll()
+{
+ if(hWindowList)
+ WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
+}
+/**
+ * show a CWarning dialog using the id value. Check whether the user has chosen to
+ * not show this message again. This has room for 64 different warning dialogs, which
+ * should be enough in the first place. Extending it should not be too hard though.
+ */
+LRESULT CWarning::show(const int uId, DWORD dwFlags, const wchar_t* tszTxt)
+{
+ wchar_t* separator_pos = 0;
+ __int64 mask = 0, val = 0;
+ LRESULT result = 0;
+ wchar_t* _s = 0;
+
+ if(0 == hWindowList)
+ hWindowList = reinterpret_cast<HANDLE>(::CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0));
+
+ /*
+ * don't open new warnings when shutdown was initiated (modal ones will otherwise
+ * block the shutdown)
+ */
+ if(CMimAPI::m_shutDown)
+ return(-1);
+
+ if(tszTxt)
+ _s = const_cast<wchar_t *>(tszTxt);
+ else {
+ if(uId != -1) {
+ if(dwFlags & CWF_UNTRANSLATED)
+ _s = const_cast<wchar_t *>(CTranslator::getUntranslatedWarning(uId));
+ else {
+ /*
+ * revert to untranslated warning when the translated message
+ * is not well-formatted.
+ */
+ _s = const_cast<wchar_t *>(CTranslator::getWarning(uId));
+
+ if(wcslen(_s) < 3 || 0 == wcschr(_s, '|'))
+ _s = const_cast<wchar_t *>(CTranslator::getUntranslatedWarning(uId));
+ }
+ }
+ else if(-1 == uId && tszTxt) {
+ dwFlags |= CWF_NOALLOWHIDE;
+ _s = (dwFlags & CWF_UNTRANSLATED ? const_cast<wchar_t *>(tszTxt) : TranslateW(tszTxt));
+ }
+ else
+ return(-1);
+ }
+
+ if((wcslen(_s) > 3) && ((separator_pos = wcschr(_s, '|')) != 0)) {
+
+ if(uId >= 0) {
+ mask = getMask();
+ val = ((__int64)1L) << uId;
+ }
+ else
+ mask = val = 0;
+
+ if(0 == (mask & val) || dwFlags & CWF_NOALLOWHIDE) {
+
+ wchar_t *s = reinterpret_cast<wchar_t *>(mir_alloc((wcslen(_s) + 1) * 2));
+ wcscpy(s, _s);
+ separator_pos = wcschr(s, '|');
+
+ if(separator_pos) {
+ separator_pos[0] = 0;
+
+ CWarning *w = new CWarning(s, &separator_pos[1], uId, dwFlags);
+ if(!(dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)) {
+ w->ShowDialog();
+ mir_free(s);
+ }
+ else {
+ result = w->ShowDialog();
+ mir_free(s);
+ return(result);
+ }
+ }
+ else
+ mir_free(s);
+ }
+ }
+ return(-1);
+}
+
+/**
+ * stub dlg procedure. Just register the object pointer in WM_INITDIALOG
+ */
+INT_PTR CALLBACK CWarning::stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ CWarning *w = reinterpret_cast<CWarning *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+ if(w)
+ return(w->dlgProc(hwnd, msg, wParam, lParam));
+
+ switch(msg) {
+ case WM_INITDIALOG: {
+ w = reinterpret_cast<CWarning *>(lParam);
+ if(w) {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ return(w->dlgProc(hwnd, msg, wParam, lParam));
+ }
+ break;
+ }
+
+#if defined(__LOGDEBUG_)
+ case WM_NCDESTROY:
+ _DebugTraceW(L"window destroyed");
+ break;
+#endif
+
+ default:
+ break;
+ }
+ return(FALSE);
+}
+
+/**
+ * dialog procedure for the warning dialog box
+ */
+INT_PTR CALLBACK CWarning::dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+ case WM_INITDIALOG: {
+ HICON hIcon = 0;
+ UINT uResId = 0;
+ TCHAR temp[1024];
+ SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
+ size_t pos = 0;
+
+ m_hwnd = hwnd;
+
+ ::SetWindowTextW(hwnd, CTranslator::get(CTranslator::GEN_STRING_WARNING_TITLE));
+ ::SendMessage(hwnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(::LoadSkinnedIconBig(SKINICON_OTHER_MIRANDA)));
+ ::SendMessage(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(::LoadSkinnedIcon(SKINICON_OTHER_MIRANDA)));
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, (WPARAM) TRUE, 0);
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
+
+ mir_sntprintf(temp, 1024, RTF_DEFAULT_HEADER, 0, 0, 0, 30*15);
+ tstring *str = new tstring(temp);
+
+ str->append(m_szText->c_str());
+ str->append(L"}");
+
+ TranslateDialogDefault(hwnd);
+
+ /*
+ * convert normal line breaks to rtf
+ */
+ while((pos = str->find(L"\n")) != str->npos) {
+ str->erase(pos, 1);
+ str->insert(pos, L"\\line ");
+ }
+
+ char *utf8 = M->utf8_encodeT(str->c_str());
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)utf8);
+ mir_free(utf8);
+ delete str;
+
+ ::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle->c_str());
+
+ if(m_dwFlags & CWF_NOALLOWHIDE)
+ Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
+ if(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
+ Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
+ ::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
+ }
+ else {
+ Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
+ Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
+ Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
+ ::SetFocus(::GetDlgItem(hwnd, IDOK));
+ }
+ if(m_dwFlags & MB_ICONERROR || m_dwFlags & MB_ICONHAND)
+ uResId = 32513;
+ else if(m_dwFlags & MB_ICONEXCLAMATION || m_dwFlags & MB_ICONWARNING)
+ uResId = 32515;
+ else if(m_dwFlags & MB_ICONASTERISK || m_dwFlags & MB_ICONINFORMATION)
+ uResId = 32516;
+ else if(m_dwFlags & MB_ICONQUESTION)
+ uResId = 32514;
+
+ if(uResId)
+ hIcon = reinterpret_cast<HICON>(::LoadImage(0, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
+ else
+ hIcon = ::LoadSkinnedIconBig(SKINICON_EVENT_MESSAGE);
+
+ ::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);
+ if(!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
+ ::ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ WindowList_Add(hWindowList, hwnd, hwnd);
+ return(TRUE);
+ }
+
+ case WM_CTLCOLORSTATIC: {
+ HWND hwndChild = reinterpret_cast<HWND>(lParam);
+ UINT id = ::GetDlgCtrlID(hwndChild);
+ if(0 == m_hFontCaption) {
+ HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
+ LOGFONT lf = {0};
+
+ ::GetObject(hFont, sizeof(lf), &lf);
+ lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
+ m_hFontCaption = ::CreateFontIndirect(&lf);
+ ::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+ }
+
+ if(IDC_CAPTION == id) {
+ ::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
+ ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+ }
+
+ if(IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
+ ::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
+ return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
+ }
+ break;
+ }
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ case IDYES:
+ case IDNO:
+ if(!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) { // modeless dialogs can receive a IDCANCEL from destroyAll()
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ delete this;
+ WindowList_Remove(hWindowList, hwnd);
+ ::DestroyWindow(hwnd);
+ }
+ else {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ delete this;
+ WindowList_Remove(hWindowList, hwnd);
+ ::EndDialog(hwnd, LOWORD(wParam));
+ }
+ break;
+
+ case IDC_DONTSHOWAGAIN: {
+ __int64 mask = getMask(), val64 = ((__int64)1L << m_uId), newVal = 0;
+
+ newVal = mask | val64;
+
+ if(::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
+ DWORD val = (DWORD)(newVal & 0x00000000ffffffff);
+ M->WriteDword(SRMSGMOD_T, "cWarningsL", val);
+ val = (DWORD)((newVal >> 32) & 0x00000000ffffffff);
+ M->WriteDword(SRMSGMOD_T, "cWarningsH", val);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case WM_NOTIFY: {
+ switch (((NMHDR *) lParam)->code) {
+ case EN_LINK:
+ switch (((ENLINK *) lParam)->msg) {
+ case WM_LBUTTONUP: {
+ ENLINK* e = reinterpret_cast<ENLINK *>(lParam);
+
+ const wchar_t* wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
+ if(wszUrl) {
+ char* szUrl = mir_t2a(wszUrl);
+
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ mir_free(szUrl);
+ mir_free(const_cast<TCHAR *>(wszUrl));
+ }
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return(FALSE);
+}
|