summaryrefslogtreecommitdiff
path: root/plugins/TabSRMM/src/sidebar.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-23 07:44:30 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-23 07:44:30 +0000
commitf3d44bc057201407373012b7f682881bda7b3d98 (patch)
treeddf031a82529c84e13222131cf184ecf176d3954 /plugins/TabSRMM/src/sidebar.cpp
parentc2d827972a16f1710406d15e58304aecc4e1c9b5 (diff)
some includes restored, mir_full.sln updated, some renaming of folders and projects
git-svn-id: http://svn.miranda-ng.org/main/trunk@140 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/TabSRMM/src/sidebar.cpp')
-rw-r--r--plugins/TabSRMM/src/sidebar.cpp1232
1 files changed, 1232 insertions, 0 deletions
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());
+}