summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordartraiden <wowemuh@gmail.com>2023-01-02 21:10:29 +0300
committerdartraiden <wowemuh@gmail.com>2023-01-02 21:10:29 +0300
commit1979fd80424d16b2e489f9b57d01d9c7811d25a2 (patch)
tree960d42c5fe4a51f0fe2850bea91256e226bce221 /src
parentadfbbb217d4f4a05acf198755f219a5223d31c27 (diff)
Update copyrights
Diffstat (limited to 'src')
-rw-r--r--src/core/stdautoaway/src/autoaway.cpp2
-rw-r--r--src/core/stdautoaway/src/idle.cpp178
-rw-r--r--src/core/stdautoaway/src/main.cpp2
-rw-r--r--src/core/stdautoaway/src/options.cpp268
-rw-r--r--src/core/stdautoaway/src/stdafx.h2
-rw-r--r--src/core/stdautoaway/src/version.h2
-rw-r--r--src/core/stdaway/src/awaymsg.cpp2
-rw-r--r--src/core/stdaway/src/main.cpp2
-rw-r--r--src/core/stdaway/src/options.cpp280
-rw-r--r--src/core/stdaway/src/sendmsg.cpp2
-rw-r--r--src/core/stdaway/src/stdafx.h2
-rw-r--r--src/core/stdaway/src/version.h2
-rw-r--r--src/core/stdclist/src/clc.h2
-rw-r--r--src/core/stdclist/src/clcfonts.cpp2
-rw-r--r--src/core/stdclist/src/clcopts.cpp2
-rw-r--r--src/core/stdclist/src/clcpaint.cpp2
-rw-r--r--src/core/stdclist/src/clistmenus.cpp2
-rw-r--r--src/core/stdclist/src/clistopts.cpp2
-rw-r--r--src/core/stdclist/src/cluiopts.cpp2
-rw-r--r--src/core/stdclist/src/contact.cpp2
-rw-r--r--src/core/stdclist/src/init.cpp2
-rw-r--r--src/core/stdclist/src/stdafx.cxx2
-rw-r--r--src/core/stdclist/src/stdafx.h2
-rw-r--r--src/core/stdclist/src/version.h2
-rw-r--r--src/core/stdcrypt/src/encrypt.cpp2
-rw-r--r--src/core/stdcrypt/src/main.cpp2
-rw-r--r--src/core/stdcrypt/src/stdafx.h2
-rw-r--r--src/core/stdcrypt/src/stdcrypt.h2
-rw-r--r--src/core/stdcrypt/src/utils.cpp2
-rw-r--r--src/core/stdcrypt/src/version.h2
-rw-r--r--src/core/stdemail/src/email.cpp2
-rw-r--r--src/core/stdemail/src/main.cpp2
-rw-r--r--src/core/stdemail/src/stdafx.h2
-rw-r--r--src/core/stdemail/src/version.h2
-rw-r--r--src/core/stdfile/src/file.cpp2
-rw-r--r--src/core/stdfile/src/file.h2
-rw-r--r--src/core/stdfile/src/fileexistsdlg.cpp2
-rw-r--r--src/core/stdfile/src/fileopts.cpp2
-rw-r--r--src/core/stdfile/src/filerecvdlg.cpp2
-rw-r--r--src/core/stdfile/src/filesenddlg.cpp2
-rw-r--r--src/core/stdfile/src/filexferdlg.cpp1530
-rw-r--r--src/core/stdfile/src/ftmanager.cpp2
-rw-r--r--src/core/stdfile/src/main.cpp2
-rw-r--r--src/core/stdfile/src/stdafx.h2
-rw-r--r--src/core/stdfile/src/version.h2
-rw-r--r--src/core/stdmsg/src/chat_manager.cpp486
-rw-r--r--src/core/stdmsg/src/chat_window.cpp550
-rw-r--r--src/core/stdmsg/src/cmdlist.cpp2
-rw-r--r--src/core/stdmsg/src/cmdlist.h2
-rw-r--r--src/core/stdmsg/src/globals.cpp2
-rw-r--r--src/core/stdmsg/src/msgdialog.cpp2
-rw-r--r--src/core/stdmsg/src/msglog.cpp2
-rw-r--r--src/core/stdmsg/src/msgoptions.cpp2
-rw-r--r--src/core/stdmsg/src/msgs.cpp2
-rw-r--r--src/core/stdmsg/src/msgs.h2
-rw-r--r--src/core/stdmsg/src/msgtimedout.cpp2
-rw-r--r--src/core/stdmsg/src/srmm.cpp2
-rw-r--r--src/core/stdmsg/src/statusicon.cpp2
-rw-r--r--src/core/stdmsg/src/stdafx.cxx2
-rw-r--r--src/core/stdmsg/src/stdafx.h2
-rw-r--r--src/core/stdmsg/src/tabs.cpp1336
-rw-r--r--src/core/stdmsg/src/version.h2
-rw-r--r--src/core/stdpopup/src/stdafx.cxx34
-rw-r--r--src/core/stdpopup/src/version.h2
-rw-r--r--src/core/stduihist/src/history.cpp2
-rw-r--r--src/core/stduihist/src/main.cpp2
-rw-r--r--src/core/stduihist/src/stdafx.h2
-rw-r--r--src/core/stduihist/src/version.h2
-rw-r--r--src/core/stduserinfo/src/contactinfo.cpp2
-rw-r--r--src/core/stduserinfo/src/main.cpp2
-rw-r--r--src/core/stduserinfo/src/stdafx.h2
-rw-r--r--src/core/stduserinfo/src/stdinfo.cpp2
-rw-r--r--src/core/stduserinfo/src/userinfo.cpp2
-rw-r--r--src/core/stduserinfo/src/utils.h428
-rw-r--r--src/core/stduserinfo/src/version.h2
-rw-r--r--src/core/stduseronline/src/main.cpp2
-rw-r--r--src/core/stduseronline/src/stdafx.h2
-rw-r--r--src/core/stduseronline/src/useronline.cpp2
-rw-r--r--src/core/stduseronline/src/version.h2
-rw-r--r--src/mir_app/src/CMPluginBase.cpp686
-rw-r--r--src/mir_app/src/Docking.cpp2
-rw-r--r--src/mir_app/src/FontOptions.cpp2
-rw-r--r--src/mir_app/src/FontService.cpp2
-rw-r--r--src/mir_app/src/FontService.h2
-rw-r--r--src/mir_app/src/IcoLib.h2
-rw-r--r--src/mir_app/src/MDatabaseCommon.cpp2
-rw-r--r--src/mir_app/src/MDatabaseCommonCrypt.cpp898
-rw-r--r--src/mir_app/src/MDatabaseReadonly.cpp360
-rw-r--r--src/mir_app/src/MHttpRequest.cpp186
-rw-r--r--src/mir_app/src/addcontact.cpp2
-rw-r--r--src/mir_app/src/auth.cpp734
-rw-r--r--src/mir_app/src/button.cpp2
-rw-r--r--src/mir_app/src/chat_manager.cpp2
-rw-r--r--src/mir_app/src/chat_rtf.cpp408
-rw-r--r--src/mir_app/src/chat_svc.cpp2
-rw-r--r--src/mir_app/src/chat_tools.cpp2
-rw-r--r--src/mir_app/src/chat_ui.cpp440
-rw-r--r--src/mir_app/src/clc.cpp2
-rw-r--r--src/mir_app/src/clc.h2
-rw-r--r--src/mir_app/src/clccontact.cpp144
-rw-r--r--src/mir_app/src/clcfiledrop.cpp2
-rw-r--r--src/mir_app/src/clcidents.cpp2
-rw-r--r--src/mir_app/src/clcitems.cpp1460
-rw-r--r--src/mir_app/src/clcmsgs.cpp2
-rw-r--r--src/mir_app/src/clcutils.cpp2
-rw-r--r--src/mir_app/src/clistcore.cpp2
-rw-r--r--src/mir_app/src/clistevents.cpp2
-rw-r--r--src/mir_app/src/clistgroups.cpp2
-rw-r--r--src/mir_app/src/clistmod.cpp2
-rw-r--r--src/mir_app/src/clistopts.cpp324
-rw-r--r--src/mir_app/src/clistsettings.cpp2
-rw-r--r--src/mir_app/src/clisttray.cpp2
-rw-r--r--src/mir_app/src/clui.cpp2
-rw-r--r--src/mir_app/src/cluiservices.cpp2
-rw-r--r--src/mir_app/src/colorchooser.cpp2
-rw-r--r--src/mir_app/src/contacts.cpp2
-rw-r--r--src/mir_app/src/copyright.h4
-rw-r--r--src/mir_app/src/database.cpp2
-rw-r--r--src/mir_app/src/database.h2
-rw-r--r--src/mir_app/src/db_events.cpp2
-rw-r--r--src/mir_app/src/db_ini.cpp2
-rw-r--r--src/mir_app/src/db_intf.cpp2
-rw-r--r--src/mir_app/src/db_upgrade.cpp142
-rw-r--r--src/mir_app/src/db_util.cpp258
-rw-r--r--src/mir_app/src/descbutton.cpp2
-rw-r--r--src/mir_app/src/dll_sniffer.cpp2
-rw-r--r--src/mir_app/src/ei_baseIcon.cpp140
-rw-r--r--src/mir_app/src/ei_callbackIcon.cpp150
-rw-r--r--src/mir_app/src/ei_defaulticons.cpp678
-rw-r--r--src/mir_app/src/ei_extraIcon.cpp166
-rw-r--r--src/mir_app/src/ei_groupIcon.cpp400
-rw-r--r--src/mir_app/src/ei_icolibIcon.cpp236
-rw-r--r--src/mir_app/src/ei_options.cpp962
-rw-r--r--src/mir_app/src/ei_services.cpp1008
-rw-r--r--src/mir_app/src/encrypt.cpp2
-rw-r--r--src/mir_app/src/encrypt.h94
-rw-r--r--src/mir_app/src/enterstring.cpp2
-rw-r--r--src/mir_app/src/extracticon.cpp2
-rw-r--r--src/mir_app/src/extraicons.h2
-rw-r--r--src/mir_app/src/filter.cpp2
-rw-r--r--src/mir_app/src/filter.h2
-rw-r--r--src/mir_app/src/findadd.cpp2070
-rw-r--r--src/mir_app/src/findadd.h2
-rw-r--r--src/mir_app/src/genmenu.h2
-rw-r--r--src/mir_app/src/headerbar.cpp2
-rw-r--r--src/mir_app/src/help.cpp378
-rw-r--r--src/mir_app/src/hotkey_opts.cpp2052
-rw-r--r--src/mir_app/src/hotkeys.cpp2
-rw-r--r--src/mir_app/src/icolib.cpp2
-rw-r--r--src/mir_app/src/idle.cpp174
-rw-r--r--src/mir_app/src/ignore.cpp2
-rw-r--r--src/mir_app/src/keyboard.cpp2
-rw-r--r--src/mir_app/src/langpack.h2
-rw-r--r--src/mir_app/src/lpopts.cpp2
-rw-r--r--src/mir_app/src/mdatabasecache.cpp2
-rw-r--r--src/mir_app/src/menu_clist.cpp2
-rw-r--r--src/mir_app/src/menu_frames.cpp2
-rw-r--r--src/mir_app/src/menu_groups.cpp2
-rw-r--r--src/mir_app/src/menu_options.cpp1176
-rw-r--r--src/mir_app/src/menu_tray.cpp2
-rw-r--r--src/mir_app/src/menu_utils.cpp2
-rw-r--r--src/mir_app/src/meta_addto.cpp2
-rw-r--r--src/mir_app/src/meta_api.cpp2
-rw-r--r--src/mir_app/src/meta_edit.cpp2
-rw-r--r--src/mir_app/src/meta_main.cpp2
-rw-r--r--src/mir_app/src/meta_menu.cpp2
-rw-r--r--src/mir_app/src/meta_options.cpp2
-rw-r--r--src/mir_app/src/meta_services.cpp2
-rw-r--r--src/mir_app/src/meta_utils.cpp2
-rw-r--r--src/mir_app/src/metacontacts.h2
-rw-r--r--src/mir_app/src/miranda.cpp2
-rw-r--r--src/mir_app/src/miranda.h2
-rw-r--r--src/mir_app/src/modules.cpp2
-rw-r--r--src/mir_app/src/movetogroup.cpp2
-rw-r--r--src/mir_app/src/netlib.cpp2
-rw-r--r--src/mir_app/src/netlib.h2
-rw-r--r--src/mir_app/src/netlib_autoproxy.cpp730
-rw-r--r--src/mir_app/src/netlib_bind.cpp660
-rw-r--r--src/mir_app/src/netlib_http.cpp2300
-rw-r--r--src/mir_app/src/netlib_httpproxy.cpp170
-rw-r--r--src/mir_app/src/netlib_log.cpp1156
-rw-r--r--src/mir_app/src/netlib_openconn.cpp1452
-rw-r--r--src/mir_app/src/netlib_opts.cpp1036
-rw-r--r--src/mir_app/src/netlib_pktrecver.cpp160
-rw-r--r--src/mir_app/src/netlib_security.cpp726
-rw-r--r--src/mir_app/src/netlib_sock.cpp574
-rw-r--r--src/mir_app/src/netlib_ssl.cpp930
-rw-r--r--src/mir_app/src/netlib_upnp.cpp1626
-rw-r--r--src/mir_app/src/netlib_websocket.cpp348
-rw-r--r--src/mir_app/src/newplugins.cpp2
-rw-r--r--src/mir_app/src/options.cpp2
-rw-r--r--src/mir_app/src/path.cpp2
-rw-r--r--src/mir_app/src/pluginopts.cpp2
-rw-r--r--src/mir_app/src/popupOption.cpp246
-rw-r--r--src/mir_app/src/popups.cpp314
-rw-r--r--src/mir_app/src/profilemanager.cpp1282
-rw-r--r--src/mir_app/src/profilemanager.h2
-rw-r--r--src/mir_app/src/proto_accs.cpp870
-rw-r--r--src/mir_app/src/proto_chains.cpp2
-rw-r--r--src/mir_app/src/proto_interface.cpp702
-rw-r--r--src/mir_app/src/proto_internal.cpp2
-rw-r--r--src/mir_app/src/proto_opts.cpp2
-rw-r--r--src/mir_app/src/proto_order.cpp2
-rw-r--r--src/mir_app/src/proto_ui.cpp2
-rw-r--r--src/mir_app/src/proto_utils.cpp2
-rw-r--r--src/mir_app/src/protocols.cpp2
-rw-r--r--src/mir_app/src/pu_utils.cpp662
-rw-r--r--src/mir_app/src/searchresults.cpp2
-rw-r--r--src/mir_app/src/skin.h2
-rw-r--r--src/mir_app/src/skin2opts.cpp2
-rw-r--r--src/mir_app/src/skinicons.cpp2
-rw-r--r--src/mir_app/src/sounds.cpp2
-rw-r--r--src/mir_app/src/srmm_base.cpp1846
-rw-r--r--src/mir_app/src/srmm_log.cpp392
-rw-r--r--src/mir_app/src/srmm_log_hpp.cpp454
-rw-r--r--src/mir_app/src/srmm_log_rtf.cpp816
-rw-r--r--src/mir_app/src/srmm_statusicon.cpp2
-rw-r--r--src/mir_app/src/srmm_toolbar.cpp1772
-rw-r--r--src/mir_app/src/srmm_util.cpp294
-rw-r--r--src/mir_app/src/stdafx.cxx2
-rw-r--r--src/mir_app/src/stdafx.h2
-rw-r--r--src/mir_app/src/usedIcons.cpp2
-rw-r--r--src/mir_app/src/usedIcons.h2
-rw-r--r--src/mir_app/src/userInfo.cpp90
-rw-r--r--src/mir_app/src/utils.cpp2
-rw-r--r--src/mir_app/src/visibility.cpp2
-rw-r--r--src/mir_core/res/version.rc114
-rw-r--r--src/mir_core/src/Linux/CCtrlBase.cpp410
-rw-r--r--src/mir_core/src/Linux/CCtrlButton.cpp110
-rw-r--r--src/mir_core/src/Linux/CCtrlCheck.cpp140
-rw-r--r--src/mir_core/src/Linux/CCtrlClc.cpp414
-rw-r--r--src/mir_core/src/Linux/CCtrlColor.cpp122
-rw-r--r--src/mir_core/src/Linux/CCtrlCombo.cpp338
-rw-r--r--src/mir_core/src/Linux/CCtrlData.cpp104
-rw-r--r--src/mir_core/src/Linux/CCtrlEdit.cpp136
-rw-r--r--src/mir_core/src/Linux/CCtrlHyperlink.cpp108
-rw-r--r--src/mir_core/src/Linux/CCtrlLabel.cpp60
-rw-r--r--src/mir_core/src/Linux/CCtrlListBox.cpp320
-rw-r--r--src/mir_core/src/Linux/CCtrlListView.cpp1102
-rw-r--r--src/mir_core/src/Linux/CCtrlMButton.cpp124
-rw-r--r--src/mir_core/src/Linux/CCtrlPages.cpp822
-rw-r--r--src/mir_core/src/Linux/CCtrlSlider.cpp140
-rw-r--r--src/mir_core/src/Linux/CCtrlSpin.cpp162
-rw-r--r--src/mir_core/src/Linux/CCtrlTreeOpts.cpp432
-rw-r--r--src/mir_core/src/Linux/CCtrlTreeView.cpp1634
-rw-r--r--src/mir_core/src/Linux/CDbLink.cpp184
-rw-r--r--src/mir_core/src/Linux/CDlgBase.cpp520
-rw-r--r--src/mir_core/src/Linux/CProgress.cpp106
-rw-r--r--src/mir_core/src/Linux/CSplitter.cpp166
-rw-r--r--src/mir_core/src/Linux/CTimer.cpp186
-rw-r--r--src/mir_core/src/Linux/cctrldate.cpp98
-rw-r--r--src/mir_core/src/Linux/fileutil.cpp178
-rw-r--r--src/mir_core/src/Linux/strutil.cpp96
-rw-r--r--src/mir_core/src/Windows/CCtrlBase.cpp448
-rw-r--r--src/mir_core/src/Windows/CCtrlButton.cpp108
-rw-r--r--src/mir_core/src/Windows/CCtrlCheck.cpp136
-rw-r--r--src/mir_core/src/Windows/CCtrlClc.cpp422
-rw-r--r--src/mir_core/src/Windows/CCtrlColor.cpp122
-rw-r--r--src/mir_core/src/Windows/CCtrlCombo.cpp370
-rw-r--r--src/mir_core/src/Windows/CCtrlData.cpp104
-rw-r--r--src/mir_core/src/Windows/CCtrlDate.cpp98
-rw-r--r--src/mir_core/src/Windows/CCtrlEdit.cpp136
-rw-r--r--src/mir_core/src/Windows/CCtrlHyperlink.cpp108
-rw-r--r--src/mir_core/src/Windows/CCtrlLabel.cpp62
-rw-r--r--src/mir_core/src/Windows/CCtrlListBox.cpp320
-rw-r--r--src/mir_core/src/Windows/CCtrlListView.cpp1102
-rw-r--r--src/mir_core/src/Windows/CCtrlMButton.cpp124
-rw-r--r--src/mir_core/src/Windows/CCtrlPages.cpp822
-rw-r--r--src/mir_core/src/Windows/CCtrlRichEdit.cpp384
-rw-r--r--src/mir_core/src/Windows/CCtrlSlider.cpp140
-rw-r--r--src/mir_core/src/Windows/CCtrlSpin.cpp162
-rw-r--r--src/mir_core/src/Windows/CCtrlTreeOpts.cpp2
-rw-r--r--src/mir_core/src/Windows/CCtrlTreeView.cpp2
-rw-r--r--src/mir_core/src/Windows/CDbLink.cpp184
-rw-r--r--src/mir_core/src/Windows/CDlgBase.cpp1044
-rw-r--r--src/mir_core/src/Windows/CProgress.cpp106
-rw-r--r--src/mir_core/src/Windows/CSplitter.cpp166
-rw-r--r--src/mir_core/src/Windows/CTimer.cpp180
-rw-r--r--src/mir_core/src/Windows/cmdline.cpp154
-rw-r--r--src/mir_core/src/Windows/colourpicker.cpp210
-rw-r--r--src/mir_core/src/Windows/diatheme.cpp340
-rw-r--r--src/mir_core/src/Windows/fileutil.cpp156
-rw-r--r--src/mir_core/src/Windows/hyperlink.cpp554
-rw-r--r--src/mir_core/src/Windows/icons.cpp148
-rw-r--r--src/mir_core/src/Windows/langpack.cpp1530
-rw-r--r--src/mir_core/src/Windows/locks.cpp92
-rw-r--r--src/mir_core/src/Windows/miranda.cpp2
-rw-r--r--src/mir_core/src/Windows/openurl.cpp152
-rw-r--r--src/mir_core/src/Windows/path.cpp492
-rw-r--r--src/mir_core/src/Windows/resizer.cpp302
-rw-r--r--src/mir_core/src/Windows/subclass.cpp402
-rw-r--r--src/mir_core/src/Windows/threads.cpp800
-rw-r--r--src/mir_core/src/Windows/timezones.cpp1186
-rw-r--r--src/mir_core/src/Windows/windowlist.cpp210
-rw-r--r--src/mir_core/src/Windows/winutil.cpp354
-rw-r--r--src/mir_core/src/Windows/winver.cpp744
-rw-r--r--src/mir_core/src/binbuffer.cpp340
-rw-r--r--src/mir_core/src/bitmaps.cpp2
-rw-r--r--src/mir_core/src/db.cpp2
-rw-r--r--src/mir_core/src/http.cpp2
-rw-r--r--src/mir_core/src/lists.cpp2
-rw-r--r--src/mir_core/src/logger.cpp406
-rw-r--r--src/mir_core/src/memory.cpp590
-rw-r--r--src/mir_core/src/miranda.h202
-rw-r--r--src/mir_core/src/modules.cpp1408
-rw-r--r--src/mir_core/src/mstring.cpp290
-rw-r--r--src/mir_core/src/stdafx.cxx2
-rw-r--r--src/mir_core/src/stdafx.h168
-rw-r--r--src/mir_core/src/utf.cpp882
-rw-r--r--src/mir_core/src/utils.cpp1140
-rw-r--r--src/miranda32/res/version.rc2
-rw-r--r--src/miranda32/src/miranda.cpp2
-rw-r--r--src/miranda32/src/stdafx.cxx2
-rw-r--r--src/miranda32/src/stdafx.h2
314 files changed, 35602 insertions, 35602 deletions
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 <io.h>
-#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<virusscanthreadstartinfo>(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)&param);
- 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<virusscanthreadstartinfo>(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 <io.h>
+#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<virusscanthreadstartinfo>(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)&param);
+ 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<virusscanthreadstartinfo>(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 <http://www.gnu.org/licenses/>.
-*/
-
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
#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 <size_t _Size>
-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 <size_t _Size>
-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("<not specified>");
- 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("<not specified>"));
- 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 <size_t _Size>
+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 <size_t _Size>
+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("<not specified>");
+ 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("<not specified>"));
+ 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<CMPluginBase> 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<CMPluginBase> 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 &param)
-{
- 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 &param)
-{
- 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 &param)
-{
- 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 &param)
-{
- 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 &param)
+{
+ 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 &param)
+{
+ 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 &param)
+{
+ 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 &param)
+{
+ 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("<Unknown>");
-
- 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("<Unknown>"), _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("<Unknown>");
+
+ 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("<Unknown>"), _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<bool> g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true);
-CMOption<bool> 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<bool> g_bChatPopupInactive(CHAT_MODULE, "PopupInactiveOnly", true);
+CMOption<bool> 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<SavedContactState_t> saveContact(10, NumericKeySortT);
- OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT);
- OBJLIST<SavedInfoState_t> 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<SavedContactState_t> saveContact(10, NumericKeySortT);
+ OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT);
+ OBJLIST<SavedInfoState_t> 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<bool> Clist::UseGroups(MODULENAME, "UseGroups", true);
-CMOption<bool> Clist::HideOffline(MODULENAME, "HideOffline", false);
-CMOption<bool> Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true);
-CMOption<bool> Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true);
-CMOption<bool> Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true);
-CMOption<bool> Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false);
-CMOption<bool> Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true);
-
-CMOption<bool> Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus());
-CMOption<bool> Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false);
-
-CMOption<bool> Clist::FilterSearch("CLC", "FilterSearch", false);
-CMOption<uint32_t> 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<bool> Clist::UseGroups(MODULENAME, "UseGroups", true);
+CMOption<bool> Clist::HideOffline(MODULENAME, "HideOffline", false);
+CMOption<bool> Clist::ConfirmDelete(MODULENAME, "ConfirmDelete", true);
+CMOption<bool> Clist::EnableIconBlink(MODULENAME, "EnableIconBlink", true);
+CMOption<bool> Clist::EnableTrayFlash(MODULENAME, "EnableTrayFlash", true);
+CMOption<bool> Clist::HideEmptyGroups(MODULENAME, "HideEmptyGroups", false);
+CMOption<bool> Clist::RemoveTempContacts(MODULENAME, "RemoveTempContacts", true);
+
+CMOption<bool> Clist::Tray1Click(MODULENAME, "Tray1Click", IsWinVer7Plus());
+CMOption<bool> Clist::TrayAlwaysStatus(MODULENAME, "AlwaysStatus", false);
+
+CMOption<bool> Clist::FilterSearch("CLC", "FilterSearch", false);
+CMOption<uint32_t> 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<ProtoInfo> 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<ProtoInfo> 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<ExtraIconGroup> 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<ExtraIconGroup> 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<ExtraIcon> extraIconsBySlot(10, SortFunc);
-LIST<BaseExtraIcon> 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<ExtraIconGroup> &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<ExtraIconGroup> &groups, BaseExtraIcon *extra)
-{
- for (auto &group : groups)
- for (auto &it : group->m_items)
- if (extra == it)
- return group;
-
- return nullptr;
-}
-
-void RebuildListsBasedOnGroups(LIST<ExtraIconGroup> &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<ExtraIcon> 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<ExtraIconGroup> 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<ExtraIconGroup> 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<ExtraIcon> extraIconsBySlot(10, SortFunc);
+LIST<BaseExtraIcon> 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<ExtraIconGroup> &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<ExtraIconGroup> &groups, BaseExtraIcon *extra)
+{
+ for (auto &group : groups)
+ for (auto &it : group->m_items)
+ if (extra == it)
+ return group;
+
+ return nullptr;
+}
+
+void RebuildListsBasedOnGroups(LIST<ExtraIconGroup> &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<ExtraIcon> 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<ExtraIconGroup> 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<ExtraIconGroup> 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<MCryptoProvider> 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<MCryptoProvider> 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("<not specified>");
-
- 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("<not specified>");
+
+ 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 <m_hotkeys.h>
-#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 <m_hotkeys.h>
+#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<TMO_IntMenuItem> 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<MenuItemOptData> 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<TMO_IntMenuItem> 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<MenuItemOptData> 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 <wininet.h>
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// local module data
-
-static char *szProxyHost[3];
-static LIST<char> 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, "<local>") == 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<IeProxyParam>(NetlibIeProxyThread, &param);
- 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 <wininet.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// local module data
+
+static char *szProxyHost[3];
+static LIST<char> 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, "<local>") == 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<IeProxyParam>(NetlibIeProxyThread, &param);
+ 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<NetlibBoundPort>(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<NetlibBoundPort>(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<ProxyAuth>
-{
- ProxyAuthList() : OBJLIST<ProxyAuth>(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<ProxyAuth>::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<ProxyAuth>
+{
+ ProxyAuthList() : OBJLIST<ProxyAuth>(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<ProxyAuth>::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, &notblocking) != 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, &notblocking);
- 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, &notblocking) != 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, &notblocking);
+ 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 <NetlibTempSettings> 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("<mixed>"), 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("<All connections>"));
- 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 <NetlibTempSettings> 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("<mixed>"), 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("<All connections>"));
+ 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 <security.h>
-#include <rpcdce.h>
-
-#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 <security.h>
+#include <rpcdce.h>
+
+#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 <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-
-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<SslHandle> 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 <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+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<SslHandle> 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[] =
- "<serviceType>%s</serviceType>";
-
-static const char soap_action[] =
- "<?xml version = \"1.0\"?>\r\n"
- "<s:Envelope\r\n"
- " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
- " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
- " <s:Body>\r\n"
- " <u:%s xmlns:u = \"%s\">\r\n"
- "%s"
- " </u:%s>\r\n"
- " </s:Body>\r\n"
- "</s:Envelope>\r\n";
-
-static const char soap_query[] =
- "<s:Envelope\r\n"
- " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
- " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
- " <s:Body>\r\n"
- " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n"
- " <u:varName>%s</u:varName>\r\n"
- " </u:QueryStateVariable>\r\n"
- " </s:Body>\r\n"
- "</s:Envelope>\r\n";
-
-static const char add_port_mapping[] =
- " <NewRemoteHost></NewRemoteHost>\r\n"
- " <NewExternalPort>%i</NewExternalPort>\r\n"
- " <NewProtocol>%s</NewProtocol>\r\n"
- " <NewInternalPort>%i</NewInternalPort>\r\n"
- " <NewInternalClient>%s</NewInternalClient>\r\n"
- " <NewEnabled>1</NewEnabled>\r\n"
- " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
- " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
-
-static const char delete_port_mapping[] =
- " <NewRemoteHost></NewRemoteHost>\r\n"
- " <NewExternalPort>%i</NewExternalPort>\r\n"
- " <NewProtocol>%s</NewProtocol>\r\n";
-
-static const char get_port_mapping[] =
- " <NewPortMappingIndex>%i</NewPortMappingIndex>\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, "<NewExternalIPAddress", ">", "<", 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, "<URLBase>", "</URLBase>", 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, "<controlURL>", "</controlURL>", 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, "<errorCode>", "</errorCode>", 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", "<return>", "<", 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, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0)
- continue;
-
- if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0)
- continue;
-
- if (txtParseParam(szData, "<NewExternalPort", ">", "<", 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[] =
+ "<serviceType>%s</serviceType>";
+
+static const char soap_action[] =
+ "<?xml version = \"1.0\"?>\r\n"
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:%s xmlns:u = \"%s\">\r\n"
+ "%s"
+ " </u:%s>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char soap_query[] =
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n"
+ " <u:varName>%s</u:varName>\r\n"
+ " </u:QueryStateVariable>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char add_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n"
+ " <NewInternalPort>%i</NewInternalPort>\r\n"
+ " <NewInternalClient>%s</NewInternalClient>\r\n"
+ " <NewEnabled>1</NewEnabled>\r\n"
+ " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
+ " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
+
+static const char delete_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n";
+
+static const char get_port_mapping[] =
+ " <NewPortMappingIndex>%i</NewPortMappingIndex>\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, "<NewExternalIPAddress", ">", "<", 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, "<URLBase>", "</URLBase>", 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, "<controlURL>", "</controlURL>", 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, "<errorCode>", "</errorCode>", 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", "<return>", "<", 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, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0)
+ continue;
+
+ if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0)
+ continue;
+
+ if (txtParseParam(szData, "<NewExternalPort", ">", "<", 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<bool> &pVal) :
- m_plugin(pPlugin),
- m_val(pVal),
- m_descr(pszDescr)
- {}
-
- MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption<bool> &pVal) :
- m_plugin(pPlugin),
- m_val(pVal),
- m_descr(pwszDescr)
- {}
-
- CMPluginBase *m_plugin;
- CMOption<bool> &m_val;
- CMStringW m_descr;
-};
-
-static OBJLIST<MPopupOption> g_arOptions(1);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPluginBase::addPopupOption(const char *pszDescr, CMOption<bool> &pVal)
-{
- g_arOptions.insert(new MPopupOption(this, pszDescr, pVal));
- return 0;
-}
-
-int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &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<bool> &pVal) :
+ m_plugin(pPlugin),
+ m_val(pVal),
+ m_descr(pszDescr)
+ {}
+
+ MPopupOption(CMPluginBase *pPlugin, const wchar_t *pwszDescr, CMOption<bool> &pVal) :
+ m_plugin(pPlugin),
+ m_val(pVal),
+ m_descr(pwszDescr)
+ {}
+
+ CMPluginBase *m_plugin;
+ CMOption<bool> &m_val;
+ CMStringW m_descr;
+};
+
+static OBJLIST<MPopupOption> g_arOptions(1);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPluginBase::addPopupOption(const char *pszDescr, CMOption<bool> &pVal)
+{
+ g_arOptions.insert(new MPopupOption(this, pszDescr, pVal));
+ return 0;
+}
+
+int CMPluginBase::addPopupOption(const wchar_t *pwszDescr, CMOption<bool> &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 <sys/stat.h>
-
-#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("<In use>"));
- else
- list.SetItemText(iItem, 1, TranslateT("<Unknown format>"));
-
- 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 <sys/stat.h>
+
+#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("<In use>"));
+ else
+ list.SetItemText(iItem, 1, TranslateT("<Unknown format>"));
+
+ 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<PROTOACCOUNT> 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<DeactivationThreadParam>(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<PROTOACCOUNT> 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<DeactivationThreadParam>(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<uint8_t> 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<uint8_t> 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 <m_history.h>
-
-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"<b>%s:</b>\t%s\n<b>%s:</b>\t%s\n<b>%s:</b>\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<SESSION_INFO>(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 <m_history.h>
+
+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"<b>%s:</b>\t%s\n<b>%s:</b>\t%s\n<b>%s:</b>\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<SESSION_INFO>(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<LoggerClass> g_arLogClasses(1, PtrKeySortT);
-
-static CMOption<uint8_t> 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<LoggerClass> g_arLogClasses(1, PtrKeySortT);
+
+static CMOption<uint8_t> 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<uint8_t> 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<CustomButtonData> 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("<Separator>");
- 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("<Separator>");
- 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<uint8_t> 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<CustomButtonData> 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("<Separator>");
+ 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("<Separator>");
+ 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, &param);
-}
+/*
+
+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, &param);
+}
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 <windows.h>
-#include <winres.h>
-
-#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 <windows.h>
+#include <winres.h>
+
+#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<CCtrlBase> 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<CCtrlBase> 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<bool> &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<bool> &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<TVINSERTSTRUCT> 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<TVINSERTSTRUCT> 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<CDlgBase> 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<CDlgBase> 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, &param);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-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, &param);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "../stdafx.h"
-#include <unistd.h>
-
-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+#include <unistd.h>
+
+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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<CCtrlBase> 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<CCtrlBase> 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 <RichOle.h>
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// 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)&gtl, 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 <RichOle.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 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)&gtl, 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<CDlgBase> 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<DLGTEMPLATE> 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<DLGTEMPLATE> 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<CDlgBase> 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<DLGTEMPLATE> 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<DLGTEMPLATE> 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, &param);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-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, &param);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+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<CmdLineParam> 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<CmdLineParam> 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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<MUUID> 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<MUUID> 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 <ctype.h>
-
-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<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow));
-}
-
-MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow)
-{
- if (pszUrl)
- mir_forkThread<TOpenUrlInfo>(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 <ctype.h>
+
+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<TOpenUrlInfo>(OpenURLThread, new TOpenUrlInfo(mir_a2u(pszUrl), bOpenInNewWindow));
+}
+
+MIR_CORE_DLL(void) Utils_OpenUrlW(const wchar_t *pszUrl, bool bOpenInNewWindow)
+{
+ if (pszUrl)
+ mir_forkThread<TOpenUrlInfo>(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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "../stdafx.h"
-
-struct MSubclassData
-{
- HWND m_hWnd;
-
- int m_iHooks;
- WNDPROC *m_hooks;
- WNDPROC m_origWndProc;
-
- ~MSubclassData()
- {
- free(m_hooks);
- }
-};
-
-static LIST<MSubclassData> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "../stdafx.h"
+
+struct MSubclassData
+{
+ HWND m_hWnd;
+
+ int m_iHooks;
+ WNDPROC *m_hooks;
+ WNDPROC m_origWndProc;
+
+ ~MSubclassData()
+ {
+ free(m_hooks);
+ }
+};
+
+static LIST<MSubclassData> 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 <m_netlib.h>
-
-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<THREAD_WAIT_ENTRY> 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 <m_netlib.h>
+
+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<THREAD_WAIT_ENTRY> 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<MIM_TIMEZONE> g_timezones(55, NumericKeySortT);
-static LIST<MIM_TIMEZONE> 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"<unspecified>"));
-
- 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<MIM_TIMEZONE> g_timezones(55, NumericKeySortT);
+static LIST<MIM_TIMEZONE> 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"<unspecified>"));
+
+ 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<TWindowListItem>
-{
- TWindowList() :
- OBJLIST<TWindowListItem>(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*)&param);
- 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<TWindowListItem>
+{
+ TWindowList() :
+ OBJLIST<TWindowListItem>(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*)&param);
+ 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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
-*/
-
-#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<Logger> 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<Logger> 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<CMPluginBase> 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<CMPluginBase> 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<THook> 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<TService> 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, &param, 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<THook> 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<TService> 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, &param, 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<CMStringData*>(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<CMStringData*>(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<CMStringData*>(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<CMStringData*>(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 <winsock2.h>
- #include <ws2tcpip.h>
- #include <windows.h>
- #include <windowsx.h>
- #include <shlobj.h>
- #include <commctrl.h>
- #include <ShellAPI.h>
- #include <vssym32.h>
- #include <Uxtheme.h>
- #include <Richedit.h>
- #include <Wtsapi32.h>
-
- #include <process.h>
- #include <io.h>
- #include <direct.h>
-
- #ifdef _DEBUG
- #include <crtdbg.h>
- #endif
-#else
- #include <Elementary.h>
-#endif // _WINDOWS
-
-#include <malloc.h>
-#include <stdio.h>
-#include <time.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <limits.h>
-#include <string.h>
-#include <locale.h>
-
-#define __NO_CMPLUGIN_NEEDED
-#include <m_system.h>
-#include <m_database.h>
-#include <m_db_int.h>
-#include <newpluginapi.h>
-#include <m_langpack.h>
-#include <m_metacontacts.h>
-#include <m_skin.h>
-#include <m_icolib.h>
-#include <m_netlib.h>
-#include <m_timezones.h>
-#include <m_protocols.h>
-#include <m_button.h>
-#include <m_gui.h>
-#include <m_chat_int.h>
-
-#include "miranda.h"
-
-#include <m_xml.h>
-
-#include <m_string.inl>
-
-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 <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <windowsx.h>
+ #include <shlobj.h>
+ #include <commctrl.h>
+ #include <ShellAPI.h>
+ #include <vssym32.h>
+ #include <Uxtheme.h>
+ #include <Richedit.h>
+ #include <Wtsapi32.h>
+
+ #include <process.h>
+ #include <io.h>
+ #include <direct.h>
+
+ #ifdef _DEBUG
+ #include <crtdbg.h>
+ #endif
+#else
+ #include <Elementary.h>
+#endif // _WINDOWS
+
+#include <malloc.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <limits.h>
+#include <string.h>
+#include <locale.h>
+
+#define __NO_CMPLUGIN_NEEDED
+#include <m_system.h>
+#include <m_database.h>
+#include <m_db_int.h>
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_metacontacts.h>
+#include <m_skin.h>
+#include <m_icolib.h>
+#include <m_netlib.h>
+#include <m_timezones.h>
+#include <m_protocols.h>
+#include <m_button.h>
+#include <m_gui.h>
+#include <m_chat_int.h>
+
+#include "miranda.h"
+
+#include <m_xml.h>
+
+#include <m_string.inl>
+
+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<uint16_t> 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<uint16_t> 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.