/* Miranda NG: the free IM client for Microsoft* Windows* Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-03 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. */ #include "stdafx.h" #include #include "cluiframes.h" #include "coolscroll.h" #define TM_AUTOALPHA 1 #define TIMERID_AUTOSIZE 100 #define MENU_MIRANDAMENU 0xFFFF1234 int g_fading_active = 0; static RECT g_PreSizeRect, g_SizingRect; static int g_sizingmethod; static LONG g_CLUI_x_off, g_CLUI_y_off, g_CLUI_y1_off, g_CLUI_x1_off; static RECT rcWPC; static int transparentFocus = 1; static byte oldhideoffline; static int disableautoupd = 1; static int hFrameContactTree; extern RECT old_window_rect, new_window_rect; extern BOOL g_trayTooltipActive; extern POINT tray_hover_pos; extern HWND g_hwndViewModeFrame, g_hwndEventArea, g_hwndToolbarFrame; extern ImageItem *g_CLUIImageItem; extern HBRUSH g_CLUISkinnedBkColor; extern HWND g_hwndSFL; extern ButtonItem *g_ButtonItems; extern COLORREF g_CLUISkinnedBkColorRGB; extern FRAMEWND *wndFrameCLC; extern HPEN g_hPenCLUIFrames; static uint8_t old_cliststate, show_on_first_autosize = FALSE; RECT cluiPos; wchar_t *statusNames[12]; extern LRESULT CALLBACK EventAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); extern int hNotifyFrame; void MF_InitCheck(void); void InitGroupMenus(); void FS_RegisterFonts(); void LoadExtraIconModule(); void RemoveFromTaskBar(HWND hWnd); extern LONG g_cxsmIcon, g_cysmIcon; SIZE g_oldSize = { 0 }; POINT g_oldPos = { 0 }; int during_sizing = 0; extern int dock_prevent_moving; static HDC hdcLockedPoint = nullptr; static HBITMAP hbmLockedPoint = nullptr, hbmOldLockedPoint = nullptr; HICON overlayicons[10]; static IconItem myIcons[] = { { LPGEN("Toggle show online/offline"), "CLN_online", IDI_HIDEOFFLINE }, { LPGEN("Toggle groups"), "CLN_groups", IDI_HIDEGROUPS }, { LPGEN("Find contacts"), "CLN_findadd", IDI_FINDANDADD }, { LPGEN("Open preferences"), "CLN_options", IDI_TBOPTIONS }, { LPGEN("Toggle sounds"), "CLN_sound", IDI_SOUNDSON }, { LPGEN("Minimize contact list"), "CLN_minimize", IDI_MINIMIZE }, { LPGEN("Show TabSRMM session list"), "CLN_slist", IDI_TABSRMMSESSIONLIST }, { LPGEN("Show TabSRMM menu"), "CLN_menu", IDI_TABSRMMMENU }, { LPGEN("Sounds are off"), "CLN_soundsoff", IDI_SOUNDSOFF }, { LPGEN("Select view mode"), "CLN_CLVM_select", IDI_CLVM_SELECT }, { LPGEN("Reset view mode"), "CLN_CLVM_reset", IDI_DELETE }, { LPGEN("Configure view modes"), "CLN_CLVM_options", IDI_CLVM_OPTIONS }, { LPGEN("Show menu"), "CLN_topmenu", IDI_TBTOPMENU }, { LPGEN("Setup accounts"), "CLN_accounts", IDI_TBACCOUNTS } }; HWND hTbMenu, hTbGlobalStatus; static void Tweak_It(COLORREF clr) { SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED); SetLayeredWindowAttributes(g_clistApi.hwndContactList, clr, 0, LWA_COLORKEY); cfg::dat.colorkey = clr; } static void LayoutButtons(HWND hwnd, RECT *rc) { RECT rect; uint8_t left_offset = cfg::dat.bCLeft - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0); uint8_t right_offset = cfg::dat.bCRight - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0); uint8_t delta = left_offset + right_offset; ButtonItem *btnItems = g_ButtonItems; if (rc == nullptr) GetClientRect(hwnd, &rect); else rect = *rc; rect.bottom -= cfg::dat.bCBottom; if (g_ButtonItems) { while (btnItems) { LONG x = (btnItems->xOff >= 0) ? rect.left + btnItems->xOff : rect.right - abs(btnItems->xOff); LONG y = (btnItems->yOff >= 0) ? rect.top + btnItems->yOff : rect.bottom - cfg::dat.statusBarHeight; SetWindowPos(btnItems->hWnd, nullptr, x, y, btnItems->width, btnItems->height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); btnItems = btnItems->nextItem; } } SetWindowPos(hTbMenu, nullptr, 2 + left_offset, rect.bottom - cfg::dat.statusBarHeight - 21 - 1, 21 * 3, 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); SetWindowPos(hTbGlobalStatus, nullptr, left_offset + (3 * 21) + 3, rect.bottom - cfg::dat.statusBarHeight - 21 - 1, rect.right - delta - (3 * 21 + 5), 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); } static int FS_FontsChanged(WPARAM, LPARAM) { COLORREF clr_cluiframes = db_get_dw(0, "CLUI", "clr_frameborder", RGB(40, 40, 40)); if (g_hPenCLUIFrames) DeleteObject(g_hPenCLUIFrames); g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, clr_cluiframes); Clist_ClcOptionsChanged(); RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); return 0; } // create the CLC control, but not yet the frame. The frame containing the CLC should be created as the // last frame of all. static HWND PreCreateCLC(HWND parent) { g_clistApi.hwndContactTree = CreateWindow(CLISTCONTROL_CLASSW, L"", WS_CHILD | CLS_CONTACTLIST | (Clist::UseGroups ? CLS_USEGROUPS : 0) | (Clist::HideOffline ? CLS_HIDEOFFLINE : 0) | (Clist::HideEmptyGroups ? CLS_HIDEEMPTYGROUPS : 0) | CLS_MULTICOLUMN, 0, 0, 0, 0, parent, nullptr, g_plugin.getInst(), (LPVOID)0xff00ff00); cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0); return g_clistApi.hwndContactTree; } // create internal frames, including the last frame (actual CLC control) static int CreateCLC() { ExtraIcon_Reload(); g_clistApi.pfnSetHideOffline(oldhideoffline); disableautoupd = 0; { CLISTFrame frame = { 0 }; frame.cbSize = sizeof(frame); frame.szName.a = "EventArea"; frame.szTBname.a = LPGEN("Event area"); frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME); frame.height = 20; frame.Flags = F_VISIBLE | F_SHOWTBTIP | F_NOBORDER; frame.align = alBottom; frame.hWnd = CreateWindowExA(0, "EventAreaClass", "evt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, g_clistApi.hwndContactList, (HMENU)nullptr, g_plugin.getInst(), nullptr); g_hwndEventArea = frame.hWnd; hNotifyFrame = g_plugin.addFrame(&frame); CallService(MS_CLIST_FRAMES_UPDATEFRAME, hNotifyFrame, FU_FMPOS); HideShowNotifyFrame(); CreateViewModeFrame(); } { CLISTFrame Frame = { 0 }; Frame.cbSize = sizeof(CLISTFrame); Frame.hWnd = g_clistApi.hwndContactTree; Frame.align = alClient; Frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME); Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER; Frame.szName.a = "My contacts"; Frame.szTBname.a = LPGEN("My contacts"); Frame.height = 200; hFrameContactTree = g_plugin.addFrame(&Frame); CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_TBTIPNAME | FO_UNICODETEXT, hFrameContactTree), (LPARAM)TranslateT("My contacts")); // ugly, but working hack. Prevent that annoying little scroll bar from appearing in the "My Contacts" title bar uint32_t flags = (uint32_t)CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), 0); flags |= F_VISIBLE; CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), flags); } SetButtonToSkinned(); return 0; } static int CluiModulesLoaded(WPARAM, LPARAM) { FS_RegisterFonts(); HookEvent(ME_FONT_RELOAD, FS_FontsChanged); return 0; } static HICON hIconSaved = nullptr; void ClearIcons(int mode) { for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) { if (overlayicons[i - IDI_OVL_OFFLINE] != nullptr) { if (mode) DestroyIcon(overlayicons[i - IDI_OVL_OFFLINE]); overlayicons[i - IDI_OVL_OFFLINE] = nullptr; } } } static void CacheClientIcons() { ClearIcons(0); for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) { char szBuffer[128]; mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE)); overlayicons[i - IDI_OVL_OFFLINE] = IcoLib_GetIcon(szBuffer); } } static void InitIcoLib() { g_plugin.registerIcon(LPGEN("Contact list") "/" LPGEN("Default"), myIcons); for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) { char szBuffer[128]; mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE)); IconItemT icon[] = { { Clist_GetStatusModeDescription(ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE), 0), szBuffer, i } }; g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Overlay icons"), icon); } for (auto &pa : Accounts()) { if (!pa->IsEnabled() || CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0) continue; wchar_t szDescr[128]; mir_snwprintf(szDescr, TranslateT("%s connecting"), pa->tszAccountName); IconItemT icon[] = { { szDescr, "conn", IDI_PROTOCONNECTING } }; g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Connecting icons"), icon, pa->szModuleName); } } static int IcoLibChanged(WPARAM, LPARAM) { IcoLibReloadIcons(); return 0; } void CreateButtonBar(HWND hWnd) { hTbMenu = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBMENU, g_plugin.getInst(), nullptr); CustomizeButton(hTbMenu, false, false, false); SetWindowText(hTbMenu, TranslateT("Menu")); SendMessage(hTbMenu, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_OTHER_MAINMENU)); SendMessage(hTbMenu, BUTTONSETSENDONDOWN, TRUE, 0); SendMessage(hTbMenu, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open main menu"), 0); hTbGlobalStatus = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBGLOBALSTATUS, g_plugin.getInst(), nullptr); CustomizeButton(hTbGlobalStatus, false, false, false); SetWindowText(hTbGlobalStatus, TranslateT("Offline")); SendMessage(hTbGlobalStatus, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_STATUS_OFFLINE)); SendMessage(hTbGlobalStatus, BUTTONSETSENDONDOWN, TRUE, 0); SendMessage(hTbGlobalStatus, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Set status modes"), 0); } // if mode != 0 we do first time init, otherwise only reload the extra icon stuff void CLN_LoadAllIcons(BOOL mode) { if (mode) { InitIcoLib(); HookEvent(ME_SKIN_ICONSCHANGED, IcoLibChanged); } CacheClientIcons(); } void ConfigureEventArea() { int iCount = GetMenuItemCount(cfg::dat.hMenuNotify); uint32_t dwFlags = cfg::dat.dwFlags; int oldstate = cfg::dat.notifyActive; int dwVisible = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hNotifyFrame), 0) & F_VISIBLE; if (dwVisible) { if (dwFlags & CLUI_FRAME_AUTOHIDENOTIFY) cfg::dat.notifyActive = iCount > 0 ? 1 : 0; else cfg::dat.notifyActive = 1; } else cfg::dat.notifyActive = 0; if (oldstate != cfg::dat.notifyActive) HideShowNotifyFrame(); } void ConfigureFrame() { int show = cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? SW_SHOW : SW_HIDE; ShowWindow(hTbMenu, show); ShowWindow(hTbGlobalStatus, show); } void IcoLibReloadIcons() { CacheClientIcons(); ExtraIcon_Reload(); ExtraIcon_SetAll(); Clist_Broadcast(CLM_AUTOREBUILD, 0, 0); SendMessage(g_hwndViewModeFrame, WM_USER + 100, 0, 0); } void ConfigureCLUIGeometry(int mode) { RECT rcStatus; uint32_t clmargins = db_get_dw(0, "CLUI", "clmargins", 0); cfg::dat.bCLeft = LOBYTE(LOWORD(clmargins)); cfg::dat.bCRight = HIBYTE(LOWORD(clmargins)); cfg::dat.bCTop = LOBYTE(HIWORD(clmargins)); cfg::dat.bCBottom = HIBYTE(HIWORD(clmargins)); if (mode) { if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) { SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0); GetWindowRect(g_clistApi.hwndStatus, &rcStatus); cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top); } else cfg::dat.statusBarHeight = 0; } cfg::dat.topOffset = cfg::dat.bCTop; cfg::dat.bottomOffset = (cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? 2 + 21 : 0) + cfg::dat.bCBottom; if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) { cfg::dat.topOffset += 2; cfg::dat.bottomOffset += 2; cfg::dat.bCLeft += 3; cfg::dat.bCRight += 3; } } // set the states of defined database action buttons (only if button is a toggle) void SetDBButtonStates(MCONTACT hPassedContact) { ButtonItem *buttonItem = g_ButtonItems; MCONTACT hContact = 0, hFinalContact = 0; char *szModule, *szSetting; ClcContact *contact = nullptr; if (cfg::clcdat && hPassedContact == 0) { g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr); if (contact && contact->type == CLCIT_CONTACT) { hContact = contact->hContact; } } while (buttonItem) { BOOL result = FALSE; if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) { buttonItem = buttonItem->nextItem; continue; } szModule = buttonItem->szModule; szSetting = buttonItem->szSetting; if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) { if (hContact == 0) { SendMessage(buttonItem->hWnd, BM_SETCHECK, BST_UNCHECKED, 0); buttonItem = buttonItem->nextItem; continue; } if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) szModule = Proto_GetBaseAccountName(hContact); hFinalContact = hContact; } else hFinalContact = 0; if (buttonItem->type == DBVT_ASCIIZ) { DBVARIANT dbv = { 0 }; if (!db_get_s(hFinalContact, szModule, szSetting, &dbv)) { result = !mir_strcmp((char *)buttonItem->bValuePush, dbv.pszVal); db_free(&dbv); } } else { switch (buttonItem->type) { case DBVT_BYTE: { uint8_t val = db_get_b(hFinalContact, szModule, szSetting, 0); result = (val == buttonItem->bValuePush[0]); break; } case DBVT_WORD: { uint16_t val = db_get_w(hFinalContact, szModule, szSetting, 0); result = (val == *((uint16_t *)&buttonItem->bValuePush)); break; } case DBVT_DWORD: uint32_t val = db_get_dw(hFinalContact, szModule, szSetting, 0); result = (val == *((uint32_t *)&buttonItem->bValuePush)); break; } } SendMessage(buttonItem->hWnd, BM_SETCHECK, (WPARAM)result, 0); buttonItem = buttonItem->nextItem; } } // set states of standard buttons (pressed/unpressed) void SetButtonStates() { ButtonItem *buttonItem = g_ButtonItems; if (g_ButtonItems) { while (buttonItem) { if (buttonItem->dwFlags & BUTTON_ISINTERNAL) { switch (buttonItem->uId) { case IDC_STBSOUND: SendMessage(buttonItem->hWnd, BM_SETCHECK, cfg::dat.soundsOff ? BST_CHECKED : BST_UNCHECKED, 0); break; case IDC_STBHIDEOFFLINE: SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::HideOffline, 0); break; case IDC_STBHIDEGROUPS: SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::UseGroups, 0); break; } } buttonItem = buttonItem->nextItem; } } } void BlitWallpaper(HDC hdc, RECT *rc, struct ClcData *dat) { int x, y; int bitx, bity; int maxx, maxy; int destw, desth, height, width; BITMAP *bmp = &cfg::dat.bminfoBg; LONG clip = cfg::dat.bClipBorder; if (dat == nullptr) return; SetStretchBltMode(hdc, HALFTONE); y = rc->top; rc->left = max(rc->left, clip); rc->right = min(rc->right - clip, rc->right); rc->top = max(rc->top, clip); rc->bottom = min(rc->bottom - clip, rc->bottom); width = rc->right - rc->left; height = rc->bottom - rc->top; HRGN my_rgn = CreateRectRgn(rc->left, rc->top, rc->right, rc->bottom); SelectClipRgn(hdc, my_rgn); maxx = dat->backgroundBmpUse & CLBF_TILEH ? rc->right : rc->left + 1; maxy = dat->backgroundBmpUse & CLBF_TILEV ? rc->bottom : y + 1; switch (dat->backgroundBmpUse & CLBM_TYPE) { case CLB_STRETCH: if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) { if (width * bmp->bmHeight < height * bmp->bmWidth) { desth = height; destw = desth * bmp->bmWidth / bmp->bmHeight; } else { destw = width; desth = destw * bmp->bmHeight / bmp->bmWidth; } } else { destw = width; desth = height; } break; case CLB_STRETCHH: if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) { destw = width; desth = destw * bmp->bmHeight / bmp->bmWidth; } else { destw = width; desth = bmp->bmHeight; } break; case CLB_STRETCHV: if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) { desth = height; destw = desth * bmp->bmWidth / bmp->bmHeight; } else { destw = bmp->bmWidth; desth = height; } break; default: //clb_topleft destw = bmp->bmWidth; desth = bmp->bmHeight; break; } bitx = 0; bity = 0; for (; y < maxy; y += desth) { for (x = rc->left; x < maxx; x += destw) StretchBlt(hdc, x, y, destw, desth, cfg::dat.hdcPic, bitx, bity, bmp->bmWidth, bmp->bmHeight, SRCCOPY); } SelectClipRgn(hdc, nullptr); DeleteObject(my_rgn); } void ReloadThemedOptions() { cfg::dat.bSkinnedStatusBar = db_get_b(0, "CLUI", "sb_skinned", 0); cfg::dat.bUsePerProto = db_get_b(0, "CLCExt", "useperproto", 0); cfg::dat.bOverridePerStatusColors = db_get_b(0, "CLCExt", "override_status", 0); cfg::dat.bRowSpacing = db_get_b(0, "CLC", "RowGap", 0); cfg::dat.bApplyIndentToBg = db_get_b(0, "CLCExt", "applyindentbg", 0); cfg::dat.bWallpaperMode = db_get_b(0, "CLUI", "UseBkSkin", 1); cfg::dat.bClipBorder = db_get_b(0, "CLUI", "clipborder", 0); cfg::dat.cornerRadius = db_get_b(0, "CLCExt", "CornerRad", 6); cfg::dat.gapBetweenFrames = (uint8_t)db_get_dw(0, "CLUIFrames", "GapBetweenFrames", 1); cfg::dat.bUseDCMirroring = db_get_b(0, "CLC", "MirrorDC", 0); cfg::dat.bGroupAlign = db_get_b(0, "CLC", "GroupAlign", 0); if (cfg::dat.hBrushColorKey) DeleteObject(cfg::dat.hBrushColorKey); cfg::dat.hBrushColorKey = CreateSolidBrush(RGB(255, 0, 255)); cfg::dat.bWantFastGradients = db_get_b(0, "CLCExt", "FastGradients", 0); cfg::dat.titleBarHeight = db_get_b(0, "CLCExt", "frame_height", DEFAULT_TITLEBAR_HEIGHT); cfg::dat.group_padding = db_get_dw(0, "CLCExt", "grp_padding", 0); } static RECT rcWindow = { 0 }; static void sttProcessResize(HWND hwnd, NMCLISTCONTROL *nmc) { RECT rcTree, rcWorkArea, rcOld; int maxHeight, newHeight; int winstyle, skinHeight = 0; if (disableautoupd) return; if (!db_get_b(0, "CLUI", "AutoSize", 0)) return; if (Docking_IsDocked(0, 0)) return; if (hFrameContactTree == 0) return; maxHeight = db_get_b(0, "CLUI", "MaxSizeHeight", 75); rcOld = rcWindow; GetWindowRect(hwnd, &rcWindow); GetWindowRect(g_clistApi.hwndContactTree, &rcTree); winstyle = GetWindowLongPtr(g_clistApi.hwndContactTree, GWL_STYLE); SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE); HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMon, &mi)) rcWorkArea = mi.rcWork; if (nmc->pt.y > (rcWorkArea.bottom - rcWorkArea.top)) { nmc->pt.y = (rcWorkArea.bottom - rcWorkArea.top); } if (winstyle & CLS_SKINNEDFRAME) { BOOL hasTitleBar = wndFrameCLC ? wndFrameCLC->TitleBar.ShowTitleBar : 0; StatusItems_t *item = arStatusItems[(hasTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB : ID_EXTBKOWNEDFRAMEBORDER) - ID_STATUS_OFFLINE]; skinHeight = item->IGNORED ? 0 : item->MARGIN_BOTTOM + item->MARGIN_TOP; } newHeight = max(nmc->pt.y, 3) + 1 + ((winstyle & WS_BORDER) ? 2 : 0) + skinHeight + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top); if (newHeight == (rcWindow.bottom - rcWindow.top) && show_on_first_autosize == FALSE) return; if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100) newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100; if (db_get_b(0, "CLUI", "AutoSizeUpward", 0)) { rcWindow.top = rcWindow.bottom - newHeight; if (rcWindow.top < rcWorkArea.top) rcWindow.top = rcWorkArea.top; } else { rcWindow.bottom = rcWindow.top + newHeight; if (rcWindow.bottom > rcWorkArea.bottom) rcWindow.bottom = rcWorkArea.bottom; } if (cfg::dat.szOldCTreeSize.cx != rcTree.right - rcTree.left) { cfg::dat.szOldCTreeSize.cx = rcTree.right - rcTree.left; return; } KillTimer(hwnd, TIMERID_AUTOSIZE); SetTimer(hwnd, TIMERID_AUTOSIZE, 100, nullptr); } int CustomDrawScrollBars(NMCSBCUSTOMDRAW *nmcsbcd) { switch (nmcsbcd->hdr.code) { case NM_COOLSB_CUSTOMDRAW: static HDC hdcScroll = nullptr; static HBITMAP hbmScroll, hbmScrollOld; static LONG scrollLeft, scrollRight, scrollHeight, scrollYmin, scrollYmax; switch (nmcsbcd->dwDrawStage) { case CDDS_PREPAINT: if (cfg::dat.bSkinnedScrollbar) // XXX fix (verify skin items to be complete, otherwise don't draw return CDRF_SKIPDEFAULT; return CDRF_DODEFAULT; case CDDS_POSTPAINT: return 0; case CDDS_ITEMPREPAINT: HDC hdc = nmcsbcd->hdc; StatusItems_t *item = nullptr, *arrowItem = nullptr; UINT uItemID = ID_EXTBKSCROLLBACK; HRGN rgn = nullptr; RECT rc; GetWindowRect(g_clistApi.hwndContactTree, &rc); POINT pt; pt.x = rc.left; pt.y = rc.top; ScreenToClient(g_clistApi.hwndContactList, &pt); hdcScroll = hdc; BitBlt(hdcScroll, nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right - nmcsbcd->rect.left, nmcsbcd->rect.bottom - nmcsbcd->rect.top, cfg::dat.hdcBg, pt.x + nmcsbcd->rect.left, pt.y + nmcsbcd->rect.top, SRCCOPY); switch (nmcsbcd->uItem) { case HTSCROLL_UP: case HTSCROLL_DOWN: uItemID = (nmcsbcd->uState == CDIS_DEFAULT || nmcsbcd->uState == CDIS_DISABLED) ? ID_EXTBKSCROLLBUTTON : (nmcsbcd->uState == CDIS_HOT ? ID_EXTBKSCROLLBUTTONHOVER : ID_EXTBKSCROLLBUTTONPRESSED); break; case HTSCROLL_PAGEGDOWN: case HTSCROLL_PAGEGUP: uItemID = nmcsbcd->uItem == HTSCROLL_PAGEGUP ? ID_EXTBKSCROLLBACK : ID_EXTBKSCROLLBACKLOWER; rgn = CreateRectRgn(nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right, nmcsbcd->rect.bottom); SelectClipRgn(hdcScroll, rgn); break; case HTSCROLL_THUMB: uItemID = nmcsbcd->uState == CDIS_SELECTED ? ID_EXTBKSCROLLTHUMBPRESSED : ID_EXTBKSCROLLTHUMB; break; default: break; } uItemID -= ID_STATUS_OFFLINE; item = arStatusItems[uItemID]; if (!item->IGNORED) { int alpha = nmcsbcd->uState == CDIS_DISABLED ? item->ALPHA - 50 : item->ALPHA; DrawAlpha(hdcScroll, &nmcsbcd->rect, item->COLOR, alpha, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); } uint32_t dfcFlags = DFCS_FLAT | (nmcsbcd->uState == CDIS_DISABLED ? DFCS_INACTIVE : (nmcsbcd->uState == CDIS_HOT ? DFCS_HOT : (nmcsbcd->uState == CDIS_SELECTED ? DFCS_PUSHED : 0))); if (nmcsbcd->uItem == HTSCROLL_UP) arrowItem = arStatusItems[ID_EXTBKSCROLLARROWUP - ID_STATUS_OFFLINE]; if (nmcsbcd->uItem == HTSCROLL_DOWN) arrowItem = arStatusItems[ID_EXTBKSCROLLARROWDOWN - ID_STATUS_OFFLINE]; if (arrowItem && !arrowItem->IGNORED) DrawAlpha(hdcScroll, &nmcsbcd->rect, arrowItem->COLOR, arrowItem->ALPHA, arrowItem->COLOR2, arrowItem->COLOR2_TRANSPARENT, arrowItem->GRADIENT, arrowItem->CORNER, arrowItem->BORDERSTYLE, arrowItem->imageItem); else if (arrowItem) DrawFrameControl(hdcScroll, &nmcsbcd->rect, DFC_SCROLL, (nmcsbcd->uItem == HTSCROLL_UP ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | dfcFlags); if (rgn) { SelectClipRgn(hdcScroll, nullptr); DeleteObject(rgn); } } } return 0; } static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact) { if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) { if (hContact == 0) return 0; if (item->dwFlags & BUTTON_PASSHCONTACTW) *wParam = hContact; else if (item->dwFlags & BUTTON_PASSHCONTACTL) *lParam = hContact; } return 1; } static void ShowCLUI(HWND hwnd) { int state = old_cliststate; int onTop = g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT); SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE); if (state == SETTING_STATE_NORMAL) { SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0); ShowWindow(g_clistApi.hwndContactList, SW_SHOWNORMAL); SendMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0); } else if (state == SETTING_STATE_MINIMIZED) { cfg::dat.forceResize = TRUE; ShowWindow(g_clistApi.hwndContactList, SW_HIDE); } else if (state == SETTING_STATE_HIDDEN) { cfg::dat.forceResize = TRUE; ShowWindow(g_clistApi.hwndContactList, SW_HIDE); } SetWindowPos(g_clistApi.hwndContactList, onTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING); DrawMenuBar(hwnd); if (cfg::dat.autosize) { SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0); SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); } } static void GetButtonRect(HWND hwnd, RECT *rc) { if (hwnd) GetWindowRect(hwnd, rc); else { POINT pt; GetCursorPos(&pt); rc->bottom = rc->top = pt.y; rc->left = rc->right = pt.x; } } LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { RECT rc; switch (msg) { case WM_CREATE: { int flags = WS_CHILD | CCS_BOTTOM; flags |= db_get_b(0, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0; flags |= db_get_b(0, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0; g_clistApi.hwndStatus = CreateWindow(STATUSCLASSNAME, nullptr, flags, 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr); if (flags & WS_VISIBLE) { ShowWindow(g_clistApi.hwndStatus, SW_SHOW); SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0); } mir_subclassWindow(g_clistApi.hwndStatus, NewStatusBarWndProc); SetClassLong(g_clistApi.hwndStatus, GCL_STYLE, GetClassLong(g_clistApi.hwndStatus, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW)); } g_oldSize.cx = g_oldSize.cy = 0; old_cliststate = g_plugin.getByte("State", SETTING_STATE_NORMAL); g_plugin.setByte("State", SETTING_STATE_HIDDEN); SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VISIBLE); SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | WS_CLIPCHILDREN); if (!cfg::dat.bFirstRun) ConfigureEventArea(); ConfigureCLUIGeometry(0); CluiProtocolStatusChanged(0, nullptr); for (int i = ID_STATUS_OFFLINE; i <= ID_STATUS_MAX; i++) statusNames[i - ID_STATUS_OFFLINE] = Clist_GetStatusModeDescription(i, 0); //delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded) if (cfg::dat.bLayeredHack) { SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | (WS_EX_LAYERED)); SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA); } if (cfg::dat.isTransparent) { SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); } transparentFocus = 1; TranslateMenu(GetMenu(hwnd)); PostMessage(hwnd, M_CREATECLC, 0, 0); return FALSE; case WM_NCCREATE: { LPCREATESTRUCT p = (LPCREATESTRUCT)lParam; p->style &= ~(CS_HREDRAW | CS_VREDRAW); } break; case M_CREATECLC: { if (db_get_b(0, "CLUI", "useskin", 0)) IMG_LoadItems(); CreateButtonBar(hwnd); SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE); { LONG style; uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_TOOLWINDOW); ShowWindow(g_clistApi.hwndContactList, SW_HIDE); style = GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE); if (windowStyle != SETTING_WINDOWSTYLE_DEFAULT) { style |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE; style &= ~WS_EX_APPWINDOW; } else { style &= ~(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE); if (g_plugin.getByte("AlwaysHideOnTB", 1)) style &= ~WS_EX_APPWINDOW; else style |= WS_EX_APPWINDOW; } SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, style); ApplyCLUIBorderStyle(); SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE); } if (cfg::dat.bSkinnedButtonMode) SetButtonToSkinned(); ConfigureFrame(); SetButtonStates(); CreateCLC(); cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0); if (cfg::dat.bFullTransparent) { if (g_CLUISkinnedBkColorRGB) Tweak_It(g_CLUISkinnedBkColorRGB); else if (cfg::dat.bClipBorder || (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME)) Tweak_It(RGB(255, 0, 255)); else Tweak_It(cfg::clcdat->bkColour); } g_plugin.setByte("State", old_cliststate); if (g_plugin.getByte("AutoApplyLastViewMode", 0)) { DBVARIANT dbv = { 0 }; if (!g_plugin.getString("LastViewMode", &dbv)) { if (mir_strlen(dbv.pszVal) > 2) { if (db_get_dw(0, CLVM_MODULE, dbv.pszVal, -1) != 0xffffffff) ApplyViewMode((char *)dbv.pszVal); } db_free(&dbv); } } if (!cfg::dat.autosize) ShowCLUI(hwnd); else { show_on_first_autosize = TRUE; RecalcScrollBar(g_clistApi.hwndContactTree, cfg::clcdat); } return 0; } case WM_ERASEBKGND: return TRUE; /* if (cfg::dat.bSkinnedButtonMode) return TRUE; return DefWindowProc(hwnd, msg, wParam, lParam); */ case WM_PAINT: { PAINTSTRUCT ps; RECT rcFrame, rcClient; HDC hdc; HRGN rgn = nullptr; HDC hdcReal = BeginPaint(hwnd, &ps); if (during_sizing) rcClient = rcWPC; else GetClientRect(hwnd, &rcClient); CopyRect(&rc, &rcClient); if (!cfg::dat.hdcBg || rc.right > cfg::dat.dcSize.cx || rc.bottom + cfg::dat.statusBarHeight > cfg::dat.dcSize.cy) { RECT rcWorkArea; SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE); HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMon, &mi)) rcWorkArea = mi.rcWork; cfg::dat.dcSize.cy = max(rc.bottom + cfg::dat.statusBarHeight, rcWorkArea.bottom - rcWorkArea.top); cfg::dat.dcSize.cx = max(rc.right, (rcWorkArea.right - rcWorkArea.left) / 2); if (cfg::dat.hdcBg) { SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld); DeleteObject(cfg::dat.hbmBg); DeleteDC(cfg::dat.hdcBg); } cfg::dat.hdcBg = CreateCompatibleDC(hdcReal); cfg::dat.hbmBg = CreateCompatibleBitmap(hdcReal, cfg::dat.dcSize.cx, cfg::dat.dcSize.cy); cfg::dat.hbmBgOld = reinterpret_cast(SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBg)); } if (cfg::shutDown) { EndPaint(hwnd, &ps); return 0; } hdc = cfg::dat.hdcBg; CopyRect(&rcFrame, &rcClient); if (g_CLUISkinnedBkColor) { if (cfg::dat.fOnDesktop) { HDC dc = GetDC(nullptr); RECT rcWin; GetWindowRect(hwnd, &rcWin); BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, dc, rcWin.left, rcWin.top, SRCCOPY); ReleaseDC(nullptr, dc); } else FillRect(hdc, &rcClient, g_CLUISkinnedBkColor); } if (cfg::dat.bClipBorder != 0 || cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) { int docked = Clist_IsDocked(); int clip = cfg::dat.bClipBorder; if (!g_CLUISkinnedBkColor) FillRect(hdc, &rcClient, cfg::dat.hBrushColorKey); if (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) rgn = CreateRoundRectRgn(clip, docked ? 0 : clip, rcClient.right - clip + 1, rcClient.bottom - (docked ? 0 : clip - 1), 8 + clip, 8 + clip); else rgn = CreateRectRgn(clip, docked ? 0 : clip, rcClient.right - clip, rcClient.bottom - (docked ? 0 : clip)); SelectClipRgn(hdc, rgn); } if (g_CLUIImageItem) { IMG_RenderImageItem(hdc, g_CLUIImageItem, &rcFrame); cfg::dat.ptW.x = cfg::dat.ptW.y = 0; ClientToScreen(hwnd, &cfg::dat.ptW); goto skipbg; } if (cfg::dat.bWallpaperMode) FillRect(hdc, &rcClient, cfg::dat.hBrushCLCBk); else FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE)); rcFrame.left += (cfg::dat.bCLeft - 1); rcFrame.right -= (cfg::dat.bCRight - 1); rcFrame.bottom++; rcFrame.bottom -= cfg::dat.statusBarHeight; rcFrame.top += (cfg::dat.topOffset - 1); if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) { if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) { InflateRect(&rcFrame, -1, -1); if (cfg::dat.bmpBackground) BlitWallpaper(hdc, &rcFrame, cfg::clcdat); cfg::dat.ptW.x = cfg::dat.ptW.y = 0; ClientToScreen(hwnd, &cfg::dat.ptW); } InflateRect(&rcFrame, 1, 1); if (cfg::dat.bSkinnedButtonMode) rcFrame.bottom -= (cfg::dat.bottomOffset); DrawEdge(hdc, &rcFrame, BDR_SUNKENOUTER, BF_RECT); } else if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) { if (cfg::dat.bmpBackground) BlitWallpaper(hdc, &rcFrame, cfg::clcdat); cfg::dat.ptW.x = cfg::dat.ptW.y = 0; ClientToScreen(hwnd, &cfg::dat.ptW); } skipbg: BitBlt(hdcReal, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdc, 0, 0, SRCCOPY); if (rgn) { SelectClipRgn(hdc, nullptr); DeleteObject(rgn); } EndPaint(hwnd, &ps); } return 0; case WM_ENTERSIZEMOVE: { POINT pt = { 0 }; GetWindowRect(hwnd, &g_PreSizeRect); GetClientRect(hwnd, &rc); ClientToScreen(hwnd, &pt); g_CLUI_x_off = pt.x - g_PreSizeRect.left; g_CLUI_y_off = pt.y - g_PreSizeRect.top; pt.x = rc.right; ClientToScreen(hwnd, &pt); g_CLUI_x1_off = g_PreSizeRect.right - pt.x; pt.x = 0; pt.y = rc.bottom; ClientToScreen(hwnd, &pt); g_CLUI_y1_off = g_PreSizeRect.bottom - pt.y; } break; case WM_EXITSIZEMOVE: PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0); break; case WM_SIZING: break; case WM_WINDOWPOSCHANGED: if (Docking_IsDocked(0, 0)) break; case WM_WINDOWPOSCHANGING: if (g_clistApi.hwndContactList != nullptr) { WINDOWPOS *wp = (WINDOWPOS *)lParam; if (!wp || (wp->flags & SWP_NOSIZE)) return FALSE; RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); during_sizing = true; new_window_rect.left = 0; new_window_rect.right = wp->cx - (g_CLUI_x_off + g_CLUI_x1_off); new_window_rect.top = 0; new_window_rect.bottom = wp->cy - g_CLUI_y_off - g_CLUI_y1_off; if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) { RECT rcStatus; SetWindowPos(g_clistApi.hwndStatus, nullptr, 0, new_window_rect.bottom - 20, new_window_rect.right, 20, SWP_NOZORDER); GetWindowRect(g_clistApi.hwndStatus, &rcStatus); cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top); if (wp->cx != g_oldSize.cx) SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0); RedrawWindow(g_clistApi.hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); } else cfg::dat.statusBarHeight = 0; SizeFramesByWindowRect(&new_window_rect); dock_prevent_moving = 0; LayoutButtons(hwnd, &new_window_rect); dock_prevent_moving = 1; g_oldPos.x = wp->x; g_oldPos.y = wp->y; g_oldSize.cx = wp->cx; g_oldSize.cy = wp->cy; rcWPC = new_window_rect; } during_sizing = false; return 0; case WM_SIZE: if ((wParam == 0 && lParam == 0) || Docking_IsDocked(0, 0)) { if (IsZoomed(hwnd)) ShowWindow(hwnd, SW_SHOWNORMAL); if (g_clistApi.hwndContactList != nullptr) { SendMessage(hwnd, WM_ENTERSIZEMOVE, 0, 0); GetWindowRect(hwnd, &rc); WINDOWPOS wp = {}; wp.cx = rc.right - rc.left; wp.cy = rc.bottom - rc.top; wp.x = rc.left; wp.y = rc.top; wp.flags = 0; SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); SendMessage(hwnd, WM_EXITSIZEMOVE, 0, 0); } } case WM_MOVE: if (!IsIconic(hwnd)) { GetWindowRect(hwnd, &rc); if (!Docking_IsDocked(0, 0)) { cluiPos.bottom = (uint32_t)(rc.bottom - rc.top); cluiPos.left = rc.left; cluiPos.top = rc.top; } cluiPos.right = rc.right - rc.left; if (cfg::dat.realTimeSaving) { GetWindowRect(hwnd, &rc); // if docked, dont remember pos (except for width) if (!Clist_IsDocked()) { g_plugin.setDword("Height", (uint32_t)(rc.bottom - rc.top)); g_plugin.setDword("x", (uint32_t)rc.left); g_plugin.setDword("y", (uint32_t)rc.top); } g_plugin.setDword("Width", (uint32_t)(rc.right - rc.left)); } } return TRUE; case WM_SETFOCUS: SetFocus(g_clistApi.hwndContactTree); return 0; case CLUIINTM_REMOVEFROMTASKBAR: { uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT); if (windowStyle == SETTING_WINDOWSTYLE_DEFAULT && g_plugin.getByte("AlwaysHideOnTB", 0)) RemoveFromTaskBar(hwnd); return 0; } case WM_ACTIVATE: if (g_fading_active) { if (wParam != WA_INACTIVE && cfg::dat.isTransparent) transparentFocus = 1; return DefWindowProc(hwnd, msg, wParam, lParam); } if (wParam == WA_INACTIVE) { if ((HWND)wParam != hwnd) if (cfg::dat.isTransparent) if (transparentFocus) SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr); } else { if (cfg::dat.isTransparent) { KillTimer(hwnd, TM_AUTOALPHA); SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); transparentFocus = 1; } SetWindowPos(g_clistApi.hwndContactList, g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING); } PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0); return DefWindowProc(hwnd, msg, wParam, lParam); case WM_SETCURSOR: if (cfg::dat.isTransparent) { if (!transparentFocus && GetForegroundWindow() != hwnd) { SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); transparentFocus = 1; SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr); } } return DefWindowProc(hwnd, msg, wParam, lParam); case WM_NCHITTEST: { LRESULT result; RECT r; POINT pt; int clip = cfg::dat.bClipBorder; GetWindowRect(hwnd, &r); GetCursorPos(&pt); if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6 && !db_get_b(0, "CLUI", "AutoSize", 0)) { if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) return HTBOTTOM; if (pt.x < r.left + clip + 10) return HTBOTTOMLEFT; if (pt.x > r.right - clip - 10) return HTBOTTOMRIGHT; } else if (pt.y >= r.top && pt.y <= r.top + 3 && !db_get_b(0, "CLUI", "AutoSize", 0)) { if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10) return HTTOP; if (pt.x < r.left + clip + 10) return HTTOPLEFT; if (pt.x > r.right - clip - 10) return HTTOPRIGHT; } else if (pt.x >= r.left && pt.x <= r.left + clip + 6) return HTLEFT; else if (pt.x >= r.right - clip - 6 && pt.x <= r.right) return HTRIGHT; result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam); if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT || result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT) if (cfg::dat.autosize) return HTCLIENT; return result; } case WM_TIMER: if (wParam == TM_AUTOALPHA) { int inwnd; if (GetForegroundWindow() == hwnd) { KillTimer(hwnd, TM_AUTOALPHA); inwnd = 1; } else { POINT pt; HWND hwndPt; pt.x = (short)LOWORD(GetMessagePos()); pt.y = (short)HIWORD(GetMessagePos()); hwndPt = WindowFromPoint(pt); inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd); } if (inwnd != transparentFocus) { //change transparentFocus = inwnd; if (transparentFocus) SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); else SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.autoalpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); } if (!transparentFocus) KillTimer(hwnd, TM_AUTOALPHA); } else if (wParam == TIMERID_AUTOSIZE) { KillTimer(hwnd, wParam); SetWindowPos(hwnd, nullptr, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING); PostMessage(hwnd, WM_SIZE, 0, 0); PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0); } return TRUE; case WM_SHOWWINDOW: { static int noRecurse = 0; uint32_t thisTick, startTick; int sourceAlpha, destAlpha; if (cfg::dat.forceResize && wParam != SW_HIDE) { cfg::dat.forceResize = FALSE; SendMessage(hwnd, WM_SIZE, 0, 0); PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0); } PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0); if (lParam) return DefWindowProc(hwnd, msg, wParam, lParam); if (noRecurse) return DefWindowProc(hwnd, msg, wParam, lParam); if (!cfg::dat.fadeinout) return DefWindowProc(hwnd, msg, wParam, lParam); g_fading_active = 1; if (wParam) { sourceAlpha = 0; destAlpha = cfg::dat.isTransparent ? cfg::dat.alpha : 255; SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? (COLORREF)cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)sourceAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); noRecurse = 1; ShowWindow(hwnd, SW_SHOW); RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); noRecurse = 0; } else { sourceAlpha = cfg::dat.isTransparent ? (transparentFocus ? cfg::dat.alpha : cfg::dat.autoalpha) : 255; destAlpha = 0; } for (startTick = GetTickCount();;) { thisTick = GetTickCount(); if (thisTick >= startTick + 200) { SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)destAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); g_fading_active = 0; return DefWindowProc(hwnd, msg, wParam, lParam); } SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)(sourceAlpha + (destAlpha - sourceAlpha) * (int)(thisTick - startTick) / 200), LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0)); } } case WM_SYSCOMMAND: { uint8_t bWindowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT); if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle) { if (wParam == SC_RESTORE) { CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); SendMessage(hwnd, WM_SIZE, 0, 0); SendMessage(hwnd, CLUIINTM_REDRAW, 0, 0); SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0); g_plugin.setByte("State", SETTING_STATE_NORMAL); break; } } if (wParam == SC_MAXIMIZE) return 0; if (wParam == SC_MINIMIZE) { if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle && !g_plugin.getByte("AlwaysHideOnTB", 0)) { g_plugin.setByte("State", SETTING_STATE_MINIMIZED); break; } g_clistApi.pfnShowHide(); return 0; } if (wParam == SC_RESTORE) { g_clistApi.pfnShowHide(); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); case WM_COMMAND: { uint32_t dwOldFlags = cfg::dat.dwFlags; if (HIWORD(wParam) == BN_CLICKED && lParam != 0) { if (LOWORD(wParam) == IDC_TBFIRSTUID - 1) break; else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling ButtonItem *item = g_ButtonItems; WPARAM wwParam = 0; LPARAM llParam = 0; MCONTACT hContact = 0; ClcContact *contact = nullptr; int serviceFailure = FALSE; if (cfg::clcdat) { g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr); if (contact && contact->type == CLCIT_CONTACT) hContact = contact->hContact; } while (item) { if (item->uId == (uint32_t)LOWORD(wParam)) { int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact); if (item->dwFlags & BUTTON_ISSERVICE) { if (ServiceExists(item->szService) && contactOK) CallService(item->szService, wwParam, llParam); else if (contactOK) serviceFailure = TRUE; } else if (item->dwFlags & BUTTON_ISPROTOSERVICE && cfg::clcdat) { if (contactOK) { char *szProto = Proto_GetBaseAccountName(hContact); if (ProtoServiceExists(szProto, item->szService)) CallProtoService(szProto, item->szService, wwParam, llParam); else serviceFailure = TRUE; } } else if (item->dwFlags & BUTTON_ISDBACTION) { uint8_t *pValue; char *szModule = item->szModule; char *szSetting = item->szSetting; MCONTACT finalhContact = 0; if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) { contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact); if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION) szModule = Proto_GetBaseAccountName(hContact); finalhContact = hContact; } else contactOK = 1; if (contactOK) { BOOL fDelete = FALSE; if (item->dwFlags & BUTTON_ISTOGGLE) { BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED); pValue = fChecked ? item->bValueRelease : item->bValuePush; if (fChecked && pValue[0] == 0) fDelete = TRUE; } else pValue = item->bValuePush; if (fDelete) db_unset(finalhContact, szModule, szSetting); else { switch (item->type) { case DBVT_BYTE: db_set_b(finalhContact, szModule, szSetting, pValue[0]); break; case DBVT_WORD: db_set_w(finalhContact, szModule, szSetting, *((uint16_t *)&pValue[0])); break; case DBVT_DWORD: db_set_dw(finalhContact, szModule, szSetting, *((uint32_t *)&pValue[0])); break; case DBVT_ASCIIZ: db_set_s(finalhContact, szModule, szSetting, (char *)pValue); break; } } } else if (item->dwFlags & BUTTON_ISTOGGLE) SendMessage(item->hWnd, BM_SETCHECK, 0, 0); } if (!contactOK) MessageBox(nullptr, TranslateT("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat."), TranslateT("Parameter mismatch"), MB_OK); if (serviceFailure) { wchar_t szError[512]; mir_snwprintf(szError, TranslateT("The service %S specified by the %S button definition was not found. You may need to install additional plugins."), item->szService, item->szName); MessageBox(nullptr, szError, TranslateT("Service failure"), MB_OK); } break; } item = item->nextItem; } goto buttons_done; } switch (LOWORD(wParam)) { case IDC_TBMENU: case IDC_TBTOPMENU: case IDC_STBTOPMENU: GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc); TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBMENU ? rc.top : rc.bottom, 0, hwnd, nullptr); return 0; case IDC_TBTOPSTATUS: case IDC_STBTOPSTATUS: case IDC_TBGLOBALSTATUS: GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc); TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBGLOBALSTATUS ? rc.top : rc.bottom, 0, hwnd, nullptr); return 0; case IDC_TBSOUND: case IDC_STBSOUND: cfg::dat.soundsOff = !cfg::dat.soundsOff; db_set_b(0, "CLUI", "NoSounds", (uint8_t)cfg::dat.soundsOff); db_set_b(0, "Skin", "UseSound", (uint8_t)(cfg::dat.soundsOff ? 0 : 1)); return 0; case IDC_TBSELECTVIEWMODE: case IDC_STBSELECTVIEWMODE: SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_SELECTMODE, lParam); break; case IDC_TBCLEARVIEWMODE: case IDC_STBCLEARVIEWMODE: SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_RESETMODES, lParam); break; case IDC_TBCONFIGUREVIEWMODE: case IDC_STBCONFIGUREVIEWMODE: SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_CONFIGUREMODES, lParam); break; case IDC_TBFINDANDADD: case IDC_STBFINDANDADD: CallService(MS_FINDADD_FINDADD, 0, 0); return 0; case IDC_TBACCOUNTS: case IDC_STBACCOUNTS: CallService(MS_PROTO_SHOWACCMGR, 0, 0); break; case IDC_TBOPTIONS: case IDC_STBOPTIONS: CallService("Options/OptionsCommand", 0, 0); return 0; } } else if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_MAINMENU, NULL)) return 0; buttons_done: switch (LOWORD(wParam)) { case ID_TRAY_EXIT: cfg::shutDown = 1; if (Miranda_OkToExit()) DestroyWindow(hwnd); break; case ID_TRAY_HIDE: case IDC_TBMINIMIZE: case IDC_STBMINIMIZE: g_clistApi.pfnShowHide(); break; case POPUP_NEWGROUP: SendMessage(g_clistApi.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0); SendMessage(g_clistApi.hwndContactTree, CLM_SETUSEGROUPS, 1, 0); Clist_GroupCreate(NULL, nullptr); break; case POPUP_HIDEOFFLINE: case IDC_TBHIDEOFFLINE: case IDC_STBHIDEOFFLINE: g_clistApi.pfnSetHideOffline(-1); break; case POPUP_HIDEOFFLINEROOT: CallService(MS_CLIST_TOGGLEHIDEOFFLINEROOT, 0, 0); break; case POPUP_HIDEEMPTYGROUPS: CallService(MS_CLIST_TOGGLEEMPTYGROUPS, 0, 0); break; case IDC_TBHIDEGROUPS: case IDC_STBHIDEGROUPS: case POPUP_DISABLEGROUPS: ClcSetButtonState(IDC_TBHIDEGROUPS, CallService(MS_CLIST_TOGGLEGROUPS, 0, 0)); SetButtonStates(); break; case POPUP_HIDEMIRANDA: g_clistApi.pfnShowHide(); break; case POPUP_SHOWMETAICONS: cfg::dat.dwFlags ^= CLUI_USEMETAICONS; Clist_InitAutoRebuild(g_clistApi.hwndContactTree); break; case POPUP_FRAME: cfg::dat.dwFlags ^= CLUI_FRAME_CLISTSUNKEN; break; case POPUP_BUTTONS: cfg::dat.dwFlags ^= CLUI_FRAME_SHOWBOTTOMBUTTONS; break; case POPUP_SHOWSTATUSICONS: cfg::dat.dwFlags ^= CLUI_FRAME_STATUSICONS; break; } if (dwOldFlags != cfg::dat.dwFlags) { InvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE); db_set_dw(0, "CLUI", "Frameflags", cfg::dat.dwFlags); if ((dwOldFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN)) != (cfg::dat.dwFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN))) { ConfigureFrame(); ConfigureCLUIGeometry(1); } ConfigureEventArea(); PostMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0); PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0); } } return FALSE; case WM_LBUTTONDOWN: if (g_ButtonItems) { POINT pt; GetCursorPos(&pt); return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); } break; case WM_DISPLAYCHANGE: SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged break; case WM_NOTIFY: if (((LPNMHDR)lParam)->hwndFrom == g_clistApi.hwndContactTree) { switch (((LPNMHDR)lParam)->code) { case CLN_LISTSIZECHANGE: sttProcessResize(hwnd, (NMCLISTCONTROL *)lParam); return FALSE; case NM_CLICK: { NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam; uint32_t hitFlags; SendMessage(g_clistApi.hwndContactTree, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y)); if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0) break; if (db_get_b(0, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) { POINT pt; pt = nm->pt; ClientToScreen(g_clistApi.hwndContactTree, &pt); return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y)); } } return FALSE; } } break; case WM_DRAWITEM: { LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; if (hbmLockedPoint == nullptr) { hdcLockedPoint = CreateCompatibleDC(dis->hDC); hbmLockedPoint = CreateCompatibleBitmap(dis->hDC, 5, 5); hbmOldLockedPoint = reinterpret_cast(SelectObject(hdcLockedPoint, hbmLockedPoint)); } if (dis->hwndItem == g_clistApi.hwndStatus) { ProtocolData *pd = (ProtocolData *)dis->itemData; if (IsBadCodePtr((FARPROC)pd)) return TRUE; if (cfg::shutDown) return TRUE; char *szProto = pd->RealName; PROTOACCOUNT *pa = Proto_GetAccount(szProto); if (pa == nullptr) return TRUE; int nParts = SendMessage(g_clistApi.hwndStatus, SB_GETPARTS, 0, 0); SIZE textSize; uint8_t showOpts = db_get_b(0, "CLUI", "SBarShow", 1); SetBkMode(dis->hDC, TRANSPARENT); int x = dis->rcItem.left; if (showOpts & 1) { HICON hIcon; if (pa->iRealStatus >= ID_STATUS_CONNECTING && pa->iRealStatus < ID_STATUS_OFFLINE) { char szBuffer[128]; mir_snprintf(szBuffer, "%s_conn", pd->RealName); hIcon = IcoLib_GetIcon(szBuffer); } else if (cfg::dat.bShowXStatusOnSbar && pa->iRealStatus > ID_STATUS_OFFLINE) { int xStatus; CUSTOM_STATUS cst = { sizeof(cst) }; cst.flags = CSSF_MASK_STATUS; cst.status = &xStatus; if (ProtoServiceExists(pd->RealName, PS_GETCUSTOMSTATUSEX) && !CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cst) && xStatus > 0) hIcon = (HICON)CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); // get OWN xStatus icon (if set) else hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus); } else hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus); if (!(showOpts & 6) && cfg::dat.bEqualSections) x = (dis->rcItem.left + dis->rcItem.right - 16) >> 1; if (pd->protopos == 0) x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft); else if (pd->protopos == nParts - 1) x -= (cfg::dat.bCRight / 2); DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL); IcoLib_ReleaseIcon(hIcon); if (db_get_b(0, "CLUI", "sbar_showlocked", 1)) { if (pa->bIsLocked) { hIcon = Skin_LoadIcon(SKINICON_OTHER_STATUS_LOCKED); if (hIcon != nullptr) { DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL); IcoLib_ReleaseIcon(hIcon); } } } x += 18; } else { x += 2; if (pd->protopos == 0) x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft); else if (pd->protopos == nParts - 1) x -= (cfg::dat.bCRight / 2); } if (showOpts & 2) { wchar_t szName[64]; wcsncpy_s(szName, pa->tszAccountName, _TRUNCATE); if (mir_wstrlen(szName) < _countof(szName) - 1) mir_wstrcat(szName, L" "); GetTextExtentPoint32(dis->hDC, szName, (int)mir_wstrlen(szName), &textSize); TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szName, (int)mir_wstrlen(szName)); x += textSize.cx; } if (showOpts & 4) { wchar_t *szStatus = Clist_GetStatusModeDescription(pa->iRealStatus, 0); GetTextExtentPoint32(dis->hDC, szStatus, (int)mir_wstrlen(szStatus), &textSize); TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szStatus, (int)mir_wstrlen(szStatus)); } } else if (dis->CtlType == ODT_MENU) { if (dis->itemData == MENU_MIRANDAMENU) break; return Menu_DrawItem(lParam); } } return 0; case WM_CLOSE: if (SETTING_WINDOWSTYLE_DEFAULT == db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT) && !g_plugin.getByte("AlwaysHideOnTB", 0)) { PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); return 0; } g_clistApi.pfnShowHide(); return 0; case CLUIINTM_REDRAW: if (show_on_first_autosize) { show_on_first_autosize = FALSE; ShowCLUI(hwnd); } RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN); return 0; case CLUIINTM_STATUSBARUPDATE: CluiProtocolStatusChanged(0, nullptr); return 0; case WM_THEMECHANGED: API::updateState(); break; case WM_DESTROY: if (cfg::dat.hdcBg) { SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld); DeleteObject(cfg::dat.hbmBg); DeleteDC(cfg::dat.hdcBg); cfg::dat.hdcBg = nullptr; } if (cfg::dat.bmpBackground) { SelectObject(cfg::dat.hdcPic, cfg::dat.hbmPicOld); DeleteDC(cfg::dat.hdcPic); DeleteObject(cfg::dat.bmpBackground); cfg::dat.bmpBackground = nullptr; } FreeProtocolData(); if (hdcLockedPoint) { SelectObject(hdcLockedPoint, hbmOldLockedPoint); DeleteObject(hbmLockedPoint); DeleteDC(hdcLockedPoint); } // if this has not yet been set, do it now. // indicates that clist is shutting down and prevents various things // from happening at shutdown. if (!cfg::shutDown) cfg::shutDown = 1; CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)hFrameContactTree, 0); break; } return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam); } #ifndef CS_DROPSHADOW #define CS_DROPSHADOW 0x00020000 #endif static int MetaChanged(WPARAM wParam, LPARAM lParam) { Clist_Broadcast(INTM_METACHANGEDEVENT, wParam, lParam); return 0; } static INT_PTR CLN_ShowMainMenu(WPARAM, LPARAM) { POINT pt; GetCursorPos(&pt); TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr); return 0; } static INT_PTR CLN_ShowStatusMenu(WPARAM, LPARAM) { POINT pt; GetCursorPos(&pt); TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr); return 0; } #define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu" #define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu" void LoadCLUIModule(void) { HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded); WNDCLASS wndclass; wndclass.style = 0; wndclass.lpfnWndProc = EventAreaWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = g_plugin.getInst(); wndclass.hIcon = nullptr; wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)COLOR_3DFACE; wndclass.lpszMenuName = nullptr; wndclass.lpszClassName = L"EventAreaClass"; RegisterClass(&wndclass); oldhideoffline = Clist::HideOffline; cluiPos.left = g_plugin.getDword("x", 600); cluiPos.top = g_plugin.getDword("y", 200); cluiPos.right = g_plugin.getDword("Width", 150); cluiPos.bottom = g_plugin.getDword("Height", 350); LoadExtraIconModule(); LoadCLUIFramesModule(); CreateServiceFunction(MS_CLUI_SHOWMAINMENU, CLN_ShowMainMenu); CreateServiceFunction(MS_CLUI_SHOWSTATUSMENU, CLN_ShowStatusMenu); if (db_get_b(0, "CLUI", "FloaterMode", 0)) { MessageBox(nullptr, TranslateT("You need the FloatingContacts plugin, cause the embedded floating contacts were removed."), TranslateT("Warning"), MB_OK | MB_ICONWARNING); db_unset(0, "CLUI", "FloaterMode"); } MF_InitCheck(); } void OnCreateClc() { HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged); HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaChanged); InitGroupMenus(); LoadExtBkSettingsFromDB(); PreCreateCLC(g_clistApi.hwndContactList); } struct { const wchar_t *tszName; int iMask; } static clistFontDescr[] = { { LPGENW("Standard contacts"), FIDF_CLASSGENERAL }, { LPGENW("Online contacts to whom you have a different visibility"), FIDF_CLASSGENERAL }, { LPGENW("Offline contacts"), FIDF_CLASSGENERAL }, { LPGENW("Contacts which are 'not on list'"), FIDF_CLASSGENERAL }, { LPGENW("Groups"), FIDF_CLASSHEADER }, { LPGENW("Group member counts"), FIDF_CLASSHEADER }, { LPGENW("Dividers"), FIDF_CLASSSMALL }, { LPGENW("Offline contacts to whom you have a different visibility"), FIDF_CLASSGENERAL }, { LPGENW("Status mode"), FIDF_CLASSGENERAL }, { LPGENW("Frame titles"), FIDF_CLASSGENERAL }, { LPGENW("Event area"), FIDF_CLASSGENERAL }, { LPGENW("Contact list local time"), FIDF_CLASSGENERAL } }; void FS_RegisterFonts() { FontIDW fid = {}; wcsncpy_s(fid.group, LPGENW("Contact list"), _TRUNCATE); strncpy_s(fid.dbSettingsGroup, "CLC", _TRUNCATE); fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS | FIDF_APPENDNAME | FIDF_SAVEPOINTSIZE; HDC hdc = GetDC(nullptr); for (int i = 0; i < _countof(clistFontDescr); i++) { LOGFONT lf; Clist_GetFontSetting(i, &lf, &fid.deffontsettings.colour); lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72); wcsncpy_s(fid.deffontsettings.szFace, lf.lfFaceName, _TRUNCATE); fid.deffontsettings.charset = lf.lfCharSet; fid.deffontsettings.size = (char)lf.lfHeight; fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0); fid.flags &= ~FIDF_CLASSMASK; fid.flags |= clistFontDescr[i].iMask; wcsncpy_s(fid.name, clistFontDescr[i].tszName, _TRUNCATE); char idstr[10]; mir_snprintf(idstr, "Font%d", i); strncpy_s(fid.setting, idstr, _TRUNCATE); fid.order = i; g_plugin.addFont(&fid); } ReleaseDC(nullptr, hdc); // and colours ColourIDW colourid = {}; colourid.order = 0; strncpy_s(colourid.dbSettingsGroup, "CLC", _TRUNCATE); strncpy_s(colourid.setting, "BkColour", _TRUNCATE); wcsncpy_s(colourid.name, LPGENW("Background"), _TRUNCATE); wcsncpy_s(colourid.group, LPGENW("Contact list"), _TRUNCATE); colourid.defcolour = CLCDEFAULT_BKCOLOUR; g_plugin.addColor(&colourid); strncpy_s(colourid.setting, "SelTextColour", _TRUNCATE); wcsncpy_s(colourid.name, LPGENW("Selected text"), _TRUNCATE); colourid.order = 1; colourid.defcolour = CLCDEFAULT_SELTEXTCOLOUR; g_plugin.addColor(&colourid); strncpy_s(colourid.setting, "HotTextColour", _TRUNCATE); wcsncpy_s(colourid.name, LPGENW("Hottrack text"), _TRUNCATE); colourid.order = 1; colourid.defcolour = CLCDEFAULT_HOTTEXTCOLOUR; g_plugin.addColor(&colourid); strncpy_s(colourid.setting, "QuickSearchColour", _TRUNCATE); wcsncpy_s(colourid.name, LPGENW("Quicksearch text"), _TRUNCATE); colourid.order = 1; colourid.defcolour = CLCDEFAULT_QUICKSEARCHCOLOUR; g_plugin.addColor(&colourid); strncpy_s(colourid.dbSettingsGroup, "CLUI", _TRUNCATE); strncpy_s(colourid.setting, "clr_frameborder", _TRUNCATE); wcsncpy_s(colourid.name, LPGENW("Embedded frames border"), _TRUNCATE); colourid.order = 1; colourid.defcolour = RGB(40, 40, 40); g_plugin.addColor(&colourid); }