/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2007 Artem Shpynov Copyright 2000-12 Miranda IM, 2012-13 Miranda NG 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. */ #include "..\..\core\commonheaders.h" #include "m_iconheader.h" extern HINSTANCE hInst; static BOOL IsAeroMode() { BOOL result; return dwmIsCompositionEnabled && (dwmIsCompositionEnabled(&result) == S_OK) && result; } static BOOL IsVSMode() { return IsWinVerVistaPlus() && IsThemeActive(); } //////////////////////////////////////////////////////////////////////////////////// // Internals #define ITC_BORDER_SIZE 3 static LRESULT CALLBACK MIcoTabWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); // structure is used for storing list of tab info struct MIcoTabCtrl : public MZeroedObject { MIcoTabCtrl(): pList(1) {} HWND hwnd; int nSelectedIdx, nHotIdx; LIST pList; // UI info BOOL bMouseInside; RECT rc; int width, height; int itemWidth, itemHeight; //background bitmap HBITMAP hBkgBmp; HBITMAP hBkgOldBmp; HDC hBkgDC; SIZE BkgSize; // control colors RGBQUAD rgbBkgTop, rgbBkgBottom; RGBQUAD rgbSelTop, rgbSelBottom; RGBQUAD rgbHotTop, rgbHotBottom; COLORREF clText; COLORREF clSelText, clSelBorder; COLORREF clHotText, clHotBorder; // fonts HFONT hFont; }; typedef void (*ItemDestuctor)(void*); static void MITListDestructor(void * adr) { MIcoTab * mit = (MIcoTab *)adr; mir_free(mit->tcsName); if (mit->hIcon && !(mit->flag&MITCF_SHAREDICON)) DestroyIcon(mit->hIcon); mir_free(adr); } void li_ListDestruct(LIST &pList, ItemDestuctor pItemDestructor) { for (int i=0; irgbBkgBottom.rgbRed = (dat->rgbBkgTop.rgbRed = GetRValue(cl)) * .95; dat->rgbBkgBottom.rgbGreen = (dat->rgbBkgTop.rgbGreen = GetGValue(cl)) * .95; dat->rgbBkgBottom.rgbBlue = (dat->rgbBkgTop.rgbBlue = GetBValue(cl)) * .95; cl = GetSysColor(COLOR_HIGHLIGHT); dat->rgbSelTop.rgbRed = (dat->rgbSelBottom.rgbRed = GetRValue(cl)) * .75; dat->rgbSelTop.rgbGreen = (dat->rgbSelBottom.rgbGreen = GetGValue(cl)) * .75; dat->rgbSelTop.rgbBlue = (dat->rgbSelBottom.rgbBlue = GetBValue(cl)) * .75; dat->rgbHotTop.rgbRed = (dat->rgbSelTop.rgbRed + 255) / 2; dat->rgbHotTop.rgbGreen = (dat->rgbSelTop.rgbGreen + 255) / 2; dat->rgbHotTop.rgbBlue = (dat->rgbSelTop.rgbBlue + 255) / 2; dat->rgbHotBottom.rgbRed = (dat->rgbSelBottom.rgbRed + 255) / 2; dat->rgbHotBottom.rgbGreen = (dat->rgbSelBottom.rgbGreen + 255) / 2; dat->rgbHotBottom.rgbBlue = (dat->rgbSelBottom.rgbBlue + 255) / 2; dat->clText = GetSysColor(COLOR_WINDOWTEXT); dat->clSelText = GetSysColor(COLOR_HIGHLIGHTTEXT); dat->clSelBorder = RGB(dat->rgbSelTop.rgbRed, dat->rgbSelTop.rgbGreen, dat->rgbSelTop.rgbBlue); dat->clHotBorder = RGB(dat->rgbHotTop.rgbRed, dat->rgbHotTop.rgbGreen, dat->rgbHotTop.rgbBlue); if ( !dat->hFont) dat->hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); } static void MIcoTab_FillRect(HDC hdc, int x, int y, int width, int height, COLORREF cl) { int oldMode = SetBkMode(hdc, OPAQUE); COLORREF oldColor = SetBkColor(hdc, cl); RECT rc; SetRect(&rc, x, y, x+width, y+height); ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0); SetBkMode(hdc, oldMode); SetBkColor(hdc, oldColor); } static void MIcoTab_DrawGradient(HDC hdc, int x, int y, int width, int height, RGBQUAD *rgb0, RGBQUAD *rgb1) { int oldMode = SetBkMode(hdc, OPAQUE); COLORREF oldColor = SetBkColor(hdc, 0); RECT rc; SetRect(&rc, x, 0, x+width, 0); for (int i = y+height; --i >= y;) { COLORREF color = RGB( ((height-i-1)*rgb0->rgbRed + i*rgb1->rgbRed) / height, ((height-i-1)*rgb0->rgbGreen + i*rgb1->rgbGreen) / height, ((height-i-1)*rgb0->rgbBlue + i*rgb1->rgbBlue) / height); rc.top = rc.bottom = i; ++rc.bottom; SetBkColor(hdc, color); ExtTextOutA(hdc, 0, 0, ETO_OPAQUE, &rc, "", 0, 0); } SetBkMode(hdc, oldMode); SetBkColor(hdc, oldColor); } static void MIcoTab_DrawItem(HWND hwnd, HDC hdc, MIcoTabCtrl *dat, MIcoTab *tab, int i) { int iTopSpace = IsAeroMode() ? 0 : ITC_BORDER_SIZE; int itemX = ITC_BORDER_SIZE + dat->itemWidth * i; int iconTop = iTopSpace + 5; int textTop = iconTop + 32 + 3; HFONT hFntSave = NULL; if (dat->nSelectedIdx == i) { LOGFONT lf; GetObject(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf); lf.lfWeight = FW_BOLD; hFntSave = (HFONT)SelectObject(hdc, CreateFontIndirect(&lf)); if (IsVSMode()) { RECT rc; rc.left = itemX; rc.top = iTopSpace; rc.right = itemX + dat->itemWidth; rc.bottom = iTopSpace + dat->itemHeight; HANDLE hTheme = OpenThemeData(hwnd, L"ListView"); if (dat->nHotIdx == i || GetFocus() == hwnd) DrawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_HOTSELECTED, &rc, NULL); else DrawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_SELECTED, &rc, NULL); CloseThemeData(hTheme); } else { MIcoTab_FillRect(hdc, itemX, ITC_BORDER_SIZE, dat->itemWidth, dat->itemHeight, dat->clSelBorder); MIcoTab_DrawGradient(hdc, itemX+1, ITC_BORDER_SIZE+1, dat->itemWidth-2, dat->itemHeight-2, &dat->rgbSelTop, &dat->rgbSelBottom); } SetTextColor(hdc, dat->clSelText); } else if (dat->nHotIdx == i) { if (IsVSMode()) { RECT rc; rc.left = itemX; rc.top = iTopSpace; rc.right = itemX + dat->itemWidth; rc.bottom = iTopSpace + dat->itemHeight; SetWindowTheme(hwnd, L"explorer", NULL); HANDLE hTheme = OpenThemeData(hwnd, L"ListView"); DrawThemeBackground(hTheme, hdc, LVP_LISTITEM, LISS_HOT, &rc, NULL); CloseThemeData(hTheme); } else { MIcoTab_FillRect(hdc, itemX, ITC_BORDER_SIZE, dat->itemWidth, dat->itemHeight, dat->clHotBorder); MIcoTab_DrawGradient(hdc, itemX+1, ITC_BORDER_SIZE+1, dat->itemWidth-2, dat->itemHeight-2, &dat->rgbHotTop, &dat->rgbHotBottom); } SetTextColor(hdc, dat->clHotText); } else SetTextColor(hdc, dat->clText); RECT textRect; textRect.left = itemX; textRect.right = itemX+dat->itemWidth; textRect.top = textTop; textRect.bottom = iconTop+dat->itemHeight; DrawIcon(hdc, itemX+dat->itemWidth/2-16, iconTop, tab->hIcon); if (IsVSMode()) { DTTOPTS dto = {0}; dto.dwSize = sizeof(dto); dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE; dto.iGlowSize = 10; HANDLE hTheme = OpenThemeData(hwnd, L"Window"); wchar_t *tcsNameW = mir_t2u(tab->tcsName); drawThemeTextEx(hTheme, hdc, WP_CAPTION, CS_ACTIVE, tcsNameW, -1, DT_VCENTER|DT_CENTER|DT_END_ELLIPSIS, &textRect, &dto); mir_free(tcsNameW); CloseThemeData(hTheme); } else DrawText(hdc, tab->tcsName, -1, &textRect, DT_VCENTER|DT_CENTER|DT_END_ELLIPSIS); if (hFntSave) DeleteObject(SelectObject(hdc, hFntSave)); } static LRESULT MIcoTab_OnPaint(HWND hwndDlg, MIcoTabCtrl *mit, UINT msg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwndDlg, &ps); HDC tempDC = CreateCompatibleDC(hdc); BITMAPINFO bmi; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = mit->width; bmi.bmiHeader.biHeight = -mit->height; // we need this for DrawThemeTextEx bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; HBITMAP hBmp = CreateDIBSection(tempDC, &bmi, DIB_RGB_COLORS, NULL, NULL, 0); HBITMAP hOldBmp = (HBITMAP)SelectObject(tempDC, hBmp); if (IsAeroMode()) { RECT temprc; temprc.left = 0; temprc.right = mit->width; temprc.top = 0; temprc.bottom = mit->width; FillRect(tempDC, &temprc, (HBRUSH)GetStockObject(BLACK_BRUSH)); } else { if (mit->hBkgBmp) StretchBlt(tempDC, 0, 0, mit->width, mit->height, mit->hBkgDC, 0, 0, mit->BkgSize.cx, mit->BkgSize.cy, SRCCOPY); else { if (IsVSMode()) MIcoTab_FillRect(tempDC, 0, 0, mit->width, mit->height, GetSysColor(COLOR_WINDOW)); else MIcoTab_DrawGradient(tempDC, 0, 0, mit->width, mit->height, &mit->rgbBkgTop, &mit->rgbBkgBottom); MIcoTab_FillRect(tempDC, 0, mit->height-2, mit->width, 1, GetSysColor(COLOR_BTNSHADOW)); MIcoTab_FillRect(tempDC, 0, mit->height-1, mit->width, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); } } //Draw Items HFONT hFont = mit->hFont; HFONT hOldFont = (HFONT)SelectObject(tempDC, hFont); SetBkMode(tempDC, TRANSPARENT); for (int i=0; ipList.getCount(); i++) { MIcoTab *tab = (MIcoTab *)mit->pList[i]; MIcoTab_DrawItem(hwndDlg, tempDC, mit, tab, i); } //Copy to output BitBlt(hdc, mit->rc.left, mit->rc.top, mit->width, mit->height, tempDC, 0, 0, SRCCOPY); SelectObject(tempDC, hOldBmp); DeleteObject(hBmp); SelectObject(tempDC,hOldFont); DeleteDC(tempDC); EndPaint(hwndDlg, &ps); return TRUE; } static LRESULT CALLBACK MIcoTabWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { MIcoTabCtrl* itc = (MIcoTabCtrl *)GetWindowLongPtr(hwndDlg, 0); switch(msg) { case WM_NCCREATE: itc = new MIcoTabCtrl; //(MIcoTabCtrl*)mir_alloc(sizeof(MIcoTabCtrl)); if (itc == NULL) return FALSE; itc->nSelectedIdx = -1; itc->nHotIdx = -1; itc->bMouseInside = FALSE; SetWindowLongPtr(hwndDlg, 0, (LONG_PTR)itc); MIcoTab_SetupColors(itc); if (IsAeroMode()) { RECT rc; GetWindowRect(hwndDlg, &rc); MARGINS margins = {0, 0, rc.bottom-rc.top, 0}; dwmExtendFrameIntoClientArea(GetParent(hwndDlg), &margins); } return TRUE; case WM_SETFONT: itc->hFont = (HFONT)wParam; break; case WM_SIZE: GetClientRect(hwndDlg, &itc->rc); itc->width = itc->rc.right-itc->rc.left; itc->height = itc->rc.bottom-itc->rc.top; if (itc->pList.getCount()) { itc->itemWidth = (itc->width-2*ITC_BORDER_SIZE)/itc->pList.getCount(); itc->itemHeight = itc->height-2*ITC_BORDER_SIZE-2; } else itc->itemWidth = itc->itemHeight = 0; return TRUE; case WM_THEMECHANGED: case WM_STYLECHANGED: MIcoTab_SetupColors(itc); return TRUE; case WM_MOUSEMOVE: if ( !itc->bMouseInside) { TRACKMOUSEEVENT tme = {0}; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwndDlg; _TrackMouseEvent(&tme); itc->bMouseInside = TRUE; } itc->nHotIdx = (LOWORD(lParam) - ITC_BORDER_SIZE) / itc->itemWidth; if (itc->nHotIdx >= itc->pList.getCount()) itc->nHotIdx = -1; RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); return 0; case WM_MOUSELEAVE: itc->bMouseInside = FALSE; itc->nHotIdx = -1; RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); return 0; case WM_LBUTTONUP: if ((itc->nHotIdx >= 0) && (itc->nHotIdx != itc->nSelectedIdx)) { itc->nSelectedIdx = itc->nHotIdx; SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName); RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGED), itc->nSelectedIdx); } return 0; case WM_SETFOCUS: case WM_KILLFOCUS: RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); break; case WM_MOUSEACTIVATE: SetFocus(hwndDlg); return MA_ACTIVATE; case WM_GETDLGCODE: { if (lParam) { MSG *msg = (MSG *) lParam; if (msg->message == WM_KEYDOWN) { if (msg->wParam == VK_TAB) return 0; if (msg->wParam == VK_ESCAPE) return 0; } else if (msg->message == WM_CHAR) { if (msg->wParam == '\t') return 0; if (msg->wParam == 27) return 0; } } return DLGC_WANTMESSAGE; } case WM_KEYDOWN: { int newIdx = itc->nSelectedIdx; switch (wParam) { case VK_NEXT: case VK_RIGHT: newIdx++; break; case VK_PRIOR: case VK_LEFT: newIdx--; break; } if ((newIdx >= 0) && (newIdx < itc->pList.getCount()) && (newIdx != itc->nSelectedIdx)) { itc->nSelectedIdx = newIdx; SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName); RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGEDKBD), itc->nSelectedIdx); } return 0; } case WM_ERASEBKGND: return 1; case WM_NCPAINT: InvalidateRect(hwndDlg, NULL, FALSE); break; case WM_PAINT: MIcoTab_OnPaint(hwndDlg, itc, msg, wParam, lParam); break; case ITCM_SETBACKGROUND: itc->hBkgBmp = (HBITMAP)lParam; if ( !itc->hBkgDC) itc->hBkgDC = CreateCompatibleDC(NULL); itc->hBkgOldBmp = (HBITMAP)SelectObject(itc->hBkgDC, itc->hBkgBmp); { BITMAPINFO bmp; GetObject(itc->hBkgBmp, sizeof(bmp), &bmp); itc->BkgSize.cx = bmp.bmiHeader.biWidth; itc->BkgSize.cy = bmp.bmiHeader.biHeight; } return TRUE; case ITCM_ADDITEM: { MIcoTab* pMit = (MIcoTab *)wParam; if ( !pMit) return FALSE; MIcoTab* pListMit = (MIcoTab *)mir_calloc(sizeof(MIcoTab)); pListMit->flag = pMit->flag; pListMit->data = pMit->data; if (pMit->flag & MITCF_UNICODE) pListMit->tcsName = mir_u2t(pMit->lpwzName); else pListMit->tcsName = mir_a2t(pMit->lpzName); if (pMit->hIcon) { if (pListMit->flag&MITCF_SHAREDICON) pListMit->hIcon = pMit->hIcon; else pListMit->hIcon = CopyIcon(pMit->hIcon); } itc->pList.insert(pListMit); itc->itemWidth = (itc->width-2*ITC_BORDER_SIZE)/itc->pList.getCount(); itc->itemHeight = itc->height-2*ITC_BORDER_SIZE-2; RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); return TRUE; } case ITCM_SETSEL: if (wParam >= 0 && (int)wParam < itc->pList.getCount()) { itc->nSelectedIdx = wParam; SetWindowText(hwndDlg, itc->pList[itc->nSelectedIdx]->tcsName); RedrawWindow(hwndDlg, NULL, NULL, RDW_INVALIDATE); SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(GetWindowLongPtr(hwndDlg, GWL_ID), ITCN_SELCHANGED), itc->nSelectedIdx); } return TRUE; case ITCM_GETSEL: return itc->nSelectedIdx; case ITCM_GETITEMDATA: if (wParam >= 0 && (int)wParam < itc->pList.getCount()) return ((MIcoTab *)itc->pList[wParam])->data; return 0; case WM_DESTROY: if (itc->hBkgDC) { SelectObject(itc->hBkgDC, itc->hBkgOldBmp); DeleteDC(itc->hBkgDC); } li_ListDestruct(itc->pList, MITListDestructor); delete itc; return TRUE; } return DefWindowProc(hwndDlg, msg, wParam, lParam); }