diff options
author | George Hazan <ghazan@miranda.im> | 2021-05-13 11:45:07 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2021-05-13 11:45:07 +0300 |
commit | b9abdafff215858880890ec5aaacd5ea9bb880f1 (patch) | |
tree | dfa5b0494b1698cbef370e0a35862e7037ea90d7 | |
parent | 1aecfbf8295912ccfc26bdbd9d745aed96966e66 (diff) |
welcome back to C++, QuickSearch
26 files changed, 4074 insertions, 0 deletions
diff --git a/bin15/mir_full.sln b/bin15/mir_full.sln index 70299c6dfb..6729aad9fa 100644 --- a/bin15/mir_full.sln +++ b/bin15/mir_full.sln @@ -447,6 +447,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewStory", "..\plugins\NewS EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DbChecker", "..\plugins\DbChecker\dbchecker.vcxproj", "{A2E9DA24-95E4-4414-94AF-488A382E276A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QuickSearch", "..\plugins\QuickSearch\quicksearch.vcxproj", "{3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -2013,6 +2015,14 @@ Global {A2E9DA24-95E4-4414-94AF-488A382E276A}.Release|Win32.Build.0 = Release|Win32 {A2E9DA24-95E4-4414-94AF-488A382E276A}.Release|x64.ActiveCfg = Release|x64 {A2E9DA24-95E4-4414-94AF-488A382E276A}.Release|x64.Build.0 = Release|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|Win32.ActiveCfg = Debug|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|Win32.Build.0 = Debug|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|x64.ActiveCfg = Debug|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|x64.Build.0 = Debug|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|Win32.ActiveCfg = Release|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|Win32.Build.0 = Release|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|x64.ActiveCfg = Release|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/bin16/mir_full.sln b/bin16/mir_full.sln index 0338d314b9..205fa66aad 100644 --- a/bin16/mir_full.sln +++ b/bin16/mir_full.sln @@ -447,6 +447,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewStory", "..\plugins\NewS EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DbChecker", "..\plugins\DbChecker\dbchecker.vcxproj", "{A2E9DA24-95E4-4414-94AF-488A382E276A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QuickSearch", "..\plugins\QuickSearch\quicksearch.vcxproj", "{3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -2013,6 +2015,14 @@ Global {A2E9DA24-95E4-4414-94AF-488A382E276A}.Release|Win32.Build.0 = Release|Win32 {A2E9DA24-95E4-4414-94AF-488A382E276A}.Release|x64.ActiveCfg = Release|x64 {A2E9DA24-95E4-4414-94AF-488A382E276A}.Release|x64.Build.0 = Release|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|Win32.ActiveCfg = Debug|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|Win32.Build.0 = Debug|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|x64.ActiveCfg = Debug|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Debug|x64.Build.0 = Debug|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|Win32.ActiveCfg = Release|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|Win32.Build.0 = Release|Win32 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|x64.ActiveCfg = Release|x64 + {3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/plugins/QuickSearch/quicksearch.vcxproj b/plugins/QuickSearch/quicksearch.vcxproj new file mode 100644 index 0000000000..c61516a7d4 --- /dev/null +++ b/plugins/QuickSearch/quicksearch.vcxproj @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectName>QuickSearch</ProjectName> + <ProjectGuid>{3C3EAFC2-01FB-4BA8-8E47-42E0969C0D75}</ProjectGuid> + </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(ProjectDir)..\..\build\vc.common\plugin.props" /> + </ImportGroup> + <ItemGroup> + <ClCompile Include="src\frame.cpp" /> + <ClCompile Include="src\options.cpp" /> + <ClCompile Include="src\main.cpp" /> + <ClCompile Include="src\stdafx.cxx"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="src\utils.cpp" /> + <ClCompile Include="src\window.cpp" /> + <ClCompile Include="src\window_misc.cpp" /> + <ClCompile Include="src\window_row.cpp" /> + <ClInclude Include="src\resource.h" /> + <ClInclude Include="src\stdafx.h" /> + <ClInclude Include="src\version.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="res\resource.rc" /> + <ResourceCompile Include="res\version.rc" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/plugins/QuickSearch/quicksearch.vcxproj.filters b/plugins/QuickSearch/quicksearch.vcxproj.filters new file mode 100644 index 0000000000..d03459b649 --- /dev/null +++ b/plugins/QuickSearch/quicksearch.vcxproj.filters @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" /> + <ItemGroup> + <ClCompile Include="src\options.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\stdafx.cxx"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\frame.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\window.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\utils.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\window_misc.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="src\window_row.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="src\resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\stdafx.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="src\version.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="res\version.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + <ResourceCompile Include="res\resource.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/plugins/QuickSearch/res/default.ico b/plugins/QuickSearch/res/default.ico Binary files differnew file mode 100644 index 0000000000..59707dab07 --- /dev/null +++ b/plugins/QuickSearch/res/default.ico diff --git a/plugins/QuickSearch/res/delete.ico b/plugins/QuickSearch/res/delete.ico Binary files differnew file mode 100644 index 0000000000..c2ab44c6eb --- /dev/null +++ b/plugins/QuickSearch/res/delete.ico diff --git a/plugins/QuickSearch/res/down.ico b/plugins/QuickSearch/res/down.ico Binary files differnew file mode 100644 index 0000000000..79ec3929df --- /dev/null +++ b/plugins/QuickSearch/res/down.ico diff --git a/plugins/QuickSearch/res/female.ico b/plugins/QuickSearch/res/female.ico Binary files differnew file mode 100644 index 0000000000..581ff41d3b --- /dev/null +++ b/plugins/QuickSearch/res/female.ico diff --git a/plugins/QuickSearch/res/male.ico b/plugins/QuickSearch/res/male.ico Binary files differnew file mode 100644 index 0000000000..b638465f43 --- /dev/null +++ b/plugins/QuickSearch/res/male.ico diff --git a/plugins/QuickSearch/res/new.ico b/plugins/QuickSearch/res/new.ico Binary files differnew file mode 100644 index 0000000000..ece3037034 --- /dev/null +++ b/plugins/QuickSearch/res/new.ico diff --git a/plugins/QuickSearch/res/qs.ico b/plugins/QuickSearch/res/qs.ico Binary files differnew file mode 100644 index 0000000000..f8c5526cfb --- /dev/null +++ b/plugins/QuickSearch/res/qs.ico diff --git a/plugins/QuickSearch/res/reload.ico b/plugins/QuickSearch/res/reload.ico Binary files differnew file mode 100644 index 0000000000..feadf04bf2 --- /dev/null +++ b/plugins/QuickSearch/res/reload.ico diff --git a/plugins/QuickSearch/res/resource.rc b/plugins/QuickSearch/res/resource.rc new file mode 100644 index 0000000000..e1de82739e --- /dev/null +++ b/plugins/QuickSearch/res/resource.rc @@ -0,0 +1,182 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\src\resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1251) + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_FRAME DIALOGEX 0, 0, 114, 16 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + PUSHBUTTON "*",IDC_FRAME_OPEN,2,2,12,12,BS_ICON + EDITTEXT IDC_FRAME_EDIT,16,2,96,12 +END + +IDD_OPTIONS DIALOGEX 0, 0, 314, 250 +STYLE DS_SETFONT | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "Ms Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "Reload",IDC_RELOAD,"MButtonClass",WS_TABSTOP,2,148,16,16,WS_EX_NOACTIVATE | 0x10000000L + CONTROL "New",IDC_NEW,"MButtonClass",WS_TABSTOP,32,148,16,16,WS_EX_NOACTIVATE | 0x10000000L + CONTROL "Up",IDC_UP,"MButtonClass",WS_TABSTOP,62,148,16,16,WS_EX_NOACTIVATE | 0x10000000L + CONTROL "Down",IDC_DN,"MButtonClass",WS_TABSTOP,80,148,16,16,WS_EX_NOACTIVATE | 0x10000000L + CONTROL "Delete",IDC_DELETE,"MButtonClass",WS_TABSTOP,112,148,16,16,WS_EX_NOACTIVATE | 0x10000000L + CONTROL "Default",IDC_DEFAULT,"MButtonClass",WS_TABSTOP,142,148,16,16,WS_EX_NOACTIVATE | 0x10000000L + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,0,2,158,144 + PUSHBUTTON ">>",IDC_B_RESIZE,160,2,10,246,BS_FLAT + CTEXT "Settings",IDC_S_COLSETTING,172,2,139,12,SS_CENTERIMAGE + CONTROL "",IDC_S_LINE,"Static",SS_ETCHEDHORZ,172,14,139,2 + LTEXT "Title:",IDC_S_TITLE,174,16,137,12,SS_CENTERIMAGE + EDITTEXT IDC_E_TITLE,172,28,139,14,ES_AUTOHSCROLL + LTEXT "Type:",IDC_S_VARTYPE,174,44,137,12,SS_CENTERIMAGE + COMBOBOX IDC_C_VARTYPE,172,56,139,80,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Data type:",IDC_S_DATATYPE,174,72,137,12,SS_CENTERIMAGE + COMBOBOX IDC_C_DATATYPE,172,84,139,80,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Module:",IDC_S_MODULE,174,104,137,12,SS_CENTERIMAGE + EDITTEXT IDC_E_MODULE,172,116,139,14,ES_AUTOHSCROLL + LTEXT "Setting:",IDC_S_SETTING,174,132,137,12,SS_CENTERIMAGE + EDITTEXT IDC_E_SETTING,172,144,139,14,ES_AUTOHSCROLL + LTEXT "InfoType:",IDC_S_CNFTYPE,174,72,137,12,SS_CENTERIMAGE + COMBOBOX IDC_C_CNFTYPE,172,84,139,80,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_E_SCRIPT,172,72,139,14,ES_AUTOHSCROLL + COMBOBOX IDC_C_OTHER,172,72,139,150,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Save",IDC_SETITEM,259,234,52,14 + CONTROL "Tool Window Style",IDC_CH_USETOOLSTYLE,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,3,176,152,12 + CONTROL "Draw Grid",IDC_CH_DRAWGRID,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,3,188,152,12 + CONTROL "Save search pattern",IDC_CH_SAVEPATTERN,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,3,200,152,12 + CONTROL "Auto Close mode",IDC_CH_AUTOCLOSE,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,3,212,152,12 + CONTROL "Sort by Status",IDC_CH_SORTSTATUS,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,3,224,152,12 + CONTROL "Show Client Icons",IDC_CH_CLIENTICONS,"Button",BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,3,236,152,12 + GROUPBOX "Additional Options",IDC_CH_GROUP,0,168,158,82 +END + +IDD_MAIN DIALOGEX 40, 40, 520, 240 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "Ms Shell Dlg", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_E_SEARCHTEXT,180,8,276,14 + PUSHBUTTON "Close",IDCANCEL,462,0,52,14 + PUSHBUTTON "Refresh",IDC_REFRESH,462,16,52,14 + CONTROL "Show Offline contacts",IDC_CH_SHOWOFFLINE,"Button",BS_AUTOCHECKBOX | BS_VCENTER | BS_MULTILINE | BS_FLAT | WS_TABSTOP,2,0,96,20 + CONTROL "Colorize",IDC_CH_COLORIZE,"Button",BS_AUTOCHECKBOX | BS_VCENTER | BS_MULTILINE | BS_FLAT | WS_TABSTOP,2,20,96,10 + COMBOBOX IDC_CB_PROTOCOLS,100,8,76,80,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_NOLABELWRAP | WS_BORDER | WS_TABSTOP,2,32,516,194 + CONTROL "",IDC_STATUSBAR,"msctls_statusbar32",0x903,0,226,520,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_DEFAULT ICON "default.ico" + +IDI_DELETE ICON "delete.ico" + +IDI_DOWN ICON "down.ico" + +IDI_FEMALE ICON "female.ico" + +IDI_MALE ICON "male.ico" + +IDI_NEW ICON "new.ico" + +IDI_QS ICON "qs.ico" + +IDI_RELOAD ICON "reload.ico" + +IDI_UP ICON "up.ico" + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\src\\resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTIONS, DIALOG + BEGIN + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_OPTIONS AFX_DIALOG_LAYOUT +BEGIN + 0 +END + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/QuickSearch/res/up.ico b/plugins/QuickSearch/res/up.ico Binary files differnew file mode 100644 index 0000000000..6efc4d7257 --- /dev/null +++ b/plugins/QuickSearch/res/up.ico diff --git a/plugins/QuickSearch/res/version.rc b/plugins/QuickSearch/res/version.rc new file mode 100644 index 0000000000..5a5ddd63ed --- /dev/null +++ b/plugins/QuickSearch/res/version.rc @@ -0,0 +1,9 @@ +// Microsoft Visual C++ generated resource script. +// +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include "..\src\version.h" + +#include "..\..\build\Version.rc" diff --git a/plugins/QuickSearch/src/frame.cpp b/plugins/QuickSearch/src/frame.cpp new file mode 100644 index 0000000000..2674ebcdb2 --- /dev/null +++ b/plugins/QuickSearch/src/frame.cpp @@ -0,0 +1,181 @@ +/* +Copyright (C) 2012-21 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h" + +#define colorName "Frame background" + +static HWND hwndFrame = nullptr; +static int frameId = -1; +static WNDPROC OldEditProc; +static CMStringW wszPattern; +static HBRUSH hBrush; +static COLORREF frm_bkg; + +///////////////////////////////////////////////////////////////////////////////////////// +// frame window procedure + +static INT_PTR CALLBACK NewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CHAR: + if (wParam == 27) { // Escape + SendMessage(hwnd, WM_SETTEXT, 0, 0); + return 0; + } + if (wParam == 13) { // Enter + CallService(QS_SHOWSERVICE, WPARAM(wszPattern.c_str()), 0); + return 0; + } + } + return CallWindowProc(OldEditProc, hwnd, msg, wParam, lParam); +} + +static int Resizer(HWND, LPARAM, UTILRESIZECONTROL *urc) +{ + switch (urc->wId) { + case IDC_FRAME_OPEN: return RD_ANCHORX_LEFT | RD_ANCHORY_CENTRE; + case IDC_FRAME_EDIT: return RD_ANCHORX_WIDTH | RD_ANCHORY_CENTRE; + } + return 0; +} + +static INT_PTR CALLBACK FrameWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + RECT rc; + HWND hwndTooltip; + + switch (msg) { + case WM_INITDIALOG: + OldEditProc = (WNDPROC)SetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_FRAME_EDIT), GWLP_WNDPROC, LPARAM(&NewEditProc)); + + hwndTooltip = CreateWindowW(TOOLTIPS_CLASS, nullptr, TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, 0, g_plugin.getInst(), 0); + { + TOOLINFOW ti = {}; + ti.cbSize = sizeof(ti); + ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + ti.hwnd = hwndDlg; + ti.hinst = g_plugin.getInst(); + ti.uId = (UINT_PTR)GetDlgItem(hwndDlg, IDC_FRAME_OPEN); + ti.lpszText = TranslateT("Open QS window"); + SendMessageW(hwndTooltip, TTM_ADDTOOLW, 0, LPARAM(&ti)); + } + SendMessage(GetDlgItem(hwndDlg, IDC_FRAME_OPEN), BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_QS)); + return TRUE; + + case WM_DESTROY: + DeleteObject(hBrush); + hBrush = nullptr; + break; + + case WM_SIZE: + Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FRAME), Resizer); + break; + + case WM_ERASEBKGND: + GetClientRect(hwndDlg, &rc); + FillRect(HDC(wParam), &rc, hBrush); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == EN_CHANGE) { + wchar_t str[100]; + int length = GetDlgItemTextW(hwndDlg, IDC_FRAME_EDIT, str, _countof(str)); + if (length > 0) { + CharLowerW(str); + wszPattern = str; + } + else wszPattern.Empty(); + } + else if (wParam == IDC_FRAME_OPEN) { + CallService(QS_SHOWSERVICE, WPARAM(wszPattern.c_str()), 0); + } + break; + } + + return DefWindowProc(hwndDlg, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int OnChangeColors(WPARAM, LPARAM) +{ + frm_bkg = Colour_Get("QuickSearch", colorName); + + DeleteObject(hBrush); + hBrush = CreateSolidBrush(frm_bkg); + return 0; +} + +void CreateFrame(HWND hwndParent) +{ + if (!ServiceExists(MS_CLIST_FRAMES_ADDFRAME)) + return; + + if (hwndParent) + hwndParent = g_clistApi.hwndContactList; + + if (hwndFrame == nullptr) + hwndFrame = CreateDialogW(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FRAME), hwndParent, FrameWndProc); + + if (hwndFrame == nullptr) + return; + + RECT tr; + GetWindowRect(hwndFrame, &tr); + + CLISTFrame Frame = { sizeof(Frame) }; + Frame.hWnd = hwndFrame; + Frame.hIcon = g_plugin.getIcon(IDI_QS); + Frame.align = alTop; + Frame.height = tr.bottom - tr.top + 2; + Frame.Flags = F_NOBORDER | F_UNICODE; + Frame.szName.w = TranslateT("Quick search"); + + frameId = CallService(MS_CLIST_FRAMES_ADDFRAME, WPARAM(&Frame), LPARAM(&g_plugin)); + if (frameId <= 0) + return; + + CallService(MS_CLIST_FRAMES_UPDATEFRAME, frameId, FU_FMPOS); + + HWND wnd = g_clistApi.hwndContactList; + DWORD tmp = SendMessage(wnd, CLM_GETEXSTYLE, 0, 0); + SendMessage(wnd, CLM_SETEXSTYLE, tmp | CLS_EX_SHOWSELALWAYS, 0); + + ColourID cid; + strncpy_s(cid.group, MODULENAME, _TRUNCATE); + strncpy_s(cid.dbSettingsGroup, MODULENAME, _TRUNCATE); + strncpy_s(cid.name, colorName, _TRUNCATE); + strncpy_s(cid.setting, "frame_back", _TRUNCATE); + cid.defcolour = GetSysColor(COLOR_3DFACE); + cid.order = 0; + g_plugin.addColor(&cid); + + HookEvent(ME_COLOUR_RELOAD, &OnChangeColors); + OnChangeColors(0,0); +} + +void DestroyFrame() +{ + if (frameId >= 0) { + CallService(MS_CLIST_FRAMES_REMOVEFRAME, frameId, 0); + frameId = -1; + } + + DestroyWindow(hwndFrame); + hwndFrame = nullptr; +} diff --git a/plugins/QuickSearch/src/main.cpp b/plugins/QuickSearch/src/main.cpp new file mode 100644 index 0000000000..fef90535c5 --- /dev/null +++ b/plugins/QuickSearch/src/main.cpp @@ -0,0 +1,190 @@ +/* +Copyright (C) 2012-21 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h" + +CMPlugin g_plugin; + +HANDLE hTTBButton; + +bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled; + +int OnOptInit(WPARAM, LPARAM); + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C} + {0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}} +}; + +CMPlugin::CMPlugin() : + PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx), + m_columns(1) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int OnTTBLoaded(WPARAM, LPARAM) +{ + TTBButton ttb = {}; + ttb.dwFlags = TTBBF_VISIBLE; + ttb.pszService = QS_SHOWSERVICE; + ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS); + ttb.name = MODULENAME; + ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search"); + hTTBButton = g_plugin.addTTB(&ttb); + return 0; +} + +static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM) +{ + OpenSrWindow((wchar_t *)wParam); + return 0; +} + +static int OnCheckPlugins(WPARAM, LPARAM) +{ + g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING); + g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW); + g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW); + + return 0; +} + +static int OnModulesLoaded(WPARAM, LPARAM) +{ + HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded); + + CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow); + + // add menu item + CMenuItem mi(&g_plugin); + SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0); + mi.name.a = "Quick Search"; + mi.position = 500050000; + mi.pszService = QS_SHOWSERVICE; + mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS); + Menu_AddMainMenuItem(&mi); + + // register hotkey + HOTKEYDESC hkd = {}; + hkd.pszName = "QS_Global"; + hkd.szDescription.a = "QuickSearch window hotkey"; + hkd.szSection.a = "QuickSearch"; + hkd.pszService = QS_SHOWSERVICE; + hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3); + g_plugin.addHotkey(&hkd); + + CreateFrame(0); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static IconItem iconList[] = +{ + { "Quick Search", "QS", IDI_QS }, + { "New Column", "New", IDI_NEW }, + { "Column Up", "Up", IDI_UP }, + { "Column Down", "Down", IDI_DOWN }, + { "Delete Column", "Delete", IDI_DELETE }, + { "Default", "Default", IDI_DEFAULT}, + { "Reload", "Reload", IDI_RELOAD }, + { "Male", "Male", IDI_MALE }, + { "Female", "Female", IDI_FEMALE }, +}; + +struct +{ + COLORREF defValue; + const char *szSetting, *szDescr; +} +static sttColors[color_max] = { + { 0x00FFFFFF, "back_norm", LPGEN("Normal background") }, + { 0x00000000, "fore_norm", LPGEN("Normal foreground") }, + { 0x00EBE6DE, "back_odd" , LPGEN("Odd background") }, + { 0x00000000, "fore_odd" , LPGEN("Odd foreground") }, + { 0x008080FF, "back_dis" , LPGEN("Disabled account background") }, + { 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") }, + { 0x008000FF, "back_del" , LPGEN("Deleted account background") }, + { 0x00000000, "fore_del" , LPGEN("Deleted account foreground") }, + { 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background") }, + { 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground") }, + { 0x00BAE699, "back_meta", LPGEN("Metacontact background") }, + { 0x00000000, "fore_meta", LPGEN("Metacontact foreground") }, + { 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background") }, + { 0x00000000, "fore_sub" , LPGEN("Subcontact foreground") }, +}; + +static int OnColorReload(WPARAM, LPARAM) +{ + for (int i = 0; i < color_max; i++) + g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szSetting); + return 0; +} + +int CMPlugin::Load() +{ + g_plugin.registerIcon(MODULENAME, iconList); + + ColourID colourid = {}; + strncpy_s(colourid.group, MODULENAME, _TRUNCATE); + strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE); + + for (auto &it : sttColors) { + strncpy_s(colourid.name, it.szDescr, _TRUNCATE); + strncpy_s(colourid.setting, it.szSetting, _TRUNCATE); + colourid.defcolour = it.defValue; + colourid.order = int(&it - sttColors); + g_plugin.addColor(&colourid); + } + OnColorReload(0, 0); + OnCheckPlugins(0, 0); + + HookEvent(ME_COLOUR_RELOAD, OnColorReload); + HookEvent(ME_OPT_INITIALISE, OnOptInit); + HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); + HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins); + HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins); + + LoadColumns(m_columns); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Unload() +{ + if (hTTBButton) { + CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0); + hTTBButton = 0; + } + + DestroyFrame(); + CloseSrWindow(); + return 0; +} diff --git a/plugins/QuickSearch/src/options.cpp b/plugins/QuickSearch/src/options.cpp new file mode 100644 index 0000000000..c8b7320dbf --- /dev/null +++ b/plugins/QuickSearch/src/options.cpp @@ -0,0 +1,519 @@ +/* +Copyright (C) 2012-21 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h" + +class COptionsDlg : public CDlgBase +{ + OBJLIST<ColumnItem> m_columns; + + void AddColumn(int idx, ColumnItem *pCol) + { + LVITEM lvi = {}; + lvi.mask = LVIF_PARAM; + lvi.iItem = idx; + lvi.lParam = LPARAM(pCol); + m_list.InsertItem(&lvi); + } + + void CheckDirection(int iItem) + { + btnUp.Enable(iItem > 0); + btnDown.Enable(iItem < m_list.GetItemCount()-1); + } + + void ClearScreen() + { + // setting + ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE); + + // contact info + ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE); + + // others + ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE); + } + + void DisplayCurInfo(const ColumnItem *pCol) + { + ClearScreen(); + SetupScreen(pCol->setting_type); + + editTitle.SetText(pCol->title); + cmbVarType.SelectData(pCol->setting_type); + + switch (pCol->setting_type) { + case QST_SETTING: + cmbDataType.SelectData(pCol->datatype); + editModule.SetTextA(pCol->module); + editSetting.SetTextA(pCol->setting); + break; + + case QST_SCRIPT: + SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script); + break; + + case QST_CONTACTINFO: + cmbCnfType.SelectData(pCol->cnftype); + break; + + case QST_OTHER: + cmbOther.SelectData(pCol->other); + break; + } + } + + void FillTableLine(int item, ColumnItem *pColumn) + { + m_list.SetItemText(item, 1, pColumn->title); + + switch (pColumn->setting_type) { + case QST_SETTING: + m_list.SetItemText(item, 2, _A2T(pColumn->module)); + m_list.SetItemText(item, 3, _A2T(pColumn->setting)); + break; + + case QST_SCRIPT: + m_list.SetItemText(item, 2, TranslateT("Script")); + break; + + case QST_SERVICE: + m_list.SetItemText(item, 2, TranslateT("Service")); + m_list.SetItemText(item, 3, _A2T(pColumn->svc.service)); + break; + + case QST_CONTACTINFO: + m_list.SetItemText(item, 2, TranslateT("Contact info")); + m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype)); + break; + + case QST_OTHER: + m_list.SetItemText(item, 2, TranslateT("Other")); + if (pColumn->other == QSTO_METACONTACT) + m_list.SetItemText(item, 3, TranslateT("Metacontact")); + break; + } + } + + void InitScreen() + { + // setting + cmbDataType.SetCurSel(0); + editModule.SetText(L""); + editSetting.SetText(L""); + + // contact info + cmbCnfType.SetCurSel(0); + + // others + cmbOther.SetCurSel(0); + } + + void ResizeControl(int id, int width) + { + HWND hwnd = GetDlgItem(m_hwnd, id); + RECT rc; + ::GetWindowRect(hwnd, &rc); + ::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); + } + + void SetupScreen(int type) + { + if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE))) + return; + + // setting + int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE; + ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd); + editModule.Show(cmd == SW_SHOW); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd); + editSetting.Show(cmd == SW_SHOW); + + // contact info + cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE; + ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd); + + // script + cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE; + ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd); + + // others + cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE; + ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd); + } + + void UpdateList() + { + m_list.DeleteAllItems(); + + int cnt = 0; + for (auto &it : m_columns) { + AddColumn(cnt, it); + FillTableLine(cnt, it); + m_list.SetCheckState(cnt, it->bEnabled); + cnt++; + } + + m_list.SetCurSel(0); + } + + CCtrlEdit editTitle, editModule, editSetting; + CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons; + CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType; + CCtrlListView m_list; + CCtrlButton btnSave, btnResize; + CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload; + +public: + COptionsDlg() : + CDlgBase(g_plugin, IDD_OPTIONS), + m_columns(1), + m_list(this, IDC_LIST), + btnSave(this, IDC_SETITEM), + btnResize(this, IDC_B_RESIZE), + btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")), + btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")), + btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")), + btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")), + btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")), + btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")), + editTitle(this, IDC_E_TITLE), + editModule(this, IDC_E_MODULE), + editSetting(this, IDC_E_SETTING), + cmbOther(this, IDC_C_OTHER), + cmbCnfType(this, IDC_C_CNFTYPE), + cmbVarType(this, IDC_C_VARTYPE), + cmbDataType(this, IDC_C_DATATYPE), + chkDrawGrid(this, IDC_CH_DRAWGRID), + chkAutoClose(this, IDC_CH_AUTOCLOSE), + chkSortStatus(this, IDC_CH_SORTSTATUS), + chkSavePattern(this, IDC_CH_SAVEPATTERN), + chkClientIcons(this, IDC_CH_CLIENTICONS), + chkUseToolstyle(this, IDC_CH_USETOOLSTYLE) + { + m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List); + + btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up); + btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New); + btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down); + btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save); + btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete); + btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload); + btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize); + btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default); + + cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var); + } + + bool OnInitDialog() override + { + editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent(); + cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent(); + + m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); + m_list.AddColumn(0, L"#", 20); + m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1")); + m_list.AddColumn(2, TranslateT("Module/InfoType"), g_plugin.getWord("col2")); + m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3")); + + cmbVarType.AddString(TranslateT("DB setting"), QST_SETTING); + cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT); + cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO); + cmbVarType.AddString(TranslateT("Other"), QST_OTHER); + cmbVarType.SetCurSel(0); + + cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE); + cmbDataType.AddString(TranslateT("Word"), QSTS_WORD); + cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD); + cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED); + cmbDataType.AddString(TranslateT("As hex"), QSTS_HEXNUM); + cmbDataType.AddString(TranslateT("String"), QSTS_STRING); + cmbDataType.AddString(TranslateT("TimeStamp"), QSTS_TIMESTAMP); + cmbDataType.SetCurSel(0); + + cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN); + cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT); + cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT); + cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT); + cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME); + cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT); + cmbOther.SetCurSel(0); + + for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++) + cmbCnfType.AddString(cnf2str(i), i); + cmbCnfType.SetCurSel(0); + + chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0); + chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0); + chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0); + chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0); + chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0); + chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0); + + // make local copy of column descriptions + for (auto &it : g_plugin.m_columns) + m_columns.insert(new ColumnItem(*it)); + + UpdateList(); + onClick_Resize(0); + if (m_columns.getCount()) + DisplayCurInfo(&m_columns[0]); + return true; + } + + bool OnApply() override + { + // checkboxes + g_plugin.m_flags &= ~QSO_MAINOPTIONS; + if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID; + if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE; + if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS; + if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS; + if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN; + if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE; + + int tmpbool = CloseSrWindow(false); + + g_plugin.m_columns.destroy(); + int nCount = m_list.GetItemCount(); + for (int i = 0; i < nCount; i++) { + auto *pCol = (ColumnItem *)m_list.GetItemData(i); + pCol->bEnabled = m_list.GetCheckState(i) != 0; + g_plugin.m_columns.insert(new ColumnItem(*pCol)); + } + + g_plugin.SaveOptions(); + + if (tmpbool) + OpenSrWindow(0); + return true; + } + + void OnDestroy() override + { + m_columns.destroy(); + + g_plugin.setWord("col1", m_list.GetColumnWidth(1)); + g_plugin.setWord("col2", m_list.GetColumnWidth(2)); + g_plugin.setWord("col3", m_list.GetColumnWidth(3)); + } + + void onClick_New(CCtrlButton *) + { + int idx = m_list.GetSelectionMark()+1; + auto *pNew = new ColumnItem(TranslateT("New column")); + m_columns.insert(pNew); + + AddColumn(idx, pNew); + m_list.EnsureVisible(idx, FALSE); + m_list.SetCurSel(idx); + InitScreen(); + CheckDirection(idx); + btnDelete.Enable(); + NotifyChange(); + } + + void onClick_Delete(CCtrlButton *) + { + int idx = m_list.GetSelectionMark(); + auto *pCol = (ColumnItem *)m_list.GetItemData(idx); + + m_list.DeleteItem(idx); + m_columns.remove(pCol); + + int nCount = m_list.GetItemCount(); + if (nCount == 0) { + m_list.Disable(); + InitScreen(); + } + else { + if (nCount == idx) + idx--; + m_list.SetCurSel(idx); + } + CheckDirection(idx); + NotifyChange(); + } + + void onClick_Up(CCtrlButton *) + { + int idx = m_list.GetSelectionMark(); + if (idx > 0) { + CheckDirection(m_list.MoveItem(idx, -1)); + NotifyChange(); + } + } + + void onClick_Down(CCtrlButton *) + { + int idx = m_list.GetSelectionMark(); + if (idx < m_list.GetItemCount() - 1) { + CheckDirection(m_list.MoveItem(idx, 1)); + NotifyChange(); + } + } + + void onClick_Reload(CCtrlButton *) + { + g_plugin.LoadColumns(m_columns); + UpdateList(); + } + + void onClick_Default(CCtrlButton *) + { + LoadDefaultColumns(m_columns); + UpdateList(); + NotifyChange(); + } + + void onClick_Save(CCtrlButton *) + { + if (m_list.GetItemCount() == 0) { + AddColumn(0, new ColumnItem(TranslateT("New column"))); + m_list.SetCurSel(0); + btnDelete.Enable(); + } + + int idx = m_list.GetSelectionMark(); + auto *pCol = (ColumnItem *)m_list.GetItemData(idx); + pCol->dwFlags = 0; + if (m_list.GetCheckState(idx)) + pCol->bEnabled = pCol->bFilter = true; + pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel()); + replaceStrW(pCol->title, editTitle.GetText()); + + switch (pCol->setting_type) { + case QST_SETTING: + pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel()); + pCol->module = mir_u2a(ptrW(editModule.GetText())); + pCol->setting = mir_u2a(ptrW(editSetting.GetText())); + break; + + case QST_CONTACTINFO: + pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel()); + break; + + case QST_OTHER: + pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel()); + break; + } + + FillTableLine(idx, pCol); + NotifyChange(); + } + + void onClick_Resize(CCtrlButton *) + { + wchar_t *pcw; + int dx, rside; + + HWND wSeparator = GetDlgItem(m_hwnd,IDC_B_RESIZE); + RECT rc, rc1; + GetClientRect(m_hwnd, &rc); + GetWindowRect(wSeparator, &rc1); + + POINT pt = { rc1.left, 0 }; + ScreenToClient(m_hwnd, &pt); + if (pt.x < (rc.right - 50)) { + rside = SW_HIDE; + dx = rc.right - (rc1.right - rc1.left) - 4; + pcw = L"<"; + } + else { + rside = SW_SHOW; + + GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc); + pt.x = rc.left; + pt.y = 0; + ScreenToClient(m_hwnd, &pt); + dx = pt.x - (rc1.right - rc1.left) - 4; + pcw = L">"; + } + + SendMessageW(wSeparator, WM_SETTEXT, 0, LPARAM(pcw)); + + // move separator button + SetWindowPos(wSeparator,0,dx+2,2,0,0,SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); + + // resize left side controls + ResizeControl(IDC_LIST, dx); + ResizeControl(IDC_CH_GROUP, dx); + + ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8); + ResizeControl(IDC_CH_DRAWGRID, dx - 8); + ResizeControl(IDC_CH_SAVEPATTERN, dx - 8); + ResizeControl(IDC_CH_AUTOCLOSE, dx - 8); + ResizeControl(IDC_CH_SORTSTATUS, dx - 8); + ResizeControl(IDC_CH_CLIENTICONS, dx - 8); + + // show/hide setting block (ugly, i know!) + ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside); + ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside); + + ClearScreen(); + if (rside == SW_SHOW) + SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel())); + } + + void onItemChanged_List(CCtrlListView::TEventInfo *ev) + { + auto *nmlv = ev->nmlv; + // we got new focus + if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) { + CheckDirection(nmlv->iItem); + InitScreen(); + DisplayCurInfo((ColumnItem*)nmlv->lParam); + } + } + + void onSelChanged_Var(CCtrlCombo *pCombo) + { + SetupScreen(pCombo->GetItemData(pCombo->GetCurSel())); + } +}; + +int OnOptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS; + odp.szGroup.a = LPGEN("Contacts"); + odp.szTitle.a = LPGEN("Quick Search"); + odp.position = 900003000; + odp.pDialog = new COptionsDlg(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/plugins/QuickSearch/src/resource.h b/plugins/QuickSearch/src/resource.h new file mode 100644 index 0000000000..8058dc6038 --- /dev/null +++ b/plugins/QuickSearch/src/resource.h @@ -0,0 +1,67 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by w:\miranda-ng\plugins\QuickSearch\res\resource.rc +// +#define IDI_DEFAULT 101 +#define IDI_DELETE 102 +#define IDI_DOWN 103 +#define IDI_FEMALE 104 +#define IDI_MALE 105 +#define IDI_NEW 106 +#define IDI_QS 107 +#define IDI_RELOAD 108 +#define IDI_UP 109 +#define IDD_MAIN 110 +#define IDD_OPTIONS 111 +#define IDD_FRAME 112 +#define IDC_FRAME_OPEN 1001 +#define IDC_FRAME_EDIT 1002 +#define IDC_RELOAD 1003 +#define IDC_NEW 1004 +#define IDC_UP 1005 +#define IDC_DN 1006 +#define IDC_DELETE 1007 +#define IDC_DEFAULT 1008 +#define IDC_LIST 1009 +#define IDC_B_RESIZE 1010 +#define IDC_S_COLSETTING 1011 +#define IDC_S_LINE 1012 +#define IDC_S_TITLE 1013 +#define IDC_E_TITLE 1014 +#define IDC_S_VARTYPE 1015 +#define IDC_C_VARTYPE 1016 +#define IDC_S_DATATYPE 1017 +#define IDC_C_DATATYPE 1018 +#define IDC_S_MODULE 1019 +#define IDC_E_MODULE 1020 +#define IDC_S_SETTING 1021 +#define IDC_E_SETTING 1022 +#define IDC_S_CNFTYPE 1023 +#define IDC_C_CNFTYPE 1024 +#define IDC_E_SCRIPT 1025 +#define IDC_C_OTHER 1026 +#define IDC_SETITEM 1027 +#define IDC_CH_USETOOLSTYLE 1028 +#define IDC_CH_DRAWGRID 1029 +#define IDC_CH_SAVEPATTERN 1030 +#define IDC_CH_AUTOCLOSE 1031 +#define IDC_CH_SORTSTATUS 1032 +#define IDC_CH_CLIENTICONS 1033 +#define IDC_CH_GROUP 1034 +#define IDC_E_SEARCHTEXT 1035 +#define IDC_REFRESH 1036 +#define IDC_CH_SHOWOFFLINE 1037 +#define IDC_CB_PROTOCOLS 1038 +#define IDC_STATUSBAR 1039 +#define IDC_CH_COLORIZE 1040 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1032 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/QuickSearch/src/stdafx.cxx b/plugins/QuickSearch/src/stdafx.cxx new file mode 100644 index 0000000000..20c49240b6 --- /dev/null +++ b/plugins/QuickSearch/src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-21 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h"
\ No newline at end of file diff --git a/plugins/QuickSearch/src/stdafx.h b/plugins/QuickSearch/src/stdafx.h new file mode 100644 index 0000000000..591ac22596 --- /dev/null +++ b/plugins/QuickSearch/src/stdafx.h @@ -0,0 +1,417 @@ +#include <windows.h> +#include <CommCtrl.h> +#include <stdio.h> +#include <malloc.h> + +#include <newpluginapi.h> +#include <m_clistint.h> +#include <m_cluiframes.h> +#include <m_contacts.h> +#include <m_database.h> +#include <m_findadd.h> +#include <m_fingerprint.h> +#include <m_fontservice.h> +#include <m_hotkeys.h> +#include <m_genmenu.h> +#include <m_gui.h> +#include <m_langpack.h> +#include <m_metacontacts.h> +#include <m_netlib.h> +#include <m_options.h> +#include <m_popup.h> +#include <m_protocols.h> +#include <m_protosvc.h> +#include <m_skin.h> +#include <m_timezones.h> +#include <m_tipper.h> +#include <m_toptoolbar.h> +#include <m_utils.h> +#include <m_variables.h> +#include <m_xstatus.h> + +#include "resource.h" +#include "version.h" + +#define MODULENAME "QuickSearch" +#define QS_SHOWSERVICE "QuickSearch/Show" + +#define StatusSort 1000 + +#define QSTS_BYTE 0 +#define QSTS_WORD 1 +#define QSTS_DWORD 2 +#define QSTS_STRING 3 +#define QSTS_IP 4 +#define QSTS_TIMESTAMP 5 +#define QSTS_SIGNED 6 +#define QSTS_HEXNUM 7 + +// must be non-zero for empty-column checking +#define QST_SETTING 100 +#define QST_SCRIPT 1 +#define QST_SERVICE 2 +#define QST_CONTACTINFO 3 +#define QST_OTHER 200 + +#define QSTO_LASTSEEN 0 +#define QSTO_LASTEVENT 1 +#define QSTO_METACONTACT 2 +#define QSTO_EVENTCOUNT 3 +#define QSTO_DISPLAYNAME 4 +#define QSTO_ACCOUNT 5 + +#define QSO_SORTBYSTATUS 0x00000001 // Sort by status +#define QSO_DRAWGRID 0x00000002 // Draw listview grid +#define QSO_TOOLSTYLE 0x00000004 // QS window tool style +#define QSO_SAVEPATTERN 0x00000008 // Save filter pattern +#define QSO_AUTOCLOSE 0x00000010 // Close QS window after action +#define QSO_CLIENTICONS 0x00000020 // Show client icons (fingerprint) +#define QSO_MAINOPTIONS 0x0000FFFF // mask for common options + +// QS window options +#define QSO_STAYONTOP 0x00010000 // Stay QS window on top +#define QSO_SHOWOFFLINE 0x00020000 // Show offline contacts +#define QSO_COLORIZE 0x00040000 // Colorize lines +#define QSO_SORTASC 0x00080000 // Sort column ascending + +struct ServiceDescr +{ + char *service; + LPARAM wParam, lParam; + DWORD wFlags, lFlags, flags; +}; + +struct ColumnItem : public MZeroedObject +{ + ColumnItem(const wchar_t *pszTitle, int _width = 64, int _setting_type = QST_SETTING); + ColumnItem(const ColumnItem &src); + ~ColumnItem(); + + void SetSpecialColumns(); + + wchar_t *title; + uint16_t setting_type = 0; // QST_* constants + uint16_t width = 0; + + union + { + uint32_t dwFlags = 0; + struct { + bool bEnabled : 1; + bool bInit : 1; + bool bFilter : 1; + bool isXstatus : 1; + bool isGender : 1; + bool isClient : 1; + bool isGroup : 1; + bool isContainer : 1; + }; + }; + + + union { + // 0: db setting + struct { + uint16_t datatype; // QSTS_* constants + char *module; + char *setting; + }; + + // 1: script + wchar_t *script; + + // 2: service + ServiceDescr svc; + + // 3: contact info + int cnftype; // CNF_* constants + + // 4: other + int other; // QSTO_* constants + }; +}; + +struct ContactIntoColumn : public ColumnItem +{ + ContactIntoColumn(const wchar_t *pwszTitle, int width, int _type) : + ColumnItem(pwszTitle, width, QST_CONTACTINFO) + { + cnftype = _type; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#define so_mbottom "mbottom" +#define so_mright "mright" +#define so_mtop "mtop" +#define so_mleft "mleft" + +#define so_columnsort "columnsort" +#define so_flags "flags" + +#define so_numcolumns "numcolumns" +#define so_item "item" + +#define so_title "title" +#define so_width "width" +#define so_flags "flags" +#define so_setting_type "setting_type" + +#define so_cnftype "cnftype" + +#define so_datatype "datatype" +#define so_module "module" +#define so_setting "setting" + +#define so_script "script" + +#define so_service "service" +#define so_restype "flags" +#define so_wparam "wparam" +#define so_lparam "lparam" + +#define so_other "other" + +///////////////////////////////////////////////////////////////////////////////////////// + +enum ColorCode +{ + bkg_norm, fgr_norm, + bkg_odd, fgr_odd, + bkg_dis, fgr_dis, + bkg_del, fgr_del, + bkg_hid, fgr_hid, + bkg_meta, fgr_meta, + bkg_sub, fgr_sub, + color_max +}; + +struct CMPlugin : public PLUGIN<CMPlugin> +{ + CMPlugin(); + + COLORREF m_colors[color_max]; + RECT m_rect; + OBJLIST<ColumnItem> m_columns; + int m_sortOrder; + DWORD m_flags; // QSO_* constants + + void LoadOptWnd(); + void SaveOptWnd(); + + void LoadColumns(OBJLIST<ColumnItem>&); + void LoadColumn(int n, ColumnItem &col); + void LoadParamValue(char *buf, DWORD &flags, LPARAM &value); + + void SaveOptions(); + void SaveColumn(int n, const ColumnItem &col); + void SaveParamValue(char *buf, DWORD flags, LPARAM value); + + int Load() override; + int Unload() override; +}; + +extern bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled; + +///////////////////////////////////////////////////////////////////////////////////////// +// frame.cpp + +void CreateFrame(HWND); +void DestroyFrame(); + +///////////////////////////////////////////////////////////////////////////////////////// +// utils.cpp + +int ColumnToListView(int col); +int ListViewToColumn(int col); +void LoadDefaultColumns(OBJLIST<ColumnItem> &dst); + +void SnapToScreen(RECT &rc); + +const wchar_t* cnf2str(int); + +wchar_t* BuildLastSeenTime(DWORD timestamp); +DWORD BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule); +wchar_t* TimeToStrW(DWORD timestamp); + +///////////////////////////////////////////////////////////////////////////////////////// +// windows.cpp + +struct QSFlags +{ + union { + DWORD flags = 0; + struct { + bool bInList : 1; // in constant list + bool bActive : 1; // contact in listview + bool bDeleted : 1; // contact deleted + bool bPattern : 1; // pattern check passed + bool bAccDel : 1; // account deleted + bool bAccOff : 1; // account disabled + bool bIsMeta : 1; // contact is metacontact + bool bIsSub : 1; // contact is part of metacontact + }; + }; +}; + +struct CRowItem : public QSFlags +{ + CRowItem(MCONTACT _1, class QSMainDlg *pDlg); + ~CRowItem(); + + MCONTACT hContact; + const char *szProto; + + int status = ID_STATUS_OFFLINE; + WPARAM wparam = 0; + LPARAM lparam = 0; + + void GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText); + + struct Val + { + wchar_t *text = nullptr; + UINT_PTR data = UINT_PTR(-1); + + void LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, class QSMainDlg *pDlg); + } + *pValues; +}; + +// status bar data +struct CStatusBarItem : public QSFlags +{ + CStatusBarItem(const char *_proto, int _flags) : + szProto(_proto) + { + flags = _flags; + } + + const char *szProto; + + int total = 0; + int found = 0; + int online = 0; + int liston = 0; +}; + +class QSMainDlg : public CDlgBase +{ + friend struct CRowItem; + + OBJLIST<CRowItem> m_rows; + OBJLIST<CStatusBarItem> m_sbdata; + + UI_MESSAGE_MAP(QSMainDlg, CDlgBase); + UI_MESSAGE(WM_SYSCOMMAND, OnSysCommand); + UI_MESSAGE(WM_MOUSEMOVE, OnMouseMove); + UI_MESSAGE(WM_KEYDOWN, OnKeydown); + UI_MESSAGE_MAP_END(); + + // internal methods + void AddColumn(int idx, ColumnItem *pCol); + void AddContactToList(MCONTACT hContact, CRowItem *pRow); + void AdvancedFilter(); + void ConvertToMeta(); + void CopyMultiLines(); + void DeleteByList(); + void DeleteOneContact(MCONTACT hContact); + wchar_t* DoMeta(MCONTACT hContact); + void FillGrid(); + void FillProtoCombo(); + int FindItem(CRowItem *pRow); + int FindMeta(MCONTACT hMeta, WPARAM &metaNum); + CRowItem* FindRow(MCONTACT hContact); + MCONTACT GetFocusedContact(); + int GetLVSubItem(int x, int y); + void MoveToContainer(const wchar_t *pwszName); + void MoveToGroup(const wchar_t *pwszName); + void PrepareTable(bool bReset = false); + void ProcessLine(CRowItem *pRow, bool test = true); + void SaveColumnOrder(); + void ShowContactMenu(MCONTACT, int); + void ShowContactMsgDlg(MCONTACT hContact); + void ShowMultiPopup(int); + void Sort(); + void UpdateLVCell(int item, int column, const wchar_t *pwszText = nullptr); + + __forceinline CRowItem* GetRow(int i) { + return (CRowItem *)m_grid.GetItemData(i); + } + + // status bar + HWND hwndStatusBar = 0, HintWnd = 0; + void DrawSB(); + void UpdateSB(); + + // internal data + int hLastMeta; + bool TTShowed = false; + int tableColumns = 0; + HICON hIconF = 0, hIconM = 0; + HANDLE hAdd, hDelete, hChange; + HGENMENU mnuhandle = 0; + + // patterns + wchar_t *pattern = nullptr; // edit field text + wchar_t *patstr = nullptr; // edit field text + + struct + { + wchar_t *str; + bool res; + } + static patterns[8]; + + int numpattern = 0; + + bool bShowOffline; + char *szFilterProto = nullptr; + + bool CheckPattern(CRowItem *); + void MakePattern(); + + // controls + CTimer m_hover; + CCtrlEdit edtFilter; + CCtrlCheck chkShowOffline, chkColorize; + CCtrlCombo cmbProto; + CCtrlButton btnRefresh; + CCtrlListView m_grid; + +public: + QSMainDlg(const wchar_t *pattern); + + bool OnInitDialog() override; + void OnDestroy() override; + int Resizer(UTILRESIZECONTROL *urc) override; + + void ChangeCellValue(MCONTACT hContact, int col); + bool PrepareToFill(); + + INT_PTR OnSysCommand(UINT, WPARAM, LPARAM); + INT_PTR OnMouseMove(UINT, WPARAM, LPARAM); + INT_PTR OnKeydown(UINT, WPARAM, LPARAM); + + INT_PTR NewEditProc(UINT, WPARAM, LPARAM); + INT_PTR NewLVProc(UINT, WPARAM, LPARAM); + + void onBuildMenu_Grid(CContextMenuPos *pos); + void onColumnClick_Grid(CCtrlListView::TEventInfo *ev); + void onCustomDraw_Grid(CCtrlListView::TEventInfo *ev); + void onDblClick_Grid(CCtrlListView::TEventInfo *ev); + + void onSelChange_Proto(CCtrlCombo *); + + void onChange_Filter(CCtrlEdit *); + void onChange_ShowOffline(CCtrlCheck *); + void onChange_Colorize(CCtrlCheck *); + + void onClick_Refresh(CCtrlButton *); + + void onTimer_Hover(CTimer *); +}; + +int CloseSrWindow(bool = true); +int OpenSrWindow(const wchar_t *pwszPattern); diff --git a/plugins/QuickSearch/src/utils.cpp b/plugins/QuickSearch/src/utils.cpp new file mode 100644 index 0000000000..d8a2795d01 --- /dev/null +++ b/plugins/QuickSearch/src/utils.cpp @@ -0,0 +1,554 @@ +/* +Copyright (C) 2012-21 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h" + +#define ACF_TYPE_NUMBER 0x00 // Parameter is number +#define ACF_TYPE_STRING 0x01 // Parameter is ANSI String +#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string +#define ACF_TYPE_STRUCT 0x03 // Parameter is (result is in) structure +#define ACF_TYPE_PARAM 0x08 // Parameter is Call parameter +#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window +#define ACF_TYPE_RESULT 0x0A // Parameter is previous action result +#define ACF_TYPE_MASK 0x0F // parameter/result type mask + +ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) : + title(mir_wstrdup(pwszTitle)), + width(_width), + setting_type(_setting_type) +{ + bEnabled = true; +} + +ColumnItem::ColumnItem(const ColumnItem &src) +{ + memcpy(this, &src, sizeof(ColumnItem)); + + title = mir_wstrdup(title); + + switch (setting_type) { + case QST_SETTING: + module = mir_strdup(module); + setting = mir_strdup(setting); + break; + + case QST_SCRIPT: + script = mir_wstrdup(script); + break; + + case QST_SERVICE: + svc.service = mir_strdup(svc.service); + switch (svc.wFlags) { + case ACF_TYPE_NUMBER: + case ACF_TYPE_STRING: + case ACF_TYPE_UNICODE: + svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam); + break; + case ACF_TYPE_STRUCT: + svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam); + break; + } + + switch (svc.lFlags) { + case ACF_TYPE_NUMBER: + case ACF_TYPE_STRING: + case ACF_TYPE_UNICODE: + svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam); + break; + case ACF_TYPE_STRUCT: + svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam); + break; + } + break; + } +} + +ColumnItem::~ColumnItem() +{ + mir_free(title); + + switch (setting_type) { + case QST_SETTING: + mir_free(module); + mir_free(setting); + break; + + case QST_SCRIPT: + mir_free(script); + break; + + case QST_SERVICE: + mir_free(svc.service); + switch (svc.wFlags) { + case ACF_TYPE_NUMBER: + case ACF_TYPE_STRING: + case ACF_TYPE_UNICODE: + mir_free((wchar_t *)svc.wParam); + break; + case ACF_TYPE_STRUCT: + mir_free((char *)svc.wParam); + break; + } + + switch (svc.lFlags) { + case ACF_TYPE_NUMBER: + case ACF_TYPE_STRING: + case ACF_TYPE_UNICODE: + mir_free((wchar_t *)svc.lParam); + break; + case ACF_TYPE_STRUCT: + mir_free((char *)svc.lParam); + break; + } + break; + } +} + +void ColumnItem::SetSpecialColumns() +{ + if (setting_type == QST_SETTING) { + if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group")) + isGroup = true; + + else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW")) + isContainer = true; + + else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId")) + isXstatus = true; + + else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled) + isClient = true; + } + else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER) + isGender = true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// column functions + +int ListViewToColumn(int col) +{ + for (auto &it : g_plugin.m_columns) { + if (!it->bEnabled) + continue; + + if (col-- <= 0) + return g_plugin.m_columns.indexOf(&it); + } + return -1; +} + +int ColumnToListView(int col) +{ + int res = -1; + for (auto &it : g_plugin.m_columns) { + if (it->bEnabled) + res++; + + if (col-- <= 0) + break; + } + return res; +} + +void LoadDefaultColumns(OBJLIST<ColumnItem> &dst) +{ + dst.destroy(); + + auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER); + pNew->other = QSTO_ACCOUNT; + dst.insert(pNew); + + dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER)); + + pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER); + pNew->bFilter = true; + pNew->other = QSTO_DISPLAYNAME; + + pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING); + pNew->datatype = QSTS_STRING; + pNew->module = mir_strdup("CList"); + pNew->setting = mir_strdup("Group"); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING); + pNew->datatype = QSTS_STRING; + pNew->module = mir_strdup("Tab_SRMsg"); + pNew->setting = mir_strdup("containerW"); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING); + pNew->datatype = QSTS_STRING; + pNew->setting = mir_strdup("MirVer"); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER); + pNew->other = QSTO_LASTSEEN; + pNew->dwFlags = 0; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER); + pNew->other = QSTO_LASTEVENT; + pNew->dwFlags = 0; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING); + pNew->datatype = QSTS_TIMESTAMP; + pNew->setting = mir_strdup("LogonTS"); + pNew->bFilter = true; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER); + pNew->other = QSTO_METACONTACT; + pNew->dwFlags = 0; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER); + pNew->other = QSTO_EVENTCOUNT; + pNew->dwFlags = 0; + dst.insert(pNew); + + pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING); + pNew->datatype = QSTS_TIMESTAMP; + pNew->module = mir_strdup("UserInfo"); + pNew->setting = mir_strdup("ContactAddTime"); + dst.insert(pNew); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// window options + +void CMPlugin::LoadOptWnd() +{ + m_rect.bottom = getDword(so_mbottom); + m_rect.right = getDword(so_mright); + m_rect.left = getDword(so_mleft); + m_rect.top = getDword(so_mtop); + + m_flags = getDword(so_flags); + m_sortOrder = getDword(so_columnsort); +} + +void CMPlugin::SaveOptWnd() +{ + setDword(so_mbottom, m_rect.bottom); + setDword(so_mright, m_rect.right); + setDword(so_mleft, m_rect.left); + setDword(so_mtop, m_rect.top); + + setDword(so_flags, m_flags); + setDword(so_columnsort, m_sortOrder); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// load options + +void CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst) +{ + m_flags = getDword(so_flags); + int numCols = getWord(so_numcolumns); + + for (int i = 0; i < numCols; i++) { + auto *pNew = new ColumnItem(nullptr); + LoadColumn(i, *pNew); + dst.insert(pNew); + } +} + +void CMPlugin::LoadColumn(int n, ColumnItem &col) +{ + char buf[127]; + int offset = mir_snprintf(buf, "%s%d_", so_item, n); + + strcpy(buf + offset, so_title); col.title = getWStringA(buf); + strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf); + strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf); + strcpy(buf + offset, so_width); col.width = getWord(buf); + + switch (col.setting_type) { + case QST_SETTING: + strcpy(buf + offset, so_datatype); col.datatype = getWord(buf); + strcpy(buf + offset, so_module); col.module = getStringA(buf); + strcpy(buf + offset, so_setting); col.setting = getStringA(buf); + break; + + case QST_SCRIPT: + strcpy(buf + offset, so_script); col.script = getWStringA(buf); + break; + + case QST_CONTACTINFO: + strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf); + break; + + case QST_SERVICE: + offset = mir_snprintf(buf, "%s%d/service/", so_item, n); + strcpy(buf + offset, so_service); col.svc.service = getStringA(buf); + strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf); + if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) { + col.setting_type = QST_OTHER; + col.other = QSTO_ACCOUNT; + break; + } + + strcpy(buf + offset, so_wparam); + LoadParamValue(buf, col.svc.wFlags, col.svc.wParam); + + strcpy(buf + offset, so_lparam); + LoadParamValue(buf, col.svc.lFlags, col.svc.lParam); + break; + + case QST_OTHER: + strcpy(buf + offset, so_other); col.other = getWord(buf); + break; + } +} + +void CMPlugin::LoadParamValue(char *buf, DWORD &dwFlags, LPARAM &dwWalue) +{ + char *pEnd = buf + strlen(buf); + strcpy(pEnd, "flags"); dwFlags = getDword(buf); + + strcpy(pEnd, "value"); + switch (dwFlags) { + case ACF_TYPE_NUMBER: + case ACF_TYPE_STRING: + case ACF_TYPE_UNICODE: + dwWalue = LPARAM(getWStringA(buf)); + break; + + case ACF_TYPE_STRUCT: + dwWalue = LPARAM(getStringA(buf)); + break; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// save options + +static int ListSettings(const char *szSetting, void *param) +{ + if (!memcmp(szSetting, so_item, 4)) { + auto *pList = (LIST<char>*)param; + pList->insert(mir_strdup(szSetting)); + } + return 0; +} + +void CMPlugin::SaveOptions() +{ + // remove old settings + LIST<char> settings(30); + db_enum_settings(0, ListSettings, MODULENAME, &settings); + + for (auto &it : settings) { + delSetting(it); + mir_free(it); + } + + // write new settings + setDword(so_flags, m_flags); + setWord(so_numcolumns, m_columns.getCount()); + + int i = 0; + for (auto &it : m_columns) + SaveColumn(i++, *it); +} + +void CMPlugin::SaveColumn(int n, const ColumnItem &col) +{ + char buf[127]; + int offset = mir_snprintf(buf, "%s%d_", so_item, n); + + strcpy(buf + offset, so_title); setWString(buf, col.title); + strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type); + strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags); + strcpy(buf + offset, so_width); setWord(buf, col.width); + + switch (col.setting_type) { + case QST_SETTING: + strcpy(buf + offset, so_datatype); setWord(buf, col.datatype); + strcpy(buf + offset, so_module); setString(buf, col.module); + strcpy(buf + offset, so_setting); setString(buf, col.setting); + break; + + case QST_SCRIPT: + strcpy(buf + offset, so_script); setWString(buf, col.script); + break; + + case QST_CONTACTINFO: + strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype); + break; + + case QST_SERVICE: + offset = mir_snprintf(buf, "%s%d/service/", so_item, n); + strcpy(buf + offset, so_service); setString(buf, col.svc.service); + strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags); + + strcpy(buf + offset, so_wparam); + SaveParamValue(buf, col.svc.wFlags, col.svc.wParam); + + strcpy(buf + offset, so_lparam); + SaveParamValue(buf, col.svc.lFlags, col.svc.lParam); + break; + + case QST_OTHER: + strcpy(buf + offset, so_other); setWord(buf, col.other); + break; + } +} + +void CMPlugin::SaveParamValue(char *buf, DWORD flags, LPARAM value) +{ + char *pEnd = buf + strlen(buf); + strcpy(pEnd, "flags"); setDword(buf, flags); + + strcpy(pEnd, "value"); + switch (flags) { + case ACF_TYPE_NUMBER: + case ACF_TYPE_STRING: + case ACF_TYPE_UNICODE: + setWString(buf, (wchar_t *)value); + break; + + case ACF_TYPE_STRUCT: + setString(buf, (char *)value); + break; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +const wchar_t* cnf2str(int cnf) +{ + switch (cnf) { + case CNF_FIRSTNAME: return TranslateT("FIRSTNAME"); + case CNF_LASTNAME: return TranslateT("LASTNAME"); + case CNF_NICK: return TranslateT("NICK"); + case CNF_CUSTOMNICK: return TranslateT("CUSTOMNICK"); + case CNF_EMAIL: return TranslateT("EMAIL"); + case CNF_CITY: return TranslateT("CITY"); + case CNF_STATE: return TranslateT("STATE"); + case CNF_COUNTRY: return TranslateT("COUNTRY"); + case CNF_PHONE: return TranslateT("PHONE"); + case CNF_HOMEPAGE: return TranslateT("HOMEPAGE"); + case CNF_ABOUT: return TranslateT("ABOUT"); + case CNF_GENDER: return TranslateT("GENDER"); + case CNF_AGE: return TranslateT("AGE"); + case CNF_FIRSTLAST: return TranslateT("FIRSTLAST"); + case CNF_UNIQUEID: return TranslateT("UNIQUEID"); + case CNF_FAX: return TranslateT("FAX"); + case CNF_CELLULAR: return TranslateT("CELLULAR"); + case CNF_TIMEZONE: return TranslateT("TIMEZONE"); + case CNF_MYNOTES: return TranslateT("MYNOTES"); + case CNF_BIRTHDAY: return TranslateT("BIRTHDAY"); + case CNF_BIRTHMONTH: return TranslateT("BIRTHMONTH"); + case CNF_BIRTHYEAR: return TranslateT("BIRTHYEAR"); + case CNF_STREET: return TranslateT("STREET"); + case CNF_ZIP: return TranslateT("ZIP"); + case CNF_LANGUAGE1: return TranslateT("LANGUAGE1"); + case CNF_LANGUAGE2: return TranslateT("LANGUAGE2"); + case CNF_LANGUAGE3: return TranslateT("LANGUAGE3"); + case CNF_CONAME: return TranslateT("CONAME"); + case CNF_CODEPT: return TranslateT("CODEPT"); + case CNF_COPOSITION: return TranslateT("COPOSITION"); + case CNF_COSTREET: return TranslateT("COSTREET"); + case CNF_COCITY: return TranslateT("COCITY"); + case CNF_COSTATE: return TranslateT("COSTATE"); + case CNF_COZIP: return TranslateT("COZIP"); + case CNF_COCOUNTRY: return TranslateT("COCOUNTRY"); + case CNF_COHOMEPAGE: return TranslateT("COHOMEPAGE"); + case CNF_DISPLAYUID: return TranslateT("DISPLAYUID"); + } + return L""; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// formatters + +wchar_t* BuildLastSeenTime(DWORD ts) +{ + int year = ts / (60 * 24 * 31 * 356); + if (year == 0) + return nullptr; + + year += 1980; ts = ts % (60 * 24 * 31 * 356); + + int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31); + int day = ts / (60 * 24); ts = ts % (60 * 24); + int hours = ts / 60; + int mins = ts % 60; + + return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach(); +} + +DWORD BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule) +{ + int year = db_get_w(hContact, szModule, "Year"); + if (year == 0) + return 0; + + int day = db_get_w(hContact, szModule, "Day"); + int month = db_get_w(hContact, szModule, "Month"); + int hours = db_get_w(hContact, szModule, "Hours"); + int minutes = db_get_w(hContact, szModule, "Minutes"); + + return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes; +} + +wchar_t* TimeToStrW(DWORD timestamp) +{ + wchar_t buf[63]; + TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf)); + return mir_wstrdup(buf); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void SnapToScreen(RECT &rc) +{ + int left = GetSystemMetrics(SM_XVIRTUALSCREEN); + int top = GetSystemMetrics(SM_YVIRTUALSCREEN); + int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left; + int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top; + if (rc.right > right) + rc.right = right; + if (rc.bottom > bottom) + rc.bottom = bottom; + if (rc.left < left) + rc.left = left; + if (rc.top < top) + rc.top = top; +} diff --git a/plugins/QuickSearch/src/version.h b/plugins/QuickSearch/src/version.h new file mode 100644 index 0000000000..f9508cadc2 --- /dev/null +++ b/plugins/QuickSearch/src/version.h @@ -0,0 +1,13 @@ +#define __MAJOR_VERSION 1 +#define __MINOR_VERSION 5 +#define __RELEASE_NUM 0 +#define __BUILD_NUM 1 + +#include <stdver.h> + +#define __PLUGIN_NAME "Quick Search" +#define __FILENAME "QuickSearch.dll" +#define __DESCRIPTION "This plugin allows you to quick search for nickname, firstname, lastname, email, uin in your contact list." +#define __AUTHOR "Bethoven, Awkward" +#define __AUTHORWEB "https://miranda-ng.org/p/QuickSearch/" +#define __COPYRIGHT "© 2004-05 Bethoven; 2006-13 Awkward; 2014-21 Miranda NG team" diff --git a/plugins/QuickSearch/src/window.cpp b/plugins/QuickSearch/src/window.cpp new file mode 100644 index 0000000000..63d4a652a8 --- /dev/null +++ b/plugins/QuickSearch/src/window.cpp @@ -0,0 +1,777 @@ +/* +Copyright (C) 2012-21 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; + +///////////////////////////////////////////////////////////////////////////////////////// + +static int OnContactAdded(WPARAM, LPARAM) +{ + return 0; +} + +static int OnContactDeleted(WPARAM, LPARAM) +{ + return 0; +} + +static int OnStatusChanged(WPARAM, LPARAM) +{ + return 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 + PostMessage(m_hwnd, WM_COMMAND, IDCANCEL, 0); + 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() +{} + +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 + if (pattern != nullptr) { + size_t len = mir_wstrlen(pattern); + pattern[len - 1] = 0; + edtFilter.SetText(pattern); + } + break; + } + + if (wParam >= 32 && wParam <= 127) { // letters + CMStringW buf; + if (pattern) + buf = pattern; + buf.AppendChar(wParam); + edtFilter.SetText(buf); + } + 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); + 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 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_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) { + pattern = mir_wstrdup(pwszPattern); + CharLowerW(pattern); + } + else if (g_plugin.m_flags & QSO_SAVEPATTERN) + pattern = g_plugin.getWStringA("pattern"); + else + pattern = nullptr; + + 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); + + 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); + } + + CheckDlgButton(m_hwnd, IDC_CH_SHOWOFFLINE, (g_plugin.m_flags & QSO_SHOWOFFLINE) != 0); + + szFilterProto = nullptr; // display all protocols + if (g_plugin.m_flags & QSO_SHOWOFFLINE) + bShowOffline = true; + + CheckDlgButton(m_hwnd, IDC_CH_COLORIZE, (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 (pattern != nullptr) + SetDlgItemTextW(m_hwnd, IDC_E_SEARCHTEXT, pattern); + else { + SetDlgItemTextW(m_hwnd, IDC_E_SEARCHTEXT, L""); + 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 = HookEvent(ME_DB_CONTACT_ADDED, &OnContactAdded); + hDelete = HookEvent(ME_DB_CONTACT_DELETED, &OnContactDeleted); + hChange = HookEvent(ME_CLIST_CONTACTICONCHANGED, &OnStatusChanged); + 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", pattern); + + mir_free(patstr); + mir_free(pattern); + + m_rows.destroy(); +} + +int QSMainDlg::Resizer(UTILRESIZECONTROL *urc) +{ + switch (urc->wId) { + case IDCANCEL: + 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; +} + +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; + + wchar_t buf[256]; + GetDlgItemTextW(m_hwnd, IDC_E_SEARCHTEXT, buf, _countof(buf)); + CharLowerW(buf); + replaceStrW(pattern, (buf[0]) ? buf : nullptr); + 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 (IsDlgButtonChecked(m_hwnd, IDC_CH_COLORIZE)) + 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; +} diff --git a/plugins/QuickSearch/src/window_misc.cpp b/plugins/QuickSearch/src/window_misc.cpp new file mode 100644 index 0000000000..4e79febb1f --- /dev/null +++ b/plugins/QuickSearch/src/window_misc.cpp @@ -0,0 +1,809 @@ +/* +Copyright (C) 2012-21 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" + +///////////////////////////////////////////////////////////////////////////////////////// +// patterns + +bool QSMainDlg::CheckPattern(CRowItem *) +{ + return true; +} + +void QSMainDlg::MakePattern() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void QSMainDlg::AddColumn(int idx, ColumnItem *pCol) +{ + LV_COLUMN lvcol = {}; + lvcol.mask = LVCF_TEXT | LVCF_WIDTH; + lvcol.pszText = TranslateW(pCol->title); + lvcol.cx = pCol->width; + m_grid.InsertColumn(idx, &lvcol); + + HDITEM hdi; + hdi.mask = HDI_FORMAT; + if (pCol->bFilter) + hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED; + else + hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX; + SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi)); +} + +void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow) +{ + LV_ITEMW li = {}; + li.mask = LVIF_IMAGE | LVIF_PARAM; + li.iItem = 100000; + li.iImage = Clist_GetContactIcon(hContact); + li.lParam = LPARAM(pRow); + + li.iItem = m_grid.InsertItem(&li); + li.iImage = 0; + li.iSubItem = 0; + + for (int i = 0; i < g_plugin.m_columns.getCount(); i++) { + auto &col = g_plugin.m_columns[i]; + if (!col.bEnabled) + continue; + + // Client icons preprocess + li.pszText = pRow->pValues[i].text; + li.mask = LVIF_TEXT; + if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender) + li.mask |= LVIF_IMAGE; + m_grid.SetItem(&li); + li.iSubItem++; + } +} + +void QSMainDlg::AdvancedFilter() +{ + m_grid.SendMsg(WM_SETREDRAW, FALSE, 0); + + for (auto &it : m_rows) { + bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto); + if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE) + bShow = false; + + if (it->bPattern) { + if (bShow) { + if (!it->bActive) + ProcessLine(it, false); + } + else { + it->bActive = false; + m_grid.DeleteItem(FindItem(it)); + } + } + } + + m_grid.SendMsg(WM_SETREDRAW, TRUE, 0); + InvalidateRect(m_grid.GetHwnd(), 0, false); + + Sort(); + UpdateSB(); +} + +void QSMainDlg::CopyMultiLines() +{ + CMStringW buf; + + int i = 0; + for (auto &it : g_plugin.m_columns) { + it->width = m_grid.GetColumnWidth(i++); + if (it->width >= 10) + buf.AppendFormat(L"%s\t", it->title); + } + buf.Append(L"\r\n"); + + int nRows = m_grid.GetItemCount(); + for (int j = 0; j < nRows; j++) { + auto *pRow = GetRow(j); + + i = 0; + for (auto &it : g_plugin.m_columns) { + if (it->width >= 10) + buf.AppendFormat(L"%s\t", pRow->pValues[i].text); + i++; + } + buf.Append(L"\r\n"); + } + + if (OpenClipboard(m_hwnd)) { + EmptyClipboard(); + if (HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE, (buf.GetLength() + 1) * sizeof(wchar_t))) { + mir_wstrcpy((wchar_t *)GlobalLock(hData), buf); + GlobalUnlock(hData); + SetClipboardData(CF_UNICODETEXT, hData); + } + CloseClipboard(); + } +} + +void QSMainDlg::DeleteByList() +{ + if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING)) + return; + + m_grid.SendMsg(WM_SETREDRAW, FALSE, 0); + + for (int i = m_grid.GetItemCount() - 1; i >= 0; i--) + if (m_grid.GetItemState(i, LVIS_SELECTED)) + db_delete_contact(GetRow(i)->hContact); + + m_grid.SendMsg(WM_SETREDRAW, TRUE, 0); +} + +void QSMainDlg::DeleteOneContact(MCONTACT hContact) +{ + if (ServiceExists(MS_CLIST_DELETECONTACT)) + CallService(MS_CLIST_DELETECONTACT, hContact, 0); + else + db_delete_contact(hContact); +} + +wchar_t* QSMainDlg::DoMeta(MCONTACT hContact) +{ + for (auto &it : m_rows) { + if (it->hContact != hContact) + continue; + + if (it->bIsMeta) { + if (it->wparam == 0) + it->wparam = ++hLastMeta; + } + else if (it->bIsSub) + it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam); + + if (it->wparam > 0) { + CMStringW tmp(FORMAT, L"[%d]", int(it->wparam)); + if (it->lparam > 0) + tmp.AppendFormat(L" %d", int(it->lparam)); + return tmp.Detach(); + } + break; + } + + return nullptr; +} + +void QSMainDlg::DrawSB() +{ + CStatusBarItem global(0, 0); + for (auto &it : m_sbdata) { + global.found += it->found; + global.liston += it->liston; + global.online += it->online; + global.total += it->total; + } + + CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online); + + RECT rc; + HDC hdc = GetDC(hwndStatusBar); + DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT); + ReleaseDC(hwndStatusBar, hdc); + + int all = rc.right - rc.left, i = 1; + + mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2))); + parts[0] = all; + for (auto &it : m_sbdata) { + UNREFERENCED_PARAMETER(it); + all += 55; + parts[i++] = all; + } + parts[i] = -1; + SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get())); + SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str())); + + i = 1; + for (auto &it : m_sbdata) { + HICON hIcon; + wchar_t c, *pc; + if (it->bAccDel) { + c = '!'; + pc = TranslateT("deleted"); + hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE); + } + else if (it->bAccOff) { + c = '?'; + pc = TranslateT("disabled"); + hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE); + } + else { + c = ' '; + pc = TranslateT("active"); + hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE); + } + + SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon); + + buf.Format(L"%c %d", c, it->found); + SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str())); + + auto *pa = Proto_GetAccount(it->szProto); + buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online); + SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str())); + i++; + } +} + +void QSMainDlg::FillGrid() +{ + m_grid.SendMsg(WM_SETREDRAW, FALSE, 0); + + MakePattern(); + + for (auto &it: m_rows) + ProcessLine(it); + + m_grid.SendMsg(WM_SETREDRAW, TRUE, 0); + InvalidateRect(m_grid.GetHwnd(), 0, FALSE); + + Sort(); + UpdateSB(); + AdvancedFilter(); + + m_grid.SetCurSel(0); +} + +void QSMainDlg::FillProtoCombo() +{ + cmbProto.ResetContent(); + cmbProto.AddString(TranslateT("All")); + + for (auto &it : Accounts()) + cmbProto.AddString(it->tszAccountName, (LPARAM)it); + + cmbProto.SetCurSel(0); +} + +int QSMainDlg::FindItem(CRowItem *pRow) +{ + if (pRow == nullptr) + return -1; + + LV_FINDINFO fi = {}; + fi.flags = LVFI_PARAM; + fi.lParam = LPARAM(pRow); + return m_grid.FindItem(-1, &fi); +} + +int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum) +{ + for (auto &it : m_rows) { + if (it->hContact != hMeta) + continue; + + // new meta + if (it->wparam == 0) { + it->wparam = ++hLastMeta; + it->lparam = 0; + } + metaNum = it->wparam; + it->lparam++; + return it->lparam; + } + + return 0; +} + +CRowItem* QSMainDlg::FindRow(MCONTACT hContact) +{ + for (auto &it : m_rows) + if (it->hContact == hContact) + return it; + + return nullptr; +} + +MCONTACT QSMainDlg::GetFocusedContact() +{ + int idx = m_grid.GetSelectionMark(); + if (idx == -1) + return -1; + + INT_PTR data = m_grid.GetItemData(idx); + return (data == -1) ? -1 : ((CRowItem *)data)->hContact; +} + +int QSMainDlg::GetLVSubItem(int x, int y) +{ + LV_HITTESTINFO info = {}; + info.pt.x = x; + info.pt.y = y; + ScreenToClient(m_grid.GetHwnd(), &info.pt); + if (m_grid.SubItemHitTest(&info) == -1) + return -1; + + return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1; +} + +void QSMainDlg::PrepareTable(bool bReset) +{ + m_grid.DeleteAllItems(); + + HDITEM hdi = {}; + hdi.mask = HDI_FORMAT; + + int old = tableColumns; + tableColumns = 0; + + LV_COLUMN lvc = {}; + lvc.mask = LVCF_TEXT | LVCF_WIDTH; + for (auto &it : g_plugin.m_columns) { + if (it->bEnabled) + AddColumn(tableColumns++, it); + + it->SetSpecialColumns(); + } + + if (bReset) + for (int i = old + tableColumns - 1; i >= tableColumns; i--) + m_grid.DeleteColumn(i); +} + +bool QSMainDlg::PrepareToFill() +{ + if (g_plugin.m_columns.getCount() == 0) + return false; + + for (auto &it : g_plugin.m_columns) + if (it->bEnabled) + it->bInit = true; + + hLastMeta = 0; + + m_rows.destroy(); + for (auto &hContact : Contacts()) + m_rows.insert(new CRowItem(hContact, this)); + + return m_rows.getCount() != 0; +} + +void QSMainDlg::ProcessLine(CRowItem *pRow, bool test) +{ + if (pRow->bDeleted) + return; + + if (test) + pRow->bPattern = CheckPattern(pRow); + + if (pRow->bPattern) { + if (!pRow->bActive) { + if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) { + // check for proto in combo + if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) { + pRow->bActive = true; + AddContactToList(pRow->hContact, pRow); + } + } + } + } + else if (pRow->bActive) { + pRow->bActive = false; + m_grid.DeleteItem(FindItem(pRow)); + } +} + +void QSMainDlg::SaveColumnOrder() +{ + int idx = 0, col = 0; + for (auto &it : g_plugin.m_columns) { + if (it->bEnabled) { + it->width = m_grid.GetColumnWidth(col++); + g_plugin.SaveColumn(idx, *it); + } + idx++; + } +} + +void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact) +{ + if (hContact) { + Clist_ContactDoubleClicked(hContact); + if (g_plugin.m_flags & QSO_AUTOCLOSE) + Close(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// contact menu + +static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param) +{ + ((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param); + return 0; +} + +void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col) +{ + auto &pCol = g_plugin.m_columns[col]; + + auto *pRow = FindRow(hContact); + if (pRow == nullptr) + return; + + const char *szModule = pCol.module; + if (szModule == nullptr) + szModule = pRow->szProto; + + auto &pVal = pRow->pValues[col]; + CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title); + + ENTER_STRING es = {}; + es.szModuleName = MODULENAME; + es.caption = TranslateT("Enter new cell value"); + es.ptszInitVal = pVal.text; + if (!EnterString(&es)) + return; + + replaceStrW(pVal.text, es.ptszResult); + if (pCol.datatype != QSTS_STRING) + pVal.data = _wtoi(pVal.text); + + switch (pCol.datatype) { + case QSTS_BYTE: + db_set_b(hContact, szModule, pCol.setting, pVal.data); + break; + case QSTS_WORD: + db_set_w(hContact, szModule, pCol.setting, pVal.data); + break; + case QSTS_DWORD: + case QSTS_SIGNED: + case QSTS_HEXNUM: + db_set_dw(hContact, szModule, pCol.setting, pVal.data); + break; + + case QSTS_STRING: + db_set_ws(hContact, szModule, pCol.setting, pVal.text); + break; + } + + UpdateLVCell(FindItem(pRow), col, pVal.text); +} + +void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col) +{ + if (hContact == 0) + return; + + HANDLE srvhandle = 0; + + bool bDoit = false; + if (col >= 0) { + if ((col = ListViewToColumn(col)) == -1) + return; + + auto &pCol = g_plugin.m_columns[col]; + if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) { + bDoit = true; + + srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col); + + if (mnuhandle == nullptr) { + CMenuItem mi(&g_plugin); + SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81); + mi.name.a = LPGEN("Change setting through QS"); + mi.pszService = "QS/Dummy"; + mnuhandle = Menu_AddContactMenuItem(&mi); + } + else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0); + } + } + + POINT pt; + GetCursorPos(&pt); + HMENU hMenu = Menu_BuildContactMenu(hContact); + if (hMenu) { + int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0); + if (iCmd) { + if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) { + if (g_plugin.m_flags & QSO_AUTOCLOSE) + CloseSrWindow(); + } + } + + ::DestroyMenu(hMenu); + } + + if (srvhandle) + DestroyServiceFunction(srvhandle); + if (bDoit) + Menu_ShowItem(mnuhandle, false); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// muptiple selection popup menu + +static HMENU MakeContainerMenu() +{ + HMENU hMenu = CreatePopupMenu(); + + for (int i = 0;; i++) { + char setting[10]; + _itoa_s(i, setting, 10); + ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting)); + if (wszName != nullptr) + AppendMenuW(hMenu, MF_STRING, 300 + i, wszName); + else + break; + } + + return hMenu; +} + +void QSMainDlg::ShowMultiPopup(int cnt) +{ + HMENU hMenu = CreatePopupMenu(); + AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt)); + AppendMenuW(hMenu, MF_SEPARATOR, 0, 0); + AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete")); + AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy")); + AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta")); + + HMENU cntmenu = MakeContainerMenu(); + AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container")); + + if (HMENU grpmenu = Clist_GroupBuildMenu(400)) + AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group")); + + POINT pt; + GetCursorPos(&pt); + + int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0); + switch (iRes) { + case 101: + DeleteByList(); + break; + + case 102: + CopyMultiLines(); + break; + + case 103: + ConvertToMeta(); + break; + } + + if (iRes >= 300 && iRes <= 399) { + wchar_t buf[100]; + if (iRes == 300) // default container, just delete setting + buf[0] = 0; + else + GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND); + + MoveToContainer(buf); + } + else if (iRes >= 400 && iRes <= 499) { + wchar_t buf[100]; + if (iRes == 400) // default container, just delete setting + buf[0] = 0; + else + GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND); + + MoveToGroup(buf); + } +} + +void QSMainDlg::ConvertToMeta() +{ + MCONTACT hMeta = 0; + + int nCount = m_grid.GetItemCount(); + for (int i = 0; i < nCount; i++) { + if (!m_grid.GetItemState(i, LVIS_SELECTED)) + continue; + + auto *pRow = GetRow(i); + if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) { + if (hMeta == 0) + hMeta = tmp; + else if (hMeta != tmp) { + MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR); + return; + } + } + } + + if (hMeta != 0) + if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING)) + return; + + for (int i = 0; i < nCount; i++) { + if (!m_grid.GetItemState(i, LVIS_SELECTED)) + continue; + + auto *pRow = GetRow(i); + if (hMeta) + db_mc_addToMeta(pRow->hContact, hMeta); + else + db_mc_convertToMeta(pRow->hContact); + } +} + +void QSMainDlg::MoveToContainer(const wchar_t *pwszName) +{ + int grcol = -1; + for (auto &it : g_plugin.m_columns) { + if (it->isContainer) { + if (it->bEnabled) + grcol = g_plugin.m_columns.indexOf(&it); + else + it->bInit = false; + } + } + + int nCount = m_grid.GetItemCount(); + for (int i = 0; i < nCount; i++) { + if (!m_grid.GetItemState(i, LVIS_SELECTED)) + continue; + + auto *pRow = GetRow(i); + if (*pwszName == 0) + db_unset(pRow->hContact, "Tab_SRMsg", "containerW"); + else + db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName); + + if (grcol != -1) { + auto &pVal = pRow->pValues[grcol]; + replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr); + UpdateLVCell(i, grcol, pwszName); + } + } +} + +void QSMainDlg::MoveToGroup(const wchar_t *pwszName) +{ + int grcol = -1; + for (auto &it : g_plugin.m_columns) { + if (it->isGroup) { + if (it->bEnabled) + grcol = g_plugin.m_columns.indexOf(&it); + else + it->bInit = false; + } + } + + int nCount = m_grid.GetItemCount(); + for (int i = 0; i < nCount; i++) { + if (!m_grid.GetItemState(i, LVIS_SELECTED)) + continue; + + auto *pRow = GetRow(i); + Clist_SetGroup(pRow->hContact, pwszName); + + if (grcol != -1) { + auto &pVal = pRow->pValues[grcol]; + replaceStrW(pVal.text, pwszName); + UpdateLVCell(i, grcol, pwszName); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// grid sorting + +static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type) +{ + bool typ1, typ2; + UINT_PTR i1, i2; + int result = 0; + CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2; + + if (type == StatusSort) { + i1 = r1->status, i2 = r2->status; + if (i1 == ID_STATUS_OFFLINE) i1 += 64; + if (i2 == ID_STATUS_OFFLINE) i2 += 64; + typ1 = typ2 = false; + } + else { + auto &res1 = r1->pValues[type], &res2 = r2->pValues[type]; + i1 = res1.data, i2 = res2.data; + typ1 = i1 == -1; typ2 = i1 == -1; + + if (typ1 && typ2) { // two strings + if (res1.text == 0 && res2.text == 0) + result = 0; + else if (res2.text == 0) + result = 1; + else if (res1.text == 0) + result = -1; + else + result = lstrcmpiW(res1.text, res2.text); + } + else if (typ1 || typ2) // string & num + result = (typ1) ? 1 : -1; + } + + if (!typ1 && !typ2) { // not strings + if (i1 > i2) + result = 1; + else if (i1 < i2) + result = -1; + else + result = 0; + } + + if (g_plugin.m_flags & QSO_SORTASC) + result = -result; + return result; +} + +void QSMainDlg::Sort() +{ + if (g_plugin.m_sortOrder >= tableColumns) + g_plugin.m_sortOrder = StatusSort; + m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder)); + + if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS)) + m_grid.SortItems(&CompareItem, StatusSort); +} + +void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText) +{ + auto &pCol = g_plugin.m_columns[column]; + auto *pRow = GetRow(item); + + if (pwszText == nullptr) { + auto &pVal = pRow->pValues[column]; + replaceStrW(pVal.text, 0); + pVal.LoadOneItem(pRow->hContact, pCol, this); + pwszText = pVal.text; + } + + m_grid.SetItemText(item, ColumnToListView(column), pwszText); + + if (pCol.bFilter) + ProcessLine(pRow, true); + if (g_plugin.m_sortOrder == column) + Sort(); +} + +void QSMainDlg::UpdateSB() +{ + m_sbdata.destroy(); + + for (auto &it : m_rows) { + if (it->szProto == nullptr) + continue; + + CStatusBarItem tmp(it->szProto, it->flags); + auto *pItem = m_sbdata.find(&tmp); + if (pItem == nullptr) + m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags)); + + pItem->total++; + + if (it->bActive) + pItem->found++; + + if (it->status != ID_STATUS_OFFLINE) { + pItem->online++; + if (it->bActive) + pItem->liston++; + } + } + + DrawSB(); +} diff --git a/plugins/QuickSearch/src/window_row.cpp b/plugins/QuickSearch/src/window_row.cpp new file mode 100644 index 0000000000..16427be551 --- /dev/null +++ b/plugins/QuickSearch/src/window_row.cpp @@ -0,0 +1,222 @@ +/* +Copyright (C) 2012-21 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h" + +CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) : + hContact(_1) +{ + auto *pa = Proto_GetContactAccount(hContact); + if (pa != nullptr) { + szProto = pa->szModuleName; + if (!pa->IsEnabled()) + bAccOff = true; + + if (db_mc_isMeta(hContact)) + bIsMeta = true; + else if (db_mc_isSub(hContact)) + bIsSub = true; + } + else { + szProto = nullptr; + bAccDel = true; + } + + if (bAccDel || bAccOff) + status = ID_STATUS_OFFLINE; + else + status = Contact_GetStatus(hContact); + + if (int nCount = g_plugin.m_columns.getCount()) { + pValues = new Val[nCount]; + for (int i = 0; i < nCount; i++) + pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg); + } + else pValues = nullptr; +} + +CRowItem::~CRowItem() +{ + delete[] pValues; +} + +void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText) +{ + if (g_plugin.m_flags & QSO_COLORIZE) { + if (bAccDel) { + clrBack = g_plugin.m_colors[bkg_del]; + clrText = g_plugin.m_colors[fgr_del]; + return; + } + if (bAccOff) { + clrBack = g_plugin.m_colors[bkg_dis]; + clrText = g_plugin.m_colors[fgr_dis]; + return; + } + if (bIsMeta) { + clrBack = g_plugin.m_colors[bkg_meta]; + clrText = g_plugin.m_colors[fgr_meta]; + return; + } + if (bIsSub) { + clrBack = g_plugin.m_colors[bkg_sub]; + clrText = g_plugin.m_colors[fgr_sub]; + return; + } + if (bInList) { + clrBack = g_plugin.m_colors[bkg_hid]; + clrText = g_plugin.m_colors[fgr_hid]; + return; + } + } + + if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) { + clrBack = g_plugin.m_colors[bkg_odd]; + clrText = g_plugin.m_colors[fgr_odd]; + } + else { + clrBack = g_plugin.m_colors[bkg_norm]; + clrText = g_plugin.m_colors[fgr_norm]; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static wchar_t* int2strw(DWORD num) +{ + wchar_t buf[64]; + _itow_s(num, buf, 10); + return mir_wstrdup(buf); +} + +static wchar_t* hex2strw(DWORD num) +{ + wchar_t buf[64]; + _itow_s(num, buf, 16); + return mir_wstrdup(buf); +} + +void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg) +{ + data = UINT_PTR(-1); + replaceStrW(text, nullptr); + + switch (pCol.setting_type) { + case QST_SCRIPT: + { + VARSW vars(pCol.script); + if (g_bVarsInstalled) + text = variables_parse(vars, 0, hContact); + else + text = vars.detach(); + } + break; + + case QST_SERVICE: + // !!!!!!!!!!!!!!!!!!! not implemented + break; + + case QST_CONTACTINFO: + text = Contact_GetInfo(pCol.cnftype, hContact); + break; + + case QST_OTHER: + switch (pCol.other) { + case QSTO_ACCOUNT: + if (auto *pa = Proto_GetContactAccount(hContact)) + text = mir_wstrdup(pa->tszAccountName); + break; + + case QSTO_LASTSEEN: + data = BuildLastSeenTimeInt(hContact, "SeenModule"); + text = BuildLastSeenTime(data); + break; + + case QSTO_DISPLAYNAME: + text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0)); + break; + + case QSTO_LASTEVENT: + if (MEVENT hDbEvent = db_event_last(hContact)) { + DBEVENTINFO dbei = {}; + db_event_get(hDbEvent, &dbei); + data = dbei.timestamp; + text = TimeToStrW(data); + } + else text = 0; + break; + + case QSTO_METACONTACT: + text = pDlg->DoMeta(hContact); + break; + + case QSTO_EVENTCOUNT: + data = db_event_count(hContact); + text = int2strw(data); + break; + } + break; + + case QST_SETTING: + auto *szNodule = pCol.module; + if (!mir_strlen(szNodule)) + szNodule = Proto_GetBaseAccountName(hContact); + + switch (pCol.datatype) { + case QSTS_STRING: + text = db_get_wsa(hContact, szNodule, pCol.setting); + break; + + case QSTS_BYTE: + data = db_get_b(hContact, szNodule, pCol.setting); + text = int2strw(data); + break; + + case QSTS_WORD: + data = db_get_w(hContact, szNodule, pCol.setting); + text = int2strw(data); + break; + + case QSTS_DWORD: + if (pCol.setting == nullptr) { + data = hContact; + text = hex2strw(data); + } + else { + data = db_get_dw(hContact, szNodule, pCol.setting); + text = int2strw(data); + } + break; + + case QSTS_SIGNED: + data = db_get_dw(hContact, szNodule, pCol.setting); + text = int2strw(data); + break; + + case QSTS_HEXNUM: + data = db_get_dw(hContact, szNodule, pCol.setting); + text = hex2strw(data); + break; + + case QSTS_TIMESTAMP: + data = db_get_dw(hContact, szNodule, pCol.setting); + if (data != 0) + text = TimeToStrW(data); + break; + } + } +} |