/* Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org) 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 version 2 of the License. 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, see . */ #include "stdafx.h" #define IDC_ICO 12344 #define IDC_ENTER 2000 // Pseudo control to handle enter in the main window HIMAGELIST hIml; long main_dialog_open = 0; HWND hwndMain = nullptr; // array where the contacts are put into struct c_struct { wchar_t szname[120]; wchar_t szgroup[50]; MCONTACT hcontact; wchar_t proto[20]; c_struct() { szname[0] = 0; szgroup[0] = 0; hcontact = 0; proto[0] = 0; } }; LIST contacts(200); long max_proto_width; // Get the name the contact has in list // This was not made to be called by more than one thread! wchar_t tmp_list_name[120]; wchar_t *GetListName(c_struct *cs) { if (opts.group_append && cs->szgroup[0] != '\0') { mir_snwprintf(tmp_list_name, L"%s (%s)", cs->szname, cs->szgroup); return tmp_list_name; } else { return cs->szname; } } int lstreq(wchar_t *a, wchar_t *b, size_t len = -1) { a = CharLower(wcsdup(a)); b = CharLower(wcsdup(b)); int ret; if (len > 0) ret = wcsncmp(a, b, len); else ret = mir_wstrcmp(a, b); free(a); free(b); return ret; } // simple sorting function to have // the contact array in alphabetical order void SortArray(void) { int loop, doop; c_struct *cs_temp; SortedList *sl = (SortedList *)&contacts; for (loop = 0; loop < contacts.getCount(); loop++) { for (doop = loop + 1; doop < contacts.getCount(); doop++) { int cmp = lstreq(contacts[loop]->szname, contacts[doop]->szname); if (cmp > 0) { cs_temp = contacts[loop]; sl->items[loop] = contacts[doop]; sl->items[doop] = cs_temp; } else if (cmp == 0) { if (lstreq(contacts[loop]->proto, contacts[doop]->proto) > 0) { cs_temp = contacts[loop]; sl->items[loop] = contacts[doop]; sl->items[doop] = cs_temp; } } } } } int GetStatus(MCONTACT hContact, char *proto = nullptr) { if (proto == nullptr) proto = Proto_GetBaseAccountName(hContact); if (proto == nullptr) return ID_STATUS_OFFLINE; return db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE); } void FreeContacts() { for (auto &it : contacts) delete it; contacts.destroy(); } void LoadContacts(HWND hwndDlg, BOOL show_all) { BOOL metacontactsEnabled = db_mc_isEnabled(); // Read last-sent-to contact from db and set handle as window-userdata HANDLE hlastsent = (HANDLE)g_plugin.getDword("LastSentTo", -1); SetWindowLongPtr(hwndMain, GWLP_USERDATA, (LONG_PTR)hlastsent); // enumerate all contacts and write them to the array // item data of listbox-strings is the array position FreeContacts(); for (auto &hContact : Contacts()) { char *pszProto = Proto_GetBaseAccountName(hContact); if (pszProto == nullptr) continue; // Get meta MCONTACT hMeta = NULL; if (metacontactsEnabled) { if ((!show_all && opts.hide_subcontacts) || opts.group_append) hMeta = db_mc_getMeta(hContact); } else if (!mir_strcmp(META_PROTO, pszProto)) continue; if (!show_all) { // Check if is offline and have to show if (GetStatus(hContact, pszProto) <= ID_STATUS_OFFLINE) { // See if has to show char setting[128]; mir_snprintf(setting, "ShowOffline%s", pszProto); if (!g_plugin.getByte(setting, FALSE)) continue; // Check if proto offline else if (opts.hide_from_offline_proto && Proto_GetStatus(pszProto) <= ID_STATUS_OFFLINE) continue; } // Check if is subcontact if (opts.hide_subcontacts && hMeta != NULL) { if (!opts.keep_subcontacts_from_offline) continue; if (GetStatus(hMeta, META_PROTO) > ID_STATUS_OFFLINE) continue; char setting[128]; mir_snprintf(setting, "ShowOffline%s", META_PROTO); if (g_plugin.getByte(setting, FALSE)) continue; } } // Add to list // Get group c_struct *contact = new c_struct(); if (opts.group_append) { ptrW wszGroup(Clist_GetGroup(hMeta == NULL ? hContact : hMeta)); if (wszGroup) wcsncpy_s(contact->szgroup, wszGroup, _TRUNCATE); } // Make contact name wchar_t *tmp = (wchar_t *)Clist_GetContactDisplayName(hContact); mir_wstrncpy(contact->szname, tmp, _countof(contact->szname)); PROTOACCOUNT *acc = Proto_GetAccount(pszProto); if (acc != nullptr) mir_wstrncpy(contact->proto, acc->tszAccountName, _countof(contact->proto)); contact->hcontact = hContact; contacts.insert(contact); } SortArray(); SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_RESETCONTENT, 0, 0); for (int loop = 0; loop < contacts.getCount(); loop++) SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_ADDSTRING, 0, (LPARAM)GetListName(contacts[loop])), loop); } // Enable buttons for the selected contact void EnableButtons(HWND hwndDlg, MCONTACT hContact) { if (hContact == NULL) { EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), FALSE); SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, 0, 0); } else { // Is a meta? MCONTACT hSub = db_mc_getMostOnline(hContact); if (hSub != NULL) hContact = hSub; // Get caps INT_PTR caps = 0; char *pszProto = Proto_GetBaseAccountName(hContact); if (pszProto != nullptr) caps = CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0); EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), caps & PF1_IMSEND ? TRUE : FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), caps & PF1_FILESEND ? TRUE : FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), TRUE); EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), TRUE); EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), TRUE); HICON ico = ImageList_GetIcon(hIml, Clist_GetContactIcon(hContact), ILD_IMAGE); SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, (WPARAM)ico, 0); } } // check if the char(s) entered appears in a contacts name int CheckText(HWND hdlg, wchar_t *sztext, BOOL only_enable = FALSE) { EnableButtons(hwndMain, NULL); if (sztext == nullptr || sztext[0] == '\0') return 0; size_t len = mir_wstrlen(sztext); if (only_enable) { for (auto &it : contacts) { if (lstreq(sztext, it->szname) == 0 || lstreq(sztext, GetListName(it)) == 0) { EnableButtons(hwndMain, it->hcontact); return 0; } } } else { for (auto &it : contacts) { if (lstreq(sztext, GetListName(it), len) == 0) { SetWindowText(hdlg, GetListName(it)); SendMessage(hdlg, EM_SETSEL, (WPARAM)len, (LPARAM)-1); EnableButtons(hwndMain, it->hcontact); return 0; } } } EnableButtons(hwndMain, NULL); return 0; } MCONTACT GetSelectedContact(HWND hwndDlg) { // First try selection int sel = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0); if (sel != CB_ERR) { int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETITEMDATA, sel, 0); if (pos != CB_ERR) return contacts[pos]->hcontact; } // Now try the name wchar_t cname[120] = L""; GetDlgItemText(hwndDlg, IDC_USERNAME, cname, _countof(cname)); for (auto &it : contacts) if (!mir_wstrcmpi(cname, GetListName(it))) return it->hcontact; return NULL; } // get array position from handle int GetItemPos(MCONTACT hcontact) { for (auto &it : contacts) if (hcontact == it->hcontact) return contacts.indexOf(&it); return -1; } // callback function for edit-box of the listbox // without this the autofill function isn't possible // this was done like ie does it..as far as spy++ could tell ;) LRESULT CALLBACK EditProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_CHAR: { if (wparam < 32 && wparam != VK_BACK) break; wchar_t sztext[120] = L""; uint32_t start; uint32_t end; int ret = SendMessage(hdlg, EM_GETSEL, (WPARAM)&start, (LPARAM)&end); GetWindowText(hdlg, sztext, _countof(sztext)); BOOL at_end = (mir_wstrlen(sztext) == (int)end); if (ret != -1) { if (wparam == VK_BACK) { if (start > 0) SendMessage(hdlg, EM_SETSEL, (WPARAM)start - 1, (LPARAM)end); sztext[0] = 0; } else { sztext[0] = wparam; sztext[1] = 0; } SendMessage(hdlg, EM_REPLACESEL, 0, (LPARAM)sztext); GetWindowText(hdlg, sztext, _countof(sztext)); } CheckText(hdlg, sztext, !at_end); return 1; } case WM_KEYUP: { wchar_t sztext[120] = L""; if (wparam == VK_RETURN) { switch (SendMessage(GetParent(hdlg), CB_GETDROPPEDSTATE, 0, 0)) { case FALSE: SendMessage(GetParent(GetParent(hdlg)), WM_COMMAND, MAKEWPARAM(IDC_ENTER, STN_CLICKED), 0); break; case TRUE: SendMessage(GetParent(hdlg), CB_SHOWDROPDOWN, FALSE, 0); break; } } else if (wparam == VK_DELETE) { GetWindowText(hdlg, sztext, _countof(sztext)); CheckText(hdlg, sztext, TRUE); } return 0; } case WM_GETDLGCODE: return DLGC_WANTCHARS | DLGC_WANTARROWS; } return mir_callNextSubclass(hdlg, EditProc, msg, wparam, lparam); } HACCEL hAcct; HHOOK hHook; // This function filters the message queue and translates // the keyboard accelerators LRESULT CALLBACK HookProc(int code, WPARAM, LPARAM lparam) { if (code != MSGF_DIALOGBOX) return 0; MSG *msg = (MSG*)lparam; int action = Hotkey_Check(msg, "Quick Contacts"); if (action != 0) { SendMessage(hwndMain, WM_COMMAND, action, 0); return 1; } if (msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE) { switch (SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_GETDROPPEDSTATE, 0, 0)) { case FALSE: SendMessage(hwndMain, WM_CLOSE, 0, 0); break; case TRUE: SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_SHOWDROPDOWN, FALSE, 0); break; } } return 0; } BOOL ScreenToClient(HWND hWnd, LPRECT lpRect) { BOOL ret; POINT pt; pt.x = lpRect->left; pt.y = lpRect->top; ret = ScreenToClient(hWnd, &pt); if (!ret) return ret; lpRect->left = pt.x; lpRect->top = pt.y; pt.x = lpRect->right; pt.y = lpRect->bottom; ret = ScreenToClient(hWnd, &pt); lpRect->right = pt.x; lpRect->bottom = pt.y; return ret; } BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint) { return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint); } static void FillButton(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key, HICON icon) { wchar_t tmp[256]; wchar_t *full = tmp; if (key == nullptr) full = TranslateW(name); else mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key); SendDlgItemMessage(hwndDlg, dlgItem, BUTTONSETASFLATBTN, 0, 0); SendDlgItemMessage(hwndDlg, dlgItem, BUTTONADDTOOLTIP, (LPARAM)full, BATF_UNICODE); SendDlgItemMessage(hwndDlg, dlgItem, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon); } static void FillCheckbox(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key) { wchar_t tmp[256]; wchar_t *full = tmp; if (key == nullptr) full = TranslateW(name); else mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key); SetDlgItemText(hwndDlg, dlgItem, full); } static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: TranslateDialogDefault(hwndDlg); { RECT rc; GetWindowRect(GetDlgItem(hwndDlg, IDC_USERNAME), &rc); ScreenToClient(hwndDlg, &rc); CreateWindow(L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE, rc.left - 20, rc.top + (rc.bottom - rc.top - 16) / 2, 16, 16, hwndDlg, (HMENU)IDC_ICO, g_plugin.getInst(), nullptr); hHook = SetWindowsHookEx(WH_MSGFILTER, HookProc, nullptr, GetCurrentThreadId()); // Combo SendDlgItemMessage(hwndDlg, IDC_USERNAME, EM_LIMITTEXT, (WPARAM)119, 0); mir_subclassWindow(GetWindow(GetDlgItem(hwndDlg, IDC_USERNAME), GW_CHILD), EditProc); // Buttons FillCheckbox(hwndDlg, IDC_SHOW_ALL_CONTACTS, LPGENW("Show all contacts"), NULL); FillButton(hwndDlg, IDC_MESSAGE, LPGENW("Send message"), nullptr, Skin_LoadIcon(SKINICON_EVENT_MESSAGE)); FillButton(hwndDlg, IDC_FILE, LPGENW("Send file"), NULL, Skin_LoadIcon(SKINICON_EVENT_FILE)); FillButton(hwndDlg, IDC_USERINFO, LPGENW("Open user info"), NULL, Skin_LoadIcon(SKINICON_OTHER_USERDETAILS)); FillButton(hwndDlg, IDC_HISTORY, LPGENW("Open history"), NULL, Skin_LoadIcon(SKINICON_OTHER_HISTORY)); FillButton(hwndDlg, IDC_MENU, LPGENW("Open contact menu"), NULL, Skin_LoadIcon(SKINICON_OTHER_DOWNARROW)); SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETEXTENDEDUI, TRUE, 0); Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, MODULENAME, "window"); LoadContacts(hwndDlg, FALSE); EnableButtons(hwndDlg, NULL); if (g_plugin.getByte("EnableLastSentTo", 0)) { int pos = GetItemPos(g_plugin.getDword("LastSentTo", -1)); if (pos != -1) { SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETCURSEL, (WPARAM)pos, 0); EnableButtons(hwndDlg, contacts[pos]->hcontact); } } SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); } return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_USERNAME: if (HIWORD(wParam) == CBN_SELCHANGE) { int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0); EnableButtons(hwndDlg, pos < contacts.getCount() ? contacts[pos]->hcontact : NULL); } break; case IDC_ENTER: { MCONTACT hContact = GetSelectedContact(hwndDlg); if (hContact == NULL) break; Clist_ContactDoubleClicked(hContact); g_plugin.setDword("LastSentTo", hContact); SendMessage(hwndDlg, WM_CLOSE, 0, 0); } break; case IDC_MESSAGE: { MCONTACT hContact = GetSelectedContact(hwndDlg); if (hContact == NULL) { SetDlgItemText(hwndDlg, IDC_USERNAME, L""); SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); break; } // Is button enabled? if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MESSAGE))) break; CallService(MS_MSG_SENDMESSAGEW, hContact, 0); g_plugin.setDword("LastSentTo", hContact); SendMessage(hwndDlg, WM_CLOSE, 0, 0); break; } case HOTKEY_FILE: case IDC_FILE: { MCONTACT hContact = GetSelectedContact(hwndDlg); if (hContact == NULL) { SetDlgItemText(hwndDlg, IDC_USERNAME, L""); SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); break; } // Is button enabled? if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FILE))) break; File::Send(hContact); g_plugin.setDword("LastSentTo", hContact); SendMessage(hwndDlg, WM_CLOSE, 0, 0); } break; case HOTKEY_INFO: case IDC_USERINFO: { MCONTACT hContact = GetSelectedContact(hwndDlg); if (hContact == NULL) { SetDlgItemText(hwndDlg, IDC_USERNAME, L""); SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); break; } // Is button enabled? if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_USERINFO))) break; CallService(MS_USERINFO_SHOWDIALOG, hContact, 0); g_plugin.setDword("LastSentTo", hContact); SendMessage(hwndDlg, WM_CLOSE, 0, 0); } break; case HOTKEY_HISTORY: case IDC_HISTORY: { MCONTACT hContact = GetSelectedContact(hwndDlg); if (hContact == NULL) { SetDlgItemText(hwndDlg, IDC_USERNAME, L""); SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); break; } // Is button enabled? if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_HISTORY))) break; CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0); g_plugin.setDword("LastSentTo", hContact); SendMessage(hwndDlg, WM_CLOSE, 0, 0); } break; case HOTKEY_MENU: case IDC_MENU: { MCONTACT hContact = GetSelectedContact(hwndDlg); if (hContact == NULL) { SetDlgItemText(hwndDlg, IDC_USERNAME, L""); SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME)); break; } // Is button enabled? if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MENU))) break; RECT rc; GetWindowRect(GetDlgItem(hwndDlg, IDC_MENU), &rc); HMENU hMenu = Menu_BuildContactMenu(hContact); int ret = TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, nullptr); DestroyMenu(hMenu); if (ret) { SendMessage(hwndDlg, WM_CLOSE, 0, 0); Clist_MenuProcessCommand(LOWORD(ret), MPCF_CONTACTMENU, hContact); } g_plugin.setDword("LastSentTo", (uint32_t)hContact); } break; case HOTKEY_ALL_CONTACTS: case IDC_SHOW_ALL_CONTACTS: { // Get old text HWND hEdit = GetWindow(GetWindow(hwndDlg, GW_CHILD), GW_CHILD); wchar_t sztext[120] = L""; if (SendMessage(hEdit, EM_GETSEL, 0, 0) != -1) SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)L""); GetWindowText(hEdit, sztext, _countof(sztext)); // Fill combo BOOL all = IsDlgButtonChecked(hwndDlg, IDC_SHOW_ALL_CONTACTS); if (LOWORD(wParam) == HOTKEY_ALL_CONTACTS) { // Toggle checkbox all = !all; CheckDlgButton(hwndDlg, IDC_SHOW_ALL_CONTACTS, all ? BST_CHECKED : BST_UNCHECKED); } LoadContacts(hwndDlg, all); // Return selection CheckText(hEdit, sztext); } } break; case WM_CLOSE: Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "window"); DestroyWindow(hwndDlg); break; case WM_DESTROY: UnhookWindowsHookEx(hHook); hwndMain = nullptr; FreeContacts(); InterlockedExchange(&main_dialog_open, 0); break; case WM_DRAWITEM: { // add icons and protocol to listbox LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; // Handle contact menu if (lpdis->CtlID != IDC_USERNAME) { if (lpdis->CtlType == ODT_MENU) return Menu_DrawItem(lParam); break; } // Handle combo if (lpdis->itemID == -1) break; TEXTMETRIC tm; int icon_width = 0, icon_height = 0; RECT rc; GetTextMetrics(lpdis->hDC, &tm); ImageList_GetIconSize(hIml, &icon_width, &icon_height); COLORREF clrfore = SetTextColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); COLORREF clrback = SetBkColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); // Draw icon rc.left = lpdis->rcItem.left + 5; rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - icon_height) / 2; ImageList_Draw(hIml, Clist_GetContactIcon(contacts[lpdis->itemData]->hcontact), lpdis->hDC, rc.left, rc.top, ILD_NORMAL); // Make rect for text rc.left += icon_width + 5; rc.right = lpdis->rcItem.right - 1; rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; rc.bottom = rc.top + tm.tmHeight; // Draw Protocol if (opts.num_protos > 1) { if (max_proto_width == 0) { // Has to be done, else the DC isnt the right one // Dont ask me why for (auto &it : contacts) { RECT rcc = { 0, 0, 0x7FFF, 0x7FFF }; DrawText(lpdis->hDC, it->proto, -1, &rcc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT); max_proto_width = max(max_proto_width, rcc.right - rcc.left); } // Fix max_proto_width if (opts.group_append && opts.group_column) max_proto_width = min(max_proto_width, (rc.right - rc.left) / 5); else if (opts.group_append) max_proto_width = min(max_proto_width, (rc.right - rc.left) / 4); else max_proto_width = min(max_proto_width, (rc.right - rc.left) / 3); } RECT rc_tmp = rc; rc_tmp.left = rc_tmp.right - max_proto_width; DrawText(lpdis->hDC, contacts[lpdis->itemData]->proto, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE); rc.right = rc_tmp.left - 5; } // Draw group if (opts.group_append && opts.group_column) { RECT rc_tmp = rc; if (opts.group_column_left) { rc_tmp.right = rc_tmp.left + (rc.right - rc.left) / 3; rc.left = rc_tmp.right + 5; } else { rc_tmp.left = rc_tmp.right - (rc.right - rc.left) / 3; rc.right = rc_tmp.left - 5; } DrawText(lpdis->hDC, contacts[lpdis->itemData]->szgroup, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE); } // Draw text wchar_t *name; if (opts.group_append && !opts.group_column) name = GetListName(contacts[lpdis->itemData]); else name = contacts[lpdis->itemData]->szname; DrawText(lpdis->hDC, name, -1, &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE); // Restore old colors SetTextColor(lpdis->hDC, clrfore); SetBkColor(lpdis->hDC, clrback); } return TRUE; case WM_MEASUREITEM: { LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam; // Handle contact menu if (lpmis->CtlID != IDC_USERNAME) { if (lpmis->CtlType == ODT_MENU) return Menu_MeasureItem(lParam); break; } // Handle combo TEXTMETRIC tm; int icon_width = 0, icon_height = 0; GetTextMetrics(GetDC(hwndDlg), &tm); ImageList_GetIconSize(hIml, &icon_width, &icon_height); lpmis->itemHeight = max(icon_height, tm.tmHeight); return TRUE; } } return FALSE; } // Show the main dialog INT_PTR ShowDialog(WPARAM, LPARAM) { // Get the icons for the listbox hIml = Clist_GetImageList(); if (!main_dialog_open) { InterlockedExchange(&main_dialog_open, 1); hwndMain = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_MAIN), nullptr, MainDlgProc); } // Show it SetForegroundWindow(hwndMain); SetFocus(hwndMain); ShowWindow(hwndMain, SW_SHOW); return 0; }