summaryrefslogtreecommitdiff
path: root/plugins/TabSRMM/src/infopanel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/TabSRMM/src/infopanel.cpp')
-rw-r--r--plugins/TabSRMM/src/infopanel.cpp1696
1 files changed, 1696 insertions, 0 deletions
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));
+}