summaryrefslogtreecommitdiff
path: root/plugins/QuickSearch/src/window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/QuickSearch/src/window.cpp')
-rw-r--r--plugins/QuickSearch/src/window.cpp1752
1 files changed, 876 insertions, 876 deletions
diff --git a/plugins/QuickSearch/src/window.cpp b/plugins/QuickSearch/src/window.cpp
index 891d39aa92..8624be7d98 100644
--- a/plugins/QuickSearch/src/window.cpp
+++ b/plugins/QuickSearch/src/window.cpp
@@ -1,876 +1,876 @@
-/*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it &/|
-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 | 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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-#define IDM_STAYONTOP (WM_USER+1)
-
-static QSMainDlg *g_pDlg = 0;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// edit control window procedure
-
-static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
- if (pDlg)
- if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
- return res;
-
- return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
-}
-
-INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
-{
- switch (msg) {
- case WM_CHAR:
- if (wParam == 27) // Escape
- Close();
- break;
-
- case WM_KEYUP:
- if (wParam == VK_RETURN) {
- if (m_grid.GetSelectedCount() == 1)
- ShowContactMsgDlg(GetFocusedContact());
- return 0;
- }
- break;
-
- case WM_KEYDOWN:
- int count = m_grid.GetItemCount();
- int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
- int next = -1;
- if (count > 0) {
- switch (wParam) {
- case VK_UP:
- if (current > 0)
- next = current - 1;
- break;
-
- case VK_DOWN:
- if (current < count - 1)
- next = current + 1;
- break;
-
- case VK_F5:
- onClick_Refresh(0);
- return 0;
-
- case VK_NEXT:
- case VK_PRIOR:
- int perpage = m_grid.GetCountPerPage();
- if (wParam == VK_NEXT)
- next = min(current + perpage, count);
- else
- next = max(current - perpage, 0);
- break;
- }
- }
-
- if (next >= 0) {
- m_grid.SetItemState(-1, 0, LVIS_SELECTED);
- m_grid.SetCurSel(next);
- m_grid.EnsureVisible(next, FALSE);
- }
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// list header window procedure
-
-static void MakeColumnMenu()
-{
- HMENU hMenu = CreatePopupMenu();
-
- for (auto &it : g_plugin.m_columns) {
- int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
- AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
- }
-
- POINT pt;
- GetCursorPos(&pt);
-
- int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
- if (id >= 100)
- g_pDlg->ToggleColumn(id - 100);
-
- DestroyMenu(hMenu);
-}
-
-void QSMainDlg::ToggleColumn(int col)
-{
- auto &pCol = g_plugin.m_columns[col];
-
- if (!pCol.bEnabled) { // show column
- pCol.bEnabled = true;
-
- if (!pCol.bInit) {
- for (auto &it : m_rows)
- it->pValues[col].LoadOneItem(it->hContact, pCol, this);
- pCol.bInit = true;
- }
-
- // screen
- int lvcol = ColumnToListView(col);
- AddColumn(lvcol, &pCol);
-
- int nCount = m_grid.GetItemCount();
- for (int i = 0; i < nCount; i++) {
- auto *pRow = GetRow(i);
-
- LV_ITEMW li;
- li.iItem = i;
- li.iSubItem = lvcol;
- li.mask = LVIF_TEXT;
- li.pszText = pRow->pValues[col].text;
- if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
- li.mask |= LVIF_IMAGE;
- m_grid.SetItem(&li);
- }
- }
- else { // hide column
- int cnt = 0;
- for (auto &it : g_plugin.m_columns)
- if (it->bEnabled)
- cnt++;
-
- // keep at least one visible column (1 + this)
- if (cnt > 2) {
- m_grid.DeleteColumn(col);
- pCol.bEnabled = false;
- }
- }
-}
-
-static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_RBUTTONUP:
- return 0;
-
- case WM_RBUTTONDOWN:
- MakeColumnMenu();
- break;
- }
-
- return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// grid list window procedure
-
-static int OldHSubItem = 0, OldHItem = 0;
-
-static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
- if (pDlg)
- if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
- return res;
-
- return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
-}
-
-INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
-{
- LV_HITTESTINFO pinfo;
-
- switch (msg) {
- case WM_CHAR:
- switch (wParam) { // ESC
- case 27:
- Close();
- break;
-
- case 1: // Cltr+A
- m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
- break;
-
- case 3: // Ctrl-C
- CopyMultiLines();
- break;
-
- case 8: // backspace
- edtFilter.SendMsg(msg, wParam, lParam);
- break;
- }
-
- if (wParam >= 32 && wParam <= 127) // letters
- edtFilter.SendMsg(msg, wParam, lParam);
- break;
-
- case WM_MOUSEMOVE:
- pinfo.pt.x = LOWORD(lParam);
- pinfo.pt.y = HIWORD(lParam);
- pinfo.flags = 0;
- if (m_grid.SubItemHitTest(&pinfo) == -1)
- break;
-
- if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
- OldHSubItem = pinfo.iSubItem;
- OldHItem = pinfo.iItem;
-
- if (g_bTipperInstalled) {
- if (TTShowed) {
- TTShowed = false;
- Tipper_Hide();
- }
- m_hover.Stop();
-
- if (OldHSubItem == 0)
- m_hover.Start(450);
- }
-
- TOOLINFOW ti = {};
- ti.cbSize = sizeof(ti);
- ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
- ti.hwnd = m_hwnd;
- ti.uId = LPARAM(m_hwnd);
-
- int num = ListViewToColumn(OldHSubItem);
- auto &pCol = g_plugin.m_columns[num];
- if (pCol.isXstatus || pCol.isGender) {
- auto *pRow = GetRow(OldHItem);
- if (pCol.isGender) {
- switch (pRow->pValues[num].data) {
- case 'M': ti.lpszText = TranslateT("Male"); break;
- case 'F': ti.lpszText = TranslateT("Female"); break;
- default: ti.lpszText = TranslateT("Unknown"); break;
- }
- }
- else {
- wchar_t buf[256];
- mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
- int iStatus = _wtoi(buf);
-
- CUSTOM_STATUS ics = {};
- ics.cbSize = sizeof(ics);
- ics.status = &iStatus;
- ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
- ics.pwszName = buf;
- CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
- ti.lpszText = TranslateW(buf);
- }
- }
-
- SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
- }
- break;
-
- case WM_KEYUP:
- switch (wParam) {
- case VK_RETURN:
- if (m_grid.GetSelectedCount() == 1)
- ShowContactMsgDlg(GetFocusedContact());
- break;
-
- case VK_INSERT:
- CallService(MS_FINDADD_FINDADD, 0, 0);
- break;
-
- case VK_DELETE:
- lParam = m_grid.GetSelectedCount();
- if (lParam > 1)
- DeleteByList();
- else if (lParam == 1)
- DeleteOneContact(GetFocusedContact());
- break;
-
- case VK_F5:
- onClick_Refresh(0);
- break;
- }
- break;
-
- case WM_NOTIFY:
- if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
- NMHEADER *pdhr = (NMHEADER *)lParam;
- if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
- int i = ListViewToColumn(pdhr->iItem);
- auto &pCol = g_plugin.m_columns[i];
-
- if (pdhr->pitem->fmt & HDF_CHECKED) {
- pCol.bFilter = false;
- pdhr->pitem->fmt &= ~HDF_CHECKED;
- }
- else {
- pCol.bFilter = true;
- pdhr->pitem->fmt |= HDF_CHECKED;
- }
-
- SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
- FillGrid();
- return TRUE;
- }
- }
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// QSMainDlg class implementation
-
-static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
-{
- if (p1->bAccDel != p2->bAccDel)
- return (p1->bAccDel) ? 1 : -1;
-
- if (p1->bAccOff != p2->bAccOff)
- return (p1->bAccOff) ? 1 : -1;
-
- return mir_strcmp(p1->szProto, p2->szProto);
-}
-
-QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
- CDlgBase(g_plugin, IDD_MAIN),
- m_rows(50),
- m_patterns(1),
- m_sbdata(10, CompareSb),
- m_grid(this, IDC_LIST),
- m_hover(this, 10),
- cmbProto(this, IDC_CB_PROTOCOLS),
- edtFilter(this, IDC_E_SEARCHTEXT),
- btnRefresh(this, IDC_REFRESH),
- chkColorize(this, IDC_CH_COLORIZE),
- chkShowOffline(this, IDC_CH_SHOWOFFLINE)
-{
- SetMinSize(300, 160);
-
- if (pwszPattern)
- m_wszPatternBuf = mir_wstrdup(pwszPattern);
- else if (g_plugin.m_flags & QSO_SAVEPATTERN)
- m_wszPatternBuf = g_plugin.getWStringA("pattern");
-
- m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
-
- m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
- m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
- m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
- m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
-
- btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
-
- cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
-
- edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
- chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
- chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
-}
-
-bool QSMainDlg::OnInitDialog()
-{
- g_pDlg = this;
- mnuhandle = 0;
-
- SetCaption(TranslateT("Quick Search"));
-
- hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
-
- HMENU smenu = GetSystemMenu(m_hwnd, false);
- InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
- InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
-
- if (g_plugin.m_flags & QSO_STAYONTOP) {
- CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
- SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- }
-
- chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
-
- szFilterProto = nullptr; // display all protocols
- if (g_plugin.m_flags & QSO_SHOWOFFLINE)
- bShowOffline = true;
-
- chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
-
- // Window
- INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
- if (g_plugin.m_flags & QSO_TOOLSTYLE)
- tmp |= WS_EX_TOOLWINDOW;
- else
- tmp &= ~WS_EX_TOOLWINDOW;
- SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
-
- SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
-
- // ListView
- m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
-
- tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
- LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
- if (g_plugin.m_flags & QSO_DRAWGRID)
- tmp |= LVS_EX_GRIDLINES;
- m_grid.SetExtendedListViewStyle(tmp);
-
- // ListView header
- HWND header = m_grid.GetHeader();
- SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
-
- mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
-
- SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
- mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
-
- SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
- mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
-
- FillProtoCombo();
-
- PrepareTable();
-
- if (m_wszPatternBuf) {
- edtFilter.SetText(m_wszPatternBuf);
- MakePattern(m_wszPatternBuf);
- }
- FillGrid();
-
- // Show sorting column
- HDITEM hdi = {};
- hdi.mask = HDI_FORMAT;
- SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
- if (g_plugin.m_flags & QSO_SORTASC)
- hdi.fmt |= HDF_SORTUP;
- else
- hdi.fmt |= HDF_SORTDOWN;
- SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
- RECT rc = g_plugin.m_rect;
- ::SnapToScreen(rc);
- ::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
-
- HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
-
- TOOLINFOW ti;
- ti.cbSize = sizeof(ti);
- ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
- ti.hwnd = m_hwnd;
- ti.uId = UINT_PTR(m_grid.GetHwnd());
- SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
-
- hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
- hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
- hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
- return true;
-}
-
-void QSMainDlg::OnDestroy()
-{
- if (mnuhandle)
- Menu_RemoveItem(mnuhandle);
-
- UnhookEvent(hAdd);
- UnhookEvent(hDelete);
- UnhookEvent(hChange);
-
- g_pDlg = nullptr;
-
- RECT rc;
- GetWindowRect(m_hwnd, &rc);
- CopyRect(&g_plugin.m_rect, &rc);
-
- // save column width/order
- SaveColumnOrder();
-
- g_plugin.SaveOptWnd();
-
- m_grid.SetImageList(0, LVSIL_SMALL);
-
- if (g_plugin.m_flags & QSO_SAVEPATTERN)
- g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
-
- m_rows.destroy();
- m_patterns.destroy();
-}
-
-int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
-{
- switch (urc->wId) {
- case IDC_REFRESH:
- return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
-
- case IDC_E_SEARCHTEXT:
- return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
-
- case IDC_LIST:
- return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
-
- case IDC_STATUSBAR:
- return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
- }
- return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// event handlers
-
-INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
-{
- auto *pRow = new CRowItem(hContact, this);
- m_rows.insert(pRow);
- AddContactToList(hContact, pRow);
- ProcessLine(pRow);
- Sort();
- UpdateSB();
- return 0;
-}
-
-INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
-{
- int idx = -1;
- CRowItem *pRow = 0;
-
- for (auto &it : m_rows)
- if (it->hContact == hContact) {
- pRow = it;
- idx = m_rows.indexOf(&it);
- break;
- }
-
- if (idx == -1)
- return 0;
-
- int iItem = FindItem(pRow);
- if (iItem != -1)
- m_grid.DeleteItem(iItem);
-
- m_rows.remove(idx);
- UpdateSB();
- return 0;
-}
-
-INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
-{
- auto *pRow = FindRow(hContact);
- if (pRow == nullptr)
- return 0;
-
- int oldStatus = pRow->status;
- int newStatus = Contact::GetStatus(hContact);
- pRow->status = newStatus;
-
- if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
- ChangeStatusPicture(pRow, hContact, lParam);
- else if (oldStatus != ID_STATUS_OFFLINE) {
- if (g_plugin.m_flags & QSO_SHOWOFFLINE)
- ChangeStatusPicture(pRow, hContact, lParam);
- else
- ProcessLine(pRow, true);
- }
- else if (newStatus != ID_STATUS_OFFLINE) {
- if (g_plugin.m_flags & QSO_SHOWOFFLINE)
- ChangeStatusPicture(pRow, hContact, lParam);
- else {
- pRow->bActive = false;
- m_grid.DeleteItem(FindItem(pRow));
- }
- }
-
- if (g_plugin.m_flags & QSO_SORTBYSTATUS)
- Sort();
-
- UpdateSB();
- return 0;
-}
-
-INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
-{
- if (wParam == IDM_STAYONTOP) {
- int h; HWND w;
- if (g_plugin.m_flags & QSO_STAYONTOP) {
- h = MF_BYCOMMAND | MF_UNCHECKED;
- w = HWND_NOTOPMOST;
- }
- else {
- h = MF_BYCOMMAND | MF_CHECKED;
- w = HWND_TOPMOST;
- }
- CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
- SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- g_plugin.m_flags ^= QSO_STAYONTOP;
- }
- return 0;
-}
-
-INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
-{
- if (g_bTipperInstalled) {
- RECT rc;
- GetWindowRect(m_grid.GetHwnd(), &rc);
-
- POINT pt = { LOWORD(lParam), HIWORD(lParam) };
- ClientToScreen(m_hwnd, &pt);
- if (!PtInRect(&rc, pt)) {
- if (TTShowed) {
- TTShowed = false;
- CallService(MS_TIPPER_HIDETIP, 0, 0);
- }
- }
-
- m_hover.Stop();
- }
- return 0;
-}
-
-INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
-{
- if (wParam == VK_F5)
- PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
- return 0;
-}
-
-void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
-{
- int w = m_grid.GetSelectedCount();
- if (w > 1)
- ShowMultiPopup(w);
- else
- ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
-}
-
-void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
-{
- LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
- if (lParam == -1 || lParam == 0)
- szFilterProto = nullptr;
- else
- szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
-
- AdvancedFilter();
-}
-
-void QSMainDlg::onChange_Filter(CCtrlEdit *)
-{
- if (!m_bInitialized)
- return;
-
- MakePattern(ptrW(edtFilter.GetText()));
- FillGrid();
-}
-
-void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
-{
- if (chkShowOffline.IsChecked()) {
- g_plugin.m_flags |= QSO_SHOWOFFLINE;
- bShowOffline = true;
- }
- else {
- g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
- bShowOffline = false;
- }
-
- AdvancedFilter();
-}
-
-void QSMainDlg::onChange_Colorize(CCtrlCheck *)
-{
- if (chkColorize.IsChecked())
- g_plugin.m_flags |= QSO_COLORIZE;
- else
- g_plugin.m_flags &= ~QSO_COLORIZE;
- RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
-}
-
-void QSMainDlg::onClick_Refresh(CCtrlButton *)
-{
- m_rows.destroy();
- PrepareToFill();
- PrepareTable(true);
- FillGrid();
-}
-
-void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
-{
- HWND header = m_grid.GetHeader();
-
- // clear sort mark
- HDITEM hdi = {};
- hdi.mask = HDI_FORMAT;
- SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
- hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
- SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
- if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
- g_plugin.m_flags |= QSO_SORTASC;
- g_plugin.m_sortOrder = ev->nmlv->iSubItem;
- }
- else g_plugin.m_flags ^= QSO_SORTASC;;
-
- // set new sort mark
- SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
- if ((g_plugin.m_flags & QSO_SORTASC) == 0)
- hdi.fmt |= HDF_SORTDOWN;
- else
- hdi.fmt &= ~HDF_SORTUP;
- SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
- Sort();
-}
-
-void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
-{
- ShowContactMsgDlg(GetFocusedContact());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
-{
- LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
- HICON h;
- RECT rc;
- auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
-
- int result = CDRF_DODEFAULT;
- switch (lplvcd->nmcd.dwDrawStage) {
- case CDDS_PREPAINT:
- result = CDRF_NOTIFYITEMDRAW;
- break;
-
- case CDDS_ITEMPREPAINT:
- pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
- result = CDRF_NOTIFYSUBITEMDRAW;
- break;
-
- case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
- pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
- {
- int sub = ListViewToColumn(lplvcd->iSubItem);
- auto *pCol = &g_plugin.m_columns[sub];
- if (pCol == nullptr)
- break;
-
- if (pCol->isGender) {
- m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
-
- switch (pRow->pValues[sub].data) {
- case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
- case 'M': h = g_plugin.getIcon(IDI_MALE); break;
- default: h = 0;
- }
-
- if (h)
- DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
- result = CDRF_SKIPDEFAULT;
- }
- else if (pCol->isXstatus) {
- int j = _wtoi(pRow->pValues[sub].text);
- if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
- h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
- m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
- DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
- }
- result = CDRF_SKIPDEFAULT;
- }
- else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
- result = CDRF_NOTIFYPOSTPAINT;
- }
- break;
-
- case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
- {
- int sub = ListViewToColumn(lplvcd->iSubItem);
- auto *pCol = &g_plugin.m_columns[sub];
- if (pCol == nullptr)
- break;
-
- if (pCol->isClient) {
- auto *MirVerW = pRow->pValues[sub].text;
- if (MirVerW && *MirVerW && g_bFingerInstalled) {
- h = Finger_GetClientIcon(MirVerW, FALSE);
- m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
- DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
- DestroyIcon(h);
- }
- }
- result = CDRF_SKIPDEFAULT;
- }
- break;
- }
-
- SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
-}
-
-void QSMainDlg::onTimer_Hover(CTimer *pTimer)
-{
- pTimer->Stop();
-
- if (GetForegroundWindow() != m_hwnd)
- return;
-
- auto *pRow = GetRow(OldHItem);
- if (pRow == 0)
- return;
-
- POINT pt;
- GetCursorPos(&pt);
-
- RECT rcItem;
- m_grid.GetItemRect(OldHItem, &rcItem, 0);
- ScreenToClient(m_grid.GetHwnd(), &pt);
- if (!PtInRect(&rcItem, pt))
- return;
-
- CLCINFOTIP info = {};
- info.cbSize = sizeof(info);
- info.hItem = HANDLE(pRow->hContact);
- Tipper_ShowTip(0, &info);
- TTShowed = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CloseSrWindow(bool)
-{
- if (!g_pDlg)
- return false;
-
- g_pDlg->Close();
- return true;
-}
-
-int OpenSrWindow(const wchar_t *pwszPattern)
-{
- if (g_pDlg) {
- WINDOWPLACEMENT wp;
- wp.length = sizeof(wp);
- GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
- if (wp.showCmd == SW_SHOWMINIMIZED)
- g_pDlg->Show(SW_RESTORE);
- SetForegroundWindow(g_pDlg->GetHwnd());
- return true;
- }
-
- int count = 0;
- for (auto &it : g_plugin.m_columns)
- if (it->bEnabled)
- count++;
-
- // no even one visible column
- if (count == 0)
- return true;
-
- g_plugin.LoadOptWnd();
-
- auto *pDlg = new QSMainDlg(pwszPattern);
- if (pDlg->PrepareToFill())
- pDlg->Create();
- else
- delete pDlg;
-
- return true;
-}
+/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+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 | 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+#define IDM_STAYONTOP (WM_USER+1)
+
+static QSMainDlg *g_pDlg = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// edit control window procedure
+
+static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+ if (pDlg)
+ if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
+ return res;
+
+ return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_CHAR:
+ if (wParam == 27) // Escape
+ Close();
+ break;
+
+ case WM_KEYUP:
+ if (wParam == VK_RETURN) {
+ if (m_grid.GetSelectedCount() == 1)
+ ShowContactMsgDlg(GetFocusedContact());
+ return 0;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ int count = m_grid.GetItemCount();
+ int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
+ int next = -1;
+ if (count > 0) {
+ switch (wParam) {
+ case VK_UP:
+ if (current > 0)
+ next = current - 1;
+ break;
+
+ case VK_DOWN:
+ if (current < count - 1)
+ next = current + 1;
+ break;
+
+ case VK_F5:
+ onClick_Refresh(0);
+ return 0;
+
+ case VK_NEXT:
+ case VK_PRIOR:
+ int perpage = m_grid.GetCountPerPage();
+ if (wParam == VK_NEXT)
+ next = min(current + perpage, count);
+ else
+ next = max(current - perpage, 0);
+ break;
+ }
+ }
+
+ if (next >= 0) {
+ m_grid.SetItemState(-1, 0, LVIS_SELECTED);
+ m_grid.SetCurSel(next);
+ m_grid.EnsureVisible(next, FALSE);
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// list header window procedure
+
+static void MakeColumnMenu()
+{
+ HMENU hMenu = CreatePopupMenu();
+
+ for (auto &it : g_plugin.m_columns) {
+ int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
+ AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
+ }
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
+ if (id >= 100)
+ g_pDlg->ToggleColumn(id - 100);
+
+ DestroyMenu(hMenu);
+}
+
+void QSMainDlg::ToggleColumn(int col)
+{
+ auto &pCol = g_plugin.m_columns[col];
+
+ if (!pCol.bEnabled) { // show column
+ pCol.bEnabled = true;
+
+ if (!pCol.bInit) {
+ for (auto &it : m_rows)
+ it->pValues[col].LoadOneItem(it->hContact, pCol, this);
+ pCol.bInit = true;
+ }
+
+ // screen
+ int lvcol = ColumnToListView(col);
+ AddColumn(lvcol, &pCol);
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ auto *pRow = GetRow(i);
+
+ LV_ITEMW li;
+ li.iItem = i;
+ li.iSubItem = lvcol;
+ li.mask = LVIF_TEXT;
+ li.pszText = pRow->pValues[col].text;
+ if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
+ li.mask |= LVIF_IMAGE;
+ m_grid.SetItem(&li);
+ }
+ }
+ else { // hide column
+ int cnt = 0;
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ cnt++;
+
+ // keep at least one visible column (1 + this)
+ if (cnt > 2) {
+ m_grid.DeleteColumn(col);
+ pCol.bEnabled = false;
+ }
+ }
+}
+
+static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_RBUTTONUP:
+ return 0;
+
+ case WM_RBUTTONDOWN:
+ MakeColumnMenu();
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid list window procedure
+
+static int OldHSubItem = 0, OldHItem = 0;
+
+static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+ if (pDlg)
+ if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
+ return res;
+
+ return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LV_HITTESTINFO pinfo;
+
+ switch (msg) {
+ case WM_CHAR:
+ switch (wParam) { // ESC
+ case 27:
+ Close();
+ break;
+
+ case 1: // Cltr+A
+ m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
+ break;
+
+ case 3: // Ctrl-C
+ CopyMultiLines();
+ break;
+
+ case 8: // backspace
+ edtFilter.SendMsg(msg, wParam, lParam);
+ break;
+ }
+
+ if (wParam >= 32 && wParam <= 127) // letters
+ edtFilter.SendMsg(msg, wParam, lParam);
+ break;
+
+ case WM_MOUSEMOVE:
+ pinfo.pt.x = LOWORD(lParam);
+ pinfo.pt.y = HIWORD(lParam);
+ pinfo.flags = 0;
+ if (m_grid.SubItemHitTest(&pinfo) == -1)
+ break;
+
+ if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
+ OldHSubItem = pinfo.iSubItem;
+ OldHItem = pinfo.iItem;
+
+ if (g_bTipperInstalled) {
+ if (TTShowed) {
+ TTShowed = false;
+ Tipper_Hide();
+ }
+ m_hover.Stop();
+
+ if (OldHSubItem == 0)
+ m_hover.Start(450);
+ }
+
+ TOOLINFOW ti = {};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+ ti.hwnd = m_hwnd;
+ ti.uId = LPARAM(m_hwnd);
+
+ int num = ListViewToColumn(OldHSubItem);
+ auto &pCol = g_plugin.m_columns[num];
+ if (pCol.isXstatus || pCol.isGender) {
+ auto *pRow = GetRow(OldHItem);
+ if (pCol.isGender) {
+ switch (pRow->pValues[num].data) {
+ case 'M': ti.lpszText = TranslateT("Male"); break;
+ case 'F': ti.lpszText = TranslateT("Female"); break;
+ default: ti.lpszText = TranslateT("Unknown"); break;
+ }
+ }
+ else {
+ wchar_t buf[256];
+ mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
+ int iStatus = _wtoi(buf);
+
+ CUSTOM_STATUS ics = {};
+ ics.cbSize = sizeof(ics);
+ ics.status = &iStatus;
+ ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
+ ics.pwszName = buf;
+ CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
+ ti.lpszText = TranslateW(buf);
+ }
+ }
+
+ SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
+ }
+ break;
+
+ case WM_KEYUP:
+ switch (wParam) {
+ case VK_RETURN:
+ if (m_grid.GetSelectedCount() == 1)
+ ShowContactMsgDlg(GetFocusedContact());
+ break;
+
+ case VK_INSERT:
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ break;
+
+ case VK_DELETE:
+ lParam = m_grid.GetSelectedCount();
+ if (lParam > 1)
+ DeleteByList();
+ else if (lParam == 1)
+ DeleteOneContact(GetFocusedContact());
+ break;
+
+ case VK_F5:
+ onClick_Refresh(0);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
+ NMHEADER *pdhr = (NMHEADER *)lParam;
+ if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
+ int i = ListViewToColumn(pdhr->iItem);
+ auto &pCol = g_plugin.m_columns[i];
+
+ if (pdhr->pitem->fmt & HDF_CHECKED) {
+ pCol.bFilter = false;
+ pdhr->pitem->fmt &= ~HDF_CHECKED;
+ }
+ else {
+ pCol.bFilter = true;
+ pdhr->pitem->fmt |= HDF_CHECKED;
+ }
+
+ SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
+ FillGrid();
+ return TRUE;
+ }
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// QSMainDlg class implementation
+
+static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
+{
+ if (p1->bAccDel != p2->bAccDel)
+ return (p1->bAccDel) ? 1 : -1;
+
+ if (p1->bAccOff != p2->bAccOff)
+ return (p1->bAccOff) ? 1 : -1;
+
+ return mir_strcmp(p1->szProto, p2->szProto);
+}
+
+QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
+ CDlgBase(g_plugin, IDD_MAIN),
+ m_rows(50),
+ m_patterns(1),
+ m_sbdata(10, CompareSb),
+ m_grid(this, IDC_LIST),
+ m_hover(this, 10),
+ cmbProto(this, IDC_CB_PROTOCOLS),
+ edtFilter(this, IDC_E_SEARCHTEXT),
+ btnRefresh(this, IDC_REFRESH),
+ chkColorize(this, IDC_CH_COLORIZE),
+ chkShowOffline(this, IDC_CH_SHOWOFFLINE)
+{
+ SetMinSize(300, 160);
+
+ if (pwszPattern)
+ m_wszPatternBuf = mir_wstrdup(pwszPattern);
+ else if (g_plugin.m_flags & QSO_SAVEPATTERN)
+ m_wszPatternBuf = g_plugin.getWStringA("pattern");
+
+ m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
+
+ m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
+ m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
+ m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
+ m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
+
+ btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
+
+ cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
+
+ edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
+ chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
+ chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
+}
+
+bool QSMainDlg::OnInitDialog()
+{
+ g_pDlg = this;
+ mnuhandle = 0;
+
+ SetCaption(TranslateT("Quick Search"));
+
+ hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
+
+ HMENU smenu = GetSystemMenu(m_hwnd, false);
+ InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+ InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
+
+ if (g_plugin.m_flags & QSO_STAYONTOP) {
+ CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
+ SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
+
+ szFilterProto = nullptr; // display all protocols
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ bShowOffline = true;
+
+ chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
+
+ // Window
+ INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
+ if (g_plugin.m_flags & QSO_TOOLSTYLE)
+ tmp |= WS_EX_TOOLWINDOW;
+ else
+ tmp &= ~WS_EX_TOOLWINDOW;
+ SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
+
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
+
+ // ListView
+ m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
+
+ tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
+ LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
+ if (g_plugin.m_flags & QSO_DRAWGRID)
+ tmp |= LVS_EX_GRIDLINES;
+ m_grid.SetExtendedListViewStyle(tmp);
+
+ // ListView header
+ HWND header = m_grid.GetHeader();
+ SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
+
+ mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
+
+ SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
+
+ SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
+
+ FillProtoCombo();
+
+ PrepareTable();
+
+ if (m_wszPatternBuf) {
+ edtFilter.SetText(m_wszPatternBuf);
+ MakePattern(m_wszPatternBuf);
+ }
+ FillGrid();
+
+ // Show sorting column
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+ SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ if (g_plugin.m_flags & QSO_SORTASC)
+ hdi.fmt |= HDF_SORTUP;
+ else
+ hdi.fmt |= HDF_SORTDOWN;
+ SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ RECT rc = g_plugin.m_rect;
+ ::SnapToScreen(rc);
+ ::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
+
+ HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
+
+ TOOLINFOW ti;
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+ ti.hwnd = m_hwnd;
+ ti.uId = UINT_PTR(m_grid.GetHwnd());
+ SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
+
+ hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
+ hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
+ hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
+ return true;
+}
+
+void QSMainDlg::OnDestroy()
+{
+ if (mnuhandle)
+ Menu_RemoveItem(mnuhandle);
+
+ UnhookEvent(hAdd);
+ UnhookEvent(hDelete);
+ UnhookEvent(hChange);
+
+ g_pDlg = nullptr;
+
+ RECT rc;
+ GetWindowRect(m_hwnd, &rc);
+ CopyRect(&g_plugin.m_rect, &rc);
+
+ // save column width/order
+ SaveColumnOrder();
+
+ g_plugin.SaveOptWnd();
+
+ m_grid.SetImageList(0, LVSIL_SMALL);
+
+ if (g_plugin.m_flags & QSO_SAVEPATTERN)
+ g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
+
+ m_rows.destroy();
+ m_patterns.destroy();
+}
+
+int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId) {
+ case IDC_REFRESH:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ case IDC_E_SEARCHTEXT:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ case IDC_LIST:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDC_STATUSBAR:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// event handlers
+
+INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
+{
+ auto *pRow = new CRowItem(hContact, this);
+ m_rows.insert(pRow);
+ AddContactToList(hContact, pRow);
+ ProcessLine(pRow);
+ Sort();
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
+{
+ int idx = -1;
+ CRowItem *pRow = 0;
+
+ for (auto &it : m_rows)
+ if (it->hContact == hContact) {
+ pRow = it;
+ idx = m_rows.indexOf(&it);
+ break;
+ }
+
+ if (idx == -1)
+ return 0;
+
+ int iItem = FindItem(pRow);
+ if (iItem != -1)
+ m_grid.DeleteItem(iItem);
+
+ m_rows.remove(idx);
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
+{
+ auto *pRow = FindRow(hContact);
+ if (pRow == nullptr)
+ return 0;
+
+ int oldStatus = pRow->status;
+ int newStatus = Contact::GetStatus(hContact);
+ pRow->status = newStatus;
+
+ if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else if (oldStatus != ID_STATUS_OFFLINE) {
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else
+ ProcessLine(pRow, true);
+ }
+ else if (newStatus != ID_STATUS_OFFLINE) {
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else {
+ pRow->bActive = false;
+ m_grid.DeleteItem(FindItem(pRow));
+ }
+ }
+
+ if (g_plugin.m_flags & QSO_SORTBYSTATUS)
+ Sort();
+
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
+{
+ if (wParam == IDM_STAYONTOP) {
+ int h; HWND w;
+ if (g_plugin.m_flags & QSO_STAYONTOP) {
+ h = MF_BYCOMMAND | MF_UNCHECKED;
+ w = HWND_NOTOPMOST;
+ }
+ else {
+ h = MF_BYCOMMAND | MF_CHECKED;
+ w = HWND_TOPMOST;
+ }
+ CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
+ SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ g_plugin.m_flags ^= QSO_STAYONTOP;
+ }
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
+{
+ if (g_bTipperInstalled) {
+ RECT rc;
+ GetWindowRect(m_grid.GetHwnd(), &rc);
+
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ ClientToScreen(m_hwnd, &pt);
+ if (!PtInRect(&rc, pt)) {
+ if (TTShowed) {
+ TTShowed = false;
+ CallService(MS_TIPPER_HIDETIP, 0, 0);
+ }
+ }
+
+ m_hover.Stop();
+ }
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
+{
+ if (wParam == VK_F5)
+ PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
+ return 0;
+}
+
+void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
+{
+ int w = m_grid.GetSelectedCount();
+ if (w > 1)
+ ShowMultiPopup(w);
+ else
+ ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
+}
+
+void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
+{
+ LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
+ if (lParam == -1 || lParam == 0)
+ szFilterProto = nullptr;
+ else
+ szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
+
+ AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Filter(CCtrlEdit *)
+{
+ if (!m_bInitialized)
+ return;
+
+ MakePattern(ptrW(edtFilter.GetText()));
+ FillGrid();
+}
+
+void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
+{
+ if (chkShowOffline.IsChecked()) {
+ g_plugin.m_flags |= QSO_SHOWOFFLINE;
+ bShowOffline = true;
+ }
+ else {
+ g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
+ bShowOffline = false;
+ }
+
+ AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Colorize(CCtrlCheck *)
+{
+ if (chkColorize.IsChecked())
+ g_plugin.m_flags |= QSO_COLORIZE;
+ else
+ g_plugin.m_flags &= ~QSO_COLORIZE;
+ RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
+}
+
+void QSMainDlg::onClick_Refresh(CCtrlButton *)
+{
+ m_rows.destroy();
+ PrepareToFill();
+ PrepareTable(true);
+ FillGrid();
+}
+
+void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
+{
+ HWND header = m_grid.GetHeader();
+
+ // clear sort mark
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+ SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
+ SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
+ g_plugin.m_flags |= QSO_SORTASC;
+ g_plugin.m_sortOrder = ev->nmlv->iSubItem;
+ }
+ else g_plugin.m_flags ^= QSO_SORTASC;;
+
+ // set new sort mark
+ SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ if ((g_plugin.m_flags & QSO_SORTASC) == 0)
+ hdi.fmt |= HDF_SORTDOWN;
+ else
+ hdi.fmt &= ~HDF_SORTUP;
+ SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ Sort();
+}
+
+void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
+{
+ ShowContactMsgDlg(GetFocusedContact());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
+{
+ LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
+ HICON h;
+ RECT rc;
+ auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
+
+ int result = CDRF_DODEFAULT;
+ switch (lplvcd->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ result = CDRF_NOTIFYITEMDRAW;
+ break;
+
+ case CDDS_ITEMPREPAINT:
+ pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+ result = CDRF_NOTIFYSUBITEMDRAW;
+ break;
+
+ case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
+ pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+ {
+ int sub = ListViewToColumn(lplvcd->iSubItem);
+ auto *pCol = &g_plugin.m_columns[sub];
+ if (pCol == nullptr)
+ break;
+
+ if (pCol->isGender) {
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+
+ switch (pRow->pValues[sub].data) {
+ case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
+ case 'M': h = g_plugin.getIcon(IDI_MALE); break;
+ default: h = 0;
+ }
+
+ if (h)
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ result = CDRF_SKIPDEFAULT;
+ }
+ else if (pCol->isXstatus) {
+ int j = _wtoi(pRow->pValues[sub].text);
+ if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
+ h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ }
+ result = CDRF_SKIPDEFAULT;
+ }
+ else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
+ result = CDRF_NOTIFYPOSTPAINT;
+ }
+ break;
+
+ case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
+ {
+ int sub = ListViewToColumn(lplvcd->iSubItem);
+ auto *pCol = &g_plugin.m_columns[sub];
+ if (pCol == nullptr)
+ break;
+
+ if (pCol->isClient) {
+ auto *MirVerW = pRow->pValues[sub].text;
+ if (MirVerW && *MirVerW && g_bFingerInstalled) {
+ h = Finger_GetClientIcon(MirVerW, FALSE);
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ DestroyIcon(h);
+ }
+ }
+ result = CDRF_SKIPDEFAULT;
+ }
+ break;
+ }
+
+ SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
+}
+
+void QSMainDlg::onTimer_Hover(CTimer *pTimer)
+{
+ pTimer->Stop();
+
+ if (GetForegroundWindow() != m_hwnd)
+ return;
+
+ auto *pRow = GetRow(OldHItem);
+ if (pRow == 0)
+ return;
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rcItem;
+ m_grid.GetItemRect(OldHItem, &rcItem, 0);
+ ScreenToClient(m_grid.GetHwnd(), &pt);
+ if (!PtInRect(&rcItem, pt))
+ return;
+
+ CLCINFOTIP info = {};
+ info.cbSize = sizeof(info);
+ info.hItem = HANDLE(pRow->hContact);
+ Tipper_ShowTip(0, &info);
+ TTShowed = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CloseSrWindow(bool)
+{
+ if (!g_pDlg)
+ return false;
+
+ g_pDlg->Close();
+ return true;
+}
+
+int OpenSrWindow(const wchar_t *pwszPattern)
+{
+ if (g_pDlg) {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
+ if (wp.showCmd == SW_SHOWMINIMIZED)
+ g_pDlg->Show(SW_RESTORE);
+ SetForegroundWindow(g_pDlg->GetHwnd());
+ return true;
+ }
+
+ int count = 0;
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ count++;
+
+ // no even one visible column
+ if (count == 0)
+ return true;
+
+ g_plugin.LoadOptWnd();
+
+ auto *pDlg = new QSMainDlg(pwszPattern);
+ if (pDlg->PrepareToFill())
+ pDlg->Create();
+ else
+ delete pDlg;
+
+ return true;
+}