From 1979fd80424d16b2e489f9b57d01d9c7811d25a2 Mon Sep 17 00:00:00 2001 From: dartraiden Date: Mon, 2 Jan 2023 21:10:29 +0300 Subject: Update copyrights --- src/core/stdautoaway/src/autoaway.cpp | 2 +- src/core/stdautoaway/src/idle.cpp | 178 +-- src/core/stdautoaway/src/main.cpp | 2 +- src/core/stdautoaway/src/options.cpp | 268 ++-- src/core/stdautoaway/src/stdafx.h | 2 +- src/core/stdautoaway/src/version.h | 2 +- src/core/stdaway/src/awaymsg.cpp | 2 +- src/core/stdaway/src/main.cpp | 2 +- src/core/stdaway/src/options.cpp | 280 ++-- src/core/stdaway/src/sendmsg.cpp | 2 +- src/core/stdaway/src/stdafx.h | 2 +- src/core/stdaway/src/version.h | 2 +- src/core/stdclist/src/clc.h | 2 +- src/core/stdclist/src/clcfonts.cpp | 2 +- src/core/stdclist/src/clcopts.cpp | 2 +- src/core/stdclist/src/clcpaint.cpp | 2 +- src/core/stdclist/src/clistmenus.cpp | 2 +- src/core/stdclist/src/clistopts.cpp | 2 +- src/core/stdclist/src/cluiopts.cpp | 2 +- src/core/stdclist/src/contact.cpp | 2 +- src/core/stdclist/src/init.cpp | 2 +- src/core/stdclist/src/stdafx.cxx | 2 +- src/core/stdclist/src/stdafx.h | 2 +- src/core/stdclist/src/version.h | 2 +- src/core/stdcrypt/src/encrypt.cpp | 2 +- src/core/stdcrypt/src/main.cpp | 2 +- src/core/stdcrypt/src/stdafx.h | 2 +- src/core/stdcrypt/src/stdcrypt.h | 2 +- src/core/stdcrypt/src/utils.cpp | 2 +- src/core/stdcrypt/src/version.h | 2 +- src/core/stdemail/src/email.cpp | 2 +- src/core/stdemail/src/main.cpp | 2 +- src/core/stdemail/src/stdafx.h | 2 +- src/core/stdemail/src/version.h | 2 +- src/core/stdfile/src/file.cpp | 2 +- src/core/stdfile/src/file.h | 2 +- src/core/stdfile/src/fileexistsdlg.cpp | 2 +- src/core/stdfile/src/fileopts.cpp | 2 +- src/core/stdfile/src/filerecvdlg.cpp | 2 +- src/core/stdfile/src/filesenddlg.cpp | 2 +- src/core/stdfile/src/filexferdlg.cpp | 1530 +++++++++--------- src/core/stdfile/src/ftmanager.cpp | 2 +- src/core/stdfile/src/main.cpp | 2 +- src/core/stdfile/src/stdafx.h | 2 +- src/core/stdfile/src/version.h | 2 +- src/core/stdmsg/src/chat_manager.cpp | 486 +++--- src/core/stdmsg/src/chat_window.cpp | 550 +++---- src/core/stdmsg/src/cmdlist.cpp | 2 +- src/core/stdmsg/src/cmdlist.h | 2 +- src/core/stdmsg/src/globals.cpp | 2 +- src/core/stdmsg/src/msgdialog.cpp | 2 +- src/core/stdmsg/src/msglog.cpp | 2 +- src/core/stdmsg/src/msgoptions.cpp | 2 +- src/core/stdmsg/src/msgs.cpp | 2 +- src/core/stdmsg/src/msgs.h | 2 +- src/core/stdmsg/src/msgtimedout.cpp | 2 +- src/core/stdmsg/src/srmm.cpp | 2 +- src/core/stdmsg/src/statusicon.cpp | 2 +- src/core/stdmsg/src/stdafx.cxx | 2 +- src/core/stdmsg/src/stdafx.h | 2 +- src/core/stdmsg/src/tabs.cpp | 1336 ++++++++-------- src/core/stdmsg/src/version.h | 2 +- src/core/stdpopup/src/stdafx.cxx | 34 +- src/core/stdpopup/src/version.h | 2 +- src/core/stduihist/src/history.cpp | 2 +- src/core/stduihist/src/main.cpp | 2 +- src/core/stduihist/src/stdafx.h | 2 +- src/core/stduihist/src/version.h | 2 +- src/core/stduserinfo/src/contactinfo.cpp | 2 +- src/core/stduserinfo/src/main.cpp | 2 +- src/core/stduserinfo/src/stdafx.h | 2 +- src/core/stduserinfo/src/stdinfo.cpp | 2 +- src/core/stduserinfo/src/userinfo.cpp | 2 +- src/core/stduserinfo/src/utils.h | 428 ++--- src/core/stduserinfo/src/version.h | 2 +- src/core/stduseronline/src/main.cpp | 2 +- src/core/stduseronline/src/stdafx.h | 2 +- src/core/stduseronline/src/useronline.cpp | 2 +- src/core/stduseronline/src/version.h | 2 +- src/mir_app/src/CMPluginBase.cpp | 686 ++++---- src/mir_app/src/Docking.cpp | 2 +- src/mir_app/src/FontOptions.cpp | 2 +- src/mir_app/src/FontService.cpp | 2 +- src/mir_app/src/FontService.h | 2 +- src/mir_app/src/IcoLib.h | 2 +- src/mir_app/src/MDatabaseCommon.cpp | 2 +- src/mir_app/src/MDatabaseCommonCrypt.cpp | 898 +++++------ src/mir_app/src/MDatabaseReadonly.cpp | 360 ++--- src/mir_app/src/MHttpRequest.cpp | 186 +-- src/mir_app/src/addcontact.cpp | 2 +- src/mir_app/src/auth.cpp | 734 ++++----- src/mir_app/src/button.cpp | 2 +- src/mir_app/src/chat_manager.cpp | 2 +- src/mir_app/src/chat_rtf.cpp | 408 ++--- src/mir_app/src/chat_svc.cpp | 2 +- src/mir_app/src/chat_tools.cpp | 2 +- src/mir_app/src/chat_ui.cpp | 440 ++--- src/mir_app/src/clc.cpp | 2 +- src/mir_app/src/clc.h | 2 +- src/mir_app/src/clccontact.cpp | 144 +- src/mir_app/src/clcfiledrop.cpp | 2 +- src/mir_app/src/clcidents.cpp | 2 +- src/mir_app/src/clcitems.cpp | 1460 ++++++++--------- src/mir_app/src/clcmsgs.cpp | 2 +- src/mir_app/src/clcutils.cpp | 2 +- src/mir_app/src/clistcore.cpp | 2 +- src/mir_app/src/clistevents.cpp | 2 +- src/mir_app/src/clistgroups.cpp | 2 +- src/mir_app/src/clistmod.cpp | 2 +- src/mir_app/src/clistopts.cpp | 324 ++-- src/mir_app/src/clistsettings.cpp | 2 +- src/mir_app/src/clisttray.cpp | 2 +- src/mir_app/src/clui.cpp | 2 +- src/mir_app/src/cluiservices.cpp | 2 +- src/mir_app/src/colorchooser.cpp | 2 +- src/mir_app/src/contacts.cpp | 2 +- src/mir_app/src/copyright.h | 4 +- src/mir_app/src/database.cpp | 2 +- src/mir_app/src/database.h | 2 +- src/mir_app/src/db_events.cpp | 2 +- src/mir_app/src/db_ini.cpp | 2 +- src/mir_app/src/db_intf.cpp | 2 +- src/mir_app/src/db_upgrade.cpp | 142 +- src/mir_app/src/db_util.cpp | 258 +-- src/mir_app/src/descbutton.cpp | 2 +- src/mir_app/src/dll_sniffer.cpp | 2 +- src/mir_app/src/ei_baseIcon.cpp | 140 +- src/mir_app/src/ei_callbackIcon.cpp | 150 +- src/mir_app/src/ei_defaulticons.cpp | 678 ++++---- src/mir_app/src/ei_extraIcon.cpp | 166 +- src/mir_app/src/ei_groupIcon.cpp | 400 ++--- src/mir_app/src/ei_icolibIcon.cpp | 236 +-- src/mir_app/src/ei_options.cpp | 962 +++++------ src/mir_app/src/ei_services.cpp | 1008 ++++++------ src/mir_app/src/encrypt.cpp | 2 +- src/mir_app/src/encrypt.h | 94 +- src/mir_app/src/enterstring.cpp | 2 +- src/mir_app/src/extracticon.cpp | 2 +- src/mir_app/src/extraicons.h | 2 +- src/mir_app/src/filter.cpp | 2 +- src/mir_app/src/filter.h | 2 +- src/mir_app/src/findadd.cpp | 2070 ++++++++++++------------ src/mir_app/src/findadd.h | 2 +- src/mir_app/src/genmenu.h | 2 +- src/mir_app/src/headerbar.cpp | 2 +- src/mir_app/src/help.cpp | 378 ++--- src/mir_app/src/hotkey_opts.cpp | 2052 ++++++++++++------------ src/mir_app/src/hotkeys.cpp | 2 +- src/mir_app/src/icolib.cpp | 2 +- src/mir_app/src/idle.cpp | 174 +- src/mir_app/src/ignore.cpp | 2 +- src/mir_app/src/keyboard.cpp | 2 +- src/mir_app/src/langpack.h | 2 +- src/mir_app/src/lpopts.cpp | 2 +- src/mir_app/src/mdatabasecache.cpp | 2 +- src/mir_app/src/menu_clist.cpp | 2 +- src/mir_app/src/menu_frames.cpp | 2 +- src/mir_app/src/menu_groups.cpp | 2 +- src/mir_app/src/menu_options.cpp | 1176 +++++++------- src/mir_app/src/menu_tray.cpp | 2 +- src/mir_app/src/menu_utils.cpp | 2 +- src/mir_app/src/meta_addto.cpp | 2 +- src/mir_app/src/meta_api.cpp | 2 +- src/mir_app/src/meta_edit.cpp | 2 +- src/mir_app/src/meta_main.cpp | 2 +- src/mir_app/src/meta_menu.cpp | 2 +- src/mir_app/src/meta_options.cpp | 2 +- src/mir_app/src/meta_services.cpp | 2 +- src/mir_app/src/meta_utils.cpp | 2 +- src/mir_app/src/metacontacts.h | 2 +- src/mir_app/src/miranda.cpp | 2 +- src/mir_app/src/miranda.h | 2 +- src/mir_app/src/modules.cpp | 2 +- src/mir_app/src/movetogroup.cpp | 2 +- src/mir_app/src/netlib.cpp | 2 +- src/mir_app/src/netlib.h | 2 +- src/mir_app/src/netlib_autoproxy.cpp | 730 ++++----- src/mir_app/src/netlib_bind.cpp | 660 ++++---- src/mir_app/src/netlib_http.cpp | 2300 +++++++++++++-------------- src/mir_app/src/netlib_httpproxy.cpp | 170 +- src/mir_app/src/netlib_log.cpp | 1156 +++++++------- src/mir_app/src/netlib_openconn.cpp | 1452 ++++++++--------- src/mir_app/src/netlib_opts.cpp | 1036 ++++++------ src/mir_app/src/netlib_pktrecver.cpp | 160 +- src/mir_app/src/netlib_security.cpp | 726 ++++----- src/mir_app/src/netlib_sock.cpp | 574 +++---- src/mir_app/src/netlib_ssl.cpp | 930 +++++------ src/mir_app/src/netlib_upnp.cpp | 1626 +++++++++---------- src/mir_app/src/netlib_websocket.cpp | 348 ++-- src/mir_app/src/newplugins.cpp | 2 +- src/mir_app/src/options.cpp | 2 +- src/mir_app/src/path.cpp | 2 +- src/mir_app/src/pluginopts.cpp | 2 +- src/mir_app/src/popupOption.cpp | 246 +-- src/mir_app/src/popups.cpp | 314 ++-- src/mir_app/src/profilemanager.cpp | 1282 +++++++-------- src/mir_app/src/profilemanager.h | 2 +- src/mir_app/src/proto_accs.cpp | 870 +++++----- src/mir_app/src/proto_chains.cpp | 2 +- src/mir_app/src/proto_interface.cpp | 702 ++++---- src/mir_app/src/proto_internal.cpp | 2 +- src/mir_app/src/proto_opts.cpp | 2 +- src/mir_app/src/proto_order.cpp | 2 +- src/mir_app/src/proto_ui.cpp | 2 +- src/mir_app/src/proto_utils.cpp | 2 +- src/mir_app/src/protocols.cpp | 2 +- src/mir_app/src/pu_utils.cpp | 662 ++++---- src/mir_app/src/searchresults.cpp | 2 +- src/mir_app/src/skin.h | 2 +- src/mir_app/src/skin2opts.cpp | 2 +- src/mir_app/src/skinicons.cpp | 2 +- src/mir_app/src/sounds.cpp | 2 +- src/mir_app/src/srmm_base.cpp | 1846 ++++++++++----------- src/mir_app/src/srmm_log.cpp | 392 ++--- src/mir_app/src/srmm_log_hpp.cpp | 454 +++--- src/mir_app/src/srmm_log_rtf.cpp | 816 +++++----- src/mir_app/src/srmm_statusicon.cpp | 2 +- src/mir_app/src/srmm_toolbar.cpp | 1772 ++++++++++----------- src/mir_app/src/srmm_util.cpp | 294 ++-- src/mir_app/src/stdafx.cxx | 2 +- src/mir_app/src/stdafx.h | 2 +- src/mir_app/src/usedIcons.cpp | 2 +- src/mir_app/src/usedIcons.h | 2 +- src/mir_app/src/userInfo.cpp | 90 +- src/mir_app/src/utils.cpp | 2 +- src/mir_app/src/visibility.cpp | 2 +- src/mir_core/res/version.rc | 114 +- src/mir_core/src/Linux/CCtrlBase.cpp | 410 ++--- src/mir_core/src/Linux/CCtrlButton.cpp | 110 +- src/mir_core/src/Linux/CCtrlCheck.cpp | 140 +- src/mir_core/src/Linux/CCtrlClc.cpp | 414 ++--- src/mir_core/src/Linux/CCtrlColor.cpp | 122 +- src/mir_core/src/Linux/CCtrlCombo.cpp | 338 ++-- src/mir_core/src/Linux/CCtrlData.cpp | 104 +- src/mir_core/src/Linux/CCtrlEdit.cpp | 136 +- src/mir_core/src/Linux/CCtrlHyperlink.cpp | 108 +- src/mir_core/src/Linux/CCtrlLabel.cpp | 60 +- src/mir_core/src/Linux/CCtrlListBox.cpp | 320 ++-- src/mir_core/src/Linux/CCtrlListView.cpp | 1102 ++++++------- src/mir_core/src/Linux/CCtrlMButton.cpp | 124 +- src/mir_core/src/Linux/CCtrlPages.cpp | 822 +++++----- src/mir_core/src/Linux/CCtrlSlider.cpp | 140 +- src/mir_core/src/Linux/CCtrlSpin.cpp | 162 +- src/mir_core/src/Linux/CCtrlTreeOpts.cpp | 432 ++--- src/mir_core/src/Linux/CCtrlTreeView.cpp | 1634 +++++++++---------- src/mir_core/src/Linux/CDbLink.cpp | 184 +-- src/mir_core/src/Linux/CDlgBase.cpp | 520 +++--- src/mir_core/src/Linux/CProgress.cpp | 106 +- src/mir_core/src/Linux/CSplitter.cpp | 166 +- src/mir_core/src/Linux/CTimer.cpp | 186 +-- src/mir_core/src/Linux/cctrldate.cpp | 98 +- src/mir_core/src/Linux/fileutil.cpp | 178 +-- src/mir_core/src/Linux/strutil.cpp | 96 +- src/mir_core/src/Windows/CCtrlBase.cpp | 448 +++--- src/mir_core/src/Windows/CCtrlButton.cpp | 108 +- src/mir_core/src/Windows/CCtrlCheck.cpp | 136 +- src/mir_core/src/Windows/CCtrlClc.cpp | 422 ++--- src/mir_core/src/Windows/CCtrlColor.cpp | 122 +- src/mir_core/src/Windows/CCtrlCombo.cpp | 370 ++--- src/mir_core/src/Windows/CCtrlData.cpp | 104 +- src/mir_core/src/Windows/CCtrlDate.cpp | 98 +- src/mir_core/src/Windows/CCtrlEdit.cpp | 136 +- src/mir_core/src/Windows/CCtrlHyperlink.cpp | 108 +- src/mir_core/src/Windows/CCtrlLabel.cpp | 62 +- src/mir_core/src/Windows/CCtrlListBox.cpp | 320 ++-- src/mir_core/src/Windows/CCtrlListView.cpp | 1102 ++++++------- src/mir_core/src/Windows/CCtrlMButton.cpp | 124 +- src/mir_core/src/Windows/CCtrlPages.cpp | 822 +++++----- src/mir_core/src/Windows/CCtrlRichEdit.cpp | 384 ++--- src/mir_core/src/Windows/CCtrlSlider.cpp | 140 +- src/mir_core/src/Windows/CCtrlSpin.cpp | 162 +- src/mir_core/src/Windows/CCtrlTreeOpts.cpp | 2 +- src/mir_core/src/Windows/CCtrlTreeView.cpp | 2 +- src/mir_core/src/Windows/CDbLink.cpp | 184 +-- src/mir_core/src/Windows/CDlgBase.cpp | 1044 ++++++------ src/mir_core/src/Windows/CProgress.cpp | 106 +- src/mir_core/src/Windows/CSplitter.cpp | 166 +- src/mir_core/src/Windows/CTimer.cpp | 180 +-- src/mir_core/src/Windows/cmdline.cpp | 154 +- src/mir_core/src/Windows/colourpicker.cpp | 210 +-- src/mir_core/src/Windows/diatheme.cpp | 340 ++-- src/mir_core/src/Windows/fileutil.cpp | 156 +- src/mir_core/src/Windows/hyperlink.cpp | 554 +++---- src/mir_core/src/Windows/icons.cpp | 148 +- src/mir_core/src/Windows/langpack.cpp | 1530 +++++++++--------- src/mir_core/src/Windows/locks.cpp | 92 +- src/mir_core/src/Windows/miranda.cpp | 2 +- src/mir_core/src/Windows/openurl.cpp | 152 +- src/mir_core/src/Windows/path.cpp | 492 +++--- src/mir_core/src/Windows/resizer.cpp | 302 ++-- src/mir_core/src/Windows/subclass.cpp | 402 ++--- src/mir_core/src/Windows/threads.cpp | 800 +++++----- src/mir_core/src/Windows/timezones.cpp | 1186 +++++++------- src/mir_core/src/Windows/windowlist.cpp | 210 +-- src/mir_core/src/Windows/winutil.cpp | 354 ++--- src/mir_core/src/Windows/winver.cpp | 744 ++++----- src/mir_core/src/binbuffer.cpp | 340 ++-- src/mir_core/src/bitmaps.cpp | 2 +- src/mir_core/src/db.cpp | 2 +- src/mir_core/src/http.cpp | 2 +- src/mir_core/src/lists.cpp | 2 +- src/mir_core/src/logger.cpp | 406 ++--- src/mir_core/src/memory.cpp | 590 +++---- src/mir_core/src/miranda.h | 202 +-- src/mir_core/src/modules.cpp | 1408 ++++++++-------- src/mir_core/src/mstring.cpp | 290 ++-- src/mir_core/src/stdafx.cxx | 2 +- src/mir_core/src/stdafx.h | 168 +- src/mir_core/src/utf.cpp | 882 +++++----- src/mir_core/src/utils.cpp | 1140 ++++++------- src/miranda32/res/version.rc | 2 +- src/miranda32/src/miranda.cpp | 2 +- src/miranda32/src/stdafx.cxx | 2 +- src/miranda32/src/stdafx.h | 2 +- 314 files changed, 35602 insertions(+), 35602 deletions(-) (limited to 'src') diff --git a/src/core/stdautoaway/src/autoaway.cpp b/src/core/stdautoaway/src/autoaway.cpp index f9a7fa8379..024487ed5a 100644 --- a/src/core/stdautoaway/src/autoaway.cpp +++ b/src/core/stdautoaway/src/autoaway.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdautoaway/src/idle.cpp b/src/core/stdautoaway/src/idle.cpp index aaabe0ddb2..b4faa31c4b 100644 --- a/src/core/stdautoaway/src/idle.cpp +++ b/src/core/stdautoaway/src/idle.cpp @@ -1,89 +1,89 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static UINT_PTR g_hTimer; - -int IdleOptInit(WPARAM wParam, LPARAM); - -static int IdleObject_IsUserIdle() -{ - if (g_plugin.bIdleMethod) { - uint32_t dwTick = Miranda_GetIdle(); - return GetTickCount() - dwTick > (g_plugin.iIdleTime1st * 60 * 1000); - } - - LASTINPUTINFO ii = { sizeof(ii) }; - if (GetLastInputInfo(&ii)) - return GetTickCount() - ii.dwTime > (g_plugin.iIdleTime1st * 60 * 1000); - - return FALSE; -} - -static void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD) -{ - if (g_hTimer != idEvent) - return; - - if (g_plugin.bIdleCheck && IdleObject_IsUserIdle()) - Idle_Enter(1); - - else if (g_plugin.bIdleOnSaver && IsScreenSaverRunning()) - Idle_Enter(2); - - else if (g_plugin.bIdleOnFullScr && IsFullScreen()) - Idle_Enter(3); - - else if (g_plugin.bIdleOnLock && IsWorkstationLocked()) - Idle_Enter(4); - - else if (g_plugin.bIdleOnTerminal && IsTerminalDisconnected()) - Idle_Enter(5); - - else // not idle - Idle_Enter(-1); -} - -void IdleObject_Create() -{ - g_hTimer = SetTimer(nullptr, 0, 2000, IdleTimer); -} - -void IdleObject_Destroy() -{ - KillTimer(nullptr, g_hTimer); -} - -void LoadIdleModule(void) -{ - IdleObject_Create(); - - HookEvent(ME_OPT_INITIALISE, IdleOptInit); -} - -void UnloadIdleModule() -{ - IdleObject_Destroy(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static UINT_PTR g_hTimer; + +int IdleOptInit(WPARAM wParam, LPARAM); + +static int IdleObject_IsUserIdle() +{ + if (g_plugin.bIdleMethod) { + uint32_t dwTick = Miranda_GetIdle(); + return GetTickCount() - dwTick > (g_plugin.iIdleTime1st * 60 * 1000); + } + + LASTINPUTINFO ii = { sizeof(ii) }; + if (GetLastInputInfo(&ii)) + return GetTickCount() - ii.dwTime > (g_plugin.iIdleTime1st * 60 * 1000); + + return FALSE; +} + +static void CALLBACK IdleTimer(HWND, UINT, UINT_PTR idEvent, DWORD) +{ + if (g_hTimer != idEvent) + return; + + if (g_plugin.bIdleCheck && IdleObject_IsUserIdle()) + Idle_Enter(1); + + else if (g_plugin.bIdleOnSaver && IsScreenSaverRunning()) + Idle_Enter(2); + + else if (g_plugin.bIdleOnFullScr && IsFullScreen()) + Idle_Enter(3); + + else if (g_plugin.bIdleOnLock && IsWorkstationLocked()) + Idle_Enter(4); + + else if (g_plugin.bIdleOnTerminal && IsTerminalDisconnected()) + Idle_Enter(5); + + else // not idle + Idle_Enter(-1); +} + +void IdleObject_Create() +{ + g_hTimer = SetTimer(nullptr, 0, 2000, IdleTimer); +} + +void IdleObject_Destroy() +{ + KillTimer(nullptr, g_hTimer); +} + +void LoadIdleModule(void) +{ + IdleObject_Create(); + + HookEvent(ME_OPT_INITIALISE, IdleOptInit); +} + +void UnloadIdleModule() +{ + IdleObject_Destroy(); +} diff --git a/src/core/stdautoaway/src/main.cpp b/src/core/stdautoaway/src/main.cpp index 971fd166ff..1022f2a4d5 100644 --- a/src/core/stdautoaway/src/main.cpp +++ b/src/core/stdautoaway/src/main.cpp @@ -2,7 +2,7 @@ Standard auto away module for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stdautoaway/src/options.cpp b/src/core/stdautoaway/src/options.cpp index 17a8f8381b..fed12a11f8 100644 --- a/src/core/stdautoaway/src/options.cpp +++ b/src/core/stdautoaway/src/options.cpp @@ -1,134 +1,134 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static const uint16_t aa_Status[] = { ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND }; - -int IdleGetStatusIndex(uint16_t status) -{ - for (int j = 0; j < _countof(aa_Status); j++) - if (aa_Status[j] == status) - return j; - - return 0; -} - -class COptionsDlg : public CDlgBase -{ - CCtrlEdit edt1sttime; - CCtrlSpin spinIdle; - CCtrlCombo cmbAAStatus; - CCtrlCheck chkShort, chkOnWindows, chkOnMiranda, chkScreenSaver, chkFullScreen, chkLocked; - CCtrlCheck chkPrivate, chkStatusLock, chkTerminal, chkSoundsOff, chkShortIdle; - - void ShowHide() - { - BOOL bChecked = chkShort.GetState(); - chkOnWindows.Enable(bChecked); - chkOnMiranda.Enable(bChecked); - edt1sttime.Enable(bChecked); - - bChecked = chkShortIdle.GetState(); - cmbAAStatus.Enable(bChecked); - chkStatusLock.Enable(bChecked); - } - -public: - COptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_IDLE), - edt1sttime(this, IDC_IDLE1STTIME), - spinIdle(this, IDC_IDLESPIN, 60, 1), - cmbAAStatus(this, IDC_AASTATUS), - chkShort(this, IDC_IDLESHORT), - chkLocked(this, IDC_LOCKED), - chkPrivate(this, IDC_IDLEPRIVATE), - chkTerminal(this, IDC_IDLETERMINAL), - chkOnWindows(this, IDC_IDLEONWINDOWS), - chkSoundsOff(this, IDC_IDLESOUNDSOFF), - chkOnMiranda(this, IDC_IDLEONMIRANDA), - chkShortIdle(this, IDC_AASHORTIDLE), - chkStatusLock(this, IDC_IDLESTATUSLOCK), - chkFullScreen(this, IDC_FULLSCREEN), - chkScreenSaver(this, IDC_SCREENSAVER) - { - CreateLink(chkShort, g_plugin.bIdleCheck); - CreateLink(chkLocked, g_plugin.bIdleOnLock); - CreateLink(chkPrivate, g_plugin.bIdlePrivate); - CreateLink(chkTerminal, g_plugin.bIdleOnTerminal); - CreateLink(chkShortIdle, g_plugin.bAAEnable); - CreateLink(chkOnMiranda, g_plugin.bIdleMethod); - CreateLink(chkSoundsOff, g_plugin.bIdleSoundsOff); - CreateLink(chkStatusLock, g_plugin.bIdleStatusLock); - CreateLink(chkFullScreen, g_plugin.bIdleOnFullScr); - CreateLink(chkScreenSaver, g_plugin.bIdleOnSaver); - - chkShortIdle.OnChange = chkShort.OnChange = Callback(this, &COptionsDlg::onChange); - } - - bool OnInitDialog() override - { - chkOnWindows.SetState(!g_plugin.bIdleMethod); - - spinIdle.SetPosition(g_plugin.iIdleTime1st); - - for (auto &it : aa_Status) - cmbAAStatus.AddString(Clist_GetStatusModeDescription(it, 0)); - cmbAAStatus.SetCurSel(IdleGetStatusIndex(g_plugin.bAAStatus)); - - ShowHide(); - return true; - } - - bool OnApply() override - { - g_plugin.iIdleTime1st = spinIdle.GetPosition(); - - int curSel = cmbAAStatus.GetCurSel(); - if (curSel != CB_ERR) - g_plugin.bAAStatus = aa_Status[curSel]; - - // destroy any current idle and reset settings. - IdleObject_Destroy(); - IdleObject_Create(); - return true; - } - - void onChange(CCtrlCheck*) - { - ShowHide(); - } -}; - -int IdleOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 100000000; - odp.szGroup.a = LPGEN("Status"); - odp.szTitle.a = LPGEN("Idle"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new COptionsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static const uint16_t aa_Status[] = { ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND }; + +int IdleGetStatusIndex(uint16_t status) +{ + for (int j = 0; j < _countof(aa_Status); j++) + if (aa_Status[j] == status) + return j; + + return 0; +} + +class COptionsDlg : public CDlgBase +{ + CCtrlEdit edt1sttime; + CCtrlSpin spinIdle; + CCtrlCombo cmbAAStatus; + CCtrlCheck chkShort, chkOnWindows, chkOnMiranda, chkScreenSaver, chkFullScreen, chkLocked; + CCtrlCheck chkPrivate, chkStatusLock, chkTerminal, chkSoundsOff, chkShortIdle; + + void ShowHide() + { + BOOL bChecked = chkShort.GetState(); + chkOnWindows.Enable(bChecked); + chkOnMiranda.Enable(bChecked); + edt1sttime.Enable(bChecked); + + bChecked = chkShortIdle.GetState(); + cmbAAStatus.Enable(bChecked); + chkStatusLock.Enable(bChecked); + } + +public: + COptionsDlg() : + CDlgBase(g_plugin, IDD_OPT_IDLE), + edt1sttime(this, IDC_IDLE1STTIME), + spinIdle(this, IDC_IDLESPIN, 60, 1), + cmbAAStatus(this, IDC_AASTATUS), + chkShort(this, IDC_IDLESHORT), + chkLocked(this, IDC_LOCKED), + chkPrivate(this, IDC_IDLEPRIVATE), + chkTerminal(this, IDC_IDLETERMINAL), + chkOnWindows(this, IDC_IDLEONWINDOWS), + chkSoundsOff(this, IDC_IDLESOUNDSOFF), + chkOnMiranda(this, IDC_IDLEONMIRANDA), + chkShortIdle(this, IDC_AASHORTIDLE), + chkStatusLock(this, IDC_IDLESTATUSLOCK), + chkFullScreen(this, IDC_FULLSCREEN), + chkScreenSaver(this, IDC_SCREENSAVER) + { + CreateLink(chkShort, g_plugin.bIdleCheck); + CreateLink(chkLocked, g_plugin.bIdleOnLock); + CreateLink(chkPrivate, g_plugin.bIdlePrivate); + CreateLink(chkTerminal, g_plugin.bIdleOnTerminal); + CreateLink(chkShortIdle, g_plugin.bAAEnable); + CreateLink(chkOnMiranda, g_plugin.bIdleMethod); + CreateLink(chkSoundsOff, g_plugin.bIdleSoundsOff); + CreateLink(chkStatusLock, g_plugin.bIdleStatusLock); + CreateLink(chkFullScreen, g_plugin.bIdleOnFullScr); + CreateLink(chkScreenSaver, g_plugin.bIdleOnSaver); + + chkShortIdle.OnChange = chkShort.OnChange = Callback(this, &COptionsDlg::onChange); + } + + bool OnInitDialog() override + { + chkOnWindows.SetState(!g_plugin.bIdleMethod); + + spinIdle.SetPosition(g_plugin.iIdleTime1st); + + for (auto &it : aa_Status) + cmbAAStatus.AddString(Clist_GetStatusModeDescription(it, 0)); + cmbAAStatus.SetCurSel(IdleGetStatusIndex(g_plugin.bAAStatus)); + + ShowHide(); + return true; + } + + bool OnApply() override + { + g_plugin.iIdleTime1st = spinIdle.GetPosition(); + + int curSel = cmbAAStatus.GetCurSel(); + if (curSel != CB_ERR) + g_plugin.bAAStatus = aa_Status[curSel]; + + // destroy any current idle and reset settings. + IdleObject_Destroy(); + IdleObject_Create(); + return true; + } + + void onChange(CCtrlCheck*) + { + ShowHide(); + } +}; + +int IdleOptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = 100000000; + odp.szGroup.a = LPGEN("Status"); + odp.szTitle.a = LPGEN("Idle"); + odp.flags = ODPF_BOLDGROUPS; + odp.pDialog = new COptionsDlg(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/core/stdautoaway/src/stdafx.h b/src/core/stdautoaway/src/stdafx.h index be6446162a..aed3748da5 100644 --- a/src/core/stdautoaway/src/stdafx.h +++ b/src/core/stdautoaway/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdautoaway/src/version.h b/src/core/stdautoaway/src/version.h index 50fcd17758..e85f99715c 100644 --- a/src/core/stdautoaway/src/version.h +++ b/src/core/stdautoaway/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for away state processing." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdAutoAway" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdaway/src/awaymsg.cpp b/src/core/stdaway/src/awaymsg.cpp index 94f097fbe1..391a61383e 100644 --- a/src/core/stdaway/src/awaymsg.cpp +++ b/src/core/stdaway/src/awaymsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdaway/src/main.cpp b/src/core/stdaway/src/main.cpp index 1dfab72a34..bf275c259a 100644 --- a/src/core/stdaway/src/main.cpp +++ b/src/core/stdaway/src/main.cpp @@ -2,7 +2,7 @@ Standard away message processing module for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stdaway/src/options.cpp b/src/core/stdaway/src/options.cpp index 64943461d9..1287a623d3 100644 --- a/src/core/stdaway/src/options.cpp +++ b/src/core/stdaway/src/options.cpp @@ -1,140 +1,140 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static const int statusModes[] = -{ - ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND, - ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_IDLE -}; - -class CAwayMsgOptsDlg : public CDlgBase -{ - struct - { - int ignore; - int noDialog; - int usePrevious; - wchar_t msg[1024]; - } - m_info[_countof(statusModes)]; - - int oldPage = -1; - - CCtrlEdit etdMsg; - CCtrlCombo cmbStatus; - CCtrlCheck chkDontReply, chkUsePrev, chkUseSpecific, chkNoDialog; - -public: - CAwayMsgOptsDlg() : - CDlgBase(g_plugin, IDD_OPT_AWAYMSG), - etdMsg(this, IDC_MSG), - cmbStatus(this, IDC_STATUS), - chkUsePrev(this, IDC_USEPREVIOUS), - chkNoDialog(this, IDC_NODIALOG), - chkDontReply(this, IDC_DONTREPLY), - chkUseSpecific(this, IDC_USESPECIFIC) - { - chkUsePrev.OnChange = chkDontReply.OnChange = chkUseSpecific.OnChange = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); - cmbStatus.OnSelChanged = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); - } - - bool OnInitDialog() override - { - for (auto &it : statusModes) { - if (!(protoModeMsgFlags & Proto_Status2Flag(it))) - continue; - - int j = cmbStatus.AddString(Clist_GetStatusModeDescription(it, 0), it); - m_info[j].ignore = g_plugin.GetStatusModeByte(it, "Ignore"); - m_info[j].noDialog = g_plugin.GetStatusModeByte(it, "NoDlg", true); - m_info[j].usePrevious = g_plugin.GetStatusModeByte(it, "UsePrev"); - - DBVARIANT dbv; - if (g_plugin.getWString(StatusModeToDbSetting(it, "Default"), &dbv)) - if (g_plugin.getWString(StatusModeToDbSetting(it, "Msg"), &dbv)) - dbv.pwszVal = mir_wstrdup(GetDefaultMessage(it)); - mir_wstrcpy(m_info[j].msg, dbv.pwszVal); - mir_free(dbv.pwszVal); - } - - cmbStatus.SetCurSel(0); - onSelChange_Status(0); - return true; - } - - bool OnApply() override - { - onSelChange_Status(0); - - for (int i = cmbStatus.GetCount() - 1; i >= 0; i--) { - int status = cmbStatus.GetItemData(i); - g_plugin.SetStatusModeByte(status, "Ignore", (uint8_t)m_info[i].ignore); - g_plugin.SetStatusModeByte(status, "NoDlg", (uint8_t)m_info[i].noDialog); - g_plugin.SetStatusModeByte(status, "UsePrev", (uint8_t)m_info[i].usePrevious); - g_plugin.setWString(StatusModeToDbSetting(status, "Default"), m_info[i].msg); - } - return true; - } - - void onSelChange_Status(CCtrlCombo*) - { - if (oldPage != -1) { - m_info[oldPage].ignore = chkDontReply.GetState(); - m_info[oldPage].noDialog = chkNoDialog.GetState(); - m_info[oldPage].usePrevious = chkUsePrev.GetState(); - etdMsg.GetText(m_info[oldPage].msg, _countof(m_info[oldPage].msg)); - } - - int i = cmbStatus.GetCurSel(); - chkDontReply.SetState(i < 0 ? 0 : m_info[i].ignore); - chkNoDialog.SetState(i < 0 ? 0 : m_info[i].noDialog); - chkUsePrev.SetState(i < 0 ? 0 : m_info[i].usePrevious); - chkUseSpecific.SetState(i < 0 ? 0 : !m_info[i].usePrevious); - - etdMsg.SetText(i < 0 ? L"" : m_info[i].msg); - - chkNoDialog.Enable(i < 0 ? 0 : !m_info[i].ignore); - chkUsePrev.Enable(i < 0 ? 0 : !m_info[i].ignore); - chkUseSpecific.Enable(i < 0 ? 0 : !m_info[i].ignore); - etdMsg.Enable(i < 0 ? 0 : !(m_info[i].ignore || m_info[i].usePrevious)); - oldPage = i; - } -}; - -int AwayMsgOptInitialise(WPARAM wParam, LPARAM) -{ - if (protoModeMsgFlags == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = 870000000; - odp.szTitle.a = LPGEN("Status messages"); - odp.szGroup.a = LPGEN("Status"); - odp.pDialog = new CAwayMsgOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static const int statusModes[] = +{ + ID_STATUS_OFFLINE, ID_STATUS_ONLINE, ID_STATUS_AWAY, ID_STATUS_NA, ID_STATUS_OCCUPIED, ID_STATUS_DND, + ID_STATUS_FREECHAT, ID_STATUS_INVISIBLE, ID_STATUS_IDLE +}; + +class CAwayMsgOptsDlg : public CDlgBase +{ + struct + { + int ignore; + int noDialog; + int usePrevious; + wchar_t msg[1024]; + } + m_info[_countof(statusModes)]; + + int oldPage = -1; + + CCtrlEdit etdMsg; + CCtrlCombo cmbStatus; + CCtrlCheck chkDontReply, chkUsePrev, chkUseSpecific, chkNoDialog; + +public: + CAwayMsgOptsDlg() : + CDlgBase(g_plugin, IDD_OPT_AWAYMSG), + etdMsg(this, IDC_MSG), + cmbStatus(this, IDC_STATUS), + chkUsePrev(this, IDC_USEPREVIOUS), + chkNoDialog(this, IDC_NODIALOG), + chkDontReply(this, IDC_DONTREPLY), + chkUseSpecific(this, IDC_USESPECIFIC) + { + chkUsePrev.OnChange = chkDontReply.OnChange = chkUseSpecific.OnChange = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); + cmbStatus.OnSelChanged = Callback(this, &CAwayMsgOptsDlg::onSelChange_Status); + } + + bool OnInitDialog() override + { + for (auto &it : statusModes) { + if (!(protoModeMsgFlags & Proto_Status2Flag(it))) + continue; + + int j = cmbStatus.AddString(Clist_GetStatusModeDescription(it, 0), it); + m_info[j].ignore = g_plugin.GetStatusModeByte(it, "Ignore"); + m_info[j].noDialog = g_plugin.GetStatusModeByte(it, "NoDlg", true); + m_info[j].usePrevious = g_plugin.GetStatusModeByte(it, "UsePrev"); + + DBVARIANT dbv; + if (g_plugin.getWString(StatusModeToDbSetting(it, "Default"), &dbv)) + if (g_plugin.getWString(StatusModeToDbSetting(it, "Msg"), &dbv)) + dbv.pwszVal = mir_wstrdup(GetDefaultMessage(it)); + mir_wstrcpy(m_info[j].msg, dbv.pwszVal); + mir_free(dbv.pwszVal); + } + + cmbStatus.SetCurSel(0); + onSelChange_Status(0); + return true; + } + + bool OnApply() override + { + onSelChange_Status(0); + + for (int i = cmbStatus.GetCount() - 1; i >= 0; i--) { + int status = cmbStatus.GetItemData(i); + g_plugin.SetStatusModeByte(status, "Ignore", (uint8_t)m_info[i].ignore); + g_plugin.SetStatusModeByte(status, "NoDlg", (uint8_t)m_info[i].noDialog); + g_plugin.SetStatusModeByte(status, "UsePrev", (uint8_t)m_info[i].usePrevious); + g_plugin.setWString(StatusModeToDbSetting(status, "Default"), m_info[i].msg); + } + return true; + } + + void onSelChange_Status(CCtrlCombo*) + { + if (oldPage != -1) { + m_info[oldPage].ignore = chkDontReply.GetState(); + m_info[oldPage].noDialog = chkNoDialog.GetState(); + m_info[oldPage].usePrevious = chkUsePrev.GetState(); + etdMsg.GetText(m_info[oldPage].msg, _countof(m_info[oldPage].msg)); + } + + int i = cmbStatus.GetCurSel(); + chkDontReply.SetState(i < 0 ? 0 : m_info[i].ignore); + chkNoDialog.SetState(i < 0 ? 0 : m_info[i].noDialog); + chkUsePrev.SetState(i < 0 ? 0 : m_info[i].usePrevious); + chkUseSpecific.SetState(i < 0 ? 0 : !m_info[i].usePrevious); + + etdMsg.SetText(i < 0 ? L"" : m_info[i].msg); + + chkNoDialog.Enable(i < 0 ? 0 : !m_info[i].ignore); + chkUsePrev.Enable(i < 0 ? 0 : !m_info[i].ignore); + chkUseSpecific.Enable(i < 0 ? 0 : !m_info[i].ignore); + etdMsg.Enable(i < 0 ? 0 : !(m_info[i].ignore || m_info[i].usePrevious)); + oldPage = i; + } +}; + +int AwayMsgOptInitialise(WPARAM wParam, LPARAM) +{ + if (protoModeMsgFlags == 0) + return 0; + + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS; + odp.position = 870000000; + odp.szTitle.a = LPGEN("Status messages"); + odp.szGroup.a = LPGEN("Status"); + odp.pDialog = new CAwayMsgOptsDlg(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/core/stdaway/src/sendmsg.cpp b/src/core/stdaway/src/sendmsg.cpp index 1eaaac5cc4..dff644a928 100644 --- a/src/core/stdaway/src/sendmsg.cpp +++ b/src/core/stdaway/src/sendmsg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdaway/src/stdafx.h b/src/core/stdaway/src/stdafx.h index 4cd3dd5b24..83d5a859c9 100644 --- a/src/core/stdaway/src/stdafx.h +++ b/src/core/stdaway/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdaway/src/version.h b/src/core/stdaway/src/version.h index 1cbc94e44c..03131a5179 100644 --- a/src/core/stdaway/src/version.h +++ b/src/core/stdaway/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for the away messages processing." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdAway" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdclist/src/clc.h b/src/core/stdclist/src/clc.h index 9ecb9767af..c5a6581b38 100644 --- a/src/core/stdclist/src/clc.h +++ b/src/core/stdclist/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/clcfonts.cpp b/src/core/stdclist/src/clcfonts.cpp index c5c4af1a9a..e435026332 100644 --- a/src/core/stdclist/src/clcfonts.cpp +++ b/src/core/stdclist/src/clcfonts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/clcopts.cpp b/src/core/stdclist/src/clcopts.cpp index 688fba0c86..077cd80ca0 100644 --- a/src/core/stdclist/src/clcopts.cpp +++ b/src/core/stdclist/src/clcopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/clcpaint.cpp b/src/core/stdclist/src/clcpaint.cpp index 9bf6af7581..d2e59b1878 100644 --- a/src/core/stdclist/src/clcpaint.cpp +++ b/src/core/stdclist/src/clcpaint.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/clistmenus.cpp b/src/core/stdclist/src/clistmenus.cpp index 7d10154fde..57da5b4f22 100644 --- a/src/core/stdclist/src/clistmenus.cpp +++ b/src/core/stdclist/src/clistmenus.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/clistopts.cpp b/src/core/stdclist/src/clistopts.cpp index 29683b6506..3a6f435e24 100644 --- a/src/core/stdclist/src/clistopts.cpp +++ b/src/core/stdclist/src/clistopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/cluiopts.cpp b/src/core/stdclist/src/cluiopts.cpp index 785c0605b1..4816b59b7f 100644 --- a/src/core/stdclist/src/cluiopts.cpp +++ b/src/core/stdclist/src/cluiopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/contact.cpp b/src/core/stdclist/src/contact.cpp index daba1cdc61..cf9ba02cb7 100644 --- a/src/core/stdclist/src/contact.cpp +++ b/src/core/stdclist/src/contact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/init.cpp b/src/core/stdclist/src/init.cpp index 3ca412c5f7..f379863284 100644 --- a/src/core/stdclist/src/init.cpp +++ b/src/core/stdclist/src/init.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/stdafx.cxx b/src/core/stdclist/src/stdafx.cxx index f64d25234b..ebbde0ade1 100644 --- a/src/core/stdclist/src/stdafx.cxx +++ b/src/core/stdclist/src/stdafx.cxx @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stdclist/src/stdafx.h b/src/core/stdclist/src/stdafx.h index d50af5e471..62b233fe55 100644 --- a/src/core/stdclist/src/stdafx.h +++ b/src/core/stdclist/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdclist/src/version.h b/src/core/stdclist/src/version.h index 1b178cd258..a652e79523 100644 --- a/src/core/stdclist/src/version.h +++ b/src/core/stdclist/src/version.h @@ -9,4 +9,4 @@ #define __DESCRIPTION "Core module for displaying contacts." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdClist" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdcrypt/src/encrypt.cpp b/src/core/stdcrypt/src/encrypt.cpp index e9689aa374..9d605848a2 100644 --- a/src/core/stdcrypt/src/encrypt.cpp +++ b/src/core/stdcrypt/src/encrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdcrypt/src/main.cpp b/src/core/stdcrypt/src/main.cpp index bc53c00f54..e1dc9218a6 100644 --- a/src/core/stdcrypt/src/main.cpp +++ b/src/core/stdcrypt/src/main.cpp @@ -1,7 +1,7 @@ /* Standard encryption plugin for Miranda NG -Copyright (C) 2012-22 George Hazan +Copyright (C) 2012-23 George Hazan 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 diff --git a/src/core/stdcrypt/src/stdafx.h b/src/core/stdcrypt/src/stdafx.h index 1ea23e6c16..4b6c1e7319 100644 --- a/src/core/stdcrypt/src/stdafx.h +++ b/src/core/stdcrypt/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdcrypt/src/stdcrypt.h b/src/core/stdcrypt/src/stdcrypt.h index 791e827d1a..3966a81631 100644 --- a/src/core/stdcrypt/src/stdcrypt.h +++ b/src/core/stdcrypt/src/stdcrypt.h @@ -1,7 +1,7 @@ /* Standard encryption plugin for Miranda NG -Copyright (C) 2012-22 George Hazan +Copyright (C) 2012-23 George Hazan 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 diff --git a/src/core/stdcrypt/src/utils.cpp b/src/core/stdcrypt/src/utils.cpp index fd5b3cff22..a3279e805e 100644 --- a/src/core/stdcrypt/src/utils.cpp +++ b/src/core/stdcrypt/src/utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdcrypt/src/version.h b/src/core/stdcrypt/src/version.h index 6f6d4f9c96..30de347877 100644 --- a/src/core/stdcrypt/src/version.h +++ b/src/core/stdcrypt/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for encryption." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdCrypt" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdemail/src/email.cpp b/src/core/stdemail/src/email.cpp index b6c68d672f..0f40657fff 100644 --- a/src/core/stdemail/src/email.cpp +++ b/src/core/stdemail/src/email.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdemail/src/main.cpp b/src/core/stdemail/src/main.cpp index 344e9501a9..19aaf95380 100644 --- a/src/core/stdemail/src/main.cpp +++ b/src/core/stdemail/src/main.cpp @@ -2,7 +2,7 @@ Standard e-mail urls launcher for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stdemail/src/stdafx.h b/src/core/stdemail/src/stdafx.h index 8214c32bfe..4520b2f62f 100644 --- a/src/core/stdemail/src/stdafx.h +++ b/src/core/stdemail/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdemail/src/version.h b/src/core/stdemail/src/version.h index a158424f02..dd12d45a66 100644 --- a/src/core/stdemail/src/version.h +++ b/src/core/stdemail/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for e-mail urls handling." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdEmail" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdfile/src/file.cpp b/src/core/stdfile/src/file.cpp index 8dcb922619..234f84404f 100644 --- a/src/core/stdfile/src/file.cpp +++ b/src/core/stdfile/src/file.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/file.h b/src/core/stdfile/src/file.h index b89671ab8a..2bdddb04e5 100644 --- a/src/core/stdfile/src/file.h +++ b/src/core/stdfile/src/file.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/fileexistsdlg.cpp b/src/core/stdfile/src/fileexistsdlg.cpp index bd715cfa24..c2e4f6dfa8 100644 --- a/src/core/stdfile/src/fileexistsdlg.cpp +++ b/src/core/stdfile/src/fileexistsdlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/fileopts.cpp b/src/core/stdfile/src/fileopts.cpp index f5385174e1..f0d9e1bda2 100644 --- a/src/core/stdfile/src/fileopts.cpp +++ b/src/core/stdfile/src/fileopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/filerecvdlg.cpp b/src/core/stdfile/src/filerecvdlg.cpp index 54aefe44c5..7a364e210e 100644 --- a/src/core/stdfile/src/filerecvdlg.cpp +++ b/src/core/stdfile/src/filerecvdlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/filesenddlg.cpp b/src/core/stdfile/src/filesenddlg.cpp index 017725e89d..024cf68129 100644 --- a/src/core/stdfile/src/filesenddlg.cpp +++ b/src/core/stdfile/src/filesenddlg.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/filexferdlg.cpp b/src/core/stdfile/src/filexferdlg.cpp index bcd6c16c54..32ef89ecf3 100644 --- a/src/core/stdfile/src/filexferdlg.cpp +++ b/src/core/stdfile/src/filexferdlg.cpp @@ -1,765 +1,765 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include -#include "file.h" - -static int CheckVirusScanned(HWND hwnd, FileDlgData *dat, int i) -{ - if (dat->send) return 1; - if (dat->fileVirusScanned == nullptr) return 0; - if (dat->fileVirusScanned[i]) return 1; - if (g_plugin.getByte("WarnBeforeOpening", 1) == 0) return 1; - - return IDYES == MessageBox(hwnd, - TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"), - TranslateT("File received"), - MB_YESNO | MB_DEFBUTTON2); -} - -#define M_VIRUSSCANDONE (WM_USER+100) -struct virusscanthreadstartinfo -{ - wchar_t *szFile; - int returnCode; - HWND hwndReply; -}; - -wchar_t* PFTS_StringToTchar(int flags, const wchar_t *s) -{ - if (flags & PFTS_UTF) - return mir_utf8decodeW((char*)s); - if (flags & PFTS_UNICODE) - return mir_wstrdup(s); - return mir_a2u((char*)s); -} - -int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS *ft, const wchar_t *s, wchar_t *r) -{ - if (ft->flags & PFTS_UTF) { - wchar_t *ts = mir_utf8decodeW((char*)s); - int res = mir_wstrcmp(ts, r); - mir_free(ts); - return res; - } - if (ft->flags & PFTS_UNICODE) - return mir_wstrcmp(s, r); - - wchar_t *ts = mir_a2u((char*)s); - int res = mir_wstrcmp(ts, r); - mir_free(ts); - return res; -} - -static void SetOpenFileButtonStyle(HWND hwndButton, int enabled) -{ - EnableWindow(hwndButton, enabled); -} - -void FillSendData(FileDlgData *dat, DBEVENTINFO &dbei) -{ - dbei.szModule = Proto_GetBaseAccountName(dat->hContact); - dbei.eventType = EVENTTYPE_FILE; - dbei.flags = DBEF_SENT; - dbei.timestamp = time(0); - char *szFileNames = mir_utf8encodeW(dat->szFilenames), *szMsg = mir_utf8encodeW(dat->szMsg); - dbei.flags |= DBEF_UTF; - - dbei.cbBlob = int(sizeof(uint32_t) + mir_strlen(szFileNames) + mir_strlen(szMsg) + 2); - dbei.pBlob = (uint8_t*)mir_alloc(dbei.cbBlob); - *(PDWORD)dbei.pBlob = 0; - mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t), szFileNames); - mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t) + mir_strlen(szFileNames) + 1, szMsg); - - mir_free(szFileNames), mir_free(szMsg); -} - -static void __cdecl RunVirusScannerThread(virusscanthreadstartinfo *info) -{ - DBVARIANT dbv; - if (!g_plugin.getWString("ScanCmdLine", &dbv)) { - if (dbv.pwszVal[0]) { - STARTUPINFO si = { 0 }; - si.cb = sizeof(si); - wchar_t *pszReplace = wcsstr(dbv.pwszVal, L"%f"); - wchar_t szCmdLine[768]; - if (pszReplace) { - if (info->szFile[mir_wstrlen(info->szFile) - 1] == '\\') - info->szFile[mir_wstrlen(info->szFile) - 1] = '\0'; - *pszReplace = 0; - mir_snwprintf(szCmdLine, L"%s\"%s\"%s", dbv.pwszVal, info->szFile, pszReplace + 2); - } - else - wcsncpy_s(szCmdLine, dbv.pwszVal, _TRUNCATE); - - PROCESS_INFORMATION pi; - if (CreateProcess(nullptr, szCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { - if (WaitForSingleObject(pi.hProcess, 3600 * 1000) == WAIT_OBJECT_0) - PostMessage(info->hwndReply, M_VIRUSSCANDONE, info->returnCode, 0); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - } - db_free(&dbv); - } - mir_free(info->szFile); - mir_free(info); -} - -static void SetFilenameControls(HWND hwndDlg, FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts) -{ - wchar_t msg[MAX_PATH]; - wchar_t *fnbuf = nullptr, *fn = nullptr; - SHFILEINFO shfi = {}; - - if (fts->szCurrentFile.w) { - fnbuf = mir_wstrdup(fts->szCurrentFile.w); - if ((fn = wcsrchr(fnbuf, '\\')) == nullptr) - fn = fnbuf; - else fn++; - } - - if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = nullptr; - - if (fn && (fts->totalFiles > 1)) { - mir_snwprintf(msg, L"%s: %s (%d %s %d)", Clist_GetContactDisplayName(fts->hContact), fn, fts->currentFileNumber + 1, TranslateT("of"), fts->totalFiles); - - SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIcon = shfi.hIcon; - } - else if (fn) { - mir_snwprintf(msg, L"%s: %s", Clist_GetContactDisplayName(fts->hContact), fn); - - SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIcon = shfi.hIcon; - } - else { - mir_wstrncpy(msg, Clist_GetContactDisplayName(fts->hContact), _countof(msg)); - HICON hIcon = Skin_LoadIcon(SKINICON_OTHER_DOWNARROW); - dat->hIcon = CopyIcon(hIcon); - IcoLib_ReleaseIcon(hIcon, NULL); - } - - mir_free(fnbuf); - - SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon); - SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg); -} - -enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN }; -static void SetFtStatus(HWND hwndDlg, wchar_t *text, int mode) -{ - SetDlgItemText(hwndDlg, IDC_STATUS, TranslateW(text)); - SetDlgItemText(hwndDlg, IDC_TRANSFERCOMPLETED, TranslateW(text)); - - ShowWindow(GetDlgItem(hwndDlg, IDC_STATUS), (mode == FTS_TEXT) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_FILEICON), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); -} - -static void HideProgressControls(HWND hwndDlg) -{ - RECT rc; - char buf[64]; - - GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc); - MapWindowPoints(nullptr, hwndDlg, (LPPOINT)&rc, 2); - SetWindowPos(hwndDlg, nullptr, 0, 0, 100, rc.bottom + 3, SWP_NOMOVE | SWP_NOZORDER); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE); - - _strtime(buf); - SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf); - - PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg); -} - -static int FileTransferDlgResizer(HWND, LPARAM param, UTILRESIZECONTROL *urc) -{ - auto *dat = (FileDlgData *)param; - - switch (urc->wId) { - case IDC_CONTACTNAME: - case IDC_STATUS: - case IDC_ALLFILESPROGRESS: - case IDC_TRANSFERCOMPLETED: - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - case IDC_FRAME: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - case IDC_ALLPRECENTS: - case IDCANCEL: - case IDC_OPENFILE: - case IDC_OPENFOLDER: - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - case IDC_ALLTRANSFERRED: - if (dat->waitingForAcceptance) - return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; - - urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3; - return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; - - case IDC_ALLSPEED: - if (dat->waitingForAcceptance) - return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; - - urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx; - urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3; - return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - FileDlgData *dat = (FileDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - dat = (FileDlgData *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - dat->hNotifyEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_RECVEVENT); - dat->transferStatus.currentFileNumber = -1; - if (dat->send) { - if (db_mc_isMeta(dat->hContact)) - dat->hContact = db_mc_getMostOnline(dat->hContact); - dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILE, (WPARAM)dat->szMsg, (LPARAM)dat->files); - SetFtStatus(hwndDlg, LPGENW("Request sent, waiting for acceptance..."), FTS_TEXT); - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - dat->waitingForAcceptance = 1; - // hide "open" button since it may cause potential access violations... - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE); - ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE); - } - else { //recv - CreateDirectoryTreeW(dat->szSavePath); - dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILEALLOW, (WPARAM)dat->fs, (LPARAM)dat->szSavePath); - dat->transferStatus.szWorkingDir.w = mir_wstrdup(dat->szSavePath); - if (!Contact::OnList(dat->hContact)) - dat->resumeBehaviour = FILERESUME_ASK; - else - dat->resumeBehaviour = g_plugin.getByte("IfExists", FILERESUME_ASK); - SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); - } - - /* check we actually got an fs handle back from the protocol */ - if (!dat->fs) { - SetFtStatus(hwndDlg, LPGENW("Unable to initiate transfer."), FTS_TEXT); - dat->waitingForAcceptance = 0; - } - { - LOGFONT lf; - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); - GetObject(hFont, sizeof(lf), &lf); - lf.lfWeight = FW_BOLD; - hFont = CreateFontIndirect(&lf); - SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_SETFONT, (WPARAM)hFont, 0); - - SHFILEINFO shfi = {}; - SHGetFileInfo(L"", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); - dat->hIconFolder = shfi.hIcon; - } - dat->hIcon = nullptr; - { - char *szProto = Proto_GetBaseAccountName(dat->hContact); - uint16_t status = db_get_w(dat->hContact, szProto, "Status", ID_STATUS_ONLINE); - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadProtoIcon(szProto, status)); - } - - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0); - SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, TRUE, 0); - - Button_SetSkin_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open...")); - SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, TRUE, 0); - - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder); - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0); - SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, TRUE, 0); - - Button_SetSkin_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel")); - - SetDlgItemText(hwndDlg, IDC_CONTACTNAME, Clist_GetContactDisplayName(dat->hContact)); - - if (!dat->waitingForAcceptance) - SetTimer(hwndDlg, 1, 1000, nullptr); - return TRUE; - - case WM_TIMER: - memmove(dat->bytesRecvedHistory + 1, dat->bytesRecvedHistory, sizeof(dat->bytesRecvedHistory) - sizeof(dat->bytesRecvedHistory[0])); - dat->bytesRecvedHistory[0] = dat->transferStatus.totalProgress; - if (dat->bytesRecvedHistorySize < _countof(dat->bytesRecvedHistory)) - dat->bytesRecvedHistorySize++; - - wchar_t szSpeed[32], szTime[32], szDisplay[96]; - SYSTEMTIME st; - ULARGE_INTEGER li; - FILETIME ft; - - GetSensiblyFormattedSize((dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) / dat->bytesRecvedHistorySize, szSpeed, _countof(szSpeed), 0, 1, NULL); - if (dat->bytesRecvedHistory[0] == dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) - mir_wstrcpy(szTime, L"??:??:??"); - else { - li.QuadPart = 10000000ll * (dat->transferStatus.currentFileSize - dat->transferStatus.currentFileProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); - ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, &st); - GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); - } - if (dat->bytesRecvedHistory[0] != dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) { - li.QuadPart = 10000000ll * (dat->transferStatus.totalBytes - dat->transferStatus.totalProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); - ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; - FileTimeToSystemTime(&ft, &st); - GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); - } - - mir_snwprintf(szDisplay, L"%s/%s (%s %s)", szSpeed, TranslateT("sec"), szTime, TranslateT("remaining")); - SetDlgItemText(hwndDlg, IDC_ALLSPEED, szDisplay); - break; - - case WM_MEASUREITEM: - return Menu_MeasureItem(lParam); - - case WM_DRAWITEM: - return Menu_DrawItem(lParam); - - case WM_FT_CLEANUP: - if (!dat->fs) { - PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); - DestroyWindow(hwndDlg); - } - break; - - case WM_COMMAND: - if (!dat) - break; - - if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, dat->hContact)) - break; - - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); - DestroyWindow(hwndDlg); - break; - - case IDC_CONTACT: - { - RECT rc; - HMENU hMenu = Menu_BuildContactMenu(dat->hContact); - GetWindowRect((HWND)lParam, &rc); - TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL); - DestroyMenu(hMenu); - } - break; - - case IDC_TRANSFERCOMPLETED: - if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0)) { - ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW); - break; - } - - case IDC_OPENFOLDER: - { - wchar_t *path = dat->transferStatus.szWorkingDir.w; - if (!path || !path[0]) { - path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); - wchar_t *p = wcsrchr(path, '\\'); if (p) *p = 0; - } - - if (path) ShellExecute(NULL, L"open", path, NULL, NULL, SW_SHOW); - } - break; - - case IDC_OPENFILE: - wchar_t **files; - if (dat->send) { - if (dat->files == nullptr) - files = dat->transferStatus.pszFiles.w; - else - files = dat->files; - } - else files = dat->files; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - - if (files && *files) { - int limit; - wchar_t *pszFilename, *pszNewFileName; - - if (dat->send) - limit = dat->transferStatus.totalFiles; - else - limit = dat->transferStatus.currentFileNumber; - - // Loop over all transfered files and add them to the menu - for (int i = 0; i < limit; i++) { - pszFilename = wcsrchr(files[i], '\\'); - if (pszFilename == nullptr) - pszFilename = files[i]; - else - pszFilename++; - - if (pszFilename) { - size_t cbFileNameLen = mir_wstrlen(pszFilename); - - pszNewFileName = (wchar_t*)mir_alloc(cbFileNameLen * 2 * sizeof(wchar_t)); - wchar_t *p = pszNewFileName; - for (size_t pszlen = 0; pszlen < cbFileNameLen; pszlen++) { - *p++ = pszFilename[pszlen]; - if (pszFilename[pszlen] == '&') - *p++ = '&'; - } - *p = '\0'; - AppendMenu(hMenu, MF_STRING, i + 10, pszNewFileName); - mir_free(pszNewFileName); - } - } - } - - RECT rc; - GetWindowRect((HWND)lParam, &rc); - CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED); - int ret = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, nullptr); - CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED); - DestroyMenu(hMenu); - - if (ret == 1) { - wchar_t *path = dat->transferStatus.szWorkingDir.w; - if (!path || !path[0]) { - path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); - wchar_t *p = wcsrchr(path, '\\'); - if (p) - *p = 0; - } - - if (path) ShellExecute(nullptr, L"open", path, nullptr, nullptr, SW_SHOW); - } - else if (ret && CheckVirusScanned(hwndDlg, dat, ret)) - ShellExecute(nullptr, nullptr, files[ret - 10], nullptr, nullptr, SW_SHOW); - } - break; - - case M_FILEEXISTSDLGREPLY: - EnableWindow(hwndDlg, TRUE); - { - PROTOFILERESUME *pfr = (PROTOFILERESUME *)lParam; - wchar_t *szOriginalFilename = (wchar_t *)wParam; - char *szProto = Proto_GetBaseAccountName(dat->hContact); - - switch (pfr->action) { - case FILERESUME_CANCEL: - if (dat->fs) ProtoChainSend(dat->hContact, PSS_FILECANCEL, (WPARAM)dat->fs, 0); - dat->fs = nullptr; - mir_free(szOriginalFilename); - if (pfr->szFilename) mir_free((char *)pfr->szFilename); - mir_free(pfr); - return 0; - case FILERESUME_RESUMEALL: - case FILERESUME_OVERWRITEALL: - dat->resumeBehaviour = pfr->action; - pfr->action &= ~FILERESUMEF_ALL; - break; - case FILERESUME_RENAMEALL: - pfr->action = FILERESUME_RENAME; - { - wchar_t *pszExtension, *pszFilename; - if ((pszFilename = wcsrchr(szOriginalFilename, '\\')) == nullptr) pszFilename = szOriginalFilename; - if ((pszExtension = wcsrchr(pszFilename + 1, '.')) == nullptr) pszExtension = pszFilename + mir_wstrlen(pszFilename); - if (pfr->szFilename) mir_free((wchar_t *)pfr->szFilename); - size_t size = (pszExtension - szOriginalFilename) + 21 + mir_wstrlen(pszExtension); - pfr->szFilename = (wchar_t *)mir_alloc(sizeof(wchar_t) * size); - for (int i = 1;; i++) { - mir_snwprintf((wchar_t *)pfr->szFilename, size, L"%.*s (%u)%s", pszExtension - szOriginalFilename, szOriginalFilename, i, pszExtension); - if (_waccess(pfr->szFilename, 0) != 0) - break; - } - } - break; - } - mir_free(szOriginalFilename); - CallProtoService(szProto, PS_FILERESUME, (WPARAM)dat->fs, (LPARAM)pfr); - delete pfr; - } - break; - - case HM_RECVEVENT: - { - ACKDATA *ack = (ACKDATA *)lParam; - if (ack->hProcess != dat->fs) break; - if (ack->type != ACKTYPE_FILE) break; - if (ack->hContact != dat->hContact) break; - - if (dat->waitingForAcceptance) { - SetTimer(hwndDlg, 1, 1000, nullptr); - dat->waitingForAcceptance = 0; - } - - switch (ack->result) { - case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break; - case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break; - case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break; - case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break; - case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break; - case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENW("Initializing..."), FTS_TEXT); break; - case ACKRESULT_NEXTFILE: - SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT); - SetDlgItemTextA(hwndDlg, IDC_FILENAME, ""); - if (dat->transferStatus.currentFileNumber == 1 && dat->transferStatus.totalFiles > 1 && !dat->send) - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - if (dat->transferStatus.currentFileNumber != -1 && dat->files && !dat->send && g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE) == VIRUSSCAN_DURINGDL) { - if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) - PostMessage(hwndDlg, M_VIRUSSCANDONE, dat->transferStatus.currentFileNumber, 0); - else { - virusscanthreadstartinfo *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); - vstsi->hwndReply = hwndDlg; - vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); - vstsi->returnCode = dat->transferStatus.currentFileNumber; - mir_forkThread(RunVirusScannerThread, vstsi); - } - } - break; - - case ACKRESULT_FILERESUME: - UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS *)ack->lParam); - { - PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus; - SetFilenameControls(hwndDlg, dat, fts); - if (_waccess(fts->szCurrentFile.w, 0)) - break; - - SetFtStatus(hwndDlg, LPGENW("File already exists"), FTS_TEXT); - if (dat->resumeBehaviour == FILERESUME_ASK) { - TDlgProcFileExistsParam param = { hwndDlg, fts }; - ShowWindow(hwndDlg, SW_SHOWNORMAL); - CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILEEXISTS), hwndDlg, DlgProcFileExists, (LPARAM)¶m); - EnableWindow(hwndDlg, FALSE); - } - else { - PROTOFILERESUME *pfr = new PROTOFILERESUME(); - pfr->action = dat->resumeBehaviour; - pfr->szFilename = nullptr; - PostMessage(hwndDlg, M_FILEEXISTSDLGREPLY, (WPARAM)mir_wstrdup(fts->szCurrentFile.w), (LPARAM)pfr); - } - } - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); - return TRUE; - - case ACKRESULT_DATA: - { - PROTOFILETRANSFERSTATUS *fts = (PROTOFILETRANSFERSTATUS *)ack->lParam; - wchar_t str[64], str2[64], szSizeDone[32], szSizeTotal[32];//, *contactName; - - if (dat->fileVirusScanned == nullptr) - dat->fileVirusScanned = (int *)mir_calloc(sizeof(int) * fts->totalFiles); - - // This needs to be here - otherwise we get holes in the files array - if (!dat->send) { - if (dat->files == nullptr) - dat->files = (wchar_t **)mir_calloc((fts->totalFiles + 1) * sizeof(wchar_t *)); - if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == nullptr) - dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->szCurrentFile.w); - } - - /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */ - if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650)) - break; // the last update was less than a second ago! - dat->dwTicks = GetTickCount(); - - // Update local transfer status with data from protocol - UpdateProtoFileTransferStatus(&dat->transferStatus, fts); - fts = &dat->transferStatus; - - bool firstTime = false; - if ((GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0) { - SetFtStatus(hwndDlg, (fts->flags & PFTS_SENDING) ? LPGENW("Sending...") : LPGENW("Receiving..."), FTS_PROGRESS); - SetFilenameControls(hwndDlg, dat, fts); - firstTime = true; - } - - const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0); - const unsigned long nextPos = fts->totalBytes ? (100ll * fts->totalProgress / fts->totalBytes) : 0; - if (lastPos != nextPos || firstTime) { - SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0); - mir_snwprintf(str, L"%u%%", nextPos); - SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str); - } - - int units; - GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, _countof(szSizeTotal), 0, 1, &units); - GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, _countof(szSizeDone), units, 0, NULL); - mir_snwprintf(str, L"%s/%s", szSizeDone, szSizeTotal); - str2[0] = 0; - GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, _countof(str2)); - if (mir_wstrcmp(str, str2)) - SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str); - } - break; - - case ACKRESULT_SUCCESS: - case ACKRESULT_FAILED: - case ACKRESULT_DENIED: - HideProgressControls(hwndDlg); - KillTimer(hwndDlg, 1); - if (!dat->send) - SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); - SetDlgItemText(hwndDlg, IDCANCEL, TranslateT("Close")); - if (dat->hNotifyEvent) - UnhookEvent(dat->hNotifyEvent); - dat->hNotifyEvent = nullptr; - - if (ack->result == ACKRESULT_DENIED) { - dat->fs = nullptr; /* protocol will free structure */ - Skin_PlaySound("FileDenied"); - SetFtStatus(hwndDlg, LPGENW("File transfer denied"), FTS_TEXT); - } - else if (ack->result == ACKRESULT_FAILED) { - dat->fs = nullptr; /* protocol will free structure */ - Skin_PlaySound("FileFailed"); - SetFtStatus(hwndDlg, LPGENW("File transfer failed"), FTS_TEXT); - } - else { - Skin_PlaySound("FileDone"); - if (dat->send) { - dat->fs = nullptr; /* protocol will free structure */ - SetFtStatus(hwndDlg, LPGENW("Transfer completed."), FTS_TEXT); - - DBEVENTINFO dbei = {}; - FillSendData(dat, dbei); - db_event_add(dat->hContact, &dbei); - if (dbei.pBlob) - mir_free(dbei.pBlob); - dat->files = nullptr; //protocol library frees this - } - else { - SetFtStatus(hwndDlg, - (dat->transferStatus.totalFiles == 1) ? - LPGENW("Transfer completed, open file.") : - LPGENW("Transfer completed, open folder."), - FTS_OPEN); - - int useScanner = g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE); - if (useScanner != VIRUSSCAN_DISABLE) { - auto *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); - vstsi->hwndReply = hwndDlg; - if (useScanner == VIRUSSCAN_DURINGDL) { - vstsi->returnCode = dat->transferStatus.currentFileNumber; - if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) { - PostMessage(hwndDlg, M_VIRUSSCANDONE, vstsi->returnCode, 0); - mir_free(vstsi); - vstsi = nullptr; - } - else vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); - } - else { - vstsi->szFile = mir_wstrdup(dat->transferStatus.szWorkingDir.w); - vstsi->returnCode = -1; - } - SetFtStatus(hwndDlg, LPGENW("Scanning for viruses..."), FTS_TEXT); - if (vstsi) - mir_forkThread(RunVirusScannerThread, vstsi); - } - else dat->fs = nullptr; /* protocol will free structure */ - - dat->transferStatus.currentFileNumber = dat->transferStatus.totalFiles; - } - } - - PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg); - break; - } - } - break; - - case M_VIRUSSCANDONE: - { - int done = 1; - if ((int)wParam == -1) { - for (int i = 0; i < dat->transferStatus.totalFiles; i++) - dat->fileVirusScanned[i] = 1; - } - else { - dat->fileVirusScanned[wParam] = 1; - for (int i = 0; i < dat->transferStatus.totalFiles; i++) - if (!dat->fileVirusScanned[i]) { - done = 0; - break; - } - } - if (done) { - dat->fs = nullptr; /* protocol will free structure */ - SetFtStatus(hwndDlg, LPGENW("Transfer and virus scan complete"), FTS_TEXT); - } - } - break; - - case WM_SIZE: - Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FILETRANSFERINFO), FileTransferDlgResizer, LPARAM(dat)); - - RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); - break; - - case WM_DESTROY: - KillTimer(hwndDlg, 1); - - HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); - DeleteObject(hFont); - - Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT); - Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE); - Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL); - - delete dat; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); - break; - } - return FALSE; -} - -FileDlgData::~FileDlgData() -{ - if (fs) - ProtoChainSend(hContact, PSS_FILECANCEL, (WPARAM)fs, 0); - if (hPreshutdownEvent) - UnhookEvent(hPreshutdownEvent); - if (hNotifyEvent) - UnhookEvent(hNotifyEvent); - - FreeProtoFileTransferStatus(&transferStatus); - FreeFilesMatrix(&files); - - mir_free(fileVirusScanned); - if (hIcon) - DestroyIcon(hIcon); - if (hIconFolder) - DestroyIcon(hIconFolder); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include +#include "file.h" + +static int CheckVirusScanned(HWND hwnd, FileDlgData *dat, int i) +{ + if (dat->send) return 1; + if (dat->fileVirusScanned == nullptr) return 0; + if (dat->fileVirusScanned[i]) return 1; + if (g_plugin.getByte("WarnBeforeOpening", 1) == 0) return 1; + + return IDYES == MessageBox(hwnd, + TranslateT("This file has not yet been scanned for viruses. Are you certain you want to open it?"), + TranslateT("File received"), + MB_YESNO | MB_DEFBUTTON2); +} + +#define M_VIRUSSCANDONE (WM_USER+100) +struct virusscanthreadstartinfo +{ + wchar_t *szFile; + int returnCode; + HWND hwndReply; +}; + +wchar_t* PFTS_StringToTchar(int flags, const wchar_t *s) +{ + if (flags & PFTS_UTF) + return mir_utf8decodeW((char*)s); + if (flags & PFTS_UNICODE) + return mir_wstrdup(s); + return mir_a2u((char*)s); +} + +int PFTS_CompareWithTchar(PROTOFILETRANSFERSTATUS *ft, const wchar_t *s, wchar_t *r) +{ + if (ft->flags & PFTS_UTF) { + wchar_t *ts = mir_utf8decodeW((char*)s); + int res = mir_wstrcmp(ts, r); + mir_free(ts); + return res; + } + if (ft->flags & PFTS_UNICODE) + return mir_wstrcmp(s, r); + + wchar_t *ts = mir_a2u((char*)s); + int res = mir_wstrcmp(ts, r); + mir_free(ts); + return res; +} + +static void SetOpenFileButtonStyle(HWND hwndButton, int enabled) +{ + EnableWindow(hwndButton, enabled); +} + +void FillSendData(FileDlgData *dat, DBEVENTINFO &dbei) +{ + dbei.szModule = Proto_GetBaseAccountName(dat->hContact); + dbei.eventType = EVENTTYPE_FILE; + dbei.flags = DBEF_SENT; + dbei.timestamp = time(0); + char *szFileNames = mir_utf8encodeW(dat->szFilenames), *szMsg = mir_utf8encodeW(dat->szMsg); + dbei.flags |= DBEF_UTF; + + dbei.cbBlob = int(sizeof(uint32_t) + mir_strlen(szFileNames) + mir_strlen(szMsg) + 2); + dbei.pBlob = (uint8_t*)mir_alloc(dbei.cbBlob); + *(PDWORD)dbei.pBlob = 0; + mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t), szFileNames); + mir_strcpy((char*)dbei.pBlob + sizeof(uint32_t) + mir_strlen(szFileNames) + 1, szMsg); + + mir_free(szFileNames), mir_free(szMsg); +} + +static void __cdecl RunVirusScannerThread(virusscanthreadstartinfo *info) +{ + DBVARIANT dbv; + if (!g_plugin.getWString("ScanCmdLine", &dbv)) { + if (dbv.pwszVal[0]) { + STARTUPINFO si = { 0 }; + si.cb = sizeof(si); + wchar_t *pszReplace = wcsstr(dbv.pwszVal, L"%f"); + wchar_t szCmdLine[768]; + if (pszReplace) { + if (info->szFile[mir_wstrlen(info->szFile) - 1] == '\\') + info->szFile[mir_wstrlen(info->szFile) - 1] = '\0'; + *pszReplace = 0; + mir_snwprintf(szCmdLine, L"%s\"%s\"%s", dbv.pwszVal, info->szFile, pszReplace + 2); + } + else + wcsncpy_s(szCmdLine, dbv.pwszVal, _TRUNCATE); + + PROCESS_INFORMATION pi; + if (CreateProcess(nullptr, szCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + if (WaitForSingleObject(pi.hProcess, 3600 * 1000) == WAIT_OBJECT_0) + PostMessage(info->hwndReply, M_VIRUSSCANDONE, info->returnCode, 0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + } + db_free(&dbv); + } + mir_free(info->szFile); + mir_free(info); +} + +static void SetFilenameControls(HWND hwndDlg, FileDlgData *dat, PROTOFILETRANSFERSTATUS *fts) +{ + wchar_t msg[MAX_PATH]; + wchar_t *fnbuf = nullptr, *fn = nullptr; + SHFILEINFO shfi = {}; + + if (fts->szCurrentFile.w) { + fnbuf = mir_wstrdup(fts->szCurrentFile.w); + if ((fn = wcsrchr(fnbuf, '\\')) == nullptr) + fn = fnbuf; + else fn++; + } + + if (dat->hIcon) DestroyIcon(dat->hIcon); dat->hIcon = nullptr; + + if (fn && (fts->totalFiles > 1)) { + mir_snwprintf(msg, L"%s: %s (%d %s %d)", Clist_GetContactDisplayName(fts->hContact), fn, fts->currentFileNumber + 1, TranslateT("of"), fts->totalFiles); + + SHGetFileInfo(fn, FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); + dat->hIcon = shfi.hIcon; + } + else if (fn) { + mir_snwprintf(msg, L"%s: %s", Clist_GetContactDisplayName(fts->hContact), fn); + + SHGetFileInfo(fn, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); + dat->hIcon = shfi.hIcon; + } + else { + mir_wstrncpy(msg, Clist_GetContactDisplayName(fts->hContact), _countof(msg)); + HICON hIcon = Skin_LoadIcon(SKINICON_OTHER_DOWNARROW); + dat->hIcon = CopyIcon(hIcon); + IcoLib_ReleaseIcon(hIcon, NULL); + } + + mir_free(fnbuf); + + SendDlgItemMessage(hwndDlg, IDC_FILEICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIcon); + SetDlgItemText(hwndDlg, IDC_CONTACTNAME, msg); +} + +enum { FTS_TEXT, FTS_PROGRESS, FTS_OPEN }; +static void SetFtStatus(HWND hwndDlg, wchar_t *text, int mode) +{ + SetDlgItemText(hwndDlg, IDC_STATUS, TranslateW(text)); + SetDlgItemText(hwndDlg, IDC_TRANSFERCOMPLETED, TranslateW(text)); + + ShowWindow(GetDlgItem(hwndDlg, IDC_STATUS), (mode == FTS_TEXT) ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), (mode == FTS_PROGRESS) ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_TRANSFERCOMPLETED), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_FILEICON), (mode == FTS_OPEN) ? SW_SHOW : SW_HIDE); +} + +static void HideProgressControls(HWND hwndDlg) +{ + RECT rc; + char buf[64]; + + GetWindowRect(GetDlgItem(hwndDlg, IDC_ALLPRECENTS), &rc); + MapWindowPoints(nullptr, hwndDlg, (LPPOINT)&rc, 2); + SetWindowPos(hwndDlg, nullptr, 0, 0, 100, rc.bottom + 3, SWP_NOMOVE | SWP_NOZORDER); + ShowWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), SW_HIDE); + + _strtime(buf); + SetDlgItemTextA(hwndDlg, IDC_ALLPRECENTS, buf); + + PostMessage(GetParent(hwndDlg), WM_FT_RESIZE, 0, (LPARAM)hwndDlg); +} + +static int FileTransferDlgResizer(HWND, LPARAM param, UTILRESIZECONTROL *urc) +{ + auto *dat = (FileDlgData *)param; + + switch (urc->wId) { + case IDC_CONTACTNAME: + case IDC_STATUS: + case IDC_ALLFILESPROGRESS: + case IDC_TRANSFERCOMPLETED: + return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; + + case IDC_FRAME: + return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; + case IDC_ALLPRECENTS: + case IDCANCEL: + case IDC_OPENFILE: + case IDC_OPENFOLDER: + return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; + + case IDC_ALLTRANSFERRED: + if (dat->waitingForAcceptance) + return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; + + urc->rcItem.right = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx) / 3; + return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; + + case IDC_ALLSPEED: + if (dat->waitingForAcceptance) + return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; + + urc->rcItem.right = urc->rcItem.right - urc->dlgOriginalSize.cx + urc->dlgNewSize.cx; + urc->rcItem.left = urc->rcItem.left + (urc->rcItem.right - urc->rcItem.left) / 3; + return RD_ANCHORX_CUSTOM | RD_ANCHORY_TOP; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +INT_PTR CALLBACK DlgProcFileTransfer(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + FileDlgData *dat = (FileDlgData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + dat = (FileDlgData *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + dat->hNotifyEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_RECVEVENT); + dat->transferStatus.currentFileNumber = -1; + if (dat->send) { + if (db_mc_isMeta(dat->hContact)) + dat->hContact = db_mc_getMostOnline(dat->hContact); + dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILE, (WPARAM)dat->szMsg, (LPARAM)dat->files); + SetFtStatus(hwndDlg, LPGENW("Request sent, waiting for acceptance..."), FTS_TEXT); + SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); + dat->waitingForAcceptance = 1; + // hide "open" button since it may cause potential access violations... + ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFILE), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_OPENFOLDER), SW_HIDE); + } + else { //recv + CreateDirectoryTreeW(dat->szSavePath); + dat->fs = (HANDLE)ProtoChainSend(dat->hContact, PSS_FILEALLOW, (WPARAM)dat->fs, (LPARAM)dat->szSavePath); + dat->transferStatus.szWorkingDir.w = mir_wstrdup(dat->szSavePath); + if (!Contact::OnList(dat->hContact)) + dat->resumeBehaviour = FILERESUME_ASK; + else + dat->resumeBehaviour = g_plugin.getByte("IfExists", FILERESUME_ASK); + SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); + } + + /* check we actually got an fs handle back from the protocol */ + if (!dat->fs) { + SetFtStatus(hwndDlg, LPGENW("Unable to initiate transfer."), FTS_TEXT); + dat->waitingForAcceptance = 0; + } + { + LOGFONT lf; + HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); + GetObject(hFont, sizeof(lf), &lf); + lf.lfWeight = FW_BOLD; + hFont = CreateFontIndirect(&lf); + SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_SETFONT, (WPARAM)hFont, 0); + + SHFILEINFO shfi = {}; + SHGetFileInfo(L"", FILE_ATTRIBUTE_DIRECTORY, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON); + dat->hIconFolder = shfi.hIcon; + } + dat->hIcon = nullptr; + { + char *szProto = Proto_GetBaseAccountName(dat->hContact); + uint16_t status = db_get_w(dat->hContact, szProto, "Status", ID_STATUS_ONLINE); + SendDlgItemMessage(hwndDlg, IDC_CONTACT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadProtoIcon(szProto, status)); + } + + SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Contact menu"), 0); + SendDlgItemMessage(hwndDlg, IDC_CONTACT, BUTTONSETASFLATBTN, TRUE, 0); + + Button_SetSkin_IcoLib(hwndDlg, IDC_OPENFILE, SKINICON_OTHER_DOWNARROW, LPGEN("Open...")); + SendDlgItemMessage(hwndDlg, IDC_OPENFILE, BUTTONSETASPUSHBTN, TRUE, 0); + + SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BM_SETIMAGE, IMAGE_ICON, (LPARAM)dat->hIconFolder); + SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open folder"), 0); + SendDlgItemMessage(hwndDlg, IDC_OPENFOLDER, BUTTONSETASFLATBTN, TRUE, 0); + + Button_SetSkin_IcoLib(hwndDlg, IDCANCEL, SKINICON_OTHER_DELETE, LPGEN("Cancel")); + + SetDlgItemText(hwndDlg, IDC_CONTACTNAME, Clist_GetContactDisplayName(dat->hContact)); + + if (!dat->waitingForAcceptance) + SetTimer(hwndDlg, 1, 1000, nullptr); + return TRUE; + + case WM_TIMER: + memmove(dat->bytesRecvedHistory + 1, dat->bytesRecvedHistory, sizeof(dat->bytesRecvedHistory) - sizeof(dat->bytesRecvedHistory[0])); + dat->bytesRecvedHistory[0] = dat->transferStatus.totalProgress; + if (dat->bytesRecvedHistorySize < _countof(dat->bytesRecvedHistory)) + dat->bytesRecvedHistorySize++; + + wchar_t szSpeed[32], szTime[32], szDisplay[96]; + SYSTEMTIME st; + ULARGE_INTEGER li; + FILETIME ft; + + GetSensiblyFormattedSize((dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) / dat->bytesRecvedHistorySize, szSpeed, _countof(szSpeed), 0, 1, NULL); + if (dat->bytesRecvedHistory[0] == dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) + mir_wstrcpy(szTime, L"??:??:??"); + else { + li.QuadPart = 10000000ll * (dat->transferStatus.currentFileSize - dat->transferStatus.currentFileProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); + ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; + FileTimeToSystemTime(&ft, &st); + GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); + } + if (dat->bytesRecvedHistory[0] != dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]) { + li.QuadPart = 10000000ll * (dat->transferStatus.totalBytes - dat->transferStatus.totalProgress) * dat->bytesRecvedHistorySize / (dat->bytesRecvedHistory[0] - dat->bytesRecvedHistory[dat->bytesRecvedHistorySize - 1]); + ft.dwHighDateTime = li.HighPart; ft.dwLowDateTime = li.LowPart; + FileTimeToSystemTime(&ft, &st); + GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, szTime, _countof(szTime)); + } + + mir_snwprintf(szDisplay, L"%s/%s (%s %s)", szSpeed, TranslateT("sec"), szTime, TranslateT("remaining")); + SetDlgItemText(hwndDlg, IDC_ALLSPEED, szDisplay); + break; + + case WM_MEASUREITEM: + return Menu_MeasureItem(lParam); + + case WM_DRAWITEM: + return Menu_DrawItem(lParam); + + case WM_FT_CLEANUP: + if (!dat->fs) { + PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); + DestroyWindow(hwndDlg); + } + break; + + case WM_COMMAND: + if (!dat) + break; + + if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, dat->hContact)) + break; + + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + PostMessage(GetParent(hwndDlg), WM_FT_REMOVE, 0, (LPARAM)hwndDlg); + DestroyWindow(hwndDlg); + break; + + case IDC_CONTACT: + { + RECT rc; + HMENU hMenu = Menu_BuildContactMenu(dat->hContact); + GetWindowRect((HWND)lParam, &rc); + TrackPopupMenu(hMenu, 0, rc.left, rc.bottom, 0, hwndDlg, NULL); + DestroyMenu(hMenu); + } + break; + + case IDC_TRANSFERCOMPLETED: + if (dat->transferStatus.currentFileNumber <= 1 && CheckVirusScanned(hwndDlg, dat, 0)) { + ShellExecute(NULL, NULL, dat->files[0], NULL, NULL, SW_SHOW); + break; + } + + case IDC_OPENFOLDER: + { + wchar_t *path = dat->transferStatus.szWorkingDir.w; + if (!path || !path[0]) { + path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); + wchar_t *p = wcsrchr(path, '\\'); if (p) *p = 0; + } + + if (path) ShellExecute(NULL, L"open", path, NULL, NULL, SW_SHOW); + } + break; + + case IDC_OPENFILE: + wchar_t **files; + if (dat->send) { + if (dat->files == nullptr) + files = dat->transferStatus.pszFiles.w; + else + files = dat->files; + } + else files = dat->files; + + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, 1, TranslateT("Open folder")); + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + + if (files && *files) { + int limit; + wchar_t *pszFilename, *pszNewFileName; + + if (dat->send) + limit = dat->transferStatus.totalFiles; + else + limit = dat->transferStatus.currentFileNumber; + + // Loop over all transfered files and add them to the menu + for (int i = 0; i < limit; i++) { + pszFilename = wcsrchr(files[i], '\\'); + if (pszFilename == nullptr) + pszFilename = files[i]; + else + pszFilename++; + + if (pszFilename) { + size_t cbFileNameLen = mir_wstrlen(pszFilename); + + pszNewFileName = (wchar_t*)mir_alloc(cbFileNameLen * 2 * sizeof(wchar_t)); + wchar_t *p = pszNewFileName; + for (size_t pszlen = 0; pszlen < cbFileNameLen; pszlen++) { + *p++ = pszFilename[pszlen]; + if (pszFilename[pszlen] == '&') + *p++ = '&'; + } + *p = '\0'; + AppendMenu(hMenu, MF_STRING, i + 10, pszNewFileName); + mir_free(pszNewFileName); + } + } + } + + RECT rc; + GetWindowRect((HWND)lParam, &rc); + CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_CHECKED); + int ret = TrackPopupMenu(hMenu, TPM_RETURNCMD | TPM_RIGHTALIGN, rc.right, rc.bottom, 0, hwndDlg, nullptr); + CheckDlgButton(hwndDlg, IDC_OPENFILE, BST_UNCHECKED); + DestroyMenu(hMenu); + + if (ret == 1) { + wchar_t *path = dat->transferStatus.szWorkingDir.w; + if (!path || !path[0]) { + path = NEWWSTR_ALLOCA(dat->transferStatus.szCurrentFile.w); + wchar_t *p = wcsrchr(path, '\\'); + if (p) + *p = 0; + } + + if (path) ShellExecute(nullptr, L"open", path, nullptr, nullptr, SW_SHOW); + } + else if (ret && CheckVirusScanned(hwndDlg, dat, ret)) + ShellExecute(nullptr, nullptr, files[ret - 10], nullptr, nullptr, SW_SHOW); + } + break; + + case M_FILEEXISTSDLGREPLY: + EnableWindow(hwndDlg, TRUE); + { + PROTOFILERESUME *pfr = (PROTOFILERESUME *)lParam; + wchar_t *szOriginalFilename = (wchar_t *)wParam; + char *szProto = Proto_GetBaseAccountName(dat->hContact); + + switch (pfr->action) { + case FILERESUME_CANCEL: + if (dat->fs) ProtoChainSend(dat->hContact, PSS_FILECANCEL, (WPARAM)dat->fs, 0); + dat->fs = nullptr; + mir_free(szOriginalFilename); + if (pfr->szFilename) mir_free((char *)pfr->szFilename); + mir_free(pfr); + return 0; + case FILERESUME_RESUMEALL: + case FILERESUME_OVERWRITEALL: + dat->resumeBehaviour = pfr->action; + pfr->action &= ~FILERESUMEF_ALL; + break; + case FILERESUME_RENAMEALL: + pfr->action = FILERESUME_RENAME; + { + wchar_t *pszExtension, *pszFilename; + if ((pszFilename = wcsrchr(szOriginalFilename, '\\')) == nullptr) pszFilename = szOriginalFilename; + if ((pszExtension = wcsrchr(pszFilename + 1, '.')) == nullptr) pszExtension = pszFilename + mir_wstrlen(pszFilename); + if (pfr->szFilename) mir_free((wchar_t *)pfr->szFilename); + size_t size = (pszExtension - szOriginalFilename) + 21 + mir_wstrlen(pszExtension); + pfr->szFilename = (wchar_t *)mir_alloc(sizeof(wchar_t) * size); + for (int i = 1;; i++) { + mir_snwprintf((wchar_t *)pfr->szFilename, size, L"%.*s (%u)%s", pszExtension - szOriginalFilename, szOriginalFilename, i, pszExtension); + if (_waccess(pfr->szFilename, 0) != 0) + break; + } + } + break; + } + mir_free(szOriginalFilename); + CallProtoService(szProto, PS_FILERESUME, (WPARAM)dat->fs, (LPARAM)pfr); + delete pfr; + } + break; + + case HM_RECVEVENT: + { + ACKDATA *ack = (ACKDATA *)lParam; + if (ack->hProcess != dat->fs) break; + if (ack->type != ACKTYPE_FILE) break; + if (ack->hContact != dat->hContact) break; + + if (dat->waitingForAcceptance) { + SetTimer(hwndDlg, 1, 1000, nullptr); + dat->waitingForAcceptance = 0; + } + + switch (ack->result) { + case ACKRESULT_SENTREQUEST: SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break; + case ACKRESULT_CONNECTING: SetFtStatus(hwndDlg, LPGENW("Connecting..."), FTS_TEXT); break; + case ACKRESULT_CONNECTPROXY: SetFtStatus(hwndDlg, LPGENW("Connecting to proxy..."), FTS_TEXT); break; + case ACKRESULT_CONNECTED: SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break; + case ACKRESULT_LISTENING: SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break; + case ACKRESULT_INITIALISING: SetFtStatus(hwndDlg, LPGENW("Initializing..."), FTS_TEXT); break; + case ACKRESULT_NEXTFILE: + SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT); + SetDlgItemTextA(hwndDlg, IDC_FILENAME, ""); + if (dat->transferStatus.currentFileNumber == 1 && dat->transferStatus.totalFiles > 1 && !dat->send) + SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); + if (dat->transferStatus.currentFileNumber != -1 && dat->files && !dat->send && g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE) == VIRUSSCAN_DURINGDL) { + if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) + PostMessage(hwndDlg, M_VIRUSSCANDONE, dat->transferStatus.currentFileNumber, 0); + else { + virusscanthreadstartinfo *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); + vstsi->hwndReply = hwndDlg; + vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); + vstsi->returnCode = dat->transferStatus.currentFileNumber; + mir_forkThread(RunVirusScannerThread, vstsi); + } + } + break; + + case ACKRESULT_FILERESUME: + UpdateProtoFileTransferStatus(&dat->transferStatus, (PROTOFILETRANSFERSTATUS *)ack->lParam); + { + PROTOFILETRANSFERSTATUS *fts = &dat->transferStatus; + SetFilenameControls(hwndDlg, dat, fts); + if (_waccess(fts->szCurrentFile.w, 0)) + break; + + SetFtStatus(hwndDlg, LPGENW("File already exists"), FTS_TEXT); + if (dat->resumeBehaviour == FILERESUME_ASK) { + TDlgProcFileExistsParam param = { hwndDlg, fts }; + ShowWindow(hwndDlg, SW_SHOWNORMAL); + CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILEEXISTS), hwndDlg, DlgProcFileExists, (LPARAM)¶m); + EnableWindow(hwndDlg, FALSE); + } + else { + PROTOFILERESUME *pfr = new PROTOFILERESUME(); + pfr->action = dat->resumeBehaviour; + pfr->szFilename = nullptr; + PostMessage(hwndDlg, M_FILEEXISTSDLGREPLY, (WPARAM)mir_wstrdup(fts->szCurrentFile.w), (LPARAM)pfr); + } + } + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 1); + return TRUE; + + case ACKRESULT_DATA: + { + PROTOFILETRANSFERSTATUS *fts = (PROTOFILETRANSFERSTATUS *)ack->lParam; + wchar_t str[64], str2[64], szSizeDone[32], szSizeTotal[32];//, *contactName; + + if (dat->fileVirusScanned == nullptr) + dat->fileVirusScanned = (int *)mir_calloc(sizeof(int) * fts->totalFiles); + + // This needs to be here - otherwise we get holes in the files array + if (!dat->send) { + if (dat->files == nullptr) + dat->files = (wchar_t **)mir_calloc((fts->totalFiles + 1) * sizeof(wchar_t *)); + if (fts->currentFileNumber < fts->totalFiles && dat->files[fts->currentFileNumber] == nullptr) + dat->files[fts->currentFileNumber] = PFTS_StringToTchar(fts->flags, fts->szCurrentFile.w); + } + + /* HACK: for 0.3.3, limit updates to around 1.1 ack per second */ + if (fts->totalProgress != fts->totalBytes && GetTickCount() < (dat->dwTicks + 650)) + break; // the last update was less than a second ago! + dat->dwTicks = GetTickCount(); + + // Update local transfer status with data from protocol + UpdateProtoFileTransferStatus(&dat->transferStatus, fts); + fts = &dat->transferStatus; + + bool firstTime = false; + if ((GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_ALLFILESPROGRESS), GWL_STYLE) & WS_VISIBLE) == 0) { + SetFtStatus(hwndDlg, (fts->flags & PFTS_SENDING) ? LPGENW("Sending...") : LPGENW("Receiving..."), FTS_PROGRESS); + SetFilenameControls(hwndDlg, dat, fts); + firstTime = true; + } + + const unsigned long lastPos = SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_GETPOS, 0, 0); + const unsigned long nextPos = fts->totalBytes ? (100ll * fts->totalProgress / fts->totalBytes) : 0; + if (lastPos != nextPos || firstTime) { + SendDlgItemMessage(hwndDlg, IDC_ALLFILESPROGRESS, PBM_SETPOS, nextPos, 0); + mir_snwprintf(str, L"%u%%", nextPos); + SetDlgItemText(hwndDlg, IDC_ALLPRECENTS, str); + } + + int units; + GetSensiblyFormattedSize(fts->totalBytes, szSizeTotal, _countof(szSizeTotal), 0, 1, &units); + GetSensiblyFormattedSize(fts->totalProgress, szSizeDone, _countof(szSizeDone), units, 0, NULL); + mir_snwprintf(str, L"%s/%s", szSizeDone, szSizeTotal); + str2[0] = 0; + GetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str2, _countof(str2)); + if (mir_wstrcmp(str, str2)) + SetDlgItemText(hwndDlg, IDC_ALLTRANSFERRED, str); + } + break; + + case ACKRESULT_SUCCESS: + case ACKRESULT_FAILED: + case ACKRESULT_DENIED: + HideProgressControls(hwndDlg); + KillTimer(hwndDlg, 1); + if (!dat->send) + SetOpenFileButtonStyle(GetDlgItem(hwndDlg, IDC_OPENFILE), 1); + SetDlgItemText(hwndDlg, IDCANCEL, TranslateT("Close")); + if (dat->hNotifyEvent) + UnhookEvent(dat->hNotifyEvent); + dat->hNotifyEvent = nullptr; + + if (ack->result == ACKRESULT_DENIED) { + dat->fs = nullptr; /* protocol will free structure */ + Skin_PlaySound("FileDenied"); + SetFtStatus(hwndDlg, LPGENW("File transfer denied"), FTS_TEXT); + } + else if (ack->result == ACKRESULT_FAILED) { + dat->fs = nullptr; /* protocol will free structure */ + Skin_PlaySound("FileFailed"); + SetFtStatus(hwndDlg, LPGENW("File transfer failed"), FTS_TEXT); + } + else { + Skin_PlaySound("FileDone"); + if (dat->send) { + dat->fs = nullptr; /* protocol will free structure */ + SetFtStatus(hwndDlg, LPGENW("Transfer completed."), FTS_TEXT); + + DBEVENTINFO dbei = {}; + FillSendData(dat, dbei); + db_event_add(dat->hContact, &dbei); + if (dbei.pBlob) + mir_free(dbei.pBlob); + dat->files = nullptr; //protocol library frees this + } + else { + SetFtStatus(hwndDlg, + (dat->transferStatus.totalFiles == 1) ? + LPGENW("Transfer completed, open file.") : + LPGENW("Transfer completed, open folder."), + FTS_OPEN); + + int useScanner = g_plugin.getByte("UseScanner", VIRUSSCAN_DISABLE); + if (useScanner != VIRUSSCAN_DISABLE) { + auto *vstsi = (virusscanthreadstartinfo *)mir_alloc(sizeof(virusscanthreadstartinfo)); + vstsi->hwndReply = hwndDlg; + if (useScanner == VIRUSSCAN_DURINGDL) { + vstsi->returnCode = dat->transferStatus.currentFileNumber; + if (GetFileAttributes(dat->files[dat->transferStatus.currentFileNumber]) & FILE_ATTRIBUTE_DIRECTORY) { + PostMessage(hwndDlg, M_VIRUSSCANDONE, vstsi->returnCode, 0); + mir_free(vstsi); + vstsi = nullptr; + } + else vstsi->szFile = mir_wstrdup(dat->files[dat->transferStatus.currentFileNumber]); + } + else { + vstsi->szFile = mir_wstrdup(dat->transferStatus.szWorkingDir.w); + vstsi->returnCode = -1; + } + SetFtStatus(hwndDlg, LPGENW("Scanning for viruses..."), FTS_TEXT); + if (vstsi) + mir_forkThread(RunVirusScannerThread, vstsi); + } + else dat->fs = nullptr; /* protocol will free structure */ + + dat->transferStatus.currentFileNumber = dat->transferStatus.totalFiles; + } + } + + PostMessage(GetParent(hwndDlg), WM_FT_COMPLETED, ack->result, (LPARAM)hwndDlg); + break; + } + } + break; + + case M_VIRUSSCANDONE: + { + int done = 1; + if ((int)wParam == -1) { + for (int i = 0; i < dat->transferStatus.totalFiles; i++) + dat->fileVirusScanned[i] = 1; + } + else { + dat->fileVirusScanned[wParam] = 1; + for (int i = 0; i < dat->transferStatus.totalFiles; i++) + if (!dat->fileVirusScanned[i]) { + done = 0; + break; + } + } + if (done) { + dat->fs = nullptr; /* protocol will free structure */ + SetFtStatus(hwndDlg, LPGENW("Transfer and virus scan complete"), FTS_TEXT); + } + } + break; + + case WM_SIZE: + Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FILETRANSFERINFO), FileTransferDlgResizer, LPARAM(dat)); + + RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLTRANSFERRED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); + RedrawWindow(GetDlgItem(hwndDlg, IDC_ALLSPEED), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); + RedrawWindow(GetDlgItem(hwndDlg, IDC_CONTACTNAME), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); + RedrawWindow(GetDlgItem(hwndDlg, IDC_STATUS), NULL, NULL, RDW_INVALIDATE | RDW_NOERASE); + break; + + case WM_DESTROY: + KillTimer(hwndDlg, 1); + + HFONT hFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_CONTACTNAME, WM_GETFONT, 0, 0); + DeleteObject(hFont); + + Button_FreeIcon_IcoLib(hwndDlg, IDC_CONTACT); + Button_FreeIcon_IcoLib(hwndDlg, IDC_OPENFILE); + Button_FreeIcon_IcoLib(hwndDlg, IDCANCEL); + + delete dat; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); + break; + } + return FALSE; +} + +FileDlgData::~FileDlgData() +{ + if (fs) + ProtoChainSend(hContact, PSS_FILECANCEL, (WPARAM)fs, 0); + if (hPreshutdownEvent) + UnhookEvent(hPreshutdownEvent); + if (hNotifyEvent) + UnhookEvent(hNotifyEvent); + + FreeProtoFileTransferStatus(&transferStatus); + FreeFilesMatrix(&files); + + mir_free(fileVirusScanned); + if (hIcon) + DestroyIcon(hIcon); + if (hIconFolder) + DestroyIcon(hIconFolder); +} diff --git a/src/core/stdfile/src/ftmanager.cpp b/src/core/stdfile/src/ftmanager.cpp index 20206908be..56b0a168a7 100644 --- a/src/core/stdfile/src/ftmanager.cpp +++ b/src/core/stdfile/src/ftmanager.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/main.cpp b/src/core/stdfile/src/main.cpp index 275c58cf44..3797ea1e49 100644 --- a/src/core/stdfile/src/main.cpp +++ b/src/core/stdfile/src/main.cpp @@ -2,7 +2,7 @@ Standard file transfers' plugin for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stdfile/src/stdafx.h b/src/core/stdfile/src/stdafx.h index b732c1278c..775d957d38 100644 --- a/src/core/stdfile/src/stdafx.h +++ b/src/core/stdfile/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdfile/src/version.h b/src/core/stdfile/src/version.h index 777678748d..2099819477 100644 --- a/src/core/stdfile/src/version.h +++ b/src/core/stdfile/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for sending/receiving files." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdFile" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdmsg/src/chat_manager.cpp b/src/core/stdmsg/src/chat_manager.cpp index b72f53acd7..b461b91703 100644 --- a/src/core/stdmsg/src/chat_manager.cpp +++ b/src/core/stdmsg/src/chat_manager.cpp @@ -1,243 +1,243 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -pfnDoTrayIcon oldDoTrayIcon; -pfnDoPopup oldDoPopup; - -SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si) -{ - int i = g_chatApi.arSessions.indexOf(si); - if (i == -1) - return nullptr; - - for (i--; i >= 0; i--) { - SESSION_INFO *p = g_chatApi.arSessions[i]; - if (p->pDlg) - return p; - } - - return nullptr; -} - -SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si) -{ - int i = g_chatApi.arSessions.indexOf(si); - if (i == -1) - return nullptr; - - for (i++; i < g_chatApi.arSessions.getCount(); i++) { - SESSION_INFO *p = g_chatApi.arSessions[i]; - if (p->pDlg) - return p; - } - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HMENU g_hMenu = nullptr; - -GlobalLogSettings g_Settings; - -static MODULEINFO* MM_CreateModule() -{ - return new MODULEINFO(); -} - -static void OnReplaceSession(SESSION_INFO *si) -{ - if (si->pDlg) - RedrawWindow(GetDlgItem(si->pDlg->GetHwnd(), IDC_SRMM_NICKLIST), nullptr, nullptr, RDW_INVALIDATE); -} - -static void OnFlashHighlight(SESSION_INFO *si, int bInactive) -{ - if (!bInactive || si->pDlg == nullptr) - return; - - if (g_Settings.bTabsEnable) - g_pTabDialog->SetMessageHighlight(si->pDlg); - else if (Chat::bFlashWindowHighlight) - si->pDlg->StartFlash(); -} - -static void OnFlashWindow(SESSION_INFO *si, int bInactive) -{ - if (!bInactive || si->pDlg == nullptr) - return; - - if (g_Settings.bTabsEnable) - g_pTabDialog->SetTabHighlight(si->pDlg); - else if (Chat::bFlashWindow) - si->pDlg->StartFlash(); -} - -static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce) -{ - if (gce->iType & g_Settings.dwTrayIconFlags) - return oldDoTrayIcon(si, gce); - return TRUE; -} - -static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce) -{ - if (gce->iType & g_Settings.dwPopupFlags) - return oldDoPopup(si, gce); - return TRUE; -} - -static void OnLoadSettings() -{ - g_Settings.iX = db_get_dw(0, CHAT_MODULE, "roomx", -1); - g_Settings.iY = db_get_dw(0, CHAT_MODULE, "roomy", -1); - - g_Settings.bTabsEnable = db_get_b(0, CHAT_MODULE, "Tabs", 1) != 0; - g_Settings.bTabsAtBottom = db_get_b(0, CHAT_MODULE, "TabBottom", 0) != 0; - g_Settings.bTabCloseOnDblClick = db_get_b(0, CHAT_MODULE, "TabCloseOnDblClick", 0) != 0; - g_Settings.bAddColonToAutoComplete = db_get_b(0, CHAT_MODULE, "AddColonToAutoComplete", 1) != 0; - - g_Settings.iSplitterX = db_get_w(0, CHAT_MODULE, "SplitterX", 105); - if (g_Settings.iSplitterX <= 50) - g_Settings.iSplitterX = 105; - g_Settings.iSplitterY = db_get_w(0, CHAT_MODULE, "SplitterY", 90); - if (g_Settings.iSplitterY <= 65) - g_Settings.iSplitterY = 90; -} - -static void RegisterFonts() -{ - ColourIDW colourid = {}; - strncpy(colourid.dbSettingsGroup, CHAT_MODULE, sizeof(colourid.dbSettingsGroup)); - wcsncpy(colourid.group, LPGENW("Message sessions") L"/" LPGENW("Chat module"), _countof(colourid.group)); - - strncpy(colourid.setting, "ColorLogBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Group chat log background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorMessageBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Message background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list background"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_WINDOW); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistLines", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list lines"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER); - g_plugin.addColor(&colourid); - - strncpy(colourid.setting, "ColorNicklistSelectedBG", _countof(colourid.setting)); - wcsncpy(colourid.name, LPGENW("Nick list background (selected)"), _countof(colourid.name)); - colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT); - g_plugin.addColor(&colourid); -} - -static void ShowRoom(SESSION_INFO *si) -{ - if (!si) - return; - - // Do we need to create a window? - if (si->pDlg == nullptr) { - CTabbedWindow *pContainer = GetContainer(); - if (g_Settings.bTabsEnable) { - pContainer->AddPage(si); - PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); - } - else { - CMsgDialog *pDlg = pContainer->m_pEmbed = new CMsgDialog(pContainer, si); - pContainer->Create(); - pDlg->SetParent(pContainer->GetHwnd()); - pDlg->Create(); - pContainer->Show(); - pContainer->FixTabIcons(pDlg); - PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); - } - - if (si->iType != GCW_SERVER) - si->pDlg->UpdateNickList(); - else - si->pDlg->UpdateTitle(); - si->pDlg->UpdateStatusBar(); - } - else if (g_Settings.bTabsEnable && g_pTabDialog) - g_pTabDialog->m_tab.ActivatePage(g_pTabDialog->m_tab.GetDlgIndex(si->pDlg)); - - SetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE) | WS_EX_APPWINDOW); - - if (IsIconic(si->pDlg->GetHwnd())) - si->pDlg->Show(SW_NORMAL); - si->pDlg->Show(SW_SHOW); - SetForegroundWindow(si->pDlg->GetHwnd()); -} - -int OnCheckPlugins(WPARAM, LPARAM) -{ - g_plugin.bSmileyInstalled = ServiceExists(MS_SMILEYADD_REPLACESMILEYS); - return 0; -} - -void Load_ChatModule() -{ - AddIcons(); - RegisterFonts(); - - CHAT_MANAGER_INITDATA data = { &g_Settings, sizeof(MODULEINFO), sizeof(SESSION_INFO), LPGENW("Message sessions") L"/" LPGENW("Chat module"), FONTMODE_USE, &g_plugin }; - Chat_CustomizeApi(&data); - - g_chatApi.MM_CreateModule = MM_CreateModule; - g_chatApi.OnReplaceSession = OnReplaceSession; - - g_chatApi.OnLoadSettings = OnLoadSettings; - g_chatApi.OnFlashWindow = OnFlashWindow; - g_chatApi.OnFlashHighlight = OnFlashHighlight; - g_chatApi.ShowRoom = ShowRoom; - - Srmm_CreateHotkey(LPGEN("Messaging"), LPGEN("Send message")); - - oldDoPopup = g_chatApi.DoPopup; g_chatApi.DoPopup = DoPopup; - oldDoTrayIcon = g_chatApi.DoTrayIcon; g_chatApi.DoTrayIcon = DoTrayIcon; - g_chatApi.ReloadSettings(); - - g_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENU)); - - HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins); -} - -void Unload_ChatModule() -{ - db_set_w(0, CHAT_MODULE, "SplitterX", (uint16_t)g_Settings.iSplitterX); - db_set_w(0, CHAT_MODULE, "SplitterY", (uint16_t)g_Settings.iSplitterY); - db_set_dw(0, CHAT_MODULE, "roomx", g_Settings.iX); - db_set_dw(0, CHAT_MODULE, "roomy", g_Settings.iY); - db_set_dw(0, CHAT_MODULE, "roomwidth", g_Settings.iWidth); - db_set_dw(0, CHAT_MODULE, "roomheight", g_Settings.iHeight); - - DestroyMenu(g_hMenu); -} +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +pfnDoTrayIcon oldDoTrayIcon; +pfnDoPopup oldDoPopup; + +SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si) +{ + int i = g_chatApi.arSessions.indexOf(si); + if (i == -1) + return nullptr; + + for (i--; i >= 0; i--) { + SESSION_INFO *p = g_chatApi.arSessions[i]; + if (p->pDlg) + return p; + } + + return nullptr; +} + +SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si) +{ + int i = g_chatApi.arSessions.indexOf(si); + if (i == -1) + return nullptr; + + for (i++; i < g_chatApi.arSessions.getCount(); i++) { + SESSION_INFO *p = g_chatApi.arSessions[i]; + if (p->pDlg) + return p; + } + + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +HMENU g_hMenu = nullptr; + +GlobalLogSettings g_Settings; + +static MODULEINFO* MM_CreateModule() +{ + return new MODULEINFO(); +} + +static void OnReplaceSession(SESSION_INFO *si) +{ + if (si->pDlg) + RedrawWindow(GetDlgItem(si->pDlg->GetHwnd(), IDC_SRMM_NICKLIST), nullptr, nullptr, RDW_INVALIDATE); +} + +static void OnFlashHighlight(SESSION_INFO *si, int bInactive) +{ + if (!bInactive || si->pDlg == nullptr) + return; + + if (g_Settings.bTabsEnable) + g_pTabDialog->SetMessageHighlight(si->pDlg); + else if (Chat::bFlashWindowHighlight) + si->pDlg->StartFlash(); +} + +static void OnFlashWindow(SESSION_INFO *si, int bInactive) +{ + if (!bInactive || si->pDlg == nullptr) + return; + + if (g_Settings.bTabsEnable) + g_pTabDialog->SetTabHighlight(si->pDlg); + else if (Chat::bFlashWindow) + si->pDlg->StartFlash(); +} + +static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce) +{ + if (gce->iType & g_Settings.dwTrayIconFlags) + return oldDoTrayIcon(si, gce); + return TRUE; +} + +static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce) +{ + if (gce->iType & g_Settings.dwPopupFlags) + return oldDoPopup(si, gce); + return TRUE; +} + +static void OnLoadSettings() +{ + g_Settings.iX = db_get_dw(0, CHAT_MODULE, "roomx", -1); + g_Settings.iY = db_get_dw(0, CHAT_MODULE, "roomy", -1); + + g_Settings.bTabsEnable = db_get_b(0, CHAT_MODULE, "Tabs", 1) != 0; + g_Settings.bTabsAtBottom = db_get_b(0, CHAT_MODULE, "TabBottom", 0) != 0; + g_Settings.bTabCloseOnDblClick = db_get_b(0, CHAT_MODULE, "TabCloseOnDblClick", 0) != 0; + g_Settings.bAddColonToAutoComplete = db_get_b(0, CHAT_MODULE, "AddColonToAutoComplete", 1) != 0; + + g_Settings.iSplitterX = db_get_w(0, CHAT_MODULE, "SplitterX", 105); + if (g_Settings.iSplitterX <= 50) + g_Settings.iSplitterX = 105; + g_Settings.iSplitterY = db_get_w(0, CHAT_MODULE, "SplitterY", 90); + if (g_Settings.iSplitterY <= 65) + g_Settings.iSplitterY = 90; +} + +static void RegisterFonts() +{ + ColourIDW colourid = {}; + strncpy(colourid.dbSettingsGroup, CHAT_MODULE, sizeof(colourid.dbSettingsGroup)); + wcsncpy(colourid.group, LPGENW("Message sessions") L"/" LPGENW("Chat module"), _countof(colourid.group)); + + strncpy(colourid.setting, "ColorLogBG", _countof(colourid.setting)); + wcsncpy(colourid.name, LPGENW("Group chat log background"), _countof(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_WINDOW); + g_plugin.addColor(&colourid); + + strncpy(colourid.setting, "ColorMessageBG", _countof(colourid.setting)); + wcsncpy(colourid.name, LPGENW("Message background"), _countof(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_WINDOW); + g_plugin.addColor(&colourid); + + strncpy(colourid.setting, "ColorNicklistBG", _countof(colourid.setting)); + wcsncpy(colourid.name, LPGENW("Nick list background"), _countof(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_WINDOW); + g_plugin.addColor(&colourid); + + strncpy(colourid.setting, "ColorNicklistLines", _countof(colourid.setting)); + wcsncpy(colourid.name, LPGENW("Nick list lines"), _countof(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER); + g_plugin.addColor(&colourid); + + strncpy(colourid.setting, "ColorNicklistSelectedBG", _countof(colourid.setting)); + wcsncpy(colourid.name, LPGENW("Nick list background (selected)"), _countof(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT); + g_plugin.addColor(&colourid); +} + +static void ShowRoom(SESSION_INFO *si) +{ + if (!si) + return; + + // Do we need to create a window? + if (si->pDlg == nullptr) { + CTabbedWindow *pContainer = GetContainer(); + if (g_Settings.bTabsEnable) { + pContainer->AddPage(si); + PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); + } + else { + CMsgDialog *pDlg = pContainer->m_pEmbed = new CMsgDialog(pContainer, si); + pContainer->Create(); + pDlg->SetParent(pContainer->GetHwnd()); + pDlg->Create(); + pContainer->Show(); + pContainer->FixTabIcons(pDlg); + PostMessage(pContainer->GetHwnd(), WM_SIZE, 0, 0); + } + + if (si->iType != GCW_SERVER) + si->pDlg->UpdateNickList(); + else + si->pDlg->UpdateTitle(); + si->pDlg->UpdateStatusBar(); + } + else if (g_Settings.bTabsEnable && g_pTabDialog) + g_pTabDialog->m_tab.ActivatePage(g_pTabDialog->m_tab.GetDlgIndex(si->pDlg)); + + SetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(si->pDlg->GetHwnd(), GWL_EXSTYLE) | WS_EX_APPWINDOW); + + if (IsIconic(si->pDlg->GetHwnd())) + si->pDlg->Show(SW_NORMAL); + si->pDlg->Show(SW_SHOW); + SetForegroundWindow(si->pDlg->GetHwnd()); +} + +int OnCheckPlugins(WPARAM, LPARAM) +{ + g_plugin.bSmileyInstalled = ServiceExists(MS_SMILEYADD_REPLACESMILEYS); + return 0; +} + +void Load_ChatModule() +{ + AddIcons(); + RegisterFonts(); + + CHAT_MANAGER_INITDATA data = { &g_Settings, sizeof(MODULEINFO), sizeof(SESSION_INFO), LPGENW("Message sessions") L"/" LPGENW("Chat module"), FONTMODE_USE, &g_plugin }; + Chat_CustomizeApi(&data); + + g_chatApi.MM_CreateModule = MM_CreateModule; + g_chatApi.OnReplaceSession = OnReplaceSession; + + g_chatApi.OnLoadSettings = OnLoadSettings; + g_chatApi.OnFlashWindow = OnFlashWindow; + g_chatApi.OnFlashHighlight = OnFlashHighlight; + g_chatApi.ShowRoom = ShowRoom; + + Srmm_CreateHotkey(LPGEN("Messaging"), LPGEN("Send message")); + + oldDoPopup = g_chatApi.DoPopup; g_chatApi.DoPopup = DoPopup; + oldDoTrayIcon = g_chatApi.DoTrayIcon; g_chatApi.DoTrayIcon = DoTrayIcon; + g_chatApi.ReloadSettings(); + + g_hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_MENU)); + + HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins); +} + +void Unload_ChatModule() +{ + db_set_w(0, CHAT_MODULE, "SplitterX", (uint16_t)g_Settings.iSplitterX); + db_set_w(0, CHAT_MODULE, "SplitterY", (uint16_t)g_Settings.iSplitterY); + db_set_dw(0, CHAT_MODULE, "roomx", g_Settings.iX); + db_set_dw(0, CHAT_MODULE, "roomy", g_Settings.iY); + db_set_dw(0, CHAT_MODULE, "roomwidth", g_Settings.iWidth); + db_set_dw(0, CHAT_MODULE, "roomheight", g_Settings.iHeight); + + DestroyMenu(g_hMenu); +} diff --git a/src/core/stdmsg/src/chat_window.cpp b/src/core/stdmsg/src/chat_window.cpp index 52106ae755..8e1871dd4c 100644 --- a/src/core/stdmsg/src/chat_window.cpp +++ b/src/core/stdmsg/src/chat_window.cpp @@ -1,275 +1,275 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::LoadSettings() -{ - m_clrInputBG = db_get_dw(0, CHAT_MODULE, "ColorMessageBG", GetSysColor(COLOR_WINDOW)); - LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMsgDialog::ShowFilterMenu() -{ - HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_hwnd, FilterWndProc, (LPARAM)this); - TranslateDialogDefault(hwnd); - - RECT rc; - GetWindowRect(m_btnFilter.GetHwnd(), &rc); - SetWindowPos(hwnd, HWND_TOP, rc.left - 85, (IsWindowVisible(m_btnFilter.GetHwnd()) || IsWindowVisible(m_btnBold.GetHwnd())) ? rc.top - 206 : rc.top - 186, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); -} - -void CMsgDialog::UpdateNickList() -{ - int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0); - m_nickList.SendMsg(LB_SETTOPINDEX, i, 0); - - UpdateTitle(); -} - -void CMsgDialog::UpdateOptions() -{ - m_btnNickList.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bNicklistEnabled ? IDI_NICKLIST2 : IDI_NICKLIST, FALSE)); - m_btnFilter.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bFilterEnabled ? IDI_FILTER2 : IDI_FILTER, FALSE)); - - HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); - SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); - DestroyIcon(hIcon); - - Window_SetIcon_IcoLib(m_pOwner->GetHwnd(), g_plugin.getIconHandle(IDI_CHANMGR)); - - m_pLog->UpdateOptions(); - - // nicklist - int ih = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListFont, FALSE); - int ih2 = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListHeadingsFont, FALSE); - int height = db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12); - int font = ih > ih2 ? ih : ih2; - - // make sure we have space for icon! - if (g_Settings.bShowContactStatus) - font = font > 16 ? font : 16; - - m_nickList.SendMsg(LB_SETITEMHEIGHT, 0, height > font ? height : font); - InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE); - - CSuper::UpdateOptions(); -} - -void CMsgDialog::UpdateStatusBar() -{ - wchar_t *ptszDispName = m_si->pMI->ptszModDispName; - int x = 12; - x += Chat_GetTextPixelSize(ptszDispName, (HFONT)SendMessage(m_pOwner->m_hwndStatus, WM_GETFONT, 0, 0), TRUE); - x += GetSystemMetrics(SM_CXSMICON); - int iStatusbarParts[2] = { x, -1 }; - SendMessage(m_pOwner->m_hwndStatus, SB_SETPARTS, 2, (LPARAM)&iStatusbarParts); - - HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); - SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); - DestroyIcon(hIcon); - - SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)ptszDispName); - SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); - SendMessage(m_pOwner->m_hwndStatus, SB_SETTIPTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) -{ - auto *si = m_pDlg.m_si; - if (lin == nullptr || si == nullptr) - return; - - if (!bRedraw && si->iType == GCW_CHATROOM && m_pDlg.m_bFilterEnabled && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0) - return; - - LOGSTREAMDATA streamData; - memset(&streamData, 0, sizeof(streamData)); - streamData.hwnd = m_rtf.GetHwnd(); - streamData.si = si; - streamData.lin = lin; - streamData.bStripFormat = FALSE; - - bool bFlag = false; - - EDITSTREAM stream = {}; - stream.pfnCallback = Srmm_LogStreamCallback; - stream.dwCookie = (DWORD_PTR)& streamData; - - SCROLLINFO scroll; - scroll.cbSize = sizeof(SCROLLINFO); - scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll); - - POINT point = {}; - m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point); - - // do not scroll to bottom if there is a selection - CHARRANGE oldsel, sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel); - if (oldsel.cpMax != oldsel.cpMin) - m_rtf.SetDraw(false); - - //set the insertion point at the bottom - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - - // fix for the indent... must be a M$ bug - if (sel.cpMax == 0) - bRedraw = TRUE; - - // should the event(s) be appended to the current log - WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF; - - //get the number of pixels per logical inch - if (bRedraw) { - HDC hdc = GetDC(nullptr); - g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); - g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); - ReleaseDC(nullptr, hdc); - m_rtf.SetDraw(false); - bFlag = true; - } - - // stream in the event(s) - streamData.lin = lin; - streamData.bRedraw = bRedraw; - m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream); - - // do smileys - if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) { - CHARRANGE newsel; - newsel.cpMax = -1; - newsel.cpMin = sel.cpMin; - if (newsel.cpMin < 0) - newsel.cpMin = 0; - - SMADD_RICHEDIT3 sm = {}; - sm.cbSize = sizeof(sm); - sm.hwndRichEditControl = m_rtf.GetHwnd(); - sm.Protocolname = si->pszModule; - sm.rangeToReplace = bRedraw ? nullptr : &newsel; - sm.disableRedraw = TRUE; - sm.hContact = si->hContact; - CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm); - } - - // scroll log to bottom if the log was previously scrolled to bottom, else restore old position - if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50) - ScrollToBottom(); - else - m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point); - - // do we need to restore the selection - if (oldsel.cpMax != oldsel.cpMin) { - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } - - // need to invalidate the window - if (bFlag) { - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - static CMsgDialog *pDlg = nullptr; - switch (uMsg) { - case WM_INITDIALOG: - pDlg = (CMsgDialog*)lParam; - CheckDlgButton(hwndDlg, IDC_1, pDlg->m_iLogFilterFlags & GC_EVENT_ACTION ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_2, pDlg->m_iLogFilterFlags & GC_EVENT_MESSAGE ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_3, pDlg->m_iLogFilterFlags & GC_EVENT_NICK ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_4, pDlg->m_iLogFilterFlags & GC_EVENT_JOIN ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_5, pDlg->m_iLogFilterFlags & GC_EVENT_PART ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_6, pDlg->m_iLogFilterFlags & GC_EVENT_TOPIC ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_7, pDlg->m_iLogFilterFlags & GC_EVENT_ADDSTATUS ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_8, pDlg->m_iLogFilterFlags & GC_EVENT_INFORMATION ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_9, pDlg->m_iLogFilterFlags & GC_EVENT_QUIT ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_10, pDlg->m_iLogFilterFlags & GC_EVENT_KICK ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(hwndDlg, IDC_11, pDlg->m_iLogFilterFlags & GC_EVENT_NOTICE ? BST_CHECKED : BST_UNCHECKED); - break; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - SetTextColor((HDC)wParam, RGB(60, 60, 150)); - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) { - int iFlags = 0; - - if (IsDlgButtonChecked(hwndDlg, IDC_1) == BST_CHECKED) - iFlags |= GC_EVENT_ACTION; - if (IsDlgButtonChecked(hwndDlg, IDC_2) == BST_CHECKED) - iFlags |= GC_EVENT_MESSAGE; - if (IsDlgButtonChecked(hwndDlg, IDC_3) == BST_CHECKED) - iFlags |= GC_EVENT_NICK; - if (IsDlgButtonChecked(hwndDlg, IDC_4) == BST_CHECKED) - iFlags |= GC_EVENT_JOIN; - if (IsDlgButtonChecked(hwndDlg, IDC_5) == BST_CHECKED) - iFlags |= GC_EVENT_PART; - if (IsDlgButtonChecked(hwndDlg, IDC_6) == BST_CHECKED) - iFlags |= GC_EVENT_TOPIC; - if (IsDlgButtonChecked(hwndDlg, IDC_7) == BST_CHECKED) - iFlags |= GC_EVENT_ADDSTATUS; - if (IsDlgButtonChecked(hwndDlg, IDC_8) == BST_CHECKED) - iFlags |= GC_EVENT_INFORMATION; - if (IsDlgButtonChecked(hwndDlg, IDC_9) == BST_CHECKED) - iFlags |= GC_EVENT_QUIT; - if (IsDlgButtonChecked(hwndDlg, IDC_10) == BST_CHECKED) - iFlags |= GC_EVENT_KICK; - if (IsDlgButtonChecked(hwndDlg, IDC_11) == BST_CHECKED) - iFlags |= GC_EVENT_NOTICE; - - if (iFlags & GC_EVENT_ADDSTATUS) - iFlags |= GC_EVENT_REMOVESTATUS; - - pDlg->m_iLogFilterFlags = iFlags; - if (pDlg->m_bFilterEnabled) - pDlg->RedrawLog(); - PostMessage(hwndDlg, WM_CLOSE, 0, 0); - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - } - - return FALSE; -} +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// + +void CMsgDialog::LoadSettings() +{ + m_clrInputBG = db_get_dw(0, CHAT_MODULE, "ColorMessageBG", GetSysColor(COLOR_WINDOW)); + LoadMsgDlgFont(MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CMsgDialog::ShowFilterMenu() +{ + HWND hwnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_hwnd, FilterWndProc, (LPARAM)this); + TranslateDialogDefault(hwnd); + + RECT rc; + GetWindowRect(m_btnFilter.GetHwnd(), &rc); + SetWindowPos(hwnd, HWND_TOP, rc.left - 85, (IsWindowVisible(m_btnFilter.GetHwnd()) || IsWindowVisible(m_btnBold.GetHwnd())) ? rc.top - 206 : rc.top - 186, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); +} + +void CMsgDialog::UpdateNickList() +{ + int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); + m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0); + m_nickList.SendMsg(LB_SETTOPINDEX, i, 0); + + UpdateTitle(); +} + +void CMsgDialog::UpdateOptions() +{ + m_btnNickList.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bNicklistEnabled ? IDI_NICKLIST2 : IDI_NICKLIST, FALSE)); + m_btnFilter.SendMsg(BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_plugin.getIcon(m_bFilterEnabled ? IDI_FILTER2 : IDI_FILTER, FALSE)); + + HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); + SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); + DestroyIcon(hIcon); + + Window_SetIcon_IcoLib(m_pOwner->GetHwnd(), g_plugin.getIconHandle(IDI_CHANMGR)); + + m_pLog->UpdateOptions(); + + // nicklist + int ih = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListFont, FALSE); + int ih2 = Chat_GetTextPixelSize(L"AQGglo", g_Settings.UserListHeadingsFont, FALSE); + int height = db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12); + int font = ih > ih2 ? ih : ih2; + + // make sure we have space for icon! + if (g_Settings.bShowContactStatus) + font = font > 16 ? font : 16; + + m_nickList.SendMsg(LB_SETITEMHEIGHT, 0, height > font ? height : font); + InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE); + + CSuper::UpdateOptions(); +} + +void CMsgDialog::UpdateStatusBar() +{ + wchar_t *ptszDispName = m_si->pMI->ptszModDispName; + int x = 12; + x += Chat_GetTextPixelSize(ptszDispName, (HFONT)SendMessage(m_pOwner->m_hwndStatus, WM_GETFONT, 0, 0), TRUE); + x += GetSystemMetrics(SM_CXSMICON); + int iStatusbarParts[2] = { x, -1 }; + SendMessage(m_pOwner->m_hwndStatus, SB_SETPARTS, 2, (LPARAM)&iStatusbarParts); + + HICON hIcon = ImageList_GetIcon(Clist_GetImageList(), GetImageId(), ILD_TRANSPARENT); + SendMessage(m_pOwner->m_hwndStatus, SB_SETICON, 0, (LPARAM)hIcon); + DestroyIcon(hIcon); + + SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)ptszDispName); + SendMessage(m_pOwner->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); + SendMessage(m_pOwner->m_hwndStatus, SB_SETTIPTEXT, 1, (LPARAM)(m_si->ptszStatusbarText ? m_si->ptszStatusbarText : L"")); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) +{ + auto *si = m_pDlg.m_si; + if (lin == nullptr || si == nullptr) + return; + + if (!bRedraw && si->iType == GCW_CHATROOM && m_pDlg.m_bFilterEnabled && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0) + return; + + LOGSTREAMDATA streamData; + memset(&streamData, 0, sizeof(streamData)); + streamData.hwnd = m_rtf.GetHwnd(); + streamData.si = si; + streamData.lin = lin; + streamData.bStripFormat = FALSE; + + bool bFlag = false; + + EDITSTREAM stream = {}; + stream.pfnCallback = Srmm_LogStreamCallback; + stream.dwCookie = (DWORD_PTR)& streamData; + + SCROLLINFO scroll; + scroll.cbSize = sizeof(SCROLLINFO); + scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll); + + POINT point = {}; + m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point); + + // do not scroll to bottom if there is a selection + CHARRANGE oldsel, sel; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel); + if (oldsel.cpMax != oldsel.cpMin) + m_rtf.SetDraw(false); + + //set the insertion point at the bottom + sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + + // fix for the indent... must be a M$ bug + if (sel.cpMax == 0) + bRedraw = TRUE; + + // should the event(s) be appended to the current log + WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF; + + //get the number of pixels per logical inch + if (bRedraw) { + HDC hdc = GetDC(nullptr); + g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); + g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(nullptr, hdc); + m_rtf.SetDraw(false); + bFlag = true; + } + + // stream in the event(s) + streamData.lin = lin; + streamData.bRedraw = bRedraw; + m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream); + + // do smileys + if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) { + CHARRANGE newsel; + newsel.cpMax = -1; + newsel.cpMin = sel.cpMin; + if (newsel.cpMin < 0) + newsel.cpMin = 0; + + SMADD_RICHEDIT3 sm = {}; + sm.cbSize = sizeof(sm); + sm.hwndRichEditControl = m_rtf.GetHwnd(); + sm.Protocolname = si->pszModule; + sm.rangeToReplace = bRedraw ? nullptr : &newsel; + sm.disableRedraw = TRUE; + sm.hContact = si->hContact; + CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm); + } + + // scroll log to bottom if the log was previously scrolled to bottom, else restore old position + if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50) + ScrollToBottom(); + else + m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point); + + // do we need to restore the selection + if (oldsel.cpMax != oldsel.cpMin) { + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel); + m_rtf.SetDraw(true); + InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); + } + + // need to invalidate the window + if (bFlag) { + sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + m_rtf.SetDraw(true); + InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static CMsgDialog *pDlg = nullptr; + switch (uMsg) { + case WM_INITDIALOG: + pDlg = (CMsgDialog*)lParam; + CheckDlgButton(hwndDlg, IDC_1, pDlg->m_iLogFilterFlags & GC_EVENT_ACTION ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_2, pDlg->m_iLogFilterFlags & GC_EVENT_MESSAGE ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_3, pDlg->m_iLogFilterFlags & GC_EVENT_NICK ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_4, pDlg->m_iLogFilterFlags & GC_EVENT_JOIN ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_5, pDlg->m_iLogFilterFlags & GC_EVENT_PART ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_6, pDlg->m_iLogFilterFlags & GC_EVENT_TOPIC ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_7, pDlg->m_iLogFilterFlags & GC_EVENT_ADDSTATUS ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_8, pDlg->m_iLogFilterFlags & GC_EVENT_INFORMATION ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_9, pDlg->m_iLogFilterFlags & GC_EVENT_QUIT ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_10, pDlg->m_iLogFilterFlags & GC_EVENT_KICK ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_11, pDlg->m_iLogFilterFlags & GC_EVENT_NOTICE ? BST_CHECKED : BST_UNCHECKED); + break; + + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + SetTextColor((HDC)wParam, RGB(60, 60, 150)); + SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); + + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) { + int iFlags = 0; + + if (IsDlgButtonChecked(hwndDlg, IDC_1) == BST_CHECKED) + iFlags |= GC_EVENT_ACTION; + if (IsDlgButtonChecked(hwndDlg, IDC_2) == BST_CHECKED) + iFlags |= GC_EVENT_MESSAGE; + if (IsDlgButtonChecked(hwndDlg, IDC_3) == BST_CHECKED) + iFlags |= GC_EVENT_NICK; + if (IsDlgButtonChecked(hwndDlg, IDC_4) == BST_CHECKED) + iFlags |= GC_EVENT_JOIN; + if (IsDlgButtonChecked(hwndDlg, IDC_5) == BST_CHECKED) + iFlags |= GC_EVENT_PART; + if (IsDlgButtonChecked(hwndDlg, IDC_6) == BST_CHECKED) + iFlags |= GC_EVENT_TOPIC; + if (IsDlgButtonChecked(hwndDlg, IDC_7) == BST_CHECKED) + iFlags |= GC_EVENT_ADDSTATUS; + if (IsDlgButtonChecked(hwndDlg, IDC_8) == BST_CHECKED) + iFlags |= GC_EVENT_INFORMATION; + if (IsDlgButtonChecked(hwndDlg, IDC_9) == BST_CHECKED) + iFlags |= GC_EVENT_QUIT; + if (IsDlgButtonChecked(hwndDlg, IDC_10) == BST_CHECKED) + iFlags |= GC_EVENT_KICK; + if (IsDlgButtonChecked(hwndDlg, IDC_11) == BST_CHECKED) + iFlags |= GC_EVENT_NOTICE; + + if (iFlags & GC_EVENT_ADDSTATUS) + iFlags |= GC_EVENT_REMOVESTATUS; + + pDlg->m_iLogFilterFlags = iFlags; + if (pDlg->m_bFilterEnabled) + pDlg->RedrawLog(); + PostMessage(hwndDlg, WM_CLOSE, 0, 0); + } + break; + + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + } + + return FALSE; +} diff --git a/src/core/stdmsg/src/cmdlist.cpp b/src/core/stdmsg/src/cmdlist.cpp index beb669dcf5..1ace5b0545 100644 --- a/src/core/stdmsg/src/cmdlist.cpp +++ b/src/core/stdmsg/src/cmdlist.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/cmdlist.h b/src/core/stdmsg/src/cmdlist.h index bbe8c9b11b..fda5562689 100644 --- a/src/core/stdmsg/src/cmdlist.h +++ b/src/core/stdmsg/src/cmdlist.h @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/globals.cpp b/src/core/stdmsg/src/globals.cpp index de2ca11c6d..5d2b95cc46 100644 --- a/src/core/stdmsg/src/globals.cpp +++ b/src/core/stdmsg/src/globals.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/msgdialog.cpp b/src/core/stdmsg/src/msgdialog.cpp index 8e0c4ff3ed..359b308022 100644 --- a/src/core/stdmsg/src/msgdialog.cpp +++ b/src/core/stdmsg/src/msgdialog.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/msglog.cpp b/src/core/stdmsg/src/msglog.cpp index 8fb1f5edbd..791cd1b71e 100644 --- a/src/core/stdmsg/src/msglog.cpp +++ b/src/core/stdmsg/src/msglog.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/msgoptions.cpp b/src/core/stdmsg/src/msgoptions.cpp index 590afa2478..34dc8e044f 100644 --- a/src/core/stdmsg/src/msgoptions.cpp +++ b/src/core/stdmsg/src/msgoptions.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/msgs.cpp b/src/core/stdmsg/src/msgs.cpp index 95b209fd02..9f5be7be28 100644 --- a/src/core/stdmsg/src/msgs.cpp +++ b/src/core/stdmsg/src/msgs.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/msgs.h b/src/core/stdmsg/src/msgs.h index f21110e641..0e9f38dead 100644 --- a/src/core/stdmsg/src/msgs.h +++ b/src/core/stdmsg/src/msgs.h @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/msgtimedout.cpp b/src/core/stdmsg/src/msgtimedout.cpp index 19744fe695..828924580e 100644 --- a/src/core/stdmsg/src/msgtimedout.cpp +++ b/src/core/stdmsg/src/msgtimedout.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/srmm.cpp b/src/core/stdmsg/src/srmm.cpp index 69c350aad7..4485d40bb3 100644 --- a/src/core/stdmsg/src/srmm.cpp +++ b/src/core/stdmsg/src/srmm.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/statusicon.cpp b/src/core/stdmsg/src/statusicon.cpp index 4244f9037c..6255dc3e1e 100644 --- a/src/core/stdmsg/src/statusicon.cpp +++ b/src/core/stdmsg/src/statusicon.cpp @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/stdafx.cxx b/src/core/stdmsg/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/core/stdmsg/src/stdafx.cxx +++ b/src/core/stdmsg/src/stdafx.cxx @@ -1,6 +1,6 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stdmsg/src/stdafx.h b/src/core/stdmsg/src/stdafx.h index a42feafac9..33d557a6a9 100644 --- a/src/core/stdmsg/src/stdafx.h +++ b/src/core/stdmsg/src/stdafx.h @@ -1,6 +1,6 @@ /* -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stdmsg/src/tabs.cpp b/src/core/stdmsg/src/tabs.cpp index d60bec7634..402d1f2f0f 100644 --- a/src/core/stdmsg/src/tabs.cpp +++ b/src/core/stdmsg/src/tabs.cpp @@ -1,668 +1,668 @@ -/* - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "statusicon.h" - -CTabbedWindow *g_pTabDialog = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow* GetContainer() -{ - if (g_Settings.bTabsEnable) { - if (g_pTabDialog == nullptr) { - g_pTabDialog = new CTabbedWindow(); - g_pTabDialog->Show(); - } - return g_pTabDialog; - } - - return new CTabbedWindow(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK TabSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CTabbedWindow *pOwner = (CTabbedWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - TCHITTESTINFO tci = {}; - - static bool bDragging = false; - static int iBeginIndex = 0; - switch (msg) { - case WM_LBUTTONDOWN: - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) { - tci.flags = TCHT_ONITEM; - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) { - bDragging = true; - iBeginIndex = idx; - ImageList_BeginDrag(Clist_GetImageList(), pDlg->GetImageId(), 8, 8); - ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y); - SetCapture(hwnd); - } - return TRUE; - } - } - else pOwner->TabClicked(); - break; - - case WM_CAPTURECHANGED: - bDragging = false; - ImageList_DragLeave(hwnd); - ImageList_EndDrag(); - break; - - case WM_MOUSEMOVE: - if (bDragging) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(hwnd, &tci.pt); - ImageList_DragMove(tci.pt.x, tci.pt.y); - } - break; - - case WM_LBUTTONUP: - if (bDragging && ReleaseCapture()) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - bDragging = false; - ImageList_DragLeave(hwnd); - ImageList_EndDrag(); - - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1 && idx != iBeginIndex) - pOwner->DropTab(idx, iBeginIndex); - } - break; - - case WM_LBUTTONDBLCLK: - if (g_Settings.bTabCloseOnDblClick) { - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(hwnd, &tci.pt); - - tci.flags = TCHT_ONITEM; - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) - pDlg->CloseTab(); - } - } - break; - - case WM_MBUTTONUP: - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - - ScreenToClient(hwnd, &tci.pt); - int idx = TabCtrl_HitTest(hwnd, &tci); - if (idx != -1) { - CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); - if (pDlg) - pDlg->CloseTab(); - } - break; - } - - return mir_callNextSubclass(hwnd, TabSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow::CTabbedWindow() : - CDlgBase(g_plugin, IDD_CONTAINER), - m_tab(this, IDC_TAB) -{ - SetMinSize(450, 350); -} - -bool CTabbedWindow::OnInitDialog() -{ - SetWindowLongPtr(m_tab.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_tab.GetHwnd(), ::TabSubclassProc); - - m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr); - SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); - - RECT rc; - GetWindowRect(m_hwndStatus, &rc); - m_statusHeight = rc.bottom - rc.top; - - SetWindowPosition(); - - if (!g_Settings.bTabsEnable) { - m_tab.Hide(); - return false; - } - - LONG_PTR mask = GetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE); - if (g_Settings.bTabsAtBottom) - mask |= TCS_BOTTOM; - else - mask &= ~TCS_BOTTOM; - SetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE, mask); - - TabCtrl_SetMinTabWidth(m_tab.GetHwnd(), 80); - TabCtrl_SetImageList(m_tab.GetHwnd(), Clist_GetImageList()); - return true; -} - -void CTabbedWindow::OnDestroy() -{ - DestroyWindow(m_hwndStatus); m_hwndStatus = nullptr; - - SaveWindowPosition(true); - - Utils_SaveWindowPosition(m_hwnd, g_plugin.bSavePerContact ? ((m_pEmbed == nullptr) ? 0 : m_pEmbed->m_hContact) : 0, CHAT_MODULE, "room"); - - if (m_pEmbed == nullptr) - g_pTabDialog = nullptr; -} - -void CTabbedWindow::OnResize() -{ - CDlgBase::OnResize(); - - SendMessage(m_hwndStatus, WM_SIZE, 0, 0); - - if (m_pEmbed) { - RECT rc; - GetClientRect(m_tab.GetHwnd(), &rc); - MoveWindow(m_pEmbed->GetHwnd(), 0, 0, rc.right - rc.left, rc.bottom - rc.top, FALSE); - } - - SaveWindowPosition(false); -} - -int CTabbedWindow::Resizer(UTILRESIZECONTROL *urc) -{ - if (urc->wId == IDC_TAB) { - urc->rcItem.top = 1; - urc->rcItem.bottom = urc->dlgNewSize.cy - m_statusHeight - 1; - return RD_ANCHORX_WIDTH | RD_ANCHORY_CUSTOM; - } - - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; // status bar -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CTabbedWindow* CTabbedWindow::AddPage(MCONTACT hContact, wchar_t *pwszText, int iNoActivate) -{ - CMsgDialog *pDlg = new CMsgDialog(this, hContact); - pDlg->m_wszInitialText = pwszText; - if (iNoActivate != -1) - pDlg->m_bNoActivate = iNoActivate != 0; - - if (g_Settings.bTabsEnable) { - m_tab.AddPage(Clist_GetContactDisplayName(hContact), nullptr, pDlg); - FixTabIcons(pDlg); - - m_tab.ActivatePage(m_tab.GetCount() - 1); - } - else { - m_pEmbed = pDlg; - Create(); - pDlg->SetParent(m_hwnd); - pDlg->Create(); - Show(); - } - - PostMessage(m_hwnd, WM_SIZE, 0, 0); - return this; -} - -void CTabbedWindow::AddPage(SESSION_INFO *si, int insertAt) -{ - // does the tab already exist? - int indexfound = (si->pDlg) ? m_tab.GetDlgIndex(si->pDlg) : -1; - if (indexfound == -1) { // create a new tab - wchar_t szTemp[30]; - mir_wstrncpy(szTemp, si->ptszName, 21); - if (mir_wstrlen(si->ptszName) > 20) - mir_wstrncpy(szTemp + 20, L"...", 4); - - if (!IsWindowVisible(m_hwnd)) - Show(SW_SHOW); - - CMsgDialog *pDlg = new CMsgDialog(this, si); - pDlg->SetParent(m_hwnd); - m_tab.AddPage(szTemp, nullptr, pDlg); - m_tab.ActivatePage(m_tab.GetCount() - 1); - FixTabIcons(pDlg); - } - else if (insertAt == -1) - m_tab.ActivatePage(indexfound); -} - -CMsgDialog* CTabbedWindow::CurrPage() const -{ - return (m_pEmbed != nullptr) ? m_pEmbed : (CMsgDialog*)m_tab.GetActivePage(); -} - -void CTabbedWindow::DropTab(int begin, int end) -{ - if (begin == end) - return; - - m_tab.SwapPages(begin, end); - - CMsgDialog *pDlg = (CMsgDialog *)m_tab.GetNthPage(end); - if (pDlg) { - FixTabIcons(pDlg); - m_tab.ActivatePage(end); - } - - // fix the "fixed" positions - int tabCount = m_tab.GetCount(); - for (int i = 0; i < tabCount; i++) { - pDlg = (CMsgDialog *)m_tab.GetNthPage(i); - if (pDlg == nullptr) - continue; - - SESSION_INFO *si = pDlg->m_si; - if (si && si->hContact && db_get_w(si->hContact, si->pszModule, "TabPosition", 0) != 0) - db_set_w(si->hContact, si->pszModule, "TabPosition", i + 1); - } -} - -void CTabbedWindow::FixTabIcons(CMsgDialog *pDlg) -{ - if (pDlg == nullptr) - return; - - int image = pDlg->GetImageId(); - - // if tabs are turned off, simply change the window's icon, otherwise set the tab's icon first - if (m_pEmbed == nullptr) { - int idx = m_tab.GetDlgIndex(pDlg); - if (idx == -1) - return; - - TCITEM tci = {}; - tci.mask = TCIF_IMAGE; - TabCtrl_GetItem(m_tab.GetHwnd(), idx, &tci); - if (tci.iImage != image) { - tci.iImage = image; - TabCtrl_SetItem(m_tab.GetHwnd(), idx, &tci); - } - } - - // set the container's icon only if we're processing the current page - if (pDlg == CurrPage()) { - Window_FreeIcon_IcoLib(m_hwnd); - if (g_plugin.bUseStatusWinIcon) - Window_SetProtoIcon_IcoLib(m_hwnd, pDlg->m_szProto, pDlg->m_wStatus); - else if (pDlg->isChat()) - Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_CHANMGR)); - else - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_EVENT_MESSAGE); - } -} - -void CTabbedWindow::RemoveTab(CMsgDialog *pDlg) -{ - int idx = m_tab.GetDlgIndex(pDlg); - if (idx == -1) - return; - - m_tab.RemovePage(idx); - if (m_tab.GetCount() == 0) - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - else { - if (m_tab.GetNthPage(idx) == nullptr) - idx--; - m_tab.ActivatePage(idx); - } -} - -void CTabbedWindow::SaveWindowPosition(bool bUpdateSession) -{ - WINDOWPLACEMENT wp = {}; - wp.length = sizeof(wp); - GetWindowPlacement(m_hwnd, &wp); - - g_Settings.iX = wp.rcNormalPosition.left; - g_Settings.iY = wp.rcNormalPosition.top; - g_Settings.iWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left; - g_Settings.iHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; - - if (bUpdateSession) { - iX = g_Settings.iX; - iY = g_Settings.iY; - iWidth = g_Settings.iWidth; - iHeight = g_Settings.iHeight; - } -} - -void CTabbedWindow::SetMessageHighlight(CMsgDialog *pDlg) -{ - if (m_tab.GetDlgIndex(pDlg) == -1) - return; - - pDlg->m_si->wState |= GC_EVENT_HIGHLIGHT; - FixTabIcons(pDlg); - if (Chat::bFlashWindowHighlight && pDlg != m_tab.GetActivePage()) - pDlg->StartFlash(); -} - -void CTabbedWindow::SetTabHighlight(CMsgDialog *pDlg) -{ - if (m_tab.GetDlgIndex(pDlg) == -1) - return; - - FixTabIcons(pDlg); - if (Chat::bFlashWindow && pDlg != m_tab.GetActivePage()) - pDlg->StartFlash(); -} - -void CTabbedWindow::SetWindowPosition() -{ - if (m_pEmbed == nullptr) { - Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room"); - return; - } - - if (iX) { - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(m_hwnd, &wp); - - wp.rcNormalPosition.left = iX; - wp.rcNormalPosition.top = iY; - wp.rcNormalPosition.right = wp.rcNormalPosition.left + iWidth; - wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + iHeight; - wp.showCmd = SW_HIDE; - SetWindowPlacement(m_hwnd, &wp); - return; - } - - int flag = m_pEmbed->m_bNoActivate ? RWPF_HIDDEN : 0; - if (Utils_RestoreWindowPosition(m_hwnd, g_plugin.bSavePerContact ? m_pEmbed->m_hContact : 0, CHAT_MODULE, "room", flag)) { - if (g_plugin.bSavePerContact) { - if (Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room", flag | RWPF_NOMOVE)) - SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); - } - else SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); - } - - if (!g_plugin.bSavePerContact && g_plugin.bCascade) { - RECT rc, rcMax = {}; - CTabbedWindow *pMaxTab = nullptr; - - for (auto &it : g_arDialogs) { - auto *pTab = it->m_pOwner; - if (pTab == this) - continue; - - GetWindowRect(pTab->GetHwnd(), &rc); - if (rc.left > rcMax.left && rc.top > rcMax.top) { - pMaxTab = pTab; - rcMax = rc; - } - } - - if (pMaxTab) { - m_windowWasCascaded = 1; - int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME); - SetWindowPos(m_hwnd, nullptr, rcMax.left + offset, rcMax.top + offset, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); - } - } -} - -void CTabbedWindow::SwitchNextTab() -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total > 1) { - if (i < total - 1) - i++; - else - i = 0; - m_tab.ActivatePage(i); - TabClicked(); - } -} - -void CTabbedWindow::SwitchPrevTab() -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total >= 1) { - if (i > 0) - i--; - else - i = total - 1; - m_tab.ActivatePage(i); - TabClicked(); - } -} - -void CTabbedWindow::SwitchTab(int iNewTab) -{ - int total = m_tab.GetCount(); - int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (i != -1 && total != -1 && total != 1 && i != iNewTab && total > iNewTab) { - m_tab.ActivatePage(iNewTab); - TabClicked(); - } -} - -void CTabbedWindow::TabClicked() -{ - CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetActivePage(); - if (pDlg == nullptr) - return; - - SetFocus(pDlg->m_message.GetHwnd()); - - SESSION_INFO *si = pDlg->m_si; - if (si) { - if (si->wState & STATE_TALK) { - si->wState &= ~STATE_TALK; - db_set_w(si->hContact, si->pszModule, "ApparentMode", 0); - } - - if (si->wState & GC_EVENT_HIGHLIGHT) { - si->wState &= ~GC_EVENT_HIGHLIGHT; - - if (g_clistApi.pfnGetEvent(si->hContact, 0)) - g_clistApi.pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); - } - - if (!si->pDlg) { - g_chatApi.ShowRoom(si); - SendMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - } - } - - FixTabIcons(pDlg); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CTabbedWindow::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - int idx; - - switch (msg) { - case WM_MOVE: - SaveWindowPosition(false); - break; - - case WM_ENTERSIZEMOVE: - GetClientRect(m_hwnd, &rc); - oldSizeX = rc.right - rc.left; - oldSizeY = rc.bottom - rc.top; - break; - - case WM_EXITSIZEMOVE: - GetClientRect(m_hwnd, &rc); - if (!((rc.right - rc.left) == oldSizeX && (rc.bottom - rc.top) == oldSizeY)) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) { - pDlg->m_pLog->ScrollToBottom(); - pDlg->Resize(); - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; - if (dis->hwndItem == m_hwndStatus) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) - DrawStatusIcons(pDlg->m_hContact, dis->hDC, dis->rcItem, 2); - return TRUE; - } - } - break; - - case WM_COMMAND: - if (LOWORD(wParam) == IDOK) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) { - pDlg->m_btnOk.Click(); - return TRUE; - } - } - break; - - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) - break; - - if (!m_pEmbed) { - idx = TabCtrl_GetCurSel(m_tab.GetHwnd()); - if (idx != -1) - m_tab.ActivatePage(idx); - } - else m_pEmbed->OnActivate(); - break; - - case WM_NOTIFY: - if (((LPNMHDR)lParam)->hwndFrom == m_hwndStatus) { - if (((LPNMHDR)lParam)->code == NM_CLICK || ((LPNMHDR)lParam)->code == NM_RCLICK) { - NMMOUSE *nm = (NMMOUSE *)lParam; - SendMessage(m_hwndStatus, SB_GETRECT, SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0) - 1, (LPARAM)&rc); - if (nm->pt.x >= rc.left) { - CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; - if (pDlg != nullptr) - CheckStatusIconClick(pDlg->m_hContact, m_hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0); - } - return TRUE; - } - } - - if (((LPNMHDR)lParam)->idFrom == IDC_TAB) { - switch (((LPNMHDR)lParam)->code) { - case TCN_SELCHANGE: - m_tab.ActivatePage(TabCtrl_GetCurSel(m_tab.GetHwnd())); - break; - - case NM_RCLICK: - int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); - if (i == -1) - break; - - TCHITTESTINFO tci = {}; - tci.pt.x = (short)LOWORD(GetMessagePos()); - tci.pt.y = (short)HIWORD(GetMessagePos()); - tci.flags = TCHT_ONITEM; - ScreenToClient(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); - if ((i = TabCtrl_HitTest(((LPNMHDR)lParam)->hwndFrom, &tci)) == -1) - break; - - CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetNthPage(i); - SESSION_INFO *si = pDlg->m_si; - - ClientToScreen(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); - HMENU hSubMenu = GetSubMenu(g_hMenu, 1); - TranslateMenu(hSubMenu); - - if (si != nullptr) { - uint16_t w = db_get_w(si->hContact, si->pszModule, "TabPosition", 0); - if (w == 0) - CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); - else - CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_CHECKED); - } - else CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, tci.pt.x, tci.pt.y, 0, m_hwnd, nullptr)) { - case ID_CLOSE: - pDlg->CloseTab(); - break; - - case ID_LOCKPOSITION: - if (si != nullptr) { - if (!(GetMenuState(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND)&MF_CHECKED)) { - if (si->hContact) - db_set_w(si->hContact, si->pszModule, "TabPosition", (uint16_t)(i + 1)); - } - else db_unset(si->hContact, si->pszModule, "TabPosition"); - } - break; - - case ID_CLOSEOTHER: - int tabCount = m_tab.GetCount(); - if (tabCount > 1) { - while (tabCount--) { - if (tabCount == i) - continue; - - if (pDlg = (CMsgDialog*)m_tab.GetNthPage(tabCount)) - pDlg->CloseTab(); - } - m_tab.ActivatePage(0); - } - break; - } - } - } - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void UninitTabs() -{ - if (g_pTabDialog != nullptr) { - g_pTabDialog->Close(); - g_pTabDialog = nullptr; - } -} +/* + +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "statusicon.h" + +CTabbedWindow *g_pTabDialog = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// + +CTabbedWindow* GetContainer() +{ + if (g_Settings.bTabsEnable) { + if (g_pTabDialog == nullptr) { + g_pTabDialog = new CTabbedWindow(); + g_pTabDialog->Show(); + } + return g_pTabDialog; + } + + return new CTabbedWindow(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static LRESULT CALLBACK TabSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CTabbedWindow *pOwner = (CTabbedWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + TCHITTESTINFO tci = {}; + + static bool bDragging = false; + static int iBeginIndex = 0; + switch (msg) { + case WM_LBUTTONDOWN: + tci.pt.x = (short)LOWORD(GetMessagePos()); + tci.pt.y = (short)HIWORD(GetMessagePos()); + if (DragDetect(hwnd, tci.pt) && TabCtrl_GetItemCount(hwnd) > 1) { + tci.flags = TCHT_ONITEM; + ScreenToClient(hwnd, &tci.pt); + int idx = TabCtrl_HitTest(hwnd, &tci); + if (idx != -1) { + CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); + if (pDlg) { + bDragging = true; + iBeginIndex = idx; + ImageList_BeginDrag(Clist_GetImageList(), pDlg->GetImageId(), 8, 8); + ImageList_DragEnter(hwnd, tci.pt.x, tci.pt.y); + SetCapture(hwnd); + } + return TRUE; + } + } + else pOwner->TabClicked(); + break; + + case WM_CAPTURECHANGED: + bDragging = false; + ImageList_DragLeave(hwnd); + ImageList_EndDrag(); + break; + + case WM_MOUSEMOVE: + if (bDragging) { + tci.pt.x = (short)LOWORD(GetMessagePos()); + tci.pt.y = (short)HIWORD(GetMessagePos()); + ScreenToClient(hwnd, &tci.pt); + ImageList_DragMove(tci.pt.x, tci.pt.y); + } + break; + + case WM_LBUTTONUP: + if (bDragging && ReleaseCapture()) { + tci.pt.x = (short)LOWORD(GetMessagePos()); + tci.pt.y = (short)HIWORD(GetMessagePos()); + tci.flags = TCHT_ONITEM; + bDragging = false; + ImageList_DragLeave(hwnd); + ImageList_EndDrag(); + + ScreenToClient(hwnd, &tci.pt); + int idx = TabCtrl_HitTest(hwnd, &tci); + if (idx != -1 && idx != iBeginIndex) + pOwner->DropTab(idx, iBeginIndex); + } + break; + + case WM_LBUTTONDBLCLK: + if (g_Settings.bTabCloseOnDblClick) { + tci.pt.x = (short)LOWORD(GetMessagePos()); + tci.pt.y = (short)HIWORD(GetMessagePos()); + ScreenToClient(hwnd, &tci.pt); + + tci.flags = TCHT_ONITEM; + int idx = TabCtrl_HitTest(hwnd, &tci); + if (idx != -1) { + CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); + if (pDlg) + pDlg->CloseTab(); + } + } + break; + + case WM_MBUTTONUP: + tci.pt.x = (short)LOWORD(GetMessagePos()); + tci.pt.y = (short)HIWORD(GetMessagePos()); + tci.flags = TCHT_ONITEM; + + ScreenToClient(hwnd, &tci.pt); + int idx = TabCtrl_HitTest(hwnd, &tci); + if (idx != -1) { + CMsgDialog *pDlg = (CMsgDialog*)pOwner->m_tab.GetNthPage(idx); + if (pDlg) + pDlg->CloseTab(); + } + break; + } + + return mir_callNextSubclass(hwnd, TabSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CTabbedWindow::CTabbedWindow() : + CDlgBase(g_plugin, IDD_CONTAINER), + m_tab(this, IDC_TAB) +{ + SetMinSize(450, 350); +} + +bool CTabbedWindow::OnInitDialog() +{ + SetWindowLongPtr(m_tab.GetHwnd(), GWLP_USERDATA, LPARAM(this)); + mir_subclassWindow(m_tab.GetHwnd(), ::TabSubclassProc); + + m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, nullptr, WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | SBARS_SIZEGRIP, 0, 0, 0, 0, m_hwnd, nullptr, g_plugin.getInst(), nullptr); + SendMessage(m_hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON), 0); + + RECT rc; + GetWindowRect(m_hwndStatus, &rc); + m_statusHeight = rc.bottom - rc.top; + + SetWindowPosition(); + + if (!g_Settings.bTabsEnable) { + m_tab.Hide(); + return false; + } + + LONG_PTR mask = GetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE); + if (g_Settings.bTabsAtBottom) + mask |= TCS_BOTTOM; + else + mask &= ~TCS_BOTTOM; + SetWindowLongPtr(m_tab.GetHwnd(), GWL_STYLE, mask); + + TabCtrl_SetMinTabWidth(m_tab.GetHwnd(), 80); + TabCtrl_SetImageList(m_tab.GetHwnd(), Clist_GetImageList()); + return true; +} + +void CTabbedWindow::OnDestroy() +{ + DestroyWindow(m_hwndStatus); m_hwndStatus = nullptr; + + SaveWindowPosition(true); + + Utils_SaveWindowPosition(m_hwnd, g_plugin.bSavePerContact ? ((m_pEmbed == nullptr) ? 0 : m_pEmbed->m_hContact) : 0, CHAT_MODULE, "room"); + + if (m_pEmbed == nullptr) + g_pTabDialog = nullptr; +} + +void CTabbedWindow::OnResize() +{ + CDlgBase::OnResize(); + + SendMessage(m_hwndStatus, WM_SIZE, 0, 0); + + if (m_pEmbed) { + RECT rc; + GetClientRect(m_tab.GetHwnd(), &rc); + MoveWindow(m_pEmbed->GetHwnd(), 0, 0, rc.right - rc.left, rc.bottom - rc.top, FALSE); + } + + SaveWindowPosition(false); +} + +int CTabbedWindow::Resizer(UTILRESIZECONTROL *urc) +{ + if (urc->wId == IDC_TAB) { + urc->rcItem.top = 1; + urc->rcItem.bottom = urc->dlgNewSize.cy - m_statusHeight - 1; + return RD_ANCHORX_WIDTH | RD_ANCHORY_CUSTOM; + } + + return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; // status bar +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CTabbedWindow* CTabbedWindow::AddPage(MCONTACT hContact, wchar_t *pwszText, int iNoActivate) +{ + CMsgDialog *pDlg = new CMsgDialog(this, hContact); + pDlg->m_wszInitialText = pwszText; + if (iNoActivate != -1) + pDlg->m_bNoActivate = iNoActivate != 0; + + if (g_Settings.bTabsEnable) { + m_tab.AddPage(Clist_GetContactDisplayName(hContact), nullptr, pDlg); + FixTabIcons(pDlg); + + m_tab.ActivatePage(m_tab.GetCount() - 1); + } + else { + m_pEmbed = pDlg; + Create(); + pDlg->SetParent(m_hwnd); + pDlg->Create(); + Show(); + } + + PostMessage(m_hwnd, WM_SIZE, 0, 0); + return this; +} + +void CTabbedWindow::AddPage(SESSION_INFO *si, int insertAt) +{ + // does the tab already exist? + int indexfound = (si->pDlg) ? m_tab.GetDlgIndex(si->pDlg) : -1; + if (indexfound == -1) { // create a new tab + wchar_t szTemp[30]; + mir_wstrncpy(szTemp, si->ptszName, 21); + if (mir_wstrlen(si->ptszName) > 20) + mir_wstrncpy(szTemp + 20, L"...", 4); + + if (!IsWindowVisible(m_hwnd)) + Show(SW_SHOW); + + CMsgDialog *pDlg = new CMsgDialog(this, si); + pDlg->SetParent(m_hwnd); + m_tab.AddPage(szTemp, nullptr, pDlg); + m_tab.ActivatePage(m_tab.GetCount() - 1); + FixTabIcons(pDlg); + } + else if (insertAt == -1) + m_tab.ActivatePage(indexfound); +} + +CMsgDialog* CTabbedWindow::CurrPage() const +{ + return (m_pEmbed != nullptr) ? m_pEmbed : (CMsgDialog*)m_tab.GetActivePage(); +} + +void CTabbedWindow::DropTab(int begin, int end) +{ + if (begin == end) + return; + + m_tab.SwapPages(begin, end); + + CMsgDialog *pDlg = (CMsgDialog *)m_tab.GetNthPage(end); + if (pDlg) { + FixTabIcons(pDlg); + m_tab.ActivatePage(end); + } + + // fix the "fixed" positions + int tabCount = m_tab.GetCount(); + for (int i = 0; i < tabCount; i++) { + pDlg = (CMsgDialog *)m_tab.GetNthPage(i); + if (pDlg == nullptr) + continue; + + SESSION_INFO *si = pDlg->m_si; + if (si && si->hContact && db_get_w(si->hContact, si->pszModule, "TabPosition", 0) != 0) + db_set_w(si->hContact, si->pszModule, "TabPosition", i + 1); + } +} + +void CTabbedWindow::FixTabIcons(CMsgDialog *pDlg) +{ + if (pDlg == nullptr) + return; + + int image = pDlg->GetImageId(); + + // if tabs are turned off, simply change the window's icon, otherwise set the tab's icon first + if (m_pEmbed == nullptr) { + int idx = m_tab.GetDlgIndex(pDlg); + if (idx == -1) + return; + + TCITEM tci = {}; + tci.mask = TCIF_IMAGE; + TabCtrl_GetItem(m_tab.GetHwnd(), idx, &tci); + if (tci.iImage != image) { + tci.iImage = image; + TabCtrl_SetItem(m_tab.GetHwnd(), idx, &tci); + } + } + + // set the container's icon only if we're processing the current page + if (pDlg == CurrPage()) { + Window_FreeIcon_IcoLib(m_hwnd); + if (g_plugin.bUseStatusWinIcon) + Window_SetProtoIcon_IcoLib(m_hwnd, pDlg->m_szProto, pDlg->m_wStatus); + else if (pDlg->isChat()) + Window_SetIcon_IcoLib(m_hwnd, g_plugin.getIconHandle(IDI_CHANMGR)); + else + Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_EVENT_MESSAGE); + } +} + +void CTabbedWindow::RemoveTab(CMsgDialog *pDlg) +{ + int idx = m_tab.GetDlgIndex(pDlg); + if (idx == -1) + return; + + m_tab.RemovePage(idx); + if (m_tab.GetCount() == 0) + PostMessage(m_hwnd, WM_CLOSE, 0, 0); + else { + if (m_tab.GetNthPage(idx) == nullptr) + idx--; + m_tab.ActivatePage(idx); + } +} + +void CTabbedWindow::SaveWindowPosition(bool bUpdateSession) +{ + WINDOWPLACEMENT wp = {}; + wp.length = sizeof(wp); + GetWindowPlacement(m_hwnd, &wp); + + g_Settings.iX = wp.rcNormalPosition.left; + g_Settings.iY = wp.rcNormalPosition.top; + g_Settings.iWidth = wp.rcNormalPosition.right - wp.rcNormalPosition.left; + g_Settings.iHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; + + if (bUpdateSession) { + iX = g_Settings.iX; + iY = g_Settings.iY; + iWidth = g_Settings.iWidth; + iHeight = g_Settings.iHeight; + } +} + +void CTabbedWindow::SetMessageHighlight(CMsgDialog *pDlg) +{ + if (m_tab.GetDlgIndex(pDlg) == -1) + return; + + pDlg->m_si->wState |= GC_EVENT_HIGHLIGHT; + FixTabIcons(pDlg); + if (Chat::bFlashWindowHighlight && pDlg != m_tab.GetActivePage()) + pDlg->StartFlash(); +} + +void CTabbedWindow::SetTabHighlight(CMsgDialog *pDlg) +{ + if (m_tab.GetDlgIndex(pDlg) == -1) + return; + + FixTabIcons(pDlg); + if (Chat::bFlashWindow && pDlg != m_tab.GetActivePage()) + pDlg->StartFlash(); +} + +void CTabbedWindow::SetWindowPosition() +{ + if (m_pEmbed == nullptr) { + Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room"); + return; + } + + if (iX) { + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + GetWindowPlacement(m_hwnd, &wp); + + wp.rcNormalPosition.left = iX; + wp.rcNormalPosition.top = iY; + wp.rcNormalPosition.right = wp.rcNormalPosition.left + iWidth; + wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + iHeight; + wp.showCmd = SW_HIDE; + SetWindowPlacement(m_hwnd, &wp); + return; + } + + int flag = m_pEmbed->m_bNoActivate ? RWPF_HIDDEN : 0; + if (Utils_RestoreWindowPosition(m_hwnd, g_plugin.bSavePerContact ? m_pEmbed->m_hContact : 0, CHAT_MODULE, "room", flag)) { + if (g_plugin.bSavePerContact) { + if (Utils_RestoreWindowPosition(m_hwnd, 0, CHAT_MODULE, "room", flag | RWPF_NOMOVE)) + SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); + } + else SetWindowPos(m_hwnd, nullptr, 0, 0, 550, 400, SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW); + } + + if (!g_plugin.bSavePerContact && g_plugin.bCascade) { + RECT rc, rcMax = {}; + CTabbedWindow *pMaxTab = nullptr; + + for (auto &it : g_arDialogs) { + auto *pTab = it->m_pOwner; + if (pTab == this) + continue; + + GetWindowRect(pTab->GetHwnd(), &rc); + if (rc.left > rcMax.left && rc.top > rcMax.top) { + pMaxTab = pTab; + rcMax = rc; + } + } + + if (pMaxTab) { + m_windowWasCascaded = 1; + int offset = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME); + SetWindowPos(m_hwnd, nullptr, rcMax.left + offset, rcMax.top + offset, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE); + } + } +} + +void CTabbedWindow::SwitchNextTab() +{ + int total = m_tab.GetCount(); + int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); + if (i != -1 && total > 1) { + if (i < total - 1) + i++; + else + i = 0; + m_tab.ActivatePage(i); + TabClicked(); + } +} + +void CTabbedWindow::SwitchPrevTab() +{ + int total = m_tab.GetCount(); + int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); + if (i != -1 && total >= 1) { + if (i > 0) + i--; + else + i = total - 1; + m_tab.ActivatePage(i); + TabClicked(); + } +} + +void CTabbedWindow::SwitchTab(int iNewTab) +{ + int total = m_tab.GetCount(); + int i = TabCtrl_GetCurSel(m_tab.GetHwnd()); + if (i != -1 && total != -1 && total != 1 && i != iNewTab && total > iNewTab) { + m_tab.ActivatePage(iNewTab); + TabClicked(); + } +} + +void CTabbedWindow::TabClicked() +{ + CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetActivePage(); + if (pDlg == nullptr) + return; + + SetFocus(pDlg->m_message.GetHwnd()); + + SESSION_INFO *si = pDlg->m_si; + if (si) { + if (si->wState & STATE_TALK) { + si->wState &= ~STATE_TALK; + db_set_w(si->hContact, si->pszModule, "ApparentMode", 0); + } + + if (si->wState & GC_EVENT_HIGHLIGHT) { + si->wState &= ~GC_EVENT_HIGHLIGHT; + + if (g_clistApi.pfnGetEvent(si->hContact, 0)) + g_clistApi.pfnRemoveEvent(si->hContact, GC_FAKE_EVENT); + } + + if (!si->pDlg) { + g_chatApi.ShowRoom(si); + SendMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + } + } + + FixTabIcons(pDlg); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CTabbedWindow::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + RECT rc; + int idx; + + switch (msg) { + case WM_MOVE: + SaveWindowPosition(false); + break; + + case WM_ENTERSIZEMOVE: + GetClientRect(m_hwnd, &rc); + oldSizeX = rc.right - rc.left; + oldSizeY = rc.bottom - rc.top; + break; + + case WM_EXITSIZEMOVE: + GetClientRect(m_hwnd, &rc); + if (!((rc.right - rc.left) == oldSizeX && (rc.bottom - rc.top) == oldSizeY)) { + CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; + if (pDlg != nullptr) { + pDlg->m_pLog->ScrollToBottom(); + pDlg->Resize(); + } + } + break; + + case WM_DRAWITEM: + { + LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam; + if (dis->hwndItem == m_hwndStatus) { + CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; + if (pDlg != nullptr) + DrawStatusIcons(pDlg->m_hContact, dis->hDC, dis->rcItem, 2); + return TRUE; + } + } + break; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK) { + CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; + if (pDlg != nullptr) { + pDlg->m_btnOk.Click(); + return TRUE; + } + } + break; + + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) + break; + + if (!m_pEmbed) { + idx = TabCtrl_GetCurSel(m_tab.GetHwnd()); + if (idx != -1) + m_tab.ActivatePage(idx); + } + else m_pEmbed->OnActivate(); + break; + + case WM_NOTIFY: + if (((LPNMHDR)lParam)->hwndFrom == m_hwndStatus) { + if (((LPNMHDR)lParam)->code == NM_CLICK || ((LPNMHDR)lParam)->code == NM_RCLICK) { + NMMOUSE *nm = (NMMOUSE *)lParam; + SendMessage(m_hwndStatus, SB_GETRECT, SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0) - 1, (LPARAM)&rc); + if (nm->pt.x >= rc.left) { + CMsgDialog *pDlg = (g_Settings.bTabsEnable) ? (CMsgDialog*)m_tab.GetActivePage() : m_pEmbed; + if (pDlg != nullptr) + CheckStatusIconClick(pDlg->m_hContact, m_hwndStatus, nm->pt, rc, 2, ((LPNMHDR)lParam)->code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0); + } + return TRUE; + } + } + + if (((LPNMHDR)lParam)->idFrom == IDC_TAB) { + switch (((LPNMHDR)lParam)->code) { + case TCN_SELCHANGE: + m_tab.ActivatePage(TabCtrl_GetCurSel(m_tab.GetHwnd())); + break; + + case NM_RCLICK: + int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom); + if (i == -1) + break; + + TCHITTESTINFO tci = {}; + tci.pt.x = (short)LOWORD(GetMessagePos()); + tci.pt.y = (short)HIWORD(GetMessagePos()); + tci.flags = TCHT_ONITEM; + ScreenToClient(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); + if ((i = TabCtrl_HitTest(((LPNMHDR)lParam)->hwndFrom, &tci)) == -1) + break; + + CMsgDialog *pDlg = (CMsgDialog*)m_tab.GetNthPage(i); + SESSION_INFO *si = pDlg->m_si; + + ClientToScreen(GetDlgItem(m_hwnd, IDC_TAB), &tci.pt); + HMENU hSubMenu = GetSubMenu(g_hMenu, 1); + TranslateMenu(hSubMenu); + + if (si != nullptr) { + uint16_t w = db_get_w(si->hContact, si->pszModule, "TabPosition", 0); + if (w == 0) + CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); + else + CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_CHECKED); + } + else CheckMenuItem(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND | MF_UNCHECKED); + + switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, tci.pt.x, tci.pt.y, 0, m_hwnd, nullptr)) { + case ID_CLOSE: + pDlg->CloseTab(); + break; + + case ID_LOCKPOSITION: + if (si != nullptr) { + if (!(GetMenuState(hSubMenu, ID_LOCKPOSITION, MF_BYCOMMAND)&MF_CHECKED)) { + if (si->hContact) + db_set_w(si->hContact, si->pszModule, "TabPosition", (uint16_t)(i + 1)); + } + else db_unset(si->hContact, si->pszModule, "TabPosition"); + } + break; + + case ID_CLOSEOTHER: + int tabCount = m_tab.GetCount(); + if (tabCount > 1) { + while (tabCount--) { + if (tabCount == i) + continue; + + if (pDlg = (CMsgDialog*)m_tab.GetNthPage(tabCount)) + pDlg->CloseTab(); + } + m_tab.ActivatePage(0); + } + break; + } + } + } + break; + } + + return CDlgBase::DlgProc(msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void UninitTabs() +{ + if (g_pTabDialog != nullptr) { + g_pTabDialog->Close(); + g_pTabDialog = nullptr; + } +} diff --git a/src/core/stdmsg/src/version.h b/src/core/stdmsg/src/version.h index 80b240e521..c2573ed98b 100644 --- a/src/core/stdmsg/src/version.h +++ b/src/core/stdmsg/src/version.h @@ -9,4 +9,4 @@ #define __DESCRIPTION "Core module for send/receive instant messages." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdMsg" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stdpopup/src/stdafx.cxx b/src/core/stdpopup/src/stdafx.cxx index 1ab0efee94..ebbde0ade1 100644 --- a/src/core/stdpopup/src/stdafx.cxx +++ b/src/core/stdpopup/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 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 . -*/ - +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + #include "stdafx.h" \ No newline at end of file diff --git a/src/core/stdpopup/src/version.h b/src/core/stdpopup/src/version.h index 2ffc660f71..16d5b005cd 100644 --- a/src/core/stdpopup/src/version.h +++ b/src/core/stdpopup/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for popups." #define __AUTHOR "Scott Ellis, Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdPopup" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stduihist/src/history.cpp b/src/core/stduihist/src/history.cpp index 56c3aba8a5..ae92875fab 100644 --- a/src/core/stduihist/src/history.cpp +++ b/src/core/stduihist/src/history.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduihist/src/main.cpp b/src/core/stduihist/src/main.cpp index 1911bd9792..1be11d7ac3 100644 --- a/src/core/stduihist/src/main.cpp +++ b/src/core/stduihist/src/main.cpp @@ -2,7 +2,7 @@ Standard ugly history viewer for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stduihist/src/stdafx.h b/src/core/stduihist/src/stdafx.h index 9817742ad3..aae29d451d 100644 --- a/src/core/stduihist/src/stdafx.h +++ b/src/core/stduihist/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduihist/src/version.h b/src/core/stduihist/src/version.h index 62eda51681..ee7cceff2a 100644 --- a/src/core/stduihist/src/version.h +++ b/src/core/stduihist/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for built-in history viewer." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdUIHist" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stduserinfo/src/contactinfo.cpp b/src/core/stduserinfo/src/contactinfo.cpp index a19783e4df..7adec4c09b 100644 --- a/src/core/stduserinfo/src/contactinfo.cpp +++ b/src/core/stduserinfo/src/contactinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduserinfo/src/main.cpp b/src/core/stduserinfo/src/main.cpp index 5334c9727f..9bd9b6f7be 100644 --- a/src/core/stduserinfo/src/main.cpp +++ b/src/core/stduserinfo/src/main.cpp @@ -2,7 +2,7 @@ Standard User Info plugin for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stduserinfo/src/stdafx.h b/src/core/stduserinfo/src/stdafx.h index 76b456e716..ca5412f200 100644 --- a/src/core/stduserinfo/src/stdafx.h +++ b/src/core/stduserinfo/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduserinfo/src/stdinfo.cpp b/src/core/stduserinfo/src/stdinfo.cpp index 5acec2d245..ca3b2edd20 100644 --- a/src/core/stduserinfo/src/stdinfo.cpp +++ b/src/core/stduserinfo/src/stdinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduserinfo/src/userinfo.cpp b/src/core/stduserinfo/src/userinfo.cpp index 9448d5c96f..30792d9244 100644 --- a/src/core/stduserinfo/src/userinfo.cpp +++ b/src/core/stduserinfo/src/userinfo.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduserinfo/src/utils.h b/src/core/stduserinfo/src/utils.h index cb766c47ae..9ceb33b61d 100644 --- a/src/core/stduserinfo/src/utils.h +++ b/src/core/stduserinfo/src/utils.h @@ -1,214 +1,214 @@ -/* - -Standard User Info plugin for Miranda NG - -Copyright (C) 2012-22 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; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#define SVS_NORMAL 0 -#define SVS_GENDER 1 -#define SVS_ZEROISUNSPEC 2 -#define SVS_IP 3 -#define SVS_COUNTRY 4 -#define SVS_MONTH 5 -#define SVS_SIGNED 6 -#define SVS_TIMEZONE 7 -#define SVS_MARITAL 8 - -///////////////////////////////////////////////////////////////////////////////////////// - -struct DataItem -{ - const char *szSetting; - int idCtrl, special; -}; - -template -bool IsEmptyValue(MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) -{ - if (szModule == nullptr) - szModule = Proto_GetBaseAccountName(hContact); - - DBVARIANT dbv; - for (auto &it : buffer) { - if (db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0) - continue; - - db_free(&dbv); - return false; - } - - return true; -} - -template -void SetValue(HWND hwndDlg, MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) -{ - if (szModule == nullptr) - szModule = Proto_GetBaseAccountName(hContact); - - for (auto &it : buffer) { - char *pstr = nullptr; - wchar_t *pwstr = nullptr, wstr[80]; - - DBVARIANT dbv = { DBVT_DELETED }; - - bool unspecified; - if (szModule == nullptr) - unspecified = true; - else - unspecified = db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0; - - if (!unspecified) { - switch (dbv.type) { - case DBVT_BYTE: - if (it.special == SVS_GENDER) { - if (dbv.cVal == 'M') pwstr = TranslateT("Male"); - else if (dbv.cVal == 'F') pwstr = TranslateT("Female"); - else unspecified = 1; - } - else if (it.special == SVS_MONTH) { - if (dbv.bVal > 0 && dbv.bVal <= 12) { - pwstr = wstr; - GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, wstr, _countof(wstr)); - } - else unspecified = 1; - } - else if (it.special == SVS_TIMEZONE) { - if (dbv.cVal == -100) unspecified = 1; - else { - pwstr = wstr; - mir_snwprintf(wstr, dbv.cVal ? L"UTC%+d:%02d" : L"UTC", -dbv.cVal / 2, (dbv.cVal & 1) * 30); - } - } - else if (it.special == SVS_MARITAL) { - switch (dbv.cVal) { - case 0: - pwstr = TranslateT(""); - break; - case 10: - pwstr = TranslateT("Single"); - break; - case 11: - pwstr = TranslateT("Close relationships"); - break; - case 12: - pwstr = TranslateT("Engaged"); - break; - case 20: - pwstr = TranslateT("Married"); - break; - case 30: - pwstr = TranslateT("Divorced"); - break; - case 31: - pwstr = TranslateT("Separated"); - break; - case 40: - pwstr = TranslateT("Widowed"); - break; - case 50: - pwstr = TranslateT("Actively searching"); - break; - case 60: - pwstr = TranslateT("In love"); - break; - case 70: - pwstr = TranslateT("It's complicated"); - break; - case 80: - pwstr = TranslateT("In a civil union"); - break; - default: - unspecified = 1; - } - } - else { - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.bVal == 0); - pwstr = _itow(it.special == SVS_SIGNED ? dbv.cVal : dbv.bVal, wstr, 10); - } - break; - - case DBVT_WORD: - if (it.special == SVS_COUNTRY) { - uint16_t wSave = dbv.wVal; - if (wSave == (uint16_t)-1) { - char szSettingName[100]; - mir_snprintf(szSettingName, "%sName", it.szSetting); - if (!db_get_ws(hContact, szModule, szSettingName, &dbv)) { - pwstr = dbv.pwszVal; - unspecified = false; - break; - } - } - - pwstr = TranslateW(_A2T((char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, wSave, 0))); - unspecified = pwstr == nullptr; - } - else { - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.wVal == 0); - pwstr = _itow(it.special == SVS_SIGNED ? dbv.sVal : dbv.wVal, wstr, 10); - } - break; - - case DBVT_DWORD: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.dVal == 0); - if (it.special == SVS_IP) { - struct in_addr ia; - ia.S_un.S_addr = htonl(dbv.dVal); - mir_wstrncpy(wstr, _A2T(inet_ntoa(ia)), _countof(wstr)); - pwstr = wstr; - if (dbv.dVal == 0) - unspecified = 1; - } - else pwstr = _itow(it.special == SVS_SIGNED ? dbv.lVal : dbv.dVal, wstr, 10); - break; - - case DBVT_ASCIIZ: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - pstr = dbv.pszVal; - break; - - case DBVT_UTF8: - unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); - if (!unspecified) { - SetDlgItemTextW(hwndDlg, it.idCtrl, TranslateW(ptrW(mir_utf8decodeW(dbv.pszVal)))); - goto LBL_Exit; - } - - mir_utf8decode(dbv.pszVal, &pwstr); - break; - - default: - pwstr = wstr; - mir_wstrcpy(wstr, L"???"); - break; - } - } - - if (unspecified) - SetDlgItemText(hwndDlg, it.idCtrl, TranslateT("")); - else if (pwstr != nullptr) - SetDlgItemText(hwndDlg, it.idCtrl, pwstr); - else - SetDlgItemTextA(hwndDlg, it.idCtrl, pstr); - -LBL_Exit: - EnableWindow(GetDlgItem(hwndDlg, it.idCtrl), !unspecified); - db_free(&dbv); - } -} +/* + +Standard User Info plugin for Miranda NG + +Copyright (C) 2012-23 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; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#define SVS_NORMAL 0 +#define SVS_GENDER 1 +#define SVS_ZEROISUNSPEC 2 +#define SVS_IP 3 +#define SVS_COUNTRY 4 +#define SVS_MONTH 5 +#define SVS_SIGNED 6 +#define SVS_TIMEZONE 7 +#define SVS_MARITAL 8 + +///////////////////////////////////////////////////////////////////////////////////////// + +struct DataItem +{ + const char *szSetting; + int idCtrl, special; +}; + +template +bool IsEmptyValue(MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) +{ + if (szModule == nullptr) + szModule = Proto_GetBaseAccountName(hContact); + + DBVARIANT dbv; + for (auto &it : buffer) { + if (db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0) + continue; + + db_free(&dbv); + return false; + } + + return true; +} + +template +void SetValue(HWND hwndDlg, MCONTACT hContact, DataItem(&buffer)[_Size], const char *szModule = nullptr) +{ + if (szModule == nullptr) + szModule = Proto_GetBaseAccountName(hContact); + + for (auto &it : buffer) { + char *pstr = nullptr; + wchar_t *pwstr = nullptr, wstr[80]; + + DBVARIANT dbv = { DBVT_DELETED }; + + bool unspecified; + if (szModule == nullptr) + unspecified = true; + else + unspecified = db_get_s(hContact, szModule, it.szSetting, &dbv, 0) != 0; + + if (!unspecified) { + switch (dbv.type) { + case DBVT_BYTE: + if (it.special == SVS_GENDER) { + if (dbv.cVal == 'M') pwstr = TranslateT("Male"); + else if (dbv.cVal == 'F') pwstr = TranslateT("Female"); + else unspecified = 1; + } + else if (it.special == SVS_MONTH) { + if (dbv.bVal > 0 && dbv.bVal <= 12) { + pwstr = wstr; + GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, wstr, _countof(wstr)); + } + else unspecified = 1; + } + else if (it.special == SVS_TIMEZONE) { + if (dbv.cVal == -100) unspecified = 1; + else { + pwstr = wstr; + mir_snwprintf(wstr, dbv.cVal ? L"UTC%+d:%02d" : L"UTC", -dbv.cVal / 2, (dbv.cVal & 1) * 30); + } + } + else if (it.special == SVS_MARITAL) { + switch (dbv.cVal) { + case 0: + pwstr = TranslateT(""); + break; + case 10: + pwstr = TranslateT("Single"); + break; + case 11: + pwstr = TranslateT("Close relationships"); + break; + case 12: + pwstr = TranslateT("Engaged"); + break; + case 20: + pwstr = TranslateT("Married"); + break; + case 30: + pwstr = TranslateT("Divorced"); + break; + case 31: + pwstr = TranslateT("Separated"); + break; + case 40: + pwstr = TranslateT("Widowed"); + break; + case 50: + pwstr = TranslateT("Actively searching"); + break; + case 60: + pwstr = TranslateT("In love"); + break; + case 70: + pwstr = TranslateT("It's complicated"); + break; + case 80: + pwstr = TranslateT("In a civil union"); + break; + default: + unspecified = 1; + } + } + else { + unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.bVal == 0); + pwstr = _itow(it.special == SVS_SIGNED ? dbv.cVal : dbv.bVal, wstr, 10); + } + break; + + case DBVT_WORD: + if (it.special == SVS_COUNTRY) { + uint16_t wSave = dbv.wVal; + if (wSave == (uint16_t)-1) { + char szSettingName[100]; + mir_snprintf(szSettingName, "%sName", it.szSetting); + if (!db_get_ws(hContact, szModule, szSettingName, &dbv)) { + pwstr = dbv.pwszVal; + unspecified = false; + break; + } + } + + pwstr = TranslateW(_A2T((char *)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, wSave, 0))); + unspecified = pwstr == nullptr; + } + else { + unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.wVal == 0); + pwstr = _itow(it.special == SVS_SIGNED ? dbv.sVal : dbv.wVal, wstr, 10); + } + break; + + case DBVT_DWORD: + unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.dVal == 0); + if (it.special == SVS_IP) { + struct in_addr ia; + ia.S_un.S_addr = htonl(dbv.dVal); + mir_wstrncpy(wstr, _A2T(inet_ntoa(ia)), _countof(wstr)); + pwstr = wstr; + if (dbv.dVal == 0) + unspecified = 1; + } + else pwstr = _itow(it.special == SVS_SIGNED ? dbv.lVal : dbv.dVal, wstr, 10); + break; + + case DBVT_ASCIIZ: + unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); + pstr = dbv.pszVal; + break; + + case DBVT_UTF8: + unspecified = (it.special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0'); + if (!unspecified) { + SetDlgItemTextW(hwndDlg, it.idCtrl, TranslateW(ptrW(mir_utf8decodeW(dbv.pszVal)))); + goto LBL_Exit; + } + + mir_utf8decode(dbv.pszVal, &pwstr); + break; + + default: + pwstr = wstr; + mir_wstrcpy(wstr, L"???"); + break; + } + } + + if (unspecified) + SetDlgItemText(hwndDlg, it.idCtrl, TranslateT("")); + else if (pwstr != nullptr) + SetDlgItemText(hwndDlg, it.idCtrl, pwstr); + else + SetDlgItemTextA(hwndDlg, it.idCtrl, pstr); + +LBL_Exit: + EnableWindow(GetDlgItem(hwndDlg, it.idCtrl), !unspecified); + db_free(&dbv); + } +} diff --git a/src/core/stduserinfo/src/version.h b/src/core/stduserinfo/src/version.h index 1a68b614db..441fd0cdef 100644 --- a/src/core/stduserinfo/src/version.h +++ b/src/core/stduserinfo/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for providing user information." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdUserInfo" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/core/stduseronline/src/main.cpp b/src/core/stduseronline/src/main.cpp index 255a954e35..da3e0c5cd8 100644 --- a/src/core/stduseronline/src/main.cpp +++ b/src/core/stduseronline/src/main.cpp @@ -2,7 +2,7 @@ Standard user online monitor for Miranda NG -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/core/stduseronline/src/stdafx.h b/src/core/stduseronline/src/stdafx.h index a34a26fd31..1f069e243c 100644 --- a/src/core/stduseronline/src/stdafx.h +++ b/src/core/stduseronline/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduseronline/src/useronline.cpp b/src/core/stduseronline/src/useronline.cpp index 3b1520a0a6..be50bf3165 100644 --- a/src/core/stduseronline/src/useronline.cpp +++ b/src/core/stduseronline/src/useronline.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/core/stduseronline/src/version.h b/src/core/stduseronline/src/version.h index 3edec197fb..ec2b47a6f8 100644 --- a/src/core/stduseronline/src/version.h +++ b/src/core/stduseronline/src/version.h @@ -8,4 +8,4 @@ #define __DESCRIPTION "Core module for user-is-online event processing." #define __AUTHOR "Miranda NG team" #define __AUTHORWEB "https://miranda-ng.org/p/StdUserOnline" -#define __COPYRIGHT "© 2012-22 Miranda NG team" +#define __COPYRIGHT "© 2012-23 Miranda NG team" diff --git a/src/mir_app/src/CMPluginBase.cpp b/src/mir_app/src/CMPluginBase.cpp index bc768335a2..92c444e46c 100644 --- a/src/mir_app/src/CMPluginBase.cpp +++ b/src/mir_app/src/CMPluginBase.cpp @@ -1,343 +1,343 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" -#include "IcoLib.h" - -static int sttFakeID = -100; - -static int Compare(const CMPluginBase *p1, const CMPluginBase *p2) -{ - return INT_PTR(p1->getInst()) - INT_PTR(p2->getInst()); -} - -static LIST g_arPlugins(10, Compare); - -void RegisterModule(CMPluginBase *pPlugin) -{ - g_arPlugins.insert(pPlugin); -} - -MIR_APP_DLL(HINSTANCE) GetInstByAddress(void *codePtr) -{ - if (g_arPlugins.getCount() == 0) - return nullptr; - - int idx; - void *boo[2] = { 0, codePtr }; - List_GetIndex((SortedList*)&g_arPlugins, (CMPluginBase*)&boo, &idx); - if (idx > 0) - idx--; - - HINSTANCE result = g_arPlugins[idx]->getInst(); - if (result < g_plugin.getInst() && codePtr > g_plugin.getInst()) - return g_plugin.getInst(); - - if (idx == 0 && codePtr < (void*)result) - return nullptr; - - return result; -} - -MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int _hLang) -{ - if (uuid == miid_last) - return --sttFakeID; - - for (auto &it : g_arPlugins) - if (it->getInfo().uuid == uuid) - return (_hLang) ? _hLang : --sttFakeID; - - return 0; -} - -MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid) -{ - for (auto &it : g_arPlugins) - if (it->getInfo().uuid == uuid) - return it->getInst() != nullptr; - - return false; -} - -const char* GetPluginNameByInstance(HINSTANCE hInst) -{ - HINSTANCE boo[2] = { 0, hInst }; - CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); - return (pPlugin == nullptr) ? nullptr : pPlugin->getInfo().shortName; -} - -MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst) -{ - HINSTANCE boo[2] = { 0, hInst }; - CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); - return (pPlugin == nullptr) ? g_plugin : *pPlugin; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// stubs for pascal plugins - -static void wipePluginData(CMPluginBase *pPlugin) -{ - if (Miranda_IsTerminated()) - return; - - KillModuleMenus(pPlugin); - KillModuleFonts(pPlugin); - KillModuleIcons(pPlugin); - KillModuleHotkeys(pPlugin); - KillModulePopups(pPlugin); - KillModuleSounds(pPlugin); - KillModuleExtraIcons(pPlugin); - KillModuleSrmmIcons(pPlugin); - KillModuleToolbarIcons(pPlugin); - KillModuleOptions(pPlugin); -} - -// emulates the call of CMPluginBase::CMPluginBase for Pascal plugins -EXTERN_C MIR_APP_DLL(void) RegisterPlugin(CMPluginBase *pPlugin) -{ - if (pPlugin->getInst() != nullptr) - g_arPlugins.insert(pPlugin); -} - -// emulates the call of CMPluginBase::~CMPluginBase for Pascal plugins -EXTERN_C MIR_APP_DLL(void) UnregisterPlugin(CMPluginBase *pPlugin) -{ - wipePluginData(pPlugin); - g_arPlugins.remove(pPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CompareIcons(const IcolibItem *p1, const IcolibItem *p2) -{ - return p1->default_indx - p2->default_indx; -} - -CMPluginBase::CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo) : - m_szModuleName(moduleName), - m_pInfo(pInfo), - m_arIcons(10, CompareIcons) -{ - if (m_hInst != nullptr) - g_arPlugins.insert(this); -} - -CMPluginBase::~CMPluginBase() -{ - wipePluginData(this); - - if (m_hLogger) { - mir_closeLog(m_hLogger); - m_hLogger = nullptr; - } - - g_arPlugins.remove(this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::Load() -{ - return 0; -} - -int CMPluginBase::Unload() -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::tryOpenLog() -{ - wchar_t path[MAX_PATH]; - mir_snwprintf(path, L"%s\\%S.txt", VARSW(L"%miranda_logpath%").get(), m_szModuleName); - m_hLogger = mir_createLog(m_szModuleName, nullptr, path, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addOptions(WPARAM wParam, OPTIONSDIALOGPAGE *odp) -{ - return ::Options_AddPage(wParam, odp, this); -} - -void CMPluginBase::openOptions(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) -{ - ::Options_Open(pszGroup, pszPage, pszTab, this); -} - -void CMPluginBase::openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) -{ - ::Options_OpenPage(pszGroup, pszPage, pszTab, this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addFont(FontID *pFont) -{ - return Font_Register(pFont, this); -} - -int CMPluginBase::addFont(FontIDW *pFont) -{ - return Font_RegisterW(pFont, this); -} - -int CMPluginBase::addColor(ColourID *pColor) -{ - return Colour_Register(pColor, this); -} - -int CMPluginBase::addColor(ColourIDW *pColor) -{ - return Colour_RegisterW(pColor, this); -} - -int CMPluginBase::addEffect(EffectID *pEffect) -{ - return Effect_Register(pEffect, this); -} - -int CMPluginBase::addEffect(EffectIDW *pEffect) -{ - return Effect_RegisterW(pEffect, this); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addFrame(const CLISTFrame *F) -{ - return (int)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)F, (LPARAM)this); -} - -int CMPluginBase::addHotkey(const HOTKEYDESC *hk) -{ - return Hotkey_Register(hk, this); -} - -HANDLE CMPluginBase::addIcon(const SKINICONDESC *sid) -{ - return IcoLib_AddIcon(sid, this); -} - -HGENMENU CMPluginBase::addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib) -{ - return Menu_CreateRoot(hMenuObject, ptszName, position, hIcoLib, this); -} - -HANDLE CMPluginBase::addTTB(const struct TTBButton *pButton) -{ - return (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)pButton, (LPARAM)this); -} - -int CMPluginBase::addUserInfo(WPARAM wParam, USERINFOPAGE *uip) -{ - uip->pPlugin = this; - return CallService("UserInfo/AddPage", wParam, (LPARAM)uip); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::debugLogA(LPCSTR szFormat, ...) -{ - if (m_hLogger == nullptr) - tryOpenLog(); - - va_list args; - va_start(args, szFormat); - mir_writeLogVA(m_hLogger, szFormat, args); - va_end(args); -} - -void CMPluginBase::debugLogW(LPCWSTR wszFormat, ...) -{ - if (m_hLogger == nullptr) - tryOpenLog(); - - va_list args; - va_start(args, wszFormat); - mir_writeLogVW(m_hLogger, wszFormat, args); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#ifdef _WINDOWS -int CMPluginBase::addImgListIcon(HIMAGELIST himl, int iconId) -{ - HICON hIcon = getIcon(iconId); - int ret = ::ImageList_AddIcon(himl, hIcon); - IcoLib_ReleaseIcon(hIcon); - return ret; -} -#endif - -HICON CMPluginBase::getIcon(int iconId, bool big) -{ - return IcoLib_GetIconByHandle(getIconHandle(iconId), big); -} - -HANDLE CMPluginBase::getIconHandle(int iconId) -{ - IcolibItem *p = (IcolibItem*)alloca(sizeof(IcolibItem)); - p->default_indx = -iconId; - return m_arIcons.find(p); -} - -void CMPluginBase::releaseIcon(int iconId, bool big) -{ - IcoLib_ReleaseIcon(getIcon(iconId), big); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CMPluginBase::RegisterProtocol(int type, pfnInitProto fnInit, pfnUninitProto fnUninit) -{ - if (isPluginBanned(m_pInfo.uuid)) - return; - - if (type == PROTOTYPE_PROTOCOL && fnInit != nullptr) - type = PROTOTYPE_PROTOWITHACCS; - - MBaseProto *pd = (MBaseProto*)Proto_RegisterModule(type, m_szModuleName); - if (pd) { - pd->fnInit = fnInit; - pd->fnUninit = fnUninit; - pd->hInst = m_hInst; - } -} - -void CMPluginBase::SetUniqueId(const char *pszUniqueId) -{ - if (pszUniqueId == nullptr) - return; - - MBaseProto *pd = g_arProtos.find((MBaseProto*)&m_szModuleName); - if (pd != nullptr) - pd->szUniqueId = mir_strdup(pszUniqueId); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "plugins.h" +#include "IcoLib.h" + +static int sttFakeID = -100; + +static int Compare(const CMPluginBase *p1, const CMPluginBase *p2) +{ + return INT_PTR(p1->getInst()) - INT_PTR(p2->getInst()); +} + +static LIST g_arPlugins(10, Compare); + +void RegisterModule(CMPluginBase *pPlugin) +{ + g_arPlugins.insert(pPlugin); +} + +MIR_APP_DLL(HINSTANCE) GetInstByAddress(void *codePtr) +{ + if (g_arPlugins.getCount() == 0) + return nullptr; + + int idx; + void *boo[2] = { 0, codePtr }; + List_GetIndex((SortedList*)&g_arPlugins, (CMPluginBase*)&boo, &idx); + if (idx > 0) + idx--; + + HINSTANCE result = g_arPlugins[idx]->getInst(); + if (result < g_plugin.getInst() && codePtr > g_plugin.getInst()) + return g_plugin.getInst(); + + if (idx == 0 && codePtr < (void*)result) + return nullptr; + + return result; +} + +MIR_APP_DLL(int) GetPluginLangId(const MUUID &uuid, int _hLang) +{ + if (uuid == miid_last) + return --sttFakeID; + + for (auto &it : g_arPlugins) + if (it->getInfo().uuid == uuid) + return (_hLang) ? _hLang : --sttFakeID; + + return 0; +} + +MIR_APP_DLL(int) IsPluginLoaded(const MUUID &uuid) +{ + for (auto &it : g_arPlugins) + if (it->getInfo().uuid == uuid) + return it->getInst() != nullptr; + + return false; +} + +const char* GetPluginNameByInstance(HINSTANCE hInst) +{ + HINSTANCE boo[2] = { 0, hInst }; + CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); + return (pPlugin == nullptr) ? nullptr : pPlugin->getInfo().shortName; +} + +MIR_APP_DLL(CMPluginBase&) GetPluginByInstance(HINSTANCE hInst) +{ + HINSTANCE boo[2] = { 0, hInst }; + CMPluginBase *pPlugin = g_arPlugins.find((CMPluginBase*)&boo); + return (pPlugin == nullptr) ? g_plugin : *pPlugin; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// stubs for pascal plugins + +static void wipePluginData(CMPluginBase *pPlugin) +{ + if (Miranda_IsTerminated()) + return; + + KillModuleMenus(pPlugin); + KillModuleFonts(pPlugin); + KillModuleIcons(pPlugin); + KillModuleHotkeys(pPlugin); + KillModulePopups(pPlugin); + KillModuleSounds(pPlugin); + KillModuleExtraIcons(pPlugin); + KillModuleSrmmIcons(pPlugin); + KillModuleToolbarIcons(pPlugin); + KillModuleOptions(pPlugin); +} + +// emulates the call of CMPluginBase::CMPluginBase for Pascal plugins +EXTERN_C MIR_APP_DLL(void) RegisterPlugin(CMPluginBase *pPlugin) +{ + if (pPlugin->getInst() != nullptr) + g_arPlugins.insert(pPlugin); +} + +// emulates the call of CMPluginBase::~CMPluginBase for Pascal plugins +EXTERN_C MIR_APP_DLL(void) UnregisterPlugin(CMPluginBase *pPlugin) +{ + wipePluginData(pPlugin); + g_arPlugins.remove(pPlugin); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int CompareIcons(const IcolibItem *p1, const IcolibItem *p2) +{ + return p1->default_indx - p2->default_indx; +} + +CMPluginBase::CMPluginBase(const char *moduleName, const PLUGININFOEX &pInfo) : + m_szModuleName(moduleName), + m_pInfo(pInfo), + m_arIcons(10, CompareIcons) +{ + if (m_hInst != nullptr) + g_arPlugins.insert(this); +} + +CMPluginBase::~CMPluginBase() +{ + wipePluginData(this); + + if (m_hLogger) { + mir_closeLog(m_hLogger); + m_hLogger = nullptr; + } + + g_arPlugins.remove(this); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPluginBase::Load() +{ + return 0; +} + +int CMPluginBase::Unload() +{ + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CMPluginBase::tryOpenLog() +{ + wchar_t path[MAX_PATH]; + mir_snwprintf(path, L"%s\\%S.txt", VARSW(L"%miranda_logpath%").get(), m_szModuleName); + m_hLogger = mir_createLog(m_szModuleName, nullptr, path, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPluginBase::addOptions(WPARAM wParam, OPTIONSDIALOGPAGE *odp) +{ + return ::Options_AddPage(wParam, odp, this); +} + +void CMPluginBase::openOptions(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) +{ + ::Options_Open(pszGroup, pszPage, pszTab, this); +} + +void CMPluginBase::openOptionsPage(const wchar_t *pszGroup, const wchar_t *pszPage, const wchar_t *pszTab) +{ + ::Options_OpenPage(pszGroup, pszPage, pszTab, this); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPluginBase::addFont(FontID *pFont) +{ + return Font_Register(pFont, this); +} + +int CMPluginBase::addFont(FontIDW *pFont) +{ + return Font_RegisterW(pFont, this); +} + +int CMPluginBase::addColor(ColourID *pColor) +{ + return Colour_Register(pColor, this); +} + +int CMPluginBase::addColor(ColourIDW *pColor) +{ + return Colour_RegisterW(pColor, this); +} + +int CMPluginBase::addEffect(EffectID *pEffect) +{ + return Effect_Register(pEffect, this); +} + +int CMPluginBase::addEffect(EffectIDW *pEffect) +{ + return Effect_RegisterW(pEffect, this); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPluginBase::addFrame(const CLISTFrame *F) +{ + return (int)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)F, (LPARAM)this); +} + +int CMPluginBase::addHotkey(const HOTKEYDESC *hk) +{ + return Hotkey_Register(hk, this); +} + +HANDLE CMPluginBase::addIcon(const SKINICONDESC *sid) +{ + return IcoLib_AddIcon(sid, this); +} + +HGENMENU CMPluginBase::addRootMenu(int hMenuObject, LPCWSTR ptszName, int position, HANDLE hIcoLib) +{ + return Menu_CreateRoot(hMenuObject, ptszName, position, hIcoLib, this); +} + +HANDLE CMPluginBase::addTTB(const struct TTBButton *pButton) +{ + return (HANDLE)CallService(MS_TTB_ADDBUTTON, (WPARAM)pButton, (LPARAM)this); +} + +int CMPluginBase::addUserInfo(WPARAM wParam, USERINFOPAGE *uip) +{ + uip->pPlugin = this; + return CallService("UserInfo/AddPage", wParam, (LPARAM)uip); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CMPluginBase::debugLogA(LPCSTR szFormat, ...) +{ + if (m_hLogger == nullptr) + tryOpenLog(); + + va_list args; + va_start(args, szFormat); + mir_writeLogVA(m_hLogger, szFormat, args); + va_end(args); +} + +void CMPluginBase::debugLogW(LPCWSTR wszFormat, ...) +{ + if (m_hLogger == nullptr) + tryOpenLog(); + + va_list args; + va_start(args, wszFormat); + mir_writeLogVW(m_hLogger, wszFormat, args); + va_end(args); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef _WINDOWS +int CMPluginBase::addImgListIcon(HIMAGELIST himl, int iconId) +{ + HICON hIcon = getIcon(iconId); + int ret = ::ImageList_AddIcon(himl, hIcon); + IcoLib_ReleaseIcon(hIcon); + return ret; +} +#endif + +HICON CMPluginBase::getIcon(int iconId, bool big) +{ + return IcoLib_GetIconByHandle(getIconHandle(iconId), big); +} + +HANDLE CMPluginBase::getIconHandle(int iconId) +{ + IcolibItem *p = (IcolibItem*)alloca(sizeof(IcolibItem)); + p->default_indx = -iconId; + return m_arIcons.find(p); +} + +void CMPluginBase::releaseIcon(int iconId, bool big) +{ + IcoLib_ReleaseIcon(getIcon(iconId), big); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CMPluginBase::RegisterProtocol(int type, pfnInitProto fnInit, pfnUninitProto fnUninit) +{ + if (isPluginBanned(m_pInfo.uuid)) + return; + + if (type == PROTOTYPE_PROTOCOL && fnInit != nullptr) + type = PROTOTYPE_PROTOWITHACCS; + + MBaseProto *pd = (MBaseProto*)Proto_RegisterModule(type, m_szModuleName); + if (pd) { + pd->fnInit = fnInit; + pd->fnUninit = fnUninit; + pd->hInst = m_hInst; + } +} + +void CMPluginBase::SetUniqueId(const char *pszUniqueId) +{ + if (pszUniqueId == nullptr) + return; + + MBaseProto *pd = g_arProtos.find((MBaseProto*)&m_szModuleName); + if (pd != nullptr) + pd->szUniqueId = mir_strdup(pszUniqueId); +} diff --git a/src/mir_app/src/Docking.cpp b/src/mir_app/src/Docking.cpp index 9ea5cd9f58..c8dad68b95 100644 --- a/src/mir_app/src/Docking.cpp +++ b/src/mir_app/src/Docking.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/FontOptions.cpp b/src/mir_app/src/FontOptions.cpp index 071da22914..5a8fd4aff8 100644 --- a/src/mir_app/src/FontOptions.cpp +++ b/src/mir_app/src/FontOptions.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/FontService.cpp b/src/mir_app/src/FontService.cpp index 2f4f300ff2..27f16e2ce2 100644 --- a/src/mir_app/src/FontService.cpp +++ b/src/mir_app/src/FontService.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/FontService.h b/src/mir_app/src/FontService.h index e86799ab35..2e562bd185 100644 --- a/src/mir_app/src/FontService.h +++ b/src/mir_app/src/FontService.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/IcoLib.h b/src/mir_app/src/IcoLib.h index 385d1acfc4..78dffe4cf8 100644 --- a/src/mir_app/src/IcoLib.h +++ b/src/mir_app/src/IcoLib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/MDatabaseCommon.cpp b/src/mir_app/src/MDatabaseCommon.cpp index 033cbea2b9..323954a516 100644 --- a/src/mir_app/src/MDatabaseCommon.cpp +++ b/src/mir_app/src/MDatabaseCommon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/MDatabaseCommonCrypt.cpp b/src/mir_app/src/MDatabaseCommonCrypt.cpp index bbfd18f399..88ec00a1cd 100644 --- a/src/mir_app/src/MDatabaseCommonCrypt.cpp +++ b/src/mir_app/src/MDatabaseCommonCrypt.cpp @@ -1,449 +1,449 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "database.h" -#include "encrypt.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Provider selection dialog - -class CSelectCryptoDialog : public CDlgBase -{ - CCtrlCombo m_combo; - CCtrlData m_descr; - CRYPTO_PROVIDER *m_selected = nullptr; - - CRYPTO_PROVIDER *getCurrent() - { - return (CRYPTO_PROVIDER*)m_combo.GetCurData(); - } - - bool OnInitDialog() override - { - for (auto &p : arCryptoProviders) - m_combo.AddStringA(p->pszName, LPARAM(p)); - - m_combo.SetCurSel(0); - m_descr.SetText(arCryptoProviders[0].szDescr.w); - return true; - } - - bool OnApply() override - { - m_selected = getCurrent(); - return true; - } - - void OnComboChanged(CCtrlCombo*) - { - m_descr.SetText(getCurrent()->szDescr.w); - } - -public: - CSelectCryptoDialog() : - CDlgBase(g_plugin, IDD_SELECT_CRYPTOPROVIDER), - m_combo(this, IDC_SELECTCRYPT_COMBO), - m_descr(this, IDC_CRYPTOPROVIDER_DESCR) - { - m_combo.OnChange = Callback(this, &CSelectCryptoDialog::OnComboChanged); - } - - inline CRYPTO_PROVIDER* GetSelected() - { return m_selected; - } -}; - -CRYPTO_PROVIDER* MDatabaseCommon::SelectProvider() -{ - if (arCryptoProviders.getCount() == 0) - return nullptr; - - CRYPTO_PROVIDER *pProv; - if (arCryptoProviders.getCount() > 1) { - CSelectCryptoDialog dlg; - dlg.DoModal(); - pProv = dlg.GetSelected(); - } - else pProv = &arCryptoProviders[0]; - - return (StoreProvider(pProv)) ? pProv : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseCommon::IsSettingEncrypted(LPCSTR szModule, LPCSTR szSetting) -{ - if (!_strnicmp(szSetting, "password", 8)) return true; - if (!mir_strcmp(szSetting, "NLProxyAuthPassword")) return true; - if (!mir_strcmp(szSetting, "LNPassword")) return true; - if (!mir_strcmp(szSetting, "FileProxyPassword")) return true; - if (!mir_strcmp(szSetting, "TokenSecret")) return true; - - if (!mir_strcmp(szModule, "SecureIM")) { - if (!mir_strcmp(szSetting, "pgp")) return true; - if (!mir_strcmp(szSetting, "pgpPrivKey")) return true; - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HGENMENU hSetPwdMenu; - -__forceinline wchar_t *GetMenuTitle(bool bUsesPassword) -{ - return bUsesPassword ? LPGENW("Change/remove password") : LPGENW("Set password"); -} - -void MDatabaseCommon::SetPassword(const wchar_t *ptszPassword) -{ - if (mir_wstrlen(ptszPassword) == 0) { - m_bUsesPassword = false; - m_crypto->setPassword(nullptr); - } - else { - m_bUsesPassword = true; - m_crypto->setPassword(pass_ptrA(mir_utf8encodeW(ptszPassword))); - } - - Menu_ModifyItem(hSetPwdMenu, GetMenuTitle(m_bUsesPassword), Skin_GetIconHandle(SKINICON_OTHER_KEYS)); -} - -static UINT oldLangID; -void LanguageChanged(HWND hwndDlg) -{ - UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); - char Lang[3] = { 0 }; - if (LangID != oldLangID) { - oldLangID = LangID; - GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); - Lang[0] = toupper(Lang[0]); - Lang[1] = tolower(Lang[1]); - SetDlgItemTextA(hwndDlg, IDC_LANG, Lang); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool CheckOldPassword(HWND hwndDlg, MDatabaseCommon *db) -{ - if (db->usesPassword()) { - wchar_t buf[100]; - GetDlgItemText(hwndDlg, IDC_OLDPASS, buf, _countof(buf)); - pass_ptrA oldPass(mir_utf8encodeW(buf)); - if (!db->getCrypt()->checkPassword(oldPass)) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Wrong old password entered!")); - return false; - } - } - return true; -} - -static INT_PTR CALLBACK sttChangePassword(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MDatabaseCommon *db = (MDatabaseCommon *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (uMsg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); - - oldLangID = 0; - SetTimer(hwndDlg, 1, 200, nullptr); - LanguageChanged(hwndDlg); - return TRUE; - - case WM_CTLCOLORSTATIC: - if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_LANG)) { - SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); - } - return FALSE; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - EndDialog(hwndDlg, IDCANCEL); - break; - - case IDREMOVE: - if (!CheckOldPassword(hwndDlg, db)) { -LBL_Error: - SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_NCPAINT, 0, 0); - SetDlgItemTextA(hwndDlg, IDC_USERPASS1, ""); - SetDlgItemTextA(hwndDlg, IDC_USERPASS2, ""); - } - else { - db->SetPassword(nullptr); - db->StoreCryptoKey(); - EndDialog(hwndDlg, IDREMOVE); - } - break; - - case IDOK: - wchar_t buf2[100]; - GetDlgItemText(hwndDlg, IDC_USERPASS1, buf2, _countof(buf2)); - if (wcslen(buf2) < 3) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Password is too short!")); - goto LBL_Error; - } - - wchar_t buf[100]; - GetDlgItemText(hwndDlg, IDC_USERPASS2, buf, _countof(buf)); - if (wcscmp(buf2, buf)) { - SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Passwords do not match!")); - goto LBL_Error; - } - - if (!CheckOldPassword(hwndDlg, db)) - goto LBL_Error; - - db->SetPassword(buf2); - db->StoreCryptoKey(); - SecureZeroMemory(buf, sizeof(buf)); - SecureZeroMemory(buf2, sizeof(buf2)); - EndDialog(hwndDlg, IDOK); - } - break; - - case WM_TIMER: - LanguageChanged(hwndDlg); - return FALSE; - - case WM_DESTROY: - KillTimer(hwndDlg, 1); - Window_FreeIcon_IcoLib(GetDlgItem(hwndDlg, IDC_HEADERBAR)); - } - - return FALSE; -} - -static INT_PTR ChangePassword(void* obj, WPARAM, LPARAM) -{ - MDatabaseCommon *db = (MDatabaseCommon*)obj; - DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(db->usesPassword() ? IDD_CHANGEPASS : IDD_NEWPASS), nullptr, sttChangePassword, (LPARAM)db); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Options - -class CDatabaseOptionsDialog : public CDlgBase -{ - CCtrlCheck m_chkStandart, m_chkTotal; - CCtrlButton m_btnChangePass; - MDatabaseCommon *m_db; - -public: - CDatabaseOptionsDialog(MDatabaseCommon *db) : - CDlgBase(g_plugin, IDD_OPT_DATABASE), - m_db(db), - m_chkTotal(this, IDC_TOTAL), - m_chkStandart(this, IDC_STANDARD), - m_btnChangePass(this, IDC_USERPASS1) - { - m_btnChangePass.OnClick = Callback(this, &CDatabaseOptionsDialog::onClick_ChangePass); - } - - bool OnInitDialog() override - { - m_chkStandart.SetState(!m_db->isEncrypted()); - m_chkTotal.SetState(m_db->isEncrypted()); - return true; - } - - bool OnApply() override - { - SetCursor(LoadCursor(nullptr, IDC_WAIT)); - m_db->EnableEncryption(m_chkTotal.GetState() != 0); - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - m_chkStandart.SetState(!m_db->isEncrypted()); - m_chkTotal.SetState(m_db->isEncrypted()); - return true; - } - - void onClick_ChangePass(CCtrlButton *) - { - ChangePassword(m_db, 0, 0); - } -}; - -static int OnOptionsInit(PVOID obj, WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = { sizeof(odp) }; - odp.position = -790000000; - odp.flags = ODPF_BOLDGROUPS; - odp.szTitle.a = LPGEN("Database"); - odp.pDialog = new CDatabaseOptionsDialog((MDatabaseCommon*)obj); - g_plugin.addOptions(wParam, &odp); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void InitCryptMenuItem(CMenuItem &mi) -{ - auto *pDb = db_get_current(); - - HookEventObj(ME_OPT_INITIALISE, OnOptionsInit, pDb); - - SET_UID(mi, 0x50321866, 0xba1, 0x46dd, 0xb3, 0xa6, 0xc3, 0xcc, 0x55, 0xf2, 0x42, 0x9e); - mi.flags = CMIF_UNICODE; - mi.position = 1000000001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_KEYS); - mi.name.w = GetMenuTitle(pDb->usesPassword()); - mi.pszService = "DB/UI/ChangePassword"; - hSetPwdMenu = Menu_AddMainMenuItem(&mi); - CreateServiceFunctionObj(mi.pszService, ChangePassword, pDb); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CEnterPasswordDialog : public CDlgBase -{ - friend class MDatabaseCommon; - - CTimer m_timer; - CCtrlData m_header; - CCtrlData m_language; - CCtrlEdit m_passwordEdit; - - int m_wrongPass = 0; - wchar_t m_newPass[100]; - MDatabaseCommon *m_db; - - void OnTimer(CTimer*) - { - UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); - char Lang[3] = { 0 }; - GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); - Lang[0] = toupper(Lang[0]); - Lang[1] = tolower(Lang[1]); - m_language.SetTextA(Lang); - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - if (msg == WM_CTLCOLORSTATIC) { - if ((HWND)lParam == m_language.GetHwnd()) { - SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkMode((HDC)wParam, TRANSPARENT); - return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); - } - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - -public: - CEnterPasswordDialog(MDatabaseCommon *db) : - CDlgBase(g_plugin, IDD_LOGIN), - m_timer(this, 1), - m_header(this, IDC_HEADERBAR), - m_language(this, IDC_LANG), - m_passwordEdit(this, IDC_USERPASS), - m_db(db) - { - m_newPass[0] = 0; - m_timer.OnEvent = Callback(this, &CEnterPasswordDialog::OnTimer); - } - - ~CEnterPasswordDialog() - { - SecureZeroMemory(m_newPass, sizeof(m_newPass)); - } - - bool OnInitDialog() override - { - m_header.SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); - - if (m_wrongPass) { - if (m_wrongPass > 2) { - m_passwordEdit.Disable(); - EnableWindow(GetDlgItem(m_hwnd, IDOK), false); - m_header.SetText(TranslateT("Too many errors!")); - } - else m_header.SetText(TranslateT("Password is not correct!")); - } - else m_header.SetText(TranslateT("Please type in your password")); - - m_timer.Start(200); - return true; - } - - bool OnApply() override - { - m_passwordEdit.GetText(m_newPass, _countof(m_newPass)); - return true; - } - - void OnDestroy() override - { - Window_FreeIcon_IcoLib(m_header.GetHwnd()); - } -}; - -int MDatabaseCommon::InitCrypt() -{ - if (m_crypto != nullptr) - return 0; - - CRYPTO_PROVIDER *pProvider = ReadProvider(); - if (pProvider == nullptr) - pProvider = SelectProvider(); - if (pProvider == nullptr) - return 1; - - if ((m_crypto = pProvider->pFactory()) == nullptr) - return 3; - - MBinBuffer key; - BOOL bSuccess = ReadCryptoKey(key); - if (bSuccess && (key.length() == m_crypto->getKeyLength())) { - // first try to set a key without password - if (!m_crypto->setKey(nullptr, (const uint8_t*)key.data(), key.length())) { - CEnterPasswordDialog dlg(this); - while (true) { - if (!dlg.DoModal()) - return 4; - - pass_ptrA szPassword(mir_utf8encodeW(dlg.m_newPass)); - if (m_crypto->setKey(szPassword, (const uint8_t*)key.data(), key.length())) { - m_bUsesPassword = true; - break; - } - dlg.m_wrongPass++; - } - } - } - else { - if (!m_crypto->generateKey()) - return 6; - StoreCryptoKey(); - } - - m_bEncrypted = ReadEncryption(); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "database.h" +#include "encrypt.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// Provider selection dialog + +class CSelectCryptoDialog : public CDlgBase +{ + CCtrlCombo m_combo; + CCtrlData m_descr; + CRYPTO_PROVIDER *m_selected = nullptr; + + CRYPTO_PROVIDER *getCurrent() + { + return (CRYPTO_PROVIDER*)m_combo.GetCurData(); + } + + bool OnInitDialog() override + { + for (auto &p : arCryptoProviders) + m_combo.AddStringA(p->pszName, LPARAM(p)); + + m_combo.SetCurSel(0); + m_descr.SetText(arCryptoProviders[0].szDescr.w); + return true; + } + + bool OnApply() override + { + m_selected = getCurrent(); + return true; + } + + void OnComboChanged(CCtrlCombo*) + { + m_descr.SetText(getCurrent()->szDescr.w); + } + +public: + CSelectCryptoDialog() : + CDlgBase(g_plugin, IDD_SELECT_CRYPTOPROVIDER), + m_combo(this, IDC_SELECTCRYPT_COMBO), + m_descr(this, IDC_CRYPTOPROVIDER_DESCR) + { + m_combo.OnChange = Callback(this, &CSelectCryptoDialog::OnComboChanged); + } + + inline CRYPTO_PROVIDER* GetSelected() + { return m_selected; + } +}; + +CRYPTO_PROVIDER* MDatabaseCommon::SelectProvider() +{ + if (arCryptoProviders.getCount() == 0) + return nullptr; + + CRYPTO_PROVIDER *pProv; + if (arCryptoProviders.getCount() > 1) { + CSelectCryptoDialog dlg; + dlg.DoModal(); + pProv = dlg.GetSelected(); + } + else pProv = &arCryptoProviders[0]; + + return (StoreProvider(pProv)) ? pProv : nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL MDatabaseCommon::IsSettingEncrypted(LPCSTR szModule, LPCSTR szSetting) +{ + if (!_strnicmp(szSetting, "password", 8)) return true; + if (!mir_strcmp(szSetting, "NLProxyAuthPassword")) return true; + if (!mir_strcmp(szSetting, "LNPassword")) return true; + if (!mir_strcmp(szSetting, "FileProxyPassword")) return true; + if (!mir_strcmp(szSetting, "TokenSecret")) return true; + + if (!mir_strcmp(szModule, "SecureIM")) { + if (!mir_strcmp(szSetting, "pgp")) return true; + if (!mir_strcmp(szSetting, "pgpPrivKey")) return true; + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static HGENMENU hSetPwdMenu; + +__forceinline wchar_t *GetMenuTitle(bool bUsesPassword) +{ + return bUsesPassword ? LPGENW("Change/remove password") : LPGENW("Set password"); +} + +void MDatabaseCommon::SetPassword(const wchar_t *ptszPassword) +{ + if (mir_wstrlen(ptszPassword) == 0) { + m_bUsesPassword = false; + m_crypto->setPassword(nullptr); + } + else { + m_bUsesPassword = true; + m_crypto->setPassword(pass_ptrA(mir_utf8encodeW(ptszPassword))); + } + + Menu_ModifyItem(hSetPwdMenu, GetMenuTitle(m_bUsesPassword), Skin_GetIconHandle(SKINICON_OTHER_KEYS)); +} + +static UINT oldLangID; +void LanguageChanged(HWND hwndDlg) +{ + UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); + char Lang[3] = { 0 }; + if (LangID != oldLangID) { + oldLangID = LangID; + GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); + Lang[0] = toupper(Lang[0]); + Lang[1] = tolower(Lang[1]); + SetDlgItemTextA(hwndDlg, IDC_LANG, Lang); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool CheckOldPassword(HWND hwndDlg, MDatabaseCommon *db) +{ + if (db->usesPassword()) { + wchar_t buf[100]; + GetDlgItemText(hwndDlg, IDC_OLDPASS, buf, _countof(buf)); + pass_ptrA oldPass(mir_utf8encodeW(buf)); + if (!db->getCrypt()->checkPassword(oldPass)) { + SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Wrong old password entered!")); + return false; + } + } + return true; +} + +static INT_PTR CALLBACK sttChangePassword(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + MDatabaseCommon *db = (MDatabaseCommon *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (uMsg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + + oldLangID = 0; + SetTimer(hwndDlg, 1, 200, nullptr); + LanguageChanged(hwndDlg); + return TRUE; + + case WM_CTLCOLORSTATIC: + if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_LANG)) { + SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkMode((HDC)wParam, TRANSPARENT); + return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); + } + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + break; + + case IDREMOVE: + if (!CheckOldPassword(hwndDlg, db)) { +LBL_Error: + SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_NCPAINT, 0, 0); + SetDlgItemTextA(hwndDlg, IDC_USERPASS1, ""); + SetDlgItemTextA(hwndDlg, IDC_USERPASS2, ""); + } + else { + db->SetPassword(nullptr); + db->StoreCryptoKey(); + EndDialog(hwndDlg, IDREMOVE); + } + break; + + case IDOK: + wchar_t buf2[100]; + GetDlgItemText(hwndDlg, IDC_USERPASS1, buf2, _countof(buf2)); + if (wcslen(buf2) < 3) { + SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Password is too short!")); + goto LBL_Error; + } + + wchar_t buf[100]; + GetDlgItemText(hwndDlg, IDC_USERPASS2, buf, _countof(buf)); + if (wcscmp(buf2, buf)) { + SetDlgItemText(hwndDlg, IDC_HEADERBAR, TranslateT("Passwords do not match!")); + goto LBL_Error; + } + + if (!CheckOldPassword(hwndDlg, db)) + goto LBL_Error; + + db->SetPassword(buf2); + db->StoreCryptoKey(); + SecureZeroMemory(buf, sizeof(buf)); + SecureZeroMemory(buf2, sizeof(buf2)); + EndDialog(hwndDlg, IDOK); + } + break; + + case WM_TIMER: + LanguageChanged(hwndDlg); + return FALSE; + + case WM_DESTROY: + KillTimer(hwndDlg, 1); + Window_FreeIcon_IcoLib(GetDlgItem(hwndDlg, IDC_HEADERBAR)); + } + + return FALSE; +} + +static INT_PTR ChangePassword(void* obj, WPARAM, LPARAM) +{ + MDatabaseCommon *db = (MDatabaseCommon*)obj; + DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(db->usesPassword() ? IDD_CHANGEPASS : IDD_NEWPASS), nullptr, sttChangePassword, (LPARAM)db); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Options + +class CDatabaseOptionsDialog : public CDlgBase +{ + CCtrlCheck m_chkStandart, m_chkTotal; + CCtrlButton m_btnChangePass; + MDatabaseCommon *m_db; + +public: + CDatabaseOptionsDialog(MDatabaseCommon *db) : + CDlgBase(g_plugin, IDD_OPT_DATABASE), + m_db(db), + m_chkTotal(this, IDC_TOTAL), + m_chkStandart(this, IDC_STANDARD), + m_btnChangePass(this, IDC_USERPASS1) + { + m_btnChangePass.OnClick = Callback(this, &CDatabaseOptionsDialog::onClick_ChangePass); + } + + bool OnInitDialog() override + { + m_chkStandart.SetState(!m_db->isEncrypted()); + m_chkTotal.SetState(m_db->isEncrypted()); + return true; + } + + bool OnApply() override + { + SetCursor(LoadCursor(nullptr, IDC_WAIT)); + m_db->EnableEncryption(m_chkTotal.GetState() != 0); + SetCursor(LoadCursor(nullptr, IDC_ARROW)); + m_chkStandart.SetState(!m_db->isEncrypted()); + m_chkTotal.SetState(m_db->isEncrypted()); + return true; + } + + void onClick_ChangePass(CCtrlButton *) + { + ChangePassword(m_db, 0, 0); + } +}; + +static int OnOptionsInit(PVOID obj, WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = { sizeof(odp) }; + odp.position = -790000000; + odp.flags = ODPF_BOLDGROUPS; + odp.szTitle.a = LPGEN("Database"); + odp.pDialog = new CDatabaseOptionsDialog((MDatabaseCommon*)obj); + g_plugin.addOptions(wParam, &odp); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void InitCryptMenuItem(CMenuItem &mi) +{ + auto *pDb = db_get_current(); + + HookEventObj(ME_OPT_INITIALISE, OnOptionsInit, pDb); + + SET_UID(mi, 0x50321866, 0xba1, 0x46dd, 0xb3, 0xa6, 0xc3, 0xcc, 0x55, 0xf2, 0x42, 0x9e); + mi.flags = CMIF_UNICODE; + mi.position = 1000000001; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_KEYS); + mi.name.w = GetMenuTitle(pDb->usesPassword()); + mi.pszService = "DB/UI/ChangePassword"; + hSetPwdMenu = Menu_AddMainMenuItem(&mi); + CreateServiceFunctionObj(mi.pszService, ChangePassword, pDb); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class CEnterPasswordDialog : public CDlgBase +{ + friend class MDatabaseCommon; + + CTimer m_timer; + CCtrlData m_header; + CCtrlData m_language; + CCtrlEdit m_passwordEdit; + + int m_wrongPass = 0; + wchar_t m_newPass[100]; + MDatabaseCommon *m_db; + + void OnTimer(CTimer*) + { + UINT_PTR LangID = (UINT_PTR)GetKeyboardLayout(0); + char Lang[3] = { 0 }; + GetLocaleInfoA(MAKELCID((LangID & 0xffffffff), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, Lang, 2); + Lang[0] = toupper(Lang[0]); + Lang[1] = tolower(Lang[1]); + m_language.SetTextA(Lang); + } + + INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override + { + if (msg == WM_CTLCOLORSTATIC) { + if ((HWND)lParam == m_language.GetHwnd()) { + SetTextColor((HDC)wParam, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkMode((HDC)wParam, TRANSPARENT); + return (INT_PTR)GetSysColorBrush(COLOR_HIGHLIGHT); + } + } + return CDlgBase::DlgProc(msg, wParam, lParam); + } + +public: + CEnterPasswordDialog(MDatabaseCommon *db) : + CDlgBase(g_plugin, IDD_LOGIN), + m_timer(this, 1), + m_header(this, IDC_HEADERBAR), + m_language(this, IDC_LANG), + m_passwordEdit(this, IDC_USERPASS), + m_db(db) + { + m_newPass[0] = 0; + m_timer.OnEvent = Callback(this, &CEnterPasswordDialog::OnTimer); + } + + ~CEnterPasswordDialog() + { + SecureZeroMemory(m_newPass, sizeof(m_newPass)); + } + + bool OnInitDialog() override + { + m_header.SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_DATABASE, true)); + + if (m_wrongPass) { + if (m_wrongPass > 2) { + m_passwordEdit.Disable(); + EnableWindow(GetDlgItem(m_hwnd, IDOK), false); + m_header.SetText(TranslateT("Too many errors!")); + } + else m_header.SetText(TranslateT("Password is not correct!")); + } + else m_header.SetText(TranslateT("Please type in your password")); + + m_timer.Start(200); + return true; + } + + bool OnApply() override + { + m_passwordEdit.GetText(m_newPass, _countof(m_newPass)); + return true; + } + + void OnDestroy() override + { + Window_FreeIcon_IcoLib(m_header.GetHwnd()); + } +}; + +int MDatabaseCommon::InitCrypt() +{ + if (m_crypto != nullptr) + return 0; + + CRYPTO_PROVIDER *pProvider = ReadProvider(); + if (pProvider == nullptr) + pProvider = SelectProvider(); + if (pProvider == nullptr) + return 1; + + if ((m_crypto = pProvider->pFactory()) == nullptr) + return 3; + + MBinBuffer key; + BOOL bSuccess = ReadCryptoKey(key); + if (bSuccess && (key.length() == m_crypto->getKeyLength())) { + // first try to set a key without password + if (!m_crypto->setKey(nullptr, (const uint8_t*)key.data(), key.length())) { + CEnterPasswordDialog dlg(this); + while (true) { + if (!dlg.DoModal()) + return 4; + + pass_ptrA szPassword(mir_utf8encodeW(dlg.m_newPass)); + if (m_crypto->setKey(szPassword, (const uint8_t*)key.data(), key.length())) { + m_bUsesPassword = true; + break; + } + dlg.m_wrongPass++; + } + } + } + else { + if (!m_crypto->generateKey()) + return 6; + StoreCryptoKey(); + } + + m_bEncrypted = ReadEncryption(); + return 0; +} diff --git a/src/mir_app/src/MDatabaseReadonly.cpp b/src/mir_app/src/MDatabaseReadonly.cpp index f3ed3da7ef..f022d5e5fb 100644 --- a/src/mir_app/src/MDatabaseReadonly.cpp +++ b/src/mir_app/src/MDatabaseReadonly.cpp @@ -1,180 +1,180 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "database.h" - -MDatabaseReadonly::MDatabaseReadonly() -{ -} - -BOOL MDatabaseReadonly::IsRelational(void) -{ - return FALSE; -} - -void MDatabaseReadonly::SetCacheSafetyMode(BOOL) -{ -} - -BOOL MDatabaseReadonly::EnumModuleNames(DBMODULEENUMPROC, void*) -{ - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadCryptoKey(MBinBuffer&) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreCryptoKey() -{ - return FALSE; -} - -STDMETHODIMP_(CRYPTO_PROVIDER*) MDatabaseReadonly::ReadProvider() -{ - return nullptr; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreProvider(CRYPTO_PROVIDER *) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::EnableEncryption(BOOL) -{ - return FALSE; -} - -STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadEncryption() -{ - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MCONTACT MDatabaseReadonly::AddContact(void) -{ - return 0; -} - -int MDatabaseReadonly::DeleteContact(MCONTACT) -{ - return 1; -} - -BOOL MDatabaseReadonly::IsDbContact(MCONTACT contactID) -{ - return contactID == 1; -} - -int MDatabaseReadonly::GetContactSize(void) -{ - return sizeof(DBCachedContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MEVENT MDatabaseReadonly::AddEvent(MCONTACT, const DBEVENTINFO*) -{ - return 0; -} - -BOOL MDatabaseReadonly::DeleteEvent(MEVENT) -{ - return 1; -} - -BOOL MDatabaseReadonly::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*) -{ - return 1; -} - -int MDatabaseReadonly::GetBlobSize(MEVENT) -{ - return 0; -} - -MEVENT MDatabaseReadonly::FindFirstUnreadEvent(MCONTACT) -{ - return 0; -} - -BOOL MDatabaseReadonly::MarkEventRead(MCONTACT, MEVENT) -{ - return 1; -} - -MCONTACT MDatabaseReadonly::GetEventContact(MEVENT) -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseReadonly::GetContactSettingWorker(MCONTACT, LPCSTR, LPCSTR, DBVARIANT*, int) -{ - return 1; -} - -BOOL MDatabaseReadonly::WriteContactSettingWorker(MCONTACT, DBCONTACTWRITESETTING&) -{ - return 1; -} - -BOOL MDatabaseReadonly::DeleteContactSetting(MCONTACT, LPCSTR, LPCSTR) -{ - return 1; -} - -BOOL MDatabaseReadonly::EnumContactSettings(MCONTACT, DBSETTINGENUMPROC, const char*, void*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL MDatabaseReadonly::MetaMergeHistory(DBCachedContact*, DBCachedContact*) -{ - return 1; -} - -BOOL MDatabaseReadonly::MetaSplitHistory(DBCachedContact*, DBCachedContact*) -{ - return 1; -} - -BOOL MDatabaseReadonly::MetaRemoveSubHistory(DBCachedContact*) -{ - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MEVENT MDatabaseReadonly::GetEventById(LPCSTR, LPCSTR) -{ - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "database.h" + +MDatabaseReadonly::MDatabaseReadonly() +{ +} + +BOOL MDatabaseReadonly::IsRelational(void) +{ + return FALSE; +} + +void MDatabaseReadonly::SetCacheSafetyMode(BOOL) +{ +} + +BOOL MDatabaseReadonly::EnumModuleNames(DBMODULEENUMPROC, void*) +{ + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadCryptoKey(MBinBuffer&) +{ + return FALSE; +} + +STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreCryptoKey() +{ + return FALSE; +} + +STDMETHODIMP_(CRYPTO_PROVIDER*) MDatabaseReadonly::ReadProvider() +{ + return nullptr; +} + +STDMETHODIMP_(BOOL) MDatabaseReadonly::StoreProvider(CRYPTO_PROVIDER *) +{ + return FALSE; +} + +STDMETHODIMP_(BOOL) MDatabaseReadonly::EnableEncryption(BOOL) +{ + return FALSE; +} + +STDMETHODIMP_(BOOL) MDatabaseReadonly::ReadEncryption() +{ + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MCONTACT MDatabaseReadonly::AddContact(void) +{ + return 0; +} + +int MDatabaseReadonly::DeleteContact(MCONTACT) +{ + return 1; +} + +BOOL MDatabaseReadonly::IsDbContact(MCONTACT contactID) +{ + return contactID == 1; +} + +int MDatabaseReadonly::GetContactSize(void) +{ + return sizeof(DBCachedContact); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MEVENT MDatabaseReadonly::AddEvent(MCONTACT, const DBEVENTINFO*) +{ + return 0; +} + +BOOL MDatabaseReadonly::DeleteEvent(MEVENT) +{ + return 1; +} + +BOOL MDatabaseReadonly::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*) +{ + return 1; +} + +int MDatabaseReadonly::GetBlobSize(MEVENT) +{ + return 0; +} + +MEVENT MDatabaseReadonly::FindFirstUnreadEvent(MCONTACT) +{ + return 0; +} + +BOOL MDatabaseReadonly::MarkEventRead(MCONTACT, MEVENT) +{ + return 1; +} + +MCONTACT MDatabaseReadonly::GetEventContact(MEVENT) +{ + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL MDatabaseReadonly::GetContactSettingWorker(MCONTACT, LPCSTR, LPCSTR, DBVARIANT*, int) +{ + return 1; +} + +BOOL MDatabaseReadonly::WriteContactSettingWorker(MCONTACT, DBCONTACTWRITESETTING&) +{ + return 1; +} + +BOOL MDatabaseReadonly::DeleteContactSetting(MCONTACT, LPCSTR, LPCSTR) +{ + return 1; +} + +BOOL MDatabaseReadonly::EnumContactSettings(MCONTACT, DBSETTINGENUMPROC, const char*, void*) +{ + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL MDatabaseReadonly::MetaMergeHistory(DBCachedContact*, DBCachedContact*) +{ + return 1; +} + +BOOL MDatabaseReadonly::MetaSplitHistory(DBCachedContact*, DBCachedContact*) +{ + return 1; +} + +BOOL MDatabaseReadonly::MetaRemoveSubHistory(DBCachedContact*) +{ + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MEVENT MDatabaseReadonly::GetEventById(LPCSTR, LPCSTR) +{ + return 0; +} diff --git a/src/mir_app/src/MHttpRequest.cpp b/src/mir_app/src/MHttpRequest.cpp index cefe4d551c..7935974463 100644 --- a/src/mir_app/src/MHttpRequest.cpp +++ b/src/mir_app/src/MHttpRequest.cpp @@ -1,93 +1,93 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -MHttpRequest::MHttpRequest() -{ - cbSize = sizeof(NETLIBHTTPREQUEST); - requestType = REQUEST_GET; -} - -MHttpRequest::~MHttpRequest() -{ - for (int i = 0; i < headersCount; i++) { - mir_free(headers[i].szName); - mir_free(headers[i].szValue); - } - mir_free(headers); - mir_free(pData); -} - -void MHttpRequest::AddHeader(LPCSTR szName, LPCSTR szValue) -{ - for (int i = 0; i < headersCount; i++) - if (!mir_strcmp(headers[i].szName, szName)) { - replaceStr(headers[i].szValue, szValue); - return; - } - - headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1)); - headers[headersCount].szName = mir_strdup(szName); - headers[headersCount].szValue = mir_strdup(szValue); - headersCount++; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%ld", param.szName, param.iValue); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%lld", param.szName, param.iValue); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM ¶m) -{ - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%s", param.szName, mir_urlEncode(param.szValue).c_str()); - return pReq; -} - -MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const WCHAR_PARAM ¶m) -{ - T2Utf szValue(param.wszValue); - CMStringA &s = pReq->m_szParam; - if (!s.IsEmpty()) - s.AppendChar('&'); - s.AppendFormat("%s=%s", param.szName, mir_urlEncode(szValue).c_str()); - return pReq; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +MHttpRequest::MHttpRequest() +{ + cbSize = sizeof(NETLIBHTTPREQUEST); + requestType = REQUEST_GET; +} + +MHttpRequest::~MHttpRequest() +{ + for (int i = 0; i < headersCount; i++) { + mir_free(headers[i].szName); + mir_free(headers[i].szValue); + } + mir_free(headers); + mir_free(pData); +} + +void MHttpRequest::AddHeader(LPCSTR szName, LPCSTR szValue) +{ + for (int i = 0; i < headersCount; i++) + if (!mir_strcmp(headers[i].szName, szName)) { + replaceStr(headers[i].szValue, szValue); + return; + } + + headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1)); + headers[headersCount].szName = mir_strdup(szName); + headers[headersCount].szValue = mir_strdup(szValue); + headersCount++; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT_PARAM ¶m) +{ + CMStringA &s = pReq->m_szParam; + if (!s.IsEmpty()) + s.AppendChar('&'); + s.AppendFormat("%s=%ld", param.szName, param.iValue); + return pReq; +} + +MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const INT64_PARAM ¶m) +{ + CMStringA &s = pReq->m_szParam; + if (!s.IsEmpty()) + s.AppendChar('&'); + s.AppendFormat("%s=%lld", param.szName, param.iValue); + return pReq; +} + +MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const CHAR_PARAM ¶m) +{ + CMStringA &s = pReq->m_szParam; + if (!s.IsEmpty()) + s.AppendChar('&'); + s.AppendFormat("%s=%s", param.szName, mir_urlEncode(param.szValue).c_str()); + return pReq; +} + +MIR_APP_DLL(MHttpRequest*) operator<<(MHttpRequest *pReq, const WCHAR_PARAM ¶m) +{ + T2Utf szValue(param.wszValue); + CMStringA &s = pReq->m_szParam; + if (!s.IsEmpty()) + s.AppendChar('&'); + s.AppendFormat("%s=%s", param.szName, mir_urlEncode(szValue).c_str()); + return pReq; +} diff --git a/src/mir_app/src/addcontact.cpp b/src/mir_app/src/addcontact.cpp index e065bf94df..2eda1400ca 100644 --- a/src/mir_app/src/addcontact.cpp +++ b/src/mir_app/src/addcontact.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/auth.cpp b/src/mir_app/src/auth.cpp index c40053819f..0520c20a11 100644 --- a/src/mir_app/src/auth.cpp +++ b/src/mir_app/src/auth.cpp @@ -1,367 +1,367 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// Auth Request dialog - -class CAuthReqDlg : public CDlgBase -{ - MEVENT m_hDbEvent; - MCONTACT m_hContact; - const char *m_szProto; - - CCtrlBase fldHeader, fldReason; - CCtrlEdit edtReason; - CCtrlCheck chkAdd; - CCtrlButton btnAdd, btnDetails, btnLater; - -public: - CAuthReqDlg(MEVENT hEvent) : - CDlgBase(g_plugin, IDD_AUTHREQ), - m_hDbEvent(hEvent), - fldHeader(this, IDC_HEADERBAR), - fldReason(this, IDC_REASON), - edtReason(this, IDC_DENYREASON), - btnAdd(this, IDC_ADD), - btnLater(this, IDC_DECIDELATER), - btnDetails(this, IDC_DETAILS), - chkAdd(this, IDC_ADDCHECK) - { - btnLater.OnClick = Callback(this, &CAuthReqDlg::onClick_Later); - btnDetails.OnClick = Callback(this, &CAuthReqDlg::onClick_Details); - } - - bool OnInitDialog() override - { - Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); - Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); - - // blob is: uin(uint32_t), hcontact(uint32_t), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ) - DBEVENTINFO dbei = {}; - dbei.cbBlob = -1; - if (db_event_get(m_hDbEvent, &dbei)) - return false; - - m_szProto = dbei.szModule; - PROTOACCOUNT *acc = Proto_GetAccount(dbei.szModule); - - uint32_t uin = *(uint32_t*)dbei.pBlob; - m_hContact = DbGetAuthEventContact(&dbei); - char *nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; - char *first = nick + mir_strlen(nick) + 1; - char *last = first + mir_strlen(first) + 1; - char *email = last + mir_strlen(last) + 1; - char *reason = email + mir_strlen(email) + 1; - - #ifdef _WINDOWS - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); - #endif - - ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); - ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); - ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); - ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); - ptrW reasonT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(reason) : mir_a2u(reason)); - - CMStringW wszHeader; - if (firstT[0] && lastT[0]) - wszHeader.Format(L"%s %s", (wchar_t*)firstT, (wchar_t*)lastT); - else if (firstT[0]) - wszHeader = firstT.get(); - else if (lastT[0]) - wszHeader = lastT.get(); - - if (mir_wstrlen(nickT)) { - if (wszHeader.IsEmpty()) - wszHeader = nickT.get(); - else - wszHeader.AppendFormat(L" %s", nickT.get()); - } - if (wszHeader.IsEmpty()) - wszHeader = TranslateT(""); - - if (uin && emailT[0]) - wszHeader.AppendFormat(TranslateT(" requested authorization\n%u (%s) on %s"), uin, emailT.get(), acc->tszAccountName); - else if (uin) - wszHeader.AppendFormat(TranslateT(" requested authorization\n%u on %s"), uin, acc->tszAccountName); - else - wszHeader.AppendFormat(TranslateT(" requested authorization\n%s on %s"), emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); - fldHeader.SetText(wszHeader); - - fldReason.SetText(reasonT); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - - edtReason.SetMaxLength(255); - if (CallProtoService(dbei.szModule, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON) { - edtReason.Disable(); - edtReason.SetText(TranslateT("Feature is not supported by protocol")); - } - - if (Contact::OnList(m_hContact)) { - chkAdd.Disable(); - chkAdd.SetState(false); - } - else chkAdd.SetState(true); - return true; - } - - bool OnApply() override - { - CallProtoService(m_szProto, PS_AUTHALLOW, m_hDbEvent, 0); - - if (chkAdd.GetState()) - Contact::AddByEvent(m_hDbEvent, m_hwnd); - return true; - } - - void OnDestroy() override - { - if (!m_bSucceeded) { - if (edtReason.Enabled()) - CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, (LPARAM)ptrW(edtReason.GetText())); - else - CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, 0); - } - - Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); - Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); - - #ifdef _WINDOWS - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - #endif - } - - void onClick_Later(CCtrlButton*) - { - m_bSucceeded = true; - Close(); - } - - void onClick_Details(CCtrlButton*) - { - CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); - } -}; - -static INT_PTR ShowReqWindow(WPARAM, LPARAM lParam) -{ - (new CAuthReqDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CAddedDlg : public CDlgBase -{ - MEVENT m_hDbEvent; - MCONTACT m_hContact; - - CCtrlBase fldHeader; - CCtrlButton btnDetails, btnAdd; - -public: - CAddedDlg(MEVENT hEvent) : - CDlgBase(g_plugin, IDD_ADDED), - m_hDbEvent(hEvent), - btnAdd(this, IDC_ADD), - btnDetails(this, IDC_DETAILS), - fldHeader(this, IDC_HEADERBAR) - { - btnAdd.OnClick = Callback(this, &CAddedDlg::onClick_Add); - btnDetails.OnClick = Callback(this, &CAddedDlg::onClick_Details); - } - - bool OnInitDialog() override - { - Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); - Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); - - // blob is: uin(uint32_t), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) - DB::EventInfo dbei; - dbei.cbBlob = -1; - db_event_get(m_hDbEvent, &dbei); - - m_hContact = DbGetAuthEventContact(&dbei); - - uint32_t uin = *(uint32_t*)dbei.pBlob; - char* nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; - char* first = nick + mir_strlen(nick) + 1; - char* last = first + mir_strlen(first) + 1; - char* email = last + mir_strlen(last) + 1; - - #ifdef _WINDOWS - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); - #endif - - PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule); - - ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); - ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); - ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); - ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); - - wchar_t name[128] = L""; - int off = 0; - if (firstT[0] && lastT[0]) - off = mir_snwprintf(name, L"%s %s", firstT.get(), lastT.get()); - else if (firstT[0]) - off = mir_snwprintf(name, L"%s", firstT.get()); - else if (lastT[0]) - off = mir_snwprintf(name, L"%s", lastT.get()); - if (nickT[0]) { - if (off) - mir_snwprintf(name + off, _countof(name) - off, L" (%s)", nickT.get()); - else - wcsncpy_s(name, nickT, _TRUNCATE); - } - if (!name[0]) - wcsncpy_s(name, TranslateT(""), _TRUNCATE); - - wchar_t hdr[256]; - if (uin && emailT[0]) - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT.get(), acc->tszAccountName); - else if (uin) - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName); - else - mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); - fldHeader.SetText(hdr); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - return true; - } - - bool OnApply() - { - Contact::AddByEvent(m_hDbEvent, m_hwnd); - return true; - } - - void OnDestroy() override - { - Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); - Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); - - #ifdef _WINDOWS - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - #endif - } - - void onClick_Add(CCtrlButton*) - { - Contact::AddByEvent(m_hDbEvent, m_hwnd); - - if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) - btnAdd.Hide(); - } - - void onClick_Details(CCtrlButton*) - { - CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); - } -}; - -static INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam) -{ - (new CAddedDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define MS_AUTH_SHOWADDED "Auth/ShowAdded" -#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest" - -static int AuthEventAdded(WPARAM, LPARAM lParam) -{ - wchar_t szTooltip[256]; - MEVENT hDbEvent = (MEVENT)lParam; - - DB::EventInfo dbei; - db_event_get(lParam, &dbei); - if (dbei.flags & (DBEF_SENT | DBEF_READ) || (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED)) - return 0; - - dbei.cbBlob = -1; - db_event_get(hDbEvent, &dbei); - - MCONTACT hContact = DbGetAuthEventContact(&dbei); - - CLISTEVENT cle = {}; - cle.hContact = hContact; - cle.szTooltip.w = szTooltip; - cle.flags = CLEF_UNICODE; - cle.lParam = lParam; - cle.hDbEvent = hDbEvent; - - ptrW szUid(Contact::GetInfo(CNF_UNIQUEID, hContact)); - - if (dbei.eventType == EVENTTYPE_AUTHREQUEST) { - Skin_PlaySound("AuthRequest"); - if (szUid) - mir_snwprintf(szTooltip, TranslateT("%s requests authorization"), szUid.get()); - else - mir_snwprintf(szTooltip, TranslateT("%u requests authorization"), *(uint32_t*)dbei.pBlob); - - cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_REQUEST); - cle.pszService = MS_AUTH_SHOWREQUEST; - g_clistApi.pfnAddEvent(&cle); - } - else if (dbei.eventType == EVENTTYPE_ADDED) { - Skin_PlaySound("AddedEvent"); - if (szUid) - mir_snwprintf(szTooltip, TranslateT("%s added you to their contact list"), szUid.get()); - else - mir_snwprintf(szTooltip, TranslateT("%u added you to their contact list"), *(uint32_t*)dbei.pBlob); - - cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_ADD); - cle.pszService = MS_AUTH_SHOWADDED; - g_clistApi.pfnAddEvent(&cle); - } - return 0; -} - -static void CALLBACK LaunchAuth() -{ - HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded); -} - -int LoadSendRecvAuthModule(void) -{ - CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow); - CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow); - Miranda_WaitOnHandle(LaunchAuth); - - g_plugin.addSound("AuthRequest", LPGENW("Alerts"), LPGENW("Authorization request")); - g_plugin.addSound("AddedEvent", LPGENW("Alerts"), LPGENW("Added event")); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// Auth Request dialog + +class CAuthReqDlg : public CDlgBase +{ + MEVENT m_hDbEvent; + MCONTACT m_hContact; + const char *m_szProto; + + CCtrlBase fldHeader, fldReason; + CCtrlEdit edtReason; + CCtrlCheck chkAdd; + CCtrlButton btnAdd, btnDetails, btnLater; + +public: + CAuthReqDlg(MEVENT hEvent) : + CDlgBase(g_plugin, IDD_AUTHREQ), + m_hDbEvent(hEvent), + fldHeader(this, IDC_HEADERBAR), + fldReason(this, IDC_REASON), + edtReason(this, IDC_DENYREASON), + btnAdd(this, IDC_ADD), + btnLater(this, IDC_DECIDELATER), + btnDetails(this, IDC_DETAILS), + chkAdd(this, IDC_ADDCHECK) + { + btnLater.OnClick = Callback(this, &CAuthReqDlg::onClick_Later); + btnDetails.OnClick = Callback(this, &CAuthReqDlg::onClick_Details); + } + + bool OnInitDialog() override + { + Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); + Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); + + // blob is: uin(uint32_t), hcontact(uint32_t), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ), reason(ASCIIZ) + DBEVENTINFO dbei = {}; + dbei.cbBlob = -1; + if (db_event_get(m_hDbEvent, &dbei)) + return false; + + m_szProto = dbei.szModule; + PROTOACCOUNT *acc = Proto_GetAccount(dbei.szModule); + + uint32_t uin = *(uint32_t*)dbei.pBlob; + m_hContact = DbGetAuthEventContact(&dbei); + char *nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; + char *first = nick + mir_strlen(nick) + 1; + char *last = first + mir_strlen(first) + 1; + char *email = last + mir_strlen(last) + 1; + char *reason = email + mir_strlen(email) + 1; + + #ifdef _WINDOWS + SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); + SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); + #endif + + ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); + ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); + ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); + ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); + ptrW reasonT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(reason) : mir_a2u(reason)); + + CMStringW wszHeader; + if (firstT[0] && lastT[0]) + wszHeader.Format(L"%s %s", (wchar_t*)firstT, (wchar_t*)lastT); + else if (firstT[0]) + wszHeader = firstT.get(); + else if (lastT[0]) + wszHeader = lastT.get(); + + if (mir_wstrlen(nickT)) { + if (wszHeader.IsEmpty()) + wszHeader = nickT.get(); + else + wszHeader.AppendFormat(L" %s", nickT.get()); + } + if (wszHeader.IsEmpty()) + wszHeader = TranslateT(""); + + if (uin && emailT[0]) + wszHeader.AppendFormat(TranslateT(" requested authorization\n%u (%s) on %s"), uin, emailT.get(), acc->tszAccountName); + else if (uin) + wszHeader.AppendFormat(TranslateT(" requested authorization\n%u on %s"), uin, acc->tszAccountName); + else + wszHeader.AppendFormat(TranslateT(" requested authorization\n%s on %s"), emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); + fldHeader.SetText(wszHeader); + + fldReason.SetText(reasonT); + + if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) + btnAdd.Hide(); + + edtReason.SetMaxLength(255); + if (CallProtoService(dbei.szModule, PS_GETCAPS, PFLAGNUM_4, 0) & PF4_NOAUTHDENYREASON) { + edtReason.Disable(); + edtReason.SetText(TranslateT("Feature is not supported by protocol")); + } + + if (Contact::OnList(m_hContact)) { + chkAdd.Disable(); + chkAdd.SetState(false); + } + else chkAdd.SetState(true); + return true; + } + + bool OnApply() override + { + CallProtoService(m_szProto, PS_AUTHALLOW, m_hDbEvent, 0); + + if (chkAdd.GetState()) + Contact::AddByEvent(m_hDbEvent, m_hwnd); + return true; + } + + void OnDestroy() override + { + if (!m_bSucceeded) { + if (edtReason.Enabled()) + CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, (LPARAM)ptrW(edtReason.GetText())); + else + CallProtoService(m_szProto, PS_AUTHDENY, m_hDbEvent, 0); + } + + Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); + Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); + + #ifdef _WINDOWS + DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); + DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); + #endif + } + + void onClick_Later(CCtrlButton*) + { + m_bSucceeded = true; + Close(); + } + + void onClick_Details(CCtrlButton*) + { + CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); + } +}; + +static INT_PTR ShowReqWindow(WPARAM, LPARAM lParam) +{ + (new CAuthReqDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class CAddedDlg : public CDlgBase +{ + MEVENT m_hDbEvent; + MCONTACT m_hContact; + + CCtrlBase fldHeader; + CCtrlButton btnDetails, btnAdd; + +public: + CAddedDlg(MEVENT hEvent) : + CDlgBase(g_plugin, IDD_ADDED), + m_hDbEvent(hEvent), + btnAdd(this, IDC_ADD), + btnDetails(this, IDC_DETAILS), + fldHeader(this, IDC_HEADERBAR) + { + btnAdd.OnClick = Callback(this, &CAddedDlg::onClick_Add); + btnDetails.OnClick = Callback(this, &CAddedDlg::onClick_Details); + } + + bool OnInitDialog() override + { + Button_SetSkin_IcoLib(m_hwnd, IDC_DETAILS, SKINICON_OTHER_USERDETAILS, LPGEN("View user's details")); + Button_SetSkin_IcoLib(m_hwnd, IDC_ADD, SKINICON_OTHER_ADDCONTACT, LPGEN("Add contact permanently to list")); + + // blob is: uin(uint32_t), hcontact(HANDLE), nick(ASCIIZ), first(ASCIIZ), last(ASCIIZ), email(ASCIIZ) + DB::EventInfo dbei; + dbei.cbBlob = -1; + db_event_get(m_hDbEvent, &dbei); + + m_hContact = DbGetAuthEventContact(&dbei); + + uint32_t uin = *(uint32_t*)dbei.pBlob; + char* nick = (char*)dbei.pBlob + sizeof(uint32_t) * 2; + char* first = nick + mir_strlen(nick) + 1; + char* last = first + mir_strlen(first) + 1; + char* email = last + mir_strlen(last) + 1; + + #ifdef _WINDOWS + SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0)); + SendMessage(m_hwnd, WM_SETICON, ICON_BIG, CallProtoService(dbei.szModule, PS_LOADICON, PLI_PROTOCOL | PLIF_LARGE, 0)); + #endif + + PROTOACCOUNT* acc = Proto_GetAccount(dbei.szModule); + + ptrW lastT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(last) : mir_a2u(last)); + ptrW firstT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(first) : mir_a2u(first)); + ptrW nickT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(nick) : mir_a2u(nick)); + ptrW emailT(dbei.flags & DBEF_UTF ? mir_utf8decodeW(email) : mir_a2u(email)); + + wchar_t name[128] = L""; + int off = 0; + if (firstT[0] && lastT[0]) + off = mir_snwprintf(name, L"%s %s", firstT.get(), lastT.get()); + else if (firstT[0]) + off = mir_snwprintf(name, L"%s", firstT.get()); + else if (lastT[0]) + off = mir_snwprintf(name, L"%s", lastT.get()); + if (nickT[0]) { + if (off) + mir_snwprintf(name + off, _countof(name) - off, L" (%s)", nickT.get()); + else + wcsncpy_s(name, nickT, _TRUNCATE); + } + if (!name[0]) + wcsncpy_s(name, TranslateT(""), _TRUNCATE); + + wchar_t hdr[256]; + if (uin && emailT[0]) + mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u (%s) on %s"), name, uin, emailT.get(), acc->tszAccountName); + else if (uin) + mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%u on %s"), name, uin, acc->tszAccountName); + else + mir_snwprintf(hdr, TranslateT("%s added you to the contact list\n%s on %s"), name, emailT[0] ? emailT.get() : TranslateT("(Unknown)"), acc->tszAccountName); + fldHeader.SetText(hdr); + + if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) + btnAdd.Hide(); + return true; + } + + bool OnApply() + { + Contact::AddByEvent(m_hDbEvent, m_hwnd); + return true; + } + + void OnDestroy() override + { + Button_FreeIcon_IcoLib(m_hwnd, IDC_ADD); + Button_FreeIcon_IcoLib(m_hwnd, IDC_DETAILS); + + #ifdef _WINDOWS + DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); + DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); + #endif + } + + void onClick_Add(CCtrlButton*) + { + Contact::AddByEvent(m_hDbEvent, m_hwnd); + + if (m_hContact == INVALID_CONTACT_ID || Contact::OnList(m_hContact)) + btnAdd.Hide(); + } + + void onClick_Details(CCtrlButton*) + { + CallService(MS_USERINFO_SHOWDIALOG, m_hContact, 0); + } +}; + +static INT_PTR ShowAddedWindow(WPARAM, LPARAM lParam) +{ + (new CAddedDlg(((CLISTEVENT *)lParam)->hDbEvent))->Show(); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define MS_AUTH_SHOWADDED "Auth/ShowAdded" +#define MS_AUTH_SHOWREQUEST "Auth/ShowRequest" + +static int AuthEventAdded(WPARAM, LPARAM lParam) +{ + wchar_t szTooltip[256]; + MEVENT hDbEvent = (MEVENT)lParam; + + DB::EventInfo dbei; + db_event_get(lParam, &dbei); + if (dbei.flags & (DBEF_SENT | DBEF_READ) || (dbei.eventType != EVENTTYPE_AUTHREQUEST && dbei.eventType != EVENTTYPE_ADDED)) + return 0; + + dbei.cbBlob = -1; + db_event_get(hDbEvent, &dbei); + + MCONTACT hContact = DbGetAuthEventContact(&dbei); + + CLISTEVENT cle = {}; + cle.hContact = hContact; + cle.szTooltip.w = szTooltip; + cle.flags = CLEF_UNICODE; + cle.lParam = lParam; + cle.hDbEvent = hDbEvent; + + ptrW szUid(Contact::GetInfo(CNF_UNIQUEID, hContact)); + + if (dbei.eventType == EVENTTYPE_AUTHREQUEST) { + Skin_PlaySound("AuthRequest"); + if (szUid) + mir_snwprintf(szTooltip, TranslateT("%s requests authorization"), szUid.get()); + else + mir_snwprintf(szTooltip, TranslateT("%u requests authorization"), *(uint32_t*)dbei.pBlob); + + cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_REQUEST); + cle.pszService = MS_AUTH_SHOWREQUEST; + g_clistApi.pfnAddEvent(&cle); + } + else if (dbei.eventType == EVENTTYPE_ADDED) { + Skin_PlaySound("AddedEvent"); + if (szUid) + mir_snwprintf(szTooltip, TranslateT("%s added you to their contact list"), szUid.get()); + else + mir_snwprintf(szTooltip, TranslateT("%u added you to their contact list"), *(uint32_t*)dbei.pBlob); + + cle.hIcon = Skin_LoadIcon(SKINICON_AUTH_ADD); + cle.pszService = MS_AUTH_SHOWADDED; + g_clistApi.pfnAddEvent(&cle); + } + return 0; +} + +static void CALLBACK LaunchAuth() +{ + HookEvent(ME_DB_EVENT_ADDED, AuthEventAdded); +} + +int LoadSendRecvAuthModule(void) +{ + CreateServiceFunction(MS_AUTH_SHOWREQUEST, ShowReqWindow); + CreateServiceFunction(MS_AUTH_SHOWADDED, ShowAddedWindow); + Miranda_WaitOnHandle(LaunchAuth); + + g_plugin.addSound("AuthRequest", LPGENW("Alerts"), LPGENW("Authorization request")); + g_plugin.addSound("AddedEvent", LPGENW("Alerts"), LPGENW("Added event")); + return 0; +} diff --git a/src/mir_app/src/button.cpp b/src/mir_app/src/button.cpp index 4a9b548c83..b97685716e 100644 --- a/src/mir_app/src/button.cpp +++ b/src/mir_app/src/button.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/chat_manager.cpp b/src/mir_app/src/chat_manager.cpp index 3e0f2b8b5d..7019e3a2a7 100644 --- a/src/mir_app/src/chat_manager.cpp +++ b/src/mir_app/src/chat_manager.cpp @@ -1,7 +1,7 @@ /* Chat module plugin for Miranda IM -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/chat_rtf.cpp b/src/mir_app/src/chat_rtf.cpp index 82545dc363..508fc1a86d 100644 --- a/src/mir_app/src/chat_rtf.cpp +++ b/src/mir_app/src/chat_rtf.cpp @@ -1,204 +1,204 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting -// tags and return plain text - -static wchar_t tszRtfBreaks[] = L" \\\n\r"; - -static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst) -{ - const wchar_t *pszText = Text; - int iIndex = 1; - - static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];"; - wchar_t szRed[10], szGreen[10], szBlue[10]; - - const wchar_t *p1 = wcsstr(pszText, L"\\colortbl"); - if (!p1) - return; - - const wchar_t *pEnd = wcschr(p1, '}'); - - const wchar_t *p2 = wcsstr(p1, L"\\red"); - - for (int i = 0; i < iCount; i++) - pDst[i] = -1; - - while (p2 && p2 < pEnd) { - if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) { - for (int i = 0; i < iCount; i++) { - if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue))) - pDst[i] = iIndex; - } - } - iIndex++; - p1 = p2; - p1++; - - p2 = wcsstr(p1, L"\\red"); - } -} - -static int GetRtfIndex(int iCol, int iCount, int *pIndex) -{ - for (int i = 0; i < iCount; i++) - if (pIndex[i] == iCol) - return i; - - return -1; -} - -int DoRtfToTags(CMStringW &pszText, int iNumColors, COLORREF *pColors) -{ - if (pszText.IsEmpty()) - return FALSE; - - // create an index of colors in the module and map them to - // corresponding colors in the RTF color table - int *pIndex = (int*)_alloca(iNumColors * sizeof(int)); - CreateColorMap(pszText, iNumColors, pColors, pIndex); - - // scan the file for rtf commands and remove or parse them - int idx = pszText.Find(L"\\pard"); - if (idx == -1) { - if ((idx = pszText.Find(L"\\ltrpar")) == -1) - return FALSE; - idx += 7; - } - else idx += 5; - - bool bInsideColor = false, bInsideUl = false; - CMStringW res; - - // iterate through all characters, if rtf control character found then take action - for (const wchar_t *p = pszText.GetString() + idx; *p;) { - switch (*p) { - case '\\': - if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters - res.AppendChar(p[1]); - p += 2; break; - } - if (p[1] == '~') { // non-breaking space - res.AppendChar(0xA0); - p += 2; break; - } - - if (!wcsncmp(p, L"\\cf", 3)) { // foreground color - int iCol = _wtoi(p + 3); - int iInd = GetRtfIndex(iCol, iNumColors, pIndex); - bInsideColor = iInd > 0; - } - else if (!wcsncmp(p, L"\\highlight", 10)) { //background color - wchar_t szTemp[20]; - int iCol = _wtoi(p + 10); - mir_snwprintf(szTemp, L"%d", iCol); - } - else if (!wcsncmp(p, L"\\line", 5)) { // soft line break; - res.AppendChar('\n'); - } - else if (!wcsncmp(p, L"\\endash", 7)) { - res.AppendChar(0x2013); - } - else if (!wcsncmp(p, L"\\emdash", 7)) { - res.AppendChar(0x2014); - } - else if (!wcsncmp(p, L"\\bullet", 7)) { - res.AppendChar(0x2022); - } - else if (!wcsncmp(p, L"\\ldblquote", 10)) { - res.AppendChar(0x201C); - } - else if (!wcsncmp(p, L"\\rdblquote", 10)) { - res.AppendChar(0x201D); - } - else if (!wcsncmp(p, L"\\lquote", 7)) { - res.AppendChar(0x2018); - } - else if (!wcsncmp(p, L"\\rquote", 7)) { - res.AppendChar(0x2019); - } - else if (!wcsncmp(p, L"\\b", 2)) { //bold - res.Append((p[2] != '0') ? L"[b]" : L"[/b]"); - } - else if (!wcsncmp(p, L"\\i", 2)) { // italics - res.Append((p[2] != '0') ? L"[i]" : L"[/i]"); - } - else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out - res.Append((p[7] != '0') ? L"[s]" : L"[/s]"); - } - else if (!wcsncmp(p, L"\\ul", 3)) { // underlined - if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) { - res.Append(L"[u]"); - bInsideUl = true; - } - else if (!wcsncmp(p + 3, L"none", 4)) { - if (bInsideUl) - res.Append(L"[/u]"); - bInsideUl = false; - } - } - else if (!wcsncmp(p, L"\\tab", 4)) { // tab - res.AppendChar('\t'); - } - else if (p[1] == '\'') { // special character - if (p[2] != ' ' && p[2] != '\\') { - wchar_t tmp[10], *t = tmp; - *t++ = p[2]; - if (p[3] != ' ' && p[3] != '\\') - *t++ = p[3]; - *t = 0; - - // convert string containing char in hex format to int. - wchar_t *stoppedHere; - res.AppendChar(wcstol(tmp, &stoppedHere, 16)); - } - } - - p++; // skip initial slash - p += wcscspn(p, tszRtfBreaks); - if (*p == ' ') - p++; - break; - - case '{': // other RTF control characters - case '}': - p++; - break; - - default: // other text that should not be touched - res.AppendChar(*p++); - break; - } - } - - if (bInsideUl) - res.Append(L"[/u]"); - - pszText = res; - return TRUE; -} +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "chat.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting +// tags and return plain text + +static wchar_t tszRtfBreaks[] = L" \\\n\r"; + +static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst) +{ + const wchar_t *pszText = Text; + int iIndex = 1; + + static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];"; + wchar_t szRed[10], szGreen[10], szBlue[10]; + + const wchar_t *p1 = wcsstr(pszText, L"\\colortbl"); + if (!p1) + return; + + const wchar_t *pEnd = wcschr(p1, '}'); + + const wchar_t *p2 = wcsstr(p1, L"\\red"); + + for (int i = 0; i < iCount; i++) + pDst[i] = -1; + + while (p2 && p2 < pEnd) { + if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) { + for (int i = 0; i < iCount; i++) { + if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue))) + pDst[i] = iIndex; + } + } + iIndex++; + p1 = p2; + p1++; + + p2 = wcsstr(p1, L"\\red"); + } +} + +static int GetRtfIndex(int iCol, int iCount, int *pIndex) +{ + for (int i = 0; i < iCount; i++) + if (pIndex[i] == iCol) + return i; + + return -1; +} + +int DoRtfToTags(CMStringW &pszText, int iNumColors, COLORREF *pColors) +{ + if (pszText.IsEmpty()) + return FALSE; + + // create an index of colors in the module and map them to + // corresponding colors in the RTF color table + int *pIndex = (int*)_alloca(iNumColors * sizeof(int)); + CreateColorMap(pszText, iNumColors, pColors, pIndex); + + // scan the file for rtf commands and remove or parse them + int idx = pszText.Find(L"\\pard"); + if (idx == -1) { + if ((idx = pszText.Find(L"\\ltrpar")) == -1) + return FALSE; + idx += 7; + } + else idx += 5; + + bool bInsideColor = false, bInsideUl = false; + CMStringW res; + + // iterate through all characters, if rtf control character found then take action + for (const wchar_t *p = pszText.GetString() + idx; *p;) { + switch (*p) { + case '\\': + if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters + res.AppendChar(p[1]); + p += 2; break; + } + if (p[1] == '~') { // non-breaking space + res.AppendChar(0xA0); + p += 2; break; + } + + if (!wcsncmp(p, L"\\cf", 3)) { // foreground color + int iCol = _wtoi(p + 3); + int iInd = GetRtfIndex(iCol, iNumColors, pIndex); + bInsideColor = iInd > 0; + } + else if (!wcsncmp(p, L"\\highlight", 10)) { //background color + wchar_t szTemp[20]; + int iCol = _wtoi(p + 10); + mir_snwprintf(szTemp, L"%d", iCol); + } + else if (!wcsncmp(p, L"\\line", 5)) { // soft line break; + res.AppendChar('\n'); + } + else if (!wcsncmp(p, L"\\endash", 7)) { + res.AppendChar(0x2013); + } + else if (!wcsncmp(p, L"\\emdash", 7)) { + res.AppendChar(0x2014); + } + else if (!wcsncmp(p, L"\\bullet", 7)) { + res.AppendChar(0x2022); + } + else if (!wcsncmp(p, L"\\ldblquote", 10)) { + res.AppendChar(0x201C); + } + else if (!wcsncmp(p, L"\\rdblquote", 10)) { + res.AppendChar(0x201D); + } + else if (!wcsncmp(p, L"\\lquote", 7)) { + res.AppendChar(0x2018); + } + else if (!wcsncmp(p, L"\\rquote", 7)) { + res.AppendChar(0x2019); + } + else if (!wcsncmp(p, L"\\b", 2)) { //bold + res.Append((p[2] != '0') ? L"[b]" : L"[/b]"); + } + else if (!wcsncmp(p, L"\\i", 2)) { // italics + res.Append((p[2] != '0') ? L"[i]" : L"[/i]"); + } + else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out + res.Append((p[7] != '0') ? L"[s]" : L"[/s]"); + } + else if (!wcsncmp(p, L"\\ul", 3)) { // underlined + if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) { + res.Append(L"[u]"); + bInsideUl = true; + } + else if (!wcsncmp(p + 3, L"none", 4)) { + if (bInsideUl) + res.Append(L"[/u]"); + bInsideUl = false; + } + } + else if (!wcsncmp(p, L"\\tab", 4)) { // tab + res.AppendChar('\t'); + } + else if (p[1] == '\'') { // special character + if (p[2] != ' ' && p[2] != '\\') { + wchar_t tmp[10], *t = tmp; + *t++ = p[2]; + if (p[3] != ' ' && p[3] != '\\') + *t++ = p[3]; + *t = 0; + + // convert string containing char in hex format to int. + wchar_t *stoppedHere; + res.AppendChar(wcstol(tmp, &stoppedHere, 16)); + } + } + + p++; // skip initial slash + p += wcscspn(p, tszRtfBreaks); + if (*p == ' ') + p++; + break; + + case '{': // other RTF control characters + case '}': + p++; + break; + + default: // other text that should not be touched + res.AppendChar(*p++); + break; + } + } + + if (bInsideUl) + res.Append(L"[/u]"); + + pszText = res; + return TRUE; +} diff --git a/src/mir_app/src/chat_svc.cpp b/src/mir_app/src/chat_svc.cpp index 0043df7ae6..021079ba46 100644 --- a/src/mir_app/src/chat_svc.cpp +++ b/src/mir_app/src/chat_svc.cpp @@ -1,7 +1,7 @@ /* Chat module plugin for Miranda IM -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/chat_tools.cpp b/src/mir_app/src/chat_tools.cpp index 4c2bcc7698..cb962183de 100644 --- a/src/mir_app/src/chat_tools.cpp +++ b/src/mir_app/src/chat_tools.cpp @@ -1,7 +1,7 @@ /* Chat module plugin for Miranda IM -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/chat_ui.cpp b/src/mir_app/src/chat_ui.cpp index 789f6fd075..4c5c21b6cd 100644 --- a/src/mir_app/src/chat_ui.cpp +++ b/src/mir_app/src/chat_ui.cpp @@ -1,220 +1,220 @@ -/* -Chat module plugin for Miranda IM - -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" - -CMOption g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true); -CMOption g_bChatTrayInactive(CHAT_MODULE, "TrayIconInactiveOnly", true); - -///////////////////////////////////////////////////////////////////////////////////////// -// Group chat - Events - -#define NR_GC_EVENTS 12 - -static UINT _eventorder[] = -{ - GC_EVENT_ACTION, - GC_EVENT_MESSAGE, - GC_EVENT_NICK, - GC_EVENT_JOIN, - GC_EVENT_PART, - GC_EVENT_TOPIC, - GC_EVENT_ADDSTATUS, - GC_EVENT_INFORMATION, - GC_EVENT_QUIT, - GC_EVENT_KICK, - GC_EVENT_NOTICE, - GC_EVENT_HIGHLIGHT -}; - -class CChatEventOptionDlg : public CDlgBase -{ - CCtrlCheck chkTray, chkPopup, chkRightClick; - CCtrlMButton btn1, btn2, btn3, btn4; - - void InvertColumn(int ctrlId) - { - int enabled = !IsDlgButtonChecked(m_hwnd, ctrlId); - for (int i = 0; i < _countof(_eventorder); i++) - CheckDlgButton(m_hwnd, ctrlId + i, enabled); - NotifyChange(); - } - -public: - CChatEventOptionDlg() : - CDlgBase(g_plugin, IDD_OPT_CHAT_EVENTS), - chkTray(this, IDC_TRAYONLYFORINACTIVE), - chkPopup(this, IDC_POPUPONLYFORINACTIVE), - chkRightClick(this, IDC_RIGHTCLICK), - btn1(this, IDC_ICON1, SKINICON_OTHER_POPUP, LPGEN("Popup")), - btn2(this, IDC_ICON2, SKINICON_OTHER_MIRANDA, LPGEN("Tray")), - btn3(this, IDC_ICON3, SKINICON_OTHER_SOUND, LPGEN("Sound")), - btn4(this, IDC_ICON4, SKINICON_EVENT_FILE, LPGEN("Log to file")) - { - CreateLink(chkTray, g_bChatTrayInactive); - CreateLink(chkPopup, g_bChatPopupInactive); - CreateLink(chkRightClick, g_chatApi.bRightClickFilter); - - btn1.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Popup); - btn2.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Tray); - btn3.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Sound); - btn4.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Log); - } - - bool OnInitDialog() override - { - btn1.MakeFlat(); btn2.MakeFlat(); btn3.MakeFlat(); btn4.MakeFlat(); - - uint32_t dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL); - uint32_t dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwSoundFlags = db_get_dw(0, CHAT_MODULE, "SoundFlags", GC_EVENT_HIGHLIGHT); - uint32_t dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL); - - for (int i = 0; i < _countof(_eventorder); i++) { - if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { - CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - } - CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_S1 + i, dwSoundFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); - } - return true; - } - - bool OnApply() override - { - uint32_t dwFilterFlags = 0, dwTrayFlags = 0, dwPopupFlags = 0, dwSoundFlags = 0, dwLogFlags = 0; - - for (int i = 0; i < _countof(_eventorder); i++) { - if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { - dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0); - dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0); - } - dwSoundFlags |= (IsDlgButtonChecked(m_hwnd, IDC_S1 + i) ? _eventorder[i] : 0); - dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0); - dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0); - } - db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags); - db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags); - db_set_dw(0, CHAT_MODULE, "SoundFlags", dwSoundFlags); - db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags); - db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags); - - LoadGlobalSettings(); - return true; - } - - void onClick_Popup(CCtrlButton *) { InvertColumn(IDC_P1); } - void onClick_Sound(CCtrlButton *) { InvertColumn(IDC_S1); } - void onClick_Tray(CCtrlButton *) { InvertColumn(IDC_T1); } - void onClick_Log(CCtrlButton *) { InvertColumn(IDC_L1); } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup options - -class COptPopupDlg : public CDlgBase -{ - CCtrlSpin spinTimeout; - CCtrlCheck chkRadio1, chkRadio2, chkRadio3; - CCtrlColor clrBack, clrText; - -public: - COptPopupDlg() : - CDlgBase(g_plugin, IDD_OPTIONSPOPUP), - clrBack(this, IDC_BKG), - clrText(this, IDC_TEXT), - chkRadio1(this, IDC_RADIO1), - chkRadio2(this, IDC_RADIO2), - chkRadio3(this, IDC_RADIO3), - spinTimeout(this, IDC_SPIN1, 100, -1) - { - chkRadio1.OnChange = chkRadio2.OnChange = chkRadio3.OnChange = Callback(this, &COptPopupDlg::onChange_Radio); - } - - bool OnInitDialog() override - { - clrBack.SetColor(g_Settings->crPUBkgColour); - clrText.SetColor(g_Settings->crPUTextColour); - - if (g_Settings->iPopupStyle == 2) - CheckDlgButton(m_hwnd, IDC_RADIO2, BST_CHECKED); - else if (g_Settings->iPopupStyle == 3) - CheckDlgButton(m_hwnd, IDC_RADIO3, BST_CHECKED); - else - CheckDlgButton(m_hwnd, IDC_RADIO1, BST_CHECKED); - onChange_Radio(0); - - spinTimeout.SetPosition(g_Settings->iPopupTimeout); - return true; - } - - bool OnApply() override - { - if (IsDlgButtonChecked(m_hwnd, IDC_RADIO2) == BST_CHECKED) - g_Settings->iPopupStyle = 2; - else if (IsDlgButtonChecked(m_hwnd, IDC_RADIO3) == BST_CHECKED) - g_Settings->iPopupStyle = 3; - else - g_Settings->iPopupStyle = 1; - db_set_b(0, CHAT_MODULE, "PopupStyle", g_Settings->iPopupStyle); - - db_set_w(0, CHAT_MODULE, "PopupTimeout", g_Settings->iPopupTimeout = spinTimeout.GetPosition()); - db_set_dw(0, CHAT_MODULE, "PopupColorBG", g_Settings->crPUBkgColour = clrBack.GetColor()); - db_set_dw(0, CHAT_MODULE, "PopupColorText", g_Settings->crPUTextColour = clrText.GetColor()); - return true; - } - - void onChange_Radio(CCtrlCheck *) - { - bool bStatus = chkRadio3.GetState(); - clrBack.Enable(bStatus); - clrText.Enable(bStatus); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -void ChatOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Group chats"); - odp.szTab.a = LPGEN("Events and filters"); - odp.pDialog = new CChatEventOptionDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.position = 910000002; - odp.szTitle.a = LPGEN("Group chats"); - odp.szGroup.a = LPGEN("Popups"); - odp.szTab.a = nullptr; - odp.pDialog = new COptPopupDlg(); - g_plugin.addOptions(wParam, &odp); -} +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "chat.h" + +CMOption g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true); +CMOption g_bChatTrayInactive(CHAT_MODULE, "TrayIconInactiveOnly", true); + +///////////////////////////////////////////////////////////////////////////////////////// +// Group chat - Events + +#define NR_GC_EVENTS 12 + +static UINT _eventorder[] = +{ + GC_EVENT_ACTION, + GC_EVENT_MESSAGE, + GC_EVENT_NICK, + GC_EVENT_JOIN, + GC_EVENT_PART, + GC_EVENT_TOPIC, + GC_EVENT_ADDSTATUS, + GC_EVENT_INFORMATION, + GC_EVENT_QUIT, + GC_EVENT_KICK, + GC_EVENT_NOTICE, + GC_EVENT_HIGHLIGHT +}; + +class CChatEventOptionDlg : public CDlgBase +{ + CCtrlCheck chkTray, chkPopup, chkRightClick; + CCtrlMButton btn1, btn2, btn3, btn4; + + void InvertColumn(int ctrlId) + { + int enabled = !IsDlgButtonChecked(m_hwnd, ctrlId); + for (int i = 0; i < _countof(_eventorder); i++) + CheckDlgButton(m_hwnd, ctrlId + i, enabled); + NotifyChange(); + } + +public: + CChatEventOptionDlg() : + CDlgBase(g_plugin, IDD_OPT_CHAT_EVENTS), + chkTray(this, IDC_TRAYONLYFORINACTIVE), + chkPopup(this, IDC_POPUPONLYFORINACTIVE), + chkRightClick(this, IDC_RIGHTCLICK), + btn1(this, IDC_ICON1, SKINICON_OTHER_POPUP, LPGEN("Popup")), + btn2(this, IDC_ICON2, SKINICON_OTHER_MIRANDA, LPGEN("Tray")), + btn3(this, IDC_ICON3, SKINICON_OTHER_SOUND, LPGEN("Sound")), + btn4(this, IDC_ICON4, SKINICON_EVENT_FILE, LPGEN("Log to file")) + { + CreateLink(chkTray, g_bChatTrayInactive); + CreateLink(chkPopup, g_bChatPopupInactive); + CreateLink(chkRightClick, g_chatApi.bRightClickFilter); + + btn1.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Popup); + btn2.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Tray); + btn3.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Sound); + btn4.OnClick = Callback(this, &CChatEventOptionDlg::onClick_Log); + } + + bool OnInitDialog() override + { + btn1.MakeFlat(); btn2.MakeFlat(); btn3.MakeFlat(); btn4.MakeFlat(); + + uint32_t dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL); + uint32_t dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT); + uint32_t dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT); + uint32_t dwSoundFlags = db_get_dw(0, CHAT_MODULE, "SoundFlags", GC_EVENT_HIGHLIGHT); + uint32_t dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL); + + for (int i = 0; i < _countof(_eventorder); i++) { + if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { + CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); + } + CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_S1 + i, dwSoundFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED); + } + return true; + } + + bool OnApply() override + { + uint32_t dwFilterFlags = 0, dwTrayFlags = 0, dwPopupFlags = 0, dwSoundFlags = 0, dwLogFlags = 0; + + for (int i = 0; i < _countof(_eventorder); i++) { + if (_eventorder[i] != GC_EVENT_HIGHLIGHT) { + dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0); + dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0); + } + dwSoundFlags |= (IsDlgButtonChecked(m_hwnd, IDC_S1 + i) ? _eventorder[i] : 0); + dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0); + dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0); + } + db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags); + db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags); + db_set_dw(0, CHAT_MODULE, "SoundFlags", dwSoundFlags); + db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags); + db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags); + + LoadGlobalSettings(); + return true; + } + + void onClick_Popup(CCtrlButton *) { InvertColumn(IDC_P1); } + void onClick_Sound(CCtrlButton *) { InvertColumn(IDC_S1); } + void onClick_Tray(CCtrlButton *) { InvertColumn(IDC_T1); } + void onClick_Log(CCtrlButton *) { InvertColumn(IDC_L1); } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////// +// Popup options + +class COptPopupDlg : public CDlgBase +{ + CCtrlSpin spinTimeout; + CCtrlCheck chkRadio1, chkRadio2, chkRadio3; + CCtrlColor clrBack, clrText; + +public: + COptPopupDlg() : + CDlgBase(g_plugin, IDD_OPTIONSPOPUP), + clrBack(this, IDC_BKG), + clrText(this, IDC_TEXT), + chkRadio1(this, IDC_RADIO1), + chkRadio2(this, IDC_RADIO2), + chkRadio3(this, IDC_RADIO3), + spinTimeout(this, IDC_SPIN1, 100, -1) + { + chkRadio1.OnChange = chkRadio2.OnChange = chkRadio3.OnChange = Callback(this, &COptPopupDlg::onChange_Radio); + } + + bool OnInitDialog() override + { + clrBack.SetColor(g_Settings->crPUBkgColour); + clrText.SetColor(g_Settings->crPUTextColour); + + if (g_Settings->iPopupStyle == 2) + CheckDlgButton(m_hwnd, IDC_RADIO2, BST_CHECKED); + else if (g_Settings->iPopupStyle == 3) + CheckDlgButton(m_hwnd, IDC_RADIO3, BST_CHECKED); + else + CheckDlgButton(m_hwnd, IDC_RADIO1, BST_CHECKED); + onChange_Radio(0); + + spinTimeout.SetPosition(g_Settings->iPopupTimeout); + return true; + } + + bool OnApply() override + { + if (IsDlgButtonChecked(m_hwnd, IDC_RADIO2) == BST_CHECKED) + g_Settings->iPopupStyle = 2; + else if (IsDlgButtonChecked(m_hwnd, IDC_RADIO3) == BST_CHECKED) + g_Settings->iPopupStyle = 3; + else + g_Settings->iPopupStyle = 1; + db_set_b(0, CHAT_MODULE, "PopupStyle", g_Settings->iPopupStyle); + + db_set_w(0, CHAT_MODULE, "PopupTimeout", g_Settings->iPopupTimeout = spinTimeout.GetPosition()); + db_set_dw(0, CHAT_MODULE, "PopupColorBG", g_Settings->crPUBkgColour = clrBack.GetColor()); + db_set_dw(0, CHAT_MODULE, "PopupColorText", g_Settings->crPUTextColour = clrText.GetColor()); + return true; + } + + void onChange_Radio(CCtrlCheck *) + { + bool bStatus = chkRadio3.GetState(); + clrBack.Enable(bStatus); + clrText.Enable(bStatus); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +void ChatOptionsInit(WPARAM wParam) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS; + odp.position = 910000000; + odp.szGroup.a = LPGEN("Message sessions"); + odp.szTitle.a = LPGEN("Group chats"); + odp.szTab.a = LPGEN("Events and filters"); + odp.pDialog = new CChatEventOptionDlg(); + g_plugin.addOptions(wParam, &odp); + + odp.position = 910000002; + odp.szTitle.a = LPGEN("Group chats"); + odp.szGroup.a = LPGEN("Popups"); + odp.szTab.a = nullptr; + odp.pDialog = new COptPopupDlg(); + g_plugin.addOptions(wParam, &odp); +} diff --git a/src/mir_app/src/clc.cpp b/src/mir_app/src/clc.cpp index d44673425e..0266b7bcf3 100644 --- a/src/mir_app/src/clc.cpp +++ b/src/mir_app/src/clc.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clc.h b/src/mir_app/src/clc.h index d57ec3b1ab..15eae7d75c 100644 --- a/src/mir_app/src/clc.h +++ b/src/mir_app/src/clc.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clccontact.cpp b/src/mir_app/src/clccontact.cpp index 3dbf578cd6..06639ba8d0 100644 --- a/src/mir_app/src/clccontact.cpp +++ b/src/mir_app/src/clccontact.cpp @@ -1,72 +1,72 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -extern HANDLE hGroupChangeEvent; - -MIR_APP_DLL(void) Clist_LoadContactTree(void) -{ - bool hideOffline = Clist::HideOffline; - for (auto &hContact : Contacts()) { - int status = Contact::GetStatus(hContact); - if ((!hideOffline || status != ID_STATUS_OFFLINE) && !Contact::IsHidden(hContact)) - Clist_ChangeContactIcon(hContact, g_clistApi.pfnIconFromStatusMode(Proto_GetBaseAccountName(hContact), status, hContact)); - } - Clist_EndRebuild(); -} - -MIR_APP_DLL(int) Clist_ContactChangeGroup(MCONTACT hContact, MGROUP hGroup) -{ - CLISTGROUPCHANGE grpChg = {}; - - if (hGroup == 0) - db_unset(hContact, "CList", "Group"); - else { - grpChg.pszNewName = Clist_GroupGetName(hGroup, nullptr); - db_set_ws(hContact, "CList", "Group", grpChg.pszNewName); - } - - NotifyEventHooks(hGroupChangeEvent, hContact, (LPARAM)&grpChg); - return 0; -} - -int fnSetHideOffline(int iValue) -{ - if (iValue == -1) // invert the current value - iValue = !Clist::HideOffline; - - switch (iValue) { - case 0: - case 1: - Clist::HideOffline = iValue; - break; - - default: - return -1; - } - Clist_LoadContactTree(); - return iValue; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "clc.h" + +extern HANDLE hGroupChangeEvent; + +MIR_APP_DLL(void) Clist_LoadContactTree(void) +{ + bool hideOffline = Clist::HideOffline; + for (auto &hContact : Contacts()) { + int status = Contact::GetStatus(hContact); + if ((!hideOffline || status != ID_STATUS_OFFLINE) && !Contact::IsHidden(hContact)) + Clist_ChangeContactIcon(hContact, g_clistApi.pfnIconFromStatusMode(Proto_GetBaseAccountName(hContact), status, hContact)); + } + Clist_EndRebuild(); +} + +MIR_APP_DLL(int) Clist_ContactChangeGroup(MCONTACT hContact, MGROUP hGroup) +{ + CLISTGROUPCHANGE grpChg = {}; + + if (hGroup == 0) + db_unset(hContact, "CList", "Group"); + else { + grpChg.pszNewName = Clist_GroupGetName(hGroup, nullptr); + db_set_ws(hContact, "CList", "Group", grpChg.pszNewName); + } + + NotifyEventHooks(hGroupChangeEvent, hContact, (LPARAM)&grpChg); + return 0; +} + +int fnSetHideOffline(int iValue) +{ + if (iValue == -1) // invert the current value + iValue = !Clist::HideOffline; + + switch (iValue) { + case 0: + case 1: + Clist::HideOffline = iValue; + break; + + default: + return -1; + } + Clist_LoadContactTree(); + return iValue; +} diff --git a/src/mir_app/src/clcfiledrop.cpp b/src/mir_app/src/clcfiledrop.cpp index 25501d7dac..840a046eae 100644 --- a/src/mir_app/src/clcfiledrop.cpp +++ b/src/mir_app/src/clcfiledrop.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clcidents.cpp b/src/mir_app/src/clcidents.cpp index 98ca64a451..83497cf50d 100644 --- a/src/mir_app/src/clcidents.cpp +++ b/src/mir_app/src/clcidents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clcitems.cpp b/src/mir_app/src/clcitems.cpp index 42c3f37d2f..eef6b57d56 100644 --- a/src/mir_app/src/clcitems.cpp +++ b/src/mir_app/src/clcitems.cpp @@ -1,730 +1,730 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -static ClcCacheEntry nullpce = {}; - -// routines for managing adding/removal of items in the list, including sorting - -ClcContact* fnAddItemToGroup(ClcGroup *group, int iAboveItem) -{ - ClcContact* newItem = g_clistApi.pfnCreateClcContact(); - newItem->type = CLCIT_DIVIDER; - newItem->flags = 0; - newItem->szText[0] = '\0'; - memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage)); - group->cl.insert(newItem, iAboveItem); - return newItem; -} - -ClcGroup* fnAddGroup(HWND hwnd, ClcData *dat, const wchar_t *szName, uint32_t flags, int groupId, int calcTotalMembers) -{ - dat->bNeedsResort = true; - if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS)) - return &dat->list; - - ClcGroup *group = &dat->list; - wchar_t *pNextField = NEWWSTR_ALLOCA(szName); - do { - wchar_t *pBackslash = wcschr(pNextField, '\\'), *pThisField = pNextField; - if (pBackslash == nullptr) { - pNextField = nullptr; - } - else { - *pBackslash = 0; - pNextField = pBackslash + 1; - } - - int i, compareResult = 1; - for (i = 0; i < group->cl.getCount(); i++) { - ClcContact *cc = group->cl[i]; - if (cc->type == CLCIT_CONTACT) - break; - if (cc->type != CLCIT_GROUP) - continue; - compareResult = mir_wstrcmp(pThisField, cc->szText); - if (compareResult == 0) { - if (pNextField == nullptr && flags != (uint32_t)-1) { - cc->groupId = (uint16_t)groupId; - group = cc->group; - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - group->groupId = groupId; - } - else group = cc->group; - break; - } - if (pNextField == nullptr && cc->groupId == 0) - break; - if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && cc->groupId > groupId) - break; - } - - if (compareResult) { // not found - if (groupId == 0) - return nullptr; - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - cc->type = CLCIT_GROUP; - mir_wstrncpy(cc->szText, pThisField, _countof(cc->szText)); - cc->groupId = (uint16_t)(pNextField ? 0 : groupId); - cc->group = new ClcGroup(10); - cc->group->parent = group; - group = cc->group; - - if (flags == (uint32_t)-1 || pNextField != nullptr) { - group->expanded = 0; - group->hideOffline = 0; - } - else { - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - } - group->groupId = pNextField ? 0 : groupId; - group->totalMembers = 0; - if (flags != (uint32_t)-1 && pNextField == nullptr && calcTotalMembers) { - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - for (auto &hContact : Contacts()) { - ClcCacheEntry *cache = Clist_GetCacheEntry(hContact); - if (!mir_wstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden)) - group->totalMembers++; - } - } - } - } while (pNextField); - return group; -} - -void fnFreeContact(ClcContact* p) -{ - if (p->type == CLCIT_GROUP) { - FreeGroup(p->group); - delete p->group; p->group = nullptr; - } - - mir_free(p); -} - -void FreeGroup(ClcGroup *group) -{ - if (!group) - return; - - for (auto &it : group->cl) - g_clistApi.pfnFreeContact(it); - - group->cl.destroy(); -} - -static int iInfoItemUniqueHandle = 0; -ClcContact* fnAddInfoItemToGroup(ClcGroup *group, int flags, const wchar_t *pszText) -{ - int i = 0; - - if (flags & CLCIIF_BELOWCONTACTS) - i = group->cl.getCount(); - else if (flags & CLCIIF_BELOWGROUPS) { - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type == CLCIT_CONTACT) - break; - } - else - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type != CLCIT_INFO) - break; - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle + 1); - if (iInfoItemUniqueHandle == 0) - ++iInfoItemUniqueHandle; - cc->type = CLCIT_INFO; - cc->flags = (uint8_t)flags; - cc->hContact = (MCONTACT)++iInfoItemUniqueHandle; - mir_wstrncpy(cc->szText, pszText, _countof(cc->szText)); - return cc; -} - -ClcContact* fnAddContactToGroup(ClcData *dat, ClcGroup *group, MCONTACT hContact) -{ - int i, index = -1; - - dat->bNeedsResort = true; - for (i = group->cl.getCount() - 1; i >= 0; i--) { - ClcContact *cc = group->cl[i]; - if (cc->hContact == hContact) - return cc; - - if (index == -1) - if (cc->type != CLCIT_INFO || !(cc->flags & CLCIIF_BELOWCONTACTS)) - index = i; - } - - char *szProto = Proto_GetBaseAccountName(hContact); - - ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); - replaceStrW(pce->tszGroup, nullptr); - - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, index + 1); - cc->type = CLCIT_CONTACT; - cc->iImage = Clist_GetContactIcon(hContact); - cc->hContact = hContact; - cc->pce = pce; - if (szProto != nullptr && !Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) - cc->flags |= CONTACTF_ONLINE; - uint16_t apparentMode = szProto != nullptr ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0; - if (apparentMode == ID_STATUS_OFFLINE) - cc->flags |= CONTACTF_INVISTO; - else if (apparentMode == ID_STATUS_ONLINE) - cc->flags |= CONTACTF_VISTO; - else if (apparentMode) - cc->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; - if (!Contact::OnList(hContact)) - cc->flags |= CONTACTF_NOTONLIST; - uint32_t idleMode = szProto != nullptr ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0; - if (idleMode) - cc->flags |= CONTACTF_IDLE; - mir_wstrncpy(cc->szText, Clist_GetContactDisplayName(hContact), _countof(cc->szText)); - return cc; -} - -void fnAddContactToTree(HWND hwnd, ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline) -{ - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - uint16_t status = ID_STATUS_OFFLINE; - char *szProto = Proto_GetBaseAccountName(hContact); - - dat->bNeedsResort = true; - if (style & CLS_NOHIDEOFFLINE) - checkHideOffline = 0; - if (checkHideOffline) - if (szProto != nullptr) - status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); - - int i; - uint32_t groupFlags; - ClcGroup *group; - ptrW tszGroup(Clist_GetGroup(hContact)); - if (tszGroup == nullptr) - group = &dat->list; - else { - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, (uint32_t)-1, 0, 0); - if (group == nullptr) { - if (!(style & CLS_HIDEEMPTYGROUPS)) - return; - - if (checkHideOffline && Clist_IsHiddenMode(dat, status)) { - for (i = 1;; i++) { - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - return; - - if (!mir_wstrcmp(szGroupName, tszGroup)) - break; - } - if (groupFlags & GROUPF_HIDEOFFLINE) - return; - } - for (i = 1;; i++) { - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - return; - - if (!mir_wstrcmp(szGroupName, tszGroup)) - break; - - size_t len = mir_wstrlen(szGroupName); - if (!wcsncmp(szGroupName, tszGroup, len) && tszGroup[len] == '\\') - g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1); - } - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, groupFlags, i, 1); - } - } - - if (checkHideOffline) { - if (Clist_IsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - if (updateTotalCount) - group->totalMembers++; - return; - } - } - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - if (updateTotalCount) - group->totalMembers++; -} - -MIR_APP_DLL(ClcGroup*) Clist_RemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) -{ - int iContact = group->cl.indexOf(contact); - if (iContact == -1) - return group; - - if (contact->type == CLCIT_CONTACT) { - if (updateTotalCount) - group->totalMembers--; - - g_clistApi.pfnInvalidateDisplayNameCacheEntry(contact->hContact); - } - - g_clistApi.pfnFreeContact(group->cl[iContact]); - group->cl.remove(iContact); - - if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.getCount() == 0 && group->parent != nullptr) - for (auto &cc : group->parent->cl) - if (cc->type == CLCIT_GROUP && cc->groupId == group->groupId) - return Clist_RemoveItemFromGroup(hwnd, group->parent, cc, 0); - - return group; -} - -MIR_APP_DLL(void) Clist_DeleteItemFromTree(HWND hwnd, MCONTACT hItem) -{ - ClcData *dat = (ClcData*)GetWindowLongPtr(hwnd, 0); - dat->bNeedsResort = true; - - // if a contact is found in our contact list, remove it from its group and detach from cache - ClcGroup *group; - ClcContact *contact; - if (Clist_FindItem(hwnd, dat, hItem, &contact, &group)) { - Clist_RemoveItemFromGroup(hwnd, group, contact, 1); - contact->pce = &nullpce; - return; - } - - // if we don't have this contact, simply try to update the number of contacts in a group - if (!IsHContactContact(hItem)) - return; - - ptrW wszGroup(Clist_GetGroup(hItem)); - if (wszGroup == nullptr) - return; - - // decrease member counts of all parent groups too - group = &dat->list; - int nameOffset = 0; - for (int i = 0;; i++) { - if (group->scanIndex == group->cl.getCount()) - break; - - ClcContact *cc = group->cl[i]; - if (cc->type == CLCIT_GROUP) { - size_t len = mir_wstrlen(cc->szText); - if (!wcsncmp(cc->szText, wszGroup.get() + nameOffset, len) && (wszGroup[nameOffset + len] == '\\' || wszGroup[nameOffset + len] == '\0')) { - group->totalMembers--; - if (wszGroup[nameOffset + len] == '\0') - break; - } - } - } -} - -int fnGetContactHiddenStatus(MCONTACT hContact, char*, ClcData*) -{ - return Contact::IsHidden(hContact); -} - -void fnRebuildEntireList(HWND hwnd, ClcData *dat) -{ - uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); - - dat->list.expanded = 1; - dat->list.hideOffline = db_get_b(0, "CLC", "HideOfflineRoot", 0) && (style & CLS_USEGROUPS); - dat->list.cl.destroy(); - dat->list.totalMembers = 0; - dat->selection = -1; - - for (int i = 1;; i++) { - uint32_t groupFlags; - wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); - if (szGroupName == nullptr) - break; - g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0); - } - - for (auto &hContact : Contacts()) { - int nHiddenStatus = g_clistApi.pfnGetContactHiddenStatus(hContact, nullptr, dat); - if (((style & CLS_SHOWHIDDEN) && nHiddenStatus != -1) || !nHiddenStatus) { - ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); - if (pce->szProto == nullptr) - continue; - - ClcGroup *group; - ptrW tszGroupName(Clist_GetGroup(hContact)); - if (tszGroupName == nullptr) - group = &dat->list; - else { - group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroupName, (uint32_t)-1, 0, 0); - if (group == nullptr && style & CLS_SHOWHIDDEN) - group = &dat->list; - } - - if (group != nullptr) { - group->totalMembers++; - - if (dat->bFilterSearch && dat->szQuickSearch[0] != '\0') { - wchar_t *name = Clist_GetContactDisplayName(hContact); - wchar_t *lowered_name = CharLowerW(NEWWSTR_ALLOCA(name)); - wchar_t *lowered_search = CharLowerW(NEWWSTR_ALLOCA(dat->szQuickSearch)); - - if (wcsstr(lowered_name, lowered_search)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto == nullptr) { - if (!Clist_IsHiddenMode(dat, ID_STATUS_OFFLINE) || g_clistApi.pfnIsVisibleContact(pce, group)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else if (!Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)) || g_clistApi.pfnIsVisibleContact(pce, group)) - g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - else g_clistApi.pfnAddContactToGroup(dat, group, hContact); - } - } - } - - if (style & CLS_HIDEEMPTYGROUPS) { - ClcGroup *group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - if (cc->group->cl.getCount() == 0) { - group = Clist_RemoveItemFromGroup(hwnd, group, cc, 0); - } - else { - group = cc->group; - group->scanIndex = 0; - } - continue; - } - group->scanIndex++; - } - } - - g_clistApi.pfnSortCLC(hwnd, dat, 0); - ExtraIcon_SetAll(); -} - -int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly) -{ - int count = group->cl.getCount(); - ClcGroup *topgroup = group; - - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if (group == topgroup) - break; - group = group->parent; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP && (!visibleOnly || cc->group->expanded)) { - group = cc->group; - group->scanIndex = 0; - count += group->cl.getCount(); - continue; - } - group->scanIndex++; - } - return count; -} - -static int __cdecl GroupSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - return mir_wstrcmpi(contact1[0]->szText, contact2[0]->szText); -} - -static int __cdecl ContactSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - int result = g_clistApi.pfnCompareContacts(contact1[0], contact2[0]); - if (result) - return result; - //nothing to distinguish them, so make sure they stay in the same order - return (int)((INT_PTR)contact2[0]->hContact - (INT_PTR)contact1[0]->hContact); -} - -static void InsertionSort(ClcContact **pContactArray, int nArray, int(*CompareProc) (const void *, const void *)) -{ - int i, j; - ClcContact* testElement; - - for (i = 1; i < nArray; i++) { - if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) { - testElement = pContactArray[i]; - for (j = i - 2; j >= 0; j--) - if (CompareProc(&pContactArray[j], &testElement) <= 0) - break; - j++; - memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j)); - pContactArray[j] = testElement; - } - } -} - -static void SortGroup(ClcData *dat, ClcGroup *group, int useInsertionSort) -{ - int i, sortCount; - - for (i = group->cl.getCount() - 1; i >= 0; i--) { - if (group->cl[i]->type == CLCIT_DIVIDER) { - mir_free(group->cl[i]); - group->cl.remove(i); - } - } - - for (i = 0; i < group->cl.getCount(); i++) - if (group->cl[i]->type != CLCIT_INFO) - break; - if (i > group->cl.getCount() - 2) - return; - if (group->cl[i]->type == CLCIT_GROUP) { - if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) { - for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) - if (group->cl[i + sortCount]->type != CLCIT_GROUP) - break; - qsort(group->cl.getArray() + i, sortCount, sizeof(void*), GroupSortProc); - i = i + sortCount; - } - for (; i < group->cl.getCount(); i++) - if (group->cl[i]->type == CLCIT_CONTACT) - break; - if (group->cl.getCount() - i < 2) - return; - } - for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) - if (group->cl[i + sortCount]->type != CLCIT_CONTACT) - break; - if (useInsertionSort) - InsertionSort(group->cl.getArray() + i, sortCount, ContactSortProc); - else - qsort(group->cl.getArray() + i, sortCount, sizeof(void*), ContactSortProc); - if (dat->exStyle & CLS_EX_DIVIDERONOFF) { - int prevContactOnline = 0; - for (i = 0; i < group->cl.getCount(); i++) { - if (group->cl[i]->type != CLCIT_CONTACT) - continue; - if (group->cl[i]->flags & CONTACTF_ONLINE) - prevContactOnline = 1; - else { - if (prevContactOnline) { - ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); - cc->type = CLCIT_DIVIDER; - mir_wstrcpy(cc->szText, TranslateT("Offline")); - } - break; - } - } - } -} - -void fnSortCLC(HWND hwnd, ClcData *dat, int useInsertionSort) -{ - ClcGroup *group = &dat->list; - - if (dat->bNeedsResort) { - MCONTACT hSelItem; - ClcContact *selcontact; - if (g_clistApi.pfnGetRowByIndex(dat, dat->selection, &selcontact, nullptr) == -1) - hSelItem = 0; - else - hSelItem = Clist_ContactToHItem(selcontact); - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - continue; - } - group->scanIndex++; - } - - if (hSelItem) { - ClcGroup *selgroup; - if (Clist_FindItem(hwnd, dat, hSelItem, &selcontact, &selgroup)) - dat->selection = g_clistApi.pfnGetRowsPriorTo(&dat->list, selgroup, selgroup->cl.indexOf(selcontact)); - } - - g_clistApi.pfnRecalcScrollBar(hwnd, dat); - } - dat->bNeedsResort = false; - g_clistApi.pfnInvalidateRect(hwnd, nullptr, FALSE); -} - -struct SavedContactState_t -{ - MCONTACT hContact; - uint16_t iExtraImage[EXTRA_ICON_COUNT]; - int checked; -}; - -struct SavedGroupState_t -{ - int groupId, expanded; -}; - -struct SavedInfoState_t -{ - int parentId; - ClcContact contact; -}; - -MIR_APP_DLL(void) Clist_SaveStateAndRebuildList(HWND hwnd, ClcData *dat) -{ - Clist_HideInfoTip(dat); - KillTimer(hwnd, TIMERID_INFOTIP); - KillTimer(hwnd, TIMERID_RENAME); - Clist_EndRename(dat, 1); - - dat->bLockScrollbar = true; - - OBJLIST saveContact(10, NumericKeySortT); - OBJLIST saveGroup(100, NumericKeySortT); - OBJLIST saveInfo(10, NumericKeySortT); - - dat->bNeedsResort = true; - ClcGroup *group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - - SavedGroupState_t *p = new SavedGroupState_t; - p->groupId = group->groupId; - p->expanded = group->expanded; - saveGroup.insert(p); - continue; - } - else if (cc->type == CLCIT_CONTACT) { - SavedContactState_t *p = new SavedContactState_t; - p->hContact = cc->hContact; - memcpy(p->iExtraImage, cc->iExtraImage, sizeof(p->iExtraImage)); - p->checked = cc->flags & CONTACTF_CHECKED; - saveContact.insert(p); - } - else if (cc->type == CLCIT_INFO) { - SavedInfoState_t *p = new SavedInfoState_t; - p->parentId = (group->parent == nullptr) ? -1 : group->groupId; - p->contact = *cc; - saveInfo.insert(p); - } - group->scanIndex++; - } - - FreeGroup(&dat->list); - g_clistApi.pfnRebuildEntireList(hwnd, dat); - - group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.getCount()) { - if ((group = group->parent) == nullptr) - break; - group->scanIndex++; - continue; - } - - ClcContact *cc = group->cl[group->scanIndex]; - if (cc->type == CLCIT_GROUP) { - group = cc->group; - group->scanIndex = 0; - - SavedGroupState_t tmp, *p; - tmp.groupId = group->groupId; - if ((p = saveGroup.find(&tmp)) != nullptr) - group->expanded = p->expanded; - continue; - } - else if (cc->type == CLCIT_CONTACT) { - SavedContactState_t tmp, *p; - tmp.hContact = cc->hContact; - if ((p = saveContact.find(&tmp)) != nullptr) { - memcpy(cc->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage)); - if (p->checked) - cc->flags |= CONTACTF_CHECKED; - } - } - - group->scanIndex++; - } - - for (auto &it : saveInfo) { - if (it->parentId == -1) - group = &dat->list; - else { - ClcContact *contact; - if (!Clist_FindItem(hwnd, dat, it->parentId | HCONTACT_ISGROUP, &contact)) - continue; - group = contact->group; - } - - ClcContact *cc = g_clistApi.pfnAddInfoItemToGroup(group, it->contact.flags, L""); - *cc = it->contact; - } - - dat->bLockScrollbar = false; - Clist_RecalculateGroupCheckboxes(dat); - - g_clistApi.pfnRecalcScrollBar(hwnd, dat); - - NMCLISTCONTROL nm; - nm.hdr.code = CLN_LISTREBUILT; - nm.hdr.hwndFrom = hwnd; - nm.hdr.idFrom = GetDlgCtrlID(hwnd); - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "clc.h" + +static ClcCacheEntry nullpce = {}; + +// routines for managing adding/removal of items in the list, including sorting + +ClcContact* fnAddItemToGroup(ClcGroup *group, int iAboveItem) +{ + ClcContact* newItem = g_clistApi.pfnCreateClcContact(); + newItem->type = CLCIT_DIVIDER; + newItem->flags = 0; + newItem->szText[0] = '\0'; + memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage)); + group->cl.insert(newItem, iAboveItem); + return newItem; +} + +ClcGroup* fnAddGroup(HWND hwnd, ClcData *dat, const wchar_t *szName, uint32_t flags, int groupId, int calcTotalMembers) +{ + dat->bNeedsResort = true; + if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS)) + return &dat->list; + + ClcGroup *group = &dat->list; + wchar_t *pNextField = NEWWSTR_ALLOCA(szName); + do { + wchar_t *pBackslash = wcschr(pNextField, '\\'), *pThisField = pNextField; + if (pBackslash == nullptr) { + pNextField = nullptr; + } + else { + *pBackslash = 0; + pNextField = pBackslash + 1; + } + + int i, compareResult = 1; + for (i = 0; i < group->cl.getCount(); i++) { + ClcContact *cc = group->cl[i]; + if (cc->type == CLCIT_CONTACT) + break; + if (cc->type != CLCIT_GROUP) + continue; + compareResult = mir_wstrcmp(pThisField, cc->szText); + if (compareResult == 0) { + if (pNextField == nullptr && flags != (uint32_t)-1) { + cc->groupId = (uint16_t)groupId; + group = cc->group; + group->expanded = (flags & GROUPF_EXPANDED) != 0; + group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; + group->groupId = groupId; + } + else group = cc->group; + break; + } + if (pNextField == nullptr && cc->groupId == 0) + break; + if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && cc->groupId > groupId) + break; + } + + if (compareResult) { // not found + if (groupId == 0) + return nullptr; + + ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); + cc->type = CLCIT_GROUP; + mir_wstrncpy(cc->szText, pThisField, _countof(cc->szText)); + cc->groupId = (uint16_t)(pNextField ? 0 : groupId); + cc->group = new ClcGroup(10); + cc->group->parent = group; + group = cc->group; + + if (flags == (uint32_t)-1 || pNextField != nullptr) { + group->expanded = 0; + group->hideOffline = 0; + } + else { + group->expanded = (flags & GROUPF_EXPANDED) != 0; + group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; + } + group->groupId = pNextField ? 0 : groupId; + group->totalMembers = 0; + if (flags != (uint32_t)-1 && pNextField == nullptr && calcTotalMembers) { + uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); + for (auto &hContact : Contacts()) { + ClcCacheEntry *cache = Clist_GetCacheEntry(hContact); + if (!mir_wstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden)) + group->totalMembers++; + } + } + } + } while (pNextField); + return group; +} + +void fnFreeContact(ClcContact* p) +{ + if (p->type == CLCIT_GROUP) { + FreeGroup(p->group); + delete p->group; p->group = nullptr; + } + + mir_free(p); +} + +void FreeGroup(ClcGroup *group) +{ + if (!group) + return; + + for (auto &it : group->cl) + g_clistApi.pfnFreeContact(it); + + group->cl.destroy(); +} + +static int iInfoItemUniqueHandle = 0; +ClcContact* fnAddInfoItemToGroup(ClcGroup *group, int flags, const wchar_t *pszText) +{ + int i = 0; + + if (flags & CLCIIF_BELOWCONTACTS) + i = group->cl.getCount(); + else if (flags & CLCIIF_BELOWGROUPS) { + for (; i < group->cl.getCount(); i++) + if (group->cl[i]->type == CLCIT_CONTACT) + break; + } + else + for (; i < group->cl.getCount(); i++) + if (group->cl[i]->type != CLCIT_INFO) + break; + + ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); + iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle + 1); + if (iInfoItemUniqueHandle == 0) + ++iInfoItemUniqueHandle; + cc->type = CLCIT_INFO; + cc->flags = (uint8_t)flags; + cc->hContact = (MCONTACT)++iInfoItemUniqueHandle; + mir_wstrncpy(cc->szText, pszText, _countof(cc->szText)); + return cc; +} + +ClcContact* fnAddContactToGroup(ClcData *dat, ClcGroup *group, MCONTACT hContact) +{ + int i, index = -1; + + dat->bNeedsResort = true; + for (i = group->cl.getCount() - 1; i >= 0; i--) { + ClcContact *cc = group->cl[i]; + if (cc->hContact == hContact) + return cc; + + if (index == -1) + if (cc->type != CLCIT_INFO || !(cc->flags & CLCIIF_BELOWCONTACTS)) + index = i; + } + + char *szProto = Proto_GetBaseAccountName(hContact); + + ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); + replaceStrW(pce->tszGroup, nullptr); + + ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, index + 1); + cc->type = CLCIT_CONTACT; + cc->iImage = Clist_GetContactIcon(hContact); + cc->hContact = hContact; + cc->pce = pce; + if (szProto != nullptr && !Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) + cc->flags |= CONTACTF_ONLINE; + uint16_t apparentMode = szProto != nullptr ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0; + if (apparentMode == ID_STATUS_OFFLINE) + cc->flags |= CONTACTF_INVISTO; + else if (apparentMode == ID_STATUS_ONLINE) + cc->flags |= CONTACTF_VISTO; + else if (apparentMode) + cc->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; + if (!Contact::OnList(hContact)) + cc->flags |= CONTACTF_NOTONLIST; + uint32_t idleMode = szProto != nullptr ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0; + if (idleMode) + cc->flags |= CONTACTF_IDLE; + mir_wstrncpy(cc->szText, Clist_GetContactDisplayName(hContact), _countof(cc->szText)); + return cc; +} + +void fnAddContactToTree(HWND hwnd, ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline) +{ + uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); + uint16_t status = ID_STATUS_OFFLINE; + char *szProto = Proto_GetBaseAccountName(hContact); + + dat->bNeedsResort = true; + if (style & CLS_NOHIDEOFFLINE) + checkHideOffline = 0; + if (checkHideOffline) + if (szProto != nullptr) + status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); + + int i; + uint32_t groupFlags; + ClcGroup *group; + ptrW tszGroup(Clist_GetGroup(hContact)); + if (tszGroup == nullptr) + group = &dat->list; + else { + group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, (uint32_t)-1, 0, 0); + if (group == nullptr) { + if (!(style & CLS_HIDEEMPTYGROUPS)) + return; + + if (checkHideOffline && Clist_IsHiddenMode(dat, status)) { + for (i = 1;; i++) { + wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); + if (szGroupName == nullptr) + return; + + if (!mir_wstrcmp(szGroupName, tszGroup)) + break; + } + if (groupFlags & GROUPF_HIDEOFFLINE) + return; + } + for (i = 1;; i++) { + wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); + if (szGroupName == nullptr) + return; + + if (!mir_wstrcmp(szGroupName, tszGroup)) + break; + + size_t len = mir_wstrlen(szGroupName); + if (!wcsncmp(szGroupName, tszGroup, len) && tszGroup[len] == '\\') + g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1); + } + group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroup, groupFlags, i, 1); + } + } + + if (checkHideOffline) { + if (Clist_IsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { + if (updateTotalCount) + group->totalMembers++; + return; + } + } + g_clistApi.pfnAddContactToGroup(dat, group, hContact); + if (updateTotalCount) + group->totalMembers++; +} + +MIR_APP_DLL(ClcGroup*) Clist_RemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) +{ + int iContact = group->cl.indexOf(contact); + if (iContact == -1) + return group; + + if (contact->type == CLCIT_CONTACT) { + if (updateTotalCount) + group->totalMembers--; + + g_clistApi.pfnInvalidateDisplayNameCacheEntry(contact->hContact); + } + + g_clistApi.pfnFreeContact(group->cl[iContact]); + group->cl.remove(iContact); + + if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.getCount() == 0 && group->parent != nullptr) + for (auto &cc : group->parent->cl) + if (cc->type == CLCIT_GROUP && cc->groupId == group->groupId) + return Clist_RemoveItemFromGroup(hwnd, group->parent, cc, 0); + + return group; +} + +MIR_APP_DLL(void) Clist_DeleteItemFromTree(HWND hwnd, MCONTACT hItem) +{ + ClcData *dat = (ClcData*)GetWindowLongPtr(hwnd, 0); + dat->bNeedsResort = true; + + // if a contact is found in our contact list, remove it from its group and detach from cache + ClcGroup *group; + ClcContact *contact; + if (Clist_FindItem(hwnd, dat, hItem, &contact, &group)) { + Clist_RemoveItemFromGroup(hwnd, group, contact, 1); + contact->pce = &nullpce; + return; + } + + // if we don't have this contact, simply try to update the number of contacts in a group + if (!IsHContactContact(hItem)) + return; + + ptrW wszGroup(Clist_GetGroup(hItem)); + if (wszGroup == nullptr) + return; + + // decrease member counts of all parent groups too + group = &dat->list; + int nameOffset = 0; + for (int i = 0;; i++) { + if (group->scanIndex == group->cl.getCount()) + break; + + ClcContact *cc = group->cl[i]; + if (cc->type == CLCIT_GROUP) { + size_t len = mir_wstrlen(cc->szText); + if (!wcsncmp(cc->szText, wszGroup.get() + nameOffset, len) && (wszGroup[nameOffset + len] == '\\' || wszGroup[nameOffset + len] == '\0')) { + group->totalMembers--; + if (wszGroup[nameOffset + len] == '\0') + break; + } + } + } +} + +int fnGetContactHiddenStatus(MCONTACT hContact, char*, ClcData*) +{ + return Contact::IsHidden(hContact); +} + +void fnRebuildEntireList(HWND hwnd, ClcData *dat) +{ + uint32_t style = GetWindowLongPtr(hwnd, GWL_STYLE); + + dat->list.expanded = 1; + dat->list.hideOffline = db_get_b(0, "CLC", "HideOfflineRoot", 0) && (style & CLS_USEGROUPS); + dat->list.cl.destroy(); + dat->list.totalMembers = 0; + dat->selection = -1; + + for (int i = 1;; i++) { + uint32_t groupFlags; + wchar_t *szGroupName = Clist_GroupGetName(i, &groupFlags); + if (szGroupName == nullptr) + break; + g_clistApi.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0); + } + + for (auto &hContact : Contacts()) { + int nHiddenStatus = g_clistApi.pfnGetContactHiddenStatus(hContact, nullptr, dat); + if (((style & CLS_SHOWHIDDEN) && nHiddenStatus != -1) || !nHiddenStatus) { + ClcCacheEntry *pce = Clist_GetCacheEntry(hContact); + if (pce->szProto == nullptr) + continue; + + ClcGroup *group; + ptrW tszGroupName(Clist_GetGroup(hContact)); + if (tszGroupName == nullptr) + group = &dat->list; + else { + group = g_clistApi.pfnAddGroup(hwnd, dat, tszGroupName, (uint32_t)-1, 0, 0); + if (group == nullptr && style & CLS_SHOWHIDDEN) + group = &dat->list; + } + + if (group != nullptr) { + group->totalMembers++; + + if (dat->bFilterSearch && dat->szQuickSearch[0] != '\0') { + wchar_t *name = Clist_GetContactDisplayName(hContact); + wchar_t *lowered_name = CharLowerW(NEWWSTR_ALLOCA(name)); + wchar_t *lowered_search = CharLowerW(NEWWSTR_ALLOCA(dat->szQuickSearch)); + + if (wcsstr(lowered_name, lowered_search)) + g_clistApi.pfnAddContactToGroup(dat, group, hContact); + } + else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { + char *szProto = Proto_GetBaseAccountName(hContact); + if (szProto == nullptr) { + if (!Clist_IsHiddenMode(dat, ID_STATUS_OFFLINE) || g_clistApi.pfnIsVisibleContact(pce, group)) + g_clistApi.pfnAddContactToGroup(dat, group, hContact); + } + else if (!Clist_IsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE)) || g_clistApi.pfnIsVisibleContact(pce, group)) + g_clistApi.pfnAddContactToGroup(dat, group, hContact); + } + else g_clistApi.pfnAddContactToGroup(dat, group, hContact); + } + } + } + + if (style & CLS_HIDEEMPTYGROUPS) { + ClcGroup *group = &dat->list; + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.getCount()) { + if ((group = group->parent) == nullptr) + break; + group->scanIndex++; + continue; + } + + ClcContact *cc = group->cl[group->scanIndex]; + if (cc->type == CLCIT_GROUP) { + if (cc->group->cl.getCount() == 0) { + group = Clist_RemoveItemFromGroup(hwnd, group, cc, 0); + } + else { + group = cc->group; + group->scanIndex = 0; + } + continue; + } + group->scanIndex++; + } + } + + g_clistApi.pfnSortCLC(hwnd, dat, 0); + ExtraIcon_SetAll(); +} + +int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly) +{ + int count = group->cl.getCount(); + ClcGroup *topgroup = group; + + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.getCount()) { + if (group == topgroup) + break; + group = group->parent; + group->scanIndex++; + continue; + } + + ClcContact *cc = group->cl[group->scanIndex]; + if (cc->type == CLCIT_GROUP && (!visibleOnly || cc->group->expanded)) { + group = cc->group; + group->scanIndex = 0; + count += group->cl.getCount(); + continue; + } + group->scanIndex++; + } + return count; +} + +static int __cdecl GroupSortProc(const void* p1, const void* p2) +{ + ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; + + return mir_wstrcmpi(contact1[0]->szText, contact2[0]->szText); +} + +static int __cdecl ContactSortProc(const void* p1, const void* p2) +{ + ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; + + int result = g_clistApi.pfnCompareContacts(contact1[0], contact2[0]); + if (result) + return result; + //nothing to distinguish them, so make sure they stay in the same order + return (int)((INT_PTR)contact2[0]->hContact - (INT_PTR)contact1[0]->hContact); +} + +static void InsertionSort(ClcContact **pContactArray, int nArray, int(*CompareProc) (const void *, const void *)) +{ + int i, j; + ClcContact* testElement; + + for (i = 1; i < nArray; i++) { + if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) { + testElement = pContactArray[i]; + for (j = i - 2; j >= 0; j--) + if (CompareProc(&pContactArray[j], &testElement) <= 0) + break; + j++; + memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j)); + pContactArray[j] = testElement; + } + } +} + +static void SortGroup(ClcData *dat, ClcGroup *group, int useInsertionSort) +{ + int i, sortCount; + + for (i = group->cl.getCount() - 1; i >= 0; i--) { + if (group->cl[i]->type == CLCIT_DIVIDER) { + mir_free(group->cl[i]); + group->cl.remove(i); + } + } + + for (i = 0; i < group->cl.getCount(); i++) + if (group->cl[i]->type != CLCIT_INFO) + break; + if (i > group->cl.getCount() - 2) + return; + if (group->cl[i]->type == CLCIT_GROUP) { + if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) { + for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) + if (group->cl[i + sortCount]->type != CLCIT_GROUP) + break; + qsort(group->cl.getArray() + i, sortCount, sizeof(void*), GroupSortProc); + i = i + sortCount; + } + for (; i < group->cl.getCount(); i++) + if (group->cl[i]->type == CLCIT_CONTACT) + break; + if (group->cl.getCount() - i < 2) + return; + } + for (sortCount = 0; i + sortCount < group->cl.getCount(); sortCount++) + if (group->cl[i + sortCount]->type != CLCIT_CONTACT) + break; + if (useInsertionSort) + InsertionSort(group->cl.getArray() + i, sortCount, ContactSortProc); + else + qsort(group->cl.getArray() + i, sortCount, sizeof(void*), ContactSortProc); + if (dat->exStyle & CLS_EX_DIVIDERONOFF) { + int prevContactOnline = 0; + for (i = 0; i < group->cl.getCount(); i++) { + if (group->cl[i]->type != CLCIT_CONTACT) + continue; + if (group->cl[i]->flags & CONTACTF_ONLINE) + prevContactOnline = 1; + else { + if (prevContactOnline) { + ClcContact *cc = g_clistApi.pfnAddItemToGroup(group, i); + cc->type = CLCIT_DIVIDER; + mir_wstrcpy(cc->szText, TranslateT("Offline")); + } + break; + } + } + } +} + +void fnSortCLC(HWND hwnd, ClcData *dat, int useInsertionSort) +{ + ClcGroup *group = &dat->list; + + if (dat->bNeedsResort) { + MCONTACT hSelItem; + ClcContact *selcontact; + if (g_clistApi.pfnGetRowByIndex(dat, dat->selection, &selcontact, nullptr) == -1) + hSelItem = 0; + else + hSelItem = Clist_ContactToHItem(selcontact); + group->scanIndex = 0; + SortGroup(dat, group, useInsertionSort); + for (;;) { + if (group->scanIndex == group->cl.getCount()) { + if ((group = group->parent) == nullptr) + break; + group->scanIndex++; + continue; + } + + ClcContact *cc = group->cl[group->scanIndex]; + if (cc->type == CLCIT_GROUP) { + group = cc->group; + group->scanIndex = 0; + SortGroup(dat, group, useInsertionSort); + continue; + } + group->scanIndex++; + } + + if (hSelItem) { + ClcGroup *selgroup; + if (Clist_FindItem(hwnd, dat, hSelItem, &selcontact, &selgroup)) + dat->selection = g_clistApi.pfnGetRowsPriorTo(&dat->list, selgroup, selgroup->cl.indexOf(selcontact)); + } + + g_clistApi.pfnRecalcScrollBar(hwnd, dat); + } + dat->bNeedsResort = false; + g_clistApi.pfnInvalidateRect(hwnd, nullptr, FALSE); +} + +struct SavedContactState_t +{ + MCONTACT hContact; + uint16_t iExtraImage[EXTRA_ICON_COUNT]; + int checked; +}; + +struct SavedGroupState_t +{ + int groupId, expanded; +}; + +struct SavedInfoState_t +{ + int parentId; + ClcContact contact; +}; + +MIR_APP_DLL(void) Clist_SaveStateAndRebuildList(HWND hwnd, ClcData *dat) +{ + Clist_HideInfoTip(dat); + KillTimer(hwnd, TIMERID_INFOTIP); + KillTimer(hwnd, TIMERID_RENAME); + Clist_EndRename(dat, 1); + + dat->bLockScrollbar = true; + + OBJLIST saveContact(10, NumericKeySortT); + OBJLIST saveGroup(100, NumericKeySortT); + OBJLIST saveInfo(10, NumericKeySortT); + + dat->bNeedsResort = true; + ClcGroup *group = &dat->list; + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.getCount()) { + if ((group = group->parent) == nullptr) + break; + group->scanIndex++; + continue; + } + + ClcContact *cc = group->cl[group->scanIndex]; + if (cc->type == CLCIT_GROUP) { + group = cc->group; + group->scanIndex = 0; + + SavedGroupState_t *p = new SavedGroupState_t; + p->groupId = group->groupId; + p->expanded = group->expanded; + saveGroup.insert(p); + continue; + } + else if (cc->type == CLCIT_CONTACT) { + SavedContactState_t *p = new SavedContactState_t; + p->hContact = cc->hContact; + memcpy(p->iExtraImage, cc->iExtraImage, sizeof(p->iExtraImage)); + p->checked = cc->flags & CONTACTF_CHECKED; + saveContact.insert(p); + } + else if (cc->type == CLCIT_INFO) { + SavedInfoState_t *p = new SavedInfoState_t; + p->parentId = (group->parent == nullptr) ? -1 : group->groupId; + p->contact = *cc; + saveInfo.insert(p); + } + group->scanIndex++; + } + + FreeGroup(&dat->list); + g_clistApi.pfnRebuildEntireList(hwnd, dat); + + group = &dat->list; + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.getCount()) { + if ((group = group->parent) == nullptr) + break; + group->scanIndex++; + continue; + } + + ClcContact *cc = group->cl[group->scanIndex]; + if (cc->type == CLCIT_GROUP) { + group = cc->group; + group->scanIndex = 0; + + SavedGroupState_t tmp, *p; + tmp.groupId = group->groupId; + if ((p = saveGroup.find(&tmp)) != nullptr) + group->expanded = p->expanded; + continue; + } + else if (cc->type == CLCIT_CONTACT) { + SavedContactState_t tmp, *p; + tmp.hContact = cc->hContact; + if ((p = saveContact.find(&tmp)) != nullptr) { + memcpy(cc->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage)); + if (p->checked) + cc->flags |= CONTACTF_CHECKED; + } + } + + group->scanIndex++; + } + + for (auto &it : saveInfo) { + if (it->parentId == -1) + group = &dat->list; + else { + ClcContact *contact; + if (!Clist_FindItem(hwnd, dat, it->parentId | HCONTACT_ISGROUP, &contact)) + continue; + group = contact->group; + } + + ClcContact *cc = g_clistApi.pfnAddInfoItemToGroup(group, it->contact.flags, L""); + *cc = it->contact; + } + + dat->bLockScrollbar = false; + Clist_RecalculateGroupCheckboxes(dat); + + g_clistApi.pfnRecalcScrollBar(hwnd, dat); + + NMCLISTCONTROL nm; + nm.hdr.code = CLN_LISTREBUILT; + nm.hdr.hwndFrom = hwnd; + nm.hdr.idFrom = GetDlgCtrlID(hwnd); + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM)& nm); +} diff --git a/src/mir_app/src/clcmsgs.cpp b/src/mir_app/src/clcmsgs.cpp index 49267f5cdb..5b92d1a0f0 100644 --- a/src/mir_app/src/clcmsgs.cpp +++ b/src/mir_app/src/clcmsgs.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clcutils.cpp b/src/mir_app/src/clcutils.cpp index c3849572cb..1cd238ffc6 100644 --- a/src/mir_app/src/clcutils.cpp +++ b/src/mir_app/src/clcutils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clistcore.cpp b/src/mir_app/src/clistcore.cpp index 27372c0101..ebfa53dda6 100644 --- a/src/mir_app/src/clistcore.cpp +++ b/src/mir_app/src/clistcore.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clistevents.cpp b/src/mir_app/src/clistevents.cpp index 0533b5b1c7..4e5455ebc2 100644 --- a/src/mir_app/src/clistevents.cpp +++ b/src/mir_app/src/clistevents.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clistgroups.cpp b/src/mir_app/src/clistgroups.cpp index e91b5686d1..79d320515b 100644 --- a/src/mir_app/src/clistgroups.cpp +++ b/src/mir_app/src/clistgroups.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clistmod.cpp b/src/mir_app/src/clistmod.cpp index 6a205db148..19b9a8a860 100644 --- a/src/mir_app/src/clistmod.cpp +++ b/src/mir_app/src/clistmod.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clistopts.cpp b/src/mir_app/src/clistopts.cpp index 169ee85b6b..8df408c45e 100644 --- a/src/mir_app/src/clistopts.cpp +++ b/src/mir_app/src/clistopts.cpp @@ -1,162 +1,162 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "clc.h" - -CMOption Clist::UseGroups(MODULENAME, "UseGroups", true); -CMOption Clist::HideOffline(MODULENAME, "HideOffline", false); -CMOption Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true); -CMOption Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true); -CMOption Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true); -CMOption Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false); -CMOption Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true); - -CMOption Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus()); -CMOption Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false); - -CMOption Clist::FilterSearch("CLC", "FilterSearch", false); -CMOption Clist::OfflineModes("CLC", "OfflineModes", MODEF_OFFLINE); - -struct -{ - uint32_t style; - wchar_t *szDescr; -} -static const offlineValues[] = -{ - { MODEF_OFFLINE, LPGENW("Offline") }, - { PF2_ONLINE, LPGENW("Online") }, - { PF2_SHORTAWAY, LPGENW("Away") }, - { PF2_LONGAWAY, LPGENW("Not available") }, - { PF2_LIGHTDND, LPGENW("Occupied") }, - { PF2_HEAVYDND, LPGENW("Do not disturb") }, - { PF2_FREECHAT, LPGENW("Free for chat") }, - { PF2_INVISIBLE, LPGENW("Invisible") } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -class ClistCommonOptsDlg : public CDlgBase -{ - CCtrlSpin blink; - CCtrlCheck chkUseGroups, chkHideOffline, chkConfirmDelete, chkHideEmptyGroups, chkRemoveTempContacts, chkEnableIconBlink, chkFilterSearch; - CCtrlCheck chkAlwaysStatus, chkOneClick, chkEnableTrayBlink; - CCtrlTreeView hideStatuses; - -public: - ClistCommonOptsDlg() : - CDlgBase(g_plugin, IDD_OPT_CLIST), - blink(this, IDC_BLINKSPIN, 0x3FFF, 250), - hideStatuses(this, IDC_HIDEOFFLINEOPTS), - chkOneClick(this, IDC_ONECLK), - chkUseGroups(this, IDC_USEGROUPS), - chkHideOffline(this, IDC_HIDEOFFLINE), - chkFilterSearch(this, IDC_FILTER_SEARCH), - chkAlwaysStatus(this, IDC_ALWAYSSTATUS), - chkConfirmDelete(this, IDC_CONFIRMDELETE), - chkHideEmptyGroups(this, IDC_HIDEEMPTYGROUPS), - chkEnableIconBlink(this, IDC_ENABLE_ICON_BLINK), - chkEnableTrayBlink(this, IDC_ENABLE_TRAY_BLINK), - chkRemoveTempContacts(this, IDC_REMOVETEMP) - { - chkEnableTrayBlink.OnChange = Callback(this, &ClistCommonOptsDlg::onChange_TrayBlink); - - CreateLink(chkOneClick, Clist::Tray1Click); - CreateLink(chkUseGroups, Clist::UseGroups); - CreateLink(chkHideOffline, Clist::HideOffline); - CreateLink(chkFilterSearch, Clist::FilterSearch); - CreateLink(chkAlwaysStatus, Clist::TrayAlwaysStatus); - CreateLink(chkConfirmDelete, Clist::ConfirmDelete); - CreateLink(chkHideEmptyGroups, Clist::HideEmptyGroups); - CreateLink(chkEnableIconBlink, Clist::EnableIconBlink); - CreateLink(chkEnableTrayBlink, Clist::EnableTrayFlash); - CreateLink(chkRemoveTempContacts, Clist::RemoveTempContacts); - } - - bool OnInitDialog() override - { - blink.SetPosition(db_get_w(0, MODULENAME, "IconFlashTime", 550)); - - SetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE, - GetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); - - int style = Clist::OfflineModes; - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - for (auto &it : offlineValues) { - tvis.item.lParam = it.style; - tvis.item.pszText = TranslateW(it.szDescr); - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.state = INDEXTOSTATEIMAGEMASK((style & it.style) != 0 ? 2 : 1); - hideStatuses.InsertItem(&tvis); - } - return true; - } - - bool OnApply() override - { - db_set_w(0, MODULENAME, "IconFlashTime", blink.GetPosition()); - - uint32_t flags = 0; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.hItem = hideStatuses.GetRoot(); - while (tvi.hItem) { - hideStatuses.GetItem(&tvi); - if ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2) - flags |= tvi.lParam; - tvi.hItem = hideStatuses.GetNextSibling(tvi.hItem); - } - Clist::OfflineModes = flags; - - Clist_ClcOptionsChanged(); - Clist_LoadContactTree(); - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - return true; - } - - void onChange_TrayBlink(CCtrlCheck*) - { - bool bEnabled = chkEnableTrayBlink.IsChecked(); - EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKTIME), bEnabled); - EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKSPIN), bEnabled); - } -}; - -int ClcOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -200000000; - odp.szGroup.a = LPGEN("Contact list"); - odp.szTitle.a = LPGEN("Common"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new ClistCommonOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "clc.h" + +CMOption Clist::UseGroups(MODULENAME, "UseGroups", true); +CMOption Clist::HideOffline(MODULENAME, "HideOffline", false); +CMOption Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true); +CMOption Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true); +CMOption Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true); +CMOption Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false); +CMOption Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true); + +CMOption Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus()); +CMOption Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false); + +CMOption Clist::FilterSearch("CLC", "FilterSearch", false); +CMOption Clist::OfflineModes("CLC", "OfflineModes", MODEF_OFFLINE); + +struct +{ + uint32_t style; + wchar_t *szDescr; +} +static const offlineValues[] = +{ + { MODEF_OFFLINE, LPGENW("Offline") }, + { PF2_ONLINE, LPGENW("Online") }, + { PF2_SHORTAWAY, LPGENW("Away") }, + { PF2_LONGAWAY, LPGENW("Not available") }, + { PF2_LIGHTDND, LPGENW("Occupied") }, + { PF2_HEAVYDND, LPGENW("Do not disturb") }, + { PF2_FREECHAT, LPGENW("Free for chat") }, + { PF2_INVISIBLE, LPGENW("Invisible") } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class ClistCommonOptsDlg : public CDlgBase +{ + CCtrlSpin blink; + CCtrlCheck chkUseGroups, chkHideOffline, chkConfirmDelete, chkHideEmptyGroups, chkRemoveTempContacts, chkEnableIconBlink, chkFilterSearch; + CCtrlCheck chkAlwaysStatus, chkOneClick, chkEnableTrayBlink; + CCtrlTreeView hideStatuses; + +public: + ClistCommonOptsDlg() : + CDlgBase(g_plugin, IDD_OPT_CLIST), + blink(this, IDC_BLINKSPIN, 0x3FFF, 250), + hideStatuses(this, IDC_HIDEOFFLINEOPTS), + chkOneClick(this, IDC_ONECLK), + chkUseGroups(this, IDC_USEGROUPS), + chkHideOffline(this, IDC_HIDEOFFLINE), + chkFilterSearch(this, IDC_FILTER_SEARCH), + chkAlwaysStatus(this, IDC_ALWAYSSTATUS), + chkConfirmDelete(this, IDC_CONFIRMDELETE), + chkHideEmptyGroups(this, IDC_HIDEEMPTYGROUPS), + chkEnableIconBlink(this, IDC_ENABLE_ICON_BLINK), + chkEnableTrayBlink(this, IDC_ENABLE_TRAY_BLINK), + chkRemoveTempContacts(this, IDC_REMOVETEMP) + { + chkEnableTrayBlink.OnChange = Callback(this, &ClistCommonOptsDlg::onChange_TrayBlink); + + CreateLink(chkOneClick, Clist::Tray1Click); + CreateLink(chkUseGroups, Clist::UseGroups); + CreateLink(chkHideOffline, Clist::HideOffline); + CreateLink(chkFilterSearch, Clist::FilterSearch); + CreateLink(chkAlwaysStatus, Clist::TrayAlwaysStatus); + CreateLink(chkConfirmDelete, Clist::ConfirmDelete); + CreateLink(chkHideEmptyGroups, Clist::HideEmptyGroups); + CreateLink(chkEnableIconBlink, Clist::EnableIconBlink); + CreateLink(chkEnableTrayBlink, Clist::EnableTrayFlash); + CreateLink(chkRemoveTempContacts, Clist::RemoveTempContacts); + } + + bool OnInitDialog() override + { + blink.SetPosition(db_get_w(0, MODULENAME, "IconFlashTime", 550)); + + SetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE, + GetWindowLongPtr(hideStatuses.GetHwnd(), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES); + + int style = Clist::OfflineModes; + + TVINSERTSTRUCT tvis; + tvis.hParent = nullptr; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; + for (auto &it : offlineValues) { + tvis.item.lParam = it.style; + tvis.item.pszText = TranslateW(it.szDescr); + tvis.item.stateMask = TVIS_STATEIMAGEMASK; + tvis.item.state = INDEXTOSTATEIMAGEMASK((style & it.style) != 0 ? 2 : 1); + hideStatuses.InsertItem(&tvis); + } + return true; + } + + bool OnApply() override + { + db_set_w(0, MODULENAME, "IconFlashTime", blink.GetPosition()); + + uint32_t flags = 0; + + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; + tvi.hItem = hideStatuses.GetRoot(); + while (tvi.hItem) { + hideStatuses.GetItem(&tvi); + if ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2) + flags |= tvi.lParam; + tvi.hItem = hideStatuses.GetNextSibling(tvi.hItem); + } + Clist::OfflineModes = flags; + + Clist_ClcOptionsChanged(); + Clist_LoadContactTree(); + Clist_InitAutoRebuild(g_clistApi.hwndContactTree); + return true; + } + + void onChange_TrayBlink(CCtrlCheck*) + { + bool bEnabled = chkEnableTrayBlink.IsChecked(); + EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKTIME), bEnabled); + EnableWindow(GetDlgItem(m_hwnd, IDC_BLINKSPIN), bEnabled); + } +}; + +int ClcOptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = -200000000; + odp.szGroup.a = LPGEN("Contact list"); + odp.szTitle.a = LPGEN("Common"); + odp.flags = ODPF_BOLDGROUPS; + odp.pDialog = new ClistCommonOptsDlg(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/mir_app/src/clistsettings.cpp b/src/mir_app/src/clistsettings.cpp index 6b160e0046..4d3e60b9d4 100644 --- a/src/mir_app/src/clistsettings.cpp +++ b/src/mir_app/src/clistsettings.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clisttray.cpp b/src/mir_app/src/clisttray.cpp index 9d4137748b..c10100a1b3 100644 --- a/src/mir_app/src/clisttray.cpp +++ b/src/mir_app/src/clisttray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/clui.cpp b/src/mir_app/src/clui.cpp index e5201fb244..39015a1819 100644 --- a/src/mir_app/src/clui.cpp +++ b/src/mir_app/src/clui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/cluiservices.cpp b/src/mir_app/src/cluiservices.cpp index dc17de4dfb..ae6d2c66de 100644 --- a/src/mir_app/src/cluiservices.cpp +++ b/src/mir_app/src/cluiservices.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/colorchooser.cpp b/src/mir_app/src/colorchooser.cpp index 2931d9f3cf..6a93e61f54 100644 --- a/src/mir_app/src/colorchooser.cpp +++ b/src/mir_app/src/colorchooser.cpp @@ -1,7 +1,7 @@ /* Chat module plugin for Miranda IM -Copyright 2000-12 Miranda IM, 2012-22 Miranda NG team, +Copyright 2000-12 Miranda IM, 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/contacts.cpp b/src/mir_app/src/contacts.cpp index 3a4c32b18b..8cec2a273a 100644 --- a/src/mir_app/src/contacts.cpp +++ b/src/mir_app/src/contacts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/copyright.h b/src/mir_app/src/copyright.h index 29b0f97206..641ed66f54 100644 --- a/src/mir_app/src/copyright.h +++ b/src/mir_app/src/copyright.h @@ -1,2 +1,2 @@ - -#define LEGAL_COPYRIGHT "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" + +#define LEGAL_COPYRIGHT "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" diff --git a/src/mir_app/src/database.cpp b/src/mir_app/src/database.cpp index d20361c3b4..149377bc27 100644 --- a/src/mir_app/src/database.cpp +++ b/src/mir_app/src/database.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/database.h b/src/mir_app/src/database.h index 11d5b1b58b..07f32d6bad 100644 --- a/src/mir_app/src/database.h +++ b/src/mir_app/src/database.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright 2012-22 Miranda NG team, +Copyright 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/db_events.cpp b/src/mir_app/src/db_events.cpp index 68049037f1..b6bce698d4 100644 --- a/src/mir_app/src/db_events.cpp +++ b/src/mir_app/src/db_events.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/db_ini.cpp b/src/mir_app/src/db_ini.cpp index 8ce8f88385..6953345787 100644 --- a/src/mir_app/src/db_ini.cpp +++ b/src/mir_app/src/db_ini.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/db_intf.cpp b/src/mir_app/src/db_intf.cpp index fc9467d8fd..a422ab7b25 100644 --- a/src/mir_app/src/db_intf.cpp +++ b/src/mir_app/src/db_intf.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/db_upgrade.cpp b/src/mir_app/src/db_upgrade.cpp index c96f638125..2b6f6d7755 100644 --- a/src/mir_app/src/db_upgrade.cpp +++ b/src/mir_app/src/db_upgrade.cpp @@ -1,71 +1,71 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define CONVERT_MSG LPGEN("This database is in the old format that isn't supported anymore. Press Yes to convert it to the new format or No to return") -#define MISSING_DB_MSG LPGEN("To open this database you need to install the Dbx_sqlite plugin. Click Yes to download it from Miranda NG's site or No to return") -#define MISSING_PLUG_MSG LPGEN("To open this database you need to install the Import plugin. Click Yes to download it from Miranda NG's site or No to return") - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(MDatabaseCommon*) DB::Upgrade(const wchar_t *profile) -{ - DATABASELINK *pLink = GetDatabasePlugin("dbx_sqlite"); - if (pLink == nullptr) { - if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_DB_MSG), L"Miranda NG", MB_YESNO)) - Utils_OpenUrl("https://miranda-ng.org/p/Dbx_sqlite"); - return nullptr; - } - - if (!Profile_GetSettingInt(L"Database/SilentUpgrade")) - if (IDYES != MessageBoxW(nullptr, TranslateT(CONVERT_MSG), L"Miranda NG", MB_YESNO)) - return nullptr; - - int errorCode; - CMStringW wszBackupName(profile); - wszBackupName.Append(L".bak"); - DeleteFileW(wszBackupName); - if (!MoveFileW(profile, wszBackupName)) { - uint32_t dwError = GetLastError(); - CMStringW wszError(FORMAT, TranslateT("Cannot move old profile '%s' to '%s': error %d"), profile, wszBackupName.c_str(), dwError); - MessageBoxW(nullptr, wszError, L"Miranda NG", MB_ICONERROR | MB_OK); - return nullptr; - } - - if ((errorCode = pLink->makeDatabase(profile)) != 0) { - MessageBoxW(nullptr, CMStringW(FORMAT, TranslateT("Attempt to create database '%s' failed with error code %d"), profile, errorCode), L"Miranda NG", MB_ICONERROR | MB_OK); -LBL_Error: - DeleteFileW(profile); - MoveFileW(wszBackupName, profile); - return nullptr; - } - - if (SetServiceModePlugin("import", 1) != ERROR_SUCCESS) { - if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_PLUG_MSG), L"Miranda NG", MB_YESNO)) - Utils_OpenUrl("https://miranda-ng.org/p/Import"); - goto LBL_Error; - } - - return pLink->Load(profile, false); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#define CONVERT_MSG LPGEN("This database is in the old format that isn't supported anymore. Press Yes to convert it to the new format or No to return") +#define MISSING_DB_MSG LPGEN("To open this database you need to install the Dbx_sqlite plugin. Click Yes to download it from Miranda NG's site or No to return") +#define MISSING_PLUG_MSG LPGEN("To open this database you need to install the Import plugin. Click Yes to download it from Miranda NG's site or No to return") + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(MDatabaseCommon*) DB::Upgrade(const wchar_t *profile) +{ + DATABASELINK *pLink = GetDatabasePlugin("dbx_sqlite"); + if (pLink == nullptr) { + if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_DB_MSG), L"Miranda NG", MB_YESNO)) + Utils_OpenUrl("https://miranda-ng.org/p/Dbx_sqlite"); + return nullptr; + } + + if (!Profile_GetSettingInt(L"Database/SilentUpgrade")) + if (IDYES != MessageBoxW(nullptr, TranslateT(CONVERT_MSG), L"Miranda NG", MB_YESNO)) + return nullptr; + + int errorCode; + CMStringW wszBackupName(profile); + wszBackupName.Append(L".bak"); + DeleteFileW(wszBackupName); + if (!MoveFileW(profile, wszBackupName)) { + uint32_t dwError = GetLastError(); + CMStringW wszError(FORMAT, TranslateT("Cannot move old profile '%s' to '%s': error %d"), profile, wszBackupName.c_str(), dwError); + MessageBoxW(nullptr, wszError, L"Miranda NG", MB_ICONERROR | MB_OK); + return nullptr; + } + + if ((errorCode = pLink->makeDatabase(profile)) != 0) { + MessageBoxW(nullptr, CMStringW(FORMAT, TranslateT("Attempt to create database '%s' failed with error code %d"), profile, errorCode), L"Miranda NG", MB_ICONERROR | MB_OK); +LBL_Error: + DeleteFileW(profile); + MoveFileW(wszBackupName, profile); + return nullptr; + } + + if (SetServiceModePlugin("import", 1) != ERROR_SUCCESS) { + if (IDYES == MessageBoxW(nullptr, TranslateT(MISSING_PLUG_MSG), L"Miranda NG", MB_YESNO)) + Utils_OpenUrl("https://miranda-ng.org/p/Import"); + goto LBL_Error; + } + + return pLink->Load(profile, false); +} diff --git a/src/mir_app/src/db_util.cpp b/src/mir_app/src/db_util.cpp index fc23a10050..6fd23cb4f5 100644 --- a/src/mir_app/src/db_util.cpp +++ b/src/mir_app/src/db_util.cpp @@ -1,129 +1,129 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "profilemanager.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Profile_GetPathA(size_t cbLen, char *pszDest) -{ - if (!pszDest || !cbLen) - return 1; - - strncpy_s(pszDest, cbLen, _T2A(g_profileDir), _TRUNCATE); - return 0; -} - -MIR_APP_DLL(int) Profile_GetPathW(size_t cbLen, wchar_t *pwszDest) -{ - if (!pwszDest || !cbLen) - return 1; - - wcsncpy_s(pwszDest, cbLen, g_profileDir, _TRUNCATE); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Profile_GetNameA(size_t cbLen, char *pszDest) -{ - if (!cbLen || !pszDest) - return 1; - - strncpy_s(pszDest, cbLen, ptrA(makeFileName(g_profileName)), _TRUNCATE); - return 0; -} - -MIR_APP_DLL(int) Profile_GetNameW(size_t cbLen, wchar_t *pwszDest) -{ - if (!cbLen || !pwszDest) - return 1; - - wcsncpy_s(pwszDest, cbLen, g_profileName, _TRUNCATE); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Profile_GetSetting(const wchar_t *pwszSetting, wchar_t *pwszBuf, size_t cbLen, const wchar_t *pwszDefault) -{ - if (pwszSetting == nullptr) { - *pwszBuf = 0; - return false; - } - - if (pwszDefault == nullptr) - pwszDefault = L""; - - wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); - - wchar_t *p = wcschr(pBuf, '/'); - if (p) { - *p = 0; p++; - GetPrivateProfileStringW(pBuf, p, pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); - } - else GetPrivateProfileStringW(pBuf, L"", pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); - - return pwszBuf[0] != 0; -} - -MIR_APP_DLL(int) Profile_GetSettingInt(const wchar_t *pwszSetting, int iDefault) -{ - if (pwszSetting == nullptr) - return iDefault; - - wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); - - wchar_t *p = wcschr(pBuf, '/'); - if (p) { - *p = 0; p++; - return GetPrivateProfileIntW(pBuf, p, iDefault, mirandabootini); - } - - return GetPrivateProfileIntW(pBuf, L"", iDefault, mirandabootini); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Profile_SetDefault(const wchar_t *pwszPath) -{ - extern wchar_t* g_defaultProfile; - replaceStrW(g_defaultProfile, pwszPath); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Profile_CheckOpened(const wchar_t *pwszProfileName) -{ - CMStringW wszPhysName(pwszProfileName); - wszPhysName.Replace(L"\\", L"_"); - wszPhysName.Insert(0, L"Global\\"); - - HANDLE hMutex = ::OpenMutexW(SYNCHRONIZE, false, wszPhysName); - if (hMutex == nullptr) - return false; - - ::CloseHandle(hMutex); - return true; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "profilemanager.h" + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Profile_GetPathA(size_t cbLen, char *pszDest) +{ + if (!pszDest || !cbLen) + return 1; + + strncpy_s(pszDest, cbLen, _T2A(g_profileDir), _TRUNCATE); + return 0; +} + +MIR_APP_DLL(int) Profile_GetPathW(size_t cbLen, wchar_t *pwszDest) +{ + if (!pwszDest || !cbLen) + return 1; + + wcsncpy_s(pwszDest, cbLen, g_profileDir, _TRUNCATE); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Profile_GetNameA(size_t cbLen, char *pszDest) +{ + if (!cbLen || !pszDest) + return 1; + + strncpy_s(pszDest, cbLen, ptrA(makeFileName(g_profileName)), _TRUNCATE); + return 0; +} + +MIR_APP_DLL(int) Profile_GetNameW(size_t cbLen, wchar_t *pwszDest) +{ + if (!cbLen || !pwszDest) + return 1; + + wcsncpy_s(pwszDest, cbLen, g_profileName, _TRUNCATE); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(bool) Profile_GetSetting(const wchar_t *pwszSetting, wchar_t *pwszBuf, size_t cbLen, const wchar_t *pwszDefault) +{ + if (pwszSetting == nullptr) { + *pwszBuf = 0; + return false; + } + + if (pwszDefault == nullptr) + pwszDefault = L""; + + wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); + + wchar_t *p = wcschr(pBuf, '/'); + if (p) { + *p = 0; p++; + GetPrivateProfileStringW(pBuf, p, pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); + } + else GetPrivateProfileStringW(pBuf, L"", pwszDefault, pwszBuf, (uint32_t)cbLen, mirandabootini); + + return pwszBuf[0] != 0; +} + +MIR_APP_DLL(int) Profile_GetSettingInt(const wchar_t *pwszSetting, int iDefault) +{ + if (pwszSetting == nullptr) + return iDefault; + + wchar_t *pBuf = NEWWSTR_ALLOCA(pwszSetting); + + wchar_t *p = wcschr(pBuf, '/'); + if (p) { + *p = 0; p++; + return GetPrivateProfileIntW(pBuf, p, iDefault, mirandabootini); + } + + return GetPrivateProfileIntW(pBuf, L"", iDefault, mirandabootini); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(void) Profile_SetDefault(const wchar_t *pwszPath) +{ + extern wchar_t* g_defaultProfile; + replaceStrW(g_defaultProfile, pwszPath); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(bool) Profile_CheckOpened(const wchar_t *pwszProfileName) +{ + CMStringW wszPhysName(pwszProfileName); + wszPhysName.Replace(L"\\", L"_"); + wszPhysName.Insert(0, L"Global\\"); + + HANDLE hMutex = ::OpenMutexW(SYNCHRONIZE, false, wszPhysName); + if (hMutex == nullptr) + return false; + + ::CloseHandle(hMutex); + return true; +} diff --git a/src/mir_app/src/descbutton.cpp b/src/mir_app/src/descbutton.cpp index ad198bdaf1..4fdd74f856 100644 --- a/src/mir_app/src/descbutton.cpp +++ b/src/mir_app/src/descbutton.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, Copyright (c) 2007 Artem Shpynov all portions of this codebase are copyrighted to the people diff --git a/src/mir_app/src/dll_sniffer.cpp b/src/mir_app/src/dll_sniffer.cpp index 037ca328e5..7f2aca1955 100644 --- a/src/mir_app/src/dll_sniffer.cpp +++ b/src/mir_app/src/dll_sniffer.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/ei_baseIcon.cpp b/src/mir_app/src/ei_baseIcon.cpp index 5ddce65bab..7244d20edb 100644 --- a/src/mir_app/src/ei_baseIcon.cpp +++ b/src/mir_app/src/ei_baseIcon.cpp @@ -1,70 +1,70 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -BaseExtraIcon::BaseExtraIcon(const char *name, const wchar_t *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM param) : - ExtraIcon(name), - m_OnClick(OnClick), - m_onClickParam(param), - m_tszDescription(mir_wstrdup(description)), - m_hDescIcon(descIcon) -{ - if (!IcoLib_IsManaged((HICON)descIcon)) - m_hDescIcon = IcoLib_GetIconHandle((const char *)descIcon); - - m_id = registeredExtraIcons.getCount() + 1; -} - -BaseExtraIcon::~BaseExtraIcon() -{ -} - -void BaseExtraIcon::setOnClick(MIRANDAHOOKPARAM pFunc, LPARAM pParam) -{ - m_OnClick = pFunc; - m_onClickParam = pParam; -} - -const wchar_t* BaseExtraIcon::getDescription() const -{ - return TranslateW_LP(m_tszDescription, m_pPlugin); -} - -HANDLE BaseExtraIcon::getDescIcon() const -{ - return m_hDescIcon; -} - -void BaseExtraIcon::onClick(MCONTACT hContact) -{ - if (m_OnClick != nullptr) - m_OnClick(hContact, (LPARAM)ConvertToClistSlot(m_slot), m_onClickParam); -} - -int BaseExtraIcon::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) -{ - if (m_pParent) - return m_pParent->ClistSetExtraIcon(hContact, hImage); - return Clist_SetExtraIcon(hContact, m_slot, hImage); -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "extraicons.h" + +BaseExtraIcon::BaseExtraIcon(const char *name, const wchar_t *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM param) : + ExtraIcon(name), + m_OnClick(OnClick), + m_onClickParam(param), + m_tszDescription(mir_wstrdup(description)), + m_hDescIcon(descIcon) +{ + if (!IcoLib_IsManaged((HICON)descIcon)) + m_hDescIcon = IcoLib_GetIconHandle((const char *)descIcon); + + m_id = registeredExtraIcons.getCount() + 1; +} + +BaseExtraIcon::~BaseExtraIcon() +{ +} + +void BaseExtraIcon::setOnClick(MIRANDAHOOKPARAM pFunc, LPARAM pParam) +{ + m_OnClick = pFunc; + m_onClickParam = pParam; +} + +const wchar_t* BaseExtraIcon::getDescription() const +{ + return TranslateW_LP(m_tszDescription, m_pPlugin); +} + +HANDLE BaseExtraIcon::getDescIcon() const +{ + return m_hDescIcon; +} + +void BaseExtraIcon::onClick(MCONTACT hContact) +{ + if (m_OnClick != nullptr) + m_OnClick(hContact, (LPARAM)ConvertToClistSlot(m_slot), m_onClickParam); +} + +int BaseExtraIcon::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) +{ + if (m_pParent) + return m_pParent->ClistSetExtraIcon(hContact, hImage); + return Clist_SetExtraIcon(hContact, m_slot, hImage); +} diff --git a/src/mir_app/src/ei_callbackIcon.cpp b/src/mir_app/src/ei_callbackIcon.cpp index a28bd24ed0..205003d054 100644 --- a/src/mir_app/src/ei_callbackIcon.cpp +++ b/src/mir_app/src/ei_callbackIcon.cpp @@ -1,75 +1,75 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -CallbackExtraIcon::CallbackExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, - MIRANDAHOOK _RebuildIcons, MIRANDAHOOK _ApplyIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : - BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param), - m_pfnRebuildIcons(_RebuildIcons), m_pfnApplyIcon(_ApplyIcon), m_needToRebuild(true) -{ -} - -CallbackExtraIcon::~CallbackExtraIcon() -{ -} - -int CallbackExtraIcon::getType() const -{ - return EXTRAICON_TYPE_CALLBACK; -} - -void CallbackExtraIcon::rebuildIcons() -{ - if (!isEnabled()) { - m_needToRebuild = true; - return; - } - - m_needToRebuild = false; - m_pfnRebuildIcons(0, 0); -} - -void CallbackExtraIcon::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - if (m_needToRebuild) - rebuildIcons(); - - m_pfnApplyIcon(hContact, 0); -} - -int CallbackExtraIcon::setIcon(MCONTACT hContact, HANDLE icon) -{ - if (!isEnabled() || hContact == 0) - return -1; - - return ClistSetExtraIcon(hContact, icon); -} - -int CallbackExtraIcon::setIconByName(MCONTACT, const char*) -{ - return -1; -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "extraicons.h" + +CallbackExtraIcon::CallbackExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, + MIRANDAHOOK _RebuildIcons, MIRANDAHOOK _ApplyIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : + BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param), + m_pfnRebuildIcons(_RebuildIcons), m_pfnApplyIcon(_ApplyIcon), m_needToRebuild(true) +{ +} + +CallbackExtraIcon::~CallbackExtraIcon() +{ +} + +int CallbackExtraIcon::getType() const +{ + return EXTRAICON_TYPE_CALLBACK; +} + +void CallbackExtraIcon::rebuildIcons() +{ + if (!isEnabled()) { + m_needToRebuild = true; + return; + } + + m_needToRebuild = false; + m_pfnRebuildIcons(0, 0); +} + +void CallbackExtraIcon::applyIcon(MCONTACT hContact) +{ + if (!isEnabled() || hContact == 0) + return; + + if (m_needToRebuild) + rebuildIcons(); + + m_pfnApplyIcon(hContact, 0); +} + +int CallbackExtraIcon::setIcon(MCONTACT hContact, HANDLE icon) +{ + if (!isEnabled() || hContact == 0) + return -1; + + return ClistSetExtraIcon(hContact, icon); +} + +int CallbackExtraIcon::setIconByName(MCONTACT, const char*) +{ + return -1; +} diff --git a/src/mir_app/src/ei_defaulticons.cpp b/src/mir_app/src/ei_defaulticons.cpp index 14c1b2b359..e2eb20cbf0 100644 --- a/src/mir_app/src/ei_defaulticons.cpp +++ b/src/mir_app/src/ei_defaulticons.cpp @@ -1,339 +1,339 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "m_cluiframes.h" - -#include "extraicons.h" -#include "chat.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// DB extra icons - -HANDLE hExtraVisibility, hExtraChat, hExtraChatMute, hExtraGender, hExtraProto; - -static void SetVisibility(MCONTACT hContact, int apparentMode, bool clear) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return; - - if (apparentMode <= 0) - apparentMode = db_get_w(hContact, proto, "ApparentMode", 0); - - HANDLE hExtraIcon, hIcolib = nullptr; - - // Is chat - if (Contact::IsGroupChat(hContact, proto)) { - hExtraIcon = hExtraChat; - if (apparentMode == ID_STATUS_OFFLINE) - hIcolib = IcoLib_GetIconHandle("ChatActivity"); - } - else { // Not chat - hExtraIcon = hExtraVisibility; - if (apparentMode == ID_STATUS_OFFLINE) - hIcolib = Skin_GetIconHandle(SKINICON_OTHER_INVISIBLE_ALL); - else if (apparentMode == ID_STATUS_ONLINE) - hIcolib = Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL); - } - - if (hIcolib != nullptr || clear) - ExtraIcon_SetIcon(hExtraIcon, hContact, hIcolib); -} - -static void SetGender(MCONTACT hContact, int gender, bool clear) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return; - - if (gender <= 0) - gender = db_get_b(hContact, proto, "Gender", 0); - if (gender <= 0) - gender = db_get_b(hContact, "UserInfo", "Gender", 0); - - const char *ico; - if (gender == 'M') - ico = "gender_male"; - else if (gender == 'F') - ico = "gender_female"; - else - ico = nullptr; - - if (ico != nullptr || clear) - ExtraIcon_SetIconByName(hExtraGender, hContact, ico); -} - -static void SetChatMute(MCONTACT hContact, int mode) -{ - if (hContact == 0) - return; - - if (mode == -1) - mode = db_get_b(hContact, "SRMM", "MuteMode", CHATMODE_NORMAL); - - HANDLE hIcon; - switch (mode) { - case CHATMODE_MUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_OFF); break; - case CHATMODE_UNMUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_ON); break; - default: - hIcon = nullptr; break; - } - - ExtraIcon_SetIcon(hExtraChatMute, hContact, hIcon); -} - -struct Info -{ - const char *name; - const char *desc; - int iSkinIcon; - const char *db[8]; - void(*OnClick)(Info *info, const char *text); - int flags; - - HANDLE hIcolib, hExtraIcon; -}; - -static void EmailOnClick(Info*, const char *text) -{ - char cmd[1024]; - mir_snprintf(cmd, "mailto:%s", text); - ShellExecuteA(nullptr, "open", cmd, nullptr, nullptr, SW_SHOW); -} - -static void HomepageOnClick(Info*, const char *text) -{ - ShellExecuteA(nullptr, "open", text, nullptr, nullptr, SW_SHOW); -} - -static Info infos[] = -{ - { "homepage", LPGEN("Homepage"), SKINICON_OTHER_MIRANDAWEB, - { nullptr, "Homepage", "UserInfo", "Homepage" }, - &HomepageOnClick, EIF_DISABLED_BY_DEFAULT }, - { "sms", LPGEN("Phone/SMS"), SKINICON_OTHER_SMS, - { nullptr, "Cellular", "UserInfo", "Cellular", "UserInfo", "Phone", "UserInfo", "MyPhone0" }, - nullptr, EIF_DISABLED_BY_DEFAULT }, - { "email", LPGEN("E-mail"), SKINICON_OTHER_SENDEMAIL, - { nullptr, "e-mail", "UserInfo", "e-mail", "UserInfo", "Mye-mail0" }, - &EmailOnClick, EIF_DISABLED_BY_DEFAULT }, -}; - -static void SetExtraIcons(MCONTACT hContact) -{ - if (hContact == 0) - return; - - char *proto = Proto_GetBaseAccountName(hContact); - if ( IsEmpty(proto)) - return; - - for (auto &p : infos) { - for (unsigned int j = 0; j < _countof(p.db); j += 2) { - if (p.db[j + 1] == nullptr) - break; - - ptrA szValue(db_get_sa(hContact, p.db[j] == nullptr ? proto : p.db[j], p.db[j + 1])); - if (!IsEmpty(szValue)) { - ExtraIcon_SetIcon(p.hExtraIcon, hContact, p.hIcolib); - break; - } - } - } -} - -static int SettingChanged(WPARAM hContact, LPARAM lParam) -{ - if (hContact == 0) - return 0; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam; - bool isProto = (strcmp(cws->szModule, proto) == 0); - if (isProto && !strcmp(cws->szSetting, "ApparentMode")) { - SetVisibility(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.wVal, true); - return 0; - } - - if (!strcmp(cws->szModule, "SRMM") && !strcmp(cws->szSetting, "MuteMode")) { - SetChatMute(hContact, cws->value.type == DBVT_DELETED ? CHATMODE_NORMAL : cws->value.bVal); - return 0; - } - - if (!strcmp(cws->szSetting, "Gender") && (isProto || !strcmp(cws->szModule, "UserInfo"))) { - SetGender(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.bVal, true); - return 0; - } - - for (auto &p : infos) { - for (int j = 0; j < _countof(p.db); j += 2) { - if (p.db[j + 1] == nullptr) - break; - if (p.db[j] == nullptr && !isProto) - continue; - if (p.db[j] != nullptr && strcmp(cws->szModule, p.db[j])) - continue; - if (strcmp(cws->szSetting, p.db[j + 1])) - continue; - - bool show = (cws->value.type != DBVT_DELETED && !IsEmpty(cws->value.pszVal)); - ExtraIcon_SetIcon(p.hExtraIcon, hContact, show ? p.hIcolib : nullptr); - break; - } - } - - return 0; -} - -static int DefaultOnClick(WPARAM hContact, LPARAM, LPARAM param) -{ - Info *p = (Info*)param; - if (p == nullptr) - return 0; - - if (hContact == 0) - return 0; - - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - bool found = false; - for (int j = 0; !found && j < _countof(p->db); j += 2) { - if (p->db[j + 1] == nullptr) - break; - - ptrA szValue(db_get_sa(hContact, p->db[j] == nullptr ? proto : p->db[j], p->db[j + 1])); - if (!IsEmpty(szValue)) { - p->OnClick(p, szValue); - found = true; - } - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// Protocol icon - -struct ProtoInfo -{ - ProtoInfo(LPCSTR _proto, HANDLE _image) : - proto(mir_strdup(_proto)), - hImage(_image) - {} - - ptrA proto; - HANDLE hImage; -}; - -static int CompareProtos(const ProtoInfo *p1, const ProtoInfo *p2) -{ return mir_strcmp(p1->proto, p2->proto); -} - -OBJLIST arProtos(10, CompareProtos); - -static int ProtocolRebuildIcons(WPARAM, LPARAM) -{ - arProtos.destroy(); - return 0; -} - -static ProtoInfo* FindProto(const char *proto) -{ - ProtoInfo *p = arProtos.find((ProtoInfo*)&proto); - if (p) - return p; - - HICON hIcon = Skin_LoadProtoIcon(proto, ID_STATUS_ONLINE); - if (hIcon == nullptr) - return nullptr; - - HANDLE hImage = ExtraIcon_AddIcon(hIcon); - if (hImage == INVALID_HANDLE_VALUE) - return nullptr; - - p = new ProtoInfo(proto, hImage); - arProtos.insert(p); - return p; -} - -static int ProtocolApplyIcon(WPARAM hContact, LPARAM) -{ - char *proto = Proto_GetBaseAccountName(hContact); - if (IsEmpty(proto)) - return 0; - - HANDLE hImage = INVALID_HANDLE_VALUE; - ProtoInfo *pi = FindProto(proto); - if (pi != nullptr) - hImage = pi->hImage; - - ExtraIcon_SetIcon(hExtraProto, hContact, hImage); - return 0; -} - -static int ProtocolOnClick(WPARAM wParam, LPARAM, LPARAM) -{ - if (wParam) - CallService(MS_USERINFO_SHOWDIALOG, wParam, 0); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void DefaultExtraIcons_Load() -{ - hExtraChat = ExtraIcon_RegisterIcolib("chat_activity", LPGEN("Chat activity"), "ChatActivity"); - hExtraChatMute = ExtraIcon_RegisterIcolib("chat_mute", LPGEN("Chat mute mode"), "ChatMute"); - hExtraVisibility = ExtraIcon_RegisterIcolib("visibility", "Visibility", Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL)); - hExtraGender = ExtraIcon_RegisterIcolib("gender", "Gender", "gender_male", nullptr, 0, EIF_DISABLED_BY_DEFAULT); - hExtraProto = ExtraIcon_RegisterCallback("protocol", "Account", Skin_GetIconHandle(SKINICON_OTHER_ACCMGR), - &ProtocolRebuildIcons, &ProtocolApplyIcon, &ProtocolOnClick, 0, EIF_DISABLED_BY_DEFAULT); - - for (auto &p : infos) { - p.hIcolib = Skin_GetIconHandle(p.iSkinIcon); - if (p.OnClick) - p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), DefaultOnClick, (LPARAM)&p, p.flags); - else - p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), nullptr, 0, p.flags); - } - - for (auto &hContact : Contacts()) { - SetChatMute(hContact, -1); - SetExtraIcons(hContact); - SetVisibility(hContact, -1, false); - SetGender(hContact, -1, false); - } - - HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "m_cluiframes.h" + +#include "extraicons.h" +#include "chat.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// DB extra icons + +HANDLE hExtraVisibility, hExtraChat, hExtraChatMute, hExtraGender, hExtraProto; + +static void SetVisibility(MCONTACT hContact, int apparentMode, bool clear) +{ + if (hContact == 0) + return; + + char *proto = Proto_GetBaseAccountName(hContact); + if (IsEmpty(proto)) + return; + + if (apparentMode <= 0) + apparentMode = db_get_w(hContact, proto, "ApparentMode", 0); + + HANDLE hExtraIcon, hIcolib = nullptr; + + // Is chat + if (Contact::IsGroupChat(hContact, proto)) { + hExtraIcon = hExtraChat; + if (apparentMode == ID_STATUS_OFFLINE) + hIcolib = IcoLib_GetIconHandle("ChatActivity"); + } + else { // Not chat + hExtraIcon = hExtraVisibility; + if (apparentMode == ID_STATUS_OFFLINE) + hIcolib = Skin_GetIconHandle(SKINICON_OTHER_INVISIBLE_ALL); + else if (apparentMode == ID_STATUS_ONLINE) + hIcolib = Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL); + } + + if (hIcolib != nullptr || clear) + ExtraIcon_SetIcon(hExtraIcon, hContact, hIcolib); +} + +static void SetGender(MCONTACT hContact, int gender, bool clear) +{ + if (hContact == 0) + return; + + char *proto = Proto_GetBaseAccountName(hContact); + if (IsEmpty(proto)) + return; + + if (gender <= 0) + gender = db_get_b(hContact, proto, "Gender", 0); + if (gender <= 0) + gender = db_get_b(hContact, "UserInfo", "Gender", 0); + + const char *ico; + if (gender == 'M') + ico = "gender_male"; + else if (gender == 'F') + ico = "gender_female"; + else + ico = nullptr; + + if (ico != nullptr || clear) + ExtraIcon_SetIconByName(hExtraGender, hContact, ico); +} + +static void SetChatMute(MCONTACT hContact, int mode) +{ + if (hContact == 0) + return; + + if (mode == -1) + mode = db_get_b(hContact, "SRMM", "MuteMode", CHATMODE_NORMAL); + + HANDLE hIcon; + switch (mode) { + case CHATMODE_MUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_OFF); break; + case CHATMODE_UNMUTE: hIcon = Skin_GetIconHandle(SKINICON_OTHER_ON); break; + default: + hIcon = nullptr; break; + } + + ExtraIcon_SetIcon(hExtraChatMute, hContact, hIcon); +} + +struct Info +{ + const char *name; + const char *desc; + int iSkinIcon; + const char *db[8]; + void(*OnClick)(Info *info, const char *text); + int flags; + + HANDLE hIcolib, hExtraIcon; +}; + +static void EmailOnClick(Info*, const char *text) +{ + char cmd[1024]; + mir_snprintf(cmd, "mailto:%s", text); + ShellExecuteA(nullptr, "open", cmd, nullptr, nullptr, SW_SHOW); +} + +static void HomepageOnClick(Info*, const char *text) +{ + ShellExecuteA(nullptr, "open", text, nullptr, nullptr, SW_SHOW); +} + +static Info infos[] = +{ + { "homepage", LPGEN("Homepage"), SKINICON_OTHER_MIRANDAWEB, + { nullptr, "Homepage", "UserInfo", "Homepage" }, + &HomepageOnClick, EIF_DISABLED_BY_DEFAULT }, + { "sms", LPGEN("Phone/SMS"), SKINICON_OTHER_SMS, + { nullptr, "Cellular", "UserInfo", "Cellular", "UserInfo", "Phone", "UserInfo", "MyPhone0" }, + nullptr, EIF_DISABLED_BY_DEFAULT }, + { "email", LPGEN("E-mail"), SKINICON_OTHER_SENDEMAIL, + { nullptr, "e-mail", "UserInfo", "e-mail", "UserInfo", "Mye-mail0" }, + &EmailOnClick, EIF_DISABLED_BY_DEFAULT }, +}; + +static void SetExtraIcons(MCONTACT hContact) +{ + if (hContact == 0) + return; + + char *proto = Proto_GetBaseAccountName(hContact); + if ( IsEmpty(proto)) + return; + + for (auto &p : infos) { + for (unsigned int j = 0; j < _countof(p.db); j += 2) { + if (p.db[j + 1] == nullptr) + break; + + ptrA szValue(db_get_sa(hContact, p.db[j] == nullptr ? proto : p.db[j], p.db[j + 1])); + if (!IsEmpty(szValue)) { + ExtraIcon_SetIcon(p.hExtraIcon, hContact, p.hIcolib); + break; + } + } + } +} + +static int SettingChanged(WPARAM hContact, LPARAM lParam) +{ + if (hContact == 0) + return 0; + + char *proto = Proto_GetBaseAccountName(hContact); + if (IsEmpty(proto)) + return 0; + + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam; + bool isProto = (strcmp(cws->szModule, proto) == 0); + if (isProto && !strcmp(cws->szSetting, "ApparentMode")) { + SetVisibility(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.wVal, true); + return 0; + } + + if (!strcmp(cws->szModule, "SRMM") && !strcmp(cws->szSetting, "MuteMode")) { + SetChatMute(hContact, cws->value.type == DBVT_DELETED ? CHATMODE_NORMAL : cws->value.bVal); + return 0; + } + + if (!strcmp(cws->szSetting, "Gender") && (isProto || !strcmp(cws->szModule, "UserInfo"))) { + SetGender(hContact, cws->value.type == DBVT_DELETED ? 0 : cws->value.bVal, true); + return 0; + } + + for (auto &p : infos) { + for (int j = 0; j < _countof(p.db); j += 2) { + if (p.db[j + 1] == nullptr) + break; + if (p.db[j] == nullptr && !isProto) + continue; + if (p.db[j] != nullptr && strcmp(cws->szModule, p.db[j])) + continue; + if (strcmp(cws->szSetting, p.db[j + 1])) + continue; + + bool show = (cws->value.type != DBVT_DELETED && !IsEmpty(cws->value.pszVal)); + ExtraIcon_SetIcon(p.hExtraIcon, hContact, show ? p.hIcolib : nullptr); + break; + } + } + + return 0; +} + +static int DefaultOnClick(WPARAM hContact, LPARAM, LPARAM param) +{ + Info *p = (Info*)param; + if (p == nullptr) + return 0; + + if (hContact == 0) + return 0; + + char *proto = Proto_GetBaseAccountName(hContact); + if (IsEmpty(proto)) + return 0; + + bool found = false; + for (int j = 0; !found && j < _countof(p->db); j += 2) { + if (p->db[j + 1] == nullptr) + break; + + ptrA szValue(db_get_sa(hContact, p->db[j] == nullptr ? proto : p->db[j], p->db[j + 1])); + if (!IsEmpty(szValue)) { + p->OnClick(p, szValue); + found = true; + } + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Protocol icon + +struct ProtoInfo +{ + ProtoInfo(LPCSTR _proto, HANDLE _image) : + proto(mir_strdup(_proto)), + hImage(_image) + {} + + ptrA proto; + HANDLE hImage; +}; + +static int CompareProtos(const ProtoInfo *p1, const ProtoInfo *p2) +{ return mir_strcmp(p1->proto, p2->proto); +} + +OBJLIST arProtos(10, CompareProtos); + +static int ProtocolRebuildIcons(WPARAM, LPARAM) +{ + arProtos.destroy(); + return 0; +} + +static ProtoInfo* FindProto(const char *proto) +{ + ProtoInfo *p = arProtos.find((ProtoInfo*)&proto); + if (p) + return p; + + HICON hIcon = Skin_LoadProtoIcon(proto, ID_STATUS_ONLINE); + if (hIcon == nullptr) + return nullptr; + + HANDLE hImage = ExtraIcon_AddIcon(hIcon); + if (hImage == INVALID_HANDLE_VALUE) + return nullptr; + + p = new ProtoInfo(proto, hImage); + arProtos.insert(p); + return p; +} + +static int ProtocolApplyIcon(WPARAM hContact, LPARAM) +{ + char *proto = Proto_GetBaseAccountName(hContact); + if (IsEmpty(proto)) + return 0; + + HANDLE hImage = INVALID_HANDLE_VALUE; + ProtoInfo *pi = FindProto(proto); + if (pi != nullptr) + hImage = pi->hImage; + + ExtraIcon_SetIcon(hExtraProto, hContact, hImage); + return 0; +} + +static int ProtocolOnClick(WPARAM wParam, LPARAM, LPARAM) +{ + if (wParam) + CallService(MS_USERINFO_SHOWDIALOG, wParam, 0); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void DefaultExtraIcons_Load() +{ + hExtraChat = ExtraIcon_RegisterIcolib("chat_activity", LPGEN("Chat activity"), "ChatActivity"); + hExtraChatMute = ExtraIcon_RegisterIcolib("chat_mute", LPGEN("Chat mute mode"), "ChatMute"); + hExtraVisibility = ExtraIcon_RegisterIcolib("visibility", "Visibility", Skin_GetIconHandle(SKINICON_OTHER_VISIBLE_ALL)); + hExtraGender = ExtraIcon_RegisterIcolib("gender", "Gender", "gender_male", nullptr, 0, EIF_DISABLED_BY_DEFAULT); + hExtraProto = ExtraIcon_RegisterCallback("protocol", "Account", Skin_GetIconHandle(SKINICON_OTHER_ACCMGR), + &ProtocolRebuildIcons, &ProtocolApplyIcon, &ProtocolOnClick, 0, EIF_DISABLED_BY_DEFAULT); + + for (auto &p : infos) { + p.hIcolib = Skin_GetIconHandle(p.iSkinIcon); + if (p.OnClick) + p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), DefaultOnClick, (LPARAM)&p, p.flags); + else + p.hExtraIcon = ExtraIcon_RegisterIcolib(p.name, p.desc, Skin_GetIconHandle(p.iSkinIcon), nullptr, 0, p.flags); + } + + for (auto &hContact : Contacts()) { + SetChatMute(hContact, -1); + SetExtraIcons(hContact); + SetVisibility(hContact, -1, false); + SetGender(hContact, -1, false); + } + + HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); +} diff --git a/src/mir_app/src/ei_extraIcon.cpp b/src/mir_app/src/ei_extraIcon.cpp index ecec626b7e..fbdfc901ec 100644 --- a/src/mir_app/src/ei_extraIcon.cpp +++ b/src/mir_app/src/ei_extraIcon.cpp @@ -1,83 +1,83 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -ExtraIcon::ExtraIcon(const char *name) : - m_szName(mir_strdup(name)) -{ -} - -ExtraIcon::~ExtraIcon() -{ -} - -const char *ExtraIcon::getName() const -{ - return m_szName; -} - -int ExtraIcon::getSlot() const -{ - return m_slot; -} - -void ExtraIcon::setSlot(int slot) -{ - m_slot = slot; -} - -int ExtraIcon::getPosition() const -{ - return m_position; -} - -void ExtraIcon::setPosition(int position) -{ - m_position = position; -} - -bool ExtraIcon::isEnabled() const -{ - return m_slot >= 0; -} - -void ExtraIcon::doApply(MCONTACT hContact) -{ - if (m_pParent) - m_pParent->applyIcon(hContact); - else - applyIcon(hContact); -} - -void ExtraIcon::applyIcons() -{ - if (!isEnabled()) - return; - - for (auto &hContact : Contacts()) { - // Clear to assert that it will be cleared - Clist_SetExtraIcon(hContact, m_slot, INVALID_HANDLE_VALUE); - doApply(hContact); - } -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "extraicons.h" + +ExtraIcon::ExtraIcon(const char *name) : + m_szName(mir_strdup(name)) +{ +} + +ExtraIcon::~ExtraIcon() +{ +} + +const char *ExtraIcon::getName() const +{ + return m_szName; +} + +int ExtraIcon::getSlot() const +{ + return m_slot; +} + +void ExtraIcon::setSlot(int slot) +{ + m_slot = slot; +} + +int ExtraIcon::getPosition() const +{ + return m_position; +} + +void ExtraIcon::setPosition(int position) +{ + m_position = position; +} + +bool ExtraIcon::isEnabled() const +{ + return m_slot >= 0; +} + +void ExtraIcon::doApply(MCONTACT hContact) +{ + if (m_pParent) + m_pParent->applyIcon(hContact); + else + applyIcon(hContact); +} + +void ExtraIcon::applyIcons() +{ + if (!isEnabled()) + return; + + for (auto &hContact : Contacts()) { + // Clear to assert that it will be cleared + Clist_SetExtraIcon(hContact, m_slot, INVALID_HANDLE_VALUE); + doApply(hContact); + } +} diff --git a/src/mir_app/src/ei_groupIcon.cpp b/src/mir_app/src/ei_groupIcon.cpp index 50bea072e0..44dcbbd686 100644 --- a/src/mir_app/src/ei_groupIcon.cpp +++ b/src/mir_app/src/ei_groupIcon.cpp @@ -1,200 +1,200 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -ExtraIconGroup::ExtraIconGroup(const char *_name) : - ExtraIcon(_name), - m_items(1) -{ - db_set_resident(EI_MODULE_NAME, _name); -} - -ExtraIconGroup::~ExtraIconGroup() -{ -} - -void ExtraIconGroup::addExtraIcon(BaseExtraIcon *extra) -{ - m_items.insert(extra); - - CMStringW description; - for (auto &p : m_items) { - if (!description.IsEmpty()) - description.Append(L" / "); - description += p->getDescription(); - } - - m_tszDescription = mir_wstrdup(description); -} - -void ExtraIconGroup::rebuildIcons() -{ - for (auto &p : m_items) - p->rebuildIcons(); -} - -void ExtraIconGroup::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - m_setValidExtraIcon = false; - m_insideApply = true; - - for (auto &p : m_items) { - p->applyIcon(hContact); - if (m_setValidExtraIcon) { - m_pCurrentItem = p; - break; - } - } - - m_insideApply = false; -} - -int ExtraIconGroup::getPosition() const -{ - int pos = INT_MAX; - for (auto &p : m_items) - pos = min(pos, p->getPosition()); - return pos; -} - -void ExtraIconGroup::setSlot(int slot) -{ - ExtraIcon::setSlot(slot); - - for (auto &p : m_items) - p->setSlot(slot); -} - -void ExtraIconGroup::onClick(MCONTACT hContact) -{ - if (m_pCurrentItem != nullptr) - m_pCurrentItem->onClick(hContact); -} - -int ExtraIconGroup::setIcon(MCONTACT, HANDLE) -{ - return -1; - // return internalSetIcon(hContact, (void*)value, false); -} - -int ExtraIconGroup::setIconByName(MCONTACT, const char*) -{ - return -1; - // return internalSetIcon(hContact, (void*)value, true); -} - -int ExtraIconGroup::internalSetIcon(ExtraIcon *pChild, MCONTACT hContact, HANDLE value, bool bByName) -{ - if (m_insideApply) { - for (auto &p : m_items) - if (p == pChild) { - if (bByName) - return p->setIconByName(hContact, (const char*)value); - return p->setIcon(hContact, value); - } - - return -1; - } - - int currentPos = m_items.getCount(); - int storePos = m_items.getCount(); - for (int i = 0; i < m_items.getCount(); i++) { - if (m_items[i] == pChild) - storePos = i; - - if (m_items[i] == m_pCurrentItem) - currentPos = i; - } - - if (storePos == m_items.getCount()) - return -1; - - if (storePos > currentPos) { - m_items[storePos]->storeIcon(hContact, value); - return 0; - } - - // Ok, we have to set the icon, but we have to assert it is a valid icon - m_setValidExtraIcon = false; - - int ret; - if (bByName) - ret = m_items[storePos]->setIconByName(hContact, (const char*)value); - else - ret = m_items[storePos]->setIcon(hContact, (HANDLE)value); - - if (storePos < currentPos) { - if (m_setValidExtraIcon) - m_pCurrentItem = m_items[storePos]; - } - else if (storePos == currentPos) { - if (!m_setValidExtraIcon) { - m_pCurrentItem = nullptr; - - m_insideApply = true; - - for (++storePos; storePos < m_items.getCount(); ++storePos) { - m_items[storePos]->applyIcon(hContact); - if (m_setValidExtraIcon) { - m_pCurrentItem = m_items[storePos]; - break; - } - } - - m_insideApply = false; - } - } - - return ret; -} - -const wchar_t* ExtraIconGroup::getDescription() const -{ - return m_tszDescription; -} - -HANDLE ExtraIconGroup::getDescIcon() const -{ - for (auto &p : m_items) - if (HANDLE ret = p->getDescIcon()) - return ret; - - return nullptr; -} - -int ExtraIconGroup::getType() const -{ - return EXTRAICON_TYPE_GROUP; -} - -int ExtraIconGroup::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) -{ - if (hImage != INVALID_HANDLE_VALUE) - m_setValidExtraIcon = true; - - return Clist_SetExtraIcon(hContact, m_slot, hImage); -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "extraicons.h" + +ExtraIconGroup::ExtraIconGroup(const char *_name) : + ExtraIcon(_name), + m_items(1) +{ + db_set_resident(EI_MODULE_NAME, _name); +} + +ExtraIconGroup::~ExtraIconGroup() +{ +} + +void ExtraIconGroup::addExtraIcon(BaseExtraIcon *extra) +{ + m_items.insert(extra); + + CMStringW description; + for (auto &p : m_items) { + if (!description.IsEmpty()) + description.Append(L" / "); + description += p->getDescription(); + } + + m_tszDescription = mir_wstrdup(description); +} + +void ExtraIconGroup::rebuildIcons() +{ + for (auto &p : m_items) + p->rebuildIcons(); +} + +void ExtraIconGroup::applyIcon(MCONTACT hContact) +{ + if (!isEnabled() || hContact == 0) + return; + + m_setValidExtraIcon = false; + m_insideApply = true; + + for (auto &p : m_items) { + p->applyIcon(hContact); + if (m_setValidExtraIcon) { + m_pCurrentItem = p; + break; + } + } + + m_insideApply = false; +} + +int ExtraIconGroup::getPosition() const +{ + int pos = INT_MAX; + for (auto &p : m_items) + pos = min(pos, p->getPosition()); + return pos; +} + +void ExtraIconGroup::setSlot(int slot) +{ + ExtraIcon::setSlot(slot); + + for (auto &p : m_items) + p->setSlot(slot); +} + +void ExtraIconGroup::onClick(MCONTACT hContact) +{ + if (m_pCurrentItem != nullptr) + m_pCurrentItem->onClick(hContact); +} + +int ExtraIconGroup::setIcon(MCONTACT, HANDLE) +{ + return -1; + // return internalSetIcon(hContact, (void*)value, false); +} + +int ExtraIconGroup::setIconByName(MCONTACT, const char*) +{ + return -1; + // return internalSetIcon(hContact, (void*)value, true); +} + +int ExtraIconGroup::internalSetIcon(ExtraIcon *pChild, MCONTACT hContact, HANDLE value, bool bByName) +{ + if (m_insideApply) { + for (auto &p : m_items) + if (p == pChild) { + if (bByName) + return p->setIconByName(hContact, (const char*)value); + return p->setIcon(hContact, value); + } + + return -1; + } + + int currentPos = m_items.getCount(); + int storePos = m_items.getCount(); + for (int i = 0; i < m_items.getCount(); i++) { + if (m_items[i] == pChild) + storePos = i; + + if (m_items[i] == m_pCurrentItem) + currentPos = i; + } + + if (storePos == m_items.getCount()) + return -1; + + if (storePos > currentPos) { + m_items[storePos]->storeIcon(hContact, value); + return 0; + } + + // Ok, we have to set the icon, but we have to assert it is a valid icon + m_setValidExtraIcon = false; + + int ret; + if (bByName) + ret = m_items[storePos]->setIconByName(hContact, (const char*)value); + else + ret = m_items[storePos]->setIcon(hContact, (HANDLE)value); + + if (storePos < currentPos) { + if (m_setValidExtraIcon) + m_pCurrentItem = m_items[storePos]; + } + else if (storePos == currentPos) { + if (!m_setValidExtraIcon) { + m_pCurrentItem = nullptr; + + m_insideApply = true; + + for (++storePos; storePos < m_items.getCount(); ++storePos) { + m_items[storePos]->applyIcon(hContact); + if (m_setValidExtraIcon) { + m_pCurrentItem = m_items[storePos]; + break; + } + } + + m_insideApply = false; + } + } + + return ret; +} + +const wchar_t* ExtraIconGroup::getDescription() const +{ + return m_tszDescription; +} + +HANDLE ExtraIconGroup::getDescIcon() const +{ + for (auto &p : m_items) + if (HANDLE ret = p->getDescIcon()) + return ret; + + return nullptr; +} + +int ExtraIconGroup::getType() const +{ + return EXTRAICON_TYPE_GROUP; +} + +int ExtraIconGroup::ClistSetExtraIcon(MCONTACT hContact, HANDLE hImage) +{ + if (hImage != INVALID_HANDLE_VALUE) + m_setValidExtraIcon = true; + + return Clist_SetExtraIcon(hContact, m_slot, hImage); +} diff --git a/src/mir_app/src/ei_icolibIcon.cpp b/src/mir_app/src/ei_icolibIcon.cpp index 07f4a2b3df..7095c96902 100644 --- a/src/mir_app/src/ei_icolibIcon.cpp +++ b/src/mir_app/src/ei_icolibIcon.cpp @@ -1,118 +1,118 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" -#include "usedIcons.h" - -#include "IcoLib.h" - -IcolibExtraIcon::IcolibExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : - BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param) -{ - db_set_resident(EI_MODULE_NAME, _name); -} - -IcolibExtraIcon::~IcolibExtraIcon() -{ -} - -int IcolibExtraIcon::getType() const -{ - return EXTRAICON_TYPE_ICOLIB; -} - -void IcolibExtraIcon::rebuildIcons() -{ -} - -void IcolibExtraIcon::applyIcon(MCONTACT hContact) -{ - if (!isEnabled() || hContact == 0) - return; - - HANDLE hImage = INVALID_HANDLE_VALUE; - - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - hImage = GetIcon(szIconName); - - ClistSetExtraIcon(hContact, hImage); -} - -int IcolibExtraIcon::setIcon(MCONTACT hContact, HANDLE hIcoLib) -{ - if (hContact == 0) - return -1; - - if (hIcoLib == INVALID_HANDLE_VALUE) - hIcoLib = nullptr; - - if (isEnabled()) { - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - RemoveIcon(szIconName); - } - - IcolibItem *p = (IcolibItem*)hIcoLib; - char *szName = (p) ? p->name : nullptr; - storeIcon(hContact, szName); - - if (isEnabled()) - return ClistSetExtraIcon(hContact, (hIcoLib == nullptr) ? INVALID_HANDLE_VALUE : AddIcon(szName)); - - return 0; -} - -int IcolibExtraIcon::setIconByName(MCONTACT hContact, const char *icon) -{ - if (hContact == 0) - return -1; - - if (icon == INVALID_HANDLE_VALUE) - icon = nullptr; - - if (isEnabled()) { - ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); - if (!IsEmpty(szIconName)) - RemoveIcon(szIconName); - } - - storeIcon(hContact, (char*)icon); - - if (isEnabled()) - return ClistSetExtraIcon(hContact, (IsEmpty(icon)) ? INVALID_HANDLE_VALUE : AddIcon(icon)); - - return 0; -} - -void IcolibExtraIcon::storeIcon(MCONTACT hContact, void *icon) -{ - if (hContact == 0) - return; - - const char *icolibName = (const char *)icon; - if (IsEmpty(icolibName)) - db_unset(hContact, EI_MODULE_NAME, m_szName); - else - db_set_s(hContact, EI_MODULE_NAME, m_szName, icolibName); -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "extraicons.h" +#include "usedIcons.h" + +#include "IcoLib.h" + +IcolibExtraIcon::IcolibExtraIcon(const char *_name, const wchar_t *_description, HANDLE _descIcon, MIRANDAHOOKPARAM _OnClick, LPARAM _param) : + BaseExtraIcon(_name, _description, _descIcon, _OnClick, _param) +{ + db_set_resident(EI_MODULE_NAME, _name); +} + +IcolibExtraIcon::~IcolibExtraIcon() +{ +} + +int IcolibExtraIcon::getType() const +{ + return EXTRAICON_TYPE_ICOLIB; +} + +void IcolibExtraIcon::rebuildIcons() +{ +} + +void IcolibExtraIcon::applyIcon(MCONTACT hContact) +{ + if (!isEnabled() || hContact == 0) + return; + + HANDLE hImage = INVALID_HANDLE_VALUE; + + ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); + if (!IsEmpty(szIconName)) + hImage = GetIcon(szIconName); + + ClistSetExtraIcon(hContact, hImage); +} + +int IcolibExtraIcon::setIcon(MCONTACT hContact, HANDLE hIcoLib) +{ + if (hContact == 0) + return -1; + + if (hIcoLib == INVALID_HANDLE_VALUE) + hIcoLib = nullptr; + + if (isEnabled()) { + ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); + if (!IsEmpty(szIconName)) + RemoveIcon(szIconName); + } + + IcolibItem *p = (IcolibItem*)hIcoLib; + char *szName = (p) ? p->name : nullptr; + storeIcon(hContact, szName); + + if (isEnabled()) + return ClistSetExtraIcon(hContact, (hIcoLib == nullptr) ? INVALID_HANDLE_VALUE : AddIcon(szName)); + + return 0; +} + +int IcolibExtraIcon::setIconByName(MCONTACT hContact, const char *icon) +{ + if (hContact == 0) + return -1; + + if (icon == INVALID_HANDLE_VALUE) + icon = nullptr; + + if (isEnabled()) { + ptrA szIconName(db_get_sa(hContact, EI_MODULE_NAME, m_szName)); + if (!IsEmpty(szIconName)) + RemoveIcon(szIconName); + } + + storeIcon(hContact, (char*)icon); + + if (isEnabled()) + return ClistSetExtraIcon(hContact, (IsEmpty(icon)) ? INVALID_HANDLE_VALUE : AddIcon(icon)); + + return 0; +} + +void IcolibExtraIcon::storeIcon(MCONTACT hContact, void *icon) +{ + if (hContact == 0) + return; + + const char *icolibName = (const char *)icon; + if (IsEmpty(icolibName)) + db_unset(hContact, EI_MODULE_NAME, m_szName); + else + db_set_s(hContact, EI_MODULE_NAME, m_szName, icolibName); +} diff --git a/src/mir_app/src/ei_options.cpp b/src/mir_app/src/ei_options.cpp index 442c120fbb..11e98abd18 100644 --- a/src/mir_app/src/ei_options.cpp +++ b/src/mir_app/src/ei_options.cpp @@ -1,481 +1,481 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "extraicons.h" - -static class CExtraIconOptsDlg *pGlgOptions; - -int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2); - -struct intlist -{ - intlist() : count(0), data(nullptr) {} - ~intlist() { mir_free(data); } - - void add(int val) - { - data = (int*)mir_realloc(data, sizeof(int)*(count + 1)); - data[count++] = val; - } - - int count; - int *data; -}; - -static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM) -{ - intlist *a = (intlist*)lParam1; - intlist *b = (intlist*)lParam2; - return SortFunc(registeredExtraIcons[a->data[0] - 1], registeredExtraIcons[b->data[0] - 1]); -} - -// Functions ////////////////////////////////////////////////////////////////////////////////////// - -BOOL ScreenToClient(HWND hWnd, LPRECT lpRect) -{ - POINT pt; - pt.x = lpRect->left; - pt.y = lpRect->top; - - BOOL ret = ScreenToClient(hWnd, &pt); - if (!ret) - return ret; - - lpRect->left = pt.x; - lpRect->top = pt.y; - - pt.x = lpRect->right; - pt.y = lpRect->bottom; - - ret = ScreenToClient(hWnd, &pt); - - lpRect->right = pt.x; - lpRect->bottom = pt.y; - - return ret; -} - -static void RemoveExtraIcons(int slot) -{ - for (auto &hContact : Contacts()) - Clist_SetExtraIcon(hContact, slot, INVALID_HANDLE_VALUE); -} - -class CExtraIconOptsDlg : public CDlgBase -{ - intlist* Tree_GetIDs(HTREEITEM hItem) - { - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - return (intlist*)tvi.lParam; - } - - HTREEITEM Tree_AddExtraIcon(BaseExtraIcon *extra, bool selected, HTREEITEM hAfter = TVI_LAST) - { - intlist *ids = new intlist(); - ids->add(extra->getID()); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = hAfter; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.iSelectedImage = tvis.item.iImage = extra->getID(); - tvis.item.lParam = (LPARAM)ids; - tvis.item.pszText = (LPTSTR)extra->getDescription(); - tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); - return m_tree.InsertItem(&tvis); - } - - HTREEITEM Tree_AddExtraIconGroup(intlist &group, bool selected, HTREEITEM hAfter = TVI_LAST) - { - intlist *ids = new intlist(); - CMStringW desc; - int img = 0; - for (int i = 0; i < group.count; i++) { - BaseExtraIcon *extra = registeredExtraIcons[group.data[i] - 1]; - ids->add(extra->getID()); - - if (img == 0 && extra->getDescIcon() != nullptr) - img = extra->getID(); - - if (i > 0) - desc += L" / "; - desc += extra->getDescription(); - } - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = hAfter; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - tvis.item.iSelectedImage = tvis.item.iImage = img; - tvis.item.lParam = (LPARAM)ids; - tvis.item.pszText = (wchar_t*)desc.c_str(); - tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); - return m_tree.InsertItem(&tvis); - } - - void GroupSelectedItems() - { - LIST<_TREEITEM> toRemove(1); - intlist ids; - bool selected = false; - HTREEITEM hPlace = nullptr; - - // Find items - HTREEITEM hItem = m_tree.GetRoot(); - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - while (hItem) { - if (m_tree.IsSelected(hItem)) { - if (hPlace == nullptr) - hPlace = hItem; - - tvi.hItem = hItem; - m_tree.GetItem(&tvi); - - intlist *iids = (intlist*)tvi.lParam; - for (int i = 0; i < iids->count; i++) - ids.add(iids->data[i]); - - if ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)) - selected = true; - - toRemove.insert(hItem); - } - - hItem = m_tree.GetNextSibling(hItem); - } - - if (hPlace != nullptr) { - // Add new - HTREEITEM hNew = Tree_AddExtraIconGroup(ids, selected, hPlace); - - // Remove old - for (auto &p : toRemove) { - delete Tree_GetIDs(p); - m_tree.DeleteItem(p); - } - - // Select - m_tree.UnselectAll(); - m_tree.SelectItem(hNew); - } - } - - void UngroupSelectedItems() - { - HTREEITEM hItem = m_tree.GetSelection(); - if (hItem == nullptr) - return; - - intlist *ids = Tree_GetIDs(hItem); - if (ids->count < 2) - return; - - bool selected = m_tree.IsSelected(hItem); - - for (int i = ids->count - 1; i >= 0; i--) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; - Tree_AddExtraIcon(extra, selected, hItem); - } - - delete ids; - m_tree.DeleteItem(hItem); - - m_tree.UnselectAll(); - } - - int ShowPopup(int popup) - { - // Fix selection - HTREEITEM hSelected = m_tree.GetDropHilight(); - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - if (hItem != hSelected && m_tree.IsSelected(hItem)) - m_tree.DropHilite(hItem); - - hItem = m_tree.GetNextSibling(hItem); - } - - HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_OPT_POPUP)); - HMENU submenu = GetSubMenu(menu, popup); - TranslateMenu(submenu); - - uint32_t pos = GetMessagePos(); - int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_LEFTALIGN, LOWORD(pos), HIWORD(pos), 0, m_hwnd, nullptr); - - DestroyMenu(menu); - - // Revert selection - hItem = m_tree.GetRoot(); - while (hItem) { - if (hItem != hSelected && m_tree.IsSelected(hItem)) - m_tree.DropUnhilite(hItem); - hItem = m_tree.GetNextSibling(hItem); - } - - return ret; - } - - CCtrlTreeView m_tree; - CTimer m_timer; - -public: - CExtraIconOptsDlg() : - CDlgBase(g_plugin, IDD_EI_OPTIONS), - m_tree(this, IDC_EXTRAORDER), - m_timer(this, 1) - { - m_tree.SetFlags(MTREE_DND | MTREE_MULTISELECT); - m_tree.OnRightClick = Callback(this, &CExtraIconOptsDlg::onRClick); - - m_timer.OnEvent = Callback(this, &CExtraIconOptsDlg::onTimer); - } - - bool OnInitDialog() override - { - pGlgOptions = this; - - int numSlots = EXTRA_ICON_COUNT; - if (numSlots < (int)registeredExtraIcons.getCount()) { - HWND label = GetDlgItem(m_hwnd, IDC_MAX_ICONS_L); - SetWindowText(label, CMStringW(FORMAT, TranslateT("*only the first %d icons will be shown"), numSlots)); - ShowWindow(label, SW_SHOW); - } - - BuildIconList(); - return true; - } - - bool OnApply() override - { - // Store old slots - int *oldSlots = new int[registeredExtraIcons.getCount()]; - int lastUsedSlot = -1; - for (int i = 0; i < registeredExtraIcons.getCount(); i++) { - if (registeredExtraIcons[i]->getType() != EXTRAICON_TYPE_GROUP) - oldSlots[i] = registeredExtraIcons[i]->getSlot(); - else // Remove old slot for groups to re-set images - oldSlots[i] = -1; - lastUsedSlot = max(lastUsedSlot, registeredExtraIcons[i]->getSlot()); - } - lastUsedSlot = min(lastUsedSlot, EXTRA_ICON_COUNT); - - // Get user data and create new groups - LIST groups(1); - - uint8_t pos = 0; - int firstEmptySlot = 0; - HTREEITEM ht = m_tree.GetRoot(); - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; - tvi.stateMask = TVIS_STATEIMAGEMASK; - while (ht) { - tvi.hItem = ht; - m_tree.GetItem(&tvi); - - intlist *ids = (intlist*)tvi.lParam; - if (ids == nullptr || ids->count < 1) - continue; // ??? - - bool enabled = ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)); - int slot = (enabled ? firstEmptySlot++ : -1); - if (slot >= EXTRA_ICON_COUNT) - slot = -1; - - if (ids->count == 1) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[0] - 1]; - extra->setPosition(pos++); - extra->setSlot(slot); - } - else { - char name[128]; - mir_snprintf(name, "__group_%d", groups.getCount()); - - ExtraIconGroup *group = new ExtraIconGroup(name); - - for (int i = 0; i < ids->count; i++) { - BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; - extra->setPosition(pos++); - - group->addExtraIcon(extra); - } - - group->setSlot(slot); - groups.insert(group); - } - - ht = m_tree.GetNextSibling(ht); - } - - // Store data - for (auto &extra : registeredExtraIcons) { - char setting[512]; - mir_snprintf(setting, "Position_%s", extra->getName()); - db_set_w(0, EI_MODULE_NAME, setting, extra->getPosition()); - - mir_snprintf(setting, "Slot_%s", extra->getName()); - db_set_w(0, EI_MODULE_NAME, setting, extra->getSlot()); - } - - db_delete_module(0, EI_MODULE_NAME "Groups"); - db_set_w(0, EI_MODULE_NAME "Groups", "Count", groups.getCount()); - for (int k = 0; k < groups.getCount(); k++) { - ExtraIconGroup *group = groups[k]; - - char setting[512]; - mir_snprintf(setting, "%d_count", k); - db_set_w(0, EI_MODULE_NAME "Groups", setting, (uint16_t)group->m_items.getCount()); - - for (int j = 0; j < group->m_items.getCount(); j++) { - BaseExtraIcon *extra = group->m_items[j]; - - mir_snprintf(setting, "%d_%d", k, j); - db_set_s(0, EI_MODULE_NAME "Groups", setting, extra->getName()); - } - } - - // Clean removed slots - for (int j = firstEmptySlot; j <= lastUsedSlot; j++) - RemoveExtraIcons(j); - - // Apply icons to new slots - RebuildListsBasedOnGroups(groups); - for (auto &extra : extraIconsBySlot) - if (extra->isEnabled()) - extra->applyIcons(); - - delete[] oldSlots; - return true; - } - - void OnDestroy() override - { - pGlgOptions = nullptr; - - HTREEITEM hItem = m_tree.GetRoot(); - while (hItem) { - delete Tree_GetIDs(hItem); - hItem = m_tree.GetNextSibling(hItem); - } - - ImageList_Destroy(m_tree.GetImageList(TVSIL_NORMAL)); - } - - void onRClick(CCtrlTreeView::TEventInfo*) - { - HTREEITEM hSelected = m_tree.GetDropHilight(); - if (hSelected != nullptr && !m_tree.IsSelected(hSelected)) { - m_tree.UnselectAll(); - m_tree.SelectItem(hSelected); - } - - int sels = m_tree.GetNumSelected(); - if (sels > 1) { - if (ShowPopup(0) == ID_GROUP) { - GroupSelectedItems(); - NotifyChange(); - } - } - else if (sels == 1) { - HTREEITEM hItem = m_tree.GetSelection(); - intlist *ids = Tree_GetIDs(hItem); - if (ids->count > 1) { - if (ShowPopup(1) == ID_UNGROUP) { - UngroupSelectedItems(); - NotifyChange(); - } - } - } - } - - void BuildIconList() - { - HIMAGELIST hImageList = ImageList_Create(g_iIconSX, g_iIconSX, ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); - - for (auto &extra : registeredExtraIcons) { - extra->setID(registeredExtraIcons.indexOf(&extra)+1); - - HICON hIcon = IcoLib_GetIconByHandle(extra->getDescIcon()); - if (hIcon == nullptr) - ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); - else { - ImageList_AddIcon(hImageList, hIcon); - IcoLib_ReleaseIcon(hIcon); - } - } - m_tree.SetImageList(hImageList, TVSIL_NORMAL); - - for (auto &extra : extraIconsBySlot) { - if (extra->getType() == EXTRAICON_TYPE_GROUP) { - ExtraIconGroup *group = (ExtraIconGroup *)extra; - intlist ids; - for (auto &p : group->m_items) - ids.add(p->getID()); - Tree_AddExtraIconGroup(ids, extra->isEnabled()); - } - else Tree_AddExtraIcon((BaseExtraIcon*)extra, extra->isEnabled()); - } - - TVSORTCB sort = {}; - sort.hParent = nullptr; - sort.lParam = 0; - sort.lpfnCompare = CompareFunc; - m_tree.SortChildrenCB(&sort, 0); - } - - void onTimer(CTimer*) - { - m_timer.Stop(); - m_tree.DeleteAllItems(); - BuildIconList(); - } - - void ResetIconList() - { - m_timer.Start(100); - } -}; - -void eiOptionsRefresh() -{ - if (pGlgOptions) - pGlgOptions->ResetIconList(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int InitOptionsCallback(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szGroup.a = LPGEN("Contact list"); - odp.szTitle.a = LPGEN("Extra icons"); - odp.szTab.a = LPGEN("General"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CExtraIconOptsDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "extraicons.h" + +static class CExtraIconOptsDlg *pGlgOptions; + +int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2); + +struct intlist +{ + intlist() : count(0), data(nullptr) {} + ~intlist() { mir_free(data); } + + void add(int val) + { + data = (int*)mir_realloc(data, sizeof(int)*(count + 1)); + data[count++] = val; + } + + int count; + int *data; +}; + +static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM) +{ + intlist *a = (intlist*)lParam1; + intlist *b = (intlist*)lParam2; + return SortFunc(registeredExtraIcons[a->data[0] - 1], registeredExtraIcons[b->data[0] - 1]); +} + +// Functions ////////////////////////////////////////////////////////////////////////////////////// + +BOOL ScreenToClient(HWND hWnd, LPRECT lpRect) +{ + POINT pt; + pt.x = lpRect->left; + pt.y = lpRect->top; + + BOOL ret = ScreenToClient(hWnd, &pt); + if (!ret) + return ret; + + lpRect->left = pt.x; + lpRect->top = pt.y; + + pt.x = lpRect->right; + pt.y = lpRect->bottom; + + ret = ScreenToClient(hWnd, &pt); + + lpRect->right = pt.x; + lpRect->bottom = pt.y; + + return ret; +} + +static void RemoveExtraIcons(int slot) +{ + for (auto &hContact : Contacts()) + Clist_SetExtraIcon(hContact, slot, INVALID_HANDLE_VALUE); +} + +class CExtraIconOptsDlg : public CDlgBase +{ + intlist* Tree_GetIDs(HTREEITEM hItem) + { + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hItem; + m_tree.GetItem(&tvi); + return (intlist*)tvi.lParam; + } + + HTREEITEM Tree_AddExtraIcon(BaseExtraIcon *extra, bool selected, HTREEITEM hAfter = TVI_LAST) + { + intlist *ids = new intlist(); + ids->add(extra->getID()); + + TVINSERTSTRUCT tvis = {}; + tvis.hInsertAfter = hAfter; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; + tvis.item.stateMask = TVIS_STATEIMAGEMASK; + tvis.item.iSelectedImage = tvis.item.iImage = extra->getID(); + tvis.item.lParam = (LPARAM)ids; + tvis.item.pszText = (LPTSTR)extra->getDescription(); + tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); + return m_tree.InsertItem(&tvis); + } + + HTREEITEM Tree_AddExtraIconGroup(intlist &group, bool selected, HTREEITEM hAfter = TVI_LAST) + { + intlist *ids = new intlist(); + CMStringW desc; + int img = 0; + for (int i = 0; i < group.count; i++) { + BaseExtraIcon *extra = registeredExtraIcons[group.data[i] - 1]; + ids->add(extra->getID()); + + if (img == 0 && extra->getDescIcon() != nullptr) + img = extra->getID(); + + if (i > 0) + desc += L" / "; + desc += extra->getDescription(); + } + + TVINSERTSTRUCT tvis = {}; + tvis.hInsertAfter = hAfter; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; + tvis.item.stateMask = TVIS_STATEIMAGEMASK; + tvis.item.iSelectedImage = tvis.item.iImage = img; + tvis.item.lParam = (LPARAM)ids; + tvis.item.pszText = (wchar_t*)desc.c_str(); + tvis.item.state = INDEXTOSTATEIMAGEMASK(selected ? 2 : 1); + return m_tree.InsertItem(&tvis); + } + + void GroupSelectedItems() + { + LIST<_TREEITEM> toRemove(1); + intlist ids; + bool selected = false; + HTREEITEM hPlace = nullptr; + + // Find items + HTREEITEM hItem = m_tree.GetRoot(); + TVITEMEX tvi = { 0 }; + tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_TEXT | TVIF_STATE; + while (hItem) { + if (m_tree.IsSelected(hItem)) { + if (hPlace == nullptr) + hPlace = hItem; + + tvi.hItem = hItem; + m_tree.GetItem(&tvi); + + intlist *iids = (intlist*)tvi.lParam; + for (int i = 0; i < iids->count; i++) + ids.add(iids->data[i]); + + if ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)) + selected = true; + + toRemove.insert(hItem); + } + + hItem = m_tree.GetNextSibling(hItem); + } + + if (hPlace != nullptr) { + // Add new + HTREEITEM hNew = Tree_AddExtraIconGroup(ids, selected, hPlace); + + // Remove old + for (auto &p : toRemove) { + delete Tree_GetIDs(p); + m_tree.DeleteItem(p); + } + + // Select + m_tree.UnselectAll(); + m_tree.SelectItem(hNew); + } + } + + void UngroupSelectedItems() + { + HTREEITEM hItem = m_tree.GetSelection(); + if (hItem == nullptr) + return; + + intlist *ids = Tree_GetIDs(hItem); + if (ids->count < 2) + return; + + bool selected = m_tree.IsSelected(hItem); + + for (int i = ids->count - 1; i >= 0; i--) { + BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; + Tree_AddExtraIcon(extra, selected, hItem); + } + + delete ids; + m_tree.DeleteItem(hItem); + + m_tree.UnselectAll(); + } + + int ShowPopup(int popup) + { + // Fix selection + HTREEITEM hSelected = m_tree.GetDropHilight(); + HTREEITEM hItem = m_tree.GetRoot(); + while (hItem) { + if (hItem != hSelected && m_tree.IsSelected(hItem)) + m_tree.DropHilite(hItem); + + hItem = m_tree.GetNextSibling(hItem); + } + + HMENU menu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_OPT_POPUP)); + HMENU submenu = GetSubMenu(menu, popup); + TranslateMenu(submenu); + + uint32_t pos = GetMessagePos(); + int ret = TrackPopupMenu(submenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_LEFTALIGN, LOWORD(pos), HIWORD(pos), 0, m_hwnd, nullptr); + + DestroyMenu(menu); + + // Revert selection + hItem = m_tree.GetRoot(); + while (hItem) { + if (hItem != hSelected && m_tree.IsSelected(hItem)) + m_tree.DropUnhilite(hItem); + hItem = m_tree.GetNextSibling(hItem); + } + + return ret; + } + + CCtrlTreeView m_tree; + CTimer m_timer; + +public: + CExtraIconOptsDlg() : + CDlgBase(g_plugin, IDD_EI_OPTIONS), + m_tree(this, IDC_EXTRAORDER), + m_timer(this, 1) + { + m_tree.SetFlags(MTREE_DND | MTREE_MULTISELECT); + m_tree.OnRightClick = Callback(this, &CExtraIconOptsDlg::onRClick); + + m_timer.OnEvent = Callback(this, &CExtraIconOptsDlg::onTimer); + } + + bool OnInitDialog() override + { + pGlgOptions = this; + + int numSlots = EXTRA_ICON_COUNT; + if (numSlots < (int)registeredExtraIcons.getCount()) { + HWND label = GetDlgItem(m_hwnd, IDC_MAX_ICONS_L); + SetWindowText(label, CMStringW(FORMAT, TranslateT("*only the first %d icons will be shown"), numSlots)); + ShowWindow(label, SW_SHOW); + } + + BuildIconList(); + return true; + } + + bool OnApply() override + { + // Store old slots + int *oldSlots = new int[registeredExtraIcons.getCount()]; + int lastUsedSlot = -1; + for (int i = 0; i < registeredExtraIcons.getCount(); i++) { + if (registeredExtraIcons[i]->getType() != EXTRAICON_TYPE_GROUP) + oldSlots[i] = registeredExtraIcons[i]->getSlot(); + else // Remove old slot for groups to re-set images + oldSlots[i] = -1; + lastUsedSlot = max(lastUsedSlot, registeredExtraIcons[i]->getSlot()); + } + lastUsedSlot = min(lastUsedSlot, EXTRA_ICON_COUNT); + + // Get user data and create new groups + LIST groups(1); + + uint8_t pos = 0; + int firstEmptySlot = 0; + HTREEITEM ht = m_tree.GetRoot(); + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE; + tvi.stateMask = TVIS_STATEIMAGEMASK; + while (ht) { + tvi.hItem = ht; + m_tree.GetItem(&tvi); + + intlist *ids = (intlist*)tvi.lParam; + if (ids == nullptr || ids->count < 1) + continue; // ??? + + bool enabled = ((tvi.state & INDEXTOSTATEIMAGEMASK(3)) == INDEXTOSTATEIMAGEMASK(2)); + int slot = (enabled ? firstEmptySlot++ : -1); + if (slot >= EXTRA_ICON_COUNT) + slot = -1; + + if (ids->count == 1) { + BaseExtraIcon *extra = registeredExtraIcons[ids->data[0] - 1]; + extra->setPosition(pos++); + extra->setSlot(slot); + } + else { + char name[128]; + mir_snprintf(name, "__group_%d", groups.getCount()); + + ExtraIconGroup *group = new ExtraIconGroup(name); + + for (int i = 0; i < ids->count; i++) { + BaseExtraIcon *extra = registeredExtraIcons[ids->data[i] - 1]; + extra->setPosition(pos++); + + group->addExtraIcon(extra); + } + + group->setSlot(slot); + groups.insert(group); + } + + ht = m_tree.GetNextSibling(ht); + } + + // Store data + for (auto &extra : registeredExtraIcons) { + char setting[512]; + mir_snprintf(setting, "Position_%s", extra->getName()); + db_set_w(0, EI_MODULE_NAME, setting, extra->getPosition()); + + mir_snprintf(setting, "Slot_%s", extra->getName()); + db_set_w(0, EI_MODULE_NAME, setting, extra->getSlot()); + } + + db_delete_module(0, EI_MODULE_NAME "Groups"); + db_set_w(0, EI_MODULE_NAME "Groups", "Count", groups.getCount()); + for (int k = 0; k < groups.getCount(); k++) { + ExtraIconGroup *group = groups[k]; + + char setting[512]; + mir_snprintf(setting, "%d_count", k); + db_set_w(0, EI_MODULE_NAME "Groups", setting, (uint16_t)group->m_items.getCount()); + + for (int j = 0; j < group->m_items.getCount(); j++) { + BaseExtraIcon *extra = group->m_items[j]; + + mir_snprintf(setting, "%d_%d", k, j); + db_set_s(0, EI_MODULE_NAME "Groups", setting, extra->getName()); + } + } + + // Clean removed slots + for (int j = firstEmptySlot; j <= lastUsedSlot; j++) + RemoveExtraIcons(j); + + // Apply icons to new slots + RebuildListsBasedOnGroups(groups); + for (auto &extra : extraIconsBySlot) + if (extra->isEnabled()) + extra->applyIcons(); + + delete[] oldSlots; + return true; + } + + void OnDestroy() override + { + pGlgOptions = nullptr; + + HTREEITEM hItem = m_tree.GetRoot(); + while (hItem) { + delete Tree_GetIDs(hItem); + hItem = m_tree.GetNextSibling(hItem); + } + + ImageList_Destroy(m_tree.GetImageList(TVSIL_NORMAL)); + } + + void onRClick(CCtrlTreeView::TEventInfo*) + { + HTREEITEM hSelected = m_tree.GetDropHilight(); + if (hSelected != nullptr && !m_tree.IsSelected(hSelected)) { + m_tree.UnselectAll(); + m_tree.SelectItem(hSelected); + } + + int sels = m_tree.GetNumSelected(); + if (sels > 1) { + if (ShowPopup(0) == ID_GROUP) { + GroupSelectedItems(); + NotifyChange(); + } + } + else if (sels == 1) { + HTREEITEM hItem = m_tree.GetSelection(); + intlist *ids = Tree_GetIDs(hItem); + if (ids->count > 1) { + if (ShowPopup(1) == ID_UNGROUP) { + UngroupSelectedItems(); + NotifyChange(); + } + } + } + } + + void BuildIconList() + { + HIMAGELIST hImageList = ImageList_Create(g_iIconSX, g_iIconSX, ILC_COLOR32 | ILC_MASK, 2, 2); + ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); + + for (auto &extra : registeredExtraIcons) { + extra->setID(registeredExtraIcons.indexOf(&extra)+1); + + HICON hIcon = IcoLib_GetIconByHandle(extra->getDescIcon()); + if (hIcon == nullptr) + ImageList_AddIcon_NotShared(hImageList, MAKEINTRESOURCE(IDI_BLANK)); + else { + ImageList_AddIcon(hImageList, hIcon); + IcoLib_ReleaseIcon(hIcon); + } + } + m_tree.SetImageList(hImageList, TVSIL_NORMAL); + + for (auto &extra : extraIconsBySlot) { + if (extra->getType() == EXTRAICON_TYPE_GROUP) { + ExtraIconGroup *group = (ExtraIconGroup *)extra; + intlist ids; + for (auto &p : group->m_items) + ids.add(p->getID()); + Tree_AddExtraIconGroup(ids, extra->isEnabled()); + } + else Tree_AddExtraIcon((BaseExtraIcon*)extra, extra->isEnabled()); + } + + TVSORTCB sort = {}; + sort.hParent = nullptr; + sort.lParam = 0; + sort.lpfnCompare = CompareFunc; + m_tree.SortChildrenCB(&sort, 0); + } + + void onTimer(CTimer*) + { + m_timer.Stop(); + m_tree.DeleteAllItems(); + BuildIconList(); + } + + void ResetIconList() + { + m_timer.Start(100); + } +}; + +void eiOptionsRefresh() +{ + if (pGlgOptions) + pGlgOptions->ResetIconList(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int InitOptionsCallback(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.szGroup.a = LPGEN("Contact list"); + odp.szTitle.a = LPGEN("Extra icons"); + odp.szTab.a = LPGEN("General"); + odp.flags = ODPF_BOLDGROUPS; + odp.pDialog = new CExtraIconOptsDlg(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/mir_app/src/ei_services.cpp b/src/mir_app/src/ei_services.cpp index 9f41eb4679..f55551980d 100644 --- a/src/mir_app/src/ei_services.cpp +++ b/src/mir_app/src/ei_services.cpp @@ -1,504 +1,504 @@ -/* - -Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team - -This is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this file; see the file license.txt. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "m_cluiframes.h" - -#include "extraicons.h" -#include "usedIcons.h" -#include "clc.h" - -// Prototypes /////////////////////////////////////////////////////////////////////////// - -int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2) -{ - int ret = p1->getPosition() - p2->getPosition(); - if (ret != 0) - return ret; - - return p1->getID() - p2->getID(); -} - -LIST extraIconsBySlot(10, SortFunc); -LIST registeredExtraIcons(10, PtrKeySortT); - -static bool clistRebuildAlreadyCalled = false, clistApplyAlreadyCalled = false; - -// Functions //////////////////////////////////////////////////////////////////////////// - -int InitOptionsCallback(WPARAM wParam, LPARAM lParam); - -int ConvertToClistSlot(int slot) -{ - if (slot < 0) - return slot; - - return slot + 1; -} - -int ExtraImage_ExtraIDToColumnNum(int extra) -{ - return (extra < 1 || extra > EXTRA_ICON_COUNT) ? -1 : extra - 1; -} - -int Clist_SetExtraIcon(MCONTACT hContact, int slot, HANDLE hImage) -{ - if (g_clistApi.hwndContactTree == nullptr) - return -1; - - int icol = ExtraImage_ExtraIDToColumnNum(ConvertToClistSlot(slot)); - if (icol == -1) - return -1; - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGE, hContact, MAKELPARAM(icol, hImage)); - return 0; -} - -BaseExtraIcon* GetExtraIconByName(const char *name) -{ - for (auto &extra : registeredExtraIcons) - if (mir_strcmp(name, extra->getName()) == 0) - return extra; - - return nullptr; -} - -static void LoadGroups(LIST &groups) -{ - int count = db_get_w(0, EI_MODULE_NAME "Groups", "Count", 0); - for (int i = 0; i < count; i++) { - char setting[512]; - mir_snprintf(setting, "%d_count", i); - unsigned int items = db_get_w(0, EI_MODULE_NAME "Groups", setting, 0); - if (items < 1) - continue; - - mir_snprintf(setting, "__group_%d", i); - ExtraIconGroup *group = new ExtraIconGroup(setting); - - for (unsigned int j = 0; j < items; j++) { - mir_snprintf(setting, "%d_%d", i, j); - ptrA szIconName(db_get_sa(0, EI_MODULE_NAME "Groups", setting)); - if (IsEmpty(szIconName)) - continue; - - BaseExtraIcon *extra = GetExtraIconByName(szIconName); - if (extra == nullptr) - continue; - - group->m_items.insert(extra); - if (extra->getSlot() >= 0) - group->setSlot(extra->getSlot()); - } - - if (group->m_items.getCount() < 2) { - delete group; - continue; - } - - groups.insert(group); - } -} - -static ExtraIconGroup* IsInGroup(LIST &groups, BaseExtraIcon *extra) -{ - for (auto &group : groups) - for (auto &it : group->m_items) - if (extra == it) - return group; - - return nullptr; -} - -void RebuildListsBasedOnGroups(LIST &groups) -{ - for (auto &extra : registeredExtraIcons) - extra->setParent(nullptr); - - for (auto &extra : extraIconsBySlot) - if (extra->getType() == EXTRAICON_TYPE_GROUP) - delete extra; - extraIconsBySlot.destroy(); - - for (auto &group : groups) { - for (auto &it : group->m_items) - it->setParent(group); - - extraIconsBySlot.insert(group); - } - - for (auto &extra : registeredExtraIcons) - if (extra->getParent() == nullptr) - extraIconsBySlot.insert(extra); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void ResetSlots(BaseExtraIcon *extra, ExtraIconGroup *group, int iOldSlot = -1) -{ - int slot = 0, oldMaxSlot = -1; - for (auto &ex : extraIconsBySlot) { - if (ex->getSlot() < 0) - continue; - - int oldSlot = ex->getSlot(); - if (oldSlot > oldMaxSlot) - oldMaxSlot = oldSlot+1; - - ex->setSlot(slot++); - - if (clistApplyAlreadyCalled && (ex == group || ex == extra || oldSlot != slot)) - ex->applyIcons(); - } - - if (iOldSlot > oldMaxSlot) - oldMaxSlot = iOldSlot + 1; - - // slots were freed, we need to clear one or more items - if (extra == nullptr) - for (int i = slot; i < oldMaxSlot; i++) - for (auto &hContact : Contacts()) - Clist_SetExtraIcon(hContact, i, INVALID_HANDLE_VALUE); - - if (!Miranda_IsTerminated()) { - Clist_InitAutoRebuild(g_clistApi.hwndContactTree); - eiOptionsRefresh(); - } -} - -void KillModuleExtraIcons(CMPluginBase *pPlugin) -{ - LIST arIcons(1); - - for (auto &it : registeredExtraIcons.rev_iter()) - if (it->m_pPlugin == pPlugin) { - arIcons.insert(it); - registeredExtraIcons.removeItem(&it); - } - - if (arIcons.getCount() == 0) - return; - - int iOldSlot = -1; - for (auto &it : arIcons) - if (it->getSlot() > iOldSlot) - iOldSlot = it->getSlot() + 1; - - LIST groups(1); - LoadGroups(groups); - RebuildListsBasedOnGroups(groups); - ResetSlots(0, 0, iOldSlot); - - for (auto &it : arIcons) - delete it; -} - -/////////////////////////////////////////////////////////////////////////////// - -int ClistExtraListRebuild(WPARAM, LPARAM) -{ - clistRebuildAlreadyCalled = true; - - ResetIcons(); - - for (auto &it : extraIconsBySlot) - it->rebuildIcons(); - - return 0; -} - -int ClistExtraImageApply(WPARAM hContact, LPARAM) -{ - if (hContact == 0) - return 0; - - clistApplyAlreadyCalled = true; - - for (auto &it : extraIconsBySlot) - it->doApply(hContact); - - return 0; -} - -int ClistExtraClick(WPARAM hContact, LPARAM lParam) -{ - if (hContact == 0) - return 0; - - int clistSlot = (int)lParam; - - for (auto &extra : extraIconsBySlot) { - if (ConvertToClistSlot(extra->getSlot()) == clistSlot) { - extra->onClick(hContact); - break; - } - } - - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// Extra image list functions - -HANDLE hEventExtraImageListRebuilding, hEventExtraImageApplying, hEventExtraClick; - -static bool bImageCreated = false; -static HIMAGELIST hExtraImageList = nullptr; - -MIR_APP_DLL(HANDLE) ExtraIcon_AddIcon(HICON hIcon) -{ - if (hExtraImageList == nullptr || hIcon == nullptr) - return INVALID_HANDLE_VALUE; - - int res = ImageList_AddIcon(hExtraImageList, hIcon); - return (res > 0xFFFE) ? INVALID_HANDLE_VALUE : (HANDLE)res; -} - -MIR_APP_DLL(void) ExtraIcon_Reload() -{ - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRASPACE, db_get_b(0, "CLUI", "ExtraColumnSpace", 18), 0); - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, 0); - - if (hExtraImageList) - ImageList_Destroy(hExtraImageList); - - hExtraImageList = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, 1, 256); - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hExtraImageList); - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); - NotifyEventHooks(hEventExtraImageListRebuilding, 0, 0); - bImageCreated = true; -} - -MIR_APP_DLL(void) ExtraIcon_SetAll(MCONTACT hContact) -{ - if (g_clistApi.hwndContactTree == nullptr) - return; - - if (!bImageCreated) - ExtraIcon_Reload(); - - SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); - - if (hContact == 0) { - for (auto &it : Contacts()) - NotifyEventHooks(hEventExtraImageApplying, it, 0); - } - else NotifyEventHooks(hEventExtraImageApplying, hContact, 0); - - g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE); -} - -/////////////////////////////////////////////////////////////////////////////// -// external functions - -static void EI_PostCreate(BaseExtraIcon *extra, int flags) -{ - char setting[512]; - mir_snprintf(setting, "Position_%s", extra->getName()); - extra->setPosition(db_get_w(0, EI_MODULE_NAME, setting, 1000)); - - mir_snprintf(setting, "Slot_%s", extra->getName()); - int slot = db_get_w(0, EI_MODULE_NAME, setting, -100); - if (slot == EMPTY_EXTRA_ICON) - slot = -1; - else if (slot == -100) { - if (flags & EIF_DISABLED_BY_DEFAULT) { - db_set_w(0, EI_MODULE_NAME, setting, EMPTY_EXTRA_ICON); - slot = -1; - } - else slot = 1; - } - extra->setSlot(slot); - - registeredExtraIcons.insert(extra); - - LIST groups(1); - LoadGroups(groups); - - ExtraIconGroup *group = IsInGroup(groups, extra); - if (group != nullptr) - RebuildListsBasedOnGroups(groups); - else { - for (auto &it : groups) - delete it; - - extraIconsBySlot.insert(extra); - } - - if (slot >= 0 || group != nullptr) { - if (clistRebuildAlreadyCalled) - extra->rebuildIcons(); - - ResetSlots(extra, group); - } -} - -EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterCallback(const char *name, const char *description, HANDLE descIcon, - MIRANDAHOOK RebuildIcons, MIRANDAHOOK ApplyIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) -{ - // EXTRAICON_TYPE_CALLBACK - if (IsEmpty(name) || IsEmpty(description)) - return nullptr; - - if (ApplyIcon == nullptr || RebuildIcons == nullptr) - return nullptr; - - // no way to merge - if (GetExtraIconByName(name) != nullptr) - return nullptr; - - ptrW tszDesc(mir_a2u(description)); - - BaseExtraIcon *extra = new CallbackExtraIcon(name, tszDesc, descIcon, RebuildIcons, ApplyIcon, OnClick, onClickParam); - extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress(RebuildIcons)); - EI_PostCreate(extra, flags); - return extra; -} - -EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterIcolib(const char *name, const char *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) -{ - if (IsEmpty(name) || IsEmpty(description)) - return nullptr; - - ptrW tszDesc(mir_a2u(description)); - - BaseExtraIcon *extra = GetExtraIconByName(name); - if (extra != nullptr) { - if (extra->getType() != EXTRAICON_TYPE_ICOLIB) - return nullptr; - - // Found one, now merge it - if (descIcon) - extra->setDescIcon(descIcon); - - if (OnClick != nullptr) - extra->setOnClick(OnClick, onClickParam); - - if (extra->getSlot() > 0) { - if (clistRebuildAlreadyCalled) - extra->rebuildIcons(); - if (clistApplyAlreadyCalled) - extra->applyIcons(); - } - } - else { - extra = new IcolibExtraIcon(name, tszDesc, descIcon, OnClick, onClickParam); - extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress((void*)name)); - EI_PostCreate(extra, flags); - } - - return extra; -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) ExtraIcon_SetIcon(HANDLE hExtraIcon, MCONTACT hContact, HANDLE hImage) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, hImage, false); - - return extra->setIcon(hContact, hImage); -} - -MIR_APP_DLL(int) ExtraIcon_SetIconByName(HANDLE hExtraIcon, MCONTACT hContact, const char *icoName) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, (HANDLE)icoName, true); - - return extra->setIconByName(hContact, icoName); -} - -MIR_APP_DLL(int) ExtraIcon_Clear(HANDLE hExtraIcon, MCONTACT hContact) -{ - if (hExtraIcon == nullptr || hContact == 0) - return -1; - - BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); - if (extra == nullptr) - return -1; - - if (extra->getParent()) - return extra->getParent()->internalSetIcon(extra, hContact, nullptr, false); - - return extra->setIcon(hContact, nullptr); -} - -/////////////////////////////////////////////////////////////////////////////// - -static IconItem iconList[] = -{ - { LPGEN("Chat activity"), "ChatActivity", IDI_CHAT }, - { LPGEN("Mute chat"), "ChatMute", IDI_OFF }, - { LPGEN("Male"), "gender_male", IDI_MALE }, - { LPGEN("Female"), "gender_female", IDI_FEMALE }, - { LPGEN("Database"), "database", IDI_DATABASE }, -}; - -void LoadExtraIconsModule() -{ - // Events - hEventExtraClick = CreateHookableEvent(ME_CLIST_EXTRA_CLICK); - hEventExtraImageApplying = CreateHookableEvent(ME_CLIST_EXTRA_IMAGE_APPLY); - hEventExtraImageListRebuilding = CreateHookableEvent(ME_CLIST_EXTRA_LIST_REBUILD); - - // Icons - g_plugin.registerIcon(LPGEN("Contact list"), iconList); - - // Hooks - HookEvent(ME_OPT_INITIALISE, InitOptionsCallback); - - HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ClistExtraListRebuild); - HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ClistExtraImageApply); - HookEvent(ME_CLIST_EXTRA_CLICK, ClistExtraClick); - - DefaultExtraIcons_Load(); -} - -void UnloadExtraIconsModule(void) -{ - for (auto &extra : extraIconsBySlot) - if (extra->getType() == EXTRAICON_TYPE_GROUP) - delete extra; - - for (auto &it : registeredExtraIcons) - delete it; - - if (hExtraImageList) { - ImageList_Destroy(hExtraImageList); - hExtraImageList = nullptr; - } -} +/* + +Copyright (C) 2009 Ricardo Pescuma Domenecci +Copyright (C) 2012-23 Miranda NG team + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "m_cluiframes.h" + +#include "extraicons.h" +#include "usedIcons.h" +#include "clc.h" + +// Prototypes /////////////////////////////////////////////////////////////////////////// + +int SortFunc(const ExtraIcon *p1, const ExtraIcon *p2) +{ + int ret = p1->getPosition() - p2->getPosition(); + if (ret != 0) + return ret; + + return p1->getID() - p2->getID(); +} + +LIST extraIconsBySlot(10, SortFunc); +LIST registeredExtraIcons(10, PtrKeySortT); + +static bool clistRebuildAlreadyCalled = false, clistApplyAlreadyCalled = false; + +// Functions //////////////////////////////////////////////////////////////////////////// + +int InitOptionsCallback(WPARAM wParam, LPARAM lParam); + +int ConvertToClistSlot(int slot) +{ + if (slot < 0) + return slot; + + return slot + 1; +} + +int ExtraImage_ExtraIDToColumnNum(int extra) +{ + return (extra < 1 || extra > EXTRA_ICON_COUNT) ? -1 : extra - 1; +} + +int Clist_SetExtraIcon(MCONTACT hContact, int slot, HANDLE hImage) +{ + if (g_clistApi.hwndContactTree == nullptr) + return -1; + + int icol = ExtraImage_ExtraIDToColumnNum(ConvertToClistSlot(slot)); + if (icol == -1) + return -1; + + SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGE, hContact, MAKELPARAM(icol, hImage)); + return 0; +} + +BaseExtraIcon* GetExtraIconByName(const char *name) +{ + for (auto &extra : registeredExtraIcons) + if (mir_strcmp(name, extra->getName()) == 0) + return extra; + + return nullptr; +} + +static void LoadGroups(LIST &groups) +{ + int count = db_get_w(0, EI_MODULE_NAME "Groups", "Count", 0); + for (int i = 0; i < count; i++) { + char setting[512]; + mir_snprintf(setting, "%d_count", i); + unsigned int items = db_get_w(0, EI_MODULE_NAME "Groups", setting, 0); + if (items < 1) + continue; + + mir_snprintf(setting, "__group_%d", i); + ExtraIconGroup *group = new ExtraIconGroup(setting); + + for (unsigned int j = 0; j < items; j++) { + mir_snprintf(setting, "%d_%d", i, j); + ptrA szIconName(db_get_sa(0, EI_MODULE_NAME "Groups", setting)); + if (IsEmpty(szIconName)) + continue; + + BaseExtraIcon *extra = GetExtraIconByName(szIconName); + if (extra == nullptr) + continue; + + group->m_items.insert(extra); + if (extra->getSlot() >= 0) + group->setSlot(extra->getSlot()); + } + + if (group->m_items.getCount() < 2) { + delete group; + continue; + } + + groups.insert(group); + } +} + +static ExtraIconGroup* IsInGroup(LIST &groups, BaseExtraIcon *extra) +{ + for (auto &group : groups) + for (auto &it : group->m_items) + if (extra == it) + return group; + + return nullptr; +} + +void RebuildListsBasedOnGroups(LIST &groups) +{ + for (auto &extra : registeredExtraIcons) + extra->setParent(nullptr); + + for (auto &extra : extraIconsBySlot) + if (extra->getType() == EXTRAICON_TYPE_GROUP) + delete extra; + extraIconsBySlot.destroy(); + + for (auto &group : groups) { + for (auto &it : group->m_items) + it->setParent(group); + + extraIconsBySlot.insert(group); + } + + for (auto &extra : registeredExtraIcons) + if (extra->getParent() == nullptr) + extraIconsBySlot.insert(extra); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void ResetSlots(BaseExtraIcon *extra, ExtraIconGroup *group, int iOldSlot = -1) +{ + int slot = 0, oldMaxSlot = -1; + for (auto &ex : extraIconsBySlot) { + if (ex->getSlot() < 0) + continue; + + int oldSlot = ex->getSlot(); + if (oldSlot > oldMaxSlot) + oldMaxSlot = oldSlot+1; + + ex->setSlot(slot++); + + if (clistApplyAlreadyCalled && (ex == group || ex == extra || oldSlot != slot)) + ex->applyIcons(); + } + + if (iOldSlot > oldMaxSlot) + oldMaxSlot = iOldSlot + 1; + + // slots were freed, we need to clear one or more items + if (extra == nullptr) + for (int i = slot; i < oldMaxSlot; i++) + for (auto &hContact : Contacts()) + Clist_SetExtraIcon(hContact, i, INVALID_HANDLE_VALUE); + + if (!Miranda_IsTerminated()) { + Clist_InitAutoRebuild(g_clistApi.hwndContactTree); + eiOptionsRefresh(); + } +} + +void KillModuleExtraIcons(CMPluginBase *pPlugin) +{ + LIST arIcons(1); + + for (auto &it : registeredExtraIcons.rev_iter()) + if (it->m_pPlugin == pPlugin) { + arIcons.insert(it); + registeredExtraIcons.removeItem(&it); + } + + if (arIcons.getCount() == 0) + return; + + int iOldSlot = -1; + for (auto &it : arIcons) + if (it->getSlot() > iOldSlot) + iOldSlot = it->getSlot() + 1; + + LIST groups(1); + LoadGroups(groups); + RebuildListsBasedOnGroups(groups); + ResetSlots(0, 0, iOldSlot); + + for (auto &it : arIcons) + delete it; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ClistExtraListRebuild(WPARAM, LPARAM) +{ + clistRebuildAlreadyCalled = true; + + ResetIcons(); + + for (auto &it : extraIconsBySlot) + it->rebuildIcons(); + + return 0; +} + +int ClistExtraImageApply(WPARAM hContact, LPARAM) +{ + if (hContact == 0) + return 0; + + clistApplyAlreadyCalled = true; + + for (auto &it : extraIconsBySlot) + it->doApply(hContact); + + return 0; +} + +int ClistExtraClick(WPARAM hContact, LPARAM lParam) +{ + if (hContact == 0) + return 0; + + int clistSlot = (int)lParam; + + for (auto &extra : extraIconsBySlot) { + if (ConvertToClistSlot(extra->getSlot()) == clistSlot) { + extra->onClick(hContact); + break; + } + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// Extra image list functions + +HANDLE hEventExtraImageListRebuilding, hEventExtraImageApplying, hEventExtraClick; + +static bool bImageCreated = false; +static HIMAGELIST hExtraImageList = nullptr; + +MIR_APP_DLL(HANDLE) ExtraIcon_AddIcon(HICON hIcon) +{ + if (hExtraImageList == nullptr || hIcon == nullptr) + return INVALID_HANDLE_VALUE; + + int res = ImageList_AddIcon(hExtraImageList, hIcon); + return (res > 0xFFFE) ? INVALID_HANDLE_VALUE : (HANDLE)res; +} + +MIR_APP_DLL(void) ExtraIcon_Reload() +{ + SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRASPACE, db_get_b(0, "CLUI", "ExtraColumnSpace", 18), 0); + SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, 0); + + if (hExtraImageList) + ImageList_Destroy(hExtraImageList); + + hExtraImageList = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, 1, 256); + + SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hExtraImageList); + SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); + NotifyEventHooks(hEventExtraImageListRebuilding, 0, 0); + bImageCreated = true; +} + +MIR_APP_DLL(void) ExtraIcon_SetAll(MCONTACT hContact) +{ + if (g_clistApi.hwndContactTree == nullptr) + return; + + if (!bImageCreated) + ExtraIcon_Reload(); + + SendMessage(g_clistApi.hwndContactTree, CLM_SETEXTRACOLUMNS, EXTRA_ICON_COUNT, 0); + + if (hContact == 0) { + for (auto &it : Contacts()) + NotifyEventHooks(hEventExtraImageApplying, it, 0); + } + else NotifyEventHooks(hEventExtraImageApplying, hContact, 0); + + g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE); +} + +/////////////////////////////////////////////////////////////////////////////// +// external functions + +static void EI_PostCreate(BaseExtraIcon *extra, int flags) +{ + char setting[512]; + mir_snprintf(setting, "Position_%s", extra->getName()); + extra->setPosition(db_get_w(0, EI_MODULE_NAME, setting, 1000)); + + mir_snprintf(setting, "Slot_%s", extra->getName()); + int slot = db_get_w(0, EI_MODULE_NAME, setting, -100); + if (slot == EMPTY_EXTRA_ICON) + slot = -1; + else if (slot == -100) { + if (flags & EIF_DISABLED_BY_DEFAULT) { + db_set_w(0, EI_MODULE_NAME, setting, EMPTY_EXTRA_ICON); + slot = -1; + } + else slot = 1; + } + extra->setSlot(slot); + + registeredExtraIcons.insert(extra); + + LIST groups(1); + LoadGroups(groups); + + ExtraIconGroup *group = IsInGroup(groups, extra); + if (group != nullptr) + RebuildListsBasedOnGroups(groups); + else { + for (auto &it : groups) + delete it; + + extraIconsBySlot.insert(extra); + } + + if (slot >= 0 || group != nullptr) { + if (clistRebuildAlreadyCalled) + extra->rebuildIcons(); + + ResetSlots(extra, group); + } +} + +EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterCallback(const char *name, const char *description, HANDLE descIcon, + MIRANDAHOOK RebuildIcons, MIRANDAHOOK ApplyIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) +{ + // EXTRAICON_TYPE_CALLBACK + if (IsEmpty(name) || IsEmpty(description)) + return nullptr; + + if (ApplyIcon == nullptr || RebuildIcons == nullptr) + return nullptr; + + // no way to merge + if (GetExtraIconByName(name) != nullptr) + return nullptr; + + ptrW tszDesc(mir_a2u(description)); + + BaseExtraIcon *extra = new CallbackExtraIcon(name, tszDesc, descIcon, RebuildIcons, ApplyIcon, OnClick, onClickParam); + extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress(RebuildIcons)); + EI_PostCreate(extra, flags); + return extra; +} + +EXTERN_C MIR_APP_DLL(HANDLE) ExtraIcon_RegisterIcolib(const char *name, const char *description, HANDLE descIcon, MIRANDAHOOKPARAM OnClick, LPARAM onClickParam, int flags) +{ + if (IsEmpty(name) || IsEmpty(description)) + return nullptr; + + ptrW tszDesc(mir_a2u(description)); + + BaseExtraIcon *extra = GetExtraIconByName(name); + if (extra != nullptr) { + if (extra->getType() != EXTRAICON_TYPE_ICOLIB) + return nullptr; + + // Found one, now merge it + if (descIcon) + extra->setDescIcon(descIcon); + + if (OnClick != nullptr) + extra->setOnClick(OnClick, onClickParam); + + if (extra->getSlot() > 0) { + if (clistRebuildAlreadyCalled) + extra->rebuildIcons(); + if (clistApplyAlreadyCalled) + extra->applyIcons(); + } + } + else { + extra = new IcolibExtraIcon(name, tszDesc, descIcon, OnClick, onClickParam); + extra->m_pPlugin = &GetPluginByInstance(GetInstByAddress((void*)name)); + EI_PostCreate(extra, flags); + } + + return extra; +} + +/////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) ExtraIcon_SetIcon(HANDLE hExtraIcon, MCONTACT hContact, HANDLE hImage) +{ + if (hExtraIcon == nullptr || hContact == 0) + return -1; + + BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); + if (extra == nullptr) + return -1; + + if (extra->getParent()) + return extra->getParent()->internalSetIcon(extra, hContact, hImage, false); + + return extra->setIcon(hContact, hImage); +} + +MIR_APP_DLL(int) ExtraIcon_SetIconByName(HANDLE hExtraIcon, MCONTACT hContact, const char *icoName) +{ + if (hExtraIcon == nullptr || hContact == 0) + return -1; + + BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); + if (extra == nullptr) + return -1; + + if (extra->getParent()) + return extra->getParent()->internalSetIcon(extra, hContact, (HANDLE)icoName, true); + + return extra->setIconByName(hContact, icoName); +} + +MIR_APP_DLL(int) ExtraIcon_Clear(HANDLE hExtraIcon, MCONTACT hContact) +{ + if (hExtraIcon == nullptr || hContact == 0) + return -1; + + BaseExtraIcon *extra = registeredExtraIcons.find((BaseExtraIcon*)hExtraIcon); + if (extra == nullptr) + return -1; + + if (extra->getParent()) + return extra->getParent()->internalSetIcon(extra, hContact, nullptr, false); + + return extra->setIcon(hContact, nullptr); +} + +/////////////////////////////////////////////////////////////////////////////// + +static IconItem iconList[] = +{ + { LPGEN("Chat activity"), "ChatActivity", IDI_CHAT }, + { LPGEN("Mute chat"), "ChatMute", IDI_OFF }, + { LPGEN("Male"), "gender_male", IDI_MALE }, + { LPGEN("Female"), "gender_female", IDI_FEMALE }, + { LPGEN("Database"), "database", IDI_DATABASE }, +}; + +void LoadExtraIconsModule() +{ + // Events + hEventExtraClick = CreateHookableEvent(ME_CLIST_EXTRA_CLICK); + hEventExtraImageApplying = CreateHookableEvent(ME_CLIST_EXTRA_IMAGE_APPLY); + hEventExtraImageListRebuilding = CreateHookableEvent(ME_CLIST_EXTRA_LIST_REBUILD); + + // Icons + g_plugin.registerIcon(LPGEN("Contact list"), iconList); + + // Hooks + HookEvent(ME_OPT_INITIALISE, InitOptionsCallback); + + HookEvent(ME_CLIST_EXTRA_LIST_REBUILD, ClistExtraListRebuild); + HookEvent(ME_CLIST_EXTRA_IMAGE_APPLY, ClistExtraImageApply); + HookEvent(ME_CLIST_EXTRA_CLICK, ClistExtraClick); + + DefaultExtraIcons_Load(); +} + +void UnloadExtraIconsModule(void) +{ + for (auto &extra : extraIconsBySlot) + if (extra->getType() == EXTRAICON_TYPE_GROUP) + delete extra; + + for (auto &it : registeredExtraIcons) + delete it; + + if (hExtraImageList) { + ImageList_Destroy(hExtraImageList); + hExtraImageList = nullptr; + } +} diff --git a/src/mir_app/src/encrypt.cpp b/src/mir_app/src/encrypt.cpp index 79298a9221..d08d8e24c6 100644 --- a/src/mir_app/src/encrypt.cpp +++ b/src/mir_app/src/encrypt.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/encrypt.h b/src/mir_app/src/encrypt.h index bcb37cd799..034e38804e 100644 --- a/src/mir_app/src/encrypt.h +++ b/src/mir_app/src/encrypt.h @@ -1,47 +1,47 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -struct MCryptoProvider : public CRYPTO_PROVIDER -{ - MCryptoProvider(const CRYPTO_PROVIDER *pProvider) - { - memcpy(this, pProvider, sizeof(*pProvider)); - pszName = mir_strdup(pszName); - if (dwFlags & CPF_UNICODE) - szDescr.w = mir_wstrdup(TranslateW_LP(pProvider->szDescr.w, pProvider->pPlugin)); - else - szDescr.w = mir_a2u(TranslateA_LP(pProvider->szDescr.a, pProvider->pPlugin)); - } - - ~MCryptoProvider() - { - mir_free(pszName); - mir_free(szDescr.w); - } -}; - -extern OBJLIST arCryptoProviders; - -void InitCryptMenuItem(CMenuItem &mi); +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#pragma once + +struct MCryptoProvider : public CRYPTO_PROVIDER +{ + MCryptoProvider(const CRYPTO_PROVIDER *pProvider) + { + memcpy(this, pProvider, sizeof(*pProvider)); + pszName = mir_strdup(pszName); + if (dwFlags & CPF_UNICODE) + szDescr.w = mir_wstrdup(TranslateW_LP(pProvider->szDescr.w, pProvider->pPlugin)); + else + szDescr.w = mir_a2u(TranslateA_LP(pProvider->szDescr.a, pProvider->pPlugin)); + } + + ~MCryptoProvider() + { + mir_free(pszName); + mir_free(szDescr.w); + } +}; + +extern OBJLIST arCryptoProviders; + +void InitCryptMenuItem(CMenuItem &mi); diff --git a/src/mir_app/src/enterstring.cpp b/src/mir_app/src/enterstring.cpp index adfed7e805..69897231c1 100644 --- a/src/mir_app/src/enterstring.cpp +++ b/src/mir_app/src/enterstring.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/extracticon.cpp b/src/mir_app/src/extracticon.cpp index 5bd7a1cd76..415eaee2f1 100644 --- a/src/mir_app/src/extracticon.cpp +++ b/src/mir_app/src/extracticon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/extraicons.h b/src/mir_app/src/extraicons.h index d0b4b8a711..b22075a467 100644 --- a/src/mir_app/src/extraicons.h +++ b/src/mir_app/src/extraicons.h @@ -1,7 +1,7 @@ /* Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/src/mir_app/src/filter.cpp b/src/mir_app/src/filter.cpp index 8e6af687b5..27bf9cd3b0 100644 --- a/src/mir_app/src/filter.cpp +++ b/src/mir_app/src/filter.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/filter.h b/src/mir_app/src/filter.h index a583915eba..8c8964bfa1 100644 --- a/src/mir_app/src/filter.h +++ b/src/mir_app/src/filter.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/findadd.cpp b/src/mir_app/src/findadd.cpp index 88850481d8..bd7613b444 100644 --- a/src/mir_app/src/findadd.cpp +++ b/src/mir_app/src/findadd.cpp @@ -1,1035 +1,1035 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "findadd.h" - -#define TIMERID_THROBBER 111 - -#define HM_SEARCHACK (WM_USER+10) -#define M_SETGROUPVISIBILITIES (WM_USER+11) - -static HWND hwndFindAdd = nullptr; -static HANDLE hHookModulesLoaded = nullptr; -static HGENMENU hMainMenuItem = nullptr; -static int OnSystemModulesLoaded(WPARAM wParam, LPARAM lParam); - -static int FindAddDlgResizer(HWND, LPARAM lParam, UTILRESIZECONTROL *urc) -{ - static int y, nextY, oldTop; - FindAddDlgData *dat = (FindAddDlgData*)lParam; - - switch (urc->wId) { - case IDC_RESULTS: - return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; - - case IDOK: - dat->minDlgHeight = nextY + urc->rcItem.bottom - urc->rcItem.top; - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_ADD: - case IDC_MOREOPTIONS: - return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; - - case IDC_STATUSBAR: - return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; - - case IDC_PROTOIDGROUP: //the resize is always processed in template order - nextY = y = urc->rcItem.top; - if (dat->showProtoId) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - break; - - case IDC_EMAILGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showEmail) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_NAMEGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showName) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_ADVANCEDGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showAdvanced) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_TINYEXTENDEDGROUP: - oldTop = urc->rcItem.top; - y = nextY; - if (dat->showTiny) { - int height = urc->dlgNewSize.cy - y - (urc->dlgOriginalSize.cy - urc->rcItem.bottom); - nextY = y + 200; //min height for custom dialog - urc->rcItem.top = urc->rcItem.bottom - height; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; - - case IDC_BYEMAIL: - case IDC_EMAIL: - case IDC_BYNAME: - case IDC_STNAMENICK: - case IDC_STNAMEFIRST: - case IDC_STNAMELAST: - case IDC_NAMENICK: - case IDC_NAMEFIRST: - case IDC_NAMELAST: - case IDC_BYADVANCED: - case IDC_BYCUSTOM: - case IDC_ADVANCED: - OffsetRect(&urc->rcItem, 0, y - oldTop); - return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; - - case IDC_HEADERBAR: - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; - } - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -static void RenderThrobber(HDC hdc, RECT *rcItem, int *throbbing, int *pivot) -{ - InflateRect(rcItem, -1, 0); - int width = rcItem->right - rcItem->left; - int height = rcItem->bottom - rcItem->top; - int height2 = height / 2; - - if (*throbbing) { - /* create memdc */ - HDC hMemDC = CreateCompatibleDC(nullptr); - HBITMAP hBitmap = (HBITMAP)SelectObject(hMemDC, CreateCompatibleBitmap(hdc, width, height)); - /* flush it */ - RECT rc; - rc.left = rc.top = 0; - rc.right = width; - rc.bottom = height; - HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); - FillRect(hMemDC, &rc, hBr); - DeleteObject(hBr); - /* set up the pen */ - HPEN hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 4, GetSysColor(COLOR_BTNSHADOW))); - /* draw everything before the pivot */ - int x = *pivot; - while (x > (-height)) { - MoveToEx(hMemDC, x + height2, 0, nullptr); - LineTo(hMemDC, x - height2, height); - x -= 12; - } - - /* draw everything after the pivot */ - x = *pivot; - while (x < width + height) { - MoveToEx(hMemDC, x + height2, 0, nullptr); - LineTo(hMemDC, x - height2, height); - x += 12; - } - - /* move the pivot */ - *pivot += 2; - /* reset the pivot point if it gets past the rect */ - if (*pivot > width) *pivot = 0; - /* put back the old pen and delete the new one */ - DeleteObject(SelectObject(hMemDC, hPen)); - /* cap the top and bottom */ - hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE))); - MoveToEx(hMemDC, 0, 0, nullptr); - LineTo(hMemDC, width, 0); - MoveToEx(hMemDC, 0, height - 1, nullptr); - LineTo(hMemDC, width, height - 1); - /* select in the old pen and delete the new pen */ - DeleteObject(SelectObject(hMemDC, hPen)); - /* paint to screen */ - BitBlt(hdc, rcItem->left, rcItem->top, width, height, hMemDC, 0, 0, SRCCOPY); - /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */ - hBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); - DeleteObject(hBitmap); - DeleteDC(hMemDC); - } - else { - /* just flush the DC */ - HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); - FillRect(hdc, rcItem, hBr); - DeleteObject(hBr); - } -} - -static void StartThrobber(HWND hwndDlg, FindAddDlgData *dat) -{ - dat->throbbing = 1; - SetTimer(hwndDlg, TIMERID_THROBBER, 25, nullptr); -} - -static void StopThrobber(HWND hwndDlg, FindAddDlgData *dat) -{ - KillTimer(hwndDlg, TIMERID_THROBBER); - dat->throbbing = 0; - dat->pivot = 0; - InvalidateRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), nullptr, FALSE); -} - -static LRESULT CALLBACK AdvancedSearchDlgSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_COMMAND) { - HWND parentHwnd = GetParent(hwndDlg); - switch (LOWORD(wParam)) { - case IDOK: - SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDOK)); - SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); - break; - - case IDCANCEL: - CheckDlgButton(parentHwnd, IDC_ADVANCED, BST_UNCHECKED); - SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDC_ADVANCED, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDC_ADVANCED)); - SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); - break; - } - } - return mir_callNextSubclass(hwndDlg, AdvancedSearchDlgSubclassProc, msg, wParam, lParam); -} - -static void ShowAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == nullptr) - return; - - if (dat->hwndAdvSearch == nullptr) { - RECT rc; - dat->hwndAdvSearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)hwndDlg); - if (dat->hwndAdvSearch != nullptr) - mir_subclassWindow(dat->hwndAdvSearch, AdvancedSearchDlgSubclassProc); - GetWindowRect(GetDlgItem(hwndDlg, IDC_RESULTS), &rc); - SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - - AnimateWindow(dat->hwndAdvSearch, 150, AW_ACTIVATE | AW_SLIDE | AW_HOR_POSITIVE); - RedrawWindow(dat->hwndAdvSearch, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); - - CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_CHECKED); -} - -static void ReposTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - if (dat->hwndTinySearch == nullptr) - return; - - RECT rc; - RECT clientRect; - POINT pt = { 0, 0 }; - GetWindowRect(GetDlgItem(hwndDlg, IDC_TINYEXTENDEDGROUP), &rc); - GetWindowRect(dat->hwndTinySearch, &clientRect); - pt.x = rc.left; - pt.y = rc.top; - ScreenToClient(hwndDlg, &pt); - SetWindowPos(dat->hwndTinySearch, nullptr, pt.x + 5, pt.y + 15, rc.right - rc.left - 10, rc.bottom - rc.top - 30, SWP_NOZORDER); -} - -static void ShowTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == nullptr) - return; - - if (dat->hwndTinySearch == nullptr) { - dat->hwndTinySearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)/*GetDlgItem(*/hwndDlg/*, IDC_TINYEXTENDEDGROUP)*/); - if (dat->hwndTinySearch) - ReposTinySearchDlg(hwndDlg, dat); - else - dat->showTiny = false; - } - ShowWindow(dat->hwndTinySearch, SW_SHOW); -} - -static void HideAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) -{ - if (dat->hwndAdvSearch == nullptr) - return; - - AnimateWindow(dat->hwndAdvSearch, 150, AW_HIDE | AW_BLEND); - CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_UNCHECKED); -} - -void EnableResultButtons(HWND hwndDlg, int enable) -{ - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enable); - EnableWindow(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), enable); -} - -static const int controls[] = { IDC_BYPROTOID, IDC_BYEMAIL, IDC_BYNAME, IDC_BYADVANCED, IDC_BYCUSTOM }; - -static void CheckSearchTypeRadioButton(HWND hwndDlg, int idControl) -{ - for (auto &it : controls) - CheckDlgButton(hwndDlg, it, idControl == it ? BST_CHECKED : BST_UNCHECKED); -} - -#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.") -#define sttErrTitle TranslateT("Search") - -static void SetListItemText(HWND hwndList, int idx, int col, wchar_t *szText) -{ - if (szText == nullptr || *szText == 0) - szText = TranslateT(""); - - ListView_SetItemText(hwndList, idx, col, szText); -} - -static wchar_t* sttDecodeString(uint32_t dwFlags, MAllStrings &src) -{ - if (dwFlags & PSR_UNICODE) - return mir_wstrdup(src.w); - - if (dwFlags & PSR_UTF8) - return mir_utf8decodeW(src.a); - - return mir_a2u(src.a); -} - -static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - FindAddDlgData *dat = (FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS); - RECT rc, rc2; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - Window_SetSkinIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER); - dat = (FindAddDlgData*)mir_calloc(sizeof(FindAddDlgData)); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - dat->notSearchedYet = 1; - dat->iLastColumnSortIndex = 1; - dat->bSortAscending = 1; - SendDlgItemMessage(hwndDlg, IDC_MOREOPTIONS, BUTTONSETARROW, 1, 0); - SendDlgItemMessage(hwndDlg, IDOK, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Ctrl+Search add contact"), BATF_UNICODE); - - ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); - - GetClientRect(hwndList, &rc); - { - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH; - lvc.pszText = TranslateT("Results"); - lvc.cx = rc.right - 1; - ListView_InsertColumn(hwndList, 0, &lvc); - - LVITEM lvi; - lvi.mask = LVIF_TEXT; - lvi.iItem = 0; - lvi.iSubItem = 0; - lvi.pszText = TranslateT("There are no results to display."); - ListView_InsertItem(hwndList, &lvi); - - // Allocate a reasonable amount of space in the status bar - HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); - SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_GETFONT, 0, 0)); - - SIZE textSize; - GetTextExtentPoint32(hdc, TranslateT("Searching"), (int)mir_wstrlen(TranslateT("Searching")), &textSize); - - int partWidth[3]; - partWidth[0] = textSize.cx; - GetTextExtentPoint32(hdc, L"01234567890123456789", 20, &textSize); - partWidth[0] += textSize.cx; - ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); - partWidth[1] = partWidth[0] + 150; - partWidth[2] = -1; - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETPARTS, _countof(partWidth), (LPARAM)partWidth); - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETTEXT, 1 | SBT_OWNERDRAW, 0); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - - wchar_t *szProto = nullptr; - ptrW tszLast(db_get_wsa(0, "FindAdd", "LastSearched")); - if (tszLast) - szProto = NEWWSTR_ALLOCA(tszLast); - - int index = 0, cbwidth = 0, netProtoCount = 0; - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (caps & PF1_ANYSEARCH) - netProtoCount++; - } - dat->himlComboIcons = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, netProtoCount + 1, netProtoCount + 1); - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_SETIMAGELIST, 0, (LPARAM)dat->himlComboIcons); - - COMBOBOXEXITEM cbei; - cbei.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM; - cbei.iItem = 0; - hdc = GetDC(hwndDlg); - SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, WM_GETFONT, 0, 0)); - if (netProtoCount > 1) { - cbei.pszText = TranslateT("All networks"); - GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); - if (textSize.cx > cbwidth) - cbwidth = textSize.cx; - cbei.iImage = cbei.iSelectedImage = ImageList_AddSkinIcon(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL); - cbei.lParam = 0; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); - cbei.iItem++; - } - - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (!(caps & PF1_ANYSEARCH)) - continue; - - cbei.pszText = pa->tszAccountName; - GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); - if (textSize.cx > cbwidth) - cbwidth = textSize.cx; - - HICON hIcon = (HICON)CallProtoServiceInt(0, pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0); - cbei.iImage = cbei.iSelectedImage = ImageList_AddIcon(dat->himlComboIcons, hIcon); - DestroyIcon(hIcon); - cbei.lParam = (LPARAM)pa->szModuleName; - SendDlgItemMessageA(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); - if (szProto && cbei.pszText && !mir_wstrcmp(szProto, pa->tszAccountName)) - index = cbei.iItem; - cbei.iItem++; - } - cbwidth += 32; - - RECT rect; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rect); - if ((rect.right - rect.left) < cbwidth) - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETDROPPEDWIDTH, cbwidth, 0); - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETCURSEL, index, 0); - - SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); - Utils_RestoreWindowPosition(hwndDlg, 0, "FindAdd", ""); - } - return TRUE; - - case WM_SIZE: - Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FINDADD), FindAddDlgResizer, (LPARAM)dat); - ReposTinySearchDlg(hwndDlg, dat); - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_SIZE, 0, 0); - if (dat->notSearchedYet) { - GetClientRect(hwndList, &rc); - ListView_SetColumnWidth(hwndList, 0, rc.right); - } - __fallthrough; - - case WM_MOVE: - if (dat && dat->hwndAdvSearch) { - GetWindowRect(hwndList, &rc); - SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } - break; - - case WM_GETMINMAXINFO: - GetWindowRect(hwndList, &rc); - GetWindowRect(hwndDlg, &rc2); - { - MINMAXINFO *mmi = (MINMAXINFO*)lParam; - mmi->ptMinTrackSize.x = rc.left - rc2.left + 10 + GetSystemMetrics(SM_CXFRAME); - GetClientRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); - mmi->ptMinTrackSize.x += rc.right + 5; - GetClientRect(GetDlgItem(hwndDlg, IDC_ADD), &rc); - mmi->ptMinTrackSize.x += rc.right + 5; - GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); - mmi->ptMinTrackSize.y = dat->minDlgHeight + 20 + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFRAME); - GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); - mmi->ptMinTrackSize.y += rc.bottom; - } - return 0; - - case M_SETGROUPVISIBILITIES: - dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0; - { - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (szProto == (char *)CB_ERR) - break; - if (szProto == nullptr) { - for (auto &pa : g_arAccounts) { - if (pa->IsEnabled()) { - uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; - if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; - } - } - } - else { - uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_BASICSEARCH) dat->showProtoId = 1; - if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; - if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; - - if (protoCaps & PF1_EXTSEARCHUI) dat->showAdvanced = 1; - else if (protoCaps & PF1_EXTSEARCH) dat->showTiny = 1; - - if (protoCaps & PF1_USERIDISEMAIL && dat->showProtoId) { dat->showProtoId = 0; dat->showEmail = 1; } - if (dat->showProtoId) { - wchar_t *wszUniqueId = (wchar_t *)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); - if (wszUniqueId) - SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, wszUniqueId); - else - SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, TranslateT("Handle")); - - if (protoCaps & PF1_NUMERICUSERID) - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE) | ES_NUMBER); - else - SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE)&~ES_NUMBER); - } - } - - if (dat->showTiny) - ShowTinySearchDlg(hwndDlg, dat); - else if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - -#define en(id, t) ShowWindow( GetDlgItem(hwndDlg, IDC_##id), dat->show##t?SW_SHOW:SW_HIDE) - en(PROTOIDGROUP, ProtoId); en(BYPROTOID, ProtoId); en(PROTOID, ProtoId); - en(EMAILGROUP, Email); en(BYEMAIL, Email); en(EMAIL, Email); - en(NAMEGROUP, Name); en(BYNAME, Name); - en(STNAMENICK, Name); en(NAMENICK, Name); - en(STNAMEFIRST, Name); en(NAMEFIRST, Name); - en(STNAMELAST, Name); en(NAMELAST, Name); - en(ADVANCEDGROUP, Advanced); en(BYADVANCED, Advanced); en(ADVANCED, Advanced); - en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny); -#undef en - int checkmarkVisible = (dat->showAdvanced && IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) || - (dat->showEmail && IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) || - (dat->showTiny && IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) || - (dat->showName && IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) || - (dat->showProtoId && IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)); - if (!checkmarkVisible) { - if (dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); - else if (dat->showEmail) CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); - else if (dat->showName) CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); - else if (dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); - else if (dat->showTiny) CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); - } - - SendMessage(hwndDlg, WM_SIZE, 0, 0); - - MINMAXINFO mmi; - SendMessage(hwndDlg, WM_GETMINMAXINFO, 0, (LPARAM)&mmi); - - GetWindowRect(hwndDlg, &rc); - if (rc.bottom - rc.top < mmi.ptMinTrackSize.y) - SetWindowPos(hwndDlg, nullptr, 0, 0, rc.right - rc.left, mmi.ptMinTrackSize.y, SWP_NOZORDER | SWP_NOMOVE); - } - break; - - case WM_TIMER: - if (wParam == TIMERID_THROBBER) { - int borders[3]; - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETBORDERS, 0, (LPARAM)borders); - - SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETRECT, 1, (LPARAM)&rc); - InflateRect(&rc, -borders[2] / 2, -borders[1] / 2); - HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); - RenderThrobber(hdc, &rc, &dat->throbbing, &dat->pivot); - ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); - } - break; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam; - if (dis->CtlID == IDC_STATUSBAR && dis->itemID == 1) { - RenderThrobber(dis->hDC, &dis->rcItem, &dat->throbbing, &dat->pivot); - return TRUE; - } - } - break; - - case WM_NOTIFY: - if (wParam == IDC_RESULTS) { - switch (((LPNMHDR)lParam)->code) { - case LVN_ITEMCHANGED: - { - int count = ListView_GetSelectedCount(hwndList); - if (dat->notSearchedYet) - count = 0; - EnableResultButtons(hwndDlg, count); - } - break; - - case LVN_COLUMNCLICK: - HDITEM hdi; - hdi.mask = HDI_FORMAT; - hdi.fmt = HDF_LEFT | HDF_STRING; - Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); - - LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam; - if (nmlv->iSubItem != dat->iLastColumnSortIndex) { - dat->bSortAscending = TRUE; - dat->iLastColumnSortIndex = nmlv->iSubItem; - } - else dat->bSortAscending = !dat->bSortAscending; - - hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP); - Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); - - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - } - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDC_PROTOLIST: - if (HIWORD(wParam) == CBN_SELCHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - if (dat->hwndAdvSearch) { - DestroyWindow(dat->hwndAdvSearch); - dat->hwndAdvSearch = nullptr; - } - if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); - } - break; - - case IDC_BYPROTOID: - case IDC_BYEMAIL: - case IDC_BYNAME: - { - int count = ListView_GetSelectedCount(hwndList); - if (dat->notSearchedYet) - count = 0; - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), count); - HideAdvancedSearchDlg(hwndDlg, dat); - } - break; - - case IDC_PROTOID: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); - } - break; - - case IDC_EMAIL: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); - } - break; - - case IDC_NAMENICK: - case IDC_NAMEFIRST: - case IDC_NAMELAST: - if (HIWORD(wParam) == EN_CHANGE) { - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); - } - break; - - case IDC_ADVANCED: - EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), ListView_GetSelectedCount(hwndList) > 0); - if (IsDlgButtonChecked(hwndDlg, IDC_ADVANCED)) - ShowAdvancedSearchDlg(hwndDlg, dat); - else - HideAdvancedSearchDlg(hwndDlg, dat); - CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); - break; - - case IDCANCEL: - DestroyWindow(hwndDlg); - break; - - case IDOK: - HideAdvancedSearchDlg(hwndDlg, dat); - if (dat->searchCount) { //cancel search - SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); - if (dat->hResultHook) { UnhookEvent(dat->hResultHook); dat->hResultHook = nullptr; } - if (dat->search) { mir_free(dat->search); dat->search = nullptr; } - dat->searchCount = 0; - StopThrobber(hwndDlg, dat); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - } - else { - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - if (dat->search) - mir_free(dat->search), dat->search = nullptr; - dat->searchCount = 0; - dat->hResultHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_SEARCHACK); - if (IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndTinySearch); - else if (IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)) { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); - rtrimw(str); - if (str[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_BASICSEARCH, PF1_BASICSEARCH, str); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_EMAIL, str, _countof(str)); - rtrimw(str); - if (str[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYEMAIL, PF1_SEARCHBYEMAIL, str); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) { - wchar_t nick[256], first[256], last[256]; - PROTOSEARCHBYNAME psbn; - GetDlgItemText(hwndDlg, IDC_NAMENICK, nick, _countof(nick)); - GetDlgItemText(hwndDlg, IDC_NAMEFIRST, first, _countof(first)); - GetDlgItemText(hwndDlg, IDC_NAMELAST, last, _countof(last)); - psbn.pszFirstName = first; - psbn.pszLastName = last; - psbn.pszNick = nick; - if (nick[0] == 0 && first[0] == 0 && last[0] == 0) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYNAME, PF1_SEARCHBYNAME, &psbn); - } - else if (IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) { - if (dat->hwndAdvSearch == nullptr) - MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); - else - BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndAdvSearch); - } - - if (dat->searchCount == 0) { - if (dat->hResultHook) { - UnhookEvent(dat->hResultHook); - dat->hResultHook = nullptr; - } - break; - } - - dat->notSearchedYet = 0; - FreeSearchResults(hwndList); - - CreateResultsColumns(hwndList, dat, szProto); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - SetStatusBarResultInfo(hwndDlg); - StartThrobber(hwndDlg, dat); - SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel")); - } - break; - - case IDC_ADD: - if (ListView_GetSelectedCount(hwndList) == 1) { - LVITEM lvi; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED); - ListView_GetItem(hwndList, &lvi); - ListSearchResult *lsr = (ListSearchResult*)lvi.lParam; - Contact::AddBySearch(lsr->szProto, &lsr->psr, hwndDlg); - } - else { - wchar_t str[256]; - GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); - if (*rtrimw(str) == 0) - break; - - char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - - PROTOSEARCHRESULT psr = { 0 }; - psr.cbSize = sizeof(psr); - psr.flags = PSR_UNICODE; - psr.id.w = str; - Contact::AddBySearch(szProto, &psr, hwndDlg); - } - break; - - case IDC_MOREOPTIONS: - GetWindowRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); - ShowMoreOptionsMenu(hwndDlg, rc.left, rc.bottom); - break; - } - - if (lParam && dat->hwndTinySearch == (HWND)lParam && - HIWORD(wParam) == EN_SETFOCUS && LOWORD(wParam) == 0 && - BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) { - CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); - } - break; - - case WM_CONTEXTMENU: - { - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - - LVHITTESTINFO lvhti; - lvhti.pt = pt; - ScreenToClient(hwndDlg, &pt); - switch (GetDlgCtrlID(ChildWindowFromPoint(hwndDlg, pt))) { - case IDC_RESULTS: - if (dat->notSearchedYet) - return TRUE; - ScreenToClient(hwndList, &lvhti.pt); - if (ListView_HitTest(hwndList, &lvhti) == -1) - break; - ShowMoreOptionsMenu(hwndDlg, (short)LOWORD(lParam), (short)HIWORD(lParam)); - return TRUE; - } - } - break; - - case HM_SEARCHACK: - { - ACKDATA *ack = (ACKDATA*)lParam; - if (ack->type != ACKTYPE_SEARCH) - break; - - int i; - for (i = 0; i < dat->searchCount; i++) - if (dat->search[i].hProcess == ack->hProcess && dat->search[i].hProcess != nullptr && !mir_strcmp(dat->search[i].szProto, ack->szModule)) break; - if (i == dat->searchCount) - break; - - if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) { - dat->searchCount--; - memmove(dat->search + i, dat->search + i + 1, sizeof(struct ProtoSearchInfo)*(dat->searchCount - i)); - if (dat->searchCount == 0) { - mir_free(dat->search); - dat->search = nullptr; - UnhookEvent(dat->hResultHook); - dat->hResultHook = nullptr; - SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); - StopThrobber(hwndDlg, dat); - } - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); - } - else if (ack->result == ACKRESULT_SEARCHRESULT && ack->lParam) { - CUSTOMSEARCHRESULTS *csr = (CUSTOMSEARCHRESULTS*)ack->lParam; - dat->bFlexSearchResult = TRUE; - PROTOSEARCHRESULT *psr = &csr->psr; - // check if this is column names data (psr->cbSize == 0) - if (psr->cbSize == 0) { // blob contain info about columns - // first remove all exist items - FreeSearchResults(hwndList); - ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too - - //second remove all columns - while (ListView_DeleteColumn(hwndList, 1)); //will delete fist column till it possible - - // now will add columns and captions; - LVCOLUMN lvc = { 0 }; - lvc.mask = LVCF_TEXT; - for (int iColumn = 0; iColumn < csr->nFieldCount; iColumn++) { - lvc.pszText = TranslateW(csr->pszFields[iColumn]); - ListView_InsertColumn(hwndList, iColumn + 1, &lvc); - } - } - else { // blob contain info about found contacts - ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); - lsr->szProto = ack->szModule; - memcpy(&lsr->psr, psr, psr->cbSize); - - /* Next block is not needed but behavior will be kept */ - lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); - lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); - lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); - lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); - lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); - lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; - - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.lParam = (LPARAM)lsr; - for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { - char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); - if (szComboProto == nullptr) continue; - if (!mir_strcmp(szComboProto, ack->szModule)) { - COMBOBOXEXITEM cbei = { 0 }; - cbei.mask = CBEIF_IMAGE; - cbei.iItem = i; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); - lvi.iImage = cbei.iImage; - } - } - int iItem = ListView_InsertItem(hwndList, &lvi); - for (int col = 0; col < csr->nFieldCount; col++) - SetListItemText(hwndList, iItem, col + 1, csr->pszFields[col]); - - ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); - iItem = 0; - while (ListView_SetColumnWidth(hwndList, iItem++, LVSCW_AUTOSIZE_USEHEADER)); - SetStatusBarResultInfo(hwndDlg); - } - break; - } - else if (ack->result == ACKRESULT_DATA) { - PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)ack->lParam; - ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); - lsr->szProto = ack->szModule; - - dat->bFlexSearchResult = FALSE; - - memcpy(&lsr->psr, psr, psr->cbSize); - lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); - lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); - lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); - lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); - lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); - lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; - - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.lParam = (LPARAM)lsr; - for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { - char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); - if (szComboProto == nullptr) continue; - if (!mir_strcmp(szComboProto, ack->szModule)) { - COMBOBOXEXITEM cbei = { 0 }; - cbei.mask = CBEIF_IMAGE; - cbei.iItem = i; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); - lvi.iImage = cbei.iImage; - break; - } - } - - int iItem = ListView_InsertItem(hwndList, &lvi); - SetListItemText(hwndList, iItem, 1, lsr->psr.id.w); - SetListItemText(hwndList, iItem, 2, lsr->psr.nick.w); - SetListItemText(hwndList, iItem, 3, lsr->psr.firstName.w); - SetListItemText(hwndList, iItem, 4, lsr->psr.lastName.w); - SetListItemText(hwndList, iItem, 5, lsr->psr.email.w); - SetStatusBarResultInfo(hwndDlg); - } - } - break; - - case WM_CLOSE: - DestroyWindow(hwndDlg); - break; - - case WM_DESTROY: - int len = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXTLEN, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); - wchar_t *szProto = (wchar_t*)alloca(sizeof(wchar_t)*(len + 1)); - if (szProto != nullptr) { - *szProto = '\0'; - SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXT, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), (LPARAM)szProto); - db_set_ws(0, "FindAdd", "LastSearched", szProto); - } - - SaveColumnSizes(hwndList); - if (dat->hResultHook != nullptr) - UnhookEvent(dat->hResultHook); - FreeSearchResults(hwndList); - ImageList_Destroy(dat->himlComboIcons); - mir_free(dat->search); - if (dat->hwndAdvSearch) { - DestroyWindow(dat->hwndAdvSearch); - dat->hwndAdvSearch = nullptr; - } - if (dat->hwndTinySearch) { - DestroyWindow(dat->hwndTinySearch); - dat->hwndTinySearch = nullptr; - } - mir_free(dat); - Window_FreeIcon_IcoLib(hwndDlg); - Utils_SaveWindowPosition(hwndDlg, 0, "FindAdd", ""); - break; - } - return FALSE; -} - -static INT_PTR FindAddCommand(WPARAM, LPARAM) -{ - if (IsWindow(hwndFindAdd)) { - ShowWindow(hwndFindAdd, SW_SHOWNORMAL); - SetForegroundWindow(hwndFindAdd); - SetFocus(hwndFindAdd); - } - else { - int netProtoCount = 0; - - // Make sure we have some networks to search on. This is not ideal since - // this check will be repeated every time the dialog is requested, but it - // must be done since this service can be called from other places than the menu. - // One alternative would be to only create the service if we have network - // protocols loaded but that would delay the creation until MODULE_LOADED and - // that is not good either... - for (auto &pa : g_arAccounts) { - if (!pa->IsEnabled()) - continue; - - int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_ANYSEARCH) - netProtoCount++; - } - if (netProtoCount > 0) - hwndFindAdd = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FINDADD), nullptr, DlgProcFindAdd); - } - return 0; -} - -int FindAddPreShutdown(WPARAM, LPARAM) -{ - if (IsWindow(hwndFindAdd)) - DestroyWindow(hwndFindAdd); - hwndFindAdd = nullptr; - return 0; -} - -int LoadFindAddModule(void) -{ - CreateServiceFunction(MS_FINDADD_FINDADD, FindAddCommand); - - HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded); - HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded); - HookEvent(ME_SYSTEM_PRESHUTDOWN, FindAddPreShutdown); - - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x860556b9, 0x1577, 0x4f6f, 0x8c, 0xb0, 0x93, 0x24, 0xa8, 0x2e, 0x20, 0x92); - mi.position = 500020000; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FINDUSER); - mi.name.a = LPGEN("&Find/add contacts..."); - mi.pszService = MS_FINDADD_FINDADD; - hMainMenuItem = Menu_AddMainMenuItem(&mi); - return 0; -} - -static int OnSystemModulesLoaded(WPARAM, LPARAM) -{ - int netProtoCount = 0; - - // Make sure we have some networks to search on. - for (auto &pa : g_arAccounts) { - int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); - if (protoCaps & PF1_ANYSEARCH) - netProtoCount++; - } - - Menu_ShowItem(hMainMenuItem, netProtoCount != 0); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "findadd.h" + +#define TIMERID_THROBBER 111 + +#define HM_SEARCHACK (WM_USER+10) +#define M_SETGROUPVISIBILITIES (WM_USER+11) + +static HWND hwndFindAdd = nullptr; +static HANDLE hHookModulesLoaded = nullptr; +static HGENMENU hMainMenuItem = nullptr; +static int OnSystemModulesLoaded(WPARAM wParam, LPARAM lParam); + +static int FindAddDlgResizer(HWND, LPARAM lParam, UTILRESIZECONTROL *urc) +{ + static int y, nextY, oldTop; + FindAddDlgData *dat = (FindAddDlgData*)lParam; + + switch (urc->wId) { + case IDC_RESULTS: + return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; + + case IDOK: + dat->minDlgHeight = nextY + urc->rcItem.bottom - urc->rcItem.top; + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; + + case IDC_ADD: + case IDC_MOREOPTIONS: + return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM; + + case IDC_STATUSBAR: + return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM; + + case IDC_PROTOIDGROUP: //the resize is always processed in template order + nextY = y = urc->rcItem.top; + if (dat->showProtoId) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; + break; + + case IDC_EMAILGROUP: + oldTop = urc->rcItem.top; + y = nextY; + if (dat->showEmail) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; + OffsetRect(&urc->rcItem, 0, y - oldTop); + return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; + + case IDC_NAMEGROUP: + oldTop = urc->rcItem.top; + y = nextY; + if (dat->showName) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; + OffsetRect(&urc->rcItem, 0, y - oldTop); + return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; + + case IDC_ADVANCEDGROUP: + oldTop = urc->rcItem.top; + y = nextY; + if (dat->showAdvanced) nextY = y + urc->rcItem.bottom - urc->rcItem.top + 7; + OffsetRect(&urc->rcItem, 0, y - oldTop); + return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; + + case IDC_TINYEXTENDEDGROUP: + oldTop = urc->rcItem.top; + y = nextY; + if (dat->showTiny) { + int height = urc->dlgNewSize.cy - y - (urc->dlgOriginalSize.cy - urc->rcItem.bottom); + nextY = y + 200; //min height for custom dialog + urc->rcItem.top = urc->rcItem.bottom - height; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM; + + case IDC_BYEMAIL: + case IDC_EMAIL: + case IDC_BYNAME: + case IDC_STNAMENICK: + case IDC_STNAMEFIRST: + case IDC_STNAMELAST: + case IDC_NAMENICK: + case IDC_NAMEFIRST: + case IDC_NAMELAST: + case IDC_BYADVANCED: + case IDC_BYCUSTOM: + case IDC_ADVANCED: + OffsetRect(&urc->rcItem, 0, y - oldTop); + return RD_ANCHORX_LEFT | RD_ANCHORY_CUSTOM; + + case IDC_HEADERBAR: + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH; + } + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +static void RenderThrobber(HDC hdc, RECT *rcItem, int *throbbing, int *pivot) +{ + InflateRect(rcItem, -1, 0); + int width = rcItem->right - rcItem->left; + int height = rcItem->bottom - rcItem->top; + int height2 = height / 2; + + if (*throbbing) { + /* create memdc */ + HDC hMemDC = CreateCompatibleDC(nullptr); + HBITMAP hBitmap = (HBITMAP)SelectObject(hMemDC, CreateCompatibleBitmap(hdc, width, height)); + /* flush it */ + RECT rc; + rc.left = rc.top = 0; + rc.right = width; + rc.bottom = height; + HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); + FillRect(hMemDC, &rc, hBr); + DeleteObject(hBr); + /* set up the pen */ + HPEN hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 4, GetSysColor(COLOR_BTNSHADOW))); + /* draw everything before the pivot */ + int x = *pivot; + while (x > (-height)) { + MoveToEx(hMemDC, x + height2, 0, nullptr); + LineTo(hMemDC, x - height2, height); + x -= 12; + } + + /* draw everything after the pivot */ + x = *pivot; + while (x < width + height) { + MoveToEx(hMemDC, x + height2, 0, nullptr); + LineTo(hMemDC, x - height2, height); + x += 12; + } + + /* move the pivot */ + *pivot += 2; + /* reset the pivot point if it gets past the rect */ + if (*pivot > width) *pivot = 0; + /* put back the old pen and delete the new one */ + DeleteObject(SelectObject(hMemDC, hPen)); + /* cap the top and bottom */ + hPen = (HPEN)SelectObject(hMemDC, CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE))); + MoveToEx(hMemDC, 0, 0, nullptr); + LineTo(hMemDC, width, 0); + MoveToEx(hMemDC, 0, height - 1, nullptr); + LineTo(hMemDC, width, height - 1); + /* select in the old pen and delete the new pen */ + DeleteObject(SelectObject(hMemDC, hPen)); + /* paint to screen */ + BitBlt(hdc, rcItem->left, rcItem->top, width, height, hMemDC, 0, 0, SRCCOPY); + /* select back in the old bitmap and delete the created one, as well as freeing the mem dc. */ + hBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); + DeleteObject(hBitmap); + DeleteDC(hMemDC); + } + else { + /* just flush the DC */ + HBRUSH hBr = GetSysColorBrush(COLOR_BTNFACE); + FillRect(hdc, rcItem, hBr); + DeleteObject(hBr); + } +} + +static void StartThrobber(HWND hwndDlg, FindAddDlgData *dat) +{ + dat->throbbing = 1; + SetTimer(hwndDlg, TIMERID_THROBBER, 25, nullptr); +} + +static void StopThrobber(HWND hwndDlg, FindAddDlgData *dat) +{ + KillTimer(hwndDlg, TIMERID_THROBBER); + dat->throbbing = 0; + dat->pivot = 0; + InvalidateRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), nullptr, FALSE); +} + +static LRESULT CALLBACK AdvancedSearchDlgSubclassProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_COMMAND) { + HWND parentHwnd = GetParent(hwndDlg); + switch (LOWORD(wParam)) { + case IDOK: + SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDOK, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDOK)); + SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); + break; + + case IDCANCEL: + CheckDlgButton(parentHwnd, IDC_ADVANCED, BST_UNCHECKED); + SendMessage(parentHwnd, WM_COMMAND, MAKEWPARAM(IDC_ADVANCED, BN_CLICKED), (LPARAM)GetDlgItem(parentHwnd, IDC_ADVANCED)); + SetFocus(GetDlgItem(parentHwnd, IDC_ADVANCED)); + break; + } + } + return mir_callNextSubclass(hwndDlg, AdvancedSearchDlgSubclassProc, msg, wParam, lParam); +} + +static void ShowAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) +{ + char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); + if (szProto == nullptr) + return; + + if (dat->hwndAdvSearch == nullptr) { + RECT rc; + dat->hwndAdvSearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)hwndDlg); + if (dat->hwndAdvSearch != nullptr) + mir_subclassWindow(dat->hwndAdvSearch, AdvancedSearchDlgSubclassProc); + GetWindowRect(GetDlgItem(hwndDlg, IDC_RESULTS), &rc); + SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + } + + AnimateWindow(dat->hwndAdvSearch, 150, AW_ACTIVATE | AW_SLIDE | AW_HOR_POSITIVE); + RedrawWindow(dat->hwndAdvSearch, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); + + CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_CHECKED); +} + +static void ReposTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) +{ + if (dat->hwndTinySearch == nullptr) + return; + + RECT rc; + RECT clientRect; + POINT pt = { 0, 0 }; + GetWindowRect(GetDlgItem(hwndDlg, IDC_TINYEXTENDEDGROUP), &rc); + GetWindowRect(dat->hwndTinySearch, &clientRect); + pt.x = rc.left; + pt.y = rc.top; + ScreenToClient(hwndDlg, &pt); + SetWindowPos(dat->hwndTinySearch, nullptr, pt.x + 5, pt.y + 15, rc.right - rc.left - 10, rc.bottom - rc.top - 30, SWP_NOZORDER); +} + +static void ShowTinySearchDlg(HWND hwndDlg, FindAddDlgData *dat) +{ + char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); + if (szProto == nullptr) + return; + + if (dat->hwndTinySearch == nullptr) { + dat->hwndTinySearch = (HWND)CallProtoServiceInt(0, szProto, PS_CREATEADVSEARCHUI, 0, (LPARAM)/*GetDlgItem(*/hwndDlg/*, IDC_TINYEXTENDEDGROUP)*/); + if (dat->hwndTinySearch) + ReposTinySearchDlg(hwndDlg, dat); + else + dat->showTiny = false; + } + ShowWindow(dat->hwndTinySearch, SW_SHOW); +} + +static void HideAdvancedSearchDlg(HWND hwndDlg, FindAddDlgData *dat) +{ + if (dat->hwndAdvSearch == nullptr) + return; + + AnimateWindow(dat->hwndAdvSearch, 150, AW_HIDE | AW_BLEND); + CheckDlgButton(hwndDlg, IDC_ADVANCED, BST_UNCHECKED); +} + +void EnableResultButtons(HWND hwndDlg, int enable) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), enable); + EnableWindow(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), enable); +} + +static const int controls[] = { IDC_BYPROTOID, IDC_BYEMAIL, IDC_BYNAME, IDC_BYADVANCED, IDC_BYCUSTOM }; + +static void CheckSearchTypeRadioButton(HWND hwndDlg, int idControl) +{ + for (auto &it : controls) + CheckDlgButton(hwndDlg, it, idControl == it ? BST_CHECKED : BST_UNCHECKED); +} + +#define sttErrMsg TranslateT("You haven't filled in the search field. Please enter a search term and try again.") +#define sttErrTitle TranslateT("Search") + +static void SetListItemText(HWND hwndList, int idx, int col, wchar_t *szText) +{ + if (szText == nullptr || *szText == 0) + szText = TranslateT(""); + + ListView_SetItemText(hwndList, idx, col, szText); +} + +static wchar_t* sttDecodeString(uint32_t dwFlags, MAllStrings &src) +{ + if (dwFlags & PSR_UNICODE) + return mir_wstrdup(src.w); + + if (dwFlags & PSR_UTF8) + return mir_utf8decodeW(src.a); + + return mir_a2u(src.a); +} + +static INT_PTR CALLBACK DlgProcFindAdd(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + FindAddDlgData *dat = (FindAddDlgData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + HWND hwndList = GetDlgItem(hwndDlg, IDC_RESULTS); + RECT rc, rc2; + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + Window_SetSkinIcon_IcoLib(hwndDlg, SKINICON_OTHER_FINDUSER); + dat = (FindAddDlgData*)mir_calloc(sizeof(FindAddDlgData)); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + dat->notSearchedYet = 1; + dat->iLastColumnSortIndex = 1; + dat->bSortAscending = 1; + SendDlgItemMessage(hwndDlg, IDC_MOREOPTIONS, BUTTONSETARROW, 1, 0); + SendDlgItemMessage(hwndDlg, IDOK, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Ctrl+Search add contact"), BATF_UNICODE); + + ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); + + GetClientRect(hwndList, &rc); + { + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT | LVCF_WIDTH; + lvc.pszText = TranslateT("Results"); + lvc.cx = rc.right - 1; + ListView_InsertColumn(hwndList, 0, &lvc); + + LVITEM lvi; + lvi.mask = LVIF_TEXT; + lvi.iItem = 0; + lvi.iSubItem = 0; + lvi.pszText = TranslateT("There are no results to display."); + ListView_InsertItem(hwndList, &lvi); + + // Allocate a reasonable amount of space in the status bar + HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); + SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_GETFONT, 0, 0)); + + SIZE textSize; + GetTextExtentPoint32(hdc, TranslateT("Searching"), (int)mir_wstrlen(TranslateT("Searching")), &textSize); + + int partWidth[3]; + partWidth[0] = textSize.cx; + GetTextExtentPoint32(hdc, L"01234567890123456789", 20, &textSize); + partWidth[0] += textSize.cx; + ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); + partWidth[1] = partWidth[0] + 150; + partWidth[2] = -1; + SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETPARTS, _countof(partWidth), (LPARAM)partWidth); + SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_SETTEXT, 1 | SBT_OWNERDRAW, 0); + SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); + + wchar_t *szProto = nullptr; + ptrW tszLast(db_get_wsa(0, "FindAdd", "LastSearched")); + if (tszLast) + szProto = NEWWSTR_ALLOCA(tszLast); + + int index = 0, cbwidth = 0, netProtoCount = 0; + for (auto &pa : g_arAccounts) { + if (!pa->IsEnabled()) + continue; + + uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); + if (caps & PF1_ANYSEARCH) + netProtoCount++; + } + dat->himlComboIcons = ImageList_Create(g_iIconSX, g_iIconSY, ILC_COLOR32 | ILC_MASK, netProtoCount + 1, netProtoCount + 1); + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_SETIMAGELIST, 0, (LPARAM)dat->himlComboIcons); + + COMBOBOXEXITEM cbei; + cbei.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM; + cbei.iItem = 0; + hdc = GetDC(hwndDlg); + SelectObject(hdc, (HFONT)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, WM_GETFONT, 0, 0)); + if (netProtoCount > 1) { + cbei.pszText = TranslateT("All networks"); + GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); + if (textSize.cx > cbwidth) + cbwidth = textSize.cx; + cbei.iImage = cbei.iSelectedImage = ImageList_AddSkinIcon(dat->himlComboIcons, SKINICON_OTHER_SEARCHALL); + cbei.lParam = 0; + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); + cbei.iItem++; + } + + for (auto &pa : g_arAccounts) { + if (!pa->IsEnabled()) + continue; + + uint32_t caps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); + if (!(caps & PF1_ANYSEARCH)) + continue; + + cbei.pszText = pa->tszAccountName; + GetTextExtentPoint32(hdc, cbei.pszText, (int)mir_wstrlen(cbei.pszText), &textSize); + if (textSize.cx > cbwidth) + cbwidth = textSize.cx; + + HICON hIcon = (HICON)CallProtoServiceInt(0, pa->szModuleName, PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0); + cbei.iImage = cbei.iSelectedImage = ImageList_AddIcon(dat->himlComboIcons, hIcon); + DestroyIcon(hIcon); + cbei.lParam = (LPARAM)pa->szModuleName; + SendDlgItemMessageA(hwndDlg, IDC_PROTOLIST, CBEM_INSERTITEM, 0, (LPARAM)&cbei); + if (szProto && cbei.pszText && !mir_wstrcmp(szProto, pa->tszAccountName)) + index = cbei.iItem; + cbei.iItem++; + } + cbwidth += 32; + + RECT rect; + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rect); + if ((rect.right - rect.left) < cbwidth) + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETDROPPEDWIDTH, cbwidth, 0); + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_SETCURSEL, index, 0); + + SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); + Utils_RestoreWindowPosition(hwndDlg, 0, "FindAdd", ""); + } + return TRUE; + + case WM_SIZE: + Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_FINDADD), FindAddDlgResizer, (LPARAM)dat); + ReposTinySearchDlg(hwndDlg, dat); + SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, WM_SIZE, 0, 0); + if (dat->notSearchedYet) { + GetClientRect(hwndList, &rc); + ListView_SetColumnWidth(hwndList, 0, rc.right); + } + __fallthrough; + + case WM_MOVE: + if (dat && dat->hwndAdvSearch) { + GetWindowRect(hwndList, &rc); + SetWindowPos(dat->hwndAdvSearch, nullptr, rc.left, rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + } + break; + + case WM_GETMINMAXINFO: + GetWindowRect(hwndList, &rc); + GetWindowRect(hwndDlg, &rc2); + { + MINMAXINFO *mmi = (MINMAXINFO*)lParam; + mmi->ptMinTrackSize.x = rc.left - rc2.left + 10 + GetSystemMetrics(SM_CXFRAME); + GetClientRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); + mmi->ptMinTrackSize.x += rc.right + 5; + GetClientRect(GetDlgItem(hwndDlg, IDC_ADD), &rc); + mmi->ptMinTrackSize.x += rc.right + 5; + GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); + mmi->ptMinTrackSize.y = dat->minDlgHeight + 20 + GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFRAME); + GetClientRect(GetDlgItem(hwndDlg, IDC_STATUSBAR), &rc); + mmi->ptMinTrackSize.y += rc.bottom; + } + return 0; + + case M_SETGROUPVISIBILITIES: + dat->showAdvanced = dat->showEmail = dat->showName = dat->showProtoId = dat->showTiny = 0; + { + char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); + if (szProto == (char *)CB_ERR) + break; + if (szProto == nullptr) { + for (auto &pa : g_arAccounts) { + if (pa->IsEnabled()) { + uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); + if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; + if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; + } + } + } + else { + uint32_t protoCaps = (uint32_t)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAGNUM_1, 0); + if (protoCaps & PF1_BASICSEARCH) dat->showProtoId = 1; + if (protoCaps & PF1_SEARCHBYEMAIL) dat->showEmail = 1; + if (protoCaps & PF1_SEARCHBYNAME) dat->showName = 1; + + if (protoCaps & PF1_EXTSEARCHUI) dat->showAdvanced = 1; + else if (protoCaps & PF1_EXTSEARCH) dat->showTiny = 1; + + if (protoCaps & PF1_USERIDISEMAIL && dat->showProtoId) { dat->showProtoId = 0; dat->showEmail = 1; } + if (dat->showProtoId) { + wchar_t *wszUniqueId = (wchar_t *)CallProtoServiceInt(0, szProto, PS_GETCAPS, PFLAG_UNIQUEIDTEXT, 0); + if (wszUniqueId) + SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, wszUniqueId); + else + SetDlgItemTextW(hwndDlg, IDC_BYPROTOID, TranslateT("Handle")); + + if (protoCaps & PF1_NUMERICUSERID) + SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE) | ES_NUMBER); + else + SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_PROTOID), GWL_STYLE)&~ES_NUMBER); + } + } + + if (dat->showTiny) + ShowTinySearchDlg(hwndDlg, dat); + else if (dat->hwndTinySearch) { + DestroyWindow(dat->hwndTinySearch); + dat->hwndTinySearch = nullptr; + } + +#define en(id, t) ShowWindow( GetDlgItem(hwndDlg, IDC_##id), dat->show##t?SW_SHOW:SW_HIDE) + en(PROTOIDGROUP, ProtoId); en(BYPROTOID, ProtoId); en(PROTOID, ProtoId); + en(EMAILGROUP, Email); en(BYEMAIL, Email); en(EMAIL, Email); + en(NAMEGROUP, Name); en(BYNAME, Name); + en(STNAMENICK, Name); en(NAMENICK, Name); + en(STNAMEFIRST, Name); en(NAMEFIRST, Name); + en(STNAMELAST, Name); en(NAMELAST, Name); + en(ADVANCEDGROUP, Advanced); en(BYADVANCED, Advanced); en(ADVANCED, Advanced); + en(BYCUSTOM, Tiny); en(TINYEXTENDEDGROUP, Tiny); +#undef en + int checkmarkVisible = (dat->showAdvanced && IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) || + (dat->showEmail && IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) || + (dat->showTiny && IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) || + (dat->showName && IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) || + (dat->showProtoId && IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)); + if (!checkmarkVisible) { + if (dat->showProtoId) CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); + else if (dat->showEmail) CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); + else if (dat->showName) CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); + else if (dat->showAdvanced) CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); + else if (dat->showTiny) CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); + } + + SendMessage(hwndDlg, WM_SIZE, 0, 0); + + MINMAXINFO mmi; + SendMessage(hwndDlg, WM_GETMINMAXINFO, 0, (LPARAM)&mmi); + + GetWindowRect(hwndDlg, &rc); + if (rc.bottom - rc.top < mmi.ptMinTrackSize.y) + SetWindowPos(hwndDlg, nullptr, 0, 0, rc.right - rc.left, mmi.ptMinTrackSize.y, SWP_NOZORDER | SWP_NOMOVE); + } + break; + + case WM_TIMER: + if (wParam == TIMERID_THROBBER) { + int borders[3]; + SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETBORDERS, 0, (LPARAM)borders); + + SendDlgItemMessage(hwndDlg, IDC_STATUSBAR, SB_GETRECT, 1, (LPARAM)&rc); + InflateRect(&rc, -borders[2] / 2, -borders[1] / 2); + HDC hdc = GetDC(GetDlgItem(hwndDlg, IDC_STATUSBAR)); + RenderThrobber(hdc, &rc, &dat->throbbing, &dat->pivot); + ReleaseDC(GetDlgItem(hwndDlg, IDC_STATUSBAR), hdc); + } + break; + + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam; + if (dis->CtlID == IDC_STATUSBAR && dis->itemID == 1) { + RenderThrobber(dis->hDC, &dis->rcItem, &dat->throbbing, &dat->pivot); + return TRUE; + } + } + break; + + case WM_NOTIFY: + if (wParam == IDC_RESULTS) { + switch (((LPNMHDR)lParam)->code) { + case LVN_ITEMCHANGED: + { + int count = ListView_GetSelectedCount(hwndList); + if (dat->notSearchedYet) + count = 0; + EnableResultButtons(hwndDlg, count); + } + break; + + case LVN_COLUMNCLICK: + HDITEM hdi; + hdi.mask = HDI_FORMAT; + hdi.fmt = HDF_LEFT | HDF_STRING; + Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); + + LPNMLISTVIEW nmlv = (LPNMLISTVIEW)lParam; + if (nmlv->iSubItem != dat->iLastColumnSortIndex) { + dat->bSortAscending = TRUE; + dat->iLastColumnSortIndex = nmlv->iSubItem; + } + else dat->bSortAscending = !dat->bSortAscending; + + hdi.fmt = HDF_LEFT | HDF_STRING | (dat->bSortAscending ? HDF_SORTDOWN : HDF_SORTUP); + Header_SetItem(ListView_GetHeader(hwndList), dat->iLastColumnSortIndex, &hdi); + + ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); + } + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_PROTOLIST: + if (HIWORD(wParam) == CBN_SELCHANGE) { + HideAdvancedSearchDlg(hwndDlg, dat); + if (dat->hwndAdvSearch) { + DestroyWindow(dat->hwndAdvSearch); + dat->hwndAdvSearch = nullptr; + } + if (dat->hwndTinySearch) { + DestroyWindow(dat->hwndTinySearch); + dat->hwndTinySearch = nullptr; + } + SendMessage(hwndDlg, M_SETGROUPVISIBILITIES, 0, 0); + } + break; + + case IDC_BYPROTOID: + case IDC_BYEMAIL: + case IDC_BYNAME: + { + int count = ListView_GetSelectedCount(hwndList); + if (dat->notSearchedYet) + count = 0; + EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), count); + HideAdvancedSearchDlg(hwndDlg, dat); + } + break; + + case IDC_PROTOID: + if (HIWORD(wParam) == EN_CHANGE) { + HideAdvancedSearchDlg(hwndDlg, dat); + CheckSearchTypeRadioButton(hwndDlg, IDC_BYPROTOID); + } + break; + + case IDC_EMAIL: + if (HIWORD(wParam) == EN_CHANGE) { + HideAdvancedSearchDlg(hwndDlg, dat); + CheckSearchTypeRadioButton(hwndDlg, IDC_BYEMAIL); + } + break; + + case IDC_NAMENICK: + case IDC_NAMEFIRST: + case IDC_NAMELAST: + if (HIWORD(wParam) == EN_CHANGE) { + HideAdvancedSearchDlg(hwndDlg, dat); + CheckSearchTypeRadioButton(hwndDlg, IDC_BYNAME); + } + break; + + case IDC_ADVANCED: + EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), ListView_GetSelectedCount(hwndList) > 0); + if (IsDlgButtonChecked(hwndDlg, IDC_ADVANCED)) + ShowAdvancedSearchDlg(hwndDlg, dat); + else + HideAdvancedSearchDlg(hwndDlg, dat); + CheckSearchTypeRadioButton(hwndDlg, IDC_BYADVANCED); + break; + + case IDCANCEL: + DestroyWindow(hwndDlg); + break; + + case IDOK: + HideAdvancedSearchDlg(hwndDlg, dat); + if (dat->searchCount) { //cancel search + SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); + if (dat->hResultHook) { UnhookEvent(dat->hResultHook); dat->hResultHook = nullptr; } + if (dat->search) { mir_free(dat->search); dat->search = nullptr; } + dat->searchCount = 0; + StopThrobber(hwndDlg, dat); + SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); + } + else { + char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); + if (dat->search) + mir_free(dat->search), dat->search = nullptr; + dat->searchCount = 0; + dat->hResultHook = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_SEARCHACK); + if (IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) + BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndTinySearch); + else if (IsDlgButtonChecked(hwndDlg, IDC_BYPROTOID)) { + wchar_t str[256]; + GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); + rtrimw(str); + if (str[0] == 0) + MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); + else + BeginSearch(hwndDlg, dat, szProto, PS_BASICSEARCH, PF1_BASICSEARCH, str); + } + else if (IsDlgButtonChecked(hwndDlg, IDC_BYEMAIL)) { + wchar_t str[256]; + GetDlgItemText(hwndDlg, IDC_EMAIL, str, _countof(str)); + rtrimw(str); + if (str[0] == 0) + MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); + else + BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYEMAIL, PF1_SEARCHBYEMAIL, str); + } + else if (IsDlgButtonChecked(hwndDlg, IDC_BYNAME)) { + wchar_t nick[256], first[256], last[256]; + PROTOSEARCHBYNAME psbn; + GetDlgItemText(hwndDlg, IDC_NAMENICK, nick, _countof(nick)); + GetDlgItemText(hwndDlg, IDC_NAMEFIRST, first, _countof(first)); + GetDlgItemText(hwndDlg, IDC_NAMELAST, last, _countof(last)); + psbn.pszFirstName = first; + psbn.pszLastName = last; + psbn.pszNick = nick; + if (nick[0] == 0 && first[0] == 0 && last[0] == 0) + MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); + else + BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYNAME, PF1_SEARCHBYNAME, &psbn); + } + else if (IsDlgButtonChecked(hwndDlg, IDC_BYADVANCED)) { + if (dat->hwndAdvSearch == nullptr) + MessageBoxW(hwndDlg, sttErrMsg, sttErrTitle, MB_ICONERROR | MB_OK); + else + BeginSearch(hwndDlg, dat, szProto, PS_SEARCHBYADVANCED, PF1_EXTSEARCHUI, dat->hwndAdvSearch); + } + + if (dat->searchCount == 0) { + if (dat->hResultHook) { + UnhookEvent(dat->hResultHook); + dat->hResultHook = nullptr; + } + break; + } + + dat->notSearchedYet = 0; + FreeSearchResults(hwndList); + + CreateResultsColumns(hwndList, dat, szProto); + SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); + SetStatusBarResultInfo(hwndDlg); + StartThrobber(hwndDlg, dat); + SetDlgItemText(hwndDlg, IDOK, TranslateT("Cancel")); + } + break; + + case IDC_ADD: + if (ListView_GetSelectedCount(hwndList) == 1) { + LVITEM lvi; + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED); + ListView_GetItem(hwndList, &lvi); + ListSearchResult *lsr = (ListSearchResult*)lvi.lParam; + Contact::AddBySearch(lsr->szProto, &lsr->psr, hwndDlg); + } + else { + wchar_t str[256]; + GetDlgItemText(hwndDlg, IDC_PROTOID, str, _countof(str)); + if (*rtrimw(str) == 0) + break; + + char *szProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); + + PROTOSEARCHRESULT psr = { 0 }; + psr.cbSize = sizeof(psr); + psr.flags = PSR_UNICODE; + psr.id.w = str; + Contact::AddBySearch(szProto, &psr, hwndDlg); + } + break; + + case IDC_MOREOPTIONS: + GetWindowRect(GetDlgItem(hwndDlg, IDC_MOREOPTIONS), &rc); + ShowMoreOptionsMenu(hwndDlg, rc.left, rc.bottom); + break; + } + + if (lParam && dat->hwndTinySearch == (HWND)lParam && + HIWORD(wParam) == EN_SETFOCUS && LOWORD(wParam) == 0 && + BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_BYCUSTOM)) { + CheckSearchTypeRadioButton(hwndDlg, IDC_BYCUSTOM); + } + break; + + case WM_CONTEXTMENU: + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + + LVHITTESTINFO lvhti; + lvhti.pt = pt; + ScreenToClient(hwndDlg, &pt); + switch (GetDlgCtrlID(ChildWindowFromPoint(hwndDlg, pt))) { + case IDC_RESULTS: + if (dat->notSearchedYet) + return TRUE; + ScreenToClient(hwndList, &lvhti.pt); + if (ListView_HitTest(hwndList, &lvhti) == -1) + break; + ShowMoreOptionsMenu(hwndDlg, (short)LOWORD(lParam), (short)HIWORD(lParam)); + return TRUE; + } + } + break; + + case HM_SEARCHACK: + { + ACKDATA *ack = (ACKDATA*)lParam; + if (ack->type != ACKTYPE_SEARCH) + break; + + int i; + for (i = 0; i < dat->searchCount; i++) + if (dat->search[i].hProcess == ack->hProcess && dat->search[i].hProcess != nullptr && !mir_strcmp(dat->search[i].szProto, ack->szModule)) break; + if (i == dat->searchCount) + break; + + if (ack->result == ACKRESULT_SUCCESS || ack->result == ACKRESULT_FAILED) { + dat->searchCount--; + memmove(dat->search + i, dat->search + i + 1, sizeof(struct ProtoSearchInfo)*(dat->searchCount - i)); + if (dat->searchCount == 0) { + mir_free(dat->search); + dat->search = nullptr; + UnhookEvent(dat->hResultHook); + dat->hResultHook = nullptr; + SetDlgItemText(hwndDlg, IDOK, TranslateT("&Search")); + StopThrobber(hwndDlg, dat); + } + ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); + SetStatusBarSearchInfo(GetDlgItem(hwndDlg, IDC_STATUSBAR), dat); + } + else if (ack->result == ACKRESULT_SEARCHRESULT && ack->lParam) { + CUSTOMSEARCHRESULTS *csr = (CUSTOMSEARCHRESULTS*)ack->lParam; + dat->bFlexSearchResult = TRUE; + PROTOSEARCHRESULT *psr = &csr->psr; + // check if this is column names data (psr->cbSize == 0) + if (psr->cbSize == 0) { // blob contain info about columns + // first remove all exist items + FreeSearchResults(hwndList); + ListView_DeleteAllItems(hwndList); //not sure if previous delete list items too + + //second remove all columns + while (ListView_DeleteColumn(hwndList, 1)); //will delete fist column till it possible + + // now will add columns and captions; + LVCOLUMN lvc = { 0 }; + lvc.mask = LVCF_TEXT; + for (int iColumn = 0; iColumn < csr->nFieldCount; iColumn++) { + lvc.pszText = TranslateW(csr->pszFields[iColumn]); + ListView_InsertColumn(hwndList, iColumn + 1, &lvc); + } + } + else { // blob contain info about found contacts + ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); + lsr->szProto = ack->szModule; + memcpy(&lsr->psr, psr, psr->cbSize); + + /* Next block is not needed but behavior will be kept */ + lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); + lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); + lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); + lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); + lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); + lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; + + LVITEM lvi = { 0 }; + lvi.mask = LVIF_PARAM | LVIF_IMAGE; + lvi.lParam = (LPARAM)lsr; + for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { + char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); + if (szComboProto == nullptr) continue; + if (!mir_strcmp(szComboProto, ack->szModule)) { + COMBOBOXEXITEM cbei = { 0 }; + cbei.mask = CBEIF_IMAGE; + cbei.iItem = i; + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); + lvi.iImage = cbei.iImage; + } + } + int iItem = ListView_InsertItem(hwndList, &lvi); + for (int col = 0; col < csr->nFieldCount; col++) + SetListItemText(hwndList, iItem, col + 1, csr->pszFields[col]); + + ListView_SortItemsEx(hwndList, SearchResultsCompareFunc, (LPARAM)hwndDlg); + iItem = 0; + while (ListView_SetColumnWidth(hwndList, iItem++, LVSCW_AUTOSIZE_USEHEADER)); + SetStatusBarResultInfo(hwndDlg); + } + break; + } + else if (ack->result == ACKRESULT_DATA) { + PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)ack->lParam; + ListSearchResult *lsr = (ListSearchResult*)mir_alloc(offsetof(ListSearchResult, psr) + psr->cbSize); + lsr->szProto = ack->szModule; + + dat->bFlexSearchResult = FALSE; + + memcpy(&lsr->psr, psr, psr->cbSize); + lsr->psr.nick.w = sttDecodeString(psr->flags, psr->nick); + lsr->psr.firstName.w = sttDecodeString(psr->flags, psr->firstName); + lsr->psr.lastName.w = sttDecodeString(psr->flags, psr->lastName); + lsr->psr.email.w = sttDecodeString(psr->flags, psr->email); + lsr->psr.id.w = sttDecodeString(psr->flags, psr->id); + lsr->psr.flags = psr->flags & ~PSR_UNICODE | PSR_UNICODE; + + LVITEM lvi = { 0 }; + lvi.mask = LVIF_PARAM | LVIF_IMAGE; + lvi.lParam = (LPARAM)lsr; + for (i = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCOUNT, 0, 0); i--;) { + char *szComboProto = (char*)SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETITEMDATA, i, 0); + if (szComboProto == nullptr) continue; + if (!mir_strcmp(szComboProto, ack->szModule)) { + COMBOBOXEXITEM cbei = { 0 }; + cbei.mask = CBEIF_IMAGE; + cbei.iItem = i; + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CBEM_GETITEM, 0, (LPARAM)&cbei); + lvi.iImage = cbei.iImage; + break; + } + } + + int iItem = ListView_InsertItem(hwndList, &lvi); + SetListItemText(hwndList, iItem, 1, lsr->psr.id.w); + SetListItemText(hwndList, iItem, 2, lsr->psr.nick.w); + SetListItemText(hwndList, iItem, 3, lsr->psr.firstName.w); + SetListItemText(hwndList, iItem, 4, lsr->psr.lastName.w); + SetListItemText(hwndList, iItem, 5, lsr->psr.email.w); + SetStatusBarResultInfo(hwndDlg); + } + } + break; + + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + + case WM_DESTROY: + int len = SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXTLEN, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), 0); + wchar_t *szProto = (wchar_t*)alloca(sizeof(wchar_t)*(len + 1)); + if (szProto != nullptr) { + *szProto = '\0'; + SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETLBTEXT, SendDlgItemMessage(hwndDlg, IDC_PROTOLIST, CB_GETCURSEL, 0, 0), (LPARAM)szProto); + db_set_ws(0, "FindAdd", "LastSearched", szProto); + } + + SaveColumnSizes(hwndList); + if (dat->hResultHook != nullptr) + UnhookEvent(dat->hResultHook); + FreeSearchResults(hwndList); + ImageList_Destroy(dat->himlComboIcons); + mir_free(dat->search); + if (dat->hwndAdvSearch) { + DestroyWindow(dat->hwndAdvSearch); + dat->hwndAdvSearch = nullptr; + } + if (dat->hwndTinySearch) { + DestroyWindow(dat->hwndTinySearch); + dat->hwndTinySearch = nullptr; + } + mir_free(dat); + Window_FreeIcon_IcoLib(hwndDlg); + Utils_SaveWindowPosition(hwndDlg, 0, "FindAdd", ""); + break; + } + return FALSE; +} + +static INT_PTR FindAddCommand(WPARAM, LPARAM) +{ + if (IsWindow(hwndFindAdd)) { + ShowWindow(hwndFindAdd, SW_SHOWNORMAL); + SetForegroundWindow(hwndFindAdd); + SetFocus(hwndFindAdd); + } + else { + int netProtoCount = 0; + + // Make sure we have some networks to search on. This is not ideal since + // this check will be repeated every time the dialog is requested, but it + // must be done since this service can be called from other places than the menu. + // One alternative would be to only create the service if we have network + // protocols loaded but that would delay the creation until MODULE_LOADED and + // that is not good either... + for (auto &pa : g_arAccounts) { + if (!pa->IsEnabled()) + continue; + + int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); + if (protoCaps & PF1_ANYSEARCH) + netProtoCount++; + } + if (netProtoCount > 0) + hwndFindAdd = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FINDADD), nullptr, DlgProcFindAdd); + } + return 0; +} + +int FindAddPreShutdown(WPARAM, LPARAM) +{ + if (IsWindow(hwndFindAdd)) + DestroyWindow(hwndFindAdd); + hwndFindAdd = nullptr; + return 0; +} + +int LoadFindAddModule(void) +{ + CreateServiceFunction(MS_FINDADD_FINDADD, FindAddCommand); + + HookEvent(ME_SYSTEM_MODULESLOADED, OnSystemModulesLoaded); + HookEvent(ME_PROTO_ACCLISTCHANGED, OnSystemModulesLoaded); + HookEvent(ME_SYSTEM_PRESHUTDOWN, FindAddPreShutdown); + + CMenuItem mi(&g_plugin); + SET_UID(mi, 0x860556b9, 0x1577, 0x4f6f, 0x8c, 0xb0, 0x93, 0x24, 0xa8, 0x2e, 0x20, 0x92); + mi.position = 500020000; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FINDUSER); + mi.name.a = LPGEN("&Find/add contacts..."); + mi.pszService = MS_FINDADD_FINDADD; + hMainMenuItem = Menu_AddMainMenuItem(&mi); + return 0; +} + +static int OnSystemModulesLoaded(WPARAM, LPARAM) +{ + int netProtoCount = 0; + + // Make sure we have some networks to search on. + for (auto &pa : g_arAccounts) { + int protoCaps = CallProtoServiceInt(0, pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); + if (protoCaps & PF1_ANYSEARCH) + netProtoCount++; + } + + Menu_ShowItem(hMainMenuItem, netProtoCount != 0); + return 0; +} diff --git a/src/mir_app/src/findadd.h b/src/mir_app/src/findadd.h index 81a546fa1a..e0993d696b 100644 --- a/src/mir_app/src/findadd.h +++ b/src/mir_app/src/findadd.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/genmenu.h b/src/mir_app/src/genmenu.h index 693a5bf9a7..8ed7a83359 100644 --- a/src/mir_app/src/genmenu.h +++ b/src/mir_app/src/genmenu.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/headerbar.cpp b/src/mir_app/src/headerbar.cpp index 319334f7a9..f312b22145 100644 --- a/src/mir_app/src/headerbar.cpp +++ b/src/mir_app/src/headerbar.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, Copyright (c) 2007 Artem Shpynov all portions of this codebase are copyrighted to the people diff --git a/src/mir_app/src/help.cpp b/src/mir_app/src/help.cpp index 30bd95bcbf..dca9b784b6 100644 --- a/src/mir_app/src/help.cpp +++ b/src/mir_app/src/help.cpp @@ -1,189 +1,189 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "resource.h" - -static class CAboutDlg *pAboutDialog; - -class CAboutDlg : public CDlgBase -{ - int m_iState = 0; - - CCtrlBase ctrlHeaderBar, ctrlDevelopers, ctrlCredits, ctrlWhiteRect; - CCtrlButton btnLink; - -public: - CAboutDlg() : - CDlgBase(g_plugin, IDD_ABOUT), - btnLink(this, IDC_CONTRIBLINK), - ctrlCredits(this, IDC_CREDITSFILE), - ctrlHeaderBar(this, IDC_HEADERBAR), - ctrlWhiteRect(this, IDC_WHITERECT), - ctrlDevelopers(this, IDC_DEVS) - { - btnLink.OnClick = Callback(this, &CAboutDlg::onClick); - - ctrlCredits.UseSystemColors(); - ctrlWhiteRect.UseSystemColors(); - ctrlDevelopers.UseSystemColors(); - } - - bool OnInitDialog() override - { - ptrW wszCopyright(mir_utf8decodeW(LEGAL_COPYRIGHT)); - if (wszCopyright == nullptr) - wszCopyright = mir_a2u(LEGAL_COPYRIGHT); - ctrlDevelopers.SetText(wszCopyright); - - char productVersion[56]; - Miranda_GetVersionText(productVersion, _countof(productVersion)); - ctrlHeaderBar.SetText(CMStringW(FORMAT, L"Miranda NG\nv%S", productVersion)); - - HRSRC hResInfo = FindResource(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CREDITS), L"TEXT"); - uint32_t ResSize = SizeofResource(g_plugin.getInst(), hResInfo); - HGLOBAL hRes = LoadResource(g_plugin.getInst(), hResInfo); - char *pszMsg = (char*)LockResource(hRes); - if (pszMsg) { - char *pszMsgt = (char*)alloca(ResSize + 1); - memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0; - - ptrW ptszMsg; - if (ResSize >= 3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf') - ptszMsg = mir_utf8decodeW(pszMsgt + 3); - else - ptszMsg = mir_a2u_cp(pszMsgt, 1252); - ctrlCredits.SetText(ptszMsg); - - UnlockResource(pszMsg); - } - FreeResource(hRes); - ctrlCredits.Hide(); - - Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_MIRANDA); - return true; - } - - void OnDestroy() override - { - pAboutDialog = nullptr; - Window_FreeIcon_IcoLib(m_hwnd); - } - - void onClick(CCtrlButton*) - { - if (m_iState) { - btnLink.SetText(TranslateT("Credits >")); - ctrlDevelopers.Show(); - ctrlCredits.Hide(); - } - else { - btnLink.SetText(TranslateT("< Copyright")); - ctrlDevelopers.Hide(); - ctrlCredits.Show(); - } - m_iState = !m_iState; - } -}; - -static INT_PTR AboutCommand(WPARAM wParam, LPARAM) -{ - if (pAboutDialog) { - SetForegroundWindow(pAboutDialog->GetHwnd()); - SetFocus(pAboutDialog->GetHwnd()); - } - else { - pAboutDialog = new CAboutDlg(); - pAboutDialog->SetParent((HWND)wParam); - pAboutDialog->Show(); - } - return 0; -} - -static INT_PTR IndexCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://wiki.miranda-ng.org"); - return 0; -} - -static INT_PTR WebsiteCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://miranda-ng.org"); - return 0; -} - -static INT_PTR BugCommand(WPARAM, LPARAM) -{ - Utils_OpenUrl("https://github.com/miranda-ng/miranda-ng/issues/new"); - return 0; -} - -int ShutdownHelpModule(WPARAM, LPARAM) -{ - if (pAboutDialog) - pAboutDialog->Close(); - return 0; -} - -int LoadHelpModule(void) -{ - HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownHelpModule); - - CMenuItem mi(&g_plugin); - mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("&Help"), 2000090000); - Menu_ConfigureItem(mi.root, MCI_OPT_UID, "8824ECA5-6942-46D7-9D07-1BA600E0D02E"); - - SET_UID(mi, 0xf3ebf1fa, 0x587c, 0x494d, 0xbd, 0x33, 0x7f, 0x88, 0xb3, 0x61, 0x1e, 0xd3); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA); - mi.position = 2000090000; - mi.name.a = LPGEN("&About..."); - mi.pszService = "Help/AboutCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, AboutCommand); - - SET_UID(mi, 0x495df66f, 0x844e, 0x479a, 0xaf, 0x21, 0x3e, 0x42, 0xc5, 0x14, 0x7c, 0x7e); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP); - mi.position = -500050000; - mi.name.a = LPGEN("&Support"); - mi.pszService = "Help/IndexCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, IndexCommand); - - SET_UID(mi, 0x15e18b58, 0xec73, 0x45c2, 0xb9, 0xf4, 0x2a, 0xfe, 0xc2, 0xb7, 0xd3, 0x25); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB); - mi.position = 2000050000; - mi.name.a = LPGEN("&Miranda NG homepage"); - mi.pszService = "Help/WebsiteCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, WebsiteCommand); - - SET_UID(mi, 0xe7d0fe8b, 0xfdeb, 0x45b3, 0xba, 0x83, 0x3, 0x1e, 0x15, 0xda, 0x7e, 0x52); - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_URL); - mi.position = 2000040000; - mi.name.a = LPGEN("&Report bug"); - mi.pszService = "Help/BugCommand"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, BugCommand); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "resource.h" + +static class CAboutDlg *pAboutDialog; + +class CAboutDlg : public CDlgBase +{ + int m_iState = 0; + + CCtrlBase ctrlHeaderBar, ctrlDevelopers, ctrlCredits, ctrlWhiteRect; + CCtrlButton btnLink; + +public: + CAboutDlg() : + CDlgBase(g_plugin, IDD_ABOUT), + btnLink(this, IDC_CONTRIBLINK), + ctrlCredits(this, IDC_CREDITSFILE), + ctrlHeaderBar(this, IDC_HEADERBAR), + ctrlWhiteRect(this, IDC_WHITERECT), + ctrlDevelopers(this, IDC_DEVS) + { + btnLink.OnClick = Callback(this, &CAboutDlg::onClick); + + ctrlCredits.UseSystemColors(); + ctrlWhiteRect.UseSystemColors(); + ctrlDevelopers.UseSystemColors(); + } + + bool OnInitDialog() override + { + ptrW wszCopyright(mir_utf8decodeW(LEGAL_COPYRIGHT)); + if (wszCopyright == nullptr) + wszCopyright = mir_a2u(LEGAL_COPYRIGHT); + ctrlDevelopers.SetText(wszCopyright); + + char productVersion[56]; + Miranda_GetVersionText(productVersion, _countof(productVersion)); + ctrlHeaderBar.SetText(CMStringW(FORMAT, L"Miranda NG\nv%S", productVersion)); + + HRSRC hResInfo = FindResource(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CREDITS), L"TEXT"); + uint32_t ResSize = SizeofResource(g_plugin.getInst(), hResInfo); + HGLOBAL hRes = LoadResource(g_plugin.getInst(), hResInfo); + char *pszMsg = (char*)LockResource(hRes); + if (pszMsg) { + char *pszMsgt = (char*)alloca(ResSize + 1); + memcpy(pszMsgt, pszMsg, ResSize); pszMsgt[ResSize] = 0; + + ptrW ptszMsg; + if (ResSize >= 3 && pszMsgt[0] == '\xef' && pszMsgt[1] == '\xbb' && pszMsgt[2] == '\xbf') + ptszMsg = mir_utf8decodeW(pszMsgt + 3); + else + ptszMsg = mir_a2u_cp(pszMsgt, 1252); + ctrlCredits.SetText(ptszMsg); + + UnlockResource(pszMsg); + } + FreeResource(hRes); + ctrlCredits.Hide(); + + Window_SetSkinIcon_IcoLib(m_hwnd, SKINICON_OTHER_MIRANDA); + return true; + } + + void OnDestroy() override + { + pAboutDialog = nullptr; + Window_FreeIcon_IcoLib(m_hwnd); + } + + void onClick(CCtrlButton*) + { + if (m_iState) { + btnLink.SetText(TranslateT("Credits >")); + ctrlDevelopers.Show(); + ctrlCredits.Hide(); + } + else { + btnLink.SetText(TranslateT("< Copyright")); + ctrlDevelopers.Hide(); + ctrlCredits.Show(); + } + m_iState = !m_iState; + } +}; + +static INT_PTR AboutCommand(WPARAM wParam, LPARAM) +{ + if (pAboutDialog) { + SetForegroundWindow(pAboutDialog->GetHwnd()); + SetFocus(pAboutDialog->GetHwnd()); + } + else { + pAboutDialog = new CAboutDlg(); + pAboutDialog->SetParent((HWND)wParam); + pAboutDialog->Show(); + } + return 0; +} + +static INT_PTR IndexCommand(WPARAM, LPARAM) +{ + Utils_OpenUrl("https://wiki.miranda-ng.org"); + return 0; +} + +static INT_PTR WebsiteCommand(WPARAM, LPARAM) +{ + Utils_OpenUrl("https://miranda-ng.org"); + return 0; +} + +static INT_PTR BugCommand(WPARAM, LPARAM) +{ + Utils_OpenUrl("https://github.com/miranda-ng/miranda-ng/issues/new"); + return 0; +} + +int ShutdownHelpModule(WPARAM, LPARAM) +{ + if (pAboutDialog) + pAboutDialog->Close(); + return 0; +} + +int LoadHelpModule(void) +{ + HookEvent(ME_SYSTEM_PRESHUTDOWN, ShutdownHelpModule); + + CMenuItem mi(&g_plugin); + mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("&Help"), 2000090000); + Menu_ConfigureItem(mi.root, MCI_OPT_UID, "8824ECA5-6942-46D7-9D07-1BA600E0D02E"); + + SET_UID(mi, 0xf3ebf1fa, 0x587c, 0x494d, 0xbd, 0x33, 0x7f, 0x88, 0xb3, 0x61, 0x1e, 0xd3); + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA); + mi.position = 2000090000; + mi.name.a = LPGEN("&About..."); + mi.pszService = "Help/AboutCommand"; + Menu_AddMainMenuItem(&mi); + CreateServiceFunction(mi.pszService, AboutCommand); + + SET_UID(mi, 0x495df66f, 0x844e, 0x479a, 0xaf, 0x21, 0x3e, 0x42, 0xc5, 0x14, 0x7c, 0x7e); + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP); + mi.position = -500050000; + mi.name.a = LPGEN("&Support"); + mi.pszService = "Help/IndexCommand"; + Menu_AddMainMenuItem(&mi); + CreateServiceFunction(mi.pszService, IndexCommand); + + SET_UID(mi, 0x15e18b58, 0xec73, 0x45c2, 0xb9, 0xf4, 0x2a, 0xfe, 0xc2, 0xb7, 0xd3, 0x25); + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDAWEB); + mi.position = 2000050000; + mi.name.a = LPGEN("&Miranda NG homepage"); + mi.pszService = "Help/WebsiteCommand"; + Menu_AddMainMenuItem(&mi); + CreateServiceFunction(mi.pszService, WebsiteCommand); + + SET_UID(mi, 0xe7d0fe8b, 0xfdeb, 0x45b3, 0xba, 0x83, 0x3, 0x1e, 0x15, 0xda, 0x7e, 0x52); + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_EVENT_URL); + mi.position = 2000040000; + mi.name.a = LPGEN("&Report bug"); + mi.pszService = "Help/BugCommand"; + Menu_AddMainMenuItem(&mi); + CreateServiceFunction(mi.pszService, BugCommand); + return 0; +} diff --git a/src/mir_app/src/hotkey_opts.cpp b/src/mir_app/src/hotkey_opts.cpp index 7221e4df60..04d78d2bf8 100644 --- a/src/mir_app/src/hotkey_opts.cpp +++ b/src/mir_app/src/hotkey_opts.cpp @@ -1,1026 +1,1026 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ -#include "stdafx.h" - -#include -#include "skin.h" - -static wchar_t *sttHokeyVkToName(uint16_t vkKey) -{ - static wchar_t buf[256] = {}; - uint32_t code = MapVirtualKey(vkKey, 0) << 16; - - switch (vkKey) { - case 0: - case VK_CONTROL: - case VK_SHIFT: - case VK_MENU: - case VK_LWIN: - case VK_RWIN: - case VK_PAUSE: - case VK_CANCEL: - case VK_NUMLOCK: - case VK_CAPITAL: - case VK_SCROLL: - return L""; - case VK_BROWSER_BACK: - return TranslateT("Browser: Back"); - case VK_BROWSER_FORWARD: - return TranslateT("Browser: Forward"); - case VK_BROWSER_REFRESH: - return TranslateT("Browser: Refresh"); - case VK_BROWSER_STOP: - return TranslateT("Browser: Stop"); - case VK_BROWSER_SEARCH: - return TranslateT("Browser: Search"); - case VK_BROWSER_FAVORITES: - return TranslateT("Browser: Fav"); - case VK_BROWSER_HOME: - return TranslateT("Browser: Home"); - case VK_VOLUME_MUTE: - return TranslateT("Mute"); - case VK_VOLUME_DOWN: - return TranslateT("Vol-"); - case VK_VOLUME_UP: - return TranslateT("Vol+"); - case VK_MEDIA_NEXT_TRACK: - return TranslateT("Media: Next Track"); - case VK_MEDIA_PREV_TRACK: - return TranslateT("Media: Prev. Track"); - case VK_MEDIA_STOP: - return TranslateT("Media: Stop"); - case VK_MEDIA_PLAY_PAUSE: - return TranslateT("Media: Play/Pause"); - case VK_LAUNCH_MAIL: - return TranslateT("Mail"); - case VK_LAUNCH_MEDIA_SELECT: - return TranslateT("Media: Select"); - case VK_LAUNCH_APP1: - return TranslateT("App 1"); - case VK_LAUNCH_APP2: - return TranslateT("App 2"); - - case VK_DIVIDE: - case VK_INSERT: - case VK_HOME: - case VK_PRIOR: - case VK_DELETE: - case VK_END: - case VK_NEXT: - case VK_LEFT: - case VK_RIGHT: - case VK_UP: - case VK_DOWN: - code |= (1UL << 24); - } - - GetKeyNameText(code, buf, 256); - return buf; -} - -void HotkeyToName(wchar_t *buf, int size, uint8_t shift, uint8_t key) -{ - mir_snwprintf(buf, size, L"%s%s%s%s%s", - (shift & HOTKEYF_CONTROL) ? TranslateT("Ctrl + ") : L"", - (shift & HOTKEYF_ALT) ? TranslateT("Alt + ") : L"", - (shift & HOTKEYF_SHIFT) ? TranslateT("Shift + ") : L"", - (shift & HOTKEYF_EXT) ? TranslateT("Win + ") : L"", - sttHokeyVkToName(key)); -} - -/////////////////////////////////////////////////////////////////////////////// -// Hotkey control - -static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (!data) - return 0; - - bool bKeyDown = false; - - switch (msg) { - case HKM_GETHOTKEY: - return data->key ? MAKEWORD(data->key, data->shift) : 0; - - case HKM_SETHOTKEY: - { - wchar_t buf[256] = {}; - data->key = (uint8_t)LOWORD(wParam); - data->shift = (uint8_t)HIWORD(wParam); - HotkeyToName(buf, _countof(buf), data->shift, data->key); - SetWindowText(hwnd, buf); - } - return 0; - - case WM_GETDLGCODE: - return DLGC_WANTALLKEYS; - - case WM_KILLFOCUS: - break; - - case WM_CHAR: - case WM_SYSCHAR: - case WM_PASTE: - case WM_CONTEXTMENU: - return TRUE; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - bKeyDown = true; - - case WM_KEYUP: - case WM_SYSKEYUP: - { - wchar_t buf[256] = {}; - - uint8_t shift = 0; - uint8_t key = wParam; - wchar_t *name = sttHokeyVkToName(key); - if (!*name || !bKeyDown) - key = 0; - - if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL; - if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT; - if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT; - if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT; - - if (bKeyDown || !data->key) { - data->shift = shift; - data->key = key; - } - - HotkeyToName(buf, _countof(buf), data->shift, data->key); - SetWindowText(hwnd, buf); - - if (bKeyDown && data->key) - SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd); - } - return TRUE; - - case WM_DESTROY: - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - mir_free(data); - } - - return mir_callNextSubclass(hwnd, sttHotkeyEditProc, msg, wParam, lParam); -} - -MIR_APP_DLL(void) Hotkey_Subclass(HWND hwnd) -{ - THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData)); - SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data); - mir_subclassWindow(hwnd, sttHotkeyEditProc); -} - -MIR_APP_DLL(void) Hotkey_Unsubclass(HWND hwnd) -{ - THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); - mir_free(data); -} - -/////////////////////////////////////////////////////////////////////////////// -// Options - -enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE }; - -static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item) -{ - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.iItem = idx; - - if (!item->rootHotkey) { - lvi.mask = LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = COL_NAME; - lvi.pszText = item->getDescr(); - lvi.iImage = item->OptType; - ListView_SetItem(hwndList, &lvi); - - ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled); - } - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = COL_KEY; - HotkeyToName(buf, _countof(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey)); - lvi.pszText = buf; - ListView_SetItem(hwndList, &lvi); - - if (item->rootHotkey) { - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_TYPE; - lvi.iImage = item->OptType; - ListView_SetItem(hwndList, &lvi); - } - - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_RESET; - lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1; - ListView_SetItem(hwndList, &lvi); - - lvi.mask = LVIF_IMAGE | LVIF_TEXT; - lvi.iSubItem = COL_ADDREMOVE; - if (item->rootHotkey) { - lvi.iImage = 4; - lvi.pszText = TranslateT("Remove shortcut"); - } - else { - lvi.iImage = 3; - lvi.pszText = TranslateT("Add another shortcut"); - } - ListView_SetItem(hwndList, &lvi); -} - -static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item) -{ - item->OptDeleted = true; - ListView_DeleteItem(hwndList, idx); - if (item->rootHotkey) - item->rootHotkey->OptChanged = true; -} - -static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) -{ - wchar_t title1[256] = {}, title2[256] = {}; - THotkeyItem *item1 = nullptr, *item2 = nullptr; - LVITEM lvi = {}; - int res; - - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = lParam1; - lvi.pszText = title1; - lvi.cchTextMax = _countof(title1); - if (ListView_GetItem((HWND)lParamSort, &lvi)) - item1 = (THotkeyItem *)lvi.lParam; - - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = lParam2; - lvi.pszText = title2; - lvi.cchTextMax = _countof(title2); - if (ListView_GetItem((HWND)lParamSort, &lvi)) - item2 = (THotkeyItem *)lvi.lParam; - - if (!item1 && !item2) - return mir_wstrcmp(title1, title2); - - if (!item1 && item2) { - if (res = mir_wstrcmp(title1, item2->getSection())) - return res; - return -1; - } - - if (!item2 && item1) { - if (res = mir_wstrcmp(item1->getSection(), title2)) - return res; - return 1; - } - /* item1 != nullptr && item2 != nullptr */ - - if (res = mir_wstrcmp(item1->getSection(), item2->getSection())) return res; - if (res = mir_wstrcmp(item1->getDescr(), item2->getDescr())) return res; - if (!item1->rootHotkey && item2->rootHotkey) return -1; - if (item1->rootHotkey && !item2->rootHotkey) return 1; - return 0; -} - -static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item) -{ - char buf[256]; - mir_snprintf(buf, "mir_hotkey_%d_%d", g_pid, g_hkid++); - - THotkeyItem *newItem = (THotkeyItem *)mir_calloc(sizeof(THotkeyItem)); - newItem->pPlugin = item->pPlugin; - newItem->pszService = mir_strdup(item->pszService); - newItem->pwszSection = mir_wstrdup(item->pwszSection); - newItem->pwszDescription = mir_wstrdup(item->pwszDescription); - newItem->lParam = item->lParam; - newItem->idHotkey = GlobalAddAtomA(buf); - newItem->rootHotkey = item; - newItem->type = newItem->OptType = item->OptType; - newItem->Enabled = newItem->OptEnabled = newItem->OptNew = true; - hotkeys.insert(newItem); - - SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); - - LVITEM lvi = {}; - lvi.mask |= LVIF_PARAM; - lvi.lParam = (LPARAM)newItem; - sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem); - ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); - - SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); - RedrawWindow(hwndList, nullptr, nullptr, RDW_INVALIDATE); - - item->OptChanged = true; -} - -static void sttOptionsSetChanged(THotkeyItem *item) -{ - item->OptChanged = true; - if (item->rootHotkey) - item->rootHotkey->OptChanged = true; -} - -static void sttOptionsSaveItem(THotkeyItem *item) -{ - char buf[MAXMODULELABELLENGTH]; - - if (item->rootHotkey) return; - if (!item->OptChanged) return; - - item->Hotkey = item->OptHotkey; - item->type = item->OptType; - item->Enabled = item->OptEnabled; - - db_set_w(0, DBMODULENAME, item->pszName, item->Hotkey); - db_set_b(0, DBMODULENAME "Off", item->pszName, (uint8_t)!item->Enabled); - if (item->type != HKT_MANUAL) - db_set_b(0, DBMODULENAME "Types", item->pszName, (uint8_t)item->type); - - item->nSubHotkeys = 0; - for (auto &it : hotkeys) { - if (it->rootHotkey == item) { - it->Hotkey = it->OptHotkey; - it->type = it->OptType; - - mir_snprintf(buf, "%s$%d", item->pszName, item->nSubHotkeys); - db_set_w(0, DBMODULENAME, buf, it->Hotkey); - if (it->type != HKT_MANUAL) - db_set_b(0, DBMODULENAME "Types", buf, (uint8_t)it->type); - - ++item->nSubHotkeys; - } - } - - mir_snprintf(buf, "%s$count", item->pszName); - db_set_dw(0, DBMODULENAME, buf, item->nSubHotkeys); -} - -static void sttBuildHotkeyList(HWND hwndList) -{ - ListView_DeleteAllItems(hwndList); - - int nItems = 0; - THotkeyItem *prevItem = nullptr; - for (auto &item : hotkeys) { - LVITEM lvi = {}; - - if (!item->OptDeleted) { - if (!prevItem || mir_wstrcmp(item->pwszSection, prevItem->pwszSection)) { - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = nItems++; - lvi.iSubItem = 0; - lvi.lParam = 0; - lvi.pszText = item->getSection(); - ListView_InsertItem(hwndList, &lvi); - ListView_SetCheckState(hwndList, lvi.iItem, TRUE); - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - lvi.pszText = item->pwszSection; - ListView_SetItem(hwndList, &lvi); - - lvi.iSubItem = 0; - } - - lvi.mask = LVIF_PARAM | LVIF_INDENT; - lvi.iIndent = 1; - lvi.iItem = nItems++; - lvi.lParam = (LPARAM)item; - ListView_InsertItem(hwndList, &lvi); - sttOptionsSetupItem(hwndList, nItems - 1, item); - } - - prevItem = item; - } - - ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); -} - -static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey) -{ - LVITEM lvi; - THotkeyItem *item; - int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (iItem < 0) - return; - - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - ListView_GetItem(hwndHotkey, &lvi); - - if (item = (THotkeyItem *)lvi.lParam) { - RECT rc; - ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc); - MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2); - SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0); - - SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); - SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW); - RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); - - SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY)); - RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); - } -} - -static void sttOptionsDrawTextChunk(HDC hdc, wchar_t *text, RECT *rc) -{ - DrawText(hdc, text, -1, rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS); - - SIZE sz; - GetTextExtentPoint32(hdc, text, (int)mir_wstrlen(text), &sz); - rc->left += sz.cx; -} - -static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - static bool initialized = false; - static int colWidth = 0; - static uint16_t currentLanguage = 0; - - HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS); - - switch (msg) { - case WM_INITDIALOG: - initialized = false; - - TranslateDialogDefault(hwndDlg); - - Hotkey_Subclass(GetDlgItem(hwndDlg, IDC_HOTKEY)); - { - HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 3, 1); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOWS); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_MIRANDA); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOW); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_ADDCONTACT); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_DELETE); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_UNDO); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPOPEN); - ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPSHUT); - ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL); - } - ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP); - { - RECT rc; - GetClientRect(hwndHotkey, &rc); - colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3 * g_iIconSX - 5; - - LVCOLUMN lvc; - lvc.mask = LVCF_WIDTH; - lvc.cx = colWidth * 2 / 3; - ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc); - lvc.cx = colWidth / 3; - ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc); - lvc.cx = g_iIconSX; - ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc); - - for (auto &it : hotkeys) { - it->OptChanged = false; - it->OptDeleted = it->OptNew = false; - it->OptEnabled = it->Enabled; - it->OptHotkey = it->Hotkey; - it->OptType = it->type; - } - - currentLanguage = LOWORD(GetKeyboardLayout(0)); - sttBuildHotkeyList(hwndHotkey); - } - SetTimer(hwndDlg, 1024, 1000, nullptr); - initialized = TRUE; - { - /* load group states */ - int count = ListView_GetItemCount(hwndHotkey); - wchar_t buf[128]; - LVITEM lvi = {}; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - lvi.mask = LVIF_PARAM; - lvi.iSubItem = 0; - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) - continue; - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - ListView_GetItem(hwndHotkey, &lvi); - - ListView_SetCheckState(hwndHotkey, lvi.iItem, db_get_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), TRUE)); - } - } - - g_hwndHkOptions = hwndDlg; - break; - - case WM_TIMER: - if (initialized) { - uint16_t newLanguage = LOWORD(GetKeyboardLayout(0)); - if (newLanguage == currentLanguage) - break; - - int count = ListView_GetItemCount(hwndHotkey); - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) - sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam); - } - currentLanguage = newLanguage; - } - break; - - case WM_HOTKEYUNREGISTERED: - { - int count = ListView_GetItemCount(hwndHotkey); - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - ListView_GetItem(hwndHotkey, &lvi); - if (!lvi.lParam) continue; - - if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) { - ListView_DeleteItem(hwndHotkey, lvi.iItem); - --lvi.iItem; - --count; - } - } - } - break; - - case WM_DRAWITEM: - { - LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; - RECT rc = lpdis->rcItem; - int prefix = 65; - int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3; - rc.left += 5; - - HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); - if (lpdis->CtlID == IDC_CANVAS2) { - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc); - - rc.left = prefix; - ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc); - - rc.left = prefix + width; - ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc); - - rc.left = prefix + width * 2; - ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc); - return TRUE; - } - - if (lpdis->CtlID == IDC_CANVAS) { - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc); - - rc.left = prefix; - ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc); - - rc.left = prefix + width * 1; - ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc); - - rc.left = prefix + width * 2; - ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 20; - sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc); - return TRUE; - } - } - break; - - case WM_COMMAND: - if ((LOWORD(wParam) == IDC_HOTKEY) && ((HIWORD(wParam) == EN_KILLFOCUS) || (HIWORD(wParam) == 0))) { - LVITEM lvi; - THotkeyItem *item; - uint16_t wHotkey = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0); - - ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE); - SetFocus(hwndHotkey); - if (!wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0)) - break; - - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (lvi.iItem >= 0) { - ListView_GetItem(hwndHotkey, &lvi); - if (item = (THotkeyItem *)lvi.lParam) { - item->OptHotkey = wHotkey; - - sttOptionsSetupItem(hwndHotkey, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - } - break; - - case WM_CONTEXTMENU: - if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) { - HWND hwndList = (HWND)wParam; - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - LVITEM lvi = {}; - THotkeyItem *item = nullptr; - - lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); - if (lvi.iItem < 0) - return FALSE; - - lvi.mask = LVIF_PARAM; - ListView_GetItem(hwndList, &lvi); - if (!(item = (THotkeyItem *)lvi.lParam)) - return FALSE; - - if (pt.x == -1 && pt.y == -1) { - RECT rc; - ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL); - pt.x = rc.left; - pt.y = rc.bottom; - ClientToScreen(hwndList, &pt); - } - - enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT }; - - MENUITEMINFO mii = {}; - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_STATE; - mii.fState = MFS_DEFAULT; - - HMENU hMenu = CreatePopupMenu(); - AppendMenu(hMenu, MF_STRING, MI_CHANGE, TranslateT("Modify")); - SetMenuItemInfo(hMenu, MI_CHANGE, FALSE, &mii); - if (item->type != HKT_MANUAL) { - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING | - ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0), - (UINT_PTR)MI_SYSTEM, TranslateT("System scope")); - AppendMenu(hMenu, MF_STRING | - ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0), - (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope")); - } - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - if (!item->rootHotkey) - AppendMenu(hMenu, MF_STRING, MI_ADD, TranslateT("Add binding")); - else - AppendMenu(hMenu, MF_STRING, MI_REMOVE, TranslateT("Remove")); - if (item->Hotkey != item->OptHotkey) { - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - AppendMenu(hMenu, MF_STRING, MI_REVERT, TranslateT("Undo")); - } - - switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr)) { - case MI_CHANGE: - sttOptionsStartEdit(hwndDlg, hwndHotkey); - break; - case MI_SYSTEM: - item->OptType = HKT_GLOBAL; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case MI_LOCAL: - item->OptType = HKT_LOCAL; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - case MI_ADD: - initialized = false; - sttOptionsAddHotkey(hwndList, item); - initialized = true; - break; - case MI_REMOVE: - sttOptionsDeleteHotkey(hwndList, lvi.iItem, item); - break; - case MI_REVERT: - item->OptHotkey = item->Hotkey; - sttOptionsSetupItem(hwndList, lvi.iItem, item); - break; - } - DestroyMenu(hMenu); - break; - } - break; - - case WM_NOTIFY: - { - LPNMHDR lpnmhdr = (LPNMHDR)lParam; - switch (lpnmhdr->idFrom) { - case 0: - if ((lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET)) - break; - - UnregisterHotkeys(); - - for (auto &p : hotkeys.rev_iter()) - if (p->OptNew && p->OptDeleted || p->rootHotkey && !p->OptHotkey || (lpnmhdr->code == PSN_APPLY) && p->OptDeleted || (lpnmhdr->code == PSN_RESET) && p->OptNew) - FreeHotkey(hotkeys.removeItem(&p)); - - if (lpnmhdr->code == PSN_APPLY) { - LVITEM lvi = {}; - int count = ListView_GetItemCount(hwndHotkey); - - for (auto &it : hotkeys) - sttOptionsSaveItem(it); - - lvi.mask = LVIF_IMAGE; - lvi.iSubItem = COL_RESET; - lvi.iImage = -1; - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) - ListView_SetItem(hwndHotkey, &lvi); - } - - RegisterHotkeys(); - - NotifyEventHooks(hEvChanged, 0, 0); - break; - - case IDC_LV_HOTKEYS: - switch (lpnmhdr->code) { - case NM_CLICK: - { - LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam; - - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM | LVIF_IMAGE; - lvi.iItem = lpnmia->iItem; - ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi); - - auto *item = (THotkeyItem *)lvi.lParam; - if (item == nullptr) - break; - - LVHITTESTINFO lvhti = {}; - lvhti.pt = lpnmia->ptAction; - lvhti.iItem = lpnmia->iItem; - lvhti.iSubItem = lpnmia->iSubItem; - ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti); - - if ((!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) || - item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) && - ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) { - item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL; - sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - else if (lpnmia->iSubItem == COL_RESET) { - item->OptHotkey = item->Hotkey; - sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - } - else if (lpnmia->iSubItem == COL_ADDREMOVE) { - if (item->rootHotkey) - sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); - else { - initialized = false; - sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item); - initialized = true; - } - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - } - break; - - case LVN_KEYDOWN: - { - LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam; - if (param->wVKey == VK_SUBTRACT || param->wVKey == VK_LEFT || param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); - if (lvi.iItem < 0) - break; - - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - if (lvi.lParam) - break; - - if (param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE); - } - else { - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE); - } - } - else if (param->wVKey == VK_F2) - sttOptionsStartEdit(hwndDlg, hwndHotkey); - } - break; - - case LVN_ITEMACTIVATE: - { - LVITEM lvi = {}; - lvi.mask = LVIF_PARAM; - lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); - if (lvi.iItem < 0) break; - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - if (lvi.lParam) - sttOptionsStartEdit(hwndDlg, hwndHotkey); - else - ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem)); - } - break; - - case LVN_ITEMCHANGED: - if (initialized) { - LPNMLISTVIEW param = (LPNMLISTVIEW)lParam; - THotkeyItem *item = (THotkeyItem *)param->lParam; - if (param->uNewState >> 12 == param->uOldState >> 12) - break; - - if (item && !item->rootHotkey) { - item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0; - sttOptionsSetChanged(item); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - } - else if (!item) { - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT; - lvi.iItem = param->iItem; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - if (param->uNewState >> 12 == 1) { - int count = ListView_GetItemCount(lpnmhdr->hwndFrom); - LVITEM lvi2 = {}; - lvi2.mask = LVIF_PARAM; - for (lvi2.iItem = 0; lvi2.iItem < count; ++lvi2.iItem) { - ListView_GetItem(lpnmhdr->hwndFrom, &lvi2); - item = (THotkeyItem *)lvi2.lParam; - if (!item) continue; - if (!mir_wstrcmp(item->getSection(), buf)) { - ListView_DeleteItem(lpnmhdr->hwndFrom, lvi2.iItem); - --lvi2.iItem; - --count; - } - } - } - else if (param->uNewState >> 12 == 2) { - int nItems = ListView_GetItemCount(lpnmhdr->hwndFrom); - initialized = false; - for (auto &it : hotkeys) { - LVITEM lvi2 = {}; - if (it->OptDeleted || mir_wstrcmp(buf, it->getSection())) - continue; - - lvi2.mask = LVIF_PARAM | LVIF_INDENT; - lvi2.iIndent = 1; - lvi2.iItem = nItems++; - lvi2.lParam = (LPARAM)it; - ListView_InsertItem(lpnmhdr->hwndFrom, &lvi2); - sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems - 1, it); - } - ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom); - initialized = TRUE; - } - } - } - break; - - case NM_CUSTOMDRAW: - { - NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *)lParam; - switch (param->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - case CDDS_ITEMPREPAINT: - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW); - return TRUE; - - case CDDS_SUBITEM | CDDS_ITEMPREPAINT: - { - THotkeyItem *item; - wchar_t buf[256]; - LVITEM lvi = {}; - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iItem = param->nmcd.dwItemSpec; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - ListView_GetItem(lpnmhdr->hwndFrom, &lvi); - - item = (THotkeyItem *)lvi.lParam; - if (!item) { - RECT rc; - HFONT hfnt; - - ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); - FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); - SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); - - if (param->iSubItem == 0) { - rc.left += 3; - HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); - ImageList_Draw(hIml, - ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7, - param->nmcd.hdc, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); - rc.left += 18; - hfnt = (HFONT)SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0)); - DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); - SelectObject(param->nmcd.hdc, hfnt); - } - - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - - if (item->rootHotkey && (param->iSubItem == 0)) { - RECT rc; - ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); - FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); - return TRUE; - } - break; - } - } - break; - } - break; - } - } - } - break; - - case WM_DESTROY: - { - int count = ListView_GetItemCount(hwndHotkey); - - g_hwndHkOptions = nullptr; - - KillTimer(hwndDlg, 1024); - - wchar_t buf[128]; - LVITEM lvi = {}; - lvi.pszText = buf; - lvi.cchTextMax = _countof(buf); - for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { - lvi.mask = LVIF_PARAM; - lvi.iSubItem = 0; - ListView_GetItem(hwndHotkey, &lvi); - if (lvi.lParam) continue; - - lvi.mask = LVIF_TEXT; - lvi.iSubItem = 1; - ListView_GetItem(hwndHotkey, &lvi); - - db_set_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), ListView_GetCheckState(hwndHotkey, lvi.iItem)); - } - } - } - - return FALSE; -} - -int HotkeyOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.flags = ODPF_BOLDGROUPS; - odp.position = -180000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS); - odp.szTitle.a = LPGEN("Hotkeys"); - odp.szGroup.a = LPGEN("Customize"); - odp.pfnDlgProc = sttOptionsDlgProc; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#include "stdafx.h" + +#include +#include "skin.h" + +static wchar_t *sttHokeyVkToName(uint16_t vkKey) +{ + static wchar_t buf[256] = {}; + uint32_t code = MapVirtualKey(vkKey, 0) << 16; + + switch (vkKey) { + case 0: + case VK_CONTROL: + case VK_SHIFT: + case VK_MENU: + case VK_LWIN: + case VK_RWIN: + case VK_PAUSE: + case VK_CANCEL: + case VK_NUMLOCK: + case VK_CAPITAL: + case VK_SCROLL: + return L""; + case VK_BROWSER_BACK: + return TranslateT("Browser: Back"); + case VK_BROWSER_FORWARD: + return TranslateT("Browser: Forward"); + case VK_BROWSER_REFRESH: + return TranslateT("Browser: Refresh"); + case VK_BROWSER_STOP: + return TranslateT("Browser: Stop"); + case VK_BROWSER_SEARCH: + return TranslateT("Browser: Search"); + case VK_BROWSER_FAVORITES: + return TranslateT("Browser: Fav"); + case VK_BROWSER_HOME: + return TranslateT("Browser: Home"); + case VK_VOLUME_MUTE: + return TranslateT("Mute"); + case VK_VOLUME_DOWN: + return TranslateT("Vol-"); + case VK_VOLUME_UP: + return TranslateT("Vol+"); + case VK_MEDIA_NEXT_TRACK: + return TranslateT("Media: Next Track"); + case VK_MEDIA_PREV_TRACK: + return TranslateT("Media: Prev. Track"); + case VK_MEDIA_STOP: + return TranslateT("Media: Stop"); + case VK_MEDIA_PLAY_PAUSE: + return TranslateT("Media: Play/Pause"); + case VK_LAUNCH_MAIL: + return TranslateT("Mail"); + case VK_LAUNCH_MEDIA_SELECT: + return TranslateT("Media: Select"); + case VK_LAUNCH_APP1: + return TranslateT("App 1"); + case VK_LAUNCH_APP2: + return TranslateT("App 2"); + + case VK_DIVIDE: + case VK_INSERT: + case VK_HOME: + case VK_PRIOR: + case VK_DELETE: + case VK_END: + case VK_NEXT: + case VK_LEFT: + case VK_RIGHT: + case VK_UP: + case VK_DOWN: + code |= (1UL << 24); + } + + GetKeyNameText(code, buf, 256); + return buf; +} + +void HotkeyToName(wchar_t *buf, int size, uint8_t shift, uint8_t key) +{ + mir_snwprintf(buf, size, L"%s%s%s%s%s", + (shift & HOTKEYF_CONTROL) ? TranslateT("Ctrl + ") : L"", + (shift & HOTKEYF_ALT) ? TranslateT("Alt + ") : L"", + (shift & HOTKEYF_SHIFT) ? TranslateT("Shift + ") : L"", + (shift & HOTKEYF_EXT) ? TranslateT("Win + ") : L"", + sttHokeyVkToName(key)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Hotkey control + +static LRESULT CALLBACK sttHotkeyEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (!data) + return 0; + + bool bKeyDown = false; + + switch (msg) { + case HKM_GETHOTKEY: + return data->key ? MAKEWORD(data->key, data->shift) : 0; + + case HKM_SETHOTKEY: + { + wchar_t buf[256] = {}; + data->key = (uint8_t)LOWORD(wParam); + data->shift = (uint8_t)HIWORD(wParam); + HotkeyToName(buf, _countof(buf), data->shift, data->key); + SetWindowText(hwnd, buf); + } + return 0; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + case WM_KILLFOCUS: + break; + + case WM_CHAR: + case WM_SYSCHAR: + case WM_PASTE: + case WM_CONTEXTMENU: + return TRUE; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + bKeyDown = true; + + case WM_KEYUP: + case WM_SYSKEYUP: + { + wchar_t buf[256] = {}; + + uint8_t shift = 0; + uint8_t key = wParam; + wchar_t *name = sttHokeyVkToName(key); + if (!*name || !bKeyDown) + key = 0; + + if (GetAsyncKeyState(VK_CONTROL)) shift |= HOTKEYF_CONTROL; + if (GetAsyncKeyState(VK_MENU)) shift |= HOTKEYF_ALT; + if (GetAsyncKeyState(VK_SHIFT)) shift |= HOTKEYF_SHIFT; + if (GetAsyncKeyState(VK_LWIN) || GetAsyncKeyState(VK_RWIN)) shift |= HOTKEYF_EXT; + + if (bKeyDown || !data->key) { + data->shift = shift; + data->key = key; + } + + HotkeyToName(buf, _countof(buf), data->shift, data->key); + SetWindowText(hwnd, buf); + + if (bKeyDown && data->key) + SendMessage(GetParent(hwnd), WM_COMMAND, MAKELONG(GetWindowLongPtr(hwnd, GWL_ID), 0), (LPARAM)hwnd); + } + return TRUE; + + case WM_DESTROY: + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + mir_free(data); + } + + return mir_callNextSubclass(hwnd, sttHotkeyEditProc, msg, wParam, lParam); +} + +MIR_APP_DLL(void) Hotkey_Subclass(HWND hwnd) +{ + THotkeyBoxData *data = (THotkeyBoxData *)mir_alloc(sizeof(THotkeyBoxData)); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (ULONG_PTR)data); + mir_subclassWindow(hwnd, sttHotkeyEditProc); +} + +MIR_APP_DLL(void) Hotkey_Unsubclass(HWND hwnd) +{ + THotkeyBoxData *data = (THotkeyBoxData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + mir_free(data); +} + +/////////////////////////////////////////////////////////////////////////////// +// Options + +enum { COL_NAME, COL_TYPE, COL_KEY, COL_RESET, COL_ADDREMOVE }; + +static void sttOptionsSetupItem(HWND hwndList, int idx, THotkeyItem *item) +{ + wchar_t buf[256]; + LVITEM lvi = {}; + lvi.iItem = idx; + + if (!item->rootHotkey) { + lvi.mask = LVIF_TEXT | LVIF_IMAGE; + lvi.iSubItem = COL_NAME; + lvi.pszText = item->getDescr(); + lvi.iImage = item->OptType; + ListView_SetItem(hwndList, &lvi); + + ListView_SetCheckState(hwndList, lvi.iItem, item->Enabled); + } + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = COL_KEY; + HotkeyToName(buf, _countof(buf), HIBYTE(item->OptHotkey), LOBYTE(item->OptHotkey)); + lvi.pszText = buf; + ListView_SetItem(hwndList, &lvi); + + if (item->rootHotkey) { + lvi.mask = LVIF_IMAGE; + lvi.iSubItem = COL_TYPE; + lvi.iImage = item->OptType; + ListView_SetItem(hwndList, &lvi); + } + + lvi.mask = LVIF_IMAGE; + lvi.iSubItem = COL_RESET; + lvi.iImage = (item->Hotkey != item->OptHotkey) ? 5 : -1; + ListView_SetItem(hwndList, &lvi); + + lvi.mask = LVIF_IMAGE | LVIF_TEXT; + lvi.iSubItem = COL_ADDREMOVE; + if (item->rootHotkey) { + lvi.iImage = 4; + lvi.pszText = TranslateT("Remove shortcut"); + } + else { + lvi.iImage = 3; + lvi.pszText = TranslateT("Add another shortcut"); + } + ListView_SetItem(hwndList, &lvi); +} + +static void sttOptionsDeleteHotkey(HWND hwndList, int idx, THotkeyItem *item) +{ + item->OptDeleted = true; + ListView_DeleteItem(hwndList, idx); + if (item->rootHotkey) + item->rootHotkey->OptChanged = true; +} + +static int CALLBACK sttOptionsSortList(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) +{ + wchar_t title1[256] = {}, title2[256] = {}; + THotkeyItem *item1 = nullptr, *item2 = nullptr; + LVITEM lvi = {}; + int res; + + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = lParam1; + lvi.pszText = title1; + lvi.cchTextMax = _countof(title1); + if (ListView_GetItem((HWND)lParamSort, &lvi)) + item1 = (THotkeyItem *)lvi.lParam; + + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = lParam2; + lvi.pszText = title2; + lvi.cchTextMax = _countof(title2); + if (ListView_GetItem((HWND)lParamSort, &lvi)) + item2 = (THotkeyItem *)lvi.lParam; + + if (!item1 && !item2) + return mir_wstrcmp(title1, title2); + + if (!item1 && item2) { + if (res = mir_wstrcmp(title1, item2->getSection())) + return res; + return -1; + } + + if (!item2 && item1) { + if (res = mir_wstrcmp(item1->getSection(), title2)) + return res; + return 1; + } + /* item1 != nullptr && item2 != nullptr */ + + if (res = mir_wstrcmp(item1->getSection(), item2->getSection())) return res; + if (res = mir_wstrcmp(item1->getDescr(), item2->getDescr())) return res; + if (!item1->rootHotkey && item2->rootHotkey) return -1; + if (item1->rootHotkey && !item2->rootHotkey) return 1; + return 0; +} + +static void sttOptionsAddHotkey(HWND hwndList, THotkeyItem *item) +{ + char buf[256]; + mir_snprintf(buf, "mir_hotkey_%d_%d", g_pid, g_hkid++); + + THotkeyItem *newItem = (THotkeyItem *)mir_calloc(sizeof(THotkeyItem)); + newItem->pPlugin = item->pPlugin; + newItem->pszService = mir_strdup(item->pszService); + newItem->pwszSection = mir_wstrdup(item->pwszSection); + newItem->pwszDescription = mir_wstrdup(item->pwszDescription); + newItem->lParam = item->lParam; + newItem->idHotkey = GlobalAddAtomA(buf); + newItem->rootHotkey = item; + newItem->type = newItem->OptType = item->OptType; + newItem->Enabled = newItem->OptEnabled = newItem->OptNew = true; + hotkeys.insert(newItem); + + SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); + + LVITEM lvi = {}; + lvi.mask |= LVIF_PARAM; + lvi.lParam = (LPARAM)newItem; + sttOptionsSetupItem(hwndList, ListView_InsertItem(hwndList, &lvi), newItem); + ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); + + SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); + RedrawWindow(hwndList, nullptr, nullptr, RDW_INVALIDATE); + + item->OptChanged = true; +} + +static void sttOptionsSetChanged(THotkeyItem *item) +{ + item->OptChanged = true; + if (item->rootHotkey) + item->rootHotkey->OptChanged = true; +} + +static void sttOptionsSaveItem(THotkeyItem *item) +{ + char buf[MAXMODULELABELLENGTH]; + + if (item->rootHotkey) return; + if (!item->OptChanged) return; + + item->Hotkey = item->OptHotkey; + item->type = item->OptType; + item->Enabled = item->OptEnabled; + + db_set_w(0, DBMODULENAME, item->pszName, item->Hotkey); + db_set_b(0, DBMODULENAME "Off", item->pszName, (uint8_t)!item->Enabled); + if (item->type != HKT_MANUAL) + db_set_b(0, DBMODULENAME "Types", item->pszName, (uint8_t)item->type); + + item->nSubHotkeys = 0; + for (auto &it : hotkeys) { + if (it->rootHotkey == item) { + it->Hotkey = it->OptHotkey; + it->type = it->OptType; + + mir_snprintf(buf, "%s$%d", item->pszName, item->nSubHotkeys); + db_set_w(0, DBMODULENAME, buf, it->Hotkey); + if (it->type != HKT_MANUAL) + db_set_b(0, DBMODULENAME "Types", buf, (uint8_t)it->type); + + ++item->nSubHotkeys; + } + } + + mir_snprintf(buf, "%s$count", item->pszName); + db_set_dw(0, DBMODULENAME, buf, item->nSubHotkeys); +} + +static void sttBuildHotkeyList(HWND hwndList) +{ + ListView_DeleteAllItems(hwndList); + + int nItems = 0; + THotkeyItem *prevItem = nullptr; + for (auto &item : hotkeys) { + LVITEM lvi = {}; + + if (!item->OptDeleted) { + if (!prevItem || mir_wstrcmp(item->pwszSection, prevItem->pwszSection)) { + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = nItems++; + lvi.iSubItem = 0; + lvi.lParam = 0; + lvi.pszText = item->getSection(); + ListView_InsertItem(hwndList, &lvi); + ListView_SetCheckState(hwndList, lvi.iItem, TRUE); + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + lvi.pszText = item->pwszSection; + ListView_SetItem(hwndList, &lvi); + + lvi.iSubItem = 0; + } + + lvi.mask = LVIF_PARAM | LVIF_INDENT; + lvi.iIndent = 1; + lvi.iItem = nItems++; + lvi.lParam = (LPARAM)item; + ListView_InsertItem(hwndList, &lvi); + sttOptionsSetupItem(hwndList, nItems - 1, item); + } + + prevItem = item; + } + + ListView_SortItemsEx(hwndList, sttOptionsSortList, (LPARAM)hwndList); +} + +static void sttOptionsStartEdit(HWND hwndDlg, HWND hwndHotkey) +{ + LVITEM lvi; + THotkeyItem *item; + int iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); + if (iItem < 0) + return; + + lvi.mask = LVIF_PARAM; + lvi.iItem = iItem; + ListView_GetItem(hwndHotkey, &lvi); + + if (item = (THotkeyItem *)lvi.lParam) { + RECT rc; + ListView_GetSubItemRect(hwndHotkey, iItem, COL_KEY, LVIR_BOUNDS, &rc); + MapWindowPoints(hwndHotkey, hwndDlg, (LPPOINT)&rc, 2); + SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_SETHOTKEY, MAKELONG(LOBYTE(item->OptHotkey), HIBYTE(item->OptHotkey)), 0); + + SetWindowPos(hwndHotkey, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hwndDlg, IDC_HOTKEY), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW); + RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); + + SetFocus(GetDlgItem(hwndDlg, IDC_HOTKEY)); + RedrawWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), nullptr, nullptr, RDW_INVALIDATE); + } +} + +static void sttOptionsDrawTextChunk(HDC hdc, wchar_t *text, RECT *rc) +{ + DrawText(hdc, text, -1, rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS); + + SIZE sz; + GetTextExtentPoint32(hdc, text, (int)mir_wstrlen(text), &sz); + rc->left += sz.cx; +} + +static INT_PTR CALLBACK sttOptionsDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static bool initialized = false; + static int colWidth = 0; + static uint16_t currentLanguage = 0; + + HWND hwndHotkey = GetDlgItem(hwndDlg, IDC_LV_HOTKEYS); + + switch (msg) { + case WM_INITDIALOG: + initialized = false; + + TranslateDialogDefault(hwndDlg); + + Hotkey_Subclass(GetDlgItem(hwndDlg, IDC_HOTKEY)); + { + HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 3, 1); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOWS); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_MIRANDA); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_WINDOW); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_ADDCONTACT); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_DELETE); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_UNDO); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPOPEN); + ImageList_AddSkinIcon(hIml, SKINICON_OTHER_GROUPSHUT); + ListView_SetImageList(hwndHotkey, hIml, LVSIL_SMALL); + } + ListView_SetExtendedListViewStyle(hwndHotkey, LVS_EX_CHECKBOXES | LVS_EX_SUBITEMIMAGES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP); + { + RECT rc; + GetClientRect(hwndHotkey, &rc); + colWidth = rc.right - GetSystemMetrics(SM_CXHTHUMB) - 3 * g_iIconSX - 5; + + LVCOLUMN lvc; + lvc.mask = LVCF_WIDTH; + lvc.cx = colWidth * 2 / 3; + ListView_InsertColumn(hwndHotkey, COL_NAME, &lvc); + lvc.cx = g_iIconSX; + ListView_InsertColumn(hwndHotkey, COL_TYPE, &lvc); + lvc.cx = colWidth / 3; + ListView_InsertColumn(hwndHotkey, COL_KEY, &lvc); + lvc.cx = g_iIconSX; + ListView_InsertColumn(hwndHotkey, COL_RESET, &lvc); + lvc.cx = g_iIconSX; + ListView_InsertColumn(hwndHotkey, COL_ADDREMOVE, &lvc); + + for (auto &it : hotkeys) { + it->OptChanged = false; + it->OptDeleted = it->OptNew = false; + it->OptEnabled = it->Enabled; + it->OptHotkey = it->Hotkey; + it->OptType = it->type; + } + + currentLanguage = LOWORD(GetKeyboardLayout(0)); + sttBuildHotkeyList(hwndHotkey); + } + SetTimer(hwndDlg, 1024, 1000, nullptr); + initialized = TRUE; + { + /* load group states */ + int count = ListView_GetItemCount(hwndHotkey); + wchar_t buf[128]; + LVITEM lvi = {}; + lvi.pszText = buf; + lvi.cchTextMax = _countof(buf); + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + lvi.mask = LVIF_PARAM; + lvi.iSubItem = 0; + ListView_GetItem(hwndHotkey, &lvi); + if (lvi.lParam) + continue; + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + ListView_GetItem(hwndHotkey, &lvi); + + ListView_SetCheckState(hwndHotkey, lvi.iItem, db_get_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), TRUE)); + } + } + + g_hwndHkOptions = hwndDlg; + break; + + case WM_TIMER: + if (initialized) { + uint16_t newLanguage = LOWORD(GetKeyboardLayout(0)); + if (newLanguage == currentLanguage) + break; + + int count = ListView_GetItemCount(hwndHotkey); + + LVITEM lvi = {}; + lvi.mask = LVIF_PARAM; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + ListView_GetItem(hwndHotkey, &lvi); + if (lvi.lParam) + sttOptionsSetupItem(hwndHotkey, lvi.iItem, (THotkeyItem *)lvi.lParam); + } + currentLanguage = newLanguage; + } + break; + + case WM_HOTKEYUNREGISTERED: + { + int count = ListView_GetItemCount(hwndHotkey); + + LVITEM lvi = {}; + lvi.mask = LVIF_PARAM; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + ListView_GetItem(hwndHotkey, &lvi); + if (!lvi.lParam) continue; + + if (((THotkeyItem *)lvi.lParam)->UnregisterHotkey) { + ListView_DeleteItem(hwndHotkey, lvi.iItem); + --lvi.iItem; + --count; + } + } + } + break; + + case WM_DRAWITEM: + { + LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; + RECT rc = lpdis->rcItem; + int prefix = 65; + int width = (lpdis->rcItem.right - lpdis->rcItem.left - prefix) / 3; + rc.left += 5; + + HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); + if (lpdis->CtlID == IDC_CANVAS2) { + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Scope:"), &rc); + + rc.left = prefix; + ImageList_Draw(hIml, 0, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("System"), &rc); + + rc.left = prefix + width; + ImageList_Draw(hIml, 1, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Miranda"), &rc); + + rc.left = prefix + width * 2; + ImageList_Draw(hIml, 2, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Window"), &rc); + return TRUE; + } + + if (lpdis->CtlID == IDC_CANVAS) { + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Actions:"), &rc); + + rc.left = prefix; + ImageList_Draw(hIml, 5, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Undo"), &rc); + + rc.left = prefix + width * 1; + ImageList_Draw(hIml, 3, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Add binding"), &rc); + + rc.left = prefix + width * 2; + ImageList_Draw(hIml, 4, lpdis->hDC, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 20; + sttOptionsDrawTextChunk(lpdis->hDC, TranslateT("Remove"), &rc); + return TRUE; + } + } + break; + + case WM_COMMAND: + if ((LOWORD(wParam) == IDC_HOTKEY) && ((HIWORD(wParam) == EN_KILLFOCUS) || (HIWORD(wParam) == 0))) { + LVITEM lvi; + THotkeyItem *item; + uint16_t wHotkey = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0); + + ShowWindow(GetDlgItem(hwndDlg, IDC_HOTKEY), SW_HIDE); + SetFocus(hwndHotkey); + if (!wHotkey || (wHotkey == VK_ESCAPE) || (HIWORD(wParam) != 0)) + break; + + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); + if (lvi.iItem >= 0) { + ListView_GetItem(hwndHotkey, &lvi); + if (item = (THotkeyItem *)lvi.lParam) { + item->OptHotkey = wHotkey; + + sttOptionsSetupItem(hwndHotkey, lvi.iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + } + break; + + case WM_CONTEXTMENU: + if (GetWindowLongPtr((HWND)wParam, GWL_ID) == IDC_LV_HOTKEYS) { + HWND hwndList = (HWND)wParam; + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; + LVITEM lvi = {}; + THotkeyItem *item = nullptr; + + lvi.iItem = ListView_GetNextItem(hwndHotkey, -1, LVNI_SELECTED); + if (lvi.iItem < 0) + return FALSE; + + lvi.mask = LVIF_PARAM; + ListView_GetItem(hwndList, &lvi); + if (!(item = (THotkeyItem *)lvi.lParam)) + return FALSE; + + if (pt.x == -1 && pt.y == -1) { + RECT rc; + ListView_GetItemRect(hwndList, lvi.iItem, &rc, LVIR_LABEL); + pt.x = rc.left; + pt.y = rc.bottom; + ClientToScreen(hwndList, &pt); + } + + enum { MI_CANCEL, MI_CHANGE, MI_SYSTEM, MI_LOCAL, MI_ADD, MI_REMOVE, MI_REVERT }; + + MENUITEMINFO mii = {}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STATE; + mii.fState = MFS_DEFAULT; + + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, MI_CHANGE, TranslateT("Modify")); + SetMenuItemInfo(hMenu, MI_CHANGE, FALSE, &mii); + if (item->type != HKT_MANUAL) { + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + AppendMenu(hMenu, MF_STRING | + ((item->OptType == HKT_GLOBAL) ? MF_CHECKED : 0), + (UINT_PTR)MI_SYSTEM, TranslateT("System scope")); + AppendMenu(hMenu, MF_STRING | + ((item->OptType == HKT_LOCAL) ? MF_CHECKED : 0), + (UINT_PTR)MI_LOCAL, TranslateT("Miranda scope")); + } + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + if (!item->rootHotkey) + AppendMenu(hMenu, MF_STRING, MI_ADD, TranslateT("Add binding")); + else + AppendMenu(hMenu, MF_STRING, MI_REMOVE, TranslateT("Remove")); + if (item->Hotkey != item->OptHotkey) { + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + AppendMenu(hMenu, MF_STRING, MI_REVERT, TranslateT("Undo")); + } + + switch (TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr)) { + case MI_CHANGE: + sttOptionsStartEdit(hwndDlg, hwndHotkey); + break; + case MI_SYSTEM: + item->OptType = HKT_GLOBAL; + sttOptionsSetupItem(hwndList, lvi.iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case MI_LOCAL: + item->OptType = HKT_LOCAL; + sttOptionsSetupItem(hwndList, lvi.iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + case MI_ADD: + initialized = false; + sttOptionsAddHotkey(hwndList, item); + initialized = true; + break; + case MI_REMOVE: + sttOptionsDeleteHotkey(hwndList, lvi.iItem, item); + break; + case MI_REVERT: + item->OptHotkey = item->Hotkey; + sttOptionsSetupItem(hwndList, lvi.iItem, item); + break; + } + DestroyMenu(hMenu); + break; + } + break; + + case WM_NOTIFY: + { + LPNMHDR lpnmhdr = (LPNMHDR)lParam; + switch (lpnmhdr->idFrom) { + case 0: + if ((lpnmhdr->code != PSN_APPLY) && (lpnmhdr->code != PSN_RESET)) + break; + + UnregisterHotkeys(); + + for (auto &p : hotkeys.rev_iter()) + if (p->OptNew && p->OptDeleted || p->rootHotkey && !p->OptHotkey || (lpnmhdr->code == PSN_APPLY) && p->OptDeleted || (lpnmhdr->code == PSN_RESET) && p->OptNew) + FreeHotkey(hotkeys.removeItem(&p)); + + if (lpnmhdr->code == PSN_APPLY) { + LVITEM lvi = {}; + int count = ListView_GetItemCount(hwndHotkey); + + for (auto &it : hotkeys) + sttOptionsSaveItem(it); + + lvi.mask = LVIF_IMAGE; + lvi.iSubItem = COL_RESET; + lvi.iImage = -1; + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) + ListView_SetItem(hwndHotkey, &lvi); + } + + RegisterHotkeys(); + + NotifyEventHooks(hEvChanged, 0, 0); + break; + + case IDC_LV_HOTKEYS: + switch (lpnmhdr->code) { + case NM_CLICK: + { + LPNMITEMACTIVATE lpnmia = (LPNMITEMACTIVATE)lParam; + + LVITEM lvi = {}; + lvi.mask = LVIF_PARAM | LVIF_IMAGE; + lvi.iItem = lpnmia->iItem; + ListView_GetItem(lpnmia->hdr.hwndFrom, &lvi); + + auto *item = (THotkeyItem *)lvi.lParam; + if (item == nullptr) + break; + + LVHITTESTINFO lvhti = {}; + lvhti.pt = lpnmia->ptAction; + lvhti.iItem = lpnmia->iItem; + lvhti.iSubItem = lpnmia->iSubItem; + ListView_HitTest(lpnmia->hdr.hwndFrom, &lvhti); + + if ((!item->rootHotkey && (lpnmia->iSubItem == COL_NAME) && ((lvhti.flags & LVHT_ONITEM) == LVHT_ONITEMICON) || + item->rootHotkey && (lpnmia->iSubItem == COL_TYPE)) && + ((item->OptType == HKT_GLOBAL) || (item->OptType == HKT_LOCAL))) { + item->OptType = (item->OptType == HKT_GLOBAL) ? HKT_LOCAL : HKT_GLOBAL; + sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + else if (lpnmia->iSubItem == COL_RESET) { + item->OptHotkey = item->Hotkey; + sttOptionsSetupItem(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); + } + else if (lpnmia->iSubItem == COL_ADDREMOVE) { + if (item->rootHotkey) + sttOptionsDeleteHotkey(lpnmia->hdr.hwndFrom, lpnmia->iItem, item); + else { + initialized = false; + sttOptionsAddHotkey(lpnmia->hdr.hwndFrom, item); + initialized = true; + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + break; + + case LVN_KEYDOWN: + { + LPNMLVKEYDOWN param = (LPNMLVKEYDOWN)lParam; + if (param->wVKey == VK_SUBTRACT || param->wVKey == VK_LEFT || param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { + LVITEM lvi = {}; + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); + if (lvi.iItem < 0) + break; + + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + if (lvi.lParam) + break; + + if (param->wVKey == VK_ADD || param->wVKey == VK_RIGHT) { + ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, TRUE); + } + else { + ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, FALSE); + } + } + else if (param->wVKey == VK_F2) + sttOptionsStartEdit(hwndDlg, hwndHotkey); + } + break; + + case LVN_ITEMACTIVATE: + { + LVITEM lvi = {}; + lvi.mask = LVIF_PARAM; + lvi.iItem = ListView_GetNextItem(lpnmhdr->hwndFrom, -1, LVNI_SELECTED); + if (lvi.iItem < 0) break; + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + + if (lvi.lParam) + sttOptionsStartEdit(hwndDlg, hwndHotkey); + else + ListView_SetCheckState(lpnmhdr->hwndFrom, lvi.iItem, !ListView_GetCheckState(lpnmhdr->hwndFrom, lvi.iItem)); + } + break; + + case LVN_ITEMCHANGED: + if (initialized) { + LPNMLISTVIEW param = (LPNMLISTVIEW)lParam; + THotkeyItem *item = (THotkeyItem *)param->lParam; + if (param->uNewState >> 12 == param->uOldState >> 12) + break; + + if (item && !item->rootHotkey) { + item->OptEnabled = ListView_GetCheckState(lpnmhdr->hwndFrom, param->iItem) ? 1 : 0; + sttOptionsSetChanged(item); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + else if (!item) { + wchar_t buf[256]; + LVITEM lvi = {}; + lvi.mask = LVIF_TEXT; + lvi.iItem = param->iItem; + lvi.pszText = buf; + lvi.cchTextMax = _countof(buf); + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + + if (param->uNewState >> 12 == 1) { + int count = ListView_GetItemCount(lpnmhdr->hwndFrom); + LVITEM lvi2 = {}; + lvi2.mask = LVIF_PARAM; + for (lvi2.iItem = 0; lvi2.iItem < count; ++lvi2.iItem) { + ListView_GetItem(lpnmhdr->hwndFrom, &lvi2); + item = (THotkeyItem *)lvi2.lParam; + if (!item) continue; + if (!mir_wstrcmp(item->getSection(), buf)) { + ListView_DeleteItem(lpnmhdr->hwndFrom, lvi2.iItem); + --lvi2.iItem; + --count; + } + } + } + else if (param->uNewState >> 12 == 2) { + int nItems = ListView_GetItemCount(lpnmhdr->hwndFrom); + initialized = false; + for (auto &it : hotkeys) { + LVITEM lvi2 = {}; + if (it->OptDeleted || mir_wstrcmp(buf, it->getSection())) + continue; + + lvi2.mask = LVIF_PARAM | LVIF_INDENT; + lvi2.iIndent = 1; + lvi2.iItem = nItems++; + lvi2.lParam = (LPARAM)it; + ListView_InsertItem(lpnmhdr->hwndFrom, &lvi2); + sttOptionsSetupItem(lpnmhdr->hwndFrom, nItems - 1, it); + } + ListView_SortItemsEx(lpnmhdr->hwndFrom, sttOptionsSortList, (LPARAM)lpnmhdr->hwndFrom); + initialized = TRUE; + } + } + } + break; + + case NM_CUSTOMDRAW: + { + NMLVCUSTOMDRAW *param = (NMLVCUSTOMDRAW *)lParam; + switch (param->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + case CDDS_ITEMPREPAINT: + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_NOTIFYSUBITEMDRAW); + return TRUE; + + case CDDS_SUBITEM | CDDS_ITEMPREPAINT: + { + THotkeyItem *item; + wchar_t buf[256]; + LVITEM lvi = {}; + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = param->nmcd.dwItemSpec; + lvi.pszText = buf; + lvi.cchTextMax = _countof(buf); + ListView_GetItem(lpnmhdr->hwndFrom, &lvi); + + item = (THotkeyItem *)lvi.lParam; + if (!item) { + RECT rc; + HFONT hfnt; + + ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); + FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); + SetTextColor(param->nmcd.hdc, GetSysColor(param->nmcd.uItemState & CDIS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); + + if (param->iSubItem == 0) { + rc.left += 3; + HIMAGELIST hIml = ListView_GetImageList(hwndHotkey, LVSIL_SMALL); + ImageList_Draw(hIml, + ListView_GetCheckState(hwndHotkey, lvi.iItem) ? 6 : 7, + param->nmcd.hdc, rc.left, (rc.top + rc.bottom - 16) / 2, ILD_TRANSPARENT); + rc.left += 18; + hfnt = (HFONT)SelectObject(param->nmcd.hdc, (HFONT)SendMessage(GetParent(hwndDlg), PSM_GETBOLDFONT, 0, 0)); + DrawText(param->nmcd.hdc, buf, -1, &rc, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); + SelectObject(param->nmcd.hdc, hfnt); + } + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); + return TRUE; + } + + if (item->rootHotkey && (param->iSubItem == 0)) { + RECT rc; + ListView_GetSubItemRect(lpnmhdr->hwndFrom, param->nmcd.dwItemSpec, param->iSubItem, LVIR_BOUNDS, &rc); + FillRect(param->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW)); + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT); + return TRUE; + } + break; + } + } + break; + } + break; + } + } + } + break; + + case WM_DESTROY: + { + int count = ListView_GetItemCount(hwndHotkey); + + g_hwndHkOptions = nullptr; + + KillTimer(hwndDlg, 1024); + + wchar_t buf[128]; + LVITEM lvi = {}; + lvi.pszText = buf; + lvi.cchTextMax = _countof(buf); + for (lvi.iItem = 0; lvi.iItem < count; ++lvi.iItem) { + lvi.mask = LVIF_PARAM; + lvi.iSubItem = 0; + ListView_GetItem(hwndHotkey, &lvi); + if (lvi.lParam) continue; + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + ListView_GetItem(hwndHotkey, &lvi); + + db_set_b(0, DBMODULENAME "UI", _T2A(lvi.pszText), ListView_GetCheckState(hwndHotkey, lvi.iItem)); + } + } + } + + return FALSE; +} + +int HotkeyOptionsInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS; + odp.position = -180000000; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_HOTKEYS); + odp.szTitle.a = LPGEN("Hotkeys"); + odp.szGroup.a = LPGEN("Customize"); + odp.pfnDlgProc = sttOptionsDlgProc; + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/mir_app/src/hotkeys.cpp b/src/mir_app/src/hotkeys.cpp index 336bf3602e..a60cf84fe8 100644 --- a/src/mir_app/src/hotkeys.cpp +++ b/src/mir_app/src/hotkeys.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/icolib.cpp b/src/mir_app/src/icolib.cpp index aaccc00fd1..3327151b41 100644 --- a/src/mir_app/src/icolib.cpp +++ b/src/mir_app/src/icolib.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/idle.cpp b/src/mir_app/src/idle.cpp index cb1c771f11..fdf0f7b49b 100644 --- a/src/mir_app/src/idle.cpp +++ b/src/mir_app/src/idle.cpp @@ -1,87 +1,87 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define IDLE_MODULE "Idle" - -static bool bModuleInitialized = false; - -static int g_idleType; -static int g_bIsIdle; - -static HANDLE hIdleEvent; - -MIR_APP_DLL(void) Idle_Enter(int type) -{ - int flags = 0; - - if (db_get_b(0, IDLE_MODULE, "IdlePrivate")) - flags |= IDF_PRIVACY; - - if (!g_bIsIdle && type != -1) { - g_bIsIdle = true; - g_idleType = type; - NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags); - } - - if (g_bIsIdle && type == -1) { - g_bIsIdle = false; - g_idleType = 0; - NotifyEventHooks(hIdleEvent, 0, flags); - } -} - -MIR_APP_DLL(void) Idle_GetInfo(MIRANDA_IDLE_INFO &pInfo) -{ - pInfo.idleTime = db_get_dw(0, IDLE_MODULE, "IdleTime1st"); - pInfo.privacy = db_get_b(0, IDLE_MODULE, "IdlePrivate"); - pInfo.aaStatus = db_get_b(0, IDLE_MODULE, "AAEnable", 1) ? db_get_dw(0, IDLE_MODULE, "AAStatus") : 0; - pInfo.aaLock = db_get_b(0, IDLE_MODULE, "IdleStatusLock"); - pInfo.idlesoundsoff = db_get_b(0, IDLE_MODULE, "IdleSoundsOff"); - pInfo.idleType = g_idleType; -} - -int LoadIdleModule(void) -{ - bModuleInitialized = true; - - hIdleEvent = CreateHookableEvent(ME_IDLE_CHANGED); - - g_idleType = g_bIsIdle = 0; - return 0; -} - -void UnloadIdleModule() -{ - if (!bModuleInitialized) return; - - if (g_bIsIdle) { - NotifyEventHooks(hIdleEvent, 0, 0); - g_bIsIdle = false; - } - - DestroyHookableEvent(hIdleEvent); - hIdleEvent = nullptr; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#define IDLE_MODULE "Idle" + +static bool bModuleInitialized = false; + +static int g_idleType; +static int g_bIsIdle; + +static HANDLE hIdleEvent; + +MIR_APP_DLL(void) Idle_Enter(int type) +{ + int flags = 0; + + if (db_get_b(0, IDLE_MODULE, "IdlePrivate")) + flags |= IDF_PRIVACY; + + if (!g_bIsIdle && type != -1) { + g_bIsIdle = true; + g_idleType = type; + NotifyEventHooks(hIdleEvent, 0, IDF_ISIDLE | flags); + } + + if (g_bIsIdle && type == -1) { + g_bIsIdle = false; + g_idleType = 0; + NotifyEventHooks(hIdleEvent, 0, flags); + } +} + +MIR_APP_DLL(void) Idle_GetInfo(MIRANDA_IDLE_INFO &pInfo) +{ + pInfo.idleTime = db_get_dw(0, IDLE_MODULE, "IdleTime1st"); + pInfo.privacy = db_get_b(0, IDLE_MODULE, "IdlePrivate"); + pInfo.aaStatus = db_get_b(0, IDLE_MODULE, "AAEnable", 1) ? db_get_dw(0, IDLE_MODULE, "AAStatus") : 0; + pInfo.aaLock = db_get_b(0, IDLE_MODULE, "IdleStatusLock"); + pInfo.idlesoundsoff = db_get_b(0, IDLE_MODULE, "IdleSoundsOff"); + pInfo.idleType = g_idleType; +} + +int LoadIdleModule(void) +{ + bModuleInitialized = true; + + hIdleEvent = CreateHookableEvent(ME_IDLE_CHANGED); + + g_idleType = g_bIsIdle = 0; + return 0; +} + +void UnloadIdleModule() +{ + if (!bModuleInitialized) return; + + if (g_bIsIdle) { + NotifyEventHooks(hIdleEvent, 0, 0); + g_bIsIdle = false; + } + + DestroyHookableEvent(hIdleEvent); + hIdleEvent = nullptr; +} diff --git a/src/mir_app/src/ignore.cpp b/src/mir_app/src/ignore.cpp index 4e5eb804cf..df84141dfc 100644 --- a/src/mir_app/src/ignore.cpp +++ b/src/mir_app/src/ignore.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/keyboard.cpp b/src/mir_app/src/keyboard.cpp index dd5643ae7e..87929e756c 100644 --- a/src/mir_app/src/keyboard.cpp +++ b/src/mir_app/src/keyboard.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/langpack.h b/src/mir_app/src/langpack.h index 04a5abd7d1..1683e3e0ac 100644 --- a/src/mir_app/src/langpack.h +++ b/src/mir_app/src/langpack.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/lpopts.cpp b/src/mir_app/src/lpopts.cpp index 3675694ff9..7444c8e1dc 100644 --- a/src/mir_app/src/lpopts.cpp +++ b/src/mir_app/src/lpopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/mdatabasecache.cpp b/src/mir_app/src/mdatabasecache.cpp index 29cc99cfb5..01ef939bd0 100644 --- a/src/mir_app/src/mdatabasecache.cpp +++ b/src/mir_app/src/mdatabasecache.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/menu_clist.cpp b/src/mir_app/src/menu_clist.cpp index 0a545db4a0..bf94b21017 100644 --- a/src/mir_app/src/menu_clist.cpp +++ b/src/mir_app/src/menu_clist.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/menu_frames.cpp b/src/mir_app/src/menu_frames.cpp index 323cd0c5b0..1b64b9bd0b 100644 --- a/src/mir_app/src/menu_frames.cpp +++ b/src/mir_app/src/menu_frames.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/menu_groups.cpp b/src/mir_app/src/menu_groups.cpp index 4b12e7a0f8..3460d68b1d 100644 --- a/src/mir_app/src/menu_groups.cpp +++ b/src/mir_app/src/menu_groups.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-08 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/menu_options.cpp b/src/mir_app/src/menu_options.cpp index 648e3bbc4e..2f5456918b 100644 --- a/src/mir_app/src/menu_options.cpp +++ b/src/mir_app/src/menu_options.cpp @@ -1,588 +1,588 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "genmenu.h" -#include "plugins.h" - -#define STR_SEPARATOR L"-----------------------------------" - -extern bool bIconsDisabled; -extern int DefaultImageListColorDepth; -void RebuildProtoMenus(); - -MIR_APP_DLL(void) Menu_SetVisible(TMO_IntMenuItem *pimi, bool bVisible) -{ - if ((pimi = MO_GetIntMenuItem(pimi)) == nullptr) - return; - - char szModule[256], menuItemName[256]; - mir_snprintf(szModule, "%s_Items", pimi->parent->pszName); - bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); - - ptrW wszValue(db_get_wsa(0, szModule, menuItemName, L"1;;;")); - wszValue[0] = bVisible ? '1' : '0'; - db_set_ws(0, szModule, menuItemName, wszValue); - - Menu_ShowItem(pimi, bVisible); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct MenuItemOptData : public MZeroedObject -{ - ~MenuItemOptData() {} - - int pos; - - ptrW name; - ptrW defname; - - bool bShow; - int id; - - TMO_IntMenuItem *pimi; -}; - -static int SortMenuItems(const MenuItemOptData *p1, const MenuItemOptData *p2) -{ - if (p1->pos < p2->pos) return -1; - if (p1->pos > p2->pos) return 1; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CGenMenuOptionsPage : public CDlgBase -{ - int iInitMenuValue; - LIST m_arDeleted; - - wchar_t idstr[100]; - - void SaveTreeInternal(MenuItemOptData *pParent, HTREEITEM hRootItem, const char *szModule) - { - TVITEMEX tvi; - tvi.hItem = hRootItem; - tvi.cchTextMax = _countof(idstr); - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE | TVIF_IMAGE; - tvi.pszText = idstr; - - int count = 0, customOrder = 0; - int runtimepos = 100; - - char pszParent[33]; - if (pParent == nullptr) - pszParent[0] = 0; - else - bin2hex(&pParent->pimi->mi.uid, sizeof(MUUID), pszParent); - - while (tvi.hItem != nullptr) { - m_menuItems.GetItem(&tvi); - auto *iod = (MenuItemOptData*)tvi.lParam; - if (TMO_IntMenuItem *pimi = iod->pimi) { - if (pimi->mi.uid != miid_last) { - char menuItemName[256]; - bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); - - int visible = tvi.iImage != 0; - wchar_t *ptszCustomName; - if (iod->name != nullptr && iod->defname != nullptr && mir_wstrcmp(iod->name, iod->defname) != 0) - ptszCustomName = iod->name; - else - ptszCustomName = L""; - - CMStringW tszValue(FORMAT, L"%d;%d;%S;%s", visible, runtimepos, pszParent, ptszCustomName); - db_set_ws(0, szModule, menuItemName, tszValue); - - if (pimi->mi.flags & CMIF_CUSTOM) - db_set_s(0, szModule, CMStringA(FORMAT, "Custom%d", customOrder++), menuItemName); - } - - HTREEITEM hChild = m_menuItems.GetChild(tvi.hItem); - if (hChild != nullptr) - SaveTreeInternal(iod, hChild, szModule); - - runtimepos += 100; - } - - if (iod->name && !mir_wstrcmp(iod->name, STR_SEPARATOR) && tvi.iImage) - runtimepos += SEPARATORPOSITIONINTERVAL; - - tvi.hItem = m_menuItems.GetNextSibling(tvi.hItem); - count++; - } - } - - void SaveTree() - { - int MenuObjectId; - if (!GetCurrentMenuObjectID(MenuObjectId)) - return; - - TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); - if (pmo == nullptr) - return; - - char szModule[256]; - mir_snprintf(szModule, "%s_Items", pmo->pszName); - db_delete_module(0, szModule); - SaveTreeInternal(nullptr, m_menuItems.GetRoot(), szModule); - db_set_b(0, szModule, "MenuFormat", 1); - } - - void FreeTreeData() - { - HTREEITEM hItem = m_menuItems.GetRoot(); - while (hItem != nullptr) { - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_menuItems.GetItem(&tvi); - delete (MenuItemOptData *)tvi.lParam; - - tvi.lParam = 0; - m_menuItems.SetItem(&tvi); - - hItem = m_menuItems.GetNextSibling(hItem); - } - } - - void RebuildCurrent() - { - int MenuObjectID; - if (GetCurrentMenuObjectID(MenuObjectID)) - BuildTree(MenuObjectID, true); - } - - void BuildTreeInternal(const char *pszModule, bool bReread, TMO_IntMenuItem *pFirst, HTREEITEM hRoot) - { - LIST arItems(10, SortMenuItems); - - for (TMO_IntMenuItem *p = pFirst; p != nullptr; p = p->next) { - // filter out items whose presence & position might not be changed - if (p->mi.flags & CMIF_SYSTEM) - continue; - - MenuItemOptData *PD = new MenuItemOptData(); - PD->pimi = p; - PD->defname = mir_wstrdup(GetMenuItemText(p)); - PD->name = mir_wstrdup((bReread && p->ptszCustomName != nullptr) ? p->ptszCustomName : PD->defname); - PD->bShow = (p->mi.flags & CMIF_HIDDEN) == 0; - PD->pos = (bReread) ? p->mi.position : p->originalPosition; - PD->id = p->iCommand; - arItems.insert(PD); - } - - int lastpos = 0; - bool bIsFirst = TRUE; - - TVINSERTSTRUCT tvis; - tvis.hParent = hRoot; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - - for (auto &it : arItems) { - if (it != arItems[0] && it->pos - lastpos >= SEPARATORPOSITIONINTERVAL) { - MenuItemOptData *sep = new MenuItemOptData(); - sep->id = -1; - sep->name = mir_wstrdup(STR_SEPARATOR); - sep->pos = it->pos - 1; - - tvis.item.lParam = (LPARAM)sep; - tvis.item.pszText = sep->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.item.cChildren = 0; - m_menuItems.InsertItem(&tvis); - } - - tvis.item.lParam = (LPARAM)it; - tvis.item.pszText = it->name; - tvis.item.iImage = tvis.item.iSelectedImage = it->bShow; - tvis.item.cChildren = it->pimi->submenu.first != nullptr; - - HTREEITEM hti = m_menuItems.InsertItem(&tvis); - if (bIsFirst) { - if (hRoot == nullptr) - m_menuItems.SelectItem(hti); - bIsFirst = false; - } - - if (it->pimi->submenu.first != nullptr) { - BuildTreeInternal(pszModule, bReread, it->pimi->submenu.first, hti); - m_menuItems.Expand(hti, TVE_EXPAND); - } - - lastpos = it->pos; - } - } - - bool BuildTree(int MenuObjectId, bool bReread) - { - FreeTreeData(); - - TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); - if (pmo == nullptr || pmo->m_items.first == nullptr) - return false; - - char szModule[256]; - mir_snprintf(szModule, "%s_Items", pmo->pszName); - - if (bReread) // no need to reread database on reset - MO_RecursiveWalkMenu(pmo->m_items.first, Menu_LoadFromDatabase, szModule); - - m_menuItems.SetDraw(false); - m_menuItems.DeleteAllItems(); - - BuildTreeInternal(szModule, bReread, pmo->m_items.first, nullptr); - - m_menuItems.SetDraw(true); - - m_warning.Show(!pmo->m_bUseUserDefinedItems); - m_menuItems.Enable(pmo->m_bUseUserDefinedItems); - m_btnInsSeparator.Enable(pmo->m_bUseUserDefinedItems); - m_btnInsMenu.Enable(pmo->m_bUseUserDefinedItems); - return 1; - } - - bool GetCurrentMenuObjectID(int &result) - { - int iItem = m_menuObjects.GetCurSel(); - if (iItem == -1) - return false; - - result = (int)m_menuObjects.GetItemData(iItem); - return true; - } - - CCtrlListBox m_menuObjects; - CCtrlTreeView m_menuItems; - CCtrlCheck m_radio1, m_radio2, m_enableIcons; - CCtrlEdit m_customName, m_service, m_module; - CCtrlButton m_btnInsSeparator, m_btnInsMenu, m_btnReset, m_btnSet, m_btnDefault, m_btnDelete; - CCtrlBase m_warning; - -public: - CGenMenuOptionsPage() : - CDlgBase(g_plugin, IDD_OPT_GENMENU), - m_arDeleted(1), - m_menuItems(this, IDC_MENUITEMS), - m_menuObjects(this, IDC_MENUOBJECTS), - m_radio1(this, IDC_RADIO1), - m_radio2(this, IDC_RADIO2), - m_enableIcons(this, IDC_DISABLEMENUICONS), - m_btnInsSeparator(this, IDC_INSERTSEPARATOR), - m_btnInsMenu(this, IDC_INSERTSUBMENU), - m_btnReset(this, IDC_RESETMENU), - m_btnSet(this, IDC_GENMENU_SET), - m_btnDelete(this, IDC_GENMENU_DELETE), - m_btnDefault(this, IDC_GENMENU_DEFAULT), - m_customName(this, IDC_GENMENU_CUSTOMNAME), - m_service(this, IDC_GENMENU_SERVICE), - m_module(this, IDC_GENMENU_MODULE), - m_warning(this, IDC_NOTSUPPORTWARNING) - { - m_btnSet.OnClick = Callback(this, &CGenMenuOptionsPage::btnSet_Clicked); - m_btnReset.OnClick = Callback(this, &CGenMenuOptionsPage::btnReset_Clicked); - m_btnInsSeparator.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsSep_Clicked); - m_btnInsMenu.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsMenu_Clicked); - m_btnDefault.OnClick = Callback(this, &CGenMenuOptionsPage::btnDefault_Clicked); - m_btnDelete.OnClick = Callback(this, &CGenMenuOptionsPage::btnDelete_Clicked); - - m_menuObjects.OnSelChange = Callback(this, &CGenMenuOptionsPage::onMenuObjectChanged); - - m_menuItems.SetFlags(MTREE_CHECKBOX | MTREE_DND); - m_menuItems.OnSelChanged = Callback(this, &CGenMenuOptionsPage::onMenuItemChanged); - m_menuItems.OnBeginDrag = Callback(this, &CGenMenuOptionsPage::onMenuItemBeginDrag); - - m_customName.SetSilent(); - m_service.SetSilent(); - m_module.SetSilent(); - } - - //---- init dialog ------------------------------------------- - bool OnInitDialog() override - { - iInitMenuValue = db_get_b(0, "CList", "MoveProtoMenus", TRUE); - - if (iInitMenuValue) - m_radio2.SetState(true); - else - m_radio1.SetState(true); - - m_enableIcons.SetState(!bIconsDisabled); - - //---- init menu object list -------------------------------------- - for (auto &p : g_menus) - if (p->id != (int)hStatusMenuObject && p->m_bUseUserDefinedItems) - m_menuObjects.AddString(TranslateW(p->ptszDisplayName), p->id); - - m_menuObjects.SetCurSel(0); - RebuildCurrent(); - return true; - } - - bool OnApply() override - { - bIconsDisabled = m_enableIcons.GetState() == 0; - db_set_b(0, "CList", "DisableMenuIcons", bIconsDisabled); - SaveTree(); - - for (auto &pimi : m_arDeleted) - Menu_RemoveItem(pimi); - - int iNewMenuValue = !m_radio1.GetState(); - if (iNewMenuValue != iInitMenuValue) { - db_set_b(0, "CList", "MoveProtoMenus", iNewMenuValue); - - RebuildProtoMenus(); - iInitMenuValue = iNewMenuValue; - } - RebuildCurrent(); - return true; - } - - void OnDestroy() override - { - FreeTreeData(); - } - - void btnInsSep_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; - tvi.hItem = hti; - if (!m_menuItems.GetItem(&tvi)) - return; - - MenuItemOptData *PD = new MenuItemOptData(); - PD->id = -1; - PD->name = mir_wstrdup(STR_SEPARATOR); - PD->pos = ((MenuItemOptData *)tvi.lParam)->pos - 1; - - TVINSERTSTRUCT tvis = {}; - tvis.item.lParam = (LPARAM)PD; - tvis.item.pszText = PD->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - m_menuItems.InsertItem(&tvis); - - NotifyChange(); - } - - void btnInsMenu_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi = { 0 }; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; - tvi.hItem = hti; - if (!m_menuItems.GetItem(&tvi)) - return; - - MenuItemOptData *curData = (MenuItemOptData*)tvi.lParam; - - TMO_MenuItem mi = {}; - UuidCreate((UUID*)&mi.uid); - mi.flags = CMIF_CUSTOM; - mi.name.a = LPGEN("New submenu"); - mi.position = curData->pos - 1; - TMO_IntMenuItem *pimi = Menu_AddItem(curData->pimi->parent->id, &mi, nullptr); - - MenuItemOptData *PD = new MenuItemOptData(); - PD->id = -1; - PD->name = mir_wstrdup(pimi->mi.name.w); - PD->pos = pimi->mi.position; - PD->pimi = pimi; - - TVINSERTSTRUCT tvis = {}; - tvis.item.lParam = (LPARAM)PD; - tvis.item.pszText = PD->name; - tvis.item.iImage = tvis.item.iSelectedImage = 1; - tvis.hInsertAfter = hti; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - m_menuItems.InsertItem(&tvis); - - NotifyChange(); - } - - void btnReset_Clicked(CCtrlButton*) - { - int MenuObjectID; - if (GetCurrentMenuObjectID(MenuObjectID)) { - BuildTree(MenuObjectID, false); - NotifyChange(); - } - } - - void btnDefault_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - iod->name = mir_wstrdup(iod->defname); - m_customName.SetText(iod->defname); - - tvi.mask = TVIF_TEXT; - tvi.pszText = iod->name; - m_menuItems.SetItem(&tvi); - NotifyChange(); - } - - void btnSet_Clicked(CCtrlButton*) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - iod->name = m_customName.GetText(); - - tvi.mask = TVIF_TEXT; - tvi.pszText = iod->name; - m_menuItems.SetItem(&tvi); - NotifyChange(); - } - - void btnDelete_Clicked(CCtrlButton *) - { - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (!(iod->pimi->mi.flags & CMIF_CUSTOM)) - return; - - if (IDYES == MessageBoxW(m_hwnd, TranslateT("Do you really want to delete this menu item?"), TranslateT("Miranda"), MB_YESNO | MB_ICONQUESTION)) { - m_arDeleted.insert(iod->pimi); - m_menuItems.DeleteItem(hti); - delete iod; - NotifyChange(); - } - } - - void onMenuObjectChanged(void*) - { - m_bInitialized = false; - RebuildCurrent(); - m_bInitialized = true; - } - - void onMenuItemChanged(void*) - { - m_customName.SetTextA(""); - m_service.SetTextA(""); - m_module.SetTextA(""); - - m_btnInsMenu.Disable(); - m_btnDefault.Disable(); - m_btnSet.Disable(); - m_btnDelete.Disable(); - m_customName.Disable(); - - HTREEITEM hti = m_menuItems.GetSelection(); - if (hti == nullptr) - return; - - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.hItem = hti; - m_menuItems.GetItem(&tvi); - if (tvi.lParam == 0) - return; - - MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; - if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) - return; - - m_customName.SetText(iod->name); - - if (iod->pimi->mi.uid != miid_last) { - char szText[100]; - bin2hex(&iod->pimi->mi.uid, sizeof(iod->pimi->mi.uid), szText); - m_service.SetTextA(szText); - } - - const CMPluginBase *pPlugin = iod->pimi->mi.pPlugin; - m_module.SetTextA(pPlugin == nullptr ? "" : pPlugin->getInfo().shortName); - - m_btnInsMenu.Enable(iod->pimi->mi.root == nullptr); - m_btnDefault.Enable(mir_wstrcmp(iod->name, iod->defname) != 0); - m_btnDelete.Enable(iod->pimi->mi.flags & CMIF_CUSTOM); - m_btnSet.Enable(true); - m_customName.Enable(true); - } - - void onMenuItemBeginDrag(CCtrlTreeView::TEventInfo *evt) - { - MenuItemOptData *p = (MenuItemOptData*)evt->nmtv->itemNew.lParam; - if (p->pimi != nullptr) - if (p->pimi->mi.flags & CMIF_UNMOVABLE) - evt->nmhdr->code = 0; // reject an attempt to change item's position - } -}; - -int GenMenuOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -1000000000; - odp.szTitle.a = LPGEN("Menus"); - odp.szGroup.a = LPGEN("Customize"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CGenMenuOptionsPage(); - g_plugin.addOptions(wParam, &odp); - - return ProtocolOrderOptInit(wParam, 0); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "genmenu.h" +#include "plugins.h" + +#define STR_SEPARATOR L"-----------------------------------" + +extern bool bIconsDisabled; +extern int DefaultImageListColorDepth; +void RebuildProtoMenus(); + +MIR_APP_DLL(void) Menu_SetVisible(TMO_IntMenuItem *pimi, bool bVisible) +{ + if ((pimi = MO_GetIntMenuItem(pimi)) == nullptr) + return; + + char szModule[256], menuItemName[256]; + mir_snprintf(szModule, "%s_Items", pimi->parent->pszName); + bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); + + ptrW wszValue(db_get_wsa(0, szModule, menuItemName, L"1;;;")); + wszValue[0] = bVisible ? '1' : '0'; + db_set_ws(0, szModule, menuItemName, wszValue); + + Menu_ShowItem(pimi, bVisible); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct MenuItemOptData : public MZeroedObject +{ + ~MenuItemOptData() {} + + int pos; + + ptrW name; + ptrW defname; + + bool bShow; + int id; + + TMO_IntMenuItem *pimi; +}; + +static int SortMenuItems(const MenuItemOptData *p1, const MenuItemOptData *p2) +{ + if (p1->pos < p2->pos) return -1; + if (p1->pos > p2->pos) return 1; + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class CGenMenuOptionsPage : public CDlgBase +{ + int iInitMenuValue; + LIST m_arDeleted; + + wchar_t idstr[100]; + + void SaveTreeInternal(MenuItemOptData *pParent, HTREEITEM hRootItem, const char *szModule) + { + TVITEMEX tvi; + tvi.hItem = hRootItem; + tvi.cchTextMax = _countof(idstr); + tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE | TVIF_IMAGE; + tvi.pszText = idstr; + + int count = 0, customOrder = 0; + int runtimepos = 100; + + char pszParent[33]; + if (pParent == nullptr) + pszParent[0] = 0; + else + bin2hex(&pParent->pimi->mi.uid, sizeof(MUUID), pszParent); + + while (tvi.hItem != nullptr) { + m_menuItems.GetItem(&tvi); + auto *iod = (MenuItemOptData*)tvi.lParam; + if (TMO_IntMenuItem *pimi = iod->pimi) { + if (pimi->mi.uid != miid_last) { + char menuItemName[256]; + bin2hex(&pimi->mi.uid, sizeof(pimi->mi.uid), menuItemName); + + int visible = tvi.iImage != 0; + wchar_t *ptszCustomName; + if (iod->name != nullptr && iod->defname != nullptr && mir_wstrcmp(iod->name, iod->defname) != 0) + ptszCustomName = iod->name; + else + ptszCustomName = L""; + + CMStringW tszValue(FORMAT, L"%d;%d;%S;%s", visible, runtimepos, pszParent, ptszCustomName); + db_set_ws(0, szModule, menuItemName, tszValue); + + if (pimi->mi.flags & CMIF_CUSTOM) + db_set_s(0, szModule, CMStringA(FORMAT, "Custom%d", customOrder++), menuItemName); + } + + HTREEITEM hChild = m_menuItems.GetChild(tvi.hItem); + if (hChild != nullptr) + SaveTreeInternal(iod, hChild, szModule); + + runtimepos += 100; + } + + if (iod->name && !mir_wstrcmp(iod->name, STR_SEPARATOR) && tvi.iImage) + runtimepos += SEPARATORPOSITIONINTERVAL; + + tvi.hItem = m_menuItems.GetNextSibling(tvi.hItem); + count++; + } + } + + void SaveTree() + { + int MenuObjectId; + if (!GetCurrentMenuObjectID(MenuObjectId)) + return; + + TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); + if (pmo == nullptr) + return; + + char szModule[256]; + mir_snprintf(szModule, "%s_Items", pmo->pszName); + db_delete_module(0, szModule); + SaveTreeInternal(nullptr, m_menuItems.GetRoot(), szModule); + db_set_b(0, szModule, "MenuFormat", 1); + } + + void FreeTreeData() + { + HTREEITEM hItem = m_menuItems.GetRoot(); + while (hItem != nullptr) { + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hItem; + m_menuItems.GetItem(&tvi); + delete (MenuItemOptData *)tvi.lParam; + + tvi.lParam = 0; + m_menuItems.SetItem(&tvi); + + hItem = m_menuItems.GetNextSibling(hItem); + } + } + + void RebuildCurrent() + { + int MenuObjectID; + if (GetCurrentMenuObjectID(MenuObjectID)) + BuildTree(MenuObjectID, true); + } + + void BuildTreeInternal(const char *pszModule, bool bReread, TMO_IntMenuItem *pFirst, HTREEITEM hRoot) + { + LIST arItems(10, SortMenuItems); + + for (TMO_IntMenuItem *p = pFirst; p != nullptr; p = p->next) { + // filter out items whose presence & position might not be changed + if (p->mi.flags & CMIF_SYSTEM) + continue; + + MenuItemOptData *PD = new MenuItemOptData(); + PD->pimi = p; + PD->defname = mir_wstrdup(GetMenuItemText(p)); + PD->name = mir_wstrdup((bReread && p->ptszCustomName != nullptr) ? p->ptszCustomName : PD->defname); + PD->bShow = (p->mi.flags & CMIF_HIDDEN) == 0; + PD->pos = (bReread) ? p->mi.position : p->originalPosition; + PD->id = p->iCommand; + arItems.insert(PD); + } + + int lastpos = 0; + bool bIsFirst = TRUE; + + TVINSERTSTRUCT tvis; + tvis.hParent = hRoot; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + + for (auto &it : arItems) { + if (it != arItems[0] && it->pos - lastpos >= SEPARATORPOSITIONINTERVAL) { + MenuItemOptData *sep = new MenuItemOptData(); + sep->id = -1; + sep->name = mir_wstrdup(STR_SEPARATOR); + sep->pos = it->pos - 1; + + tvis.item.lParam = (LPARAM)sep; + tvis.item.pszText = sep->name; + tvis.item.iImage = tvis.item.iSelectedImage = 1; + tvis.item.cChildren = 0; + m_menuItems.InsertItem(&tvis); + } + + tvis.item.lParam = (LPARAM)it; + tvis.item.pszText = it->name; + tvis.item.iImage = tvis.item.iSelectedImage = it->bShow; + tvis.item.cChildren = it->pimi->submenu.first != nullptr; + + HTREEITEM hti = m_menuItems.InsertItem(&tvis); + if (bIsFirst) { + if (hRoot == nullptr) + m_menuItems.SelectItem(hti); + bIsFirst = false; + } + + if (it->pimi->submenu.first != nullptr) { + BuildTreeInternal(pszModule, bReread, it->pimi->submenu.first, hti); + m_menuItems.Expand(hti, TVE_EXPAND); + } + + lastpos = it->pos; + } + } + + bool BuildTree(int MenuObjectId, bool bReread) + { + FreeTreeData(); + + TIntMenuObject *pmo = GetMenuObjbyId(MenuObjectId); + if (pmo == nullptr || pmo->m_items.first == nullptr) + return false; + + char szModule[256]; + mir_snprintf(szModule, "%s_Items", pmo->pszName); + + if (bReread) // no need to reread database on reset + MO_RecursiveWalkMenu(pmo->m_items.first, Menu_LoadFromDatabase, szModule); + + m_menuItems.SetDraw(false); + m_menuItems.DeleteAllItems(); + + BuildTreeInternal(szModule, bReread, pmo->m_items.first, nullptr); + + m_menuItems.SetDraw(true); + + m_warning.Show(!pmo->m_bUseUserDefinedItems); + m_menuItems.Enable(pmo->m_bUseUserDefinedItems); + m_btnInsSeparator.Enable(pmo->m_bUseUserDefinedItems); + m_btnInsMenu.Enable(pmo->m_bUseUserDefinedItems); + return 1; + } + + bool GetCurrentMenuObjectID(int &result) + { + int iItem = m_menuObjects.GetCurSel(); + if (iItem == -1) + return false; + + result = (int)m_menuObjects.GetItemData(iItem); + return true; + } + + CCtrlListBox m_menuObjects; + CCtrlTreeView m_menuItems; + CCtrlCheck m_radio1, m_radio2, m_enableIcons; + CCtrlEdit m_customName, m_service, m_module; + CCtrlButton m_btnInsSeparator, m_btnInsMenu, m_btnReset, m_btnSet, m_btnDefault, m_btnDelete; + CCtrlBase m_warning; + +public: + CGenMenuOptionsPage() : + CDlgBase(g_plugin, IDD_OPT_GENMENU), + m_arDeleted(1), + m_menuItems(this, IDC_MENUITEMS), + m_menuObjects(this, IDC_MENUOBJECTS), + m_radio1(this, IDC_RADIO1), + m_radio2(this, IDC_RADIO2), + m_enableIcons(this, IDC_DISABLEMENUICONS), + m_btnInsSeparator(this, IDC_INSERTSEPARATOR), + m_btnInsMenu(this, IDC_INSERTSUBMENU), + m_btnReset(this, IDC_RESETMENU), + m_btnSet(this, IDC_GENMENU_SET), + m_btnDelete(this, IDC_GENMENU_DELETE), + m_btnDefault(this, IDC_GENMENU_DEFAULT), + m_customName(this, IDC_GENMENU_CUSTOMNAME), + m_service(this, IDC_GENMENU_SERVICE), + m_module(this, IDC_GENMENU_MODULE), + m_warning(this, IDC_NOTSUPPORTWARNING) + { + m_btnSet.OnClick = Callback(this, &CGenMenuOptionsPage::btnSet_Clicked); + m_btnReset.OnClick = Callback(this, &CGenMenuOptionsPage::btnReset_Clicked); + m_btnInsSeparator.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsSep_Clicked); + m_btnInsMenu.OnClick = Callback(this, &CGenMenuOptionsPage::btnInsMenu_Clicked); + m_btnDefault.OnClick = Callback(this, &CGenMenuOptionsPage::btnDefault_Clicked); + m_btnDelete.OnClick = Callback(this, &CGenMenuOptionsPage::btnDelete_Clicked); + + m_menuObjects.OnSelChange = Callback(this, &CGenMenuOptionsPage::onMenuObjectChanged); + + m_menuItems.SetFlags(MTREE_CHECKBOX | MTREE_DND); + m_menuItems.OnSelChanged = Callback(this, &CGenMenuOptionsPage::onMenuItemChanged); + m_menuItems.OnBeginDrag = Callback(this, &CGenMenuOptionsPage::onMenuItemBeginDrag); + + m_customName.SetSilent(); + m_service.SetSilent(); + m_module.SetSilent(); + } + + //---- init dialog ------------------------------------------- + bool OnInitDialog() override + { + iInitMenuValue = db_get_b(0, "CList", "MoveProtoMenus", TRUE); + + if (iInitMenuValue) + m_radio2.SetState(true); + else + m_radio1.SetState(true); + + m_enableIcons.SetState(!bIconsDisabled); + + //---- init menu object list -------------------------------------- + for (auto &p : g_menus) + if (p->id != (int)hStatusMenuObject && p->m_bUseUserDefinedItems) + m_menuObjects.AddString(TranslateW(p->ptszDisplayName), p->id); + + m_menuObjects.SetCurSel(0); + RebuildCurrent(); + return true; + } + + bool OnApply() override + { + bIconsDisabled = m_enableIcons.GetState() == 0; + db_set_b(0, "CList", "DisableMenuIcons", bIconsDisabled); + SaveTree(); + + for (auto &pimi : m_arDeleted) + Menu_RemoveItem(pimi); + + int iNewMenuValue = !m_radio1.GetState(); + if (iNewMenuValue != iInitMenuValue) { + db_set_b(0, "CList", "MoveProtoMenus", iNewMenuValue); + + RebuildProtoMenus(); + iInitMenuValue = iNewMenuValue; + } + RebuildCurrent(); + return true; + } + + void OnDestroy() override + { + FreeTreeData(); + } + + void btnInsSep_Clicked(CCtrlButton*) + { + HTREEITEM hti = m_menuItems.GetSelection(); + if (hti == nullptr) + return; + + TVITEMEX tvi = { 0 }; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; + tvi.hItem = hti; + if (!m_menuItems.GetItem(&tvi)) + return; + + MenuItemOptData *PD = new MenuItemOptData(); + PD->id = -1; + PD->name = mir_wstrdup(STR_SEPARATOR); + PD->pos = ((MenuItemOptData *)tvi.lParam)->pos - 1; + + TVINSERTSTRUCT tvis = {}; + tvis.item.lParam = (LPARAM)PD; + tvis.item.pszText = PD->name; + tvis.item.iImage = tvis.item.iSelectedImage = 1; + tvis.hInsertAfter = hti; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + m_menuItems.InsertItem(&tvis); + + NotifyChange(); + } + + void btnInsMenu_Clicked(CCtrlButton*) + { + HTREEITEM hti = m_menuItems.GetSelection(); + if (hti == nullptr) + return; + + TVITEMEX tvi = { 0 }; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_TEXT; + tvi.hItem = hti; + if (!m_menuItems.GetItem(&tvi)) + return; + + MenuItemOptData *curData = (MenuItemOptData*)tvi.lParam; + + TMO_MenuItem mi = {}; + UuidCreate((UUID*)&mi.uid); + mi.flags = CMIF_CUSTOM; + mi.name.a = LPGEN("New submenu"); + mi.position = curData->pos - 1; + TMO_IntMenuItem *pimi = Menu_AddItem(curData->pimi->parent->id, &mi, nullptr); + + MenuItemOptData *PD = new MenuItemOptData(); + PD->id = -1; + PD->name = mir_wstrdup(pimi->mi.name.w); + PD->pos = pimi->mi.position; + PD->pimi = pimi; + + TVINSERTSTRUCT tvis = {}; + tvis.item.lParam = (LPARAM)PD; + tvis.item.pszText = PD->name; + tvis.item.iImage = tvis.item.iSelectedImage = 1; + tvis.hInsertAfter = hti; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + m_menuItems.InsertItem(&tvis); + + NotifyChange(); + } + + void btnReset_Clicked(CCtrlButton*) + { + int MenuObjectID; + if (GetCurrentMenuObjectID(MenuObjectID)) { + BuildTree(MenuObjectID, false); + NotifyChange(); + } + } + + void btnDefault_Clicked(CCtrlButton*) + { + HTREEITEM hti = m_menuItems.GetSelection(); + if (hti == nullptr) + return; + + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; + tvi.hItem = hti; + m_menuItems.GetItem(&tvi); + + MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; + if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) + return; + + iod->name = mir_wstrdup(iod->defname); + m_customName.SetText(iod->defname); + + tvi.mask = TVIF_TEXT; + tvi.pszText = iod->name; + m_menuItems.SetItem(&tvi); + NotifyChange(); + } + + void btnSet_Clicked(CCtrlButton*) + { + HTREEITEM hti = m_menuItems.GetSelection(); + if (hti == nullptr) + return; + + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; + tvi.hItem = hti; + m_menuItems.GetItem(&tvi); + + MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; + if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) + return; + + iod->name = m_customName.GetText(); + + tvi.mask = TVIF_TEXT; + tvi.pszText = iod->name; + m_menuItems.SetItem(&tvi); + NotifyChange(); + } + + void btnDelete_Clicked(CCtrlButton *) + { + HTREEITEM hti = m_menuItems.GetSelection(); + if (hti == nullptr) + return; + + TVITEMEX tvi; + tvi.mask = TVIF_PARAM; + tvi.hItem = hti; + m_menuItems.GetItem(&tvi); + + MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; + if (!(iod->pimi->mi.flags & CMIF_CUSTOM)) + return; + + if (IDYES == MessageBoxW(m_hwnd, TranslateT("Do you really want to delete this menu item?"), TranslateT("Miranda"), MB_YESNO | MB_ICONQUESTION)) { + m_arDeleted.insert(iod->pimi); + m_menuItems.DeleteItem(hti); + delete iod; + NotifyChange(); + } + } + + void onMenuObjectChanged(void*) + { + m_bInitialized = false; + RebuildCurrent(); + m_bInitialized = true; + } + + void onMenuItemChanged(void*) + { + m_customName.SetTextA(""); + m_service.SetTextA(""); + m_module.SetTextA(""); + + m_btnInsMenu.Disable(); + m_btnDefault.Disable(); + m_btnSet.Disable(); + m_btnDelete.Disable(); + m_customName.Disable(); + + HTREEITEM hti = m_menuItems.GetSelection(); + if (hti == nullptr) + return; + + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; + tvi.hItem = hti; + m_menuItems.GetItem(&tvi); + if (tvi.lParam == 0) + return; + + MenuItemOptData *iod = (MenuItemOptData *)tvi.lParam; + if (iod->name && wcsstr(iod->name, STR_SEPARATOR)) + return; + + m_customName.SetText(iod->name); + + if (iod->pimi->mi.uid != miid_last) { + char szText[100]; + bin2hex(&iod->pimi->mi.uid, sizeof(iod->pimi->mi.uid), szText); + m_service.SetTextA(szText); + } + + const CMPluginBase *pPlugin = iod->pimi->mi.pPlugin; + m_module.SetTextA(pPlugin == nullptr ? "" : pPlugin->getInfo().shortName); + + m_btnInsMenu.Enable(iod->pimi->mi.root == nullptr); + m_btnDefault.Enable(mir_wstrcmp(iod->name, iod->defname) != 0); + m_btnDelete.Enable(iod->pimi->mi.flags & CMIF_CUSTOM); + m_btnSet.Enable(true); + m_customName.Enable(true); + } + + void onMenuItemBeginDrag(CCtrlTreeView::TEventInfo *evt) + { + MenuItemOptData *p = (MenuItemOptData*)evt->nmtv->itemNew.lParam; + if (p->pimi != nullptr) + if (p->pimi->mi.flags & CMIF_UNMOVABLE) + evt->nmhdr->code = 0; // reject an attempt to change item's position + } +}; + +int GenMenuOptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = -1000000000; + odp.szTitle.a = LPGEN("Menus"); + odp.szGroup.a = LPGEN("Customize"); + odp.flags = ODPF_BOLDGROUPS; + odp.pDialog = new CGenMenuOptionsPage(); + g_plugin.addOptions(wParam, &odp); + + return ProtocolOrderOptInit(wParam, 0); +} diff --git a/src/mir_app/src/menu_tray.cpp b/src/mir_app/src/menu_tray.cpp index 69446e1590..77b4a482b9 100644 --- a/src/mir_app/src/menu_tray.cpp +++ b/src/mir_app/src/menu_tray.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/menu_utils.cpp b/src/mir_app/src/menu_utils.cpp index 12df821386..ff048552de 100644 --- a/src/mir_app/src/menu_utils.cpp +++ b/src/mir_app/src/menu_utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/meta_addto.cpp b/src/mir_app/src/meta_addto.cpp index 7cf6b8d7ce..345598d429 100644 --- a/src/mir_app/src/meta_addto.cpp +++ b/src/mir_app/src/meta_addto.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_api.cpp b/src/mir_app/src/meta_api.cpp index 97da5e0acd..886e0be1c7 100644 --- a/src/mir_app/src/meta_api.cpp +++ b/src/mir_app/src/meta_api.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_edit.cpp b/src/mir_app/src/meta_edit.cpp index 9f57a4131f..ac7917ac77 100644 --- a/src/mir_app/src/meta_edit.cpp +++ b/src/mir_app/src/meta_edit.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_main.cpp b/src/mir_app/src/meta_main.cpp index 7ac06606b7..f59794693b 100644 --- a/src/mir_app/src/meta_main.cpp +++ b/src/mir_app/src/meta_main.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_menu.cpp b/src/mir_app/src/meta_menu.cpp index dc4ad1252f..1ae7ec2247 100644 --- a/src/mir_app/src/meta_menu.cpp +++ b/src/mir_app/src/meta_menu.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_options.cpp b/src/mir_app/src/meta_options.cpp index b6e3f81b7f..71f7c6a997 100644 --- a/src/mir_app/src/meta_options.cpp +++ b/src/mir_app/src/meta_options.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_services.cpp b/src/mir_app/src/meta_services.cpp index e8d5b042d6..74f218e3d1 100644 --- a/src/mir_app/src/meta_services.cpp +++ b/src/mir_app/src/meta_services.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/meta_utils.cpp b/src/mir_app/src/meta_utils.cpp index ad75ed4910..3db142ba98 100644 --- a/src/mir_app/src/meta_utils.cpp +++ b/src/mir_app/src/meta_utils.cpp @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/metacontacts.h b/src/mir_app/src/metacontacts.h index 49e86ef32c..47f8b5bab2 100644 --- a/src/mir_app/src/metacontacts.h +++ b/src/mir_app/src/metacontacts.h @@ -1,7 +1,7 @@ /* former MetaContacts Plugin for Miranda IM. -Copyright © 2014-22 Miranda NG team +Copyright © 2014-23 Miranda NG team Copyright © 2004-07 Scott Ellis Copyright © 2004 Universite Louis PASTEUR, STRASBOURG. diff --git a/src/mir_app/src/miranda.cpp b/src/mir_app/src/miranda.cpp index 7628da2e66..850e769535 100644 --- a/src/mir_app/src/miranda.cpp +++ b/src/mir_app/src/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/miranda.h b/src/mir_app/src/miranda.h index b3dd931124..b0508c18f3 100644 --- a/src/mir_app/src/miranda.h +++ b/src/mir_app/src/miranda.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/modules.cpp b/src/mir_app/src/modules.cpp index 2274247cc2..ff0b6682f2 100644 --- a/src/mir_app/src/modules.cpp +++ b/src/mir_app/src/modules.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/movetogroup.cpp b/src/mir_app/src/movetogroup.cpp index 4f810c4002..3eb84c9e87 100644 --- a/src/mir_app/src/movetogroup.cpp +++ b/src/mir_app/src/movetogroup.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/netlib.cpp b/src/mir_app/src/netlib.cpp index 68c2d6e3d0..2914620911 100644 --- a/src/mir_app/src/netlib.cpp +++ b/src/mir_app/src/netlib.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/netlib.h b/src/mir_app/src/netlib.h index 4b85f6c365..09480637bf 100644 --- a/src/mir_app/src/netlib.h +++ b/src/mir_app/src/netlib.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/netlib_autoproxy.cpp b/src/mir_app/src/netlib_autoproxy.cpp index cc2ef2b9e1..f325f3a721 100644 --- a/src/mir_app/src/netlib_autoproxy.cpp +++ b/src/mir_app/src/netlib_autoproxy.cpp @@ -1,365 +1,365 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include - -///////////////////////////////////////////////////////////////////////////////////////// -// local module data - -static char *szProxyHost[3]; -static LIST proxyBypass(5); - -static HMODULE hModJS; - -static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll; -static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll; -static pfnInternetGetProxyInfo pInternetGetProxyInfo; - -static bool bEnabled, bOneProxy; - -///////////////////////////////////////////////////////////////////////////////////////// - -static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf) -{ - NetlibUser nlu = {}; - nlu.handleType = NLH_USER; - nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS; - nlu.user.szSettingsModule = "(NULL)"; - nlu.toLog = 1; - - // initialize the netlib request - NETLIBHTTPREQUEST nlhr = {}; - nlhr.cbSize = sizeof(nlhr); - nlhr.requestType = REQUEST_GET; - nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT; - nlhr.szUrl = szUrl; - - // download the page - NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr)); - if (nlhrReply) { - if (nlhrReply->resultCode == 200) { - buf.lpszScriptBuffer = nlhrReply->pData; - buf.dwScriptBufferSize = nlhrReply->dataLength + 1; - - nlhrReply->dataLength = 0; - nlhrReply->pData = nullptr; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps) -{ - bool noHttp = false; - bool usingSsl = false; - char szUrl[1024]; - - if ((nlc->url.flags & NLOCF_HTTP) && (nlc->url.flags & NLOCF_SSL) || nlc->url.port == 443 || forceHttps) { - mir_snprintf(szUrl, "https://%s", nlc->url.szHost.c_str()); - usingSsl = true; - } - else if ((nlc->url.flags & NLOCF_HTTP)) - mir_snprintf(szUrl, "http://%s", nlc->url.szHost.c_str()); - else { - strncpy_s(szUrl, nlc->url.szHost, _TRUNCATE); - noHttp = true; - } - - mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr; - nlc->wProxyPort = 0; - nlc->proxyType = 0; - - char *mt = NetlibGetIeProxy(szUrl); - char *m = NEWSTR_ALLOCA(mt); - mir_free(mt); - - if (m == nullptr) - return false; - - // if multiple servers, use the first one - char *c = strchr(m, ';'); if (c) *c = 0; - - // if 'direct' no proxy - if (_stricmp(lrtrim(m), "direct") == 0) - return false; - - // find proxy address - char *h = strchr(m, ' '); - if (h == nullptr) - return false; - - // find proxy port - *h = 0; ++h; - char *p = strchr(h, ':'); - if (p) { *p = 0; ++p; } - - lrtrim(h); ltrim(p); - if (_stricmp(m, "proxy") == 0 && h[0]) { - nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP; - nlc->wProxyPort = p ? atol(p) : 8080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS4; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else if (_stricmp(m, "socks5") == 0 && h[0]) { - nlc->proxyType = PROXYTYPE_SOCKS5; - nlc->wProxyPort = p ? atol(p) : 1080; - nlc->szProxyServer = mir_strdup(h); - } - else return false; - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szAutoUrlStr[MAX_PATH] = ""; -static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 }; -static HANDLE hIeProxyMutex; -static bool bAutoProxyInit; - -static void NetlibInitAutoProxy(void) -{ - if (bAutoProxyInit) return; - - if (!hModJS) { - if (!(hModJS = LoadLibraryA("jsproxy.dll"))) - return; - - pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll"); - pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"); - pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo"); - } - - if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) { - abuf.dwStructSize = sizeof(abuf); - GetFile(szAutoUrlStr, abuf); - } - bAutoProxyInit = true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct IeProxyParam -{ - char *szUrl; - char *szHost; - char *szProxy; -}; - -static void __cdecl NetlibIeProxyThread(IeProxyParam *param) -{ - param->szProxy = nullptr; - - if (!bAutoProxyInit) { - WaitForSingleObject(hIeProxyMutex, INFINITE); - NetlibInitAutoProxy(); - ReleaseMutex(hIeProxyMutex); - } - - BOOL res; - char *loc = strstr(szAutoUrlStr, "file://"); - if (loc || strstr(szAutoUrlStr, "://") == nullptr) { - Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc); - loc = loc ? loc + 7 : szAutoUrlStr; - res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr); - } - else { - Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize); - if (abuf.dwScriptBufferSize) - res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf); - else - res = false; - } - - if (res) { - char proxyBuffer[1024]; - char *proxy = proxyBuffer; - DWORD dwProxyLen = sizeof(proxyBuffer); - - if (pInternetGetProxyInfo(param->szUrl, (uint32_t)mir_strlen(param->szUrl), - param->szHost, (uint32_t)mir_strlen(param->szHost), &proxy, &dwProxyLen)) - param->szProxy = mir_strdup(lrtrim(proxy)); - - Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost); - pInternetDeInitializeAutoProxyDll(nullptr, 0); - } - else Netlib_Logf(nullptr, "Autoproxy init failed"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NetlibGetIeProxy(char *szUrl) -{ - char *res = nullptr, *szHost; - { - char* p = strstr(szUrl, "://"); - if (p) p += 3; else p = szUrl; - - szHost = NEWSTR_ALLOCA(p); - p = strchr(szHost, '/'); if (p) *p = 0; - p = strchr(szHost, ':'); if (p) *p = 0; - _strlwr(szHost); - } - - if (bEnabled) { - for (auto &p : proxyBypass) { - if (mir_strcmp(p, "") == 0) { - if (strchr(szHost, '.') == nullptr) - return nullptr; - } - else if (wildcmp(szHost, p)) - return nullptr; - } - - int ind = -1; - if (strstr(szUrl, "http://")) - ind = szProxyHost[0] ? 0 : 2; - else if (strstr(szUrl, "https://")) - ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2); - else - ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2)); - - if (ind < 0 || !szProxyHost[ind]) - return nullptr; - - size_t len = mir_strlen(szHost) + 20; - res = (char*)mir_alloc(len); - mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]); - return res; - } - - if (szAutoUrlStr[0]) { - IeProxyParam param = { szUrl, szHost, nullptr }; - HANDLE hThread = mir_forkThread(NetlibIeProxyThread, ¶m); - WaitForSingleObject(hThread, INFINITE); - res = param.szProxy; - } - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLoadIeProxy(void) -{ - HKEY hSettings; - if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings)) - return; - - DWORD tValueLen, enabled = 0; - char szHostStr[256] = "", szProxyBypassStr[4096] = ""; - - tValueLen = sizeof(enabled); - int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (uint8_t*)&enabled, &tValueLen); - bEnabled = enabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szHostStr); - tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (uint8_t*)szHostStr, &tValueLen); - bEnabled = bEnabled && tResult == ERROR_SUCCESS; - - tValueLen = _countof(szAutoUrlStr); - RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (uint8_t*)szAutoUrlStr, &tValueLen); - - tValueLen = _countof(szProxyBypassStr); - RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (uint8_t*)szProxyBypassStr, &tValueLen); - - RegCloseKey(hSettings); - - if (bEnabled) { - char *szProxy = ltrim(szHostStr); - if (szProxy[0] == 0) { - enabled = false; - return; - } - - while (true) { - char *szProxyEnd = strchr(szProxy, ';'); - if (szProxyEnd) - *szProxyEnd = 0; - - int ind = -1; - if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; } - else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; } - else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; } - else if (strchr(szProxy, '=')) ind = -2; - - if (ind != -2) { - bOneProxy = ind < 0; if (ind < 0) ind = 0; - - lrtrim(szProxy); - - if (strchr(szProxy, ':')) - szProxyHost[ind] = mir_strdup(szProxy); - else { - size_t len = mir_strlen(szProxy) + 10; - szProxyHost[ind] = (char*)mir_alloc(len); - mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080); - } - if (bOneProxy) - break; - } - if (szProxyEnd == nullptr) - break; - szProxy = szProxyEnd + 1; - } - - char *szProxyBypass = szProxyBypassStr; - while (true) { - char *szProxyBypassEnd = strchr(szProxyBypass, ';'); - if (szProxyBypassEnd) - *szProxyBypassEnd = 0; - - lrtrim(szProxyBypass); - - proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass))); - if (szProxyBypassEnd == nullptr) - break; - - szProxyBypass = szProxyBypassEnd + 1; - } - } - - if (bEnabled || szAutoUrlStr[0]) - hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUnloadIeProxy(void) -{ - for (int i = 0; i < 3; i++) - mir_free(szProxyHost[i]); - - for (auto &p : proxyBypass) - mir_free(p); - - mir_free(abuf.lpszScriptBuffer); - - CloseHandle(hIeProxyMutex); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +// local module data + +static char *szProxyHost[3]; +static LIST proxyBypass(5); + +static HMODULE hModJS; + +static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll; +static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll; +static pfnInternetGetProxyInfo pInternetGetProxyInfo; + +static bool bEnabled, bOneProxy; + +///////////////////////////////////////////////////////////////////////////////////////// + +static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf) +{ + NetlibUser nlu = {}; + nlu.handleType = NLH_USER; + nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS; + nlu.user.szSettingsModule = "(NULL)"; + nlu.toLog = 1; + + // initialize the netlib request + NETLIBHTTPREQUEST nlhr = {}; + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT; + nlhr.szUrl = szUrl; + + // download the page + NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr)); + if (nlhrReply) { + if (nlhrReply->resultCode == 200) { + buf.lpszScriptBuffer = nlhrReply->pData; + buf.dwScriptBufferSize = nlhrReply->dataLength + 1; + + nlhrReply->dataLength = 0; + nlhrReply->pData = nullptr; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps) +{ + bool noHttp = false; + bool usingSsl = false; + char szUrl[1024]; + + if ((nlc->url.flags & NLOCF_HTTP) && (nlc->url.flags & NLOCF_SSL) || nlc->url.port == 443 || forceHttps) { + mir_snprintf(szUrl, "https://%s", nlc->url.szHost.c_str()); + usingSsl = true; + } + else if ((nlc->url.flags & NLOCF_HTTP)) + mir_snprintf(szUrl, "http://%s", nlc->url.szHost.c_str()); + else { + strncpy_s(szUrl, nlc->url.szHost, _TRUNCATE); + noHttp = true; + } + + mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr; + nlc->wProxyPort = 0; + nlc->proxyType = 0; + + char *mt = NetlibGetIeProxy(szUrl); + char *m = NEWSTR_ALLOCA(mt); + mir_free(mt); + + if (m == nullptr) + return false; + + // if multiple servers, use the first one + char *c = strchr(m, ';'); if (c) *c = 0; + + // if 'direct' no proxy + if (_stricmp(lrtrim(m), "direct") == 0) + return false; + + // find proxy address + char *h = strchr(m, ' '); + if (h == nullptr) + return false; + + // find proxy port + *h = 0; ++h; + char *p = strchr(h, ':'); + if (p) { *p = 0; ++p; } + + lrtrim(h); ltrim(p); + if (_stricmp(m, "proxy") == 0 && h[0]) { + nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP; + nlc->wProxyPort = p ? atol(p) : 8080; + nlc->szProxyServer = mir_strdup(h); + } + else if (_stricmp(m, "socks") == 0 && h[0]) { + nlc->proxyType = PROXYTYPE_SOCKS4; + nlc->wProxyPort = p ? atol(p) : 1080; + nlc->szProxyServer = mir_strdup(h); + } + else if (_stricmp(m, "socks5") == 0 && h[0]) { + nlc->proxyType = PROXYTYPE_SOCKS5; + nlc->wProxyPort = p ? atol(p) : 1080; + nlc->szProxyServer = mir_strdup(h); + } + else return false; + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static char szAutoUrlStr[MAX_PATH] = ""; +static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 }; +static HANDLE hIeProxyMutex; +static bool bAutoProxyInit; + +static void NetlibInitAutoProxy(void) +{ + if (bAutoProxyInit) return; + + if (!hModJS) { + if (!(hModJS = LoadLibraryA("jsproxy.dll"))) + return; + + pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll"); + pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"); + pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo"); + } + + if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) { + abuf.dwStructSize = sizeof(abuf); + GetFile(szAutoUrlStr, abuf); + } + bAutoProxyInit = true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct IeProxyParam +{ + char *szUrl; + char *szHost; + char *szProxy; +}; + +static void __cdecl NetlibIeProxyThread(IeProxyParam *param) +{ + param->szProxy = nullptr; + + if (!bAutoProxyInit) { + WaitForSingleObject(hIeProxyMutex, INFINITE); + NetlibInitAutoProxy(); + ReleaseMutex(hIeProxyMutex); + } + + BOOL res; + char *loc = strstr(szAutoUrlStr, "file://"); + if (loc || strstr(szAutoUrlStr, "://") == nullptr) { + Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc); + loc = loc ? loc + 7 : szAutoUrlStr; + res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr); + } + else { + Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize); + if (abuf.dwScriptBufferSize) + res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf); + else + res = false; + } + + if (res) { + char proxyBuffer[1024]; + char *proxy = proxyBuffer; + DWORD dwProxyLen = sizeof(proxyBuffer); + + if (pInternetGetProxyInfo(param->szUrl, (uint32_t)mir_strlen(param->szUrl), + param->szHost, (uint32_t)mir_strlen(param->szHost), &proxy, &dwProxyLen)) + param->szProxy = mir_strdup(lrtrim(proxy)); + + Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost); + pInternetDeInitializeAutoProxyDll(nullptr, 0); + } + else Netlib_Logf(nullptr, "Autoproxy init failed"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* NetlibGetIeProxy(char *szUrl) +{ + char *res = nullptr, *szHost; + { + char* p = strstr(szUrl, "://"); + if (p) p += 3; else p = szUrl; + + szHost = NEWSTR_ALLOCA(p); + p = strchr(szHost, '/'); if (p) *p = 0; + p = strchr(szHost, ':'); if (p) *p = 0; + _strlwr(szHost); + } + + if (bEnabled) { + for (auto &p : proxyBypass) { + if (mir_strcmp(p, "") == 0) { + if (strchr(szHost, '.') == nullptr) + return nullptr; + } + else if (wildcmp(szHost, p)) + return nullptr; + } + + int ind = -1; + if (strstr(szUrl, "http://")) + ind = szProxyHost[0] ? 0 : 2; + else if (strstr(szUrl, "https://")) + ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2); + else + ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2)); + + if (ind < 0 || !szProxyHost[ind]) + return nullptr; + + size_t len = mir_strlen(szHost) + 20; + res = (char*)mir_alloc(len); + mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]); + return res; + } + + if (szAutoUrlStr[0]) { + IeProxyParam param = { szUrl, szHost, nullptr }; + HANDLE hThread = mir_forkThread(NetlibIeProxyThread, ¶m); + WaitForSingleObject(hThread, INFINITE); + res = param.szProxy; + } + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void NetlibLoadIeProxy(void) +{ + HKEY hSettings; + if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings)) + return; + + DWORD tValueLen, enabled = 0; + char szHostStr[256] = "", szProxyBypassStr[4096] = ""; + + tValueLen = sizeof(enabled); + int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (uint8_t*)&enabled, &tValueLen); + bEnabled = enabled && tResult == ERROR_SUCCESS; + + tValueLen = _countof(szHostStr); + tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (uint8_t*)szHostStr, &tValueLen); + bEnabled = bEnabled && tResult == ERROR_SUCCESS; + + tValueLen = _countof(szAutoUrlStr); + RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (uint8_t*)szAutoUrlStr, &tValueLen); + + tValueLen = _countof(szProxyBypassStr); + RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (uint8_t*)szProxyBypassStr, &tValueLen); + + RegCloseKey(hSettings); + + if (bEnabled) { + char *szProxy = ltrim(szHostStr); + if (szProxy[0] == 0) { + enabled = false; + return; + } + + while (true) { + char *szProxyEnd = strchr(szProxy, ';'); + if (szProxyEnd) + *szProxyEnd = 0; + + int ind = -1; + if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; } + else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; } + else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; } + else if (strchr(szProxy, '=')) ind = -2; + + if (ind != -2) { + bOneProxy = ind < 0; if (ind < 0) ind = 0; + + lrtrim(szProxy); + + if (strchr(szProxy, ':')) + szProxyHost[ind] = mir_strdup(szProxy); + else { + size_t len = mir_strlen(szProxy) + 10; + szProxyHost[ind] = (char*)mir_alloc(len); + mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080); + } + if (bOneProxy) + break; + } + if (szProxyEnd == nullptr) + break; + szProxy = szProxyEnd + 1; + } + + char *szProxyBypass = szProxyBypassStr; + while (true) { + char *szProxyBypassEnd = strchr(szProxyBypass, ';'); + if (szProxyBypassEnd) + *szProxyBypassEnd = 0; + + lrtrim(szProxyBypass); + + proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass))); + if (szProxyBypassEnd == nullptr) + break; + + szProxyBypass = szProxyBypassEnd + 1; + } + } + + if (bEnabled || szAutoUrlStr[0]) + hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr); +} + +void NetlibUnloadIeProxy(void) +{ + for (int i = 0; i < 3; i++) + mir_free(szProxyHost[i]); + + for (auto &p : proxyBypass) + mir_free(p); + + mir_free(abuf.lpszScriptBuffer); + + CloseHandle(hIeProxyMutex); +} diff --git a/src/mir_app/src/netlib_bind.cpp b/src/mir_app/src/netlib_bind.cpp index 53aad20fa3..cd9c955a75 100644 --- a/src/mir_app/src/netlib_bind.cpp +++ b/src/mir_app/src/netlib_bind.cpp @@ -1,330 +1,330 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) -{ - SOCKADDR_IN sin = {0}; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = {0}; - sin6.sin6_family = AF_INET6; - - mir_cslock lck(csNetlibUser); - - if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { - BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); - if (*portn == 0) - return false; - - uint16_t num; - Utils_GetRandom(&num, sizeof(uint16_t)); - *portn = num % *portn; - } - - bool before = false; - while (true) { - const char *psz; - char *pszEnd; - int portMin, portMax, port, portnum = 0; - - for (psz = szPorts;*psz;) { - while (*psz == ' ' || *psz == ',') psz++; - portMin = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) - break; - while (*pszEnd == ' ') - pszEnd++; - if (*pszEnd == '-') { - psz = pszEnd + 1; - portMax = strtol(psz, &pszEnd, 0); - if (pszEnd == psz) portMax = 65535; - if (portMin > portMax) { - port = portMin; - portMin = portMax; - portMax = port; - } - } - else portMax = portMin; - if (portMax >= 1) { - if (portMin <= 0) portMin = 1; - for (port = portMin; port <= portMax; port++) { - if (port > 65535) - break; - - ++portnum; - - if (s == INVALID_SOCKET) continue; - if (!before && portnum <= *portn) continue; - if (before && portnum >= *portn) - return false; - - sin.sin_port = htons((uint16_t)port); - bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; - - sin6.sin6_port = htons((uint16_t)port); - bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; - - if (bV4Mapped && bV6Mapped) { - *portn = portnum + 1; - return true; - } - } - } - psz = pszEnd; - } - - if (*portn < 0) { - *portn = portnum; - return true; - } - - if (*portn >= portnum) - *portn = 0; - else - before = true; - } -} - -int NetlibFreeBoundPort(NetlibBoundPort *nlbp) -{ - NETLIBCONNECTIONEVENTINFO ncei; - - ZeroMemory(&ncei, sizeof(ncei)); - ncei.connected = 0; - ncei.listening = 1; - ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size); - NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0); - - nlbp->close(); - if (nlbp->hThread) - WaitForSingleObject(nlbp->hThread, INFINITE); - Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); - delete nlbp; - return 1; -} - -static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp) -{ - Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort); - - while (true) { - fd_set r; - FD_ZERO(&r); - if (nlbp->s != INVALID_SOCKET) - FD_SET(nlbp->s, &r); - if (nlbp->s6 != INVALID_SOCKET) - FD_SET(nlbp->s6, &r); - if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - - sockaddr_in sin; - int sinLen = sizeof(sin); - memset(&sin, 0, sizeof(sin)); - - SOCKET s; - if (FD_ISSET(nlbp->s, &r)) { - s = accept(nlbp->s, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else if (FD_ISSET(nlbp->s6, &r)) { - s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen); - if (s == INVALID_SOCKET) { - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError()); - break; - } - } - else s = 0; - - Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlbp->nlu; - nlc->s = s; - - if (nlbp->pfnNewConnection) - nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); - } - - NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); - nlbp->hThread = nullptr; - - Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort); -} - -MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb); - if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError()); -LBL_Error: - delete nlbp; - return nullptr; - } - - SOCKADDR_IN sin = { 0 }; - sin.sin_family = AF_INET; - - SOCKADDR_IN6 sin6 = { 0 }; - sin6.sin6_family = AF_INET6; - - /* if the netlib user wanted a free port given in the range, then - they better have given wPort == 0, let's hope so */ - int foundPort = 0; - if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { - if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { - Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); - SetLastError(WSAEADDRINUSE); - } - else foundPort = 1; - } - else { - /* if ->wPort == 0 then they'll get any free port, otherwise they'll - be asking for whatever was in nlb->wPort*/ - if (nlb->wPort != 0) { - Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort); - sin.sin_port = htons(nlb->wPort); - sin6.sin6_port = htons(nlb->wPort); - } - - if (nlbp->s != INVALID_SOCKET) - if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) { - SOCKADDR_IN sin2 = { 0 }; - int len = sizeof(sin2); - if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len)) - sin6.sin6_port = sin2.sin_port; - foundPort = 1; - } - - if (nlbp->s6 != INVALID_SOCKET) - if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) - foundPort = 1; - } - if (!foundPort) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); - goto LBL_Error; - } - - SOCKADDR_INET_M sinm = { 0 }; - int len = sizeof(sinm); - if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) { - nlb->wPort = ntohs(sinm.Ipv4.sin_port); - nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr); - } - else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len)) - nlb->wPort = ntohs(sinm.Ipv6.sin6_port); - else { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError()); - goto LBL_Error; - } - nlbp->wPort = nlb->wPort; - - if (nlb->dwInternalIP == 0) { - char hostname[64] = ""; - gethostname(hostname, _countof(hostname)); - - PHOSTENT he = gethostbyname(hostname); - if (he && he->h_addr) - nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); - } - - uint32_t extIP; - if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) { - Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort); - nlb->wExPort = nlbp->wExPort; - nlb->dwExternalIP = extIP; - } - else { - if (nlu->settings.enableUPnP) - Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort); - else - Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort); - - nlbp->wExPort = 0; - nlb->wExPort = nlb->wPort; - nlb->dwExternalIP = nlb->dwInternalIP; - } - - nlbp->hThread = mir_forkThread(NetlibBindAcceptThread, nlbp); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.listening = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - memcpy(&ncei.local, &sin, sizeof(sin)); - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - return nlbp; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb) - : handleType(NLH_BOUNDPORT), - nlu(_nlu) -{ - pfnNewConnection = nlb->pfnNewConnection; - pExtra = nlb->pExtra; - - s = socket(PF_INET, SOCK_STREAM, 0); - s6 = socket(PF_INET6, SOCK_STREAM, 0); -} - -void NetlibBoundPort::close() -{ - if (s != INVALID_SOCKET) { - closesocket(s); - s = INVALID_SOCKET; - } - if (s6 != INVALID_SOCKET) { - closesocket(s6); - s6 = INVALID_SOCKET; - } -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn) +{ + SOCKADDR_IN sin = {0}; + sin.sin_family = AF_INET; + + SOCKADDR_IN6 sin6 = {0}; + sin6.sin6_family = AF_INET6; + + mir_cslock lck(csNetlibUser); + + if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) { + BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn); + if (*portn == 0) + return false; + + uint16_t num; + Utils_GetRandom(&num, sizeof(uint16_t)); + *portn = num % *portn; + } + + bool before = false; + while (true) { + const char *psz; + char *pszEnd; + int portMin, portMax, port, portnum = 0; + + for (psz = szPorts;*psz;) { + while (*psz == ' ' || *psz == ',') psz++; + portMin = strtol(psz, &pszEnd, 0); + if (pszEnd == psz) + break; + while (*pszEnd == ' ') + pszEnd++; + if (*pszEnd == '-') { + psz = pszEnd + 1; + portMax = strtol(psz, &pszEnd, 0); + if (pszEnd == psz) portMax = 65535; + if (portMin > portMax) { + port = portMin; + portMin = portMax; + portMax = port; + } + } + else portMax = portMin; + if (portMax >= 1) { + if (portMin <= 0) portMin = 1; + for (port = portMin; port <= portMax; port++) { + if (port > 65535) + break; + + ++portnum; + + if (s == INVALID_SOCKET) continue; + if (!before && portnum <= *portn) continue; + if (before && portnum >= *portn) + return false; + + sin.sin_port = htons((uint16_t)port); + bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0; + + sin6.sin6_port = htons((uint16_t)port); + bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0; + + if (bV4Mapped && bV6Mapped) { + *portn = portnum + 1; + return true; + } + } + } + psz = pszEnd; + } + + if (*portn < 0) { + *portn = portnum; + return true; + } + + if (*portn >= portnum) + *portn = 0; + else + before = true; + } +} + +int NetlibFreeBoundPort(NetlibBoundPort *nlbp) +{ + NETLIBCONNECTIONEVENTINFO ncei; + + ZeroMemory(&ncei, sizeof(ncei)); + ncei.connected = 0; + ncei.listening = 1; + ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule; + int size = sizeof(SOCKADDR_IN); + getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size); + NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0); + + nlbp->close(); + if (nlbp->hThread) + WaitForSingleObject(nlbp->hThread, INFINITE); + Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort); + delete nlbp; + return 1; +} + +static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp) +{ + Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort); + + while (true) { + fd_set r; + FD_ZERO(&r); + if (nlbp->s != INVALID_SOCKET) + FD_SET(nlbp->s, &r); + if (nlbp->s6 != INVALID_SOCKET) + FD_SET(nlbp->s6, &r); + if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) { + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError()); + break; + } + + sockaddr_in sin; + int sinLen = sizeof(sin); + memset(&sin, 0, sizeof(sin)); + + SOCKET s; + if (FD_ISSET(nlbp->s, &r)) { + s = accept(nlbp->s, (sockaddr*)&sin, &sinLen); + if (s == INVALID_SOCKET) { + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError()); + break; + } + } + else if (FD_ISSET(nlbp->s6, &r)) { + s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen); + if (s == INVALID_SOCKET) { + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError()); + break; + } + } + else s = 0; + + Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s); + + NetlibConnection *nlc = new NetlibConnection(); + nlc->nlu = nlbp->nlu; + nlc->s = s; + + if (nlbp->pfnNewConnection) + nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra); + } + + NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP"); + nlbp->hThread = nullptr; + + Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort); +} + +MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb) +{ + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb); + if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError()); +LBL_Error: + delete nlbp; + return nullptr; + } + + SOCKADDR_IN sin = { 0 }; + sin.sin_family = AF_INET; + + SOCKADDR_IN6 sin6 = { 0 }; + sin6.sin6_family = AF_INET6; + + /* if the netlib user wanted a free port given in the range, then + they better have given wPort == 0, let's hope so */ + int foundPort = 0; + if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) { + if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) { + Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified"); + SetLastError(WSAEADDRINUSE); + } + else foundPort = 1; + } + else { + /* if ->wPort == 0 then they'll get any free port, otherwise they'll + be asking for whatever was in nlb->wPort*/ + if (nlb->wPort != 0) { + Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort); + sin.sin_port = htons(nlb->wPort); + sin6.sin6_port = htons(nlb->wPort); + } + + if (nlbp->s != INVALID_SOCKET) + if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) { + SOCKADDR_IN sin2 = { 0 }; + int len = sizeof(sin2); + if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len)) + sin6.sin6_port = sin2.sin_port; + foundPort = 1; + } + + if (nlbp->s6 != INVALID_SOCKET) + if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0) + foundPort = 1; + } + if (!foundPort) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError()); + goto LBL_Error; + } + + if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); + goto LBL_Error; + } + + if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError()); + goto LBL_Error; + } + + SOCKADDR_INET_M sinm = { 0 }; + int len = sizeof(sinm); + if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) { + nlb->wPort = ntohs(sinm.Ipv4.sin_port); + nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr); + } + else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len)) + nlb->wPort = ntohs(sinm.Ipv6.sin6_port); + else { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError()); + goto LBL_Error; + } + nlbp->wPort = nlb->wPort; + + if (nlb->dwInternalIP == 0) { + char hostname[64] = ""; + gethostname(hostname, _countof(hostname)); + + PHOSTENT he = gethostbyname(hostname); + if (he && he->h_addr) + nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr); + } + + uint32_t extIP; + if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) { + Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort); + nlb->wExPort = nlbp->wExPort; + nlb->dwExternalIP = extIP; + } + else { + if (nlu->settings.enableUPnP) + Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort); + else + Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort); + + nlbp->wExPort = 0; + nlb->wExPort = nlb->wPort; + nlb->dwExternalIP = nlb->dwInternalIP; + } + + nlbp->hThread = mir_forkThread(NetlibBindAcceptThread, nlbp); + + if (GetSubscribersCount((THook*)hEventConnected)) { + NETLIBCONNECTIONEVENTINFO ncei = {}; + ncei.connected = 1; + ncei.listening = 1; + ncei.szSettingsModule = nlu->user.szSettingsModule; + memcpy(&ncei.local, &sin, sizeof(sin)); + NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); + } + + return nlbp; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb) + : handleType(NLH_BOUNDPORT), + nlu(_nlu) +{ + pfnNewConnection = nlb->pfnNewConnection; + pExtra = nlb->pExtra; + + s = socket(PF_INET, SOCK_STREAM, 0); + s6 = socket(PF_INET6, SOCK_STREAM, 0); +} + +void NetlibBoundPort::close() +{ + if (s != INVALID_SOCKET) { + closesocket(s); + s = INVALID_SOCKET; + } + if (s6 != INVALID_SOCKET) { + closesocket(s6); + s6 = INVALID_SOCKET; + } +} diff --git a/src/mir_app/src/netlib_http.cpp b/src/mir_app/src/netlib_http.cpp index 7c94db57ca..260d3a6199 100644 --- a/src/mir_app/src/netlib_http.cpp +++ b/src/mir_app/src/netlib_http.cpp @@ -1,1150 +1,1150 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "../libs/zlib/src/zlib.h" -#include "netlib.h" - -#define HTTPRECVHEADERSTIMEOUT 30000 //in ms -#define HTTPRECVDATATIMEOUT 20000 - -struct ProxyAuth -{ - char *szServer; - char *szMethod; - - ProxyAuth(const char *pszServer, const char *pszMethod) - { - szServer = mir_strdup(pszServer); - szMethod = mir_strdup(pszMethod); - } - ~ProxyAuth() - { - mir_free(szServer); - mir_free(szMethod); - } - static int Compare(const ProxyAuth *p1, const ProxyAuth *p2) - { - return mir_strcmpi(p1->szServer, p2->szServer); - } -}; - -struct ProxyAuthList : OBJLIST -{ - ProxyAuthList() : OBJLIST(2, ProxyAuth::Compare) {} - - void add(const char *szServer, const char *szMethod) - { - if (szServer == nullptr) return; - int i = getIndex((ProxyAuth*)&szServer); - if (i >= 0) { - ProxyAuth &rec = (*this)[i]; - if (szMethod == nullptr) - remove(i); - else if (_stricmp(rec.szMethod, szMethod)) { - mir_free(rec.szMethod); - rec.szMethod = mir_strdup(szMethod); - } - } - else insert(new ProxyAuth(szServer, szMethod)); - } - - const char* find(const char *szServer) - { - ProxyAuth *rec = szServer ? OBJLIST::find((ProxyAuth*)&szServer) : nullptr; - return rec ? rec->szMethod : nullptr; - } -}; - -ProxyAuthList proxyAuthList; - -static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags) -{ - int dwTimeNow; - - if (nlc->foreBuf.isEmpty() && !Netlib_SslPending(nlc->hSsl)) { - while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) { - int dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000); - int res = WaitUntilReadable(nlc->s, dwDeltaTime); - - switch (res) { - case SOCKET_ERROR: - return SOCKET_ERROR; - - case 1: - return Netlib_Recv(nlc, buf, len, flags); - } - - if (nlc->termRequested || Miranda_IsTerminated()) - return 0; - } - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - return Netlib_Recv(nlc, buf, len, flags); -} - -MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr) -{ - if (nlhr == nullptr || hdr == nullptr) - return nullptr; - - for (int i=0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (_stricmp(p.szName, hdr) == 0) - return p.szValue; - } - - return nullptr; -} - -static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider) -{ - char *szBasicHdr = nullptr; - char *szNegoHdr = nullptr; - char *szNtlmHdr = nullptr; - - for (int i=0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (_stricmp(p.szName, hdr) == 0) { - if (_strnicmp(p.szValue, "Negotiate", 9) == 0) - szNegoHdr = p.szValue; - else if (_strnicmp(p.szValue, "NTLM", 4) == 0) - szNtlmHdr = p.szValue; - else if (_strnicmp(p.szValue, "Basic", 5) == 0) - szBasicHdr = p.szValue; - } - } - - if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr; - if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr; - if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr; - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url) -{ - secur = secur || _strnicmp(szUrl, "https", 5) == 0; - - const char* phost = strstr(szUrl, "://"); - url.szHost = phost ? phost + 3 : szUrl; - - int idx = url.szHost.Find('/'); - if (idx != -1) - url.szHost.Truncate(idx); - - if ((idx = url.szHost.Find(':')) != -1) { - url.port = strtol(url.szHost.c_str() + idx + 1, nullptr, 10); - url.szHost.Truncate(idx); - } - else url.port = secur ? 443 : 80; - url.flags = (secur ? NLOCF_SSL : 0); -} - -static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr) -{ - NetlibUrl url; - - if (szUrl == nullptr) - NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, url); - else - NetlibConnFromUrl(szUrl, false, url); - - url.flags |= NLOCF_HTTP; - if (url.flags & NLOCF_SSL) - nlhr->flags |= NLHRF_SSL; - else - nlhr->flags &= ~NLHRF_SSL; - - if (nlc != nullptr) { - bool httpProxy = !(url.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP; - bool sameHost = mir_strcmp(nlc->url.szHost, url.szHost) == 0 && nlc->url.port == url.port; - - if (!httpProxy && !sameHost) { - NetlibDoCloseSocket(nlc); - - nlc->url = url; - return NetlibDoConnect(nlc) ? nlc : nullptr; - } - } - else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, url.szHost, url.port, 0, url.flags); - - return nlc; -} - -struct HttpSecurityContext -{ - HANDLE m_hNtlmSecurity; - char *m_szHost; - char *m_szProvider; - - HttpSecurityContext() - { - m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr; - } - - ~HttpSecurityContext() { Destroy(); } - - void Destroy(void) - { - if (!m_hNtlmSecurity) return; - - Netlib_DestroySecurityProvider(m_hNtlmSecurity); - m_hNtlmSecurity = nullptr; - mir_free(m_szHost); m_szHost = nullptr; - mir_free(m_szProvider); m_szProvider = nullptr; - } - - bool TryBasic(void) - { - return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic"); - } - - char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete) - { - char *szAuthHdr = nullptr; - bool justCreated = false; - NetlibUser *nlu = nlc->nlu; - - if (m_hNtlmSecurity) { - bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider); - newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost))); - if (newAuth) - Destroy(); - } - - if (m_hNtlmSecurity == nullptr) { - CMStringA szSpnStr; - if (szHost && _stricmp(szProvider, "Basic")) { - unsigned long ip = inet_addr(szHost); - PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET); - szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost); - _strlwr(szSpnStr.GetBuffer() + 5); - Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str()); - } - m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str())); - if (m_hNtlmSecurity) { - m_szProvider = mir_strdup(szProvider); - m_szHost = mir_strdup(szHost); - justCreated = true; - } - } - - if (m_hNtlmSecurity) { - ptrW szLogin, szPassw; - - if (nlu->settings.useProxyAuth) { - mir_cslock lck(csNetlibUser); - szLogin = mir_a2u(nlu->settings.szProxyAuthUser); - szPassw = mir_a2u(nlu->settings.szProxyAuthPassword); - } - - szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete); - if (!szAuthHdr) - Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)"); - else if (justCreated) - proxyAuthList.add(m_szHost, m_szProvider); - } - else complete = 1; - - return szAuthHdr; - } -}; - -static int HttpPeekFirstResponseLine(NetlibConnection *nlc, uint32_t dwTimeoutTime, uint32_t recvFlags, int *resultCode, char **ppszResultDescr, int *length) -{ - int bytesPeeked; - char buffer[2048], *peol; - - while (true) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags); - if (bytesPeeked == 0) { - SetLastError(ERROR_HANDLE_EOF); - return 0; - } - if (bytesPeeked == SOCKET_ERROR) - return 0; - - buffer[bytesPeeked] = '\0'; - if ((peol = strchr(buffer, '\n')) != nullptr) - break; - - if ((int)mir_strlen(buffer) < bytesPeeked) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - if (bytesPeeked == _countof(buffer) - 1) { - SetLastError(ERROR_BUFFER_OVERFLOW); - return 0; - } - if (Miranda_IsTerminated()) - return 0; - Sleep(10); - } - - if (peol == buffer) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - *peol = '\0'; - - if (_strnicmp(buffer, "HTTP/", 5)) { - SetLastError(ERROR_BAD_FORMAT); - return 0; - } - - size_t off = strcspn(buffer, " \t"); - if (off >= (unsigned)bytesPeeked) - return 0; - - char *pResultCode = buffer + off; - *(pResultCode++) = 0; - - char *pResultDescr; - *resultCode = strtol(pResultCode, &pResultDescr, 10); - - if (ppszResultDescr) - *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr)); - - if (length) - *length = peol - buffer + 1; - return 1; -} - -static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader) -{ - bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH); - - if (sendContentLengthHeader && sendData) - httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength); - else - httpRequest.AppendFormat("\r\n"); - - uint32_t hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags); - if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) { - uint32_t sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags); - - bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR; - } - - return bytesSent; -} - -MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) -{ - NETLIBHTTPREQUEST *nlhrReply = nullptr; - HttpSecurityContext httpSecurity; - - char *szHost = nullptr, *szNewUrl = nullptr; - char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr; - int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader; - int bytesSent = 0; - bool lastFirstLineFail = false; - - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - NetlibUser *nlu = nlc->nlu; - if (GetNetlibHandleType(nlu) != NLH_USER) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT; - - const char *pszRequest; - switch (nlhr->requestType) { - case REQUEST_GET: pszRequest = "GET"; break; - case REQUEST_POST: pszRequest = "POST"; break; - case REQUEST_CONNECT: pszRequest = "CONNECT"; break; - case REQUEST_HEAD: pszRequest = "HEAD"; break; - case REQUEST_PUT: pszRequest = "PUT"; break; - case REQUEST_DELETE: pszRequest = "DELETE"; break; - case REQUEST_PATCH: pszRequest = "PATCH"; break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - const char *pszFullUrl = nlhr->szUrl; - const char *pszUrl = nullptr; - - unsigned complete = false; - int count = 11; - while (--count) { - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - bytesSent = SOCKET_ERROR; - break; - } - - if (!NetlibReconnect(nlc)) { - bytesSent = SOCKET_ERROR; - break; - } - - if (!pszUrl) { - pszUrl = pszFullUrl; - if (!(nlhr->flags & NLHRF_MANUALHOST)) { - bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL); - - const char *ppath, *phost; - phost = strstr(pszUrl, "://"); - if (phost == nullptr) phost = pszUrl; - else phost += 3; - ppath = strchr(phost, '/'); - if (ppath == phost) - phost = nullptr; - - replaceStr(szHost, phost); - if (ppath && phost) - szHost[ppath - phost] = 0; - - if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy) - pszUrl = ppath ? ppath : "/"; - - if (usingProxy && phost && !nlc->dnsThroughProxy) { - char *tszHost = mir_strdup(phost); - if (ppath) - tszHost[ppath - phost] = 0; - char *cln = strchr(tszHost, ':'); if (cln) *cln = 0; - - if (inet_addr(tszHost) == INADDR_NONE) { - in_addr ip; - if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) { - mir_free(szHost); - if (cln) *cln = ':'; - szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach(); - } - } - mir_free(tszHost); - } - } - } - - if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) { - if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) { - const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer); - if (szAuthMethodNlu) { - mir_free(pszProxyAuthHdr); - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete); - } - } - } - nlc->proxyAuthNeeded = false; - - CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); - - // HTTP headers - doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0; - for (i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1; - else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1; - else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1; - else if (!mir_strcmpi(p.szName, "Connection")) continue; - if (p.szValue == nullptr) continue; - httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue); - } - if (szHost && !doneHostHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost); - if (pszProxyAuthHdr && !doneProxyAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr); - if (pszAuthHdr && !doneAuthHeader) - httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr); - httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive"); - httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive"); - - // Add Sticky Headers - if (nlu->szStickyHeaders != nullptr) - httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders); - - // send it - bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader); - if (bytesSent == SOCKET_ERROR) - break; - - // ntlm reply - if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD) - break; - - uint32_t fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0); - uint32_t dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout; - if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) { - uint32_t err = GetLastError(); - Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count); - - // connection died while we were waiting - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { - nlc = nullptr; - break; - } - - if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) { - bytesSent = SOCKET_ERROR; - break; - } - - lastFirstLineFail = true; - continue; - } - - int resultCode = nlhr->resultCode; - lastFirstLineFail = false; - - uint32_t hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ? - MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - uint32_t dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP; - - if (resultCode == 100) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - - else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect - pszUrl = nullptr; - - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - auto *tmpUrl = Netlib_GetHeader(nlhrReply, "Location"); - if (tmpUrl) { - size_t rlen = 0; - if (tmpUrl[0] == '/') { - const char *ppath, *phost; - phost = strstr(pszFullUrl, "://"); - phost = phost ? phost + 3 : pszFullUrl; - ppath = strchr(phost, '/'); - rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl); - } - - nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1); - - strncpy(nlc->szNewUrl, pszFullUrl, rlen); - mir_strcpy(nlc->szNewUrl + rlen, tmpUrl); - pszFullUrl = nlc->szNewUrl; - pszUrl = nullptr; - - if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) { - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else { - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 401 && !doneAuthHeader) { //auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - replaceStr(pszAuthHdr, nullptr); - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete); - } - } - if (pszAuthHdr == nullptr) { - proxyAuthList.add(szHost, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr; - if (nlhrReply) { - char *szAuthStr = nullptr; - if (!complete) { - szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider); - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (!szChallenge || !*lrtrimp(szChallenge + 1)) - complete = true; - } - } - if (complete && httpSecurity.m_hNtlmSecurity) - szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr; - - if (szAuthStr) { - char *szChallenge = strchr(szAuthStr, ' '); - if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } - - pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete); - } - } - if (pszProxyAuthHdr == nullptr) { - proxyAuthList.add(nlc->szProxyServer, nullptr); - NetlibHttpSetLastErrorUsingHttpResult(resultCode); - bytesSent = SOCKET_ERROR; - break; - } - } - else break; - - if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader) - replaceStr(pszProxyAuthHdr, nullptr); - - if (pszAuthHdr && resultCode != 401 && !doneAuthHeader) - replaceStr(pszAuthHdr, nullptr); - - if (nlhrReply) { - Netlib_FreeHttpRequest(nlhrReply); - nlhrReply = nullptr; - } - } - - if (count == 0) bytesSent = SOCKET_ERROR; - if (nlhrReply) - Netlib_FreeHttpRequest(nlhrReply); - - //clean up - mir_free(pszProxyAuthHdr); - mir_free(pszAuthHdr); - mir_free(szHost); - mir_free(szNewUrl); - - if (nlc) - NetlibLeaveNestedCS(&nlc->ncsSend); - - return bytesSent; -} - -MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) -{ - if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - if (nlhr->headers) { - for (int i = 0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->headers[i]; - mir_free(p.szName); - mir_free(p.szValue); - } - mir_free(nlhr->headers); - } - mir_free(nlhr->pData); - mir_free(nlhr->szResultDescr); - mir_free(nlhr->szUrl); - mir_free(nlhr); - return true; -} - -#define NHRV_BUF_SIZE 8192 - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return nullptr; - - uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; - NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); - nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); - nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions - nlhr->requestType = REQUEST_RESPONSE; - - int firstLineLength = 0; - if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1); - int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT); - if (bytesPeeked != firstLineLength) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - if (bytesPeeked != SOCKET_ERROR) - SetLastError(ERROR_HANDLE_EOF); - return nullptr; - } - - // Make sure all headers arrived - MBinBuffer buf; - int headersCount = 0; - bytesPeeked = 0; - for (bool headersCompleted = false; !headersCompleted;) { - bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE); - if (bytesPeeked == 0) - break; - - if (bytesPeeked == SOCKET_ERROR) { - bytesPeeked = 0; - break; - } - - buf.append(buffer, bytesPeeked); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr) break; - if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) { - bytesPeeked = peol - (char*)buf.data() + 1; - headersCompleted = true; - break; - } - pbuffer = peol + 1; - } - } - - if (bytesPeeked <= 0) { - NetlibLeaveNestedCS(&nlc->ncsRecv); - Netlib_FreeHttpRequest(nlhr); - return nullptr; - } - - // Receive headers - nlhr->headersCount = headersCount; - nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount); - - headersCount = 0; - for (char *pbuffer = (char*)buf.data();; headersCount++) { - char *peol = strchr(pbuffer, '\n'); - if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r')) - break; - *peol = 0; - - char *pColon = strchr(pbuffer, ':'); - if (pColon == nullptr) { - Netlib_FreeHttpRequest(nlhr); nlhr = nullptr; - SetLastError(ERROR_INVALID_DATA); - break; - } - - *pColon = 0; - nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer)); - nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1)); - pbuffer = peol + 1; - } - - // remove processed data - buf.remove(bytesPeeked); - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - return nlhr; -} - -MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) -{ - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || - nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || - nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0) - { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION) - nlhr->nlc = nullptr; - - NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc); - if (nlc == nullptr) - return nullptr; - - NETLIBHTTPREQUEST nlhrSend = *nlhr; - nlhrSend.flags |= NLHRF_SMARTREMOVEHOST; - - bool doneUserAgentHeader = Netlib_GetHeader(nlhr, "User-Agent") != nullptr; - bool doneAcceptEncoding = Netlib_GetHeader(nlhr, "Accept-Encoding") != nullptr; - if (!doneUserAgentHeader || !doneAcceptEncoding) { - nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2)); - memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount); - } - - char szUserAgent[64]; - if (!doneUserAgentHeader) { - nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent"; - nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent; - ++nlhrSend.headersCount; - - char szMirandaVer[64]; - strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE); - #if defined(_WIN64) - strncat_s(szMirandaVer, " x64", _TRUNCATE); - #endif - - char *pspace = strchr(szMirandaVer, ' '); - if (pspace) { - *pspace++ = '\0'; - mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace); - } - else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer); - } - if (!doneAcceptEncoding) { - nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding"; - nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip"; - ++nlhrSend.headersCount; - } - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) { - if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers); - nlhr->resultCode = nlhrSend.resultCode; - Netlib_CloseHandle(nlc); - return nullptr; - } - if (!doneUserAgentHeader || !doneAcceptEncoding) - mir_free(nlhrSend.headers); - - uint32_t dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - uint32_t hflags = - (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | - (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); - - NETLIBHTTPREQUEST *nlhrReply; - if (nlhr->requestType == REQUEST_HEAD) - nlhrReply = Netlib_RecvHttpHeaders(nlc); - else - nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); - - if (nlhrReply) { - nlhrReply->szUrl = nlc->szNewUrl; - nlc->szNewUrl = nullptr; - } - - if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) { - Netlib_CloseHandle(nlc); - if (nlhrReply) - nlhrReply->nlc = nullptr; - } - else nlhrReply->nlc = nlc; - - return nlhrReply; -} - -void NetlibHttpSetLastErrorUsingHttpResult(int result) -{ - if (result >= 200 && result < 300) { - SetLastError(ERROR_SUCCESS); - return; - } - switch (result) { - case 400: SetLastError(ERROR_BAD_FORMAT); break; - case 401: - case 402: - case 403: - case 407: SetLastError(ERROR_ACCESS_DENIED); break; - case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; - case 405: - case 406: SetLastError(ERROR_INVALID_FUNCTION); break; - case 408: SetLastError(ERROR_TIMEOUT); break; - default: SetLastError(ERROR_GEN_FAILURE); break; - } -} - -char* gzip_decode(char *gzip_data, int *len_ptr, int window) -{ - if (*len_ptr == 0) return nullptr; - - int gzip_len = *len_ptr * 5; - char* output_data = nullptr; - - int gzip_err; - z_stream zstr; - - do { - output_data = (char*)mir_realloc(output_data, gzip_len+1); - if (output_data == nullptr) - break; - - zstr.next_in = (Bytef*)gzip_data; - zstr.avail_in = *len_ptr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream)); - - zstr.next_out = (Bytef*)output_data; - zstr.avail_out = gzip_len; - - gzip_err = inflate(&zstr, Z_FINISH); - - inflateEnd(&zstr); - gzip_len *= 2; - if (gzip_len > 10000000) - break; - } while (gzip_err == Z_BUF_ERROR); - - gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1; - - if (gzip_len <= 0) { - mir_free(output_data); - output_data = nullptr; - } - else output_data[gzip_len] = 0; - - *len_ptr = gzip_len; - return output_data; -} - -static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, uint32_t flags) -{ - MBinBuffer buf; - - while (true) { - char data[1000]; - int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags); - if (recvResult <= 0 || recvResult >= _countof(data)) - return SOCKET_ERROR; - - buf.append(data, recvResult); // add chunk - - auto *peol1 = (const char*)memchr(buf.data(), '\n', buf.length()); - if (peol1 == nullptr) - continue; - - auto *pStart = (const char *)buf.data(); - int cbRest = int(peol1 - pStart) + 1; - const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest); - if (peol2 == nullptr) - continue; - - int sz = peol2 - pStart + 1; - int r = strtol(first ? pStart : peol1 + 1, nullptr, 16); - if (r == 0) { - const char *peol3 = strchr(peol2 + 1, '\n'); - if (peol3 == nullptr) - continue; - sz = peol3 - pStart + 1; - } - buf.remove(sz); // remove all our data from buffer - nlc->foreBuf.appendBefore(buf.data(), buf.length()); - return r; - } -} - -NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect) -{ - int dataLen = -1, i, chunkhdr = 0; - bool chunked = false; - int cenc = 0, cenctype = 0, close = 0; - -next: - NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); - if (nlhrReply == nullptr) - return nullptr; - - if (nlhrReply->resultCode == 100) { - Netlib_FreeHttpRequest(nlhrReply); - goto next; - } - - if (nlhrReply->resultCode == 204) - dataLen = 0; - - for (i = 0; i < nlhrReply->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhrReply->headers[i]; - if (!mir_strcmpi(p.szName, "Content-Length")) - dataLen = atoi(p.szValue); - - if (!mir_strcmpi(p.szName, "Content-Encoding")) { - cenc = i; - if (strstr(p.szValue, "gzip")) - cenctype = 1; - else if (strstr(p.szValue, "deflate")) - cenctype = 2; - } - - if (!mir_strcmpi(p.szName, "Connection")) - close = !mir_strcmpi(p.szValue, "close"); - - if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) { - chunked = true; - chunkhdr = i; - dataLen = -1; - } - } - - if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) { - int recvResult, chunksz = -1; - int dataBufferAlloced; - - if (chunked) { - chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0)); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen = chunksz; - } - dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - - while (chunksz != 0) { - while (true) { - recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT, - nlhrReply->pData + nlhrReply->dataLength, - dataBufferAlloced - nlhrReply->dataLength - 1, - dflags | (cenctype ? MSG_NODUMP : 0)); - - if (recvResult == 0) break; - if (recvResult == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - nlhrReply->dataLength += recvResult; - - if (dataLen >= 0) { - if (nlhrReply->dataLength >= dataLen) - break; - } - else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) { - dataBufferAlloced += 2048; - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - if (nlhrReply->pData == nullptr) { - SetLastError(ERROR_OUTOFMEMORY); - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - } - Sleep(10); - } - - if (!chunked) - break; - - chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP); - if (chunksz == SOCKET_ERROR) { - Netlib_FreeHttpRequest(nlhrReply); - return nullptr; - } - dataLen += chunksz; - dataBufferAlloced += chunksz; - - nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); - } - - nlhrReply->pData[nlhrReply->dataLength] = '\0'; - } - - if (chunked) { - nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16); - mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length"); - - nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16); - mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength); - } - - if (cenctype) { - int bufsz = nlhrReply->dataLength; - char* szData = nullptr; - - switch (cenctype) { - case 1: - szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS); - break; - - case 2: - szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS); - if (bufsz < 0) { - bufsz = nlhrReply->dataLength; - szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS); - } - break; - } - - if (bufsz > 0) { - Netlib_Dump(nlc, (uint8_t*)szData, bufsz, false, dflags | MSG_NOTITLE); - mir_free(nlhrReply->pData); - nlhrReply->pData = szData; - nlhrReply->dataLength = bufsz; - - mir_free(nlhrReply->headers[cenc].szName); - mir_free(nlhrReply->headers[cenc].szValue); - memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0])); - } - else if (bufsz == 0) { - mir_free(nlhrReply->pData); - nlhrReply->pData = nullptr; - nlhrReply->dataLength = 0; - } - } - - if (close && - (nlc->proxyType != PROXYTYPE_HTTP || nlc->url.flags & NLOCF_SSL) && - (!isConnect || nlhrReply->resultCode != 200)) - NetlibDoCloseSocket(nlc); - - return nlhrReply; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "../libs/zlib/src/zlib.h" +#include "netlib.h" + +#define HTTPRECVHEADERSTIMEOUT 30000 //in ms +#define HTTPRECVDATATIMEOUT 20000 + +struct ProxyAuth +{ + char *szServer; + char *szMethod; + + ProxyAuth(const char *pszServer, const char *pszMethod) + { + szServer = mir_strdup(pszServer); + szMethod = mir_strdup(pszMethod); + } + ~ProxyAuth() + { + mir_free(szServer); + mir_free(szMethod); + } + static int Compare(const ProxyAuth *p1, const ProxyAuth *p2) + { + return mir_strcmpi(p1->szServer, p2->szServer); + } +}; + +struct ProxyAuthList : OBJLIST +{ + ProxyAuthList() : OBJLIST(2, ProxyAuth::Compare) {} + + void add(const char *szServer, const char *szMethod) + { + if (szServer == nullptr) return; + int i = getIndex((ProxyAuth*)&szServer); + if (i >= 0) { + ProxyAuth &rec = (*this)[i]; + if (szMethod == nullptr) + remove(i); + else if (_stricmp(rec.szMethod, szMethod)) { + mir_free(rec.szMethod); + rec.szMethod = mir_strdup(szMethod); + } + } + else insert(new ProxyAuth(szServer, szMethod)); + } + + const char* find(const char *szServer) + { + ProxyAuth *rec = szServer ? OBJLIST::find((ProxyAuth*)&szServer) : nullptr; + return rec ? rec->szMethod : nullptr; + } +}; + +ProxyAuthList proxyAuthList; + +static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags) +{ + int dwTimeNow; + + if (nlc->foreBuf.isEmpty() && !Netlib_SslPending(nlc->hSsl)) { + while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) { + int dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000); + int res = WaitUntilReadable(nlc->s, dwDeltaTime); + + switch (res) { + case SOCKET_ERROR: + return SOCKET_ERROR; + + case 1: + return Netlib_Recv(nlc, buf, len, flags); + } + + if (nlc->termRequested || Miranda_IsTerminated()) + return 0; + } + SetLastError(ERROR_TIMEOUT); + return SOCKET_ERROR; + } + return Netlib_Recv(nlc, buf, len, flags); +} + +MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr) +{ + if (nlhr == nullptr || hdr == nullptr) + return nullptr; + + for (int i=0; i < nlhr->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhr->headers[i]; + if (_stricmp(p.szName, hdr) == 0) + return p.szValue; + } + + return nullptr; +} + +static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider) +{ + char *szBasicHdr = nullptr; + char *szNegoHdr = nullptr; + char *szNtlmHdr = nullptr; + + for (int i=0; i < nlhrReply->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhrReply->headers[i]; + if (_stricmp(p.szName, hdr) == 0) { + if (_strnicmp(p.szValue, "Negotiate", 9) == 0) + szNegoHdr = p.szValue; + else if (_strnicmp(p.szValue, "NTLM", 4) == 0) + szNtlmHdr = p.szValue; + else if (_strnicmp(p.szValue, "Basic", 5) == 0) + szBasicHdr = p.szValue; + } + } + + if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr; + if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr; + if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr; + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url) +{ + secur = secur || _strnicmp(szUrl, "https", 5) == 0; + + const char* phost = strstr(szUrl, "://"); + url.szHost = phost ? phost + 3 : szUrl; + + int idx = url.szHost.Find('/'); + if (idx != -1) + url.szHost.Truncate(idx); + + if ((idx = url.szHost.Find(':')) != -1) { + url.port = strtol(url.szHost.c_str() + idx + 1, nullptr, 10); + url.szHost.Truncate(idx); + } + else url.port = secur ? 443 : 80; + url.flags = (secur ? NLOCF_SSL : 0); +} + +static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr) +{ + NetlibUrl url; + + if (szUrl == nullptr) + NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, url); + else + NetlibConnFromUrl(szUrl, false, url); + + url.flags |= NLOCF_HTTP; + if (url.flags & NLOCF_SSL) + nlhr->flags |= NLHRF_SSL; + else + nlhr->flags &= ~NLHRF_SSL; + + if (nlc != nullptr) { + bool httpProxy = !(url.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP; + bool sameHost = mir_strcmp(nlc->url.szHost, url.szHost) == 0 && nlc->url.port == url.port; + + if (!httpProxy && !sameHost) { + NetlibDoCloseSocket(nlc); + + nlc->url = url; + return NetlibDoConnect(nlc) ? nlc : nullptr; + } + } + else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, url.szHost, url.port, 0, url.flags); + + return nlc; +} + +struct HttpSecurityContext +{ + HANDLE m_hNtlmSecurity; + char *m_szHost; + char *m_szProvider; + + HttpSecurityContext() + { + m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr; + } + + ~HttpSecurityContext() { Destroy(); } + + void Destroy(void) + { + if (!m_hNtlmSecurity) return; + + Netlib_DestroySecurityProvider(m_hNtlmSecurity); + m_hNtlmSecurity = nullptr; + mir_free(m_szHost); m_szHost = nullptr; + mir_free(m_szProvider); m_szProvider = nullptr; + } + + bool TryBasic(void) + { + return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic"); + } + + char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete) + { + char *szAuthHdr = nullptr; + bool justCreated = false; + NetlibUser *nlu = nlc->nlu; + + if (m_hNtlmSecurity) { + bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider); + newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost))); + if (newAuth) + Destroy(); + } + + if (m_hNtlmSecurity == nullptr) { + CMStringA szSpnStr; + if (szHost && _stricmp(szProvider, "Basic")) { + unsigned long ip = inet_addr(szHost); + PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET); + szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost); + _strlwr(szSpnStr.GetBuffer() + 5); + Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str()); + } + m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str())); + if (m_hNtlmSecurity) { + m_szProvider = mir_strdup(szProvider); + m_szHost = mir_strdup(szHost); + justCreated = true; + } + } + + if (m_hNtlmSecurity) { + ptrW szLogin, szPassw; + + if (nlu->settings.useProxyAuth) { + mir_cslock lck(csNetlibUser); + szLogin = mir_a2u(nlu->settings.szProxyAuthUser); + szPassw = mir_a2u(nlu->settings.szProxyAuthPassword); + } + + szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete); + if (!szAuthHdr) + Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)"); + else if (justCreated) + proxyAuthList.add(m_szHost, m_szProvider); + } + else complete = 1; + + return szAuthHdr; + } +}; + +static int HttpPeekFirstResponseLine(NetlibConnection *nlc, uint32_t dwTimeoutTime, uint32_t recvFlags, int *resultCode, char **ppszResultDescr, int *length) +{ + int bytesPeeked; + char buffer[2048], *peol; + + while (true) { + bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags); + if (bytesPeeked == 0) { + SetLastError(ERROR_HANDLE_EOF); + return 0; + } + if (bytesPeeked == SOCKET_ERROR) + return 0; + + buffer[bytesPeeked] = '\0'; + if ((peol = strchr(buffer, '\n')) != nullptr) + break; + + if ((int)mir_strlen(buffer) < bytesPeeked) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + if (bytesPeeked == _countof(buffer) - 1) { + SetLastError(ERROR_BUFFER_OVERFLOW); + return 0; + } + if (Miranda_IsTerminated()) + return 0; + Sleep(10); + } + + if (peol == buffer) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + + *peol = '\0'; + + if (_strnicmp(buffer, "HTTP/", 5)) { + SetLastError(ERROR_BAD_FORMAT); + return 0; + } + + size_t off = strcspn(buffer, " \t"); + if (off >= (unsigned)bytesPeeked) + return 0; + + char *pResultCode = buffer + off; + *(pResultCode++) = 0; + + char *pResultDescr; + *resultCode = strtol(pResultCode, &pResultDescr, 10); + + if (ppszResultDescr) + *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr)); + + if (length) + *length = peol - buffer + 1; + return 1; +} + +static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader) +{ + bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH); + + if (sendContentLengthHeader && sendData) + httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength); + else + httpRequest.AppendFormat("\r\n"); + + uint32_t hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | + (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ? + MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags); + if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) { + uint32_t sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | + (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? + MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags); + + bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR; + } + + return bytesSent; +} + +MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) +{ + NETLIBHTTPREQUEST *nlhrReply = nullptr; + HttpSecurityContext httpSecurity; + + char *szHost = nullptr, *szNewUrl = nullptr; + char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr; + int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader; + int bytesSent = 0; + bool lastFirstLineFail = false; + + if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + NetlibUser *nlu = nlc->nlu; + if (GetNetlibHandleType(nlu) != NLH_USER) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT; + + const char *pszRequest; + switch (nlhr->requestType) { + case REQUEST_GET: pszRequest = "GET"; break; + case REQUEST_POST: pszRequest = "POST"; break; + case REQUEST_CONNECT: pszRequest = "CONNECT"; break; + case REQUEST_HEAD: pszRequest = "HEAD"; break; + case REQUEST_PUT: pszRequest = "PUT"; break; + case REQUEST_DELETE: pszRequest = "DELETE"; break; + case REQUEST_PATCH: pszRequest = "PATCH"; break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) + return SOCKET_ERROR; + + const char *pszFullUrl = nlhr->szUrl; + const char *pszUrl = nullptr; + + unsigned complete = false; + int count = 11; + while (--count) { + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { + nlc = nullptr; + bytesSent = SOCKET_ERROR; + break; + } + + if (!NetlibReconnect(nlc)) { + bytesSent = SOCKET_ERROR; + break; + } + + if (!pszUrl) { + pszUrl = pszFullUrl; + if (!(nlhr->flags & NLHRF_MANUALHOST)) { + bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL); + + const char *ppath, *phost; + phost = strstr(pszUrl, "://"); + if (phost == nullptr) phost = pszUrl; + else phost += 3; + ppath = strchr(phost, '/'); + if (ppath == phost) + phost = nullptr; + + replaceStr(szHost, phost); + if (ppath && phost) + szHost[ppath - phost] = 0; + + if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy) + pszUrl = ppath ? ppath : "/"; + + if (usingProxy && phost && !nlc->dnsThroughProxy) { + char *tszHost = mir_strdup(phost); + if (ppath) + tszHost[ppath - phost] = 0; + char *cln = strchr(tszHost, ':'); if (cln) *cln = 0; + + if (inet_addr(tszHost) == INADDR_NONE) { + in_addr ip; + if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) { + mir_free(szHost); + if (cln) *cln = ':'; + szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach(); + } + } + mir_free(tszHost); + } + } + } + + if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) { + if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) { + const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer); + if (szAuthMethodNlu) { + mir_free(pszProxyAuthHdr); + pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete); + } + } + } + nlc->proxyAuthNeeded = false; + + CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0); + + // HTTP headers + doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0; + for (i = 0; i < nlhr->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhr->headers[i]; + if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1; + else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1; + else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1; + else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1; + else if (!mir_strcmpi(p.szName, "Connection")) continue; + if (p.szValue == nullptr) continue; + httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue); + } + if (szHost && !doneHostHeader) + httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost); + if (pszProxyAuthHdr && !doneProxyAuthHeader) + httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr); + if (pszAuthHdr && !doneAuthHeader) + httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr); + httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive"); + httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive"); + + // Add Sticky Headers + if (nlu->szStickyHeaders != nullptr) + httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders); + + // send it + bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader); + if (bytesSent == SOCKET_ERROR) + break; + + // ntlm reply + if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD) + break; + + uint32_t fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0); + uint32_t dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout; + if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) { + uint32_t err = GetLastError(); + Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count); + + // connection died while we were waiting + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) { + nlc = nullptr; + break; + } + + if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) { + bytesSent = SOCKET_ERROR; + break; + } + + lastFirstLineFail = true; + continue; + } + + int resultCode = nlhr->resultCode; + lastFirstLineFail = false; + + uint32_t hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ? + MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + uint32_t dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP; + + if (resultCode == 100) + nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); + + else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect + pszUrl = nullptr; + + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + if (nlhrReply) { + auto *tmpUrl = Netlib_GetHeader(nlhrReply, "Location"); + if (tmpUrl) { + size_t rlen = 0; + if (tmpUrl[0] == '/') { + const char *ppath, *phost; + phost = strstr(pszFullUrl, "://"); + phost = phost ? phost + 3 : pszFullUrl; + ppath = strchr(phost, '/'); + rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl); + } + + nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1); + + strncpy(nlc->szNewUrl, pszFullUrl, rlen); + mir_strcpy(nlc->szNewUrl + rlen, tmpUrl); + pszFullUrl = nlc->szNewUrl; + pszUrl = nullptr; + + if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) { + bytesSent = SOCKET_ERROR; + break; + } + } + else { + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else { + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else if (resultCode == 401 && !doneAuthHeader) { //auth required + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + replaceStr(pszAuthHdr, nullptr); + if (nlhrReply) { + char *szAuthStr = nullptr; + if (!complete) { + szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider); + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (!szChallenge || !*lrtrimp(szChallenge)) + complete = true; + } + } + if (complete && httpSecurity.m_hNtlmSecurity) + szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr; + + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } + + pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete); + } + } + if (pszAuthHdr == nullptr) { + proxyAuthList.add(szHost, nullptr); + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr; + if (nlhrReply) { + char *szAuthStr = nullptr; + if (!complete) { + szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider); + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (!szChallenge || !*lrtrimp(szChallenge + 1)) + complete = true; + } + } + if (complete && httpSecurity.m_hNtlmSecurity) + szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr; + + if (szAuthStr) { + char *szChallenge = strchr(szAuthStr, ' '); + if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); } + + pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete); + } + } + if (pszProxyAuthHdr == nullptr) { + proxyAuthList.add(nlc->szProxyServer, nullptr); + NetlibHttpSetLastErrorUsingHttpResult(resultCode); + bytesSent = SOCKET_ERROR; + break; + } + } + else break; + + if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader) + replaceStr(pszProxyAuthHdr, nullptr); + + if (pszAuthHdr && resultCode != 401 && !doneAuthHeader) + replaceStr(pszAuthHdr, nullptr); + + if (nlhrReply) { + Netlib_FreeHttpRequest(nlhrReply); + nlhrReply = nullptr; + } + } + + if (count == 0) bytesSent = SOCKET_ERROR; + if (nlhrReply) + Netlib_FreeHttpRequest(nlhrReply); + + //clean up + mir_free(pszProxyAuthHdr); + mir_free(pszAuthHdr); + mir_free(szHost); + mir_free(szNewUrl); + + if (nlc) + NetlibLeaveNestedCS(&nlc->ncsSend); + + return bytesSent; +} + +MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) +{ + if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + if (nlhr->headers) { + for (int i = 0; i < nlhr->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhr->headers[i]; + mir_free(p.szName); + mir_free(p.szValue); + } + mir_free(nlhr->headers); + } + mir_free(nlhr->pData); + mir_free(nlhr->szResultDescr); + mir_free(nlhr->szUrl); + mir_free(nlhr); + return true; +} + +#define NHRV_BUF_SIZE 8192 + +MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) +{ + NetlibConnection *nlc = (NetlibConnection*)hConnection; + if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) + return nullptr; + + uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; + NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST)); + nlhr->cbSize = sizeof(NETLIBHTTPREQUEST); + nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions + nlhr->requestType = REQUEST_RESPONSE; + + int firstLineLength = 0; + if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + Netlib_FreeHttpRequest(nlhr); + return nullptr; + } + + char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1); + int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT); + if (bytesPeeked != firstLineLength) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + Netlib_FreeHttpRequest(nlhr); + if (bytesPeeked != SOCKET_ERROR) + SetLastError(ERROR_HANDLE_EOF); + return nullptr; + } + + // Make sure all headers arrived + MBinBuffer buf; + int headersCount = 0; + bytesPeeked = 0; + for (bool headersCompleted = false; !headersCompleted;) { + bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE); + if (bytesPeeked == 0) + break; + + if (bytesPeeked == SOCKET_ERROR) { + bytesPeeked = 0; + break; + } + + buf.append(buffer, bytesPeeked); + + headersCount = 0; + for (char *pbuffer = (char*)buf.data();; headersCount++) { + char *peol = strchr(pbuffer, '\n'); + if (peol == nullptr) break; + if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) { + bytesPeeked = peol - (char*)buf.data() + 1; + headersCompleted = true; + break; + } + pbuffer = peol + 1; + } + } + + if (bytesPeeked <= 0) { + NetlibLeaveNestedCS(&nlc->ncsRecv); + Netlib_FreeHttpRequest(nlhr); + return nullptr; + } + + // Receive headers + nlhr->headersCount = headersCount; + nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount); + + headersCount = 0; + for (char *pbuffer = (char*)buf.data();; headersCount++) { + char *peol = strchr(pbuffer, '\n'); + if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r')) + break; + *peol = 0; + + char *pColon = strchr(pbuffer, ':'); + if (pColon == nullptr) { + Netlib_FreeHttpRequest(nlhr); nlhr = nullptr; + SetLastError(ERROR_INVALID_DATA); + break; + } + + *pColon = 0; + nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer)); + nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1)); + pbuffer = peol + 1; + } + + // remove processed data + buf.remove(bytesPeeked); + nlc->foreBuf.appendBefore(buf.data(), buf.length()); + + NetlibLeaveNestedCS(&nlc->ncsRecv); + return nlhr; +} + +MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) +{ + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || + nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || + nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION) + nlhr->nlc = nullptr; + + NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc); + if (nlc == nullptr) + return nullptr; + + NETLIBHTTPREQUEST nlhrSend = *nlhr; + nlhrSend.flags |= NLHRF_SMARTREMOVEHOST; + + bool doneUserAgentHeader = Netlib_GetHeader(nlhr, "User-Agent") != nullptr; + bool doneAcceptEncoding = Netlib_GetHeader(nlhr, "Accept-Encoding") != nullptr; + if (!doneUserAgentHeader || !doneAcceptEncoding) { + nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2)); + memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount); + } + + char szUserAgent[64]; + if (!doneUserAgentHeader) { + nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent"; + nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent; + ++nlhrSend.headersCount; + + char szMirandaVer[64]; + strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE); + #if defined(_WIN64) + strncat_s(szMirandaVer, " x64", _TRUNCATE); + #endif + + char *pspace = strchr(szMirandaVer, ' '); + if (pspace) { + *pspace++ = '\0'; + mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace); + } + else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer); + } + if (!doneAcceptEncoding) { + nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding"; + nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip"; + ++nlhrSend.headersCount; + } + if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) { + if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers); + nlhr->resultCode = nlhrSend.resultCode; + Netlib_CloseHandle(nlc); + return nullptr; + } + if (!doneUserAgentHeader || !doneAcceptEncoding) + mir_free(nlhrSend.headers); + + uint32_t dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) | + (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + uint32_t hflags = + (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) | + (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0); + + NETLIBHTTPREQUEST *nlhrReply; + if (nlhr->requestType == REQUEST_HEAD) + nlhrReply = Netlib_RecvHttpHeaders(nlc); + else + nlhrReply = NetlibHttpRecv(nlc, hflags, dflags); + + if (nlhrReply) { + nlhrReply->szUrl = nlc->szNewUrl; + nlc->szNewUrl = nullptr; + } + + if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) { + Netlib_CloseHandle(nlc); + if (nlhrReply) + nlhrReply->nlc = nullptr; + } + else nlhrReply->nlc = nlc; + + return nlhrReply; +} + +void NetlibHttpSetLastErrorUsingHttpResult(int result) +{ + if (result >= 200 && result < 300) { + SetLastError(ERROR_SUCCESS); + return; + } + switch (result) { + case 400: SetLastError(ERROR_BAD_FORMAT); break; + case 401: + case 402: + case 403: + case 407: SetLastError(ERROR_ACCESS_DENIED); break; + case 404: SetLastError(ERROR_FILE_NOT_FOUND); break; + case 405: + case 406: SetLastError(ERROR_INVALID_FUNCTION); break; + case 408: SetLastError(ERROR_TIMEOUT); break; + default: SetLastError(ERROR_GEN_FAILURE); break; + } +} + +char* gzip_decode(char *gzip_data, int *len_ptr, int window) +{ + if (*len_ptr == 0) return nullptr; + + int gzip_len = *len_ptr * 5; + char* output_data = nullptr; + + int gzip_err; + z_stream zstr; + + do { + output_data = (char*)mir_realloc(output_data, gzip_len+1); + if (output_data == nullptr) + break; + + zstr.next_in = (Bytef*)gzip_data; + zstr.avail_in = *len_ptr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream)); + + zstr.next_out = (Bytef*)output_data; + zstr.avail_out = gzip_len; + + gzip_err = inflate(&zstr, Z_FINISH); + + inflateEnd(&zstr); + gzip_len *= 2; + if (gzip_len > 10000000) + break; + } while (gzip_err == Z_BUF_ERROR); + + gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1; + + if (gzip_len <= 0) { + mir_free(output_data); + output_data = nullptr; + } + else output_data[gzip_len] = 0; + + *len_ptr = gzip_len; + return output_data; +} + +static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, uint32_t flags) +{ + MBinBuffer buf; + + while (true) { + char data[1000]; + int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags); + if (recvResult <= 0 || recvResult >= _countof(data)) + return SOCKET_ERROR; + + buf.append(data, recvResult); // add chunk + + auto *peol1 = (const char*)memchr(buf.data(), '\n', buf.length()); + if (peol1 == nullptr) + continue; + + auto *pStart = (const char *)buf.data(); + int cbRest = int(peol1 - pStart) + 1; + const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest); + if (peol2 == nullptr) + continue; + + int sz = peol2 - pStart + 1; + int r = strtol(first ? pStart : peol1 + 1, nullptr, 16); + if (r == 0) { + const char *peol3 = strchr(peol2 + 1, '\n'); + if (peol3 == nullptr) + continue; + sz = peol3 - pStart + 1; + } + buf.remove(sz); // remove all our data from buffer + nlc->foreBuf.appendBefore(buf.data(), buf.length()); + return r; + } +} + +NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect) +{ + int dataLen = -1, i, chunkhdr = 0; + bool chunked = false; + int cenc = 0, cenctype = 0, close = 0; + +next: + NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags); + if (nlhrReply == nullptr) + return nullptr; + + if (nlhrReply->resultCode == 100) { + Netlib_FreeHttpRequest(nlhrReply); + goto next; + } + + if (nlhrReply->resultCode == 204) + dataLen = 0; + + for (i = 0; i < nlhrReply->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhrReply->headers[i]; + if (!mir_strcmpi(p.szName, "Content-Length")) + dataLen = atoi(p.szValue); + + if (!mir_strcmpi(p.szName, "Content-Encoding")) { + cenc = i; + if (strstr(p.szValue, "gzip")) + cenctype = 1; + else if (strstr(p.szValue, "deflate")) + cenctype = 2; + } + + if (!mir_strcmpi(p.szName, "Connection")) + close = !mir_strcmpi(p.szValue, "close"); + + if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) { + chunked = true; + chunkhdr = i; + dataLen = -1; + } + } + + if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) { + int recvResult, chunksz = -1; + int dataBufferAlloced; + + if (chunked) { + chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0)); + if (chunksz == SOCKET_ERROR) { + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + dataLen = chunksz; + } + dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1; + nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); + + while (chunksz != 0) { + while (true) { + recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT, + nlhrReply->pData + nlhrReply->dataLength, + dataBufferAlloced - nlhrReply->dataLength - 1, + dflags | (cenctype ? MSG_NODUMP : 0)); + + if (recvResult == 0) break; + if (recvResult == SOCKET_ERROR) { + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + nlhrReply->dataLength += recvResult; + + if (dataLen >= 0) { + if (nlhrReply->dataLength >= dataLen) + break; + } + else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) { + dataBufferAlloced += 2048; + nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); + if (nlhrReply->pData == nullptr) { + SetLastError(ERROR_OUTOFMEMORY); + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + } + Sleep(10); + } + + if (!chunked) + break; + + chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP); + if (chunksz == SOCKET_ERROR) { + Netlib_FreeHttpRequest(nlhrReply); + return nullptr; + } + dataLen += chunksz; + dataBufferAlloced += chunksz; + + nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced); + } + + nlhrReply->pData[nlhrReply->dataLength] = '\0'; + } + + if (chunked) { + nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16); + mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length"); + + nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16); + mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength); + } + + if (cenctype) { + int bufsz = nlhrReply->dataLength; + char* szData = nullptr; + + switch (cenctype) { + case 1: + szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS); + break; + + case 2: + szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS); + if (bufsz < 0) { + bufsz = nlhrReply->dataLength; + szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS); + } + break; + } + + if (bufsz > 0) { + Netlib_Dump(nlc, (uint8_t*)szData, bufsz, false, dflags | MSG_NOTITLE); + mir_free(nlhrReply->pData); + nlhrReply->pData = szData; + nlhrReply->dataLength = bufsz; + + mir_free(nlhrReply->headers[cenc].szName); + mir_free(nlhrReply->headers[cenc].szValue); + memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0])); + } + else if (bufsz == 0) { + mir_free(nlhrReply->pData); + nlhrReply->pData = nullptr; + nlhrReply->dataLength = 0; + } + } + + if (close && + (nlc->proxyType != PROXYTYPE_HTTP || nlc->url.flags & NLOCF_SSL) && + (!isConnect || nlhrReply->resultCode != 200)) + NetlibDoCloseSocket(nlc); + + return nlhrReply; +} diff --git a/src/mir_app/src/netlib_httpproxy.cpp b/src/mir_app/src/netlib_httpproxy.cpp index 4ea58fcd57..66adc7fe9a 100644 --- a/src/mir_app/src/netlib_httpproxy.cpp +++ b/src/mir_app/src/netlib_httpproxy.cpp @@ -1,85 +1,85 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -typedef enum -{ - reqHelloGet, - reqOldGet, - reqOldPost, - reqNewPost, -} -RequestType; - -///////////////////////////////////////////////////////////////////////////////////////// - -#define NETLIBHTTP_RETRYCOUNT 3 -#define NETLIBHTTP_RETRYTIMEOUT 2000 - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - mir_free(nlc->nlhpi.szHttpGetUrl); - mir_free(nlc->nlhpi.szHttpPostUrl); - - nlc->nlhpi.combinePackets = 1; - memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi)); - if (nlc->nlhpi.combinePackets == 0) - nlc->nlhpi.combinePackets = 1; - - nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl); - nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders) -{ - if (GetNetlibHandleType(nlu) != NLH_USER) - return ERROR_INVALID_PARAMETER; - - replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return -1; - - int oldTimeout = nlc->pollingTimeout; - nlc->pollingTimeout = iTimeout; - return oldTimeout; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +typedef enum +{ + reqHelloGet, + reqOldGet, + reqOldPost, + reqNewPost, +} +RequestType; + +///////////////////////////////////////////////////////////////////////////////////////// + +#define NETLIBHTTP_RETRYCOUNT 3 +#define NETLIBHTTP_RETRYTIMEOUT 2000 + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi) +{ + if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + mir_free(nlc->nlhpi.szHttpGetUrl); + mir_free(nlc->nlhpi.szHttpPostUrl); + + nlc->nlhpi.combinePackets = 1; + memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi)); + if (nlc->nlhpi.combinePackets == 0) + nlc->nlhpi.combinePackets = 1; + + nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl); + nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl); + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders) +{ + if (GetNetlibHandleType(nlu) != NLH_USER) + return ERROR_INVALID_PARAMETER; + + replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout) +{ + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) + return -1; + + int oldTimeout = nlc->pollingTimeout; + nlc->pollingTimeout = iTimeout; + return oldTimeout; +} diff --git a/src/mir_app/src/netlib_log.cpp b/src/mir_app/src/netlib_log.cpp index 4a1cea21df..1ddbb63f6f 100644 --- a/src/mir_app/src/netlib_log.cpp +++ b/src/mir_app/src/netlib_log.cpp @@ -1,578 +1,578 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define MS_NETLIB_LOGWIN "Netlib/Log/Win" - -#define TIMEFORMAT_NONE 0 -#define TIMEFORMAT_HHMMSS 1 -#define TIMEFORMAT_MILLISECONDS 2 -#define TIMEFORMAT_MICROSECONDS 3 -struct { - HWND hwndOpts; - bool toOutputDebugString, toFile, toLog; - bool showUser, rotateLogs, bPrintDate; - bool dumpSent, dumpRecv, dumpProxy, dumpSsl; - bool textDumps, autoDetectText; - int timeFormat; - CMStringW tszFile, tszUserFile; -} -static logOptions = {}; - -struct LOGMSG -{ - const char* pszHead; - const char* pszMsg; -}; - -static __int64 mirandaStartTime, perfCounterFreq; -static int bIsActive = TRUE; -static HANDLE hLogEvent = nullptr; -static HANDLE hLogger = nullptr; - -static void InitLog() -{ - logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", true) != 0; - logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", true) != 0; - logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", true) != 0; - logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", false) != 0; - logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", true) != 0; - logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", true) != 0; - logOptions.bPrintDate = db_get_b(0, "Netlib", "PrintDate", false) != 0; - logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS); - logOptions.rotateLogs = db_get_b(0, "Netlib", "RotateLogs", false); - logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", true) != 0; - logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", false) != 0; - logOptions.toFile = db_get_b(0, "Netlib", "ToFile", false) != 0; - logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", true) != 0; - - if (hLogger) { - mir_closeLog(hLogger); - hLogger = nullptr; - } - - ptrW szBuf(db_get_wsa(0, "Netlib", "File")); - if (mir_wstrlen(szBuf)) { - logOptions.tszUserFile = szBuf.get(); - - wchar_t path[MAX_PATH]; - PathToAbsoluteW(VARSW(szBuf), path); - logOptions.tszFile = path; - } - else { - db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt"); - logOptions.tszFile = VARSW(logOptions.tszUserFile); - } - - if (logOptions.toFile) { - CMStringW wszFileName = logOptions.tszFile; - if (logOptions.rotateLogs) { - int iLogNumber = db_get_dw(0, "Netlib", "RotateId"); - wszFileName.AppendFormat(L".%d", iLogNumber); - db_set_dw(0, "Netlib", "RotateId", (iLogNumber + 1) % 10); - } - - hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), wszFileName, 0); - } -} - -static const wchar_t *szTimeFormats[] = -{ - LPGENW("No times"), - LPGENW("Standard hh:mm:ss times"), - LPGENW("Times in milliseconds"), - LPGENW("Times in microseconds") -}; - -class CLogOptionsDlg : public CDlgBase -{ - CCtrlEdit edtFileName; - CCtrlCombo cmbTimeFormat; - CCtrlButton btnRunNow, btnFileName, btnRunAtStart; - CCtrlTreeView treeFilter; - -public: - CLogOptionsDlg() : - CDlgBase(g_plugin, IDD_NETLIBLOGOPTS), - treeFilter(this, IDC_FILTER), - edtFileName(this, IDC_FILENAME), - cmbTimeFormat(this, IDC_TIMEFORMAT), - btnRunNow(this, IDC_RUNNOW), - btnFileName(this, IDC_FILENAMEBROWSE), - btnRunAtStart(this, IDC_RUNATSTARTBROWSE) - { - btnFileName.OnClick = btnRunAtStart.OnClick = Callback(this, &CLogOptionsDlg::onClick_Browse); - - edtFileName.OnChange = Callback(this, &CLogOptionsDlg::onChange_FileName); - } - - bool OnInitDialog() override - { - logOptions.hwndOpts = m_hwnd; - - CheckDlgButton(m_hwnd, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWDATE, logOptions.bPrintDate ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_LOGROTATE, logOptions.rotateLogs ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED); - CheckDlgButton(m_hwnd, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED); - - for (auto &it : szTimeFormats) - cmbTimeFormat.AddString(TranslateW(it)); - cmbTimeFormat.SetCurSel(logOptions.timeFormat); - - edtFileName.SetText(logOptions.tszUserFile); - SetDlgItemText(m_hwnd, IDC_PATH, logOptions.tszFile); - - ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart")); - if (szRun) - SetDlgItemTextA(m_hwnd, IDC_RUNATSTART, szRun); - - SetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES)); - - TVINSERTSTRUCT tvis = {}; - tvis.hInsertAfter = TVI_SORT; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; - tvis.item.stateMask = TVIS_STATEIMAGEMASK; - - for (auto &it : netlibUser) { - tvis.item.pszText = it->user.szDescriptiveName.w; - tvis.item.lParam = netlibUser.indexOf(&it); - tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1); - treeFilter.InsertItem(&tvis); - } - - tvis.item.lParam = -1; - tvis.item.pszText = TranslateT("(Miranda core logging)"); - tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1); - treeFilter.InsertItem(&tvis); - return true; - } - - bool OnApply() override - { - wchar_t str[MAX_PATH]; - GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); - db_set_ws(0, "Netlib", "RunAtStart", str); - - edtFileName.GetText(str, _countof(str)); - logOptions.tszUserFile = rtrimw(str); - db_set_ws(0, "Netlib", "File", str); - - GetDlgItemText(m_hwnd, IDC_PATH, str, _countof(str)); - logOptions.tszFile = rtrimw(str); - - db_set_b(0, "Netlib", "ToFile", IsDlgButtonChecked(m_hwnd, IDC_TOFILE)); - db_set_b(0, "Netlib", "DumpSsl", IsDlgButtonChecked(m_hwnd, IDC_DUMPSSL)); - db_set_b(0, "Netlib", "DumpRecv", IsDlgButtonChecked(m_hwnd, IDC_DUMPRECV)); - db_set_b(0, "Netlib", "DumpSent", IsDlgButtonChecked(m_hwnd, IDC_DUMPSENT)); - db_set_b(0, "Netlib", "ShowUser", IsDlgButtonChecked(m_hwnd, IDC_SHOWNAMES)); - db_set_b(0, "Netlib", "DumpProxy", IsDlgButtonChecked(m_hwnd, IDC_DUMPPROXY)); - db_set_b(0, "Netlib", "PrintDate", IsDlgButtonChecked(m_hwnd, IDC_SHOWDATE)); - db_set_b(0, "Netlib", "TextDumps", IsDlgButtonChecked(m_hwnd, IDC_TEXTDUMPS)); - db_set_b(0, "Netlib", "RotateLogs", IsDlgButtonChecked(m_hwnd, IDC_LOGROTATE)); - db_set_b(0, "Netlib", "AutoDetectText", IsDlgButtonChecked(m_hwnd, IDC_AUTODETECTTEXT)); - db_set_b(0, "Netlib", "ShowLogOptsAtStart", IsDlgButtonChecked(m_hwnd, IDC_SHOWTHISDLGATSTART)); - db_set_b(0, "Netlib", "ToOutputDebugString", IsDlgButtonChecked(m_hwnd, IDC_TOOUTPUTDEBUGSTRING)); - - db_set_b(0, "Netlib", "TimeFormat", cmbTimeFormat.GetCurSel()); - - TVITEMEX tvi = {}; - tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; - tvi.hItem = treeFilter.GetRoot(); - - while (tvi.hItem) { - treeFilter.GetItem(&tvi); - bool checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2); - - if (tvi.lParam == -1) { - logOptions.toLog = checked; - db_set_dw(0, "Netlib", "NLlog", checked); - } - else if (tvi.lParam < netlibUser.getCount()) { - netlibUser[tvi.lParam]->toLog = checked; - db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked); - } - - tvi.hItem = treeFilter.GetNextSibling(tvi.hItem); - } - - InitLog(); - return true; - } - - void OnDestroy() override - { - ImageList_Destroy(TreeView_GetImageList(GetDlgItem(m_hwnd, IDC_FILTER), TVSIL_STATE)); - logOptions.hwndOpts = nullptr; - } - - void onChange_FileName(CCtrlEdit *pEdit) - { - if (pEdit->GetHwnd() == GetFocus()) - CheckDlgButton(m_hwnd, IDC_TOFILE, BST_CHECKED); - - wchar_t path[MAX_PATH]; - pEdit->GetText(path, _countof(path)); - PathToAbsoluteW(VARSW(path), path); - SetDlgItemText(m_hwnd, IDC_PATH, path); - } - - void onClick_Browse(CCtrlButton *pButton) - { - wchar_t str[MAX_PATH]; - GetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str, _countof(str)); - - wchar_t filter[200]; - mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0); - - OPENFILENAME ofn = { 0 }; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - ofn.hwndOwner = m_hwnd; - ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT; - if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) - ofn.lpstrTitle = TranslateT("Select where log file will be created"); - else { - ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - ofn.lpstrTitle = TranslateT("Select program to be run"); - } - ofn.lpstrFilter = filter; - ofn.lpstrFile = str; - ofn.nMaxFile = _countof(str) - 2; - ofn.nMaxFileTitle = MAX_PATH; - if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) { - if (!GetSaveFileName(&ofn)) - return; - } - else if (!GetOpenFileName(&ofn)) - return; - - if (pButton->GetCtrlId() == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) { - memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t))); - str[0] = '"'; - mir_wstrcat(str, L"\""); - } - SetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str); - } - - void onClick_RunNow(CCtrlButton *) - { - wchar_t str[MAX_PATH]; - GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); - if (!str[0]) - return; - - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcessW(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } -}; - -void NetlibLogShowOptions(void) -{ - if (logOptions.hwndOpts == nullptr) - (new CLogOptionsDlg())->Create(); - SetForegroundWindow(logOptions.hwndOpts); -} - -static INT_PTR ShowOptions(WPARAM, LPARAM) -{ - NetlibLogShowOptions(); - return 0; -} - -int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags) -{ - if (!bIsActive) - return 0; - - uint32_t dwOriginalLastError = GetLastError(); - - if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - /* if the Netlib user handle is nullptr, just pretend its not */ - if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog)) - return 1; - - LARGE_INTEGER liTimeNow; - char szDate[32], szTime[32], szHead[128]; - switch (logOptions.timeFormat) { - case TIMEFORMAT_HHMMSS: - GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime)); - mir_strcat(szTime, " "); - break; - - case TIMEFORMAT_MILLISECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - case TIMEFORMAT_MICROSECONDS: - QueryPerformanceCounter(&liTimeNow); - liTimeNow.QuadPart -= mirandaStartTime; - mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq, - 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); - break; - - default: - szTime[0] = 0; - break; - } - - if (logOptions.bPrintDate) { - GetDateFormatA(LOCALE_USER_DEFAULT, 0, nullptr, "yyyy-MM-dd", szDate, _countof(szDate)); - mir_strcat(szDate, " "); - } - else szDate[0] = 0; - - if (flags & MSG_NOTITLE) - szHead[0] = 0; - else { - char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr; - if (szUser) - mir_snprintf(szHead, "[%s%s%04X] [%s] ", szDate, szTime, GetCurrentThreadId(), szUser); - else - mir_snprintf(szHead, "[%s%s%04X] ", szDate, szTime, GetCurrentThreadId()); - } - - if (logOptions.toOutputDebugString) { - if (szHead[0]) - OutputDebugStringA(szHead); - OutputDebugStringA(pszMsg); - OutputDebugStringA("\n"); - } - - if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) { - size_t len = mir_strlen(pszMsg); - mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n"); - } - - LOGMSG logMsg = { szHead, pszMsg }; - NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg); - - SetLastError(dwOriginalLastError); - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void PROTO_INTERFACE::debugLogA(const char *szFormat, ...) -{ - char buf[4096]; - va_list args; - va_start(args, szFormat); - int res = _vsnprintf(buf, _countof(buf), szFormat, args); - NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0); - va_end(args); -} - -void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...) -{ - wchar_t buf[4096]; - va_list args; - va_start(args, wszFormat); - int res = _vsnwprintf(buf, _countof(buf), wszFormat, args); - NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0); - va_end(args); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - char szText[8000]; - mir_vsnprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, szText, 0); -} - -MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...) -{ - va_list va; - va_start(va, fmt); - wchar_t szText[8000]; - mir_vsnwprintf(szText, _countof(szText), fmt, va); - va_end(va); - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr) -{ - return NetlibLog_Worker(hUser, pszStr, 0); -} - -MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr) -{ - return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *pBuf, size_t len, bool bIsSent, int flags) -{ - // This section checks a number of conditions and aborts - // the dump if the data should not be written to the log - - // Check packet flags - if (flags & (MSG_PEEK | MSG_NODUMP)) - return; - - // Check user's log settings - if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty()))) - return; - if ((bIsSent && !logOptions.dumpSent) || (!bIsSent && !logOptions.dumpRecv)) - return; - if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy) - return; - if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl) - return; - - NetlibUser *nlu; - CMStringA str; - { - mir_cslock lock(csConnectionHeader); - - nlu = nlc ? nlc->nlu : nullptr; - if (!(flags & MSG_NOTITLE)) - str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, bIsSent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : ""); - } - - // check filter settings - if (nlu == nullptr) { - if (!logOptions.toLog) - return; - } - else if (!nlu->toLog) - return; - - const uint8_t *buf = (const uint8_t *)pBuf; - - bool isText = true; - if (!logOptions.textDumps) - isText = false; - else if (!(flags & MSG_DUMPASTEXT)) { - if (logOptions.autoDetectText) { - for (size_t i = 0; i < len; i++) { - if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) { - isText = false; - break; - } - } - } - else isText = false; - } - - // Text data - if (isText) { - str.Append((const char*)buf, (int)len); - } - // Binary data - else { - for (int line = 0;; line += 16) { - auto *p = buf + line; - int colsInLine = min(16, (int)len - line); - if (colsInLine == 16) - str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ", - line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); - else { - str.AppendFormat("%08X: ", line); - - // Dump data as hex - int col; - for (col = 0; col < colsInLine; col++) - str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' '); - - // Fill out last line with blanks - for (; col < 16; col++) - str.Append(" "); - - str.AppendChar(' '); - } - - for (int col = 0; col < colsInLine; col++) - str.AppendChar((p[col] < ' ') ? '.' : p[col]); - - if (len - line <= 16) - break; - - str.AppendChar('\r'); // End each line with a break - str.AppendChar('\n'); // End each line with a break - } - } - - NetlibLog_Worker(nlu, str, flags); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void NetlibLogInit(void) -{ - LARGE_INTEGER li; - QueryPerformanceFrequency(&li); - perfCounterFreq = li.QuadPart; - QueryPerformanceCounter(&li); - mirandaStartTime = li.QuadPart; - - CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions); - hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP); - - InitLog(); - - if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0)) - NetlibLogShowOptions(); - - ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart")); - if (szBuf) { - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi; - CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); - } -} - -void NetlibLogShutdown(void) -{ - bIsActive = FALSE; - DestroyHookableEvent(hLogEvent); hLogEvent = nullptr; - if (IsWindow(logOptions.hwndOpts)) - DestroyWindow(logOptions.hwndOpts); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#define MS_NETLIB_LOGWIN "Netlib/Log/Win" + +#define TIMEFORMAT_NONE 0 +#define TIMEFORMAT_HHMMSS 1 +#define TIMEFORMAT_MILLISECONDS 2 +#define TIMEFORMAT_MICROSECONDS 3 +struct { + HWND hwndOpts; + bool toOutputDebugString, toFile, toLog; + bool showUser, rotateLogs, bPrintDate; + bool dumpSent, dumpRecv, dumpProxy, dumpSsl; + bool textDumps, autoDetectText; + int timeFormat; + CMStringW tszFile, tszUserFile; +} +static logOptions = {}; + +struct LOGMSG +{ + const char* pszHead; + const char* pszMsg; +}; + +static __int64 mirandaStartTime, perfCounterFreq; +static int bIsActive = TRUE; +static HANDLE hLogEvent = nullptr; +static HANDLE hLogger = nullptr; + +static void InitLog() +{ + logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", true) != 0; + logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", true) != 0; + logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", true) != 0; + logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", false) != 0; + logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", true) != 0; + logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", true) != 0; + logOptions.bPrintDate = db_get_b(0, "Netlib", "PrintDate", false) != 0; + logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS); + logOptions.rotateLogs = db_get_b(0, "Netlib", "RotateLogs", false); + logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", true) != 0; + logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", false) != 0; + logOptions.toFile = db_get_b(0, "Netlib", "ToFile", false) != 0; + logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", true) != 0; + + if (hLogger) { + mir_closeLog(hLogger); + hLogger = nullptr; + } + + ptrW szBuf(db_get_wsa(0, "Netlib", "File")); + if (mir_wstrlen(szBuf)) { + logOptions.tszUserFile = szBuf.get(); + + wchar_t path[MAX_PATH]; + PathToAbsoluteW(VARSW(szBuf), path); + logOptions.tszFile = path; + } + else { + db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt"); + logOptions.tszFile = VARSW(logOptions.tszUserFile); + } + + if (logOptions.toFile) { + CMStringW wszFileName = logOptions.tszFile; + if (logOptions.rotateLogs) { + int iLogNumber = db_get_dw(0, "Netlib", "RotateId"); + wszFileName.AppendFormat(L".%d", iLogNumber); + db_set_dw(0, "Netlib", "RotateId", (iLogNumber + 1) % 10); + } + + hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), wszFileName, 0); + } +} + +static const wchar_t *szTimeFormats[] = +{ + LPGENW("No times"), + LPGENW("Standard hh:mm:ss times"), + LPGENW("Times in milliseconds"), + LPGENW("Times in microseconds") +}; + +class CLogOptionsDlg : public CDlgBase +{ + CCtrlEdit edtFileName; + CCtrlCombo cmbTimeFormat; + CCtrlButton btnRunNow, btnFileName, btnRunAtStart; + CCtrlTreeView treeFilter; + +public: + CLogOptionsDlg() : + CDlgBase(g_plugin, IDD_NETLIBLOGOPTS), + treeFilter(this, IDC_FILTER), + edtFileName(this, IDC_FILENAME), + cmbTimeFormat(this, IDC_TIMEFORMAT), + btnRunNow(this, IDC_RUNNOW), + btnFileName(this, IDC_FILENAMEBROWSE), + btnRunAtStart(this, IDC_RUNATSTARTBROWSE) + { + btnFileName.OnClick = btnRunAtStart.OnClick = Callback(this, &CLogOptionsDlg::onClick_Browse); + + edtFileName.OnChange = Callback(this, &CLogOptionsDlg::onChange_FileName); + } + + bool OnInitDialog() override + { + logOptions.hwndOpts = m_hwnd; + + CheckDlgButton(m_hwnd, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_SHOWDATE, logOptions.bPrintDate ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_LOGROTATE, logOptions.rotateLogs ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(m_hwnd, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED); + + for (auto &it : szTimeFormats) + cmbTimeFormat.AddString(TranslateW(it)); + cmbTimeFormat.SetCurSel(logOptions.timeFormat); + + edtFileName.SetText(logOptions.tszUserFile); + SetDlgItemText(m_hwnd, IDC_PATH, logOptions.tszFile); + + ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart")); + if (szRun) + SetDlgItemTextA(m_hwnd, IDC_RUNATSTART, szRun); + + SetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeFilter.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES)); + + TVINSERTSTRUCT tvis = {}; + tvis.hInsertAfter = TVI_SORT; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE; + tvis.item.stateMask = TVIS_STATEIMAGEMASK; + + for (auto &it : netlibUser) { + tvis.item.pszText = it->user.szDescriptiveName.w; + tvis.item.lParam = netlibUser.indexOf(&it); + tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1); + treeFilter.InsertItem(&tvis); + } + + tvis.item.lParam = -1; + tvis.item.pszText = TranslateT("(Miranda core logging)"); + tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1); + treeFilter.InsertItem(&tvis); + return true; + } + + bool OnApply() override + { + wchar_t str[MAX_PATH]; + GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); + db_set_ws(0, "Netlib", "RunAtStart", str); + + edtFileName.GetText(str, _countof(str)); + logOptions.tszUserFile = rtrimw(str); + db_set_ws(0, "Netlib", "File", str); + + GetDlgItemText(m_hwnd, IDC_PATH, str, _countof(str)); + logOptions.tszFile = rtrimw(str); + + db_set_b(0, "Netlib", "ToFile", IsDlgButtonChecked(m_hwnd, IDC_TOFILE)); + db_set_b(0, "Netlib", "DumpSsl", IsDlgButtonChecked(m_hwnd, IDC_DUMPSSL)); + db_set_b(0, "Netlib", "DumpRecv", IsDlgButtonChecked(m_hwnd, IDC_DUMPRECV)); + db_set_b(0, "Netlib", "DumpSent", IsDlgButtonChecked(m_hwnd, IDC_DUMPSENT)); + db_set_b(0, "Netlib", "ShowUser", IsDlgButtonChecked(m_hwnd, IDC_SHOWNAMES)); + db_set_b(0, "Netlib", "DumpProxy", IsDlgButtonChecked(m_hwnd, IDC_DUMPPROXY)); + db_set_b(0, "Netlib", "PrintDate", IsDlgButtonChecked(m_hwnd, IDC_SHOWDATE)); + db_set_b(0, "Netlib", "TextDumps", IsDlgButtonChecked(m_hwnd, IDC_TEXTDUMPS)); + db_set_b(0, "Netlib", "RotateLogs", IsDlgButtonChecked(m_hwnd, IDC_LOGROTATE)); + db_set_b(0, "Netlib", "AutoDetectText", IsDlgButtonChecked(m_hwnd, IDC_AUTODETECTTEXT)); + db_set_b(0, "Netlib", "ShowLogOptsAtStart", IsDlgButtonChecked(m_hwnd, IDC_SHOWTHISDLGATSTART)); + db_set_b(0, "Netlib", "ToOutputDebugString", IsDlgButtonChecked(m_hwnd, IDC_TOOUTPUTDEBUGSTRING)); + + db_set_b(0, "Netlib", "TimeFormat", cmbTimeFormat.GetCurSel()); + + TVITEMEX tvi = {}; + tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; + tvi.hItem = treeFilter.GetRoot(); + + while (tvi.hItem) { + treeFilter.GetItem(&tvi); + bool checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2); + + if (tvi.lParam == -1) { + logOptions.toLog = checked; + db_set_dw(0, "Netlib", "NLlog", checked); + } + else if (tvi.lParam < netlibUser.getCount()) { + netlibUser[tvi.lParam]->toLog = checked; + db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked); + } + + tvi.hItem = treeFilter.GetNextSibling(tvi.hItem); + } + + InitLog(); + return true; + } + + void OnDestroy() override + { + ImageList_Destroy(TreeView_GetImageList(GetDlgItem(m_hwnd, IDC_FILTER), TVSIL_STATE)); + logOptions.hwndOpts = nullptr; + } + + void onChange_FileName(CCtrlEdit *pEdit) + { + if (pEdit->GetHwnd() == GetFocus()) + CheckDlgButton(m_hwnd, IDC_TOFILE, BST_CHECKED); + + wchar_t path[MAX_PATH]; + pEdit->GetText(path, _countof(path)); + PathToAbsoluteW(VARSW(path), path); + SetDlgItemText(m_hwnd, IDC_PATH, path); + } + + void onClick_Browse(CCtrlButton *pButton) + { + wchar_t str[MAX_PATH]; + GetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str, _countof(str)); + + wchar_t filter[200]; + mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0); + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + ofn.hwndOwner = m_hwnd; + ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT; + if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) + ofn.lpstrTitle = TranslateT("Select where log file will be created"); + else { + ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + ofn.lpstrTitle = TranslateT("Select program to be run"); + } + ofn.lpstrFilter = filter; + ofn.lpstrFile = str; + ofn.nMaxFile = _countof(str) - 2; + ofn.nMaxFileTitle = MAX_PATH; + if (pButton->GetCtrlId() == IDC_FILENAMEBROWSE) { + if (!GetSaveFileName(&ofn)) + return; + } + else if (!GetOpenFileName(&ofn)) + return; + + if (pButton->GetCtrlId() == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) { + memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t))); + str[0] = '"'; + mir_wstrcat(str, L"\""); + } + SetWindowText(GetWindow(pButton->GetHwnd(), GW_HWNDPREV), str); + } + + void onClick_RunNow(CCtrlButton *) + { + wchar_t str[MAX_PATH]; + GetDlgItemText(m_hwnd, IDC_RUNATSTART, str, _countof(str)); + if (!str[0]) + return; + + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + CreateProcessW(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } +}; + +void NetlibLogShowOptions(void) +{ + if (logOptions.hwndOpts == nullptr) + (new CLogOptionsDlg())->Create(); + SetForegroundWindow(logOptions.hwndOpts); +} + +static INT_PTR ShowOptions(WPARAM, LPARAM) +{ + NetlibLogShowOptions(); + return 0; +} + +int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags) +{ + if (!bIsActive) + return 0; + + uint32_t dwOriginalLastError = GetLastError(); + + if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + /* if the Netlib user handle is nullptr, just pretend its not */ + if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog)) + return 1; + + LARGE_INTEGER liTimeNow; + char szDate[32], szTime[32], szHead[128]; + switch (logOptions.timeFormat) { + case TIMEFORMAT_HHMMSS: + GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime)); + mir_strcat(szTime, " "); + break; + + case TIMEFORMAT_MILLISECONDS: + QueryPerformanceCounter(&liTimeNow); + liTimeNow.QuadPart -= mirandaStartTime; + mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq, + 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); + break; + + case TIMEFORMAT_MICROSECONDS: + QueryPerformanceCounter(&liTimeNow); + liTimeNow.QuadPart -= mirandaStartTime; + mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq, + 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq); + break; + + default: + szTime[0] = 0; + break; + } + + if (logOptions.bPrintDate) { + GetDateFormatA(LOCALE_USER_DEFAULT, 0, nullptr, "yyyy-MM-dd", szDate, _countof(szDate)); + mir_strcat(szDate, " "); + } + else szDate[0] = 0; + + if (flags & MSG_NOTITLE) + szHead[0] = 0; + else { + char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr; + if (szUser) + mir_snprintf(szHead, "[%s%s%04X] [%s] ", szDate, szTime, GetCurrentThreadId(), szUser); + else + mir_snprintf(szHead, "[%s%s%04X] ", szDate, szTime, GetCurrentThreadId()); + } + + if (logOptions.toOutputDebugString) { + if (szHead[0]) + OutputDebugStringA(szHead); + OutputDebugStringA(pszMsg); + OutputDebugStringA("\n"); + } + + if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) { + size_t len = mir_strlen(pszMsg); + mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n"); + } + + LOGMSG logMsg = { szHead, pszMsg }; + NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg); + + SetLastError(dwOriginalLastError); + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void PROTO_INTERFACE::debugLogA(const char *szFormat, ...) +{ + char buf[4096]; + va_list args; + va_start(args, szFormat); + int res = _vsnprintf(buf, _countof(buf), szFormat, args); + NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0); + va_end(args); +} + +void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...) +{ + wchar_t buf[4096]; + va_list args; + va_start(args, wszFormat); + int res = _vsnwprintf(buf, _countof(buf), wszFormat, args); + NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0); + va_end(args); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + char szText[8000]; + mir_vsnprintf(szText, _countof(szText), fmt, va); + va_end(va); + return NetlibLog_Worker(hUser, szText, 0); +} + +MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...) +{ + va_list va; + va_start(va, fmt); + wchar_t szText[8000]; + mir_vsnwprintf(szText, _countof(szText), fmt, va); + va_end(va); + return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr) +{ + return NetlibLog_Worker(hUser, pszStr, 0); +} + +MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr) +{ + return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(void) Netlib_Dump(HNETLIBCONN nlc, const void *pBuf, size_t len, bool bIsSent, int flags) +{ + // This section checks a number of conditions and aborts + // the dump if the data should not be written to the log + + // Check packet flags + if (flags & (MSG_PEEK | MSG_NODUMP)) + return; + + // Check user's log settings + if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty()))) + return; + if ((bIsSent && !logOptions.dumpSent) || (!bIsSent && !logOptions.dumpRecv)) + return; + if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy) + return; + if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl) + return; + + NetlibUser *nlu; + CMStringA str; + { + mir_cslock lock(csConnectionHeader); + + nlu = nlc ? nlc->nlu : nullptr; + if (!(flags & MSG_NOTITLE)) + str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, bIsSent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : ""); + } + + // check filter settings + if (nlu == nullptr) { + if (!logOptions.toLog) + return; + } + else if (!nlu->toLog) + return; + + const uint8_t *buf = (const uint8_t *)pBuf; + + bool isText = true; + if (!logOptions.textDumps) + isText = false; + else if (!(flags & MSG_DUMPASTEXT)) { + if (logOptions.autoDetectText) { + for (size_t i = 0; i < len; i++) { + if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) { + isText = false; + break; + } + } + } + else isText = false; + } + + // Text data + if (isText) { + str.Append((const char*)buf, (int)len); + } + // Binary data + else { + for (int line = 0;; line += 16) { + auto *p = buf + line; + int colsInLine = min(16, (int)len - line); + if (colsInLine == 16) + str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ", + line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + else { + str.AppendFormat("%08X: ", line); + + // Dump data as hex + int col; + for (col = 0; col < colsInLine; col++) + str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' '); + + // Fill out last line with blanks + for (; col < 16; col++) + str.Append(" "); + + str.AppendChar(' '); + } + + for (int col = 0; col < colsInLine; col++) + str.AppendChar((p[col] < ' ') ? '.' : p[col]); + + if (len - line <= 16) + break; + + str.AppendChar('\r'); // End each line with a break + str.AppendChar('\n'); // End each line with a break + } + } + + NetlibLog_Worker(nlu, str, flags); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void NetlibLogInit(void) +{ + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + perfCounterFreq = li.QuadPart; + QueryPerformanceCounter(&li); + mirandaStartTime = li.QuadPart; + + CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions); + hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP); + + InitLog(); + + if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0)) + NetlibLogShowOptions(); + + ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart")); + if (szBuf) { + STARTUPINFO si = { sizeof(si) }; + PROCESS_INFORMATION pi; + CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); + } +} + +void NetlibLogShutdown(void) +{ + bIsActive = FALSE; + DestroyHookableEvent(hLogEvent); hLogEvent = nullptr; + if (IsWindow(logOptions.hwndOpts)) + DestroyWindow(logOptions.hwndOpts); +} diff --git a/src/mir_app/src/netlib_openconn.cpp b/src/mir_app/src/netlib_openconn.cpp index 49e584e4d3..7d9b0b9114 100644 --- a/src/mir_app/src/netlib_openconn.cpp +++ b/src/mir_app/src/netlib_openconn.cpp @@ -1,726 +1,726 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern mir_cs csNetlibUser; -extern uint32_t g_LastConnectionTick; -extern int connectionTimeout; -static int iUPnPCleanup = 0; - -#define RECV_DEFAULT_TIMEOUT 60000 - -//returns in network byte order -uint32_t DnsLookup(NetlibUser *nlu, const char *szHost) -{ - HOSTENT* host; - uint32_t ip = inet_addr(szHost); - if (ip != INADDR_NONE) - return ip; - - __try { - host = gethostbyname(szHost); - if (host) - return *(u_long*)host->h_addr_list[0]; - - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError()); - } - __except (EXCEPTION_EXECUTE_HANDLER) {} - - return 0; -} - -int WaitUntilReadable(SOCKET s, uint32_t dwTimeout, bool check) -{ - fd_set readfd; - TIMEVAL tv; - - if (s == INVALID_SOCKET) return SOCKET_ERROR; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - int result = select(0, &readfd, nullptr, nullptr, &tv); - if (result == 0 && !check) SetLastError(ERROR_TIMEOUT); - return result; -} - -int WaitUntilWritable(SOCKET s, uint32_t dwTimeout) -{ - fd_set writefd; - TIMEVAL tv; - - tv.tv_sec = dwTimeout / 1000; - tv.tv_usec = (dwTimeout % 1000) * 1000; - - FD_ZERO(&writefd); - FD_SET(s, &writefd); - - switch (select(0, nullptr, &writefd, nullptr, &tv)) { - case 0: - SetLastError(ERROR_TIMEOUT); - case SOCKET_ERROR: - return 0; - } - return 1; -} - -bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, uint32_t dwTimeout) -{ - int nReceived = 0; - uint32_t dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout; - - while ((dwTimeNow = GetTickCount()) < dwCompleteTime) { - if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false; - nReceived = Netlib_Recv(nlc, buf, len, flags); - if (nReceived <= 0) return false; - - buf += nReceived; - len -= nReceived; - if (len <= 0) return true; - } - SetLastError(ERROR_TIMEOUT); - return false; -} - -static int NetlibInitSocks4Connection(NetlibConnection *nlc) -{ - // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol - NetlibUrl &url = nlc->url; - if (url.szHost.IsEmpty()) - return 0; - - NetlibUser *nlu = nlc->nlu; - size_t nHostLen = mir_strlen(url.szHost) + 1; - size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1; - size_t len = 8 + nUserLen; - - char* pInit = (char*)alloca(len + nHostLen); - pInit[0] = 4; // SOCKS4 - pInit[1] = 1; //connect - *(PWORD)&pInit[2] = htons(url.port); - - if (nUserLen <= 1) pInit[8] = 0; - else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen); - - //if cannot resolve host, try resolving through proxy (requires SOCKS4a) - uint32_t ip = DnsLookup(nlu, url.szHost); - *(PDWORD)&pInit[4] = ip ? ip : 0x01000000; - if (!ip) { - memcpy(&pInit[len], url.szHost, nHostLen); - len += nHostLen; - } - - if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - char reply[8]; - if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - switch ((uint8_t)reply[1]) { - case 90: return 1; - case 91: SetLastError(ERROR_ACCESS_DENIED); break; - case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; - case 93: SetLastError(ERROR_INVALID_ACCESS); break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (uint8_t)reply[1], GetLastError()); - return 0; -} - -static int NetlibInitSocks5Connection(NetlibConnection *nlc) -{ - //rfc1928 - uint8_t buf[258]; - NetlibUser *nlu = nlc->nlu; - - buf[0] = 5; //yep, socks5 - buf[1] = 1; //one auth method - buf[2] = nlu->settings.useProxyAuth ? 2 : 0; - if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - return 0; - } - - //confirmation of auth method - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if ((buf[1] != 0 && buf[1] != 2)) { - SetLastError(ERROR_INVALID_ID_AUTHORITY); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError()); - return 0; - } - - if (buf[1] == 2) { //rfc1929 - size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser); - size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword); - uint8_t *pAuthBuf = (uint8_t*)mir_alloc(3 + nUserLen + nPassLen); - pAuthBuf[0] = 1; //auth version - pAuthBuf[1] = (uint8_t)nUserLen; - memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen); - pAuthBuf[2 + nUserLen] = (uint8_t)nPassLen; - memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen); - if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pAuthBuf); - return 0; - } - mir_free(pAuthBuf); - - if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - if (buf[1]) { - SetLastError(ERROR_ACCESS_DENIED); - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - } - - size_t nHostLen; - uint32_t hostIP; - - NetlibUrl &url = nlc->url; - if (nlc->dnsThroughProxy) { - hostIP = inet_addr(url.szHost); - nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(url.szHost) + 1 : 4; - } - else { - hostIP = DnsLookup(nlu, url.szHost); - if (hostIP == 0) - return 0; - nHostLen = 4; - } - uint8_t *pInit = (uint8_t*)mir_alloc(6 + nHostLen); - pInit[0] = 5; //SOCKS5 - pInit[1] = url.flags & NLOCF_UDP ? 3 : 1; //connect or UDP - pInit[2] = 0; //reserved - if (hostIP == INADDR_NONE) { //DNS lookup through proxy - pInit[3] = 3; - pInit[4] = uint8_t(nHostLen - 1); - memcpy(pInit + 5, url.szHost, nHostLen - 1); - } - else { - pInit[3] = 1; - *(PDWORD)(pInit + 4) = hostIP; - } - *(PWORD)(pInit + 4 + nHostLen) = htons(url.port); - if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); - mir_free(pInit); - return 0; - } - mir_free(pInit); - - if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - if (buf[0] != 5 || buf[1]) { - const char* err = "Unknown response"; - if (buf[0] != 5) - SetLastError(ERROR_BAD_FORMAT); - else { - switch (buf[1]) { - case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break; - case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break; - case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break; - case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break; - case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break; - case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break; - case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break; - case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break; - default: SetLastError(ERROR_INVALID_DATA); break; - } - } - Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err); - return 0; - } - - int nRecvSize = 0; - switch (buf[3]) { - case 1:// ipv4 addr - nRecvSize = 5; - break; - case 3:// dns name addr - nRecvSize = buf[4] + 2; - break; - case 4:// ipv6 addr - nRecvSize = 17; - break; - default: - Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]); - return 0; - } - if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); - return 0; - } - - //connected - return 1; -} - -static bool NetlibInitHttpsConnection(NetlibConnection *nlc) -{ - // rfc2817 - NetlibUrl &url = nlc->url; - CMStringA szUrl; - if (nlc->dnsThroughProxy) - szUrl.Format("%s:%u", url.szHost.c_str(), url.port); - else { - uint32_t ip = DnsLookup(nlc->nlu, url.szHost); - if (ip == 0) return false; - szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), url.port); - } - - NETLIBHTTPREQUEST nlhrSend = { 0 }; - nlhrSend.cbSize = sizeof(nlhrSend); - nlhrSend.requestType = REQUEST_CONNECT; - nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT; - nlhrSend.szUrl = szUrl.GetBuffer(); - - if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) - return false; - - NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true); - if (nlhrReply == nullptr) - return false; - - if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) { - if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) { - Netlib_FreeHttpRequest(nlhrReply); - nlc->dnsThroughProxy = 0; - return NetlibInitHttpsConnection(nlc); - } - - NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); - Netlib_Logf(nlc->nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__, - nlc->nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr); - Netlib_FreeHttpRequest(nlhrReply); - return 0; - } - Netlib_FreeHttpRequest(nlhrReply); - return true; // connected -} - -static void FreePartiallyInitedConnection(NetlibConnection *nlc) -{ - uint32_t dwOriginalLastError = GetLastError(); - - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) - delete nlc; - - SetLastError(dwOriginalLastError); -} - -static bool my_connectIP(NetlibConnection *nlc) -{ - NetlibUser *nlu = nlc->nlu; - int rc = SOCKET_ERROR, retrycnt = 0; - u_long notblocking = 1; - uint32_t lasterr = 0; - static const TIMEVAL tv = { 1, 0 }; - - // if timeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway - int timeout = (nlc->timeout <= 0) ? 30 : nlc->timeout; - - // this is for XP SP2 where there is a default connection attempt limit of 10/second - if (connectionTimeout) { - WaitForSingleObject(hConnectionOpenMutex, 10000); - int waitdiff = GetTickCount() - g_LastConnectionTick; - if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); - g_LastConnectionTick = GetTickCount(); - ReleaseMutex(hConnectionOpenMutex); - - // might have died in between the wait - if (Miranda_IsTerminated()) - return false; - } - - char szPort[6]; - addrinfo *air = nullptr, *ai, hints = { 0 }; - - hints.ai_family = AF_UNSPEC; - - NetlibUrl &url = nlc->url; - if (url.flags & NLOCF_UDP) { - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - } - else { - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - } - - if (nlc->proxyType) { - if (!nlc->szProxyServer) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, url.szHost.c_str(), url.port); - - _itoa(nlc->wProxyPort, szPort, 10); - if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError()); - return false; - } - } - else { - if (url.szHost.IsEmpty()) - return false; - - Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, url.szHost.c_str(), url.port); - - _itoa(url.port, szPort, 10); - - if (GetAddrInfoA(url.szHost, szPort, &hints, &air)) { - Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", url.szHost.c_str(), WSAGetLastError()); - return false; - } - } - - for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) { - Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get()); -retry: - nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (nlc->s == INVALID_SOCKET) { - FreeAddrInfoA(air); - return false; - } - - // return the socket to non blocking - if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) { - FreeAddrInfoA(air); - return false; - } - - if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { - SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; - SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; - if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum)) - Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); - } - - // try a connect - if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) { - rc = 0; - break; - } - - // didn't work, was it cos of nonblocking? - if (WSAGetLastError() != WSAEWOULDBLOCK) { - rc = SOCKET_ERROR; - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - continue; - } - - while (true) { // timeout loop - fd_set r, w, e; - FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); - FD_SET(nlc->s, &r); - FD_SET(nlc->s, &w); - FD_SET(nlc->s, &e); - if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) - break; - - if (rc > 0) { - if (FD_ISSET(nlc->s, &w)) { - // connection was successful - rc = 0; - lasterr = 0; - } - if (FD_ISSET(nlc->s, &r)) { - // connection was closed - rc = SOCKET_ERROR; - lasterr = WSAECONNRESET; - } - if (FD_ISSET(nlc->s, &e)) { - // connection failed. - int len = sizeof(lasterr); - rc = SOCKET_ERROR; - getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); - if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - goto retry; - } - } - break; - } - else if (Miranda_IsTerminated()) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - - if (--timeout == 0) { - rc = SOCKET_ERROR; - lasterr = ERROR_TIMEOUT; - break; - } - } - - if (rc == 0) break; - - closesocket(nlc->s); - nlc->s = INVALID_SOCKET; - } - - FreeAddrInfoA(air); - - notblocking = 0; - if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); - if (rc && lasterr) SetLastError(lasterr); - return rc == 0; -} - -static int NetlibHttpFallbackToDirect(NetlibConnection *nlc) -{ - NetlibDoCloseSocket(nlc, true); - - Netlib_Logf(nlc->nlu, "Fallback to direct connection"); - - nlc->proxyAuthNeeded = false; - nlc->proxyType = 0; - replaceStr(nlc->szProxyServer, nullptr); - if (!my_connectIP(nlc)) { - Netlib_Logf(nlc->nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - return true; -} - -bool NetlibDoConnect(NetlibConnection *nlc) -{ - NetlibUser *nlu = nlc->nlu; - - replaceStr(nlc->szProxyServer, nullptr); - - bool usingProxy = false, forceHttps = false; - if (nlu->settings.useProxy) { - if (nlu->settings.proxyType == PROXYTYPE_IE) - usingProxy = NetlibGetIeProxyConn(nlc, false); - else { - if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) { - nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer); - nlc->wProxyPort = nlu->settings.wProxyPort; - nlc->proxyType = nlu->settings.proxyType; - usingProxy = true; - } - } - } - - while (!my_connectIP(nlc)) { - // if connection failed, the state of nlc might be unpredictable - if (GetNetlibHandleType(nlc) == NLH_CONNECTION) { - // Fallback to direct only when using HTTP proxy, as this is what used by companies - // If other type of proxy used it's an indication of security nutcase, leave him alone - if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) { - usingProxy = false; - nlc->proxyType = 0; - Netlib_Logf(nlu, "Fallback to direct connection"); - continue; - } - if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) { - forceHttps = true; - usingProxy = NetlibGetIeProxyConn(nlc, true); - if (usingProxy) - continue; - } - } - Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); - return false; - } - - if (usingProxy && !((nlc->url.flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) { - if (!WaitUntilWritable(nlc->s, 30000)) - return false; - - switch (nlc->proxyType) { - case PROXYTYPE_SOCKS4: - if (!NetlibInitSocks4Connection(nlc)) - return false; - break; - - case PROXYTYPE_SOCKS5: - if (!NetlibInitSocks5Connection(nlc)) - return false; - break; - - case PROXYTYPE_HTTPS: - case PROXYTYPE_HTTP: - nlc->proxyAuthNeeded = true; - if (!NetlibInitHttpsConnection(nlc)) { - usingProxy = false; - if (!NetlibHttpFallbackToDirect(nlc)) - return false; - } - break; - - default: - SetLastError(ERROR_INVALID_PARAMETER); - FreePartiallyInitedConnection(nlc); - return false; - } - } - - Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nlc->url.szHost.c_str(), nlc->url.port); - - if (GetSubscribersCount((THook*)hEventConnected)) { - NETLIBCONNECTIONEVENTINFO ncei = {}; - ncei.connected = 1; - ncei.szSettingsModule = nlu->user.szSettingsModule; - int size = sizeof(SOCKADDR_IN); - getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size); - if (nlu->settings.useProxy) { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size); - ncei.remote.sin_family = AF_INET; - ncei.remote.sin_port = htons(nlc->url.port); - ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nlc->url.szHost); - } - else { - size = sizeof(SOCKADDR_IN); - getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size); - } - NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); - } - - if (NLOCF_SSL & nlc->url.flags) - return Netlib_StartSsl(nlc, nullptr) != 0; - - return true; -} - -bool NetlibReconnect(NetlibConnection *nlc) -{ - // a connection might be freed already - if (GetNetlibHandleType(nlc) != NLH_CONNECTION) - return false; - - char buf[4]; - bool opened = nlc->s != INVALID_SOCKET; - if (opened) { - switch (WaitUntilReadable(nlc->s, 0, true)) { - case SOCKET_ERROR: - opened = false; - break; - - case 0: - opened = true; - break; - - case 1: - opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - NetlibDoCloseSocket(nlc, true); - } - - if (!opened) { - if (Miranda_IsTerminated()) - return false; - - return NetlibDoConnect(nlc); - } - return true; -} - -MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const char *szHost, int port, int timeout, int flags) -{ - if (szHost == nullptr || port == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING)) - return nullptr; - - Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", szHost, port, flags); - - NetlibConnection *nlc = new NetlibConnection(); - nlc->nlu = nlu; - nlc->timeout = timeout; - nlc->url.szHost = szHost; - nlc->url.port = port; - nlc->url.flags = flags; - nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0; - - if (!NetlibDoConnect(nlc)) { - FreePartiallyInitedConnection(nlc); - return nullptr; - } - - if (iUPnPCleanup == 0) { - mir_cslock lck(csNetlibUser); - iUPnPCleanup = 1; - mir_forkthread(NetlibUPnPCleanup); - } - - return nlc; -} - -NetlibConnection::NetlibConnection() -{ - handleType = NLH_CONNECTION; - s = s2 = INVALID_SOCKET; - hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); - NetlibInitializeNestedCS(&ncsSend); - NetlibInitializeNestedCS(&ncsRecv); -} - -NetlibConnection::~NetlibConnection() -{ - handleType = 0; - - if (s != INVALID_SOCKET) - closesocket(s); - - mir_free(szNewUrl); - mir_free(szProxyServer); - - mir_free(nlhpi.szHttpPostUrl); - mir_free(nlhpi.szHttpGetUrl); - - NetlibDeleteNestedCS(&ncsSend); - NetlibDeleteNestedCS(&ncsRecv); - - CloseHandle(hOkToCloseEvent); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +extern mir_cs csNetlibUser; +extern uint32_t g_LastConnectionTick; +extern int connectionTimeout; +static int iUPnPCleanup = 0; + +#define RECV_DEFAULT_TIMEOUT 60000 + +//returns in network byte order +uint32_t DnsLookup(NetlibUser *nlu, const char *szHost) +{ + HOSTENT* host; + uint32_t ip = inet_addr(szHost); + if (ip != INADDR_NONE) + return ip; + + __try { + host = gethostbyname(szHost); + if (host) + return *(u_long*)host->h_addr_list[0]; + + Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "gethostbyname", szHost, WSAGetLastError()); + } + __except (EXCEPTION_EXECUTE_HANDLER) {} + + return 0; +} + +int WaitUntilReadable(SOCKET s, uint32_t dwTimeout, bool check) +{ + fd_set readfd; + TIMEVAL tv; + + if (s == INVALID_SOCKET) return SOCKET_ERROR; + + tv.tv_sec = dwTimeout / 1000; + tv.tv_usec = (dwTimeout % 1000) * 1000; + + FD_ZERO(&readfd); + FD_SET(s, &readfd); + + int result = select(0, &readfd, nullptr, nullptr, &tv); + if (result == 0 && !check) SetLastError(ERROR_TIMEOUT); + return result; +} + +int WaitUntilWritable(SOCKET s, uint32_t dwTimeout) +{ + fd_set writefd; + TIMEVAL tv; + + tv.tv_sec = dwTimeout / 1000; + tv.tv_usec = (dwTimeout % 1000) * 1000; + + FD_ZERO(&writefd); + FD_SET(s, &writefd); + + switch (select(0, nullptr, &writefd, nullptr, &tv)) { + case 0: + SetLastError(ERROR_TIMEOUT); + case SOCKET_ERROR: + return 0; + } + return 1; +} + +bool RecvUntilTimeout(NetlibConnection *nlc, char *buf, int len, int flags, uint32_t dwTimeout) +{ + int nReceived = 0; + uint32_t dwTimeNow, dwCompleteTime = GetTickCount() + dwTimeout; + + while ((dwTimeNow = GetTickCount()) < dwCompleteTime) { + if (WaitUntilReadable(nlc->s, dwCompleteTime - dwTimeNow) <= 0) return false; + nReceived = Netlib_Recv(nlc, buf, len, flags); + if (nReceived <= 0) return false; + + buf += nReceived; + len -= nReceived; + if (len <= 0) return true; + } + SetLastError(ERROR_TIMEOUT); + return false; +} + +static int NetlibInitSocks4Connection(NetlibConnection *nlc) +{ + // http://www.socks.nec.com/protocol/socks4.protocol and http://www.socks.nec.com/protocol/socks4a.protocol + NetlibUrl &url = nlc->url; + if (url.szHost.IsEmpty()) + return 0; + + NetlibUser *nlu = nlc->nlu; + size_t nHostLen = mir_strlen(url.szHost) + 1; + size_t nUserLen = nlu->settings.szProxyAuthUser ? mir_strlen(nlu->settings.szProxyAuthUser) + 1 : 1; + size_t len = 8 + nUserLen; + + char* pInit = (char*)alloca(len + nHostLen); + pInit[0] = 4; // SOCKS4 + pInit[1] = 1; //connect + *(PWORD)&pInit[2] = htons(url.port); + + if (nUserLen <= 1) pInit[8] = 0; + else memcpy(&pInit[8], nlu->settings.szProxyAuthUser, nUserLen); + + //if cannot resolve host, try resolving through proxy (requires SOCKS4a) + uint32_t ip = DnsLookup(nlu, url.szHost); + *(PDWORD)&pInit[4] = ip ? ip : 0x01000000; + if (!ip) { + memcpy(&pInit[len], url.szHost, nHostLen); + len += nHostLen; + } + + if (Netlib_Send(nlc, pInit, (int)len, MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + return 0; + } + + char reply[8]; + if (!RecvUntilTimeout(nlc, reply, sizeof(reply), MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + + switch ((uint8_t)reply[1]) { + case 90: return 1; + case 91: SetLastError(ERROR_ACCESS_DENIED); break; + case 92: SetLastError(ERROR_CONNECTION_UNAVAIL); break; + case 93: SetLastError(ERROR_INVALID_ACCESS); break; + default: SetLastError(ERROR_INVALID_DATA); break; + } + Netlib_Logf(nlu, "%s %d: Proxy connection failed (%x %u)", __FILE__, __LINE__, (uint8_t)reply[1], GetLastError()); + return 0; +} + +static int NetlibInitSocks5Connection(NetlibConnection *nlc) +{ + //rfc1928 + uint8_t buf[258]; + NetlibUser *nlu = nlc->nlu; + + buf[0] = 5; //yep, socks5 + buf[1] = 1; //one auth method + buf[2] = nlu->settings.useProxyAuth ? 2 : 0; + if (Netlib_Send(nlc, (char*)buf, 3, MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + return 0; + } + + //confirmation of auth method + if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + if ((buf[1] != 0 && buf[1] != 2)) { + SetLastError(ERROR_INVALID_ID_AUTHORITY); + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Recv", GetLastError()); + return 0; + } + + if (buf[1] == 2) { //rfc1929 + size_t nUserLen = mir_strlen(nlu->settings.szProxyAuthUser); + size_t nPassLen = mir_strlen(nlu->settings.szProxyAuthPassword); + uint8_t *pAuthBuf = (uint8_t*)mir_alloc(3 + nUserLen + nPassLen); + pAuthBuf[0] = 1; //auth version + pAuthBuf[1] = (uint8_t)nUserLen; + memcpy(pAuthBuf + 2, nlu->settings.szProxyAuthUser, nUserLen); + pAuthBuf[2 + nUserLen] = (uint8_t)nPassLen; + memcpy(pAuthBuf + 3 + nUserLen, nlu->settings.szProxyAuthPassword, nPassLen); + if (Netlib_Send(nlc, (char*)pAuthBuf, int(3 + nUserLen + nPassLen), MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + mir_free(pAuthBuf); + return 0; + } + mir_free(pAuthBuf); + + if (!RecvUntilTimeout(nlc, (char*)buf, 2, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + if (buf[1]) { + SetLastError(ERROR_ACCESS_DENIED); + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + } + + size_t nHostLen; + uint32_t hostIP; + + NetlibUrl &url = nlc->url; + if (nlc->dnsThroughProxy) { + hostIP = inet_addr(url.szHost); + nHostLen = (hostIP == INADDR_NONE) ? mir_strlen(url.szHost) + 1 : 4; + } + else { + hostIP = DnsLookup(nlu, url.szHost); + if (hostIP == 0) + return 0; + nHostLen = 4; + } + uint8_t *pInit = (uint8_t*)mir_alloc(6 + nHostLen); + pInit[0] = 5; //SOCKS5 + pInit[1] = url.flags & NLOCF_UDP ? 3 : 1; //connect or UDP + pInit[2] = 0; //reserved + if (hostIP == INADDR_NONE) { //DNS lookup through proxy + pInit[3] = 3; + pInit[4] = uint8_t(nHostLen - 1); + memcpy(pInit + 5, url.szHost, nHostLen - 1); + } + else { + pInit[3] = 1; + *(PDWORD)(pInit + 4) = hostIP; + } + *(PWORD)(pInit + 4 + nHostLen) = htons(url.port); + if (Netlib_Send(nlc, (char*)pInit, int(6 + nHostLen), MSG_DUMPPROXY) == SOCKET_ERROR) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "Netlib_Send", GetLastError()); + mir_free(pInit); + return 0; + } + mir_free(pInit); + + if (!RecvUntilTimeout(nlc, (char*)buf, 5, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + + if (buf[0] != 5 || buf[1]) { + const char* err = "Unknown response"; + if (buf[0] != 5) + SetLastError(ERROR_BAD_FORMAT); + else { + switch (buf[1]) { + case 1: SetLastError(ERROR_GEN_FAILURE); err = "General failure"; break; + case 2: SetLastError(ERROR_ACCESS_DENIED); err = "Connection not allowed by ruleset"; break; + case 3: SetLastError(WSAENETUNREACH); err = "Network unreachable"; break; + case 4: SetLastError(WSAEHOSTUNREACH); err = "Host unreachable"; break; + case 5: SetLastError(WSAECONNREFUSED); err = "Connection refused by destination host"; break; + case 6: SetLastError(WSAETIMEDOUT); err = "TTL expired"; break; + case 7: SetLastError(ERROR_CALL_NOT_IMPLEMENTED); err = "Command not supported / protocol error"; break; + case 8: SetLastError(ERROR_INVALID_ADDRESS); err = "Address type not supported"; break; + default: SetLastError(ERROR_INVALID_DATA); break; + } + } + Netlib_Logf(nlu, "%s %d: Proxy conection failed. %s.", __FILE__, __LINE__, err); + return 0; + } + + int nRecvSize = 0; + switch (buf[3]) { + case 1:// ipv4 addr + nRecvSize = 5; + break; + case 3:// dns name addr + nRecvSize = buf[4] + 2; + break; + case 4:// ipv6 addr + nRecvSize = 17; + break; + default: + Netlib_Logf(nlu, "%s %d: %s() unknown address type (%u)", __FILE__, __LINE__, "NetlibInitSocks5Connection", (int)buf[3]); + return 0; + } + if (!RecvUntilTimeout(nlc, (char*)buf, nRecvSize, MSG_DUMPPROXY, RECV_DEFAULT_TIMEOUT)) { + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "RecvUntilTimeout", GetLastError()); + return 0; + } + + //connected + return 1; +} + +static bool NetlibInitHttpsConnection(NetlibConnection *nlc) +{ + // rfc2817 + NetlibUrl &url = nlc->url; + CMStringA szUrl; + if (nlc->dnsThroughProxy) + szUrl.Format("%s:%u", url.szHost.c_str(), url.port); + else { + uint32_t ip = DnsLookup(nlc->nlu, url.szHost); + if (ip == 0) return false; + szUrl.Format("%s:%u", inet_ntoa(*(PIN_ADDR)&ip), url.port); + } + + NETLIBHTTPREQUEST nlhrSend = { 0 }; + nlhrSend.cbSize = sizeof(nlhrSend); + nlhrSend.requestType = REQUEST_CONNECT; + nlhrSend.flags = NLHRF_DUMPPROXY | NLHRF_HTTP11 | NLHRF_NOPROXY | NLHRF_REDIRECT; + nlhrSend.szUrl = szUrl.GetBuffer(); + + if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) + return false; + + NETLIBHTTPREQUEST *nlhrReply = NetlibHttpRecv(nlc, MSG_DUMPPROXY | MSG_RAW, MSG_DUMPPROXY | MSG_RAW, true); + if (nlhrReply == nullptr) + return false; + + if (nlhrReply->resultCode < 200 || nlhrReply->resultCode >= 300) { + if (nlhrReply->resultCode == 403 && nlc->dnsThroughProxy) { + Netlib_FreeHttpRequest(nlhrReply); + nlc->dnsThroughProxy = 0; + return NetlibInitHttpsConnection(nlc); + } + + NetlibHttpSetLastErrorUsingHttpResult(nlhrReply->resultCode); + Netlib_Logf(nlc->nlu, "%s %d: %s request failed (%u %s)", __FILE__, __LINE__, + nlc->nlu->settings.proxyType == PROXYTYPE_HTTP ? "HTTP" : "HTTPS", nlhrReply->resultCode, nlhrReply->szResultDescr); + Netlib_FreeHttpRequest(nlhrReply); + return 0; + } + Netlib_FreeHttpRequest(nlhrReply); + return true; // connected +} + +static void FreePartiallyInitedConnection(NetlibConnection *nlc) +{ + uint32_t dwOriginalLastError = GetLastError(); + + if (GetNetlibHandleType(nlc) == NLH_CONNECTION) + delete nlc; + + SetLastError(dwOriginalLastError); +} + +static bool my_connectIP(NetlibConnection *nlc) +{ + NetlibUser *nlu = nlc->nlu; + int rc = SOCKET_ERROR, retrycnt = 0; + u_long notblocking = 1; + uint32_t lasterr = 0; + static const TIMEVAL tv = { 1, 0 }; + + // if timeout is zero then its an old style connection or new with a 0 timeout, select() will error quicker anyway + int timeout = (nlc->timeout <= 0) ? 30 : nlc->timeout; + + // this is for XP SP2 where there is a default connection attempt limit of 10/second + if (connectionTimeout) { + WaitForSingleObject(hConnectionOpenMutex, 10000); + int waitdiff = GetTickCount() - g_LastConnectionTick; + if (waitdiff < connectionTimeout) SleepEx(connectionTimeout, TRUE); + g_LastConnectionTick = GetTickCount(); + ReleaseMutex(hConnectionOpenMutex); + + // might have died in between the wait + if (Miranda_IsTerminated()) + return false; + } + + char szPort[6]; + addrinfo *air = nullptr, *ai, hints = { 0 }; + + hints.ai_family = AF_UNSPEC; + + NetlibUrl &url = nlc->url; + if (url.flags & NLOCF_UDP) { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } + else { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + + if (nlc->proxyType) { + if (!nlc->szProxyServer) + return false; + + Netlib_Logf(nlu, "(%p) Connecting to proxy %s:%d for %s:%d ....", nlc, nlc->szProxyServer, nlc->wProxyPort, url.szHost.c_str(), url.port); + + _itoa(nlc->wProxyPort, szPort, 10); + if (GetAddrInfoA(nlc->szProxyServer, szPort, &hints, &air)) { + Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", nlc->szProxyServer, WSAGetLastError()); + return false; + } + } + else { + if (url.szHost.IsEmpty()) + return false; + + Netlib_Logf(nlu, "(%p) Connecting to server %s:%d....", nlc, url.szHost.c_str(), url.port); + + _itoa(url.port, szPort, 10); + + if (GetAddrInfoA(url.szHost, szPort, &hints, &air)) { + Netlib_Logf(nlu, "%s %d: %s() for host %s failed (%u)", __FILE__, __LINE__, "getaddrinfo", url.szHost.c_str(), WSAGetLastError()); + return false; + } + } + + for (ai = air; ai && !Miranda_IsTerminated(); ai = ai->ai_next) { + Netlib_Logf(nlu, "(%p) Connecting to ip %s ....", nlc, ptrA(Netlib_AddressToString((sockaddr_in*)ai->ai_addr)).get()); +retry: + nlc->s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (nlc->s == INVALID_SOCKET) { + FreeAddrInfoA(air); + return false; + } + + // return the socket to non blocking + if (ioctlsocket(nlc->s, FIONBIO, ¬blocking) != 0) { + FreeAddrInfoA(air); + return false; + } + + if (nlu->settings.specifyOutgoingPorts && nlu->settings.szOutgoingPorts && nlu->settings.szOutgoingPorts[0]) { + SOCKET s = ai->ai_family == AF_INET ? nlc->s : INVALID_SOCKET; + SOCKET s6 = ai->ai_family == AF_INET6 ? nlc->s : INVALID_SOCKET; + if (!BindSocketToPort(nlu->settings.szOutgoingPorts, s, s6, &nlu->inportnum)) + Netlib_Logf(nlu, "Netlib connect: Not enough ports for outgoing connections specified"); + } + + // try a connect + if (connect(nlc->s, ai->ai_addr, (int)ai->ai_addrlen) == 0) { + rc = 0; + break; + } + + // didn't work, was it cos of nonblocking? + if (WSAGetLastError() != WSAEWOULDBLOCK) { + rc = SOCKET_ERROR; + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + continue; + } + + while (true) { // timeout loop + fd_set r, w, e; + FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e); + FD_SET(nlc->s, &r); + FD_SET(nlc->s, &w); + FD_SET(nlc->s, &e); + if ((rc = select(0, &r, &w, &e, &tv)) == SOCKET_ERROR) + break; + + if (rc > 0) { + if (FD_ISSET(nlc->s, &w)) { + // connection was successful + rc = 0; + lasterr = 0; + } + if (FD_ISSET(nlc->s, &r)) { + // connection was closed + rc = SOCKET_ERROR; + lasterr = WSAECONNRESET; + } + if (FD_ISSET(nlc->s, &e)) { + // connection failed. + int len = sizeof(lasterr); + rc = SOCKET_ERROR; + getsockopt(nlc->s, SOL_SOCKET, SO_ERROR, (char*)&lasterr, &len); + if (lasterr == WSAEADDRINUSE && ++retrycnt <= 2) { + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + goto retry; + } + } + break; + } + else if (Miranda_IsTerminated()) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + + if (--timeout == 0) { + rc = SOCKET_ERROR; + lasterr = ERROR_TIMEOUT; + break; + } + } + + if (rc == 0) break; + + closesocket(nlc->s); + nlc->s = INVALID_SOCKET; + } + + FreeAddrInfoA(air); + + notblocking = 0; + if (nlc->s != INVALID_SOCKET) ioctlsocket(nlc->s, FIONBIO, ¬blocking); + if (rc && lasterr) SetLastError(lasterr); + return rc == 0; +} + +static int NetlibHttpFallbackToDirect(NetlibConnection *nlc) +{ + NetlibDoCloseSocket(nlc, true); + + Netlib_Logf(nlc->nlu, "Fallback to direct connection"); + + nlc->proxyAuthNeeded = false; + nlc->proxyType = 0; + replaceStr(nlc->szProxyServer, nullptr); + if (!my_connectIP(nlc)) { + Netlib_Logf(nlc->nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); + return false; + } + return true; +} + +bool NetlibDoConnect(NetlibConnection *nlc) +{ + NetlibUser *nlu = nlc->nlu; + + replaceStr(nlc->szProxyServer, nullptr); + + bool usingProxy = false, forceHttps = false; + if (nlu->settings.useProxy) { + if (nlu->settings.proxyType == PROXYTYPE_IE) + usingProxy = NetlibGetIeProxyConn(nlc, false); + else { + if (nlu->settings.szProxyServer && nlu->settings.szProxyServer[0]) { + nlc->szProxyServer = mir_strdup(nlu->settings.szProxyServer); + nlc->wProxyPort = nlu->settings.wProxyPort; + nlc->proxyType = nlu->settings.proxyType; + usingProxy = true; + } + } + } + + while (!my_connectIP(nlc)) { + // if connection failed, the state of nlc might be unpredictable + if (GetNetlibHandleType(nlc) == NLH_CONNECTION) { + // Fallback to direct only when using HTTP proxy, as this is what used by companies + // If other type of proxy used it's an indication of security nutcase, leave him alone + if (usingProxy && (nlc->proxyType == PROXYTYPE_HTTPS || nlc->proxyType == PROXYTYPE_HTTP)) { + usingProxy = false; + nlc->proxyType = 0; + Netlib_Logf(nlu, "Fallback to direct connection"); + continue; + } + if (nlu->settings.useProxy && !usingProxy && nlu->settings.proxyType == PROXYTYPE_IE && !forceHttps) { + forceHttps = true; + usingProxy = NetlibGetIeProxyConn(nlc, true); + if (usingProxy) + continue; + } + } + Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "connect", WSAGetLastError()); + return false; + } + + if (usingProxy && !((nlc->url.flags & (NLOCF_HTTP | NLOCF_SSL)) == NLOCF_HTTP && (nlc->proxyType == PROXYTYPE_HTTP || nlc->proxyType == PROXYTYPE_HTTPS))) { + if (!WaitUntilWritable(nlc->s, 30000)) + return false; + + switch (nlc->proxyType) { + case PROXYTYPE_SOCKS4: + if (!NetlibInitSocks4Connection(nlc)) + return false; + break; + + case PROXYTYPE_SOCKS5: + if (!NetlibInitSocks5Connection(nlc)) + return false; + break; + + case PROXYTYPE_HTTPS: + case PROXYTYPE_HTTP: + nlc->proxyAuthNeeded = true; + if (!NetlibInitHttpsConnection(nlc)) { + usingProxy = false; + if (!NetlibHttpFallbackToDirect(nlc)) + return false; + } + break; + + default: + SetLastError(ERROR_INVALID_PARAMETER); + FreePartiallyInitedConnection(nlc); + return false; + } + } + + Netlib_Logf(nlu, "(%d) Connected to %s:%d", nlc->s, nlc->url.szHost.c_str(), nlc->url.port); + + if (GetSubscribersCount((THook*)hEventConnected)) { + NETLIBCONNECTIONEVENTINFO ncei = {}; + ncei.connected = 1; + ncei.szSettingsModule = nlu->user.szSettingsModule; + int size = sizeof(SOCKADDR_IN); + getsockname(nlc->s, (SOCKADDR *)&ncei.local, &size); + if (nlu->settings.useProxy) { + size = sizeof(SOCKADDR_IN); + getpeername(nlc->s, (SOCKADDR *)&ncei.proxy, &size); + ncei.remote.sin_family = AF_INET; + ncei.remote.sin_port = htons(nlc->url.port); + ncei.remote.sin_addr.S_un.S_addr = DnsLookup(nlu, nlc->url.szHost); + } + else { + size = sizeof(SOCKADDR_IN); + getpeername(nlc->s, (SOCKADDR *)&ncei.remote, &size); + } + NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0); + } + + if (NLOCF_SSL & nlc->url.flags) + return Netlib_StartSsl(nlc, nullptr) != 0; + + return true; +} + +bool NetlibReconnect(NetlibConnection *nlc) +{ + // a connection might be freed already + if (GetNetlibHandleType(nlc) != NLH_CONNECTION) + return false; + + char buf[4]; + bool opened = nlc->s != INVALID_SOCKET; + if (opened) { + switch (WaitUntilReadable(nlc->s, 0, true)) { + case SOCKET_ERROR: + opened = false; + break; + + case 0: + opened = true; + break; + + case 1: + opened = recv(nlc->s, buf, 1, MSG_PEEK) > 0; + break; + } + + if (!opened) + NetlibDoCloseSocket(nlc, true); + } + + if (!opened) { + if (Miranda_IsTerminated()) + return false; + + return NetlibDoConnect(nlc); + } + return true; +} + +MIR_APP_DLL(HNETLIBCONN) Netlib_OpenConnection(NetlibUser *nlu, const char *szHost, int port, int timeout, int flags) +{ + if (szHost == nullptr || port == 0) { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING)) + return nullptr; + + Netlib_Logf(nlu, "Connection request to %s:%d (Flags %x)....", szHost, port, flags); + + NetlibConnection *nlc = new NetlibConnection(); + nlc->nlu = nlu; + nlc->timeout = timeout; + nlc->url.szHost = szHost; + nlc->url.port = port; + nlc->url.flags = flags; + nlc->dnsThroughProxy = nlu->settings.dnsThroughProxy != 0; + + if (!NetlibDoConnect(nlc)) { + FreePartiallyInitedConnection(nlc); + return nullptr; + } + + if (iUPnPCleanup == 0) { + mir_cslock lck(csNetlibUser); + iUPnPCleanup = 1; + mir_forkthread(NetlibUPnPCleanup); + } + + return nlc; +} + +NetlibConnection::NetlibConnection() +{ + handleType = NLH_CONNECTION; + s = s2 = INVALID_SOCKET; + hOkToCloseEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr); + NetlibInitializeNestedCS(&ncsSend); + NetlibInitializeNestedCS(&ncsRecv); +} + +NetlibConnection::~NetlibConnection() +{ + handleType = 0; + + if (s != INVALID_SOCKET) + closesocket(s); + + mir_free(szNewUrl); + mir_free(szProxyServer); + + mir_free(nlhpi.szHttpPostUrl); + mir_free(nlhpi.szHttpGetUrl); + + NetlibDeleteNestedCS(&ncsSend); + NetlibDeleteNestedCS(&ncsRecv); + + CloseHandle(hOkToCloseEvent); +} diff --git a/src/mir_app/src/netlib_opts.cpp b/src/mir_app/src/netlib_opts.cpp index a7e0419287..be75114fac 100644 --- a/src/mir_app/src/netlib_opts.cpp +++ b/src/mir_app/src/netlib_opts.cpp @@ -1,518 +1,518 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -struct NetlibTempSettings -{ - uint32_t flags; - char *szSettingsModule; - NETLIBUSERSETTINGS settings; -}; - -static LIST tempSettings(5); - -static const UINT outgoingConnectionsControls[] = -{ - IDC_STATIC12, - IDC_USEPROXY, - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS, - IDC_SPECIFYPORTSO, - IDC_PORTSRANGEO, - IDC_STATIC54, - IDC_VALIDATESSL}; -static const UINT useProxyControls[] = { - IDC_STATIC21, IDC_PROXYTYPE, - IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, - IDC_PROXYAUTH, - IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, - IDC_PROXYDNS}; -static const UINT specifyOPortsControls[] = { - IDC_PORTSRANGEO, - IDC_STATIC54 -}; -static const UINT incomingConnectionsControls[] = { - IDC_STATIC43, - IDC_SPECIFYPORTS, - IDC_PORTSRANGE, - IDC_STATIC52, - IDC_ENABLEUPNP}; -static const UINT specifyPortsControls[] = { - IDC_PORTSRANGE, - IDC_STATIC52}; - -static const wchar_t* szProxyTypes[] = {LPGENW(""), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"}; -static const uint16_t oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080}; - -#define M_REFRESHALL (WM_USER+100) -#define M_REFRESHENABLING (WM_USER+101) - -static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) -{ - for (int i = 0; i < cControls; i++) - EnableWindow(GetDlgItem(hwndDlg, controls[i]), state); -} - -static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType) -{ - int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type])); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type); - if (type == selectType) - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0); -} - -static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source) -{ - *dest = *source; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); -} - -static void CombineSettingsStrings(char **dest, char **source) -{ - if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; } -} - -static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, uint32_t *destFlags, NETLIBUSERSETTINGS *source, uint32_t sourceFlags) -{ - if (sourceFlags & NUF_OUTGOING) { - if (*destFlags & NUF_OUTGOING) { - if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2; - if (dest->useProxy != source->useProxy) dest->useProxy = 2; - if (dest->proxyType != source->proxyType) dest->proxyType = 0; - CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer); - if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0; - if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2; - CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser); - CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword); - if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2; - if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2; - CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts); - } - else { - dest->validateSSL = source->validateSSL; - dest->useProxy = source->useProxy; - dest->proxyType = source->proxyType; - dest->szProxyServer = source->szProxyServer; - if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); - dest->wProxyPort = source->wProxyPort; - dest->useProxyAuth = source->useProxyAuth; - dest->szProxyAuthUser = source->szProxyAuthUser; - if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); - dest->szProxyAuthPassword = source->szProxyAuthPassword; - if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); - dest->dnsThroughProxy = source->dnsThroughProxy; - dest->specifyOutgoingPorts = source->specifyOutgoingPorts; - dest->szOutgoingPorts = source->szOutgoingPorts; - if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); - } - } - if (sourceFlags & NUF_INCOMING) { - if (*destFlags & NUF_INCOMING) { - if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2; - if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2; - CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts); - } - else { - dest->enableUPnP = source->enableUPnP; - dest->specifyIncomingPorts = source->specifyIncomingPorts; - dest->szIncomingPorts = source->szIncomingPorts; - if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); - } - } - if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION)) - *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION; - else *destFlags |= sourceFlags; -} - -static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED; - CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - *(int*)(((uint8_t*)&p->settings) + memberOffset) = newValue; - } - else *(int*)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset) = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); -} - -static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) -{ - int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId)); - char *szNewValue = (char*)mir_alloc(newValueLen+1); - GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1); - if (iUser == -1) { - for (auto &p : tempSettings) { - if (!(p->flags & NUF_NOOPTIONS)) { - char **ppszNew = (char**)(((uint8_t*)&p->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = mir_strdup(szNewValue); - } - } - mir_free(szNewValue); - } - else { - char **ppszNew = (char**)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset); - mir_free(*ppszNew); - *ppszNew = szNewValue; - } -} - -static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, uint32_t flags) -{ - if (flags & NUF_OUTGOING) { - db_set_b(0, szSettingsModule, "NLValidateSSL", (uint8_t)settings->validateSSL); - db_set_b(0, szSettingsModule, "NLUseProxy", (uint8_t)settings->useProxy); - db_set_b(0, szSettingsModule, "NLProxyType", (uint8_t)settings->proxyType); - db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : ""); - db_set_w(0, szSettingsModule, "NLProxyPort", (uint16_t)settings->wProxyPort); - db_set_b(0, szSettingsModule, "NLUseProxyAuth", (uint8_t)settings->useProxyAuth); - db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : ""); - db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : ""); - db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (uint8_t)settings->dnsThroughProxy); - db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (uint8_t)settings->specifyOutgoingPorts); - db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : ""); - } - if (flags & NUF_INCOMING) { - db_set_b(0, szSettingsModule, "NLEnableUPnP", (uint8_t)settings->enableUPnP); - db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (uint8_t)settings->specifyIncomingPorts); - db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : ""); - } -} - -void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings) -{ - mir_cslock lck(csNetlibUser); - - NetlibUser tUser; - tUser.user.szSettingsModule = (char*)szSettingsModule; - NetlibUser *thisUser = netlibUser.find(&tUser); - if (thisUser == nullptr) - return; - - NetlibFreeUserSettingsStruct(&thisUser->settings); - CopySettingsStruct(&thisUser->settings, settings); - WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags); - - NETLIBUSERSETTINGS combinedSettings = { 0 }; - combinedSettings.cbSize = sizeof(combinedSettings); - - uint32_t flags = 0; - for (auto &p : netlibUser) { - if (p->user.flags & NUF_NOOPTIONS) - continue; - CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags); - } - if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0; - if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0; - if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5; - if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0; - if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1; - if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1; - if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0; - WriteSettingsStructToDb("Netlib", &combinedSettings, flags); - NetlibFreeUserSettingsStruct(&combinedSettings); -} - -static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - int iUser; - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("")); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0); - - mir_cslock lck(csNetlibUser); - for (auto &it : netlibUser) { - NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings)); - thisSettings->flags = it->user.flags; - thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule); - CopySettingsStruct(&thisSettings->settings, &it->settings); - tempSettings.insert(thisSettings); - - if (it->user.flags & NUF_NOOPTIONS) - continue; - iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w); - SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it)); - } - } - - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return TRUE; - - case M_REFRESHALL: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - { - NETLIBUSERSETTINGS settings = { 0 }; - uint32_t flags = 0; - - if (iUser == -1) { - settings.cbSize = sizeof(settings); - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - CombineSettingsStructs(&settings, &flags, &p->settings, p->flags); - } - else { - NetlibFreeUserSettingsStruct(&settings); - CopySettingsStruct(&settings, &tempSettings[iUser]->settings); - flags = tempSettings[iUser]->flags; - } - ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy); - SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0); - if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType); - AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType); - if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType); - if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType); - if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION)) - AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType); - SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : ""); - if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE); - else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, ""); - CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth); - SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : ""); - SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : ""); - CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy); - CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL); - - ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE); - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts); - SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : ""); - - CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP); - - NetlibFreeUserSettingsStruct(&settings); - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - break; - - case M_REFRESHENABLING: - wchar_t str[80]; - { - int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]); - SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str); - if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) { - int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1; - EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE); - if (selectedProxyType == 0) { - for (auto &p : tempSettings) { - if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING)) - continue; - - if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - enableAuth = 1; - if (p->settings.useProxyAuth) - enableUser = enablePass = 1; - } - } - } - else { - if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1; - else { - if (selectedProxyType == PROXYTYPE_IE) enableServer = 0; - enableAuth = 1; - if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED) - enableUser = enablePass = 1; - } - } - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer); - } - else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE); - EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED); - EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED); - } - break; - - case WM_COMMAND: - iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); - switch (LOWORD(wParam)) { - case IDC_NETLIBUSERS: - if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - return 0; - - case IDC_LOGOPTIONS: - NetlibLogShowOptions(); - return 0; - - case IDC_PROXYTYPE: - if (HIWORD(wParam) == CBN_SELCHANGE) { - int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); - if (iUser == -1) { - if (newValue == 0) - return 0; - - for (auto &p : tempSettings) { - if (p->flags & NUF_NOOPTIONS) - continue; - if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS)) - p->settings.proxyType = PROXYTYPE_HTTPS; - else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION) - p->settings.proxyType = PROXYTYPE_HTTP; - else p->settings.proxyType = newValue; - } - SendMessage(hwndDlg, M_REFRESHALL, 0, 0); - } - else { - tempSettings[iUser]->settings.proxyType = newValue; - SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); - } - } - break; - case IDC_USEPROXY: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy)); - break; - case IDC_PROXYAUTH: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth)); - break; - case IDC_PROXYDNS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy)); - break; - case IDC_SPECIFYPORTS: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts)); - break; - case IDC_SPECIFYPORTSO: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts)); - break; - case IDC_ENABLEUPNP: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP)); - break; - case IDC_VALIDATESSL: - ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL)); - break; - case IDC_PROXYHOST: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer)); - break; - case IDC_PROXYPORT: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - { - int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE); - if (iUser == -1) { - for (auto &p : tempSettings) - if (!(p->flags & NUF_NOOPTIONS)) - p->settings.wProxyPort = newValue; - } - else tempSettings[iUser]->settings.wProxyPort = newValue; - } - break; - case IDC_PROXYUSER: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser)); - break; - case IDC_PROXYPASS: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword)); - break; - case IDC_PORTSRANGE: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts)); - break; - case IDC_PORTSRANGEO: - if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; - ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts)); - break; - } - ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW); - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - - case WM_NOTIFY: - switch (((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR)lParam)->code) { - case PSN_APPLY: - for (auto &p : tempSettings) - NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings); - return TRUE; - } - break; - } - break; - - case WM_DESTROY: - for (auto &p : tempSettings) { - mir_free(p->szSettingsModule); - NetlibFreeUserSettingsStruct(&p->settings); - mir_free(p); - } - tempSettings.destroy(); - break; - } - return FALSE; -} - -int NetlibOptInitialise(WPARAM wParam, LPARAM) -{ - int optionsCount = 0; - { - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (!(p->user.flags & NUF_NOOPTIONS)) - ++optionsCount; - } - - if (optionsCount == 0) - return 0; - - OPTIONSDIALOGPAGE odp = {}; - odp.position = 900000000; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); - odp.szTitle.a = LPGEN("Network"); - odp.pfnDlgProc = DlgProcNetlibOpts; - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +struct NetlibTempSettings +{ + uint32_t flags; + char *szSettingsModule; + NETLIBUSERSETTINGS settings; +}; + +static LIST tempSettings(5); + +static const UINT outgoingConnectionsControls[] = +{ + IDC_STATIC12, + IDC_USEPROXY, + IDC_STATIC21, IDC_PROXYTYPE, + IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, + IDC_PROXYAUTH, + IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, + IDC_PROXYDNS, + IDC_SPECIFYPORTSO, + IDC_PORTSRANGEO, + IDC_STATIC54, + IDC_VALIDATESSL}; +static const UINT useProxyControls[] = { + IDC_STATIC21, IDC_PROXYTYPE, + IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT, + IDC_PROXYAUTH, + IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS, + IDC_PROXYDNS}; +static const UINT specifyOPortsControls[] = { + IDC_PORTSRANGEO, + IDC_STATIC54 +}; +static const UINT incomingConnectionsControls[] = { + IDC_STATIC43, + IDC_SPECIFYPORTS, + IDC_PORTSRANGE, + IDC_STATIC52, + IDC_ENABLEUPNP}; +static const UINT specifyPortsControls[] = { + IDC_PORTSRANGE, + IDC_STATIC52}; + +static const wchar_t* szProxyTypes[] = {LPGENW(""), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"}; +static const uint16_t oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080}; + +#define M_REFRESHALL (WM_USER+100) +#define M_REFRESHENABLING (WM_USER+101) + +static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) +{ + for (int i = 0; i < cControls; i++) + ShowWindow(GetDlgItem(hwndDlg, controls[i]), state); +} + +static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state) +{ + for (int i = 0; i < cControls; i++) + EnableWindow(GetDlgItem(hwndDlg, controls[i]), state); +} + +static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType) +{ + int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type])); + SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type); + if (type == selectType) + SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0); +} + +static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source) +{ + *dest = *source; + if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); + if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); + if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); + if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); + if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); +} + +static void CombineSettingsStrings(char **dest, char **source) +{ + if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; } +} + +static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, uint32_t *destFlags, NETLIBUSERSETTINGS *source, uint32_t sourceFlags) +{ + if (sourceFlags & NUF_OUTGOING) { + if (*destFlags & NUF_OUTGOING) { + if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2; + if (dest->useProxy != source->useProxy) dest->useProxy = 2; + if (dest->proxyType != source->proxyType) dest->proxyType = 0; + CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer); + if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0; + if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2; + CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser); + CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword); + if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2; + if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2; + CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts); + } + else { + dest->validateSSL = source->validateSSL; + dest->useProxy = source->useProxy; + dest->proxyType = source->proxyType; + dest->szProxyServer = source->szProxyServer; + if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer); + dest->wProxyPort = source->wProxyPort; + dest->useProxyAuth = source->useProxyAuth; + dest->szProxyAuthUser = source->szProxyAuthUser; + if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser); + dest->szProxyAuthPassword = source->szProxyAuthPassword; + if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword); + dest->dnsThroughProxy = source->dnsThroughProxy; + dest->specifyOutgoingPorts = source->specifyOutgoingPorts; + dest->szOutgoingPorts = source->szOutgoingPorts; + if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts); + } + } + if (sourceFlags & NUF_INCOMING) { + if (*destFlags & NUF_INCOMING) { + if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2; + if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2; + CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts); + } + else { + dest->enableUPnP = source->enableUPnP; + dest->specifyIncomingPorts = source->specifyIncomingPorts; + dest->szIncomingPorts = source->szIncomingPorts; + if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts); + } + } + if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION)) + *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION; + else *destFlags |= sourceFlags; +} + +static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) +{ + int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED; + CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED); + if (iUser == -1) { + for (auto &p : tempSettings) + if (!(p->flags & NUF_NOOPTIONS)) + *(int*)(((uint8_t*)&p->settings) + memberOffset) = newValue; + } + else *(int*)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset) = newValue; + SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); +} + +static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset) +{ + int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId)); + char *szNewValue = (char*)mir_alloc(newValueLen+1); + GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1); + if (iUser == -1) { + for (auto &p : tempSettings) { + if (!(p->flags & NUF_NOOPTIONS)) { + char **ppszNew = (char**)(((uint8_t*)&p->settings) + memberOffset); + mir_free(*ppszNew); + *ppszNew = mir_strdup(szNewValue); + } + } + mir_free(szNewValue); + } + else { + char **ppszNew = (char**)(((uint8_t*)&tempSettings[iUser]->settings) + memberOffset); + mir_free(*ppszNew); + *ppszNew = szNewValue; + } +} + +static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, uint32_t flags) +{ + if (flags & NUF_OUTGOING) { + db_set_b(0, szSettingsModule, "NLValidateSSL", (uint8_t)settings->validateSSL); + db_set_b(0, szSettingsModule, "NLUseProxy", (uint8_t)settings->useProxy); + db_set_b(0, szSettingsModule, "NLProxyType", (uint8_t)settings->proxyType); + db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : ""); + db_set_w(0, szSettingsModule, "NLProxyPort", (uint16_t)settings->wProxyPort); + db_set_b(0, szSettingsModule, "NLUseProxyAuth", (uint8_t)settings->useProxyAuth); + db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : ""); + db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : ""); + db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (uint8_t)settings->dnsThroughProxy); + db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (uint8_t)settings->specifyOutgoingPorts); + db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : ""); + } + if (flags & NUF_INCOMING) { + db_set_b(0, szSettingsModule, "NLEnableUPnP", (uint8_t)settings->enableUPnP); + db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (uint8_t)settings->specifyIncomingPorts); + db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : ""); + } +} + +void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings) +{ + mir_cslock lck(csNetlibUser); + + NetlibUser tUser; + tUser.user.szSettingsModule = (char*)szSettingsModule; + NetlibUser *thisUser = netlibUser.find(&tUser); + if (thisUser == nullptr) + return; + + NetlibFreeUserSettingsStruct(&thisUser->settings); + CopySettingsStruct(&thisUser->settings, settings); + WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags); + + NETLIBUSERSETTINGS combinedSettings = { 0 }; + combinedSettings.cbSize = sizeof(combinedSettings); + + uint32_t flags = 0; + for (auto &p : netlibUser) { + if (p->user.flags & NUF_NOOPTIONS) + continue; + CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags); + } + if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0; + if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0; + if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5; + if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0; + if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1; + if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1; + if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0; + WriteSettingsStructToDb("Netlib", &combinedSettings, flags); + NetlibFreeUserSettingsStruct(&combinedSettings); +} + +static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + int iUser; + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("")); + SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1); + SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0); + + mir_cslock lck(csNetlibUser); + for (auto &it : netlibUser) { + NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings)); + thisSettings->flags = it->user.flags; + thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule); + CopySettingsStruct(&thisSettings->settings, &it->settings); + tempSettings.insert(thisSettings); + + if (it->user.flags & NUF_NOOPTIONS) + continue; + iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w); + SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it)); + } + } + + SendMessage(hwndDlg, M_REFRESHALL, 0, 0); + return TRUE; + + case M_REFRESHALL: + iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); + { + NETLIBUSERSETTINGS settings = { 0 }; + uint32_t flags = 0; + + if (iUser == -1) { + settings.cbSize = sizeof(settings); + for (auto &p : tempSettings) + if (!(p->flags & NUF_NOOPTIONS)) + CombineSettingsStructs(&settings, &flags, &p->settings, p->flags); + } + else { + NetlibFreeUserSettingsStruct(&settings); + CopySettingsStruct(&settings, &tempSettings[iUser]->settings); + flags = tempSettings[iUser]->flags; + } + ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE); + CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy); + SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0); + if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType); + AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType); + AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType); + if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType); + if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType); + if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION)) + AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType); + SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : ""); + if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE); + else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, ""); + CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth); + SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : ""); + SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : ""); + CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy); + CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL); + + ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE); + CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts); + SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : ""); + + CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts); + SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : ""); + + CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP); + + NetlibFreeUserSettingsStruct(&settings); + SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); + } + break; + + case M_REFRESHENABLING: + wchar_t str[80]; + { + int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); + mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]); + SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str); + if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) { + int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1; + EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE); + if (selectedProxyType == 0) { + for (auto &p : tempSettings) { + if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING)) + continue; + + if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1; + else { + enableAuth = 1; + if (p->settings.useProxyAuth) + enableUser = enablePass = 1; + } + } + } + else { + if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1; + else { + if (selectedProxyType == PROXYTYPE_IE) enableServer = 0; + enableAuth = 1; + if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED) + enableUser = enablePass = 1; + } + } + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer); + } + else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE); + EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED); + EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED); + } + break; + + case WM_COMMAND: + iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0); + switch (LOWORD(wParam)) { + case IDC_NETLIBUSERS: + if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0); + return 0; + + case IDC_LOGOPTIONS: + NetlibLogShowOptions(); + return 0; + + case IDC_PROXYTYPE: + if (HIWORD(wParam) == CBN_SELCHANGE) { + int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0); + if (iUser == -1) { + if (newValue == 0) + return 0; + + for (auto &p : tempSettings) { + if (p->flags & NUF_NOOPTIONS) + continue; + if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS)) + p->settings.proxyType = PROXYTYPE_HTTPS; + else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION) + p->settings.proxyType = PROXYTYPE_HTTP; + else p->settings.proxyType = newValue; + } + SendMessage(hwndDlg, M_REFRESHALL, 0, 0); + } + else { + tempSettings[iUser]->settings.proxyType = newValue; + SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0); + } + } + break; + case IDC_USEPROXY: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy)); + break; + case IDC_PROXYAUTH: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth)); + break; + case IDC_PROXYDNS: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy)); + break; + case IDC_SPECIFYPORTS: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts)); + break; + case IDC_SPECIFYPORTSO: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts)); + break; + case IDC_ENABLEUPNP: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP)); + break; + case IDC_VALIDATESSL: + ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL)); + break; + case IDC_PROXYHOST: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer)); + break; + case IDC_PROXYPORT: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + { + int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE); + if (iUser == -1) { + for (auto &p : tempSettings) + if (!(p->flags & NUF_NOOPTIONS)) + p->settings.wProxyPort = newValue; + } + else tempSettings[iUser]->settings.wProxyPort = newValue; + } + break; + case IDC_PROXYUSER: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser)); + break; + case IDC_PROXYPASS: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword)); + break; + case IDC_PORTSRANGE: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts)); + break; + case IDC_PORTSRANGEO: + if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0; + ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts)); + break; + } + ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR)lParam)->code) { + case PSN_APPLY: + for (auto &p : tempSettings) + NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings); + return TRUE; + } + break; + } + break; + + case WM_DESTROY: + for (auto &p : tempSettings) { + mir_free(p->szSettingsModule); + NetlibFreeUserSettingsStruct(&p->settings); + mir_free(p); + } + tempSettings.destroy(); + break; + } + return FALSE; +} + +int NetlibOptInitialise(WPARAM wParam, LPARAM) +{ + int optionsCount = 0; + { + mir_cslock lck(csNetlibUser); + for (auto &p : netlibUser) + if (!(p->user.flags & NUF_NOOPTIONS)) + ++optionsCount; + } + + if (optionsCount == 0) + return 0; + + OPTIONSDIALOGPAGE odp = {}; + odp.position = 900000000; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB); + odp.szTitle.a = LPGEN("Network"); + odp.pfnDlgProc = DlgProcNetlibOpts; + odp.flags = ODPF_BOLDGROUPS; + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/mir_app/src/netlib_pktrecver.cpp b/src/mir_app/src/netlib_pktrecver.cpp index 66621d43a3..3b86fed51d 100644 --- a/src/mir_app/src/netlib_pktrecver.cpp +++ b/src/mir_app/src/netlib_pktrecver.cpp @@ -1,80 +1,80 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize) -{ - if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) { - SetLastError(ERROR_INVALID_PARAMETER); - return nullptr; - } - - NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); - nlpr->handleType = NLH_PACKETRECVER; - nlpr->nlc = nlc; - nlpr->packetRecver.bufferSize = iMaxSize; - nlpr->packetRecver.buffer = (uint8_t*)mir_alloc(nlpr->packetRecver.bufferSize); - nlpr->packetRecver.bytesUsed = 0; - nlpr->packetRecver.bytesAvailable = 0; - return nlpr; -} - -MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam) -{ - NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver; - if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ - SetLastError(ERROR_TIMEOUT); - return SOCKET_ERROR; - } - nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout; - if (nlprParam->bytesUsed == 0) { - if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) { - nlpr->packetRecver.bytesAvailable = 0; - Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching"); - } - } - else { - memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed); - nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed; - } - - if (nlprParam->dwTimeout != INFINITE) { - if (!Netlib_SslPending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) { - *nlprParam = nlpr->packetRecver; - return SOCKET_ERROR; - } - } - - INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0); - if (recvResult > 0) - nlpr->packetRecver.bytesAvailable += recvResult; - *nlprParam = nlpr->packetRecver; - return recvResult; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize) +{ + if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) { + SetLastError(ERROR_INVALID_PARAMETER); + return nullptr; + } + + NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver)); + nlpr->handleType = NLH_PACKETRECVER; + nlpr->nlc = nlc; + nlpr->packetRecver.bufferSize = iMaxSize; + nlpr->packetRecver.buffer = (uint8_t*)mir_alloc(nlpr->packetRecver.bufferSize); + nlpr->packetRecver.bytesUsed = 0; + nlpr->packetRecver.bytesAvailable = 0; + return nlpr; +} + +MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam) +{ + NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver; + if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */ + SetLastError(ERROR_TIMEOUT); + return SOCKET_ERROR; + } + nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout; + if (nlprParam->bytesUsed == 0) { + if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) { + nlpr->packetRecver.bytesAvailable = 0; + Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching"); + } + } + else { + memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed); + nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed; + } + + if (nlprParam->dwTimeout != INFINITE) { + if (!Netlib_SslPending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) { + *nlprParam = nlpr->packetRecver; + return SOCKET_ERROR; + } + } + + INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0); + if (recvResult > 0) + nlpr->packetRecver.bytesAvailable += recvResult; + *nlprParam = nlpr->packetRecver; + return recvResult; +} diff --git a/src/mir_app/src/netlib_security.cpp b/src/mir_app/src/netlib_security.cpp index 278991b7e4..90aa9c68ff 100644 --- a/src/mir_app/src/netlib_security.cpp +++ b/src/mir_app/src/netlib_security.cpp @@ -1,363 +1,363 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#define SECURITY_WIN32 -#include -#include - -#pragma comment(lib, "secur32.lib") - -struct NtlmHandleType -{ - CtxtHandle hClientContext; - CredHandle hClientCredential; - wchar_t* szProvider; - wchar_t* szPrincipal; - unsigned cbMaxToken; - bool hasDomain; -}; - -struct NTLM_String -{ - uint16_t len; - uint16_t allocedSpace; - uint32_t offset; -}; - -struct NtlmType2packet -{ - char sign[8]; - uint32_t type; // == 2 - NTLM_String targetName; - uint32_t flags; - uint8_t challenge[8]; - uint8_t context[8]; - NTLM_String targetInfo; -}; - -static unsigned ntlmCnt = 0; -static mir_cs csSec; - -static void ReportSecError(SECURITY_STATUS scRet, int line) -{ - wchar_t szMsgBuf[256]; - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - - wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0; - - Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal) -{ - HANDLE hSecurity = nullptr; - - if (mir_wstrcmpi(szProvider, L"Basic") == 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->szProvider = mir_wstrdup(szProvider); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - - return hNtlm; - } - - mir_cslock lck(csSec); - - PSecPkgInfo ntlmSecurityPackageInfo; - SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo); - if (sc == SEC_E_OK) { - NtlmHandleType* hNtlm; - - hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); - hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; - FreeContextBuffer(ntlmSecurityPackageInfo); - - hNtlm->szProvider = mir_wstrdup(szProvider); - hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L""); - SecInvalidateHandle(&hNtlm->hClientContext); - SecInvalidateHandle(&hNtlm->hClientCredential); - ntlmCnt++; - } - return hSecurity; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity) -{ - if (hSecurity == nullptr) - return; - - mir_cslock lck(csSec); - - if (ntlmCnt != 0) { - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - if (hNtlm != nullptr) { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - mir_free(hNtlm->szProvider); - mir_free(hNtlm->szPrincipal); - mir_free(hNtlm); - } - - --ntlmCnt; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) -{ - if (!szChallenge || !szChallenge[0]) return nullptr; - - NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; - unsigned char inDataBuffer[1024]; - - SecBuffer inBuffers[2] = - { - { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, - { chlsz, SECBUFFER_STREAM, szChallenge } - }; - - SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; - - unsigned long qop = 0; - SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - // unsigned char LayerMask = inDataBuffer[0]; - // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); - - SecPkgContext_Sizes sizes; - sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); - unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); - - unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; - - SecBuffer outBuffers[3] = - { - { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, - { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, - { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } - }; - SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; - - sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - unsigned i, ressz = 0; - for (i = 0; i < outBuffersDesc.cBuffers; i++) - ressz += outBuffersDesc.pBuffers[i].cbBuffer; - - unsigned char *response = (unsigned char*)alloca(ressz), *p = response; - for (i = 0; i < outBuffersDesc.cBuffers; i++) { - memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); - p += outBuffersDesc.pBuffers[i].cbBuffer; - } - - return mir_base64_encode(response, ressz); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete) -{ - if (hSecurity == nullptr || ntlmCnt == 0) - return nullptr; - - SecBufferDesc outputBufferDescriptor, inputBufferDescriptor; - SecBuffer outputSecurityToken, inputSecurityToken; - char *szOutputToken; - - NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity; - - Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete); - - if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) { - bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0; - bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0'; - if (hasChallenge) { - size_t tokenLen; - uint8_t *token = (uint8_t*)mir_base64_decode(szChallenge, &tokenLen); - if (token == nullptr) - return nullptr; - - if (isGSSAPI && complete) - return CompleteGssapi(hSecurity, token, (unsigned)tokenLen); - - inputBufferDescriptor.cBuffers = 1; - inputBufferDescriptor.pBuffers = &inputSecurityToken; - inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - inputSecurityToken.BufferType = SECBUFFER_TOKEN; - inputSecurityToken.cbBuffer = (unsigned)tokenLen; - inputSecurityToken.pvBuffer = token; - - // try to decode the domain name from the NTLM challenge - if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) { - NtlmType2packet* pkt = (NtlmType2packet*)token; - if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { - - wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset]; - int domainLen = pkt->targetName.len; - - // Negotiate ANSI? if yes, convert the ANSI name to unicode - if ((pkt->flags & 1) == 0) { - int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0); - wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t)); - domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; - buf[domainLen] = 0; - domainName = buf; - } - else domainLen /= sizeof(wchar_t); - - if (domainLen) { - CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login); - char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete); - mir_free(szChl); - } - } - } - } - else { - if (SecIsValidHandle(&hNtlm->hClientContext)) - DeleteSecurityContext(&hNtlm->hClientContext); - if (SecIsValidHandle(&hNtlm->hClientCredential)) - FreeCredentialsHandle(&hNtlm->hClientCredential); - - SEC_WINNT_AUTH_IDENTITY auth; - - if (login != nullptr && login[0] != '\0') { - memset(&auth, 0, sizeof(auth)); - - Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); - - const wchar_t* loginName = login; - const wchar_t* domainName = wcschr(login, '\\'); - size_t domainLen = 0; - size_t loginLen = mir_wstrlen(loginName); - if (domainName != nullptr) { - loginName = domainName + 1; - loginLen = mir_wstrlen(loginName); - domainLen = domainName - login; - domainName = login; - } - else if ((domainName = wcschr(login, '@')) != nullptr) { - loginName = login; - loginLen = domainName - login; - domainLen = mir_wstrlen(++domainName); - } - - auth.User = (PWORD)loginName; - auth.UserLength = (ULONG)loginLen; - auth.Password = (PWORD)psw; - auth.PasswordLength = (ULONG)mir_wstrlen(psw); - auth.Domain = (PWORD)domainName; - auth.DomainLength = (ULONG)domainLen; - auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; - - hNtlm->hasDomain = domainLen != 0; - } - - TimeStamp tokenExpiration; - SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration); - if (sc != SEC_E_OK) { - ReportSecError(sc, __LINE__); - return nullptr; - } - } - - outputBufferDescriptor.cBuffers = 1; - outputBufferDescriptor.pBuffers = &outputSecurityToken; - outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; - outputSecurityToken.BufferType = SECBUFFER_TOKEN; - outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; - outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); - - ULONG contextAttributes; - TimeStamp tokenExpiration; - SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential, - hasChallenge ? &hNtlm->hClientContext : nullptr, - hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, - hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext, - &outputBufferDescriptor, &contextAttributes, &tokenExpiration); - Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc); - - complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); - if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { - sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); - Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc); - } - - if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { - ReportSecError(sc, __LINE__); - return nullptr; - } - - szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer); - } - else { - if (!login || !psw) - return nullptr; - - T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw)); - szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth)); - complete = true; - } - - if (szOutputToken == nullptr) - return nullptr; - - if (!http) - return szOutputToken; - - CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken); - mir_free(szOutputToken); - return szResult.Detach(); -} - -MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete) -{ - return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#define SECURITY_WIN32 +#include +#include + +#pragma comment(lib, "secur32.lib") + +struct NtlmHandleType +{ + CtxtHandle hClientContext; + CredHandle hClientCredential; + wchar_t* szProvider; + wchar_t* szPrincipal; + unsigned cbMaxToken; + bool hasDomain; +}; + +struct NTLM_String +{ + uint16_t len; + uint16_t allocedSpace; + uint32_t offset; +}; + +struct NtlmType2packet +{ + char sign[8]; + uint32_t type; // == 2 + NTLM_String targetName; + uint32_t flags; + uint8_t challenge[8]; + uint8_t context[8]; + NTLM_String targetInfo; +}; + +static unsigned ntlmCnt = 0; +static mir_cs csSec; + +static void ReportSecError(SECURITY_STATUS scRet, int line) +{ + wchar_t szMsgBuf[256]; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); + + wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0; + + Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal) +{ + HANDLE hSecurity = nullptr; + + if (mir_wstrcmpi(szProvider, L"Basic") == 0) { + NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); + hNtlm->szProvider = mir_wstrdup(szProvider); + SecInvalidateHandle(&hNtlm->hClientContext); + SecInvalidateHandle(&hNtlm->hClientCredential); + ntlmCnt++; + + return hNtlm; + } + + mir_cslock lck(csSec); + + PSecPkgInfo ntlmSecurityPackageInfo; + SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo); + if (sc == SEC_E_OK) { + NtlmHandleType* hNtlm; + + hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType)); + hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken; + FreeContextBuffer(ntlmSecurityPackageInfo); + + hNtlm->szProvider = mir_wstrdup(szProvider); + hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L""); + SecInvalidateHandle(&hNtlm->hClientContext); + SecInvalidateHandle(&hNtlm->hClientCredential); + ntlmCnt++; + } + return hSecurity; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity) +{ + if (hSecurity == nullptr) + return; + + mir_cslock lck(csSec); + + if (ntlmCnt != 0) { + NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; + if (hNtlm != nullptr) { + if (SecIsValidHandle(&hNtlm->hClientContext)) + DeleteSecurityContext(&hNtlm->hClientContext); + if (SecIsValidHandle(&hNtlm->hClientCredential)) + FreeCredentialsHandle(&hNtlm->hClientCredential); + mir_free(hNtlm->szProvider); + mir_free(hNtlm->szPrincipal); + mir_free(hNtlm); + } + + --ntlmCnt; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz) +{ + if (!szChallenge || !szChallenge[0]) return nullptr; + + NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity; + unsigned char inDataBuffer[1024]; + + SecBuffer inBuffers[2] = + { + { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer }, + { chlsz, SECBUFFER_STREAM, szChallenge } + }; + + SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers }; + + unsigned long qop = 0; + SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + // unsigned char LayerMask = inDataBuffer[0]; + // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]); + + SecPkgContext_Sizes sizes; + sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer); + unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize); + + unsigned char outDataBuffer[4] = { 1, 0, 16, 0 }; + + SecBuffer outBuffers[3] = + { + { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer }, + { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer }, + { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer } + }; + SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers }; + + sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + unsigned i, ressz = 0; + for (i = 0; i < outBuffersDesc.cBuffers; i++) + ressz += outBuffersDesc.pBuffers[i].cbBuffer; + + unsigned char *response = (unsigned char*)alloca(ressz), *p = response; + for (i = 0; i < outBuffersDesc.cBuffers; i++) { + memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer); + p += outBuffersDesc.pBuffers[i].cbBuffer; + } + + return mir_base64_encode(response, ressz); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete) +{ + if (hSecurity == nullptr || ntlmCnt == 0) + return nullptr; + + SecBufferDesc outputBufferDescriptor, inputBufferDescriptor; + SecBuffer outputSecurityToken, inputSecurityToken; + char *szOutputToken; + + NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity; + + Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete); + + if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) { + bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0; + bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0'; + if (hasChallenge) { + size_t tokenLen; + uint8_t *token = (uint8_t*)mir_base64_decode(szChallenge, &tokenLen); + if (token == nullptr) + return nullptr; + + if (isGSSAPI && complete) + return CompleteGssapi(hSecurity, token, (unsigned)tokenLen); + + inputBufferDescriptor.cBuffers = 1; + inputBufferDescriptor.pBuffers = &inputSecurityToken; + inputBufferDescriptor.ulVersion = SECBUFFER_VERSION; + inputSecurityToken.BufferType = SECBUFFER_TOKEN; + inputSecurityToken.cbBuffer = (unsigned)tokenLen; + inputSecurityToken.pvBuffer = token; + + // try to decode the domain name from the NTLM challenge + if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) { + NtlmType2packet* pkt = (NtlmType2packet*)token; + if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) { + + wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset]; + int domainLen = pkt->targetName.len; + + // Negotiate ANSI? if yes, convert the ANSI name to unicode + if ((pkt->flags & 1) == 0) { + int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0); + wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t)); + domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1; + buf[domainLen] = 0; + domainName = buf; + } + else domainLen /= sizeof(wchar_t); + + if (domainLen) { + CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login); + char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete); + mir_free(szChl); + } + } + } + } + else { + if (SecIsValidHandle(&hNtlm->hClientContext)) + DeleteSecurityContext(&hNtlm->hClientContext); + if (SecIsValidHandle(&hNtlm->hClientCredential)) + FreeCredentialsHandle(&hNtlm->hClientCredential); + + SEC_WINNT_AUTH_IDENTITY auth; + + if (login != nullptr && login[0] != '\0') { + memset(&auth, 0, sizeof(auth)); + + Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)"); + + const wchar_t* loginName = login; + const wchar_t* domainName = wcschr(login, '\\'); + size_t domainLen = 0; + size_t loginLen = mir_wstrlen(loginName); + if (domainName != nullptr) { + loginName = domainName + 1; + loginLen = mir_wstrlen(loginName); + domainLen = domainName - login; + domainName = login; + } + else if ((domainName = wcschr(login, '@')) != nullptr) { + loginName = login; + loginLen = domainName - login; + domainLen = mir_wstrlen(++domainName); + } + + auth.User = (PWORD)loginName; + auth.UserLength = (ULONG)loginLen; + auth.Password = (PWORD)psw; + auth.PasswordLength = (ULONG)mir_wstrlen(psw); + auth.Domain = (PWORD)domainName; + auth.DomainLength = (ULONG)domainLen; + auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + + hNtlm->hasDomain = domainLen != 0; + } + + TimeStamp tokenExpiration; + SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration); + if (sc != SEC_E_OK) { + ReportSecError(sc, __LINE__); + return nullptr; + } + } + + outputBufferDescriptor.cBuffers = 1; + outputBufferDescriptor.pBuffers = &outputSecurityToken; + outputBufferDescriptor.ulVersion = SECBUFFER_VERSION; + outputSecurityToken.BufferType = SECBUFFER_TOKEN; + outputSecurityToken.cbBuffer = hNtlm->cbMaxToken; + outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer); + + ULONG contextAttributes; + TimeStamp tokenExpiration; + SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential, + hasChallenge ? &hNtlm->hClientContext : nullptr, + hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP, + hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext, + &outputBufferDescriptor, &contextAttributes, &tokenExpiration); + Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc); + + complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED); + if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) { + sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor); + Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc); + } + + if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) { + ReportSecError(sc, __LINE__); + return nullptr; + } + + szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer); + } + else { + if (!login || !psw) + return nullptr; + + T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw)); + szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth)); + complete = true; + } + + if (szOutputToken == nullptr) + return nullptr; + + if (!http) + return szOutputToken; + + CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken); + mir_free(szOutputToken); + return szResult.Detach(); +} + +MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete) +{ + return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete); +} diff --git a/src/mir_app/src/netlib_sock.cpp b/src/mir_app/src/netlib_sock.cpp index ebd7f3a7ea..032bfed993 100644 --- a/src/mir_app/src/netlib_sock.cpp +++ b/src/mir_app/src/netlib_sock.cpp @@ -1,287 +1,287 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -extern HANDLE hSendEvent, hRecvEvent; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) - return SOCKET_ERROR; - - int result; - Netlib_Dump(nlc, (uint8_t*)buf, len, true, flags); - if (nlc->hSsl) - result = Netlib_SslWrite(nlc->hSsl, buf, len); - else - result = send(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsSend); - - NETLIBNOTIFY nln = { buf, len, flags, result }; - NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags) -{ - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return SOCKET_ERROR; - - int recvResult; - if (!nlc->foreBuf.isEmpty()) { - recvResult = min(len, (int)nlc->foreBuf.length()); - memcpy(buf, nlc->foreBuf.data(), recvResult); - nlc->foreBuf.remove(recvResult); - } - else if (nlc->hSsl) - recvResult = Netlib_SslRead(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0); - else - recvResult = recv(nlc->s, buf, len, flags & 0xFFFF); - - NetlibLeaveNestedCS(&nlc->ncsRecv); - if (recvResult <= 0) - return recvResult; - - Netlib_Dump(nlc, (uint8_t*)buf, recvResult, false, flags); - - if ((flags & MSG_PEEK) == 0) { - NETLIBNOTIFY nln = { buf, len, flags, recvResult }; - NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); - } - return recvResult; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending) -{ - FD_ZERO(fd); - for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) { - NetlibConnection *nlcCheck = hConns[i]; - if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) { - SetLastError(ERROR_INVALID_DATA); - return 0; - } - FD_SET(nlcCheck->s, fd); - if (!nlcCheck->foreBuf.isEmpty() || Netlib_SslPending(nlcCheck->hSsl)) - pending++; - } - return 1; -} - -MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - { - mir_cslock lock(csConnectionHeader); - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { - return SOCKET_ERROR; - } - } - if (pending) - return 1; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); -} - -MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls) -{ - if (nls == nullptr) { - SetLastError(ERROR_INVALID_PARAMETER); - return SOCKET_ERROR; - } - - int pending = 0; - fd_set readfd, writefd, exceptfd; - - TIMEVAL tv; - tv.tv_sec = nls->dwTimeout / 1000; - tv.tv_usec = (nls->dwTimeout % 1000) * 1000; - { - mir_cslock lock(csConnectionHeader); - - if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) - || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) - || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { - return SOCKET_ERROR; - } - } - - int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); - - mir_cslock lock(csConnectionHeader); - /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() - to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) - This happens for read/write/except */ - NetlibConnection *conn = nullptr; - int j; - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hReadConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - - if (Netlib_SslPending(conn->hSsl)) - nls->hReadStatus[j] = TRUE; - nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hWriteConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd); - } - - for (j = 0; j < FD_SETSIZE; j++) { - conn = (NetlibConnection*)nls->hExceptConns[j]; - if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; - nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd); - } - return rc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr) -{ - if (!str) return false; - - int len = sizeof(SOCKADDR_INET_M); - return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len); -} - -MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr) -{ - char saddr[128]; - DWORD len = sizeof(saddr); - if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len)) - return mir_strdup(saddr); - - if (addr->sin_family == AF_INET) { - char *szIp = inet_ntoa(addr->sin_addr); - if (addr->sin_port != 0) { - mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port); - return mir_strdup(saddr); - } - return mir_strdup(szIp); - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN nlc, NETLIBCONNINFO *connInfo) -{ - if (!nlc || !connInfo) - return 1; - - sockaddr_in sin = { 0 }; - int len = sizeof(sin); - if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) { - connInfo->wPort = ntohs(sin.sin_port); - connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0; - strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE); - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -inline bool IsAddrGlobal(const IN6_ADDR *a) -{ - unsigned char High = a->s6_bytes[0] & 0xf0; - return High != 0 && High != 0xf0; -} - -MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly) -{ - addrinfo *air = nullptr, *ai, hints = { 0 }; - const char *szMyHost = ""; - - hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_PASSIVE; - - if (GetAddrInfoA(szMyHost, nullptr, &hints, &air)) - return nullptr; - - unsigned n = 0; - for (ai = air; ai; ai = ai->ai_next) { - SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) - ++n; - } - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - unsigned i = 0; - for (ai = air; ai; ai = ai->ai_next) { - sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr; - if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) { - char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr); - if (szIp) - strncpy_s(addr->szIp[i++], szIp, _TRUNCATE); - mir_free(szIp); - } - } - FreeAddrInfoA(air); - return addr; -} - -static NETLIBIPLIST* GetMyIpv4(void) -{ - char hostname[256] = ""; - - gethostname(hostname, sizeof(hostname)); - PHOSTENT he = gethostbyname(hostname); - - unsigned n; - for (n = 0; he->h_addr_list[n]; ++n) - ; - - NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); - addr->cbNum = n; - - for (unsigned i = 0; i < n; i++) - strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE); - - return addr; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +extern HANDLE hSendEvent, hRecvEvent; + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags) +{ + if (!NetlibEnterNestedCS(nlc, NLNCS_SEND)) + return SOCKET_ERROR; + + int result; + Netlib_Dump(nlc, (uint8_t*)buf, len, true, flags); + if (nlc->hSsl) + result = Netlib_SslWrite(nlc->hSsl, buf, len); + else + result = send(nlc->s, buf, len, flags & 0xFFFF); + + NetlibLeaveNestedCS(&nlc->ncsSend); + + NETLIBNOTIFY nln = { buf, len, flags, result }; + NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags) +{ + if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) + return SOCKET_ERROR; + + int recvResult; + if (!nlc->foreBuf.isEmpty()) { + recvResult = min(len, (int)nlc->foreBuf.length()); + memcpy(buf, nlc->foreBuf.data(), recvResult); + nlc->foreBuf.remove(recvResult); + } + else if (nlc->hSsl) + recvResult = Netlib_SslRead(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0); + else + recvResult = recv(nlc->s, buf, len, flags & 0xFFFF); + + NetlibLeaveNestedCS(&nlc->ncsRecv); + if (recvResult <= 0) + return recvResult; + + Netlib_Dump(nlc, (uint8_t*)buf, recvResult, false, flags); + + if ((flags & MSG_PEEK) == 0) { + NETLIBNOTIFY nln = { buf, len, flags, recvResult }; + NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user); + } + return recvResult; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending) +{ + FD_ZERO(fd); + for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) { + NetlibConnection *nlcCheck = hConns[i]; + if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) { + SetLastError(ERROR_INVALID_DATA); + return 0; + } + FD_SET(nlcCheck->s, fd); + if (!nlcCheck->foreBuf.isEmpty() || Netlib_SslPending(nlcCheck->hSsl)) + pending++; + } + return 1; +} + +MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls) +{ + if (nls == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + int pending = 0; + fd_set readfd, writefd, exceptfd; + { + mir_cslock lock(csConnectionHeader); + if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) + || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) + || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { + return SOCKET_ERROR; + } + } + if (pending) + return 1; + + TIMEVAL tv; + tv.tv_sec = nls->dwTimeout / 1000; + tv.tv_usec = (nls->dwTimeout % 1000) * 1000; + return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); +} + +MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls) +{ + if (nls == nullptr) { + SetLastError(ERROR_INVALID_PARAMETER); + return SOCKET_ERROR; + } + + int pending = 0; + fd_set readfd, writefd, exceptfd; + + TIMEVAL tv; + tv.tv_sec = nls->dwTimeout / 1000; + tv.tv_usec = (nls->dwTimeout % 1000) * 1000; + { + mir_cslock lock(csConnectionHeader); + + if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending) + || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending) + || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending)) { + return SOCKET_ERROR; + } + } + + int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv); + + mir_cslock lock(csConnectionHeader); + /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET() + to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not) + This happens for read/write/except */ + NetlibConnection *conn = nullptr; + int j; + for (j = 0; j < FD_SETSIZE; j++) { + conn = (NetlibConnection*)nls->hReadConns[j]; + if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; + + if (Netlib_SslPending(conn->hSsl)) + nls->hReadStatus[j] = TRUE; + nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd); + } + + for (j = 0; j < FD_SETSIZE; j++) { + conn = (NetlibConnection*)nls->hWriteConns[j]; + if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; + nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd); + } + + for (j = 0; j < FD_SETSIZE; j++) { + conn = (NetlibConnection*)nls->hExceptConns[j]; + if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break; + nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd); + } + return rc; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr) +{ + if (!str) return false; + + int len = sizeof(SOCKADDR_INET_M); + return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len); +} + +MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr) +{ + char saddr[128]; + DWORD len = sizeof(saddr); + if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len)) + return mir_strdup(saddr); + + if (addr->sin_family == AF_INET) { + char *szIp = inet_ntoa(addr->sin_addr); + if (addr->sin_port != 0) { + mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port); + return mir_strdup(saddr); + } + return mir_strdup(szIp); + } + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN nlc, NETLIBCONNINFO *connInfo) +{ + if (!nlc || !connInfo) + return 1; + + sockaddr_in sin = { 0 }; + int len = sizeof(sin); + if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) { + connInfo->wPort = ntohs(sin.sin_port); + connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0; + strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE); + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +inline bool IsAddrGlobal(const IN6_ADDR *a) +{ + unsigned char High = a->s6_bytes[0] & 0xf0; + return High != 0 && High != 0xf0; +} + +MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly) +{ + addrinfo *air = nullptr, *ai, hints = { 0 }; + const char *szMyHost = ""; + + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + + if (GetAddrInfoA(szMyHost, nullptr, &hints, &air)) + return nullptr; + + unsigned n = 0; + for (ai = air; ai; ai = ai->ai_next) { + SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr; + if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr)))) + ++n; + } + + NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); + addr->cbNum = n; + + unsigned i = 0; + for (ai = air; ai; ai = ai->ai_next) { + sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr; + if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) { + char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr); + if (szIp) + strncpy_s(addr->szIp[i++], szIp, _TRUNCATE); + mir_free(szIp); + } + } + FreeAddrInfoA(air); + return addr; +} + +static NETLIBIPLIST* GetMyIpv4(void) +{ + char hostname[256] = ""; + + gethostname(hostname, sizeof(hostname)); + PHOSTENT he = gethostbyname(hostname); + + unsigned n; + for (n = 0; he->h_addr_list[n]; ++n) + ; + + NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4); + addr->cbNum = n; + + for (unsigned i = 0; i < n; i++) + strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE); + + return addr; +} diff --git a/src/mir_app/src/netlib_ssl.cpp b/src/mir_app/src/netlib_ssl.cpp index d2ab355ec6..14d2ca5d13 100644 --- a/src/mir_app/src/netlib_ssl.cpp +++ b/src/mir_app/src/netlib_ssl.cpp @@ -1,465 +1,465 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include -#include -#include - -static bool bSslInitDone; - -enum SocketState -{ - sockOpen, - sockClosed, - sockError -}; - -struct SslHandle : public MZeroedObject -{ - ~SslHandle() - { - if (session) - SSL_free(session); - } - - SOCKET s; - SSL *session; - SocketState state; -}; - -static SSL_CTX *g_ctx; -static mir_cs csSsl; - -static void dump_error(SSL *session, int err) -{ - err = SSL_get_error(session, err); - - char buf[100]; - ERR_error_string_n(err, buf, sizeof(buf)); - Netlib_Logf(nullptr, "SSL negotiation failure: %s (%d)", buf, err); -} - -const char* SSL_GetCipherName(SslHandle *ssl) -{ - if (!ssl || !ssl->session) - return nullptr; - - return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl->session)); -} - -static void ReportSslError(SECURITY_STATUS scRet, int line, bool = false) -{ - CMStringW tszMsg(FORMAT, L"SSL connection failure(%x %u) :", scRet, line); - - switch (scRet) { - case 0: - case ERROR_NOT_READY: - return; - - case SEC_E_INVALID_TOKEN: - tszMsg += TranslateW_LP(L"Client cannot decode host message. Possible causes: host does not support SSL or requires not existing security package"); - break; - - case CERT_E_CN_NO_MATCH: - case SEC_E_WRONG_PRINCIPAL: - tszMsg += TranslateW_LP(L"Host we are connecting to is not the one certificate was issued for"); - break; - - default: - wchar_t szMsgBuf[256]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); - tszMsg += szMsgBuf; - } - - Netlib_LogfW(nullptr, tszMsg); - - SetLastError(scRet); - PUShowMessageW(tszMsg.GetBuffer(), SM_WARNING); -} - -static PCCERT_CONTEXT SSL_X509ToCryptCert(X509 * x509) -{ - unsigned char *buf = nullptr; - PCCERT_CONTEXT pCertContext = nullptr; - - int len = i2d_X509(x509, &buf); - if ((len >= 0) && buf) { - pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); - - CRYPTO_free(buf, __FILE__, __LINE__); - } - return pCertContext; -} - -static PCCERT_CONTEXT SSL_CertChainToCryptAnchor(SSL* session) -{ - /* convert the active certificate chain provided in the handshake of 'session' into - the format used by CryptAPI. - */ - PCCERT_CONTEXT anchor = nullptr; - // create cert store - HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr); - - if (store) { - X509 *server_cert = SSL_get_peer_certificate(session); - if (server_cert) { - // add the server's cert first, to make sure CryptAPI builds the correct chain - PCCERT_CONTEXT primary_cert; - BOOL ok = CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(server_cert), CERT_STORE_ADD_ALWAYS, &primary_cert); - if (ok && primary_cert) { - // add all remaining certs to store (note: stack needs not be freed, it is not a copy) - STACK_OF(X509) *server_chain = SSL_get_peer_cert_chain(session); - if (server_chain) { - for (int i = 0; i < OPENSSL_sk_num((OPENSSL_STACK *)server_chain); i++) { - X509 *next_cert = (X509 *)OPENSSL_sk_value((OPENSSL_STACK *)server_chain, i); - CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(next_cert), CERT_STORE_ADD_USE_EXISTING, nullptr); - } - } - - // return primary cert; MUST be freed by caller which will free the associated store - anchor = primary_cert; - } - else { - if (primary_cert) - CertFreeCertificateContext(primary_cert); - } - - X509_free(server_cert); - } - - CertCloseStore(store, 0); - } - - return anchor; -} - -static LPSTR rgszUsages[] = -{ - szOID_PKIX_KP_SERVER_AUTH, - szOID_SERVER_GATED_CRYPTO, - szOID_SGC_NETSCAPE -}; - -static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, uint32_t dwCertFlags) -{ - uint32_t scRet; - - ptrW pwszServerName(mir_a2u(pszServerName)); - - HTTPSPolicyCallbackData polHttps = {}; - CERT_CHAIN_POLICY_PARA PolicyPara = {}; - CERT_CHAIN_POLICY_STATUS PolicyStatus = {}; - CERT_CHAIN_PARA ChainPara = {}; - - PCCERT_CHAIN_CONTEXT pChainContext = nullptr; - PCCERT_CONTEXT pServerCert = SSL_CertChainToCryptAnchor(ssl->session); - if (pServerCert == nullptr) { - scRet = SEC_E_WRONG_PRINCIPAL; - goto cleanup; - } - - ChainPara.cbSize = sizeof(ChainPara); - ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; - ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages); - ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; - - if (!CertGetCertificateChain(nullptr, pServerCert, nullptr, pServerCert->hCertStore, &ChainPara, 0, nullptr, &pChainContext)) { - scRet = GetLastError(); - goto cleanup; - } - - polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); - polHttps.dwAuthType = AUTHTYPE_SERVER; - polHttps.fdwChecks = dwCertFlags; - polHttps.pwszServerName = pwszServerName; - - PolicyPara.cbSize = sizeof(PolicyPara); - PolicyPara.pvExtraPolicyPara = &polHttps; - - PolicyStatus.cbSize = sizeof(PolicyStatus); - - if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { - scRet = GetLastError(); - goto cleanup; - } - - if (PolicyStatus.dwError) { - scRet = PolicyStatus.dwError; - goto cleanup; - } - - scRet = SEC_E_OK; - -cleanup: - if (pChainContext) - CertFreeCertificateChain(pChainContext); - if (pServerCert) - CertFreeCertificateContext(pServerCert); - - ReportSslError(scRet, __LINE__, true); - return scRet == SEC_E_OK; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// negotiate SSL session, verify cert, return NULL if failed - -MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify) -{ - std::unique_ptr ssl(new SslHandle()); - ssl->s = s; - { - mir_cslock lck(csSsl); - ssl->session = SSL_new(g_ctx); - } - - if (!ssl->session) { - Netlib_Logf(nullptr, "SSL setup failure: session"); - return false; - } - SSL_set_fd(ssl->session, ssl->s); - - SSL_set_tlsext_host_name(ssl->session, host); - - int err = SSL_connect(ssl->session); - if (err != 1) { - dump_error(ssl->session, err); - return nullptr; - } - - if (verify) { - uint32_t dwFlags = 0; - if (!host || inet_addr(host) != INADDR_NONE) - dwFlags |= 0x00001000; - if (!VerifyCertificate(ssl.get(), host, dwFlags)) - return nullptr; - } - - return ssl.release(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// return true if there is either unsend or buffered received data (ie. after peek) - -MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl) -{ - return ssl && ssl->session && (SSL_pending(ssl->session) > 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// reads number of bytes, keeps in buffer if peek != 0 - -MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek) -{ - if (!ssl || !ssl->session) return SOCKET_ERROR; - if (num <= 0) return 0; - - int err = 0; - if (peek) - err = SSL_peek(ssl->session, buf, num); - else - err = SSL_read(ssl->session, buf, num); - - if (err <= 0) { - int err2 = SSL_get_error(ssl->session, err); - if (err2 == SSL_ERROR_ZERO_RETURN) { - Netlib_Logf(nullptr, "SSL connection gracefully closed"); - ssl->state = sockClosed; - return 0; - } - - int err3 = ERR_get_error(); - if (err3) { - Netlib_Logf(nullptr, "SSL failure recieving data (%d, %d, %d, %d)", err, err2, err3, WSAGetLastError()); - ssl->state = sockError; - return SOCKET_ERROR; - } - } - - return err; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// writes data to the SSL socket - -MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num) -{ - if (!ssl || !ssl->session) - return SOCKET_ERROR; - if (num <= 0) - return 0; - - int err = SSL_write(ssl->session, buf, num); - if (err > 0) - return err; - - int err2 = SSL_get_error(ssl->session, err); - switch (err2) { - case SSL_ERROR_ZERO_RETURN: - Netlib_Logf(nullptr, "SSL connection gracefully closed"); - ssl->state = sockClosed; - break; - - default: - Netlib_Logf(nullptr, "SSL failure sending data (%d, %d, %d)", err, err2, WSAGetLastError()); - ssl->state = sockError; - return SOCKET_ERROR; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// closes SSL session, but keeps socket open - -MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl) -{ - if (ssl && ssl->session) { - SOCKET s = SSL_get_fd(ssl->session); - if (s != -1) - shutdown(s, SD_BOTH); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// frees all data associated with the SSL socket - -MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl) -{ - delete ssl; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// makes connection SSL -// returns 0 on failure / 1 on success - -MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (nlc == nullptr) - return 0; - - NetlibUser *nlu = nlc->nlu; - if (szHost == nullptr) - szHost = nlc->url.szHost; - szHost = NEWSTR_ALLOCA(szHost); - - Netlib_Logf(nlu, "(%d %s) Starting SSL/TLS negotiation", int(nlc->s), szHost); - - nlc->hSsl = Netlib_SslConnect(nlc->s, szHost, nlu->settings.validateSSL); - if (nlc->hSsl == nullptr) - Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL/TLS connection", int(nlc->s), szHost); - else - Netlib_Logf(nlu, "(%d %s) SSL/TLS negotiation successful", int(nlc->s), szHost); - - return nlc->hSsl != nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// gets TLS channel binging data for a socket - -static char TLS13_Label[] = "EXPORTER-Channel-Binding"; - -MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer) -{ - if (nlc == nullptr || nlc->hSsl == nullptr) - return nullptr; - - char buf[1000]; - auto *pszVersion = SSL_get_version(nlc->hSsl->session); - if (tlsVer && !mir_strcmp(pszVersion, "TLSv1.3")) { - int res = SSL_export_keying_material(nlc->hSsl->session, - (uint8_t *)buf, 32, TLS13_Label, sizeof(TLS13_Label) - 1, 0, 0, 0); - if (res == 1) { - tlsVer = 13; - void *pBuf = mir_alloc(cbLen = 32); - memcpy(pBuf, buf, cbLen); - return pBuf; - } - } - - size_t len = SSL_get_finished(nlc->hSsl->session, buf, sizeof(buf)); - if (len == 0) - return nullptr; - - tlsVer = 12; - cbLen = (int)len; - void *pBuf = mir_alloc(len); - memcpy(pBuf, buf, len); - return pBuf; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// module entry point - -bool OpenSsl_Init(void) -{ - /* Load Library Pointers */ - if (bSslInitDone) - return true; - - if (!bSslInitDone) { // init OpenSSL - SSL_library_init(); - SSL_load_error_strings(); - // FIXME check errors - - const SSL_METHOD *meth = TLS_client_method(); - if (!meth) { - Netlib_Logf(nullptr, "SSL setup failure: client method"); - return false; - } - - g_ctx = SSL_CTX_new(meth); - if (!g_ctx) { - Netlib_Logf(nullptr, "SSL setup failure: context"); - return false; - } - - VARSW wszPemFile(L"%miranda_path%\\libs\\microsoft.pem"); - SSL_CTX_load_verify_locations(g_ctx, _T2A(wszPemFile), NULL); - - // SSL_read/write should transparently handle renegotiations - SSL_CTX_ctrl(g_ctx, SSL_CTRL_MODE, SSL_MODE_AUTO_RETRY, nullptr); - // SSL_CTX_set_quiet_shutdown(g_ctx, TRUE); - - RAND_screen(); - - bSslInitDone = true; - } - - return bSslInitDone; -} - -void OpenSsl_Unload(void) -{ - /* Load Library Pointers */ - if (!bSslInitDone) - return; - - if (g_ctx) - SSL_CTX_free(g_ctx); - - bSslInitDone = false; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#include +#include +#include + +static bool bSslInitDone; + +enum SocketState +{ + sockOpen, + sockClosed, + sockError +}; + +struct SslHandle : public MZeroedObject +{ + ~SslHandle() + { + if (session) + SSL_free(session); + } + + SOCKET s; + SSL *session; + SocketState state; +}; + +static SSL_CTX *g_ctx; +static mir_cs csSsl; + +static void dump_error(SSL *session, int err) +{ + err = SSL_get_error(session, err); + + char buf[100]; + ERR_error_string_n(err, buf, sizeof(buf)); + Netlib_Logf(nullptr, "SSL negotiation failure: %s (%d)", buf, err); +} + +const char* SSL_GetCipherName(SslHandle *ssl) +{ + if (!ssl || !ssl->session) + return nullptr; + + return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl->session)); +} + +static void ReportSslError(SECURITY_STATUS scRet, int line, bool = false) +{ + CMStringW tszMsg(FORMAT, L"SSL connection failure(%x %u) :", scRet, line); + + switch (scRet) { + case 0: + case ERROR_NOT_READY: + return; + + case SEC_E_INVALID_TOKEN: + tszMsg += TranslateW_LP(L"Client cannot decode host message. Possible causes: host does not support SSL or requires not existing security package"); + break; + + case CERT_E_CN_NO_MATCH: + case SEC_E_WRONG_PRINCIPAL: + tszMsg += TranslateW_LP(L"Host we are connecting to is not the one certificate was issued for"); + break; + + default: + wchar_t szMsgBuf[256]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr); + tszMsg += szMsgBuf; + } + + Netlib_LogfW(nullptr, tszMsg); + + SetLastError(scRet); + PUShowMessageW(tszMsg.GetBuffer(), SM_WARNING); +} + +static PCCERT_CONTEXT SSL_X509ToCryptCert(X509 * x509) +{ + unsigned char *buf = nullptr; + PCCERT_CONTEXT pCertContext = nullptr; + + int len = i2d_X509(x509, &buf); + if ((len >= 0) && buf) { + pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); + + CRYPTO_free(buf, __FILE__, __LINE__); + } + return pCertContext; +} + +static PCCERT_CONTEXT SSL_CertChainToCryptAnchor(SSL* session) +{ + /* convert the active certificate chain provided in the handshake of 'session' into + the format used by CryptAPI. + */ + PCCERT_CONTEXT anchor = nullptr; + // create cert store + HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, nullptr); + + if (store) { + X509 *server_cert = SSL_get_peer_certificate(session); + if (server_cert) { + // add the server's cert first, to make sure CryptAPI builds the correct chain + PCCERT_CONTEXT primary_cert; + BOOL ok = CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(server_cert), CERT_STORE_ADD_ALWAYS, &primary_cert); + if (ok && primary_cert) { + // add all remaining certs to store (note: stack needs not be freed, it is not a copy) + STACK_OF(X509) *server_chain = SSL_get_peer_cert_chain(session); + if (server_chain) { + for (int i = 0; i < OPENSSL_sk_num((OPENSSL_STACK *)server_chain); i++) { + X509 *next_cert = (X509 *)OPENSSL_sk_value((OPENSSL_STACK *)server_chain, i); + CertAddCertificateContextToStore(store, SSL_X509ToCryptCert(next_cert), CERT_STORE_ADD_USE_EXISTING, nullptr); + } + } + + // return primary cert; MUST be freed by caller which will free the associated store + anchor = primary_cert; + } + else { + if (primary_cert) + CertFreeCertificateContext(primary_cert); + } + + X509_free(server_cert); + } + + CertCloseStore(store, 0); + } + + return anchor; +} + +static LPSTR rgszUsages[] = +{ + szOID_PKIX_KP_SERVER_AUTH, + szOID_SERVER_GATED_CRYPTO, + szOID_SGC_NETSCAPE +}; + +static bool VerifyCertificate(SslHandle *ssl, PCSTR pszServerName, uint32_t dwCertFlags) +{ + uint32_t scRet; + + ptrW pwszServerName(mir_a2u(pszServerName)); + + HTTPSPolicyCallbackData polHttps = {}; + CERT_CHAIN_POLICY_PARA PolicyPara = {}; + CERT_CHAIN_POLICY_STATUS PolicyStatus = {}; + CERT_CHAIN_PARA ChainPara = {}; + + PCCERT_CHAIN_CONTEXT pChainContext = nullptr; + PCCERT_CONTEXT pServerCert = SSL_CertChainToCryptAnchor(ssl->session); + if (pServerCert == nullptr) { + scRet = SEC_E_WRONG_PRINCIPAL; + goto cleanup; + } + + ChainPara.cbSize = sizeof(ChainPara); + ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + ChainPara.RequestedUsage.Usage.cUsageIdentifier = _countof(rgszUsages); + ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; + + if (!CertGetCertificateChain(nullptr, pServerCert, nullptr, pServerCert->hCertStore, &ChainPara, 0, nullptr, &pChainContext)) { + scRet = GetLastError(); + goto cleanup; + } + + polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); + polHttps.dwAuthType = AUTHTYPE_SERVER; + polHttps.fdwChecks = dwCertFlags; + polHttps.pwszServerName = pwszServerName; + + PolicyPara.cbSize = sizeof(PolicyPara); + PolicyPara.pvExtraPolicyPara = &polHttps; + + PolicyStatus.cbSize = sizeof(PolicyStatus); + + if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)) { + scRet = GetLastError(); + goto cleanup; + } + + if (PolicyStatus.dwError) { + scRet = PolicyStatus.dwError; + goto cleanup; + } + + scRet = SEC_E_OK; + +cleanup: + if (pChainContext) + CertFreeCertificateChain(pChainContext); + if (pServerCert) + CertFreeCertificateContext(pServerCert); + + ReportSslError(scRet, __LINE__, true); + return scRet == SEC_E_OK; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// negotiate SSL session, verify cert, return NULL if failed + +MIR_APP_DLL(HSSL) Netlib_SslConnect(SOCKET s, const char* host, int verify) +{ + std::unique_ptr ssl(new SslHandle()); + ssl->s = s; + { + mir_cslock lck(csSsl); + ssl->session = SSL_new(g_ctx); + } + + if (!ssl->session) { + Netlib_Logf(nullptr, "SSL setup failure: session"); + return false; + } + SSL_set_fd(ssl->session, ssl->s); + + SSL_set_tlsext_host_name(ssl->session, host); + + int err = SSL_connect(ssl->session); + if (err != 1) { + dump_error(ssl->session, err); + return nullptr; + } + + if (verify) { + uint32_t dwFlags = 0; + if (!host || inet_addr(host) != INADDR_NONE) + dwFlags |= 0x00001000; + if (!VerifyCertificate(ssl.get(), host, dwFlags)) + return nullptr; + } + + return ssl.release(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// return true if there is either unsend or buffered received data (ie. after peek) + +MIR_APP_DLL(BOOL) Netlib_SslPending(HSSL ssl) +{ + return ssl && ssl->session && (SSL_pending(ssl->session) > 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// reads number of bytes, keeps in buffer if peek != 0 + +MIR_APP_DLL(int) Netlib_SslRead(HSSL ssl, char *buf, int num, int peek) +{ + if (!ssl || !ssl->session) return SOCKET_ERROR; + if (num <= 0) return 0; + + int err = 0; + if (peek) + err = SSL_peek(ssl->session, buf, num); + else + err = SSL_read(ssl->session, buf, num); + + if (err <= 0) { + int err2 = SSL_get_error(ssl->session, err); + if (err2 == SSL_ERROR_ZERO_RETURN) { + Netlib_Logf(nullptr, "SSL connection gracefully closed"); + ssl->state = sockClosed; + return 0; + } + + int err3 = ERR_get_error(); + if (err3) { + Netlib_Logf(nullptr, "SSL failure recieving data (%d, %d, %d, %d)", err, err2, err3, WSAGetLastError()); + ssl->state = sockError; + return SOCKET_ERROR; + } + } + + return err; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// writes data to the SSL socket + +MIR_APP_DLL(int) Netlib_SslWrite(HSSL ssl, const char *buf, int num) +{ + if (!ssl || !ssl->session) + return SOCKET_ERROR; + if (num <= 0) + return 0; + + int err = SSL_write(ssl->session, buf, num); + if (err > 0) + return err; + + int err2 = SSL_get_error(ssl->session, err); + switch (err2) { + case SSL_ERROR_ZERO_RETURN: + Netlib_Logf(nullptr, "SSL connection gracefully closed"); + ssl->state = sockClosed; + break; + + default: + Netlib_Logf(nullptr, "SSL failure sending data (%d, %d, %d)", err, err2, WSAGetLastError()); + ssl->state = sockError; + return SOCKET_ERROR; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// closes SSL session, but keeps socket open + +MIR_APP_DLL(void) Netlib_SslShutdown(HSSL ssl) +{ + if (ssl && ssl->session) { + SOCKET s = SSL_get_fd(ssl->session); + if (s != -1) + shutdown(s, SD_BOTH); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// frees all data associated with the SSL socket + +MIR_APP_DLL(void) Netlib_SslFree(HSSL ssl) +{ + delete ssl; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// makes connection SSL +// returns 0 on failure / 1 on success + +MIR_APP_DLL(int) Netlib_StartSsl(HNETLIBCONN hConnection, const char *szHost) +{ + NetlibConnection *nlc = (NetlibConnection*)hConnection; + if (nlc == nullptr) + return 0; + + NetlibUser *nlu = nlc->nlu; + if (szHost == nullptr) + szHost = nlc->url.szHost; + szHost = NEWSTR_ALLOCA(szHost); + + Netlib_Logf(nlu, "(%d %s) Starting SSL/TLS negotiation", int(nlc->s), szHost); + + nlc->hSsl = Netlib_SslConnect(nlc->s, szHost, nlu->settings.validateSSL); + if (nlc->hSsl == nullptr) + Netlib_Logf(nlu, "(%d %s) Failure to negotiate SSL/TLS connection", int(nlc->s), szHost); + else + Netlib_Logf(nlu, "(%d %s) SSL/TLS negotiation successful", int(nlc->s), szHost); + + return nlc->hSsl != nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// gets TLS channel binging data for a socket + +static char TLS13_Label[] = "EXPORTER-Channel-Binding"; + +MIR_APP_DLL(void*) Netlib_GetTlsUnique(HNETLIBCONN nlc, int &cbLen, int &tlsVer) +{ + if (nlc == nullptr || nlc->hSsl == nullptr) + return nullptr; + + char buf[1000]; + auto *pszVersion = SSL_get_version(nlc->hSsl->session); + if (tlsVer && !mir_strcmp(pszVersion, "TLSv1.3")) { + int res = SSL_export_keying_material(nlc->hSsl->session, + (uint8_t *)buf, 32, TLS13_Label, sizeof(TLS13_Label) - 1, 0, 0, 0); + if (res == 1) { + tlsVer = 13; + void *pBuf = mir_alloc(cbLen = 32); + memcpy(pBuf, buf, cbLen); + return pBuf; + } + } + + size_t len = SSL_get_finished(nlc->hSsl->session, buf, sizeof(buf)); + if (len == 0) + return nullptr; + + tlsVer = 12; + cbLen = (int)len; + void *pBuf = mir_alloc(len); + memcpy(pBuf, buf, len); + return pBuf; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// module entry point + +bool OpenSsl_Init(void) +{ + /* Load Library Pointers */ + if (bSslInitDone) + return true; + + if (!bSslInitDone) { // init OpenSSL + SSL_library_init(); + SSL_load_error_strings(); + // FIXME check errors + + const SSL_METHOD *meth = TLS_client_method(); + if (!meth) { + Netlib_Logf(nullptr, "SSL setup failure: client method"); + return false; + } + + g_ctx = SSL_CTX_new(meth); + if (!g_ctx) { + Netlib_Logf(nullptr, "SSL setup failure: context"); + return false; + } + + VARSW wszPemFile(L"%miranda_path%\\libs\\microsoft.pem"); + SSL_CTX_load_verify_locations(g_ctx, _T2A(wszPemFile), NULL); + + // SSL_read/write should transparently handle renegotiations + SSL_CTX_ctrl(g_ctx, SSL_CTRL_MODE, SSL_MODE_AUTO_RETRY, nullptr); + // SSL_CTX_set_quiet_shutdown(g_ctx, TRUE); + + RAND_screen(); + + bSslInitDone = true; + } + + return bSslInitDone; +} + +void OpenSsl_Unload(void) +{ + /* Load Library Pointers */ + if (!bSslInitDone) + return; + + if (g_ctx) + SSL_CTX_free(g_ctx); + + bSslInitDone = false; +} diff --git a/src/mir_app/src/netlib_upnp.cpp b/src/mir_app/src/netlib_upnp.cpp index d9570b23af..17b7f5f463 100644 --- a/src/mir_app/src/netlib_upnp.cpp +++ b/src/mir_app/src/netlib_upnp.cpp @@ -1,813 +1,813 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -static const char search_request_msg[] = - "M-SEARCH * HTTP/1.1\r\n" - "HOST: 239.255.255.250:1900\r\n" - "MAN: \"ssdp:discover\"\r\n" - "MX: 1\r\n" - "ST: urn:schemas-upnp-org:service:%s\r\n" - "\r\n"; - -static const char xml_get_hdr[] = - "GET %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "ACCEPT-LANGUAGE: *\r\n\r\n"; - -static const char soap_post_hdr[] = - "POST %s HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char soap_post_hdr_m[] = - "M-POST %s URL HTTP/1.1\r\n" - "HOST: %s:%u\r\n" - "CONTENT-LENGTH: %u\r\n" - "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" - "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n" - "01-SOAPACTION: \"%s#%s\"\r\n\r\n" - "%s"; - -static const char search_device[] = - "%s"; - -static const char soap_action[] = - "\r\n" - "\r\n" - " \r\n" - " \r\n" - "%s" - " \r\n" - " \r\n" - "\r\n"; - -static const char soap_query[] = - "\r\n" - " \r\n" - " \r\n" - " %s\r\n" - " \r\n" - " \r\n" - "\r\n"; - -static const char add_port_mapping[] = - " \r\n" - " %i\r\n" - " %s\r\n" - " %i\r\n" - " %s\r\n" - " 1\r\n" - " Miranda\r\n" - " 0\r\n"; - -static const char delete_port_mapping[] = - " \r\n" - " %i\r\n" - " %s\r\n"; - -static const char get_port_mapping[] = - " %i\r\n"; - -static bool gatewayFound; -static SOCKADDR_IN locIP; -static time_t lastDiscTime; -static int expireTime = 120; - -static int retryCount; -static SOCKET sock = INVALID_SOCKET; -static char szConnHost[256]; -static unsigned short sConnPort; - -static uint16_t *portList; -static unsigned numports, numportsAlloc; -static HANDLE portListMutex; - -static char szCtlUrl[256], szDev[256]; - -typedef enum -{ - DeviceGetReq, - ControlAction, - ControlQuery -} ReqType; - -static bool txtParseParam(char* szData, char* presearch, - char* start, char* finish, char* param, size_t size) -{ - char *cp, *cp1; - size_t len; - - *param = 0; - - if (presearch != nullptr) { - cp1 = strstr(szData, presearch); - if (cp1 == nullptr) return false; - } - else - cp1 = szData; - - cp = strstr(cp1, start); - if (cp == nullptr) return false; - cp += mir_strlen(start); - while (*cp == ' ') ++cp; - - cp1 = strstr(cp, finish); - if (cp1 == nullptr) return false; - while (*(cp1-1) == ' ' && cp1 > cp) --cp1; - - len = min((size_t)(cp1 - cp), size-1); - strncpy(param, cp, len); - param[len] = 0; - - return true; -} - -void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath) -{ - char *ppath, *phost, *pport; - int sz; - - phost = strstr(szUrl, "://"); - if (phost == nullptr) phost = szUrl; - else phost += 3; - - ppath = strchr(phost, '/'); - if (ppath == nullptr) ppath = phost + mir_strlen(phost); - - pport = strchr(phost, ':'); - if (pport == nullptr) pport = ppath; - - if (szHost != nullptr) { - sz = pport - phost + 1; - if (sz > 256) sz = 256; - strncpy(szHost, phost, sz); - szHost[sz - 1] = 0; - } - - if (sPort != nullptr) { - if (pport < ppath) { - long prt = atol(pport + 1); - *sPort = prt != 0 ? (unsigned short)prt : 80; - } - else - *sPort = 80; - } - - if (szPath != nullptr) { - strncpy(szPath, ppath, 256); - szPath[255] = 0; - } -} - -static void LongLog(char* szData) -{ - Netlib_Logf(nullptr, szData); -} - -static void closeRouterConnection(void) -{ - if (sock != INVALID_SOCKET) { - closesocket(sock); - sock = INVALID_SOCKET; - } -} - -static void validateSocket(void) -{ - static const TIMEVAL tv = { 0, 0 }; - fd_set rfd; - char buf[4]; - - if (sock == INVALID_SOCKET) - return; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - bool opened = false; - switch (select(1, &rfd, nullptr, nullptr, &tv)) { - case 0: - opened = true; - break; - - case 1: - opened = recv(sock, buf, 1, MSG_PEEK) > 0; - break; - } - - if (!opened) - closeRouterConnection(); -} - -static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype) -{ - // Parse URL - char szHost[256], szPath[256], szRes[16]; - int sz = 0, res = 0; - unsigned short sPort; - bool needClose = false; - - const char* szPostHdr = soap_post_hdr; - char* szData = (char*)mir_alloc(4096); - char* szReq = nullptr; - - parseURL(szUrl, szHost, &sPort, szPath); - - if (sPort != sConnPort || _stricmp(szHost, szConnHost)) - closeRouterConnection(); - else - validateSocket(); - - while (true) { - retryCount = 0; - switch (reqtype) { - case DeviceGetReq: - sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort); - break; - - case ControlAction: - { - char szData1[1024]; - - szReq = mir_strdup(szResult); - sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1); - } - break; - - case ControlQuery: - { - char szData1[1024]; - sz = mir_snprintf(szData1, soap_query, szActionName); - sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1); - } - break; - } - szResult[0] = 0; - { - static const TIMEVAL tv = { 6, 0 }; - static unsigned ttl = 4; - static u_long mode = 1; - fd_set rfd, wfd, efd; - SOCKADDR_IN enetaddr; - -retrycon: - if (sock == INVALID_SOCKET) { - sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(sPort); - enetaddr.sin_addr.s_addr = inet_addr(szHost); - - // Resolve host name if needed - if (enetaddr.sin_addr.s_addr == INADDR_NONE) { - PHOSTENT he = gethostbyname(szHost); - if (he) - enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; - } - - Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort); - - FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); - FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd); - - // Limit the scope of the connection (does not work for - setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); - - // Put socket into non-blocking mode for timeout on connect - ioctlsocket(sock, FIONBIO, &mode); - - // Connect to the remote host - if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) { - int err = WSAGetLastError(); - - // Socket connection failed - if (err != WSAEWOULDBLOCK) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed %d", err); - break; - } - // Wait for socket to connect - else if (select(1, &rfd, &wfd, &efd, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect timeout"); - break; - } - else if (!FD_ISSET(sock, &wfd)) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP connect failed"); - break; - } - } - strncpy_s(szConnHost, szHost, _TRUNCATE); - sConnPort = sPort; - } - - if (send(sock, szData, sz, 0) != SOCKET_ERROR) { - char *hdrend = nullptr; - int acksz = 0, pktsz = 0; - - if (szActionName == nullptr) { - int len = sizeof(locIP); - getsockname(sock, (SOCKADDR*)&locIP, &len); - if (locIP.sin_addr.S_un.S_addr == 0x0100007f) { - struct hostent *he; - - gethostname(szPath, sizeof(szPath)); - he = gethostbyname(szPath); - if (he != nullptr) - locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0]; - } - } - - LongLog(szData); - sz = 0; - while (true) { - int bytesRecv; - - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - // Wait for the next packet - if (select(1, &rfd, nullptr, nullptr, &tv) != 1) { - closeRouterConnection(); - Netlib_Logf(nullptr, "UPnP recieve timeout"); - break; - } - - // - bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0); - - // Connection closed or aborted, all data received - if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) { - closeRouterConnection(); - if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) { - ++retryCount; - goto retrycon; - } - break; - } - - sz += bytesRecv; - - // Insert null terminator to use string functions - if (sz >= (resSize - 1)) { - szResult[resSize - 1] = 0; - break; - } - else - szResult[sz] = 0; - - // HTTP header found? - if (hdrend == nullptr) { - // Find HTTP header end - hdrend = strstr(szResult, "\r\n\r\n"); - if (hdrend == nullptr) { - hdrend = strstr(szResult, "\n\n"); - if (hdrend) hdrend += 2; - } - - else - hdrend += 4; - - if (hdrend != nullptr) { - // Get packet size if provided - if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) || - txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) { - // Add size of HTTP header to the packet size to compute full transmission size - pktsz = atol(ltrimp(szRes)) + (hdrend - szResult); - } - // Get encoding type if provided - else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) { - if (_stricmp(lrtrimp(szRes), "Chunked") == 0) - acksz = hdrend - szResult; - } - if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) { - needClose = (_stricmp(lrtrimp(szRes), "close") == 0); - } - } - } - - // Content-Length bytes reached, all data received - if (sz >= pktsz && pktsz != 0) { - szResult[pktsz] = 0; - break; - } - - // Chunked encoding processing - if (sz > acksz && acksz != 0) { -retry: - // Parse out chunk size - char* data = szResult + acksz; - char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n'); - if (peol1 != nullptr) { - char *peol2 = strchr(++peol1, '\n'); - if (peol2 != nullptr) { - // Get chunk size - int chunkBytes = strtol(peol1, nullptr, 16); - acksz += chunkBytes; - peol2++; - - memmove(data, peol2, mir_strlen(peol2) + 1); - sz -= peol2 - data; - - // Last chunk, all data received - if (chunkBytes == 0) break; - if (sz > acksz) goto retry; - } - } - } - } - LongLog(szResult); - } - else { - if (retryCount < 2) { - closeRouterConnection(); - ++retryCount; - goto retrycon; - } - else - Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError()); - } - } - txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); - res = atol(szRes); - if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr) - szPostHdr = soap_post_hdr_m; - else - break; - } - - if (needClose) - closeRouterConnection(); - - mir_free(szData); - mir_free(szReq); - return res; -} - -static unsigned getExtIP(void) -{ - char szExtIP[30]; - char* szData = (char*)mir_alloc(4096); szData[0] = 0; - - unsigned extip = 0; - int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction); - if (res == 200 && txtParseParam(szData, "", "<", szExtIP, sizeof(szExtIP))) - extip = ntohl(inet_addr(szExtIP)); - - mir_free(szData); - return extip; -} - -static bool getUPnPURLs(char* szUrl, size_t sizeUrl) -{ - char* szData = (char*)mir_alloc(8192); - - gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200; - if (gatewayFound) { - char szTemp[256], *rpth; - size_t ctlLen; - - txtParseParam(szData, nullptr, "", "", szTemp, sizeof(szTemp)); - strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl)); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - - mir_snprintf(szTemp, search_device, szDev); - txtParseParam(szData, szTemp, "", "", szUrl, sizeUrl); - - // URL combining per RFC 2396 - if (szUrl[0] != 0) { - if (strstr(szUrl, "://") != nullptr) // absolute URI - rpth = szCtlUrl; - else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path - { - rpth = strstr(szCtlUrl, "//"); - if (rpth == nullptr) rpth = szCtlUrl; - } - else if (szUrl[0] == '/') // relative URI abs_path - { - rpth = strstr(szCtlUrl, "//"); - rpth = rpth ? rpth + 2 : szCtlUrl; - - rpth = strchr(rpth, '/'); - if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl); - } - else { // relative URI rel_path - size_t ctlCLen = mir_strlen(szCtlUrl); - rpth = szCtlUrl + ctlCLen; - if (ctlCLen != 0 && *(rpth - 1) != '/') - strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen); - } - - ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl); - strncpy(rpth, szUrl, ctlLen); - szCtlUrl[sizeof(szCtlUrl) - 1] = 0; - } - else { - szCtlUrl[0] = 0; - gatewayFound = false; - } - } - mir_free(szData); - - return gatewayFound; -} - -static void discoverUPnP(void) -{ - char* buf; - int buflen; - unsigned i, j, nip = 0; - unsigned* ips = nullptr; - - static const unsigned any = INADDR_ANY; - static const TIMEVAL tv = { 1, 600000 }; - - char szUrl[256] = ""; - char hostname[256]; - PHOSTENT he; - fd_set readfd; - - SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - - SOCKADDR_IN enetaddr; - enetaddr.sin_family = AF_INET; - enetaddr.sin_port = htons(1900); - enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); - - gethostname(hostname, sizeof(hostname)); - he = gethostbyname(hostname); - - if (he) { - while (he->h_addr_list[nip]) ++nip; - - ips = (unsigned*)mir_alloc(nip * sizeof(unsigned)); - - for (j = 0; j < nip; j++) - ips[j] = *(unsigned*)he->h_addr_list[j]; - } - - buf = (char*)mir_alloc(1500); - - for (i = 3; --i && szUrl[0] == 0;) { - for (j = 0; j < nip; j++) { - if (ips) - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned)); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - - buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1"); - sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); - LongLog(buf); - } - - if (Miranda_IsTerminated()) break; - - FD_ZERO(&readfd); - FD_SET(s, &readfd); - - while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) { - buflen = recv(s, buf, 1500, 0); - if (buflen != SOCKET_ERROR) { - buf[buflen] = 0; - LongLog(buf); - - if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) || - txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) { - char age[30]; - char szHostNew[256], szHostExist[256]; - - lrtrim(szUrl); - - parseURL(szUrl, szHostNew, nullptr, nullptr); - parseURL(szCtlUrl, szHostExist, nullptr, nullptr); - if (mir_strcmp(szHostNew, szHostExist) == 0) { - gatewayFound = true; - break; - } - - txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev)); - txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age)); - expireTime = atoi(lrtrimp(age)); - lrtrim(szDev); - - if (getUPnPURLs(szUrl, sizeof(szUrl))) { - gatewayFound = getExtIP() != 0; - if (gatewayFound) break; - } - } - } - FD_ZERO(&readfd); - FD_SET(s, &readfd); - } - } - - mir_free(buf); - mir_free(ips); - setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned)); - closesocket(s); -} - -static bool findUPnPGateway(void) -{ - if ((time(0) - lastDiscTime) >= expireTime) { - WaitForSingleObject(portListMutex, INFINITE); - - time_t curTime = time(0); - - if ((curTime - lastDiscTime) >= expireTime) { - gatewayFound = false; - - discoverUPnP(); - lastDiscTime = curTime; - - Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl); - } - - ReleaseMutex(portListMutex); - } - - return gatewayFound; -} - -bool NetlibUPnPAddPortMapping(uint16_t intport, char *proto, uint16_t *extport, uint32_t *extip, bool search) -{ - int res = 0, i = 5; - - if (findUPnPGateway()) { - char* szData = (char*)mir_alloc(4096); - char szExtIP[30]; - - *extport = intport - 1; - *extip = ntohl(locIP.sin_addr.S_un.S_addr); - - WaitForSingleObject(portListMutex, INFINITE); - - do { - ++*extport; - mir_snprintf(szData, 4096, add_port_mapping, - *extport, proto, intport, inet_ntoa(locIP.sin_addr)); - res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction); - txtParseParam(szData, nullptr, "", "", szExtIP, sizeof(szExtIP)); - - } while (search && res == 500 && atol(szExtIP) == 718 && --i); - - mir_free(szData); - - if (res == 200) { - unsigned ip = getExtIP(); - if (ip) *extip = ip; - - if (numports >= numportsAlloc) - mir_realloc(portList, sizeof(uint16_t)*(numportsAlloc += 10)); - portList[numports++] = *extport; - } - - ReleaseMutex(portListMutex); - } - - return res == 200; -} - -void NetlibUPnPDeletePortMapping(uint16_t extport, char* proto) -{ - if (extport == 0) - return; - - // findUPnPGateway(); - - if (gatewayFound) { - unsigned i; - char* szData = (char*)mir_alloc(4096); - - WaitForSingleObject(portListMutex, INFINITE); - mir_snprintf(szData, 4096, delete_port_mapping, extport, proto); - httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction); - - for (i = 0; i < numports; i++) - if (portList[i] == extport && --numports > 0) - memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(uint16_t)); - - mir_free(szData); - ReleaseMutex(portListMutex); - } -} - -void NetlibUPnPCleanup(void*) -{ - // upnp is disabled globally, no need for a cleanup - if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0) - return; - - { - int incoming = 0; - mir_cslock lck(csNetlibUser); - for (auto &p : netlibUser) - if (p->user.flags & NUF_INCOMING) { - incoming = 1; - break; - } - - if (!incoming) - return; - } - - if (findUPnPGateway()) { - char *szData = (char*)alloca(4096); - char buf[50], lip[50]; - unsigned j = 0, k, num = 100; - - strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE); - - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 && - txtParseParam(szData, "QueryStateVariableResponse", "", "<", buf, sizeof(buf))) - num = atol(buf); - - uint16_t ports[30]; - for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) { - mir_snprintf(szData, 4096, get_port_mapping, i); - - ReleaseMutex(portListMutex); - WaitForSingleObject(portListMutex, INFINITE); - - if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200) - break; - - if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0) - continue; - - if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0) - continue; - - if (txtParseParam(szData, "", "<", buf, sizeof(buf))) { - uint16_t mport = (uint16_t)atol(buf); - - if (j >= _countof(ports)) - break; - - for (k = 0; k < numports; ++k) - if (portList[k] == mport) - break; - - if (k >= numports) - ports[j++] = mport; - } - } - - ReleaseMutex(portListMutex); - - for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++) - NetlibUPnPDeletePortMapping(ports[i], "TCP"); - } -} - -void NetlibUPnPInit(void) -{ - numports = 0; - numportsAlloc = 10; - portList = (uint16_t*)mir_alloc(sizeof(uint16_t)*numportsAlloc); - - portListMutex = CreateMutex(nullptr, FALSE, nullptr); -} - -void NetlibUPnPDestroy(void) -{ - mir_free(portList); - CloseHandle(portListMutex); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +static const char search_request_msg[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 1\r\n" + "ST: urn:schemas-upnp-org:service:%s\r\n" + "\r\n"; + +static const char xml_get_hdr[] = + "GET %s HTTP/1.1\r\n" + "HOST: %s:%u\r\n" + "ACCEPT-LANGUAGE: *\r\n\r\n"; + +static const char soap_post_hdr[] = + "POST %s HTTP/1.1\r\n" + "HOST: %s:%u\r\n" + "CONTENT-LENGTH: %u\r\n" + "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" + "SOAPACTION: \"%s#%s\"\r\n\r\n" + "%s"; + +static const char soap_post_hdr_m[] = + "M-POST %s URL HTTP/1.1\r\n" + "HOST: %s:%u\r\n" + "CONTENT-LENGTH: %u\r\n" + "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n" + "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n" + "01-SOAPACTION: \"%s#%s\"\r\n\r\n" + "%s"; + +static const char search_device[] = + "%s"; + +static const char soap_action[] = + "\r\n" + "\r\n" + " \r\n" + " \r\n" + "%s" + " \r\n" + " \r\n" + "\r\n"; + +static const char soap_query[] = + "\r\n" + " \r\n" + " \r\n" + " %s\r\n" + " \r\n" + " \r\n" + "\r\n"; + +static const char add_port_mapping[] = + " \r\n" + " %i\r\n" + " %s\r\n" + " %i\r\n" + " %s\r\n" + " 1\r\n" + " Miranda\r\n" + " 0\r\n"; + +static const char delete_port_mapping[] = + " \r\n" + " %i\r\n" + " %s\r\n"; + +static const char get_port_mapping[] = + " %i\r\n"; + +static bool gatewayFound; +static SOCKADDR_IN locIP; +static time_t lastDiscTime; +static int expireTime = 120; + +static int retryCount; +static SOCKET sock = INVALID_SOCKET; +static char szConnHost[256]; +static unsigned short sConnPort; + +static uint16_t *portList; +static unsigned numports, numportsAlloc; +static HANDLE portListMutex; + +static char szCtlUrl[256], szDev[256]; + +typedef enum +{ + DeviceGetReq, + ControlAction, + ControlQuery +} ReqType; + +static bool txtParseParam(char* szData, char* presearch, + char* start, char* finish, char* param, size_t size) +{ + char *cp, *cp1; + size_t len; + + *param = 0; + + if (presearch != nullptr) { + cp1 = strstr(szData, presearch); + if (cp1 == nullptr) return false; + } + else + cp1 = szData; + + cp = strstr(cp1, start); + if (cp == nullptr) return false; + cp += mir_strlen(start); + while (*cp == ' ') ++cp; + + cp1 = strstr(cp, finish); + if (cp1 == nullptr) return false; + while (*(cp1-1) == ' ' && cp1 > cp) --cp1; + + len = min((size_t)(cp1 - cp), size-1); + strncpy(param, cp, len); + param[len] = 0; + + return true; +} + +void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath) +{ + char *ppath, *phost, *pport; + int sz; + + phost = strstr(szUrl, "://"); + if (phost == nullptr) phost = szUrl; + else phost += 3; + + ppath = strchr(phost, '/'); + if (ppath == nullptr) ppath = phost + mir_strlen(phost); + + pport = strchr(phost, ':'); + if (pport == nullptr) pport = ppath; + + if (szHost != nullptr) { + sz = pport - phost + 1; + if (sz > 256) sz = 256; + strncpy(szHost, phost, sz); + szHost[sz - 1] = 0; + } + + if (sPort != nullptr) { + if (pport < ppath) { + long prt = atol(pport + 1); + *sPort = prt != 0 ? (unsigned short)prt : 80; + } + else + *sPort = 80; + } + + if (szPath != nullptr) { + strncpy(szPath, ppath, 256); + szPath[255] = 0; + } +} + +static void LongLog(char* szData) +{ + Netlib_Logf(nullptr, szData); +} + +static void closeRouterConnection(void) +{ + if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } +} + +static void validateSocket(void) +{ + static const TIMEVAL tv = { 0, 0 }; + fd_set rfd; + char buf[4]; + + if (sock == INVALID_SOCKET) + return; + + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + + bool opened = false; + switch (select(1, &rfd, nullptr, nullptr, &tv)) { + case 0: + opened = true; + break; + + case 1: + opened = recv(sock, buf, 1, MSG_PEEK) > 0; + break; + } + + if (!opened) + closeRouterConnection(); +} + +static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype) +{ + // Parse URL + char szHost[256], szPath[256], szRes[16]; + int sz = 0, res = 0; + unsigned short sPort; + bool needClose = false; + + const char* szPostHdr = soap_post_hdr; + char* szData = (char*)mir_alloc(4096); + char* szReq = nullptr; + + parseURL(szUrl, szHost, &sPort, szPath); + + if (sPort != sConnPort || _stricmp(szHost, szConnHost)) + closeRouterConnection(); + else + validateSocket(); + + while (true) { + retryCount = 0; + switch (reqtype) { + case DeviceGetReq: + sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort); + break; + + case ControlAction: + { + char szData1[1024]; + + szReq = mir_strdup(szResult); + sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName); + sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1); + } + break; + + case ControlQuery: + { + char szData1[1024]; + sz = mir_snprintf(szData1, soap_query, szActionName); + sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1); + } + break; + } + szResult[0] = 0; + { + static const TIMEVAL tv = { 6, 0 }; + static unsigned ttl = 4; + static u_long mode = 1; + fd_set rfd, wfd, efd; + SOCKADDR_IN enetaddr; + +retrycon: + if (sock == INVALID_SOCKET) { + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + enetaddr.sin_family = AF_INET; + enetaddr.sin_port = htons(sPort); + enetaddr.sin_addr.s_addr = inet_addr(szHost); + + // Resolve host name if needed + if (enetaddr.sin_addr.s_addr == INADDR_NONE) { + PHOSTENT he = gethostbyname(szHost); + if (he) + enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0]; + } + + Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort); + + FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); + FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd); + + // Limit the scope of the connection (does not work for + setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned)); + + // Put socket into non-blocking mode for timeout on connect + ioctlsocket(sock, FIONBIO, &mode); + + // Connect to the remote host + if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) { + int err = WSAGetLastError(); + + // Socket connection failed + if (err != WSAEWOULDBLOCK) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP connect failed %d", err); + break; + } + // Wait for socket to connect + else if (select(1, &rfd, &wfd, &efd, &tv) != 1) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP connect timeout"); + break; + } + else if (!FD_ISSET(sock, &wfd)) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP connect failed"); + break; + } + } + strncpy_s(szConnHost, szHost, _TRUNCATE); + sConnPort = sPort; + } + + if (send(sock, szData, sz, 0) != SOCKET_ERROR) { + char *hdrend = nullptr; + int acksz = 0, pktsz = 0; + + if (szActionName == nullptr) { + int len = sizeof(locIP); + getsockname(sock, (SOCKADDR*)&locIP, &len); + if (locIP.sin_addr.S_un.S_addr == 0x0100007f) { + struct hostent *he; + + gethostname(szPath, sizeof(szPath)); + he = gethostbyname(szPath); + if (he != nullptr) + locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0]; + } + } + + LongLog(szData); + sz = 0; + while (true) { + int bytesRecv; + + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + + // Wait for the next packet + if (select(1, &rfd, nullptr, nullptr, &tv) != 1) { + closeRouterConnection(); + Netlib_Logf(nullptr, "UPnP recieve timeout"); + break; + } + + // + bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0); + + // Connection closed or aborted, all data received + if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) { + closeRouterConnection(); + if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) { + ++retryCount; + goto retrycon; + } + break; + } + + sz += bytesRecv; + + // Insert null terminator to use string functions + if (sz >= (resSize - 1)) { + szResult[resSize - 1] = 0; + break; + } + else + szResult[sz] = 0; + + // HTTP header found? + if (hdrend == nullptr) { + // Find HTTP header end + hdrend = strstr(szResult, "\r\n\r\n"); + if (hdrend == nullptr) { + hdrend = strstr(szResult, "\n\n"); + if (hdrend) hdrend += 2; + } + + else + hdrend += 4; + + if (hdrend != nullptr) { + // Get packet size if provided + if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) || + txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) { + // Add size of HTTP header to the packet size to compute full transmission size + pktsz = atol(ltrimp(szRes)) + (hdrend - szResult); + } + // Get encoding type if provided + else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) { + if (_stricmp(lrtrimp(szRes), "Chunked") == 0) + acksz = hdrend - szResult; + } + if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) { + needClose = (_stricmp(lrtrimp(szRes), "close") == 0); + } + } + } + + // Content-Length bytes reached, all data received + if (sz >= pktsz && pktsz != 0) { + szResult[pktsz] = 0; + break; + } + + // Chunked encoding processing + if (sz > acksz && acksz != 0) { +retry: + // Parse out chunk size + char* data = szResult + acksz; + char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n'); + if (peol1 != nullptr) { + char *peol2 = strchr(++peol1, '\n'); + if (peol2 != nullptr) { + // Get chunk size + int chunkBytes = strtol(peol1, nullptr, 16); + acksz += chunkBytes; + peol2++; + + memmove(data, peol2, mir_strlen(peol2) + 1); + sz -= peol2 - data; + + // Last chunk, all data received + if (chunkBytes == 0) break; + if (sz > acksz) goto retry; + } + } + } + } + LongLog(szResult); + } + else { + if (retryCount < 2) { + closeRouterConnection(); + ++retryCount; + goto retrycon; + } + else + Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError()); + } + } + txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes)); + res = atol(szRes); + if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr) + szPostHdr = soap_post_hdr_m; + else + break; + } + + if (needClose) + closeRouterConnection(); + + mir_free(szData); + mir_free(szReq); + return res; +} + +static unsigned getExtIP(void) +{ + char szExtIP[30]; + char* szData = (char*)mir_alloc(4096); szData[0] = 0; + + unsigned extip = 0; + int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction); + if (res == 200 && txtParseParam(szData, "", "<", szExtIP, sizeof(szExtIP))) + extip = ntohl(inet_addr(szExtIP)); + + mir_free(szData); + return extip; +} + +static bool getUPnPURLs(char* szUrl, size_t sizeUrl) +{ + char* szData = (char*)mir_alloc(8192); + + gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200; + if (gatewayFound) { + char szTemp[256], *rpth; + size_t ctlLen; + + txtParseParam(szData, nullptr, "", "", szTemp, sizeof(szTemp)); + strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl)); + szCtlUrl[sizeof(szCtlUrl) - 1] = 0; + + mir_snprintf(szTemp, search_device, szDev); + txtParseParam(szData, szTemp, "", "", szUrl, sizeUrl); + + // URL combining per RFC 2396 + if (szUrl[0] != 0) { + if (strstr(szUrl, "://") != nullptr) // absolute URI + rpth = szCtlUrl; + else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path + { + rpth = strstr(szCtlUrl, "//"); + if (rpth == nullptr) rpth = szCtlUrl; + } + else if (szUrl[0] == '/') // relative URI abs_path + { + rpth = strstr(szCtlUrl, "//"); + rpth = rpth ? rpth + 2 : szCtlUrl; + + rpth = strchr(rpth, '/'); + if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl); + } + else { // relative URI rel_path + size_t ctlCLen = mir_strlen(szCtlUrl); + rpth = szCtlUrl + ctlCLen; + if (ctlCLen != 0 && *(rpth - 1) != '/') + strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen); + } + + ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl); + strncpy(rpth, szUrl, ctlLen); + szCtlUrl[sizeof(szCtlUrl) - 1] = 0; + } + else { + szCtlUrl[0] = 0; + gatewayFound = false; + } + } + mir_free(szData); + + return gatewayFound; +} + +static void discoverUPnP(void) +{ + char* buf; + int buflen; + unsigned i, j, nip = 0; + unsigned* ips = nullptr; + + static const unsigned any = INADDR_ANY; + static const TIMEVAL tv = { 1, 600000 }; + + char szUrl[256] = ""; + char hostname[256]; + PHOSTENT he; + fd_set readfd; + + SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + SOCKADDR_IN enetaddr; + enetaddr.sin_family = AF_INET; + enetaddr.sin_port = htons(1900); + enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250"); + + gethostname(hostname, sizeof(hostname)); + he = gethostbyname(hostname); + + if (he) { + while (he->h_addr_list[nip]) ++nip; + + ips = (unsigned*)mir_alloc(nip * sizeof(unsigned)); + + for (j = 0; j < nip; j++) + ips[j] = *(unsigned*)he->h_addr_list[j]; + } + + buf = (char*)mir_alloc(1500); + + for (i = 3; --i && szUrl[0] == 0;) { + for (j = 0; j < nip; j++) { + if (ips) + setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned)); + + buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1"); + sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); + LongLog(buf); + + buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1"); + sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr)); + LongLog(buf); + } + + if (Miranda_IsTerminated()) break; + + FD_ZERO(&readfd); + FD_SET(s, &readfd); + + while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) { + buflen = recv(s, buf, 1500, 0); + if (buflen != SOCKET_ERROR) { + buf[buflen] = 0; + LongLog(buf); + + if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) || + txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) { + char age[30]; + char szHostNew[256], szHostExist[256]; + + lrtrim(szUrl); + + parseURL(szUrl, szHostNew, nullptr, nullptr); + parseURL(szCtlUrl, szHostExist, nullptr, nullptr); + if (mir_strcmp(szHostNew, szHostExist) == 0) { + gatewayFound = true; + break; + } + + txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev)); + txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age)); + expireTime = atoi(lrtrimp(age)); + lrtrim(szDev); + + if (getUPnPURLs(szUrl, sizeof(szUrl))) { + gatewayFound = getExtIP() != 0; + if (gatewayFound) break; + } + } + } + FD_ZERO(&readfd); + FD_SET(s, &readfd); + } + } + + mir_free(buf); + mir_free(ips); + setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned)); + closesocket(s); +} + +static bool findUPnPGateway(void) +{ + if ((time(0) - lastDiscTime) >= expireTime) { + WaitForSingleObject(portListMutex, INFINITE); + + time_t curTime = time(0); + + if ((curTime - lastDiscTime) >= expireTime) { + gatewayFound = false; + + discoverUPnP(); + lastDiscTime = curTime; + + Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl); + } + + ReleaseMutex(portListMutex); + } + + return gatewayFound; +} + +bool NetlibUPnPAddPortMapping(uint16_t intport, char *proto, uint16_t *extport, uint32_t *extip, bool search) +{ + int res = 0, i = 5; + + if (findUPnPGateway()) { + char* szData = (char*)mir_alloc(4096); + char szExtIP[30]; + + *extport = intport - 1; + *extip = ntohl(locIP.sin_addr.S_un.S_addr); + + WaitForSingleObject(portListMutex, INFINITE); + + do { + ++*extport; + mir_snprintf(szData, 4096, add_port_mapping, + *extport, proto, intport, inet_ntoa(locIP.sin_addr)); + res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction); + txtParseParam(szData, nullptr, "", "", szExtIP, sizeof(szExtIP)); + + } while (search && res == 500 && atol(szExtIP) == 718 && --i); + + mir_free(szData); + + if (res == 200) { + unsigned ip = getExtIP(); + if (ip) *extip = ip; + + if (numports >= numportsAlloc) + mir_realloc(portList, sizeof(uint16_t)*(numportsAlloc += 10)); + portList[numports++] = *extport; + } + + ReleaseMutex(portListMutex); + } + + return res == 200; +} + +void NetlibUPnPDeletePortMapping(uint16_t extport, char* proto) +{ + if (extport == 0) + return; + + // findUPnPGateway(); + + if (gatewayFound) { + unsigned i; + char* szData = (char*)mir_alloc(4096); + + WaitForSingleObject(portListMutex, INFINITE); + mir_snprintf(szData, 4096, delete_port_mapping, extport, proto); + httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction); + + for (i = 0; i < numports; i++) + if (portList[i] == extport && --numports > 0) + memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(uint16_t)); + + mir_free(szData); + ReleaseMutex(portListMutex); + } +} + +void NetlibUPnPCleanup(void*) +{ + // upnp is disabled globally, no need for a cleanup + if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0) + return; + + { + int incoming = 0; + mir_cslock lck(csNetlibUser); + for (auto &p : netlibUser) + if (p->user.flags & NUF_INCOMING) { + incoming = 1; + break; + } + + if (!incoming) + return; + } + + if (findUPnPGateway()) { + char *szData = (char*)alloca(4096); + char buf[50], lip[50]; + unsigned j = 0, k, num = 100; + + strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE); + + WaitForSingleObject(portListMutex, INFINITE); + + if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 && + txtParseParam(szData, "QueryStateVariableResponse", "", "<", buf, sizeof(buf))) + num = atol(buf); + + uint16_t ports[30]; + for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) { + mir_snprintf(szData, 4096, get_port_mapping, i); + + ReleaseMutex(portListMutex); + WaitForSingleObject(portListMutex, INFINITE); + + if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200) + break; + + if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0) + continue; + + if (!txtParseParam(szData, "", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0) + continue; + + if (txtParseParam(szData, "", "<", buf, sizeof(buf))) { + uint16_t mport = (uint16_t)atol(buf); + + if (j >= _countof(ports)) + break; + + for (k = 0; k < numports; ++k) + if (portList[k] == mport) + break; + + if (k >= numports) + ports[j++] = mport; + } + } + + ReleaseMutex(portListMutex); + + for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++) + NetlibUPnPDeletePortMapping(ports[i], "TCP"); + } +} + +void NetlibUPnPInit(void) +{ + numports = 0; + numportsAlloc = 10; + portList = (uint16_t*)mir_alloc(sizeof(uint16_t)*numportsAlloc); + + portListMutex = CreateMutex(nullptr, FALSE, nullptr); +} + +void NetlibUPnPDestroy(void) +{ + mir_free(portList); + CloseHandle(portListMutex); +} diff --git a/src/mir_app/src/netlib_websocket.cpp b/src/mir_app/src/netlib_websocket.cpp index 4b860cc0db..cd3aaaef4e 100644 --- a/src/mir_app/src/netlib_websocket.cpp +++ b/src/mir_app/src/netlib_websocket.cpp @@ -1,174 +1,174 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "netlib.h" - -#include "../../libs/zlib/src/zlib.h" - -MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost, NETLIBHTTPHEADER *pHeaders) -{ - CMStringA tmpHost(szHost); - - // connect to the gateway server - if (!mir_strncmp(tmpHost, "wss://", 6)) - tmpHost.Delete(0, 6); - - auto *nlr = new MHttpRequest; - nlr->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL; - nlr->szUrl = tmpHost.GetBuffer(); - nlr->AddHeader("Accept", "*/*"); - nlr->AddHeader("Upgrade", "websocket"); - nlr->AddHeader("Pragma", "no-cache"); - nlr->AddHeader("Cache-Control", "no-cache"); - nlr->AddHeader("Connection", "keep-alive, Upgrade"); - - uint8_t binNonce[16]; - Utils_GetRandom(binNonce, sizeof(binNonce)); - nlr->AddHeader("Sec-WebSocket-Key", ptrA(mir_base64_encode(binNonce, sizeof(binNonce)))); - nlr->AddHeader("Sec-WebSocket-Version", "13"); - nlr->AddHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits"); - - if (pHeaders) { - while (pHeaders->szName != nullptr) { - nlr->AddHeader(pHeaders->szName, pHeaders->szValue); - pHeaders++; - } - } - - auto *pReply = Netlib_HttpTransaction(nlu, nlr); - delete nlr; - - if (pReply == nullptr) { - Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, send failed", tmpHost.c_str()); - return nullptr; - } - - if (pReply->resultCode != 101) - Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, status %d", tmpHost.c_str(), pReply->resultCode); - - return pReply; -} - -MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize) -{ - if (bufSize < 2) - return false; - - auto *buf = (const uint8_t *)pData; - hdr.bIsFinal = (buf[0] & 0x80) != 0; - hdr.bIsMasked = (buf[1] & 0x80) != 0; - hdr.opCode = buf[0] & 0x0F; - hdr.firstByte = buf[1] & 0x7F; - hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0); - if (bufSize < hdr.headerSize) - return false; - - uint64_t tmpSize = 0; - switch (hdr.firstByte) { - case 0x7F: - tmpSize += ((uint64_t)buf[2]) << 56; - tmpSize += ((uint64_t)buf[3]) << 48; - tmpSize += ((uint64_t)buf[4]) << 40; - tmpSize += ((uint64_t)buf[5]) << 32; - tmpSize += ((uint64_t)buf[6]) << 24; - tmpSize += ((uint64_t)buf[7]) << 16; - tmpSize += ((uint64_t)buf[8]) << 8; - tmpSize += ((uint64_t)buf[9]); - break; - - case 0x7E: - tmpSize += ((uint64_t)buf[2]) << 8; - tmpSize += ((uint64_t)buf[3]); - break; - - default: - tmpSize = hdr.firstByte; - } - hdr.payloadSize = tmpSize; - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t dataLen, uint8_t opCode) -{ - uint8_t header[20]; - size_t datalen; - - header[0] = 0x80 + (opCode & 0x7F); - if (dataLen < 126) { - header[1] = (dataLen & 0xFF); - datalen = 2; - } - else if (dataLen < 65536) { - header[1] = 0x7E; - header[2] = (dataLen >> 8) & 0xFF; - header[3] = dataLen & 0xFF; - datalen = 4; - } - else { - header[1] = 0x7F; - header[2] = (dataLen >> 56) & 0xff; - header[3] = (dataLen >> 48) & 0xff; - header[4] = (dataLen >> 40) & 0xff; - header[5] = (dataLen >> 32) & 0xff; - header[6] = (dataLen >> 24) & 0xff; - header[7] = (dataLen >> 16) & 0xff; - header[8] = (dataLen >> 8) & 0xff; - header[9] = dataLen & 0xff; - datalen = 10; - } - - union { - uint32_t dwMask; - uint8_t arMask[4]; - }; - - dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)dataLen); - memcpy(header + datalen, arMask, _countof(arMask)); - datalen += _countof(arMask); - header[1] |= 0x80; - - ptrA sendBuf((char*)mir_alloc(dataLen + datalen)); - memcpy(sendBuf, header, datalen); - if (dataLen) { - memcpy(sendBuf.get() + datalen, pData, dataLen); - for (size_t i = 0; i < dataLen; i++) - sendBuf[i + datalen] ^= arMask[i & 3]; - } - Netlib_Send(nlc, sendBuf, int(dataLen + datalen), MSG_NODUMP); -} - -MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData) -{ - if (nlc && pData) - WebSocket_Send(nlc, pData, strlen(pData), 1); -} - -MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t dataLen) -{ - if (nlc && pData) - WebSocket_Send(nlc, pData, dataLen, 2); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "netlib.h" + +#include "../../libs/zlib/src/zlib.h" + +MIR_APP_DLL(NETLIBHTTPREQUEST*) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost, NETLIBHTTPHEADER *pHeaders) +{ + CMStringA tmpHost(szHost); + + // connect to the gateway server + if (!mir_strncmp(tmpHost, "wss://", 6)) + tmpHost.Delete(0, 6); + + auto *nlr = new MHttpRequest; + nlr->flags = NLHRF_PERSISTENT | NLHRF_HTTP11 | NLHRF_SSL; + nlr->szUrl = tmpHost.GetBuffer(); + nlr->AddHeader("Accept", "*/*"); + nlr->AddHeader("Upgrade", "websocket"); + nlr->AddHeader("Pragma", "no-cache"); + nlr->AddHeader("Cache-Control", "no-cache"); + nlr->AddHeader("Connection", "keep-alive, Upgrade"); + + uint8_t binNonce[16]; + Utils_GetRandom(binNonce, sizeof(binNonce)); + nlr->AddHeader("Sec-WebSocket-Key", ptrA(mir_base64_encode(binNonce, sizeof(binNonce)))); + nlr->AddHeader("Sec-WebSocket-Version", "13"); + nlr->AddHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits"); + + if (pHeaders) { + while (pHeaders->szName != nullptr) { + nlr->AddHeader(pHeaders->szName, pHeaders->szValue); + pHeaders++; + } + } + + auto *pReply = Netlib_HttpTransaction(nlu, nlr); + delete nlr; + + if (pReply == nullptr) { + Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, send failed", tmpHost.c_str()); + return nullptr; + } + + if (pReply->resultCode != 101) + Netlib_Logf(nlu, "Error establishing WebSocket connection to %s, status %d", tmpHost.c_str(), pReply->resultCode); + + return pReply; +} + +MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize) +{ + if (bufSize < 2) + return false; + + auto *buf = (const uint8_t *)pData; + hdr.bIsFinal = (buf[0] & 0x80) != 0; + hdr.bIsMasked = (buf[1] & 0x80) != 0; + hdr.opCode = buf[0] & 0x0F; + hdr.firstByte = buf[1] & 0x7F; + hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0); + if (bufSize < hdr.headerSize) + return false; + + uint64_t tmpSize = 0; + switch (hdr.firstByte) { + case 0x7F: + tmpSize += ((uint64_t)buf[2]) << 56; + tmpSize += ((uint64_t)buf[3]) << 48; + tmpSize += ((uint64_t)buf[4]) << 40; + tmpSize += ((uint64_t)buf[5]) << 32; + tmpSize += ((uint64_t)buf[6]) << 24; + tmpSize += ((uint64_t)buf[7]) << 16; + tmpSize += ((uint64_t)buf[8]) << 8; + tmpSize += ((uint64_t)buf[9]); + break; + + case 0x7E: + tmpSize += ((uint64_t)buf[2]) << 8; + tmpSize += ((uint64_t)buf[3]); + break; + + default: + tmpSize = hdr.firstByte; + } + hdr.payloadSize = tmpSize; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t dataLen, uint8_t opCode) +{ + uint8_t header[20]; + size_t datalen; + + header[0] = 0x80 + (opCode & 0x7F); + if (dataLen < 126) { + header[1] = (dataLen & 0xFF); + datalen = 2; + } + else if (dataLen < 65536) { + header[1] = 0x7E; + header[2] = (dataLen >> 8) & 0xFF; + header[3] = dataLen & 0xFF; + datalen = 4; + } + else { + header[1] = 0x7F; + header[2] = (dataLen >> 56) & 0xff; + header[3] = (dataLen >> 48) & 0xff; + header[4] = (dataLen >> 40) & 0xff; + header[5] = (dataLen >> 32) & 0xff; + header[6] = (dataLen >> 24) & 0xff; + header[7] = (dataLen >> 16) & 0xff; + header[8] = (dataLen >> 8) & 0xff; + header[9] = dataLen & 0xff; + datalen = 10; + } + + union { + uint32_t dwMask; + uint8_t arMask[4]; + }; + + dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)dataLen); + memcpy(header + datalen, arMask, _countof(arMask)); + datalen += _countof(arMask); + header[1] |= 0x80; + + ptrA sendBuf((char*)mir_alloc(dataLen + datalen)); + memcpy(sendBuf, header, datalen); + if (dataLen) { + memcpy(sendBuf.get() + datalen, pData, dataLen); + for (size_t i = 0; i < dataLen; i++) + sendBuf[i + datalen] ^= arMask[i & 3]; + } + Netlib_Send(nlc, sendBuf, int(dataLen + datalen), MSG_NODUMP); +} + +MIR_APP_DLL(void) WebSocket_SendText(HNETLIBCONN nlc, const char *pData) +{ + if (nlc && pData) + WebSocket_Send(nlc, pData, strlen(pData), 1); +} + +MIR_APP_DLL(void) WebSocket_SendBinary(HNETLIBCONN nlc, const void *pData, size_t dataLen) +{ + if (nlc && pData) + WebSocket_Send(nlc, pData, dataLen, 2); +} diff --git a/src/mir_app/src/newplugins.cpp b/src/mir_app/src/newplugins.cpp index 6a79d72330..367afde7b0 100644 --- a/src/mir_app/src/newplugins.cpp +++ b/src/mir_app/src/newplugins.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/options.cpp b/src/mir_app/src/options.cpp index ea7f32ac99..81c136a0ab 100644 --- a/src/mir_app/src/options.cpp +++ b/src/mir_app/src/options.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, Copyright (c) 2007 Artem Shpynov all portions of this codebase are copyrighted to the people diff --git a/src/mir_app/src/path.cpp b/src/mir_app/src/path.cpp index 616100510f..49e5cd7487 100644 --- a/src/mir_app/src/path.cpp +++ b/src/mir_app/src/path.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/pluginopts.cpp b/src/mir_app/src/pluginopts.cpp index c601a83072..7e58c5fca3 100644 --- a/src/mir_app/src/pluginopts.cpp +++ b/src/mir_app/src/pluginopts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/popupOption.cpp b/src/mir_app/src/popupOption.cpp index 6749ba4e9c..4ac5290c6d 100644 --- a/src/mir_app/src/popupOption.cpp +++ b/src/mir_app/src/popupOption.cpp @@ -1,123 +1,123 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" - -struct MPopupOption -{ - MPopupOption(CMPluginBase *pPlugin, const char *pszDescr, CMOption &pVal) : - m_plugin(pPlugin), - m_val(pVal), - m_descr(pszDescr) - {} - - MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption &pVal) : - m_plugin(pPlugin), - m_val(pVal), - m_descr(pwszDescr) - {} - - CMPluginBase *m_plugin; - CMOption &m_val; - CMStringW m_descr; -}; - -static OBJLIST g_arOptions(1); - -///////////////////////////////////////////////////////////////////////////////////////// - -int CMPluginBase::addPopupOption(const char *pszDescr, CMOption &pVal) -{ - g_arOptions.insert(new MPopupOption(this, pszDescr, pVal)); - return 0; -} - -int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption &pVal) -{ - g_arOptions.insert(new MPopupOption(this, pwszDescr, pVal)); - return 0; -} - -void KillModulePopups(CMPluginBase *pPlugin) -{ - for (auto &it : g_arOptions.rev_iter()) - if (it->m_plugin == pPlugin) - g_arOptions.remove(g_arOptions.indexOf(&it)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CPopupOptionsDlg : public CDlgBase -{ - CCtrlListView m_list; - -public: - CPopupOptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_POPUPOPTION), - m_list(this, IDC_TREE) - {} - - bool OnInitDialog() override - { - m_list.SetExtendedListViewStyleEx(0, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); - - LVITEM lvi; - lvi.mask = LVIF_TEXT | LVIF_PARAM; - lvi.iSubItem = 0; - - for (auto &it : g_arOptions) { - lvi.pszText = TranslateW_LP(it->m_descr, it->m_plugin); - lvi.lParam = LPARAM(it); - - int iRow = m_list.InsertItem(&lvi); - m_list.SetItemState(iRow, it->m_val ? 0x2000 : 0x1000, LVIS_STATEIMAGEMASK); - } - - return true; - } - - bool OnApply() override - { - int iRows = m_list.GetItemCount(); - - for (int i = 0; i < iRows; i++) { - auto *p = (MPopupOption *)m_list.GetItemData(i); - p->m_val = m_list.GetItemState(i, LVIS_STATEIMAGEMASK) == 0x2000; - } - return true; - } -}; - -int PopupOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = -1000000000; - odp.szGroup.a = LPGEN("Popups"); - odp.szTitle.a = LPGEN("Events"); - odp.pDialog = new CPopupOptionsDlg(); - odp.flags = ODPF_BOLDGROUPS; - g_plugin.addOptions(wParam, &odp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "plugins.h" + +struct MPopupOption +{ + MPopupOption(CMPluginBase *pPlugin, const char *pszDescr, CMOption &pVal) : + m_plugin(pPlugin), + m_val(pVal), + m_descr(pszDescr) + {} + + MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption &pVal) : + m_plugin(pPlugin), + m_val(pVal), + m_descr(pwszDescr) + {} + + CMPluginBase *m_plugin; + CMOption &m_val; + CMStringW m_descr; +}; + +static OBJLIST g_arOptions(1); + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPluginBase::addPopupOption(const char *pszDescr, CMOption &pVal) +{ + g_arOptions.insert(new MPopupOption(this, pszDescr, pVal)); + return 0; +} + +int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption &pVal) +{ + g_arOptions.insert(new MPopupOption(this, pwszDescr, pVal)); + return 0; +} + +void KillModulePopups(CMPluginBase *pPlugin) +{ + for (auto &it : g_arOptions.rev_iter()) + if (it->m_plugin == pPlugin) + g_arOptions.remove(g_arOptions.indexOf(&it)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class CPopupOptionsDlg : public CDlgBase +{ + CCtrlListView m_list; + +public: + CPopupOptionsDlg() : + CDlgBase(g_plugin, IDD_OPT_POPUPOPTION), + m_list(this, IDC_TREE) + {} + + bool OnInitDialog() override + { + m_list.SetExtendedListViewStyleEx(0, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); + + LVITEM lvi; + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iSubItem = 0; + + for (auto &it : g_arOptions) { + lvi.pszText = TranslateW_LP(it->m_descr, it->m_plugin); + lvi.lParam = LPARAM(it); + + int iRow = m_list.InsertItem(&lvi); + m_list.SetItemState(iRow, it->m_val ? 0x2000 : 0x1000, LVIS_STATEIMAGEMASK); + } + + return true; + } + + bool OnApply() override + { + int iRows = m_list.GetItemCount(); + + for (int i = 0; i < iRows; i++) { + auto *p = (MPopupOption *)m_list.GetItemData(i); + p->m_val = m_list.GetItemState(i, LVIS_STATEIMAGEMASK) == 0x2000; + } + return true; + } +}; + +int PopupOptionsInit(WPARAM wParam) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = -1000000000; + odp.szGroup.a = LPGEN("Popups"); + odp.szTitle.a = LPGEN("Events"); + odp.pDialog = new CPopupOptionsDlg(); + odp.flags = ODPF_BOLDGROUPS; + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/src/mir_app/src/popups.cpp b/src/mir_app/src/popups.cpp index bc4d5f86d8..aa7af870f3 100644 --- a/src/mir_app/src/popups.cpp +++ b/src/mir_app/src/popups.cpp @@ -1,157 +1,157 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static bool bModuleInit = false, bPopupsEnabled = true; - -MIR_APP_DLL(bool) Popup_Enabled() -{ - if (!bModuleInit) { - bModuleInit = true; - bPopupsEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0; - } - - return bPopupsEnabled; -} - -MIR_APP_DLL(void) Popup_Enable(bool bEnable) -{ - bPopupsEnabled = bEnable; - db_set_b(0, "Popup", "ModuleIsEnabled", bEnable); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup v2.0 - -MIR_APP_DLL(HWND) Popup_Add(const POPUPDATA2 *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUP2, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(void) Popup_Change(HWND hwndPopup, const POPUPDATA2 *pData) -{ - CallService(MS_POPUP_CHANGEPOPUP2, (WPARAM)hwndPopup, (LPARAM)pData); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popups v1.0 - -MIR_APP_DLL(HWND) PUAddPopup(POPUPDATA *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(HWND) PUAddPopupW(POPUPDATAW *ppdp, int flags) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp, flags); -} - -MIR_APP_DLL(int) PUChangeW(HWND hWndPopup, POPUPDATAW *newData) -{ - return (int)CallService(MS_POPUP_CHANGEW, (WPARAM)hWndPopup, (LPARAM)newData); -} - -MIR_APP_DLL(int) PUChangeTextW(HWND hWndPopup, const wchar_t *lpwzNewText) -{ - return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopup, (LPARAM)lpwzNewText); -} - -MIR_APP_DLL(int) PUDeletePopup(HWND hWndPopup) -{ - return (int)CallService(MS_POPUP_DESTROYPOPUP, 0, (LPARAM)hWndPopup); -} - -MIR_APP_DLL(MCONTACT) PUGetContact(HWND hPopupWindow) -{ - return (MCONTACT)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopupWindow, 0); -} - -MIR_APP_DLL(void*) PUGetPluginData(HWND hPopupWindow) -{ - return (void*)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hPopupWindow, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Messages - -MIR_APP_DLL(int) PUShowMessage(const char *lpzText, uint32_t kind) -{ - return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText, (LPARAM)kind); -} - -MIR_APP_DLL(int) PUShowMessageW(const wchar_t *lpwzText, uint32_t kind) -{ - return (int)CallService(MS_POPUP_SHOWMESSAGEW, (WPARAM)lpwzText, (LPARAM)kind); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Actions - -MIR_APP_DLL(int) PURegisterActions(POPUPACTION *actions, int count) -{ - return (int)CallService(MS_POPUP_REGISTERACTIONS, (WPARAM)actions, (LPARAM)count); -} - -MIR_APP_DLL(HANDLE) PURegisterNotification(POPUPNOTIFICATION *notification) -{ - return (HANDLE)CallService(MS_POPUP_REGISTERNOTIFICATION, (WPARAM)notification, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Popup classes - -MIR_APP_DLL(HANDLE) Popup_RegisterClass(POPUPCLASS *pc) -{ - return (HANDLE)CallService(MS_POPUP_REGISTERCLASS, 0, LPARAM(pc)); -} - -MIR_APP_DLL(void) Popup_UnregisterClass(HANDLE ppc) -{ - if (ppc) - CallService(MS_POPUP_UNREGISTERCLASS, 0, LPARAM(ppc)); -} - -MIR_APP_DLL(HWND) Popup_AddClass(POPUPDATACLASS *pData) -{ - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)pData); -} - -MIR_APP_DLL(HWND) ShowClassPopup(const char *name, const char *title, const char *text) -{ - POPUPDATACLASS d = {}; - d.pszClassName = name; - d.szTitle.a = title; - d.szText.a = text; - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); -} - -MIR_APP_DLL(HWND) ShowClassPopupW(const char *name, const wchar_t *title, const wchar_t *text) -{ - POPUPDATACLASS d = {}; - d.pszClassName = name; - d.szTitle.w = title; - d.szText.w = text; - return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static bool bModuleInit = false, bPopupsEnabled = true; + +MIR_APP_DLL(bool) Popup_Enabled() +{ + if (!bModuleInit) { + bModuleInit = true; + bPopupsEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0; + } + + return bPopupsEnabled; +} + +MIR_APP_DLL(void) Popup_Enable(bool bEnable) +{ + bPopupsEnabled = bEnable; + db_set_b(0, "Popup", "ModuleIsEnabled", bEnable); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Popup v2.0 + +MIR_APP_DLL(HWND) Popup_Add(const POPUPDATA2 *ppdp, int flags) +{ + return (HWND)CallService(MS_POPUP_ADDPOPUP2, (WPARAM)ppdp, flags); +} + +MIR_APP_DLL(void) Popup_Change(HWND hwndPopup, const POPUPDATA2 *pData) +{ + CallService(MS_POPUP_CHANGEPOPUP2, (WPARAM)hwndPopup, (LPARAM)pData); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Popups v1.0 + +MIR_APP_DLL(HWND) PUAddPopup(POPUPDATA *ppdp, int flags) +{ + return (HWND)CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp, flags); +} + +MIR_APP_DLL(HWND) PUAddPopupW(POPUPDATAW *ppdp, int flags) +{ + return (HWND)CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp, flags); +} + +MIR_APP_DLL(int) PUChangeW(HWND hWndPopup, POPUPDATAW *newData) +{ + return (int)CallService(MS_POPUP_CHANGEW, (WPARAM)hWndPopup, (LPARAM)newData); +} + +MIR_APP_DLL(int) PUChangeTextW(HWND hWndPopup, const wchar_t *lpwzNewText) +{ + return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopup, (LPARAM)lpwzNewText); +} + +MIR_APP_DLL(int) PUDeletePopup(HWND hWndPopup) +{ + return (int)CallService(MS_POPUP_DESTROYPOPUP, 0, (LPARAM)hWndPopup); +} + +MIR_APP_DLL(MCONTACT) PUGetContact(HWND hPopupWindow) +{ + return (MCONTACT)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopupWindow, 0); +} + +MIR_APP_DLL(void*) PUGetPluginData(HWND hPopupWindow) +{ + return (void*)CallService(MS_POPUP_GETPLUGINDATA, (WPARAM)hPopupWindow, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Messages + +MIR_APP_DLL(int) PUShowMessage(const char *lpzText, uint32_t kind) +{ + return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText, (LPARAM)kind); +} + +MIR_APP_DLL(int) PUShowMessageW(const wchar_t *lpwzText, uint32_t kind) +{ + return (int)CallService(MS_POPUP_SHOWMESSAGEW, (WPARAM)lpwzText, (LPARAM)kind); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Actions + +MIR_APP_DLL(int) PURegisterActions(POPUPACTION *actions, int count) +{ + return (int)CallService(MS_POPUP_REGISTERACTIONS, (WPARAM)actions, (LPARAM)count); +} + +MIR_APP_DLL(HANDLE) PURegisterNotification(POPUPNOTIFICATION *notification) +{ + return (HANDLE)CallService(MS_POPUP_REGISTERNOTIFICATION, (WPARAM)notification, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Popup classes + +MIR_APP_DLL(HANDLE) Popup_RegisterClass(POPUPCLASS *pc) +{ + return (HANDLE)CallService(MS_POPUP_REGISTERCLASS, 0, LPARAM(pc)); +} + +MIR_APP_DLL(void) Popup_UnregisterClass(HANDLE ppc) +{ + if (ppc) + CallService(MS_POPUP_UNREGISTERCLASS, 0, LPARAM(ppc)); +} + +MIR_APP_DLL(HWND) Popup_AddClass(POPUPDATACLASS *pData) +{ + return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)pData); +} + +MIR_APP_DLL(HWND) ShowClassPopup(const char *name, const char *title, const char *text) +{ + POPUPDATACLASS d = {}; + d.pszClassName = name; + d.szTitle.a = title; + d.szText.a = text; + return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); +} + +MIR_APP_DLL(HWND) ShowClassPopupW(const char *name, const wchar_t *title, const wchar_t *text) +{ + POPUPDATACLASS d = {}; + d.pszClassName = name; + d.szTitle.w = title; + d.szText.w = text; + return (HWND)CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&d); +} diff --git a/src/mir_app/src/profilemanager.cpp b/src/mir_app/src/profilemanager.cpp index edbd7101cb..b2ea141b6c 100644 --- a/src/mir_app/src/profilemanager.cpp +++ b/src/mir_app/src/profilemanager.cpp @@ -1,641 +1,641 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "plugins.h" -#include "langpack.h" -#include "profilemanager.h" -#include - -#pragma warning(disable : 4512) - -#define WM_INPUTCHANGED (WM_USER + 0x3000) -#define WM_FOCUSTEXTBOX (WM_USER + 0x3001) - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile creator - -static BOOL EnumProfilesForList(const wchar_t *tszFullPath, wchar_t *profile, CCtrlListView &list, const wchar_t *szProfile) -{ - wchar_t sizeBuf[64]; - bool bFileLocked; - - wchar_t *p = wcsrchr(profile, '.'); - mir_wstrcpy(sizeBuf, L"0 KB"); - if (p != nullptr) *p = 0; - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; - item.pszText = profile; - item.iItem = 0; - - struct _stat statbuf; - if (_wstat(tszFullPath, &statbuf) == 0) { - if (statbuf.st_size > 1000000) { - mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1048576.0); - mir_wstrcpy(sizeBuf + 5, L" MB"); - } - else { - mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1024.0); - mir_wstrcpy(sizeBuf + 5, L" KB"); - } - bFileLocked = Profile_CheckOpened(tszFullPath); - } - else bFileLocked = true; - - DATABASELINK *dblink; - switch (touchDatabase(tszFullPath, &dblink)) { - case ERROR_SUCCESS: - item.iImage = (bFileLocked) ? 1 : 0; - break; - - case EGROKPRF_OBSOLETE: - item.iImage = 2; - break; - - case EGROKPRF_CANTREAD: - item.iImage = (bFileLocked) ? 1 : 3; - break; - - default: - item.iImage = 3; - } - - item.lParam = (LPARAM)dblink; - - int iItem = list.InsertItem(&item); - if (mir_wstrcmpi(szProfile, tszFullPath) == 0) - list.SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - - list.SetItemText(iItem, 2, sizeBuf); - - if (dblink != nullptr) - list.SetItemText(iItem, 1, TranslateW(dblink->szFullName)); - else if (bFileLocked) // file locked - list.SetItemText(iItem, 1, TranslateT("")); - else - list.SetItemText(iItem, 1, TranslateT("")); - - return TRUE; -} - -static int findProfiles(CCtrlListView &list, const wchar_t *szProfile) -{ - // find in Miranda NG profile subfolders - MFilePath searchspec; - searchspec.Format(L"%s\\*.*", g_profileDir); - - for (auto &it: searchspec.search()) { - // find all subfolders except "." and ".." - if (!it.isDir() || !wcscmp(it.getPath(), L".") || !wcscmp(it.getPath(), L"..")) - continue; - - MFilePath fullPath; - fullPath.Format(L"%s\\%s\\%s.dat", g_profileDir, it.getPath(), it.getPath()); - if (fullPath.isExist()) { - wchar_t profileName[MAX_PATH]; - mir_snwprintf(profileName, L"%s.dat", it.getPath()); - if (!EnumProfilesForList(fullPath, profileName, list, szProfile)) - break; - } - } - - return 1; -} - -static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_CHAR) { - if (wcschr(L".?/\\#' ", (wchar_t)wParam) != nullptr) - return 0; - PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0); - } - return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam); -} - -class CCreateProfileDlg : public CDlgBase -{ - CCtrlButton &m_btnOk; - PROFILEMANAGERDATA *m_pd; - - int CreateProfile(const wchar_t *profile, DATABASELINK *link) - { - // check if the file already exists - const wchar_t *file = wcsrchr(profile, '\\'); - if (file) - file++; - - int err = 0; - wchar_t buf[256]; - - if (_waccess(profile, 0) == 0) { - // file already exists! - mir_snwprintf(buf, - TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."), - file); - if (MessageBoxW(m_hwnd, buf, TranslateT("The profile already exists"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) - return 0; - - // move the file - if (DeleteDirectoryTreeW(profile, true) != 0) { - mir_snwprintf(buf, TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file); - MessageBoxW(m_hwnd, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION | MB_OK); - return 0; - } - // now the file should be gone! - } - // ask the database to create the profile - CreatePathToFileW(profile); - if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) { - mir_snwprintf(buf, TranslateT("Unable to create the profile '%s', the error was %x"), file, err); - MessageBoxW(m_hwnd, buf, TranslateT("Problem creating profile"), MB_ICONERROR | MB_OK); - return 0; - } - - // the profile has been created! - g_bDbCreated = true; - return 1; - } - - bool m_bFocused; - CCtrlCombo m_driverList; - CCtrlEdit m_profileName; - CCtrlBase m_warning; - -public: - CCreateProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILE_NEW), - m_btnOk(_btn), - m_pd(_pd), - m_bFocused(false), - m_driverList(this, IDC_PROFILEDRIVERS), - m_profileName(this, IDC_PROFILENAME), - m_warning(this, IDC_NODBDRIVERS) - {} - - bool OnInitDialog() override - { - // what, no plugins?! - if (arDbPlugins.getCount() == 0) { - m_driverList.Enable(false); - m_profileName.Enable(false); - ShowWindow(m_warning.GetHwnd(), TRUE); - } - else { - for (auto &p : arDbPlugins) - if (p->capabilities & MDB_CAPS_CREATE) - m_driverList.AddString(TranslateW(p->szFullName), (LPARAM)p); - } - - // default item - m_driverList.SetCurSel(0); - - // subclass the profile name box - mir_subclassWindow(m_profileName.GetHwnd(), ProfileNameValidate); - - // decide if there is a default profile name given in the INI and if it should be used - if (m_pd->noProfiles || (shouldAutoCreate(m_pd->m_profile) && !m_pd->m_profile.isExist())) { - wchar_t *profile = wcsrchr(m_pd->m_profile.GetBuffer(), '\\'); - if (profile) ++profile; - else profile = m_pd->m_profile.GetBuffer(); - - wchar_t *p = wcsrchr(profile, '.'); - wchar_t c = 0; - if (p) { c = *p; *p = 0; } - - m_profileName.SetText(profile); - if (c) *p = c; - } - - // focus on the textbox - PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); - return true; - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - switch (msg) { - case WM_FOCUSTEXTBOX: - SetFocus(m_profileName.GetHwnd()); - break; - - case WM_INPUTCHANGED: // when input in the edit box changes - NotifyChange(); - m_btnOk.Enable(GetWindowTextLength(m_profileName.GetHwnd()) > 0); - break; - - case WM_SHOWWINDOW: - if (wParam) { - m_btnOk.SetText(TranslateT("&Create")); - SendMessage(m_hwnd, WM_INPUTCHANGED, 0, 0); - m_bFocused = true; - } - else m_bFocused = false; - break; - } - return CDlgBase::DlgProc(msg, wParam, lParam); - } - - bool OnApply() override - { - LRESULT curSel = m_driverList.GetCurSel(); - if (curSel == -1 || !m_bFocused) - return false; // should never happen - - ptrW szName(m_profileName.GetText()); - if (mir_wstrlen(szName) == 0) - return false; - - // profile placed in "profile_name" subfolder - m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, szName.get(), szName.get()); - m_pd->dblink = (DATABASELINK *)m_driverList.GetItemData(curSel); - - if (CreateProfile(m_pd->m_profile, m_pd->dblink) == 0) - SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); - return true; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile selector - -static int numMessages[5]; - -static void stubAddMessage(int iType, const wchar_t *, ...) -{ - if (iType < 5) - numMessages[iType]++; -} - -class CChooseProfileDlg : public CDlgBase -{ - CCtrlButton &m_btnOk; - PROFILEMANAGERDATA *m_pd; - HANDLE m_hFileNotify; - - void DeleteProfile(const LVITEM &item) - { - CMStringW wszMessage(FORMAT, TranslateT("Are you sure you want to remove profile \"%s\"?"), item.pszText); - if (IDYES != MessageBoxW(nullptr, wszMessage, L"Miranda NG", MB_YESNO | MB_TASKMODAL | MB_ICONWARNING)) - return; - - wszMessage.Format(L"%s\\%s", g_profileDir, item.pszText); - DeleteDirectoryTreeW(wszMessage, true); - - m_profileList.DeleteItem(item.iItem); - } - - void CheckProfile(const wchar_t *profile) - { - CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - - if (TryLoadPlugin(plugin_checker, false)) - CallService(MS_DB_CHECKPROFILE, (WPARAM)wszFullName.c_str(), 0); - else - Plugin_Uninit(plugin_checker); - } - - void CompactProfile(DATABASELINK *dblink, const wchar_t *profile) - { - CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - - if (auto *db = dblink->Load(wszFullName, false)) { - db->Compact(); - delete db; - - MessageBoxW(nullptr, TranslateT("Database was compacted successfully"), L"Miranda NG", MB_OK | MB_ICONINFORMATION); - } - } - - void CheckRun() - { - m_btnOk.Enable(m_profileList.GetSelectedCount() == 1); - - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE; - item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (!m_profileList.GetItem(&item)) - return; - - switch(item.iImage) { - case 3: - m_btnOk.Enable(false); - return; - - case 2: - m_btnOk.SetText(TranslateT("&Convert")); - break; - - default: - m_btnOk.SetText(TranslateT("&Run")); - } - - // profile is placed in "profile_name" subfolder - - wchar_t tmpPath[MAX_PATH]; - mir_snwprintf(tmpPath, L"%s\\%s.dat", g_profileDir, profile); - if (_waccess(tmpPath, 2)) - m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, profile, profile); - else - m_pd->m_profile = tmpPath; - } - - void ExecuteMenu(LPARAM lParam) - { - LVHITTESTINFO lvht = { 0 }; - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - ScreenToClient(m_profileList.GetHwnd(), &lvht.pt); - - if (m_profileList.HitTest(&lvht) == -1) - return; - - if (lvht.iItem == -1) - return; - - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT; - item.iItem = lvht.iItem; - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (!m_profileList.GetItem(&item)) - return; - - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - - HMENU hMenu = CreatePopupMenu(); - if (item.iImage < 2) { - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run")); - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - } - - DATABASELINK *dblink = (DATABASELINK*)item.lParam; - if (dblink != nullptr) { - bool bAdded = false; - if (dblink->capabilities & MDB_CAPS_COMPACT) { - AppendMenu(hMenu, MF_STRING, 3, TranslateT("Compact database")); - bAdded = true; - } - - if (plugin_checker && (dblink->capabilities & MDB_CAPS_CHECK)) { - AppendMenu(hMenu, MF_STRING, 4, TranslateT("Check database")); - bAdded = true; - } - - if (bAdded) - AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); - } - - AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete")); - int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, m_hwnd, nullptr); - switch (index) { - case 1: - SendMessage(GetParent(m_hwndParent), WM_COMMAND, IDOK, 0); - break; - - case 2: - DeleteProfile(item); - break; - - case 3: - CompactProfile(dblink, profile); - break; - - case 4: - CheckProfile(profile); - break; - } - DestroyMenu(hMenu); - } - - CCtrlListView m_profileList; - -public: - CChooseProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILE_SELECTION), - m_btnOk(_btn), - m_pd(_pd), - m_profileList(this, IDC_PROFILELIST) - { - m_profileList.OnItemChanged = Callback(this, &CChooseProfileDlg::list_OnItemChanged); - m_profileList.OnKeyDown = Callback(this, &CChooseProfileDlg::list_OnKeyDown); - m_profileList.OnGetInfoTip = Callback(this, &CChooseProfileDlg::list_OnGetTip); - m_profileList.OnDoubleClick = Callback(this, &CChooseProfileDlg::list_OnDblClick); - } - - bool OnInitDialog() override - { - // set columns - LVCOLUMN col; - col.mask = LVCF_TEXT | LVCF_WIDTH; - col.pszText = TranslateT("Profile"); - col.cx = 100; - m_profileList.InsertColumn(0, &col); - - col.pszText = TranslateT("Driver"); - col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL); - m_profileList.InsertColumn(1, &col); - - col.pszText = TranslateT("Size"); - col.cx = 60; - m_profileList.InsertColumn(2, &col); - - // icons - HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL)); - - // LV will destroy the image list - m_profileList.SetImageList(hImgList, LVSIL_SMALL); - m_profileList.SetExtendedListViewStyle(m_profileList.GetExtendedListViewStyle() | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT); - - // find all the profiles - findProfiles(m_profileList, m_pd->m_profile); - PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); - - m_hFileNotify = FindFirstChangeNotification(g_profileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE); - if (m_hFileNotify != INVALID_HANDLE_VALUE) - SetTimer(m_hwnd, 0, 1200, nullptr); - return true; - } - - void OnDestroy() - { - KillTimer(m_hwnd, 0); - FindCloseChangeNotification(m_hFileNotify); - } - - void list_OnItemChanged(CCtrlListView::TEventInfo*) - { - CheckRun(); - } - - void list_OnKeyDown(CCtrlListView::TEventInfo *evt) - { - if (evt->nmlvkey->wVKey == VK_DELETE) { - wchar_t profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT; - item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = _countof(profile); - if (m_profileList.GetItem(&item)) - DeleteProfile(item); - } - } - - void list_OnGetTip(CCtrlListView::TEventInfo *evt) - { - if (auto pTip = evt->nmlvit) { - wchar_t profilename[MAX_PATH], tszFullPath[MAX_PATH]; - struct _stat statbuf; - m_profileList.GetItemText(pTip->iItem, 0, profilename, _countof(profilename)); - mir_snwprintf(tszFullPath, L"%s\\%s\\%s.dat", g_profileDir, profilename, profilename); - _wstat(tszFullPath, &statbuf); - mir_snwprintf(pTip->pszText, pTip->cchTextMax, L"%s\n%s: %s\n%s: %s", tszFullPath, TranslateT("Created"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_mtime)))); - } - } - - void list_OnDblClick(CCtrlListView::TEventInfo*) - { - CheckRun(); - EndDialog(GetParent(m_hwndParent), 1); - } - - INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override - { - switch (msg) { - case WM_TIMER: - if (WaitForSingleObject(m_hFileNotify, 0) == WAIT_OBJECT_0) { - m_profileList.DeleteAllItems(); - findProfiles(m_profileList, m_pd->m_profile); - FindNextChangeNotification(m_hFileNotify); - } - break; - - case WM_FOCUSTEXTBOX: - SetFocus(m_profileList.GetHwnd()); - if (m_pd->m_profile.IsEmpty() || m_profileList.GetSelectedCount() == 0) - m_profileList.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - break; - - case WM_SHOWWINDOW: - if (wParam) - CheckRun(); - break; - - case WM_CONTEXTMENU: - ExecuteMenu(lParam); - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Tab manager + its envelope - -class CProfileManager : public CDlgBase -{ - PROFILEMANAGERDATA *m_pd; - - CCtrlPages m_tab; - CCtrlButton m_btnOk; - CCtrlCheck m_chkSmEnabled; - CCtrlCombo m_servicePlugs; - CCtrlBase m_warning; - -public: - CProfileManager(PROFILEMANAGERDATA *_pd) : - CDlgBase(g_plugin, IDD_PROFILEMANAGER), - m_btnOk(this, IDOK), - m_pd(_pd), - m_tab(this, IDC_TABS), - m_warning(this, IDC_SM_LABEL), - m_servicePlugs(this, IDC_SM_COMBO), - m_chkSmEnabled(this, IDC_SM_ENABLED) - { - m_chkSmEnabled.OnChange = Callback(this, &CProfileManager::onChanged); - - m_tab.AddPage(LPGENW("My profiles"), nullptr, new CChooseProfileDlg(m_btnOk, m_pd)); - m_tab.AddPage(LPGENW("New profile"), nullptr, new CCreateProfileDlg(m_btnOk, m_pd)); - } - - bool OnInitDialog() override - { - // MUST NOT be replaced with Window_SetIcon_IcoLib!!! - SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconSX, g_iIconSY, 0)); - SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconX, g_iIconY, 0)); - - if (m_pd->noProfiles || shouldAutoCreate(m_pd->m_profile)) - m_tab.ActivatePage(1); - - // service mode combobox - if (servicePlugins.getCount() == 0) { - ShowWindow(m_warning.GetHwnd(), FALSE); - ShowWindow(m_chkSmEnabled.GetHwnd(), FALSE); - ShowWindow(m_servicePlugs.GetHwnd(), FALSE); - } - else { - for (int i = 0; i < servicePlugins.getCount(); i++) { - pluginEntry *p = servicePlugins[i]; - m_servicePlugs.AddStringA(p->pluginname, i); - } - - m_servicePlugs.Disable(); - m_servicePlugs.SetCurSel(0); - } - return true; - } - - void OnDestroy() - { - if (m_chkSmEnabled.GetState()) { - int idx = m_servicePlugs.GetCurData(); - if (idx != -1) - plugin_service = servicePlugins[idx]; - } - - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); - DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); - } - - void onChanged(CCtrlCheck*) - { - m_servicePlugs.Enable(m_chkSmEnabled.GetState()); - } -}; - -int getProfileManager(PROFILEMANAGERDATA *pd) -{ - return CProfileManager(pd).DoModal(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "plugins.h" +#include "langpack.h" +#include "profilemanager.h" +#include + +#pragma warning(disable : 4512) + +#define WM_INPUTCHANGED (WM_USER + 0x3000) +#define WM_FOCUSTEXTBOX (WM_USER + 0x3001) + +///////////////////////////////////////////////////////////////////////////////////////// +// Profile creator + +static BOOL EnumProfilesForList(const wchar_t *tszFullPath, wchar_t *profile, CCtrlListView &list, const wchar_t *szProfile) +{ + wchar_t sizeBuf[64]; + bool bFileLocked; + + wchar_t *p = wcsrchr(profile, '.'); + mir_wstrcpy(sizeBuf, L"0 KB"); + if (p != nullptr) *p = 0; + + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; + item.pszText = profile; + item.iItem = 0; + + struct _stat statbuf; + if (_wstat(tszFullPath, &statbuf) == 0) { + if (statbuf.st_size > 1000000) { + mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1048576.0); + mir_wstrcpy(sizeBuf + 5, L" MB"); + } + else { + mir_snwprintf(sizeBuf, L"%.3lf", (double)statbuf.st_size / 1024.0); + mir_wstrcpy(sizeBuf + 5, L" KB"); + } + bFileLocked = Profile_CheckOpened(tszFullPath); + } + else bFileLocked = true; + + DATABASELINK *dblink; + switch (touchDatabase(tszFullPath, &dblink)) { + case ERROR_SUCCESS: + item.iImage = (bFileLocked) ? 1 : 0; + break; + + case EGROKPRF_OBSOLETE: + item.iImage = 2; + break; + + case EGROKPRF_CANTREAD: + item.iImage = (bFileLocked) ? 1 : 3; + break; + + default: + item.iImage = 3; + } + + item.lParam = (LPARAM)dblink; + + int iItem = list.InsertItem(&item); + if (mir_wstrcmpi(szProfile, tszFullPath) == 0) + list.SetItemState(iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + + list.SetItemText(iItem, 2, sizeBuf); + + if (dblink != nullptr) + list.SetItemText(iItem, 1, TranslateW(dblink->szFullName)); + else if (bFileLocked) // file locked + list.SetItemText(iItem, 1, TranslateT("")); + else + list.SetItemText(iItem, 1, TranslateT("")); + + return TRUE; +} + +static int findProfiles(CCtrlListView &list, const wchar_t *szProfile) +{ + // find in Miranda NG profile subfolders + MFilePath searchspec; + searchspec.Format(L"%s\\*.*", g_profileDir); + + for (auto &it: searchspec.search()) { + // find all subfolders except "." and ".." + if (!it.isDir() || !wcscmp(it.getPath(), L".") || !wcscmp(it.getPath(), L"..")) + continue; + + MFilePath fullPath; + fullPath.Format(L"%s\\%s\\%s.dat", g_profileDir, it.getPath(), it.getPath()); + if (fullPath.isExist()) { + wchar_t profileName[MAX_PATH]; + mir_snwprintf(profileName, L"%s.dat", it.getPath()); + if (!EnumProfilesForList(fullPath, profileName, list, szProfile)) + break; + } + } + + return 1; +} + +static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_CHAR) { + if (wcschr(L".?/\\#' ", (wchar_t)wParam) != nullptr) + return 0; + PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0); + } + return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam); +} + +class CCreateProfileDlg : public CDlgBase +{ + CCtrlButton &m_btnOk; + PROFILEMANAGERDATA *m_pd; + + int CreateProfile(const wchar_t *profile, DATABASELINK *link) + { + // check if the file already exists + const wchar_t *file = wcsrchr(profile, '\\'); + if (file) + file++; + + int err = 0; + wchar_t buf[256]; + + if (_waccess(profile, 0) == 0) { + // file already exists! + mir_snwprintf(buf, + TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."), + file); + if (MessageBoxW(m_hwnd, buf, TranslateT("The profile already exists"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) + return 0; + + // move the file + if (DeleteDirectoryTreeW(profile, true) != 0) { + mir_snwprintf(buf, TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file); + MessageBoxW(m_hwnd, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION | MB_OK); + return 0; + } + // now the file should be gone! + } + // ask the database to create the profile + CreatePathToFileW(profile); + if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) { + mir_snwprintf(buf, TranslateT("Unable to create the profile '%s', the error was %x"), file, err); + MessageBoxW(m_hwnd, buf, TranslateT("Problem creating profile"), MB_ICONERROR | MB_OK); + return 0; + } + + // the profile has been created! + g_bDbCreated = true; + return 1; + } + + bool m_bFocused; + CCtrlCombo m_driverList; + CCtrlEdit m_profileName; + CCtrlBase m_warning; + +public: + CCreateProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : + CDlgBase(g_plugin, IDD_PROFILE_NEW), + m_btnOk(_btn), + m_pd(_pd), + m_bFocused(false), + m_driverList(this, IDC_PROFILEDRIVERS), + m_profileName(this, IDC_PROFILENAME), + m_warning(this, IDC_NODBDRIVERS) + {} + + bool OnInitDialog() override + { + // what, no plugins?! + if (arDbPlugins.getCount() == 0) { + m_driverList.Enable(false); + m_profileName.Enable(false); + ShowWindow(m_warning.GetHwnd(), TRUE); + } + else { + for (auto &p : arDbPlugins) + if (p->capabilities & MDB_CAPS_CREATE) + m_driverList.AddString(TranslateW(p->szFullName), (LPARAM)p); + } + + // default item + m_driverList.SetCurSel(0); + + // subclass the profile name box + mir_subclassWindow(m_profileName.GetHwnd(), ProfileNameValidate); + + // decide if there is a default profile name given in the INI and if it should be used + if (m_pd->noProfiles || (shouldAutoCreate(m_pd->m_profile) && !m_pd->m_profile.isExist())) { + wchar_t *profile = wcsrchr(m_pd->m_profile.GetBuffer(), '\\'); + if (profile) ++profile; + else profile = m_pd->m_profile.GetBuffer(); + + wchar_t *p = wcsrchr(profile, '.'); + wchar_t c = 0; + if (p) { c = *p; *p = 0; } + + m_profileName.SetText(profile); + if (c) *p = c; + } + + // focus on the textbox + PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); + return true; + } + + INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override + { + switch (msg) { + case WM_FOCUSTEXTBOX: + SetFocus(m_profileName.GetHwnd()); + break; + + case WM_INPUTCHANGED: // when input in the edit box changes + NotifyChange(); + m_btnOk.Enable(GetWindowTextLength(m_profileName.GetHwnd()) > 0); + break; + + case WM_SHOWWINDOW: + if (wParam) { + m_btnOk.SetText(TranslateT("&Create")); + SendMessage(m_hwnd, WM_INPUTCHANGED, 0, 0); + m_bFocused = true; + } + else m_bFocused = false; + break; + } + return CDlgBase::DlgProc(msg, wParam, lParam); + } + + bool OnApply() override + { + LRESULT curSel = m_driverList.GetCurSel(); + if (curSel == -1 || !m_bFocused) + return false; // should never happen + + ptrW szName(m_profileName.GetText()); + if (mir_wstrlen(szName) == 0) + return false; + + // profile placed in "profile_name" subfolder + m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, szName.get(), szName.get()); + m_pd->dblink = (DATABASELINK *)m_driverList.GetItemData(curSel); + + if (CreateProfile(m_pd->m_profile, m_pd->dblink) == 0) + SetWindowLongPtr(m_hwnd, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); + return true; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// Profile selector + +static int numMessages[5]; + +static void stubAddMessage(int iType, const wchar_t *, ...) +{ + if (iType < 5) + numMessages[iType]++; +} + +class CChooseProfileDlg : public CDlgBase +{ + CCtrlButton &m_btnOk; + PROFILEMANAGERDATA *m_pd; + HANDLE m_hFileNotify; + + void DeleteProfile(const LVITEM &item) + { + CMStringW wszMessage(FORMAT, TranslateT("Are you sure you want to remove profile \"%s\"?"), item.pszText); + if (IDYES != MessageBoxW(nullptr, wszMessage, L"Miranda NG", MB_YESNO | MB_TASKMODAL | MB_ICONWARNING)) + return; + + wszMessage.Format(L"%s\\%s", g_profileDir, item.pszText); + DeleteDirectoryTreeW(wszMessage, true); + + m_profileList.DeleteItem(item.iItem); + } + + void CheckProfile(const wchar_t *profile) + { + CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); + + if (TryLoadPlugin(plugin_checker, false)) + CallService(MS_DB_CHECKPROFILE, (WPARAM)wszFullName.c_str(), 0); + else + Plugin_Uninit(plugin_checker); + } + + void CompactProfile(DATABASELINK *dblink, const wchar_t *profile) + { + CMStringW wszFullName(FORMAT, L"%s\\%s\\%s.dat", g_profileDir, profile, profile); + + if (auto *db = dblink->Load(wszFullName, false)) { + db->Compact(); + delete db; + + MessageBoxW(nullptr, TranslateT("Database was compacted successfully"), L"Miranda NG", MB_OK | MB_ICONINFORMATION); + } + } + + void CheckRun() + { + m_btnOk.Enable(m_profileList.GetSelectedCount() == 1); + + wchar_t profile[MAX_PATH]; + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_IMAGE; + item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); + item.pszText = profile; + item.cchTextMax = _countof(profile); + if (!m_profileList.GetItem(&item)) + return; + + switch(item.iImage) { + case 3: + m_btnOk.Enable(false); + return; + + case 2: + m_btnOk.SetText(TranslateT("&Convert")); + break; + + default: + m_btnOk.SetText(TranslateT("&Run")); + } + + // profile is placed in "profile_name" subfolder + + wchar_t tmpPath[MAX_PATH]; + mir_snwprintf(tmpPath, L"%s\\%s.dat", g_profileDir, profile); + if (_waccess(tmpPath, 2)) + m_pd->m_profile.Format(L"%s\\%s\\%s.dat", g_profileDir, profile, profile); + else + m_pd->m_profile = tmpPath; + } + + void ExecuteMenu(LPARAM lParam) + { + LVHITTESTINFO lvht = { 0 }; + lvht.pt.x = GET_X_LPARAM(lParam); + lvht.pt.y = GET_Y_LPARAM(lParam); + ScreenToClient(m_profileList.GetHwnd(), &lvht.pt); + + if (m_profileList.HitTest(&lvht) == -1) + return; + + if (lvht.iItem == -1) + return; + + wchar_t profile[MAX_PATH]; + LVITEM item = { 0 }; + item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT; + item.iItem = lvht.iItem; + item.pszText = profile; + item.cchTextMax = _countof(profile); + if (!m_profileList.GetItem(&item)) + return; + + lvht.pt.x = GET_X_LPARAM(lParam); + lvht.pt.y = GET_Y_LPARAM(lParam); + + HMENU hMenu = CreatePopupMenu(); + if (item.iImage < 2) { + AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run")); + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + } + + DATABASELINK *dblink = (DATABASELINK*)item.lParam; + if (dblink != nullptr) { + bool bAdded = false; + if (dblink->capabilities & MDB_CAPS_COMPACT) { + AppendMenu(hMenu, MF_STRING, 3, TranslateT("Compact database")); + bAdded = true; + } + + if (plugin_checker && (dblink->capabilities & MDB_CAPS_CHECK)) { + AppendMenu(hMenu, MF_STRING, 4, TranslateT("Check database")); + bAdded = true; + } + + if (bAdded) + AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr); + } + + AppendMenu(hMenu, MF_STRING, 2, TranslateT("Delete")); + int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, m_hwnd, nullptr); + switch (index) { + case 1: + SendMessage(GetParent(m_hwndParent), WM_COMMAND, IDOK, 0); + break; + + case 2: + DeleteProfile(item); + break; + + case 3: + CompactProfile(dblink, profile); + break; + + case 4: + CheckProfile(profile); + break; + } + DestroyMenu(hMenu); + } + + CCtrlListView m_profileList; + +public: + CChooseProfileDlg(CCtrlButton &_btn, PROFILEMANAGERDATA *_pd) : + CDlgBase(g_plugin, IDD_PROFILE_SELECTION), + m_btnOk(_btn), + m_pd(_pd), + m_profileList(this, IDC_PROFILELIST) + { + m_profileList.OnItemChanged = Callback(this, &CChooseProfileDlg::list_OnItemChanged); + m_profileList.OnKeyDown = Callback(this, &CChooseProfileDlg::list_OnKeyDown); + m_profileList.OnGetInfoTip = Callback(this, &CChooseProfileDlg::list_OnGetTip); + m_profileList.OnDoubleClick = Callback(this, &CChooseProfileDlg::list_OnDblClick); + } + + bool OnInitDialog() override + { + // set columns + LVCOLUMN col; + col.mask = LVCF_TEXT | LVCF_WIDTH; + col.pszText = TranslateT("Profile"); + col.cx = 100; + m_profileList.InsertColumn(0, &col); + + col.pszText = TranslateT("Driver"); + col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL); + m_profileList.InsertColumn(1, &col); + + col.pszText = TranslateT("Size"); + col.cx = 60; + m_profileList.InsertColumn(2, &col); + + // icons + HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS)); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE)); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING)); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL)); + + // LV will destroy the image list + m_profileList.SetImageList(hImgList, LVSIL_SMALL); + m_profileList.SetExtendedListViewStyle(m_profileList.GetExtendedListViewStyle() | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT); + + // find all the profiles + findProfiles(m_profileList, m_pd->m_profile); + PostMessage(m_hwnd, WM_FOCUSTEXTBOX, 0, 0); + + m_hFileNotify = FindFirstChangeNotification(g_profileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE); + if (m_hFileNotify != INVALID_HANDLE_VALUE) + SetTimer(m_hwnd, 0, 1200, nullptr); + return true; + } + + void OnDestroy() + { + KillTimer(m_hwnd, 0); + FindCloseChangeNotification(m_hFileNotify); + } + + void list_OnItemChanged(CCtrlListView::TEventInfo*) + { + CheckRun(); + } + + void list_OnKeyDown(CCtrlListView::TEventInfo *evt) + { + if (evt->nmlvkey->wVKey == VK_DELETE) { + wchar_t profile[MAX_PATH]; + LVITEM item = { 0 }; + item.mask = LVIF_TEXT; + item.iItem = m_profileList.GetNextItem(-1, LVNI_SELECTED | LVNI_ALL); + item.pszText = profile; + item.cchTextMax = _countof(profile); + if (m_profileList.GetItem(&item)) + DeleteProfile(item); + } + } + + void list_OnGetTip(CCtrlListView::TEventInfo *evt) + { + if (auto pTip = evt->nmlvit) { + wchar_t profilename[MAX_PATH], tszFullPath[MAX_PATH]; + struct _stat statbuf; + m_profileList.GetItemText(pTip->iItem, 0, profilename, _countof(profilename)); + mir_snwprintf(tszFullPath, L"%s\\%s\\%s.dat", g_profileDir, profilename, profilename); + _wstat(tszFullPath, &statbuf); + mir_snwprintf(pTip->pszText, pTip->cchTextMax, L"%s\n%s: %s\n%s: %s", tszFullPath, TranslateT("Created"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimw(NEWWSTR_ALLOCA(_wctime(&statbuf.st_mtime)))); + } + } + + void list_OnDblClick(CCtrlListView::TEventInfo*) + { + CheckRun(); + EndDialog(GetParent(m_hwndParent), 1); + } + + INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override + { + switch (msg) { + case WM_TIMER: + if (WaitForSingleObject(m_hFileNotify, 0) == WAIT_OBJECT_0) { + m_profileList.DeleteAllItems(); + findProfiles(m_profileList, m_pd->m_profile); + FindNextChangeNotification(m_hFileNotify); + } + break; + + case WM_FOCUSTEXTBOX: + SetFocus(m_profileList.GetHwnd()); + if (m_pd->m_profile.IsEmpty() || m_profileList.GetSelectedCount() == 0) + m_profileList.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + break; + + case WM_SHOWWINDOW: + if (wParam) + CheckRun(); + break; + + case WM_CONTEXTMENU: + ExecuteMenu(lParam); + break; + } + + return CDlgBase::DlgProc(msg, wParam, lParam); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// Tab manager + its envelope + +class CProfileManager : public CDlgBase +{ + PROFILEMANAGERDATA *m_pd; + + CCtrlPages m_tab; + CCtrlButton m_btnOk; + CCtrlCheck m_chkSmEnabled; + CCtrlCombo m_servicePlugs; + CCtrlBase m_warning; + +public: + CProfileManager(PROFILEMANAGERDATA *_pd) : + CDlgBase(g_plugin, IDD_PROFILEMANAGER), + m_btnOk(this, IDOK), + m_pd(_pd), + m_tab(this, IDC_TABS), + m_warning(this, IDC_SM_LABEL), + m_servicePlugs(this, IDC_SM_COMBO), + m_chkSmEnabled(this, IDC_SM_ENABLED) + { + m_chkSmEnabled.OnChange = Callback(this, &CProfileManager::onChanged); + + m_tab.AddPage(LPGENW("My profiles"), nullptr, new CChooseProfileDlg(m_btnOk, m_pd)); + m_tab.AddPage(LPGENW("New profile"), nullptr, new CCreateProfileDlg(m_btnOk, m_pd)); + } + + bool OnInitDialog() override + { + // MUST NOT be replaced with Window_SetIcon_IcoLib!!! + SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconSX, g_iIconSY, 0)); + SendMessage(m_hwnd, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(g_plugin.getInst(), MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, g_iIconX, g_iIconY, 0)); + + if (m_pd->noProfiles || shouldAutoCreate(m_pd->m_profile)) + m_tab.ActivatePage(1); + + // service mode combobox + if (servicePlugins.getCount() == 0) { + ShowWindow(m_warning.GetHwnd(), FALSE); + ShowWindow(m_chkSmEnabled.GetHwnd(), FALSE); + ShowWindow(m_servicePlugs.GetHwnd(), FALSE); + } + else { + for (int i = 0; i < servicePlugins.getCount(); i++) { + pluginEntry *p = servicePlugins[i]; + m_servicePlugs.AddStringA(p->pluginname, i); + } + + m_servicePlugs.Disable(); + m_servicePlugs.SetCurSel(0); + } + return true; + } + + void OnDestroy() + { + if (m_chkSmEnabled.GetState()) { + int idx = m_servicePlugs.GetCurData(); + if (idx != -1) + plugin_service = servicePlugins[idx]; + } + + DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, 0)); + DestroyIcon((HICON)SendMessage(m_hwnd, WM_SETICON, ICON_BIG, 0)); + } + + void onChanged(CCtrlCheck*) + { + m_servicePlugs.Enable(m_chkSmEnabled.GetState()); + } +}; + +int getProfileManager(PROFILEMANAGERDATA *pd) +{ + return CProfileManager(pd).DoModal(); +} diff --git a/src/mir_app/src/profilemanager.h b/src/mir_app/src/profilemanager.h index 39685e82bc..ab7e9e8a14 100644 --- a/src/mir_app/src/profilemanager.h +++ b/src/mir_app/src/profilemanager.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/proto_accs.cpp b/src/mir_app/src/proto_accs.cpp index 8947568395..501497361c 100644 --- a/src/mir_app/src/proto_accs.cpp +++ b/src/mir_app/src/proto_accs.cpp @@ -1,435 +1,435 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "clc.h" - -bool CheckProtocolOrder(void); -void BuildProtoMenus(); - -HICON Proto_GetIcon(PROTO_INTERFACE *ppro, int iconIndex); - -static bool bModuleInitialized = false; -static HANDLE hHooks[4]; - -static int CompareAccounts(const PROTOACCOUNT* p1, const PROTOACCOUNT* p2) -{ - return mir_strcmp(p1->szModuleName, p2->szModuleName); -} - -LIST g_arAccounts(10, CompareAccounts); - -///////////////////////////////////////////////////////////////////////////////////////// - -static int EnumDbModules(const char *szModuleName, void*) -{ - ptrA szProtoName(db_get_sa(0, szModuleName, "AM_BaseProto")); - if (szProtoName) { - if (!Proto_GetAccount(szModuleName)) { - PROTOACCOUNT *pa = new PROTOACCOUNT(szModuleName); - pa->szProtoName = szProtoName.detach(); - pa->tszAccountName = mir_a2u(szModuleName); - pa->bIsVisible = true; - pa->bIsEnabled = false; - pa->iOrder = g_arAccounts.getCount(); - g_arAccounts.insert(pa); - } - } - return 0; -} - -void LoadDbAccounts(void) -{ - int ver = db_get_dw(0, "Protocols", "PrVer", -1); - int count = db_get_dw(0, "Protocols", "ProtoCount", 0); - - for (int i = 0; i < count; i++) { - char buf[10]; - _itoa(i, buf, 10); - ptrA szModuleName(db_get_sa(0, "Protocols", buf)); - if (szModuleName == nullptr) - continue; - - PROTOACCOUNT *pa = Proto_GetAccount(szModuleName); - if (pa == nullptr) { - pa = new PROTOACCOUNT(szModuleName); - g_arAccounts.insert(pa); - } - - _itoa(OFFSET_VISIBLE + i, buf, 10); - pa->bIsVisible = db_get_dw(0, "Protocols", buf, 1) != 0; - - _itoa(OFFSET_PROTOPOS + i, buf, 10); - pa->iOrder = db_get_dw(0, "Protocols", buf, 1); - - if (ver >= 4) { - _itoa(OFFSET_NAME + i, buf, 10); - pa->tszAccountName = db_get_wsa(0, "Protocols", buf); - - _itoa(OFFSET_ENABLED + i, buf, 10); - pa->bIsEnabled = db_get_dw(0, "Protocols", buf, 1) != 0; - if (!pa->bIsEnabled && !mir_strcmp(pa->szModuleName, META_PROTO)) { - pa->bIsEnabled = true; - db_set_dw(0, "Protocols", buf, 1); - } - pa->szProtoName = db_get_sa(0, szModuleName, "AM_BaseProto"); - } - else pa->bIsEnabled = true; - - if (!pa->szProtoName) { - pa->szProtoName = mir_strdup(szModuleName); - db_set_s(0, szModuleName, "AM_BaseProto", pa->szProtoName); - } - - if (!pa->tszAccountName) - pa->tszAccountName = mir_a2u(szModuleName); - } - - if (CheckProtocolOrder()) - WriteDbAccounts(); - - int anum = g_arAccounts.getCount(); - db_enum_modules(EnumDbModules); - if (anum != g_arAccounts.getCount()) - WriteDbAccounts(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WriteDbAccounts() -{ - // enum all old settings to delete - db_delete_module(0, "Protocols"); - - // write new data - for (int i = 0; i < g_arAccounts.getCount(); i++) { - PROTOACCOUNT *pa = g_arAccounts[i]; - - char buf[20]; - _itoa(i, buf, 10); - db_set_s(0, "Protocols", buf, pa->szModuleName); - - _itoa(OFFSET_PROTOPOS + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->iOrder); - - _itoa(OFFSET_VISIBLE + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->bIsVisible); - - _itoa(OFFSET_ENABLED + i, buf, 10); - db_set_dw(0, "Protocols", buf, pa->bIsEnabled); - - _itoa(OFFSET_NAME + i, buf, 10); - db_set_ws(0, "Protocols", buf, pa->tszAccountName); - } - - db_set_dw(0, "Protocols", "ProtoCount", g_arAccounts.getCount()); - db_set_dw(0, "Protocols", "PrVer", 4); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int OnContactDeleted(WPARAM hContact, LPARAM) -{ - if (auto *ppro = Proto_GetInstance(hContact)) - ppro->OnContactDeleted(hContact); - return 0; -} - -static int OnEventEdited(WPARAM hContact, LPARAM hDbEvent) -{ - if (auto *ppro = Proto_GetInstance(hContact)) - ppro->OnEventEdited(hContact, hDbEvent); - return 0; -} - -void InitStaticAccounts() -{ - int count = 0; - - for (auto &pa : g_arAccounts) { - if (!pa->ppro || !pa->IsEnabled()) - continue; - - pa->ppro->OnModulesLoaded(); - - if (!pa->bOldProto) - count++; - - if (pa->IsVisible()) - pa->ppro->OnBuildProtoMenu(); - } - - if (count == 0 && !db_get_b(0, "FirstRun", "AccManager", 0)) { - db_set_b(0, "FirstRun", "AccManager", 1); - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - } - // This is for pack creators with a profile with predefined g_arAccounts - else if (db_get_b(0, "FirstRun", "ForceShowAccManager", 0)) { - CallService(MS_PROTO_SHOWACCMGR, 0, 0); - db_unset(0, "FirstRun", "ForceShowAccManager"); - } -} - -static int UninitializeStaticAccounts(WPARAM, LPARAM) -{ - // request permission to exit first - for (auto &pa : g_arAccounts) - if (pa->ppro && pa->IsEnabled()) - if (!pa->ppro->IsReadyToExit()) - return 1; - - // okay, all protocols are ready, exiting - for (auto &pa : g_arAccounts) - if (pa->ppro && pa->IsEnabled()) - pa->ppro->OnShutdown(); - - return 0; -} - -int LoadAccountsModule(void) -{ - bModuleInitialized = true; - - for (auto &pa : g_arAccounts) { - pa->bDynDisabled = !Proto_IsProtocolLoaded(pa->szProtoName); - if (pa->ppro) - continue; - - if (!pa->IsEnabled()) - continue; - - if (!ActivateAccount(pa, false)) - pa->bDynDisabled = true; - } - - hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts); - hHooks[2] = HookEvent(ME_DB_CONTACT_DELETED, OnContactDeleted); - hHooks[3] = HookEvent(ME_DB_EVENT_EDITED, OnEventEdited); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static HANDLE CreateProtoServiceEx(const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param) -{ - char tmp[100]; - mir_snprintf(tmp, "%s%s", szModule, szService); - return CreateServiceFunctionObj(tmp, pFunc, param); -} - -bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic) -{ - MBaseProto *ppd = Proto_GetProto(pa->szProtoName); - if (ppd == nullptr) - return false; - - if (ppd->fnInit == nullptr) - return false; - - PROTO_INTERFACE *ppi = pa->ppro; - if (ppi == nullptr) { - ppi = ppd->fnInit(pa->szModuleName, pa->tszAccountName); - if (ppi == nullptr) - return false; - - pa->ppro = ppi; - - if (bIsDynamic) { - if (g_bModulesLoadedFired) - pa->ppro->OnModulesLoaded(); - if (!db_get_b(0, "CList", "MoveProtoMenus", true)) - pa->ppro->OnBuildProtoMenu(); - pa->bDynDisabled = false; - } - } - - if (ppi->m_hProtoIcon == nullptr) - ppi->m_hProtoIcon = IcoLib_IsManaged(Skin_LoadProtoIcon(pa->szModuleName, ID_STATUS_ONLINE)); - ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE; - return true; -} - -MIR_APP_DLL(int) Proto_GetAverageStatus(int *pAccountNumber) -{ - int netProtoCount = 0, averageMode = 0; - - for (auto &pa : g_arAccounts) { - if (!pa->IsVisible() || pa->IsLocked()) - continue; - - netProtoCount++; - if (averageMode == 0) - averageMode = pa->iRealStatus; - else if (averageMode > 0 && averageMode != pa->iRealStatus) { - averageMode = -1; - if (pAccountNumber == nullptr) - break; - } - } - - if (pAccountNumber) - *pAccountNumber = netProtoCount; - return averageMode; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct DeactivationThreadParam -{ - PROTO_INTERFACE *ppro; - pfnUninitProto fnUninit; - int flags; -}; - -pfnUninitProto GetProtocolDestructor(char *szProto); - -static void __cdecl DeactivationThread(DeactivationThreadParam *param) -{ - PROTO_INTERFACE *p = (PROTO_INTERFACE*)param->ppro; - p->SetStatus(ID_STATUS_OFFLINE); - - char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName); - - if (param->flags & DAF_DYNAMIC) { - while (!p->IsReadyToExit()) - SleepEx(100, TRUE); - - p->OnShutdown(); - } - - KillObjectThreads(p); // waits for them before terminating - KillObjectEventHooks(p); // untie an object from the outside world - - if (param->flags & DAF_ERASE) - p->OnErase(); - - if (param->fnUninit) - param->fnUninit(p); - - KillObjectServices(p); - - if (param->flags & DAF_ERASE) - EraseAccount(szModuleName); - - delete param; -} - -void DeactivateAccount(PROTOACCOUNT *pa, int flags) -{ - if (pa->hwndAccMgrUI) { - DestroyWindow(pa->hwndAccMgrUI); - pa->hwndAccMgrUI = nullptr; - pa->bAccMgrUIChanged = FALSE; - } - - if (flags & DAF_DYNAMIC) - NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa); - else - pa->iIconBase = -1; - - if (pa->ppro == nullptr) { - if (flags & DAF_ERASE) - EraseAccount(pa->szModuleName); - return; - } - - DeactivationThreadParam *param = new DeactivationThreadParam; - param->ppro = pa->ppro; - param->fnUninit = GetProtocolDestructor(pa->szProtoName); - param->flags = flags; - pa->ppro = nullptr; - if (flags & DAF_FORK) - mir_forkThread(DeactivationThread, param); - else - DeactivationThread(param); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void KillModuleAccounts(HINSTANCE hInst) -{ - for (auto &pd : g_arProtos.rev_iter()) { - if (pd->hInst != hInst) - continue; - - for (auto &pa : g_arAccounts.rev_iter()) { - if (!mir_strcmp(pa->szProtoName, pd->szName)) { - pa->bDynDisabled = true; - DeactivateAccount(pa, DAF_DYNAMIC); - } - } - - g_arProtos.removeItem(&pd); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void EraseAccount(const char *pszModuleName) -{ - // remove protocol contacts first - for (MCONTACT hContact = db_find_first(pszModuleName); hContact != 0;) { - MCONTACT hNext = db_find_next(hContact, pszModuleName); - db_delete_contact(hContact); - hContact = hNext; - } - - // remove all protocol settings - db_delete_module(0, pszModuleName); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void UnloadAccount(PROTOACCOUNT *pa, int flags) -{ - DeactivateAccount(pa, flags); - - // szModuleName should be freed only on a program's exit. - // otherwise many plugins dependand on static protocol names will crash! - // do NOT fix this 'leak', please - if (!(flags & DAF_DYNAMIC)) - delete pa; - else { - replaceStrW(pa->tszAccountName, 0); - replaceStr(pa->szProtoName, 0); - replaceStr(pa->szUniqueId, 0); - } -} - -void UnloadAccountsModule() -{ - if (!bModuleInitialized) - return; - - auto T = g_arAccounts.rev_iter(); - for (auto &it : T) { - UnloadAccount(it, 0); - g_arAccounts.removeItem(&it); - } - g_arAccounts.destroy(); - - for (auto &it : hHooks) - UnhookEvent(it); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "clc.h" + +bool CheckProtocolOrder(void); +void BuildProtoMenus(); + +HICON Proto_GetIcon(PROTO_INTERFACE *ppro, int iconIndex); + +static bool bModuleInitialized = false; +static HANDLE hHooks[4]; + +static int CompareAccounts(const PROTOACCOUNT* p1, const PROTOACCOUNT* p2) +{ + return mir_strcmp(p1->szModuleName, p2->szModuleName); +} + +LIST g_arAccounts(10, CompareAccounts); + +///////////////////////////////////////////////////////////////////////////////////////// + +static int EnumDbModules(const char *szModuleName, void*) +{ + ptrA szProtoName(db_get_sa(0, szModuleName, "AM_BaseProto")); + if (szProtoName) { + if (!Proto_GetAccount(szModuleName)) { + PROTOACCOUNT *pa = new PROTOACCOUNT(szModuleName); + pa->szProtoName = szProtoName.detach(); + pa->tszAccountName = mir_a2u(szModuleName); + pa->bIsVisible = true; + pa->bIsEnabled = false; + pa->iOrder = g_arAccounts.getCount(); + g_arAccounts.insert(pa); + } + } + return 0; +} + +void LoadDbAccounts(void) +{ + int ver = db_get_dw(0, "Protocols", "PrVer", -1); + int count = db_get_dw(0, "Protocols", "ProtoCount", 0); + + for (int i = 0; i < count; i++) { + char buf[10]; + _itoa(i, buf, 10); + ptrA szModuleName(db_get_sa(0, "Protocols", buf)); + if (szModuleName == nullptr) + continue; + + PROTOACCOUNT *pa = Proto_GetAccount(szModuleName); + if (pa == nullptr) { + pa = new PROTOACCOUNT(szModuleName); + g_arAccounts.insert(pa); + } + + _itoa(OFFSET_VISIBLE + i, buf, 10); + pa->bIsVisible = db_get_dw(0, "Protocols", buf, 1) != 0; + + _itoa(OFFSET_PROTOPOS + i, buf, 10); + pa->iOrder = db_get_dw(0, "Protocols", buf, 1); + + if (ver >= 4) { + _itoa(OFFSET_NAME + i, buf, 10); + pa->tszAccountName = db_get_wsa(0, "Protocols", buf); + + _itoa(OFFSET_ENABLED + i, buf, 10); + pa->bIsEnabled = db_get_dw(0, "Protocols", buf, 1) != 0; + if (!pa->bIsEnabled && !mir_strcmp(pa->szModuleName, META_PROTO)) { + pa->bIsEnabled = true; + db_set_dw(0, "Protocols", buf, 1); + } + pa->szProtoName = db_get_sa(0, szModuleName, "AM_BaseProto"); + } + else pa->bIsEnabled = true; + + if (!pa->szProtoName) { + pa->szProtoName = mir_strdup(szModuleName); + db_set_s(0, szModuleName, "AM_BaseProto", pa->szProtoName); + } + + if (!pa->tszAccountName) + pa->tszAccountName = mir_a2u(szModuleName); + } + + if (CheckProtocolOrder()) + WriteDbAccounts(); + + int anum = g_arAccounts.getCount(); + db_enum_modules(EnumDbModules); + if (anum != g_arAccounts.getCount()) + WriteDbAccounts(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WriteDbAccounts() +{ + // enum all old settings to delete + db_delete_module(0, "Protocols"); + + // write new data + for (int i = 0; i < g_arAccounts.getCount(); i++) { + PROTOACCOUNT *pa = g_arAccounts[i]; + + char buf[20]; + _itoa(i, buf, 10); + db_set_s(0, "Protocols", buf, pa->szModuleName); + + _itoa(OFFSET_PROTOPOS + i, buf, 10); + db_set_dw(0, "Protocols", buf, pa->iOrder); + + _itoa(OFFSET_VISIBLE + i, buf, 10); + db_set_dw(0, "Protocols", buf, pa->bIsVisible); + + _itoa(OFFSET_ENABLED + i, buf, 10); + db_set_dw(0, "Protocols", buf, pa->bIsEnabled); + + _itoa(OFFSET_NAME + i, buf, 10); + db_set_ws(0, "Protocols", buf, pa->tszAccountName); + } + + db_set_dw(0, "Protocols", "ProtoCount", g_arAccounts.getCount()); + db_set_dw(0, "Protocols", "PrVer", 4); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int OnContactDeleted(WPARAM hContact, LPARAM) +{ + if (auto *ppro = Proto_GetInstance(hContact)) + ppro->OnContactDeleted(hContact); + return 0; +} + +static int OnEventEdited(WPARAM hContact, LPARAM hDbEvent) +{ + if (auto *ppro = Proto_GetInstance(hContact)) + ppro->OnEventEdited(hContact, hDbEvent); + return 0; +} + +void InitStaticAccounts() +{ + int count = 0; + + for (auto &pa : g_arAccounts) { + if (!pa->ppro || !pa->IsEnabled()) + continue; + + pa->ppro->OnModulesLoaded(); + + if (!pa->bOldProto) + count++; + + if (pa->IsVisible()) + pa->ppro->OnBuildProtoMenu(); + } + + if (count == 0 && !db_get_b(0, "FirstRun", "AccManager", 0)) { + db_set_b(0, "FirstRun", "AccManager", 1); + CallService(MS_PROTO_SHOWACCMGR, 0, 0); + } + // This is for pack creators with a profile with predefined g_arAccounts + else if (db_get_b(0, "FirstRun", "ForceShowAccManager", 0)) { + CallService(MS_PROTO_SHOWACCMGR, 0, 0); + db_unset(0, "FirstRun", "ForceShowAccManager"); + } +} + +static int UninitializeStaticAccounts(WPARAM, LPARAM) +{ + // request permission to exit first + for (auto &pa : g_arAccounts) + if (pa->ppro && pa->IsEnabled()) + if (!pa->ppro->IsReadyToExit()) + return 1; + + // okay, all protocols are ready, exiting + for (auto &pa : g_arAccounts) + if (pa->ppro && pa->IsEnabled()) + pa->ppro->OnShutdown(); + + return 0; +} + +int LoadAccountsModule(void) +{ + bModuleInitialized = true; + + for (auto &pa : g_arAccounts) { + pa->bDynDisabled = !Proto_IsProtocolLoaded(pa->szProtoName); + if (pa->ppro) + continue; + + if (!pa->IsEnabled()) + continue; + + if (!ActivateAccount(pa, false)) + pa->bDynDisabled = true; + } + + hHooks[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, UninitializeStaticAccounts); + hHooks[2] = HookEvent(ME_DB_CONTACT_DELETED, OnContactDeleted); + hHooks[3] = HookEvent(ME_DB_EVENT_EDITED, OnEventEdited); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static HANDLE CreateProtoServiceEx(const char* szModule, const char* szService, MIRANDASERVICEOBJ pFunc, void* param) +{ + char tmp[100]; + mir_snprintf(tmp, "%s%s", szModule, szService); + return CreateServiceFunctionObj(tmp, pFunc, param); +} + +bool ActivateAccount(PROTOACCOUNT *pa, bool bIsDynamic) +{ + MBaseProto *ppd = Proto_GetProto(pa->szProtoName); + if (ppd == nullptr) + return false; + + if (ppd->fnInit == nullptr) + return false; + + PROTO_INTERFACE *ppi = pa->ppro; + if (ppi == nullptr) { + ppi = ppd->fnInit(pa->szModuleName, pa->tszAccountName); + if (ppi == nullptr) + return false; + + pa->ppro = ppi; + + if (bIsDynamic) { + if (g_bModulesLoadedFired) + pa->ppro->OnModulesLoaded(); + if (!db_get_b(0, "CList", "MoveProtoMenus", true)) + pa->ppro->OnBuildProtoMenu(); + pa->bDynDisabled = false; + } + } + + if (ppi->m_hProtoIcon == nullptr) + ppi->m_hProtoIcon = IcoLib_IsManaged(Skin_LoadProtoIcon(pa->szModuleName, ID_STATUS_ONLINE)); + ppi->m_iDesiredStatus = ppi->m_iStatus = ID_STATUS_OFFLINE; + return true; +} + +MIR_APP_DLL(int) Proto_GetAverageStatus(int *pAccountNumber) +{ + int netProtoCount = 0, averageMode = 0; + + for (auto &pa : g_arAccounts) { + if (!pa->IsVisible() || pa->IsLocked()) + continue; + + netProtoCount++; + if (averageMode == 0) + averageMode = pa->iRealStatus; + else if (averageMode > 0 && averageMode != pa->iRealStatus) { + averageMode = -1; + if (pAccountNumber == nullptr) + break; + } + } + + if (pAccountNumber) + *pAccountNumber = netProtoCount; + return averageMode; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct DeactivationThreadParam +{ + PROTO_INTERFACE *ppro; + pfnUninitProto fnUninit; + int flags; +}; + +pfnUninitProto GetProtocolDestructor(char *szProto); + +static void __cdecl DeactivationThread(DeactivationThreadParam *param) +{ + PROTO_INTERFACE *p = (PROTO_INTERFACE*)param->ppro; + p->SetStatus(ID_STATUS_OFFLINE); + + char *szModuleName = NEWSTR_ALLOCA(p->m_szModuleName); + + if (param->flags & DAF_DYNAMIC) { + while (!p->IsReadyToExit()) + SleepEx(100, TRUE); + + p->OnShutdown(); + } + + KillObjectThreads(p); // waits for them before terminating + KillObjectEventHooks(p); // untie an object from the outside world + + if (param->flags & DAF_ERASE) + p->OnErase(); + + if (param->fnUninit) + param->fnUninit(p); + + KillObjectServices(p); + + if (param->flags & DAF_ERASE) + EraseAccount(szModuleName); + + delete param; +} + +void DeactivateAccount(PROTOACCOUNT *pa, int flags) +{ + if (pa->hwndAccMgrUI) { + DestroyWindow(pa->hwndAccMgrUI); + pa->hwndAccMgrUI = nullptr; + pa->bAccMgrUIChanged = FALSE; + } + + if (flags & DAF_DYNAMIC) + NotifyEventHooks(hAccListChanged, PRAC_REMOVED, (LPARAM)pa); + else + pa->iIconBase = -1; + + if (pa->ppro == nullptr) { + if (flags & DAF_ERASE) + EraseAccount(pa->szModuleName); + return; + } + + DeactivationThreadParam *param = new DeactivationThreadParam; + param->ppro = pa->ppro; + param->fnUninit = GetProtocolDestructor(pa->szProtoName); + param->flags = flags; + pa->ppro = nullptr; + if (flags & DAF_FORK) + mir_forkThread(DeactivationThread, param); + else + DeactivationThread(param); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void KillModuleAccounts(HINSTANCE hInst) +{ + for (auto &pd : g_arProtos.rev_iter()) { + if (pd->hInst != hInst) + continue; + + for (auto &pa : g_arAccounts.rev_iter()) { + if (!mir_strcmp(pa->szProtoName, pd->szName)) { + pa->bDynDisabled = true; + DeactivateAccount(pa, DAF_DYNAMIC); + } + } + + g_arProtos.removeItem(&pd); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void EraseAccount(const char *pszModuleName) +{ + // remove protocol contacts first + for (MCONTACT hContact = db_find_first(pszModuleName); hContact != 0;) { + MCONTACT hNext = db_find_next(hContact, pszModuleName); + db_delete_contact(hContact); + hContact = hNext; + } + + // remove all protocol settings + db_delete_module(0, pszModuleName); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void UnloadAccount(PROTOACCOUNT *pa, int flags) +{ + DeactivateAccount(pa, flags); + + // szModuleName should be freed only on a program's exit. + // otherwise many plugins dependand on static protocol names will crash! + // do NOT fix this 'leak', please + if (!(flags & DAF_DYNAMIC)) + delete pa; + else { + replaceStrW(pa->tszAccountName, 0); + replaceStr(pa->szProtoName, 0); + replaceStr(pa->szUniqueId, 0); + } +} + +void UnloadAccountsModule() +{ + if (!bModuleInitialized) + return; + + auto T = g_arAccounts.rev_iter(); + for (auto &it : T) { + UnloadAccount(it, 0); + g_arAccounts.removeItem(&it); + } + g_arAccounts.destroy(); + + for (auto &it : hHooks) + UnhookEvent(it); +} diff --git a/src/mir_app/src/proto_chains.cpp b/src/mir_app/src/proto_chains.cpp index 0764a01164..5417c07995 100644 --- a/src/mir_app/src/proto_chains.cpp +++ b/src/mir_app/src/proto_chains.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/proto_interface.cpp b/src/mir_app/src/proto_interface.cpp index ed500cf69e..4ccab724de 100644 --- a/src/mir_app/src/proto_interface.cpp +++ b/src/mir_app/src/proto_interface.cpp @@ -1,351 +1,351 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -static HGENMENU hReqAuth = nullptr, hGrantAuth = nullptr, hRevokeAuth = nullptr, hServerHist = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol constructor & destructor - -PROTO_INTERFACE::PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName) -{ - m_iVersion = 2; - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - m_szModuleName = mir_strdup(pszModuleName); - m_tszUserName = mir_wstrdup(ptszUserName); - db_set_resident(m_szModuleName, "Status"); -} - -PROTO_INTERFACE::~PROTO_INTERFACE() -{ - if (m_hNetlibUser) - Netlib_CloseHandle(m_hNetlibUser); - - mir_free(m_szModuleName); - mir_free(m_tszUserName); - - WindowList_Destroy(m_hWindowList); -} - -HGENMENU PROTO_INTERFACE::GetMenuItem(ProtoMenuItemType aType) -{ - switch (aType) { - case PROTO_MENU_REQ_AUTH: return hReqAuth; - case PROTO_MENU_GRANT_AUTH: return hGrantAuth; - case PROTO_MENU_REVOKE_AUTH: return hRevokeAuth; - case PROTO_MENU_LOAD_HISTORY: return hServerHist; - } - - return nullptr; -} - -void PROTO_INTERFACE::OnBuildProtoMenu() -{} - -void PROTO_INTERFACE::OnContactAdded(MCONTACT) -{} - -void PROTO_INTERFACE::OnContactDeleted(MCONTACT) -{} - -void PROTO_INTERFACE::OnEventEdited(MCONTACT, MEVENT) -{} - -void PROTO_INTERFACE::OnErase() -{} - -void PROTO_INTERFACE::OnModulesLoaded() -{} - -bool PROTO_INTERFACE::IsReadyToExit() -{ - return true; -} - -void PROTO_INTERFACE::OnShutdown() -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// default PROTO_INTERFACE method implementations - -MCONTACT PROTO_INTERFACE::AddToList(int, PROTOSEARCHRESULT*) -{ - return 0; // error -} - -MCONTACT PROTO_INTERFACE::AddToListByEvent(int, int, MEVENT) -{ - return 0; // error -} - -int PROTO_INTERFACE::Authorize(MEVENT) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthDeny(MEVENT, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthRecv(MCONTACT, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::AuthRequest(MCONTACT, const wchar_t*) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::FileAllow(MCONTACT, HANDLE, const wchar_t*) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::FileCancel(MCONTACT, HANDLE) -{ - return 1; // error -} - -int PROTO_INTERFACE::FileDeny(MCONTACT, HANDLE, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::FileResume(HANDLE, int, const wchar_t*) -{ - return 1; // error -} - -INT_PTR PROTO_INTERFACE::GetCaps(int, MCONTACT) -{ - return 0; // empty value -} - -int PROTO_INTERFACE::GetInfo(MCONTACT, int) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::SearchBasic(const wchar_t*) -{ - return nullptr; // error -} - -HANDLE PROTO_INTERFACE::SearchByEmail(const wchar_t*) -{ - return nullptr; // error -} - -HANDLE PROTO_INTERFACE::SearchByName(const wchar_t*, const wchar_t*, const wchar_t*) -{ - return nullptr; // error -} - -HWND PROTO_INTERFACE::SearchAdvanced(HWND) -{ - return nullptr; // error -} - -HWND PROTO_INTERFACE::CreateExtendedSearchUI(HWND) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::RecvContacts(MCONTACT, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::RecvFile(MCONTACT hContact, PROTORECVFILE *pcre) -{ - CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)pcre }; - return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs); -} - -MEVENT PROTO_INTERFACE::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre) -{ - if (pre->szMessage == nullptr) - return 0; - - ptrA pszTemp; - mir_ptr pszBlob; - - DBEVENTINFO dbei = {}; - dbei.flags = DBEF_UTF; - dbei.szModule = Proto_GetBaseAccountName(hContact); - dbei.timestamp = pre->timestamp; - dbei.eventType = EVENTTYPE_MESSAGE; - dbei.cbBlob = (uint32_t)mir_strlen(pre->szMessage) + 1; - dbei.pBlob = (uint8_t*)pre->szMessage; - - if (pre->flags & PREF_CREATEREAD) - dbei.flags |= DBEF_READ; - if (pre->flags & PREF_SENT) - dbei.flags |= DBEF_SENT; - - // if it's possible to find an existing event by its id, do that - if ((GetCaps(PFLAGNUM_4) & PF4_SERVERMSGID) && pre->szMsgId != nullptr) { - MEVENT hDbEvent = db_event_getById(m_szModuleName, pre->szMsgId); - if (hDbEvent == 0 || db_event_edit(hContact, hDbEvent, &dbei)) { - dbei.szId = pre->szMsgId; - hDbEvent = db_event_add(hContact, &dbei); - } - return hDbEvent; - } - - // event is new? add it - return (INT_PTR)db_event_add(hContact, &dbei); -} - -int PROTO_INTERFACE::SendContacts(MCONTACT, int, int, MCONTACT*) -{ - return 1; // error -} - -HANDLE PROTO_INTERFACE::SendFile(MCONTACT, const wchar_t*, wchar_t**) -{ - return nullptr; // error -} - -int PROTO_INTERFACE::SendMsg(MCONTACT, int, const char*) -{ - return 0; // error -} - -int PROTO_INTERFACE::SetApparentMode(MCONTACT, int) -{ - return 1; // error -} - -int PROTO_INTERFACE::SetStatus(int) -{ - return 1; // you better declare it -} - -HANDLE PROTO_INTERFACE::GetAwayMsg(MCONTACT) -{ - return nullptr; // no away message -} - -int PROTO_INTERFACE::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*) -{ - return 1; // error -} - -int PROTO_INTERFACE::SetAwayMsg(int, const wchar_t*) -{ - return 1; // error -} - -int PROTO_INTERFACE::UserIsTyping(MCONTACT, int) -{ - return 1; // error -} - -///////////////////////////////////////////////////////////////////////////////////////// -// protocol menus - -static INT_PTR __cdecl stubRequestAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_REQAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubGrantAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_GRANTAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubRevokeAuth(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_REVOKEAUTH, hContact, 0); - return 0; -} - -static INT_PTR __cdecl stubLoadHistory(WPARAM hContact, LPARAM) -{ - const char *szProto = Proto_GetBaseAccountName(hContact); - if (szProto) - ProtoCallService(szProto, PS_MENU_LOADHISTORY, hContact, 0); - return 0; -} - -static int __cdecl ProtoPrebuildContactMenu(WPARAM hContact, LPARAM) -{ - Menu_ShowItem(hReqAuth, false); - Menu_ShowItem(hGrantAuth, false); - Menu_ShowItem(hRevokeAuth, false); - - const char *szProto = Proto_GetBaseAccountName(hContact); - Menu_ShowItem(hServerHist, ProtoServiceExists(szProto, PS_MENU_LOADHISTORY)); - return 0; -} - -void InitProtoMenus(void) -{ - // "Request authorization" - CMenuItem mi(&g_plugin); - SET_UID(mi, 0x36375a1f, 0xc142, 0x4d6e, 0xa6, 0x57, 0xe4, 0x76, 0x5d, 0xbc, 0x59, 0x8e); - mi.pszService = "Proto/Menu/ReqAuth"; - mi.name.a = LPGEN("Request authorization"); - mi.position = -2000001002; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST); - hReqAuth = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, stubRequestAuth); - - // "Grant authorization" - SET_UID(mi, 0x4c90452a, 0x869a, 0x4a81, 0xaf, 0xa8, 0x28, 0x34, 0xaf, 0x2b, 0x6b, 0x30); - mi.pszService = "Proto/Menu/GrantAuth"; - mi.name.a = LPGEN("Grant authorization"); - mi.position = -2000001001; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_GRANT); - hGrantAuth = Menu_AddContactMenuItem(&mi); - - // "Revoke authorization" - SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77); - mi.pszService = "Proto/Menu/RevokeAuth"; - mi.name.a = LPGEN("Revoke authorization"); - mi.position = -2000001000; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE); - hRevokeAuth = Menu_AddContactMenuItem(&mi); - - SET_UID(mi, 0xd15b841d, 0xb0fc, 0x4ab5, 0x96, 0x94, 0xcf, 0x6c, 0x6e, 0x99, 0x4b, 0x3c); // {D15B841D-B0FC-4AB5-9694-CF6C6E994B3C} - mi.pszService = "Proto/Menu/LoadHistory"; - mi.name.a = LPGEN("Load server history"); - mi.position = -200001004; - mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HISTORY); - hServerHist = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, stubLoadHistory); - - HookEvent(ME_CLIST_PREBUILDCONTACTMENU, ProtoPrebuildContactMenu); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static HGENMENU hReqAuth = nullptr, hGrantAuth = nullptr, hRevokeAuth = nullptr, hServerHist = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// +// protocol constructor & destructor + +PROTO_INTERFACE::PROTO_INTERFACE(const char *pszModuleName, const wchar_t *ptszUserName) +{ + m_iVersion = 2; + m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; + m_szModuleName = mir_strdup(pszModuleName); + m_tszUserName = mir_wstrdup(ptszUserName); + db_set_resident(m_szModuleName, "Status"); +} + +PROTO_INTERFACE::~PROTO_INTERFACE() +{ + if (m_hNetlibUser) + Netlib_CloseHandle(m_hNetlibUser); + + mir_free(m_szModuleName); + mir_free(m_tszUserName); + + WindowList_Destroy(m_hWindowList); +} + +HGENMENU PROTO_INTERFACE::GetMenuItem(ProtoMenuItemType aType) +{ + switch (aType) { + case PROTO_MENU_REQ_AUTH: return hReqAuth; + case PROTO_MENU_GRANT_AUTH: return hGrantAuth; + case PROTO_MENU_REVOKE_AUTH: return hRevokeAuth; + case PROTO_MENU_LOAD_HISTORY: return hServerHist; + } + + return nullptr; +} + +void PROTO_INTERFACE::OnBuildProtoMenu() +{} + +void PROTO_INTERFACE::OnContactAdded(MCONTACT) +{} + +void PROTO_INTERFACE::OnContactDeleted(MCONTACT) +{} + +void PROTO_INTERFACE::OnEventEdited(MCONTACT, MEVENT) +{} + +void PROTO_INTERFACE::OnErase() +{} + +void PROTO_INTERFACE::OnModulesLoaded() +{} + +bool PROTO_INTERFACE::IsReadyToExit() +{ + return true; +} + +void PROTO_INTERFACE::OnShutdown() +{} + +///////////////////////////////////////////////////////////////////////////////////////// +// default PROTO_INTERFACE method implementations + +MCONTACT PROTO_INTERFACE::AddToList(int, PROTOSEARCHRESULT*) +{ + return 0; // error +} + +MCONTACT PROTO_INTERFACE::AddToListByEvent(int, int, MEVENT) +{ + return 0; // error +} + +int PROTO_INTERFACE::Authorize(MEVENT) +{ + return 1; // error +} + +int PROTO_INTERFACE::AuthDeny(MEVENT, const wchar_t*) +{ + return 1; // error +} + +int PROTO_INTERFACE::AuthRecv(MCONTACT, PROTORECVEVENT*) +{ + return 1; // error +} + +int PROTO_INTERFACE::AuthRequest(MCONTACT, const wchar_t*) +{ + return 1; // error +} + +HANDLE PROTO_INTERFACE::FileAllow(MCONTACT, HANDLE, const wchar_t*) +{ + return nullptr; // error +} + +int PROTO_INTERFACE::FileCancel(MCONTACT, HANDLE) +{ + return 1; // error +} + +int PROTO_INTERFACE::FileDeny(MCONTACT, HANDLE, const wchar_t*) +{ + return 1; // error +} + +int PROTO_INTERFACE::FileResume(HANDLE, int, const wchar_t*) +{ + return 1; // error +} + +INT_PTR PROTO_INTERFACE::GetCaps(int, MCONTACT) +{ + return 0; // empty value +} + +int PROTO_INTERFACE::GetInfo(MCONTACT, int) +{ + return 1; // error +} + +HANDLE PROTO_INTERFACE::SearchBasic(const wchar_t*) +{ + return nullptr; // error +} + +HANDLE PROTO_INTERFACE::SearchByEmail(const wchar_t*) +{ + return nullptr; // error +} + +HANDLE PROTO_INTERFACE::SearchByName(const wchar_t*, const wchar_t*, const wchar_t*) +{ + return nullptr; // error +} + +HWND PROTO_INTERFACE::SearchAdvanced(HWND) +{ + return nullptr; // error +} + +HWND PROTO_INTERFACE::CreateExtendedSearchUI(HWND) +{ + return nullptr; // error +} + +int PROTO_INTERFACE::RecvContacts(MCONTACT, PROTORECVEVENT*) +{ + return 1; // error +} + +int PROTO_INTERFACE::RecvFile(MCONTACT hContact, PROTORECVFILE *pcre) +{ + CCSDATA ccs = { hContact, PSR_FILE, 0, (LPARAM)pcre }; + return CallService(MS_PROTO_RECVFILET, 0, (LPARAM)&ccs); +} + +MEVENT PROTO_INTERFACE::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre) +{ + if (pre->szMessage == nullptr) + return 0; + + ptrA pszTemp; + mir_ptr pszBlob; + + DBEVENTINFO dbei = {}; + dbei.flags = DBEF_UTF; + dbei.szModule = Proto_GetBaseAccountName(hContact); + dbei.timestamp = pre->timestamp; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = (uint32_t)mir_strlen(pre->szMessage) + 1; + dbei.pBlob = (uint8_t*)pre->szMessage; + + if (pre->flags & PREF_CREATEREAD) + dbei.flags |= DBEF_READ; + if (pre->flags & PREF_SENT) + dbei.flags |= DBEF_SENT; + + // if it's possible to find an existing event by its id, do that + if ((GetCaps(PFLAGNUM_4) & PF4_SERVERMSGID) && pre->szMsgId != nullptr) { + MEVENT hDbEvent = db_event_getById(m_szModuleName, pre->szMsgId); + if (hDbEvent == 0 || db_event_edit(hContact, hDbEvent, &dbei)) { + dbei.szId = pre->szMsgId; + hDbEvent = db_event_add(hContact, &dbei); + } + return hDbEvent; + } + + // event is new? add it + return (INT_PTR)db_event_add(hContact, &dbei); +} + +int PROTO_INTERFACE::SendContacts(MCONTACT, int, int, MCONTACT*) +{ + return 1; // error +} + +HANDLE PROTO_INTERFACE::SendFile(MCONTACT, const wchar_t*, wchar_t**) +{ + return nullptr; // error +} + +int PROTO_INTERFACE::SendMsg(MCONTACT, int, const char*) +{ + return 0; // error +} + +int PROTO_INTERFACE::SetApparentMode(MCONTACT, int) +{ + return 1; // error +} + +int PROTO_INTERFACE::SetStatus(int) +{ + return 1; // you better declare it +} + +HANDLE PROTO_INTERFACE::GetAwayMsg(MCONTACT) +{ + return nullptr; // no away message +} + +int PROTO_INTERFACE::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*) +{ + return 1; // error +} + +int PROTO_INTERFACE::SetAwayMsg(int, const wchar_t*) +{ + return 1; // error +} + +int PROTO_INTERFACE::UserIsTyping(MCONTACT, int) +{ + return 1; // error +} + +///////////////////////////////////////////////////////////////////////////////////////// +// protocol menus + +static INT_PTR __cdecl stubRequestAuth(WPARAM hContact, LPARAM) +{ + const char *szProto = Proto_GetBaseAccountName(hContact); + if (szProto) + ProtoCallService(szProto, PS_MENU_REQAUTH, hContact, 0); + return 0; +} + +static INT_PTR __cdecl stubGrantAuth(WPARAM hContact, LPARAM) +{ + const char *szProto = Proto_GetBaseAccountName(hContact); + if (szProto) + ProtoCallService(szProto, PS_MENU_GRANTAUTH, hContact, 0); + return 0; +} + +static INT_PTR __cdecl stubRevokeAuth(WPARAM hContact, LPARAM) +{ + const char *szProto = Proto_GetBaseAccountName(hContact); + if (szProto) + ProtoCallService(szProto, PS_MENU_REVOKEAUTH, hContact, 0); + return 0; +} + +static INT_PTR __cdecl stubLoadHistory(WPARAM hContact, LPARAM) +{ + const char *szProto = Proto_GetBaseAccountName(hContact); + if (szProto) + ProtoCallService(szProto, PS_MENU_LOADHISTORY, hContact, 0); + return 0; +} + +static int __cdecl ProtoPrebuildContactMenu(WPARAM hContact, LPARAM) +{ + Menu_ShowItem(hReqAuth, false); + Menu_ShowItem(hGrantAuth, false); + Menu_ShowItem(hRevokeAuth, false); + + const char *szProto = Proto_GetBaseAccountName(hContact); + Menu_ShowItem(hServerHist, ProtoServiceExists(szProto, PS_MENU_LOADHISTORY)); + return 0; +} + +void InitProtoMenus(void) +{ + // "Request authorization" + CMenuItem mi(&g_plugin); + SET_UID(mi, 0x36375a1f, 0xc142, 0x4d6e, 0xa6, 0x57, 0xe4, 0x76, 0x5d, 0xbc, 0x59, 0x8e); + mi.pszService = "Proto/Menu/ReqAuth"; + mi.name.a = LPGEN("Request authorization"); + mi.position = -2000001002; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST); + hReqAuth = Menu_AddContactMenuItem(&mi); + CreateServiceFunction(mi.pszService, stubRequestAuth); + + // "Grant authorization" + SET_UID(mi, 0x4c90452a, 0x869a, 0x4a81, 0xaf, 0xa8, 0x28, 0x34, 0xaf, 0x2b, 0x6b, 0x30); + mi.pszService = "Proto/Menu/GrantAuth"; + mi.name.a = LPGEN("Grant authorization"); + mi.position = -2000001001; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_GRANT); + hGrantAuth = Menu_AddContactMenuItem(&mi); + + // "Revoke authorization" + SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77); + mi.pszService = "Proto/Menu/RevokeAuth"; + mi.name.a = LPGEN("Revoke authorization"); + mi.position = -2000001000; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE); + hRevokeAuth = Menu_AddContactMenuItem(&mi); + + SET_UID(mi, 0xd15b841d, 0xb0fc, 0x4ab5, 0x96, 0x94, 0xcf, 0x6c, 0x6e, 0x99, 0x4b, 0x3c); // {D15B841D-B0FC-4AB5-9694-CF6C6E994B3C} + mi.pszService = "Proto/Menu/LoadHistory"; + mi.name.a = LPGEN("Load server history"); + mi.position = -200001004; + mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HISTORY); + hServerHist = Menu_AddContactMenuItem(&mi); + CreateServiceFunction(mi.pszService, stubLoadHistory); + + HookEvent(ME_CLIST_PREBUILDCONTACTMENU, ProtoPrebuildContactMenu); +} diff --git a/src/mir_app/src/proto_internal.cpp b/src/mir_app/src/proto_internal.cpp index b7a74d8d4d..0b40f9cee1 100644 --- a/src/mir_app/src/proto_internal.cpp +++ b/src/mir_app/src/proto_internal.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/proto_opts.cpp b/src/mir_app/src/proto_opts.cpp index c25a6c937b..c2dfe73e72 100644 --- a/src/mir_app/src/proto_opts.cpp +++ b/src/mir_app/src/proto_opts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/proto_order.cpp b/src/mir_app/src/proto_order.cpp index 9096b0cb61..3024e62b0e 100644 --- a/src/mir_app/src/proto_order.cpp +++ b/src/mir_app/src/proto_order.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/proto_ui.cpp b/src/mir_app/src/proto_ui.cpp index 3ef0a00233..13319b83a1 100644 --- a/src/mir_app/src/proto_ui.cpp +++ b/src/mir_app/src/proto_ui.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/proto_utils.cpp b/src/mir_app/src/proto_utils.cpp index 79bd7012fa..281d76719e 100644 --- a/src/mir_app/src/proto_utils.cpp +++ b/src/mir_app/src/proto_utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/protocols.cpp b/src/mir_app/src/protocols.cpp index 12fe246fb1..f3a1d60f5c 100644 --- a/src/mir_app/src/protocols.cpp +++ b/src/mir_app/src/protocols.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/pu_utils.cpp b/src/mir_app/src/pu_utils.cpp index 9495031fe7..1ed944ea3a 100644 --- a/src/mir_app/src/pu_utils.cpp +++ b/src/mir_app/src/pu_utils.cpp @@ -1,331 +1,331 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// pu_stub.exe interface - -#include "stdafx.h" - -static HANDLE g_hPipe = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// are we running with admin priviledges? - -static bool IsRunAsAdmin() -{ - BOOL bIsRunAsAdmin = false; - uint32_t dwError = ERROR_SUCCESS; - PSID pAdministratorsGroup = nullptr; - - // Allocate and initialize a SID of the administrators group. - SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) { - dwError = GetLastError(); - goto Cleanup; - } - - // Determine whether the SID of administrators group is bEnabled in - // the primary access token of the process. - if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) { - dwError = GetLastError(); - goto Cleanup; - } - -Cleanup: - // Centralized cleanup for all allocated resources. - if (pAdministratorsGroup) { - FreeSid(pAdministratorsGroup); - pAdministratorsGroup = nullptr; - } - - // Throw the error if something failed in the function. - if (ERROR_SUCCESS != dwError) { - throw dwError; - } - - return bIsRunAsAdmin != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if we're working via pu_stub or not - -MIR_APP_DLL(bool) PU::IsDirect() -{ - return g_hPipe == nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if Miranda's folder is writeable - -MIR_APP_DLL(bool) PU::IsMirandaFolderWritable() -{ - if (!IsWinVerVistaPlus()) - return true; - - wchar_t wszPath[MAX_PATH]; - GetModuleFileNameW(nullptr, wszPath, _countof(wszPath)); - wchar_t *ext = wcsrchr(wszPath, '.'); - if (ext != nullptr) - *ext = '\0'; - wcscat(wszPath, L".test"); - HANDLE hFile = CreateFileW(wszPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile == INVALID_HANDLE_VALUE) - return false; - - CloseHandle(hFile); - DeleteFileW(wszPath); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Checks if a process has enough rights to write into Miranda's folder - -MIR_APP_DLL(bool) PU::IsProcessElevated() -{ - bool bIsElevated = false; - HANDLE hToken = nullptr; - - // Open the primary access token of the process with TOKEN_QUERY. - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) - goto Cleanup; - - // Retrieve token elevation information. - TOKEN_ELEVATION elevation; - DWORD dwSize; - if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { - // When the process is run on operating systems prior to Windows - // Vista, GetTokenInformation returns FALSE with the - // ERROR_INVALID_PARAMETER error code because TokenElevation is - // not supported on those operating systems. - goto Cleanup; - } - - bIsElevated = elevation.TokenIsElevated != 0; - -Cleanup: - // Centralized cleanup for all allocated resources. - if (hToken) - CloseHandle(hToken); - - return bIsElevated; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Launches pu_stub.exe with elevated priviledges if needed - -MIR_APP_DLL(bool) PU::PrepareEscalation(const wchar_t *pwszFile) -{ - CMStringW wszFilePath; - // First try to create a file near Miranda32.exe - if (pwszFile == nullptr) { - wchar_t szPath[MAX_PATH]; - GetModuleFileName(nullptr, szPath, _countof(szPath)); - wchar_t *ext = wcsrchr(szPath, '.'); - if (ext != nullptr) - *ext = '\0'; - wszFilePath = szPath; - } - else wszFilePath = pwszFile; - - wszFilePath.Append(L".test"); - - HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile != INVALID_HANDLE_VALUE) { - // we are admins or UAC is disable, cool - CloseHandle(hFile); - DeleteFileW(wszFilePath); - return true; - } - - // Check the current process's "run as administrator" status. - if (IsRunAsAdmin()) - return true; - - // if pipe already opened? - if (g_hPipe != nullptr) - return true; - - // Elevate the process. Create a pipe for a stub first - wchar_t wzPipeName[MAX_PATH]; - mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId()); - g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr); - if (g_hPipe == INVALID_HANDLE_VALUE) { - g_hPipe = nullptr; - } - else { - wchar_t cmdLine[100], *p; - wchar_t szPath[MAX_PATH]; - GetModuleFileName(nullptr, szPath, _countof(szPath)); - if ((p = wcsrchr(szPath, '\\')) != nullptr) - wcscpy(p + 1, L"pu_stub.exe"); - mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId()); - - // Launch a stub - SHELLEXECUTEINFO sei = { sizeof(sei) }; - sei.lpVerb = L"runas"; - sei.lpFile = szPath; - sei.lpParameters = cmdLine; - sei.hwnd = nullptr; - sei.nShow = SW_NORMAL; - if (ShellExecuteEx(&sei)) { - if (g_hPipe != nullptr) - ConnectNamedPipe(g_hPipe, nullptr); - return true; - } - - uint32_t dwError = GetLastError(); - if (dwError == ERROR_CANCELLED) { - // The user refused to allow privileges elevation. - // Do nothing ... - } - } - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2) -{ - uint8_t buf[1024]; - uint32_t l1 = lstrlen(p1), l2 = lstrlen(p2); - if (l1 > MAX_PATH || l2 > MAX_PATH) - return ERROR_BAD_ARGUMENTS; - - *(uint32_t *)buf = opcode; - wchar_t *dst = (wchar_t *)&buf[sizeof(uint32_t)]; - lstrcpy(dst, p1); - dst += l1 + 1; - if (p2) { - lstrcpy(dst, p2); - dst += l2 + 1; - } - else *dst++ = 0; - - DWORD dwBytes = 0, dwError; - if (!WriteFile(g_hPipe, buf, (uint32_t)((uint8_t *)dst - buf), &dwBytes, nullptr)) - return GetLastError(); - - dwError = 0; - if (!ReadFile(g_hPipe, &dwError, sizeof(uint32_t), &dwBytes, nullptr)) - return GetLastError(); - if (dwBytes != sizeof(uint32_t)) - return ERROR_BAD_ARGUMENTS; - - return dwError; -} - -MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst) -{ - if (g_hPipe == nullptr) - return CopyFileW(pSrc, pDst, FALSE); - - return TransactPipe(1, pSrc, pDst); -} - -MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst) -{ - if (g_hPipe == nullptr) { - if (!DeleteFileW(pDst)) { - uint32_t dwError = GetLastError(); - if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND) - return dwError; - } - - if (!MoveFileW(pSrc, pDst)) { // use copy on error - switch (uint32_t dwError = GetLastError()) { - case ERROR_ALREADY_EXISTS: - case ERROR_FILE_NOT_FOUND: - return 0; // this file was included into many archives, so Miranda tries to move it again & again - - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_LOCK_VIOLATION: - // use copy routine if a move operation isn't available - // for example, when files are on different disks - if (!CopyFileW(pSrc, pDst, FALSE)) - return GetLastError(); - - if (!DeleteFileW(pSrc)) - return GetLastError(); - break; - - default: - return dwError; - } - } - - return ERROR_SUCCESS; - } - - return TransactPipe(2, pSrc, pDst); -} - -MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile) -{ - if (g_hPipe == nullptr) - return DeleteFileW(pwszFile); - - return TransactPipe(3, pwszFile, nullptr); -} - -MIR_APP_DLL(int) PU::SafeRecycleBin(const wchar_t *pwszFile) -{ - if (g_hPipe == nullptr) { - CMStringW tmpPath(pwszFile); - tmpPath.AppendChar(0); - - SHFILEOPSTRUCT shfo = {}; - shfo.wFunc = FO_DELETE; - shfo.pFrom = tmpPath; - shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO; - return SHFileOperation(&shfo); - } - - return TransactPipe(7, pwszFile, nullptr); -} - -MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder) -{ - if (g_hPipe == nullptr) - return CreateDirectoryTreeW(pwszFolder); - - return TransactPipe(4, pwszFolder, nullptr); -} - -MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName) -{ - if (g_hPipe == nullptr) - return DeleteDirectoryTreeW(pwszDirName); - - return TransactPipe(6, pwszDirName, nullptr); -} - -MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder) -{ - if (g_hPipe == nullptr) { - CreatePathToFileW(pwszFolder); - return 0; - } - - return TransactPipe(5, pwszFolder, nullptr); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// pu_stub.exe interface + +#include "stdafx.h" + +static HANDLE g_hPipe = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// +// are we running with admin priviledges? + +static bool IsRunAsAdmin() +{ + BOOL bIsRunAsAdmin = false; + uint32_t dwError = ERROR_SUCCESS; + PSID pAdministratorsGroup = nullptr; + + // Allocate and initialize a SID of the administrators group. + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdministratorsGroup)) { + dwError = GetLastError(); + goto Cleanup; + } + + // Determine whether the SID of administrators group is bEnabled in + // the primary access token of the process. + if (!CheckTokenMembership(nullptr, pAdministratorsGroup, &bIsRunAsAdmin)) { + dwError = GetLastError(); + goto Cleanup; + } + +Cleanup: + // Centralized cleanup for all allocated resources. + if (pAdministratorsGroup) { + FreeSid(pAdministratorsGroup); + pAdministratorsGroup = nullptr; + } + + // Throw the error if something failed in the function. + if (ERROR_SUCCESS != dwError) { + throw dwError; + } + + return bIsRunAsAdmin != 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Checks if we're working via pu_stub or not + +MIR_APP_DLL(bool) PU::IsDirect() +{ + return g_hPipe == nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Checks if Miranda's folder is writeable + +MIR_APP_DLL(bool) PU::IsMirandaFolderWritable() +{ + if (!IsWinVerVistaPlus()) + return true; + + wchar_t wszPath[MAX_PATH]; + GetModuleFileNameW(nullptr, wszPath, _countof(wszPath)); + wchar_t *ext = wcsrchr(wszPath, '.'); + if (ext != nullptr) + *ext = '\0'; + wcscat(wszPath, L".test"); + HANDLE hFile = CreateFileW(wszPath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + return false; + + CloseHandle(hFile); + DeleteFileW(wszPath); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Checks if a process has enough rights to write into Miranda's folder + +MIR_APP_DLL(bool) PU::IsProcessElevated() +{ + bool bIsElevated = false; + HANDLE hToken = nullptr; + + // Open the primary access token of the process with TOKEN_QUERY. + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) + goto Cleanup; + + // Retrieve token elevation information. + TOKEN_ELEVATION elevation; + DWORD dwSize; + if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { + // When the process is run on operating systems prior to Windows + // Vista, GetTokenInformation returns FALSE with the + // ERROR_INVALID_PARAMETER error code because TokenElevation is + // not supported on those operating systems. + goto Cleanup; + } + + bIsElevated = elevation.TokenIsElevated != 0; + +Cleanup: + // Centralized cleanup for all allocated resources. + if (hToken) + CloseHandle(hToken); + + return bIsElevated; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Launches pu_stub.exe with elevated priviledges if needed + +MIR_APP_DLL(bool) PU::PrepareEscalation(const wchar_t *pwszFile) +{ + CMStringW wszFilePath; + // First try to create a file near Miranda32.exe + if (pwszFile == nullptr) { + wchar_t szPath[MAX_PATH]; + GetModuleFileName(nullptr, szPath, _countof(szPath)); + wchar_t *ext = wcsrchr(szPath, '.'); + if (ext != nullptr) + *ext = '\0'; + wszFilePath = szPath; + } + else wszFilePath = pwszFile; + + wszFilePath.Append(L".test"); + + HANDLE hFile = CreateFileW(wszFilePath, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile != INVALID_HANDLE_VALUE) { + // we are admins or UAC is disable, cool + CloseHandle(hFile); + DeleteFileW(wszFilePath); + return true; + } + + // Check the current process's "run as administrator" status. + if (IsRunAsAdmin()) + return true; + + // if pipe already opened? + if (g_hPipe != nullptr) + return true; + + // Elevate the process. Create a pipe for a stub first + wchar_t wzPipeName[MAX_PATH]; + mir_snwprintf(wzPipeName, L"\\\\.\\pipe\\Miranda_Pu_%d", GetCurrentProcessId()); + g_hPipe = CreateNamedPipe(wzPipeName, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, nullptr); + if (g_hPipe == INVALID_HANDLE_VALUE) { + g_hPipe = nullptr; + } + else { + wchar_t cmdLine[100], *p; + wchar_t szPath[MAX_PATH]; + GetModuleFileName(nullptr, szPath, _countof(szPath)); + if ((p = wcsrchr(szPath, '\\')) != nullptr) + wcscpy(p + 1, L"pu_stub.exe"); + mir_snwprintf(cmdLine, L"%d", GetCurrentProcessId()); + + // Launch a stub + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.lpVerb = L"runas"; + sei.lpFile = szPath; + sei.lpParameters = cmdLine; + sei.hwnd = nullptr; + sei.nShow = SW_NORMAL; + if (ShellExecuteEx(&sei)) { + if (g_hPipe != nullptr) + ConnectNamedPipe(g_hPipe, nullptr); + return true; + } + + uint32_t dwError = GetLastError(); + if (dwError == ERROR_CANCELLED) { + // The user refused to allow privileges elevation. + // Do nothing ... + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int TransactPipe(int opcode, const wchar_t *p1, const wchar_t *p2) +{ + uint8_t buf[1024]; + uint32_t l1 = lstrlen(p1), l2 = lstrlen(p2); + if (l1 > MAX_PATH || l2 > MAX_PATH) + return ERROR_BAD_ARGUMENTS; + + *(uint32_t *)buf = opcode; + wchar_t *dst = (wchar_t *)&buf[sizeof(uint32_t)]; + lstrcpy(dst, p1); + dst += l1 + 1; + if (p2) { + lstrcpy(dst, p2); + dst += l2 + 1; + } + else *dst++ = 0; + + DWORD dwBytes = 0, dwError; + if (!WriteFile(g_hPipe, buf, (uint32_t)((uint8_t *)dst - buf), &dwBytes, nullptr)) + return GetLastError(); + + dwError = 0; + if (!ReadFile(g_hPipe, &dwError, sizeof(uint32_t), &dwBytes, nullptr)) + return GetLastError(); + if (dwBytes != sizeof(uint32_t)) + return ERROR_BAD_ARGUMENTS; + + return dwError; +} + +MIR_APP_DLL(int) PU::SafeCopyFile(const wchar_t *pSrc, const wchar_t *pDst) +{ + if (g_hPipe == nullptr) + return CopyFileW(pSrc, pDst, FALSE); + + return TransactPipe(1, pSrc, pDst); +} + +MIR_APP_DLL(int) PU::SafeMoveFile(const wchar_t *pSrc, const wchar_t *pDst) +{ + if (g_hPipe == nullptr) { + if (!DeleteFileW(pDst)) { + uint32_t dwError = GetLastError(); + if (dwError != ERROR_ACCESS_DENIED && dwError != ERROR_FILE_NOT_FOUND) + return dwError; + } + + if (!MoveFileW(pSrc, pDst)) { // use copy on error + switch (uint32_t dwError = GetLastError()) { + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_NOT_FOUND: + return 0; // this file was included into many archives, so Miranda tries to move it again & again + + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + // use copy routine if a move operation isn't available + // for example, when files are on different disks + if (!CopyFileW(pSrc, pDst, FALSE)) + return GetLastError(); + + if (!DeleteFileW(pSrc)) + return GetLastError(); + break; + + default: + return dwError; + } + } + + return ERROR_SUCCESS; + } + + return TransactPipe(2, pSrc, pDst); +} + +MIR_APP_DLL(int) PU::SafeDeleteFile(const wchar_t *pwszFile) +{ + if (g_hPipe == nullptr) + return DeleteFileW(pwszFile); + + return TransactPipe(3, pwszFile, nullptr); +} + +MIR_APP_DLL(int) PU::SafeRecycleBin(const wchar_t *pwszFile) +{ + if (g_hPipe == nullptr) { + CMStringW tmpPath(pwszFile); + tmpPath.AppendChar(0); + + SHFILEOPSTRUCT shfo = {}; + shfo.wFunc = FO_DELETE; + shfo.pFrom = tmpPath; + shfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO; + return SHFileOperation(&shfo); + } + + return TransactPipe(7, pwszFile, nullptr); +} + +MIR_APP_DLL(int) PU::SafeCreateDirectory(const wchar_t *pwszFolder) +{ + if (g_hPipe == nullptr) + return CreateDirectoryTreeW(pwszFolder); + + return TransactPipe(4, pwszFolder, nullptr); +} + +MIR_APP_DLL(int) PU::SafeDeleteDirectory(const wchar_t *pwszDirName) +{ + if (g_hPipe == nullptr) + return DeleteDirectoryTreeW(pwszDirName); + + return TransactPipe(6, pwszDirName, nullptr); +} + +MIR_APP_DLL(int) PU::SafeCreateFilePath(const wchar_t *pwszFolder) +{ + if (g_hPipe == nullptr) { + CreatePathToFileW(pwszFolder); + return 0; + } + + return TransactPipe(5, pwszFolder, nullptr); +} diff --git a/src/mir_app/src/searchresults.cpp b/src/mir_app/src/searchresults.cpp index 2a298d6598..892ec2c7dd 100644 --- a/src/mir_app/src/searchresults.cpp +++ b/src/mir_app/src/searchresults.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/skin.h b/src/mir_app/src/skin.h index d0a76adfc0..e6225825a8 100644 --- a/src/mir_app/src/skin.h +++ b/src/mir_app/src/skin.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/skin2opts.cpp b/src/mir_app/src/skin2opts.cpp index 5d1cd2e072..a42db0b245 100644 --- a/src/mir_app/src/skin2opts.cpp +++ b/src/mir_app/src/skin2opts.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/skinicons.cpp b/src/mir_app/src/skinicons.cpp index 927f5e798f..f6ce8e8b5d 100644 --- a/src/mir_app/src/skinicons.cpp +++ b/src/mir_app/src/skinicons.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/sounds.cpp b/src/mir_app/src/sounds.cpp index 8da80075df..8762adb26b 100644 --- a/src/mir_app/src/sounds.cpp +++ b/src/mir_app/src/sounds.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/srmm_base.cpp b/src/mir_app/src/srmm_base.cpp index 85a9308301..fd1d564a8a 100644 --- a/src/mir_app/src/srmm_base.cpp +++ b/src/mir_app/src/srmm_base.cpp @@ -1,923 +1,923 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#include "chat.h" -#include "resource.h" -#include "skin.h" -#include - -CSrmmBaseDialog::CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, SESSION_INFO *si) : - CDlgBase(pPlugin, idDialog), - timerFlash(this, 1), - timerType(this, 2), - - m_message(this, IDC_SRMM_MESSAGE), - m_nickList(this, IDC_SRMM_NICKLIST), - - m_btnOk(this, IDOK), - m_btnFilter(this, IDC_SRMM_FILTER), - m_btnHistory(this, IDC_SRMM_HISTORY), - m_btnNickList(this, IDC_SRMM_SHOWNICKLIST), - m_btnChannelMgr(this, IDC_SRMM_CHANMGR), - - m_btnColor(this, IDC_SRMM_COLOR), - m_btnBkColor(this, IDC_SRMM_BKGCOLOR), - m_btnBold(this, IDC_SRMM_BOLD), - - m_btnItalic(this, IDC_SRMM_ITALICS), - m_btnUnderline(this, IDC_SRMM_UNDERLINE), - - m_si(si), - m_hContact(0), - m_clrInputBG(GetSysColor(COLOR_WINDOW)) -{ - m_bFilterEnabled = db_get_b(0, CHAT_MODULE, "FilterEnabled", 0) != 0; - m_bNicklistEnabled = db_get_b(0, CHAT_MODULE, "ShowNicklist", 1) != 0; - m_iLogFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", 0x03E0); - - m_btnColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_Color); - m_btnBkColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BkColor); - m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BIU); - - m_btnHistory.OnClick = Callback(this, &CSrmmBaseDialog::onClick_History); - m_btnChannelMgr.OnClick = Callback(this, &CSrmmBaseDialog::onClick_ChanMgr); - - m_nickList.OnDblClick = Callback(this, &CMsgDialog::onDblClick_List); - - if (si) { - m_hContact = si->hContact; - - if (si->pMI->bColor) { - m_iFG = 4; - m_bFGSet = true; - } - if (si->pMI->bBkgColor) { - m_iBG = 2; - m_bBGSet = true; - } - } -} - -void CSrmmBaseDialog::RunUserMenu(HWND hwndOwner, USERINFO *ui, const POINT &pt) -{ - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_USERMENU)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - - USERINFO uinew; - memcpy(&uinew, ui, sizeof(USERINFO)); - - wchar_t szTemp[50]; - if (uinew.pszNick) - mir_snwprintf(szTemp, TranslateT("&Message %s"), uinew.pszNick); - else - mir_wstrncpy(szTemp, TranslateT("&Message"), _countof(szTemp) - 1); - - if (mir_wstrlen(szTemp) > 40) - mir_wstrncpy(szTemp + 40, L"...", 4); - ModifyMenu(hMenu, 0, MF_STRING | MF_BYPOSITION, IDM_SENDMESSAGE, szTemp); - - UINT uID = Chat_CreateMenu(hwndOwner, hSubMenu, pt, m_si, uinew.pszUID); - switch (uID) { - case 0: - break; - - case IDM_SENDMESSAGE: - Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); - break; - - default: - Chat_DoEventHook(m_si, GC_USER_NICKLISTMENU, ui, nullptr, uID); - break; - } - DestroyMenu(hMenu); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK Srmm_ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_RBUTTONUP: - if (g_chatApi.bRightClickFilter) { - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg == nullptr) - break; - - switch (GetDlgCtrlID(hwnd)) { - case IDC_SRMM_FILTER: - pDlg->ShowFilterMenu(); - break; - - case IDC_SRMM_COLOR: - pDlg->ShowColorChooser(IDC_SRMM_COLOR); - break; - - case IDC_SRMM_BKGCOLOR: - pDlg->ShowColorChooser(IDC_SRMM_BKGCOLOR); - break; - } - } - break; - } - - return mir_callNextSubclass(hwnd, Srmm_ButtonSubclassProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg != nullptr) - return pDlg->WndProc_Message(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubMessageProc, msg, wParam, lParam); -} - -LRESULT CSrmmBaseDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_SETCURSOR: - if (m_bInMenu) { - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - return TRUE; - } - break; - - case WM_CHAR: - switch (wParam) { - case 0x02: // ctrl+B - if (m_btnBold.Enabled()) - return 1; - break; - case 0x09: // ctrl+I - if (m_btnItalic.Enabled()) - return 1; - break; - case 0x15: // ctrl+U - if (m_btnUnderline.Enabled()) - return 1; - break; - } - break; - - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - if (wParam == VK_BACK) - if (m_message.GetRichTextLength() == 0) - return 1; - - MSG tmp = { m_hwnd, msg, wParam, lParam }; - if (Hotkey_Check(&tmp, g_pszHotkeySection) == 100) { - if (!(GetWindowLongPtr(m_message.GetHwnd(), GWL_STYLE) & ES_READONLY)) { - PostMessage(m_hwnd, WM_COMMAND, IDOK, 0); - return true; - } - } - } - - LRESULT res = mir_callNextSubclass(m_message.GetHwnd(), stubMessageProc, msg, wParam, lParam); - switch (msg) { - case WM_GETDLGCODE: - return res & ~DLGC_HASSETSEL; - - case WM_KEYUP: - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - RefreshButtonStatus(); - break; - - case WM_KEYDOWN: - if ((GetKeyState(VK_CONTROL) & 0x8000) && wParam == 'V' || (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_INSERT) { - if (IsClipboardFormatAvailable(CF_HDROP)) { - m_message.SendMsg(WM_PASTE, 0, 0); - return 0; - } - } - - __fallthrough; - - case WM_SYSKEYDOWN: - if (!(GetKeyState(VK_RMENU) & 0x8000)) { - MSG message = { m_hwnd, msg, wParam, lParam }; - LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); - if (iButtonFrom) { - Srmm_ProcessToolbarHotkey(m_hContact, iButtonFrom, m_hwnd); - return TRUE; - } - } - break; - } - - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// process mouse - hovering for the nickname list.fires events so the protocol can -// show the userinfo - tooltip. - -static void ProcessNickListHovering(HWND hwnd, int hoveredItem, SESSION_INFO *parentdat) -{ - static int currentHovered = -1; - static HWND hwndToolTip = nullptr; - static HWND oldParent = nullptr; - - if (hoveredItem == currentHovered) - return; - - currentHovered = hoveredItem; - - if (oldParent != hwnd && hwndToolTip) { - SendMessage(hwndToolTip, TTM_DELTOOL, 0, 0); - DestroyWindow(hwndToolTip); - hwndToolTip = nullptr; - } - - if (hoveredItem == -1) { - SendMessage(hwndToolTip, TTM_ACTIVATE, 0, 0); - return; - } - - bool bNewTip = false; - if (!hwndToolTip) { - bNewTip = true; - hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr, - WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - hwnd, nullptr, g_plugin.getInst(), nullptr); - } - - RECT clientRect; - GetClientRect(hwnd, &clientRect); - - TOOLINFO ti = { sizeof(ti) }; - ti.uFlags = TTF_SUBCLASS; - ti.hinst = g_plugin.getInst(); - ti.hwnd = hwnd; - ti.uId = 1; - ti.rect = clientRect; - - CMStringW wszBuf; - - USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(parentdat->ptszID, parentdat->pszModule, currentHovered); - if (ui1) { - if (ProtoServiceExists(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { - wchar_t *p = (wchar_t*)CallProtoService(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)parentdat->ptszID, (LPARAM)ui1->pszUID); - if (p != nullptr) { - wszBuf = p; - mir_free(p); - } - } - - if (wszBuf.IsEmpty()) - wszBuf.Format(L"%s: %s\r\n%s: %s\r\n%s: %s", - TranslateT("Nickname"), ui1->pszNick, - TranslateT("Unique ID"), ui1->pszUID, - TranslateT("Status"), g_chatApi.TM_WordToString(parentdat->pStatuses, ui1->Status)); - ti.lpszText = wszBuf.GetBuffer(); - } - - SendMessage(hwndToolTip, bNewTip ? TTM_ADDTOOL : TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); - SendMessage(hwndToolTip, TTM_ACTIVATE, (ti.lpszText != nullptr), 0); - SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 400); -} - -static void CALLBACK ChatTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD) -{ - SESSION_INFO *si = (SESSION_INFO*)idEvent; - - POINT pt; - GetCursorPos(&pt); - ScreenToClient(hwnd, &pt); - - uint32_t nItemUnderMouse = (uint32_t)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); - if (HIWORD(nItemUnderMouse) == 1) - nItemUnderMouse = (uint32_t)(-1); - else - nItemUnderMouse &= 0xFFFF; - if (((int)nItemUnderMouse != si->currentHovered) || (nItemUnderMouse == -1)) { - KillTimer(hwnd, idEvent); - return; - } - - USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(si->ptszID, si->pszModule, si->currentHovered); - if (ui1) { - CMStringW wszBuf; - if (ProtoServiceExists(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { - wchar_t *p = (wchar_t*)CallProtoService(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)si->ptszID, (LPARAM)ui1->pszUID); - if (p) { - wszBuf = p; - mir_free(p); - } - } - if (wszBuf.IsEmpty()) - wszBuf.Format(L"%s:\t%s\n%s:\t%s\n%s:\t%s", - TranslateT("Nick"), ui1->pszNick, - TranslateT("Unique ID"), ui1->pszUID, - TranslateT("Status"), g_chatApi.TM_WordToString(si->pStatuses, ui1->Status)); - - CLCINFOTIP ti = { sizeof(ti) }; - Tipper_ShowTip(wszBuf, &ti); - si->bHasToolTip = true; - } - KillTimer(hwnd, idEvent); -} - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - if (pDlg != nullptr) - return pDlg->WndProc_Nicklist(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubNicklistProc, msg, wParam, lParam); -} - -LRESULT CSrmmBaseDialog::WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam) -{ - RECT rc; - - switch (msg) { - case WM_MEASUREITEM: - { - MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; - if (mis->CtlType == ODT_MENU) - return Menu_MeasureItem(lParam); - } - return FALSE; - - case WM_DRAWITEM: - { - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - if (dis->CtlType == ODT_MENU) - return Menu_DrawItem(lParam); - } - return FALSE; - - case WM_MOUSEMOVE: - RECT clientRect; - { - bool bTooltipExists = ServiceExists(MS_TIPPER_HIDETIP); - - POINT pt = { LOWORD(lParam), HIWORD(lParam) }; - GetClientRect(m_nickList.GetHwnd(), &clientRect); - if (PtInRect(&clientRect, pt)) { - // hit test item under mouse - uint32_t nItemUnderMouse = m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, lParam); - if (HIWORD(nItemUnderMouse) == 1) - nItemUnderMouse = (uint32_t)(-1); - else - nItemUnderMouse &= 0xFFFF; - - if (bTooltipExists) { - if ((int)nItemUnderMouse == m_si->currentHovered) - break; - m_si->currentHovered = (int)nItemUnderMouse; - - KillTimer(m_nickList.GetHwnd(), 1); - - if (m_si->bHasToolTip) { - Tipper_Hide(); - m_si->bHasToolTip = false; - } - - if (nItemUnderMouse != -1) - SetTimer(m_nickList.GetHwnd(), (UINT_PTR)m_si, 450, ChatTimerProc); - } - else ProcessNickListHovering(m_nickList.GetHwnd(), (int)nItemUnderMouse, m_si); - } - else { - if (bTooltipExists) { - KillTimer(m_nickList.GetHwnd(), 1); - if (m_si->bHasToolTip) { - Tipper_Hide(); - m_si->bHasToolTip = false; - } - } - else ProcessNickListHovering(m_nickList.GetHwnd(), -1, nullptr); - } - } - break; - - case WM_ERASEBKGND: - { - HDC dc = (HDC)wParam; - if (dc == nullptr) - break; - - int nUsers = m_si->getUserList().getCount(); - - int index = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - if (index == LB_ERR || nUsers <= 0) - break; - - int height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); - if (height == LB_ERR) - break; - - GetClientRect(m_nickList.GetHwnd(), &rc); - - int items = nUsers - index; - if (rc.bottom - rc.top > items * height) { - rc.top = items * height; - FillRect(dc, &rc, g_chatApi.hListBkgBrush); - } - } - return 1; - - case WM_CONTEXTMENU: - POINT pt; - { - int height = 0; - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - if (pt.x == -1 && pt.y == -1) { - int index = m_nickList.GetCurSel(); - int top = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); - height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); - pt.x = 4; - pt.y = (index - top)*height + 1; - } - else ScreenToClient(m_nickList.GetHwnd(), &pt); - - int item = LOWORD(m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y))); - USERINFO *ui = g_chatApi.SM_GetUserFromIndex(m_si->ptszID, m_si->pszModule, item); - if (ui != nullptr) { - if (pt.x == -1 && pt.y == -1) - pt.y += height - 4; - ClientToScreen(m_nickList.GetHwnd(), &pt); - - RunUserMenu(m_nickList.GetHwnd(), ui, pt); - return TRUE; - } - } - break; - } - - return mir_callNextSubclass(m_nickList.GetHwnd(), stubNicklistProc, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::OnInitDialog() -{ - WindowList_Add(g_hWindowList, m_hwnd, m_hContact); - SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this); - - m_pLog = Srmm_GetLogWindow((CMsgDialog*)this); - if (m_pLog->GetType() != 0) { // custom log type - HWND hwndLog = GetDlgItem(m_hwnd, IDC_SRMM_LOG); - EnableWindow(hwndLog, FALSE); - ShowWindow(hwndLog, SW_HIDE); - } - m_pLog->Attach(); - - SetWindowLongPtr(m_message.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_message.GetHwnd(), stubMessageProc); - m_message.SetReadOnly(false); - ::DragAcceptFiles(m_message.GetHwnd(), TRUE); - - if (isChat()) { - SetWindowLongPtr(m_nickList.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - mir_subclassWindow(m_nickList.GetHwnd(), stubNicklistProc); - } - - // three buttons below are initiated inside this call, so button creation must precede subclassing - Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); - - mir_subclassWindow(m_btnFilter.GetHwnd(), Srmm_ButtonSubclassProc); - mir_subclassWindow(m_btnColor.GetHwnd(), Srmm_ButtonSubclassProc); - mir_subclassWindow(m_btnBkColor.GetHwnd(), Srmm_ButtonSubclassProc); - - LoadSettings(); - return true; -} - -void CSrmmBaseDialog::OnDestroy() -{ - m_pLog->Detach(); - delete m_pLog; - - WindowList_Remove(g_hWindowList, m_hwnd); - - SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); - mir_unsubclassWindow(m_message.GetHwnd(), stubMessageProc); - mir_unsubclassWindow(m_nickList.GetHwnd(), stubNicklistProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CSrmmBaseDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_COMMAND: - if (!lParam && Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact)) - return 0; - - if (wParam >= MIN_CBUTTONID && wParam <= MAX_CBUTTONID) { - Srmm_ClickToolbarIcon(m_hContact, wParam, m_hwnd, 0); - return 0; - } - break; - - case WM_ACTIVATE: - if (m_si && LOWORD(wParam) == WA_INACTIVE) { - m_si->wState &= ~GC_EVENT_HIGHLIGHT; - m_si->wState &= ~STATE_TALK; - } - break; - - case WM_CBD_RECREATE: - Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); - break; - - case WM_NOTIFY: - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->hwndFrom == m_pLog->GetHwnd()) - m_pLog->Notify(wParam, lParam); - break; - } - - return CDlgBase::DlgProc(msg, wParam, lParam); -} - -void CSrmmBaseDialog::AddLog() -{ - if (m_si->pLogEnd) - m_pLog->LogEvents(m_si->pLog, false); - else - m_pLog->Clear(); -} - -bool CSrmmBaseDialog::AllowTyping() const -{ - return isChat() ? m_si->iType != GCW_SERVER : true; -} - -void CSrmmBaseDialog::ClearLog() -{ - m_pLog->Clear(); -} - -void CSrmmBaseDialog::UpdateOptions() -{ - MODULEINFO *mi = m_si->pMI; - EnableWindow(m_btnBold.GetHwnd(), mi->bBold); - EnableWindow(m_btnItalic.GetHwnd(), mi->bItalics); - EnableWindow(m_btnUnderline.GetHwnd(), mi->bUnderline); - EnableWindow(m_btnColor.GetHwnd(), mi->bColor); - EnableWindow(m_btnBkColor.GetHwnd(), mi->bBkgColor); - if (m_si->iType == GCW_CHATROOM) - EnableWindow(m_btnChannelMgr.GetHwnd(), mi->bChanMgr); - - Resize(); - RedrawLog2(m_si); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void RedrawLog2(SESSION_INFO *si) -{ - si->LastTime = 0; - if (si->pLog) - si->pDlg->log()->LogEvents(si->pLogEnd, TRUE); -} - -static void __cdecl phase2(SESSION_INFO *si) -{ - Sleep(30); - if (si && si->pDlg) - RedrawLog2(si); -} - -void CSrmmBaseDialog::RedrawLog() -{ - m_si->LastTime = 0; - if (m_si->pLog) { - LOGINFO *pLog = m_si->pLog; - if (m_si->iEventCount > 60) { - int index = 0; - while (index < 59) { - if (pLog->next == nullptr) - break; - - pLog = pLog->next; - if (m_si->iType != GCW_CHATROOM || !m_bFilterEnabled || (m_iLogFilterFlags & pLog->iType) != 0) - index++; - } - m_pLog->LogEvents(pLog, true); - mir_forkThread(phase2, m_si); - } - else m_pLog->LogEvents(m_si->pLogEnd, true); - } - else ClearLog(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CSrmmBaseDialog::onClick_Color(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwEffects = 0; - cf.dwMask = CFM_COLOR; - - if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { - if (!g_chatApi.bRightClickFilter) { - ShowColorChooser(pButton->GetCtrlId()); - return; - } - if (m_bFGSet) - cf.crTextColor = m_iFG; - } - else cf.crTextColor = m_clrInputFG; - - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_BkColor(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwEffects = 0; - cf.dwMask = CFM_BACKCOLOR; - - if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { - if (!g_chatApi.bRightClickFilter) { - ShowColorChooser(pButton->GetCtrlId()); - return; - } - if (m_bBGSet) - cf.crBackColor = m_iBG; - } - else cf.crBackColor = m_clrInputBG; - - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_BIU(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; - cf.dwEffects = 0; - - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_BOLD)) - cf.dwEffects |= CFE_BOLD; - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_ITALICS)) - cf.dwEffects |= CFE_ITALIC; - if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_UNDERLINE)) - cf.dwEffects |= CFE_UNDERLINE; - m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); -} - -void CSrmmBaseDialog::onClick_History(CCtrlButton *pButton) -{ - if (!pButton->Enabled()) - return; - - if (m_si != nullptr) - ShellExecute(m_hwnd, nullptr, g_chatApi.GetChatLogsFilename(m_si, 0), nullptr, nullptr, SW_SHOW); - else - CallService(MS_HISTORY_SHOWCONTACTHISTORY, m_hContact, 0); -} - -void CSrmmBaseDialog::onClick_ChanMgr(CCtrlButton *pButton) -{ - if (pButton->Enabled()) - Chat_DoEventHook(m_si, GC_USER_CHANMGR, nullptr, nullptr, 0); -} - -void CSrmmBaseDialog::onDblClick_List(CCtrlListBox *pList) -{ - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pList->GetHwnd(), &hti.pt); - - int item = LOWORD(pList->SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y))); - USERINFO *ui = g_chatApi.UM_FindUserFromIndex(m_si, item); - if (ui == nullptr) - return; - - bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - if (Chat::bDoubleClick4Privat ? bShift : !bShift) { - int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0)); - CMStringW tszName(ui->pszNick); - if (selStart == 0) - tszName.AppendChar(':'); - tszName.AppendChar(' '); - - m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString()); - PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); - SetFocus(m_message.GetHwnd()); - } - else Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern HANDLE hHookSrmmEvent; - -int CSrmmBaseDialog::NotifyEvent(int code) -{ - if (m_hContact == 0 && m_hwnd == nullptr) - return -1; - - MessageWindowEventData mwe = {}; - mwe.hContact = m_hContact; - mwe.hwndWindow = m_hwnd; - mwe.uType = code; - mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH; - mwe.hwndInput = m_message.GetHwnd(); - mwe.hwndLog = m_pLog->GetHwnd(); - return ::NotifyEventHooks(hHookSrmmEvent, 0, (LPARAM)&mwe); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact) -{ - if (PasteFilesAsURL(hDrop)) - return true; - - return ::ProcessFileDrop(hDrop, hContact); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// If enabled pastes droped files as list of URL of file:/// type -// Can be enabled/disabled by Chat/ShiftDropFilePasteURL database parameter -// @param hDrop - Drop handle -// @return Returns true if processed here, returns false if should be processed elsewhere - -bool CSrmmBaseDialog::PasteFilesAsURL(HDROP hDrop) -{ - bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - if (db_get_b(0, CHAT_MODULE, "ShiftDropFilePasteURL", 1) == 0 || !isShift) // hidden setting: Chat/ShiftDropFilePasteURL - return false; - - int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0); - if (fileCount == 0) - return true; - - CMStringW pasteString(L" "); - for (int i = 0; i < fileCount; i++) { - wchar_t szFilename[MAX_PATH]; - if (DragQueryFileW(hDrop, i, szFilename, _countof(szFilename))) { - CMStringW fileString(L"file:///"); - fileString.Append(szFilename); - fileString.Replace(L"%", L"%25"); - fileString.Replace(L" ", L"%20"); - fileString.Append((i != fileCount - 1) ? L"\r\n" : L" "); - pasteString += fileString; - } - } - - m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pasteString.c_str()); - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CSrmmBaseDialog::ProcessHotkeys(int key, bool isShift, bool isCtrl, bool isAlt) -{ - // Esc (close tab) - if (key == VK_ESCAPE && !isShift && !isCtrl && !isAlt) { - CloseTab(); - return true; - } - - if (isCtrl && !isAlt) { - switch (key) { - case VK_SPACE: // ctrl-space (paste clean text) - m_btnBold.Push(false); m_btnBold.Click(); - m_btnItalic.Push(false); m_btnItalic.Click(); - m_btnUnderline.Push(false); m_btnUnderline.Click(); - - m_btnColor.Push(false); m_btnColor.Click(); - m_btnBkColor.Push(false); m_btnBkColor.Click(); - return true; - - case 0x42: // ctrl-b (bold) - m_btnBold.Push(!m_btnBold.IsPushed()); - m_btnBold.Click(); - return true; - - case 0x48: // ctrl-h (history) - m_btnHistory.Click(); - return true; - - case 0x49: // ctrl-i (italics) - m_btnItalic.Push(!m_btnItalic.IsPushed()); - m_btnItalic.Click(); - return true; - - case 0x4b: // ctrl-k (text color) - m_btnColor.Push(!m_btnColor.IsPushed()); - m_btnColor.Click(); - return true; - - case 0x4c: // ctrl-l (back color) - m_btnBkColor.Push(!m_btnBkColor.IsPushed()); - m_btnBkColor.Click(); - return true; - - case 0x55: // ctrl-u (underlining) - m_btnUnderline.Push(!m_btnUnderline.IsPushed()); - m_btnUnderline.Click(); - return true; - - case VK_F4: // ctrl-F4 - CloseTab(); - return true; - } - } - - return false; -} - -void CSrmmBaseDialog::RefreshButtonStatus() -{ - if (m_si == nullptr) - return; - - CHARFORMAT2 cf; - cf.cbSize = sizeof(CHARFORMAT2); - cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR; - m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); - - if (m_si->pMI->bColor) { - bool bState = m_btnColor.IsPushed(); - if (!bState && cf.crTextColor != m_clrInputFG) - m_btnColor.Push(true); - else if (bState && cf.crTextColor == m_clrInputFG) - m_btnColor.Push(false); - } - - if (m_si->pMI->bBkgColor) { - bool bState = m_btnBkColor.IsPushed(); - if (!bState && cf.crBackColor != m_clrInputBG) - m_btnBkColor.Push(true); - else if (bState && cf.crBackColor == m_clrInputBG) - m_btnBkColor.Push(false); - } - - if (m_si->pMI->bBold) { - bool bState = m_btnBold.IsPushed(); - UINT u2 = cf.dwEffects & CFE_BOLD; - if (!bState && u2 != 0) - m_btnBold.Push(true); - else if (bState && u2 == 0) - m_btnBold.Push(false); - } - - if (m_si->pMI->bItalics) { - bool bState = m_btnItalic.IsPushed(); - UINT u2 = cf.dwEffects & CFE_ITALIC; - if (!bState && u2 != 0) - m_btnItalic.Push(true); - else if (bState && u2 == 0) - m_btnItalic.Push(false); - } - - if (m_si->pMI->bUnderline) { - bool bState = m_btnUnderline.IsPushed(); - UINT u2 = cf.dwEffects & CFE_UNDERLINE; - if (!bState && u2 != 0) - m_btnUnderline.Push(true); - else if (bState && u2 == 0) - m_btnUnderline.Push(false); - } -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#include "chat.h" +#include "resource.h" +#include "skin.h" +#include + +CSrmmBaseDialog::CSrmmBaseDialog(CMPluginBase &pPlugin, int idDialog, SESSION_INFO *si) : + CDlgBase(pPlugin, idDialog), + timerFlash(this, 1), + timerType(this, 2), + + m_message(this, IDC_SRMM_MESSAGE), + m_nickList(this, IDC_SRMM_NICKLIST), + + m_btnOk(this, IDOK), + m_btnFilter(this, IDC_SRMM_FILTER), + m_btnHistory(this, IDC_SRMM_HISTORY), + m_btnNickList(this, IDC_SRMM_SHOWNICKLIST), + m_btnChannelMgr(this, IDC_SRMM_CHANMGR), + + m_btnColor(this, IDC_SRMM_COLOR), + m_btnBkColor(this, IDC_SRMM_BKGCOLOR), + m_btnBold(this, IDC_SRMM_BOLD), + + m_btnItalic(this, IDC_SRMM_ITALICS), + m_btnUnderline(this, IDC_SRMM_UNDERLINE), + + m_si(si), + m_hContact(0), + m_clrInputBG(GetSysColor(COLOR_WINDOW)) +{ + m_bFilterEnabled = db_get_b(0, CHAT_MODULE, "FilterEnabled", 0) != 0; + m_bNicklistEnabled = db_get_b(0, CHAT_MODULE, "ShowNicklist", 1) != 0; + m_iLogFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", 0x03E0); + + m_btnColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_Color); + m_btnBkColor.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BkColor); + m_btnBold.OnClick = m_btnItalic.OnClick = m_btnUnderline.OnClick = Callback(this, &CSrmmBaseDialog::onClick_BIU); + + m_btnHistory.OnClick = Callback(this, &CSrmmBaseDialog::onClick_History); + m_btnChannelMgr.OnClick = Callback(this, &CSrmmBaseDialog::onClick_ChanMgr); + + m_nickList.OnDblClick = Callback(this, &CMsgDialog::onDblClick_List); + + if (si) { + m_hContact = si->hContact; + + if (si->pMI->bColor) { + m_iFG = 4; + m_bFGSet = true; + } + if (si->pMI->bBkgColor) { + m_iBG = 2; + m_bBGSet = true; + } + } +} + +void CSrmmBaseDialog::RunUserMenu(HWND hwndOwner, USERINFO *ui, const POINT &pt) +{ + HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_USERMENU)); + HMENU hSubMenu = GetSubMenu(hMenu, 0); + TranslateMenu(hSubMenu); + + USERINFO uinew; + memcpy(&uinew, ui, sizeof(USERINFO)); + + wchar_t szTemp[50]; + if (uinew.pszNick) + mir_snwprintf(szTemp, TranslateT("&Message %s"), uinew.pszNick); + else + mir_wstrncpy(szTemp, TranslateT("&Message"), _countof(szTemp) - 1); + + if (mir_wstrlen(szTemp) > 40) + mir_wstrncpy(szTemp + 40, L"...", 4); + ModifyMenu(hMenu, 0, MF_STRING | MF_BYPOSITION, IDM_SENDMESSAGE, szTemp); + + UINT uID = Chat_CreateMenu(hwndOwner, hSubMenu, pt, m_si, uinew.pszUID); + switch (uID) { + case 0: + break; + + case IDM_SENDMESSAGE: + Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); + break; + + default: + Chat_DoEventHook(m_si, GC_USER_NICKLISTMENU, ui, nullptr, uID); + break; + } + DestroyMenu(hMenu); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static LRESULT CALLBACK Srmm_ButtonSubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_RBUTTONUP: + if (g_chatApi.bRightClickFilter) { + CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + if (pDlg == nullptr) + break; + + switch (GetDlgCtrlID(hwnd)) { + case IDC_SRMM_FILTER: + pDlg->ShowFilterMenu(); + break; + + case IDC_SRMM_COLOR: + pDlg->ShowColorChooser(IDC_SRMM_COLOR); + break; + + case IDC_SRMM_BKGCOLOR: + pDlg->ShowColorChooser(IDC_SRMM_BKGCOLOR); + break; + } + } + break; + } + + return mir_callNextSubclass(hwnd, Srmm_ButtonSubclassProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + if (pDlg != nullptr) + return pDlg->WndProc_Message(msg, wParam, lParam); + + return mir_callNextSubclass(hwnd, stubMessageProc, msg, wParam, lParam); +} + +LRESULT CSrmmBaseDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_SETCURSOR: + if (m_bInMenu) { + SetCursor(LoadCursor(nullptr, IDC_ARROW)); + return TRUE; + } + break; + + case WM_CHAR: + switch (wParam) { + case 0x02: // ctrl+B + if (m_btnBold.Enabled()) + return 1; + break; + case 0x09: // ctrl+I + if (m_btnItalic.Enabled()) + return 1; + break; + case 0x15: // ctrl+U + if (m_btnUnderline.Enabled()) + return 1; + break; + } + break; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if (wParam == VK_BACK) + if (m_message.GetRichTextLength() == 0) + return 1; + + MSG tmp = { m_hwnd, msg, wParam, lParam }; + if (Hotkey_Check(&tmp, g_pszHotkeySection) == 100) { + if (!(GetWindowLongPtr(m_message.GetHwnd(), GWL_STYLE) & ES_READONLY)) { + PostMessage(m_hwnd, WM_COMMAND, IDOK, 0); + return true; + } + } + } + + LRESULT res = mir_callNextSubclass(m_message.GetHwnd(), stubMessageProc, msg, wParam, lParam); + switch (msg) { + case WM_GETDLGCODE: + return res & ~DLGC_HASSETSEL; + + case WM_KEYUP: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + RefreshButtonStatus(); + break; + + case WM_KEYDOWN: + if ((GetKeyState(VK_CONTROL) & 0x8000) && wParam == 'V' || (GetKeyState(VK_SHIFT) & 0x8000) && wParam == VK_INSERT) { + if (IsClipboardFormatAvailable(CF_HDROP)) { + m_message.SendMsg(WM_PASTE, 0, 0); + return 0; + } + } + + __fallthrough; + + case WM_SYSKEYDOWN: + if (!(GetKeyState(VK_RMENU) & 0x8000)) { + MSG message = { m_hwnd, msg, wParam, lParam }; + LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); + if (iButtonFrom) { + Srmm_ProcessToolbarHotkey(m_hContact, iButtonFrom, m_hwnd); + return TRUE; + } + } + break; + } + + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// process mouse - hovering for the nickname list.fires events so the protocol can +// show the userinfo - tooltip. + +static void ProcessNickListHovering(HWND hwnd, int hoveredItem, SESSION_INFO *parentdat) +{ + static int currentHovered = -1; + static HWND hwndToolTip = nullptr; + static HWND oldParent = nullptr; + + if (hoveredItem == currentHovered) + return; + + currentHovered = hoveredItem; + + if (oldParent != hwnd && hwndToolTip) { + SendMessage(hwndToolTip, TTM_DELTOOL, 0, 0); + DestroyWindow(hwndToolTip); + hwndToolTip = nullptr; + } + + if (hoveredItem == -1) { + SendMessage(hwndToolTip, TTM_ACTIVATE, 0, 0); + return; + } + + bool bNewTip = false; + if (!hwndToolTip) { + bNewTip = true; + hwndToolTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nullptr, + WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + hwnd, nullptr, g_plugin.getInst(), nullptr); + } + + RECT clientRect; + GetClientRect(hwnd, &clientRect); + + TOOLINFO ti = { sizeof(ti) }; + ti.uFlags = TTF_SUBCLASS; + ti.hinst = g_plugin.getInst(); + ti.hwnd = hwnd; + ti.uId = 1; + ti.rect = clientRect; + + CMStringW wszBuf; + + USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(parentdat->ptszID, parentdat->pszModule, currentHovered); + if (ui1) { + if (ProtoServiceExists(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { + wchar_t *p = (wchar_t*)CallProtoService(parentdat->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)parentdat->ptszID, (LPARAM)ui1->pszUID); + if (p != nullptr) { + wszBuf = p; + mir_free(p); + } + } + + if (wszBuf.IsEmpty()) + wszBuf.Format(L"%s: %s\r\n%s: %s\r\n%s: %s", + TranslateT("Nickname"), ui1->pszNick, + TranslateT("Unique ID"), ui1->pszUID, + TranslateT("Status"), g_chatApi.TM_WordToString(parentdat->pStatuses, ui1->Status)); + ti.lpszText = wszBuf.GetBuffer(); + } + + SendMessage(hwndToolTip, bNewTip ? TTM_ADDTOOL : TTM_UPDATETIPTEXT, 0, (LPARAM)&ti); + SendMessage(hwndToolTip, TTM_ACTIVATE, (ti.lpszText != nullptr), 0); + SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 400); +} + +static void CALLBACK ChatTimerProc(HWND hwnd, UINT, UINT_PTR idEvent, DWORD) +{ + SESSION_INFO *si = (SESSION_INFO*)idEvent; + + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hwnd, &pt); + + uint32_t nItemUnderMouse = (uint32_t)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); + if (HIWORD(nItemUnderMouse) == 1) + nItemUnderMouse = (uint32_t)(-1); + else + nItemUnderMouse &= 0xFFFF; + if (((int)nItemUnderMouse != si->currentHovered) || (nItemUnderMouse == -1)) { + KillTimer(hwnd, idEvent); + return; + } + + USERINFO *ui1 = g_chatApi.SM_GetUserFromIndex(si->ptszID, si->pszModule, si->currentHovered); + if (ui1) { + CMStringW wszBuf; + if (ProtoServiceExists(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT)) { + wchar_t *p = (wchar_t*)CallProtoService(si->pszModule, MS_GC_PROTO_GETTOOLTIPTEXT, (WPARAM)si->ptszID, (LPARAM)ui1->pszUID); + if (p) { + wszBuf = p; + mir_free(p); + } + } + if (wszBuf.IsEmpty()) + wszBuf.Format(L"%s:\t%s\n%s:\t%s\n%s:\t%s", + TranslateT("Nick"), ui1->pszNick, + TranslateT("Unique ID"), ui1->pszUID, + TranslateT("Status"), g_chatApi.TM_WordToString(si->pStatuses, ui1->Status)); + + CLCINFOTIP ti = { sizeof(ti) }; + Tipper_ShowTip(wszBuf, &ti); + si->bHasToolTip = true; + } + KillTimer(hwnd, idEvent); +} + +EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubNicklistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); + if (pDlg != nullptr) + return pDlg->WndProc_Nicklist(msg, wParam, lParam); + + return mir_callNextSubclass(hwnd, stubNicklistProc, msg, wParam, lParam); +} + +LRESULT CSrmmBaseDialog::WndProc_Nicklist(UINT msg, WPARAM wParam, LPARAM lParam) +{ + RECT rc; + + switch (msg) { + case WM_MEASUREITEM: + { + MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lParam; + if (mis->CtlType == ODT_MENU) + return Menu_MeasureItem(lParam); + } + return FALSE; + + case WM_DRAWITEM: + { + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; + if (dis->CtlType == ODT_MENU) + return Menu_DrawItem(lParam); + } + return FALSE; + + case WM_MOUSEMOVE: + RECT clientRect; + { + bool bTooltipExists = ServiceExists(MS_TIPPER_HIDETIP); + + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + GetClientRect(m_nickList.GetHwnd(), &clientRect); + if (PtInRect(&clientRect, pt)) { + // hit test item under mouse + uint32_t nItemUnderMouse = m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, lParam); + if (HIWORD(nItemUnderMouse) == 1) + nItemUnderMouse = (uint32_t)(-1); + else + nItemUnderMouse &= 0xFFFF; + + if (bTooltipExists) { + if ((int)nItemUnderMouse == m_si->currentHovered) + break; + m_si->currentHovered = (int)nItemUnderMouse; + + KillTimer(m_nickList.GetHwnd(), 1); + + if (m_si->bHasToolTip) { + Tipper_Hide(); + m_si->bHasToolTip = false; + } + + if (nItemUnderMouse != -1) + SetTimer(m_nickList.GetHwnd(), (UINT_PTR)m_si, 450, ChatTimerProc); + } + else ProcessNickListHovering(m_nickList.GetHwnd(), (int)nItemUnderMouse, m_si); + } + else { + if (bTooltipExists) { + KillTimer(m_nickList.GetHwnd(), 1); + if (m_si->bHasToolTip) { + Tipper_Hide(); + m_si->bHasToolTip = false; + } + } + else ProcessNickListHovering(m_nickList.GetHwnd(), -1, nullptr); + } + } + break; + + case WM_ERASEBKGND: + { + HDC dc = (HDC)wParam; + if (dc == nullptr) + break; + + int nUsers = m_si->getUserList().getCount(); + + int index = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); + if (index == LB_ERR || nUsers <= 0) + break; + + int height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); + if (height == LB_ERR) + break; + + GetClientRect(m_nickList.GetHwnd(), &rc); + + int items = nUsers - index; + if (rc.bottom - rc.top > items * height) { + rc.top = items * height; + FillRect(dc, &rc, g_chatApi.hListBkgBrush); + } + } + return 1; + + case WM_CONTEXTMENU: + POINT pt; + { + int height = 0; + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + if (pt.x == -1 && pt.y == -1) { + int index = m_nickList.GetCurSel(); + int top = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0); + height = m_nickList.SendMsg(LB_GETITEMHEIGHT, 0, 0); + pt.x = 4; + pt.y = (index - top)*height + 1; + } + else ScreenToClient(m_nickList.GetHwnd(), &pt); + + int item = LOWORD(m_nickList.SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y))); + USERINFO *ui = g_chatApi.SM_GetUserFromIndex(m_si->ptszID, m_si->pszModule, item); + if (ui != nullptr) { + if (pt.x == -1 && pt.y == -1) + pt.y += height - 4; + ClientToScreen(m_nickList.GetHwnd(), &pt); + + RunUserMenu(m_nickList.GetHwnd(), ui, pt); + return TRUE; + } + } + break; + } + + return mir_callNextSubclass(m_nickList.GetHwnd(), stubNicklistProc, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CSrmmBaseDialog::OnInitDialog() +{ + WindowList_Add(g_hWindowList, m_hwnd, m_hContact); + SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this); + + m_pLog = Srmm_GetLogWindow((CMsgDialog*)this); + if (m_pLog->GetType() != 0) { // custom log type + HWND hwndLog = GetDlgItem(m_hwnd, IDC_SRMM_LOG); + EnableWindow(hwndLog, FALSE); + ShowWindow(hwndLog, SW_HIDE); + } + m_pLog->Attach(); + + SetWindowLongPtr(m_message.GetHwnd(), GWLP_USERDATA, LPARAM(this)); + mir_subclassWindow(m_message.GetHwnd(), stubMessageProc); + m_message.SetReadOnly(false); + ::DragAcceptFiles(m_message.GetHwnd(), TRUE); + + if (isChat()) { + SetWindowLongPtr(m_nickList.GetHwnd(), GWLP_USERDATA, LPARAM(this)); + mir_subclassWindow(m_nickList.GetHwnd(), stubNicklistProc); + } + + // three buttons below are initiated inside this call, so button creation must precede subclassing + Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); + + mir_subclassWindow(m_btnFilter.GetHwnd(), Srmm_ButtonSubclassProc); + mir_subclassWindow(m_btnColor.GetHwnd(), Srmm_ButtonSubclassProc); + mir_subclassWindow(m_btnBkColor.GetHwnd(), Srmm_ButtonSubclassProc); + + LoadSettings(); + return true; +} + +void CSrmmBaseDialog::OnDestroy() +{ + m_pLog->Detach(); + delete m_pLog; + + WindowList_Remove(g_hWindowList, m_hwnd); + + SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); + mir_unsubclassWindow(m_message.GetHwnd(), stubMessageProc); + mir_unsubclassWindow(m_nickList.GetHwnd(), stubNicklistProc); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CSrmmBaseDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_COMMAND: + if (!lParam && Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, m_hContact)) + return 0; + + if (wParam >= MIN_CBUTTONID && wParam <= MAX_CBUTTONID) { + Srmm_ClickToolbarIcon(m_hContact, wParam, m_hwnd, 0); + return 0; + } + break; + + case WM_ACTIVATE: + if (m_si && LOWORD(wParam) == WA_INACTIVE) { + m_si->wState &= ~GC_EVENT_HIGHLIGHT; + m_si->wState &= ~STATE_TALK; + } + break; + + case WM_CBD_RECREATE: + Srmm_CreateToolbarIcons(m_hwnd, isChat() ? BBBF_ISCHATBUTTON : BBBF_ISIMBUTTON); + break; + + case WM_NOTIFY: + LPNMHDR hdr = (LPNMHDR)lParam; + if (hdr->hwndFrom == m_pLog->GetHwnd()) + m_pLog->Notify(wParam, lParam); + break; + } + + return CDlgBase::DlgProc(msg, wParam, lParam); +} + +void CSrmmBaseDialog::AddLog() +{ + if (m_si->pLogEnd) + m_pLog->LogEvents(m_si->pLog, false); + else + m_pLog->Clear(); +} + +bool CSrmmBaseDialog::AllowTyping() const +{ + return isChat() ? m_si->iType != GCW_SERVER : true; +} + +void CSrmmBaseDialog::ClearLog() +{ + m_pLog->Clear(); +} + +void CSrmmBaseDialog::UpdateOptions() +{ + MODULEINFO *mi = m_si->pMI; + EnableWindow(m_btnBold.GetHwnd(), mi->bBold); + EnableWindow(m_btnItalic.GetHwnd(), mi->bItalics); + EnableWindow(m_btnUnderline.GetHwnd(), mi->bUnderline); + EnableWindow(m_btnColor.GetHwnd(), mi->bColor); + EnableWindow(m_btnBkColor.GetHwnd(), mi->bBkgColor); + if (m_si->iType == GCW_CHATROOM) + EnableWindow(m_btnChannelMgr.GetHwnd(), mi->bChanMgr); + + Resize(); + RedrawLog2(m_si); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void RedrawLog2(SESSION_INFO *si) +{ + si->LastTime = 0; + if (si->pLog) + si->pDlg->log()->LogEvents(si->pLogEnd, TRUE); +} + +static void __cdecl phase2(SESSION_INFO *si) +{ + Sleep(30); + if (si && si->pDlg) + RedrawLog2(si); +} + +void CSrmmBaseDialog::RedrawLog() +{ + m_si->LastTime = 0; + if (m_si->pLog) { + LOGINFO *pLog = m_si->pLog; + if (m_si->iEventCount > 60) { + int index = 0; + while (index < 59) { + if (pLog->next == nullptr) + break; + + pLog = pLog->next; + if (m_si->iType != GCW_CHATROOM || !m_bFilterEnabled || (m_iLogFilterFlags & pLog->iType) != 0) + index++; + } + m_pLog->LogEvents(pLog, true); + mir_forkThread(phase2, m_si); + } + else m_pLog->LogEvents(m_si->pLogEnd, true); + } + else ClearLog(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CSrmmBaseDialog::onClick_Color(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwEffects = 0; + cf.dwMask = CFM_COLOR; + + if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { + if (!g_chatApi.bRightClickFilter) { + ShowColorChooser(pButton->GetCtrlId()); + return; + } + if (m_bFGSet) + cf.crTextColor = m_iFG; + } + else cf.crTextColor = m_clrInputFG; + + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); +} + +void CSrmmBaseDialog::onClick_BkColor(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwEffects = 0; + cf.dwMask = CFM_BACKCOLOR; + + if (IsDlgButtonChecked(m_hwnd, pButton->GetCtrlId())) { + if (!g_chatApi.bRightClickFilter) { + ShowColorChooser(pButton->GetCtrlId()); + return; + } + if (m_bBGSet) + cf.crBackColor = m_iBG; + } + else cf.crBackColor = m_clrInputBG; + + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); +} + +void CSrmmBaseDialog::onClick_BIU(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; + cf.dwEffects = 0; + + if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_BOLD)) + cf.dwEffects |= CFE_BOLD; + if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_ITALICS)) + cf.dwEffects |= CFE_ITALIC; + if (IsDlgButtonChecked(m_hwnd, IDC_SRMM_UNDERLINE)) + cf.dwEffects |= CFE_UNDERLINE; + m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); +} + +void CSrmmBaseDialog::onClick_History(CCtrlButton *pButton) +{ + if (!pButton->Enabled()) + return; + + if (m_si != nullptr) + ShellExecute(m_hwnd, nullptr, g_chatApi.GetChatLogsFilename(m_si, 0), nullptr, nullptr, SW_SHOW); + else + CallService(MS_HISTORY_SHOWCONTACTHISTORY, m_hContact, 0); +} + +void CSrmmBaseDialog::onClick_ChanMgr(CCtrlButton *pButton) +{ + if (pButton->Enabled()) + Chat_DoEventHook(m_si, GC_USER_CHANMGR, nullptr, nullptr, 0); +} + +void CSrmmBaseDialog::onDblClick_List(CCtrlListBox *pList) +{ + TVHITTESTINFO hti; + hti.pt.x = (short)LOWORD(GetMessagePos()); + hti.pt.y = (short)HIWORD(GetMessagePos()); + ScreenToClient(pList->GetHwnd(), &hti.pt); + + int item = LOWORD(pList->SendMsg(LB_ITEMFROMPOINT, 0, MAKELPARAM(hti.pt.x, hti.pt.y))); + USERINFO *ui = g_chatApi.UM_FindUserFromIndex(m_si, item); + if (ui == nullptr) + return; + + bool bShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; + if (Chat::bDoubleClick4Privat ? bShift : !bShift) { + int selStart = LOWORD(m_message.SendMsg(EM_GETSEL, 0, 0)); + CMStringW tszName(ui->pszNick); + if (selStart == 0) + tszName.AppendChar(':'); + tszName.AppendChar(' '); + + m_message.SendMsg(EM_REPLACESEL, FALSE, (LPARAM)tszName.GetString()); + PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0); + SetFocus(m_message.GetHwnd()); + } + else Chat_DoEventHook(m_si, GC_USER_PRIVMESS, ui, nullptr, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern HANDLE hHookSrmmEvent; + +int CSrmmBaseDialog::NotifyEvent(int code) +{ + if (m_hContact == 0 && m_hwnd == nullptr) + return -1; + + MessageWindowEventData mwe = {}; + mwe.hContact = m_hContact; + mwe.hwndWindow = m_hwnd; + mwe.uType = code; + mwe.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH; + mwe.hwndInput = m_message.GetHwnd(); + mwe.hwndLog = m_pLog->GetHwnd(); + return ::NotifyEventHooks(hHookSrmmEvent, 0, (LPARAM)&mwe); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CSrmmBaseDialog::ProcessFileDrop(HDROP hDrop, MCONTACT hContact) +{ + if (PasteFilesAsURL(hDrop)) + return true; + + return ::ProcessFileDrop(hDrop, hContact); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// If enabled pastes droped files as list of URL of file:/// type +// Can be enabled/disabled by Chat/ShiftDropFilePasteURL database parameter +// @param hDrop - Drop handle +// @return Returns true if processed here, returns false if should be processed elsewhere + +bool CSrmmBaseDialog::PasteFilesAsURL(HDROP hDrop) +{ + bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; + if (db_get_b(0, CHAT_MODULE, "ShiftDropFilePasteURL", 1) == 0 || !isShift) // hidden setting: Chat/ShiftDropFilePasteURL + return false; + + int fileCount = DragQueryFileW(hDrop, -1, nullptr, 0); + if (fileCount == 0) + return true; + + CMStringW pasteString(L" "); + for (int i = 0; i < fileCount; i++) { + wchar_t szFilename[MAX_PATH]; + if (DragQueryFileW(hDrop, i, szFilename, _countof(szFilename))) { + CMStringW fileString(L"file:///"); + fileString.Append(szFilename); + fileString.Replace(L"%", L"%25"); + fileString.Replace(L" ", L"%20"); + fileString.Append((i != fileCount - 1) ? L"\r\n" : L" "); + pasteString += fileString; + } + } + + m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pasteString.c_str()); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CSrmmBaseDialog::ProcessHotkeys(int key, bool isShift, bool isCtrl, bool isAlt) +{ + // Esc (close tab) + if (key == VK_ESCAPE && !isShift && !isCtrl && !isAlt) { + CloseTab(); + return true; + } + + if (isCtrl && !isAlt) { + switch (key) { + case VK_SPACE: // ctrl-space (paste clean text) + m_btnBold.Push(false); m_btnBold.Click(); + m_btnItalic.Push(false); m_btnItalic.Click(); + m_btnUnderline.Push(false); m_btnUnderline.Click(); + + m_btnColor.Push(false); m_btnColor.Click(); + m_btnBkColor.Push(false); m_btnBkColor.Click(); + return true; + + case 0x42: // ctrl-b (bold) + m_btnBold.Push(!m_btnBold.IsPushed()); + m_btnBold.Click(); + return true; + + case 0x48: // ctrl-h (history) + m_btnHistory.Click(); + return true; + + case 0x49: // ctrl-i (italics) + m_btnItalic.Push(!m_btnItalic.IsPushed()); + m_btnItalic.Click(); + return true; + + case 0x4b: // ctrl-k (text color) + m_btnColor.Push(!m_btnColor.IsPushed()); + m_btnColor.Click(); + return true; + + case 0x4c: // ctrl-l (back color) + m_btnBkColor.Push(!m_btnBkColor.IsPushed()); + m_btnBkColor.Click(); + return true; + + case 0x55: // ctrl-u (underlining) + m_btnUnderline.Push(!m_btnUnderline.IsPushed()); + m_btnUnderline.Click(); + return true; + + case VK_F4: // ctrl-F4 + CloseTab(); + return true; + } + } + + return false; +} + +void CSrmmBaseDialog::RefreshButtonStatus() +{ + if (m_si == nullptr) + return; + + CHARFORMAT2 cf; + cf.cbSize = sizeof(CHARFORMAT2); + cf.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_BACKCOLOR | CFM_COLOR; + m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf); + + if (m_si->pMI->bColor) { + bool bState = m_btnColor.IsPushed(); + if (!bState && cf.crTextColor != m_clrInputFG) + m_btnColor.Push(true); + else if (bState && cf.crTextColor == m_clrInputFG) + m_btnColor.Push(false); + } + + if (m_si->pMI->bBkgColor) { + bool bState = m_btnBkColor.IsPushed(); + if (!bState && cf.crBackColor != m_clrInputBG) + m_btnBkColor.Push(true); + else if (bState && cf.crBackColor == m_clrInputBG) + m_btnBkColor.Push(false); + } + + if (m_si->pMI->bBold) { + bool bState = m_btnBold.IsPushed(); + UINT u2 = cf.dwEffects & CFE_BOLD; + if (!bState && u2 != 0) + m_btnBold.Push(true); + else if (bState && u2 == 0) + m_btnBold.Push(false); + } + + if (m_si->pMI->bItalics) { + bool bState = m_btnItalic.IsPushed(); + UINT u2 = cf.dwEffects & CFE_ITALIC; + if (!bState && u2 != 0) + m_btnItalic.Push(true); + else if (bState && u2 == 0) + m_btnItalic.Push(false); + } + + if (m_si->pMI->bUnderline) { + bool bState = m_btnUnderline.IsPushed(); + UINT u2 = cf.dwEffects & CFE_UNDERLINE; + if (!bState && u2 != 0) + m_btnUnderline.Push(true); + else if (bState && u2 == 0) + m_btnUnderline.Push(false); + } +} diff --git a/src/mir_app/src/srmm_log.cpp b/src/mir_app/src/srmm_log.cpp index bc97e37023..d60636339d 100644 --- a/src/mir_app/src/srmm_log.cpp +++ b/src/mir_app/src/srmm_log.cpp @@ -1,196 +1,196 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log container - -#include "stdafx.h" -#include "chat.h" - -struct LoggerClass -{ - LoggerClass(CMPlugin *p1, const char *p2, const wchar_t *p3, pfnSrmmLogCreator p4) : - pPlugin(p1), - szShortName(mir_strdup(p2)), - wszScreenName(mir_wstrdup(p3)), - pfnBuilder(p4) - {} - - CMPlugin *pPlugin; - ptrA szShortName; - ptrW wszScreenName; - pfnSrmmLogCreator pfnBuilder; -}; - -static OBJLIST g_arLogClasses(1, PtrKeySortT); - -static CMOption g_bEnableCustomLogs("SRMM", "EnableCustomLogs", 0); - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool sttEnableCustomLogs(CMsgDialog *pDlg) -{ - // always enable custom log viewers for private chats - if (!pDlg->isChat()) - return true; - - // if custom log viewers are disable, use build-in one - if (!g_bEnableCustomLogs) - return false; - - // check if custom viewers are forbidden for this particular account - auto *szProto = Proto_GetBaseAccountName(pDlg->m_hContact); - if (szProto) { - // hidden setting !!!!!!!! - CMStringA szProtoList(db_get_sm(0, "SRMM", "DisableCustomLogsForProto")); - - int iStart = 0; - while (true) { - auto forbiddenProto = szProtoList.Tokenize(",; ", iStart); - if (forbiddenProto.IsEmpty()) - break; - - if (forbiddenProto == szProto) - return false; - } - } - - // ok-ok, use that custom viewer - return true; -} - -CSrmmLogWindow* Srmm_GetLogWindow(CMsgDialog *pDlg) -{ - if (sttEnableCustomLogs(pDlg)) { - CMStringA szViewerName(db_get_sm(pDlg->m_hContact, SRMM_MODULE, "Logger")); - if (szViewerName.IsEmpty()) - szViewerName = db_get_sm(0, "SRMM", "Logger", "built-in"); - - for (auto &it : g_arLogClasses) - if (szViewerName == it->szShortName) - return it->pfnBuilder(*pDlg); - } - - for (auto &it : g_arLogClasses) - if (!mir_strcmp(it->szShortName, "built-in")) - return it->pfnBuilder(*pDlg); - - return nullptr; // shall never happen -} - -///////////////////////////////////////////////////////////////////////////////////////// -// options dialog - -static class CSrmmLogOptionsDlg *pDialog = nullptr; - -class CSrmmLogOptionsDlg : public CDlgBase -{ - CCtrlListBox m_list; - CCtrlCheck chkCustomLogs; - -public: - CSrmmLogOptionsDlg() : - CDlgBase(g_plugin, IDD_OPT_SRMMLOG), - m_list(this, IDC_LIST), - chkCustomLogs(this, IDC_ENABLE_CUSTOM) - { - CreateLink(chkCustomLogs, g_bEnableCustomLogs); - - m_list.OnSelChange = Callback(this, &CSrmmLogOptionsDlg::onChange_List); - } - - bool OnInitDialog() override - { - pDialog = this; - ptrA szCurr(db_get_sa(0, "SRMM", "Logger", "built-in")); - - for (auto &it : g_arLogClasses) { - int idx = m_list.AddString(TranslateW_LP(it->wszScreenName, it->pPlugin), LPARAM(it)); - if (!mir_strcmp(szCurr, it->szShortName)) - m_list.SetCurSel(idx); - } - - return true; - } - - bool OnApply() override - { - int idx = m_list.GetCurSel(); - if (idx == -1) - return false; - - auto *pLogger = (LoggerClass *)m_list.GetItemData(idx); - db_set_s(0, "SRMM", "Logger", pLogger->szShortName); - return true; - } - - void OnDestroy() override - { - pDialog = nullptr; - } - - void Rebuild() - { - m_list.ResetContent(); - OnInitDialog(); - } - - void onChange_List(CCtrlListBox *) - { - NotifyChange(); - } -}; - -void SrmmLogOptionsInit(WPARAM wParam) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Log viewer"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CSrmmLogOptionsDlg(); - g_plugin.addOptions(wParam, &odp); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder) -{ - if (!pszShortName || !pwszScreenName || !fnBuilder) - return nullptr; - - auto *p = new LoggerClass(pPlugin, pszShortName, pwszScreenName, fnBuilder); - g_arLogClasses.insert(p); - - if (pDialog) - pDialog->Rebuild(); - return p; -} - -MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE pLogger) -{ - g_arLogClasses.remove((LoggerClass *)pLogger); - - if (pDialog) - pDialog->Rebuild(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// SRMM log container + +#include "stdafx.h" +#include "chat.h" + +struct LoggerClass +{ + LoggerClass(CMPlugin *p1, const char *p2, const wchar_t *p3, pfnSrmmLogCreator p4) : + pPlugin(p1), + szShortName(mir_strdup(p2)), + wszScreenName(mir_wstrdup(p3)), + pfnBuilder(p4) + {} + + CMPlugin *pPlugin; + ptrA szShortName; + ptrW wszScreenName; + pfnSrmmLogCreator pfnBuilder; +}; + +static OBJLIST g_arLogClasses(1, PtrKeySortT); + +static CMOption g_bEnableCustomLogs("SRMM", "EnableCustomLogs", 0); + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool sttEnableCustomLogs(CMsgDialog *pDlg) +{ + // always enable custom log viewers for private chats + if (!pDlg->isChat()) + return true; + + // if custom log viewers are disable, use build-in one + if (!g_bEnableCustomLogs) + return false; + + // check if custom viewers are forbidden for this particular account + auto *szProto = Proto_GetBaseAccountName(pDlg->m_hContact); + if (szProto) { + // hidden setting !!!!!!!! + CMStringA szProtoList(db_get_sm(0, "SRMM", "DisableCustomLogsForProto")); + + int iStart = 0; + while (true) { + auto forbiddenProto = szProtoList.Tokenize(",; ", iStart); + if (forbiddenProto.IsEmpty()) + break; + + if (forbiddenProto == szProto) + return false; + } + } + + // ok-ok, use that custom viewer + return true; +} + +CSrmmLogWindow* Srmm_GetLogWindow(CMsgDialog *pDlg) +{ + if (sttEnableCustomLogs(pDlg)) { + CMStringA szViewerName(db_get_sm(pDlg->m_hContact, SRMM_MODULE, "Logger")); + if (szViewerName.IsEmpty()) + szViewerName = db_get_sm(0, "SRMM", "Logger", "built-in"); + + for (auto &it : g_arLogClasses) + if (szViewerName == it->szShortName) + return it->pfnBuilder(*pDlg); + } + + for (auto &it : g_arLogClasses) + if (!mir_strcmp(it->szShortName, "built-in")) + return it->pfnBuilder(*pDlg); + + return nullptr; // shall never happen +} + +///////////////////////////////////////////////////////////////////////////////////////// +// options dialog + +static class CSrmmLogOptionsDlg *pDialog = nullptr; + +class CSrmmLogOptionsDlg : public CDlgBase +{ + CCtrlListBox m_list; + CCtrlCheck chkCustomLogs; + +public: + CSrmmLogOptionsDlg() : + CDlgBase(g_plugin, IDD_OPT_SRMMLOG), + m_list(this, IDC_LIST), + chkCustomLogs(this, IDC_ENABLE_CUSTOM) + { + CreateLink(chkCustomLogs, g_bEnableCustomLogs); + + m_list.OnSelChange = Callback(this, &CSrmmLogOptionsDlg::onChange_List); + } + + bool OnInitDialog() override + { + pDialog = this; + ptrA szCurr(db_get_sa(0, "SRMM", "Logger", "built-in")); + + for (auto &it : g_arLogClasses) { + int idx = m_list.AddString(TranslateW_LP(it->wszScreenName, it->pPlugin), LPARAM(it)); + if (!mir_strcmp(szCurr, it->szShortName)) + m_list.SetCurSel(idx); + } + + return true; + } + + bool OnApply() override + { + int idx = m_list.GetCurSel(); + if (idx == -1) + return false; + + auto *pLogger = (LoggerClass *)m_list.GetItemData(idx); + db_set_s(0, "SRMM", "Logger", pLogger->szShortName); + return true; + } + + void OnDestroy() override + { + pDialog = nullptr; + } + + void Rebuild() + { + m_list.ResetContent(); + OnInitDialog(); + } + + void onChange_List(CCtrlListBox *) + { + NotifyChange(); + } +}; + +void SrmmLogOptionsInit(WPARAM wParam) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = 910000000; + odp.szGroup.a = LPGEN("Message sessions"); + odp.szTitle.a = LPGEN("Log viewer"); + odp.flags = ODPF_BOLDGROUPS; + odp.pDialog = new CSrmmLogOptionsDlg(); + g_plugin.addOptions(wParam, &odp); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(HANDLE) RegisterSrmmLog(CMPlugin *pPlugin, const char *pszShortName, const wchar_t *pwszScreenName, pfnSrmmLogCreator fnBuilder) +{ + if (!pszShortName || !pwszScreenName || !fnBuilder) + return nullptr; + + auto *p = new LoggerClass(pPlugin, pszShortName, pwszScreenName, fnBuilder); + g_arLogClasses.insert(p); + + if (pDialog) + pDialog->Rebuild(); + return p; +} + +MIR_APP_DLL(void) UnregisterSrmmLog(HANDLE pLogger) +{ + g_arLogClasses.remove((LoggerClass *)pLogger); + + if (pDialog) + pDialog->Rebuild(); +} diff --git a/src/mir_app/src/srmm_log_hpp.cpp b/src/mir_app/src/srmm_log_hpp.cpp index 09fe9b3f4b..584ed539f9 100644 --- a/src/mir_app/src/srmm_log_hpp.cpp +++ b/src/mir_app/src/srmm_log_hpp.cpp @@ -1,227 +1,227 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// CHppLogWindow class - -#include "stdafx.h" -#include "chat.h" - -#define EVENTTYPE_STATUSCHANGE 25368 -#define EVENTTYPE_ERRMSG 25366 - -class CHppLogWindow : public CSrmmLogWindow -{ - HWND m_hwnd = nullptr; - -public: - CHppLogWindow(CMsgDialog &pDlg) : - CSrmmLogWindow(pDlg) - { - } - - void Attach() override - { - IEVIEWWINDOW ieWindow = {}; - ieWindow.iType = IEW_CREATE; - ieWindow.dwMode = IEWM_TABSRMM; - ieWindow.parent = m_pDlg.GetHwnd(); - ieWindow.cx = 10; - ieWindow.cy = 10; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - m_hwnd = ieWindow.hwnd; - } - - void Detach() override - { - IEVIEWWINDOW ieWindow = {}; - ieWindow.iType = IEW_DESTROY; - ieWindow.hwnd = m_hwnd; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - - ////////////////////////////////////////////////////////////////////////////////////// - - bool AtBottom() override - { - return false; - } - - void Clear() override - { - IEVIEWEVENT event = {}; - event.iType = IEE_CLEAR_LOG; - event.hwnd = m_hwnd; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - HWND GetHwnd() override - { - return m_hwnd; - } - - int GetType() override - { - return 2; - } - - wchar_t* GetSelection() override - { - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.iType = IEE_GET_SELECTION; - event.hContact = m_pDlg.m_hContact; - event.dwFlags = 0; - return (wchar_t *)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override - { - if (!bAppend) - Clear(); - - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.iType = IEE_LOG_DB_EVENTS; - event.hDbEventFirst = hDbEventFirst; - event.hContact = m_pDlg.m_hContact; - event.count = count; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); - } - - void CHppLogWindow::LogEvents(LOGINFO *pLog, bool) - { - IEVIEWEVENTDATA ied = {}; - ied.dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT; - - IEVIEWEVENT event = {}; - event.hwnd = m_hwnd; - event.hContact = m_pDlg.m_hContact; - event.codepage = CP_ACP; - event.iType = IEE_LOG_MEM_EVENTS; - event.eventData = &ied; - event.count = 1; - - while (pLog) { - if (pLog->ptszText) { - ied.szNick.w = pLog->ptszNick; - ied.szText.w = pLog->ptszText; - ied.time = pLog->time; - ied.bIsMe = pLog->bIsMe; - - switch (pLog->iType) { - case GC_EVENT_MESSAGE: - ied.iType = IEED_GC_EVENT_MESSAGE; - ied.dwData = IEEDD_GC_SHOW_NICK; - break; - case GC_EVENT_ACTION: - ied.iType = IEED_GC_EVENT_ACTION; - break; - case GC_EVENT_JOIN: - ied.iType = IEED_GC_EVENT_JOIN; - break; - case GC_EVENT_PART: - ied.iType = IEED_GC_EVENT_PART; - break; - case GC_EVENT_QUIT: - ied.iType = IEED_GC_EVENT_QUIT; - break; - case GC_EVENT_NICK: - ied.iType = IEED_GC_EVENT_NICK; - break; - case GC_EVENT_KICK: - ied.iType = IEED_GC_EVENT_KICK; - break; - case GC_EVENT_NOTICE: - ied.iType = IEED_GC_EVENT_NOTICE; - break; - case GC_EVENT_TOPIC: - ied.iType = IEED_GC_EVENT_TOPIC; - break; - case GC_EVENT_INFORMATION: - ied.iType = IEED_GC_EVENT_INFORMATION; - break; - case GC_EVENT_ADDSTATUS: - ied.iType = IEED_GC_EVENT_ADDSTATUS; - break; - case GC_EVENT_REMOVESTATUS: - ied.iType = IEED_GC_EVENT_REMOVESTATUS; - break; - } - ied.dwData |= g_Settings->bShowTime ? IEEDD_GC_SHOW_TIME : 0; - ied.dwData |= IEEDD_GC_SHOW_ICON; - ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK; - CallService(MS_HPP_EG_EVENT, 0, (LPARAM) & event); - } - - pLog = pLog->prev; - } - } - - void Resize() override - { - RECT rcRichEdit; - GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rcRichEdit); - - POINT pt = { rcRichEdit.left, rcRichEdit.top }; - ScreenToClient(GetParent(m_hwnd), &pt); - - IEVIEWWINDOW ieWindow = { sizeof(ieWindow) }; - ieWindow.iType = IEW_SETPOS; - ieWindow.parent = m_hwnd; - ieWindow.hwnd = m_hwnd; - ieWindow.x = pt.x; - ieWindow.y = pt.y; - ieWindow.cx = rcRichEdit.right - rcRichEdit.left; - ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top; - if (ieWindow.cx != 0 && ieWindow.cy != 0) - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); - } - - void ScrollToBottom() override - { - IEVIEWWINDOW iew = { sizeof(iew) }; - iew.iType = IEW_SCROLLBOTTOM; - iew.hwnd = m_hwnd; - CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static HANDLE hLogger; - -static CSrmmLogWindow *logBuilder(CMsgDialog &pDlg) -{ - return new CHppLogWindow(pDlg); -} - -MIR_APP_DLL(void) RegisterHppLogger() -{ - hLogger = RegisterSrmmLog(&g_plugin, "hpp", L"History++", &logBuilder); -} - -MIR_APP_DLL(void) UnregisterHppLogger() -{ - UnregisterSrmmLog(hLogger); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// CHppLogWindow class + +#include "stdafx.h" +#include "chat.h" + +#define EVENTTYPE_STATUSCHANGE 25368 +#define EVENTTYPE_ERRMSG 25366 + +class CHppLogWindow : public CSrmmLogWindow +{ + HWND m_hwnd = nullptr; + +public: + CHppLogWindow(CMsgDialog &pDlg) : + CSrmmLogWindow(pDlg) + { + } + + void Attach() override + { + IEVIEWWINDOW ieWindow = {}; + ieWindow.iType = IEW_CREATE; + ieWindow.dwMode = IEWM_TABSRMM; + ieWindow.parent = m_pDlg.GetHwnd(); + ieWindow.cx = 10; + ieWindow.cy = 10; + CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); + m_hwnd = ieWindow.hwnd; + } + + void Detach() override + { + IEVIEWWINDOW ieWindow = {}; + ieWindow.iType = IEW_DESTROY; + ieWindow.hwnd = m_hwnd; + CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); + } + + ////////////////////////////////////////////////////////////////////////////////////// + + bool AtBottom() override + { + return false; + } + + void Clear() override + { + IEVIEWEVENT event = {}; + event.iType = IEE_CLEAR_LOG; + event.hwnd = m_hwnd; + CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); + } + + HWND GetHwnd() override + { + return m_hwnd; + } + + int GetType() override + { + return 2; + } + + wchar_t* GetSelection() override + { + IEVIEWEVENT event = {}; + event.hwnd = m_hwnd; + event.iType = IEE_GET_SELECTION; + event.hContact = m_pDlg.m_hContact; + event.dwFlags = 0; + return (wchar_t *)CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); + } + + void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override + { + if (!bAppend) + Clear(); + + IEVIEWEVENT event = {}; + event.hwnd = m_hwnd; + event.iType = IEE_LOG_DB_EVENTS; + event.hDbEventFirst = hDbEventFirst; + event.hContact = m_pDlg.m_hContact; + event.count = count; + CallService(MS_HPP_EG_EVENT, 0, (LPARAM)&event); + } + + void CHppLogWindow::LogEvents(LOGINFO *pLog, bool) + { + IEVIEWEVENTDATA ied = {}; + ied.dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT; + + IEVIEWEVENT event = {}; + event.hwnd = m_hwnd; + event.hContact = m_pDlg.m_hContact; + event.codepage = CP_ACP; + event.iType = IEE_LOG_MEM_EVENTS; + event.eventData = &ied; + event.count = 1; + + while (pLog) { + if (pLog->ptszText) { + ied.szNick.w = pLog->ptszNick; + ied.szText.w = pLog->ptszText; + ied.time = pLog->time; + ied.bIsMe = pLog->bIsMe; + + switch (pLog->iType) { + case GC_EVENT_MESSAGE: + ied.iType = IEED_GC_EVENT_MESSAGE; + ied.dwData = IEEDD_GC_SHOW_NICK; + break; + case GC_EVENT_ACTION: + ied.iType = IEED_GC_EVENT_ACTION; + break; + case GC_EVENT_JOIN: + ied.iType = IEED_GC_EVENT_JOIN; + break; + case GC_EVENT_PART: + ied.iType = IEED_GC_EVENT_PART; + break; + case GC_EVENT_QUIT: + ied.iType = IEED_GC_EVENT_QUIT; + break; + case GC_EVENT_NICK: + ied.iType = IEED_GC_EVENT_NICK; + break; + case GC_EVENT_KICK: + ied.iType = IEED_GC_EVENT_KICK; + break; + case GC_EVENT_NOTICE: + ied.iType = IEED_GC_EVENT_NOTICE; + break; + case GC_EVENT_TOPIC: + ied.iType = IEED_GC_EVENT_TOPIC; + break; + case GC_EVENT_INFORMATION: + ied.iType = IEED_GC_EVENT_INFORMATION; + break; + case GC_EVENT_ADDSTATUS: + ied.iType = IEED_GC_EVENT_ADDSTATUS; + break; + case GC_EVENT_REMOVESTATUS: + ied.iType = IEED_GC_EVENT_REMOVESTATUS; + break; + } + ied.dwData |= g_Settings->bShowTime ? IEEDD_GC_SHOW_TIME : 0; + ied.dwData |= IEEDD_GC_SHOW_ICON; + ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK; + CallService(MS_HPP_EG_EVENT, 0, (LPARAM) & event); + } + + pLog = pLog->prev; + } + } + + void Resize() override + { + RECT rcRichEdit; + GetWindowRect(GetDlgItem(m_pDlg.GetHwnd(), IDC_SRMM_LOG), &rcRichEdit); + + POINT pt = { rcRichEdit.left, rcRichEdit.top }; + ScreenToClient(GetParent(m_hwnd), &pt); + + IEVIEWWINDOW ieWindow = { sizeof(ieWindow) }; + ieWindow.iType = IEW_SETPOS; + ieWindow.parent = m_hwnd; + ieWindow.hwnd = m_hwnd; + ieWindow.x = pt.x; + ieWindow.y = pt.y; + ieWindow.cx = rcRichEdit.right - rcRichEdit.left; + ieWindow.cy = rcRichEdit.bottom - rcRichEdit.top; + if (ieWindow.cx != 0 && ieWindow.cy != 0) + CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&ieWindow); + } + + void ScrollToBottom() override + { + IEVIEWWINDOW iew = { sizeof(iew) }; + iew.iType = IEW_SCROLLBOTTOM; + iew.hwnd = m_hwnd; + CallService(MS_HPP_EG_WINDOW, 0, (LPARAM)&iew); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +static HANDLE hLogger; + +static CSrmmLogWindow *logBuilder(CMsgDialog &pDlg) +{ + return new CHppLogWindow(pDlg); +} + +MIR_APP_DLL(void) RegisterHppLogger() +{ + hLogger = RegisterSrmmLog(&g_plugin, "hpp", L"History++", &logBuilder); +} + +MIR_APP_DLL(void) UnregisterHppLogger() +{ + UnregisterSrmmLog(hLogger); +} diff --git a/src/mir_app/src/srmm_log_rtf.cpp b/src/mir_app/src/srmm_log_rtf.cpp index 01d8b7c5c8..c6bf86af64 100644 --- a/src/mir_app/src/srmm_log_rtf.cpp +++ b/src/mir_app/src/srmm_log_rtf.cpp @@ -1,408 +1,408 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -///////////////////////////////////////////////////////////////////////////////////////// -// SRMM log container - -#include "stdafx.h" -#include "chat.h" - -#define EVENTTYPE_STATUSCHANGE 25368 -#define EVENTTYPE_ERRMSG 25366 - -CRtfLogWindow::CRtfLogWindow(CMsgDialog &pDlg) : - CSrmmLogWindow(pDlg), - m_rtf(*(CCtrlRichEdit*)pDlg.FindControl(IDC_SRMM_LOG)) -{ -} - -CRtfLogWindow::~CRtfLogWindow() -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CRtfLogWindow *pLog = (CRtfLogWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (pLog != nullptr) - return pLog->WndProc(msg, wParam, lParam); - - return mir_callNextSubclass(hwnd, stubLogProc, msg, wParam, lParam); -} - -void CRtfLogWindow::Attach() -{ - SetWindowLongPtr(m_rtf.GetHwnd(), GWLP_USERDATA, LPARAM(this)); - m_rtf.SetReadOnly(true); - - mir_subclassWindow(m_rtf.GetHwnd(), stubLogProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CRtfLogWindow::Detach() -{ - mir_unsubclassWindow(m_rtf.GetHwnd(), stubLogProc); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CRtfLogWindow::AtBottom() -{ - if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) - return false; - - SCROLLINFO si = {}; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); - return (si.nPos + (int)si.nPage + 5) >= si.nMax; -} - -void CRtfLogWindow::Clear() -{ - m_rtf.SetText(L""); -} - -HWND CRtfLogWindow::GetHwnd() -{ - return m_rtf.GetHwnd(); -} - -int CRtfLogWindow::GetType() -{ - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - CMStringW *str = (CMStringW *)dwCookie; - str->Append((wchar_t*)pbBuff, cb / 2); - *pcb = cb; - return 0; -} - -wchar_t* CRtfLogWindow::GetSelection() -{ - CHARRANGE sel; - SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) - return nullptr; - - CMStringW result; - - EDITSTREAM stream; - memset(&stream, 0, sizeof(stream)); - stream.pfnCallback = StreamOutCallback; - stream.dwCookie = (DWORD_PTR)&result; - SendMessage(m_rtf.GetHwnd(), EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream); - return result.Detach(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam) -{ - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr->code != EN_LINK) - return FALSE; - - ENLINK *pLink = (ENLINK *)lParam; - switch (pLink->msg) { - case WM_SETCURSOR: - SetCursor(g_hCurHyperlinkHand); - SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); - return TRUE; - - case WM_RBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - CHARRANGE sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) - break; - - CMStringW wszText(' ', pLink->chrg.cpMax - pLink->chrg.cpMin + 1); - - TEXTRANGE tr; - tr.chrg = pLink->chrg; - tr.lpstrText = wszText.GetBuffer(); - m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (wcschr(tr.lpstrText, '@') != nullptr && wcschr(tr.lpstrText, ':') == nullptr && wcschr(tr.lpstrText, '/') == nullptr) - wszText.Insert(0, L"mailto:"); - - if (pLink->msg == WM_RBUTTONDOWN) { - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); - HMENU hSubMenu = GetSubMenu(hMenu, 6); - TranslateMenu(hSubMenu); - - POINT pt = { GET_X_LPARAM(pLink->lParam), GET_Y_LPARAM(pLink->lParam) }; - ClientToScreen(((NMHDR *)lParam)->hwndFrom, &pt); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) { - case IDM_OPENLINK: - Utils_OpenUrlW(wszText); - break; - - case IDM_COPYLINK: - Utils_ClipboardCopy(wszText); - break; - } - - DestroyMenu(hMenu); - SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); - return TRUE; - } - - Utils_OpenUrlW(wszText); - SetFocus(m_pDlg.m_message.GetHwnd()); - } - - return FALSE; -} - -void CRtfLogWindow::Resize() -{ - bool bottomScroll = !m_pDlg.isChat(); - if (AtBottom()) - bottomScroll = true; - - // ::MoveWindow(m_rtf.GetHwnd(), x, y, cx, cy, true); - - if (bottomScroll) - ScrollToBottom(); -} - -void CRtfLogWindow::ScrollToBottom() -{ - if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) - return; - - SCROLLINFO si = {}; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_RANGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); - - si.fMask = SIF_POS; - si.nPos = si.nMax - si.nPage; - SetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si, TRUE); - m_rtf.SendMsg(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n"; - -INT_PTR CRtfLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - CHARRANGE sel; - - switch (msg) { - case WM_ACTIVATE: - if (LOWORD(wParam) == WA_INACTIVE) { - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) { - sel.cpMin = sel.cpMax; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - } - } - break; - - case WM_SETCURSOR: - if (m_pDlg.m_bInMenu) { - SetCursor(LoadCursor(nullptr, IDC_ARROW)); - return TRUE; - } - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (!(GetKeyState(VK_RMENU) & 0x8000)) { - MSG message = { m_pDlg.m_hwnd, msg, wParam, lParam }; - LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); - if (iButtonFrom) { - Srmm_ProcessToolbarHotkey(m_pDlg.m_hContact, iButtonFrom, m_pDlg.m_hwnd); - return TRUE; - } - } - break; - - case WM_CHAR: - if (wParam >= ' ') { - SetFocus(m_pDlg.m_message.GetHwnd()); - m_pDlg.m_message.SendMsg(WM_CHAR, wParam, lParam); - } - else if (wParam == '\t') - SetFocus(m_pDlg.m_message.GetHwnd()); - break; - - case WM_CONTEXTMENU: - POINT pt, ptl; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (lParam == 0xFFFFFFFF) { - m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); - ClientToScreen(m_rtf.GetHwnd(), &pt); - } - else { - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - } - ptl = pt; - ScreenToClient(m_rtf.GetHwnd(), &ptl); - { - wchar_t *pszWord = (wchar_t *)_alloca(8192); - pszWord[0] = '\0'; - - // get a word under cursor - if (sel.cpMin == sel.cpMax) { - int iCharIndex = m_rtf.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl); - if (iCharIndex < 0) - break; - - sel.cpMin = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex); - sel.cpMax = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex); - } - - if (sel.cpMax > sel.cpMin) { - TEXTRANGE tr = { 0 }; - tr.chrg = sel; - tr.lpstrText = pszWord; - int iRes = m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); - if (iRes > 0) { - wchar_t *p = wcschr(pszWord, '\r'); - if (p) - *p = 0; - - size_t iLen = mir_wstrlen(pszWord) - 1; - while (wcschr(szTrimString, pszWord[iLen])) { - pszWord[iLen] = '\0'; - iLen--; - } - } - } - - CHARRANGE all = { 0, -1 }; - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_LOGMENU)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - m_pDlg.m_bInMenu = true; - - int flags = MF_BYPOSITION | (GetRichTextLength(m_rtf.GetHwnd()) == 0 ? MF_GRAYED : MF_ENABLED); - EnableMenuItem(hSubMenu, 0, flags); - EnableMenuItem(hSubMenu, 2, flags); - - if (pszWord && pszWord[0]) { - CMStringW wszText(FORMAT, TranslateT("Look up '%s':"), pszWord); - if (wszText.GetLength() > 30) { - wszText.Truncate(30); - wszText.AppendChar('\''); - } - ModifyMenu(hSubMenu, 4, MF_STRING | MF_BYPOSITION, 4, wszText); - } - else ModifyMenu(hSubMenu, 4, MF_STRING | MF_GRAYED | MF_BYPOSITION, 4, TranslateT("No word to look up")); - - UINT uID = Chat_CreateMenu(m_rtf.GetHwnd(), hSubMenu, pt, m_pDlg.m_si, nullptr); - m_pDlg.m_bInMenu = false; - DestroyMenu(hMenu); - - switch (uID) { - case 0: - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_COPYALL: - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); - m_rtf.SendMsg(WM_COPY, 0, 0); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_CLEAR: - m_rtf.SetText(L""); - if (auto *si = m_pDlg.m_si) { - g_chatApi.LM_RemoveAll(&si->pLog, &si->pLogEnd); - si->iEventCount = 0; - si->LastTime = 0; - } - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - case IDM_SEARCH_GOOGLE: - case IDM_SEARCH_BING: - case IDM_SEARCH_YANDEX: - case IDM_SEARCH_YAHOO: - case IDM_SEARCH_WIKIPEDIA: - case IDM_SEARCH_FOODNETWORK: - case IDM_SEARCH_GOOGLE_MAPS: - case IDM_SEARCH_GOOGLE_TRANSLATE: - { - CMStringW szURL; - switch (uID) { - case IDM_SEARCH_WIKIPEDIA: - szURL.Format(L"http://en.wikipedia.org/wiki/%s", pszWord); - break; - case IDM_SEARCH_YAHOO: - szURL.Format(L"http://search.yahoo.com/search?p=%s&ei=UTF-8", pszWord); - break; - case IDM_SEARCH_FOODNETWORK: - szURL.Format(L"http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", pszWord); - break; - case IDM_SEARCH_BING: - szURL.Format(L"http://www.bing.com/search?q=%s&form=OSDSRC", pszWord); - break; - case IDM_SEARCH_GOOGLE_MAPS: - szURL.Format(L"http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - case IDM_SEARCH_GOOGLE_TRANSLATE: - szURL.Format(L"http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - case IDM_SEARCH_YANDEX: - szURL.Format(L"http://yandex.ru/yandsearch?text=%s", pszWord); - break; - case IDM_SEARCH_GOOGLE: - szURL.Format(L"http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", pszWord); - break; - } - Utils_OpenUrlW(szURL); - } - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - break; - - default: - PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); - Chat_DoEventHook(m_pDlg.m_si, GC_USER_LOGMENU, nullptr, nullptr, uID); - break; - } - } - return 0; - } - - LRESULT res = mir_callNextSubclass(m_rtf.GetHwnd(), stubLogProc, msg, wParam, lParam); - if (msg == WM_GETDLGCODE) - return res & ~DLGC_HASSETSEL; - return res; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// SRMM log container + +#include "stdafx.h" +#include "chat.h" + +#define EVENTTYPE_STATUSCHANGE 25368 +#define EVENTTYPE_ERRMSG 25366 + +CRtfLogWindow::CRtfLogWindow(CMsgDialog &pDlg) : + CSrmmLogWindow(pDlg), + m_rtf(*(CCtrlRichEdit*)pDlg.FindControl(IDC_SRMM_LOG)) +{ +} + +CRtfLogWindow::~CRtfLogWindow() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +EXTERN_C MIR_APP_DLL(LRESULT) CALLBACK stubLogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CRtfLogWindow *pLog = (CRtfLogWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (pLog != nullptr) + return pLog->WndProc(msg, wParam, lParam); + + return mir_callNextSubclass(hwnd, stubLogProc, msg, wParam, lParam); +} + +void CRtfLogWindow::Attach() +{ + SetWindowLongPtr(m_rtf.GetHwnd(), GWLP_USERDATA, LPARAM(this)); + m_rtf.SetReadOnly(true); + + mir_subclassWindow(m_rtf.GetHwnd(), stubLogProc); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CRtfLogWindow::Detach() +{ + mir_unsubclassWindow(m_rtf.GetHwnd(), stubLogProc); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CRtfLogWindow::AtBottom() +{ + if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) + return false; + + SCROLLINFO si = {}; + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); + return (si.nPos + (int)si.nPage + 5) >= si.nMax; +} + +void CRtfLogWindow::Clear() +{ + m_rtf.SetText(L""); +} + +HWND CRtfLogWindow::GetHwnd() +{ + return m_rtf.GetHwnd(); +} + +int CRtfLogWindow::GetType() +{ + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static DWORD CALLBACK StreamOutCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + CMStringW *str = (CMStringW *)dwCookie; + str->Append((wchar_t*)pbBuff, cb / 2); + *pcb = cb; + return 0; +} + +wchar_t* CRtfLogWindow::GetSelection() +{ + CHARRANGE sel; + SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) + return nullptr; + + CMStringW result; + + EDITSTREAM stream; + memset(&stream, 0, sizeof(stream)); + stream.pfnCallback = StreamOutCallback; + stream.dwCookie = (DWORD_PTR)&result; + SendMessage(m_rtf.GetHwnd(), EM_STREAMOUT, SF_TEXT | SF_UNICODE | SFF_SELECTION, (LPARAM)&stream); + return result.Detach(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam) +{ + LPNMHDR hdr = (LPNMHDR)lParam; + if (hdr->code != EN_LINK) + return FALSE; + + ENLINK *pLink = (ENLINK *)lParam; + switch (pLink->msg) { + case WM_SETCURSOR: + SetCursor(g_hCurHyperlinkHand); + SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); + return TRUE; + + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + CHARRANGE sel; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) + break; + + CMStringW wszText(' ', pLink->chrg.cpMax - pLink->chrg.cpMin + 1); + + TEXTRANGE tr; + tr.chrg = pLink->chrg; + tr.lpstrText = wszText.GetBuffer(); + m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); + if (wcschr(tr.lpstrText, '@') != nullptr && wcschr(tr.lpstrText, ':') == nullptr && wcschr(tr.lpstrText, '/') == nullptr) + wszText.Insert(0, L"mailto:"); + + if (pLink->msg == WM_RBUTTONDOWN) { + HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); + HMENU hSubMenu = GetSubMenu(hMenu, 6); + TranslateMenu(hSubMenu); + + POINT pt = { GET_X_LPARAM(pLink->lParam), GET_Y_LPARAM(pLink->lParam) }; + ClientToScreen(((NMHDR *)lParam)->hwndFrom, &pt); + + switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) { + case IDM_OPENLINK: + Utils_OpenUrlW(wszText); + break; + + case IDM_COPYLINK: + Utils_ClipboardCopy(wszText); + break; + } + + DestroyMenu(hMenu); + SetWindowLongPtr(m_pDlg.m_hwnd, DWLP_MSGRESULT, TRUE); + return TRUE; + } + + Utils_OpenUrlW(wszText); + SetFocus(m_pDlg.m_message.GetHwnd()); + } + + return FALSE; +} + +void CRtfLogWindow::Resize() +{ + bool bottomScroll = !m_pDlg.isChat(); + if (AtBottom()) + bottomScroll = true; + + // ::MoveWindow(m_rtf.GetHwnd(), x, y, cx, cy, true); + + if (bottomScroll) + ScrollToBottom(); +} + +void CRtfLogWindow::ScrollToBottom() +{ + if (!(GetWindowLongPtr(m_rtf.GetHwnd(), GWL_STYLE) & WS_VSCROLL)) + return; + + SCROLLINFO si = {}; + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_RANGE; + GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si); + + si.fMask = SIF_POS; + si.nPos = si.nMax - si.nPage; + SetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &si, TRUE); + m_rtf.SendMsg(WM_VSCROLL, MAKEWPARAM(SB_BOTTOM, 0), 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static wchar_t szTrimString[] = L":;,.!?\'\"><()[]- \r\n"; + +INT_PTR CRtfLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + CHARRANGE sel; + + switch (msg) { + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) { + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) { + sel.cpMin = sel.cpMax; + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + } + } + break; + + case WM_SETCURSOR: + if (m_pDlg.m_bInMenu) { + SetCursor(LoadCursor(nullptr, IDC_ARROW)); + return TRUE; + } + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!(GetKeyState(VK_RMENU) & 0x8000)) { + MSG message = { m_pDlg.m_hwnd, msg, wParam, lParam }; + LRESULT iButtonFrom = Hotkey_Check(&message, BB_HK_SECTION); + if (iButtonFrom) { + Srmm_ProcessToolbarHotkey(m_pDlg.m_hContact, iButtonFrom, m_pDlg.m_hwnd); + return TRUE; + } + } + break; + + case WM_CHAR: + if (wParam >= ' ') { + SetFocus(m_pDlg.m_message.GetHwnd()); + m_pDlg.m_message.SendMsg(WM_CHAR, wParam, lParam); + } + else if (wParam == '\t') + SetFocus(m_pDlg.m_message.GetHwnd()); + break; + + case WM_CONTEXTMENU: + POINT pt, ptl; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + if (lParam == 0xFFFFFFFF) { + m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); + ClientToScreen(m_rtf.GetHwnd(), &pt); + } + else { + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + } + ptl = pt; + ScreenToClient(m_rtf.GetHwnd(), &ptl); + { + wchar_t *pszWord = (wchar_t *)_alloca(8192); + pszWord[0] = '\0'; + + // get a word under cursor + if (sel.cpMin == sel.cpMax) { + int iCharIndex = m_rtf.SendMsg(EM_CHARFROMPOS, 0, (LPARAM)&ptl); + if (iCharIndex < 0) + break; + + sel.cpMin = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_LEFT, iCharIndex); + sel.cpMax = m_rtf.SendMsg(EM_FINDWORDBREAK, WB_RIGHT, iCharIndex); + } + + if (sel.cpMax > sel.cpMin) { + TEXTRANGE tr = { 0 }; + tr.chrg = sel; + tr.lpstrText = pszWord; + int iRes = m_rtf.SendMsg(EM_GETTEXTRANGE, 0, (LPARAM)&tr); + if (iRes > 0) { + wchar_t *p = wcschr(pszWord, '\r'); + if (p) + *p = 0; + + size_t iLen = mir_wstrlen(pszWord) - 1; + while (wcschr(szTrimString, pszWord[iLen])) { + pszWord[iLen] = '\0'; + iLen--; + } + } + } + + CHARRANGE all = { 0, -1 }; + HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_LOGMENU)); + HMENU hSubMenu = GetSubMenu(hMenu, 0); + TranslateMenu(hSubMenu); + m_pDlg.m_bInMenu = true; + + int flags = MF_BYPOSITION | (GetRichTextLength(m_rtf.GetHwnd()) == 0 ? MF_GRAYED : MF_ENABLED); + EnableMenuItem(hSubMenu, 0, flags); + EnableMenuItem(hSubMenu, 2, flags); + + if (pszWord && pszWord[0]) { + CMStringW wszText(FORMAT, TranslateT("Look up '%s':"), pszWord); + if (wszText.GetLength() > 30) { + wszText.Truncate(30); + wszText.AppendChar('\''); + } + ModifyMenu(hSubMenu, 4, MF_STRING | MF_BYPOSITION, 4, wszText); + } + else ModifyMenu(hSubMenu, 4, MF_STRING | MF_GRAYED | MF_BYPOSITION, 4, TranslateT("No word to look up")); + + UINT uID = Chat_CreateMenu(m_rtf.GetHwnd(), hSubMenu, pt, m_pDlg.m_si, nullptr); + m_pDlg.m_bInMenu = false; + DestroyMenu(hMenu); + + switch (uID) { + case 0: + PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + case IDM_COPYALL: + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); + m_rtf.SendMsg(WM_COPY, 0, 0); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + case IDM_CLEAR: + m_rtf.SetText(L""); + if (auto *si = m_pDlg.m_si) { + g_chatApi.LM_RemoveAll(&si->pLog, &si->pLogEnd); + si->iEventCount = 0; + si->LastTime = 0; + } + PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + case IDM_SEARCH_GOOGLE: + case IDM_SEARCH_BING: + case IDM_SEARCH_YANDEX: + case IDM_SEARCH_YAHOO: + case IDM_SEARCH_WIKIPEDIA: + case IDM_SEARCH_FOODNETWORK: + case IDM_SEARCH_GOOGLE_MAPS: + case IDM_SEARCH_GOOGLE_TRANSLATE: + { + CMStringW szURL; + switch (uID) { + case IDM_SEARCH_WIKIPEDIA: + szURL.Format(L"http://en.wikipedia.org/wiki/%s", pszWord); + break; + case IDM_SEARCH_YAHOO: + szURL.Format(L"http://search.yahoo.com/search?p=%s&ei=UTF-8", pszWord); + break; + case IDM_SEARCH_FOODNETWORK: + szURL.Format(L"http://search.foodnetwork.com/search/delegate.do?fnSearchString=%s", pszWord); + break; + case IDM_SEARCH_BING: + szURL.Format(L"http://www.bing.com/search?q=%s&form=OSDSRC", pszWord); + break; + case IDM_SEARCH_GOOGLE_MAPS: + szURL.Format(L"http://maps.google.com/maps?q=%s&ie=utf-8&oe=utf-8", pszWord); + break; + case IDM_SEARCH_GOOGLE_TRANSLATE: + szURL.Format(L"http://translate.google.com/?q=%s&ie=utf-8&oe=utf-8", pszWord); + break; + case IDM_SEARCH_YANDEX: + szURL.Format(L"http://yandex.ru/yandsearch?text=%s", pszWord); + break; + case IDM_SEARCH_GOOGLE: + szURL.Format(L"http://www.google.com/search?q=%s&ie=utf-8&oe=utf-8", pszWord); + break; + } + Utils_OpenUrlW(szURL); + } + PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); + break; + + default: + PostMessage(m_pDlg.m_hwnd, WM_MOUSEACTIVATE, 0, 0); + Chat_DoEventHook(m_pDlg.m_si, GC_USER_LOGMENU, nullptr, nullptr, uID); + break; + } + } + return 0; + } + + LRESULT res = mir_callNextSubclass(m_rtf.GetHwnd(), stubLogProc, msg, wParam, lParam); + if (msg == WM_GETDLGCODE) + return res & ~DLGC_HASSETSEL; + return res; +} diff --git a/src/mir_app/src/srmm_statusicon.cpp b/src/mir_app/src/srmm_statusicon.cpp index 1324a30eb7..12a613610e 100644 --- a/src/mir_app/src/srmm_statusicon.cpp +++ b/src/mir_app/src/srmm_statusicon.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/srmm_toolbar.cpp b/src/mir_app/src/srmm_toolbar.cpp index f833ea16f9..fb593ea1a4 100644 --- a/src/mir_app/src/srmm_toolbar.cpp +++ b/src/mir_app/src/srmm_toolbar.cpp @@ -1,886 +1,886 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "chat.h" -#include "skin.h" - -#define BB_MODULE_NAME "SRMM_Toolbar" - -#define DPISCALEY_S(argY) ((int)((double)(argY) * g_DPIscaleY)) -#define DPISCALEX_S(argX) ((int)((double)(argX) * g_DPIscaleX)) - -static double g_DPIscaleX, g_DPIscaleY; -static class CSrmmToolbarOptions *g_pDialog = nullptr; - -static CMOption g_iButtonGap(BB_MODULE_NAME, "ButtonsBarGap", 1); - -static int SortButtons(const CustomButtonData *p1, const CustomButtonData *p2) -{ - if (p1->m_bRSided != p2->m_bRSided) - return (p2->m_bRSided) ? -1 : 1; - if (p1->m_dwPosition != p2->m_dwPosition) - return p1->m_dwPosition - p2->m_dwPosition; - int res = mir_strcmp(p1->m_pszModuleName, p2->m_pszModuleName); - if (res != 0) - return res; - return p1->m_dwButtonID - p2->m_dwButtonID; -} - -static LIST arButtonsList(1, SortButtons); - -int LastCID = MIN_CBUTTONID; -int dwSepCount = 0; - -static mir_cs csToolBar; -static HANDLE hHookToolBarLoadedEvt, hHookButtonPressedEvt; - -static int sstSortButtons(const void *p1, const void *p2) -{ - return SortButtons(*(CustomButtonData**)p1, *(CustomButtonData**)p2); -} - -static void CB_RegisterSeparators() -{ - BBButton bbd = {}; - bbd.pszModuleName = "Tabsrmm_sep"; - for (int i = 0; dwSepCount > i; i++) { - bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; - bbd.dwButtonID = i + 1; - bbd.dwDefPos = 410 + i; - Srmm_AddButton(&bbd, &g_plugin); - } -} - -MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i) -{ - return arButtonsList[i]; -} - -MIR_APP_DLL(int) Srmm_GetButtonCount(void) -{ - return arButtonsList.getCount(); -} - -MIR_APP_DLL(int) Srmm_GetButtonGap() -{ - return g_iButtonGap; -} - -MIR_APP_DLL(int) Srmm_GetButtonState(HWND hwndDlg, BBButton *bbdi) -{ - if (hwndDlg == nullptr || bbdi == nullptr) - return 1; - - uint32_t tempCID = 0; - bbdi->bbbFlags = 0; - for (auto &cbd : arButtonsList) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { - tempCID = cbd->m_dwButtonCID; - break; - } - - if (!tempCID) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED); - return 0; -} - -MIR_APP_DLL(int) Srmm_SetButtonState(MCONTACT hContact, BBButton *bbdi) -{ - if (hContact == 0 || bbdi == nullptr) - return 1; - - uint32_t tempCID = 0; - for (auto &cbd : arButtonsList) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { - tempCID = cbd->m_dwButtonCID; - break; - } - - if (!tempCID) - return 1; - - HWND hwndDlg = WindowList_Find(g_hWindowList, hContact); - if (hwndDlg == nullptr) - return 1; - - HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); - if (hwndBtn == nullptr) - return 1; - - SetWindowTextA(hwndBtn, bbdi->pszModuleName); - if (bbdi->hIcon) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(bbdi->hIcon)); - if (bbdi->pwszTooltip) - SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)bbdi->pwszTooltip, BATF_UNICODE); - if (bbdi->bbbFlags) { - ShowWindow(hwndBtn, (bbdi->bbbFlags & BBSF_HIDDEN) ? SW_HIDE : SW_SHOW); - EnableWindow(hwndBtn, !(bbdi->bbbFlags & BBSF_DISABLED)); - Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_PUSHED) != 0); - Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_RELEASED) == 0); - } - return 0; -} - -MIR_APP_DLL(int) Srmm_ModifyButton(BBButton *bbdi) -{ - if (!bbdi) - return 1; - - CustomButtonData *cbd = nullptr; - { - mir_cslock lck(csToolBar); - - for (auto &p : arButtonsList) - if (!mir_strcmp(p->m_pszModuleName, bbdi->pszModuleName) && (p->m_dwButtonID == bbdi->dwButtonID)) { - cbd = p; - break; - } - - if (cbd != nullptr) { - if (bbdi->pwszTooltip) - cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); - if (bbdi->hIcon) - cbd->m_hIcon = bbdi->hIcon; - if (bbdi->bbbFlags) { - cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - } - } - } - - if (cbd != nullptr) - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, (LPARAM)cbd); - return 0; -} - -MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndDlg, BOOL code) -{ - bool bFromArrow = false; - HWND hwndFrom = nullptr; - - CustomButtonClickData cbcd = {}; - - for (auto &cbd : arButtonsList) { - if (cbd->m_dwButtonCID == idFrom) { - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, idFrom); - } - else if (cbd->m_dwArrowCID == idFrom) { - bFromArrow = true; - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, idFrom-1); - } - } - - if (hwndFrom == nullptr) - return; - - RECT rc; - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - cbcd.hwndFrom = GetParent(hwndFrom); - cbcd.hContact = hContact; - cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); -} - -void Srmm_ProcessToolbarHotkey(MCONTACT hContact, INT_PTR iButtonFrom, HWND hwndDlg) -{ - HWND hwndFrom = nullptr; - - CustomButtonClickData cbcd = {}; - - for (auto &cbd : arButtonsList) { - if (cbd->m_hotkey == nullptr || cbd->m_bDisabled) - continue; - - if (cbd->m_hotkey->lParam == iButtonFrom) { - cbcd.pszModule = cbd->m_pszModuleName; - cbcd.dwButtonId = cbd->m_dwButtonID; - hwndFrom = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - break; - } - } - - if (hwndFrom == nullptr) - return; - - RECT rc; - GetWindowRect(hwndFrom, &rc); - cbcd.pt.x = rc.left; - cbcd.pt.y = rc.bottom; - - cbcd.hwndFrom = GetParent(hwndFrom); - cbcd.hContact = hContact; - cbcd.flags = (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0); - - NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); -} - -MIR_APP_DLL(void) Srmm_ResetToolbar() -{ - for (auto &cbd : arButtonsList) { - cbd->m_dwPosition = cbd->m_dwOrigPosition; - cbd->m_bRSided = cbd->m_dwOrigFlags.bit1; - cbd->m_bIMButton = cbd->m_dwOrigFlags.bit2; - cbd->m_bChatButton = cbd->m_dwOrigFlags.bit3; - cbd->m_bCanBeHidden = cbd->m_dwOrigFlags.bit4; - } -} - -void Srmm_CreateToolbarIcons(HWND hwndDlg, int flags) -{ - HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hwndDlg, GWLP_HINSTANCE); - - CDlgBase *pDlg = CDlgBase::Find(hwndDlg); - - for (auto &cbd : arButtonsList) { - if (cbd->m_bSeparator) - continue; - - HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if ((flags & BBBF_ISIMBUTTON) && cbd->m_bIMButton || (flags & BBBF_ISCHATBUTTON) && cbd->m_bChatButton) { - if (hwndButton == nullptr) { - hwndButton = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, cbd->m_iButtonWidth, DPISCALEX_S(22), hwndDlg, (HMENU)cbd->m_dwButtonCID, hInstance, nullptr); - if (hwndButton == nullptr) // smth went wrong - continue; - - // if there's a pre-created button control in a class, initialize it - if (pDlg != nullptr) { - CCtrlBase *pControl = (*pDlg)[cbd->m_dwButtonCID]; - if (pControl) - pControl->OnInit(); - } - } - SendMessage(hwndButton, BUTTONSETASFLATBTN, TRUE, 0); - if (cbd->m_pwszText) - SetWindowTextW(hwndButton, cbd->m_pwszText); - if (cbd->m_pwszTooltip) - SendMessage(hwndButton, BUTTONADDTOOLTIP, LPARAM(cbd->m_pwszTooltip), BATF_UNICODE); - if (cbd->m_hIcon) - SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); - - if (cbd->m_dwArrowCID) - SendMessage(hwndButton, BUTTONSETARROW, cbd->m_dwArrowCID, 0); - if (cbd->m_bPushButton) - SendMessage(hwndButton, BUTTONSETASPUSHBTN, TRUE, 0); - - if (cbd->m_bDisabled) - EnableWindow(hwndButton, FALSE); - if (cbd->m_bHidden) - ShowWindow(hwndButton, SW_HIDE); - } - else if (hwndButton) - DestroyWindow(hwndButton); - } -} - -MIR_APP_DLL(void) Srmm_UpdateToolbarIcons(HWND hwndDlg) -{ - for (auto &cbd : arButtonsList) { - if (cbd->m_bSeparator || cbd->m_hIcon == nullptr) - continue; - - HWND hwndBtn = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if (hwndBtn) - SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); - } -} - -MIR_APP_DLL(void) Srmm_RedrawToolbarIcons(HWND hwndDlg) -{ - for (auto &cbd : arButtonsList) { - HWND hwnd = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); - if (hwnd) - InvalidateRect(hwnd, nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void CB_ReInitCustomButtons() -{ - for (auto &cbd : arButtonsList.rev_iter()) - if (cbd->m_opFlags & (BBSF_NTBSWAPED | BBSF_NTBDESTRUCT)) { - cbd->m_opFlags ^= BBSF_NTBSWAPED; - - if (cbd->m_opFlags & BBSF_NTBDESTRUCT) - arButtonsList.removeItem(&cbd); - } - - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - - WindowList_Broadcast(g_hWindowList, WM_CBD_RECREATE, 0, 0); - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); - WindowList_Broadcast(g_hWindowList, WM_CBD_LOADICONS, 0, 0); -} - -static void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd) -{ - char SettingName[1024]; - char SettingParameter[1024]; - - //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - - mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); - mir_snprintf(SettingParameter, "%d_%u_%u_%u_%u_%u", cbd->m_dwPosition, cbd->m_bIMButton, cbd->m_bChatButton, 0, cbd->m_bRSided, cbd->m_bCanBeHidden); - if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) - db_set_s(hContact, BB_MODULE_NAME, SettingName, SettingParameter); - else - db_unset(hContact, BB_MODULE_NAME, SettingName); -} - -#define MIDDLE_SEPARATOR L">-------M-------<" - -class CSrmmToolbarOptions : public CDlgBase -{ - CCtrlTreeView m_toolBar; - CCtrlCheck m_btnIM, m_btnChat, m_btnHidden; - CCtrlButton m_btnReset, m_btnSeparator; - CCtrlSpin m_gap; - CTimer timer; - - HIMAGELIST m_hImgl; - - void SaveTree() - { - bool RSide = false; - int count = 10; - uint32_t loc_sepcout = 0; - wchar_t strbuf[128]; - - TVITEMEX tvi; - tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE; - tvi.hItem = m_toolBar.GetRoot(); - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - { - mir_cslock lck(csToolBar); - - while (tvi.hItem != nullptr) { - m_toolBar.GetItem(&tvi); - - if (mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) { - RSide = true; - count = m_toolBar.GetCount() * 10 - count; - tvi.hItem = m_toolBar.GetNextSibling(tvi.hItem); - continue; - } - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - if (cbd && arButtonsList.indexOf(cbd) != -1) { - if (cbd->m_opFlags) { - cbd->m_bIMButton = (cbd->m_opFlags & BBSF_IMBUTTON) != 0; - cbd->m_bChatButton = (cbd->m_opFlags & BBSF_CHATBUTTON) != 0; - cbd->m_bCanBeHidden = (cbd->m_opFlags & BBSF_CANBEHIDDEN) != 0; - } - - if (RSide && !cbd->m_bRSided) { - cbd->m_bRSided = true; - cbd->m_opFlags |= BBSF_NTBSWAPED; - } - else if (!RSide && cbd->m_bRSided) { - cbd->m_bRSided = false; - cbd->m_opFlags |= BBSF_NTBSWAPED; - } - - if (!cbd->m_bCantBeHidden && !m_toolBar.GetCheckState(tvi.hItem)) { - cbd->m_bIMButton = false; - cbd->m_bChatButton = false; - - if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) - cbd->m_opFlags = BBSF_NTBDESTRUCT; - } - else { - if (!cbd->m_bIMButton && !cbd->m_bChatButton) - cbd->m_bIMButton = true; - if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) { - cbd->m_bHidden = false; - cbd->m_opFlags &= ~BBSF_NTBDESTRUCT; - ++loc_sepcout; - } - } - - cbd->m_dwPosition = (uint32_t)count; - CB_WriteButtonSettings(0, cbd); - - if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) - (RSide) ? (count -= 10) : (count += 10); - } - - HTREEITEM hItem = m_toolBar.GetNextSibling(tvi.hItem); - if (cbd->m_opFlags & BBSF_NTBDESTRUCT) - m_toolBar.DeleteItem(tvi.hItem); - tvi.hItem = hItem; - } - - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - } - db_set_dw(0, BB_MODULE_NAME, "SeparatorsCount", loc_sepcout); - dwSepCount = loc_sepcout; - } - - void BuildMenuObjectsTree() - { - m_toolBar.DeleteAllItems(); - - m_hImgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2); - ImageList_AddIcon(m_hImgl, Skin_LoadIcon(SKINICON_OTHER_SMALLDOT)); - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); - m_toolBar.SetImageList(m_hImgl, TVSIL_NORMAL); - - if (arButtonsList.getCount() == 0) - return; - - bool bPrevSide = false; - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE; - - mir_cslock lck(csToolBar); - for (auto &cbd : arButtonsList) { - if (bPrevSide != cbd->m_bRSided) { - bPrevSide = true; - - TVINSERTSTRUCT tvis2 = {}; - tvis.hInsertAfter = TVI_LAST; - tvis2.itemex.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_STATE | TVIF_STATEEX; - tvis2.itemex.pszText = MIDDLE_SEPARATOR; - tvis2.itemex.stateMask = TVIS_BOLD; - tvis2.itemex.state = TVIS_BOLD; - tvis2.itemex.iImage = tvis.item.iSelectedImage = -1; - tvis2.itemex.uStateEx = TVIS_EX_DISABLED; - tvis.hInsertAfter = m_toolBar.InsertItem(&tvis2); - m_toolBar.SetItemState(tvis.hInsertAfter, 0x3000, TVIS_STATEIMAGEMASK); - } - - tvis.item.lParam = (LPARAM)cbd; - - if (cbd->m_bSeparator) { - tvis.item.pszText = TranslateT(""); - tvis.item.iImage = tvis.item.iSelectedImage = 0; - } - else { - tvis.item.pszText = TranslateW(cbd->m_pwszTooltip); - tvis.item.iImage = tvis.item.iSelectedImage = ImageList_AddIcon(m_hImgl, IcoLib_GetIconByHandle(cbd->m_hIcon)); - } - cbd->m_opFlags = 0; - HTREEITEM hti = m_toolBar.InsertItem(&tvis); - - m_toolBar.SetCheckState(hti, (cbd->m_bIMButton || cbd->m_bChatButton)); - if (cbd->m_bCantBeHidden) - m_toolBar.SetItemState(hti, 0x3000, TVIS_STATEIMAGEMASK); - } - } - -public: - CSrmmToolbarOptions() : - CDlgBase(g_plugin, IDD_OPT_TOOLBAR), - m_gap(this, IDC_SPIN1, 10), - m_btnIM(this, IDC_IMCHECK), - m_btnChat(this, IDC_CHATCHECK), - m_toolBar(this, IDC_TOOLBARTREE), - m_btnReset(this, IDC_BBRESET), - m_btnHidden(this, IDC_CANBEHIDDEN), - m_btnSeparator(this, IDC_SEPARATOR), - m_hImgl(nullptr), - timer(this, 1) - { - timer.OnEvent = Callback(this, &CSrmmToolbarOptions::OnTimer); - - m_toolBar.SetFlags(MTREE_DND); // enable drag-n-drop - m_toolBar.OnSelChanged = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanged); - m_toolBar.OnSelChanging = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanging); - m_toolBar.OnItemChanged = Callback(this, &CSrmmToolbarOptions::OnTreeItemChanged); - - m_btnReset.OnClick = Callback(this, &CSrmmToolbarOptions::btnResetClicked); - m_btnSeparator.OnClick = Callback(this, &CSrmmToolbarOptions::btnSeparatorClicked); - } - - bool OnInitDialog() override - { - g_pDialog = this; - BuildMenuObjectsTree(); - - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - - m_gap.SetPosition(g_iButtonGap); - return true; - } - - void OnDestroy() override - { - g_pDialog = nullptr; - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); - ImageList_Destroy(m_toolBar.GetImageList(TVSIL_STATE)); - } - - bool OnApply() override - { - OnTreeSelChanging(nullptr); // save latest changes - SaveTree(); // save the whole tree then - CB_ReInitCustomButtons(); - Chat_UpdateOptions(); // also restore chat windows - - uint16_t newGap = m_gap.GetPosition(); - if (newGap != g_iButtonGap) { - g_iButtonGap = newGap; - WindowList_BroadcastAsync(g_hWindowList, WM_SIZE, 0, 0); - } - - BuildMenuObjectsTree(); - - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - return true; - } - - virtual void OnReset() override - { - CB_ReInitCustomButtons(); - dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); - } - - void btnResetClicked(void*) - { - db_delete_module(0, BB_MODULE_NAME); - - Srmm_ResetToolbar(); - qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); - - BuildMenuObjectsTree(); - NotifyChange(); - } - - void btnSeparatorClicked(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (!hItem) - hItem = TVI_FIRST; - - BBButton bbd = {}; - bbd.pszModuleName = "Tabsrmm_sep"; - bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; - bbd.dwButtonID = ++dwSepCount; - - CustomButtonData *cbd = (CustomButtonData*)Srmm_AddButton(&bbd, &g_plugin); - - TVINSERTSTRUCT tvis; - tvis.hParent = nullptr; - tvis.hInsertAfter = hItem; - tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = TranslateT(""); - tvis.item.iImage = tvis.item.iSelectedImage = -1; - tvis.item.lParam = (LPARAM)cbd; - hItem = m_toolBar.InsertItem(&tvis); - - m_toolBar.SetCheckState(hItem, (cbd->m_bIMButton || cbd->m_bChatButton)); - NotifyChange(); - } - - void OnTreeSelChanging(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (hItem == nullptr) - return; - - wchar_t strbuf[128]; - TVITEMEX tvi; - tvi.hItem = hItem; - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - m_toolBar.GetItem(&tvi); - - if (tvi.lParam == 0 || !m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) - return; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - cbd->m_bIMButton = m_btnIM.GetState() != 0; - cbd->m_bChatButton = m_btnChat.GetState() != 0; - cbd->m_bCanBeHidden = !cbd->m_bCantBeHidden && m_btnHidden.GetState() != 0; - cbd->m_opFlags = (cbd->m_bIMButton ? BBSF_IMBUTTON : 0) + (cbd->m_bChatButton ? BBSF_CHATBUTTON : 0) + (cbd->m_bCanBeHidden ? BBSF_CANBEHIDDEN : 0); - - if (!cbd->m_bChatButton && !cbd->m_bIMButton) - m_toolBar.SetCheckState(tvi.hItem, 0); - } - - void OnTreeSelChanged(void*) - { - HTREEITEM hItem = m_toolBar.GetSelection(); - if (hItem == nullptr) - return; - - wchar_t strbuf[128]; - TVITEMEX tvi; - tvi.pszText = strbuf; - tvi.cchTextMax = _countof(strbuf); - tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; - tvi.hItem = hItem; - m_toolBar.GetItem(&tvi); - - if (!m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) { - m_btnIM.Disable(); - m_btnChat.Disable(); - m_btnHidden.Disable(); - return; - } - - if (tvi.lParam == 0) - return; - - CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; - m_btnIM.Enable(); m_btnIM.SetState(cbd->m_bIMButton); - m_btnChat.Enable(); m_btnChat.SetState(cbd->m_bChatButton); - m_btnHidden.Enable(); m_btnHidden.SetState(cbd->m_bCanBeHidden); - } - - void OnTreeItemChanged(CCtrlTreeView::TEventInfo *evt) - { - bool iNewState = !m_toolBar.GetCheckState(evt->hItem); - m_btnIM.Enable(iNewState); - m_btnChat.Enable(iNewState); - m_btnHidden.Enable(iNewState); - if (iNewState) - m_btnIM.SetState(true); - } - - void OnTimer(CTimer *pTimer) - { - pTimer->Stop(); - BuildMenuObjectsTree(); - } - - static void RereadButtons() - { - if (g_pDialog) - g_pDialog->timer.Start(100); - } -}; - -void SrmmLogOptionsInit(WPARAM wParam); - -static int SrmmOptionsInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.position = 910000000; - odp.szGroup.a = LPGEN("Message sessions"); - odp.szTitle.a = LPGEN("Toolbar"); - odp.flags = ODPF_BOLDGROUPS; - odp.pDialog = new CSrmmToolbarOptions(); - g_plugin.addOptions(wParam, &odp); - - ChatOptionsInit(wParam); - SrmmLogOptionsInit(wParam); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HANDLE) Srmm_AddButton(const BBButton *bbdi, HPLUGIN _hLang) -{ - if (bbdi == nullptr) - return nullptr; - - CustomButtonData *cbd = new CustomButtonData(); - cbd->m_pszModuleName = mir_strdup(bbdi->pszModuleName); - cbd->m_pwszText = mir_wstrdup(bbdi->pwszText); - cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); - - cbd->m_dwButtonID = bbdi->dwButtonID; - cbd->m_hIcon = bbdi->hIcon; - cbd->m_dwPosition = cbd->m_dwOrigPosition = bbdi->dwDefPos; - cbd->m_dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID; - cbd->m_dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? cbd->m_dwButtonCID + 1 : 0; - cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; - cbd->m_bSeparator = (bbdi->bbbFlags & BBBF_ISSEPARATOR) != 0; - - cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; - cbd->m_bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0; - cbd->m_pPlugin = _hLang; - - cbd->m_dwOrigFlags.bit1 = cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; - cbd->m_dwOrigFlags.bit2 = cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; - cbd->m_dwOrigFlags.bit3 = cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; - cbd->m_dwOrigFlags.bit4 = cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; - - if (cbd->m_bSeparator) - cbd->m_iButtonWidth = DPISCALEX_S(10); - else if (bbdi->bbbFlags & BBBF_ISARROWBUTTON) - cbd->m_iButtonWidth = DPISCALEX_S(34); - else - cbd->m_iButtonWidth = DPISCALEX_S(22); - - if (bbdi->pszHotkey) { - for (auto &p : hotkeys) { - if (!mir_strcmp(p->getName(), bbdi->pszHotkey)) { - cbd->m_hotkey = p; - break; - } - } - } - - // download database settings - char SettingName[1024]; - mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); - - DBVARIANT dbv = { 0 }; - if (!db_get_s(0, BB_MODULE_NAME, SettingName, &dbv)) { - // modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden - char *token = strtok(dbv.pszVal, "_"); - cbd->m_dwPosition = (uint32_t)atoi(token); - token = strtok(nullptr, "_"); - cbd->m_bIMButton = atoi(token) != 0; - token = strtok(nullptr, "_"); - cbd->m_bChatButton = atoi(token) != 0; - token = strtok(nullptr, "_"); - token = strtok(nullptr, "_"); - cbd->m_bRSided = atoi(token) != 0; - token = strtok(nullptr, "_"); - cbd->m_bCanBeHidden = atoi(token) != 0; - - db_free(&dbv); - } - - arButtonsList.insert(cbd); - - if (cbd->m_dwButtonCID != cbd->m_dwButtonID) - LastCID++; - if (cbd->m_dwArrowCID == LastCID) - LastCID++; - - WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); - CSrmmToolbarOptions::RereadButtons(); - return cbd; -} - -MIR_APP_DLL(int) Srmm_RemoveButton(BBButton *bbdi) -{ - if (!bbdi) - return 1; - - CustomButtonData *pFound = nullptr; - { - mir_cslock lck(csToolBar); - - for (auto &cbd : arButtonsList.rev_iter()) - if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && cbd->m_dwButtonID == bbdi->dwButtonID) - pFound = arButtonsList.removeItem(&cbd); - } - - if (pFound) { - CSrmmToolbarOptions::RereadButtons(); - WindowList_Broadcast(g_hWindowList, WM_CBD_REMOVED, pFound->m_dwButtonCID, (LPARAM)pFound); - delete pFound; - } - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void KillModuleToolbarIcons(CMPluginBase *pPlugin) -{ - int oldCount = arButtonsList.getCount(); - - auto T = arButtonsList.rev_iter(); - for (auto &cbd : T) - if (cbd->m_pPlugin == pPlugin) - delete arButtonsList.removeItem(&cbd); - - if (oldCount != arButtonsList.getCount()) - CSrmmToolbarOptions::RereadButtons(); -} - -static INT_PTR BroadcastMessage(WPARAM, LPARAM lParam) -{ - Srmm_Broadcast((UINT)lParam, 0, 0); - return 0; -} - -static void CALLBACK SrmmLoadToolbar() -{ - NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0); - DestroyHookableEvent(hHookToolBarLoadedEvt); - - HookEvent(ME_OPT_INITIALISE, SrmmOptionsInit); -} - -static int ConvertToolbarData(const char *szSetting, void*) -{ - DBVARIANT dbv; - if (!db_get(0, "Tab" BB_MODULE_NAME, szSetting, &dbv)) { - db_set(0, BB_MODULE_NAME, szSetting, &dbv); - db_free(&dbv); - } - return 0; -} - -void LoadSrmmToolbarModule() -{ - CreateServiceFunction("SRMsg/BroadcastMessage", BroadcastMessage); - - Miranda_WaitOnHandle(SrmmLoadToolbar); - - hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED); - hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED); - - HDC hScrnDC = GetDC(nullptr); - g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0; - g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0; - ReleaseDC(nullptr, hScrnDC); - - // old data? convert them - if (db_get_dw(0, "Tab" BB_MODULE_NAME, "SeparatorsCount", -1) != -1) { - db_enum_settings(0, ConvertToolbarData, "Tab" BB_MODULE_NAME, nullptr); - db_delete_module(0, "Tab" BB_MODULE_NAME); - } - - dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); - CB_RegisterSeparators(); -} - -void UnloadSrmmToolbarModule() -{ - DestroyHookableEvent(hHookButtonPressedEvt); - - for (auto &it : arButtonsList) - delete it; - arButtonsList.destroy(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "chat.h" +#include "skin.h" + +#define BB_MODULE_NAME "SRMM_Toolbar" + +#define DPISCALEY_S(argY) ((int)((double)(argY) * g_DPIscaleY)) +#define DPISCALEX_S(argX) ((int)((double)(argX) * g_DPIscaleX)) + +static double g_DPIscaleX, g_DPIscaleY; +static class CSrmmToolbarOptions *g_pDialog = nullptr; + +static CMOption g_iButtonGap(BB_MODULE_NAME, "ButtonsBarGap", 1); + +static int SortButtons(const CustomButtonData *p1, const CustomButtonData *p2) +{ + if (p1->m_bRSided != p2->m_bRSided) + return (p2->m_bRSided) ? -1 : 1; + if (p1->m_dwPosition != p2->m_dwPosition) + return p1->m_dwPosition - p2->m_dwPosition; + int res = mir_strcmp(p1->m_pszModuleName, p2->m_pszModuleName); + if (res != 0) + return res; + return p1->m_dwButtonID - p2->m_dwButtonID; +} + +static LIST arButtonsList(1, SortButtons); + +int LastCID = MIN_CBUTTONID; +int dwSepCount = 0; + +static mir_cs csToolBar; +static HANDLE hHookToolBarLoadedEvt, hHookButtonPressedEvt; + +static int sstSortButtons(const void *p1, const void *p2) +{ + return SortButtons(*(CustomButtonData**)p1, *(CustomButtonData**)p2); +} + +static void CB_RegisterSeparators() +{ + BBButton bbd = {}; + bbd.pszModuleName = "Tabsrmm_sep"; + for (int i = 0; dwSepCount > i; i++) { + bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; + bbd.dwButtonID = i + 1; + bbd.dwDefPos = 410 + i; + Srmm_AddButton(&bbd, &g_plugin); + } +} + +MIR_APP_DLL(CustomButtonData*) Srmm_GetNthButton(int i) +{ + return arButtonsList[i]; +} + +MIR_APP_DLL(int) Srmm_GetButtonCount(void) +{ + return arButtonsList.getCount(); +} + +MIR_APP_DLL(int) Srmm_GetButtonGap() +{ + return g_iButtonGap; +} + +MIR_APP_DLL(int) Srmm_GetButtonState(HWND hwndDlg, BBButton *bbdi) +{ + if (hwndDlg == nullptr || bbdi == nullptr) + return 1; + + uint32_t tempCID = 0; + bbdi->bbbFlags = 0; + for (auto &cbd : arButtonsList) + if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { + tempCID = cbd->m_dwButtonCID; + break; + } + + if (!tempCID) + return 1; + + HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); + bbdi->bbbFlags = (IsDlgButtonChecked(hwndDlg, tempCID) ? BBSF_PUSHED : BBSF_RELEASED) | (IsWindowVisible(hwndBtn) ? 0 : BBSF_HIDDEN) | (IsWindowEnabled(hwndBtn) ? 0 : BBSF_DISABLED); + return 0; +} + +MIR_APP_DLL(int) Srmm_SetButtonState(MCONTACT hContact, BBButton *bbdi) +{ + if (hContact == 0 || bbdi == nullptr) + return 1; + + uint32_t tempCID = 0; + for (auto &cbd : arButtonsList) + if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && (cbd->m_dwButtonID == bbdi->dwButtonID)) { + tempCID = cbd->m_dwButtonCID; + break; + } + + if (!tempCID) + return 1; + + HWND hwndDlg = WindowList_Find(g_hWindowList, hContact); + if (hwndDlg == nullptr) + return 1; + + HWND hwndBtn = GetDlgItem(hwndDlg, tempCID); + if (hwndBtn == nullptr) + return 1; + + SetWindowTextA(hwndBtn, bbdi->pszModuleName); + if (bbdi->hIcon) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(bbdi->hIcon)); + if (bbdi->pwszTooltip) + SendMessage(hwndBtn, BUTTONADDTOOLTIP, (WPARAM)bbdi->pwszTooltip, BATF_UNICODE); + if (bbdi->bbbFlags) { + ShowWindow(hwndBtn, (bbdi->bbbFlags & BBSF_HIDDEN) ? SW_HIDE : SW_SHOW); + EnableWindow(hwndBtn, !(bbdi->bbbFlags & BBSF_DISABLED)); + Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_PUSHED) != 0); + Button_SetCheck(hwndBtn, (bbdi->bbbFlags & BBSF_RELEASED) == 0); + } + return 0; +} + +MIR_APP_DLL(int) Srmm_ModifyButton(BBButton *bbdi) +{ + if (!bbdi) + return 1; + + CustomButtonData *cbd = nullptr; + { + mir_cslock lck(csToolBar); + + for (auto &p : arButtonsList) + if (!mir_strcmp(p->m_pszModuleName, bbdi->pszModuleName) && (p->m_dwButtonID == bbdi->dwButtonID)) { + cbd = p; + break; + } + + if (cbd != nullptr) { + if (bbdi->pwszTooltip) + cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); + if (bbdi->hIcon) + cbd->m_hIcon = bbdi->hIcon; + if (bbdi->bbbFlags) { + cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; + cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; + cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; + cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; + cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; + cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; + } + } + } + + if (cbd != nullptr) + WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, (LPARAM)cbd); + return 0; +} + +MIR_APP_DLL(void) Srmm_ClickToolbarIcon(MCONTACT hContact, int idFrom, HWND hwndDlg, BOOL code) +{ + bool bFromArrow = false; + HWND hwndFrom = nullptr; + + CustomButtonClickData cbcd = {}; + + for (auto &cbd : arButtonsList) { + if (cbd->m_dwButtonCID == idFrom) { + cbcd.pszModule = cbd->m_pszModuleName; + cbcd.dwButtonId = cbd->m_dwButtonID; + hwndFrom = GetDlgItem(hwndDlg, idFrom); + } + else if (cbd->m_dwArrowCID == idFrom) { + bFromArrow = true; + cbcd.pszModule = cbd->m_pszModuleName; + cbcd.dwButtonId = cbd->m_dwButtonID; + hwndFrom = GetDlgItem(hwndDlg, idFrom-1); + } + } + + if (hwndFrom == nullptr) + return; + + RECT rc; + GetWindowRect(hwndFrom, &rc); + cbcd.pt.x = rc.left; + cbcd.pt.y = rc.bottom; + + cbcd.hwndFrom = GetParent(hwndFrom); + cbcd.hContact = hContact; + cbcd.flags = (code ? BBCF_RIGHTBUTTON : 0) | (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0) | (bFromArrow ? BBCF_ARROWCLICKED : 0); + + NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); +} + +void Srmm_ProcessToolbarHotkey(MCONTACT hContact, INT_PTR iButtonFrom, HWND hwndDlg) +{ + HWND hwndFrom = nullptr; + + CustomButtonClickData cbcd = {}; + + for (auto &cbd : arButtonsList) { + if (cbd->m_hotkey == nullptr || cbd->m_bDisabled) + continue; + + if (cbd->m_hotkey->lParam == iButtonFrom) { + cbcd.pszModule = cbd->m_pszModuleName; + cbcd.dwButtonId = cbd->m_dwButtonID; + hwndFrom = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); + break; + } + } + + if (hwndFrom == nullptr) + return; + + RECT rc; + GetWindowRect(hwndFrom, &rc); + cbcd.pt.x = rc.left; + cbcd.pt.y = rc.bottom; + + cbcd.hwndFrom = GetParent(hwndFrom); + cbcd.hContact = hContact; + cbcd.flags = (GetKeyState(VK_SHIFT) & 0x8000 ? BBCF_SHIFTPRESSED : 0) | (GetKeyState(VK_CONTROL) & 0x8000 ? BBCF_CONTROLPRESSED : 0); + + NotifyEventHooks(hHookButtonPressedEvt, hContact, (LPARAM)&cbcd); +} + +MIR_APP_DLL(void) Srmm_ResetToolbar() +{ + for (auto &cbd : arButtonsList) { + cbd->m_dwPosition = cbd->m_dwOrigPosition; + cbd->m_bRSided = cbd->m_dwOrigFlags.bit1; + cbd->m_bIMButton = cbd->m_dwOrigFlags.bit2; + cbd->m_bChatButton = cbd->m_dwOrigFlags.bit3; + cbd->m_bCanBeHidden = cbd->m_dwOrigFlags.bit4; + } +} + +void Srmm_CreateToolbarIcons(HWND hwndDlg, int flags) +{ + HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hwndDlg, GWLP_HINSTANCE); + + CDlgBase *pDlg = CDlgBase::Find(hwndDlg); + + for (auto &cbd : arButtonsList) { + if (cbd->m_bSeparator) + continue; + + HWND hwndButton = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); + if ((flags & BBBF_ISIMBUTTON) && cbd->m_bIMButton || (flags & BBBF_ISCHATBUTTON) && cbd->m_bChatButton) { + if (hwndButton == nullptr) { + hwndButton = CreateWindowEx(0, L"MButtonClass", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, cbd->m_iButtonWidth, DPISCALEX_S(22), hwndDlg, (HMENU)cbd->m_dwButtonCID, hInstance, nullptr); + if (hwndButton == nullptr) // smth went wrong + continue; + + // if there's a pre-created button control in a class, initialize it + if (pDlg != nullptr) { + CCtrlBase *pControl = (*pDlg)[cbd->m_dwButtonCID]; + if (pControl) + pControl->OnInit(); + } + } + SendMessage(hwndButton, BUTTONSETASFLATBTN, TRUE, 0); + if (cbd->m_pwszText) + SetWindowTextW(hwndButton, cbd->m_pwszText); + if (cbd->m_pwszTooltip) + SendMessage(hwndButton, BUTTONADDTOOLTIP, LPARAM(cbd->m_pwszTooltip), BATF_UNICODE); + if (cbd->m_hIcon) + SendMessage(hwndButton, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); + + if (cbd->m_dwArrowCID) + SendMessage(hwndButton, BUTTONSETARROW, cbd->m_dwArrowCID, 0); + if (cbd->m_bPushButton) + SendMessage(hwndButton, BUTTONSETASPUSHBTN, TRUE, 0); + + if (cbd->m_bDisabled) + EnableWindow(hwndButton, FALSE); + if (cbd->m_bHidden) + ShowWindow(hwndButton, SW_HIDE); + } + else if (hwndButton) + DestroyWindow(hwndButton); + } +} + +MIR_APP_DLL(void) Srmm_UpdateToolbarIcons(HWND hwndDlg) +{ + for (auto &cbd : arButtonsList) { + if (cbd->m_bSeparator || cbd->m_hIcon == nullptr) + continue; + + HWND hwndBtn = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); + if (hwndBtn) + SendMessage(hwndBtn, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(cbd->m_hIcon)); + } +} + +MIR_APP_DLL(void) Srmm_RedrawToolbarIcons(HWND hwndDlg) +{ + for (auto &cbd : arButtonsList) { + HWND hwnd = GetDlgItem(hwndDlg, cbd->m_dwButtonCID); + if (hwnd) + InvalidateRect(hwnd, nullptr, TRUE); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void CB_ReInitCustomButtons() +{ + for (auto &cbd : arButtonsList.rev_iter()) + if (cbd->m_opFlags & (BBSF_NTBSWAPED | BBSF_NTBDESTRUCT)) { + cbd->m_opFlags ^= BBSF_NTBSWAPED; + + if (cbd->m_opFlags & BBSF_NTBDESTRUCT) + arButtonsList.removeItem(&cbd); + } + + qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); + + WindowList_Broadcast(g_hWindowList, WM_CBD_RECREATE, 0, 0); + WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); + WindowList_Broadcast(g_hWindowList, WM_CBD_LOADICONS, 0, 0); +} + +static void CB_WriteButtonSettings(MCONTACT hContact, CustomButtonData *cbd) +{ + char SettingName[1024]; + char SettingParameter[1024]; + + //modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden + + mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); + mir_snprintf(SettingParameter, "%d_%u_%u_%u_%u_%u", cbd->m_dwPosition, cbd->m_bIMButton, cbd->m_bChatButton, 0, cbd->m_bRSided, cbd->m_bCanBeHidden); + if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) + db_set_s(hContact, BB_MODULE_NAME, SettingName, SettingParameter); + else + db_unset(hContact, BB_MODULE_NAME, SettingName); +} + +#define MIDDLE_SEPARATOR L">-------M-------<" + +class CSrmmToolbarOptions : public CDlgBase +{ + CCtrlTreeView m_toolBar; + CCtrlCheck m_btnIM, m_btnChat, m_btnHidden; + CCtrlButton m_btnReset, m_btnSeparator; + CCtrlSpin m_gap; + CTimer timer; + + HIMAGELIST m_hImgl; + + void SaveTree() + { + bool RSide = false; + int count = 10; + uint32_t loc_sepcout = 0; + wchar_t strbuf[128]; + + TVITEMEX tvi; + tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_HANDLE; + tvi.hItem = m_toolBar.GetRoot(); + tvi.pszText = strbuf; + tvi.cchTextMax = _countof(strbuf); + { + mir_cslock lck(csToolBar); + + while (tvi.hItem != nullptr) { + m_toolBar.GetItem(&tvi); + + if (mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR) == 0) { + RSide = true; + count = m_toolBar.GetCount() * 10 - count; + tvi.hItem = m_toolBar.GetNextSibling(tvi.hItem); + continue; + } + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + if (cbd && arButtonsList.indexOf(cbd) != -1) { + if (cbd->m_opFlags) { + cbd->m_bIMButton = (cbd->m_opFlags & BBSF_IMBUTTON) != 0; + cbd->m_bChatButton = (cbd->m_opFlags & BBSF_CHATBUTTON) != 0; + cbd->m_bCanBeHidden = (cbd->m_opFlags & BBSF_CANBEHIDDEN) != 0; + } + + if (RSide && !cbd->m_bRSided) { + cbd->m_bRSided = true; + cbd->m_opFlags |= BBSF_NTBSWAPED; + } + else if (!RSide && cbd->m_bRSided) { + cbd->m_bRSided = false; + cbd->m_opFlags |= BBSF_NTBSWAPED; + } + + if (!cbd->m_bCantBeHidden && !m_toolBar.GetCheckState(tvi.hItem)) { + cbd->m_bIMButton = false; + cbd->m_bChatButton = false; + + if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) + cbd->m_opFlags = BBSF_NTBDESTRUCT; + } + else { + if (!cbd->m_bIMButton && !cbd->m_bChatButton) + cbd->m_bIMButton = true; + if (cbd->m_bSeparator && !mir_strcmp(cbd->m_pszModuleName, "Tabsrmm_sep")) { + cbd->m_bHidden = false; + cbd->m_opFlags &= ~BBSF_NTBDESTRUCT; + ++loc_sepcout; + } + } + + cbd->m_dwPosition = (uint32_t)count; + CB_WriteButtonSettings(0, cbd); + + if (!(cbd->m_opFlags & BBSF_NTBDESTRUCT)) + (RSide) ? (count -= 10) : (count += 10); + } + + HTREEITEM hItem = m_toolBar.GetNextSibling(tvi.hItem); + if (cbd->m_opFlags & BBSF_NTBDESTRUCT) + m_toolBar.DeleteItem(tvi.hItem); + tvi.hItem = hItem; + } + + qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); + } + db_set_dw(0, BB_MODULE_NAME, "SeparatorsCount", loc_sepcout); + dwSepCount = loc_sepcout; + } + + void BuildMenuObjectsTree() + { + m_toolBar.DeleteAllItems(); + + m_hImgl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 2, 2); + ImageList_AddIcon(m_hImgl, Skin_LoadIcon(SKINICON_OTHER_SMALLDOT)); + ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); + m_toolBar.SetImageList(m_hImgl, TVSIL_NORMAL); + + if (arButtonsList.getCount() == 0) + return; + + bool bPrevSide = false; + + TVINSERTSTRUCT tvis; + tvis.hParent = nullptr; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE; + + mir_cslock lck(csToolBar); + for (auto &cbd : arButtonsList) { + if (bPrevSide != cbd->m_bRSided) { + bPrevSide = true; + + TVINSERTSTRUCT tvis2 = {}; + tvis.hInsertAfter = TVI_LAST; + tvis2.itemex.mask = TVIF_PARAM | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_STATE | TVIF_STATEEX; + tvis2.itemex.pszText = MIDDLE_SEPARATOR; + tvis2.itemex.stateMask = TVIS_BOLD; + tvis2.itemex.state = TVIS_BOLD; + tvis2.itemex.iImage = tvis.item.iSelectedImage = -1; + tvis2.itemex.uStateEx = TVIS_EX_DISABLED; + tvis.hInsertAfter = m_toolBar.InsertItem(&tvis2); + m_toolBar.SetItemState(tvis.hInsertAfter, 0x3000, TVIS_STATEIMAGEMASK); + } + + tvis.item.lParam = (LPARAM)cbd; + + if (cbd->m_bSeparator) { + tvis.item.pszText = TranslateT(""); + tvis.item.iImage = tvis.item.iSelectedImage = 0; + } + else { + tvis.item.pszText = TranslateW(cbd->m_pwszTooltip); + tvis.item.iImage = tvis.item.iSelectedImage = ImageList_AddIcon(m_hImgl, IcoLib_GetIconByHandle(cbd->m_hIcon)); + } + cbd->m_opFlags = 0; + HTREEITEM hti = m_toolBar.InsertItem(&tvis); + + m_toolBar.SetCheckState(hti, (cbd->m_bIMButton || cbd->m_bChatButton)); + if (cbd->m_bCantBeHidden) + m_toolBar.SetItemState(hti, 0x3000, TVIS_STATEIMAGEMASK); + } + } + +public: + CSrmmToolbarOptions() : + CDlgBase(g_plugin, IDD_OPT_TOOLBAR), + m_gap(this, IDC_SPIN1, 10), + m_btnIM(this, IDC_IMCHECK), + m_btnChat(this, IDC_CHATCHECK), + m_toolBar(this, IDC_TOOLBARTREE), + m_btnReset(this, IDC_BBRESET), + m_btnHidden(this, IDC_CANBEHIDDEN), + m_btnSeparator(this, IDC_SEPARATOR), + m_hImgl(nullptr), + timer(this, 1) + { + timer.OnEvent = Callback(this, &CSrmmToolbarOptions::OnTimer); + + m_toolBar.SetFlags(MTREE_DND); // enable drag-n-drop + m_toolBar.OnSelChanged = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanged); + m_toolBar.OnSelChanging = Callback(this, &CSrmmToolbarOptions::OnTreeSelChanging); + m_toolBar.OnItemChanged = Callback(this, &CSrmmToolbarOptions::OnTreeItemChanged); + + m_btnReset.OnClick = Callback(this, &CSrmmToolbarOptions::btnResetClicked); + m_btnSeparator.OnClick = Callback(this, &CSrmmToolbarOptions::btnSeparatorClicked); + } + + bool OnInitDialog() override + { + g_pDialog = this; + BuildMenuObjectsTree(); + + m_btnIM.Disable(); + m_btnChat.Disable(); + m_btnHidden.Disable(); + + m_gap.SetPosition(g_iButtonGap); + return true; + } + + void OnDestroy() override + { + g_pDialog = nullptr; + ImageList_Destroy(m_toolBar.GetImageList(TVSIL_NORMAL)); + ImageList_Destroy(m_toolBar.GetImageList(TVSIL_STATE)); + } + + bool OnApply() override + { + OnTreeSelChanging(nullptr); // save latest changes + SaveTree(); // save the whole tree then + CB_ReInitCustomButtons(); + Chat_UpdateOptions(); // also restore chat windows + + uint16_t newGap = m_gap.GetPosition(); + if (newGap != g_iButtonGap) { + g_iButtonGap = newGap; + WindowList_BroadcastAsync(g_hWindowList, WM_SIZE, 0, 0); + } + + BuildMenuObjectsTree(); + + m_btnIM.Disable(); + m_btnChat.Disable(); + m_btnHidden.Disable(); + return true; + } + + virtual void OnReset() override + { + CB_ReInitCustomButtons(); + dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); + } + + void btnResetClicked(void*) + { + db_delete_module(0, BB_MODULE_NAME); + + Srmm_ResetToolbar(); + qsort(arButtonsList.getArray(), arButtonsList.getCount(), sizeof(void*), sstSortButtons); + + BuildMenuObjectsTree(); + NotifyChange(); + } + + void btnSeparatorClicked(void*) + { + HTREEITEM hItem = m_toolBar.GetSelection(); + if (!hItem) + hItem = TVI_FIRST; + + BBButton bbd = {}; + bbd.pszModuleName = "Tabsrmm_sep"; + bbd.bbbFlags = BBBF_ISSEPARATOR | BBBF_ISIMBUTTON; + bbd.dwButtonID = ++dwSepCount; + + CustomButtonData *cbd = (CustomButtonData*)Srmm_AddButton(&bbd, &g_plugin); + + TVINSERTSTRUCT tvis; + tvis.hParent = nullptr; + tvis.hInsertAfter = hItem; + tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvis.item.pszText = TranslateT(""); + tvis.item.iImage = tvis.item.iSelectedImage = -1; + tvis.item.lParam = (LPARAM)cbd; + hItem = m_toolBar.InsertItem(&tvis); + + m_toolBar.SetCheckState(hItem, (cbd->m_bIMButton || cbd->m_bChatButton)); + NotifyChange(); + } + + void OnTreeSelChanging(void*) + { + HTREEITEM hItem = m_toolBar.GetSelection(); + if (hItem == nullptr) + return; + + wchar_t strbuf[128]; + TVITEMEX tvi; + tvi.hItem = hItem; + tvi.pszText = strbuf; + tvi.cchTextMax = _countof(strbuf); + tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; + m_toolBar.GetItem(&tvi); + + if (tvi.lParam == 0 || !m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) + return; + + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + cbd->m_bIMButton = m_btnIM.GetState() != 0; + cbd->m_bChatButton = m_btnChat.GetState() != 0; + cbd->m_bCanBeHidden = !cbd->m_bCantBeHidden && m_btnHidden.GetState() != 0; + cbd->m_opFlags = (cbd->m_bIMButton ? BBSF_IMBUTTON : 0) + (cbd->m_bChatButton ? BBSF_CHATBUTTON : 0) + (cbd->m_bCanBeHidden ? BBSF_CANBEHIDDEN : 0); + + if (!cbd->m_bChatButton && !cbd->m_bIMButton) + m_toolBar.SetCheckState(tvi.hItem, 0); + } + + void OnTreeSelChanged(void*) + { + HTREEITEM hItem = m_toolBar.GetSelection(); + if (hItem == nullptr) + return; + + wchar_t strbuf[128]; + TVITEMEX tvi; + tvi.pszText = strbuf; + tvi.cchTextMax = _countof(strbuf); + tvi.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_PARAM; + tvi.hItem = hItem; + m_toolBar.GetItem(&tvi); + + if (!m_toolBar.GetCheckState(tvi.hItem) || !mir_wstrcmp(tvi.pszText, MIDDLE_SEPARATOR)) { + m_btnIM.Disable(); + m_btnChat.Disable(); + m_btnHidden.Disable(); + return; + } + + if (tvi.lParam == 0) + return; + + CustomButtonData *cbd = (CustomButtonData*)tvi.lParam; + m_btnIM.Enable(); m_btnIM.SetState(cbd->m_bIMButton); + m_btnChat.Enable(); m_btnChat.SetState(cbd->m_bChatButton); + m_btnHidden.Enable(); m_btnHidden.SetState(cbd->m_bCanBeHidden); + } + + void OnTreeItemChanged(CCtrlTreeView::TEventInfo *evt) + { + bool iNewState = !m_toolBar.GetCheckState(evt->hItem); + m_btnIM.Enable(iNewState); + m_btnChat.Enable(iNewState); + m_btnHidden.Enable(iNewState); + if (iNewState) + m_btnIM.SetState(true); + } + + void OnTimer(CTimer *pTimer) + { + pTimer->Stop(); + BuildMenuObjectsTree(); + } + + static void RereadButtons() + { + if (g_pDialog) + g_pDialog->timer.Start(100); + } +}; + +void SrmmLogOptionsInit(WPARAM wParam); + +static int SrmmOptionsInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.position = 910000000; + odp.szGroup.a = LPGEN("Message sessions"); + odp.szTitle.a = LPGEN("Toolbar"); + odp.flags = ODPF_BOLDGROUPS; + odp.pDialog = new CSrmmToolbarOptions(); + g_plugin.addOptions(wParam, &odp); + + ChatOptionsInit(wParam); + SrmmLogOptionsInit(wParam); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(HANDLE) Srmm_AddButton(const BBButton *bbdi, HPLUGIN _hLang) +{ + if (bbdi == nullptr) + return nullptr; + + CustomButtonData *cbd = new CustomButtonData(); + cbd->m_pszModuleName = mir_strdup(bbdi->pszModuleName); + cbd->m_pwszText = mir_wstrdup(bbdi->pwszText); + cbd->m_pwszTooltip = mir_wstrdup(bbdi->pwszTooltip); + + cbd->m_dwButtonID = bbdi->dwButtonID; + cbd->m_hIcon = bbdi->hIcon; + cbd->m_dwPosition = cbd->m_dwOrigPosition = bbdi->dwDefPos; + cbd->m_dwButtonCID = (bbdi->bbbFlags & BBBF_CREATEBYID) ? bbdi->dwButtonID : LastCID; + cbd->m_dwArrowCID = (bbdi->bbbFlags & BBBF_ISARROWBUTTON) ? cbd->m_dwButtonCID + 1 : 0; + cbd->m_bHidden = (bbdi->bbbFlags & BBBF_HIDDEN) != 0; + cbd->m_bSeparator = (bbdi->bbbFlags & BBBF_ISSEPARATOR) != 0; + + cbd->m_bDisabled = (bbdi->bbbFlags & BBBF_DISABLED) != 0; + cbd->m_bPushButton = (bbdi->bbbFlags & BBBF_ISPUSHBUTTON) != 0; + cbd->m_pPlugin = _hLang; + + cbd->m_dwOrigFlags.bit1 = cbd->m_bRSided = (bbdi->bbbFlags & BBBF_ISRSIDEBUTTON) != 0; + cbd->m_dwOrigFlags.bit2 = cbd->m_bIMButton = (bbdi->bbbFlags & BBBF_ISIMBUTTON) != 0; + cbd->m_dwOrigFlags.bit3 = cbd->m_bChatButton = (bbdi->bbbFlags & BBBF_ISCHATBUTTON) != 0; + cbd->m_dwOrigFlags.bit4 = cbd->m_bCanBeHidden = (bbdi->bbbFlags & BBBF_CANBEHIDDEN) != 0; + + if (cbd->m_bSeparator) + cbd->m_iButtonWidth = DPISCALEX_S(10); + else if (bbdi->bbbFlags & BBBF_ISARROWBUTTON) + cbd->m_iButtonWidth = DPISCALEX_S(34); + else + cbd->m_iButtonWidth = DPISCALEX_S(22); + + if (bbdi->pszHotkey) { + for (auto &p : hotkeys) { + if (!mir_strcmp(p->getName(), bbdi->pszHotkey)) { + cbd->m_hotkey = p; + break; + } + } + } + + // download database settings + char SettingName[1024]; + mir_snprintf(SettingName, "%s_%d", cbd->m_pszModuleName.get(), cbd->m_dwButtonID); + + DBVARIANT dbv = { 0 }; + if (!db_get_s(0, BB_MODULE_NAME, SettingName, &dbv)) { + // modulename_buttonID, position_inIM_inCHAT_isLSide_isRSide_CanBeHidden + char *token = strtok(dbv.pszVal, "_"); + cbd->m_dwPosition = (uint32_t)atoi(token); + token = strtok(nullptr, "_"); + cbd->m_bIMButton = atoi(token) != 0; + token = strtok(nullptr, "_"); + cbd->m_bChatButton = atoi(token) != 0; + token = strtok(nullptr, "_"); + token = strtok(nullptr, "_"); + cbd->m_bRSided = atoi(token) != 0; + token = strtok(nullptr, "_"); + cbd->m_bCanBeHidden = atoi(token) != 0; + + db_free(&dbv); + } + + arButtonsList.insert(cbd); + + if (cbd->m_dwButtonCID != cbd->m_dwButtonID) + LastCID++; + if (cbd->m_dwArrowCID == LastCID) + LastCID++; + + WindowList_Broadcast(g_hWindowList, WM_CBD_UPDATED, 0, 0); + CSrmmToolbarOptions::RereadButtons(); + return cbd; +} + +MIR_APP_DLL(int) Srmm_RemoveButton(BBButton *bbdi) +{ + if (!bbdi) + return 1; + + CustomButtonData *pFound = nullptr; + { + mir_cslock lck(csToolBar); + + for (auto &cbd : arButtonsList.rev_iter()) + if (!mir_strcmp(cbd->m_pszModuleName, bbdi->pszModuleName) && cbd->m_dwButtonID == bbdi->dwButtonID) + pFound = arButtonsList.removeItem(&cbd); + } + + if (pFound) { + CSrmmToolbarOptions::RereadButtons(); + WindowList_Broadcast(g_hWindowList, WM_CBD_REMOVED, pFound->m_dwButtonCID, (LPARAM)pFound); + delete pFound; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void KillModuleToolbarIcons(CMPluginBase *pPlugin) +{ + int oldCount = arButtonsList.getCount(); + + auto T = arButtonsList.rev_iter(); + for (auto &cbd : T) + if (cbd->m_pPlugin == pPlugin) + delete arButtonsList.removeItem(&cbd); + + if (oldCount != arButtonsList.getCount()) + CSrmmToolbarOptions::RereadButtons(); +} + +static INT_PTR BroadcastMessage(WPARAM, LPARAM lParam) +{ + Srmm_Broadcast((UINT)lParam, 0, 0); + return 0; +} + +static void CALLBACK SrmmLoadToolbar() +{ + NotifyEventHooks(hHookToolBarLoadedEvt, 0, 0); + DestroyHookableEvent(hHookToolBarLoadedEvt); + + HookEvent(ME_OPT_INITIALISE, SrmmOptionsInit); +} + +static int ConvertToolbarData(const char *szSetting, void*) +{ + DBVARIANT dbv; + if (!db_get(0, "Tab" BB_MODULE_NAME, szSetting, &dbv)) { + db_set(0, BB_MODULE_NAME, szSetting, &dbv); + db_free(&dbv); + } + return 0; +} + +void LoadSrmmToolbarModule() +{ + CreateServiceFunction("SRMsg/BroadcastMessage", BroadcastMessage); + + Miranda_WaitOnHandle(SrmmLoadToolbar); + + hHookButtonPressedEvt = CreateHookableEvent(ME_MSG_BUTTONPRESSED); + hHookToolBarLoadedEvt = CreateHookableEvent(ME_MSG_TOOLBARLOADED); + + HDC hScrnDC = GetDC(nullptr); + g_DPIscaleX = GetDeviceCaps(hScrnDC, LOGPIXELSX) / 96.0; + g_DPIscaleY = GetDeviceCaps(hScrnDC, LOGPIXELSY) / 96.0; + ReleaseDC(nullptr, hScrnDC); + + // old data? convert them + if (db_get_dw(0, "Tab" BB_MODULE_NAME, "SeparatorsCount", -1) != -1) { + db_enum_settings(0, ConvertToolbarData, "Tab" BB_MODULE_NAME, nullptr); + db_delete_module(0, "Tab" BB_MODULE_NAME); + } + + dwSepCount = db_get_dw(0, BB_MODULE_NAME, "SeparatorsCount", 0); + CB_RegisterSeparators(); +} + +void UnloadSrmmToolbarModule() +{ + DestroyHookableEvent(hHookButtonPressedEvt); + + for (auto &it : arButtonsList) + delete it; + arButtonsList.destroy(); +} diff --git a/src/mir_app/src/srmm_util.cpp b/src/mir_app/src/srmm_util.cpp index 4f66b9ffd2..2ca887a54a 100644 --- a/src/mir_app/src/srmm_util.cpp +++ b/src/mir_app/src/srmm_util.cpp @@ -1,147 +1,147 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" -#include "chat.h" - -const char *g_pszHotkeySection; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb) -{ - LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA*)dwCookie; - if (lstrdat) { - // create the RTF - if (lstrdat->buffer == nullptr) { - lstrdat->bufferOffset = 0; - lstrdat->buffer = g_chatApi.Log_CreateRTF(lstrdat); - lstrdat->bufferLen = (int)mir_strlen(lstrdat->buffer); - } - - // give the RTF to the RE control - *pcb = min(cb, LONG(lstrdat->bufferLen - lstrdat->bufferOffset)); - memcpy(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb); - lstrdat->bufferOffset += *pcb; - - // free stuff if the streaming operation is complete - if (lstrdat->bufferOffset == lstrdat->bufferLen) { - mir_free(lstrdat->buffer); - lstrdat->buffer = nullptr; - } - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(int) Srmm_GetWindowData(MCONTACT hContact, MessageWindowData &mwd) -{ - if (hContact == 0) - return 1; - - HWND hwnd = WindowList_Find(g_hWindowList, hContact); - if (hwnd == nullptr) - return 1; - - mwd.hwndWindow = hwnd; - mwd.pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - mwd.uState = MSG_WINDOW_STATE_EXISTS; - if (IsWindowVisible(hwnd)) - mwd.uState |= MSG_WINDOW_STATE_VISIBLE; - if (GetForegroundWindow() == hwnd) - mwd.uState |= MSG_WINDOW_STATE_FOCUS; - if (IsIconic(hwnd)) - mwd.uState |= MSG_WINDOW_STATE_ICONIC; - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Srmm_Broadcast(UINT msg, WPARAM wParam, LPARAM lParam) -{ - WindowList_Broadcast(g_hWindowList, msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription) -{ - g_pszHotkeySection = pszSection; - - uint16_t wHotKey = HOTKEYCODE(0, VK_RETURN); - if (db_get_b(0, SRMM_MODULE, "SendOnCtrlEnter")) - wHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_RETURN); - - if (db_get_b(0, "Tab_SRMsg", "sendonshiftenter")) - wHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_RETURN); - - HOTKEYDESC hd = { "tabsrmm_send", pszDescription, pszSection, 0, wHotKey, 0, 100 }; - Hotkey_Register(&hd, g_pChatPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact) -{ - return WindowList_Find(g_hWindowList, hContact); -} - -MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact) -{ - HWND hwnd = Srmm_FindWindow(hContact); - return (hwnd) ? (CMsgDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA) : nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// serializes all thread-unsafe operation to the first thread - -struct SSTParam -{ - HWND hwnd; - const wchar_t *wszText; - HICON hIcon; -}; - -static INT_PTR CALLBACK sttSetStatusText(void *_p) -{ - SSTParam *param = (SSTParam*)_p; - - CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(param->hwnd, GWLP_USERDATA); - if (pDlg != nullptr) - pDlg->SetStatusText(param->wszText, param->hIcon); - return 0; -} - -MIR_APP_DLL(void) Srmm_SetStatusText(MCONTACT hContact, const wchar_t *wszText, HICON hIcon) -{ - HWND hwnd = Srmm_FindWindow(hContact); - if (hwnd == nullptr) - hwnd = Srmm_FindWindow(db_mc_getMeta(hContact)); - if (hwnd == nullptr) - return; - - SSTParam param = { hwnd, wszText, hIcon }; - CallFunctionSync(sttSetStatusText, ¶m); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" +#include "chat.h" + +const char *g_pszHotkeySection; + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(DWORD) CALLBACK Srmm_LogStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb) +{ + LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA*)dwCookie; + if (lstrdat) { + // create the RTF + if (lstrdat->buffer == nullptr) { + lstrdat->bufferOffset = 0; + lstrdat->buffer = g_chatApi.Log_CreateRTF(lstrdat); + lstrdat->bufferLen = (int)mir_strlen(lstrdat->buffer); + } + + // give the RTF to the RE control + *pcb = min(cb, LONG(lstrdat->bufferLen - lstrdat->bufferOffset)); + memcpy(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb); + lstrdat->bufferOffset += *pcb; + + // free stuff if the streaming operation is complete + if (lstrdat->bufferOffset == lstrdat->bufferLen) { + mir_free(lstrdat->buffer); + lstrdat->buffer = nullptr; + } + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(int) Srmm_GetWindowData(MCONTACT hContact, MessageWindowData &mwd) +{ + if (hContact == 0) + return 1; + + HWND hwnd = WindowList_Find(g_hWindowList, hContact); + if (hwnd == nullptr) + return 1; + + mwd.hwndWindow = hwnd; + mwd.pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + mwd.uState = MSG_WINDOW_STATE_EXISTS; + if (IsWindowVisible(hwnd)) + mwd.uState |= MSG_WINDOW_STATE_VISIBLE; + if (GetForegroundWindow() == hwnd) + mwd.uState |= MSG_WINDOW_STATE_FOCUS; + if (IsIconic(hwnd)) + mwd.uState |= MSG_WINDOW_STATE_ICONIC; + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(void) Srmm_Broadcast(UINT msg, WPARAM wParam, LPARAM lParam) +{ + WindowList_Broadcast(g_hWindowList, msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(void) Srmm_CreateHotkey(const char *pszSection, const char *pszDescription) +{ + g_pszHotkeySection = pszSection; + + uint16_t wHotKey = HOTKEYCODE(0, VK_RETURN); + if (db_get_b(0, SRMM_MODULE, "SendOnCtrlEnter")) + wHotKey = HOTKEYCODE(HOTKEYF_CONTROL, VK_RETURN); + + if (db_get_b(0, "Tab_SRMsg", "sendonshiftenter")) + wHotKey = HOTKEYCODE(HOTKEYF_SHIFT, VK_RETURN); + + HOTKEYDESC hd = { "tabsrmm_send", pszDescription, pszSection, 0, wHotKey, 0, 100 }; + Hotkey_Register(&hd, g_pChatPlugin); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_APP_DLL(HWND) Srmm_FindWindow(MCONTACT hContact) +{ + return WindowList_Find(g_hWindowList, hContact); +} + +MIR_APP_DLL(CMsgDialog*) Srmm_FindDialog(MCONTACT hContact) +{ + HWND hwnd = Srmm_FindWindow(hContact); + return (hwnd) ? (CMsgDialog *)GetWindowLongPtr(hwnd, GWLP_USERDATA) : nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// serializes all thread-unsafe operation to the first thread + +struct SSTParam +{ + HWND hwnd; + const wchar_t *wszText; + HICON hIcon; +}; + +static INT_PTR CALLBACK sttSetStatusText(void *_p) +{ + SSTParam *param = (SSTParam*)_p; + + CSrmmBaseDialog *pDlg = (CSrmmBaseDialog*)GetWindowLongPtr(param->hwnd, GWLP_USERDATA); + if (pDlg != nullptr) + pDlg->SetStatusText(param->wszText, param->hIcon); + return 0; +} + +MIR_APP_DLL(void) Srmm_SetStatusText(MCONTACT hContact, const wchar_t *wszText, HICON hIcon) +{ + HWND hwnd = Srmm_FindWindow(hContact); + if (hwnd == nullptr) + hwnd = Srmm_FindWindow(db_mc_getMeta(hContact)); + if (hwnd == nullptr) + return; + + SSTParam param = { hwnd, wszText, hIcon }; + CallFunctionSync(sttSetStatusText, ¶m); +} diff --git a/src/mir_app/src/stdafx.cxx b/src/mir_app/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/mir_app/src/stdafx.cxx +++ b/src/mir_app/src/stdafx.cxx @@ -1,6 +1,6 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/mir_app/src/stdafx.h b/src/mir_app/src/stdafx.h index e40748c95a..cbb17fac19 100644 --- a/src/mir_app/src/stdafx.h +++ b/src/mir_app/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/usedIcons.cpp b/src/mir_app/src/usedIcons.cpp index dec0672025..fc3274aa60 100644 --- a/src/mir_app/src/usedIcons.cpp +++ b/src/mir_app/src/usedIcons.cpp @@ -1,7 +1,7 @@ /* Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/src/mir_app/src/usedIcons.h b/src/mir_app/src/usedIcons.h index 62c5539bd7..699f30ea7f 100644 --- a/src/mir_app/src/usedIcons.h +++ b/src/mir_app/src/usedIcons.h @@ -1,7 +1,7 @@ /* Copyright (C) 2009 Ricardo Pescuma Domenecci -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/src/mir_app/src/userInfo.cpp b/src/mir_app/src/userInfo.cpp index deffc648a3..196ea0ae0a 100644 --- a/src/mir_app/src/userInfo.cpp +++ b/src/mir_app/src/userInfo.cpp @@ -1,45 +1,45 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// User info page base dialog - -CUserInfoPageDlg::CUserInfoPageDlg(class CMPluginBase &pPlug, int idDialog) : - CDlgBase(pPlug, idDialog) -{ - m_forceResizable = true; -} - -bool CUserInfoPageDlg::IsEmpty() const -{ - return false; -} - -bool CUserInfoPageDlg::OnRefresh() -{ - return false; -} - +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// User info page base dialog + +CUserInfoPageDlg::CUserInfoPageDlg(class CMPluginBase &pPlug, int idDialog) : + CDlgBase(pPlug, idDialog) +{ + m_forceResizable = true; +} + +bool CUserInfoPageDlg::IsEmpty() const +{ + return false; +} + +bool CUserInfoPageDlg::OnRefresh() +{ + return false; +} + diff --git a/src/mir_app/src/utils.cpp b/src/mir_app/src/utils.cpp index 36260b3905..42e916d7d7 100644 --- a/src/mir_app/src/utils.cpp +++ b/src/mir_app/src/utils.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_app/src/visibility.cpp b/src/mir_app/src/visibility.cpp index 1aaa1421a5..8c116a2497 100644 --- a/src/mir_app/src/visibility.cpp +++ b/src/mir_app/src/visibility.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/res/version.rc b/src/mir_core/res/version.rc index 626717e99b..6d60274913 100644 --- a/src/mir_core/res/version.rc +++ b/src/mir_core/res/version.rc @@ -1,57 +1,57 @@ -#ifdef APSTUDIO_INVOKED -#error this file is not editable by Microsoft Visual C++ -#endif //APSTUDIO_INVOKED - -#include -#include - -#include "../include/m_version.h" - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1250) -#endif //_WIN32 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION MIRANDA_VERSION_FILEVERSION - PRODUCTVERSION MIRANDA_VERSION_FILEVERSION - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000004b0" - BEGIN - VALUE "Comments", "Licensed under the terms of the GNU General Public License\0" - VALUE "CompanyName", "Miranda NG team\0" - VALUE "FileDescription", "Miranda NG\0" - VALUE "FileVersion", MIRANDA_VERSION_DISPLAY - VALUE "InternalName", "mir_core\0" - VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "mir_core.mir\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Miranda NG\0" - VALUE "ProductVersion", MIRANDA_VERSION_DISPLAY - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 - END -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include +#include + +#include "../include/m_version.h" + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1250) +#endif //_WIN32 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MIRANDA_VERSION_FILEVERSION + PRODUCTVERSION MIRANDA_VERSION_FILEVERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "Licensed under the terms of the GNU General Public License\0" + VALUE "CompanyName", "Miranda NG team\0" + VALUE "FileDescription", "Miranda NG\0" + VALUE "FileVersion", MIRANDA_VERSION_DISPLAY + VALUE "InternalName", "mir_core\0" + VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "mir_core.mir\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Miranda NG\0" + VALUE "ProductVersion", MIRANDA_VERSION_DISPLAY + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// diff --git a/src/mir_core/src/Linux/CCtrlBase.cpp b/src/mir_core/src/Linux/CCtrlBase.cpp index cba8567e52..ec84f1b77c 100644 --- a/src/mir_core/src/Linux/CCtrlBase.cpp +++ b/src/mir_core/src/Linux/CCtrlBase.cpp @@ -1,205 +1,205 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csCtrl; - -static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} - -static LIST arControls(10, CompareControls); - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlBase - -CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : - m_parentWnd(wnd), - m_idCtrl(idCtrl) -{ - if (wnd) - wnd->AddControl(this); -} - -CCtrlBase::~CCtrlBase() -{ -} - -void CCtrlBase::OnInit() -{ - m_hwnd = nullptr; -} - -void CCtrlBase::OnDestroy() -{ - void *bullshit[2]; // vfptr + hwnd - bullshit[1] = m_hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - pCtrl->Unsubclass(); - arControls.remove(pCtrl); - } - - evas_object_del(m_hwnd); - m_hwnd = nullptr; -} - -bool CCtrlBase::OnApply() -{ - m_bChanged = false; - return true; -} - -void CCtrlBase::OnReset() -{ -} - -void CCtrlBase::Show(bool bShow) -{ - // ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); -} - -void CCtrlBase::Enable(bool bIsEnable) -{ - // ::EnableWindow(m_hwnd, bIsEnable); -} - -bool CCtrlBase::Enabled() const -{ - return (m_hwnd != nullptr); -} - -void CCtrlBase::NotifyChange() -{ - if (!m_parentWnd) - return; - - if (m_parentWnd->IsInitialized()) { - m_bChanged = true; - if (!m_bSilent) - m_parentWnd->NotifyChange(); - } - - OnChange(this); -} - -LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const -{ - // return ::SendMessage(m_hwnd, Msg, wParam, lParam); - return 0; -} - -void CCtrlBase::SetText(const wchar_t *text) -{ - // ::SetWindowText(m_hwnd, text); -} - -void CCtrlBase::SetTextA(const char *text) -{ - // ::SetWindowTextA(m_hwnd, text); -} - -void CCtrlBase::SetDraw(bool bEnable) -{ - // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CCtrlBase::SetInt(int value) -{ - wchar_t buf[32] = { 0 }; - mir_snwprintf(buf, L"%d", value); - SetText(buf); -} - -wchar_t* CCtrlBase::GetText() const -{ - return mir_wstrdup(L""); -} - -char* CCtrlBase::GetTextA() const -{ - return mir_strdup(""); -} - -char* CCtrlBase::GetTextU() const -{ - return mir_utf8encodeW(ptrW(GetText())); -} - -wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const -{ - // GetWindowTextW(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextA(char *buf, size_t size) const -{ - // GetWindowTextA(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextU(char *buf, size_t size) const -{ - ptrW wszText(GetText()); - strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); - return buf; -} - -int CCtrlBase::GetInt() const -{ - // int length = GetWindowTextLengthW(m_hwnd) + 1; - // wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); - // GetWindowTextW(m_hwnd, result, length); - // return _wtoi(result); - return 0; -} - -void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - pos.iCurr = -1; - - // if (pos.pt.x == 0 && pos.pt.y == 0) - // GetCursorPos(&pos.pt); -} - -LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) -{ - return FALSE; -} - -void CCtrlBase::Subclass() -{ - // mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); - - mir_cslock lck(csCtrl); - arControls.insert(this); -} - -void CCtrlBase::Unsubclass() -{ - // mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +static mir_cs csCtrl; + +static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) +{ + return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); +} + +static LIST arControls(10, CompareControls); + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlBase + +CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : + m_parentWnd(wnd), + m_idCtrl(idCtrl) +{ + if (wnd) + wnd->AddControl(this); +} + +CCtrlBase::~CCtrlBase() +{ +} + +void CCtrlBase::OnInit() +{ + m_hwnd = nullptr; +} + +void CCtrlBase::OnDestroy() +{ + void *bullshit[2]; // vfptr + hwnd + bullshit[1] = m_hwnd; + CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); + if (pCtrl) { + pCtrl->Unsubclass(); + arControls.remove(pCtrl); + } + + evas_object_del(m_hwnd); + m_hwnd = nullptr; +} + +bool CCtrlBase::OnApply() +{ + m_bChanged = false; + return true; +} + +void CCtrlBase::OnReset() +{ +} + +void CCtrlBase::Show(bool bShow) +{ + // ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); +} + +void CCtrlBase::Enable(bool bIsEnable) +{ + // ::EnableWindow(m_hwnd, bIsEnable); +} + +bool CCtrlBase::Enabled() const +{ + return (m_hwnd != nullptr); +} + +void CCtrlBase::NotifyChange() +{ + if (!m_parentWnd) + return; + + if (m_parentWnd->IsInitialized()) { + m_bChanged = true; + if (!m_bSilent) + m_parentWnd->NotifyChange(); + } + + OnChange(this); +} + +LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const +{ + // return ::SendMessage(m_hwnd, Msg, wParam, lParam); + return 0; +} + +void CCtrlBase::SetText(const wchar_t *text) +{ + // ::SetWindowText(m_hwnd, text); +} + +void CCtrlBase::SetTextA(const char *text) +{ + // ::SetWindowTextA(m_hwnd, text); +} + +void CCtrlBase::SetDraw(bool bEnable) +{ + // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); +} + +void CCtrlBase::SetInt(int value) +{ + wchar_t buf[32] = { 0 }; + mir_snwprintf(buf, L"%d", value); + SetText(buf); +} + +wchar_t* CCtrlBase::GetText() const +{ + return mir_wstrdup(L""); +} + +char* CCtrlBase::GetTextA() const +{ + return mir_strdup(""); +} + +char* CCtrlBase::GetTextU() const +{ + return mir_utf8encodeW(ptrW(GetText())); +} + +wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const +{ + // GetWindowTextW(m_hwnd, buf, (int)size); + buf[size - 1] = 0; + return buf; +} + +char* CCtrlBase::GetTextA(char *buf, size_t size) const +{ + // GetWindowTextA(m_hwnd, buf, (int)size); + buf[size - 1] = 0; + return buf; +} + +char* CCtrlBase::GetTextU(char *buf, size_t size) const +{ + ptrW wszText(GetText()); + strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); + return buf; +} + +int CCtrlBase::GetInt() const +{ + // int length = GetWindowTextLengthW(m_hwnd) + 1; + // wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); + // GetWindowTextW(m_hwnd, result, length); + // return _wtoi(result); + return 0; +} + +void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + pos.iCurr = -1; + + // if (pos.pt.x == 0 && pos.pt.y == 0) + // GetCursorPos(&pos.pt); +} + +LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) +{ + return FALSE; +} + +void CCtrlBase::Subclass() +{ + // mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); + + mir_cslock lck(csCtrl); + arControls.insert(this); +} + +void CCtrlBase::Unsubclass() +{ + // mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); +} diff --git a/src/mir_core/src/Linux/CCtrlButton.cpp b/src/mir_core/src/Linux/CCtrlButton.cpp index e8c107b888..da569730c2 100644 --- a/src/mir_core/src/Linux/CCtrlButton.cpp +++ b/src/mir_core/src/Linux/CCtrlButton.cpp @@ -1,55 +1,55 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlButton - -CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - -BOOL CCtrlButton::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - // if (idCode == BN_CLICKED) - // OnClick(this); - return FALSE; -} - -void CCtrlButton::Click() -{ - // if (Enabled()) - // ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); -} - -bool CCtrlButton::IsPushed() const -{ - // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; - return false; -} - -void CCtrlButton::Push(bool bPushed) -{ - // if (Enabled()) - // ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlButton + +CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl) +{} + +BOOL CCtrlButton::OnCommand(MWindow, uint16_t, uint16_t idCode) +{ + // if (idCode == BN_CLICKED) + // OnClick(this); + return FALSE; +} + +void CCtrlButton::Click() +{ + // if (Enabled()) + // ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); +} + +bool CCtrlButton::IsPushed() const +{ + // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; + return false; +} + +void CCtrlButton::Push(bool bPushed) +{ + // if (Enabled()) + // ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); +} diff --git a/src/mir_core/src/Linux/CCtrlCheck.cpp b/src/mir_core/src/Linux/CCtrlCheck.cpp index 7c821f0329..112db0b66a 100644 --- a/src/mir_core/src/Linux/CCtrlCheck.cpp +++ b/src/mir_core/src/Linux/CCtrlCheck.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCheck class - -CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{ - m_bNotifiable = true; -} - -BOOL CCtrlCheck::OnCommand(MWindow, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlCheck::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetState()); - return true; -} - -void CCtrlCheck::OnReset() -{ - if (m_dbLink != nullptr) - SetState(LoadInt()); -} - -int CCtrlCheck::GetState() const -{ - // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); - return false; -} - -void CCtrlCheck::SetState(int state) -{ - // ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); -} - -bool CCtrlCheck::IsChecked() -{ - // return GetState() == BST_CHECKED; - return false; -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlCheck class + +CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) + : CCtrlData(dlg, ctrlId) +{ + m_bNotifiable = true; +} + +BOOL CCtrlCheck::OnCommand(MWindow, uint16_t, uint16_t) +{ + NotifyChange(); + return TRUE; +} + +bool CCtrlCheck::OnApply() +{ + CSuper::OnApply(); + + if (m_dbLink != nullptr) + SaveInt(GetState()); + return true; +} + +void CCtrlCheck::OnReset() +{ + if (m_dbLink != nullptr) + SetState(LoadInt()); +} + +int CCtrlCheck::GetState() const +{ + // return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); + return false; +} + +void CCtrlCheck::SetState(int state) +{ + // ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); +} + +bool CCtrlCheck::IsChecked() +{ + // return GetState() == BST_CHECKED; + return false; +} diff --git a/src/mir_core/src/Linux/CCtrlClc.cpp b/src/mir_core/src/Linux/CCtrlClc.cpp index cf4f05cda6..57f5607ef2 100644 --- a/src/mir_core/src/Linux/CCtrlClc.cpp +++ b/src/mir_core/src/Linux/CCtrlClc.cpp @@ -1,207 +1,207 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlClc - -CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; - switch (pnmh->code) { - case CLN_EXPANDED: OnExpanded(&evt); break; - case CLN_LISTREBUILT: OnListRebuilt(&evt); break; - case CLN_ITEMCHECKED: OnItemChecked(&evt); break; - case CLN_DRAGGING: OnDragging(&evt); break; - case CLN_DROPPED: OnDropped(&evt); break; - case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; - case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; - case CLN_DRAGSTOP: OnDragStop(&evt); break; - case CLN_NEWCONTACT: OnNewContact(&evt); break; - case CLN_CONTACTMOVED: OnContactMoved(&evt); break; - case CLN_CHECKCHANGED: OnCheckChanged(&evt); break; - case NM_CLICK: OnClick(&evt); break; - } - return FALSE; -} - -void CCtrlClc::AddContact(MCONTACT hContact) -{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); -} - -void CCtrlClc::AddGroup(HANDLE hGroup) -{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); -} - -void CCtrlClc::AutoRebuild() -{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); -} - -void CCtrlClc::DeleteItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::EditLabel(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); -} - -void CCtrlClc::EndEditLabel(bool save) -{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); -} - -void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) -{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); -} - -void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) -{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); -} - -HANDLE CCtrlClc::FindContact(MCONTACT hContact) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); -} - -HANDLE CCtrlClc::FindGroup(MGROUP hGroup) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); -} - -COLORREF CCtrlClc::GetBkColor() const -{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); -} - -bool CCtrlClc::GetCheck(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; -} - -int CCtrlClc::GetCount() const -{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); -} - -HWND CCtrlClc::GetEditControl() const -{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); -} - -uint32_t CCtrlClc::GetExpand(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); -} - -int CCtrlClc::GetExtraColumns() const -{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); -} - -uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const -{ - return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); -} - -HIMAGELIST CCtrlClc::GetExtraImageList() const -{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); -} - -HFONT CCtrlClc::GetFont(int iFontId) const -{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); -} - -HANDLE CCtrlClc::GetSelection() const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); -} - -HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); -} - -void CCtrlClc::SelectItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::SetBkColor(COLORREF clBack) -{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); -} - -void CCtrlClc::SetCheck(HANDLE hItem, bool check) -{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); -} - -void CCtrlClc::SetExtraColumns(int iColumns) -{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); -} - -void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); -} - -void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); -} - -void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) -{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); -} - -void CCtrlClc::SetItemText(HANDLE hItem, char *szText) -{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); -} - -void CCtrlClc::SetHideEmptyGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); -} - -bool CCtrlClc::GetHideOfflineRoot() const -{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; -} - -void CCtrlClc::SetHideOfflineRoot(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); -} - -void CCtrlClc::SetUseGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); -} - -void CCtrlClc::SetOfflineModes(uint32_t modes) -{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); -} - -uint32_t CCtrlClc::GetExStyle() const -{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); -} - -void CCtrlClc::SetExStyle(uint32_t exStyle) -{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); -} - -HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) -{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); -} - -int CCtrlClc::GetItemType(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); -} - -HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlClc + +CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId) +{} + +BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) +{ + TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; + switch (pnmh->code) { + case CLN_EXPANDED: OnExpanded(&evt); break; + case CLN_LISTREBUILT: OnListRebuilt(&evt); break; + case CLN_ITEMCHECKED: OnItemChecked(&evt); break; + case CLN_DRAGGING: OnDragging(&evt); break; + case CLN_DROPPED: OnDropped(&evt); break; + case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; + case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; + case CLN_DRAGSTOP: OnDragStop(&evt); break; + case CLN_NEWCONTACT: OnNewContact(&evt); break; + case CLN_CONTACTMOVED: OnContactMoved(&evt); break; + case CLN_CHECKCHANGED: OnCheckChanged(&evt); break; + case NM_CLICK: OnClick(&evt); break; + } + return FALSE; +} + +void CCtrlClc::AddContact(MCONTACT hContact) +{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); +} + +void CCtrlClc::AddGroup(HANDLE hGroup) +{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); +} + +void CCtrlClc::AutoRebuild() +{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); +} + +void CCtrlClc::DeleteItem(HANDLE hItem) +{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); +} + +void CCtrlClc::EditLabel(HANDLE hItem) +{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); +} + +void CCtrlClc::EndEditLabel(bool save) +{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); +} + +void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) +{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); +} + +void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) +{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); +} + +HANDLE CCtrlClc::FindContact(MCONTACT hContact) +{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); +} + +HANDLE CCtrlClc::FindGroup(MGROUP hGroup) +{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); +} + +COLORREF CCtrlClc::GetBkColor() const +{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); +} + +bool CCtrlClc::GetCheck(HANDLE hItem) const +{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; +} + +int CCtrlClc::GetCount() const +{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); +} + +HWND CCtrlClc::GetEditControl() const +{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); +} + +uint32_t CCtrlClc::GetExpand(HANDLE hItem) const +{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); +} + +int CCtrlClc::GetExtraColumns() const +{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); +} + +uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const +{ + return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); +} + +HIMAGELIST CCtrlClc::GetExtraImageList() const +{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); +} + +HFONT CCtrlClc::GetFont(int iFontId) const +{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); +} + +HANDLE CCtrlClc::GetSelection() const +{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); +} + +HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const +{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); +} + +void CCtrlClc::SelectItem(HANDLE hItem) +{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); +} + +void CCtrlClc::SetBkColor(COLORREF clBack) +{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); +} + +void CCtrlClc::SetCheck(HANDLE hItem, bool check) +{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); +} + +void CCtrlClc::SetExtraColumns(int iColumns) +{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); +} + +void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) +{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); +} + +void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) +{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); +} + +void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) +{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); +} + +void CCtrlClc::SetItemText(HANDLE hItem, char *szText) +{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); +} + +void CCtrlClc::SetHideEmptyGroups(bool state) +{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); +} + +bool CCtrlClc::GetHideOfflineRoot() const +{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; +} + +void CCtrlClc::SetHideOfflineRoot(bool state) +{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); +} + +void CCtrlClc::SetUseGroups(bool state) +{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); +} + +void CCtrlClc::SetOfflineModes(uint32_t modes) +{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); +} + +uint32_t CCtrlClc::GetExStyle() const +{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); +} + +void CCtrlClc::SetExStyle(uint32_t exStyle) +{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); +} + +HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) +{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); +} + +int CCtrlClc::GetItemType(HANDLE hItem) const +{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); +} + +HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const +{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); +} diff --git a/src/mir_core/src/Linux/CCtrlColor.cpp b/src/mir_core/src/Linux/CCtrlColor.cpp index 97f5e48e02..b61fb5760a 100644 --- a/src/mir_core/src/Linux/CCtrlColor.cpp +++ b/src/mir_core/src/Linux/CCtrlColor.cpp @@ -1,61 +1,61 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlColor class - -CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlColor::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetColor()); - return true; -} - -void CCtrlColor::OnReset() -{ - if (m_dbLink != nullptr) - SetColor(LoadInt()); -} - -uint32_t CCtrlColor::GetColor() -{ - return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); -} - -void CCtrlColor::SetColor(uint32_t dwValue) -{ - ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlColor class + +CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : + CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) +{ + NotifyChange(); + return TRUE; +} + +bool CCtrlColor::OnApply() +{ + CSuper::OnApply(); + + if (m_dbLink != nullptr) + SaveInt(GetColor()); + return true; +} + +void CCtrlColor::OnReset() +{ + if (m_dbLink != nullptr) + SetColor(LoadInt()); +} + +uint32_t CCtrlColor::GetColor() +{ + return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); +} + +void CCtrlColor::SetColor(uint32_t dwValue) +{ + ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); +} diff --git a/src/mir_core/src/Linux/CCtrlCombo.cpp b/src/mir_core/src/Linux/CCtrlCombo.cpp index 063588e39e..52a85f1115 100644 --- a/src/mir_core/src/Linux/CCtrlCombo.cpp +++ b/src/mir_core/src/Linux/CCtrlCombo.cpp @@ -1,169 +1,169 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCombo class - -CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlCombo::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - switch (idCode) { - // case CBN_CLOSEUP: OnCloseup(this); break; - // case CBN_DROPDOWN: OnDropdown(this); break; - // case CBN_SELCHANGE: OnSelChanged(this); break; - // case CBN_KILLFOCUS: OnKillFocus(this); break; - - // case CBN_EDITCHANGE: - // case CBN_EDITUPDATE: - // case CBN_SELENDOK: - // NotifyChange(); - break; - } - return TRUE; -} - -void CCtrlCombo::OnInit() -{ - CSuper::OnInit(); - OnReset(); -} - -bool CCtrlCombo::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - // int len = GetWindowTextLength(m_hwnd) + 1; - // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - // GetWindowText(m_hwnd, buf, len); - // SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlCombo::OnReset() -{ - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -LPARAM CCtrlCombo::GetCurData() const -{ - return GetItemData(GetCurSel()); -} - -// selects line with userdata passed -int CCtrlCombo::SelectData(LPARAM data) -{ - int ret = -1, nCount = GetCount(); - - for (int i = 0; i < nCount; i++) - if (GetItemData(i) == data) { - ret = i; - break; - } - - return SetCurSel(ret); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Windows API - -int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) -{ - return -1; -} - -int CCtrlCombo::AddStringA(const char *text, LPARAM data) -{ - return -1; -} - -void CCtrlCombo::DeleteString(int index) -{ -} - -int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) -{ return 0; -} - -int CCtrlCombo::FindStringA(const char *str, int index, bool exact) -{ return 0; -} - -int CCtrlCombo::GetCount() const -{ return 0; -} - -int CCtrlCombo::GetCurSel() const -{ return 0; -} - -bool CCtrlCombo::GetDroppedState() const -{ return 0; -} - -LPARAM CCtrlCombo::GetItemData(int index) const -{ return 0; -} - -wchar_t* CCtrlCombo::GetItemText(int index) const -{ return 0; -} - -wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const -{ return 0; -} - -int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) -{ return 0; -} - -void CCtrlCombo::ResetContent() -{ -} - -int CCtrlCombo::SelectString(const wchar_t *str) -{ return 0; -} - -int CCtrlCombo::SetCurSel(int index) -{ return 0; -} - -void CCtrlCombo::SetItemData(int index, LPARAM data) -{ -} - -void CCtrlCombo::ShowDropdown(bool show) -{ -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlCombo class + +CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) + : CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlCombo::OnCommand(MWindow, uint16_t, uint16_t idCode) +{ + switch (idCode) { + // case CBN_CLOSEUP: OnCloseup(this); break; + // case CBN_DROPDOWN: OnDropdown(this); break; + // case CBN_SELCHANGE: OnSelChanged(this); break; + // case CBN_KILLFOCUS: OnKillFocus(this); break; + + // case CBN_EDITCHANGE: + // case CBN_EDITUPDATE: + // case CBN_SELENDOK: + // NotifyChange(); + break; + } + return TRUE; +} + +void CCtrlCombo::OnInit() +{ + CSuper::OnInit(); + OnReset(); +} + +bool CCtrlCombo::OnApply() +{ + CSuper::OnApply(); + + if (GetDataType() == DBVT_WCHAR) { + // int len = GetWindowTextLength(m_hwnd) + 1; + // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); + // GetWindowText(m_hwnd, buf, len); + // SaveText(buf); + } + else if (GetDataType() != DBVT_DELETED) { + SaveInt(GetInt()); + } + return true; +} + +void CCtrlCombo::OnReset() +{ + if (GetDataType() == DBVT_WCHAR) + SetText(LoadText()); + else if (GetDataType() != DBVT_DELETED) + SetInt(LoadInt()); +} + +LPARAM CCtrlCombo::GetCurData() const +{ + return GetItemData(GetCurSel()); +} + +// selects line with userdata passed +int CCtrlCombo::SelectData(LPARAM data) +{ + int ret = -1, nCount = GetCount(); + + for (int i = 0; i < nCount; i++) + if (GetItemData(i) == data) { + ret = i; + break; + } + + return SetCurSel(ret); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Windows API + +int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) +{ + return -1; +} + +int CCtrlCombo::AddStringA(const char *text, LPARAM data) +{ + return -1; +} + +void CCtrlCombo::DeleteString(int index) +{ +} + +int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) +{ return 0; +} + +int CCtrlCombo::FindStringA(const char *str, int index, bool exact) +{ return 0; +} + +int CCtrlCombo::GetCount() const +{ return 0; +} + +int CCtrlCombo::GetCurSel() const +{ return 0; +} + +bool CCtrlCombo::GetDroppedState() const +{ return 0; +} + +LPARAM CCtrlCombo::GetItemData(int index) const +{ return 0; +} + +wchar_t* CCtrlCombo::GetItemText(int index) const +{ return 0; +} + +wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const +{ return 0; +} + +int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) +{ return 0; +} + +void CCtrlCombo::ResetContent() +{ +} + +int CCtrlCombo::SelectString(const wchar_t *str) +{ return 0; +} + +int CCtrlCombo::SetCurSel(int index) +{ return 0; +} + +void CCtrlCombo::SetItemData(int index, LPARAM data) +{ +} + +void CCtrlCombo::ShowDropdown(bool show) +{ +} diff --git a/src/mir_core/src/Linux/CCtrlData.cpp b/src/mir_core/src/Linux/CCtrlData.cpp index fab5ca0409..2a60d9d31a 100644 --- a/src/mir_core/src/Linux/CCtrlData.cpp +++ b/src/mir_core/src/Linux/CCtrlData.cpp @@ -1,52 +1,52 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlData class - -CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_dbLink(nullptr) -{} - -CCtrlData::~CCtrlData() -{ - delete m_dbLink; -} - -void CCtrlData::OnInit() -{ - CCtrlBase::OnInit(); - OnReset(); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlData class + +CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl), + m_dbLink(nullptr) +{} + +CCtrlData::~CCtrlData() +{ + delete m_dbLink; +} + +void CCtrlData::OnInit() +{ + CCtrlBase::OnInit(); + OnReset(); +} + +void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) +{ + m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); +} + +void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) +{ + m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); +} diff --git a/src/mir_core/src/Linux/CCtrlEdit.cpp b/src/mir_core/src/Linux/CCtrlEdit.cpp index 7f1ac14cc2..42a9dd6327 100644 --- a/src/mir_core/src/Linux/CCtrlEdit.cpp +++ b/src/mir_core/src/Linux/CCtrlEdit.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlEdit class - -CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlEdit::OnCommand(MWindow, uint16_t, uint16_t idCode) -{ - // if (idCode == EN_CHANGE) - // NotifyChange(); - return TRUE; -} - -bool CCtrlEdit::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - // int len = GetWindowTextLength(m_hwnd) + 1; - // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - // GetWindowText(m_hwnd, buf, len); - // SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlEdit::OnReset() -{ - // m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; - - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -void CCtrlEdit::SetMaxLength(unsigned int len) -{ - // SendMsg(EM_SETLIMITTEXT, len, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlEdit class + +CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) + : CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlEdit::OnCommand(MWindow, uint16_t, uint16_t idCode) +{ + // if (idCode == EN_CHANGE) + // NotifyChange(); + return TRUE; +} + +bool CCtrlEdit::OnApply() +{ + CSuper::OnApply(); + + if (GetDataType() == DBVT_WCHAR) { + // int len = GetWindowTextLength(m_hwnd) + 1; + // wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); + // GetWindowText(m_hwnd, buf, len); + // SaveText(buf); + } + else if (GetDataType() != DBVT_DELETED) { + SaveInt(GetInt()); + } + return true; +} + +void CCtrlEdit::OnReset() +{ + // m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; + + if (GetDataType() == DBVT_WCHAR) + SetText(LoadText()); + else if (GetDataType() != DBVT_DELETED) + SetInt(LoadInt()); +} + +void CCtrlEdit::SetMaxLength(unsigned int len) +{ + // SendMsg(EM_SETLIMITTEXT, len, 0); +} diff --git a/src/mir_core/src/Linux/CCtrlHyperlink.cpp b/src/mir_core/src/Linux/CCtrlHyperlink.cpp index ca92d19dbc..0e0d93a689 100644 --- a/src/mir_core/src/Linux/CCtrlHyperlink.cpp +++ b/src/mir_core/src/Linux/CCtrlHyperlink.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlHyperlink - -CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) - : CCtrlBase(wnd, idCtrl), - m_url(url) -{ - OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); -} - -BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) -{ - OnClick(this); - return FALSE; -} - -void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) -{ - ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); -} - -void CCtrlHyperlink::SetUrl(const char *url) -{ - m_url = url; -} - -const char* CCtrlHyperlink::GetUrl() -{ - return m_url; -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlHyperlink + +CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) + : CCtrlBase(wnd, idCtrl), + m_url(url) +{ + OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); +} + +BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) +{ + OnClick(this); + return FALSE; +} + +void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) +{ + ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); +} + +void CCtrlHyperlink::SetUrl(const char *url) +{ + m_url = url; +} + +const char* CCtrlHyperlink::GetUrl() +{ + return m_url; +} diff --git a/src/mir_core/src/Linux/CCtrlLabel.cpp b/src/mir_core/src/Linux/CCtrlLabel.cpp index 0c097c0655..ff05f3c8cf 100644 --- a/src/mir_core/src/Linux/CCtrlLabel.cpp +++ b/src/mir_core/src/Linux/CCtrlLabel.cpp @@ -1,30 +1,30 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlLabel - -CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlLabel + +CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl) +{} diff --git a/src/mir_core/src/Linux/CCtrlListBox.cpp b/src/mir_core/src/Linux/CCtrlListBox.cpp index abaa31a786..6d9f58fc23 100644 --- a/src/mir_core/src/Linux/CCtrlListBox.cpp +++ b/src/mir_core/src/Linux/CCtrlListBox.cpp @@ -1,160 +1,160 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListBox class - -CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case LBN_DBLCLK: OnDblClick(this); break; - case LBN_SELCANCEL: OnSelCancel(this); break; - case LBN_SELCHANGE: OnSelChange(this); break; - } - return TRUE; -} - -void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetCurSel(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = ListBox_AddString(m_hwnd, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::DeleteString(int index) -{ ListBox_DeleteString(m_hwnd, index); -} - -int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlListBox::GetCount() const -{ return ListBox_GetCount(m_hwnd); -} - -int CCtrlListBox::GetCurSel() const -{ return ListBox_GetCurSel(m_hwnd); -} - -LPARAM CCtrlListBox::GetItemData(int index) const -{ return ListBox_GetItemData(m_hwnd, index); -} - -int CCtrlListBox::GetItemRect(int index, RECT *pResult) const -{ return ListBox_GetItemRect(m_hwnd, index, pResult); -} - -wchar_t* CCtrlListBox::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -bool CCtrlListBox::GetSel(int index) const -{ return ListBox_GetSel(m_hwnd, index) ? true : false; -} - -int CCtrlListBox::GetSelCount() const -{ return ListBox_GetSelCount(m_hwnd); -} - -int* CCtrlListBox::GetSelItems(int *items, int count) const -{ - ListBox_GetSelItems(m_hwnd, count, items); - return items; -} - -int* CCtrlListBox::GetSelItems() const -{ - int count = GetSelCount() + 1; - int *result = (int *)mir_alloc(sizeof(int) * count); - ListBox_GetSelItems(m_hwnd, count, result); - result[count-1] = -1; - return result; -} - -int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = ListBox_InsertString(m_hwnd, pos, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::ResetContent() -{ ListBox_ResetContent(m_hwnd); -} - -int CCtrlListBox::SelectString(const wchar_t *str) -{ return ListBox_SelectString(m_hwnd, 0, str); -} - -int CCtrlListBox::SetCurSel(int index) -{ return ListBox_SetCurSel(m_hwnd, index); -} - -void CCtrlListBox::SetItemData(int index, LPARAM data) -{ ListBox_SetItemData(m_hwnd, index, data); -} - -void CCtrlListBox::SetItemHeight(int index, int iHeight) -{ ListBox_SetItemHeight(m_hwnd, index, iHeight); -} - -void CCtrlListBox::SetSel(int index, bool sel) -{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlListBox class + +CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId) +{} + +BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + switch (idCode) { + case LBN_DBLCLK: OnDblClick(this); break; + case LBN_SELCANCEL: OnSelCancel(this); break; + case LBN_SELCHANGE: OnSelChange(this); break; + } + return TRUE; +} + +void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + if (pos.pt.x == 0 && pos.pt.y == 0) { + pos.iCurr = GetCurSel(); + if (pos.iCurr != -1) { + RECT rc; + GetItemRect(pos.iCurr, &rc); + pos.pt.x = rc.left + 8; + pos.pt.y = rc.top + 8; + ClientToScreen(m_hwnd, &pos.pt); + return; + } + } + + CSuper::GetCaretPos(pos); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) +{ + int iItem = ListBox_AddString(m_hwnd, text); + ListBox_SetItemData(m_hwnd, iItem, data); + return iItem; +} + +void CCtrlListBox::DeleteString(int index) +{ ListBox_DeleteString(m_hwnd, index); +} + +int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) +{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); +} + +int CCtrlListBox::GetCount() const +{ return ListBox_GetCount(m_hwnd); +} + +int CCtrlListBox::GetCurSel() const +{ return ListBox_GetCurSel(m_hwnd); +} + +LPARAM CCtrlListBox::GetItemData(int index) const +{ return ListBox_GetItemData(m_hwnd, index); +} + +int CCtrlListBox::GetItemRect(int index, RECT *pResult) const +{ return ListBox_GetItemRect(m_hwnd, index, pResult); +} + +wchar_t* CCtrlListBox::GetItemText(int index) const +{ + wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); + SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); + return result; +} + +wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const +{ + wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); + SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); + mir_wstrncpy(buf, result, size); + return buf; +} + +bool CCtrlListBox::GetSel(int index) const +{ return ListBox_GetSel(m_hwnd, index) ? true : false; +} + +int CCtrlListBox::GetSelCount() const +{ return ListBox_GetSelCount(m_hwnd); +} + +int* CCtrlListBox::GetSelItems(int *items, int count) const +{ + ListBox_GetSelItems(m_hwnd, count, items); + return items; +} + +int* CCtrlListBox::GetSelItems() const +{ + int count = GetSelCount() + 1; + int *result = (int *)mir_alloc(sizeof(int) * count); + ListBox_GetSelItems(m_hwnd, count, result); + result[count-1] = -1; + return result; +} + +int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) +{ + int iItem = ListBox_InsertString(m_hwnd, pos, text); + ListBox_SetItemData(m_hwnd, iItem, data); + return iItem; +} + +void CCtrlListBox::ResetContent() +{ ListBox_ResetContent(m_hwnd); +} + +int CCtrlListBox::SelectString(const wchar_t *str) +{ return ListBox_SelectString(m_hwnd, 0, str); +} + +int CCtrlListBox::SetCurSel(int index) +{ return ListBox_SetCurSel(m_hwnd, index); +} + +void CCtrlListBox::SetItemData(int index, LPARAM data) +{ ListBox_SetItemData(m_hwnd, index, data); +} + +void CCtrlListBox::SetItemHeight(int index, int iHeight) +{ ListBox_SetItemHeight(m_hwnd, index, iHeight); +} + +void CCtrlListBox::SetSel(int index, bool sel) +{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); +} diff --git a/src/mir_core/src/Linux/CCtrlListView.cpp b/src/mir_core/src/Linux/CCtrlListView.cpp index eb57951a3c..40bb1f481e 100644 --- a/src/mir_core/src/Linux/CCtrlListView.cpp +++ b/src/mir_core/src/Linux/CCtrlListView.cpp @@ -1,551 +1,551 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListView - -CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_CLICK: OnClick(&evt); return TRUE; - case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; - case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; - case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; - case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; - case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; - case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; - case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; - case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; - case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; - case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; - case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; - case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - - case LVN_ITEMCHANGED: - if (!m_parentWnd || !m_parentWnd->IsInitialized()) - return FALSE; - - OnItemChanged(&evt); - - // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes - if (evt.nmlv->uChanged & LVIF_STATE) - if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) - NotifyChange(); - return TRUE; - - case LVN_ODSTATECHANGED: - NotifyChange(); - return TRUE; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) -{ - int result = l1 - l2; - int newItem = HIWORD(param); - int oldItem = LOWORD(param); - if (newItem > oldItem) - return (l1 == oldItem && l2 <= newItem) ? 1 : result; - - return (l2 == oldItem && l1 >= newItem) ? 1 : result; -} - -int CCtrlListView::MoveItem(int idx, int direction) -{ - if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) - return idx; - - if (idx < 0) - idx = GetNextItem(-1, LVNI_FOCUSED); - SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); - return idx + direction; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CCtrlListView::SetCurSel(int idx) -{ - SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -// additional api -HIMAGELIST CCtrlListView::CreateImageList(int iImageList) -{ - HIMAGELIST hIml = GetImageList(iImageList); - if (hIml) - return hIml; - - hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - SetImageList(hIml, iImageList); - return hIml; -} - -void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) -{ - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc.iImage = 0; - lvc.pszText = (LPWSTR)name; - lvc.cx = cx; - lvc.iSubItem = iSubItem; - InsertColumn(iSubItem, &lvc); -} - -void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) -{ - LVGROUP lvg = { 0 }; - lvg.cbSize = sizeof(lvg); - lvg.mask = LVGF_HEADER | LVGF_GROUPID; - lvg.pszHeader = (LPWSTR)name; - lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); - lvg.iGroupId = iGroupId; - InsertGroup(-1, &lvg); -} - -int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = 0; - lvi.pszText = (LPWSTR)text; - lvi.iImage = iIcon; - lvi.lParam = lParam; - if (iGroupId >= 0) { - lvi.mask |= LVIF_GROUPID; - lvi.iGroupId = iGroupId; - } - - return InsertItem(&lvi); -} - -void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_TEXT; - lvi.iItem = iItem; - lvi.iSubItem = iSubItem; - lvi.pszText = (LPWSTR)text; - if (iIcon >= 0) { - lvi.mask |= LVIF_IMAGE; - lvi.iImage = iIcon; - } - - SetItem(&lvi); -} - -LPARAM CCtrlListView::GetItemData(int iItem) const -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - return GetItem(&lvi) ? lvi.lParam : -1; -} - -void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetSelectionMark(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - LVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (SubItemHitTest(&hti) != -1) { - pos.iCurr = hti.iItem; - return; - } - } - CSuper::GetCaretPos(pos); -} - -// classic api -uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) -{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); -} -void CCtrlListView::Arrange(UINT code) -{ ListView_Arrange(m_hwnd, code); -} -void CCtrlListView::CancelEditLabel() -{ ListView_CancelEditLabel(m_hwnd); -} -HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) -{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); -} -void CCtrlListView::DeleteAllItems() -{ ListView_DeleteAllItems(m_hwnd); -} -void CCtrlListView::DeleteColumn(int iCol) -{ ListView_DeleteColumn(m_hwnd, iCol); -} -void CCtrlListView::DeleteItem(int iItem) -{ ListView_DeleteItem(m_hwnd, iItem); -} -HWND CCtrlListView::EditLabel(int iItem) -{ return ListView_EditLabel(m_hwnd, iItem); -} -int CCtrlListView::EnableGroupView(BOOL fEnable) -{ return ListView_EnableGroupView(m_hwnd, fEnable); -} -BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) -{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); -} -int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) -{ return ListView_FindItem(m_hwnd, iStart, plvfi); -} -COLORREF CCtrlListView::GetBkColor() const -{ return ListView_GetBkColor(m_hwnd); -} -void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const -{ ListView_GetBkImage(m_hwnd, plvbki); -} -UINT CCtrlListView::GetCallbackMask() const -{ return ListView_GetCallbackMask(m_hwnd); -} -BOOL CCtrlListView::GetCheckState(UINT iIndex) const -{ return ListView_GetCheckState(m_hwnd, iIndex); -} -void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const -{ ListView_GetColumn(m_hwnd, iCol, pcol); -} -void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const -{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -int CCtrlListView::GetColumnWidth(int iCol) const -{ return ListView_GetColumnWidth(m_hwnd, iCol); -} -int CCtrlListView::GetCountPerPage() const -{ return ListView_GetCountPerPage(m_hwnd); -} -HWND CCtrlListView::GetEditControl() const -{ return ListView_GetEditControl(m_hwnd); -} -uint32_t CCtrlListView::GetExtendedListViewStyle() const -{ return ListView_GetExtendedListViewStyle(m_hwnd); -} -void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const -{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); -} -HWND CCtrlListView::GetHeader() const -{ return ListView_GetHeader(m_hwnd); -} -HCURSOR CCtrlListView::GetHotCursor() const -{ return ListView_GetHotCursor(m_hwnd); -} -INT CCtrlListView::GetHotItem() const -{ return ListView_GetHotItem(m_hwnd); -} -uint32_t CCtrlListView::GetHoverTime() const -{ return ListView_GetHoverTime(m_hwnd); -} -HIMAGELIST CCtrlListView::GetImageList(int iImageList) const -{ return ListView_GetImageList(m_hwnd, iImageList); -} -BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const -{ return ListView_GetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::GetInsertMarkColor() const -{ return ListView_GetInsertMarkColor(m_hwnd); -} -int CCtrlListView::GetInsertMarkRect(LPRECT prc) const -{ return ListView_GetInsertMarkRect(m_hwnd, prc); -} -BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const -{ return ListView_GetISearchString(m_hwnd, lpsz); -} -bool CCtrlListView::GetItem(LPLVITEM pitem) const -{ return ListView_GetItem(m_hwnd, pitem) == TRUE; -} -int CCtrlListView::GetItemCount() const -{ return ListView_GetItemCount(m_hwnd); -} -void CCtrlListView::GetItemPosition(int i, POINT *ppt) const -{ ListView_GetItemPosition(m_hwnd, i, ppt); -} -void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const -{ ListView_GetItemRect(m_hwnd, i, prc, code); -} -uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const -{ return ListView_GetItemSpacing(m_hwnd, fSmall); -} -UINT CCtrlListView::GetItemState(int i, UINT mask) const -{ return ListView_GetItemState(m_hwnd, i, mask); -} -void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const -{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); -} -int CCtrlListView::GetNextItem(int iStart, UINT flags) const -{ return ListView_GetNextItem(m_hwnd, iStart, flags); -} -BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const -{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); -} -BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const -{ return ListView_GetOrigin(m_hwnd, lpptOrg); -} -COLORREF CCtrlListView::GetOutlineColor() const -{ return ListView_GetOutlineColor(m_hwnd); -} -UINT CCtrlListView::GetSelectedColumn() const -{ return ListView_GetSelectedColumn(m_hwnd); -} -UINT CCtrlListView::GetSelectedCount() const -{ return ListView_GetSelectedCount(m_hwnd); -} -INT CCtrlListView::GetSelectionMark() const -{ return ListView_GetSelectionMark(m_hwnd); -} -int CCtrlListView::GetStringWidth(LPCSTR psz) const -{ return ListView_GetStringWidth(m_hwnd, psz); -} -BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const -{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); -} -COLORREF CCtrlListView::GetTextBkColor() const -{ return ListView_GetTextBkColor(m_hwnd); -} -COLORREF CCtrlListView::GetTextColor() const -{ return ListView_GetTextColor(m_hwnd); -} -void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const -{ ListView_GetTileInfo(m_hwnd, plvtinfo); -} -void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const -{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::GetToolTips() const -{ return ListView_GetToolTips(m_hwnd); -} -int CCtrlListView::GetTopIndex() const -{ return ListView_GetTopIndex(m_hwnd); -} -BOOL CCtrlListView::GetUnicodeFormat() const -{ return ListView_GetUnicodeFormat(m_hwnd); -} -uint32_t CCtrlListView::GetView() const -{ return ListView_GetView(m_hwnd); -} -BOOL CCtrlListView::GetViewRect(RECT *prc) const -{ return ListView_GetViewRect(m_hwnd, prc); -} -void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const -{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -BOOL CCtrlListView::HasGroup(int dwGroupId) -{ return ListView_HasGroup(m_hwnd, dwGroupId); -} -int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const -{ return ListView_HitTest(m_hwnd, pinfo); -} -int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) -{ return ListView_InsertColumn(m_hwnd, iCol, pcol); -} -int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) -{ return ListView_InsertGroup(m_hwnd, index, pgrp); -} -void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) -{ ListView_InsertGroupSorted(m_hwnd, structInsert); -} -int CCtrlListView::InsertItem(const LVITEM *pitem) -{ return ListView_InsertItem(m_hwnd, pitem); -} -BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) -{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); -} -BOOL CCtrlListView::IsGroupViewEnabled() -{ return ListView_IsGroupViewEnabled(m_hwnd); -} -UINT CCtrlListView::MapIDToIndex(UINT id) -{ return ListView_MapIDToIndex(m_hwnd, id); -} -UINT CCtrlListView::MapIndexToID(UINT index) -{ return ListView_MapIndexToID(m_hwnd, index); -} -BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) -{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); -} -void CCtrlListView::RemoveAllGroups() -{ ListView_RemoveAllGroups(m_hwnd); -} -int CCtrlListView::RemoveGroup(int iGroupId) -{ return ListView_RemoveGroup(m_hwnd, iGroupId); -} -BOOL CCtrlListView::Scroll(int dx, int dy) -{ return ListView_Scroll(m_hwnd, dx, dy); -} -BOOL CCtrlListView::SetBkColor(COLORREF clrBk) -{ return ListView_SetBkColor(m_hwnd, clrBk); -} -BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) -{ return ListView_SetBkImage(m_hwnd, plvbki); -} -BOOL CCtrlListView::SetCallbackMask(UINT mask) -{ return ListView_SetCallbackMask(m_hwnd, mask); -} -void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) -{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); -} -BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) -{ return ListView_SetColumn(m_hwnd, iCol, pcol); -} -BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) -{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) -{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); -} -void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); -} -void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); -} -int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) -{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); -} -void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) -{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); -} -HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) -{ return ListView_SetHotCursor(m_hwnd, hCursor); -} -INT CCtrlListView::SetHotItem(INT iIndex) -{ return ListView_SetHotItem(m_hwnd, iIndex); -} -void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) -{ ListView_SetHoverTime(m_hwnd, dwHoverTime); -} -uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) -{ return ListView_SetIconSpacing(m_hwnd, cx, cy); -} -HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) -{ return ListView_SetImageList(m_hwnd, himl, iImageList); -} -BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) -{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); -} -BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) -{ return ListView_SetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) -{ return ListView_SetInsertMarkColor(m_hwnd, color); -} -BOOL CCtrlListView::SetItem(const LVITEM *pitem) -{ return ListView_SetItem(m_hwnd, pitem); -} -void CCtrlListView::SetItemCount(int cItems) -{ ListView_SetItemCount(m_hwnd, cItems); -} -void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) -{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); -} -BOOL CCtrlListView::SetItemPosition(int i, int x, int y) -{ return ListView_SetItemPosition(m_hwnd, i, x, y); -} -void CCtrlListView::SetItemPosition32(int iItem, int x, int y) -{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); -} -void CCtrlListView::SetItemState(int i, UINT state, UINT mask) -{ ListView_SetItemState(m_hwnd, i, state, mask); -} -void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) -{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); -} -COLORREF CCtrlListView::SetOutlineColor(COLORREF color) -{ return ListView_SetOutlineColor(m_hwnd, color); -} -void CCtrlListView::SetSelectedColumn(int iCol) -{ ListView_SetSelectedColumn(m_hwnd, iCol); -} -INT CCtrlListView::SetSelectionMark(INT iIndex) -{ return ListView_SetSelectionMark(m_hwnd, iIndex); -} -BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) -{ return ListView_SetTextBkColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTextColor(COLORREF clrText) -{ return ListView_SetTextColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) -{ return ListView_SetTileInfo(m_hwnd, plvtinfo); -} -BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) -{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::SetToolTips(HWND ToolTip) -{ return ListView_SetToolTips(m_hwnd, ToolTip); -} -BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) -{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); -} -int CCtrlListView::SetView(uint32_t iView) -{ return ListView_SetView(m_hwnd, iView); -} -void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) -{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) -{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); -} -BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); -} -BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); -} -INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const -{ return ListView_SubItemHitTest(m_hwnd, pInfo); -} -BOOL CCtrlListView::Update(int iItem) -{ return ListView_Update(m_hwnd, iItem); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlListView + +CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId) +{} + +BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) +{ + TEventInfo evt = { this, pnmh }; + + switch (pnmh->code) { + case NM_CLICK: OnClick(&evt); return TRUE; + case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; + case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; + case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; + case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; + case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; + case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; + case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; + case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; + case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; + case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; + case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; + case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; + case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; + case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; + case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; + case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; + case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; + case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; + case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; + case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; + + case LVN_ITEMCHANGED: + if (!m_parentWnd || !m_parentWnd->IsInitialized()) + return FALSE; + + OnItemChanged(&evt); + + // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes + if (evt.nmlv->uChanged & LVIF_STATE) + if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) + NotifyChange(); + return TRUE; + + case LVN_ODSTATECHANGED: + NotifyChange(); + return TRUE; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) +{ + int result = l1 - l2; + int newItem = HIWORD(param); + int oldItem = LOWORD(param); + if (newItem > oldItem) + return (l1 == oldItem && l2 <= newItem) ? 1 : result; + + return (l2 == oldItem && l1 >= newItem) ? 1 : result; +} + +int CCtrlListView::MoveItem(int idx, int direction) +{ + if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) + return idx; + + if (idx < 0) + idx = GetNextItem(-1, LVNI_FOCUSED); + SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); + return idx + direction; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CCtrlListView::SetCurSel(int idx) +{ + SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); +} + +// additional api +HIMAGELIST CCtrlListView::CreateImageList(int iImageList) +{ + HIMAGELIST hIml = GetImageList(iImageList); + if (hIml) + return hIml; + + hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); + SetImageList(hIml, iImageList); + return hIml; +} + +void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) +{ + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc.iImage = 0; + lvc.pszText = (LPWSTR)name; + lvc.cx = cx; + lvc.iSubItem = iSubItem; + InsertColumn(iSubItem, &lvc); +} + +void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) +{ + LVGROUP lvg = { 0 }; + lvg.cbSize = sizeof(lvg); + lvg.mask = LVGF_HEADER | LVGF_GROUPID; + lvg.pszHeader = (LPWSTR)name; + lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); + lvg.iGroupId = iGroupId; + InsertGroup(-1, &lvg); +} + +int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) +{ + LVITEM lvi = { 0 }; + lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; + lvi.iSubItem = 0; + lvi.pszText = (LPWSTR)text; + lvi.iImage = iIcon; + lvi.lParam = lParam; + if (iGroupId >= 0) { + lvi.mask |= LVIF_GROUPID; + lvi.iGroupId = iGroupId; + } + + return InsertItem(&lvi); +} + +void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) +{ + LVITEM lvi = { 0 }; + lvi.mask = LVIF_TEXT; + lvi.iItem = iItem; + lvi.iSubItem = iSubItem; + lvi.pszText = (LPWSTR)text; + if (iIcon >= 0) { + lvi.mask |= LVIF_IMAGE; + lvi.iImage = iIcon; + } + + SetItem(&lvi); +} + +LPARAM CCtrlListView::GetItemData(int iItem) const +{ + LVITEM lvi = { 0 }; + lvi.mask = LVIF_PARAM; + lvi.iItem = iItem; + return GetItem(&lvi) ? lvi.lParam : -1; +} + +void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + + // position is empty, let's fill it using selection + if (pos.pt.x == 0 && pos.pt.y == 0) { + pos.iCurr = GetSelectionMark(); + if (pos.iCurr != -1) { + RECT rc; + GetItemRect(pos.iCurr, &rc, TRUE); + pos.pt.x = rc.left + 8; + pos.pt.y = rc.top + 8; + ClientToScreen(m_hwnd, &pos.pt); + return; + } + } + // position is present, let's calculate current item + else { + LVHITTESTINFO hti; + hti.pt = pos.pt; + ScreenToClient(m_hwnd, &hti.pt); + if (SubItemHitTest(&hti) != -1) { + pos.iCurr = hti.iItem; + return; + } + } + CSuper::GetCaretPos(pos); +} + +// classic api +uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) +{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); +} +void CCtrlListView::Arrange(UINT code) +{ ListView_Arrange(m_hwnd, code); +} +void CCtrlListView::CancelEditLabel() +{ ListView_CancelEditLabel(m_hwnd); +} +HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) +{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); +} +void CCtrlListView::DeleteAllItems() +{ ListView_DeleteAllItems(m_hwnd); +} +void CCtrlListView::DeleteColumn(int iCol) +{ ListView_DeleteColumn(m_hwnd, iCol); +} +void CCtrlListView::DeleteItem(int iItem) +{ ListView_DeleteItem(m_hwnd, iItem); +} +HWND CCtrlListView::EditLabel(int iItem) +{ return ListView_EditLabel(m_hwnd, iItem); +} +int CCtrlListView::EnableGroupView(BOOL fEnable) +{ return ListView_EnableGroupView(m_hwnd, fEnable); +} +BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) +{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); +} +int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) +{ return ListView_FindItem(m_hwnd, iStart, plvfi); +} +COLORREF CCtrlListView::GetBkColor() const +{ return ListView_GetBkColor(m_hwnd); +} +void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const +{ ListView_GetBkImage(m_hwnd, plvbki); +} +UINT CCtrlListView::GetCallbackMask() const +{ return ListView_GetCallbackMask(m_hwnd); +} +BOOL CCtrlListView::GetCheckState(UINT iIndex) const +{ return ListView_GetCheckState(m_hwnd, iIndex); +} +void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const +{ ListView_GetColumn(m_hwnd, iCol, pcol); +} +void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const +{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); +} +int CCtrlListView::GetColumnWidth(int iCol) const +{ return ListView_GetColumnWidth(m_hwnd, iCol); +} +int CCtrlListView::GetCountPerPage() const +{ return ListView_GetCountPerPage(m_hwnd); +} +HWND CCtrlListView::GetEditControl() const +{ return ListView_GetEditControl(m_hwnd); +} +uint32_t CCtrlListView::GetExtendedListViewStyle() const +{ return ListView_GetExtendedListViewStyle(m_hwnd); +} +void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const +{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); +} +HWND CCtrlListView::GetHeader() const +{ return ListView_GetHeader(m_hwnd); +} +HCURSOR CCtrlListView::GetHotCursor() const +{ return ListView_GetHotCursor(m_hwnd); +} +INT CCtrlListView::GetHotItem() const +{ return ListView_GetHotItem(m_hwnd); +} +uint32_t CCtrlListView::GetHoverTime() const +{ return ListView_GetHoverTime(m_hwnd); +} +HIMAGELIST CCtrlListView::GetImageList(int iImageList) const +{ return ListView_GetImageList(m_hwnd, iImageList); +} +BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const +{ return ListView_GetInsertMark(m_hwnd, plvim); +} +COLORREF CCtrlListView::GetInsertMarkColor() const +{ return ListView_GetInsertMarkColor(m_hwnd); +} +int CCtrlListView::GetInsertMarkRect(LPRECT prc) const +{ return ListView_GetInsertMarkRect(m_hwnd, prc); +} +BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const +{ return ListView_GetISearchString(m_hwnd, lpsz); +} +bool CCtrlListView::GetItem(LPLVITEM pitem) const +{ return ListView_GetItem(m_hwnd, pitem) == TRUE; +} +int CCtrlListView::GetItemCount() const +{ return ListView_GetItemCount(m_hwnd); +} +void CCtrlListView::GetItemPosition(int i, POINT *ppt) const +{ ListView_GetItemPosition(m_hwnd, i, ppt); +} +void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const +{ ListView_GetItemRect(m_hwnd, i, prc, code); +} +uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const +{ return ListView_GetItemSpacing(m_hwnd, fSmall); +} +UINT CCtrlListView::GetItemState(int i, UINT mask) const +{ return ListView_GetItemState(m_hwnd, i, mask); +} +void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const +{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); +} +int CCtrlListView::GetNextItem(int iStart, UINT flags) const +{ return ListView_GetNextItem(m_hwnd, iStart, flags); +} +BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const +{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); +} +BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const +{ return ListView_GetOrigin(m_hwnd, lpptOrg); +} +COLORREF CCtrlListView::GetOutlineColor() const +{ return ListView_GetOutlineColor(m_hwnd); +} +UINT CCtrlListView::GetSelectedColumn() const +{ return ListView_GetSelectedColumn(m_hwnd); +} +UINT CCtrlListView::GetSelectedCount() const +{ return ListView_GetSelectedCount(m_hwnd); +} +INT CCtrlListView::GetSelectionMark() const +{ return ListView_GetSelectionMark(m_hwnd); +} +int CCtrlListView::GetStringWidth(LPCSTR psz) const +{ return ListView_GetStringWidth(m_hwnd, psz); +} +BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const +{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); +} +COLORREF CCtrlListView::GetTextBkColor() const +{ return ListView_GetTextBkColor(m_hwnd); +} +COLORREF CCtrlListView::GetTextColor() const +{ return ListView_GetTextColor(m_hwnd); +} +void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const +{ ListView_GetTileInfo(m_hwnd, plvtinfo); +} +void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const +{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); +} +HWND CCtrlListView::GetToolTips() const +{ return ListView_GetToolTips(m_hwnd); +} +int CCtrlListView::GetTopIndex() const +{ return ListView_GetTopIndex(m_hwnd); +} +BOOL CCtrlListView::GetUnicodeFormat() const +{ return ListView_GetUnicodeFormat(m_hwnd); +} +uint32_t CCtrlListView::GetView() const +{ return ListView_GetView(m_hwnd); +} +BOOL CCtrlListView::GetViewRect(RECT *prc) const +{ return ListView_GetViewRect(m_hwnd, prc); +} +void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const +{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); +} +BOOL CCtrlListView::HasGroup(int dwGroupId) +{ return ListView_HasGroup(m_hwnd, dwGroupId); +} +int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const +{ return ListView_HitTest(m_hwnd, pinfo); +} +int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) +{ return ListView_InsertColumn(m_hwnd, iCol, pcol); +} +int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) +{ return ListView_InsertGroup(m_hwnd, index, pgrp); +} +void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) +{ ListView_InsertGroupSorted(m_hwnd, structInsert); +} +int CCtrlListView::InsertItem(const LVITEM *pitem) +{ return ListView_InsertItem(m_hwnd, pitem); +} +BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) +{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); +} +BOOL CCtrlListView::IsGroupViewEnabled() +{ return ListView_IsGroupViewEnabled(m_hwnd); +} +UINT CCtrlListView::MapIDToIndex(UINT id) +{ return ListView_MapIDToIndex(m_hwnd, id); +} +UINT CCtrlListView::MapIndexToID(UINT index) +{ return ListView_MapIndexToID(m_hwnd, index); +} +BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) +{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); +} +void CCtrlListView::RemoveAllGroups() +{ ListView_RemoveAllGroups(m_hwnd); +} +int CCtrlListView::RemoveGroup(int iGroupId) +{ return ListView_RemoveGroup(m_hwnd, iGroupId); +} +BOOL CCtrlListView::Scroll(int dx, int dy) +{ return ListView_Scroll(m_hwnd, dx, dy); +} +BOOL CCtrlListView::SetBkColor(COLORREF clrBk) +{ return ListView_SetBkColor(m_hwnd, clrBk); +} +BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) +{ return ListView_SetBkImage(m_hwnd, plvbki); +} +BOOL CCtrlListView::SetCallbackMask(UINT mask) +{ return ListView_SetCallbackMask(m_hwnd, mask); +} +void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) +{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); +} +BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) +{ return ListView_SetColumn(m_hwnd, iCol, pcol); +} +BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) +{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); +} +BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) +{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); +} +void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) +{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); +} +void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) +{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); +} +int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) +{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); +} +void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) +{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); +} +HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) +{ return ListView_SetHotCursor(m_hwnd, hCursor); +} +INT CCtrlListView::SetHotItem(INT iIndex) +{ return ListView_SetHotItem(m_hwnd, iIndex); +} +void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) +{ ListView_SetHoverTime(m_hwnd, dwHoverTime); +} +uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) +{ return ListView_SetIconSpacing(m_hwnd, cx, cy); +} +HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) +{ return ListView_SetImageList(m_hwnd, himl, iImageList); +} +BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) +{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); +} +BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) +{ return ListView_SetInsertMark(m_hwnd, plvim); +} +COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) +{ return ListView_SetInsertMarkColor(m_hwnd, color); +} +BOOL CCtrlListView::SetItem(const LVITEM *pitem) +{ return ListView_SetItem(m_hwnd, pitem); +} +void CCtrlListView::SetItemCount(int cItems) +{ ListView_SetItemCount(m_hwnd, cItems); +} +void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) +{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); +} +BOOL CCtrlListView::SetItemPosition(int i, int x, int y) +{ return ListView_SetItemPosition(m_hwnd, i, x, y); +} +void CCtrlListView::SetItemPosition32(int iItem, int x, int y) +{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); +} +void CCtrlListView::SetItemState(int i, UINT state, UINT mask) +{ ListView_SetItemState(m_hwnd, i, state, mask); +} +void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) +{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); +} +COLORREF CCtrlListView::SetOutlineColor(COLORREF color) +{ return ListView_SetOutlineColor(m_hwnd, color); +} +void CCtrlListView::SetSelectedColumn(int iCol) +{ ListView_SetSelectedColumn(m_hwnd, iCol); +} +INT CCtrlListView::SetSelectionMark(INT iIndex) +{ return ListView_SetSelectionMark(m_hwnd, iIndex); +} +BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) +{ return ListView_SetTextBkColor(m_hwnd, clrText); +} +BOOL CCtrlListView::SetTextColor(COLORREF clrText) +{ return ListView_SetTextColor(m_hwnd, clrText); +} +BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) +{ return ListView_SetTileInfo(m_hwnd, plvtinfo); +} +BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) +{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); +} +HWND CCtrlListView::SetToolTips(HWND ToolTip) +{ return ListView_SetToolTips(m_hwnd, ToolTip); +} +BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) +{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); +} +int CCtrlListView::SetView(uint32_t iView) +{ return ListView_SetView(m_hwnd, iView); +} +void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) +{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); +} +int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) +{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); +} +BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) +{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); +} +BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) +{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); +} +INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const +{ return ListView_SubItemHitTest(m_hwnd, pInfo); +} +BOOL CCtrlListView::Update(int iItem) +{ return ListView_Update(m_hwnd, iItem); +} diff --git a/src/mir_core/src/Linux/CCtrlMButton.cpp b/src/mir_core/src/Linux/CCtrlMButton.cpp index 8d9198e144..cc05a3a8e4 100644 --- a/src/mir_core/src/Linux/CCtrlMButton.cpp +++ b/src/mir_core/src/Linux/CCtrlMButton.cpp @@ -1,62 +1,62 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlMButton - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(hIcon), - m_toolTip(tooltip) -{} - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(::Skin_LoadIcon(iCoreIcon)), - m_toolTip(tooltip) -{} - -CCtrlMButton::~CCtrlMButton() -{ - ::IcoLib_ReleaseIcon(m_hIcon); -} - -void CCtrlMButton::OnInit() -{ - CCtrlButton::OnInit(); - - SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); - SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); - SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); -} - -void CCtrlMButton::MakeFlat() -{ - SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); -} - -void CCtrlMButton::MakePush() -{ - SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlMButton + +CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) + : CCtrlButton(dlg, ctrlId), + m_hIcon(hIcon), + m_toolTip(tooltip) +{} + +CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) + : CCtrlButton(dlg, ctrlId), + m_hIcon(::Skin_LoadIcon(iCoreIcon)), + m_toolTip(tooltip) +{} + +CCtrlMButton::~CCtrlMButton() +{ + ::IcoLib_ReleaseIcon(m_hIcon); +} + +void CCtrlMButton::OnInit() +{ + CCtrlButton::OnInit(); + + SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); + SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); + SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); +} + +void CCtrlMButton::MakeFlat() +{ + SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); +} + +void CCtrlMButton::MakePush() +{ + SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); +} diff --git a/src/mir_core/src/Linux/CCtrlPages.cpp b/src/mir_core/src/Linux/CCtrlPages.cpp index 512c32e142..c2a95553c5 100644 --- a/src/mir_core/src/Linux/CCtrlPages.cpp +++ b/src/mir_core/src/Linux/CCtrlPages.cpp @@ -1,411 +1,411 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static volatile long g_order = 1; - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlPages - -struct CCtrlPages::TPageInfo : public MZeroedObject -{ - TPageInfo() - { - m_iOrder = InterlockedIncrement(&g_order); - } - - ~TPageInfo() - { - if (m_hIcon) - DestroyIcon(m_hIcon); - } - - int m_iOrder; - ptrW m_ptszHeader; - HICON m_hIcon; - bool m_bChanged, m_bScheduledResize; - CDlgBase *m_pDlg; -}; - -CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId), - m_hIml(nullptr), - m_pActivePage(nullptr), - m_pages(4, NumericKeySortT) -{} - -void CCtrlPages::OnInit() -{ - CSuper::OnInit(); - Subclass(); - - for (auto &it : m_pages) - InsertPage(it); - m_pages.destroy(); - - ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); - - TPageInfo *info = GetCurrPage(); - if (info) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - int tabCount; - - switch (msg) { - case WM_SIZE: - if (TPageInfo *pCurrInfo = GetCurrPage()) { - tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p == nullptr) - continue; - if (p == pCurrInfo) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - else p->m_bScheduledResize = true; - } - } - break; - - case PSM_CHANGED: - if (TPageInfo *info = GetCurrPage()) - info->m_bChanged = TRUE; - return TRUE; - - case PSM_FORCECHANGED: - tabCount = GetCount(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p) { - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - if (pshn.hdr.hwndFrom != nullptr) - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) -{ - TPageInfo *info = new TPageInfo; - info->m_pDlg = pDlg; - info->m_hIcon = hIcon; - info->m_ptszHeader = mir_wstrdup(ptszName); - - if (m_hwnd != nullptr) { - InsertPage(info); - - if (GetCount() == 1) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - } - m_pages.insert(info); -} - -void CCtrlPages::ActivatePage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - if (info == nullptr || info->m_pDlg == nullptr) - return; - - if (m_pActivePage != nullptr) - ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); - - m_pActivePage = info->m_pDlg; - if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - - TabCtrl_SetCurSel(m_hwnd, iPage); - ShowPage(m_pActivePage); - ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); -} - -void CCtrlPages::CheckRowCount() -{ - int iRowCount = TabCtrl_GetRowCount(m_hwnd); - if (m_numRows != iRowCount) { - m_numRows = iRowCount; - for (auto &p : m_pages) - p->m_bScheduledResize = true; - } -} - -int CCtrlPages::GetCount() -{ - return TabCtrl_GetItemCount(m_hwnd); -} - -CDlgBase* CCtrlPages::GetNthPage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - return (info == nullptr) ? nullptr : info->m_pDlg; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) -{ - int tabCount = TabCtrl_GetItemCount(m_hwnd); - for (int i = 0; i < tabCount; i++) { - TCITEM tci; - tci.mask = TCIF_PARAM | TCIF_IMAGE; - TabCtrl_GetItem(m_hwnd, i, &tci); - TPageInfo *pPage = (TPageInfo *)tci.lParam; - if (pPage == nullptr) - continue; - - if (pPage->m_pDlg == pDlg) - return i; - } - - return -1; -} - -void CCtrlPages::InsertPage(TPageInfo *pPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)pPage; - tci.pszText = TranslateW_LP(pPage->m_ptszHeader); - if (pPage->m_hIcon) { - if (!m_hIml) { - m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - TabCtrl_SetImageList(m_hwnd, m_hIml); - } - - tci.mask |= TCIF_IMAGE; - tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); - } - - TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); - - CheckRowCount(); -} - -void CCtrlPages::RemovePage(int iPage) -{ - TPageInfo *p = GetItemPage(iPage); - if (p == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, iPage); - m_pages.remove(p); - delete p; - - CheckRowCount(); -} - -void CCtrlPages::ShowPage(CDlgBase *pDlg) -{ - if (pDlg->GetHwnd() == nullptr) { - pDlg->SetParent(m_hwnd); - pDlg->Create(); - - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); - - EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(pDlg->GetHwnd(), SW_SHOW); -} - -void CCtrlPages::SwapPages(int idx1, int idx2) -{ - TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); - if (p1 == nullptr || p2 == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, idx1); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)p1; - tci.pszText = TranslateW_LP(p1->m_ptszHeader); - TabCtrl_InsertItem(m_hwnd, idx2, &tci); -} - -BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) -{ - TPageInfo *info; - PSHNOTIFY pshn; - - switch (pnmh->code) { - case TCN_SELCHANGING: - if (info = GetCurrPage()) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - return TRUE; - - case TCN_SELCHANGE: - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - - if (info = GetCurrPage()) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - else m_pActivePage = nullptr; - return TRUE; - } - - return FALSE; -} - -void CCtrlPages::OnReset() -{ - CSuper::OnReset(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -bool CCtrlPages::OnApply() -{ - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - if (m_pActivePage != nullptr) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) - return false; - } - - pshn.hdr.code = PSN_APPLY; - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(m_hwnd, i); - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - m_pActivePage = p->m_pDlg; - m_pActivePage->Show(); - return false; - } - } - - CSuper::OnApply(); - return true; -} - -void CCtrlPages::OnDestroy() -{ - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; - if (pDlg->GetHwnd()) - pDlg->Close(); - delete p; - } - - TabCtrl_DeleteAllItems(m_hwnd); - - if (m_hIml) { - TabCtrl_SetImageList(m_hwnd, nullptr); - ImageList_Destroy(m_hIml); - } - - CSuper::OnDestroy(); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +static volatile long g_order = 1; + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlPages + +struct CCtrlPages::TPageInfo : public MZeroedObject +{ + TPageInfo() + { + m_iOrder = InterlockedIncrement(&g_order); + } + + ~TPageInfo() + { + if (m_hIcon) + DestroyIcon(m_hIcon); + } + + int m_iOrder; + ptrW m_ptszHeader; + HICON m_hIcon; + bool m_bChanged, m_bScheduledResize; + CDlgBase *m_pDlg; +}; + +CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId), + m_hIml(nullptr), + m_pActivePage(nullptr), + m_pages(4, NumericKeySortT) +{} + +void CCtrlPages::OnInit() +{ + CSuper::OnInit(); + Subclass(); + + for (auto &it : m_pages) + InsertPage(it); + m_pages.destroy(); + + ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); + + TPageInfo *info = GetCurrPage(); + if (info) { + m_pActivePage = info->m_pDlg; + ShowPage(m_pActivePage); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } +} + +LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + int tabCount; + + switch (msg) { + case WM_SIZE: + if (TPageInfo *pCurrInfo = GetCurrPage()) { + tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p == nullptr) + continue; + if (p == pCurrInfo) { + RECT rc; + GetClientRect(m_hwnd, &rc); + TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); + SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); + } + else p->m_bScheduledResize = true; + } + } + break; + + case PSM_CHANGED: + if (TPageInfo *info = GetCurrPage()) + info->m_bChanged = TRUE; + return TRUE; + + case PSM_FORCECHANGED: + tabCount = GetCount(); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p) { + pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); + if (pshn.hdr.hwndFrom != nullptr) + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } + } + break; + } + + return CSuper::CustomWndProc(msg, wParam, lParam); +} + +void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) +{ + TPageInfo *info = new TPageInfo; + info->m_pDlg = pDlg; + info->m_hIcon = hIcon; + info->m_ptszHeader = mir_wstrdup(ptszName); + + if (m_hwnd != nullptr) { + InsertPage(info); + + if (GetCount() == 1) { + m_pActivePage = info->m_pDlg; + ShowPage(m_pActivePage); + } + } + m_pages.insert(info); +} + +void CCtrlPages::ActivatePage(int iPage) +{ + TPageInfo *info = GetItemPage(iPage); + if (info == nullptr || info->m_pDlg == nullptr) + return; + + if (m_pActivePage != nullptr) + ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); + + m_pActivePage = info->m_pDlg; + if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { + RECT rc; + GetClientRect(m_hwnd, &rc); + TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); + SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); + } + + TabCtrl_SetCurSel(m_hwnd, iPage); + ShowPage(m_pActivePage); + ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); +} + +void CCtrlPages::CheckRowCount() +{ + int iRowCount = TabCtrl_GetRowCount(m_hwnd); + if (m_numRows != iRowCount) { + m_numRows = iRowCount; + for (auto &p : m_pages) + p->m_bScheduledResize = true; + } +} + +int CCtrlPages::GetCount() +{ + return TabCtrl_GetItemCount(m_hwnd); +} + +CDlgBase* CCtrlPages::GetNthPage(int iPage) +{ + TPageInfo *info = GetItemPage(iPage); + return (info == nullptr) ? nullptr : info->m_pDlg; +} + +CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() +{ + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM; + if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) + return nullptr; + + return (TPageInfo*)tci.lParam; +} + +CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) +{ + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM; + if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) + return nullptr; + + return (TPageInfo*)tci.lParam; +} + +int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) +{ + int tabCount = TabCtrl_GetItemCount(m_hwnd); + for (int i = 0; i < tabCount; i++) { + TCITEM tci; + tci.mask = TCIF_PARAM | TCIF_IMAGE; + TabCtrl_GetItem(m_hwnd, i, &tci); + TPageInfo *pPage = (TPageInfo *)tci.lParam; + if (pPage == nullptr) + continue; + + if (pPage->m_pDlg == pDlg) + return i; + } + + return -1; +} + +void CCtrlPages::InsertPage(TPageInfo *pPage) +{ + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM | TCIF_TEXT; + tci.lParam = (LPARAM)pPage; + tci.pszText = TranslateW_LP(pPage->m_ptszHeader); + if (pPage->m_hIcon) { + if (!m_hIml) { + m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); + TabCtrl_SetImageList(m_hwnd, m_hIml); + } + + tci.mask |= TCIF_IMAGE; + tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); + } + + TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); + + CheckRowCount(); +} + +void CCtrlPages::RemovePage(int iPage) +{ + TPageInfo *p = GetItemPage(iPage); + if (p == nullptr) + return; + + TabCtrl_DeleteItem(m_hwnd, iPage); + m_pages.remove(p); + delete p; + + CheckRowCount(); +} + +void CCtrlPages::ShowPage(CDlgBase *pDlg) +{ + if (pDlg->GetHwnd() == nullptr) { + pDlg->SetParent(m_hwnd); + pDlg->Create(); + + RECT rc; + GetClientRect(m_hwnd, &rc); + TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); + SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); + + EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.hwndFrom = pDlg->GetHwnd(); + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } + ShowWindow(pDlg->GetHwnd(), SW_SHOW); +} + +void CCtrlPages::SwapPages(int idx1, int idx2) +{ + TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); + if (p1 == nullptr || p2 == nullptr) + return; + + TabCtrl_DeleteItem(m_hwnd, idx1); + + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM | TCIF_TEXT; + tci.lParam = (LPARAM)p1; + tci.pszText = TranslateW_LP(p1->m_ptszHeader); + TabCtrl_InsertItem(m_hwnd, idx2, &tci); +} + +BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) +{ + TPageInfo *info; + PSHNOTIFY pshn; + + switch (pnmh->code) { + case TCN_SELCHANGING: + if (info = GetCurrPage()) { + pshn.hdr.code = PSN_KILLACTIVE; + pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { + SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + return TRUE; + + case TCN_SELCHANGE: + if (m_pActivePage != nullptr) + m_pActivePage->Hide(); + + if (info = GetCurrPage()) { + m_pActivePage = info->m_pDlg; + ShowPage(m_pActivePage); + } + else m_pActivePage = nullptr; + return TRUE; + } + + return FALSE; +} + +void CCtrlPages::OnReset() +{ + CSuper::OnReset(); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + + int tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) + continue; + + pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } +} + +bool CCtrlPages::OnApply() +{ + PSHNOTIFY pshn; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + + if (m_pActivePage != nullptr) { + pshn.hdr.code = PSN_KILLACTIVE; + pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); + if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) + return false; + } + + pshn.hdr.code = PSN_APPLY; + int tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) + continue; + + pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { + TabCtrl_SetCurSel(m_hwnd, i); + if (m_pActivePage != nullptr) + m_pActivePage->Hide(); + m_pActivePage = p->m_pDlg; + m_pActivePage->Show(); + return false; + } + } + + CSuper::OnApply(); + return true; +} + +void CCtrlPages::OnDestroy() +{ + int tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; + if (pDlg->GetHwnd()) + pDlg->Close(); + delete p; + } + + TabCtrl_DeleteAllItems(m_hwnd); + + if (m_hIml) { + TabCtrl_SetImageList(m_hwnd, nullptr); + ImageList_Destroy(m_hIml); + } + + CSuper::OnDestroy(); +} diff --git a/src/mir_core/src/Linux/CCtrlSlider.cpp b/src/mir_core/src/Linux/CCtrlSlider.cpp index 69aeb24796..9938736e42 100644 --- a/src/mir_core/src/Linux/CCtrlSlider.cpp +++ b/src/mir_core/src/Linux/CCtrlSlider.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSlider class - -CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax) -{ - m_bNotifiable = true; -} - -BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == WM_HSCROLL) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSlider::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetPosition()); - return true; -} - -void CCtrlSlider::OnReset() -{ - SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int CCtrlSlider::GetPosition() const -{ - return SendMsg(TBM_GETPOS, 0, 0); -} - -void CCtrlSlider::SetPosition(int wPos) -{ - SendMsg(TBM_SETPOS, TRUE, wPos); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlSlider class + +CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : + CCtrlData(dlg, ctrlId), + m_wMin(wMin), + m_wMax(wMax) +{ + m_bNotifiable = true; +} + +BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + if (idCode == WM_HSCROLL) { + NotifyChange(); + return TRUE; + } + return FALSE; +} + +bool CCtrlSlider::OnApply() +{ + CSuper::OnApply(); + + if (m_dbLink != nullptr) + SaveInt(GetPosition()); + return true; +} + +void CCtrlSlider::OnReset() +{ + SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); + + if (m_dbLink != nullptr) + SetPosition(LoadInt()); +} + +int CCtrlSlider::GetPosition() const +{ + return SendMsg(TBM_GETPOS, 0, 0); +} + +void CCtrlSlider::SetPosition(int wPos) +{ + SendMsg(TBM_SETPOS, TRUE, wPos); +} diff --git a/src/mir_core/src/Linux/CCtrlSpin.cpp b/src/mir_core/src/Linux/CCtrlSpin.cpp index 54d43e933a..54dc5ffd07 100644 --- a/src/mir_core/src/Linux/CCtrlSpin.cpp +++ b/src/mir_core/src/Linux/CCtrlSpin.cpp @@ -1,81 +1,81 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSpin class - -CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax), - m_wCurr(0) -{} - -BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - auto *pEvent = (NMUPDOWN *)pnmh; - m_wCurr = pEvent->iPos + pEvent->iDelta; - - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSpin::OnApply() -{ - CSuper::OnApply(); - - m_wCurr = SendMsg(UDM_GETPOS, 0, 0); - if (m_dbLink != nullptr) - SaveInt(m_wCurr); - - HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); - if (hwndBuddy) { - wchar_t buf[100]; - _itow(m_wCurr, buf, 10); - ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); - } - - return true; -} - -void CCtrlSpin::OnReset() -{ - SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int16_t CCtrlSpin::GetPosition() -{ - return m_wCurr; -} - -void CCtrlSpin::SetPosition(int16_t wPos) -{ - SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlSpin class + +CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : + CCtrlData(dlg, ctrlId), + m_wMin(wMin), + m_wMax(wMax), + m_wCurr(0) +{} + +BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) +{ + if (pnmh->code == UDN_DELTAPOS) { + auto *pEvent = (NMUPDOWN *)pnmh; + m_wCurr = pEvent->iPos + pEvent->iDelta; + + NotifyChange(); + return TRUE; + } + return FALSE; +} + +bool CCtrlSpin::OnApply() +{ + CSuper::OnApply(); + + m_wCurr = SendMsg(UDM_GETPOS, 0, 0); + if (m_dbLink != nullptr) + SaveInt(m_wCurr); + + HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); + if (hwndBuddy) { + wchar_t buf[100]; + _itow(m_wCurr, buf, 10); + ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); + } + + return true; +} + +void CCtrlSpin::OnReset() +{ + SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); + + if (m_dbLink != nullptr) + SetPosition(LoadInt()); +} + +int16_t CCtrlSpin::GetPosition() +{ + return m_wCurr; +} + +void CCtrlSpin::SetPosition(int16_t wPos) +{ + SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); +} diff --git a/src/mir_core/src/Linux/CCtrlTreeOpts.cpp b/src/mir_core/src/Linux/CCtrlTreeOpts.cpp index 13a6b79bd3..f0b1be1fb9 100644 --- a/src/mir_core/src/Linux/CCtrlTreeOpts.cpp +++ b/src/mir_core/src/Linux/CCtrlTreeOpts.cpp @@ -1,216 +1,216 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_GRPOPEN, IMG_GRPCLOSED }; - -CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId): - CCtrlTreeView(dlg, ctrlId), - m_options(5) -{ -} - -CCtrlTreeOpts::~CCtrlTreeOpts() -{ -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, CMOption &option) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::CMOPTION); - p->m_option = &option; - m_options.insert(p, m_options.getCount()); -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, bool &option) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::BOOL); - p->m_pBool = &option; - m_options.insert(p, m_options.getCount()); -} - -void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, uint32_t &option, uint32_t mask) -{ - auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::MASK); - p->m_pDword = &option; - p->m_mask = mask; - m_options.insert(p, m_options.getCount()); -} - -BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh) -{ - switch (pnmh->code) { - case TVN_KEYDOWN: - { - LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh; - HTREEITEM hti; - if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection())) - ProcessItemClick(hti); - } - break; - - case NM_CLICK: - TVHITTESTINFO htti; - htti.pt.x = (short)LOWORD(GetMessagePos()); - htti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pnmh->hwndFrom, &htti.pt); - if (HitTest(&htti)) - if (htti.flags & TVHT_ONITEMICON) - ProcessItemClick(htti.hItem); - break; - - case TVN_ITEMEXPANDED: - LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW)pnmh; - TVITEM tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvi.hItem = lpnmtv->itemNew.hItem; - tvi.iImage = tvi.iSelectedImage = (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED; - SendMessage(pnmh->hwndFrom, TVM_SETITEM, 0, (LPARAM)&tvi); - break; - } - - return CSuper::OnNotify(idCtrl, pnmh); -} - -void CCtrlTreeOpts::OnInit() -{ - CSuper::OnInit(); - - SelectItem(nullptr); - DeleteAllItems(); - - HIMAGELIST hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_MIRANDA); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_TICK); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_NOTICK); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPOPEN); - ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPSHUT); - SetImageList(hImgLst, TVSIL_NORMAL); - - /* build options tree. based on code from IcoLib */ - for (auto &it : m_options) { - if (it->m_pwszSection) { - HTREEITEM hSection = FindNamedItem(nullptr, it->m_pwszSection); - if (!hSection) { - TVINSERTSTRUCT tvis = {}; - tvis.hParent = hSection; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = (LPWSTR)it->m_pwszSection; - tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD; - tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN; - hSection = InsertItem(&tvis); - } - - bool bValue; - switch (it->m_type) { - case COptionsItem::CMOPTION: - bValue = *it->m_option; - break; - case COptionsItem::BOOL: - bValue = *it->m_pBool; - break; - case COptionsItem::MASK: - bValue = (*it->m_pDword & it->m_mask) != 0; - break; - default: - continue; - } - - TVINSERTSTRUCT tvis = {}; - tvis.hParent = hSection; - tvis.hInsertAfter = TVI_LAST; - tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; - tvis.item.pszText = (LPWSTR)it->m_pwszName; - tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED; - tvis.item.lParam = m_options.indexOf(&it); - tvis.item.iImage = tvis.item.iSelectedImage = (bValue) ? IMG_CHECK : IMG_NOCHECK; - - it->m_hItem = InsertItem(&tvis); - } - } - - TranslateTree(); - ShowWindow(m_hwnd, SW_SHOW); - SelectItem(FindNamedItem(nullptr, nullptr)); -} - -void CCtrlTreeOpts::OnDestroy() -{ - ImageList_Destroy(GetImageList(TVSIL_NORMAL)); -} - -bool CCtrlTreeOpts::OnApply() -{ - CSuper::OnApply(); - - for (auto &it : m_options) { - TVITEMEX tvi; - GetItem(it->m_hItem, &tvi); - - bool bValue = (tvi.iImage == IMG_CHECK); - switch (it->m_type) { - case COptionsItem::CMOPTION: - *it->m_option = bValue; - break; - case COptionsItem::BOOL: - *it->m_pBool = bValue; - break; - case COptionsItem::MASK: - if (bValue) - *it->m_pDword |= it->m_mask; - else - *it->m_pDword &= ~it->m_mask; - break; - } - } - return true; -} - -void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti) -{ - TVITEMEX tvi; - GetItem(hti, &tvi); - switch (tvi.iImage) { - case IMG_GRPOPEN: - tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED; - Expand(tvi.hItem, TVE_COLLAPSE); - break; - - case IMG_GRPCLOSED: - tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN; - Expand(tvi.hItem, TVE_EXPAND); - break; - - case IMG_CHECK: - tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK; - NotifyChange(); - break; - - case IMG_NOCHECK: - tvi.iImage = tvi.iSelectedImage = IMG_CHECK; - NotifyChange(); - break; - } - - SetItem(&tvi); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +enum { IMG_GROUP, IMG_CHECK, IMG_NOCHECK, IMG_GRPOPEN, IMG_GRPCLOSED }; + +CCtrlTreeOpts::CCtrlTreeOpts(CDlgBase* dlg, int ctrlId): + CCtrlTreeView(dlg, ctrlId), + m_options(5) +{ +} + +CCtrlTreeOpts::~CCtrlTreeOpts() +{ +} + +void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, CMOption &option) +{ + auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::CMOPTION); + p->m_option = &option; + m_options.insert(p, m_options.getCount()); +} + +void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, bool &option) +{ + auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::BOOL); + p->m_pBool = &option; + m_options.insert(p, m_options.getCount()); +} + +void CCtrlTreeOpts::AddOption(const wchar_t *pwszSection, const wchar_t *pwszName, uint32_t &option, uint32_t mask) +{ + auto *p = new COptionsItem(pwszSection, pwszName, COptionsItem::MASK); + p->m_pDword = &option; + p->m_mask = mask; + m_options.insert(p, m_options.getCount()); +} + +BOOL CCtrlTreeOpts::OnNotify(int idCtrl, NMHDR *pnmh) +{ + switch (pnmh->code) { + case TVN_KEYDOWN: + { + LPNMTVKEYDOWN lpnmtvkd = (LPNMTVKEYDOWN)pnmh; + HTREEITEM hti; + if ((lpnmtvkd->wVKey == VK_SPACE) && (hti = GetSelection())) + ProcessItemClick(hti); + } + break; + + case NM_CLICK: + TVHITTESTINFO htti; + htti.pt.x = (short)LOWORD(GetMessagePos()); + htti.pt.y = (short)HIWORD(GetMessagePos()); + ScreenToClient(pnmh->hwndFrom, &htti.pt); + if (HitTest(&htti)) + if (htti.flags & TVHT_ONITEMICON) + ProcessItemClick(htti.hItem); + break; + + case TVN_ITEMEXPANDED: + LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW)pnmh; + TVITEM tvi; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvi.hItem = lpnmtv->itemNew.hItem; + tvi.iImage = tvi.iSelectedImage = (lpnmtv->itemNew.state & TVIS_EXPANDED) ? IMG_GRPOPEN : IMG_GRPCLOSED; + SendMessage(pnmh->hwndFrom, TVM_SETITEM, 0, (LPARAM)&tvi); + break; + } + + return CSuper::OnNotify(idCtrl, pnmh); +} + +void CCtrlTreeOpts::OnInit() +{ + CSuper::OnInit(); + + SelectItem(nullptr); + DeleteAllItems(); + + HIMAGELIST hImgLst = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR | ILC_COLOR32 | ILC_MASK, 5, 1); + ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_MIRANDA); + ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_TICK); + ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_NOTICK); + ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPOPEN); + ImageList_AddSkinIcon(hImgLst, SKINICON_OTHER_GROUPSHUT); + SetImageList(hImgLst, TVSIL_NORMAL); + + /* build options tree. based on code from IcoLib */ + for (auto &it : m_options) { + if (it->m_pwszSection) { + HTREEITEM hSection = FindNamedItem(nullptr, it->m_pwszSection); + if (!hSection) { + TVINSERTSTRUCT tvis = {}; + tvis.hParent = hSection; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvis.item.pszText = (LPWSTR)it->m_pwszSection; + tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD; + tvis.item.iImage = tvis.item.iSelectedImage = IMG_GRPOPEN; + hSection = InsertItem(&tvis); + } + + bool bValue; + switch (it->m_type) { + case COptionsItem::CMOPTION: + bValue = *it->m_option; + break; + case COptionsItem::BOOL: + bValue = *it->m_pBool; + break; + case COptionsItem::MASK: + bValue = (*it->m_pDword & it->m_mask) != 0; + break; + default: + continue; + } + + TVINSERTSTRUCT tvis = {}; + tvis.hParent = hSection; + tvis.hInsertAfter = TVI_LAST; + tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvis.item.pszText = (LPWSTR)it->m_pwszName; + tvis.item.state = tvis.item.stateMask = TVIS_EXPANDED; + tvis.item.lParam = m_options.indexOf(&it); + tvis.item.iImage = tvis.item.iSelectedImage = (bValue) ? IMG_CHECK : IMG_NOCHECK; + + it->m_hItem = InsertItem(&tvis); + } + } + + TranslateTree(); + ShowWindow(m_hwnd, SW_SHOW); + SelectItem(FindNamedItem(nullptr, nullptr)); +} + +void CCtrlTreeOpts::OnDestroy() +{ + ImageList_Destroy(GetImageList(TVSIL_NORMAL)); +} + +bool CCtrlTreeOpts::OnApply() +{ + CSuper::OnApply(); + + for (auto &it : m_options) { + TVITEMEX tvi; + GetItem(it->m_hItem, &tvi); + + bool bValue = (tvi.iImage == IMG_CHECK); + switch (it->m_type) { + case COptionsItem::CMOPTION: + *it->m_option = bValue; + break; + case COptionsItem::BOOL: + *it->m_pBool = bValue; + break; + case COptionsItem::MASK: + if (bValue) + *it->m_pDword |= it->m_mask; + else + *it->m_pDword &= ~it->m_mask; + break; + } + } + return true; +} + +void CCtrlTreeOpts::ProcessItemClick(HTREEITEM hti) +{ + TVITEMEX tvi; + GetItem(hti, &tvi); + switch (tvi.iImage) { + case IMG_GRPOPEN: + tvi.iImage = tvi.iSelectedImage = IMG_GRPCLOSED; + Expand(tvi.hItem, TVE_COLLAPSE); + break; + + case IMG_GRPCLOSED: + tvi.iImage = tvi.iSelectedImage = IMG_GRPOPEN; + Expand(tvi.hItem, TVE_EXPAND); + break; + + case IMG_CHECK: + tvi.iImage = tvi.iSelectedImage = IMG_NOCHECK; + NotifyChange(); + break; + + case IMG_NOCHECK: + tvi.iImage = tvi.iSelectedImage = IMG_CHECK; + NotifyChange(); + break; + } + + SetItem(&tvi); +} diff --git a/src/mir_core/src/Linux/CCtrlTreeView.cpp b/src/mir_core/src/Linux/CCtrlTreeView.cpp index 390f1618fc..5d2174fc16 100644 --- a/src/mir_core/src/Linux/CCtrlTreeView.cpp +++ b/src/mir_core/src/Linux/CCtrlTreeView.cpp @@ -1,817 +1,817 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId) -{ - HICON hIcon = Skin_LoadIcon(iconId); - int res = ImageList_AddIcon(hIml, hIcon); - IcoLib_ReleaseIcon(hIcon); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlTreeView - -CCtrlTreeView::CCtrlTreeView(CDlgBase *dlg, int ctrlId) : - CCtrlBase(dlg, ctrlId), - m_dwFlags(0), - m_hDragItem(nullptr) -{} - -void CCtrlTreeView::SetFlags(uint32_t dwFlags) -{ - if (dwFlags & MTREE_CHECKBOX) - m_bCheckBox = true; - - if (dwFlags & MTREE_MULTISELECT) - m_bMultiSelect = true; - - if (dwFlags & MTREE_DND) { - m_bDndEnabled = true; - m_bDragging = false; - m_hDragItem = nullptr; - } -} - -void CCtrlTreeView::OnInit() -{ - CSuper::OnInit(); - - Subclass(); - - if (m_bCheckBox) { - HIMAGELIST himlCheckBoxes = ::ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2); - ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK); - ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK); - SetImageList(himlCheckBoxes, TVSIL_NORMAL); - } -} - -void CCtrlTreeView::OnDestroy() -{ - if (m_bCheckBox) - ::ImageList_Destroy(GetImageList(TVSIL_NORMAL)); - - CSuper::OnDestroy(); -} - -HTREEITEM CCtrlTreeView::MoveItemAbove(HTREEITEM hItem, HTREEITEM hInsertAfter, HTREEITEM hParent) -{ - if (hItem == nullptr || hInsertAfter == nullptr) - return nullptr; - - if (hItem == hInsertAfter) - return hItem; - - wchar_t name[128]; - TVINSERTSTRUCT tvis = {}; - tvis.itemex.mask = (UINT)-1; - tvis.itemex.pszText = name; - tvis.itemex.cchTextMax = _countof(name); - tvis.itemex.hItem = hItem; - if (!GetItem(&tvis.itemex)) - return nullptr; - - OBJLIST arChildren(1); - for (HTREEITEM p = GetChild(hItem); p; p = GetNextSibling(p)) { - wchar_t buf[128]; - TVINSERTSTRUCT tvis2 = {}; - tvis2.itemex.mask = (UINT)-1; - tvis2.itemex.pszText = buf; - tvis2.itemex.cchTextMax = _countof(buf); - tvis2.itemex.hItem = p; - if (GetItem(&tvis2.itemex)) { - tvis2.itemex.pszText = mir_wstrdup(tvis2.itemex.pszText); - arChildren.insert(new TVINSERTSTRUCT(tvis2)); - - tvis2.itemex.lParam = 0; - SetItem(&tvis2.itemex); - } - } - - // the pointed lParam will be freed inside TVN_DELETEITEM - // so lets substitute it with 0 - LPARAM saveOldData = tvis.itemex.lParam; - tvis.itemex.lParam = 0; - SetItem(&tvis.itemex); - - // now current item contain lParam = 0 we can delete it. the memory will be kept. - DeleteItem(hItem); - - for (auto &it : arChildren) - DeleteItem(it->itemex.hItem); - - tvis.itemex.stateMask = tvis.itemex.state; - tvis.itemex.lParam = saveOldData; - tvis.hParent = hParent; - tvis.hInsertAfter = hInsertAfter; - auto hNewItem = InsertItem(&tvis); - - hInsertAfter = nullptr; - for (auto &it : arChildren) { - it->hParent = hNewItem; - it->hInsertAfter = hInsertAfter; - hInsertAfter = InsertItem(it); - - mir_free(it->itemex.pszText); - } - - return hNewItem; -} - -LRESULT CCtrlTreeView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - TVHITTESTINFO hti; - - switch (msg) { - case WM_MOUSEMOVE: - if (m_bDragging) { - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - HitTest(&hti); - if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) { - HTREEITEM it = hti.hItem; - hti.pt.y -= GetItemHeight() / 2; - HitTest(&hti); - if (!(hti.flags & TVHT_ABOVE)) - SetInsertMark(hti.hItem, 1); - else - SetInsertMark(it, 0); - } - else { - if (hti.flags & TVHT_ABOVE) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); - if (hti.flags & TVHT_BELOW) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); - SetInsertMark(nullptr, 0); - } - } - break; - - case WM_LBUTTONUP: - if (m_bDragging) { - SetInsertMark(nullptr, 0); - m_bDragging = false; - ReleaseCapture(); - - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam) - GetItemHeight() / 2; - HitTest(&hti); - if (m_hDragItem == hti.hItem) - break; - - if (hti.flags & TVHT_ABOVE) - hti.hItem = TVI_FIRST; - else if (hti.flags & TVHT_BELOW) - hti.hItem = TVI_LAST; - - HTREEITEM insertAfter = hti.hItem, hParent; - if (insertAfter != TVI_FIRST) { - hParent = GetParent(insertAfter); - if (GetChild(insertAfter) != nullptr) { - hParent = insertAfter; - insertAfter = TVI_FIRST; - } - } - else hParent = nullptr; - - HTREEITEM FirstItem = nullptr; - if (m_bMultiSelect) { - LIST<_TREEITEM> arItems(10); - GetSelected(arItems); - - // Proceed moving - for (auto &it : arItems) { - if (!insertAfter) - break; - if (GetParent(it) != hParent) // prevent subitems from being inserted at the same level - continue; - - insertAfter = MoveItemAbove(it, insertAfter, hParent); - if (it == arItems[0]) - FirstItem = insertAfter; - } - } - else FirstItem = MoveItemAbove(m_hDragItem, insertAfter, hParent); - if (FirstItem) - SelectItem(FirstItem); - - NotifyChange(); - } - break; - - case WM_LBUTTONDOWN: - if (!m_bMultiSelect) - break; - - hti.pt.x = (short)LOWORD(lParam); - hti.pt.y = (short)HIWORD(lParam); - if (!TreeView_HitTest(m_hwnd, &hti)) { - UnselectAll(); - break; - } - - if (!m_bDndEnabled) - if (!(wParam & (MK_CONTROL | MK_SHIFT)) || !(hti.flags & (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))) { - UnselectAll(); - TreeView_SelectItem(m_hwnd, hti.hItem); - break; - } - - if (wParam & MK_CONTROL) { - LIST<_TREEITEM> selected(1); - GetSelected(selected); - - // Check if have to deselect it - for (int i = 0; i < selected.getCount(); i++) { - if (selected[i] == hti.hItem) { - // Deselect it - UnselectAll(); - selected.remove(i); - - if (i > 0) - hti.hItem = selected[0]; - else if (i < selected.getCount()) - hti.hItem = selected[i]; - else - hti.hItem = nullptr; - break; - } - } - - TreeView_SelectItem(m_hwnd, hti.hItem); - Select(selected); - } - else if (wParam & MK_SHIFT) { - HTREEITEM hItem = TreeView_GetSelection(m_hwnd); - if (hItem == nullptr) - break; - - LIST<_TREEITEM> selected(1); - GetSelected(selected); - - TreeView_SelectItem(m_hwnd, hti.hItem); - Select(selected); - SelectRange(hItem, hti.hItem); - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_RCLICK: OnRightClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE; - case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE; - case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE; - case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE; - case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE; - - case TVN_BEGINDRAG: - OnBeginDrag(&evt); - - // user-defined can clear the event code to disable dragging - if (m_bDndEnabled && pnmh->code) { - ::SetCapture(m_hwnd); - m_bDragging = true; - m_hDragItem = evt.nmtv->itemNew.hItem; - SelectItem(m_hDragItem); - } - return TRUE; - - case TVN_KEYDOWN: - if (evt.nmtvkey->wVKey == VK_SPACE) { - evt.hItem = GetSelection(); - if (m_bCheckBox) - InvertCheck(evt.hItem); - OnItemChanged(&evt); - NotifyChange(); - } - - OnKeyDown(&evt); - return TRUE; - } - - if (pnmh->code == NM_CLICK) { - TVHITTESTINFO hti; - hti.pt.x = (short)LOWORD(GetMessagePos()); - hti.pt.y = (short)HIWORD(GetMessagePos()); - ScreenToClient(pnmh->hwndFrom, &hti.pt); - if (HitTest(&hti)) { - if (m_bCheckBox && (hti.flags & TVHT_ONITEMICON) || !m_bCheckBox && (hti.flags & TVHT_ONITEMSTATEICON)) { - if (m_bCheckBox) - InvertCheck(hti.hItem); - else - SelectItem(hti.hItem); - - evt.hItem = hti.hItem; - OnItemChanged(&evt); - NotifyChange(); - } - } - } - - return FALSE; -} - -void CCtrlTreeView::InvertCheck(HTREEITEM hItem) -{ - TVITEMEX tvi; - tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATEEX; - tvi.hItem = hItem; - if (!GetItem(&tvi)) - return; - - if (IsWinVerVistaPlus() && (tvi.uStateEx & TVIS_EX_DISABLED)) - return; - - tvi.iImage = tvi.iSelectedImage = !tvi.iImage; - SetItem(&tvi); - - SelectItem(hItem); -} - -void CCtrlTreeView::TranslateItem(HTREEITEM hItem) -{ - TVITEMEX tvi; - wchar_t buf[128]; - GetItem(hItem, &tvi, buf, _countof(buf)); - tvi.pszText = TranslateW_LP(tvi.pszText); - SetItem(&tvi); -} - -void CCtrlTreeView::TranslateTree() -{ - HTREEITEM hItem = GetRoot(); - while (hItem) { - TranslateItem(hItem); - - HTREEITEM hItemTmp = nullptr; - if (hItemTmp = GetChild(hItem)) - hItem = hItemTmp; - else if (hItemTmp = GetNextSibling(hItem)) - hItem = hItemTmp; - else { - while (true) { - if (!(hItem = GetParent(hItem))) - break; - if (hItemTmp = GetNextSibling(hItem)) { - hItem = hItemTmp; - break; - } - } - } - } -} - -HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const wchar_t *name) -{ - TVITEMEX tvi = { 0 }; - wchar_t str[MAX_PATH]; - - if (hItem) - tvi.hItem = GetChild(hItem); - else - tvi.hItem = GetRoot(); - - if (!name) - return tvi.hItem; - - tvi.mask = TVIF_TEXT; - tvi.pszText = str; - tvi.cchTextMax = _countof(str); - - while (tvi.hItem) { - GetItem(&tvi); - - if (!mir_wstrcmp(tvi.pszText, name)) - return tvi.hItem; - - tvi.hItem = GetNextSibling(tvi.hItem); - } - return nullptr; -} - -void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi) const -{ - memset(tvi, 0, sizeof(*tvi)); - tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE; - tvi->hItem = hItem; - GetItem(tvi); -} - -void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, wchar_t *szText, int iTextLength) const -{ - memset(tvi, 0, sizeof(*tvi)); - tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT; - tvi->hItem = hItem; - tvi->pszText = szText; - tvi->cchTextMax = iTextLength; - GetItem(tvi); -} - -bool CCtrlTreeView::IsSelected(HTREEITEM hItem) -{ - return (TVIS_SELECTED & TreeView_GetItemState(m_hwnd, hItem, TVIS_SELECTED)) == TVIS_SELECTED; -} - -void CCtrlTreeView::Select(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, TVIS_SELECTED, TVIS_SELECTED); -} - -void CCtrlTreeView::Unselect(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_SELECTED); -} - -void CCtrlTreeView::DropHilite(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, TVIS_DROPHILITED, TVIS_DROPHILITED); -} - -void CCtrlTreeView::DropUnhilite(HTREEITEM hItem) -{ - TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_DROPHILITED); -} - -void CCtrlTreeView::SelectAll() -{ - TreeView_SelectItem(m_hwnd, nullptr); - - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - Select(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::UnselectAll() -{ - TreeView_SelectItem(m_hwnd, nullptr); - - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - Unselect(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::SelectRange(HTREEITEM hStart, HTREEITEM hEnd) -{ - int start = 0, end = 0, i = 0; - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (hItem == hStart) - start = i; - if (hItem == hEnd) - end = i; - - i++; - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } - - if (end < start) { - int tmp = start; - start = end; - end = tmp; - } - - i = 0; - hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (i >= start) - Select(hItem); - if (i == end) - break; - - i++; - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -int CCtrlTreeView::GetNumSelected() -{ - int ret = 0; - for (HTREEITEM hItem = TreeView_GetRoot(m_hwnd); hItem; hItem = TreeView_GetNextSibling(m_hwnd, hItem)) - if (IsSelected(hItem)) - ret++; - - return ret; -} - -void CCtrlTreeView::GetSelected(LIST<_TREEITEM> &selected) -{ - HTREEITEM hItem = TreeView_GetRoot(m_hwnd); - while (hItem) { - if (IsSelected(hItem)) - selected.insert(hItem); - hItem = TreeView_GetNextSibling(m_hwnd, hItem); - } -} - -void CCtrlTreeView::Select(LIST<_TREEITEM> &selected) -{ - for (auto &it : selected) - if (it != nullptr) - Select(it); -} - -void CCtrlTreeView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - HTREEITEM hItem = GetSelection(); - if (hItem != nullptr) { - pos.pCtrl = this; - pos.hItem = hItem; - - RECT rc; - GetItemRect(hItem, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - TVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (HitTest(&hti) && (hti.flags & TVHT_ONITEM)) { - pos.hItem = hti.hItem; - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem) -{ return TreeView_CreateDragImage(m_hwnd, hItem); -} - -void CCtrlTreeView::DeleteAllItems() -{ TreeView_DeleteAllItems(m_hwnd); -} - -void CCtrlTreeView::DeleteItem(HTREEITEM hItem) -{ TreeView_DeleteItem(m_hwnd, hItem); -} - -HWND CCtrlTreeView::EditLabel(HTREEITEM hItem) -{ return TreeView_EditLabel(m_hwnd, hItem); -} - -void CCtrlTreeView::EndEditLabelNow(BOOL cancel) -{ TreeView_EndEditLabelNow(m_hwnd, cancel); -} - -void CCtrlTreeView::EnsureVisible(HTREEITEM hItem) -{ TreeView_EnsureVisible(m_hwnd, hItem); -} - -void CCtrlTreeView::Expand(HTREEITEM hItem, uint32_t flag) -{ TreeView_Expand(m_hwnd, hItem, flag); -} - -COLORREF CCtrlTreeView::GetBkColor() const -{ return TreeView_GetBkColor(m_hwnd); -} - -uint32_t CCtrlTreeView::GetCheckState(HTREEITEM hItem) const -{ return TreeView_GetCheckState(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem) const -{ return TreeView_GetChild(m_hwnd, hItem); -} - -int CCtrlTreeView::GetCount() const -{ return TreeView_GetCount(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetDropHilight() const -{ return TreeView_GetDropHilight(m_hwnd); -} - -HWND CCtrlTreeView::GetEditControl() const -{ return TreeView_GetEditControl(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetFirstVisible() const -{ return TreeView_GetFirstVisible(m_hwnd); -} - -HIMAGELIST CCtrlTreeView::GetImageList(int iImage) const -{ return TreeView_GetImageList(m_hwnd, iImage); -} - -int CCtrlTreeView::GetIndent() const -{ return TreeView_GetIndent(m_hwnd); -} - -COLORREF CCtrlTreeView::GetInsertMarkColor() const -{ return TreeView_GetInsertMarkColor(m_hwnd); -} - -bool CCtrlTreeView::GetItem(TVITEMEX *tvi) const -{ return TreeView_GetItem(m_hwnd, tvi) == TRUE; -} - -int CCtrlTreeView::GetItemHeight() const -{ return TreeView_GetItemHeight(m_hwnd); -} - -void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect) const -{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect); -} - -uint32_t CCtrlTreeView::GetItemState(HTREEITEM hItem, uint32_t stateMask) const -{ return TreeView_GetItemState(m_hwnd, hItem, stateMask); -} - -HTREEITEM CCtrlTreeView::GetLastVisible() const -{ return TreeView_GetLastVisible(m_hwnd); -} - -COLORREF CCtrlTreeView::GetLineColor() const -{ return TreeView_GetLineColor(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, uint32_t flag) const -{ return TreeView_GetNextItem(m_hwnd, hItem, flag); -} - -HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem) const -{ return TreeView_GetNextSibling(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem) const -{ return TreeView_GetNextVisible(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem) const -{ return TreeView_GetParent(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem) const -{ return TreeView_GetPrevSibling(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem) const -{ return TreeView_GetPrevVisible(m_hwnd, hItem); -} - -HTREEITEM CCtrlTreeView::GetRoot() const -{ return TreeView_GetRoot(m_hwnd); -} - -uint32_t CCtrlTreeView::GetScrollTime() const -{ return TreeView_GetScrollTime(m_hwnd); -} - -HTREEITEM CCtrlTreeView::GetSelection() const -{ return TreeView_GetSelection(m_hwnd); -} - -COLORREF CCtrlTreeView::GetTextColor() const -{ return TreeView_GetTextColor(m_hwnd); -} - -HWND CCtrlTreeView::GetToolTips() const -{ return TreeView_GetToolTips(m_hwnd); -} - -BOOL CCtrlTreeView::GetUnicodeFormat() const -{ return TreeView_GetUnicodeFormat(m_hwnd); -} - -unsigned CCtrlTreeView::GetVisibleCount() const -{ return TreeView_GetVisibleCount(m_hwnd); -} - -HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti) const -{ return TreeView_HitTest(m_hwnd, hti); -} - -HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis) -{ return TreeView_InsertItem(m_hwnd, tvis); -} - -void CCtrlTreeView::Select(HTREEITEM hItem, uint32_t flag) -{ TreeView_Select(m_hwnd, hItem, flag); -} - -void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem) -{ TreeView_SelectDropTarget(m_hwnd, hItem); -} - -void CCtrlTreeView::SelectItem(HTREEITEM hItem) -{ TreeView_SelectItem(m_hwnd, hItem); -} - -void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem) -{ TreeView_SelectSetFirstVisible(m_hwnd, hItem); -} - -COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack) -{ return TreeView_SetBkColor(m_hwnd, clBack); -} - -void CCtrlTreeView::SetCheckState(HTREEITEM hItem, uint32_t state) -{ TreeView_SetCheckState(m_hwnd, hItem, state); -} - -void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage) -{ TreeView_SetImageList(m_hwnd, hIml, iImage); -} - -void CCtrlTreeView::SetIndent(int iIndent) -{ TreeView_SetIndent(m_hwnd, iIndent); -} - -void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter) -{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter); -} - -COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark) -{ return TreeView_SetInsertMarkColor(m_hwnd, clMark); -} - -void CCtrlTreeView::SetItem(TVITEMEX *tvi) -{ TreeView_SetItem(m_hwnd, tvi); -} - -void CCtrlTreeView::SetItemHeight(short cyItem) -{ TreeView_SetItemHeight(m_hwnd, cyItem); -} - -void CCtrlTreeView::SetItemState(HTREEITEM hItem, uint32_t state, uint32_t stateMask) -{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask); -} - -COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine) -{ return TreeView_SetLineColor(m_hwnd, clLine); -} - -void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime) -{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime); -} - -COLORREF CCtrlTreeView::SetTextColor(COLORREF clText) -{ return TreeView_SetTextColor(m_hwnd, clText); -} - -HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips) -{ return TreeView_SetToolTips(m_hwnd, hwndToolTips); -} - -BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode) -{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode); -} - -void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse) -{ TreeView_SortChildren(m_hwnd, hItem, fRecurse); -} - -void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse) -{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +int ImageList_AddIcon_IconLibLoaded(HIMAGELIST hIml, int iconId) +{ + HICON hIcon = Skin_LoadIcon(iconId); + int res = ImageList_AddIcon(hIml, hIcon); + IcoLib_ReleaseIcon(hIcon); + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlTreeView + +CCtrlTreeView::CCtrlTreeView(CDlgBase *dlg, int ctrlId) : + CCtrlBase(dlg, ctrlId), + m_dwFlags(0), + m_hDragItem(nullptr) +{} + +void CCtrlTreeView::SetFlags(uint32_t dwFlags) +{ + if (dwFlags & MTREE_CHECKBOX) + m_bCheckBox = true; + + if (dwFlags & MTREE_MULTISELECT) + m_bMultiSelect = true; + + if (dwFlags & MTREE_DND) { + m_bDndEnabled = true; + m_bDragging = false; + m_hDragItem = nullptr; + } +} + +void CCtrlTreeView::OnInit() +{ + CSuper::OnInit(); + + Subclass(); + + if (m_bCheckBox) { + HIMAGELIST himlCheckBoxes = ::ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 2, 2); + ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_NOTICK); + ::ImageList_AddIcon_IconLibLoaded(himlCheckBoxes, SKINICON_OTHER_TICK); + SetImageList(himlCheckBoxes, TVSIL_NORMAL); + } +} + +void CCtrlTreeView::OnDestroy() +{ + if (m_bCheckBox) + ::ImageList_Destroy(GetImageList(TVSIL_NORMAL)); + + CSuper::OnDestroy(); +} + +HTREEITEM CCtrlTreeView::MoveItemAbove(HTREEITEM hItem, HTREEITEM hInsertAfter, HTREEITEM hParent) +{ + if (hItem == nullptr || hInsertAfter == nullptr) + return nullptr; + + if (hItem == hInsertAfter) + return hItem; + + wchar_t name[128]; + TVINSERTSTRUCT tvis = {}; + tvis.itemex.mask = (UINT)-1; + tvis.itemex.pszText = name; + tvis.itemex.cchTextMax = _countof(name); + tvis.itemex.hItem = hItem; + if (!GetItem(&tvis.itemex)) + return nullptr; + + OBJLIST arChildren(1); + for (HTREEITEM p = GetChild(hItem); p; p = GetNextSibling(p)) { + wchar_t buf[128]; + TVINSERTSTRUCT tvis2 = {}; + tvis2.itemex.mask = (UINT)-1; + tvis2.itemex.pszText = buf; + tvis2.itemex.cchTextMax = _countof(buf); + tvis2.itemex.hItem = p; + if (GetItem(&tvis2.itemex)) { + tvis2.itemex.pszText = mir_wstrdup(tvis2.itemex.pszText); + arChildren.insert(new TVINSERTSTRUCT(tvis2)); + + tvis2.itemex.lParam = 0; + SetItem(&tvis2.itemex); + } + } + + // the pointed lParam will be freed inside TVN_DELETEITEM + // so lets substitute it with 0 + LPARAM saveOldData = tvis.itemex.lParam; + tvis.itemex.lParam = 0; + SetItem(&tvis.itemex); + + // now current item contain lParam = 0 we can delete it. the memory will be kept. + DeleteItem(hItem); + + for (auto &it : arChildren) + DeleteItem(it->itemex.hItem); + + tvis.itemex.stateMask = tvis.itemex.state; + tvis.itemex.lParam = saveOldData; + tvis.hParent = hParent; + tvis.hInsertAfter = hInsertAfter; + auto hNewItem = InsertItem(&tvis); + + hInsertAfter = nullptr; + for (auto &it : arChildren) { + it->hParent = hNewItem; + it->hInsertAfter = hInsertAfter; + hInsertAfter = InsertItem(it); + + mir_free(it->itemex.pszText); + } + + return hNewItem; +} + +LRESULT CCtrlTreeView::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + TVHITTESTINFO hti; + + switch (msg) { + case WM_MOUSEMOVE: + if (m_bDragging) { + hti.pt.x = (short)LOWORD(lParam); + hti.pt.y = (short)HIWORD(lParam); + HitTest(&hti); + if (hti.flags & (TVHT_ONITEM | TVHT_ONITEMRIGHT)) { + HTREEITEM it = hti.hItem; + hti.pt.y -= GetItemHeight() / 2; + HitTest(&hti); + if (!(hti.flags & TVHT_ABOVE)) + SetInsertMark(hti.hItem, 1); + else + SetInsertMark(it, 0); + } + else { + if (hti.flags & TVHT_ABOVE) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); + if (hti.flags & TVHT_BELOW) SendMsg(WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); + SetInsertMark(nullptr, 0); + } + } + break; + + case WM_LBUTTONUP: + if (m_bDragging) { + SetInsertMark(nullptr, 0); + m_bDragging = false; + ReleaseCapture(); + + hti.pt.x = (short)LOWORD(lParam); + hti.pt.y = (short)HIWORD(lParam) - GetItemHeight() / 2; + HitTest(&hti); + if (m_hDragItem == hti.hItem) + break; + + if (hti.flags & TVHT_ABOVE) + hti.hItem = TVI_FIRST; + else if (hti.flags & TVHT_BELOW) + hti.hItem = TVI_LAST; + + HTREEITEM insertAfter = hti.hItem, hParent; + if (insertAfter != TVI_FIRST) { + hParent = GetParent(insertAfter); + if (GetChild(insertAfter) != nullptr) { + hParent = insertAfter; + insertAfter = TVI_FIRST; + } + } + else hParent = nullptr; + + HTREEITEM FirstItem = nullptr; + if (m_bMultiSelect) { + LIST<_TREEITEM> arItems(10); + GetSelected(arItems); + + // Proceed moving + for (auto &it : arItems) { + if (!insertAfter) + break; + if (GetParent(it) != hParent) // prevent subitems from being inserted at the same level + continue; + + insertAfter = MoveItemAbove(it, insertAfter, hParent); + if (it == arItems[0]) + FirstItem = insertAfter; + } + } + else FirstItem = MoveItemAbove(m_hDragItem, insertAfter, hParent); + if (FirstItem) + SelectItem(FirstItem); + + NotifyChange(); + } + break; + + case WM_LBUTTONDOWN: + if (!m_bMultiSelect) + break; + + hti.pt.x = (short)LOWORD(lParam); + hti.pt.y = (short)HIWORD(lParam); + if (!TreeView_HitTest(m_hwnd, &hti)) { + UnselectAll(); + break; + } + + if (!m_bDndEnabled) + if (!(wParam & (MK_CONTROL | MK_SHIFT)) || !(hti.flags & (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMRIGHT))) { + UnselectAll(); + TreeView_SelectItem(m_hwnd, hti.hItem); + break; + } + + if (wParam & MK_CONTROL) { + LIST<_TREEITEM> selected(1); + GetSelected(selected); + + // Check if have to deselect it + for (int i = 0; i < selected.getCount(); i++) { + if (selected[i] == hti.hItem) { + // Deselect it + UnselectAll(); + selected.remove(i); + + if (i > 0) + hti.hItem = selected[0]; + else if (i < selected.getCount()) + hti.hItem = selected[i]; + else + hti.hItem = nullptr; + break; + } + } + + TreeView_SelectItem(m_hwnd, hti.hItem); + Select(selected); + } + else if (wParam & MK_SHIFT) { + HTREEITEM hItem = TreeView_GetSelection(m_hwnd); + if (hItem == nullptr) + break; + + LIST<_TREEITEM> selected(1); + GetSelected(selected); + + TreeView_SelectItem(m_hwnd, hti.hItem); + Select(selected); + SelectRange(hItem, hti.hItem); + } + break; + } + + return CSuper::CustomWndProc(msg, wParam, lParam); +} + +BOOL CCtrlTreeView::OnNotify(int, NMHDR *pnmh) +{ + TEventInfo evt = { this, pnmh }; + + switch (pnmh->code) { + case NM_RCLICK: OnRightClick(&evt); return TRUE; + case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; + case TVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; + case TVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; + case TVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; + case TVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; + case TVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; + case TVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; + case TVN_ITEMEXPANDED: OnItemExpanded(&evt); return TRUE; + case TVN_ITEMEXPANDING: OnItemExpanding(&evt); return TRUE; + case TVN_SELCHANGED: OnSelChanged(&evt); return TRUE; + case TVN_SELCHANGING: OnSelChanging(&evt); return TRUE; + case TVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; + case TVN_SINGLEEXPAND: OnSingleExpand(&evt); return TRUE; + + case TVN_BEGINDRAG: + OnBeginDrag(&evt); + + // user-defined can clear the event code to disable dragging + if (m_bDndEnabled && pnmh->code) { + ::SetCapture(m_hwnd); + m_bDragging = true; + m_hDragItem = evt.nmtv->itemNew.hItem; + SelectItem(m_hDragItem); + } + return TRUE; + + case TVN_KEYDOWN: + if (evt.nmtvkey->wVKey == VK_SPACE) { + evt.hItem = GetSelection(); + if (m_bCheckBox) + InvertCheck(evt.hItem); + OnItemChanged(&evt); + NotifyChange(); + } + + OnKeyDown(&evt); + return TRUE; + } + + if (pnmh->code == NM_CLICK) { + TVHITTESTINFO hti; + hti.pt.x = (short)LOWORD(GetMessagePos()); + hti.pt.y = (short)HIWORD(GetMessagePos()); + ScreenToClient(pnmh->hwndFrom, &hti.pt); + if (HitTest(&hti)) { + if (m_bCheckBox && (hti.flags & TVHT_ONITEMICON) || !m_bCheckBox && (hti.flags & TVHT_ONITEMSTATEICON)) { + if (m_bCheckBox) + InvertCheck(hti.hItem); + else + SelectItem(hti.hItem); + + evt.hItem = hti.hItem; + OnItemChanged(&evt); + NotifyChange(); + } + } + } + + return FALSE; +} + +void CCtrlTreeView::InvertCheck(HTREEITEM hItem) +{ + TVITEMEX tvi; + tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM | TVIF_STATEEX; + tvi.hItem = hItem; + if (!GetItem(&tvi)) + return; + + if (IsWinVerVistaPlus() && (tvi.uStateEx & TVIS_EX_DISABLED)) + return; + + tvi.iImage = tvi.iSelectedImage = !tvi.iImage; + SetItem(&tvi); + + SelectItem(hItem); +} + +void CCtrlTreeView::TranslateItem(HTREEITEM hItem) +{ + TVITEMEX tvi; + wchar_t buf[128]; + GetItem(hItem, &tvi, buf, _countof(buf)); + tvi.pszText = TranslateW_LP(tvi.pszText); + SetItem(&tvi); +} + +void CCtrlTreeView::TranslateTree() +{ + HTREEITEM hItem = GetRoot(); + while (hItem) { + TranslateItem(hItem); + + HTREEITEM hItemTmp = nullptr; + if (hItemTmp = GetChild(hItem)) + hItem = hItemTmp; + else if (hItemTmp = GetNextSibling(hItem)) + hItem = hItemTmp; + else { + while (true) { + if (!(hItem = GetParent(hItem))) + break; + if (hItemTmp = GetNextSibling(hItem)) { + hItem = hItemTmp; + break; + } + } + } + } +} + +HTREEITEM CCtrlTreeView::FindNamedItem(HTREEITEM hItem, const wchar_t *name) +{ + TVITEMEX tvi = { 0 }; + wchar_t str[MAX_PATH]; + + if (hItem) + tvi.hItem = GetChild(hItem); + else + tvi.hItem = GetRoot(); + + if (!name) + return tvi.hItem; + + tvi.mask = TVIF_TEXT; + tvi.pszText = str; + tvi.cchTextMax = _countof(str); + + while (tvi.hItem) { + GetItem(&tvi); + + if (!mir_wstrcmp(tvi.pszText, name)) + return tvi.hItem; + + tvi.hItem = GetNextSibling(tvi.hItem); + } + return nullptr; +} + +void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi) const +{ + memset(tvi, 0, sizeof(*tvi)); + tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE; + tvi->hItem = hItem; + GetItem(tvi); +} + +void CCtrlTreeView::GetItem(HTREEITEM hItem, TVITEMEX *tvi, wchar_t *szText, int iTextLength) const +{ + memset(tvi, 0, sizeof(*tvi)); + tvi->mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATE | TVIF_TEXT; + tvi->hItem = hItem; + tvi->pszText = szText; + tvi->cchTextMax = iTextLength; + GetItem(tvi); +} + +bool CCtrlTreeView::IsSelected(HTREEITEM hItem) +{ + return (TVIS_SELECTED & TreeView_GetItemState(m_hwnd, hItem, TVIS_SELECTED)) == TVIS_SELECTED; +} + +void CCtrlTreeView::Select(HTREEITEM hItem) +{ + TreeView_SetItemState(m_hwnd, hItem, TVIS_SELECTED, TVIS_SELECTED); +} + +void CCtrlTreeView::Unselect(HTREEITEM hItem) +{ + TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_SELECTED); +} + +void CCtrlTreeView::DropHilite(HTREEITEM hItem) +{ + TreeView_SetItemState(m_hwnd, hItem, TVIS_DROPHILITED, TVIS_DROPHILITED); +} + +void CCtrlTreeView::DropUnhilite(HTREEITEM hItem) +{ + TreeView_SetItemState(m_hwnd, hItem, 0, TVIS_DROPHILITED); +} + +void CCtrlTreeView::SelectAll() +{ + TreeView_SelectItem(m_hwnd, nullptr); + + HTREEITEM hItem = TreeView_GetRoot(m_hwnd); + while (hItem) { + Select(hItem); + hItem = TreeView_GetNextSibling(m_hwnd, hItem); + } +} + +void CCtrlTreeView::UnselectAll() +{ + TreeView_SelectItem(m_hwnd, nullptr); + + HTREEITEM hItem = TreeView_GetRoot(m_hwnd); + while (hItem) { + Unselect(hItem); + hItem = TreeView_GetNextSibling(m_hwnd, hItem); + } +} + +void CCtrlTreeView::SelectRange(HTREEITEM hStart, HTREEITEM hEnd) +{ + int start = 0, end = 0, i = 0; + HTREEITEM hItem = TreeView_GetRoot(m_hwnd); + while (hItem) { + if (hItem == hStart) + start = i; + if (hItem == hEnd) + end = i; + + i++; + hItem = TreeView_GetNextSibling(m_hwnd, hItem); + } + + if (end < start) { + int tmp = start; + start = end; + end = tmp; + } + + i = 0; + hItem = TreeView_GetRoot(m_hwnd); + while (hItem) { + if (i >= start) + Select(hItem); + if (i == end) + break; + + i++; + hItem = TreeView_GetNextSibling(m_hwnd, hItem); + } +} + +int CCtrlTreeView::GetNumSelected() +{ + int ret = 0; + for (HTREEITEM hItem = TreeView_GetRoot(m_hwnd); hItem; hItem = TreeView_GetNextSibling(m_hwnd, hItem)) + if (IsSelected(hItem)) + ret++; + + return ret; +} + +void CCtrlTreeView::GetSelected(LIST<_TREEITEM> &selected) +{ + HTREEITEM hItem = TreeView_GetRoot(m_hwnd); + while (hItem) { + if (IsSelected(hItem)) + selected.insert(hItem); + hItem = TreeView_GetNextSibling(m_hwnd, hItem); + } +} + +void CCtrlTreeView::Select(LIST<_TREEITEM> &selected) +{ + for (auto &it : selected) + if (it != nullptr) + Select(it); +} + +void CCtrlTreeView::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + + // position is empty, let's fill it using selection + if (pos.pt.x == 0 && pos.pt.y == 0) { + HTREEITEM hItem = GetSelection(); + if (hItem != nullptr) { + pos.pCtrl = this; + pos.hItem = hItem; + + RECT rc; + GetItemRect(hItem, &rc, TRUE); + pos.pt.x = rc.left + 8; + pos.pt.y = rc.top + 8; + ClientToScreen(m_hwnd, &pos.pt); + return; + } + } + // position is present, let's calculate current item + else { + TVHITTESTINFO hti; + hti.pt = pos.pt; + ScreenToClient(m_hwnd, &hti.pt); + if (HitTest(&hti) && (hti.flags & TVHT_ONITEM)) { + pos.hItem = hti.hItem; + return; + } + } + + CSuper::GetCaretPos(pos); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +HIMAGELIST CCtrlTreeView::CreateDragImage(HTREEITEM hItem) +{ return TreeView_CreateDragImage(m_hwnd, hItem); +} + +void CCtrlTreeView::DeleteAllItems() +{ TreeView_DeleteAllItems(m_hwnd); +} + +void CCtrlTreeView::DeleteItem(HTREEITEM hItem) +{ TreeView_DeleteItem(m_hwnd, hItem); +} + +HWND CCtrlTreeView::EditLabel(HTREEITEM hItem) +{ return TreeView_EditLabel(m_hwnd, hItem); +} + +void CCtrlTreeView::EndEditLabelNow(BOOL cancel) +{ TreeView_EndEditLabelNow(m_hwnd, cancel); +} + +void CCtrlTreeView::EnsureVisible(HTREEITEM hItem) +{ TreeView_EnsureVisible(m_hwnd, hItem); +} + +void CCtrlTreeView::Expand(HTREEITEM hItem, uint32_t flag) +{ TreeView_Expand(m_hwnd, hItem, flag); +} + +COLORREF CCtrlTreeView::GetBkColor() const +{ return TreeView_GetBkColor(m_hwnd); +} + +uint32_t CCtrlTreeView::GetCheckState(HTREEITEM hItem) const +{ return TreeView_GetCheckState(m_hwnd, hItem); +} + +HTREEITEM CCtrlTreeView::GetChild(HTREEITEM hItem) const +{ return TreeView_GetChild(m_hwnd, hItem); +} + +int CCtrlTreeView::GetCount() const +{ return TreeView_GetCount(m_hwnd); +} + +HTREEITEM CCtrlTreeView::GetDropHilight() const +{ return TreeView_GetDropHilight(m_hwnd); +} + +HWND CCtrlTreeView::GetEditControl() const +{ return TreeView_GetEditControl(m_hwnd); +} + +HTREEITEM CCtrlTreeView::GetFirstVisible() const +{ return TreeView_GetFirstVisible(m_hwnd); +} + +HIMAGELIST CCtrlTreeView::GetImageList(int iImage) const +{ return TreeView_GetImageList(m_hwnd, iImage); +} + +int CCtrlTreeView::GetIndent() const +{ return TreeView_GetIndent(m_hwnd); +} + +COLORREF CCtrlTreeView::GetInsertMarkColor() const +{ return TreeView_GetInsertMarkColor(m_hwnd); +} + +bool CCtrlTreeView::GetItem(TVITEMEX *tvi) const +{ return TreeView_GetItem(m_hwnd, tvi) == TRUE; +} + +int CCtrlTreeView::GetItemHeight() const +{ return TreeView_GetItemHeight(m_hwnd); +} + +void CCtrlTreeView::GetItemRect(HTREEITEM hItem, RECT *rcItem, BOOL fItemRect) const +{ TreeView_GetItemRect(m_hwnd, hItem, rcItem, fItemRect); +} + +uint32_t CCtrlTreeView::GetItemState(HTREEITEM hItem, uint32_t stateMask) const +{ return TreeView_GetItemState(m_hwnd, hItem, stateMask); +} + +HTREEITEM CCtrlTreeView::GetLastVisible() const +{ return TreeView_GetLastVisible(m_hwnd); +} + +COLORREF CCtrlTreeView::GetLineColor() const +{ return TreeView_GetLineColor(m_hwnd); +} + +HTREEITEM CCtrlTreeView::GetNextItem(HTREEITEM hItem, uint32_t flag) const +{ return TreeView_GetNextItem(m_hwnd, hItem, flag); +} + +HTREEITEM CCtrlTreeView::GetNextSibling(HTREEITEM hItem) const +{ return TreeView_GetNextSibling(m_hwnd, hItem); +} + +HTREEITEM CCtrlTreeView::GetNextVisible(HTREEITEM hItem) const +{ return TreeView_GetNextVisible(m_hwnd, hItem); +} + +HTREEITEM CCtrlTreeView::GetParent(HTREEITEM hItem) const +{ return TreeView_GetParent(m_hwnd, hItem); +} + +HTREEITEM CCtrlTreeView::GetPrevSibling(HTREEITEM hItem) const +{ return TreeView_GetPrevSibling(m_hwnd, hItem); +} + +HTREEITEM CCtrlTreeView::GetPrevVisible(HTREEITEM hItem) const +{ return TreeView_GetPrevVisible(m_hwnd, hItem); +} + +HTREEITEM CCtrlTreeView::GetRoot() const +{ return TreeView_GetRoot(m_hwnd); +} + +uint32_t CCtrlTreeView::GetScrollTime() const +{ return TreeView_GetScrollTime(m_hwnd); +} + +HTREEITEM CCtrlTreeView::GetSelection() const +{ return TreeView_GetSelection(m_hwnd); +} + +COLORREF CCtrlTreeView::GetTextColor() const +{ return TreeView_GetTextColor(m_hwnd); +} + +HWND CCtrlTreeView::GetToolTips() const +{ return TreeView_GetToolTips(m_hwnd); +} + +BOOL CCtrlTreeView::GetUnicodeFormat() const +{ return TreeView_GetUnicodeFormat(m_hwnd); +} + +unsigned CCtrlTreeView::GetVisibleCount() const +{ return TreeView_GetVisibleCount(m_hwnd); +} + +HTREEITEM CCtrlTreeView::HitTest(TVHITTESTINFO *hti) const +{ return TreeView_HitTest(m_hwnd, hti); +} + +HTREEITEM CCtrlTreeView::InsertItem(TVINSERTSTRUCT *tvis) +{ return TreeView_InsertItem(m_hwnd, tvis); +} + +void CCtrlTreeView::Select(HTREEITEM hItem, uint32_t flag) +{ TreeView_Select(m_hwnd, hItem, flag); +} + +void CCtrlTreeView::SelectDropTarget(HTREEITEM hItem) +{ TreeView_SelectDropTarget(m_hwnd, hItem); +} + +void CCtrlTreeView::SelectItem(HTREEITEM hItem) +{ TreeView_SelectItem(m_hwnd, hItem); +} + +void CCtrlTreeView::SelectSetFirstVisible(HTREEITEM hItem) +{ TreeView_SelectSetFirstVisible(m_hwnd, hItem); +} + +COLORREF CCtrlTreeView::SetBkColor(COLORREF clBack) +{ return TreeView_SetBkColor(m_hwnd, clBack); +} + +void CCtrlTreeView::SetCheckState(HTREEITEM hItem, uint32_t state) +{ TreeView_SetCheckState(m_hwnd, hItem, state); +} + +void CCtrlTreeView::SetImageList(HIMAGELIST hIml, int iImage) +{ TreeView_SetImageList(m_hwnd, hIml, iImage); +} + +void CCtrlTreeView::SetIndent(int iIndent) +{ TreeView_SetIndent(m_hwnd, iIndent); +} + +void CCtrlTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter) +{ TreeView_SetInsertMark(m_hwnd, hItem, fAfter); +} + +COLORREF CCtrlTreeView::SetInsertMarkColor(COLORREF clMark) +{ return TreeView_SetInsertMarkColor(m_hwnd, clMark); +} + +void CCtrlTreeView::SetItem(TVITEMEX *tvi) +{ TreeView_SetItem(m_hwnd, tvi); +} + +void CCtrlTreeView::SetItemHeight(short cyItem) +{ TreeView_SetItemHeight(m_hwnd, cyItem); +} + +void CCtrlTreeView::SetItemState(HTREEITEM hItem, uint32_t state, uint32_t stateMask) +{ TreeView_SetItemState(m_hwnd, hItem, state, stateMask); +} + +COLORREF CCtrlTreeView::SetLineColor(COLORREF clLine) +{ return TreeView_SetLineColor(m_hwnd, clLine); +} + +void CCtrlTreeView::SetScrollTime(UINT uMaxScrollTime) +{ TreeView_SetScrollTime(m_hwnd, uMaxScrollTime); +} + +COLORREF CCtrlTreeView::SetTextColor(COLORREF clText) +{ return TreeView_SetTextColor(m_hwnd, clText); +} + +HWND CCtrlTreeView::SetToolTips(HWND hwndToolTips) +{ return TreeView_SetToolTips(m_hwnd, hwndToolTips); +} + +BOOL CCtrlTreeView::SetUnicodeFormat(BOOL fUnicode) +{ return TreeView_SetUnicodeFormat(m_hwnd, fUnicode); +} + +void CCtrlTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse) +{ TreeView_SortChildren(m_hwnd, hItem, fRecurse); +} + +void CCtrlTreeView::SortChildrenCB(TVSORTCB *cb, BOOL fRecurse) +{ TreeView_SortChildrenCB(m_hwnd, cb, fRecurse); +} diff --git a/src/mir_core/src/Linux/CDbLink.cpp b/src/mir_core/src/Linux/CDbLink.cpp index 2a0734cd3b..05925f871b 100644 --- a/src/mir_core/src/Linux/CDbLink.cpp +++ b/src/mir_core/src/Linux/CDbLink.cpp @@ -1,92 +1,92 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbLink class - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) - : CDataLink(type) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_iDefault = iValue; - m_szDefault = nullptr; - dbv.type = DBVT_DELETED; -} - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) - : CDataLink(type), - m_iDefault(0) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_szDefault = mir_wstrdup(szValue); - dbv.type = DBVT_DELETED; -} - -CDbLink::~CDbLink() -{ - mir_free(m_szModule); - mir_free(m_szSetting); - mir_free(m_szDefault); - if (dbv.type != DBVT_DELETED) - db_free(&dbv); -} - -uint32_t CDbLink::LoadInt() -{ - switch (m_type) { - case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); - default: return m_iDefault; - } -} - -void CDbLink::SaveInt(uint32_t value) -{ - switch (m_type) { - case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; - case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; - case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; - } -} - -wchar_t* CDbLink::LoadText() -{ - if (dbv.type != DBVT_DELETED) db_free(&dbv); - if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { - if (dbv.type == DBVT_WCHAR) - return dbv.pwszVal; - return m_szDefault; - } - - dbv.type = DBVT_DELETED; - return m_szDefault; -} - -void CDbLink::SaveText(wchar_t *value) -{ - db_set_ws(0, m_szModule, m_szSetting, value); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CDbLink class + +CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) + : CDataLink(type) +{ + m_szModule = mir_strdup(szModule); + m_szSetting = mir_strdup(szSetting); + m_iDefault = iValue; + m_szDefault = nullptr; + dbv.type = DBVT_DELETED; +} + +CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) + : CDataLink(type), + m_iDefault(0) +{ + m_szModule = mir_strdup(szModule); + m_szSetting = mir_strdup(szSetting); + m_szDefault = mir_wstrdup(szValue); + dbv.type = DBVT_DELETED; +} + +CDbLink::~CDbLink() +{ + mir_free(m_szModule); + mir_free(m_szSetting); + mir_free(m_szDefault); + if (dbv.type != DBVT_DELETED) + db_free(&dbv); +} + +uint32_t CDbLink::LoadInt() +{ + switch (m_type) { + case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); + case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); + case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); + default: return m_iDefault; + } +} + +void CDbLink::SaveInt(uint32_t value) +{ + switch (m_type) { + case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; + case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; + case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; + } +} + +wchar_t* CDbLink::LoadText() +{ + if (dbv.type != DBVT_DELETED) db_free(&dbv); + if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { + if (dbv.type == DBVT_WCHAR) + return dbv.pwszVal; + return m_szDefault; + } + + dbv.type = DBVT_DELETED; + return m_szDefault; +} + +void CDbLink::SaveText(wchar_t *value) +{ + db_set_ws(0, m_szModule, m_szSetting, value); +} diff --git a/src/mir_core/src/Linux/CDlgBase.cpp b/src/mir_core/src/Linux/CDlgBase.cpp index 3504a7e74c..2ec9ceb8c8 100644 --- a/src/mir_core/src/Linux/CDlgBase.cpp +++ b/src/mir_core/src/Linux/CDlgBase.cpp @@ -1,260 +1,260 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csDialogs; - -static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST arDialogs(10, CompareDialogs); - -#pragma comment(lib, "uxtheme") - -///////////////////////////////////////////////////////////////////////////////////////// -// CDlgBase - -static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) -{ - return c1->GetCtrlId() - c2->GetCtrlId(); -} - -static int CompareTimerId(const CTimer *t1, const CTimer *t2) -{ - return t1->GetEventId() - t2->GetEventId(); -} - -CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) - : m_controls(1, CompareControlId), - m_timers(1, CompareTimerId), - m_pPlugin(pPlug) -{ - m_idDialog = idDialog; - m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; -} - -CDlgBase::~CDlgBase() -{ - m_bInitialized = false; // prevent double call of destructor - // if (m_hwnd) - // DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// events - -bool CDlgBase::OnInitDialog() -{ - return true; -} - -bool CDlgBase::OnClose() -{ - return true; -} - -bool CDlgBase::OnApply() -{ - return true; -} - -void CDlgBase::OnChange() -{} - -void CDlgBase::OnDestroy() -{} - -void CDlgBase::OnReset() -{} - -void CDlgBase::OnTimer(CTimer*) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// methods - -void CDlgBase::Close() -{ - // ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); -} - -void CDlgBase::Create() -{ - // CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -int CDlgBase::DoModal() -{ - m_isModal = true; - // return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); - return 0; //!!!!!!!! -} - -void CDlgBase::EndModal(INT_PTR nResult) -{ - // ::EndDialog(m_hwnd, nResult); -} - -HINSTANCE CDlgBase::GetInst() const -{ - return m_pPlugin.getInst(); -} - -void CDlgBase::NotifyChange(void) -{ - if (!m_bInitialized) - return; - - OnChange(); - - // if (m_hwndParent) - // SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); -} - -void CDlgBase::Resize() -{ - // SendMessage(m_hwnd, WM_SIZE, 0, 0); -} - -void CDlgBase::SetCaption(const wchar_t *ptszCaption) -{ - // if (m_hwnd && ptszCaption) - // SetText(ptszCaption); -} - -void CDlgBase::SetDraw(bool bEnable) -{ - // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CDlgBase::Show(int nCmdShow) -{ - if (m_hwnd == nullptr) - Create(); - // ShowWindow(m_hwnd, nCmdShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); -} - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// virtual methods - -int CDlgBase::Resizer(UTILRESIZECONTROL*) -{ - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -BOOL CALLBACK CDlgBase::GlobalFieldEnum(MWindow hwnd, LPARAM lParam) -{ - return TRUE; -} - -INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - return FALSE; -} - -int CDlgBase::GlobalDlgResizer(MWindow hwnd, LPARAM, UTILRESIZECONTROL *urc) -{ - CDlgBase *wnd = CDlgBase::Find(hwnd); - return (wnd == nullptr) ? 0 : wnd->Resizer(urc); -} - -void CDlgBase::ThemeDialogBackground(BOOL tabbed) -{ -} - -void CDlgBase::AddControl(CCtrlBase *ctrl) -{ - m_controls.insert(ctrl); -} - -void CDlgBase::RemoveControl(CCtrlBase *ctrl) -{ - m_controls.remove(ctrl); -} - -void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - (it->*fn)(); -} - -bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - if (!(it->*fn)()) - return false; - - return true; -} - -CCtrlBase* CDlgBase::FindControl(int idCtrl) -{ - CCtrlBase search(nullptr, idCtrl); - return m_controls.find(&search); -} - -CCtrlBase* CDlgBase::FindControl(MWindow hwnd) -{ - for (auto &it : m_controls) - if (it->GetHwnd() == hwnd) - return it; - - return nullptr; -} - -void CDlgBase::AddTimer(CTimer *timer) -{ - m_timers.insert(timer); -} - -void CDlgBase::RemoveTimer(UINT_PTR idEvent) -{ - CTimer search(nullptr, idEvent); - m_timers.remove(&search); -} - -CTimer* CDlgBase::FindTimer(int idEvent) -{ - CTimer search(nullptr, idEvent); - return m_timers.find(&search); -} - -CDlgBase* CDlgBase::Find(MWindow hwnd) -{ - void *bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - return arDialogs.find((CDlgBase*)&bullshit); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +static mir_cs csDialogs; + +static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) +{ + return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); +} +static LIST arDialogs(10, CompareDialogs); + +#pragma comment(lib, "uxtheme") + +///////////////////////////////////////////////////////////////////////////////////////// +// CDlgBase + +static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) +{ + return c1->GetCtrlId() - c2->GetCtrlId(); +} + +static int CompareTimerId(const CTimer *t1, const CTimer *t2) +{ + return t1->GetEventId() - t2->GetEventId(); +} + +CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) + : m_controls(1, CompareControlId), + m_timers(1, CompareTimerId), + m_pPlugin(pPlug) +{ + m_idDialog = idDialog; + m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; +} + +CDlgBase::~CDlgBase() +{ + m_bInitialized = false; // prevent double call of destructor + // if (m_hwnd) + // DestroyWindow(m_hwnd); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// events + +bool CDlgBase::OnInitDialog() +{ + return true; +} + +bool CDlgBase::OnClose() +{ + return true; +} + +bool CDlgBase::OnApply() +{ + return true; +} + +void CDlgBase::OnChange() +{} + +void CDlgBase::OnDestroy() +{} + +void CDlgBase::OnReset() +{} + +void CDlgBase::OnTimer(CTimer*) +{} + +///////////////////////////////////////////////////////////////////////////////////////// +// methods + +void CDlgBase::Close() +{ + // ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); +} + +void CDlgBase::Create() +{ + // CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); +} + +int CDlgBase::DoModal() +{ + m_isModal = true; + // return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); + return 0; //!!!!!!!! +} + +void CDlgBase::EndModal(INT_PTR nResult) +{ + // ::EndDialog(m_hwnd, nResult); +} + +HINSTANCE CDlgBase::GetInst() const +{ + return m_pPlugin.getInst(); +} + +void CDlgBase::NotifyChange(void) +{ + if (!m_bInitialized) + return; + + OnChange(); + + // if (m_hwndParent) + // SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); +} + +void CDlgBase::Resize() +{ + // SendMessage(m_hwnd, WM_SIZE, 0, 0); +} + +void CDlgBase::SetCaption(const wchar_t *ptszCaption) +{ + // if (m_hwnd && ptszCaption) + // SetText(ptszCaption); +} + +void CDlgBase::SetDraw(bool bEnable) +{ + // ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); +} + +void CDlgBase::Show(int nCmdShow) +{ + if (m_hwnd == nullptr) + Create(); + // ShowWindow(m_hwnd, nCmdShow); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) +{ + ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); +} + +void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) +{ + ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// virtual methods + +int CDlgBase::Resizer(UTILRESIZECONTROL*) +{ + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +BOOL CALLBACK CDlgBase::GlobalFieldEnum(MWindow hwnd, LPARAM lParam) +{ + return TRUE; +} + +INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + return FALSE; +} + +int CDlgBase::GlobalDlgResizer(MWindow hwnd, LPARAM, UTILRESIZECONTROL *urc) +{ + CDlgBase *wnd = CDlgBase::Find(hwnd); + return (wnd == nullptr) ? 0 : wnd->Resizer(urc); +} + +void CDlgBase::ThemeDialogBackground(BOOL tabbed) +{ +} + +void CDlgBase::AddControl(CCtrlBase *ctrl) +{ + m_controls.insert(ctrl); +} + +void CDlgBase::RemoveControl(CCtrlBase *ctrl) +{ + m_controls.remove(ctrl); +} + +void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) +{ + for (auto &it : m_controls) + (it->*fn)(); +} + +bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) +{ + for (auto &it : m_controls) + if (!(it->*fn)()) + return false; + + return true; +} + +CCtrlBase* CDlgBase::FindControl(int idCtrl) +{ + CCtrlBase search(nullptr, idCtrl); + return m_controls.find(&search); +} + +CCtrlBase* CDlgBase::FindControl(MWindow hwnd) +{ + for (auto &it : m_controls) + if (it->GetHwnd() == hwnd) + return it; + + return nullptr; +} + +void CDlgBase::AddTimer(CTimer *timer) +{ + m_timers.insert(timer); +} + +void CDlgBase::RemoveTimer(UINT_PTR idEvent) +{ + CTimer search(nullptr, idEvent); + m_timers.remove(&search); +} + +CTimer* CDlgBase::FindTimer(int idEvent) +{ + CTimer search(nullptr, idEvent); + return m_timers.find(&search); +} + +CDlgBase* CDlgBase::Find(MWindow hwnd) +{ + void *bullshit[2]; // vfptr + hwnd + bullshit[1] = hwnd; + return arDialogs.find((CDlgBase*)&bullshit); +} diff --git a/src/mir_core/src/Linux/CProgress.cpp b/src/mir_core/src/Linux/CProgress.cpp index 991c6f239d..e2d5321f59 100644 --- a/src/mir_core/src/Linux/CProgress.cpp +++ b/src/mir_core/src/Linux/CProgress.cpp @@ -1,53 +1,53 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlProgress - -CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{ -} - -void CCtrlProgress::SetRange(uint16_t max, uint16_t min) -{ - SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); -} - -void CCtrlProgress::SetPosition(uint16_t value) -{ - SendMsg(PBM_SETPOS, value, 0); -} - -void CCtrlProgress::SetStep(uint16_t value) -{ - SendMsg(PBM_SETSTEP, value, 0); -} - -uint16_t CCtrlProgress::Move(uint16_t delta) -{ - return delta == 0 - ? SendMsg(PBM_STEPIT, 0, 0) - : SendMsg(PBM_DELTAPOS, delta, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlProgress + +CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl) +{ +} + +void CCtrlProgress::SetRange(uint16_t max, uint16_t min) +{ + SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); +} + +void CCtrlProgress::SetPosition(uint16_t value) +{ + SendMsg(PBM_SETPOS, value, 0); +} + +void CCtrlProgress::SetStep(uint16_t value) +{ + SendMsg(PBM_SETSTEP, value, 0); +} + +uint16_t CCtrlProgress::Move(uint16_t delta) +{ + return delta == 0 + ? SendMsg(PBM_STEPIT, 0, 0) + : SendMsg(PBM_DELTAPOS, delta, 0); +} diff --git a/src/mir_core/src/Linux/CSplitter.cpp b/src/mir_core/src/Linux/CSplitter.cpp index e2ee6b6fc8..153beaf95b 100644 --- a/src/mir_core/src/Linux/CSplitter.cpp +++ b/src/mir_core/src/Linux/CSplitter.cpp @@ -1,83 +1,83 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CSplitter - -CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_iPosition(0) -{ -} - -void CSplitter::OnInit() -{ - CSuper::OnInit(); - Subclass(); -} - -LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - RECT rc; - GetClientRect(m_hwnd, &rc); - SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); - return TRUE; - - case WM_LBUTTONDOWN: - SetCapture(m_hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == m_hwnd) { - POINT pt = { 0, 0 }; - GetClientRect(m_hwnd, &rc); - if (rc.right > rc.bottom) { - pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.y; - } - else { - pt.x = LOWORD(GetMessagePos()) + rc.right / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.x; - } - - OnChange(this); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - } - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - return 0; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CSplitter + +CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl), + m_iPosition(0) +{ +} + +void CSplitter::OnInit() +{ + CSuper::OnInit(); + Subclass(); +} + +LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_NCHITTEST: + return HTCLIENT; + + case WM_SETCURSOR: + RECT rc; + GetClientRect(m_hwnd, &rc); + SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); + return TRUE; + + case WM_LBUTTONDOWN: + SetCapture(m_hwnd); + return 0; + + case WM_MOUSEMOVE: + if (GetCapture() == m_hwnd) { + POINT pt = { 0, 0 }; + GetClientRect(m_hwnd, &rc); + if (rc.right > rc.bottom) { + pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; + ScreenToClient(m_parentWnd->GetHwnd(), &pt); + m_iPosition = pt.y; + } + else { + pt.x = LOWORD(GetMessagePos()) + rc.right / 2; + ScreenToClient(m_parentWnd->GetHwnd(), &pt); + m_iPosition = pt.x; + } + + OnChange(this); + PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); + } + return 0; + + case WM_LBUTTONUP: + ReleaseCapture(); + PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); + return 0; + } + + return CSuper::CustomWndProc(msg, wParam, lParam); +} diff --git a/src/mir_core/src/Linux/CTimer.cpp b/src/mir_core/src/Linux/CTimer.cpp index 5d3c8b01b1..07fc863c60 100644 --- a/src/mir_core/src/Linux/CTimer.cpp +++ b/src/mir_core/src/Linux/CTimer.cpp @@ -1,93 +1,93 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CTimer - -CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) - : m_wnd(wnd), m_idEvent(idEvent) -{ - if (wnd) - wnd->AddTimer(this); -} - -CTimer::~CTimer() -{ - if (m_wnd) - m_wnd->RemoveTimer(m_idEvent); -} - -BOOL CTimer::OnTimer() -{ - OnEvent(this); - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CTimer::Start(int elapse) -{ - // ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); -} - -bool CTimer::Stop() -{ - // return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TStartParam -{ - CTimer *pTimer; - int period; -}; - -static INT_PTR CALLBACK stubStart(void *param) -{ - auto *p = (TStartParam *)param; - // return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); - return 0; -} - -void CTimer::StartSafe(int elapse) -{ - TStartParam param = { this, elapse }; - CallFunctionSync(stubStart, ¶m); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK stubStop(void *param) -{ - auto *p = (CTimer*)param; - // return ::KillTimer(p->GetHwnd(), p->GetEventId()); - return 0; -} - -void CTimer::StopSafe() -{ - CallFunctionSync(stubStop, this); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CTimer + +CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) + : m_wnd(wnd), m_idEvent(idEvent) +{ + if (wnd) + wnd->AddTimer(this); +} + +CTimer::~CTimer() +{ + if (m_wnd) + m_wnd->RemoveTimer(m_idEvent); +} + +BOOL CTimer::OnTimer() +{ + OnEvent(this); + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CTimer::Start(int elapse) +{ + // ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); +} + +bool CTimer::Stop() +{ + // return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct TStartParam +{ + CTimer *pTimer; + int period; +}; + +static INT_PTR CALLBACK stubStart(void *param) +{ + auto *p = (TStartParam *)param; + // return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); + return 0; +} + +void CTimer::StartSafe(int elapse) +{ + TStartParam param = { this, elapse }; + CallFunctionSync(stubStart, ¶m); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static INT_PTR CALLBACK stubStop(void *param) +{ + auto *p = (CTimer*)param; + // return ::KillTimer(p->GetHwnd(), p->GetEventId()); + return 0; +} + +void CTimer::StopSafe() +{ + CallFunctionSync(stubStop, this); +} diff --git a/src/mir_core/src/Linux/cctrldate.cpp b/src/mir_core/src/Linux/cctrldate.cpp index 1967cb5678..253b549870 100644 --- a/src/mir_core/src/Linux/cctrldate.cpp +++ b/src/mir_core/src/Linux/cctrldate.cpp @@ -1,49 +1,49 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlDate class - -CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == DTN_DATETIMECHANGE) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -void CCtrlDate::GetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); -} - -void CCtrlDate::SetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlDate class + +CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : + CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) +{ + if (pnmh->code == DTN_DATETIMECHANGE) { + NotifyChange(); + return TRUE; + } + return FALSE; +} + +void CCtrlDate::GetTime(SYSTEMTIME *pDate) +{ + ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); +} + +void CCtrlDate::SetTime(SYSTEMTIME *pDate) +{ + ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); +} diff --git a/src/mir_core/src/Linux/fileutil.cpp b/src/mir_core/src/Linux/fileutil.cpp index a5002f0a66..fadc70e430 100644 --- a/src/mir_core/src/Linux/fileutil.cpp +++ b/src/mir_core/src/Linux/fileutil.cpp @@ -1,89 +1,89 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../stdafx.h" -#include - -MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode) -{ - return fopen(T2Utf(pwszFileName), T2Utf(pwszMode)); -} - -MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath) -{ - return chdir(T2Utf(pwszPath)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() -{ - // if (ptr != nullptr) { - // if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { - // ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; - // ptr = nullptr; - // } - // } - return *this; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) -{ - // if (pwszPath != nullptr) - // m_hFind = ::FindFirstFileW(pwszPath, &m_data); -} - -MFilePath::MFileIterator::~MFileIterator() -{ - // if (m_hFind != INVALID_HANDLE_VALUE) - // ::FindClose(m_hFind); -} - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() -{ - // if (m_hFind == INVALID_HANDLE_VALUE) - // return MFilePath::MFileIterator::iterator(nullptr); - - return MFilePath::MFileIterator::iterator(this); -} - -bool MFilePath::MFileIterator::isDir() const -{ - //if (m_hFind == INVALID_HANDLE_VALUE) - // return false; - - return (m_flags & 1) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool MFilePath::isExist() const -{ - return ::access(T2Utf(c_str()), 0) == 0; -} - -bool MFilePath::move(const wchar_t *pwszDest) -{ - return ::rename(T2Utf(c_str()), T2Utf(pwszDest)) != 0; -} - -MFilePath::MFileIterator MFilePath::search() -{ - return MFileIterator(c_str()); -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../stdafx.h" +#include + +MIR_CORE_DLL(FILE*) _wfopen(const wchar_t *pwszFileName, const wchar_t *pwszMode) +{ + return fopen(T2Utf(pwszFileName), T2Utf(pwszMode)); +} + +MIR_CORE_DLL(int) _wchdir(const wchar_t *pwszPath) +{ + return chdir(T2Utf(pwszPath)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() +{ + // if (ptr != nullptr) { + // if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { + // ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; + // ptr = nullptr; + // } + // } + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) +{ + // if (pwszPath != nullptr) + // m_hFind = ::FindFirstFileW(pwszPath, &m_data); +} + +MFilePath::MFileIterator::~MFileIterator() +{ + // if (m_hFind != INVALID_HANDLE_VALUE) + // ::FindClose(m_hFind); +} + +MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() +{ + // if (m_hFind == INVALID_HANDLE_VALUE) + // return MFilePath::MFileIterator::iterator(nullptr); + + return MFilePath::MFileIterator::iterator(this); +} + +bool MFilePath::MFileIterator::isDir() const +{ + //if (m_hFind == INVALID_HANDLE_VALUE) + // return false; + + return (m_flags & 1) != 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool MFilePath::isExist() const +{ + return ::access(T2Utf(c_str()), 0) == 0; +} + +bool MFilePath::move(const wchar_t *pwszDest) +{ + return ::rename(T2Utf(c_str()), T2Utf(pwszDest)) != 0; +} + +MFilePath::MFileIterator MFilePath::search() +{ + return MFileIterator(c_str()); +} diff --git a/src/mir_core/src/Linux/strutil.cpp b/src/mir_core/src/Linux/strutil.cpp index a2b854fcc8..8c44a3dd2b 100644 --- a/src/mir_core/src/Linux/strutil.cpp +++ b/src/mir_core/src/Linux/strutil.cpp @@ -1,48 +1,48 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(char*) strlwr(char *str) -{ - for (char *p = str; *p; p++) - *p = tolower(*p); - - return str; -} - -MIR_CORE_DLL(char*) strupr(char *str) -{ - for (char *p = str; *p; p++) - *p = toupper(*p); - - return str; -} - -MIR_CORE_DLL(char*) strrev(char *str) -{ - if (!str || !*str) - return str; - - char *p1, *p2; - for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { - *p1 ^= *p2; - *p2 ^= *p1; - *p1 ^= *p2; - } - return str; -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../stdafx.h" + +MIR_CORE_DLL(char*) strlwr(char *str) +{ + for (char *p = str; *p; p++) + *p = tolower(*p); + + return str; +} + +MIR_CORE_DLL(char*) strupr(char *str) +{ + for (char *p = str; *p; p++) + *p = toupper(*p); + + return str; +} + +MIR_CORE_DLL(char*) strrev(char *str) +{ + if (!str || !*str) + return str; + + char *p1, *p2; + for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { + *p1 ^= *p2; + *p2 ^= *p1; + *p1 ^= *p2; + } + return str; +} diff --git a/src/mir_core/src/Windows/CCtrlBase.cpp b/src/mir_core/src/Windows/CCtrlBase.cpp index 58787b02db..ad80ccffe7 100644 --- a/src/mir_core/src/Windows/CCtrlBase.cpp +++ b/src/mir_core/src/Windows/CCtrlBase.cpp @@ -1,224 +1,224 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static mir_cs csCtrl; - -static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST arControls(10, CompareControls); - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlBase - -CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : - m_parentWnd(wnd), - m_idCtrl(idCtrl) -{ - if (wnd) - wnd->AddControl(this); -} - -CCtrlBase::~CCtrlBase() -{ -} - -void CCtrlBase::OnInit() -{ - m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : nullptr; -} - -void CCtrlBase::OnDestroy() -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = m_hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - pCtrl->Unsubclass(); - arControls.remove(pCtrl); - } - - m_hwnd = nullptr; -} - -bool CCtrlBase::OnApply() -{ - m_bChanged = false; - return true; -} - -void CCtrlBase::OnReset() -{} - -void CCtrlBase::Show(bool bShow) -{ - ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); -} - -void CCtrlBase::Enable(bool bIsEnable) -{ - ::EnableWindow(m_hwnd, bIsEnable); -} - -bool CCtrlBase::Enabled() const -{ - return (m_hwnd) ? IsWindowEnabled(m_hwnd) != 0 : false; -} - -void CCtrlBase::NotifyChange() -{ - if (!m_parentWnd) - return; - - if (m_parentWnd->IsInitialized()) { - m_bChanged = true; - if (!m_bSilent) - m_parentWnd->NotifyChange(); - } - - OnChange(this); -} - -LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const -{ - return ::SendMessage(m_hwnd, Msg, wParam, lParam); -} - -void CCtrlBase::SetText(const wchar_t *text) -{ - ::SetWindowText(m_hwnd, text); -} - -void CCtrlBase::SetTextA(const char *text) -{ - ::SetWindowTextA(m_hwnd, text); -} - -void CCtrlBase::SetDraw(bool bEnable) -{ - ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CCtrlBase::SetInt(int value) -{ - wchar_t buf[32] = { 0 }; - mir_snwprintf(buf, L"%d", value); - SetWindowText(m_hwnd, buf); -} - -wchar_t* CCtrlBase::GetText() const -{ - int length = GetWindowTextLengthW(m_hwnd); - wchar_t *result = (wchar_t *)mir_alloc((length+1) * sizeof(wchar_t)); - if (length) - GetWindowTextW(m_hwnd, result, length+1); - result[length] = 0; - return result; -} - -char* CCtrlBase::GetTextA() const -{ - int length = GetWindowTextLengthA(m_hwnd); - char *result = (char *)mir_alloc((length+1) * sizeof(char)); - if (length) - GetWindowTextA(m_hwnd, result, length+1); - result[length] = 0; - return result; -} - -char* CCtrlBase::GetTextU() const -{ - return mir_utf8encodeW(ptrW(GetText())); -} - -wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const -{ - GetWindowTextW(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextA(char *buf, size_t size) const -{ - GetWindowTextA(m_hwnd, buf, (int)size); - buf[size - 1] = 0; - return buf; -} - -char* CCtrlBase::GetTextU(char *buf, size_t size) const -{ - ptrW wszText(GetText()); - strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); - return buf; -} - -int CCtrlBase::GetInt() const -{ - int length = GetWindowTextLengthW(m_hwnd) + 1; - wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); - GetWindowTextW(m_hwnd, result, length); - return _wtoi(result); -} - -void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - pos.iCurr = -1; - - if (pos.pt.x == 0 && pos.pt.y == 0) - GetCursorPos(&pos.pt); -} - -LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) -{ - return FALSE; -} - -LRESULT CALLBACK CCtrlBase::GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); - if (pCtrl) { - LRESULT res = pCtrl->CustomWndProc(msg, wParam, lParam); - if (res != 0) - return res; - } - - return mir_callNextSubclass(hwnd, GlobalSubclassWndProc, msg, wParam, lParam); -} - -void CCtrlBase::Subclass() -{ - mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); - - mir_cslock lck(csCtrl); - arControls.insert(this); -} - -void CCtrlBase::Unsubclass() -{ - mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +static mir_cs csCtrl; + +static int CompareControls(const CCtrlBase *p1, const CCtrlBase *p2) +{ + return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); +} +static LIST arControls(10, CompareControls); + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlBase + +CCtrlBase::CCtrlBase(CDlgBase *wnd, int idCtrl) : + m_parentWnd(wnd), + m_idCtrl(idCtrl) +{ + if (wnd) + wnd->AddControl(this); +} + +CCtrlBase::~CCtrlBase() +{ +} + +void CCtrlBase::OnInit() +{ + m_hwnd = (m_idCtrl && m_parentWnd && m_parentWnd->GetHwnd()) ? GetDlgItem(m_parentWnd->GetHwnd(), m_idCtrl) : nullptr; +} + +void CCtrlBase::OnDestroy() +{ + PVOID bullshit[2]; // vfptr + hwnd + bullshit[1] = m_hwnd; + CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); + if (pCtrl) { + pCtrl->Unsubclass(); + arControls.remove(pCtrl); + } + + m_hwnd = nullptr; +} + +bool CCtrlBase::OnApply() +{ + m_bChanged = false; + return true; +} + +void CCtrlBase::OnReset() +{} + +void CCtrlBase::Show(bool bShow) +{ + ::ShowWindow(m_hwnd, bShow ? SW_SHOW : SW_HIDE); +} + +void CCtrlBase::Enable(bool bIsEnable) +{ + ::EnableWindow(m_hwnd, bIsEnable); +} + +bool CCtrlBase::Enabled() const +{ + return (m_hwnd) ? IsWindowEnabled(m_hwnd) != 0 : false; +} + +void CCtrlBase::NotifyChange() +{ + if (!m_parentWnd) + return; + + if (m_parentWnd->IsInitialized()) { + m_bChanged = true; + if (!m_bSilent) + m_parentWnd->NotifyChange(); + } + + OnChange(this); +} + +LRESULT CCtrlBase::SendMsg(UINT Msg, WPARAM wParam, LPARAM lParam) const +{ + return ::SendMessage(m_hwnd, Msg, wParam, lParam); +} + +void CCtrlBase::SetText(const wchar_t *text) +{ + ::SetWindowText(m_hwnd, text); +} + +void CCtrlBase::SetTextA(const char *text) +{ + ::SetWindowTextA(m_hwnd, text); +} + +void CCtrlBase::SetDraw(bool bEnable) +{ + ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); +} + +void CCtrlBase::SetInt(int value) +{ + wchar_t buf[32] = { 0 }; + mir_snwprintf(buf, L"%d", value); + SetWindowText(m_hwnd, buf); +} + +wchar_t* CCtrlBase::GetText() const +{ + int length = GetWindowTextLengthW(m_hwnd); + wchar_t *result = (wchar_t *)mir_alloc((length+1) * sizeof(wchar_t)); + if (length) + GetWindowTextW(m_hwnd, result, length+1); + result[length] = 0; + return result; +} + +char* CCtrlBase::GetTextA() const +{ + int length = GetWindowTextLengthA(m_hwnd); + char *result = (char *)mir_alloc((length+1) * sizeof(char)); + if (length) + GetWindowTextA(m_hwnd, result, length+1); + result[length] = 0; + return result; +} + +char* CCtrlBase::GetTextU() const +{ + return mir_utf8encodeW(ptrW(GetText())); +} + +wchar_t* CCtrlBase::GetText(wchar_t *buf, size_t size) const +{ + GetWindowTextW(m_hwnd, buf, (int)size); + buf[size - 1] = 0; + return buf; +} + +char* CCtrlBase::GetTextA(char *buf, size_t size) const +{ + GetWindowTextA(m_hwnd, buf, (int)size); + buf[size - 1] = 0; + return buf; +} + +char* CCtrlBase::GetTextU(char *buf, size_t size) const +{ + ptrW wszText(GetText()); + strncpy_s(buf, size, T2Utf(wszText), _TRUNCATE); + return buf; +} + +int CCtrlBase::GetInt() const +{ + int length = GetWindowTextLengthW(m_hwnd) + 1; + wchar_t *result = (wchar_t *)_alloca(length * sizeof(wchar_t)); + GetWindowTextW(m_hwnd, result, length); + return _wtoi(result); +} + +void CCtrlBase::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + pos.iCurr = -1; + + if (pos.pt.x == 0 && pos.pt.y == 0) + GetCursorPos(&pos.pt); +} + +LRESULT CCtrlBase::CustomWndProc(UINT, WPARAM, LPARAM) +{ + return FALSE; +} + +LRESULT CALLBACK CCtrlBase::GlobalSubclassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PVOID bullshit[2]; // vfptr + hwnd + bullshit[1] = hwnd; + CCtrlBase *pCtrl = arControls.find((CCtrlBase*)&bullshit); + if (pCtrl) { + LRESULT res = pCtrl->CustomWndProc(msg, wParam, lParam); + if (res != 0) + return res; + } + + return mir_callNextSubclass(hwnd, GlobalSubclassWndProc, msg, wParam, lParam); +} + +void CCtrlBase::Subclass() +{ + mir_subclassWindow(m_hwnd, GlobalSubclassWndProc); + + mir_cslock lck(csCtrl); + arControls.insert(this); +} + +void CCtrlBase::Unsubclass() +{ + mir_unsubclassWindow(m_hwnd, GlobalSubclassWndProc); +} diff --git a/src/mir_core/src/Windows/CCtrlButton.cpp b/src/mir_core/src/Windows/CCtrlButton.cpp index bf84cad74f..b39e855332 100644 --- a/src/mir_core/src/Windows/CCtrlButton.cpp +++ b/src/mir_core/src/Windows/CCtrlButton.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlButton - -CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - -BOOL CCtrlButton::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == BN_CLICKED) - OnClick(this); - return FALSE; -} - -void CCtrlButton::Click() -{ - if (Enabled()) - ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); -} - -bool CCtrlButton::IsPushed() const -{ - return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; -} - -void CCtrlButton::Push(bool bPushed) -{ - if (Enabled()) - ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlButton + +CCtrlButton::CCtrlButton(CDlgBase* wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl) +{} + +BOOL CCtrlButton::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + if (idCode == BN_CLICKED) + OnClick(this); + return FALSE; +} + +void CCtrlButton::Click() +{ + if (Enabled()) + ::SendMessage(m_parentWnd->GetHwnd(), WM_COMMAND, MAKELONG(m_idCtrl, BN_CLICKED), 0); +} + +bool CCtrlButton::IsPushed() const +{ + return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; +} + +void CCtrlButton::Push(bool bPushed) +{ + if (Enabled()) + ::SendMessage(m_hwnd, BM_SETCHECK, (bPushed) ? BST_CHECKED : BST_UNCHECKED, 0); +} diff --git a/src/mir_core/src/Windows/CCtrlCheck.cpp b/src/mir_core/src/Windows/CCtrlCheck.cpp index 9c1281e8b8..3258dce27f 100644 --- a/src/mir_core/src/Windows/CCtrlCheck.cpp +++ b/src/mir_core/src/Windows/CCtrlCheck.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCheck class - -CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{ - m_bNotifiable = true; -} - -BOOL CCtrlCheck::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlCheck::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetState()); - return true; -} - -void CCtrlCheck::OnReset() -{ - if (m_dbLink != nullptr) - SetState(LoadInt()); -} - -int CCtrlCheck::GetState() const -{ - return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); -} - -void CCtrlCheck::SetState(int state) -{ - ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); -} - -bool CCtrlCheck::IsChecked() -{ - return GetState() == BST_CHECKED; -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlCheck class + +CCtrlCheck::CCtrlCheck(CDlgBase *dlg, int ctrlId) + : CCtrlData(dlg, ctrlId) +{ + m_bNotifiable = true; +} + +BOOL CCtrlCheck::OnCommand(HWND, uint16_t, uint16_t) +{ + NotifyChange(); + return TRUE; +} + +bool CCtrlCheck::OnApply() +{ + CSuper::OnApply(); + + if (m_dbLink != nullptr) + SaveInt(GetState()); + return true; +} + +void CCtrlCheck::OnReset() +{ + if (m_dbLink != nullptr) + SetState(LoadInt()); +} + +int CCtrlCheck::GetState() const +{ + return ::SendMessage(m_hwnd, BM_GETCHECK, 0, 0); +} + +void CCtrlCheck::SetState(int state) +{ + ::SendMessage(m_hwnd, BM_SETCHECK, state, 0); +} + +bool CCtrlCheck::IsChecked() +{ + return GetState() == BST_CHECKED; +} diff --git a/src/mir_core/src/Windows/CCtrlClc.cpp b/src/mir_core/src/Windows/CCtrlClc.cpp index 3e208679d4..14e88335a0 100644 --- a/src/mir_core/src/Windows/CCtrlClc.cpp +++ b/src/mir_core/src/Windows/CCtrlClc.cpp @@ -1,211 +1,211 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlClc - -CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; - switch (pnmh->code) { - case CLN_EXPANDED: OnExpanded(&evt); break; - case CLN_LISTREBUILT: OnListRebuilt(&evt); break; - case CLN_ITEMCHECKED: OnItemChecked(&evt); break; - case CLN_DRAGGING: OnDragging(&evt); break; - case CLN_DROPPED: OnDropped(&evt); break; - case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; - case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; - case CLN_DRAGSTOP: OnDragStop(&evt); break; - case CLN_NEWCONTACT: OnNewContact(&evt); break; - case CLN_CONTACTMOVED: OnContactMoved(&evt); break; - case NM_CLICK: OnClick(&evt); break; - - case CLN_CHECKCHANGED: - OnCheckChanged(&evt); - NotifyChange(); - break; - } - return FALSE; -} - -void CCtrlClc::AddContact(MCONTACT hContact) -{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); -} - -void CCtrlClc::AddGroup(HANDLE hGroup) -{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); -} - -void CCtrlClc::AutoRebuild() -{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); -} - -void CCtrlClc::DeleteItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::EditLabel(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); -} - -void CCtrlClc::EndEditLabel(bool save) -{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); -} - -void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) -{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); -} - -void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) -{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); -} - -HANDLE CCtrlClc::FindContact(MCONTACT hContact) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); -} - -HANDLE CCtrlClc::FindGroup(MGROUP hGroup) -{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); -} - -COLORREF CCtrlClc::GetBkColor() const -{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); -} - -bool CCtrlClc::GetCheck(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; -} - -int CCtrlClc::GetCount() const -{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); -} - -HWND CCtrlClc::GetEditControl() const -{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); -} - -uint32_t CCtrlClc::GetExpand(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); -} - -int CCtrlClc::GetExtraColumns() const -{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); -} - -uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const -{ - return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); -} - -HIMAGELIST CCtrlClc::GetExtraImageList() const -{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); -} - -HFONT CCtrlClc::GetFont(int iFontId) const -{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); -} - -HANDLE CCtrlClc::GetSelection() const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); -} - -HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); -} - -void CCtrlClc::SelectItem(HANDLE hItem) -{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); -} - -void CCtrlClc::SetBkColor(COLORREF clBack) -{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); -} - -void CCtrlClc::SetCheck(HANDLE hItem, bool check) -{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); -} - -void CCtrlClc::SetExtraColumns(int iColumns) -{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); -} - -void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); -} - -void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) -{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); -} - -void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) -{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); -} - -void CCtrlClc::SetItemText(HANDLE hItem, char *szText) -{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); -} - -void CCtrlClc::SetHideEmptyGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); -} - -bool CCtrlClc::GetHideOfflineRoot() const -{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; -} - -void CCtrlClc::SetHideOfflineRoot(bool state) -{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); -} - -void CCtrlClc::SetUseGroups(bool state) -{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); -} - -void CCtrlClc::SetOfflineModes(uint32_t modes) -{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); -} - -uint32_t CCtrlClc::GetExStyle() const -{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); -} - -void CCtrlClc::SetExStyle(uint32_t exStyle) -{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); -} - -HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) -{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); -} - -int CCtrlClc::GetItemType(HANDLE hItem) const -{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); -} - -HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const -{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlClc + +CCtrlClc::CCtrlClc(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId) +{} + +BOOL CCtrlClc::OnNotify(int, NMHDR *pnmh) +{ + TEventInfo evt = { this, (NMCLISTCONTROL *)pnmh }; + switch (pnmh->code) { + case CLN_EXPANDED: OnExpanded(&evt); break; + case CLN_LISTREBUILT: OnListRebuilt(&evt); break; + case CLN_ITEMCHECKED: OnItemChecked(&evt); break; + case CLN_DRAGGING: OnDragging(&evt); break; + case CLN_DROPPED: OnDropped(&evt); break; + case CLN_LISTSIZECHANGE: OnListSizeChange(&evt); break; + case CLN_OPTIONSCHANGED: OnOptionsChanged(&evt); break; + case CLN_DRAGSTOP: OnDragStop(&evt); break; + case CLN_NEWCONTACT: OnNewContact(&evt); break; + case CLN_CONTACTMOVED: OnContactMoved(&evt); break; + case NM_CLICK: OnClick(&evt); break; + + case CLN_CHECKCHANGED: + OnCheckChanged(&evt); + NotifyChange(); + break; + } + return FALSE; +} + +void CCtrlClc::AddContact(MCONTACT hContact) +{ SendMessage(m_hwnd, CLM_ADDCONTACT, hContact, 0); +} + +void CCtrlClc::AddGroup(HANDLE hGroup) +{ SendMessage(m_hwnd, CLM_ADDGROUP, (WPARAM)hGroup, 0); +} + +void CCtrlClc::AutoRebuild() +{ SendMessage(m_hwnd, CLM_AUTOREBUILD, 0, 0); +} + +void CCtrlClc::DeleteItem(HANDLE hItem) +{ SendMessage(m_hwnd, CLM_DELETEITEM, (WPARAM)hItem, 0); +} + +void CCtrlClc::EditLabel(HANDLE hItem) +{ SendMessage(m_hwnd, CLM_EDITLABEL, (WPARAM)hItem, 0); +} + +void CCtrlClc::EndEditLabel(bool save) +{ SendMessage(m_hwnd, CLM_ENDEDITLABELNOW, save ? 0 : 1, 0); +} + +void CCtrlClc::EnsureVisible(HANDLE hItem, bool partialOk) +{ SendMessage(m_hwnd, CLM_ENSUREVISIBLE, (WPARAM)hItem, partialOk ? TRUE : FALSE); +} + +void CCtrlClc::Expand(HANDLE hItem, uint32_t flags) +{ SendMessage(m_hwnd, CLM_EXPAND, (WPARAM)hItem, flags); +} + +HANDLE CCtrlClc::FindContact(MCONTACT hContact) +{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDCONTACT, hContact, 0); +} + +HANDLE CCtrlClc::FindGroup(MGROUP hGroup) +{ return (HANDLE)SendMessage(m_hwnd, CLM_FINDGROUP, hGroup, 0); +} + +COLORREF CCtrlClc::GetBkColor() const +{ return (COLORREF)SendMessage(m_hwnd, CLM_GETBKCOLOR, 0, 0); +} + +bool CCtrlClc::GetCheck(HANDLE hItem) const +{ return SendMessage(m_hwnd, CLM_GETCHECKMARK, (WPARAM)hItem, 0) ? true : false; +} + +int CCtrlClc::GetCount() const +{ return SendMessage(m_hwnd, CLM_GETCOUNT, 0, 0); +} + +HWND CCtrlClc::GetEditControl() const +{ return (HWND)SendMessage(m_hwnd, CLM_GETEDITCONTROL, 0, 0); +} + +uint32_t CCtrlClc::GetExpand(HANDLE hItem) const +{ return SendMessage(m_hwnd, CLM_GETEXPAND, (WPARAM)hItem, 0); +} + +int CCtrlClc::GetExtraColumns() const +{ return SendMessage(m_hwnd, CLM_GETEXTRACOLUMNS, 0, 0); +} + +uint8_t CCtrlClc::GetExtraImage(HANDLE hItem, int iColumn) const +{ + return (uint8_t)(SendMessage(m_hwnd, CLM_GETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, 0)) & 0xFFFF); +} + +HIMAGELIST CCtrlClc::GetExtraImageList() const +{ return (HIMAGELIST)SendMessage(m_hwnd, CLM_GETEXTRAIMAGELIST, 0, 0); +} + +HFONT CCtrlClc::GetFont(int iFontId) const +{ return (HFONT)SendMessage(m_hwnd, CLM_GETFONT, (WPARAM)iFontId, 0); +} + +HANDLE CCtrlClc::GetSelection() const +{ return (HANDLE)SendMessage(m_hwnd, CLM_GETSELECTION, 0, 0); +} + +HANDLE CCtrlClc::HitTest(int x, int y, uint32_t *hitTest) const +{ return (HANDLE)SendMessage(m_hwnd, CLM_HITTEST, (WPARAM)hitTest, MAKELPARAM(x,y)); +} + +void CCtrlClc::SelectItem(HANDLE hItem) +{ SendMessage(m_hwnd, CLM_SELECTITEM, (WPARAM)hItem, 0); +} + +void CCtrlClc::SetBkColor(COLORREF clBack) +{ SendMessage(m_hwnd, CLM_SETBKCOLOR, (WPARAM)clBack, 0); +} + +void CCtrlClc::SetCheck(HANDLE hItem, bool check) +{ SendMessage(m_hwnd, CLM_SETCHECKMARK, (WPARAM)hItem, check ? 1 : 0); +} + +void CCtrlClc::SetExtraColumns(int iColumns) +{ SendMessage(m_hwnd, CLM_SETEXTRACOLUMNS, (WPARAM)iColumns, 0); +} + +void CCtrlClc::SetExtraImage(HANDLE hItem, int iColumn, int iImage) +{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGE, (WPARAM)hItem, MAKELPARAM(iColumn, iImage)); +} + +void CCtrlClc::SetExtraImageList(HIMAGELIST hImgList) +{ SendMessage(m_hwnd, CLM_SETEXTRAIMAGELIST, 0, (LPARAM)hImgList); +} + +void CCtrlClc::SetFont(int iFontId, HANDLE hFont, bool bRedraw) +{ SendMessage(m_hwnd, CLM_SETFONT, (WPARAM)hFont, MAKELPARAM(bRedraw ? 1 : 0, iFontId)); +} + +void CCtrlClc::SetItemText(HANDLE hItem, char *szText) +{ SendMessage(m_hwnd, CLM_SETITEMTEXT, (WPARAM)hItem, (LPARAM)szText); +} + +void CCtrlClc::SetHideEmptyGroups(bool state) +{ SendMessage(m_hwnd, CLM_SETHIDEEMPTYGROUPS, state ? 1 : 0, 0); +} + +bool CCtrlClc::GetHideOfflineRoot() const +{ return SendMessage(m_hwnd, CLM_GETHIDEOFFLINEROOT, 0, 0) ? true : false; +} + +void CCtrlClc::SetHideOfflineRoot(bool state) +{ SendMessage(m_hwnd, CLM_SETHIDEOFFLINEROOT, state ? 1 : 0, 9); +} + +void CCtrlClc::SetUseGroups(bool state) +{ SendMessage(m_hwnd, CLM_SETUSEGROUPS, state ? 1 : 0, 0); +} + +void CCtrlClc::SetOfflineModes(uint32_t modes) +{ SendMessage(m_hwnd, CLM_SETOFFLINEMODES, modes, 0); +} + +uint32_t CCtrlClc::GetExStyle() const +{ return SendMessage(m_hwnd, CLM_GETEXSTYLE, 0, 0); +} + +void CCtrlClc::SetExStyle(uint32_t exStyle) +{ SendMessage(m_hwnd, CLM_SETEXSTYLE, (WPARAM)exStyle, 0); +} + +HANDLE CCtrlClc::AddInfoItem(CLCINFOITEM *cii) +{ return (HANDLE)SendMessage(m_hwnd, CLM_ADDINFOITEM, 0, (LPARAM)cii); +} + +int CCtrlClc::GetItemType(HANDLE hItem) const +{ return SendMessage(m_hwnd, CLM_GETITEMTYPE, (WPARAM)hItem, 0); +} + +HANDLE CCtrlClc::GetNextItem(HANDLE hItem, uint32_t flags) const +{ return (HANDLE)SendMessage(m_hwnd, CLM_GETNEXTITEM, (WPARAM)flags, (LPARAM)hItem); +} diff --git a/src/mir_core/src/Windows/CCtrlColor.cpp b/src/mir_core/src/Windows/CCtrlColor.cpp index 97f5e48e02..b61fb5760a 100644 --- a/src/mir_core/src/Windows/CCtrlColor.cpp +++ b/src/mir_core/src/Windows/CCtrlColor.cpp @@ -1,61 +1,61 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlColor class - -CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) -{ - NotifyChange(); - return TRUE; -} - -bool CCtrlColor::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetColor()); - return true; -} - -void CCtrlColor::OnReset() -{ - if (m_dbLink != nullptr) - SetColor(LoadInt()); -} - -uint32_t CCtrlColor::GetColor() -{ - return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); -} - -void CCtrlColor::SetColor(uint32_t dwValue) -{ - ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlColor class + +CCtrlColor::CCtrlColor(CDlgBase *dlg, int ctrlId) : + CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlColor::OnCommand(HWND, uint16_t, uint16_t) +{ + NotifyChange(); + return TRUE; +} + +bool CCtrlColor::OnApply() +{ + CSuper::OnApply(); + + if (m_dbLink != nullptr) + SaveInt(GetColor()); + return true; +} + +void CCtrlColor::OnReset() +{ + if (m_dbLink != nullptr) + SetColor(LoadInt()); +} + +uint32_t CCtrlColor::GetColor() +{ + return ::SendMessage(m_hwnd, CPM_GETCOLOUR, 0, 0); +} + +void CCtrlColor::SetColor(uint32_t dwValue) +{ + ::SendMessage(m_hwnd, CPM_SETCOLOUR, 0, dwValue); +} diff --git a/src/mir_core/src/Windows/CCtrlCombo.cpp b/src/mir_core/src/Windows/CCtrlCombo.cpp index 83cd721494..b935129b3f 100644 --- a/src/mir_core/src/Windows/CCtrlCombo.cpp +++ b/src/mir_core/src/Windows/CCtrlCombo.cpp @@ -1,185 +1,185 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlCombo class - -CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlCombo::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case CBN_CLOSEUP: OnCloseup(this); break; - case CBN_DROPDOWN: OnDropdown(this); break; - case CBN_SELCHANGE: OnSelChanged(this); break; - case CBN_KILLFOCUS: OnKillFocus(this); break; - - case CBN_EDITCHANGE: - case CBN_EDITUPDATE: - case CBN_SELENDOK: - NotifyChange(); - break; - } - return TRUE; -} - -void CCtrlCombo::OnInit() -{ - CSuper::OnInit(); - OnReset(); -} - -bool CCtrlCombo::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - int len = GetWindowTextLength(m_hwnd) + 1; - wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - GetWindowText(m_hwnd, buf, len); - SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlCombo::OnReset() -{ - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -LPARAM CCtrlCombo::GetCurData() const -{ - return GetItemData(GetCurSel()); -} - -// selects line with userdata passed -int CCtrlCombo::SelectData(LPARAM data) -{ - int ret = -1, nCount = GetCount(); - - for (int i = 0; i < nCount; i++) - if (GetItemData(i) == data) { - ret = i; - break; - } - - return SetCurSel(ret); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Windows API - -int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); - if (data) - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -int CCtrlCombo::AddStringA(const char *text, LPARAM data) -{ - int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); - if (data) - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -void CCtrlCombo::DeleteString(int index) -{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0); -} - -int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlCombo::FindStringA(const char *str, int index, bool exact) -{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlCombo::GetCount() const -{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0); -} - -int CCtrlCombo::GetCurSel() const -{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0); -} - -bool CCtrlCombo::GetDroppedState() const -{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false; -} - -LPARAM CCtrlCombo::GetItemData(int index) const -{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0); -} - -wchar_t* CCtrlCombo::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text); - SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); - return iItem; -} - -void CCtrlCombo::ResetContent() -{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0); -} - -int CCtrlCombo::SelectString(const wchar_t *str) -{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str); -} - -int CCtrlCombo::SetCurSel(int index) -{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0); -} - -void CCtrlCombo::SetItemData(int index, LPARAM data) -{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data); -} - -void CCtrlCombo::ShowDropdown(bool show) -{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlCombo class + +CCtrlCombo::CCtrlCombo(CDlgBase *dlg, int ctrlId) + : CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlCombo::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + switch (idCode) { + case CBN_CLOSEUP: OnCloseup(this); break; + case CBN_DROPDOWN: OnDropdown(this); break; + case CBN_SELCHANGE: OnSelChanged(this); break; + case CBN_KILLFOCUS: OnKillFocus(this); break; + + case CBN_EDITCHANGE: + case CBN_EDITUPDATE: + case CBN_SELENDOK: + NotifyChange(); + break; + } + return TRUE; +} + +void CCtrlCombo::OnInit() +{ + CSuper::OnInit(); + OnReset(); +} + +bool CCtrlCombo::OnApply() +{ + CSuper::OnApply(); + + if (GetDataType() == DBVT_WCHAR) { + int len = GetWindowTextLength(m_hwnd) + 1; + wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); + GetWindowText(m_hwnd, buf, len); + SaveText(buf); + } + else if (GetDataType() != DBVT_DELETED) { + SaveInt(GetInt()); + } + return true; +} + +void CCtrlCombo::OnReset() +{ + if (GetDataType() == DBVT_WCHAR) + SetText(LoadText()); + else if (GetDataType() != DBVT_DELETED) + SetInt(LoadInt()); +} + +LPARAM CCtrlCombo::GetCurData() const +{ + return GetItemData(GetCurSel()); +} + +// selects line with userdata passed +int CCtrlCombo::SelectData(LPARAM data) +{ + int ret = -1, nCount = GetCount(); + + for (int i = 0; i < nCount; i++) + if (GetItemData(i) == data) { + ret = i; + break; + } + + return SetCurSel(ret); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Windows API + +int CCtrlCombo::AddString(const wchar_t *text, LPARAM data) +{ + int iItem = SendMessage(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); + if (data) + SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); + return iItem; +} + +int CCtrlCombo::AddStringA(const char *text, LPARAM data) +{ + int iItem = SendMessageA(m_hwnd, CB_ADDSTRING, 0, (LPARAM)text); + if (data) + SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); + return iItem; +} + +void CCtrlCombo::DeleteString(int index) +{ SendMessage(m_hwnd, CB_DELETESTRING, index, 0); +} + +int CCtrlCombo::FindString(const wchar_t *str, int index, bool exact) +{ return SendMessage(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); +} + +int CCtrlCombo::FindStringA(const char *str, int index, bool exact) +{ return SendMessageA(m_hwnd, exact?CB_FINDSTRINGEXACT:CB_FINDSTRING, index, (LPARAM)str); +} + +int CCtrlCombo::GetCount() const +{ return SendMessage(m_hwnd, CB_GETCOUNT, 0, 0); +} + +int CCtrlCombo::GetCurSel() const +{ return SendMessage(m_hwnd, CB_GETCURSEL, 0, 0); +} + +bool CCtrlCombo::GetDroppedState() const +{ return SendMessage(m_hwnd, CB_GETDROPPEDSTATE, 0, 0) ? true : false; +} + +LPARAM CCtrlCombo::GetItemData(int index) const +{ return SendMessage(m_hwnd, CB_GETITEMDATA, index, 0); +} + +wchar_t* CCtrlCombo::GetItemText(int index) const +{ + wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); + SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); + return result; +} + +wchar_t* CCtrlCombo::GetItemText(int index, wchar_t *buf, int size) const +{ + wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, CB_GETLBTEXTLEN, index, 0) + 1)); + SendMessage(m_hwnd, CB_GETLBTEXT, index, (LPARAM)result); + mir_wstrncpy(buf, result, size); + return buf; +} + +int CCtrlCombo::InsertString(const wchar_t *text, int pos, LPARAM data) +{ + int iItem = SendMessage(m_hwnd, CB_INSERTSTRING, pos, (LPARAM)text); + SendMessage(m_hwnd, CB_SETITEMDATA, iItem, data); + return iItem; +} + +void CCtrlCombo::ResetContent() +{ SendMessage(m_hwnd, CB_RESETCONTENT, 0, 0); +} + +int CCtrlCombo::SelectString(const wchar_t *str) +{ return SendMessage(m_hwnd, CB_SELECTSTRING, 0, (LPARAM)str); +} + +int CCtrlCombo::SetCurSel(int index) +{ return SendMessage(m_hwnd, CB_SETCURSEL, index, 0); +} + +void CCtrlCombo::SetItemData(int index, LPARAM data) +{ SendMessage(m_hwnd, CB_SETITEMDATA, index, data); +} + +void CCtrlCombo::ShowDropdown(bool show) +{ SendMessage(m_hwnd, CB_SHOWDROPDOWN, show ? TRUE : FALSE, 0); +} diff --git a/src/mir_core/src/Windows/CCtrlData.cpp b/src/mir_core/src/Windows/CCtrlData.cpp index fab5ca0409..2a60d9d31a 100644 --- a/src/mir_core/src/Windows/CCtrlData.cpp +++ b/src/mir_core/src/Windows/CCtrlData.cpp @@ -1,52 +1,52 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlData class - -CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_dbLink(nullptr) -{} - -CCtrlData::~CCtrlData() -{ - delete m_dbLink; -} - -void CCtrlData::OnInit() -{ - CCtrlBase::OnInit(); - OnReset(); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); -} - -void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) -{ - m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlData class + +CCtrlData::CCtrlData(CDlgBase *wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl), + m_dbLink(nullptr) +{} + +CCtrlData::~CCtrlData() +{ + delete m_dbLink; +} + +void CCtrlData::OnInit() +{ + CCtrlBase::OnInit(); + OnReset(); +} + +void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, uint8_t type, uint32_t iValue) +{ + m_dbLink = new CDbLink(szModuleName, szSetting, type, iValue); +} + +void CCtrlData::CreateDbLink(const char* szModuleName, const char* szSetting, wchar_t* szValue) +{ + m_dbLink = new CDbLink(szModuleName, szSetting, DBVT_WCHAR, szValue); +} diff --git a/src/mir_core/src/Windows/CCtrlDate.cpp b/src/mir_core/src/Windows/CCtrlDate.cpp index 1967cb5678..253b549870 100644 --- a/src/mir_core/src/Windows/CCtrlDate.cpp +++ b/src/mir_core/src/Windows/CCtrlDate.cpp @@ -1,49 +1,49 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlDate class - -CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : - CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == DTN_DATETIMECHANGE) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -void CCtrlDate::GetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); -} - -void CCtrlDate::SetTime(SYSTEMTIME *pDate) -{ - ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlDate class + +CCtrlDate::CCtrlDate(CDlgBase *dlg, int ctrlId) : + CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlDate::OnNotify(int, NMHDR *pnmh) +{ + if (pnmh->code == DTN_DATETIMECHANGE) { + NotifyChange(); + return TRUE; + } + return FALSE; +} + +void CCtrlDate::GetTime(SYSTEMTIME *pDate) +{ + ::SendMessage(m_hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM)pDate); +} + +void CCtrlDate::SetTime(SYSTEMTIME *pDate) +{ + ::SendMessage(m_hwnd, DTM_SETSYSTEMTIME, 0, (LPARAM)pDate); +} diff --git a/src/mir_core/src/Windows/CCtrlEdit.cpp b/src/mir_core/src/Windows/CCtrlEdit.cpp index f035b5b14a..dd5bd2927e 100644 --- a/src/mir_core/src/Windows/CCtrlEdit.cpp +++ b/src/mir_core/src/Windows/CCtrlEdit.cpp @@ -1,68 +1,68 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlEdit class - -CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) - : CCtrlData(dlg, ctrlId) -{} - -BOOL CCtrlEdit::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == EN_CHANGE) - NotifyChange(); - return TRUE; -} - -bool CCtrlEdit::OnApply() -{ - CSuper::OnApply(); - - if (GetDataType() == DBVT_WCHAR) { - int len = GetWindowTextLength(m_hwnd) + 1; - wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); - GetWindowText(m_hwnd, buf, len); - SaveText(buf); - } - else if (GetDataType() != DBVT_DELETED) { - SaveInt(GetInt()); - } - return true; -} - -void CCtrlEdit::OnReset() -{ - m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; - - if (GetDataType() == DBVT_WCHAR) - SetText(LoadText()); - else if (GetDataType() != DBVT_DELETED) - SetInt(LoadInt()); -} - -void CCtrlEdit::SetMaxLength(unsigned int len) -{ - SendMsg(EM_SETLIMITTEXT, len, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlEdit class + +CCtrlEdit::CCtrlEdit(CDlgBase *dlg, int ctrlId) + : CCtrlData(dlg, ctrlId) +{} + +BOOL CCtrlEdit::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + if (idCode == EN_CHANGE) + NotifyChange(); + return TRUE; +} + +bool CCtrlEdit::OnApply() +{ + CSuper::OnApply(); + + if (GetDataType() == DBVT_WCHAR) { + int len = GetWindowTextLength(m_hwnd) + 1; + wchar_t *buf = (wchar_t *)_alloca(sizeof(wchar_t) * len); + GetWindowText(m_hwnd, buf, len); + SaveText(buf); + } + else if (GetDataType() != DBVT_DELETED) { + SaveInt(GetInt()); + } + return true; +} + +void CCtrlEdit::OnReset() +{ + m_bSilent = (GetWindowLong(m_hwnd, GWL_STYLE) & ES_READONLY) != 0; + + if (GetDataType() == DBVT_WCHAR) + SetText(LoadText()); + else if (GetDataType() != DBVT_DELETED) + SetInt(LoadInt()); +} + +void CCtrlEdit::SetMaxLength(unsigned int len) +{ + SendMsg(EM_SETLIMITTEXT, len, 0); +} diff --git a/src/mir_core/src/Windows/CCtrlHyperlink.cpp b/src/mir_core/src/Windows/CCtrlHyperlink.cpp index ca92d19dbc..0e0d93a689 100644 --- a/src/mir_core/src/Windows/CCtrlHyperlink.cpp +++ b/src/mir_core/src/Windows/CCtrlHyperlink.cpp @@ -1,54 +1,54 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlHyperlink - -CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) - : CCtrlBase(wnd, idCtrl), - m_url(url) -{ - OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); -} - -BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) -{ - OnClick(this); - return FALSE; -} - -void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) -{ - ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); -} - -void CCtrlHyperlink::SetUrl(const char *url) -{ - m_url = url; -} - -const char* CCtrlHyperlink::GetUrl() -{ - return m_url; -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlHyperlink + +CCtrlHyperlink::CCtrlHyperlink(CDlgBase* wnd, int idCtrl, const char* url) + : CCtrlBase(wnd, idCtrl), + m_url(url) +{ + OnClick = Callback(this, &CCtrlHyperlink::Default_OnClick); +} + +BOOL CCtrlHyperlink::OnCommand(HWND, uint16_t, uint16_t) +{ + OnClick(this); + return FALSE; +} + +void CCtrlHyperlink::Default_OnClick(CCtrlHyperlink*) +{ + ShellExecuteA(m_hwnd, "open", m_url, "", "", SW_SHOW); +} + +void CCtrlHyperlink::SetUrl(const char *url) +{ + m_url = url; +} + +const char* CCtrlHyperlink::GetUrl() +{ + return m_url; +} diff --git a/src/mir_core/src/Windows/CCtrlLabel.cpp b/src/mir_core/src/Windows/CCtrlLabel.cpp index fd460fdf8b..8e1de5e33f 100644 --- a/src/mir_core/src/Windows/CCtrlLabel.cpp +++ b/src/mir_core/src/Windows/CCtrlLabel.cpp @@ -1,31 +1,31 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlLabel - -CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{} - +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlLabel + +CCtrlLabel::CCtrlLabel(CDlgBase* wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl) +{} + diff --git a/src/mir_core/src/Windows/CCtrlListBox.cpp b/src/mir_core/src/Windows/CCtrlListBox.cpp index abaa31a786..6d9f58fc23 100644 --- a/src/mir_core/src/Windows/CCtrlListBox.cpp +++ b/src/mir_core/src/Windows/CCtrlListBox.cpp @@ -1,160 +1,160 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListBox class - -CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - switch (idCode) { - case LBN_DBLCLK: OnDblClick(this); break; - case LBN_SELCANCEL: OnSelCancel(this); break; - case LBN_SELCHANGE: OnSelChange(this); break; - } - return TRUE; -} - -void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetCurSel(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - - CSuper::GetCaretPos(pos); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) -{ - int iItem = ListBox_AddString(m_hwnd, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::DeleteString(int index) -{ ListBox_DeleteString(m_hwnd, index); -} - -int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) -{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); -} - -int CCtrlListBox::GetCount() const -{ return ListBox_GetCount(m_hwnd); -} - -int CCtrlListBox::GetCurSel() const -{ return ListBox_GetCurSel(m_hwnd); -} - -LPARAM CCtrlListBox::GetItemData(int index) const -{ return ListBox_GetItemData(m_hwnd, index); -} - -int CCtrlListBox::GetItemRect(int index, RECT *pResult) const -{ return ListBox_GetItemRect(m_hwnd, index, pResult); -} - -wchar_t* CCtrlListBox::GetItemText(int index) const -{ - wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - return result; -} - -wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const -{ - wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); - SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); - mir_wstrncpy(buf, result, size); - return buf; -} - -bool CCtrlListBox::GetSel(int index) const -{ return ListBox_GetSel(m_hwnd, index) ? true : false; -} - -int CCtrlListBox::GetSelCount() const -{ return ListBox_GetSelCount(m_hwnd); -} - -int* CCtrlListBox::GetSelItems(int *items, int count) const -{ - ListBox_GetSelItems(m_hwnd, count, items); - return items; -} - -int* CCtrlListBox::GetSelItems() const -{ - int count = GetSelCount() + 1; - int *result = (int *)mir_alloc(sizeof(int) * count); - ListBox_GetSelItems(m_hwnd, count, result); - result[count-1] = -1; - return result; -} - -int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) -{ - int iItem = ListBox_InsertString(m_hwnd, pos, text); - ListBox_SetItemData(m_hwnd, iItem, data); - return iItem; -} - -void CCtrlListBox::ResetContent() -{ ListBox_ResetContent(m_hwnd); -} - -int CCtrlListBox::SelectString(const wchar_t *str) -{ return ListBox_SelectString(m_hwnd, 0, str); -} - -int CCtrlListBox::SetCurSel(int index) -{ return ListBox_SetCurSel(m_hwnd, index); -} - -void CCtrlListBox::SetItemData(int index, LPARAM data) -{ ListBox_SetItemData(m_hwnd, index, data); -} - -void CCtrlListBox::SetItemHeight(int index, int iHeight) -{ ListBox_SetItemHeight(m_hwnd, index, iHeight); -} - -void CCtrlListBox::SetSel(int index, bool sel) -{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlListBox class + +CCtrlListBox::CCtrlListBox(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId) +{} + +BOOL CCtrlListBox::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + switch (idCode) { + case LBN_DBLCLK: OnDblClick(this); break; + case LBN_SELCANCEL: OnSelCancel(this); break; + case LBN_SELCHANGE: OnSelChange(this); break; + } + return TRUE; +} + +void CCtrlListBox::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + if (pos.pt.x == 0 && pos.pt.y == 0) { + pos.iCurr = GetCurSel(); + if (pos.iCurr != -1) { + RECT rc; + GetItemRect(pos.iCurr, &rc); + pos.pt.x = rc.left + 8; + pos.pt.y = rc.top + 8; + ClientToScreen(m_hwnd, &pos.pt); + return; + } + } + + CSuper::GetCaretPos(pos); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CCtrlListBox::AddString(const wchar_t *text, LPARAM data) +{ + int iItem = ListBox_AddString(m_hwnd, text); + ListBox_SetItemData(m_hwnd, iItem, data); + return iItem; +} + +void CCtrlListBox::DeleteString(int index) +{ ListBox_DeleteString(m_hwnd, index); +} + +int CCtrlListBox::FindString(const wchar_t *str, int index, bool exact) +{ return SendMessage(m_hwnd, exact?LB_FINDSTRINGEXACT:LB_FINDSTRING, index, (LPARAM)str); +} + +int CCtrlListBox::GetCount() const +{ return ListBox_GetCount(m_hwnd); +} + +int CCtrlListBox::GetCurSel() const +{ return ListBox_GetCurSel(m_hwnd); +} + +LPARAM CCtrlListBox::GetItemData(int index) const +{ return ListBox_GetItemData(m_hwnd, index); +} + +int CCtrlListBox::GetItemRect(int index, RECT *pResult) const +{ return ListBox_GetItemRect(m_hwnd, index, pResult); +} + +wchar_t* CCtrlListBox::GetItemText(int index) const +{ + wchar_t *result = (wchar_t *)mir_alloc(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); + SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); + return result; +} + +wchar_t* CCtrlListBox::GetItemText(int index, wchar_t *buf, int size) const +{ + wchar_t *result = (wchar_t *)_alloca(sizeof(wchar_t) * (SendMessage(m_hwnd, LB_GETTEXTLEN, index, 0) + 1)); + SendMessage(m_hwnd, LB_GETTEXT, index, (LPARAM)result); + mir_wstrncpy(buf, result, size); + return buf; +} + +bool CCtrlListBox::GetSel(int index) const +{ return ListBox_GetSel(m_hwnd, index) ? true : false; +} + +int CCtrlListBox::GetSelCount() const +{ return ListBox_GetSelCount(m_hwnd); +} + +int* CCtrlListBox::GetSelItems(int *items, int count) const +{ + ListBox_GetSelItems(m_hwnd, count, items); + return items; +} + +int* CCtrlListBox::GetSelItems() const +{ + int count = GetSelCount() + 1; + int *result = (int *)mir_alloc(sizeof(int) * count); + ListBox_GetSelItems(m_hwnd, count, result); + result[count-1] = -1; + return result; +} + +int CCtrlListBox::InsertString(const wchar_t *text, int pos, LPARAM data) +{ + int iItem = ListBox_InsertString(m_hwnd, pos, text); + ListBox_SetItemData(m_hwnd, iItem, data); + return iItem; +} + +void CCtrlListBox::ResetContent() +{ ListBox_ResetContent(m_hwnd); +} + +int CCtrlListBox::SelectString(const wchar_t *str) +{ return ListBox_SelectString(m_hwnd, 0, str); +} + +int CCtrlListBox::SetCurSel(int index) +{ return ListBox_SetCurSel(m_hwnd, index); +} + +void CCtrlListBox::SetItemData(int index, LPARAM data) +{ ListBox_SetItemData(m_hwnd, index, data); +} + +void CCtrlListBox::SetItemHeight(int index, int iHeight) +{ ListBox_SetItemHeight(m_hwnd, index, iHeight); +} + +void CCtrlListBox::SetSel(int index, bool sel) +{ ListBox_SetSel(m_hwnd, sel ? TRUE : FALSE, index); +} diff --git a/src/mir_core/src/Windows/CCtrlListView.cpp b/src/mir_core/src/Windows/CCtrlListView.cpp index eb57951a3c..40bb1f481e 100644 --- a/src/mir_core/src/Windows/CCtrlListView.cpp +++ b/src/mir_core/src/Windows/CCtrlListView.cpp @@ -1,551 +1,551 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlListView - -CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId) -{} - -BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) -{ - TEventInfo evt = { this, pnmh }; - - switch (pnmh->code) { - case NM_CLICK: OnClick(&evt); return TRUE; - case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; - case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; - case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; - case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; - case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; - case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; - case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; - case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; - case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; - case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; - case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; - case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; - case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; - case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; - case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; - case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; - case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; - case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; - case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; - case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; - - case LVN_ITEMCHANGED: - if (!m_parentWnd || !m_parentWnd->IsInitialized()) - return FALSE; - - OnItemChanged(&evt); - - // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes - if (evt.nmlv->uChanged & LVIF_STATE) - if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) - NotifyChange(); - return TRUE; - - case LVN_ODSTATECHANGED: - NotifyChange(); - return TRUE; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) -{ - int result = l1 - l2; - int newItem = HIWORD(param); - int oldItem = LOWORD(param); - if (newItem > oldItem) - return (l1 == oldItem && l2 <= newItem) ? 1 : result; - - return (l2 == oldItem && l1 >= newItem) ? 1 : result; -} - -int CCtrlListView::MoveItem(int idx, int direction) -{ - if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) - return idx; - - if (idx < 0) - idx = GetNextItem(-1, LVNI_FOCUSED); - SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); - return idx + direction; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CCtrlListView::SetCurSel(int idx) -{ - SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); -} - -// additional api -HIMAGELIST CCtrlListView::CreateImageList(int iImageList) -{ - HIMAGELIST hIml = GetImageList(iImageList); - if (hIml) - return hIml; - - hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - SetImageList(hIml, iImageList); - return hIml; -} - -void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) -{ - LVCOLUMN lvc; - lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc.iImage = 0; - lvc.pszText = (LPWSTR)name; - lvc.cx = cx; - lvc.iSubItem = iSubItem; - InsertColumn(iSubItem, &lvc); -} - -void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) -{ - LVGROUP lvg = { 0 }; - lvg.cbSize = sizeof(lvg); - lvg.mask = LVGF_HEADER | LVGF_GROUPID; - lvg.pszHeader = (LPWSTR)name; - lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); - lvg.iGroupId = iGroupId; - InsertGroup(-1, &lvg); -} - -int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; - lvi.iSubItem = 0; - lvi.pszText = (LPWSTR)text; - lvi.iImage = iIcon; - lvi.lParam = lParam; - if (iGroupId >= 0) { - lvi.mask |= LVIF_GROUPID; - lvi.iGroupId = iGroupId; - } - - return InsertItem(&lvi); -} - -void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_TEXT; - lvi.iItem = iItem; - lvi.iSubItem = iSubItem; - lvi.pszText = (LPWSTR)text; - if (iIcon >= 0) { - lvi.mask |= LVIF_IMAGE; - lvi.iImage = iIcon; - } - - SetItem(&lvi); -} - -LPARAM CCtrlListView::GetItemData(int iItem) const -{ - LVITEM lvi = { 0 }; - lvi.mask = LVIF_PARAM; - lvi.iItem = iItem; - return GetItem(&lvi) ? lvi.lParam : -1; -} - -void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const -{ - pos.pCtrl = this; - - // position is empty, let's fill it using selection - if (pos.pt.x == 0 && pos.pt.y == 0) { - pos.iCurr = GetSelectionMark(); - if (pos.iCurr != -1) { - RECT rc; - GetItemRect(pos.iCurr, &rc, TRUE); - pos.pt.x = rc.left + 8; - pos.pt.y = rc.top + 8; - ClientToScreen(m_hwnd, &pos.pt); - return; - } - } - // position is present, let's calculate current item - else { - LVHITTESTINFO hti; - hti.pt = pos.pt; - ScreenToClient(m_hwnd, &hti.pt); - if (SubItemHitTest(&hti) != -1) { - pos.iCurr = hti.iItem; - return; - } - } - CSuper::GetCaretPos(pos); -} - -// classic api -uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) -{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); -} -void CCtrlListView::Arrange(UINT code) -{ ListView_Arrange(m_hwnd, code); -} -void CCtrlListView::CancelEditLabel() -{ ListView_CancelEditLabel(m_hwnd); -} -HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) -{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); -} -void CCtrlListView::DeleteAllItems() -{ ListView_DeleteAllItems(m_hwnd); -} -void CCtrlListView::DeleteColumn(int iCol) -{ ListView_DeleteColumn(m_hwnd, iCol); -} -void CCtrlListView::DeleteItem(int iItem) -{ ListView_DeleteItem(m_hwnd, iItem); -} -HWND CCtrlListView::EditLabel(int iItem) -{ return ListView_EditLabel(m_hwnd, iItem); -} -int CCtrlListView::EnableGroupView(BOOL fEnable) -{ return ListView_EnableGroupView(m_hwnd, fEnable); -} -BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) -{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); -} -int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) -{ return ListView_FindItem(m_hwnd, iStart, plvfi); -} -COLORREF CCtrlListView::GetBkColor() const -{ return ListView_GetBkColor(m_hwnd); -} -void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const -{ ListView_GetBkImage(m_hwnd, plvbki); -} -UINT CCtrlListView::GetCallbackMask() const -{ return ListView_GetCallbackMask(m_hwnd); -} -BOOL CCtrlListView::GetCheckState(UINT iIndex) const -{ return ListView_GetCheckState(m_hwnd, iIndex); -} -void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const -{ ListView_GetColumn(m_hwnd, iCol, pcol); -} -void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const -{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -int CCtrlListView::GetColumnWidth(int iCol) const -{ return ListView_GetColumnWidth(m_hwnd, iCol); -} -int CCtrlListView::GetCountPerPage() const -{ return ListView_GetCountPerPage(m_hwnd); -} -HWND CCtrlListView::GetEditControl() const -{ return ListView_GetEditControl(m_hwnd); -} -uint32_t CCtrlListView::GetExtendedListViewStyle() const -{ return ListView_GetExtendedListViewStyle(m_hwnd); -} -void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const -{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); -} -HWND CCtrlListView::GetHeader() const -{ return ListView_GetHeader(m_hwnd); -} -HCURSOR CCtrlListView::GetHotCursor() const -{ return ListView_GetHotCursor(m_hwnd); -} -INT CCtrlListView::GetHotItem() const -{ return ListView_GetHotItem(m_hwnd); -} -uint32_t CCtrlListView::GetHoverTime() const -{ return ListView_GetHoverTime(m_hwnd); -} -HIMAGELIST CCtrlListView::GetImageList(int iImageList) const -{ return ListView_GetImageList(m_hwnd, iImageList); -} -BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const -{ return ListView_GetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::GetInsertMarkColor() const -{ return ListView_GetInsertMarkColor(m_hwnd); -} -int CCtrlListView::GetInsertMarkRect(LPRECT prc) const -{ return ListView_GetInsertMarkRect(m_hwnd, prc); -} -BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const -{ return ListView_GetISearchString(m_hwnd, lpsz); -} -bool CCtrlListView::GetItem(LPLVITEM pitem) const -{ return ListView_GetItem(m_hwnd, pitem) == TRUE; -} -int CCtrlListView::GetItemCount() const -{ return ListView_GetItemCount(m_hwnd); -} -void CCtrlListView::GetItemPosition(int i, POINT *ppt) const -{ ListView_GetItemPosition(m_hwnd, i, ppt); -} -void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const -{ ListView_GetItemRect(m_hwnd, i, prc, code); -} -uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const -{ return ListView_GetItemSpacing(m_hwnd, fSmall); -} -UINT CCtrlListView::GetItemState(int i, UINT mask) const -{ return ListView_GetItemState(m_hwnd, i, mask); -} -void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const -{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); -} -int CCtrlListView::GetNextItem(int iStart, UINT flags) const -{ return ListView_GetNextItem(m_hwnd, iStart, flags); -} -BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const -{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); -} -BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const -{ return ListView_GetOrigin(m_hwnd, lpptOrg); -} -COLORREF CCtrlListView::GetOutlineColor() const -{ return ListView_GetOutlineColor(m_hwnd); -} -UINT CCtrlListView::GetSelectedColumn() const -{ return ListView_GetSelectedColumn(m_hwnd); -} -UINT CCtrlListView::GetSelectedCount() const -{ return ListView_GetSelectedCount(m_hwnd); -} -INT CCtrlListView::GetSelectionMark() const -{ return ListView_GetSelectionMark(m_hwnd); -} -int CCtrlListView::GetStringWidth(LPCSTR psz) const -{ return ListView_GetStringWidth(m_hwnd, psz); -} -BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const -{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); -} -COLORREF CCtrlListView::GetTextBkColor() const -{ return ListView_GetTextBkColor(m_hwnd); -} -COLORREF CCtrlListView::GetTextColor() const -{ return ListView_GetTextColor(m_hwnd); -} -void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const -{ ListView_GetTileInfo(m_hwnd, plvtinfo); -} -void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const -{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::GetToolTips() const -{ return ListView_GetToolTips(m_hwnd); -} -int CCtrlListView::GetTopIndex() const -{ return ListView_GetTopIndex(m_hwnd); -} -BOOL CCtrlListView::GetUnicodeFormat() const -{ return ListView_GetUnicodeFormat(m_hwnd); -} -uint32_t CCtrlListView::GetView() const -{ return ListView_GetView(m_hwnd); -} -BOOL CCtrlListView::GetViewRect(RECT *prc) const -{ return ListView_GetViewRect(m_hwnd, prc); -} -void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const -{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -BOOL CCtrlListView::HasGroup(int dwGroupId) -{ return ListView_HasGroup(m_hwnd, dwGroupId); -} -int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const -{ return ListView_HitTest(m_hwnd, pinfo); -} -int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) -{ return ListView_InsertColumn(m_hwnd, iCol, pcol); -} -int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) -{ return ListView_InsertGroup(m_hwnd, index, pgrp); -} -void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) -{ ListView_InsertGroupSorted(m_hwnd, structInsert); -} -int CCtrlListView::InsertItem(const LVITEM *pitem) -{ return ListView_InsertItem(m_hwnd, pitem); -} -BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) -{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); -} -BOOL CCtrlListView::IsGroupViewEnabled() -{ return ListView_IsGroupViewEnabled(m_hwnd); -} -UINT CCtrlListView::MapIDToIndex(UINT id) -{ return ListView_MapIDToIndex(m_hwnd, id); -} -UINT CCtrlListView::MapIndexToID(UINT index) -{ return ListView_MapIndexToID(m_hwnd, index); -} -BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) -{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); -} -void CCtrlListView::RemoveAllGroups() -{ ListView_RemoveAllGroups(m_hwnd); -} -int CCtrlListView::RemoveGroup(int iGroupId) -{ return ListView_RemoveGroup(m_hwnd, iGroupId); -} -BOOL CCtrlListView::Scroll(int dx, int dy) -{ return ListView_Scroll(m_hwnd, dx, dy); -} -BOOL CCtrlListView::SetBkColor(COLORREF clrBk) -{ return ListView_SetBkColor(m_hwnd, clrBk); -} -BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) -{ return ListView_SetBkImage(m_hwnd, plvbki); -} -BOOL CCtrlListView::SetCallbackMask(UINT mask) -{ return ListView_SetCallbackMask(m_hwnd, mask); -} -void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) -{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); -} -BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) -{ return ListView_SetColumn(m_hwnd, iCol, pcol); -} -BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) -{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); -} -BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) -{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); -} -void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); -} -void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) -{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); -} -int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) -{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); -} -void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) -{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); -} -HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) -{ return ListView_SetHotCursor(m_hwnd, hCursor); -} -INT CCtrlListView::SetHotItem(INT iIndex) -{ return ListView_SetHotItem(m_hwnd, iIndex); -} -void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) -{ ListView_SetHoverTime(m_hwnd, dwHoverTime); -} -uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) -{ return ListView_SetIconSpacing(m_hwnd, cx, cy); -} -HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) -{ return ListView_SetImageList(m_hwnd, himl, iImageList); -} -BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) -{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); -} -BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) -{ return ListView_SetInsertMark(m_hwnd, plvim); -} -COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) -{ return ListView_SetInsertMarkColor(m_hwnd, color); -} -BOOL CCtrlListView::SetItem(const LVITEM *pitem) -{ return ListView_SetItem(m_hwnd, pitem); -} -void CCtrlListView::SetItemCount(int cItems) -{ ListView_SetItemCount(m_hwnd, cItems); -} -void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) -{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); -} -BOOL CCtrlListView::SetItemPosition(int i, int x, int y) -{ return ListView_SetItemPosition(m_hwnd, i, x, y); -} -void CCtrlListView::SetItemPosition32(int iItem, int x, int y) -{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); -} -void CCtrlListView::SetItemState(int i, UINT state, UINT mask) -{ ListView_SetItemState(m_hwnd, i, state, mask); -} -void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) -{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); -} -COLORREF CCtrlListView::SetOutlineColor(COLORREF color) -{ return ListView_SetOutlineColor(m_hwnd, color); -} -void CCtrlListView::SetSelectedColumn(int iCol) -{ ListView_SetSelectedColumn(m_hwnd, iCol); -} -INT CCtrlListView::SetSelectionMark(INT iIndex) -{ return ListView_SetSelectionMark(m_hwnd, iIndex); -} -BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) -{ return ListView_SetTextBkColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTextColor(COLORREF clrText) -{ return ListView_SetTextColor(m_hwnd, clrText); -} -BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) -{ return ListView_SetTileInfo(m_hwnd, plvtinfo); -} -BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) -{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); -} -HWND CCtrlListView::SetToolTips(HWND ToolTip) -{ return ListView_SetToolTips(m_hwnd, ToolTip); -} -BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) -{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); -} -int CCtrlListView::SetView(uint32_t iView) -{ return ListView_SetView(m_hwnd, iView); -} -void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) -{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); -} -int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) -{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); -} -BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); -} -BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) -{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); -} -INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const -{ return ListView_SubItemHitTest(m_hwnd, pInfo); -} -BOOL CCtrlListView::Update(int iItem) -{ return ListView_Update(m_hwnd, iItem); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlListView + +CCtrlListView::CCtrlListView(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId) +{} + +BOOL CCtrlListView::OnNotify(int, NMHDR *pnmh) +{ + TEventInfo evt = { this, pnmh }; + + switch (pnmh->code) { + case NM_CLICK: OnClick(&evt); return TRUE; + case NM_DBLCLK: OnDoubleClick(&evt); return TRUE; + case NM_CUSTOMDRAW: OnCustomDraw(&evt); return TRUE; + case LVN_BEGINDRAG: OnBeginDrag(&evt); return TRUE; + case LVN_BEGINLABELEDIT: OnBeginLabelEdit(&evt); return TRUE; + case LVN_BEGINRDRAG: OnBeginRDrag(&evt); return TRUE; + case LVN_BEGINSCROLL: OnBeginScroll(&evt); return TRUE; + case LVN_COLUMNCLICK: OnColumnClick(&evt); return TRUE; + case LVN_DELETEALLITEMS: OnDeleteAllItems(&evt); return TRUE; + case LVN_DELETEITEM: OnDeleteItem(&evt); return TRUE; + case LVN_ENDLABELEDIT: OnEndLabelEdit(&evt); return TRUE; + case LVN_ENDSCROLL: OnEndScroll(&evt); return TRUE; + case LVN_GETDISPINFO: OnGetDispInfo(&evt); return TRUE; + case LVN_GETINFOTIP: OnGetInfoTip(&evt); return TRUE; + case LVN_HOTTRACK: OnHotTrack(&evt); return TRUE; + case LVN_INSERTITEM: OnInsertItem(&evt); return TRUE; + case LVN_ITEMACTIVATE: OnItemActivate(&evt); return TRUE; + case LVN_ITEMCHANGING: OnItemChanging(&evt); return TRUE; + case LVN_KEYDOWN: OnKeyDown(&evt); return TRUE; + case LVN_MARQUEEBEGIN: OnMarqueeBegin(&evt); return TRUE; + case LVN_SETDISPINFO: OnSetDispInfo(&evt); return TRUE; + + case LVN_ITEMCHANGED: + if (!m_parentWnd || !m_parentWnd->IsInitialized()) + return FALSE; + + OnItemChanged(&evt); + + // item's state is calculated as 1/2 << 12, so we check it to filter out all non-state changes + if (evt.nmlv->uChanged & LVIF_STATE) + if ((evt.nmlv->uOldState >> 12) != 0 && (evt.nmlv->uNewState >> 12) != 0) + NotifyChange(); + return TRUE; + + case LVN_ODSTATECHANGED: + NotifyChange(); + return TRUE; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int CALLBACK LVMoveSortProc(LPARAM l1, LPARAM l2, LPARAM param) +{ + int result = l1 - l2; + int newItem = HIWORD(param); + int oldItem = LOWORD(param); + if (newItem > oldItem) + return (l1 == oldItem && l2 <= newItem) ? 1 : result; + + return (l2 == oldItem && l1 >= newItem) ? 1 : result; +} + +int CCtrlListView::MoveItem(int idx, int direction) +{ + if ((direction > 0 && idx >= GetItemCount() - 1) || (direction < 0 && idx <= 0)) + return idx; + + if (idx < 0) + idx = GetNextItem(-1, LVNI_FOCUSED); + SortItemsEx(&LVMoveSortProc, MAKELONG(idx, idx + direction)); + return idx + direction; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CCtrlListView::SetCurSel(int idx) +{ + SetItemState(idx, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); +} + +// additional api +HIMAGELIST CCtrlListView::CreateImageList(int iImageList) +{ + HIMAGELIST hIml = GetImageList(iImageList); + if (hIml) + return hIml; + + hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); + SetImageList(hIml, iImageList); + return hIml; +} + +void CCtrlListView::AddColumn(int iSubItem, const wchar_t *name, int cx) +{ + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc.iImage = 0; + lvc.pszText = (LPWSTR)name; + lvc.cx = cx; + lvc.iSubItem = iSubItem; + InsertColumn(iSubItem, &lvc); +} + +void CCtrlListView::AddGroup(int iGroupId, const wchar_t *name) +{ + LVGROUP lvg = { 0 }; + lvg.cbSize = sizeof(lvg); + lvg.mask = LVGF_HEADER | LVGF_GROUPID; + lvg.pszHeader = (LPWSTR)name; + lvg.cchHeader = (int)mir_wstrlen(lvg.pszHeader); + lvg.iGroupId = iGroupId; + InsertGroup(-1, &lvg); +} + +int CCtrlListView::AddItem(const wchar_t *text, int iIcon, LPARAM lParam, int iGroupId) +{ + LVITEM lvi = { 0 }; + lvi.mask = LVIF_PARAM | LVIF_TEXT | LVIF_IMAGE; + lvi.iSubItem = 0; + lvi.pszText = (LPWSTR)text; + lvi.iImage = iIcon; + lvi.lParam = lParam; + if (iGroupId >= 0) { + lvi.mask |= LVIF_GROUPID; + lvi.iGroupId = iGroupId; + } + + return InsertItem(&lvi); +} + +void CCtrlListView::SetItem(int iItem, int iSubItem, const wchar_t *text, int iIcon) +{ + LVITEM lvi = { 0 }; + lvi.mask = LVIF_TEXT; + lvi.iItem = iItem; + lvi.iSubItem = iSubItem; + lvi.pszText = (LPWSTR)text; + if (iIcon >= 0) { + lvi.mask |= LVIF_IMAGE; + lvi.iImage = iIcon; + } + + SetItem(&lvi); +} + +LPARAM CCtrlListView::GetItemData(int iItem) const +{ + LVITEM lvi = { 0 }; + lvi.mask = LVIF_PARAM; + lvi.iItem = iItem; + return GetItem(&lvi) ? lvi.lParam : -1; +} + +void CCtrlListView::GetCaretPos(CContextMenuPos &pos) const +{ + pos.pCtrl = this; + + // position is empty, let's fill it using selection + if (pos.pt.x == 0 && pos.pt.y == 0) { + pos.iCurr = GetSelectionMark(); + if (pos.iCurr != -1) { + RECT rc; + GetItemRect(pos.iCurr, &rc, TRUE); + pos.pt.x = rc.left + 8; + pos.pt.y = rc.top + 8; + ClientToScreen(m_hwnd, &pos.pt); + return; + } + } + // position is present, let's calculate current item + else { + LVHITTESTINFO hti; + hti.pt = pos.pt; + ScreenToClient(m_hwnd, &hti.pt); + if (SubItemHitTest(&hti) != -1) { + pos.iCurr = hti.iItem; + return; + } + } + CSuper::GetCaretPos(pos); +} + +// classic api +uint32_t CCtrlListView::ApproximateViewRect(int cx, int cy, int iCount) +{ return ListView_ApproximateViewRect(m_hwnd, cx, cy, iCount); +} +void CCtrlListView::Arrange(UINT code) +{ ListView_Arrange(m_hwnd, code); +} +void CCtrlListView::CancelEditLabel() +{ ListView_CancelEditLabel(m_hwnd); +} +HIMAGELIST CCtrlListView::CreateDragImage(int iItem, LPPOINT lpptUpLeft) +{ return ListView_CreateDragImage(m_hwnd, iItem, lpptUpLeft); +} +void CCtrlListView::DeleteAllItems() +{ ListView_DeleteAllItems(m_hwnd); +} +void CCtrlListView::DeleteColumn(int iCol) +{ ListView_DeleteColumn(m_hwnd, iCol); +} +void CCtrlListView::DeleteItem(int iItem) +{ ListView_DeleteItem(m_hwnd, iItem); +} +HWND CCtrlListView::EditLabel(int iItem) +{ return ListView_EditLabel(m_hwnd, iItem); +} +int CCtrlListView::EnableGroupView(BOOL fEnable) +{ return ListView_EnableGroupView(m_hwnd, fEnable); +} +BOOL CCtrlListView::EnsureVisible(int i, BOOL fPartialOK) +{ return ListView_EnsureVisible(m_hwnd, i, fPartialOK); +} +int CCtrlListView::FindItem(int iStart, const LVFINDINFO *plvfi) +{ return ListView_FindItem(m_hwnd, iStart, plvfi); +} +COLORREF CCtrlListView::GetBkColor() const +{ return ListView_GetBkColor(m_hwnd); +} +void CCtrlListView::GetBkImage(LPLVBKIMAGE plvbki) const +{ ListView_GetBkImage(m_hwnd, plvbki); +} +UINT CCtrlListView::GetCallbackMask() const +{ return ListView_GetCallbackMask(m_hwnd); +} +BOOL CCtrlListView::GetCheckState(UINT iIndex) const +{ return ListView_GetCheckState(m_hwnd, iIndex); +} +void CCtrlListView::GetColumn(int iCol, LPLVCOLUMN pcol) const +{ ListView_GetColumn(m_hwnd, iCol, pcol); +} +void CCtrlListView::GetColumnOrderArray(int iCount, int *lpiArray) const +{ ListView_GetColumnOrderArray(m_hwnd, iCount, lpiArray); +} +int CCtrlListView::GetColumnWidth(int iCol) const +{ return ListView_GetColumnWidth(m_hwnd, iCol); +} +int CCtrlListView::GetCountPerPage() const +{ return ListView_GetCountPerPage(m_hwnd); +} +HWND CCtrlListView::GetEditControl() const +{ return ListView_GetEditControl(m_hwnd); +} +uint32_t CCtrlListView::GetExtendedListViewStyle() const +{ return ListView_GetExtendedListViewStyle(m_hwnd); +} +void CCtrlListView::GetGroupMetrics(LVGROUPMETRICS *pGroupMetrics) const +{ ListView_GetGroupMetrics(m_hwnd, pGroupMetrics); +} +HWND CCtrlListView::GetHeader() const +{ return ListView_GetHeader(m_hwnd); +} +HCURSOR CCtrlListView::GetHotCursor() const +{ return ListView_GetHotCursor(m_hwnd); +} +INT CCtrlListView::GetHotItem() const +{ return ListView_GetHotItem(m_hwnd); +} +uint32_t CCtrlListView::GetHoverTime() const +{ return ListView_GetHoverTime(m_hwnd); +} +HIMAGELIST CCtrlListView::GetImageList(int iImageList) const +{ return ListView_GetImageList(m_hwnd, iImageList); +} +BOOL CCtrlListView::GetInsertMark(LVINSERTMARK *plvim) const +{ return ListView_GetInsertMark(m_hwnd, plvim); +} +COLORREF CCtrlListView::GetInsertMarkColor() const +{ return ListView_GetInsertMarkColor(m_hwnd); +} +int CCtrlListView::GetInsertMarkRect(LPRECT prc) const +{ return ListView_GetInsertMarkRect(m_hwnd, prc); +} +BOOL CCtrlListView::GetISearchString(LPSTR lpsz) const +{ return ListView_GetISearchString(m_hwnd, lpsz); +} +bool CCtrlListView::GetItem(LPLVITEM pitem) const +{ return ListView_GetItem(m_hwnd, pitem) == TRUE; +} +int CCtrlListView::GetItemCount() const +{ return ListView_GetItemCount(m_hwnd); +} +void CCtrlListView::GetItemPosition(int i, POINT *ppt) const +{ ListView_GetItemPosition(m_hwnd, i, ppt); +} +void CCtrlListView::GetItemRect(int i, RECT *prc, int code) const +{ ListView_GetItemRect(m_hwnd, i, prc, code); +} +uint32_t CCtrlListView::GetItemSpacing(BOOL fSmall) const +{ return ListView_GetItemSpacing(m_hwnd, fSmall); +} +UINT CCtrlListView::GetItemState(int i, UINT mask) const +{ return ListView_GetItemState(m_hwnd, i, mask); +} +void CCtrlListView::GetItemText(int iItem, int iSubItem, LPTSTR pszText, int cchTextMax) const +{ ListView_GetItemText(m_hwnd, iItem, iSubItem, pszText, cchTextMax); +} +int CCtrlListView::GetNextItem(int iStart, UINT flags) const +{ return ListView_GetNextItem(m_hwnd, iStart, flags); +} +BOOL CCtrlListView::GetNumberOfWorkAreas(LPUINT lpuWorkAreas) const +{ return ListView_GetNumberOfWorkAreas(m_hwnd, lpuWorkAreas); +} +BOOL CCtrlListView::GetOrigin(LPPOINT lpptOrg) const +{ return ListView_GetOrigin(m_hwnd, lpptOrg); +} +COLORREF CCtrlListView::GetOutlineColor() const +{ return ListView_GetOutlineColor(m_hwnd); +} +UINT CCtrlListView::GetSelectedColumn() const +{ return ListView_GetSelectedColumn(m_hwnd); +} +UINT CCtrlListView::GetSelectedCount() const +{ return ListView_GetSelectedCount(m_hwnd); +} +INT CCtrlListView::GetSelectionMark() const +{ return ListView_GetSelectionMark(m_hwnd); +} +int CCtrlListView::GetStringWidth(LPCSTR psz) const +{ return ListView_GetStringWidth(m_hwnd, psz); +} +BOOL CCtrlListView::GetSubItemRect(int iItem, int iSubItem, int code, LPRECT lpRect) const +{ return ListView_GetSubItemRect(m_hwnd, iItem, iSubItem, code, lpRect); +} +COLORREF CCtrlListView::GetTextBkColor() const +{ return ListView_GetTextBkColor(m_hwnd); +} +COLORREF CCtrlListView::GetTextColor() const +{ return ListView_GetTextColor(m_hwnd); +} +void CCtrlListView::GetTileInfo(PLVTILEINFO plvtinfo) const +{ ListView_GetTileInfo(m_hwnd, plvtinfo); +} +void CCtrlListView::GetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) const +{ ListView_GetTileViewInfo(m_hwnd, plvtvinfo); +} +HWND CCtrlListView::GetToolTips() const +{ return ListView_GetToolTips(m_hwnd); +} +int CCtrlListView::GetTopIndex() const +{ return ListView_GetTopIndex(m_hwnd); +} +BOOL CCtrlListView::GetUnicodeFormat() const +{ return ListView_GetUnicodeFormat(m_hwnd); +} +uint32_t CCtrlListView::GetView() const +{ return ListView_GetView(m_hwnd); +} +BOOL CCtrlListView::GetViewRect(RECT *prc) const +{ return ListView_GetViewRect(m_hwnd, prc); +} +void CCtrlListView::GetWorkAreas(INT nWorkAreas, LPRECT lprc) const +{ ListView_GetWorkAreas(m_hwnd, nWorkAreas, lprc); +} +BOOL CCtrlListView::HasGroup(int dwGroupId) +{ return ListView_HasGroup(m_hwnd, dwGroupId); +} +int CCtrlListView::HitTest(LPLVHITTESTINFO pinfo) const +{ return ListView_HitTest(m_hwnd, pinfo); +} +int CCtrlListView::InsertColumn(int iCol, const LVCOLUMN *pcol) +{ return ListView_InsertColumn(m_hwnd, iCol, pcol); +} +int CCtrlListView::InsertGroup(int index, PLVGROUP pgrp) +{ return ListView_InsertGroup(m_hwnd, index, pgrp); +} +void CCtrlListView::InsertGroupSorted(PLVINSERTGROUPSORTED structInsert) +{ ListView_InsertGroupSorted(m_hwnd, structInsert); +} +int CCtrlListView::InsertItem(const LVITEM *pitem) +{ return ListView_InsertItem(m_hwnd, pitem); +} +BOOL CCtrlListView::InsertMarkHitTest(LPPOINT point, LVINSERTMARK *plvim) +{ return ListView_InsertMarkHitTest(m_hwnd, point, plvim); +} +BOOL CCtrlListView::IsGroupViewEnabled() +{ return ListView_IsGroupViewEnabled(m_hwnd); +} +UINT CCtrlListView::MapIDToIndex(UINT id) +{ return ListView_MapIDToIndex(m_hwnd, id); +} +UINT CCtrlListView::MapIndexToID(UINT index) +{ return ListView_MapIndexToID(m_hwnd, index); +} +BOOL CCtrlListView::RedrawItems(int iFirst, int iLast) +{ return ListView_RedrawItems(m_hwnd, iFirst, iLast); +} +void CCtrlListView::RemoveAllGroups() +{ ListView_RemoveAllGroups(m_hwnd); +} +int CCtrlListView::RemoveGroup(int iGroupId) +{ return ListView_RemoveGroup(m_hwnd, iGroupId); +} +BOOL CCtrlListView::Scroll(int dx, int dy) +{ return ListView_Scroll(m_hwnd, dx, dy); +} +BOOL CCtrlListView::SetBkColor(COLORREF clrBk) +{ return ListView_SetBkColor(m_hwnd, clrBk); +} +BOOL CCtrlListView::SetBkImage(LPLVBKIMAGE plvbki) +{ return ListView_SetBkImage(m_hwnd, plvbki); +} +BOOL CCtrlListView::SetCallbackMask(UINT mask) +{ return ListView_SetCallbackMask(m_hwnd, mask); +} +void CCtrlListView::SetCheckState(UINT iIndex, BOOL fCheck) +{ ListView_SetCheckState(m_hwnd, iIndex, fCheck); +} +BOOL CCtrlListView::SetColumn(int iCol, LPLVCOLUMN pcol) +{ return ListView_SetColumn(m_hwnd, iCol, pcol); +} +BOOL CCtrlListView::SetColumnOrderArray(int iCount, int *lpiArray) +{ return ListView_SetColumnOrderArray(m_hwnd, iCount, lpiArray); +} +BOOL CCtrlListView::SetColumnWidth(int iCol, int cx) +{ return ListView_SetColumnWidth(m_hwnd, iCol, cx); +} +void CCtrlListView::SetExtendedListViewStyle(uint32_t dwExStyle) +{ ListView_SetExtendedListViewStyle(m_hwnd, dwExStyle); +} +void CCtrlListView::SetExtendedListViewStyleEx(uint32_t dwExMask, uint32_t dwExStyle) +{ ListView_SetExtendedListViewStyleEx(m_hwnd, dwExMask, dwExStyle); +} +int CCtrlListView::SetGroupInfo(int iGroupId, PLVGROUP pgrp) +{ return ListView_SetGroupInfo(m_hwnd, iGroupId, pgrp); +} +void CCtrlListView::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) +{ ListView_SetGroupMetrics(m_hwnd, pGroupMetrics); +} +HCURSOR CCtrlListView::SetHotCursor(HCURSOR hCursor) +{ return ListView_SetHotCursor(m_hwnd, hCursor); +} +INT CCtrlListView::SetHotItem(INT iIndex) +{ return ListView_SetHotItem(m_hwnd, iIndex); +} +void CCtrlListView::SetHoverTime(uint32_t dwHoverTime) +{ ListView_SetHoverTime(m_hwnd, dwHoverTime); +} +uint32_t CCtrlListView::SetIconSpacing(int cx, int cy) +{ return ListView_SetIconSpacing(m_hwnd, cx, cy); +} +HIMAGELIST CCtrlListView::SetImageList(HIMAGELIST himl, int iImageList) +{ return ListView_SetImageList(m_hwnd, himl, iImageList); +} +BOOL CCtrlListView::SetInfoTip(PLVSETINFOTIP plvSetInfoTip) +{ return ListView_SetInfoTip(m_hwnd, plvSetInfoTip); +} +BOOL CCtrlListView::SetInsertMark(LVINSERTMARK *plvim) +{ return ListView_SetInsertMark(m_hwnd, plvim); +} +COLORREF CCtrlListView::SetInsertMarkColor(COLORREF color) +{ return ListView_SetInsertMarkColor(m_hwnd, color); +} +BOOL CCtrlListView::SetItem(const LVITEM *pitem) +{ return ListView_SetItem(m_hwnd, pitem); +} +void CCtrlListView::SetItemCount(int cItems) +{ ListView_SetItemCount(m_hwnd, cItems); +} +void CCtrlListView::SetItemCountEx(int cItems, uint32_t dwFlags) +{ ListView_SetItemCountEx(m_hwnd, cItems, dwFlags); +} +BOOL CCtrlListView::SetItemPosition(int i, int x, int y) +{ return ListView_SetItemPosition(m_hwnd, i, x, y); +} +void CCtrlListView::SetItemPosition32(int iItem, int x, int y) +{ ListView_SetItemPosition32(m_hwnd, iItem, x, y); +} +void CCtrlListView::SetItemState(int i, UINT state, UINT mask) +{ ListView_SetItemState(m_hwnd, i, state, mask); +} +void CCtrlListView::SetItemText(int i, int iSubItem, const wchar_t *pszText) +{ ListView_SetItemText(m_hwnd, i, iSubItem, (LPWSTR)pszText); +} +COLORREF CCtrlListView::SetOutlineColor(COLORREF color) +{ return ListView_SetOutlineColor(m_hwnd, color); +} +void CCtrlListView::SetSelectedColumn(int iCol) +{ ListView_SetSelectedColumn(m_hwnd, iCol); +} +INT CCtrlListView::SetSelectionMark(INT iIndex) +{ return ListView_SetSelectionMark(m_hwnd, iIndex); +} +BOOL CCtrlListView::SetTextBkColor(COLORREF clrText) +{ return ListView_SetTextBkColor(m_hwnd, clrText); +} +BOOL CCtrlListView::SetTextColor(COLORREF clrText) +{ return ListView_SetTextColor(m_hwnd, clrText); +} +BOOL CCtrlListView::SetTileInfo(PLVTILEINFO plvtinfo) +{ return ListView_SetTileInfo(m_hwnd, plvtinfo); +} +BOOL CCtrlListView::SetTileViewInfo(PLVTILEVIEWINFO plvtvinfo) +{ return ListView_SetTileViewInfo(m_hwnd, plvtvinfo); +} +HWND CCtrlListView::SetToolTips(HWND ToolTip) +{ return ListView_SetToolTips(m_hwnd, ToolTip); +} +BOOL CCtrlListView::SetUnicodeFormat(BOOL fUnicode) +{ return ListView_SetUnicodeFormat(m_hwnd, fUnicode); +} +int CCtrlListView::SetView(uint32_t iView) +{ return ListView_SetView(m_hwnd, iView); +} +void CCtrlListView::SetWorkAreas(INT nWorkAreas, LPRECT lprc) +{ ListView_SetWorkAreas(m_hwnd, nWorkAreas, lprc); +} +int CCtrlListView::SortGroups(PFNLVGROUPCOMPARE pfnGroupCompare, LPVOID plv) +{ return ListView_SortGroups(m_hwnd, pfnGroupCompare, plv); +} +BOOL CCtrlListView::SortItems(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) +{ return ListView_SortItems(m_hwnd, pfnCompare, lParamSort); +} +BOOL CCtrlListView::SortItemsEx(PFNLVCOMPARE pfnCompare, LPARAM lParamSort) +{ return ListView_SortItemsEx(m_hwnd, pfnCompare, lParamSort); +} +INT CCtrlListView::SubItemHitTest(LPLVHITTESTINFO pInfo) const +{ return ListView_SubItemHitTest(m_hwnd, pInfo); +} +BOOL CCtrlListView::Update(int iItem) +{ return ListView_Update(m_hwnd, iItem); +} diff --git a/src/mir_core/src/Windows/CCtrlMButton.cpp b/src/mir_core/src/Windows/CCtrlMButton.cpp index 8d9198e144..cc05a3a8e4 100644 --- a/src/mir_core/src/Windows/CCtrlMButton.cpp +++ b/src/mir_core/src/Windows/CCtrlMButton.cpp @@ -1,62 +1,62 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlMButton - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(hIcon), - m_toolTip(tooltip) -{} - -CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) - : CCtrlButton(dlg, ctrlId), - m_hIcon(::Skin_LoadIcon(iCoreIcon)), - m_toolTip(tooltip) -{} - -CCtrlMButton::~CCtrlMButton() -{ - ::IcoLib_ReleaseIcon(m_hIcon); -} - -void CCtrlMButton::OnInit() -{ - CCtrlButton::OnInit(); - - SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); - SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); - SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); -} - -void CCtrlMButton::MakeFlat() -{ - SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); -} - -void CCtrlMButton::MakePush() -{ - SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlMButton + +CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, HICON hIcon, const char* tooltip) + : CCtrlButton(dlg, ctrlId), + m_hIcon(hIcon), + m_toolTip(tooltip) +{} + +CCtrlMButton::CCtrlMButton(CDlgBase *dlg, int ctrlId, int iCoreIcon, const char* tooltip) + : CCtrlButton(dlg, ctrlId), + m_hIcon(::Skin_LoadIcon(iCoreIcon)), + m_toolTip(tooltip) +{} + +CCtrlMButton::~CCtrlMButton() +{ + ::IcoLib_ReleaseIcon(m_hIcon); +} + +void CCtrlMButton::OnInit() +{ + CCtrlButton::OnInit(); + + SendMessage(m_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)m_hIcon); + SendMessage(m_hwnd, BUTTONADDTOOLTIP, (WPARAM)m_toolTip, 0); + SendMessage(m_hwnd, BUTTONSETASFLATBTN, (WPARAM)m_toolTip, 0); +} + +void CCtrlMButton::MakeFlat() +{ + SendMessage(m_hwnd, BUTTONSETASFLATBTN, TRUE, 0); +} + +void CCtrlMButton::MakePush() +{ + SendMessage(m_hwnd, BUTTONSETASPUSHBTN, TRUE, 0); +} diff --git a/src/mir_core/src/Windows/CCtrlPages.cpp b/src/mir_core/src/Windows/CCtrlPages.cpp index 512c32e142..c2a95553c5 100644 --- a/src/mir_core/src/Windows/CCtrlPages.cpp +++ b/src/mir_core/src/Windows/CCtrlPages.cpp @@ -1,411 +1,411 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -static volatile long g_order = 1; - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlPages - -struct CCtrlPages::TPageInfo : public MZeroedObject -{ - TPageInfo() - { - m_iOrder = InterlockedIncrement(&g_order); - } - - ~TPageInfo() - { - if (m_hIcon) - DestroyIcon(m_hIcon); - } - - int m_iOrder; - ptrW m_ptszHeader; - HICON m_hIcon; - bool m_bChanged, m_bScheduledResize; - CDlgBase *m_pDlg; -}; - -CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) - : CCtrlBase(dlg, ctrlId), - m_hIml(nullptr), - m_pActivePage(nullptr), - m_pages(4, NumericKeySortT) -{} - -void CCtrlPages::OnInit() -{ - CSuper::OnInit(); - Subclass(); - - for (auto &it : m_pages) - InsertPage(it); - m_pages.destroy(); - - ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); - - TPageInfo *info = GetCurrPage(); - if (info) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - int tabCount; - - switch (msg) { - case WM_SIZE: - if (TPageInfo *pCurrInfo = GetCurrPage()) { - tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p == nullptr) - continue; - if (p == pCurrInfo) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - else p->m_bScheduledResize = true; - } - } - break; - - case PSM_CHANGED: - if (TPageInfo *info = GetCurrPage()) - info->m_bChanged = TRUE; - return TRUE; - - case PSM_FORCECHANGED: - tabCount = GetCount(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p) { - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - if (pshn.hdr.hwndFrom != nullptr) - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} - -void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) -{ - TPageInfo *info = new TPageInfo; - info->m_pDlg = pDlg; - info->m_hIcon = hIcon; - info->m_ptszHeader = mir_wstrdup(ptszName); - - if (m_hwnd != nullptr) { - InsertPage(info); - - if (GetCount() == 1) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - } - m_pages.insert(info); -} - -void CCtrlPages::ActivatePage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - if (info == nullptr || info->m_pDlg == nullptr) - return; - - if (m_pActivePage != nullptr) - ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); - - m_pActivePage = info->m_pDlg; - if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); - } - - TabCtrl_SetCurSel(m_hwnd, iPage); - ShowPage(m_pActivePage); - ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); -} - -void CCtrlPages::CheckRowCount() -{ - int iRowCount = TabCtrl_GetRowCount(m_hwnd); - if (m_numRows != iRowCount) { - m_numRows = iRowCount; - for (auto &p : m_pages) - p->m_bScheduledResize = true; - } -} - -int CCtrlPages::GetCount() -{ - return TabCtrl_GetItemCount(m_hwnd); -} - -CDlgBase* CCtrlPages::GetNthPage(int iPage) -{ - TPageInfo *info = GetItemPage(iPage); - return (info == nullptr) ? nullptr : info->m_pDlg; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM; - if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) - return nullptr; - - return (TPageInfo*)tci.lParam; -} - -int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) -{ - int tabCount = TabCtrl_GetItemCount(m_hwnd); - for (int i = 0; i < tabCount; i++) { - TCITEM tci; - tci.mask = TCIF_PARAM | TCIF_IMAGE; - TabCtrl_GetItem(m_hwnd, i, &tci); - TPageInfo *pPage = (TPageInfo *)tci.lParam; - if (pPage == nullptr) - continue; - - if (pPage->m_pDlg == pDlg) - return i; - } - - return -1; -} - -void CCtrlPages::InsertPage(TPageInfo *pPage) -{ - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)pPage; - tci.pszText = TranslateW_LP(pPage->m_ptszHeader); - if (pPage->m_hIcon) { - if (!m_hIml) { - m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); - TabCtrl_SetImageList(m_hwnd, m_hIml); - } - - tci.mask |= TCIF_IMAGE; - tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); - } - - TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); - - CheckRowCount(); -} - -void CCtrlPages::RemovePage(int iPage) -{ - TPageInfo *p = GetItemPage(iPage); - if (p == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, iPage); - m_pages.remove(p); - delete p; - - CheckRowCount(); -} - -void CCtrlPages::ShowPage(CDlgBase *pDlg) -{ - if (pDlg->GetHwnd() == nullptr) { - pDlg->SetParent(m_hwnd); - pDlg->Create(); - - RECT rc; - GetClientRect(m_hwnd, &rc); - TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); - SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); - - EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(pDlg->GetHwnd(), SW_SHOW); -} - -void CCtrlPages::SwapPages(int idx1, int idx2) -{ - TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); - if (p1 == nullptr || p2 == nullptr) - return; - - TabCtrl_DeleteItem(m_hwnd, idx1); - - TCITEM tci = { 0 }; - tci.mask = TCIF_PARAM | TCIF_TEXT; - tci.lParam = (LPARAM)p1; - tci.pszText = TranslateW_LP(p1->m_ptszHeader); - TabCtrl_InsertItem(m_hwnd, idx2, &tci); -} - -BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) -{ - TPageInfo *info; - PSHNOTIFY pshn; - - switch (pnmh->code) { - case TCN_SELCHANGING: - if (info = GetCurrPage()) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - return TRUE; - - case TCN_SELCHANGE: - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - - if (info = GetCurrPage()) { - m_pActivePage = info->m_pDlg; - ShowPage(m_pActivePage); - } - else m_pActivePage = nullptr; - return TRUE; - } - - return FALSE; -} - -void CCtrlPages::OnReset() -{ - CSuper::OnReset(); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - } -} - -bool CCtrlPages::OnApply() -{ - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - - if (m_pActivePage != nullptr) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); - if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) - return false; - } - - pshn.hdr.code = PSN_APPLY; - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) - continue; - - pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); - SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(m_hwnd, i); - if (m_pActivePage != nullptr) - m_pActivePage->Hide(); - m_pActivePage = p->m_pDlg; - m_pActivePage->Show(); - return false; - } - } - - CSuper::OnApply(); - return true; -} - -void CCtrlPages::OnDestroy() -{ - int tabCount = GetCount(); - for (int i = 0; i < tabCount; i++) { - TPageInfo *p = GetItemPage(i); - CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; - if (pDlg->GetHwnd()) - pDlg->Close(); - delete p; - } - - TabCtrl_DeleteAllItems(m_hwnd); - - if (m_hIml) { - TabCtrl_SetImageList(m_hwnd, nullptr); - ImageList_Destroy(m_hIml); - } - - CSuper::OnDestroy(); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +static volatile long g_order = 1; + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlPages + +struct CCtrlPages::TPageInfo : public MZeroedObject +{ + TPageInfo() + { + m_iOrder = InterlockedIncrement(&g_order); + } + + ~TPageInfo() + { + if (m_hIcon) + DestroyIcon(m_hIcon); + } + + int m_iOrder; + ptrW m_ptszHeader; + HICON m_hIcon; + bool m_bChanged, m_bScheduledResize; + CDlgBase *m_pDlg; +}; + +CCtrlPages::CCtrlPages(CDlgBase *dlg, int ctrlId) + : CCtrlBase(dlg, ctrlId), + m_hIml(nullptr), + m_pActivePage(nullptr), + m_pages(4, NumericKeySortT) +{} + +void CCtrlPages::OnInit() +{ + CSuper::OnInit(); + Subclass(); + + for (auto &it : m_pages) + InsertPage(it); + m_pages.destroy(); + + ::SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, ::GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_CONTROLPARENT); + + TPageInfo *info = GetCurrPage(); + if (info) { + m_pActivePage = info->m_pDlg; + ShowPage(m_pActivePage); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } +} + +LRESULT CCtrlPages::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + int tabCount; + + switch (msg) { + case WM_SIZE: + if (TPageInfo *pCurrInfo = GetCurrPage()) { + tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p == nullptr) + continue; + if (p == pCurrInfo) { + RECT rc; + GetClientRect(m_hwnd, &rc); + TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); + SetWindowPos(p->m_pDlg->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); + } + else p->m_bScheduledResize = true; + } + } + break; + + case PSM_CHANGED: + if (TPageInfo *info = GetCurrPage()) + info->m_bChanged = TRUE; + return TRUE; + + case PSM_FORCECHANGED: + tabCount = GetCount(); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p) { + pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); + if (pshn.hdr.hwndFrom != nullptr) + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } + } + break; + } + + return CSuper::CustomWndProc(msg, wParam, lParam); +} + +void CCtrlPages::AddPage(const wchar_t *ptszName, HICON hIcon, CDlgBase *pDlg) +{ + TPageInfo *info = new TPageInfo; + info->m_pDlg = pDlg; + info->m_hIcon = hIcon; + info->m_ptszHeader = mir_wstrdup(ptszName); + + if (m_hwnd != nullptr) { + InsertPage(info); + + if (GetCount() == 1) { + m_pActivePage = info->m_pDlg; + ShowPage(m_pActivePage); + } + } + m_pages.insert(info); +} + +void CCtrlPages::ActivatePage(int iPage) +{ + TPageInfo *info = GetItemPage(iPage); + if (info == nullptr || info->m_pDlg == nullptr) + return; + + if (m_pActivePage != nullptr) + ShowWindow(m_pActivePage->GetHwnd(), SW_HIDE); + + m_pActivePage = info->m_pDlg; + if (m_pActivePage->GetHwnd() && info->m_bScheduledResize) { + RECT rc; + GetClientRect(m_hwnd, &rc); + TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); + SetWindowPos(m_pActivePage->GetHwnd(), nullptr, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); + } + + TabCtrl_SetCurSel(m_hwnd, iPage); + ShowPage(m_pActivePage); + ::SendMessage(m_pActivePage->GetHwnd(), WM_MOUSEACTIVATE, 0, 0); +} + +void CCtrlPages::CheckRowCount() +{ + int iRowCount = TabCtrl_GetRowCount(m_hwnd); + if (m_numRows != iRowCount) { + m_numRows = iRowCount; + for (auto &p : m_pages) + p->m_bScheduledResize = true; + } +} + +int CCtrlPages::GetCount() +{ + return TabCtrl_GetItemCount(m_hwnd); +} + +CDlgBase* CCtrlPages::GetNthPage(int iPage) +{ + TPageInfo *info = GetItemPage(iPage); + return (info == nullptr) ? nullptr : info->m_pDlg; +} + +CCtrlPages::TPageInfo* CCtrlPages::GetCurrPage() +{ + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM; + if (!TabCtrl_GetItem(m_hwnd, TabCtrl_GetCurSel(m_hwnd), &tci)) + return nullptr; + + return (TPageInfo*)tci.lParam; +} + +CCtrlPages::TPageInfo* CCtrlPages::GetItemPage(int iPage) +{ + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM; + if (!TabCtrl_GetItem(m_hwnd, iPage, &tci)) + return nullptr; + + return (TPageInfo*)tci.lParam; +} + +int CCtrlPages::GetDlgIndex(CDlgBase *pDlg) +{ + int tabCount = TabCtrl_GetItemCount(m_hwnd); + for (int i = 0; i < tabCount; i++) { + TCITEM tci; + tci.mask = TCIF_PARAM | TCIF_IMAGE; + TabCtrl_GetItem(m_hwnd, i, &tci); + TPageInfo *pPage = (TPageInfo *)tci.lParam; + if (pPage == nullptr) + continue; + + if (pPage->m_pDlg == pDlg) + return i; + } + + return -1; +} + +void CCtrlPages::InsertPage(TPageInfo *pPage) +{ + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM | TCIF_TEXT; + tci.lParam = (LPARAM)pPage; + tci.pszText = TranslateW_LP(pPage->m_ptszHeader); + if (pPage->m_hIcon) { + if (!m_hIml) { + m_hIml = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1); + TabCtrl_SetImageList(m_hwnd, m_hIml); + } + + tci.mask |= TCIF_IMAGE; + tci.iImage = ImageList_AddIcon(m_hIml, pPage->m_hIcon); + } + + TabCtrl_InsertItem(m_hwnd, TabCtrl_GetItemCount(m_hwnd), &tci); + + CheckRowCount(); +} + +void CCtrlPages::RemovePage(int iPage) +{ + TPageInfo *p = GetItemPage(iPage); + if (p == nullptr) + return; + + TabCtrl_DeleteItem(m_hwnd, iPage); + m_pages.remove(p); + delete p; + + CheckRowCount(); +} + +void CCtrlPages::ShowPage(CDlgBase *pDlg) +{ + if (pDlg->GetHwnd() == nullptr) { + pDlg->SetParent(m_hwnd); + pDlg->Create(); + + RECT rc; + GetClientRect(m_hwnd, &rc); + TabCtrl_AdjustRect(m_hwnd, FALSE, &rc); + SetWindowPos(pDlg->GetHwnd(), HWND_TOP, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); + + EnableThemeDialogTexture(pDlg->GetHwnd(), ETDT_ENABLETAB); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.hwndFrom = pDlg->GetHwnd(); + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } + ShowWindow(pDlg->GetHwnd(), SW_SHOW); +} + +void CCtrlPages::SwapPages(int idx1, int idx2) +{ + TPageInfo *p1 = GetItemPage(idx1), *p2 = GetItemPage(idx2); + if (p1 == nullptr || p2 == nullptr) + return; + + TabCtrl_DeleteItem(m_hwnd, idx1); + + TCITEM tci = { 0 }; + tci.mask = TCIF_PARAM | TCIF_TEXT; + tci.lParam = (LPARAM)p1; + tci.pszText = TranslateW_LP(p1->m_ptszHeader); + TabCtrl_InsertItem(m_hwnd, idx2, &tci); +} + +BOOL CCtrlPages::OnNotify(int /*idCtrl*/, NMHDR *pnmh) +{ + TPageInfo *info; + PSHNOTIFY pshn; + + switch (pnmh->code) { + case TCN_SELCHANGING: + if (info = GetCurrPage()) { + pshn.hdr.code = PSN_KILLACTIVE; + pshn.hdr.hwndFrom = info->m_pDlg->GetHwnd(); + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) { + SetWindowLongPtr(GetParent()->GetHwnd(), DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + return TRUE; + + case TCN_SELCHANGE: + if (m_pActivePage != nullptr) + m_pActivePage->Hide(); + + if (info = GetCurrPage()) { + m_pActivePage = info->m_pDlg; + ShowPage(m_pActivePage); + } + else m_pActivePage = nullptr; + return TRUE; + } + + return FALSE; +} + +void CCtrlPages::OnReset() +{ + CSuper::OnReset(); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + + int tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) + continue; + + pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + } +} + +bool CCtrlPages::OnApply() +{ + PSHNOTIFY pshn; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + + if (m_pActivePage != nullptr) { + pshn.hdr.code = PSN_KILLACTIVE; + pshn.hdr.hwndFrom = m_pActivePage->GetHwnd(); + if (SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn)) + return false; + } + + pshn.hdr.code = PSN_APPLY; + int tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + if (p->m_pDlg->GetHwnd() == nullptr || !p->m_bChanged) + continue; + + pshn.hdr.hwndFrom = p->m_pDlg->GetHwnd(); + SendMessage(pshn.hdr.hwndFrom, WM_NOTIFY, 0, (LPARAM)&pshn); + if (GetWindowLongPtr(pshn.hdr.hwndFrom, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { + TabCtrl_SetCurSel(m_hwnd, i); + if (m_pActivePage != nullptr) + m_pActivePage->Hide(); + m_pActivePage = p->m_pDlg; + m_pActivePage->Show(); + return false; + } + } + + CSuper::OnApply(); + return true; +} + +void CCtrlPages::OnDestroy() +{ + int tabCount = GetCount(); + for (int i = 0; i < tabCount; i++) { + TPageInfo *p = GetItemPage(i); + CDlgBase *pDlg = p->m_pDlg; p->m_pDlg = nullptr; + if (pDlg->GetHwnd()) + pDlg->Close(); + delete p; + } + + TabCtrl_DeleteAllItems(m_hwnd); + + if (m_hIml) { + TabCtrl_SetImageList(m_hwnd, nullptr); + ImageList_Destroy(m_hIml); + } + + CSuper::OnDestroy(); +} diff --git a/src/mir_core/src/Windows/CCtrlRichEdit.cpp b/src/mir_core/src/Windows/CCtrlRichEdit.cpp index 06bca26cb5..f41cad3f98 100644 --- a/src/mir_core/src/Windows/CCtrlRichEdit.cpp +++ b/src/mir_core/src/Windows/CCtrlRichEdit.cpp @@ -1,192 +1,192 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -#include - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlRichEdit class - -CCtrlRichEdit::CCtrlRichEdit(CDlgBase *dlg, int ctrlId) - : CCtrlEdit(dlg, ctrlId) -{} - -int CCtrlRichEdit::GetRichTextLength(int iCodePage) const -{ - GETTEXTLENGTHEX gtl; - gtl.codepage = iCodePage; - gtl.flags = GTL_PRECISE; - if (iCodePage == CP_ACP) - gtl.flags |= GTL_NUMBYTES; - else - gtl.flags |= GTL_NUMCHARS | GT_USECRLF; - - return (int)SendMessage(m_hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); -} - -int CCtrlRichEdit::SetRichText(const wchar_t *text) -{ - SETTEXTEX st; - st.flags = ST_DEFAULT; - st.codepage = 1200; - SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); - - return GetRichTextLength(1200); -} - -int CCtrlRichEdit::SetRichTextRtf(const char *text) -{ - SETTEXTEX st; - st.flags = ST_DEFAULT; - st.codepage = CP_UTF8; - SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); - - return GetRichTextLength(1200); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static DWORD CALLBACK MessageStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - static uint32_t dwRead; - char **ppText = (char **)dwCookie; - - if (*ppText == nullptr) { - *ppText = (char *)mir_alloc(cb + 2); - memcpy(*ppText, pbBuff, cb); - *pcb = cb; - dwRead = cb; - *(*ppText + cb) = '\0'; - } - else { - char *p = (char *)mir_realloc(*ppText, dwRead + cb + 2); - memcpy(p + dwRead, pbBuff, cb); - *ppText = p; - *pcb = cb; - dwRead += cb; - *(*ppText + dwRead) = '\0'; - } - return 0; -} - -char* CCtrlRichEdit::GetRichTextRtf(bool bText, bool bSelection) const -{ - char *pszText = nullptr; - uint32_t dwFlags = SF_USECODEPAGE | (CP_UTF8 << 16); - if (bText) - dwFlags |= SF_TEXT; - else - dwFlags |= SF_RTFNOOBJS | SFF_PLAINRTF; - if (bSelection) - dwFlags |= SFF_SELECTION; - - EDITSTREAM stream = { 0 }; - stream.pfnCallback = MessageStreamCallback; - stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer - SendMessage(m_hwnd, EM_STREAMOUT, dwFlags, (LPARAM)&stream); - return pszText; // pszText contains the text -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -struct CREOleCallback : public IRichEditOleCallback -{ - CREOleCallback() : refCount(0), nextStgId(0), pictStg(nullptr) {} - unsigned refCount; - IStorage *pictStg; - int nextStgId; - - STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR *ppvObj) - { - if (IsEqualIID(riid, IID_IRichEditOleCallback)) { - *ppvObj = this; - AddRef(); - return S_OK; - } - *ppvObj = nullptr; - return E_NOINTERFACE; - } - - STDMETHOD_(ULONG, AddRef)(THIS) - { - if (refCount == 0) - StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &pictStg); - - return ++refCount; - } - - STDMETHOD_(ULONG, Release)(THIS) - { - if (--refCount == 0) { - if (pictStg) { - pictStg->Release(); - pictStg = nullptr; - } - } - return refCount; - } - - STDMETHOD(GetNewStorage)(LPSTORAGE *lplpstg) - { - wchar_t sztName[64]; - mir_snwprintf(sztName, L"s%u", nextStgId++); - if (pictStg == nullptr) - return STG_E_MEDIUMFULL; - return pictStg->CreateStorage(sztName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg); - } - - STDMETHOD(ContextSensitiveHelp)(BOOL) - { return S_OK; } - STDMETHOD(GetInPlaceContext)(LPOLEINPLACEFRAME*, LPOLEINPLACEUIWINDOW*, LPOLEINPLACEFRAMEINFO) - { return E_INVALIDARG; } - STDMETHOD(ShowContainerUI)(BOOL) - { return S_OK; } - STDMETHOD(QueryInsertObject)(LPCLSID, LPSTORAGE, LONG) - { return S_OK; } - STDMETHOD(DeleteObject)(LPOLEOBJECT) - { return S_OK; } - STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT*, DWORD, BOOL, HGLOBAL) - { return S_OK; } - STDMETHOD(GetClipboardData)(CHARRANGE*, DWORD, LPDATAOBJECT*) - { return E_NOTIMPL; } - STDMETHOD(GetDragDropEffect)(BOOL, DWORD, LPDWORD) - { return S_OK; } - STDMETHOD(GetContextMenu)(uint16_t, LPOLEOBJECT, CHARRANGE*, HMENU*) - { return E_INVALIDARG; } -}; - -struct CREOleCallback2 : public CREOleCallback -{ - STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT *lpcfFormat, DWORD, BOOL, HGLOBAL) - { *lpcfFormat = CF_UNICODETEXT; - return S_OK; - } -}; - -CREOleCallback reOleCallback; -CREOleCallback2 reOleCallback2; - -void CCtrlRichEdit::SetReadOnly(bool bReadOnly) -{ - SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)(bReadOnly ? &reOleCallback : &reOleCallback2)); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlRichEdit class + +CCtrlRichEdit::CCtrlRichEdit(CDlgBase *dlg, int ctrlId) + : CCtrlEdit(dlg, ctrlId) +{} + +int CCtrlRichEdit::GetRichTextLength(int iCodePage) const +{ + GETTEXTLENGTHEX gtl; + gtl.codepage = iCodePage; + gtl.flags = GTL_PRECISE; + if (iCodePage == CP_ACP) + gtl.flags |= GTL_NUMBYTES; + else + gtl.flags |= GTL_NUMCHARS | GT_USECRLF; + + return (int)SendMessage(m_hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); +} + +int CCtrlRichEdit::SetRichText(const wchar_t *text) +{ + SETTEXTEX st; + st.flags = ST_DEFAULT; + st.codepage = 1200; + SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); + + return GetRichTextLength(1200); +} + +int CCtrlRichEdit::SetRichTextRtf(const char *text) +{ + SETTEXTEX st; + st.flags = ST_DEFAULT; + st.codepage = CP_UTF8; + SendMessage(m_hwnd, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)text); + + return GetRichTextLength(1200); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static DWORD CALLBACK MessageStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + static uint32_t dwRead; + char **ppText = (char **)dwCookie; + + if (*ppText == nullptr) { + *ppText = (char *)mir_alloc(cb + 2); + memcpy(*ppText, pbBuff, cb); + *pcb = cb; + dwRead = cb; + *(*ppText + cb) = '\0'; + } + else { + char *p = (char *)mir_realloc(*ppText, dwRead + cb + 2); + memcpy(p + dwRead, pbBuff, cb); + *ppText = p; + *pcb = cb; + dwRead += cb; + *(*ppText + dwRead) = '\0'; + } + return 0; +} + +char* CCtrlRichEdit::GetRichTextRtf(bool bText, bool bSelection) const +{ + char *pszText = nullptr; + uint32_t dwFlags = SF_USECODEPAGE | (CP_UTF8 << 16); + if (bText) + dwFlags |= SF_TEXT; + else + dwFlags |= SF_RTFNOOBJS | SFF_PLAINRTF; + if (bSelection) + dwFlags |= SFF_SELECTION; + + EDITSTREAM stream = { 0 }; + stream.pfnCallback = MessageStreamCallback; + stream.dwCookie = (DWORD_PTR)&pszText; // pass pointer to pointer + SendMessage(m_hwnd, EM_STREAMOUT, dwFlags, (LPARAM)&stream); + return pszText; // pszText contains the text +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct CREOleCallback : public IRichEditOleCallback +{ + CREOleCallback() : refCount(0), nextStgId(0), pictStg(nullptr) {} + unsigned refCount; + IStorage *pictStg; + int nextStgId; + + STDMETHOD(QueryInterface)(REFIID riid, LPVOID FAR *ppvObj) + { + if (IsEqualIID(riid, IID_IRichEditOleCallback)) { + *ppvObj = this; + AddRef(); + return S_OK; + } + *ppvObj = nullptr; + return E_NOINTERFACE; + } + + STDMETHOD_(ULONG, AddRef)(THIS) + { + if (refCount == 0) + StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &pictStg); + + return ++refCount; + } + + STDMETHOD_(ULONG, Release)(THIS) + { + if (--refCount == 0) { + if (pictStg) { + pictStg->Release(); + pictStg = nullptr; + } + } + return refCount; + } + + STDMETHOD(GetNewStorage)(LPSTORAGE *lplpstg) + { + wchar_t sztName[64]; + mir_snwprintf(sztName, L"s%u", nextStgId++); + if (pictStg == nullptr) + return STG_E_MEDIUMFULL; + return pictStg->CreateStorage(sztName, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, 0, lplpstg); + } + + STDMETHOD(ContextSensitiveHelp)(BOOL) + { return S_OK; } + STDMETHOD(GetInPlaceContext)(LPOLEINPLACEFRAME*, LPOLEINPLACEUIWINDOW*, LPOLEINPLACEFRAMEINFO) + { return E_INVALIDARG; } + STDMETHOD(ShowContainerUI)(BOOL) + { return S_OK; } + STDMETHOD(QueryInsertObject)(LPCLSID, LPSTORAGE, LONG) + { return S_OK; } + STDMETHOD(DeleteObject)(LPOLEOBJECT) + { return S_OK; } + STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT*, DWORD, BOOL, HGLOBAL) + { return S_OK; } + STDMETHOD(GetClipboardData)(CHARRANGE*, DWORD, LPDATAOBJECT*) + { return E_NOTIMPL; } + STDMETHOD(GetDragDropEffect)(BOOL, DWORD, LPDWORD) + { return S_OK; } + STDMETHOD(GetContextMenu)(uint16_t, LPOLEOBJECT, CHARRANGE*, HMENU*) + { return E_INVALIDARG; } +}; + +struct CREOleCallback2 : public CREOleCallback +{ + STDMETHOD(QueryAcceptData)(LPDATAOBJECT, CLIPFORMAT *lpcfFormat, DWORD, BOOL, HGLOBAL) + { *lpcfFormat = CF_UNICODETEXT; + return S_OK; + } +}; + +CREOleCallback reOleCallback; +CREOleCallback2 reOleCallback2; + +void CCtrlRichEdit::SetReadOnly(bool bReadOnly) +{ + SendMsg(EM_SETOLECALLBACK, 0, (LPARAM)(bReadOnly ? &reOleCallback : &reOleCallback2)); +} diff --git a/src/mir_core/src/Windows/CCtrlSlider.cpp b/src/mir_core/src/Windows/CCtrlSlider.cpp index 69aeb24796..9938736e42 100644 --- a/src/mir_core/src/Windows/CCtrlSlider.cpp +++ b/src/mir_core/src/Windows/CCtrlSlider.cpp @@ -1,70 +1,70 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSlider class - -CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax) -{ - m_bNotifiable = true; -} - -BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) -{ - if (idCode == WM_HSCROLL) { - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSlider::OnApply() -{ - CSuper::OnApply(); - - if (m_dbLink != nullptr) - SaveInt(GetPosition()); - return true; -} - -void CCtrlSlider::OnReset() -{ - SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int CCtrlSlider::GetPosition() const -{ - return SendMsg(TBM_GETPOS, 0, 0); -} - -void CCtrlSlider::SetPosition(int wPos) -{ - SendMsg(TBM_SETPOS, TRUE, wPos); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlSlider class + +CCtrlSlider::CCtrlSlider(CDlgBase *dlg, int ctrlId, int wMax, int wMin) : + CCtrlData(dlg, ctrlId), + m_wMin(wMin), + m_wMax(wMax) +{ + m_bNotifiable = true; +} + +BOOL CCtrlSlider::OnCommand(HWND, uint16_t, uint16_t idCode) +{ + if (idCode == WM_HSCROLL) { + NotifyChange(); + return TRUE; + } + return FALSE; +} + +bool CCtrlSlider::OnApply() +{ + CSuper::OnApply(); + + if (m_dbLink != nullptr) + SaveInt(GetPosition()); + return true; +} + +void CCtrlSlider::OnReset() +{ + SendMsg(TBM_SETRANGE, 0, MAKELONG(m_wMin, m_wMax)); + + if (m_dbLink != nullptr) + SetPosition(LoadInt()); +} + +int CCtrlSlider::GetPosition() const +{ + return SendMsg(TBM_GETPOS, 0, 0); +} + +void CCtrlSlider::SetPosition(int wPos) +{ + SendMsg(TBM_SETPOS, TRUE, wPos); +} diff --git a/src/mir_core/src/Windows/CCtrlSpin.cpp b/src/mir_core/src/Windows/CCtrlSpin.cpp index 54d43e933a..54dc5ffd07 100644 --- a/src/mir_core/src/Windows/CCtrlSpin.cpp +++ b/src/mir_core/src/Windows/CCtrlSpin.cpp @@ -1,81 +1,81 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlSpin class - -CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : - CCtrlData(dlg, ctrlId), - m_wMin(wMin), - m_wMax(wMax), - m_wCurr(0) -{} - -BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) -{ - if (pnmh->code == UDN_DELTAPOS) { - auto *pEvent = (NMUPDOWN *)pnmh; - m_wCurr = pEvent->iPos + pEvent->iDelta; - - NotifyChange(); - return TRUE; - } - return FALSE; -} - -bool CCtrlSpin::OnApply() -{ - CSuper::OnApply(); - - m_wCurr = SendMsg(UDM_GETPOS, 0, 0); - if (m_dbLink != nullptr) - SaveInt(m_wCurr); - - HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); - if (hwndBuddy) { - wchar_t buf[100]; - _itow(m_wCurr, buf, 10); - ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); - } - - return true; -} - -void CCtrlSpin::OnReset() -{ - SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); - - if (m_dbLink != nullptr) - SetPosition(LoadInt()); -} - -int16_t CCtrlSpin::GetPosition() -{ - return m_wCurr; -} - -void CCtrlSpin::SetPosition(int16_t wPos) -{ - SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlSpin class + +CCtrlSpin::CCtrlSpin(CDlgBase *dlg, int ctrlId, int16_t wMax, int16_t wMin) : + CCtrlData(dlg, ctrlId), + m_wMin(wMin), + m_wMax(wMax), + m_wCurr(0) +{} + +BOOL CCtrlSpin::OnNotify(int, NMHDR *pnmh) +{ + if (pnmh->code == UDN_DELTAPOS) { + auto *pEvent = (NMUPDOWN *)pnmh; + m_wCurr = pEvent->iPos + pEvent->iDelta; + + NotifyChange(); + return TRUE; + } + return FALSE; +} + +bool CCtrlSpin::OnApply() +{ + CSuper::OnApply(); + + m_wCurr = SendMsg(UDM_GETPOS, 0, 0); + if (m_dbLink != nullptr) + SaveInt(m_wCurr); + + HWND hwndBuddy = (HWND)SendMsg(UDM_GETBUDDY, 0, 0); + if (hwndBuddy) { + wchar_t buf[100]; + _itow(m_wCurr, buf, 10); + ::SendMessage(hwndBuddy, WM_SETTEXT, 0, LPARAM(buf)); + } + + return true; +} + +void CCtrlSpin::OnReset() +{ + SendMsg(UDM_SETRANGE, 0, MAKELPARAM(m_wMax, m_wMin)); + + if (m_dbLink != nullptr) + SetPosition(LoadInt()); +} + +int16_t CCtrlSpin::GetPosition() +{ + return m_wCurr; +} + +void CCtrlSpin::SetPosition(int16_t wPos) +{ + SendMsg(UDM_SETPOS, 0, m_wCurr = wPos); +} diff --git a/src/mir_core/src/Windows/CCtrlTreeOpts.cpp b/src/mir_core/src/Windows/CCtrlTreeOpts.cpp index d15f3d4b1b..7935283285 100644 --- a/src/mir_core/src/Windows/CCtrlTreeOpts.cpp +++ b/src/mir_core/src/Windows/CCtrlTreeOpts.cpp @@ -2,7 +2,7 @@ Object UI extensions Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/mir_core/src/Windows/CCtrlTreeView.cpp b/src/mir_core/src/Windows/CCtrlTreeView.cpp index 6f435920f2..178b6b730e 100644 --- a/src/mir_core/src/Windows/CCtrlTreeView.cpp +++ b/src/mir_core/src/Windows/CCtrlTreeView.cpp @@ -2,7 +2,7 @@ Object UI extensions Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team +Copyright (C) 2012-23 Miranda NG team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/mir_core/src/Windows/CDbLink.cpp b/src/mir_core/src/Windows/CDbLink.cpp index 2a0734cd3b..05925f871b 100644 --- a/src/mir_core/src/Windows/CDbLink.cpp +++ b/src/mir_core/src/Windows/CDbLink.cpp @@ -1,92 +1,92 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CDbLink class - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) - : CDataLink(type) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_iDefault = iValue; - m_szDefault = nullptr; - dbv.type = DBVT_DELETED; -} - -CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) - : CDataLink(type), - m_iDefault(0) -{ - m_szModule = mir_strdup(szModule); - m_szSetting = mir_strdup(szSetting); - m_szDefault = mir_wstrdup(szValue); - dbv.type = DBVT_DELETED; -} - -CDbLink::~CDbLink() -{ - mir_free(m_szModule); - mir_free(m_szSetting); - mir_free(m_szDefault); - if (dbv.type != DBVT_DELETED) - db_free(&dbv); -} - -uint32_t CDbLink::LoadInt() -{ - switch (m_type) { - case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); - case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); - default: return m_iDefault; - } -} - -void CDbLink::SaveInt(uint32_t value) -{ - switch (m_type) { - case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; - case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; - case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; - } -} - -wchar_t* CDbLink::LoadText() -{ - if (dbv.type != DBVT_DELETED) db_free(&dbv); - if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { - if (dbv.type == DBVT_WCHAR) - return dbv.pwszVal; - return m_szDefault; - } - - dbv.type = DBVT_DELETED; - return m_szDefault; -} - -void CDbLink::SaveText(wchar_t *value) -{ - db_set_ws(0, m_szModule, m_szSetting, value); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CDbLink class + +CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, uint32_t iValue) + : CDataLink(type) +{ + m_szModule = mir_strdup(szModule); + m_szSetting = mir_strdup(szSetting); + m_iDefault = iValue; + m_szDefault = nullptr; + dbv.type = DBVT_DELETED; +} + +CDbLink::CDbLink(const char *szModule, const char *szSetting, uint8_t type, wchar_t *szValue) + : CDataLink(type), + m_iDefault(0) +{ + m_szModule = mir_strdup(szModule); + m_szSetting = mir_strdup(szSetting); + m_szDefault = mir_wstrdup(szValue); + dbv.type = DBVT_DELETED; +} + +CDbLink::~CDbLink() +{ + mir_free(m_szModule); + mir_free(m_szSetting); + mir_free(m_szDefault); + if (dbv.type != DBVT_DELETED) + db_free(&dbv); +} + +uint32_t CDbLink::LoadInt() +{ + switch (m_type) { + case DBVT_BYTE: return db_get_b(0, m_szModule, m_szSetting, m_iDefault); + case DBVT_WORD: return db_get_w(0, m_szModule, m_szSetting, m_iDefault); + case DBVT_DWORD: return db_get_dw(0, m_szModule, m_szSetting, m_iDefault); + default: return m_iDefault; + } +} + +void CDbLink::SaveInt(uint32_t value) +{ + switch (m_type) { + case DBVT_BYTE: db_set_b(0, m_szModule, m_szSetting, (uint8_t)value); break; + case DBVT_WORD: db_set_w(0, m_szModule, m_szSetting, (uint16_t)value); break; + case DBVT_DWORD: db_set_dw(0, m_szModule, m_szSetting, value); break; + } +} + +wchar_t* CDbLink::LoadText() +{ + if (dbv.type != DBVT_DELETED) db_free(&dbv); + if (!db_get_ws(0, m_szModule, m_szSetting, &dbv)) { + if (dbv.type == DBVT_WCHAR) + return dbv.pwszVal; + return m_szDefault; + } + + dbv.type = DBVT_DELETED; + return m_szDefault; +} + +void CDbLink::SaveText(wchar_t *value) +{ + db_set_ws(0, m_szModule, m_szSetting, value); +} diff --git a/src/mir_core/src/Windows/CDlgBase.cpp b/src/mir_core/src/Windows/CDlgBase.cpp index 84066de414..45ae944e21 100644 --- a/src/mir_core/src/Windows/CDlgBase.cpp +++ b/src/mir_core/src/Windows/CDlgBase.cpp @@ -1,522 +1,522 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" -#include "diatheme.h" - -static mir_cs csDialogs; - -static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) -{ - return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); -} -static LIST arDialogs(10, CompareDialogs); - -#pragma comment(lib, "uxtheme") - -///////////////////////////////////////////////////////////////////////////////////////// -// CDlgBase - -static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) -{ - return c1->GetCtrlId() - c2->GetCtrlId(); -} - -static int CompareTimerId(const CTimer *t1, const CTimer *t2) -{ - return t1->GetEventId() - t2->GetEventId(); -} - -CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) : - m_controls(1, CompareControlId), - m_timers(1, CompareTimerId), - m_pPlugin(pPlug), - m_bFixedSize(!g_bEnableDpiAware) -{ - m_idDialog = idDialog; - m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; -} - -CDlgBase::~CDlgBase() -{ - m_bInitialized = false; // prevent double call of destructor - if (m_hwnd) - DestroyWindow(m_hwnd); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// events - -bool CDlgBase::OnInitDialog() -{ - return true; -} - -bool CDlgBase::OnClose() -{ - return true; -} - -bool CDlgBase::OnApply() -{ - return true; -} - -void CDlgBase::OnChange() -{} - -void CDlgBase::OnDestroy() -{} - -void CDlgBase::OnReset() -{} - -void CDlgBase::OnTimer(CTimer*) -{} - -///////////////////////////////////////////////////////////////////////////////////////// -// methods - -void CDlgBase::Close() -{ - ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); -} - -void CDlgBase::Create() -{ - if (!m_bFixedSize) { - mir_ptr pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); - CreateDialogIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); - } - else CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -int CDlgBase::DoModal() -{ - m_isModal = true; - - if (m_bFixedSize) - return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); - - mir_ptr pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); - return DialogBoxIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); -} - -void CDlgBase::EndModal(INT_PTR nResult) -{ - ::EndDialog(m_hwnd, nResult); -} - -HINSTANCE CDlgBase::GetInst() const -{ - return m_pPlugin.getInst(); -} - -void CDlgBase::NotifyChange(void) -{ - if (!m_bInitialized) - return; - - OnChange(); - - if (m_hwndParent) - SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); -} - -void CDlgBase::Resize() -{ - SendMessage(m_hwnd, WM_SIZE, 0, 0); -} - -void CDlgBase::SetCaption(const wchar_t *ptszCaption) -{ - if (m_hwnd && ptszCaption) - SetWindowText(m_hwnd, ptszCaption); -} - -void CDlgBase::SetDraw(bool bEnable) -{ - ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); -} - -void CDlgBase::Show(int nCmdShow) -{ - if (m_hwnd == nullptr) - Create(); - ShowWindow(m_hwnd, nCmdShow); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); -} - -void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) -{ - ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// virtual methods - -int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc) -{ - CDlgBase *wnd = CDlgBase::Find(hwnd); - return (wnd == nullptr) ? 0 : wnd->Resizer(urc); -} - -void CDlgBase::OnResize() -{ - if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_THICKFRAME)) - Utils_ResizeDialog(m_hwnd, m_pPlugin.getInst(), MAKEINTRESOURCEA(m_idDialog), GlobalDlgResizer); -} - -int CDlgBase::Resizer(UTILRESIZECONTROL*) -{ - return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; -} - -BOOL CALLBACK CDlgBase::GlobalFieldEnum(HWND hwnd, LPARAM lParam) -{ - CDlgBase *pDlg = (CDlgBase*)lParam; - int id = GetWindowLongPtrW(hwnd, GWLP_ID); - if (id <= 0) - return TRUE; - - // already declared inside the class? skipping - CCtrlBase *ctrl = pDlg->FindControl(id); - if (ctrl != nullptr) - return TRUE; - - wchar_t wszClass[100]; - GetClassNameW(hwnd, wszClass, _countof(wszClass)); - if (!wcsicmp(wszClass, L"Static")) - new CCtrlLabel(pDlg, id); - if (!wcsicmp(wszClass, L"Edit")) - new CCtrlEdit(pDlg, id); - else if (!wcsicmp(wszClass, L"ComboBox")) - new CCtrlCombo(pDlg, id); - else if (!wcsicmp(wszClass, L"Button")) { - switch (GetWindowLongW(hwnd, GWL_STYLE) & (BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON)) { - case BS_CHECKBOX: - case BS_AUTOCHECKBOX: - case BS_RADIOBUTTON: - case BS_AUTORADIOBUTTON: - new CCtrlCheck(pDlg, id); - break; - - default: - new CCtrlButton(pDlg, id); - } - } - else if (!wcsicmp(wszClass, L"RichEdit50W")) - new CCtrlRichEdit(pDlg, id); - else if (!wcsicmp(wszClass, L"msctls_updown32")) - new CCtrlSpin(pDlg, id); - - return TRUE; -} - -INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_INITDIALOG: - m_bInitialized = m_bSucceeded = false; - TranslateDialog_LP(m_hwnd, &m_pPlugin); - - ::EnumChildWindows(m_hwnd, &GlobalFieldEnum, LPARAM(this)); - - NotifyControls(&CCtrlBase::OnInit); - if (!OnInitDialog()) - return FALSE; - - for (auto &it : m_controls) - if (it->m_bNotifiable) - it->OnChange(it); - - m_bInitialized = true; - return TRUE; - - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - if (CCtrlBase *ctrl = FindControl(HWND(lParam))) { - if (ctrl->m_bUseSystemColors) { - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - } - } - break; - - case WM_GETMINMAXINFO: - if (m_iMinHeight != -1 && m_iMinWidth != -1) { - MINMAXINFO *lpmmi = (MINMAXINFO*)lParam; - lpmmi->ptMinTrackSize.y = m_iMinHeight; - lpmmi->ptMinTrackSize.x = m_iMinWidth; - return 0; - } - break; - - case WM_MEASUREITEM: - if (!Menu_MeasureItem(lParam)) { - MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnMeasureItem(param); - } - return FALSE; - - case WM_DRAWITEM: - if (!Menu_DrawItem(lParam)) { - DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnDrawItem(param); - } - return FALSE; - - case WM_DELETEITEM: - { - DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam; - if (param && param->CtlID) - if (CCtrlBase *ctrl = FindControl(param->CtlID)) - return ctrl->OnDeleteItem(param); - } - return FALSE; - - case WM_COMMAND: - { - HWND hwndCtrl = (HWND)lParam; - uint16_t idCtrl = LOWORD(wParam); - uint16_t idCode = HIWORD(wParam); - if (CCtrlBase *ctrl = FindControl(idCtrl)) { - BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode); - if (result != FALSE) - return result; - } - - if (idCode == BN_CLICKED) { - // close dialog automatically if 'Cancel' button is pressed - if (idCtrl == IDCANCEL && (m_autoClose & CLOSE_ON_CANCEL)) { - m_bExiting = true; - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - } - - // close dialog automatically if 'OK' button is pressed - if (idCtrl == IDOK && (m_autoClose & CLOSE_ON_OK)) { - // validate dialog data first - if (VerifyControls(&CCtrlBase::OnApply)) { - m_bExiting = true; - - // everything ok? good, let's close it - if (OnApply()) { - m_bSucceeded = true; - PostMessage(m_hwnd, WM_CLOSE, 0, 0); - } - else m_bExiting = false; - } - } - } - } - return FALSE; - - case WM_NOTIFY: - { - int idCtrl = wParam; - NMHDR *pnmh = (NMHDR *)lParam; - if (pnmh->idFrom == 0) { - switch (pnmh->code) { - case PSN_APPLY: - if (LPPSHNOTIFY(lParam)->lParam != 3) // IDC_APPLY - m_bExiting = true; - - if (!VerifyControls(&CCtrlBase::OnApply)) - m_bExiting = false; - else if (!OnApply()) - m_bExiting = false; - break; - - case PSN_RESET: - NotifyControls(&CCtrlBase::OnReset); - OnReset(); - break; - - case PSN_WIZFINISH: - m_OnFinishWizard(this); - break; - } - } - - if (CCtrlBase *ctrl = FindControl(pnmh->idFrom)) - return ctrl->OnNotify(idCtrl, pnmh); - } - return FALSE; - - case WM_HSCROLL: - if (auto *pCtrl = FindControl(HWND(lParam))) - pCtrl->OnCommand(HWND(lParam), pCtrl->m_idCtrl, WM_HSCROLL); - break; - - case PSM_CHANGED: - if (m_bInitialized) - OnChange(); - break; - - case WM_CONTEXTMENU: - if (CCtrlBase *ctrl = FindControl(HWND(wParam))) { - CContextMenuPos pos = {}; - if (lParam != -1) { - pos.pt.x = GET_X_LPARAM(lParam); - pos.pt.y = GET_Y_LPARAM(lParam); - } - ctrl->GetCaretPos(pos); - ctrl->OnBuildMenu(&pos); - } - break; - - case WM_SIZE: - OnResize(); - return TRUE; - - case WM_TIMER: - if (CTimer *timer = FindTimer(wParam)) - return timer->OnTimer(); - return FALSE; - - case WM_CLOSE: - if (OnClose()) { - m_bExiting = true; - if (m_isModal) - EndModal(m_bSucceeded ? IDOK : FALSE); - else - DestroyWindow(m_hwnd); - } - return TRUE; - - case WM_DESTROY: - m_bExiting = true; - OnDestroy(); - NotifyControls(&CCtrlBase::OnDestroy); - { - mir_cslock lck(csDialogs); - int idx = arDialogs.getIndex(this); - if (idx != -1) - arDialogs.remove(idx); - } - m_hwnd = nullptr; - if (m_bInitialized) { - if (m_isModal) - m_isModal = false; - else // modeless dialogs MUST be allocated with 'new' - delete this; - } - - return TRUE; - } - - return FALSE; -} - -INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - CDlgBase *wnd; - if (msg == WM_INITDIALOG) { - wnd = (CDlgBase*)lParam; - wnd->m_hwnd = hwnd; - - mir_cslock lck(csDialogs); - arDialogs.insert(wnd); - } - else wnd = CDlgBase::Find(hwnd); - - return (wnd == nullptr) ? FALSE : wnd->DlgProc(msg, wParam, lParam); -} - -void CDlgBase::ThemeDialogBackground(BOOL tabbed) -{ - EnableThemeDialogTexture(m_hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE); -} - -void CDlgBase::AddControl(CCtrlBase *ctrl) -{ - m_controls.insert(ctrl); -} - -void CDlgBase::RemoveControl(CCtrlBase *ctrl) -{ - m_controls.remove(ctrl); -} - -void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - (it->*fn)(); -} - -bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) -{ - for (auto &it : m_controls) - if (!(it->*fn)()) - return false; - - return true; -} - -CCtrlBase* CDlgBase::FindControl(int idCtrl) -{ - CCtrlBase search(nullptr, idCtrl); - return m_controls.find(&search); -} - -CCtrlBase* CDlgBase::FindControl(HWND hwnd) -{ - for (auto &it : m_controls) - if (it->GetHwnd() == hwnd) - return it; - - return nullptr; -} - -void CDlgBase::AddTimer(CTimer *timer) -{ - m_timers.insert(timer); -} - -void CDlgBase::RemoveTimer(UINT_PTR idEvent) -{ - CTimer search(nullptr, idEvent); - m_timers.remove(&search); -} - -CTimer* CDlgBase::FindTimer(int idEvent) -{ - CTimer search(nullptr, idEvent); - return m_timers.find(&search); -} - -CDlgBase* CDlgBase::Find(HWND hwnd) -{ - PVOID bullshit[2]; // vfptr + hwnd - bullshit[1] = hwnd; - return arDialogs.find((CDlgBase*)&bullshit); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" +#include "diatheme.h" + +static mir_cs csDialogs; + +static int CompareDialogs(const CDlgBase *p1, const CDlgBase *p2) +{ + return (INT_PTR)p1->GetHwnd() - (INT_PTR)p2->GetHwnd(); +} +static LIST arDialogs(10, CompareDialogs); + +#pragma comment(lib, "uxtheme") + +///////////////////////////////////////////////////////////////////////////////////////// +// CDlgBase + +static int CompareControlId(const CCtrlBase *c1, const CCtrlBase *c2) +{ + return c1->GetCtrlId() - c2->GetCtrlId(); +} + +static int CompareTimerId(const CTimer *t1, const CTimer *t2) +{ + return t1->GetEventId() - t2->GetEventId(); +} + +CDlgBase::CDlgBase(CMPluginBase &pPlug, int idDialog) : + m_controls(1, CompareControlId), + m_timers(1, CompareTimerId), + m_pPlugin(pPlug), + m_bFixedSize(!g_bEnableDpiAware) +{ + m_idDialog = idDialog; + m_autoClose = CLOSE_ON_OK | CLOSE_ON_CANCEL; +} + +CDlgBase::~CDlgBase() +{ + m_bInitialized = false; // prevent double call of destructor + if (m_hwnd) + DestroyWindow(m_hwnd); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// events + +bool CDlgBase::OnInitDialog() +{ + return true; +} + +bool CDlgBase::OnClose() +{ + return true; +} + +bool CDlgBase::OnApply() +{ + return true; +} + +void CDlgBase::OnChange() +{} + +void CDlgBase::OnDestroy() +{} + +void CDlgBase::OnReset() +{} + +void CDlgBase::OnTimer(CTimer*) +{} + +///////////////////////////////////////////////////////////////////////////////////////// +// methods + +void CDlgBase::Close() +{ + ::SendMessage(m_hwnd, WM_CLOSE, 0, 0); +} + +void CDlgBase::Create() +{ + if (!m_bFixedSize) { + mir_ptr pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); + CreateDialogIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); + } + else CreateDialogParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); +} + +int CDlgBase::DoModal() +{ + m_isModal = true; + + if (m_bFixedSize) + return DialogBoxParam(GetInst(), MAKEINTRESOURCE(m_idDialog), m_hwndParent, GlobalDlgProc, (LPARAM)this); + + mir_ptr pDlgTemplate(LoadThemedDialogTemplate(MAKEINTRESOURCE(m_idDialog), GetInst())); + return DialogBoxIndirectParam(GetInst(), pDlgTemplate, m_hwndParent, GlobalDlgProc, (LPARAM)this); +} + +void CDlgBase::EndModal(INT_PTR nResult) +{ + ::EndDialog(m_hwnd, nResult); +} + +HINSTANCE CDlgBase::GetInst() const +{ + return m_pPlugin.getInst(); +} + +void CDlgBase::NotifyChange(void) +{ + if (!m_bInitialized) + return; + + OnChange(); + + if (m_hwndParent) + SendMessage(m_hwndParent, PSM_CHANGED, (WPARAM)m_hwnd, 0); +} + +void CDlgBase::Resize() +{ + SendMessage(m_hwnd, WM_SIZE, 0, 0); +} + +void CDlgBase::SetCaption(const wchar_t *ptszCaption) +{ + if (m_hwnd && ptszCaption) + SetWindowText(m_hwnd, ptszCaption); +} + +void CDlgBase::SetDraw(bool bEnable) +{ + ::SendMessage(m_hwnd, WM_SETREDRAW, bEnable, 0); +} + +void CDlgBase::Show(int nCmdShow) +{ + if (m_hwnd == nullptr) + Create(); + ShowWindow(m_hwnd, nCmdShow); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, uint8_t type, uint32_t iValue) +{ + ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, type, iValue); +} + +void CDlgBase::CreateLink(CCtrlData& ctrl, const char *szSetting, wchar_t *szValue) +{ + ctrl.CreateDbLink(m_pPlugin.getModule(), szSetting, szValue); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// virtual methods + +int CDlgBase::GlobalDlgResizer(HWND hwnd, LPARAM, UTILRESIZECONTROL *urc) +{ + CDlgBase *wnd = CDlgBase::Find(hwnd); + return (wnd == nullptr) ? 0 : wnd->Resizer(urc); +} + +void CDlgBase::OnResize() +{ + if (m_forceResizable || (GetWindowLongPtr(m_hwnd, GWL_STYLE) & WS_THICKFRAME)) + Utils_ResizeDialog(m_hwnd, m_pPlugin.getInst(), MAKEINTRESOURCEA(m_idDialog), GlobalDlgResizer); +} + +int CDlgBase::Resizer(UTILRESIZECONTROL*) +{ + return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; +} + +BOOL CALLBACK CDlgBase::GlobalFieldEnum(HWND hwnd, LPARAM lParam) +{ + CDlgBase *pDlg = (CDlgBase*)lParam; + int id = GetWindowLongPtrW(hwnd, GWLP_ID); + if (id <= 0) + return TRUE; + + // already declared inside the class? skipping + CCtrlBase *ctrl = pDlg->FindControl(id); + if (ctrl != nullptr) + return TRUE; + + wchar_t wszClass[100]; + GetClassNameW(hwnd, wszClass, _countof(wszClass)); + if (!wcsicmp(wszClass, L"Static")) + new CCtrlLabel(pDlg, id); + if (!wcsicmp(wszClass, L"Edit")) + new CCtrlEdit(pDlg, id); + else if (!wcsicmp(wszClass, L"ComboBox")) + new CCtrlCombo(pDlg, id); + else if (!wcsicmp(wszClass, L"Button")) { + switch (GetWindowLongW(hwnd, GWL_STYLE) & (BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON)) { + case BS_CHECKBOX: + case BS_AUTOCHECKBOX: + case BS_RADIOBUTTON: + case BS_AUTORADIOBUTTON: + new CCtrlCheck(pDlg, id); + break; + + default: + new CCtrlButton(pDlg, id); + } + } + else if (!wcsicmp(wszClass, L"RichEdit50W")) + new CCtrlRichEdit(pDlg, id); + else if (!wcsicmp(wszClass, L"msctls_updown32")) + new CCtrlSpin(pDlg, id); + + return TRUE; +} + +INT_PTR CDlgBase::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + m_bInitialized = m_bSucceeded = false; + TranslateDialog_LP(m_hwnd, &m_pPlugin); + + ::EnumChildWindows(m_hwnd, &GlobalFieldEnum, LPARAM(this)); + + NotifyControls(&CCtrlBase::OnInit); + if (!OnInitDialog()) + return FALSE; + + for (auto &it : m_controls) + if (it->m_bNotifiable) + it->OnChange(it); + + m_bInitialized = true; + return TRUE; + + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + if (CCtrlBase *ctrl = FindControl(HWND(lParam))) { + if (ctrl->m_bUseSystemColors) { + SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); + } + } + break; + + case WM_GETMINMAXINFO: + if (m_iMinHeight != -1 && m_iMinWidth != -1) { + MINMAXINFO *lpmmi = (MINMAXINFO*)lParam; + lpmmi->ptMinTrackSize.y = m_iMinHeight; + lpmmi->ptMinTrackSize.x = m_iMinWidth; + return 0; + } + break; + + case WM_MEASUREITEM: + if (!Menu_MeasureItem(lParam)) { + MEASUREITEMSTRUCT *param = (MEASUREITEMSTRUCT *)lParam; + if (param && param->CtlID) + if (CCtrlBase *ctrl = FindControl(param->CtlID)) + return ctrl->OnMeasureItem(param); + } + return FALSE; + + case WM_DRAWITEM: + if (!Menu_DrawItem(lParam)) { + DRAWITEMSTRUCT *param = (DRAWITEMSTRUCT *)lParam; + if (param && param->CtlID) + if (CCtrlBase *ctrl = FindControl(param->CtlID)) + return ctrl->OnDrawItem(param); + } + return FALSE; + + case WM_DELETEITEM: + { + DELETEITEMSTRUCT *param = (DELETEITEMSTRUCT *)lParam; + if (param && param->CtlID) + if (CCtrlBase *ctrl = FindControl(param->CtlID)) + return ctrl->OnDeleteItem(param); + } + return FALSE; + + case WM_COMMAND: + { + HWND hwndCtrl = (HWND)lParam; + uint16_t idCtrl = LOWORD(wParam); + uint16_t idCode = HIWORD(wParam); + if (CCtrlBase *ctrl = FindControl(idCtrl)) { + BOOL result = ctrl->OnCommand(hwndCtrl, idCtrl, idCode); + if (result != FALSE) + return result; + } + + if (idCode == BN_CLICKED) { + // close dialog automatically if 'Cancel' button is pressed + if (idCtrl == IDCANCEL && (m_autoClose & CLOSE_ON_CANCEL)) { + m_bExiting = true; + PostMessage(m_hwnd, WM_CLOSE, 0, 0); + } + + // close dialog automatically if 'OK' button is pressed + if (idCtrl == IDOK && (m_autoClose & CLOSE_ON_OK)) { + // validate dialog data first + if (VerifyControls(&CCtrlBase::OnApply)) { + m_bExiting = true; + + // everything ok? good, let's close it + if (OnApply()) { + m_bSucceeded = true; + PostMessage(m_hwnd, WM_CLOSE, 0, 0); + } + else m_bExiting = false; + } + } + } + } + return FALSE; + + case WM_NOTIFY: + { + int idCtrl = wParam; + NMHDR *pnmh = (NMHDR *)lParam; + if (pnmh->idFrom == 0) { + switch (pnmh->code) { + case PSN_APPLY: + if (LPPSHNOTIFY(lParam)->lParam != 3) // IDC_APPLY + m_bExiting = true; + + if (!VerifyControls(&CCtrlBase::OnApply)) + m_bExiting = false; + else if (!OnApply()) + m_bExiting = false; + break; + + case PSN_RESET: + NotifyControls(&CCtrlBase::OnReset); + OnReset(); + break; + + case PSN_WIZFINISH: + m_OnFinishWizard(this); + break; + } + } + + if (CCtrlBase *ctrl = FindControl(pnmh->idFrom)) + return ctrl->OnNotify(idCtrl, pnmh); + } + return FALSE; + + case WM_HSCROLL: + if (auto *pCtrl = FindControl(HWND(lParam))) + pCtrl->OnCommand(HWND(lParam), pCtrl->m_idCtrl, WM_HSCROLL); + break; + + case PSM_CHANGED: + if (m_bInitialized) + OnChange(); + break; + + case WM_CONTEXTMENU: + if (CCtrlBase *ctrl = FindControl(HWND(wParam))) { + CContextMenuPos pos = {}; + if (lParam != -1) { + pos.pt.x = GET_X_LPARAM(lParam); + pos.pt.y = GET_Y_LPARAM(lParam); + } + ctrl->GetCaretPos(pos); + ctrl->OnBuildMenu(&pos); + } + break; + + case WM_SIZE: + OnResize(); + return TRUE; + + case WM_TIMER: + if (CTimer *timer = FindTimer(wParam)) + return timer->OnTimer(); + return FALSE; + + case WM_CLOSE: + if (OnClose()) { + m_bExiting = true; + if (m_isModal) + EndModal(m_bSucceeded ? IDOK : FALSE); + else + DestroyWindow(m_hwnd); + } + return TRUE; + + case WM_DESTROY: + m_bExiting = true; + OnDestroy(); + NotifyControls(&CCtrlBase::OnDestroy); + { + mir_cslock lck(csDialogs); + int idx = arDialogs.getIndex(this); + if (idx != -1) + arDialogs.remove(idx); + } + m_hwnd = nullptr; + if (m_bInitialized) { + if (m_isModal) + m_isModal = false; + else // modeless dialogs MUST be allocated with 'new' + delete this; + } + + return TRUE; + } + + return FALSE; +} + +INT_PTR CALLBACK CDlgBase::GlobalDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + CDlgBase *wnd; + if (msg == WM_INITDIALOG) { + wnd = (CDlgBase*)lParam; + wnd->m_hwnd = hwnd; + + mir_cslock lck(csDialogs); + arDialogs.insert(wnd); + } + else wnd = CDlgBase::Find(hwnd); + + return (wnd == nullptr) ? FALSE : wnd->DlgProc(msg, wParam, lParam); +} + +void CDlgBase::ThemeDialogBackground(BOOL tabbed) +{ + EnableThemeDialogTexture(m_hwnd, (tabbed ? ETDT_ENABLE : ETDT_DISABLE) | ETDT_USETABTEXTURE); +} + +void CDlgBase::AddControl(CCtrlBase *ctrl) +{ + m_controls.insert(ctrl); +} + +void CDlgBase::RemoveControl(CCtrlBase *ctrl) +{ + m_controls.remove(ctrl); +} + +void CDlgBase::NotifyControls(void (CCtrlBase::*fn)()) +{ + for (auto &it : m_controls) + (it->*fn)(); +} + +bool CDlgBase::VerifyControls(bool (CCtrlBase::*fn)()) +{ + for (auto &it : m_controls) + if (!(it->*fn)()) + return false; + + return true; +} + +CCtrlBase* CDlgBase::FindControl(int idCtrl) +{ + CCtrlBase search(nullptr, idCtrl); + return m_controls.find(&search); +} + +CCtrlBase* CDlgBase::FindControl(HWND hwnd) +{ + for (auto &it : m_controls) + if (it->GetHwnd() == hwnd) + return it; + + return nullptr; +} + +void CDlgBase::AddTimer(CTimer *timer) +{ + m_timers.insert(timer); +} + +void CDlgBase::RemoveTimer(UINT_PTR idEvent) +{ + CTimer search(nullptr, idEvent); + m_timers.remove(&search); +} + +CTimer* CDlgBase::FindTimer(int idEvent) +{ + CTimer search(nullptr, idEvent); + return m_timers.find(&search); +} + +CDlgBase* CDlgBase::Find(HWND hwnd) +{ + PVOID bullshit[2]; // vfptr + hwnd + bullshit[1] = hwnd; + return arDialogs.find((CDlgBase*)&bullshit); +} diff --git a/src/mir_core/src/Windows/CProgress.cpp b/src/mir_core/src/Windows/CProgress.cpp index 991c6f239d..e2d5321f59 100644 --- a/src/mir_core/src/Windows/CProgress.cpp +++ b/src/mir_core/src/Windows/CProgress.cpp @@ -1,53 +1,53 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CCtrlProgress - -CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl) -{ -} - -void CCtrlProgress::SetRange(uint16_t max, uint16_t min) -{ - SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); -} - -void CCtrlProgress::SetPosition(uint16_t value) -{ - SendMsg(PBM_SETPOS, value, 0); -} - -void CCtrlProgress::SetStep(uint16_t value) -{ - SendMsg(PBM_SETSTEP, value, 0); -} - -uint16_t CCtrlProgress::Move(uint16_t delta) -{ - return delta == 0 - ? SendMsg(PBM_STEPIT, 0, 0) - : SendMsg(PBM_DELTAPOS, delta, 0); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CCtrlProgress + +CCtrlProgress::CCtrlProgress(CDlgBase *wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl) +{ +} + +void CCtrlProgress::SetRange(uint16_t max, uint16_t min) +{ + SendMsg(PBM_SETRANGE, 0, MAKELPARAM(min, max)); +} + +void CCtrlProgress::SetPosition(uint16_t value) +{ + SendMsg(PBM_SETPOS, value, 0); +} + +void CCtrlProgress::SetStep(uint16_t value) +{ + SendMsg(PBM_SETSTEP, value, 0); +} + +uint16_t CCtrlProgress::Move(uint16_t delta) +{ + return delta == 0 + ? SendMsg(PBM_STEPIT, 0, 0) + : SendMsg(PBM_DELTAPOS, delta, 0); +} diff --git a/src/mir_core/src/Windows/CSplitter.cpp b/src/mir_core/src/Windows/CSplitter.cpp index e2ee6b6fc8..153beaf95b 100644 --- a/src/mir_core/src/Windows/CSplitter.cpp +++ b/src/mir_core/src/Windows/CSplitter.cpp @@ -1,83 +1,83 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CSplitter - -CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) - : CCtrlBase(wnd, idCtrl), - m_iPosition(0) -{ -} - -void CSplitter::OnInit() -{ - CSuper::OnInit(); - Subclass(); -} - -LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_NCHITTEST: - return HTCLIENT; - - case WM_SETCURSOR: - RECT rc; - GetClientRect(m_hwnd, &rc); - SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); - return TRUE; - - case WM_LBUTTONDOWN: - SetCapture(m_hwnd); - return 0; - - case WM_MOUSEMOVE: - if (GetCapture() == m_hwnd) { - POINT pt = { 0, 0 }; - GetClientRect(m_hwnd, &rc); - if (rc.right > rc.bottom) { - pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.y; - } - else { - pt.x = LOWORD(GetMessagePos()) + rc.right / 2; - ScreenToClient(m_parentWnd->GetHwnd(), &pt); - m_iPosition = pt.x; - } - - OnChange(this); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - } - return 0; - - case WM_LBUTTONUP: - ReleaseCapture(); - PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); - return 0; - } - - return CSuper::CustomWndProc(msg, wParam, lParam); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CSplitter + +CSplitter::CSplitter(CDlgBase *wnd, int idCtrl) + : CCtrlBase(wnd, idCtrl), + m_iPosition(0) +{ +} + +void CSplitter::OnInit() +{ + CSuper::OnInit(); + Subclass(); +} + +LRESULT CSplitter::CustomWndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_NCHITTEST: + return HTCLIENT; + + case WM_SETCURSOR: + RECT rc; + GetClientRect(m_hwnd, &rc); + SetCursor(rc.right > rc.bottom ? g_hCursorNS : g_hCursorWE); + return TRUE; + + case WM_LBUTTONDOWN: + SetCapture(m_hwnd); + return 0; + + case WM_MOUSEMOVE: + if (GetCapture() == m_hwnd) { + POINT pt = { 0, 0 }; + GetClientRect(m_hwnd, &rc); + if (rc.right > rc.bottom) { + pt.y = HIWORD(GetMessagePos()) + rc.bottom / 2; + ScreenToClient(m_parentWnd->GetHwnd(), &pt); + m_iPosition = pt.y; + } + else { + pt.x = LOWORD(GetMessagePos()) + rc.right / 2; + ScreenToClient(m_parentWnd->GetHwnd(), &pt); + m_iPosition = pt.x; + } + + OnChange(this); + PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); + } + return 0; + + case WM_LBUTTONUP: + ReleaseCapture(); + PostMessage(m_parentWnd->GetHwnd(), WM_SIZE, 0, 0); + return 0; + } + + return CSuper::CustomWndProc(msg, wParam, lParam); +} diff --git a/src/mir_core/src/Windows/CTimer.cpp b/src/mir_core/src/Windows/CTimer.cpp index b346138707..4f1141f329 100644 --- a/src/mir_core/src/Windows/CTimer.cpp +++ b/src/mir_core/src/Windows/CTimer.cpp @@ -1,90 +1,90 @@ -/* - -Object UI extensions -Copyright (c) 2008 Victor Pavlychko, George Hazan -Copyright (C) 2012-22 Miranda NG team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CTimer - -CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) - : m_wnd(wnd), m_idEvent(idEvent) -{ - if (wnd) - wnd->AddTimer(this); -} - -CTimer::~CTimer() -{ - if (m_wnd) - m_wnd->RemoveTimer(m_idEvent); -} - -BOOL CTimer::OnTimer() -{ - OnEvent(this); - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CTimer::Start(int elapse) -{ - ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); -} - -bool CTimer::Stop() -{ - return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TStartParam -{ - CTimer *pTimer; - int period; -}; - -static INT_PTR CALLBACK stubStart(void *param) -{ - auto *p = (TStartParam *)param; - return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); -} - -void CTimer::StartSafe(int elapse) -{ - TStartParam param = { this, elapse }; - CallFunctionSync(stubStart, ¶m); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static INT_PTR CALLBACK stubStop(void *param) -{ - auto *p = (CTimer*)param; - return ::KillTimer(p->GetHwnd(), p->GetEventId()); -} - -void CTimer::StopSafe() -{ - CallFunctionSync(stubStop, this); -} +/* + +Object UI extensions +Copyright (c) 2008 Victor Pavlychko, George Hazan +Copyright (C) 2012-23 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CTimer + +CTimer::CTimer(CDlgBase *wnd, UINT_PTR idEvent) + : m_wnd(wnd), m_idEvent(idEvent) +{ + if (wnd) + wnd->AddTimer(this); +} + +CTimer::~CTimer() +{ + if (m_wnd) + m_wnd->RemoveTimer(m_idEvent); +} + +BOOL CTimer::OnTimer() +{ + OnEvent(this); + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CTimer::Start(int elapse) +{ + ::SetTimer(m_wnd->GetHwnd(), m_idEvent, elapse, nullptr); +} + +bool CTimer::Stop() +{ + return 0 != ::KillTimer(m_wnd->GetHwnd(), m_idEvent); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct TStartParam +{ + CTimer *pTimer; + int period; +}; + +static INT_PTR CALLBACK stubStart(void *param) +{ + auto *p = (TStartParam *)param; + return ::SetTimer(p->pTimer->GetHwnd(), p->pTimer->GetEventId(), p->period, nullptr); +} + +void CTimer::StartSafe(int elapse) +{ + TStartParam param = { this, elapse }; + CallFunctionSync(stubStart, ¶m); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static INT_PTR CALLBACK stubStop(void *param) +{ + auto *p = (CTimer*)param; + return ::KillTimer(p->GetHwnd(), p->GetEventId()); +} + +void CTimer::StopSafe() +{ + CallFunctionSync(stubStop, this); +} diff --git a/src/mir_core/src/Windows/cmdline.cpp b/src/mir_core/src/Windows/cmdline.cpp index a4a61b2c6e..0e43610e0c 100644 --- a/src/mir_core/src/Windows/cmdline.cpp +++ b/src/mir_core/src/Windows/cmdline.cpp @@ -1,77 +1,77 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -/* command line support */ - -struct CmdLineParam -{ - __inline CmdLineParam(const wchar_t *_name, const wchar_t *_value) : - name(mir_wstrdup(_name)), value(mir_wstrdup(_value)) - {} - - ptrW name, value; -}; - -static int CompareParams(const CmdLineParam *p1, const CmdLineParam *p2) -{ - return wcscmp(p1->name, p2->name); -} - -static OBJLIST arParams(5, CompareParams); - -MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine) -{ - int nArgs = 0; - wchar_t **pArgs = CommandLineToArgvW(ptszCmdLine, &nArgs); - if (pArgs == nullptr) - return; - - for (int i=0; i < nArgs; i++) { - wchar_t *pOptionName = pArgs[i], *p; - - // not an option? skip it - if (*pOptionName != '/' && *pOptionName != '-') - continue; - - pOptionName++; - if ((p = wcspbrk(pOptionName, L"=:")) == nullptr) { // no more text in string - arParams.insert(new CmdLineParam(pOptionName, L"")); - break; - } - - // parameter with value - *p = 0; - arParams.insert(new CmdLineParam(pOptionName, p+1)); - } - - LocalFree(pArgs); -} - -MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t* ptszParameter) -{ - CmdLineParam tmp(ptszParameter, nullptr); - int idx = arParams.getIndex(&tmp); - return (idx == -1) ? nullptr : arParams[idx].value.get(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +/* command line support */ + +struct CmdLineParam +{ + __inline CmdLineParam(const wchar_t *_name, const wchar_t *_value) : + name(mir_wstrdup(_name)), value(mir_wstrdup(_value)) + {} + + ptrW name, value; +}; + +static int CompareParams(const CmdLineParam *p1, const CmdLineParam *p2) +{ + return wcscmp(p1->name, p2->name); +} + +static OBJLIST arParams(5, CompareParams); + +MIR_CORE_DLL(void) CmdLine_Parse(const wchar_t *ptszCmdLine) +{ + int nArgs = 0; + wchar_t **pArgs = CommandLineToArgvW(ptszCmdLine, &nArgs); + if (pArgs == nullptr) + return; + + for (int i=0; i < nArgs; i++) { + wchar_t *pOptionName = pArgs[i], *p; + + // not an option? skip it + if (*pOptionName != '/' && *pOptionName != '-') + continue; + + pOptionName++; + if ((p = wcspbrk(pOptionName, L"=:")) == nullptr) { // no more text in string + arParams.insert(new CmdLineParam(pOptionName, L"")); + break; + } + + // parameter with value + *p = 0; + arParams.insert(new CmdLineParam(pOptionName, p+1)); + } + + LocalFree(pArgs); +} + +MIR_CORE_DLL(const wchar_t*) CmdLine_GetOption(const wchar_t* ptszParameter) +{ + CmdLineParam tmp(ptszParameter, nullptr); + int idx = arParams.getIndex(&tmp); + return (idx == -1) ? nullptr : arParams[idx].value.get(); +} diff --git a/src/mir_core/src/Windows/colourpicker.cpp b/src/mir_core/src/Windows/colourpicker.cpp index 3272cc37ac..135f8c4096 100644 --- a/src/mir_core/src/Windows/colourpicker.cpp +++ b/src/mir_core/src/Windows/colourpicker.cpp @@ -1,105 +1,105 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_CREATE: - SetWindowLongPtr(hwnd, 0, 0); - SetWindowLongPtr(hwnd, sizeof(COLORREF), 0); - break; - - case CPM_SETDEFAULTCOLOUR: - SetWindowLongPtr(hwnd, sizeof(COLORREF), lParam); - break; - - case CPM_GETDEFAULTCOLOUR: - return GetWindowLongPtr(hwnd, sizeof(COLORREF)); - - case CPM_SETCOLOUR: - SetWindowLongPtr(hwnd, 0, lParam); - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case CPM_GETCOLOUR: - return GetWindowLongPtr(hwnd, 0); - - case WM_LBUTTONUP: - { - COLORREF custColours[16] = { 0 }; - custColours[0] = GetWindowLongPtr(hwnd, sizeof(COLORREF)); - - CHOOSECOLOR cc = { 0 }; - cc.lStructSize = sizeof(CHOOSECOLOR); - cc.hwndOwner = hwnd; - cc.hInstance = (HWND)g_hInst; - cc.rgbResult = GetWindowLongPtr(hwnd, 0); - cc.lpCustColors = custColours; - cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; - if (ChooseColor(&cc)) { - SetWindowLongPtr(hwnd, 0, cc.rgbResult); - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), CPN_COLOURCHANGED), (LPARAM)hwnd); - InvalidateRect(hwnd, nullptr, FALSE); - } - } - break; - - case WM_ENABLE: - InvalidateRect(hwnd, nullptr, FALSE); - break; - - case WM_NCPAINT: - case WM_PAINT: - PAINTSTRUCT ps; - HDC hdc1 = BeginPaint(hwnd, &ps); - - RECT rc; - GetClientRect(hwnd, &rc); - DrawEdge(hdc1, &rc, EDGE_ETCHED, BF_RECT); - InflateRect(&rc, -2, -2); - - HBRUSH hBrush = (IsWindowEnabled(hwnd)) ? CreateSolidBrush(GetWindowLongPtr(hwnd, 0)) : CreateHatchBrush(HS_BDIAGONAL, GetSysColor(COLOR_GRAYTEXT)); - SetBkColor(hdc1, GetSysColor(COLOR_BTNFACE)); - FillRect(hdc1, &rc, hBrush); - DeleteObject(hBrush); - - EndPaint(hwnd, &ps); - break; - } - return DefWindowProc(hwnd, message, wParam, lParam); -} - -void InitColourPicker(void) -{ - WNDCLASS wcl = { 0 }; - wcl.lpfnWndProc = ColourPickerWndProc; - wcl.cbWndExtra = sizeof(COLORREF) * 2; - wcl.hInstance = g_hInst; - wcl.lpszClassName = _T(WNDCLASS_COLOURPICKER); - wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); - wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; - RegisterClass(&wcl); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +static LRESULT CALLBACK ColourPickerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_CREATE: + SetWindowLongPtr(hwnd, 0, 0); + SetWindowLongPtr(hwnd, sizeof(COLORREF), 0); + break; + + case CPM_SETDEFAULTCOLOUR: + SetWindowLongPtr(hwnd, sizeof(COLORREF), lParam); + break; + + case CPM_GETDEFAULTCOLOUR: + return GetWindowLongPtr(hwnd, sizeof(COLORREF)); + + case CPM_SETCOLOUR: + SetWindowLongPtr(hwnd, 0, lParam); + InvalidateRect(hwnd, nullptr, FALSE); + break; + + case CPM_GETCOLOUR: + return GetWindowLongPtr(hwnd, 0); + + case WM_LBUTTONUP: + { + COLORREF custColours[16] = { 0 }; + custColours[0] = GetWindowLongPtr(hwnd, sizeof(COLORREF)); + + CHOOSECOLOR cc = { 0 }; + cc.lStructSize = sizeof(CHOOSECOLOR); + cc.hwndOwner = hwnd; + cc.hInstance = (HWND)g_hInst; + cc.rgbResult = GetWindowLongPtr(hwnd, 0); + cc.lpCustColors = custColours; + cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; + if (ChooseColor(&cc)) { + SetWindowLongPtr(hwnd, 0, cc.rgbResult); + SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), CPN_COLOURCHANGED), (LPARAM)hwnd); + InvalidateRect(hwnd, nullptr, FALSE); + } + } + break; + + case WM_ENABLE: + InvalidateRect(hwnd, nullptr, FALSE); + break; + + case WM_NCPAINT: + case WM_PAINT: + PAINTSTRUCT ps; + HDC hdc1 = BeginPaint(hwnd, &ps); + + RECT rc; + GetClientRect(hwnd, &rc); + DrawEdge(hdc1, &rc, EDGE_ETCHED, BF_RECT); + InflateRect(&rc, -2, -2); + + HBRUSH hBrush = (IsWindowEnabled(hwnd)) ? CreateSolidBrush(GetWindowLongPtr(hwnd, 0)) : CreateHatchBrush(HS_BDIAGONAL, GetSysColor(COLOR_GRAYTEXT)); + SetBkColor(hdc1, GetSysColor(COLOR_BTNFACE)); + FillRect(hdc1, &rc, hBrush); + DeleteObject(hBrush); + + EndPaint(hwnd, &ps); + break; + } + return DefWindowProc(hwnd, message, wParam, lParam); +} + +void InitColourPicker(void) +{ + WNDCLASS wcl = { 0 }; + wcl.lpfnWndProc = ColourPickerWndProc; + wcl.cbWndExtra = sizeof(COLORREF) * 2; + wcl.hInstance = g_hInst; + wcl.lpszClassName = _T(WNDCLASS_COLOURPICKER); + wcl.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; + RegisterClass(&wcl); +} diff --git a/src/mir_core/src/Windows/diatheme.cpp b/src/mir_core/src/Windows/diatheme.cpp index 2c16131643..12e37ff578 100644 --- a/src/mir_core/src/Windows/diatheme.cpp +++ b/src/mir_core/src/Windows/diatheme.cpp @@ -1,170 +1,170 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../stdafx.h" -#include "diatheme.h" - -// The following code was borrowed from Notepad2 sources and adapted for Miranda NG - -typedef HTHEME (WINAPI *OTD)(HWND hwnd,LPCWSTR pszClassList); -typedef HRESULT (WINAPI *GTSF)(HTHEME hTheme,int iFontId,LOGFONT *plf); -typedef HRESULT (WINAPI *CTD)(HTHEME hTheme); - -BOOL GetThemedDialogFont(LPWSTR lpFaceName, WORD *wSize) -{ - BOOL bSucceed = FALSE; - - HDC hDC = GetDC(nullptr); - int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY); - ReleaseDC(nullptr, hDC); - - if (HMODULE hModUxTheme = GetModuleHandle(L"uxtheme.dll")) { - OTD _OpenThemeData = (OTD)GetProcAddress(hModUxTheme, "OpenThemeData"); - GTSF _GetThemeSysFont = (GTSF)GetProcAddress(hModUxTheme, "GetThemeSysFont"); - CTD _CloseThemeData = (CTD)GetProcAddress(hModUxTheme, "CloseThemeData"); - - if (_CloseThemeData && _GetThemeSysFont && _OpenThemeData) { - if (HTHEME hTheme = _OpenThemeData(NULL, L"WINDOWSTYLE;WINDOW")) { - LOGFONT lf; - if (S_OK == _GetThemeSysFont(hTheme,/*TMT_MSGBOXFONT*/805, &lf)) { - if (lf.lfHeight < 0) - lf.lfHeight = -lf.lfHeight; - *wSize = (WORD)MulDiv(lf.lfHeight, 72, iLogPixelsY); - if (*wSize == 0) - *wSize = 8; - wcsncpy_s(lpFaceName, LF_FACESIZE, lf.lfFaceName, _TRUNCATE); - bSucceed = TRUE; - } - _CloseThemeData(hTheme); - } - } - } - - if (!bSucceed) { - NONCLIENTMETRICS ncm; - ncm.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0); - if (ncm.lfMessageFont.lfHeight < 0) - ncm.lfMessageFont.lfHeight = -ncm.lfMessageFont.lfHeight; - *wSize = (WORD)MulDiv(ncm.lfMessageFont.lfHeight, 72, iLogPixelsY); - if (*wSize == 0) - *wSize = 8; - - wcsncpy_s(lpFaceName, LF_FACESIZE, ncm.lfMessageFont.lfFaceName, _TRUNCATE); - } - - return TRUE; -} - -BOOL DialogTemplate_IsDialogEx(const DLGTEMPLATE *pTemplate) -{ - return ((DLGTEMPLATEEX *)pTemplate)->signature == 0xFFFF; -} - -BOOL DialogTemplate_HasFont(const DLGTEMPLATE *pTemplate) -{ - return (DS_SETFONT & (DialogTemplate_IsDialogEx(pTemplate) ? ((DLGTEMPLATEEX *)pTemplate)->style : pTemplate->style)); -} - -int DialogTemplate_FontAttrSize(BOOL bDialogEx) -{ - return (int)sizeof(WORD) * (bDialogEx ? 3 : 1); -} - -BYTE *DialogTemplate_GetFontSizeField(const DLGTEMPLATE *pTemplate) -{ - BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - - WORD *pw; - if (bDialogEx) - pw = (WORD *)((DLGTEMPLATEEX *)pTemplate + 1); - else - pw = (WORD *)(pTemplate + 1); - - if (*pw == (WORD)-1) - pw += 2; - else - while (*pw++); - - if (*pw == (WORD)-1) - pw += 2; - else - while (*pw++); - - while (*pw++); - - return (BYTE *)pw; -} - -DLGTEMPLATE* LoadThemedDialogTemplate(LPCTSTR lpDialogTemplateID, HINSTANCE hInstance) -{ - WCHAR wchFaceName[LF_FACESIZE]; - - HRSRC hRsrc = FindResource(hInstance, lpDialogTemplateID, RT_DIALOG); - if (hRsrc == nullptr) - return nullptr; - - HGLOBAL hRsrcMem = LoadResource(hInstance, hRsrc); - if (hRsrcMem == nullptr) - return nullptr; - - DLGTEMPLATE *pRsrcMem = (DLGTEMPLATE *)LockResource(hRsrcMem); - if (pRsrcMem == nullptr) - return nullptr; - - size_t dwTemplateSize = (UINT)SizeofResource(hInstance, hRsrc); - UnlockResource(hRsrcMem); - FreeResource(hRsrcMem); - - if (dwTemplateSize == 0) - return nullptr; - - auto *pTemplate = (DLGTEMPLATE *)mir_alloc(dwTemplateSize + LF_FACESIZE * 2); - memcpy(pTemplate, pRsrcMem, dwTemplateSize); - UnlockResource(hRsrcMem); - FreeResource(hRsrcMem); - - WORD wFontSize; - if (!GetThemedDialogFont(wchFaceName, &wFontSize)) - return(pTemplate); - - BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); - BOOL bHasFont = DialogTemplate_HasFont(pTemplate); - size_t cbFontAttr = DialogTemplate_FontAttrSize(bDialogEx); - - if (bDialogEx) - ((DLGTEMPLATEEX *)pTemplate)->style |= DS_SHELLFONT; - else - pTemplate->style |= DS_SHELLFONT; - - size_t cbNew = cbFontAttr + ((mir_wstrlen(wchFaceName) + 1) * sizeof(WCHAR)); - BYTE *pbNew = (BYTE *)wchFaceName; - - BYTE *pb = DialogTemplate_GetFontSizeField(pTemplate); - size_t cbOld = (int)(bHasFont ? cbFontAttr + 2 * (mir_wstrlen((WCHAR *)(pb + cbFontAttr)) + 1) : 0); - - BYTE *pOldControls = (BYTE *)(((DWORD_PTR)pb + cbOld + 3) & ~(DWORD_PTR)3); - BYTE *pNewControls = (BYTE *)(((DWORD_PTR)pb + cbNew + 3) & ~(DWORD_PTR)3); - - WORD nCtrl = bDialogEx ? (WORD)((DLGTEMPLATEEX *)pTemplate)->cDlgItems : (WORD)pTemplate->cdit; - if (cbNew != cbOld && nCtrl > 0) - MoveMemory(pNewControls, pOldControls, dwTemplateSize - (pOldControls - (BYTE *)pTemplate)); - - *(WORD *)pb = wFontSize; - MoveMemory(pb + cbFontAttr, pbNew, cbNew - cbFontAttr); - return pTemplate; -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../stdafx.h" +#include "diatheme.h" + +// The following code was borrowed from Notepad2 sources and adapted for Miranda NG + +typedef HTHEME (WINAPI *OTD)(HWND hwnd,LPCWSTR pszClassList); +typedef HRESULT (WINAPI *GTSF)(HTHEME hTheme,int iFontId,LOGFONT *plf); +typedef HRESULT (WINAPI *CTD)(HTHEME hTheme); + +BOOL GetThemedDialogFont(LPWSTR lpFaceName, WORD *wSize) +{ + BOOL bSucceed = FALSE; + + HDC hDC = GetDC(nullptr); + int iLogPixelsY = GetDeviceCaps(hDC, LOGPIXELSY); + ReleaseDC(nullptr, hDC); + + if (HMODULE hModUxTheme = GetModuleHandle(L"uxtheme.dll")) { + OTD _OpenThemeData = (OTD)GetProcAddress(hModUxTheme, "OpenThemeData"); + GTSF _GetThemeSysFont = (GTSF)GetProcAddress(hModUxTheme, "GetThemeSysFont"); + CTD _CloseThemeData = (CTD)GetProcAddress(hModUxTheme, "CloseThemeData"); + + if (_CloseThemeData && _GetThemeSysFont && _OpenThemeData) { + if (HTHEME hTheme = _OpenThemeData(NULL, L"WINDOWSTYLE;WINDOW")) { + LOGFONT lf; + if (S_OK == _GetThemeSysFont(hTheme,/*TMT_MSGBOXFONT*/805, &lf)) { + if (lf.lfHeight < 0) + lf.lfHeight = -lf.lfHeight; + *wSize = (WORD)MulDiv(lf.lfHeight, 72, iLogPixelsY); + if (*wSize == 0) + *wSize = 8; + wcsncpy_s(lpFaceName, LF_FACESIZE, lf.lfFaceName, _TRUNCATE); + bSucceed = TRUE; + } + _CloseThemeData(hTheme); + } + } + } + + if (!bSucceed) { + NONCLIENTMETRICS ncm; + ncm.cbSize = sizeof(NONCLIENTMETRICS); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0); + if (ncm.lfMessageFont.lfHeight < 0) + ncm.lfMessageFont.lfHeight = -ncm.lfMessageFont.lfHeight; + *wSize = (WORD)MulDiv(ncm.lfMessageFont.lfHeight, 72, iLogPixelsY); + if (*wSize == 0) + *wSize = 8; + + wcsncpy_s(lpFaceName, LF_FACESIZE, ncm.lfMessageFont.lfFaceName, _TRUNCATE); + } + + return TRUE; +} + +BOOL DialogTemplate_IsDialogEx(const DLGTEMPLATE *pTemplate) +{ + return ((DLGTEMPLATEEX *)pTemplate)->signature == 0xFFFF; +} + +BOOL DialogTemplate_HasFont(const DLGTEMPLATE *pTemplate) +{ + return (DS_SETFONT & (DialogTemplate_IsDialogEx(pTemplate) ? ((DLGTEMPLATEEX *)pTemplate)->style : pTemplate->style)); +} + +int DialogTemplate_FontAttrSize(BOOL bDialogEx) +{ + return (int)sizeof(WORD) * (bDialogEx ? 3 : 1); +} + +BYTE *DialogTemplate_GetFontSizeField(const DLGTEMPLATE *pTemplate) +{ + BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); + + WORD *pw; + if (bDialogEx) + pw = (WORD *)((DLGTEMPLATEEX *)pTemplate + 1); + else + pw = (WORD *)(pTemplate + 1); + + if (*pw == (WORD)-1) + pw += 2; + else + while (*pw++); + + if (*pw == (WORD)-1) + pw += 2; + else + while (*pw++); + + while (*pw++); + + return (BYTE *)pw; +} + +DLGTEMPLATE* LoadThemedDialogTemplate(LPCTSTR lpDialogTemplateID, HINSTANCE hInstance) +{ + WCHAR wchFaceName[LF_FACESIZE]; + + HRSRC hRsrc = FindResource(hInstance, lpDialogTemplateID, RT_DIALOG); + if (hRsrc == nullptr) + return nullptr; + + HGLOBAL hRsrcMem = LoadResource(hInstance, hRsrc); + if (hRsrcMem == nullptr) + return nullptr; + + DLGTEMPLATE *pRsrcMem = (DLGTEMPLATE *)LockResource(hRsrcMem); + if (pRsrcMem == nullptr) + return nullptr; + + size_t dwTemplateSize = (UINT)SizeofResource(hInstance, hRsrc); + UnlockResource(hRsrcMem); + FreeResource(hRsrcMem); + + if (dwTemplateSize == 0) + return nullptr; + + auto *pTemplate = (DLGTEMPLATE *)mir_alloc(dwTemplateSize + LF_FACESIZE * 2); + memcpy(pTemplate, pRsrcMem, dwTemplateSize); + UnlockResource(hRsrcMem); + FreeResource(hRsrcMem); + + WORD wFontSize; + if (!GetThemedDialogFont(wchFaceName, &wFontSize)) + return(pTemplate); + + BOOL bDialogEx = DialogTemplate_IsDialogEx(pTemplate); + BOOL bHasFont = DialogTemplate_HasFont(pTemplate); + size_t cbFontAttr = DialogTemplate_FontAttrSize(bDialogEx); + + if (bDialogEx) + ((DLGTEMPLATEEX *)pTemplate)->style |= DS_SHELLFONT; + else + pTemplate->style |= DS_SHELLFONT; + + size_t cbNew = cbFontAttr + ((mir_wstrlen(wchFaceName) + 1) * sizeof(WCHAR)); + BYTE *pbNew = (BYTE *)wchFaceName; + + BYTE *pb = DialogTemplate_GetFontSizeField(pTemplate); + size_t cbOld = (int)(bHasFont ? cbFontAttr + 2 * (mir_wstrlen((WCHAR *)(pb + cbFontAttr)) + 1) : 0); + + BYTE *pOldControls = (BYTE *)(((DWORD_PTR)pb + cbOld + 3) & ~(DWORD_PTR)3); + BYTE *pNewControls = (BYTE *)(((DWORD_PTR)pb + cbNew + 3) & ~(DWORD_PTR)3); + + WORD nCtrl = bDialogEx ? (WORD)((DLGTEMPLATEEX *)pTemplate)->cDlgItems : (WORD)pTemplate->cdit; + if (cbNew != cbOld && nCtrl > 0) + MoveMemory(pNewControls, pOldControls, dwTemplateSize - (pOldControls - (BYTE *)pTemplate)); + + *(WORD *)pb = wFontSize; + MoveMemory(pb + cbFontAttr, pbNew, cbNew - cbFontAttr); + return pTemplate; +} diff --git a/src/mir_core/src/Windows/fileutil.cpp b/src/mir_core/src/Windows/fileutil.cpp index 2522cc7cbe..a0dcee68f6 100644 --- a/src/mir_core/src/Windows/fileutil.cpp +++ b/src/mir_core/src/Windows/fileutil.cpp @@ -1,78 +1,78 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() -{ - if (ptr != nullptr) { - if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { - ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; - ptr = nullptr; - } - } - return *this; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) -{ - if (pwszPath != nullptr) - m_hFind = ::FindFirstFileW(pwszPath, &m_data); -} - -MFilePath::MFileIterator::~MFileIterator() -{ - if (m_hFind != INVALID_HANDLE_VALUE) - ::FindClose(m_hFind); -} - -MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() -{ - if (m_hFind == INVALID_HANDLE_VALUE) - return MFilePath::MFileIterator::iterator(nullptr); - - return MFilePath::MFileIterator::iterator(this); -} - -bool MFilePath::MFileIterator::isDir() const -{ - if (m_hFind == INVALID_HANDLE_VALUE) - return false; - - return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -bool MFilePath::isExist() const -{ - return _waccess(c_str(), 0) == 0; -} - -bool MFilePath::move(const wchar_t *pwszDest) -{ - return MoveFileW(c_str(), pwszDest) != 0; -} - -MFilePath::MFileIterator MFilePath::search() -{ - return MFileIterator(c_str()); -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// + +MFilePath::MFileIterator::iterator MFilePath::MFileIterator::iterator::operator++() +{ + if (ptr != nullptr) { + if (::FindNextFileW(ptr->m_hFind, &ptr->m_data) == 0) { + ::FindClose(ptr->m_hFind); ptr->m_hFind = INVALID_HANDLE_VALUE; + ptr = nullptr; + } + } + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MFilePath::MFileIterator::MFileIterator(const wchar_t *pwszPath) +{ + if (pwszPath != nullptr) + m_hFind = ::FindFirstFileW(pwszPath, &m_data); +} + +MFilePath::MFileIterator::~MFileIterator() +{ + if (m_hFind != INVALID_HANDLE_VALUE) + ::FindClose(m_hFind); +} + +MFilePath::MFileIterator::iterator MFilePath::MFileIterator::begin() +{ + if (m_hFind == INVALID_HANDLE_VALUE) + return MFilePath::MFileIterator::iterator(nullptr); + + return MFilePath::MFileIterator::iterator(this); +} + +bool MFilePath::MFileIterator::isDir() const +{ + if (m_hFind == INVALID_HANDLE_VALUE) + return false; + + return (m_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool MFilePath::isExist() const +{ + return _waccess(c_str(), 0) == 0; +} + +bool MFilePath::move(const wchar_t *pwszDest) +{ + return MoveFileW(c_str(), pwszDest) != 0; +} + +MFilePath::MFileIterator MFilePath::search() +{ + return MFileIterator(c_str()); +} diff --git a/src/mir_core/src/Windows/hyperlink.cpp b/src/mir_core/src/Windows/hyperlink.cpp index 2f70d23103..e83f8ff569 100644 --- a/src/mir_core/src/Windows/hyperlink.cpp +++ b/src/mir_core/src/Windows/hyperlink.cpp @@ -1,277 +1,277 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -struct HyperlinkWndData -{ - HFONT hEnableFont, hDisableFont; - RECT rcText; - COLORREF enableColor, disableColor, focusColor; - uint8_t flags; /* see HLKF_* */ -}; - -/* flags */ -#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */ -#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */ - -/* internal messages */ -#define HLK_MEASURETEXT (WM_USER+1) -#define HLK_INVALIDATE (WM_USER+2) - -static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - HyperlinkWndData *dat = (HyperlinkWndData*)GetWindowLongPtr(hwnd, 0); - - HDC hdc; - RECT rc; - POINT pt; - HFONT hFont; - LOGFONT lf; - HCURSOR hCursor; - COLORREF prevColor; - - switch (msg) { - case WM_NCCREATE: - dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData)); - if (dat == nullptr) - return FALSE; /* fail creation */ - SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */ - /* fall thru */ - - case WM_SYSCOLORCHANGE: - if (!(dat->flags&HLKF_HASENABLECOLOR)) { - if (GetSysColorBrush(COLOR_HOTLIGHT) == nullptr) dat->enableColor = RGB(0, 0, 255); - else dat->enableColor = GetSysColor(COLOR_HOTLIGHT); - dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); - } - if (!(dat->flags&HLKF_HASDISABLECOLOR)) - dat->disableColor = GetSysColor(COLOR_GRAYTEXT); - break; - - case WM_SETFOCUS: - case WM_KILLFOCUS: - RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE); - break; - - case WM_MOUSEACTIVATE: - SetFocus(hwnd); - return MA_ACTIVATE; - - case WM_GETDLGCODE: - if (lParam) { - MSG *pMsg = (MSG *)lParam; - if (pMsg->message == WM_KEYDOWN) { - if (pMsg->wParam == VK_TAB) - return 0; - if (pMsg->wParam == VK_ESCAPE) - return 0; - } - else if (pMsg->message == WM_CHAR) { - if (pMsg->wParam == '\t') - return 0; - if (pMsg->wParam == 27) - return 0; - } - } - return DLGC_WANTMESSAGE; - - case WM_KEYDOWN: - switch (wParam) { - case VK_SPACE: - case VK_RETURN: - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); - break; - } - return 0; - - case WM_LBUTTONDOWN: - POINTSTOPOINT(pt, MAKEPOINTS(lParam)); - if (!PtInRect(&dat->rcText, pt)) break; - SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); - return 0; - - case WM_SETFONT: - if ((HFONT)wParam == nullptr) { /* use default system color */ - dat->hEnableFont = dat->hDisableFont = nullptr; - return 0; - } - if (GetObject((HFONT)wParam, sizeof(lf), &lf)) { - lf.lfUnderline = 1; - hFont = CreateFontIndirect(&lf); - if (hFont != nullptr) { - dat->hEnableFont = hFont; - dat->hDisableFont = (HFONT)wParam; - if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0); - SendMessage(hwnd, HLK_MEASURETEXT, 0, 0); - } - } - return 0; - - case WM_ERASEBKGND: - return TRUE; - - case WM_ENABLE: - case HLK_INVALIDATE: - if (GetWindowRect(hwnd, &rc)) { - pt.x = rc.left; - pt.y = rc.top; - - HWND hwndParent = GetParent(hwnd); - if (hwndParent == nullptr) - hwndParent = hwnd; - if (!ScreenToClient(hwndParent, &pt)) - break; - - rc.right = pt.x + (rc.right - rc.left); - rc.bottom = pt.y + (rc.bottom - rc.top); - rc.left = pt.x; - rc.top = pt.y; - InvalidateRect(hwndParent, &rc, TRUE); - } - return 0; - - case WM_GETFONT: - return (LRESULT)dat->hDisableFont; - - case WM_CREATE: - case HLK_MEASURETEXT: - wchar_t szText[256]; - if (!GetWindowText(hwnd, szText, _countof(szText))) return 0; - lParam = (LPARAM)szText; - /* fall thru */ - - case WM_SETTEXT: - hdc = GetDC(hwnd); - if (hdc == nullptr) /* text change failed */ - return 0; - else { - BOOL fMeasured = FALSE; - HFONT hPrevFont = nullptr; - if (dat->hEnableFont != nullptr) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); - if (dat->hEnableFont == nullptr || hPrevFont != nullptr) { /* select failed? */ - SIZE textSize; - if (GetTextExtentPoint32(hdc, (wchar_t*)lParam, (int)mir_wstrlen((wchar_t*)lParam), &textSize)) { - if (GetClientRect(hwnd, &rc)) { - dat->rcText.top = 0; - dat->rcText.bottom = dat->rcText.top + textSize.cy; - LONG style = GetWindowLongPtr(hwnd, GWL_STYLE); - if (style & SS_CENTER) dat->rcText.left = (rc.right - textSize.cx) / 2; - else if (style & SS_RIGHT) dat->rcText.left = rc.right - textSize.cx; - else dat->rcText.left = 0; - dat->rcText.right = dat->rcText.left + textSize.cx; - fMeasured = TRUE; - } - } - } - if (dat->hEnableFont != nullptr && hPrevFont != nullptr) - SelectObject(hdc, hPrevFont); - ReleaseDC(hwnd, hdc); - if (!fMeasured) /* text change failed */ - return 0; - - SendMessage(hwnd, HLK_INVALIDATE, 0, 0); - } - break; - - case WM_SETCURSOR: - if (!GetCursorPos(&pt)) return FALSE; - if (!ScreenToClient(hwnd, &pt)) return FALSE; - if (PtInRect(&dat->rcText, pt)) { - hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR); - if (hCursor == nullptr) - hCursor = LoadCursor(nullptr, IDC_HAND); /* Win2000+ */ - } - else hCursor = LoadCursor(nullptr, IDC_ARROW); - SetCursor(hCursor); - return TRUE; - - case HLK_SETENABLECOLOUR: - prevColor = dat->enableColor; - dat->enableColor = (COLORREF)wParam; - dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); - dat->flags |= HLKF_HASENABLECOLOR; - return (LRESULT)prevColor; - - case HLK_SETDISABLECOLOUR: - prevColor = dat->disableColor; - dat->disableColor = (COLORREF)wParam; - dat->flags |= HLKF_HASDISABLECOLOR; - return (LRESULT)prevColor; - - case WM_NCPAINT: - return 0; - - case WM_PAINT: - PAINTSTRUCT ps; - hdc = BeginPaint(hwnd, &ps); - if (hdc != nullptr) { - HFONT hPrevFont; - COLORREF textColor; - if (IsWindowEnabled(hwnd)) { - hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); - textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor; - } - else { - hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont); - textColor = dat->disableColor; - } - if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, _countof(szText))) { - BOOL fSmoothing; - UINT fSmoothingType; - SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0); - SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0); - if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE) - DrawThemeParentBackground(hwnd, hdc, &rc); - SetBkMode(hdc, TRANSPARENT); - SetTextColor(hdc, textColor); - UINT alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE) & (SS_CENTER | SS_RIGHT | SS_LEFT)); - DrawText(hdc, szText, -1, &rc, alignFlag | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); - } - if (hPrevFont != nullptr) SelectObject(hdc, hPrevFont); - EndPaint(hwnd, &ps); - } - return 0; - - case WM_NCDESTROY: - if (dat->hEnableFont != nullptr) DeleteObject(dat->hEnableFont); - mir_free(dat); - break; - } - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -void InitHyperlink(void) -{ - g_hCursorNS = LoadCursor(nullptr, IDC_SIZENS); - g_hCursorWE = LoadCursor(nullptr, IDC_SIZEWE); - - WNDCLASS wcl = { 0 }; - wcl.lpfnWndProc = HyperlinkWndProc; - wcl.cbWndExtra = sizeof(struct HyperlinkWndData*); - wcl.hInstance = g_hInst; - wcl.lpszClassName = WNDCLASS_HYPERLINK; - wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC; - RegisterClass(&wcl); /* automatically unregistered on exit */ -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +struct HyperlinkWndData +{ + HFONT hEnableFont, hDisableFont; + RECT rcText; + COLORREF enableColor, disableColor, focusColor; + uint8_t flags; /* see HLKF_* */ +}; + +/* flags */ +#define HLKF_HASENABLECOLOR 0x1 /* dat->enableColor is not system default */ +#define HLKF_HASDISABLECOLOR 0x2 /* dat->disableColor is not system default */ + +/* internal messages */ +#define HLK_MEASURETEXT (WM_USER+1) +#define HLK_INVALIDATE (WM_USER+2) + +static LRESULT CALLBACK HyperlinkWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HyperlinkWndData *dat = (HyperlinkWndData*)GetWindowLongPtr(hwnd, 0); + + HDC hdc; + RECT rc; + POINT pt; + HFONT hFont; + LOGFONT lf; + HCURSOR hCursor; + COLORREF prevColor; + + switch (msg) { + case WM_NCCREATE: + dat = (struct HyperlinkWndData*)mir_calloc(sizeof(struct HyperlinkWndData)); + if (dat == nullptr) + return FALSE; /* fail creation */ + SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat); /* always succeeds */ + /* fall thru */ + + case WM_SYSCOLORCHANGE: + if (!(dat->flags&HLKF_HASENABLECOLOR)) { + if (GetSysColorBrush(COLOR_HOTLIGHT) == nullptr) dat->enableColor = RGB(0, 0, 255); + else dat->enableColor = GetSysColor(COLOR_HOTLIGHT); + dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); + } + if (!(dat->flags&HLKF_HASDISABLECOLOR)) + dat->disableColor = GetSysColor(COLOR_GRAYTEXT); + break; + + case WM_SETFOCUS: + case WM_KILLFOCUS: + RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE); + break; + + case WM_MOUSEACTIVATE: + SetFocus(hwnd); + return MA_ACTIVATE; + + case WM_GETDLGCODE: + if (lParam) { + MSG *pMsg = (MSG *)lParam; + if (pMsg->message == WM_KEYDOWN) { + if (pMsg->wParam == VK_TAB) + return 0; + if (pMsg->wParam == VK_ESCAPE) + return 0; + } + else if (pMsg->message == WM_CHAR) { + if (pMsg->wParam == '\t') + return 0; + if (pMsg->wParam == 27) + return 0; + } + } + return DLGC_WANTMESSAGE; + + case WM_KEYDOWN: + switch (wParam) { + case VK_SPACE: + case VK_RETURN: + SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); + break; + } + return 0; + + case WM_LBUTTONDOWN: + POINTSTOPOINT(pt, MAKEPOINTS(lParam)); + if (!PtInRect(&dat->rcText, pt)) break; + SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), STN_CLICKED), (LPARAM)hwnd); + return 0; + + case WM_SETFONT: + if ((HFONT)wParam == nullptr) { /* use default system color */ + dat->hEnableFont = dat->hDisableFont = nullptr; + return 0; + } + if (GetObject((HFONT)wParam, sizeof(lf), &lf)) { + lf.lfUnderline = 1; + hFont = CreateFontIndirect(&lf); + if (hFont != nullptr) { + dat->hEnableFont = hFont; + dat->hDisableFont = (HFONT)wParam; + if (LOWORD(lParam)) SendMessage(hwnd, HLK_INVALIDATE, 0, 0); + SendMessage(hwnd, HLK_MEASURETEXT, 0, 0); + } + } + return 0; + + case WM_ERASEBKGND: + return TRUE; + + case WM_ENABLE: + case HLK_INVALIDATE: + if (GetWindowRect(hwnd, &rc)) { + pt.x = rc.left; + pt.y = rc.top; + + HWND hwndParent = GetParent(hwnd); + if (hwndParent == nullptr) + hwndParent = hwnd; + if (!ScreenToClient(hwndParent, &pt)) + break; + + rc.right = pt.x + (rc.right - rc.left); + rc.bottom = pt.y + (rc.bottom - rc.top); + rc.left = pt.x; + rc.top = pt.y; + InvalidateRect(hwndParent, &rc, TRUE); + } + return 0; + + case WM_GETFONT: + return (LRESULT)dat->hDisableFont; + + case WM_CREATE: + case HLK_MEASURETEXT: + wchar_t szText[256]; + if (!GetWindowText(hwnd, szText, _countof(szText))) return 0; + lParam = (LPARAM)szText; + /* fall thru */ + + case WM_SETTEXT: + hdc = GetDC(hwnd); + if (hdc == nullptr) /* text change failed */ + return 0; + else { + BOOL fMeasured = FALSE; + HFONT hPrevFont = nullptr; + if (dat->hEnableFont != nullptr) hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); + if (dat->hEnableFont == nullptr || hPrevFont != nullptr) { /* select failed? */ + SIZE textSize; + if (GetTextExtentPoint32(hdc, (wchar_t*)lParam, (int)mir_wstrlen((wchar_t*)lParam), &textSize)) { + if (GetClientRect(hwnd, &rc)) { + dat->rcText.top = 0; + dat->rcText.bottom = dat->rcText.top + textSize.cy; + LONG style = GetWindowLongPtr(hwnd, GWL_STYLE); + if (style & SS_CENTER) dat->rcText.left = (rc.right - textSize.cx) / 2; + else if (style & SS_RIGHT) dat->rcText.left = rc.right - textSize.cx; + else dat->rcText.left = 0; + dat->rcText.right = dat->rcText.left + textSize.cx; + fMeasured = TRUE; + } + } + } + if (dat->hEnableFont != nullptr && hPrevFont != nullptr) + SelectObject(hdc, hPrevFont); + ReleaseDC(hwnd, hdc); + if (!fMeasured) /* text change failed */ + return 0; + + SendMessage(hwnd, HLK_INVALIDATE, 0, 0); + } + break; + + case WM_SETCURSOR: + if (!GetCursorPos(&pt)) return FALSE; + if (!ScreenToClient(hwnd, &pt)) return FALSE; + if (PtInRect(&dat->rcText, pt)) { + hCursor = (HCURSOR)GetClassLongPtr(hwnd, GCLP_HCURSOR); + if (hCursor == nullptr) + hCursor = LoadCursor(nullptr, IDC_HAND); /* Win2000+ */ + } + else hCursor = LoadCursor(nullptr, IDC_ARROW); + SetCursor(hCursor); + return TRUE; + + case HLK_SETENABLECOLOUR: + prevColor = dat->enableColor; + dat->enableColor = (COLORREF)wParam; + dat->focusColor = RGB(GetRValue(dat->enableColor) / 2, GetGValue(dat->enableColor) / 2, GetBValue(dat->enableColor) / 2); + dat->flags |= HLKF_HASENABLECOLOR; + return (LRESULT)prevColor; + + case HLK_SETDISABLECOLOUR: + prevColor = dat->disableColor; + dat->disableColor = (COLORREF)wParam; + dat->flags |= HLKF_HASDISABLECOLOR; + return (LRESULT)prevColor; + + case WM_NCPAINT: + return 0; + + case WM_PAINT: + PAINTSTRUCT ps; + hdc = BeginPaint(hwnd, &ps); + if (hdc != nullptr) { + HFONT hPrevFont; + COLORREF textColor; + if (IsWindowEnabled(hwnd)) { + hPrevFont = (HFONT)SelectObject(hdc, dat->hEnableFont); + textColor = (GetFocus() == hwnd) ? dat->focusColor : dat->enableColor; + } + else { + hPrevFont = (HFONT)SelectObject(hdc, dat->hDisableFont); + textColor = dat->disableColor; + } + if (GetClientRect(hwnd, &rc) && GetWindowText(hwnd, szText, _countof(szText))) { + BOOL fSmoothing; + UINT fSmoothingType; + SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fSmoothing, 0); + SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fSmoothingType, 0); + if (fSmoothing && fSmoothingType == FE_FONTSMOOTHINGCLEARTYPE) + DrawThemeParentBackground(hwnd, hdc, &rc); + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, textColor); + UINT alignFlag = (GetWindowLongPtr(hwnd, GWL_STYLE) & (SS_CENTER | SS_RIGHT | SS_LEFT)); + DrawText(hdc, szText, -1, &rc, alignFlag | DT_NOPREFIX | DT_SINGLELINE | DT_TOP); + } + if (hPrevFont != nullptr) SelectObject(hdc, hPrevFont); + EndPaint(hwnd, &ps); + } + return 0; + + case WM_NCDESTROY: + if (dat->hEnableFont != nullptr) DeleteObject(dat->hEnableFont); + mir_free(dat); + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void InitHyperlink(void) +{ + g_hCursorNS = LoadCursor(nullptr, IDC_SIZENS); + g_hCursorWE = LoadCursor(nullptr, IDC_SIZEWE); + + WNDCLASS wcl = { 0 }; + wcl.lpfnWndProc = HyperlinkWndProc; + wcl.cbWndExtra = sizeof(struct HyperlinkWndData*); + wcl.hInstance = g_hInst; + wcl.lpszClassName = WNDCLASS_HYPERLINK; + wcl.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC; + RegisterClass(&wcl); /* automatically unregistered on exit */ +} diff --git a/src/mir_core/src/Windows/icons.cpp b/src/mir_core/src/Windows/icons.cpp index a4555211de..122ecad5e5 100644 --- a/src/mir_core/src/Windows/icons.cpp +++ b/src/mir_core/src/Windows/icons.cpp @@ -1,74 +1,74 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) -{ - wchar_t szFile[MAX_PATH]; - GetModuleFileName(hInst, szFile, MAX_PATH); - - SKINICONDESC sid = {}; - sid.defaultFile.w = szFile; - sid.section.a = (char*)szSection; - sid.flags = SIDF_PATH_UNICODE; - - for (unsigned i = 0; i < iCount; i++) { - char szSetting[100]; - if (prefix) { - mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); - sid.pszName = szSetting; - } - else sid.pszName = pIcons[i].szName; - - sid.cx = sid.cy = pIcons[i].size; - sid.description.a = pIcons[i].szDescr; - sid.iDefaultIndex = -pIcons[i].defIconID; - pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); - } -} - -MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) -{ - wchar_t szFile[MAX_PATH]; - GetModuleFileName(hInst, szFile, MAX_PATH); - - SKINICONDESC sid = {}; - sid.defaultFile.w = szFile; - sid.section.w = (wchar_t*)szSection; - sid.flags = SIDF_ALL_UNICODE; - - for (unsigned i = 0; i < iCount; i++) { - char szSetting[100]; - if (prefix) { - mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); - sid.pszName = szSetting; - } - else sid.pszName = pIcons[i].szName; - - sid.cx = sid.cy = pIcons[i].size; - sid.description.w = pIcons[i].tszDescr; - sid.iDefaultIndex = -pIcons[i].defIconID; - pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); - } -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +MIR_CORE_DLL(void) Icon_Register(HINSTANCE hInst, const char *szSection, IconItem *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) +{ + wchar_t szFile[MAX_PATH]; + GetModuleFileName(hInst, szFile, MAX_PATH); + + SKINICONDESC sid = {}; + sid.defaultFile.w = szFile; + sid.section.a = (char*)szSection; + sid.flags = SIDF_PATH_UNICODE; + + for (unsigned i = 0; i < iCount; i++) { + char szSetting[100]; + if (prefix) { + mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); + sid.pszName = szSetting; + } + else sid.pszName = pIcons[i].szName; + + sid.cx = sid.cy = pIcons[i].size; + sid.description.a = pIcons[i].szDescr; + sid.iDefaultIndex = -pIcons[i].defIconID; + pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); + } +} + +MIR_CORE_DLL(void) Icon_RegisterT(HINSTANCE hInst, const wchar_t *szSection, IconItemT *pIcons, size_t iCount, const char *prefix, HPLUGIN pPlugin) +{ + wchar_t szFile[MAX_PATH]; + GetModuleFileName(hInst, szFile, MAX_PATH); + + SKINICONDESC sid = {}; + sid.defaultFile.w = szFile; + sid.section.w = (wchar_t*)szSection; + sid.flags = SIDF_ALL_UNICODE; + + for (unsigned i = 0; i < iCount; i++) { + char szSetting[100]; + if (prefix) { + mir_snprintf(szSetting, "%s_%s", prefix, pIcons[i].szName); + sid.pszName = szSetting; + } + else sid.pszName = pIcons[i].szName; + + sid.cx = sid.cy = pIcons[i].size; + sid.description.w = pIcons[i].tszDescr; + sid.iDefaultIndex = -pIcons[i].defIconID; + pIcons[i].hIcolib = IcoLib_AddIcon(&sid, pPlugin); + } +} diff --git a/src/mir_core/src/Windows/langpack.cpp b/src/mir_core/src/Windows/langpack.cpp index 00b7d996e1..010b1311ca 100644 --- a/src/mir_core/src/Windows/langpack.cpp +++ b/src/mir_core/src/Windows/langpack.cpp @@ -1,765 +1,765 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -#include "../../../mir_app/src/langpack.h" - -#define LANGPACK_BUF_SIZE 4000 - -static int CompareMuuids(const MUUID *p1, const MUUID *p2) -{ - return memcmp(p1, p2, sizeof(MUUID)); -} - -static LIST lMuuids(10, CompareMuuids); -static MUUID *pCurrentMuuid = nullptr; -static HANDLE hevChanged = nullptr; - -static BOOL bModuleInitialized = FALSE; - -struct LangPackEntry -{ - uint32_t englishHash; - char *szLocal; - char *utfLocal; - wchar_t *wszLocal; - MUUID *pMuuid; - LangPackEntry* pNext; // for langpack items with the same hash value -}; - -static LANGPACK_INFO langPack; -static wchar_t g_tszRoot[MAX_PATH]; - -static LangPackEntry *g_pEntries; -static int g_entryCount, g_entriesAlloced; - -static int IsEmpty(const char *str) -{ - for (int i = 0; str[i]; i++) - if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') - return 0; - - return 1; -} - -static int ConvertBackslashes(char *str, UINT fileCp) -{ - int shift = 0; - char *pstr; - for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0)) { - if (*pstr == '\\') { - shift++; - switch (pstr[1]) { - case 'n': *pstr = '\n'; break; - case 't': *pstr = '\t'; break; - case 'r': *pstr = '\r'; break; - case 's': *pstr = ' '; break; - default: *pstr = pstr[1]; break; - } - memmove(pstr + 1, pstr + 2, strlen(pstr + 2) + 1); - } - } - return shift; -} - -#ifdef _DEBUG -//#pragma optimize("gt", on) -#endif - -// MurmurHash2 -MIR_CORE_DLL(unsigned int) mir_hash(const void * key, unsigned int len) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - const unsigned int m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - unsigned int h = len; - - // Mix 4 bytes at a time into the hash - const unsigned char *data = (const unsigned char*)key; - - while (len >= 4) { - unsigned int k = *(unsigned int*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - switch (len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - } - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -static unsigned int __fastcall hashstrW(const char *key) -{ - if (key == nullptr) return 0; - const unsigned int len = (unsigned int)wcslen((const wchar_t*)key); - char *buf = (char*)alloca(len + 1); - for (unsigned i = 0; i <= len; ++i) - buf[i] = key[i << 1]; - return mir_hash(buf, len); -} - -static const MUUID* GetMuid(HPLUGIN pPlugin) -{ - if (!pPlugin) - return nullptr; - - __try { - return &pPlugin->getInfo().uuid; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - return nullptr; - } -} - -static int SortLangPackHashesProc(LangPackEntry *arg1, LangPackEntry *arg2) -{ - if (arg1->englishHash < arg2->englishHash) return -1; - if (arg1->englishHash > arg2->englishHash) return 1; - - return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1; -} - -static void swapBytes(void *p, size_t iSize) -{ - char *head = (char*)p; // here - char *tail = head + iSize - 1; - - for (; tail > head; --tail, ++head) { - char temp = *head; - *head = *tail; - *tail = temp; - } -} - -static bool EnterMuuid(const char *p, MUUID &result) -{ - if (*p++ != '{') - return false; - - uint8_t *d = (uint8_t*)&result; - - for (int nBytes = 0; *p && nBytes < 24; p++) { - if (*p == '-') - continue; - - if (*p == '}') - break; - - if (!isxdigit(*p)) - return false; - - if (!isxdigit(p[1])) - return false; - - int c = 0; - if (sscanf(p, "%2x", &c) != 1) - return false; - - *d++ = (uint8_t)c; - nBytes++; - p++; - } - - if (*p != '}') - return false; - - swapBytes(&result.a, sizeof(result.a)); - swapBytes(&result.b, sizeof(result.b)); - swapBytes(&result.c, sizeof(result.c)); - return true; -} - -static void LoadLangPackFile(FILE *fp, char *line) -{ - while (!feof(fp)) { - if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) - break; - - if (IsEmpty(line) || line[0] == ';' || line[0] == 0) - continue; - - rtrim(line); - - if (line[0] == '#') { - strlwr(line); - - if (!memcmp(line + 1, "include", 7)) { - wchar_t tszFileName[MAX_PATH]; - wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); - if (p) - *p = 0; - mir_snwprintf(tszFileName, L"%s\\%S", langPack.tszFullPath, ltrim(line + 9)); - if (p) - *p = '\\'; - - FILE *fpNew = _wfopen(tszFileName, L"r"); - if (fpNew) { - line[0] = 0; - fgets(line, LANGPACK_BUF_SIZE, fpNew); - - if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') - fseek(fpNew, 3, SEEK_SET); - else - fseek(fpNew, 0, SEEK_SET); - - LoadLangPackFile(fpNew, line); - fclose(fpNew); - } - } - else if (!memcmp(line + 1, "muuid", 5)) { - MUUID t; - if (!EnterMuuid(line + 7, t)) - continue; - - MUUID *pNew = (MUUID*)mir_alloc(sizeof(MUUID)); - memcpy(pNew, &t, sizeof(t)); - lMuuids.insert(pNew); - pCurrentMuuid = pNew; - } - - continue; - } - - char cFirst = line[0]; - - ConvertBackslashes(line, CP_UTF8); - - size_t cbLen = strlen(line) - 1; - if (cFirst == '[' && line[cbLen] == ']') { - if (g_entryCount && g_pEntries[g_entryCount-1].wszLocal == nullptr) - g_entryCount--; - - char *pszLine = line + 1; - line[cbLen] = '\0'; - if (++g_entryCount > g_entriesAlloced) { - g_entriesAlloced += 128; - g_pEntries = (LangPackEntry*)mir_realloc(g_pEntries, sizeof(LangPackEntry)*g_entriesAlloced); - } - - LangPackEntry *E = &g_pEntries[g_entryCount - 1]; - E->englishHash = mir_hashstr(pszLine); - E->szLocal = E->utfLocal = nullptr; - E->wszLocal = nullptr; - E->pMuuid = pCurrentMuuid; - E->pNext = nullptr; - continue; - } - - if (!g_entryCount) - continue; - - LangPackEntry *E = &g_pEntries[g_entryCount - 1]; - int iNeeded = MultiByteToWideChar(CP_UTF8, 0, line, -1, nullptr, 0), iOldLen; - if (E->wszLocal == nullptr) { - iOldLen = 0; - E->wszLocal = (wchar_t *)mir_alloc((iNeeded + 1) * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal, iNeeded); - } - else { - iOldLen = (int)wcslen(E->wszLocal); - E->wszLocal = (wchar_t*)mir_realloc(E->wszLocal, (sizeof(wchar_t)* (iOldLen + iNeeded + 2))); - E->wszLocal[iOldLen++] = '\n'; - } - MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal + iOldLen, iNeeded); - } -} - -static int LoadLangDescr(LANGPACK_INFO &lpinfo, FILE *fp, char *line, int &startOfLine) -{ - char szLanguage[64]; szLanguage[0] = 0; - CMStringA szAuthors; - - lpinfo.codepage = CP_ACP; - lpinfo.flags = 0; - lpinfo.tszLanguage[0] = 0; - - fgets(line, LANGPACK_BUF_SIZE, fp); - size_t lineLen = strlen(line); - if (lineLen >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') - memmove(line, line + 3, lineLen - 2); - - lrtrim(line); - if (mir_strcmp(line, "Miranda Language Pack Version 1")) - return 2; - - // headers - while (!feof(fp)) { - startOfLine = ftell(fp); - if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) - break; - - lrtrim(line); - if (IsEmpty(line) || line[0] == ';' || line[0] == 0) - continue; - - if (line[0] == '[' || line[0] == '#') - break; - - char *pszColon = strchr(line, ':'); - if (pszColon == nullptr) - return 3; - - *pszColon++ = 0; - if (!mir_strcmp(line, "Language")) { - strncpy_s(szLanguage, pszColon, _TRUNCATE); - lrtrim(szLanguage); - } - else if (!mir_strcmp(line, "Last-Modified-Using")) { - lpinfo.szLastModifiedUsing = pszColon; - lpinfo.szLastModifiedUsing.Trim(); - } - else if (!mir_strcmp(line, "Authors")) { - if (!szAuthors.IsEmpty()) - szAuthors.AppendChar(' '); - szAuthors.Append(lrtrim(pszColon)); - } - else if (!mir_strcmp(line, "Locale")) { - char szBuf[20], *stopped; - - lrtrim(pszColon + 1); - USHORT langID = (USHORT)strtol(pszColon, &stopped, 16); - lpinfo.Locale = MAKELCID(langID, 0); - GetLocaleInfoA(lpinfo.Locale, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10); - szBuf[5] = 0; // codepages have max. 5 digits - lpinfo.codepage = atoi(szBuf); - } - } - - lpinfo.szAuthors = szAuthors; - - ptrW buf(mir_utf8decodeW(szLanguage)); - if (buf) - wcsncpy_s(lpinfo.tszLanguage, buf, _TRUNCATE); - else if (lpinfo.Locale != 0) - GetLocaleInfo(lpinfo.Locale, LOCALE_SENGLANGUAGE, lpinfo.tszLanguage, _countof(lpinfo.tszLanguage)); - - if (!lpinfo.tszLanguage[0]) { - wchar_t *p = wcschr(lpinfo.tszFileName, '_'); - wcsncpy_s(lpinfo.tszLanguage, ((p != nullptr) ? (p + 1) : lpinfo.tszFileName), _TRUNCATE); - p = wcsrchr(lpinfo.tszLanguage, '.'); - if (p != nullptr) *p = '\0'; - } - return 0; -} - -MIR_CORE_DLL(int) LoadLangPack(const wchar_t *ptszLangPack) -{ - if (ptszLangPack == nullptr || !mir_wstrcmpi(ptszLangPack, L"")) - return 1; - - // ensure that a lang's name is a full file name - wchar_t tszFullPath[MAX_PATH]; - if (!PathIsAbsoluteW(ptszLangPack)) - mir_snwprintf(tszFullPath, L"%s\\%s", g_tszRoot, ptszLangPack); - else - wcsncpy_s(tszFullPath, ptszLangPack, _TRUNCATE); - - // this lang is already loaded? nothing to do then - if (!mir_wstrcmp(tszFullPath, langPack.tszFullPath)) - return 0; - - // ok... loading a new langpack. remove the old one if needed - if (g_entryCount) - UnloadLangPackModule(); - - langPack.Locale = 0; - langPack.codepage = CP_ACP; - langPack.flags = 0; - - // exists & not a directory? - uint32_t dwAttrib = GetFileAttributes(tszFullPath); - if (dwAttrib == INVALID_FILE_ATTRIBUTES || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) - return 3; - - // copy the full file name and extract a file name from it - wcsncpy_s(langPack.tszFullPath, tszFullPath, _TRUNCATE); - wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); - wcsncpy_s(langPack.tszFileName, (p == nullptr) ? tszFullPath : p + 1, _TRUNCATE); - CharLower(langPack.tszFileName); - - FILE *fp = _wfopen(tszFullPath, L"rt"); - if (fp == nullptr) - return 1; - - char line[LANGPACK_BUF_SIZE] = ""; - int startOfLine = 0; - if (LoadLangDescr(langPack, fp, line, startOfLine)) { - fclose(fp); - return 1; - } - - // body - fseek(fp, startOfLine, SEEK_SET); - - LoadLangPackFile(fp, line); - fclose(fp); - pCurrentMuuid = nullptr; - - qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); - return 0; -} - -MIR_CORE_DLL(int) LoadLangPackDescr(const wchar_t *ptszLangPack, LANGPACK_INFO *lpInfo) -{ - if (lpInfo == nullptr) - return 1; - - wcsncpy_s(lpInfo->tszFullPath, ptszLangPack, _TRUNCATE); - wchar_t *p = wcsrchr(lpInfo->tszFullPath, '\\'); - wcsncpy_s(lpInfo->tszFileName, (p == nullptr) ? ptszLangPack : p+1, _TRUNCATE); - CharLower(lpInfo->tszFileName); - - FILE *fp = _wfopen(ptszLangPack, L"rt"); - if (fp == nullptr) - return 1; - - char line[LANGPACK_BUF_SIZE] = ""; - int startOfLine = 0; - int res = LoadLangDescr(*lpInfo, fp, line, startOfLine); - fclose(fp); - return res; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static int SortLangPackHashesProc2(LangPackEntry *arg1, LangPackEntry *arg2) -{ - if (arg1->englishHash < arg2->englishHash) return -1; - if (arg1->englishHash > arg2->englishHash) return 1; - return 0; -} - -char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W) -{ - if (g_entryCount == 0 || szEnglish == nullptr) - return (char*)szEnglish; - - LangPackEntry key, *entry; - key.englishHash = (W == 1) ? hashstrW(szEnglish) : mir_hashstr(szEnglish); - entry = (LangPackEntry*)bsearch(&key, g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc2); - if (entry == nullptr) - return (char*)szEnglish; - - // try to find the exact match, otherwise the first entry will be returned - if (pUuid) { - for (LangPackEntry *p = entry->pNext; p != nullptr; p = p->pNext) { - if (p->pMuuid && *p->pMuuid == *pUuid) { - entry = p; - break; - } - } - } - - switch (W) { - case 0: - if (entry->szLocal == nullptr && entry->wszLocal != nullptr) - entry->szLocal = mir_u2a_cp(entry->wszLocal, langPack.codepage); - return entry->szLocal; - - case 1: - return (char*)entry->wszLocal; - - case 2: - if (entry->utfLocal == nullptr && entry->wszLocal != nullptr) - entry->utfLocal = mir_utf8encodeW(entry->wszLocal); - return entry->utfLocal; - } - - return nullptr; -} - -MIR_CORE_DLL(int) Langpack_GetDefaultCodePage() -{ - return langPack.codepage; -} - -MIR_CORE_DLL(int) Langpack_GetDefaultLocale() -{ - return (langPack.Locale == 0) ? LOCALE_USER_DEFAULT : langPack.Locale; -} - -MIR_CORE_DLL(wchar_t*) Langpack_PcharToTchar(const char *pszStr) -{ - if (pszStr == nullptr) - return nullptr; - - int len = (int)strlen(pszStr); - wchar_t *result = (wchar_t*)alloca((len + 1)*sizeof(wchar_t)); - MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pszStr, -1, result, len); - result[len] = 0; - return mir_wstrdup(TranslateW_LP(result)); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(char*) TranslateA_LP(const char *str, HPLUGIN pPlugin) -{ - return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 0); -} - -MIR_CORE_DLL(char*) TranslateU_LP(const char *str, HPLUGIN pPlugin) -{ - return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 2); -} - -MIR_CORE_DLL(wchar_t*) TranslateW_LP(const wchar_t *str, HPLUGIN pPlugin) -{ - return (wchar_t*)LangPackTranslateString(GetMuid(pPlugin), (LPCSTR)str, 1); -} - -MIR_CORE_DLL(void) TranslateMenu_LP(HMENU hMenu, HPLUGIN pPlugin) -{ - const MUUID *uuid = &pPlugin->getInfo().uuid; - - MENUITEMINFO mii = { 0 }; - mii.cbSize = sizeof(mii); - - for (int i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) { - wchar_t str[256]; - mii.fMask = MIIM_TYPE | MIIM_SUBMENU; - mii.dwTypeData = (wchar_t*)str; - mii.cch = _countof(str); - GetMenuItemInfo(hMenu, i, TRUE, &mii); - - if (mii.cch && mii.dwTypeData) { - wchar_t *result = (wchar_t*)LangPackTranslateString(uuid, (const char*)mii.dwTypeData, TRUE); - if (result != mii.dwTypeData) { - mii.dwTypeData = result; - mii.fMask = MIIM_TYPE; - SetMenuItemInfo(hMenu, i, TRUE, &mii); - } - } - - if (mii.hSubMenu != nullptr) - TranslateMenu_LP(mii.hSubMenu, pPlugin); - } -} - -static void TranslateWindow(const MUUID *pUuid, HWND hwnd) -{ - wchar_t title[2048]; - GetWindowText(hwnd, title, _countof(title)); - - wchar_t *result = (wchar_t*)LangPackTranslateString(pUuid, (const char*)title, TRUE); - if (result != title) - SetWindowText(hwnd, result); -} - -static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd, LPARAM lParam) -{ - HPLUGIN pPlugin = (HPLUGIN)lParam; - const MUUID *uuid = GetMuid(pPlugin); - - wchar_t szClass[32]; - GetClassName(hwnd, szClass, _countof(szClass)); - if (!mir_wstrcmpi(szClass, L"static") || !mir_wstrcmpi(szClass, L"hyperlink") || !mir_wstrcmpi(szClass, L"button") || !mir_wstrcmpi(szClass, L"MButtonClass") || !mir_wstrcmpi(szClass, L"MHeaderbarCtrl")) - TranslateWindow(uuid, hwnd); - else if (!mir_wstrcmpi(szClass, L"edit")) { - if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) - TranslateWindow(uuid, hwnd); - } - return TRUE; -} - -MIR_CORE_DLL(void) TranslateDialog_LP(HWND hDlg, HPLUGIN pPlugin) -{ - TranslateWindow(GetMuid(pPlugin), hDlg); - EnumChildWindows(hDlg, TranslateDialogEnumProc, (LPARAM)pPlugin); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Langpack_SortDuplicates(void) -{ - if (g_entryCount == 0) - return; - - LangPackEntry *s = g_pEntries + 1, *d = s, *pLast = g_pEntries; - uint32_t dwSavedHash = g_pEntries->englishHash; - bool bSortNeeded = false; - - for (int i = 1; i < g_entryCount; i++, s++) { - if (s->englishHash != dwSavedHash) { - pLast = d; - if (s != d) - *d++ = *s; - else - d++; - dwSavedHash = s->englishHash; - } - else { - bSortNeeded = true; - LangPackEntry *p = (LangPackEntry*)mir_alloc(sizeof(LangPackEntry)); - *p = *s; - pLast->pNext = p; pLast = p; - } - } - - if (bSortNeeded) { - g_entryCount = (int)(d - g_pEntries); - qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void GetDefaultLang() -{ - // calculate the langpacks' root - PathToAbsoluteW(L"\\Languages", g_tszRoot); - if (_waccess(g_tszRoot, 0) != 0) // directory Languages exists - PathToAbsoluteW(L".", g_tszRoot); - - // look into mirandaboot.ini - wchar_t tszLangName[256]; - Profile_GetSetting(L"Language/DefaultLanguage", tszLangName); - if (tszLangName[0]) { - if (!mir_wstrcmpi(tszLangName, L"default")) { - db_set_ws(0, "Langpack", "Current", L"default"); - return; - } - if (!LoadLangPack(tszLangName)) { - db_set_ws(0, "Langpack", "Current", tszLangName); - return; - } - } - - // try to load langpack that matches UserDefaultUILanguage - wchar_t tszPath[MAX_PATH]; - if (GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SENGLANGUAGE, tszLangName, _countof(tszLangName))) { - mir_snwprintf(tszPath, L"langpack_%s.txt", wcslwr(tszLangName)); - if (!LoadLangPack(tszPath)) { - db_set_ws(0, "Langpack", "Current", tszPath); - return; - } - } - - // finally try to load first file - mir_snwprintf(tszPath, L"%s\\langpack_*.txt", g_tszRoot); - - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile(tszPath, &fd); - if (hFind != INVALID_HANDLE_VALUE) { - do { - /* search first langpack that could be loaded */ - if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - continue; - - if (!LoadLangPack(fd.cFileName)) { - db_set_ws(0, "Langpack", "Current", fd.cFileName); - break; - } - } while (FindNextFile(hFind, &fd)); - FindClose(hFind); - } - else db_set_ws(0, "Langpack", "Current", L"default"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) ReloadLangpack(wchar_t *pszStr) -{ - if (pszStr == nullptr) - pszStr = NEWWSTR_ALLOCA(langPack.tszFileName); - - UnloadLangPackModule(); - LoadLangPack(pszStr); - Langpack_SortDuplicates(); - - NotifyEventHooks(hevChanged, 0, 0); -} - -static INT_PTR srvReloadLangpack(WPARAM, LPARAM lParam) -{ - ReloadLangpack((wchar_t*)lParam); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) LoadLangPackModule(void) -{ - bModuleInitialized = TRUE; - hevChanged = CreateHookableEvent(ME_LANGPACK_CHANGED); - CreateServiceFunction(MS_LANGPACK_RELOAD, srvReloadLangpack); - GetDefaultLang(); - return 0; -} - -void UnloadLangPackModule() -{ - if (!bModuleInitialized) return; - - for (auto &it : lMuuids) - mir_free(it); - lMuuids.destroy(); - - LangPackEntry *p = g_pEntries; - for (int i = 0; i < g_entryCount; i++, p++) { - if (p->pNext != nullptr) { - for (LangPackEntry *p1 = p->pNext; p1 != nullptr;) { - LangPackEntry *p2 = p1; p1 = p1->pNext; - mir_free(p2->szLocal); - mir_free(p2->wszLocal); - mir_free(p2); - } - } - - mir_free(p->szLocal); - mir_free(p->wszLocal); - } - - if (g_entryCount) { - mir_free(g_pEntries); - g_pEntries = nullptr; - g_entryCount = g_entriesAlloced = 0; - } - - langPack.tszFileName[0] = langPack.tszFullPath[0] = 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +#include "../../../mir_app/src/langpack.h" + +#define LANGPACK_BUF_SIZE 4000 + +static int CompareMuuids(const MUUID *p1, const MUUID *p2) +{ + return memcmp(p1, p2, sizeof(MUUID)); +} + +static LIST lMuuids(10, CompareMuuids); +static MUUID *pCurrentMuuid = nullptr; +static HANDLE hevChanged = nullptr; + +static BOOL bModuleInitialized = FALSE; + +struct LangPackEntry +{ + uint32_t englishHash; + char *szLocal; + char *utfLocal; + wchar_t *wszLocal; + MUUID *pMuuid; + LangPackEntry* pNext; // for langpack items with the same hash value +}; + +static LANGPACK_INFO langPack; +static wchar_t g_tszRoot[MAX_PATH]; + +static LangPackEntry *g_pEntries; +static int g_entryCount, g_entriesAlloced; + +static int IsEmpty(const char *str) +{ + for (int i = 0; str[i]; i++) + if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') + return 0; + + return 1; +} + +static int ConvertBackslashes(char *str, UINT fileCp) +{ + int shift = 0; + char *pstr; + for (pstr = str; *pstr; pstr = CharNextExA(fileCp, pstr, 0)) { + if (*pstr == '\\') { + shift++; + switch (pstr[1]) { + case 'n': *pstr = '\n'; break; + case 't': *pstr = '\t'; break; + case 'r': *pstr = '\r'; break; + case 's': *pstr = ' '; break; + default: *pstr = pstr[1]; break; + } + memmove(pstr + 1, pstr + 2, strlen(pstr + 2) + 1); + } + } + return shift; +} + +#ifdef _DEBUG +//#pragma optimize("gt", on) +#endif + +// MurmurHash2 +MIR_CORE_DLL(unsigned int) mir_hash(const void * key, unsigned int len) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + const unsigned int m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + unsigned int h = len; + + // Mix 4 bytes at a time into the hash + const unsigned char *data = (const unsigned char*)key; + + while (len >= 4) { + unsigned int k = *(unsigned int*)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + switch (len) { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + } + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +static unsigned int __fastcall hashstrW(const char *key) +{ + if (key == nullptr) return 0; + const unsigned int len = (unsigned int)wcslen((const wchar_t*)key); + char *buf = (char*)alloca(len + 1); + for (unsigned i = 0; i <= len; ++i) + buf[i] = key[i << 1]; + return mir_hash(buf, len); +} + +static const MUUID* GetMuid(HPLUGIN pPlugin) +{ + if (!pPlugin) + return nullptr; + + __try { + return &pPlugin->getInfo().uuid; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return nullptr; + } +} + +static int SortLangPackHashesProc(LangPackEntry *arg1, LangPackEntry *arg2) +{ + if (arg1->englishHash < arg2->englishHash) return -1; + if (arg1->englishHash > arg2->englishHash) return 1; + + return (arg1->pMuuid < arg2->pMuuid) ? -1 : 1; +} + +static void swapBytes(void *p, size_t iSize) +{ + char *head = (char*)p; // here + char *tail = head + iSize - 1; + + for (; tail > head; --tail, ++head) { + char temp = *head; + *head = *tail; + *tail = temp; + } +} + +static bool EnterMuuid(const char *p, MUUID &result) +{ + if (*p++ != '{') + return false; + + uint8_t *d = (uint8_t*)&result; + + for (int nBytes = 0; *p && nBytes < 24; p++) { + if (*p == '-') + continue; + + if (*p == '}') + break; + + if (!isxdigit(*p)) + return false; + + if (!isxdigit(p[1])) + return false; + + int c = 0; + if (sscanf(p, "%2x", &c) != 1) + return false; + + *d++ = (uint8_t)c; + nBytes++; + p++; + } + + if (*p != '}') + return false; + + swapBytes(&result.a, sizeof(result.a)); + swapBytes(&result.b, sizeof(result.b)); + swapBytes(&result.c, sizeof(result.c)); + return true; +} + +static void LoadLangPackFile(FILE *fp, char *line) +{ + while (!feof(fp)) { + if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) + break; + + if (IsEmpty(line) || line[0] == ';' || line[0] == 0) + continue; + + rtrim(line); + + if (line[0] == '#') { + strlwr(line); + + if (!memcmp(line + 1, "include", 7)) { + wchar_t tszFileName[MAX_PATH]; + wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); + if (p) + *p = 0; + mir_snwprintf(tszFileName, L"%s\\%S", langPack.tszFullPath, ltrim(line + 9)); + if (p) + *p = '\\'; + + FILE *fpNew = _wfopen(tszFileName, L"r"); + if (fpNew) { + line[0] = 0; + fgets(line, LANGPACK_BUF_SIZE, fpNew); + + if (strlen(line) >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') + fseek(fpNew, 3, SEEK_SET); + else + fseek(fpNew, 0, SEEK_SET); + + LoadLangPackFile(fpNew, line); + fclose(fpNew); + } + } + else if (!memcmp(line + 1, "muuid", 5)) { + MUUID t; + if (!EnterMuuid(line + 7, t)) + continue; + + MUUID *pNew = (MUUID*)mir_alloc(sizeof(MUUID)); + memcpy(pNew, &t, sizeof(t)); + lMuuids.insert(pNew); + pCurrentMuuid = pNew; + } + + continue; + } + + char cFirst = line[0]; + + ConvertBackslashes(line, CP_UTF8); + + size_t cbLen = strlen(line) - 1; + if (cFirst == '[' && line[cbLen] == ']') { + if (g_entryCount && g_pEntries[g_entryCount-1].wszLocal == nullptr) + g_entryCount--; + + char *pszLine = line + 1; + line[cbLen] = '\0'; + if (++g_entryCount > g_entriesAlloced) { + g_entriesAlloced += 128; + g_pEntries = (LangPackEntry*)mir_realloc(g_pEntries, sizeof(LangPackEntry)*g_entriesAlloced); + } + + LangPackEntry *E = &g_pEntries[g_entryCount - 1]; + E->englishHash = mir_hashstr(pszLine); + E->szLocal = E->utfLocal = nullptr; + E->wszLocal = nullptr; + E->pMuuid = pCurrentMuuid; + E->pNext = nullptr; + continue; + } + + if (!g_entryCount) + continue; + + LangPackEntry *E = &g_pEntries[g_entryCount - 1]; + int iNeeded = MultiByteToWideChar(CP_UTF8, 0, line, -1, nullptr, 0), iOldLen; + if (E->wszLocal == nullptr) { + iOldLen = 0; + E->wszLocal = (wchar_t *)mir_alloc((iNeeded + 1) * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal, iNeeded); + } + else { + iOldLen = (int)wcslen(E->wszLocal); + E->wszLocal = (wchar_t*)mir_realloc(E->wszLocal, (sizeof(wchar_t)* (iOldLen + iNeeded + 2))); + E->wszLocal[iOldLen++] = '\n'; + } + MultiByteToWideChar(CP_UTF8, 0, line, -1, E->wszLocal + iOldLen, iNeeded); + } +} + +static int LoadLangDescr(LANGPACK_INFO &lpinfo, FILE *fp, char *line, int &startOfLine) +{ + char szLanguage[64]; szLanguage[0] = 0; + CMStringA szAuthors; + + lpinfo.codepage = CP_ACP; + lpinfo.flags = 0; + lpinfo.tszLanguage[0] = 0; + + fgets(line, LANGPACK_BUF_SIZE, fp); + size_t lineLen = strlen(line); + if (lineLen >= 3 && line[0] == '\xef' && line[1] == '\xbb' && line[2] == '\xbf') + memmove(line, line + 3, lineLen - 2); + + lrtrim(line); + if (mir_strcmp(line, "Miranda Language Pack Version 1")) + return 2; + + // headers + while (!feof(fp)) { + startOfLine = ftell(fp); + if (fgets(line, LANGPACK_BUF_SIZE, fp) == nullptr) + break; + + lrtrim(line); + if (IsEmpty(line) || line[0] == ';' || line[0] == 0) + continue; + + if (line[0] == '[' || line[0] == '#') + break; + + char *pszColon = strchr(line, ':'); + if (pszColon == nullptr) + return 3; + + *pszColon++ = 0; + if (!mir_strcmp(line, "Language")) { + strncpy_s(szLanguage, pszColon, _TRUNCATE); + lrtrim(szLanguage); + } + else if (!mir_strcmp(line, "Last-Modified-Using")) { + lpinfo.szLastModifiedUsing = pszColon; + lpinfo.szLastModifiedUsing.Trim(); + } + else if (!mir_strcmp(line, "Authors")) { + if (!szAuthors.IsEmpty()) + szAuthors.AppendChar(' '); + szAuthors.Append(lrtrim(pszColon)); + } + else if (!mir_strcmp(line, "Locale")) { + char szBuf[20], *stopped; + + lrtrim(pszColon + 1); + USHORT langID = (USHORT)strtol(pszColon, &stopped, 16); + lpinfo.Locale = MAKELCID(langID, 0); + GetLocaleInfoA(lpinfo.Locale, LOCALE_IDEFAULTANSICODEPAGE, szBuf, 10); + szBuf[5] = 0; // codepages have max. 5 digits + lpinfo.codepage = atoi(szBuf); + } + } + + lpinfo.szAuthors = szAuthors; + + ptrW buf(mir_utf8decodeW(szLanguage)); + if (buf) + wcsncpy_s(lpinfo.tszLanguage, buf, _TRUNCATE); + else if (lpinfo.Locale != 0) + GetLocaleInfo(lpinfo.Locale, LOCALE_SENGLANGUAGE, lpinfo.tszLanguage, _countof(lpinfo.tszLanguage)); + + if (!lpinfo.tszLanguage[0]) { + wchar_t *p = wcschr(lpinfo.tszFileName, '_'); + wcsncpy_s(lpinfo.tszLanguage, ((p != nullptr) ? (p + 1) : lpinfo.tszFileName), _TRUNCATE); + p = wcsrchr(lpinfo.tszLanguage, '.'); + if (p != nullptr) *p = '\0'; + } + return 0; +} + +MIR_CORE_DLL(int) LoadLangPack(const wchar_t *ptszLangPack) +{ + if (ptszLangPack == nullptr || !mir_wstrcmpi(ptszLangPack, L"")) + return 1; + + // ensure that a lang's name is a full file name + wchar_t tszFullPath[MAX_PATH]; + if (!PathIsAbsoluteW(ptszLangPack)) + mir_snwprintf(tszFullPath, L"%s\\%s", g_tszRoot, ptszLangPack); + else + wcsncpy_s(tszFullPath, ptszLangPack, _TRUNCATE); + + // this lang is already loaded? nothing to do then + if (!mir_wstrcmp(tszFullPath, langPack.tszFullPath)) + return 0; + + // ok... loading a new langpack. remove the old one if needed + if (g_entryCount) + UnloadLangPackModule(); + + langPack.Locale = 0; + langPack.codepage = CP_ACP; + langPack.flags = 0; + + // exists & not a directory? + uint32_t dwAttrib = GetFileAttributes(tszFullPath); + if (dwAttrib == INVALID_FILE_ATTRIBUTES || (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) + return 3; + + // copy the full file name and extract a file name from it + wcsncpy_s(langPack.tszFullPath, tszFullPath, _TRUNCATE); + wchar_t *p = wcsrchr(langPack.tszFullPath, '\\'); + wcsncpy_s(langPack.tszFileName, (p == nullptr) ? tszFullPath : p + 1, _TRUNCATE); + CharLower(langPack.tszFileName); + + FILE *fp = _wfopen(tszFullPath, L"rt"); + if (fp == nullptr) + return 1; + + char line[LANGPACK_BUF_SIZE] = ""; + int startOfLine = 0; + if (LoadLangDescr(langPack, fp, line, startOfLine)) { + fclose(fp); + return 1; + } + + // body + fseek(fp, startOfLine, SEEK_SET); + + LoadLangPackFile(fp, line); + fclose(fp); + pCurrentMuuid = nullptr; + + qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); + return 0; +} + +MIR_CORE_DLL(int) LoadLangPackDescr(const wchar_t *ptszLangPack, LANGPACK_INFO *lpInfo) +{ + if (lpInfo == nullptr) + return 1; + + wcsncpy_s(lpInfo->tszFullPath, ptszLangPack, _TRUNCATE); + wchar_t *p = wcsrchr(lpInfo->tszFullPath, '\\'); + wcsncpy_s(lpInfo->tszFileName, (p == nullptr) ? ptszLangPack : p+1, _TRUNCATE); + CharLower(lpInfo->tszFileName); + + FILE *fp = _wfopen(ptszLangPack, L"rt"); + if (fp == nullptr) + return 1; + + char line[LANGPACK_BUF_SIZE] = ""; + int startOfLine = 0; + int res = LoadLangDescr(*lpInfo, fp, line, startOfLine); + fclose(fp); + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static int SortLangPackHashesProc2(LangPackEntry *arg1, LangPackEntry *arg2) +{ + if (arg1->englishHash < arg2->englishHash) return -1; + if (arg1->englishHash > arg2->englishHash) return 1; + return 0; +} + +char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W) +{ + if (g_entryCount == 0 || szEnglish == nullptr) + return (char*)szEnglish; + + LangPackEntry key, *entry; + key.englishHash = (W == 1) ? hashstrW(szEnglish) : mir_hashstr(szEnglish); + entry = (LangPackEntry*)bsearch(&key, g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc2); + if (entry == nullptr) + return (char*)szEnglish; + + // try to find the exact match, otherwise the first entry will be returned + if (pUuid) { + for (LangPackEntry *p = entry->pNext; p != nullptr; p = p->pNext) { + if (p->pMuuid && *p->pMuuid == *pUuid) { + entry = p; + break; + } + } + } + + switch (W) { + case 0: + if (entry->szLocal == nullptr && entry->wszLocal != nullptr) + entry->szLocal = mir_u2a_cp(entry->wszLocal, langPack.codepage); + return entry->szLocal; + + case 1: + return (char*)entry->wszLocal; + + case 2: + if (entry->utfLocal == nullptr && entry->wszLocal != nullptr) + entry->utfLocal = mir_utf8encodeW(entry->wszLocal); + return entry->utfLocal; + } + + return nullptr; +} + +MIR_CORE_DLL(int) Langpack_GetDefaultCodePage() +{ + return langPack.codepage; +} + +MIR_CORE_DLL(int) Langpack_GetDefaultLocale() +{ + return (langPack.Locale == 0) ? LOCALE_USER_DEFAULT : langPack.Locale; +} + +MIR_CORE_DLL(wchar_t*) Langpack_PcharToTchar(const char *pszStr) +{ + if (pszStr == nullptr) + return nullptr; + + int len = (int)strlen(pszStr); + wchar_t *result = (wchar_t*)alloca((len + 1)*sizeof(wchar_t)); + MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pszStr, -1, result, len); + result[len] = 0; + return mir_wstrdup(TranslateW_LP(result)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(char*) TranslateA_LP(const char *str, HPLUGIN pPlugin) +{ + return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 0); +} + +MIR_CORE_DLL(char*) TranslateU_LP(const char *str, HPLUGIN pPlugin) +{ + return (char*)LangPackTranslateString(GetMuid(pPlugin), str, 2); +} + +MIR_CORE_DLL(wchar_t*) TranslateW_LP(const wchar_t *str, HPLUGIN pPlugin) +{ + return (wchar_t*)LangPackTranslateString(GetMuid(pPlugin), (LPCSTR)str, 1); +} + +MIR_CORE_DLL(void) TranslateMenu_LP(HMENU hMenu, HPLUGIN pPlugin) +{ + const MUUID *uuid = &pPlugin->getInfo().uuid; + + MENUITEMINFO mii = { 0 }; + mii.cbSize = sizeof(mii); + + for (int i = GetMenuItemCount(hMenu) - 1; i >= 0; i--) { + wchar_t str[256]; + mii.fMask = MIIM_TYPE | MIIM_SUBMENU; + mii.dwTypeData = (wchar_t*)str; + mii.cch = _countof(str); + GetMenuItemInfo(hMenu, i, TRUE, &mii); + + if (mii.cch && mii.dwTypeData) { + wchar_t *result = (wchar_t*)LangPackTranslateString(uuid, (const char*)mii.dwTypeData, TRUE); + if (result != mii.dwTypeData) { + mii.dwTypeData = result; + mii.fMask = MIIM_TYPE; + SetMenuItemInfo(hMenu, i, TRUE, &mii); + } + } + + if (mii.hSubMenu != nullptr) + TranslateMenu_LP(mii.hSubMenu, pPlugin); + } +} + +static void TranslateWindow(const MUUID *pUuid, HWND hwnd) +{ + wchar_t title[2048]; + GetWindowText(hwnd, title, _countof(title)); + + wchar_t *result = (wchar_t*)LangPackTranslateString(pUuid, (const char*)title, TRUE); + if (result != title) + SetWindowText(hwnd, result); +} + +static BOOL CALLBACK TranslateDialogEnumProc(HWND hwnd, LPARAM lParam) +{ + HPLUGIN pPlugin = (HPLUGIN)lParam; + const MUUID *uuid = GetMuid(pPlugin); + + wchar_t szClass[32]; + GetClassName(hwnd, szClass, _countof(szClass)); + if (!mir_wstrcmpi(szClass, L"static") || !mir_wstrcmpi(szClass, L"hyperlink") || !mir_wstrcmpi(szClass, L"button") || !mir_wstrcmpi(szClass, L"MButtonClass") || !mir_wstrcmpi(szClass, L"MHeaderbarCtrl")) + TranslateWindow(uuid, hwnd); + else if (!mir_wstrcmpi(szClass, L"edit")) { + if (GetWindowLongPtr(hwnd, GWL_STYLE) & ES_READONLY) + TranslateWindow(uuid, hwnd); + } + return TRUE; +} + +MIR_CORE_DLL(void) TranslateDialog_LP(HWND hDlg, HPLUGIN pPlugin) +{ + TranslateWindow(GetMuid(pPlugin), hDlg); + EnumChildWindows(hDlg, TranslateDialogEnumProc, (LPARAM)pPlugin); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(void) Langpack_SortDuplicates(void) +{ + if (g_entryCount == 0) + return; + + LangPackEntry *s = g_pEntries + 1, *d = s, *pLast = g_pEntries; + uint32_t dwSavedHash = g_pEntries->englishHash; + bool bSortNeeded = false; + + for (int i = 1; i < g_entryCount; i++, s++) { + if (s->englishHash != dwSavedHash) { + pLast = d; + if (s != d) + *d++ = *s; + else + d++; + dwSavedHash = s->englishHash; + } + else { + bSortNeeded = true; + LangPackEntry *p = (LangPackEntry*)mir_alloc(sizeof(LangPackEntry)); + *p = *s; + pLast->pNext = p; pLast = p; + } + } + + if (bSortNeeded) { + g_entryCount = (int)(d - g_pEntries); + qsort(g_pEntries, g_entryCount, sizeof(LangPackEntry), (int(*)(const void*, const void*))SortLangPackHashesProc); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void GetDefaultLang() +{ + // calculate the langpacks' root + PathToAbsoluteW(L"\\Languages", g_tszRoot); + if (_waccess(g_tszRoot, 0) != 0) // directory Languages exists + PathToAbsoluteW(L".", g_tszRoot); + + // look into mirandaboot.ini + wchar_t tszLangName[256]; + Profile_GetSetting(L"Language/DefaultLanguage", tszLangName); + if (tszLangName[0]) { + if (!mir_wstrcmpi(tszLangName, L"default")) { + db_set_ws(0, "Langpack", "Current", L"default"); + return; + } + if (!LoadLangPack(tszLangName)) { + db_set_ws(0, "Langpack", "Current", tszLangName); + return; + } + } + + // try to load langpack that matches UserDefaultUILanguage + wchar_t tszPath[MAX_PATH]; + if (GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SENGLANGUAGE, tszLangName, _countof(tszLangName))) { + mir_snwprintf(tszPath, L"langpack_%s.txt", wcslwr(tszLangName)); + if (!LoadLangPack(tszPath)) { + db_set_ws(0, "Langpack", "Current", tszPath); + return; + } + } + + // finally try to load first file + mir_snwprintf(tszPath, L"%s\\langpack_*.txt", g_tszRoot); + + WIN32_FIND_DATA fd; + HANDLE hFind = FindFirstFile(tszPath, &fd); + if (hFind != INVALID_HANDLE_VALUE) { + do { + /* search first langpack that could be loaded */ + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + if (!LoadLangPack(fd.cFileName)) { + db_set_ws(0, "Langpack", "Current", fd.cFileName); + break; + } + } while (FindNextFile(hFind, &fd)); + FindClose(hFind); + } + else db_set_ws(0, "Langpack", "Current", L"default"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(void) ReloadLangpack(wchar_t *pszStr) +{ + if (pszStr == nullptr) + pszStr = NEWWSTR_ALLOCA(langPack.tszFileName); + + UnloadLangPackModule(); + LoadLangPack(pszStr); + Langpack_SortDuplicates(); + + NotifyEventHooks(hevChanged, 0, 0); +} + +static INT_PTR srvReloadLangpack(WPARAM, LPARAM lParam) +{ + ReloadLangpack((wchar_t*)lParam); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) LoadLangPackModule(void) +{ + bModuleInitialized = TRUE; + hevChanged = CreateHookableEvent(ME_LANGPACK_CHANGED); + CreateServiceFunction(MS_LANGPACK_RELOAD, srvReloadLangpack); + GetDefaultLang(); + return 0; +} + +void UnloadLangPackModule() +{ + if (!bModuleInitialized) return; + + for (auto &it : lMuuids) + mir_free(it); + lMuuids.destroy(); + + LangPackEntry *p = g_pEntries; + for (int i = 0; i < g_entryCount; i++, p++) { + if (p->pNext != nullptr) { + for (LangPackEntry *p1 = p->pNext; p1 != nullptr;) { + LangPackEntry *p2 = p1; p1 = p1->pNext; + mir_free(p2->szLocal); + mir_free(p2->wszLocal); + mir_free(p2); + } + } + + mir_free(p->szLocal); + mir_free(p->wszLocal); + } + + if (g_entryCount) { + mir_free(g_pEntries); + g_pEntries = nullptr; + g_entryCount = g_entriesAlloced = 0; + } + + langPack.tszFileName[0] = langPack.tszFullPath[0] = 0; +} diff --git a/src/mir_core/src/Windows/locks.cpp b/src/mir_core/src/Windows/locks.cpp index 88f4639e80..dca0e98966 100644 --- a/src/mir_core/src/Windows/locks.cpp +++ b/src/mir_core/src/Windows/locks.cpp @@ -1,46 +1,46 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -mir_cs::mir_cs() -{ - ::InitializeCriticalSection(&m_cs); -} - -mir_cs::~mir_cs() -{ - ::DeleteCriticalSection(&m_cs); -} - -void mir_cs::Lock() -{ - while (::TryEnterCriticalSection(&m_cs) == 0) - SleepEx(50, TRUE); -} - -void mir_cs::Unlock() -{ - ::LeaveCriticalSection(&m_cs); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +mir_cs::mir_cs() +{ + ::InitializeCriticalSection(&m_cs); +} + +mir_cs::~mir_cs() +{ + ::DeleteCriticalSection(&m_cs); +} + +void mir_cs::Lock() +{ + while (::TryEnterCriticalSection(&m_cs) == 0) + SleepEx(50, TRUE); +} + +void mir_cs::Unlock() +{ + ::LeaveCriticalSection(&m_cs); +} diff --git a/src/mir_core/src/Windows/miranda.cpp b/src/mir_core/src/Windows/miranda.cpp index d13e69ff1f..ce52b20b0b 100644 --- a/src/mir_core/src/Windows/miranda.cpp +++ b/src/mir_core/src/Windows/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/Windows/openurl.cpp b/src/mir_core/src/Windows/openurl.cpp index 3a9a3d2024..4bb8310e75 100644 --- a/src/mir_core/src/Windows/openurl.cpp +++ b/src/mir_core/src/Windows/openurl.cpp @@ -1,76 +1,76 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" -#include - -struct TOpenUrlInfo -{ - TOpenUrlInfo(wchar_t *_url, int _bNew) : - szUrl(_url), - newWindow(_bNew) - {} - - ptrW szUrl; - int newWindow; -}; - -static void __cdecl OpenURLThread(TOpenUrlInfo *hUrlInfo) -{ - // wack a protocol on it - CMStringW tszUrl; - if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\') - tszUrl.Format(L"file:///%s", hUrlInfo->szUrl.get()); - else { - int i; - for (i = 0; iswalpha(hUrlInfo->szUrl[i]); i++); - if (hUrlInfo->szUrl[i] == ':') - tszUrl = hUrlInfo->szUrl; - else if (!wcsnicmp(hUrlInfo->szUrl, L"ftp.", 4)) - tszUrl.Format(L"ftp://%s", hUrlInfo->szUrl.get()); - else - tszUrl.Format(L"http://%s", hUrlInfo->szUrl.get()); - } - - // check user defined browser for opening urls - ptrW tszBrowser(db_get_wsa(0, "Miranda", "OpenUrlBrowser")); - if (tszBrowser) - ShellExecute(nullptr, L"open", tszBrowser, tszUrl, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); - else - ShellExecute(nullptr, L"open", tszUrl, nullptr, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); - - delete hUrlInfo; -} - -MIR_CORE_DLL(void) Utils_OpenUrl(const char *pszUrl, bool bOpenInNewWindow) -{ - if (pszUrl) - mir_forkThread(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow)); -} - -MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow) -{ - if (pszUrl) - mir_forkThread(OpenURLThread, new TOpenUrlInfo(mir_wstrdup(pszUrl), bOpenInNewWindow)); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" +#include + +struct TOpenUrlInfo +{ + TOpenUrlInfo(wchar_t *_url, int _bNew) : + szUrl(_url), + newWindow(_bNew) + {} + + ptrW szUrl; + int newWindow; +}; + +static void __cdecl OpenURLThread(TOpenUrlInfo *hUrlInfo) +{ + // wack a protocol on it + CMStringW tszUrl; + if ((isalpha(hUrlInfo->szUrl[0]) && hUrlInfo->szUrl[1] == ':') || hUrlInfo->szUrl[0] == '\\') + tszUrl.Format(L"file:///%s", hUrlInfo->szUrl.get()); + else { + int i; + for (i = 0; iswalpha(hUrlInfo->szUrl[i]); i++); + if (hUrlInfo->szUrl[i] == ':') + tszUrl = hUrlInfo->szUrl; + else if (!wcsnicmp(hUrlInfo->szUrl, L"ftp.", 4)) + tszUrl.Format(L"ftp://%s", hUrlInfo->szUrl.get()); + else + tszUrl.Format(L"http://%s", hUrlInfo->szUrl.get()); + } + + // check user defined browser for opening urls + ptrW tszBrowser(db_get_wsa(0, "Miranda", "OpenUrlBrowser")); + if (tszBrowser) + ShellExecute(nullptr, L"open", tszBrowser, tszUrl, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); + else + ShellExecute(nullptr, L"open", tszUrl, nullptr, nullptr, (hUrlInfo->newWindow) ? SW_NORMAL : SW_SHOWDEFAULT); + + delete hUrlInfo; +} + +MIR_CORE_DLL(void) Utils_OpenUrl(const char *pszUrl, bool bOpenInNewWindow) +{ + if (pszUrl) + mir_forkThread(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow)); +} + +MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow) +{ + if (pszUrl) + mir_forkThread(OpenURLThread, new TOpenUrlInfo(mir_wstrdup(pszUrl), bOpenInNewWindow)); +} diff --git a/src/mir_core/src/Windows/path.cpp b/src/mir_core/src/Windows/path.cpp index 5623ad416f..eb14c6b0de 100644 --- a/src/mir_core/src/Windows/path.cpp +++ b/src/mir_core/src/Windows/path.cpp @@ -1,246 +1,246 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -static char szMirandaPath[MAX_PATH]; -static wchar_t szMirandaPathW[MAX_PATH]; - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) PathIsAbsolute(const char *path) -{ - if (path && strlen(path) > 2) - if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) - return 1; - return 0; -} - -MIR_CORE_DLL(int) PathToRelative(const char *pSrc, char *pOut, const char *pBase) -{ - if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - if (!PathIsAbsolute(pSrc)) - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - else { - if (pBase == nullptr) - pBase = szMirandaPath; - - size_t cbBaseLen = strlen(pBase); - if (!strnicmp(pSrc, pBase, cbBaseLen)) - strncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); - else - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - } - - return (int)strlen(pOut); -} - -MIR_CORE_DLL(int) PathToAbsolute(const char *pSrc, char *pOut, const char *base) -{ - if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - char buf[MAX_PATH]; - if (pSrc[0] < ' ') - strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - - if (PathIsAbsolute(pSrc)) - return GetFullPathNameA(pSrc, MAX_PATH, pOut, nullptr); - - if (base == nullptr) - base = szMirandaPath; - - if (pSrc[0] == '\\') - pSrc++; - mir_snprintf(buf, "%s%s", base, pSrc); - return GetFullPathNameA(buf, _countof(buf), pOut, nullptr); -} - -MIR_CORE_DLL(int) CreatePathToFile(const char *szFilePath) -{ - if (szFilePath == nullptr) - return ERROR_INVALID_PARAMETER; - - char *buf = NEWSTR_ALLOCA(szFilePath); - char *p = strrchr(buf, '\\'); - if (p == nullptr) - return 0; - - *p = '\0'; - return CreateDirectoryTree(buf); -} - -MIR_CORE_DLL(int) CreateDirectoryTree(const char *szDir) -{ - if (szDir == nullptr) - return 1; - - uint32_t dwAttributes = GetFileAttributesA(szDir); - if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - - char szTestDir[MAX_PATH]; - mir_strncpy(szTestDir, szDir, _countof(szTestDir)); - char *pszLastBackslash = strrchr(szTestDir, '\\'); - if (pszLastBackslash == nullptr) - return 0; - - *pszLastBackslash = '\0'; - CreateDirectoryTree(szTestDir); - *pszLastBackslash = '\\'; - return (CreateDirectoryA(szTestDir, nullptr) == 0) ? GetLastError() : 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) PathIsAbsoluteW(const wchar_t *path) -{ - if (path && wcslen(path) > 2) - if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) - return 1; - return 0; -} - -MIR_CORE_DLL(int) PathToRelativeW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *pBase) -{ - if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) - return 0; - - if (!PathIsAbsoluteW(pSrc)) - wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - else { - if (pBase == nullptr) - pBase = szMirandaPathW; - - size_t cbBaseLen = wcslen(pBase); - if (!wcsnicmp(pSrc, pBase, cbBaseLen)) - wcsncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); - else - wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); - } - return (int)wcslen(pOut); -} - -MIR_CORE_DLL(int) PathToAbsoluteW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *base) -{ - if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) { - *pOut = 0; - return 0; - } - - wchar_t buf[MAX_PATH]; - if (pSrc[0] < ' ') - return mir_snwprintf(pOut, MAX_PATH, L"%s", pSrc); - - if (PathIsAbsoluteW(pSrc)) - return GetFullPathName(pSrc, MAX_PATH, pOut, nullptr); - - if (base == nullptr) - base = szMirandaPathW; - - if (pSrc[0] == '\\') - pSrc++; - - mir_snwprintf(buf, MAX_PATH, L"%s%s", base, pSrc); - return GetFullPathName(buf, MAX_PATH, pOut, nullptr); -} - -MIR_CORE_DLL(int) CreatePathToFileW(const wchar_t *wszFilePath) -{ - if (wszFilePath == nullptr) - return ERROR_INVALID_PARAMETER; - - wchar_t *buf = NEWWSTR_ALLOCA(wszFilePath); - wchar_t *p = wcsrchr(buf, '\\'); - if (p == nullptr) - return 0; - - *p = '\0'; - return CreateDirectoryTreeW(buf); -} - -MIR_CORE_DLL(int) CreateDirectoryTreeW(const wchar_t *szDir) -{ - if (szDir == nullptr) - return 1; - - uint32_t dwAttributes = GetFileAttributesW(szDir); - if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) - return 0; - - wchar_t szTestDir[MAX_PATH]; - mir_wstrncpy(szTestDir, szDir, _countof(szTestDir)); - wchar_t *pszLastBackslash = wcsrchr(szTestDir, '\\'); - if (pszLastBackslash == nullptr) - return 0; - - *pszLastBackslash = '\0'; - CreateDirectoryTreeW(szTestDir); - *pszLastBackslash = '\\'; - return (CreateDirectoryW(szTestDir, nullptr) == 0) ? GetLastError() : 0; -} - -MIR_CORE_DLL(int) DeleteDirectoryTreeW(const wchar_t *pwszDir, bool bAllowUndo) -{ - if (pwszDir == nullptr) - return ERROR_BAD_ARGUMENTS; - - CMStringW wszPath(pwszDir); - wszPath.AppendChar(0); - - SHFILEOPSTRUCTW file_op = { - nullptr, - FO_DELETE, - wszPath, - L"", - FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION, - false, - nullptr, - L"" }; - - if (bAllowUndo) - file_op.fFlags |= FOF_ALLOWUNDO; - - return SHFileOperationW(&file_op); -} - -int InitPathUtils(void) -{ - GetModuleFileNameA(nullptr, szMirandaPath, _countof(szMirandaPath)); - char *p = strrchr(szMirandaPath, '\\'); - if (p) - p[1] = 0; - - GetModuleFileNameW(nullptr, szMirandaPathW, _countof(szMirandaPathW)); - wchar_t *tp = wcsrchr(szMirandaPathW, '\\'); - if (tp) - tp[1] = 0; - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +static char szMirandaPath[MAX_PATH]; +static wchar_t szMirandaPathW[MAX_PATH]; + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) PathIsAbsolute(const char *path) +{ + if (path && strlen(path) > 2) + if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) + return 1; + return 0; +} + +MIR_CORE_DLL(int) PathToRelative(const char *pSrc, char *pOut, const char *pBase) +{ + if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { + *pOut = 0; + return 0; + } + + if (!PathIsAbsolute(pSrc)) + strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); + else { + if (pBase == nullptr) + pBase = szMirandaPath; + + size_t cbBaseLen = strlen(pBase); + if (!strnicmp(pSrc, pBase, cbBaseLen)) + strncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); + else + strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); + } + + return (int)strlen(pOut); +} + +MIR_CORE_DLL(int) PathToAbsolute(const char *pSrc, char *pOut, const char *base) +{ + if (!pSrc || !pSrc[0] || strlen(pSrc) > MAX_PATH) { + *pOut = 0; + return 0; + } + + char buf[MAX_PATH]; + if (pSrc[0] < ' ') + strncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); + + if (PathIsAbsolute(pSrc)) + return GetFullPathNameA(pSrc, MAX_PATH, pOut, nullptr); + + if (base == nullptr) + base = szMirandaPath; + + if (pSrc[0] == '\\') + pSrc++; + mir_snprintf(buf, "%s%s", base, pSrc); + return GetFullPathNameA(buf, _countof(buf), pOut, nullptr); +} + +MIR_CORE_DLL(int) CreatePathToFile(const char *szFilePath) +{ + if (szFilePath == nullptr) + return ERROR_INVALID_PARAMETER; + + char *buf = NEWSTR_ALLOCA(szFilePath); + char *p = strrchr(buf, '\\'); + if (p == nullptr) + return 0; + + *p = '\0'; + return CreateDirectoryTree(buf); +} + +MIR_CORE_DLL(int) CreateDirectoryTree(const char *szDir) +{ + if (szDir == nullptr) + return 1; + + uint32_t dwAttributes = GetFileAttributesA(szDir); + if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return 0; + + char szTestDir[MAX_PATH]; + mir_strncpy(szTestDir, szDir, _countof(szTestDir)); + char *pszLastBackslash = strrchr(szTestDir, '\\'); + if (pszLastBackslash == nullptr) + return 0; + + *pszLastBackslash = '\0'; + CreateDirectoryTree(szTestDir); + *pszLastBackslash = '\\'; + return (CreateDirectoryA(szTestDir, nullptr) == 0) ? GetLastError() : 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) PathIsAbsoluteW(const wchar_t *path) +{ + if (path && wcslen(path) > 2) + if ((path[1] == ':' && path[2] == '\\') || (path[0] == '\\' && path[1] == '\\')) + return 1; + return 0; +} + +MIR_CORE_DLL(int) PathToRelativeW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *pBase) +{ + if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) + return 0; + + if (!PathIsAbsoluteW(pSrc)) + wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); + else { + if (pBase == nullptr) + pBase = szMirandaPathW; + + size_t cbBaseLen = wcslen(pBase); + if (!wcsnicmp(pSrc, pBase, cbBaseLen)) + wcsncpy_s(pOut, MAX_PATH, pSrc + cbBaseLen, _TRUNCATE); + else + wcsncpy_s(pOut, MAX_PATH, pSrc, _TRUNCATE); + } + return (int)wcslen(pOut); +} + +MIR_CORE_DLL(int) PathToAbsoluteW(const wchar_t *pSrc, wchar_t *pOut, const wchar_t *base) +{ + if (!pSrc || !pSrc[0] || wcslen(pSrc) > MAX_PATH) { + *pOut = 0; + return 0; + } + + wchar_t buf[MAX_PATH]; + if (pSrc[0] < ' ') + return mir_snwprintf(pOut, MAX_PATH, L"%s", pSrc); + + if (PathIsAbsoluteW(pSrc)) + return GetFullPathName(pSrc, MAX_PATH, pOut, nullptr); + + if (base == nullptr) + base = szMirandaPathW; + + if (pSrc[0] == '\\') + pSrc++; + + mir_snwprintf(buf, MAX_PATH, L"%s%s", base, pSrc); + return GetFullPathName(buf, MAX_PATH, pOut, nullptr); +} + +MIR_CORE_DLL(int) CreatePathToFileW(const wchar_t *wszFilePath) +{ + if (wszFilePath == nullptr) + return ERROR_INVALID_PARAMETER; + + wchar_t *buf = NEWWSTR_ALLOCA(wszFilePath); + wchar_t *p = wcsrchr(buf, '\\'); + if (p == nullptr) + return 0; + + *p = '\0'; + return CreateDirectoryTreeW(buf); +} + +MIR_CORE_DLL(int) CreateDirectoryTreeW(const wchar_t *szDir) +{ + if (szDir == nullptr) + return 1; + + uint32_t dwAttributes = GetFileAttributesW(szDir); + if (dwAttributes != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return 0; + + wchar_t szTestDir[MAX_PATH]; + mir_wstrncpy(szTestDir, szDir, _countof(szTestDir)); + wchar_t *pszLastBackslash = wcsrchr(szTestDir, '\\'); + if (pszLastBackslash == nullptr) + return 0; + + *pszLastBackslash = '\0'; + CreateDirectoryTreeW(szTestDir); + *pszLastBackslash = '\\'; + return (CreateDirectoryW(szTestDir, nullptr) == 0) ? GetLastError() : 0; +} + +MIR_CORE_DLL(int) DeleteDirectoryTreeW(const wchar_t *pwszDir, bool bAllowUndo) +{ + if (pwszDir == nullptr) + return ERROR_BAD_ARGUMENTS; + + CMStringW wszPath(pwszDir); + wszPath.AppendChar(0); + + SHFILEOPSTRUCTW file_op = { + nullptr, + FO_DELETE, + wszPath, + L"", + FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION, + false, + nullptr, + L"" }; + + if (bAllowUndo) + file_op.fFlags |= FOF_ALLOWUNDO; + + return SHFileOperationW(&file_op); +} + +int InitPathUtils(void) +{ + GetModuleFileNameA(nullptr, szMirandaPath, _countof(szMirandaPath)); + char *p = strrchr(szMirandaPath, '\\'); + if (p) + p[1] = 0; + + GetModuleFileNameW(nullptr, szMirandaPathW, _countof(szMirandaPathW)); + wchar_t *tp = wcsrchr(szMirandaPathW, '\\'); + if (tp) + tp[1] = 0; + return 0; +} diff --git a/src/mir_core/src/Windows/resizer.cpp b/src/mir_core/src/Windows/resizer.cpp index 3b5de46fcc..519659a27f 100644 --- a/src/mir_core/src/Windows/resizer.cpp +++ b/src/mir_core/src/Windows/resizer.cpp @@ -1,151 +1,151 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -#pragma pack(2) - -struct START_OF_DLGITEMTEMPLATEEX -{ - uint32_t helpID; - uint32_t exStyle; - uint32_t style; - short x, y, cx, cy; - uint32_t id; -}; - -struct START_OF_DLGTEMPLATEEX -{ - uint16_t dlgVer; - uint16_t signature; - uint32_t helpID; - uint32_t exStyle; - uint32_t style; - uint16_t cDlgItems; - short x, y, cx, cy; -}; - -MIR_CORE_DLL(int) Utils_ResizeDialog(HWND hwndDlg, HINSTANCE hInstance, LPCSTR lpTemplate, DIALOGRESIZERPROC pfnResizer, LPARAM lParam) -{ - DLGTEMPLATE *pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(hInstance, FindResourceA(hInstance, lpTemplate, MAKEINTRESOURCEA(5)))); - START_OF_DLGTEMPLATEEX *pTemplateEx = (START_OF_DLGTEMPLATEEX*)pTemplate; - int extendedDlg = pTemplateEx->signature == 0xFFFF; - if (extendedDlg && pTemplateEx->dlgVer != 1) - return 1; - - PWORD pWord = (extendedDlg) ? (PWORD)(pTemplateEx + 1) : (PWORD)(pTemplate + 1); - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class - while (*pWord++); // skip title - if (extendedDlg) { - if (pTemplateEx->style & DS_SETFONT) { - pWord += 3; // font size, weight, italic - while (*pWord++); // font name - } - } - else { - if (pTemplate->style & DS_SETFONT) { - pWord++; // font size - while (*pWord++); // font name - } - } - - UTILRESIZECONTROL urc; - urc.cbSize = sizeof(UTILRESIZECONTROL); - - RECT rc; - rc.left = 0; rc.top = 0; - if (extendedDlg) { - rc.right = pTemplateEx->cx; - rc.bottom = pTemplateEx->cy; - } - else { - rc.right = pTemplate->cx; - rc.bottom = pTemplate->cy; - } - - MapDialogRect(hwndDlg, &rc); - urc.dlgOriginalSize.cx = rc.right; urc.dlgOriginalSize.cy = rc.bottom; - GetClientRect(hwndDlg, &rc); - urc.dlgNewSize.cx = rc.right; urc.dlgNewSize.cy = rc.bottom; - - int itemCount = (extendedDlg) ? pTemplateEx->cDlgItems : pTemplate->cdit; - - HDWP hDwp = BeginDeferWindowPos(itemCount); - for (int i = 0; i < itemCount; i++) { - if ((UINT_PTR)pWord & 2) pWord++; //dword align - - if (extendedDlg) { - START_OF_DLGITEMTEMPLATEEX *pItemEx = (START_OF_DLGITEMTEMPLATEEX*)pWord; - pWord = (PWORD)(pItemEx + 1); - - urc.wId = pItemEx->id; - urc.rcItem.left = pItemEx->x; urc.rcItem.top = pItemEx->y; - urc.rcItem.right = urc.rcItem.left + pItemEx->cx; urc.rcItem.bottom = urc.rcItem.top + pItemEx->cy; - } - else { - DLGITEMTEMPLATE *pItem = (DLGITEMTEMPLATE*)pWord; - pWord = (PWORD)(pItem + 1); - - urc.wId = pItem->id; - urc.rcItem.left = pItem->x; urc.rcItem.top = pItem->y; - urc.rcItem.right = urc.rcItem.left + pItem->cx; urc.rcItem.bottom = urc.rcItem.top + pItem->cy; - } - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu - if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class - pWord += 1 + (1 + *pWord) / 2; //creation data - - if (urc.wId == 65535) // using this breaks the dwp, so just ignore it - continue; - - MapDialogRect(hwndDlg, &urc.rcItem); - int procResult = (pfnResizer)(hwndDlg, lParam, &urc); - if (procResult & RD_ANCHORX_RIGHT) { - urc.rcItem.left += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - } - else if (procResult & RD_ANCHORX_WIDTH) - urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; - else if (procResult & RD_ANCHORX_CENTRE) { - urc.rcItem.left += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; - urc.rcItem.right += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; - } - if (procResult & RD_ANCHORY_BOTTOM) { - urc.rcItem.top += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - } - else if (procResult & RD_ANCHORY_HEIGHT) - urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; - else if (procResult & RD_ANCHORY_CENTRE) { - urc.rcItem.top += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; - urc.rcItem.bottom += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; - } - - HWND hCtrl = GetDlgItem(hwndDlg, urc.wId); - if (hCtrl != nullptr && urc.wId != UINT(-1)) - hDwp = DeferWindowPos(hDwp, hCtrl, nullptr, urc.rcItem.left, urc.rcItem.top, urc.rcItem.right - urc.rcItem.left, urc.rcItem.bottom - urc.rcItem.top, SWP_NOZORDER); - } - EndDeferWindowPos(hDwp); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +#pragma pack(2) + +struct START_OF_DLGITEMTEMPLATEEX +{ + uint32_t helpID; + uint32_t exStyle; + uint32_t style; + short x, y, cx, cy; + uint32_t id; +}; + +struct START_OF_DLGTEMPLATEEX +{ + uint16_t dlgVer; + uint16_t signature; + uint32_t helpID; + uint32_t exStyle; + uint32_t style; + uint16_t cDlgItems; + short x, y, cx, cy; +}; + +MIR_CORE_DLL(int) Utils_ResizeDialog(HWND hwndDlg, HINSTANCE hInstance, LPCSTR lpTemplate, DIALOGRESIZERPROC pfnResizer, LPARAM lParam) +{ + DLGTEMPLATE *pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(hInstance, FindResourceA(hInstance, lpTemplate, MAKEINTRESOURCEA(5)))); + START_OF_DLGTEMPLATEEX *pTemplateEx = (START_OF_DLGTEMPLATEEX*)pTemplate; + int extendedDlg = pTemplateEx->signature == 0xFFFF; + if (extendedDlg && pTemplateEx->dlgVer != 1) + return 1; + + PWORD pWord = (extendedDlg) ? (PWORD)(pTemplateEx + 1) : (PWORD)(pTemplate + 1); + if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu + if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class + while (*pWord++); // skip title + if (extendedDlg) { + if (pTemplateEx->style & DS_SETFONT) { + pWord += 3; // font size, weight, italic + while (*pWord++); // font name + } + } + else { + if (pTemplate->style & DS_SETFONT) { + pWord++; // font size + while (*pWord++); // font name + } + } + + UTILRESIZECONTROL urc; + urc.cbSize = sizeof(UTILRESIZECONTROL); + + RECT rc; + rc.left = 0; rc.top = 0; + if (extendedDlg) { + rc.right = pTemplateEx->cx; + rc.bottom = pTemplateEx->cy; + } + else { + rc.right = pTemplate->cx; + rc.bottom = pTemplate->cy; + } + + MapDialogRect(hwndDlg, &rc); + urc.dlgOriginalSize.cx = rc.right; urc.dlgOriginalSize.cy = rc.bottom; + GetClientRect(hwndDlg, &rc); + urc.dlgNewSize.cx = rc.right; urc.dlgNewSize.cy = rc.bottom; + + int itemCount = (extendedDlg) ? pTemplateEx->cDlgItems : pTemplate->cdit; + + HDWP hDwp = BeginDeferWindowPos(itemCount); + for (int i = 0; i < itemCount; i++) { + if ((UINT_PTR)pWord & 2) pWord++; //dword align + + if (extendedDlg) { + START_OF_DLGITEMTEMPLATEEX *pItemEx = (START_OF_DLGITEMTEMPLATEEX*)pWord; + pWord = (PWORD)(pItemEx + 1); + + urc.wId = pItemEx->id; + urc.rcItem.left = pItemEx->x; urc.rcItem.top = pItemEx->y; + urc.rcItem.right = urc.rcItem.left + pItemEx->cx; urc.rcItem.bottom = urc.rcItem.top + pItemEx->cy; + } + else { + DLGITEMTEMPLATE *pItem = (DLGITEMTEMPLATE*)pWord; + pWord = (PWORD)(pItem + 1); + + urc.wId = pItem->id; + urc.rcItem.left = pItem->x; urc.rcItem.top = pItem->y; + urc.rcItem.right = urc.rcItem.left + pItem->cx; urc.rcItem.bottom = urc.rcItem.top + pItem->cy; + } + if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // menu + if (*pWord == 0xFFFF) pWord += 2; else while (*pWord++); // class + pWord += 1 + (1 + *pWord) / 2; //creation data + + if (urc.wId == 65535) // using this breaks the dwp, so just ignore it + continue; + + MapDialogRect(hwndDlg, &urc.rcItem); + int procResult = (pfnResizer)(hwndDlg, lParam, &urc); + if (procResult & RD_ANCHORX_RIGHT) { + urc.rcItem.left += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; + urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; + } + else if (procResult & RD_ANCHORX_WIDTH) + urc.rcItem.right += urc.dlgNewSize.cx - urc.dlgOriginalSize.cx; + else if (procResult & RD_ANCHORX_CENTRE) { + urc.rcItem.left += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; + urc.rcItem.right += (urc.dlgNewSize.cx - urc.dlgOriginalSize.cx) / 2; + } + if (procResult & RD_ANCHORY_BOTTOM) { + urc.rcItem.top += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; + urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; + } + else if (procResult & RD_ANCHORY_HEIGHT) + urc.rcItem.bottom += urc.dlgNewSize.cy - urc.dlgOriginalSize.cy; + else if (procResult & RD_ANCHORY_CENTRE) { + urc.rcItem.top += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; + urc.rcItem.bottom += (urc.dlgNewSize.cy - urc.dlgOriginalSize.cy) / 2; + } + + HWND hCtrl = GetDlgItem(hwndDlg, urc.wId); + if (hCtrl != nullptr && urc.wId != UINT(-1)) + hDwp = DeferWindowPos(hDwp, hCtrl, nullptr, urc.rcItem.left, urc.rcItem.top, urc.rcItem.right - urc.rcItem.left, urc.rcItem.bottom - urc.rcItem.top, SWP_NOZORDER); + } + EndDeferWindowPos(hDwp); + return 0; +} diff --git a/src/mir_core/src/Windows/subclass.cpp b/src/mir_core/src/Windows/subclass.cpp index ecc56206af..8ca02d78e3 100644 --- a/src/mir_core/src/Windows/subclass.cpp +++ b/src/mir_core/src/Windows/subclass.cpp @@ -1,201 +1,201 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../stdafx.h" - -struct MSubclassData -{ - HWND m_hWnd; - - int m_iHooks; - WNDPROC *m_hooks; - WNDPROC m_origWndProc; - - ~MSubclassData() - { - free(m_hooks); - } -}; - -static LIST arSubclass(10, HandleKeySortT); - -///////////////////////////////////////////////////////////////////////////////////////// - -static LRESULT CALLBACK MSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hwnd); - if (p != nullptr) { - if (p->m_iHooks) - return p->m_hooks[p->m_iHooks-1](hwnd, uMsg, wParam, lParam); - - return p->m_origWndProc(hwnd, uMsg, wParam, lParam); - } - - return DefWindowProc(hwnd, uMsg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// This is for wine: it return wrong WNDPROC for edit control in some cases. -#ifdef WIN64 -#define STD_WND_PROC_ADDR_MASK 0x7FFF00000 -#else -#define STD_WND_PROC_ADDR_MASK 0xFFFF0000 -#endif - -MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc) -{ - if (hWnd == nullptr) - return; - - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) { - p = new MSubclassData; - p->m_hWnd = hWnd; - p->m_origWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); - if (((SIZE_T)p->m_origWndProc & STD_WND_PROC_ADDR_MASK) == STD_WND_PROC_ADDR_MASK) { /* XXX: fix me. Wine fix. */ - p->m_origWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC); - if (((SIZE_T)p->m_origWndProc & 0x7FFF0000) == 0x7FFF0000) /* Delay crash. */ - p->m_origWndProc = DefWindowProc; - } - p->m_iHooks = 0; - p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); - arSubclass.insert(p); - } - else { - for (int i=0; i < p->m_iHooks; i++) - if (p->m_hooks[i] == wndProc) - return; - - void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); - if (tmp == nullptr) - return; - - p->m_hooks = (WNDPROC *)tmp; - } - - p->m_hooks[p->m_iHooks++] = wndProc; -} - -MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) { - p = new MSubclassData; - p->m_hWnd = hWnd; - p->m_origWndProc = oldWndProc; - p->m_iHooks = 0; - p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); - arSubclass.insert(p); - - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); - } - else { - for (int i=0; i < p->m_iHooks; i++) - if (p->m_hooks[i] == wndProc) - return; - - void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); - if (tmp == nullptr) - return; - - p->m_hooks = (WNDPROC *)tmp; - } - - p->m_hooks[p->m_iHooks++] = wndProc; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void removeHook(MSubclassData *p, int idx) -{ - // untie hook from a window to prevent calling mir_callNextSubclass from saveProc - for (int i = idx + 1; i < p->m_iHooks; i++) - p->m_hooks[i-1] = p->m_hooks[i]; - p->m_iHooks--; -} - -static WNDPROC finalizeSubclassing(HWND hWnd, MSubclassData *p) -{ - WNDPROC saveProc = p->m_origWndProc; - arSubclass.remove(p); - delete p; - - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)saveProc); - return saveProc; -} - -MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) - return; - - for (int i = 0; i < p->m_iHooks; i++) { - if (p->m_hooks[i] == wndProc) { - removeHook(p, i); - i--; - } - } - - if (p->m_iHooks == 0) - finalizeSubclassing(hWnd, p); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); - if (p == nullptr) - return DefWindowProc(hWnd, uMsg, wParam, lParam); - - for (int i = p->m_iHooks - 1; i >= 0; i--) { - if (p->m_hooks[i] != wndProc) - continue; - - // next hook exists, call it - if (i != 0) - return p->m_hooks[i-1](hWnd, uMsg, wParam, lParam); - - // last hook called, ping the default window procedure - if (uMsg != WM_NCDESTROY) - return p->m_origWndProc(hWnd, uMsg, wParam, lParam); - - WNDPROC saveProc = finalizeSubclassing(hWnd, p); - return saveProc(hWnd, uMsg, wParam, lParam); - } - - // invalid / closed hook - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst) -{ - for (auto &it : arSubclass.rev_iter()) { - for (int j = 0; j < it->m_iHooks; j++) { - if (GetInstByAddress(it->m_hooks[j]) == hInst) { - removeHook(it, j); - j--; - } - } - - if (it->m_iHooks == 0) - finalizeSubclassing(it->m_hWnd, it); - } -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../stdafx.h" + +struct MSubclassData +{ + HWND m_hWnd; + + int m_iHooks; + WNDPROC *m_hooks; + WNDPROC m_origWndProc; + + ~MSubclassData() + { + free(m_hooks); + } +}; + +static LIST arSubclass(10, HandleKeySortT); + +///////////////////////////////////////////////////////////////////////////////////////// + +static LRESULT CALLBACK MSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + MSubclassData *p = arSubclass.find((MSubclassData*)&hwnd); + if (p != nullptr) { + if (p->m_iHooks) + return p->m_hooks[p->m_iHooks-1](hwnd, uMsg, wParam, lParam); + + return p->m_origWndProc(hwnd, uMsg, wParam, lParam); + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// This is for wine: it return wrong WNDPROC for edit control in some cases. +#ifdef WIN64 +#define STD_WND_PROC_ADDR_MASK 0x7FFF00000 +#else +#define STD_WND_PROC_ADDR_MASK 0xFFFF0000 +#endif + +MIR_CORE_DLL(void) mir_subclassWindow(HWND hWnd, WNDPROC wndProc) +{ + if (hWnd == nullptr) + return; + + MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); + if (p == nullptr) { + p = new MSubclassData; + p->m_hWnd = hWnd; + p->m_origWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); + if (((SIZE_T)p->m_origWndProc & STD_WND_PROC_ADDR_MASK) == STD_WND_PROC_ADDR_MASK) { /* XXX: fix me. Wine fix. */ + p->m_origWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC); + if (((SIZE_T)p->m_origWndProc & 0x7FFF0000) == 0x7FFF0000) /* Delay crash. */ + p->m_origWndProc = DefWindowProc; + } + p->m_iHooks = 0; + p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); + arSubclass.insert(p); + } + else { + for (int i=0; i < p->m_iHooks; i++) + if (p->m_hooks[i] == wndProc) + return; + + void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); + if (tmp == nullptr) + return; + + p->m_hooks = (WNDPROC *)tmp; + } + + p->m_hooks[p->m_iHooks++] = wndProc; +} + +MIR_CORE_DLL(void) mir_subclassWindowFull(HWND hWnd, WNDPROC wndProc, WNDPROC oldWndProc) +{ + MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); + if (p == nullptr) { + p = new MSubclassData; + p->m_hWnd = hWnd; + p->m_origWndProc = oldWndProc; + p->m_iHooks = 0; + p->m_hooks = (WNDPROC*)malloc(sizeof(WNDPROC)); + arSubclass.insert(p); + + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MSubclassWndProc); + } + else { + for (int i=0; i < p->m_iHooks; i++) + if (p->m_hooks[i] == wndProc) + return; + + void *tmp = realloc(p->m_hooks, (p->m_iHooks+1)*sizeof(WNDPROC)); + if (tmp == nullptr) + return; + + p->m_hooks = (WNDPROC *)tmp; + } + + p->m_hooks[p->m_iHooks++] = wndProc; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void removeHook(MSubclassData *p, int idx) +{ + // untie hook from a window to prevent calling mir_callNextSubclass from saveProc + for (int i = idx + 1; i < p->m_iHooks; i++) + p->m_hooks[i-1] = p->m_hooks[i]; + p->m_iHooks--; +} + +static WNDPROC finalizeSubclassing(HWND hWnd, MSubclassData *p) +{ + WNDPROC saveProc = p->m_origWndProc; + arSubclass.remove(p); + delete p; + + SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)saveProc); + return saveProc; +} + +MIR_CORE_DLL(void) mir_unsubclassWindow(HWND hWnd, WNDPROC wndProc) +{ + MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); + if (p == nullptr) + return; + + for (int i = 0; i < p->m_iHooks; i++) { + if (p->m_hooks[i] == wndProc) { + removeHook(p, i); + i--; + } + } + + if (p->m_iHooks == 0) + finalizeSubclassing(hWnd, p); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(LRESULT) mir_callNextSubclass(HWND hWnd, WNDPROC wndProc, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + MSubclassData *p = arSubclass.find((MSubclassData*)&hWnd); + if (p == nullptr) + return DefWindowProc(hWnd, uMsg, wParam, lParam); + + for (int i = p->m_iHooks - 1; i >= 0; i--) { + if (p->m_hooks[i] != wndProc) + continue; + + // next hook exists, call it + if (i != 0) + return p->m_hooks[i-1](hWnd, uMsg, wParam, lParam); + + // last hook called, ping the default window procedure + if (uMsg != WM_NCDESTROY) + return p->m_origWndProc(hWnd, uMsg, wParam, lParam); + + WNDPROC saveProc = finalizeSubclassing(hWnd, p); + return saveProc(hWnd, uMsg, wParam, lParam); + } + + // invalid / closed hook + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(void) KillModuleSubclassing(HMODULE hInst) +{ + for (auto &it : arSubclass.rev_iter()) { + for (int j = 0; j < it->m_iHooks; j++) { + if (GetInstByAddress(it->m_hooks[j]) == hInst) { + removeHook(it, j); + j--; + } + } + + if (it->m_iHooks == 0) + finalizeSubclassing(it->m_hWnd, it); + } +} diff --git a/src/mir_core/src/Windows/threads.cpp b/src/mir_core/src/Windows/threads.cpp index 6af62d3633..c33a80faed 100644 --- a/src/mir_core/src/Windows/threads.cpp +++ b/src/mir_core/src/Windows/threads.cpp @@ -1,400 +1,400 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ - -#include "../stdafx.h" - -#include - -static mir_cs csThreads; - -///////////////////////////////////////////////////////////////////////////////////////// -// APC and mutex functions - -static void __stdcall DummyAPCFunc(ULONG_PTR) -{ - /* called in the context of thread that cleared it's APC queue */ - return; -} - -static int MirandaWaitForMutex(HANDLE hEvent) -{ - // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result - for (;;) { - uint32_t rc = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE); - if (rc == WAIT_OBJECT_0 + 1) { - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - if (msg.hwnd != nullptr && IsDialogMessage(msg.hwnd, &msg)) /* Wine fix. */ - continue; - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - else if (rc == WAIT_OBJECT_0) { // got object - return 1; - } - else if (rc == WAIT_ABANDONED_0 || rc == WAIT_FAILED) - return 0; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// exception handling - -static uint32_t __cdecl sttDefaultFilter(uint32_t, EXCEPTION_POINTERS*) -{ - return EXCEPTION_EXECUTE_HANDLER; -} - -pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter; - -MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter() -{ - return pMirandaExceptFilter; -} - -MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter _mirandaExceptFilter) -{ - pfnExceptionFilter oldOne = pMirandaExceptFilter; - if (_mirandaExceptFilter != nullptr) - pMirandaExceptFilter = _mirandaExceptFilter; - return oldOne; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// thread support functions - -struct THREAD_WAIT_ENTRY -{ - uint32_t dwThreadId; // valid if hThread isn't signalled - HANDLE hThread; - HINSTANCE hOwner; - void *pObject, *pEntryPoint; -}; - -static LIST threads(10, NumericKeySortT); - -struct FORK_ARG -{ - HANDLE hEvent, hThread; - union - { - pThreadFunc threadcode; - pThreadFuncEx threadcodeex; - }; - void *arg, *owner; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// forkthread - starts a new thread - -DWORD WINAPI forkthread_r(void *arg) -{ - FORK_ARG *fa = (FORK_ARG*)arg; - pThreadFunc callercode = fa->threadcode; - void *cookie = fa->arg; - HANDLE hThread = fa->hThread; - Thread_Push((HINSTANCE)callercode); - SetEvent(fa->hEvent); - - callercode(cookie); - - CloseHandle(hThread); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - Thread_Pop(); - return 0; -} - -MIR_CORE_DLL(HANDLE) mir_forkthread(void(__cdecl *threadcode)(void*), void *arg) -{ - FORK_ARG fa; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - fa.threadcode = threadcode; - fa.arg = arg; - - DWORD threadID; - fa.hThread = CreateThread(nullptr, 0, forkthread_r, &fa, 0, &threadID); - if (fa.hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - CloseHandle(fa.hEvent); - return fa.hThread; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// forkthreadex - starts a new thread with the extended info and returns the thread id - -DWORD WINAPI forkthreadex_r(void *arg) -{ - struct FORK_ARG *fa = (struct FORK_ARG *)arg; - pThreadFuncEx threadcode = fa->threadcodeex; - pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex; - void *cookie = fa->arg; - void *owner = fa->owner; - unsigned long rc = 0; - - Thread_Push((HINSTANCE)threadcode, fa->owner); - SetEvent(fa->hEvent); - if (owner) - rc = threadcodeex(owner, cookie); - else - rc = threadcode(cookie); - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - Thread_Pop(); - return rc; -} - -MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void* arg, unsigned *pThreadID) -{ - struct FORK_ARG fa = {}; - fa.threadcodeex = aFunc; - fa.arg = arg; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - - DWORD threadID = 0; - HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); - if (hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - if (pThreadID != nullptr) - *pThreadID = threadID; - - CloseHandle(fa.hEvent); - return hThread; -} - -MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID) -{ - struct FORK_ARG fa = {}; - fa.threadcodeex = (pThreadFuncEx)aFunc; - fa.arg = arg; - fa.owner = owner; - fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - - DWORD threadID = 0; - HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); - if (hThread != nullptr) - WaitForSingleObject(fa.hEvent, INFINITE); - - if (pThreadID != nullptr) - *pThreadID = threadID; - - CloseHandle(fa.hEvent); - return hThread; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void __cdecl KillObjectThreadsWorker(void* owner) -{ - HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE)); - int threadCount = 0; - { - mir_cslock lck(csThreads); - - for (auto &it : threads) - if (it->pObject == owner) - threadPool[threadCount++] = it->hThread; - } - - // is there anything to kill? - if (threadCount == 0) - return; - - // wait'em all - if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT) - return; - - // forcibly kill all remaining threads after 5 secs - mir_cslock lck(csThreads); - auto T = threads.rev_iter(); - for (auto &it : T) { - if (it->pObject == owner) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing object thread %s:%08x", szModuleName, it->dwThreadId); - TerminateThread(it->hThread, 9999); - CloseHandle(it->hThread); - mir_free(it); - threads.removeItem(&it); - } - } -} - -MIR_CORE_DLL(void) KillObjectThreads(void* owner) -{ - if (owner == nullptr) - return; - - uint32_t dwTicks = GetTickCount() + 6000; - HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner); - while (GetTickCount() < dwTicks) { - int res = MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE); - if (res == WAIT_OBJECT_0 || res == WAIT_FAILED) - break; - - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD) -{ - mir_cslock lck(csThreads); - for (auto &p : threads) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName)); - Netlib_Logf(nullptr, "Killing thread %s:%08x (%p)", szModuleName, p->dwThreadId, p->pEntryPoint); - TerminateThread(p->hThread, 9999); - CloseHandle(p->hThread); - mir_free(p); - } - - threads.destroy(); - - SetEvent(hThreadQueueEmpty); -} - -MIR_CORE_DLL(void) Thread_Wait(void) -{ - // acquire the list and wake up any alertable threads - { - mir_cslock lck(csThreads); - for (auto &p : threads) - QueueUserAPC(DummyAPCFunc, p->hThread, 0); - } - - // give all unclosed threads 5 seconds to close - SetTimer(nullptr, 0, 5000, KillAllThreads); - - // wait til the thread list is empty - MirandaWaitForMutex(hThreadQueueEmpty); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG); -#define ThreadQuerySetWin32StartAddress 9 - -static void* GetCurrentThreadEntryPoint() -{ - pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread"); - if (NtQueryInformationThread == nullptr) - return nullptr; - - HANDLE hDupHandle, hCurrentProcess = GetCurrentProcess(); - if (!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) { - SetLastError(ERROR_ACCESS_DENIED); - return nullptr; - } - - DWORD_PTR dwStartAddress; - LONG ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), nullptr); - CloseHandle(hDupHandle); - - return (ntStatus != ERROR_SUCCESS) ? nullptr : (void*)dwStartAddress; -} - -MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner) -{ - ResetEvent(hThreadQueueEmpty); // thread list is not empty - - mir_cslock lck(csThreads); - - THREAD_WAIT_ENTRY *p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY)); - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS); - p->dwThreadId = GetCurrentThreadId(); - p->pObject = pOwner; - p->pEntryPoint = hInst; - - // try to find the precise match - CMPluginBase &pPlugin = GetPluginByInstance(hInst); - if (pPlugin.getInst() == hInst) - p->hOwner = hInst; - else - GetInstByAddress((hInst != nullptr) ? (PVOID)hInst : GetCurrentThreadEntryPoint()); - - threads.insert(p); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(INT_PTR) Thread_Pop() -{ - uint32_t dwThreadId = GetCurrentThreadId(); - - mir_cslock lck(csThreads); - THREAD_WAIT_ENTRY *p = threads.find((THREAD_WAIT_ENTRY*)&dwThreadId); - if (p == nullptr) - return 1; - - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - CloseHandle(p->hThread); - threads.remove(p); - mir_free(p); - - if (!threads.getCount()) { - threads.destroy(); - SetEvent(hThreadQueueEmpty); // thread list is empty now - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -const uint32_t MS_VC_EXCEPTION=0x406D1388; - -#pragma pack(push,8) -typedef struct tagTHREADNAME_INFO -{ - uint32_t dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - uint32_t dwThreadID; // Thread ID (-1=caller thread). - uint32_t dwFlags; // Reserved for future use, must be zero. -} THREADNAME_INFO; -#pragma pack(pop) - -MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = szThreadName; - info.dwThreadID = GetCurrentThreadId(); - info.dwFlags = 0; - - __try { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER) - {} -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "../stdafx.h" + +#include + +static mir_cs csThreads; + +///////////////////////////////////////////////////////////////////////////////////////// +// APC and mutex functions + +static void __stdcall DummyAPCFunc(ULONG_PTR) +{ + /* called in the context of thread that cleared it's APC queue */ + return; +} + +static int MirandaWaitForMutex(HANDLE hEvent) +{ + // will get WAIT_IO_COMPLETE for QueueUserAPC() which isnt a result + for (;;) { + uint32_t rc = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE); + if (rc == WAIT_OBJECT_0 + 1) { + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + if (msg.hwnd != nullptr && IsDialogMessage(msg.hwnd, &msg)) /* Wine fix. */ + continue; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + else if (rc == WAIT_OBJECT_0) { // got object + return 1; + } + else if (rc == WAIT_ABANDONED_0 || rc == WAIT_FAILED) + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// exception handling + +static uint32_t __cdecl sttDefaultFilter(uint32_t, EXCEPTION_POINTERS*) +{ + return EXCEPTION_EXECUTE_HANDLER; +} + +pfnExceptionFilter pMirandaExceptFilter = sttDefaultFilter; + +MIR_CORE_DLL(pfnExceptionFilter) GetExceptionFilter() +{ + return pMirandaExceptFilter; +} + +MIR_CORE_DLL(pfnExceptionFilter) SetExceptionFilter(pfnExceptionFilter _mirandaExceptFilter) +{ + pfnExceptionFilter oldOne = pMirandaExceptFilter; + if (_mirandaExceptFilter != nullptr) + pMirandaExceptFilter = _mirandaExceptFilter; + return oldOne; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// thread support functions + +struct THREAD_WAIT_ENTRY +{ + uint32_t dwThreadId; // valid if hThread isn't signalled + HANDLE hThread; + HINSTANCE hOwner; + void *pObject, *pEntryPoint; +}; + +static LIST threads(10, NumericKeySortT); + +struct FORK_ARG +{ + HANDLE hEvent, hThread; + union + { + pThreadFunc threadcode; + pThreadFuncEx threadcodeex; + }; + void *arg, *owner; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// forkthread - starts a new thread + +DWORD WINAPI forkthread_r(void *arg) +{ + FORK_ARG *fa = (FORK_ARG*)arg; + pThreadFunc callercode = fa->threadcode; + void *cookie = fa->arg; + HANDLE hThread = fa->hThread; + Thread_Push((HINSTANCE)callercode); + SetEvent(fa->hEvent); + + callercode(cookie); + + CloseHandle(hThread); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + Thread_Pop(); + return 0; +} + +MIR_CORE_DLL(HANDLE) mir_forkthread(void(__cdecl *threadcode)(void*), void *arg) +{ + FORK_ARG fa; + fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + fa.threadcode = threadcode; + fa.arg = arg; + + DWORD threadID; + fa.hThread = CreateThread(nullptr, 0, forkthread_r, &fa, 0, &threadID); + if (fa.hThread != nullptr) + WaitForSingleObject(fa.hEvent, INFINITE); + + CloseHandle(fa.hEvent); + return fa.hThread; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// forkthreadex - starts a new thread with the extended info and returns the thread id + +DWORD WINAPI forkthreadex_r(void *arg) +{ + struct FORK_ARG *fa = (struct FORK_ARG *)arg; + pThreadFuncEx threadcode = fa->threadcodeex; + pThreadFuncOwner threadcodeex = (pThreadFuncOwner)fa->threadcodeex; + void *cookie = fa->arg; + void *owner = fa->owner; + unsigned long rc = 0; + + Thread_Push((HINSTANCE)threadcode, fa->owner); + SetEvent(fa->hEvent); + if (owner) + rc = threadcodeex(owner, cookie); + else + rc = threadcode(cookie); + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + Thread_Pop(); + return rc; +} + +MIR_CORE_DLL(HANDLE) mir_forkthreadex(pThreadFuncEx aFunc, void* arg, unsigned *pThreadID) +{ + struct FORK_ARG fa = {}; + fa.threadcodeex = aFunc; + fa.arg = arg; + fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + + DWORD threadID = 0; + HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); + if (hThread != nullptr) + WaitForSingleObject(fa.hEvent, INFINITE); + + if (pThreadID != nullptr) + *pThreadID = threadID; + + CloseHandle(fa.hEvent); + return hThread; +} + +MIR_CORE_DLL(HANDLE) mir_forkthreadowner(pThreadFuncOwner aFunc, void *owner, void *arg, unsigned *pThreadID) +{ + struct FORK_ARG fa = {}; + fa.threadcodeex = (pThreadFuncEx)aFunc; + fa.arg = arg; + fa.owner = owner; + fa.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + + DWORD threadID = 0; + HANDLE hThread = CreateThread(nullptr, 0, forkthreadex_r, &fa, 0, &threadID); + if (hThread != nullptr) + WaitForSingleObject(fa.hEvent, INFINITE); + + if (pThreadID != nullptr) + *pThreadID = threadID; + + CloseHandle(fa.hEvent); + return hThread; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void __cdecl KillObjectThreadsWorker(void* owner) +{ + HANDLE *threadPool = (HANDLE*)alloca(threads.getCount() * sizeof(HANDLE)); + int threadCount = 0; + { + mir_cslock lck(csThreads); + + for (auto &it : threads) + if (it->pObject == owner) + threadPool[threadCount++] = it->hThread; + } + + // is there anything to kill? + if (threadCount == 0) + return; + + // wait'em all + if (WaitForMultipleObjects(threadCount, threadPool, TRUE, 5000) != WAIT_TIMEOUT) + return; + + // forcibly kill all remaining threads after 5 secs + mir_cslock lck(csThreads); + auto T = threads.rev_iter(); + for (auto &it : T) { + if (it->pObject == owner) { + char szModuleName[MAX_PATH]; + GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); + Netlib_Logf(nullptr, "Killing object thread %s:%08x", szModuleName, it->dwThreadId); + TerminateThread(it->hThread, 9999); + CloseHandle(it->hThread); + mir_free(it); + threads.removeItem(&it); + } + } +} + +MIR_CORE_DLL(void) KillObjectThreads(void* owner) +{ + if (owner == nullptr) + return; + + uint32_t dwTicks = GetTickCount() + 6000; + HANDLE hThread = mir_forkthread(KillObjectThreadsWorker, owner); + while (GetTickCount() < dwTicks) { + int res = MsgWaitForMultipleObjectsEx(1, &hThread, 50, QS_ALLPOSTMESSAGE | QS_ALLINPUT, MWMO_ALERTABLE); + if (res == WAIT_OBJECT_0 || res == WAIT_FAILED) + break; + + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void CALLBACK KillAllThreads(HWND, UINT, UINT_PTR, DWORD) +{ + mir_cslock lck(csThreads); + for (auto &p : threads) { + char szModuleName[MAX_PATH]; + GetModuleFileNameA(p->hOwner, szModuleName, sizeof(szModuleName)); + Netlib_Logf(nullptr, "Killing thread %s:%08x (%p)", szModuleName, p->dwThreadId, p->pEntryPoint); + TerminateThread(p->hThread, 9999); + CloseHandle(p->hThread); + mir_free(p); + } + + threads.destroy(); + + SetEvent(hThreadQueueEmpty); +} + +MIR_CORE_DLL(void) Thread_Wait(void) +{ + // acquire the list and wake up any alertable threads + { + mir_cslock lck(csThreads); + for (auto &p : threads) + QueueUserAPC(DummyAPCFunc, p->hThread, 0); + } + + // give all unclosed threads 5 seconds to close + SetTimer(nullptr, 0, 5000, KillAllThreads); + + // wait til the thread list is empty + MirandaWaitForMutex(hThreadQueueEmpty); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +typedef LONG (WINAPI *pNtQIT)(HANDLE, LONG, PVOID, ULONG, PULONG); +#define ThreadQuerySetWin32StartAddress 9 + +static void* GetCurrentThreadEntryPoint() +{ + pNtQIT NtQueryInformationThread = (pNtQIT)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationThread"); + if (NtQueryInformationThread == nullptr) + return nullptr; + + HANDLE hDupHandle, hCurrentProcess = GetCurrentProcess(); + if (!DuplicateHandle(hCurrentProcess, GetCurrentThread(), hCurrentProcess, &hDupHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) { + SetLastError(ERROR_ACCESS_DENIED); + return nullptr; + } + + DWORD_PTR dwStartAddress; + LONG ntStatus = NtQueryInformationThread(hDupHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), nullptr); + CloseHandle(hDupHandle); + + return (ntStatus != ERROR_SUCCESS) ? nullptr : (void*)dwStartAddress; +} + +MIR_CORE_DLL(INT_PTR) Thread_Push(HINSTANCE hInst, void* pOwner) +{ + ResetEvent(hThreadQueueEmpty); // thread list is not empty + + mir_cslock lck(csThreads); + + THREAD_WAIT_ENTRY *p = (THREAD_WAIT_ENTRY*)mir_calloc(sizeof(THREAD_WAIT_ENTRY)); + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &p->hThread, 0, FALSE, DUPLICATE_SAME_ACCESS); + p->dwThreadId = GetCurrentThreadId(); + p->pObject = pOwner; + p->pEntryPoint = hInst; + + // try to find the precise match + CMPluginBase &pPlugin = GetPluginByInstance(hInst); + if (pPlugin.getInst() == hInst) + p->hOwner = hInst; + else + GetInstByAddress((hInst != nullptr) ? (PVOID)hInst : GetCurrentThreadEntryPoint()); + + threads.insert(p); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(INT_PTR) Thread_Pop() +{ + uint32_t dwThreadId = GetCurrentThreadId(); + + mir_cslock lck(csThreads); + THREAD_WAIT_ENTRY *p = threads.find((THREAD_WAIT_ENTRY*)&dwThreadId); + if (p == nullptr) + return 1; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + CloseHandle(p->hThread); + threads.remove(p); + mir_free(p); + + if (!threads.getCount()) { + threads.destroy(); + SetEvent(hThreadQueueEmpty); // thread list is empty now + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +const uint32_t MS_VC_EXCEPTION=0x406D1388; + +#pragma pack(push,8) +typedef struct tagTHREADNAME_INFO +{ + uint32_t dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + uint32_t dwThreadID; // Thread ID (-1=caller thread). + uint32_t dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; +#pragma pack(pop) + +MIR_CORE_DLL(void) Thread_SetName(const char *szThreadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = GetCurrentThreadId(); + info.dwFlags = 0; + + __try { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + {} +} diff --git a/src/mir_core/src/Windows/timezones.cpp b/src/mir_core/src/Windows/timezones.cpp index 63d026a3eb..13e2fddba9 100644 --- a/src/mir_core/src/Windows/timezones.cpp +++ b/src/mir_core/src/Windows/timezones.cpp @@ -1,593 +1,593 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -implements services to handle location - based timezones, instead of -simple UTC offsets. -*/ - -#include "../stdafx.h" - -typedef uint32_t (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi); -static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation; - -struct REG_TZI_FORMAT -{ - LONG Bias; - LONG StandardBias; - LONG DaylightBias; - SYSTEMTIME StandardDate; - SYSTEMTIME DaylightDate; -}; - -#define MIM_TZ_DISPLAYLEN 128 - -struct MIM_TIMEZONE -{ - unsigned hash; - int offset; - - wchar_t tszName[MIM_TZ_NAMELEN]; // windows name for the time zone - wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs) - // every hour should be sufficient. - TIME_ZONE_INFORMATION tzi; - - static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2) - { return p2->tzi.Bias - p1->tzi.Bias; - } -}; - -struct TZ_INT_INFO -{ - uint32_t timestamp; // last time updated - MIM_TIMEZONE myTZ; // set to my own timezone -}; - -static TZ_INT_INFO myInfo; - -static OBJLIST g_timezones(55, NumericKeySortT); -static LIST g_timezonesBias(55, MIM_TIMEZONE::compareBias); - -// KB167296 -void UnixTimeToFileTime(mir_time ts, LPFILETIME pft) -{ - unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64; - pft->dwLowDateTime = (uint32_t)ll; - pft->dwHighDateTime = ll >> 32; -} - -mir_time FileTimeToUnixTime(LPFILETIME pft) -{ - unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime; - ll -= 116444736000000000i64; - return (mir_time)(ll / 10000000); -} - -void FormatTime(const SYSTEMTIME *st, const wchar_t *szFormat, wchar_t *szDest, size_t cbDest) -{ - if (szDest == nullptr || cbDest == 0) return; - - CMStringW tszTemp; - - for (const wchar_t* pFormat = szFormat; *pFormat; ++pFormat) { - uint32_t fmt = 0; - bool date = false, iso = false; - switch (*pFormat) { - case 't': - fmt = TIME_NOSECONDS; - date = false; - break; - - case 's': - fmt = 0; - date = false; - break; - - case 'm': - fmt = TIME_NOMINUTESORSECONDS; - date = false; - break; - - case 'd': - fmt = DATE_SHORTDATE; - date = true; - break; - - case 'D': - fmt = DATE_LONGDATE; - date = true; - break; - - case 'I': - iso = true; - break; - - default: - tszTemp.AppendChar(*pFormat); - continue; - } - - wchar_t dateTimeStr[64]; - if (iso) - tszTemp.AppendFormat(L"%d-%02d-%02dT%02d:%02d:%02dZ", st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond); - else if (date) { - GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); - tszTemp.Append(dateTimeStr); - } - else { - GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); - tszTemp.Append(dateTimeStr); - } - } - - wcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE); -} - -MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st) -{ - if (st == nullptr) return 1; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == UTC_TIME_HANDLE) - GetSystemTime(st); - else if (tz && tz != &myInfo.myTZ) { - SYSTEMTIME sto; - GetSystemTime(&sto); - return !SystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st); - } - else - GetLocalTime(st); - - return 0; -} - -MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr) - return myInfo.myTZ.tszName; - if (tz == UTC_TIME_HANDLE) - return L"UTC"; - - return tz->tszName; -} - -MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname) -{ - for (auto &tz : g_timezonesBias) - if (!mir_wstrcmp(tz->tszName, TZname)) - return tz->szDisplay; - - return L""; -} - -static void CalcTsOffset(MIM_TIMEZONE *tz) -{ - SYSTEMTIME st, stl; - GetSystemTime(&st); - - FILETIME ft; - SystemTimeToFileTime(&st, &ft); - mir_time ts1 = FileTimeToUnixTime(&ft); - - if (!SystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl)) - return; - - SystemTimeToFileTime(&stl, &ft); - mir_time ts2 = FileTimeToUnixTime(&ft); - - tz->offset = ts2 - ts1; -} - -static bool IsSameTime(MIM_TIMEZONE *tz) -{ - SYSTEMTIME st, stl; - - if (tz == &myInfo.myTZ) - return true; - - TimeZone_GetTimeZoneTime(tz, &stl); - TimeZone_GetTimeZoneTime(nullptr, &st); - - return st.wHour == stl.wHour && st.wMinute == stl.wMinute; -} - -MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags) -{ - if (tszName == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (!(dwFlags & TZF_PLF_CB)) - if (mir_wstrcmp(myInfo.myTZ.tszName, tszName) == 0) - return (dwFlags & TZF_DIFONLY) ? nullptr : &myInfo.myTZ; - - MIM_TIMEZONE tzsearch; - tzsearch.hash = mir_hashstrT(tszName); - - MIM_TIMEZONE *tz = g_timezones.find(&tzsearch); - if (tz == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (dwFlags & TZF_DIFONLY) - return IsSameTime(tz) ? nullptr : tz; - - return tz; -} - -MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags) -{ - if (hContact == NULL && szModule == nullptr) - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; - - if (szModule == nullptr) szModule = "UserInfo"; - - DBVARIANT dbv; - if (!db_get_ws(hContact, szModule, "TzName", &dbv)) { - HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); - db_free(&dbv); - if (res) return res; - } - - signed char timezone = (signed char)db_get_b(hContact, szModule, "Timezone", -1); - if (timezone == -1) { - char *szProto = Proto_GetBaseAccountName(hContact); - if (!db_get_ws(hContact, szProto, "TzName", &dbv)) { - HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); - db_free(&dbv); - if (res) return res; - } - timezone = (signed char)db_get_b(hContact, szProto, "Timezone", -1); - } - - if (timezone != -1) { - MIM_TIMEZONE tzsearch; - tzsearch.tzi.Bias = timezone * 30; - if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias) { - if (dwFlags & TZF_DIFONLY) return nullptr; - return &myInfo.myTZ; - } - - int i = g_timezonesBias.getIndex(&tzsearch); - while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i; - - int delta = LONG_MAX; - for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j) { - int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth); - if (delta1 <= delta) { - delta = delta1; - i = j; - } - } - - if (i >= 0) { - MIM_TIMEZONE *tz = g_timezonesBias[i]; - return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? nullptr : tz; - } - } - return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; -} - -MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ) -{ - if (szModule == nullptr) szModule = "UserInfo"; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz) { - db_set_ws(hContact, szModule, "TzName", tz->tszName); - db_set_b(hContact, szModule, "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30)); - } - else { - db_unset(hContact, szModule, "TzName"); - db_unset(hContact, szModule, "Timezone"); - } -} - -MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) - return 1; - - if (tz == nullptr) - tz = &myInfo.myTZ; - - SYSTEMTIME st; - if (TimeZone_GetTimeZoneTime(tz, &st)) - return 1; - - FormatTime(&st, szFormat, szDest, cbDest); - return 0; -} - -MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time ts, SYSTEMTIME *dest, uint32_t dwFlags) -{ - if (dest == nullptr) - return 2; - - MIM_TIMEZONE *tz = (MIM_TIMEZONE *)hTZ; - if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) { - memset(dest, 0, sizeof(SYSTEMTIME)); - return 1; - } - - if (tz == nullptr) - tz = &myInfo.myTZ; - - FILETIME ft; - if (tz == UTC_TIME_HANDLE) - UnixTimeToFileTime(ts, &ft); - else { - if (tz->offset == INT_MIN) - CalcTsOffset(tz); - - UnixTimeToFileTime(ts + tz->offset, &ft); - } - - FileTimeToSystemTime(&ft, dest); - return 0; -} - -MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) -{ - SYSTEMTIME st; - if (!TimeZone_GetSystemTime(hTZ, ts, &st, dwFlags)) - FormatTime(&st, szFormat, szDest, cbDest); - return 0; -} - -MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - return tz ? &tz->tzi : &myInfo.myTZ.tzi; -} - -MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts) -{ - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; - if (tz == nullptr) - tz = &myInfo.myTZ; - - if (tz == UTC_TIME_HANDLE) - return ts; - - if (tz->offset == INT_MIN) - CalcTsOffset(tz); - - return ts + tz->offset; -} - -/////////////////////////////////////////////////////////////////////////////// - -struct ListMessages -{ - UINT addStr, getSel, setSel, getData, setData; -}; - -static const ListMessages lbMessages = { LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA }; -static const ListMessages cbMessages = { CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA }; - -static const ListMessages* GetListMessages(HWND hWnd, uint32_t dwFlags) -{ - if (hWnd == nullptr) - return nullptr; - - if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB))) { - wchar_t tszClassName[128]; - GetClassName(hWnd, tszClassName, _countof(tszClassName)); - if (!mir_wstrcmpi(tszClassName, L"COMBOBOX")) - dwFlags |= TZF_PLF_CB; - else if (!mir_wstrcmpi(tszClassName, L"LISTBOX")) - dwFlags |= TZF_PLF_LB; - } - - if (dwFlags & TZF_PLF_CB) - return &cbMessages; - if (dwFlags & TZF_PLF_LB) - return &lbMessages; - return nullptr; -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg == nullptr) - return -1; - - if (szModule == nullptr) - szModule = "UserInfo"; - - int iSelection = 0; - ptrW tszName(db_get_wsa(hContact, szModule, "TzName")); - if (tszName != NULL) { - unsigned hash = mir_hashstrT(tszName); - for (auto &it : g_timezonesBias) { - if (hash == it->hash) { - iSelection = g_timezonesBias.indexOf(&it) + 1; - break; - } - } - } - else { - signed char cBias = db_get_b(hContact, szModule, "Timezone", -100); - if (cBias != -100) { - int iBias = cBias * 30; - for (auto &it : g_timezonesBias) { - if (iBias == it->tzi.Bias) { - iSelection = g_timezonesBias.indexOf(&it) + 1; - break; - } - } - } - } - - SendMessage(hWnd, lstMsg->setSel, iSelection, 0); - return iSelection; -} - -MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg == nullptr) - return 0; - - SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateW_LP(L"")); - - for (auto &it : g_timezonesBias) { - SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)it->szDisplay); - SendMessage(hWnd, lstMsg->setData, g_timezonesBias.indexOf(&it) + 1, (LPARAM)it); - } - - return TimeZone_SelectListItem(hContact, szModule, hWnd, dwFlags); -} - -MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) -{ - if (szModule == nullptr) szModule = "UserInfo"; - - const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); - if (lstMsg) { - LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0); - if (offset > 0) { - MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0); - if ((INT_PTR)tz != CB_ERR && tz != nullptr) - TimeZone_StoreByContact(hContact, szModule, tz); - } - else TimeZone_StoreByContact(hContact, szModule, nullptr); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t timeVal) -{ - return TimeZone_UtcToLocal(nullptr, (mir_time)timeVal); -} - -MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest) -{ - wchar_t *szTemp = (wchar_t*)alloca(cchDest*sizeof(wchar_t)); - TimeZone_PrintTimeStamp(nullptr, timeVal, _A2T(szFormat), szTemp, cchDest, 0); - WideCharToMultiByte(CP_ACP, 0, szTemp, -1, szDest, (int)cchDest, nullptr, nullptr); - return szDest; -} - -MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest) -{ - TimeZone_PrintTimeStamp(nullptr, timeVal, wszFormat, wszDest, cchDest, 0); - return wszDest; -} - -/////////////////////////////////////////////////////////////////////////////// - -void GetLocalizedString(HKEY hSubKey, const wchar_t *szName, wchar_t *szBuf, uint32_t cbLen) -{ - DWORD dwLength = cbLen * sizeof(wchar_t); - RegQueryValueEx(hSubKey, szName, nullptr, nullptr, (unsigned char *)szBuf, &dwLength); - szBuf[min(dwLength / sizeof(wchar_t), cbLen - 1)] = 0; -} - -void RecalculateTime(void) -{ - GetTimeZoneInformation(&myInfo.myTZ.tzi); - myInfo.timestamp = time(0); - myInfo.myTZ.offset = INT_MIN; - - bool found = false; - DYNAMIC_TIME_ZONE_INFORMATION dtzi; - - if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID) { - wcsncpy_s(myInfo.myTZ.tszName, dtzi.TimeZoneKeyName, _TRUNCATE); - found = true; - } - - for (auto &tz : g_timezones) { - if (tz->offset != INT_MIN) - tz->offset = INT_MIN; - - if (!found) { - if (!mir_wstrcmp(tz->tzi.StandardName, myInfo.myTZ.tzi.StandardName) || !mir_wstrcmp(tz->tzi.DaylightName, myInfo.myTZ.tzi.DaylightName)) { - wcsncpy_s(myInfo.myTZ.tszName, tz->tszName, _TRUNCATE); - found = true; - } - } - } -} - -void InitTimeZones(void) -{ - REG_TZI_FORMAT tzi; - HKEY hKey; - - const wchar_t *tszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"; - - /* - * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with - * the registry key name, so finding our own time zone later will be MUCH easier for - * localized systems or systems with a MUI pack installed - */ - if (IsWinVerVistaPlus()) - pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(L"kernel32"), "GetDynamicTimeZoneInformation"); - - if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) { - uint32_t dwIndex = 0; - HKEY hSubKey; - wchar_t tszName[MIM_TZ_NAMELEN]; - - DWORD dwSize = _countof(tszName); - while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, nullptr, nullptr, nullptr, nullptr)) { - if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey)) { - dwSize = sizeof(tszName); - - DWORD dwLength = sizeof(tzi); - if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, L"TZI", nullptr, nullptr, (unsigned char *)&tzi, &dwLength)) - continue; - - MIM_TIMEZONE *tz = new MIM_TIMEZONE; - - tz->tzi.Bias = tzi.Bias; - tz->tzi.StandardDate = tzi.StandardDate; - tz->tzi.StandardBias = tzi.StandardBias; - tz->tzi.DaylightDate = tzi.DaylightDate; - tz->tzi.DaylightBias = tzi.DaylightBias; - - mir_wstrcpy(tz->tszName, tszName); - tz->hash = mir_hashstrT(tszName); - tz->offset = INT_MIN; - - GetLocalizedString(hSubKey, L"Display", tz->szDisplay, _countof(tz->szDisplay)); - GetLocalizedString(hSubKey, L"Std", tz->tzi.StandardName, _countof(tz->tzi.StandardName)); - GetLocalizedString(hSubKey, L"Dlt", tz->tzi.DaylightName, _countof(tz->tzi.DaylightName)); - - g_timezones.insert(tz); - g_timezonesBias.insert(tz); - - RegCloseKey(hSubKey); - } - dwSize = _countof(tszName); - } - RegCloseKey(hKey); - } - - RecalculateTime(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +implements services to handle location - based timezones, instead of +simple UTC offsets. +*/ + +#include "../stdafx.h" + +typedef uint32_t (WINAPI *pfnGetDynamicTimeZoneInformation_t)(DYNAMIC_TIME_ZONE_INFORMATION *pdtzi); +static pfnGetDynamicTimeZoneInformation_t pfnGetDynamicTimeZoneInformation; + +struct REG_TZI_FORMAT +{ + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; +}; + +#define MIM_TZ_DISPLAYLEN 128 + +struct MIM_TIMEZONE +{ + unsigned hash; + int offset; + + wchar_t tszName[MIM_TZ_NAMELEN]; // windows name for the time zone + wchar_t szDisplay[MIM_TZ_DISPLAYLEN]; // more descriptive display name (that's what usually appears in dialogs) + // every hour should be sufficient. + TIME_ZONE_INFORMATION tzi; + + static int compareBias(const MIM_TIMEZONE* p1, const MIM_TIMEZONE* p2) + { return p2->tzi.Bias - p1->tzi.Bias; + } +}; + +struct TZ_INT_INFO +{ + uint32_t timestamp; // last time updated + MIM_TIMEZONE myTZ; // set to my own timezone +}; + +static TZ_INT_INFO myInfo; + +static OBJLIST g_timezones(55, NumericKeySortT); +static LIST g_timezonesBias(55, MIM_TIMEZONE::compareBias); + +// KB167296 +void UnixTimeToFileTime(mir_time ts, LPFILETIME pft) +{ + unsigned __int64 ll = UInt32x32To64(ts, 10000000) + 116444736000000000i64; + pft->dwLowDateTime = (uint32_t)ll; + pft->dwHighDateTime = ll >> 32; +} + +mir_time FileTimeToUnixTime(LPFILETIME pft) +{ + unsigned __int64 ll = (unsigned __int64)pft->dwHighDateTime << 32 | pft->dwLowDateTime; + ll -= 116444736000000000i64; + return (mir_time)(ll / 10000000); +} + +void FormatTime(const SYSTEMTIME *st, const wchar_t *szFormat, wchar_t *szDest, size_t cbDest) +{ + if (szDest == nullptr || cbDest == 0) return; + + CMStringW tszTemp; + + for (const wchar_t* pFormat = szFormat; *pFormat; ++pFormat) { + uint32_t fmt = 0; + bool date = false, iso = false; + switch (*pFormat) { + case 't': + fmt = TIME_NOSECONDS; + date = false; + break; + + case 's': + fmt = 0; + date = false; + break; + + case 'm': + fmt = TIME_NOMINUTESORSECONDS; + date = false; + break; + + case 'd': + fmt = DATE_SHORTDATE; + date = true; + break; + + case 'D': + fmt = DATE_LONGDATE; + date = true; + break; + + case 'I': + iso = true; + break; + + default: + tszTemp.AppendChar(*pFormat); + continue; + } + + wchar_t dateTimeStr[64]; + if (iso) + tszTemp.AppendFormat(L"%d-%02d-%02dT%02d:%02d:%02dZ", st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond); + else if (date) { + GetDateFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); + tszTemp.Append(dateTimeStr); + } + else { + GetTimeFormat(LOCALE_USER_DEFAULT, fmt, st, nullptr, dateTimeStr, _countof(dateTimeStr)); + tszTemp.Append(dateTimeStr); + } + } + + wcsncpy_s(szDest, cbDest, tszTemp, _TRUNCATE); +} + +MIR_CORE_DLL(int) TimeZone_GetTimeZoneTime(HANDLE hTZ, SYSTEMTIME *st) +{ + if (st == nullptr) return 1; + + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; + if (tz == UTC_TIME_HANDLE) + GetSystemTime(st); + else if (tz && tz != &myInfo.myTZ) { + SYSTEMTIME sto; + GetSystemTime(&sto); + return !SystemTimeToTzSpecificLocalTime(&tz->tzi, &sto, st); + } + else + GetLocalTime(st); + + return 0; +} + +MIR_CORE_DLL(LPCTSTR) TimeZone_GetName(HANDLE hTZ) +{ + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; + if (tz == nullptr) + return myInfo.myTZ.tszName; + if (tz == UTC_TIME_HANDLE) + return L"UTC"; + + return tz->tszName; +} + +MIR_CORE_DLL(LPCTSTR) TimeZone_GetDescription(LPCTSTR TZname) +{ + for (auto &tz : g_timezonesBias) + if (!mir_wstrcmp(tz->tszName, TZname)) + return tz->szDisplay; + + return L""; +} + +static void CalcTsOffset(MIM_TIMEZONE *tz) +{ + SYSTEMTIME st, stl; + GetSystemTime(&st); + + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + mir_time ts1 = FileTimeToUnixTime(&ft); + + if (!SystemTimeToTzSpecificLocalTime(&tz->tzi, &st, &stl)) + return; + + SystemTimeToFileTime(&stl, &ft); + mir_time ts2 = FileTimeToUnixTime(&ft); + + tz->offset = ts2 - ts1; +} + +static bool IsSameTime(MIM_TIMEZONE *tz) +{ + SYSTEMTIME st, stl; + + if (tz == &myInfo.myTZ) + return true; + + TimeZone_GetTimeZoneTime(tz, &stl); + TimeZone_GetTimeZoneTime(nullptr, &st); + + return st.wHour == stl.wHour && st.wMinute == stl.wMinute; +} + +MIR_CORE_DLL(HANDLE) TimeZone_CreateByName(LPCTSTR tszName, uint32_t dwFlags) +{ + if (tszName == nullptr) + return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; + + if (!(dwFlags & TZF_PLF_CB)) + if (mir_wstrcmp(myInfo.myTZ.tszName, tszName) == 0) + return (dwFlags & TZF_DIFONLY) ? nullptr : &myInfo.myTZ; + + MIM_TIMEZONE tzsearch; + tzsearch.hash = mir_hashstrT(tszName); + + MIM_TIMEZONE *tz = g_timezones.find(&tzsearch); + if (tz == nullptr) + return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; + + if (dwFlags & TZF_DIFONLY) + return IsSameTime(tz) ? nullptr : tz; + + return tz; +} + +MIR_CORE_DLL(HANDLE) TimeZone_CreateByContact(MCONTACT hContact, LPCSTR szModule, uint32_t dwFlags) +{ + if (hContact == NULL && szModule == nullptr) + return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; + + if (szModule == nullptr) szModule = "UserInfo"; + + DBVARIANT dbv; + if (!db_get_ws(hContact, szModule, "TzName", &dbv)) { + HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); + db_free(&dbv); + if (res) return res; + } + + signed char timezone = (signed char)db_get_b(hContact, szModule, "Timezone", -1); + if (timezone == -1) { + char *szProto = Proto_GetBaseAccountName(hContact); + if (!db_get_ws(hContact, szProto, "TzName", &dbv)) { + HANDLE res = TimeZone_CreateByName(dbv.pwszVal, dwFlags); + db_free(&dbv); + if (res) return res; + } + timezone = (signed char)db_get_b(hContact, szProto, "Timezone", -1); + } + + if (timezone != -1) { + MIM_TIMEZONE tzsearch; + tzsearch.tzi.Bias = timezone * 30; + if (myInfo.myTZ.tzi.Bias == tzsearch.tzi.Bias) { + if (dwFlags & TZF_DIFONLY) return nullptr; + return &myInfo.myTZ; + } + + int i = g_timezonesBias.getIndex(&tzsearch); + while (i >= 0 && g_timezonesBias[i]->tzi.Bias == tzsearch.tzi.Bias) --i; + + int delta = LONG_MAX; + for (int j = ++i; j < g_timezonesBias.getCount() && g_timezonesBias[j]->tzi.Bias == tzsearch.tzi.Bias; ++j) { + int delta1 = abs(g_timezonesBias[j]->tzi.DaylightDate.wMonth - myInfo.myTZ.tzi.DaylightDate.wMonth); + if (delta1 <= delta) { + delta = delta1; + i = j; + } + } + + if (i >= 0) { + MIM_TIMEZONE *tz = g_timezonesBias[i]; + return ((dwFlags & TZF_DIFONLY) && IsSameTime(tz)) ? nullptr : tz; + } + } + return (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY)) ? nullptr : &myInfo.myTZ; +} + +MIR_CORE_DLL(void) TimeZone_StoreByContact(MCONTACT hContact, LPCSTR szModule, HANDLE hTZ) +{ + if (szModule == nullptr) szModule = "UserInfo"; + + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; + if (tz) { + db_set_ws(hContact, szModule, "TzName", tz->tszName); + db_set_b(hContact, szModule, "Timezone", (char)((tz->tzi.Bias + tz->tzi.StandardBias) / 30)); + } + else { + db_unset(hContact, szModule, "TzName"); + db_unset(hContact, szModule, "Timezone"); + } +} + +MIR_CORE_DLL(int) TimeZone_PrintDateTime(HANDLE hTZ, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) +{ + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; + if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) + return 1; + + if (tz == nullptr) + tz = &myInfo.myTZ; + + SYSTEMTIME st; + if (TimeZone_GetTimeZoneTime(tz, &st)) + return 1; + + FormatTime(&st, szFormat, szDest, cbDest); + return 0; +} + +MIR_CORE_DLL(int) TimeZone_GetSystemTime(HANDLE hTZ, mir_time ts, SYSTEMTIME *dest, uint32_t dwFlags) +{ + if (dest == nullptr) + return 2; + + MIM_TIMEZONE *tz = (MIM_TIMEZONE *)hTZ; + if (tz == nullptr && (dwFlags & (TZF_DIFONLY | TZF_KNOWNONLY))) { + memset(dest, 0, sizeof(SYSTEMTIME)); + return 1; + } + + if (tz == nullptr) + tz = &myInfo.myTZ; + + FILETIME ft; + if (tz == UTC_TIME_HANDLE) + UnixTimeToFileTime(ts, &ft); + else { + if (tz->offset == INT_MIN) + CalcTsOffset(tz); + + UnixTimeToFileTime(ts + tz->offset, &ft); + } + + FileTimeToSystemTime(&ft, dest); + return 0; +} + +MIR_CORE_DLL(int) TimeZone_PrintTimeStamp(HANDLE hTZ, mir_time ts, LPCTSTR szFormat, LPTSTR szDest, size_t cbDest, uint32_t dwFlags) +{ + SYSTEMTIME st; + if (!TimeZone_GetSystemTime(hTZ, ts, &st, dwFlags)) + FormatTime(&st, szFormat, szDest, cbDest); + return 0; +} + +MIR_CORE_DLL(LPTIME_ZONE_INFORMATION) TimeZone_GetInfo(HANDLE hTZ) +{ + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; + return tz ? &tz->tzi : &myInfo.myTZ.tzi; +} + +MIR_CORE_DLL(mir_time) TimeZone_UtcToLocal(HANDLE hTZ, mir_time ts) +{ + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)hTZ; + if (tz == nullptr) + tz = &myInfo.myTZ; + + if (tz == UTC_TIME_HANDLE) + return ts; + + if (tz->offset == INT_MIN) + CalcTsOffset(tz); + + return ts + tz->offset; +} + +/////////////////////////////////////////////////////////////////////////////// + +struct ListMessages +{ + UINT addStr, getSel, setSel, getData, setData; +}; + +static const ListMessages lbMessages = { LB_ADDSTRING, LB_GETCURSEL, LB_SETCURSEL, LB_GETITEMDATA, LB_SETITEMDATA }; +static const ListMessages cbMessages = { CB_ADDSTRING, CB_GETCURSEL, CB_SETCURSEL, CB_GETITEMDATA, CB_SETITEMDATA }; + +static const ListMessages* GetListMessages(HWND hWnd, uint32_t dwFlags) +{ + if (hWnd == nullptr) + return nullptr; + + if (!(dwFlags & (TZF_PLF_CB | TZF_PLF_LB))) { + wchar_t tszClassName[128]; + GetClassName(hWnd, tszClassName, _countof(tszClassName)); + if (!mir_wstrcmpi(tszClassName, L"COMBOBOX")) + dwFlags |= TZF_PLF_CB; + else if (!mir_wstrcmpi(tszClassName, L"LISTBOX")) + dwFlags |= TZF_PLF_LB; + } + + if (dwFlags & TZF_PLF_CB) + return &cbMessages; + if (dwFlags & TZF_PLF_LB) + return &lbMessages; + return nullptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) TimeZone_SelectListItem(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) +{ + const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); + if (lstMsg == nullptr) + return -1; + + if (szModule == nullptr) + szModule = "UserInfo"; + + int iSelection = 0; + ptrW tszName(db_get_wsa(hContact, szModule, "TzName")); + if (tszName != NULL) { + unsigned hash = mir_hashstrT(tszName); + for (auto &it : g_timezonesBias) { + if (hash == it->hash) { + iSelection = g_timezonesBias.indexOf(&it) + 1; + break; + } + } + } + else { + signed char cBias = db_get_b(hContact, szModule, "Timezone", -100); + if (cBias != -100) { + int iBias = cBias * 30; + for (auto &it : g_timezonesBias) { + if (iBias == it->tzi.Bias) { + iSelection = g_timezonesBias.indexOf(&it) + 1; + break; + } + } + } + } + + SendMessage(hWnd, lstMsg->setSel, iSelection, 0); + return iSelection; +} + +MIR_CORE_DLL(int) TimeZone_PrepareList(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) +{ + const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); + if (lstMsg == nullptr) + return 0; + + SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)TranslateW_LP(L"")); + + for (auto &it : g_timezonesBias) { + SendMessage(hWnd, lstMsg->addStr, 0, (LPARAM)it->szDisplay); + SendMessage(hWnd, lstMsg->setData, g_timezonesBias.indexOf(&it) + 1, (LPARAM)it); + } + + return TimeZone_SelectListItem(hContact, szModule, hWnd, dwFlags); +} + +MIR_CORE_DLL(void) TimeZone_StoreListResult(MCONTACT hContact, LPCSTR szModule, HWND hWnd, uint32_t dwFlags) +{ + if (szModule == nullptr) szModule = "UserInfo"; + + const ListMessages *lstMsg = GetListMessages(hWnd, dwFlags); + if (lstMsg) { + LRESULT offset = SendMessage(hWnd, lstMsg->getSel, 0, 0); + if (offset > 0) { + MIM_TIMEZONE *tz = (MIM_TIMEZONE*)SendMessage(hWnd, lstMsg->getData, offset, 0); + if ((INT_PTR)tz != CB_ERR && tz != nullptr) + TimeZone_StoreByContact(hContact, szModule, tz); + } + else TimeZone_StoreByContact(hContact, szModule, nullptr); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(uint32_t) TimeZone_ToLocal(uint32_t timeVal) +{ + return TimeZone_UtcToLocal(nullptr, (mir_time)timeVal); +} + +MIR_CORE_DLL(char*) TimeZone_ToString(mir_time timeVal, const char *szFormat, char *szDest, size_t cchDest) +{ + wchar_t *szTemp = (wchar_t*)alloca(cchDest*sizeof(wchar_t)); + TimeZone_PrintTimeStamp(nullptr, timeVal, _A2T(szFormat), szTemp, cchDest, 0); + WideCharToMultiByte(CP_ACP, 0, szTemp, -1, szDest, (int)cchDest, nullptr, nullptr); + return szDest; +} + +MIR_CORE_DLL(wchar_t*) TimeZone_ToStringW(mir_time timeVal, const wchar_t *wszFormat, wchar_t *wszDest, size_t cchDest) +{ + TimeZone_PrintTimeStamp(nullptr, timeVal, wszFormat, wszDest, cchDest, 0); + return wszDest; +} + +/////////////////////////////////////////////////////////////////////////////// + +void GetLocalizedString(HKEY hSubKey, const wchar_t *szName, wchar_t *szBuf, uint32_t cbLen) +{ + DWORD dwLength = cbLen * sizeof(wchar_t); + RegQueryValueEx(hSubKey, szName, nullptr, nullptr, (unsigned char *)szBuf, &dwLength); + szBuf[min(dwLength / sizeof(wchar_t), cbLen - 1)] = 0; +} + +void RecalculateTime(void) +{ + GetTimeZoneInformation(&myInfo.myTZ.tzi); + myInfo.timestamp = time(0); + myInfo.myTZ.offset = INT_MIN; + + bool found = false; + DYNAMIC_TIME_ZONE_INFORMATION dtzi; + + if (pfnGetDynamicTimeZoneInformation && pfnGetDynamicTimeZoneInformation(&dtzi) != TIME_ZONE_ID_INVALID) { + wcsncpy_s(myInfo.myTZ.tszName, dtzi.TimeZoneKeyName, _TRUNCATE); + found = true; + } + + for (auto &tz : g_timezones) { + if (tz->offset != INT_MIN) + tz->offset = INT_MIN; + + if (!found) { + if (!mir_wstrcmp(tz->tzi.StandardName, myInfo.myTZ.tzi.StandardName) || !mir_wstrcmp(tz->tzi.DaylightName, myInfo.myTZ.tzi.DaylightName)) { + wcsncpy_s(myInfo.myTZ.tszName, tz->tszName, _TRUNCATE); + found = true; + } + } + } +} + +void InitTimeZones(void) +{ + REG_TZI_FORMAT tzi; + HKEY hKey; + + const wchar_t *tszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"; + + /* + * use GetDynamicTimeZoneInformation() on Vista+ - this will return a structure with + * the registry key name, so finding our own time zone later will be MUCH easier for + * localized systems or systems with a MUI pack installed + */ + if (IsWinVerVistaPlus()) + pfnGetDynamicTimeZoneInformation = (pfnGetDynamicTimeZoneInformation_t)GetProcAddress(GetModuleHandle(L"kernel32"), "GetDynamicTimeZoneInformation"); + + if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, tszKey, 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) { + uint32_t dwIndex = 0; + HKEY hSubKey; + wchar_t tszName[MIM_TZ_NAMELEN]; + + DWORD dwSize = _countof(tszName); + while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, dwIndex++, tszName, &dwSize, nullptr, nullptr, nullptr, nullptr)) { + if (ERROR_SUCCESS == RegOpenKeyEx(hKey, tszName, 0, KEY_QUERY_VALUE, &hSubKey)) { + dwSize = sizeof(tszName); + + DWORD dwLength = sizeof(tzi); + if (ERROR_SUCCESS != RegQueryValueEx(hSubKey, L"TZI", nullptr, nullptr, (unsigned char *)&tzi, &dwLength)) + continue; + + MIM_TIMEZONE *tz = new MIM_TIMEZONE; + + tz->tzi.Bias = tzi.Bias; + tz->tzi.StandardDate = tzi.StandardDate; + tz->tzi.StandardBias = tzi.StandardBias; + tz->tzi.DaylightDate = tzi.DaylightDate; + tz->tzi.DaylightBias = tzi.DaylightBias; + + mir_wstrcpy(tz->tszName, tszName); + tz->hash = mir_hashstrT(tszName); + tz->offset = INT_MIN; + + GetLocalizedString(hSubKey, L"Display", tz->szDisplay, _countof(tz->szDisplay)); + GetLocalizedString(hSubKey, L"Std", tz->tzi.StandardName, _countof(tz->tzi.StandardName)); + GetLocalizedString(hSubKey, L"Dlt", tz->tzi.DaylightName, _countof(tz->tzi.DaylightName)); + + g_timezones.insert(tz); + g_timezonesBias.insert(tz); + + RegCloseKey(hSubKey); + } + dwSize = _countof(tszName); + } + RegCloseKey(hKey); + } + + RecalculateTime(); +} diff --git a/src/mir_core/src/Windows/windowlist.cpp b/src/mir_core/src/Windows/windowlist.cpp index 6b6e28ad66..616db3df5c 100644 --- a/src/mir_core/src/Windows/windowlist.cpp +++ b/src/mir_core/src/Windows/windowlist.cpp @@ -1,105 +1,105 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -struct TWindowListItem -{ - TWindowListItem(UINT_PTR _param, HWND _wnd) : - param(_param), - hWnd(_wnd) - {} - - UINT_PTR param; - HWND hWnd; -}; - -struct TWindowList : public OBJLIST -{ - TWindowList() : - OBJLIST(10, NumericKeySortT) - {} -}; - -MIR_CORE_DLL(MWindowList) WindowList_Create(void) -{ - return new TWindowList(); -} - -MIR_CORE_DLL(void) WindowList_Destroy(MWindowList &hList) -{ - delete hList; - hList = nullptr; -} - -MIR_CORE_DLL(int) WindowList_Add(MWindowList hList, HWND hwnd, UINT_PTR param) -{ - if (hList == nullptr) - return 1; - - hList->insert(new TWindowListItem(param, hwnd)); - return 0; -} - -MIR_CORE_DLL(int) WindowList_Remove(MWindowList hList, HWND hwnd) -{ - if (hList == nullptr) return 1; - - for (auto &it : *hList) - if (it->hWnd == hwnd) { - hList->removeItem(&it); - return 0; - } - - return 1; -} - -MIR_CORE_DLL(HWND) WindowList_Find(MWindowList hList, UINT_PTR param) -{ - if (hList == nullptr) - return nullptr; - - TWindowListItem *p = hList->find((TWindowListItem*)¶m); - return (p == nullptr) ? nullptr : p->hWnd; -} - -MIR_CORE_DLL(int) WindowList_Broadcast(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (hList == nullptr) - return 0; - - for (auto &it : hList->rev_iter()) - SendMessage(it->hWnd, message, wParam, lParam); - return 0; -} - -MIR_CORE_DLL(int) WindowList_BroadcastAsync(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (hList == nullptr) - return 0; - - for (auto &it : hList->rev_iter()) - PostMessage(it->hWnd, message, wParam, lParam); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +struct TWindowListItem +{ + TWindowListItem(UINT_PTR _param, HWND _wnd) : + param(_param), + hWnd(_wnd) + {} + + UINT_PTR param; + HWND hWnd; +}; + +struct TWindowList : public OBJLIST +{ + TWindowList() : + OBJLIST(10, NumericKeySortT) + {} +}; + +MIR_CORE_DLL(MWindowList) WindowList_Create(void) +{ + return new TWindowList(); +} + +MIR_CORE_DLL(void) WindowList_Destroy(MWindowList &hList) +{ + delete hList; + hList = nullptr; +} + +MIR_CORE_DLL(int) WindowList_Add(MWindowList hList, HWND hwnd, UINT_PTR param) +{ + if (hList == nullptr) + return 1; + + hList->insert(new TWindowListItem(param, hwnd)); + return 0; +} + +MIR_CORE_DLL(int) WindowList_Remove(MWindowList hList, HWND hwnd) +{ + if (hList == nullptr) return 1; + + for (auto &it : *hList) + if (it->hWnd == hwnd) { + hList->removeItem(&it); + return 0; + } + + return 1; +} + +MIR_CORE_DLL(HWND) WindowList_Find(MWindowList hList, UINT_PTR param) +{ + if (hList == nullptr) + return nullptr; + + TWindowListItem *p = hList->find((TWindowListItem*)¶m); + return (p == nullptr) ? nullptr : p->hWnd; +} + +MIR_CORE_DLL(int) WindowList_Broadcast(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (hList == nullptr) + return 0; + + for (auto &it : hList->rev_iter()) + SendMessage(it->hWnd, message, wParam, lParam); + return 0; +} + +MIR_CORE_DLL(int) WindowList_BroadcastAsync(MWindowList hList, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (hList == nullptr) + return 0; + + for (auto &it : hList->rev_iter()) + PostMessage(it->hWnd, message, wParam, lParam); + return 0; +} diff --git a/src/mir_core/src/Windows/winutil.cpp b/src/mir_core/src/Windows/winutil.cpp index 7a25523482..1d18041529 100644 --- a/src/mir_core/src/Windows/winutil.cpp +++ b/src/mir_core/src/Windows/winutil.cpp @@ -1,177 +1,177 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "../stdafx.h" - -MIR_CORE_DLL(int) Utils_SaveWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix) -{ - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(hwnd, &wp); - - char szSettingName[64]; - mir_snprintf(szSettingName, "%sx", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.left); - - mir_snprintf(szSettingName, "%sy", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.top); - - mir_snprintf(szSettingName, "%swidth", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left); - - mir_snprintf(szSettingName, "%sheight", szNamePrefix); - db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -EXTERN_C MIR_CORE_DLL(int) Utils_RestoreWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix, int flags) -{ - WINDOWPLACEMENT wp; - wp.length = sizeof(wp); - GetWindowPlacement(hwnd, &wp); - - char szSettingName[64]; - mir_snprintf(szSettingName, "%sx", szNamePrefix); - int x = db_get_dw(hContact, szModule, szSettingName, -1); - if (x == -1) - return 1; - - mir_snprintf(szSettingName, "%sy", szNamePrefix); - int y = (int)db_get_dw(hContact, szModule, szSettingName, -1); - - if (flags & RWPF_NOSIZE) - OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top); - else { - wp.rcNormalPosition.left = x; - wp.rcNormalPosition.top = y; - - mir_snprintf(szSettingName, "%swidth", szNamePrefix); - wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(hContact, szModule, szSettingName, -1); - - mir_snprintf(szSettingName, "%sheight", szNamePrefix); - wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(hContact, szModule, szSettingName, -1); - } - wp.flags = 0; - if (flags & RWPF_HIDDEN) - wp.showCmd = SW_HIDE; - if (flags & RWPF_NOACTIVATE) - wp.showCmd = SW_SHOWNOACTIVATE; - - if (!(flags & RWPF_NOMOVE)) - Utils_AssertInsideScreen(&wp.rcNormalPosition); - - SetWindowPlacement(hwnd, &wp); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) Utils_AssertInsideScreen(RECT *rc) -{ - if (rc == nullptr) - return -1; - - RECT rcScreen; - SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE); - if (MonitorFromRect(rc, MONITOR_DEFAULTTONULL)) - return 0; - - MONITORINFO mi = { 0 }; - HMONITOR hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMonitor, &mi)) - rcScreen = mi.rcWork; - - if (rc->top >= rcScreen.bottom) - OffsetRect(rc, 0, rcScreen.bottom - rc->bottom); - else if (rc->bottom <= rcScreen.top) - OffsetRect(rc, 0, rcScreen.top - rc->top); - if (rc->left >= rcScreen.right) - OffsetRect(rc, rcScreen.right - rc->right, 0); - else if (rc->right <= rcScreen.left) - OffsetRect(rc, rcScreen.left - rc->left, 0); - - return 1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static NONCLIENTMETRICSW ncm = {}; - -MIR_CORE_DLL(int) Utils_CorrectFontSize(int size) -{ - if (!g_bEnableDpiAware) - return size; - - if (!ncm.cbSize) { - ncm.cbSize = sizeof(ncm); - SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); - } - - return size * ncm.lfMessageFont.lfHeight / -12; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) Utils_ClipboardCopy(const char *pszText) -{ - size_t cbLen = mir_strlen(pszText); - if (!cbLen) - return; - - if (!OpenClipboard(nullptr)) - return; - - EmptyClipboard(); - - HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbLen); - if (hData) { - mir_strcpy((char *)GlobalLock(hData), pszText); - GlobalUnlock(hData); - SetClipboardData(CF_TEXT, hData); - } - CloseClipboard(); -} - -MIR_CORE_DLL(void) Utils_ClipboardCopy(const wchar_t *pwszText) -{ - size_t cbLen = mir_wstrlen(pwszText); - if (!cbLen) - return; - - if (!OpenClipboard(nullptr)) - return; - - EmptyClipboard(); - - HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (cbLen + 1) * sizeof(wchar_t)); - if (hData) { - mir_wstrcpy((wchar_t *)GlobalLock(hData), pwszText); - GlobalUnlock(hData); - SetClipboardData(CF_UNICODETEXT, hData); - } - CloseClipboard(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "../stdafx.h" + +MIR_CORE_DLL(int) Utils_SaveWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix) +{ + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + GetWindowPlacement(hwnd, &wp); + + char szSettingName[64]; + mir_snprintf(szSettingName, "%sx", szNamePrefix); + db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.left); + + mir_snprintf(szSettingName, "%sy", szNamePrefix); + db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.top); + + mir_snprintf(szSettingName, "%swidth", szNamePrefix); + db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.right-wp.rcNormalPosition.left); + + mir_snprintf(szSettingName, "%sheight", szNamePrefix); + db_set_dw(hContact, szModule, szSettingName, wp.rcNormalPosition.bottom-wp.rcNormalPosition.top); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +EXTERN_C MIR_CORE_DLL(int) Utils_RestoreWindowPosition(HWND hwnd, MCONTACT hContact, const char *szModule, const char *szNamePrefix, int flags) +{ + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + GetWindowPlacement(hwnd, &wp); + + char szSettingName[64]; + mir_snprintf(szSettingName, "%sx", szNamePrefix); + int x = db_get_dw(hContact, szModule, szSettingName, -1); + if (x == -1) + return 1; + + mir_snprintf(szSettingName, "%sy", szNamePrefix); + int y = (int)db_get_dw(hContact, szModule, szSettingName, -1); + + if (flags & RWPF_NOSIZE) + OffsetRect(&wp.rcNormalPosition, x-wp.rcNormalPosition.left, y-wp.rcNormalPosition.top); + else { + wp.rcNormalPosition.left = x; + wp.rcNormalPosition.top = y; + + mir_snprintf(szSettingName, "%swidth", szNamePrefix); + wp.rcNormalPosition.right = wp.rcNormalPosition.left+db_get_dw(hContact, szModule, szSettingName, -1); + + mir_snprintf(szSettingName, "%sheight", szNamePrefix); + wp.rcNormalPosition.bottom = wp.rcNormalPosition.top+db_get_dw(hContact, szModule, szSettingName, -1); + } + wp.flags = 0; + if (flags & RWPF_HIDDEN) + wp.showCmd = SW_HIDE; + if (flags & RWPF_NOACTIVATE) + wp.showCmd = SW_SHOWNOACTIVATE; + + if (!(flags & RWPF_NOMOVE)) + Utils_AssertInsideScreen(&wp.rcNormalPosition); + + SetWindowPlacement(hwnd, &wp); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) Utils_AssertInsideScreen(RECT *rc) +{ + if (rc == nullptr) + return -1; + + RECT rcScreen; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, FALSE); + if (MonitorFromRect(rc, MONITOR_DEFAULTTONULL)) + return 0; + + MONITORINFO mi = { 0 }; + HMONITOR hMonitor = MonitorFromRect(rc, MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMonitor, &mi)) + rcScreen = mi.rcWork; + + if (rc->top >= rcScreen.bottom) + OffsetRect(rc, 0, rcScreen.bottom - rc->bottom); + else if (rc->bottom <= rcScreen.top) + OffsetRect(rc, 0, rcScreen.top - rc->top); + if (rc->left >= rcScreen.right) + OffsetRect(rc, rcScreen.right - rc->right, 0); + else if (rc->right <= rcScreen.left) + OffsetRect(rc, rcScreen.left - rc->left, 0); + + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static NONCLIENTMETRICSW ncm = {}; + +MIR_CORE_DLL(int) Utils_CorrectFontSize(int size) +{ + if (!g_bEnableDpiAware) + return size; + + if (!ncm.cbSize) { + ncm.cbSize = sizeof(ncm); + SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); + } + + return size * ncm.lfMessageFont.lfHeight / -12; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(void) Utils_ClipboardCopy(const char *pszText) +{ + size_t cbLen = mir_strlen(pszText); + if (!cbLen) + return; + + if (!OpenClipboard(nullptr)) + return; + + EmptyClipboard(); + + HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cbLen); + if (hData) { + mir_strcpy((char *)GlobalLock(hData), pszText); + GlobalUnlock(hData); + SetClipboardData(CF_TEXT, hData); + } + CloseClipboard(); +} + +MIR_CORE_DLL(void) Utils_ClipboardCopy(const wchar_t *pwszText) +{ + size_t cbLen = mir_wstrlen(pwszText); + if (!cbLen) + return; + + if (!OpenClipboard(nullptr)) + return; + + EmptyClipboard(); + + HGLOBAL hData = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (cbLen + 1) * sizeof(wchar_t)); + if (hData) { + mir_wstrcpy((wchar_t *)GlobalLock(hData), pwszText); + GlobalUnlock(hData); + SetClipboardData(CF_UNICODETEXT, hData); + } + CloseClipboard(); +} diff --git a/src/mir_core/src/Windows/winver.cpp b/src/mir_core/src/Windows/winver.cpp index 29a6fa7f3c..f52567ccab 100644 --- a/src/mir_core/src/Windows/winver.cpp +++ b/src/mir_core/src/Windows/winver.cpp @@ -1,372 +1,372 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../stdafx.h" - -#ifndef _WIN32_WINNT_WIN8 -#define _WIN32_WINNT_WIN8 0x0602 // Windows 8 -#endif - -#ifndef _WIN32_WINNT_WINBLUE -#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1 -#endif - -#ifndef _WIN32_WINNT_WIN10 -#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10 -#endif - -static int dwWinVer; - -void InitWinver() -{ - uint32_t dwVer = LOWORD(GetVersion()); - dwWinVer = MAKEWORD(HIBYTE(dwVer), LOBYTE(dwVer)); -} - -MIR_CORE_DLL(BOOL) IsWinVerVistaPlus() -{ - return dwWinVer >= _WIN32_WINNT_VISTA; -} - -MIR_CORE_DLL(BOOL) IsWinVer7Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN7; -} - -MIR_CORE_DLL(BOOL) IsWinVer8Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN8; -} - -MIR_CORE_DLL(BOOL) IsWinVer81Plus() -{ - return dwWinVer >= _WIN32_WINNT_WINBLUE; -} - -MIR_CORE_DLL(BOOL) IsWinVer10Plus() -{ - return dwWinVer >= _WIN32_WINNT_WIN10; -} - -MIR_CORE_DLL(BOOL) IsFullScreen() -{ - RECT rcScreen = { 0 }; - - rcScreen.right = GetSystemMetrics(SM_CXSCREEN); - rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); - - HMONITOR hMon = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST); - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMon, &mi)) - rcScreen = mi.rcMonitor; - - HWND hWndDesktop = GetDesktopWindow(); - HWND hWndShell = GetShellWindow(); - - // check foregroundwindow - HWND hWnd = GetForegroundWindow(); - if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell) { - wchar_t tszClassName[128] = L""; - GetClassName(hWnd, tszClassName, _countof(tszClassName)); - if (wcscmp(tszClassName, L"WorkerW")) { - RECT rect, rectw, recti; - GetWindowRect(hWnd, &rectw); - - GetClientRect(hWnd, &rect); - ClientToScreen(hWnd, (LPPOINT)&rect); - ClientToScreen(hWnd, (LPPOINT)&rect.right); - - if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) && EqualRect(&recti, &rcScreen)) - return true; - } - } - - return false; -} - -MIR_CORE_DLL(BOOL) IsWorkstationLocked(void) -{ - HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP); - if (hDesk == nullptr) - return true; - - wchar_t tszName[100]; - DWORD cbName; - BOOL bLocked = (!GetUserObjectInformation(hDesk, UOI_NAME, tszName, _countof(tszName), &cbName) || mir_wstrcmpi(tszName, L"default") != 0); - CloseDesktop(hDesk); - return bLocked; -} - -MIR_CORE_DLL(BOOL) IsTerminalDisconnected(void) -{ - PVOID pBuffer = nullptr; - DWORD pBytesReturned = 0; - BOOL result = FALSE; - - if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&pBuffer, &pBytesReturned)) - if (*(PDWORD)pBuffer == WTSDisconnected) - result = TRUE; - - if (pBuffer) - WTSFreeMemory(pBuffer); - return result; -} - -MIR_CORE_DLL(BOOL) IsScreenSaverRunning(void) -{ - BOOL rc = FALSE; - SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE); - return rc != 0; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize) -{ - if (buf == nullptr || bufSize == 0) - return false; - - mir_snprintf(buf, bufSize, "Windows NT %d.%d", HIBYTE(dwWinVer), LOBYTE(dwWinVer)); - return true; -} - -////////////////////////////////////////////////////////////////////////////////////////////////////// - -#ifndef PRODUCT_CORE_N // Win8 -#define PRODUCT_CORE_SINGLELANGUAGE 0x00000064 -#define PRODUCT_PROFESSIONAL_WMC 0x00000067 -#endif - -typedef BOOL(WINAPI *PGPI)(uint32_t, uint32_t, uint32_t, uint32_t, PDWORD); -typedef LPCSTR(WINAPI *WGV)(void); - -MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize) -{ - if (buf == nullptr || bufSize == 0) - return 0; - - buf[0] = 0; - - OSVERSIONINFOEX osvi = { 0 }; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi); - if (!bOsVersionInfoEx) { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (!GetVersionEx((OSVERSIONINFO*)&osvi)) - return false; - } - - if (VER_PLATFORM_WIN32_NT != osvi.dwPlatformId || osvi.dwMajorVersion <= 4) - return false; - - SYSTEM_INFO sysInfo = { 0 }; - GetNativeSystemInfo(&sysInfo); - - CMStringA ret("Microsoft "); - - // Test for the specific product. - if (osvi.dwMajorVersion >= 6) { - if (osvi.dwMajorVersion == 10) { - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 10 "); - else - ret.Append("Windows Server 10 "); - } - else switch (osvi.dwMinorVersion) { - case 0: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows Vista "); - else - ret.Append("Windows Server 2008 "); - break; - - case 1: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 7 "); - else - ret.Append("Windows Server 2008 R2 "); - break; - - case 2: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 8 "); - else - ret.Append("Windows Server 2012 "); - break; - - case 3: - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Windows 8.1 "); - else - ret.Append("Windows Server 2012 R2 "); - break; - } - - DWORD dwType = 0; - HMODULE hKernel = GetModuleHandle(L"kernel32.dll"); - PGPI pGPI = (PGPI)GetProcAddress(hKernel, "GetProductInfo"); - if (pGPI != nullptr) - pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); - - switch (dwType) { - case PRODUCT_ULTIMATE: - ret.Append("Ultimate Edition"); - break; - case PRODUCT_PROFESSIONAL: - ret.Append("Professional Edition"); - break; - case PRODUCT_PROFESSIONAL_WMC: - ret.Append("Professional Edition with Media Center"); - break; - case PRODUCT_HOME_PREMIUM: - ret.Append("Home Premium Edition"); - break; - case PRODUCT_HOME_BASIC: - ret.Append("Home Basic Edition"); - break; - case PRODUCT_ENTERPRISE: - ret.Append("Enterprise Edition"); - break; - case PRODUCT_BUSINESS: - ret.Append("Business Edition"); - break; - case PRODUCT_STARTER: - ret.Append("Starter Edition"); - break; - case PRODUCT_CLUSTER_SERVER: - ret.Append("Cluster Server Edition"); - break; - case PRODUCT_DATACENTER_SERVER: - ret.Append("Datacenter Edition"); - break; - case PRODUCT_DATACENTER_SERVER_CORE: - ret.Append("Datacenter Edition (core installation)"); - break; - case PRODUCT_ENTERPRISE_SERVER: - ret.Append("Enterprise Edition"); - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - ret.Append("Enterprise Edition (core installation)"); - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - ret.Append("Enterprise Edition for Itanium-based Systems"); - break; - case PRODUCT_SMALLBUSINESS_SERVER: - ret.Append("Small Business Server"); - break; - case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: - ret.Append("Small Business Server Premium Edition"); - break; - case PRODUCT_STANDARD_SERVER: - ret.Append("Standard Edition"); - break; - case PRODUCT_STANDARD_SERVER_CORE: - ret.Append("Standard Edition (core installation)"); - break; - case PRODUCT_WEB_SERVER: - ret.Append("Web Server Edition"); - break; - case PRODUCT_CORE_SINGLELANGUAGE: - ret.Append("Home Single Language"); - break; - } - if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - ret.Append(", 64-bit"); - else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) - ret.Append(", 32-bit"); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { - if (GetSystemMetrics(SM_SERVERR2)) - ret.Append("Windows Server 2003 R2, "); - else if (osvi.wSuiteMask == VER_SUITE_STORAGE_SERVER) - ret.Append("Windows Storage Server 2003"); - else if (osvi.wSuiteMask == VER_SUITE_WH_SERVER) - ret.Append("Windows Home Server"); - else if (osvi.wProductType == VER_NT_WORKSTATION && sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - ret.Append("Windows XP Professional x64 Edition"); - else - ret.Append("Windows Server 2003, "); - - // Test for the server type. - if (osvi.wProductType != VER_NT_WORKSTATION) { - if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Edition for Itanium-based Systems"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise Edition for Itanium-based Systems"); - } - else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter x64 Edition"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise x64 Edition"); - else ret.Append("Standard x64 Edition"); - } - else { - if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER) - ret.Append("Compute Cluster Edition"); - else if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Edition"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Enterprise Edition"); - else if (osvi.wSuiteMask & VER_SUITE_BLADE) - ret.Append("Web Edition"); - else ret.Append("Standard Edition"); - } - } - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { - ret.Append("Windows XP "); - if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - ret.Append("Home Edition"); - else ret.Append("Professional"); - } - - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { - ret.Append("Windows 2000 "); - - if (osvi.wProductType == VER_NT_WORKSTATION) - ret.Append("Professional"); - else { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - ret.Append("Datacenter Server"); - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - ret.Append("Advanced Server"); - else ret.Append("Server"); - } - } - - // Include service pack (if any) and build number. - if (mir_wstrlen(osvi.szCSDVersion) > 0) { - ret.Append(" "); - ret.Append(_T2A(osvi.szCSDVersion)); - } - - ret.AppendFormat(" (build %d)", osvi.dwBuildNumber); - - HMODULE hNtDll = GetModuleHandleA("ntdll.dll"); - if (WGV wine_get_version = (WGV)GetProcAddress(hNtDll, "wine_get_version")) - { - ret.AppendFormat(" (Wine %s)", wine_get_version()); - } - - mir_strncpy(buf, ret, bufSize); - return true; -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../stdafx.h" + +#ifndef _WIN32_WINNT_WIN8 +#define _WIN32_WINNT_WIN8 0x0602 // Windows 8 +#endif + +#ifndef _WIN32_WINNT_WINBLUE +#define _WIN32_WINNT_WINBLUE 0x0603 // Windows 8.1 +#endif + +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 // Windows 10 +#endif + +static int dwWinVer; + +void InitWinver() +{ + uint32_t dwVer = LOWORD(GetVersion()); + dwWinVer = MAKEWORD(HIBYTE(dwVer), LOBYTE(dwVer)); +} + +MIR_CORE_DLL(BOOL) IsWinVerVistaPlus() +{ + return dwWinVer >= _WIN32_WINNT_VISTA; +} + +MIR_CORE_DLL(BOOL) IsWinVer7Plus() +{ + return dwWinVer >= _WIN32_WINNT_WIN7; +} + +MIR_CORE_DLL(BOOL) IsWinVer8Plus() +{ + return dwWinVer >= _WIN32_WINNT_WIN8; +} + +MIR_CORE_DLL(BOOL) IsWinVer81Plus() +{ + return dwWinVer >= _WIN32_WINNT_WINBLUE; +} + +MIR_CORE_DLL(BOOL) IsWinVer10Plus() +{ + return dwWinVer >= _WIN32_WINNT_WIN10; +} + +MIR_CORE_DLL(BOOL) IsFullScreen() +{ + RECT rcScreen = { 0 }; + + rcScreen.right = GetSystemMetrics(SM_CXSCREEN); + rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); + + HMONITOR hMon = MonitorFromWindow(GetForegroundWindow(), MONITOR_DEFAULTTONEAREST); + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMon, &mi)) + rcScreen = mi.rcMonitor; + + HWND hWndDesktop = GetDesktopWindow(); + HWND hWndShell = GetShellWindow(); + + // check foregroundwindow + HWND hWnd = GetForegroundWindow(); + if (hWnd && hWnd != hWndDesktop && hWnd != hWndShell) { + wchar_t tszClassName[128] = L""; + GetClassName(hWnd, tszClassName, _countof(tszClassName)); + if (wcscmp(tszClassName, L"WorkerW")) { + RECT rect, rectw, recti; + GetWindowRect(hWnd, &rectw); + + GetClientRect(hWnd, &rect); + ClientToScreen(hWnd, (LPPOINT)&rect); + ClientToScreen(hWnd, (LPPOINT)&rect.right); + + if (EqualRect(&rect, &rectw) && IntersectRect(&recti, &rect, &rcScreen) && EqualRect(&recti, &rcScreen)) + return true; + } + } + + return false; +} + +MIR_CORE_DLL(BOOL) IsWorkstationLocked(void) +{ + HDESK hDesk = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP); + if (hDesk == nullptr) + return true; + + wchar_t tszName[100]; + DWORD cbName; + BOOL bLocked = (!GetUserObjectInformation(hDesk, UOI_NAME, tszName, _countof(tszName), &cbName) || mir_wstrcmpi(tszName, L"default") != 0); + CloseDesktop(hDesk); + return bLocked; +} + +MIR_CORE_DLL(BOOL) IsTerminalDisconnected(void) +{ + PVOID pBuffer = nullptr; + DWORD pBytesReturned = 0; + BOOL result = FALSE; + + if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, WTSConnectState, (LPTSTR *)&pBuffer, &pBytesReturned)) + if (*(PDWORD)pBuffer == WTSDisconnected) + result = TRUE; + + if (pBuffer) + WTSFreeMemory(pBuffer); + return result; +} + +MIR_CORE_DLL(BOOL) IsScreenSaverRunning(void) +{ + BOOL rc = FALSE; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &rc, FALSE); + return rc != 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(BOOL) OS_GetShortString(char *buf, size_t bufSize) +{ + if (buf == nullptr || bufSize == 0) + return false; + + mir_snprintf(buf, bufSize, "Windows NT %d.%d", HIBYTE(dwWinVer), LOBYTE(dwWinVer)); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef PRODUCT_CORE_N // Win8 +#define PRODUCT_CORE_SINGLELANGUAGE 0x00000064 +#define PRODUCT_PROFESSIONAL_WMC 0x00000067 +#endif + +typedef BOOL(WINAPI *PGPI)(uint32_t, uint32_t, uint32_t, uint32_t, PDWORD); +typedef LPCSTR(WINAPI *WGV)(void); + +MIR_CORE_DLL(BOOL) OS_GetDisplayString(char *buf, size_t bufSize) +{ + if (buf == nullptr || bufSize == 0) + return 0; + + buf[0] = 0; + + OSVERSIONINFOEX osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + BOOL bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *)&osvi); + if (!bOsVersionInfoEx) { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx((OSVERSIONINFO*)&osvi)) + return false; + } + + if (VER_PLATFORM_WIN32_NT != osvi.dwPlatformId || osvi.dwMajorVersion <= 4) + return false; + + SYSTEM_INFO sysInfo = { 0 }; + GetNativeSystemInfo(&sysInfo); + + CMStringA ret("Microsoft "); + + // Test for the specific product. + if (osvi.dwMajorVersion >= 6) { + if (osvi.dwMajorVersion == 10) { + if (osvi.wProductType == VER_NT_WORKSTATION) + ret.Append("Windows 10 "); + else + ret.Append("Windows Server 10 "); + } + else switch (osvi.dwMinorVersion) { + case 0: + if (osvi.wProductType == VER_NT_WORKSTATION) + ret.Append("Windows Vista "); + else + ret.Append("Windows Server 2008 "); + break; + + case 1: + if (osvi.wProductType == VER_NT_WORKSTATION) + ret.Append("Windows 7 "); + else + ret.Append("Windows Server 2008 R2 "); + break; + + case 2: + if (osvi.wProductType == VER_NT_WORKSTATION) + ret.Append("Windows 8 "); + else + ret.Append("Windows Server 2012 "); + break; + + case 3: + if (osvi.wProductType == VER_NT_WORKSTATION) + ret.Append("Windows 8.1 "); + else + ret.Append("Windows Server 2012 R2 "); + break; + } + + DWORD dwType = 0; + HMODULE hKernel = GetModuleHandle(L"kernel32.dll"); + PGPI pGPI = (PGPI)GetProcAddress(hKernel, "GetProductInfo"); + if (pGPI != nullptr) + pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); + + switch (dwType) { + case PRODUCT_ULTIMATE: + ret.Append("Ultimate Edition"); + break; + case PRODUCT_PROFESSIONAL: + ret.Append("Professional Edition"); + break; + case PRODUCT_PROFESSIONAL_WMC: + ret.Append("Professional Edition with Media Center"); + break; + case PRODUCT_HOME_PREMIUM: + ret.Append("Home Premium Edition"); + break; + case PRODUCT_HOME_BASIC: + ret.Append("Home Basic Edition"); + break; + case PRODUCT_ENTERPRISE: + ret.Append("Enterprise Edition"); + break; + case PRODUCT_BUSINESS: + ret.Append("Business Edition"); + break; + case PRODUCT_STARTER: + ret.Append("Starter Edition"); + break; + case PRODUCT_CLUSTER_SERVER: + ret.Append("Cluster Server Edition"); + break; + case PRODUCT_DATACENTER_SERVER: + ret.Append("Datacenter Edition"); + break; + case PRODUCT_DATACENTER_SERVER_CORE: + ret.Append("Datacenter Edition (core installation)"); + break; + case PRODUCT_ENTERPRISE_SERVER: + ret.Append("Enterprise Edition"); + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + ret.Append("Enterprise Edition (core installation)"); + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + ret.Append("Enterprise Edition for Itanium-based Systems"); + break; + case PRODUCT_SMALLBUSINESS_SERVER: + ret.Append("Small Business Server"); + break; + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + ret.Append("Small Business Server Premium Edition"); + break; + case PRODUCT_STANDARD_SERVER: + ret.Append("Standard Edition"); + break; + case PRODUCT_STANDARD_SERVER_CORE: + ret.Append("Standard Edition (core installation)"); + break; + case PRODUCT_WEB_SERVER: + ret.Append("Web Server Edition"); + break; + case PRODUCT_CORE_SINGLELANGUAGE: + ret.Append("Home Single Language"); + break; + } + if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + ret.Append(", 64-bit"); + else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + ret.Append(", 32-bit"); + } + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) { + if (GetSystemMetrics(SM_SERVERR2)) + ret.Append("Windows Server 2003 R2, "); + else if (osvi.wSuiteMask == VER_SUITE_STORAGE_SERVER) + ret.Append("Windows Storage Server 2003"); + else if (osvi.wSuiteMask == VER_SUITE_WH_SERVER) + ret.Append("Windows Home Server"); + else if (osvi.wProductType == VER_NT_WORKSTATION && sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + ret.Append("Windows XP Professional x64 Edition"); + else + ret.Append("Windows Server 2003, "); + + // Test for the server type. + if (osvi.wProductType != VER_NT_WORKSTATION) { + if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + ret.Append("Datacenter Edition for Itanium-based Systems"); + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + ret.Append("Enterprise Edition for Itanium-based Systems"); + } + else if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + ret.Append("Datacenter x64 Edition"); + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + ret.Append("Enterprise x64 Edition"); + else ret.Append("Standard x64 Edition"); + } + else { + if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER) + ret.Append("Compute Cluster Edition"); + else if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + ret.Append("Datacenter Edition"); + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + ret.Append("Enterprise Edition"); + else if (osvi.wSuiteMask & VER_SUITE_BLADE) + ret.Append("Web Edition"); + else ret.Append("Standard Edition"); + } + } + } + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + ret.Append("Windows XP "); + if (osvi.wSuiteMask & VER_SUITE_PERSONAL) + ret.Append("Home Edition"); + else ret.Append("Professional"); + } + + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) { + ret.Append("Windows 2000 "); + + if (osvi.wProductType == VER_NT_WORKSTATION) + ret.Append("Professional"); + else { + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + ret.Append("Datacenter Server"); + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + ret.Append("Advanced Server"); + else ret.Append("Server"); + } + } + + // Include service pack (if any) and build number. + if (mir_wstrlen(osvi.szCSDVersion) > 0) { + ret.Append(" "); + ret.Append(_T2A(osvi.szCSDVersion)); + } + + ret.AppendFormat(" (build %d)", osvi.dwBuildNumber); + + HMODULE hNtDll = GetModuleHandleA("ntdll.dll"); + if (WGV wine_get_version = (WGV)GetProcAddress(hNtDll, "wine_get_version")) + { + ret.AppendFormat(" (Wine %s)", wine_get_version()); + } + + mir_strncpy(buf, ret, bufSize); + return true; +} diff --git a/src/mir_core/src/binbuffer.cpp b/src/mir_core/src/binbuffer.cpp index fbde7bfb4d..77016fbda9 100644 --- a/src/mir_core/src/binbuffer.cpp +++ b/src/mir_core/src/binbuffer.cpp @@ -1,170 +1,170 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "stdafx.h" - -struct BufImpl -{ - uint32_t size, lockCount; - - BufImpl* alloc(size_t newSize) - { - bool bEmpty = (this == nullptr); - auto *res = (BufImpl *)mir_realloc(this, newSize + sizeof(BufImpl)); - if (bEmpty) { - res->lockCount = 1; - res->size = 0; - } - return res; - } - - BufImpl* realloc(size_t newSize) - { - bool bEmpty; - newSize += sizeof(BufImpl); - if (this != nullptr) { - newSize += size; - bEmpty = false; - } - else bEmpty = true; - - auto *res = (BufImpl *)mir_realloc(this, newSize); - if (bEmpty) { - res->lockCount = 1; - res->size = 0; - } - return res; - } - - void free() - { - if (this == nullptr) - return; - - if (lockCount == 1) - mir_free(this); - else - lockCount--; - } -}; - -__forceinline BufImpl* ptr2buf(uint8_t *p) -{ - return (p == nullptr) ? nullptr : (BufImpl*)p-1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MBinBuffer::MBinBuffer() -{} - -MBinBuffer::MBinBuffer(const MBinBuffer &orig) -{ - ptr2buf(m_buf)->free(); - - BufImpl *p = ptr2buf(m_buf = orig.m_buf); - if (p) - p->lockCount++; -} - -MBinBuffer::MBinBuffer(size_t preAlloc) -{ - BufImpl *p = (BufImpl *)mir_alloc(sizeof(BufImpl) + preAlloc); - p->lockCount = 1; - p->size = (unsigned)preAlloc; - m_buf = (uint8_t *)(p + 1); -} - -MBinBuffer& MBinBuffer::operator=(MBinBuffer &&from) noexcept -{ - m_buf = from.m_buf; - from.m_buf = nullptr; - return *this; -} - -MBinBuffer::~MBinBuffer() -{ - ptr2buf(m_buf)->free(); -} - -void MBinBuffer::append(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); - if (p) { - m_buf = (uint8_t *)(p + 1); - memcpy(m_buf + p->size, pBuf, bufLen); - p->size += (unsigned)bufLen; - } - else m_buf = nullptr; -} - -void MBinBuffer::appendBefore(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); - if (p) { - m_buf = (uint8_t *)(p + 1); - memmove(m_buf + bufLen, m_buf, p->size); - memcpy(m_buf, pBuf, bufLen); - p->size += (unsigned)bufLen; - } - else m_buf = nullptr; -} - -void MBinBuffer::assign(const void *pBuf, size_t bufLen) -{ - if (pBuf == nullptr || bufLen == 0) - return; - - BufImpl *p = ptr2buf(m_buf)->alloc(bufLen); - if (p) { - p->size = (unsigned)bufLen; - m_buf = (uint8_t *)(p + 1); - memcpy(m_buf, pBuf, bufLen); - } - else m_buf = nullptr; -} - -size_t MBinBuffer::length() const -{ - BufImpl *p = ptr2buf(m_buf); - return (p) ? p->size : 0; -} - -void MBinBuffer::remove(size_t sz) -{ - BufImpl *p = ptr2buf(m_buf); - if (!p) - return; - - if (sz > p->size) - sz = p->size; - - if (p->size == sz) { - p->free(); - m_buf = nullptr; - } - else { - memmove(m_buf, m_buf + sz, p->size - sz); - p->size -= (unsigned)sz; - } -} +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "stdafx.h" + +struct BufImpl +{ + uint32_t size, lockCount; + + BufImpl* alloc(size_t newSize) + { + bool bEmpty = (this == nullptr); + auto *res = (BufImpl *)mir_realloc(this, newSize + sizeof(BufImpl)); + if (bEmpty) { + res->lockCount = 1; + res->size = 0; + } + return res; + } + + BufImpl* realloc(size_t newSize) + { + bool bEmpty; + newSize += sizeof(BufImpl); + if (this != nullptr) { + newSize += size; + bEmpty = false; + } + else bEmpty = true; + + auto *res = (BufImpl *)mir_realloc(this, newSize); + if (bEmpty) { + res->lockCount = 1; + res->size = 0; + } + return res; + } + + void free() + { + if (this == nullptr) + return; + + if (lockCount == 1) + mir_free(this); + else + lockCount--; + } +}; + +__forceinline BufImpl* ptr2buf(uint8_t *p) +{ + return (p == nullptr) ? nullptr : (BufImpl*)p-1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MBinBuffer::MBinBuffer() +{} + +MBinBuffer::MBinBuffer(const MBinBuffer &orig) +{ + ptr2buf(m_buf)->free(); + + BufImpl *p = ptr2buf(m_buf = orig.m_buf); + if (p) + p->lockCount++; +} + +MBinBuffer::MBinBuffer(size_t preAlloc) +{ + BufImpl *p = (BufImpl *)mir_alloc(sizeof(BufImpl) + preAlloc); + p->lockCount = 1; + p->size = (unsigned)preAlloc; + m_buf = (uint8_t *)(p + 1); +} + +MBinBuffer& MBinBuffer::operator=(MBinBuffer &&from) noexcept +{ + m_buf = from.m_buf; + from.m_buf = nullptr; + return *this; +} + +MBinBuffer::~MBinBuffer() +{ + ptr2buf(m_buf)->free(); +} + +void MBinBuffer::append(const void *pBuf, size_t bufLen) +{ + if (pBuf == nullptr || bufLen == 0) + return; + + BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); + if (p) { + m_buf = (uint8_t *)(p + 1); + memcpy(m_buf + p->size, pBuf, bufLen); + p->size += (unsigned)bufLen; + } + else m_buf = nullptr; +} + +void MBinBuffer::appendBefore(const void *pBuf, size_t bufLen) +{ + if (pBuf == nullptr || bufLen == 0) + return; + + BufImpl *p = ptr2buf(m_buf)->realloc(bufLen); + if (p) { + m_buf = (uint8_t *)(p + 1); + memmove(m_buf + bufLen, m_buf, p->size); + memcpy(m_buf, pBuf, bufLen); + p->size += (unsigned)bufLen; + } + else m_buf = nullptr; +} + +void MBinBuffer::assign(const void *pBuf, size_t bufLen) +{ + if (pBuf == nullptr || bufLen == 0) + return; + + BufImpl *p = ptr2buf(m_buf)->alloc(bufLen); + if (p) { + p->size = (unsigned)bufLen; + m_buf = (uint8_t *)(p + 1); + memcpy(m_buf, pBuf, bufLen); + } + else m_buf = nullptr; +} + +size_t MBinBuffer::length() const +{ + BufImpl *p = ptr2buf(m_buf); + return (p) ? p->size : 0; +} + +void MBinBuffer::remove(size_t sz) +{ + BufImpl *p = ptr2buf(m_buf); + if (!p) + return; + + if (sz > p->size) + sz = p->size; + + if (p->size == sz) { + p->free(); + m_buf = nullptr; + } + else { + memmove(m_buf, m_buf + sz, p->size - sz); + p->size -= (unsigned)sz; + } +} diff --git a/src/mir_core/src/bitmaps.cpp b/src/mir_core/src/bitmaps.cpp index eb1a8b02d2..5ab665df84 100644 --- a/src/mir_core/src/bitmaps.cpp +++ b/src/mir_core/src/bitmaps.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team, +Copyright (C) 2012-23 Miranda NG team, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/db.cpp b/src/mir_core/src/db.cpp index 8fb13fd4d3..362d359f17 100644 --- a/src/mir_core/src/db.cpp +++ b/src/mir_core/src/db.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/http.cpp b/src/mir_core/src/http.cpp index e983676dd0..879df3b038 100644 --- a/src/mir_core/src/http.cpp +++ b/src/mir_core/src/http.cpp @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/mir_core/src/lists.cpp b/src/mir_core/src/lists.cpp index 21d890a148..c5b1b36825 100644 --- a/src/mir_core/src/lists.cpp +++ b/src/mir_core/src/lists.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/mir_core/src/logger.cpp b/src/mir_core/src/logger.cpp index 24240d691d..83218f88ed 100644 --- a/src/mir_core/src/logger.cpp +++ b/src/mir_core/src/logger.cpp @@ -1,203 +1,203 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define SECRET_SIGNATURE 0x87654321 - -struct Logger -{ - Logger(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFilename, unsigned options) : - m_name(mir_strdup(pszName)), - m_descr(mir_wstrdup(ptszDescr)), - m_fileName(mir_wstrdup(ptszFilename)), - m_options(options), - m_signature(SECRET_SIGNATURE), - m_out(nullptr), - m_lastwrite(0) - { - } - - ~Logger() - { - if (m_out) - fclose(m_out); - } - - int m_signature; - ptrA m_name; - ptrW m_fileName, m_descr; - FILE *m_out; - time_t m_lastwrite; - unsigned m_options; - mir_cs m_cs; -}; - -static int CompareLoggers(const Logger *p1, const Logger *p2) -{ return strcmp(p1->m_name, p2->m_name); -} - -static OBJLIST arLoggers(1, CompareLoggers); - -void InitLogs() -{ -} - -void UninitLogs() -{ - arLoggers.destroy(); -} - -void CheckLogs() -{ - time_t tm = time(0); - - for (auto &p : arLoggers) { - mir_cslock lck(p->m_cs); - if (p->m_out && tm - p->m_lastwrite > 5) { - fclose(p->m_out); - p->m_out = nullptr; - } - else fflush(p->m_out); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(HANDLE) mir_createLog(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options) -{ - if (ptszFile == nullptr) - return nullptr; - - Logger *result = new Logger(pszName, ptszDescr, ptszFile, options); - if (result == nullptr) - return nullptr; - - int idx = arLoggers.getIndex(result); - if (idx != -1) { - delete result; - return &arLoggers[idx]; - } - - CreatePathToFileW(ptszFile); - _wremove(ptszFile); - arLoggers.insert(result); - return result; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -static Logger* prepareLogger(HANDLE hLogger) -{ - if (hLogger == nullptr) - return nullptr; - - Logger *p = (Logger*)hLogger; - return (p->m_signature == SECRET_SIGNATURE) ? p : nullptr; -} - -MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger) -{ - Logger *p = prepareLogger(hLogger); - if (p != nullptr) - arLoggers.remove(p); -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - va_list args; - va_start(args, format); - vfprintf(p->m_out, format, args); - va_end(args); - - p->m_lastwrite = time(0); - return 0; -} - -MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - va_list args; - va_start(args, format); - vfwprintf(p->m_out, format, args); - va_end(args); - - p->m_lastwrite = time(0); - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - vfprintf(p->m_out, format, args); - - p->m_lastwrite = time(0); - return 0; -} - -MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args) -{ - Logger *p = prepareLogger(hLogger); - if (p == nullptr) - return 1; - - mir_cslock lck(p->m_cs); - if (p->m_out == nullptr) - if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) - return 2; - - vfwprintf(p->m_out, format, args); - - p->m_lastwrite = time(0); - return 0; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#define SECRET_SIGNATURE 0x87654321 + +struct Logger +{ + Logger(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFilename, unsigned options) : + m_name(mir_strdup(pszName)), + m_descr(mir_wstrdup(ptszDescr)), + m_fileName(mir_wstrdup(ptszFilename)), + m_options(options), + m_signature(SECRET_SIGNATURE), + m_out(nullptr), + m_lastwrite(0) + { + } + + ~Logger() + { + if (m_out) + fclose(m_out); + } + + int m_signature; + ptrA m_name; + ptrW m_fileName, m_descr; + FILE *m_out; + time_t m_lastwrite; + unsigned m_options; + mir_cs m_cs; +}; + +static int CompareLoggers(const Logger *p1, const Logger *p2) +{ return strcmp(p1->m_name, p2->m_name); +} + +static OBJLIST arLoggers(1, CompareLoggers); + +void InitLogs() +{ +} + +void UninitLogs() +{ + arLoggers.destroy(); +} + +void CheckLogs() +{ + time_t tm = time(0); + + for (auto &p : arLoggers) { + mir_cslock lck(p->m_cs); + if (p->m_out && tm - p->m_lastwrite > 5) { + fclose(p->m_out); + p->m_out = nullptr; + } + else fflush(p->m_out); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(HANDLE) mir_createLog(const char* pszName, const wchar_t *ptszDescr, const wchar_t *ptszFile, unsigned options) +{ + if (ptszFile == nullptr) + return nullptr; + + Logger *result = new Logger(pszName, ptszDescr, ptszFile, options); + if (result == nullptr) + return nullptr; + + int idx = arLoggers.getIndex(result); + if (idx != -1) { + delete result; + return &arLoggers[idx]; + } + + CreatePathToFileW(ptszFile); + _wremove(ptszFile); + arLoggers.insert(result); + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +static Logger* prepareLogger(HANDLE hLogger) +{ + if (hLogger == nullptr) + return nullptr; + + Logger *p = (Logger*)hLogger; + return (p->m_signature == SECRET_SIGNATURE) ? p : nullptr; +} + +MIR_CORE_DLL(void) mir_closeLog(HANDLE hLogger) +{ + Logger *p = prepareLogger(hLogger); + if (p != nullptr) + arLoggers.remove(p); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +MIR_C_CORE_DLL(int) mir_writeLogA(HANDLE hLogger, const char *format, ...) +{ + Logger *p = prepareLogger(hLogger); + if (p == nullptr) + return 1; + + mir_cslock lck(p->m_cs); + if (p->m_out == nullptr) + if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) + return 2; + + va_list args; + va_start(args, format); + vfprintf(p->m_out, format, args); + va_end(args); + + p->m_lastwrite = time(0); + return 0; +} + +MIR_C_CORE_DLL(int) mir_writeLogW(HANDLE hLogger, const wchar_t *format, ...) +{ + Logger *p = prepareLogger(hLogger); + if (p == nullptr) + return 1; + + mir_cslock lck(p->m_cs); + if (p->m_out == nullptr) + if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) + return 2; + + va_list args; + va_start(args, format); + vfwprintf(p->m_out, format, args); + va_end(args); + + p->m_lastwrite = time(0); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) mir_writeLogVA(HANDLE hLogger, const char *format, va_list args) +{ + Logger *p = prepareLogger(hLogger); + if (p == nullptr) + return 1; + + mir_cslock lck(p->m_cs); + if (p->m_out == nullptr) + if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) + return 2; + + vfprintf(p->m_out, format, args); + + p->m_lastwrite = time(0); + return 0; +} + +MIR_CORE_DLL(int) mir_writeLogVW(HANDLE hLogger, const wchar_t *format, va_list args) +{ + Logger *p = prepareLogger(hLogger); + if (p == nullptr) + return 1; + + mir_cslock lck(p->m_cs); + if (p->m_out == nullptr) + if ((p->m_out = _wfopen(p->m_fileName, L"ab")) == nullptr) + return 2; + + vfwprintf(p->m_out, format, args); + + p->m_lastwrite = time(0); + return 0; +} diff --git a/src/mir_core/src/memory.cpp b/src/mir_core/src/memory.cpp index 8cf95b81ab..5183310b48 100644 --- a/src/mir_core/src/memory.cpp +++ b/src/mir_core/src/memory.cpp @@ -1,295 +1,295 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -#define BLOCK_ALLOCED 0xABBABABA -#define BLOCK_FREED 0xDEADBEEF - -static int CheckBlock(void* blk) -{ - int result = FALSE; - char* p = (char*)blk - sizeof(uint32_t)*2; - uint32_t size, *b, *e; - - __try - { - size = *(uint32_t*)p; - b = (uint32_t*)&p[ sizeof(uint32_t) ]; - e = (uint32_t*)&p[ sizeof(uint32_t)*2 + size ]; - - if (*b != BLOCK_ALLOCED || *e != BLOCK_ALLOCED) - { - #ifdef _MSC_VER - if (*b == BLOCK_FREED && *e == BLOCK_FREED) - OutputDebugStringA("memory block is already deleted\n"); - else - OutputDebugStringA("memory block is corrupted\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - } - else result = TRUE; - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - #ifdef _MSC_VER - OutputDebugStringA("access violation during checking memory block\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - } - - return result; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_alloc(size_t size) -{ - if (size == 0) - return nullptr; - - char *p = (char*)malloc(size + sizeof(uint32_t)* 3); - if (p == nullptr) { - #ifdef _MSC_VER - OutputDebugStringA("memory overflow\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - return nullptr; - } - - *(uint32_t*)p = (uint32_t)size; - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; - return p + sizeof(uint32_t)* 2; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_calloc(size_t size) -{ - void* p = mir_alloc(size); - if (p != nullptr) - memset(p, 0, size); - return p; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t size) -{ - char *p; - - if (ptr != nullptr) { - if (!CheckBlock(ptr)) - return nullptr; - p = (char*)ptr - sizeof(uint32_t)*2; - } - else p = nullptr; - - p = (char*)realloc(p, size + sizeof(uint32_t)*3); - if (p == nullptr) { - #ifdef _MSC_VER - OutputDebugStringA("memory overflow\n"); - #if defined(_DEBUG) - DebugBreak(); - #endif - #endif - return nullptr; - } - - *(uint32_t*)p = (uint32_t)size; - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; - return p + sizeof(uint32_t)*2; -} - -/******************************************************************************/ - -MIR_C_CORE_DLL(void) mir_free(void* ptr) -{ - char* p; - uint32_t size; - - if (ptr == nullptr) - return; - if (!CheckBlock(ptr)) - return; - - p = (char*)ptr - sizeof(uint32_t)*2; - size = *(uint32_t*)p; - - *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_FREED; - *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_FREED; - free(p); -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_strdup(const char *str) -{ - if (str == nullptr) - return nullptr; - - char *p = (char*)mir_alloc(strlen(str)+1); - if (p) - strcpy(p, str); - return p; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(wcslen(str)+1)); - if (p) - wcscpy(p, str); - return p; -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_strndup(const char *str, size_t len) -{ - if (str == nullptr || len == 0) - return nullptr; - - char *p = (char*)mir_alloc(len+1); - if (p) { - memcpy(p, str, len); - p[len] = 0; - } - return p; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len) -{ - if (str == nullptr || len == 0) - return nullptr; - - wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(len+1)); - if (p) { - memcpy(p, str, sizeof(wchar_t)*len); - p[len] = 0; - } - return p; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_snprintf(char *buffer, size_t count, const char* fmt, ...) -{ - va_list va; - va_start(va, fmt); - int len = _vsnprintf(buffer, count-1, fmt, va); - va_end(va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_snwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, ...) -{ - va_list va; - va_start(va, fmt); - int len = _vsnwprintf(buffer, count-1, fmt, va); - va_end(va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_vsnprintf(char *buffer, size_t count, const char* fmt, va_list va) -{ - int len = _vsnprintf(buffer, count-1, fmt, va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -MIR_CORE_DLL(int) mir_vsnwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, va_list va) -{ - int len = _vsnwprintf(buffer, count-1, fmt, va); - buffer[count-1] = 0; - return len; -} - -/******************************************************************************/ - -#ifdef _MSC_VER -MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage) -{ - if (src == nullptr) - return nullptr; - - int cbLen = MultiByteToWideChar(codepage, 0, src, -1, nullptr, 0); - wchar_t* result = (wchar_t*)mir_alloc(sizeof(wchar_t)*(cbLen+1)); - if (result == nullptr) - return nullptr; - - MultiByteToWideChar(codepage, 0, src, -1, result, cbLen); - result[cbLen] = 0; - return result; -} - -/******************************************************************************/ - -MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src) -{ - return mir_a2u_cp(src, Langpack_GetDefaultCodePage()); -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage) -{ - if (src == nullptr) - return nullptr; - - int cbLen = WideCharToMultiByte(codepage, 0, src, -1, nullptr, 0, nullptr, nullptr); - char* result = (char*)mir_alloc(cbLen+1); - if (result == nullptr) - return nullptr; - - WideCharToMultiByte(codepage, 0, src, -1, result, cbLen, nullptr, nullptr); - result[cbLen] = 0; - return result; -} - -/******************************************************************************/ - -MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src) -{ - return mir_u2a_cp(src, Langpack_GetDefaultCodePage()); -} -#endif +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +#define BLOCK_ALLOCED 0xABBABABA +#define BLOCK_FREED 0xDEADBEEF + +static int CheckBlock(void* blk) +{ + int result = FALSE; + char* p = (char*)blk - sizeof(uint32_t)*2; + uint32_t size, *b, *e; + + __try + { + size = *(uint32_t*)p; + b = (uint32_t*)&p[ sizeof(uint32_t) ]; + e = (uint32_t*)&p[ sizeof(uint32_t)*2 + size ]; + + if (*b != BLOCK_ALLOCED || *e != BLOCK_ALLOCED) + { + #ifdef _MSC_VER + if (*b == BLOCK_FREED && *e == BLOCK_FREED) + OutputDebugStringA("memory block is already deleted\n"); + else + OutputDebugStringA("memory block is corrupted\n"); + #if defined(_DEBUG) + DebugBreak(); + #endif + #endif + } + else result = TRUE; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + #ifdef _MSC_VER + OutputDebugStringA("access violation during checking memory block\n"); + #if defined(_DEBUG) + DebugBreak(); + #endif + #endif + } + + return result; +} + +/******************************************************************************/ + +MIR_C_CORE_DLL(void*) mir_alloc(size_t size) +{ + if (size == 0) + return nullptr; + + char *p = (char*)malloc(size + sizeof(uint32_t)* 3); + if (p == nullptr) { + #ifdef _MSC_VER + OutputDebugStringA("memory overflow\n"); + #if defined(_DEBUG) + DebugBreak(); + #endif + #endif + return nullptr; + } + + *(uint32_t*)p = (uint32_t)size; + *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; + *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; + return p + sizeof(uint32_t)* 2; +} + +/******************************************************************************/ + +MIR_C_CORE_DLL(void*) mir_calloc(size_t size) +{ + void* p = mir_alloc(size); + if (p != nullptr) + memset(p, 0, size); + return p; +} + +/******************************************************************************/ + +MIR_C_CORE_DLL(void*) mir_realloc(void* ptr, size_t size) +{ + char *p; + + if (ptr != nullptr) { + if (!CheckBlock(ptr)) + return nullptr; + p = (char*)ptr - sizeof(uint32_t)*2; + } + else p = nullptr; + + p = (char*)realloc(p, size + sizeof(uint32_t)*3); + if (p == nullptr) { + #ifdef _MSC_VER + OutputDebugStringA("memory overflow\n"); + #if defined(_DEBUG) + DebugBreak(); + #endif + #endif + return nullptr; + } + + *(uint32_t*)p = (uint32_t)size; + *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_ALLOCED; + *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_ALLOCED; + return p + sizeof(uint32_t)*2; +} + +/******************************************************************************/ + +MIR_C_CORE_DLL(void) mir_free(void* ptr) +{ + char* p; + uint32_t size; + + if (ptr == nullptr) + return; + if (!CheckBlock(ptr)) + return; + + p = (char*)ptr - sizeof(uint32_t)*2; + size = *(uint32_t*)p; + + *(uint32_t*)&p[sizeof(uint32_t)] = BLOCK_FREED; + *(uint32_t*)&p[size + sizeof(uint32_t)*2] = BLOCK_FREED; + free(p); +} + +/******************************************************************************/ + +MIR_CORE_DLL(char*) mir_strdup(const char *str) +{ + if (str == nullptr) + return nullptr; + + char *p = (char*)mir_alloc(strlen(str)+1); + if (p) + strcpy(p, str); + return p; +} + +MIR_CORE_DLL(wchar_t*) mir_wstrdup(const wchar_t *str) +{ + if (str == nullptr) + return nullptr; + + wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(wcslen(str)+1)); + if (p) + wcscpy(p, str); + return p; +} + +/******************************************************************************/ + +MIR_CORE_DLL(char*) mir_strndup(const char *str, size_t len) +{ + if (str == nullptr || len == 0) + return nullptr; + + char *p = (char*)mir_alloc(len+1); + if (p) { + memcpy(p, str, len); + p[len] = 0; + } + return p; +} + +MIR_CORE_DLL(wchar_t*) mir_wstrndup(const wchar_t *str, size_t len) +{ + if (str == nullptr || len == 0) + return nullptr; + + wchar_t *p = (wchar_t*)mir_alloc(sizeof(wchar_t)*(len+1)); + if (p) { + memcpy(p, str, sizeof(wchar_t)*len); + p[len] = 0; + } + return p; +} + +/******************************************************************************/ + +MIR_CORE_DLL(int) mir_snprintf(char *buffer, size_t count, const char* fmt, ...) +{ + va_list va; + va_start(va, fmt); + int len = _vsnprintf(buffer, count-1, fmt, va); + va_end(va); + buffer[count-1] = 0; + return len; +} + +/******************************************************************************/ + +MIR_CORE_DLL(int) mir_snwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, ...) +{ + va_list va; + va_start(va, fmt); + int len = _vsnwprintf(buffer, count-1, fmt, va); + va_end(va); + buffer[count-1] = 0; + return len; +} + +/******************************************************************************/ + +MIR_CORE_DLL(int) mir_vsnprintf(char *buffer, size_t count, const char* fmt, va_list va) +{ + int len = _vsnprintf(buffer, count-1, fmt, va); + buffer[count-1] = 0; + return len; +} + +/******************************************************************************/ + +MIR_CORE_DLL(int) mir_vsnwprintf(wchar_t *buffer, size_t count, const wchar_t* fmt, va_list va) +{ + int len = _vsnwprintf(buffer, count-1, fmt, va); + buffer[count-1] = 0; + return len; +} + +/******************************************************************************/ + +#ifdef _MSC_VER +MIR_CORE_DLL(wchar_t*) mir_a2u_cp(const char* src, int codepage) +{ + if (src == nullptr) + return nullptr; + + int cbLen = MultiByteToWideChar(codepage, 0, src, -1, nullptr, 0); + wchar_t* result = (wchar_t*)mir_alloc(sizeof(wchar_t)*(cbLen+1)); + if (result == nullptr) + return nullptr; + + MultiByteToWideChar(codepage, 0, src, -1, result, cbLen); + result[cbLen] = 0; + return result; +} + +/******************************************************************************/ + +MIR_CORE_DLL(wchar_t*) mir_a2u(const char* src) +{ + return mir_a2u_cp(src, Langpack_GetDefaultCodePage()); +} + +/******************************************************************************/ + +MIR_CORE_DLL(char*) mir_u2a_cp(const wchar_t* src, int codepage) +{ + if (src == nullptr) + return nullptr; + + int cbLen = WideCharToMultiByte(codepage, 0, src, -1, nullptr, 0, nullptr, nullptr); + char* result = (char*)mir_alloc(cbLen+1); + if (result == nullptr) + return nullptr; + + WideCharToMultiByte(codepage, 0, src, -1, result, cbLen, nullptr, nullptr); + result[cbLen] = 0; + return result; +} + +/******************************************************************************/ + +MIR_CORE_DLL(char*) mir_u2a(const wchar_t* src) +{ + return mir_u2a_cp(src, Langpack_GetDefaultCodePage()); +} +#endif diff --git a/src/mir_core/src/miranda.h b/src/mir_core/src/miranda.h index 7264f27444..2b725888f2 100644 --- a/src/mir_core/src/miranda.h +++ b/src/mir_core/src/miranda.h @@ -1,101 +1,101 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -void UnloadLangPackModule(void); - -int InitialiseModularEngine(void); -void DestroyModularEngine(void); - -int InitPathUtils(void); - -extern HINSTANCE g_hInst; -extern HWND hAPCWindow; -extern HANDLE hThreadQueueEmpty; -extern HCURSOR g_hCursorNS, g_hCursorWE; -extern bool g_bEnableDpiAware; - -///////////////////////////////////////////////////////////////////////////////////////// -// modules.cpp - -struct THookSubscriber -{ - HINSTANCE hOwner; - int type; - union { - struct { - union { - MIRANDAHOOK pfnHook; - MIRANDAHOOKPARAM pfnHookParam; - MIRANDAHOOKOBJ pfnHookObj; - MIRANDAHOOKOBJPARAM pfnHookObjParam; - }; - void* object; - LPARAM lParam; - }; - struct { - HWND hwnd; - UINT message; - }; - }; -}; - -#define HOOK_SECRET_SIGNATURE 0xDEADBABA - -struct THook : public MZeroedObject -{ - char name[MAXMODULELABELLENGTH]; - int id; - int subscriberCount; - THookSubscriber* subscriber; - MIRANDAHOOK pfnHook; - uint32_t secretSignature = HOOK_SECRET_SIGNATURE; - mir_cs csHook; -}; - -extern LIST pluginListAddr; - -///////////////////////////////////////////////////////////////////////////////////////// -// langpack.cpp - -char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W); - -///////////////////////////////////////////////////////////////////////////////////////// -// miranda.cpp - -EXTERN_C MIR_CORE_DLL(void) BeginMessageLoop(void); -EXTERN_C MIR_CORE_DLL(void) EnterMessageLoop(void); -EXTERN_C MIR_CORE_DLL(void) LeaveMessageLoop(void); - -///////////////////////////////////////////////////////////////////////////////////////// -// threads.cpp - -extern uint32_t mir_tls; - -///////////////////////////////////////////////////////////////////////////////////////// -// utils.cpp - -typedef BOOL(MIR_SYSCALL *PGENRANDOM)(void*, uint32_t); -extern PGENRANDOM pfnRtlGenRandom; +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#pragma once + +void UnloadLangPackModule(void); + +int InitialiseModularEngine(void); +void DestroyModularEngine(void); + +int InitPathUtils(void); + +extern HINSTANCE g_hInst; +extern HWND hAPCWindow; +extern HANDLE hThreadQueueEmpty; +extern HCURSOR g_hCursorNS, g_hCursorWE; +extern bool g_bEnableDpiAware; + +///////////////////////////////////////////////////////////////////////////////////////// +// modules.cpp + +struct THookSubscriber +{ + HINSTANCE hOwner; + int type; + union { + struct { + union { + MIRANDAHOOK pfnHook; + MIRANDAHOOKPARAM pfnHookParam; + MIRANDAHOOKOBJ pfnHookObj; + MIRANDAHOOKOBJPARAM pfnHookObjParam; + }; + void* object; + LPARAM lParam; + }; + struct { + HWND hwnd; + UINT message; + }; + }; +}; + +#define HOOK_SECRET_SIGNATURE 0xDEADBABA + +struct THook : public MZeroedObject +{ + char name[MAXMODULELABELLENGTH]; + int id; + int subscriberCount; + THookSubscriber* subscriber; + MIRANDAHOOK pfnHook; + uint32_t secretSignature = HOOK_SECRET_SIGNATURE; + mir_cs csHook; +}; + +extern LIST pluginListAddr; + +///////////////////////////////////////////////////////////////////////////////////////// +// langpack.cpp + +char* LangPackTranslateString(const MUUID *pUuid, const char *szEnglish, const int W); + +///////////////////////////////////////////////////////////////////////////////////////// +// miranda.cpp + +EXTERN_C MIR_CORE_DLL(void) BeginMessageLoop(void); +EXTERN_C MIR_CORE_DLL(void) EnterMessageLoop(void); +EXTERN_C MIR_CORE_DLL(void) LeaveMessageLoop(void); + +///////////////////////////////////////////////////////////////////////////////////////// +// threads.cpp + +extern uint32_t mir_tls; + +///////////////////////////////////////////////////////////////////////////////////////// +// utils.cpp + +typedef BOOL(MIR_SYSCALL *PGENRANDOM)(void*, uint32_t); +extern PGENRANDOM pfnRtlGenRandom; diff --git a/src/mir_core/src/modules.cpp b/src/mir_core/src/modules.cpp index 1636d7449e..0d471c8c6c 100644 --- a/src/mir_core/src/modules.cpp +++ b/src/mir_core/src/modules.cpp @@ -1,704 +1,704 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -// list of hooks - -static int compareHooks(const THook* p1, const THook* p2) -{ - return strcmp(p1->name, p2->name); -} - -static LIST hooks(50, compareHooks); - -struct THookToMainThreadItem -{ - THook* hook; - HANDLE hDoneEvent; - WPARAM wParam; - LPARAM lParam; - int result; -}; - -// list of services - -struct TService -{ - uint32_t nameHash; - HINSTANCE hOwner; - union - { - MIRANDASERVICE pfnService; - MIRANDASERVICEPARAM pfnServiceParam; - MIRANDASERVICEOBJ pfnServiceObj; - MIRANDASERVICEOBJPARAM pfnServiceObjParam; - }; - int flags; - LPARAM lParam; - void* object; - char name[1]; -}; - -LIST services(100, NumericKeySortT); - -struct TServiceToMainThreadItem -{ - HANDLE hDoneEvent; - WPARAM wParam; - LPARAM lParam; - int result; - const char *name; -}; - -// other static variables -static BOOL bServiceMode = FALSE; -static mir_cs csHooks, csServices; -static uint32_t mainThreadId; -static int sttHookId = 1; - -///////////////////////////////////////////////////////////////////////////////////////// - -__forceinline HANDLE getThreadEvent() -{ - HANDLE pData = (HANDLE)TlsGetValue(mir_tls); - if (pData == nullptr) { - pData = CreateEvent(nullptr, FALSE, FALSE, nullptr); - TlsSetValue(mir_tls, pData); - } - return pData; -} - -static int QueueMainThread(PAPCFUNC pFunc, void* pParam, HANDLE hDoneEvent) -{ - int result = PostMessage(hAPCWindow, WM_USER + 1, (WPARAM)pFunc, (LPARAM)pParam); // let this get processed in its own time - if (hDoneEvent) - WaitForSingleObject(hDoneEvent, INFINITE); - - return result; -} - -/////////////////////////////////////////////////////////////////////////////// -// HOOKS - -MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name) -{ - if (name == nullptr) - return nullptr; - - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) != -1) - return hooks[idx]; - - THook *newItem = new THook(); - strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[MAXMODULELABELLENGTH - 1] = 0; - newItem->id = sttHookId++; - hooks.insert(newItem); - return (HANDLE)newItem; -} - -MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) -{ - if (hEvent == nullptr) - return 1; - - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)hEvent)) == -1) - return 1; - - THook *p = hooks[idx]; - p->secretSignature = 0; - if (p->subscriberCount) { - mir_free(p->subscriber); - p->subscriber = nullptr; - p->subscriberCount = 0; - } - hooks.remove(idx); - delete p; - return 0; -} - -MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook) -{ - THook *p = (THook*)hEvent; - - mir_cslock lck(csHooks); - if (hooks.getIndex(p) != -1) - p->pfnHook = pfnHook; - return 0; -} - -MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam, LPARAM lParam) -{ - int idx; - if ((idx = hooks.getIndex((THook *)pszEvent)) == -1) - return -1; - - THook *p = hooks[idx]; - if (p == nullptr || hInst == nullptr) - return -1; - - mir_cslock lck(p->csHook); - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - if (s->hOwner != hInst) - continue; - - int returnVal; - switch (s->type) { - case 1: returnVal = s->pfnHook(wParam, lParam); break; - case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - if (p->subscriberCount == 0 && p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - THook *p = (THook*)hEvent; - if (p == nullptr || pObject == nullptr) - return -1; - - mir_cslock lck(p->csHook); - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - if (s->object != pObject) - continue; - - int returnVal; - switch (s->type) { - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - if (p->subscriberCount == 0 && p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -static int CallHookSubscribers(THook *p, WPARAM wParam, LPARAM lParam) -{ - if (p == nullptr) - return -1; - - mir_cslock lck(p->csHook); - - // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though. - for (int i = 0; i < p->subscriberCount; i++) { - THookSubscriber* s = &p->subscriber[i]; - - int returnVal; - switch (s->type) { - case 1: returnVal = s->pfnHook(wParam, lParam); break; - case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; - case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; - case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; - case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; - default: continue; - } - if (returnVal) - return returnVal; - } - - // call the default hook if any - if (p->pfnHook != nullptr) - return p->pfnHook(wParam, lParam); - - return 0; -} - -enum { hookOk, hookEmpty, hookInvalid }; - -int checkHook(THook *p) -{ - if (p == nullptr) - return hookInvalid; - - int ret; - __try { - if (p->secretSignature != HOOK_SECRET_SIGNATURE) - ret = hookInvalid; - else if (p->subscriberCount == 0 && p->pfnHook == nullptr) - ret = hookEmpty; - else - ret = hookOk; - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - ret = hookInvalid; - } - - return ret; -} - -static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam) -{ - THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam; - item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - switch (checkHook((THook*)hEvent)) { - case hookInvalid: return -1; - case hookEmpty: return 0; - } - - if (GetCurrentThreadId() == mainThreadId) - return CallHookSubscribers((THook*)hEvent, wParam, lParam); - - THookToMainThreadItem item; - item.hDoneEvent = getThreadEvent(); - item.hook = (THook*)hEvent; - item.wParam = wParam; - item.lParam = lParam; - QueueMainThread(HookToMainAPCFunc, &item, item.hDoneEvent); - return item.result; -} - -MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam, LPARAM lParam) -{ - switch (checkHook((THook*)hEvent)) { - case hookInvalid: return -1; - case hookEmpty: return 0; - } - - return CallHookSubscribers((THook*)hEvent, wParam, lParam); -} - -extern "C" MIR_CORE_DLL(int) GetSubscribersCount(THook* pHook) -{ - switch (checkHook(pHook)) { - case hookInvalid: - case hookEmpty: return 0; - } - return pHook->subscriberCount; -} - -static HANDLE HookEventInt(int type, const char *name, MIRANDAHOOK hookProc, void* object, LPARAM lParam) -{ - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) - return nullptr; - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - - THookSubscriber &s = p->subscriber[p->subscriberCount]; - s.type = type; - s.pfnHook = hookProc; - s.object = object; - s.lParam = lParam; - s.hOwner = GetInstByAddress(hookProc); - p->subscriberCount++; - - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc) -{ - return HookEventInt(1, name, hookProc, nullptr, 0); -} - -MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam) -{ - return HookEventInt(2, name, (MIRANDAHOOK)hookProc, nullptr, lParam); -} - -MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object) -{ - return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0); -} - -MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam) -{ - return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam); -} - -MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc) -{ - mir_cslockfull lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) { - lck.unlock(); - hookProc(0, 0); - return nullptr; - } - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - - THookSubscriber &s = p->subscriber[p->subscriberCount]; - memset(&s, 0, sizeof(THookSubscriber)); - s.type = 1; - s.pfnHook = hookProc; - s.hOwner = GetInstByAddress(hookProc); - - p->subscriberCount++; - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message) -{ - mir_cslock lck(csHooks); - - int idx; - if ((idx = hooks.getIndex((THook*)name)) == -1) - return nullptr; - - THook *p = hooks[idx]; - p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); - p->subscriber[p->subscriberCount].type = 5; - p->subscriber[p->subscriberCount].hwnd = hwnd; - p->subscriber[p->subscriberCount].message = message; - p->subscriberCount++; - return (HANDLE)((p->id << 16) | p->subscriberCount); -} - -MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook) -{ - if (hHook == nullptr) - return 0; - - int hookId = (INT_PTR)hHook >> 16; - int subscriberId = ((INT_PTR)hHook & 0xFFFF) - 1; - - mir_cslock lck(csHooks); - - THook *p = nullptr; - for (auto &it : hooks) - if (it->id == hookId) { - p = it; - break; - } - - if (p == nullptr) - return 1; - - if (subscriberId >= p->subscriberCount || subscriberId < 0) - return 1; - - p->subscriber[subscriberId].type = 0; - p->subscriber[subscriberId].pfnHook = nullptr; - p->subscriber[subscriberId].hOwner = nullptr; - while (p->subscriberCount && p->subscriber[p->subscriberCount - 1].type == 0) - p->subscriberCount--; - if (p->subscriberCount == 0) { - mir_free(p->subscriber); - p->subscriber = nullptr; - } - return 0; -} - -MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst) -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks.rev_iter()) { - if (it->subscriberCount == 0) - continue; - - for (int j = it->subscriberCount - 1; j >= 0; j--) { - if (it->subscriber[j].hOwner != hInst) - continue; - - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); - UnhookEvent((HANDLE)((it->id << 16) + j + 1)); - if (it->subscriberCount == 0) - break; - } - } -} - -MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject) -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks.rev_iter()) { - if (it->subscriberCount == 0) - continue; - - for (int j = it->subscriberCount - 1; j >= 0; j--) { - if (it->subscriber[j].object == pObject) { - UnhookEvent((HANDLE)((it->id << 16) + j + 1)); - if (it->subscriberCount == 0) - break; - } - } - } -} - -static void DestroyHooks() -{ - mir_cslock lck(csHooks); - - for (auto &it : hooks) { - if (it->subscriberCount) - mir_free(it->subscriber); - delete it; - } -} - -/////////////////////SERVICES - -static __inline TService* FindServiceByName(const char *name) -{ - unsigned hash = mir_hashstr(name); - return services.find((TService*)&hash); -} - -static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam) -{ - if (name == nullptr) - return nullptr; - - TService tmp; - tmp.nameHash = mir_hashstr(name); - - mir_cslock lck(csServices); - - if (services.getIndex(&tmp) != -1) - return nullptr; - - TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name)); - strcpy(p->name, name); - p->nameHash = tmp.nameHash; - p->pfnService = serviceProc; - p->hOwner = GetInstByAddress(serviceProc); - p->flags = type; - p->lParam = lParam; - p->object = object; - services.insert(p); - - return (HANDLE)tmp.nameHash; -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc) -{ - return CreateServiceInt(0, name, serviceProc, nullptr, 0); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam) -{ - return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, nullptr, lParam); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object) -{ - return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0); -} - -MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam) -{ - return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam); -} - -MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc) -{ - char str[MAXMODULELABELLENGTH * 2]; - strncpy_s(str, szModule, _TRUNCATE); - strncat_s(str, szService, _TRUNCATE); - return CreateServiceFunction(str, serviceProc); -} - -MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService) -{ - mir_cslock lck(csServices); - - int idx = services.getIndex((TService*)&hService); - if (idx != -1) { - mir_free(services[idx]); - services.remove(idx); - } - - return 0; -} - -MIR_CORE_DLL(bool) ServiceExists(const char *name) -{ - if (name == nullptr) - return FALSE; - - mir_cslock lck(csServices); - return FindServiceByName(name) != nullptr; -} - -MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam, LPARAM lParam) -{ - if (name == nullptr) - return CALLSERVICE_NOTFOUND; - - TService *pService; - { - mir_cslock lck(csServices); - if ((pService = FindServiceByName(name)) == nullptr) - return CALLSERVICE_NOTFOUND; - } - - MIRANDASERVICE pfnService = pService->pfnService; - int flags = pService->flags; - LPARAM fnParam = pService->lParam; - void* object = pService->object; - switch (flags) { - case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam); - case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam); - case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam); - default: return pfnService(wParam, lParam); - } -} - -static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam) -{ - TServiceToMainThreadItem *item = (TServiceToMainThreadItem*)dwParam; - item->result = CallService(item->name, item->wParam, item->lParam); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam) -{ - if (name == nullptr) - return CALLSERVICE_NOTFOUND; - - // the service is looked up within the main thread, since the time it takes - // for the APC queue to clear the service being called maybe removed. - // even thou it may exists before the call, the critsec can't be locked between calls. - if (GetCurrentThreadId() == mainThreadId) - return CallService(name, wParam, lParam); - - TServiceToMainThreadItem item; - item.wParam = wParam; - item.lParam = lParam; - item.name = name; - item.hDoneEvent = getThreadEvent(); - QueueMainThread(CallServiceToMainAPCFunc, &item, item.hDoneEvent); - return item.result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) CallFunctionAsync(void(__stdcall *func)(void *), void *arg) -{ - if (GetCurrentThreadId() == mainThreadId) - func(arg); - else - QueueMainThread((PAPCFUNC)func, arg, nullptr); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -struct TSyncCallParam -{ - INT_PTR(__stdcall *func)(void *); - void *arg; - HANDLE hDoneEvent; - INT_PTR result; -}; - -static void CALLBACK CallFuncToMainAPCFunc(ULONG_PTR dwParam) -{ - TSyncCallParam *item = (TSyncCallParam*)dwParam; - item->result = (*item->func)(item->arg); - SetEvent(item->hDoneEvent); -} - -MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(__stdcall *func)(void *), void *arg) -{ - if (GetCurrentThreadId() == mainThreadId) - return func(arg); - - TSyncCallParam param = { func, arg, getThreadEvent() }; - QueueMainThread(CallFuncToMainAPCFunc, ¶m, param.hDoneEvent); - return param.result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst) -{ - mir_cslock lck(csServices); - - for (auto &it : services.rev_iter()) { - if (it->hOwner == hInst) { - char szModuleName[MAX_PATH]; - GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); - DestroyServiceFunction((HANDLE)it->nameHash); - } - } -} - -MIR_CORE_DLL(void) KillObjectServices(void* pObject) -{ - mir_cslock lck(csServices); - - for (auto &it : services.rev_iter()) - if (it->object == pObject) - DestroyServiceFunction((HANDLE)it->nameHash); -} - -static void DestroyServices() -{ - mir_cslock lck(csServices); - - for (auto &it : services) - mir_free(it); -} - -/////////////////////////////////////////////////////////////////////////////// - -int InitialiseModularEngine(void) -{ - mainThreadId = GetCurrentThreadId(); - return 0; -} - -void DestroyModularEngine(void) -{ - DestroyHooks(); - DestroyServices(); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +// list of hooks + +static int compareHooks(const THook* p1, const THook* p2) +{ + return strcmp(p1->name, p2->name); +} + +static LIST hooks(50, compareHooks); + +struct THookToMainThreadItem +{ + THook* hook; + HANDLE hDoneEvent; + WPARAM wParam; + LPARAM lParam; + int result; +}; + +// list of services + +struct TService +{ + uint32_t nameHash; + HINSTANCE hOwner; + union + { + MIRANDASERVICE pfnService; + MIRANDASERVICEPARAM pfnServiceParam; + MIRANDASERVICEOBJ pfnServiceObj; + MIRANDASERVICEOBJPARAM pfnServiceObjParam; + }; + int flags; + LPARAM lParam; + void* object; + char name[1]; +}; + +LIST services(100, NumericKeySortT); + +struct TServiceToMainThreadItem +{ + HANDLE hDoneEvent; + WPARAM wParam; + LPARAM lParam; + int result; + const char *name; +}; + +// other static variables +static BOOL bServiceMode = FALSE; +static mir_cs csHooks, csServices; +static uint32_t mainThreadId; +static int sttHookId = 1; + +///////////////////////////////////////////////////////////////////////////////////////// + +__forceinline HANDLE getThreadEvent() +{ + HANDLE pData = (HANDLE)TlsGetValue(mir_tls); + if (pData == nullptr) { + pData = CreateEvent(nullptr, FALSE, FALSE, nullptr); + TlsSetValue(mir_tls, pData); + } + return pData; +} + +static int QueueMainThread(PAPCFUNC pFunc, void* pParam, HANDLE hDoneEvent) +{ + int result = PostMessage(hAPCWindow, WM_USER + 1, (WPARAM)pFunc, (LPARAM)pParam); // let this get processed in its own time + if (hDoneEvent) + WaitForSingleObject(hDoneEvent, INFINITE); + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// HOOKS + +MIR_CORE_DLL(HANDLE) CreateHookableEvent(const char *name) +{ + if (name == nullptr) + return nullptr; + + mir_cslock lck(csHooks); + + int idx; + if ((idx = hooks.getIndex((THook*)name)) != -1) + return hooks[idx]; + + THook *newItem = new THook(); + strncpy(newItem->name, name, sizeof(newItem->name)); newItem->name[MAXMODULELABELLENGTH - 1] = 0; + newItem->id = sttHookId++; + hooks.insert(newItem); + return (HANDLE)newItem; +} + +MIR_CORE_DLL(int) DestroyHookableEvent(HANDLE hEvent) +{ + if (hEvent == nullptr) + return 1; + + mir_cslock lck(csHooks); + + int idx; + if ((idx = hooks.getIndex((THook*)hEvent)) == -1) + return 1; + + THook *p = hooks[idx]; + p->secretSignature = 0; + if (p->subscriberCount) { + mir_free(p->subscriber); + p->subscriber = nullptr; + p->subscriberCount = 0; + } + hooks.remove(idx); + delete p; + return 0; +} + +MIR_CORE_DLL(int) SetHookDefaultForHookableEvent(HANDLE hEvent, MIRANDAHOOK pfnHook) +{ + THook *p = (THook*)hEvent; + + mir_cslock lck(csHooks); + if (hooks.getIndex(p) != -1) + p->pfnHook = pfnHook; + return 0; +} + +MIR_CORE_DLL(int) CallPluginEventHook(HINSTANCE hInst, const char *pszEvent, WPARAM wParam, LPARAM lParam) +{ + int idx; + if ((idx = hooks.getIndex((THook *)pszEvent)) == -1) + return -1; + + THook *p = hooks[idx]; + if (p == nullptr || hInst == nullptr) + return -1; + + mir_cslock lck(p->csHook); + for (int i = 0; i < p->subscriberCount; i++) { + THookSubscriber* s = &p->subscriber[i]; + if (s->hOwner != hInst) + continue; + + int returnVal; + switch (s->type) { + case 1: returnVal = s->pfnHook(wParam, lParam); break; + case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; + case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; + case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; + case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; + default: continue; + } + if (returnVal) + return returnVal; + } + + if (p->subscriberCount == 0 && p->pfnHook != nullptr) + return p->pfnHook(wParam, lParam); + + return 0; +} + +MIR_CORE_DLL(int) CallObjectEventHook(void *pObject, HANDLE hEvent, WPARAM wParam, LPARAM lParam) +{ + THook *p = (THook*)hEvent; + if (p == nullptr || pObject == nullptr) + return -1; + + mir_cslock lck(p->csHook); + for (int i = 0; i < p->subscriberCount; i++) { + THookSubscriber* s = &p->subscriber[i]; + if (s->object != pObject) + continue; + + int returnVal; + switch (s->type) { + case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; + case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; + default: continue; + } + if (returnVal) + return returnVal; + } + + if (p->subscriberCount == 0 && p->pfnHook != nullptr) + return p->pfnHook(wParam, lParam); + + return 0; +} + +static int CallHookSubscribers(THook *p, WPARAM wParam, LPARAM lParam) +{ + if (p == nullptr) + return -1; + + mir_cslock lck(p->csHook); + + // NOTE: We've got the critical section while all this lot are called. That's mostly safe, though. + for (int i = 0; i < p->subscriberCount; i++) { + THookSubscriber* s = &p->subscriber[i]; + + int returnVal; + switch (s->type) { + case 1: returnVal = s->pfnHook(wParam, lParam); break; + case 2: returnVal = s->pfnHookParam(wParam, lParam, s->lParam); break; + case 3: returnVal = s->pfnHookObj(s->object, wParam, lParam); break; + case 4: returnVal = s->pfnHookObjParam(s->object, wParam, lParam, s->lParam); break; + case 5: returnVal = SendMessage(s->hwnd, s->message, wParam, lParam); break; + default: continue; + } + if (returnVal) + return returnVal; + } + + // call the default hook if any + if (p->pfnHook != nullptr) + return p->pfnHook(wParam, lParam); + + return 0; +} + +enum { hookOk, hookEmpty, hookInvalid }; + +int checkHook(THook *p) +{ + if (p == nullptr) + return hookInvalid; + + int ret; + __try { + if (p->secretSignature != HOOK_SECRET_SIGNATURE) + ret = hookInvalid; + else if (p->subscriberCount == 0 && p->pfnHook == nullptr) + ret = hookEmpty; + else + ret = hookOk; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + ret = hookInvalid; + } + + return ret; +} + +static void CALLBACK HookToMainAPCFunc(ULONG_PTR dwParam) +{ + THookToMainThreadItem* item = (THookToMainThreadItem*)dwParam; + item->result = CallHookSubscribers(item->hook, item->wParam, item->lParam); + SetEvent(item->hDoneEvent); +} + +MIR_CORE_DLL(int) NotifyEventHooks(HANDLE hEvent, WPARAM wParam, LPARAM lParam) +{ + switch (checkHook((THook*)hEvent)) { + case hookInvalid: return -1; + case hookEmpty: return 0; + } + + if (GetCurrentThreadId() == mainThreadId) + return CallHookSubscribers((THook*)hEvent, wParam, lParam); + + THookToMainThreadItem item; + item.hDoneEvent = getThreadEvent(); + item.hook = (THook*)hEvent; + item.wParam = wParam; + item.lParam = lParam; + QueueMainThread(HookToMainAPCFunc, &item, item.hDoneEvent); + return item.result; +} + +MIR_CORE_DLL(int) NotifyFastHook(HANDLE hEvent, WPARAM wParam, LPARAM lParam) +{ + switch (checkHook((THook*)hEvent)) { + case hookInvalid: return -1; + case hookEmpty: return 0; + } + + return CallHookSubscribers((THook*)hEvent, wParam, lParam); +} + +extern "C" MIR_CORE_DLL(int) GetSubscribersCount(THook* pHook) +{ + switch (checkHook(pHook)) { + case hookInvalid: + case hookEmpty: return 0; + } + return pHook->subscriberCount; +} + +static HANDLE HookEventInt(int type, const char *name, MIRANDAHOOK hookProc, void* object, LPARAM lParam) +{ + mir_cslock lck(csHooks); + + int idx; + if ((idx = hooks.getIndex((THook*)name)) == -1) + return nullptr; + + THook *p = hooks[idx]; + p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); + + THookSubscriber &s = p->subscriber[p->subscriberCount]; + s.type = type; + s.pfnHook = hookProc; + s.object = object; + s.lParam = lParam; + s.hOwner = GetInstByAddress(hookProc); + p->subscriberCount++; + + return (HANDLE)((p->id << 16) | p->subscriberCount); +} + +MIR_CORE_DLL(HANDLE) HookEvent(const char *name, MIRANDAHOOK hookProc) +{ + return HookEventInt(1, name, hookProc, nullptr, 0); +} + +MIR_CORE_DLL(HANDLE) HookEventParam(const char *name, MIRANDAHOOKPARAM hookProc, LPARAM lParam) +{ + return HookEventInt(2, name, (MIRANDAHOOK)hookProc, nullptr, lParam); +} + +MIR_CORE_DLL(HANDLE) HookEventObj(const char *name, MIRANDAHOOKOBJ hookProc, void* object) +{ + return HookEventInt(3, name, (MIRANDAHOOK)hookProc, object, 0); +} + +MIR_CORE_DLL(HANDLE) HookEventObjParam(const char *name, MIRANDAHOOKOBJPARAM hookProc, void* object, LPARAM lParam) +{ + return HookEventInt(4, name, (MIRANDAHOOK)hookProc, object, lParam); +} + +MIR_CORE_DLL(HANDLE) HookTemporaryEvent(const char *name, MIRANDAHOOK hookProc) +{ + mir_cslockfull lck(csHooks); + + int idx; + if ((idx = hooks.getIndex((THook*)name)) == -1) { + lck.unlock(); + hookProc(0, 0); + return nullptr; + } + + THook *p = hooks[idx]; + p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); + + THookSubscriber &s = p->subscriber[p->subscriberCount]; + memset(&s, 0, sizeof(THookSubscriber)); + s.type = 1; + s.pfnHook = hookProc; + s.hOwner = GetInstByAddress(hookProc); + + p->subscriberCount++; + return (HANDLE)((p->id << 16) | p->subscriberCount); +} + +MIR_CORE_DLL(HANDLE) HookEventMessage(const char *name, HWND hwnd, UINT message) +{ + mir_cslock lck(csHooks); + + int idx; + if ((idx = hooks.getIndex((THook*)name)) == -1) + return nullptr; + + THook *p = hooks[idx]; + p->subscriber = (THookSubscriber*)mir_realloc(p->subscriber, sizeof(THookSubscriber)*(p->subscriberCount + 1)); + p->subscriber[p->subscriberCount].type = 5; + p->subscriber[p->subscriberCount].hwnd = hwnd; + p->subscriber[p->subscriberCount].message = message; + p->subscriberCount++; + return (HANDLE)((p->id << 16) | p->subscriberCount); +} + +MIR_CORE_DLL(int) UnhookEvent(HANDLE hHook) +{ + if (hHook == nullptr) + return 0; + + int hookId = (INT_PTR)hHook >> 16; + int subscriberId = ((INT_PTR)hHook & 0xFFFF) - 1; + + mir_cslock lck(csHooks); + + THook *p = nullptr; + for (auto &it : hooks) + if (it->id == hookId) { + p = it; + break; + } + + if (p == nullptr) + return 1; + + if (subscriberId >= p->subscriberCount || subscriberId < 0) + return 1; + + p->subscriber[subscriberId].type = 0; + p->subscriber[subscriberId].pfnHook = nullptr; + p->subscriber[subscriberId].hOwner = nullptr; + while (p->subscriberCount && p->subscriber[p->subscriberCount - 1].type == 0) + p->subscriberCount--; + if (p->subscriberCount == 0) { + mir_free(p->subscriber); + p->subscriber = nullptr; + } + return 0; +} + +MIR_CORE_DLL(void) KillModuleEventHooks(HINSTANCE hInst) +{ + mir_cslock lck(csHooks); + + for (auto &it : hooks.rev_iter()) { + if (it->subscriberCount == 0) + continue; + + for (int j = it->subscriberCount - 1; j >= 0; j--) { + if (it->subscriber[j].hOwner != hInst) + continue; + + char szModuleName[MAX_PATH]; + GetModuleFileNameA(it->subscriber[j].hOwner, szModuleName, sizeof(szModuleName)); + UnhookEvent((HANDLE)((it->id << 16) + j + 1)); + if (it->subscriberCount == 0) + break; + } + } +} + +MIR_CORE_DLL(void) KillObjectEventHooks(void* pObject) +{ + mir_cslock lck(csHooks); + + for (auto &it : hooks.rev_iter()) { + if (it->subscriberCount == 0) + continue; + + for (int j = it->subscriberCount - 1; j >= 0; j--) { + if (it->subscriber[j].object == pObject) { + UnhookEvent((HANDLE)((it->id << 16) + j + 1)); + if (it->subscriberCount == 0) + break; + } + } + } +} + +static void DestroyHooks() +{ + mir_cslock lck(csHooks); + + for (auto &it : hooks) { + if (it->subscriberCount) + mir_free(it->subscriber); + delete it; + } +} + +/////////////////////SERVICES + +static __inline TService* FindServiceByName(const char *name) +{ + unsigned hash = mir_hashstr(name); + return services.find((TService*)&hash); +} + +static HANDLE CreateServiceInt(int type, const char *name, MIRANDASERVICE serviceProc, void* object, LPARAM lParam) +{ + if (name == nullptr) + return nullptr; + + TService tmp; + tmp.nameHash = mir_hashstr(name); + + mir_cslock lck(csServices); + + if (services.getIndex(&tmp) != -1) + return nullptr; + + TService* p = (TService*)mir_alloc(sizeof(*p) + strlen(name)); + strcpy(p->name, name); + p->nameHash = tmp.nameHash; + p->pfnService = serviceProc; + p->hOwner = GetInstByAddress(serviceProc); + p->flags = type; + p->lParam = lParam; + p->object = object; + services.insert(p); + + return (HANDLE)tmp.nameHash; +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunction(const char *name, MIRANDASERVICE serviceProc) +{ + return CreateServiceInt(0, name, serviceProc, nullptr, 0); +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunctionParam(const char *name, MIRANDASERVICEPARAM serviceProc, LPARAM lParam) +{ + return CreateServiceInt(1, name, (MIRANDASERVICE)serviceProc, nullptr, lParam); +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunctionObj(const char *name, MIRANDASERVICEOBJ serviceProc, void* object) +{ + return CreateServiceInt(2, name, (MIRANDASERVICE)serviceProc, object, 0); +} + +MIR_CORE_DLL(HANDLE) CreateServiceFunctionObjParam(const char *name, MIRANDASERVICEOBJPARAM serviceProc, void* object, LPARAM lParam) +{ + return CreateServiceInt(3, name, (MIRANDASERVICE)serviceProc, object, lParam); +} + +MIR_CORE_DLL(HANDLE) CreateProtoServiceFunction(const char *szModule, const char *szService, MIRANDASERVICE serviceProc) +{ + char str[MAXMODULELABELLENGTH * 2]; + strncpy_s(str, szModule, _TRUNCATE); + strncat_s(str, szService, _TRUNCATE); + return CreateServiceFunction(str, serviceProc); +} + +MIR_CORE_DLL(int) DestroyServiceFunction(HANDLE hService) +{ + mir_cslock lck(csServices); + + int idx = services.getIndex((TService*)&hService); + if (idx != -1) { + mir_free(services[idx]); + services.remove(idx); + } + + return 0; +} + +MIR_CORE_DLL(bool) ServiceExists(const char *name) +{ + if (name == nullptr) + return FALSE; + + mir_cslock lck(csServices); + return FindServiceByName(name) != nullptr; +} + +MIR_CORE_DLL(INT_PTR) CallService(const char *name, WPARAM wParam, LPARAM lParam) +{ + if (name == nullptr) + return CALLSERVICE_NOTFOUND; + + TService *pService; + { + mir_cslock lck(csServices); + if ((pService = FindServiceByName(name)) == nullptr) + return CALLSERVICE_NOTFOUND; + } + + MIRANDASERVICE pfnService = pService->pfnService; + int flags = pService->flags; + LPARAM fnParam = pService->lParam; + void* object = pService->object; + switch (flags) { + case 1: return ((MIRANDASERVICEPARAM)pfnService)(wParam, lParam, fnParam); + case 2: return ((MIRANDASERVICEOBJ)pfnService)(object, wParam, lParam); + case 3: return ((MIRANDASERVICEOBJPARAM)pfnService)(object, wParam, lParam, fnParam); + default: return pfnService(wParam, lParam); + } +} + +static void CALLBACK CallServiceToMainAPCFunc(ULONG_PTR dwParam) +{ + TServiceToMainThreadItem *item = (TServiceToMainThreadItem*)dwParam; + item->result = CallService(item->name, item->wParam, item->lParam); + SetEvent(item->hDoneEvent); +} + +MIR_CORE_DLL(INT_PTR) CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam) +{ + if (name == nullptr) + return CALLSERVICE_NOTFOUND; + + // the service is looked up within the main thread, since the time it takes + // for the APC queue to clear the service being called maybe removed. + // even thou it may exists before the call, the critsec can't be locked between calls. + if (GetCurrentThreadId() == mainThreadId) + return CallService(name, wParam, lParam); + + TServiceToMainThreadItem item; + item.wParam = wParam; + item.lParam = lParam; + item.name = name; + item.hDoneEvent = getThreadEvent(); + QueueMainThread(CallServiceToMainAPCFunc, &item, item.hDoneEvent); + return item.result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) CallFunctionAsync(void(__stdcall *func)(void *), void *arg) +{ + if (GetCurrentThreadId() == mainThreadId) + func(arg); + else + QueueMainThread((PAPCFUNC)func, arg, nullptr); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct TSyncCallParam +{ + INT_PTR(__stdcall *func)(void *); + void *arg; + HANDLE hDoneEvent; + INT_PTR result; +}; + +static void CALLBACK CallFuncToMainAPCFunc(ULONG_PTR dwParam) +{ + TSyncCallParam *item = (TSyncCallParam*)dwParam; + item->result = (*item->func)(item->arg); + SetEvent(item->hDoneEvent); +} + +MIR_CORE_DLL(INT_PTR) CallFunctionSync(INT_PTR(__stdcall *func)(void *), void *arg) +{ + if (GetCurrentThreadId() == mainThreadId) + return func(arg); + + TSyncCallParam param = { func, arg, getThreadEvent() }; + QueueMainThread(CallFuncToMainAPCFunc, ¶m, param.hDoneEvent); + return param.result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(void) KillModuleServices(HINSTANCE hInst) +{ + mir_cslock lck(csServices); + + for (auto &it : services.rev_iter()) { + if (it->hOwner == hInst) { + char szModuleName[MAX_PATH]; + GetModuleFileNameA(it->hOwner, szModuleName, sizeof(szModuleName)); + DestroyServiceFunction((HANDLE)it->nameHash); + } + } +} + +MIR_CORE_DLL(void) KillObjectServices(void* pObject) +{ + mir_cslock lck(csServices); + + for (auto &it : services.rev_iter()) + if (it->object == pObject) + DestroyServiceFunction((HANDLE)it->nameHash); +} + +static void DestroyServices() +{ + mir_cslock lck(csServices); + + for (auto &it : services) + mir_free(it); +} + +/////////////////////////////////////////////////////////////////////////////// + +int InitialiseModularEngine(void) +{ + mainThreadId = GetCurrentThreadId(); + return 0; +} + +void DestroyModularEngine(void) +{ + DestroyHooks(); + DestroyServices(); +} diff --git a/src/mir_core/src/mstring.cpp b/src/mir_core/src/mstring.cpp index 2bff18e7f1..04a26f8a9d 100644 --- a/src/mir_core/src/mstring.cpp +++ b/src/mir_core/src/mstring.cpp @@ -1,145 +1,145 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// CMBaseString - -class CNilMStringData : public CMStringData -{ -public: - CNilMStringData(); - -public: - wchar_t achNil[2]; -}; - -CNilMStringData::CNilMStringData() -{ - nRefs = 2; // Never gets freed - nDataLength = 0; - nAllocLength = 0; - achNil[0] = 0; - achNil[1] = 0; -} - -static CNilMStringData *m_nil = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// CMBaseString - -MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize) -{ - nChars++; // nil char - size_t nDataBytes = nCharSize * nChars; - size_t nTotalSize = nDataBytes + sizeof(CMStringData); - - CMStringData *pData = static_cast(malloc(nTotalSize)); - if (pData == nullptr) - return nullptr; - - pData->nRefs = 1; - pData->nAllocLength = nChars - 1; - pData->nDataLength = 0; - return pData; -} - -MIR_CORE_DLL(void) mirstr_free(CMStringData *pData) -{ - free(pData); -} - -MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize) -{ - nChars++; // nil char - uint32_t nDataBytes = nCharSize * nChars; - uint32_t nTotalSize = nDataBytes + sizeof(CMStringData); - - CMStringData *pNewData = static_cast(realloc(pData, nTotalSize)); - if (pNewData == nullptr) - return nullptr; - - pNewData->nAllocLength = nChars - 1; - return pNewData; -} - -MIR_CORE_DLL(CMStringData*) mirstr_getNil() -{ - if (m_nil == nullptr) - m_nil = new CNilMStringData(); - m_nil->AddRef(); - return m_nil; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// CMStringData - -MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis) -{ - pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary - if (pThis->nRefs == 0) - pThis->nRefs = -1; -} - -MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis) -{ - if (InterlockedDecrement(&pThis->nRefs) <= 0) - mirstr_free(pThis); -} - -MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis) -{ - if (pThis->IsLocked()) - { - pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary - if (pThis->nRefs == 0) - pThis->nRefs = 1; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// -// don't remove it -// this code just instantiates templates for CMStringW[A/W] - -#ifdef _MSC_VER -template MIR_CORE_EXPORT CMStringW; -template MIR_CORE_EXPORT CMStringA; -#endif - -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const wchar_t *psz2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const wchar_t *psz1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, wchar_t ch2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, char ch2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(wchar_t ch1, const CMStringW& str2); -template MIR_CORE_EXPORT CMStringW CALLBACK operator+(char ch1, const CMStringW& str2); - -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const char *psz2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const char *psz1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, wchar_t ch2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, char ch2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(wchar_t ch1, const CMStringA& str2); -template MIR_CORE_EXPORT CMStringA CALLBACK operator+(char ch1, const CMStringA& str2); +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// CMBaseString + +class CNilMStringData : public CMStringData +{ +public: + CNilMStringData(); + +public: + wchar_t achNil[2]; +}; + +CNilMStringData::CNilMStringData() +{ + nRefs = 2; // Never gets freed + nDataLength = 0; + nAllocLength = 0; + achNil[0] = 0; + achNil[1] = 0; +} + +static CNilMStringData *m_nil = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// +// CMBaseString + +MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize) +{ + nChars++; // nil char + size_t nDataBytes = nCharSize * nChars; + size_t nTotalSize = nDataBytes + sizeof(CMStringData); + + CMStringData *pData = static_cast(malloc(nTotalSize)); + if (pData == nullptr) + return nullptr; + + pData->nRefs = 1; + pData->nAllocLength = nChars - 1; + pData->nDataLength = 0; + return pData; +} + +MIR_CORE_DLL(void) mirstr_free(CMStringData *pData) +{ + free(pData); +} + +MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize) +{ + nChars++; // nil char + uint32_t nDataBytes = nCharSize * nChars; + uint32_t nTotalSize = nDataBytes + sizeof(CMStringData); + + CMStringData *pNewData = static_cast(realloc(pData, nTotalSize)); + if (pNewData == nullptr) + return nullptr; + + pNewData->nAllocLength = nChars - 1; + return pNewData; +} + +MIR_CORE_DLL(CMStringData*) mirstr_getNil() +{ + if (m_nil == nullptr) + m_nil = new CNilMStringData(); + m_nil->AddRef(); + return m_nil; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// CMStringData + +MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis) +{ + pThis->nRefs--; // Locked buffers can't be shared, so no interlocked operation necessary + if (pThis->nRefs == 0) + pThis->nRefs = -1; +} + +MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis) +{ + if (InterlockedDecrement(&pThis->nRefs) <= 0) + mirstr_free(pThis); +} + +MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis) +{ + if (pThis->IsLocked()) + { + pThis->nRefs++; // Locked buffers can't be shared, so no interlocked operation necessary + if (pThis->nRefs == 0) + pThis->nRefs = 1; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// don't remove it +// this code just instantiates templates for CMStringW[A/W] + +#ifdef _MSC_VER +template MIR_CORE_EXPORT CMStringW; +template MIR_CORE_EXPORT CMStringA; +#endif + +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const CMStringW& str2); +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, const wchar_t *psz2); +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const wchar_t *psz1, const CMStringW& str2); +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, wchar_t ch2); +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(const CMStringW& str1, char ch2); +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(wchar_t ch1, const CMStringW& str2); +template MIR_CORE_EXPORT CMStringW CALLBACK operator+(char ch1, const CMStringW& str2); + +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const CMStringA& str2); +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, const char *psz2); +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const char *psz1, const CMStringA& str2); +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, wchar_t ch2); +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(const CMStringA& str1, char ch2); +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(wchar_t ch1, const CMStringA& str2); +template MIR_CORE_EXPORT CMStringA CALLBACK operator+(char ch1, const CMStringA& str2); diff --git a/src/mir_core/src/stdafx.cxx b/src/mir_core/src/stdafx.cxx index 564f422ca2..8c570f6949 100644 --- a/src/mir_core/src/stdafx.cxx +++ b/src/mir_core/src/stdafx.cxx @@ -1,5 +1,5 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/mir_core/src/stdafx.h b/src/mir_core/src/stdafx.h index 54ec535a4c..dd6d93d679 100644 --- a/src/mir_core/src/stdafx.h +++ b/src/mir_core/src/stdafx.h @@ -1,84 +1,84 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#pragma once - -#define INCL_WINSOCK_API_TYPEDEFS 1 - -#ifdef _MSC_VER - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - - #ifdef _DEBUG - #include - #endif -#else - #include -#endif // _WINDOWS - -#include -#include -#include -#include -#include -#include -#include -#include - -#define __NO_CMPLUGIN_NEEDED -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "miranda.h" - -#include - -#include - -void GetDefaultLang(); +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#pragma once + +#define INCL_WINSOCK_API_TYPEDEFS 1 + +#ifdef _MSC_VER + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #ifdef _DEBUG + #include + #endif +#else + #include +#endif // _WINDOWS + +#include +#include +#include +#include +#include +#include +#include +#include + +#define __NO_CMPLUGIN_NEEDED +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "miranda.h" + +#include + +#include + +void GetDefaultLang(); diff --git a/src/mir_core/src/utf.cpp b/src/mir_core/src/utf.cpp index 1e17ed60e8..eefa519727 100644 --- a/src/mir_core/src/utf.cpp +++ b/src/mir_core/src/utf.cpp @@ -1,441 +1,441 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - - Copyright 2000 Alexandre Julliard of Wine project - (UTF-8 conversion routines) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */ -static const char utf8_length[128] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */ - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0-0xcf */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0-0xdf */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xe0-0xef */ - 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0-0xff */ -}; - -/* first byte mask depending on UTF-8 sequence length */ -static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 }; - -/* minimum Unicode value depending on UTF-8 sequence length */ -static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 }; - -/* get the next char value taking surrogates into account */ -static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen) -{ - if (src[0] >= 0xd800 && src[0] <= 0xdfff) { /* surrogate pair */ - if (src[0] > 0xdbff || /* invalid high surrogate */ - srclen <= 1 || /* missing low surrogate */ - src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */ - return 0; - return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff); - } - return src[0]; -} - -/* query necessary dst length for src string */ -static int mir_utf8len(const wchar_t *src, unsigned int srclen) -{ - int len; - unsigned int val; - - for (len = 0; srclen; srclen--, src++) { - if (*src < 0x80) { /* 0x00-0x7f: 1 byte */ - len++; - continue; - } - if (*src < 0x800) { /* 0x80-0x7ff: 2 bytes */ - len += 2; - continue; - } - if (!(val = getSurrogateValue(src, srclen))) - return -2; - - if (val < 0x10000) /* 0x800-0xffff: 3 bytes */ - len += 3; - else { /* 0x10000-0x10ffff: 4 bytes */ - len += 4; - src++; - srclen--; - } - } - return len; -} - -MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src) -{ - if (src == nullptr) - return 0; - - return mir_utf8len(src, (int)wcslen(src)); -} - -/* wide char to UTF-8 string conversion */ -/* return -1 on dst buffer overflow, -2 on invalid input char */ -int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen) -{ - int len; - - for (len = dstlen; srclen; srclen--, src++) { - wchar_t ch = *src; - unsigned int val; - - if (ch < 0x80) { /* 0x00-0x7f: 1 byte */ - if (!len--) return -1; /* overflow */ - *dst++ = ch; - continue; - } - - if (ch < 0x800) { /* 0x80-0x7ff: 2 bytes */ - if ((len -= 2) < 0) return -1; /* overflow */ - dst[1] = 0x80 | (ch & 0x3f); - ch >>= 6; - dst[0] = 0xc0 | ch; - dst += 2; - continue; - } - - if (!(val = getSurrogateValue(src, srclen))) - return -2; - - if (val < 0x10000) { /* 0x800-0xffff: 3 bytes */ - if ((len -= 3) < 0) return -1; /* overflow */ - dst[2] = 0x80 | (val & 0x3f); - val >>= 6; - dst[1] = 0x80 | (val & 0x3f); - val >>= 6; - dst[0] = 0xe0 | val; - dst += 3; - } - else { /* 0x10000-0x10ffff: 4 bytes */ - if ((len -= 4) < 0) return -1; /* overflow */ - dst[3] = 0x80 | (val & 0x3f); - val >>= 6; - dst[2] = 0x80 | (val & 0x3f); - val >>= 6; - dst[1] = 0x80 | (val & 0x3f); - val >>= 6; - dst[0] = 0xf0 | val; - dst += 4; - src++; - srclen--; - } - } - return dstlen - len; -} - -/* helper for the various utf8 mbstowcs functions */ -static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend) -{ - unsigned int len = utf8_length[ch - 0x80]; - unsigned int res = ch & utf8_mask[len]; - const char *end = *str + len; - - if (end > strend) return ~0; - switch (len) { - case 3: - if ((ch = end[-3] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - - case 2: - if ((ch = end[-2] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - - case 1: - if ((ch = end[-1] ^ 0x80) >= 0x40) break; - res = (res << 6) | ch; - (*str)++; - if (res < utf8_minval[len]) break; - return res; - } - return ~0; -} - -/* query necessary dst length for src string */ -static int Utf8toUcs2Len(const char *src, size_t srclen) -{ - int ret = 0; - unsigned int res; - const char *srcend = src + srclen; - - while (src < srcend) { - unsigned char ch = *src++; - if (ch < 0x80) { /* special fast case for 7-bit ASCII */ - ret++; - continue; - } - if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff) { - if (res > 0xffff) ret++; - ret++; - } - else return -2; /* bad char */ - /* otherwise ignore it */ - } - return ret; -} - -/* UTF-8 to wide char string conversion */ -/* return -1 on dst buffer overflow, -2 on invalid input char */ -MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen) -{ - unsigned int res; - const char *srcend = src + srclen; // including trailing zero - wchar_t *dstend = dst + dstlen; - - while ((dst < dstend) && (src < srcend)) { - unsigned char ch = *src++; - if (ch < 0x80) { /* special fast case for 7-bit ASCII */ - *dst++ = ch; - continue; - } - - if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff) - *dst++ = res; - else if (res <= 0x10ffff) { /* we need surrogates */ - if (dst == dstend - 1) - return -1; /* overflow */ - res -= 0x10000; - *dst++ = 0xd800 | (res >> 10); - *dst++ = 0xdc00 | (res & 0x3ff); - } - else return -2; /* bad char */ - } - - if (src < srcend) - return -1; /* overflow */ - - return (int)(dstlen - (dstend - dst)); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8decode - converts UTF8-encoded string to the UCS2/MBCS format - -#ifdef _MSC_VER -MIR_CORE_DLL(char*) mir_utf8decodecp(char *str, int codepage, wchar_t **ucs2) -{ - bool needs_free = false; - wchar_t* tempBuf = nullptr; - if (ucs2) - *ucs2 = nullptr; - - if (str == nullptr) - return nullptr; - - size_t len = strlen(str); - if (len < 2) { - if (ucs2 != nullptr) { - *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); - MultiByteToWideChar(codepage, 0, str, (int)len, tempBuf, (int)len); - tempBuf[len] = 0; - } - return str; - } - - int destlen = Utf8toUcs2Len(str, len); - if (destlen < 0) - return nullptr; - - if (ucs2 == nullptr) { - __try { - tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - tempBuf = nullptr; - needs_free = true; - } - } - - if (tempBuf == nullptr) { - tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); - if (tempBuf == nullptr) - return nullptr; - } - - Utf8toUcs2(str, len, tempBuf, destlen); - tempBuf[destlen] = 0; - WideCharToMultiByte(codepage, 0, tempBuf, -1, str, (int)len + 1, "?", nullptr); - - if (ucs2) - *ucs2 = tempBuf; - else if (needs_free) - mir_free(tempBuf); - - return str; -} - -MIR_CORE_DLL(char*) mir_utf8decode(char *str, wchar_t **ucs2) -{ - return mir_utf8decodecp(str, Langpack_GetDefaultCodePage(), ucs2); -} -#endif - -MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char *str) -{ - if (str == nullptr) - return nullptr; - - size_t len = strlen(str); - - int destlen = Utf8toUcs2Len(str, len); - if (destlen < 0) - return nullptr; - - wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); - if (ucs2 == nullptr) - return nullptr; - - if (Utf8toUcs2(str, len, ucs2, destlen) >= 0) { - ucs2[destlen] = 0; - return ucs2; - } - - mir_free(ucs2); - - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8encode - converts MBCS string to the UTF8-encoded format - -#ifdef _MSC_VER -MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage) -{ - int len; - bool needs_free = false; - char* result = nullptr; - wchar_t* tempBuf; - - if (src == nullptr) - return nullptr; - - len = (int)strlen(src); - - __try { - tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t)); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); - if (tempBuf == nullptr) return nullptr; - needs_free = true; - } - - len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1); - - int destlen = mir_utf8len(tempBuf, len); - if (destlen >= 0) { - result = (char*)mir_alloc(destlen + 1); - if (result) { - Ucs2toUtf8(tempBuf, len, result, destlen); - result[destlen] = 0; - } - } - - if (needs_free) - mir_free(tempBuf); - - return result; -} - -MIR_CORE_DLL(char*) mir_utf8encode(const char* src) -{ - return mir_utf8encodecp(src, Langpack_GetDefaultCodePage()); -} -#endif - -///////////////////////////////////////////////////////////////////////////////////////// -// mir_utf8encode - converts UCS2 string to the UTF8-encoded format - -MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* src) -{ - if (src == nullptr) - return nullptr; - - int len = (int)wcslen(src); - - int destlen = mir_utf8len(src, len); - if (destlen < 0) return nullptr; - - char* result = (char*)mir_alloc(destlen + 1); - if (result == nullptr) - return nullptr; - - Ucs2toUtf8(src, len, result, destlen); - result[destlen] = 0; - - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Utf8CheckString - checks if a string is a valid utf8-encoded string - -MIR_CORE_DLL(BOOL) Utf8CheckString(const char *str) -{ - int expect_bytes = 0, utf_found = 0; - - if (!str) return 0; - - while (*str) { - if ((*str & 0x80) == 0) { - /* Looks like an ASCII character */ - if (expect_bytes) - /* byte of UTF-8 character expected */ - return 0; - } - else { - /* Looks like byte of an UTF-8 character */ - if (expect_bytes) { - /* expect_bytes already set: first byte of UTF-8 char already seen */ - if ((*str & 0xC0) != 0x80) { - /* again first byte ?!?! */ - return 0; - } - } - else { - /* First byte of the UTF-8 character */ - /* count initial one bits and set expect_bytes to 1 less */ - char ch = *str; - while (ch & 0x80) { - expect_bytes++; - ch = (ch & 0x7f) << 1; - } - } - /* OK, next byte of UTF-8 character */ - /* Decrement number of expected bytes */ - if (--expect_bytes == 0) - utf_found = 1; - } - str++; - } - - return (utf_found && expect_bytes == 0); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + + Copyright 2000 Alexandre Julliard of Wine project + (UTF-8 conversion routines) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +/* number of following bytes in sequence based on first byte value (for bytes above 0x7f) */ +static const char utf8_length[128] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80-0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90-0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0-0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xbf */ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xc0-0xcf */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xd0-0xdf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xe0-0xef */ + 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0-0xff */ +}; + +/* first byte mask depending on UTF-8 sequence length */ +static const unsigned char utf8_mask[4] = { 0x7f, 0x1f, 0x0f, 0x07 }; + +/* minimum Unicode value depending on UTF-8 sequence length */ +static const unsigned int utf8_minval[4] = { 0x0, 0x80, 0x800, 0x10000 }; + +/* get the next char value taking surrogates into account */ +static unsigned int getSurrogateValue(const wchar_t *src, unsigned int srclen) +{ + if (src[0] >= 0xd800 && src[0] <= 0xdfff) { /* surrogate pair */ + if (src[0] > 0xdbff || /* invalid high surrogate */ + srclen <= 1 || /* missing low surrogate */ + src[1] < 0xdc00 || src[1] > 0xdfff) /* invalid low surrogate */ + return 0; + return 0x10000 + ((src[0] & 0x3ff) << 10) + (src[1] & 0x3ff); + } + return src[0]; +} + +/* query necessary dst length for src string */ +static int mir_utf8len(const wchar_t *src, unsigned int srclen) +{ + int len; + unsigned int val; + + for (len = 0; srclen; srclen--, src++) { + if (*src < 0x80) { /* 0x00-0x7f: 1 byte */ + len++; + continue; + } + if (*src < 0x800) { /* 0x80-0x7ff: 2 bytes */ + len += 2; + continue; + } + if (!(val = getSurrogateValue(src, srclen))) + return -2; + + if (val < 0x10000) /* 0x800-0xffff: 3 bytes */ + len += 3; + else { /* 0x10000-0x10ffff: 4 bytes */ + len += 4; + src++; + srclen--; + } + } + return len; +} + +MIR_CORE_DLL(int) mir_utf8lenW(const wchar_t *src) +{ + if (src == nullptr) + return 0; + + return mir_utf8len(src, (int)wcslen(src)); +} + +/* wide char to UTF-8 string conversion */ +/* return -1 on dst buffer overflow, -2 on invalid input char */ +int Ucs2toUtf8(const wchar_t *src, int srclen, char *dst, int dstlen) +{ + int len; + + for (len = dstlen; srclen; srclen--, src++) { + wchar_t ch = *src; + unsigned int val; + + if (ch < 0x80) { /* 0x00-0x7f: 1 byte */ + if (!len--) return -1; /* overflow */ + *dst++ = ch; + continue; + } + + if (ch < 0x800) { /* 0x80-0x7ff: 2 bytes */ + if ((len -= 2) < 0) return -1; /* overflow */ + dst[1] = 0x80 | (ch & 0x3f); + ch >>= 6; + dst[0] = 0xc0 | ch; + dst += 2; + continue; + } + + if (!(val = getSurrogateValue(src, srclen))) + return -2; + + if (val < 0x10000) { /* 0x800-0xffff: 3 bytes */ + if ((len -= 3) < 0) return -1; /* overflow */ + dst[2] = 0x80 | (val & 0x3f); + val >>= 6; + dst[1] = 0x80 | (val & 0x3f); + val >>= 6; + dst[0] = 0xe0 | val; + dst += 3; + } + else { /* 0x10000-0x10ffff: 4 bytes */ + if ((len -= 4) < 0) return -1; /* overflow */ + dst[3] = 0x80 | (val & 0x3f); + val >>= 6; + dst[2] = 0x80 | (val & 0x3f); + val >>= 6; + dst[1] = 0x80 | (val & 0x3f); + val >>= 6; + dst[0] = 0xf0 | val; + dst += 4; + src++; + srclen--; + } + } + return dstlen - len; +} + +/* helper for the various utf8 mbstowcs functions */ +static unsigned int decodeUtf8Char(unsigned char ch, const char **str, const char *strend) +{ + unsigned int len = utf8_length[ch - 0x80]; + unsigned int res = ch & utf8_mask[len]; + const char *end = *str + len; + + if (end > strend) return ~0; + switch (len) { + case 3: + if ((ch = end[-3] ^ 0x80) >= 0x40) break; + res = (res << 6) | ch; + (*str)++; + + case 2: + if ((ch = end[-2] ^ 0x80) >= 0x40) break; + res = (res << 6) | ch; + (*str)++; + + case 1: + if ((ch = end[-1] ^ 0x80) >= 0x40) break; + res = (res << 6) | ch; + (*str)++; + if (res < utf8_minval[len]) break; + return res; + } + return ~0; +} + +/* query necessary dst length for src string */ +static int Utf8toUcs2Len(const char *src, size_t srclen) +{ + int ret = 0; + unsigned int res; + const char *srcend = src + srclen; + + while (src < srcend) { + unsigned char ch = *src++; + if (ch < 0x80) { /* special fast case for 7-bit ASCII */ + ret++; + continue; + } + if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0x10ffff) { + if (res > 0xffff) ret++; + ret++; + } + else return -2; /* bad char */ + /* otherwise ignore it */ + } + return ret; +} + +/* UTF-8 to wide char string conversion */ +/* return -1 on dst buffer overflow, -2 on invalid input char */ +MIR_CORE_DLL(int) Utf8toUcs2(const char *src, size_t srclen, wchar_t *dst, size_t dstlen) +{ + unsigned int res; + const char *srcend = src + srclen; // including trailing zero + wchar_t *dstend = dst + dstlen; + + while ((dst < dstend) && (src < srcend)) { + unsigned char ch = *src++; + if (ch < 0x80) { /* special fast case for 7-bit ASCII */ + *dst++ = ch; + continue; + } + + if ((res = decodeUtf8Char(ch, &src, srcend)) <= 0xffff) + *dst++ = res; + else if (res <= 0x10ffff) { /* we need surrogates */ + if (dst == dstend - 1) + return -1; /* overflow */ + res -= 0x10000; + *dst++ = 0xd800 | (res >> 10); + *dst++ = 0xdc00 | (res & 0x3ff); + } + else return -2; /* bad char */ + } + + if (src < srcend) + return -1; /* overflow */ + + return (int)(dstlen - (dstend - dst)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// mir_utf8decode - converts UTF8-encoded string to the UCS2/MBCS format + +#ifdef _MSC_VER +MIR_CORE_DLL(char*) mir_utf8decodecp(char *str, int codepage, wchar_t **ucs2) +{ + bool needs_free = false; + wchar_t* tempBuf = nullptr; + if (ucs2) + *ucs2 = nullptr; + + if (str == nullptr) + return nullptr; + + size_t len = strlen(str); + if (len < 2) { + if (ucs2 != nullptr) { + *ucs2 = tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); + MultiByteToWideChar(codepage, 0, str, (int)len, tempBuf, (int)len); + tempBuf[len] = 0; + } + return str; + } + + int destlen = Utf8toUcs2Len(str, len); + if (destlen < 0) + return nullptr; + + if (ucs2 == nullptr) { + __try { + tempBuf = (wchar_t*)alloca((destlen + 1) * sizeof(wchar_t)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + tempBuf = nullptr; + needs_free = true; + } + } + + if (tempBuf == nullptr) { + tempBuf = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); + if (tempBuf == nullptr) + return nullptr; + } + + Utf8toUcs2(str, len, tempBuf, destlen); + tempBuf[destlen] = 0; + WideCharToMultiByte(codepage, 0, tempBuf, -1, str, (int)len + 1, "?", nullptr); + + if (ucs2) + *ucs2 = tempBuf; + else if (needs_free) + mir_free(tempBuf); + + return str; +} + +MIR_CORE_DLL(char*) mir_utf8decode(char *str, wchar_t **ucs2) +{ + return mir_utf8decodecp(str, Langpack_GetDefaultCodePage(), ucs2); +} +#endif + +MIR_CORE_DLL(wchar_t*) mir_utf8decodeW(const char *str) +{ + if (str == nullptr) + return nullptr; + + size_t len = strlen(str); + + int destlen = Utf8toUcs2Len(str, len); + if (destlen < 0) + return nullptr; + + wchar_t* ucs2 = (wchar_t*)mir_alloc((destlen + 1) * sizeof(wchar_t)); + if (ucs2 == nullptr) + return nullptr; + + if (Utf8toUcs2(str, len, ucs2, destlen) >= 0) { + ucs2[destlen] = 0; + return ucs2; + } + + mir_free(ucs2); + + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// mir_utf8encode - converts MBCS string to the UTF8-encoded format + +#ifdef _MSC_VER +MIR_CORE_DLL(char*) mir_utf8encodecp(const char* src, int codepage) +{ + int len; + bool needs_free = false; + char* result = nullptr; + wchar_t* tempBuf; + + if (src == nullptr) + return nullptr; + + len = (int)strlen(src); + + __try { + tempBuf = (wchar_t*)alloca((len + 1) * sizeof(wchar_t)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + tempBuf = (wchar_t*)mir_alloc((len + 1) * sizeof(wchar_t)); + if (tempBuf == nullptr) return nullptr; + needs_free = true; + } + + len = MultiByteToWideChar(codepage, 0, src, -1, tempBuf, len + 1); + + int destlen = mir_utf8len(tempBuf, len); + if (destlen >= 0) { + result = (char*)mir_alloc(destlen + 1); + if (result) { + Ucs2toUtf8(tempBuf, len, result, destlen); + result[destlen] = 0; + } + } + + if (needs_free) + mir_free(tempBuf); + + return result; +} + +MIR_CORE_DLL(char*) mir_utf8encode(const char* src) +{ + return mir_utf8encodecp(src, Langpack_GetDefaultCodePage()); +} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +// mir_utf8encode - converts UCS2 string to the UTF8-encoded format + +MIR_CORE_DLL(char*) mir_utf8encodeW(const wchar_t* src) +{ + if (src == nullptr) + return nullptr; + + int len = (int)wcslen(src); + + int destlen = mir_utf8len(src, len); + if (destlen < 0) return nullptr; + + char* result = (char*)mir_alloc(destlen + 1); + if (result == nullptr) + return nullptr; + + Ucs2toUtf8(src, len, result, destlen); + result[destlen] = 0; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Utf8CheckString - checks if a string is a valid utf8-encoded string + +MIR_CORE_DLL(BOOL) Utf8CheckString(const char *str) +{ + int expect_bytes = 0, utf_found = 0; + + if (!str) return 0; + + while (*str) { + if ((*str & 0x80) == 0) { + /* Looks like an ASCII character */ + if (expect_bytes) + /* byte of UTF-8 character expected */ + return 0; + } + else { + /* Looks like byte of an UTF-8 character */ + if (expect_bytes) { + /* expect_bytes already set: first byte of UTF-8 char already seen */ + if ((*str & 0xC0) != 0x80) { + /* again first byte ?!?! */ + return 0; + } + } + else { + /* First byte of the UTF-8 character */ + /* count initial one bits and set expect_bytes to 1 less */ + char ch = *str; + while (ch & 0x80) { + expect_bytes++; + ch = (ch & 0x7f) << 1; + } + } + /* OK, next byte of UTF-8 character */ + /* Decrement number of expected bytes */ + if (--expect_bytes == 0) + utf_found = 1; + } + str++; + } + + return (utf_found && expect_bytes == 0); +} diff --git a/src/mir_core/src/utils.cpp b/src/mir_core/src/utils.cpp index b6953a027a..07613f6394 100644 --- a/src/mir_core/src/utils.cpp +++ b/src/mir_core/src/utils.cpp @@ -1,570 +1,570 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include "stdafx.h" - -MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src) -{ - if (dest != nullptr) - mir_free(dest); - - return dest = (src != nullptr) ? mir_strdup(src) : nullptr; -} - -MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src) -{ - if (dest != nullptr) - mir_free(dest); - - return dest = (src != nullptr) ? mir_wstrdup(src) : nullptr; -} - -MIR_CORE_DLL(char*) rtrim(char *str) -{ - if (str == nullptr) - return nullptr; - - char* p = strchr(str, 0); - while (--p >= str) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - *p = 0; break; - default: - return str; - } - } - return str; -} - -MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = wcschr(str, 0); - while (--p >= str) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - *p = 0; break; - default: - return str; - } - } - return str; -} - -MIR_CORE_DLL(char*) ltrim(char *str) -{ - if (str == nullptr) - return nullptr; - - char* p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - memmove(str, p, strlen(p) + 1); - return str; - } - } -} - -MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - memmove(str, p, sizeof(wchar_t)*(wcslen(p) + 1)); - return str; - } - } -} - -MIR_CORE_DLL(char*) ltrimp(char *str) -{ - if (str == nullptr) - return nullptr; - - char *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - return p; - } - } -} - -MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str) -{ - if (str == nullptr) - return nullptr; - - wchar_t *p = str; - for (;;) { - switch (*p) { - case ' ': case '\t': case '\n': case '\r': - ++p; break; - default: - return p; - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(char*) strdel(char *str, size_t len) -{ - char* p; - for (p = str + len; *p != 0; p++) - *(p - len) = *p; - - *(p - len) = '\0'; - return str; -} - -MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len) -{ - wchar_t* p; - for (p = str + len; *p != 0; p++) - *(p - len) = *p; - - *(p - len) = '\0'; - return str; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const char *last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && *mask != *name) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const wchar_t* last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && *mask != *name) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z')?((_c)-'a'+'A'):(_c)) - -MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const char *last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask) -{ - if (name == nullptr || mask == nullptr) - return false; - - const wchar_t* last = nullptr; - for (;; mask++, name++) { - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; - if (*name == '\0') return ((BOOL)!*mask); - } - if (*mask != '*') return FALSE; - for (;; mask++, name++) { - while (*mask == '*') { - last = mask++; - if (*mask == '\0') return ((BOOL)!*mask); /* true */ - } - if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ - if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -static char szHexTable[] = "0123456789abcdef"; - -MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest) -{ - const uint8_t *p = (const uint8_t*)pData; - char *d = dest; - - for (size_t i = 0; i < len; i++, p++) { - *d++ = szHexTable[*p >> 4]; - *d++ = szHexTable[*p & 0x0F]; - } - *d = 0; - - return dest; -} - -MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest) -{ - const uint8_t *p = (const uint8_t*)pData; - wchar_t *d = dest; - - for (size_t i = 0; i < len; i++, p++) { - *d++ = szHexTable[*p >> 4]; - *d++ = szHexTable[*p & 0x0F]; - } - *d = 0; - - return dest; -} - -static int hex2dec(int iHex) -{ - if (iHex >= '0' && iHex <= '9') - return iHex - '0'; - if (iHex >= 'a' && iHex <= 'f') - return iHex - 'a' + 10; - if (iHex >= 'A' && iHex <= 'F') - return iHex - 'A' + 10; - return 0; -} - -MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len) -{ - if (pSrc == nullptr || pData == nullptr || len == 0) - return false; - - size_t bufLen = strlen(pSrc)/2; - if (pSrc[bufLen*2] != 0 || bufLen > len) - return false; - - uint8_t *pDest = (uint8_t*)pData; - const char *p = (const char *)pSrc; - for (size_t i = 0; i < bufLen; i++, p += 2) - pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); - - if (bufLen < len) - memset(pDest + bufLen, 0, len - bufLen); - return true; -} - -MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len) -{ - if (pSrc == nullptr || pData == nullptr || len == 0) - return false; - - size_t bufLen = wcslen(pSrc)/2; - if (pSrc[bufLen * 2] != 0 || bufLen > len) - return false; - - uint8_t *pDest = (uint8_t*)pData; - const wchar_t *p = (const wchar_t *)pSrc; - for (size_t i = 0; i < bufLen; i++, p += 2) - pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); - - if (bufLen < len) - memset(pDest+bufLen, 0, len - bufLen); - return true; -} - - -///////////////////////////////////////////////////////////////////////////////////////// - -#pragma intrinsic(strlen, strcpy, strcat, strcmp, wcslen, wcscpy, wcscat, wcscmp) - -MIR_CORE_DLL(size_t) mir_strlen(const char *p) -{ - return (p) ? strlen(p) : 0; -} - -MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p) -{ - return (p) ? wcslen(p) : 0; -} - -MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return strcpy(dest, src); -} - -MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return wcscpy(dest, src); -} - -MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - strncpy_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - wcsncpy_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return strcat(dest, src); -} - -MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) { - *dest = 0; - return dest; - } - - return wcscat(dest, src); -} - -MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - strncat_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len) -{ - if (dest == nullptr) - return nullptr; - - if (src == nullptr) - *dest = 0; - else - wcsncat_s(dest, len, src, _TRUNCATE); - return dest; -} - -MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strcmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcscmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return stricmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsicmp(p1, p2); -} - -MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strncmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsncmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return strnicmp(p1, p2, n); -} - -MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n) -{ - if (p1 == nullptr) - return (p2 == nullptr) ? 0 : -1; - if (p2 == nullptr) - return 1; - return wcsnicmp(p1, p2, n); -} - -#ifdef _MSC_VER -MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2) -{ - for (int i = 0; s1[i]; i++) - for (int j = i, k = 0; towlower(s1[j]) == towlower(s2[k]); j++, k++) - if (!s2[k + 1]) - return s1 + i; - - return nullptr; -} -#endif - -///////////////////////////////////////////////////////////////////////////////////////// - -#ifdef _MSC_VER - PGENRANDOM pfnRtlGenRandom; -#endif - -MIR_CORE_DLL(void) Utils_GetRandom(void *pszDest, size_t cbLen) -{ - if (pszDest == nullptr || cbLen == 0) - return; - - #ifdef _MSC_VER - if (pfnRtlGenRandom != nullptr) { - pfnRtlGenRandom(pszDest, (uint32_t)cbLen); - return; - } - #endif - - srand(time(0)); - uint8_t *p = (uint8_t*)pszDest; - for (size_t i = 0; i < cbLen; i++) - p[i] = rand() & 0xFF; -} - -MIR_CORE_DLL(bool) Utils_IsRtl(const wchar_t *pszwText) -{ - #ifdef _MSC_VER - size_t iLen = mir_wstrlen(pszwText); - mir_ptr infoTypeC2((uint16_t*)mir_calloc(sizeof(uint16_t) * (iLen + 2))); - GetStringTypeW(CT_CTYPE2, pszwText, (int)iLen, infoTypeC2); - - for (size_t i = 0; i < iLen; i++) - if (infoTypeC2[i] == C2_RIGHTTOLEFT) - return true; - #endif - - return false; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +MIR_CORE_DLL(char*) replaceStr(char* &dest, const char *src) +{ + if (dest != nullptr) + mir_free(dest); + + return dest = (src != nullptr) ? mir_strdup(src) : nullptr; +} + +MIR_CORE_DLL(wchar_t*) replaceStrW(wchar_t* &dest, const wchar_t *src) +{ + if (dest != nullptr) + mir_free(dest); + + return dest = (src != nullptr) ? mir_wstrdup(src) : nullptr; +} + +MIR_CORE_DLL(char*) rtrim(char *str) +{ + if (str == nullptr) + return nullptr; + + char* p = strchr(str, 0); + while (--p >= str) { + switch (*p) { + case ' ': case '\t': case '\n': case '\r': + *p = 0; break; + default: + return str; + } + } + return str; +} + +MIR_CORE_DLL(wchar_t*) rtrimw(wchar_t *str) +{ + if (str == nullptr) + return nullptr; + + wchar_t *p = wcschr(str, 0); + while (--p >= str) { + switch (*p) { + case ' ': case '\t': case '\n': case '\r': + *p = 0; break; + default: + return str; + } + } + return str; +} + +MIR_CORE_DLL(char*) ltrim(char *str) +{ + if (str == nullptr) + return nullptr; + + char* p = str; + for (;;) { + switch (*p) { + case ' ': case '\t': case '\n': case '\r': + ++p; break; + default: + memmove(str, p, strlen(p) + 1); + return str; + } + } +} + +MIR_CORE_DLL(wchar_t*) ltrimw(wchar_t *str) +{ + if (str == nullptr) + return nullptr; + + wchar_t *p = str; + for (;;) { + switch (*p) { + case ' ': case '\t': case '\n': case '\r': + ++p; break; + default: + memmove(str, p, sizeof(wchar_t)*(wcslen(p) + 1)); + return str; + } + } +} + +MIR_CORE_DLL(char*) ltrimp(char *str) +{ + if (str == nullptr) + return nullptr; + + char *p = str; + for (;;) { + switch (*p) { + case ' ': case '\t': case '\n': case '\r': + ++p; break; + default: + return p; + } + } +} + +MIR_CORE_DLL(wchar_t*) ltrimpw(wchar_t *str) +{ + if (str == nullptr) + return nullptr; + + wchar_t *p = str; + for (;;) { + switch (*p) { + case ' ': case '\t': case '\n': case '\r': + ++p; break; + default: + return p; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(char*) strdel(char *str, size_t len) +{ + char* p; + for (p = str + len; *p != 0; p++) + *(p - len) = *p; + + *(p - len) = '\0'; + return str; +} + +MIR_CORE_DLL(wchar_t*) strdelw(wchar_t *str, size_t len) +{ + wchar_t* p; + for (p = str + len; *p != 0; p++) + *(p - len) = *p; + + *(p - len) = '\0'; + return str; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +MIR_CORE_DLL(int) wildcmp(const char *name, const char *mask) +{ + if (name == nullptr || mask == nullptr) + return false; + + const char *last = nullptr; + for (;; mask++, name++) { + if (*mask != '?' && *mask != *name) break; + if (*name == '\0') return ((BOOL)!*mask); + } + if (*mask != '*') return FALSE; + for (;; mask++, name++) { + while (*mask == '*') { + last = mask++; + if (*mask == '\0') return ((BOOL)!*mask); /* true */ + } + if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ + if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; + } +} + +MIR_CORE_DLL(int) wildcmpw(const wchar_t *name, const wchar_t *mask) +{ + if (name == nullptr || mask == nullptr) + return false; + + const wchar_t* last = nullptr; + for (;; mask++, name++) { + if (*mask != '?' && *mask != *name) break; + if (*name == '\0') return ((BOOL)!*mask); + } + if (*mask != '*') return FALSE; + for (;; mask++, name++) { + while (*mask == '*') { + last = mask++; + if (*mask == '\0') return ((BOOL)!*mask); /* true */ + } + if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ + if (*mask != '?' && *mask != *name) name -= (size_t)(mask - last) - 1, mask = last; + } +} + +#define _qtoupper(_c) (((_c) >= 'a' && (_c) <= 'z')?((_c)-'a'+'A'):(_c)) + +MIR_CORE_DLL(int) wildcmpi(const char *name, const char *mask) +{ + if (name == nullptr || mask == nullptr) + return false; + + const char *last = nullptr; + for (;; mask++, name++) { + if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; + if (*name == '\0') return ((BOOL)!*mask); + } + if (*mask != '*') return FALSE; + for (;; mask++, name++) { + while (*mask == '*') { + last = mask++; + if (*mask == '\0') return ((BOOL)!*mask); /* true */ + } + if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ + if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; + } +} + +MIR_CORE_DLL(int) wildcmpiw(const wchar_t *name, const wchar_t *mask) +{ + if (name == nullptr || mask == nullptr) + return false; + + const wchar_t* last = nullptr; + for (;; mask++, name++) { + if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) break; + if (*name == '\0') return ((BOOL)!*mask); + } + if (*mask != '*') return FALSE; + for (;; mask++, name++) { + while (*mask == '*') { + last = mask++; + if (*mask == '\0') return ((BOOL)!*mask); /* true */ + } + if (*name == '\0') return ((BOOL)!*mask); /* *mask == EOS */ + if (*mask != '?' && _qtoupper(*mask) != _qtoupper(*name)) name -= (size_t)(mask - last) - 1, mask = last; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static char szHexTable[] = "0123456789abcdef"; + +MIR_CORE_DLL(char*) bin2hex(const void *pData, size_t len, char *dest) +{ + const uint8_t *p = (const uint8_t*)pData; + char *d = dest; + + for (size_t i = 0; i < len; i++, p++) { + *d++ = szHexTable[*p >> 4]; + *d++ = szHexTable[*p & 0x0F]; + } + *d = 0; + + return dest; +} + +MIR_CORE_DLL(wchar_t*) bin2hexW(const void *pData, size_t len, wchar_t *dest) +{ + const uint8_t *p = (const uint8_t*)pData; + wchar_t *d = dest; + + for (size_t i = 0; i < len; i++, p++) { + *d++ = szHexTable[*p >> 4]; + *d++ = szHexTable[*p & 0x0F]; + } + *d = 0; + + return dest; +} + +static int hex2dec(int iHex) +{ + if (iHex >= '0' && iHex <= '9') + return iHex - '0'; + if (iHex >= 'a' && iHex <= 'f') + return iHex - 'a' + 10; + if (iHex >= 'A' && iHex <= 'F') + return iHex - 'A' + 10; + return 0; +} + +MIR_CORE_DLL(bool) hex2bin(const char *pSrc, void *pData, size_t len) +{ + if (pSrc == nullptr || pData == nullptr || len == 0) + return false; + + size_t bufLen = strlen(pSrc)/2; + if (pSrc[bufLen*2] != 0 || bufLen > len) + return false; + + uint8_t *pDest = (uint8_t*)pData; + const char *p = (const char *)pSrc; + for (size_t i = 0; i < bufLen; i++, p += 2) + pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); + + if (bufLen < len) + memset(pDest + bufLen, 0, len - bufLen); + return true; +} + +MIR_CORE_DLL(bool) hex2binW(const wchar_t *pSrc, void *pData, size_t len) +{ + if (pSrc == nullptr || pData == nullptr || len == 0) + return false; + + size_t bufLen = wcslen(pSrc)/2; + if (pSrc[bufLen * 2] != 0 || bufLen > len) + return false; + + uint8_t *pDest = (uint8_t*)pData; + const wchar_t *p = (const wchar_t *)pSrc; + for (size_t i = 0; i < bufLen; i++, p += 2) + pDest[i] = hex2dec(p[0]) * 16 + hex2dec(p[1]); + + if (bufLen < len) + memset(pDest+bufLen, 0, len - bufLen); + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////// + +#pragma intrinsic(strlen, strcpy, strcat, strcmp, wcslen, wcscpy, wcscat, wcscmp) + +MIR_CORE_DLL(size_t) mir_strlen(const char *p) +{ + return (p) ? strlen(p) : 0; +} + +MIR_CORE_DLL(size_t) mir_wstrlen(const wchar_t *p) +{ + return (p) ? wcslen(p) : 0; +} + +MIR_CORE_DLL(char*) mir_strcpy(char *dest, const char *src) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) { + *dest = 0; + return dest; + } + + return strcpy(dest, src); +} + +MIR_CORE_DLL(wchar_t*) mir_wstrcpy(wchar_t *dest, const wchar_t *src) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) { + *dest = 0; + return dest; + } + + return wcscpy(dest, src); +} + +MIR_CORE_DLL(char*) mir_strncpy(char *dest, const char *src, size_t len) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) + *dest = 0; + else + strncpy_s(dest, len, src, _TRUNCATE); + return dest; +} + +MIR_CORE_DLL(wchar_t*) mir_wstrncpy(wchar_t *dest, const wchar_t *src, size_t len) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) + *dest = 0; + else + wcsncpy_s(dest, len, src, _TRUNCATE); + return dest; +} + +MIR_CORE_DLL(char*) mir_strcat(char *dest, const char *src) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) { + *dest = 0; + return dest; + } + + return strcat(dest, src); +} + +MIR_CORE_DLL(wchar_t*) mir_wstrcat(wchar_t *dest, const wchar_t *src) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) { + *dest = 0; + return dest; + } + + return wcscat(dest, src); +} + +MIR_CORE_DLL(char*) mir_strncat(char *dest, const char *src, size_t len) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) + *dest = 0; + else + strncat_s(dest, len, src, _TRUNCATE); + return dest; +} + +MIR_CORE_DLL(wchar_t*) mir_wstrncat(wchar_t *dest, const wchar_t *src, size_t len) +{ + if (dest == nullptr) + return nullptr; + + if (src == nullptr) + *dest = 0; + else + wcsncat_s(dest, len, src, _TRUNCATE); + return dest; +} + +MIR_CORE_DLL(int) mir_strcmp(const char *p1, const char *p2) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return strcmp(p1, p2); +} + +MIR_CORE_DLL(int) mir_wstrcmp(const wchar_t *p1, const wchar_t *p2) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return wcscmp(p1, p2); +} + +MIR_CORE_DLL(int) mir_strcmpi(const char *p1, const char *p2) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return stricmp(p1, p2); +} + +MIR_CORE_DLL(int) mir_wstrcmpi(const wchar_t *p1, const wchar_t *p2) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return wcsicmp(p1, p2); +} + +MIR_CORE_DLL(int) mir_strncmp(const char *p1, const char *p2, size_t n) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return strncmp(p1, p2, n); +} + +MIR_CORE_DLL(int) mir_wstrncmp(const wchar_t *p1, const wchar_t *p2, size_t n) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return wcsncmp(p1, p2, n); +} + +MIR_CORE_DLL(int) mir_strncmpi(const char *p1, const char *p2, size_t n) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return strnicmp(p1, p2, n); +} + +MIR_CORE_DLL(int) mir_wstrncmpi(const wchar_t *p1, const wchar_t *p2, size_t n) +{ + if (p1 == nullptr) + return (p2 == nullptr) ? 0 : -1; + if (p2 == nullptr) + return 1; + return wcsnicmp(p1, p2, n); +} + +#ifdef _MSC_VER +MIR_CORE_DLL(const wchar_t*) mir_wstrstri(const wchar_t *s1, const wchar_t *s2) +{ + for (int i = 0; s1[i]; i++) + for (int j = i, k = 0; towlower(s1[j]) == towlower(s2[k]); j++, k++) + if (!s2[k + 1]) + return s1 + i; + + return nullptr; +} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER + PGENRANDOM pfnRtlGenRandom; +#endif + +MIR_CORE_DLL(void) Utils_GetRandom(void *pszDest, size_t cbLen) +{ + if (pszDest == nullptr || cbLen == 0) + return; + + #ifdef _MSC_VER + if (pfnRtlGenRandom != nullptr) { + pfnRtlGenRandom(pszDest, (uint32_t)cbLen); + return; + } + #endif + + srand(time(0)); + uint8_t *p = (uint8_t*)pszDest; + for (size_t i = 0; i < cbLen; i++) + p[i] = rand() & 0xFF; +} + +MIR_CORE_DLL(bool) Utils_IsRtl(const wchar_t *pszwText) +{ + #ifdef _MSC_VER + size_t iLen = mir_wstrlen(pszwText); + mir_ptr infoTypeC2((uint16_t*)mir_calloc(sizeof(uint16_t) * (iLen + 2))); + GetStringTypeW(CT_CTYPE2, pszwText, (int)iLen, infoTypeC2); + + for (size_t i = 0; i < iLen; i++) + if (infoTypeC2[i] == C2_RIGHTTOLEFT) + return true; + #endif + + return false; +} diff --git a/src/miranda32/res/version.rc b/src/miranda32/res/version.rc index e7ba01cf8e..7106e0debb 100644 --- a/src/miranda32/res/version.rc +++ b/src/miranda32/res/version.rc @@ -38,7 +38,7 @@ BEGIN VALUE "FileDescription", "Miranda NG\0" VALUE "FileVersion", MIRANDA_VERSION_DISPLAY VALUE "InternalName", "miranda32\0" - VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-22 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" + VALUE "LegalCopyright", "Copyright © 2000-11 Miranda IM, 2012-23 Miranda NG team. This software is released under the terms of the GNU General Public License.\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "miranda32.exe\0" VALUE "PrivateBuild", "\0" diff --git a/src/miranda32/src/miranda.cpp b/src/miranda32/src/miranda.cpp index 59e5dde9df..59fd3cf184 100644 --- a/src/miranda32/src/miranda.cpp +++ b/src/miranda32/src/miranda.cpp @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), all portions of this codebase are copyrighted to the people listed in contributors.txt. diff --git a/src/miranda32/src/stdafx.cxx b/src/miranda32/src/stdafx.cxx index 52ec2d6817..e23069a5b8 100644 --- a/src/miranda32/src/stdafx.cxx +++ b/src/miranda32/src/stdafx.cxx @@ -1,6 +1,6 @@ /* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) +Copyright (C) 2012-23 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 diff --git a/src/miranda32/src/stdafx.h b/src/miranda32/src/stdafx.h index 96509b59f8..986030ab1f 100644 --- a/src/miranda32/src/stdafx.h +++ b/src/miranda32/src/stdafx.h @@ -2,7 +2,7 @@ Miranda NG: the free IM client for Microsoft* Windows* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org), +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org), Copyright (c) 2000-12 Miranda IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. -- cgit v1.2.3