summaryrefslogtreecommitdiff
path: root/plugins
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 /plugins
parentadfbbb217d4f4a05acf198755f219a5223d31c27 (diff)
Update copyrights
Diffstat (limited to 'plugins')
-rw-r--r--plugins/AVS/src/acc.cpp2
-rw-r--r--plugins/AVS/src/cache.cpp2
-rw-r--r--plugins/AVS/src/contact_ava.cpp526
-rw-r--r--plugins/AVS/src/main.cpp2
-rw-r--r--plugins/AVS/src/options.cpp882
-rw-r--r--plugins/AVS/src/poll.cpp2
-rw-r--r--plugins/AVS/src/poll.h2
-rw-r--r--plugins/AVS/src/services.cpp2
-rw-r--r--plugins/AVS/src/stdafx.cxx2
-rw-r--r--plugins/AVS/src/stdafx.h2
-rw-r--r--plugins/AVS/src/userInfo.cpp970
-rw-r--r--plugins/AVS/src/utils.cpp2
-rw-r--r--plugins/AVS/src/version.h2
-rw-r--r--plugins/AddContactPlus/src/addcontact.cpp2
-rw-r--r--plugins/AddContactPlus/src/main.cpp2
-rw-r--r--plugins/AddContactPlus/src/stdafx.cxx2
-rw-r--r--plugins/AddContactPlus/src/stdafx.h2
-rw-r--r--plugins/AddContactPlus/src/version.h2
-rw-r--r--plugins/Alarms/src/stdafx.cxx2
-rw-r--r--plugins/AsSingleWindow/src/stdafx.cxx36
-rw-r--r--plugins/AsSingleWindow/src/version.h2
-rw-r--r--plugins/AssocMgr/src/stdafx.cxx2
-rw-r--r--plugins/AuthState/src/stdafx.cxx2
-rw-r--r--plugins/AutoRun/src/stdafx.cxx2
-rw-r--r--plugins/AutoShutdown/src/stdafx.cxx2
-rw-r--r--plugins/AvatarHistory/src/stdafx.cxx2
-rw-r--r--plugins/BASS_interface/src/stdafx.cxx2
-rw-r--r--plugins/BasicHistory/src/stdafx.cxx2
-rw-r--r--plugins/Boltun/src/stdafx.cxx2
-rw-r--r--plugins/BossKeyPlus/src/BossKeyIdle.cpp2
-rw-r--r--plugins/BossKeyPlus/src/stdafx.cxx2
-rw-r--r--plugins/BuddyExpectator/src/stdafx.cxx2
-rw-r--r--plugins/BuddyPounce/src/options.cpp490
-rw-r--r--plugins/BuddyPounce/src/stdafx.cxx2
-rw-r--r--plugins/CSList/src/stdafx.cxx2
-rw-r--r--plugins/CSList/src/version.h2
-rw-r--r--plugins/ChangeKeyboardLayout/src/stdafx.cxx2
-rw-r--r--plugins/ClientChangeNotify/src/stdafx.cxx2
-rw-r--r--plugins/Clist_blind/src/clc.cpp736
-rw-r--r--plugins/Clist_blind/src/clc.h2
-rw-r--r--plugins/Clist_blind/src/clcopts.cpp2
-rw-r--r--plugins/Clist_blind/src/clistmenus.cpp2
-rw-r--r--plugins/Clist_blind/src/clistopts.cpp2
-rw-r--r--plugins/Clist_blind/src/cluiopts.cpp2
-rw-r--r--plugins/Clist_blind/src/contact.cpp2
-rw-r--r--plugins/Clist_blind/src/init.cpp2
-rw-r--r--plugins/Clist_blind/src/stdafx.cxx2
-rw-r--r--plugins/Clist_blind/src/stdafx.h2
-rw-r--r--plugins/Clist_blind/src/utils.cpp82
-rw-r--r--plugins/Clist_blind/src/version.h2
-rw-r--r--plugins/Clist_modern/src/cluiframes.cpp2
-rw-r--r--plugins/Clist_modern/src/groupmenu.cpp2
-rw-r--r--plugins/Clist_modern/src/init.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_aniavatars.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_awaymsg.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_awaymsg.h2
-rw-r--r--plugins/Clist_modern/src/modern_cache_funcs.h2
-rw-r--r--plugins/Clist_modern/src/modern_cachefuncs.cpp1490
-rw-r--r--plugins/Clist_modern/src/modern_clc.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clc.h2
-rw-r--r--plugins/Clist_modern/src/modern_clcidents.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clcitems.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clcmsgs.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clcopts.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clcpaint.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clcutils.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clist.h2
-rw-r--r--plugins/Clist_modern/src/modern_clistevents.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clistmenus.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clistmod.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clistopts.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clistsettings.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clisttray.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clui.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_clui.h2
-rw-r--r--plugins/Clist_modern/src/modern_cluiservices.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_contact.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_defsettings.h2
-rw-r--r--plugins/Clist_modern/src/modern_docking.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_global.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_image_array.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_image_array.h2
-rw-r--r--plugins/Clist_modern/src/modern_keyboard.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_rowheight_funcs.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_rowheight_funcs.h2
-rw-r--r--plugins/Clist_modern/src/modern_skinbutton.cpp1432
-rw-r--r--plugins/Clist_modern/src/modern_skinengine.cpp7514
-rw-r--r--plugins/Clist_modern/src/modern_skinopt.cpp1092
-rw-r--r--plugins/Clist_modern/src/modern_skinselector.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_skinselector.h2
-rw-r--r--plugins/Clist_modern/src/modern_static_clui.h228
-rw-r--r--plugins/Clist_modern/src/modern_statusbar_options.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_toolbar.cpp2
-rw-r--r--plugins/Clist_modern/src/modern_viewmodebar.cpp2
-rw-r--r--plugins/Clist_modern/src/stdafx.h2
-rw-r--r--plugins/Clist_nicer/src/Docking.cpp2
-rw-r--r--plugins/Clist_nicer/src/alphablend.cpp2
-rw-r--r--plugins/Clist_nicer/src/alphablend.h2
-rw-r--r--plugins/Clist_nicer/src/clc.cpp2
-rw-r--r--plugins/Clist_nicer/src/clc.h2
-rw-r--r--plugins/Clist_nicer/src/clcitems.cpp2
-rw-r--r--plugins/Clist_nicer/src/clcmsgs.cpp2
-rw-r--r--plugins/Clist_nicer/src/clcopts.cpp2
-rw-r--r--plugins/Clist_nicer/src/clcpaint.cpp2
-rw-r--r--plugins/Clist_nicer/src/clcutils.cpp2
-rw-r--r--plugins/Clist_nicer/src/clist.h2
-rw-r--r--plugins/Clist_nicer/src/clistevents.cpp2
-rw-r--r--plugins/Clist_nicer/src/clistmenus.cpp2
-rw-r--r--plugins/Clist_nicer/src/clistmod.cpp2
-rw-r--r--plugins/Clist_nicer/src/clistopts.cpp2
-rw-r--r--plugins/Clist_nicer/src/clistsettings.cpp2
-rw-r--r--plugins/Clist_nicer/src/clisttray.cpp2
-rw-r--r--plugins/Clist_nicer/src/clui.cpp3868
-rw-r--r--plugins/Clist_nicer/src/cluiframes.cpp6100
-rw-r--r--plugins/Clist_nicer/src/cluiopts.cpp2
-rw-r--r--plugins/Clist_nicer/src/cluiservices.cpp2
-rw-r--r--plugins/Clist_nicer/src/config.cpp2
-rw-r--r--plugins/Clist_nicer/src/config.h2
-rw-r--r--plugins/Clist_nicer/src/contact.cpp2
-rw-r--r--plugins/Clist_nicer/src/extBackg.cpp2
-rw-r--r--plugins/Clist_nicer/src/extBackg.h2
-rw-r--r--plugins/Clist_nicer/src/groupmenu.cpp2
-rw-r--r--plugins/Clist_nicer/src/init.cpp2
-rw-r--r--plugins/Clist_nicer/src/rowheight_funcs.cpp2
-rw-r--r--plugins/Clist_nicer/src/statusbar.cpp2
-rw-r--r--plugins/Clist_nicer/src/stdafx.cxx2
-rw-r--r--plugins/Clist_nicer/src/stdafx.h2
-rw-r--r--plugins/Clist_nicer/src/viewmodes.cpp2
-rw-r--r--plugins/Clist_nicer/src/wallpaper.cpp2
-rw-r--r--plugins/Cln_skinedit/src/main.cpp1800
-rw-r--r--plugins/Cln_skinedit/src/stdafx.cxx2
-rw-r--r--plugins/Cln_skinedit/src/stdafx.h2
-rw-r--r--plugins/CmdLine/src/stdafx.cxx2
-rw-r--r--plugins/Console/src/Console.cpp2
-rw-r--r--plugins/Console/src/init.cpp2
-rw-r--r--plugins/Console/src/stdafx.cxx2
-rw-r--r--plugins/Console/src/stdafx.h2
-rw-r--r--plugins/ContactsPlus/src/stdafx.cxx2
-rw-r--r--plugins/CountryFlags/src/stdafx.cxx2
-rw-r--r--plugins/CrashDumper/src/stdafx.cxx2
-rw-r--r--plugins/CrashDumper/src/version.h2
-rw-r--r--plugins/CryptoPP/src/stdafx.cpp2
-rw-r--r--plugins/CyrTranslit/src/stdafx.cxx2
-rw-r--r--plugins/Db3x_mmap/src/database.cpp2
-rw-r--r--plugins/Db3x_mmap/src/database.h2
-rw-r--r--plugins/Db3x_mmap/src/dbcache.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbcontacts.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbcrypt.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbevents.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbheaders.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbintf.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbintf.h2
-rw-r--r--plugins/Db3x_mmap/src/dbmodulechain.cpp2
-rw-r--r--plugins/Db3x_mmap/src/dbsettings.cpp2
-rw-r--r--plugins/Db3x_mmap/src/init.cpp2
-rw-r--r--plugins/Db3x_mmap/src/stdafx.cxx2
-rw-r--r--plugins/Db3x_mmap/src/stdafx.h2
-rw-r--r--plugins/Db3x_mmap/src/version.h2
-rw-r--r--plugins/DbEditorPP/src/stdafx.cxx2
-rw-r--r--plugins/DbEditorPP/src/utils.cpp772
-rw-r--r--plugins/DbEditorPP/src/version.h2
-rw-r--r--plugins/Db_autobackups/src/options.h2
-rw-r--r--plugins/Db_autobackups/src/stdafx.cxx2
-rw-r--r--plugins/Db_autobackups/src/version.h2
-rw-r--r--plugins/Dbx_mdbx/src/dbcheck.cpp234
-rw-r--r--plugins/Dbx_mdbx/src/dbcontacts.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/dbcrypt.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/dbevents.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/dbintf.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/dbintf.h2
-rw-r--r--plugins/Dbx_mdbx/src/dbmodulechain.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/dbsettings.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/dbutils.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/init.cpp2
-rw-r--r--plugins/Dbx_mdbx/src/stdafx.cxx2
-rw-r--r--plugins/Dbx_mdbx/src/stdafx.h2
-rw-r--r--plugins/Dbx_mdbx/src/version.h2
-rw-r--r--plugins/Dbx_sqlite/src/version.h2
-rw-r--r--plugins/Exchange/src/stdafx.cxx2
-rw-r--r--plugins/ExternalAPI/m_assocmgr.h2
-rw-r--r--plugins/ExternalAPI/m_dbeditorpp.h2
-rw-r--r--plugins/ExternalAPI/m_proto_listeningto.h2
-rw-r--r--plugins/ExternalAPI/m_shutdown.h2
-rw-r--r--plugins/ExternalAPI/m_skin_eng.h2
-rw-r--r--plugins/ExternalAPI/m_stopspam.h2
-rw-r--r--plugins/ExternalAPI/m_userinfoex.h2
-rw-r--r--plugins/FTPFileYM/src/stdafx.cxx2
-rw-r--r--plugins/FavContacts/src/stdafx.cxx2
-rw-r--r--plugins/FingerprintNG/src/fingerprint.cpp1826
-rw-r--r--plugins/FingerprintNG/src/main.cpp2
-rw-r--r--plugins/FingerprintNG/src/masks.cpp2
-rw-r--r--plugins/FingerprintNG/src/options.cpp2
-rw-r--r--plugins/FingerprintNG/src/stdafx.cxx2
-rw-r--r--plugins/FingerprintNG/src/stdafx.h2
-rw-r--r--plugins/FingerprintNG/src/version.h4
-rw-r--r--plugins/FloatingContacts/src/stdafx.cxx2
-rw-r--r--plugins/Folders/src/stdafx.cxx2
-rw-r--r--plugins/Folders/src/version.h2
-rw-r--r--plugins/HTTPServer/src/stdafx.cpp2
-rw-r--r--plugins/HistoryLinkListPlus/src/stdafx.cxx2
-rw-r--r--plugins/HistoryStats/src/stdafx.cxx2
-rw-r--r--plugins/HistorySweeperLight/src/stdafx.cxx2
-rw-r--r--plugins/HwHotKeys/src/stdafx.cxx2
-rw-r--r--plugins/HwHotKeys/src/version.h74
-rw-r--r--plugins/IEHistory/src/version.h78
-rw-r--r--plugins/IEView/src/stdafx.cxx2
-rw-r--r--plugins/IgnoreState/src/stdafx.cxx2
-rw-r--r--plugins/Import/src/dbrw/dbcontacts.cpp136
-rw-r--r--plugins/Import/src/dbrw/dbevents.cpp390
-rw-r--r--plugins/Import/src/dbrw/dbintf.cpp174
-rw-r--r--plugins/Import/src/dbrw/dbintf.h318
-rw-r--r--plugins/Import/src/dbrw/dbrw.cpp210
-rw-r--r--plugins/Import/src/dbrw/dbrw.h60
-rw-r--r--plugins/Import/src/dbrw/dbsettings.cpp342
-rw-r--r--plugins/Import/src/dbrw/dbsql.cpp324
-rw-r--r--plugins/Import/src/import.cpp2
-rw-r--r--plugins/Import/src/main.cpp2
-rw-r--r--plugins/Import/src/mcontacts.cpp644
-rw-r--r--plugins/Import/src/miranda.cpp2
-rw-r--r--plugins/Import/src/patterns.cpp1464
-rw-r--r--plugins/Import/src/progress.cpp2
-rw-r--r--plugins/Import/src/stdafx.cxx2
-rw-r--r--plugins/Import/src/stdafx.h2
-rw-r--r--plugins/Import/src/textjson.cpp538
-rw-r--r--plugins/Import/src/ui.cpp260
-rw-r--r--plugins/Import/src/utils.cpp2
-rw-r--r--plugins/Import/src/version.h2
-rw-r--r--plugins/Import/src/wizard.cpp2
-rw-r--r--plugins/KeyboardNotify/src/stdafx.cxx2
-rw-r--r--plugins/ListeningTo/src/stdafx.cxx2
-rw-r--r--plugins/MagneticWindows/src/Version.h2
-rw-r--r--plugins/MagneticWindows/src/stdafx.cxx36
-rw-r--r--plugins/MenuItemEx/src/stdafx.cxx2
-rw-r--r--plugins/MessageState/src/stdafx.cxx2
-rw-r--r--plugins/MessageState/src/version.h2
-rw-r--r--plugins/MimCmd/src/stdafx.cxx2
-rw-r--r--plugins/MirFox/src/version.h2
-rw-r--r--plugins/MirLua/src/version.h2
-rw-r--r--plugins/MirandaG15/src/stdafx.cxx2
-rw-r--r--plugins/MobileState/src/clients.h2
-rw-r--r--plugins/MobileState/src/main.cpp2
-rw-r--r--plugins/MobileState/src/stdafx.cxx2
-rw-r--r--plugins/MobileState/src/stdafx.h2
-rw-r--r--plugins/MobileState/src/version.h2
-rw-r--r--plugins/MsgPopup/src/stdafx.cxx2
-rw-r--r--plugins/Msg_Export/src/stdafx.cxx2
-rw-r--r--plugins/Msg_Export/src/version.h2
-rw-r--r--plugins/MyDetails/src/stdafx.cxx2
-rw-r--r--plugins/NewAwaySysMod/src/stdafx.cxx2
-rw-r--r--plugins/NewEventNotify/src/stdafx.cxx2
-rw-r--r--plugins/NewStory/src/stdafx.cxx34
-rw-r--r--plugins/NewStory/src/version.h2
-rw-r--r--plugins/NewXstatusNotify/src/stdafx.cxx2
-rw-r--r--plugins/New_GPG/src/globals.h84
-rw-r--r--plugins/New_GPG/src/gpg_wrapper.cpp336
-rw-r--r--plugins/New_GPG/src/gpg_wrapper.h132
-rw-r--r--plugins/New_GPG/src/icons.cpp110
-rw-r--r--plugins/New_GPG/src/init.cpp482
-rw-r--r--plugins/New_GPG/src/jabber_account.h104
-rw-r--r--plugins/New_GPG/src/log.cpp98
-rw-r--r--plugins/New_GPG/src/log.h64
-rw-r--r--plugins/New_GPG/src/main.cpp1308
-rw-r--r--plugins/New_GPG/src/messages.cpp1576
-rw-r--r--plugins/New_GPG/src/metacontacts.cpp58
-rw-r--r--plugins/New_GPG/src/metacontacts.h42
-rw-r--r--plugins/New_GPG/src/options.cpp2310
-rw-r--r--plugins/New_GPG/src/options.h42
-rw-r--r--plugins/New_GPG/src/srmm.cpp156
-rw-r--r--plugins/New_GPG/src/stdafx.cxx36
-rw-r--r--plugins/New_GPG/src/stdafx.h196
-rw-r--r--plugins/New_GPG/src/ui.cpp1804
-rw-r--r--plugins/New_GPG/src/ui.h136
-rw-r--r--plugins/New_GPG/src/utilities.cpp2856
-rw-r--r--plugins/New_GPG/src/utilities.h108
-rw-r--r--plugins/New_GPG/src/version.h2
-rw-r--r--plugins/NoHistory/src/stdafx.cxx2
-rw-r--r--plugins/NoHistory/src/version.h2
-rw-r--r--plugins/NotesAndReminders/src/stdafx.cxx34
-rw-r--r--plugins/NotifyAnything/src/stdafx.cxx2
-rw-r--r--plugins/Nudge/src/stdafx.cxx2
-rw-r--r--plugins/OpenFolder/src/stdafx.cxx2
-rw-r--r--plugins/PackUpdater/Src/Events.cpp2
-rw-r--r--plugins/PackUpdater/Src/Notifications.cpp2
-rw-r--r--plugins/PackUpdater/Src/Notifications.h2
-rw-r--r--plugins/PackUpdater/Src/Options.cpp2
-rw-r--r--plugins/PackUpdater/Src/PackUpdater.cpp2
-rw-r--r--plugins/PackUpdater/Src/Utils.cpp2
-rw-r--r--plugins/PackUpdater/Src/stdafx.cxx2
-rw-r--r--plugins/PackUpdater/Src/stdafx.h2
-rw-r--r--plugins/PackUpdater/Src/version.h2
-rw-r--r--plugins/PasteIt/src/stdafx.cxx2
-rw-r--r--plugins/Ping/src/stdafx.cxx2
-rw-r--r--plugins/PluginUpdater/pu_stub/src/stdafx.cxx36
-rw-r--r--plugins/PluginUpdater/src/stdafx.cxx2
-rw-r--r--plugins/PluginUpdater/src/version.h2
-rw-r--r--plugins/Popup/src/stdafx.cxx2
-rw-r--r--plugins/ProfileManager/src/stdafx.cxx2
-rw-r--r--plugins/ProxySwitch/src/stdafx.cxx34
-rw-r--r--plugins/ProxySwitch/src/version.h2
-rw-r--r--plugins/QuickContacts/src/dialog.cpp1706
-rw-r--r--plugins/QuickContacts/src/stdafx.cxx2
-rw-r--r--plugins/QuickMessages/src/stdafx.cxx2
-rw-r--r--plugins/QuickReplies/src/stdafx.cxx2
-rw-r--r--plugins/QuickSearch/src/main.cpp376
-rw-r--r--plugins/QuickSearch/src/options.cpp1038
-rw-r--r--plugins/QuickSearch/src/stdafx.cxx34
-rw-r--r--plugins/QuickSearch/src/utils.cpp1112
-rw-r--r--plugins/QuickSearch/src/version.h2
-rw-r--r--plugins/QuickSearch/src/window.cpp1752
-rw-r--r--plugins/QuickSearch/src/window_misc.cpp1750
-rw-r--r--plugins/QuickSearch/src/window_row.cpp448
-rw-r--r--plugins/Rate/src/stdafx.cxx2
-rw-r--r--plugins/RecentContacts/src/stdafx.cxx2
-rw-r--r--plugins/RemovePersonalSettings/src/rps.cpp2
-rw-r--r--plugins/RemovePersonalSettings/src/stdafx.cxx2
-rw-r--r--plugins/Restart/src/stdafx.cxx2
-rw-r--r--plugins/Scriver/src/stdafx.cxx2
-rw-r--r--plugins/Scriver/src/version.h2
-rw-r--r--plugins/SecureIM/src/stdafx.cpp2
-rw-r--r--plugins/SeenPlugin/src/stdafx.cxx2
-rw-r--r--plugins/SendScreenshotPlus/src/CSend.cpp1226
-rw-r--r--plugins/SendScreenshotPlus/src/CSend.h274
-rw-r--r--plugins/SendScreenshotPlus/src/CSendCloduFile.h100
-rw-r--r--plugins/SendScreenshotPlus/src/CSendCloudFile.cpp156
-rw-r--r--plugins/SendScreenshotPlus/src/CSendEmail.cpp402
-rw-r--r--plugins/SendScreenshotPlus/src/CSendEmail.h106
-rw-r--r--plugins/SendScreenshotPlus/src/CSendFTPFile.cpp186
-rw-r--r--plugins/SendScreenshotPlus/src/CSendFTPFile.h96
-rw-r--r--plugins/SendScreenshotPlus/src/CSendFile.cpp100
-rw-r--r--plugins/SendScreenshotPlus/src/CSendFile.h86
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp234
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHTTPServer.h110
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp236
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h98
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp172
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHost_imgur.h60
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp210
-rw-r--r--plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h60
-rw-r--r--plugins/SendScreenshotPlus/src/Main.cpp672
-rw-r--r--plugins/SendScreenshotPlus/src/UMainForm.cpp2248
-rw-r--r--plugins/SendScreenshotPlus/src/UMainForm.h292
-rw-r--r--plugins/SendScreenshotPlus/src/Utils.cpp824
-rw-r--r--plugins/SendScreenshotPlus/src/Utils.h146
-rw-r--r--plugins/SendScreenshotPlus/src/ctrl_button.h66
-rw-r--r--plugins/SendScreenshotPlus/src/dlg_msgbox.cpp1428
-rw-r--r--plugins/SendScreenshotPlus/src/dlg_msgbox.h2
-rw-r--r--plugins/SendScreenshotPlus/src/stdafx.cxx2
-rw-r--r--plugins/SendScreenshotPlus/src/stdafx.h372
-rw-r--r--plugins/Sessions/Src/Import.cpp268
-rw-r--r--plugins/Sessions/Src/LoadSessions.cpp454
-rw-r--r--plugins/Sessions/Src/SaveSessions.cpp388
-rw-r--r--plugins/Sessions/Src/stdafx.cxx2
-rw-r--r--plugins/ShellExt/src/stdafx.cxx2
-rw-r--r--plugins/SimpleAR/src/stdafx.cxx2
-rw-r--r--plugins/SimpleStatusMsg/src/awaymsg.cpp2
-rw-r--r--plugins/SimpleStatusMsg/src/stdafx.cxx2
-rw-r--r--plugins/SkypeStatusChange/src/stdafx.cxx2
-rw-r--r--plugins/SmileyAdd/src/options.cpp2
-rw-r--r--plugins/SmileyAdd/src/options.h2
-rw-r--r--plugins/SmileyAdd/src/services.cpp2
-rw-r--r--plugins/SmileyAdd/src/smileys.cpp1954
-rw-r--r--plugins/SmileyAdd/src/smileys.h2
-rw-r--r--plugins/SmileyAdd/src/stdafx.cxx2
-rw-r--r--plugins/SmileyAdd/src/version.h2
-rw-r--r--plugins/Spamotron/src/stdafx.cxx2
-rw-r--r--plugins/SpellChecker/src/stdafx.cxx2
-rw-r--r--plugins/SplashScreen/src/stdafx.cxx2
-rw-r--r--plugins/StartPosition/src/main.cpp2
-rw-r--r--plugins/StartPosition/src/options.h186
-rw-r--r--plugins/StartPosition/src/startposition.h68
-rw-r--r--plugins/StartPosition/src/stdafx.cxx2
-rw-r--r--plugins/StartPosition/src/stdafx.h2
-rw-r--r--plugins/StartPosition/src/version.h2
-rw-r--r--plugins/StartupSilence/src/main.cpp2
-rw-r--r--plugins/StartupSilence/src/stdafx.cxx2
-rw-r--r--plugins/StartupSilence/src/version.h2
-rw-r--r--plugins/StatusChange/src/stdafx.cxx2
-rw-r--r--plugins/StatusManager/src/stdafx.cxx36
-rw-r--r--plugins/StatusManager/src/version.h2
-rw-r--r--plugins/StopSpamMod/src/stdafx.cxx2
-rw-r--r--plugins/StopSpamMod/src/version.h2
-rw-r--r--plugins/StopSpamPlus/src/stdafx.cxx2
-rw-r--r--plugins/TabSRMM/TabSRMM_icons/version.h22
-rw-r--r--plugins/TabSRMM/src/ImageDataObject.cpp2
-rw-r--r--plugins/TabSRMM/src/TSButton.cpp2
-rw-r--r--plugins/TabSRMM/src/chat.h2
-rw-r--r--plugins/TabSRMM/src/chat_log.cpp2
-rw-r--r--plugins/TabSRMM/src/chat_main.cpp2
-rw-r--r--plugins/TabSRMM/src/chat_manager.cpp2
-rw-r--r--plugins/TabSRMM/src/chat_options.cpp2
-rw-r--r--plugins/TabSRMM/src/chat_tools.cpp2
-rw-r--r--plugins/TabSRMM/src/contactcache.cpp956
-rw-r--r--plugins/TabSRMM/src/contactcache.h2
-rw-r--r--plugins/TabSRMM/src/container.cpp2
-rw-r--r--plugins/TabSRMM/src/containeroptions.cpp2
-rw-r--r--plugins/TabSRMM/src/controls.cpp2
-rw-r--r--plugins/TabSRMM/src/controls.h2
-rw-r--r--plugins/TabSRMM/src/eventpopups.cpp2
-rw-r--r--plugins/TabSRMM/src/functions.h2
-rw-r--r--plugins/TabSRMM/src/generic_msghandlers.cpp2716
-rw-r--r--plugins/TabSRMM/src/globals.cpp2
-rw-r--r--plugins/TabSRMM/src/globals.h2
-rw-r--r--plugins/TabSRMM/src/hotkeyhandler.cpp2
-rw-r--r--plugins/TabSRMM/src/infopanel.cpp2
-rw-r--r--plugins/TabSRMM/src/infopanel.h2
-rw-r--r--plugins/TabSRMM/src/mim.cpp976
-rw-r--r--plugins/TabSRMM/src/mim.h2
-rw-r--r--plugins/TabSRMM/src/modplus.cpp2
-rw-r--r--plugins/TabSRMM/src/msgdialog.cpp2
-rw-r--r--plugins/TabSRMM/src/msgdlgother.cpp5716
-rw-r--r--plugins/TabSRMM/src/msgdlgutils.cpp2
-rw-r--r--plugins/TabSRMM/src/msgdlgutils.h2
-rw-r--r--plugins/TabSRMM/src/msglog.cpp2
-rw-r--r--plugins/TabSRMM/src/msgoptions.cpp2
-rw-r--r--plugins/TabSRMM/src/msgs.cpp2
-rw-r--r--plugins/TabSRMM/src/msgs.h2
-rw-r--r--plugins/TabSRMM/src/muchighlight.cpp2
-rw-r--r--plugins/TabSRMM/src/muchighlight.h2
-rw-r--r--plugins/TabSRMM/src/nen.h2
-rw-r--r--plugins/TabSRMM/src/selectcontainer.cpp2
-rw-r--r--plugins/TabSRMM/src/sendlater.cpp2
-rw-r--r--plugins/TabSRMM/src/sendlater.h2
-rw-r--r--plugins/TabSRMM/src/sendqueue.cpp2
-rw-r--r--plugins/TabSRMM/src/sendqueue.h2
-rw-r--r--plugins/TabSRMM/src/sidebar.cpp2
-rw-r--r--plugins/TabSRMM/src/sidebar.h2
-rw-r--r--plugins/TabSRMM/src/srmm.cpp288
-rw-r--r--plugins/TabSRMM/src/stdafx.cxx2
-rw-r--r--plugins/TabSRMM/src/stdafx.h2
-rw-r--r--plugins/TabSRMM/src/tabctrl.cpp2
-rw-r--r--plugins/TabSRMM/src/taskbar.cpp2
-rw-r--r--plugins/TabSRMM/src/taskbar.h2
-rw-r--r--plugins/TabSRMM/src/templates.cpp248
-rw-r--r--plugins/TabSRMM/src/themeio.cpp2
-rw-r--r--plugins/TabSRMM/src/themes.cpp2
-rw-r--r--plugins/TabSRMM/src/themes.h2
-rw-r--r--plugins/TabSRMM/src/userprefs.cpp2
-rw-r--r--plugins/TabSRMM/src/utils.cpp2
-rw-r--r--plugins/TabSRMM/src/utils.h2
-rw-r--r--plugins/TabSRMM/src/version.h2
-rw-r--r--plugins/TabSRMM/src/warning.cpp656
-rw-r--r--plugins/TipperYM/src/stdafx.cxx2
-rw-r--r--plugins/Toaster/src/version.h2
-rw-r--r--plugins/TooltipNotify/src/stdafx.cxx2
-rw-r--r--plugins/TopToolBar/src/stdafx.cxx2
-rw-r--r--plugins/TrafficCounter/src/stdafx.cxx2
-rw-r--r--plugins/TranslitSwitcher/src/stdafx.cxx2
-rw-r--r--plugins/TranslitSwitcher/src/version.h2
-rw-r--r--plugins/UserGuide/src/stdafx.cxx2
-rw-r--r--plugins/UserInfoEx/src/dlg_msgbox.cpp2
-rw-r--r--plugins/UserInfoEx/src/dlg_msgbox.h2
-rw-r--r--plugins/UserInfoEx/src/stdafx.cxx2
-rw-r--r--plugins/Variables/src/stdafx.cxx2
-rw-r--r--plugins/VoiceService/src/stdafx.cxx34
-rw-r--r--plugins/VoiceService/src/version.h2
-rw-r--r--plugins/Watrack_MPD/src/stdafx.cxx2
-rw-r--r--plugins/WhenWasIt/src/dlg_handlers.cpp2
-rw-r--r--plugins/WhenWasIt/src/stdafx.cxx2
-rw-r--r--plugins/WhoUsesMyFiles/src/options.cpp516
-rw-r--r--plugins/WhoUsesMyFiles/src/stdafx.cxx2
-rw-r--r--plugins/WinterSpeak/src/stdafx.cxx2
-rw-r--r--plugins/XSoundNotify/src/stdafx.cxx2
-rw-r--r--plugins/YARelay/src/stdafx.cxx2
-rw-r--r--plugins/ZeroNotification/src/options.cpp274
-rw-r--r--plugins/ZeroNotification/src/stdafx.cxx2
-rw-r--r--plugins/ZeroSwitch/src/stdafx.cxx2
-rw-r--r--plugins/helpers/commonheaders.h2
-rw-r--r--plugins/wbOSD/src/stdafx.cxx2
468 files changed, 40976 insertions, 40976 deletions
diff --git a/plugins/AVS/src/acc.cpp b/plugins/AVS/src/acc.cpp
index 359a5660c3..e637b83d23 100644
--- a/plugins/AVS/src/acc.cpp
+++ b/plugins/AVS/src/acc.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-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/cache.cpp b/plugins/AVS/src/cache.cpp
index c60c8cd05b..318834644d 100644
--- a/plugins/AVS/src/cache.cpp
+++ b/plugins/AVS/src/cache.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)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/contact_ava.cpp b/plugins/AVS/src/contact_ava.cpp
index d2d8c08399..2f67afc693 100644
--- a/plugins/AVS/src/contact_ava.cpp
+++ b/plugins/AVS/src/contact_ava.cpp
@@ -1,263 +1,263 @@
-/*
-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"
-
-class CContactAvatarDlg : public CDlgBase
-{
- MCONTACT m_hContact;
- HANDLE m_hHook = 0;
-
- UI_MESSAGE_MAP(CContactAvatarDlg, CDlgBase);
- UI_MESSAGE(WM_DRAWITEM, OnDrawItem);
- UI_MESSAGE(DM_AVATARCHANGED, OnAvatarChanged);
- UI_MESSAGE_MAP_END();
-
- INT_PTR OnAvatarChanged(UINT, WPARAM, LPARAM)
- {
- InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE);
- return 0;
- }
-
- INT_PTR OnDrawItem(UINT, WPARAM, LPARAM lParam)
- {
- LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
- if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
- AVATARDRAWREQUEST avdrq = { 0 };
- GetClientRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), &avdrq.rcDraw);
-
- FillRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNFACE));
-
- avdrq.hContact = m_hContact;
- avdrq.hTargetDC = dis->hDC;
- avdrq.dwFlags |= AVDRQ_DRAWBORDER;
- avdrq.clrBorder = GetSysColor(COLOR_BTNTEXT);
- avdrq.radius = 6;
- if (!CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq)) {
- // Get text rectangle
- RECT rc = avdrq.rcDraw;
- rc.top += 10;
- rc.bottom -= 10;
- rc.left += 10;
- rc.right -= 10;
-
- // Calc text size
- RECT rc_ret = rc;
- DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc_ret,
- DT_WORDBREAK | DT_NOPREFIX | DT_CENTER | DT_CALCRECT);
-
- // Calc needed size
- rc.top += ((rc.bottom - rc.top) - (rc_ret.bottom - rc_ret.top)) / 2;
- rc.bottom = rc.top + (rc_ret.bottom - rc_ret.top);
- DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc,
- DT_WORDBREAK | DT_NOPREFIX | DT_CENTER);
- }
-
- FrameRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNSHADOW));
- }
- return TRUE;
- }
-
- void ReloadAvatar()
- {
- SaveTransparentData(m_hwnd, m_hContact, chkProtect.IsChecked());
- ChangeAvatar(m_hContact, true);
- OnAvatarChanged(0, 0, 0);
- }
-
- void SetAvatarName()
- {
- uint8_t is_locked = db_get_b(m_hContact, "ContactPhoto", "Locked", 0);
-
- wchar_t szFinalName[MAX_PATH];
- szFinalName[0] = 0;
-
- DBVARIANT dbv = {};
- if (is_locked && !db_get_ws(m_hContact, "ContactPhoto", "Backup", &dbv)) {
- MyPathToAbsolute(dbv.pwszVal, szFinalName);
- db_free(&dbv);
- }
- else if (!db_get_ws(m_hContact, "ContactPhoto", "RFile", &dbv)) {
- MyPathToAbsolute(dbv.pwszVal, szFinalName);
- db_free(&dbv);
- }
- else if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
- MyPathToAbsolute(dbv.pwszVal, szFinalName);
- db_free(&dbv);
- }
- szFinalName[MAX_PATH - 1] = 0;
- SetDlgItemText(m_hwnd, IDC_AVATARNAME, szFinalName);
- }
-
- CCtrlSpin spin1, spin2;
- CCtrlCheck chkProtect, chkMakeTrans, chkHide;
- CCtrlButton btnUseDefault, btnChange, btnReset, btnDefault;
-
-public:
- CContactAvatarDlg(MCONTACT hContact) :
- CDlgBase(g_plugin, IDD_AVATAROPTIONS),
- m_hContact(hContact),
- spin1(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2),
- spin2(this, IDC_BKG_COLOR_DIFFERENCE_SPIN, 100),
- btnReset(this, IDC_RESET),
- btnChange(this, IDC_CHANGE),
- btnDefault(this, IDC_DELETE),
- btnUseDefault(this, ID_USE_DEFAULTS),
- chkHide(this, IDC_HIDEAVATAR),
- chkProtect(this, IDC_PROTECTAVATAR),
- chkMakeTrans(this, IDC_MAKETRANSPBKG)
- {
- btnReset.OnClick = Callback(this, &CContactAvatarDlg::onClick_Reset);
- btnChange.OnClick = Callback(this, &CContactAvatarDlg::onClick_Change);
- btnDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_Default);
- btnUseDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_UseDefault);
-
- chkProtect.OnChange = Callback(this, &CContactAvatarDlg::onChange_Protect);
- chkMakeTrans.OnChange = Callback(this, &CContactAvatarDlg::onChange_MakeTrans);
- }
-
- bool OnInitDialog() override
- {
- m_hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED);
-
- if (m_hContact) {
- wchar_t szTitle[512];
- mir_snwprintf(szTitle, TranslateT("Set avatar options for %s"), Clist_GetContactDisplayName(m_hContact));
- SetWindowText(m_hwnd, szTitle);
- }
-
- SetAvatarName();
- ShowWindow(m_hwnd, SW_SHOWNORMAL);
- OnAvatarChanged(0, 0, 0);
- chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked", 0));
- chkHide.SetState(Contact::IsHidden(m_hContact));
-
- LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact));
- SendMessage(m_hwnd, WM_SETICON, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_AVATAR));
- return true;
- }
-
- bool OnApply() override
- {
- bool locked = chkProtect.IsChecked();
- bool hidden = chkHide.IsChecked();
-
- SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden);
- if (hidden != Contact::IsHidden(m_hContact))
- Contact::Hide(m_hContact, hidden);
-
- if (!locked && db_get_b(m_hContact, "ContactPhoto", "NeedUpdate", 0))
- QueueAdd(m_hContact);
- return true;
- }
-
- void OnDestroy() override
- {
- UnhookEvent(m_hHook);
- }
-
- void onClick_UseDefault(CCtrlButton *)
- {
- MCONTACT hContact = GetContactThatHaveTheAvatar(m_hContact);
-
- db_unset(hContact, "ContactPhoto", "MakeTransparentBkg");
- db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints");
- db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff");
-
- LoadTransparentData(m_hwnd, hContact);
- ReloadAvatar();
- }
-
- void onClick_Change(CCtrlButton *)
- {
- SetAvatar(m_hContact, 0);
- SetAvatarName();
- chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked"));
- }
-
- void onClick_Reset(CCtrlButton *)
- {
- ProtectAvatar(m_hContact, 0);
- if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
- DBVARIANT dbv = { 0 };
- if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
- DeleteFileW(dbv.pwszVal);
- db_free(&dbv);
- }
- }
- db_unset(m_hContact, "ContactPhoto", "Locked");
- db_unset(m_hContact, "ContactPhoto", "Backup");
- db_unset(m_hContact, "ContactPhoto", "RFile");
- db_unset(m_hContact, "ContactPhoto", "File");
- db_unset(m_hContact, "ContactPhoto", "Format");
-
- db_unset(m_hContact, Proto_GetBaseAccountName(m_hContact), "AvatarHash");
- DeleteAvatarFromCache(m_hContact, FALSE);
-
- QueueAdd(m_hContact);
- DestroyWindow(m_hwnd);
- }
-
- void onClick_Default(CCtrlButton *)
- {
- if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
- DBVARIANT dbv = { 0 };
- ProtectAvatar(m_hContact, 0);
- if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
- DeleteFileW(dbv.pwszVal);
- db_free(&dbv);
- }
- }
- db_unset(m_hContact, "ContactPhoto", "Locked");
- db_unset(m_hContact, "ContactPhoto", "Backup");
- db_unset(m_hContact, "ContactPhoto", "RFile");
- db_unset(m_hContact, "ContactPhoto", "File");
- db_unset(m_hContact, "ContactPhoto", "Format");
- DeleteAvatarFromCache(m_hContact, FALSE);
- SetAvatarName();
- OnAvatarChanged(0, 0, 0);
- }
-
- void onChange_Protect(CCtrlCheck *)
- {
- ProtectAvatar(m_hContact, chkProtect.IsChecked());
- }
-
- void onChange_MakeTrans(CCtrlCheck *)
- {
- bool enable = chkMakeTrans.IsChecked();
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable);
-
- ReloadAvatar();
- }
-};
-
-INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam)
-{
- if (wParam) {
- auto *pDlg = new CContactAvatarDlg(wParam);
- if (lParam)
- pDlg->SetParent((HWND)lParam);
- pDlg->Create();
- }
- return 0;
-}
+/*
+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"
+
+class CContactAvatarDlg : public CDlgBase
+{
+ MCONTACT m_hContact;
+ HANDLE m_hHook = 0;
+
+ UI_MESSAGE_MAP(CContactAvatarDlg, CDlgBase);
+ UI_MESSAGE(WM_DRAWITEM, OnDrawItem);
+ UI_MESSAGE(DM_AVATARCHANGED, OnAvatarChanged);
+ UI_MESSAGE_MAP_END();
+
+ INT_PTR OnAvatarChanged(UINT, WPARAM, LPARAM)
+ {
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE);
+ return 0;
+ }
+
+ INT_PTR OnDrawItem(UINT, WPARAM, LPARAM lParam)
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
+ AVATARDRAWREQUEST avdrq = { 0 };
+ GetClientRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), &avdrq.rcDraw);
+
+ FillRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNFACE));
+
+ avdrq.hContact = m_hContact;
+ avdrq.hTargetDC = dis->hDC;
+ avdrq.dwFlags |= AVDRQ_DRAWBORDER;
+ avdrq.clrBorder = GetSysColor(COLOR_BTNTEXT);
+ avdrq.radius = 6;
+ if (!CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq)) {
+ // Get text rectangle
+ RECT rc = avdrq.rcDraw;
+ rc.top += 10;
+ rc.bottom -= 10;
+ rc.left += 10;
+ rc.right -= 10;
+
+ // Calc text size
+ RECT rc_ret = rc;
+ DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc_ret,
+ DT_WORDBREAK | DT_NOPREFIX | DT_CENTER | DT_CALCRECT);
+
+ // Calc needed size
+ rc.top += ((rc.bottom - rc.top) - (rc_ret.bottom - rc_ret.top)) / 2;
+ rc.bottom = rc.top + (rc_ret.bottom - rc_ret.top);
+ DrawText(dis->hDC, TranslateT("Contact has no avatar"), -1, &rc,
+ DT_WORDBREAK | DT_NOPREFIX | DT_CENTER);
+ }
+
+ FrameRect(dis->hDC, &avdrq.rcDraw, GetSysColorBrush(COLOR_BTNSHADOW));
+ }
+ return TRUE;
+ }
+
+ void ReloadAvatar()
+ {
+ SaveTransparentData(m_hwnd, m_hContact, chkProtect.IsChecked());
+ ChangeAvatar(m_hContact, true);
+ OnAvatarChanged(0, 0, 0);
+ }
+
+ void SetAvatarName()
+ {
+ uint8_t is_locked = db_get_b(m_hContact, "ContactPhoto", "Locked", 0);
+
+ wchar_t szFinalName[MAX_PATH];
+ szFinalName[0] = 0;
+
+ DBVARIANT dbv = {};
+ if (is_locked && !db_get_ws(m_hContact, "ContactPhoto", "Backup", &dbv)) {
+ MyPathToAbsolute(dbv.pwszVal, szFinalName);
+ db_free(&dbv);
+ }
+ else if (!db_get_ws(m_hContact, "ContactPhoto", "RFile", &dbv)) {
+ MyPathToAbsolute(dbv.pwszVal, szFinalName);
+ db_free(&dbv);
+ }
+ else if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ MyPathToAbsolute(dbv.pwszVal, szFinalName);
+ db_free(&dbv);
+ }
+ szFinalName[MAX_PATH - 1] = 0;
+ SetDlgItemText(m_hwnd, IDC_AVATARNAME, szFinalName);
+ }
+
+ CCtrlSpin spin1, spin2;
+ CCtrlCheck chkProtect, chkMakeTrans, chkHide;
+ CCtrlButton btnUseDefault, btnChange, btnReset, btnDefault;
+
+public:
+ CContactAvatarDlg(MCONTACT hContact) :
+ CDlgBase(g_plugin, IDD_AVATAROPTIONS),
+ m_hContact(hContact),
+ spin1(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2),
+ spin2(this, IDC_BKG_COLOR_DIFFERENCE_SPIN, 100),
+ btnReset(this, IDC_RESET),
+ btnChange(this, IDC_CHANGE),
+ btnDefault(this, IDC_DELETE),
+ btnUseDefault(this, ID_USE_DEFAULTS),
+ chkHide(this, IDC_HIDEAVATAR),
+ chkProtect(this, IDC_PROTECTAVATAR),
+ chkMakeTrans(this, IDC_MAKETRANSPBKG)
+ {
+ btnReset.OnClick = Callback(this, &CContactAvatarDlg::onClick_Reset);
+ btnChange.OnClick = Callback(this, &CContactAvatarDlg::onClick_Change);
+ btnDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_Default);
+ btnUseDefault.OnClick = Callback(this, &CContactAvatarDlg::onClick_UseDefault);
+
+ chkProtect.OnChange = Callback(this, &CContactAvatarDlg::onChange_Protect);
+ chkMakeTrans.OnChange = Callback(this, &CContactAvatarDlg::onChange_MakeTrans);
+ }
+
+ bool OnInitDialog() override
+ {
+ m_hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED);
+
+ if (m_hContact) {
+ wchar_t szTitle[512];
+ mir_snwprintf(szTitle, TranslateT("Set avatar options for %s"), Clist_GetContactDisplayName(m_hContact));
+ SetWindowText(m_hwnd, szTitle);
+ }
+
+ SetAvatarName();
+ ShowWindow(m_hwnd, SW_SHOWNORMAL);
+ OnAvatarChanged(0, 0, 0);
+ chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked", 0));
+ chkHide.SetState(Contact::IsHidden(m_hContact));
+
+ LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact));
+ SendMessage(m_hwnd, WM_SETICON, IMAGE_ICON, (LPARAM)g_plugin.getIcon(IDI_AVATAR));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ bool locked = chkProtect.IsChecked();
+ bool hidden = chkHide.IsChecked();
+
+ SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden);
+ if (hidden != Contact::IsHidden(m_hContact))
+ Contact::Hide(m_hContact, hidden);
+
+ if (!locked && db_get_b(m_hContact, "ContactPhoto", "NeedUpdate", 0))
+ QueueAdd(m_hContact);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ UnhookEvent(m_hHook);
+ }
+
+ void onClick_UseDefault(CCtrlButton *)
+ {
+ MCONTACT hContact = GetContactThatHaveTheAvatar(m_hContact);
+
+ db_unset(hContact, "ContactPhoto", "MakeTransparentBkg");
+ db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints");
+ db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff");
+
+ LoadTransparentData(m_hwnd, hContact);
+ ReloadAvatar();
+ }
+
+ void onClick_Change(CCtrlButton *)
+ {
+ SetAvatar(m_hContact, 0);
+ SetAvatarName();
+ chkProtect.SetState(db_get_b(m_hContact, "ContactPhoto", "Locked"));
+ }
+
+ void onClick_Reset(CCtrlButton *)
+ {
+ ProtectAvatar(m_hContact, 0);
+ if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFileW(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+
+ db_unset(m_hContact, Proto_GetBaseAccountName(m_hContact), "AvatarHash");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+
+ QueueAdd(m_hContact);
+ DestroyWindow(m_hwnd);
+ }
+
+ void onClick_Default(CCtrlButton *)
+ {
+ if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ ProtectAvatar(m_hContact, 0);
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFileW(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+ SetAvatarName();
+ OnAvatarChanged(0, 0, 0);
+ }
+
+ void onChange_Protect(CCtrlCheck *)
+ {
+ ProtectAvatar(m_hContact, chkProtect.IsChecked());
+ }
+
+ void onChange_MakeTrans(CCtrlCheck *)
+ {
+ bool enable = chkMakeTrans.IsChecked();
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable);
+
+ ReloadAvatar();
+ }
+};
+
+INT_PTR ContactOptions(WPARAM wParam, LPARAM lParam)
+{
+ if (wParam) {
+ auto *pDlg = new CContactAvatarDlg(wParam);
+ if (lParam)
+ pDlg->SetParent((HWND)lParam);
+ pDlg->Create();
+ }
+ return 0;
+}
diff --git a/plugins/AVS/src/main.cpp b/plugins/AVS/src/main.cpp
index 2ad7a85047..bab379ba4d 100644
--- a/plugins/AVS/src/main.cpp
+++ b/plugins/AVS/src/main.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-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/options.cpp b/plugins/AVS/src/options.cpp
index 83348a3f7f..f281c686ed 100644
--- a/plugins/AVS/src/options.cpp
+++ b/plugins/AVS/src/options.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-04 Miranda ICQ/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"
-
-extern OBJLIST<protoPicCacheEntry> g_ProtoPictures;
-extern HANDLE hEventChanged;
-
-static BOOL dialoginit = TRUE;
-
-static void RemoveProtoPic(protoPicCacheEntry *pce)
-{
- if (pce == nullptr)
- return;
-
- db_unset(0, PPICT_MODULE, pce->szProtoname);
-
- // common for all accounts
- if (pce->cacheType == PCE_TYPE_GLOBAL) {
- for (auto &p : g_ProtoPictures) {
- if (p->szProtoname == nullptr)
- continue;
-
- p->clear();
- CreateAvatarInCache(0, p, p->szProtoname);
- NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
- }
- return;
- }
-
- // common for all accounts of this proto
- if (pce->cacheType == PCE_TYPE_PROTO) {
- for (auto &p : g_ProtoPictures) {
- if (p->szProtoname == nullptr)
- continue;
-
- PROTOACCOUNT *pdescr = Proto_GetAccount(p->szProtoname);
- if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname))
- continue;
-
- if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) {
- p->clear();
- CreateAvatarInCache(0, p, p->szProtoname);
- NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
- }
- }
- return;
- }
-
- for (auto &p : g_ProtoPictures) {
- if (!mir_strcmp(p->szProtoname, pce->szProtoname)) {
- p->clear();
- NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
- }
- }
-}
-
-static void SetProtoPic(protoPicCacheEntry *pce)
-{
- wchar_t FileName[MAX_PATH], filter[256];
- Bitmap_GetFilter(filter, _countof(filter));
-
- OPENFILENAME ofn = { 0 };
- ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
- ofn.lpstrFilter = filter;
- ofn.hwndOwner = nullptr;
- ofn.lpstrFile = FileName;
- ofn.nMaxFile = MAX_PATH;
- ofn.nMaxFileTitle = MAX_PATH;
- ofn.Flags = OFN_HIDEREADONLY;
- ofn.lpstrInitialDir = L".";
- *FileName = '\0';
- ofn.lpstrDefExt = L"";
- if (!GetOpenFileName(&ofn))
- return;
-
- if (_waccess(FileName, 4) == -1)
- return;
-
- wchar_t szNewPath[MAX_PATH];
- PathToRelativeW(FileName, szNewPath, g_szDataPath);
- db_set_ws(0, PPICT_MODULE, pce->szProtoname, szNewPath);
-
- switch(pce->cacheType) {
- case PCE_TYPE_GLOBAL:
- for (auto &p : g_ProtoPictures) {
- if (mir_strlen(p->szProtoname) == 0)
- continue;
-
- if (p->hbmPic == nullptr || !mir_strcmp(p->szProtoname, AVS_DEFAULT)) {
- CreateAvatarInCache(0, p, pce->szProtoname);
- NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
- }
- }
- break;
-
- case PCE_TYPE_PROTO:
- for (auto &p : g_ProtoPictures) {
- PROTOACCOUNT* pdescr = Proto_GetAccount(p->szProtoname);
- if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname))
- continue;
-
- if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) {
- if (mir_strlen(p->szProtoname) != 0) {
- if (p->hbmPic == nullptr) {
- CreateAvatarInCache(0, p, pce->szProtoname);
- NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
- }
- }
- }
- }
- break;
-
- default:
- for (auto &p : g_ProtoPictures) {
- if (mir_strlen(p->szProtoname) == 0)
- break;
-
- if (!mir_strcmp(p->szProtoname, pce->szProtoname) && mir_strlen(p->szProtoname) == mir_strlen(pce->szProtoname)) {
- if (p->hbmPic != nullptr)
- DeleteObject(p->hbmPic);
- memset(p, 0, sizeof(AVATARCACHEENTRY));
- CreateAvatarInCache(0, p, pce->szProtoname);
- NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
- break;
- }
- }
- }
-}
-
-static protoPicCacheEntry *g_selectedProto;
-
-static INT_PTR CALLBACK DlgProcOptionsAvatars(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
-
- CheckDlgButton(hwndDlg, IDC_SHOWWARNINGS, g_plugin.getByte("warnings", 0) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_MAKE_GRAYSCALE, g_plugin.getByte("MakeGrayscale", 0) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_MAKE_TRANSPARENT_BKG, g_plugin.getByte("MakeTransparentBkg", 0) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL, g_plugin.getByte("MakeTransparencyProportionalToColorDiff", 0) ? BST_CHECKED : BST_UNCHECKED);
-
- SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2));
- SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgNumPoints", 5));
-
- SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
- SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgColorDiff", 10));
- {
- BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), enabled);
- }
-
- return TRUE;
-
- case WM_COMMAND:
- if ((LOWORD(wParam) == IDC_BKG_NUM_POINTS || LOWORD(wParam) == IDC_BKG_COLOR_DIFFERENCE) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
- return FALSE;
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case IDC_MAKE_TRANSPARENT_BKG:
- {
- BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), transp_enabled);
- break;
- }
- case 0:
- switch (((LPNMHDR)lParam)->code) {
- case PSN_APPLY:
- g_plugin.setByte("warnings", IsDlgButtonChecked(hwndDlg, IDC_SHOWWARNINGS) ? 1 : 0);
- g_plugin.setByte("MakeGrayscale", IsDlgButtonChecked(hwndDlg, IDC_MAKE_GRAYSCALE) ? 1 : 0);
- g_plugin.setByte("MakeTransparentBkg", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG) ? 1 : 0);
- g_plugin.setByte("MakeTransparencyProportionalToColorDiff", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL) ? 1 : 0);
- g_plugin.setWord("TranspBkgNumPoints", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0));
- g_plugin.setWord("TranspBkgColorDiff", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0));
- }
- }
- break;
- }
- return FALSE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static INT_PTR CALLBACK DlgProcOptionsOwn(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
-{
- switch (msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
-
- CheckDlgButton(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP, g_plugin.getByte("MakeMyAvatarsTransparent", 0) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_SET_MAKE_SQUARE, g_plugin.getByte("SetAllwaysMakeSquare", 0) ? BST_CHECKED : BST_UNCHECKED);
-
- return TRUE;
-
- case WM_COMMAND:
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case 0:
- switch (((LPNMHDR)lParam)->code) {
- case PSN_APPLY:
- g_plugin.setByte("MakeMyAvatarsTransparent", IsDlgButtonChecked(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP) ? 1 : 0);
- g_plugin.setByte("SetAllwaysMakeSquare", IsDlgButtonChecked(hwndDlg, IDC_SET_MAKE_SQUARE) ? 1 : 0);
- }
- }
- break;
- }
- return FALSE;
-}
-
-static protoPicCacheEntry* GetProtoFromList(HWND hwndDlg, int iItem)
-{
- LVITEM item;
- item.mask = LVIF_PARAM;
- item.iItem = iItem;
- if (!ListView_GetItem(GetDlgItem(hwndDlg, IDC_PROTOCOLS), &item))
- return nullptr;
-
- return (protoPicCacheEntry*)item.lParam;
-}
-
-static INT_PTR CALLBACK DlgProcOptionsProtos(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
- HWND hwndChoosePic = GetDlgItem(hwndDlg, IDC_SETPROTOPIC);
- HWND hwndRemovePic = GetDlgItem(hwndDlg, IDC_REMOVEPROTOPIC);
-
- switch (msg) {
- case WM_INITDIALOG:
- dialoginit = TRUE;
- TranslateDialogDefault(hwndDlg);
-
- ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES);
- {
- LVCOLUMN lvc = { 0 };
- lvc.mask = LVCF_FMT;
- lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
- ListView_InsertColumn(hwndList, 0, &lvc);
-
- CMStringW tmp;
-
- LVITEM item = { 0 };
- item.mask = LVIF_TEXT | LVIF_PARAM;
- item.iItem = 1000;
- for (auto &p : g_ProtoPictures) {
- switch (p->cacheType) {
- case PCE_TYPE_ACCOUNT:
- item.pszText = p->pa->tszAccountName;
- break;
- case PCE_TYPE_PROTO:
- tmp.Format(TranslateT("Global avatar for %s accounts"), _A2T(p->pd->szName).get());
- item.pszText = tmp.GetBuffer();
- break;
- default:
- item.pszText = TranslateT("Global avatar");
- break;
- }
- item.lParam = (LPARAM)p;
- int newItem = ListView_InsertItem(hwndList, &item);
- if (newItem >= 0)
- ListView_SetCheckState(hwndList, newItem, g_plugin.getByte(p->szProtoname, 1) ? TRUE : FALSE);
- }
- ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
- ListView_Arrange(hwndList, LVA_ALIGNLEFT | LVA_ALIGNTOP);
- EnableWindow(hwndChoosePic, FALSE);
- EnableWindow(hwndRemovePic, FALSE);
- }
- dialoginit = FALSE;
- return TRUE;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDC_SETPROTOPIC:
- case IDC_REMOVEPROTOPIC:
- int iItem = ListView_GetSelectionMark(hwndList);
- auto *pce = GetProtoFromList(hwndDlg, iItem);
- if (pce) {
- if (LOWORD(wParam) == IDC_SETPROTOPIC)
- SetProtoPic(pce);
- else
- RemoveProtoPic(pce);
-
- NMHDR nm = { hwndList, IDC_PROTOCOLS, NM_CLICK };
- SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nm);
- }
- }
- break;
-
- case WM_DRAWITEM:
- {
- LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
- if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
- AVATARDRAWREQUEST avdrq = { 0 };
- avdrq.hTargetDC = dis->hDC;
- avdrq.dwFlags |= AVDRQ_PROTOPICT;
- avdrq.szProto = (g_selectedProto) ? g_selectedProto->szProtoname : nullptr;
- GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), &avdrq.rcDraw);
- CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq);
- }
- }
- return TRUE;
-
- case WM_NOTIFY:
- if (dialoginit)
- break;
-
- switch (((LPNMHDR)lParam)->idFrom) {
- case IDC_PROTOCOLS:
- switch (((LPNMHDR)lParam)->code) {
- case LVN_KEYDOWN:
- {
- NMLVKEYDOWN* ptkd = (NMLVKEYDOWN*)lParam;
- if (ptkd&&ptkd->wVKey == VK_SPACE && ListView_GetSelectedCount(ptkd->hdr.hwndFrom) == 1)
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- }
- break;
-
- case LVN_ITEMCHANGED:
- {
- NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam;
- if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_PROTOCOLS)) && ((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK))
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- }
- break;
-
- case NM_CLICK:
- EnableWindow(hwndChoosePic, TRUE);
- EnableWindow(hwndRemovePic, TRUE);
-
- int iItem = ListView_GetSelectionMark(hwndList);
- g_selectedProto = GetProtoFromList(hwndDlg, iItem);
- if (g_selectedProto) {
- DBVARIANT dbv;
- if (!db_get_ws(0, PPICT_MODULE, g_selectedProto->szProtoname, &dbv)) {
- if (!PathIsAbsoluteW(VARSW(dbv.pwszVal))) {
- wchar_t szFinalPath[MAX_PATH];
- mir_snwprintf(szFinalPath, L"%%miranda_path%%\\%s", dbv.pwszVal);
- SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, szFinalPath);
- }
- else SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, dbv.pwszVal);
-
- InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE);
- db_free(&dbv);
- }
- else {
- SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, L"");
- InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE);
- }
- }
- }
- break;
-
- case 0:
- if (((LPNMHDR)lParam)->code == PSN_APPLY) {
- for (int i = 0; i < ListView_GetItemCount(hwndList); i++) {
- auto *pce = GetProtoFromList(hwndDlg, i);
-
- BOOL oldVal = g_plugin.getByte(pce->szProtoname, 1);
- BOOL newVal = ListView_GetCheckState(hwndList, i);
-
- if (oldVal && !newVal)
- for (auto &hContact : Contacts(pce->szProtoname))
- DeleteAvatarFromCache(hContact, TRUE);
-
- if (newVal)
- g_plugin.setByte(pce->szProtoname, 1);
- else
- g_plugin.setByte(pce->szProtoname, 0);
- }
- }
- }
- break;
- }
- return FALSE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Module entry point
-
-int OptInit(WPARAM wParam, LPARAM)
-{
- OPTIONSDIALOGPAGE odp = {};
- odp.flags = ODPF_BOLDGROUPS;
- odp.szGroup.a = LPGEN("Contacts");
- odp.szTitle.a = LPGEN("Avatars");
-
- odp.szTab.a = LPGEN("Protocols");
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PICTS);
- odp.pfnDlgProc = DlgProcOptionsProtos;
- g_plugin.addOptions(wParam, &odp);
-
- odp.szTab.a = LPGEN("Contact avatars");
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_AVATARS);
- odp.pfnDlgProc = DlgProcOptionsAvatars;
- g_plugin.addOptions(wParam, &odp);
-
- odp.szTab.a = LPGEN("Own avatars");
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_OWN);
- odp.pfnDlgProc = DlgProcOptionsOwn;
- 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-04 Miranda ICQ/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"
+
+extern OBJLIST<protoPicCacheEntry> g_ProtoPictures;
+extern HANDLE hEventChanged;
+
+static BOOL dialoginit = TRUE;
+
+static void RemoveProtoPic(protoPicCacheEntry *pce)
+{
+ if (pce == nullptr)
+ return;
+
+ db_unset(0, PPICT_MODULE, pce->szProtoname);
+
+ // common for all accounts
+ if (pce->cacheType == PCE_TYPE_GLOBAL) {
+ for (auto &p : g_ProtoPictures) {
+ if (p->szProtoname == nullptr)
+ continue;
+
+ p->clear();
+ CreateAvatarInCache(0, p, p->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ return;
+ }
+
+ // common for all accounts of this proto
+ if (pce->cacheType == PCE_TYPE_PROTO) {
+ for (auto &p : g_ProtoPictures) {
+ if (p->szProtoname == nullptr)
+ continue;
+
+ PROTOACCOUNT *pdescr = Proto_GetAccount(p->szProtoname);
+ if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname))
+ continue;
+
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) {
+ p->clear();
+ CreateAvatarInCache(0, p, p->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+ return;
+ }
+
+ for (auto &p : g_ProtoPictures) {
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname)) {
+ p->clear();
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+}
+
+static void SetProtoPic(protoPicCacheEntry *pce)
+{
+ wchar_t FileName[MAX_PATH], filter[256];
+ Bitmap_GetFilter(filter, _countof(filter));
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.lpstrFilter = filter;
+ ofn.hwndOwner = nullptr;
+ ofn.lpstrFile = FileName;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.lpstrInitialDir = L".";
+ *FileName = '\0';
+ ofn.lpstrDefExt = L"";
+ if (!GetOpenFileName(&ofn))
+ return;
+
+ if (_waccess(FileName, 4) == -1)
+ return;
+
+ wchar_t szNewPath[MAX_PATH];
+ PathToRelativeW(FileName, szNewPath, g_szDataPath);
+ db_set_ws(0, PPICT_MODULE, pce->szProtoname, szNewPath);
+
+ switch(pce->cacheType) {
+ case PCE_TYPE_GLOBAL:
+ for (auto &p : g_ProtoPictures) {
+ if (mir_strlen(p->szProtoname) == 0)
+ continue;
+
+ if (p->hbmPic == nullptr || !mir_strcmp(p->szProtoname, AVS_DEFAULT)) {
+ CreateAvatarInCache(0, p, pce->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+ break;
+
+ case PCE_TYPE_PROTO:
+ for (auto &p : g_ProtoPictures) {
+ PROTOACCOUNT* pdescr = Proto_GetAccount(p->szProtoname);
+ if (pdescr == nullptr && mir_strcmp(p->szProtoname, pce->szProtoname))
+ continue;
+
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname) || !mir_strcmp(pdescr->szProtoName, pce->pd->szName)) {
+ if (mir_strlen(p->szProtoname) != 0) {
+ if (p->hbmPic == nullptr) {
+ CreateAvatarInCache(0, p, pce->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ for (auto &p : g_ProtoPictures) {
+ if (mir_strlen(p->szProtoname) == 0)
+ break;
+
+ if (!mir_strcmp(p->szProtoname, pce->szProtoname) && mir_strlen(p->szProtoname) == mir_strlen(pce->szProtoname)) {
+ if (p->hbmPic != nullptr)
+ DeleteObject(p->hbmPic);
+ memset(p, 0, sizeof(AVATARCACHEENTRY));
+ CreateAvatarInCache(0, p, pce->szProtoname);
+ NotifyEventHooks(hEventChanged, 0, (LPARAM)p);
+ break;
+ }
+ }
+ }
+}
+
+static protoPicCacheEntry *g_selectedProto;
+
+static INT_PTR CALLBACK DlgProcOptionsAvatars(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_SHOWWARNINGS, g_plugin.getByte("warnings", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MAKE_GRAYSCALE, g_plugin.getByte("MakeGrayscale", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MAKE_TRANSPARENT_BKG, g_plugin.getByte("MakeTransparentBkg", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL, g_plugin.getByte("MakeTransparencyProportionalToColorDiff", 0) ? BST_CHECKED : BST_UNCHECKED);
+
+ SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETRANGE, 0, MAKELONG(8, 2));
+ SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgNumPoints", 5));
+
+ SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, g_plugin.getWord("TranspBkgColorDiff", 10));
+ {
+ BOOL enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), enabled);
+ }
+
+ return TRUE;
+
+ case WM_COMMAND:
+ if ((LOWORD(wParam) == IDC_BKG_NUM_POINTS || LOWORD(wParam) == IDC_BKG_COLOR_DIFFERENCE) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
+ return FALSE;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_MAKE_TRANSPARENT_BKG:
+ {
+ BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL), transp_enabled);
+ break;
+ }
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ g_plugin.setByte("warnings", IsDlgButtonChecked(hwndDlg, IDC_SHOWWARNINGS) ? 1 : 0);
+ g_plugin.setByte("MakeGrayscale", IsDlgButtonChecked(hwndDlg, IDC_MAKE_GRAYSCALE) ? 1 : 0);
+ g_plugin.setByte("MakeTransparentBkg", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSPARENT_BKG) ? 1 : 0);
+ g_plugin.setByte("MakeTransparencyProportionalToColorDiff", IsDlgButtonChecked(hwndDlg, IDC_MAKE_TRANSP_PROPORTIONAL) ? 1 : 0);
+ g_plugin.setWord("TranspBkgNumPoints", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0));
+ g_plugin.setWord("TranspBkgColorDiff", (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0));
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static INT_PTR CALLBACK DlgProcOptionsOwn(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+
+ CheckDlgButton(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP, g_plugin.getByte("MakeMyAvatarsTransparent", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SET_MAKE_SQUARE, g_plugin.getByte("SetAllwaysMakeSquare", 0) ? BST_CHECKED : BST_UNCHECKED);
+
+ return TRUE;
+
+ case WM_COMMAND:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ g_plugin.setByte("MakeMyAvatarsTransparent", IsDlgButtonChecked(hwndDlg, IDC_MAKE_MY_AVATARS_TRANSP) ? 1 : 0);
+ g_plugin.setByte("SetAllwaysMakeSquare", IsDlgButtonChecked(hwndDlg, IDC_SET_MAKE_SQUARE) ? 1 : 0);
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static protoPicCacheEntry* GetProtoFromList(HWND hwndDlg, int iItem)
+{
+ LVITEM item;
+ item.mask = LVIF_PARAM;
+ item.iItem = iItem;
+ if (!ListView_GetItem(GetDlgItem(hwndDlg, IDC_PROTOCOLS), &item))
+ return nullptr;
+
+ return (protoPicCacheEntry*)item.lParam;
+}
+
+static INT_PTR CALLBACK DlgProcOptionsProtos(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_PROTOCOLS);
+ HWND hwndChoosePic = GetDlgItem(hwndDlg, IDC_SETPROTOPIC);
+ HWND hwndRemovePic = GetDlgItem(hwndDlg, IDC_REMOVEPROTOPIC);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ dialoginit = TRUE;
+ TranslateDialogDefault(hwndDlg);
+
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES);
+ {
+ LVCOLUMN lvc = { 0 };
+ lvc.mask = LVCF_FMT;
+ lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
+ ListView_InsertColumn(hwndList, 0, &lvc);
+
+ CMStringW tmp;
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_PARAM;
+ item.iItem = 1000;
+ for (auto &p : g_ProtoPictures) {
+ switch (p->cacheType) {
+ case PCE_TYPE_ACCOUNT:
+ item.pszText = p->pa->tszAccountName;
+ break;
+ case PCE_TYPE_PROTO:
+ tmp.Format(TranslateT("Global avatar for %s accounts"), _A2T(p->pd->szName).get());
+ item.pszText = tmp.GetBuffer();
+ break;
+ default:
+ item.pszText = TranslateT("Global avatar");
+ break;
+ }
+ item.lParam = (LPARAM)p;
+ int newItem = ListView_InsertItem(hwndList, &item);
+ if (newItem >= 0)
+ ListView_SetCheckState(hwndList, newItem, g_plugin.getByte(p->szProtoname, 1) ? TRUE : FALSE);
+ }
+ ListView_SetColumnWidth(hwndList, 0, LVSCW_AUTOSIZE);
+ ListView_Arrange(hwndList, LVA_ALIGNLEFT | LVA_ALIGNTOP);
+ EnableWindow(hwndChoosePic, FALSE);
+ EnableWindow(hwndRemovePic, FALSE);
+ }
+ dialoginit = FALSE;
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_SETPROTOPIC:
+ case IDC_REMOVEPROTOPIC:
+ int iItem = ListView_GetSelectionMark(hwndList);
+ auto *pce = GetProtoFromList(hwndDlg, iItem);
+ if (pce) {
+ if (LOWORD(wParam) == IDC_SETPROTOPIC)
+ SetProtoPic(pce);
+ else
+ RemoveProtoPic(pce);
+
+ NMHDR nm = { hwndList, IDC_PROTOCOLS, NM_CLICK };
+ SendMessage(hwndDlg, WM_NOTIFY, 0, (LPARAM)&nm);
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+ if (dis->CtlType == ODT_BUTTON && dis->CtlID == IDC_PROTOPIC) {
+ AVATARDRAWREQUEST avdrq = { 0 };
+ avdrq.hTargetDC = dis->hDC;
+ avdrq.dwFlags |= AVDRQ_PROTOPICT;
+ avdrq.szProto = (g_selectedProto) ? g_selectedProto->szProtoname : nullptr;
+ GetClientRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), &avdrq.rcDraw);
+ CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&avdrq);
+ }
+ }
+ return TRUE;
+
+ case WM_NOTIFY:
+ if (dialoginit)
+ break;
+
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_PROTOCOLS:
+ switch (((LPNMHDR)lParam)->code) {
+ case LVN_KEYDOWN:
+ {
+ NMLVKEYDOWN* ptkd = (NMLVKEYDOWN*)lParam;
+ if (ptkd&&ptkd->wVKey == VK_SPACE && ListView_GetSelectedCount(ptkd->hdr.hwndFrom) == 1)
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+
+ case LVN_ITEMCHANGED:
+ {
+ NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam;
+ if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_PROTOCOLS)) && ((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK))
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+
+ case NM_CLICK:
+ EnableWindow(hwndChoosePic, TRUE);
+ EnableWindow(hwndRemovePic, TRUE);
+
+ int iItem = ListView_GetSelectionMark(hwndList);
+ g_selectedProto = GetProtoFromList(hwndDlg, iItem);
+ if (g_selectedProto) {
+ DBVARIANT dbv;
+ if (!db_get_ws(0, PPICT_MODULE, g_selectedProto->szProtoname, &dbv)) {
+ if (!PathIsAbsoluteW(VARSW(dbv.pwszVal))) {
+ wchar_t szFinalPath[MAX_PATH];
+ mir_snwprintf(szFinalPath, L"%%miranda_path%%\\%s", dbv.pwszVal);
+ SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, szFinalPath);
+ }
+ else SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, dbv.pwszVal);
+
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE);
+ db_free(&dbv);
+ }
+ else {
+ SetDlgItemText(hwndDlg, IDC_PROTOAVATARNAME, L"");
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PROTOPIC), nullptr, TRUE);
+ }
+ }
+ }
+ break;
+
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY) {
+ for (int i = 0; i < ListView_GetItemCount(hwndList); i++) {
+ auto *pce = GetProtoFromList(hwndDlg, i);
+
+ BOOL oldVal = g_plugin.getByte(pce->szProtoname, 1);
+ BOOL newVal = ListView_GetCheckState(hwndList, i);
+
+ if (oldVal && !newVal)
+ for (auto &hContact : Contacts(pce->szProtoname))
+ DeleteAvatarFromCache(hContact, TRUE);
+
+ if (newVal)
+ g_plugin.setByte(pce->szProtoname, 1);
+ else
+ g_plugin.setByte(pce->szProtoname, 0);
+ }
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module entry point
+
+int OptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.szGroup.a = LPGEN("Contacts");
+ odp.szTitle.a = LPGEN("Avatars");
+
+ odp.szTab.a = LPGEN("Protocols");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_PICTS);
+ odp.pfnDlgProc = DlgProcOptionsProtos;
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.a = LPGEN("Contact avatars");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_AVATARS);
+ odp.pfnDlgProc = DlgProcOptionsAvatars;
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.a = LPGEN("Own avatars");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_OWN);
+ odp.pfnDlgProc = DlgProcOptionsOwn;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/AVS/src/poll.cpp b/plugins/AVS/src/poll.cpp
index 16b16c65dc..80b1e46ae3 100644
--- a/plugins/AVS/src/poll.cpp
+++ b/plugins/AVS/src/poll.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)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/poll.h b/plugins/AVS/src/poll.h
index 3a9c049485..70eaed598d 100644
--- a/plugins/AVS/src/poll.h
+++ b/plugins/AVS/src/poll.h
@@ -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)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/services.cpp b/plugins/AVS/src/services.cpp
index 544e521b4e..0c11763074 100644
--- a/plugins/AVS/src/services.cpp
+++ b/plugins/AVS/src/services.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-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/stdafx.cxx b/plugins/AVS/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AVS/src/stdafx.cxx
+++ b/plugins/AVS/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/plugins/AVS/src/stdafx.h b/plugins/AVS/src/stdafx.h
index c43d83e32a..d8eb699e64 100644
--- a/plugins/AVS/src/stdafx.h
+++ b/plugins/AVS/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-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/AVS/src/userInfo.cpp b/plugins/AVS/src/userInfo.cpp
index 36eadc75d6..d5e69533d0 100644
--- a/plugins/AVS/src/userInfo.cpp
+++ b/plugins/AVS/src/userInfo.cpp
@@ -1,485 +1,485 @@
-/*
-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"
-
-BOOL ScreenToClient(HWND hWnd, LPRECT lpRect);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void LoadTransparentData(HWND hwndDlg, MCONTACT hContact)
-{
- CheckDlgButton(hwndDlg, IDC_MAKETRANSPBKG, db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", g_plugin.getByte("MakeTransparentBkg", 0)) ? BST_CHECKED : BST_UNCHECKED);
- SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", g_plugin.getWord("TranspBkgNumPoints", 5)));
- SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgColorDiff", g_plugin.getWord("TranspBkgColorDiff", 10)));
-
- BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
-}
-
-static void SaveTransparentData(HWND hwndDlg, MCONTACT hContact)
-{
- BOOL transp = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
- if (g_plugin.getByte("MakeTransparentBkg", 0) == transp)
- db_unset(hContact, "ContactPhoto", "MakeTransparentBkg");
- else
- db_set_b(hContact, "ContactPhoto", "MakeTransparentBkg", transp);
-
- uint16_t tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0);
- if (g_plugin.getWord("TranspBkgNumPoints", 5) == tmp)
- db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints");
- else
- db_set_w(hContact, "ContactPhoto", "TranspBkgNumPoints", tmp);
-
- tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0);
- if (g_plugin.getWord("TranspBkgColorDiff", 10) == tmp)
- db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff");
- else
- db_set_w(hContact, "ContactPhoto", "TranspBkgColorDiff", tmp);
-}
-
-void SaveTransparentData(HWND hwndDlg, MCONTACT hContact, BOOL locked)
-{
- SaveTransparentData(hwndDlg, hContact);
-
- MCONTACT tmp = GetContactThatHaveTheAvatar(hContact, locked);
- if (tmp != hContact)
- SaveTransparentData(hwndDlg, tmp);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// User info dialog
-
-class AvatarUserInfoDlg : public CUserInfoPageDlg
-{
- HANDLE hHook = nullptr;
-
- CCtrlSpin spinPoints, spinDifference;
- CCtrlCheck chkTransparent;
- CCtrlButton btnHide, btnChange, btnDefaults, btnProtect, btnReset, btnDelete;
-
- void ReloadAvatar()
- {
- SaveTransparentData(m_hwnd, m_hContact, IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR));
- ChangeAvatar(m_hContact, true);
- }
-
- INT_PTR OnChangeAvatar(UINT, WPARAM, LPARAM)
- {
- InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE);
- return 0;
- }
-
- UI_MESSAGE_MAP(AvatarUserInfoDlg, CUserInfoPageDlg);
- UI_MESSAGE(DM_AVATARCHANGED, OnChangeAvatar);
- UI_MESSAGE_MAP_END();
-
-public:
- AvatarUserInfoDlg() :
- CUserInfoPageDlg(g_plugin, IDD_USER_AVATAR),
- btnHide(this, IDC_HIDEAVATAR),
- btnReset(this, IDC_RESET),
- btnChange(this, IDC_CHANGE),
- btnDelete(this, IDC_DELETE),
- btnProtect(this, IDC_PROTECTAVATAR),
- btnDefaults(this, ID_USE_DEFAULTS),
- chkTransparent(this, IDC_MAKETRANSPBKG),
- spinPoints(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2),
- spinDifference(this, IDC_BKG_NUM_POINTS_SPIN, 100)
- {
- btnHide.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Hide);
- btnReset.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Reset);
- btnChange.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Change);
- btnDelete.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Delete);
- btnProtect.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Protect);
- btnDefaults.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_UseDefaults);
-
- chkTransparent.OnChange = Callback(this, &AvatarUserInfoDlg::onChange_Transparent);
- }
-
- bool OnInitDialog() override
- {
- hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED);
-
- if (m_hContact != 0)
- btnDelete.Hide();
-
- LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact));
- return true;
- }
-
- bool OnRefresh() override
- {
- HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC);
- SendMessage(protopic, AVATAR_SETCONTACT, 0, m_hContact);
- SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, GetSysColor(COLOR_BTNSHADOW));
- SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("Contact has no avatar"));
- SendMessage(protopic, AVATAR_RESPECTHIDDEN, 0, FALSE);
- SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, FALSE);
-
- CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(m_hwnd, IDC_HIDEAVATAR, Contact::IsHidden(m_hContact) ? BST_CHECKED : BST_UNCHECKED);
- return false;
- }
-
- bool IsEmpty() const override
- {
- auto wszFileName(db_get_wsm(m_hContact, "ContactPhoto", "File"));
- return (wszFileName.IsEmpty() || _waccess(wszFileName, 0) != 0);
- }
-
- void OnDestroy() override
- {
- UnhookEvent(hHook);
- }
-
- void onClick_UseDefaults(CCtrlButton *)
- {
- m_hContact = GetContactThatHaveTheAvatar(m_hContact);
-
- db_unset(m_hContact, "ContactPhoto", "MakeTransparentBkg");
- db_unset(m_hContact, "ContactPhoto", "TranspBkgNumPoints");
- db_unset(m_hContact, "ContactPhoto", "TranspBkgColorDiff");
-
- LoadTransparentData(m_hwnd, m_hContact);
- ReloadAvatar();
- }
-
- void onClick_Change(CCtrlButton *)
- {
- SetAvatar(m_hContact, 0);
- CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED);
- }
-
- void onClick_Hide(CCtrlButton *)
- {
- bool hidden = IsDlgButtonChecked(m_hwnd, IDC_HIDEAVATAR) != 0;
- SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden);
- if (hidden != Contact::IsHidden(m_hContact))
- Contact::Hide(m_hContact, hidden);
- }
-
- void onClick_Protect(CCtrlButton *)
- {
- BOOL locked = IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR);
- SaveTransparentData(m_hwnd, m_hContact, locked);
- ProtectAvatar(m_hContact, locked ? 1 : 0);
- }
-
- void onChange_Transparent(CCtrlCheck *)
- {
- BOOL enable = IsDlgButtonChecked(m_hwnd, IDC_MAKETRANSPBKG);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
- EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable);
- ReloadAvatar();
- }
-
- void onClick_Reset(CCtrlButton *)
- {
- ProtectAvatar(m_hContact, 0);
- if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
- DBVARIANT dbv = { 0 };
- if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
- DeleteFile(dbv.pwszVal);
- db_free(&dbv);
- }
- }
- db_unset(m_hContact, "ContactPhoto", "Locked");
- db_unset(m_hContact, "ContactPhoto", "Backup");
- db_unset(m_hContact, "ContactPhoto", "RFile");
- db_unset(m_hContact, "ContactPhoto", "File");
- db_unset(m_hContact, "ContactPhoto", "Format");
-
- char *szProto = Proto_GetBaseAccountName(m_hContact);
- db_unset(m_hContact, szProto, "AvatarHash");
- DeleteAvatarFromCache(m_hContact, FALSE);
-
- QueueAdd(m_hContact);
- }
-
- void onClick_Delete(CCtrlButton *)
- {
- ProtectAvatar(m_hContact, 0);
- if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
- DBVARIANT dbv = { 0 };
- if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
- DeleteFile(dbv.pwszVal);
- db_free(&dbv);
- }
- }
- db_unset(m_hContact, "ContactPhoto", "Locked");
- db_unset(m_hContact, "ContactPhoto", "Backup");
- db_unset(m_hContact, "ContactPhoto", "RFile");
- db_unset(m_hContact, "ContactPhoto", "File");
- db_unset(m_hContact, "ContactPhoto", "Format");
- DeleteAvatarFromCache(m_hContact, FALSE);
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Protocol avatar dialog
-
-class AvatarProtoInfoDlg : public CUserInfoPageDlg
-{
- char* GetSelectedProtocol()
- {
- // Get selection
- int iItem = protocols.GetSelectionMark();
- if (iItem < 0)
- return nullptr;
-
- // Get protocol name
- LVITEM item = {0};
- item.mask = LVIF_PARAM;
- item.iItem = iItem;
- SendMessage(protocols.GetHwnd(), LVM_GETITEMA, 0, (LPARAM)&item);
- return (char *)item.lParam;
- }
-
- void OffsetWindow(int iCtrlId, int dx)
- {
- HWND hwnd = GetDlgItem(m_hwnd, iCtrlId);
-
- RECT rc;
- GetWindowRect(hwnd, &rc);
- ScreenToClient(m_hwnd, &rc);
- OffsetRect(&rc, dx, 0);
- MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
- }
-
- void EnableDisableControls(char *proto)
- {
- if (chkPerProto.IsChecked()) {
- if (proto == nullptr) {
- EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE);
- EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE);
- }
- else {
- if (!ProtoServiceExists(proto, PS_SETMYAVATAR)) {
- EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE);
- EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE);
- }
- else {
- EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE);
-
- int width, height;
- SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height);
- EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0);
- }
- }
- }
- else {
- EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE);
-
- if (g_plugin.getByte("GlobalUserAvatarNotConsistent", 1))
- EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), TRUE);
- else {
- int width, height;
- SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height);
- EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0);
- }
- }
- }
-
- CCtrlCheck chkPerProto;
- CCtrlButton btnChange, btnDelete;
- CCtrlListView protocols;
-
-public:
- AvatarProtoInfoDlg() :
- CUserInfoPageDlg(g_plugin, IDD_PROTO_AVATARS),
- protocols(this, IDC_PROTOCOLS),
- btnChange(this, IDC_CHANGE),
- btnDelete(this, IDC_DELETE),
- chkPerProto(this, IDC_PER_PROTO)
- {
- protocols.OnItemChanged = Callback(this, &AvatarProtoInfoDlg::onItemChanged_List);
-
- btnChange.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Change);
- btnDelete.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Delete);
-
- CreateLink(chkPerProto, g_plugin.bPerProto);
- chkPerProto.OnChange = Callback(this, &AvatarProtoInfoDlg::onChange_PerProto);
- }
-
- bool OnInitDialog() override
- {
- HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC);
- SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNSHADOW));
- SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("No avatar"));
- SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM)FALSE);
-
- protocols.SetExtendedListViewStyleEx(0, LVS_EX_SUBITEMIMAGES);
-
- HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 4, 0);
- protocols.SetImageList(hIml, LVSIL_SMALL);
-
- LVCOLUMN lvc = { 0 };
- lvc.mask = LVCF_FMT;
- lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
- protocols.InsertColumn(0, &lvc);
-
- LVITEM item = { 0 };
- item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
- item.iItem = 1000;
-
- // List protocols
- int num = 0;
- for (auto &it : Accounts()) {
- if (!ProtoServiceExists(it->szModuleName, PS_GETMYAVATAR))
- continue;
-
- if (!Proto_IsAvatarsEnabled(it->szModuleName))
- continue;
-
- ImageList_AddIcon(hIml, Skin_LoadProtoIcon(it->szModuleName, ID_STATUS_ONLINE));
- item.pszText = it->tszAccountName;
- item.iImage = num;
- item.lParam = (LPARAM)it->szModuleName;
- protocols.InsertItem(&item);
- num++;
- }
-
- protocols.SetCurSel(0);
- protocols.SetColumnWidth(0, LVSCW_AUTOSIZE);
- protocols.Arrange(LVA_ALIGNLEFT | LVA_ALIGNTOP);
- return true;
- }
-
- void onItemChanged_List(CCtrlListView::TEventInfo *ev)
- {
- LPNMLISTVIEW li = ev->nmlv;
- if (li->uNewState & LVIS_SELECTED) {
- SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, li->lParam);
- EnableDisableControls((char*)li->lParam);
- }
- }
-
- INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
- {
- if (msg == WM_NOTIFY) {
- LPNMHDR hdr = (LPNMHDR)lParam;
- if (hdr->idFrom == IDC_PROTOPIC && hdr->code == NM_AVATAR_CHANGED)
- EnableDisableControls(GetSelectedProtocol());
- }
- return CDlgBase::DlgProc(msg, wParam, lParam);
- }
-
- void onClick_Change(CCtrlButton *)
- {
- if (!chkPerProto.IsChecked())
- SetMyAvatar(NULL, NULL);
- else {
- char *proto = GetSelectedProtocol();
- if (proto != nullptr)
- SetMyAvatar((WPARAM)proto, NULL);
- }
- }
-
- void onClick_Delete(CCtrlButton *)
- {
- if (!chkPerProto.IsChecked()) {
- if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), TranslateT("Global avatar"), MB_YESNO) == IDYES)
- SetMyAvatar(NULL, (LPARAM)L"");
- }
- else {
- if (char *proto = GetSelectedProtocol()) {
- char description[256];
- CallProtoService(proto, PS_GETNAME, _countof(description), (LPARAM)description);
- wchar_t *descr = mir_a2u(description);
- if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), descr, MB_YESNO) == IDYES)
- SetMyAvatar((WPARAM)proto, (LPARAM)L"");
- mir_free(descr);
- }
- }
- }
-
- void onChange_PerProto(CCtrlCheck *pCheck)
- {
- int diff = 147; // Pre-calc
-
- if (chkPerProto.IsChecked()) {
- if (!IsWindowVisible(protocols.GetHwnd())) {
- // Show list of protocols
- protocols.Show();
-
- // Move controls
- OffsetWindow(IDC_PROTOPIC, diff);
- OffsetWindow(IDC_CHANGE, diff);
- OffsetWindow(IDC_DELETE, diff);
- }
-
- char *proto = GetSelectedProtocol();
- if (proto == nullptr) {
- protocols.SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, 0x0F);
- }
- else {
- SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, (LPARAM)proto);
- EnableDisableControls(proto);
- }
- }
- else {
- if (!m_bInitialized) {
- PostMessage(m_hwnd, WM_COMMAND, IDC_PER_PROTO, 0);
- }
- else if (!pCheck || IsWindowVisible(protocols.GetHwnd())) {
- // Hide list of protocols
- protocols.Hide();
-
- // Move controls
- OffsetWindow(IDC_PROTOPIC, -diff);
- OffsetWindow(IDC_CHANGE, -diff);
- OffsetWindow(IDC_DELETE, -diff);
- }
-
- SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, NULL);
- }
- }
-};
-
-int OnDetailsInit(WPARAM wParam, LPARAM hContact)
-{
- USERINFOPAGE uip = {};
- uip.szTitle.a = LPGEN("Avatar");
- uip.flags = ODPF_ICON;
- uip.dwInitParam = (LPARAM)g_plugin.getIconHandle(IDI_AVATAR);
-
- if (hContact == NULL) {
- // User dialog
- uip.pDialog = new AvatarProtoInfoDlg();
- g_plugin.addUserInfo(wParam, &uip);
- }
- else {
- char *szProto = Proto_GetBaseAccountName(hContact);
- if (szProto == nullptr || g_plugin.getByte(szProto, 1)) {
- // Contact dialog
- uip.position = -2000000000;
- uip.pDialog = new AvatarUserInfoDlg();
- g_plugin.addUserInfo(wParam, &uip);
- }
- }
- return 0;
-}
+/*
+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"
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void LoadTransparentData(HWND hwndDlg, MCONTACT hContact)
+{
+ CheckDlgButton(hwndDlg, IDC_MAKETRANSPBKG, db_get_b(hContact, "ContactPhoto", "MakeTransparentBkg", g_plugin.getByte("MakeTransparentBkg", 0)) ? BST_CHECKED : BST_UNCHECKED);
+ SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgNumPoints", g_plugin.getWord("TranspBkgNumPoints", 5)));
+ SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_SETPOS, 0, (LPARAM)db_get_w(hContact, "ContactPhoto", "TranspBkgColorDiff", g_plugin.getWord("TranspBkgColorDiff", 10)));
+
+ BOOL transp_enabled = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_NUM_POINTS), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_L), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN), transp_enabled);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BKG_COLOR_DIFFERENCE), transp_enabled);
+}
+
+static void SaveTransparentData(HWND hwndDlg, MCONTACT hContact)
+{
+ BOOL transp = IsDlgButtonChecked(hwndDlg, IDC_MAKETRANSPBKG);
+ if (g_plugin.getByte("MakeTransparentBkg", 0) == transp)
+ db_unset(hContact, "ContactPhoto", "MakeTransparentBkg");
+ else
+ db_set_b(hContact, "ContactPhoto", "MakeTransparentBkg", transp);
+
+ uint16_t tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_NUM_POINTS_SPIN, UDM_GETPOS, 0, 0);
+ if (g_plugin.getWord("TranspBkgNumPoints", 5) == tmp)
+ db_unset(hContact, "ContactPhoto", "TranspBkgNumPoints");
+ else
+ db_set_w(hContact, "ContactPhoto", "TranspBkgNumPoints", tmp);
+
+ tmp = (uint16_t)SendDlgItemMessage(hwndDlg, IDC_BKG_COLOR_DIFFERENCE_SPIN, UDM_GETPOS, 0, 0);
+ if (g_plugin.getWord("TranspBkgColorDiff", 10) == tmp)
+ db_unset(hContact, "ContactPhoto", "TranspBkgColorDiff");
+ else
+ db_set_w(hContact, "ContactPhoto", "TranspBkgColorDiff", tmp);
+}
+
+void SaveTransparentData(HWND hwndDlg, MCONTACT hContact, BOOL locked)
+{
+ SaveTransparentData(hwndDlg, hContact);
+
+ MCONTACT tmp = GetContactThatHaveTheAvatar(hContact, locked);
+ if (tmp != hContact)
+ SaveTransparentData(hwndDlg, tmp);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// User info dialog
+
+class AvatarUserInfoDlg : public CUserInfoPageDlg
+{
+ HANDLE hHook = nullptr;
+
+ CCtrlSpin spinPoints, spinDifference;
+ CCtrlCheck chkTransparent;
+ CCtrlButton btnHide, btnChange, btnDefaults, btnProtect, btnReset, btnDelete;
+
+ void ReloadAvatar()
+ {
+ SaveTransparentData(m_hwnd, m_hContact, IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR));
+ ChangeAvatar(m_hContact, true);
+ }
+
+ INT_PTR OnChangeAvatar(UINT, WPARAM, LPARAM)
+ {
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_PROTOPIC), nullptr, TRUE);
+ return 0;
+ }
+
+ UI_MESSAGE_MAP(AvatarUserInfoDlg, CUserInfoPageDlg);
+ UI_MESSAGE(DM_AVATARCHANGED, OnChangeAvatar);
+ UI_MESSAGE_MAP_END();
+
+public:
+ AvatarUserInfoDlg() :
+ CUserInfoPageDlg(g_plugin, IDD_USER_AVATAR),
+ btnHide(this, IDC_HIDEAVATAR),
+ btnReset(this, IDC_RESET),
+ btnChange(this, IDC_CHANGE),
+ btnDelete(this, IDC_DELETE),
+ btnProtect(this, IDC_PROTECTAVATAR),
+ btnDefaults(this, ID_USE_DEFAULTS),
+ chkTransparent(this, IDC_MAKETRANSPBKG),
+ spinPoints(this, IDC_BKG_NUM_POINTS_SPIN, 8, 2),
+ spinDifference(this, IDC_BKG_NUM_POINTS_SPIN, 100)
+ {
+ btnHide.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Hide);
+ btnReset.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Reset);
+ btnChange.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Change);
+ btnDelete.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Delete);
+ btnProtect.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_Protect);
+ btnDefaults.OnClick = Callback(this, &AvatarUserInfoDlg::onClick_UseDefaults);
+
+ chkTransparent.OnChange = Callback(this, &AvatarUserInfoDlg::onChange_Transparent);
+ }
+
+ bool OnInitDialog() override
+ {
+ hHook = HookEventMessage(ME_AV_AVATARCHANGED, m_hwnd, DM_AVATARCHANGED);
+
+ if (m_hContact != 0)
+ btnDelete.Hide();
+
+ LoadTransparentData(m_hwnd, GetContactThatHaveTheAvatar(m_hContact));
+ return true;
+ }
+
+ bool OnRefresh() override
+ {
+ HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC);
+ SendMessage(protopic, AVATAR_SETCONTACT, 0, m_hContact);
+ SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, GetSysColor(COLOR_BTNSHADOW));
+ SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("Contact has no avatar"));
+ SendMessage(protopic, AVATAR_RESPECTHIDDEN, 0, FALSE);
+ SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, FALSE);
+
+ CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(m_hwnd, IDC_HIDEAVATAR, Contact::IsHidden(m_hContact) ? BST_CHECKED : BST_UNCHECKED);
+ return false;
+ }
+
+ bool IsEmpty() const override
+ {
+ auto wszFileName(db_get_wsm(m_hContact, "ContactPhoto", "File"));
+ return (wszFileName.IsEmpty() || _waccess(wszFileName, 0) != 0);
+ }
+
+ void OnDestroy() override
+ {
+ UnhookEvent(hHook);
+ }
+
+ void onClick_UseDefaults(CCtrlButton *)
+ {
+ m_hContact = GetContactThatHaveTheAvatar(m_hContact);
+
+ db_unset(m_hContact, "ContactPhoto", "MakeTransparentBkg");
+ db_unset(m_hContact, "ContactPhoto", "TranspBkgNumPoints");
+ db_unset(m_hContact, "ContactPhoto", "TranspBkgColorDiff");
+
+ LoadTransparentData(m_hwnd, m_hContact);
+ ReloadAvatar();
+ }
+
+ void onClick_Change(CCtrlButton *)
+ {
+ SetAvatar(m_hContact, 0);
+ CheckDlgButton(m_hwnd, IDC_PROTECTAVATAR, db_get_b(m_hContact, "ContactPhoto", "Locked", 0) ? BST_CHECKED : BST_UNCHECKED);
+ }
+
+ void onClick_Hide(CCtrlButton *)
+ {
+ bool hidden = IsDlgButtonChecked(m_hwnd, IDC_HIDEAVATAR) != 0;
+ SetAvatarAttribute(m_hContact, AVS_HIDEONCLIST, hidden);
+ if (hidden != Contact::IsHidden(m_hContact))
+ Contact::Hide(m_hContact, hidden);
+ }
+
+ void onClick_Protect(CCtrlButton *)
+ {
+ BOOL locked = IsDlgButtonChecked(m_hwnd, IDC_PROTECTAVATAR);
+ SaveTransparentData(m_hwnd, m_hContact, locked);
+ ProtectAvatar(m_hContact, locked ? 1 : 0);
+ }
+
+ void onChange_Transparent(CCtrlCheck *)
+ {
+ BOOL enable = IsDlgButtonChecked(m_hwnd, IDC_MAKETRANSPBKG);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_NUM_POINTS), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_L), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE_SPIN), enable);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_BKG_COLOR_DIFFERENCE), enable);
+ ReloadAvatar();
+ }
+
+ void onClick_Reset(CCtrlButton *)
+ {
+ ProtectAvatar(m_hContact, 0);
+ if (MessageBox(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFile(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+
+ char *szProto = Proto_GetBaseAccountName(m_hContact);
+ db_unset(m_hContact, szProto, "AvatarHash");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+
+ QueueAdd(m_hContact);
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ ProtectAvatar(m_hContact, 0);
+ if (MessageBoxW(nullptr, TranslateT("Delete picture file from disk (may be necessary to force a reload, but will delete local pictures)?"), TranslateT("Reset contact picture"), MB_YESNO) == IDYES) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(m_hContact, "ContactPhoto", "File", &dbv)) {
+ DeleteFile(dbv.pwszVal);
+ db_free(&dbv);
+ }
+ }
+ db_unset(m_hContact, "ContactPhoto", "Locked");
+ db_unset(m_hContact, "ContactPhoto", "Backup");
+ db_unset(m_hContact, "ContactPhoto", "RFile");
+ db_unset(m_hContact, "ContactPhoto", "File");
+ db_unset(m_hContact, "ContactPhoto", "Format");
+ DeleteAvatarFromCache(m_hContact, FALSE);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Protocol avatar dialog
+
+class AvatarProtoInfoDlg : public CUserInfoPageDlg
+{
+ char* GetSelectedProtocol()
+ {
+ // Get selection
+ int iItem = protocols.GetSelectionMark();
+ if (iItem < 0)
+ return nullptr;
+
+ // Get protocol name
+ LVITEM item = {0};
+ item.mask = LVIF_PARAM;
+ item.iItem = iItem;
+ SendMessage(protocols.GetHwnd(), LVM_GETITEMA, 0, (LPARAM)&item);
+ return (char *)item.lParam;
+ }
+
+ void OffsetWindow(int iCtrlId, int dx)
+ {
+ HWND hwnd = GetDlgItem(m_hwnd, iCtrlId);
+
+ RECT rc;
+ GetWindowRect(hwnd, &rc);
+ ScreenToClient(m_hwnd, &rc);
+ OffsetRect(&rc, dx, 0);
+ MoveWindow(hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+ }
+
+ void EnableDisableControls(char *proto)
+ {
+ if (chkPerProto.IsChecked()) {
+ if (proto == nullptr) {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE);
+ }
+ else {
+ if (!ProtoServiceExists(proto, PS_SETMYAVATAR)) {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), FALSE);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), FALSE);
+ }
+ else {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE);
+
+ int width, height;
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0);
+ }
+ }
+ }
+ else {
+ EnableWindow(GetDlgItem(m_hwnd, IDC_CHANGE), TRUE);
+
+ if (g_plugin.getByte("GlobalUserAvatarNotConsistent", 1))
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), TRUE);
+ else {
+ int width, height;
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_GETUSEDSPACE, (WPARAM)&width, (LPARAM)&height);
+ EnableWindow(GetDlgItem(m_hwnd, IDC_DELETE), (LPARAM)width != 0 || height != 0);
+ }
+ }
+ }
+
+ CCtrlCheck chkPerProto;
+ CCtrlButton btnChange, btnDelete;
+ CCtrlListView protocols;
+
+public:
+ AvatarProtoInfoDlg() :
+ CUserInfoPageDlg(g_plugin, IDD_PROTO_AVATARS),
+ protocols(this, IDC_PROTOCOLS),
+ btnChange(this, IDC_CHANGE),
+ btnDelete(this, IDC_DELETE),
+ chkPerProto(this, IDC_PER_PROTO)
+ {
+ protocols.OnItemChanged = Callback(this, &AvatarProtoInfoDlg::onItemChanged_List);
+
+ btnChange.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Change);
+ btnDelete.OnClick = Callback(this, &AvatarProtoInfoDlg::onClick_Delete);
+
+ CreateLink(chkPerProto, g_plugin.bPerProto);
+ chkPerProto.OnChange = Callback(this, &AvatarProtoInfoDlg::onChange_PerProto);
+ }
+
+ bool OnInitDialog() override
+ {
+ HWND protopic = GetDlgItem(m_hwnd, IDC_PROTOPIC);
+ SendMessage(protopic, AVATAR_SETAVATARBORDERCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNSHADOW));
+ SendMessage(protopic, AVATAR_SETNOAVATARTEXT, 0, (LPARAM)LPGENW("No avatar"));
+ SendMessage(protopic, AVATAR_SETRESIZEIFSMALLER, 0, (LPARAM)FALSE);
+
+ protocols.SetExtendedListViewStyleEx(0, LVS_EX_SUBITEMIMAGES);
+
+ HIMAGELIST hIml = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 4, 0);
+ protocols.SetImageList(hIml, LVSIL_SMALL);
+
+ LVCOLUMN lvc = { 0 };
+ lvc.mask = LVCF_FMT;
+ lvc.fmt = LVCFMT_IMAGE | LVCFMT_LEFT;
+ protocols.InsertColumn(0, &lvc);
+
+ LVITEM item = { 0 };
+ item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
+ item.iItem = 1000;
+
+ // List protocols
+ int num = 0;
+ for (auto &it : Accounts()) {
+ if (!ProtoServiceExists(it->szModuleName, PS_GETMYAVATAR))
+ continue;
+
+ if (!Proto_IsAvatarsEnabled(it->szModuleName))
+ continue;
+
+ ImageList_AddIcon(hIml, Skin_LoadProtoIcon(it->szModuleName, ID_STATUS_ONLINE));
+ item.pszText = it->tszAccountName;
+ item.iImage = num;
+ item.lParam = (LPARAM)it->szModuleName;
+ protocols.InsertItem(&item);
+ num++;
+ }
+
+ protocols.SetCurSel(0);
+ protocols.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ protocols.Arrange(LVA_ALIGNLEFT | LVA_ALIGNTOP);
+ return true;
+ }
+
+ void onItemChanged_List(CCtrlListView::TEventInfo *ev)
+ {
+ LPNMLISTVIEW li = ev->nmlv;
+ if (li->uNewState & LVIS_SELECTED) {
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, li->lParam);
+ EnableDisableControls((char*)li->lParam);
+ }
+ }
+
+ INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
+ {
+ if (msg == WM_NOTIFY) {
+ LPNMHDR hdr = (LPNMHDR)lParam;
+ if (hdr->idFrom == IDC_PROTOPIC && hdr->code == NM_AVATAR_CHANGED)
+ EnableDisableControls(GetSelectedProtocol());
+ }
+ return CDlgBase::DlgProc(msg, wParam, lParam);
+ }
+
+ void onClick_Change(CCtrlButton *)
+ {
+ if (!chkPerProto.IsChecked())
+ SetMyAvatar(NULL, NULL);
+ else {
+ char *proto = GetSelectedProtocol();
+ if (proto != nullptr)
+ SetMyAvatar((WPARAM)proto, NULL);
+ }
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ if (!chkPerProto.IsChecked()) {
+ if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), TranslateT("Global avatar"), MB_YESNO) == IDYES)
+ SetMyAvatar(NULL, (LPARAM)L"");
+ }
+ else {
+ if (char *proto = GetSelectedProtocol()) {
+ char description[256];
+ CallProtoService(proto, PS_GETNAME, _countof(description), (LPARAM)description);
+ wchar_t *descr = mir_a2u(description);
+ if (MessageBox(m_hwnd, TranslateT("Are you sure you want to remove your avatar?"), descr, MB_YESNO) == IDYES)
+ SetMyAvatar((WPARAM)proto, (LPARAM)L"");
+ mir_free(descr);
+ }
+ }
+ }
+
+ void onChange_PerProto(CCtrlCheck *pCheck)
+ {
+ int diff = 147; // Pre-calc
+
+ if (chkPerProto.IsChecked()) {
+ if (!IsWindowVisible(protocols.GetHwnd())) {
+ // Show list of protocols
+ protocols.Show();
+
+ // Move controls
+ OffsetWindow(IDC_PROTOPIC, diff);
+ OffsetWindow(IDC_CHANGE, diff);
+ OffsetWindow(IDC_DELETE, diff);
+ }
+
+ char *proto = GetSelectedProtocol();
+ if (proto == nullptr) {
+ protocols.SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, 0x0F);
+ }
+ else {
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, (LPARAM)proto);
+ EnableDisableControls(proto);
+ }
+ }
+ else {
+ if (!m_bInitialized) {
+ PostMessage(m_hwnd, WM_COMMAND, IDC_PER_PROTO, 0);
+ }
+ else if (!pCheck || IsWindowVisible(protocols.GetHwnd())) {
+ // Hide list of protocols
+ protocols.Hide();
+
+ // Move controls
+ OffsetWindow(IDC_PROTOPIC, -diff);
+ OffsetWindow(IDC_CHANGE, -diff);
+ OffsetWindow(IDC_DELETE, -diff);
+ }
+
+ SendDlgItemMessage(m_hwnd, IDC_PROTOPIC, AVATAR_SETPROTOCOL, 0, NULL);
+ }
+ }
+};
+
+int OnDetailsInit(WPARAM wParam, LPARAM hContact)
+{
+ USERINFOPAGE uip = {};
+ uip.szTitle.a = LPGEN("Avatar");
+ uip.flags = ODPF_ICON;
+ uip.dwInitParam = (LPARAM)g_plugin.getIconHandle(IDI_AVATAR);
+
+ if (hContact == NULL) {
+ // User dialog
+ uip.pDialog = new AvatarProtoInfoDlg();
+ g_plugin.addUserInfo(wParam, &uip);
+ }
+ else {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto == nullptr || g_plugin.getByte(szProto, 1)) {
+ // Contact dialog
+ uip.position = -2000000000;
+ uip.pDialog = new AvatarUserInfoDlg();
+ g_plugin.addUserInfo(wParam, &uip);
+ }
+ }
+ return 0;
+}
diff --git a/plugins/AVS/src/utils.cpp b/plugins/AVS/src/utils.cpp
index f820d870a1..975e3808b3 100644
--- a/plugins/AVS/src/utils.cpp
+++ b/plugins/AVS/src/utils.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)
Copyright (C) 2006 Ricardo Pescuma Domenecci, Nightwish
This is free software; you can redistribute it and/or
diff --git a/plugins/AVS/src/version.h b/plugins/AVS/src/version.h
index d0bae7638f..b58daf865c 100644
--- a/plugins/AVS/src/version.h
+++ b/plugins/AVS/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Loads and manages contact pictures for other plugins."
#define __AUTHOR "Nightwish, Pescuma"
#define __AUTHORWEB "https://miranda-ng.org/p/AVS"
-#define __COPYRIGHT "© 2000-2012 Miranda-IM project, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2000-2012 Miranda-IM project, 2012-23 Miranda NG team"
diff --git a/plugins/AddContactPlus/src/addcontact.cpp b/plugins/AddContactPlus/src/addcontact.cpp
index 11770d4945..a80224650f 100644
--- a/plugins/AddContactPlus/src/addcontact.cpp
+++ b/plugins/AddContactPlus/src/addcontact.cpp
@@ -2,7 +2,7 @@
AddContact+ plugin for Miranda NG
Copyright (C) 2007-11 Bartosz 'Dezeath' Białek
-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 as published by
diff --git a/plugins/AddContactPlus/src/main.cpp b/plugins/AddContactPlus/src/main.cpp
index 8a0ee33db6..44fcf1e81d 100644
--- a/plugins/AddContactPlus/src/main.cpp
+++ b/plugins/AddContactPlus/src/main.cpp
@@ -2,7 +2,7 @@
AddContact+ plugin for Miranda NG
Copyright (C) 2007-11 Bartosz 'Dezeath' Białek
-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 as published by
diff --git a/plugins/AddContactPlus/src/stdafx.cxx b/plugins/AddContactPlus/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AddContactPlus/src/stdafx.cxx
+++ b/plugins/AddContactPlus/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/plugins/AddContactPlus/src/stdafx.h b/plugins/AddContactPlus/src/stdafx.h
index f17f161d82..e90008d0a6 100644
--- a/plugins/AddContactPlus/src/stdafx.h
+++ b/plugins/AddContactPlus/src/stdafx.h
@@ -2,7 +2,7 @@
AddContact+ plugin for Miranda NG
Copyright (C) 2007-11 Bartosz 'Dezeath' Białek
-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 as published by
diff --git a/plugins/AddContactPlus/src/version.h b/plugins/AddContactPlus/src/version.h
index 3e206c907d..436a635cc9 100644
--- a/plugins/AddContactPlus/src/version.h
+++ b/plugins/AddContactPlus/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Provides the ability to quickly add new contacts."
#define __AUTHOR "Bartosz 'Dezeath' Białek"
#define __AUTHORWEB "https://miranda-ng.org/p/AddContactPlus"
-#define __COPYRIGHT "© 2007-22 Bartosz 'Dezeath' Białek, Miranda NG team"
+#define __COPYRIGHT "© 2007-23 Bartosz 'Dezeath' Białek, Miranda NG team"
diff --git a/plugins/Alarms/src/stdafx.cxx b/plugins/Alarms/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Alarms/src/stdafx.cxx
+++ b/plugins/Alarms/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/plugins/AsSingleWindow/src/stdafx.cxx b/plugins/AsSingleWindow/src/stdafx.cxx
index d265a4c02e..8c570f6949 100644
--- a/plugins/AsSingleWindow/src/stdafx.cxx
+++ b/plugins/AsSingleWindow/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/>.
-*/
-
-#include "stdafx.h"
+/*
+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"
diff --git a/plugins/AsSingleWindow/src/version.h b/plugins/AsSingleWindow/src/version.h
index 52c99a0cb1..452e1b1883 100644
--- a/plugins/AsSingleWindow/src/version.h
+++ b/plugins/AsSingleWindow/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Allows you to move, minimize and activate Miranda's windows as if it were a single window."
#define __AUTHOR "Aleksey Smyrnov aka Soar"
#define __AUTHORWEB "https://miranda-ng.org/p/AsSingleWindow"
-#define __COPYRIGHT "© 2010-2011 Soar, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2010-2011 Soar, 2017-23 Miranda NG team"
diff --git a/plugins/AssocMgr/src/stdafx.cxx b/plugins/AssocMgr/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AssocMgr/src/stdafx.cxx
+++ b/plugins/AssocMgr/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/plugins/AuthState/src/stdafx.cxx b/plugins/AuthState/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AuthState/src/stdafx.cxx
+++ b/plugins/AuthState/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/plugins/AutoRun/src/stdafx.cxx b/plugins/AutoRun/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AutoRun/src/stdafx.cxx
+++ b/plugins/AutoRun/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/plugins/AutoShutdown/src/stdafx.cxx b/plugins/AutoShutdown/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AutoShutdown/src/stdafx.cxx
+++ b/plugins/AutoShutdown/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/plugins/AvatarHistory/src/stdafx.cxx b/plugins/AvatarHistory/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/AvatarHistory/src/stdafx.cxx
+++ b/plugins/AvatarHistory/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/plugins/BASS_interface/src/stdafx.cxx b/plugins/BASS_interface/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/BASS_interface/src/stdafx.cxx
+++ b/plugins/BASS_interface/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/plugins/BasicHistory/src/stdafx.cxx b/plugins/BasicHistory/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/BasicHistory/src/stdafx.cxx
+++ b/plugins/BasicHistory/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/plugins/Boltun/src/stdafx.cxx b/plugins/Boltun/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Boltun/src/stdafx.cxx
+++ b/plugins/Boltun/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/plugins/BossKeyPlus/src/BossKeyIdle.cpp b/plugins/BossKeyPlus/src/BossKeyIdle.cpp
index 5a83a2fa11..962d9ff6fd 100644
--- a/plugins/BossKeyPlus/src/BossKeyIdle.cpp
+++ b/plugins/BossKeyPlus/src/BossKeyIdle.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-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/BossKeyPlus/src/stdafx.cxx b/plugins/BossKeyPlus/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/BossKeyPlus/src/stdafx.cxx
+++ b/plugins/BossKeyPlus/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/plugins/BuddyExpectator/src/stdafx.cxx b/plugins/BuddyExpectator/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/BuddyExpectator/src/stdafx.cxx
+++ b/plugins/BuddyExpectator/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/plugins/BuddyPounce/src/options.cpp b/plugins/BuddyPounce/src/options.cpp
index b77dd1263e..aeedbc9b63 100644
--- a/plugins/BuddyPounce/src/options.cpp
+++ b/plugins/BuddyPounce/src/options.cpp
@@ -1,245 +1,245 @@
-/*
-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"
-
-COptionsDlg::COptionsDlg(int dlgId) :
- CSuper(dlgId),
- m_settings(this, IDC_SETTINGS),
- spin(this, IDC_SPIN, 1024),
- msg1(this, IDC_SETTINGMSG),
- msg2(this, IDC_SETTINGMSG2),
- edtNumber(this, IDC_SETTINGNUMBER),
- chkAdvanced(this, IDC_USEADVANCED),
- chkShowDelivery(this, IDC_SHOWDELIVERYMSGS)
-{
- CreateLink(chkAdvanced, g_plugin.bUseAdvanced);
- CreateLink(chkShowDelivery, g_plugin.bShowDelivery);
-
- m_settings.OnSelChange = Callback(this, &COptionsDlg::onSelChange_Settings);
-}
-
-bool COptionsDlg::OnInitDialog()
-{
- CSuper::OnInitDialog();
-
- m_settings.AddString(TranslateT("Send If My Status Is..."));
- m_settings.AddString(TranslateT("Send If They Change Status to..."));
- m_settings.AddString(L"----------------------------");
- m_settings.AddString(TranslateT("Reuse Pounce"));
- m_settings.AddString(TranslateT("Give Up delay"));
- m_settings.AddString(TranslateT("Confirmation Window"));
- m_settings.SetCurSel(g_plugin.getByte(hContact, "LastSetting"));
- onSelChange_Settings(0);
- return true;
-}
-
-bool COptionsDlg::OnApply()
-{
- CSuper::OnApply();
-
- saveLastSetting();
- return true;
-}
-
-void COptionsDlg::OnDestroy()
-{
- if (SendIfMy)
- DestroyWindow(SendIfMy);
- if (SendWhenThey)
- DestroyWindow(SendWhenThey);
-}
-
-void COptionsDlg::onSelChange_Settings(CCtrlListBox*)
-{
- if (m_bInitialized)
- saveLastSetting();
-
- int item = m_settings.GetCurSel();
- switch (item) {
- case 0: // Send If My Status Is...
- showAll(false);
- if (m_bInitialized)
- statusModes(true);
- break;
-
- case 1: // Send If They Change status to
- showAll(false);
- if (m_bInitialized)
- statusModes(false);
- break;
-
- case 3: // Reuse Pounce
- showAll(true);
- msg1.SetText(TranslateT("Reuse this message? (0 to use it once)"));
- msg2.SetText(TranslateT("Times"));
- edtNumber.SetInt(g_plugin.getByte(hContact, "Reuse"));
- break;
-
- case 4: // Give Up delay
- showAll(true);
- msg1.SetText(TranslateT("Give up after... (0 to not give up)"));
- msg2.SetText(TranslateT("Days"));
- edtNumber.SetInt(g_plugin.getByte(hContact, "GiveUpDays"));
- break;
-
- case 5: // confirm window
- showAll(true);
- msg1.SetText(TranslateT("Show confirmation window? (0 to not Show)"));
- msg2.SetText(TranslateT("Seconds to wait before sending"));
- edtNumber.SetInt(g_plugin.getWord(hContact, "ConfirmTimeout"));
- break;
- }
- g_plugin.setByte(hContact, "LastSetting", (uint8_t)item);
- NotifyChange();
-}
-
-void COptionsDlg::saveLastSetting()
-{
- switch (g_plugin.getByte(hContact, "LastSetting", 2)) {
- case 3: // Reuse Pounce
- g_plugin.setByte(hContact, "Reuse", (uint8_t)edtNumber.GetInt());
- break;
- case 4: // Give Up delay
- g_plugin.setByte(hContact, "GiveUpDays", (uint8_t)edtNumber.GetInt());
- g_plugin.setDword(hContact, "GiveUpDate", (uint32_t)edtNumber.GetInt() * SECONDSINADAY);
- break;
- case 5: // confirm window
- g_plugin.setWord(hContact, "ConfirmTimeout", (uint16_t)edtNumber.GetInt());
- break;
- }
-}
-
-void COptionsDlg::showAll(bool bShow)
-{
- msg1.Show(bShow);
- msg2.Show(bShow);
- spin.Show(bShow);
- edtNumber.Show(bShow);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Status modes dialog wrapper
-
-class CStatusModesDlg : public CDlgBase
-{
- COptionsDlg *pDlg;
- bool isMe;
-
- CCtrlCheck chk1, chk2, chk3, chk4, chk5, chk6, chk7, chk8;
-
-public:
- CStatusModesDlg(COptionsDlg *_1, bool _2) :
- CDlgBase(g_plugin, IDD_STATUSMODES),
- pDlg(_1),
- isMe(_2),
- chk1(this, IDC_CHECK1),
- chk2(this, IDC_CHECK2),
- chk3(this, IDC_CHECK3),
- chk4(this, IDC_CHECK4),
- chk5(this, IDC_CHECK5),
- chk6(this, IDC_CHECK6),
- chk7(this, IDC_CHECK7),
- chk8(this, IDC_CHECK8)
- {
- SetParent(pDlg->GetHwnd());
- }
-
- bool OnInitDialog() override
- {
- int statusFlag;
-
- if (isMe) {
- pDlg->SendIfMy = m_hwnd;
- statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfMyStatusIsFLAG", 0);
- SetCaption(TranslateT("Send If My Status Is"));
- chk1.SetText(TranslateT("Any"));
- chk2.SetText(TranslateT("Online"));
- chk3.SetText(TranslateT("Away"));
- chk4.SetText(TranslateT("Not available"));
- chk5.SetText(TranslateT("Occupied"));
- chk6.SetText(TranslateT("Do not disturb"));
- chk7.SetText(TranslateT("Free for chat"));
- chk8.SetText(TranslateT("Invisible"));
- }
- else {
- pDlg->SendWhenThey = m_hwnd;
- statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", 0);
- SetCaption(TranslateT("Send If Their Status changes"));
- chk1.SetText(TranslateT("From Offline"));
- chk2.SetText(TranslateT("To Online"));
- chk3.SetText(TranslateT("To Away"));
- chk4.SetText(TranslateT("To Not available"));
- chk5.SetText(TranslateT("To Occupied"));
- chk6.SetText(TranslateT("To Do not disturb"));
- chk7.SetText(TranslateT("To Free for chat"));
- chk8.SetText(TranslateT("To Invisible"));
- }
-
- chk1.SetState((statusFlag & ANY) != 0);
- chk2.SetState((statusFlag & ONLINE) != 0);
- chk3.SetState((statusFlag & AWAY) != 0);
- chk4.SetState((statusFlag & NA) != 0);
- chk5.SetState((statusFlag & OCCUPIED) != 0);
- chk6.SetState((statusFlag & DND) != 0);
- chk7.SetState((statusFlag & FFC) != 0);
- chk8.SetState((statusFlag & INVISIBLE) != 0);
- return true;
- }
-
- bool OnApply() override
- {
- int flag = chk1.GetState()
- | (chk2.GetState() << 1)
- | (chk3.GetState() << 2)
- | (chk4.GetState() << 3)
- | (chk5.GetState() << 4)
- | (chk6.GetState() << 5)
- | (chk7.GetState() << 6)
- | (chk8.GetState() << 7);
-
- if (isMe)
- g_plugin.setWord(pDlg->hContact, "SendIfMyStatusIsFLAG", flag);
- else
- g_plugin.setWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", flag);
- return true;
- }
-
- void OnDestroy() override
- {
- if (isMe)
- pDlg->SendIfMy = nullptr;
- else
- pDlg->SendWhenThey = nullptr;
- }
-};
-
-void COptionsDlg::statusModes(bool isMe)
-{
- if (isMe) {
- if (SendIfMy)
- SetForegroundWindow(SendIfMy);
- else
- (new CStatusModesDlg(this, true))->Create();
- }
- else {
- if (SendWhenThey)
- SetForegroundWindow(SendWhenThey);
- else
- (new CStatusModesDlg(this, false))->Create();
- }
-}
+/*
+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"
+
+COptionsDlg::COptionsDlg(int dlgId) :
+ CSuper(dlgId),
+ m_settings(this, IDC_SETTINGS),
+ spin(this, IDC_SPIN, 1024),
+ msg1(this, IDC_SETTINGMSG),
+ msg2(this, IDC_SETTINGMSG2),
+ edtNumber(this, IDC_SETTINGNUMBER),
+ chkAdvanced(this, IDC_USEADVANCED),
+ chkShowDelivery(this, IDC_SHOWDELIVERYMSGS)
+{
+ CreateLink(chkAdvanced, g_plugin.bUseAdvanced);
+ CreateLink(chkShowDelivery, g_plugin.bShowDelivery);
+
+ m_settings.OnSelChange = Callback(this, &COptionsDlg::onSelChange_Settings);
+}
+
+bool COptionsDlg::OnInitDialog()
+{
+ CSuper::OnInitDialog();
+
+ m_settings.AddString(TranslateT("Send If My Status Is..."));
+ m_settings.AddString(TranslateT("Send If They Change Status to..."));
+ m_settings.AddString(L"----------------------------");
+ m_settings.AddString(TranslateT("Reuse Pounce"));
+ m_settings.AddString(TranslateT("Give Up delay"));
+ m_settings.AddString(TranslateT("Confirmation Window"));
+ m_settings.SetCurSel(g_plugin.getByte(hContact, "LastSetting"));
+ onSelChange_Settings(0);
+ return true;
+}
+
+bool COptionsDlg::OnApply()
+{
+ CSuper::OnApply();
+
+ saveLastSetting();
+ return true;
+}
+
+void COptionsDlg::OnDestroy()
+{
+ if (SendIfMy)
+ DestroyWindow(SendIfMy);
+ if (SendWhenThey)
+ DestroyWindow(SendWhenThey);
+}
+
+void COptionsDlg::onSelChange_Settings(CCtrlListBox*)
+{
+ if (m_bInitialized)
+ saveLastSetting();
+
+ int item = m_settings.GetCurSel();
+ switch (item) {
+ case 0: // Send If My Status Is...
+ showAll(false);
+ if (m_bInitialized)
+ statusModes(true);
+ break;
+
+ case 1: // Send If They Change status to
+ showAll(false);
+ if (m_bInitialized)
+ statusModes(false);
+ break;
+
+ case 3: // Reuse Pounce
+ showAll(true);
+ msg1.SetText(TranslateT("Reuse this message? (0 to use it once)"));
+ msg2.SetText(TranslateT("Times"));
+ edtNumber.SetInt(g_plugin.getByte(hContact, "Reuse"));
+ break;
+
+ case 4: // Give Up delay
+ showAll(true);
+ msg1.SetText(TranslateT("Give up after... (0 to not give up)"));
+ msg2.SetText(TranslateT("Days"));
+ edtNumber.SetInt(g_plugin.getByte(hContact, "GiveUpDays"));
+ break;
+
+ case 5: // confirm window
+ showAll(true);
+ msg1.SetText(TranslateT("Show confirmation window? (0 to not Show)"));
+ msg2.SetText(TranslateT("Seconds to wait before sending"));
+ edtNumber.SetInt(g_plugin.getWord(hContact, "ConfirmTimeout"));
+ break;
+ }
+ g_plugin.setByte(hContact, "LastSetting", (uint8_t)item);
+ NotifyChange();
+}
+
+void COptionsDlg::saveLastSetting()
+{
+ switch (g_plugin.getByte(hContact, "LastSetting", 2)) {
+ case 3: // Reuse Pounce
+ g_plugin.setByte(hContact, "Reuse", (uint8_t)edtNumber.GetInt());
+ break;
+ case 4: // Give Up delay
+ g_plugin.setByte(hContact, "GiveUpDays", (uint8_t)edtNumber.GetInt());
+ g_plugin.setDword(hContact, "GiveUpDate", (uint32_t)edtNumber.GetInt() * SECONDSINADAY);
+ break;
+ case 5: // confirm window
+ g_plugin.setWord(hContact, "ConfirmTimeout", (uint16_t)edtNumber.GetInt());
+ break;
+ }
+}
+
+void COptionsDlg::showAll(bool bShow)
+{
+ msg1.Show(bShow);
+ msg2.Show(bShow);
+ spin.Show(bShow);
+ edtNumber.Show(bShow);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Status modes dialog wrapper
+
+class CStatusModesDlg : public CDlgBase
+{
+ COptionsDlg *pDlg;
+ bool isMe;
+
+ CCtrlCheck chk1, chk2, chk3, chk4, chk5, chk6, chk7, chk8;
+
+public:
+ CStatusModesDlg(COptionsDlg *_1, bool _2) :
+ CDlgBase(g_plugin, IDD_STATUSMODES),
+ pDlg(_1),
+ isMe(_2),
+ chk1(this, IDC_CHECK1),
+ chk2(this, IDC_CHECK2),
+ chk3(this, IDC_CHECK3),
+ chk4(this, IDC_CHECK4),
+ chk5(this, IDC_CHECK5),
+ chk6(this, IDC_CHECK6),
+ chk7(this, IDC_CHECK7),
+ chk8(this, IDC_CHECK8)
+ {
+ SetParent(pDlg->GetHwnd());
+ }
+
+ bool OnInitDialog() override
+ {
+ int statusFlag;
+
+ if (isMe) {
+ pDlg->SendIfMy = m_hwnd;
+ statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfMyStatusIsFLAG", 0);
+ SetCaption(TranslateT("Send If My Status Is"));
+ chk1.SetText(TranslateT("Any"));
+ chk2.SetText(TranslateT("Online"));
+ chk3.SetText(TranslateT("Away"));
+ chk4.SetText(TranslateT("Not available"));
+ chk5.SetText(TranslateT("Occupied"));
+ chk6.SetText(TranslateT("Do not disturb"));
+ chk7.SetText(TranslateT("Free for chat"));
+ chk8.SetText(TranslateT("Invisible"));
+ }
+ else {
+ pDlg->SendWhenThey = m_hwnd;
+ statusFlag = g_plugin.getWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", 0);
+ SetCaption(TranslateT("Send If Their Status changes"));
+ chk1.SetText(TranslateT("From Offline"));
+ chk2.SetText(TranslateT("To Online"));
+ chk3.SetText(TranslateT("To Away"));
+ chk4.SetText(TranslateT("To Not available"));
+ chk5.SetText(TranslateT("To Occupied"));
+ chk6.SetText(TranslateT("To Do not disturb"));
+ chk7.SetText(TranslateT("To Free for chat"));
+ chk8.SetText(TranslateT("To Invisible"));
+ }
+
+ chk1.SetState((statusFlag & ANY) != 0);
+ chk2.SetState((statusFlag & ONLINE) != 0);
+ chk3.SetState((statusFlag & AWAY) != 0);
+ chk4.SetState((statusFlag & NA) != 0);
+ chk5.SetState((statusFlag & OCCUPIED) != 0);
+ chk6.SetState((statusFlag & DND) != 0);
+ chk7.SetState((statusFlag & FFC) != 0);
+ chk8.SetState((statusFlag & INVISIBLE) != 0);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int flag = chk1.GetState()
+ | (chk2.GetState() << 1)
+ | (chk3.GetState() << 2)
+ | (chk4.GetState() << 3)
+ | (chk5.GetState() << 4)
+ | (chk6.GetState() << 5)
+ | (chk7.GetState() << 6)
+ | (chk8.GetState() << 7);
+
+ if (isMe)
+ g_plugin.setWord(pDlg->hContact, "SendIfMyStatusIsFLAG", flag);
+ else
+ g_plugin.setWord(pDlg->hContact, "SendIfTheirStatusIsFLAG", flag);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ if (isMe)
+ pDlg->SendIfMy = nullptr;
+ else
+ pDlg->SendWhenThey = nullptr;
+ }
+};
+
+void COptionsDlg::statusModes(bool isMe)
+{
+ if (isMe) {
+ if (SendIfMy)
+ SetForegroundWindow(SendIfMy);
+ else
+ (new CStatusModesDlg(this, true))->Create();
+ }
+ else {
+ if (SendWhenThey)
+ SetForegroundWindow(SendWhenThey);
+ else
+ (new CStatusModesDlg(this, false))->Create();
+ }
+}
diff --git a/plugins/BuddyPounce/src/stdafx.cxx b/plugins/BuddyPounce/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/BuddyPounce/src/stdafx.cxx
+++ b/plugins/BuddyPounce/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/plugins/CSList/src/stdafx.cxx b/plugins/CSList/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/CSList/src/stdafx.cxx
+++ b/plugins/CSList/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/plugins/CSList/src/version.h b/plugins/CSList/src/version.h
index d85a555d5d..c4330c6960 100644
--- a/plugins/CSList/src/version.h
+++ b/plugins/CSList/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "This plugin offers simple management functions to keep your extra statuses on one place."
#define __AUTHOR "Mataes, jarvis"
#define __AUTHORWEB "https://miranda-ng.org/p/CSList"
-#define __COPYRIGHT "© 2010-22 Mataes, 2007-09 jarvis"
+#define __COPYRIGHT "© 2010-23 Mataes, 2007-09 jarvis"
diff --git a/plugins/ChangeKeyboardLayout/src/stdafx.cxx b/plugins/ChangeKeyboardLayout/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ChangeKeyboardLayout/src/stdafx.cxx
+++ b/plugins/ChangeKeyboardLayout/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/plugins/ClientChangeNotify/src/stdafx.cxx b/plugins/ClientChangeNotify/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ClientChangeNotify/src/stdafx.cxx
+++ b/plugins/ClientChangeNotify/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/plugins/Clist_blind/src/clc.cpp b/plugins/Clist_blind/src/clc.cpp
index ca86f428cc..dbdc1fec5a 100644
--- a/plugins/Clist_blind/src/clc.cpp
+++ b/plugins/Clist_blind/src/clc.cpp
@@ -1,368 +1,368 @@
-/*
-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"
-
-static void ScrollTo(HWND, ClcData *dat, int, int)
-{
- ListView_SetSelectionMark(dat->hwnd_list, dat->selection);
- ListView_SetItemState(dat->hwnd_list, dat->selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-wchar_t status_name[128];
-static wchar_t* GetStatusName(ClcContact *item)
-{
- status_name[0] = '\0';
- if (item->hContact == NULL || item->pce->szProto == nullptr)
- return status_name;
-
- // Get XStatusName
- MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusName", status_name, _countof(status_name), nullptr);
- if (status_name[0] != '\0')
- return status_name;
-
- // Get status name
- int status = db_get_w(item->hContact, item->pce->szProto, "Status", ID_STATUS_OFFLINE);
- mir_wstrncpy(status_name, Clist_GetStatusModeDescription(status, 0), _countof(status_name));
- return status_name;
-}
-
-wchar_t status_message[256];
-static wchar_t* GetStatusMessage(ClcContact *item)
-{
- status_message[0] = '\0';
- if (item->hContact == NULL || item->pce->szProto == nullptr)
- return status_message;
-
- // Get XStatusMsg
- MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusMsg", status_message, _countof(status_message), nullptr);
- if (status_message[0] != '\0')
- return status_message;
-
- // Get status message
- MyDBGetContactSettingTString(item->hContact, "CList", "StatusMsg", status_message, _countof(status_message), nullptr);
- return status_message;
-}
-
-wchar_t proto_name[128];
-static wchar_t* GetProtoName(ClcContact *item)
-{
- proto_name[0] = '\0';
- if (item->hContact == NULL || item->pce->szProto == nullptr) {
- mir_wstrncpy(proto_name, TranslateT("Unknown protocol"), _countof(proto_name));
- return proto_name;
- }
-
- PROTOACCOUNT *acc = Proto_GetAccount(item->pce->szProto);
- if (acc == nullptr) {
- char description[128];
- CallProtoService(item->pce->szProto, PS_GETNAME, sizeof(description), (LPARAM)description);
- mir_snwprintf(proto_name, L"%S", description);
- return proto_name;
- }
-
- mir_wstrncpy(proto_name, acc->tszAccountName, _countof(proto_name));
- return proto_name;
-}
-
-static void RebuildEntireListInternal(HWND hwnd, ClcData *tmp_dat, bool call_orig)
-{
- ClcData *dat = tmp_dat;
- int level = 0, iItem = 0;
-
- wchar_t tmp[1024];
- wchar_t template_contact[1024];
- wchar_t template_group[1024];
- wchar_t template_divider[1024];
- wchar_t template_info[1024];
- int selection = dat->selection;
- BOOL has_focus = (GetFocus() == dat->hwnd_list || GetFocus() == hwnd);
-
- if (call_orig)
- coreCli.pfnRebuildEntireList(hwnd, dat);
-
- MyDBGetContactSettingTString(NULL, "CLC", "TemplateContact", template_contact, 1024, TranslateT("%name% [%status% %protocol%] %status_message%"));
- MyDBGetContactSettingTString(NULL, "CLC", "TemplateGroup", template_group, 1024, TranslateT("Group: %name% %count% [%mode%]"));
- MyDBGetContactSettingTString(NULL, "CLC", "TemplateDivider", template_divider, 1024, TranslateT("Divider: %s"));
- MyDBGetContactSettingTString(NULL, "CLC", "TemplateInfo", template_info, 1024, TranslateT("Info: %s"));
-
- SendMessage(dat->hwnd_list, WM_SETREDRAW, FALSE, 0);
-
- // Reset content
- ListView_DeleteAllItems(dat->hwnd_list);
-
- // Set font
- SendMessage(dat->hwnd_list, WM_SETFONT, (WPARAM)dat->fontInfo[FONTID_CONTACTS].hFont, 0);
-
- // Add all items to the list
- ClcGroup *group = &dat->list;
- group->scanIndex = 0;
-
- wchar_t *text = tmp;
- size_t size = _countof(tmp);
- while (true) {
- if (group->scanIndex == group->cl.getCount()) {
- if ((group = group->parent) == nullptr)
- break;
-
- level--;
- group->scanIndex++;
- continue;
- }
-
- ClcContact *item = group->cl[group->scanIndex];
- text[0] = '\0';
- switch (item->type) {
- case CLCIT_GROUP:
- {
- CMStringW wszText(template_group);
- wszText.Replace(L"%name%", item->szText);
- wszText.Replace(L"%mode%", item->group->expanded ? TranslateT("Expanded") : TranslateT("Collapsed"));
-
- wchar_t *szCounts = Clist_GetGroupCountsText(dat, item);
- wchar_t count[128];
- if (szCounts[0] != '\0')
- mir_snwprintf(count, L"%s ", szCounts);
- else
- count[0] = '\0';
- wszText.Replace(L"%count%", count);
-
- if (!wszText.IsEmpty())
- mir_wstrncpy(text, wszText, size);
- }
- break;
-
- case CLCIT_CONTACT:
- {
- CMStringW wszText(template_contact);
- wszText.Replace(L"%name%", item->szText);
- wszText.Replace(L"%status%", GetStatusName(item));
- wszText.Replace(L"%protocol%", GetProtoName(item));
- wszText.Replace(L"%status_message%", GetStatusMessage(item));
-
- if (!wszText.IsEmpty())
- mir_wstrncpy(text, wszText, size);
- }
- break;
-
- case CLCIT_DIVIDER:
- mir_snwprintf(text, size, template_divider, item->szText);
- break;
-
- case CLCIT_INFO:
- mir_snwprintf(text, size, template_info, item->szText);
- break;
- }
-
- LVITEMW lvi = {};
- lvi.iItem = iItem++;
- lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_INDENT;
- lvi.pszText = tmp;
- lvi.iIndent = level;
- lvi.lParam = LPARAM(item);
- ListView_InsertItem(dat->hwnd_list, &lvi);
-
- if (item->type == CLCIT_GROUP && item->group->expanded) {
- group = item->group;
- level++;
- group->scanIndex = 0;
- continue;
- }
- group->scanIndex++;
- }
-
- SendMessage(dat->hwnd_list, WM_SETREDRAW, TRUE, 0);
- InvalidateRect(dat->hwnd_list, nullptr, TRUE);
-
- if (dat->list.cl.getCount()) {
- RECT rc;
- ListView_GetItemRect(dat->hwnd_list, 0, &rc, LVIR_SELECTBOUNDS);
- if (rc.bottom > rc.top)
- dat->rowHeight = rc.bottom - rc.top;
- }
-
- dat->selection = selection;
- ScrollTo(dat->hwnd_list, dat, 0, 0);
- if (has_focus)
- SetFocus(dat->hwnd_list);
-
- dat->bNeedsRebuild = false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_NCCREATE:
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- case WM_CREATE:
- break;
-
- case WM_DRAWITEM:
- return Menu_DrawItem(lParam);
- }
- return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
-}
-
-static LRESULT CALLBACK ContactListControlSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_KEYDOWN:
- if (wParam == VK_LEFT || wParam == VK_RIGHT || wParam == VK_RETURN || wParam == VK_DELETE || wParam == VK_F2)
- coreCli.pfnContactListControlWndProc(GetParent(hwnd), WM_KEYDOWN, wParam, 0);
- break;
-
- case WM_CHAR:
- HWND hwndParent = GetParent(hwnd);
- ClcData *dat = (ClcData *)GetWindowLongPtr(hwndParent, 0);
- int iSaveSelection = dat->selection;
-
- coreCli.pfnContactListControlWndProc(hwndParent, msg, wParam, lParam);
-
- if (iSaveSelection != dat->selection)
- ScrollTo(dat->hwnd_list, dat, 0, 0);
-
- return 0;
- }
-
- return mir_callNextSubclass(hwnd, ContactListControlSubclass, msg, wParam, lParam);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- ClcData *dat = (ClcData *)GetWindowLongPtr(hwnd, 0);
- RECT r;
-
- switch (msg) {
- case WM_CREATE:
- dat = new ClcData();
- SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat);
-
- dat->hwnd_list = CreateWindowW(WC_LISTVIEWW, L"",
- WS_VISIBLE | WS_CHILD | WS_VSCROLL | LVS_NOCOLUMNHEADER | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_REPORT,
- 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr);
- dat->bNeedsRebuild = false;
- mir_subclassWindow(dat->hwnd_list, ContactListControlSubclass);
-
- ListView_SetExtendedListViewStyle(dat->hwnd_list, LVS_EX_FULLROWSELECT);
-
- GetClientRect(hwnd, &r);
- SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
- {
- LVCOLUMN lvc = {};
- lvc.mask = LVCF_FMT | LVCF_WIDTH;
- lvc.fmt = LVCFMT_LEFT;
- lvc.cx = r.right - r.left;
- ListView_InsertColumn(dat->hwnd_list, 0, &lvc);
-
- HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 10, 1);
- ListView_SetImageList(dat->hwnd_list, hImgList, LVSIL_SMALL);
- }
- break;
-
- case WM_SIZE:
- GetClientRect(hwnd, &r);
- SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
- ListView_SetColumnWidth(dat->hwnd_list, 0, r.right - r.left);
- break;
-
- case WM_PRINTCLIENT:
- case WM_PAINT:
- if (dat->bNeedsRebuild)
- RebuildEntireListInternal(hwnd, dat, false);
- __fallthrough;
-
- case WM_VSCROLL:
- case WM_MOUSEWHEEL:
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- case INTM_SCROLLBARCHANGED:
- return TRUE;
-
- case WM_NOTIFY:
- if (LPNMHDR pnmh = (LPNMHDR)lParam)
- if (pnmh->code == LVN_ITEMCHANGED || pnmh->code == LVN_ITEMACTIVATE)
- dat->selection = ListView_GetSelectionMark(dat->hwnd_list);
- break;
-
- case WM_SETFOCUS:
- case WM_ENABLE:
- SetFocus(dat->hwnd_list);
- break;
- }
-
- return coreCli.pfnContactListControlWndProc(hwnd, msg, wParam, lParam);
-}
-
-static void RebuildEntireList(HWND hwnd, ClcData *dat)
-{
- RebuildEntireListInternal(hwnd, dat, true);
-}
-
-static void SetGroupExpand(HWND hwnd, ClcData *dat, ClcGroup *group, int newState)
-{
- coreCli.pfnSetGroupExpand(hwnd, dat, group, newState);
- dat->bNeedsRebuild = true;
-}
-
-static void RecalcScrollBar(HWND, ClcData *)
-{
-}
-
-static int GetRowHeight(ClcData *dat, int)
-{
- return dat->rowHeight;
-}
-
-static void LoadClcOptions(HWND hwnd, ClcData *dat, BOOL bFirst)
-{
- coreCli.pfnLoadClcOptions(hwnd, dat, bFirst);
-
- dat->rowHeight = 16;
-}
-
-static void SortCLC(HWND hwnd, ClcData *dat, int useInsertionSort)
-{
- if (dat->bNeedsResort) {
- coreCli.pfnSortCLC(hwnd, dat, useInsertionSort);
- dat->bNeedsRebuild = true;
- }
-}
-
-void InitClc()
-{
- Clist_GetInterface();
- coreCli = g_clistApi;
- g_clistApi.bOwnerDrawMenu = false;
- g_clistApi.hInst = g_plugin.getInst();
- g_clistApi.pfnContactListWndProc = ContactListWndProc;
- g_clistApi.pfnContactListControlWndProc = ContactListControlWndProc;
- g_clistApi.pfnRebuildEntireList = RebuildEntireList;
- g_clistApi.pfnSetGroupExpand = SetGroupExpand;
- g_clistApi.pfnRecalcScrollBar = RecalcScrollBar;
- g_clistApi.pfnScrollTo = ScrollTo;
- g_clistApi.pfnLoadClcOptions = LoadClcOptions;
- g_clistApi.pfnGetRowHeight = GetRowHeight;
- g_clistApi.pfnSortCLC = SortCLC;
- g_clistApi.pfnCompareContacts = CompareContacts;
-}
+/*
+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"
+
+static void ScrollTo(HWND, ClcData *dat, int, int)
+{
+ ListView_SetSelectionMark(dat->hwnd_list, dat->selection);
+ ListView_SetItemState(dat->hwnd_list, dat->selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+wchar_t status_name[128];
+static wchar_t* GetStatusName(ClcContact *item)
+{
+ status_name[0] = '\0';
+ if (item->hContact == NULL || item->pce->szProto == nullptr)
+ return status_name;
+
+ // Get XStatusName
+ MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusName", status_name, _countof(status_name), nullptr);
+ if (status_name[0] != '\0')
+ return status_name;
+
+ // Get status name
+ int status = db_get_w(item->hContact, item->pce->szProto, "Status", ID_STATUS_OFFLINE);
+ mir_wstrncpy(status_name, Clist_GetStatusModeDescription(status, 0), _countof(status_name));
+ return status_name;
+}
+
+wchar_t status_message[256];
+static wchar_t* GetStatusMessage(ClcContact *item)
+{
+ status_message[0] = '\0';
+ if (item->hContact == NULL || item->pce->szProto == nullptr)
+ return status_message;
+
+ // Get XStatusMsg
+ MyDBGetContactSettingTString(item->hContact, item->pce->szProto, "XStatusMsg", status_message, _countof(status_message), nullptr);
+ if (status_message[0] != '\0')
+ return status_message;
+
+ // Get status message
+ MyDBGetContactSettingTString(item->hContact, "CList", "StatusMsg", status_message, _countof(status_message), nullptr);
+ return status_message;
+}
+
+wchar_t proto_name[128];
+static wchar_t* GetProtoName(ClcContact *item)
+{
+ proto_name[0] = '\0';
+ if (item->hContact == NULL || item->pce->szProto == nullptr) {
+ mir_wstrncpy(proto_name, TranslateT("Unknown protocol"), _countof(proto_name));
+ return proto_name;
+ }
+
+ PROTOACCOUNT *acc = Proto_GetAccount(item->pce->szProto);
+ if (acc == nullptr) {
+ char description[128];
+ CallProtoService(item->pce->szProto, PS_GETNAME, sizeof(description), (LPARAM)description);
+ mir_snwprintf(proto_name, L"%S", description);
+ return proto_name;
+ }
+
+ mir_wstrncpy(proto_name, acc->tszAccountName, _countof(proto_name));
+ return proto_name;
+}
+
+static void RebuildEntireListInternal(HWND hwnd, ClcData *tmp_dat, bool call_orig)
+{
+ ClcData *dat = tmp_dat;
+ int level = 0, iItem = 0;
+
+ wchar_t tmp[1024];
+ wchar_t template_contact[1024];
+ wchar_t template_group[1024];
+ wchar_t template_divider[1024];
+ wchar_t template_info[1024];
+ int selection = dat->selection;
+ BOOL has_focus = (GetFocus() == dat->hwnd_list || GetFocus() == hwnd);
+
+ if (call_orig)
+ coreCli.pfnRebuildEntireList(hwnd, dat);
+
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateContact", template_contact, 1024, TranslateT("%name% [%status% %protocol%] %status_message%"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateGroup", template_group, 1024, TranslateT("Group: %name% %count% [%mode%]"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateDivider", template_divider, 1024, TranslateT("Divider: %s"));
+ MyDBGetContactSettingTString(NULL, "CLC", "TemplateInfo", template_info, 1024, TranslateT("Info: %s"));
+
+ SendMessage(dat->hwnd_list, WM_SETREDRAW, FALSE, 0);
+
+ // Reset content
+ ListView_DeleteAllItems(dat->hwnd_list);
+
+ // Set font
+ SendMessage(dat->hwnd_list, WM_SETFONT, (WPARAM)dat->fontInfo[FONTID_CONTACTS].hFont, 0);
+
+ // Add all items to the list
+ ClcGroup *group = &dat->list;
+ group->scanIndex = 0;
+
+ wchar_t *text = tmp;
+ size_t size = _countof(tmp);
+ while (true) {
+ if (group->scanIndex == group->cl.getCount()) {
+ if ((group = group->parent) == nullptr)
+ break;
+
+ level--;
+ group->scanIndex++;
+ continue;
+ }
+
+ ClcContact *item = group->cl[group->scanIndex];
+ text[0] = '\0';
+ switch (item->type) {
+ case CLCIT_GROUP:
+ {
+ CMStringW wszText(template_group);
+ wszText.Replace(L"%name%", item->szText);
+ wszText.Replace(L"%mode%", item->group->expanded ? TranslateT("Expanded") : TranslateT("Collapsed"));
+
+ wchar_t *szCounts = Clist_GetGroupCountsText(dat, item);
+ wchar_t count[128];
+ if (szCounts[0] != '\0')
+ mir_snwprintf(count, L"%s ", szCounts);
+ else
+ count[0] = '\0';
+ wszText.Replace(L"%count%", count);
+
+ if (!wszText.IsEmpty())
+ mir_wstrncpy(text, wszText, size);
+ }
+ break;
+
+ case CLCIT_CONTACT:
+ {
+ CMStringW wszText(template_contact);
+ wszText.Replace(L"%name%", item->szText);
+ wszText.Replace(L"%status%", GetStatusName(item));
+ wszText.Replace(L"%protocol%", GetProtoName(item));
+ wszText.Replace(L"%status_message%", GetStatusMessage(item));
+
+ if (!wszText.IsEmpty())
+ mir_wstrncpy(text, wszText, size);
+ }
+ break;
+
+ case CLCIT_DIVIDER:
+ mir_snwprintf(text, size, template_divider, item->szText);
+ break;
+
+ case CLCIT_INFO:
+ mir_snwprintf(text, size, template_info, item->szText);
+ break;
+ }
+
+ LVITEMW lvi = {};
+ lvi.iItem = iItem++;
+ lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_INDENT;
+ lvi.pszText = tmp;
+ lvi.iIndent = level;
+ lvi.lParam = LPARAM(item);
+ ListView_InsertItem(dat->hwnd_list, &lvi);
+
+ if (item->type == CLCIT_GROUP && item->group->expanded) {
+ group = item->group;
+ level++;
+ group->scanIndex = 0;
+ continue;
+ }
+ group->scanIndex++;
+ }
+
+ SendMessage(dat->hwnd_list, WM_SETREDRAW, TRUE, 0);
+ InvalidateRect(dat->hwnd_list, nullptr, TRUE);
+
+ if (dat->list.cl.getCount()) {
+ RECT rc;
+ ListView_GetItemRect(dat->hwnd_list, 0, &rc, LVIR_SELECTBOUNDS);
+ if (rc.bottom > rc.top)
+ dat->rowHeight = rc.bottom - rc.top;
+ }
+
+ dat->selection = selection;
+ ScrollTo(dat->hwnd_list, dat, 0, 0);
+ if (has_focus)
+ SetFocus(dat->hwnd_list);
+
+ dat->bNeedsRebuild = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_NCCREATE:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_CREATE:
+ break;
+
+ case WM_DRAWITEM:
+ return Menu_DrawItem(lParam);
+ }
+ return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK ContactListControlSubclass(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ if (wParam == VK_LEFT || wParam == VK_RIGHT || wParam == VK_RETURN || wParam == VK_DELETE || wParam == VK_F2)
+ coreCli.pfnContactListControlWndProc(GetParent(hwnd), WM_KEYDOWN, wParam, 0);
+ break;
+
+ case WM_CHAR:
+ HWND hwndParent = GetParent(hwnd);
+ ClcData *dat = (ClcData *)GetWindowLongPtr(hwndParent, 0);
+ int iSaveSelection = dat->selection;
+
+ coreCli.pfnContactListControlWndProc(hwndParent, msg, wParam, lParam);
+
+ if (iSaveSelection != dat->selection)
+ ScrollTo(dat->hwnd_list, dat, 0, 0);
+
+ return 0;
+ }
+
+ return mir_callNextSubclass(hwnd, ContactListControlSubclass, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static LRESULT CALLBACK ContactListControlWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ ClcData *dat = (ClcData *)GetWindowLongPtr(hwnd, 0);
+ RECT r;
+
+ switch (msg) {
+ case WM_CREATE:
+ dat = new ClcData();
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)dat);
+
+ dat->hwnd_list = CreateWindowW(WC_LISTVIEWW, L"",
+ WS_VISIBLE | WS_CHILD | WS_VSCROLL | LVS_NOCOLUMNHEADER | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_REPORT,
+ 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr);
+ dat->bNeedsRebuild = false;
+ mir_subclassWindow(dat->hwnd_list, ContactListControlSubclass);
+
+ ListView_SetExtendedListViewStyle(dat->hwnd_list, LVS_EX_FULLROWSELECT);
+
+ GetClientRect(hwnd, &r);
+ SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+ {
+ LVCOLUMN lvc = {};
+ lvc.mask = LVCF_FMT | LVCF_WIDTH;
+ lvc.fmt = LVCFMT_LEFT;
+ lvc.cx = r.right - r.left;
+ ListView_InsertColumn(dat->hwnd_list, 0, &lvc);
+
+ HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 10, 1);
+ ListView_SetImageList(dat->hwnd_list, hImgList, LVSIL_SMALL);
+ }
+ break;
+
+ case WM_SIZE:
+ GetClientRect(hwnd, &r);
+ SetWindowPos(dat->hwnd_list, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER | SWP_NOACTIVATE);
+ ListView_SetColumnWidth(dat->hwnd_list, 0, r.right - r.left);
+ break;
+
+ case WM_PRINTCLIENT:
+ case WM_PAINT:
+ if (dat->bNeedsRebuild)
+ RebuildEntireListInternal(hwnd, dat, false);
+ __fallthrough;
+
+ case WM_VSCROLL:
+ case WM_MOUSEWHEEL:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case INTM_SCROLLBARCHANGED:
+ return TRUE;
+
+ case WM_NOTIFY:
+ if (LPNMHDR pnmh = (LPNMHDR)lParam)
+ if (pnmh->code == LVN_ITEMCHANGED || pnmh->code == LVN_ITEMACTIVATE)
+ dat->selection = ListView_GetSelectionMark(dat->hwnd_list);
+ break;
+
+ case WM_SETFOCUS:
+ case WM_ENABLE:
+ SetFocus(dat->hwnd_list);
+ break;
+ }
+
+ return coreCli.pfnContactListControlWndProc(hwnd, msg, wParam, lParam);
+}
+
+static void RebuildEntireList(HWND hwnd, ClcData *dat)
+{
+ RebuildEntireListInternal(hwnd, dat, true);
+}
+
+static void SetGroupExpand(HWND hwnd, ClcData *dat, ClcGroup *group, int newState)
+{
+ coreCli.pfnSetGroupExpand(hwnd, dat, group, newState);
+ dat->bNeedsRebuild = true;
+}
+
+static void RecalcScrollBar(HWND, ClcData *)
+{
+}
+
+static int GetRowHeight(ClcData *dat, int)
+{
+ return dat->rowHeight;
+}
+
+static void LoadClcOptions(HWND hwnd, ClcData *dat, BOOL bFirst)
+{
+ coreCli.pfnLoadClcOptions(hwnd, dat, bFirst);
+
+ dat->rowHeight = 16;
+}
+
+static void SortCLC(HWND hwnd, ClcData *dat, int useInsertionSort)
+{
+ if (dat->bNeedsResort) {
+ coreCli.pfnSortCLC(hwnd, dat, useInsertionSort);
+ dat->bNeedsRebuild = true;
+ }
+}
+
+void InitClc()
+{
+ Clist_GetInterface();
+ coreCli = g_clistApi;
+ g_clistApi.bOwnerDrawMenu = false;
+ g_clistApi.hInst = g_plugin.getInst();
+ g_clistApi.pfnContactListWndProc = ContactListWndProc;
+ g_clistApi.pfnContactListControlWndProc = ContactListControlWndProc;
+ g_clistApi.pfnRebuildEntireList = RebuildEntireList;
+ g_clistApi.pfnSetGroupExpand = SetGroupExpand;
+ g_clistApi.pfnRecalcScrollBar = RecalcScrollBar;
+ g_clistApi.pfnScrollTo = ScrollTo;
+ g_clistApi.pfnLoadClcOptions = LoadClcOptions;
+ g_clistApi.pfnGetRowHeight = GetRowHeight;
+ g_clistApi.pfnSortCLC = SortCLC;
+ g_clistApi.pfnCompareContacts = CompareContacts;
+}
diff --git a/plugins/Clist_blind/src/clc.h b/plugins/Clist_blind/src/clc.h
index 32ea4b9fbb..828cd5793d 100644
--- a/plugins/Clist_blind/src/clc.h
+++ b/plugins/Clist_blind/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/clcopts.cpp b/plugins/Clist_blind/src/clcopts.cpp
index 023d115fd7..1c540bc780 100644
--- a/plugins/Clist_blind/src/clcopts.cpp
+++ b/plugins/Clist_blind/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/clistmenus.cpp b/plugins/Clist_blind/src/clistmenus.cpp
index e9700f7bca..718e878b96 100644
--- a/plugins/Clist_blind/src/clistmenus.cpp
+++ b/plugins/Clist_blind/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/clistopts.cpp b/plugins/Clist_blind/src/clistopts.cpp
index 5ab790bb57..28352cce7c 100644
--- a/plugins/Clist_blind/src/clistopts.cpp
+++ b/plugins/Clist_blind/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/cluiopts.cpp b/plugins/Clist_blind/src/cluiopts.cpp
index 4b7a805a08..f785534c7e 100644
--- a/plugins/Clist_blind/src/cluiopts.cpp
+++ b/plugins/Clist_blind/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/contact.cpp b/plugins/Clist_blind/src/contact.cpp
index 96d106b544..2e024404db 100644
--- a/plugins/Clist_blind/src/contact.cpp
+++ b/plugins/Clist_blind/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/plugins/Clist_blind/src/init.cpp b/plugins/Clist_blind/src/init.cpp
index b9da691500..2e05c537f4 100644
--- a/plugins/Clist_blind/src/init.cpp
+++ b/plugins/Clist_blind/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-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/stdafx.cxx b/plugins/Clist_blind/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Clist_blind/src/stdafx.cxx
+++ b/plugins/Clist_blind/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/plugins/Clist_blind/src/stdafx.h b/plugins/Clist_blind/src/stdafx.h
index abb668e1da..b896fe42ff 100644
--- a/plugins/Clist_blind/src/stdafx.h
+++ b/plugins/Clist_blind/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-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_blind/src/utils.cpp b/plugins/Clist_blind/src/utils.cpp
index b53d78a47c..211139a1cc 100644
--- a/plugins/Clist_blind/src/utils.cpp
+++ b/plugins/Clist_blind/src/utils.cpp
@@ -1,41 +1,41 @@
-/*
-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"
-
-wchar_t* MyDBGetContactSettingTString(MCONTACT hContact, char *module, char *setting, wchar_t *out, size_t len, wchar_t *def)
-{
- out[0] = '\0';
-
- DBVARIANT dbv;
- if (!db_get_ws(hContact, module, setting, &dbv)) {
- if (dbv.type == DBVT_ASCIIZ)
- MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, out, (int)len);
- else if (dbv.type == DBVT_UTF8)
- MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, out, (int)len);
- else if (dbv.type == DBVT_WCHAR)
- mir_wstrncpy(out, dbv.pwszVal, len);
- else if (def != nullptr)
- mir_wstrncpy(out, def, len);
-
- db_free(&dbv);
- }
- else if (def != nullptr)
- mir_wstrncpy(out, def, len);
-
- return out;
-}
+/*
+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"
+
+wchar_t* MyDBGetContactSettingTString(MCONTACT hContact, char *module, char *setting, wchar_t *out, size_t len, wchar_t *def)
+{
+ out[0] = '\0';
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, module, setting, &dbv)) {
+ if (dbv.type == DBVT_ASCIIZ)
+ MultiByteToWideChar(CP_ACP, 0, dbv.pszVal, -1, out, (int)len);
+ else if (dbv.type == DBVT_UTF8)
+ MultiByteToWideChar(CP_UTF8, 0, dbv.pszVal, -1, out, (int)len);
+ else if (dbv.type == DBVT_WCHAR)
+ mir_wstrncpy(out, dbv.pwszVal, len);
+ else if (def != nullptr)
+ mir_wstrncpy(out, def, len);
+
+ db_free(&dbv);
+ }
+ else if (def != nullptr)
+ mir_wstrncpy(out, def, len);
+
+ return out;
+}
diff --git a/plugins/Clist_blind/src/version.h b/plugins/Clist_blind/src/version.h
index c641efe7ce..d7b227fa66 100644
--- a/plugins/Clist_blind/src/version.h
+++ b/plugins/Clist_blind/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "A contact list for blind folks."
#define __AUTHOR "Ricardo Pescuma Domenecci, based on previous work from Miranda IM project"
#define __AUTHORWEB "https://miranda-ng.org/p/Clist_blind"
-#define __COPYRIGHT "© 2000-2009 Miranda IM project, Ricardo Pescuma Domenecci, 2013-22 Miranda NG team"
+#define __COPYRIGHT "© 2000-2009 Miranda IM project, Ricardo Pescuma Domenecci, 2013-23 Miranda NG team"
diff --git a/plugins/Clist_modern/src/cluiframes.cpp b/plugins/Clist_modern/src/cluiframes.cpp
index c2cb8b459c..250c221327 100644
--- a/plugins/Clist_modern/src/cluiframes.cpp
+++ b/plugins/Clist_modern/src/cluiframes.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/plugins/Clist_modern/src/groupmenu.cpp b/plugins/Clist_modern/src/groupmenu.cpp
index 82e2873a2f..c3d6d99d0b 100644
--- a/plugins/Clist_modern/src/groupmenu.cpp
+++ b/plugins/Clist_modern/src/groupmenu.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/plugins/Clist_modern/src/init.cpp b/plugins/Clist_modern/src/init.cpp
index cfddf29267..3026d17ba2 100644
--- a/plugins/Clist_modern/src/init.cpp
+++ b/plugins/Clist_modern/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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_aniavatars.cpp b/plugins/Clist_modern/src/modern_aniavatars.cpp
index 327ed73f7f..babff44fa8 100644
--- a/plugins/Clist_modern/src/modern_aniavatars.cpp
+++ b/plugins/Clist_modern/src/modern_aniavatars.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/plugins/Clist_modern/src/modern_awaymsg.cpp b/plugins/Clist_modern/src/modern_awaymsg.cpp
index 31a9bcabed..cf8897d329 100644
--- a/plugins/Clist_modern/src/modern_awaymsg.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_awaymsg.h b/plugins/Clist_modern/src/modern_awaymsg.h
index e8b3e1c3cb..d5c0cf6337 100644
--- a/plugins/Clist_modern/src/modern_awaymsg.h
+++ b/plugins/Clist_modern/src/modern_awaymsg.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_cache_funcs.h b/plugins/Clist_modern/src/modern_cache_funcs.h
index 5adc1db25b..03f8d5ab7d 100644
--- a/plugins/Clist_modern/src/modern_cache_funcs.h
+++ b/plugins/Clist_modern/src/modern_cache_funcs.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_cachefuncs.cpp b/plugins/Clist_modern/src/modern_cachefuncs.cpp
index cb977bea6e..1fff523f92 100644
--- a/plugins/Clist_modern/src/modern_cachefuncs.cpp
+++ b/plugins/Clist_modern/src/modern_cachefuncs.cpp
@@ -1,745 +1,745 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 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.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-Created by Pescuma
-Modified by FYR
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Module for working with lines text and avatars
-
-#include "stdafx.h"
-#include "modern_sync.h"
-
-typedef BOOL(*ExecuteOnAllContactsFuncPtr) (ClcContact *contact, BOOL subcontact, void *param);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Module static declarations
-
-static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size);
-
-static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param);
-static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Get time zone for contact
-//
-void Cache_GetTimezone(ClcData *dat, MCONTACT hContact)
-{
- ClcCacheEntry *pdnce = Clist_GetCacheEntry(hContact);
- if (dat == nullptr && g_clistApi.hwndContactTree)
- dat = (ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
-
- if (dat && dat->hWnd == g_clistApi.hwndContactTree) {
- uint32_t flags = dat->contact_time_show_only_if_different ? TZF_DIFONLY : 0;
- pdnce->hTimeZone = TimeZone_CreateByContact(hContact, nullptr, flags);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Get all lines of text
-//
-void Cache_GetText(ClcData *dat, ClcContact *contact)
-{
- Cache_GetFirstLineText(dat, contact);
-
- if (!dat->bForceInDialog) {
- if (g_plugin.secondLine.bActive)
- Cache_GetNthLineText(dat, contact->pce, 2);
- if (g_plugin.thirdLine.bActive)
- Cache_GetNthLineText(dat, contact->pce, 3);
- }
-}
-
-void CSmileyString::AddListeningToIcon(ClcData *dat, wchar_t *szText)
-{
- iMaxSmileyHeight = 0;
- DestroySmileyList();
-
- if (szText == nullptr)
- return;
-
- int text_size = (int)mir_wstrlen(szText);
-
- plText = List_Create(0, 1);
-
- // Add Icon
- {
- ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
- piece->type = TEXT_PIECE_TYPE_SMILEY;
- piece->len = 0;
- piece->smiley = g_hListeningToIcon;
- piece->smiley_width = 16;
- piece->smiley_height = 16;
-
- ICONINFO icon;
- if (GetIconInfo(piece->smiley, &icon)) {
- BITMAP bm;
- if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) {
- piece->smiley_width = bm.bmWidth;
- piece->smiley_height = bm.bmHeight;
- }
-
- DeleteObject(icon.hbmMask);
- DeleteObject(icon.hbmColor);
- }
-
- dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height);
- iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight);
-
- List_Insert(plText, piece, plText->realCount);
- }
-
- // Add text
- {
- ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
- piece->type = TEXT_PIECE_TYPE_TEXT;
- piece->start_pos = 0;
- piece->len = text_size;
- List_Insert(plText, piece, plText->realCount);
- }
-}
-
-void CSmileyString::_CopySmileyList(SortedList *plInput)
-{
- if (!plInput || plInput->realCount == 0)
- return;
-
- plText = List_Create(0, 1);
- for (int i = 0; i < plInput->realCount; i++) {
- ClcContactTextPiece *pieceFrom = (ClcContactTextPiece *)plInput->items[i];
- if (pieceFrom != nullptr) {
- ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
- *piece = *pieceFrom;
- if (pieceFrom->type == TEXT_PIECE_TYPE_SMILEY)
- piece->smiley = CopyIcon(pieceFrom->smiley);
- List_Insert(plText, piece, plText->realCount);
- }
- }
-}
-
-void CSmileyString::DestroySmileyList()
-{
- if (plText == nullptr)
- return;
-
- if (IsBadReadPtr(plText, sizeof(SortedList))) {
- plText = nullptr;
- return;
- }
-
- if (plText->realCount != 0) {
- for (int i = 0; i < plText->realCount; i++) {
- if (plText->items[i] != nullptr) {
- ClcContactTextPiece *piece = (ClcContactTextPiece *)plText->items[i];
-
- if (!IsBadWritePtr(piece, sizeof(ClcContactTextPiece))) {
- if (piece->type == TEXT_PIECE_TYPE_SMILEY && piece->smiley != g_hListeningToIcon)
- DestroyIcon_protect(piece->smiley);
- mir_free(piece);
- }
- }
- }
- List_Destroy(plText);
- }
- mir_free(plText);
-
- plText = nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Parsing of text for smiley
-//
-void CSmileyString::ReplaceSmileys(ClcData *dat, ClcCacheEntry *pdnce, wchar_t *szText, BOOL replace_smileys)
-{
- int last_pos = 0;
- iMaxSmileyHeight = 0;
-
- DestroySmileyList();
-
- if (!dat->text_replace_smileys || !replace_smileys || szText == nullptr)
- return;
-
- int text_size = (int)mir_wstrlen(szText);
-
- // Call service for the first time to see if needs to be used...
- SMADD_BATCHPARSE2 sp = {};
- sp.cbSize = sizeof(sp);
- sp.hContact = pdnce->hContact;
-
- if (dat->text_use_protocol_smileys) {
- sp.Protocolname = pdnce->szProto;
-
- if (db_get_b(0, "CLC", "Meta", SETTING_USEMETAICON_DEFAULT) != 1 && pdnce->szProto != nullptr && mir_strcmp(pdnce->szProto, META_PROTO) == 0) {
- MCONTACT hContact = db_mc_getMostOnline(pdnce->hContact);
- if (hContact != 0)
- sp.Protocolname = Proto_GetBaseAccountName(hContact);
- }
- }
- else sp.Protocolname = "clist";
-
- sp.str = szText;
- sp.flag = SAFL_TCHAR;
-
- SMADD_BATCHPARSERES *spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp);
-
- // Did not find a simley
- if (spr == nullptr || (INT_PTR)spr == CALLSERVICE_NOTFOUND)
- return;
-
- // Lets add smileys
- plText = List_Create(0, 1);
-
- for (unsigned i = 0; i < sp.numSmileys; ++i) {
- if (spr[i].hIcon != nullptr) { // For deffective smileypacks
- // Add text
- if (spr[i].startChar - last_pos > 0) {
- ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
-
- piece->type = TEXT_PIECE_TYPE_TEXT;
- piece->start_pos = last_pos;//sp.str - text;
- piece->len = spr[i].startChar - last_pos;
- List_Insert(plText, piece, plText->realCount);
- }
-
- // Add smiley
- {
- BITMAP bm;
- ICONINFO icon;
- ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
-
- piece->type = TEXT_PIECE_TYPE_SMILEY;
- piece->len = spr[i].size;
- piece->smiley = spr[i].hIcon;
-
- piece->smiley_width = 16;
- piece->smiley_height = 16;
- if (GetIconInfo(piece->smiley, &icon)) {
- if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) {
- piece->smiley_width = bm.bmWidth;
- piece->smiley_height = bm.bmHeight;
- }
-
- DeleteObject(icon.hbmMask);
- DeleteObject(icon.hbmColor);
- }
-
- dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height);
- iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight);
-
- List_Insert(plText, piece, plText->realCount);
- }
- }
- // Get next
- last_pos = spr[i].startChar + spr[i].size;
- }
- CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr);
-
- // Add rest of text
- if (last_pos < text_size) {
- ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
-
- piece->type = TEXT_PIECE_TYPE_TEXT;
- piece->start_pos = last_pos;
- piece->len = text_size - last_pos;
-
- List_Insert(plText, piece, plText->realCount);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Getting Status name
-// returns -1 for XStatus, 1 for Status
-//
-int GetStatusName(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority)
-{
- BOOL noAwayMsg = FALSE;
- BOOL noXstatus = FALSE;
- // Hide status text if Offline /// no offline
- uint16_t nStatus = pdnce->getStatus();
- if ((nStatus == ID_STATUS_OFFLINE || nStatus == 0) && g_CluiData.bRemoveAwayMessageForOffline) noAwayMsg = TRUE;
- if (nStatus == ID_STATUS_OFFLINE || nStatus == 0) noXstatus = TRUE;
- text[0] = '\0';
- // Get XStatusName
- if (!noAwayMsg && !noXstatus && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
- DBVARIANT dbv = { 0 };
- if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) {
- CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1);
- db_free(&dbv);
-
- if (text[0] != '\0')
- return -1;
- }
- }
-
- // Get Status name
- wchar_t *tmp = Clist_GetStatusModeDescription(nStatus, 0);
- if (tmp && *tmp) {
- mir_wstrncpy(text, tmp, text_size);
- return 1;
- }
-
- // Get XStatusName
- if (!noAwayMsg && !noXstatus && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
- DBVARIANT dbv = { 0 };
- if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) {
- CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1);
- db_free(&dbv);
-
- if (text[0] != '\0')
- return -1;
- }
- }
-
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Get Listening to information
-//
-void GetListeningTo(wchar_t *text, int text_size, ClcCacheEntry *pdnce)
-{
- *text = '\0';
-
- if (pdnce->m_iStatus == ID_STATUS_OFFLINE || pdnce->m_iStatus == 0)
- return;
-
- ptrW tszValue(db_get_wsa(pdnce->hContact, pdnce->szProto, "ListeningTo"));
- if (tszValue != nullptr)
- CopySkipUnprintableChars(text, tszValue, text_size - 1);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Getting Status message(Away message)
-// returns -1 for XStatus, 1 for Status
-//
-int GetStatusMessage(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority)
-{
- BOOL noAwayMsg = FALSE;
- uint16_t wStatus = pdnce->getStatus();
- *text = '\0';
-
- // Hide status text if Offline /// no offline
- if (wStatus == ID_STATUS_OFFLINE || wStatus == 0)
- noAwayMsg = TRUE;
-
- // Get XStatusMsg
- if (!noAwayMsg && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
- ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
- if (tszXStatusMsg != nullptr) {
- CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1);
- if (text[0] != '\0')
- return -1;
- }
- }
-
- // Get StatusMsg
- if (pdnce->hContact && text[0] == '\0') {
- if (noAwayMsg && ServiceExists(MS_LASTSEEN_GET)) {
- ptrW pwszLastSeen((LPWSTR)CallService(MS_LASTSEEN_GET, (WPARAM)pdnce->hContact));
- if (pwszLastSeen) {
- CMStringW wszLastSeen(FORMAT, L"%s: %s", TranslateT("Last seen"), pwszLastSeen);
- CopySkipUnprintableChars(text, (wchar_t*)wszLastSeen.c_str(), text_size - 1);
- if (text[0] != '\0')
- return 1;
- }
- }
-
- ptrW tszStatusMsg(g_plugin.getWStringA(pdnce->hContact, "StatusMsg"));
- if (tszStatusMsg != nullptr) {
- CopySkipUnprintableChars(text, tszStatusMsg, text_size - 1);
- if (text[0] != '\0')
- return 1;
- }
-
- }
-
- // Get XStatusMsg
- if (!noAwayMsg && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto && text[0] == '\0') {
- // Try to get XStatusMsg
- ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
- if (tszXStatusMsg != nullptr) {
- CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1);
- if (text[0] != '\0')
- return -1;
- }
- }
-
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Get the text for specified lines
-
-int Cache_GetLineText(ClcCacheEntry *pdnce, int type, LPTSTR text, int text_size, ClcLineInfo &line)
-{
- if (text == nullptr)
- return TEXT_EMPTY;
- text[0] = '\0';
-
- switch (type) {
- case TEXT_STATUS:
-LBL_Status:
- if (GetStatusName(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) {
- // Try to get XStatusMsg
- ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
- if (tszXStatusMsg != nullptr && tszXStatusMsg[0] != 0) {
- wchar_t *tmp = NEWWSTR_ALLOCA(text);
- mir_snwprintf(text, text_size, L"%s: %s", tmp, tszXStatusMsg.get());
- CopySkipUnprintableChars(text, text, text_size - 1);
- }
- }
- return TEXT_STATUS;
-
- case TEXT_NICKNAME:
- if (pdnce->hContact && pdnce->szProto) {
- ptrW tszNick(db_get_wsa(pdnce->hContact, pdnce->szProto, "Nick"));
- if (tszNick != nullptr) {
- mir_wstrncpy(text, tszNick, text_size);
- CopySkipUnprintableChars(text, text, text_size - 1);
- }
- }
- return TEXT_NICKNAME;
-
- case TEXT_STATUS_MESSAGE:
- if (GetStatusMessage(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) {
- // Try to get XStatusName
- ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName"));
- if (tszXStatusName != nullptr && tszXStatusName[0] != 0) {
- wchar_t *tmp = NEWWSTR_ALLOCA(text);
- mir_snwprintf(text, text_size, L"%s: %s", tszXStatusName.get(), tmp);
- CopySkipUnprintableChars(text, text, text_size - 1);
- }
- }
- else if (line.bUseNameAndMessageForXstatus && line.bXstatusHasPriority) {
- // Try to get XStatusName
- ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName"));
- if (tszXStatusName != nullptr && tszXStatusName[0] != 0) {
- mir_wstrncpy(text, tszXStatusName, text_size);
- CopySkipUnprintableChars(text, text, text_size - 1);
- }
- }
-
- if (text[0] == '\0') {
- if (line.bShowListeningIfNoAway) {
- GetListeningTo(text, text_size, pdnce);
- if (text[0] != '\0')
- return TEXT_LISTENING_TO;
- }
-
- if (line.bShowStatusIfNoAway) // re-request status if no away
- goto LBL_Status;
- }
- return TEXT_STATUS_MESSAGE;
-
- case TEXT_LISTENING_TO:
- GetListeningTo(text, text_size, pdnce);
- return TEXT_LISTENING_TO;
-
- case TEXT_TEXT:
- {
- ptrW tmp(variables_parsedup(line.text, pdnce->tszName, pdnce->hContact));
- mir_wstrncpy(text, tmp, text_size);
- CopySkipUnprintableChars(text, text, text_size - 1);
- }
- return TEXT_TEXT;
-
- case TEXT_CONTACT_TIME:
- if (pdnce->hTimeZone) {
- // Get pdnce time
- text[0] = 0;
- TimeZone_PrintDateTime(pdnce->hTimeZone, L"t", text, text_size, 0);
- }
- return TEXT_CONTACT_TIME;
- }
-
- return TEXT_EMPTY;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Get the text for First Line
-
-void Cache_GetFirstLineText(ClcData *dat, ClcContact *contact)
-{
- if (GetCurrentThreadId() != g_dwMainThreadID)
- return;
-
- ClcCacheEntry *pdnce = contact->pce;
- wchar_t *name = Clist_GetContactDisplayName(contact->hContact);
- if (dat->first_line_append_nick && !dat->bForceInDialog) {
- DBVARIANT dbv = { 0 };
- if (!db_get_ws(pdnce->hContact, pdnce->szProto, "Nick", &dbv)) {
- wchar_t nick[_countof(contact->szText)];
- wcsncpy_s(nick, dbv.pwszVal, _TRUNCATE);
- db_free(&dbv);
-
- // They are the same -> use the name to keep the case
- if (mir_wstrcmpi(name, nick) == 0)
- wcsncpy_s(contact->szText, name, _TRUNCATE);
- else // Append then
- mir_snwprintf(contact->szText, L"%s - %s", name, nick);
- }
- else wcsncpy_s(contact->szText, name, _TRUNCATE);
- }
- else wcsncpy_s(contact->szText, name, _TRUNCATE);
-
- if (!dat->bForceInDialog)
- contact->ssText.ReplaceSmileys(dat, pdnce, contact->szText, dat->first_line_draw_smileys);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Get the text for Second Line
-
-void Cache_GetNthLineText(ClcData *dat, ClcCacheEntry *pdnce, int n)
-{
- if (pdnce == nullptr)
- return;
-
- wchar_t Text[240 - EXTRA_ICON_COUNT]; Text[0] = 0;
- ClcLineInfo &line = (n == 2) ? g_plugin.secondLine : g_plugin.thirdLine;
- wchar_t* &szText = (n == 2) ? pdnce->szSecondLineText : pdnce->szThirdLineText;
-
- // in most cases replaceStrW does nothing
- if (!line.bActive) {
- replaceStrW(szText, nullptr);
- return;
- }
-
- int type = Cache_GetLineText(pdnce, line.iType, Text, _countof(Text), line);
- if (Text[0] == 0) {
- replaceStrW(szText, nullptr);
- return;
- }
-
- Text[_countof(Text) - 1] = 0; //to be sure that it is null terminated string
- replaceStrW(szText, Text);
-
- CSmileyString &ss = (n == 2) ? pdnce->ssSecondLine : pdnce->ssThirdLine;
- if (type == TEXT_LISTENING_TO && szText[0] != '\0')
- ss.AddListeningToIcon(dat, szText);
- else
- ss.ReplaceSmileys(dat, pdnce, szText, line.bDrawSmilies);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void RemoveTag(wchar_t *to, wchar_t *tag)
-{
- wchar_t *st = to;
- int len = (int)mir_wstrlen(tag);
- int lastsize = (int)mir_wstrlen(to) + 1;
- while (st = wcsstr(st, tag)) {
- lastsize -= len;
- memmove((void*)st, (void*)(st + len), (lastsize)*sizeof(wchar_t));
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Copy string with removing Escape chars from text and BBcodes
-
-static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size)
-{
- uint32_t i;
- BOOL keep = 0;
- wchar_t * cp = to;
- if (!to) return 0;
- if (!buf) {
- to[0] = '\0';
- return 0;
- }
-
- for (i = 0; i < size; i++) {
- if (buf[i] == 0) break;
- if (buf[i] > 0 && buf[i] < ' ') {
- *cp = ' ';
- if (!keep) cp++;
- keep = 1;
- }
- else {
- keep = 0;
- *cp = buf[i];
- cp++;
- }
- }
- *cp = 0;
-
- //remove bbcodes: [b] [i] [u] <b> <i> <u>
- RemoveTag(to, L"[b]"); RemoveTag(to, L"[/b]");
- RemoveTag(to, L"[u]"); RemoveTag(to, L"[/u]");
- RemoveTag(to, L"[i]"); RemoveTag(to, L"[/i]");
-
- RemoveTag(to, L"<b>"); RemoveTag(to, L"</b>");
- RemoveTag(to, L"<u>"); RemoveTag(to, L"</u>");
- RemoveTag(to, L"<i>"); RemoveTag(to, L"</i>");
-
- RemoveTag(to, L"[B]"); RemoveTag(to, L"[/b]");
- RemoveTag(to, L"[U]"); RemoveTag(to, L"[/u]");
- RemoveTag(to, L"[I]"); RemoveTag(to, L"[/i]");
-
- RemoveTag(to, L"<B>"); RemoveTag(to, L"</B>");
- RemoveTag(to, L"<U>"); RemoveTag(to, L"</U>");
- RemoveTag(to, L"<I>"); RemoveTag(to, L"</I>");
- return i;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// If ExecuteOnAllContactsFuncPtr returns FALSE, stop loop
-// Return TRUE if finished, FALSE if was stoped
-//
-static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param)
-{
- return ExecuteOnAllContactsOfGroup(&dat->list, func, param);
-}
-
-static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param)
-{
- if (!group)
- return TRUE;
-
- for (auto &it : group->cl) {
- if (it->type == CLCIT_CONTACT) {
- if (!func(it, FALSE, param))
- return FALSE;
-
- if (it->iSubAllocated > 0) {
- for (int i = 0; i < it->iSubAllocated; i++)
- if (!func(&it->subcontacts[i], TRUE, param))
- return FALSE;
- }
- }
- else if (it->type == CLCIT_GROUP)
- if (!ExecuteOnAllContactsOfGroup(it->group, func, param))
- return FALSE;
- }
-
- return TRUE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Avatar working routines
-//
-BOOL UpdateAllAvatarsProxy(ClcContact *contact, BOOL, void *param)
-{
- Cache_GetAvatar((ClcData *)param, contact);
- return TRUE;
-}
-
-void UpdateAllAvatars(ClcData *dat)
-{
- ExecuteOnAllContacts(dat, UpdateAllAvatarsProxy, dat);
-}
-
-BOOL ReduceAvatarPosition(ClcContact *contact, BOOL, void *param)
-{
- if (contact->avatar_pos >= *((int *)param))
- contact->avatar_pos--;
-
- return TRUE;
-}
-
-void Cache_ProceedAvatarInList(ClcData *dat, ClcContact *contact)
-{
- AVATARCACHEENTRY *ace = contact->avatar_data;
- int old_pos = contact->avatar_pos;
-
- if (ace == nullptr || ace->dwFlags == AVS_BITMAP_EXPIRED || ace->hbmPic == nullptr) {
- // Avatar was not ready or removed - need to remove it from cache
- if (old_pos >= 0) {
- ImageArray_RemoveImage(&dat->avatar_cache, old_pos);
-
- // Update all items
- ExecuteOnAllContacts(dat, ReduceAvatarPosition, (void *)&old_pos);
- contact->avatar_pos = AVATAR_POS_DONT_HAVE;
- return;
- }
- }
- else if (contact->avatar_data->hbmPic != nullptr) { // let's add it
- // Clipping width and height
- LONG width_clip = dat->avatars_maxwidth_size ? dat->avatars_maxwidth_size : dat->avatars_maxheight_size;
- LONG height_clip = dat->avatars_maxheight_size;
-
- if (height_clip * ace->bmWidth / ace->bmHeight <= width_clip)
- width_clip = height_clip * ace->bmWidth / ace->bmHeight;
- else
- height_clip = width_clip * ace->bmHeight / ace->bmWidth;
-
- if (wildcmpiw(contact->avatar_data->szFilename, L"*.gif")) {
- if (old_pos == AVATAR_POS_ANIMATED)
- AniAva_RemoveAvatar(contact->hContact);
-
- int res = AniAva_AddAvatar(contact->hContact, contact->avatar_data->szFilename, width_clip, height_clip);
- if (res) {
- contact->avatar_pos = AVATAR_POS_ANIMATED;
- contact->avatar_size.cy = HIWORD(res);
- contact->avatar_size.cx = LOWORD(res);
- return;
- }
- }
-
- // Create objs
- HDC hdc = CreateCompatibleDC(dat->avatar_cache.hdc);
-
- void *pt;
- HBITMAP hDrawBmp = ske_CreateDIB32Point(width_clip, height_clip, &pt);
- HBITMAP oldBmp = (HBITMAP)SelectObject(hdc, hDrawBmp);
-
- // need to draw avatar bitmap here
- DrawAvatarImageWithGDIp(hdc, 0, 0, width_clip, height_clip, ace->hbmPic, 0, 0, ace->bmWidth, ace->bmHeight, ace->dwFlags, 255);
- SelectObject(hdc, oldBmp);
- DeleteDC(hdc);
-
- // Add to list
- if (old_pos >= 0) {
- ImageArray_ChangeImage(&dat->avatar_cache, hDrawBmp, old_pos);
- contact->avatar_pos = old_pos;
- }
- else contact->avatar_pos = ImageArray_AddImage(&dat->avatar_cache, hDrawBmp, -1);
-
- if (old_pos == AVATAR_POS_ANIMATED && contact->avatar_pos != AVATAR_POS_ANIMATED)
- AniAva_RemoveAvatar(contact->hContact);
-
- DeleteObject(hDrawBmp);
- }
-}
-
-void Cache_GetAvatar(ClcData *dat, ClcContact *contact)
-{
- // workaround for avatar service
- if (g_CluiData.bSTATE != STATE_NORMAL) {
- contact->avatar_pos = AVATAR_POS_DONT_HAVE;
- contact->avatar_data = nullptr;
- return;
- }
-
- if (dat->avatars_show && !g_plugin.getByte(contact->hContact, "HideContactAvatar", 0)) {
- contact->avatar_data = (AVATARCACHEENTRY*)CallService(MS_AV_GETAVATARBITMAP, contact->hContact, 0);
- if (contact->avatar_data == nullptr || contact->avatar_data->dwFlags == AVS_BITMAP_EXPIRED)
- contact->avatar_data = nullptr;
-
- if (contact->avatar_data != nullptr)
- contact->avatar_data->t_lastAccess = (uint32_t)time(0);
- }
- else contact->avatar_data = nullptr;
-
- Cache_ProceedAvatarInList(dat, contact);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+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.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Created by Pescuma
+Modified by FYR
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module for working with lines text and avatars
+
+#include "stdafx.h"
+#include "modern_sync.h"
+
+typedef BOOL(*ExecuteOnAllContactsFuncPtr) (ClcContact *contact, BOOL subcontact, void *param);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Module static declarations
+
+static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size);
+
+static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param);
+static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get time zone for contact
+//
+void Cache_GetTimezone(ClcData *dat, MCONTACT hContact)
+{
+ ClcCacheEntry *pdnce = Clist_GetCacheEntry(hContact);
+ if (dat == nullptr && g_clistApi.hwndContactTree)
+ dat = (ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
+
+ if (dat && dat->hWnd == g_clistApi.hwndContactTree) {
+ uint32_t flags = dat->contact_time_show_only_if_different ? TZF_DIFONLY : 0;
+ pdnce->hTimeZone = TimeZone_CreateByContact(hContact, nullptr, flags);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get all lines of text
+//
+void Cache_GetText(ClcData *dat, ClcContact *contact)
+{
+ Cache_GetFirstLineText(dat, contact);
+
+ if (!dat->bForceInDialog) {
+ if (g_plugin.secondLine.bActive)
+ Cache_GetNthLineText(dat, contact->pce, 2);
+ if (g_plugin.thirdLine.bActive)
+ Cache_GetNthLineText(dat, contact->pce, 3);
+ }
+}
+
+void CSmileyString::AddListeningToIcon(ClcData *dat, wchar_t *szText)
+{
+ iMaxSmileyHeight = 0;
+ DestroySmileyList();
+
+ if (szText == nullptr)
+ return;
+
+ int text_size = (int)mir_wstrlen(szText);
+
+ plText = List_Create(0, 1);
+
+ // Add Icon
+ {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+ piece->type = TEXT_PIECE_TYPE_SMILEY;
+ piece->len = 0;
+ piece->smiley = g_hListeningToIcon;
+ piece->smiley_width = 16;
+ piece->smiley_height = 16;
+
+ ICONINFO icon;
+ if (GetIconInfo(piece->smiley, &icon)) {
+ BITMAP bm;
+ if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) {
+ piece->smiley_width = bm.bmWidth;
+ piece->smiley_height = bm.bmHeight;
+ }
+
+ DeleteObject(icon.hbmMask);
+ DeleteObject(icon.hbmColor);
+ }
+
+ dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height);
+ iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight);
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+
+ // Add text
+ {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = 0;
+ piece->len = text_size;
+ List_Insert(plText, piece, plText->realCount);
+ }
+}
+
+void CSmileyString::_CopySmileyList(SortedList *plInput)
+{
+ if (!plInput || plInput->realCount == 0)
+ return;
+
+ plText = List_Create(0, 1);
+ for (int i = 0; i < plInput->realCount; i++) {
+ ClcContactTextPiece *pieceFrom = (ClcContactTextPiece *)plInput->items[i];
+ if (pieceFrom != nullptr) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+ *piece = *pieceFrom;
+ if (pieceFrom->type == TEXT_PIECE_TYPE_SMILEY)
+ piece->smiley = CopyIcon(pieceFrom->smiley);
+ List_Insert(plText, piece, plText->realCount);
+ }
+ }
+}
+
+void CSmileyString::DestroySmileyList()
+{
+ if (plText == nullptr)
+ return;
+
+ if (IsBadReadPtr(plText, sizeof(SortedList))) {
+ plText = nullptr;
+ return;
+ }
+
+ if (plText->realCount != 0) {
+ for (int i = 0; i < plText->realCount; i++) {
+ if (plText->items[i] != nullptr) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)plText->items[i];
+
+ if (!IsBadWritePtr(piece, sizeof(ClcContactTextPiece))) {
+ if (piece->type == TEXT_PIECE_TYPE_SMILEY && piece->smiley != g_hListeningToIcon)
+ DestroyIcon_protect(piece->smiley);
+ mir_free(piece);
+ }
+ }
+ }
+ List_Destroy(plText);
+ }
+ mir_free(plText);
+
+ plText = nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Parsing of text for smiley
+//
+void CSmileyString::ReplaceSmileys(ClcData *dat, ClcCacheEntry *pdnce, wchar_t *szText, BOOL replace_smileys)
+{
+ int last_pos = 0;
+ iMaxSmileyHeight = 0;
+
+ DestroySmileyList();
+
+ if (!dat->text_replace_smileys || !replace_smileys || szText == nullptr)
+ return;
+
+ int text_size = (int)mir_wstrlen(szText);
+
+ // Call service for the first time to see if needs to be used...
+ SMADD_BATCHPARSE2 sp = {};
+ sp.cbSize = sizeof(sp);
+ sp.hContact = pdnce->hContact;
+
+ if (dat->text_use_protocol_smileys) {
+ sp.Protocolname = pdnce->szProto;
+
+ if (db_get_b(0, "CLC", "Meta", SETTING_USEMETAICON_DEFAULT) != 1 && pdnce->szProto != nullptr && mir_strcmp(pdnce->szProto, META_PROTO) == 0) {
+ MCONTACT hContact = db_mc_getMostOnline(pdnce->hContact);
+ if (hContact != 0)
+ sp.Protocolname = Proto_GetBaseAccountName(hContact);
+ }
+ }
+ else sp.Protocolname = "clist";
+
+ sp.str = szText;
+ sp.flag = SAFL_TCHAR;
+
+ SMADD_BATCHPARSERES *spr = (SMADD_BATCHPARSERES*)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&sp);
+
+ // Did not find a simley
+ if (spr == nullptr || (INT_PTR)spr == CALLSERVICE_NOTFOUND)
+ return;
+
+ // Lets add smileys
+ plText = List_Create(0, 1);
+
+ for (unsigned i = 0; i < sp.numSmileys; ++i) {
+ if (spr[i].hIcon != nullptr) { // For deffective smileypacks
+ // Add text
+ if (spr[i].startChar - last_pos > 0) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = last_pos;//sp.str - text;
+ piece->len = spr[i].startChar - last_pos;
+ List_Insert(plText, piece, plText->realCount);
+ }
+
+ // Add smiley
+ {
+ BITMAP bm;
+ ICONINFO icon;
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_SMILEY;
+ piece->len = spr[i].size;
+ piece->smiley = spr[i].hIcon;
+
+ piece->smiley_width = 16;
+ piece->smiley_height = 16;
+ if (GetIconInfo(piece->smiley, &icon)) {
+ if (GetObject(icon.hbmColor, sizeof(BITMAP), &bm)) {
+ piece->smiley_width = bm.bmWidth;
+ piece->smiley_height = bm.bmHeight;
+ }
+
+ DeleteObject(icon.hbmMask);
+ DeleteObject(icon.hbmColor);
+ }
+
+ dat->text_smiley_height = max(piece->smiley_height, dat->text_smiley_height);
+ iMaxSmileyHeight = max(piece->smiley_height, iMaxSmileyHeight);
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+ }
+ // Get next
+ last_pos = spr[i].startChar + spr[i].size;
+ }
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)spr);
+
+ // Add rest of text
+ if (last_pos < text_size) {
+ ClcContactTextPiece *piece = (ClcContactTextPiece *)mir_alloc(sizeof(ClcContactTextPiece));
+
+ piece->type = TEXT_PIECE_TYPE_TEXT;
+ piece->start_pos = last_pos;
+ piece->len = text_size - last_pos;
+
+ List_Insert(plText, piece, plText->realCount);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Getting Status name
+// returns -1 for XStatus, 1 for Status
+//
+int GetStatusName(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority)
+{
+ BOOL noAwayMsg = FALSE;
+ BOOL noXstatus = FALSE;
+ // Hide status text if Offline /// no offline
+ uint16_t nStatus = pdnce->getStatus();
+ if ((nStatus == ID_STATUS_OFFLINE || nStatus == 0) && g_CluiData.bRemoveAwayMessageForOffline) noAwayMsg = TRUE;
+ if (nStatus == ID_STATUS_OFFLINE || nStatus == 0) noXstatus = TRUE;
+ text[0] = '\0';
+ // Get XStatusName
+ if (!noAwayMsg && !noXstatus && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) {
+ CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1);
+ db_free(&dbv);
+
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ // Get Status name
+ wchar_t *tmp = Clist_GetStatusModeDescription(nStatus, 0);
+ if (tmp && *tmp) {
+ mir_wstrncpy(text, tmp, text_size);
+ return 1;
+ }
+
+ // Get XStatusName
+ if (!noAwayMsg && !noXstatus && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(pdnce->hContact, pdnce->szProto, "XStatusName", &dbv)) {
+ CopySkipUnprintableChars(text, dbv.pwszVal, text_size - 1);
+ db_free(&dbv);
+
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get Listening to information
+//
+void GetListeningTo(wchar_t *text, int text_size, ClcCacheEntry *pdnce)
+{
+ *text = '\0';
+
+ if (pdnce->m_iStatus == ID_STATUS_OFFLINE || pdnce->m_iStatus == 0)
+ return;
+
+ ptrW tszValue(db_get_wsa(pdnce->hContact, pdnce->szProto, "ListeningTo"));
+ if (tszValue != nullptr)
+ CopySkipUnprintableChars(text, tszValue, text_size - 1);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Getting Status message(Away message)
+// returns -1 for XStatus, 1 for Status
+//
+int GetStatusMessage(wchar_t *text, int text_size, ClcCacheEntry *pdnce, BOOL bXstatusHasPriority)
+{
+ BOOL noAwayMsg = FALSE;
+ uint16_t wStatus = pdnce->getStatus();
+ *text = '\0';
+
+ // Hide status text if Offline /// no offline
+ if (wStatus == ID_STATUS_OFFLINE || wStatus == 0)
+ noAwayMsg = TRUE;
+
+ // Get XStatusMsg
+ if (!noAwayMsg && bXstatusHasPriority && pdnce->hContact && pdnce->szProto) {
+ ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
+ if (tszXStatusMsg != nullptr) {
+ CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1);
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ // Get StatusMsg
+ if (pdnce->hContact && text[0] == '\0') {
+ if (noAwayMsg && ServiceExists(MS_LASTSEEN_GET)) {
+ ptrW pwszLastSeen((LPWSTR)CallService(MS_LASTSEEN_GET, (WPARAM)pdnce->hContact));
+ if (pwszLastSeen) {
+ CMStringW wszLastSeen(FORMAT, L"%s: %s", TranslateT("Last seen"), pwszLastSeen);
+ CopySkipUnprintableChars(text, (wchar_t*)wszLastSeen.c_str(), text_size - 1);
+ if (text[0] != '\0')
+ return 1;
+ }
+ }
+
+ ptrW tszStatusMsg(g_plugin.getWStringA(pdnce->hContact, "StatusMsg"));
+ if (tszStatusMsg != nullptr) {
+ CopySkipUnprintableChars(text, tszStatusMsg, text_size - 1);
+ if (text[0] != '\0')
+ return 1;
+ }
+
+ }
+
+ // Get XStatusMsg
+ if (!noAwayMsg && !bXstatusHasPriority && pdnce->hContact && pdnce->szProto && text[0] == '\0') {
+ // Try to get XStatusMsg
+ ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
+ if (tszXStatusMsg != nullptr) {
+ CopySkipUnprintableChars(text, tszXStatusMsg, text_size - 1);
+ if (text[0] != '\0')
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the text for specified lines
+
+int Cache_GetLineText(ClcCacheEntry *pdnce, int type, LPTSTR text, int text_size, ClcLineInfo &line)
+{
+ if (text == nullptr)
+ return TEXT_EMPTY;
+ text[0] = '\0';
+
+ switch (type) {
+ case TEXT_STATUS:
+LBL_Status:
+ if (GetStatusName(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) {
+ // Try to get XStatusMsg
+ ptrW tszXStatusMsg(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusMsg"));
+ if (tszXStatusMsg != nullptr && tszXStatusMsg[0] != 0) {
+ wchar_t *tmp = NEWWSTR_ALLOCA(text);
+ mir_snwprintf(text, text_size, L"%s: %s", tmp, tszXStatusMsg.get());
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+ return TEXT_STATUS;
+
+ case TEXT_NICKNAME:
+ if (pdnce->hContact && pdnce->szProto) {
+ ptrW tszNick(db_get_wsa(pdnce->hContact, pdnce->szProto, "Nick"));
+ if (tszNick != nullptr) {
+ mir_wstrncpy(text, tszNick, text_size);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+ return TEXT_NICKNAME;
+
+ case TEXT_STATUS_MESSAGE:
+ if (GetStatusMessage(text, text_size, pdnce, line.bXstatusHasPriority) == -1 && line.bUseNameAndMessageForXstatus) {
+ // Try to get XStatusName
+ ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName"));
+ if (tszXStatusName != nullptr && tszXStatusName[0] != 0) {
+ wchar_t *tmp = NEWWSTR_ALLOCA(text);
+ mir_snwprintf(text, text_size, L"%s: %s", tszXStatusName.get(), tmp);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+ else if (line.bUseNameAndMessageForXstatus && line.bXstatusHasPriority) {
+ // Try to get XStatusName
+ ptrW tszXStatusName(db_get_wsa(pdnce->hContact, pdnce->szProto, "XStatusName"));
+ if (tszXStatusName != nullptr && tszXStatusName[0] != 0) {
+ mir_wstrncpy(text, tszXStatusName, text_size);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ }
+
+ if (text[0] == '\0') {
+ if (line.bShowListeningIfNoAway) {
+ GetListeningTo(text, text_size, pdnce);
+ if (text[0] != '\0')
+ return TEXT_LISTENING_TO;
+ }
+
+ if (line.bShowStatusIfNoAway) // re-request status if no away
+ goto LBL_Status;
+ }
+ return TEXT_STATUS_MESSAGE;
+
+ case TEXT_LISTENING_TO:
+ GetListeningTo(text, text_size, pdnce);
+ return TEXT_LISTENING_TO;
+
+ case TEXT_TEXT:
+ {
+ ptrW tmp(variables_parsedup(line.text, pdnce->tszName, pdnce->hContact));
+ mir_wstrncpy(text, tmp, text_size);
+ CopySkipUnprintableChars(text, text, text_size - 1);
+ }
+ return TEXT_TEXT;
+
+ case TEXT_CONTACT_TIME:
+ if (pdnce->hTimeZone) {
+ // Get pdnce time
+ text[0] = 0;
+ TimeZone_PrintDateTime(pdnce->hTimeZone, L"t", text, text_size, 0);
+ }
+ return TEXT_CONTACT_TIME;
+ }
+
+ return TEXT_EMPTY;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the text for First Line
+
+void Cache_GetFirstLineText(ClcData *dat, ClcContact *contact)
+{
+ if (GetCurrentThreadId() != g_dwMainThreadID)
+ return;
+
+ ClcCacheEntry *pdnce = contact->pce;
+ wchar_t *name = Clist_GetContactDisplayName(contact->hContact);
+ if (dat->first_line_append_nick && !dat->bForceInDialog) {
+ DBVARIANT dbv = { 0 };
+ if (!db_get_ws(pdnce->hContact, pdnce->szProto, "Nick", &dbv)) {
+ wchar_t nick[_countof(contact->szText)];
+ wcsncpy_s(nick, dbv.pwszVal, _TRUNCATE);
+ db_free(&dbv);
+
+ // They are the same -> use the name to keep the case
+ if (mir_wstrcmpi(name, nick) == 0)
+ wcsncpy_s(contact->szText, name, _TRUNCATE);
+ else // Append then
+ mir_snwprintf(contact->szText, L"%s - %s", name, nick);
+ }
+ else wcsncpy_s(contact->szText, name, _TRUNCATE);
+ }
+ else wcsncpy_s(contact->szText, name, _TRUNCATE);
+
+ if (!dat->bForceInDialog)
+ contact->ssText.ReplaceSmileys(dat, pdnce, contact->szText, dat->first_line_draw_smileys);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Get the text for Second Line
+
+void Cache_GetNthLineText(ClcData *dat, ClcCacheEntry *pdnce, int n)
+{
+ if (pdnce == nullptr)
+ return;
+
+ wchar_t Text[240 - EXTRA_ICON_COUNT]; Text[0] = 0;
+ ClcLineInfo &line = (n == 2) ? g_plugin.secondLine : g_plugin.thirdLine;
+ wchar_t* &szText = (n == 2) ? pdnce->szSecondLineText : pdnce->szThirdLineText;
+
+ // in most cases replaceStrW does nothing
+ if (!line.bActive) {
+ replaceStrW(szText, nullptr);
+ return;
+ }
+
+ int type = Cache_GetLineText(pdnce, line.iType, Text, _countof(Text), line);
+ if (Text[0] == 0) {
+ replaceStrW(szText, nullptr);
+ return;
+ }
+
+ Text[_countof(Text) - 1] = 0; //to be sure that it is null terminated string
+ replaceStrW(szText, Text);
+
+ CSmileyString &ss = (n == 2) ? pdnce->ssSecondLine : pdnce->ssThirdLine;
+ if (type == TEXT_LISTENING_TO && szText[0] != '\0')
+ ss.AddListeningToIcon(dat, szText);
+ else
+ ss.ReplaceSmileys(dat, pdnce, szText, line.bDrawSmilies);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void RemoveTag(wchar_t *to, wchar_t *tag)
+{
+ wchar_t *st = to;
+ int len = (int)mir_wstrlen(tag);
+ int lastsize = (int)mir_wstrlen(to) + 1;
+ while (st = wcsstr(st, tag)) {
+ lastsize -= len;
+ memmove((void*)st, (void*)(st + len), (lastsize)*sizeof(wchar_t));
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Copy string with removing Escape chars from text and BBcodes
+
+static int CopySkipUnprintableChars(wchar_t *to, wchar_t * buf, uint32_t size)
+{
+ uint32_t i;
+ BOOL keep = 0;
+ wchar_t * cp = to;
+ if (!to) return 0;
+ if (!buf) {
+ to[0] = '\0';
+ return 0;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (buf[i] == 0) break;
+ if (buf[i] > 0 && buf[i] < ' ') {
+ *cp = ' ';
+ if (!keep) cp++;
+ keep = 1;
+ }
+ else {
+ keep = 0;
+ *cp = buf[i];
+ cp++;
+ }
+ }
+ *cp = 0;
+
+ //remove bbcodes: [b] [i] [u] <b> <i> <u>
+ RemoveTag(to, L"[b]"); RemoveTag(to, L"[/b]");
+ RemoveTag(to, L"[u]"); RemoveTag(to, L"[/u]");
+ RemoveTag(to, L"[i]"); RemoveTag(to, L"[/i]");
+
+ RemoveTag(to, L"<b>"); RemoveTag(to, L"</b>");
+ RemoveTag(to, L"<u>"); RemoveTag(to, L"</u>");
+ RemoveTag(to, L"<i>"); RemoveTag(to, L"</i>");
+
+ RemoveTag(to, L"[B]"); RemoveTag(to, L"[/b]");
+ RemoveTag(to, L"[U]"); RemoveTag(to, L"[/u]");
+ RemoveTag(to, L"[I]"); RemoveTag(to, L"[/i]");
+
+ RemoveTag(to, L"<B>"); RemoveTag(to, L"</B>");
+ RemoveTag(to, L"<U>"); RemoveTag(to, L"</U>");
+ RemoveTag(to, L"<I>"); RemoveTag(to, L"</I>");
+ return i;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// If ExecuteOnAllContactsFuncPtr returns FALSE, stop loop
+// Return TRUE if finished, FALSE if was stoped
+//
+static BOOL ExecuteOnAllContacts(ClcData *dat, ExecuteOnAllContactsFuncPtr func, void *param)
+{
+ return ExecuteOnAllContactsOfGroup(&dat->list, func, param);
+}
+
+static BOOL ExecuteOnAllContactsOfGroup(ClcGroup *group, ExecuteOnAllContactsFuncPtr func, void *param)
+{
+ if (!group)
+ return TRUE;
+
+ for (auto &it : group->cl) {
+ if (it->type == CLCIT_CONTACT) {
+ if (!func(it, FALSE, param))
+ return FALSE;
+
+ if (it->iSubAllocated > 0) {
+ for (int i = 0; i < it->iSubAllocated; i++)
+ if (!func(&it->subcontacts[i], TRUE, param))
+ return FALSE;
+ }
+ }
+ else if (it->type == CLCIT_GROUP)
+ if (!ExecuteOnAllContactsOfGroup(it->group, func, param))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Avatar working routines
+//
+BOOL UpdateAllAvatarsProxy(ClcContact *contact, BOOL, void *param)
+{
+ Cache_GetAvatar((ClcData *)param, contact);
+ return TRUE;
+}
+
+void UpdateAllAvatars(ClcData *dat)
+{
+ ExecuteOnAllContacts(dat, UpdateAllAvatarsProxy, dat);
+}
+
+BOOL ReduceAvatarPosition(ClcContact *contact, BOOL, void *param)
+{
+ if (contact->avatar_pos >= *((int *)param))
+ contact->avatar_pos--;
+
+ return TRUE;
+}
+
+void Cache_ProceedAvatarInList(ClcData *dat, ClcContact *contact)
+{
+ AVATARCACHEENTRY *ace = contact->avatar_data;
+ int old_pos = contact->avatar_pos;
+
+ if (ace == nullptr || ace->dwFlags == AVS_BITMAP_EXPIRED || ace->hbmPic == nullptr) {
+ // Avatar was not ready or removed - need to remove it from cache
+ if (old_pos >= 0) {
+ ImageArray_RemoveImage(&dat->avatar_cache, old_pos);
+
+ // Update all items
+ ExecuteOnAllContacts(dat, ReduceAvatarPosition, (void *)&old_pos);
+ contact->avatar_pos = AVATAR_POS_DONT_HAVE;
+ return;
+ }
+ }
+ else if (contact->avatar_data->hbmPic != nullptr) { // let's add it
+ // Clipping width and height
+ LONG width_clip = dat->avatars_maxwidth_size ? dat->avatars_maxwidth_size : dat->avatars_maxheight_size;
+ LONG height_clip = dat->avatars_maxheight_size;
+
+ if (height_clip * ace->bmWidth / ace->bmHeight <= width_clip)
+ width_clip = height_clip * ace->bmWidth / ace->bmHeight;
+ else
+ height_clip = width_clip * ace->bmHeight / ace->bmWidth;
+
+ if (wildcmpiw(contact->avatar_data->szFilename, L"*.gif")) {
+ if (old_pos == AVATAR_POS_ANIMATED)
+ AniAva_RemoveAvatar(contact->hContact);
+
+ int res = AniAva_AddAvatar(contact->hContact, contact->avatar_data->szFilename, width_clip, height_clip);
+ if (res) {
+ contact->avatar_pos = AVATAR_POS_ANIMATED;
+ contact->avatar_size.cy = HIWORD(res);
+ contact->avatar_size.cx = LOWORD(res);
+ return;
+ }
+ }
+
+ // Create objs
+ HDC hdc = CreateCompatibleDC(dat->avatar_cache.hdc);
+
+ void *pt;
+ HBITMAP hDrawBmp = ske_CreateDIB32Point(width_clip, height_clip, &pt);
+ HBITMAP oldBmp = (HBITMAP)SelectObject(hdc, hDrawBmp);
+
+ // need to draw avatar bitmap here
+ DrawAvatarImageWithGDIp(hdc, 0, 0, width_clip, height_clip, ace->hbmPic, 0, 0, ace->bmWidth, ace->bmHeight, ace->dwFlags, 255);
+ SelectObject(hdc, oldBmp);
+ DeleteDC(hdc);
+
+ // Add to list
+ if (old_pos >= 0) {
+ ImageArray_ChangeImage(&dat->avatar_cache, hDrawBmp, old_pos);
+ contact->avatar_pos = old_pos;
+ }
+ else contact->avatar_pos = ImageArray_AddImage(&dat->avatar_cache, hDrawBmp, -1);
+
+ if (old_pos == AVATAR_POS_ANIMATED && contact->avatar_pos != AVATAR_POS_ANIMATED)
+ AniAva_RemoveAvatar(contact->hContact);
+
+ DeleteObject(hDrawBmp);
+ }
+}
+
+void Cache_GetAvatar(ClcData *dat, ClcContact *contact)
+{
+ // workaround for avatar service
+ if (g_CluiData.bSTATE != STATE_NORMAL) {
+ contact->avatar_pos = AVATAR_POS_DONT_HAVE;
+ contact->avatar_data = nullptr;
+ return;
+ }
+
+ if (dat->avatars_show && !g_plugin.getByte(contact->hContact, "HideContactAvatar", 0)) {
+ contact->avatar_data = (AVATARCACHEENTRY*)CallService(MS_AV_GETAVATARBITMAP, contact->hContact, 0);
+ if (contact->avatar_data == nullptr || contact->avatar_data->dwFlags == AVS_BITMAP_EXPIRED)
+ contact->avatar_data = nullptr;
+
+ if (contact->avatar_data != nullptr)
+ contact->avatar_data->t_lastAccess = (uint32_t)time(0);
+ }
+ else contact->avatar_data = nullptr;
+
+ Cache_ProceedAvatarInList(dat, contact);
+}
diff --git a/plugins/Clist_modern/src/modern_clc.cpp b/plugins/Clist_modern/src/modern_clc.cpp
index e549d98f88..d89b70b541 100644
--- a/plugins/Clist_modern/src/modern_clc.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clc.h b/plugins/Clist_modern/src/modern_clc.h
index 1d0d2328e4..c0956ee8cd 100644
--- a/plugins/Clist_modern/src/modern_clc.h
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcidents.cpp b/plugins/Clist_modern/src/modern_clcidents.cpp
index 36ac3c3291..29be4b6442 100644
--- a/plugins/Clist_modern/src/modern_clcidents.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcitems.cpp b/plugins/Clist_modern/src/modern_clcitems.cpp
index 61b6d6eca5..a2472b99d8 100644
--- a/plugins/Clist_modern/src/modern_clcitems.cpp
+++ b/plugins/Clist_modern/src/modern_clcitems.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/plugins/Clist_modern/src/modern_clcmsgs.cpp b/plugins/Clist_modern/src/modern_clcmsgs.cpp
index 7c64c944ff..663872a25c 100644
--- a/plugins/Clist_modern/src/modern_clcmsgs.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcopts.cpp b/plugins/Clist_modern/src/modern_clcopts.cpp
index d5d25caf10..52e16a6d53 100644
--- a/plugins/Clist_modern/src/modern_clcopts.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcpaint.cpp b/plugins/Clist_modern/src/modern_clcpaint.cpp
index 1bd6370e1d..aeb4dd1ade 100644
--- a/plugins/Clist_modern/src/modern_clcpaint.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clcutils.cpp b/plugins/Clist_modern/src/modern_clcutils.cpp
index 1fa39e4c78..29e438a846 100644
--- a/plugins/Clist_modern/src/modern_clcutils.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clist.h b/plugins/Clist_modern/src/modern_clist.h
index 0aa53268d4..79d9ca48cf 100644
--- a/plugins/Clist_modern/src/modern_clist.h
+++ b/plugins/Clist_modern/src/modern_clist.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistevents.cpp b/plugins/Clist_modern/src/modern_clistevents.cpp
index 264f15f57e..e918bcee65 100644
--- a/plugins/Clist_modern/src/modern_clistevents.cpp
+++ b/plugins/Clist_modern/src/modern_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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistmenus.cpp b/plugins/Clist_modern/src/modern_clistmenus.cpp
index caae8ff8d1..802cad6cd7 100644
--- a/plugins/Clist_modern/src/modern_clistmenus.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistmod.cpp b/plugins/Clist_modern/src/modern_clistmod.cpp
index 0f06f872d0..08373b20d4 100644
--- a/plugins/Clist_modern/src/modern_clistmod.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistopts.cpp b/plugins/Clist_modern/src/modern_clistopts.cpp
index cef88490dd..4862068c16 100644
--- a/plugins/Clist_modern/src/modern_clistopts.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clistsettings.cpp b/plugins/Clist_modern/src/modern_clistsettings.cpp
index ac35bad94a..c786522386 100644
--- a/plugins/Clist_modern/src/modern_clistsettings.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clisttray.cpp b/plugins/Clist_modern/src/modern_clisttray.cpp
index fde9a29921..c79aff2f00 100644
--- a/plugins/Clist_modern/src/modern_clisttray.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clui.cpp b/plugins/Clist_modern/src/modern_clui.cpp
index 7406276759..8eecc76542 100644
--- a/plugins/Clist_modern/src/modern_clui.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_clui.h b/plugins/Clist_modern/src/modern_clui.h
index c27ea5bb95..c6267ccabc 100644
--- a/plugins/Clist_modern/src/modern_clui.h
+++ b/plugins/Clist_modern/src/modern_clui.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_cluiservices.cpp b/plugins/Clist_modern/src/modern_cluiservices.cpp
index 3aa536ed2b..ba1011b445 100644
--- a/plugins/Clist_modern/src/modern_cluiservices.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_contact.cpp b/plugins/Clist_modern/src/modern_contact.cpp
index c39b2504aa..4a8420752a 100644
--- a/plugins/Clist_modern/src/modern_contact.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_defsettings.h b/plugins/Clist_modern/src/modern_defsettings.h
index 966f33e47c..04bfb6f73b 100644
--- a/plugins/Clist_modern/src/modern_defsettings.h
+++ b/plugins/Clist_modern/src/modern_defsettings.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-08 Miranda ICQ/IM project,
Copyright 2007 Artem Shpynov
diff --git a/plugins/Clist_modern/src/modern_docking.cpp b/plugins/Clist_modern/src/modern_docking.cpp
index 5820132744..ee9a5d9807 100644
--- a/plugins/Clist_modern/src/modern_docking.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_global.cpp b/plugins/Clist_modern/src/modern_global.cpp
index fc24f84c09..d5d3c5d765 100644
--- a/plugins/Clist_modern/src/modern_global.cpp
+++ b/plugins/Clist_modern/src/modern_global.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/plugins/Clist_modern/src/modern_image_array.cpp b/plugins/Clist_modern/src/modern_image_array.cpp
index de65d5b9fa..230cbd119a 100644
--- a/plugins/Clist_modern/src/modern_image_array.cpp
+++ b/plugins/Clist_modern/src/modern_image_array.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/plugins/Clist_modern/src/modern_image_array.h b/plugins/Clist_modern/src/modern_image_array.h
index c817883b5a..369535e687 100644
--- a/plugins/Clist_modern/src/modern_image_array.h
+++ b/plugins/Clist_modern/src/modern_image_array.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_keyboard.cpp b/plugins/Clist_modern/src/modern_keyboard.cpp
index d1c956982d..075b8b9cab 100644
--- a/plugins/Clist_modern/src/modern_keyboard.cpp
+++ b/plugins/Clist_modern/src/modern_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_rowheight_funcs.cpp b/plugins/Clist_modern/src/modern_rowheight_funcs.cpp
index c35cb7fcf7..bd54fbd582 100644
--- a/plugins/Clist_modern/src/modern_rowheight_funcs.cpp
+++ b/plugins/Clist_modern/src/modern_rowheight_funcs.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/plugins/Clist_modern/src/modern_rowheight_funcs.h b/plugins/Clist_modern/src/modern_rowheight_funcs.h
index 8699035b3c..ba99a67cf8 100644
--- a/plugins/Clist_modern/src/modern_rowheight_funcs.h
+++ b/plugins/Clist_modern/src/modern_rowheight_funcs.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_skinbutton.cpp b/plugins/Clist_modern/src/modern_skinbutton.cpp
index aa517a5ef2..f04d68d3b7 100644
--- a/plugins/Clist_modern/src/modern_skinbutton.cpp
+++ b/plugins/Clist_modern/src/modern_skinbutton.cpp
@@ -1,716 +1,716 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 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.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-/*
-This file contains code related to new modern free positioned skinned buttons
-*/
-
-#include "stdafx.h"
-#include "modern_clcpaint.h"
-
-#define MODERNSKINBUTTONCLASS "MirandaModernSkinButtonClass"
-BOOL ModernSkinButtonModuleIsLoaded = FALSE;
-static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
-int ModernSkinButtonUnloadModule(WPARAM wParam, LPARAM lParam);
-HWND SetToolTip(HWND hwnd, wchar_t * tip);
-
-typedef struct _ModernSkinButtonCtrl
-{
- HWND hwnd;
- uint8_t down; // button state
- uint8_t focus; // has focus (1 or 0)
- uint8_t hover;
- uint8_t IsSwitcher;
- BOOL fCallOnPress;
- char * ID;
- char * CommandService;
- char * StateService;
- char * HandleService;
- char * ValueDBSection;
- char * ValueTypeDef;
- int Left, Top, Bottom, Right;
- HMENU hMenu;
- wchar_t * Hint;
-
-} ModernSkinButtonCtrl;
-typedef struct _HandleServiceParams
-{
- HWND hwnd;
- uint32_t msg;
- WPARAM wParam;
- LPARAM lParam;
- BOOL handled;
-} HandleServiceParams;
-
-static mir_cs csTips;
-static HWND hwndToolTips = nullptr;
-
-int ModernSkinButtonLoadModule()
-{
- WNDCLASSEX wc;
- memset(&wc, 0, sizeof(wc));
- wc.cbSize = sizeof(wc);
- wc.lpszClassName = _A2W(MODERNSKINBUTTONCLASS);
- wc.lpfnWndProc = ModernSkinButtonWndProc;
- wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wc.cbWndExtra = sizeof(ModernSkinButtonCtrl*);
- wc.hbrBackground = nullptr;
- wc.style = CS_GLOBALCLASS;
- RegisterClassEx(&wc);
- ModernSkinButtonModuleIsLoaded = TRUE;
- return 0;
-}
-
-int ModernSkinButtonUnloadModule(WPARAM, LPARAM)
-{
- return 0;
-}
-
-static int ModernSkinButtonPaintWorker(HWND hwnd, HDC whdc)
-{
- HDC hdc;
- RECT rc;
- ModernSkinButtonCtrl* bct = (ModernSkinButtonCtrl *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
- if (!bct) return 0;
- if (!IsWindowVisible(hwnd)) return 0;
- if (!whdc && !g_CluiData.fLayered) InvalidateRect(hwnd, nullptr, FALSE);
-
- if (whdc && g_CluiData.fLayered) hdc = whdc;
- else {
- //sdc = GetWindowDC(GetParent(hwnd));
- hdc = CreateCompatibleDC(nullptr);
- }
- GetClientRect(hwnd, &rc);
- HBITMAP bmp = ske_CreateDIB32(rc.right, rc.bottom);
- HBITMAP oldbmp = (HBITMAP)SelectObject(hdc, bmp);
- if (!g_CluiData.fLayered)
- ske_BltBackImage(bct->hwnd, hdc, nullptr);
- {
- MODERNMASK Request = {};
- // int res;
- //HBRUSH br = CreateSolidBrush(RGB(255,255,255));
- char * Value = nullptr;
- {
- if (bct->ValueDBSection && bct->ValueTypeDef) {
- char * key;
- char * section;
- uint32_t defval = 0;
- char buf[20];
- key = mir_strdup(bct->ValueDBSection);
- section = key;
- if (bct->ValueTypeDef[0] != 's')
- defval = (uint32_t)atol(bct->ValueTypeDef + 1);
- do {
- if (key[0] == '/') { key[0] = '\0'; key++; break; }
- key++;
- } while (key[0] != '\0');
- switch (bct->ValueTypeDef[0]) {
- case 's':
- Value = db_get_sa(0, section, key, bct->ValueTypeDef + 1);
- break;
- case 'd':
- defval = db_get_dw(0, section, key, defval);
- Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
- break;
- case 'w':
- defval = db_get_w(0, section, key, defval);
- Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
- break;
- case 'b':
- defval = db_get_b(0, section, key, defval);
- Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
- break;
- }
- mir_free(section);
- }
-
- }
- g_clcPainter.AddParam(&Request, mod_CalcHash("Module"), "MButton", 0);
- g_clcPainter.AddParam(&Request, mod_CalcHash("ID"), bct->ID, 0);
- g_clcPainter.AddParam(&Request, mod_CalcHash("Down"), bct->down ? "1" : "0", 0);
- g_clcPainter.AddParam(&Request, mod_CalcHash("Focused"), bct->focus ? "1" : "0", 0);
- g_clcPainter.AddParam(&Request, mod_CalcHash("Hovered"), bct->hover ? "1" : "0", 0);
- if (Value) {
- g_clcPainter.AddParam(&Request, mod_CalcHash("Value"), Value, 0);
- mir_free(Value);
- }
- SkinDrawGlyphMask(hdc, &rc, &rc, &Request);
- SkinSelector_DeleteMask(&Request);
- }
-
- if (!whdc && g_CluiData.fLayered) {
- RECT r;
- SetRect(&r, bct->Left, bct->Top, bct->Right, bct->Bottom);
- ske_DrawImageAt(hdc, &r);
- //CallingService to immeadeately update window with new image.
- }
- if (whdc && !g_CluiData.fLayered) {
- RECT r = { 0 };
- GetClientRect(bct->hwnd, &r);
- BitBlt(whdc, 0, 0, r.right, r.bottom, hdc, 0, 0, SRCCOPY);
- }
- SelectObject(hdc, oldbmp);
- DeleteObject(bmp);
- if (!whdc || !g_CluiData.fLayered) {
- SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
- DeleteDC(hdc);
- }
- // if (sdc)
- // ReleaseDC(GetParent(hwnd),sdc);
- return 0;
-}
-
-static int ModernSkinButtonToggleDBValue(char * ValueDBSection, char *ValueTypeDef)
-{
- if (ValueDBSection && ValueTypeDef) {
- char * key;
- char * section;
- char * val;
- char * val2;
- char * Value;
- long l1 = 0, l2 = 0, curval;
- // char buf[20];
- key = mir_strdup(ValueDBSection);
- section = key;
- do {
- if (key[0] == '/') { key[0] = '\0'; key++; break; }
- key++;
- } while (key[0] != '\0');
-
- val = mir_strdup(ValueTypeDef + 1);
- val2 = val;
- do {
- if (val2[0] == '/') { val2[0] = '\0'; val2++; break; }
- val2++;
- } while (val2[0] != '\0');
-
- if (ValueTypeDef[0] != 's') {
- l1 = (uint32_t)atol(val);
- l2 = (uint32_t)atol(val2);
- }
-
- switch (ValueTypeDef[0]) {
- case 's':
- Value = db_get_sa(0, section, key);
- if (!Value || (Value && !mir_strcmpi(Value, val2)))
- Value = mir_strdup(val);
- else
- Value = mir_strdup(val2);
- db_set_s(0, section, key, Value);
- mir_free(Value);
- break;
-
- case 'd':
- curval = db_get_dw(0, section, key, l2);
- curval = (curval == l2) ? l1 : l2;
- db_set_dw(0, section, key, (uint32_t)curval);
- break;
-
- case 'w':
- curval = db_get_w(0, section, key, l2);
- curval = (curval == l2) ? l1 : l2;
- db_set_w(0, section, key, (uint16_t)curval);
- break;
-
- case 'b':
- curval = db_get_b(0, section, key, l2);
- curval = (curval == l2) ? l1 : l2;
- db_set_b(0, section, key, (uint8_t)curval);
- break;
- }
- mir_free(section);
- mir_free(val);
- }
- return 0;
-}
-
-static char *_skipblank(char * str) //str will be modified;
-{
- char * endstr = str + mir_strlen(str);
- while ((*str == ' ' || *str == '\t') && *str != '\0') str++;
- while ((*endstr == ' ' || *endstr == '\t') && *endstr != '\0' && endstr < str) endstr--;
- if (*endstr != '\0') {
- endstr++;
- *endstr = '\0';
- }
- return str;
-}
-
-static int _CallServiceStrParams(IN char * toParce, OUT int *Return)
-{
- int paramCount = 0;
- int result = 0;
-
- char *pszService = mir_strdup(toParce);
- if (!pszService)
- return 0;
- if (mir_strlen(pszService) == 0) {
- mir_free(pszService);
- return 0;
- }
- char *param2 = strrchr(pszService, '%');
- if (param2) {
- paramCount++;
- *param2 = '\0'; param2++;
- _skipblank(param2);
- if (mir_strlen(param2) == 0)
- param2 = nullptr;
- }
- char *param1 = strrchr(pszService, '%');
- if (param1) {
- paramCount++;
- *param1 = '\0'; param1++;
- _skipblank(param1);
- if (mir_strlen(param1) == 0)
- param1 = nullptr;
- }
- if (param1 && *param1 == '\"') {
- param1++;
- *(param1 + mir_strlen(param1)) = '\0';
- }
- else if (param1) {
- param1 = (char*)atoi(param1);
- }
- if (param2 && *param2 == '\"') {
- param2++;
- *(param2 + mir_strlen(param2)) = '\0';
- }
- else if (param2)
- param2 = (char*)atoi(param2);
-
- if (paramCount == 1) {
- param1 = param2;
- param2 = nullptr;
- }
- if (!ServiceExists(pszService)) {
- result = 0;
- }
- else {
- result = 1;
- int ret = CallService(pszService, (WPARAM)param1, (WPARAM)param2);
- if (Return) *Return = ret;
- }
- mir_free(pszService);
- return result;
-}
-
-
-static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- ModernSkinButtonCtrl* bct = (msg != WM_NCCREATE) ? (ModernSkinButtonCtrl *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA) : nullptr;
- if (bct) {
- if (bct->HandleService && IsBadStringPtrA(bct->HandleService, 255))
- bct->HandleService = nullptr;
- else if (bct->HandleService && ServiceExists(bct->HandleService)) {
- HandleServiceParams MSG = {};
- MSG.hwnd = hwndDlg;
- MSG.msg = msg;
- MSG.wParam = wParam;
- MSG.lParam = lParam;
- int t = CallService(bct->HandleService, (WPARAM)&MSG, 0);
- if (MSG.handled) return t;
- }
- }
-
- switch (msg) {
- case WM_NCCREATE:
- SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW);
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- if (((CREATESTRUCT *)lParam)->lpszName) SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName);
- return TRUE;
-
- case WM_DESTROY:
- if (bct == nullptr)
- break;
-
- if (hwndToolTips) {
- mir_cslock lck(csTips);
- TOOLINFO ti = { 0 };
- ti.cbSize = sizeof(ti);
- ti.uFlags = TTF_IDISHWND;
- ti.hwnd = bct->hwnd;
- ti.uId = (UINT_PTR)bct->hwnd;
- if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
- SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
-
- if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
- DestroyWindow(hwndToolTips);
- hwndToolTips = nullptr;
- }
- }
- mir_free(bct->ID);
- mir_free(bct->CommandService);
- mir_free(bct->StateService);
- mir_free(bct->HandleService);
- mir_free(bct->Hint);
- mir_free(bct->ValueDBSection);
- mir_free(bct->ValueTypeDef);
- mir_free(bct);
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- break; // DONT! fall thru
-
- case WM_SETCURSOR:
- {
- HCURSOR hCurs1 = LoadCursor(nullptr, IDC_ARROW);
- if (hCurs1) SetCursor(hCurs1);
- if (bct) SetToolTip(hwndDlg, bct->Hint);
- }
- return 1;
-
- case WM_PRINT:
- if (IsWindowVisible(hwndDlg))
- ModernSkinButtonPaintWorker(hwndDlg, (HDC)wParam);
- break;
-
- case WM_PAINT:
- if (IsWindowVisible(hwndDlg) && !g_CluiData.fLayered) {
- PAINTSTRUCT ps = {};
- BeginPaint(hwndDlg, &ps);
- ModernSkinButtonPaintWorker(hwndDlg, (HDC)ps.hdc);
- EndPaint(hwndDlg, &ps);
- }
- return DefWindowProc(hwndDlg, msg, wParam, lParam);
-
- case WM_CAPTURECHANGED:
- if (bct) {
- bct->hover = 0;
- bct->down = 0;
- ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
- }
- break;
-
- case WM_MOUSEMOVE:
- if (bct) {
- if (!bct->hover) {
- SetCapture(bct->hwnd);
- bct->hover = 1;
- ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
- }
- else {
- POINT t = UNPACK_POINT(lParam);
- ClientToScreen(bct->hwnd, &t);
- if (WindowFromPoint(t) != bct->hwnd)
- ReleaseCapture();
- }
- }
- return 0;
-
- case WM_LBUTTONDOWN:
- if (bct) {
- bct->down = 1;
- SetForegroundWindow(GetParent(bct->hwnd));
- ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
- if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
- bct->CommandService = nullptr;
- if (bct->fCallOnPress) {
- if (bct->CommandService) {
- if (!_CallServiceStrParams(bct->CommandService, nullptr) && (bct->ValueDBSection && bct->ValueTypeDef))
- ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
- }
- bct->down = 0;
-
- ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
- }
- }
- return 0;
-
- case WM_LBUTTONUP:
- if (bct && bct->down) {
- ReleaseCapture();
- bct->hover = 0;
- bct->down = 0;
- ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
- if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
- bct->CommandService = nullptr;
- if (bct->CommandService)
- if (_CallServiceStrParams(bct->CommandService, nullptr)) {
- }
- else if (bct->ValueDBSection && bct->ValueTypeDef)
- ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
- }
- }
- return DefWindowProc(hwndDlg, msg, wParam, lParam);
-}
-
-HWND SetToolTip(HWND hwnd, wchar_t * tip)
-{
- TOOLINFO ti;
- if (!tip) return nullptr;
- mir_cslock lck(csTips);
- if (!hwndToolTips) {
- hwndToolTips = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr,
- WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
- CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT,
- hwnd, nullptr, g_hMirApp, nullptr);
-
- SetWindowPos(hwndToolTips, HWND_TOPMOST, 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
- }
-
- memset(&ti, 0, sizeof(ti));
- ti.cbSize = sizeof(ti);
- ti.uFlags = TTF_IDISHWND;
- ti.hwnd = hwnd;
- ti.uId = (UINT_PTR)hwnd;
- if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
- SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
- }
- ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
- ti.uId = (UINT_PTR)hwnd;
- ti.lpszText = (wchar_t*)tip;
- SendMessage(hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
-
- return hwndToolTips;
-}
-
-
-
-typedef struct _MButton
-{
- HWND hwnd;
- uint8_t ConstrainPositionFrom; //(BBRRTTLL) L = 0 - from left, L = 1 from right, L = 2 from center
- int OrL, OrR, OrT, OrB;
- int minW, minH;
- ModernSkinButtonCtrl * bct;
-
-} MButton;
-MButton * Buttons = nullptr;
-uint32_t ButtonsCount = 0;
-
-#define _center_h( rc ) (((rc)->right + (rc)->left ) >> 1)
-#define _center_v( rc ) (((rc)->bottom + (rc)->top ) >> 1)
-
-int ModernSkinButton_AddButton(HWND parent,
- char * ID,
- char * CommandService,
- char * StateDefService,
- char * HandeService,
- int Left,
- int Top,
- int Right,
- int Bottom,
- uint32_t sbFlags,
- wchar_t * Hint,
- char * DBkey,
- char * TypeDef,
- int MinWidth, int MinHeight)
-{
- // if (!parent) return 0;
- if (!ModernSkinButtonModuleIsLoaded) return 0;
- if (!Buttons)
- Buttons = (MButton*)mir_alloc(sizeof(MButton));
- else
- Buttons = (MButton*)mir_realloc(Buttons, sizeof(MButton)*(ButtonsCount + 1));
- {
- //HWND hwnd;
- RECT rc = { 0 };
- ModernSkinButtonCtrl* bct;
- int l, r, b, t;
- if (parent) GetClientRect(parent, &rc);
- l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Left) :
- (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Left) :
- (rc.left + Left);
-
- t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Top) :
- (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Top) :
- (rc.top + Top);
-
- r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Right) :
- (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Right) :
- (rc.left + Right);
-
- b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Bottom) :
- (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Bottom) :
- (rc.top + Bottom);
- bct = (ModernSkinButtonCtrl *)mir_alloc(sizeof(ModernSkinButtonCtrl));
- memset(bct, 0, sizeof(ModernSkinButtonCtrl));
- bct->Left = l;
- bct->Right = r;
- bct->Top = t;
- bct->Bottom = b;
- bct->fCallOnPress = (sbFlags & SBF_CALL_ON_PRESS) != 0;
- bct->HandleService = mir_strdup(HandeService);
- bct->CommandService = mir_strdup(CommandService);
- bct->StateService = mir_strdup(StateDefService);
- if (DBkey && *DBkey != '\0') bct->ValueDBSection = mir_strdup(DBkey); else bct->ValueDBSection = nullptr;
- if (TypeDef && *TypeDef != '\0') bct->ValueTypeDef = mir_strdup(TypeDef); else bct->ValueTypeDef = mir_strdup("sDefault");
- bct->ID = mir_strdup(ID);
- bct->Hint = mir_wstrdup(Hint);
- Buttons[ButtonsCount].bct = bct;
- Buttons[ButtonsCount].hwnd = nullptr;
- Buttons[ButtonsCount].OrL = Left;
- Buttons[ButtonsCount].OrT = Top;
- Buttons[ButtonsCount].OrR = Right;
- Buttons[ButtonsCount].OrB = Bottom;
- Buttons[ButtonsCount].ConstrainPositionFrom = (uint8_t)sbFlags;
- Buttons[ButtonsCount].minH = MinHeight;
- Buttons[ButtonsCount].minW = MinWidth;
- ButtonsCount++;
- // CLUI_ShowWindowMod(hwnd,SW_SHOW);
- }
- return 0;
-}
-
-
-
-static int ModernSkinButtonErase(int l, int t, int r, int b)
-{
- uint32_t i;
- if (!ModernSkinButtonModuleIsLoaded) return 0;
- if (!g_CluiData.fLayered) return 0;
- if (!g_pCachedWindow) return 0;
- if (!g_pCachedWindow->hImageDC || !g_pCachedWindow->hBackDC) return 0;
- if (!(l || r || t || b)) {
- for (i = 0; i < ButtonsCount; i++) {
- if (g_clistApi.hwndContactList && Buttons[i].hwnd != nullptr) {
- //TODO: Erase button
- BitBlt(g_pCachedWindow->hImageDC, Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right - Buttons[i].bct->Left, Buttons[i].bct->Bottom - Buttons[i].bct->Top,
- g_pCachedWindow->hBackDC, Buttons[i].bct->Left, Buttons[i].bct->Top, SRCCOPY);
- }
- }
- }
- else {
- BitBlt(g_pCachedWindow->hImageDC, l, t, r - l, b - t, g_pCachedWindow->hBackDC, l, t, SRCCOPY);
- }
- return 0;
-}
-
-static HWND ModernSkinButtonCreateWindow(ModernSkinButtonCtrl * bct, HWND parent)
-{
- HWND hwnd;
-
- if (bct == nullptr) return FALSE;
- {
- wchar_t *UnicodeID = mir_a2u(bct->ID);
- hwnd = CreateWindow(_A2W(MODERNSKINBUTTONCLASS), UnicodeID, WS_VISIBLE | WS_CHILD, bct->Left, bct->Top, bct->Right - bct->Left, bct->Bottom - bct->Top, parent, nullptr, g_plugin.getInst(), nullptr);
- mir_free(UnicodeID);
- }
-
- bct->hwnd = hwnd;
- bct->focus = 0;
- SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)bct);
- return hwnd;
-}
-
-int ModernSkinButtonRedrawAll()
-{
- if (!ModernSkinButtonModuleIsLoaded) return 0;
- g_mutex_bLockUpdating++;
- for (uint32_t i = 0; i < ButtonsCount; i++) {
- if (g_clistApi.hwndContactList && Buttons[i].hwnd == nullptr)
- Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, g_clistApi.hwndContactList);
- ModernSkinButtonPaintWorker(Buttons[i].hwnd, nullptr);
- }
- g_mutex_bLockUpdating--;
- return 0;
-}
-
-int ModernSkinButtonDeleteAll()
-{
- if (!ModernSkinButtonModuleIsLoaded)
- return 0;
-
- for (size_t i = 0; i < ButtonsCount; i++)
- if (Buttons[i].hwnd)
- DestroyWindow(Buttons[i].hwnd);
-
- mir_free_and_nil(Buttons);
- ButtonsCount = 0;
- return 0;
-}
-
-int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *pRect)
-{
- RECT rc, clr, rd;
- BOOL altDraw = FALSE;
- static SIZE oldWndSize = { 0 };
- if (!ModernSkinButtonModuleIsLoaded) return 0;
- GetWindowRect(parent, &rd);
- GetClientRect(parent, &clr);
- if (!pRect)
- GetWindowRect(parent, &rc);
- else
- rc = *pRect;
-
- if (g_CluiData.fLayered && (draw & SBRF_DO_ALT_DRAW)) {
- int sx, sy;
- sx = rd.right - rd.left;
- sy = rd.bottom - rd.top;
- if (sx != oldWndSize.cx || sy != oldWndSize.cy)
- altDraw = TRUE;//EraseButtons();
- oldWndSize.cx = sx;
- oldWndSize.cy = sy;
- }
-
- OffsetRect(&rc, -rc.left, -rc.top);
- rc.right = rc.left + (clr.right - clr.left);
- rc.bottom = rc.top + (clr.bottom - clr.top);
- for (uint32_t i = 0; i < ButtonsCount; i++) {
- int sbFlags = Buttons[i].ConstrainPositionFrom;
- if (parent && Buttons[i].hwnd == nullptr) {
- Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, parent);
- altDraw = FALSE;
- }
-
- int l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Buttons[i].OrL) :
- (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Buttons[i].OrL) :
- (rc.left + Buttons[i].OrL);
-
- int t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Buttons[i].OrT) :
- (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Buttons[i].OrT) :
- (rc.top + Buttons[i].OrT);
-
- int r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Buttons[i].OrR) :
- (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Buttons[i].OrR) :
- (rc.left + Buttons[i].OrR);
-
- int b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Buttons[i].OrB) :
- (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Buttons[i].OrB) :
- (rc.top + Buttons[i].OrB);
-
- SetWindowPos(Buttons[i].hwnd, HWND_TOP, l, t, r - l, b - t, 0);
- if (rc.right - rc.left < Buttons[i].minW || rc.bottom - rc.top < Buttons[i].minH)
- CLUI_ShowWindowMod(Buttons[i].hwnd, SW_HIDE);
- else
- CLUI_ShowWindowMod(Buttons[i].hwnd, SW_SHOW);
- if ((1 || altDraw) &&
- (Buttons[i].bct->Left != l ||
- Buttons[i].bct->Top != t ||
- Buttons[i].bct->Right != r ||
- Buttons[i].bct->Bottom != b)) {
- //Need to erase in old location
- ModernSkinButtonErase(Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right, Buttons[i].bct->Bottom);
- }
-
- Buttons[i].bct->Left = l;
- Buttons[i].bct->Top = t;
- Buttons[i].bct->Right = r;
- Buttons[i].bct->Bottom = b;
- }
-
- if (draw & SBRF_DO_REDRAW_ALL)
- ModernSkinButtonRedrawAll();
- 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-08 Miranda ICQ/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.
+*/
+
+/*
+This file contains code related to new modern free positioned skinned buttons
+*/
+
+#include "stdafx.h"
+#include "modern_clcpaint.h"
+
+#define MODERNSKINBUTTONCLASS "MirandaModernSkinButtonClass"
+BOOL ModernSkinButtonModuleIsLoaded = FALSE;
+static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+int ModernSkinButtonUnloadModule(WPARAM wParam, LPARAM lParam);
+HWND SetToolTip(HWND hwnd, wchar_t * tip);
+
+typedef struct _ModernSkinButtonCtrl
+{
+ HWND hwnd;
+ uint8_t down; // button state
+ uint8_t focus; // has focus (1 or 0)
+ uint8_t hover;
+ uint8_t IsSwitcher;
+ BOOL fCallOnPress;
+ char * ID;
+ char * CommandService;
+ char * StateService;
+ char * HandleService;
+ char * ValueDBSection;
+ char * ValueTypeDef;
+ int Left, Top, Bottom, Right;
+ HMENU hMenu;
+ wchar_t * Hint;
+
+} ModernSkinButtonCtrl;
+typedef struct _HandleServiceParams
+{
+ HWND hwnd;
+ uint32_t msg;
+ WPARAM wParam;
+ LPARAM lParam;
+ BOOL handled;
+} HandleServiceParams;
+
+static mir_cs csTips;
+static HWND hwndToolTips = nullptr;
+
+int ModernSkinButtonLoadModule()
+{
+ WNDCLASSEX wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.cbSize = sizeof(wc);
+ wc.lpszClassName = _A2W(MODERNSKINBUTTONCLASS);
+ wc.lpfnWndProc = ModernSkinButtonWndProc;
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wc.cbWndExtra = sizeof(ModernSkinButtonCtrl*);
+ wc.hbrBackground = nullptr;
+ wc.style = CS_GLOBALCLASS;
+ RegisterClassEx(&wc);
+ ModernSkinButtonModuleIsLoaded = TRUE;
+ return 0;
+}
+
+int ModernSkinButtonUnloadModule(WPARAM, LPARAM)
+{
+ return 0;
+}
+
+static int ModernSkinButtonPaintWorker(HWND hwnd, HDC whdc)
+{
+ HDC hdc;
+ RECT rc;
+ ModernSkinButtonCtrl* bct = (ModernSkinButtonCtrl *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (!bct) return 0;
+ if (!IsWindowVisible(hwnd)) return 0;
+ if (!whdc && !g_CluiData.fLayered) InvalidateRect(hwnd, nullptr, FALSE);
+
+ if (whdc && g_CluiData.fLayered) hdc = whdc;
+ else {
+ //sdc = GetWindowDC(GetParent(hwnd));
+ hdc = CreateCompatibleDC(nullptr);
+ }
+ GetClientRect(hwnd, &rc);
+ HBITMAP bmp = ske_CreateDIB32(rc.right, rc.bottom);
+ HBITMAP oldbmp = (HBITMAP)SelectObject(hdc, bmp);
+ if (!g_CluiData.fLayered)
+ ske_BltBackImage(bct->hwnd, hdc, nullptr);
+ {
+ MODERNMASK Request = {};
+ // int res;
+ //HBRUSH br = CreateSolidBrush(RGB(255,255,255));
+ char * Value = nullptr;
+ {
+ if (bct->ValueDBSection && bct->ValueTypeDef) {
+ char * key;
+ char * section;
+ uint32_t defval = 0;
+ char buf[20];
+ key = mir_strdup(bct->ValueDBSection);
+ section = key;
+ if (bct->ValueTypeDef[0] != 's')
+ defval = (uint32_t)atol(bct->ValueTypeDef + 1);
+ do {
+ if (key[0] == '/') { key[0] = '\0'; key++; break; }
+ key++;
+ } while (key[0] != '\0');
+ switch (bct->ValueTypeDef[0]) {
+ case 's':
+ Value = db_get_sa(0, section, key, bct->ValueTypeDef + 1);
+ break;
+ case 'd':
+ defval = db_get_dw(0, section, key, defval);
+ Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
+ break;
+ case 'w':
+ defval = db_get_w(0, section, key, defval);
+ Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
+ break;
+ case 'b':
+ defval = db_get_b(0, section, key, defval);
+ Value = mir_strdup(_ltoa(defval, buf, _countof(buf)));
+ break;
+ }
+ mir_free(section);
+ }
+
+ }
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Module"), "MButton", 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("ID"), bct->ID, 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Down"), bct->down ? "1" : "0", 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Focused"), bct->focus ? "1" : "0", 0);
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Hovered"), bct->hover ? "1" : "0", 0);
+ if (Value) {
+ g_clcPainter.AddParam(&Request, mod_CalcHash("Value"), Value, 0);
+ mir_free(Value);
+ }
+ SkinDrawGlyphMask(hdc, &rc, &rc, &Request);
+ SkinSelector_DeleteMask(&Request);
+ }
+
+ if (!whdc && g_CluiData.fLayered) {
+ RECT r;
+ SetRect(&r, bct->Left, bct->Top, bct->Right, bct->Bottom);
+ ske_DrawImageAt(hdc, &r);
+ //CallingService to immeadeately update window with new image.
+ }
+ if (whdc && !g_CluiData.fLayered) {
+ RECT r = { 0 };
+ GetClientRect(bct->hwnd, &r);
+ BitBlt(whdc, 0, 0, r.right, r.bottom, hdc, 0, 0, SRCCOPY);
+ }
+ SelectObject(hdc, oldbmp);
+ DeleteObject(bmp);
+ if (!whdc || !g_CluiData.fLayered) {
+ SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
+ DeleteDC(hdc);
+ }
+ // if (sdc)
+ // ReleaseDC(GetParent(hwnd),sdc);
+ return 0;
+}
+
+static int ModernSkinButtonToggleDBValue(char * ValueDBSection, char *ValueTypeDef)
+{
+ if (ValueDBSection && ValueTypeDef) {
+ char * key;
+ char * section;
+ char * val;
+ char * val2;
+ char * Value;
+ long l1 = 0, l2 = 0, curval;
+ // char buf[20];
+ key = mir_strdup(ValueDBSection);
+ section = key;
+ do {
+ if (key[0] == '/') { key[0] = '\0'; key++; break; }
+ key++;
+ } while (key[0] != '\0');
+
+ val = mir_strdup(ValueTypeDef + 1);
+ val2 = val;
+ do {
+ if (val2[0] == '/') { val2[0] = '\0'; val2++; break; }
+ val2++;
+ } while (val2[0] != '\0');
+
+ if (ValueTypeDef[0] != 's') {
+ l1 = (uint32_t)atol(val);
+ l2 = (uint32_t)atol(val2);
+ }
+
+ switch (ValueTypeDef[0]) {
+ case 's':
+ Value = db_get_sa(0, section, key);
+ if (!Value || (Value && !mir_strcmpi(Value, val2)))
+ Value = mir_strdup(val);
+ else
+ Value = mir_strdup(val2);
+ db_set_s(0, section, key, Value);
+ mir_free(Value);
+ break;
+
+ case 'd':
+ curval = db_get_dw(0, section, key, l2);
+ curval = (curval == l2) ? l1 : l2;
+ db_set_dw(0, section, key, (uint32_t)curval);
+ break;
+
+ case 'w':
+ curval = db_get_w(0, section, key, l2);
+ curval = (curval == l2) ? l1 : l2;
+ db_set_w(0, section, key, (uint16_t)curval);
+ break;
+
+ case 'b':
+ curval = db_get_b(0, section, key, l2);
+ curval = (curval == l2) ? l1 : l2;
+ db_set_b(0, section, key, (uint8_t)curval);
+ break;
+ }
+ mir_free(section);
+ mir_free(val);
+ }
+ return 0;
+}
+
+static char *_skipblank(char * str) //str will be modified;
+{
+ char * endstr = str + mir_strlen(str);
+ while ((*str == ' ' || *str == '\t') && *str != '\0') str++;
+ while ((*endstr == ' ' || *endstr == '\t') && *endstr != '\0' && endstr < str) endstr--;
+ if (*endstr != '\0') {
+ endstr++;
+ *endstr = '\0';
+ }
+ return str;
+}
+
+static int _CallServiceStrParams(IN char * toParce, OUT int *Return)
+{
+ int paramCount = 0;
+ int result = 0;
+
+ char *pszService = mir_strdup(toParce);
+ if (!pszService)
+ return 0;
+ if (mir_strlen(pszService) == 0) {
+ mir_free(pszService);
+ return 0;
+ }
+ char *param2 = strrchr(pszService, '%');
+ if (param2) {
+ paramCount++;
+ *param2 = '\0'; param2++;
+ _skipblank(param2);
+ if (mir_strlen(param2) == 0)
+ param2 = nullptr;
+ }
+ char *param1 = strrchr(pszService, '%');
+ if (param1) {
+ paramCount++;
+ *param1 = '\0'; param1++;
+ _skipblank(param1);
+ if (mir_strlen(param1) == 0)
+ param1 = nullptr;
+ }
+ if (param1 && *param1 == '\"') {
+ param1++;
+ *(param1 + mir_strlen(param1)) = '\0';
+ }
+ else if (param1) {
+ param1 = (char*)atoi(param1);
+ }
+ if (param2 && *param2 == '\"') {
+ param2++;
+ *(param2 + mir_strlen(param2)) = '\0';
+ }
+ else if (param2)
+ param2 = (char*)atoi(param2);
+
+ if (paramCount == 1) {
+ param1 = param2;
+ param2 = nullptr;
+ }
+ if (!ServiceExists(pszService)) {
+ result = 0;
+ }
+ else {
+ result = 1;
+ int ret = CallService(pszService, (WPARAM)param1, (WPARAM)param2);
+ if (Return) *Return = ret;
+ }
+ mir_free(pszService);
+ return result;
+}
+
+
+static LRESULT CALLBACK ModernSkinButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ ModernSkinButtonCtrl* bct = (msg != WM_NCCREATE) ? (ModernSkinButtonCtrl *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA) : nullptr;
+ if (bct) {
+ if (bct->HandleService && IsBadStringPtrA(bct->HandleService, 255))
+ bct->HandleService = nullptr;
+ else if (bct->HandleService && ServiceExists(bct->HandleService)) {
+ HandleServiceParams MSG = {};
+ MSG.hwnd = hwndDlg;
+ MSG.msg = msg;
+ MSG.wParam = wParam;
+ MSG.lParam = lParam;
+ int t = CallService(bct->HandleService, (WPARAM)&MSG, 0);
+ if (MSG.handled) return t;
+ }
+ }
+
+ switch (msg) {
+ case WM_NCCREATE:
+ SetWindowLongPtr(hwndDlg, GWL_STYLE, GetWindowLongPtr(hwndDlg, GWL_STYLE) | BS_OWNERDRAW);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ if (((CREATESTRUCT *)lParam)->lpszName) SetWindowText(hwndDlg, ((CREATESTRUCT *)lParam)->lpszName);
+ return TRUE;
+
+ case WM_DESTROY:
+ if (bct == nullptr)
+ break;
+
+ if (hwndToolTips) {
+ mir_cslock lck(csTips);
+ TOOLINFO ti = { 0 };
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = bct->hwnd;
+ ti.uId = (UINT_PTR)bct->hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+
+ if (SendMessage(hwndToolTips, TTM_GETTOOLCOUNT, 0, (LPARAM)&ti) == 0) {
+ DestroyWindow(hwndToolTips);
+ hwndToolTips = nullptr;
+ }
+ }
+ mir_free(bct->ID);
+ mir_free(bct->CommandService);
+ mir_free(bct->StateService);
+ mir_free(bct->HandleService);
+ mir_free(bct->Hint);
+ mir_free(bct->ValueDBSection);
+ mir_free(bct->ValueTypeDef);
+ mir_free(bct);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break; // DONT! fall thru
+
+ case WM_SETCURSOR:
+ {
+ HCURSOR hCurs1 = LoadCursor(nullptr, IDC_ARROW);
+ if (hCurs1) SetCursor(hCurs1);
+ if (bct) SetToolTip(hwndDlg, bct->Hint);
+ }
+ return 1;
+
+ case WM_PRINT:
+ if (IsWindowVisible(hwndDlg))
+ ModernSkinButtonPaintWorker(hwndDlg, (HDC)wParam);
+ break;
+
+ case WM_PAINT:
+ if (IsWindowVisible(hwndDlg) && !g_CluiData.fLayered) {
+ PAINTSTRUCT ps = {};
+ BeginPaint(hwndDlg, &ps);
+ ModernSkinButtonPaintWorker(hwndDlg, (HDC)ps.hdc);
+ EndPaint(hwndDlg, &ps);
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+
+ case WM_CAPTURECHANGED:
+ if (bct) {
+ bct->hover = 0;
+ bct->down = 0;
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ if (bct) {
+ if (!bct->hover) {
+ SetCapture(bct->hwnd);
+ bct->hover = 1;
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ }
+ else {
+ POINT t = UNPACK_POINT(lParam);
+ ClientToScreen(bct->hwnd, &t);
+ if (WindowFromPoint(t) != bct->hwnd)
+ ReleaseCapture();
+ }
+ }
+ return 0;
+
+ case WM_LBUTTONDOWN:
+ if (bct) {
+ bct->down = 1;
+ SetForegroundWindow(GetParent(bct->hwnd));
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
+ bct->CommandService = nullptr;
+ if (bct->fCallOnPress) {
+ if (bct->CommandService) {
+ if (!_CallServiceStrParams(bct->CommandService, nullptr) && (bct->ValueDBSection && bct->ValueTypeDef))
+ ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
+ }
+ bct->down = 0;
+
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ }
+ }
+ return 0;
+
+ case WM_LBUTTONUP:
+ if (bct && bct->down) {
+ ReleaseCapture();
+ bct->hover = 0;
+ bct->down = 0;
+ ModernSkinButtonPaintWorker(bct->hwnd, nullptr);
+ if (bct->CommandService && IsBadStringPtrA(bct->CommandService, 255))
+ bct->CommandService = nullptr;
+ if (bct->CommandService)
+ if (_CallServiceStrParams(bct->CommandService, nullptr)) {
+ }
+ else if (bct->ValueDBSection && bct->ValueTypeDef)
+ ModernSkinButtonToggleDBValue(bct->ValueDBSection, bct->ValueTypeDef);
+ }
+ }
+ return DefWindowProc(hwndDlg, msg, wParam, lParam);
+}
+
+HWND SetToolTip(HWND hwnd, wchar_t * tip)
+{
+ TOOLINFO ti;
+ if (!tip) return nullptr;
+ mir_cslock lck(csTips);
+ if (!hwndToolTips) {
+ hwndToolTips = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr,
+ WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, nullptr, g_hMirApp, nullptr);
+
+ SetWindowPos(hwndToolTips, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+
+ memset(&ti, 0, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_IDISHWND;
+ ti.hwnd = hwnd;
+ ti.uId = (UINT_PTR)hwnd;
+ if (SendMessage(hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti)) {
+ SendMessage(hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
+ }
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)hwnd;
+ ti.lpszText = (wchar_t*)tip;
+ SendMessage(hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ return hwndToolTips;
+}
+
+
+
+typedef struct _MButton
+{
+ HWND hwnd;
+ uint8_t ConstrainPositionFrom; //(BBRRTTLL) L = 0 - from left, L = 1 from right, L = 2 from center
+ int OrL, OrR, OrT, OrB;
+ int minW, minH;
+ ModernSkinButtonCtrl * bct;
+
+} MButton;
+MButton * Buttons = nullptr;
+uint32_t ButtonsCount = 0;
+
+#define _center_h( rc ) (((rc)->right + (rc)->left ) >> 1)
+#define _center_v( rc ) (((rc)->bottom + (rc)->top ) >> 1)
+
+int ModernSkinButton_AddButton(HWND parent,
+ char * ID,
+ char * CommandService,
+ char * StateDefService,
+ char * HandeService,
+ int Left,
+ int Top,
+ int Right,
+ int Bottom,
+ uint32_t sbFlags,
+ wchar_t * Hint,
+ char * DBkey,
+ char * TypeDef,
+ int MinWidth, int MinHeight)
+{
+ // if (!parent) return 0;
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ if (!Buttons)
+ Buttons = (MButton*)mir_alloc(sizeof(MButton));
+ else
+ Buttons = (MButton*)mir_realloc(Buttons, sizeof(MButton)*(ButtonsCount + 1));
+ {
+ //HWND hwnd;
+ RECT rc = { 0 };
+ ModernSkinButtonCtrl* bct;
+ int l, r, b, t;
+ if (parent) GetClientRect(parent, &rc);
+ l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Left) :
+ (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Left) :
+ (rc.left + Left);
+
+ t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Top) :
+ (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Top) :
+ (rc.top + Top);
+
+ r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Right) :
+ (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Right) :
+ (rc.left + Right);
+
+ b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Bottom) :
+ (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Bottom) :
+ (rc.top + Bottom);
+ bct = (ModernSkinButtonCtrl *)mir_alloc(sizeof(ModernSkinButtonCtrl));
+ memset(bct, 0, sizeof(ModernSkinButtonCtrl));
+ bct->Left = l;
+ bct->Right = r;
+ bct->Top = t;
+ bct->Bottom = b;
+ bct->fCallOnPress = (sbFlags & SBF_CALL_ON_PRESS) != 0;
+ bct->HandleService = mir_strdup(HandeService);
+ bct->CommandService = mir_strdup(CommandService);
+ bct->StateService = mir_strdup(StateDefService);
+ if (DBkey && *DBkey != '\0') bct->ValueDBSection = mir_strdup(DBkey); else bct->ValueDBSection = nullptr;
+ if (TypeDef && *TypeDef != '\0') bct->ValueTypeDef = mir_strdup(TypeDef); else bct->ValueTypeDef = mir_strdup("sDefault");
+ bct->ID = mir_strdup(ID);
+ bct->Hint = mir_wstrdup(Hint);
+ Buttons[ButtonsCount].bct = bct;
+ Buttons[ButtonsCount].hwnd = nullptr;
+ Buttons[ButtonsCount].OrL = Left;
+ Buttons[ButtonsCount].OrT = Top;
+ Buttons[ButtonsCount].OrR = Right;
+ Buttons[ButtonsCount].OrB = Bottom;
+ Buttons[ButtonsCount].ConstrainPositionFrom = (uint8_t)sbFlags;
+ Buttons[ButtonsCount].minH = MinHeight;
+ Buttons[ButtonsCount].minW = MinWidth;
+ ButtonsCount++;
+ // CLUI_ShowWindowMod(hwnd,SW_SHOW);
+ }
+ return 0;
+}
+
+
+
+static int ModernSkinButtonErase(int l, int t, int r, int b)
+{
+ uint32_t i;
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ if (!g_CluiData.fLayered) return 0;
+ if (!g_pCachedWindow) return 0;
+ if (!g_pCachedWindow->hImageDC || !g_pCachedWindow->hBackDC) return 0;
+ if (!(l || r || t || b)) {
+ for (i = 0; i < ButtonsCount; i++) {
+ if (g_clistApi.hwndContactList && Buttons[i].hwnd != nullptr) {
+ //TODO: Erase button
+ BitBlt(g_pCachedWindow->hImageDC, Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right - Buttons[i].bct->Left, Buttons[i].bct->Bottom - Buttons[i].bct->Top,
+ g_pCachedWindow->hBackDC, Buttons[i].bct->Left, Buttons[i].bct->Top, SRCCOPY);
+ }
+ }
+ }
+ else {
+ BitBlt(g_pCachedWindow->hImageDC, l, t, r - l, b - t, g_pCachedWindow->hBackDC, l, t, SRCCOPY);
+ }
+ return 0;
+}
+
+static HWND ModernSkinButtonCreateWindow(ModernSkinButtonCtrl * bct, HWND parent)
+{
+ HWND hwnd;
+
+ if (bct == nullptr) return FALSE;
+ {
+ wchar_t *UnicodeID = mir_a2u(bct->ID);
+ hwnd = CreateWindow(_A2W(MODERNSKINBUTTONCLASS), UnicodeID, WS_VISIBLE | WS_CHILD, bct->Left, bct->Top, bct->Right - bct->Left, bct->Bottom - bct->Top, parent, nullptr, g_plugin.getInst(), nullptr);
+ mir_free(UnicodeID);
+ }
+
+ bct->hwnd = hwnd;
+ bct->focus = 0;
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)bct);
+ return hwnd;
+}
+
+int ModernSkinButtonRedrawAll()
+{
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ g_mutex_bLockUpdating++;
+ for (uint32_t i = 0; i < ButtonsCount; i++) {
+ if (g_clistApi.hwndContactList && Buttons[i].hwnd == nullptr)
+ Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, g_clistApi.hwndContactList);
+ ModernSkinButtonPaintWorker(Buttons[i].hwnd, nullptr);
+ }
+ g_mutex_bLockUpdating--;
+ return 0;
+}
+
+int ModernSkinButtonDeleteAll()
+{
+ if (!ModernSkinButtonModuleIsLoaded)
+ return 0;
+
+ for (size_t i = 0; i < ButtonsCount; i++)
+ if (Buttons[i].hwnd)
+ DestroyWindow(Buttons[i].hwnd);
+
+ mir_free_and_nil(Buttons);
+ ButtonsCount = 0;
+ return 0;
+}
+
+int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *pRect)
+{
+ RECT rc, clr, rd;
+ BOOL altDraw = FALSE;
+ static SIZE oldWndSize = { 0 };
+ if (!ModernSkinButtonModuleIsLoaded) return 0;
+ GetWindowRect(parent, &rd);
+ GetClientRect(parent, &clr);
+ if (!pRect)
+ GetWindowRect(parent, &rc);
+ else
+ rc = *pRect;
+
+ if (g_CluiData.fLayered && (draw & SBRF_DO_ALT_DRAW)) {
+ int sx, sy;
+ sx = rd.right - rd.left;
+ sy = rd.bottom - rd.top;
+ if (sx != oldWndSize.cx || sy != oldWndSize.cy)
+ altDraw = TRUE;//EraseButtons();
+ oldWndSize.cx = sx;
+ oldWndSize.cy = sy;
+ }
+
+ OffsetRect(&rc, -rc.left, -rc.top);
+ rc.right = rc.left + (clr.right - clr.left);
+ rc.bottom = rc.top + (clr.bottom - clr.top);
+ for (uint32_t i = 0; i < ButtonsCount; i++) {
+ int sbFlags = Buttons[i].ConstrainPositionFrom;
+ if (parent && Buttons[i].hwnd == nullptr) {
+ Buttons[i].hwnd = ModernSkinButtonCreateWindow(Buttons[i].bct, parent);
+ altDraw = FALSE;
+ }
+
+ int l = (sbFlags & SBF_ALIGN_TL_RIGHT) ? (rc.right + Buttons[i].OrL) :
+ (sbFlags & SBF_ALIGN_TL_HCENTER) ? (_center_h(&rc) + Buttons[i].OrL) :
+ (rc.left + Buttons[i].OrL);
+
+ int t = (sbFlags & SBF_ALIGN_TL_BOTTOM) ? (rc.bottom + Buttons[i].OrT) :
+ (sbFlags & SBF_ALIGN_TL_VCENTER) ? (_center_v(&rc) + Buttons[i].OrT) :
+ (rc.top + Buttons[i].OrT);
+
+ int r = (sbFlags & SBF_ALIGN_BR_RIGHT) ? (rc.right + Buttons[i].OrR) :
+ (sbFlags & SBF_ALIGN_BR_HCENTER) ? (_center_h(&rc) + Buttons[i].OrR) :
+ (rc.left + Buttons[i].OrR);
+
+ int b = (sbFlags & SBF_ALIGN_BR_BOTTOM) ? (rc.bottom + Buttons[i].OrB) :
+ (sbFlags & SBF_ALIGN_BR_VCENTER) ? (_center_v(&rc) + Buttons[i].OrB) :
+ (rc.top + Buttons[i].OrB);
+
+ SetWindowPos(Buttons[i].hwnd, HWND_TOP, l, t, r - l, b - t, 0);
+ if (rc.right - rc.left < Buttons[i].minW || rc.bottom - rc.top < Buttons[i].minH)
+ CLUI_ShowWindowMod(Buttons[i].hwnd, SW_HIDE);
+ else
+ CLUI_ShowWindowMod(Buttons[i].hwnd, SW_SHOW);
+ if ((1 || altDraw) &&
+ (Buttons[i].bct->Left != l ||
+ Buttons[i].bct->Top != t ||
+ Buttons[i].bct->Right != r ||
+ Buttons[i].bct->Bottom != b)) {
+ //Need to erase in old location
+ ModernSkinButtonErase(Buttons[i].bct->Left, Buttons[i].bct->Top, Buttons[i].bct->Right, Buttons[i].bct->Bottom);
+ }
+
+ Buttons[i].bct->Left = l;
+ Buttons[i].bct->Top = t;
+ Buttons[i].bct->Right = r;
+ Buttons[i].bct->Bottom = b;
+ }
+
+ if (draw & SBRF_DO_REDRAW_ALL)
+ ModernSkinButtonRedrawAll();
+ return 0;
+}
diff --git a/plugins/Clist_modern/src/modern_skinengine.cpp b/plugins/Clist_modern/src/modern_skinengine.cpp
index cda79568be..faf1616152 100644
--- a/plugins/Clist_modern/src/modern_skinengine.cpp
+++ b/plugins/Clist_modern/src/modern_skinengine.cpp
@@ -1,3757 +1,3757 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 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.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-//Include
-#include "stdafx.h"
-
-#define _EFFECTENUM_FULL_H
-#include "modern_effectenum.h"
-#undef _EFFECTENUM_FULL_H
-
-#include "modern_sync.h"
-
-//Implementation
-
-#pragma pack(push, 1)
-/* tga header */
-struct tga_header_t
-{
- uint8_t id_lenght; /* size of image id */
- uint8_t colormap_type; /* 1 is has a colormap */
- uint8_t image_type; /* compression type */
-
- short cm_first_entry; /* colormap origin */
- short cm_length; /* colormap length */
- uint8_t cm_size; /* colormap size */
-
- short x_origin; /* bottom left x coord origin */
- short y_origin; /* bottom left y coord origin */
-
- short width; /* picture width (in pixels) */
- short height; /* picture height (in pixels) */
-
- uint8_t pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */
- uint8_t image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */
-};
-#pragma pack(pop)
-
-/* Global variables */
-
-SKINOBJECTSLIST g_SkinObjectList = { 0 };
-CURRWNDIMAGEDATA *g_pCachedWindow = nullptr;
-
-bool g_bPostWasCanceled = false;
-bool g_bFullRepaint = false;
-
-int g_mutex_bLockUpdating = 0;
-
-SortedList *gl_plGlyphTexts = nullptr;
-SortedList *gl_plSkinFonts = nullptr;
-
-/* Private module variables */
-
-static HANDLE hSkinLoadedEvent;
-
-static GLYPHIMAGE *pLoadedImages = nullptr;
-static uint32_t dwLoadedImagesCount = 0;
-static uint32_t dwLoadedImagesAlocated = 0;
-
-static BOOL flag_bUpdateQueued = FALSE;
-static BOOL flag_bJustDrawNonFramedObjects = FALSE;
-static BOOL mutex_bLockUpdate = FALSE;
-
-static LIST<EFFECTSSTACKITEM> arEffectStack(10, HandleKeySortT);
-static SKINOBJECTSLIST *pCurrentSkin = nullptr;
-static char **pszSettingName = nullptr;
-static int nArrayLen = 0;
-
-static uint8_t pbGammaWeight[256] = { 0 };
-static uint8_t pbGammaWeightAdv[256] = { 0 };
-static BOOL bGammaWeightFilled = FALSE;
-
-static mir_cs cs_SkinChanging;
-
-static LISTMODERNMASK *MainModernMaskList = nullptr;
-
-/* Private module procedures */
-static BOOL ske_GetMaskBit(uint8_t *line, int x);
-static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM lParam);
-static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM lParam);
-
-static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor);
-static void ske_AddParseTextGlyphObject(char * szGlyphTextID, char * szDefineString, SKINOBJECTSLIST *Skin);
-static void ske_AddParseSkinFont(char * szFontID, char * szDefineString);
-static int ske_GetSkinFromDB(char * szSection, SKINOBJECTSLIST * Skin);
-static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin);
-static int ske_LoadSkinFromResource(BOOL bOnlyObjects);
-static void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult);
-static int ske_ValidateSingleFrameImage(FRAMEWND * Frame, BOOL SkipBkgBlitting);
-static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM lParam);
-static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam);
-static INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM lParam);
-
-static MODERNEFFECT meCurrentEffect = { 0xFF, { 0 }, 0, 0 };
-
-//////////////////////////////////////////////////////////////////////////
-// Ini file parser
-
-IniParser::IniParser(wchar_t * tcsFileName, uint8_t flags) : _Flags(flags)
-{
- _DoInit();
- if (!tcsFileName) return;
-
- if (tcsFileName[0] == '%') {
- //TODO: Add parser of resource filename here
- _LoadResourceIni(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF");
- return;
- }
-
- _hFile = _wfopen(tcsFileName, L"r");
- if (_hFile != nullptr) {
- _eType = IT_FILE;
- _isValid = true;
- }
-}
-
-IniParser::IniParser(HINSTANCE hInst, const char * resourceName, const char * resourceType, uint8_t flags) : _Flags(flags)
-{
- _DoInit();
- _LoadResourceIni(hInst, resourceName, resourceType);
-}
-
-IniParser::~IniParser()
-{
- mir_free(_szSection);
- if (_hFile) fclose(_hFile);
- if (_hGlobalRes) {
- UnlockResource(_hGlobalRes);
- FreeResource(_hGlobalRes);
- }
-
- _szSection = nullptr;
- _hGlobalRes = nullptr;
- _hFile = nullptr;
- _isValid = false;
- _eType = IT_UNKNOWN;
-}
-
-HRESULT IniParser::Parse(ParserCallback_t pLineCallBackProc, LPARAM SecCheck)
-{
- if (_isValid && pLineCallBackProc) {
- _pLineCallBackProc = pLineCallBackProc;
- _SecCheck = SecCheck;
- switch (_eType) {
- case IT_FILE:
- return _DoParseFile();
- case IT_RESOURCE:
- return _DoParseResource();
- }
- }
- return E_FAIL;
-}
-
-HRESULT IniParser::WriteStrToDb(const char * szSection, const char * szName, const char * szValue, IniParser * This)
-{
- if (This->_SecCheck) {
- //TODO check security here
- if (wildcmp(szSection, "Skin_Description_Section"))
- return S_OK;
- }
- if ((This->_Flags == IniParser::FLAG_ONLY_OBJECTS) && !wildcmp(szSection, DEFAULTSKINSECTION))
- return S_OK; // skip not objects
-
- switch (szValue[0]) {
- case 'b':
- db_set_b(0, szSection, szName, (uint8_t)atoi(szValue + 1));
- break;
-
- case 'w':
- db_set_w(0, szSection, szName, (uint16_t)atoi(szValue + 1));
- break;
-
- case 'd':
- db_set_dw(0, szSection, szName, (uint32_t)atoi(szValue + 1));
- break;
-
- case 's':
- db_set_s(0, szSection, szName, szValue + 1);
- break;
- }
- return S_OK;
-}
-
-int IniParser::GetSkinFolder(IN const wchar_t * szFileName, OUT wchar_t * pszFolderName)
-{
- wchar_t *szBuff = mir_wstrdup(szFileName);
- wchar_t *pszPos = szBuff + mir_wstrlen(szBuff);
- while (pszPos > szBuff && *pszPos != '.') { pszPos--; }
- *pszPos = '\0';
- mir_wstrcpy(pszFolderName, szBuff);
-
- wchar_t custom_folder[MAX_PATH], cus[MAX_PATH];
- wchar_t *b3;
- mir_wstrncpy(custom_folder, pszFolderName, _countof(custom_folder));
- b3 = custom_folder + mir_wstrlen(custom_folder);
- while (b3 > custom_folder && *b3 != '\\') { b3--; }
- *b3 = '\0';
-
- GetPrivateProfileString(L"Skin_Description_Section", L"SkinFolder", L"", cus, _countof(custom_folder), szFileName);
- if (cus[0] != 0)
- mir_snwprintf(pszFolderName, MAX_PATH, L"%s\\%s", custom_folder, cus);
-
- mir_free(szBuff);
- PathToRelativeW(pszFolderName, pszFolderName);
- return 0;
-}
-
-void IniParser::_DoInit()
-{
- _isValid = false;
- _eType = IT_UNKNOWN;
- _szSection = nullptr;
- _hFile = nullptr;
- _hGlobalRes = nullptr;
- _dwSizeOfRes = 0;
- _pPosition = nullptr;
- _pLineCallBackProc = nullptr;
- _SecCheck = 0;
-}
-
-void IniParser::_LoadResourceIni(HINSTANCE hInst, const char * resourceName, const char * resourceType)
-{
- if (_eType != IT_UNKNOWN)
- return;
-
- HRSRC hRSrc = FindResourceA(hInst, resourceName, resourceType);
- if (!hRSrc)
- return;
-
- _hGlobalRes = LoadResource(hInst, hRSrc);
- if (!_hGlobalRes)
- return;
-
- _dwSizeOfRes = SizeofResource(hInst, hRSrc);
- _pPosition = (char*)LockResource(_hGlobalRes);
-
- _isValid = true;
- _eType = IT_RESOURCE;
-}
-
-HRESULT IniParser::_DoParseFile()
-{
- char szLine[MAX_LINE_LEN];
- _nLine = 0;
- while (fgets(szLine, _countof(szLine), _hFile) != nullptr) {
- size_t len = 0;
- char *pLine = (char *)_RemoveTailings(szLine, len);
- if (len > 0) {
- pLine[len] = '\0';
- if (!_DoParseLine(pLine))
- return E_FAIL;
- }
- else _nLine++;
- };
-
- return S_OK;
-}
-
-HRESULT IniParser::_DoParseResource()
-{
- _nLine = 0;
- char szLine[MAX_LINE_LEN];
- char *pos = (char *)_pPosition;
-
- while (pos < _pPosition + _dwSizeOfRes) {
- int i = 0;
- while (pos < _pPosition + _dwSizeOfRes && *pos != '\n' && *pos != '\0' && i < MAX_LINE_LEN - 1) {
- if ((*pos) != '\r')
- szLine[i++] = *pos;
- pos++;
- }
- szLine[i] = '\0';
- pos++;
-
- size_t len = 0;
- char *pLine = (char *)_RemoveTailings(szLine, len);
- if (len > 0) {
- pLine[len] = '\0';
- if (!_DoParseLine(pLine))
- return E_FAIL;
- }
- else _nLine++;
- }
- return S_OK;
-}
-
-const char *IniParser::_RemoveTailings(const char *szLine, size_t &len)
-{
- const char *pStart = szLine;
- while (*pStart == ' ' || *pStart == '\t')
- pStart++; //skip spaces at begin
- const char *pEnd = pStart + mir_strlen(pStart);
- while (pEnd > pStart && (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n' || *pEnd == '\r'))
- pEnd--;
-
- len = pEnd - pStart;
- return pStart;
-}
-
-BOOL IniParser::_DoParseLine(char *szLine)
-{
- _nLine++;
- size_t len = mir_strlen(szLine);
- if (len == 0)
- return TRUE;
-
- switch (szLine[0]) {
- case ';':
- return TRUE; // start of comment is found
-
- case '[':
- //New section start here
- mir_free(_szSection);
- _szSection = nullptr;
- {
- char *tbuf = szLine + 1; // skip [
-
- char *ebuf = tbuf;
-
- while (*ebuf != ']' && *ebuf != '\0') ebuf++;
- if (*ebuf == '\0')
- return FALSE; // no close bracket
-
- uint32_t sectionLen = ebuf - tbuf + 1;
- _szSection = (char*)mir_alloc(sectionLen + 1);
- mir_strncpy(_szSection, tbuf, sectionLen);
- _szSection[sectionLen] = '\0';
- }
- return TRUE;
-
- default:
- if (!_szSection)
- return TRUE; //param found out of section
-
- char *keyName = szLine;
- char *keyValue = szLine;
-
- size_t eqPlace = 0, len2 = mir_strlen(keyName);
- while (eqPlace < len2 && keyName[eqPlace] != '=')
- eqPlace++; //find '='
-
- if (eqPlace == 0 || eqPlace == len2)
- return TRUE; // = not found or no key name //say false
-
- keyName[eqPlace] = '\0';
-
- keyValue = keyName + eqPlace + 1;
-
- //remove tail spaces in Name
- rtrim(keyName);
- while (*keyValue) {
- if (!isspace(*keyValue))
- break;
- keyValue++;
- }
- rtrim(keyValue);
- _pLineCallBackProc(_szSection, keyName, keyValue, this);
- }
- return TRUE;
-}
-//////////////////////////////////////////////////////////////////////////
-// End of IniParser
-//////////////////////////////////////////////////////////////////////////
-
-HRESULT SkinEngineLoadModule()
-{
- ModernSkinButtonLoadModule();
- MainModernMaskList = (LISTMODERNMASK*)mir_calloc(sizeof(LISTMODERNMASK));
- //init variables
- g_SkinObjectList.dwObjLPAlocated = 0;
- g_SkinObjectList.dwObjLPReserved = 0;
- g_SkinObjectList.pObjects = nullptr;
- // Initialize GDI+
- InitGdiPlus();
- AniAva_InitModule();
- //create services
- CreateServiceFunction(MS_SKIN_DRAWGLYPH, ske_Service_DrawGlyph);
- CreateServiceFunction(MS_SKINENG_UPTATEFRAMEIMAGE, ske_Service_UpdateFrameImage);
- CreateServiceFunction(MS_SKINENG_INVALIDATEFRAMEIMAGE, ske_Service_InvalidateFrameImage);
- CreateServiceFunction(MS_SKINENG_ALPHATEXTOUT, ske_Service_AlphaTextOut);
- CreateServiceFunction(MS_SKINENG_DRAWICONEXFIX, ske_Service_DrawIconEx);
-
- CreateServiceFunction(MS_DRAW_TEXT_WITH_EFFECT, ske_Service_DrawTextWithEffect);
-
- //create event handle
- hSkinLoadedEvent = HookEvent(ME_SKIN_SERVICESCREATED, CLUI_OnSkinLoad);
- NotifyEventHooks(g_CluiData.hEventSkinServicesCreated, 0, 0);
- return S_OK;
-}
-
-int SkinEngineUnloadModule()
-{
- //unload services
- ModernSkinButtonUnloadModule(0, 0);
- ske_UnloadSkin(&g_SkinObjectList);
-
- mir_free_and_nil(g_SkinObjectList.pObjects);
- mir_free_and_nil(g_SkinObjectList.pMaskList);
- mir_free_and_nil(MainModernMaskList);
-
- for (auto &it : arEffectStack)
- mir_free(it);
- arEffectStack.destroy();
-
- if (g_pCachedWindow) {
- SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld);
- SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld);
- DeleteObject(g_pCachedWindow->hBackDIB);
- DeleteObject(g_pCachedWindow->hImageDIB);
- DeleteDC(g_pCachedWindow->hBackDC);
- DeleteDC(g_pCachedWindow->hImageDC);
- ReleaseDC(nullptr, g_pCachedWindow->hScreenDC);
- mir_free_and_nil(g_pCachedWindow);
- }
- GdiFlush();
- DestroyHookableEvent(g_CluiData.hEventSkinServicesCreated);
- AniAva_UnloadModule();
- ShutdownGdiPlus();
- //free variables
- return 1;
-}
-
-BOOL ske_AlphaBlend(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction)
-{
- if (g_CluiData.fDisableSkinEngine && !(blendFunction.BlendFlags & 128)) {
- if (nWidthDest != nWidthSrc || nHeightDest != nHeightSrc)
- return StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, SRCCOPY);
- return BitBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
- }
-
- if (blendFunction.BlendFlags & 128) // Use gdi+ engine
- return GDIPlus_AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
- hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
- &blendFunction);
-
- blendFunction.BlendFlags &= ~128;
- return AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction);
-}
-
-struct DCBUFFER
-{
- HDC hdcOwnedBy;
- int nUsageID;
- int width;
- int height;
- void *pImage;
- HDC hDC;
- HBITMAP oldBitmap;
- HBITMAP hBitmap;
- uint32_t dwDestroyAfterTime;
-};
-
-static int SortBufferList(const DCBUFFER *buf1, const DCBUFFER *buf2)
-{
- if (buf1->hdcOwnedBy != buf2->hdcOwnedBy) return (int)(buf1->hdcOwnedBy < buf2->hdcOwnedBy);
- else if (buf1->nUsageID != buf2->nUsageID) return (int)(buf1->nUsageID < buf2->nUsageID);
- else return (int)(buf1->hDC < buf2->hDC);
-}
-
-LIST<DCBUFFER> BufferList(2, SortBufferList);
-mir_cs BufferListCS;
-
-enum
-{
- BUFFER_DRAWICON = 0,
- BUFFER_DRAWIMAGE
-};
-
-HDC ske_RequestBufferDC(HDC hdcOwner, int dcID, int width, int height, BOOL fClear)
-{
- mir_cslock lck(BufferListCS);
- //Try to find DC in buffer list
- DCBUFFER buf;
- buf.hdcOwnedBy = hdcOwner;
- buf.nUsageID = dcID;
- buf.hDC = nullptr;
- DCBUFFER *pBuf = BufferList.find(&buf);
- if (!pBuf) {
- //if not found - allocate it
- pBuf = (DCBUFFER *)mir_alloc(sizeof(DCBUFFER));
- *pBuf = buf;
- pBuf->width = width;
- pBuf->height = height;
- pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage));
- pBuf->hDC = CreateCompatibleDC(hdcOwner);
- pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap);
- pBuf->dwDestroyAfterTime = 0;
- BufferList.insert(pBuf);
- }
- else {
- if (pBuf->width != width || pBuf->height != height) {
- //resize
- SelectObject(pBuf->hDC, pBuf->oldBitmap);
- DeleteObject(pBuf->hBitmap);
- pBuf->width = width;
- pBuf->height = height;
- pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage));
- pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap);
- }
- else if (fClear)
- memset(pBuf->pImage, 0, width*height*sizeof(uint32_t));
- }
- pBuf->dwDestroyAfterTime = 0;
- return pBuf->hDC;
-}
-
-int ske_ReleaseBufferDC(HDC hDC, int keepTime)
-{
- uint32_t dwCurrentTime = GetTickCount();
-
- // Try to find DC in buffer list - set flag to be release after time;
- mir_cslock lck(BufferListCS);
- for (auto &it : BufferList.rev_iter()) {
- if (it) {
- if (hDC != nullptr && it->hDC == hDC) {
- it->dwDestroyAfterTime = dwCurrentTime + keepTime;
- break;
- }
-
- if ((it->dwDestroyAfterTime && it->dwDestroyAfterTime < dwCurrentTime) || keepTime == -1) {
- SelectObject(it->hDC, it->oldBitmap);
- DeleteObject(it->hBitmap);
- DeleteDC(it->hDC);
- mir_free(BufferList.removeItem(&it));
- }
- }
- }
- return 0;
-}
-
-BOOL ske_SetRgnOpaque(HDC memdc, HRGN hrgn, BOOL force)
-{
- if (g_CluiData.fDisableSkinEngine && !force) return TRUE;
- uint32_t rgnsz = GetRegionData(hrgn, 0, nullptr);
- RGNDATA *rdata = (RGNDATA *)mir_alloc(rgnsz);
- GetRegionData(hrgn, rgnsz, rdata);
- RECT *rect = (RECT *)rdata->Buffer;
- for (uint32_t d = 0; d < rdata->rdh.nCount; d++) {
- ske_SetRectOpaque(memdc, &rect[d], force);
- }
- mir_free(rdata);
- return TRUE;
-}
-
-
-BOOL ske_SetRectOpaque(HDC memdc, RECT *fr, BOOL force)
-{
- int f = 0;
- uint8_t *bits;
- BITMAP bmp;
-
- if (g_CluiData.fDisableSkinEngine && !force)
- return TRUE;
-
- HBITMAP hbmp = (HBITMAP)GetCurrentObject(memdc, OBJ_BITMAP);
- GetObject(hbmp, sizeof(bmp), &bmp);
-
- if (bmp.bmPlanes != 1)
- return FALSE;
-
- if (!bmp.bmBits) {
- f = 1;
- bits = (uint8_t*)mir_alloc(bmp.bmWidthBytes*bmp.bmHeight);
- GetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits);
- }
- else
- bits = (uint8_t*)bmp.bmBits;
-
- int sx = (fr->left > 0) ? fr->left : 0;
- int sy = (fr->top > 0) ? fr->top : 0;
- int ex = (fr->right < bmp.bmWidth) ? fr->right : bmp.bmWidth;
- int ey = (fr->bottom < bmp.bmHeight) ? fr->bottom : bmp.bmHeight;
-
- int width = ex - sx;
-
- uint8_t *pLine = ((uint8_t *)bits) + (bmp.bmHeight - sy - 1) * bmp.bmWidthBytes + (sx << 2) + 3;
- for (int y = 0; y < (ey - sy); y++) {
- uint8_t *pColumn = pLine;
- for (int x = 0; x < width; x++) {
- *pColumn = 255;
- pColumn += 4;
- }
- pLine -= bmp.bmWidthBytes;
- }
- if (f) {
- SetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits);
- mir_free(bits);
- }
- // DeleteObject(hbmp);
- return 1;
-}
-
-static BOOL ske_SkinFillRectByGlyph(HDC hDest, HDC hSource, RECT *rFill, RECT *rGlyph, RECT *rClip, uint8_t mode, uint8_t drawMode)
-{
- BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
-
- //initializations
- if (mode == FM_STRETCH) {
- HDC mem2dc = nullptr;
- HBITMAP mem2bmp = nullptr, oldbmp = nullptr;
- RECT wr;
- IntersectRect(&wr, rClip, rFill);
- if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
- if (drawMode != 2) {
- mem2dc = CreateCompatibleDC(hDest);
- mem2bmp = ske_CreateDIB32(wr.right - wr.left, wr.bottom - wr.top);
- oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
- }
-
- if (drawMode == 0 || drawMode == 2) {
- if (drawMode == 0) {
- ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top,
- hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
- ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf);
- }
- else {
- ske_AlphaBlend(hDest, rFill->left, rFill->top, rFill->right - rFill->left, rFill->bottom - rFill->top,
- hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
-
- }
- }
- else {
- // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
- ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top,
- hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
- ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf);
- }
- if (drawMode != 2) {
- SelectObject(mem2dc, oldbmp);
- DeleteObject(mem2bmp);
- DeleteDC(mem2dc);
- }
- return 1;
- }
- else if (mode == FM_TILE_VERT && (rGlyph->bottom - rGlyph->top > 0) && (rGlyph->right - rGlyph->left > 0)) {
- RECT wr;
- IntersectRect(&wr, rClip, rFill);
- if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
- HDC mem2dc = CreateCompatibleDC(hDest);
- //SetStretchBltMode(mem2dc, HALFTONE);
- HBITMAP mem2bmp = ske_CreateDIB32(wr.right - wr.left, rGlyph->bottom - rGlyph->top);
- HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
- if (!oldbmp)
- return 0;
-
- /// draw here
- {
- int y = 0;
- int w = rFill->right - rFill->left;
- int h = rGlyph->bottom - rGlyph->top;
- if (h > 0 && (wr.bottom - wr.top)*(wr.right - wr.left) != 0) {
- w = wr.right - wr.left;
- {
- // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
- ske_AlphaBlend(mem2dc, -(wr.left - rFill->left), 0, rFill->right - rFill->left, h, hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, h, bf);
- //StretchBlt(mem2dc,-(wr.left-rFill->left), 0, rFill->right-rFill->left,h,hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,h,SRCCOPY);
- }
- if (drawMode == 0 || drawMode == 2) {
- if (drawMode == 0) {
- int dy = (wr.top - rFill->top) % h;
- if (dy >= 0) {
- y = wr.top;
- int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top);
- BitBlt(hDest, wr.left, y, w, ht, mem2dc, 0, dy, SRCCOPY);
- }
-
- y = wr.top + h - dy;
- while (y < wr.bottom - h) {
- BitBlt(hDest, wr.left, y, w, h, mem2dc, 0, 0, SRCCOPY);
- y += h;
- }
- if (y <= wr.bottom)
- BitBlt(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, SRCCOPY);
-
- }
- else {
- y = wr.top;
- while (y < wr.bottom - h) {
- // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
- ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf);
- y += h;
- }
- if (y <= wr.bottom) {
- // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
- ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf);
- }
- }
-
- }
- else {
- BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
- int dy = (wr.top - rFill->top) % h;
- if (dy >= 0) {
- y = wr.top;
- int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top);
- ske_AlphaBlend(hDest, wr.left, y, w, ht, mem2dc, 0, dy, w, ht, bf2);
- }
-
- y = wr.top + h - dy;
- while (y < wr.bottom - h) {
- ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf2);
- y += h;
- }
- if (y <= wr.bottom)
- ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf2);
- }
- }
- }
- SelectObject(mem2dc, oldbmp);
- DeleteObject(mem2bmp);
- DeleteDC(mem2dc);
- }
- else if (mode == FM_TILE_HORZ && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0) && (rFill->bottom - rFill->top) > 0 && (rFill->right - rFill->left) > 0) {
- RECT wr;
- int w = rGlyph->right - rGlyph->left;
- int h = rFill->bottom - rFill->top;
- IntersectRect(&wr, rClip, rFill);
- if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
- h = wr.bottom - wr.top;
- HDC mem2dc = CreateCompatibleDC(hDest);
-
- HBITMAP mem2bmp = ske_CreateDIB32(w, h);
- HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
-
- if (!oldbmp)
- return 0;
- /// draw here
- {
- int x = 0;
- {
- //SetStretchBltMode(mem2dc, HALFTONE);
- //StretchBlt(mem2dc, 0, 0, w,h,hSource,rGlyph->left+(wr.left-rFill->left),rGlyph->top,w,h,SRCCOPY);
-
- // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
- ske_AlphaBlend(mem2dc, 0, -(wr.top - rFill->top), w, rFill->bottom - rFill->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf);
- if (drawMode == 0 || drawMode == 2) {
- if (drawMode == 0) {
- int dx = (wr.left - rFill->left) % w;
- if (dx >= 0) {
- x = wr.left;
- int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
- BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY);
- }
- x = wr.left + w - dx;
- while (x < wr.right - w) {
- BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY);
- x += w;
- }
- if (x <= wr.right)
- BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY);
- }
- else {
- int dx = (wr.left - rFill->left) % w;
- x = wr.left - dx;
- while (x < wr.right - w) {
- ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf);
- x += w;
- }
- if (x <= wr.right)
- ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf);
- }
-
- }
- else {
- BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
- int dx = (wr.left - rFill->left) % w;
- if (dx >= 0) {
- x = wr.left;
- int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
- ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2);
- }
- x = wr.left + w - dx;
- while (x < wr.right - w) {
- ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2);
- x += w;
- }
- if (x <= wr.right)
- ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2);
- }
- }
- }
- SelectObject(mem2dc, oldbmp);
- DeleteObject(mem2bmp);
- DeleteDC(mem2dc);
- }
- else if (mode == FM_TILE_BOTH && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0)) {
- int w = rGlyph->right - rGlyph->left;
- int x = 0;
- int h = rFill->bottom - rFill->top;
- RECT wr;
- IntersectRect(&wr, rClip, rFill);
- if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
- HDC mem2dc = CreateCompatibleDC(hDest);
- HBITMAP mem2bmp = ske_CreateDIB32(w, wr.bottom - wr.top);
- h = wr.bottom - wr.top;
- HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
-#ifdef _DEBUG
- if (!oldbmp)
- (nullptr, "Tile bitmap not selected", "ERROR", MB_OK);
-#endif
- /// draw here
- {
- //fill temp bitmap
- {
- int dy = (wr.top - rFill->top) % (rGlyph->bottom - rGlyph->top);
- int y = -dy;
- while (y < wr.bottom - wr.top) {
-
- ske_AlphaBlend(mem2dc, 0, y, w, rGlyph->bottom - rGlyph->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf);
- y += rGlyph->bottom - rGlyph->top;
- }
-
- //--
- //end temp bitmap
- if (drawMode == 0 || drawMode == 2) {
- if (drawMode == 0) {
- int dx = (wr.left - rFill->left) % w;
- if (dx >= 0) {
- x = wr.left;
- int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
- BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY);
- }
- x = wr.left + w - dx;
- while (x < wr.right - w) {
- BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY);
- x += w;
- }
- if (x <= wr.right)
- BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY);
- }
- else {
- int dx = (wr.left - rFill->left) % w;
- x = wr.left - dx;
- while (x < wr.right - w) {
- ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf);
- x += w;
- }
- if (x <= wr.right)
- ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf);
- }
-
- }
- else {
- BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
- int dx = (wr.left - rFill->left) % w;
- if (dx >= 0) {
- x = wr.left;
- int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
- ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2);
- }
- x = wr.left + w - dx;
- while (x < wr.right - w) {
- ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2);
- x += w;
- }
- if (x <= wr.right)
- ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2);
- }
- }
- }
- SelectObject(mem2dc, oldbmp);
- DeleteObject(mem2bmp);
- DeleteDC(mem2dc);
- }
- return 1;
-
-}
-
-HBITMAP ske_CreateDIB32(int cx, int cy)
-{
- return ske_CreateDIB32Point(cx, cy, nullptr);
-}
-
-HBITMAP ske_CreateDIB32Point(int cx, int cy, void **bits)
-{
- if (cx < 0 || cy < 0)
- return nullptr;
-
- BITMAPINFO RGB32BitsBITMAPINFO = { 0 };
- RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- RGB32BitsBITMAPINFO.bmiHeader.biWidth = cx;//bm.bmWidth;
- RGB32BitsBITMAPINFO.bmiHeader.biHeight = cy;//bm.bmHeight;
- RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1;
- RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32;
- // pointer used for direct Bitmap pixels access
-
- UINT *ptPixels;
- HBITMAP DirectBitmap = CreateDIBSection(nullptr,
- &RGB32BitsBITMAPINFO,
- DIB_RGB_COLORS,
- (void **)&ptPixels,
- nullptr, 0);
- if ((DirectBitmap == nullptr || ptPixels == nullptr) && cx != 0 && cy != 0) {
-#ifdef _DEBUG
- MessageBoxA(nullptr, "Object not allocated. Check GDI object count", "ERROR", MB_OK | MB_ICONERROR);
- DebugBreak();
-#endif
- ;
- }
- else memset(ptPixels, 0, cx*cy * 4);
- if (bits != nullptr) *bits = ptPixels;
- return DirectBitmap;
-}
-
-HRGN ske_CreateOpaqueRgn(uint8_t Level, bool Opaque)
-{
- if (!g_pCachedWindow)
- return nullptr;
-
- RGBQUAD *buf = (RGBQUAD *)g_pCachedWindow->hImageDIBByte;
- if (buf == nullptr)
- return nullptr;
-
- unsigned int cRect = 64;
- PRGNDATA pRgnData = (PRGNDATA)mir_alloc(sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
- memset(pRgnData, 0, sizeof(RGNDATAHEADER));
- pRgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
- pRgnData->rdh.iType = RDH_RECTANGLES;
-
- for (int y = 0; y < g_pCachedWindow->Height; ++y) {
- bool inside = false;
- bool lastin = false;
- unsigned int entry = 0;
-
- for (int x = 0; x < g_pCachedWindow->Width; ++x) {
- inside = Opaque ? (buf->rgbReserved > Level) : (buf->rgbReserved < Level);
- ++buf;
-
- if (inside != lastin) {
- if (inside) {
- lastin = true;
- entry = x;
- }
- else {
- if (pRgnData->rdh.nCount == cRect) {
- cRect = cRect + 64;
- pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
- }
- SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, x, g_pCachedWindow->Height - y + 1);
-
- pRgnData->rdh.nCount++;
- lastin = false;
- }
- }
- }
-
- if (lastin) {
- if (pRgnData->rdh.nCount == cRect) {
- cRect = cRect + 64;
- pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
- }
- SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, g_pCachedWindow->Width, g_pCachedWindow->Height - y + 1);
-
- pRgnData->rdh.nCount++;
- }
- }
-
- HRGN hRgn = ExtCreateRegion(nullptr, sizeof(RGNDATAHEADER) + pRgnData->rdh.nCount*sizeof(RECT), (LPRGNDATA)pRgnData);
- mir_free(pRgnData);
- return hRgn;
-}
-
-static int ske_DrawSkinObject(SKINDRAWREQUEST *preq, GLYPHOBJECT *pobj)
-{
- HDC memdc = nullptr, glyphdc = nullptr;
- int k = 0;
- //BITMAP bmp = {0};
- HBITMAP membmp = nullptr, oldbmp = nullptr, oldglyph = nullptr;
- uint8_t Is32Bit = 0;
- RECT PRect;
- POINT mode2offset = { 0 };
- int depth = 0;
- int mode = 0; //0-FastDraw, 1-DirectAlphaDraw, 2-BufferedAlphaDraw
-
- if (!(preq && pobj)) return -1;
- if ((!pobj->hGlyph || pobj->hGlyph == (HBITMAP)-1) && ((pobj->Style & 7) == ST_IMAGE || (pobj->Style & 7) == ST_FRAGMENT || (pobj->Style & 7) == ST_SOLARIZE)) return 0;
- // Determine painting mode
- depth = GetDeviceCaps(preq->hDC, BITSPIXEL);
- depth = depth < 16 ? 16 : depth;
- Is32Bit = pobj->bmBitsPixel == 32;
- if ((!Is32Bit && pobj->dwAlpha == 255) && pobj->Style != ST_BRUSH) mode = 0;
- else if (pobj->dwAlpha == 255 && pobj->Style != ST_BRUSH) mode = 1;
- else mode = 2;
- // End painting mode
-
- //force mode
-
- if (preq->rcClipRect.bottom - preq->rcClipRect.top*preq->rcClipRect.right - preq->rcClipRect.left == 0)
- preq->rcClipRect = preq->rcDestRect;
- IntersectRect(&PRect, &preq->rcDestRect, &preq->rcClipRect);
- if (IsRectEmpty(&PRect)) {
- return 0;
- }
- if (mode == 2) {
- memdc = CreateCompatibleDC(preq->hDC);
- membmp = ske_CreateDIB32(PRect.right - PRect.left, PRect.bottom - PRect.top);
- oldbmp = (HBITMAP)SelectObject(memdc, membmp);
- if (oldbmp == nullptr) {
- SelectObject(memdc, oldbmp);
- DeleteDC(memdc);
- DeleteObject(membmp);
- return 0;
- }
- }
-
- if (mode != 2) memdc = preq->hDC;
- {
- if (pobj->hGlyph && pobj->hGlyph != (HBITMAP)-1) {
- glyphdc = CreateCompatibleDC(preq->hDC);
- oldglyph = (HBITMAP)SelectObject(glyphdc, pobj->hGlyph);
- }
- // Drawing
- {
- RECT rFill, rGlyph, rClip;
- if ((pobj->Style & 7) == ST_BRUSH) {
- HBRUSH br = CreateSolidBrush(pobj->dwColor);
- RECT fr;
- if (mode == 2) {
- SetRect(&fr, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top);
- FillRect(memdc, &fr, br);
- ske_SetRectOpaque(memdc, &fr);
- // FillRectAlpha(memdc,&fr,pobj->dwColor|0xFF000000);
- }
- else {
- fr = PRect;
- // SetRect(&fr, 0, 0, PRect.right-PRect.left,PRect.bottom-PRect.top);
- FillRect(preq->hDC, &fr, br);
- }
- DeleteObject(br);
- k = -1;
- }
- else {
- if (mode == 2) {
- mode2offset.x = PRect.left;
- mode2offset.y = PRect.top;
- OffsetRect(&PRect, -mode2offset.x, -mode2offset.y);
- }
- rClip = (preq->rcClipRect);
-
- {
- int lft = 0;
- int top = 0;
- int rgh = pobj->bmWidth;
- int btm = pobj->bmHeight;
- if ((pobj->Style & 7) == ST_FRAGMENT) {
- lft = pobj->clipArea.x;
- top = pobj->clipArea.y;
- rgh = min(rgh, lft + pobj->szclipArea.cx);
- btm = min(btm, top + pobj->szclipArea.cy);
- }
-
- // Draw center...
- if (1) {
- rFill.top = preq->rcDestRect.top + pobj->dwTop;
- rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
- rFill.left = preq->rcDestRect.left + pobj->dwLeft;
- rFill.right = preq->rcDestRect.right - pobj->dwRight;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
- rGlyph.top = top + pobj->dwTop;
- rGlyph.left = lft + pobj->dwLeft;
- rGlyph.right = rgh - pobj->dwRight;
- rGlyph.bottom = btm - pobj->dwBottom;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode, mode);
- }
-
- // Draw top side...
- if (1) {
- rFill.top = preq->rcDestRect.top;
- rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
- rFill.left = preq->rcDestRect.left + pobj->dwLeft;
- rFill.right = preq->rcDestRect.right - pobj->dwRight;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
- rGlyph.top = top + 0;
- rGlyph.left = lft + pobj->dwLeft;
- rGlyph.right = rgh - pobj->dwRight;
- rGlyph.bottom = top + pobj->dwTop;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode);
- }
- // Draw bottom side...
- if (1) {
- rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
- rFill.bottom = preq->rcDestRect.bottom;
- rFill.left = preq->rcDestRect.left + pobj->dwLeft;
- rFill.right = preq->rcDestRect.right - pobj->dwRight;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
-
- rGlyph.top = btm - pobj->dwBottom;
- rGlyph.left = lft + pobj->dwLeft;
- rGlyph.right = rgh - pobj->dwRight;
- rGlyph.bottom = btm;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode);
- }
- // Draw left side...
- if (1) {
- rFill.top = preq->rcDestRect.top + pobj->dwTop;
- rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
- rFill.left = preq->rcDestRect.left;
- rFill.right = preq->rcDestRect.left + pobj->dwLeft;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
-
- rGlyph.top = top + pobj->dwTop;
- rGlyph.left = lft;
- rGlyph.right = lft + pobj->dwLeft;
- rGlyph.bottom = btm - pobj->dwBottom;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode);
- }
-
- // Draw right side...
- if (1) {
- rFill.top = preq->rcDestRect.top + pobj->dwTop;
- rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
- rFill.left = preq->rcDestRect.right - pobj->dwRight;
- rFill.right = preq->rcDestRect.right;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
-
- rGlyph.top = top + pobj->dwTop;
- rGlyph.left = rgh - pobj->dwRight;
- rGlyph.right = rgh;
- rGlyph.bottom = btm - pobj->dwBottom;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode);
- }
-
-
- // Draw Top-Left corner...
- if (1) {
- rFill.top = preq->rcDestRect.top;
- rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
- rFill.left = preq->rcDestRect.left;
- rFill.right = preq->rcDestRect.left + pobj->dwLeft;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
-
- rGlyph.top = top;
- rGlyph.left = lft;
- rGlyph.right = lft + pobj->dwLeft;
- rGlyph.bottom = top + pobj->dwTop;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
- }
- // Draw Top-Right corner...
- if (1) {
- rFill.top = preq->rcDestRect.top;
- rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
- rFill.left = preq->rcDestRect.right - pobj->dwRight;
- rFill.right = preq->rcDestRect.right;
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
-
- rGlyph.top = top;
- rGlyph.left = rgh - pobj->dwRight;
- rGlyph.right = rgh;
- rGlyph.bottom = top + pobj->dwTop;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
- }
-
- // Draw Bottom-Left corner...
- if (1) {
- rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
- rFill.bottom = preq->rcDestRect.bottom;
- rFill.left = preq->rcDestRect.left;
- rFill.right = preq->rcDestRect.left + pobj->dwLeft;
-
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
-
- rGlyph.left = lft;
- rGlyph.right = lft + pobj->dwLeft;
- rGlyph.top = btm - pobj->dwBottom;
- rGlyph.bottom = btm;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
- }
- // Draw Bottom-Right corner...
- if (1) {
- rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
- rFill.bottom = preq->rcDestRect.bottom;
- rFill.left = preq->rcDestRect.right - pobj->dwRight;
- rFill.right = preq->rcDestRect.right;
-
-
- if (mode == 2)
- OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
-
- rGlyph.left = rgh - pobj->dwRight;
- rGlyph.right = rgh;
- rGlyph.top = btm - pobj->dwBottom;
- rGlyph.bottom = btm;
-
- k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
- }
- }
-
- }
-
- if ((k > 0 || k == -1) && mode == 2) {
- {
- BLENDFUNCTION bf = { AC_SRC_OVER, 0, pobj->dwAlpha, uint8_t(pobj->bmBitsPixel == 32 && pobj->Style != ST_BRUSH ? AC_SRC_ALPHA : 0) };
- OffsetRect(&PRect, mode2offset.x, mode2offset.y);
- ske_AlphaBlend(preq->hDC, PRect.left, PRect.top, PRect.right - PRect.left, PRect.bottom - PRect.top,
- memdc, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top, bf);
- }
- }
- }
- //free GDI resources
- //--++--
-
- //free GDI resources
- {
-
- if (oldglyph) SelectObject(glyphdc, oldglyph);
- if (glyphdc) DeleteDC(glyphdc);
- }
- if (mode == 2) {
- SelectObject(memdc, oldbmp);
- DeleteDC(memdc);
- DeleteObject(membmp);
- }
-
- }
- if (pobj->plTextList && pobj->plTextList->realCount > 0) {
- HFONT hOldFont;
- for (int i = 0; i < pobj->plTextList->realCount; i++) {
- GLYPHTEXT *gt = (GLYPHTEXT *)pobj->plTextList->items[i];
- if (!gt->hFont) {
- if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) {
- int j = 0;
- for (j = 0; j < gl_plSkinFonts->realCount; j++) {
- SKINFONT *sf = (SKINFONT*)gl_plSkinFonts->items[j];
- if (sf->szFontID && !mir_strcmp(sf->szFontID, gt->szFontID)) {
- gt->hFont = sf->hFont;
- break;
- }
- }
- }
- if (!gt->hFont) gt->hFont = (HFONT)-1;
- }
- if (gt->hFont != (HFONT)-1) {
- RECT rc = { 0 };
- hOldFont = (HFONT)SelectObject(preq->hDC, gt->hFont);
-
-
-
- if (gt->RelativeFlags & 2) rc.left = preq->rcDestRect.right + gt->iLeft;
- else if (gt->RelativeFlags & 1) rc.left = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iLeft;
- else rc.left = preq->rcDestRect.left + gt->iLeft;
-
- if (gt->RelativeFlags & 8) rc.top = preq->rcDestRect.bottom + gt->iTop;
- else if (gt->RelativeFlags & 4) rc.top = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iTop;
- else rc.top = preq->rcDestRect.top + gt->iTop;
-
- if (gt->RelativeFlags & 32) rc.right = preq->rcDestRect.right + gt->iRight;
- else if (gt->RelativeFlags & 16) rc.right = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iRight;
- else rc.right = preq->rcDestRect.left + gt->iRight;
-
- if (gt->RelativeFlags & 128) rc.bottom = preq->rcDestRect.bottom + gt->iBottom;
- else if (gt->RelativeFlags & 64) rc.bottom = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iBottom;
- else rc.bottom = preq->rcDestRect.top + gt->iBottom;
-
- ske_AlphaTextOut(preq->hDC, gt->stText, -1, &rc, gt->dwFlags, gt->dwColor);
- SelectObject(preq->hDC, hOldFont);
- }
- }
- }
-
- return 0;
-}
-
-
-
-int ske_AddDescriptorToSkinObjectList(SKINOBJECTDESCRIPTOR *lpDescr, SKINOBJECTSLIST *Skin)
-{
- SKINOBJECTSLIST *sk = (Skin ? Skin : &g_SkinObjectList);
- if (!mir_strcmpi(lpDescr->szObjectID, "_HEADER_"))
- return 0;
- //check if new object allready presents.
- for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++)
- if (!mir_strcmp(sk->pObjects[i].szObjectID, lpDescr->szObjectID))
- return 0;
- // Realocated list to add space for new object
- if (sk->dwObjLPAlocated + 1 > sk->dwObjLPReserved) {
- sk->pObjects = (SKINOBJECTDESCRIPTOR*)mir_realloc(sk->pObjects, sizeof(SKINOBJECTDESCRIPTOR)*(sk->dwObjLPReserved + 1)/*alloc step*/);
- sk->dwObjLPReserved++;
- }
- { //filling new objects field
- sk->pObjects[sk->dwObjLPAlocated].bType = lpDescr->bType;
- sk->pObjects[sk->dwObjLPAlocated].Data = nullptr;
- sk->pObjects[sk->dwObjLPAlocated].szObjectID = mir_strdup(lpDescr->szObjectID);
- // sk->Objects[sk->dwObjLPAlocated].szObjectName = mir_strdup(lpDescr->szObjectName);
- if (lpDescr->Data != nullptr) { //Copy defaults values
- switch (lpDescr->bType) {
- case OT_GLYPHOBJECT:
- {
- GLYPHOBJECT *gl = (GLYPHOBJECT *)lpDescr->Data;
- sk->pObjects[sk->dwObjLPAlocated].Data = mir_alloc(sizeof(GLYPHOBJECT));
- GLYPHOBJECT *obdat = (GLYPHOBJECT *)sk->pObjects[sk->dwObjLPAlocated].Data;
- memcpy(obdat, gl, sizeof(GLYPHOBJECT));
- if (gl->szFileName != nullptr) {
- obdat->szFileName = mir_strdup(gl->szFileName);
- replaceStr(gl->szFileName, nullptr);
- }
- else obdat->szFileName = nullptr;
-
- obdat->hGlyph = nullptr;
- break;
- }
- }
-
- }
- }
- sk->dwObjLPAlocated++;
- return 1;
-}
-
-static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin)
-{
- SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
- return skin_FindObjectByRequest((char *)szName, sk->pMaskList);
-}
-
-static SKINOBJECTDESCRIPTOR* ske_FindObjectByMask(MODERNMASK *pModernMask, SKINOBJECTSLIST *Skin)
-{
- SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
- return sk->pMaskList ? skin_FindObjectByMask(pModernMask, sk->pMaskList) : nullptr;
-}
-
-SKINOBJECTDESCRIPTOR* ske_FindObjectByName(const char *szName, uint8_t objType, SKINOBJECTSLIST *Skin)
-{
- SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
- for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++) {
- if (sk->pObjects[i].bType == objType || objType == OT_ANY) {
- if (!mir_strcmp(sk->pObjects[i].szObjectID, szName))
- return &(sk->pObjects[i]);
- }
- }
- return nullptr;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Paint glyph
-// wParam - LPSKINDRAWREQUEST
-// lParam - possible direct pointer to modern mask
-//////////////////////////////////////////////////////////////////////////
-
-INT_PTR ske_Service_DrawGlyph(WPARAM wParam, LPARAM lParam)
-{
- auto *preq = (SKINDRAWREQUEST *)wParam;
- if (preq == nullptr)
- return -1;
-
- mir_cslock lck(cs_SkinChanging);
-
- SKINOBJECTDESCRIPTOR *pgl = (lParam ? ske_FindObjectByMask((MODERNMASK*)lParam, nullptr) : ske_FindObject(preq->szObjectID, nullptr));
- if (pgl == nullptr) return -1;
- if (pgl->Data == nullptr) return -1;
-
- GLYPHOBJECT *gl = (GLYPHOBJECT*)pgl->Data;
- int iStyle = gl->Style & 7;
- if (iStyle == ST_SKIP)
- return ST_SKIP;
-
- if (gl->hGlyph == nullptr && gl->hGlyph != (HBITMAP)-1 && (iStyle == ST_IMAGE || iStyle == ST_FRAGMENT || iStyle == ST_SOLARIZE)) {
- if (gl->szFileName) {
- gl->hGlyph = ske_LoadGlyphImage(_A2T(gl->szFileName));
- if (gl->hGlyph) {
- BITMAP bmp = { 0 };
- GetObject(gl->hGlyph, sizeof(BITMAP), &bmp);
- gl->bmBitsPixel = (uint8_t)bmp.bmBitsPixel;
- gl->bmHeight = bmp.bmHeight;
- gl->bmWidth = bmp.bmWidth;
- }
- else gl->hGlyph = (HBITMAP)-1; //invalid
- }
- }
- return ske_DrawSkinObject(preq, gl);
-}
-
-
-void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult)
-{
- BITMAP bmp;
- BOOL flag = FALSE;
- uint8_t *pBitmapBits;
- uint32_t Len;
- int bh, bw, y, x;
-
- GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp);
- bh = bmp.bmHeight;
- bw = bmp.bmWidth;
- Len = bh * bw * 4;
- flag = (bmp.bmBits == nullptr);
- if (flag) {
- pBitmapBits = (LPBYTE)mir_alloc(Len);
- GetBitmapBits(hbmp, Len, pBitmapBits);
- }
- else
- pBitmapBits = (uint8_t *)bmp.bmBits;
- for (y = 0; y < bh; ++y) {
- uint8_t *pPixel = pBitmapBits + bw * 4 * y;
-
- for (x = 0; x < bw; ++x) {
- if (Mult) {
- pPixel[0] = pPixel[0] * pPixel[3] / 255;
- pPixel[1] = pPixel[1] * pPixel[3] / 255;
- pPixel[2] = pPixel[2] * pPixel[3] / 255;
- }
- else {
- pPixel[3] = 255;
- }
- pPixel += 4;
- }
- }
- if (flag) {
- Len = SetBitmapBits(hbmp, Len, pBitmapBits);
- mir_free(pBitmapBits);
- }
- return;
-}
-
-int ske_GetFullFilename(wchar_t *buf, const wchar_t *file, wchar_t *skinfolder, BOOL madeAbsolute)
-{
- wchar_t *SkinPlace = db_get_wsa(0, SKIN, "SkinFolder");
- if (SkinPlace == nullptr)
- SkinPlace = mir_wstrdup(L"\\Skin\\default");
-
- wchar_t b2[MAX_PATH];
- if (file[0] != '\\' && file[1] != ':')
- mir_snwprintf(b2, L"%s\\%s", (skinfolder == nullptr) ? SkinPlace : ((INT_PTR)skinfolder != -1) ? skinfolder : L"", file);
- else
- mir_wstrncpy(b2, file, _countof(b2));
-
- if (madeAbsolute) {
- if (b2[0] == '\\' && b2[1] != '\\')
- PathToAbsoluteW(b2 + 1, buf);
- else
- PathToAbsoluteW(b2, buf);
- }
- else mir_wstrncpy(buf, b2, MAX_PATH);
-
- mir_free(SkinPlace);
- return 0;
-}
-
-/*
-This function is required to load TGA to dib buffer myself
-Major part of routines is from http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga.c
-*/
-
-static BOOL ske_ReadTGAImageData(void *From, uint32_t fromSize, uint8_t *destBuf, uint32_t bufSize, BOOL RLE)
-{
- uint8_t *pos = destBuf;
- uint8_t *from = fromSize ? (uint8_t *)From : nullptr;
- FILE *fp = !fromSize ? (FILE *)From : nullptr;
- uint32_t destCount = 0;
- uint32_t fromCount = 0;
- if (!RLE) {
- while (((from && fromCount < fromSize) || (fp && fromCount < bufSize))
- && (destCount < bufSize)) {
- uint8_t r = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- uint8_t g = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- uint8_t b = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- uint8_t a = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- pos[destCount++] = r;
- pos[destCount++] = g;
- pos[destCount++] = b;
- pos[destCount++] = a;
-
- if (destCount > bufSize) break;
- if (from) if (fromCount < fromSize) break;
- }
- }
- else {
- uint8_t rgba[4];
- uint8_t packet_header;
- uint8_t *ptr = pos;
- uint8_t size;
- int i;
- while (ptr < pos + bufSize) {
- /* read first byte */
- packet_header = from ? from[fromCount] : (uint8_t)fgetc(fp);
- if (from) from++;
- size = 1 + (packet_header & 0x7f);
- if (packet_header & 0x80) {
- /* run-length packet */
- if (from) {
- *((uint32_t*)rgba) = *((uint32_t*)(from + fromCount));
- fromCount += 4;
- }
- else fread(rgba, sizeof(uint8_t), 4, fp);
- for (i = 0; i < size; ++i, ptr += 4) {
- ptr[2] = rgba[2];
- ptr[1] = rgba[1];
- ptr[0] = rgba[0];
- ptr[3] = rgba[3];
- }
- }
- else { /* not run-length packet */
- for (i = 0; i < size; ++i, ptr += 4) {
- ptr[0] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- ptr[1] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- ptr[2] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- ptr[3] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
- }
- }
- }
- }
- return TRUE;
-}
-
-static HBITMAP ske_LoadGlyphImage_TGA(const wchar_t *szFilename)
-{
- uint8_t *colormap = nullptr;
- int cx = 0, cy = 0;
- BOOL err = FALSE;
- tga_header_t header;
- if (!szFilename) return nullptr;
- if (!wildcmpiw(szFilename, L"*\\*%.tga")) {
- //Loading TGA image from file
- FILE *fp = _wfopen(szFilename, L"rb");
- if (!fp) {
- TRACEVAR("error: couldn't open \"%s\"!\n", szFilename);
- return nullptr;
- }
- /* read header */
- fread(&header, sizeof(tga_header_t), 1, fp);
- if ((header.pixel_depth != 32) || ((header.image_type != 10) && (header.image_type != 2))) {
- fclose(fp);
- return nullptr;
- }
-
- /*memory allocation */
- colormap = (uint8_t*)mir_alloc(header.width*header.height * 4);
- cx = header.width;
- cy = header.height;
- fseek(fp, header.id_lenght, SEEK_CUR);
- fseek(fp, header.cm_length, SEEK_CUR);
- err = !ske_ReadTGAImageData((void*)fp, 0, colormap, header.width*header.height * 4, header.image_type == 10);
- fclose(fp);
- }
- else {
- /* reading from resources IDR_TGA_DEFAULT_SKIN */
- HRSRC hRSrc = FindResourceA(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_TGA_DEFAULT_SKIN), "TGA");
- if (!hRSrc) return nullptr;
- HGLOBAL hRes = LoadResource(g_plugin.getInst(), hRSrc);
- if (!hRes) return nullptr;
- uint32_t size = SizeofResource(g_plugin.getInst(), hRSrc);
- uint8_t *mem = (uint8_t*)LockResource(hRes);
- if (size > sizeof(header)) {
- tga_header_t *tgahdr = (tga_header_t*)mem;
- if (tgahdr->pixel_depth == 32 && (tgahdr->image_type == 2 || tgahdr->image_type == 10)) {
- colormap = (uint8_t*)mir_alloc(tgahdr->width*tgahdr->height * 4);
- cx = tgahdr->width;
- cy = tgahdr->height;
- ske_ReadTGAImageData((void*)(mem + sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), size - (sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), colormap, cx*cy * 4, tgahdr->image_type == 10);
- }
- }
- FreeResource(hRes);
- }
-
- if (colormap == nullptr)
- return nullptr;
-
- // create dib section
- uint8_t *pt;
- HBITMAP hbmp = ske_CreateDIB32Point(cx, cy, (void**)&pt);
- if (hbmp)
- memcpy(pt, colormap, cx*cy * 4);
- mir_free(colormap);
- return hbmp;
-}
-
-static HBITMAP ske_LoadGlyphImageByDecoders(const wchar_t *tszFileName)
-{
- if (!wcschr(tszFileName, '%') && !PathFileExists(tszFileName))
- return nullptr;
-
- const wchar_t *ext = wcsrchr(tszFileName, '.');
- if (ext == nullptr)
- return nullptr;
-
- BITMAP bmpInfo;
- HBITMAP hBitmap;
- bool f = false;
-
- if (!mir_wstrcmpi(ext, L".tga")) {
- hBitmap = ske_LoadGlyphImage_TGA(tszFileName);
- f = true;
- }
- else hBitmap = Bitmap_Load(tszFileName);
-
- if (hBitmap == nullptr)
- return nullptr;
-
- GetObject(hBitmap, sizeof(BITMAP), &bmpInfo);
- if (bmpInfo.bmBitsPixel == 32)
- ske_PreMultiplyChannels(hBitmap, f);
- else {
- HDC dc32 = CreateCompatibleDC(nullptr);
- HDC dc24 = CreateCompatibleDC(nullptr);
- HBITMAP hBitmap32 = ske_CreateDIB32(bmpInfo.bmWidth, bmpInfo.bmHeight);
- HBITMAP obmp24 = (HBITMAP)SelectObject(dc24, hBitmap);
- HBITMAP obmp32 = (HBITMAP)SelectObject(dc32, hBitmap32);
- BitBlt(dc32, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, dc24, 0, 0, SRCCOPY);
- SelectObject(dc24, obmp24);
- SelectObject(dc32, obmp32);
- DeleteDC(dc24);
- DeleteDC(dc32);
- DeleteObject(hBitmap);
- hBitmap = hBitmap32;
- ske_PreMultiplyChannels(hBitmap, 0);
- }
- return hBitmap;
-}
-
-static HBITMAP ske_skinLoadGlyphImage(const wchar_t *tszFileName)
-{
- if (!wildcmpiw(tszFileName, L"*.tga"))
- return GDIPlus_LoadGlyphImage(tszFileName);
-
- return ske_LoadGlyphImageByDecoders(tszFileName);
-}
-
-HBITMAP ske_LoadGlyphImage(const wchar_t *tszFileName)
-{
- // try to find image in loaded
- wchar_t szFile[MAX_PATH] = { 0 };
- ske_GetFullFilename(szFile, tszFileName, g_SkinObjectList.szSkinPlace, TRUE);
-
- mir_cslock lck(cs_SkinChanging);
-
- if (pLoadedImages) {
- for (uint32_t i = 0; i < dwLoadedImagesCount; i++) {
- if (!mir_wstrcmpi(pLoadedImages[i].szFileName, szFile)) {
- pLoadedImages[i].dwLoadedTimes++;
- return pLoadedImages[i].hGlyph;
- }
- }
- }
-
- // load new image
- HBITMAP hbmp = ske_skinLoadGlyphImage(szFile);
- if (hbmp == nullptr)
- return nullptr;
-
- // add to loaded list
- if (dwLoadedImagesCount + 1 > dwLoadedImagesAlocated) {
- pLoadedImages = (GLYPHIMAGE*)mir_realloc(pLoadedImages, sizeof(GLYPHIMAGE)*(dwLoadedImagesCount + 1));
- if (!pLoadedImages)
- return nullptr;
- dwLoadedImagesAlocated++;
- }
-
- pLoadedImages[dwLoadedImagesCount].dwLoadedTimes = 1;
- pLoadedImages[dwLoadedImagesCount].hGlyph = hbmp;
- pLoadedImages[dwLoadedImagesCount].szFileName = mir_wstrdup(szFile);
- dwLoadedImagesCount++;
- return hbmp;
-}
-
-int ske_UnloadGlyphImage(HBITMAP hbmp)
-{
- for (uint32_t i = 0; i < dwLoadedImagesCount && pLoadedImages; i++) {
- if (hbmp != pLoadedImages[i].hGlyph)
- continue;
-
- pLoadedImages[i].dwLoadedTimes--;
- if (pLoadedImages[i].dwLoadedTimes == 0) {
- LPGLYPHIMAGE gl = &(pLoadedImages[i]);
- replaceStrW(gl->szFileName, nullptr);
- memmove(&(pLoadedImages[i]), &(pLoadedImages[i + 1]), sizeof(GLYPHIMAGE) * (dwLoadedImagesCount - i - 1));
- dwLoadedImagesCount--;
- DeleteObject(hbmp);
- if (dwLoadedImagesCount == 0) {
- dwLoadedImagesAlocated = 0;
- mir_free_and_nil(pLoadedImages);
- }
- }
- return 0;
- }
- DeleteObject(hbmp);
- return 0;
-}
-
-int ske_UnloadSkin(SKINOBJECTSLIST *Skin)
-{
- ClearMaskList(Skin->pMaskList);
-
- //clear font list
- if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) {
- for (int i = 0; i < gl_plSkinFonts->realCount; i++) {
- SKINFONT *sf = (SKINFONT *)gl_plSkinFonts->items[i];
- if (sf) {
- mir_free(sf->szFontID);
- DeleteObject(sf->hFont);
- mir_free(sf);
- }
- }
- List_Destroy(gl_plSkinFonts);
- mir_free_and_nil(gl_plSkinFonts);
- }
-
- replaceStrW(Skin->szSkinPlace, nullptr);
- if (Skin->pTextList) List_Destroy(Skin->pTextList);
- mir_free_and_nil(Skin->pTextList);
- ModernSkinButtonDeleteAll();
- if (Skin->dwObjLPAlocated == 0)
- return 0;
-
- for (uint32_t i = 0; i < Skin->dwObjLPAlocated; i++) {
- switch (Skin->pObjects[i].bType) {
- case OT_GLYPHOBJECT:
- GLYPHOBJECT *dt = (GLYPHOBJECT*)Skin->pObjects[i].Data;
- if (dt->hGlyph && dt->hGlyph != (HBITMAP)-1)
- ske_UnloadGlyphImage(dt->hGlyph);
- dt->hGlyph = nullptr;
- replaceStr(dt->szFileName, nullptr);
-
- if (dt->plTextList && dt->plTextList->realCount > 0) {
- for (int k = 0; k < dt->plTextList->realCount; k++) {
- GLYPHTEXT *gt = (GLYPHTEXT *)dt->plTextList->items[k];
- if (gt) {
- mir_free(gt->stText);
- mir_free(gt->stValueText);
- mir_free(gt->szFontID);
- mir_free(gt->szGlyphTextID);
- mir_free(gt);
- }
- }
- List_Destroy(dt->plTextList);
- mir_free(dt->plTextList);
- }
- mir_free(dt);
- break;
- }
- replaceStr(Skin->pObjects[i].szObjectID, nullptr);
- }
- mir_free_and_nil(Skin->pObjects);
- Skin->pTextList = nullptr;
- Skin->dwObjLPAlocated = 0;
- Skin->dwObjLPReserved = 0;
- return 0;
-}
-
-static void RegisterMaskByParce(const char *szSetting, char *szValue, SKINOBJECTSLIST *pSkin)
-{
- size_t i, val_len = mir_strlen(szValue);
-
- for (i = 0; i < val_len; i++)
- if (szValue[i] == ':')
- break;
-
- if (i < val_len) {
- char *Obj, *Mask;
- int res;
- uint32_t ID = atoi(szSetting + 1);
- Mask = szValue + i + 1;
- Obj = (char*)mir_alloc(i + 2);
- mir_strncpy(Obj, szValue, i + 1);
- Obj[i + 1] = '\0';
- res = AddStrModernMaskToList(ID, Mask, Obj, pSkin->pMaskList);
- mir_free(Obj);
- }
-}
-
-static int ske_ProcessLoadindString(const char *szSetting, char *szValue)
-{
- if (!pCurrentSkin) return 0;
- if (szSetting[0] == '$')
- RegisterObjectByParce((char *)szSetting, szValue);
- else if (szSetting[0] == '#')
- RegisterButtonByParce((char *)szSetting, szValue);
- else if (szSetting[0] == '@')
- RegisterMaskByParce((char *)szSetting, szValue, pCurrentSkin); ///
- else if (szSetting[0] == 't')
- ske_AddParseTextGlyphObject((char*)szSetting, szValue, pCurrentSkin);
- else if (szSetting[0] == 'f')
- ske_AddParseSkinFont((char*)szSetting, szValue);
- else return 0;
- return 1;
-}
-
-static int ske_enumdb_SkinObjectsProc(const char *szSetting, void *)
-{
- ptrA value(db_get_sa(0, SKIN, szSetting));
- ske_ProcessLoadindString(szSetting, value);
- return 0;
-}
-
-static int ske_SortTextGlyphObjectFunc(void *first, void *second)
-{
- GLYPHTEXT *p1 = *(GLYPHTEXT**)first, *p2 = *(GLYPHTEXT**)second;
- return mir_strcmp(p1->szGlyphTextID, p2->szGlyphTextID);
-}
-
-static void ske_LinkSkinObjects(SKINOBJECTSLIST *pObjectList)
-{
- // LINK Mask with objects
- for (uint32_t i = 0; i < pObjectList->pMaskList->dwMaskCnt; i++) {
- MODERNMASK *mm = &(pObjectList->pMaskList->pl_Masks[i]);
- void *pObject = (void *)ske_FindObjectByName(mm->szObjectName, OT_ANY, (SKINOBJECTSLIST *)pObjectList);
- replaceStr(mm->szObjectName, nullptr);
- mm->bObjectFound = TRUE;
- mm->pObject = pObject;
- }
-
- if (pObjectList->pTextList) {
- // LINK Text with objects
- for (int i = 0; i < pObjectList->pTextList->realCount; i++) {
- GLYPHTEXT *glText = (GLYPHTEXT *)pObjectList->pTextList->items[i];
- SKINOBJECTDESCRIPTOR *lpobj = ske_FindObjectByName(glText->szObjectName, OT_GLYPHOBJECT, pObjectList);
- replaceStr(glText->szObjectName, nullptr);
- GLYPHOBJECT *globj = nullptr;
- if (lpobj)
- globj = (GLYPHOBJECT*)lpobj->Data;
- if (globj) {
- if (!globj->plTextList) {
- globj->plTextList = List_Create(0, 1);
- globj->plTextList->sortFunc = ske_SortTextGlyphObjectFunc;
- }
- List_Insert(globj->plTextList, (void*)glText, globj->plTextList->realCount);
- qsort(globj->plTextList->items, globj->plTextList->realCount, sizeof(GLYPHTEXT*), (int(*)(const void*, const void*))globj->plTextList->sortFunc);
- pObjectList->pTextList->items[i] = nullptr;
- }
- else {
- GLYPHTEXT *gt = glText;
- if (gt) {
- mir_free(gt->stText);
- mir_free(gt->stValueText);
- mir_free(gt->szFontID);
- mir_free(gt->szGlyphTextID);
- mir_free(gt);
- }
- }
- }
- List_Destroy(pObjectList->pTextList);
- mir_free_and_nil(pObjectList->pTextList);
- }
-}
-
-// Getting skin objects and masks from DB
-
-static int ske_GetSkinFromDB(char *, SKINOBJECTSLIST *Skin)
-{
- if (Skin == nullptr) return 0;
- ske_UnloadSkin(Skin);
- g_CluiData.fDisableSkinEngine = db_get_b(0, "ModernData", "DisableEngine", SETTING_DISABLESKIN_DEFAULT);
- // window borders
- if (g_CluiData.fDisableSkinEngine) {
- g_CluiData.LeftClientMargin = 0;
- g_CluiData.RightClientMargin = 0;
- g_CluiData.TopClientMargin = 0;
- g_CluiData.BottomClientMargin = 0;
- return 0;
- }
-
- // window borders
- g_CluiData.LeftClientMargin = (int)db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT);
- g_CluiData.RightClientMargin = (int)db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT);
- g_CluiData.TopClientMargin = (int)db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT);
- g_CluiData.BottomClientMargin = (int)db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT);
-
- Skin->pMaskList = (LISTMODERNMASK*)mir_alloc(sizeof(LISTMODERNMASK));
- memset(Skin->pMaskList, 0, sizeof(LISTMODERNMASK));
- Skin->szSkinPlace = db_get_wsa(0, SKIN, "SkinFolder");
- if (!Skin->szSkinPlace || (wcschr(Skin->szSkinPlace, '%') && !db_get_b(0, SKIN, "Modified", 0))) {
- BOOL bOnlyObjects = FALSE;
- if (Skin->szSkinPlace && wcschr(Skin->szSkinPlace, '%'))
- bOnlyObjects = TRUE;
- mir_free(Skin->szSkinPlace);
- Skin->szSkinPlace = mir_wstrdup(L"%Default%");
- ske_LoadSkinFromResource(bOnlyObjects);
- }
-
- // Load objects
- pCurrentSkin = Skin;
- db_enum_settings(0, ske_enumdb_SkinObjectsProc, SKIN);
-
- SortMaskList(pCurrentSkin->pMaskList);
- ske_LinkSkinObjects(pCurrentSkin);
-
- // Load Masks
- return 0;
-}
-
-// surrogate to be called from outside
-void ske_LoadSkinFromDB(void)
-{
- ske_GetSkinFromDB(SKIN, &g_SkinObjectList);
- g_CluiData.dwKeyColor = db_get_dw(0, "ModernSettings", "KeyColor", (uint32_t)SETTING_KEYCOLOR_DEFAULT);
-}
-
-static int ske_LoadSkinFromResource(BOOL bOnlyObjects)
-{
- IniParser parser(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF", bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS);
- if (parser.CheckOK()) {
- db_delete_module(0, "ModernSkin");
- db_set_s(0, SKIN, "SkinFolder", "%Default%");
- db_set_s(0, SKIN, "SkinFile", "%Default%");
- parser.Parse(IniParser::WriteStrToDb, 0);
- }
- return 0;
-}
-
-// Load data from ini file
-int ske_LoadSkinFromIniFile(wchar_t *szFileName, BOOL bOnlyObjects)
-{
- if (wcschr(szFileName, '%'))
- return ske_LoadSkinFromResource(bOnlyObjects);
-
- IniParser parser(szFileName, bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS);
- if (!parser.CheckOK())
- return 0;
-
- db_delete_module(0, "ModernSkin");
-
- wchar_t skinFolder[MAX_PATH], skinFile[MAX_PATH];
- IniParser::GetSkinFolder(szFileName, skinFolder);
- PathToRelativeW(szFileName, skinFile);
-
- db_set_ws(0, SKIN, "SkinFolder", skinFolder);
- db_set_ws(0, SKIN, "SkinFile", skinFile);
-
- parser.Parse(IniParser::WriteStrToDb, 1);
- return 0;
-}
-
-BOOL ske_TextOut(HDC hdc, int x, int y, LPCTSTR lpString, int nCount)
-{
- SIZE sz;
- GetTextExtentPoint32(hdc, lpString, nCount, &sz);
-
- RECT rc = { 0 };
- SetRect(&rc, x, y, x + sz.cx, y + sz.cy);
- ske_DrawText(hdc, lpString, nCount, &rc, DT_NOCLIP | DT_SINGLELINE | DT_LEFT);
- return 1;
-}
-
-static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM)
-{
- if (!wParam) return 0;
-
- AlphaTextOutParams ap = *(AlphaTextOutParams*)wParam;
- return ske_AlphaTextOut(ap.hDC, ap.lpString, ap.nCount, ap.lpRect, ap.format, ap.ARGBcolor);
-}
-
-static __inline void ske_SetMatrix(sbyte *matrix,
- sbyte a, sbyte b, sbyte c,
- sbyte d, sbyte e, sbyte f,
- sbyte g, sbyte h, sbyte i)
-{
- matrix[0] = a; matrix[1] = b; matrix[2] = c;
- matrix[3] = d; matrix[4] = e; matrix[5] = f;
- matrix[6] = g; matrix[7] = h; matrix[8] = i;
-}
-
-bool ske_ResetTextEffect(HDC hdc)
-{
- int idx = arEffectStack.getIndex((EFFECTSSTACKITEM*)&hdc);
- if (idx == -1)
- return false;
-
- mir_free(arEffectStack[idx]);
- arEffectStack.remove(idx);
- return true;
-}
-
-bool ske_SelectTextEffect(HDC hdc, uint8_t EffectID, uint32_t FirstColor, uint32_t SecondColor)
-{
- if (EffectID > MAXPREDEFINEDEFFECTS)
- return false;
-
- if (EffectID == -1)
- return ske_ResetTextEffect(hdc);
-
- EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc);
- if (effect == nullptr) {
- effect = (EFFECTSSTACKITEM *)mir_alloc(sizeof(EFFECTSSTACKITEM));
- effect->hdc = hdc;
- arEffectStack.insert(effect);
- }
-
- effect->EffectID = EffectID;
- effect->FirstColor = FirstColor;
- effect->SecondColor = SecondColor;
- return true;
-}
-
-static bool ske_GetTextEffect(HDC hdc, MODERNEFFECT *modernEffect)
-{
- if (!modernEffect)
- return false;
-
- EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc);
- if (effect == nullptr)
- return false;
-
- modernEffect->EffectID = effect->EffectID;
- modernEffect->EffectColor1 = effect->FirstColor;
- modernEffect->EffectColor2 = effect->SecondColor;
- modernEffect->EffectMatrix = ModernEffectsEnum[effect->EffectID];
- return true;
-}
-
-static bool ske_DrawTextEffect(uint8_t *destPt, uint8_t *maskPt, uint32_t width, uint32_t height, MODERNEFFECT *effect)
-{
- sbyte *buf;
- sbyte *outbuf;
- sbyte *bufline, *buflineTop, *buflineMid;
- int sign = 0;
- uint8_t *maskline, *destline;
- uint8_t al, rl, gl, bl, ad, rd, gd, bd;
- int k = 0;
- uint32_t x, y;
- sbyte *matrix;
- uint8_t mcTopStart;
- uint8_t mcBottomEnd;
- uint8_t mcLeftStart;
- uint8_t mcRightEnd;
- uint8_t effectCount;
- int minX = width;
- int maxX = 0;
- int minY = height;
- int maxY = 0;
- if (effect->EffectID == 0xFF) return false;
- if (!width || !height) return false;
- if (!destPt) return false;
- buf = (sbyte*)mir_alloc(width*height*sizeof(uint8_t));
- {
- matrix = effect->EffectMatrix.matrix;
- mcTopStart = 2 - effect->EffectMatrix.topEffect;
- mcBottomEnd = 3 + effect->EffectMatrix.bottomEffect;
- mcLeftStart = 2 - effect->EffectMatrix.leftEffect;
- mcRightEnd = 3 + effect->EffectMatrix.rightEffect;
- effectCount = effect->EffectMatrix.cycleCount;
- }
- al = 255 - ((uint8_t)(effect->EffectColor1 >> 24));
- rl = GetRValue(effect->EffectColor1);
- gl = GetGValue(effect->EffectColor1);
- bl = GetBValue(effect->EffectColor1);
- ad = 255 - ((uint8_t)(effect->EffectColor2 >> 24));
- rd = GetRValue(effect->EffectColor2);
- gd = GetGValue(effect->EffectColor2);
- bd = GetBValue(effect->EffectColor2);
-
- // Fill buffer by mid values of image
- for (y = 0; y < height; y++) {
- bufline = buf + y*width;
- maskline = maskPt + ((y*width) << 2);
- for (x = 0; x < width; x++) {
- uint8_t a = (sbyte)(uint32_t)((maskline[0] + maskline[2] + maskline[1] + maskline[1]) >> 4);
- *bufline = a;
- if (a != 0) {
- minX = min((int)x, minX);
- minY = min((int)y, minY);
- maxX = max((int)x, maxX);
- maxY = max((int)y, maxY);
- }
- bufline++;
- maskline += 4;
- }
- }
- // Here perform effect on buffer and place results to outbuf
- for (k = 0; k < (effectCount & 0x7F); k++) {
- minX = max(0, minX + mcLeftStart - 2);
- minY = max(0, minY + mcTopStart - 2);
- maxX = min((int)width, maxX + mcRightEnd - 1);
- maxY = min((int)height, maxY + mcBottomEnd - 1);
-
- outbuf = (sbyte*)mir_alloc(width*height*sizeof(sbyte));
- memset(outbuf, 0, width*height*sizeof(sbyte));
- for (y = (uint32_t)minY; y < (uint32_t)maxY; y++) {
- int val;
- bufline = outbuf + y*width + minX;
- buflineMid = buf + y*width + minX;
- for (x = (uint32_t)minX; x < (uint32_t)maxX; x++) {
- int matrixHor, matrixVer;
- val = 0;
- for (matrixVer = mcTopStart; matrixVer < mcBottomEnd; matrixVer++) {
- int buflineStep = width*(matrixVer - 2);
- int as = y + matrixVer - 2;
- sbyte *buflineTopS = nullptr;
- if (as >= 0 && (uint32_t)as < height) buflineTopS = buflineMid + buflineStep;
-
- for (matrixHor = mcLeftStart; matrixHor < mcRightEnd; matrixHor++) {
- buflineTop = buflineTopS;
- int a = x + matrixHor - 2;
- if (buflineTop && a >= 0 && (uint32_t)a < width) buflineTop += matrixHor - 2;
- else buflineTop = nullptr;
- if (buflineTop)
- val += ((*buflineTop)*matrix[matrixVer * 5 + matrixHor]);
- }
- }
- val = (val + 1) >> 5;
- *bufline = (sbyte)((val>127) ? 127 : (val < -125) ? -125 : val);
- bufline++;
- buflineMid++;
- }
- }
- mir_free(buf);
- buf = outbuf;
- }
- {
- uint8_t r1, b1, g1, a1;
- b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1;
- //perform out to dest
- for (y = 0; y < height; y++) {
- bufline = buf + y*width;
- destline = destPt + ((y*width) << 2);
- for (x = 0; x < width; x++) {
- sbyte val = *bufline;
- uint8_t absVal = ((val < 0) ? -val : val);
-
- if (val != 0) {
- if (val > 0 && sign < 0) {
- b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1;
- }
- else if (val < 0 && sign>0) {
- b1 = bd; r1 = rd; g1 = gd; a1 = ad; sign = -1;
- }
-
- absVal = absVal*a1 / 255;
-
- destline[0] = ((destline[0] * (128 - absVal)) + absVal*b1) >> 7;
- destline[1] = ((destline[1] * (128 - absVal)) + absVal*g1) >> 7;
- destline[2] = ((destline[2] * (128 - absVal)) + absVal*r1) >> 7;
- destline[3] += ((255 - destline[3])*(a1*absVal)) / 32640;
- }
- bufline++;
- destline += 4;
- }
- }
- mir_free(buf);
- }
- return false;
-}
-
-static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor)
-{
- if (!(lpString && lpRect))
- return 0;
-
- // Step first fill fast calc correction tables:
- static bool _tables_empty = true;
- static uint8_t gammaTbl[256]; // Gamma correction table
- static uint16_t blueMulTbl[256]; // blue coefficient multiplication table
- static uint16_t greenMulTbl[256]; // green coefficient multiplication table
- static uint16_t redMulTbl[256]; // red coefficient multiplication table
- if (_tables_empty) {
- // fill tables
- double gammaCfPw = 1000 / (double)DBGetContactSettingRangedWord(0, "ModernData", "AlphaTextOutGamma", 700, 1, 5000);
- uint8_t blueCf = db_get_b(0, "ModernData", "AlphaTextOutBlueCorrection", 28);
- uint8_t redCf = db_get_b(0, "ModernData", "AlphaTextOutRed Correction", 77);
- uint8_t greenCf = db_get_b(0, "ModernData", "AlphaTextOutGreen Correction", 151);
-
- for (int i = 0; i < 256; i++) {
- gammaTbl[i] = (uint8_t)(255 * pow((double)i / 255, gammaCfPw));
- blueMulTbl[i] = i * blueCf;
- redMulTbl[i] = i * redCf;
- greenMulTbl[i] = i * greenCf;
- }
- }
-
- // Calc len of input string
- if (nCount == -1)
- nCount = (int)mir_wstrlen(lpString);
-
- // retrieve destination bitmap bits
- HBITMAP hDestBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP);
- BITMAP bmpDest;
- GetObject(hDestBitmap, sizeof(BITMAP), &bmpDest);
-
- bool destHasNotDIB = (bmpDest.bmBits == nullptr);
- uint8_t *pDestBits;
- if (destHasNotDIB) {
- pDestBits = (uint8_t*)mir_alloc(bmpDest.bmHeight * bmpDest.bmWidthBytes);
- GetBitmapBits(hDestBitmap, bmpDest.bmHeight*bmpDest.bmWidthBytes, pDestBits);
- }
- else
- pDestBits = (uint8_t*)bmpDest.bmBits;
-
- // Creating offscreen buffer
- HDC hOffscreenDC = CreateCompatibleDC(hDC);
-
- // Font to be used to draw text
- HFONT hFont = (HFONT)GetCurrentObject(hDC, OBJ_FONT);
- HFONT hOldOffscreenFont = (HFONT)SelectObject(hOffscreenDC, hFont);
-
- // Calculating text geometric size
- RECT workRect = *lpRect;
- int workRectWidth = workRect.right - workRect.left;
- int workRectHeight = workRect.bottom - workRect.top;
- if (workRectWidth <= 0 || workRectHeight <= 0) {
- if (destHasNotDIB)
- mir_free(pDestBits);
- return 0;
- }
-
- SIZE textSize;
- GetTextExtentPoint32(hOffscreenDC, lpString, nCount, &textSize);
-
- LPCTSTR lpWorkString = lpString;
- BOOL bNeedFreeWorkString = FALSE;
-
- // if we need to cut the text with ellipsis
- if ((format & DT_END_ELLIPSIS) && textSize.cx > workRectWidth) {
- // Calc geometric width of ellipsis
- SIZE szEllipsis;
- GetTextExtentPoint32A(hOffscreenDC, "...", 3, &szEllipsis);
- szEllipsis.cx++; // CORRECTION: some width correction
-
- // Calc count of visible chars
- int visibleCharCount = nCount;
- if (workRectWidth > szEllipsis.cx)
- GetTextExtentExPoint(hOffscreenDC, lpString, nCount, workRectWidth - szEllipsis.cx, &visibleCharCount, nullptr, &textSize);
- else
- GetTextExtentExPoint(hOffscreenDC, lpString, nCount, 0, &visibleCharCount, nullptr, &textSize);
-
- // replace end of string by elipsis
- bNeedFreeWorkString = TRUE;
- lpWorkString = (wchar_t*)mir_alloc((visibleCharCount + 4) * sizeof(wchar_t));
-
- memcpy((void*)lpWorkString, lpString, visibleCharCount * sizeof(wchar_t));
- memcpy((void*)(lpWorkString + visibleCharCount), L"...", 4 * sizeof(wchar_t)); // 3 + 1
-
- nCount = visibleCharCount + 3;
- }
-
- // Calc sizes and offsets
-
- textSize.cx += 2; // CORRECTION: for italic
-
- int drx = 0; // x-axis offset of draw point
-
- if (workRectWidth > textSize.cx) {
- if (format & (DT_RIGHT | DT_RTLREADING))
- drx = workRectWidth - textSize.cx;
- else if (format & DT_CENTER)
- drx = (workRectWidth - textSize.cx) >> 1;
- }
- else textSize.cx = workRectWidth;
-
- int dry = 0; // y-axis offset of draw point
-
- if (workRectHeight > textSize.cy) {
- if (format & DT_BOTTOM)
- dry = workRectHeight - textSize.cy;
- else if (format & DT_VCENTER)
- dry = (workRectHeight - textSize.cy) >> 1;
- }
- else textSize.cy = workRectHeight;
-
- textSize.cx += 4; // CORRECTION: for effects ???
- textSize.cy += 4; // CORRECTION: for effects ???
-
- if (textSize.cx > 0 && textSize.cy > 0) { // Ok we need to paint
- // probably here are mess ofscreen and temp buff dc
-
- //Create bitmap image for offscreen
- uint8_t *bits = nullptr;
- HBITMAP hbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bits);
- if (bits != nullptr) {
- HBITMAP holdbmp = (HBITMAP)SelectObject(hOffscreenDC, hbmp);
-
- //Create buffer bitmap image for temp text
- uint8_t *bufbits = nullptr;
- HBITMAP bufbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bufbits);
- if (bufbits != nullptr) {
- HDC bufDC = CreateCompatibleDC(hDC);
- HBITMAP bufoldbmp = (HBITMAP)SelectObject(bufDC, bufbmp);
- HFONT hOldBufFont = (HFONT)SelectObject(bufDC, hFont);
- SetBkColor(bufDC, RGB(0, 0, 0));
- SetTextColor(bufDC, RGB(255, 255, 255));
-
- // Copy from destination to temp buffer
- BitBlt(hOffscreenDC, 0, 0, textSize.cx, textSize.cy, hDC, workRect.left + drx - 2, workRect.top + dry - 2, SRCCOPY);
-
- //Draw text on offscreen bitmap
- TextOut(bufDC, 2, 2, lpWorkString, nCount);
-
- MODERNEFFECT effect;
- if (ske_GetTextEffect(hDC, &effect))
- ske_DrawTextEffect(bits, bufbits, textSize.cx, textSize.cy, &effect);
-
- // RenderText
- RECT drawRect;
- drawRect.left = 0; drawRect.top = 0;
- drawRect.right = textSize.cx;
- drawRect.bottom = textSize.cy;
-
- uint32_t width = textSize.cx;
- uint32_t heigh = textSize.cy;
-
- uint8_t *pDestScanLine, *pBufScanLine, *pix, *bufpix;
-
- uint8_t al = 255 - ((uint8_t)(ARGBcolor >> 24));
- uint8_t r = GetRValue(ARGBcolor);
- uint8_t g = GetGValue(ARGBcolor);
- uint8_t b = GetBValue(ARGBcolor);
-
- for (uint32_t y = 2; y < heigh - 2; y++) {
- int lineBytes = y * (width << 2);
-
- pDestScanLine = bits + lineBytes;
- pBufScanLine = bufbits + lineBytes;
-
- for (uint32_t x = 2; x < width - 2; x++) {
- pix = pDestScanLine + (x << 2);
- bufpix = pBufScanLine + (x << 2);
-
- // Monochromatic
- uint8_t bx = gammaTbl[bufpix[0]];
- uint8_t gx = gammaTbl[bufpix[1]];
- uint8_t rx = gammaTbl[bufpix[2]];
-
- if (al != 255) {
- bx *= al / 255;
- gx *= al / 255;
- rx *= al / 255;
- }
-
- uint8_t ax = (uint8_t)(((uint32_t)rx * 77 + (uint32_t)gx * 151 + (uint32_t)bx * 28 + 128) / 256);
- if (ax) {
- //Normalize components to gray
- uint8_t axx = 255 - ((r + g + b) >> 2); // Coefficient of grayance, more white font - more gray edges
- uint16_t atx = ax * (255 - axx);
- bx = (atx + bx * axx) / 255;
- gx = (atx + gx * axx) / 255;
- rx = (atx + rx * axx) / 255;
-
- short brx = (short)((b - pix[0])*bx / 255);
- short grx = (short)((g - pix[1])*gx / 255);
- short rrx = (short)((r - pix[2])*rx / 255);
-
- pix[0] += brx;
- pix[1] += grx;
- pix[2] += rrx;
- pix[3] = (uint8_t)(ax + (uint8_t)(255 - ax)*pix[3] / 255);
- }
- }
- }
-
- // Blit to destination
- BitBlt(hDC, workRect.left + drx - 2, workRect.top + dry - 2, textSize.cx, textSize.cy, hOffscreenDC, 0, 0, SRCCOPY);
-
- //free resources
- SelectObject(bufDC, bufoldbmp);
- DeleteObject(bufbmp);
- SelectObject(bufDC, hOldBufFont);
- DeleteDC(bufDC);
- }
- SelectObject(hOffscreenDC, holdbmp);
- DeleteObject(hbmp);
- }
- }
-
- // Final cleanup
- SelectObject(hOffscreenDC, hOldOffscreenFont);
- DeleteDC(hOffscreenDC);
-
- if (destHasNotDIB)
- mir_free(pDestBits);
-
- if (bNeedFreeWorkString)
- mir_free((void*)lpWorkString);
-
- return 0;
-}
-
-static int ske_DrawTextWithEffectWorker(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, FONTEFFECT *effect)
-{
- if (format & DT_CALCRECT)
- return DrawText(hdc, lpString, nCount, lpRect, format);
-
- if (format & DT_RTLREADING)
- SetTextAlign(hdc, TA_RTLREADING);
-
- uint32_t color = GetTextColor(hdc);
- RECT r = *lpRect;
- OffsetRect(&r, 1, 1);
- uint32_t form = format;
- if (effect && effect->effectIndex)
- ske_SelectTextEffect(hdc, effect->effectIndex - 1, effect->baseColour, effect->secondaryColour);
-
- int res = ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color);
-
- if (effect && effect->effectIndex)
- ske_ResetTextEffect(hdc);
-
- return res;
-}
-
-INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM)
-{
- DrawTextWithEffectParam *p = (DrawTextWithEffectParam *)wParam;
- return ske_DrawTextWithEffectWorker(p->hdc, p->lpchText, p->cchText, p->lprc, p->dwDTFormat, p->pEffect);
-}
-
-BOOL ske_DrawText(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format)
-{
- RECT r = *lpRect;
- OffsetRect(&r, 1, 1);
- if (format & DT_RTLREADING)
- SetTextAlign(hdc, TA_RTLREADING);
- if (format & DT_CALCRECT)
- return DrawText(hdc, lpString, nCount, lpRect, format);
- if (format & DT_FORCENATIVERENDER || g_CluiData.fDisableSkinEngine)
- return DrawText(hdc, lpString, nCount, lpRect, format & ~DT_FORCENATIVERENDER);
-
- uint32_t form = format;
- uint32_t color = GetTextColor(hdc);
- return ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color);
-}
-
-HICON ske_ImageList_GetIcon(HIMAGELIST himl, int i)
-{
- IMAGEINFO imi = {};
- BITMAP bm = { 0 };
- if (i != -1) {
- ImageList_GetImageInfo(himl, i, &imi);
- GetObject(imi.hbmImage, sizeof(bm), &bm);
- // stupid bug of Microsoft
- // Icons bitmaps are not premultiplied
- // So Imagelist_AddIcon - premultiply alpha
- // But incorrect - it is possible that alpha will
- // be less than color and
- // ImageList_GetIcon will return overflowed colors
- // TODO: Direct draw Icon from imagelist without
- // extracting of icon
- if (bm.bmBitsPixel == 32) {
- uint8_t *bits = (uint8_t*)bm.bmBits;
- if (!bits) {
- bits = (uint8_t*)mir_alloc(bm.bmWidthBytes*bm.bmHeight);
- GetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits);
- }
-
- uint8_t *bcbits = bits + (bm.bmHeight - imi.rcImage.bottom)*bm.bmWidthBytes + (imi.rcImage.left*bm.bmBitsPixel >> 3);
- for (int iy = 0; iy < imi.rcImage.bottom - imi.rcImage.top; iy++) {
- int x;
- // Dummy microsoft fix - alpha can be less than r,g or b
- // Looks like color channels in icons should be non-premultiplied with alpha
- // But AddIcon store it premultiplied (incorrectly cause can be Alpha == 7F, but R,G or B == 80
- // So i check that alpha is 0x7F and set it to 0x80
- uint32_t *c = ((uint32_t*)bcbits);
- for (x = 0; x < imi.rcImage.right - imi.rcImage.left; x++) {
- uint32_t val = *c;
- uint8_t a = (uint8_t)((val) >> 24);
- if (a != 0) {
- uint8_t r = (uint8_t)((val & 0xFF0000) >> 16);
- uint8_t g = (uint8_t)((val & 0xFF00) >> 8);
- uint8_t b = (uint8_t)(val & 0xFF);
- if (a < r || a < g || a < b) {
- a = max(max(r, g), b);
- val = a << 24 | r << 16 | g << 8 | b;
- *c = val;
- }
- }
- c++;
- }
- bcbits += bm.bmWidthBytes;
- }
-
- if (!bm.bmBits) {
- SetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits);
- mir_free(bits);
- }
- }
- }
- return ImageList_GetIcon(himl, i, ILD_NORMAL);
-}
-
-BOOL ske_ImageList_DrawEx(HIMAGELIST himl, int i, HDC hdcDst, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle)
-{
- // the routine to directly draw icon from image list without creating icon from there - should be some faster
- if (i < 0)
- return FALSE;
-
- if (g_CluiData.fDisableSkinEngine)
- return ImageList_DrawEx(himl, i, hdcDst, x, y, dx, dy, rgbBk, rgbFg, fStyle);
-
- uint8_t alpha;
- if (fStyle & ILD_BLEND25)
- alpha = 64;
- else if (fStyle & ILD_BLEND50)
- alpha = 128;
- else
- alpha = 255;
-
- HICON hIcon = ske_ImageList_GetIcon(himl, i);
- if (hIcon == nullptr)
- return FALSE;
-
- ske_DrawIconEx(hdcDst, x, y, hIcon, dx ? dx : GetSystemMetrics(SM_CXSMICON), dy ? dy : GetSystemMetrics(SM_CYSMICON), 0, nullptr, DI_NORMAL | (alpha << 24));
- DestroyIcon(hIcon);
- return TRUE;
-}
-
-static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM)
-{
- DrawIconFixParam *p = (DrawIconFixParam*)wParam;
- if (!p)
- return 0;
-
- return ske_DrawIconEx(p->hdc, p->xLeft, p->yTop, p->hIcon, p->cxWidth, p->cyWidth, p->istepIfAniCur, p->hbrFlickerFreeDraw, p->diFlags);
-}
-
-
-BOOL ske_DrawIconEx(HDC hdcDst, int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags)
-{
- ICONINFO ici;
- uint8_t alpha = (uint8_t)((diFlags & 0xFF000000) >> 24);
-
- HBITMAP tBmp = nullptr;
- uint8_t *imbits, *imimagbits, *immaskbits;
- uint8_t *t1, *t2, *t3;
-
- //lockimagelist
- uint8_t hasmask = FALSE, no32bit = FALSE, noMirrorMask = FALSE, hasalpha = FALSE;
- alpha = alpha ? alpha : 255;
-
- if (g_CluiData.fDisableSkinEngine && !(diFlags & 0x80))
- return DrawIconEx(hdcDst, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags & 0xFFFF7F);
-
- if (!GetIconInfo(hIcon, &ici))
- return 0;
-
- BITMAP imbt;
- GetObject(ici.hbmColor, sizeof(BITMAP), &imbt);
- if (imbt.bmWidth*imbt.bmHeight == 0) {
- DeleteObject(ici.hbmColor);
- DeleteObject(ici.hbmMask);
- return 0;
- }
-
- BITMAP immaskbt;
- GetObject(ici.hbmMask, sizeof(BITMAP), &immaskbt);
- uint32_t cy = imbt.bmHeight;
-
- if (imbt.bmBitsPixel != 32) {
- no32bit = TRUE;
- HDC tempDC1 = CreateCompatibleDC(hdcDst);
- tBmp = ske_CreateDIB32(imbt.bmWidth, imbt.bmHeight);
- if (tBmp) {
- GetObject(tBmp, sizeof(BITMAP), &imbt);
- HBITMAP otBmp = (HBITMAP)SelectObject(tempDC1, tBmp);
- DrawIconEx(tempDC1, 0, 0, hIcon, imbt.bmWidth, imbt.bmHeight, istepIfAniCur, hbrFlickerFreeDraw, DI_IMAGE);
- noMirrorMask = TRUE;
- SelectObject(tempDC1, otBmp);
- }
- DeleteDC(tempDC1);
- }
-
- bool NoDIBImage = (imbt.bmBits == nullptr);
- if (NoDIBImage) {
- imimagbits = (uint8_t*)mir_alloc(cy*imbt.bmWidthBytes);
- GetBitmapBits(ici.hbmColor, cy*imbt.bmWidthBytes, (void*)imimagbits);
- }
- else imimagbits = (uint8_t*)imbt.bmBits;
-
- if (immaskbt.bmBits == nullptr) {
- immaskbits = (uint8_t*)mir_alloc(cy*immaskbt.bmWidthBytes);
- GetBitmapBits(ici.hbmMask, cy*immaskbt.bmWidthBytes, (void*)immaskbits);
- }
- else immaskbits = (uint8_t*)immaskbt.bmBits;
-
- HDC imDC = CreateCompatibleDC(hdcDst);
- uint32_t icy = imbt.bmHeight;
- uint32_t cx = imbt.bmWidth;
- HBITMAP imBmp = ske_CreateDIB32Point(cx, icy, (void**)&imbits);
- HBITMAP oldBmp = (HBITMAP)SelectObject(imDC, imBmp);
- if (imbits != nullptr && imimagbits != nullptr && immaskbits != nullptr) {
- int x, y;
- int mwb = immaskbt.bmWidthBytes;
- int mwb2 = imbt.bmWidthBytes;
- int bottom = icy;
- int right = cx;
- int top = 0;
- int h = icy;
- for (y = top; (y < bottom) && !hasmask; y++) {
- t1 = immaskbits + y*mwb;
- for (x = 0; (x < mwb) && !hasmask; x++)
- hasmask |= (*(t1 + x) != 0);
- }
-
- for (y = top; (y < bottom) && !hasalpha; y++) {
- t1 = imimagbits + (cy - y - 1)*mwb2;
- for (x = 0; (x < right) && !hasalpha; x++)
- hasalpha |= (*(t1 + (x << 2) + 3) != 0);
- }
-
- for (y = 0; y < (int)icy; y++) {
- t1 = imimagbits + (h - y - 1 - top)*mwb2;
- t2 = imbits + (!no32bit ? y : (icy - y - 1))*mwb2;
- t3 = immaskbits + (noMirrorMask ? y : (h - y - 1 - top))*mwb;
- for (x = 0; x < right; x++) {
- uint8_t mask = 0;
- uint8_t a = 0;
- uint32_t *src = (uint32_t*)(t1 + (x << 2));
- uint32_t *dest = (uint32_t*)(t2 + (x << 2));
- if (hasalpha && !hasmask)
- a = ((uint8_t*)src)[3];
- else {
- mask = ((1 << (7 - x % 8))&(*(t3 + (x >> 3)))) != 0;
- if (mask) {
- if (!hasalpha) {
- *dest = 0;
- continue;
- }
-
- if (((uint8_t*)src)[3]>0)
- a = ((uint8_t*)src)[3];
- else
- a = 0;
- }
- else if (hasalpha || hasmask)
- a = (((uint8_t*)src)[3] > 0 ? ((uint8_t*)src)[3] : 255);
- else if (!hasalpha && !hasmask)
- a = 255;
- else { *dest = 0; continue; }
- }
- if (a > 0) {
- ((uint8_t*)dest)[3] = a;
- ((uint8_t*)dest)[0] = ((uint8_t*)src)[0] * a / 255;
- ((uint8_t*)dest)[1] = ((uint8_t*)src)[1] * a / 255;
- ((uint8_t*)dest)[2] = ((uint8_t*)src)[2] * a / 255;
- }
- else *dest = 0;
- }
- }
- }
-
- BLENDFUNCTION bf = { AC_SRC_OVER, diFlags & 128, alpha, AC_SRC_ALPHA };
- ske_AlphaBlend(hdcDst, xLeft, yTop, cxWidth, cyWidth, imDC, 0, 0, cx, icy, bf);
-
- if (immaskbt.bmBits == nullptr) mir_free(immaskbits);
- if (imbt.bmBits == nullptr) mir_free(imimagbits);
- SelectObject(imDC, oldBmp);
- DeleteObject(imBmp);
- if (no32bit)DeleteObject(tBmp);
- DeleteObject(ici.hbmColor);
- DeleteObject(ici.hbmMask);
- SelectObject(imDC, GetStockObject(DEFAULT_GUI_FONT));
- DeleteDC(imDC);
- return 1;
-}
-
-int ske_PrepareImageButDontUpdateIt(RECT *r)
-{
- if (!g_CluiData.fLayered)
- return ske_ReCreateBackImage(FALSE, r);
-
- mutex_bLockUpdate = 1;
- ske_DrawNonFramedObjects(TRUE, r);
- ske_ValidateFrameImageProc(r);
- mutex_bLockUpdate = 0;
- return 0;
-}
-
-int ske_RedrawCompleteWindow()
-{
- if (g_CluiData.fLayered) {
- ske_DrawNonFramedObjects(TRUE, nullptr);
- CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE, 0, 0);
- }
- else RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
-
- return 0;
-}
-
-// Request to repaint frame or change/drop callback data
-// wParam = hWnd of called frame
-// lParam = pointer to sPaintRequest (or nullptr to redraw all)
-// return 2 - already queued, data updated, 1-have been queued, 0 - failure
-
-static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM) // Immideately recall paint routines for frame and refresh image
-{
- if (MirandaLoading())
- return 0;
-
- RECT wnd;
- bool NoCancelPost = false;
- bool IsAnyQueued = false;
- if (!g_CluiData.mutexOnEdgeSizing)
- GetWindowRect(g_clistApi.hwndContactList, &wnd);
- else
- wnd = g_rcEdgeSizingRect;
-
- if (!g_CluiData.fLayered) {
- RedrawWindow((HWND)wParam, nullptr, nullptr, RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
- return 0;
- }
-
- if (g_pCachedWindow == nullptr) ske_ValidateFrameImageProc(&wnd);
- else if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) ske_ValidateFrameImageProc(&wnd);
- else if (wParam == 0) ske_ValidateFrameImageProc(&wnd);
- else { // all Ok Update Single Frame
- // TO BE LOCKED OR PROXIED
- FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam);
- if (!frm)
- ske_ValidateFrameImageProc(&wnd);
- // Validate frame, update window image and remove it from queue
- else {
- if (frm->UpdateRgn) {
- DeleteObject(frm->UpdateRgn);
- frm->UpdateRgn = nullptr;
- }
- ske_ValidateSingleFrameImage(frm, 0);
- ske_UpdateWindowImage();
- NoCancelPost = 1;
- //-- Remove frame from queue
- if (flag_bUpdateQueued) {
- frm->bQueued = 0;
- for (int i = 0; i < g_nFramesCount; i++)
- if (IsAnyQueued |= g_pfwFrames[i].bQueued)
- break;
- }
- }
- }
-
- if ((!NoCancelPost || !IsAnyQueued) && flag_bUpdateQueued) { // no any queued updating cancel post or need to cancel post
- flag_bUpdateQueued = 0;
- g_bPostWasCanceled = true;
- }
- return 1;
-}
-
-static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam) // Post request for updating
-{
- if (MirandaLoading()) return 0;
-
- if (wParam) {
- FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam);
- sPaintRequest *pr = (sPaintRequest*)lParam;
- if (!g_CluiData.fLayered || (frm && frm->floating))
- return InvalidateRect((HWND)wParam, pr ? (RECT*)&(pr->rcUpdate) : nullptr, FALSE);
-
- if (frm) {
- if (frm->PaintCallbackProc != nullptr) {
- frm->PaintData = (sPaintRequest *)pr;
- frm->bQueued = 1;
- if (pr) {
- HRGN r2;
- if (!IsRectEmpty(&pr->rcUpdate)) {
- RECT rcClient;
- RECT rcUpdate;
- GetClientRect(frm->hWnd, &rcClient);
- IntersectRect(&rcUpdate, &rcClient, &pr->rcUpdate);
- if (IsRectEmpty(&rcUpdate))
- return 0;
- r2 = CreateRectRgn(rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom);
- }
- else {
- RECT r;
- GetClientRect(frm->hWnd, &r);
- r2 = CreateRectRgn(r.left, r.top, r.right, r.bottom);
- }
-
- if (!frm->UpdateRgn) {
- frm->UpdateRgn = CreateRectRgn(0, 0, 1, 1);
- CombineRgn(frm->UpdateRgn, r2, nullptr, RGN_COPY);
- }
- else CombineRgn(frm->UpdateRgn, frm->UpdateRgn, r2, RGN_OR);
- DeleteObject(r2);
- }
- }
- }
- else Sync(QueueAllFramesUpdating, true);
- }
- else Sync(QueueAllFramesUpdating, true);
-
- if (!flag_bUpdateQueued || g_bPostWasCanceled)
- if (PostMessage(g_clistApi.hwndContactList, UM_UPDATE, 0, 0)) {
- flag_bUpdateQueued = 1;
- g_bPostWasCanceled = false;
- }
- return 1;
-}
-
-static int ske_ValidateSingleFrameImage(FRAMEWND *Frame, BOOL SkipBkgBlitting) // Calling frame paint proc
-{
- if (!g_pCachedWindow) { TRACE("ske_ValidateSingleFrameImage calling without cached\n"); return 0; }
- if (Frame->hWnd == (HWND)-1 && !Frame->PaintCallbackProc) { TRACE("ske_ValidateSingleFrameImage calling without FrameProc\n"); return 0; }
-
- // if ok update image
- RECT rcPaint, wnd;
- RECT ru = { 0 };
- int w1, h1, x1, y1;
-
- CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &wnd);
- rcPaint = Frame->wndSize;
- {
- int dx, dy, bx, by;
- if (g_CluiData.mutexOnEdgeSizing) {
- dx = rcPaint.left - wnd.left;
- dy = rcPaint.top - wnd.top;
- bx = rcPaint.right - wnd.right;
- by = rcPaint.bottom - wnd.bottom;
- wnd = g_rcEdgeSizingRect;
- rcPaint.left = wnd.left + dx;
- rcPaint.top = wnd.top + dy;
- rcPaint.right = wnd.right + bx;
- rcPaint.bottom = wnd.bottom + by;
- }
- }
-
- int w = rcPaint.right - rcPaint.left;
- int h = rcPaint.bottom - rcPaint.top;
- if (w <= 0 || h <= 0) {
- TRACE("Frame size smaller than 0\n");
- return 0;
- }
- int x = rcPaint.left;
- int y = rcPaint.top;
- HDC hdc = CreateCompatibleDC(g_pCachedWindow->hImageDC);
- HBITMAP n = ske_CreateDIB32(w, h);
- HBITMAP o = (HBITMAP)SelectObject(hdc, n);
-
- if (Frame->UpdateRgn && !SkipBkgBlitting) {
- GetRgnBox(Frame->UpdateRgn, &ru);
- {
- RECT rc;
- GetClientRect(Frame->hWnd, &rc);
- if (ru.top < 0) ru.top = 0;
- if (ru.left < 0) ru.left = 0;
- if (ru.right > rc.right) ru.right = rc.right;
- if (ru.bottom > rc.bottom) ru.bottom = rc.bottom;
- }
- if (!IsRectEmpty(&ru)) {
- x1 = ru.left;
- y1 = ru.top;
- w1 = ru.right - ru.left;
- h1 = ru.bottom - ru.top;
- }
- else {
- x1 = 0; y1 = 0; w1 = w; h1 = h;
- }
-
- // copy image at hdc
- BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY);
-
- Frame->PaintCallbackProc(Frame->hWnd, hdc, &ru, Frame->UpdateRgn, Frame->dwFlags, Frame->PaintData);
- }
- else {
- RECT r;
- GetClientRect(Frame->hWnd, &r);
- HRGN rgnUpdate = CreateRectRgn(r.left, r.top, r.right, r.bottom);
- ru = r;
- if (!IsRectEmpty(&ru)) {
- x1 = ru.left;
- y1 = ru.top;
- w1 = ru.right - ru.left;
- h1 = ru.bottom - ru.top;
- }
- else {
- x1 = 0; y1 = 0; w1 = w; h1 = h;
- }
-
- // copy image at hdc
- if (SkipBkgBlitting) //image already at foreground
- BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hImageDC, x + x1, y + y1, SRCCOPY);
- else
- BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY);
-
- Frame->PaintCallbackProc(Frame->hWnd, hdc, &r, rgnUpdate, Frame->dwFlags, Frame->PaintData);
- ru = r;
- DeleteObject(rgnUpdate);
- }
- DeleteObject(Frame->UpdateRgn);
- Frame->UpdateRgn = nullptr;
-
- if (!IsRectEmpty(&ru)) {
- x1 = ru.left;
- y1 = ru.top;
- w1 = ru.right - ru.left;
- h1 = ru.bottom - ru.top;
- }
- else {
- x1 = 0; y1 = 0; w1 = w; h1 = h;
- }
-
- BitBlt(g_pCachedWindow->hImageDC, x + x1, y + y1, w1, h1, hdc, x1, y1, SRCCOPY);
-
- if (GetWindowLongPtr(Frame->hWnd, GWL_STYLE) & WS_VSCROLL) {
- //Draw vertical scroll bar
- //
- SCROLLBARINFO si = { 0 };
- si.cbSize = sizeof(SCROLLBARINFO);
- GetScrollBarInfo(Frame->hWnd, OBJID_VSCROLL, &si);
-
- RECT rLine = (si.rcScrollBar);
- RECT rUpBtn = rLine;
- RECT rDnBtn = rLine;
- RECT rThumb = rLine;
-
- rUpBtn.bottom = rUpBtn.top + si.dxyLineButton;
- rDnBtn.top = rDnBtn.bottom - si.dxyLineButton;
- rThumb.top = rLine.top + si.xyThumbTop;
- rThumb.bottom = rLine.top + si.xyThumbBottom;
-
- int dx = Frame->wndSize.right - rLine.right;
- int dy = -rLine.top + Frame->wndSize.top;
-
- OffsetRect(&rLine, dx, dy);
- OffsetRect(&rUpBtn, dx, dy);
- OffsetRect(&rDnBtn, dx, dy);
- OffsetRect(&rThumb, dx, dy);
- BitBlt(g_pCachedWindow->hImageDC, rLine.left, rLine.top, rLine.right - rLine.left, rLine.bottom - rLine.top, g_pCachedWindow->hBackDC, rLine.left, rLine.top, SRCCOPY);
-
- char req[255];
- mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Back", Frame->name);
- SkinDrawGlyph(g_pCachedWindow->hImageDC, &rLine, &rLine, req);
- mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Thumb", Frame->name);
- SkinDrawGlyph(g_pCachedWindow->hImageDC, &rThumb, &rThumb, req);
- mir_snprintf(req, "Main,ID=ScrollBar, Frame=%S,Part=UpLineButton", Frame->name);
- SkinDrawGlyph(g_pCachedWindow->hImageDC, &rUpBtn, &rUpBtn, req);
- mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=DownLineButton", Frame->name);
- SkinDrawGlyph(g_pCachedWindow->hImageDC, &rDnBtn, &rDnBtn, req);
- }
-
- SelectObject(hdc, o);
- DeleteObject(n);
- DeleteDC(hdc);
- return 1;
-}
-
-int ske_BltBackImage(HWND destHWND, HDC destDC, RECT *BltClientRect)
-{
- POINT ptMainWnd = { 0 };
- POINT ptChildWnd = { 0 };
- RECT w = { 0 };
- if (g_CluiData.fDisableSkinEngine) {
- FillRect(destDC, BltClientRect, GetSysColorBrush(COLOR_3DFACE));
- return 0;
- }
- ske_ReCreateBackImage(FALSE, nullptr);
- if (BltClientRect) w = *BltClientRect;
- else GetClientRect(destHWND, &w);
- ptChildWnd.x = w.left;
- ptChildWnd.y = w.top;
- ClientToScreen(destHWND, &ptChildWnd);
- ClientToScreen(g_clistApi.hwndContactList, &ptMainWnd);
- //TODO if main not relative to client area
- return BitBlt(destDC, w.left, w.top, (w.right - w.left), (w.bottom - w.top), g_pCachedWindow->hBackDC, (ptChildWnd.x - ptMainWnd.x), (ptChildWnd.y - ptMainWnd.y), SRCCOPY);
-
-}
-
-int ske_ReCreateBackImage(BOOL Erase, RECT *w)
-{
- RECT wnd = { 0 };
- BOOL IsNewCache = 0;
- if (g_CluiData.fDisableSkinEngine) return 0;
- GetClientRect(g_clistApi.hwndContactList, &wnd);
- if (w) wnd = *w;
- //-- Check cached.
- if (g_pCachedWindow == nullptr) {
- //-- Create New Cache
- g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
- g_pCachedWindow->hScreenDC = GetDC(nullptr);
- g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
- g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
- g_pCachedWindow->Width = wnd.right - wnd.left;
- g_pCachedWindow->Height = wnd.bottom - wnd.top;
- if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) {
- g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
- g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
- g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB);
- g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB);
- }
- IsNewCache = 1;
- }
-
- if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) {
- HBITMAP hb1 = nullptr, hb2 = nullptr;
- g_pCachedWindow->Width = wnd.right - wnd.left;
- g_pCachedWindow->Height = wnd.bottom - wnd.top;
- if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) {
- hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
- hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
- SelectObject(g_pCachedWindow->hImageDC, hb1);
- SelectObject(g_pCachedWindow->hBackDC, hb2);
- }
- else {
- SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld);
- SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld);
- }
- if (g_pCachedWindow->hImageDIB) DeleteObject(g_pCachedWindow->hImageDIB);
- if (g_pCachedWindow->hBackDIB) DeleteObject(g_pCachedWindow->hBackDIB);
- g_pCachedWindow->hImageDIB = hb1;
- g_pCachedWindow->hBackDIB = hb2;
- IsNewCache = 1;
- }
-
- if ((Erase || IsNewCache) && (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0)) {
- HBITMAP hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height);
- SelectObject(g_pCachedWindow->hBackDC, hb2);
- DeleteObject(g_pCachedWindow->hBackDIB);
- g_pCachedWindow->hBackDIB = hb2;
- FillRect(g_pCachedWindow->hBackDC, &wnd, GetSysColorBrush(COLOR_BTNFACE));
- SkinDrawGlyph(g_pCachedWindow->hBackDC, &wnd, &wnd, "Main,ID=Background,Opt=Non-Layered");
- ske_SetRectOpaque(g_pCachedWindow->hBackDC, &wnd);
- }
- return 1;
-}
-
-int ske_DrawNonFramedObjects(BOOL Erase, RECT *r)
-{
- RECT w, wnd;
- if (r) w = *r;
- else CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &w);
- if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr);
- if (g_pCachedWindow == nullptr)
- return ske_ValidateFrameImageProc(&w);
-
- wnd = w;
- OffsetRect(&w, -w.left, -w.top);
- if (Erase) {
- HBITMAP hb2;
- hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height);
- SelectObject(g_pCachedWindow->hBackDC, hb2);
- DeleteObject(g_pCachedWindow->hBackDIB);
- g_pCachedWindow->hBackDIB = hb2;
- }
-
- SkinDrawGlyph(g_pCachedWindow->hBackDC, &w, &w, "Main,ID=Background");
-
- //--Draw frames captions
- for (int i = 0; i < g_nFramesCount; i++) {
- if (g_pfwFrames[i].TitleBar.ShowTitleBar && g_pfwFrames[i].visible && !g_pfwFrames[i].floating) {
- RECT rc;
- SetRect(&rc, g_pfwFrames[i].wndSize.left, g_pfwFrames[i].wndSize.top - g_nTitleBarHeight - g_CluiData.nGapBetweenTitlebar, g_pfwFrames[i].wndSize.right, g_pfwFrames[i].wndSize.top - g_CluiData.nGapBetweenTitlebar);
- Sync(DrawTitleBar, g_pCachedWindow->hBackDC, &rc, g_pfwFrames[i].id);
- }
- }
- g_mutex_bLockUpdating = 1;
-
- flag_bJustDrawNonFramedObjects = 1;
- return 0;
-}
-
-// Calling queued frame paint procs and refresh image
-int ske_ValidateFrameImageProc(RECT *r)
-{
- RECT wnd = { 0 };
- BOOL IsNewCache = 0;
- BOOL IsForceAllPainting = 0;
- if (r) wnd = *r;
- else GetWindowRect(g_clistApi.hwndContactList, &wnd);
- if (wnd.right - wnd.left == 0 || wnd.bottom - wnd.top == 0)
- return 0;
-
- g_mutex_bLockUpdating = 1;
-
- //-- Check cached.
- if (g_pCachedWindow == nullptr) {
- //-- Create New Cache
- g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
- g_pCachedWindow->hScreenDC = GetDC(nullptr);
- g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
- g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
- g_pCachedWindow->Width = wnd.right - wnd.left;
- g_pCachedWindow->Height = wnd.bottom - wnd.top;
- g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
- g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
- g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB);
- g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB);
- IsNewCache = 1;
- }
- if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) {
- HBITMAP hb1, hb2;
- g_pCachedWindow->Width = wnd.right - wnd.left;
- g_pCachedWindow->Height = wnd.bottom - wnd.top;
- hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
- hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
- SelectObject(g_pCachedWindow->hImageDC, hb1);
- SelectObject(g_pCachedWindow->hBackDC, hb2);
- DeleteObject(g_pCachedWindow->hImageDIB);
- DeleteObject(g_pCachedWindow->hBackDIB);
- g_pCachedWindow->hImageDIB = hb1;
- g_pCachedWindow->hBackDIB = hb2;
- IsNewCache = 1;
- }
- if (IsNewCache) {
- ske_DrawNonFramedObjects(0, &wnd);
- IsForceAllPainting = 1;
- }
- if (flag_bJustDrawNonFramedObjects) {
- IsForceAllPainting = 1;
- flag_bJustDrawNonFramedObjects = 0;
- }
- if (IsForceAllPainting) {
- BitBlt(g_pCachedWindow->hImageDC, 0, 0, g_pCachedWindow->Width, g_pCachedWindow->Height, g_pCachedWindow->hBackDC, 0, 0, SRCCOPY);
- Sync(QueueAllFramesUpdating, true);
- }
- //-- Validating frames
- for (int i = 0; i < g_nFramesCount; i++)
- if (g_pfwFrames[i].PaintCallbackProc && g_pfwFrames[i].visible && !g_pfwFrames[i].floating)
- if (g_pfwFrames[i].bQueued || IsForceAllPainting)
- ske_ValidateSingleFrameImage(&g_pfwFrames[i], IsForceAllPainting);
-
- g_mutex_bLockUpdating = 1;
- ModernSkinButtonRedrawAll();
- g_mutex_bLockUpdating = 0;
- if (!mutex_bLockUpdate)
- ske_UpdateWindowImageRect(&wnd);
-
- //-- Clear queue
- Sync(QueueAllFramesUpdating, false);
- flag_bUpdateQueued = 0;
- g_bPostWasCanceled = false;
- return 1;
-}
-
-int ske_UpdateWindowImage()
-{
- if (MirandaExiting())
- return 0;
-
- if (g_CluiData.fLayered) {
- RECT r;
- GetWindowRect(g_clistApi.hwndContactList, &r);
- return ske_UpdateWindowImageRect(&r);
- }
- else ske_ReCreateBackImage(FALSE, nullptr);
- ske_ApplyTranslucency();
- return 0;
-}
-
-int ske_UpdateWindowImageRect(RECT *r) // Update window with current image and
-{
- //if not validity -> ValidateImageProc
- //else Update using current alpha
- RECT wnd = *r;
-
- if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr);
- if (g_pCachedWindow == nullptr) return ske_ValidateFrameImageProc(&wnd);
- if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) return ske_ValidateFrameImageProc(&wnd);
- if (g_bFullRepaint) {
- g_bFullRepaint = false;
- return ske_ValidateFrameImageProc(&wnd);
- }
- ske_JustUpdateWindowImageRect(&wnd);
- return 0;
-}
-
-void ske_ApplyTranslucency()
-{
- int IsTransparancy;
- HWND hwnd = g_clistApi.hwndContactList;
- BOOL layered = (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) ? TRUE : FALSE;
-
- IsTransparancy = g_CluiData.fSmoothAnimation || g_bTransparentFlag;
- if (!g_bTransparentFlag && !g_CluiData.fSmoothAnimation && g_CluiData.bCurrentAlpha != 0)
- g_CluiData.bCurrentAlpha = 255;
-
- if (!g_CluiData.fLayered && IsTransparancy) {
- if (!layered)
- SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
- SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (uint8_t)g_CluiData.bCurrentAlpha, LWA_ALPHA);
- }
-
- AniAva_RedrawAllAvatars(FALSE);
- return;
-}
-
-int ske_JustUpdateWindowImage()
-{
- RECT r;
- if (!g_CluiData.fLayered) {
- ske_ApplyTranslucency();
- return 0;
- }
- GetWindowRect(g_clistApi.hwndContactList, &r);
- return ske_JustUpdateWindowImageRect(&r);
-}
-
-// Update window image
-int ske_JustUpdateWindowImageRect(RECT *rty)
-{
- if (!g_CluiData.fLayered) {
- ske_ApplyTranslucency();
- return 0;
- }
- if (!g_clistApi.hwndContactList)
- return 0;
-
- RECT wnd = *rty;
- RECT rect = wnd;
- POINT dest = { 0 }, src = { 0 };
- dest.x = rect.left;
- dest.y = rect.top;
- SIZE sz = { rect.right - rect.left, rect.bottom - rect.top };
- if (g_CluiData.fLayered) {
- if (!(GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) & WS_EX_LAYERED))
- SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
- Sync(SetAlpha, g_CluiData.bCurrentAlpha);
-
- BLENDFUNCTION bf = { AC_SRC_OVER, 0, g_CluiData.bCurrentAlpha, AC_SRC_ALPHA };
- UpdateLayeredWindow(g_clistApi.hwndContactList, g_pCachedWindow->hScreenDC, &dest, &sz, g_pCachedWindow->hImageDC, &src, RGB(1, 1, 1), &bf, ULW_ALPHA);
- g_CluiData.fAeroGlass = false;
- CLUI_UpdateAeroGlass();
- }
- else InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
- return 0;
-}
-
-int ske_DrawImageAt(HDC hdc, RECT *rc)
-{
- BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
- BitBlt(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, g_pCachedWindow->hBackDC, rc->left, rc->top, SRCCOPY);
- ske_AlphaBlend(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, hdc, 0, 0, rc->right - rc->left, rc->bottom - rc->top, bf);
- if (!g_mutex_bLockUpdating)
- ske_UpdateWindowImage();
- return 0;
-}
-
-HBITMAP ske_GetCurrentWindowImage()
-{
- return g_pCachedWindow->hImageDIB;
-}
-
-/*
-* Glyph text routine
-*/
-
-static uint32_t ske_HexToARGB(char *Hex)
-{
- char buf[10] = { 0 };
- char buf2[11] = { 0 };
- mir_snprintf(buf, "%s\n", Hex);
- if (buf[1] == 'x' || buf[1] == 'X')
- mir_snprintf(buf2, "0x%s\n", buf + 2);
- else
- mir_snprintf(buf2, "0x%s\n", buf);
- buf2[10] = '\0';
-
- char *st;
- uint32_t AARRGGBB = strtoul(buf2, &st, 16);
- uint8_t alpha = (uint8_t)((AARRGGBB & 0xFF000000) >> 24);
- alpha = 255 - ((alpha == 0) ? 255 : alpha);
- AARRGGBB = (alpha << 24) + ((AARRGGBB & 0x00FF0000) >> 16) + ((AARRGGBB & 0x000000FF) << 16) + (AARRGGBB & 0x0000FF00);
- return AARRGGBB;
-}
-
-static wchar_t *ske_ReAppend(wchar_t *lfirst, wchar_t *lsecond, int len)
-{
- size_t l1 = lfirst ? mir_wstrlen(lfirst) : 0;
- size_t l2 = (len ? len : (mir_wstrlen(lsecond) + 1));
- wchar_t *buf = (wchar_t *)mir_alloc((l1 + l2 + 1)*sizeof(wchar_t));
- if (lfirst) memmove(buf, lfirst, l1*sizeof(wchar_t));
- memmove(buf + l1, lsecond, l2*sizeof(wchar_t));
- mir_free(lfirst);
- if (len) buf[l1 + l2] = '\0';
- return buf;
-}
-
-wchar_t* ske_ReplaceVar(wchar_t *var)
-{
- if (!var) return mir_wstrdup(L"");
- if (!mir_wstrcmpi(var, L"Profile")) {
- char buf[MAX_PATH] = { 0 };
- Profile_GetNameA(MAX_PATH, buf);
-
- char *p = strrchr(buf, '.');
- if (p) *p = 0;
-
- mir_free(var);
- return mir_a2u(buf);
- }
-
- mir_free(var);
- return mir_wstrdup(L"");
-}
-
-wchar_t *ske_ParseText(wchar_t *stzText)
-{
- size_t len = mir_wstrlen(stzText);
- wchar_t *result = nullptr;
- size_t stpos = 0, curpos = 0;
-
- while (curpos < len) {
- //1 find first %
- while (curpos < len && stzText[curpos] != (wchar_t)'%')
- curpos++;
- if (curpos < len) { //% found
- if (curpos - stpos > 0)
- result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos));
- stpos = curpos + 1;
- curpos++;
- //3 find second %
- while (curpos < len && stzText[curpos] != (wchar_t)'%')
- curpos++;
- if (curpos >= len)
- break;
- if (curpos - stpos > 0) {
- wchar_t *var = (wchar_t *)mir_alloc((curpos - stpos + 1)*sizeof(wchar_t));
- memcpy(var, stzText + stpos, (curpos - stpos)*sizeof(wchar_t));
- var[curpos - stpos] = (wchar_t)'\0';
- var = ske_ReplaceVar(var);
- result = ske_ReAppend(result, var, 0);
- mir_free(var);
- }
- else result = ske_ReAppend(result, L"%", 0);
-
- curpos++;
- stpos = curpos;
- }
- else {
- if (curpos - stpos > 0)
- result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos));
- break;
- }
- }
- return result;
-}
-/*
-* Parse text object string, find glyph object and add text to it.
-* szGlyphTextID and Define string is:
-* t[szGlyphTextID] = s[HostObjectID],[Left],[Top],[Right],[Bottom],[LTRBHV],[FontID],[Color1],[reservedforColor2],[Text]
-*/
-
-static void ske_AddParseTextGlyphObject(char *szGlyphTextID, char *szDefineString, SKINOBJECTSLIST *Skin)
-{
- char buf[255] = { 0 };
- GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE);
- if (buf[0] == 0)
- return;
-
- GLYPHTEXT *glText = (GLYPHTEXT*)mir_calloc(sizeof(GLYPHTEXT));
- glText->szGlyphTextID = mir_strdup(szGlyphTextID);
- glText->szObjectName = mir_strdup(buf);
- glText->iLeft = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE));
- glText->iTop = atoi(GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE));
- glText->iRight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 3, ',', TRUE));
- glText->iBottom = atoi(GetParamN(szDefineString, buf, sizeof(buf), 4, ',', TRUE));
- {
- memset(buf, 0, 6);
- GetParamN(szDefineString, buf, sizeof(buf), 5, ',', TRUE);
- buf[0] &= 95; buf[1] &= 95; buf[2] &= 95; buf[3] &= 95; buf[4] &= 95; buf[5] &= 95; //to uppercase: &01011111 (0-95)
- glText->RelativeFlags =
- (buf[0] == 'C' ? 1 : ((buf[0] == 'R') ? 2 : 0)) //[BC][RC][BC][RC] --- Left relative
- | (buf[1] == 'C' ? 4 : ((buf[1] == 'B') ? 8 : 0)) // | | |--------- Top relative
- | (buf[2] == 'C' ? 16 : ((buf[2] == 'R') ? 32 : 0)) // | |--------------Right relative
- | (buf[3] == 'C' ? 64 : ((buf[3] == 'B') ? 128 : 0)); // |------------------Bottom relative
- glText->dwFlags = (buf[4] == 'C' ? DT_CENTER : ((buf[4] == 'R') ? DT_RIGHT : DT_LEFT))
- | (buf[5] == 'C' ? DT_VCENTER : ((buf[5] == 'B') ? DT_BOTTOM : DT_TOP));
- }
- glText->szFontID = mir_strdup(GetParamN(szDefineString, buf, sizeof(buf), 6, ',', TRUE));
-
- glText->dwColor = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 7, ',', TRUE));
- glText->dwShadow = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 8, ',', TRUE));
- glText->stValueText = mir_a2u(GetParamN(szDefineString, buf, sizeof(buf), 9, ',', TRUE));
- glText->stText = ske_ParseText(glText->stValueText);
-
- if (!Skin->pTextList)
- Skin->pTextList = List_Create(0, 1);
- List_InsertPtr(Skin->pTextList, glText);
-}
-
-
-/*
-* Parse font definition string.
-* szGlyphTextID and Define string is:
-* f[szFontID] = s[FontTypefaceName],[size],[BIU]
-*/
-static void ske_AddParseSkinFont(char *szFontID, char *szDefineString)
-{
- SKINFONT *sf = (SKINFONT*)mir_calloc(sizeof(SKINFONT));
- if (!sf)
- return;
-
- LOGFONTA logfont = { 0 };
- logfont.lfCharSet = DEFAULT_CHARSET;
- logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
- logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
- logfont.lfQuality = DEFAULT_QUALITY;
- logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
-
- char buf[255];
- strncpy_s(logfont.lfFaceName, GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE), _TRUNCATE);
- logfont.lfHeight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE));
- if (logfont.lfHeight < 0) {
- HDC hdc = CreateCompatibleDC(nullptr);
- logfont.lfHeight = (long)-MulDiv(logfont.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
- DeleteDC(hdc);
- }
- logfont.lfHeight = -logfont.lfHeight;
- GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE);
- buf[0] &= 95; buf[1] &= 95; buf[2] &= 95;
- logfont.lfWeight = (buf[0] == 'B') ? FW_BOLD : FW_NORMAL;
- logfont.lfItalic = (buf[1] == 'I') ? 1 : 0;
- logfont.lfUnderline = (buf[2] == 'U') ? 1 : 0;
-
- sf->hFont = CreateFontIndirectA(&logfont);
- if (sf->hFont) {
- sf->szFontID = mir_strdup(szFontID);
- if (!gl_plSkinFonts)
- gl_plSkinFonts = List_Create(0, 1);
- if (gl_plSkinFonts)
- List_Insert(gl_plSkinFonts, sf, gl_plSkinFonts->realCount);
- else
- mir_free(sf);
- }
- else mir_free(sf);
-}
-
-/*
- * ske_CheckHasAlfaChannel - checks if image has at least one uint8_t in alpha chennel
- * that is not a 0. (is image real 32 bit or just 24 bit)
- */
-static BOOL ske_CheckHasAlfaChannel(uint8_t *from, int widthByte, int height)
-{
- uint32_t *pt = (uint32_t*)from;
- for (int j = 0; j < height; j++) {
- uint8_t *add = (uint8_t*)pt + widthByte;
- while (pt < (uint32_t*)add) {
- if ((*pt & 0xFF000000) != 0)
- return TRUE;
- pt++;
- }
- pt = (uint32_t*)(from + widthByte*j);
- }
- return FALSE;
-}
-
-/*
- * ske_CheckIconHasMask - checks if mask image has at least one that is not a 0.
- * Not sure is ir required or not
- */
-static BOOL ske_CheckIconHasMask(uint8_t *from)
-{
- for (int i = 0; i < 16 * 16 / 8; i++)
- if (from[i] != 0)
- return TRUE;
-
- return FALSE;
-}
-
-/*
- * ske_GetMaskBit - return value of apropriate mask bit in line at x position
- */
-static BOOL ske_GetMaskBit(uint8_t *line, int x)
-{
- return ((*(line + (x >> 3)))&(0x01 << (7 - (x & 0x07)))) != 0;
-}
-
-/*
- * ske_Blend - alpha ske_Blend ARGB values of 2 pixels. X1 - underlaying,
- * X2 - overlaying points.
- */
-
-static uint32_t ske_Blend(uint32_t X1, uint32_t X2, uint8_t alpha)
-{
- uint8_t a1 = (uint8_t)(X1 >> 24);
- uint8_t a2 = (uint8_t)(((X2 >> 24)*alpha) >> 8);
- uint8_t r1 = (uint8_t)(X1 >> 16);
- uint8_t r2 = (uint8_t)(X2 >> 16);
- uint8_t g1 = (uint8_t)(X1 >> 8);
- uint8_t g2 = (uint8_t)(X2 >> 8);
- uint8_t b1 = (uint8_t)(X1);
- uint8_t b2 = (uint8_t)(X2);
-
- uint8_t a_1 = ~a1;
- uint8_t a_2 = ~a2;
- uint16_t am = (uint16_t)a1*a_2;
-
- /* it is possible to use >>8 instead of /255 but it is require additional
- * checking of alphavalues
- */
- uint16_t ar = a1 + (((uint16_t)a_1*a2) / 255);
- // if a2 more than 0 than result should be more
- // or equal (if a1 == 0) to a2, else in combination
- // with mask we can get here black points
-
- ar = (a2 > ar) ? a2 : ar;
-
- if (ar == 0) return 0;
-
- uint16_t arm = ar * 255;
- uint16_t rr = (((uint16_t)r1*am + (uint16_t)r2*a2 * 255)) / arm;
- uint16_t gr = (((uint16_t)g1*am + (uint16_t)g2*a2 * 255)) / arm;
- uint16_t br = (((uint16_t)b1*am + (uint16_t)b2*a2 * 255)) / arm;
- return (ar << 24) | (rr << 16) | (gr << 8) | br;
-}
-
-/*
- * CreateJoinedIcon - creates new icon by drawing hTop over hBottom.
- */
-
-HICON ske_CreateJoinedIcon(HICON hBottom, HICON hTop, uint8_t alpha)
-{
- ICONINFO iNew = { 0 };
- ICONINFO iciBottom = { 0 };
- ICONINFO iciTop = { 0 };
-
- BITMAP bmp_top = { 0 };
- BITMAP bmp_top_mask = { 0 };
-
- BITMAP bmp_bottom = { 0 };
- BITMAP bmp_bottom_mask = { 0 };
-
- HDC tempDC = CreateCompatibleDC(nullptr);
-
- uint8_t *ptPixels;
- HBITMAP nImage = ske_CreateDIB32Point(16, 16, (void**)&ptPixels);
- HBITMAP oImage = (HBITMAP)SelectObject(tempDC, nImage);
-
- GetIconInfo(hBottom, &iciBottom);
- GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
- GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);
-
- GetIconInfo(hTop, &iciTop);
- GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
- GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);
-
- if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32) {
- uint8_t *BottomBuffer, *TopBuffer, *BottomMaskBuffer, *TopMaskBuffer;
- uint8_t *bb, *tb, *bmb, *tmb;
- uint8_t *db = ptPixels;
- int vstep_d = 16 * 4;
- int vstep_b = bmp_bottom.bmWidthBytes;
- int vstep_t = bmp_top.bmWidthBytes;
- int vstep_bm = bmp_bottom_mask.bmWidthBytes;
- int vstep_tm = bmp_top_mask.bmWidthBytes;
- alpha = alpha ? alpha : 255;
- if (bmp_bottom.bmBits) bb = BottomBuffer = (uint8_t*)bmp_bottom.bmBits;
- else {
- BottomBuffer = (uint8_t*)mir_alloc(bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes);
- GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes, BottomBuffer);
- bb = BottomBuffer + vstep_b*(bmp_bottom.bmHeight - 1);
- vstep_b = -vstep_b;
- }
-
- if (bmp_top.bmBits) tb = TopBuffer = (uint8_t*)bmp_top.bmBits;
- else {
- TopBuffer = (uint8_t*)mir_alloc(bmp_top.bmHeight*bmp_top.bmWidthBytes);
- GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight*bmp_top.bmWidthBytes, TopBuffer);
- tb = TopBuffer + vstep_t*(bmp_top.bmHeight - 1);
- vstep_t = -vstep_t;
- }
-
- if (bmp_bottom_mask.bmBits) {
- BottomMaskBuffer = (uint8_t*)bmp_bottom_mask.bmBits;
- bmb = BottomMaskBuffer;
- }
- else {
- BottomMaskBuffer = (uint8_t*)mir_alloc(bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes);
- GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
- bmb = BottomMaskBuffer + vstep_bm*(bmp_bottom_mask.bmHeight - 1);
- vstep_bm = -vstep_bm;
-
- }
-
- if (bmp_top_mask.bmBits) {
- TopMaskBuffer = (uint8_t*)bmp_top_mask.bmBits;
- tmb = TopMaskBuffer;
- }
- else {
- TopMaskBuffer = (uint8_t*)mir_alloc(bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes);
- GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes, TopMaskBuffer);
- tmb = TopMaskBuffer + vstep_tm*(bmp_top_mask.bmHeight - 1);
- vstep_tm = -vstep_tm;
- }
-
- BOOL topHasAlpha = ske_CheckHasAlfaChannel(TopBuffer, bmp_top.bmWidthBytes, bmp_top.bmHeight);
- BOOL bottomHasAlpha = ske_CheckHasAlfaChannel(BottomBuffer, bmp_bottom.bmWidthBytes, bmp_bottom.bmHeight);
- BOOL topHasMask = ske_CheckIconHasMask(TopMaskBuffer);
- BOOL bottomHasMask = ske_CheckIconHasMask(BottomMaskBuffer);
- for (int y = 0; y < 16; y++) {
- for (int x = 0; x < 16; x++) {
- BOOL mask_b = ske_GetMaskBit(bmb, x);
- BOOL mask_t = ske_GetMaskBit(tmb, x);
- uint32_t bottom_d = ((uint32_t*)bb)[x];
- uint32_t top_d = ((uint32_t*)tb)[x];
- if (topHasMask) {
- if (mask_t == 1 && !topHasAlpha) top_d &= 0xFFFFFF;
- else if (!topHasAlpha) top_d |= 0xFF000000;
- }
- if (bottomHasMask) {
- if (mask_b == 1 && !bottomHasAlpha) bottom_d &= 0xFFFFFF;
- else if (!bottomHasAlpha) bottom_d |= 0xFF000000;
- }
- ((uint32_t*)db)[x] = ske_Blend(bottom_d, top_d, alpha);
- }
- bb += vstep_b;
- tb += vstep_t;
- bmb += vstep_bm;
- tmb += vstep_tm;
- db += vstep_d;
- }
-
- if (!bmp_bottom.bmBits) mir_free(BottomBuffer);
- if (!bmp_top.bmBits) mir_free(TopBuffer);
- if (!bmp_bottom_mask.bmBits) mir_free(BottomMaskBuffer);
- if (!bmp_top_mask.bmBits) mir_free(TopMaskBuffer);
- }
- else {
- ske_DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL);
- ske_DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL | (alpha << 24));
- }
-
- DeleteObject(iciBottom.hbmColor);
- DeleteObject(iciTop.hbmColor);
- DeleteObject(iciBottom.hbmMask);
- DeleteObject(iciTop.hbmMask);
-
- SelectObject(tempDC, oImage);
- DeleteDC(tempDC);
-
- uint8_t p[32] = { 0 };
- HBITMAP nMask = CreateBitmap(16, 16, 1, 1, (void*)&p);
- {
- HDC tempDC2 = CreateCompatibleDC(nullptr);
- HDC tempDC3 = CreateCompatibleDC(nullptr);
- HBITMAP hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
- HBITMAP obmp = (HBITMAP)SelectObject(tempDC2, nMask);
- HBITMAP obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
- DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK);
- DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK);
- BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);
- SelectObject(tempDC2, obmp);
- SelectObject(tempDC3, obmp2);
- DeleteObject(hbm);
- DeleteDC(tempDC2);
- DeleteDC(tempDC3);
- }
- iNew.fIcon = TRUE;
- iNew.hbmColor = nImage;
- iNew.hbmMask = nMask;
- HICON res = CreateIconIndirect(&iNew);
- DeleteObject(nImage);
- DeleteObject(nMask);
- return res;
-}
-
-#define NEWJOINEDSTR(destination, first, separator, last) \
- destination = (char*)alloca(mir_strlen(first)+mir_strlen(separator)+mir_strlen(last)+1); \
- if (destination) { \
- *destination = '\0'; \
- mir_strcat(destination,first); \
- mir_strcat(destination,separator); \
- mir_strcat(destination,last); \
- }
-
-#define SKINSETSECTION "SkinnedSettings"
-
-BOOL SkinDBGetContactSetting(MCONTACT hContact, const char *szSection, const char *szKey, DBVARIANT *retdbv, BOOL *bSkinned)
-{
- if (!hContact) { //only for not contact settings
- char *szSkinKey;
- NEWJOINEDSTR(szSkinKey, szSection, "@", szKey);
- if (!db_get(hContact, SKINSETSECTION, szSkinKey, retdbv)) {
- if (bSkinned) *bSkinned = TRUE;
- return FALSE;
- }
- }
- // not skinned
- if (bSkinned) bSkinned = FALSE;
- return db_get(hContact, szSection, szKey, retdbv);
-}
-
-uint8_t SkinDBGetContactSettingByte(MCONTACT hContact, const char *szSection, const char *szKey, uint8_t bDefault)
-{
- DBVARIANT dbv = { 0 };
- BOOL bSkinned = FALSE;
- if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
- if (dbv.type == DBVT_BYTE) {
- uint8_t retVal = dbv.bVal;
- db_free(&dbv);
- return retVal;
- }
- else {
- db_free(&dbv);
- if (!bSkinned)
- return db_get_b(hContact, szSection, szKey, bDefault);
- }
- }
- return bDefault;
-}
-
-uint16_t SkinDBGetContactSettingWord(MCONTACT hContact, const char *szSection, const char *szKey, uint16_t wDefault)
-{
- BOOL bSkinned = FALSE;
- DBVARIANT dbv = { 0 };
- if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
- if (dbv.type == DBVT_WORD) {
- uint16_t retVal = dbv.wVal;
- db_free(&dbv);
- return retVal;
- }
- db_free(&dbv);
- if (!bSkinned)
- return db_get_w(hContact, szSection, szKey, wDefault);
- }
- return wDefault;
-}
-
-uint32_t SkinDBGetContactSettingDword(MCONTACT hContact, const char *szSection, const char *szKey, uint32_t dwDefault)
-{
- DBVARIANT dbv = { 0 };
- BOOL bSkinned = FALSE;
- if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
- if (dbv.type == DBVT_DWORD) {
- uint32_t retVal = dbv.dVal;
- db_free(&dbv);
- return retVal;
- }
- db_free(&dbv);
- if (!bSkinned)
- return db_get_dw(hContact, szSection, szKey, dwDefault);
- }
- return dwDefault;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+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.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//Include
+#include "stdafx.h"
+
+#define _EFFECTENUM_FULL_H
+#include "modern_effectenum.h"
+#undef _EFFECTENUM_FULL_H
+
+#include "modern_sync.h"
+
+//Implementation
+
+#pragma pack(push, 1)
+/* tga header */
+struct tga_header_t
+{
+ uint8_t id_lenght; /* size of image id */
+ uint8_t colormap_type; /* 1 is has a colormap */
+ uint8_t image_type; /* compression type */
+
+ short cm_first_entry; /* colormap origin */
+ short cm_length; /* colormap length */
+ uint8_t cm_size; /* colormap size */
+
+ short x_origin; /* bottom left x coord origin */
+ short y_origin; /* bottom left y coord origin */
+
+ short width; /* picture width (in pixels) */
+ short height; /* picture height (in pixels) */
+
+ uint8_t pixel_depth; /* bits per pixel: 8, 16, 24 or 32 */
+ uint8_t image_descriptor; /* 24 bits = 0x00; 32 bits = 0x80 */
+};
+#pragma pack(pop)
+
+/* Global variables */
+
+SKINOBJECTSLIST g_SkinObjectList = { 0 };
+CURRWNDIMAGEDATA *g_pCachedWindow = nullptr;
+
+bool g_bPostWasCanceled = false;
+bool g_bFullRepaint = false;
+
+int g_mutex_bLockUpdating = 0;
+
+SortedList *gl_plGlyphTexts = nullptr;
+SortedList *gl_plSkinFonts = nullptr;
+
+/* Private module variables */
+
+static HANDLE hSkinLoadedEvent;
+
+static GLYPHIMAGE *pLoadedImages = nullptr;
+static uint32_t dwLoadedImagesCount = 0;
+static uint32_t dwLoadedImagesAlocated = 0;
+
+static BOOL flag_bUpdateQueued = FALSE;
+static BOOL flag_bJustDrawNonFramedObjects = FALSE;
+static BOOL mutex_bLockUpdate = FALSE;
+
+static LIST<EFFECTSSTACKITEM> arEffectStack(10, HandleKeySortT);
+static SKINOBJECTSLIST *pCurrentSkin = nullptr;
+static char **pszSettingName = nullptr;
+static int nArrayLen = 0;
+
+static uint8_t pbGammaWeight[256] = { 0 };
+static uint8_t pbGammaWeightAdv[256] = { 0 };
+static BOOL bGammaWeightFilled = FALSE;
+
+static mir_cs cs_SkinChanging;
+
+static LISTMODERNMASK *MainModernMaskList = nullptr;
+
+/* Private module procedures */
+static BOOL ske_GetMaskBit(uint8_t *line, int x);
+static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM lParam);
+static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM lParam);
+
+static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor);
+static void ske_AddParseTextGlyphObject(char * szGlyphTextID, char * szDefineString, SKINOBJECTSLIST *Skin);
+static void ske_AddParseSkinFont(char * szFontID, char * szDefineString);
+static int ske_GetSkinFromDB(char * szSection, SKINOBJECTSLIST * Skin);
+static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin);
+static int ske_LoadSkinFromResource(BOOL bOnlyObjects);
+static void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult);
+static int ske_ValidateSingleFrameImage(FRAMEWND * Frame, BOOL SkipBkgBlitting);
+static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM lParam);
+static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam);
+static INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM lParam);
+
+static MODERNEFFECT meCurrentEffect = { 0xFF, { 0 }, 0, 0 };
+
+//////////////////////////////////////////////////////////////////////////
+// Ini file parser
+
+IniParser::IniParser(wchar_t * tcsFileName, uint8_t flags) : _Flags(flags)
+{
+ _DoInit();
+ if (!tcsFileName) return;
+
+ if (tcsFileName[0] == '%') {
+ //TODO: Add parser of resource filename here
+ _LoadResourceIni(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF");
+ return;
+ }
+
+ _hFile = _wfopen(tcsFileName, L"r");
+ if (_hFile != nullptr) {
+ _eType = IT_FILE;
+ _isValid = true;
+ }
+}
+
+IniParser::IniParser(HINSTANCE hInst, const char * resourceName, const char * resourceType, uint8_t flags) : _Flags(flags)
+{
+ _DoInit();
+ _LoadResourceIni(hInst, resourceName, resourceType);
+}
+
+IniParser::~IniParser()
+{
+ mir_free(_szSection);
+ if (_hFile) fclose(_hFile);
+ if (_hGlobalRes) {
+ UnlockResource(_hGlobalRes);
+ FreeResource(_hGlobalRes);
+ }
+
+ _szSection = nullptr;
+ _hGlobalRes = nullptr;
+ _hFile = nullptr;
+ _isValid = false;
+ _eType = IT_UNKNOWN;
+}
+
+HRESULT IniParser::Parse(ParserCallback_t pLineCallBackProc, LPARAM SecCheck)
+{
+ if (_isValid && pLineCallBackProc) {
+ _pLineCallBackProc = pLineCallBackProc;
+ _SecCheck = SecCheck;
+ switch (_eType) {
+ case IT_FILE:
+ return _DoParseFile();
+ case IT_RESOURCE:
+ return _DoParseResource();
+ }
+ }
+ return E_FAIL;
+}
+
+HRESULT IniParser::WriteStrToDb(const char * szSection, const char * szName, const char * szValue, IniParser * This)
+{
+ if (This->_SecCheck) {
+ //TODO check security here
+ if (wildcmp(szSection, "Skin_Description_Section"))
+ return S_OK;
+ }
+ if ((This->_Flags == IniParser::FLAG_ONLY_OBJECTS) && !wildcmp(szSection, DEFAULTSKINSECTION))
+ return S_OK; // skip not objects
+
+ switch (szValue[0]) {
+ case 'b':
+ db_set_b(0, szSection, szName, (uint8_t)atoi(szValue + 1));
+ break;
+
+ case 'w':
+ db_set_w(0, szSection, szName, (uint16_t)atoi(szValue + 1));
+ break;
+
+ case 'd':
+ db_set_dw(0, szSection, szName, (uint32_t)atoi(szValue + 1));
+ break;
+
+ case 's':
+ db_set_s(0, szSection, szName, szValue + 1);
+ break;
+ }
+ return S_OK;
+}
+
+int IniParser::GetSkinFolder(IN const wchar_t * szFileName, OUT wchar_t * pszFolderName)
+{
+ wchar_t *szBuff = mir_wstrdup(szFileName);
+ wchar_t *pszPos = szBuff + mir_wstrlen(szBuff);
+ while (pszPos > szBuff && *pszPos != '.') { pszPos--; }
+ *pszPos = '\0';
+ mir_wstrcpy(pszFolderName, szBuff);
+
+ wchar_t custom_folder[MAX_PATH], cus[MAX_PATH];
+ wchar_t *b3;
+ mir_wstrncpy(custom_folder, pszFolderName, _countof(custom_folder));
+ b3 = custom_folder + mir_wstrlen(custom_folder);
+ while (b3 > custom_folder && *b3 != '\\') { b3--; }
+ *b3 = '\0';
+
+ GetPrivateProfileString(L"Skin_Description_Section", L"SkinFolder", L"", cus, _countof(custom_folder), szFileName);
+ if (cus[0] != 0)
+ mir_snwprintf(pszFolderName, MAX_PATH, L"%s\\%s", custom_folder, cus);
+
+ mir_free(szBuff);
+ PathToRelativeW(pszFolderName, pszFolderName);
+ return 0;
+}
+
+void IniParser::_DoInit()
+{
+ _isValid = false;
+ _eType = IT_UNKNOWN;
+ _szSection = nullptr;
+ _hFile = nullptr;
+ _hGlobalRes = nullptr;
+ _dwSizeOfRes = 0;
+ _pPosition = nullptr;
+ _pLineCallBackProc = nullptr;
+ _SecCheck = 0;
+}
+
+void IniParser::_LoadResourceIni(HINSTANCE hInst, const char * resourceName, const char * resourceType)
+{
+ if (_eType != IT_UNKNOWN)
+ return;
+
+ HRSRC hRSrc = FindResourceA(hInst, resourceName, resourceType);
+ if (!hRSrc)
+ return;
+
+ _hGlobalRes = LoadResource(hInst, hRSrc);
+ if (!_hGlobalRes)
+ return;
+
+ _dwSizeOfRes = SizeofResource(hInst, hRSrc);
+ _pPosition = (char*)LockResource(_hGlobalRes);
+
+ _isValid = true;
+ _eType = IT_RESOURCE;
+}
+
+HRESULT IniParser::_DoParseFile()
+{
+ char szLine[MAX_LINE_LEN];
+ _nLine = 0;
+ while (fgets(szLine, _countof(szLine), _hFile) != nullptr) {
+ size_t len = 0;
+ char *pLine = (char *)_RemoveTailings(szLine, len);
+ if (len > 0) {
+ pLine[len] = '\0';
+ if (!_DoParseLine(pLine))
+ return E_FAIL;
+ }
+ else _nLine++;
+ };
+
+ return S_OK;
+}
+
+HRESULT IniParser::_DoParseResource()
+{
+ _nLine = 0;
+ char szLine[MAX_LINE_LEN];
+ char *pos = (char *)_pPosition;
+
+ while (pos < _pPosition + _dwSizeOfRes) {
+ int i = 0;
+ while (pos < _pPosition + _dwSizeOfRes && *pos != '\n' && *pos != '\0' && i < MAX_LINE_LEN - 1) {
+ if ((*pos) != '\r')
+ szLine[i++] = *pos;
+ pos++;
+ }
+ szLine[i] = '\0';
+ pos++;
+
+ size_t len = 0;
+ char *pLine = (char *)_RemoveTailings(szLine, len);
+ if (len > 0) {
+ pLine[len] = '\0';
+ if (!_DoParseLine(pLine))
+ return E_FAIL;
+ }
+ else _nLine++;
+ }
+ return S_OK;
+}
+
+const char *IniParser::_RemoveTailings(const char *szLine, size_t &len)
+{
+ const char *pStart = szLine;
+ while (*pStart == ' ' || *pStart == '\t')
+ pStart++; //skip spaces at begin
+ const char *pEnd = pStart + mir_strlen(pStart);
+ while (pEnd > pStart && (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n' || *pEnd == '\r'))
+ pEnd--;
+
+ len = pEnd - pStart;
+ return pStart;
+}
+
+BOOL IniParser::_DoParseLine(char *szLine)
+{
+ _nLine++;
+ size_t len = mir_strlen(szLine);
+ if (len == 0)
+ return TRUE;
+
+ switch (szLine[0]) {
+ case ';':
+ return TRUE; // start of comment is found
+
+ case '[':
+ //New section start here
+ mir_free(_szSection);
+ _szSection = nullptr;
+ {
+ char *tbuf = szLine + 1; // skip [
+
+ char *ebuf = tbuf;
+
+ while (*ebuf != ']' && *ebuf != '\0') ebuf++;
+ if (*ebuf == '\0')
+ return FALSE; // no close bracket
+
+ uint32_t sectionLen = ebuf - tbuf + 1;
+ _szSection = (char*)mir_alloc(sectionLen + 1);
+ mir_strncpy(_szSection, tbuf, sectionLen);
+ _szSection[sectionLen] = '\0';
+ }
+ return TRUE;
+
+ default:
+ if (!_szSection)
+ return TRUE; //param found out of section
+
+ char *keyName = szLine;
+ char *keyValue = szLine;
+
+ size_t eqPlace = 0, len2 = mir_strlen(keyName);
+ while (eqPlace < len2 && keyName[eqPlace] != '=')
+ eqPlace++; //find '='
+
+ if (eqPlace == 0 || eqPlace == len2)
+ return TRUE; // = not found or no key name //say false
+
+ keyName[eqPlace] = '\0';
+
+ keyValue = keyName + eqPlace + 1;
+
+ //remove tail spaces in Name
+ rtrim(keyName);
+ while (*keyValue) {
+ if (!isspace(*keyValue))
+ break;
+ keyValue++;
+ }
+ rtrim(keyValue);
+ _pLineCallBackProc(_szSection, keyName, keyValue, this);
+ }
+ return TRUE;
+}
+//////////////////////////////////////////////////////////////////////////
+// End of IniParser
+//////////////////////////////////////////////////////////////////////////
+
+HRESULT SkinEngineLoadModule()
+{
+ ModernSkinButtonLoadModule();
+ MainModernMaskList = (LISTMODERNMASK*)mir_calloc(sizeof(LISTMODERNMASK));
+ //init variables
+ g_SkinObjectList.dwObjLPAlocated = 0;
+ g_SkinObjectList.dwObjLPReserved = 0;
+ g_SkinObjectList.pObjects = nullptr;
+ // Initialize GDI+
+ InitGdiPlus();
+ AniAva_InitModule();
+ //create services
+ CreateServiceFunction(MS_SKIN_DRAWGLYPH, ske_Service_DrawGlyph);
+ CreateServiceFunction(MS_SKINENG_UPTATEFRAMEIMAGE, ske_Service_UpdateFrameImage);
+ CreateServiceFunction(MS_SKINENG_INVALIDATEFRAMEIMAGE, ske_Service_InvalidateFrameImage);
+ CreateServiceFunction(MS_SKINENG_ALPHATEXTOUT, ske_Service_AlphaTextOut);
+ CreateServiceFunction(MS_SKINENG_DRAWICONEXFIX, ske_Service_DrawIconEx);
+
+ CreateServiceFunction(MS_DRAW_TEXT_WITH_EFFECT, ske_Service_DrawTextWithEffect);
+
+ //create event handle
+ hSkinLoadedEvent = HookEvent(ME_SKIN_SERVICESCREATED, CLUI_OnSkinLoad);
+ NotifyEventHooks(g_CluiData.hEventSkinServicesCreated, 0, 0);
+ return S_OK;
+}
+
+int SkinEngineUnloadModule()
+{
+ //unload services
+ ModernSkinButtonUnloadModule(0, 0);
+ ske_UnloadSkin(&g_SkinObjectList);
+
+ mir_free_and_nil(g_SkinObjectList.pObjects);
+ mir_free_and_nil(g_SkinObjectList.pMaskList);
+ mir_free_and_nil(MainModernMaskList);
+
+ for (auto &it : arEffectStack)
+ mir_free(it);
+ arEffectStack.destroy();
+
+ if (g_pCachedWindow) {
+ SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld);
+ SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ DeleteObject(g_pCachedWindow->hImageDIB);
+ DeleteDC(g_pCachedWindow->hBackDC);
+ DeleteDC(g_pCachedWindow->hImageDC);
+ ReleaseDC(nullptr, g_pCachedWindow->hScreenDC);
+ mir_free_and_nil(g_pCachedWindow);
+ }
+ GdiFlush();
+ DestroyHookableEvent(g_CluiData.hEventSkinServicesCreated);
+ AniAva_UnloadModule();
+ ShutdownGdiPlus();
+ //free variables
+ return 1;
+}
+
+BOOL ske_AlphaBlend(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction)
+{
+ if (g_CluiData.fDisableSkinEngine && !(blendFunction.BlendFlags & 128)) {
+ if (nWidthDest != nWidthSrc || nHeightDest != nHeightSrc)
+ return StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, SRCCOPY);
+ return BitBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
+ }
+
+ if (blendFunction.BlendFlags & 128) // Use gdi+ engine
+ return GDIPlus_AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
+ hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
+ &blendFunction);
+
+ blendFunction.BlendFlags &= ~128;
+ return AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction);
+}
+
+struct DCBUFFER
+{
+ HDC hdcOwnedBy;
+ int nUsageID;
+ int width;
+ int height;
+ void *pImage;
+ HDC hDC;
+ HBITMAP oldBitmap;
+ HBITMAP hBitmap;
+ uint32_t dwDestroyAfterTime;
+};
+
+static int SortBufferList(const DCBUFFER *buf1, const DCBUFFER *buf2)
+{
+ if (buf1->hdcOwnedBy != buf2->hdcOwnedBy) return (int)(buf1->hdcOwnedBy < buf2->hdcOwnedBy);
+ else if (buf1->nUsageID != buf2->nUsageID) return (int)(buf1->nUsageID < buf2->nUsageID);
+ else return (int)(buf1->hDC < buf2->hDC);
+}
+
+LIST<DCBUFFER> BufferList(2, SortBufferList);
+mir_cs BufferListCS;
+
+enum
+{
+ BUFFER_DRAWICON = 0,
+ BUFFER_DRAWIMAGE
+};
+
+HDC ske_RequestBufferDC(HDC hdcOwner, int dcID, int width, int height, BOOL fClear)
+{
+ mir_cslock lck(BufferListCS);
+ //Try to find DC in buffer list
+ DCBUFFER buf;
+ buf.hdcOwnedBy = hdcOwner;
+ buf.nUsageID = dcID;
+ buf.hDC = nullptr;
+ DCBUFFER *pBuf = BufferList.find(&buf);
+ if (!pBuf) {
+ //if not found - allocate it
+ pBuf = (DCBUFFER *)mir_alloc(sizeof(DCBUFFER));
+ *pBuf = buf;
+ pBuf->width = width;
+ pBuf->height = height;
+ pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage));
+ pBuf->hDC = CreateCompatibleDC(hdcOwner);
+ pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap);
+ pBuf->dwDestroyAfterTime = 0;
+ BufferList.insert(pBuf);
+ }
+ else {
+ if (pBuf->width != width || pBuf->height != height) {
+ //resize
+ SelectObject(pBuf->hDC, pBuf->oldBitmap);
+ DeleteObject(pBuf->hBitmap);
+ pBuf->width = width;
+ pBuf->height = height;
+ pBuf->hBitmap = ske_CreateDIB32Point(width, height, &(pBuf->pImage));
+ pBuf->oldBitmap = (HBITMAP)SelectObject(pBuf->hDC, pBuf->hBitmap);
+ }
+ else if (fClear)
+ memset(pBuf->pImage, 0, width*height*sizeof(uint32_t));
+ }
+ pBuf->dwDestroyAfterTime = 0;
+ return pBuf->hDC;
+}
+
+int ske_ReleaseBufferDC(HDC hDC, int keepTime)
+{
+ uint32_t dwCurrentTime = GetTickCount();
+
+ // Try to find DC in buffer list - set flag to be release after time;
+ mir_cslock lck(BufferListCS);
+ for (auto &it : BufferList.rev_iter()) {
+ if (it) {
+ if (hDC != nullptr && it->hDC == hDC) {
+ it->dwDestroyAfterTime = dwCurrentTime + keepTime;
+ break;
+ }
+
+ if ((it->dwDestroyAfterTime && it->dwDestroyAfterTime < dwCurrentTime) || keepTime == -1) {
+ SelectObject(it->hDC, it->oldBitmap);
+ DeleteObject(it->hBitmap);
+ DeleteDC(it->hDC);
+ mir_free(BufferList.removeItem(&it));
+ }
+ }
+ }
+ return 0;
+}
+
+BOOL ske_SetRgnOpaque(HDC memdc, HRGN hrgn, BOOL force)
+{
+ if (g_CluiData.fDisableSkinEngine && !force) return TRUE;
+ uint32_t rgnsz = GetRegionData(hrgn, 0, nullptr);
+ RGNDATA *rdata = (RGNDATA *)mir_alloc(rgnsz);
+ GetRegionData(hrgn, rgnsz, rdata);
+ RECT *rect = (RECT *)rdata->Buffer;
+ for (uint32_t d = 0; d < rdata->rdh.nCount; d++) {
+ ske_SetRectOpaque(memdc, &rect[d], force);
+ }
+ mir_free(rdata);
+ return TRUE;
+}
+
+
+BOOL ske_SetRectOpaque(HDC memdc, RECT *fr, BOOL force)
+{
+ int f = 0;
+ uint8_t *bits;
+ BITMAP bmp;
+
+ if (g_CluiData.fDisableSkinEngine && !force)
+ return TRUE;
+
+ HBITMAP hbmp = (HBITMAP)GetCurrentObject(memdc, OBJ_BITMAP);
+ GetObject(hbmp, sizeof(bmp), &bmp);
+
+ if (bmp.bmPlanes != 1)
+ return FALSE;
+
+ if (!bmp.bmBits) {
+ f = 1;
+ bits = (uint8_t*)mir_alloc(bmp.bmWidthBytes*bmp.bmHeight);
+ GetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits);
+ }
+ else
+ bits = (uint8_t*)bmp.bmBits;
+
+ int sx = (fr->left > 0) ? fr->left : 0;
+ int sy = (fr->top > 0) ? fr->top : 0;
+ int ex = (fr->right < bmp.bmWidth) ? fr->right : bmp.bmWidth;
+ int ey = (fr->bottom < bmp.bmHeight) ? fr->bottom : bmp.bmHeight;
+
+ int width = ex - sx;
+
+ uint8_t *pLine = ((uint8_t *)bits) + (bmp.bmHeight - sy - 1) * bmp.bmWidthBytes + (sx << 2) + 3;
+ for (int y = 0; y < (ey - sy); y++) {
+ uint8_t *pColumn = pLine;
+ for (int x = 0; x < width; x++) {
+ *pColumn = 255;
+ pColumn += 4;
+ }
+ pLine -= bmp.bmWidthBytes;
+ }
+ if (f) {
+ SetBitmapBits(hbmp, bmp.bmWidthBytes*bmp.bmHeight, bits);
+ mir_free(bits);
+ }
+ // DeleteObject(hbmp);
+ return 1;
+}
+
+static BOOL ske_SkinFillRectByGlyph(HDC hDest, HDC hSource, RECT *rFill, RECT *rGlyph, RECT *rClip, uint8_t mode, uint8_t drawMode)
+{
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+
+ //initializations
+ if (mode == FM_STRETCH) {
+ HDC mem2dc = nullptr;
+ HBITMAP mem2bmp = nullptr, oldbmp = nullptr;
+ RECT wr;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ if (drawMode != 2) {
+ mem2dc = CreateCompatibleDC(hDest);
+ mem2bmp = ske_CreateDIB32(wr.right - wr.left, wr.bottom - wr.top);
+ oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+ }
+
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top,
+ hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
+ ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf);
+ }
+ else {
+ ske_AlphaBlend(hDest, rFill->left, rFill->top, rFill->right - rFill->left, rFill->bottom - rFill->top,
+ hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
+
+ }
+ }
+ else {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(mem2dc, rFill->left - wr.left, rFill->top - wr.top, rFill->right - rFill->left, rFill->bottom - rFill->top,
+ hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, rGlyph->bottom - rGlyph->top, bf);
+ ske_AlphaBlend(hDest, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, mem2dc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, bf);
+ }
+ if (drawMode != 2) {
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ return 1;
+ }
+ else if (mode == FM_TILE_VERT && (rGlyph->bottom - rGlyph->top > 0) && (rGlyph->right - rGlyph->left > 0)) {
+ RECT wr;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ HDC mem2dc = CreateCompatibleDC(hDest);
+ //SetStretchBltMode(mem2dc, HALFTONE);
+ HBITMAP mem2bmp = ske_CreateDIB32(wr.right - wr.left, rGlyph->bottom - rGlyph->top);
+ HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+ if (!oldbmp)
+ return 0;
+
+ /// draw here
+ {
+ int y = 0;
+ int w = rFill->right - rFill->left;
+ int h = rGlyph->bottom - rGlyph->top;
+ if (h > 0 && (wr.bottom - wr.top)*(wr.right - wr.left) != 0) {
+ w = wr.right - wr.left;
+ {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(mem2dc, -(wr.left - rFill->left), 0, rFill->right - rFill->left, h, hSource, rGlyph->left, rGlyph->top, rGlyph->right - rGlyph->left, h, bf);
+ //StretchBlt(mem2dc,-(wr.left-rFill->left), 0, rFill->right-rFill->left,h,hSource,rGlyph->left,rGlyph->top,rGlyph->right-rGlyph->left,h,SRCCOPY);
+ }
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ int dy = (wr.top - rFill->top) % h;
+ if (dy >= 0) {
+ y = wr.top;
+ int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top);
+ BitBlt(hDest, wr.left, y, w, ht, mem2dc, 0, dy, SRCCOPY);
+ }
+
+ y = wr.top + h - dy;
+ while (y < wr.bottom - h) {
+ BitBlt(hDest, wr.left, y, w, h, mem2dc, 0, 0, SRCCOPY);
+ y += h;
+ }
+ if (y <= wr.bottom)
+ BitBlt(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, SRCCOPY);
+
+ }
+ else {
+ y = wr.top;
+ while (y < wr.bottom - h) {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf);
+ y += h;
+ }
+ if (y <= wr.bottom) {
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf);
+ }
+ }
+
+ }
+ else {
+ BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ int dy = (wr.top - rFill->top) % h;
+ if (dy >= 0) {
+ y = wr.top;
+ int ht = (y + h - dy <= wr.bottom) ? (h - dy) : (wr.bottom - wr.top);
+ ske_AlphaBlend(hDest, wr.left, y, w, ht, mem2dc, 0, dy, w, ht, bf2);
+ }
+
+ y = wr.top + h - dy;
+ while (y < wr.bottom - h) {
+ ske_AlphaBlend(hDest, wr.left, y, w, h, mem2dc, 0, 0, w, h, bf2);
+ y += h;
+ }
+ if (y <= wr.bottom)
+ ske_AlphaBlend(hDest, wr.left, y, w, wr.bottom - y, mem2dc, 0, 0, w, wr.bottom - y, bf2);
+ }
+ }
+ }
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ else if (mode == FM_TILE_HORZ && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0) && (rFill->bottom - rFill->top) > 0 && (rFill->right - rFill->left) > 0) {
+ RECT wr;
+ int w = rGlyph->right - rGlyph->left;
+ int h = rFill->bottom - rFill->top;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ h = wr.bottom - wr.top;
+ HDC mem2dc = CreateCompatibleDC(hDest);
+
+ HBITMAP mem2bmp = ske_CreateDIB32(w, h);
+ HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+
+ if (!oldbmp)
+ return 0;
+ /// draw here
+ {
+ int x = 0;
+ {
+ //SetStretchBltMode(mem2dc, HALFTONE);
+ //StretchBlt(mem2dc, 0, 0, w,h,hSource,rGlyph->left+(wr.left-rFill->left),rGlyph->top,w,h,SRCCOPY);
+
+ // BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, 0 };
+ ske_AlphaBlend(mem2dc, 0, -(wr.top - rFill->top), w, rFill->bottom - rFill->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf);
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY);
+ x += w;
+ }
+ if (x <= wr.right)
+ BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY);
+ }
+ else {
+ int dx = (wr.left - rFill->left) % w;
+ x = wr.left - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf);
+ }
+
+ }
+ else {
+ BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2);
+ }
+ }
+ }
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ else if (mode == FM_TILE_BOTH && (rGlyph->right - rGlyph->left > 0) && (rGlyph->bottom - rGlyph->top > 0)) {
+ int w = rGlyph->right - rGlyph->left;
+ int x = 0;
+ int h = rFill->bottom - rFill->top;
+ RECT wr;
+ IntersectRect(&wr, rClip, rFill);
+ if ((wr.bottom - wr.top)*(wr.right - wr.left) == 0) return 0;
+ HDC mem2dc = CreateCompatibleDC(hDest);
+ HBITMAP mem2bmp = ske_CreateDIB32(w, wr.bottom - wr.top);
+ h = wr.bottom - wr.top;
+ HBITMAP oldbmp = (HBITMAP)SelectObject(mem2dc, mem2bmp);
+#ifdef _DEBUG
+ if (!oldbmp)
+ (nullptr, "Tile bitmap not selected", "ERROR", MB_OK);
+#endif
+ /// draw here
+ {
+ //fill temp bitmap
+ {
+ int dy = (wr.top - rFill->top) % (rGlyph->bottom - rGlyph->top);
+ int y = -dy;
+ while (y < wr.bottom - wr.top) {
+
+ ske_AlphaBlend(mem2dc, 0, y, w, rGlyph->bottom - rGlyph->top, hSource, rGlyph->left, rGlyph->top, w, rGlyph->bottom - rGlyph->top, bf);
+ y += rGlyph->bottom - rGlyph->top;
+ }
+
+ //--
+ //end temp bitmap
+ if (drawMode == 0 || drawMode == 2) {
+ if (drawMode == 0) {
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ BitBlt(hDest, x, wr.top, wt, h, mem2dc, dx, 0, SRCCOPY);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ BitBlt(hDest, x, wr.top, w, h, mem2dc, 0, 0, SRCCOPY);
+ x += w;
+ }
+ if (x <= wr.right)
+ BitBlt(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, SRCCOPY);
+ }
+ else {
+ int dx = (wr.left - rFill->left) % w;
+ x = wr.left - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf);
+ }
+
+ }
+ else {
+ BLENDFUNCTION bf2 = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ int dx = (wr.left - rFill->left) % w;
+ if (dx >= 0) {
+ x = wr.left;
+ int wt = (x + w - dx <= wr.right) ? (w - dx) : (wr.right - wr.left);
+ ske_AlphaBlend(hDest, x, wr.top, wt, h, mem2dc, dx, 0, wt, h, bf2);
+ }
+ x = wr.left + w - dx;
+ while (x < wr.right - w) {
+ ske_AlphaBlend(hDest, x, wr.top, w, h, mem2dc, 0, 0, w, h, bf2);
+ x += w;
+ }
+ if (x <= wr.right)
+ ske_AlphaBlend(hDest, x, wr.top, wr.right - x, h, mem2dc, 0, 0, wr.right - x, h, bf2);
+ }
+ }
+ }
+ SelectObject(mem2dc, oldbmp);
+ DeleteObject(mem2bmp);
+ DeleteDC(mem2dc);
+ }
+ return 1;
+
+}
+
+HBITMAP ske_CreateDIB32(int cx, int cy)
+{
+ return ske_CreateDIB32Point(cx, cy, nullptr);
+}
+
+HBITMAP ske_CreateDIB32Point(int cx, int cy, void **bits)
+{
+ if (cx < 0 || cy < 0)
+ return nullptr;
+
+ BITMAPINFO RGB32BitsBITMAPINFO = { 0 };
+ RGB32BitsBITMAPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ RGB32BitsBITMAPINFO.bmiHeader.biWidth = cx;//bm.bmWidth;
+ RGB32BitsBITMAPINFO.bmiHeader.biHeight = cy;//bm.bmHeight;
+ RGB32BitsBITMAPINFO.bmiHeader.biPlanes = 1;
+ RGB32BitsBITMAPINFO.bmiHeader.biBitCount = 32;
+ // pointer used for direct Bitmap pixels access
+
+ UINT *ptPixels;
+ HBITMAP DirectBitmap = CreateDIBSection(nullptr,
+ &RGB32BitsBITMAPINFO,
+ DIB_RGB_COLORS,
+ (void **)&ptPixels,
+ nullptr, 0);
+ if ((DirectBitmap == nullptr || ptPixels == nullptr) && cx != 0 && cy != 0) {
+#ifdef _DEBUG
+ MessageBoxA(nullptr, "Object not allocated. Check GDI object count", "ERROR", MB_OK | MB_ICONERROR);
+ DebugBreak();
+#endif
+ ;
+ }
+ else memset(ptPixels, 0, cx*cy * 4);
+ if (bits != nullptr) *bits = ptPixels;
+ return DirectBitmap;
+}
+
+HRGN ske_CreateOpaqueRgn(uint8_t Level, bool Opaque)
+{
+ if (!g_pCachedWindow)
+ return nullptr;
+
+ RGBQUAD *buf = (RGBQUAD *)g_pCachedWindow->hImageDIBByte;
+ if (buf == nullptr)
+ return nullptr;
+
+ unsigned int cRect = 64;
+ PRGNDATA pRgnData = (PRGNDATA)mir_alloc(sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
+ memset(pRgnData, 0, sizeof(RGNDATAHEADER));
+ pRgnData->rdh.dwSize = sizeof(RGNDATAHEADER);
+ pRgnData->rdh.iType = RDH_RECTANGLES;
+
+ for (int y = 0; y < g_pCachedWindow->Height; ++y) {
+ bool inside = false;
+ bool lastin = false;
+ unsigned int entry = 0;
+
+ for (int x = 0; x < g_pCachedWindow->Width; ++x) {
+ inside = Opaque ? (buf->rgbReserved > Level) : (buf->rgbReserved < Level);
+ ++buf;
+
+ if (inside != lastin) {
+ if (inside) {
+ lastin = true;
+ entry = x;
+ }
+ else {
+ if (pRgnData->rdh.nCount == cRect) {
+ cRect = cRect + 64;
+ pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
+ }
+ SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, x, g_pCachedWindow->Height - y + 1);
+
+ pRgnData->rdh.nCount++;
+ lastin = false;
+ }
+ }
+ }
+
+ if (lastin) {
+ if (pRgnData->rdh.nCount == cRect) {
+ cRect = cRect + 64;
+ pRgnData = (PRGNDATA)mir_realloc(pRgnData, sizeof(RGNDATAHEADER) + (cRect)*sizeof(RECT));
+ }
+ SetRect(((LPRECT)pRgnData->Buffer) + pRgnData->rdh.nCount, entry, g_pCachedWindow->Height - y, g_pCachedWindow->Width, g_pCachedWindow->Height - y + 1);
+
+ pRgnData->rdh.nCount++;
+ }
+ }
+
+ HRGN hRgn = ExtCreateRegion(nullptr, sizeof(RGNDATAHEADER) + pRgnData->rdh.nCount*sizeof(RECT), (LPRGNDATA)pRgnData);
+ mir_free(pRgnData);
+ return hRgn;
+}
+
+static int ske_DrawSkinObject(SKINDRAWREQUEST *preq, GLYPHOBJECT *pobj)
+{
+ HDC memdc = nullptr, glyphdc = nullptr;
+ int k = 0;
+ //BITMAP bmp = {0};
+ HBITMAP membmp = nullptr, oldbmp = nullptr, oldglyph = nullptr;
+ uint8_t Is32Bit = 0;
+ RECT PRect;
+ POINT mode2offset = { 0 };
+ int depth = 0;
+ int mode = 0; //0-FastDraw, 1-DirectAlphaDraw, 2-BufferedAlphaDraw
+
+ if (!(preq && pobj)) return -1;
+ if ((!pobj->hGlyph || pobj->hGlyph == (HBITMAP)-1) && ((pobj->Style & 7) == ST_IMAGE || (pobj->Style & 7) == ST_FRAGMENT || (pobj->Style & 7) == ST_SOLARIZE)) return 0;
+ // Determine painting mode
+ depth = GetDeviceCaps(preq->hDC, BITSPIXEL);
+ depth = depth < 16 ? 16 : depth;
+ Is32Bit = pobj->bmBitsPixel == 32;
+ if ((!Is32Bit && pobj->dwAlpha == 255) && pobj->Style != ST_BRUSH) mode = 0;
+ else if (pobj->dwAlpha == 255 && pobj->Style != ST_BRUSH) mode = 1;
+ else mode = 2;
+ // End painting mode
+
+ //force mode
+
+ if (preq->rcClipRect.bottom - preq->rcClipRect.top*preq->rcClipRect.right - preq->rcClipRect.left == 0)
+ preq->rcClipRect = preq->rcDestRect;
+ IntersectRect(&PRect, &preq->rcDestRect, &preq->rcClipRect);
+ if (IsRectEmpty(&PRect)) {
+ return 0;
+ }
+ if (mode == 2) {
+ memdc = CreateCompatibleDC(preq->hDC);
+ membmp = ske_CreateDIB32(PRect.right - PRect.left, PRect.bottom - PRect.top);
+ oldbmp = (HBITMAP)SelectObject(memdc, membmp);
+ if (oldbmp == nullptr) {
+ SelectObject(memdc, oldbmp);
+ DeleteDC(memdc);
+ DeleteObject(membmp);
+ return 0;
+ }
+ }
+
+ if (mode != 2) memdc = preq->hDC;
+ {
+ if (pobj->hGlyph && pobj->hGlyph != (HBITMAP)-1) {
+ glyphdc = CreateCompatibleDC(preq->hDC);
+ oldglyph = (HBITMAP)SelectObject(glyphdc, pobj->hGlyph);
+ }
+ // Drawing
+ {
+ RECT rFill, rGlyph, rClip;
+ if ((pobj->Style & 7) == ST_BRUSH) {
+ HBRUSH br = CreateSolidBrush(pobj->dwColor);
+ RECT fr;
+ if (mode == 2) {
+ SetRect(&fr, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top);
+ FillRect(memdc, &fr, br);
+ ske_SetRectOpaque(memdc, &fr);
+ // FillRectAlpha(memdc,&fr,pobj->dwColor|0xFF000000);
+ }
+ else {
+ fr = PRect;
+ // SetRect(&fr, 0, 0, PRect.right-PRect.left,PRect.bottom-PRect.top);
+ FillRect(preq->hDC, &fr, br);
+ }
+ DeleteObject(br);
+ k = -1;
+ }
+ else {
+ if (mode == 2) {
+ mode2offset.x = PRect.left;
+ mode2offset.y = PRect.top;
+ OffsetRect(&PRect, -mode2offset.x, -mode2offset.y);
+ }
+ rClip = (preq->rcClipRect);
+
+ {
+ int lft = 0;
+ int top = 0;
+ int rgh = pobj->bmWidth;
+ int btm = pobj->bmHeight;
+ if ((pobj->Style & 7) == ST_FRAGMENT) {
+ lft = pobj->clipArea.x;
+ top = pobj->clipArea.y;
+ rgh = min(rgh, lft + pobj->szclipArea.cx);
+ btm = min(btm, top + pobj->szclipArea.cy);
+ }
+
+ // Draw center...
+ if (1) {
+ rFill.top = preq->rcDestRect.top + pobj->dwTop;
+ rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.left = preq->rcDestRect.left + pobj->dwLeft;
+ rFill.right = preq->rcDestRect.right - pobj->dwRight;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+ rGlyph.top = top + pobj->dwTop;
+ rGlyph.left = lft + pobj->dwLeft;
+ rGlyph.right = rgh - pobj->dwRight;
+ rGlyph.bottom = btm - pobj->dwBottom;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode, mode);
+ }
+
+ // Draw top side...
+ if (1) {
+ rFill.top = preq->rcDestRect.top;
+ rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
+ rFill.left = preq->rcDestRect.left + pobj->dwLeft;
+ rFill.right = preq->rcDestRect.right - pobj->dwRight;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+ rGlyph.top = top + 0;
+ rGlyph.left = lft + pobj->dwLeft;
+ rGlyph.right = rgh - pobj->dwRight;
+ rGlyph.bottom = top + pobj->dwTop;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode);
+ }
+ // Draw bottom side...
+ if (1) {
+ rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.bottom = preq->rcDestRect.bottom;
+ rFill.left = preq->rcDestRect.left + pobj->dwLeft;
+ rFill.right = preq->rcDestRect.right - pobj->dwRight;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = btm - pobj->dwBottom;
+ rGlyph.left = lft + pobj->dwLeft;
+ rGlyph.right = rgh - pobj->dwRight;
+ rGlyph.bottom = btm;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_HORZ, mode);
+ }
+ // Draw left side...
+ if (1) {
+ rFill.top = preq->rcDestRect.top + pobj->dwTop;
+ rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.left = preq->rcDestRect.left;
+ rFill.right = preq->rcDestRect.left + pobj->dwLeft;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top + pobj->dwTop;
+ rGlyph.left = lft;
+ rGlyph.right = lft + pobj->dwLeft;
+ rGlyph.bottom = btm - pobj->dwBottom;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode);
+ }
+
+ // Draw right side...
+ if (1) {
+ rFill.top = preq->rcDestRect.top + pobj->dwTop;
+ rFill.bottom = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.left = preq->rcDestRect.right - pobj->dwRight;
+ rFill.right = preq->rcDestRect.right;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top + pobj->dwTop;
+ rGlyph.left = rgh - pobj->dwRight;
+ rGlyph.right = rgh;
+ rGlyph.bottom = btm - pobj->dwBottom;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, pobj->FitMode & FM_TILE_VERT, mode);
+ }
+
+
+ // Draw Top-Left corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.top;
+ rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
+ rFill.left = preq->rcDestRect.left;
+ rFill.right = preq->rcDestRect.left + pobj->dwLeft;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top;
+ rGlyph.left = lft;
+ rGlyph.right = lft + pobj->dwLeft;
+ rGlyph.bottom = top + pobj->dwTop;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+ // Draw Top-Right corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.top;
+ rFill.bottom = preq->rcDestRect.top + pobj->dwTop;
+ rFill.left = preq->rcDestRect.right - pobj->dwRight;
+ rFill.right = preq->rcDestRect.right;
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.top = top;
+ rGlyph.left = rgh - pobj->dwRight;
+ rGlyph.right = rgh;
+ rGlyph.bottom = top + pobj->dwTop;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+
+ // Draw Bottom-Left corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.bottom = preq->rcDestRect.bottom;
+ rFill.left = preq->rcDestRect.left;
+ rFill.right = preq->rcDestRect.left + pobj->dwLeft;
+
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+
+ rGlyph.left = lft;
+ rGlyph.right = lft + pobj->dwLeft;
+ rGlyph.top = btm - pobj->dwBottom;
+ rGlyph.bottom = btm;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+ // Draw Bottom-Right corner...
+ if (1) {
+ rFill.top = preq->rcDestRect.bottom - pobj->dwBottom;
+ rFill.bottom = preq->rcDestRect.bottom;
+ rFill.left = preq->rcDestRect.right - pobj->dwRight;
+ rFill.right = preq->rcDestRect.right;
+
+
+ if (mode == 2)
+ OffsetRect(&rFill, -mode2offset.x, -mode2offset.y);
+
+ rGlyph.left = rgh - pobj->dwRight;
+ rGlyph.right = rgh;
+ rGlyph.top = btm - pobj->dwBottom;
+ rGlyph.bottom = btm;
+
+ k += ske_SkinFillRectByGlyph(memdc, glyphdc, &rFill, &rGlyph, &PRect, 0, mode);
+ }
+ }
+
+ }
+
+ if ((k > 0 || k == -1) && mode == 2) {
+ {
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, pobj->dwAlpha, uint8_t(pobj->bmBitsPixel == 32 && pobj->Style != ST_BRUSH ? AC_SRC_ALPHA : 0) };
+ OffsetRect(&PRect, mode2offset.x, mode2offset.y);
+ ske_AlphaBlend(preq->hDC, PRect.left, PRect.top, PRect.right - PRect.left, PRect.bottom - PRect.top,
+ memdc, 0, 0, PRect.right - PRect.left, PRect.bottom - PRect.top, bf);
+ }
+ }
+ }
+ //free GDI resources
+ //--++--
+
+ //free GDI resources
+ {
+
+ if (oldglyph) SelectObject(glyphdc, oldglyph);
+ if (glyphdc) DeleteDC(glyphdc);
+ }
+ if (mode == 2) {
+ SelectObject(memdc, oldbmp);
+ DeleteDC(memdc);
+ DeleteObject(membmp);
+ }
+
+ }
+ if (pobj->plTextList && pobj->plTextList->realCount > 0) {
+ HFONT hOldFont;
+ for (int i = 0; i < pobj->plTextList->realCount; i++) {
+ GLYPHTEXT *gt = (GLYPHTEXT *)pobj->plTextList->items[i];
+ if (!gt->hFont) {
+ if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) {
+ int j = 0;
+ for (j = 0; j < gl_plSkinFonts->realCount; j++) {
+ SKINFONT *sf = (SKINFONT*)gl_plSkinFonts->items[j];
+ if (sf->szFontID && !mir_strcmp(sf->szFontID, gt->szFontID)) {
+ gt->hFont = sf->hFont;
+ break;
+ }
+ }
+ }
+ if (!gt->hFont) gt->hFont = (HFONT)-1;
+ }
+ if (gt->hFont != (HFONT)-1) {
+ RECT rc = { 0 };
+ hOldFont = (HFONT)SelectObject(preq->hDC, gt->hFont);
+
+
+
+ if (gt->RelativeFlags & 2) rc.left = preq->rcDestRect.right + gt->iLeft;
+ else if (gt->RelativeFlags & 1) rc.left = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iLeft;
+ else rc.left = preq->rcDestRect.left + gt->iLeft;
+
+ if (gt->RelativeFlags & 8) rc.top = preq->rcDestRect.bottom + gt->iTop;
+ else if (gt->RelativeFlags & 4) rc.top = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iTop;
+ else rc.top = preq->rcDestRect.top + gt->iTop;
+
+ if (gt->RelativeFlags & 32) rc.right = preq->rcDestRect.right + gt->iRight;
+ else if (gt->RelativeFlags & 16) rc.right = ((preq->rcDestRect.right - preq->rcDestRect.left) >> 1) + gt->iRight;
+ else rc.right = preq->rcDestRect.left + gt->iRight;
+
+ if (gt->RelativeFlags & 128) rc.bottom = preq->rcDestRect.bottom + gt->iBottom;
+ else if (gt->RelativeFlags & 64) rc.bottom = ((preq->rcDestRect.bottom - preq->rcDestRect.top) >> 1) + gt->iBottom;
+ else rc.bottom = preq->rcDestRect.top + gt->iBottom;
+
+ ske_AlphaTextOut(preq->hDC, gt->stText, -1, &rc, gt->dwFlags, gt->dwColor);
+ SelectObject(preq->hDC, hOldFont);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+int ske_AddDescriptorToSkinObjectList(SKINOBJECTDESCRIPTOR *lpDescr, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin ? Skin : &g_SkinObjectList);
+ if (!mir_strcmpi(lpDescr->szObjectID, "_HEADER_"))
+ return 0;
+ //check if new object allready presents.
+ for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++)
+ if (!mir_strcmp(sk->pObjects[i].szObjectID, lpDescr->szObjectID))
+ return 0;
+ // Realocated list to add space for new object
+ if (sk->dwObjLPAlocated + 1 > sk->dwObjLPReserved) {
+ sk->pObjects = (SKINOBJECTDESCRIPTOR*)mir_realloc(sk->pObjects, sizeof(SKINOBJECTDESCRIPTOR)*(sk->dwObjLPReserved + 1)/*alloc step*/);
+ sk->dwObjLPReserved++;
+ }
+ { //filling new objects field
+ sk->pObjects[sk->dwObjLPAlocated].bType = lpDescr->bType;
+ sk->pObjects[sk->dwObjLPAlocated].Data = nullptr;
+ sk->pObjects[sk->dwObjLPAlocated].szObjectID = mir_strdup(lpDescr->szObjectID);
+ // sk->Objects[sk->dwObjLPAlocated].szObjectName = mir_strdup(lpDescr->szObjectName);
+ if (lpDescr->Data != nullptr) { //Copy defaults values
+ switch (lpDescr->bType) {
+ case OT_GLYPHOBJECT:
+ {
+ GLYPHOBJECT *gl = (GLYPHOBJECT *)lpDescr->Data;
+ sk->pObjects[sk->dwObjLPAlocated].Data = mir_alloc(sizeof(GLYPHOBJECT));
+ GLYPHOBJECT *obdat = (GLYPHOBJECT *)sk->pObjects[sk->dwObjLPAlocated].Data;
+ memcpy(obdat, gl, sizeof(GLYPHOBJECT));
+ if (gl->szFileName != nullptr) {
+ obdat->szFileName = mir_strdup(gl->szFileName);
+ replaceStr(gl->szFileName, nullptr);
+ }
+ else obdat->szFileName = nullptr;
+
+ obdat->hGlyph = nullptr;
+ break;
+ }
+ }
+
+ }
+ }
+ sk->dwObjLPAlocated++;
+ return 1;
+}
+
+static SKINOBJECTDESCRIPTOR* ske_FindObject(const char *szName, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
+ return skin_FindObjectByRequest((char *)szName, sk->pMaskList);
+}
+
+static SKINOBJECTDESCRIPTOR* ske_FindObjectByMask(MODERNMASK *pModernMask, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
+ return sk->pMaskList ? skin_FindObjectByMask(pModernMask, sk->pMaskList) : nullptr;
+}
+
+SKINOBJECTDESCRIPTOR* ske_FindObjectByName(const char *szName, uint8_t objType, SKINOBJECTSLIST *Skin)
+{
+ SKINOBJECTSLIST *sk = (Skin == nullptr) ? (&g_SkinObjectList) : Skin;
+ for (uint32_t i = 0; i < sk->dwObjLPAlocated; i++) {
+ if (sk->pObjects[i].bType == objType || objType == OT_ANY) {
+ if (!mir_strcmp(sk->pObjects[i].szObjectID, szName))
+ return &(sk->pObjects[i]);
+ }
+ }
+ return nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Paint glyph
+// wParam - LPSKINDRAWREQUEST
+// lParam - possible direct pointer to modern mask
+//////////////////////////////////////////////////////////////////////////
+
+INT_PTR ske_Service_DrawGlyph(WPARAM wParam, LPARAM lParam)
+{
+ auto *preq = (SKINDRAWREQUEST *)wParam;
+ if (preq == nullptr)
+ return -1;
+
+ mir_cslock lck(cs_SkinChanging);
+
+ SKINOBJECTDESCRIPTOR *pgl = (lParam ? ske_FindObjectByMask((MODERNMASK*)lParam, nullptr) : ske_FindObject(preq->szObjectID, nullptr));
+ if (pgl == nullptr) return -1;
+ if (pgl->Data == nullptr) return -1;
+
+ GLYPHOBJECT *gl = (GLYPHOBJECT*)pgl->Data;
+ int iStyle = gl->Style & 7;
+ if (iStyle == ST_SKIP)
+ return ST_SKIP;
+
+ if (gl->hGlyph == nullptr && gl->hGlyph != (HBITMAP)-1 && (iStyle == ST_IMAGE || iStyle == ST_FRAGMENT || iStyle == ST_SOLARIZE)) {
+ if (gl->szFileName) {
+ gl->hGlyph = ske_LoadGlyphImage(_A2T(gl->szFileName));
+ if (gl->hGlyph) {
+ BITMAP bmp = { 0 };
+ GetObject(gl->hGlyph, sizeof(BITMAP), &bmp);
+ gl->bmBitsPixel = (uint8_t)bmp.bmBitsPixel;
+ gl->bmHeight = bmp.bmHeight;
+ gl->bmWidth = bmp.bmWidth;
+ }
+ else gl->hGlyph = (HBITMAP)-1; //invalid
+ }
+ }
+ return ske_DrawSkinObject(preq, gl);
+}
+
+
+void ske_PreMultiplyChannels(HBITMAP hbmp, uint8_t Mult)
+{
+ BITMAP bmp;
+ BOOL flag = FALSE;
+ uint8_t *pBitmapBits;
+ uint32_t Len;
+ int bh, bw, y, x;
+
+ GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp);
+ bh = bmp.bmHeight;
+ bw = bmp.bmWidth;
+ Len = bh * bw * 4;
+ flag = (bmp.bmBits == nullptr);
+ if (flag) {
+ pBitmapBits = (LPBYTE)mir_alloc(Len);
+ GetBitmapBits(hbmp, Len, pBitmapBits);
+ }
+ else
+ pBitmapBits = (uint8_t *)bmp.bmBits;
+ for (y = 0; y < bh; ++y) {
+ uint8_t *pPixel = pBitmapBits + bw * 4 * y;
+
+ for (x = 0; x < bw; ++x) {
+ if (Mult) {
+ pPixel[0] = pPixel[0] * pPixel[3] / 255;
+ pPixel[1] = pPixel[1] * pPixel[3] / 255;
+ pPixel[2] = pPixel[2] * pPixel[3] / 255;
+ }
+ else {
+ pPixel[3] = 255;
+ }
+ pPixel += 4;
+ }
+ }
+ if (flag) {
+ Len = SetBitmapBits(hbmp, Len, pBitmapBits);
+ mir_free(pBitmapBits);
+ }
+ return;
+}
+
+int ske_GetFullFilename(wchar_t *buf, const wchar_t *file, wchar_t *skinfolder, BOOL madeAbsolute)
+{
+ wchar_t *SkinPlace = db_get_wsa(0, SKIN, "SkinFolder");
+ if (SkinPlace == nullptr)
+ SkinPlace = mir_wstrdup(L"\\Skin\\default");
+
+ wchar_t b2[MAX_PATH];
+ if (file[0] != '\\' && file[1] != ':')
+ mir_snwprintf(b2, L"%s\\%s", (skinfolder == nullptr) ? SkinPlace : ((INT_PTR)skinfolder != -1) ? skinfolder : L"", file);
+ else
+ mir_wstrncpy(b2, file, _countof(b2));
+
+ if (madeAbsolute) {
+ if (b2[0] == '\\' && b2[1] != '\\')
+ PathToAbsoluteW(b2 + 1, buf);
+ else
+ PathToAbsoluteW(b2, buf);
+ }
+ else mir_wstrncpy(buf, b2, MAX_PATH);
+
+ mir_free(SkinPlace);
+ return 0;
+}
+
+/*
+This function is required to load TGA to dib buffer myself
+Major part of routines is from http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga.c
+*/
+
+static BOOL ske_ReadTGAImageData(void *From, uint32_t fromSize, uint8_t *destBuf, uint32_t bufSize, BOOL RLE)
+{
+ uint8_t *pos = destBuf;
+ uint8_t *from = fromSize ? (uint8_t *)From : nullptr;
+ FILE *fp = !fromSize ? (FILE *)From : nullptr;
+ uint32_t destCount = 0;
+ uint32_t fromCount = 0;
+ if (!RLE) {
+ while (((from && fromCount < fromSize) || (fp && fromCount < bufSize))
+ && (destCount < bufSize)) {
+ uint8_t r = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ uint8_t g = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ uint8_t b = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ uint8_t a = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ pos[destCount++] = r;
+ pos[destCount++] = g;
+ pos[destCount++] = b;
+ pos[destCount++] = a;
+
+ if (destCount > bufSize) break;
+ if (from) if (fromCount < fromSize) break;
+ }
+ }
+ else {
+ uint8_t rgba[4];
+ uint8_t packet_header;
+ uint8_t *ptr = pos;
+ uint8_t size;
+ int i;
+ while (ptr < pos + bufSize) {
+ /* read first byte */
+ packet_header = from ? from[fromCount] : (uint8_t)fgetc(fp);
+ if (from) from++;
+ size = 1 + (packet_header & 0x7f);
+ if (packet_header & 0x80) {
+ /* run-length packet */
+ if (from) {
+ *((uint32_t*)rgba) = *((uint32_t*)(from + fromCount));
+ fromCount += 4;
+ }
+ else fread(rgba, sizeof(uint8_t), 4, fp);
+ for (i = 0; i < size; ++i, ptr += 4) {
+ ptr[2] = rgba[2];
+ ptr[1] = rgba[1];
+ ptr[0] = rgba[0];
+ ptr[3] = rgba[3];
+ }
+ }
+ else { /* not run-length packet */
+ for (i = 0; i < size; ++i, ptr += 4) {
+ ptr[0] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ ptr[1] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ ptr[2] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ ptr[3] = from ? from[fromCount++] : (uint8_t)fgetc(fp);
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+static HBITMAP ske_LoadGlyphImage_TGA(const wchar_t *szFilename)
+{
+ uint8_t *colormap = nullptr;
+ int cx = 0, cy = 0;
+ BOOL err = FALSE;
+ tga_header_t header;
+ if (!szFilename) return nullptr;
+ if (!wildcmpiw(szFilename, L"*\\*%.tga")) {
+ //Loading TGA image from file
+ FILE *fp = _wfopen(szFilename, L"rb");
+ if (!fp) {
+ TRACEVAR("error: couldn't open \"%s\"!\n", szFilename);
+ return nullptr;
+ }
+ /* read header */
+ fread(&header, sizeof(tga_header_t), 1, fp);
+ if ((header.pixel_depth != 32) || ((header.image_type != 10) && (header.image_type != 2))) {
+ fclose(fp);
+ return nullptr;
+ }
+
+ /*memory allocation */
+ colormap = (uint8_t*)mir_alloc(header.width*header.height * 4);
+ cx = header.width;
+ cy = header.height;
+ fseek(fp, header.id_lenght, SEEK_CUR);
+ fseek(fp, header.cm_length, SEEK_CUR);
+ err = !ske_ReadTGAImageData((void*)fp, 0, colormap, header.width*header.height * 4, header.image_type == 10);
+ fclose(fp);
+ }
+ else {
+ /* reading from resources IDR_TGA_DEFAULT_SKIN */
+ HRSRC hRSrc = FindResourceA(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_TGA_DEFAULT_SKIN), "TGA");
+ if (!hRSrc) return nullptr;
+ HGLOBAL hRes = LoadResource(g_plugin.getInst(), hRSrc);
+ if (!hRes) return nullptr;
+ uint32_t size = SizeofResource(g_plugin.getInst(), hRSrc);
+ uint8_t *mem = (uint8_t*)LockResource(hRes);
+ if (size > sizeof(header)) {
+ tga_header_t *tgahdr = (tga_header_t*)mem;
+ if (tgahdr->pixel_depth == 32 && (tgahdr->image_type == 2 || tgahdr->image_type == 10)) {
+ colormap = (uint8_t*)mir_alloc(tgahdr->width*tgahdr->height * 4);
+ cx = tgahdr->width;
+ cy = tgahdr->height;
+ ske_ReadTGAImageData((void*)(mem + sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), size - (sizeof(tga_header_t) + tgahdr->id_lenght + tgahdr->cm_length), colormap, cx*cy * 4, tgahdr->image_type == 10);
+ }
+ }
+ FreeResource(hRes);
+ }
+
+ if (colormap == nullptr)
+ return nullptr;
+
+ // create dib section
+ uint8_t *pt;
+ HBITMAP hbmp = ske_CreateDIB32Point(cx, cy, (void**)&pt);
+ if (hbmp)
+ memcpy(pt, colormap, cx*cy * 4);
+ mir_free(colormap);
+ return hbmp;
+}
+
+static HBITMAP ske_LoadGlyphImageByDecoders(const wchar_t *tszFileName)
+{
+ if (!wcschr(tszFileName, '%') && !PathFileExists(tszFileName))
+ return nullptr;
+
+ const wchar_t *ext = wcsrchr(tszFileName, '.');
+ if (ext == nullptr)
+ return nullptr;
+
+ BITMAP bmpInfo;
+ HBITMAP hBitmap;
+ bool f = false;
+
+ if (!mir_wstrcmpi(ext, L".tga")) {
+ hBitmap = ske_LoadGlyphImage_TGA(tszFileName);
+ f = true;
+ }
+ else hBitmap = Bitmap_Load(tszFileName);
+
+ if (hBitmap == nullptr)
+ return nullptr;
+
+ GetObject(hBitmap, sizeof(BITMAP), &bmpInfo);
+ if (bmpInfo.bmBitsPixel == 32)
+ ske_PreMultiplyChannels(hBitmap, f);
+ else {
+ HDC dc32 = CreateCompatibleDC(nullptr);
+ HDC dc24 = CreateCompatibleDC(nullptr);
+ HBITMAP hBitmap32 = ske_CreateDIB32(bmpInfo.bmWidth, bmpInfo.bmHeight);
+ HBITMAP obmp24 = (HBITMAP)SelectObject(dc24, hBitmap);
+ HBITMAP obmp32 = (HBITMAP)SelectObject(dc32, hBitmap32);
+ BitBlt(dc32, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, dc24, 0, 0, SRCCOPY);
+ SelectObject(dc24, obmp24);
+ SelectObject(dc32, obmp32);
+ DeleteDC(dc24);
+ DeleteDC(dc32);
+ DeleteObject(hBitmap);
+ hBitmap = hBitmap32;
+ ske_PreMultiplyChannels(hBitmap, 0);
+ }
+ return hBitmap;
+}
+
+static HBITMAP ske_skinLoadGlyphImage(const wchar_t *tszFileName)
+{
+ if (!wildcmpiw(tszFileName, L"*.tga"))
+ return GDIPlus_LoadGlyphImage(tszFileName);
+
+ return ske_LoadGlyphImageByDecoders(tszFileName);
+}
+
+HBITMAP ske_LoadGlyphImage(const wchar_t *tszFileName)
+{
+ // try to find image in loaded
+ wchar_t szFile[MAX_PATH] = { 0 };
+ ske_GetFullFilename(szFile, tszFileName, g_SkinObjectList.szSkinPlace, TRUE);
+
+ mir_cslock lck(cs_SkinChanging);
+
+ if (pLoadedImages) {
+ for (uint32_t i = 0; i < dwLoadedImagesCount; i++) {
+ if (!mir_wstrcmpi(pLoadedImages[i].szFileName, szFile)) {
+ pLoadedImages[i].dwLoadedTimes++;
+ return pLoadedImages[i].hGlyph;
+ }
+ }
+ }
+
+ // load new image
+ HBITMAP hbmp = ske_skinLoadGlyphImage(szFile);
+ if (hbmp == nullptr)
+ return nullptr;
+
+ // add to loaded list
+ if (dwLoadedImagesCount + 1 > dwLoadedImagesAlocated) {
+ pLoadedImages = (GLYPHIMAGE*)mir_realloc(pLoadedImages, sizeof(GLYPHIMAGE)*(dwLoadedImagesCount + 1));
+ if (!pLoadedImages)
+ return nullptr;
+ dwLoadedImagesAlocated++;
+ }
+
+ pLoadedImages[dwLoadedImagesCount].dwLoadedTimes = 1;
+ pLoadedImages[dwLoadedImagesCount].hGlyph = hbmp;
+ pLoadedImages[dwLoadedImagesCount].szFileName = mir_wstrdup(szFile);
+ dwLoadedImagesCount++;
+ return hbmp;
+}
+
+int ske_UnloadGlyphImage(HBITMAP hbmp)
+{
+ for (uint32_t i = 0; i < dwLoadedImagesCount && pLoadedImages; i++) {
+ if (hbmp != pLoadedImages[i].hGlyph)
+ continue;
+
+ pLoadedImages[i].dwLoadedTimes--;
+ if (pLoadedImages[i].dwLoadedTimes == 0) {
+ LPGLYPHIMAGE gl = &(pLoadedImages[i]);
+ replaceStrW(gl->szFileName, nullptr);
+ memmove(&(pLoadedImages[i]), &(pLoadedImages[i + 1]), sizeof(GLYPHIMAGE) * (dwLoadedImagesCount - i - 1));
+ dwLoadedImagesCount--;
+ DeleteObject(hbmp);
+ if (dwLoadedImagesCount == 0) {
+ dwLoadedImagesAlocated = 0;
+ mir_free_and_nil(pLoadedImages);
+ }
+ }
+ return 0;
+ }
+ DeleteObject(hbmp);
+ return 0;
+}
+
+int ske_UnloadSkin(SKINOBJECTSLIST *Skin)
+{
+ ClearMaskList(Skin->pMaskList);
+
+ //clear font list
+ if (gl_plSkinFonts && gl_plSkinFonts->realCount > 0) {
+ for (int i = 0; i < gl_plSkinFonts->realCount; i++) {
+ SKINFONT *sf = (SKINFONT *)gl_plSkinFonts->items[i];
+ if (sf) {
+ mir_free(sf->szFontID);
+ DeleteObject(sf->hFont);
+ mir_free(sf);
+ }
+ }
+ List_Destroy(gl_plSkinFonts);
+ mir_free_and_nil(gl_plSkinFonts);
+ }
+
+ replaceStrW(Skin->szSkinPlace, nullptr);
+ if (Skin->pTextList) List_Destroy(Skin->pTextList);
+ mir_free_and_nil(Skin->pTextList);
+ ModernSkinButtonDeleteAll();
+ if (Skin->dwObjLPAlocated == 0)
+ return 0;
+
+ for (uint32_t i = 0; i < Skin->dwObjLPAlocated; i++) {
+ switch (Skin->pObjects[i].bType) {
+ case OT_GLYPHOBJECT:
+ GLYPHOBJECT *dt = (GLYPHOBJECT*)Skin->pObjects[i].Data;
+ if (dt->hGlyph && dt->hGlyph != (HBITMAP)-1)
+ ske_UnloadGlyphImage(dt->hGlyph);
+ dt->hGlyph = nullptr;
+ replaceStr(dt->szFileName, nullptr);
+
+ if (dt->plTextList && dt->plTextList->realCount > 0) {
+ for (int k = 0; k < dt->plTextList->realCount; k++) {
+ GLYPHTEXT *gt = (GLYPHTEXT *)dt->plTextList->items[k];
+ if (gt) {
+ mir_free(gt->stText);
+ mir_free(gt->stValueText);
+ mir_free(gt->szFontID);
+ mir_free(gt->szGlyphTextID);
+ mir_free(gt);
+ }
+ }
+ List_Destroy(dt->plTextList);
+ mir_free(dt->plTextList);
+ }
+ mir_free(dt);
+ break;
+ }
+ replaceStr(Skin->pObjects[i].szObjectID, nullptr);
+ }
+ mir_free_and_nil(Skin->pObjects);
+ Skin->pTextList = nullptr;
+ Skin->dwObjLPAlocated = 0;
+ Skin->dwObjLPReserved = 0;
+ return 0;
+}
+
+static void RegisterMaskByParce(const char *szSetting, char *szValue, SKINOBJECTSLIST *pSkin)
+{
+ size_t i, val_len = mir_strlen(szValue);
+
+ for (i = 0; i < val_len; i++)
+ if (szValue[i] == ':')
+ break;
+
+ if (i < val_len) {
+ char *Obj, *Mask;
+ int res;
+ uint32_t ID = atoi(szSetting + 1);
+ Mask = szValue + i + 1;
+ Obj = (char*)mir_alloc(i + 2);
+ mir_strncpy(Obj, szValue, i + 1);
+ Obj[i + 1] = '\0';
+ res = AddStrModernMaskToList(ID, Mask, Obj, pSkin->pMaskList);
+ mir_free(Obj);
+ }
+}
+
+static int ske_ProcessLoadindString(const char *szSetting, char *szValue)
+{
+ if (!pCurrentSkin) return 0;
+ if (szSetting[0] == '$')
+ RegisterObjectByParce((char *)szSetting, szValue);
+ else if (szSetting[0] == '#')
+ RegisterButtonByParce((char *)szSetting, szValue);
+ else if (szSetting[0] == '@')
+ RegisterMaskByParce((char *)szSetting, szValue, pCurrentSkin); ///
+ else if (szSetting[0] == 't')
+ ske_AddParseTextGlyphObject((char*)szSetting, szValue, pCurrentSkin);
+ else if (szSetting[0] == 'f')
+ ske_AddParseSkinFont((char*)szSetting, szValue);
+ else return 0;
+ return 1;
+}
+
+static int ske_enumdb_SkinObjectsProc(const char *szSetting, void *)
+{
+ ptrA value(db_get_sa(0, SKIN, szSetting));
+ ske_ProcessLoadindString(szSetting, value);
+ return 0;
+}
+
+static int ske_SortTextGlyphObjectFunc(void *first, void *second)
+{
+ GLYPHTEXT *p1 = *(GLYPHTEXT**)first, *p2 = *(GLYPHTEXT**)second;
+ return mir_strcmp(p1->szGlyphTextID, p2->szGlyphTextID);
+}
+
+static void ske_LinkSkinObjects(SKINOBJECTSLIST *pObjectList)
+{
+ // LINK Mask with objects
+ for (uint32_t i = 0; i < pObjectList->pMaskList->dwMaskCnt; i++) {
+ MODERNMASK *mm = &(pObjectList->pMaskList->pl_Masks[i]);
+ void *pObject = (void *)ske_FindObjectByName(mm->szObjectName, OT_ANY, (SKINOBJECTSLIST *)pObjectList);
+ replaceStr(mm->szObjectName, nullptr);
+ mm->bObjectFound = TRUE;
+ mm->pObject = pObject;
+ }
+
+ if (pObjectList->pTextList) {
+ // LINK Text with objects
+ for (int i = 0; i < pObjectList->pTextList->realCount; i++) {
+ GLYPHTEXT *glText = (GLYPHTEXT *)pObjectList->pTextList->items[i];
+ SKINOBJECTDESCRIPTOR *lpobj = ske_FindObjectByName(glText->szObjectName, OT_GLYPHOBJECT, pObjectList);
+ replaceStr(glText->szObjectName, nullptr);
+ GLYPHOBJECT *globj = nullptr;
+ if (lpobj)
+ globj = (GLYPHOBJECT*)lpobj->Data;
+ if (globj) {
+ if (!globj->plTextList) {
+ globj->plTextList = List_Create(0, 1);
+ globj->plTextList->sortFunc = ske_SortTextGlyphObjectFunc;
+ }
+ List_Insert(globj->plTextList, (void*)glText, globj->plTextList->realCount);
+ qsort(globj->plTextList->items, globj->plTextList->realCount, sizeof(GLYPHTEXT*), (int(*)(const void*, const void*))globj->plTextList->sortFunc);
+ pObjectList->pTextList->items[i] = nullptr;
+ }
+ else {
+ GLYPHTEXT *gt = glText;
+ if (gt) {
+ mir_free(gt->stText);
+ mir_free(gt->stValueText);
+ mir_free(gt->szFontID);
+ mir_free(gt->szGlyphTextID);
+ mir_free(gt);
+ }
+ }
+ }
+ List_Destroy(pObjectList->pTextList);
+ mir_free_and_nil(pObjectList->pTextList);
+ }
+}
+
+// Getting skin objects and masks from DB
+
+static int ske_GetSkinFromDB(char *, SKINOBJECTSLIST *Skin)
+{
+ if (Skin == nullptr) return 0;
+ ske_UnloadSkin(Skin);
+ g_CluiData.fDisableSkinEngine = db_get_b(0, "ModernData", "DisableEngine", SETTING_DISABLESKIN_DEFAULT);
+ // window borders
+ if (g_CluiData.fDisableSkinEngine) {
+ g_CluiData.LeftClientMargin = 0;
+ g_CluiData.RightClientMargin = 0;
+ g_CluiData.TopClientMargin = 0;
+ g_CluiData.BottomClientMargin = 0;
+ return 0;
+ }
+
+ // window borders
+ g_CluiData.LeftClientMargin = (int)db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT);
+ g_CluiData.RightClientMargin = (int)db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT);
+ g_CluiData.TopClientMargin = (int)db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT);
+ g_CluiData.BottomClientMargin = (int)db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT);
+
+ Skin->pMaskList = (LISTMODERNMASK*)mir_alloc(sizeof(LISTMODERNMASK));
+ memset(Skin->pMaskList, 0, sizeof(LISTMODERNMASK));
+ Skin->szSkinPlace = db_get_wsa(0, SKIN, "SkinFolder");
+ if (!Skin->szSkinPlace || (wcschr(Skin->szSkinPlace, '%') && !db_get_b(0, SKIN, "Modified", 0))) {
+ BOOL bOnlyObjects = FALSE;
+ if (Skin->szSkinPlace && wcschr(Skin->szSkinPlace, '%'))
+ bOnlyObjects = TRUE;
+ mir_free(Skin->szSkinPlace);
+ Skin->szSkinPlace = mir_wstrdup(L"%Default%");
+ ske_LoadSkinFromResource(bOnlyObjects);
+ }
+
+ // Load objects
+ pCurrentSkin = Skin;
+ db_enum_settings(0, ske_enumdb_SkinObjectsProc, SKIN);
+
+ SortMaskList(pCurrentSkin->pMaskList);
+ ske_LinkSkinObjects(pCurrentSkin);
+
+ // Load Masks
+ return 0;
+}
+
+// surrogate to be called from outside
+void ske_LoadSkinFromDB(void)
+{
+ ske_GetSkinFromDB(SKIN, &g_SkinObjectList);
+ g_CluiData.dwKeyColor = db_get_dw(0, "ModernSettings", "KeyColor", (uint32_t)SETTING_KEYCOLOR_DEFAULT);
+}
+
+static int ske_LoadSkinFromResource(BOOL bOnlyObjects)
+{
+ IniParser parser(g_plugin.getInst(), MAKEINTRESOURCEA(IDR_MSF_DEFAULT_SKIN), "MSF", bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS);
+ if (parser.CheckOK()) {
+ db_delete_module(0, "ModernSkin");
+ db_set_s(0, SKIN, "SkinFolder", "%Default%");
+ db_set_s(0, SKIN, "SkinFile", "%Default%");
+ parser.Parse(IniParser::WriteStrToDb, 0);
+ }
+ return 0;
+}
+
+// Load data from ini file
+int ske_LoadSkinFromIniFile(wchar_t *szFileName, BOOL bOnlyObjects)
+{
+ if (wcschr(szFileName, '%'))
+ return ske_LoadSkinFromResource(bOnlyObjects);
+
+ IniParser parser(szFileName, bOnlyObjects ? IniParser::FLAG_ONLY_OBJECTS : IniParser::FLAG_WITH_SETTINGS);
+ if (!parser.CheckOK())
+ return 0;
+
+ db_delete_module(0, "ModernSkin");
+
+ wchar_t skinFolder[MAX_PATH], skinFile[MAX_PATH];
+ IniParser::GetSkinFolder(szFileName, skinFolder);
+ PathToRelativeW(szFileName, skinFile);
+
+ db_set_ws(0, SKIN, "SkinFolder", skinFolder);
+ db_set_ws(0, SKIN, "SkinFile", skinFile);
+
+ parser.Parse(IniParser::WriteStrToDb, 1);
+ return 0;
+}
+
+BOOL ske_TextOut(HDC hdc, int x, int y, LPCTSTR lpString, int nCount)
+{
+ SIZE sz;
+ GetTextExtentPoint32(hdc, lpString, nCount, &sz);
+
+ RECT rc = { 0 };
+ SetRect(&rc, x, y, x + sz.cx, y + sz.cy);
+ ske_DrawText(hdc, lpString, nCount, &rc, DT_NOCLIP | DT_SINGLELINE | DT_LEFT);
+ return 1;
+}
+
+static INT_PTR ske_Service_AlphaTextOut(WPARAM wParam, LPARAM)
+{
+ if (!wParam) return 0;
+
+ AlphaTextOutParams ap = *(AlphaTextOutParams*)wParam;
+ return ske_AlphaTextOut(ap.hDC, ap.lpString, ap.nCount, ap.lpRect, ap.format, ap.ARGBcolor);
+}
+
+static __inline void ske_SetMatrix(sbyte *matrix,
+ sbyte a, sbyte b, sbyte c,
+ sbyte d, sbyte e, sbyte f,
+ sbyte g, sbyte h, sbyte i)
+{
+ matrix[0] = a; matrix[1] = b; matrix[2] = c;
+ matrix[3] = d; matrix[4] = e; matrix[5] = f;
+ matrix[6] = g; matrix[7] = h; matrix[8] = i;
+}
+
+bool ske_ResetTextEffect(HDC hdc)
+{
+ int idx = arEffectStack.getIndex((EFFECTSSTACKITEM*)&hdc);
+ if (idx == -1)
+ return false;
+
+ mir_free(arEffectStack[idx]);
+ arEffectStack.remove(idx);
+ return true;
+}
+
+bool ske_SelectTextEffect(HDC hdc, uint8_t EffectID, uint32_t FirstColor, uint32_t SecondColor)
+{
+ if (EffectID > MAXPREDEFINEDEFFECTS)
+ return false;
+
+ if (EffectID == -1)
+ return ske_ResetTextEffect(hdc);
+
+ EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc);
+ if (effect == nullptr) {
+ effect = (EFFECTSSTACKITEM *)mir_alloc(sizeof(EFFECTSSTACKITEM));
+ effect->hdc = hdc;
+ arEffectStack.insert(effect);
+ }
+
+ effect->EffectID = EffectID;
+ effect->FirstColor = FirstColor;
+ effect->SecondColor = SecondColor;
+ return true;
+}
+
+static bool ske_GetTextEffect(HDC hdc, MODERNEFFECT *modernEffect)
+{
+ if (!modernEffect)
+ return false;
+
+ EFFECTSSTACKITEM *effect = arEffectStack.find((EFFECTSSTACKITEM*)&hdc);
+ if (effect == nullptr)
+ return false;
+
+ modernEffect->EffectID = effect->EffectID;
+ modernEffect->EffectColor1 = effect->FirstColor;
+ modernEffect->EffectColor2 = effect->SecondColor;
+ modernEffect->EffectMatrix = ModernEffectsEnum[effect->EffectID];
+ return true;
+}
+
+static bool ske_DrawTextEffect(uint8_t *destPt, uint8_t *maskPt, uint32_t width, uint32_t height, MODERNEFFECT *effect)
+{
+ sbyte *buf;
+ sbyte *outbuf;
+ sbyte *bufline, *buflineTop, *buflineMid;
+ int sign = 0;
+ uint8_t *maskline, *destline;
+ uint8_t al, rl, gl, bl, ad, rd, gd, bd;
+ int k = 0;
+ uint32_t x, y;
+ sbyte *matrix;
+ uint8_t mcTopStart;
+ uint8_t mcBottomEnd;
+ uint8_t mcLeftStart;
+ uint8_t mcRightEnd;
+ uint8_t effectCount;
+ int minX = width;
+ int maxX = 0;
+ int minY = height;
+ int maxY = 0;
+ if (effect->EffectID == 0xFF) return false;
+ if (!width || !height) return false;
+ if (!destPt) return false;
+ buf = (sbyte*)mir_alloc(width*height*sizeof(uint8_t));
+ {
+ matrix = effect->EffectMatrix.matrix;
+ mcTopStart = 2 - effect->EffectMatrix.topEffect;
+ mcBottomEnd = 3 + effect->EffectMatrix.bottomEffect;
+ mcLeftStart = 2 - effect->EffectMatrix.leftEffect;
+ mcRightEnd = 3 + effect->EffectMatrix.rightEffect;
+ effectCount = effect->EffectMatrix.cycleCount;
+ }
+ al = 255 - ((uint8_t)(effect->EffectColor1 >> 24));
+ rl = GetRValue(effect->EffectColor1);
+ gl = GetGValue(effect->EffectColor1);
+ bl = GetBValue(effect->EffectColor1);
+ ad = 255 - ((uint8_t)(effect->EffectColor2 >> 24));
+ rd = GetRValue(effect->EffectColor2);
+ gd = GetGValue(effect->EffectColor2);
+ bd = GetBValue(effect->EffectColor2);
+
+ // Fill buffer by mid values of image
+ for (y = 0; y < height; y++) {
+ bufline = buf + y*width;
+ maskline = maskPt + ((y*width) << 2);
+ for (x = 0; x < width; x++) {
+ uint8_t a = (sbyte)(uint32_t)((maskline[0] + maskline[2] + maskline[1] + maskline[1]) >> 4);
+ *bufline = a;
+ if (a != 0) {
+ minX = min((int)x, minX);
+ minY = min((int)y, minY);
+ maxX = max((int)x, maxX);
+ maxY = max((int)y, maxY);
+ }
+ bufline++;
+ maskline += 4;
+ }
+ }
+ // Here perform effect on buffer and place results to outbuf
+ for (k = 0; k < (effectCount & 0x7F); k++) {
+ minX = max(0, minX + mcLeftStart - 2);
+ minY = max(0, minY + mcTopStart - 2);
+ maxX = min((int)width, maxX + mcRightEnd - 1);
+ maxY = min((int)height, maxY + mcBottomEnd - 1);
+
+ outbuf = (sbyte*)mir_alloc(width*height*sizeof(sbyte));
+ memset(outbuf, 0, width*height*sizeof(sbyte));
+ for (y = (uint32_t)minY; y < (uint32_t)maxY; y++) {
+ int val;
+ bufline = outbuf + y*width + minX;
+ buflineMid = buf + y*width + minX;
+ for (x = (uint32_t)minX; x < (uint32_t)maxX; x++) {
+ int matrixHor, matrixVer;
+ val = 0;
+ for (matrixVer = mcTopStart; matrixVer < mcBottomEnd; matrixVer++) {
+ int buflineStep = width*(matrixVer - 2);
+ int as = y + matrixVer - 2;
+ sbyte *buflineTopS = nullptr;
+ if (as >= 0 && (uint32_t)as < height) buflineTopS = buflineMid + buflineStep;
+
+ for (matrixHor = mcLeftStart; matrixHor < mcRightEnd; matrixHor++) {
+ buflineTop = buflineTopS;
+ int a = x + matrixHor - 2;
+ if (buflineTop && a >= 0 && (uint32_t)a < width) buflineTop += matrixHor - 2;
+ else buflineTop = nullptr;
+ if (buflineTop)
+ val += ((*buflineTop)*matrix[matrixVer * 5 + matrixHor]);
+ }
+ }
+ val = (val + 1) >> 5;
+ *bufline = (sbyte)((val>127) ? 127 : (val < -125) ? -125 : val);
+ bufline++;
+ buflineMid++;
+ }
+ }
+ mir_free(buf);
+ buf = outbuf;
+ }
+ {
+ uint8_t r1, b1, g1, a1;
+ b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1;
+ //perform out to dest
+ for (y = 0; y < height; y++) {
+ bufline = buf + y*width;
+ destline = destPt + ((y*width) << 2);
+ for (x = 0; x < width; x++) {
+ sbyte val = *bufline;
+ uint8_t absVal = ((val < 0) ? -val : val);
+
+ if (val != 0) {
+ if (val > 0 && sign < 0) {
+ b1 = bl; r1 = rl; g1 = gl; a1 = al; sign = 1;
+ }
+ else if (val < 0 && sign>0) {
+ b1 = bd; r1 = rd; g1 = gd; a1 = ad; sign = -1;
+ }
+
+ absVal = absVal*a1 / 255;
+
+ destline[0] = ((destline[0] * (128 - absVal)) + absVal*b1) >> 7;
+ destline[1] = ((destline[1] * (128 - absVal)) + absVal*g1) >> 7;
+ destline[2] = ((destline[2] * (128 - absVal)) + absVal*r1) >> 7;
+ destline[3] += ((255 - destline[3])*(a1*absVal)) / 32640;
+ }
+ bufline++;
+ destline += 4;
+ }
+ }
+ mir_free(buf);
+ }
+ return false;
+}
+
+static int ske_AlphaTextOut(HDC hDC, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, uint32_t ARGBcolor)
+{
+ if (!(lpString && lpRect))
+ return 0;
+
+ // Step first fill fast calc correction tables:
+ static bool _tables_empty = true;
+ static uint8_t gammaTbl[256]; // Gamma correction table
+ static uint16_t blueMulTbl[256]; // blue coefficient multiplication table
+ static uint16_t greenMulTbl[256]; // green coefficient multiplication table
+ static uint16_t redMulTbl[256]; // red coefficient multiplication table
+ if (_tables_empty) {
+ // fill tables
+ double gammaCfPw = 1000 / (double)DBGetContactSettingRangedWord(0, "ModernData", "AlphaTextOutGamma", 700, 1, 5000);
+ uint8_t blueCf = db_get_b(0, "ModernData", "AlphaTextOutBlueCorrection", 28);
+ uint8_t redCf = db_get_b(0, "ModernData", "AlphaTextOutRed Correction", 77);
+ uint8_t greenCf = db_get_b(0, "ModernData", "AlphaTextOutGreen Correction", 151);
+
+ for (int i = 0; i < 256; i++) {
+ gammaTbl[i] = (uint8_t)(255 * pow((double)i / 255, gammaCfPw));
+ blueMulTbl[i] = i * blueCf;
+ redMulTbl[i] = i * redCf;
+ greenMulTbl[i] = i * greenCf;
+ }
+ }
+
+ // Calc len of input string
+ if (nCount == -1)
+ nCount = (int)mir_wstrlen(lpString);
+
+ // retrieve destination bitmap bits
+ HBITMAP hDestBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP);
+ BITMAP bmpDest;
+ GetObject(hDestBitmap, sizeof(BITMAP), &bmpDest);
+
+ bool destHasNotDIB = (bmpDest.bmBits == nullptr);
+ uint8_t *pDestBits;
+ if (destHasNotDIB) {
+ pDestBits = (uint8_t*)mir_alloc(bmpDest.bmHeight * bmpDest.bmWidthBytes);
+ GetBitmapBits(hDestBitmap, bmpDest.bmHeight*bmpDest.bmWidthBytes, pDestBits);
+ }
+ else
+ pDestBits = (uint8_t*)bmpDest.bmBits;
+
+ // Creating offscreen buffer
+ HDC hOffscreenDC = CreateCompatibleDC(hDC);
+
+ // Font to be used to draw text
+ HFONT hFont = (HFONT)GetCurrentObject(hDC, OBJ_FONT);
+ HFONT hOldOffscreenFont = (HFONT)SelectObject(hOffscreenDC, hFont);
+
+ // Calculating text geometric size
+ RECT workRect = *lpRect;
+ int workRectWidth = workRect.right - workRect.left;
+ int workRectHeight = workRect.bottom - workRect.top;
+ if (workRectWidth <= 0 || workRectHeight <= 0) {
+ if (destHasNotDIB)
+ mir_free(pDestBits);
+ return 0;
+ }
+
+ SIZE textSize;
+ GetTextExtentPoint32(hOffscreenDC, lpString, nCount, &textSize);
+
+ LPCTSTR lpWorkString = lpString;
+ BOOL bNeedFreeWorkString = FALSE;
+
+ // if we need to cut the text with ellipsis
+ if ((format & DT_END_ELLIPSIS) && textSize.cx > workRectWidth) {
+ // Calc geometric width of ellipsis
+ SIZE szEllipsis;
+ GetTextExtentPoint32A(hOffscreenDC, "...", 3, &szEllipsis);
+ szEllipsis.cx++; // CORRECTION: some width correction
+
+ // Calc count of visible chars
+ int visibleCharCount = nCount;
+ if (workRectWidth > szEllipsis.cx)
+ GetTextExtentExPoint(hOffscreenDC, lpString, nCount, workRectWidth - szEllipsis.cx, &visibleCharCount, nullptr, &textSize);
+ else
+ GetTextExtentExPoint(hOffscreenDC, lpString, nCount, 0, &visibleCharCount, nullptr, &textSize);
+
+ // replace end of string by elipsis
+ bNeedFreeWorkString = TRUE;
+ lpWorkString = (wchar_t*)mir_alloc((visibleCharCount + 4) * sizeof(wchar_t));
+
+ memcpy((void*)lpWorkString, lpString, visibleCharCount * sizeof(wchar_t));
+ memcpy((void*)(lpWorkString + visibleCharCount), L"...", 4 * sizeof(wchar_t)); // 3 + 1
+
+ nCount = visibleCharCount + 3;
+ }
+
+ // Calc sizes and offsets
+
+ textSize.cx += 2; // CORRECTION: for italic
+
+ int drx = 0; // x-axis offset of draw point
+
+ if (workRectWidth > textSize.cx) {
+ if (format & (DT_RIGHT | DT_RTLREADING))
+ drx = workRectWidth - textSize.cx;
+ else if (format & DT_CENTER)
+ drx = (workRectWidth - textSize.cx) >> 1;
+ }
+ else textSize.cx = workRectWidth;
+
+ int dry = 0; // y-axis offset of draw point
+
+ if (workRectHeight > textSize.cy) {
+ if (format & DT_BOTTOM)
+ dry = workRectHeight - textSize.cy;
+ else if (format & DT_VCENTER)
+ dry = (workRectHeight - textSize.cy) >> 1;
+ }
+ else textSize.cy = workRectHeight;
+
+ textSize.cx += 4; // CORRECTION: for effects ???
+ textSize.cy += 4; // CORRECTION: for effects ???
+
+ if (textSize.cx > 0 && textSize.cy > 0) { // Ok we need to paint
+ // probably here are mess ofscreen and temp buff dc
+
+ //Create bitmap image for offscreen
+ uint8_t *bits = nullptr;
+ HBITMAP hbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bits);
+ if (bits != nullptr) {
+ HBITMAP holdbmp = (HBITMAP)SelectObject(hOffscreenDC, hbmp);
+
+ //Create buffer bitmap image for temp text
+ uint8_t *bufbits = nullptr;
+ HBITMAP bufbmp = ske_CreateDIB32Point(textSize.cx, textSize.cy, (void**)&bufbits);
+ if (bufbits != nullptr) {
+ HDC bufDC = CreateCompatibleDC(hDC);
+ HBITMAP bufoldbmp = (HBITMAP)SelectObject(bufDC, bufbmp);
+ HFONT hOldBufFont = (HFONT)SelectObject(bufDC, hFont);
+ SetBkColor(bufDC, RGB(0, 0, 0));
+ SetTextColor(bufDC, RGB(255, 255, 255));
+
+ // Copy from destination to temp buffer
+ BitBlt(hOffscreenDC, 0, 0, textSize.cx, textSize.cy, hDC, workRect.left + drx - 2, workRect.top + dry - 2, SRCCOPY);
+
+ //Draw text on offscreen bitmap
+ TextOut(bufDC, 2, 2, lpWorkString, nCount);
+
+ MODERNEFFECT effect;
+ if (ske_GetTextEffect(hDC, &effect))
+ ske_DrawTextEffect(bits, bufbits, textSize.cx, textSize.cy, &effect);
+
+ // RenderText
+ RECT drawRect;
+ drawRect.left = 0; drawRect.top = 0;
+ drawRect.right = textSize.cx;
+ drawRect.bottom = textSize.cy;
+
+ uint32_t width = textSize.cx;
+ uint32_t heigh = textSize.cy;
+
+ uint8_t *pDestScanLine, *pBufScanLine, *pix, *bufpix;
+
+ uint8_t al = 255 - ((uint8_t)(ARGBcolor >> 24));
+ uint8_t r = GetRValue(ARGBcolor);
+ uint8_t g = GetGValue(ARGBcolor);
+ uint8_t b = GetBValue(ARGBcolor);
+
+ for (uint32_t y = 2; y < heigh - 2; y++) {
+ int lineBytes = y * (width << 2);
+
+ pDestScanLine = bits + lineBytes;
+ pBufScanLine = bufbits + lineBytes;
+
+ for (uint32_t x = 2; x < width - 2; x++) {
+ pix = pDestScanLine + (x << 2);
+ bufpix = pBufScanLine + (x << 2);
+
+ // Monochromatic
+ uint8_t bx = gammaTbl[bufpix[0]];
+ uint8_t gx = gammaTbl[bufpix[1]];
+ uint8_t rx = gammaTbl[bufpix[2]];
+
+ if (al != 255) {
+ bx *= al / 255;
+ gx *= al / 255;
+ rx *= al / 255;
+ }
+
+ uint8_t ax = (uint8_t)(((uint32_t)rx * 77 + (uint32_t)gx * 151 + (uint32_t)bx * 28 + 128) / 256);
+ if (ax) {
+ //Normalize components to gray
+ uint8_t axx = 255 - ((r + g + b) >> 2); // Coefficient of grayance, more white font - more gray edges
+ uint16_t atx = ax * (255 - axx);
+ bx = (atx + bx * axx) / 255;
+ gx = (atx + gx * axx) / 255;
+ rx = (atx + rx * axx) / 255;
+
+ short brx = (short)((b - pix[0])*bx / 255);
+ short grx = (short)((g - pix[1])*gx / 255);
+ short rrx = (short)((r - pix[2])*rx / 255);
+
+ pix[0] += brx;
+ pix[1] += grx;
+ pix[2] += rrx;
+ pix[3] = (uint8_t)(ax + (uint8_t)(255 - ax)*pix[3] / 255);
+ }
+ }
+ }
+
+ // Blit to destination
+ BitBlt(hDC, workRect.left + drx - 2, workRect.top + dry - 2, textSize.cx, textSize.cy, hOffscreenDC, 0, 0, SRCCOPY);
+
+ //free resources
+ SelectObject(bufDC, bufoldbmp);
+ DeleteObject(bufbmp);
+ SelectObject(bufDC, hOldBufFont);
+ DeleteDC(bufDC);
+ }
+ SelectObject(hOffscreenDC, holdbmp);
+ DeleteObject(hbmp);
+ }
+ }
+
+ // Final cleanup
+ SelectObject(hOffscreenDC, hOldOffscreenFont);
+ DeleteDC(hOffscreenDC);
+
+ if (destHasNotDIB)
+ mir_free(pDestBits);
+
+ if (bNeedFreeWorkString)
+ mir_free((void*)lpWorkString);
+
+ return 0;
+}
+
+static int ske_DrawTextWithEffectWorker(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format, FONTEFFECT *effect)
+{
+ if (format & DT_CALCRECT)
+ return DrawText(hdc, lpString, nCount, lpRect, format);
+
+ if (format & DT_RTLREADING)
+ SetTextAlign(hdc, TA_RTLREADING);
+
+ uint32_t color = GetTextColor(hdc);
+ RECT r = *lpRect;
+ OffsetRect(&r, 1, 1);
+ uint32_t form = format;
+ if (effect && effect->effectIndex)
+ ske_SelectTextEffect(hdc, effect->effectIndex - 1, effect->baseColour, effect->secondaryColour);
+
+ int res = ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color);
+
+ if (effect && effect->effectIndex)
+ ske_ResetTextEffect(hdc);
+
+ return res;
+}
+
+INT_PTR ske_Service_DrawTextWithEffect(WPARAM wParam, LPARAM)
+{
+ DrawTextWithEffectParam *p = (DrawTextWithEffectParam *)wParam;
+ return ske_DrawTextWithEffectWorker(p->hdc, p->lpchText, p->cchText, p->lprc, p->dwDTFormat, p->pEffect);
+}
+
+BOOL ske_DrawText(HDC hdc, LPCTSTR lpString, int nCount, RECT *lpRect, UINT format)
+{
+ RECT r = *lpRect;
+ OffsetRect(&r, 1, 1);
+ if (format & DT_RTLREADING)
+ SetTextAlign(hdc, TA_RTLREADING);
+ if (format & DT_CALCRECT)
+ return DrawText(hdc, lpString, nCount, lpRect, format);
+ if (format & DT_FORCENATIVERENDER || g_CluiData.fDisableSkinEngine)
+ return DrawText(hdc, lpString, nCount, lpRect, format & ~DT_FORCENATIVERENDER);
+
+ uint32_t form = format;
+ uint32_t color = GetTextColor(hdc);
+ return ske_AlphaTextOut(hdc, lpString, nCount, lpRect, form, color);
+}
+
+HICON ske_ImageList_GetIcon(HIMAGELIST himl, int i)
+{
+ IMAGEINFO imi = {};
+ BITMAP bm = { 0 };
+ if (i != -1) {
+ ImageList_GetImageInfo(himl, i, &imi);
+ GetObject(imi.hbmImage, sizeof(bm), &bm);
+ // stupid bug of Microsoft
+ // Icons bitmaps are not premultiplied
+ // So Imagelist_AddIcon - premultiply alpha
+ // But incorrect - it is possible that alpha will
+ // be less than color and
+ // ImageList_GetIcon will return overflowed colors
+ // TODO: Direct draw Icon from imagelist without
+ // extracting of icon
+ if (bm.bmBitsPixel == 32) {
+ uint8_t *bits = (uint8_t*)bm.bmBits;
+ if (!bits) {
+ bits = (uint8_t*)mir_alloc(bm.bmWidthBytes*bm.bmHeight);
+ GetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits);
+ }
+
+ uint8_t *bcbits = bits + (bm.bmHeight - imi.rcImage.bottom)*bm.bmWidthBytes + (imi.rcImage.left*bm.bmBitsPixel >> 3);
+ for (int iy = 0; iy < imi.rcImage.bottom - imi.rcImage.top; iy++) {
+ int x;
+ // Dummy microsoft fix - alpha can be less than r,g or b
+ // Looks like color channels in icons should be non-premultiplied with alpha
+ // But AddIcon store it premultiplied (incorrectly cause can be Alpha == 7F, but R,G or B == 80
+ // So i check that alpha is 0x7F and set it to 0x80
+ uint32_t *c = ((uint32_t*)bcbits);
+ for (x = 0; x < imi.rcImage.right - imi.rcImage.left; x++) {
+ uint32_t val = *c;
+ uint8_t a = (uint8_t)((val) >> 24);
+ if (a != 0) {
+ uint8_t r = (uint8_t)((val & 0xFF0000) >> 16);
+ uint8_t g = (uint8_t)((val & 0xFF00) >> 8);
+ uint8_t b = (uint8_t)(val & 0xFF);
+ if (a < r || a < g || a < b) {
+ a = max(max(r, g), b);
+ val = a << 24 | r << 16 | g << 8 | b;
+ *c = val;
+ }
+ }
+ c++;
+ }
+ bcbits += bm.bmWidthBytes;
+ }
+
+ if (!bm.bmBits) {
+ SetBitmapBits(imi.hbmImage, bm.bmWidthBytes*bm.bmHeight, bits);
+ mir_free(bits);
+ }
+ }
+ }
+ return ImageList_GetIcon(himl, i, ILD_NORMAL);
+}
+
+BOOL ske_ImageList_DrawEx(HIMAGELIST himl, int i, HDC hdcDst, int x, int y, int dx, int dy, COLORREF rgbBk, COLORREF rgbFg, UINT fStyle)
+{
+ // the routine to directly draw icon from image list without creating icon from there - should be some faster
+ if (i < 0)
+ return FALSE;
+
+ if (g_CluiData.fDisableSkinEngine)
+ return ImageList_DrawEx(himl, i, hdcDst, x, y, dx, dy, rgbBk, rgbFg, fStyle);
+
+ uint8_t alpha;
+ if (fStyle & ILD_BLEND25)
+ alpha = 64;
+ else if (fStyle & ILD_BLEND50)
+ alpha = 128;
+ else
+ alpha = 255;
+
+ HICON hIcon = ske_ImageList_GetIcon(himl, i);
+ if (hIcon == nullptr)
+ return FALSE;
+
+ ske_DrawIconEx(hdcDst, x, y, hIcon, dx ? dx : GetSystemMetrics(SM_CXSMICON), dy ? dy : GetSystemMetrics(SM_CYSMICON), 0, nullptr, DI_NORMAL | (alpha << 24));
+ DestroyIcon(hIcon);
+ return TRUE;
+}
+
+static INT_PTR ske_Service_DrawIconEx(WPARAM wParam, LPARAM)
+{
+ DrawIconFixParam *p = (DrawIconFixParam*)wParam;
+ if (!p)
+ return 0;
+
+ return ske_DrawIconEx(p->hdc, p->xLeft, p->yTop, p->hIcon, p->cxWidth, p->cyWidth, p->istepIfAniCur, p->hbrFlickerFreeDraw, p->diFlags);
+}
+
+
+BOOL ske_DrawIconEx(HDC hdcDst, int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, HBRUSH hbrFlickerFreeDraw, UINT diFlags)
+{
+ ICONINFO ici;
+ uint8_t alpha = (uint8_t)((diFlags & 0xFF000000) >> 24);
+
+ HBITMAP tBmp = nullptr;
+ uint8_t *imbits, *imimagbits, *immaskbits;
+ uint8_t *t1, *t2, *t3;
+
+ //lockimagelist
+ uint8_t hasmask = FALSE, no32bit = FALSE, noMirrorMask = FALSE, hasalpha = FALSE;
+ alpha = alpha ? alpha : 255;
+
+ if (g_CluiData.fDisableSkinEngine && !(diFlags & 0x80))
+ return DrawIconEx(hdcDst, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags & 0xFFFF7F);
+
+ if (!GetIconInfo(hIcon, &ici))
+ return 0;
+
+ BITMAP imbt;
+ GetObject(ici.hbmColor, sizeof(BITMAP), &imbt);
+ if (imbt.bmWidth*imbt.bmHeight == 0) {
+ DeleteObject(ici.hbmColor);
+ DeleteObject(ici.hbmMask);
+ return 0;
+ }
+
+ BITMAP immaskbt;
+ GetObject(ici.hbmMask, sizeof(BITMAP), &immaskbt);
+ uint32_t cy = imbt.bmHeight;
+
+ if (imbt.bmBitsPixel != 32) {
+ no32bit = TRUE;
+ HDC tempDC1 = CreateCompatibleDC(hdcDst);
+ tBmp = ske_CreateDIB32(imbt.bmWidth, imbt.bmHeight);
+ if (tBmp) {
+ GetObject(tBmp, sizeof(BITMAP), &imbt);
+ HBITMAP otBmp = (HBITMAP)SelectObject(tempDC1, tBmp);
+ DrawIconEx(tempDC1, 0, 0, hIcon, imbt.bmWidth, imbt.bmHeight, istepIfAniCur, hbrFlickerFreeDraw, DI_IMAGE);
+ noMirrorMask = TRUE;
+ SelectObject(tempDC1, otBmp);
+ }
+ DeleteDC(tempDC1);
+ }
+
+ bool NoDIBImage = (imbt.bmBits == nullptr);
+ if (NoDIBImage) {
+ imimagbits = (uint8_t*)mir_alloc(cy*imbt.bmWidthBytes);
+ GetBitmapBits(ici.hbmColor, cy*imbt.bmWidthBytes, (void*)imimagbits);
+ }
+ else imimagbits = (uint8_t*)imbt.bmBits;
+
+ if (immaskbt.bmBits == nullptr) {
+ immaskbits = (uint8_t*)mir_alloc(cy*immaskbt.bmWidthBytes);
+ GetBitmapBits(ici.hbmMask, cy*immaskbt.bmWidthBytes, (void*)immaskbits);
+ }
+ else immaskbits = (uint8_t*)immaskbt.bmBits;
+
+ HDC imDC = CreateCompatibleDC(hdcDst);
+ uint32_t icy = imbt.bmHeight;
+ uint32_t cx = imbt.bmWidth;
+ HBITMAP imBmp = ske_CreateDIB32Point(cx, icy, (void**)&imbits);
+ HBITMAP oldBmp = (HBITMAP)SelectObject(imDC, imBmp);
+ if (imbits != nullptr && imimagbits != nullptr && immaskbits != nullptr) {
+ int x, y;
+ int mwb = immaskbt.bmWidthBytes;
+ int mwb2 = imbt.bmWidthBytes;
+ int bottom = icy;
+ int right = cx;
+ int top = 0;
+ int h = icy;
+ for (y = top; (y < bottom) && !hasmask; y++) {
+ t1 = immaskbits + y*mwb;
+ for (x = 0; (x < mwb) && !hasmask; x++)
+ hasmask |= (*(t1 + x) != 0);
+ }
+
+ for (y = top; (y < bottom) && !hasalpha; y++) {
+ t1 = imimagbits + (cy - y - 1)*mwb2;
+ for (x = 0; (x < right) && !hasalpha; x++)
+ hasalpha |= (*(t1 + (x << 2) + 3) != 0);
+ }
+
+ for (y = 0; y < (int)icy; y++) {
+ t1 = imimagbits + (h - y - 1 - top)*mwb2;
+ t2 = imbits + (!no32bit ? y : (icy - y - 1))*mwb2;
+ t3 = immaskbits + (noMirrorMask ? y : (h - y - 1 - top))*mwb;
+ for (x = 0; x < right; x++) {
+ uint8_t mask = 0;
+ uint8_t a = 0;
+ uint32_t *src = (uint32_t*)(t1 + (x << 2));
+ uint32_t *dest = (uint32_t*)(t2 + (x << 2));
+ if (hasalpha && !hasmask)
+ a = ((uint8_t*)src)[3];
+ else {
+ mask = ((1 << (7 - x % 8))&(*(t3 + (x >> 3)))) != 0;
+ if (mask) {
+ if (!hasalpha) {
+ *dest = 0;
+ continue;
+ }
+
+ if (((uint8_t*)src)[3]>0)
+ a = ((uint8_t*)src)[3];
+ else
+ a = 0;
+ }
+ else if (hasalpha || hasmask)
+ a = (((uint8_t*)src)[3] > 0 ? ((uint8_t*)src)[3] : 255);
+ else if (!hasalpha && !hasmask)
+ a = 255;
+ else { *dest = 0; continue; }
+ }
+ if (a > 0) {
+ ((uint8_t*)dest)[3] = a;
+ ((uint8_t*)dest)[0] = ((uint8_t*)src)[0] * a / 255;
+ ((uint8_t*)dest)[1] = ((uint8_t*)src)[1] * a / 255;
+ ((uint8_t*)dest)[2] = ((uint8_t*)src)[2] * a / 255;
+ }
+ else *dest = 0;
+ }
+ }
+ }
+
+ BLENDFUNCTION bf = { AC_SRC_OVER, diFlags & 128, alpha, AC_SRC_ALPHA };
+ ske_AlphaBlend(hdcDst, xLeft, yTop, cxWidth, cyWidth, imDC, 0, 0, cx, icy, bf);
+
+ if (immaskbt.bmBits == nullptr) mir_free(immaskbits);
+ if (imbt.bmBits == nullptr) mir_free(imimagbits);
+ SelectObject(imDC, oldBmp);
+ DeleteObject(imBmp);
+ if (no32bit)DeleteObject(tBmp);
+ DeleteObject(ici.hbmColor);
+ DeleteObject(ici.hbmMask);
+ SelectObject(imDC, GetStockObject(DEFAULT_GUI_FONT));
+ DeleteDC(imDC);
+ return 1;
+}
+
+int ske_PrepareImageButDontUpdateIt(RECT *r)
+{
+ if (!g_CluiData.fLayered)
+ return ske_ReCreateBackImage(FALSE, r);
+
+ mutex_bLockUpdate = 1;
+ ske_DrawNonFramedObjects(TRUE, r);
+ ske_ValidateFrameImageProc(r);
+ mutex_bLockUpdate = 0;
+ return 0;
+}
+
+int ske_RedrawCompleteWindow()
+{
+ if (g_CluiData.fLayered) {
+ ske_DrawNonFramedObjects(TRUE, nullptr);
+ CallService(MS_SKINENG_INVALIDATEFRAMEIMAGE, 0, 0);
+ }
+ else RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+
+ return 0;
+}
+
+// Request to repaint frame or change/drop callback data
+// wParam = hWnd of called frame
+// lParam = pointer to sPaintRequest (or nullptr to redraw all)
+// return 2 - already queued, data updated, 1-have been queued, 0 - failure
+
+static INT_PTR ske_Service_UpdateFrameImage(WPARAM wParam, LPARAM) // Immideately recall paint routines for frame and refresh image
+{
+ if (MirandaLoading())
+ return 0;
+
+ RECT wnd;
+ bool NoCancelPost = false;
+ bool IsAnyQueued = false;
+ if (!g_CluiData.mutexOnEdgeSizing)
+ GetWindowRect(g_clistApi.hwndContactList, &wnd);
+ else
+ wnd = g_rcEdgeSizingRect;
+
+ if (!g_CluiData.fLayered) {
+ RedrawWindow((HWND)wParam, nullptr, nullptr, RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+ return 0;
+ }
+
+ if (g_pCachedWindow == nullptr) ske_ValidateFrameImageProc(&wnd);
+ else if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) ske_ValidateFrameImageProc(&wnd);
+ else if (wParam == 0) ske_ValidateFrameImageProc(&wnd);
+ else { // all Ok Update Single Frame
+ // TO BE LOCKED OR PROXIED
+ FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam);
+ if (!frm)
+ ske_ValidateFrameImageProc(&wnd);
+ // Validate frame, update window image and remove it from queue
+ else {
+ if (frm->UpdateRgn) {
+ DeleteObject(frm->UpdateRgn);
+ frm->UpdateRgn = nullptr;
+ }
+ ske_ValidateSingleFrameImage(frm, 0);
+ ske_UpdateWindowImage();
+ NoCancelPost = 1;
+ //-- Remove frame from queue
+ if (flag_bUpdateQueued) {
+ frm->bQueued = 0;
+ for (int i = 0; i < g_nFramesCount; i++)
+ if (IsAnyQueued |= g_pfwFrames[i].bQueued)
+ break;
+ }
+ }
+ }
+
+ if ((!NoCancelPost || !IsAnyQueued) && flag_bUpdateQueued) { // no any queued updating cancel post or need to cancel post
+ flag_bUpdateQueued = 0;
+ g_bPostWasCanceled = true;
+ }
+ return 1;
+}
+
+static INT_PTR ske_Service_InvalidateFrameImage(WPARAM wParam, LPARAM lParam) // Post request for updating
+{
+ if (MirandaLoading()) return 0;
+
+ if (wParam) {
+ FRAMEWND *frm = FindFrameByItsHWND((HWND)wParam);
+ sPaintRequest *pr = (sPaintRequest*)lParam;
+ if (!g_CluiData.fLayered || (frm && frm->floating))
+ return InvalidateRect((HWND)wParam, pr ? (RECT*)&(pr->rcUpdate) : nullptr, FALSE);
+
+ if (frm) {
+ if (frm->PaintCallbackProc != nullptr) {
+ frm->PaintData = (sPaintRequest *)pr;
+ frm->bQueued = 1;
+ if (pr) {
+ HRGN r2;
+ if (!IsRectEmpty(&pr->rcUpdate)) {
+ RECT rcClient;
+ RECT rcUpdate;
+ GetClientRect(frm->hWnd, &rcClient);
+ IntersectRect(&rcUpdate, &rcClient, &pr->rcUpdate);
+ if (IsRectEmpty(&rcUpdate))
+ return 0;
+ r2 = CreateRectRgn(rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom);
+ }
+ else {
+ RECT r;
+ GetClientRect(frm->hWnd, &r);
+ r2 = CreateRectRgn(r.left, r.top, r.right, r.bottom);
+ }
+
+ if (!frm->UpdateRgn) {
+ frm->UpdateRgn = CreateRectRgn(0, 0, 1, 1);
+ CombineRgn(frm->UpdateRgn, r2, nullptr, RGN_COPY);
+ }
+ else CombineRgn(frm->UpdateRgn, frm->UpdateRgn, r2, RGN_OR);
+ DeleteObject(r2);
+ }
+ }
+ }
+ else Sync(QueueAllFramesUpdating, true);
+ }
+ else Sync(QueueAllFramesUpdating, true);
+
+ if (!flag_bUpdateQueued || g_bPostWasCanceled)
+ if (PostMessage(g_clistApi.hwndContactList, UM_UPDATE, 0, 0)) {
+ flag_bUpdateQueued = 1;
+ g_bPostWasCanceled = false;
+ }
+ return 1;
+}
+
+static int ske_ValidateSingleFrameImage(FRAMEWND *Frame, BOOL SkipBkgBlitting) // Calling frame paint proc
+{
+ if (!g_pCachedWindow) { TRACE("ske_ValidateSingleFrameImage calling without cached\n"); return 0; }
+ if (Frame->hWnd == (HWND)-1 && !Frame->PaintCallbackProc) { TRACE("ske_ValidateSingleFrameImage calling without FrameProc\n"); return 0; }
+
+ // if ok update image
+ RECT rcPaint, wnd;
+ RECT ru = { 0 };
+ int w1, h1, x1, y1;
+
+ CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &wnd);
+ rcPaint = Frame->wndSize;
+ {
+ int dx, dy, bx, by;
+ if (g_CluiData.mutexOnEdgeSizing) {
+ dx = rcPaint.left - wnd.left;
+ dy = rcPaint.top - wnd.top;
+ bx = rcPaint.right - wnd.right;
+ by = rcPaint.bottom - wnd.bottom;
+ wnd = g_rcEdgeSizingRect;
+ rcPaint.left = wnd.left + dx;
+ rcPaint.top = wnd.top + dy;
+ rcPaint.right = wnd.right + bx;
+ rcPaint.bottom = wnd.bottom + by;
+ }
+ }
+
+ int w = rcPaint.right - rcPaint.left;
+ int h = rcPaint.bottom - rcPaint.top;
+ if (w <= 0 || h <= 0) {
+ TRACE("Frame size smaller than 0\n");
+ return 0;
+ }
+ int x = rcPaint.left;
+ int y = rcPaint.top;
+ HDC hdc = CreateCompatibleDC(g_pCachedWindow->hImageDC);
+ HBITMAP n = ske_CreateDIB32(w, h);
+ HBITMAP o = (HBITMAP)SelectObject(hdc, n);
+
+ if (Frame->UpdateRgn && !SkipBkgBlitting) {
+ GetRgnBox(Frame->UpdateRgn, &ru);
+ {
+ RECT rc;
+ GetClientRect(Frame->hWnd, &rc);
+ if (ru.top < 0) ru.top = 0;
+ if (ru.left < 0) ru.left = 0;
+ if (ru.right > rc.right) ru.right = rc.right;
+ if (ru.bottom > rc.bottom) ru.bottom = rc.bottom;
+ }
+ if (!IsRectEmpty(&ru)) {
+ x1 = ru.left;
+ y1 = ru.top;
+ w1 = ru.right - ru.left;
+ h1 = ru.bottom - ru.top;
+ }
+ else {
+ x1 = 0; y1 = 0; w1 = w; h1 = h;
+ }
+
+ // copy image at hdc
+ BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY);
+
+ Frame->PaintCallbackProc(Frame->hWnd, hdc, &ru, Frame->UpdateRgn, Frame->dwFlags, Frame->PaintData);
+ }
+ else {
+ RECT r;
+ GetClientRect(Frame->hWnd, &r);
+ HRGN rgnUpdate = CreateRectRgn(r.left, r.top, r.right, r.bottom);
+ ru = r;
+ if (!IsRectEmpty(&ru)) {
+ x1 = ru.left;
+ y1 = ru.top;
+ w1 = ru.right - ru.left;
+ h1 = ru.bottom - ru.top;
+ }
+ else {
+ x1 = 0; y1 = 0; w1 = w; h1 = h;
+ }
+
+ // copy image at hdc
+ if (SkipBkgBlitting) //image already at foreground
+ BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hImageDC, x + x1, y + y1, SRCCOPY);
+ else
+ BitBlt(hdc, x1, y1, w1, h1, g_pCachedWindow->hBackDC, x + x1, y + y1, SRCCOPY);
+
+ Frame->PaintCallbackProc(Frame->hWnd, hdc, &r, rgnUpdate, Frame->dwFlags, Frame->PaintData);
+ ru = r;
+ DeleteObject(rgnUpdate);
+ }
+ DeleteObject(Frame->UpdateRgn);
+ Frame->UpdateRgn = nullptr;
+
+ if (!IsRectEmpty(&ru)) {
+ x1 = ru.left;
+ y1 = ru.top;
+ w1 = ru.right - ru.left;
+ h1 = ru.bottom - ru.top;
+ }
+ else {
+ x1 = 0; y1 = 0; w1 = w; h1 = h;
+ }
+
+ BitBlt(g_pCachedWindow->hImageDC, x + x1, y + y1, w1, h1, hdc, x1, y1, SRCCOPY);
+
+ if (GetWindowLongPtr(Frame->hWnd, GWL_STYLE) & WS_VSCROLL) {
+ //Draw vertical scroll bar
+ //
+ SCROLLBARINFO si = { 0 };
+ si.cbSize = sizeof(SCROLLBARINFO);
+ GetScrollBarInfo(Frame->hWnd, OBJID_VSCROLL, &si);
+
+ RECT rLine = (si.rcScrollBar);
+ RECT rUpBtn = rLine;
+ RECT rDnBtn = rLine;
+ RECT rThumb = rLine;
+
+ rUpBtn.bottom = rUpBtn.top + si.dxyLineButton;
+ rDnBtn.top = rDnBtn.bottom - si.dxyLineButton;
+ rThumb.top = rLine.top + si.xyThumbTop;
+ rThumb.bottom = rLine.top + si.xyThumbBottom;
+
+ int dx = Frame->wndSize.right - rLine.right;
+ int dy = -rLine.top + Frame->wndSize.top;
+
+ OffsetRect(&rLine, dx, dy);
+ OffsetRect(&rUpBtn, dx, dy);
+ OffsetRect(&rDnBtn, dx, dy);
+ OffsetRect(&rThumb, dx, dy);
+ BitBlt(g_pCachedWindow->hImageDC, rLine.left, rLine.top, rLine.right - rLine.left, rLine.bottom - rLine.top, g_pCachedWindow->hBackDC, rLine.left, rLine.top, SRCCOPY);
+
+ char req[255];
+ mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Back", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rLine, &rLine, req);
+ mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=Thumb", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rThumb, &rThumb, req);
+ mir_snprintf(req, "Main,ID=ScrollBar, Frame=%S,Part=UpLineButton", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rUpBtn, &rUpBtn, req);
+ mir_snprintf(req, "Main,ID=ScrollBar,Frame=%S,Part=DownLineButton", Frame->name);
+ SkinDrawGlyph(g_pCachedWindow->hImageDC, &rDnBtn, &rDnBtn, req);
+ }
+
+ SelectObject(hdc, o);
+ DeleteObject(n);
+ DeleteDC(hdc);
+ return 1;
+}
+
+int ske_BltBackImage(HWND destHWND, HDC destDC, RECT *BltClientRect)
+{
+ POINT ptMainWnd = { 0 };
+ POINT ptChildWnd = { 0 };
+ RECT w = { 0 };
+ if (g_CluiData.fDisableSkinEngine) {
+ FillRect(destDC, BltClientRect, GetSysColorBrush(COLOR_3DFACE));
+ return 0;
+ }
+ ske_ReCreateBackImage(FALSE, nullptr);
+ if (BltClientRect) w = *BltClientRect;
+ else GetClientRect(destHWND, &w);
+ ptChildWnd.x = w.left;
+ ptChildWnd.y = w.top;
+ ClientToScreen(destHWND, &ptChildWnd);
+ ClientToScreen(g_clistApi.hwndContactList, &ptMainWnd);
+ //TODO if main not relative to client area
+ return BitBlt(destDC, w.left, w.top, (w.right - w.left), (w.bottom - w.top), g_pCachedWindow->hBackDC, (ptChildWnd.x - ptMainWnd.x), (ptChildWnd.y - ptMainWnd.y), SRCCOPY);
+
+}
+
+int ske_ReCreateBackImage(BOOL Erase, RECT *w)
+{
+ RECT wnd = { 0 };
+ BOOL IsNewCache = 0;
+ if (g_CluiData.fDisableSkinEngine) return 0;
+ GetClientRect(g_clistApi.hwndContactList, &wnd);
+ if (w) wnd = *w;
+ //-- Check cached.
+ if (g_pCachedWindow == nullptr) {
+ //-- Create New Cache
+ g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
+ g_pCachedWindow->hScreenDC = GetDC(nullptr);
+ g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) {
+ g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB);
+ g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB);
+ }
+ IsNewCache = 1;
+ }
+
+ if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) {
+ HBITMAP hb1 = nullptr, hb2 = nullptr;
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ if (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0) {
+ hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ SelectObject(g_pCachedWindow->hImageDC, hb1);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ }
+ else {
+ SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageOld);
+ SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackOld);
+ }
+ if (g_pCachedWindow->hImageDIB) DeleteObject(g_pCachedWindow->hImageDIB);
+ if (g_pCachedWindow->hBackDIB) DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hImageDIB = hb1;
+ g_pCachedWindow->hBackDIB = hb2;
+ IsNewCache = 1;
+ }
+
+ if ((Erase || IsNewCache) && (g_pCachedWindow->Width != 0 && g_pCachedWindow->Height != 0)) {
+ HBITMAP hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hBackDIB = hb2;
+ FillRect(g_pCachedWindow->hBackDC, &wnd, GetSysColorBrush(COLOR_BTNFACE));
+ SkinDrawGlyph(g_pCachedWindow->hBackDC, &wnd, &wnd, "Main,ID=Background,Opt=Non-Layered");
+ ske_SetRectOpaque(g_pCachedWindow->hBackDC, &wnd);
+ }
+ return 1;
+}
+
+int ske_DrawNonFramedObjects(BOOL Erase, RECT *r)
+{
+ RECT w, wnd;
+ if (r) w = *r;
+ else CLUI_SizingGetWindowRect(g_clistApi.hwndContactList, &w);
+ if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr);
+ if (g_pCachedWindow == nullptr)
+ return ske_ValidateFrameImageProc(&w);
+
+ wnd = w;
+ OffsetRect(&w, -w.left, -w.top);
+ if (Erase) {
+ HBITMAP hb2;
+ hb2 = ske_CreateDIB32(g_pCachedWindow->Width, g_pCachedWindow->Height);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hBackDIB = hb2;
+ }
+
+ SkinDrawGlyph(g_pCachedWindow->hBackDC, &w, &w, "Main,ID=Background");
+
+ //--Draw frames captions
+ for (int i = 0; i < g_nFramesCount; i++) {
+ if (g_pfwFrames[i].TitleBar.ShowTitleBar && g_pfwFrames[i].visible && !g_pfwFrames[i].floating) {
+ RECT rc;
+ SetRect(&rc, g_pfwFrames[i].wndSize.left, g_pfwFrames[i].wndSize.top - g_nTitleBarHeight - g_CluiData.nGapBetweenTitlebar, g_pfwFrames[i].wndSize.right, g_pfwFrames[i].wndSize.top - g_CluiData.nGapBetweenTitlebar);
+ Sync(DrawTitleBar, g_pCachedWindow->hBackDC, &rc, g_pfwFrames[i].id);
+ }
+ }
+ g_mutex_bLockUpdating = 1;
+
+ flag_bJustDrawNonFramedObjects = 1;
+ return 0;
+}
+
+// Calling queued frame paint procs and refresh image
+int ske_ValidateFrameImageProc(RECT *r)
+{
+ RECT wnd = { 0 };
+ BOOL IsNewCache = 0;
+ BOOL IsForceAllPainting = 0;
+ if (r) wnd = *r;
+ else GetWindowRect(g_clistApi.hwndContactList, &wnd);
+ if (wnd.right - wnd.left == 0 || wnd.bottom - wnd.top == 0)
+ return 0;
+
+ g_mutex_bLockUpdating = 1;
+
+ //-- Check cached.
+ if (g_pCachedWindow == nullptr) {
+ //-- Create New Cache
+ g_pCachedWindow = (CURRWNDIMAGEDATA*)mir_calloc(sizeof(CURRWNDIMAGEDATA));
+ g_pCachedWindow->hScreenDC = GetDC(nullptr);
+ g_pCachedWindow->hBackDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->hImageDC = CreateCompatibleDC(g_pCachedWindow->hScreenDC);
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ g_pCachedWindow->hImageDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ g_pCachedWindow->hBackDIB = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ g_pCachedWindow->hImageOld = (HBITMAP)SelectObject(g_pCachedWindow->hImageDC, g_pCachedWindow->hImageDIB);
+ g_pCachedWindow->hBackOld = (HBITMAP)SelectObject(g_pCachedWindow->hBackDC, g_pCachedWindow->hBackDIB);
+ IsNewCache = 1;
+ }
+ if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) {
+ HBITMAP hb1, hb2;
+ g_pCachedWindow->Width = wnd.right - wnd.left;
+ g_pCachedWindow->Height = wnd.bottom - wnd.top;
+ hb1 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hImageDIBByte));
+ hb2 = ske_CreateDIB32Point(g_pCachedWindow->Width, g_pCachedWindow->Height, (void**)&(g_pCachedWindow->hBackDIBByte));
+ SelectObject(g_pCachedWindow->hImageDC, hb1);
+ SelectObject(g_pCachedWindow->hBackDC, hb2);
+ DeleteObject(g_pCachedWindow->hImageDIB);
+ DeleteObject(g_pCachedWindow->hBackDIB);
+ g_pCachedWindow->hImageDIB = hb1;
+ g_pCachedWindow->hBackDIB = hb2;
+ IsNewCache = 1;
+ }
+ if (IsNewCache) {
+ ske_DrawNonFramedObjects(0, &wnd);
+ IsForceAllPainting = 1;
+ }
+ if (flag_bJustDrawNonFramedObjects) {
+ IsForceAllPainting = 1;
+ flag_bJustDrawNonFramedObjects = 0;
+ }
+ if (IsForceAllPainting) {
+ BitBlt(g_pCachedWindow->hImageDC, 0, 0, g_pCachedWindow->Width, g_pCachedWindow->Height, g_pCachedWindow->hBackDC, 0, 0, SRCCOPY);
+ Sync(QueueAllFramesUpdating, true);
+ }
+ //-- Validating frames
+ for (int i = 0; i < g_nFramesCount; i++)
+ if (g_pfwFrames[i].PaintCallbackProc && g_pfwFrames[i].visible && !g_pfwFrames[i].floating)
+ if (g_pfwFrames[i].bQueued || IsForceAllPainting)
+ ske_ValidateSingleFrameImage(&g_pfwFrames[i], IsForceAllPainting);
+
+ g_mutex_bLockUpdating = 1;
+ ModernSkinButtonRedrawAll();
+ g_mutex_bLockUpdating = 0;
+ if (!mutex_bLockUpdate)
+ ske_UpdateWindowImageRect(&wnd);
+
+ //-- Clear queue
+ Sync(QueueAllFramesUpdating, false);
+ flag_bUpdateQueued = 0;
+ g_bPostWasCanceled = false;
+ return 1;
+}
+
+int ske_UpdateWindowImage()
+{
+ if (MirandaExiting())
+ return 0;
+
+ if (g_CluiData.fLayered) {
+ RECT r;
+ GetWindowRect(g_clistApi.hwndContactList, &r);
+ return ske_UpdateWindowImageRect(&r);
+ }
+ else ske_ReCreateBackImage(FALSE, nullptr);
+ ske_ApplyTranslucency();
+ return 0;
+}
+
+int ske_UpdateWindowImageRect(RECT *r) // Update window with current image and
+{
+ //if not validity -> ValidateImageProc
+ //else Update using current alpha
+ RECT wnd = *r;
+
+ if (!g_CluiData.fLayered) return ske_ReCreateBackImage(FALSE, nullptr);
+ if (g_pCachedWindow == nullptr) return ske_ValidateFrameImageProc(&wnd);
+ if (g_pCachedWindow->Width != wnd.right - wnd.left || g_pCachedWindow->Height != wnd.bottom - wnd.top) return ske_ValidateFrameImageProc(&wnd);
+ if (g_bFullRepaint) {
+ g_bFullRepaint = false;
+ return ske_ValidateFrameImageProc(&wnd);
+ }
+ ske_JustUpdateWindowImageRect(&wnd);
+ return 0;
+}
+
+void ske_ApplyTranslucency()
+{
+ int IsTransparancy;
+ HWND hwnd = g_clistApi.hwndContactList;
+ BOOL layered = (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) ? TRUE : FALSE;
+
+ IsTransparancy = g_CluiData.fSmoothAnimation || g_bTransparentFlag;
+ if (!g_bTransparentFlag && !g_CluiData.fSmoothAnimation && g_CluiData.bCurrentAlpha != 0)
+ g_CluiData.bCurrentAlpha = 255;
+
+ if (!g_CluiData.fLayered && IsTransparancy) {
+ if (!layered)
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), (uint8_t)g_CluiData.bCurrentAlpha, LWA_ALPHA);
+ }
+
+ AniAva_RedrawAllAvatars(FALSE);
+ return;
+}
+
+int ske_JustUpdateWindowImage()
+{
+ RECT r;
+ if (!g_CluiData.fLayered) {
+ ske_ApplyTranslucency();
+ return 0;
+ }
+ GetWindowRect(g_clistApi.hwndContactList, &r);
+ return ske_JustUpdateWindowImageRect(&r);
+}
+
+// Update window image
+int ske_JustUpdateWindowImageRect(RECT *rty)
+{
+ if (!g_CluiData.fLayered) {
+ ske_ApplyTranslucency();
+ return 0;
+ }
+ if (!g_clistApi.hwndContactList)
+ return 0;
+
+ RECT wnd = *rty;
+ RECT rect = wnd;
+ POINT dest = { 0 }, src = { 0 };
+ dest.x = rect.left;
+ dest.y = rect.top;
+ SIZE sz = { rect.right - rect.left, rect.bottom - rect.top };
+ if (g_CluiData.fLayered) {
+ if (!(GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) & WS_EX_LAYERED))
+ SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
+ Sync(SetAlpha, g_CluiData.bCurrentAlpha);
+
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, g_CluiData.bCurrentAlpha, AC_SRC_ALPHA };
+ UpdateLayeredWindow(g_clistApi.hwndContactList, g_pCachedWindow->hScreenDC, &dest, &sz, g_pCachedWindow->hImageDC, &src, RGB(1, 1, 1), &bf, ULW_ALPHA);
+ g_CluiData.fAeroGlass = false;
+ CLUI_UpdateAeroGlass();
+ }
+ else InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ return 0;
+}
+
+int ske_DrawImageAt(HDC hdc, RECT *rc)
+{
+ BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
+ BitBlt(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, g_pCachedWindow->hBackDC, rc->left, rc->top, SRCCOPY);
+ ske_AlphaBlend(g_pCachedWindow->hImageDC, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, hdc, 0, 0, rc->right - rc->left, rc->bottom - rc->top, bf);
+ if (!g_mutex_bLockUpdating)
+ ske_UpdateWindowImage();
+ return 0;
+}
+
+HBITMAP ske_GetCurrentWindowImage()
+{
+ return g_pCachedWindow->hImageDIB;
+}
+
+/*
+* Glyph text routine
+*/
+
+static uint32_t ske_HexToARGB(char *Hex)
+{
+ char buf[10] = { 0 };
+ char buf2[11] = { 0 };
+ mir_snprintf(buf, "%s\n", Hex);
+ if (buf[1] == 'x' || buf[1] == 'X')
+ mir_snprintf(buf2, "0x%s\n", buf + 2);
+ else
+ mir_snprintf(buf2, "0x%s\n", buf);
+ buf2[10] = '\0';
+
+ char *st;
+ uint32_t AARRGGBB = strtoul(buf2, &st, 16);
+ uint8_t alpha = (uint8_t)((AARRGGBB & 0xFF000000) >> 24);
+ alpha = 255 - ((alpha == 0) ? 255 : alpha);
+ AARRGGBB = (alpha << 24) + ((AARRGGBB & 0x00FF0000) >> 16) + ((AARRGGBB & 0x000000FF) << 16) + (AARRGGBB & 0x0000FF00);
+ return AARRGGBB;
+}
+
+static wchar_t *ske_ReAppend(wchar_t *lfirst, wchar_t *lsecond, int len)
+{
+ size_t l1 = lfirst ? mir_wstrlen(lfirst) : 0;
+ size_t l2 = (len ? len : (mir_wstrlen(lsecond) + 1));
+ wchar_t *buf = (wchar_t *)mir_alloc((l1 + l2 + 1)*sizeof(wchar_t));
+ if (lfirst) memmove(buf, lfirst, l1*sizeof(wchar_t));
+ memmove(buf + l1, lsecond, l2*sizeof(wchar_t));
+ mir_free(lfirst);
+ if (len) buf[l1 + l2] = '\0';
+ return buf;
+}
+
+wchar_t* ske_ReplaceVar(wchar_t *var)
+{
+ if (!var) return mir_wstrdup(L"");
+ if (!mir_wstrcmpi(var, L"Profile")) {
+ char buf[MAX_PATH] = { 0 };
+ Profile_GetNameA(MAX_PATH, buf);
+
+ char *p = strrchr(buf, '.');
+ if (p) *p = 0;
+
+ mir_free(var);
+ return mir_a2u(buf);
+ }
+
+ mir_free(var);
+ return mir_wstrdup(L"");
+}
+
+wchar_t *ske_ParseText(wchar_t *stzText)
+{
+ size_t len = mir_wstrlen(stzText);
+ wchar_t *result = nullptr;
+ size_t stpos = 0, curpos = 0;
+
+ while (curpos < len) {
+ //1 find first %
+ while (curpos < len && stzText[curpos] != (wchar_t)'%')
+ curpos++;
+ if (curpos < len) { //% found
+ if (curpos - stpos > 0)
+ result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos));
+ stpos = curpos + 1;
+ curpos++;
+ //3 find second %
+ while (curpos < len && stzText[curpos] != (wchar_t)'%')
+ curpos++;
+ if (curpos >= len)
+ break;
+ if (curpos - stpos > 0) {
+ wchar_t *var = (wchar_t *)mir_alloc((curpos - stpos + 1)*sizeof(wchar_t));
+ memcpy(var, stzText + stpos, (curpos - stpos)*sizeof(wchar_t));
+ var[curpos - stpos] = (wchar_t)'\0';
+ var = ske_ReplaceVar(var);
+ result = ske_ReAppend(result, var, 0);
+ mir_free(var);
+ }
+ else result = ske_ReAppend(result, L"%", 0);
+
+ curpos++;
+ stpos = curpos;
+ }
+ else {
+ if (curpos - stpos > 0)
+ result = ske_ReAppend(result, stzText + stpos, int(curpos - stpos));
+ break;
+ }
+ }
+ return result;
+}
+/*
+* Parse text object string, find glyph object and add text to it.
+* szGlyphTextID and Define string is:
+* t[szGlyphTextID] = s[HostObjectID],[Left],[Top],[Right],[Bottom],[LTRBHV],[FontID],[Color1],[reservedforColor2],[Text]
+*/
+
+static void ske_AddParseTextGlyphObject(char *szGlyphTextID, char *szDefineString, SKINOBJECTSLIST *Skin)
+{
+ char buf[255] = { 0 };
+ GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE);
+ if (buf[0] == 0)
+ return;
+
+ GLYPHTEXT *glText = (GLYPHTEXT*)mir_calloc(sizeof(GLYPHTEXT));
+ glText->szGlyphTextID = mir_strdup(szGlyphTextID);
+ glText->szObjectName = mir_strdup(buf);
+ glText->iLeft = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE));
+ glText->iTop = atoi(GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE));
+ glText->iRight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 3, ',', TRUE));
+ glText->iBottom = atoi(GetParamN(szDefineString, buf, sizeof(buf), 4, ',', TRUE));
+ {
+ memset(buf, 0, 6);
+ GetParamN(szDefineString, buf, sizeof(buf), 5, ',', TRUE);
+ buf[0] &= 95; buf[1] &= 95; buf[2] &= 95; buf[3] &= 95; buf[4] &= 95; buf[5] &= 95; //to uppercase: &01011111 (0-95)
+ glText->RelativeFlags =
+ (buf[0] == 'C' ? 1 : ((buf[0] == 'R') ? 2 : 0)) //[BC][RC][BC][RC] --- Left relative
+ | (buf[1] == 'C' ? 4 : ((buf[1] == 'B') ? 8 : 0)) // | | |--------- Top relative
+ | (buf[2] == 'C' ? 16 : ((buf[2] == 'R') ? 32 : 0)) // | |--------------Right relative
+ | (buf[3] == 'C' ? 64 : ((buf[3] == 'B') ? 128 : 0)); // |------------------Bottom relative
+ glText->dwFlags = (buf[4] == 'C' ? DT_CENTER : ((buf[4] == 'R') ? DT_RIGHT : DT_LEFT))
+ | (buf[5] == 'C' ? DT_VCENTER : ((buf[5] == 'B') ? DT_BOTTOM : DT_TOP));
+ }
+ glText->szFontID = mir_strdup(GetParamN(szDefineString, buf, sizeof(buf), 6, ',', TRUE));
+
+ glText->dwColor = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 7, ',', TRUE));
+ glText->dwShadow = ske_HexToARGB(GetParamN(szDefineString, buf, sizeof(buf), 8, ',', TRUE));
+ glText->stValueText = mir_a2u(GetParamN(szDefineString, buf, sizeof(buf), 9, ',', TRUE));
+ glText->stText = ske_ParseText(glText->stValueText);
+
+ if (!Skin->pTextList)
+ Skin->pTextList = List_Create(0, 1);
+ List_InsertPtr(Skin->pTextList, glText);
+}
+
+
+/*
+* Parse font definition string.
+* szGlyphTextID and Define string is:
+* f[szFontID] = s[FontTypefaceName],[size],[BIU]
+*/
+static void ske_AddParseSkinFont(char *szFontID, char *szDefineString)
+{
+ SKINFONT *sf = (SKINFONT*)mir_calloc(sizeof(SKINFONT));
+ if (!sf)
+ return;
+
+ LOGFONTA logfont = { 0 };
+ logfont.lfCharSet = DEFAULT_CHARSET;
+ logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ logfont.lfQuality = DEFAULT_QUALITY;
+ logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+
+ char buf[255];
+ strncpy_s(logfont.lfFaceName, GetParamN(szDefineString, buf, sizeof(buf), 0, ',', TRUE), _TRUNCATE);
+ logfont.lfHeight = atoi(GetParamN(szDefineString, buf, sizeof(buf), 1, ',', TRUE));
+ if (logfont.lfHeight < 0) {
+ HDC hdc = CreateCompatibleDC(nullptr);
+ logfont.lfHeight = (long)-MulDiv(logfont.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+ DeleteDC(hdc);
+ }
+ logfont.lfHeight = -logfont.lfHeight;
+ GetParamN(szDefineString, buf, sizeof(buf), 2, ',', TRUE);
+ buf[0] &= 95; buf[1] &= 95; buf[2] &= 95;
+ logfont.lfWeight = (buf[0] == 'B') ? FW_BOLD : FW_NORMAL;
+ logfont.lfItalic = (buf[1] == 'I') ? 1 : 0;
+ logfont.lfUnderline = (buf[2] == 'U') ? 1 : 0;
+
+ sf->hFont = CreateFontIndirectA(&logfont);
+ if (sf->hFont) {
+ sf->szFontID = mir_strdup(szFontID);
+ if (!gl_plSkinFonts)
+ gl_plSkinFonts = List_Create(0, 1);
+ if (gl_plSkinFonts)
+ List_Insert(gl_plSkinFonts, sf, gl_plSkinFonts->realCount);
+ else
+ mir_free(sf);
+ }
+ else mir_free(sf);
+}
+
+/*
+ * ske_CheckHasAlfaChannel - checks if image has at least one uint8_t in alpha chennel
+ * that is not a 0. (is image real 32 bit or just 24 bit)
+ */
+static BOOL ske_CheckHasAlfaChannel(uint8_t *from, int widthByte, int height)
+{
+ uint32_t *pt = (uint32_t*)from;
+ for (int j = 0; j < height; j++) {
+ uint8_t *add = (uint8_t*)pt + widthByte;
+ while (pt < (uint32_t*)add) {
+ if ((*pt & 0xFF000000) != 0)
+ return TRUE;
+ pt++;
+ }
+ pt = (uint32_t*)(from + widthByte*j);
+ }
+ return FALSE;
+}
+
+/*
+ * ske_CheckIconHasMask - checks if mask image has at least one that is not a 0.
+ * Not sure is ir required or not
+ */
+static BOOL ske_CheckIconHasMask(uint8_t *from)
+{
+ for (int i = 0; i < 16 * 16 / 8; i++)
+ if (from[i] != 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * ske_GetMaskBit - return value of apropriate mask bit in line at x position
+ */
+static BOOL ske_GetMaskBit(uint8_t *line, int x)
+{
+ return ((*(line + (x >> 3)))&(0x01 << (7 - (x & 0x07)))) != 0;
+}
+
+/*
+ * ske_Blend - alpha ske_Blend ARGB values of 2 pixels. X1 - underlaying,
+ * X2 - overlaying points.
+ */
+
+static uint32_t ske_Blend(uint32_t X1, uint32_t X2, uint8_t alpha)
+{
+ uint8_t a1 = (uint8_t)(X1 >> 24);
+ uint8_t a2 = (uint8_t)(((X2 >> 24)*alpha) >> 8);
+ uint8_t r1 = (uint8_t)(X1 >> 16);
+ uint8_t r2 = (uint8_t)(X2 >> 16);
+ uint8_t g1 = (uint8_t)(X1 >> 8);
+ uint8_t g2 = (uint8_t)(X2 >> 8);
+ uint8_t b1 = (uint8_t)(X1);
+ uint8_t b2 = (uint8_t)(X2);
+
+ uint8_t a_1 = ~a1;
+ uint8_t a_2 = ~a2;
+ uint16_t am = (uint16_t)a1*a_2;
+
+ /* it is possible to use >>8 instead of /255 but it is require additional
+ * checking of alphavalues
+ */
+ uint16_t ar = a1 + (((uint16_t)a_1*a2) / 255);
+ // if a2 more than 0 than result should be more
+ // or equal (if a1 == 0) to a2, else in combination
+ // with mask we can get here black points
+
+ ar = (a2 > ar) ? a2 : ar;
+
+ if (ar == 0) return 0;
+
+ uint16_t arm = ar * 255;
+ uint16_t rr = (((uint16_t)r1*am + (uint16_t)r2*a2 * 255)) / arm;
+ uint16_t gr = (((uint16_t)g1*am + (uint16_t)g2*a2 * 255)) / arm;
+ uint16_t br = (((uint16_t)b1*am + (uint16_t)b2*a2 * 255)) / arm;
+ return (ar << 24) | (rr << 16) | (gr << 8) | br;
+}
+
+/*
+ * CreateJoinedIcon - creates new icon by drawing hTop over hBottom.
+ */
+
+HICON ske_CreateJoinedIcon(HICON hBottom, HICON hTop, uint8_t alpha)
+{
+ ICONINFO iNew = { 0 };
+ ICONINFO iciBottom = { 0 };
+ ICONINFO iciTop = { 0 };
+
+ BITMAP bmp_top = { 0 };
+ BITMAP bmp_top_mask = { 0 };
+
+ BITMAP bmp_bottom = { 0 };
+ BITMAP bmp_bottom_mask = { 0 };
+
+ HDC tempDC = CreateCompatibleDC(nullptr);
+
+ uint8_t *ptPixels;
+ HBITMAP nImage = ske_CreateDIB32Point(16, 16, (void**)&ptPixels);
+ HBITMAP oImage = (HBITMAP)SelectObject(tempDC, nImage);
+
+ GetIconInfo(hBottom, &iciBottom);
+ GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
+ GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);
+
+ GetIconInfo(hTop, &iciTop);
+ GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
+ GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);
+
+ if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32) {
+ uint8_t *BottomBuffer, *TopBuffer, *BottomMaskBuffer, *TopMaskBuffer;
+ uint8_t *bb, *tb, *bmb, *tmb;
+ uint8_t *db = ptPixels;
+ int vstep_d = 16 * 4;
+ int vstep_b = bmp_bottom.bmWidthBytes;
+ int vstep_t = bmp_top.bmWidthBytes;
+ int vstep_bm = bmp_bottom_mask.bmWidthBytes;
+ int vstep_tm = bmp_top_mask.bmWidthBytes;
+ alpha = alpha ? alpha : 255;
+ if (bmp_bottom.bmBits) bb = BottomBuffer = (uint8_t*)bmp_bottom.bmBits;
+ else {
+ BottomBuffer = (uint8_t*)mir_alloc(bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight*bmp_bottom.bmWidthBytes, BottomBuffer);
+ bb = BottomBuffer + vstep_b*(bmp_bottom.bmHeight - 1);
+ vstep_b = -vstep_b;
+ }
+
+ if (bmp_top.bmBits) tb = TopBuffer = (uint8_t*)bmp_top.bmBits;
+ else {
+ TopBuffer = (uint8_t*)mir_alloc(bmp_top.bmHeight*bmp_top.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight*bmp_top.bmWidthBytes, TopBuffer);
+ tb = TopBuffer + vstep_t*(bmp_top.bmHeight - 1);
+ vstep_t = -vstep_t;
+ }
+
+ if (bmp_bottom_mask.bmBits) {
+ BottomMaskBuffer = (uint8_t*)bmp_bottom_mask.bmBits;
+ bmb = BottomMaskBuffer;
+ }
+ else {
+ BottomMaskBuffer = (uint8_t*)mir_alloc(bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight*bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
+ bmb = BottomMaskBuffer + vstep_bm*(bmp_bottom_mask.bmHeight - 1);
+ vstep_bm = -vstep_bm;
+
+ }
+
+ if (bmp_top_mask.bmBits) {
+ TopMaskBuffer = (uint8_t*)bmp_top_mask.bmBits;
+ tmb = TopMaskBuffer;
+ }
+ else {
+ TopMaskBuffer = (uint8_t*)mir_alloc(bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight*bmp_top_mask.bmWidthBytes, TopMaskBuffer);
+ tmb = TopMaskBuffer + vstep_tm*(bmp_top_mask.bmHeight - 1);
+ vstep_tm = -vstep_tm;
+ }
+
+ BOOL topHasAlpha = ske_CheckHasAlfaChannel(TopBuffer, bmp_top.bmWidthBytes, bmp_top.bmHeight);
+ BOOL bottomHasAlpha = ske_CheckHasAlfaChannel(BottomBuffer, bmp_bottom.bmWidthBytes, bmp_bottom.bmHeight);
+ BOOL topHasMask = ske_CheckIconHasMask(TopMaskBuffer);
+ BOOL bottomHasMask = ske_CheckIconHasMask(BottomMaskBuffer);
+ for (int y = 0; y < 16; y++) {
+ for (int x = 0; x < 16; x++) {
+ BOOL mask_b = ske_GetMaskBit(bmb, x);
+ BOOL mask_t = ske_GetMaskBit(tmb, x);
+ uint32_t bottom_d = ((uint32_t*)bb)[x];
+ uint32_t top_d = ((uint32_t*)tb)[x];
+ if (topHasMask) {
+ if (mask_t == 1 && !topHasAlpha) top_d &= 0xFFFFFF;
+ else if (!topHasAlpha) top_d |= 0xFF000000;
+ }
+ if (bottomHasMask) {
+ if (mask_b == 1 && !bottomHasAlpha) bottom_d &= 0xFFFFFF;
+ else if (!bottomHasAlpha) bottom_d |= 0xFF000000;
+ }
+ ((uint32_t*)db)[x] = ske_Blend(bottom_d, top_d, alpha);
+ }
+ bb += vstep_b;
+ tb += vstep_t;
+ bmb += vstep_bm;
+ tmb += vstep_tm;
+ db += vstep_d;
+ }
+
+ if (!bmp_bottom.bmBits) mir_free(BottomBuffer);
+ if (!bmp_top.bmBits) mir_free(TopBuffer);
+ if (!bmp_bottom_mask.bmBits) mir_free(BottomMaskBuffer);
+ if (!bmp_top_mask.bmBits) mir_free(TopMaskBuffer);
+ }
+ else {
+ ske_DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL);
+ ske_DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL | (alpha << 24));
+ }
+
+ DeleteObject(iciBottom.hbmColor);
+ DeleteObject(iciTop.hbmColor);
+ DeleteObject(iciBottom.hbmMask);
+ DeleteObject(iciTop.hbmMask);
+
+ SelectObject(tempDC, oImage);
+ DeleteDC(tempDC);
+
+ uint8_t p[32] = { 0 };
+ HBITMAP nMask = CreateBitmap(16, 16, 1, 1, (void*)&p);
+ {
+ HDC tempDC2 = CreateCompatibleDC(nullptr);
+ HDC tempDC3 = CreateCompatibleDC(nullptr);
+ HBITMAP hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
+ HBITMAP obmp = (HBITMAP)SelectObject(tempDC2, nMask);
+ HBITMAP obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
+ DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK);
+ DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK);
+ BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);
+ SelectObject(tempDC2, obmp);
+ SelectObject(tempDC3, obmp2);
+ DeleteObject(hbm);
+ DeleteDC(tempDC2);
+ DeleteDC(tempDC3);
+ }
+ iNew.fIcon = TRUE;
+ iNew.hbmColor = nImage;
+ iNew.hbmMask = nMask;
+ HICON res = CreateIconIndirect(&iNew);
+ DeleteObject(nImage);
+ DeleteObject(nMask);
+ return res;
+}
+
+#define NEWJOINEDSTR(destination, first, separator, last) \
+ destination = (char*)alloca(mir_strlen(first)+mir_strlen(separator)+mir_strlen(last)+1); \
+ if (destination) { \
+ *destination = '\0'; \
+ mir_strcat(destination,first); \
+ mir_strcat(destination,separator); \
+ mir_strcat(destination,last); \
+ }
+
+#define SKINSETSECTION "SkinnedSettings"
+
+BOOL SkinDBGetContactSetting(MCONTACT hContact, const char *szSection, const char *szKey, DBVARIANT *retdbv, BOOL *bSkinned)
+{
+ if (!hContact) { //only for not contact settings
+ char *szSkinKey;
+ NEWJOINEDSTR(szSkinKey, szSection, "@", szKey);
+ if (!db_get(hContact, SKINSETSECTION, szSkinKey, retdbv)) {
+ if (bSkinned) *bSkinned = TRUE;
+ return FALSE;
+ }
+ }
+ // not skinned
+ if (bSkinned) bSkinned = FALSE;
+ return db_get(hContact, szSection, szKey, retdbv);
+}
+
+uint8_t SkinDBGetContactSettingByte(MCONTACT hContact, const char *szSection, const char *szKey, uint8_t bDefault)
+{
+ DBVARIANT dbv = { 0 };
+ BOOL bSkinned = FALSE;
+ if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
+ if (dbv.type == DBVT_BYTE) {
+ uint8_t retVal = dbv.bVal;
+ db_free(&dbv);
+ return retVal;
+ }
+ else {
+ db_free(&dbv);
+ if (!bSkinned)
+ return db_get_b(hContact, szSection, szKey, bDefault);
+ }
+ }
+ return bDefault;
+}
+
+uint16_t SkinDBGetContactSettingWord(MCONTACT hContact, const char *szSection, const char *szKey, uint16_t wDefault)
+{
+ BOOL bSkinned = FALSE;
+ DBVARIANT dbv = { 0 };
+ if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
+ if (dbv.type == DBVT_WORD) {
+ uint16_t retVal = dbv.wVal;
+ db_free(&dbv);
+ return retVal;
+ }
+ db_free(&dbv);
+ if (!bSkinned)
+ return db_get_w(hContact, szSection, szKey, wDefault);
+ }
+ return wDefault;
+}
+
+uint32_t SkinDBGetContactSettingDword(MCONTACT hContact, const char *szSection, const char *szKey, uint32_t dwDefault)
+{
+ DBVARIANT dbv = { 0 };
+ BOOL bSkinned = FALSE;
+ if (!SkinDBGetContactSetting(hContact, szSection, szKey, &dbv, &bSkinned)) {
+ if (dbv.type == DBVT_DWORD) {
+ uint32_t retVal = dbv.dVal;
+ db_free(&dbv);
+ return retVal;
+ }
+ db_free(&dbv);
+ if (!bSkinned)
+ return db_get_dw(hContact, szSection, szKey, dwDefault);
+ }
+ return dwDefault;
+}
diff --git a/plugins/Clist_modern/src/modern_skinopt.cpp b/plugins/Clist_modern/src/modern_skinopt.cpp
index 5c18867fe2..c13f0c8bb8 100644
--- a/plugins/Clist_modern/src/modern_skinopt.cpp
+++ b/plugins/Clist_modern/src/modern_skinopt.cpp
@@ -1,546 +1,546 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 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.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or ( at your option ) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with 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 "modern_sync.h"
-
-/*******************************/
-// Main skin selection routine //
-/*******************************/
-#define MAX_NAME 100
-
-struct SkinListData
-{
- wchar_t Name[MAX_NAME];
- wchar_t File[MAX_PATH];
-};
-
-HBITMAP hPreviewBitmap = nullptr;
-HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data);
-HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName);
-HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file);
-HTREEITEM FillAvailableSkinList(HWND hwndDlg);
-
-INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
-
-int SkinOptInit(WPARAM wParam, LPARAM)
-{
- if (!g_CluiData.fDisableSkinEngine) {
- //Tabbed settings
- OPTIONSDIALOGPAGE odp = {};
- odp.position = -200000000;
- odp.pfnDlgProc = DlgSkinOpts;
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SKIN);
- odp.szGroup.w = LPGENW("Skins");
- odp.szTitle.w = LPGENW("Contact list");
- odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE;
- g_plugin.addOptions(wParam, &odp);
- }
- return 0;
-}
-
-INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_DESTROY:
- if (hPreviewBitmap)
- ske_UnloadGlyphImage(hPreviewBitmap);
- break;
-
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- SetDlgItemText(hwndDlg, IDC_SKINFOLDERLABEL, SkinsFolder);
- {
- HTREEITEM it = FillAvailableSkinList(hwndDlg);
- TreeView_SelectItem(GetDlgItem(hwndDlg, IDC_TREE1), it);
- }
- return 0;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDC_COLOUR_MENUNORMAL:
- case IDC_COLOUR_MENUSELECTED:
- case IDC_COLOUR_FRAMES:
- case IDC_COLOUR_STATUSBAR:
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case IDC_BUTTON_INFO:
- {
- HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
- if (hti == nullptr)
- return 0;
-
- TVITEM tvi = { 0 };
- tvi.hItem = hti;
- tvi.mask = TVIF_HANDLE | TVIF_PARAM;
- TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
- SkinListData *sd = (SkinListData*)(tvi.lParam);
- if (!sd)
- return 0;
-
- wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000];
- if (!wcschr(sd->File, '%')) {
- GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd->File);
- GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd->File);
- GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd->File);
- GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd->File);
- mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s):\t %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"),
- sd->Name, Description, Author, Contact, URL, sd->File);
- }
- else {
- mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s): %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"),
- TranslateT("reVista for Modern v0.5"),
- TranslateT("This is second default Modern Contact list skin in Vista Aero style"),
- TranslateT("Angeli-Ka (graphics), FYR (template)"),
- L"JID: fyr@jabber.ru",
- L"fyr.mirandaim.ru",
- TranslateT("Inside library"));
- }
- MessageBox(hwndDlg, text, TranslateT("Skin information"), MB_OK | MB_ICONINFORMATION);
- }
- break;
-
- case IDC_BUTTON_APPLY_SKIN:
- if (HIWORD(wParam) == BN_CLICKED) {
- HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
- if (hti == nullptr)
- return 0;
-
- TVITEM tvi = { 0 };
- tvi.hItem = hti;
- tvi.mask = TVIF_HANDLE | TVIF_PARAM;
- TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
- SkinListData *sd = (SkinListData*)(tvi.lParam);
- if (!sd)
- return 0;
-
- ske_LoadSkinFromIniFile(sd->File, FALSE);
- ske_LoadSkinFromDB();
- Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
- Sync(CLUIFrames_OnClistResize_mod, 0, 0);
- ske_RedrawCompleteWindow();
- Sync(CLUIFrames_OnClistResize_mod, 0, 0);
-
- RECT rc = {};
- GetWindowRect(g_clistApi.hwndContactList, &rc);
- Sync(CLUIFrames_OnMoving, g_clistApi.hwndContactList, &rc);
-
- if (g_hCLUIOptionsWnd) {
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT));
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT));
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT));
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT));
- }
- }
- break;
-
- case IDC_GETSKINS:
- if (HIWORD(wParam) == BN_CLICKED)
- Utils_OpenUrl("https://miranda-ng.org/tags/modern-contact-list/");
- break;
-
-
- case IDC_BUTTON_RESCAN:
- if (HIWORD(wParam) == BN_CLICKED) {
- HTREEITEM it = FillAvailableSkinList(hwndDlg);
- HWND wnd = GetDlgItem(hwndDlg, IDC_TREE1);
- TreeView_SelectItem(wnd, it);
- }
- }
- break;
-
- case WM_DRAWITEM:
- if (wParam == IDC_PREVIEW) {
- // TODO:Draw hPreviewBitmap here
- HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
- DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam;
- int mWidth = dis->rcItem.right - dis->rcItem.left;
- int mHeight = dis->rcItem.bottom - dis->rcItem.top;
- HDC memDC = CreateCompatibleDC(dis->hDC);
- HBITMAP hbmp = ske_CreateDIB32(mWidth, mHeight);
- HBITMAP holdbmp = (HBITMAP)SelectObject(memDC, hbmp);
- RECT workRect = dis->rcItem;
- OffsetRect(&workRect, -workRect.left, -workRect.top);
- FillRect(memDC, &workRect, hbr);
- DeleteObject(hbr);
- if (hPreviewBitmap) {
- // variables
- BITMAP bmp = {};
- GetObject(hPreviewBitmap, sizeof(bmp), &bmp);
-
- // GetSize
- float xScale = 1, yScale = 1;
- int wWidth = workRect.right - workRect.left;
- int wHeight = workRect.bottom - workRect.top;
- if (wWidth < bmp.bmWidth)
- xScale = (float)wWidth / bmp.bmWidth;
- if (wHeight < bmp.bmHeight)
- yScale = (float)wHeight / bmp.bmHeight;
- xScale = min(xScale, yScale);
- yScale = xScale;
- int dWidth = (int)(xScale*bmp.bmWidth);
- int dHeight = (int)(yScale*bmp.bmHeight);
-
- // CalcPosition
- POINT imgPos = { 0 };
- imgPos.x = workRect.left + ((wWidth - dWidth) >> 1);
- imgPos.y = workRect.top + ((wHeight - dHeight) >> 1);
-
- // DrawImage
- DrawAvatarImageWithGDIp(memDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255);
- }
- BitBlt(dis->hDC, dis->rcItem.left, dis->rcItem.top, mWidth, mHeight, memDC, 0, 0, SRCCOPY);
- SelectObject(memDC, holdbmp);
- DeleteObject(hbmp);
- DeleteDC(memDC);
- }
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case 0:
- if (((LPNMHDR)lParam)->code == PSN_APPLY) {
- Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
- NotifyEventHooks(g_CluiData.hEventBkgrChanged, 0, 0);
- Clist_Broadcast(INTM_INVALIDATE, 0, 0);
- RedrawWindow(GetParent(g_clistApi.hwndContactTree), nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
- }
- break;
-
- case IDC_TREE1:
- NMTREEVIEW *nmtv = (NMTREEVIEW*)lParam;
- if (nmtv == nullptr)
- return 0;
-
- if (nmtv->hdr.code == TVN_SELCHANGED) {
- if (hPreviewBitmap) {
- ske_UnloadGlyphImage(hPreviewBitmap);
- hPreviewBitmap = nullptr;
- }
-
- if (nmtv->itemNew.lParam) {
- SkinListData *sd = (SkinListData*)nmtv->itemNew.lParam;
-
- wchar_t buf[MAX_PATH];
- PathToRelativeW(sd->File, buf);
- SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, buf);
-
- wchar_t prfn[MAX_PATH] = { 0 }, imfn[MAX_PATH] = { 0 }, skinfolder[MAX_PATH] = { 0 };
- GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), sd->File);
- IniParser::GetSkinFolder(sd->File, skinfolder);
- mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn);
- PathToAbsoluteW(prfn, imfn);
- hPreviewBitmap = ske_LoadGlyphImage(imfn);
-
- EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), TRUE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), TRUE);
- if (hPreviewBitmap)
- InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEW), nullptr, TRUE);
- else { // prepare text
- HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
- if (hti == nullptr)
- return 0;
-
- TVITEM tvi = { 0 };
- tvi.hItem = hti;
- tvi.mask = TVIF_HANDLE | TVIF_PARAM;
- TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
- SkinListData *sd2 = (SkinListData*)(tvi.lParam);
- if (!sd2)
- return 0;
-
- wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000];
- if (!wcschr(sd2->File, '%')) {
- GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd2->File);
- GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd2->File);
- GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd2->File);
- GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd2->File);
- mir_snwprintf(text, TranslateT("Preview is not available\n\n%s\n----------------------\n\n%s\n\nAUTHOR(S):\n%s\n\nCONTACT:\n%s\n\nHOMEPAGE:\n%s"),
- sd2->Name, Description, Author, Contact, URL);
- }
- else {
- mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAUTHORS:\n%s\n\nCONTACT:\n%s\n\nWEB:\n%s\n\n\n"),
- TranslateT("reVista for Modern v0.5"),
- TranslateT("This is second default Modern Contact list skin in Vista Aero style"),
- TranslateT("graphics by Angeli-Ka\ntemplate by FYR"),
- L"JID: fyr@jabber.ru",
- L"fyr.mirandaim.ru");
- }
- ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
- ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_INFO), SW_SHOW);
- SetDlgItemText(hwndDlg, IDC_STATIC_INFO, text);
- }
- }
- else {
- // no selected
- SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, TranslateT("Select skin from list"));
- EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), FALSE);
- SetDlgItemText(hwndDlg, IDC_STATIC_INFO, TranslateT("Please select skin to apply"));
- ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
- }
- ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), hPreviewBitmap ? SW_SHOW : SW_HIDE);
- return 0;
- }
- else if (nmtv->hdr.code == TVN_DELETEITEM) {
- mir_free_and_nil(nmtv->itemOld.lParam);
- return 0;
- }
- break;
- }
- }
- return 0;
-}
-
-int SearchSkinFiles(HWND hwndDlg, wchar_t *Folder)
-{
- wchar_t mask[MAX_PATH];
- mir_snwprintf(mask, L"%s\\*.msf", Folder);
-
- struct _wfinddata_t fd = {};
- intptr_t hFile = _wfindfirst(mask, &fd);
- if (hFile != -1) {
- do {
- AddSkinToList(hwndDlg, Folder, fd.name);
- } while (!_wfindnext(hFile, &fd));
- _findclose(hFile);
- }
-
- mir_snwprintf(mask, L"%s\\*", Folder);
- hFile = _wfindfirst(mask, &fd);
-
- do {
- if (fd.attrib & _A_SUBDIR && !(mir_wstrcmpi(fd.name, L".") == 0 || mir_wstrcmpi(fd.name, L"..") == 0)) { //Next level of subfolders
- wchar_t path[MAX_PATH];
- mir_snwprintf(path, L"%s\\%s", Folder, fd.name);
- SearchSkinFiles(hwndDlg, path);
- }
- } while (!_wfindnext(hFile, &fd));
-
- _findclose(hFile);
- return 0;
-}
-
-HTREEITEM FillAvailableSkinList(HWND hwndDlg)
-{
- TreeView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_TREE1));
- AddSkinToList(hwndDlg, TranslateT("Default Skin"), L"%Default Skin%");
- int attrib = GetFileAttributes(SkinsFolder);
- if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY))
- SearchSkinFiles(hwndDlg, SkinsFolder);
-
- HTREEITEM res = (HTREEITEM)-1;
- wchar_t skinfull[MAX_PATH];
- ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile"));
- if (skinfile) {
- PathToAbsoluteW(skinfile, skinfull);
- res = AddSkinToListFullName(hwndDlg, skinfull);
- }
-
- return res;
-}
-
-HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName)
-{
- wchar_t path[MAX_PATH] = {}, file[MAX_PATH] = {};
- mir_wstrncpy(path, fullName, _countof(path));
-
- wchar_t *buf = path + mir_wstrlen(path);
- while (buf > path) {
- if (*buf == '\\') {
- *buf = '\0';
- break;
- }
- buf--;
- }
- buf++;
- mir_wstrncpy(file, buf, _countof(file));
- return AddSkinToList(hwndDlg, path, file);
-}
-
-HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file)
-{
- wchar_t fullName[MAX_PATH], defskinname[MAX_PATH];
- SkinListData *sd = (SkinListData*)mir_alloc(sizeof(SkinListData));
- if (!sd)
- return nullptr;
-
- if (!file || wcschr(file, '%')) {
- mir_snwprintf(sd->File, L"%%Default Skin%%");
- mir_snwprintf(sd->Name, TranslateT("%Default Skin%"));
- wcsncpy_s(fullName, TranslateT("Default Skin"), _TRUNCATE);
- }
- else {
- mir_snwprintf(fullName, L"%s\\%s", path, file);
- wcsncpy_s(defskinname, file, _TRUNCATE);
- wchar_t *p = wcsrchr(defskinname, '.'); if (p) *p = 0;
- GetPrivateProfileString(L"Skin_Description_Section", L"Name", defskinname, sd->Name, _countof(sd->Name), fullName);
- wcsncpy_s(sd->File, fullName, _TRUNCATE);
- }
- return AddItemToTree(GetDlgItem(hwndDlg, IDC_TREE1), sd->Name, sd);
-}
-
-HTREEITEM FindChild(HWND hTree, HTREEITEM Parent, wchar_t *Caption, void *data)
-{
- HTREEITEM tmp = nullptr;
- if (Parent)
- tmp = TreeView_GetChild(hTree, Parent);
- else
- tmp = TreeView_GetRoot(hTree);
-
- while (tmp) {
- TVITEM tvi;
- wchar_t buf[255];
- tvi.hItem = tmp;
- tvi.mask = TVIF_TEXT | TVIF_HANDLE;
- tvi.pszText = buf;
- tvi.cchTextMax = _countof(buf);
- TreeView_GetItem(hTree, &tvi);
- if (mir_wstrcmpi(Caption, tvi.pszText) == 0) {
- if (!data)
- return tmp;
-
- TVITEM tvi2 = { 0 };
- tvi2.hItem = tmp;
- tvi2.mask = TVIF_HANDLE | TVIF_PARAM;
- TreeView_GetItem(hTree, &tvi2);
- SkinListData *sd = (SkinListData*)tvi2.lParam;
- if (sd)
- if (!mir_wstrcmpi(sd->File, ((SkinListData*)data)->File))
- return tmp;
- }
- tmp = TreeView_GetNextSibling(hTree, tmp);
- }
- return tmp;
-}
-
-HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data)
-{
- HTREEITEM cItem = nullptr;
- // Insert item node
- cItem = FindChild(hTree, nullptr, itemName, data);
- if (!cItem) {
- TVINSERTSTRUCT tvis = {};
- tvis.hInsertAfter = TVI_SORT;
- tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_PARAM;
- tvis.item.pszText = itemName;
- tvis.item.lParam = (LPARAM)data;
- return TreeView_InsertItem(hTree, &tvis);
- }
-
- mir_free(data); // need to free otherwise memory leak
- return cItem;
-}
-
-INT_PTR SvcActiveSkin(WPARAM, LPARAM)
-{
- ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile"));
- if (skinfile) {
- wchar_t skinfull[MAX_PATH];
- PathToAbsoluteW(skinfile, skinfull);
- return (INT_PTR)mir_wstrdup(skinfull);
- }
-
- return 0;
-}
-
-INT_PTR SvcApplySkin(WPARAM, LPARAM lParam)
-{
- ske_LoadSkinFromIniFile((wchar_t*)lParam, FALSE);
- ske_LoadSkinFromDB();
- Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
- Sync(CLUIFrames_OnClistResize_mod, 0, 0);
- ske_RedrawCompleteWindow();
- Sync(CLUIFrames_OnClistResize_mod, 0, 0);
-
- HWND hwnd = g_clistApi.hwndContactList;
- RECT rc = { 0 };
- GetWindowRect(hwnd, &rc);
- Sync(CLUIFrames_OnMoving, hwnd, &rc);
-
- g_bChangingMode = TRUE;
- CLUI_UpdateLayeredMode();
- CLUI_ChangeWindowMode();
- SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
- CLUI_ReloadCLUIOptions();
- cliShowHide(true);
- g_bChangingMode = FALSE;
-
- if (g_hCLUIOptionsWnd) {
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT));
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT));
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT));
- SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT));
- }
- return 0;
-}
-
-INT_PTR SvcPreviewSkin(WPARAM wParam, LPARAM lParam)
-{
- DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)wParam;
- RECT workRect = dis->rcItem;
- OffsetRect(&workRect, -workRect.left, -workRect.top);
-
- if (lParam) {
- wchar_t prfn[MAX_PATH] = { 0 };
- wchar_t imfn[MAX_PATH] = { 0 };
- wchar_t skinfolder[MAX_PATH] = { 0 };
- GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), (LPCTSTR)lParam);
- IniParser::GetSkinFolder((LPCTSTR)lParam, skinfolder);
- mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn);
- PathToAbsoluteW(prfn, imfn);
-
- hPreviewBitmap = ske_LoadGlyphImage(imfn);
- if (hPreviewBitmap) {
- // variables
- BITMAP bmp = { 0 };
- GetObject(hPreviewBitmap, sizeof(BITMAP), &bmp);
-
- // GetSize
- float xScale = 1, yScale = 1;
- int wWidth = workRect.right - workRect.left;
- int wHeight = workRect.bottom - workRect.top;
- if (wWidth < bmp.bmWidth)
- xScale = (float)wWidth / bmp.bmWidth;
- if (wHeight < bmp.bmHeight)
- yScale = (float)wHeight / bmp.bmHeight;
- xScale = min(xScale, yScale);
- yScale = xScale;
- int dWidth = (int)(xScale*bmp.bmWidth);
- int dHeight = (int)(yScale*bmp.bmHeight);
-
- // CalcPosition
- POINT imgPos = { 0 };
- imgPos.x = workRect.left + ((wWidth - dWidth) >> 1);
- imgPos.y = workRect.top + ((wHeight - dHeight) >> 1);
-
- // DrawImage
- DrawAvatarImageWithGDIp(dis->hDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255);
- ske_UnloadGlyphImage(hPreviewBitmap);
- }
- }
-
- 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-08 Miranda ICQ/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 "modern_sync.h"
+
+/*******************************/
+// Main skin selection routine //
+/*******************************/
+#define MAX_NAME 100
+
+struct SkinListData
+{
+ wchar_t Name[MAX_NAME];
+ wchar_t File[MAX_PATH];
+};
+
+HBITMAP hPreviewBitmap = nullptr;
+HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data);
+HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName);
+HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file);
+HTREEITEM FillAvailableSkinList(HWND hwndDlg);
+
+INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+int SkinOptInit(WPARAM wParam, LPARAM)
+{
+ if (!g_CluiData.fDisableSkinEngine) {
+ //Tabbed settings
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = -200000000;
+ odp.pfnDlgProc = DlgSkinOpts;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_SKIN);
+ odp.szGroup.w = LPGENW("Skins");
+ odp.szTitle.w = LPGENW("Contact list");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE;
+ g_plugin.addOptions(wParam, &odp);
+ }
+ return 0;
+}
+
+INT_PTR CALLBACK DlgSkinOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_DESTROY:
+ if (hPreviewBitmap)
+ ske_UnloadGlyphImage(hPreviewBitmap);
+ break;
+
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemText(hwndDlg, IDC_SKINFOLDERLABEL, SkinsFolder);
+ {
+ HTREEITEM it = FillAvailableSkinList(hwndDlg);
+ TreeView_SelectItem(GetDlgItem(hwndDlg, IDC_TREE1), it);
+ }
+ return 0;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_COLOUR_MENUNORMAL:
+ case IDC_COLOUR_MENUSELECTED:
+ case IDC_COLOUR_FRAMES:
+ case IDC_COLOUR_STATUSBAR:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_BUTTON_INFO:
+ {
+ HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
+ if (hti == nullptr)
+ return 0;
+
+ TVITEM tvi = { 0 };
+ tvi.hItem = hti;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
+ SkinListData *sd = (SkinListData*)(tvi.lParam);
+ if (!sd)
+ return 0;
+
+ wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000];
+ if (!wcschr(sd->File, '%')) {
+ GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd->File);
+ mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s):\t %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"),
+ sd->Name, Description, Author, Contact, URL, sd->File);
+ }
+ else {
+ mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAuthor(s): %s\nContact:\t %s\nWeb:\t %s\n\nFile:\t %s"),
+ TranslateT("reVista for Modern v0.5"),
+ TranslateT("This is second default Modern Contact list skin in Vista Aero style"),
+ TranslateT("Angeli-Ka (graphics), FYR (template)"),
+ L"JID: fyr@jabber.ru",
+ L"fyr.mirandaim.ru",
+ TranslateT("Inside library"));
+ }
+ MessageBox(hwndDlg, text, TranslateT("Skin information"), MB_OK | MB_ICONINFORMATION);
+ }
+ break;
+
+ case IDC_BUTTON_APPLY_SKIN:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
+ if (hti == nullptr)
+ return 0;
+
+ TVITEM tvi = { 0 };
+ tvi.hItem = hti;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
+ SkinListData *sd = (SkinListData*)(tvi.lParam);
+ if (!sd)
+ return 0;
+
+ ske_LoadSkinFromIniFile(sd->File, FALSE);
+ ske_LoadSkinFromDB();
+ Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+ ske_RedrawCompleteWindow();
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+
+ RECT rc = {};
+ GetWindowRect(g_clistApi.hwndContactList, &rc);
+ Sync(CLUIFrames_OnMoving, g_clistApi.hwndContactList, &rc);
+
+ if (g_hCLUIOptionsWnd) {
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT));
+ }
+ }
+ break;
+
+ case IDC_GETSKINS:
+ if (HIWORD(wParam) == BN_CLICKED)
+ Utils_OpenUrl("https://miranda-ng.org/tags/modern-contact-list/");
+ break;
+
+
+ case IDC_BUTTON_RESCAN:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ HTREEITEM it = FillAvailableSkinList(hwndDlg);
+ HWND wnd = GetDlgItem(hwndDlg, IDC_TREE1);
+ TreeView_SelectItem(wnd, it);
+ }
+ }
+ break;
+
+ case WM_DRAWITEM:
+ if (wParam == IDC_PREVIEW) {
+ // TODO:Draw hPreviewBitmap here
+ HBRUSH hbr = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)lParam;
+ int mWidth = dis->rcItem.right - dis->rcItem.left;
+ int mHeight = dis->rcItem.bottom - dis->rcItem.top;
+ HDC memDC = CreateCompatibleDC(dis->hDC);
+ HBITMAP hbmp = ske_CreateDIB32(mWidth, mHeight);
+ HBITMAP holdbmp = (HBITMAP)SelectObject(memDC, hbmp);
+ RECT workRect = dis->rcItem;
+ OffsetRect(&workRect, -workRect.left, -workRect.top);
+ FillRect(memDC, &workRect, hbr);
+ DeleteObject(hbr);
+ if (hPreviewBitmap) {
+ // variables
+ BITMAP bmp = {};
+ GetObject(hPreviewBitmap, sizeof(bmp), &bmp);
+
+ // GetSize
+ float xScale = 1, yScale = 1;
+ int wWidth = workRect.right - workRect.left;
+ int wHeight = workRect.bottom - workRect.top;
+ if (wWidth < bmp.bmWidth)
+ xScale = (float)wWidth / bmp.bmWidth;
+ if (wHeight < bmp.bmHeight)
+ yScale = (float)wHeight / bmp.bmHeight;
+ xScale = min(xScale, yScale);
+ yScale = xScale;
+ int dWidth = (int)(xScale*bmp.bmWidth);
+ int dHeight = (int)(yScale*bmp.bmHeight);
+
+ // CalcPosition
+ POINT imgPos = { 0 };
+ imgPos.x = workRect.left + ((wWidth - dWidth) >> 1);
+ imgPos.y = workRect.top + ((wHeight - dHeight) >> 1);
+
+ // DrawImage
+ DrawAvatarImageWithGDIp(memDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255);
+ }
+ BitBlt(dis->hDC, dis->rcItem.left, dis->rcItem.top, mWidth, mHeight, memDC, 0, 0, SRCCOPY);
+ SelectObject(memDC, holdbmp);
+ DeleteObject(hbmp);
+ DeleteDC(memDC);
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ if (((LPNMHDR)lParam)->code == PSN_APPLY) {
+ Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
+ NotifyEventHooks(g_CluiData.hEventBkgrChanged, 0, 0);
+ Clist_Broadcast(INTM_INVALIDATE, 0, 0);
+ RedrawWindow(GetParent(g_clistApi.hwndContactTree), nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
+ }
+ break;
+
+ case IDC_TREE1:
+ NMTREEVIEW *nmtv = (NMTREEVIEW*)lParam;
+ if (nmtv == nullptr)
+ return 0;
+
+ if (nmtv->hdr.code == TVN_SELCHANGED) {
+ if (hPreviewBitmap) {
+ ske_UnloadGlyphImage(hPreviewBitmap);
+ hPreviewBitmap = nullptr;
+ }
+
+ if (nmtv->itemNew.lParam) {
+ SkinListData *sd = (SkinListData*)nmtv->itemNew.lParam;
+
+ wchar_t buf[MAX_PATH];
+ PathToRelativeW(sd->File, buf);
+ SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, buf);
+
+ wchar_t prfn[MAX_PATH] = { 0 }, imfn[MAX_PATH] = { 0 }, skinfolder[MAX_PATH] = { 0 };
+ GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), sd->File);
+ IniParser::GetSkinFolder(sd->File, skinfolder);
+ mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn);
+ PathToAbsoluteW(prfn, imfn);
+ hPreviewBitmap = ske_LoadGlyphImage(imfn);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), TRUE);
+ if (hPreviewBitmap)
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_PREVIEW), nullptr, TRUE);
+ else { // prepare text
+ HTREEITEM hti = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_TREE1));
+ if (hti == nullptr)
+ return 0;
+
+ TVITEM tvi = { 0 };
+ tvi.hItem = hti;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(GetDlgItem(hwndDlg, IDC_TREE1), &tvi);
+ SkinListData *sd2 = (SkinListData*)(tvi.lParam);
+ if (!sd2)
+ return 0;
+
+ wchar_t Author[255], URL[MAX_PATH], Contact[255], Description[400], text[2000];
+ if (!wcschr(sd2->File, '%')) {
+ GetPrivateProfileString(L"Skin_Description_Section", L"Author", TranslateT("( unknown )"), Author, _countof(Author), sd2->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"URL", L"", URL, _countof(URL), sd2->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Contact", L"", Contact, _countof(Contact), sd2->File);
+ GetPrivateProfileString(L"Skin_Description_Section", L"Description", L"", Description, _countof(Description), sd2->File);
+ mir_snwprintf(text, TranslateT("Preview is not available\n\n%s\n----------------------\n\n%s\n\nAUTHOR(S):\n%s\n\nCONTACT:\n%s\n\nHOMEPAGE:\n%s"),
+ sd2->Name, Description, Author, Contact, URL);
+ }
+ else {
+ mir_snwprintf(text, TranslateT("%s\n\n%s\n\nAUTHORS:\n%s\n\nCONTACT:\n%s\n\nWEB:\n%s\n\n\n"),
+ TranslateT("reVista for Modern v0.5"),
+ TranslateT("This is second default Modern Contact list skin in Vista Aero style"),
+ TranslateT("graphics by Angeli-Ka\ntemplate by FYR"),
+ L"JID: fyr@jabber.ru",
+ L"fyr.mirandaim.ru");
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_INFO), SW_SHOW);
+ SetDlgItemText(hwndDlg, IDC_STATIC_INFO, text);
+ }
+ }
+ else {
+ // no selected
+ SetDlgItemText(hwndDlg, IDC_EDIT_SKIN_FILENAME, TranslateT("Select skin from list"));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_APPLY_SKIN), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_INFO), FALSE);
+ SetDlgItemText(hwndDlg, IDC_STATIC_INFO, TranslateT("Please select skin to apply"));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), SW_HIDE);
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_PREVIEW), hPreviewBitmap ? SW_SHOW : SW_HIDE);
+ return 0;
+ }
+ else if (nmtv->hdr.code == TVN_DELETEITEM) {
+ mir_free_and_nil(nmtv->itemOld.lParam);
+ return 0;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+int SearchSkinFiles(HWND hwndDlg, wchar_t *Folder)
+{
+ wchar_t mask[MAX_PATH];
+ mir_snwprintf(mask, L"%s\\*.msf", Folder);
+
+ struct _wfinddata_t fd = {};
+ intptr_t hFile = _wfindfirst(mask, &fd);
+ if (hFile != -1) {
+ do {
+ AddSkinToList(hwndDlg, Folder, fd.name);
+ } while (!_wfindnext(hFile, &fd));
+ _findclose(hFile);
+ }
+
+ mir_snwprintf(mask, L"%s\\*", Folder);
+ hFile = _wfindfirst(mask, &fd);
+
+ do {
+ if (fd.attrib & _A_SUBDIR && !(mir_wstrcmpi(fd.name, L".") == 0 || mir_wstrcmpi(fd.name, L"..") == 0)) { //Next level of subfolders
+ wchar_t path[MAX_PATH];
+ mir_snwprintf(path, L"%s\\%s", Folder, fd.name);
+ SearchSkinFiles(hwndDlg, path);
+ }
+ } while (!_wfindnext(hFile, &fd));
+
+ _findclose(hFile);
+ return 0;
+}
+
+HTREEITEM FillAvailableSkinList(HWND hwndDlg)
+{
+ TreeView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_TREE1));
+ AddSkinToList(hwndDlg, TranslateT("Default Skin"), L"%Default Skin%");
+ int attrib = GetFileAttributes(SkinsFolder);
+ if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY))
+ SearchSkinFiles(hwndDlg, SkinsFolder);
+
+ HTREEITEM res = (HTREEITEM)-1;
+ wchar_t skinfull[MAX_PATH];
+ ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile"));
+ if (skinfile) {
+ PathToAbsoluteW(skinfile, skinfull);
+ res = AddSkinToListFullName(hwndDlg, skinfull);
+ }
+
+ return res;
+}
+
+HTREEITEM AddSkinToListFullName(HWND hwndDlg, wchar_t *fullName)
+{
+ wchar_t path[MAX_PATH] = {}, file[MAX_PATH] = {};
+ mir_wstrncpy(path, fullName, _countof(path));
+
+ wchar_t *buf = path + mir_wstrlen(path);
+ while (buf > path) {
+ if (*buf == '\\') {
+ *buf = '\0';
+ break;
+ }
+ buf--;
+ }
+ buf++;
+ mir_wstrncpy(file, buf, _countof(file));
+ return AddSkinToList(hwndDlg, path, file);
+}
+
+HTREEITEM AddSkinToList(HWND hwndDlg, wchar_t *path, wchar_t *file)
+{
+ wchar_t fullName[MAX_PATH], defskinname[MAX_PATH];
+ SkinListData *sd = (SkinListData*)mir_alloc(sizeof(SkinListData));
+ if (!sd)
+ return nullptr;
+
+ if (!file || wcschr(file, '%')) {
+ mir_snwprintf(sd->File, L"%%Default Skin%%");
+ mir_snwprintf(sd->Name, TranslateT("%Default Skin%"));
+ wcsncpy_s(fullName, TranslateT("Default Skin"), _TRUNCATE);
+ }
+ else {
+ mir_snwprintf(fullName, L"%s\\%s", path, file);
+ wcsncpy_s(defskinname, file, _TRUNCATE);
+ wchar_t *p = wcsrchr(defskinname, '.'); if (p) *p = 0;
+ GetPrivateProfileString(L"Skin_Description_Section", L"Name", defskinname, sd->Name, _countof(sd->Name), fullName);
+ wcsncpy_s(sd->File, fullName, _TRUNCATE);
+ }
+ return AddItemToTree(GetDlgItem(hwndDlg, IDC_TREE1), sd->Name, sd);
+}
+
+HTREEITEM FindChild(HWND hTree, HTREEITEM Parent, wchar_t *Caption, void *data)
+{
+ HTREEITEM tmp = nullptr;
+ if (Parent)
+ tmp = TreeView_GetChild(hTree, Parent);
+ else
+ tmp = TreeView_GetRoot(hTree);
+
+ while (tmp) {
+ TVITEM tvi;
+ wchar_t buf[255];
+ tvi.hItem = tmp;
+ tvi.mask = TVIF_TEXT | TVIF_HANDLE;
+ tvi.pszText = buf;
+ tvi.cchTextMax = _countof(buf);
+ TreeView_GetItem(hTree, &tvi);
+ if (mir_wstrcmpi(Caption, tvi.pszText) == 0) {
+ if (!data)
+ return tmp;
+
+ TVITEM tvi2 = { 0 };
+ tvi2.hItem = tmp;
+ tvi2.mask = TVIF_HANDLE | TVIF_PARAM;
+ TreeView_GetItem(hTree, &tvi2);
+ SkinListData *sd = (SkinListData*)tvi2.lParam;
+ if (sd)
+ if (!mir_wstrcmpi(sd->File, ((SkinListData*)data)->File))
+ return tmp;
+ }
+ tmp = TreeView_GetNextSibling(hTree, tmp);
+ }
+ return tmp;
+}
+
+HTREEITEM AddItemToTree(HWND hTree, wchar_t *itemName, void *data)
+{
+ HTREEITEM cItem = nullptr;
+ // Insert item node
+ cItem = FindChild(hTree, nullptr, itemName, data);
+ if (!cItem) {
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_PARAM;
+ tvis.item.pszText = itemName;
+ tvis.item.lParam = (LPARAM)data;
+ return TreeView_InsertItem(hTree, &tvis);
+ }
+
+ mir_free(data); // need to free otherwise memory leak
+ return cItem;
+}
+
+INT_PTR SvcActiveSkin(WPARAM, LPARAM)
+{
+ ptrW skinfile(db_get_wsa(0, SKIN, "SkinFile"));
+ if (skinfile) {
+ wchar_t skinfull[MAX_PATH];
+ PathToAbsoluteW(skinfile, skinfull);
+ return (INT_PTR)mir_wstrdup(skinfull);
+ }
+
+ return 0;
+}
+
+INT_PTR SvcApplySkin(WPARAM, LPARAM lParam)
+{
+ ske_LoadSkinFromIniFile((wchar_t*)lParam, FALSE);
+ ske_LoadSkinFromDB();
+ Clist_Broadcast(INTM_RELOADOPTIONS, 0, 0);
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+ ske_RedrawCompleteWindow();
+ Sync(CLUIFrames_OnClistResize_mod, 0, 0);
+
+ HWND hwnd = g_clistApi.hwndContactList;
+ RECT rc = { 0 };
+ GetWindowRect(hwnd, &rc);
+ Sync(CLUIFrames_OnMoving, hwnd, &rc);
+
+ g_bChangingMode = TRUE;
+ CLUI_UpdateLayeredMode();
+ CLUI_ChangeWindowMode();
+ SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
+ CLUI_ReloadCLUIOptions();
+ cliShowHide(true);
+ g_bChangingMode = FALSE;
+
+ if (g_hCLUIOptionsWnd) {
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_LEFTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "LeftClientMargin", SETTING_LEFTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_RIGHTMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "RightClientMargin", SETTING_RIGHTCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_TOPMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "TopClientMargin", SETTING_TOPCLIENTMARIGN_DEFAULT));
+ SendDlgItemMessage(g_hCLUIOptionsWnd, IDC_BOTTOMMARGINSPIN, UDM_SETPOS, 0, db_get_b(0, "CLUI", "BottomClientMargin", SETTING_BOTTOMCLIENTMARIGN_DEFAULT));
+ }
+ return 0;
+}
+
+INT_PTR SvcPreviewSkin(WPARAM wParam, LPARAM lParam)
+{
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*)wParam;
+ RECT workRect = dis->rcItem;
+ OffsetRect(&workRect, -workRect.left, -workRect.top);
+
+ if (lParam) {
+ wchar_t prfn[MAX_PATH] = { 0 };
+ wchar_t imfn[MAX_PATH] = { 0 };
+ wchar_t skinfolder[MAX_PATH] = { 0 };
+ GetPrivateProfileString(L"Skin_Description_Section", L"Preview", L"", imfn, _countof(imfn), (LPCTSTR)lParam);
+ IniParser::GetSkinFolder((LPCTSTR)lParam, skinfolder);
+ mir_snwprintf(prfn, L"%s\\%s", skinfolder, imfn);
+ PathToAbsoluteW(prfn, imfn);
+
+ hPreviewBitmap = ske_LoadGlyphImage(imfn);
+ if (hPreviewBitmap) {
+ // variables
+ BITMAP bmp = { 0 };
+ GetObject(hPreviewBitmap, sizeof(BITMAP), &bmp);
+
+ // GetSize
+ float xScale = 1, yScale = 1;
+ int wWidth = workRect.right - workRect.left;
+ int wHeight = workRect.bottom - workRect.top;
+ if (wWidth < bmp.bmWidth)
+ xScale = (float)wWidth / bmp.bmWidth;
+ if (wHeight < bmp.bmHeight)
+ yScale = (float)wHeight / bmp.bmHeight;
+ xScale = min(xScale, yScale);
+ yScale = xScale;
+ int dWidth = (int)(xScale*bmp.bmWidth);
+ int dHeight = (int)(yScale*bmp.bmHeight);
+
+ // CalcPosition
+ POINT imgPos = { 0 };
+ imgPos.x = workRect.left + ((wWidth - dWidth) >> 1);
+ imgPos.y = workRect.top + ((wHeight - dHeight) >> 1);
+
+ // DrawImage
+ DrawAvatarImageWithGDIp(dis->hDC, imgPos.x, imgPos.y, dWidth, dHeight, hPreviewBitmap, 0, 0, bmp.bmWidth, bmp.bmHeight, 8, 255);
+ ske_UnloadGlyphImage(hPreviewBitmap);
+ }
+ }
+
+ return 0;
+}
diff --git a/plugins/Clist_modern/src/modern_skinselector.cpp b/plugins/Clist_modern/src/modern_skinselector.cpp
index e1a5a0633c..d68177c48b 100644
--- a/plugins/Clist_modern/src/modern_skinselector.cpp
+++ b/plugins/Clist_modern/src/modern_skinselector.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/plugins/Clist_modern/src/modern_skinselector.h b/plugins/Clist_modern/src/modern_skinselector.h
index a5e4ed6f94..c4ea040f4d 100644
--- a/plugins/Clist_modern/src/modern_skinselector.h
+++ b/plugins/Clist_modern/src/modern_skinselector.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_static_clui.h b/plugins/Clist_modern/src/modern_static_clui.h
index a13723f8c0..b416c3f001 100644
--- a/plugins/Clist_modern/src/modern_static_clui.h
+++ b/plugins/Clist_modern/src/modern_static_clui.h
@@ -1,114 +1,114 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 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.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-/************************************************************************/
-/** FILE CONTAINS HEADER PART FOR .../modernb/clui.c file **/
-/** **/
-/** !!! DO NOT INCLUDE IN TO OTHER FILES !!! **/
-/************************************************************************/
-
-/* Definitions */
-#pragma once
-
-#define TM_AUTOALPHA 1
-#define TM_DELAYEDSIZING 2
-#define TM_BRINGOUTTIMEOUT 3
-#define TM_BRINGINTIMEOUT 4
-#define TM_UPDATEBRINGTIMER 5
-#define TM_SMOTHALPHATRANSITION 20
-#define TM_WINDOWUPDATE 100
-#define TM_STATUSBARUPDATE 200
-
-#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu"
-#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu"
-
-#define AC_SRC_NO_PREMULT_ALPHA 0x01
-#define AC_SRC_NO_ALPHA 0x02
-#define AC_DST_NO_PREMULT_ALPHA 0x10
-#define AC_DST_NO_ALPHA 0x20
-
-#define ANIMATION_STEP 40
-
-#define AEROGLASS_MINALPHA 24
-
-/* Declaration of prototypes in other modules */
-
-int ClcEnterDragToScroll(HWND hwnd, int Y);
-
-int CListMod_ContactListShutdownProc(WPARAM wParam, LPARAM lParam);
-int CListMod_HideWindow(HWND hwndContactList, int mode);
-
-int CLUIServices_LoadModule(void);
-INT_PTR CLUIServices_SortList(WPARAM wParam, LPARAM lParam);
-
-void Docking_GetMonitorRectFromWindow(HWND hWnd, RECT *rc);
-
-int EventArea_Create(HWND hCluiWnd);
-
-int ExtraImage_ExtraIDToColumnNum(int extra);
-
-int ModernSkinButtonLoadModule();
-int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *r);
-
-void ske_ApplyTranslucency();
-HBITMAP ske_CreateDIB32(int cx, int cy);
-HBITMAP ske_CreateDIB32Point(int cx, int cy, void ** bits);
-int ske_JustUpdateWindowImage();
-void ske_LoadSkinFromDB(void);
-int ske_RedrawCompleteWindow();
-int ske_UpdateWindowImage();
-int ske_ValidateFrameImageProc(RECT *r);
-
-HWND StatusBar_Create(HWND parent);
-
-int UnhookAll();
-
-/* Module function prototypes */
-
-int CLUI_IsInMainWindow(HWND hwnd);
-int CLUI_SizingOnBorder(POINT pt, int size);
-int CLUI_SmoothAlphaTransition(HWND hwnd, uint8_t GoalAlpha, BOOL wParam);
-int CLUI_TestCursorOnBorders();
-
-static int CLUI_SmoothAlphaThreadTransition();
-
-/* structs */
-
-struct CHECKFILLING
-{
- HDC hDC;
- RECT rcRect;
-};
-
-int CheckFramesPos(RECT *wr); //cluiframes.c
-int CLUIFrames_ApplyNewSizes(int mode); //cluiframes.c
-int CLUIFrames_GetTotalHeight(); //cluiframes.c
-int CLUIFrames_RepaintSubContainers(); //cluiframes.c
-int CLUIFramesGetMinHeight(); //cluiframes.c
-
-int SizeFramesByWindowRect(RECT *r, HDWP * PosBatch, int mode); //cluiframes.c
-
-int InitSkinHotKeys();
-BOOL amWakeThread();
-void CreateViewModeFrame();
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+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.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/************************************************************************/
+/** FILE CONTAINS HEADER PART FOR .../modernb/clui.c file **/
+/** **/
+/** !!! DO NOT INCLUDE IN TO OTHER FILES !!! **/
+/************************************************************************/
+
+/* Definitions */
+#pragma once
+
+#define TM_AUTOALPHA 1
+#define TM_DELAYEDSIZING 2
+#define TM_BRINGOUTTIMEOUT 3
+#define TM_BRINGINTIMEOUT 4
+#define TM_UPDATEBRINGTIMER 5
+#define TM_SMOTHALPHATRANSITION 20
+#define TM_WINDOWUPDATE 100
+#define TM_STATUSBARUPDATE 200
+
+#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu"
+#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu"
+
+#define AC_SRC_NO_PREMULT_ALPHA 0x01
+#define AC_SRC_NO_ALPHA 0x02
+#define AC_DST_NO_PREMULT_ALPHA 0x10
+#define AC_DST_NO_ALPHA 0x20
+
+#define ANIMATION_STEP 40
+
+#define AEROGLASS_MINALPHA 24
+
+/* Declaration of prototypes in other modules */
+
+int ClcEnterDragToScroll(HWND hwnd, int Y);
+
+int CListMod_ContactListShutdownProc(WPARAM wParam, LPARAM lParam);
+int CListMod_HideWindow(HWND hwndContactList, int mode);
+
+int CLUIServices_LoadModule(void);
+INT_PTR CLUIServices_SortList(WPARAM wParam, LPARAM lParam);
+
+void Docking_GetMonitorRectFromWindow(HWND hWnd, RECT *rc);
+
+int EventArea_Create(HWND hCluiWnd);
+
+int ExtraImage_ExtraIDToColumnNum(int extra);
+
+int ModernSkinButtonLoadModule();
+int ModernSkinButton_ReposButtons(HWND parent, uint8_t draw, RECT *r);
+
+void ske_ApplyTranslucency();
+HBITMAP ske_CreateDIB32(int cx, int cy);
+HBITMAP ske_CreateDIB32Point(int cx, int cy, void ** bits);
+int ske_JustUpdateWindowImage();
+void ske_LoadSkinFromDB(void);
+int ske_RedrawCompleteWindow();
+int ske_UpdateWindowImage();
+int ske_ValidateFrameImageProc(RECT *r);
+
+HWND StatusBar_Create(HWND parent);
+
+int UnhookAll();
+
+/* Module function prototypes */
+
+int CLUI_IsInMainWindow(HWND hwnd);
+int CLUI_SizingOnBorder(POINT pt, int size);
+int CLUI_SmoothAlphaTransition(HWND hwnd, uint8_t GoalAlpha, BOOL wParam);
+int CLUI_TestCursorOnBorders();
+
+static int CLUI_SmoothAlphaThreadTransition();
+
+/* structs */
+
+struct CHECKFILLING
+{
+ HDC hDC;
+ RECT rcRect;
+};
+
+int CheckFramesPos(RECT *wr); //cluiframes.c
+int CLUIFrames_ApplyNewSizes(int mode); //cluiframes.c
+int CLUIFrames_GetTotalHeight(); //cluiframes.c
+int CLUIFrames_RepaintSubContainers(); //cluiframes.c
+int CLUIFramesGetMinHeight(); //cluiframes.c
+
+int SizeFramesByWindowRect(RECT *r, HDWP * PosBatch, int mode); //cluiframes.c
+
+int InitSkinHotKeys();
+BOOL amWakeThread();
+void CreateViewModeFrame();
diff --git a/plugins/Clist_modern/src/modern_statusbar_options.cpp b/plugins/Clist_modern/src/modern_statusbar_options.cpp
index 1df676fafe..953920171e 100644
--- a/plugins/Clist_modern/src/modern_statusbar_options.cpp
+++ b/plugins/Clist_modern/src/modern_statusbar_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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/modern_toolbar.cpp b/plugins/Clist_modern/src/modern_toolbar.cpp
index c6c7844c38..564cb31117 100644
--- a/plugins/Clist_modern/src/modern_toolbar.cpp
+++ b/plugins/Clist_modern/src/modern_toolbar.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,
Copyright 2007 Artem Shpynov
diff --git a/plugins/Clist_modern/src/modern_viewmodebar.cpp b/plugins/Clist_modern/src/modern_viewmodebar.cpp
index 5507206936..6e687e9407 100644
--- a/plugins/Clist_modern/src/modern_viewmodebar.cpp
+++ b/plugins/Clist_modern/src/modern_viewmodebar.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_modern/src/stdafx.h b/plugins/Clist_modern/src/stdafx.h
index 814ee05d0b..683384d396 100644
--- a/plugins/Clist_modern/src/stdafx.h
+++ b/plugins/Clist_modern/src/stdafx.h
@@ -5,7 +5,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/plugins/Clist_nicer/src/Docking.cpp b/plugins/Clist_nicer/src/Docking.cpp
index 860c42e17a..47b9ffc90b 100644
--- a/plugins/Clist_nicer/src/Docking.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/alphablend.cpp b/plugins/Clist_nicer/src/alphablend.cpp
index d9bf9c14b8..eb369f4a93 100644
--- a/plugins/Clist_nicer/src/alphablend.cpp
+++ b/plugins/Clist_nicer/src/alphablend.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/alphablend.h b/plugins/Clist_nicer/src/alphablend.h
index 8298faf75c..1f30c04a71 100644
--- a/plugins/Clist_nicer/src/alphablend.h
+++ b/plugins/Clist_nicer/src/alphablend.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clc.cpp b/plugins/Clist_nicer/src/clc.cpp
index dc43f20aeb..4924dccd16 100644
--- a/plugins/Clist_nicer/src/clc.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clc.h b/plugins/Clist_nicer/src/clc.h
index 5913377c6c..ba06b835be 100644
--- a/plugins/Clist_nicer/src/clc.h
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcitems.cpp b/plugins/Clist_nicer/src/clcitems.cpp
index 80900a0150..7682c77ca5 100644
--- a/plugins/Clist_nicer/src/clcitems.cpp
+++ b/plugins/Clist_nicer/src/clcitems.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcmsgs.cpp b/plugins/Clist_nicer/src/clcmsgs.cpp
index 6bf599b703..fbd9dd0430 100644
--- a/plugins/Clist_nicer/src/clcmsgs.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcopts.cpp b/plugins/Clist_nicer/src/clcopts.cpp
index 4b419fd134..cc3c0c5923 100644
--- a/plugins/Clist_nicer/src/clcopts.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcpaint.cpp b/plugins/Clist_nicer/src/clcpaint.cpp
index a9e425ed08..e7f49f900c 100644
--- a/plugins/Clist_nicer/src/clcpaint.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clcutils.cpp b/plugins/Clist_nicer/src/clcutils.cpp
index c27ffec502..5be75febe9 100644
--- a/plugins/Clist_nicer/src/clcutils.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clist.h b/plugins/Clist_nicer/src/clist.h
index 2c14e865ce..be2c18f62e 100644
--- a/plugins/Clist_nicer/src/clist.h
+++ b/plugins/Clist_nicer/src/clist.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistevents.cpp b/plugins/Clist_nicer/src/clistevents.cpp
index c2eeadf296..134738fcff 100644
--- a/plugins/Clist_nicer/src/clistevents.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistmenus.cpp b/plugins/Clist_nicer/src/clistmenus.cpp
index f018828fef..4ee15c4fe5 100644
--- a/plugins/Clist_nicer/src/clistmenus.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistmod.cpp b/plugins/Clist_nicer/src/clistmod.cpp
index 5943c891c0..44db56f4e1 100644
--- a/plugins/Clist_nicer/src/clistmod.cpp
+++ b/plugins/Clist_nicer/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-10 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistopts.cpp b/plugins/Clist_nicer/src/clistopts.cpp
index 8869c4a965..cf2701715e 100644
--- a/plugins/Clist_nicer/src/clistopts.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clistsettings.cpp b/plugins/Clist_nicer/src/clistsettings.cpp
index ff20975394..773c070a71 100644
--- a/plugins/Clist_nicer/src/clistsettings.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clisttray.cpp b/plugins/Clist_nicer/src/clisttray.cpp
index bdfe001037..c3e7def30b 100644
--- a/plugins/Clist_nicer/src/clisttray.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/clui.cpp b/plugins/Clist_nicer/src/clui.cpp
index f81267c173..d7854a4197 100644
--- a/plugins/Clist_nicer/src/clui.cpp
+++ b/plugins/Clist_nicer/src/clui.cpp
@@ -1,1934 +1,1934 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-03 Miranda ICQ/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_findadd.h>
-#include "cluiframes.h"
-#include "coolscroll.h"
-
-#define TM_AUTOALPHA 1
-#define TIMERID_AUTOSIZE 100
-#define MENU_MIRANDAMENU 0xFFFF1234
-
-int g_fading_active = 0;
-
-static RECT g_PreSizeRect, g_SizingRect;
-static int g_sizingmethod;
-static LONG g_CLUI_x_off, g_CLUI_y_off, g_CLUI_y1_off, g_CLUI_x1_off;
-static RECT rcWPC;
-
-static int transparentFocus = 1;
-static byte oldhideoffline;
-static int disableautoupd = 1;
-static int hFrameContactTree;
-extern RECT old_window_rect, new_window_rect;
-
-extern BOOL g_trayTooltipActive;
-extern POINT tray_hover_pos;
-extern HWND g_hwndViewModeFrame, g_hwndEventArea, g_hwndToolbarFrame;
-
-extern ImageItem *g_CLUIImageItem;
-extern HBRUSH g_CLUISkinnedBkColor;
-extern HWND g_hwndSFL;
-extern ButtonItem *g_ButtonItems;
-extern COLORREF g_CLUISkinnedBkColorRGB;
-extern FRAMEWND *wndFrameCLC;
-extern HPEN g_hPenCLUIFrames;
-
-static uint8_t old_cliststate, show_on_first_autosize = FALSE;
-
-RECT cluiPos;
-
-wchar_t *statusNames[12];
-
-extern LRESULT CALLBACK EventAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-extern int hNotifyFrame;
-
-void MF_InitCheck(void);
-void InitGroupMenus();
-void FS_RegisterFonts();
-void LoadExtraIconModule();
-void RemoveFromTaskBar(HWND hWnd);
-
-extern LONG g_cxsmIcon, g_cysmIcon;
-
-SIZE g_oldSize = { 0 };
-POINT g_oldPos = { 0 };
-int during_sizing = 0;
-extern int dock_prevent_moving;
-
-static HDC hdcLockedPoint = nullptr;
-static HBITMAP hbmLockedPoint = nullptr, hbmOldLockedPoint = nullptr;
-
-HICON overlayicons[10];
-
-static IconItem myIcons[] = {
- { LPGEN("Toggle show online/offline"), "CLN_online", IDI_HIDEOFFLINE },
- { LPGEN("Toggle groups"), "CLN_groups", IDI_HIDEGROUPS },
- { LPGEN("Find contacts"), "CLN_findadd", IDI_FINDANDADD },
- { LPGEN("Open preferences"), "CLN_options", IDI_TBOPTIONS },
- { LPGEN("Toggle sounds"), "CLN_sound", IDI_SOUNDSON },
- { LPGEN("Minimize contact list"), "CLN_minimize", IDI_MINIMIZE },
- { LPGEN("Show TabSRMM session list"), "CLN_slist", IDI_TABSRMMSESSIONLIST },
- { LPGEN("Show TabSRMM menu"), "CLN_menu", IDI_TABSRMMMENU },
- { LPGEN("Sounds are off"), "CLN_soundsoff", IDI_SOUNDSOFF },
- { LPGEN("Select view mode"), "CLN_CLVM_select", IDI_CLVM_SELECT },
- { LPGEN("Reset view mode"), "CLN_CLVM_reset", IDI_DELETE },
- { LPGEN("Configure view modes"), "CLN_CLVM_options", IDI_CLVM_OPTIONS },
- { LPGEN("Show menu"), "CLN_topmenu", IDI_TBTOPMENU },
- { LPGEN("Setup accounts"), "CLN_accounts", IDI_TBACCOUNTS }
-};
-
-HWND hTbMenu, hTbGlobalStatus;
-
-static void Tweak_It(COLORREF clr)
-{
- SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
- SetLayeredWindowAttributes(g_clistApi.hwndContactList, clr, 0, LWA_COLORKEY);
- cfg::dat.colorkey = clr;
-}
-
-static void LayoutButtons(HWND hwnd, RECT *rc)
-{
- RECT rect;
- uint8_t left_offset = cfg::dat.bCLeft - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
- uint8_t right_offset = cfg::dat.bCRight - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
- uint8_t delta = left_offset + right_offset;
- ButtonItem *btnItems = g_ButtonItems;
-
- if (rc == nullptr)
- GetClientRect(hwnd, &rect);
- else
- rect = *rc;
-
- rect.bottom -= cfg::dat.bCBottom;
-
- if (g_ButtonItems) {
- while (btnItems) {
- LONG x = (btnItems->xOff >= 0) ? rect.left + btnItems->xOff : rect.right - abs(btnItems->xOff);
- LONG y = (btnItems->yOff >= 0) ? rect.top + btnItems->yOff : rect.bottom - cfg::dat.statusBarHeight;
-
- SetWindowPos(btnItems->hWnd, nullptr, x, y, btnItems->width, btnItems->height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
- btnItems = btnItems->nextItem;
- }
- }
-
- SetWindowPos(hTbMenu, nullptr, 2 + left_offset, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
- 21 * 3, 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
-
- SetWindowPos(hTbGlobalStatus, nullptr, left_offset + (3 * 21) + 3, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
- rect.right - delta - (3 * 21 + 5), 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
-
-}
-
-static int FS_FontsChanged(WPARAM, LPARAM)
-{
- COLORREF clr_cluiframes = db_get_dw(0, "CLUI", "clr_frameborder", RGB(40, 40, 40));
-
- if (g_hPenCLUIFrames)
- DeleteObject(g_hPenCLUIFrames);
- g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, clr_cluiframes);
-
- Clist_ClcOptionsChanged();
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-}
-
-// create the CLC control, but not yet the frame. The frame containing the CLC should be created as the
-// last frame of all.
-static HWND PreCreateCLC(HWND parent)
-{
- g_clistApi.hwndContactTree = CreateWindow(CLISTCONTROL_CLASSW, L"",
- WS_CHILD | CLS_CONTACTLIST | (Clist::UseGroups ? CLS_USEGROUPS : 0) | (Clist::HideOffline ? CLS_HIDEOFFLINE : 0) | (Clist::HideEmptyGroups ? CLS_HIDEEMPTYGROUPS : 0) | CLS_MULTICOLUMN,
- 0, 0, 0, 0, parent, nullptr, g_plugin.getInst(), (LPVOID)0xff00ff00);
-
- cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
- return g_clistApi.hwndContactTree;
-}
-
-// create internal frames, including the last frame (actual CLC control)
-static int CreateCLC()
-{
- ExtraIcon_Reload();
- g_clistApi.pfnSetHideOffline(oldhideoffline);
- disableautoupd = 0;
- {
- CLISTFrame frame = { 0 };
- frame.cbSize = sizeof(frame);
- frame.szName.a = "EventArea";
- frame.szTBname.a = LPGEN("Event area");
- frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
- frame.height = 20;
- frame.Flags = F_VISIBLE | F_SHOWTBTIP | F_NOBORDER;
- frame.align = alBottom;
- frame.hWnd = CreateWindowExA(0, "EventAreaClass", "evt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, g_clistApi.hwndContactList, (HMENU)nullptr, g_plugin.getInst(), nullptr);
- g_hwndEventArea = frame.hWnd;
- hNotifyFrame = g_plugin.addFrame(&frame);
- CallService(MS_CLIST_FRAMES_UPDATEFRAME, hNotifyFrame, FU_FMPOS);
- HideShowNotifyFrame();
- CreateViewModeFrame();
- }
- {
- CLISTFrame Frame = { 0 };
- Frame.cbSize = sizeof(CLISTFrame);
- Frame.hWnd = g_clistApi.hwndContactTree;
- Frame.align = alClient;
- Frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
- Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER;
- Frame.szName.a = "My contacts";
- Frame.szTBname.a = LPGEN("My contacts");
- Frame.height = 200;
- hFrameContactTree = g_plugin.addFrame(&Frame);
- CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_TBTIPNAME | FO_UNICODETEXT, hFrameContactTree), (LPARAM)TranslateT("My contacts"));
-
- // ugly, but working hack. Prevent that annoying little scroll bar from appearing in the "My Contacts" title bar
- uint32_t flags = (uint32_t)CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), 0);
- flags |= F_VISIBLE;
- CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), flags);
- }
-
- SetButtonToSkinned();
- return 0;
-}
-
-static int CluiModulesLoaded(WPARAM, LPARAM)
-{
- FS_RegisterFonts();
- HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
- return 0;
-}
-
-static HICON hIconSaved = nullptr;
-
-void ClearIcons(int mode)
-{
- for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
- if (overlayicons[i - IDI_OVL_OFFLINE] != nullptr) {
- if (mode)
- DestroyIcon(overlayicons[i - IDI_OVL_OFFLINE]);
- overlayicons[i - IDI_OVL_OFFLINE] = nullptr;
- }
- }
-}
-
-static void CacheClientIcons()
-{
- ClearIcons(0);
-
- for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
- char szBuffer[128];
- mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
- overlayicons[i - IDI_OVL_OFFLINE] = IcoLib_GetIcon(szBuffer);
- }
-}
-
-static void InitIcoLib()
-{
- g_plugin.registerIcon(LPGEN("Contact list") "/" LPGEN("Default"), myIcons);
-
- for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
- char szBuffer[128];
- mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
- IconItemT icon[] = { { Clist_GetStatusModeDescription(ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE), 0), szBuffer, i } };
- g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Overlay icons"), icon);
- }
-
- for (auto &pa : Accounts()) {
- if (!pa->IsEnabled() || CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
- continue;
-
- wchar_t szDescr[128];
- mir_snwprintf(szDescr, TranslateT("%s connecting"), pa->tszAccountName);
- IconItemT icon[] = { { szDescr, "conn", IDI_PROTOCONNECTING } };
- g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Connecting icons"), icon, pa->szModuleName);
- }
-}
-
-static int IcoLibChanged(WPARAM, LPARAM)
-{
- IcoLibReloadIcons();
- return 0;
-}
-
-void CreateButtonBar(HWND hWnd)
-{
- hTbMenu = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBMENU, g_plugin.getInst(), nullptr);
- CustomizeButton(hTbMenu, false, false, false);
- SetWindowText(hTbMenu, TranslateT("Menu"));
- SendMessage(hTbMenu, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_OTHER_MAINMENU));
- SendMessage(hTbMenu, BUTTONSETSENDONDOWN, TRUE, 0);
- SendMessage(hTbMenu, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open main menu"), 0);
-
- hTbGlobalStatus = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBGLOBALSTATUS, g_plugin.getInst(), nullptr);
- CustomizeButton(hTbGlobalStatus, false, false, false);
- SetWindowText(hTbGlobalStatus, TranslateT("Offline"));
- SendMessage(hTbGlobalStatus, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_STATUS_OFFLINE));
- SendMessage(hTbGlobalStatus, BUTTONSETSENDONDOWN, TRUE, 0);
- SendMessage(hTbGlobalStatus, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Set status modes"), 0);
-}
-
-// if mode != 0 we do first time init, otherwise only reload the extra icon stuff
-void CLN_LoadAllIcons(BOOL mode)
-{
- if (mode) {
- InitIcoLib();
- HookEvent(ME_SKIN_ICONSCHANGED, IcoLibChanged);
- }
- CacheClientIcons();
-}
-
-void ConfigureEventArea()
-{
- int iCount = GetMenuItemCount(cfg::dat.hMenuNotify);
- uint32_t dwFlags = cfg::dat.dwFlags;
- int oldstate = cfg::dat.notifyActive;
- int dwVisible = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hNotifyFrame), 0) & F_VISIBLE;
-
- if (dwVisible) {
- if (dwFlags & CLUI_FRAME_AUTOHIDENOTIFY)
- cfg::dat.notifyActive = iCount > 0 ? 1 : 0;
- else
- cfg::dat.notifyActive = 1;
- }
- else
- cfg::dat.notifyActive = 0;
-
- if (oldstate != cfg::dat.notifyActive)
- HideShowNotifyFrame();
-}
-
-void ConfigureFrame()
-{
- int show = cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? SW_SHOW : SW_HIDE;
- ShowWindow(hTbMenu, show);
- ShowWindow(hTbGlobalStatus, show);
-}
-
-void IcoLibReloadIcons()
-{
- CacheClientIcons();
- ExtraIcon_Reload();
- ExtraIcon_SetAll();
-
- Clist_Broadcast(CLM_AUTOREBUILD, 0, 0);
- SendMessage(g_hwndViewModeFrame, WM_USER + 100, 0, 0);
-}
-
-void ConfigureCLUIGeometry(int mode)
-{
- RECT rcStatus;
- uint32_t clmargins = db_get_dw(0, "CLUI", "clmargins", 0);
-
- cfg::dat.bCLeft = LOBYTE(LOWORD(clmargins));
- cfg::dat.bCRight = HIBYTE(LOWORD(clmargins));
- cfg::dat.bCTop = LOBYTE(HIWORD(clmargins));
- cfg::dat.bCBottom = HIBYTE(HIWORD(clmargins));
-
- if (mode) {
- if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
- SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0);
- GetWindowRect(g_clistApi.hwndStatus, &rcStatus);
- cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
- }
- else cfg::dat.statusBarHeight = 0;
- }
-
- cfg::dat.topOffset = cfg::dat.bCTop;
- cfg::dat.bottomOffset = (cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? 2 + 21 : 0) + cfg::dat.bCBottom;
-
- if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
- cfg::dat.topOffset += 2;
- cfg::dat.bottomOffset += 2;
- cfg::dat.bCLeft += 3;
- cfg::dat.bCRight += 3;
- }
-}
-
-// set the states of defined database action buttons (only if button is a toggle)
-void SetDBButtonStates(MCONTACT hPassedContact)
-{
- ButtonItem *buttonItem = g_ButtonItems;
- MCONTACT hContact = 0, hFinalContact = 0;
- char *szModule, *szSetting;
- ClcContact *contact = nullptr;
-
- if (cfg::clcdat && hPassedContact == 0) {
- g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
- if (contact && contact->type == CLCIT_CONTACT) {
- hContact = contact->hContact;
- }
- }
-
- while (buttonItem) {
- BOOL result = FALSE;
-
- if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
- buttonItem = buttonItem->nextItem;
- continue;
- }
- szModule = buttonItem->szModule;
- szSetting = buttonItem->szSetting;
- if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
- if (hContact == 0) {
- SendMessage(buttonItem->hWnd, BM_SETCHECK, BST_UNCHECKED, 0);
- buttonItem = buttonItem->nextItem;
- continue;
- }
- if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
- szModule = Proto_GetBaseAccountName(hContact);
- hFinalContact = hContact;
- }
- else
- hFinalContact = 0;
-
- if (buttonItem->type == DBVT_ASCIIZ) {
- DBVARIANT dbv = { 0 };
-
- if (!db_get_s(hFinalContact, szModule, szSetting, &dbv)) {
- result = !mir_strcmp((char *)buttonItem->bValuePush, dbv.pszVal);
- db_free(&dbv);
- }
- }
- else {
- switch (buttonItem->type) {
- case DBVT_BYTE: {
- uint8_t val = db_get_b(hFinalContact, szModule, szSetting, 0);
- result = (val == buttonItem->bValuePush[0]);
- break;
- }
- case DBVT_WORD: {
- uint16_t val = db_get_w(hFinalContact, szModule, szSetting, 0);
- result = (val == *((uint16_t *)&buttonItem->bValuePush));
- break;
- }
- case DBVT_DWORD:
- uint32_t val = db_get_dw(hFinalContact, szModule, szSetting, 0);
- result = (val == *((uint32_t *)&buttonItem->bValuePush));
- break;
- }
- }
- SendMessage(buttonItem->hWnd, BM_SETCHECK, (WPARAM)result, 0);
- buttonItem = buttonItem->nextItem;
- }
-}
-
-// set states of standard buttons (pressed/unpressed)
-void SetButtonStates()
-{
- ButtonItem *buttonItem = g_ButtonItems;
-
- if (g_ButtonItems) {
- while (buttonItem) {
- if (buttonItem->dwFlags & BUTTON_ISINTERNAL) {
- switch (buttonItem->uId) {
- case IDC_STBSOUND:
- SendMessage(buttonItem->hWnd, BM_SETCHECK, cfg::dat.soundsOff ? BST_CHECKED : BST_UNCHECKED, 0);
- break;
- case IDC_STBHIDEOFFLINE:
- SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::HideOffline, 0);
- break;
- case IDC_STBHIDEGROUPS:
- SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::UseGroups, 0);
- break;
- }
- }
- buttonItem = buttonItem->nextItem;
- }
- }
-}
-
-void BlitWallpaper(HDC hdc, RECT *rc, struct ClcData *dat)
-{
- int x, y;
- int bitx, bity;
- int maxx, maxy;
- int destw, desth, height, width;
- BITMAP *bmp = &cfg::dat.bminfoBg;
- LONG clip = cfg::dat.bClipBorder;
-
- if (dat == nullptr)
- return;
-
- SetStretchBltMode(hdc, HALFTONE);
-
- y = rc->top;
-
- rc->left = max(rc->left, clip);
- rc->right = min(rc->right - clip, rc->right);
- rc->top = max(rc->top, clip);
- rc->bottom = min(rc->bottom - clip, rc->bottom);
-
- width = rc->right - rc->left;
- height = rc->bottom - rc->top;
- HRGN my_rgn = CreateRectRgn(rc->left, rc->top, rc->right, rc->bottom);
- SelectClipRgn(hdc, my_rgn);
- maxx = dat->backgroundBmpUse & CLBF_TILEH ? rc->right : rc->left + 1;
- maxy = dat->backgroundBmpUse & CLBF_TILEV ? rc->bottom : y + 1;
- switch (dat->backgroundBmpUse & CLBM_TYPE) {
- case CLB_STRETCH:
- if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
- if (width * bmp->bmHeight < height * bmp->bmWidth) {
- desth = height;
- destw = desth * bmp->bmWidth / bmp->bmHeight;
- }
- else {
- destw = width;
- desth = destw * bmp->bmHeight / bmp->bmWidth;
- }
- }
- else {
- destw = width;
- desth = height;
- }
- break;
- case CLB_STRETCHH:
- if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
- destw = width;
- desth = destw * bmp->bmHeight / bmp->bmWidth;
- }
- else {
- destw = width;
- desth = bmp->bmHeight;
- }
- break;
-
- case CLB_STRETCHV:
- if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
- desth = height;
- destw = desth * bmp->bmWidth / bmp->bmHeight;
- }
- else {
- destw = bmp->bmWidth;
- desth = height;
- }
- break;
-
- default:
- //clb_topleft
- destw = bmp->bmWidth;
- desth = bmp->bmHeight;
- break;
- }
-
- bitx = 0;
- bity = 0;
- for (; y < maxy; y += desth) {
- for (x = rc->left; x < maxx; x += destw)
- StretchBlt(hdc, x, y, destw, desth, cfg::dat.hdcPic, bitx, bity, bmp->bmWidth, bmp->bmHeight, SRCCOPY);
- }
- SelectClipRgn(hdc, nullptr);
- DeleteObject(my_rgn);
-}
-
-void ReloadThemedOptions()
-{
- cfg::dat.bSkinnedStatusBar = db_get_b(0, "CLUI", "sb_skinned", 0);
- cfg::dat.bUsePerProto = db_get_b(0, "CLCExt", "useperproto", 0);
- cfg::dat.bOverridePerStatusColors = db_get_b(0, "CLCExt", "override_status", 0);
- cfg::dat.bRowSpacing = db_get_b(0, "CLC", "RowGap", 0);
- cfg::dat.bApplyIndentToBg = db_get_b(0, "CLCExt", "applyindentbg", 0);
- cfg::dat.bWallpaperMode = db_get_b(0, "CLUI", "UseBkSkin", 1);
- cfg::dat.bClipBorder = db_get_b(0, "CLUI", "clipborder", 0);
- cfg::dat.cornerRadius = db_get_b(0, "CLCExt", "CornerRad", 6);
- cfg::dat.gapBetweenFrames = (uint8_t)db_get_dw(0, "CLUIFrames", "GapBetweenFrames", 1);
- cfg::dat.bUseDCMirroring = db_get_b(0, "CLC", "MirrorDC", 0);
- cfg::dat.bGroupAlign = db_get_b(0, "CLC", "GroupAlign", 0);
- if (cfg::dat.hBrushColorKey)
- DeleteObject(cfg::dat.hBrushColorKey);
- cfg::dat.hBrushColorKey = CreateSolidBrush(RGB(255, 0, 255));
- cfg::dat.bWantFastGradients = db_get_b(0, "CLCExt", "FastGradients", 0);
- cfg::dat.titleBarHeight = db_get_b(0, "CLCExt", "frame_height", DEFAULT_TITLEBAR_HEIGHT);
- cfg::dat.group_padding = db_get_dw(0, "CLCExt", "grp_padding", 0);
-}
-
-static RECT rcWindow = { 0 };
-
-static void sttProcessResize(HWND hwnd, NMCLISTCONTROL *nmc)
-{
- RECT rcTree, rcWorkArea, rcOld;
- int maxHeight, newHeight;
- int winstyle, skinHeight = 0;
-
- if (disableautoupd)
- return;
-
- if (!db_get_b(0, "CLUI", "AutoSize", 0))
- return;
-
- if (Docking_IsDocked(0, 0))
- return;
- if (hFrameContactTree == 0)
- return;
-
- maxHeight = db_get_b(0, "CLUI", "MaxSizeHeight", 75);
- rcOld = rcWindow;
-
- GetWindowRect(hwnd, &rcWindow);
- GetWindowRect(g_clistApi.hwndContactTree, &rcTree);
- winstyle = GetWindowLongPtr(g_clistApi.hwndContactTree, GWL_STYLE);
-
- SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
- HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
- MONITORINFO mi;
- mi.cbSize = sizeof(mi);
- if (GetMonitorInfo(hMon, &mi))
- rcWorkArea = mi.rcWork;
-
- if (nmc->pt.y > (rcWorkArea.bottom - rcWorkArea.top)) {
- nmc->pt.y = (rcWorkArea.bottom - rcWorkArea.top);
- }
-
- if (winstyle & CLS_SKINNEDFRAME) {
- BOOL hasTitleBar = wndFrameCLC ? wndFrameCLC->TitleBar.ShowTitleBar : 0;
- StatusItems_t *item = arStatusItems[(hasTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB : ID_EXTBKOWNEDFRAMEBORDER) - ID_STATUS_OFFLINE];
- skinHeight = item->IGNORED ? 0 : item->MARGIN_BOTTOM + item->MARGIN_TOP;
- }
-
- newHeight = max(nmc->pt.y, 3) + 1 + ((winstyle & WS_BORDER) ? 2 : 0) + skinHeight + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top);
- if (newHeight == (rcWindow.bottom - rcWindow.top) && show_on_first_autosize == FALSE)
- return;
-
- if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100)
- newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100;
- if (db_get_b(0, "CLUI", "AutoSizeUpward", 0)) {
- rcWindow.top = rcWindow.bottom - newHeight;
- if (rcWindow.top < rcWorkArea.top) rcWindow.top = rcWorkArea.top;
- }
- else {
- rcWindow.bottom = rcWindow.top + newHeight;
- if (rcWindow.bottom > rcWorkArea.bottom) rcWindow.bottom = rcWorkArea.bottom;
- }
- if (cfg::dat.szOldCTreeSize.cx != rcTree.right - rcTree.left) {
- cfg::dat.szOldCTreeSize.cx = rcTree.right - rcTree.left;
- return;
- }
- KillTimer(hwnd, TIMERID_AUTOSIZE);
- SetTimer(hwnd, TIMERID_AUTOSIZE, 100, nullptr);
-}
-
-int CustomDrawScrollBars(NMCSBCUSTOMDRAW *nmcsbcd)
-{
- switch (nmcsbcd->hdr.code) {
- case NM_COOLSB_CUSTOMDRAW:
- static HDC hdcScroll = nullptr;
- static HBITMAP hbmScroll, hbmScrollOld;
- static LONG scrollLeft, scrollRight, scrollHeight, scrollYmin, scrollYmax;
-
- switch (nmcsbcd->dwDrawStage) {
- case CDDS_PREPAINT:
- if (cfg::dat.bSkinnedScrollbar) // XXX fix (verify skin items to be complete, otherwise don't draw
- return CDRF_SKIPDEFAULT;
- return CDRF_DODEFAULT;
-
- case CDDS_POSTPAINT:
- return 0;
-
- case CDDS_ITEMPREPAINT:
- HDC hdc = nmcsbcd->hdc;
- StatusItems_t *item = nullptr, *arrowItem = nullptr;
- UINT uItemID = ID_EXTBKSCROLLBACK;
- HRGN rgn = nullptr;
-
- RECT rc;
- GetWindowRect(g_clistApi.hwndContactTree, &rc);
-
- POINT pt;
- pt.x = rc.left;
- pt.y = rc.top;
- ScreenToClient(g_clistApi.hwndContactList, &pt);
- hdcScroll = hdc;
- BitBlt(hdcScroll, nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right - nmcsbcd->rect.left,
- nmcsbcd->rect.bottom - nmcsbcd->rect.top, cfg::dat.hdcBg, pt.x + nmcsbcd->rect.left, pt.y + nmcsbcd->rect.top, SRCCOPY);
-
- switch (nmcsbcd->uItem) {
- case HTSCROLL_UP:
- case HTSCROLL_DOWN:
- uItemID = (nmcsbcd->uState == CDIS_DEFAULT || nmcsbcd->uState == CDIS_DISABLED) ? ID_EXTBKSCROLLBUTTON :
- (nmcsbcd->uState == CDIS_HOT ? ID_EXTBKSCROLLBUTTONHOVER : ID_EXTBKSCROLLBUTTONPRESSED);
- break;
- case HTSCROLL_PAGEGDOWN:
- case HTSCROLL_PAGEGUP:
- uItemID = nmcsbcd->uItem == HTSCROLL_PAGEGUP ? ID_EXTBKSCROLLBACK : ID_EXTBKSCROLLBACKLOWER;
- rgn = CreateRectRgn(nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right, nmcsbcd->rect.bottom);
- SelectClipRgn(hdcScroll, rgn);
- break;
- case HTSCROLL_THUMB:
- uItemID = nmcsbcd->uState == CDIS_SELECTED ? ID_EXTBKSCROLLTHUMBPRESSED : ID_EXTBKSCROLLTHUMB;
- break;
- default:
- break;
- }
-
- uItemID -= ID_STATUS_OFFLINE;
- item = arStatusItems[uItemID];
- if (!item->IGNORED) {
- int alpha = nmcsbcd->uState == CDIS_DISABLED ? item->ALPHA - 50 : item->ALPHA;
- DrawAlpha(hdcScroll, &nmcsbcd->rect, item->COLOR, alpha, item->COLOR2, item->COLOR2_TRANSPARENT,
- item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
- }
- uint32_t dfcFlags = DFCS_FLAT | (nmcsbcd->uState == CDIS_DISABLED ? DFCS_INACTIVE :
- (nmcsbcd->uState == CDIS_HOT ? DFCS_HOT : (nmcsbcd->uState == CDIS_SELECTED ? DFCS_PUSHED : 0)));
-
- if (nmcsbcd->uItem == HTSCROLL_UP)
- arrowItem = arStatusItems[ID_EXTBKSCROLLARROWUP - ID_STATUS_OFFLINE];
- if (nmcsbcd->uItem == HTSCROLL_DOWN)
- arrowItem = arStatusItems[ID_EXTBKSCROLLARROWDOWN - ID_STATUS_OFFLINE];
- if (arrowItem && !arrowItem->IGNORED)
- DrawAlpha(hdcScroll, &nmcsbcd->rect, arrowItem->COLOR, arrowItem->ALPHA, arrowItem->COLOR2, arrowItem->COLOR2_TRANSPARENT,
- arrowItem->GRADIENT, arrowItem->CORNER, arrowItem->BORDERSTYLE, arrowItem->imageItem);
- else if (arrowItem)
- DrawFrameControl(hdcScroll, &nmcsbcd->rect, DFC_SCROLL, (nmcsbcd->uItem == HTSCROLL_UP ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | dfcFlags);
-
- if (rgn) {
- SelectClipRgn(hdcScroll, nullptr);
- DeleteObject(rgn);
- }
- }
- }
- return 0;
-}
-
-static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact)
-{
- if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) {
- if (hContact == 0)
- return 0;
-
- if (item->dwFlags & BUTTON_PASSHCONTACTW)
- *wParam = hContact;
- else if (item->dwFlags & BUTTON_PASSHCONTACTL)
- *lParam = hContact;
- }
- return 1;
-}
-
-static void ShowCLUI(HWND hwnd)
-{
- int state = old_cliststate;
- int onTop = g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT);
-
- SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
-
- if (state == SETTING_STATE_NORMAL) {
- SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
- ShowWindow(g_clistApi.hwndContactList, SW_SHOWNORMAL);
- SendMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
- }
- else if (state == SETTING_STATE_MINIMIZED) {
- cfg::dat.forceResize = TRUE;
- ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
- }
- else if (state == SETTING_STATE_HIDDEN) {
- cfg::dat.forceResize = TRUE;
- ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
- }
- SetWindowPos(g_clistApi.hwndContactList, onTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
- DrawMenuBar(hwnd);
- if (cfg::dat.autosize) {
- SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
- SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0);
- }
-}
-
-static void GetButtonRect(HWND hwnd, RECT *rc)
-{
- if (hwnd)
- GetWindowRect(hwnd, rc);
- else {
- POINT pt;
- GetCursorPos(&pt);
- rc->bottom = rc->top = pt.y;
- rc->left = rc->right = pt.x;
- }
-}
-
-LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- RECT rc;
-
- switch (msg) {
- case WM_CREATE:
- {
- int flags = WS_CHILD | CCS_BOTTOM;
- flags |= db_get_b(0, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0;
- flags |= db_get_b(0, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0;
- g_clistApi.hwndStatus = CreateWindow(STATUSCLASSNAME, nullptr, flags, 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr);
- if (flags & WS_VISIBLE) {
- ShowWindow(g_clistApi.hwndStatus, SW_SHOW);
- SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0);
- }
- mir_subclassWindow(g_clistApi.hwndStatus, NewStatusBarWndProc);
- SetClassLong(g_clistApi.hwndStatus, GCL_STYLE, GetClassLong(g_clistApi.hwndStatus, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
- }
- g_oldSize.cx = g_oldSize.cy = 0;
- old_cliststate = g_plugin.getByte("State", SETTING_STATE_NORMAL);
- g_plugin.setByte("State", SETTING_STATE_HIDDEN);
- SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VISIBLE);
- SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | WS_CLIPCHILDREN);
- if (!cfg::dat.bFirstRun)
- ConfigureEventArea();
- ConfigureCLUIGeometry(0);
- CluiProtocolStatusChanged(0, nullptr);
-
- for (int i = ID_STATUS_OFFLINE; i <= ID_STATUS_MAX; i++)
- statusNames[i - ID_STATUS_OFFLINE] = Clist_GetStatusModeDescription(i, 0);
-
- //delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded)
- if (cfg::dat.bLayeredHack) {
- SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | (WS_EX_LAYERED));
- SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
- }
-
- if (cfg::dat.isTransparent) {
- SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- }
- transparentFocus = 1;
-
- TranslateMenu(GetMenu(hwnd));
- PostMessage(hwnd, M_CREATECLC, 0, 0);
- return FALSE;
-
- case WM_NCCREATE:
- {
- LPCREATESTRUCT p = (LPCREATESTRUCT)lParam;
- p->style &= ~(CS_HREDRAW | CS_VREDRAW);
- }
- break;
-
- case M_CREATECLC: {
- if (db_get_b(0, "CLUI", "useskin", 0))
- IMG_LoadItems();
- CreateButtonBar(hwnd);
- SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
- {
- LONG style;
- uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_TOOLWINDOW);
- ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
- style = GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE);
- if (windowStyle != SETTING_WINDOWSTYLE_DEFAULT) {
- style |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
- style &= ~WS_EX_APPWINDOW;
- }
- else {
- style &= ~(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE);
- if (g_plugin.getByte("AlwaysHideOnTB", 1))
- style &= ~WS_EX_APPWINDOW;
- else
- style |= WS_EX_APPWINDOW;
- }
-
- SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, style);
- ApplyCLUIBorderStyle();
-
- SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE);
- }
-
- if (cfg::dat.bSkinnedButtonMode)
- SetButtonToSkinned();
- ConfigureFrame();
- SetButtonStates();
-
- CreateCLC();
- cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
-
- if (cfg::dat.bFullTransparent) {
- if (g_CLUISkinnedBkColorRGB)
- Tweak_It(g_CLUISkinnedBkColorRGB);
- else if (cfg::dat.bClipBorder || (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME))
- Tweak_It(RGB(255, 0, 255));
- else
- Tweak_It(cfg::clcdat->bkColour);
- }
-
- g_plugin.setByte("State", old_cliststate);
-
- if (g_plugin.getByte("AutoApplyLastViewMode", 0)) {
- DBVARIANT dbv = { 0 };
- if (!g_plugin.getString("LastViewMode", &dbv)) {
- if (mir_strlen(dbv.pszVal) > 2) {
- if (db_get_dw(0, CLVM_MODULE, dbv.pszVal, -1) != 0xffffffff)
- ApplyViewMode((char *)dbv.pszVal);
- }
- db_free(&dbv);
- }
- }
- if (!cfg::dat.autosize)
- ShowCLUI(hwnd);
- else {
- show_on_first_autosize = TRUE;
- RecalcScrollBar(g_clistApi.hwndContactTree, cfg::clcdat);
- }
- return 0;
- }
- case WM_ERASEBKGND:
- return TRUE;
- /*
- if (cfg::dat.bSkinnedButtonMode)
- return TRUE;
- return DefWindowProc(hwnd, msg, wParam, lParam);
- */
-
- case WM_PAINT:
- {
- PAINTSTRUCT ps;
- RECT rcFrame, rcClient;
- HDC hdc;
- HRGN rgn = nullptr;
- HDC hdcReal = BeginPaint(hwnd, &ps);
-
- if (during_sizing)
- rcClient = rcWPC;
- else
- GetClientRect(hwnd, &rcClient);
- CopyRect(&rc, &rcClient);
-
- if (!cfg::dat.hdcBg || rc.right > cfg::dat.dcSize.cx || rc.bottom + cfg::dat.statusBarHeight > cfg::dat.dcSize.cy) {
- RECT rcWorkArea;
-
- SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
- HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
- MONITORINFO mi;
- mi.cbSize = sizeof(mi);
- if (GetMonitorInfo(hMon, &mi))
- rcWorkArea = mi.rcWork;
-
- cfg::dat.dcSize.cy = max(rc.bottom + cfg::dat.statusBarHeight, rcWorkArea.bottom - rcWorkArea.top);
- cfg::dat.dcSize.cx = max(rc.right, (rcWorkArea.right - rcWorkArea.left) / 2);
-
- if (cfg::dat.hdcBg) {
- SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
- DeleteObject(cfg::dat.hbmBg);
- DeleteDC(cfg::dat.hdcBg);
- }
- cfg::dat.hdcBg = CreateCompatibleDC(hdcReal);
- cfg::dat.hbmBg = CreateCompatibleBitmap(hdcReal, cfg::dat.dcSize.cx, cfg::dat.dcSize.cy);
- cfg::dat.hbmBgOld = reinterpret_cast<HBITMAP>(SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBg));
- }
-
- if (cfg::shutDown) {
- EndPaint(hwnd, &ps);
- return 0;
- }
-
- hdc = cfg::dat.hdcBg;
-
- CopyRect(&rcFrame, &rcClient);
- if (g_CLUISkinnedBkColor) {
- if (cfg::dat.fOnDesktop) {
- HDC dc = GetDC(nullptr);
- RECT rcWin;
-
- GetWindowRect(hwnd, &rcWin);
- BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, dc, rcWin.left, rcWin.top, SRCCOPY);
- ReleaseDC(nullptr, dc);
- }
- else FillRect(hdc, &rcClient, g_CLUISkinnedBkColor);
- }
-
- if (cfg::dat.bClipBorder != 0 || cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) {
- int docked = Clist_IsDocked();
- int clip = cfg::dat.bClipBorder;
-
- if (!g_CLUISkinnedBkColor)
- FillRect(hdc, &rcClient, cfg::dat.hBrushColorKey);
- if (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME)
- rgn = CreateRoundRectRgn(clip, docked ? 0 : clip, rcClient.right - clip + 1, rcClient.bottom - (docked ? 0 : clip - 1), 8 + clip, 8 + clip);
- else
- rgn = CreateRectRgn(clip, docked ? 0 : clip, rcClient.right - clip, rcClient.bottom - (docked ? 0 : clip));
- SelectClipRgn(hdc, rgn);
- }
-
- if (g_CLUIImageItem) {
- IMG_RenderImageItem(hdc, g_CLUIImageItem, &rcFrame);
- cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
- ClientToScreen(hwnd, &cfg::dat.ptW);
- goto skipbg;
- }
-
- if (cfg::dat.bWallpaperMode)
- FillRect(hdc, &rcClient, cfg::dat.hBrushCLCBk);
- else
- FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE));
-
- rcFrame.left += (cfg::dat.bCLeft - 1);
- rcFrame.right -= (cfg::dat.bCRight - 1);
- rcFrame.bottom++;
- rcFrame.bottom -= cfg::dat.statusBarHeight;
- rcFrame.top += (cfg::dat.topOffset - 1);
-
- if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
- if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
- InflateRect(&rcFrame, -1, -1);
- if (cfg::dat.bmpBackground)
- BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
- cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
- ClientToScreen(hwnd, &cfg::dat.ptW);
- }
- InflateRect(&rcFrame, 1, 1);
- if (cfg::dat.bSkinnedButtonMode)
- rcFrame.bottom -= (cfg::dat.bottomOffset);
- DrawEdge(hdc, &rcFrame, BDR_SUNKENOUTER, BF_RECT);
- }
- else if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
- if (cfg::dat.bmpBackground)
- BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
- cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
- ClientToScreen(hwnd, &cfg::dat.ptW);
- }
-skipbg:
- BitBlt(hdcReal, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdc, 0, 0, SRCCOPY);
- if (rgn) {
- SelectClipRgn(hdc, nullptr);
- DeleteObject(rgn);
- }
- EndPaint(hwnd, &ps);
- }
- return 0;
-
- case WM_ENTERSIZEMOVE:
- {
- POINT pt = { 0 };
-
- GetWindowRect(hwnd, &g_PreSizeRect);
- GetClientRect(hwnd, &rc);
- ClientToScreen(hwnd, &pt);
- g_CLUI_x_off = pt.x - g_PreSizeRect.left;
- g_CLUI_y_off = pt.y - g_PreSizeRect.top;
- pt.x = rc.right;
- ClientToScreen(hwnd, &pt);
- g_CLUI_x1_off = g_PreSizeRect.right - pt.x;
- pt.x = 0;
- pt.y = rc.bottom;
- ClientToScreen(hwnd, &pt);
- g_CLUI_y1_off = g_PreSizeRect.bottom - pt.y;
- }
- break;
-
- case WM_EXITSIZEMOVE:
- PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
- break;
-
- case WM_SIZING:
- break;
-
- case WM_WINDOWPOSCHANGED:
- if (Docking_IsDocked(0, 0))
- break;
-
- case WM_WINDOWPOSCHANGING:
- if (g_clistApi.hwndContactList != nullptr) {
- WINDOWPOS *wp = (WINDOWPOS *)lParam;
- if (!wp || (wp->flags & SWP_NOSIZE))
- return FALSE;
-
- RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
- during_sizing = true;
-
- new_window_rect.left = 0;
- new_window_rect.right = wp->cx - (g_CLUI_x_off + g_CLUI_x1_off);
- new_window_rect.top = 0;
- new_window_rect.bottom = wp->cy - g_CLUI_y_off - g_CLUI_y1_off;
-
- if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
- RECT rcStatus;
- SetWindowPos(g_clistApi.hwndStatus, nullptr, 0, new_window_rect.bottom - 20, new_window_rect.right, 20, SWP_NOZORDER);
- GetWindowRect(g_clistApi.hwndStatus, &rcStatus);
- cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
- if (wp->cx != g_oldSize.cx)
- SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
- RedrawWindow(g_clistApi.hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
- }
- else
- cfg::dat.statusBarHeight = 0;
-
- SizeFramesByWindowRect(&new_window_rect);
- dock_prevent_moving = 0;
- LayoutButtons(hwnd, &new_window_rect);
- dock_prevent_moving = 1;
- g_oldPos.x = wp->x;
- g_oldPos.y = wp->y;
- g_oldSize.cx = wp->cx;
- g_oldSize.cy = wp->cy;
- rcWPC = new_window_rect;
- }
- during_sizing = false;
- return 0;
-
- case WM_SIZE:
- if ((wParam == 0 && lParam == 0) || Docking_IsDocked(0, 0)) {
-
- if (IsZoomed(hwnd))
- ShowWindow(hwnd, SW_SHOWNORMAL);
-
- if (g_clistApi.hwndContactList != nullptr) {
- SendMessage(hwnd, WM_ENTERSIZEMOVE, 0, 0);
- GetWindowRect(hwnd, &rc);
- WINDOWPOS wp = {};
- wp.cx = rc.right - rc.left;
- wp.cy = rc.bottom - rc.top;
- wp.x = rc.left;
- wp.y = rc.top;
- wp.flags = 0;
- SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
- SendMessage(hwnd, WM_EXITSIZEMOVE, 0, 0);
- }
- }
-
- case WM_MOVE:
- if (!IsIconic(hwnd)) {
- GetWindowRect(hwnd, &rc);
-
- if (!Docking_IsDocked(0, 0)) {
- cluiPos.bottom = (uint32_t)(rc.bottom - rc.top);
- cluiPos.left = rc.left;
- cluiPos.top = rc.top;
- }
- cluiPos.right = rc.right - rc.left;
- if (cfg::dat.realTimeSaving) {
- GetWindowRect(hwnd, &rc);
-
- // if docked, dont remember pos (except for width)
- if (!Clist_IsDocked()) {
- g_plugin.setDword("Height", (uint32_t)(rc.bottom - rc.top));
- g_plugin.setDword("x", (uint32_t)rc.left);
- g_plugin.setDword("y", (uint32_t)rc.top);
- }
- g_plugin.setDword("Width", (uint32_t)(rc.right - rc.left));
- }
- }
- return TRUE;
-
- case WM_SETFOCUS:
- SetFocus(g_clistApi.hwndContactTree);
- return 0;
-
- case CLUIINTM_REMOVEFROMTASKBAR: {
- uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
- if (windowStyle == SETTING_WINDOWSTYLE_DEFAULT && g_plugin.getByte("AlwaysHideOnTB", 0))
- RemoveFromTaskBar(hwnd);
- return 0;
- }
- case WM_ACTIVATE:
- if (g_fading_active) {
- if (wParam != WA_INACTIVE && cfg::dat.isTransparent)
- transparentFocus = 1;
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
- if (wParam == WA_INACTIVE) {
- if ((HWND)wParam != hwnd)
- if (cfg::dat.isTransparent)
- if (transparentFocus)
- SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
- }
- else {
- if (cfg::dat.isTransparent) {
- KillTimer(hwnd, TM_AUTOALPHA);
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- transparentFocus = 1;
- }
- SetWindowPos(g_clistApi.hwndContactList, g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
- }
- PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- case WM_SETCURSOR:
- if (cfg::dat.isTransparent) {
- if (!transparentFocus && GetForegroundWindow() != hwnd) {
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- transparentFocus = 1;
- SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
- }
- }
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- case WM_NCHITTEST: {
- LRESULT result;
- RECT r;
- POINT pt;
- int clip = cfg::dat.bClipBorder;
-
- GetWindowRect(hwnd, &r);
- GetCursorPos(&pt);
- if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6 && !db_get_b(0, "CLUI", "AutoSize", 0)) {
- if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
- return HTBOTTOM;
- if (pt.x < r.left + clip + 10)
- return HTBOTTOMLEFT;
- if (pt.x > r.right - clip - 10)
- return HTBOTTOMRIGHT;
- }
- else if (pt.y >= r.top && pt.y <= r.top + 3 && !db_get_b(0, "CLUI", "AutoSize", 0)) {
- if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
- return HTTOP;
- if (pt.x < r.left + clip + 10)
- return HTTOPLEFT;
- if (pt.x > r.right - clip - 10)
- return HTTOPRIGHT;
- }
- else if (pt.x >= r.left && pt.x <= r.left + clip + 6)
- return HTLEFT;
- else if (pt.x >= r.right - clip - 6 && pt.x <= r.right)
- return HTRIGHT;
-
- result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
- if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT || result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT)
- if (cfg::dat.autosize)
- return HTCLIENT;
- return result;
- }
-
- case WM_TIMER:
- if (wParam == TM_AUTOALPHA) {
- int inwnd;
-
- if (GetForegroundWindow() == hwnd) {
- KillTimer(hwnd, TM_AUTOALPHA);
- inwnd = 1;
- }
- else {
- POINT pt;
- HWND hwndPt;
- pt.x = (short)LOWORD(GetMessagePos());
- pt.y = (short)HIWORD(GetMessagePos());
- hwndPt = WindowFromPoint(pt);
- inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd);
- }
- if (inwnd != transparentFocus) {
- //change
- transparentFocus = inwnd;
- if (transparentFocus)
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- else
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.autoalpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- }
- if (!transparentFocus)
- KillTimer(hwnd, TM_AUTOALPHA);
- }
- else if (wParam == TIMERID_AUTOSIZE) {
- KillTimer(hwnd, wParam);
- SetWindowPos(hwnd, nullptr, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
- PostMessage(hwnd, WM_SIZE, 0, 0);
- PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
- }
- return TRUE;
-
- case WM_SHOWWINDOW:
- {
- static int noRecurse = 0;
- uint32_t thisTick, startTick;
- int sourceAlpha, destAlpha;
-
- if (cfg::dat.forceResize && wParam != SW_HIDE) {
- cfg::dat.forceResize = FALSE;
- SendMessage(hwnd, WM_SIZE, 0, 0);
- PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
- }
- PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
-
- if (lParam)
- return DefWindowProc(hwnd, msg, wParam, lParam);
- if (noRecurse)
- return DefWindowProc(hwnd, msg, wParam, lParam);
- if (!cfg::dat.fadeinout)
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- g_fading_active = 1;
-
- if (wParam) {
- sourceAlpha = 0;
- destAlpha = cfg::dat.isTransparent ? cfg::dat.alpha : 255;
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? (COLORREF)cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)sourceAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- noRecurse = 1;
- ShowWindow(hwnd, SW_SHOW);
- RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
- noRecurse = 0;
- }
- else {
- sourceAlpha = cfg::dat.isTransparent ? (transparentFocus ? cfg::dat.alpha : cfg::dat.autoalpha) : 255;
- destAlpha = 0;
- }
- for (startTick = GetTickCount();;) {
- thisTick = GetTickCount();
- if (thisTick >= startTick + 200) {
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)destAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- g_fading_active = 0;
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
- SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)(sourceAlpha + (destAlpha - sourceAlpha) * (int)(thisTick - startTick) / 200), LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
- }
- }
-
- case WM_SYSCOMMAND:
- {
- uint8_t bWindowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
- if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle) {
- if (wParam == SC_RESTORE) {
- CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
- SendMessage(hwnd, WM_SIZE, 0, 0);
- SendMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
- SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
- g_plugin.setByte("State", SETTING_STATE_NORMAL);
- break;
- }
- }
-
- if (wParam == SC_MAXIMIZE)
- return 0;
-
- if (wParam == SC_MINIMIZE) {
- if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle && !g_plugin.getByte("AlwaysHideOnTB", 0)) {
- g_plugin.setByte("State", SETTING_STATE_MINIMIZED);
- break;
- }
- g_clistApi.pfnShowHide();
- return 0;
- }
- if (wParam == SC_RESTORE) {
- g_clistApi.pfnShowHide();
- return 0;
- }
- }
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- case WM_COMMAND:
- {
- uint32_t dwOldFlags = cfg::dat.dwFlags;
- if (HIWORD(wParam) == BN_CLICKED && lParam != 0) {
- if (LOWORD(wParam) == IDC_TBFIRSTUID - 1)
- break;
-
- else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling
- ButtonItem *item = g_ButtonItems;
- WPARAM wwParam = 0;
- LPARAM llParam = 0;
- MCONTACT hContact = 0;
- ClcContact *contact = nullptr;
- int serviceFailure = FALSE;
-
- if (cfg::clcdat) {
- g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
- if (contact && contact->type == CLCIT_CONTACT)
- hContact = contact->hContact;
- }
- while (item) {
- if (item->uId == (uint32_t)LOWORD(wParam)) {
- int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
-
- if (item->dwFlags & BUTTON_ISSERVICE) {
- if (ServiceExists(item->szService) && contactOK)
- CallService(item->szService, wwParam, llParam);
- else if (contactOK)
- serviceFailure = TRUE;
- }
- else if (item->dwFlags & BUTTON_ISPROTOSERVICE && cfg::clcdat) {
- if (contactOK) {
- char *szProto = Proto_GetBaseAccountName(hContact);
- if (ProtoServiceExists(szProto, item->szService))
- CallProtoService(szProto, item->szService, wwParam, llParam);
- else
- serviceFailure = TRUE;
- }
- }
- else if (item->dwFlags & BUTTON_ISDBACTION) {
- uint8_t *pValue;
- char *szModule = item->szModule;
- char *szSetting = item->szSetting;
- MCONTACT finalhContact = 0;
-
- if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) {
- contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
- if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION)
- szModule = Proto_GetBaseAccountName(hContact);
- finalhContact = hContact;
- }
- else
- contactOK = 1;
-
- if (contactOK) {
- BOOL fDelete = FALSE;
-
- if (item->dwFlags & BUTTON_ISTOGGLE) {
- BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED);
-
- pValue = fChecked ? item->bValueRelease : item->bValuePush;
- if (fChecked && pValue[0] == 0)
- fDelete = TRUE;
- }
- else
- pValue = item->bValuePush;
-
- if (fDelete)
- db_unset(finalhContact, szModule, szSetting);
- else {
- switch (item->type) {
- case DBVT_BYTE:
- db_set_b(finalhContact, szModule, szSetting, pValue[0]);
- break;
- case DBVT_WORD:
- db_set_w(finalhContact, szModule, szSetting, *((uint16_t *)&pValue[0]));
- break;
- case DBVT_DWORD:
- db_set_dw(finalhContact, szModule, szSetting, *((uint32_t *)&pValue[0]));
- break;
- case DBVT_ASCIIZ:
- db_set_s(finalhContact, szModule, szSetting, (char *)pValue);
- break;
- }
- }
- }
- else if (item->dwFlags & BUTTON_ISTOGGLE)
- SendMessage(item->hWnd, BM_SETCHECK, 0, 0);
- }
- if (!contactOK)
- MessageBox(nullptr, TranslateT("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat."), TranslateT("Parameter mismatch"), MB_OK);
- if (serviceFailure) {
- wchar_t szError[512];
- mir_snwprintf(szError, TranslateT("The service %S specified by the %S button definition was not found. You may need to install additional plugins."), item->szService, item->szName);
- MessageBox(nullptr, szError, TranslateT("Service failure"), MB_OK);
- }
- break;
- }
- item = item->nextItem;
- }
- goto buttons_done;
- }
-
- switch (LOWORD(wParam)) {
- case IDC_TBMENU:
- case IDC_TBTOPMENU:
- case IDC_STBTOPMENU:
- GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
- TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBMENU ? rc.top : rc.bottom, 0, hwnd, nullptr);
- return 0;
-
- case IDC_TBTOPSTATUS:
- case IDC_STBTOPSTATUS:
- case IDC_TBGLOBALSTATUS:
- GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
- TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBGLOBALSTATUS ? rc.top : rc.bottom, 0, hwnd, nullptr);
- return 0;
-
- case IDC_TBSOUND:
- case IDC_STBSOUND:
- cfg::dat.soundsOff = !cfg::dat.soundsOff;
- db_set_b(0, "CLUI", "NoSounds", (uint8_t)cfg::dat.soundsOff);
- db_set_b(0, "Skin", "UseSound", (uint8_t)(cfg::dat.soundsOff ? 0 : 1));
- return 0;
-
- case IDC_TBSELECTVIEWMODE:
- case IDC_STBSELECTVIEWMODE:
- SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_SELECTMODE, lParam);
- break;
- case IDC_TBCLEARVIEWMODE:
- case IDC_STBCLEARVIEWMODE:
- SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_RESETMODES, lParam);
- break;
- case IDC_TBCONFIGUREVIEWMODE:
- case IDC_STBCONFIGUREVIEWMODE:
- SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_CONFIGUREMODES, lParam);
- break;
- case IDC_TBFINDANDADD:
- case IDC_STBFINDANDADD:
- CallService(MS_FINDADD_FINDADD, 0, 0);
- return 0;
- case IDC_TBACCOUNTS:
- case IDC_STBACCOUNTS:
- CallService(MS_PROTO_SHOWACCMGR, 0, 0);
- break;
- case IDC_TBOPTIONS:
- case IDC_STBOPTIONS:
- CallService("Options/OptionsCommand", 0, 0);
- return 0;
- }
- }
- else if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_MAINMENU, NULL))
- return 0;
-
-buttons_done:
- switch (LOWORD(wParam)) {
- case ID_TRAY_EXIT:
- cfg::shutDown = 1;
- if (Miranda_OkToExit())
- DestroyWindow(hwnd);
- break;
- case ID_TRAY_HIDE:
- case IDC_TBMINIMIZE:
- case IDC_STBMINIMIZE:
- g_clistApi.pfnShowHide();
- break;
- case POPUP_NEWGROUP:
- SendMessage(g_clistApi.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0);
- SendMessage(g_clistApi.hwndContactTree, CLM_SETUSEGROUPS, 1, 0);
- Clist_GroupCreate(NULL, nullptr);
- break;
- case POPUP_HIDEOFFLINE:
- case IDC_TBHIDEOFFLINE:
- case IDC_STBHIDEOFFLINE:
- g_clistApi.pfnSetHideOffline(-1);
- break;
- case POPUP_HIDEOFFLINEROOT:
- CallService(MS_CLIST_TOGGLEHIDEOFFLINEROOT, 0, 0);
- break;
- case POPUP_HIDEEMPTYGROUPS:
- CallService(MS_CLIST_TOGGLEEMPTYGROUPS, 0, 0);
- break;
- case IDC_TBHIDEGROUPS:
- case IDC_STBHIDEGROUPS:
- case POPUP_DISABLEGROUPS:
- ClcSetButtonState(IDC_TBHIDEGROUPS, CallService(MS_CLIST_TOGGLEGROUPS, 0, 0));
- SetButtonStates();
- break;
- case POPUP_HIDEMIRANDA:
- g_clistApi.pfnShowHide();
- break;
- case POPUP_SHOWMETAICONS:
- cfg::dat.dwFlags ^= CLUI_USEMETAICONS;
- Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
- break;
- case POPUP_FRAME:
- cfg::dat.dwFlags ^= CLUI_FRAME_CLISTSUNKEN;
- break;
- case POPUP_BUTTONS:
- cfg::dat.dwFlags ^= CLUI_FRAME_SHOWBOTTOMBUTTONS;
- break;
- case POPUP_SHOWSTATUSICONS:
- cfg::dat.dwFlags ^= CLUI_FRAME_STATUSICONS;
- break;
- }
- if (dwOldFlags != cfg::dat.dwFlags) {
- InvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE);
- db_set_dw(0, "CLUI", "Frameflags", cfg::dat.dwFlags);
- if ((dwOldFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN)) != (cfg::dat.dwFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN))) {
- ConfigureFrame();
- ConfigureCLUIGeometry(1);
- }
- ConfigureEventArea();
- PostMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
- PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
- }
- }
- return FALSE;
-
- case WM_LBUTTONDOWN:
- if (g_ButtonItems) {
- POINT pt;
- GetCursorPos(&pt);
- return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
- }
- break;
-
- case WM_DISPLAYCHANGE:
- SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
- break;
-
- case WM_NOTIFY:
- if (((LPNMHDR)lParam)->hwndFrom == g_clistApi.hwndContactTree) {
- switch (((LPNMHDR)lParam)->code) {
- case CLN_LISTSIZECHANGE:
- sttProcessResize(hwnd, (NMCLISTCONTROL *)lParam);
- return FALSE;
-
- case NM_CLICK:
- {
- NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam;
- uint32_t hitFlags;
- SendMessage(g_clistApi.hwndContactTree, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y));
- if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0)
- break;
-
- if (db_get_b(0, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) {
- POINT pt;
- pt = nm->pt;
- ClientToScreen(g_clistApi.hwndContactTree, &pt);
- return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
- }
- }
- return FALSE;
- }
- }
- break;
-
- case WM_CONTEXTMENU:
- GetWindowRect(g_clistApi.hwndContactTree, &rc);
- {
- // x/y might be -1 if it was generated by a kb click
- POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
- if (pt.x == -1 && pt.y == -1) {
- // all this is done in screen-coords!
- GetCursorPos(&pt);
- // the mouse isnt near the window, so put it in the middle of the window
- if (!PtInRect(&rc, pt)) {
- pt.x = rc.left + (rc.right - rc.left) / 2;
- pt.y = rc.top + (rc.bottom - rc.top) / 2;
- }
- }
- if (PtInRect(&rc, pt)) {
- HMENU hMenu = Menu_BuildGroupMenu();
- TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
- Menu_DestroyNestedMenu(hMenu);
- return 0;
- }
- GetWindowRect(g_clistApi.hwndStatus, &rc);
- if (PtInRect(&rc, pt)) {
- HMENU hMenu;
- if (db_get_b(0, "CLUI", "SBarRightClk", 0))
- hMenu = Menu_GetMainMenu();
- else
- hMenu = Menu_GetStatusMenu();
- TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
- return 0;
- }
- }
- break;
-
- case WM_MEASUREITEM:
- if (((LPMEASUREITEMSTRUCT)lParam)->itemData == MENU_MIRANDAMENU) {
- ((LPMEASUREITEMSTRUCT)lParam)->itemWidth = g_cxsmIcon * 4 / 3;
- ((LPMEASUREITEMSTRUCT)lParam)->itemHeight = 0;
- return TRUE;
- }
- return Menu_MeasureItem(lParam);
-
- case WM_DRAWITEM:
- {
- LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
-
- if (hbmLockedPoint == nullptr) {
- hdcLockedPoint = CreateCompatibleDC(dis->hDC);
- hbmLockedPoint = CreateCompatibleBitmap(dis->hDC, 5, 5);
- hbmOldLockedPoint = reinterpret_cast<HBITMAP>(SelectObject(hdcLockedPoint, hbmLockedPoint));
- }
- if (dis->hwndItem == g_clistApi.hwndStatus) {
- ProtocolData *pd = (ProtocolData *)dis->itemData;
- if (IsBadCodePtr((FARPROC)pd))
- return TRUE;
- if (cfg::shutDown)
- return TRUE;
-
- char *szProto = pd->RealName;
- PROTOACCOUNT *pa = Proto_GetAccount(szProto);
- if (pa == nullptr)
- return TRUE;
-
- int nParts = SendMessage(g_clistApi.hwndStatus, SB_GETPARTS, 0, 0);
- SIZE textSize;
- uint8_t showOpts = db_get_b(0, "CLUI", "SBarShow", 1);
-
- SetBkMode(dis->hDC, TRANSPARENT);
- int x = dis->rcItem.left;
-
- if (showOpts & 1) {
- HICON hIcon;
-
- if (pa->iRealStatus >= ID_STATUS_CONNECTING && pa->iRealStatus < ID_STATUS_OFFLINE) {
- char szBuffer[128];
- mir_snprintf(szBuffer, "%s_conn", pd->RealName);
- hIcon = IcoLib_GetIcon(szBuffer);
- }
- else if (cfg::dat.bShowXStatusOnSbar && pa->iRealStatus > ID_STATUS_OFFLINE) {
- int xStatus;
- CUSTOM_STATUS cst = { sizeof(cst) };
- cst.flags = CSSF_MASK_STATUS;
- cst.status = &xStatus;
- if (ProtoServiceExists(pd->RealName, PS_GETCUSTOMSTATUSEX) && !CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cst) && xStatus > 0)
- hIcon = (HICON)CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); // get OWN xStatus icon (if set)
- else
- hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus);
- }
- else hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus);
-
- if (!(showOpts & 6) && cfg::dat.bEqualSections)
- x = (dis->rcItem.left + dis->rcItem.right - 16) >> 1;
- if (pd->protopos == 0)
- x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
- else if (pd->protopos == nParts - 1)
- x -= (cfg::dat.bCRight / 2);
- DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
- IcoLib_ReleaseIcon(hIcon);
-
- if (db_get_b(0, "CLUI", "sbar_showlocked", 1)) {
- if (pa->bIsLocked) {
- hIcon = Skin_LoadIcon(SKINICON_OTHER_STATUS_LOCKED);
- if (hIcon != nullptr) {
- DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
- IcoLib_ReleaseIcon(hIcon);
- }
- }
- }
- x += 18;
- }
- else {
- x += 2;
- if (pd->protopos == 0)
- x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
- else if (pd->protopos == nParts - 1)
- x -= (cfg::dat.bCRight / 2);
- }
-
- if (showOpts & 2) {
- wchar_t szName[64];
- wcsncpy_s(szName, pa->tszAccountName, _TRUNCATE);
-
- if (mir_wstrlen(szName) < _countof(szName) - 1)
- mir_wstrcat(szName, L" ");
- GetTextExtentPoint32(dis->hDC, szName, (int)mir_wstrlen(szName), &textSize);
- TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szName, (int)mir_wstrlen(szName));
- x += textSize.cx;
- }
- if (showOpts & 4) {
- wchar_t *szStatus = Clist_GetStatusModeDescription(pa->iRealStatus, 0);
- GetTextExtentPoint32(dis->hDC, szStatus, (int)mir_wstrlen(szStatus), &textSize);
- TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szStatus, (int)mir_wstrlen(szStatus));
- }
- }
- else if (dis->CtlType == ODT_MENU) {
- if (dis->itemData == MENU_MIRANDAMENU)
- break;
- return Menu_DrawItem(lParam);
- }
- }
- return 0;
-
- case WM_CLOSE:
- if (SETTING_WINDOWSTYLE_DEFAULT == db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT) && !g_plugin.getByte("AlwaysHideOnTB", 0)) {
- PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
- return 0;
- }
- g_clistApi.pfnShowHide();
- return 0;
-
- case CLUIINTM_REDRAW:
- if (show_on_first_autosize) {
- show_on_first_autosize = FALSE;
- ShowCLUI(hwnd);
- }
- RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-
- case CLUIINTM_STATUSBARUPDATE:
- CluiProtocolStatusChanged(0, nullptr);
- return 0;
-
- case WM_THEMECHANGED:
- API::updateState();
- break;
-
- case WM_DESTROY:
- if (cfg::dat.hdcBg) {
- SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
- DeleteObject(cfg::dat.hbmBg);
- DeleteDC(cfg::dat.hdcBg);
- cfg::dat.hdcBg = nullptr;
- }
- if (cfg::dat.bmpBackground) {
- SelectObject(cfg::dat.hdcPic, cfg::dat.hbmPicOld);
- DeleteDC(cfg::dat.hdcPic);
- DeleteObject(cfg::dat.bmpBackground);
- cfg::dat.bmpBackground = nullptr;
- }
- FreeProtocolData();
- if (hdcLockedPoint) {
- SelectObject(hdcLockedPoint, hbmOldLockedPoint);
- DeleteObject(hbmLockedPoint);
- DeleteDC(hdcLockedPoint);
- }
- // if this has not yet been set, do it now.
- // indicates that clist is shutting down and prevents various things
- // from happening at shutdown.
- if (!cfg::shutDown)
- cfg::shutDown = 1;
- CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)hFrameContactTree, 0);
- break;
- }
-
- return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
-}
-
-#ifndef CS_DROPSHADOW
-#define CS_DROPSHADOW 0x00020000
-#endif
-
-static int MetaChanged(WPARAM wParam, LPARAM lParam)
-{
- Clist_Broadcast(INTM_METACHANGEDEVENT, wParam, lParam);
- return 0;
-}
-
-static INT_PTR CLN_ShowMainMenu(WPARAM, LPARAM)
-{
- POINT pt;
- GetCursorPos(&pt);
- TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr);
- return 0;
-}
-
-static INT_PTR CLN_ShowStatusMenu(WPARAM, LPARAM)
-{
- POINT pt;
- GetCursorPos(&pt);
- TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr);
- return 0;
-}
-
-#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu"
-#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu"
-
-void LoadCLUIModule(void)
-{
- HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded);
-
- WNDCLASS wndclass;
- wndclass.style = 0;
- wndclass.lpfnWndProc = EventAreaWndProc;
- wndclass.cbClsExtra = 0;
- wndclass.cbWndExtra = 0;
- wndclass.hInstance = g_plugin.getInst();
- wndclass.hIcon = nullptr;
- wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wndclass.hbrBackground = (HBRUSH)COLOR_3DFACE;
- wndclass.lpszMenuName = nullptr;
- wndclass.lpszClassName = L"EventAreaClass";
- RegisterClass(&wndclass);
-
- oldhideoffline = Clist::HideOffline;
- cluiPos.left = g_plugin.getDword("x", 600);
- cluiPos.top = g_plugin.getDword("y", 200);
- cluiPos.right = g_plugin.getDword("Width", 150);
- cluiPos.bottom = g_plugin.getDword("Height", 350);
-
- LoadExtraIconModule();
- LoadCLUIFramesModule();
-
- CreateServiceFunction(MS_CLUI_SHOWMAINMENU, CLN_ShowMainMenu);
- CreateServiceFunction(MS_CLUI_SHOWSTATUSMENU, CLN_ShowStatusMenu);
-
- if (db_get_b(0, "CLUI", "FloaterMode", 0)) {
- MessageBox(nullptr,
- TranslateT("You need the FloatingContacts plugin, cause the embedded floating contacts were removed."),
- TranslateT("Warning"), MB_OK | MB_ICONWARNING);
- db_unset(0, "CLUI", "FloaterMode");
- }
-
- MF_InitCheck();
-}
-
-void OnCreateClc()
-{
- HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged);
- HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaChanged);
-
- InitGroupMenus();
- LoadExtBkSettingsFromDB();
- PreCreateCLC(g_clistApi.hwndContactList);
-}
-
-struct
-{
- const wchar_t *tszName;
- int iMask;
-}
-static clistFontDescr[] =
-{
- { LPGENW("Standard contacts"), FIDF_CLASSGENERAL },
- { LPGENW("Online contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
- { LPGENW("Offline contacts"), FIDF_CLASSGENERAL },
- { LPGENW("Contacts which are 'not on list'"), FIDF_CLASSGENERAL },
- { LPGENW("Groups"), FIDF_CLASSHEADER },
- { LPGENW("Group member counts"), FIDF_CLASSHEADER },
- { LPGENW("Dividers"), FIDF_CLASSSMALL },
- { LPGENW("Offline contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
- { LPGENW("Status mode"), FIDF_CLASSGENERAL },
- { LPGENW("Frame titles"), FIDF_CLASSGENERAL },
- { LPGENW("Event area"), FIDF_CLASSGENERAL },
- { LPGENW("Contact list local time"), FIDF_CLASSGENERAL }
-};
-
-void FS_RegisterFonts()
-{
- FontIDW fid = {};
- wcsncpy_s(fid.group, LPGENW("Contact list"), _TRUNCATE);
- strncpy_s(fid.dbSettingsGroup, "CLC", _TRUNCATE);
- fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS | FIDF_APPENDNAME | FIDF_SAVEPOINTSIZE;
-
- HDC hdc = GetDC(nullptr);
- for (int i = 0; i < _countof(clistFontDescr); i++) {
- LOGFONT lf;
- Clist_GetFontSetting(i, &lf, &fid.deffontsettings.colour);
- lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
-
- wcsncpy_s(fid.deffontsettings.szFace, lf.lfFaceName, _TRUNCATE);
- fid.deffontsettings.charset = lf.lfCharSet;
- fid.deffontsettings.size = (char)lf.lfHeight;
- fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0);
-
- fid.flags &= ~FIDF_CLASSMASK;
- fid.flags |= clistFontDescr[i].iMask;
-
- wcsncpy_s(fid.name, clistFontDescr[i].tszName, _TRUNCATE);
-
- char idstr[10];
- mir_snprintf(idstr, "Font%d", i);
- strncpy_s(fid.setting, idstr, _TRUNCATE);
- fid.order = i;
- g_plugin.addFont(&fid);
- }
- ReleaseDC(nullptr, hdc);
-
- // and colours
- ColourIDW colourid = {};
- colourid.order = 0;
- strncpy_s(colourid.dbSettingsGroup, "CLC", _TRUNCATE);
-
- strncpy_s(colourid.setting, "BkColour", _TRUNCATE);
- wcsncpy_s(colourid.name, LPGENW("Background"), _TRUNCATE);
- wcsncpy_s(colourid.group, LPGENW("Contact list"), _TRUNCATE);
- colourid.defcolour = CLCDEFAULT_BKCOLOUR;
- g_plugin.addColor(&colourid);
-
- strncpy_s(colourid.setting, "SelTextColour", _TRUNCATE);
- wcsncpy_s(colourid.name, LPGENW("Selected text"), _TRUNCATE);
- colourid.order = 1;
- colourid.defcolour = CLCDEFAULT_SELTEXTCOLOUR;
- g_plugin.addColor(&colourid);
-
- strncpy_s(colourid.setting, "HotTextColour", _TRUNCATE);
- wcsncpy_s(colourid.name, LPGENW("Hottrack text"), _TRUNCATE);
- colourid.order = 1;
- colourid.defcolour = CLCDEFAULT_HOTTEXTCOLOUR;
- g_plugin.addColor(&colourid);
-
- strncpy_s(colourid.setting, "QuickSearchColour", _TRUNCATE);
- wcsncpy_s(colourid.name, LPGENW("Quicksearch text"), _TRUNCATE);
- colourid.order = 1;
- colourid.defcolour = CLCDEFAULT_QUICKSEARCHCOLOUR;
- g_plugin.addColor(&colourid);
-
- strncpy_s(colourid.dbSettingsGroup, "CLUI", _TRUNCATE);
- strncpy_s(colourid.setting, "clr_frameborder", _TRUNCATE);
- wcsncpy_s(colourid.name, LPGENW("Embedded frames border"), _TRUNCATE);
- colourid.order = 1;
- colourid.defcolour = RGB(40, 40, 40);
- g_plugin.addColor(&colourid);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-03 Miranda ICQ/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_findadd.h>
+#include "cluiframes.h"
+#include "coolscroll.h"
+
+#define TM_AUTOALPHA 1
+#define TIMERID_AUTOSIZE 100
+#define MENU_MIRANDAMENU 0xFFFF1234
+
+int g_fading_active = 0;
+
+static RECT g_PreSizeRect, g_SizingRect;
+static int g_sizingmethod;
+static LONG g_CLUI_x_off, g_CLUI_y_off, g_CLUI_y1_off, g_CLUI_x1_off;
+static RECT rcWPC;
+
+static int transparentFocus = 1;
+static byte oldhideoffline;
+static int disableautoupd = 1;
+static int hFrameContactTree;
+extern RECT old_window_rect, new_window_rect;
+
+extern BOOL g_trayTooltipActive;
+extern POINT tray_hover_pos;
+extern HWND g_hwndViewModeFrame, g_hwndEventArea, g_hwndToolbarFrame;
+
+extern ImageItem *g_CLUIImageItem;
+extern HBRUSH g_CLUISkinnedBkColor;
+extern HWND g_hwndSFL;
+extern ButtonItem *g_ButtonItems;
+extern COLORREF g_CLUISkinnedBkColorRGB;
+extern FRAMEWND *wndFrameCLC;
+extern HPEN g_hPenCLUIFrames;
+
+static uint8_t old_cliststate, show_on_first_autosize = FALSE;
+
+RECT cluiPos;
+
+wchar_t *statusNames[12];
+
+extern LRESULT CALLBACK EventAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+extern int hNotifyFrame;
+
+void MF_InitCheck(void);
+void InitGroupMenus();
+void FS_RegisterFonts();
+void LoadExtraIconModule();
+void RemoveFromTaskBar(HWND hWnd);
+
+extern LONG g_cxsmIcon, g_cysmIcon;
+
+SIZE g_oldSize = { 0 };
+POINT g_oldPos = { 0 };
+int during_sizing = 0;
+extern int dock_prevent_moving;
+
+static HDC hdcLockedPoint = nullptr;
+static HBITMAP hbmLockedPoint = nullptr, hbmOldLockedPoint = nullptr;
+
+HICON overlayicons[10];
+
+static IconItem myIcons[] = {
+ { LPGEN("Toggle show online/offline"), "CLN_online", IDI_HIDEOFFLINE },
+ { LPGEN("Toggle groups"), "CLN_groups", IDI_HIDEGROUPS },
+ { LPGEN("Find contacts"), "CLN_findadd", IDI_FINDANDADD },
+ { LPGEN("Open preferences"), "CLN_options", IDI_TBOPTIONS },
+ { LPGEN("Toggle sounds"), "CLN_sound", IDI_SOUNDSON },
+ { LPGEN("Minimize contact list"), "CLN_minimize", IDI_MINIMIZE },
+ { LPGEN("Show TabSRMM session list"), "CLN_slist", IDI_TABSRMMSESSIONLIST },
+ { LPGEN("Show TabSRMM menu"), "CLN_menu", IDI_TABSRMMMENU },
+ { LPGEN("Sounds are off"), "CLN_soundsoff", IDI_SOUNDSOFF },
+ { LPGEN("Select view mode"), "CLN_CLVM_select", IDI_CLVM_SELECT },
+ { LPGEN("Reset view mode"), "CLN_CLVM_reset", IDI_DELETE },
+ { LPGEN("Configure view modes"), "CLN_CLVM_options", IDI_CLVM_OPTIONS },
+ { LPGEN("Show menu"), "CLN_topmenu", IDI_TBTOPMENU },
+ { LPGEN("Setup accounts"), "CLN_accounts", IDI_TBACCOUNTS }
+};
+
+HWND hTbMenu, hTbGlobalStatus;
+
+static void Tweak_It(COLORREF clr)
+{
+ SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes(g_clistApi.hwndContactList, clr, 0, LWA_COLORKEY);
+ cfg::dat.colorkey = clr;
+}
+
+static void LayoutButtons(HWND hwnd, RECT *rc)
+{
+ RECT rect;
+ uint8_t left_offset = cfg::dat.bCLeft - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
+ uint8_t right_offset = cfg::dat.bCRight - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
+ uint8_t delta = left_offset + right_offset;
+ ButtonItem *btnItems = g_ButtonItems;
+
+ if (rc == nullptr)
+ GetClientRect(hwnd, &rect);
+ else
+ rect = *rc;
+
+ rect.bottom -= cfg::dat.bCBottom;
+
+ if (g_ButtonItems) {
+ while (btnItems) {
+ LONG x = (btnItems->xOff >= 0) ? rect.left + btnItems->xOff : rect.right - abs(btnItems->xOff);
+ LONG y = (btnItems->yOff >= 0) ? rect.top + btnItems->yOff : rect.bottom - cfg::dat.statusBarHeight;
+
+ SetWindowPos(btnItems->hWnd, nullptr, x, y, btnItems->width, btnItems->height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ btnItems = btnItems->nextItem;
+ }
+ }
+
+ SetWindowPos(hTbMenu, nullptr, 2 + left_offset, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
+ 21 * 3, 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
+
+ SetWindowPos(hTbGlobalStatus, nullptr, left_offset + (3 * 21) + 3, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
+ rect.right - delta - (3 * 21 + 5), 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
+
+}
+
+static int FS_FontsChanged(WPARAM, LPARAM)
+{
+ COLORREF clr_cluiframes = db_get_dw(0, "CLUI", "clr_frameborder", RGB(40, 40, 40));
+
+ if (g_hPenCLUIFrames)
+ DeleteObject(g_hPenCLUIFrames);
+ g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, clr_cluiframes);
+
+ Clist_ClcOptionsChanged();
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+// create the CLC control, but not yet the frame. The frame containing the CLC should be created as the
+// last frame of all.
+static HWND PreCreateCLC(HWND parent)
+{
+ g_clistApi.hwndContactTree = CreateWindow(CLISTCONTROL_CLASSW, L"",
+ WS_CHILD | CLS_CONTACTLIST | (Clist::UseGroups ? CLS_USEGROUPS : 0) | (Clist::HideOffline ? CLS_HIDEOFFLINE : 0) | (Clist::HideEmptyGroups ? CLS_HIDEEMPTYGROUPS : 0) | CLS_MULTICOLUMN,
+ 0, 0, 0, 0, parent, nullptr, g_plugin.getInst(), (LPVOID)0xff00ff00);
+
+ cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
+ return g_clistApi.hwndContactTree;
+}
+
+// create internal frames, including the last frame (actual CLC control)
+static int CreateCLC()
+{
+ ExtraIcon_Reload();
+ g_clistApi.pfnSetHideOffline(oldhideoffline);
+ disableautoupd = 0;
+ {
+ CLISTFrame frame = { 0 };
+ frame.cbSize = sizeof(frame);
+ frame.szName.a = "EventArea";
+ frame.szTBname.a = LPGEN("Event area");
+ frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
+ frame.height = 20;
+ frame.Flags = F_VISIBLE | F_SHOWTBTIP | F_NOBORDER;
+ frame.align = alBottom;
+ frame.hWnd = CreateWindowExA(0, "EventAreaClass", "evt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, g_clistApi.hwndContactList, (HMENU)nullptr, g_plugin.getInst(), nullptr);
+ g_hwndEventArea = frame.hWnd;
+ hNotifyFrame = g_plugin.addFrame(&frame);
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME, hNotifyFrame, FU_FMPOS);
+ HideShowNotifyFrame();
+ CreateViewModeFrame();
+ }
+ {
+ CLISTFrame Frame = { 0 };
+ Frame.cbSize = sizeof(CLISTFrame);
+ Frame.hWnd = g_clistApi.hwndContactTree;
+ Frame.align = alClient;
+ Frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
+ Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER;
+ Frame.szName.a = "My contacts";
+ Frame.szTBname.a = LPGEN("My contacts");
+ Frame.height = 200;
+ hFrameContactTree = g_plugin.addFrame(&Frame);
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_TBTIPNAME | FO_UNICODETEXT, hFrameContactTree), (LPARAM)TranslateT("My contacts"));
+
+ // ugly, but working hack. Prevent that annoying little scroll bar from appearing in the "My Contacts" title bar
+ uint32_t flags = (uint32_t)CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), 0);
+ flags |= F_VISIBLE;
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), flags);
+ }
+
+ SetButtonToSkinned();
+ return 0;
+}
+
+static int CluiModulesLoaded(WPARAM, LPARAM)
+{
+ FS_RegisterFonts();
+ HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
+ return 0;
+}
+
+static HICON hIconSaved = nullptr;
+
+void ClearIcons(int mode)
+{
+ for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
+ if (overlayicons[i - IDI_OVL_OFFLINE] != nullptr) {
+ if (mode)
+ DestroyIcon(overlayicons[i - IDI_OVL_OFFLINE]);
+ overlayicons[i - IDI_OVL_OFFLINE] = nullptr;
+ }
+ }
+}
+
+static void CacheClientIcons()
+{
+ ClearIcons(0);
+
+ for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
+ char szBuffer[128];
+ mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
+ overlayicons[i - IDI_OVL_OFFLINE] = IcoLib_GetIcon(szBuffer);
+ }
+}
+
+static void InitIcoLib()
+{
+ g_plugin.registerIcon(LPGEN("Contact list") "/" LPGEN("Default"), myIcons);
+
+ for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_INVISIBLE; i++) {
+ char szBuffer[128];
+ mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
+ IconItemT icon[] = { { Clist_GetStatusModeDescription(ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE), 0), szBuffer, i } };
+ g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Overlay icons"), icon);
+ }
+
+ for (auto &pa : Accounts()) {
+ if (!pa->IsEnabled() || CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
+ continue;
+
+ wchar_t szDescr[128];
+ mir_snwprintf(szDescr, TranslateT("%s connecting"), pa->tszAccountName);
+ IconItemT icon[] = { { szDescr, "conn", IDI_PROTOCONNECTING } };
+ g_plugin.registerIconW(LPGENW("Contact list") L"/" LPGENW("Connecting icons"), icon, pa->szModuleName);
+ }
+}
+
+static int IcoLibChanged(WPARAM, LPARAM)
+{
+ IcoLibReloadIcons();
+ return 0;
+}
+
+void CreateButtonBar(HWND hWnd)
+{
+ hTbMenu = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBMENU, g_plugin.getInst(), nullptr);
+ CustomizeButton(hTbMenu, false, false, false);
+ SetWindowText(hTbMenu, TranslateT("Menu"));
+ SendMessage(hTbMenu, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_OTHER_MAINMENU));
+ SendMessage(hTbMenu, BUTTONSETSENDONDOWN, TRUE, 0);
+ SendMessage(hTbMenu, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open main menu"), 0);
+
+ hTbGlobalStatus = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBGLOBALSTATUS, g_plugin.getInst(), nullptr);
+ CustomizeButton(hTbGlobalStatus, false, false, false);
+ SetWindowText(hTbGlobalStatus, TranslateT("Offline"));
+ SendMessage(hTbGlobalStatus, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_STATUS_OFFLINE));
+ SendMessage(hTbGlobalStatus, BUTTONSETSENDONDOWN, TRUE, 0);
+ SendMessage(hTbGlobalStatus, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Set status modes"), 0);
+}
+
+// if mode != 0 we do first time init, otherwise only reload the extra icon stuff
+void CLN_LoadAllIcons(BOOL mode)
+{
+ if (mode) {
+ InitIcoLib();
+ HookEvent(ME_SKIN_ICONSCHANGED, IcoLibChanged);
+ }
+ CacheClientIcons();
+}
+
+void ConfigureEventArea()
+{
+ int iCount = GetMenuItemCount(cfg::dat.hMenuNotify);
+ uint32_t dwFlags = cfg::dat.dwFlags;
+ int oldstate = cfg::dat.notifyActive;
+ int dwVisible = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hNotifyFrame), 0) & F_VISIBLE;
+
+ if (dwVisible) {
+ if (dwFlags & CLUI_FRAME_AUTOHIDENOTIFY)
+ cfg::dat.notifyActive = iCount > 0 ? 1 : 0;
+ else
+ cfg::dat.notifyActive = 1;
+ }
+ else
+ cfg::dat.notifyActive = 0;
+
+ if (oldstate != cfg::dat.notifyActive)
+ HideShowNotifyFrame();
+}
+
+void ConfigureFrame()
+{
+ int show = cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? SW_SHOW : SW_HIDE;
+ ShowWindow(hTbMenu, show);
+ ShowWindow(hTbGlobalStatus, show);
+}
+
+void IcoLibReloadIcons()
+{
+ CacheClientIcons();
+ ExtraIcon_Reload();
+ ExtraIcon_SetAll();
+
+ Clist_Broadcast(CLM_AUTOREBUILD, 0, 0);
+ SendMessage(g_hwndViewModeFrame, WM_USER + 100, 0, 0);
+}
+
+void ConfigureCLUIGeometry(int mode)
+{
+ RECT rcStatus;
+ uint32_t clmargins = db_get_dw(0, "CLUI", "clmargins", 0);
+
+ cfg::dat.bCLeft = LOBYTE(LOWORD(clmargins));
+ cfg::dat.bCRight = HIBYTE(LOWORD(clmargins));
+ cfg::dat.bCTop = LOBYTE(HIWORD(clmargins));
+ cfg::dat.bCBottom = HIBYTE(HIWORD(clmargins));
+
+ if (mode) {
+ if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
+ SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0);
+ GetWindowRect(g_clistApi.hwndStatus, &rcStatus);
+ cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
+ }
+ else cfg::dat.statusBarHeight = 0;
+ }
+
+ cfg::dat.topOffset = cfg::dat.bCTop;
+ cfg::dat.bottomOffset = (cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? 2 + 21 : 0) + cfg::dat.bCBottom;
+
+ if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
+ cfg::dat.topOffset += 2;
+ cfg::dat.bottomOffset += 2;
+ cfg::dat.bCLeft += 3;
+ cfg::dat.bCRight += 3;
+ }
+}
+
+// set the states of defined database action buttons (only if button is a toggle)
+void SetDBButtonStates(MCONTACT hPassedContact)
+{
+ ButtonItem *buttonItem = g_ButtonItems;
+ MCONTACT hContact = 0, hFinalContact = 0;
+ char *szModule, *szSetting;
+ ClcContact *contact = nullptr;
+
+ if (cfg::clcdat && hPassedContact == 0) {
+ g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
+ if (contact && contact->type == CLCIT_CONTACT) {
+ hContact = contact->hContact;
+ }
+ }
+
+ while (buttonItem) {
+ BOOL result = FALSE;
+
+ if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+ szModule = buttonItem->szModule;
+ szSetting = buttonItem->szSetting;
+ if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (hContact == 0) {
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, BST_UNCHECKED, 0);
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+ if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = Proto_GetBaseAccountName(hContact);
+ hFinalContact = hContact;
+ }
+ else
+ hFinalContact = 0;
+
+ if (buttonItem->type == DBVT_ASCIIZ) {
+ DBVARIANT dbv = { 0 };
+
+ if (!db_get_s(hFinalContact, szModule, szSetting, &dbv)) {
+ result = !mir_strcmp((char *)buttonItem->bValuePush, dbv.pszVal);
+ db_free(&dbv);
+ }
+ }
+ else {
+ switch (buttonItem->type) {
+ case DBVT_BYTE: {
+ uint8_t val = db_get_b(hFinalContact, szModule, szSetting, 0);
+ result = (val == buttonItem->bValuePush[0]);
+ break;
+ }
+ case DBVT_WORD: {
+ uint16_t val = db_get_w(hFinalContact, szModule, szSetting, 0);
+ result = (val == *((uint16_t *)&buttonItem->bValuePush));
+ break;
+ }
+ case DBVT_DWORD:
+ uint32_t val = db_get_dw(hFinalContact, szModule, szSetting, 0);
+ result = (val == *((uint32_t *)&buttonItem->bValuePush));
+ break;
+ }
+ }
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, (WPARAM)result, 0);
+ buttonItem = buttonItem->nextItem;
+ }
+}
+
+// set states of standard buttons (pressed/unpressed)
+void SetButtonStates()
+{
+ ButtonItem *buttonItem = g_ButtonItems;
+
+ if (g_ButtonItems) {
+ while (buttonItem) {
+ if (buttonItem->dwFlags & BUTTON_ISINTERNAL) {
+ switch (buttonItem->uId) {
+ case IDC_STBSOUND:
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, cfg::dat.soundsOff ? BST_CHECKED : BST_UNCHECKED, 0);
+ break;
+ case IDC_STBHIDEOFFLINE:
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::HideOffline, 0);
+ break;
+ case IDC_STBHIDEGROUPS:
+ SendMessage(buttonItem->hWnd, BM_SETCHECK, Clist::UseGroups, 0);
+ break;
+ }
+ }
+ buttonItem = buttonItem->nextItem;
+ }
+ }
+}
+
+void BlitWallpaper(HDC hdc, RECT *rc, struct ClcData *dat)
+{
+ int x, y;
+ int bitx, bity;
+ int maxx, maxy;
+ int destw, desth, height, width;
+ BITMAP *bmp = &cfg::dat.bminfoBg;
+ LONG clip = cfg::dat.bClipBorder;
+
+ if (dat == nullptr)
+ return;
+
+ SetStretchBltMode(hdc, HALFTONE);
+
+ y = rc->top;
+
+ rc->left = max(rc->left, clip);
+ rc->right = min(rc->right - clip, rc->right);
+ rc->top = max(rc->top, clip);
+ rc->bottom = min(rc->bottom - clip, rc->bottom);
+
+ width = rc->right - rc->left;
+ height = rc->bottom - rc->top;
+ HRGN my_rgn = CreateRectRgn(rc->left, rc->top, rc->right, rc->bottom);
+ SelectClipRgn(hdc, my_rgn);
+ maxx = dat->backgroundBmpUse & CLBF_TILEH ? rc->right : rc->left + 1;
+ maxy = dat->backgroundBmpUse & CLBF_TILEV ? rc->bottom : y + 1;
+ switch (dat->backgroundBmpUse & CLBM_TYPE) {
+ case CLB_STRETCH:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ if (width * bmp->bmHeight < height * bmp->bmWidth) {
+ desth = height;
+ destw = desth * bmp->bmWidth / bmp->bmHeight;
+ }
+ else {
+ destw = width;
+ desth = destw * bmp->bmHeight / bmp->bmWidth;
+ }
+ }
+ else {
+ destw = width;
+ desth = height;
+ }
+ break;
+ case CLB_STRETCHH:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ destw = width;
+ desth = destw * bmp->bmHeight / bmp->bmWidth;
+ }
+ else {
+ destw = width;
+ desth = bmp->bmHeight;
+ }
+ break;
+
+ case CLB_STRETCHV:
+ if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
+ desth = height;
+ destw = desth * bmp->bmWidth / bmp->bmHeight;
+ }
+ else {
+ destw = bmp->bmWidth;
+ desth = height;
+ }
+ break;
+
+ default:
+ //clb_topleft
+ destw = bmp->bmWidth;
+ desth = bmp->bmHeight;
+ break;
+ }
+
+ bitx = 0;
+ bity = 0;
+ for (; y < maxy; y += desth) {
+ for (x = rc->left; x < maxx; x += destw)
+ StretchBlt(hdc, x, y, destw, desth, cfg::dat.hdcPic, bitx, bity, bmp->bmWidth, bmp->bmHeight, SRCCOPY);
+ }
+ SelectClipRgn(hdc, nullptr);
+ DeleteObject(my_rgn);
+}
+
+void ReloadThemedOptions()
+{
+ cfg::dat.bSkinnedStatusBar = db_get_b(0, "CLUI", "sb_skinned", 0);
+ cfg::dat.bUsePerProto = db_get_b(0, "CLCExt", "useperproto", 0);
+ cfg::dat.bOverridePerStatusColors = db_get_b(0, "CLCExt", "override_status", 0);
+ cfg::dat.bRowSpacing = db_get_b(0, "CLC", "RowGap", 0);
+ cfg::dat.bApplyIndentToBg = db_get_b(0, "CLCExt", "applyindentbg", 0);
+ cfg::dat.bWallpaperMode = db_get_b(0, "CLUI", "UseBkSkin", 1);
+ cfg::dat.bClipBorder = db_get_b(0, "CLUI", "clipborder", 0);
+ cfg::dat.cornerRadius = db_get_b(0, "CLCExt", "CornerRad", 6);
+ cfg::dat.gapBetweenFrames = (uint8_t)db_get_dw(0, "CLUIFrames", "GapBetweenFrames", 1);
+ cfg::dat.bUseDCMirroring = db_get_b(0, "CLC", "MirrorDC", 0);
+ cfg::dat.bGroupAlign = db_get_b(0, "CLC", "GroupAlign", 0);
+ if (cfg::dat.hBrushColorKey)
+ DeleteObject(cfg::dat.hBrushColorKey);
+ cfg::dat.hBrushColorKey = CreateSolidBrush(RGB(255, 0, 255));
+ cfg::dat.bWantFastGradients = db_get_b(0, "CLCExt", "FastGradients", 0);
+ cfg::dat.titleBarHeight = db_get_b(0, "CLCExt", "frame_height", DEFAULT_TITLEBAR_HEIGHT);
+ cfg::dat.group_padding = db_get_dw(0, "CLCExt", "grp_padding", 0);
+}
+
+static RECT rcWindow = { 0 };
+
+static void sttProcessResize(HWND hwnd, NMCLISTCONTROL *nmc)
+{
+ RECT rcTree, rcWorkArea, rcOld;
+ int maxHeight, newHeight;
+ int winstyle, skinHeight = 0;
+
+ if (disableautoupd)
+ return;
+
+ if (!db_get_b(0, "CLUI", "AutoSize", 0))
+ return;
+
+ if (Docking_IsDocked(0, 0))
+ return;
+ if (hFrameContactTree == 0)
+ return;
+
+ maxHeight = db_get_b(0, "CLUI", "MaxSizeHeight", 75);
+ rcOld = rcWindow;
+
+ GetWindowRect(hwnd, &rcWindow);
+ GetWindowRect(g_clistApi.hwndContactTree, &rcTree);
+ winstyle = GetWindowLongPtr(g_clistApi.hwndContactTree, GWL_STYLE);
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
+ HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ rcWorkArea = mi.rcWork;
+
+ if (nmc->pt.y > (rcWorkArea.bottom - rcWorkArea.top)) {
+ nmc->pt.y = (rcWorkArea.bottom - rcWorkArea.top);
+ }
+
+ if (winstyle & CLS_SKINNEDFRAME) {
+ BOOL hasTitleBar = wndFrameCLC ? wndFrameCLC->TitleBar.ShowTitleBar : 0;
+ StatusItems_t *item = arStatusItems[(hasTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB : ID_EXTBKOWNEDFRAMEBORDER) - ID_STATUS_OFFLINE];
+ skinHeight = item->IGNORED ? 0 : item->MARGIN_BOTTOM + item->MARGIN_TOP;
+ }
+
+ newHeight = max(nmc->pt.y, 3) + 1 + ((winstyle & WS_BORDER) ? 2 : 0) + skinHeight + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top);
+ if (newHeight == (rcWindow.bottom - rcWindow.top) && show_on_first_autosize == FALSE)
+ return;
+
+ if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100)
+ newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100;
+ if (db_get_b(0, "CLUI", "AutoSizeUpward", 0)) {
+ rcWindow.top = rcWindow.bottom - newHeight;
+ if (rcWindow.top < rcWorkArea.top) rcWindow.top = rcWorkArea.top;
+ }
+ else {
+ rcWindow.bottom = rcWindow.top + newHeight;
+ if (rcWindow.bottom > rcWorkArea.bottom) rcWindow.bottom = rcWorkArea.bottom;
+ }
+ if (cfg::dat.szOldCTreeSize.cx != rcTree.right - rcTree.left) {
+ cfg::dat.szOldCTreeSize.cx = rcTree.right - rcTree.left;
+ return;
+ }
+ KillTimer(hwnd, TIMERID_AUTOSIZE);
+ SetTimer(hwnd, TIMERID_AUTOSIZE, 100, nullptr);
+}
+
+int CustomDrawScrollBars(NMCSBCUSTOMDRAW *nmcsbcd)
+{
+ switch (nmcsbcd->hdr.code) {
+ case NM_COOLSB_CUSTOMDRAW:
+ static HDC hdcScroll = nullptr;
+ static HBITMAP hbmScroll, hbmScrollOld;
+ static LONG scrollLeft, scrollRight, scrollHeight, scrollYmin, scrollYmax;
+
+ switch (nmcsbcd->dwDrawStage) {
+ case CDDS_PREPAINT:
+ if (cfg::dat.bSkinnedScrollbar) // XXX fix (verify skin items to be complete, otherwise don't draw
+ return CDRF_SKIPDEFAULT;
+ return CDRF_DODEFAULT;
+
+ case CDDS_POSTPAINT:
+ return 0;
+
+ case CDDS_ITEMPREPAINT:
+ HDC hdc = nmcsbcd->hdc;
+ StatusItems_t *item = nullptr, *arrowItem = nullptr;
+ UINT uItemID = ID_EXTBKSCROLLBACK;
+ HRGN rgn = nullptr;
+
+ RECT rc;
+ GetWindowRect(g_clistApi.hwndContactTree, &rc);
+
+ POINT pt;
+ pt.x = rc.left;
+ pt.y = rc.top;
+ ScreenToClient(g_clistApi.hwndContactList, &pt);
+ hdcScroll = hdc;
+ BitBlt(hdcScroll, nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right - nmcsbcd->rect.left,
+ nmcsbcd->rect.bottom - nmcsbcd->rect.top, cfg::dat.hdcBg, pt.x + nmcsbcd->rect.left, pt.y + nmcsbcd->rect.top, SRCCOPY);
+
+ switch (nmcsbcd->uItem) {
+ case HTSCROLL_UP:
+ case HTSCROLL_DOWN:
+ uItemID = (nmcsbcd->uState == CDIS_DEFAULT || nmcsbcd->uState == CDIS_DISABLED) ? ID_EXTBKSCROLLBUTTON :
+ (nmcsbcd->uState == CDIS_HOT ? ID_EXTBKSCROLLBUTTONHOVER : ID_EXTBKSCROLLBUTTONPRESSED);
+ break;
+ case HTSCROLL_PAGEGDOWN:
+ case HTSCROLL_PAGEGUP:
+ uItemID = nmcsbcd->uItem == HTSCROLL_PAGEGUP ? ID_EXTBKSCROLLBACK : ID_EXTBKSCROLLBACKLOWER;
+ rgn = CreateRectRgn(nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right, nmcsbcd->rect.bottom);
+ SelectClipRgn(hdcScroll, rgn);
+ break;
+ case HTSCROLL_THUMB:
+ uItemID = nmcsbcd->uState == CDIS_SELECTED ? ID_EXTBKSCROLLTHUMBPRESSED : ID_EXTBKSCROLLTHUMB;
+ break;
+ default:
+ break;
+ }
+
+ uItemID -= ID_STATUS_OFFLINE;
+ item = arStatusItems[uItemID];
+ if (!item->IGNORED) {
+ int alpha = nmcsbcd->uState == CDIS_DISABLED ? item->ALPHA - 50 : item->ALPHA;
+ DrawAlpha(hdcScroll, &nmcsbcd->rect, item->COLOR, alpha, item->COLOR2, item->COLOR2_TRANSPARENT,
+ item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ }
+ uint32_t dfcFlags = DFCS_FLAT | (nmcsbcd->uState == CDIS_DISABLED ? DFCS_INACTIVE :
+ (nmcsbcd->uState == CDIS_HOT ? DFCS_HOT : (nmcsbcd->uState == CDIS_SELECTED ? DFCS_PUSHED : 0)));
+
+ if (nmcsbcd->uItem == HTSCROLL_UP)
+ arrowItem = arStatusItems[ID_EXTBKSCROLLARROWUP - ID_STATUS_OFFLINE];
+ if (nmcsbcd->uItem == HTSCROLL_DOWN)
+ arrowItem = arStatusItems[ID_EXTBKSCROLLARROWDOWN - ID_STATUS_OFFLINE];
+ if (arrowItem && !arrowItem->IGNORED)
+ DrawAlpha(hdcScroll, &nmcsbcd->rect, arrowItem->COLOR, arrowItem->ALPHA, arrowItem->COLOR2, arrowItem->COLOR2_TRANSPARENT,
+ arrowItem->GRADIENT, arrowItem->CORNER, arrowItem->BORDERSTYLE, arrowItem->imageItem);
+ else if (arrowItem)
+ DrawFrameControl(hdcScroll, &nmcsbcd->rect, DFC_SCROLL, (nmcsbcd->uItem == HTSCROLL_UP ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | dfcFlags);
+
+ if (rgn) {
+ SelectClipRgn(hdcScroll, nullptr);
+ DeleteObject(rgn);
+ }
+ }
+ }
+ return 0;
+}
+
+static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact)
+{
+ if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (hContact == 0)
+ return 0;
+
+ if (item->dwFlags & BUTTON_PASSHCONTACTW)
+ *wParam = hContact;
+ else if (item->dwFlags & BUTTON_PASSHCONTACTL)
+ *lParam = hContact;
+ }
+ return 1;
+}
+
+static void ShowCLUI(HWND hwnd)
+{
+ int state = old_cliststate;
+ int onTop = g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT);
+
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
+
+ if (state == SETTING_STATE_NORMAL) {
+ SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
+ ShowWindow(g_clistApi.hwndContactList, SW_SHOWNORMAL);
+ SendMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
+ }
+ else if (state == SETTING_STATE_MINIMIZED) {
+ cfg::dat.forceResize = TRUE;
+ ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
+ }
+ else if (state == SETTING_STATE_HIDDEN) {
+ cfg::dat.forceResize = TRUE;
+ ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
+ }
+ SetWindowPos(g_clistApi.hwndContactList, onTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
+ DrawMenuBar(hwnd);
+ if (cfg::dat.autosize) {
+ SendMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
+ SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0);
+ }
+}
+
+static void GetButtonRect(HWND hwnd, RECT *rc)
+{
+ if (hwnd)
+ GetWindowRect(hwnd, rc);
+ else {
+ POINT pt;
+ GetCursorPos(&pt);
+ rc->bottom = rc->top = pt.y;
+ rc->left = rc->right = pt.x;
+ }
+}
+
+LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+
+ switch (msg) {
+ case WM_CREATE:
+ {
+ int flags = WS_CHILD | CCS_BOTTOM;
+ flags |= db_get_b(0, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0;
+ flags |= db_get_b(0, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0;
+ g_clistApi.hwndStatus = CreateWindow(STATUSCLASSNAME, nullptr, flags, 0, 0, 0, 0, hwnd, nullptr, g_plugin.getInst(), nullptr);
+ if (flags & WS_VISIBLE) {
+ ShowWindow(g_clistApi.hwndStatus, SW_SHOW);
+ SendMessage(g_clistApi.hwndStatus, WM_SIZE, 0, 0);
+ }
+ mir_subclassWindow(g_clistApi.hwndStatus, NewStatusBarWndProc);
+ SetClassLong(g_clistApi.hwndStatus, GCL_STYLE, GetClassLong(g_clistApi.hwndStatus, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
+ }
+ g_oldSize.cx = g_oldSize.cy = 0;
+ old_cliststate = g_plugin.getByte("State", SETTING_STATE_NORMAL);
+ g_plugin.setByte("State", SETTING_STATE_HIDDEN);
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VISIBLE);
+ SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | WS_CLIPCHILDREN);
+ if (!cfg::dat.bFirstRun)
+ ConfigureEventArea();
+ ConfigureCLUIGeometry(0);
+ CluiProtocolStatusChanged(0, nullptr);
+
+ for (int i = ID_STATUS_OFFLINE; i <= ID_STATUS_MAX; i++)
+ statusNames[i - ID_STATUS_OFFLINE] = Clist_GetStatusModeDescription(i, 0);
+
+ //delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded)
+ if (cfg::dat.bLayeredHack) {
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | (WS_EX_LAYERED));
+ SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
+ }
+
+ if (cfg::dat.isTransparent) {
+ SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ }
+ transparentFocus = 1;
+
+ TranslateMenu(GetMenu(hwnd));
+ PostMessage(hwnd, M_CREATECLC, 0, 0);
+ return FALSE;
+
+ case WM_NCCREATE:
+ {
+ LPCREATESTRUCT p = (LPCREATESTRUCT)lParam;
+ p->style &= ~(CS_HREDRAW | CS_VREDRAW);
+ }
+ break;
+
+ case M_CREATECLC: {
+ if (db_get_b(0, "CLUI", "useskin", 0))
+ IMG_LoadItems();
+ CreateButtonBar(hwnd);
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
+ {
+ LONG style;
+ uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_TOOLWINDOW);
+ ShowWindow(g_clistApi.hwndContactList, SW_HIDE);
+ style = GetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE);
+ if (windowStyle != SETTING_WINDOWSTYLE_DEFAULT) {
+ style |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
+ style &= ~WS_EX_APPWINDOW;
+ }
+ else {
+ style &= ~(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE);
+ if (g_plugin.getByte("AlwaysHideOnTB", 1))
+ style &= ~WS_EX_APPWINDOW;
+ else
+ style |= WS_EX_APPWINDOW;
+ }
+
+ SetWindowLongPtr(g_clistApi.hwndContactList, GWL_EXSTYLE, style);
+ ApplyCLUIBorderStyle();
+
+ SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE);
+ }
+
+ if (cfg::dat.bSkinnedButtonMode)
+ SetButtonToSkinned();
+ ConfigureFrame();
+ SetButtonStates();
+
+ CreateCLC();
+ cfg::clcdat = (struct ClcData *)GetWindowLongPtr(g_clistApi.hwndContactTree, 0);
+
+ if (cfg::dat.bFullTransparent) {
+ if (g_CLUISkinnedBkColorRGB)
+ Tweak_It(g_CLUISkinnedBkColorRGB);
+ else if (cfg::dat.bClipBorder || (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME))
+ Tweak_It(RGB(255, 0, 255));
+ else
+ Tweak_It(cfg::clcdat->bkColour);
+ }
+
+ g_plugin.setByte("State", old_cliststate);
+
+ if (g_plugin.getByte("AutoApplyLastViewMode", 0)) {
+ DBVARIANT dbv = { 0 };
+ if (!g_plugin.getString("LastViewMode", &dbv)) {
+ if (mir_strlen(dbv.pszVal) > 2) {
+ if (db_get_dw(0, CLVM_MODULE, dbv.pszVal, -1) != 0xffffffff)
+ ApplyViewMode((char *)dbv.pszVal);
+ }
+ db_free(&dbv);
+ }
+ }
+ if (!cfg::dat.autosize)
+ ShowCLUI(hwnd);
+ else {
+ show_on_first_autosize = TRUE;
+ RecalcScrollBar(g_clistApi.hwndContactTree, cfg::clcdat);
+ }
+ return 0;
+ }
+ case WM_ERASEBKGND:
+ return TRUE;
+ /*
+ if (cfg::dat.bSkinnedButtonMode)
+ return TRUE;
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ */
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ RECT rcFrame, rcClient;
+ HDC hdc;
+ HRGN rgn = nullptr;
+ HDC hdcReal = BeginPaint(hwnd, &ps);
+
+ if (during_sizing)
+ rcClient = rcWPC;
+ else
+ GetClientRect(hwnd, &rcClient);
+ CopyRect(&rc, &rcClient);
+
+ if (!cfg::dat.hdcBg || rc.right > cfg::dat.dcSize.cx || rc.bottom + cfg::dat.statusBarHeight > cfg::dat.dcSize.cy) {
+ RECT rcWorkArea;
+
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
+ HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ rcWorkArea = mi.rcWork;
+
+ cfg::dat.dcSize.cy = max(rc.bottom + cfg::dat.statusBarHeight, rcWorkArea.bottom - rcWorkArea.top);
+ cfg::dat.dcSize.cx = max(rc.right, (rcWorkArea.right - rcWorkArea.left) / 2);
+
+ if (cfg::dat.hdcBg) {
+ SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
+ DeleteObject(cfg::dat.hbmBg);
+ DeleteDC(cfg::dat.hdcBg);
+ }
+ cfg::dat.hdcBg = CreateCompatibleDC(hdcReal);
+ cfg::dat.hbmBg = CreateCompatibleBitmap(hdcReal, cfg::dat.dcSize.cx, cfg::dat.dcSize.cy);
+ cfg::dat.hbmBgOld = reinterpret_cast<HBITMAP>(SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBg));
+ }
+
+ if (cfg::shutDown) {
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+
+ hdc = cfg::dat.hdcBg;
+
+ CopyRect(&rcFrame, &rcClient);
+ if (g_CLUISkinnedBkColor) {
+ if (cfg::dat.fOnDesktop) {
+ HDC dc = GetDC(nullptr);
+ RECT rcWin;
+
+ GetWindowRect(hwnd, &rcWin);
+ BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, dc, rcWin.left, rcWin.top, SRCCOPY);
+ ReleaseDC(nullptr, dc);
+ }
+ else FillRect(hdc, &rcClient, g_CLUISkinnedBkColor);
+ }
+
+ if (cfg::dat.bClipBorder != 0 || cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) {
+ int docked = Clist_IsDocked();
+ int clip = cfg::dat.bClipBorder;
+
+ if (!g_CLUISkinnedBkColor)
+ FillRect(hdc, &rcClient, cfg::dat.hBrushColorKey);
+ if (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME)
+ rgn = CreateRoundRectRgn(clip, docked ? 0 : clip, rcClient.right - clip + 1, rcClient.bottom - (docked ? 0 : clip - 1), 8 + clip, 8 + clip);
+ else
+ rgn = CreateRectRgn(clip, docked ? 0 : clip, rcClient.right - clip, rcClient.bottom - (docked ? 0 : clip));
+ SelectClipRgn(hdc, rgn);
+ }
+
+ if (g_CLUIImageItem) {
+ IMG_RenderImageItem(hdc, g_CLUIImageItem, &rcFrame);
+ cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
+ ClientToScreen(hwnd, &cfg::dat.ptW);
+ goto skipbg;
+ }
+
+ if (cfg::dat.bWallpaperMode)
+ FillRect(hdc, &rcClient, cfg::dat.hBrushCLCBk);
+ else
+ FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE));
+
+ rcFrame.left += (cfg::dat.bCLeft - 1);
+ rcFrame.right -= (cfg::dat.bCRight - 1);
+ rcFrame.bottom++;
+ rcFrame.bottom -= cfg::dat.statusBarHeight;
+ rcFrame.top += (cfg::dat.topOffset - 1);
+
+ if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
+ if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
+ InflateRect(&rcFrame, -1, -1);
+ if (cfg::dat.bmpBackground)
+ BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
+ cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
+ ClientToScreen(hwnd, &cfg::dat.ptW);
+ }
+ InflateRect(&rcFrame, 1, 1);
+ if (cfg::dat.bSkinnedButtonMode)
+ rcFrame.bottom -= (cfg::dat.bottomOffset);
+ DrawEdge(hdc, &rcFrame, BDR_SUNKENOUTER, BF_RECT);
+ }
+ else if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
+ if (cfg::dat.bmpBackground)
+ BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
+ cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
+ ClientToScreen(hwnd, &cfg::dat.ptW);
+ }
+skipbg:
+ BitBlt(hdcReal, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdc, 0, 0, SRCCOPY);
+ if (rgn) {
+ SelectClipRgn(hdc, nullptr);
+ DeleteObject(rgn);
+ }
+ EndPaint(hwnd, &ps);
+ }
+ return 0;
+
+ case WM_ENTERSIZEMOVE:
+ {
+ POINT pt = { 0 };
+
+ GetWindowRect(hwnd, &g_PreSizeRect);
+ GetClientRect(hwnd, &rc);
+ ClientToScreen(hwnd, &pt);
+ g_CLUI_x_off = pt.x - g_PreSizeRect.left;
+ g_CLUI_y_off = pt.y - g_PreSizeRect.top;
+ pt.x = rc.right;
+ ClientToScreen(hwnd, &pt);
+ g_CLUI_x1_off = g_PreSizeRect.right - pt.x;
+ pt.x = 0;
+ pt.y = rc.bottom;
+ ClientToScreen(hwnd, &pt);
+ g_CLUI_y1_off = g_PreSizeRect.bottom - pt.y;
+ }
+ break;
+
+ case WM_EXITSIZEMOVE:
+ PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ break;
+
+ case WM_SIZING:
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ if (Docking_IsDocked(0, 0))
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ if (g_clistApi.hwndContactList != nullptr) {
+ WINDOWPOS *wp = (WINDOWPOS *)lParam;
+ if (!wp || (wp->flags & SWP_NOSIZE))
+ return FALSE;
+
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ during_sizing = true;
+
+ new_window_rect.left = 0;
+ new_window_rect.right = wp->cx - (g_CLUI_x_off + g_CLUI_x1_off);
+ new_window_rect.top = 0;
+ new_window_rect.bottom = wp->cy - g_CLUI_y_off - g_CLUI_y1_off;
+
+ if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
+ RECT rcStatus;
+ SetWindowPos(g_clistApi.hwndStatus, nullptr, 0, new_window_rect.bottom - 20, new_window_rect.right, 20, SWP_NOZORDER);
+ GetWindowRect(g_clistApi.hwndStatus, &rcStatus);
+ cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
+ if (wp->cx != g_oldSize.cx)
+ SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
+ RedrawWindow(g_clistApi.hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ }
+ else
+ cfg::dat.statusBarHeight = 0;
+
+ SizeFramesByWindowRect(&new_window_rect);
+ dock_prevent_moving = 0;
+ LayoutButtons(hwnd, &new_window_rect);
+ dock_prevent_moving = 1;
+ g_oldPos.x = wp->x;
+ g_oldPos.y = wp->y;
+ g_oldSize.cx = wp->cx;
+ g_oldSize.cy = wp->cy;
+ rcWPC = new_window_rect;
+ }
+ during_sizing = false;
+ return 0;
+
+ case WM_SIZE:
+ if ((wParam == 0 && lParam == 0) || Docking_IsDocked(0, 0)) {
+
+ if (IsZoomed(hwnd))
+ ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ if (g_clistApi.hwndContactList != nullptr) {
+ SendMessage(hwnd, WM_ENTERSIZEMOVE, 0, 0);
+ GetWindowRect(hwnd, &rc);
+ WINDOWPOS wp = {};
+ wp.cx = rc.right - rc.left;
+ wp.cy = rc.bottom - rc.top;
+ wp.x = rc.left;
+ wp.y = rc.top;
+ wp.flags = 0;
+ SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
+ SendMessage(hwnd, WM_EXITSIZEMOVE, 0, 0);
+ }
+ }
+
+ case WM_MOVE:
+ if (!IsIconic(hwnd)) {
+ GetWindowRect(hwnd, &rc);
+
+ if (!Docking_IsDocked(0, 0)) {
+ cluiPos.bottom = (uint32_t)(rc.bottom - rc.top);
+ cluiPos.left = rc.left;
+ cluiPos.top = rc.top;
+ }
+ cluiPos.right = rc.right - rc.left;
+ if (cfg::dat.realTimeSaving) {
+ GetWindowRect(hwnd, &rc);
+
+ // if docked, dont remember pos (except for width)
+ if (!Clist_IsDocked()) {
+ g_plugin.setDword("Height", (uint32_t)(rc.bottom - rc.top));
+ g_plugin.setDword("x", (uint32_t)rc.left);
+ g_plugin.setDword("y", (uint32_t)rc.top);
+ }
+ g_plugin.setDword("Width", (uint32_t)(rc.right - rc.left));
+ }
+ }
+ return TRUE;
+
+ case WM_SETFOCUS:
+ SetFocus(g_clistApi.hwndContactTree);
+ return 0;
+
+ case CLUIINTM_REMOVEFROMTASKBAR: {
+ uint8_t windowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
+ if (windowStyle == SETTING_WINDOWSTYLE_DEFAULT && g_plugin.getByte("AlwaysHideOnTB", 0))
+ RemoveFromTaskBar(hwnd);
+ return 0;
+ }
+ case WM_ACTIVATE:
+ if (g_fading_active) {
+ if (wParam != WA_INACTIVE && cfg::dat.isTransparent)
+ transparentFocus = 1;
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ if (wParam == WA_INACTIVE) {
+ if ((HWND)wParam != hwnd)
+ if (cfg::dat.isTransparent)
+ if (transparentFocus)
+ SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
+ }
+ else {
+ if (cfg::dat.isTransparent) {
+ KillTimer(hwnd, TM_AUTOALPHA);
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ transparentFocus = 1;
+ }
+ SetWindowPos(g_clistApi.hwndContactList, g_plugin.getByte("OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
+ }
+ PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_SETCURSOR:
+ if (cfg::dat.isTransparent) {
+ if (!transparentFocus && GetForegroundWindow() != hwnd) {
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ transparentFocus = 1;
+ SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_NCHITTEST: {
+ LRESULT result;
+ RECT r;
+ POINT pt;
+ int clip = cfg::dat.bClipBorder;
+
+ GetWindowRect(hwnd, &r);
+ GetCursorPos(&pt);
+ if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6 && !db_get_b(0, "CLUI", "AutoSize", 0)) {
+ if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
+ return HTBOTTOM;
+ if (pt.x < r.left + clip + 10)
+ return HTBOTTOMLEFT;
+ if (pt.x > r.right - clip - 10)
+ return HTBOTTOMRIGHT;
+ }
+ else if (pt.y >= r.top && pt.y <= r.top + 3 && !db_get_b(0, "CLUI", "AutoSize", 0)) {
+ if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
+ return HTTOP;
+ if (pt.x < r.left + clip + 10)
+ return HTTOPLEFT;
+ if (pt.x > r.right - clip - 10)
+ return HTTOPRIGHT;
+ }
+ else if (pt.x >= r.left && pt.x <= r.left + clip + 6)
+ return HTLEFT;
+ else if (pt.x >= r.right - clip - 6 && pt.x <= r.right)
+ return HTRIGHT;
+
+ result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
+ if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT || result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT)
+ if (cfg::dat.autosize)
+ return HTCLIENT;
+ return result;
+ }
+
+ case WM_TIMER:
+ if (wParam == TM_AUTOALPHA) {
+ int inwnd;
+
+ if (GetForegroundWindow() == hwnd) {
+ KillTimer(hwnd, TM_AUTOALPHA);
+ inwnd = 1;
+ }
+ else {
+ POINT pt;
+ HWND hwndPt;
+ pt.x = (short)LOWORD(GetMessagePos());
+ pt.y = (short)HIWORD(GetMessagePos());
+ hwndPt = WindowFromPoint(pt);
+ inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd);
+ }
+ if (inwnd != transparentFocus) {
+ //change
+ transparentFocus = inwnd;
+ if (transparentFocus)
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ else
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.autoalpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ }
+ if (!transparentFocus)
+ KillTimer(hwnd, TM_AUTOALPHA);
+ }
+ else if (wParam == TIMERID_AUTOSIZE) {
+ KillTimer(hwnd, wParam);
+ SetWindowPos(hwnd, nullptr, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+ PostMessage(hwnd, WM_SIZE, 0, 0);
+ PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ }
+ return TRUE;
+
+ case WM_SHOWWINDOW:
+ {
+ static int noRecurse = 0;
+ uint32_t thisTick, startTick;
+ int sourceAlpha, destAlpha;
+
+ if (cfg::dat.forceResize && wParam != SW_HIDE) {
+ cfg::dat.forceResize = FALSE;
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ }
+ PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
+
+ if (lParam)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ if (noRecurse)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ if (!cfg::dat.fadeinout)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ g_fading_active = 1;
+
+ if (wParam) {
+ sourceAlpha = 0;
+ destAlpha = cfg::dat.isTransparent ? cfg::dat.alpha : 255;
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? (COLORREF)cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)sourceAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ noRecurse = 1;
+ ShowWindow(hwnd, SW_SHOW);
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ noRecurse = 0;
+ }
+ else {
+ sourceAlpha = cfg::dat.isTransparent ? (transparentFocus ? cfg::dat.alpha : cfg::dat.autoalpha) : 255;
+ destAlpha = 0;
+ }
+ for (startTick = GetTickCount();;) {
+ thisTick = GetTickCount();
+ if (thisTick >= startTick + 200) {
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)destAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ g_fading_active = 0;
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (uint8_t)(sourceAlpha + (destAlpha - sourceAlpha) * (int)(thisTick - startTick) / 200), LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
+ }
+ }
+
+ case WM_SYSCOMMAND:
+ {
+ uint8_t bWindowStyle = db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
+ if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle) {
+ if (wParam == SC_RESTORE) {
+ CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
+ SendMessage(hwnd, WM_SIZE, 0, 0);
+ SendMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
+ SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
+ g_plugin.setByte("State", SETTING_STATE_NORMAL);
+ break;
+ }
+ }
+
+ if (wParam == SC_MAXIMIZE)
+ return 0;
+
+ if (wParam == SC_MINIMIZE) {
+ if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle && !g_plugin.getByte("AlwaysHideOnTB", 0)) {
+ g_plugin.setByte("State", SETTING_STATE_MINIMIZED);
+ break;
+ }
+ g_clistApi.pfnShowHide();
+ return 0;
+ }
+ if (wParam == SC_RESTORE) {
+ g_clistApi.pfnShowHide();
+ return 0;
+ }
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ case WM_COMMAND:
+ {
+ uint32_t dwOldFlags = cfg::dat.dwFlags;
+ if (HIWORD(wParam) == BN_CLICKED && lParam != 0) {
+ if (LOWORD(wParam) == IDC_TBFIRSTUID - 1)
+ break;
+
+ else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling
+ ButtonItem *item = g_ButtonItems;
+ WPARAM wwParam = 0;
+ LPARAM llParam = 0;
+ MCONTACT hContact = 0;
+ ClcContact *contact = nullptr;
+ int serviceFailure = FALSE;
+
+ if (cfg::clcdat) {
+ g_clistApi.pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
+ if (contact && contact->type == CLCIT_CONTACT)
+ hContact = contact->hContact;
+ }
+ while (item) {
+ if (item->uId == (uint32_t)LOWORD(wParam)) {
+ int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
+
+ if (item->dwFlags & BUTTON_ISSERVICE) {
+ if (ServiceExists(item->szService) && contactOK)
+ CallService(item->szService, wwParam, llParam);
+ else if (contactOK)
+ serviceFailure = TRUE;
+ }
+ else if (item->dwFlags & BUTTON_ISPROTOSERVICE && cfg::clcdat) {
+ if (contactOK) {
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (ProtoServiceExists(szProto, item->szService))
+ CallProtoService(szProto, item->szService, wwParam, llParam);
+ else
+ serviceFailure = TRUE;
+ }
+ }
+ else if (item->dwFlags & BUTTON_ISDBACTION) {
+ uint8_t *pValue;
+ char *szModule = item->szModule;
+ char *szSetting = item->szSetting;
+ MCONTACT finalhContact = 0;
+
+ if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) {
+ contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
+ if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = Proto_GetBaseAccountName(hContact);
+ finalhContact = hContact;
+ }
+ else
+ contactOK = 1;
+
+ if (contactOK) {
+ BOOL fDelete = FALSE;
+
+ if (item->dwFlags & BUTTON_ISTOGGLE) {
+ BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED);
+
+ pValue = fChecked ? item->bValueRelease : item->bValuePush;
+ if (fChecked && pValue[0] == 0)
+ fDelete = TRUE;
+ }
+ else
+ pValue = item->bValuePush;
+
+ if (fDelete)
+ db_unset(finalhContact, szModule, szSetting);
+ else {
+ switch (item->type) {
+ case DBVT_BYTE:
+ db_set_b(finalhContact, szModule, szSetting, pValue[0]);
+ break;
+ case DBVT_WORD:
+ db_set_w(finalhContact, szModule, szSetting, *((uint16_t *)&pValue[0]));
+ break;
+ case DBVT_DWORD:
+ db_set_dw(finalhContact, szModule, szSetting, *((uint32_t *)&pValue[0]));
+ break;
+ case DBVT_ASCIIZ:
+ db_set_s(finalhContact, szModule, szSetting, (char *)pValue);
+ break;
+ }
+ }
+ }
+ else if (item->dwFlags & BUTTON_ISTOGGLE)
+ SendMessage(item->hWnd, BM_SETCHECK, 0, 0);
+ }
+ if (!contactOK)
+ MessageBox(nullptr, TranslateT("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat."), TranslateT("Parameter mismatch"), MB_OK);
+ if (serviceFailure) {
+ wchar_t szError[512];
+ mir_snwprintf(szError, TranslateT("The service %S specified by the %S button definition was not found. You may need to install additional plugins."), item->szService, item->szName);
+ MessageBox(nullptr, szError, TranslateT("Service failure"), MB_OK);
+ }
+ break;
+ }
+ item = item->nextItem;
+ }
+ goto buttons_done;
+ }
+
+ switch (LOWORD(wParam)) {
+ case IDC_TBMENU:
+ case IDC_TBTOPMENU:
+ case IDC_STBTOPMENU:
+ GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
+ TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBMENU ? rc.top : rc.bottom, 0, hwnd, nullptr);
+ return 0;
+
+ case IDC_TBTOPSTATUS:
+ case IDC_STBTOPSTATUS:
+ case IDC_TBGLOBALSTATUS:
+ GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
+ TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBGLOBALSTATUS ? rc.top : rc.bottom, 0, hwnd, nullptr);
+ return 0;
+
+ case IDC_TBSOUND:
+ case IDC_STBSOUND:
+ cfg::dat.soundsOff = !cfg::dat.soundsOff;
+ db_set_b(0, "CLUI", "NoSounds", (uint8_t)cfg::dat.soundsOff);
+ db_set_b(0, "Skin", "UseSound", (uint8_t)(cfg::dat.soundsOff ? 0 : 1));
+ return 0;
+
+ case IDC_TBSELECTVIEWMODE:
+ case IDC_STBSELECTVIEWMODE:
+ SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_SELECTMODE, lParam);
+ break;
+ case IDC_TBCLEARVIEWMODE:
+ case IDC_STBCLEARVIEWMODE:
+ SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_RESETMODES, lParam);
+ break;
+ case IDC_TBCONFIGUREVIEWMODE:
+ case IDC_STBCONFIGUREVIEWMODE:
+ SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_CONFIGUREMODES, lParam);
+ break;
+ case IDC_TBFINDANDADD:
+ case IDC_STBFINDANDADD:
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ return 0;
+ case IDC_TBACCOUNTS:
+ case IDC_STBACCOUNTS:
+ CallService(MS_PROTO_SHOWACCMGR, 0, 0);
+ break;
+ case IDC_TBOPTIONS:
+ case IDC_STBOPTIONS:
+ CallService("Options/OptionsCommand", 0, 0);
+ return 0;
+ }
+ }
+ else if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_MAINMENU, NULL))
+ return 0;
+
+buttons_done:
+ switch (LOWORD(wParam)) {
+ case ID_TRAY_EXIT:
+ cfg::shutDown = 1;
+ if (Miranda_OkToExit())
+ DestroyWindow(hwnd);
+ break;
+ case ID_TRAY_HIDE:
+ case IDC_TBMINIMIZE:
+ case IDC_STBMINIMIZE:
+ g_clistApi.pfnShowHide();
+ break;
+ case POPUP_NEWGROUP:
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0);
+ SendMessage(g_clistApi.hwndContactTree, CLM_SETUSEGROUPS, 1, 0);
+ Clist_GroupCreate(NULL, nullptr);
+ break;
+ case POPUP_HIDEOFFLINE:
+ case IDC_TBHIDEOFFLINE:
+ case IDC_STBHIDEOFFLINE:
+ g_clistApi.pfnSetHideOffline(-1);
+ break;
+ case POPUP_HIDEOFFLINEROOT:
+ CallService(MS_CLIST_TOGGLEHIDEOFFLINEROOT, 0, 0);
+ break;
+ case POPUP_HIDEEMPTYGROUPS:
+ CallService(MS_CLIST_TOGGLEEMPTYGROUPS, 0, 0);
+ break;
+ case IDC_TBHIDEGROUPS:
+ case IDC_STBHIDEGROUPS:
+ case POPUP_DISABLEGROUPS:
+ ClcSetButtonState(IDC_TBHIDEGROUPS, CallService(MS_CLIST_TOGGLEGROUPS, 0, 0));
+ SetButtonStates();
+ break;
+ case POPUP_HIDEMIRANDA:
+ g_clistApi.pfnShowHide();
+ break;
+ case POPUP_SHOWMETAICONS:
+ cfg::dat.dwFlags ^= CLUI_USEMETAICONS;
+ Clist_InitAutoRebuild(g_clistApi.hwndContactTree);
+ break;
+ case POPUP_FRAME:
+ cfg::dat.dwFlags ^= CLUI_FRAME_CLISTSUNKEN;
+ break;
+ case POPUP_BUTTONS:
+ cfg::dat.dwFlags ^= CLUI_FRAME_SHOWBOTTOMBUTTONS;
+ break;
+ case POPUP_SHOWSTATUSICONS:
+ cfg::dat.dwFlags ^= CLUI_FRAME_STATUSICONS;
+ break;
+ }
+ if (dwOldFlags != cfg::dat.dwFlags) {
+ InvalidateRect(g_clistApi.hwndContactTree, nullptr, FALSE);
+ db_set_dw(0, "CLUI", "Frameflags", cfg::dat.dwFlags);
+ if ((dwOldFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN)) != (cfg::dat.dwFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN))) {
+ ConfigureFrame();
+ ConfigureCLUIGeometry(1);
+ }
+ ConfigureEventArea();
+ PostMessage(g_clistApi.hwndContactList, WM_SIZE, 0, 0);
+ PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
+ }
+ }
+ return FALSE;
+
+ case WM_LBUTTONDOWN:
+ if (g_ButtonItems) {
+ POINT pt;
+ GetCursorPos(&pt);
+ return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ break;
+
+ case WM_DISPLAYCHANGE:
+ SendMessage(g_clistApi.hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->hwndFrom == g_clistApi.hwndContactTree) {
+ switch (((LPNMHDR)lParam)->code) {
+ case CLN_LISTSIZECHANGE:
+ sttProcessResize(hwnd, (NMCLISTCONTROL *)lParam);
+ return FALSE;
+
+ case NM_CLICK:
+ {
+ NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam;
+ uint32_t hitFlags;
+ SendMessage(g_clistApi.hwndContactTree, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y));
+ if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0)
+ break;
+
+ if (db_get_b(0, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) {
+ POINT pt;
+ pt = nm->pt;
+ ClientToScreen(g_clistApi.hwndContactTree, &pt);
+ return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ }
+ return FALSE;
+ }
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ GetWindowRect(g_clistApi.hwndContactTree, &rc);
+ {
+ // x/y might be -1 if it was generated by a kb click
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ if (pt.x == -1 && pt.y == -1) {
+ // all this is done in screen-coords!
+ GetCursorPos(&pt);
+ // the mouse isnt near the window, so put it in the middle of the window
+ if (!PtInRect(&rc, pt)) {
+ pt.x = rc.left + (rc.right - rc.left) / 2;
+ pt.y = rc.top + (rc.bottom - rc.top) / 2;
+ }
+ }
+ if (PtInRect(&rc, pt)) {
+ HMENU hMenu = Menu_BuildGroupMenu();
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
+ Menu_DestroyNestedMenu(hMenu);
+ return 0;
+ }
+ GetWindowRect(g_clistApi.hwndStatus, &rc);
+ if (PtInRect(&rc, pt)) {
+ HMENU hMenu;
+ if (db_get_b(0, "CLUI", "SBarRightClk", 0))
+ hMenu = Menu_GetMainMenu();
+ else
+ hMenu = Menu_GetStatusMenu();
+ TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MEASUREITEM:
+ if (((LPMEASUREITEMSTRUCT)lParam)->itemData == MENU_MIRANDAMENU) {
+ ((LPMEASUREITEMSTRUCT)lParam)->itemWidth = g_cxsmIcon * 4 / 3;
+ ((LPMEASUREITEMSTRUCT)lParam)->itemHeight = 0;
+ return TRUE;
+ }
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM:
+ {
+ LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+
+ if (hbmLockedPoint == nullptr) {
+ hdcLockedPoint = CreateCompatibleDC(dis->hDC);
+ hbmLockedPoint = CreateCompatibleBitmap(dis->hDC, 5, 5);
+ hbmOldLockedPoint = reinterpret_cast<HBITMAP>(SelectObject(hdcLockedPoint, hbmLockedPoint));
+ }
+ if (dis->hwndItem == g_clistApi.hwndStatus) {
+ ProtocolData *pd = (ProtocolData *)dis->itemData;
+ if (IsBadCodePtr((FARPROC)pd))
+ return TRUE;
+ if (cfg::shutDown)
+ return TRUE;
+
+ char *szProto = pd->RealName;
+ PROTOACCOUNT *pa = Proto_GetAccount(szProto);
+ if (pa == nullptr)
+ return TRUE;
+
+ int nParts = SendMessage(g_clistApi.hwndStatus, SB_GETPARTS, 0, 0);
+ SIZE textSize;
+ uint8_t showOpts = db_get_b(0, "CLUI", "SBarShow", 1);
+
+ SetBkMode(dis->hDC, TRANSPARENT);
+ int x = dis->rcItem.left;
+
+ if (showOpts & 1) {
+ HICON hIcon;
+
+ if (pa->iRealStatus >= ID_STATUS_CONNECTING && pa->iRealStatus < ID_STATUS_OFFLINE) {
+ char szBuffer[128];
+ mir_snprintf(szBuffer, "%s_conn", pd->RealName);
+ hIcon = IcoLib_GetIcon(szBuffer);
+ }
+ else if (cfg::dat.bShowXStatusOnSbar && pa->iRealStatus > ID_STATUS_OFFLINE) {
+ int xStatus;
+ CUSTOM_STATUS cst = { sizeof(cst) };
+ cst.flags = CSSF_MASK_STATUS;
+ cst.status = &xStatus;
+ if (ProtoServiceExists(pd->RealName, PS_GETCUSTOMSTATUSEX) && !CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cst) && xStatus > 0)
+ hIcon = (HICON)CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); // get OWN xStatus icon (if set)
+ else
+ hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus);
+ }
+ else hIcon = Skin_LoadProtoIcon(szProto, pa->iRealStatus);
+
+ if (!(showOpts & 6) && cfg::dat.bEqualSections)
+ x = (dis->rcItem.left + dis->rcItem.right - 16) >> 1;
+ if (pd->protopos == 0)
+ x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
+ else if (pd->protopos == nParts - 1)
+ x -= (cfg::dat.bCRight / 2);
+ DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+ IcoLib_ReleaseIcon(hIcon);
+
+ if (db_get_b(0, "CLUI", "sbar_showlocked", 1)) {
+ if (pa->bIsLocked) {
+ hIcon = Skin_LoadIcon(SKINICON_OTHER_STATUS_LOCKED);
+ if (hIcon != nullptr) {
+ DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+ IcoLib_ReleaseIcon(hIcon);
+ }
+ }
+ }
+ x += 18;
+ }
+ else {
+ x += 2;
+ if (pd->protopos == 0)
+ x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
+ else if (pd->protopos == nParts - 1)
+ x -= (cfg::dat.bCRight / 2);
+ }
+
+ if (showOpts & 2) {
+ wchar_t szName[64];
+ wcsncpy_s(szName, pa->tszAccountName, _TRUNCATE);
+
+ if (mir_wstrlen(szName) < _countof(szName) - 1)
+ mir_wstrcat(szName, L" ");
+ GetTextExtentPoint32(dis->hDC, szName, (int)mir_wstrlen(szName), &textSize);
+ TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szName, (int)mir_wstrlen(szName));
+ x += textSize.cx;
+ }
+ if (showOpts & 4) {
+ wchar_t *szStatus = Clist_GetStatusModeDescription(pa->iRealStatus, 0);
+ GetTextExtentPoint32(dis->hDC, szStatus, (int)mir_wstrlen(szStatus), &textSize);
+ TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szStatus, (int)mir_wstrlen(szStatus));
+ }
+ }
+ else if (dis->CtlType == ODT_MENU) {
+ if (dis->itemData == MENU_MIRANDAMENU)
+ break;
+ return Menu_DrawItem(lParam);
+ }
+ }
+ return 0;
+
+ case WM_CLOSE:
+ if (SETTING_WINDOWSTYLE_DEFAULT == db_get_b(0, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT) && !g_plugin.getByte("AlwaysHideOnTB", 0)) {
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
+ return 0;
+ }
+ g_clistApi.pfnShowHide();
+ return 0;
+
+ case CLUIINTM_REDRAW:
+ if (show_on_first_autosize) {
+ show_on_first_autosize = FALSE;
+ ShowCLUI(hwnd);
+ }
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+
+ case CLUIINTM_STATUSBARUPDATE:
+ CluiProtocolStatusChanged(0, nullptr);
+ return 0;
+
+ case WM_THEMECHANGED:
+ API::updateState();
+ break;
+
+ case WM_DESTROY:
+ if (cfg::dat.hdcBg) {
+ SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
+ DeleteObject(cfg::dat.hbmBg);
+ DeleteDC(cfg::dat.hdcBg);
+ cfg::dat.hdcBg = nullptr;
+ }
+ if (cfg::dat.bmpBackground) {
+ SelectObject(cfg::dat.hdcPic, cfg::dat.hbmPicOld);
+ DeleteDC(cfg::dat.hdcPic);
+ DeleteObject(cfg::dat.bmpBackground);
+ cfg::dat.bmpBackground = nullptr;
+ }
+ FreeProtocolData();
+ if (hdcLockedPoint) {
+ SelectObject(hdcLockedPoint, hbmOldLockedPoint);
+ DeleteObject(hbmLockedPoint);
+ DeleteDC(hdcLockedPoint);
+ }
+ // if this has not yet been set, do it now.
+ // indicates that clist is shutting down and prevents various things
+ // from happening at shutdown.
+ if (!cfg::shutDown)
+ cfg::shutDown = 1;
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)hFrameContactTree, 0);
+ break;
+ }
+
+ return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
+}
+
+#ifndef CS_DROPSHADOW
+#define CS_DROPSHADOW 0x00020000
+#endif
+
+static int MetaChanged(WPARAM wParam, LPARAM lParam)
+{
+ Clist_Broadcast(INTM_METACHANGEDEVENT, wParam, lParam);
+ return 0;
+}
+
+static INT_PTR CLN_ShowMainMenu(WPARAM, LPARAM)
+{
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr);
+ return 0;
+}
+
+static INT_PTR CLN_ShowStatusMenu(WPARAM, LPARAM)
+{
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, g_clistApi.hwndContactList, nullptr);
+ return 0;
+}
+
+#define MS_CLUI_SHOWMAINMENU "CList/ShowMainMenu"
+#define MS_CLUI_SHOWSTATUSMENU "CList/ShowStatusMenu"
+
+void LoadCLUIModule(void)
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded);
+
+ WNDCLASS wndclass;
+ wndclass.style = 0;
+ wndclass.lpfnWndProc = EventAreaWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = g_plugin.getInst();
+ wndclass.hIcon = nullptr;
+ wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH)COLOR_3DFACE;
+ wndclass.lpszMenuName = nullptr;
+ wndclass.lpszClassName = L"EventAreaClass";
+ RegisterClass(&wndclass);
+
+ oldhideoffline = Clist::HideOffline;
+ cluiPos.left = g_plugin.getDword("x", 600);
+ cluiPos.top = g_plugin.getDword("y", 200);
+ cluiPos.right = g_plugin.getDword("Width", 150);
+ cluiPos.bottom = g_plugin.getDword("Height", 350);
+
+ LoadExtraIconModule();
+ LoadCLUIFramesModule();
+
+ CreateServiceFunction(MS_CLUI_SHOWMAINMENU, CLN_ShowMainMenu);
+ CreateServiceFunction(MS_CLUI_SHOWSTATUSMENU, CLN_ShowStatusMenu);
+
+ if (db_get_b(0, "CLUI", "FloaterMode", 0)) {
+ MessageBox(nullptr,
+ TranslateT("You need the FloatingContacts plugin, cause the embedded floating contacts were removed."),
+ TranslateT("Warning"), MB_OK | MB_ICONWARNING);
+ db_unset(0, "CLUI", "FloaterMode");
+ }
+
+ MF_InitCheck();
+}
+
+void OnCreateClc()
+{
+ HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged);
+ HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaChanged);
+
+ InitGroupMenus();
+ LoadExtBkSettingsFromDB();
+ PreCreateCLC(g_clistApi.hwndContactList);
+}
+
+struct
+{
+ const wchar_t *tszName;
+ int iMask;
+}
+static clistFontDescr[] =
+{
+ { LPGENW("Standard contacts"), FIDF_CLASSGENERAL },
+ { LPGENW("Online contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
+ { LPGENW("Offline contacts"), FIDF_CLASSGENERAL },
+ { LPGENW("Contacts which are 'not on list'"), FIDF_CLASSGENERAL },
+ { LPGENW("Groups"), FIDF_CLASSHEADER },
+ { LPGENW("Group member counts"), FIDF_CLASSHEADER },
+ { LPGENW("Dividers"), FIDF_CLASSSMALL },
+ { LPGENW("Offline contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
+ { LPGENW("Status mode"), FIDF_CLASSGENERAL },
+ { LPGENW("Frame titles"), FIDF_CLASSGENERAL },
+ { LPGENW("Event area"), FIDF_CLASSGENERAL },
+ { LPGENW("Contact list local time"), FIDF_CLASSGENERAL }
+};
+
+void FS_RegisterFonts()
+{
+ FontIDW fid = {};
+ wcsncpy_s(fid.group, LPGENW("Contact list"), _TRUNCATE);
+ strncpy_s(fid.dbSettingsGroup, "CLC", _TRUNCATE);
+ fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS | FIDF_APPENDNAME | FIDF_SAVEPOINTSIZE;
+
+ HDC hdc = GetDC(nullptr);
+ for (int i = 0; i < _countof(clistFontDescr); i++) {
+ LOGFONT lf;
+ Clist_GetFontSetting(i, &lf, &fid.deffontsettings.colour);
+ lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+
+ wcsncpy_s(fid.deffontsettings.szFace, lf.lfFaceName, _TRUNCATE);
+ fid.deffontsettings.charset = lf.lfCharSet;
+ fid.deffontsettings.size = (char)lf.lfHeight;
+ fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0);
+
+ fid.flags &= ~FIDF_CLASSMASK;
+ fid.flags |= clistFontDescr[i].iMask;
+
+ wcsncpy_s(fid.name, clistFontDescr[i].tszName, _TRUNCATE);
+
+ char idstr[10];
+ mir_snprintf(idstr, "Font%d", i);
+ strncpy_s(fid.setting, idstr, _TRUNCATE);
+ fid.order = i;
+ g_plugin.addFont(&fid);
+ }
+ ReleaseDC(nullptr, hdc);
+
+ // and colours
+ ColourIDW colourid = {};
+ colourid.order = 0;
+ strncpy_s(colourid.dbSettingsGroup, "CLC", _TRUNCATE);
+
+ strncpy_s(colourid.setting, "BkColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Background"), _TRUNCATE);
+ wcsncpy_s(colourid.group, LPGENW("Contact list"), _TRUNCATE);
+ colourid.defcolour = CLCDEFAULT_BKCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.setting, "SelTextColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Selected text"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = CLCDEFAULT_SELTEXTCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.setting, "HotTextColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Hottrack text"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = CLCDEFAULT_HOTTEXTCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.setting, "QuickSearchColour", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Quicksearch text"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = CLCDEFAULT_QUICKSEARCHCOLOUR;
+ g_plugin.addColor(&colourid);
+
+ strncpy_s(colourid.dbSettingsGroup, "CLUI", _TRUNCATE);
+ strncpy_s(colourid.setting, "clr_frameborder", _TRUNCATE);
+ wcsncpy_s(colourid.name, LPGENW("Embedded frames border"), _TRUNCATE);
+ colourid.order = 1;
+ colourid.defcolour = RGB(40, 40, 40);
+ g_plugin.addColor(&colourid);
+}
diff --git a/plugins/Clist_nicer/src/cluiframes.cpp b/plugins/Clist_nicer/src/cluiframes.cpp
index 26dc766248..883fdbea8c 100644
--- a/plugins/Clist_nicer/src/cluiframes.cpp
+++ b/plugins/Clist_nicer/src/cluiframes.cpp
@@ -1,3050 +1,3050 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-03 Miranda ICQ/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 "cluiframes.h"
-HFONT __fastcall ChangeToFont(HDC hdc, struct ClcData *dat, int id, int *fontHeight);
-
-extern HWND g_hwndViewModeFrame, g_hwndEventArea;
-extern int mf_updatethread_running;
-
-extern HANDLE hThreadMFUpdate;
-
-void MF_UpdateThread(LPVOID);
-
-HANDLE hStatusBarShowToolTipEvent, hStatusBarHideToolTipEvent;
-HANDLE g_hEventThread = nullptr;
-
-LOGFONT TitleBarLogFont = { 0 };
-
-// we use dynamic frame list,
-// but who wants so huge number of frames ??
-#define MAX_FRAMES 40
-
-#define UNCOLLAPSED_FRAME_SIZE 0
-
-// legacy menu support
-#define frame_menu_lock 1
-#define frame_menu_visible 2
-#define frame_menu_showtitlebar 3
-#define frame_menu_floating 4
-#define frame_menu_skinned 5
-
-static int UpdateTBToolTip(int framepos);
-INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam);
-int CLUIFrameResizeFloatingFrame(int framepos);
-static int CLUIFramesReSort();
-
-boolean FramesSysNotStarted = TRUE;
-HPEN g_hPenCLUIFrames = nullptr;
-
-static SortData g_sd[MAX_FRAMES];
-
-static HHOOK g_hFrameHook = nullptr;
-
-static int sortfunc(const void *a, const void *b)
-{
- SortData *sd1, *sd2;
- sd1 = (SortData *)a;
- sd2 = (SortData *)b;
- if (sd1->order > sd2->order)
- return 1;
- if (sd1->order < sd2->order)
- return -1;
- return 0;
-}
-
-static FRAMEWND *Frames = nullptr;
-
-FRAMEWND *wndFrameCLC = nullptr, *wndFrameEventArea = nullptr, *wndFrameViewMode = nullptr;
-
-static int nFramescount = 0;
-static int alclientFrame = -1;//for fast access to frame with alclient properties
-static int NextFrameId = 100;
-
-static int TitleBarH = DEFAULT_TITLEBAR_HEIGHT;
-static boolean resizing = FALSE;
-
-// menus
-static FrameMenuHandles cont;
-static LIST<TMO_IntMenuItem> g_frameMenus(10);
-
-// others
-static int ContactListHeight;
-static int LastStoreTick = 0;
-
-static int lbypos = -1;
-static int oldframeheight = -1;
-static int curdragbar = -1;
-static mir_cs csFrameHook;
-
-static bool CLUIFramesFitInSize(void);
-HWND hWndExplorerToolBar;
-static int GapBetweenFrames = 1;
-
-static int RemoveItemFromList(int pos, FRAMEWND **lpFrames, int *FrameItemCount)
-{
- memcpy(&((*lpFrames)[pos]), &((*lpFrames)[pos + 1]), sizeof(FRAMEWND) * (*FrameItemCount - pos - 1));
- (*FrameItemCount)--;
- return 0;
-}
-
-static int id2pos(int id)
-{
- int i;
-
- if (FramesSysNotStarted)
- return -1;
-
- for (i = 0; i < nFramescount; i++) {
- if (Frames[i].id == id)
- return i;
- }
- return -1;
-}
-
-int __forceinline btoint(bool b)
-{
- return (b ? 1 : 0);
-}
-
-static FRAMEWND* FindFrameByWnd(HWND hwnd)
-{
- if (hwnd == nullptr)
- return nullptr;
-
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (F.floating && F.ContainerWnd == hwnd)
- return &F;
- }
-
- return nullptr;
-}
-
-static void DockThumbs(FRAMEWND *pThumbLeft, FRAMEWND *pThumbRight, BOOL)
-{
- if ((pThumbRight->dockOpt.hwndLeft == nullptr) && (pThumbLeft->dockOpt.hwndRight == nullptr)) {
- pThumbRight->dockOpt.hwndLeft = pThumbLeft->ContainerWnd;
- pThumbLeft->dockOpt.hwndRight = pThumbRight->ContainerWnd;
- }
-}
-
-static void UndockThumbs(FRAMEWND *pThumb1, FRAMEWND *pThumb2)
-{
- if ((pThumb1 == nullptr) || (pThumb2 == nullptr))
- return;
-
- if (pThumb1->dockOpt.hwndRight == pThumb2->ContainerWnd)
- pThumb1->dockOpt.hwndRight = nullptr;
-
- if (pThumb1->dockOpt.hwndLeft == pThumb2->ContainerWnd)
- pThumb1->dockOpt.hwndLeft = nullptr;
-
- if (pThumb2->dockOpt.hwndRight == pThumb1->ContainerWnd)
- pThumb2->dockOpt.hwndRight = nullptr;
-
- if (pThumb2->dockOpt.hwndLeft == pThumb1->ContainerWnd)
- pThumb2->dockOpt.hwndLeft = nullptr;
-}
-
-BOOLEAN bMoveTogether;
-
-static void PositionThumb(FRAMEWND *pThumb, short nX, short nY)
-{
- FRAMEWND *pCurThumb = &Frames[0];
- FRAMEWND *pDockThumb = pThumb;
- FRAMEWND fakeMainWindow;
- FRAMEWND fakeTaskBarWindow;
- RECT rc;
- RECT rcThumb;
- RECT rcOld;
- SIZE sizeScreen;
- int nOffs = 10;
- POINT pt;
- RECT rcLeft;
- RECT rcTop;
- RECT rcRight;
- RECT rcBottom;
- int frmidx = 0;
-
- if (pThumb == nullptr)
- return;
-
- sizeScreen.cx = GetSystemMetrics(SM_CXSCREEN);
- sizeScreen.cy = GetSystemMetrics(SM_CYSCREEN);
-
- // Get thumb dimnsions
- GetWindowRect(pThumb->ContainerWnd, &rcThumb);
- int nWidth = rcThumb.right - rcThumb.left;
- int nHeight = rcThumb.bottom - rcThumb.top;
-
- // Docking to the edges of the screen
- int nNewX = nX < nOffs ? 0 : nX;
- nNewX = nNewX >(sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX;
- int nNewY = nY < nOffs ? 0 : nY;
- nNewY = nNewY >(sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY;
-
- bool bLeading = pThumb->dockOpt.hwndRight != nullptr;
-
- if (bMoveTogether) {
- UndockThumbs(pThumb, FindFrameByWnd(pThumb->dockOpt.hwndLeft));
- GetWindowRect(pThumb->ContainerWnd, &rcOld);
- }
-
- memset(&fakeMainWindow, 0, sizeof(fakeMainWindow));
- fakeMainWindow.ContainerWnd = g_clistApi.hwndContactList;
- fakeMainWindow.floating = TRUE;
-
- memset(&fakeTaskBarWindow, 0, sizeof(fakeTaskBarWindow));
- fakeTaskBarWindow.ContainerWnd = hWndExplorerToolBar;
- fakeTaskBarWindow.floating = TRUE;
-
- while (pCurThumb != nullptr) {
- if (pCurThumb->floating) {
-
- if (pCurThumb != pThumb) {
- GetWindowRect(pThumb->ContainerWnd, &rcThumb);
- OffsetRect(&rcThumb, nX - rcThumb.left, nY - rcThumb.top);
-
- GetWindowRect(pCurThumb->ContainerWnd, &rc);
-
- rcLeft.left = rc.left - nOffs;
- rcLeft.top = rc.top - nOffs;
- rcLeft.right = rc.left + nOffs;
- rcLeft.bottom = rc.bottom + nOffs;
-
- rcTop.left = rc.left - nOffs;
- rcTop.top = rc.top - nOffs;
- rcTop.right = rc.right + nOffs;
- rcTop.bottom = rc.top + nOffs;
-
- rcRight.left = rc.right - nOffs;
- rcRight.top = rc.top - nOffs;
- rcRight.right = rc.right + nOffs;
- rcRight.bottom = rc.bottom + nOffs;
-
- rcBottom.left = rc.left - nOffs;
- rcBottom.top = rc.bottom - nOffs;
- rcBottom.right = rc.right + nOffs;
- rcBottom.bottom = rc.bottom + nOffs;
-
- bool bDockedLeft = false, bDockedRight = false, bDocked = false;
-
- // Upper-left
- pt.x = rcThumb.left;
- pt.y = rcThumb.top;
-
- if (PtInRect(&rcRight, pt)) {
- nNewX = rc.right;
- bDocked = true;
- }
-
- if (PtInRect(&rcBottom, pt)) {
- nNewY = rc.bottom;
- if (PtInRect(&rcLeft, pt))
- nNewX = rc.left;
- }
-
- if (PtInRect(&rcTop, pt)) {
- nNewY = rc.top;
- bDockedLeft = bDocked;
- }
-
- // Upper-right
- pt.x = rcThumb.right;
- pt.y = rcThumb.top;
- bDocked = false;
-
- if (!bLeading && PtInRect(&rcLeft, pt)) {
- if (!bDockedLeft) {
- nNewX = rc.left - nWidth;
- bDocked = true;
- }
- else if (rc.right == rcThumb.left)
- bDocked = true;
- }
-
-
- if (PtInRect(&rcBottom, pt)) {
- nNewY = rc.bottom;
- if (PtInRect(&rcRight, pt))
- nNewX = rc.right - nWidth;
- }
-
- if (!bLeading && PtInRect(&rcTop, pt)) {
- nNewY = rc.top;
- bDockedRight = bDocked;
- }
-
- if (bMoveTogether) {
- if (bDockedRight)
- DockThumbs(pThumb, pCurThumb, TRUE);
-
- if (bDockedLeft)
- DockThumbs(pCurThumb, pThumb, FALSE);
- }
-
- // Lower-left
- pt.x = rcThumb.left;
- pt.y = rcThumb.bottom;
-
- if (PtInRect(&rcRight, pt))
- nNewX = rc.right;
-
- if (PtInRect(&rcTop, pt)) {
- nNewY = rc.top - nHeight;
-
- if (PtInRect(&rcLeft, pt))
- nNewX = rc.left;
- }
-
-
- // Lower-right
- pt.x = rcThumb.right;
- pt.y = rcThumb.bottom;
-
- if (!bLeading && PtInRect(&rcLeft, pt))
- nNewX = rc.left - nWidth;
-
- if (!bLeading && PtInRect(&rcTop, pt)) {
- nNewY = rc.top - nHeight;
-
- if (PtInRect(&rcRight, pt))
- nNewX = rc.right - nWidth;
- }
- }
- }
-
- frmidx++;
- if (pCurThumb->ContainerWnd == fakeTaskBarWindow.ContainerWnd)
- break;
-
- if (pCurThumb->ContainerWnd == fakeMainWindow.ContainerWnd) {
- pCurThumb = &fakeTaskBarWindow;
- continue;
- }
- if (frmidx == nFramescount) {
- pCurThumb = &fakeMainWindow;
- continue;
- }
- pCurThumb = &Frames[frmidx];
- }
-
- // Adjust coords once again
- nNewX = nNewX < nOffs ? 0 : nNewX;
- nNewX = nNewX > (sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX;
- nNewY = nNewY < nOffs ? 0 : nNewY;
- nNewY = nNewY > (sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY;
- SetWindowPos(pThumb->ContainerWnd, nullptr, nNewX, nNewY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
-
- // OK, move all docked thumbs
- if (bMoveTogether) {
- pDockThumb = FindFrameByWnd(pDockThumb->dockOpt.hwndRight);
- PositionThumb(pDockThumb, (short)(nNewX + nWidth), (short)nNewY);
- }
-}
-
-void GetBorderSize(HWND hwnd, RECT *rect)
-{
- RECT wr, cr;
- POINT pt1, pt2;
-
- GetWindowRect(hwnd, &wr);
- GetClientRect(hwnd, &cr);
- pt1.y = cr.top;
- pt1.x = cr.left;
- pt2.y = cr.bottom;
- pt2.x = cr.right;
-
- ClientToScreen(hwnd, &pt1);
- ClientToScreen(hwnd, &pt2);
-
- cr.top = pt1.y;
- cr.left = pt1.x;
- cr.bottom = pt2.y;
- cr.right = pt2.x;
-
- rect->top = cr.top - wr.top;
- rect->left = cr.left - wr.left;
- rect->right = wr.right - cr.right;
- rect->bottom = wr.bottom - cr.bottom;
-}
-
-int DBLoadFrameSettingsAtPos(int pos, int Frameid)
-{
- CMStringA buf;
-
- Frames[Frameid].collapsed = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), Frames[Frameid].collapsed);
-
- Frames[Frameid].Locked = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), Frames[Frameid].Locked);
- Frames[Frameid].visible = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), Frames[Frameid].visible);
- Frames[Frameid].TitleBar.ShowTitleBar = 0 != db_get_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), Frames[Frameid].TitleBar.ShowTitleBar);
-
- Frames[Frameid].height = db_get_w(0, CLUIFrameModule, buf.Format("Height%d", pos), Frames[Frameid].height);
- Frames[Frameid].HeightWhenCollapsed = db_get_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), 0);
- Frames[Frameid].align = db_get_w(0, CLUIFrameModule, buf.Format("Align%d", pos), Frames[Frameid].align);
-
- Frames[Frameid].FloatingPos.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatX%d", pos), 100, 0, 1024);
- Frames[Frameid].FloatingPos.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatY%d", pos), 100, 0, 1024);
- Frames[Frameid].FloatingSize.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatW%d", pos), 100, 0, 1024);
- Frames[Frameid].FloatingSize.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatH%d", pos), 100, 0, 1024);
-
- Frames[Frameid].floating = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), 0);
- Frames[Frameid].order = db_get_w(0, CLUIFrameModule, buf.Format("Order%d", pos), 0);
-
- Frames[Frameid].UseBorder = 0 != db_get_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), Frames[Frameid].UseBorder);
- Frames[Frameid].Skinned = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned);
- return 0;
-}
-
-int DBStoreFrameSettingsAtPos(int pos, int Frameid)
-{
- CMStringA buf;
-
- db_set_ws(0, CLUIFrameModule, buf.Format("Name%d", pos), Frames[Frameid].name);
- //boolean
- db_set_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), (uint8_t)btoint(Frames[Frameid].collapsed));
- db_set_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), (uint8_t)btoint(Frames[Frameid].Locked));
- db_set_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), (uint8_t)btoint(Frames[Frameid].visible));
- db_set_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), (uint8_t)btoint(Frames[Frameid].TitleBar.ShowTitleBar));
-
- db_set_w(0, CLUIFrameModule, buf.Format("Height%d", pos), (uint16_t)Frames[Frameid].height);
- db_set_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), (uint16_t)Frames[Frameid].HeightWhenCollapsed);
- db_set_w(0, CLUIFrameModule, buf.Format("Align%d", pos), (uint16_t)Frames[Frameid].align);
- //FloatingPos
- db_set_w(0, CLUIFrameModule, buf.Format("FloatX%d", pos), (uint16_t)Frames[Frameid].FloatingPos.x);
- db_set_w(0, CLUIFrameModule, buf.Format("FloatY%d", pos), (uint16_t)Frames[Frameid].FloatingPos.y);
- db_set_w(0, CLUIFrameModule, buf.Format("FloatW%d", pos), (uint16_t)Frames[Frameid].FloatingSize.x);
- db_set_w(0, CLUIFrameModule, buf.Format("FloatH%d", pos), (uint16_t)Frames[Frameid].FloatingSize.y);
-
- db_set_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), (uint8_t)btoint(Frames[Frameid].floating));
- db_set_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), (uint8_t)btoint(Frames[Frameid].UseBorder));
- db_set_w(0, CLUIFrameModule, buf.Format("Order%d", pos), (uint16_t)Frames[Frameid].order);
-
- db_set_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned);
- return 0;
-}
-
-int LocateStorePosition(int Frameid, int maxstored)
-{
- if (Frames[Frameid].name == nullptr) return -1;
-
- for (int i = 0; i < maxstored; i++) {
- char settingname[255];
- mir_snprintf(settingname, "Name%d", i);
- ptrW frmname(db_get_wsa(0, CLUIFrameModule, settingname));
- if (frmname == NULL) continue;
- if (mir_wstrcmpi(frmname, Frames[Frameid].name) == 0)
- return i;
- }
- return -1;
-}
-
-int CLUIFramesLoadFrameSettings(int Frameid)
-{
- if (FramesSysNotStarted) return -1;
-
- if (Frameid < 0 || Frameid >= nFramescount)
- return -1;
-
- int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1);
- if (maxstored == -1)
- return 0;
-
- int storpos = LocateStorePosition(Frameid, maxstored);
- if (storpos == -1)
- return 0;
-
- DBLoadFrameSettingsAtPos(storpos, Frameid);
- return 0;
-}
-
-int CLUIFramesStoreFrameSettings(int Frameid)
-{
- if (FramesSysNotStarted)
- return -1;
-
- if (Frameid < 0 || Frameid >= nFramescount)
- return -1;
-
- int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1);
- if (maxstored == -1)
- maxstored = 0;
-
- int storpos = LocateStorePosition(Frameid, maxstored);
- if (storpos == -1) {
- storpos = maxstored;
- maxstored++;
- }
-
- DBStoreFrameSettingsAtPos(storpos, Frameid);
- db_set_w(0, CLUIFrameModule, "StoredFrames", (uint16_t)maxstored);
- return 0;
-}
-
-int CLUIFramesStoreAllFrames()
-{
- if (FramesSysNotStarted)
- return -1;
-
- if (cfg::shutDown)
- return -1;
-
- mir_cslock lck(csFrameHook);
- for (int i = 0; i < nFramescount; i++)
- CLUIFramesStoreFrameSettings(i);
- return 0;
-}
-
-// Get client frame
-int CLUIFramesGetalClientFrame(void)
-{
- if (FramesSysNotStarted)
- return -1;
-
- if (alclientFrame != -1) {
- /* this value could become invalid if RemoveItemFromList was called,
- * so we double-check */
- if (alclientFrame < nFramescount)
- if (Frames[alclientFrame].align == alClient)
- return alclientFrame;
- }
-
- for (int i = 0; i < nFramescount; i++)
- if (Frames[i].align == alClient) {
- alclientFrame = i;
- return i;
- }
- return -1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static HGENMENU addFrameMenuItem(TMO_MenuItem *pmi, int frameid, bool bMain)
-{
- HGENMENU res = (bMain) ? Menu_AddMainMenuItem(pmi) : Menu_AddContextFrameMenuItem(pmi);
- if (pmi->pszService != nullptr)
- Menu_ConfigureItem(res, MCI_OPT_EXECPARAM, frameid);
- return res;
-}
-
-HMENU CLUIFramesCreateMenuForFrame(int frameid, HGENMENU root, int popuppos, bool bMain)
-{
- if (FramesSysNotStarted)
- return nullptr;
-
- int framepos = id2pos(frameid);
- FrameMenuHandles &fmh = (frameid == -1) ? cont : Frames[framepos].MenuHandles;
-
- CMenuItem mi((frameid == -1) ? &g_plugin : Frames[framepos].pPlugin);
- mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
- mi.root = root;
- mi.position = popuppos++;
- mi.name.a = LPGEN("&FrameTitle");
- mi.flags = CMIF_SYSTEM | CMIF_GRAYED;
- fmh.MITitle = addFrameMenuItem(&mi, frameid, bMain);
-
- popuppos += 100000;
-
- mi.hIcolibItem = nullptr;
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Visible");
- mi.flags = CMIF_SYSTEM | CMIF_CHECKED;
- mi.pszService = MS_CLIST_FRAMES_SHFRAME;
- fmh.MIVisible = addFrameMenuItem(&mi, frameid, bMain);
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Show title bar");
- mi.pszService = MS_CLIST_FRAMES_SHFRAMETITLEBAR;
- fmh.MITBVisible = addFrameMenuItem(&mi, frameid, bMain);
-
- popuppos += 100000;
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Locked");
- mi.pszService = MS_CLIST_FRAMES_ULFRAME;
- fmh.MILock = addFrameMenuItem(&mi, frameid, bMain);
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Collapsed");
- mi.pszService = MS_CLIST_FRAMES_UCOLLFRAME;
- fmh.MIColl = addFrameMenuItem(&mi, frameid, bMain);
-
- // floating
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Floating mode");
- mi.flags = CMIF_SYSTEM;
- mi.pszService = "Set_Floating";
- fmh.MIFloating = addFrameMenuItem(&mi, frameid, bMain);
-
- popuppos += 100000;
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Border");
- mi.flags = CMIF_SYSTEM | CMIF_CHECKED;
- mi.pszService = MS_CLIST_FRAMES_SETUNBORDER;
- fmh.MIBorder = addFrameMenuItem(&mi, frameid, bMain);
-
- popuppos += 100000;
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Skinned frame");
- mi.pszService = MS_CLIST_FRAMES_SETSKINNED;
- fmh.MISkinned = addFrameMenuItem(&mi, frameid, bMain);
-
- popuppos += 100000;
-
- // alignment root
- mi.root = root;
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Align");
- mi.flags = CMIF_SYSTEM;
- mi.pszService = nullptr;
- fmh.MIAlignRoot = addFrameMenuItem(&mi, frameid, bMain);
-
- // align top
- mi.root = fmh.MIAlignRoot;
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Top");
- mi.pszService = CLUIFRAMESSETALIGNALTOP;
- fmh.MIAlignTop = addFrameMenuItem(&mi, frameid, bMain);
-
- // align client
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Client");
- mi.pszService = CLUIFRAMESSETALIGNALCLIENT;
- fmh.MIAlignClient = addFrameMenuItem(&mi, frameid, bMain);
-
- // align bottom
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Bottom");
- mi.pszService = CLUIFRAMESSETALIGNALBOTTOM;
- fmh.MIAlignBottom = addFrameMenuItem(&mi, frameid, bMain);
-
- // position root
- mi.root = root;
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Position");
- mi.pszService = nullptr;
- mi.root = addFrameMenuItem(&mi, frameid, bMain);
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Up");
- mi.pszService = CLUIFRAMESMOVEUP;
- addFrameMenuItem(&mi, frameid, bMain);
-
- mi.position = popuppos++;
- mi.name.a = LPGEN("&Down");
- mi.pszService = CLUIFRAMESMOVEDOWN;
- addFrameMenuItem(&mi, frameid, bMain);
- return nullptr;
-}
-
-static int CLUIFramesModifyContextMenuForFrame(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- mir_cslock lck(csFrameHook);
- int pos = id2pos(wParam);
- if (pos >= 0 && pos < nFramescount) {
- FRAMEWND &p = Frames[pos];
- Menu_ModifyItem(cont.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name);
- Menu_SetChecked(cont.MIVisible, p.visible);
- Menu_SetChecked(cont.MILock, p.Locked);
- Menu_SetChecked(cont.MITBVisible, p.TitleBar.ShowTitleBar);
- Menu_SetChecked(cont.MIFloating, p.floating);
- Menu_SetChecked(cont.MIBorder, p.UseBorder);
- Menu_SetChecked(cont.MISkinned, p.Skinned);
- Menu_SetChecked(cont.MIAlignTop, (p.align & alTop) != 0);
- Menu_SetChecked(cont.MIAlignClient, (p.align & alClient) != 0);
- Menu_SetChecked(cont.MIAlignBottom, (p.align & alBottom) != 0);
-
- Menu_SetChecked(cont.MIColl, !p.collapsed);
- Menu_EnableItem(cont.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame());
- }
- return 0;
-}
-
-INT_PTR CLUIFramesModifyMainMenuItems(WPARAM frameId, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- mir_cslock lck(csFrameHook);
- int pos = id2pos(frameId);
-
- if (pos >= 0 && pos < nFramescount) {
- FRAMEWND &p = Frames[pos];
- Menu_ModifyItem(p.MenuHandles.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name);
-
- Menu_SetChecked(p.MenuHandles.MIVisible, p.visible);
- Menu_SetChecked(p.MenuHandles.MILock, p.Locked);
- Menu_SetChecked(p.MenuHandles.MITBVisible, p.TitleBar.ShowTitleBar);
- Menu_SetChecked(p.MenuHandles.MIFloating, p.floating);
- Menu_SetChecked(p.MenuHandles.MIBorder, p.UseBorder);
- Menu_SetChecked(p.MenuHandles.MISkinned, p.Skinned);
-
- Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0);
- Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alTop) != 0);
-
- Menu_SetChecked(p.MenuHandles.MIAlignClient, (p.align & alClient) != 0);
-
- Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0);
- Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alBottom) != 0);
-
- Menu_SetChecked(p.MenuHandles.MIColl, !p.collapsed);
- Menu_EnableItem(p.MenuHandles.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame());
- }
- return 0;
-}
-
-INT_PTR CLUIFramesGetFrameOptions(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted) return -1;
-
- mir_cslock lck(csFrameHook);
- int pos = id2pos(HIWORD(wParam));
- if (pos < 0 || pos >= nFramescount)
- return -1;
-
- switch (LOWORD(wParam)) {
- case FO_NAME:
- return (INT_PTR)Frames[pos].name;
-
- case FO_TBNAME:
- return (INT_PTR)Frames[pos].TitleBar.tbname;
-
- case FO_TBTIPNAME:
- return (INT_PTR)Frames[pos].TitleBar.tooltip;
-
- case FO_TBSTYLE:
- return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE);
-
- case FO_TBEXSTYLE:
- return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE);
-
- case FO_ICON:
- return (INT_PTR)Frames[pos].TitleBar.hicon;
-
- case FO_HEIGHT:
- return (INT_PTR)Frames[pos].height;
-
- case FO_ALIGN:
- return (INT_PTR)Frames[pos].align;
-
- case FO_FLOATING:
- return (INT_PTR)Frames[pos].floating;
-
- case FO_FLAGS:
- INT_PTR dwFlags = 0;
- if (Frames[pos].visible) dwFlags |= F_VISIBLE;
- if (!Frames[pos].collapsed) dwFlags |= F_UNCOLLAPSED;
- if (Frames[pos].Locked) dwFlags |= F_LOCKED;
- if (Frames[pos].TitleBar.ShowTitleBar) dwFlags |= F_SHOWTB;
- if (Frames[pos].TitleBar.ShowTitleBarTip) dwFlags |= F_SHOWTBTIP;
- if (Frames[pos].Skinned) dwFlags |= F_SKINNED;
- if (!(GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE)&WS_BORDER)) dwFlags |= F_NOBORDER;
- return dwFlags;
- }
-
- return -1;
-}
-
-INT_PTR CLUIFramesSetFrameOptions(WPARAM wParam, LPARAM lParam)
-{
- int retval; // value to be returned
-
- if (FramesSysNotStarted)
- return -1;
-
- mir_cslockfull lck(csFrameHook);
- int pos = id2pos(HIWORD(wParam));
- if (pos < 0 || pos >= nFramescount)
- return -1;
-
- switch (LOWORD(wParam) & ~FO_UNICODETEXT) {
- case FO_FLAGS:
- {
- int flag = lParam;
- LONG_PTR style;
-
- Frames[pos].dwFlags = flag;
- Frames[pos].visible = FALSE;
- if (flag & F_VISIBLE) Frames[pos].visible = TRUE;
-
- Frames[pos].collapsed = TRUE;
- if (flag & F_UNCOLLAPSED) Frames[pos].collapsed = FALSE;
-
- Frames[pos].Locked = FALSE;
- if (flag & F_LOCKED) Frames[pos].Locked = TRUE;
-
- Frames[pos].UseBorder = TRUE;
- if (flag & F_NOBORDER) Frames[pos].UseBorder = FALSE;
-
- Frames[pos].TitleBar.ShowTitleBar = FALSE;
- if (flag & F_SHOWTB) Frames[pos].TitleBar.ShowTitleBar = TRUE;
-
- Frames[pos].TitleBar.ShowTitleBarTip = FALSE;
- if (flag & F_SHOWTBTIP) Frames[pos].TitleBar.ShowTitleBarTip = TRUE;
-
- SendMessage(Frames[pos].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[pos].TitleBar.ShowTitleBarTip, 0);
-
- style = GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE);
- style |= WS_BORDER;
- style |= CLS_SKINNEDFRAME;
-
- if (flag & F_NOBORDER)
- style &= (~WS_BORDER);
-
- Frames[pos].Skinned = FALSE;
- if (flag & F_SKINNED)
- Frames[pos].Skinned = TRUE;
-
- if (!(flag & F_SKINNED))
- style &= ~CLS_SKINNEDFRAME;
-
- SetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE, (LONG_PTR)style);
- SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, (LONG_PTR)style & ~(WS_VSCROLL | WS_HSCROLL));
- lck.unlock();
-
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- SetWindowPos(Frames[pos].TitleBar.hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
- }
- return 0;
-
- case FO_NAME:
- if (lParam == 0)
- return -1;
-
- mir_free(Frames[pos].name);
- Frames[pos].name = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
- return 0;
-
- case FO_TBNAME:
- if (lParam == 0)
- return -1;
-
- mir_free(Frames[pos].TitleBar.tbname);
- Frames[pos].TitleBar.tbname = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
- lck.unlock();
-
- if (Frames[pos].floating && (Frames[pos].TitleBar.tbname != nullptr))
- SetWindowText(Frames[pos].ContainerWnd, Frames[pos].TitleBar.tbname);
- return 0;
-
- case FO_TBTIPNAME:
- if (lParam == 0)
- return -1;
-
- mir_free(Frames[pos].TitleBar.tooltip);
- Frames[pos].TitleBar.tooltip = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
- UpdateTBToolTip(pos);
- return 0;
-
- case FO_TBSTYLE:
- SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, lParam);
- return 0;
-
- case FO_TBEXSTYLE:
- SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE, lParam);
- return 0;
-
- case FO_ICON:
- Frames[pos].TitleBar.hicon = (HICON)lParam;
- return 0;
-
- case FO_HEIGHT:
- if (lParam < 0)
- return -1;
-
- if (Frames[pos].Skinned) {
- int uID = (Frames[pos].TitleBar.ShowTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE : ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE);
- lParam += (arStatusItems[uID]->MARGIN_BOTTOM + arStatusItems[uID]->MARGIN_TOP);
- }
- if (Frames[pos].collapsed) {
- int oldHeight = Frames[pos].height;
- retval = Frames[pos].height;
- Frames[pos].height = lParam;
- if (!CLUIFramesFitInSize())
- Frames[pos].height = retval;
- retval = Frames[pos].height;
-
- if (Frames[pos].height != oldHeight) {
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- if (Frames[pos].Skinned)
- RedrawWindow(Frames[pos].hWnd, nullptr, nullptr, RDW_FRAME | RDW_UPDATENOW | RDW_INVALIDATE);
- }
- }
- else {
- retval = Frames[pos].HeightWhenCollapsed;
- Frames[pos].HeightWhenCollapsed = lParam;
- if (!CLUIFramesFitInSize())
- Frames[pos].HeightWhenCollapsed = retval;
- retval = Frames[pos].HeightWhenCollapsed;
- }
- return retval;
-
- case FO_FLOATING:
- if (lParam < 0)
- return -1;
- else {
- int id = Frames[pos].id;
- Frames[pos].floating = !(lParam);
- lck.unlock();
-
- CLUIFrameSetFloat(id, 1);//lparam=1 use stored width and height
- }
- return wParam;
-
- case FO_ALIGN:
- if (!(lParam&alTop || lParam&alBottom || lParam&alClient))
- return -1;
-
- if ((lParam&alClient) && (CLUIFramesGetalClientFrame() >= 0)) { //only one alClient frame possible
- alclientFrame = -1;//recalc it
- return -1;
- }
- Frames[pos].align = lParam;
- return 0;
- }
- lck.unlock();
-
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- return -1;
-}
-
-static INT_PTR CLUIFramesShowAll(WPARAM, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- for (int i = 0; i < nFramescount; i++)
- Frames[i].visible = TRUE;
-
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- return 0;
-}
-
-INT_PTR CLUIFramesShowAllTitleBars(WPARAM, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- F.TitleBar.ShowTitleBar = TRUE;
- SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
- }
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-}
-
-INT_PTR CLUIFramesHideAllTitleBars(WPARAM, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- F.TitleBar.ShowTitleBar = FALSE;
- SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
- }
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-}
-
-INT_PTR CLUIFramesShowHideFrame(WPARAM frameId, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- int pos;
- {
- mir_cslock lck(csFrameHook);
- pos = id2pos(frameId);
- if (pos >= 0 && !mir_wstrcmp(Frames[pos].name, L"My contacts"))
- Frames[pos].visible = 1;
- else {
- if (pos >= 0 && (int)pos < nFramescount)
- Frames[pos].visible = !Frames[pos].visible;
- if (Frames[pos].floating)
- CLUIFrameResizeFloatingFrame(pos);
- }
- }
-
- if (!Frames[pos].floating)
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-}
-
-INT_PTR CLUIFramesShowHideFrameTitleBar(WPARAM frameId, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- {
- mir_cslock lck(csFrameHook);
- int pos = id2pos(frameId);
- if (pos >= 0 && (int)pos < nFramescount) {
- Frames[pos].TitleBar.ShowTitleBar = !Frames[pos].TitleBar.ShowTitleBar;
- SetWindowPos(Frames[pos].hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
- }
- }
-
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-}
-
-// lparam=-1 up ,1 down
-INT_PTR CLUIFramesMoveUpDown(WPARAM frameId, LPARAM lParam)
-{
- int i, tmpval;
-
- if (FramesSysNotStarted)
- return -1;
-
- mir_cslockfull lck(csFrameHook);
- int pos = id2pos(frameId);
- if (pos < 0 || pos >= nFramescount)
- return 0;
-
- int curalign = Frames[pos].align;
- int v = 0;
- memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (F.floating || (!F.visible) || (F.align != curalign))
- continue;
- g_sd[v].order = F.order;
- g_sd[v].realpos = i;
- v++;
- }
- if (v == 0)
- return 0;
-
- qsort(g_sd, v, sizeof(SortData), sortfunc);
- for (i = 0; i < v; i++) {
- if (g_sd[i].realpos == pos) {
- if (lParam == -1) {
- if (i < 1) break;
- tmpval = Frames[g_sd[i - 1].realpos].order;
- Frames[g_sd[i - 1].realpos].order = Frames[pos].order;
- Frames[pos].order = tmpval;
- break;
- }
- if (lParam == 1) {
- if (i > v - 1) break;
- tmpval = Frames[g_sd[i + 1].realpos].order;
- Frames[g_sd[i + 1].realpos].order = Frames[pos].order;
- Frames[pos].order = tmpval;
- break;
- }
- }
- }
- lck.unlock();
-
- CLUIFramesReSort();
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
- return 0;
-}
-
-static INT_PTR CLUIFramesMoveUp(WPARAM frameId, LPARAM)
-{
- return CLUIFramesMoveUpDown(frameId, -1);
-}
-
-static INT_PTR CLUIFramesMoveDown(WPARAM frameId, LPARAM)
-{
- return CLUIFramesMoveUpDown(frameId, 1);
-}
-
-//lparam=alignment
-INT_PTR CLUIFramesSetAlign(WPARAM frameId, LPARAM lParam)
-{
- if (FramesSysNotStarted) return -1;
-
- CLUIFramesSetFrameOptions(MAKEWPARAM(FO_ALIGN, frameId), lParam);
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-}
-
-INT_PTR CLUIFramesSetAlignalTop(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted) return -1;
-
- return CLUIFramesSetAlign(wParam, alTop);
-}
-
-INT_PTR CLUIFramesSetAlignalBottom(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted) return -1;
-
- return CLUIFramesSetAlign(wParam, alBottom);
-}
-
-INT_PTR CLUIFramesSetAlignalClient(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted) return -1;
-
- return CLUIFramesSetAlign(wParam, alClient);
-}
-
-//wparam=frameid
-INT_PTR CLUIFramesLockUnlockFrame(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- mir_cslock lck(csFrameHook);
- int pos = id2pos(wParam);
- if (pos >= 0 && (int)pos < nFramescount) {
- Frames[pos].Locked = !Frames[pos].Locked;
- CLUIFramesStoreFrameSettings(pos);
- }
- return 0;
-}
-
-//wparam=frameid
-INT_PTR CLUIFramesSetUnSetBorder(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- HWND hw;
- int FrameId, oldflags;
- {
- mir_cslock lck(csFrameHook);
- FrameId = id2pos(wParam);
- if (FrameId == -1)
- return -1;
-
- oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0);
- if (oldflags & F_NOBORDER)
- oldflags &= (~F_NOBORDER);
- else
- oldflags |= F_NOBORDER;
-
- hw = Frames[FrameId].hWnd;
- }
-
- CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags);
- SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER);
- return 0;
-}
-
-//wparam=frameid
-INT_PTR CLUIFramesSetUnSetSkinned(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- HWND hw;
- int FrameId, oldflags;
- {
- mir_cslock lck(csFrameHook);
- FrameId = id2pos(wParam);
- if (FrameId == -1)
- return -1;
-
- oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0);
- if (oldflags & F_SKINNED)
- oldflags &= ~F_SKINNED;
- else
- oldflags |= F_SKINNED;
-
- hw = Frames[FrameId].hWnd;
- }
-
- CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags);
- SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER);
- return 0;
-}
-
-//wparam=frameid
-INT_PTR CLUIFramesCollapseUnCollapseFrame(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- TitleBarH = cfg::dat.titleBarHeight;
-
- mir_cslockfull lck(csFrameHook);
- int FrameId = id2pos(wParam);
- if (FrameId < 0 || FrameId >= nFramescount)
- return -1;
-
- int oldHeight;
-
- // do not collapse/uncollapse client/locked/invisible frames
- if (Frames[FrameId].align == alClient && !(Frames[FrameId].Locked || (!Frames[FrameId].visible) || Frames[FrameId].floating)) {
- RECT rc;
- if (Clist_IsDocked())
- return 0;
-
- if (db_get_b(0, "CLUI", "AutoSize", 0))
- return 0;
-
- GetWindowRect(g_clistApi.hwndContactList, &rc);
-
- if (Frames[FrameId].collapsed == TRUE) {
- rc.bottom -= rc.top;
- rc.bottom -= Frames[FrameId].height;
- Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height;
- Frames[FrameId].collapsed = FALSE;
- }
- else {
- rc.bottom -= rc.top;
- rc.bottom += Frames[FrameId].HeightWhenCollapsed;
- Frames[FrameId].collapsed = TRUE;
- }
-
- SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, rc.right - rc.left, rc.bottom, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
-
- CLUIFramesStoreAllFrames();
- lck.unlock();
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return 0;
-
- }
- if (Frames[FrameId].Locked || (!Frames[FrameId].visible))
- return 0;
-
- oldHeight = Frames[FrameId].height;
-
- // if collapsed, uncollapse
- if (Frames[FrameId].collapsed == TRUE) {
- Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height;
- Frames[FrameId].height = UNCOLLAPSED_FRAME_SIZE;
- Frames[FrameId].collapsed = FALSE;
- }
- // if uncollapsed, collapse
- else {
- Frames[FrameId].height = Frames[FrameId].HeightWhenCollapsed;
- Frames[FrameId].collapsed = TRUE;
- }
-
- if (!Frames[FrameId].floating) {
-
- if (!CLUIFramesFitInSize()) {
- //cant collapse,we can resize only for height<alclient frame height
- int alfrm = CLUIFramesGetalClientFrame();
-
- if (alfrm != -1) {
- Frames[FrameId].collapsed = FALSE;
- if (Frames[alfrm].height > 2 * UNCOLLAPSED_FRAME_SIZE) {
- oldHeight = Frames[alfrm].height - UNCOLLAPSED_FRAME_SIZE;
- Frames[FrameId].collapsed = TRUE;
- }
- }
- else {
- int i, sumheight = 0;
-
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) {
- sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2;
- return FALSE;
- }
- if (sumheight > ContactListHeight - 0 - 2)
- Frames[FrameId].height = (ContactListHeight - 0 - 2) - sumheight;
- }
- }
- Frames[FrameId].height = oldHeight;
- if (Frames[FrameId].collapsed == FALSE) {
- if (Frames[FrameId].floating)
- SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, Frames[FrameId].wndSize.right - Frames[FrameId].wndSize.left + 6, Frames[FrameId].height + DEFAULT_TITLEBAR_HEIGHT + 4, SWP_SHOWWINDOW | SWP_NOMOVE);
- return -1;
- }
- }
- }
- lck.unlock();
- if (!Frames[FrameId].floating)
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- else {
- RECT contwnd;
- GetWindowRect(Frames[FrameId].ContainerWnd, &contwnd);
- contwnd.top = contwnd.bottom - contwnd.top;//height
- contwnd.left = contwnd.right - contwnd.left;//width
-
- contwnd.top -= (oldHeight - Frames[FrameId].height);//newheight
- SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, contwnd.left, contwnd.top, SWP_SHOWWINDOW | SWP_NOMOVE);
- }
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- CLUIFramesStoreAllFrames();
- return 0;
-}
-
-static int CLUIFramesLoadMainMenu()
-{
- if (FramesSysNotStarted)
- return -1;
-
- for (auto &it : g_frameMenus)
- Menu_RemoveItem(it);
- g_frameMenus.destroy();
-
- // create frames menu
- CMenuItem mi(&g_plugin);
- mi.root = cont.MainMenuItem;
- mi.flags = CMIF_UNICODE | CMIF_SYSTEM;
- int separator = (int)3000200000;
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- mi.hIcolibItem = F.TitleBar.hicon;
- mi.position = separator;
- mi.name.w = F.TitleBar.tbname ? F.TitleBar.tbname : F.name;
- mi.pszService = nullptr;
- g_frameMenus.insert(F.MenuHandles.MainMenuItem = Menu_AddMainMenuItem(&mi));
- CLUIFramesCreateMenuForFrame(F.id, F.MenuHandles.MainMenuItem, separator, true);
- CLUIFramesModifyMainMenuItems(F.id, 0);
- CallService(MS_CLIST_FRAMEMENUNOTIFY, (WPARAM)F.id, (LPARAM)F.MenuHandles.MainMenuItem);
- separator++;
- }
- return 0;
-}
-
-static HFONT CLUILoadTitleBarFont()
-{
- char facename[] = "MS Shell Dlg";
- LOGFONT logfont;
- memset(&logfont, 0, sizeof(logfont));
- memcpy(logfont.lfFaceName, facename, sizeof(facename));
- logfont.lfWeight = FW_NORMAL;
- logfont.lfHeight = -10;
- return CreateFontIndirect(&logfont);
-}
-
-static int UpdateTBToolTip(int framepos)
-{
- TOOLINFO ti;
-
- memset(&ti, 0, sizeof(ti));
- ti.cbSize = sizeof(ti);
- ti.lpszText = Frames[framepos].TitleBar.tooltip;
- ti.hinst = g_plugin.getInst();
- ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
- ti.uId = (UINT_PTR)Frames[framepos].TitleBar.hwnd;
-
- return SendMessage(Frames[framepos].TitleBar.hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
-};
-
-int FrameNCPaint(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar)
-{
- RECT rcWindow, rc;
- HWND hwndParent = GetParent(hwnd);
- LRESULT result = 0;
-
- if (hwndParent != g_clistApi.hwndContactList || !cfg::dat.bSkinnedScrollbar)
- result = CallWindowProc(oldWndProc, hwnd, WM_NCPAINT, wParam, lParam);
- if (!g_clistApi.hwndContactList || hwndParent != g_clistApi.hwndContactList)
- return result;
-
- if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SKINNEDFRAME) {
- StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr;
- if (item == nullptr)
- return 0;
-
- GetWindowRect(hwnd, &rcWindow);
- rc.left = rc.top = 0;
- rc.right = rcWindow.right - rcWindow.left;
- rc.bottom = rcWindow.bottom - rcWindow.top;
-
- HDC hdc = GetWindowDC(hwnd);
- if (hwnd == g_clistApi.hwndContactTree) {
- HDC realDC = CreateCompatibleDC(hdc);
- HBITMAP hbmDraw = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
- HBITMAP hbmOld = reinterpret_cast<HBITMAP>(SelectObject(realDC, hbmDraw));
-
- ExcludeClipRect(realDC, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
- BitBlt(realDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY);
- DrawAlpha(realDC, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
-
- ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
- BitBlt(hdc, 0, 0, rc.right, rc.bottom, realDC, 0, 0, SRCCOPY);
- SelectObject(realDC, hbmOld);
- DeleteObject(hbmDraw);
- DeleteDC(realDC);
- }
- else {
- ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
- BitBlt(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY);
- DrawAlpha(hdc, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
- }
- ReleaseDC(hwnd, hdc);
- return 0;
- }
-
- if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) {
- HDC hdc = GetWindowDC(hwnd);
- HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames));
- GetWindowRect(hwnd, &rcWindow);
- rc.left = rc.top = 0;
- rc.right = rcWindow.right - rcWindow.left;
- rc.bottom = rcWindow.bottom - rcWindow.top;
- HBRUSH brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)));
- Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
- SelectObject(hdc, hPenOld);
- SelectObject(hdc, brold);
- ReleaseDC(hwnd, hdc);
- return 0;
- }
-
- return result;
-}
-
-int FrameNCCalcSize(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar)
-{
- StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr;
- LRESULT orig = oldWndProc ? CallWindowProc(oldWndProc, hwnd, WM_NCCALCSIZE, wParam, lParam) : 0;
- NCCALCSIZE_PARAMS *nccp = (NCCALCSIZE_PARAMS *)lParam;
- uint32_t dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
-
- if (item == nullptr)
- return orig;
-
- if (item->IGNORED || !(dwStyle & CLS_SKINNEDFRAME) || GetParent(hwnd) != g_clistApi.hwndContactList)
- return orig;
-
- nccp->rgrc[0].left += item->MARGIN_LEFT;
- nccp->rgrc[0].right -= item->MARGIN_RIGHT;
- nccp->rgrc[0].bottom -= item->MARGIN_BOTTOM;
- nccp->rgrc[0].top += item->MARGIN_TOP;
- return WVR_REDRAW;
-}
-
-static LRESULT CALLBACK FramesSubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- int i;
-
- WNDPROC oldWndProc = nullptr;
- BOOL hasTitleBar = FALSE;
-
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (F.hWnd == hwnd) {
- oldWndProc = F.wndProc;
- hasTitleBar = F.TitleBar.ShowTitleBar;
- }
- }
- switch (msg) {
- case WM_NCPAINT:
- return FrameNCPaint(hwnd, oldWndProc ? oldWndProc : DefWindowProc, wParam, lParam, hasTitleBar);
-
- case WM_NCCALCSIZE:
- return FrameNCCalcSize(hwnd, oldWndProc, wParam, lParam, hasTitleBar);
-
- case WM_PRINTCLIENT:
- return 0;
- }
-
- if (oldWndProc)
- return CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
- return DefWindowProc(hwnd, msg, wParam, lParam);
-}
-
-/*
- * re-sort all frames and correct frame ordering
- */
-
-static int CLUIFramesReSort()
-{
- int v = 0, i;
- int order = 1;
-
- mir_cslock lck(csFrameHook);
- memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (F.align != alTop)
- continue;
- g_sd[v].order = F.order;
- g_sd[v].realpos = i;
- v++;
- }
- if (v > 0) {
- qsort(g_sd, v, sizeof(SortData), sortfunc);
- for (i = 0; i < v; i++)
- Frames[g_sd[i].realpos].order = order++;
- }
-
- memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
- v = 0;
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (F.align != alBottom)
- continue;
- g_sd[v].order = F.order;
- g_sd[v].realpos = i;
- v++;
- }
- if (v > 0) {
- qsort(g_sd, v, sizeof(SortData), sortfunc);
- for (i = 0; i < v; i++)
- Frames[g_sd[i].realpos].order = order++;
- }
- CLUIFramesStoreAllFrames();
- return 0;
-}
-
-//wparam=(CLISTFrame*)clfrm
-INT_PTR CLUIFramesAddFrame(WPARAM wParam, LPARAM lParam)
-{
- int style;
- CLISTFrame *clfrm = (CLISTFrame *)wParam;
-
- if (g_clistApi.hwndContactList == nullptr) return -1;
- if (FramesSysNotStarted) return -1;
- if (clfrm->cbSize != sizeof(CLISTFrame)) return -1;
-
- mir_cslockfull lck(csFrameHook);
- if (nFramescount >= MAX_FRAMES)
- return -1;
-
- if (Frames == nullptr) {
- Frames = (FRAMEWND*)malloc(sizeof(FRAMEWND) * (MAX_FRAMES + 2));
- memset(Frames, 0, (sizeof(FRAMEWND) * (MAX_FRAMES + 2)));
- }
- memset(&Frames[nFramescount], 0, sizeof(FRAMEWND));
-
- Frames[nFramescount].id = NextFrameId++;
- Frames[nFramescount].align = clfrm->align;
- Frames[nFramescount].hWnd = clfrm->hWnd;
- Frames[nFramescount].height = clfrm->height;
- Frames[nFramescount].TitleBar.hicon = clfrm->hIcon;
- Frames[nFramescount].floating = false;
- Frames[nFramescount].pPlugin = (HPLUGIN)lParam;
-
- if (clfrm->Flags & F_NO_SUBCONTAINER)
- Frames[nFramescount].OwnerWindow = (HWND)-2;
- else
- Frames[nFramescount].OwnerWindow = g_clistApi.hwndContactList;
-
- SetClassLong(clfrm->hWnd, GCL_STYLE, GetClassLong(clfrm->hWnd, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
- SetWindowLongPtr(clfrm->hWnd, GWL_STYLE, GetWindowLongPtr(clfrm->hWnd, GWL_STYLE) | WS_CLIPCHILDREN);
-
- if (GetCurrentThreadId() == GetWindowThreadProcessId(clfrm->hWnd, nullptr)) {
- if (clfrm->hWnd != g_clistApi.hwndContactTree && clfrm->hWnd != g_hwndViewModeFrame && clfrm->hWnd != g_hwndEventArea) {
- Frames[nFramescount].wndProc = (WNDPROC)GetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC);
- SetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC, (LONG_PTR)FramesSubClassProc);
- }
- }
-
- if (clfrm->hWnd == g_hwndEventArea)
- wndFrameEventArea = &Frames[nFramescount];
- else if (clfrm->hWnd == g_clistApi.hwndContactTree)
- wndFrameCLC = &Frames[nFramescount];
- else if (clfrm->hWnd == g_hwndViewModeFrame)
- wndFrameViewMode = &Frames[nFramescount];
-
- Frames[nFramescount].dwFlags = clfrm->Flags;
-
- if (clfrm->szName.a == nullptr || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szName.w) : mir_strlen(clfrm->szName.a)) == 0) {
- wchar_t ptszClassName[256];
- GetClassName(Frames[nFramescount].hWnd, ptszClassName, _countof(ptszClassName));
- Frames[nFramescount].name = mir_wstrdup(ptszClassName);
- }
- else Frames[nFramescount].name = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szName.w) : mir_a2u(clfrm->szName.a);
-
- if (IsBadCodePtr((FARPROC)clfrm->szTBname.a) || clfrm->szTBname.a == nullptr
- || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szTBname.w) : mir_strlen(clfrm->szTBname.a)) == 0)
- Frames[nFramescount].TitleBar.tbname = mir_wstrdup(Frames[nFramescount].name);
- else
- Frames[nFramescount].TitleBar.tbname = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szTBname.w) : mir_a2u(clfrm->szTBname.a);
- Frames[nFramescount].needhide = FALSE;
- Frames[nFramescount].TitleBar.ShowTitleBar = (clfrm->Flags & F_SHOWTB ? TRUE : FALSE);
- Frames[nFramescount].TitleBar.ShowTitleBarTip = (clfrm->Flags & F_SHOWTBTIP ? TRUE : FALSE);
-
- Frames[nFramescount].collapsed = clfrm->Flags & F_UNCOLLAPSED ? FALSE : TRUE;
- Frames[nFramescount].Locked = clfrm->Flags & F_LOCKED ? TRUE : FALSE;
- Frames[nFramescount].visible = clfrm->Flags & F_VISIBLE ? TRUE : FALSE;
-
- Frames[nFramescount].UseBorder = (clfrm->Flags & F_NOBORDER) ? FALSE : TRUE;
- Frames[nFramescount].Skinned = (clfrm->Flags & F_SKINNED) ? TRUE : FALSE;
-
- // create frame
- Frames[nFramescount].TitleBar.hwnd =
- CreateWindow(CLUIFrameTitleBarClassName, Frames[nFramescount].name,
- (db_get_b(0, CLUIFrameModule, "RemoveAllTitleBarBorders", 1) ? 0 : WS_BORDER)
- | WS_CHILD | WS_CLIPCHILDREN | (Frames[nFramescount].TitleBar.ShowTitleBar ? WS_VISIBLE : 0) |
- WS_CLIPCHILDREN, 0, 0, 0, 0, g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr);
-
- SetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWLP_USERDATA, Frames[nFramescount].id);
-
- Frames[nFramescount].TitleBar.hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr);
-
- SetWindowPos(Frames[nFramescount].TitleBar.hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
- {
- TOOLINFOA ti = { 0 };
- ti.cbSize = sizeof(ti);
- ti.lpszText = "";
- ti.hinst = g_plugin.getInst();
- ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
- ti.uId = (UINT_PTR)Frames[nFramescount].TitleBar.hwnd;
- SendMessageA(Frames[nFramescount].TitleBar.hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
- }
-
- SendMessage(Frames[nFramescount].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[nFramescount].TitleBar.ShowTitleBarTip, 0);
-
- Frames[nFramescount].oldstyles = GetWindowLongPtr(Frames[nFramescount].hWnd, GWL_STYLE);
- Frames[nFramescount].TitleBar.oldstyles = GetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWL_STYLE);
-
- int retval = Frames[nFramescount].id;
- Frames[nFramescount].order = nFramescount + 1;
- nFramescount++;
-
- CLUIFramesLoadFrameSettings(id2pos(retval));
- style = GetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE);
- style &= ~(WS_BORDER);
- style |= ((Frames[nFramescount - 1].UseBorder) ? WS_BORDER : 0);
-
- style |= Frames[nFramescount - 1].Skinned ? CLS_SKINNEDFRAME : 0;
-
- SetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE, style);
- SetWindowLongPtr(Frames[nFramescount - 1].TitleBar.hwnd, GWL_STYLE, style & ~(WS_VSCROLL | WS_HSCROLL));
-
- if (Frames[nFramescount - 1].order == 0)
- Frames[nFramescount - 1].order = nFramescount;
-
- lck.unlock();
-
- alclientFrame = -1;//recalc it
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
-
- if (Frames[nFramescount - 1].floating) {
- Frames[nFramescount - 1].floating = FALSE;
- CLUIFrameSetFloat(retval, 1);//lparam=1 use stored width and height
- }
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- return retval;
-}
-
-static INT_PTR CLUIFramesRemoveFrame(WPARAM wParam, LPARAM)
-{
- if (FramesSysNotStarted)
- return -1;
-
- {
- mir_cslock lck(csFrameHook);
- int pos = id2pos(wParam);
- if (pos < 0 || pos > nFramescount)
- return -1;
-
- FRAMEWND* F = &Frames[pos];
- if (F->hWnd == g_hwndEventArea)
- wndFrameEventArea = nullptr;
- else if (F->hWnd == g_clistApi.hwndContactTree)
- wndFrameCLC = nullptr;
- else if (F->hWnd == g_hwndViewModeFrame)
- wndFrameViewMode = nullptr;
-
- mir_free(F->name);
- mir_free(F->TitleBar.tbname);
- mir_free(F->TitleBar.tooltip);
-
- DestroyWindow(F->hWnd);
- F->hWnd = (HWND)-1;
- DestroyWindow(F->TitleBar.hwnd);
- F->TitleBar.hwnd = (HWND)-1;
- DestroyWindow(F->ContainerWnd);
- F->ContainerWnd = (HWND)-1;
- DestroyMenu(F->TitleBar.hmenu);
-
- RemoveItemFromList(pos, &Frames, &nFramescount);
- }
-
- if (!cfg::shutDown) {
- InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- }
- return 0;
-}
-
-INT_PTR CLUIFramesForceUpdateTB(const FRAMEWND *Frame)
-{
- if (Frame->TitleBar.hwnd != nullptr)
- RedrawWindow(Frame->TitleBar.hwnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
- return 0;
-}
-
-INT_PTR CLUIFramesForceUpdateFrame(const FRAMEWND *Frame)
-{
- if (Frame->hWnd != nullptr)
- RedrawWindow(Frame->hWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_FRAME | RDW_ERASE | RDW_INVALIDATE);
-
- if (Frame->floating)
- if (Frame->ContainerWnd != nullptr) RedrawWindow(Frame->ContainerWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
- return 0;
-}
-
-int CLUIFrameMoveResize(const FRAMEWND *Frame)
-{
- TitleBarH = cfg::dat.titleBarHeight;
- // we need to show or hide the frame?
- if (Frame->visible && (!Frame->needhide)) {
- ShowWindow(Frame->hWnd, SW_SHOW);
- ShowWindow(Frame->TitleBar.hwnd, Frame->TitleBar.ShowTitleBar == TRUE ? SW_SHOW : SW_HIDE);
- }
- else {
- ShowWindow(Frame->hWnd, SW_HIDE);
- ShowWindow(Frame->TitleBar.hwnd, SW_HIDE);
- return 0;
- }
-
- SetWindowPos(Frame->hWnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset,
- (Frame->wndSize.right - Frame->wndSize.left),
- (Frame->wndSize.bottom - Frame->wndSize.top), SWP_NOZORDER | SWP_NOREDRAW);
- if (Frame->TitleBar.ShowTitleBar) {
- SetWindowPos(Frame->TitleBar.hwnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset - TitleBarH,
- (Frame->wndSize.right - Frame->wndSize.left),
- TitleBarH + (Frame->UseBorder ? (!Frame->collapsed ? (Frame->align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER);
- }
- return 0;
-}
-
-bool CLUIFramesFitInSize(void)
-{
- int i;
- int sumheight = 0;
- int tbh = 0; // title bar height
- int clientfrm;
-
- TitleBarH = cfg::dat.titleBarHeight;
-
- clientfrm = CLUIFramesGetalClientFrame();
- if (clientfrm != -1)
- tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
-
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) {
- sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2/*+btoint(F.UseBorder)*2*/;
- if (sumheight > ContactListHeight - tbh - 2)
- return FALSE;
- }
- }
- return TRUE;
-}
-
-int CLUIFramesGetMinHeight()
-{
- if (g_clistApi.hwndContactList == nullptr)
- return 0;
-
- int i, tbh, clientfrm, sumheight = 0;
- RECT border;
- int allbord = 0;
- {
- mir_cslock lck(csFrameHook);
-
- TitleBarH = cfg::dat.titleBarHeight;
- // search for alClient frame and get the titlebar's height
- tbh = 0;
- clientfrm = CLUIFramesGetalClientFrame();
- if (clientfrm != -1)
- tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
-
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if ((F.align != alClient) && (F.visible) && (!F.needhide) && (!F.floating)) {
- RECT wsize;
-
- GetWindowRect(F.hWnd, &wsize);
- sumheight += (wsize.bottom - wsize.top) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 3;
- }
- }
- }
-
- GetBorderSize(g_clistApi.hwndContactList, &border);
- return(sumheight + border.top + border.bottom + allbord + tbh + 3);
-}
-
-int SizeMoveNewSizes()
-{
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (F.floating)
- CLUIFrameResizeFloatingFrame(i);
- else
- CLUIFrameMoveResize(&F);
- }
- return 0;
-}
-
-/*
- * changed Nightwish
- * gap calculation was broken. Now, it doesn't calculate and store the gaps in Frames[] anymore.
- * instead, it remembers the smallest wndSize.top value (which has to be the top frame) and then passes
- * the gap to all following frame(s) to the actual resizing function which just adds the gap to
- * wndSize.top and corrects the frame height accordingly.
-
- * Title bar gap has been removed (can be simulated by using a clist_nicer skin item for frame title bars
- * and setting the bottom margin of the skin item
- */
-
-int CLUIFramesResize(const RECT newsize)
-{
- int sumheight = 9999999;
- int clientframe = -1;
- int i, j;
- int topOff = 0, botOff = 0, last_bottomtop;
-
- GapBetweenFrames = cfg::dat.gapBetweenFrames;
- int sepw = GapBetweenFrames;
-
- if (nFramescount < 1 || cfg::shutDown)
- return 0;
-
- int newheight = newsize.bottom - newsize.top;
- TitleBarH = cfg::dat.titleBarHeight;
-
- // search for alClient frame and get the titlebar's height
- int tbh = 0;
- int clientfrm = CLUIFramesGetalClientFrame();
- if (clientfrm != -1)
- tbh = (TitleBarH)* btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
-
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (!F.floating) {
- F.needhide = FALSE;
- F.wndSize.left = 0;
- F.wndSize.right = newsize.right - newsize.left;
- }
- }
- {
- //sorting stuff
- memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
- for (i = 0; i < nFramescount; i++) {
- g_sd[i].order = Frames[i].order;
- g_sd[i].realpos = i;
- }
- qsort(g_sd, nFramescount, sizeof(SortData), sortfunc);
-
- }
- int drawitems = nFramescount;
- while (sumheight >(newheight - tbh) && drawitems > 0) {
- sumheight = 0;
- drawitems = 0;
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (((F.align != alClient)) && (!F.floating) && (F.visible) && (!F.needhide)) {
- drawitems++;
- int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
- sumheight += (F.height) + curfrmtbh + (i > 0 ? sepw : 0) + (F.UseBorder ? 2 : 0);
- if (sumheight > newheight - tbh) {
- sumheight -= (F.height) + curfrmtbh + (i > 0 ? sepw : 0);
- F.needhide = TRUE;
- drawitems--;
- break;
- }
- }
- }
- }
-
- int prevframe = -1;
- int prevframebottomline = 0;
- for (j = 0; j < nFramescount; j++) {
- // move all alTop frames
- i = g_sd[j].realpos;
- FRAMEWND &F = Frames[i];
- if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alTop)) {
- int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
- F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (curfrmtbh);
- F.wndSize.bottom = F.height + F.wndSize.top + (F.UseBorder ? 2 : 0);
- F.prevvisframe = prevframe;
- prevframe = i;
- prevframebottomline = F.wndSize.bottom;
- topOff = prevframebottomline;
- }
- }
-
- if (sumheight < newheight) {
- for (j = 0; j < nFramescount; j++) {
- // move alClient frame
- i = g_sd[j].realpos;
- FRAMEWND &F = Frames[i];
- if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alClient)) {
- int oldh;
- F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (tbh);
- F.wndSize.bottom = F.wndSize.top + newheight - sumheight - tbh - ((prevframebottomline > 0) ? sepw : 0);
- clientframe = i;
- oldh = F.height;
- F.height = F.wndSize.bottom - F.wndSize.top;
- F.prevvisframe = prevframe;
- prevframe = i;
- prevframebottomline = F.wndSize.bottom;
- if (prevframebottomline > newheight) {
- // prevframebottomline-=F.height+(tbh+1);
- // F.needhide=TRUE;
- }
- break;
- }
- }
- }
-
- // newheight
- prevframebottomline = last_bottomtop = newheight;
- for (j = nFramescount - 1; j >= 0; j--) {
- // move all alBottom frames
- i = g_sd[j].realpos;
- FRAMEWND &F = Frames[i];
- if ((F.visible) && (!F.floating) && (!F.needhide) && (F.align == alBottom)) {
- int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
- F.wndSize.bottom = prevframebottomline - ((prevframebottomline < newheight) ? sepw : 0);
- F.wndSize.top = F.wndSize.bottom - F.height - (F.UseBorder ? 2 : 0);
- F.prevvisframe = prevframe;
- prevframe = i;
- prevframebottomline = F.wndSize.top - curfrmtbh;
- botOff = prevframebottomline;
- last_bottomtop = F.wndSize.top - curfrmtbh;
- }
- }
-
- // correct client frame bottom gap if there is no other top frame.
- if (clientframe != -1) {
- Frames[clientframe].wndSize.bottom = last_bottomtop - (last_bottomtop < newheight ? sepw : 0);
- Frames[clientframe].height = Frames[clientframe].wndSize.bottom - Frames[clientframe].wndSize.top;
- }
- return 0;
-}
-
-INT_PTR CLUIFramesUpdateFrame(WPARAM wParam, LPARAM lParam)
-{
- if (FramesSysNotStarted)
- return -1;
-
- if (wParam == -1) {
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- return 0;
- }
-
- if (lParam & FU_FMPOS)
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 1);
-
- mir_cslock lck(csFrameHook);
- int pos = id2pos(wParam);
- if (pos < 0 || pos >= nFramescount)
- return -1;
-
- if (lParam & FU_TBREDRAW)
- CLUIFramesForceUpdateTB(&Frames[pos]);
- if (lParam & FU_FMREDRAW)
- CLUIFramesForceUpdateFrame(&Frames[pos]);
- return 0;
-}
-
-int dock_prevent_moving = 0;
-
-int CLUIFramesApplyNewSizes(int mode)
-{
- dock_prevent_moving = 0;
-
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if ((mode == 1 && F.OwnerWindow != (HWND)-2 && F.OwnerWindow) ||
- (mode == 2 && F.OwnerWindow == (HWND)-2) || (mode == 3))
- if (F.floating)
- CLUIFrameResizeFloatingFrame(i);
- else
- CLUIFrameMoveResize(&Frames[i]);
- }
- dock_prevent_moving = 1;
- return 0;
-}
-
-RECT old_window_rect = { 0 }, new_window_rect = { 0 };
-
-int SizeFramesByWindowRect(RECT *r)
-{
- if (FramesSysNotStarted)
- return -1;
-
- TitleBarH = cfg::dat.titleBarHeight;
-
- mir_cslock lck(csFrameHook);
- GapBetweenFrames = cfg::dat.gapBetweenFrames;
-
- RECT nRect = *r;
- nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset);
- nRect.right -= cfg::dat.bCRight;
- nRect.left = cfg::dat.bCLeft;
- nRect.top = cfg::dat.topOffset;
- ContactListHeight = nRect.bottom - nRect.top;
-
- CLUIFramesResize(nRect);
- {
- int i;
- for (i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- if (!F.floating) {
- if (F.OwnerWindow && F.OwnerWindow != (HWND)-2) {
- SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset,
- (F.wndSize.right - F.wndSize.left),
- (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
-
- if (F.TitleBar.ShowTitleBar) {
- SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH,
- (F.wndSize.right - F.wndSize.left),
- TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
- }
- }
- else {
- // set frame position
- SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset,
- (F.wndSize.right - F.wndSize.left),
- (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOREDRAW);
-
- // set titlebar position
- if (F.TitleBar.ShowTitleBar) {
- SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH,
- (F.wndSize.right - F.wndSize.left),
- TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW);
- }
- if (F.TitleBar.ShowTitleBar)
- UpdateWindow(F.TitleBar.hwnd);
- }
- }
- }
-
- if (GetTickCount() - LastStoreTick > 1000) {
- CLUIFramesStoreAllFrames();
- LastStoreTick = GetTickCount();
- }
- }
- return 0;
-}
-
-int CLUIFramesOnClistResize(WPARAM wParam, LPARAM lParam)
-{
- GapBetweenFrames = cfg::dat.gapBetweenFrames;
-
- if (FramesSysNotStarted || cfg::shutDown)
- return -1;
-
- RECT nRect, rcStatus;
- int tick;
- {
- mir_cslock lck(csFrameHook);
-
- GetClientRect(g_clistApi.hwndContactList, &nRect);
- if (lParam && lParam != 1) {
- RECT oldRect;
- POINT pt;
- RECT * newRect = (RECT *)lParam;
- int dl, dt, dr, db;
- GetWindowRect((HWND)wParam, &oldRect);
- pt.x = nRect.left;
- pt.y = nRect.top;
- ClientToScreen(g_clistApi.hwndContactList, &pt);
- dl = pt.x - oldRect.left;
- dt = pt.y - oldRect.top;
- dr = (oldRect.right - oldRect.left) - (nRect.right - nRect.left) - dl;
- db = (oldRect.bottom - oldRect.top) - (nRect.bottom - nRect.top) - dt;
- nRect.left = newRect->left + dl;
- nRect.top = newRect->top + dt;
- nRect.bottom = newRect->bottom - db;
- nRect.right = newRect->right - dr;
- }
-
- rcStatus.top = rcStatus.bottom = 0;
-
- nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset);
- nRect.right -= cfg::dat.bCRight;
- nRect.left = cfg::dat.bCLeft;
- nRect.top = cfg::dat.topOffset;
- ContactListHeight = nRect.bottom - nRect.top;
-
- tick = GetTickCount();
-
- CLUIFramesResize(nRect);
- CLUIFramesApplyNewSizes(3);
- }
-
- tick = GetTickCount() - tick;
-
- if (g_clistApi.hwndContactList != nullptr)
- InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
- if (g_clistApi.hwndContactList != nullptr)
- UpdateWindow(g_clistApi.hwndContactList);
-
- Sleep(0);
-
- if (GetTickCount() - LastStoreTick > 2000) {
- CLUIFramesStoreAllFrames();
- LastStoreTick = GetTickCount();
- }
- return 0;
-}
-
-static HBITMAP hBmpBackground;
-static int backgroundBmpUse;
-static COLORREF bkColour;
-static COLORREF SelBkColour;
-boolean AlignCOLLIconToLeft; //will hide frame icon
-
-int OnFrameTitleBarBackgroundChange()
-{
- AlignCOLLIconToLeft = db_get_b(0, "FrameTitleBar", "AlignCOLLIconToLeft", 0);
- bkColour = db_get_dw(0, "FrameTitleBar", "BkColour", CLCDEFAULT_BKCOLOUR);
-
- if (hBmpBackground) {
- DeleteObject(hBmpBackground);
- hBmpBackground = nullptr;
- }
- if (db_get_b(0, "FrameTitleBar", "UseBitmap", CLCDEFAULT_USEBITMAP)) {
- ptrW tszBitmapName(db_get_wsa(0, "FrameTitleBar", "BkBitmap"));
- if (tszBitmapName != NULL)
- hBmpBackground = Bitmap_Load(tszBitmapName);
- }
- backgroundBmpUse = db_get_w(0, "FrameTitleBar", "BkBmpUse", CLCDEFAULT_BKBMPUSE);
-
- CLUIFramesOnClistResize(0, 0);
- return 0;
-}
-
-static int DrawTitleBar(HDC dc, RECT rect, int Frameid)
-{
- StatusItems_t *item = arStatusItems[ID_EXTBKFRAMETITLE - ID_STATUS_OFFLINE];
-
- /*
- * no need to redraw anything while shutting down
- */
- if (cfg::shutDown)
- return 0;
-
- TitleBarH = cfg::dat.titleBarHeight;
- HDC hdcMem = CreateCompatibleDC(dc);
- HBITMAP hBmpOsb = CreateCompatibleBitmap(dc, rect.right, rect.bottom);
- HBITMAP hoBmp = reinterpret_cast<HBITMAP>(SelectObject(hdcMem, hBmpOsb));
-
- SetBkMode(hdcMem, TRANSPARENT);
-
- HBRUSH hBack = GetSysColorBrush(COLOR_3DFACE);
- HBRUSH hoBrush = reinterpret_cast<HBRUSH>(SelectObject(hdcMem, hBack));
- {
- mir_cslock lck(csFrameHook);
- int pos = id2pos(Frameid);
- if (pos >= 0 && pos < nFramescount) {
- HFONT oFont;
- int fHeight, fontTop;
- GetClientRect(Frames[pos].TitleBar.hwnd, &Frames[pos].TitleBar.wndSize);
-
- if (cfg::clcdat) {
- oFont = ChangeToFont(hdcMem, cfg::clcdat, FONTID_FRAMETITLE, &fHeight);
- }
- else {
- oFont = reinterpret_cast<HFONT>(SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT)));
- fHeight = 10;
- }
- fontTop = (TitleBarH - fHeight) / 2;
-
- if (cfg::dat.bWallpaperMode && !Frames[pos].floating)
- SkinDrawBg(Frames[pos].TitleBar.hwnd, hdcMem);
-
- if (!item->IGNORED) {
- RECT rc = Frames[pos].TitleBar.wndSize;
- rc.top += item->MARGIN_TOP;
- rc.bottom -= item->MARGIN_BOTTOM;
- rc.left += item->MARGIN_LEFT;
- rc.right -= item->MARGIN_RIGHT;
- DrawAlpha(hdcMem, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT,
- item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
- SetTextColor(hdcMem, item->TEXTCOLOR);
- }
- else if (cfg::clcdat) {
- FillRect(hdcMem, &rect, hBack);
- SetTextColor(hdcMem, cfg::clcdat->fontInfo[FONTID_FRAMETITLE].colour);
- }
- else {
- FillRect(hdcMem, &rect, hBack);
- SetTextColor(hdcMem, GetSysColor(COLOR_BTNTEXT));
- }
-
- const wchar_t *pwszTitle = TranslateW_LP(Frames[pos].TitleBar.tbname, Frames[pos].pPlugin);
- int iTitleLen = (int)mir_wstrlen(pwszTitle);
-
- if (!AlignCOLLIconToLeft) {
- if (Frames[pos].TitleBar.hicon != nullptr) {
- DrawIconEx(hdcMem, 6 + cfg::dat.bClipBorder, ((TitleBarH >> 1) - 8), Frames[pos].TitleBar.hicon, 16, 16, 0, nullptr, DI_NORMAL);
- TextOut(hdcMem, 24 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
- }
- else TextOut(hdcMem, 6 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
- }
- else TextOut(hdcMem, 18 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
-
- if (!AlignCOLLIconToLeft)
- DrawIconEx(hdcMem, Frames[pos].TitleBar.wndSize.right - 22, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL);
- else
- DrawIconEx(hdcMem, 0, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL);
- SelectObject(hdcMem, oFont);
- }
- }
-
- BitBlt(dc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, SRCCOPY);
- SelectObject(hdcMem, hoBmp);
- SelectObject(hdcMem, hoBrush);
- DeleteDC(hdcMem);
- DeleteObject(hBack);
- DeleteObject(hBmpOsb);
- return 0;
-}
-
-#define MPCF_CONTEXTFRAMEMENU 3
-POINT ptOld;
-short nLeft = 0;
-short nTop = 0;
-
-LRESULT CALLBACK CLUIFrameTitleBarProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- RECT rect;
- int Frameid, Framemod, direction;
- int xpos, ypos, framepos;
-
- Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA);
- memset(&rect, 0, sizeof(rect));
-
- switch (msg) {
- case WM_CREATE:
- return FALSE;
-
- case WM_MEASUREITEM:
- return Menu_MeasureItem(lParam);
-
- case WM_DRAWITEM:
- return Menu_DrawItem(lParam);
-
- case WM_ENABLE:
- if (hwnd != nullptr) InvalidateRect(hwnd, nullptr, FALSE);
- return 0;
- case WM_SIZE:
- return 0;
-
- case WM_COMMAND:
- if (Clist_MenuProcessCommand(LOWORD(wParam), 0, Frameid))
- break;
-
- if (HIWORD(wParam) == 0) {//mouse events for self created menu
- framepos = id2pos(Frameid);
- if (framepos == -1)
- break;
-
- switch (LOWORD(wParam)) {
- case frame_menu_lock:
- Frames[framepos].Locked = !Frames[framepos].Locked;
- break;
- case frame_menu_visible:
- Frames[framepos].visible = !Frames[framepos].visible;
- break;
- case frame_menu_showtitlebar:
- Frames[framepos].TitleBar.ShowTitleBar = !Frames[framepos].TitleBar.ShowTitleBar;
- break;
- case frame_menu_floating:
- CLUIFrameSetFloat(Frameid, 0);
- break;
- }
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- }
- break;
-
- case WM_RBUTTONDOWN:
- {
- HMENU hmenu;
- if (ServiceExists(MS_CLIST_MENUBUILDFRAMECONTEXT))
- hmenu = (HMENU)CallService(MS_CLIST_MENUBUILDFRAMECONTEXT, Frameid, 0);
- else {
- framepos = id2pos(Frameid);
-
- mir_cslock lck(csFrameHook);
- if (framepos == -1)
- break;
-
- hmenu = CreatePopupMenu();
- AppendMenu(hmenu, MF_STRING | MF_DISABLED | MF_GRAYED, 15, Frames[framepos].name);
- AppendMenu(hmenu, MF_SEPARATOR, 16, L"");
-
- if (Frames[framepos].Locked)
- AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_lock, TranslateT("Lock frame"));
- else
- AppendMenu(hmenu, MF_STRING, frame_menu_lock, TranslateT("Lock frame"));
-
- if (Frames[framepos].visible)
- AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_visible, TranslateT("Visible"));
- else
- AppendMenu(hmenu, MF_STRING, frame_menu_visible, TranslateT("Visible"));
-
- if (Frames[framepos].TitleBar.ShowTitleBar)
- AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_showtitlebar, TranslateT("Show title bar"));
- else
- AppendMenu(hmenu, MF_STRING, frame_menu_showtitlebar, TranslateT("Show title bar"));
-
- if (Frames[framepos].Skinned)
- AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_skinned, TranslateT("Skinned frame"));
- else
- AppendMenu(hmenu, MF_STRING, frame_menu_skinned, TranslateT("Skinned frame"));
-
- if (Frames[framepos].floating)
- AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_floating, TranslateT("Floating"));
- else
- AppendMenu(hmenu, MF_STRING, frame_menu_floating, TranslateT("Floating"));
- }
- POINT pt;
- GetCursorPos(&pt);
- TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, nullptr);
- DestroyMenu(hmenu);
- }
- break;
-
- case WM_LBUTTONDBLCLK:
- Framemod = -1;
- lbypos = -1;
- oldframeheight = -1;
- ReleaseCapture();
- CallService(MS_CLIST_FRAMES_UCOLLFRAME, Frameid, 0);
- lbypos = -1;
- oldframeheight = -1;
- ReleaseCapture();
- break;
-
- case WM_LBUTTONUP:
- if (GetCapture() != hwnd)
- break;
-
- curdragbar = -1;
- lbypos = -1;
- oldframeheight = -1;
- ReleaseCapture();
- RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
- break;
-
- case WM_LBUTTONDOWN:
- framepos = id2pos(Frameid);
- {
- mir_cslock lck(csFrameHook);
- if (framepos == -1)
- break;
-
- if (Frames[framepos].floating) {
- POINT pt;
- GetCursorPos(&pt);
- Frames[framepos].TitleBar.oldpos = pt;
- }
-
- if ((!(wParam&MK_CONTROL)) && Frames[framepos].Locked && (!(Frames[framepos].floating))) {
- if (db_get_b(0, "CLUI", "ClientAreaDrag", 0)) {
- POINT pt;
- GetCursorPos(&pt);
- return SendMessage(GetParent(hwnd), WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
- }
- }
- if (Frames[framepos].floating) {
- RECT rc;
- GetCursorPos(&ptOld);
- GetWindowRect(hwnd, &rc);
- nLeft = (short)rc.left;
- nTop = (short)rc.top;
- }
- }
- SetCapture(hwnd);
- break;
-
- case WM_MOUSEMOVE:
- {
- mir_cslock lck(csFrameHook);
- int pos = id2pos(Frameid);
- if (pos != -1) {
- int oldflags;
- char TBcapt[255];
- mir_snprintf(TBcapt, "%s - h:%d, vis:%d, fl:%d, fl:(%d,%d,%d,%d),or: %d",
- Frames[pos].name, Frames[pos].height, Frames[pos].visible, Frames[pos].floating,
- Frames[pos].FloatingPos.x, Frames[pos].FloatingPos.y,
- Frames[pos].FloatingSize.x, Frames[pos].FloatingSize.y,
- Frames[pos].order);
-
- oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, Frames[pos].id), 0);
- if (!(oldflags & F_SHOWTBTIP))
- oldflags |= F_SHOWTBTIP;
- }
- }
- if (wParam & MK_LBUTTON) {
- RECT rcMiranda;
- RECT rcwnd, rcOverlap;
- POINT newpt, ofspt, curpt, newpos;
-
- mir_cslockfull lck(csFrameHook);
-
- int pos = id2pos(Frameid);
- if (Frames[pos].floating) {
- GetCursorPos(&curpt);
- rcwnd.bottom = curpt.y + 5;
- rcwnd.top = curpt.y;
- rcwnd.left = curpt.x;
- rcwnd.right = curpt.x + 5;
-
- GetWindowRect(g_clistApi.hwndContactList, &rcMiranda);
- if (IsWindowVisible(g_clistApi.hwndContactList) && IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) {
- int id = Frames[pos].id;
-
- lck.unlock();
- ofspt.x = 0;
- ofspt.y = 0;
- ClientToScreen(Frames[pos].TitleBar.hwnd, &ofspt);
- ofspt.x = curpt.x - ofspt.x;
- ofspt.y = curpt.y - ofspt.y;
-
- CLUIFrameSetFloat(id, 0);
- newpt.x = 0;
- newpt.y = 0;
- ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt);
- SetCursorPos(newpt.x + ofspt.x, newpt.y + ofspt.y);
- GetCursorPos(&curpt);
-
- lck.lock();
- Frames[pos].TitleBar.oldpos = curpt;
- return 0;
- }
- }
- else {
- int id = Frames[pos].id;
-
- GetCursorPos(&curpt);
- rcwnd.bottom = curpt.y + 5;
- rcwnd.top = curpt.y;
- rcwnd.left = curpt.x;
- rcwnd.right = curpt.x + 5;
-
- GetWindowRect(g_clistApi.hwndContactList, &rcMiranda);
-
- if (!IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) {
- lck.unlock();
- GetCursorPos(&curpt);
- GetWindowRect(Frames[pos].hWnd, &rcwnd);
- rcwnd.left = rcwnd.right - rcwnd.left;
- rcwnd.top = rcwnd.bottom - rcwnd.top;
- newpos.x = curpt.x;
- newpos.y = curpt.y;
- if (curpt.x >= (rcMiranda.right - 1))
- newpos.x = curpt.x + 5;
- if (curpt.x <= (rcMiranda.left + 1))
- newpos.x = curpt.x - (rcwnd.left) - 5;
- if (curpt.y >= (rcMiranda.bottom - 1))
- newpos.y = curpt.y + 5;
- if (curpt.y <= (rcMiranda.top + 1))
- newpos.y = curpt.y - (rcwnd.top) - 5;
-
- ofspt.x = 0;
- ofspt.y = 0;
- GetWindowRect(Frames[pos].TitleBar.hwnd, &rcwnd);
- ofspt.x = curpt.x - ofspt.x;
- ofspt.y = curpt.y - ofspt.y;
- Frames[pos].FloatingPos.x = newpos.x;
- Frames[pos].FloatingPos.y = newpos.y;
- CLUIFrameSetFloat(id, 0);
-
- lck.lock();
- newpt.x = 0;
- newpt.y = 0;
- ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt);
- GetWindowRect(Frames[pos].hWnd, &rcwnd);
- SetCursorPos(newpt.x + (rcwnd.right - rcwnd.left) / 2, newpt.y + (rcwnd.bottom - rcwnd.top) / 2);
- GetCursorPos(&curpt);
- Frames[pos].TitleBar.oldpos = curpt;
- return 0;
- }
- }
- }
- if (wParam & MK_LBUTTON) {
- int newh = -1, prevold;
-
- if (GetCapture() != hwnd)
- break;
-
- POINT pt, pt2;
- mir_cslockfull lck(csFrameHook);
- int pos = id2pos(Frameid);
-
- if (Frames[pos].floating) {
- RECT wndr;
- GetCursorPos(&pt);
- if ((Frames[pos].TitleBar.oldpos.x != pt.x) || (Frames[pos].TitleBar.oldpos.y != pt.y)) {
- pt2 = pt;
- ScreenToClient(hwnd, &pt2);
- GetWindowRect(Frames[pos].ContainerWnd, &wndr);
-
- POINT ptNew = pt;
-
- nLeft += (short)ptNew.x - ptOld.x;
- nTop += (short)ptNew.y - ptOld.y;
-
- if (!(wParam & MK_CONTROL))
- PositionThumb(&Frames[pos], nLeft, nTop);
- else
- SetWindowPos(Frames[pos].ContainerWnd, nullptr, nLeft, nTop, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
-
- ptOld = ptNew;
-
- pt.x = nLeft;
- pt.y = nTop;
- Frames[pos].TitleBar.oldpos = pt;
- }
- return 0;
- }
- if (Frames[pos].prevvisframe != -1) {
- GetCursorPos(&pt);
-
- if ((Frames[pos].TitleBar.oldpos.x == pt.x) && (Frames[pos].TitleBar.oldpos.y == pt.y))
- break;
-
- ypos = rect.top + pt.y;
- xpos = rect.left + pt.x;
- Framemod = -1;
-
- if (Frames[pos].align == alBottom) {
- direction = -1;
- Framemod = pos;
- }
- else {
- direction = 1;
- Framemod = Frames[pos].prevvisframe;
- }
- if (Frames[Framemod].Locked)
- break;
- if (curdragbar != -1 && curdragbar != pos)
- break;
-
- if (lbypos == -1) {
- curdragbar = pos;
- lbypos = ypos;
- oldframeheight = Frames[Framemod].height;
- SetCapture(hwnd);
- break;
- }
- newh = oldframeheight + direction * (ypos - lbypos);
- if (newh > 0) {
- prevold = Frames[Framemod].height;
- Frames[Framemod].height = newh;
- if (!CLUIFramesFitInSize()) {
- Frames[Framemod].height = prevold;
- return TRUE;
- }
- Frames[Framemod].height = newh;
- if (newh > 3) Frames[Framemod].collapsed = TRUE;
-
- }
- Frames[pos].TitleBar.oldpos = pt;
- }
- lck.unlock();
-
- if (newh > 0)
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- break;
- }
- curdragbar = -1;
- lbypos = -1;
- oldframeheight = -1;
- ReleaseCapture();
- break;
-
- case WM_NCPAINT:
- if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) {
- HDC hdc = GetWindowDC(hwnd);
- HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames));
- RECT rcWindow, rc;
- HBRUSH brold;
-
- CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
- GetWindowRect(hwnd, &rcWindow);
- rc.left = rc.top = 0;
- rc.right = rcWindow.right - rcWindow.left;
- rc.bottom = rcWindow.bottom - rcWindow.top;
- brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)));
- Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
- SelectObject(hdc, hPenOld);
- SelectObject(hdc, brold);
- ReleaseDC(hwnd, hdc);
- return 0;
- }
- break;
-
- case WM_PRINT:
- case WM_PRINTCLIENT:
- GetClientRect(hwnd, &rect);
- DrawTitleBar((HDC)wParam, rect, Frameid);
-
- case WM_PAINT:
- {
- PAINTSTRUCT paintStruct;
- HDC paintDC = BeginPaint(hwnd, &paintStruct);
- rect = paintStruct.rcPaint;
- DrawTitleBar(paintDC, rect, Frameid);
- EndPaint(hwnd, &paintStruct);
- }
- return 0;
-
- default:
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
- return TRUE;
-}
-
-int CLUIFrameResizeFloatingFrame(int framepos)
-{
- if (!Frames[framepos].floating)
- return 0;
- if (Frames[framepos].ContainerWnd == nullptr)
- return 0;
-
- RECT rect;
- GetClientRect(Frames[framepos].ContainerWnd, &rect);
-
- int width = rect.right - rect.left;
- int height = rect.bottom - rect.top;
- int floatingHeight = cfg::dat.titleBarHeight;
-
- if (floatingHeight <= 0 || floatingHeight > 50)
- floatingHeight = 18;
-
- Frames[framepos].visible ? ShowWindow(Frames[framepos].ContainerWnd, SW_SHOWNOACTIVATE) : ShowWindow(Frames[framepos].ContainerWnd, SW_HIDE);
-
- if (Frames[framepos].TitleBar.ShowTitleBar) {
- ShowWindow(Frames[framepos].TitleBar.hwnd, SW_SHOWNOACTIVATE);
- Frames[framepos].height = height - floatingHeight;
- SetWindowPos(Frames[framepos].TitleBar.hwnd, HWND_TOP, 0, 0, width, floatingHeight, SWP_SHOWWINDOW | SWP_DRAWFRAME | SWP_NOACTIVATE);
- InvalidateRect(Frames[framepos].TitleBar.hwnd, nullptr, FALSE);
- SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, floatingHeight, width, height - floatingHeight, SWP_SHOWWINDOW | SWP_NOACTIVATE);
-
- }
- else {
- Frames[framepos].height = height;
- ShowWindow(Frames[framepos].TitleBar.hwnd, SW_HIDE);
- SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, 0, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
- }
-
- if (Frames[framepos].ContainerWnd != nullptr)
- UpdateWindow(Frames[framepos].ContainerWnd);
- GetWindowRect(Frames[framepos].hWnd, &Frames[framepos].wndSize);
-
- if (Frames[framepos].TitleBar.ShowTitleBar)
- RedrawWindow(Frames[framepos].TitleBar.hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
-
- RedrawWindow(Frames[framepos].hWnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
- return 0;
-}
-
-static int CLUIFrameOnMainMenuBuild(WPARAM, LPARAM)
-{
- CLUIFramesLoadMainMenu();
- return 0;
-}
-
-LRESULT CALLBACK CLUIFrameContainerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- int framepos;
- RECT rect;
- INT_PTR Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA);
-
- switch (msg) {
- case WM_CREATE:
- {
- mir_cslockfull lck(csFrameHook);
- framepos = id2pos(Frameid);
- }
- return 0;
-
- case WM_GETMINMAXINFO:
- TitleBarH = cfg::dat.titleBarHeight;
- {
- mir_cslock lck(csFrameHook);
- framepos = id2pos(Frameid);
- if (framepos < 0 || framepos >= nFramescount)
- break;
-
- if (!Frames[framepos].minmaxenabled)
- break;
-
- if (Frames[framepos].ContainerWnd == nullptr)
- break;
-
- if (Frames[framepos].Locked) {
- RECT rct;
- GetWindowRect(hwnd, &rct);
- ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = rct.right - rct.left;
- ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = rct.bottom - rct.top;
- ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = rct.right - rct.left;
- ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = rct.bottom - rct.top;
- }
-
- MINMAXINFO minmax;
- memset(&minmax, 0, sizeof(minmax));
- if (SendMessage(Frames[framepos].hWnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax) != 0)
- return DefWindowProc(hwnd, msg, wParam, lParam);
-
- RECT border;
- int tbh = TitleBarH * btoint(Frames[framepos].TitleBar.ShowTitleBar);
- GetBorderSize(hwnd, &border);
- if (minmax.ptMaxTrackSize.x != 0 && minmax.ptMaxTrackSize.y != 0) {
- ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = minmax.ptMinTrackSize.x;
- ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = minmax.ptMinTrackSize.y;
- ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = minmax.ptMaxTrackSize.x + border.left + border.right;
- ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = minmax.ptMaxTrackSize.y + tbh + border.top + border.bottom;
- }
- }
-
- case WM_MOVE:
- {
- mir_cslock lck(csFrameHook);
- framepos = id2pos(Frameid);
- if (framepos < 0 || framepos >= nFramescount)
- break;
-
- if (Frames[framepos].ContainerWnd == nullptr)
- return 0;
-
- GetWindowRect(Frames[framepos].ContainerWnd, &rect);
- Frames[framepos].FloatingPos.x = rect.left;
- Frames[framepos].FloatingPos.y = rect.top;
- Frames[framepos].FloatingSize.x = rect.right - rect.left;
- Frames[framepos].FloatingSize.y = rect.bottom - rect.top;
- CLUIFramesStoreFrameSettings(framepos);
- }
- return 0;
-
- case WM_SIZE:
- {
- mir_cslock lck(csFrameHook);
- framepos = id2pos(Frameid);
- if (framepos < 0 || framepos >= nFramescount)
- break;
-
- if (Frames[framepos].ContainerWnd == nullptr)
- return 0;
-
- CLUIFrameResizeFloatingFrame(framepos);
-
- GetWindowRect(Frames[framepos].ContainerWnd, &rect);
- Frames[framepos].FloatingPos.x = rect.left;
- Frames[framepos].FloatingPos.y = rect.top;
- Frames[framepos].FloatingSize.x = rect.right - rect.left;
- Frames[framepos].FloatingSize.y = rect.bottom - rect.top;
-
- CLUIFramesStoreFrameSettings(framepos);
- }
- return 0;
-
- case WM_CLOSE:
- DestroyWindow(hwnd);
- break;
-
- case WM_DESTROY:
- return 0;
- }
- return DefWindowProc(hwnd, msg, wParam, lParam);
-}
-
-static HWND CreateContainerWindow(HWND parent, int x, int y, int width, int height)
-{
- return(CreateWindowA("FramesContainer", "aaaa", WS_POPUP | WS_THICKFRAME, x, y, width, height, parent, nullptr, g_plugin.getInst(), nullptr));
-}
-
-INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam)
-{
- HWND hwndtmp, hwndtooltiptmp;
- {
- mir_cslock lck(csFrameHook);
- wParam = id2pos(wParam);
- if ((int)wParam >= 0 && (int)wParam < nFramescount) {
- if (Frames[wParam].floating) {
- SetParent(Frames[wParam].hWnd, g_clistApi.hwndContactList);
- SetParent(Frames[wParam].TitleBar.hwnd, g_clistApi.hwndContactList);
- Frames[wParam].floating = FALSE;
- DestroyWindow(Frames[wParam].ContainerWnd);
- Frames[wParam].ContainerWnd = nullptr;
- }
- else {
- RECT recttb, rectw, border;
- int temp;
- int neww, newh;
-
- Frames[wParam].oldstyles = GetWindowLongPtr(Frames[wParam].hWnd, GWL_STYLE);
- Frames[wParam].TitleBar.oldstyles = GetWindowLongPtr(Frames[wParam].TitleBar.hwnd, GWL_STYLE);
- bool locked = Frames[wParam].Locked;
- Frames[wParam].Locked = FALSE;
- Frames[wParam].minmaxenabled = FALSE;
-
- GetWindowRect(Frames[wParam].hWnd, &rectw);
- GetWindowRect(Frames[wParam].TitleBar.hwnd, &recttb);
- if (!Frames[wParam].TitleBar.ShowTitleBar)
- recttb.top = recttb.bottom = recttb.left = recttb.right = 0;
-
- Frames[wParam].ContainerWnd = CreateContainerWindow(g_clistApi.hwndContactList, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, 10, 10);
-
- SetParent(Frames[wParam].hWnd, Frames[wParam].ContainerWnd);
- SetParent(Frames[wParam].TitleBar.hwnd, Frames[wParam].ContainerWnd);
-
- GetBorderSize(Frames[wParam].ContainerWnd, &border);
-
- SetWindowLongPtr(Frames[wParam].ContainerWnd, GWLP_USERDATA, Frames[wParam].id);
- if ((lParam == 1)) {
- if ((Frames[wParam].FloatingPos.x != 0) && (Frames[wParam].FloatingPos.y != 0)) {
- if (Frames[wParam].FloatingPos.x < 20)
- Frames[wParam].FloatingPos.x = 40;
-
- if (Frames[wParam].FloatingPos.y < 20)
- Frames[wParam].FloatingPos.y = 40;
-
- SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, Frames[wParam].FloatingSize.x, Frames[wParam].FloatingSize.y, SWP_HIDEWINDOW);
- }
- else SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, 120, 120, 140, 140, SWP_HIDEWINDOW);
- }
- else {
- neww = rectw.right - rectw.left + border.left + border.right;
- newh = (rectw.bottom - rectw.top) + (recttb.bottom - recttb.top) + border.top + border.bottom;
- if (neww < 20)
- neww = 40;
-
- if (newh < 20)
- newh = 40;
-
- if (Frames[wParam].FloatingPos.x < 20)
- Frames[wParam].FloatingPos.x = 40;
-
- if (Frames[wParam].FloatingPos.y < 20)
- Frames[wParam].FloatingPos.y = 40;
-
- SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, neww, newh, SWP_HIDEWINDOW);
- }
- SetWindowText(Frames[wParam].ContainerWnd, Frames[wParam].TitleBar.tbname);
- temp = GetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE);
- temp |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
- SetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE, temp);
- Frames[wParam].floating = TRUE;
- Frames[wParam].Locked = locked;
- }
- }
-
- CLUIFramesStoreFrameSettings(wParam);
- Frames[wParam].minmaxenabled = TRUE;
- hwndtooltiptmp = Frames[wParam].TitleBar.hwndTip;
-
- hwndtmp = Frames[wParam].ContainerWnd;
- }
-
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- SendMessage(hwndtmp, WM_SIZE, 0, 0);
- SetWindowPos(hwndtooltiptmp, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- return 0;
-}
-
-wchar_t g_ptszEventName[100];
-
-static int CLUIFrameOnModulesLoad(WPARAM, LPARAM)
-{
- mir_snwprintf(g_ptszEventName, L"mf_update_evt_%d", GetCurrentThreadId());
- g_hEventThread = CreateEvent(nullptr, TRUE, FALSE, g_ptszEventName);
- hThreadMFUpdate = mir_forkthread(MF_UpdateThread);
- SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_IDLE);
- CLUIFramesLoadMainMenu();
- CLUIFramesCreateMenuForFrame(-1, nullptr, 000010000, false);
- return 0;
-}
-
-static int CLUIFrameLangChanged(WPARAM, LPARAM)
-{
- ApplyViewMode(0);
- g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
- return 0;
-}
-
-static int CLUIFrameOnModulesUnload(WPARAM, LPARAM)
-{
- mf_updatethread_running = FALSE;
-
- SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_NORMAL);
- SetEvent(g_hEventThread);
- WaitForSingleObject(hThreadMFUpdate, 2000);
- CloseHandle(g_hEventThread);
-
- Menu_RemoveItem(cont.MIVisible);
- Menu_RemoveItem(cont.MITitle);
- Menu_RemoveItem(cont.MITBVisible);
- Menu_RemoveItem(cont.MILock);
- Menu_RemoveItem(cont.MIColl);
- Menu_RemoveItem(cont.MIFloating);
- Menu_RemoveItem(cont.MIAlignRoot);
- Menu_RemoveItem(cont.MIAlignTop);
- Menu_RemoveItem(cont.MIAlignClient);
- Menu_RemoveItem(cont.MIAlignBottom);
- Menu_RemoveItem(cont.MIBorder);
- return 0;
-}
-
-/*
- * wparam=hIcon
- * return hImage on success,-1 on failure
- */
-
-int LoadCLUIFramesModule(void)
-{
- GapBetweenFrames = cfg::dat.gapBetweenFrames;
-
- nFramescount = 0;
-
- WNDCLASS wndclass = {};
- wndclass.style = CS_DBLCLKS;
- wndclass.lpfnWndProc = CLUIFrameTitleBarProc;
- wndclass.hInstance = g_plugin.getInst();
- wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wndclass.lpszClassName = CLUIFrameTitleBarClassName;
- RegisterClass(&wndclass);
-
- WNDCLASS cntclass = {};
- cntclass.style = CS_DBLCLKS | CS_DROPSHADOW;
- cntclass.lpfnWndProc = CLUIFrameContainerWndProc;
- cntclass.hInstance = g_plugin.getInst();
- cntclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
- cntclass.lpszClassName = L"FramesContainer";
- RegisterClass(&cntclass);
-
- // create root menu
- CMenuItem mi(&g_plugin);
- SET_UID(mi, 0x3931AC4, 0x7A32, 0x4D9C, 0x99, 0x92, 0x94, 0xD4, 0xB5, 0x9B, 0xD6, 0xB6);
- mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FRAME);
- mi.position = 3000090000;
- mi.name.a = LPGEN("Frames");
- mi.pszService = nullptr;
- cont.MainMenuItem = Menu_AddMainMenuItem(&mi);
- UNSET_UID(mi);
-
- mi.root = cont.MainMenuItem;
- mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
- mi.flags = CMIF_UNMOVABLE;
-
- // create "show all frames" menu
- mi.uid.d[7]++;
- mi.position = 4000090000;
- mi.name.a = LPGEN("Show all frames");
- mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMES;
- Menu_AddMainMenuItem(&mi);
-
- // create "show all titlebars" menu
- mi.uid.d[7]++;
- mi.position++;
- mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP);
- mi.name.a = LPGEN("Show all title bars");
- mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMESTB;
- Menu_AddMainMenuItem(&mi);
-
- // create "hide all titlebars" menu
- mi.uid.d[7]++;
- mi.position++;
- mi.name.a = LPGEN("Hide all title bars");
- mi.pszService = MS_CLIST_FRAMES_HIDEALLFRAMESTB;
- Menu_AddMainMenuItem(&mi);
-
- HookEvent(ME_SYSTEM_MODULESLOADED, CLUIFrameOnModulesLoad);
- HookEvent(ME_CLIST_PREBUILDFRAMEMENU, CLUIFramesModifyContextMenuForFrame);
- HookEvent(ME_CLIST_PREBUILDMAINMENU, CLUIFrameOnMainMenuBuild);
- HookEvent(ME_SYSTEM_PRESHUTDOWN, CLUIFrameOnModulesUnload);
- HookEvent(ME_LANGPACK_CHANGED, CLUIFrameLangChanged);
-
- CreateServiceFunction(MS_CLIST_FRAMES_ADDFRAME, CLUIFramesAddFrame);
- CreateServiceFunction(MS_CLIST_FRAMES_REMOVEFRAME, CLUIFramesRemoveFrame);
-
- CreateServiceFunction(MS_CLIST_FRAMES_SETFRAMEOPTIONS, CLUIFramesSetFrameOptions);
- CreateServiceFunction(MS_CLIST_FRAMES_GETFRAMEOPTIONS, CLUIFramesGetFrameOptions);
- CreateServiceFunction(MS_CLIST_FRAMES_UPDATEFRAME, CLUIFramesUpdateFrame);
-
- CreateServiceFunction(MS_CLIST_FRAMES_SHFRAMETITLEBAR, CLUIFramesShowHideFrameTitleBar);
- CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMESTB, CLUIFramesShowAllTitleBars);
- CreateServiceFunction(MS_CLIST_FRAMES_HIDEALLFRAMESTB, CLUIFramesHideAllTitleBars);
- CreateServiceFunction(MS_CLIST_FRAMES_SHFRAME, CLUIFramesShowHideFrame);
- CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMES, CLUIFramesShowAll);
-
- CreateServiceFunction(MS_CLIST_FRAMES_ULFRAME, CLUIFramesLockUnlockFrame);
- CreateServiceFunction(MS_CLIST_FRAMES_UCOLLFRAME, CLUIFramesCollapseUnCollapseFrame);
- CreateServiceFunction(MS_CLIST_FRAMES_SETUNBORDER, CLUIFramesSetUnSetBorder);
- CreateServiceFunction(MS_CLIST_FRAMES_SETSKINNED, CLUIFramesSetUnSetSkinned);
-
- CreateServiceFunction(CLUIFRAMESSETALIGN, CLUIFramesSetAlign);
- CreateServiceFunction(CLUIFRAMESMOVEDOWN, CLUIFramesMoveDown);
- CreateServiceFunction(CLUIFRAMESMOVEUP, CLUIFramesMoveUp);
-
- CreateServiceFunction(CLUIFRAMESSETALIGNALTOP, CLUIFramesSetAlignalTop);
- CreateServiceFunction(CLUIFRAMESSETALIGNALCLIENT, CLUIFramesSetAlignalClient);
- CreateServiceFunction(CLUIFRAMESSETALIGNALBOTTOM, CLUIFramesSetAlignalBottom);
-
- CreateServiceFunction("Set_Floating", CLUIFrameSetFloat);
- hWndExplorerToolBar = FindWindowExA(nullptr, nullptr, "Shell_TrayWnd", nullptr);
- OnFrameTitleBarBackgroundChange();
-
- FramesSysNotStarted = FALSE;
- g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, db_get_dw(0, "CLUI", "clr_frameborder", GetSysColor(COLOR_3DDKSHADOW)));
- return 0;
-}
-
-void LoadExtraIconModule()
-{
- hStatusBarShowToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_SHOW_TOOLTIP);
- hStatusBarHideToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_HIDE_TOOLTIP);
-}
-
-int UnLoadCLUIFramesModule(void)
-{
- CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
- CLUIFramesStoreAllFrames();
- DeleteObject(g_hPenCLUIFrames);
-
- mir_cslock lck(csFrameHook);
- FramesSysNotStarted = TRUE;
- for (int i = 0; i < nFramescount; i++) {
- FRAMEWND &F = Frames[i];
- DestroyWindow(F.hWnd);
- F.hWnd = (HWND)-1;
- DestroyWindow(F.TitleBar.hwnd);
- F.TitleBar.hwnd = (HWND)-1;
- DestroyWindow(F.ContainerWnd);
- F.ContainerWnd = (HWND)-1;
- DestroyMenu(F.TitleBar.hmenu);
-
- if (F.name != nullptr)
- mir_free(F.name);
- if (F.TitleBar.tbname != nullptr)
- mir_free(F.TitleBar.tbname);
- }
- free(Frames);
- Frames = nullptr;
- nFramescount = 0;
- UnregisterClass(CLUIFrameTitleBarClassName, g_plugin.getInst());
- 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-03 Miranda ICQ/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 "cluiframes.h"
+HFONT __fastcall ChangeToFont(HDC hdc, struct ClcData *dat, int id, int *fontHeight);
+
+extern HWND g_hwndViewModeFrame, g_hwndEventArea;
+extern int mf_updatethread_running;
+
+extern HANDLE hThreadMFUpdate;
+
+void MF_UpdateThread(LPVOID);
+
+HANDLE hStatusBarShowToolTipEvent, hStatusBarHideToolTipEvent;
+HANDLE g_hEventThread = nullptr;
+
+LOGFONT TitleBarLogFont = { 0 };
+
+// we use dynamic frame list,
+// but who wants so huge number of frames ??
+#define MAX_FRAMES 40
+
+#define UNCOLLAPSED_FRAME_SIZE 0
+
+// legacy menu support
+#define frame_menu_lock 1
+#define frame_menu_visible 2
+#define frame_menu_showtitlebar 3
+#define frame_menu_floating 4
+#define frame_menu_skinned 5
+
+static int UpdateTBToolTip(int framepos);
+INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam);
+int CLUIFrameResizeFloatingFrame(int framepos);
+static int CLUIFramesReSort();
+
+boolean FramesSysNotStarted = TRUE;
+HPEN g_hPenCLUIFrames = nullptr;
+
+static SortData g_sd[MAX_FRAMES];
+
+static HHOOK g_hFrameHook = nullptr;
+
+static int sortfunc(const void *a, const void *b)
+{
+ SortData *sd1, *sd2;
+ sd1 = (SortData *)a;
+ sd2 = (SortData *)b;
+ if (sd1->order > sd2->order)
+ return 1;
+ if (sd1->order < sd2->order)
+ return -1;
+ return 0;
+}
+
+static FRAMEWND *Frames = nullptr;
+
+FRAMEWND *wndFrameCLC = nullptr, *wndFrameEventArea = nullptr, *wndFrameViewMode = nullptr;
+
+static int nFramescount = 0;
+static int alclientFrame = -1;//for fast access to frame with alclient properties
+static int NextFrameId = 100;
+
+static int TitleBarH = DEFAULT_TITLEBAR_HEIGHT;
+static boolean resizing = FALSE;
+
+// menus
+static FrameMenuHandles cont;
+static LIST<TMO_IntMenuItem> g_frameMenus(10);
+
+// others
+static int ContactListHeight;
+static int LastStoreTick = 0;
+
+static int lbypos = -1;
+static int oldframeheight = -1;
+static int curdragbar = -1;
+static mir_cs csFrameHook;
+
+static bool CLUIFramesFitInSize(void);
+HWND hWndExplorerToolBar;
+static int GapBetweenFrames = 1;
+
+static int RemoveItemFromList(int pos, FRAMEWND **lpFrames, int *FrameItemCount)
+{
+ memcpy(&((*lpFrames)[pos]), &((*lpFrames)[pos + 1]), sizeof(FRAMEWND) * (*FrameItemCount - pos - 1));
+ (*FrameItemCount)--;
+ return 0;
+}
+
+static int id2pos(int id)
+{
+ int i;
+
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (i = 0; i < nFramescount; i++) {
+ if (Frames[i].id == id)
+ return i;
+ }
+ return -1;
+}
+
+int __forceinline btoint(bool b)
+{
+ return (b ? 1 : 0);
+}
+
+static FRAMEWND* FindFrameByWnd(HWND hwnd)
+{
+ if (hwnd == nullptr)
+ return nullptr;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.floating && F.ContainerWnd == hwnd)
+ return &F;
+ }
+
+ return nullptr;
+}
+
+static void DockThumbs(FRAMEWND *pThumbLeft, FRAMEWND *pThumbRight, BOOL)
+{
+ if ((pThumbRight->dockOpt.hwndLeft == nullptr) && (pThumbLeft->dockOpt.hwndRight == nullptr)) {
+ pThumbRight->dockOpt.hwndLeft = pThumbLeft->ContainerWnd;
+ pThumbLeft->dockOpt.hwndRight = pThumbRight->ContainerWnd;
+ }
+}
+
+static void UndockThumbs(FRAMEWND *pThumb1, FRAMEWND *pThumb2)
+{
+ if ((pThumb1 == nullptr) || (pThumb2 == nullptr))
+ return;
+
+ if (pThumb1->dockOpt.hwndRight == pThumb2->ContainerWnd)
+ pThumb1->dockOpt.hwndRight = nullptr;
+
+ if (pThumb1->dockOpt.hwndLeft == pThumb2->ContainerWnd)
+ pThumb1->dockOpt.hwndLeft = nullptr;
+
+ if (pThumb2->dockOpt.hwndRight == pThumb1->ContainerWnd)
+ pThumb2->dockOpt.hwndRight = nullptr;
+
+ if (pThumb2->dockOpt.hwndLeft == pThumb1->ContainerWnd)
+ pThumb2->dockOpt.hwndLeft = nullptr;
+}
+
+BOOLEAN bMoveTogether;
+
+static void PositionThumb(FRAMEWND *pThumb, short nX, short nY)
+{
+ FRAMEWND *pCurThumb = &Frames[0];
+ FRAMEWND *pDockThumb = pThumb;
+ FRAMEWND fakeMainWindow;
+ FRAMEWND fakeTaskBarWindow;
+ RECT rc;
+ RECT rcThumb;
+ RECT rcOld;
+ SIZE sizeScreen;
+ int nOffs = 10;
+ POINT pt;
+ RECT rcLeft;
+ RECT rcTop;
+ RECT rcRight;
+ RECT rcBottom;
+ int frmidx = 0;
+
+ if (pThumb == nullptr)
+ return;
+
+ sizeScreen.cx = GetSystemMetrics(SM_CXSCREEN);
+ sizeScreen.cy = GetSystemMetrics(SM_CYSCREEN);
+
+ // Get thumb dimnsions
+ GetWindowRect(pThumb->ContainerWnd, &rcThumb);
+ int nWidth = rcThumb.right - rcThumb.left;
+ int nHeight = rcThumb.bottom - rcThumb.top;
+
+ // Docking to the edges of the screen
+ int nNewX = nX < nOffs ? 0 : nX;
+ nNewX = nNewX >(sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX;
+ int nNewY = nY < nOffs ? 0 : nY;
+ nNewY = nNewY >(sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY;
+
+ bool bLeading = pThumb->dockOpt.hwndRight != nullptr;
+
+ if (bMoveTogether) {
+ UndockThumbs(pThumb, FindFrameByWnd(pThumb->dockOpt.hwndLeft));
+ GetWindowRect(pThumb->ContainerWnd, &rcOld);
+ }
+
+ memset(&fakeMainWindow, 0, sizeof(fakeMainWindow));
+ fakeMainWindow.ContainerWnd = g_clistApi.hwndContactList;
+ fakeMainWindow.floating = TRUE;
+
+ memset(&fakeTaskBarWindow, 0, sizeof(fakeTaskBarWindow));
+ fakeTaskBarWindow.ContainerWnd = hWndExplorerToolBar;
+ fakeTaskBarWindow.floating = TRUE;
+
+ while (pCurThumb != nullptr) {
+ if (pCurThumb->floating) {
+
+ if (pCurThumb != pThumb) {
+ GetWindowRect(pThumb->ContainerWnd, &rcThumb);
+ OffsetRect(&rcThumb, nX - rcThumb.left, nY - rcThumb.top);
+
+ GetWindowRect(pCurThumb->ContainerWnd, &rc);
+
+ rcLeft.left = rc.left - nOffs;
+ rcLeft.top = rc.top - nOffs;
+ rcLeft.right = rc.left + nOffs;
+ rcLeft.bottom = rc.bottom + nOffs;
+
+ rcTop.left = rc.left - nOffs;
+ rcTop.top = rc.top - nOffs;
+ rcTop.right = rc.right + nOffs;
+ rcTop.bottom = rc.top + nOffs;
+
+ rcRight.left = rc.right - nOffs;
+ rcRight.top = rc.top - nOffs;
+ rcRight.right = rc.right + nOffs;
+ rcRight.bottom = rc.bottom + nOffs;
+
+ rcBottom.left = rc.left - nOffs;
+ rcBottom.top = rc.bottom - nOffs;
+ rcBottom.right = rc.right + nOffs;
+ rcBottom.bottom = rc.bottom + nOffs;
+
+ bool bDockedLeft = false, bDockedRight = false, bDocked = false;
+
+ // Upper-left
+ pt.x = rcThumb.left;
+ pt.y = rcThumb.top;
+
+ if (PtInRect(&rcRight, pt)) {
+ nNewX = rc.right;
+ bDocked = true;
+ }
+
+ if (PtInRect(&rcBottom, pt)) {
+ nNewY = rc.bottom;
+ if (PtInRect(&rcLeft, pt))
+ nNewX = rc.left;
+ }
+
+ if (PtInRect(&rcTop, pt)) {
+ nNewY = rc.top;
+ bDockedLeft = bDocked;
+ }
+
+ // Upper-right
+ pt.x = rcThumb.right;
+ pt.y = rcThumb.top;
+ bDocked = false;
+
+ if (!bLeading && PtInRect(&rcLeft, pt)) {
+ if (!bDockedLeft) {
+ nNewX = rc.left - nWidth;
+ bDocked = true;
+ }
+ else if (rc.right == rcThumb.left)
+ bDocked = true;
+ }
+
+
+ if (PtInRect(&rcBottom, pt)) {
+ nNewY = rc.bottom;
+ if (PtInRect(&rcRight, pt))
+ nNewX = rc.right - nWidth;
+ }
+
+ if (!bLeading && PtInRect(&rcTop, pt)) {
+ nNewY = rc.top;
+ bDockedRight = bDocked;
+ }
+
+ if (bMoveTogether) {
+ if (bDockedRight)
+ DockThumbs(pThumb, pCurThumb, TRUE);
+
+ if (bDockedLeft)
+ DockThumbs(pCurThumb, pThumb, FALSE);
+ }
+
+ // Lower-left
+ pt.x = rcThumb.left;
+ pt.y = rcThumb.bottom;
+
+ if (PtInRect(&rcRight, pt))
+ nNewX = rc.right;
+
+ if (PtInRect(&rcTop, pt)) {
+ nNewY = rc.top - nHeight;
+
+ if (PtInRect(&rcLeft, pt))
+ nNewX = rc.left;
+ }
+
+
+ // Lower-right
+ pt.x = rcThumb.right;
+ pt.y = rcThumb.bottom;
+
+ if (!bLeading && PtInRect(&rcLeft, pt))
+ nNewX = rc.left - nWidth;
+
+ if (!bLeading && PtInRect(&rcTop, pt)) {
+ nNewY = rc.top - nHeight;
+
+ if (PtInRect(&rcRight, pt))
+ nNewX = rc.right - nWidth;
+ }
+ }
+ }
+
+ frmidx++;
+ if (pCurThumb->ContainerWnd == fakeTaskBarWindow.ContainerWnd)
+ break;
+
+ if (pCurThumb->ContainerWnd == fakeMainWindow.ContainerWnd) {
+ pCurThumb = &fakeTaskBarWindow;
+ continue;
+ }
+ if (frmidx == nFramescount) {
+ pCurThumb = &fakeMainWindow;
+ continue;
+ }
+ pCurThumb = &Frames[frmidx];
+ }
+
+ // Adjust coords once again
+ nNewX = nNewX < nOffs ? 0 : nNewX;
+ nNewX = nNewX > (sizeScreen.cx - nWidth - nOffs) ? (sizeScreen.cx - nWidth) : nNewX;
+ nNewY = nNewY < nOffs ? 0 : nNewY;
+ nNewY = nNewY > (sizeScreen.cy - nHeight - nOffs) ? (sizeScreen.cy - nHeight) : nNewY;
+ SetWindowPos(pThumb->ContainerWnd, nullptr, nNewX, nNewY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+
+ // OK, move all docked thumbs
+ if (bMoveTogether) {
+ pDockThumb = FindFrameByWnd(pDockThumb->dockOpt.hwndRight);
+ PositionThumb(pDockThumb, (short)(nNewX + nWidth), (short)nNewY);
+ }
+}
+
+void GetBorderSize(HWND hwnd, RECT *rect)
+{
+ RECT wr, cr;
+ POINT pt1, pt2;
+
+ GetWindowRect(hwnd, &wr);
+ GetClientRect(hwnd, &cr);
+ pt1.y = cr.top;
+ pt1.x = cr.left;
+ pt2.y = cr.bottom;
+ pt2.x = cr.right;
+
+ ClientToScreen(hwnd, &pt1);
+ ClientToScreen(hwnd, &pt2);
+
+ cr.top = pt1.y;
+ cr.left = pt1.x;
+ cr.bottom = pt2.y;
+ cr.right = pt2.x;
+
+ rect->top = cr.top - wr.top;
+ rect->left = cr.left - wr.left;
+ rect->right = wr.right - cr.right;
+ rect->bottom = wr.bottom - cr.bottom;
+}
+
+int DBLoadFrameSettingsAtPos(int pos, int Frameid)
+{
+ CMStringA buf;
+
+ Frames[Frameid].collapsed = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), Frames[Frameid].collapsed);
+
+ Frames[Frameid].Locked = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), Frames[Frameid].Locked);
+ Frames[Frameid].visible = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), Frames[Frameid].visible);
+ Frames[Frameid].TitleBar.ShowTitleBar = 0 != db_get_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), Frames[Frameid].TitleBar.ShowTitleBar);
+
+ Frames[Frameid].height = db_get_w(0, CLUIFrameModule, buf.Format("Height%d", pos), Frames[Frameid].height);
+ Frames[Frameid].HeightWhenCollapsed = db_get_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), 0);
+ Frames[Frameid].align = db_get_w(0, CLUIFrameModule, buf.Format("Align%d", pos), Frames[Frameid].align);
+
+ Frames[Frameid].FloatingPos.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatX%d", pos), 100, 0, 1024);
+ Frames[Frameid].FloatingPos.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatY%d", pos), 100, 0, 1024);
+ Frames[Frameid].FloatingSize.x = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatW%d", pos), 100, 0, 1024);
+ Frames[Frameid].FloatingSize.y = DBGetContactSettingRangedWord(0, CLUIFrameModule, buf.Format("FloatH%d", pos), 100, 0, 1024);
+
+ Frames[Frameid].floating = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), 0);
+ Frames[Frameid].order = db_get_w(0, CLUIFrameModule, buf.Format("Order%d", pos), 0);
+
+ Frames[Frameid].UseBorder = 0 != db_get_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), Frames[Frameid].UseBorder);
+ Frames[Frameid].Skinned = 0 != db_get_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned);
+ return 0;
+}
+
+int DBStoreFrameSettingsAtPos(int pos, int Frameid)
+{
+ CMStringA buf;
+
+ db_set_ws(0, CLUIFrameModule, buf.Format("Name%d", pos), Frames[Frameid].name);
+ //boolean
+ db_set_b(0, CLUIFrameModule, buf.Format("Collapse%d", pos), (uint8_t)btoint(Frames[Frameid].collapsed));
+ db_set_b(0, CLUIFrameModule, buf.Format("Locked%d", pos), (uint8_t)btoint(Frames[Frameid].Locked));
+ db_set_b(0, CLUIFrameModule, buf.Format("Visible%d", pos), (uint8_t)btoint(Frames[Frameid].visible));
+ db_set_b(0, CLUIFrameModule, buf.Format("TBVisile%d", pos), (uint8_t)btoint(Frames[Frameid].TitleBar.ShowTitleBar));
+
+ db_set_w(0, CLUIFrameModule, buf.Format("Height%d", pos), (uint16_t)Frames[Frameid].height);
+ db_set_w(0, CLUIFrameModule, buf.Format("HeightCollapsed%d", pos), (uint16_t)Frames[Frameid].HeightWhenCollapsed);
+ db_set_w(0, CLUIFrameModule, buf.Format("Align%d", pos), (uint16_t)Frames[Frameid].align);
+ //FloatingPos
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatX%d", pos), (uint16_t)Frames[Frameid].FloatingPos.x);
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatY%d", pos), (uint16_t)Frames[Frameid].FloatingPos.y);
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatW%d", pos), (uint16_t)Frames[Frameid].FloatingSize.x);
+ db_set_w(0, CLUIFrameModule, buf.Format("FloatH%d", pos), (uint16_t)Frames[Frameid].FloatingSize.y);
+
+ db_set_b(0, CLUIFrameModule, buf.Format("Floating%d", pos), (uint8_t)btoint(Frames[Frameid].floating));
+ db_set_b(0, CLUIFrameModule, buf.Format("UseBorder%d", pos), (uint8_t)btoint(Frames[Frameid].UseBorder));
+ db_set_w(0, CLUIFrameModule, buf.Format("Order%d", pos), (uint16_t)Frames[Frameid].order);
+
+ db_set_b(0, CLUIFrameModule, buf.Format("Skinned%d", pos), Frames[Frameid].Skinned);
+ return 0;
+}
+
+int LocateStorePosition(int Frameid, int maxstored)
+{
+ if (Frames[Frameid].name == nullptr) return -1;
+
+ for (int i = 0; i < maxstored; i++) {
+ char settingname[255];
+ mir_snprintf(settingname, "Name%d", i);
+ ptrW frmname(db_get_wsa(0, CLUIFrameModule, settingname));
+ if (frmname == NULL) continue;
+ if (mir_wstrcmpi(frmname, Frames[Frameid].name) == 0)
+ return i;
+ }
+ return -1;
+}
+
+int CLUIFramesLoadFrameSettings(int Frameid)
+{
+ if (FramesSysNotStarted) return -1;
+
+ if (Frameid < 0 || Frameid >= nFramescount)
+ return -1;
+
+ int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1);
+ if (maxstored == -1)
+ return 0;
+
+ int storpos = LocateStorePosition(Frameid, maxstored);
+ if (storpos == -1)
+ return 0;
+
+ DBLoadFrameSettingsAtPos(storpos, Frameid);
+ return 0;
+}
+
+int CLUIFramesStoreFrameSettings(int Frameid)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (Frameid < 0 || Frameid >= nFramescount)
+ return -1;
+
+ int maxstored = db_get_w(0, CLUIFrameModule, "StoredFrames", -1);
+ if (maxstored == -1)
+ maxstored = 0;
+
+ int storpos = LocateStorePosition(Frameid, maxstored);
+ if (storpos == -1) {
+ storpos = maxstored;
+ maxstored++;
+ }
+
+ DBStoreFrameSettingsAtPos(storpos, Frameid);
+ db_set_w(0, CLUIFrameModule, "StoredFrames", (uint16_t)maxstored);
+ return 0;
+}
+
+int CLUIFramesStoreAllFrames()
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (cfg::shutDown)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ for (int i = 0; i < nFramescount; i++)
+ CLUIFramesStoreFrameSettings(i);
+ return 0;
+}
+
+// Get client frame
+int CLUIFramesGetalClientFrame(void)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (alclientFrame != -1) {
+ /* this value could become invalid if RemoveItemFromList was called,
+ * so we double-check */
+ if (alclientFrame < nFramescount)
+ if (Frames[alclientFrame].align == alClient)
+ return alclientFrame;
+ }
+
+ for (int i = 0; i < nFramescount; i++)
+ if (Frames[i].align == alClient) {
+ alclientFrame = i;
+ return i;
+ }
+ return -1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static HGENMENU addFrameMenuItem(TMO_MenuItem *pmi, int frameid, bool bMain)
+{
+ HGENMENU res = (bMain) ? Menu_AddMainMenuItem(pmi) : Menu_AddContextFrameMenuItem(pmi);
+ if (pmi->pszService != nullptr)
+ Menu_ConfigureItem(res, MCI_OPT_EXECPARAM, frameid);
+ return res;
+}
+
+HMENU CLUIFramesCreateMenuForFrame(int frameid, HGENMENU root, int popuppos, bool bMain)
+{
+ if (FramesSysNotStarted)
+ return nullptr;
+
+ int framepos = id2pos(frameid);
+ FrameMenuHandles &fmh = (frameid == -1) ? cont : Frames[framepos].MenuHandles;
+
+ CMenuItem mi((frameid == -1) ? &g_plugin : Frames[framepos].pPlugin);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.root = root;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&FrameTitle");
+ mi.flags = CMIF_SYSTEM | CMIF_GRAYED;
+ fmh.MITitle = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.hIcolibItem = nullptr;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Visible");
+ mi.flags = CMIF_SYSTEM | CMIF_CHECKED;
+ mi.pszService = MS_CLIST_FRAMES_SHFRAME;
+ fmh.MIVisible = addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Show title bar");
+ mi.pszService = MS_CLIST_FRAMES_SHFRAMETITLEBAR;
+ fmh.MITBVisible = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Locked");
+ mi.pszService = MS_CLIST_FRAMES_ULFRAME;
+ fmh.MILock = addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Collapsed");
+ mi.pszService = MS_CLIST_FRAMES_UCOLLFRAME;
+ fmh.MIColl = addFrameMenuItem(&mi, frameid, bMain);
+
+ // floating
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Floating mode");
+ mi.flags = CMIF_SYSTEM;
+ mi.pszService = "Set_Floating";
+ fmh.MIFloating = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Border");
+ mi.flags = CMIF_SYSTEM | CMIF_CHECKED;
+ mi.pszService = MS_CLIST_FRAMES_SETUNBORDER;
+ fmh.MIBorder = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Skinned frame");
+ mi.pszService = MS_CLIST_FRAMES_SETSKINNED;
+ fmh.MISkinned = addFrameMenuItem(&mi, frameid, bMain);
+
+ popuppos += 100000;
+
+ // alignment root
+ mi.root = root;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Align");
+ mi.flags = CMIF_SYSTEM;
+ mi.pszService = nullptr;
+ fmh.MIAlignRoot = addFrameMenuItem(&mi, frameid, bMain);
+
+ // align top
+ mi.root = fmh.MIAlignRoot;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Top");
+ mi.pszService = CLUIFRAMESSETALIGNALTOP;
+ fmh.MIAlignTop = addFrameMenuItem(&mi, frameid, bMain);
+
+ // align client
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Client");
+ mi.pszService = CLUIFRAMESSETALIGNALCLIENT;
+ fmh.MIAlignClient = addFrameMenuItem(&mi, frameid, bMain);
+
+ // align bottom
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Bottom");
+ mi.pszService = CLUIFRAMESSETALIGNALBOTTOM;
+ fmh.MIAlignBottom = addFrameMenuItem(&mi, frameid, bMain);
+
+ // position root
+ mi.root = root;
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Position");
+ mi.pszService = nullptr;
+ mi.root = addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Up");
+ mi.pszService = CLUIFRAMESMOVEUP;
+ addFrameMenuItem(&mi, frameid, bMain);
+
+ mi.position = popuppos++;
+ mi.name.a = LPGEN("&Down");
+ mi.pszService = CLUIFRAMESMOVEDOWN;
+ addFrameMenuItem(&mi, frameid, bMain);
+ return nullptr;
+}
+
+static int CLUIFramesModifyContextMenuForFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos >= 0 && pos < nFramescount) {
+ FRAMEWND &p = Frames[pos];
+ Menu_ModifyItem(cont.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name);
+ Menu_SetChecked(cont.MIVisible, p.visible);
+ Menu_SetChecked(cont.MILock, p.Locked);
+ Menu_SetChecked(cont.MITBVisible, p.TitleBar.ShowTitleBar);
+ Menu_SetChecked(cont.MIFloating, p.floating);
+ Menu_SetChecked(cont.MIBorder, p.UseBorder);
+ Menu_SetChecked(cont.MISkinned, p.Skinned);
+ Menu_SetChecked(cont.MIAlignTop, (p.align & alTop) != 0);
+ Menu_SetChecked(cont.MIAlignClient, (p.align & alClient) != 0);
+ Menu_SetChecked(cont.MIAlignBottom, (p.align & alBottom) != 0);
+
+ Menu_SetChecked(cont.MIColl, !p.collapsed);
+ Menu_EnableItem(cont.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame());
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesModifyMainMenuItems(WPARAM frameId, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(frameId);
+
+ if (pos >= 0 && pos < nFramescount) {
+ FRAMEWND &p = Frames[pos];
+ Menu_ModifyItem(p.MenuHandles.MITitle, p.TitleBar.tbname ? p.TitleBar.tbname : p.name);
+
+ Menu_SetChecked(p.MenuHandles.MIVisible, p.visible);
+ Menu_SetChecked(p.MenuHandles.MILock, p.Locked);
+ Menu_SetChecked(p.MenuHandles.MITBVisible, p.TitleBar.ShowTitleBar);
+ Menu_SetChecked(p.MenuHandles.MIFloating, p.floating);
+ Menu_SetChecked(p.MenuHandles.MIBorder, p.UseBorder);
+ Menu_SetChecked(p.MenuHandles.MISkinned, p.Skinned);
+
+ Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0);
+ Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alTop) != 0);
+
+ Menu_SetChecked(p.MenuHandles.MIAlignClient, (p.align & alClient) != 0);
+
+ Menu_EnableItem(p.MenuHandles.MIAlignTop, (p.align & alClient) == 0);
+ Menu_SetChecked(p.MenuHandles.MIAlignTop, (p.align & alBottom) != 0);
+
+ Menu_SetChecked(p.MenuHandles.MIColl, !p.collapsed);
+ Menu_EnableItem(p.MenuHandles.MIColl, p.visible && !p.Locked && pos != CLUIFramesGetalClientFrame());
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesGetFrameOptions(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(HIWORD(wParam));
+ if (pos < 0 || pos >= nFramescount)
+ return -1;
+
+ switch (LOWORD(wParam)) {
+ case FO_NAME:
+ return (INT_PTR)Frames[pos].name;
+
+ case FO_TBNAME:
+ return (INT_PTR)Frames[pos].TitleBar.tbname;
+
+ case FO_TBTIPNAME:
+ return (INT_PTR)Frames[pos].TitleBar.tooltip;
+
+ case FO_TBSTYLE:
+ return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE);
+
+ case FO_TBEXSTYLE:
+ return GetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE);
+
+ case FO_ICON:
+ return (INT_PTR)Frames[pos].TitleBar.hicon;
+
+ case FO_HEIGHT:
+ return (INT_PTR)Frames[pos].height;
+
+ case FO_ALIGN:
+ return (INT_PTR)Frames[pos].align;
+
+ case FO_FLOATING:
+ return (INT_PTR)Frames[pos].floating;
+
+ case FO_FLAGS:
+ INT_PTR dwFlags = 0;
+ if (Frames[pos].visible) dwFlags |= F_VISIBLE;
+ if (!Frames[pos].collapsed) dwFlags |= F_UNCOLLAPSED;
+ if (Frames[pos].Locked) dwFlags |= F_LOCKED;
+ if (Frames[pos].TitleBar.ShowTitleBar) dwFlags |= F_SHOWTB;
+ if (Frames[pos].TitleBar.ShowTitleBarTip) dwFlags |= F_SHOWTBTIP;
+ if (Frames[pos].Skinned) dwFlags |= F_SKINNED;
+ if (!(GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE)&WS_BORDER)) dwFlags |= F_NOBORDER;
+ return dwFlags;
+ }
+
+ return -1;
+}
+
+INT_PTR CLUIFramesSetFrameOptions(WPARAM wParam, LPARAM lParam)
+{
+ int retval; // value to be returned
+
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslockfull lck(csFrameHook);
+ int pos = id2pos(HIWORD(wParam));
+ if (pos < 0 || pos >= nFramescount)
+ return -1;
+
+ switch (LOWORD(wParam) & ~FO_UNICODETEXT) {
+ case FO_FLAGS:
+ {
+ int flag = lParam;
+ LONG_PTR style;
+
+ Frames[pos].dwFlags = flag;
+ Frames[pos].visible = FALSE;
+ if (flag & F_VISIBLE) Frames[pos].visible = TRUE;
+
+ Frames[pos].collapsed = TRUE;
+ if (flag & F_UNCOLLAPSED) Frames[pos].collapsed = FALSE;
+
+ Frames[pos].Locked = FALSE;
+ if (flag & F_LOCKED) Frames[pos].Locked = TRUE;
+
+ Frames[pos].UseBorder = TRUE;
+ if (flag & F_NOBORDER) Frames[pos].UseBorder = FALSE;
+
+ Frames[pos].TitleBar.ShowTitleBar = FALSE;
+ if (flag & F_SHOWTB) Frames[pos].TitleBar.ShowTitleBar = TRUE;
+
+ Frames[pos].TitleBar.ShowTitleBarTip = FALSE;
+ if (flag & F_SHOWTBTIP) Frames[pos].TitleBar.ShowTitleBarTip = TRUE;
+
+ SendMessage(Frames[pos].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[pos].TitleBar.ShowTitleBarTip, 0);
+
+ style = GetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE);
+ style |= WS_BORDER;
+ style |= CLS_SKINNEDFRAME;
+
+ if (flag & F_NOBORDER)
+ style &= (~WS_BORDER);
+
+ Frames[pos].Skinned = FALSE;
+ if (flag & F_SKINNED)
+ Frames[pos].Skinned = TRUE;
+
+ if (!(flag & F_SKINNED))
+ style &= ~CLS_SKINNEDFRAME;
+
+ SetWindowLongPtr(Frames[pos].hWnd, GWL_STYLE, (LONG_PTR)style);
+ SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, (LONG_PTR)style & ~(WS_VSCROLL | WS_HSCROLL));
+ lck.unlock();
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ SetWindowPos(Frames[pos].TitleBar.hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ return 0;
+
+ case FO_NAME:
+ if (lParam == 0)
+ return -1;
+
+ mir_free(Frames[pos].name);
+ Frames[pos].name = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
+ return 0;
+
+ case FO_TBNAME:
+ if (lParam == 0)
+ return -1;
+
+ mir_free(Frames[pos].TitleBar.tbname);
+ Frames[pos].TitleBar.tbname = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
+ lck.unlock();
+
+ if (Frames[pos].floating && (Frames[pos].TitleBar.tbname != nullptr))
+ SetWindowText(Frames[pos].ContainerWnd, Frames[pos].TitleBar.tbname);
+ return 0;
+
+ case FO_TBTIPNAME:
+ if (lParam == 0)
+ return -1;
+
+ mir_free(Frames[pos].TitleBar.tooltip);
+ Frames[pos].TitleBar.tooltip = (wParam & FO_UNICODETEXT) ? mir_wstrdup((LPWSTR)lParam) : mir_a2u((LPSTR)lParam);
+ UpdateTBToolTip(pos);
+ return 0;
+
+ case FO_TBSTYLE:
+ SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_STYLE, lParam);
+ return 0;
+
+ case FO_TBEXSTYLE:
+ SetWindowLongPtr(Frames[pos].TitleBar.hwnd, GWL_EXSTYLE, lParam);
+ return 0;
+
+ case FO_ICON:
+ Frames[pos].TitleBar.hicon = (HICON)lParam;
+ return 0;
+
+ case FO_HEIGHT:
+ if (lParam < 0)
+ return -1;
+
+ if (Frames[pos].Skinned) {
+ int uID = (Frames[pos].TitleBar.ShowTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE : ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE);
+ lParam += (arStatusItems[uID]->MARGIN_BOTTOM + arStatusItems[uID]->MARGIN_TOP);
+ }
+ if (Frames[pos].collapsed) {
+ int oldHeight = Frames[pos].height;
+ retval = Frames[pos].height;
+ Frames[pos].height = lParam;
+ if (!CLUIFramesFitInSize())
+ Frames[pos].height = retval;
+ retval = Frames[pos].height;
+
+ if (Frames[pos].height != oldHeight) {
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ if (Frames[pos].Skinned)
+ RedrawWindow(Frames[pos].hWnd, nullptr, nullptr, RDW_FRAME | RDW_UPDATENOW | RDW_INVALIDATE);
+ }
+ }
+ else {
+ retval = Frames[pos].HeightWhenCollapsed;
+ Frames[pos].HeightWhenCollapsed = lParam;
+ if (!CLUIFramesFitInSize())
+ Frames[pos].HeightWhenCollapsed = retval;
+ retval = Frames[pos].HeightWhenCollapsed;
+ }
+ return retval;
+
+ case FO_FLOATING:
+ if (lParam < 0)
+ return -1;
+ else {
+ int id = Frames[pos].id;
+ Frames[pos].floating = !(lParam);
+ lck.unlock();
+
+ CLUIFrameSetFloat(id, 1);//lparam=1 use stored width and height
+ }
+ return wParam;
+
+ case FO_ALIGN:
+ if (!(lParam&alTop || lParam&alBottom || lParam&alClient))
+ return -1;
+
+ if ((lParam&alClient) && (CLUIFramesGetalClientFrame() >= 0)) { //only one alClient frame possible
+ alclientFrame = -1;//recalc it
+ return -1;
+ }
+ Frames[pos].align = lParam;
+ return 0;
+ }
+ lck.unlock();
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ return -1;
+}
+
+static INT_PTR CLUIFramesShowAll(WPARAM, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (int i = 0; i < nFramescount; i++)
+ Frames[i].visible = TRUE;
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ return 0;
+}
+
+INT_PTR CLUIFramesShowAllTitleBars(WPARAM, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ F.TitleBar.ShowTitleBar = TRUE;
+ SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesHideAllTitleBars(WPARAM, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ F.TitleBar.ShowTitleBar = FALSE;
+ SetWindowPos(F.hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesShowHideFrame(WPARAM frameId, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ int pos;
+ {
+ mir_cslock lck(csFrameHook);
+ pos = id2pos(frameId);
+ if (pos >= 0 && !mir_wstrcmp(Frames[pos].name, L"My contacts"))
+ Frames[pos].visible = 1;
+ else {
+ if (pos >= 0 && (int)pos < nFramescount)
+ Frames[pos].visible = !Frames[pos].visible;
+ if (Frames[pos].floating)
+ CLUIFrameResizeFloatingFrame(pos);
+ }
+ }
+
+ if (!Frames[pos].floating)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesShowHideFrameTitleBar(WPARAM frameId, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(frameId);
+ if (pos >= 0 && (int)pos < nFramescount) {
+ Frames[pos].TitleBar.ShowTitleBar = !Frames[pos].TitleBar.ShowTitleBar;
+ SetWindowPos(Frames[pos].hWnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
+ }
+ }
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+// lparam=-1 up ,1 down
+INT_PTR CLUIFramesMoveUpDown(WPARAM frameId, LPARAM lParam)
+{
+ int i, tmpval;
+
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslockfull lck(csFrameHook);
+ int pos = id2pos(frameId);
+ if (pos < 0 || pos >= nFramescount)
+ return 0;
+
+ int curalign = Frames[pos].align;
+ int v = 0;
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.floating || (!F.visible) || (F.align != curalign))
+ continue;
+ g_sd[v].order = F.order;
+ g_sd[v].realpos = i;
+ v++;
+ }
+ if (v == 0)
+ return 0;
+
+ qsort(g_sd, v, sizeof(SortData), sortfunc);
+ for (i = 0; i < v; i++) {
+ if (g_sd[i].realpos == pos) {
+ if (lParam == -1) {
+ if (i < 1) break;
+ tmpval = Frames[g_sd[i - 1].realpos].order;
+ Frames[g_sd[i - 1].realpos].order = Frames[pos].order;
+ Frames[pos].order = tmpval;
+ break;
+ }
+ if (lParam == 1) {
+ if (i > v - 1) break;
+ tmpval = Frames[g_sd[i + 1].realpos].order;
+ Frames[g_sd[i + 1].realpos].order = Frames[pos].order;
+ Frames[pos].order = tmpval;
+ break;
+ }
+ }
+ }
+ lck.unlock();
+
+ CLUIFramesReSort();
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ PostMessage(g_clistApi.hwndContactList, CLUIINTM_REDRAW, 0, 0);
+ return 0;
+}
+
+static INT_PTR CLUIFramesMoveUp(WPARAM frameId, LPARAM)
+{
+ return CLUIFramesMoveUpDown(frameId, -1);
+}
+
+static INT_PTR CLUIFramesMoveDown(WPARAM frameId, LPARAM)
+{
+ return CLUIFramesMoveUpDown(frameId, 1);
+}
+
+//lparam=alignment
+INT_PTR CLUIFramesSetAlign(WPARAM frameId, LPARAM lParam)
+{
+ if (FramesSysNotStarted) return -1;
+
+ CLUIFramesSetFrameOptions(MAKEWPARAM(FO_ALIGN, frameId), lParam);
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+}
+
+INT_PTR CLUIFramesSetAlignalTop(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ return CLUIFramesSetAlign(wParam, alTop);
+}
+
+INT_PTR CLUIFramesSetAlignalBottom(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ return CLUIFramesSetAlign(wParam, alBottom);
+}
+
+INT_PTR CLUIFramesSetAlignalClient(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted) return -1;
+
+ return CLUIFramesSetAlign(wParam, alClient);
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesLockUnlockFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos >= 0 && (int)pos < nFramescount) {
+ Frames[pos].Locked = !Frames[pos].Locked;
+ CLUIFramesStoreFrameSettings(pos);
+ }
+ return 0;
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesSetUnSetBorder(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ HWND hw;
+ int FrameId, oldflags;
+ {
+ mir_cslock lck(csFrameHook);
+ FrameId = id2pos(wParam);
+ if (FrameId == -1)
+ return -1;
+
+ oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0);
+ if (oldflags & F_NOBORDER)
+ oldflags &= (~F_NOBORDER);
+ else
+ oldflags |= F_NOBORDER;
+
+ hw = Frames[FrameId].hWnd;
+ }
+
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags);
+ SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER);
+ return 0;
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesSetUnSetSkinned(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ HWND hw;
+ int FrameId, oldflags;
+ {
+ mir_cslock lck(csFrameHook);
+ FrameId = id2pos(wParam);
+ if (FrameId == -1)
+ return -1;
+
+ oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), 0);
+ if (oldflags & F_SKINNED)
+ oldflags &= ~F_SKINNED;
+ else
+ oldflags |= F_SKINNED;
+
+ hw = Frames[FrameId].hWnd;
+ }
+
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, wParam), oldflags);
+ SetWindowPos(hw, nullptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_DRAWFRAME | SWP_NOZORDER);
+ return 0;
+}
+
+//wparam=frameid
+INT_PTR CLUIFramesCollapseUnCollapseFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ mir_cslockfull lck(csFrameHook);
+ int FrameId = id2pos(wParam);
+ if (FrameId < 0 || FrameId >= nFramescount)
+ return -1;
+
+ int oldHeight;
+
+ // do not collapse/uncollapse client/locked/invisible frames
+ if (Frames[FrameId].align == alClient && !(Frames[FrameId].Locked || (!Frames[FrameId].visible) || Frames[FrameId].floating)) {
+ RECT rc;
+ if (Clist_IsDocked())
+ return 0;
+
+ if (db_get_b(0, "CLUI", "AutoSize", 0))
+ return 0;
+
+ GetWindowRect(g_clistApi.hwndContactList, &rc);
+
+ if (Frames[FrameId].collapsed == TRUE) {
+ rc.bottom -= rc.top;
+ rc.bottom -= Frames[FrameId].height;
+ Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height;
+ Frames[FrameId].collapsed = FALSE;
+ }
+ else {
+ rc.bottom -= rc.top;
+ rc.bottom += Frames[FrameId].HeightWhenCollapsed;
+ Frames[FrameId].collapsed = TRUE;
+ }
+
+ SetWindowPos(g_clistApi.hwndContactList, nullptr, 0, 0, rc.right - rc.left, rc.bottom, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
+
+ CLUIFramesStoreAllFrames();
+ lck.unlock();
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return 0;
+
+ }
+ if (Frames[FrameId].Locked || (!Frames[FrameId].visible))
+ return 0;
+
+ oldHeight = Frames[FrameId].height;
+
+ // if collapsed, uncollapse
+ if (Frames[FrameId].collapsed == TRUE) {
+ Frames[FrameId].HeightWhenCollapsed = Frames[FrameId].height;
+ Frames[FrameId].height = UNCOLLAPSED_FRAME_SIZE;
+ Frames[FrameId].collapsed = FALSE;
+ }
+ // if uncollapsed, collapse
+ else {
+ Frames[FrameId].height = Frames[FrameId].HeightWhenCollapsed;
+ Frames[FrameId].collapsed = TRUE;
+ }
+
+ if (!Frames[FrameId].floating) {
+
+ if (!CLUIFramesFitInSize()) {
+ //cant collapse,we can resize only for height<alclient frame height
+ int alfrm = CLUIFramesGetalClientFrame();
+
+ if (alfrm != -1) {
+ Frames[FrameId].collapsed = FALSE;
+ if (Frames[alfrm].height > 2 * UNCOLLAPSED_FRAME_SIZE) {
+ oldHeight = Frames[alfrm].height - UNCOLLAPSED_FRAME_SIZE;
+ Frames[FrameId].collapsed = TRUE;
+ }
+ }
+ else {
+ int i, sumheight = 0;
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) {
+ sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2;
+ return FALSE;
+ }
+ if (sumheight > ContactListHeight - 0 - 2)
+ Frames[FrameId].height = (ContactListHeight - 0 - 2) - sumheight;
+ }
+ }
+ Frames[FrameId].height = oldHeight;
+ if (Frames[FrameId].collapsed == FALSE) {
+ if (Frames[FrameId].floating)
+ SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, Frames[FrameId].wndSize.right - Frames[FrameId].wndSize.left + 6, Frames[FrameId].height + DEFAULT_TITLEBAR_HEIGHT + 4, SWP_SHOWWINDOW | SWP_NOMOVE);
+ return -1;
+ }
+ }
+ }
+ lck.unlock();
+ if (!Frames[FrameId].floating)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ else {
+ RECT contwnd;
+ GetWindowRect(Frames[FrameId].ContainerWnd, &contwnd);
+ contwnd.top = contwnd.bottom - contwnd.top;//height
+ contwnd.left = contwnd.right - contwnd.left;//width
+
+ contwnd.top -= (oldHeight - Frames[FrameId].height);//newheight
+ SetWindowPos(Frames[FrameId].ContainerWnd, HWND_TOP, 0, 0, contwnd.left, contwnd.top, SWP_SHOWWINDOW | SWP_NOMOVE);
+ }
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ CLUIFramesStoreAllFrames();
+ return 0;
+}
+
+static int CLUIFramesLoadMainMenu()
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ for (auto &it : g_frameMenus)
+ Menu_RemoveItem(it);
+ g_frameMenus.destroy();
+
+ // create frames menu
+ CMenuItem mi(&g_plugin);
+ mi.root = cont.MainMenuItem;
+ mi.flags = CMIF_UNICODE | CMIF_SYSTEM;
+ int separator = (int)3000200000;
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ mi.hIcolibItem = F.TitleBar.hicon;
+ mi.position = separator;
+ mi.name.w = F.TitleBar.tbname ? F.TitleBar.tbname : F.name;
+ mi.pszService = nullptr;
+ g_frameMenus.insert(F.MenuHandles.MainMenuItem = Menu_AddMainMenuItem(&mi));
+ CLUIFramesCreateMenuForFrame(F.id, F.MenuHandles.MainMenuItem, separator, true);
+ CLUIFramesModifyMainMenuItems(F.id, 0);
+ CallService(MS_CLIST_FRAMEMENUNOTIFY, (WPARAM)F.id, (LPARAM)F.MenuHandles.MainMenuItem);
+ separator++;
+ }
+ return 0;
+}
+
+static HFONT CLUILoadTitleBarFont()
+{
+ char facename[] = "MS Shell Dlg";
+ LOGFONT logfont;
+ memset(&logfont, 0, sizeof(logfont));
+ memcpy(logfont.lfFaceName, facename, sizeof(facename));
+ logfont.lfWeight = FW_NORMAL;
+ logfont.lfHeight = -10;
+ return CreateFontIndirect(&logfont);
+}
+
+static int UpdateTBToolTip(int framepos)
+{
+ TOOLINFO ti;
+
+ memset(&ti, 0, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.lpszText = Frames[framepos].TitleBar.tooltip;
+ ti.hinst = g_plugin.getInst();
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)Frames[framepos].TitleBar.hwnd;
+
+ return SendMessage(Frames[framepos].TitleBar.hwndTip, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
+};
+
+int FrameNCPaint(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar)
+{
+ RECT rcWindow, rc;
+ HWND hwndParent = GetParent(hwnd);
+ LRESULT result = 0;
+
+ if (hwndParent != g_clistApi.hwndContactList || !cfg::dat.bSkinnedScrollbar)
+ result = CallWindowProc(oldWndProc, hwnd, WM_NCPAINT, wParam, lParam);
+ if (!g_clistApi.hwndContactList || hwndParent != g_clistApi.hwndContactList)
+ return result;
+
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_SKINNEDFRAME) {
+ StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr;
+ if (item == nullptr)
+ return 0;
+
+ GetWindowRect(hwnd, &rcWindow);
+ rc.left = rc.top = 0;
+ rc.right = rcWindow.right - rcWindow.left;
+ rc.bottom = rcWindow.bottom - rcWindow.top;
+
+ HDC hdc = GetWindowDC(hwnd);
+ if (hwnd == g_clistApi.hwndContactTree) {
+ HDC realDC = CreateCompatibleDC(hdc);
+ HBITMAP hbmDraw = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(SelectObject(realDC, hbmDraw));
+
+ ExcludeClipRect(realDC, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
+ BitBlt(realDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY);
+ DrawAlpha(realDC, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+
+ ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
+ BitBlt(hdc, 0, 0, rc.right, rc.bottom, realDC, 0, 0, SRCCOPY);
+ SelectObject(realDC, hbmOld);
+ DeleteObject(hbmDraw);
+ DeleteDC(realDC);
+ }
+ else {
+ ExcludeClipRect(hdc, item->MARGIN_LEFT, item->MARGIN_TOP, rc.right - item->MARGIN_RIGHT, rc.bottom - item->MARGIN_BOTTOM);
+ BitBlt(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, cfg::dat.hdcBg, rcWindow.left - cfg::dat.ptW.x, rcWindow.top - cfg::dat.ptW.y, SRCCOPY);
+ DrawAlpha(hdc, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ }
+ ReleaseDC(hwnd, hdc);
+ return 0;
+ }
+
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) {
+ HDC hdc = GetWindowDC(hwnd);
+ HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames));
+ GetWindowRect(hwnd, &rcWindow);
+ rc.left = rc.top = 0;
+ rc.right = rcWindow.right - rcWindow.left;
+ rc.bottom = rcWindow.bottom - rcWindow.top;
+ HBRUSH brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)));
+ Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
+ SelectObject(hdc, hPenOld);
+ SelectObject(hdc, brold);
+ ReleaseDC(hwnd, hdc);
+ return 0;
+ }
+
+ return result;
+}
+
+int FrameNCCalcSize(HWND hwnd, WNDPROC oldWndProc, WPARAM wParam, LPARAM lParam, BOOL hasTitleBar)
+{
+ StatusItems_t *item = (arStatusItems.getCount() != 0) ? (hasTitleBar ? arStatusItems[ID_EXTBKOWNEDFRAMEBORDERTB - ID_STATUS_OFFLINE] : arStatusItems[ID_EXTBKOWNEDFRAMEBORDER - ID_STATUS_OFFLINE]) : nullptr;
+ LRESULT orig = oldWndProc ? CallWindowProc(oldWndProc, hwnd, WM_NCCALCSIZE, wParam, lParam) : 0;
+ NCCALCSIZE_PARAMS *nccp = (NCCALCSIZE_PARAMS *)lParam;
+ uint32_t dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
+
+ if (item == nullptr)
+ return orig;
+
+ if (item->IGNORED || !(dwStyle & CLS_SKINNEDFRAME) || GetParent(hwnd) != g_clistApi.hwndContactList)
+ return orig;
+
+ nccp->rgrc[0].left += item->MARGIN_LEFT;
+ nccp->rgrc[0].right -= item->MARGIN_RIGHT;
+ nccp->rgrc[0].bottom -= item->MARGIN_BOTTOM;
+ nccp->rgrc[0].top += item->MARGIN_TOP;
+ return WVR_REDRAW;
+}
+
+static LRESULT CALLBACK FramesSubClassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int i;
+
+ WNDPROC oldWndProc = nullptr;
+ BOOL hasTitleBar = FALSE;
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.hWnd == hwnd) {
+ oldWndProc = F.wndProc;
+ hasTitleBar = F.TitleBar.ShowTitleBar;
+ }
+ }
+ switch (msg) {
+ case WM_NCPAINT:
+ return FrameNCPaint(hwnd, oldWndProc ? oldWndProc : DefWindowProc, wParam, lParam, hasTitleBar);
+
+ case WM_NCCALCSIZE:
+ return FrameNCCalcSize(hwnd, oldWndProc, wParam, lParam, hasTitleBar);
+
+ case WM_PRINTCLIENT:
+ return 0;
+ }
+
+ if (oldWndProc)
+ return CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+/*
+ * re-sort all frames and correct frame ordering
+ */
+
+static int CLUIFramesReSort()
+{
+ int v = 0, i;
+ int order = 1;
+
+ mir_cslock lck(csFrameHook);
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.align != alTop)
+ continue;
+ g_sd[v].order = F.order;
+ g_sd[v].realpos = i;
+ v++;
+ }
+ if (v > 0) {
+ qsort(g_sd, v, sizeof(SortData), sortfunc);
+ for (i = 0; i < v; i++)
+ Frames[g_sd[i].realpos].order = order++;
+ }
+
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ v = 0;
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.align != alBottom)
+ continue;
+ g_sd[v].order = F.order;
+ g_sd[v].realpos = i;
+ v++;
+ }
+ if (v > 0) {
+ qsort(g_sd, v, sizeof(SortData), sortfunc);
+ for (i = 0; i < v; i++)
+ Frames[g_sd[i].realpos].order = order++;
+ }
+ CLUIFramesStoreAllFrames();
+ return 0;
+}
+
+//wparam=(CLISTFrame*)clfrm
+INT_PTR CLUIFramesAddFrame(WPARAM wParam, LPARAM lParam)
+{
+ int style;
+ CLISTFrame *clfrm = (CLISTFrame *)wParam;
+
+ if (g_clistApi.hwndContactList == nullptr) return -1;
+ if (FramesSysNotStarted) return -1;
+ if (clfrm->cbSize != sizeof(CLISTFrame)) return -1;
+
+ mir_cslockfull lck(csFrameHook);
+ if (nFramescount >= MAX_FRAMES)
+ return -1;
+
+ if (Frames == nullptr) {
+ Frames = (FRAMEWND*)malloc(sizeof(FRAMEWND) * (MAX_FRAMES + 2));
+ memset(Frames, 0, (sizeof(FRAMEWND) * (MAX_FRAMES + 2)));
+ }
+ memset(&Frames[nFramescount], 0, sizeof(FRAMEWND));
+
+ Frames[nFramescount].id = NextFrameId++;
+ Frames[nFramescount].align = clfrm->align;
+ Frames[nFramescount].hWnd = clfrm->hWnd;
+ Frames[nFramescount].height = clfrm->height;
+ Frames[nFramescount].TitleBar.hicon = clfrm->hIcon;
+ Frames[nFramescount].floating = false;
+ Frames[nFramescount].pPlugin = (HPLUGIN)lParam;
+
+ if (clfrm->Flags & F_NO_SUBCONTAINER)
+ Frames[nFramescount].OwnerWindow = (HWND)-2;
+ else
+ Frames[nFramescount].OwnerWindow = g_clistApi.hwndContactList;
+
+ SetClassLong(clfrm->hWnd, GCL_STYLE, GetClassLong(clfrm->hWnd, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
+ SetWindowLongPtr(clfrm->hWnd, GWL_STYLE, GetWindowLongPtr(clfrm->hWnd, GWL_STYLE) | WS_CLIPCHILDREN);
+
+ if (GetCurrentThreadId() == GetWindowThreadProcessId(clfrm->hWnd, nullptr)) {
+ if (clfrm->hWnd != g_clistApi.hwndContactTree && clfrm->hWnd != g_hwndViewModeFrame && clfrm->hWnd != g_hwndEventArea) {
+ Frames[nFramescount].wndProc = (WNDPROC)GetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC);
+ SetWindowLongPtr(clfrm->hWnd, GWLP_WNDPROC, (LONG_PTR)FramesSubClassProc);
+ }
+ }
+
+ if (clfrm->hWnd == g_hwndEventArea)
+ wndFrameEventArea = &Frames[nFramescount];
+ else if (clfrm->hWnd == g_clistApi.hwndContactTree)
+ wndFrameCLC = &Frames[nFramescount];
+ else if (clfrm->hWnd == g_hwndViewModeFrame)
+ wndFrameViewMode = &Frames[nFramescount];
+
+ Frames[nFramescount].dwFlags = clfrm->Flags;
+
+ if (clfrm->szName.a == nullptr || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szName.w) : mir_strlen(clfrm->szName.a)) == 0) {
+ wchar_t ptszClassName[256];
+ GetClassName(Frames[nFramescount].hWnd, ptszClassName, _countof(ptszClassName));
+ Frames[nFramescount].name = mir_wstrdup(ptszClassName);
+ }
+ else Frames[nFramescount].name = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szName.w) : mir_a2u(clfrm->szName.a);
+
+ if (IsBadCodePtr((FARPROC)clfrm->szTBname.a) || clfrm->szTBname.a == nullptr
+ || ((clfrm->Flags & F_UNICODE) ? mir_wstrlen(clfrm->szTBname.w) : mir_strlen(clfrm->szTBname.a)) == 0)
+ Frames[nFramescount].TitleBar.tbname = mir_wstrdup(Frames[nFramescount].name);
+ else
+ Frames[nFramescount].TitleBar.tbname = (clfrm->Flags & F_UNICODE) ? mir_wstrdup(clfrm->szTBname.w) : mir_a2u(clfrm->szTBname.a);
+ Frames[nFramescount].needhide = FALSE;
+ Frames[nFramescount].TitleBar.ShowTitleBar = (clfrm->Flags & F_SHOWTB ? TRUE : FALSE);
+ Frames[nFramescount].TitleBar.ShowTitleBarTip = (clfrm->Flags & F_SHOWTBTIP ? TRUE : FALSE);
+
+ Frames[nFramescount].collapsed = clfrm->Flags & F_UNCOLLAPSED ? FALSE : TRUE;
+ Frames[nFramescount].Locked = clfrm->Flags & F_LOCKED ? TRUE : FALSE;
+ Frames[nFramescount].visible = clfrm->Flags & F_VISIBLE ? TRUE : FALSE;
+
+ Frames[nFramescount].UseBorder = (clfrm->Flags & F_NOBORDER) ? FALSE : TRUE;
+ Frames[nFramescount].Skinned = (clfrm->Flags & F_SKINNED) ? TRUE : FALSE;
+
+ // create frame
+ Frames[nFramescount].TitleBar.hwnd =
+ CreateWindow(CLUIFrameTitleBarClassName, Frames[nFramescount].name,
+ (db_get_b(0, CLUIFrameModule, "RemoveAllTitleBarBorders", 1) ? 0 : WS_BORDER)
+ | WS_CHILD | WS_CLIPCHILDREN | (Frames[nFramescount].TitleBar.ShowTitleBar ? WS_VISIBLE : 0) |
+ WS_CLIPCHILDREN, 0, 0, 0, 0, g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr);
+
+ SetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWLP_USERDATA, Frames[nFramescount].id);
+
+ Frames[nFramescount].TitleBar.hwndTip = CreateWindowExA(0, TOOLTIPS_CLASSA, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), nullptr);
+
+ SetWindowPos(Frames[nFramescount].TitleBar.hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ {
+ TOOLINFOA ti = { 0 };
+ ti.cbSize = sizeof(ti);
+ ti.lpszText = "";
+ ti.hinst = g_plugin.getInst();
+ ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
+ ti.uId = (UINT_PTR)Frames[nFramescount].TitleBar.hwnd;
+ SendMessageA(Frames[nFramescount].TitleBar.hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+ }
+
+ SendMessage(Frames[nFramescount].TitleBar.hwndTip, TTM_ACTIVATE, (WPARAM)Frames[nFramescount].TitleBar.ShowTitleBarTip, 0);
+
+ Frames[nFramescount].oldstyles = GetWindowLongPtr(Frames[nFramescount].hWnd, GWL_STYLE);
+ Frames[nFramescount].TitleBar.oldstyles = GetWindowLongPtr(Frames[nFramescount].TitleBar.hwnd, GWL_STYLE);
+
+ int retval = Frames[nFramescount].id;
+ Frames[nFramescount].order = nFramescount + 1;
+ nFramescount++;
+
+ CLUIFramesLoadFrameSettings(id2pos(retval));
+ style = GetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE);
+ style &= ~(WS_BORDER);
+ style |= ((Frames[nFramescount - 1].UseBorder) ? WS_BORDER : 0);
+
+ style |= Frames[nFramescount - 1].Skinned ? CLS_SKINNEDFRAME : 0;
+
+ SetWindowLongPtr(Frames[nFramescount - 1].hWnd, GWL_STYLE, style);
+ SetWindowLongPtr(Frames[nFramescount - 1].TitleBar.hwnd, GWL_STYLE, style & ~(WS_VSCROLL | WS_HSCROLL));
+
+ if (Frames[nFramescount - 1].order == 0)
+ Frames[nFramescount - 1].order = nFramescount;
+
+ lck.unlock();
+
+ alclientFrame = -1;//recalc it
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+
+ if (Frames[nFramescount - 1].floating) {
+ Frames[nFramescount - 1].floating = FALSE;
+ CLUIFrameSetFloat(retval, 1);//lparam=1 use stored width and height
+ }
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ return retval;
+}
+
+static INT_PTR CLUIFramesRemoveFrame(WPARAM wParam, LPARAM)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos < 0 || pos > nFramescount)
+ return -1;
+
+ FRAMEWND* F = &Frames[pos];
+ if (F->hWnd == g_hwndEventArea)
+ wndFrameEventArea = nullptr;
+ else if (F->hWnd == g_clistApi.hwndContactTree)
+ wndFrameCLC = nullptr;
+ else if (F->hWnd == g_hwndViewModeFrame)
+ wndFrameViewMode = nullptr;
+
+ mir_free(F->name);
+ mir_free(F->TitleBar.tbname);
+ mir_free(F->TitleBar.tooltip);
+
+ DestroyWindow(F->hWnd);
+ F->hWnd = (HWND)-1;
+ DestroyWindow(F->TitleBar.hwnd);
+ F->TitleBar.hwnd = (HWND)-1;
+ DestroyWindow(F->ContainerWnd);
+ F->ContainerWnd = (HWND)-1;
+ DestroyMenu(F->TitleBar.hmenu);
+
+ RemoveItemFromList(pos, &Frames, &nFramescount);
+ }
+
+ if (!cfg::shutDown) {
+ InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesForceUpdateTB(const FRAMEWND *Frame)
+{
+ if (Frame->TitleBar.hwnd != nullptr)
+ RedrawWindow(Frame->TitleBar.hwnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+ return 0;
+}
+
+INT_PTR CLUIFramesForceUpdateFrame(const FRAMEWND *Frame)
+{
+ if (Frame->hWnd != nullptr)
+ RedrawWindow(Frame->hWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_FRAME | RDW_ERASE | RDW_INVALIDATE);
+
+ if (Frame->floating)
+ if (Frame->ContainerWnd != nullptr) RedrawWindow(Frame->ContainerWnd, nullptr, nullptr, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE | RDW_FRAME);
+ return 0;
+}
+
+int CLUIFrameMoveResize(const FRAMEWND *Frame)
+{
+ TitleBarH = cfg::dat.titleBarHeight;
+ // we need to show or hide the frame?
+ if (Frame->visible && (!Frame->needhide)) {
+ ShowWindow(Frame->hWnd, SW_SHOW);
+ ShowWindow(Frame->TitleBar.hwnd, Frame->TitleBar.ShowTitleBar == TRUE ? SW_SHOW : SW_HIDE);
+ }
+ else {
+ ShowWindow(Frame->hWnd, SW_HIDE);
+ ShowWindow(Frame->TitleBar.hwnd, SW_HIDE);
+ return 0;
+ }
+
+ SetWindowPos(Frame->hWnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset,
+ (Frame->wndSize.right - Frame->wndSize.left),
+ (Frame->wndSize.bottom - Frame->wndSize.top), SWP_NOZORDER | SWP_NOREDRAW);
+ if (Frame->TitleBar.ShowTitleBar) {
+ SetWindowPos(Frame->TitleBar.hwnd, nullptr, Frame->wndSize.left + cfg::dat.bCLeft, Frame->wndSize.top + cfg::dat.topOffset - TitleBarH,
+ (Frame->wndSize.right - Frame->wndSize.left),
+ TitleBarH + (Frame->UseBorder ? (!Frame->collapsed ? (Frame->align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER);
+ }
+ return 0;
+}
+
+bool CLUIFramesFitInSize(void)
+{
+ int i;
+ int sumheight = 0;
+ int tbh = 0; // title bar height
+ int clientfrm;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ clientfrm = CLUIFramesGetalClientFrame();
+ if (clientfrm != -1)
+ tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((F.align != alClient) && (!F.floating) && (F.visible) && (!F.needhide)) {
+ sumheight += (F.height) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 2/*+btoint(F.UseBorder)*2*/;
+ if (sumheight > ContactListHeight - tbh - 2)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+int CLUIFramesGetMinHeight()
+{
+ if (g_clistApi.hwndContactList == nullptr)
+ return 0;
+
+ int i, tbh, clientfrm, sumheight = 0;
+ RECT border;
+ int allbord = 0;
+ {
+ mir_cslock lck(csFrameHook);
+
+ TitleBarH = cfg::dat.titleBarHeight;
+ // search for alClient frame and get the titlebar's height
+ tbh = 0;
+ clientfrm = CLUIFramesGetalClientFrame();
+ if (clientfrm != -1)
+ tbh = TitleBarH * btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((F.align != alClient) && (F.visible) && (!F.needhide) && (!F.floating)) {
+ RECT wsize;
+
+ GetWindowRect(F.hWnd, &wsize);
+ sumheight += (wsize.bottom - wsize.top) + (TitleBarH * btoint(F.TitleBar.ShowTitleBar)) + 3;
+ }
+ }
+ }
+
+ GetBorderSize(g_clistApi.hwndContactList, &border);
+ return(sumheight + border.top + border.bottom + allbord + tbh + 3);
+}
+
+int SizeMoveNewSizes()
+{
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (F.floating)
+ CLUIFrameResizeFloatingFrame(i);
+ else
+ CLUIFrameMoveResize(&F);
+ }
+ return 0;
+}
+
+/*
+ * changed Nightwish
+ * gap calculation was broken. Now, it doesn't calculate and store the gaps in Frames[] anymore.
+ * instead, it remembers the smallest wndSize.top value (which has to be the top frame) and then passes
+ * the gap to all following frame(s) to the actual resizing function which just adds the gap to
+ * wndSize.top and corrects the frame height accordingly.
+
+ * Title bar gap has been removed (can be simulated by using a clist_nicer skin item for frame title bars
+ * and setting the bottom margin of the skin item
+ */
+
+int CLUIFramesResize(const RECT newsize)
+{
+ int sumheight = 9999999;
+ int clientframe = -1;
+ int i, j;
+ int topOff = 0, botOff = 0, last_bottomtop;
+
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+ int sepw = GapBetweenFrames;
+
+ if (nFramescount < 1 || cfg::shutDown)
+ return 0;
+
+ int newheight = newsize.bottom - newsize.top;
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ // search for alClient frame and get the titlebar's height
+ int tbh = 0;
+ int clientfrm = CLUIFramesGetalClientFrame();
+ if (clientfrm != -1)
+ tbh = (TitleBarH)* btoint(Frames[clientfrm].TitleBar.ShowTitleBar);
+
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (!F.floating) {
+ F.needhide = FALSE;
+ F.wndSize.left = 0;
+ F.wndSize.right = newsize.right - newsize.left;
+ }
+ }
+ {
+ //sorting stuff
+ memset(g_sd, 0, sizeof(SortData) * MAX_FRAMES);
+ for (i = 0; i < nFramescount; i++) {
+ g_sd[i].order = Frames[i].order;
+ g_sd[i].realpos = i;
+ }
+ qsort(g_sd, nFramescount, sizeof(SortData), sortfunc);
+
+ }
+ int drawitems = nFramescount;
+ while (sumheight >(newheight - tbh) && drawitems > 0) {
+ sumheight = 0;
+ drawitems = 0;
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (((F.align != alClient)) && (!F.floating) && (F.visible) && (!F.needhide)) {
+ drawitems++;
+ int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
+ sumheight += (F.height) + curfrmtbh + (i > 0 ? sepw : 0) + (F.UseBorder ? 2 : 0);
+ if (sumheight > newheight - tbh) {
+ sumheight -= (F.height) + curfrmtbh + (i > 0 ? sepw : 0);
+ F.needhide = TRUE;
+ drawitems--;
+ break;
+ }
+ }
+ }
+ }
+
+ int prevframe = -1;
+ int prevframebottomline = 0;
+ for (j = 0; j < nFramescount; j++) {
+ // move all alTop frames
+ i = g_sd[j].realpos;
+ FRAMEWND &F = Frames[i];
+ if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alTop)) {
+ int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
+ F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (curfrmtbh);
+ F.wndSize.bottom = F.height + F.wndSize.top + (F.UseBorder ? 2 : 0);
+ F.prevvisframe = prevframe;
+ prevframe = i;
+ prevframebottomline = F.wndSize.bottom;
+ topOff = prevframebottomline;
+ }
+ }
+
+ if (sumheight < newheight) {
+ for (j = 0; j < nFramescount; j++) {
+ // move alClient frame
+ i = g_sd[j].realpos;
+ FRAMEWND &F = Frames[i];
+ if ((!F.needhide) && (!F.floating) && (F.visible) && (F.align == alClient)) {
+ int oldh;
+ F.wndSize.top = prevframebottomline + (prevframebottomline > 0 ? sepw : 0) + (tbh);
+ F.wndSize.bottom = F.wndSize.top + newheight - sumheight - tbh - ((prevframebottomline > 0) ? sepw : 0);
+ clientframe = i;
+ oldh = F.height;
+ F.height = F.wndSize.bottom - F.wndSize.top;
+ F.prevvisframe = prevframe;
+ prevframe = i;
+ prevframebottomline = F.wndSize.bottom;
+ if (prevframebottomline > newheight) {
+ // prevframebottomline-=F.height+(tbh+1);
+ // F.needhide=TRUE;
+ }
+ break;
+ }
+ }
+ }
+
+ // newheight
+ prevframebottomline = last_bottomtop = newheight;
+ for (j = nFramescount - 1; j >= 0; j--) {
+ // move all alBottom frames
+ i = g_sd[j].realpos;
+ FRAMEWND &F = Frames[i];
+ if ((F.visible) && (!F.floating) && (!F.needhide) && (F.align == alBottom)) {
+ int curfrmtbh = (TitleBarH)* btoint(F.TitleBar.ShowTitleBar);
+ F.wndSize.bottom = prevframebottomline - ((prevframebottomline < newheight) ? sepw : 0);
+ F.wndSize.top = F.wndSize.bottom - F.height - (F.UseBorder ? 2 : 0);
+ F.prevvisframe = prevframe;
+ prevframe = i;
+ prevframebottomline = F.wndSize.top - curfrmtbh;
+ botOff = prevframebottomline;
+ last_bottomtop = F.wndSize.top - curfrmtbh;
+ }
+ }
+
+ // correct client frame bottom gap if there is no other top frame.
+ if (clientframe != -1) {
+ Frames[clientframe].wndSize.bottom = last_bottomtop - (last_bottomtop < newheight ? sepw : 0);
+ Frames[clientframe].height = Frames[clientframe].wndSize.bottom - Frames[clientframe].wndSize.top;
+ }
+ return 0;
+}
+
+INT_PTR CLUIFramesUpdateFrame(WPARAM wParam, LPARAM lParam)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ if (wParam == -1) {
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ return 0;
+ }
+
+ if (lParam & FU_FMPOS)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 1);
+
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(wParam);
+ if (pos < 0 || pos >= nFramescount)
+ return -1;
+
+ if (lParam & FU_TBREDRAW)
+ CLUIFramesForceUpdateTB(&Frames[pos]);
+ if (lParam & FU_FMREDRAW)
+ CLUIFramesForceUpdateFrame(&Frames[pos]);
+ return 0;
+}
+
+int dock_prevent_moving = 0;
+
+int CLUIFramesApplyNewSizes(int mode)
+{
+ dock_prevent_moving = 0;
+
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if ((mode == 1 && F.OwnerWindow != (HWND)-2 && F.OwnerWindow) ||
+ (mode == 2 && F.OwnerWindow == (HWND)-2) || (mode == 3))
+ if (F.floating)
+ CLUIFrameResizeFloatingFrame(i);
+ else
+ CLUIFrameMoveResize(&Frames[i]);
+ }
+ dock_prevent_moving = 1;
+ return 0;
+}
+
+RECT old_window_rect = { 0 }, new_window_rect = { 0 };
+
+int SizeFramesByWindowRect(RECT *r)
+{
+ if (FramesSysNotStarted)
+ return -1;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+
+ mir_cslock lck(csFrameHook);
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+
+ RECT nRect = *r;
+ nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset);
+ nRect.right -= cfg::dat.bCRight;
+ nRect.left = cfg::dat.bCLeft;
+ nRect.top = cfg::dat.topOffset;
+ ContactListHeight = nRect.bottom - nRect.top;
+
+ CLUIFramesResize(nRect);
+ {
+ int i;
+ for (i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ if (!F.floating) {
+ if (F.OwnerWindow && F.OwnerWindow != (HWND)-2) {
+ SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset,
+ (F.wndSize.right - F.wndSize.left),
+ (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+
+ if (F.TitleBar.ShowTitleBar) {
+ SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH,
+ (F.wndSize.right - F.wndSize.left),
+ TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS);
+ }
+ }
+ else {
+ // set frame position
+ SetWindowPos(F.hWnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset,
+ (F.wndSize.right - F.wndSize.left),
+ (F.wndSize.bottom - F.wndSize.top), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOREDRAW);
+
+ // set titlebar position
+ if (F.TitleBar.ShowTitleBar) {
+ SetWindowPos(F.TitleBar.hwnd, nullptr, F.wndSize.left + cfg::dat.bCLeft, F.wndSize.top + cfg::dat.topOffset - TitleBarH,
+ (F.wndSize.right - F.wndSize.left),
+ TitleBarH + (F.UseBorder ? (!F.collapsed ? (F.align == alClient ? 0 : 2) : 1) : 0), SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW);
+ }
+ if (F.TitleBar.ShowTitleBar)
+ UpdateWindow(F.TitleBar.hwnd);
+ }
+ }
+ }
+
+ if (GetTickCount() - LastStoreTick > 1000) {
+ CLUIFramesStoreAllFrames();
+ LastStoreTick = GetTickCount();
+ }
+ }
+ return 0;
+}
+
+int CLUIFramesOnClistResize(WPARAM wParam, LPARAM lParam)
+{
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+
+ if (FramesSysNotStarted || cfg::shutDown)
+ return -1;
+
+ RECT nRect, rcStatus;
+ int tick;
+ {
+ mir_cslock lck(csFrameHook);
+
+ GetClientRect(g_clistApi.hwndContactList, &nRect);
+ if (lParam && lParam != 1) {
+ RECT oldRect;
+ POINT pt;
+ RECT * newRect = (RECT *)lParam;
+ int dl, dt, dr, db;
+ GetWindowRect((HWND)wParam, &oldRect);
+ pt.x = nRect.left;
+ pt.y = nRect.top;
+ ClientToScreen(g_clistApi.hwndContactList, &pt);
+ dl = pt.x - oldRect.left;
+ dt = pt.y - oldRect.top;
+ dr = (oldRect.right - oldRect.left) - (nRect.right - nRect.left) - dl;
+ db = (oldRect.bottom - oldRect.top) - (nRect.bottom - nRect.top) - dt;
+ nRect.left = newRect->left + dl;
+ nRect.top = newRect->top + dt;
+ nRect.bottom = newRect->bottom - db;
+ nRect.right = newRect->right - dr;
+ }
+
+ rcStatus.top = rcStatus.bottom = 0;
+
+ nRect.bottom -= (cfg::dat.statusBarHeight + cfg::dat.bottomOffset);
+ nRect.right -= cfg::dat.bCRight;
+ nRect.left = cfg::dat.bCLeft;
+ nRect.top = cfg::dat.topOffset;
+ ContactListHeight = nRect.bottom - nRect.top;
+
+ tick = GetTickCount();
+
+ CLUIFramesResize(nRect);
+ CLUIFramesApplyNewSizes(3);
+ }
+
+ tick = GetTickCount() - tick;
+
+ if (g_clistApi.hwndContactList != nullptr)
+ InvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ if (g_clistApi.hwndContactList != nullptr)
+ UpdateWindow(g_clistApi.hwndContactList);
+
+ Sleep(0);
+
+ if (GetTickCount() - LastStoreTick > 2000) {
+ CLUIFramesStoreAllFrames();
+ LastStoreTick = GetTickCount();
+ }
+ return 0;
+}
+
+static HBITMAP hBmpBackground;
+static int backgroundBmpUse;
+static COLORREF bkColour;
+static COLORREF SelBkColour;
+boolean AlignCOLLIconToLeft; //will hide frame icon
+
+int OnFrameTitleBarBackgroundChange()
+{
+ AlignCOLLIconToLeft = db_get_b(0, "FrameTitleBar", "AlignCOLLIconToLeft", 0);
+ bkColour = db_get_dw(0, "FrameTitleBar", "BkColour", CLCDEFAULT_BKCOLOUR);
+
+ if (hBmpBackground) {
+ DeleteObject(hBmpBackground);
+ hBmpBackground = nullptr;
+ }
+ if (db_get_b(0, "FrameTitleBar", "UseBitmap", CLCDEFAULT_USEBITMAP)) {
+ ptrW tszBitmapName(db_get_wsa(0, "FrameTitleBar", "BkBitmap"));
+ if (tszBitmapName != NULL)
+ hBmpBackground = Bitmap_Load(tszBitmapName);
+ }
+ backgroundBmpUse = db_get_w(0, "FrameTitleBar", "BkBmpUse", CLCDEFAULT_BKBMPUSE);
+
+ CLUIFramesOnClistResize(0, 0);
+ return 0;
+}
+
+static int DrawTitleBar(HDC dc, RECT rect, int Frameid)
+{
+ StatusItems_t *item = arStatusItems[ID_EXTBKFRAMETITLE - ID_STATUS_OFFLINE];
+
+ /*
+ * no need to redraw anything while shutting down
+ */
+ if (cfg::shutDown)
+ return 0;
+
+ TitleBarH = cfg::dat.titleBarHeight;
+ HDC hdcMem = CreateCompatibleDC(dc);
+ HBITMAP hBmpOsb = CreateCompatibleBitmap(dc, rect.right, rect.bottom);
+ HBITMAP hoBmp = reinterpret_cast<HBITMAP>(SelectObject(hdcMem, hBmpOsb));
+
+ SetBkMode(hdcMem, TRANSPARENT);
+
+ HBRUSH hBack = GetSysColorBrush(COLOR_3DFACE);
+ HBRUSH hoBrush = reinterpret_cast<HBRUSH>(SelectObject(hdcMem, hBack));
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(Frameid);
+ if (pos >= 0 && pos < nFramescount) {
+ HFONT oFont;
+ int fHeight, fontTop;
+ GetClientRect(Frames[pos].TitleBar.hwnd, &Frames[pos].TitleBar.wndSize);
+
+ if (cfg::clcdat) {
+ oFont = ChangeToFont(hdcMem, cfg::clcdat, FONTID_FRAMETITLE, &fHeight);
+ }
+ else {
+ oFont = reinterpret_cast<HFONT>(SelectObject(hdcMem, GetStockObject(DEFAULT_GUI_FONT)));
+ fHeight = 10;
+ }
+ fontTop = (TitleBarH - fHeight) / 2;
+
+ if (cfg::dat.bWallpaperMode && !Frames[pos].floating)
+ SkinDrawBg(Frames[pos].TitleBar.hwnd, hdcMem);
+
+ if (!item->IGNORED) {
+ RECT rc = Frames[pos].TitleBar.wndSize;
+ rc.top += item->MARGIN_TOP;
+ rc.bottom -= item->MARGIN_BOTTOM;
+ rc.left += item->MARGIN_LEFT;
+ rc.right -= item->MARGIN_RIGHT;
+ DrawAlpha(hdcMem, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT,
+ item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
+ SetTextColor(hdcMem, item->TEXTCOLOR);
+ }
+ else if (cfg::clcdat) {
+ FillRect(hdcMem, &rect, hBack);
+ SetTextColor(hdcMem, cfg::clcdat->fontInfo[FONTID_FRAMETITLE].colour);
+ }
+ else {
+ FillRect(hdcMem, &rect, hBack);
+ SetTextColor(hdcMem, GetSysColor(COLOR_BTNTEXT));
+ }
+
+ const wchar_t *pwszTitle = TranslateW_LP(Frames[pos].TitleBar.tbname, Frames[pos].pPlugin);
+ int iTitleLen = (int)mir_wstrlen(pwszTitle);
+
+ if (!AlignCOLLIconToLeft) {
+ if (Frames[pos].TitleBar.hicon != nullptr) {
+ DrawIconEx(hdcMem, 6 + cfg::dat.bClipBorder, ((TitleBarH >> 1) - 8), Frames[pos].TitleBar.hicon, 16, 16, 0, nullptr, DI_NORMAL);
+ TextOut(hdcMem, 24 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
+ }
+ else TextOut(hdcMem, 6 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
+ }
+ else TextOut(hdcMem, 18 + cfg::dat.bClipBorder, fontTop, pwszTitle, iTitleLen);
+
+ if (!AlignCOLLIconToLeft)
+ DrawIconEx(hdcMem, Frames[pos].TitleBar.wndSize.right - 22, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL);
+ else
+ DrawIconEx(hdcMem, 0, ((TitleBarH >> 1) - 8), Frames[pos].collapsed ? Skin_LoadIcon(SKINICON_OTHER_GROUPOPEN) : Skin_LoadIcon(SKINICON_OTHER_GROUPSHUT), 16, 16, 0, nullptr, DI_NORMAL);
+ SelectObject(hdcMem, oFont);
+ }
+ }
+
+ BitBlt(dc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, SRCCOPY);
+ SelectObject(hdcMem, hoBmp);
+ SelectObject(hdcMem, hoBrush);
+ DeleteDC(hdcMem);
+ DeleteObject(hBack);
+ DeleteObject(hBmpOsb);
+ return 0;
+}
+
+#define MPCF_CONTEXTFRAMEMENU 3
+POINT ptOld;
+short nLeft = 0;
+short nTop = 0;
+
+LRESULT CALLBACK CLUIFrameTitleBarProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rect;
+ int Frameid, Framemod, direction;
+ int xpos, ypos, framepos;
+
+ Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ memset(&rect, 0, sizeof(rect));
+
+ switch (msg) {
+ case WM_CREATE:
+ return FALSE;
+
+ case WM_MEASUREITEM:
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM:
+ return Menu_DrawItem(lParam);
+
+ case WM_ENABLE:
+ if (hwnd != nullptr) InvalidateRect(hwnd, nullptr, FALSE);
+ return 0;
+ case WM_SIZE:
+ return 0;
+
+ case WM_COMMAND:
+ if (Clist_MenuProcessCommand(LOWORD(wParam), 0, Frameid))
+ break;
+
+ if (HIWORD(wParam) == 0) {//mouse events for self created menu
+ framepos = id2pos(Frameid);
+ if (framepos == -1)
+ break;
+
+ switch (LOWORD(wParam)) {
+ case frame_menu_lock:
+ Frames[framepos].Locked = !Frames[framepos].Locked;
+ break;
+ case frame_menu_visible:
+ Frames[framepos].visible = !Frames[framepos].visible;
+ break;
+ case frame_menu_showtitlebar:
+ Frames[framepos].TitleBar.ShowTitleBar = !Frames[framepos].TitleBar.ShowTitleBar;
+ break;
+ case frame_menu_floating:
+ CLUIFrameSetFloat(Frameid, 0);
+ break;
+ }
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ }
+ break;
+
+ case WM_RBUTTONDOWN:
+ {
+ HMENU hmenu;
+ if (ServiceExists(MS_CLIST_MENUBUILDFRAMECONTEXT))
+ hmenu = (HMENU)CallService(MS_CLIST_MENUBUILDFRAMECONTEXT, Frameid, 0);
+ else {
+ framepos = id2pos(Frameid);
+
+ mir_cslock lck(csFrameHook);
+ if (framepos == -1)
+ break;
+
+ hmenu = CreatePopupMenu();
+ AppendMenu(hmenu, MF_STRING | MF_DISABLED | MF_GRAYED, 15, Frames[framepos].name);
+ AppendMenu(hmenu, MF_SEPARATOR, 16, L"");
+
+ if (Frames[framepos].Locked)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_lock, TranslateT("Lock frame"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_lock, TranslateT("Lock frame"));
+
+ if (Frames[framepos].visible)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_visible, TranslateT("Visible"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_visible, TranslateT("Visible"));
+
+ if (Frames[framepos].TitleBar.ShowTitleBar)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_showtitlebar, TranslateT("Show title bar"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_showtitlebar, TranslateT("Show title bar"));
+
+ if (Frames[framepos].Skinned)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_skinned, TranslateT("Skinned frame"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_skinned, TranslateT("Skinned frame"));
+
+ if (Frames[framepos].floating)
+ AppendMenu(hmenu, MF_STRING | MF_CHECKED, frame_menu_floating, TranslateT("Floating"));
+ else
+ AppendMenu(hmenu, MF_STRING, frame_menu_floating, TranslateT("Floating"));
+ }
+ POINT pt;
+ GetCursorPos(&pt);
+ TrackPopupMenu(hmenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, nullptr);
+ DestroyMenu(hmenu);
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ Framemod = -1;
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ CallService(MS_CLIST_FRAMES_UCOLLFRAME, Frameid, 0);
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ break;
+
+ case WM_LBUTTONUP:
+ if (GetCapture() != hwnd)
+ break;
+
+ curdragbar = -1;
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ RedrawWindow(g_clistApi.hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ break;
+
+ case WM_LBUTTONDOWN:
+ framepos = id2pos(Frameid);
+ {
+ mir_cslock lck(csFrameHook);
+ if (framepos == -1)
+ break;
+
+ if (Frames[framepos].floating) {
+ POINT pt;
+ GetCursorPos(&pt);
+ Frames[framepos].TitleBar.oldpos = pt;
+ }
+
+ if ((!(wParam&MK_CONTROL)) && Frames[framepos].Locked && (!(Frames[framepos].floating))) {
+ if (db_get_b(0, "CLUI", "ClientAreaDrag", 0)) {
+ POINT pt;
+ GetCursorPos(&pt);
+ return SendMessage(GetParent(hwnd), WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
+ }
+ }
+ if (Frames[framepos].floating) {
+ RECT rc;
+ GetCursorPos(&ptOld);
+ GetWindowRect(hwnd, &rc);
+ nLeft = (short)rc.left;
+ nTop = (short)rc.top;
+ }
+ }
+ SetCapture(hwnd);
+ break;
+
+ case WM_MOUSEMOVE:
+ {
+ mir_cslock lck(csFrameHook);
+ int pos = id2pos(Frameid);
+ if (pos != -1) {
+ int oldflags;
+ char TBcapt[255];
+ mir_snprintf(TBcapt, "%s - h:%d, vis:%d, fl:%d, fl:(%d,%d,%d,%d),or: %d",
+ Frames[pos].name, Frames[pos].height, Frames[pos].visible, Frames[pos].floating,
+ Frames[pos].FloatingPos.x, Frames[pos].FloatingPos.y,
+ Frames[pos].FloatingSize.x, Frames[pos].FloatingSize.y,
+ Frames[pos].order);
+
+ oldflags = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, Frames[pos].id), 0);
+ if (!(oldflags & F_SHOWTBTIP))
+ oldflags |= F_SHOWTBTIP;
+ }
+ }
+ if (wParam & MK_LBUTTON) {
+ RECT rcMiranda;
+ RECT rcwnd, rcOverlap;
+ POINT newpt, ofspt, curpt, newpos;
+
+ mir_cslockfull lck(csFrameHook);
+
+ int pos = id2pos(Frameid);
+ if (Frames[pos].floating) {
+ GetCursorPos(&curpt);
+ rcwnd.bottom = curpt.y + 5;
+ rcwnd.top = curpt.y;
+ rcwnd.left = curpt.x;
+ rcwnd.right = curpt.x + 5;
+
+ GetWindowRect(g_clistApi.hwndContactList, &rcMiranda);
+ if (IsWindowVisible(g_clistApi.hwndContactList) && IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) {
+ int id = Frames[pos].id;
+
+ lck.unlock();
+ ofspt.x = 0;
+ ofspt.y = 0;
+ ClientToScreen(Frames[pos].TitleBar.hwnd, &ofspt);
+ ofspt.x = curpt.x - ofspt.x;
+ ofspt.y = curpt.y - ofspt.y;
+
+ CLUIFrameSetFloat(id, 0);
+ newpt.x = 0;
+ newpt.y = 0;
+ ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt);
+ SetCursorPos(newpt.x + ofspt.x, newpt.y + ofspt.y);
+ GetCursorPos(&curpt);
+
+ lck.lock();
+ Frames[pos].TitleBar.oldpos = curpt;
+ return 0;
+ }
+ }
+ else {
+ int id = Frames[pos].id;
+
+ GetCursorPos(&curpt);
+ rcwnd.bottom = curpt.y + 5;
+ rcwnd.top = curpt.y;
+ rcwnd.left = curpt.x;
+ rcwnd.right = curpt.x + 5;
+
+ GetWindowRect(g_clistApi.hwndContactList, &rcMiranda);
+
+ if (!IntersectRect(&rcOverlap, &rcwnd, &rcMiranda)) {
+ lck.unlock();
+ GetCursorPos(&curpt);
+ GetWindowRect(Frames[pos].hWnd, &rcwnd);
+ rcwnd.left = rcwnd.right - rcwnd.left;
+ rcwnd.top = rcwnd.bottom - rcwnd.top;
+ newpos.x = curpt.x;
+ newpos.y = curpt.y;
+ if (curpt.x >= (rcMiranda.right - 1))
+ newpos.x = curpt.x + 5;
+ if (curpt.x <= (rcMiranda.left + 1))
+ newpos.x = curpt.x - (rcwnd.left) - 5;
+ if (curpt.y >= (rcMiranda.bottom - 1))
+ newpos.y = curpt.y + 5;
+ if (curpt.y <= (rcMiranda.top + 1))
+ newpos.y = curpt.y - (rcwnd.top) - 5;
+
+ ofspt.x = 0;
+ ofspt.y = 0;
+ GetWindowRect(Frames[pos].TitleBar.hwnd, &rcwnd);
+ ofspt.x = curpt.x - ofspt.x;
+ ofspt.y = curpt.y - ofspt.y;
+ Frames[pos].FloatingPos.x = newpos.x;
+ Frames[pos].FloatingPos.y = newpos.y;
+ CLUIFrameSetFloat(id, 0);
+
+ lck.lock();
+ newpt.x = 0;
+ newpt.y = 0;
+ ClientToScreen(Frames[pos].TitleBar.hwnd, &newpt);
+ GetWindowRect(Frames[pos].hWnd, &rcwnd);
+ SetCursorPos(newpt.x + (rcwnd.right - rcwnd.left) / 2, newpt.y + (rcwnd.bottom - rcwnd.top) / 2);
+ GetCursorPos(&curpt);
+ Frames[pos].TitleBar.oldpos = curpt;
+ return 0;
+ }
+ }
+ }
+ if (wParam & MK_LBUTTON) {
+ int newh = -1, prevold;
+
+ if (GetCapture() != hwnd)
+ break;
+
+ POINT pt, pt2;
+ mir_cslockfull lck(csFrameHook);
+ int pos = id2pos(Frameid);
+
+ if (Frames[pos].floating) {
+ RECT wndr;
+ GetCursorPos(&pt);
+ if ((Frames[pos].TitleBar.oldpos.x != pt.x) || (Frames[pos].TitleBar.oldpos.y != pt.y)) {
+ pt2 = pt;
+ ScreenToClient(hwnd, &pt2);
+ GetWindowRect(Frames[pos].ContainerWnd, &wndr);
+
+ POINT ptNew = pt;
+
+ nLeft += (short)ptNew.x - ptOld.x;
+ nTop += (short)ptNew.y - ptOld.y;
+
+ if (!(wParam & MK_CONTROL))
+ PositionThumb(&Frames[pos], nLeft, nTop);
+ else
+ SetWindowPos(Frames[pos].ContainerWnd, nullptr, nLeft, nTop, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+
+ ptOld = ptNew;
+
+ pt.x = nLeft;
+ pt.y = nTop;
+ Frames[pos].TitleBar.oldpos = pt;
+ }
+ return 0;
+ }
+ if (Frames[pos].prevvisframe != -1) {
+ GetCursorPos(&pt);
+
+ if ((Frames[pos].TitleBar.oldpos.x == pt.x) && (Frames[pos].TitleBar.oldpos.y == pt.y))
+ break;
+
+ ypos = rect.top + pt.y;
+ xpos = rect.left + pt.x;
+ Framemod = -1;
+
+ if (Frames[pos].align == alBottom) {
+ direction = -1;
+ Framemod = pos;
+ }
+ else {
+ direction = 1;
+ Framemod = Frames[pos].prevvisframe;
+ }
+ if (Frames[Framemod].Locked)
+ break;
+ if (curdragbar != -1 && curdragbar != pos)
+ break;
+
+ if (lbypos == -1) {
+ curdragbar = pos;
+ lbypos = ypos;
+ oldframeheight = Frames[Framemod].height;
+ SetCapture(hwnd);
+ break;
+ }
+ newh = oldframeheight + direction * (ypos - lbypos);
+ if (newh > 0) {
+ prevold = Frames[Framemod].height;
+ Frames[Framemod].height = newh;
+ if (!CLUIFramesFitInSize()) {
+ Frames[Framemod].height = prevold;
+ return TRUE;
+ }
+ Frames[Framemod].height = newh;
+ if (newh > 3) Frames[Framemod].collapsed = TRUE;
+
+ }
+ Frames[pos].TitleBar.oldpos = pt;
+ }
+ lck.unlock();
+
+ if (newh > 0)
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ break;
+ }
+ curdragbar = -1;
+ lbypos = -1;
+ oldframeheight = -1;
+ ReleaseCapture();
+ break;
+
+ case WM_NCPAINT:
+ if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER) {
+ HDC hdc = GetWindowDC(hwnd);
+ HPEN hPenOld = reinterpret_cast<HPEN>(SelectObject(hdc, g_hPenCLUIFrames));
+ RECT rcWindow, rc;
+ HBRUSH brold;
+
+ CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
+ GetWindowRect(hwnd, &rcWindow);
+ rc.left = rc.top = 0;
+ rc.right = rcWindow.right - rcWindow.left;
+ rc.bottom = rcWindow.bottom - rcWindow.top;
+ brold = reinterpret_cast<HBRUSH>(SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)));
+ Rectangle(hdc, 0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
+ SelectObject(hdc, hPenOld);
+ SelectObject(hdc, brold);
+ ReleaseDC(hwnd, hdc);
+ return 0;
+ }
+ break;
+
+ case WM_PRINT:
+ case WM_PRINTCLIENT:
+ GetClientRect(hwnd, &rect);
+ DrawTitleBar((HDC)wParam, rect, Frameid);
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT paintStruct;
+ HDC paintDC = BeginPaint(hwnd, &paintStruct);
+ rect = paintStruct.rcPaint;
+ DrawTitleBar(paintDC, rect, Frameid);
+ EndPaint(hwnd, &paintStruct);
+ }
+ return 0;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ return TRUE;
+}
+
+int CLUIFrameResizeFloatingFrame(int framepos)
+{
+ if (!Frames[framepos].floating)
+ return 0;
+ if (Frames[framepos].ContainerWnd == nullptr)
+ return 0;
+
+ RECT rect;
+ GetClientRect(Frames[framepos].ContainerWnd, &rect);
+
+ int width = rect.right - rect.left;
+ int height = rect.bottom - rect.top;
+ int floatingHeight = cfg::dat.titleBarHeight;
+
+ if (floatingHeight <= 0 || floatingHeight > 50)
+ floatingHeight = 18;
+
+ Frames[framepos].visible ? ShowWindow(Frames[framepos].ContainerWnd, SW_SHOWNOACTIVATE) : ShowWindow(Frames[framepos].ContainerWnd, SW_HIDE);
+
+ if (Frames[framepos].TitleBar.ShowTitleBar) {
+ ShowWindow(Frames[framepos].TitleBar.hwnd, SW_SHOWNOACTIVATE);
+ Frames[framepos].height = height - floatingHeight;
+ SetWindowPos(Frames[framepos].TitleBar.hwnd, HWND_TOP, 0, 0, width, floatingHeight, SWP_SHOWWINDOW | SWP_DRAWFRAME | SWP_NOACTIVATE);
+ InvalidateRect(Frames[framepos].TitleBar.hwnd, nullptr, FALSE);
+ SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, floatingHeight, width, height - floatingHeight, SWP_SHOWWINDOW | SWP_NOACTIVATE);
+
+ }
+ else {
+ Frames[framepos].height = height;
+ ShowWindow(Frames[framepos].TitleBar.hwnd, SW_HIDE);
+ SetWindowPos(Frames[framepos].hWnd, HWND_TOP, 0, 0, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
+ }
+
+ if (Frames[framepos].ContainerWnd != nullptr)
+ UpdateWindow(Frames[framepos].ContainerWnd);
+ GetWindowRect(Frames[framepos].hWnd, &Frames[framepos].wndSize);
+
+ if (Frames[framepos].TitleBar.ShowTitleBar)
+ RedrawWindow(Frames[framepos].TitleBar.hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
+
+ RedrawWindow(Frames[framepos].hWnd, nullptr, nullptr, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW);
+ return 0;
+}
+
+static int CLUIFrameOnMainMenuBuild(WPARAM, LPARAM)
+{
+ CLUIFramesLoadMainMenu();
+ return 0;
+}
+
+LRESULT CALLBACK CLUIFrameContainerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int framepos;
+ RECT rect;
+ INT_PTR Frameid = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_CREATE:
+ {
+ mir_cslockfull lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ }
+ return 0;
+
+ case WM_GETMINMAXINFO:
+ TitleBarH = cfg::dat.titleBarHeight;
+ {
+ mir_cslock lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ if (framepos < 0 || framepos >= nFramescount)
+ break;
+
+ if (!Frames[framepos].minmaxenabled)
+ break;
+
+ if (Frames[framepos].ContainerWnd == nullptr)
+ break;
+
+ if (Frames[framepos].Locked) {
+ RECT rct;
+ GetWindowRect(hwnd, &rct);
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = rct.right - rct.left;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = rct.bottom - rct.top;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = rct.right - rct.left;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = rct.bottom - rct.top;
+ }
+
+ MINMAXINFO minmax;
+ memset(&minmax, 0, sizeof(minmax));
+ if (SendMessage(Frames[framepos].hWnd, WM_GETMINMAXINFO, 0, (LPARAM)&minmax) != 0)
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+
+ RECT border;
+ int tbh = TitleBarH * btoint(Frames[framepos].TitleBar.ShowTitleBar);
+ GetBorderSize(hwnd, &border);
+ if (minmax.ptMaxTrackSize.x != 0 && minmax.ptMaxTrackSize.y != 0) {
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = minmax.ptMinTrackSize.x;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = minmax.ptMinTrackSize.y;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.x = minmax.ptMaxTrackSize.x + border.left + border.right;
+ ((LPMINMAXINFO)lParam)->ptMaxTrackSize.y = minmax.ptMaxTrackSize.y + tbh + border.top + border.bottom;
+ }
+ }
+
+ case WM_MOVE:
+ {
+ mir_cslock lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ if (framepos < 0 || framepos >= nFramescount)
+ break;
+
+ if (Frames[framepos].ContainerWnd == nullptr)
+ return 0;
+
+ GetWindowRect(Frames[framepos].ContainerWnd, &rect);
+ Frames[framepos].FloatingPos.x = rect.left;
+ Frames[framepos].FloatingPos.y = rect.top;
+ Frames[framepos].FloatingSize.x = rect.right - rect.left;
+ Frames[framepos].FloatingSize.y = rect.bottom - rect.top;
+ CLUIFramesStoreFrameSettings(framepos);
+ }
+ return 0;
+
+ case WM_SIZE:
+ {
+ mir_cslock lck(csFrameHook);
+ framepos = id2pos(Frameid);
+ if (framepos < 0 || framepos >= nFramescount)
+ break;
+
+ if (Frames[framepos].ContainerWnd == nullptr)
+ return 0;
+
+ CLUIFrameResizeFloatingFrame(framepos);
+
+ GetWindowRect(Frames[framepos].ContainerWnd, &rect);
+ Frames[framepos].FloatingPos.x = rect.left;
+ Frames[framepos].FloatingPos.y = rect.top;
+ Frames[framepos].FloatingSize.x = rect.right - rect.left;
+ Frames[framepos].FloatingSize.y = rect.bottom - rect.top;
+
+ CLUIFramesStoreFrameSettings(framepos);
+ }
+ return 0;
+
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+
+ case WM_DESTROY:
+ return 0;
+ }
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+static HWND CreateContainerWindow(HWND parent, int x, int y, int width, int height)
+{
+ return(CreateWindowA("FramesContainer", "aaaa", WS_POPUP | WS_THICKFRAME, x, y, width, height, parent, nullptr, g_plugin.getInst(), nullptr));
+}
+
+INT_PTR CLUIFrameSetFloat(WPARAM wParam, LPARAM lParam)
+{
+ HWND hwndtmp, hwndtooltiptmp;
+ {
+ mir_cslock lck(csFrameHook);
+ wParam = id2pos(wParam);
+ if ((int)wParam >= 0 && (int)wParam < nFramescount) {
+ if (Frames[wParam].floating) {
+ SetParent(Frames[wParam].hWnd, g_clistApi.hwndContactList);
+ SetParent(Frames[wParam].TitleBar.hwnd, g_clistApi.hwndContactList);
+ Frames[wParam].floating = FALSE;
+ DestroyWindow(Frames[wParam].ContainerWnd);
+ Frames[wParam].ContainerWnd = nullptr;
+ }
+ else {
+ RECT recttb, rectw, border;
+ int temp;
+ int neww, newh;
+
+ Frames[wParam].oldstyles = GetWindowLongPtr(Frames[wParam].hWnd, GWL_STYLE);
+ Frames[wParam].TitleBar.oldstyles = GetWindowLongPtr(Frames[wParam].TitleBar.hwnd, GWL_STYLE);
+ bool locked = Frames[wParam].Locked;
+ Frames[wParam].Locked = FALSE;
+ Frames[wParam].minmaxenabled = FALSE;
+
+ GetWindowRect(Frames[wParam].hWnd, &rectw);
+ GetWindowRect(Frames[wParam].TitleBar.hwnd, &recttb);
+ if (!Frames[wParam].TitleBar.ShowTitleBar)
+ recttb.top = recttb.bottom = recttb.left = recttb.right = 0;
+
+ Frames[wParam].ContainerWnd = CreateContainerWindow(g_clistApi.hwndContactList, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, 10, 10);
+
+ SetParent(Frames[wParam].hWnd, Frames[wParam].ContainerWnd);
+ SetParent(Frames[wParam].TitleBar.hwnd, Frames[wParam].ContainerWnd);
+
+ GetBorderSize(Frames[wParam].ContainerWnd, &border);
+
+ SetWindowLongPtr(Frames[wParam].ContainerWnd, GWLP_USERDATA, Frames[wParam].id);
+ if ((lParam == 1)) {
+ if ((Frames[wParam].FloatingPos.x != 0) && (Frames[wParam].FloatingPos.y != 0)) {
+ if (Frames[wParam].FloatingPos.x < 20)
+ Frames[wParam].FloatingPos.x = 40;
+
+ if (Frames[wParam].FloatingPos.y < 20)
+ Frames[wParam].FloatingPos.y = 40;
+
+ SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, Frames[wParam].FloatingSize.x, Frames[wParam].FloatingSize.y, SWP_HIDEWINDOW);
+ }
+ else SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, 120, 120, 140, 140, SWP_HIDEWINDOW);
+ }
+ else {
+ neww = rectw.right - rectw.left + border.left + border.right;
+ newh = (rectw.bottom - rectw.top) + (recttb.bottom - recttb.top) + border.top + border.bottom;
+ if (neww < 20)
+ neww = 40;
+
+ if (newh < 20)
+ newh = 40;
+
+ if (Frames[wParam].FloatingPos.x < 20)
+ Frames[wParam].FloatingPos.x = 40;
+
+ if (Frames[wParam].FloatingPos.y < 20)
+ Frames[wParam].FloatingPos.y = 40;
+
+ SetWindowPos(Frames[wParam].ContainerWnd, HWND_TOPMOST, Frames[wParam].FloatingPos.x, Frames[wParam].FloatingPos.y, neww, newh, SWP_HIDEWINDOW);
+ }
+ SetWindowText(Frames[wParam].ContainerWnd, Frames[wParam].TitleBar.tbname);
+ temp = GetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE);
+ temp |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+ SetWindowLongPtr(Frames[wParam].ContainerWnd, GWL_EXSTYLE, temp);
+ Frames[wParam].floating = TRUE;
+ Frames[wParam].Locked = locked;
+ }
+ }
+
+ CLUIFramesStoreFrameSettings(wParam);
+ Frames[wParam].minmaxenabled = TRUE;
+ hwndtooltiptmp = Frames[wParam].TitleBar.hwndTip;
+
+ hwndtmp = Frames[wParam].ContainerWnd;
+ }
+
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ SendMessage(hwndtmp, WM_SIZE, 0, 0);
+ SetWindowPos(hwndtooltiptmp, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ return 0;
+}
+
+wchar_t g_ptszEventName[100];
+
+static int CLUIFrameOnModulesLoad(WPARAM, LPARAM)
+{
+ mir_snwprintf(g_ptszEventName, L"mf_update_evt_%d", GetCurrentThreadId());
+ g_hEventThread = CreateEvent(nullptr, TRUE, FALSE, g_ptszEventName);
+ hThreadMFUpdate = mir_forkthread(MF_UpdateThread);
+ SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_IDLE);
+ CLUIFramesLoadMainMenu();
+ CLUIFramesCreateMenuForFrame(-1, nullptr, 000010000, false);
+ return 0;
+}
+
+static int CLUIFrameLangChanged(WPARAM, LPARAM)
+{
+ ApplyViewMode(0);
+ g_clistApi.pfnInvalidateRect(g_clistApi.hwndContactList, nullptr, TRUE);
+ return 0;
+}
+
+static int CLUIFrameOnModulesUnload(WPARAM, LPARAM)
+{
+ mf_updatethread_running = FALSE;
+
+ SetThreadPriority(hThreadMFUpdate, THREAD_PRIORITY_NORMAL);
+ SetEvent(g_hEventThread);
+ WaitForSingleObject(hThreadMFUpdate, 2000);
+ CloseHandle(g_hEventThread);
+
+ Menu_RemoveItem(cont.MIVisible);
+ Menu_RemoveItem(cont.MITitle);
+ Menu_RemoveItem(cont.MITBVisible);
+ Menu_RemoveItem(cont.MILock);
+ Menu_RemoveItem(cont.MIColl);
+ Menu_RemoveItem(cont.MIFloating);
+ Menu_RemoveItem(cont.MIAlignRoot);
+ Menu_RemoveItem(cont.MIAlignTop);
+ Menu_RemoveItem(cont.MIAlignClient);
+ Menu_RemoveItem(cont.MIAlignBottom);
+ Menu_RemoveItem(cont.MIBorder);
+ return 0;
+}
+
+/*
+ * wparam=hIcon
+ * return hImage on success,-1 on failure
+ */
+
+int LoadCLUIFramesModule(void)
+{
+ GapBetweenFrames = cfg::dat.gapBetweenFrames;
+
+ nFramescount = 0;
+
+ WNDCLASS wndclass = {};
+ wndclass.style = CS_DBLCLKS;
+ wndclass.lpfnWndProc = CLUIFrameTitleBarProc;
+ wndclass.hInstance = g_plugin.getInst();
+ wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wndclass.lpszClassName = CLUIFrameTitleBarClassName;
+ RegisterClass(&wndclass);
+
+ WNDCLASS cntclass = {};
+ cntclass.style = CS_DBLCLKS | CS_DROPSHADOW;
+ cntclass.lpfnWndProc = CLUIFrameContainerWndProc;
+ cntclass.hInstance = g_plugin.getInst();
+ cntclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ cntclass.lpszClassName = L"FramesContainer";
+ RegisterClass(&cntclass);
+
+ // create root menu
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x3931AC4, 0x7A32, 0x4D9C, 0x99, 0x92, 0x94, 0xD4, 0xB5, 0x9B, 0xD6, 0xB6);
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_FRAME);
+ mi.position = 3000090000;
+ mi.name.a = LPGEN("Frames");
+ mi.pszService = nullptr;
+ cont.MainMenuItem = Menu_AddMainMenuItem(&mi);
+ UNSET_UID(mi);
+
+ mi.root = cont.MainMenuItem;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_MIRANDA);
+ mi.flags = CMIF_UNMOVABLE;
+
+ // create "show all frames" menu
+ mi.uid.d[7]++;
+ mi.position = 4000090000;
+ mi.name.a = LPGEN("Show all frames");
+ mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMES;
+ Menu_AddMainMenuItem(&mi);
+
+ // create "show all titlebars" menu
+ mi.uid.d[7]++;
+ mi.position++;
+ mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_HELP);
+ mi.name.a = LPGEN("Show all title bars");
+ mi.pszService = MS_CLIST_FRAMES_SHOWALLFRAMESTB;
+ Menu_AddMainMenuItem(&mi);
+
+ // create "hide all titlebars" menu
+ mi.uid.d[7]++;
+ mi.position++;
+ mi.name.a = LPGEN("Hide all title bars");
+ mi.pszService = MS_CLIST_FRAMES_HIDEALLFRAMESTB;
+ Menu_AddMainMenuItem(&mi);
+
+ HookEvent(ME_SYSTEM_MODULESLOADED, CLUIFrameOnModulesLoad);
+ HookEvent(ME_CLIST_PREBUILDFRAMEMENU, CLUIFramesModifyContextMenuForFrame);
+ HookEvent(ME_CLIST_PREBUILDMAINMENU, CLUIFrameOnMainMenuBuild);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, CLUIFrameOnModulesUnload);
+ HookEvent(ME_LANGPACK_CHANGED, CLUIFrameLangChanged);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_ADDFRAME, CLUIFramesAddFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_REMOVEFRAME, CLUIFramesRemoveFrame);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_SETFRAMEOPTIONS, CLUIFramesSetFrameOptions);
+ CreateServiceFunction(MS_CLIST_FRAMES_GETFRAMEOPTIONS, CLUIFramesGetFrameOptions);
+ CreateServiceFunction(MS_CLIST_FRAMES_UPDATEFRAME, CLUIFramesUpdateFrame);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_SHFRAMETITLEBAR, CLUIFramesShowHideFrameTitleBar);
+ CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMESTB, CLUIFramesShowAllTitleBars);
+ CreateServiceFunction(MS_CLIST_FRAMES_HIDEALLFRAMESTB, CLUIFramesHideAllTitleBars);
+ CreateServiceFunction(MS_CLIST_FRAMES_SHFRAME, CLUIFramesShowHideFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_SHOWALLFRAMES, CLUIFramesShowAll);
+
+ CreateServiceFunction(MS_CLIST_FRAMES_ULFRAME, CLUIFramesLockUnlockFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_UCOLLFRAME, CLUIFramesCollapseUnCollapseFrame);
+ CreateServiceFunction(MS_CLIST_FRAMES_SETUNBORDER, CLUIFramesSetUnSetBorder);
+ CreateServiceFunction(MS_CLIST_FRAMES_SETSKINNED, CLUIFramesSetUnSetSkinned);
+
+ CreateServiceFunction(CLUIFRAMESSETALIGN, CLUIFramesSetAlign);
+ CreateServiceFunction(CLUIFRAMESMOVEDOWN, CLUIFramesMoveDown);
+ CreateServiceFunction(CLUIFRAMESMOVEUP, CLUIFramesMoveUp);
+
+ CreateServiceFunction(CLUIFRAMESSETALIGNALTOP, CLUIFramesSetAlignalTop);
+ CreateServiceFunction(CLUIFRAMESSETALIGNALCLIENT, CLUIFramesSetAlignalClient);
+ CreateServiceFunction(CLUIFRAMESSETALIGNALBOTTOM, CLUIFramesSetAlignalBottom);
+
+ CreateServiceFunction("Set_Floating", CLUIFrameSetFloat);
+ hWndExplorerToolBar = FindWindowExA(nullptr, nullptr, "Shell_TrayWnd", nullptr);
+ OnFrameTitleBarBackgroundChange();
+
+ FramesSysNotStarted = FALSE;
+ g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, db_get_dw(0, "CLUI", "clr_frameborder", GetSysColor(COLOR_3DDKSHADOW)));
+ return 0;
+}
+
+void LoadExtraIconModule()
+{
+ hStatusBarShowToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_SHOW_TOOLTIP);
+ hStatusBarHideToolTipEvent = CreateHookableEvent(ME_CLIST_FRAMES_SB_HIDE_TOOLTIP);
+}
+
+int UnLoadCLUIFramesModule(void)
+{
+ CLUIFramesOnClistResize((WPARAM)g_clistApi.hwndContactList, 0);
+ CLUIFramesStoreAllFrames();
+ DeleteObject(g_hPenCLUIFrames);
+
+ mir_cslock lck(csFrameHook);
+ FramesSysNotStarted = TRUE;
+ for (int i = 0; i < nFramescount; i++) {
+ FRAMEWND &F = Frames[i];
+ DestroyWindow(F.hWnd);
+ F.hWnd = (HWND)-1;
+ DestroyWindow(F.TitleBar.hwnd);
+ F.TitleBar.hwnd = (HWND)-1;
+ DestroyWindow(F.ContainerWnd);
+ F.ContainerWnd = (HWND)-1;
+ DestroyMenu(F.TitleBar.hmenu);
+
+ if (F.name != nullptr)
+ mir_free(F.name);
+ if (F.TitleBar.tbname != nullptr)
+ mir_free(F.TitleBar.tbname);
+ }
+ free(Frames);
+ Frames = nullptr;
+ nFramescount = 0;
+ UnregisterClass(CLUIFrameTitleBarClassName, g_plugin.getInst());
+ return 0;
+}
diff --git a/plugins/Clist_nicer/src/cluiopts.cpp b/plugins/Clist_nicer/src/cluiopts.cpp
index dff966b643..1ea16fee6b 100644
--- a/plugins/Clist_nicer/src/cluiopts.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/cluiservices.cpp b/plugins/Clist_nicer/src/cluiservices.cpp
index 3cf9271360..f5bb7a06cc 100644
--- a/plugins/Clist_nicer/src/cluiservices.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/config.cpp b/plugins/Clist_nicer/src/config.cpp
index 22160f77f6..022bf2a195 100644
--- a/plugins/Clist_nicer/src/config.cpp
+++ b/plugins/Clist_nicer/src/config.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/config.h b/plugins/Clist_nicer/src/config.h
index 3a2a947e2a..0fb24a4f9c 100644
--- a/plugins/Clist_nicer/src/config.h
+++ b/plugins/Clist_nicer/src/config.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/contact.cpp b/plugins/Clist_nicer/src/contact.cpp
index 9f626b4a9c..e0825ca835 100644
--- a/plugins/Clist_nicer/src/contact.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/extBackg.cpp b/plugins/Clist_nicer/src/extBackg.cpp
index c6dc99c19c..5655d67a07 100644
--- a/plugins/Clist_nicer/src/extBackg.cpp
+++ b/plugins/Clist_nicer/src/extBackg.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/extBackg.h b/plugins/Clist_nicer/src/extBackg.h
index e418eff6ca..6892e71007 100644
--- a/plugins/Clist_nicer/src/extBackg.h
+++ b/plugins/Clist_nicer/src/extBackg.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/groupmenu.cpp b/plugins/Clist_nicer/src/groupmenu.cpp
index 0688aea6fa..24f68f487d 100644
--- a/plugins/Clist_nicer/src/groupmenu.cpp
+++ b/plugins/Clist_nicer/src/groupmenu.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/plugins/Clist_nicer/src/init.cpp b/plugins/Clist_nicer/src/init.cpp
index 5fe0bbcd0d..a46350cb87 100644
--- a/plugins/Clist_nicer/src/init.cpp
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/rowheight_funcs.cpp b/plugins/Clist_nicer/src/rowheight_funcs.cpp
index 7f2a5722e1..6a44596915 100644
--- a/plugins/Clist_nicer/src/rowheight_funcs.cpp
+++ b/plugins/Clist_nicer/src/rowheight_funcs.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/statusbar.cpp b/plugins/Clist_nicer/src/statusbar.cpp
index 3850a1b69c..71cbff13f6 100644
--- a/plugins/Clist_nicer/src/statusbar.cpp
+++ b/plugins/Clist_nicer/src/statusbar.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/stdafx.cxx b/plugins/Clist_nicer/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/Clist_nicer/src/stdafx.cxx
+++ b/plugins/Clist_nicer/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/plugins/Clist_nicer/src/stdafx.h b/plugins/Clist_nicer/src/stdafx.h
index 4cbd1fbf71..6762de295e 100644
--- a/plugins/Clist_nicer/src/stdafx.h
+++ b/plugins/Clist_nicer/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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/viewmodes.cpp b/plugins/Clist_nicer/src/viewmodes.cpp
index 7a9e410a58..8d4b8e264f 100644
--- a/plugins/Clist_nicer/src/viewmodes.cpp
+++ b/plugins/Clist_nicer/src/viewmodes.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Clist_nicer/src/wallpaper.cpp b/plugins/Clist_nicer/src/wallpaper.cpp
index e02498c784..1aa486116e 100644
--- a/plugins/Clist_nicer/src/wallpaper.cpp
+++ b/plugins/Clist_nicer/src/wallpaper.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Cln_skinedit/src/main.cpp b/plugins/Cln_skinedit/src/main.cpp
index e0b33ce15f..6f7c76632c 100644
--- a/plugins/Cln_skinedit/src/main.cpp
+++ b/plugins/Cln_skinedit/src/main.cpp
@@ -1,900 +1,900 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-04 Miranda ICQ/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 ID_EXTBKSEPARATOR 40200
-
-CMPlugin g_plugin;
-
-StatusItems_t **StatusItems;
-ChangedSItems_t ChangedSItems = { 0 };
-
-static int LastModifiedItem = -1;
-static int last_selcount = 0;
-static int last_indizes[64];
-static int ID_EXTBK_LAST = 0, ID_EXTBK_FIRST = 0;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx = {
- sizeof(PLUGININFOEX),
- __PLUGIN_NAME,
- PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
- __DESCRIPTION,
- __AUTHOR,
- __COPYRIGHT,
- __AUTHORWEB,
- UNICODE_AWARE,
- // {21948C89-B549-4C9D-8B4F-3F3726EC6B4B}
- {0x21948c89, 0xb549, 0x4c9d, {0x8b, 0x4f, 0x3f, 0x37, 0x26, 0xec, 0x6b, 0x4b}}
-};
-
-CMPlugin::CMPlugin() :
- PLUGIN<CMPlugin>("Skin editor", pluginInfoEx)
-{}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// prototypes
-
-static void ChangeControlItems(HWND hwndDlg, int status, int except);
-static BOOL CheckItem(int item, HWND hwndDlg);
-
-static void ReActiveCombo(HWND hwndDlg)
-{
- if (IsDlgButtonChecked(hwndDlg, IDC_IGNORE)) {
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
-
- EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
-
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE);
- }
- else {
- ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
-
- EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
-
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
- }
-}
-
-// enabled or disabled the whole status controlitems group (with exceptional control)
-static void ChangeControlItems(HWND hwndDlg, int status, int except)
-{
- if (except != IDC_GRADIENT)
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT), status);
- if (except != IDC_GRADIENT_LR)
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), status);
- if (except != IDC_GRADIENT_RL)
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), status);
- if (except != IDC_GRADIENT_TB)
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), status);
- if (except != IDC_GRADIENT_BT)
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), status);
- if (except != IDC_CORNER)
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER), status);
- if (except != IDC_CORNER_TL)
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status);
- if (except != IDC_CORNER_TR)
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), status);
- if (except != IDC_CORNER_BR)
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), status);
- if (except != IDC_CORNER_BL)
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), status);
- if (except != IDC_CORNER_TL)
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status);
- if (except != IDC_MARGINLABLE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MARGINLABLE), status);
- if (except != IDC_MRGN_TOP)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP), status);
- if (except != IDC_MRGN_RIGHT)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT), status);
- if (except != IDC_MRGN_BOTTOM)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM), status);
- if (except != IDC_MRGN_LEFT)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT), status);
- if (except != IDC_MRGN_TOP_SPIN)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP_SPIN), status);
- if (except != IDC_MRGN_RIGHT_SPIN)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT_SPIN), status);
- if (except != IDC_MRGN_BOTTOM_SPIN)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM_SPIN), status);
- if (except != IDC_MRGN_LEFT_SPIN)
- EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT_SPIN), status);
- if (except != IDC_BASECOLOUR)
- EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR), status);
- if (except != IDC_COLORLABLE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLORLABLE), status);
- if (except != IDC_BASECOLOUR2)
- EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), status);
- if (except != IDC_COLOR2LABLE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), status);
- if (except != IDC_COLOR2_TRANSPARENT)
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2_TRANSPARENT), status);
- if (except != IDC_TEXTCOLOUR)
- EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOUR), status);
- if (except != IDC_TEXTCOLOURLABLE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOURLABLE), status);
-
- if (except != IDC_ALPHA)
- EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHA), status);
- if (except != IDC_ALPHASPIN)
- EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHASPIN), status);
- if (except != IDC_ALPHALABLE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHALABLE), status);
- if (except != IDC_IGNORE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORE), status);
-
- if (except != IDC_BORDERTYPE)
- EnableWindow(GetDlgItem(hwndDlg, IDC_BORDERTYPE), status);
-}
-
-static void FillOptionDialogByStatusItem(HWND hwndDlg, StatusItems_t *item)
-{
- char itoabuf[15];
- uint32_t ret;
- int index;
-
- CheckDlgButton(hwndDlg, IDC_IGNORE, (item->IGNORED) ? BST_CHECKED : BST_UNCHECKED);
-
- CheckDlgButton(hwndDlg, IDC_GRADIENT, (item->GRADIENT & GRADIENT_ACTIVE) ? BST_CHECKED : BST_UNCHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), item->GRADIENT & GRADIENT_ACTIVE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), item->GRADIENT & GRADIENT_ACTIVE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), item->GRADIENT & GRADIENT_ACTIVE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), item->GRADIENT & GRADIENT_ACTIVE);
- CheckDlgButton(hwndDlg, IDC_GRADIENT_LR, (item->GRADIENT & GRADIENT_LR) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_GRADIENT_RL, (item->GRADIENT & GRADIENT_RL) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_GRADIENT_TB, (item->GRADIENT & GRADIENT_TB) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_GRADIENT_BT, (item->GRADIENT & GRADIENT_BT) ? BST_CHECKED : BST_UNCHECKED);
-
- CheckDlgButton(hwndDlg, IDC_CORNER, (item->CORNER & CORNER_ACTIVE) ? BST_CHECKED : BST_UNCHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), item->CORNER & CORNER_ACTIVE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), item->CORNER & CORNER_ACTIVE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), item->CORNER & CORNER_ACTIVE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), item->CORNER & CORNER_ACTIVE);
-
- CheckDlgButton(hwndDlg, IDC_CORNER_TL, (item->CORNER & CORNER_TL) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_CORNER_TR, (item->CORNER & CORNER_TR) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_CORNER_BR, (item->CORNER & CORNER_BR) ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_CORNER_BL, (item->CORNER & CORNER_BL) ? BST_CHECKED : BST_UNCHECKED);
-
- ret = item->COLOR;
- SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR);
- SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETCOLOUR, 0, ret);
-
- ret = item->COLOR2;
- SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR2);
- SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETCOLOUR, 0, ret);
-
- CheckDlgButton(hwndDlg, IDC_COLOR2_TRANSPARENT, (item->COLOR2_TRANSPARENT) ? BST_CHECKED : BST_UNCHECKED);
-
- ret = item->TEXTCOLOR;
- SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_TEXTCOLOR);
- SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETCOLOUR, 0, ret);
-
- if (item->ALPHA == -1) {
- SetDlgItemTextA(hwndDlg, IDC_ALPHA, "");
- }
- else {
- ret = item->ALPHA;
- _itoa(ret, itoabuf, 10);
- SetDlgItemTextA(hwndDlg, IDC_ALPHA, itoabuf);
- }
-
- if (item->MARGIN_LEFT == -1)
- SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, "");
- else {
- ret = item->MARGIN_LEFT;
- _itoa(ret, itoabuf, 10);
- SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, itoabuf);
- }
-
- if (item->MARGIN_TOP == -1)
- SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, "");
- else {
- ret = item->MARGIN_TOP;
- _itoa(ret, itoabuf, 10);
- SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, itoabuf);
- }
-
- if (item->MARGIN_RIGHT == -1)
- SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, "");
- else {
- ret = item->MARGIN_RIGHT;
- _itoa(ret, itoabuf, 10);
- SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, itoabuf);
- }
-
- if (item->MARGIN_BOTTOM == -1)
- SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, "");
- else {
- ret = item->MARGIN_BOTTOM;
- _itoa(ret, itoabuf, 10);
- SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, itoabuf);
- }
- if (item->BORDERSTYLE == -1)
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, 0, 0);
- else {
- index = 0;
- switch (item->BORDERSTYLE) {
- case 0:
- case -1:
- index = 0;
- break;
- case BDR_RAISEDOUTER:
- index = 1;
- break;
- case BDR_SUNKENINNER:
- index = 2;
- break;
- case EDGE_BUMP:
- index = 3;
- break;
- case EDGE_ETCHED:
- index = 4;
- break;
- }
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, (WPARAM)index, 0);
- }
- ReActiveCombo(hwndDlg);
-}
-// update dlg with selected item
-static void FillOptionDialogByCurrentSel(HWND hwndDlg)
-{
- int index = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0);
- int itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, index, 0);
- if (itemData != ID_EXTBKSEPARATOR) {
- LastModifiedItem = itemData - ID_EXTBK_FIRST;
-
- if (CheckItem(itemData - ID_EXTBK_FIRST, hwndDlg))
- FillOptionDialogByStatusItem(hwndDlg, StatusItems[itemData - ID_EXTBK_FIRST]);
- }
-}
-
-
-// enabled all status controls if the selected item is a separator
-static BOOL CheckItem(int item, HWND hwndDlg)
-{
- if (StatusItems[item]->statusID == ID_EXTBKSEPARATOR) {
- ChangeControlItems(hwndDlg, 0, 0);
- return FALSE;
- }
- else {
- ChangeControlItems(hwndDlg, 1, 0);
- return TRUE;
- }
-}
-
-static void SetChangedStatusItemFlag(WPARAM wParam, HWND hwndDlg)
-{
- if (LOWORD(wParam) != IDC_ITEMS
- && (GetDlgItem(hwndDlg, LOWORD(wParam)) == GetFocus() || HIWORD(wParam) == CPN_COLOURCHANGED)
- && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CPN_COLOURCHANGED)) {
- switch (LOWORD(wParam)) {
- case IDC_IGNORE:
- ChangedSItems.bIGNORED = TRUE; break;
- case IDC_GRADIENT:
- ChangedSItems.bGRADIENT = TRUE; break;
- case IDC_GRADIENT_LR:
- ChangedSItems.bGRADIENT = TRUE; break;
- case IDC_GRADIENT_RL:
- ChangedSItems.bGRADIENT = TRUE; break;
- case IDC_GRADIENT_BT:
- ChangedSItems.bGRADIENT = TRUE; break;
- case IDC_GRADIENT_TB:
- ChangedSItems.bGRADIENT = TRUE; break;
-
- case IDC_CORNER:
- ChangedSItems.bCORNER = TRUE; break;
- case IDC_CORNER_TL:
- ChangedSItems.bCORNER = TRUE; break;
- case IDC_CORNER_TR:
- ChangedSItems.bCORNER = TRUE; break;
- case IDC_CORNER_BR:
- ChangedSItems.bCORNER = TRUE; break;
- case IDC_CORNER_BL:
- ChangedSItems.bCORNER = TRUE; break;
-
- case IDC_BASECOLOUR:
- ChangedSItems.bCOLOR = TRUE; break;
- case IDC_BASECOLOUR2:
- ChangedSItems.bCOLOR2 = TRUE; break;
- case IDC_COLOR2_TRANSPARENT:
- ChangedSItems.bCOLOR2_TRANSPARENT = TRUE; break;
- case IDC_TEXTCOLOUR:
- ChangedSItems.bTEXTCOLOR = TRUE; break;
-
- case IDC_ALPHA:
- ChangedSItems.bALPHA = TRUE; break;
- case IDC_ALPHASPIN:
- ChangedSItems.bALPHA = TRUE; break;
-
- case IDC_MRGN_LEFT:
- ChangedSItems.bMARGIN_LEFT = TRUE; break;
- case IDC_MRGN_LEFT_SPIN:
- ChangedSItems.bMARGIN_LEFT = TRUE; break;
-
- case IDC_MRGN_TOP:
- ChangedSItems.bMARGIN_TOP = TRUE; break;
- case IDC_MRGN_TOP_SPIN:
- ChangedSItems.bMARGIN_TOP = TRUE; break;
-
- case IDC_MRGN_RIGHT:
- ChangedSItems.bMARGIN_RIGHT = TRUE; break;
- case IDC_MRGN_RIGHT_SPIN:
- ChangedSItems.bMARGIN_RIGHT = TRUE; break;
-
- case IDC_MRGN_BOTTOM:
- ChangedSItems.bMARGIN_BOTTOM = TRUE; break;
- case IDC_MRGN_BOTTOM_SPIN:
- ChangedSItems.bMARGIN_BOTTOM = TRUE; break;
-
- case IDC_BORDERTYPE:
- ChangedSItems.bBORDERSTYLE = TRUE; break;
- }
- }
-}
-
-// updates the struct with the changed dlg item
-static void UpdateStatusStructSettingsFromOptDlg(HWND hwndDlg, int index)
-{
- char buf[15];
- ULONG bdrtype;
- StatusItems_t *p = StatusItems[index];
-
- if (ChangedSItems.bIGNORED)
- p->IGNORED = IsDlgButtonChecked(hwndDlg, IDC_IGNORE);
-
- if (ChangedSItems.bGRADIENT) {
- p->GRADIENT = GRADIENT_NONE;
- if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT))
- p->GRADIENT |= GRADIENT_ACTIVE;
- if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_LR))
- p->GRADIENT |= GRADIENT_LR;
- if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_RL))
- p->GRADIENT |= GRADIENT_RL;
- if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_TB))
- p->GRADIENT |= GRADIENT_TB;
- if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_BT))
- p->GRADIENT |= GRADIENT_BT;
- }
- if (ChangedSItems.bCORNER) {
- p->CORNER = CORNER_NONE;
- if (IsDlgButtonChecked(hwndDlg, IDC_CORNER))
- p->CORNER |= CORNER_ACTIVE;
- if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TL))
- p->CORNER |= CORNER_TL;
- if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TR))
- p->CORNER |= CORNER_TR;
- if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BR))
- p->CORNER |= CORNER_BR;
- if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BL))
- p->CORNER |= CORNER_BL;
- }
-
- if (ChangedSItems.bCOLOR)
- p->COLOR = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_GETCOLOUR, 0, 0);
-
- if (ChangedSItems.bCOLOR2)
- p->COLOR2 = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_GETCOLOUR, 0, 0);
-
- if (ChangedSItems.bCOLOR2_TRANSPARENT)
- p->COLOR2_TRANSPARENT = IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT);
-
- if (ChangedSItems.bTEXTCOLOR)
- p->TEXTCOLOR = SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_GETCOLOUR, 0, 0);
-
- if (ChangedSItems.bALPHA) {
- GetDlgItemTextA(hwndDlg, IDC_ALPHA, buf, 10); // can be removed now
- if (buf[0] != 0)
- p->ALPHA = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_GETPOS, 0, 0);
- }
-
- if (ChangedSItems.bMARGIN_LEFT) {
- GetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, buf, 10);
- if (buf[0] != 0)
- p->MARGIN_LEFT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_GETPOS, 0, 0);
- }
-
- if (ChangedSItems.bMARGIN_TOP) {
- GetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, buf, 10);
- if (buf[0] != 0)
- p->MARGIN_TOP = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_GETPOS, 0, 0);
- }
-
- if (ChangedSItems.bMARGIN_RIGHT) {
- GetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, buf, 10);
- if (buf[0] != 0)
- p->MARGIN_RIGHT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_GETPOS, 0, 0);
- }
-
- if (ChangedSItems.bMARGIN_BOTTOM) {
- GetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, buf, 10);
- if (buf[0] != 0)
- p->MARGIN_BOTTOM = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_GETPOS, 0, 0);
- }
- if (ChangedSItems.bBORDERSTYLE) {
- bdrtype = SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_GETCURSEL, 0, 0);
- if (bdrtype == CB_ERR)
- p->BORDERSTYLE = 0;
- else {
- switch (bdrtype) {
- case 0:
- p->BORDERSTYLE = 0;
- break;
- case 1:
- p->BORDERSTYLE = BDR_RAISEDOUTER;
- break;
- case 2:
- p->BORDERSTYLE = BDR_SUNKENINNER;
- break;
- case 3:
- p->BORDERSTYLE = EDGE_BUMP;
- break;
- case 4:
- p->BORDERSTYLE = EDGE_ETCHED;
- break;
- default:
- p->BORDERSTYLE = 0;
- break;
- }
- }
- }
-}
-
-static void SaveLatestChanges(HWND hwndDlg)
-{
- int n, itemData;
- // process old selection
- if (last_selcount > 0) {
- for (n = 0; n < last_selcount; n++) {
- itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0);
- if (itemData != ID_EXTBKSEPARATOR) {
- UpdateStatusStructSettingsFromOptDlg(hwndDlg, itemData - ID_EXTBK_FIRST);
- }
- }
- }
-
- // reset bChange
- ChangedSItems.bALPHA = FALSE;
- ChangedSItems.bGRADIENT = FALSE;
- ChangedSItems.bCORNER = FALSE;
- ChangedSItems.bCOLOR = FALSE;
- ChangedSItems.bCOLOR2 = FALSE;
- ChangedSItems.bCOLOR2_TRANSPARENT = FALSE;
- ChangedSItems.bTEXTCOLOR = FALSE;
- ChangedSItems.bMARGIN_LEFT = FALSE;
- ChangedSItems.bMARGIN_TOP = FALSE;
- ChangedSItems.bMARGIN_RIGHT = FALSE;
- ChangedSItems.bMARGIN_BOTTOM = FALSE;
- ChangedSItems.bIGNORED = FALSE;
- ChangedSItems.bBORDERSTYLE = FALSE;
-}
-
-static UINT _controls_to_refresh[] = {
- IDC_BORDERTYPE,
- IDC_3DDARKCOLOR,
- IDC_3DLIGHTCOLOR,
- IDC_MRGN_BOTTOM,
- IDC_MRGN_LEFT,
- IDC_ALPHASPIN,
- IDC_CORNER,
- IDC_MRGN_TOP_SPIN,
- IDC_MRGN_RIGHT_SPIN,
- IDC_MRGN_BOTTOM_SPIN,
- IDC_MRGN_LEFT_SPIN,
- IDC_GRADIENT,
- IDC_GRADIENT_LR,
- IDC_GRADIENT_RL,
- IDC_GRADIENT_TB,
- IDC_BASECOLOUR,
- IDC_ALPHA,
- IDC_MRGN_TOP,
- IDC_MRGN_RIGHT,
- IDC_GRADIENT_BT,
- IDC_BASECOLOUR2,
- IDC_TEXTCOLOUR,
- IDC_CORNER_TL,
- IDC_CORNER_TR,
- IDC_CORNER_BR,
- IDC_CORNER_BL,
- IDC_IGNORE,
- IDC_ALPHALABLE,
- IDC_COLOR2LABLE,
- IDC_COLORLABLE,
- IDC_TEXTCOLOURLABLE,
- IDC_COLOR2_TRANSPARENT,
- 0
-};
-
-static void RefreshControls(HWND hwnd)
-{
- for (int i = 0; _controls_to_refresh[i]; i++)
- InvalidateRect(GetDlgItem(hwnd, _controls_to_refresh[i]), nullptr, FALSE);
-}
-
-// wenn die listbox geändert wurde
-static void OnListItemsChange(HWND hwndDlg)
-{
- SendMessage(hwndDlg, WM_SETREDRAW, FALSE, 0);
- SaveLatestChanges(hwndDlg);
-
- // set new selection
- last_selcount = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELCOUNT, 0, 0);
- if (last_selcount > 0) {
- int n, itemData, first_item;
-
- // get selected indizes
- SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELITEMS, 64, (LPARAM)last_indizes);
-
- // initialize with first items value
-
- first_item = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[0], 0) - ID_EXTBK_FIRST;
- StatusItems_t *pFirst = StatusItems[first_item];
- StatusItems_t DialogSettingForMultiSel = *StatusItems[first_item];
- for (n = 0; n < last_selcount; n++) {
- itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0);
- if (itemData == ID_EXTBKSEPARATOR)
- continue;
-
- StatusItems_t *p = StatusItems[itemData - ID_EXTBK_FIRST];
- if (p->ALPHA != pFirst->ALPHA)
- DialogSettingForMultiSel.ALPHA = -1;
- if (p->COLOR != pFirst->COLOR)
- DialogSettingForMultiSel.COLOR = CLCDEFAULT_COLOR;
- if (p->COLOR2 != pFirst->COLOR2)
- DialogSettingForMultiSel.COLOR2 = CLCDEFAULT_COLOR2;
- if (p->COLOR2_TRANSPARENT != pFirst->COLOR2_TRANSPARENT)
- DialogSettingForMultiSel.COLOR2_TRANSPARENT = CLCDEFAULT_COLOR2_TRANSPARENT;
- if (p->TEXTCOLOR != pFirst->TEXTCOLOR)
- DialogSettingForMultiSel.TEXTCOLOR = CLCDEFAULT_TEXTCOLOR;
- if (p->CORNER != pFirst->CORNER)
- DialogSettingForMultiSel.CORNER = CLCDEFAULT_CORNER;
- if (p->GRADIENT != pFirst->GRADIENT)
- DialogSettingForMultiSel.GRADIENT = CLCDEFAULT_GRADIENT;
- if (p->IGNORED != pFirst->IGNORED)
- DialogSettingForMultiSel.IGNORED = CLCDEFAULT_IGNORE;
- if (p->MARGIN_BOTTOM != pFirst->MARGIN_BOTTOM)
- DialogSettingForMultiSel.MARGIN_BOTTOM = -1;
- if (p->MARGIN_LEFT != pFirst->MARGIN_LEFT)
- DialogSettingForMultiSel.MARGIN_LEFT = -1;
- if (p->MARGIN_RIGHT != pFirst->MARGIN_RIGHT)
- DialogSettingForMultiSel.MARGIN_RIGHT = -1;
- if (p->MARGIN_TOP != pFirst->MARGIN_TOP)
- DialogSettingForMultiSel.MARGIN_TOP = -1;
- if (p->BORDERSTYLE != pFirst->BORDERSTYLE)
- DialogSettingForMultiSel.BORDERSTYLE = -1;
- }
-
- if (last_selcount == 1 && pFirst->statusID == ID_EXTBKSEPARATOR) {
- ChangeControlItems(hwndDlg, 0, 0);
- last_selcount = 0;
- }
- else
- ChangeControlItems(hwndDlg, 1, 0);
- FillOptionDialogByStatusItem(hwndDlg, &DialogSettingForMultiSel);
- InvalidateRect(GetDlgItem(hwndDlg, IDC_ITEMS), nullptr, FALSE);
- }
- SendMessage(hwndDlg, WM_SETREDRAW, TRUE, 0);
- RefreshControls(hwndDlg);
-}
-
-// fills the combobox of the options dlg for the first time
-static void FillItemList(HWND hwndDlg)
-{
- int n, iOff;
- UINT item;
-
- for (n = 0; n < ID_EXTBK_LAST - ID_EXTBK_FIRST; n++) {
- iOff = 0;
- if (strstr(StatusItems[n]->szName, "{-}")) {
- item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)"------------------------");
- SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBKSEPARATOR);
- iOff = 3;
- }
- item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)StatusItems[n]->szName[iOff]);
- SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBK_FIRST + n);
- }
-}
-
-static INT_PTR CALLBACK SkinEdit_ExtBkDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- SKINDESCRIPTION *psd = (SKINDESCRIPTION *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
- if (psd) {
- ID_EXTBK_FIRST = psd->firstItem;
- ID_EXTBK_LAST = psd->lastItem;
- StatusItems = psd->StatusItems;
- }
-
- switch (msg) {
- case WM_INITDIALOG:
- psd = (SKINDESCRIPTION *)malloc(sizeof(SKINDESCRIPTION));
- if (psd == nullptr)
- return FALSE;
- memset(psd, 0, sizeof(SKINDESCRIPTION));
- memcpy(psd, (void *)lParam, sizeof(SKINDESCRIPTION));
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)psd);
-
- ID_EXTBK_FIRST = psd->firstItem;
- ID_EXTBK_LAST = psd->lastItem;
- StatusItems = psd->StatusItems;
-
- TranslateDialogDefault(hwndDlg);
- FillItemList(hwndDlg);
- SendMessage(hwndDlg, WM_USER + 101, 0, 0);
-
- psd->hMenuItems = CreatePopupMenu();
- AppendMenu(psd->hMenuItems, MF_STRING | MF_DISABLED, (UINT_PTR)0, LPGENW("Copy from"));
- AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr);
-
- {
- for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) {
- int iOff = StatusItems[i - ID_EXTBK_FIRST]->szName[0] == '{' ? 3 : 0;
- if (iOff)
- AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr);
- AppendMenuA(psd->hMenuItems, MF_STRING, (UINT_PTR)i, &StatusItems[i - ID_EXTBK_FIRST]->szName[iOff]);
- }
- }
- return TRUE;
-
- case WM_USER + 101:
- SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
- SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
- SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
- SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
- SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
-
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("<None>"));
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Raised"));
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Sunken"));
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Bumped"));
- SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Etched"));
-
- SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3ddark", RGB(224, 224, 224)));
- SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3dbright", RGB(224, 224, 224)));
- return 0;
-
- case WM_DRAWITEM:
- {
- DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
- int iItem = dis->itemData;
- StatusItems_t *item = nullptr;
-
- SetBkMode(dis->hDC, TRANSPARENT);
- FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
-
- if (iItem >= ID_EXTBK_FIRST && iItem < ID_EXTBK_LAST)
- item = StatusItems[iItem - ID_EXTBK_FIRST];
-
- if (dis->itemState & ODS_SELECTED && iItem != ID_EXTBKSEPARATOR) {
- FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
- SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
- }
- else {
- FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
- if (item && item->IGNORED)
- SetTextColor(dis->hDC, RGB(255, 0, 0));
- else
- SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT));
- }
- if (iItem == ID_EXTBKSEPARATOR) {
- HPEN hPen, hPenOld;
- POINT pt;
-
- hPen = CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWTEXT));
- hPenOld = (HPEN)SelectObject(dis->hDC, hPen);
-
- MoveToEx(dis->hDC, dis->rcItem.left, (dis->rcItem.top + dis->rcItem.bottom) / 2, &pt);
- LineTo(dis->hDC, dis->rcItem.right, (dis->rcItem.top + dis->rcItem.bottom) / 2);
- SelectObject(dis->hDC, hPenOld);
- DeleteObject((HGDIOBJ)hPen);
- }
- else if (item) {
- char *szName = item->szName[0] == '{' ? &item->szName[3] : item->szName;
- TextOutA(dis->hDC, dis->rcItem.left, dis->rcItem.top, szName, (int)mir_strlen(szName));
- }
- return TRUE;
- }
-
- case WM_CONTEXTMENU:
- {
- HWND hwndList = GetDlgItem(hwndDlg, IDC_ITEMS);
-
- POINT pt;
- GetCursorPos(&pt);
-
- RECT rc;
- GetWindowRect(hwndList, &rc);
-
- if (PtInRect(&rc, pt)) {
- int iSelection = (int)TrackPopupMenu(psd->hMenuItems, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr);
-
- if (iSelection >= ID_EXTBK_FIRST && iSelection < ID_EXTBK_LAST) {
- iSelection -= ID_EXTBK_FIRST;
- StatusItems_t *pSel = StatusItems[iSelection];
-
- for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) {
- if (SendMessage(hwndList, LB_GETSEL, i - ID_EXTBK_FIRST, 0) <= 0)
- continue;
-
- int iIndex = SendMessage(hwndList, LB_GETITEMDATA, i - ID_EXTBK_FIRST, 0);
- iIndex -= ID_EXTBK_FIRST;
- if (iIndex < 0)
- continue;
-
- StatusItems_t *p = StatusItems[iIndex];
- p->ALPHA = pSel->ALPHA;
- p->BORDERSTYLE = pSel->BORDERSTYLE;
- p->COLOR = pSel->COLOR;
- p->COLOR2 = pSel->COLOR2;
- p->COLOR2_TRANSPARENT = pSel->COLOR2_TRANSPARENT;
- p->CORNER = pSel->CORNER;
- p->GRADIENT = pSel->GRADIENT;
- p->IGNORED = pSel->IGNORED;
- p->imageItem = pSel->imageItem;
- p->MARGIN_BOTTOM = pSel->MARGIN_BOTTOM;
- p->MARGIN_LEFT = pSel->MARGIN_LEFT;
- p->MARGIN_RIGHT = pSel->MARGIN_RIGHT;
- p->MARGIN_TOP = pSel->MARGIN_TOP;
- p->TEXTCOLOR = pSel->TEXTCOLOR;
- }
- OnListItemsChange(hwndDlg);
- }
- }
- }
- break;
-
- case WM_COMMAND:
- // this will check if the user changed some actual statusitems values
- // if yes the flag bChanged will be set to TRUE
- SetChangedStatusItemFlag(wParam, hwndDlg);
- switch (LOWORD(wParam)) {
- case IDC_ITEMS:
- if (HIWORD(wParam) != LBN_SELCHANGE)
- return FALSE;
- {
- int iItem = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0), 0);
- if (iItem == ID_EXTBKSEPARATOR)
- return FALSE;
- }
- OnListItemsChange(hwndDlg);
- if (psd->pfnClcOptionsChanged)
- psd->pfnClcOptionsChanged();
- break;
- case IDC_GRADIENT:
- ReActiveCombo(hwndDlg);
- break;
- case IDC_CORNER:
- ReActiveCombo(hwndDlg);
- break;
- case IDC_IGNORE:
- ReActiveCombo(hwndDlg);
- break;
- case IDC_COLOR2_TRANSPARENT:
- ReActiveCombo(hwndDlg);
- break;
- case IDC_BORDERTYPE:
- break;
- }
- if ((LOWORD(wParam) == IDC_ALPHA || LOWORD(wParam) == IDC_MRGN_LEFT || LOWORD(wParam) == IDC_MRGN_BOTTOM || LOWORD(wParam) == IDC_MRGN_TOP || LOWORD(wParam) == IDC_MRGN_RIGHT) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
- return 0;
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case 0:
- switch (((LPNMHDR)lParam)->code) {
- case PSN_APPLY:
- // save user made changes
- SaveLatestChanges(hwndDlg);
- // save struct to DB
- if (psd->pfnSaveCompleteStruct)
- psd->pfnSaveCompleteStruct();
- db_set_dw(0, "CLCExt", "3dbright", SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_GETCOLOUR, 0, 0));
- db_set_dw(0, "CLCExt", "3ddark", SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_GETCOLOUR, 0, 0));
-
- if (psd->pfnClcOptionsChanged)
- psd->pfnClcOptionsChanged();
- if (psd->hwndCLUI) {
- SendMessage(psd->hwndCLUI, WM_SIZE, 0, 0);
- PostMessage(psd->hwndCLUI, WM_USER + 100, 0, 0); // CLUIINTM_REDRAW
- }
- break;
- }
- }
- break;
- case WM_DESTROY:
- DestroyMenu(psd->hMenuItems);
- break;
- case WM_NCDESTROY:
- free(psd);
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- break;
- }
- return FALSE;
-}
-
-/*
- * unimplemented
-*/
-
-static INT_PTR SkinEdit_FillByCurrentSel(WPARAM wParam, LPARAM)
-{
- if (wParam)
- FillOptionDialogByCurrentSel((HWND)wParam);
- return 0;
-}
-
-/*
- * service function
- * creates additional tab pages under the given parent window handle
- * expects a SKINDESCRIPTON * in lParam
-*/
-
-static INT_PTR SkinEdit_Invoke(WPARAM, LPARAM lParam)
-{
- SKINDESCRIPTION *psd = (SKINDESCRIPTION *)lParam;
- TCITEM tci = { 0 };
- RECT rcClient;
- int iTabs;
-
- if (psd->cbSize != sizeof(SKINDESCRIPTION))
- return 0;
-
- iTabs = TabCtrl_GetItemCount(psd->hWndTab);
- GetClientRect(psd->hWndParent, &rcClient);
-
- tci.mask = TCIF_PARAM | TCIF_TEXT;
- tci.lParam = (LPARAM)CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SKINITEMEDIT), psd->hWndParent, SkinEdit_ExtBkDlgProc, (LPARAM)psd);
-
- tci.pszText = TranslateT("Skin items");
- TabCtrl_InsertItem(psd->hWndTab, iTabs++, &tci);
- MoveWindow((HWND)tci.lParam, 5, 25, rcClient.right - 9, rcClient.bottom - 60, 1);
- psd->hwndSkinEdit = (HWND)tci.lParam;
- return (INT_PTR)psd->hwndSkinEdit;
-}
-
-int CMPlugin::Load()
-{
- CreateServiceFunction(MS_CLNSE_INVOKE, SkinEdit_Invoke);
- CreateServiceFunction(MS_CLNSE_FILLBYCURRENTSEL, SkinEdit_FillByCurrentSel);
- 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-04 Miranda ICQ/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 ID_EXTBKSEPARATOR 40200
+
+CMPlugin g_plugin;
+
+StatusItems_t **StatusItems;
+ChangedSItems_t ChangedSItems = { 0 };
+
+static int LastModifiedItem = -1;
+static int last_selcount = 0;
+static int last_indizes[64];
+static int ID_EXTBK_LAST = 0, ID_EXTBK_FIRST = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {21948C89-B549-4C9D-8B4F-3F3726EC6B4B}
+ {0x21948c89, 0xb549, 0x4c9d, {0x8b, 0x4f, 0x3f, 0x37, 0x26, 0xec, 0x6b, 0x4b}}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>("Skin editor", pluginInfoEx)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// prototypes
+
+static void ChangeControlItems(HWND hwndDlg, int status, int except);
+static BOOL CheckItem(int item, HWND hwndDlg);
+
+static void ReActiveCombo(HWND hwndDlg)
+{
+ if (IsDlgButtonChecked(hwndDlg, IDC_IGNORE)) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE);
+ }
+ else {
+ ChangeControlItems(hwndDlg, BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_IGNORE), IDC_IGNORE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), IsDlgButtonChecked(hwndDlg, IDC_GRADIENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), BST_UNCHECKED == IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), IsDlgButtonChecked(hwndDlg, IDC_CORNER));
+ }
+}
+
+// enabled or disabled the whole status controlitems group (with exceptional control)
+static void ChangeControlItems(HWND hwndDlg, int status, int except)
+{
+ if (except != IDC_GRADIENT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT), status);
+ if (except != IDC_GRADIENT_LR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), status);
+ if (except != IDC_GRADIENT_RL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), status);
+ if (except != IDC_GRADIENT_TB)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), status);
+ if (except != IDC_GRADIENT_BT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), status);
+ if (except != IDC_CORNER)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER), status);
+ if (except != IDC_CORNER_TL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status);
+ if (except != IDC_CORNER_TR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), status);
+ if (except != IDC_CORNER_BR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), status);
+ if (except != IDC_CORNER_BL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), status);
+ if (except != IDC_CORNER_TL)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), status);
+ if (except != IDC_MARGINLABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MARGINLABLE), status);
+ if (except != IDC_MRGN_TOP)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP), status);
+ if (except != IDC_MRGN_RIGHT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT), status);
+ if (except != IDC_MRGN_BOTTOM)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM), status);
+ if (except != IDC_MRGN_LEFT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT), status);
+ if (except != IDC_MRGN_TOP_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_TOP_SPIN), status);
+ if (except != IDC_MRGN_RIGHT_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_RIGHT_SPIN), status);
+ if (except != IDC_MRGN_BOTTOM_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_BOTTOM_SPIN), status);
+ if (except != IDC_MRGN_LEFT_SPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MRGN_LEFT_SPIN), status);
+ if (except != IDC_BASECOLOUR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR), status);
+ if (except != IDC_COLORLABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLORLABLE), status);
+ if (except != IDC_BASECOLOUR2)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BASECOLOUR2), status);
+ if (except != IDC_COLOR2LABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2LABLE), status);
+ if (except != IDC_COLOR2_TRANSPARENT)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR2_TRANSPARENT), status);
+ if (except != IDC_TEXTCOLOUR)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOUR), status);
+ if (except != IDC_TEXTCOLOURLABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TEXTCOLOURLABLE), status);
+
+ if (except != IDC_ALPHA)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHA), status);
+ if (except != IDC_ALPHASPIN)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHASPIN), status);
+ if (except != IDC_ALPHALABLE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_ALPHALABLE), status);
+ if (except != IDC_IGNORE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORE), status);
+
+ if (except != IDC_BORDERTYPE)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BORDERTYPE), status);
+}
+
+static void FillOptionDialogByStatusItem(HWND hwndDlg, StatusItems_t *item)
+{
+ char itoabuf[15];
+ uint32_t ret;
+ int index;
+
+ CheckDlgButton(hwndDlg, IDC_IGNORE, (item->IGNORED) ? BST_CHECKED : BST_UNCHECKED);
+
+ CheckDlgButton(hwndDlg, IDC_GRADIENT, (item->GRADIENT & GRADIENT_ACTIVE) ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_LR), item->GRADIENT & GRADIENT_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_RL), item->GRADIENT & GRADIENT_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_TB), item->GRADIENT & GRADIENT_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_GRADIENT_BT), item->GRADIENT & GRADIENT_ACTIVE);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_LR, (item->GRADIENT & GRADIENT_LR) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_RL, (item->GRADIENT & GRADIENT_RL) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_TB, (item->GRADIENT & GRADIENT_TB) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_GRADIENT_BT, (item->GRADIENT & GRADIENT_BT) ? BST_CHECKED : BST_UNCHECKED);
+
+ CheckDlgButton(hwndDlg, IDC_CORNER, (item->CORNER & CORNER_ACTIVE) ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TL), item->CORNER & CORNER_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_TR), item->CORNER & CORNER_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BR), item->CORNER & CORNER_ACTIVE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CORNER_BL), item->CORNER & CORNER_ACTIVE);
+
+ CheckDlgButton(hwndDlg, IDC_CORNER_TL, (item->CORNER & CORNER_TL) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CORNER_TR, (item->CORNER & CORNER_TR) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CORNER_BR, (item->CORNER & CORNER_BR) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CORNER_BL, (item->CORNER & CORNER_BL) ? BST_CHECKED : BST_UNCHECKED);
+
+ ret = item->COLOR;
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR);
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_SETCOLOUR, 0, ret);
+
+ ret = item->COLOR2;
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_COLOR2);
+ SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_SETCOLOUR, 0, ret);
+
+ CheckDlgButton(hwndDlg, IDC_COLOR2_TRANSPARENT, (item->COLOR2_TRANSPARENT) ? BST_CHECKED : BST_UNCHECKED);
+
+ ret = item->TEXTCOLOR;
+ SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETDEFAULTCOLOUR, 0, CLCDEFAULT_TEXTCOLOR);
+ SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_SETCOLOUR, 0, ret);
+
+ if (item->ALPHA == -1) {
+ SetDlgItemTextA(hwndDlg, IDC_ALPHA, "");
+ }
+ else {
+ ret = item->ALPHA;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_ALPHA, itoabuf);
+ }
+
+ if (item->MARGIN_LEFT == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, "");
+ else {
+ ret = item->MARGIN_LEFT;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, itoabuf);
+ }
+
+ if (item->MARGIN_TOP == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, "");
+ else {
+ ret = item->MARGIN_TOP;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, itoabuf);
+ }
+
+ if (item->MARGIN_RIGHT == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, "");
+ else {
+ ret = item->MARGIN_RIGHT;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, itoabuf);
+ }
+
+ if (item->MARGIN_BOTTOM == -1)
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, "");
+ else {
+ ret = item->MARGIN_BOTTOM;
+ _itoa(ret, itoabuf, 10);
+ SetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, itoabuf);
+ }
+ if (item->BORDERSTYLE == -1)
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, 0, 0);
+ else {
+ index = 0;
+ switch (item->BORDERSTYLE) {
+ case 0:
+ case -1:
+ index = 0;
+ break;
+ case BDR_RAISEDOUTER:
+ index = 1;
+ break;
+ case BDR_SUNKENINNER:
+ index = 2;
+ break;
+ case EDGE_BUMP:
+ index = 3;
+ break;
+ case EDGE_ETCHED:
+ index = 4;
+ break;
+ }
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_SETCURSEL, (WPARAM)index, 0);
+ }
+ ReActiveCombo(hwndDlg);
+}
+// update dlg with selected item
+static void FillOptionDialogByCurrentSel(HWND hwndDlg)
+{
+ int index = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0);
+ int itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, index, 0);
+ if (itemData != ID_EXTBKSEPARATOR) {
+ LastModifiedItem = itemData - ID_EXTBK_FIRST;
+
+ if (CheckItem(itemData - ID_EXTBK_FIRST, hwndDlg))
+ FillOptionDialogByStatusItem(hwndDlg, StatusItems[itemData - ID_EXTBK_FIRST]);
+ }
+}
+
+
+// enabled all status controls if the selected item is a separator
+static BOOL CheckItem(int item, HWND hwndDlg)
+{
+ if (StatusItems[item]->statusID == ID_EXTBKSEPARATOR) {
+ ChangeControlItems(hwndDlg, 0, 0);
+ return FALSE;
+ }
+ else {
+ ChangeControlItems(hwndDlg, 1, 0);
+ return TRUE;
+ }
+}
+
+static void SetChangedStatusItemFlag(WPARAM wParam, HWND hwndDlg)
+{
+ if (LOWORD(wParam) != IDC_ITEMS
+ && (GetDlgItem(hwndDlg, LOWORD(wParam)) == GetFocus() || HIWORD(wParam) == CPN_COLOURCHANGED)
+ && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CPN_COLOURCHANGED)) {
+ switch (LOWORD(wParam)) {
+ case IDC_IGNORE:
+ ChangedSItems.bIGNORED = TRUE; break;
+ case IDC_GRADIENT:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_LR:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_RL:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_BT:
+ ChangedSItems.bGRADIENT = TRUE; break;
+ case IDC_GRADIENT_TB:
+ ChangedSItems.bGRADIENT = TRUE; break;
+
+ case IDC_CORNER:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_TL:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_TR:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_BR:
+ ChangedSItems.bCORNER = TRUE; break;
+ case IDC_CORNER_BL:
+ ChangedSItems.bCORNER = TRUE; break;
+
+ case IDC_BASECOLOUR:
+ ChangedSItems.bCOLOR = TRUE; break;
+ case IDC_BASECOLOUR2:
+ ChangedSItems.bCOLOR2 = TRUE; break;
+ case IDC_COLOR2_TRANSPARENT:
+ ChangedSItems.bCOLOR2_TRANSPARENT = TRUE; break;
+ case IDC_TEXTCOLOUR:
+ ChangedSItems.bTEXTCOLOR = TRUE; break;
+
+ case IDC_ALPHA:
+ ChangedSItems.bALPHA = TRUE; break;
+ case IDC_ALPHASPIN:
+ ChangedSItems.bALPHA = TRUE; break;
+
+ case IDC_MRGN_LEFT:
+ ChangedSItems.bMARGIN_LEFT = TRUE; break;
+ case IDC_MRGN_LEFT_SPIN:
+ ChangedSItems.bMARGIN_LEFT = TRUE; break;
+
+ case IDC_MRGN_TOP:
+ ChangedSItems.bMARGIN_TOP = TRUE; break;
+ case IDC_MRGN_TOP_SPIN:
+ ChangedSItems.bMARGIN_TOP = TRUE; break;
+
+ case IDC_MRGN_RIGHT:
+ ChangedSItems.bMARGIN_RIGHT = TRUE; break;
+ case IDC_MRGN_RIGHT_SPIN:
+ ChangedSItems.bMARGIN_RIGHT = TRUE; break;
+
+ case IDC_MRGN_BOTTOM:
+ ChangedSItems.bMARGIN_BOTTOM = TRUE; break;
+ case IDC_MRGN_BOTTOM_SPIN:
+ ChangedSItems.bMARGIN_BOTTOM = TRUE; break;
+
+ case IDC_BORDERTYPE:
+ ChangedSItems.bBORDERSTYLE = TRUE; break;
+ }
+ }
+}
+
+// updates the struct with the changed dlg item
+static void UpdateStatusStructSettingsFromOptDlg(HWND hwndDlg, int index)
+{
+ char buf[15];
+ ULONG bdrtype;
+ StatusItems_t *p = StatusItems[index];
+
+ if (ChangedSItems.bIGNORED)
+ p->IGNORED = IsDlgButtonChecked(hwndDlg, IDC_IGNORE);
+
+ if (ChangedSItems.bGRADIENT) {
+ p->GRADIENT = GRADIENT_NONE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT))
+ p->GRADIENT |= GRADIENT_ACTIVE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_LR))
+ p->GRADIENT |= GRADIENT_LR;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_RL))
+ p->GRADIENT |= GRADIENT_RL;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_TB))
+ p->GRADIENT |= GRADIENT_TB;
+ if (IsDlgButtonChecked(hwndDlg, IDC_GRADIENT_BT))
+ p->GRADIENT |= GRADIENT_BT;
+ }
+ if (ChangedSItems.bCORNER) {
+ p->CORNER = CORNER_NONE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER))
+ p->CORNER |= CORNER_ACTIVE;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TL))
+ p->CORNER |= CORNER_TL;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_TR))
+ p->CORNER |= CORNER_TR;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BR))
+ p->CORNER |= CORNER_BR;
+ if (IsDlgButtonChecked(hwndDlg, IDC_CORNER_BL))
+ p->CORNER |= CORNER_BL;
+ }
+
+ if (ChangedSItems.bCOLOR)
+ p->COLOR = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR, CPM_GETCOLOUR, 0, 0);
+
+ if (ChangedSItems.bCOLOR2)
+ p->COLOR2 = SendDlgItemMessage(hwndDlg, IDC_BASECOLOUR2, CPM_GETCOLOUR, 0, 0);
+
+ if (ChangedSItems.bCOLOR2_TRANSPARENT)
+ p->COLOR2_TRANSPARENT = IsDlgButtonChecked(hwndDlg, IDC_COLOR2_TRANSPARENT);
+
+ if (ChangedSItems.bTEXTCOLOR)
+ p->TEXTCOLOR = SendDlgItemMessage(hwndDlg, IDC_TEXTCOLOUR, CPM_GETCOLOUR, 0, 0);
+
+ if (ChangedSItems.bALPHA) {
+ GetDlgItemTextA(hwndDlg, IDC_ALPHA, buf, 10); // can be removed now
+ if (buf[0] != 0)
+ p->ALPHA = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_LEFT) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_LEFT, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_LEFT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_TOP) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_TOP, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_TOP = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_RIGHT) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_RIGHT, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_RIGHT = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_GETPOS, 0, 0);
+ }
+
+ if (ChangedSItems.bMARGIN_BOTTOM) {
+ GetDlgItemTextA(hwndDlg, IDC_MRGN_BOTTOM, buf, 10);
+ if (buf[0] != 0)
+ p->MARGIN_BOTTOM = (uint8_t)SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_GETPOS, 0, 0);
+ }
+ if (ChangedSItems.bBORDERSTYLE) {
+ bdrtype = SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_GETCURSEL, 0, 0);
+ if (bdrtype == CB_ERR)
+ p->BORDERSTYLE = 0;
+ else {
+ switch (bdrtype) {
+ case 0:
+ p->BORDERSTYLE = 0;
+ break;
+ case 1:
+ p->BORDERSTYLE = BDR_RAISEDOUTER;
+ break;
+ case 2:
+ p->BORDERSTYLE = BDR_SUNKENINNER;
+ break;
+ case 3:
+ p->BORDERSTYLE = EDGE_BUMP;
+ break;
+ case 4:
+ p->BORDERSTYLE = EDGE_ETCHED;
+ break;
+ default:
+ p->BORDERSTYLE = 0;
+ break;
+ }
+ }
+ }
+}
+
+static void SaveLatestChanges(HWND hwndDlg)
+{
+ int n, itemData;
+ // process old selection
+ if (last_selcount > 0) {
+ for (n = 0; n < last_selcount; n++) {
+ itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0);
+ if (itemData != ID_EXTBKSEPARATOR) {
+ UpdateStatusStructSettingsFromOptDlg(hwndDlg, itemData - ID_EXTBK_FIRST);
+ }
+ }
+ }
+
+ // reset bChange
+ ChangedSItems.bALPHA = FALSE;
+ ChangedSItems.bGRADIENT = FALSE;
+ ChangedSItems.bCORNER = FALSE;
+ ChangedSItems.bCOLOR = FALSE;
+ ChangedSItems.bCOLOR2 = FALSE;
+ ChangedSItems.bCOLOR2_TRANSPARENT = FALSE;
+ ChangedSItems.bTEXTCOLOR = FALSE;
+ ChangedSItems.bMARGIN_LEFT = FALSE;
+ ChangedSItems.bMARGIN_TOP = FALSE;
+ ChangedSItems.bMARGIN_RIGHT = FALSE;
+ ChangedSItems.bMARGIN_BOTTOM = FALSE;
+ ChangedSItems.bIGNORED = FALSE;
+ ChangedSItems.bBORDERSTYLE = FALSE;
+}
+
+static UINT _controls_to_refresh[] = {
+ IDC_BORDERTYPE,
+ IDC_3DDARKCOLOR,
+ IDC_3DLIGHTCOLOR,
+ IDC_MRGN_BOTTOM,
+ IDC_MRGN_LEFT,
+ IDC_ALPHASPIN,
+ IDC_CORNER,
+ IDC_MRGN_TOP_SPIN,
+ IDC_MRGN_RIGHT_SPIN,
+ IDC_MRGN_BOTTOM_SPIN,
+ IDC_MRGN_LEFT_SPIN,
+ IDC_GRADIENT,
+ IDC_GRADIENT_LR,
+ IDC_GRADIENT_RL,
+ IDC_GRADIENT_TB,
+ IDC_BASECOLOUR,
+ IDC_ALPHA,
+ IDC_MRGN_TOP,
+ IDC_MRGN_RIGHT,
+ IDC_GRADIENT_BT,
+ IDC_BASECOLOUR2,
+ IDC_TEXTCOLOUR,
+ IDC_CORNER_TL,
+ IDC_CORNER_TR,
+ IDC_CORNER_BR,
+ IDC_CORNER_BL,
+ IDC_IGNORE,
+ IDC_ALPHALABLE,
+ IDC_COLOR2LABLE,
+ IDC_COLORLABLE,
+ IDC_TEXTCOLOURLABLE,
+ IDC_COLOR2_TRANSPARENT,
+ 0
+};
+
+static void RefreshControls(HWND hwnd)
+{
+ for (int i = 0; _controls_to_refresh[i]; i++)
+ InvalidateRect(GetDlgItem(hwnd, _controls_to_refresh[i]), nullptr, FALSE);
+}
+
+// wenn die listbox geändert wurde
+static void OnListItemsChange(HWND hwndDlg)
+{
+ SendMessage(hwndDlg, WM_SETREDRAW, FALSE, 0);
+ SaveLatestChanges(hwndDlg);
+
+ // set new selection
+ last_selcount = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELCOUNT, 0, 0);
+ if (last_selcount > 0) {
+ int n, itemData, first_item;
+
+ // get selected indizes
+ SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETSELITEMS, 64, (LPARAM)last_indizes);
+
+ // initialize with first items value
+
+ first_item = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[0], 0) - ID_EXTBK_FIRST;
+ StatusItems_t *pFirst = StatusItems[first_item];
+ StatusItems_t DialogSettingForMultiSel = *StatusItems[first_item];
+ for (n = 0; n < last_selcount; n++) {
+ itemData = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, last_indizes[n], 0);
+ if (itemData == ID_EXTBKSEPARATOR)
+ continue;
+
+ StatusItems_t *p = StatusItems[itemData - ID_EXTBK_FIRST];
+ if (p->ALPHA != pFirst->ALPHA)
+ DialogSettingForMultiSel.ALPHA = -1;
+ if (p->COLOR != pFirst->COLOR)
+ DialogSettingForMultiSel.COLOR = CLCDEFAULT_COLOR;
+ if (p->COLOR2 != pFirst->COLOR2)
+ DialogSettingForMultiSel.COLOR2 = CLCDEFAULT_COLOR2;
+ if (p->COLOR2_TRANSPARENT != pFirst->COLOR2_TRANSPARENT)
+ DialogSettingForMultiSel.COLOR2_TRANSPARENT = CLCDEFAULT_COLOR2_TRANSPARENT;
+ if (p->TEXTCOLOR != pFirst->TEXTCOLOR)
+ DialogSettingForMultiSel.TEXTCOLOR = CLCDEFAULT_TEXTCOLOR;
+ if (p->CORNER != pFirst->CORNER)
+ DialogSettingForMultiSel.CORNER = CLCDEFAULT_CORNER;
+ if (p->GRADIENT != pFirst->GRADIENT)
+ DialogSettingForMultiSel.GRADIENT = CLCDEFAULT_GRADIENT;
+ if (p->IGNORED != pFirst->IGNORED)
+ DialogSettingForMultiSel.IGNORED = CLCDEFAULT_IGNORE;
+ if (p->MARGIN_BOTTOM != pFirst->MARGIN_BOTTOM)
+ DialogSettingForMultiSel.MARGIN_BOTTOM = -1;
+ if (p->MARGIN_LEFT != pFirst->MARGIN_LEFT)
+ DialogSettingForMultiSel.MARGIN_LEFT = -1;
+ if (p->MARGIN_RIGHT != pFirst->MARGIN_RIGHT)
+ DialogSettingForMultiSel.MARGIN_RIGHT = -1;
+ if (p->MARGIN_TOP != pFirst->MARGIN_TOP)
+ DialogSettingForMultiSel.MARGIN_TOP = -1;
+ if (p->BORDERSTYLE != pFirst->BORDERSTYLE)
+ DialogSettingForMultiSel.BORDERSTYLE = -1;
+ }
+
+ if (last_selcount == 1 && pFirst->statusID == ID_EXTBKSEPARATOR) {
+ ChangeControlItems(hwndDlg, 0, 0);
+ last_selcount = 0;
+ }
+ else
+ ChangeControlItems(hwndDlg, 1, 0);
+ FillOptionDialogByStatusItem(hwndDlg, &DialogSettingForMultiSel);
+ InvalidateRect(GetDlgItem(hwndDlg, IDC_ITEMS), nullptr, FALSE);
+ }
+ SendMessage(hwndDlg, WM_SETREDRAW, TRUE, 0);
+ RefreshControls(hwndDlg);
+}
+
+// fills the combobox of the options dlg for the first time
+static void FillItemList(HWND hwndDlg)
+{
+ int n, iOff;
+ UINT item;
+
+ for (n = 0; n < ID_EXTBK_LAST - ID_EXTBK_FIRST; n++) {
+ iOff = 0;
+ if (strstr(StatusItems[n]->szName, "{-}")) {
+ item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)"------------------------");
+ SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBKSEPARATOR);
+ iOff = 3;
+ }
+ item = SendDlgItemMessageA(hwndDlg, IDC_ITEMS, LB_ADDSTRING, 0, (LPARAM)StatusItems[n]->szName[iOff]);
+ SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_SETITEMDATA, item, ID_EXTBK_FIRST + n);
+ }
+}
+
+static INT_PTR CALLBACK SkinEdit_ExtBkDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ SKINDESCRIPTION *psd = (SKINDESCRIPTION *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (psd) {
+ ID_EXTBK_FIRST = psd->firstItem;
+ ID_EXTBK_LAST = psd->lastItem;
+ StatusItems = psd->StatusItems;
+ }
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ psd = (SKINDESCRIPTION *)malloc(sizeof(SKINDESCRIPTION));
+ if (psd == nullptr)
+ return FALSE;
+ memset(psd, 0, sizeof(SKINDESCRIPTION));
+ memcpy(psd, (void *)lParam, sizeof(SKINDESCRIPTION));
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)psd);
+
+ ID_EXTBK_FIRST = psd->firstItem;
+ ID_EXTBK_LAST = psd->lastItem;
+ StatusItems = psd->StatusItems;
+
+ TranslateDialogDefault(hwndDlg);
+ FillItemList(hwndDlg);
+ SendMessage(hwndDlg, WM_USER + 101, 0, 0);
+
+ psd->hMenuItems = CreatePopupMenu();
+ AppendMenu(psd->hMenuItems, MF_STRING | MF_DISABLED, (UINT_PTR)0, LPGENW("Copy from"));
+ AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr);
+
+ {
+ for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) {
+ int iOff = StatusItems[i - ID_EXTBK_FIRST]->szName[0] == '{' ? 3 : 0;
+ if (iOff)
+ AppendMenuA(psd->hMenuItems, MF_SEPARATOR, (UINT_PTR)0, nullptr);
+ AppendMenuA(psd->hMenuItems, MF_STRING, (UINT_PTR)i, &StatusItems[i - ID_EXTBK_FIRST]->szName[iOff]);
+ }
+ }
+ return TRUE;
+
+ case WM_USER + 101:
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_LEFT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_TOP_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_RIGHT_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_MRGN_BOTTOM_SPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+ SendDlgItemMessage(hwndDlg, IDC_ALPHASPIN, UDM_SETRANGE, 0, MAKELONG(100, 0));
+
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("<None>"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Raised"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Sunken"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Bumped"));
+ SendDlgItemMessage(hwndDlg, IDC_BORDERTYPE, CB_INSERTSTRING, -1, (LPARAM)TranslateT("Etched"));
+
+ SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3ddark", RGB(224, 224, 224)));
+ SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_SETCOLOUR, 0, db_get_dw(0, "CLCExt", "3dbright", RGB(224, 224, 224)));
+ return 0;
+
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
+ int iItem = dis->itemData;
+ StatusItems_t *item = nullptr;
+
+ SetBkMode(dis->hDC, TRANSPARENT);
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+
+ if (iItem >= ID_EXTBK_FIRST && iItem < ID_EXTBK_LAST)
+ item = StatusItems[iItem - ID_EXTBK_FIRST];
+
+ if (dis->itemState & ODS_SELECTED && iItem != ID_EXTBKSEPARATOR) {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_HIGHLIGHT));
+ SetTextColor(dis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else {
+ FillRect(dis->hDC, &dis->rcItem, GetSysColorBrush(COLOR_WINDOW));
+ if (item && item->IGNORED)
+ SetTextColor(dis->hDC, RGB(255, 0, 0));
+ else
+ SetTextColor(dis->hDC, GetSysColor(COLOR_WINDOWTEXT));
+ }
+ if (iItem == ID_EXTBKSEPARATOR) {
+ HPEN hPen, hPenOld;
+ POINT pt;
+
+ hPen = CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWTEXT));
+ hPenOld = (HPEN)SelectObject(dis->hDC, hPen);
+
+ MoveToEx(dis->hDC, dis->rcItem.left, (dis->rcItem.top + dis->rcItem.bottom) / 2, &pt);
+ LineTo(dis->hDC, dis->rcItem.right, (dis->rcItem.top + dis->rcItem.bottom) / 2);
+ SelectObject(dis->hDC, hPenOld);
+ DeleteObject((HGDIOBJ)hPen);
+ }
+ else if (item) {
+ char *szName = item->szName[0] == '{' ? &item->szName[3] : item->szName;
+ TextOutA(dis->hDC, dis->rcItem.left, dis->rcItem.top, szName, (int)mir_strlen(szName));
+ }
+ return TRUE;
+ }
+
+ case WM_CONTEXTMENU:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_ITEMS);
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rc;
+ GetWindowRect(hwndList, &rc);
+
+ if (PtInRect(&rc, pt)) {
+ int iSelection = (int)TrackPopupMenu(psd->hMenuItems, TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, nullptr);
+
+ if (iSelection >= ID_EXTBK_FIRST && iSelection < ID_EXTBK_LAST) {
+ iSelection -= ID_EXTBK_FIRST;
+ StatusItems_t *pSel = StatusItems[iSelection];
+
+ for (int i = ID_EXTBK_FIRST; i < ID_EXTBK_LAST; i++) {
+ if (SendMessage(hwndList, LB_GETSEL, i - ID_EXTBK_FIRST, 0) <= 0)
+ continue;
+
+ int iIndex = SendMessage(hwndList, LB_GETITEMDATA, i - ID_EXTBK_FIRST, 0);
+ iIndex -= ID_EXTBK_FIRST;
+ if (iIndex < 0)
+ continue;
+
+ StatusItems_t *p = StatusItems[iIndex];
+ p->ALPHA = pSel->ALPHA;
+ p->BORDERSTYLE = pSel->BORDERSTYLE;
+ p->COLOR = pSel->COLOR;
+ p->COLOR2 = pSel->COLOR2;
+ p->COLOR2_TRANSPARENT = pSel->COLOR2_TRANSPARENT;
+ p->CORNER = pSel->CORNER;
+ p->GRADIENT = pSel->GRADIENT;
+ p->IGNORED = pSel->IGNORED;
+ p->imageItem = pSel->imageItem;
+ p->MARGIN_BOTTOM = pSel->MARGIN_BOTTOM;
+ p->MARGIN_LEFT = pSel->MARGIN_LEFT;
+ p->MARGIN_RIGHT = pSel->MARGIN_RIGHT;
+ p->MARGIN_TOP = pSel->MARGIN_TOP;
+ p->TEXTCOLOR = pSel->TEXTCOLOR;
+ }
+ OnListItemsChange(hwndDlg);
+ }
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ // this will check if the user changed some actual statusitems values
+ // if yes the flag bChanged will be set to TRUE
+ SetChangedStatusItemFlag(wParam, hwndDlg);
+ switch (LOWORD(wParam)) {
+ case IDC_ITEMS:
+ if (HIWORD(wParam) != LBN_SELCHANGE)
+ return FALSE;
+ {
+ int iItem = SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_ITEMS, LB_GETCURSEL, 0, 0), 0);
+ if (iItem == ID_EXTBKSEPARATOR)
+ return FALSE;
+ }
+ OnListItemsChange(hwndDlg);
+ if (psd->pfnClcOptionsChanged)
+ psd->pfnClcOptionsChanged();
+ break;
+ case IDC_GRADIENT:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_CORNER:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_IGNORE:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_COLOR2_TRANSPARENT:
+ ReActiveCombo(hwndDlg);
+ break;
+ case IDC_BORDERTYPE:
+ break;
+ }
+ if ((LOWORD(wParam) == IDC_ALPHA || LOWORD(wParam) == IDC_MRGN_LEFT || LOWORD(wParam) == IDC_MRGN_BOTTOM || LOWORD(wParam) == IDC_MRGN_TOP || LOWORD(wParam) == IDC_MRGN_RIGHT) && (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()))
+ return 0;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ // save user made changes
+ SaveLatestChanges(hwndDlg);
+ // save struct to DB
+ if (psd->pfnSaveCompleteStruct)
+ psd->pfnSaveCompleteStruct();
+ db_set_dw(0, "CLCExt", "3dbright", SendDlgItemMessage(hwndDlg, IDC_3DLIGHTCOLOR, CPM_GETCOLOUR, 0, 0));
+ db_set_dw(0, "CLCExt", "3ddark", SendDlgItemMessage(hwndDlg, IDC_3DDARKCOLOR, CPM_GETCOLOUR, 0, 0));
+
+ if (psd->pfnClcOptionsChanged)
+ psd->pfnClcOptionsChanged();
+ if (psd->hwndCLUI) {
+ SendMessage(psd->hwndCLUI, WM_SIZE, 0, 0);
+ PostMessage(psd->hwndCLUI, WM_USER + 100, 0, 0); // CLUIINTM_REDRAW
+ }
+ break;
+ }
+ }
+ break;
+ case WM_DESTROY:
+ DestroyMenu(psd->hMenuItems);
+ break;
+ case WM_NCDESTROY:
+ free(psd);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+/*
+ * unimplemented
+*/
+
+static INT_PTR SkinEdit_FillByCurrentSel(WPARAM wParam, LPARAM)
+{
+ if (wParam)
+ FillOptionDialogByCurrentSel((HWND)wParam);
+ return 0;
+}
+
+/*
+ * service function
+ * creates additional tab pages under the given parent window handle
+ * expects a SKINDESCRIPTON * in lParam
+*/
+
+static INT_PTR SkinEdit_Invoke(WPARAM, LPARAM lParam)
+{
+ SKINDESCRIPTION *psd = (SKINDESCRIPTION *)lParam;
+ TCITEM tci = { 0 };
+ RECT rcClient;
+ int iTabs;
+
+ if (psd->cbSize != sizeof(SKINDESCRIPTION))
+ return 0;
+
+ iTabs = TabCtrl_GetItemCount(psd->hWndTab);
+ GetClientRect(psd->hWndParent, &rcClient);
+
+ tci.mask = TCIF_PARAM | TCIF_TEXT;
+ tci.lParam = (LPARAM)CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SKINITEMEDIT), psd->hWndParent, SkinEdit_ExtBkDlgProc, (LPARAM)psd);
+
+ tci.pszText = TranslateT("Skin items");
+ TabCtrl_InsertItem(psd->hWndTab, iTabs++, &tci);
+ MoveWindow((HWND)tci.lParam, 5, 25, rcClient.right - 9, rcClient.bottom - 60, 1);
+ psd->hwndSkinEdit = (HWND)tci.lParam;
+ return (INT_PTR)psd->hwndSkinEdit;
+}
+
+int CMPlugin::Load()
+{
+ CreateServiceFunction(MS_CLNSE_INVOKE, SkinEdit_Invoke);
+ CreateServiceFunction(MS_CLNSE_FILLBYCURRENTSEL, SkinEdit_FillByCurrentSel);
+ return 0;
+}
diff --git a/plugins/Cln_skinedit/src/stdafx.cxx b/plugins/Cln_skinedit/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Cln_skinedit/src/stdafx.cxx
+++ b/plugins/Cln_skinedit/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/plugins/Cln_skinedit/src/stdafx.h b/plugins/Cln_skinedit/src/stdafx.h
index 8bd8e8f793..830c83e5ab 100644
--- a/plugins/Cln_skinedit/src/stdafx.h
+++ b/plugins/Cln_skinedit/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-04 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/CmdLine/src/stdafx.cxx b/plugins/CmdLine/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/CmdLine/src/stdafx.cxx
+++ b/plugins/CmdLine/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/plugins/Console/src/Console.cpp b/plugins/Console/src/Console.cpp
index 51696191c2..5b6de12dcd 100644
--- a/plugins/Console/src/Console.cpp
+++ b/plugins/Console/src/Console.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/plugins/Console/src/init.cpp b/plugins/Console/src/init.cpp
index 89bf4c5a31..5b3f3fe099 100644
--- a/plugins/Console/src/init.cpp
+++ b/plugins/Console/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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Console/src/stdafx.cxx b/plugins/Console/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Console/src/stdafx.cxx
+++ b/plugins/Console/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/plugins/Console/src/stdafx.h b/plugins/Console/src/stdafx.h
index 2c68175f6f..f3afeaf2d3 100644
--- a/plugins/Console/src/stdafx.h
+++ b/plugins/Console/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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/ContactsPlus/src/stdafx.cxx b/plugins/ContactsPlus/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ContactsPlus/src/stdafx.cxx
+++ b/plugins/ContactsPlus/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/plugins/CountryFlags/src/stdafx.cxx b/plugins/CountryFlags/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/CountryFlags/src/stdafx.cxx
+++ b/plugins/CountryFlags/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/plugins/CrashDumper/src/stdafx.cxx b/plugins/CrashDumper/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/CrashDumper/src/stdafx.cxx
+++ b/plugins/CrashDumper/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/plugins/CrashDumper/src/version.h b/plugins/CrashDumper/src/version.h
index 191606abac..f493899a6b 100644
--- a/plugins/CrashDumper/src/version.h
+++ b/plugins/CrashDumper/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Crash Dumper and Version Information for Miranda NG."
#define __AUTHOR "borkra"
#define __AUTHORWEB "https://miranda-ng.org/p/CrashDumper"
-#define __COPYRIGHT "© 2008-12 Boris Krasnovskiy, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2008-12 Boris Krasnovskiy, 2012-23 Miranda NG team"
diff --git a/plugins/CryptoPP/src/stdafx.cpp b/plugins/CryptoPP/src/stdafx.cpp
index b1991ac69e..73ac0be178 100644
--- a/plugins/CryptoPP/src/stdafx.cpp
+++ b/plugins/CryptoPP/src/stdafx.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/plugins/CyrTranslit/src/stdafx.cxx b/plugins/CyrTranslit/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/CyrTranslit/src/stdafx.cxx
+++ b/plugins/CyrTranslit/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/plugins/Db3x_mmap/src/database.cpp b/plugins/Db3x_mmap/src/database.cpp
index da212ebbfc..ae0bfaa74e 100644
--- a/plugins/Db3x_mmap/src/database.cpp
+++ b/plugins/Db3x_mmap/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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/database.h b/plugins/Db3x_mmap/src/database.h
index 4078db6b9f..e69550552c 100644
--- a/plugins/Db3x_mmap/src/database.h
+++ b/plugins/Db3x_mmap/src/database.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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbcache.cpp b/plugins/Db3x_mmap/src/dbcache.cpp
index 92af668352..c591dfc01e 100644
--- a/plugins/Db3x_mmap/src/dbcache.cpp
+++ b/plugins/Db3x_mmap/src/dbcache.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/plugins/Db3x_mmap/src/dbcontacts.cpp b/plugins/Db3x_mmap/src/dbcontacts.cpp
index a42b010187..222228d454 100644
--- a/plugins/Db3x_mmap/src/dbcontacts.cpp
+++ b/plugins/Db3x_mmap/src/dbcontacts.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/plugins/Db3x_mmap/src/dbcrypt.cpp b/plugins/Db3x_mmap/src/dbcrypt.cpp
index ee90c2fdf8..00424d458b 100644
--- a/plugins/Db3x_mmap/src/dbcrypt.cpp
+++ b/plugins/Db3x_mmap/src/dbcrypt.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/plugins/Db3x_mmap/src/dbevents.cpp b/plugins/Db3x_mmap/src/dbevents.cpp
index d4e99675ed..139a573b6b 100644
--- a/plugins/Db3x_mmap/src/dbevents.cpp
+++ b/plugins/Db3x_mmap/src/dbevents.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/plugins/Db3x_mmap/src/dbheaders.cpp b/plugins/Db3x_mmap/src/dbheaders.cpp
index 3f8ab34067..f5c9d92ea2 100644
--- a/plugins/Db3x_mmap/src/dbheaders.cpp
+++ b/plugins/Db3x_mmap/src/dbheaders.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/plugins/Db3x_mmap/src/dbintf.cpp b/plugins/Db3x_mmap/src/dbintf.cpp
index 25f9efb597..6d2916d109 100644
--- a/plugins/Db3x_mmap/src/dbintf.cpp
+++ b/plugins/Db3x_mmap/src/dbintf.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/plugins/Db3x_mmap/src/dbintf.h b/plugins/Db3x_mmap/src/dbintf.h
index 981a9a6928..dd425f70dc 100644
--- a/plugins/Db3x_mmap/src/dbintf.h
+++ b/plugins/Db3x_mmap/src/dbintf.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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/dbmodulechain.cpp b/plugins/Db3x_mmap/src/dbmodulechain.cpp
index d29847bfea..d8b0eaf5cb 100644
--- a/plugins/Db3x_mmap/src/dbmodulechain.cpp
+++ b/plugins/Db3x_mmap/src/dbmodulechain.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/plugins/Db3x_mmap/src/dbsettings.cpp b/plugins/Db3x_mmap/src/dbsettings.cpp
index bcc84ad428..b50226b9b3 100644
--- a/plugins/Db3x_mmap/src/dbsettings.cpp
+++ b/plugins/Db3x_mmap/src/dbsettings.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/plugins/Db3x_mmap/src/init.cpp b/plugins/Db3x_mmap/src/init.cpp
index 4068ad3632..b7f6365b54 100644
--- a/plugins/Db3x_mmap/src/init.cpp
+++ b/plugins/Db3x_mmap/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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/stdafx.cxx b/plugins/Db3x_mmap/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Db3x_mmap/src/stdafx.cxx
+++ b/plugins/Db3x_mmap/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/plugins/Db3x_mmap/src/stdafx.h b/plugins/Db3x_mmap/src/stdafx.h
index 5facbe33cc..963c59fe0c 100644
--- a/plugins/Db3x_mmap/src/stdafx.h
+++ b/plugins/Db3x_mmap/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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db3x_mmap/src/version.h b/plugins/Db3x_mmap/src/version.h
index d9dbacd9dc..c1eae24738 100644
--- a/plugins/Db3x_mmap/src/version.h
+++ b/plugins/Db3x_mmap/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
#define __AUTHOR "Miranda-NG project"
#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_mmap"
-#define __COPYRIGHT "© 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-23 Miranda NG team"
diff --git a/plugins/DbEditorPP/src/stdafx.cxx b/plugins/DbEditorPP/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/DbEditorPP/src/stdafx.cxx
+++ b/plugins/DbEditorPP/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/plugins/DbEditorPP/src/utils.cpp b/plugins/DbEditorPP/src/utils.cpp
index 19733db080..6662ba4f83 100644
--- a/plugins/DbEditorPP/src/utils.cpp
+++ b/plugins/DbEditorPP/src/utils.cpp
@@ -1,386 +1,386 @@
-/*
-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"
-
-extern uint8_t nameOrder[NAMEORDERCOUNT];
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int ListView_GetItemTextA(HWND hwndLV, int i, int iSubItem, char *pszText, int cchTextMax)
-{
- LV_ITEMA lvi;
- lvi.iSubItem = iSubItem;
- lvi.cchTextMax = cchTextMax;
- lvi.pszText = pszText;
- return SendMessageA(hwndLV, LVM_GETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi);
-}
-
-int ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, const char *pszText)
-{
- LV_ITEMA lvi;
- lvi.iSubItem = iSubItem;
- lvi.pszText = (char*)pszText;
- return SendMessageA(hwndLV, LVM_SETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-char* StringFromBlob(uint8_t *blob, uint16_t len)
-{
- int j;
- char tmp[16];
-
- char *data = (char*)mir_alloc(3 * (len + 2));
- data[0] = 0;
-
- for (j = 0; j < len; j++) {
- mir_snprintf(tmp, "%02X ", blob[j]);
- mir_strcat(data, tmp);
- }
- return data;
-}
-
-int WriteBlobFromString(MCONTACT hContact, const char *szModule, const char *szSetting, const char *szValue, int len)
-{
- int j = 0, i = 0;
- uint8_t b;
- int tmp, res = 0;
- uint8_t *data = (uint8_t*)mir_alloc(2 + len / 2);
-
- if (!data)
- return 0;
-
- while (j < len) {
- b = szValue[j];
-
- if ((b >= '0' && b <= '9') ||
- (b >= 'A' && b <= 'F') ||
- (b >= 'a' && b <= 'f')) {
- if (sscanf(&szValue[j], "%02X", &tmp) == 1) {
- data[i++] = (uint8_t)(tmp & 0xFF);
- j++;
- }
- }
- j++;
- }
-
-
- if (i)
- res = !db_set_blob(hContact, szModule, szSetting, data, (uint16_t)i);
-
- mir_free(data);
- return res;
-}
-
-wchar_t* DBVType(uint8_t type)
-{
- switch (type) {
- case DBVT_BYTE: return L"BYTE";
- case DBVT_WORD: return L"WORD";
- case DBVT_DWORD: return L"DWORD";
- case DBVT_ASCIIZ: return L"STRING";
- case DBVT_WCHAR:
- case DBVT_UTF8: return L"UNICODE";
- case DBVT_BLOB: return L"BLOB";
- case DBVT_DELETED: return L"DELETED";
- }
- return L"";
-}
-
-uint32_t getNumericValue(DBVARIANT *dbv)
-{
- switch (dbv->type) {
- case DBVT_DWORD:
- return dbv->dVal;
- case DBVT_WORD:
- return dbv->wVal;
- case DBVT_BYTE:
- return dbv->bVal;
- }
- return 0;
-}
-
-int setNumericValue(MCONTACT hContact, const char *module, const char *setting, uint32_t value, int type)
-{
- switch (type) {
- case DBVT_BYTE:
- if (value <= 0xFF)
- return !db_set_b(hContact, module, setting, (uint8_t)value);
- break;
-
- case DBVT_WORD:
- if (value <= 0xFFFF)
- return !db_set_w(hContact, module, setting, (uint16_t)value);
- break;
-
- case DBVT_DWORD:
- return !db_set_dw(hContact, module, setting, value);
- }
- return 0;
-}
-
-int IsRealUnicode(wchar_t *value)
-{
- BOOL nonascii = 0;
- WideCharToMultiByte(Langpack_GetDefaultCodePage(), WC_NO_BEST_FIT_CHARS, value, -1, nullptr, 0, nullptr, &nonascii);
- return nonascii;
-}
-
-int setTextValue(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int type)
-{
- if (type == DBVT_UTF8 || type == DBVT_WCHAR)
- return !db_set_ws(hContact, module, setting, value);
-
- if (type == DBVT_ASCIIZ && IsRealUnicode(value))
- return 0;
-
- return !db_set_s(hContact, module, setting, _T2A(value));
-}
-
-int GetValueA(MCONTACT hContact, const char *module, const char *setting, char *value, int length)
-{
- DBVARIANT dbv = {};
-
- if (!module || !setting || !value)
- return 0;
-
- if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) {
- switch (dbv.type) {
-
- case DBVT_ASCIIZ:
- mir_strncpy(value, dbv.pszVal, length);
- break;
-
- case DBVT_DWORD:
- case DBVT_WORD:
- case DBVT_BYTE:
- _ultoa(getNumericValue(&dbv), value, 10);
- break;
-
- case DBVT_WCHAR:
- mir_strncpy(value, ptrA(mir_u2a(dbv.pwszVal)), length);
- break;
-
- case DBVT_UTF8:
- mir_strncpy(value, ptrA(mir_utf8decodeA(dbv.pszVal)), length);
- break;
-
- case DBVT_DELETED:
- value[0] = 0;
- return 0;
- }
-
- int type = dbv.type;
- db_free(&dbv);
- return type;
- }
-
- value[0] = 0;
- return 0;
-}
-
-int GetValueW(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int length)
-{
- DBVARIANT dbv = {};
-
- if (!module || !setting || !value)
- return 0;
-
- if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) {
- switch (dbv.type) {
-
- case DBVT_ASCIIZ:
- mir_wstrncpy(value, ptrW(mir_a2u(dbv.pszVal)), length);
- break;
-
- case DBVT_DWORD:
- case DBVT_WORD:
- case DBVT_BYTE:
- _ultow(getNumericValue(&dbv), value, 10);
- break;
-
- case DBVT_WCHAR:
- mir_wstrncpy(value, dbv.pwszVal, length);
- break;
-
- case DBVT_UTF8:
- mir_wstrncpy(value, ptrW(mir_utf8decodeW(dbv.pszVal)), length);
- break;
-
- case DBVT_DELETED:
- value[0] = 0;
- return 0;
- }
-
- int type = dbv.type;
- db_free(&dbv);
- return type;
- }
-
- value[0] = 0;
- return 0;
-}
-
-int GetContactName(MCONTACT hContact, const char *proto, wchar_t *value, int maxlen)
-{
- if (!value)
- return 0;
-
- if (!hContact) {
- mir_wstrncpy(value, TranslateT("Settings"), maxlen);
- return 1;
- }
-
- char *szProto = (char*)proto;
- char tmp[FLD_SIZE];
- wchar_t name[NAME_SIZE]; name[0] = 0;
-
- if (hContact && (!proto || !proto[0]))
- if (!db_get_static(hContact, "Protocol", "p", tmp, _countof(tmp)))
- szProto = tmp;
-
- for (int i = 0; i < NAMEORDERCOUNT - 1; i++) {
- switch (nameOrder[i]) {
- case 0: // custom name
- GetValueW(hContact, "CList", "MyHandle", name, _countof(name));
- break;
-
- case 1: // nick
- if (!szProto) break;
- GetValueW(hContact, szProto, "Nick", name, _countof(name));
- break;
-
- case 2: // First Name
- // if (!szProto) break;
- // GetValueW(hContact, szProto, "FirstName", name, _countof(name));
- break;
-
- case 3: // E-mail
- if (!szProto) break;
- GetValueW(hContact, szProto, "e-mail", name, _countof(name));
- break;
-
- case 4: // Last Name
- // GetValueW(hContact, szProto, "LastName", name, _countof(name));
- break;
-
- case 5: // Unique id
- if (szProto) {
- // protocol must define a PFLAG_UNIQUEIDSETTING
- const char *uid = Proto_GetUniqueId(szProto);
- if (uid)
- GetValueW(hContact, szProto, uid, name, _countof(name));
- }
- break;
-
- case 6: // first + last name
- if (szProto) {
- GetValueW(hContact, szProto, "FirstName", name, _countof(name));
-
- int len = (int)mir_wstrlen(name);
- if (len + 2 < _countof(name)) {
- if (len)
- mir_wstrncat(name, L" ", _countof(name));
- len++;
- GetValueW(hContact, szProto, "LastName", &name[len], _countof(name) - len);
- }
- }
- break;
- }
-
- if (name[0])
- break;
- }
-
- if (!name[0])
- mir_wstrncpy(name, TranslateT("<UNKNOWN>"), _countof(name));
-
- if (szProto && szProto[0]) {
- if (g_Order)
- mir_snwprintf(value, maxlen, L"(%S) %s", szProto, name);
- else
- mir_snwprintf(value, maxlen, L"%s (%S)", name, szProto);
- }
- else mir_wstrncpy(value, name, maxlen);
-
- PROTOACCOUNT *pa = Proto_GetAccount(szProto);
- if (!pa->IsEnabled()) {
- mir_wstrncat(value, L" ", maxlen);
- mir_wstrncat(value, TranslateT("[UNLOADED]"), maxlen);
- }
-
- return 1;
-}
-
-int ApplyProtoFilter(MCONTACT hContact)
-{
- if (g_Mode == MODE_ALL) return 0;
-
- int loaded = 0;
- char szProto[FLD_SIZE];
-
- if (!db_get_static(hContact, "Protocol", "p", szProto, _countof(szProto)))
- loaded = Proto_GetAccount(szProto) ? 1 : 0;
-
- if ((loaded && g_Mode == MODE_UNLOADED) || (!loaded && g_Mode == MODE_LOADED))
- return 1;
-
- return 0;
-}
-
-void loadListSettings(HWND hwnd, ColumnsSettings *cs)
-{
- LVCOLUMN sLC = {};
- sLC.fmt = LVCFMT_LEFT;
- sLC.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
- int i = 0;
- while (cs[i].name) {
- sLC.pszText = TranslateW(cs[i].name);
- sLC.cx = g_plugin.getWord(cs[i].dbname, cs[i].defsize);
- ListView_InsertColumn(hwnd, cs[i].index, &sLC);
- i++;
- }
-}
-
-void saveListSettings(HWND hwnd, ColumnsSettings *cs)
-{
- char tmp[FLD_SIZE];
- LVCOLUMN sLC = {};
- sLC.mask = LVCF_WIDTH;
- int i = 0;
- while (cs[i].name) {
- if (ListView_GetColumn(hwnd, cs[i].index, &sLC)) {
- mir_snprintf(tmp, cs[i].dbname, i);
- g_plugin.setWord(tmp, (uint16_t)sLC.cx);
- }
- i++;
- }
-}
-
-int CALLBACK ColumnsCompare(LPARAM lParam1, LPARAM lParam2, LPARAM myParam)
-{
- ColumnsSortParams params = *(ColumnsSortParams *)myParam;
- const int maxSize = 1024;
- wchar_t text1[maxSize];
- wchar_t text2[maxSize];
- ListView_GetItemText(params.hList, lParam1, params.column, text1, _countof(text1));
- ListView_GetItemText(params.hList, lParam2, params.column, text2, _countof(text2));
-
- int res = mir_wstrcmpi(text1, text2);
- return (params.column == params.last) ? -res : res;
-}
+/*
+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"
+
+extern uint8_t nameOrder[NAMEORDERCOUNT];
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int ListView_GetItemTextA(HWND hwndLV, int i, int iSubItem, char *pszText, int cchTextMax)
+{
+ LV_ITEMA lvi;
+ lvi.iSubItem = iSubItem;
+ lvi.cchTextMax = cchTextMax;
+ lvi.pszText = pszText;
+ return SendMessageA(hwndLV, LVM_GETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi);
+}
+
+int ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, const char *pszText)
+{
+ LV_ITEMA lvi;
+ lvi.iSubItem = iSubItem;
+ lvi.pszText = (char*)pszText;
+ return SendMessageA(hwndLV, LVM_SETITEMTEXTA, (WPARAM)(i), (LPARAM)(LV_ITEMA *)&lvi);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* StringFromBlob(uint8_t *blob, uint16_t len)
+{
+ int j;
+ char tmp[16];
+
+ char *data = (char*)mir_alloc(3 * (len + 2));
+ data[0] = 0;
+
+ for (j = 0; j < len; j++) {
+ mir_snprintf(tmp, "%02X ", blob[j]);
+ mir_strcat(data, tmp);
+ }
+ return data;
+}
+
+int WriteBlobFromString(MCONTACT hContact, const char *szModule, const char *szSetting, const char *szValue, int len)
+{
+ int j = 0, i = 0;
+ uint8_t b;
+ int tmp, res = 0;
+ uint8_t *data = (uint8_t*)mir_alloc(2 + len / 2);
+
+ if (!data)
+ return 0;
+
+ while (j < len) {
+ b = szValue[j];
+
+ if ((b >= '0' && b <= '9') ||
+ (b >= 'A' && b <= 'F') ||
+ (b >= 'a' && b <= 'f')) {
+ if (sscanf(&szValue[j], "%02X", &tmp) == 1) {
+ data[i++] = (uint8_t)(tmp & 0xFF);
+ j++;
+ }
+ }
+ j++;
+ }
+
+
+ if (i)
+ res = !db_set_blob(hContact, szModule, szSetting, data, (uint16_t)i);
+
+ mir_free(data);
+ return res;
+}
+
+wchar_t* DBVType(uint8_t type)
+{
+ switch (type) {
+ case DBVT_BYTE: return L"BYTE";
+ case DBVT_WORD: return L"WORD";
+ case DBVT_DWORD: return L"DWORD";
+ case DBVT_ASCIIZ: return L"STRING";
+ case DBVT_WCHAR:
+ case DBVT_UTF8: return L"UNICODE";
+ case DBVT_BLOB: return L"BLOB";
+ case DBVT_DELETED: return L"DELETED";
+ }
+ return L"";
+}
+
+uint32_t getNumericValue(DBVARIANT *dbv)
+{
+ switch (dbv->type) {
+ case DBVT_DWORD:
+ return dbv->dVal;
+ case DBVT_WORD:
+ return dbv->wVal;
+ case DBVT_BYTE:
+ return dbv->bVal;
+ }
+ return 0;
+}
+
+int setNumericValue(MCONTACT hContact, const char *module, const char *setting, uint32_t value, int type)
+{
+ switch (type) {
+ case DBVT_BYTE:
+ if (value <= 0xFF)
+ return !db_set_b(hContact, module, setting, (uint8_t)value);
+ break;
+
+ case DBVT_WORD:
+ if (value <= 0xFFFF)
+ return !db_set_w(hContact, module, setting, (uint16_t)value);
+ break;
+
+ case DBVT_DWORD:
+ return !db_set_dw(hContact, module, setting, value);
+ }
+ return 0;
+}
+
+int IsRealUnicode(wchar_t *value)
+{
+ BOOL nonascii = 0;
+ WideCharToMultiByte(Langpack_GetDefaultCodePage(), WC_NO_BEST_FIT_CHARS, value, -1, nullptr, 0, nullptr, &nonascii);
+ return nonascii;
+}
+
+int setTextValue(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int type)
+{
+ if (type == DBVT_UTF8 || type == DBVT_WCHAR)
+ return !db_set_ws(hContact, module, setting, value);
+
+ if (type == DBVT_ASCIIZ && IsRealUnicode(value))
+ return 0;
+
+ return !db_set_s(hContact, module, setting, _T2A(value));
+}
+
+int GetValueA(MCONTACT hContact, const char *module, const char *setting, char *value, int length)
+{
+ DBVARIANT dbv = {};
+
+ if (!module || !setting || !value)
+ return 0;
+
+ if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) {
+ switch (dbv.type) {
+
+ case DBVT_ASCIIZ:
+ mir_strncpy(value, dbv.pszVal, length);
+ break;
+
+ case DBVT_DWORD:
+ case DBVT_WORD:
+ case DBVT_BYTE:
+ _ultoa(getNumericValue(&dbv), value, 10);
+ break;
+
+ case DBVT_WCHAR:
+ mir_strncpy(value, ptrA(mir_u2a(dbv.pwszVal)), length);
+ break;
+
+ case DBVT_UTF8:
+ mir_strncpy(value, ptrA(mir_utf8decodeA(dbv.pszVal)), length);
+ break;
+
+ case DBVT_DELETED:
+ value[0] = 0;
+ return 0;
+ }
+
+ int type = dbv.type;
+ db_free(&dbv);
+ return type;
+ }
+
+ value[0] = 0;
+ return 0;
+}
+
+int GetValueW(MCONTACT hContact, const char *module, const char *setting, wchar_t *value, int length)
+{
+ DBVARIANT dbv = {};
+
+ if (!module || !setting || !value)
+ return 0;
+
+ if (length >= 10 && !db_get_s(hContact, module, setting, &dbv, 0)) {
+ switch (dbv.type) {
+
+ case DBVT_ASCIIZ:
+ mir_wstrncpy(value, ptrW(mir_a2u(dbv.pszVal)), length);
+ break;
+
+ case DBVT_DWORD:
+ case DBVT_WORD:
+ case DBVT_BYTE:
+ _ultow(getNumericValue(&dbv), value, 10);
+ break;
+
+ case DBVT_WCHAR:
+ mir_wstrncpy(value, dbv.pwszVal, length);
+ break;
+
+ case DBVT_UTF8:
+ mir_wstrncpy(value, ptrW(mir_utf8decodeW(dbv.pszVal)), length);
+ break;
+
+ case DBVT_DELETED:
+ value[0] = 0;
+ return 0;
+ }
+
+ int type = dbv.type;
+ db_free(&dbv);
+ return type;
+ }
+
+ value[0] = 0;
+ return 0;
+}
+
+int GetContactName(MCONTACT hContact, const char *proto, wchar_t *value, int maxlen)
+{
+ if (!value)
+ return 0;
+
+ if (!hContact) {
+ mir_wstrncpy(value, TranslateT("Settings"), maxlen);
+ return 1;
+ }
+
+ char *szProto = (char*)proto;
+ char tmp[FLD_SIZE];
+ wchar_t name[NAME_SIZE]; name[0] = 0;
+
+ if (hContact && (!proto || !proto[0]))
+ if (!db_get_static(hContact, "Protocol", "p", tmp, _countof(tmp)))
+ szProto = tmp;
+
+ for (int i = 0; i < NAMEORDERCOUNT - 1; i++) {
+ switch (nameOrder[i]) {
+ case 0: // custom name
+ GetValueW(hContact, "CList", "MyHandle", name, _countof(name));
+ break;
+
+ case 1: // nick
+ if (!szProto) break;
+ GetValueW(hContact, szProto, "Nick", name, _countof(name));
+ break;
+
+ case 2: // First Name
+ // if (!szProto) break;
+ // GetValueW(hContact, szProto, "FirstName", name, _countof(name));
+ break;
+
+ case 3: // E-mail
+ if (!szProto) break;
+ GetValueW(hContact, szProto, "e-mail", name, _countof(name));
+ break;
+
+ case 4: // Last Name
+ // GetValueW(hContact, szProto, "LastName", name, _countof(name));
+ break;
+
+ case 5: // Unique id
+ if (szProto) {
+ // protocol must define a PFLAG_UNIQUEIDSETTING
+ const char *uid = Proto_GetUniqueId(szProto);
+ if (uid)
+ GetValueW(hContact, szProto, uid, name, _countof(name));
+ }
+ break;
+
+ case 6: // first + last name
+ if (szProto) {
+ GetValueW(hContact, szProto, "FirstName", name, _countof(name));
+
+ int len = (int)mir_wstrlen(name);
+ if (len + 2 < _countof(name)) {
+ if (len)
+ mir_wstrncat(name, L" ", _countof(name));
+ len++;
+ GetValueW(hContact, szProto, "LastName", &name[len], _countof(name) - len);
+ }
+ }
+ break;
+ }
+
+ if (name[0])
+ break;
+ }
+
+ if (!name[0])
+ mir_wstrncpy(name, TranslateT("<UNKNOWN>"), _countof(name));
+
+ if (szProto && szProto[0]) {
+ if (g_Order)
+ mir_snwprintf(value, maxlen, L"(%S) %s", szProto, name);
+ else
+ mir_snwprintf(value, maxlen, L"%s (%S)", name, szProto);
+ }
+ else mir_wstrncpy(value, name, maxlen);
+
+ PROTOACCOUNT *pa = Proto_GetAccount(szProto);
+ if (!pa->IsEnabled()) {
+ mir_wstrncat(value, L" ", maxlen);
+ mir_wstrncat(value, TranslateT("[UNLOADED]"), maxlen);
+ }
+
+ return 1;
+}
+
+int ApplyProtoFilter(MCONTACT hContact)
+{
+ if (g_Mode == MODE_ALL) return 0;
+
+ int loaded = 0;
+ char szProto[FLD_SIZE];
+
+ if (!db_get_static(hContact, "Protocol", "p", szProto, _countof(szProto)))
+ loaded = Proto_GetAccount(szProto) ? 1 : 0;
+
+ if ((loaded && g_Mode == MODE_UNLOADED) || (!loaded && g_Mode == MODE_LOADED))
+ return 1;
+
+ return 0;
+}
+
+void loadListSettings(HWND hwnd, ColumnsSettings *cs)
+{
+ LVCOLUMN sLC = {};
+ sLC.fmt = LVCFMT_LEFT;
+ sLC.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
+ int i = 0;
+ while (cs[i].name) {
+ sLC.pszText = TranslateW(cs[i].name);
+ sLC.cx = g_plugin.getWord(cs[i].dbname, cs[i].defsize);
+ ListView_InsertColumn(hwnd, cs[i].index, &sLC);
+ i++;
+ }
+}
+
+void saveListSettings(HWND hwnd, ColumnsSettings *cs)
+{
+ char tmp[FLD_SIZE];
+ LVCOLUMN sLC = {};
+ sLC.mask = LVCF_WIDTH;
+ int i = 0;
+ while (cs[i].name) {
+ if (ListView_GetColumn(hwnd, cs[i].index, &sLC)) {
+ mir_snprintf(tmp, cs[i].dbname, i);
+ g_plugin.setWord(tmp, (uint16_t)sLC.cx);
+ }
+ i++;
+ }
+}
+
+int CALLBACK ColumnsCompare(LPARAM lParam1, LPARAM lParam2, LPARAM myParam)
+{
+ ColumnsSortParams params = *(ColumnsSortParams *)myParam;
+ const int maxSize = 1024;
+ wchar_t text1[maxSize];
+ wchar_t text2[maxSize];
+ ListView_GetItemText(params.hList, lParam1, params.column, text1, _countof(text1));
+ ListView_GetItemText(params.hList, lParam2, params.column, text2, _countof(text2));
+
+ int res = mir_wstrcmpi(text1, text2);
+ return (params.column == params.last) ? -res : res;
+}
diff --git a/plugins/DbEditorPP/src/version.h b/plugins/DbEditorPP/src/version.h
index 19d7f43571..082cc92b36 100644
--- a/plugins/DbEditorPP/src/version.h
+++ b/plugins/DbEditorPP/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Advanced Database Editor."
#define __AUTHOR "Bio, Jonathan Gordon"
#define __AUTHORWEB "https://miranda-ng.org/p/DbEditorPP"
-#define __COPYRIGHT "© 2003-22 Bio, Jonathan Gordon, Miranda NG team"
+#define __COPYRIGHT "© 2003-23 Bio, Jonathan Gordon, Miranda NG team"
diff --git a/plugins/Db_autobackups/src/options.h b/plugins/Db_autobackups/src/options.h
index df231e0ac7..3efb713158 100644
--- a/plugins/Db_autobackups/src/options.h
+++ b/plugins/Db_autobackups/src/options.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Db_autobackups/src/stdafx.cxx b/plugins/Db_autobackups/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Db_autobackups/src/stdafx.cxx
+++ b/plugins/Db_autobackups/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/plugins/Db_autobackups/src/version.h b/plugins/Db_autobackups/src/version.h
index 4fc7564caf..a015445ec2 100644
--- a/plugins/Db_autobackups/src/version.h
+++ b/plugins/Db_autobackups/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Database autobackuper plugin."
#define __AUTHOR "chaos.persei, sje, Kildor, Billy_Bons"
#define __AUTHORWEB "https://miranda-ng.org/p/Db_autobackups"
-#define __COPYRIGHT "© 2005-22 chaos.persei, sje, Kildor, Billy_Bons, Vasilich, Miranda NG team"
+#define __COPYRIGHT "© 2005-23 chaos.persei, sje, Kildor, Billy_Bons, Vasilich, Miranda NG team"
diff --git a/plugins/Dbx_mdbx/src/dbcheck.cpp b/plugins/Dbx_mdbx/src/dbcheck.cpp
index ed812365be..48449d360b 100644
--- a/plugins/Dbx_mdbx/src/dbcheck.cpp
+++ b/plugins/Dbx_mdbx/src/dbcheck.cpp
@@ -1,117 +1,117 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// we are tracing EventsSort table to verify that they have correct event ids
-
-int CDbxMDBX::CheckEvents1(void)
-{
- txn_ptr trnlck(this);
- cursor_ptr cursor(trnlck, m_dbEventsSort);
-
- MDBX_val key, data;
- for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
- auto *pData = (DBEventSortingKey *)key.iov_base;
-
- // if that's not a member of system event, check contact's existence first
- if (pData->hContact != 0) {
- auto *cc = m_cache->GetCachedContact(pData->hContact);
- if (cc == nullptr) {
- mdbx_cursor_del(cursor, MDBX_UPSERT);
- cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong contact ID %d, deleting"), pData->hContact));
- continue;
- }
- }
-
- if (GetBlobSize(pData->hEvent) == -1) {
- mdbx_cursor_del(cursor, MDBX_UPSERT);
- cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong event ID %d:%08X, deleting"), pData->hContact, pData->hEvent));
- continue;
- }
- }
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// we are tracing EventId table to verify that they have correct event ids
-
-int CDbxMDBX::CheckEvents2(void)
-{
- txn_ptr trnlck(this);
- cursor_ptr cursor(trnlck, m_dbEventIds);
-
- MDBX_val key, data;
- for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
- MEVENT hDbEvent = *(MEVENT *)data.iov_base;
- if (GetBlobSize(hDbEvent) == -1) {
- mdbx_cursor_del(cursor, MDBX_UPSERT);
- cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event id with wrong event ID %08X, deleting"), hDbEvent));
- continue;
- }
- }
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// we are tracing Settings table to verify that they have correct contact ids
-
-int CDbxMDBX::CheckEvents3(void)
-{
- txn_ptr trnlck(this);
- cursor_ptr cursor(trnlck, m_dbSettings);
-
- MDBX_val key, data;
- for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
- auto *pKey = (DBSettingKey *)key.iov_base;
-
- if (pKey->hContact) {
- auto *cc = m_cache->GetCachedContact(pKey->hContact);
- if (cc == nullptr) {
- mdbx_cursor_del(cursor, MDBX_UPSERT);
- cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned setting with wrong contact ID %08X, deleting"), pKey->hContact));
- continue;
- }
- }
- }
-
- return 0;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// MIDatabaseChecker
-
-int CDbxMDBX::CheckDb(int phase)
-{
- switch (phase) {
- case 0: return CheckEvents1();
- case 1: return CheckEvents2();
- case 2: return CheckEvents3();
- }
-
- return ERROR_OUT_OF_PAPER;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+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.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// we are tracing EventsSort table to verify that they have correct event ids
+
+int CDbxMDBX::CheckEvents1(void)
+{
+ txn_ptr trnlck(this);
+ cursor_ptr cursor(trnlck, m_dbEventsSort);
+
+ MDBX_val key, data;
+ for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ auto *pData = (DBEventSortingKey *)key.iov_base;
+
+ // if that's not a member of system event, check contact's existence first
+ if (pData->hContact != 0) {
+ auto *cc = m_cache->GetCachedContact(pData->hContact);
+ if (cc == nullptr) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong contact ID %d, deleting"), pData->hContact));
+ continue;
+ }
+ }
+
+ if (GetBlobSize(pData->hEvent) == -1) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong event ID %d:%08X, deleting"), pData->hContact, pData->hEvent));
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// we are tracing EventId table to verify that they have correct event ids
+
+int CDbxMDBX::CheckEvents2(void)
+{
+ txn_ptr trnlck(this);
+ cursor_ptr cursor(trnlck, m_dbEventIds);
+
+ MDBX_val key, data;
+ for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ MEVENT hDbEvent = *(MEVENT *)data.iov_base;
+ if (GetBlobSize(hDbEvent) == -1) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event id with wrong event ID %08X, deleting"), hDbEvent));
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// we are tracing Settings table to verify that they have correct contact ids
+
+int CDbxMDBX::CheckEvents3(void)
+{
+ txn_ptr trnlck(this);
+ cursor_ptr cursor(trnlck, m_dbSettings);
+
+ MDBX_val key, data;
+ for (int ret = mdbx_cursor_get(cursor, &key, &data, MDBX_FIRST); ret == MDBX_SUCCESS; ret = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ auto *pKey = (DBSettingKey *)key.iov_base;
+
+ if (pKey->hContact) {
+ auto *cc = m_cache->GetCachedContact(pKey->hContact);
+ if (cc == nullptr) {
+ mdbx_cursor_del(cursor, MDBX_UPSERT);
+ cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned setting with wrong contact ID %08X, deleting"), pKey->hContact));
+ continue;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// MIDatabaseChecker
+
+int CDbxMDBX::CheckDb(int phase)
+{
+ switch (phase) {
+ case 0: return CheckEvents1();
+ case 1: return CheckEvents2();
+ case 2: return CheckEvents3();
+ }
+
+ return ERROR_OUT_OF_PAPER;
+}
diff --git a/plugins/Dbx_mdbx/src/dbcontacts.cpp b/plugins/Dbx_mdbx/src/dbcontacts.cpp
index 13beacb0ac..d96a94f806 100644
--- a/plugins/Dbx_mdbx/src/dbcontacts.cpp
+++ b/plugins/Dbx_mdbx/src/dbcontacts.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/plugins/Dbx_mdbx/src/dbcrypt.cpp b/plugins/Dbx_mdbx/src/dbcrypt.cpp
index 5b67f18ea7..7dd373f743 100644
--- a/plugins/Dbx_mdbx/src/dbcrypt.cpp
+++ b/plugins/Dbx_mdbx/src/dbcrypt.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/plugins/Dbx_mdbx/src/dbevents.cpp b/plugins/Dbx_mdbx/src/dbevents.cpp
index 581a762c0a..0d26fa1510 100644
--- a/plugins/Dbx_mdbx/src/dbevents.cpp
+++ b/plugins/Dbx_mdbx/src/dbevents.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/plugins/Dbx_mdbx/src/dbintf.cpp b/plugins/Dbx_mdbx/src/dbintf.cpp
index 3570b60690..32993384b8 100644
--- a/plugins/Dbx_mdbx/src/dbintf.cpp
+++ b/plugins/Dbx_mdbx/src/dbintf.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/plugins/Dbx_mdbx/src/dbintf.h b/plugins/Dbx_mdbx/src/dbintf.h
index cdbdfc7cf3..ee612dba63 100644
--- a/plugins/Dbx_mdbx/src/dbintf.h
+++ b/plugins/Dbx_mdbx/src/dbintf.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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/dbmodulechain.cpp b/plugins/Dbx_mdbx/src/dbmodulechain.cpp
index e528c23397..229f6cb4fc 100644
--- a/plugins/Dbx_mdbx/src/dbmodulechain.cpp
+++ b/plugins/Dbx_mdbx/src/dbmodulechain.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/plugins/Dbx_mdbx/src/dbsettings.cpp b/plugins/Dbx_mdbx/src/dbsettings.cpp
index 68fdcbe46a..074c107634 100644
--- a/plugins/Dbx_mdbx/src/dbsettings.cpp
+++ b/plugins/Dbx_mdbx/src/dbsettings.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/plugins/Dbx_mdbx/src/dbutils.cpp b/plugins/Dbx_mdbx/src/dbutils.cpp
index 380b714069..ebe6c85123 100644
--- a/plugins/Dbx_mdbx/src/dbutils.cpp
+++ b/plugins/Dbx_mdbx/src/dbutils.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/plugins/Dbx_mdbx/src/init.cpp b/plugins/Dbx_mdbx/src/init.cpp
index d3911ae0c9..b4f0bf711f 100644
--- a/plugins/Dbx_mdbx/src/init.cpp
+++ b/plugins/Dbx_mdbx/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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/stdafx.cxx b/plugins/Dbx_mdbx/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Dbx_mdbx/src/stdafx.cxx
+++ b/plugins/Dbx_mdbx/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/plugins/Dbx_mdbx/src/stdafx.h b/plugins/Dbx_mdbx/src/stdafx.h
index f150177cd9..f329331d8f 100644
--- a/plugins/Dbx_mdbx/src/stdafx.h
+++ b/plugins/Dbx_mdbx/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)
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/Dbx_mdbx/src/version.h b/plugins/Dbx_mdbx/src/version.h
index 29a2eeb1b7..bd19e10b94 100644
--- a/plugins/Dbx_mdbx/src/version.h
+++ b/plugins/Dbx_mdbx/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
#define __AUTHOR "Miranda-NG project"
#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_mdbx"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/Dbx_sqlite/src/version.h b/plugins/Dbx_sqlite/src/version.h
index 00fd440b6f..ffe245bc61 100644
--- a/plugins/Dbx_sqlite/src/version.h
+++ b/plugins/Dbx_sqlite/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Provides Miranda database support: global settings, contacts, history, settings per contact."
#define __AUTHOR "Miranda-NG project"
#define __AUTHORWEB "https://miranda-ng.org/p/Dbx_sqlite"
-#define __COPYRIGHT "© 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2018-23 Miranda NG team"
diff --git a/plugins/Exchange/src/stdafx.cxx b/plugins/Exchange/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Exchange/src/stdafx.cxx
+++ b/plugins/Exchange/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/plugins/ExternalAPI/m_assocmgr.h b/plugins/ExternalAPI/m_assocmgr.h
index 9ffdb98953..e1d06c8f3e 100644
--- a/plugins/ExternalAPI/m_assocmgr.h
+++ b/plugins/ExternalAPI/m_assocmgr.h
@@ -3,7 +3,7 @@
'File Association Manager'-Plugin for
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) 2005-2007 H. Herkenrath
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_dbeditorpp.h b/plugins/ExternalAPI/m_dbeditorpp.h
index 36418f9162..10785ee0c5 100644
--- a/plugins/ExternalAPI/m_dbeditorpp.h
+++ b/plugins/ExternalAPI/m_dbeditorpp.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) 2003-11 Bio, Jonathan Gordon
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_proto_listeningto.h b/plugins/ExternalAPI/m_proto_listeningto.h
index 0411a21660..24a0cd0ddf 100644
--- a/plugins/ExternalAPI/m_proto_listeningto.h
+++ b/plugins/ExternalAPI/m_proto_listeningto.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-06 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/ExternalAPI/m_shutdown.h b/plugins/ExternalAPI/m_shutdown.h
index edd66b6271..56d7e8a301 100644
--- a/plugins/ExternalAPI/m_shutdown.h
+++ b/plugins/ExternalAPI/m_shutdown.h
@@ -3,7 +3,7 @@
'AutoShutdown'-Plugin for
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) 2004-2007 H. Herkenrath
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_skin_eng.h b/plugins/ExternalAPI/m_skin_eng.h
index f22b6a3e1b..6ea48aced9 100644
--- a/plugins/ExternalAPI/m_skin_eng.h
+++ b/plugins/ExternalAPI/m_skin_eng.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-08 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/ExternalAPI/m_stopspam.h b/plugins/ExternalAPI/m_stopspam.h
index d2f843ab6a..692630eaaa 100644
--- a/plugins/ExternalAPI/m_stopspam.h
+++ b/plugins/ExternalAPI/m_stopspam.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) 2004-009 Roman Miklashevsky, A. Petkevich, Kosh&chka, persei
This program is free software; you can redistribute it and/or
diff --git a/plugins/ExternalAPI/m_userinfoex.h b/plugins/ExternalAPI/m_userinfoex.h
index 08b412b16e..f73c23d1d9 100644
--- a/plugins/ExternalAPI/m_userinfoex.h
+++ b/plugins/ExternalAPI/m_userinfoex.h
@@ -1,7 +1,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-09 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/FTPFileYM/src/stdafx.cxx b/plugins/FTPFileYM/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/FTPFileYM/src/stdafx.cxx
+++ b/plugins/FTPFileYM/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/plugins/FavContacts/src/stdafx.cxx b/plugins/FavContacts/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/FavContacts/src/stdafx.cxx
+++ b/plugins/FavContacts/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/plugins/FingerprintNG/src/fingerprint.cpp b/plugins/FingerprintNG/src/fingerprint.cpp
index 0842cc1007..e15f77536d 100644
--- a/plugins/FingerprintNG/src/fingerprint.cpp
+++ b/plugins/FingerprintNG/src/fingerprint.cpp
@@ -1,913 +1,913 @@
-fv/*
-Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-//Start of header
-#include "stdafx.h"
-
-static UINT g_LPCodePage;
-static wchar_t g_szSkinLib[MAX_PATH];
-static HANDLE hExtraIcon = nullptr;
-static HANDLE hFolderChanged = nullptr, hIconFolder = nullptr;
-
-static int CompareFI(const FOUNDINFO *p1, const FOUNDINFO *p2)
-{
- if (p1->iBase != p2->iBase)
- return p1->iBase - p2->iBase;
- return p1->iOverlay - p2->iOverlay;
-}
-
-static OBJLIST<FOUNDINFO> arFI(50, CompareFI);
-
-static LIST<void> arMonitoredWindows(3, PtrKeySortT);
-
-static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM lParam);
-
-/*
-* Prepare
-* prepares upperstring masks and registers them in IcoLib
-*/
-
-static wchar_t* getSectionName(int flag)
-{
- switch (flag)
- {
-#include "finger_groups.h"
- }
- return nullptr;
-}
-
-void __fastcall Prepare(KN_FP_MASK* mask, bool bEnable)
-{
- mask->szMaskUpper = nullptr;
-
- if (mask->hIcolibItem)
- IcoLib_RemoveIcon(mask->szIconName);
- mask->hIcolibItem = nullptr;
-
- if (!mask->szMask || !bEnable)
- return;
-
- size_t iMaskLen = mir_wstrlen(mask->szMask) + 1;
- LPTSTR pszNewMask = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, iMaskLen * sizeof(wchar_t));
- wcscpy_s(pszNewMask, iMaskLen, mask->szMask);
- _wcsupr_s(pszNewMask, iMaskLen);
- mask->szMaskUpper = pszNewMask;
-
- wchar_t destfile[MAX_PATH];
- if (mask->iIconIndex == IDI_NOTFOUND || mask->iIconIndex == IDI_UNKNOWN || mask->iIconIndex == IDI_UNDETECTED)
- GetModuleFileName(g_plugin.getInst(), destfile, MAX_PATH);
- else {
- wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE);
-
- struct _stat64i32 stFileInfo;
- if (_wstat(destfile, &stFileInfo) == -1)
- return;
- }
-
- LPTSTR SectName = getSectionName(mask->iSectionFlag);
- if (SectName == nullptr)
- return;
-
- SKINICONDESC sid = {};
- sid.flags = SIDF_ALL_UNICODE;
- sid.section.w = SectName;
- sid.pszName = mask->szIconName;
- sid.description.w = mask->szClientDescription;
- sid.defaultFile.w = destfile;
- sid.iDefaultIndex = -mask->iIconIndex;
- mask->hIcolibItem = g_plugin.addIcon(&sid);
-}
-
-/*
-* Register icons
-*/
-
-void RegisterIcons()
-{
- // prepare masks
- int i;
-
- if (hHeap)
- HeapDestroy(hHeap);
- hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
-
- for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++)
- Prepare(&def_kn_fp_mask[i], true);
-
- for (i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++)
- Prepare(&def_kn_fp_overlays_mask[i], true);
-
- if (g_plugin.getByte("GroupMirandaVersion", 0)) {
- for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
- Prepare(&def_kn_fp_overlays2_mask[i], true);
- }
- else {
- for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++)
- Prepare(&def_kn_fp_overlays2_mask[i], true);
- for (; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
- Prepare(&def_kn_fp_overlays2_mask[i], false);
- }
-
- if (g_plugin.getByte("GroupOverlaysUnicode", 1)) {
- for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
- Prepare(&def_kn_fp_overlays3_mask[i], true);
- }
- else {
- for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_NO_UNICODE_COUNT; i++)
- Prepare(&def_kn_fp_overlays3_mask[i], true);
- for (; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
- Prepare(&def_kn_fp_overlays3_mask[i], false);
- }
-
- for (i = 0; i < DEFAULT_KN_FP_OVERLAYS4_COUNT; i++)
- Prepare(&def_kn_fp_overlays4_mask[i], true);
-}
-
-/* ApplyFingerprintImage
-* 1)Try to find appropriate mask
-* 2)Register icon in extraimage list if not yet registered (EMPTY_EXTRA_ICON)
-* 3)Set ExtraImage for contact
-*/
-
-static void SetSrmmIcon(MCONTACT hContact, LPTSTR ptszMirver)
-{
- if (mir_wstrlen(ptszMirver))
- Srmm_ModifyIcon(hContact, MODULENAME, 1, (HICON)ServiceGetClientIconW((WPARAM)ptszMirver, TRUE), ptszMirver);
- else
- Srmm_SetIconFlags(hContact, MODULENAME, 1, MBF_HIDDEN);
-}
-
-int __fastcall ApplyFingerprintImage(MCONTACT hContact, LPTSTR szMirVer)
-{
- if (hContact == NULL)
- return 0;
-
- HANDLE hImage = INVALID_HANDLE_VALUE;
- if (szMirVer)
- hImage = GetIconIndexFromFI(szMirVer);
-
- ExtraIcon_SetIcon(hExtraIcon, hContact, hImage);
-
- if (arMonitoredWindows.getIndex((HANDLE)hContact) != -1)
- SetSrmmIcon(hContact, szMirVer);
-
- return 0;
-}
-
-int OnExtraIconClick(WPARAM wParam, LPARAM, LPARAM)
-{
- CallService(MS_USERINFO_SHOWDIALOG, wParam, NULL);
- return 0;
-}
-
-
-/*
-* WildCompare
-* Compare 'name' string with 'mask' strings.
-* Masks can contain '*' or '?' wild symbols
-* Asterics '*' symbol covers 'empty' symbol too e.g WildCompare("Tst","T*st*"), returns TRUE
-* In order to handle situation 'at least one any sybol' use "?*" combination:
-* e.g WildCompare("Tst","T?*st*"), returns FALSE, but both WildCompare("Test","T?*st*") and
-* WildCompare("Teeest","T?*st*") return TRUE.
-*
-* Function is case sensitive! so convert input or modify func to use _qtoupper()
-*
-* Mask can contain several submasks. In this case each submask (including first)
-* should start from '|' e.g: "|first*submask|second*mask".
-*
-* Dec 25, 2006 by FYR:
-* Added Exception to masks: the mask "|^mask3|mask2|mask1" means:
-* if NOT according to mask 3 AND (mask1 OR mask2)
-* EXCEPTION should be BEFORE main mask:
-* IF Exception match - the comparing stops as FALSE
-* IF Exception does not match - comparing continue
-* IF Mask match - comparing stops as TRUE
-* IF Mask does not not match comparing continue
-*/
-BOOL __fastcall WildCompare(LPWSTR wszName, LPWSTR wszMask)
-{
- if (wszMask == nullptr)
- return NULL;
-
- if (*wszMask != L'|')
- return wildcmpw(wszName, wszMask);
-
- size_t s = 1, e = 1;
- LPWSTR wszTemp = (LPWSTR)_alloca(mir_wstrlen(wszMask) * sizeof(wchar_t) + sizeof(wchar_t));
- BOOL bExcept;
-
- while (wszMask[e] != L'\0')
- {
- s = e;
- while (wszMask[e] != L'\0' && wszMask[e] != L'|') e++;
-
- // exception mask
- bExcept = (*(wszMask + s) == L'^');
- if (bExcept) s++;
-
- memcpy(wszTemp, wszMask + s, (e - s) * sizeof(wchar_t));
- wszTemp[e - s] = L'\0';
-
- if (wildcmpw(wszName, wszTemp))
- return !bExcept;
-
- if (wszMask[e] != L'\0')
- e++;
- else
- return FALSE;
- }
- return FALSE;
-}
-
-static void MatchMasks(wchar_t* szMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4)
-{
- int i = 0, j = -1, k = -1, n = -1, m = -1;
-
- for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++) {
- KN_FP_MASK& p = def_kn_fp_mask[i];
- if (p.hIcolibItem == nullptr)
- continue;
-
- if (!WildCompare(szMirVer, p.szMaskUpper))
- continue;
-
- if (p.iIconIndex != IDI_NOTFOUND && p.iIconIndex != IDI_UNKNOWN && p.iIconIndex != IDI_UNDETECTED) {
- wchar_t destfile[MAX_PATH];
- wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE);
-
- struct _stat64i32 stFileInfo;
- if (_wstat(destfile, &stFileInfo) == -1)
- i = NOTFOUND_MASK_NUMBER;
- }
- break;
- }
- if (i == DEFAULT_KN_FP_MASK_COUNT - 1)
- i = -1;
-
- else if (!def_kn_fp_mask[i].fNotUseOverlay && i < DEFAULT_KN_FP_MASK_COUNT) {
- for (j = 0; j < DEFAULT_KN_FP_OVERLAYS_COUNT; j++) {
- KN_FP_MASK& p = def_kn_fp_overlays_mask[j];
- if (p.hIcolibItem == nullptr)
- continue;
-
- if (!WildCompare(szMirVer, p.szMaskUpper))
- continue;
-
- struct _stat64i32 stFileInfo;
- if (_wstat(g_szSkinLib, &stFileInfo) != -1)
- break;
- }
-
- for (k = 0; k < DEFAULT_KN_FP_OVERLAYS2_COUNT; k++) {
- KN_FP_MASK& p = def_kn_fp_overlays2_mask[k];
- if (p.hIcolibItem == nullptr)
- continue;
-
- if (WildCompare(szMirVer, p.szMaskUpper))
- break;
- }
-
- for (n = 0; n < DEFAULT_KN_FP_OVERLAYS3_COUNT; n++) {
- KN_FP_MASK& p = def_kn_fp_overlays3_mask[n];
- if (p.hIcolibItem == nullptr)
- continue;
-
- if (WildCompare(szMirVer, p.szMaskUpper))
- break;
- }
-
- for (m = 0; m < DEFAULT_KN_FP_OVERLAYS4_COUNT; m++) {
- KN_FP_MASK& p = def_kn_fp_overlays4_mask[m];
- if (p.hIcolibItem == nullptr)
- continue;
-
- if (WildCompare(szMirVer, p.szMaskUpper))
- break;
- }
- }
-
- *base = (i < DEFAULT_KN_FP_MASK_COUNT) ? i : -1;
- *overlay = (j < DEFAULT_KN_FP_OVERLAYS_COUNT) ? j : -1;
- *overlay2 = (k < DEFAULT_KN_FP_OVERLAYS2_COUNT) ? k : -1;
- *overlay3 = (n < DEFAULT_KN_FP_OVERLAYS3_COUNT) ? n : -1;
- *overlay4 = (m < DEFAULT_KN_FP_OVERLAYS4_COUNT) ? m : -1;
-}
-
-/* GetIconsIndexes
-* Retrieves Icons indexes by Mirver
-*/
-
-void __fastcall GetIconsIndexes(LPWSTR wszMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4)
-{
- if (mir_wstrcmp(wszMirVer, L"?") == 0) {
- *base = UNKNOWN_MASK_NUMBER;
- *overlay = -1;
- *overlay2 = -1;
- *overlay3 = -1;
- *overlay4 = -1;
- return;
- }
-
- LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer);
- _wcsupr(wszMirVerUp);
- MatchMasks(wszMirVerUp, base, overlay, overlay2, overlay3, overlay4);
-}
-
-/*
-* CreateIconFromIndexes
-* returns hIcon of joined icon by given indexes
-*/
-
-HICON __fastcall CreateIconFromIndexes(int base, int overlay, int overlay2, int overlay3, int overlay4)
-{
- HICON hIcon = nullptr; // returned HICON
- HICON hTmp = nullptr;
- HICON icMain = nullptr;
- HICON icOverlay = nullptr;
- HICON icOverlay2 = nullptr;
- HICON icOverlay3 = nullptr;
- HICON icOverlay4 = nullptr;
-
- KN_FP_MASK* mainMask = &(def_kn_fp_mask[base]);
- icMain = IcoLib_GetIconByHandle(mainMask->hIcolibItem);
-
- if (icMain) {
- KN_FP_MASK* overlayMask = (overlay != -1) ? &(def_kn_fp_overlays_mask[overlay]) : nullptr;
- KN_FP_MASK* overlay2Mask = (overlay2 != -1) ? &(def_kn_fp_overlays2_mask[overlay2]) : nullptr;
- KN_FP_MASK* overlay3Mask = (overlay3 != -1) ? &(def_kn_fp_overlays3_mask[overlay3]) : nullptr;
- KN_FP_MASK* overlay4Mask = (overlay4 != -1) ? &(def_kn_fp_overlays4_mask[overlay4]) : nullptr;
- icOverlay = (overlayMask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlayMask->hIcolibItem);
- icOverlay2 = (overlay2Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay2Mask->hIcolibItem);
- icOverlay3 = (overlay3Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay3Mask->hIcolibItem);
- icOverlay4 = (overlay4Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay4Mask->hIcolibItem);
-
- hIcon = icMain;
-
- if (overlayMask)
- hTmp = hIcon = CreateJoinedIcon(hIcon, icOverlay);
-
- if (overlay2Mask) {
- hIcon = CreateJoinedIcon(hIcon, icOverlay2);
- if (hTmp) DestroyIcon(hTmp);
- hTmp = hIcon;
- }
-
- if (overlay3Mask) {
- hIcon = CreateJoinedIcon(hIcon, icOverlay3);
- if (hTmp) DestroyIcon(hTmp);
- hTmp = hIcon;
- }
-
- if (overlay4Mask) {
- hIcon = CreateJoinedIcon(hIcon, icOverlay4);
- if (hTmp) DestroyIcon(hTmp);
- }
- }
-
- if (hIcon == icMain)
- hIcon = CopyIcon(icMain);
-
- IcoLib_ReleaseIcon(icMain);
- IcoLib_ReleaseIcon(icOverlay);
- IcoLib_ReleaseIcon(icOverlay2);
- IcoLib_ReleaseIcon(icOverlay3);
- IcoLib_ReleaseIcon(icOverlay4);
- return hIcon;
-}
-
-/******************************************************************************
- * Futher routines is for creating joined 'overlay' icons.
- ******************************************************************************/
-
- /*
- * CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy
- */
-
-HBITMAP __inline CreateBitmap32(int cx, int cy)
-{
- return CreateBitmap32Point(cx, cy, nullptr);
-}
-
-/*
-* CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy and put reference
-* to new bitmap pixel image memory area to void ** bits
-*/
-HBITMAP __fastcall CreateBitmap32Point(int cx, int cy, LPVOID* bits)
-{
- LPVOID ptPixels = nullptr;
-
- if (cx < 0 || cy < 0) return nullptr;
-
- BITMAPINFO bmpi = { 0 };
- bmpi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmpi.bmiHeader.biWidth = cx;
- bmpi.bmiHeader.biHeight = cy;
- bmpi.bmiHeader.biPlanes = 1;
- bmpi.bmiHeader.biBitCount = 32;
- HBITMAP DirectBitmap = CreateDIBSection(nullptr, &bmpi, DIB_RGB_COLORS, &ptPixels, nullptr, 0);
-
- GdiFlush();
- if (ptPixels) memset(ptPixels, 0, cx * cy * 4);
- if (bits != nullptr) *bits = ptPixels;
-
- return DirectBitmap;
-}
-
-/*
-* checkHasAlfa - checks if image has at least one uint8_t in alpha channel
-* that is not a 0. (is image real 32 bit or just 24 bit)
-*/
-BOOL __fastcall checkHasAlfa(LPBYTE from, int width, int height)
-{
- LPDWORD pt = (LPDWORD)from;
- LPDWORD lim = pt + width * height;
- while (pt < lim)
- {
- if (*pt & 0xFF000000)
- return TRUE;
- pt++;
- }
-
- return FALSE;
-}
-
-/*
-* checkMaskUsed - checks if mask image has at least one that is not a 0.
-* Not sure is it required or not
-*/
-BOOL __fastcall checkMaskUsed(LPBYTE from)
-{
- int i;
- for (i = 0; i < 16 * 16 / 8; i++)
- {
- if (from[i] != 0) return TRUE;
- }
- return FALSE;
-}
-
-/*
-* GetMaskBit - return value of apropriate mask bit in line at x position
-*/
-BOOL __inline GetMaskBit(LPBYTE line, int x)
-{
- return ((*(line + (x >> 3))) & (0x01 << (7 - (x & 0x07)))) != 0;
-}
-
-/*
-* blend - alpha blend ARGB values of 2 pixels. X1 - underlaying,
-* X2 - overlaying points.
-*/
-uint32_t __fastcall blend(uint32_t X1, uint32_t X2)
-{
- RGBA* q1 = (RGBA*)&X1;
- RGBA* q2 = (RGBA*)&X2;
- uint8_t a_1 = ~q1->a;
- uint8_t a_2 = ~q2->a;
- uint16_t am = q1->a * a_2;
-
- uint16_t ar = q1->a + ((a_1 * q2->a) / 255);
- // if a2 more than 0 than result should be more
- // or equal (if a1==0) to a2, else in combination
- // with mask we can get here black points
-
- ar = (q2->a > ar) ? q2->a : ar;
-
- if (ar == 0) return 0;
-
- {
- uint16_t arm = ar * 255;
- uint16_t rr = ((q1->r * am + q2->r * q2->a * 255)) / arm;
- uint16_t gr = ((q1->g * am + q2->g * q2->a * 255)) / arm;
- uint16_t br = ((q1->b * am + q2->b * q2->a * 255)) / arm;
- return (ar << 24) | (rr << 16) | (gr << 8) | br;
- }
-}
-
-/*
-* CreateJoinedIcon - creates new icon by drawing hTop over hBottom.
-*/
-HICON __fastcall CreateJoinedIcon(HICON hBottom, HICON hTop)
-{
- BOOL drawn = FALSE;
- HDC tempDC, tempDC2, tempDC3;
- HICON res = nullptr;
- HBITMAP oImage, nImage;
- HBITMAP nMask, hbm, obmp, obmp2;
- LPBYTE ptPixels = nullptr;
- ICONINFO iNew = { 0 };
- uint8_t p[32] = { 0 };
-
- tempDC = CreateCompatibleDC(nullptr);
- nImage = CreateBitmap32Point(16, 16, (LPVOID*)&ptPixels);
- oImage = (HBITMAP)SelectObject(tempDC, nImage);
-
- ICONINFO iciBottom = { 0 };
- ICONINFO iciTop = { 0 };
-
- BITMAP bmp_top = { 0 };
- BITMAP bmp_top_mask = { 0 };
-
- BITMAP bmp_bottom = { 0 };
- BITMAP bmp_bottom_mask = { 0 };
-
- GetIconInfo(hBottom, &iciBottom);
- GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
- GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);
-
- GetIconInfo(hTop, &iciTop);
- GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
- GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);
-
- if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32)
- {
- LPBYTE BottomBuffer, TopBuffer, BottomMaskBuffer, TopMaskBuffer;
- LPBYTE bb, tb, bmb, tmb;
- LPBYTE db = ptPixels;
- int vstep_d = 16 * 4;
- int vstep_b = bmp_bottom.bmWidthBytes;
- int vstep_t = bmp_top.bmWidthBytes;
- int vstep_bm = bmp_bottom_mask.bmWidthBytes;
- int vstep_tm = bmp_top_mask.bmWidthBytes;
-
- if (bmp_bottom.bmBits)
- bb = BottomBuffer = (LPBYTE)bmp_bottom.bmBits;
- else
- {
- BottomBuffer = (LPBYTE)_alloca(bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes);
- GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes, BottomBuffer);
- bb = BottomBuffer + vstep_b * (bmp_bottom.bmHeight - 1);
- vstep_b = -vstep_b;
- }
- if (bmp_top.bmBits)
- tb = TopBuffer = (LPBYTE)bmp_top.bmBits;
- else
- {
- TopBuffer = (LPBYTE)_alloca(bmp_top.bmHeight * bmp_top.bmWidthBytes);
- GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight * bmp_top.bmWidthBytes, TopBuffer);
- tb = TopBuffer + vstep_t * (bmp_top.bmHeight - 1);
- vstep_t = -vstep_t;
- }
- if (bmp_bottom_mask.bmBits)
- bmb = BottomMaskBuffer = (LPBYTE)bmp_bottom_mask.bmBits;
- else
- {
- BottomMaskBuffer = (LPBYTE)_alloca(bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes);
- GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
- bmb = BottomMaskBuffer + vstep_bm * (bmp_bottom_mask.bmHeight - 1);
- vstep_bm = -vstep_bm;
- }
- if (bmp_top_mask.bmBits)
- tmb = TopMaskBuffer = (LPBYTE)bmp_top_mask.bmBits;
- else
- {
- TopMaskBuffer = (LPBYTE)_alloca(bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes);
- GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes, TopMaskBuffer);
- tmb = TopMaskBuffer + vstep_tm * (bmp_top_mask.bmHeight - 1);
- vstep_tm = -vstep_tm;
- }
- {
- int x; int y;
- BOOL topHasAlpha = checkHasAlfa(TopBuffer, bmp_top.bmWidth, bmp_top.bmHeight);
- BOOL bottomHasAlpha = checkHasAlfa(BottomBuffer, bmp_bottom.bmWidth, bmp_bottom.bmHeight);
- BOOL topMaskUsed = !topHasAlpha && checkMaskUsed(TopMaskBuffer);
- BOOL bottomMaskUsed = !bottomHasAlpha && checkMaskUsed(BottomMaskBuffer);
-
- for (y = 0; y < 16; y++)
- {
- for (x = 0; x < 16; x++)
- {
- uint32_t bottom_d = ((LPDWORD)bb)[x];
- uint32_t top_d = ((LPDWORD)tb)[x];
-
- if (topMaskUsed)
- {
- if (GetMaskBit(tmb, x))
- top_d &= 0x00FFFFFF;
- else
- top_d |= 0xFF000000;
- }
- else if (!topHasAlpha)
- top_d |= 0xFF000000;
-
- if (bottomMaskUsed)
- {
- if (GetMaskBit(bmb, x))
- bottom_d &= 0x00FFFFFF;
- else
- bottom_d |= 0xFF000000;
- }
- else if (!bottomHasAlpha)
- bottom_d |= 0xFF000000;
-
- ((LPDWORD)db)[x] = blend(bottom_d, top_d);
- }
- bb += vstep_b;
- tb += vstep_t;
- bmb += vstep_bm;
- tmb += vstep_tm;
- db += vstep_d;
- }
- }
-
- drawn = TRUE;
- }
-
- DeleteObject(iciBottom.hbmColor);
- DeleteObject(iciTop.hbmColor);
- DeleteObject(iciBottom.hbmMask);
- DeleteObject(iciTop.hbmMask);
-
- if (!drawn) {
- DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL);
- DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL);
- }
-
- nMask = CreateBitmap(16, 16, 1, 1, p);
- tempDC2 = CreateCompatibleDC(nullptr);
- tempDC3 = CreateCompatibleDC(nullptr);
- hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
- obmp = (HBITMAP)SelectObject(tempDC2, nMask);
- obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
- DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK);
- DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK);
- BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);
-
- GdiFlush();
-
- SelectObject(tempDC2, obmp);
- DeleteDC(tempDC2);
-
- SelectObject(tempDC3, obmp2);
- DeleteDC(tempDC3);
-
- SelectObject(tempDC, oImage);
- DeleteDC(tempDC);
-
- DeleteObject(hbm);
-
- iNew.fIcon = TRUE;
- iNew.hbmColor = nImage;
- iNew.hbmMask = nMask;
- res = CreateIconIndirect(&iNew);
-
- DeleteObject(nImage);
- DeleteObject(nMask);
-
- return res;
-}
-
-HANDLE __fastcall GetIconIndexFromFI(LPTSTR szMirVer)
-{
- int base, overlay, overlay2, overlay3, overlay4;
- GetIconsIndexes(szMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4);
- if (base == -1)
- return INVALID_HANDLE_VALUE;
-
- // MAX: 256 + 64 + 64 + 64 + 64
- FOUNDINFO tmp = { base, ((overlay & 0xFF) << 18) | ((overlay2 & 0x3F) << 12) | ((overlay3 & 0x3F) << 6) | (overlay4 & 0x3F) };
- auto *F = arFI.find(&tmp);
- if (F != nullptr)
- return F->hRegisteredImage;
-
- // not found - then add
- F = new FOUNDINFO(tmp);
- HICON hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4);
- if (hIcon != nullptr) {
- F->hRegisteredImage = ExtraIcon_AddIcon(hIcon);
- DestroyIcon(hIcon);
- }
- else F->hRegisteredImage = INVALID_HANDLE_VALUE;
-
- arFI.insert(F);
-
- return F->hRegisteredImage;
-}
-
-VOID ClearFI()
-{
- arFI.destroy();
-}
-
-/****************************************************************************************
-* ServiceGetClientIconW
-* MS_FP_GETCLIENTICONW service implementation.
-* wParam - LPWSTR MirVer value to get client for.
-* lParam - int noCopy - if wParam is equal to "1" will return icon handler without copiing icon.
-* ICON IS ALWAYS COPIED!!!
-*/
-
-static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM)
-{
- LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for.
- if (wszMirVer == nullptr)
- return 0;
-
- int base, overlay, overlay2, overlay3, overlay4;
- GetIconsIndexes(wszMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4);
-
- HICON hIcon = nullptr; // returned HICON
- if (base != -1)
- hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4);
-
- return (INT_PTR)hIcon;
-}
-
-/****************************************************************************************
- * ServiceGetClientDescrW
- * MS_FP_GETCLIENTDESCRW service implementation.
- * wParam - LPCWSTR MirVer value
- * lParam - (NULL) unused
- * returns LPCWSTR: client description (do not destroy) or NULL
- */
-
-static INT_PTR ServiceGetClientDescrW(WPARAM wParam, LPARAM)
-{
- LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for.
- if (wszMirVer == nullptr)
- return 0;
-
- LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer); _wcsupr(wszMirVerUp);
- if (mir_wstrcmp(wszMirVerUp, L"?") == 0)
- return (INT_PTR)def_kn_fp_mask[UNKNOWN_MASK_NUMBER].szClientDescription;
-
- for (int index = 0; index < DEFAULT_KN_FP_MASK_COUNT; index++)
- if (WildCompare(wszMirVerUp, def_kn_fp_mask[index].szMaskUpper))
- return (INT_PTR)def_kn_fp_mask[index].szClientDescription;
-
- return NULL;
-}
-
-/****************************************************************************************
- * ServiceSameClientW
- * MS_FP_SAMECLIENTSW service implementation.
- * wParam - LPWSTR first MirVer value
- * lParam - LPWSTR second MirVer value
- * returns LPCWSTR: client description (do not destroy) if clients are same or NULL
- */
-
-static INT_PTR ServiceSameClientsW(WPARAM wParam, LPARAM lParam)
-{
- if (!wParam || !lParam)
- return NULL; //one of its is not null
-
- INT_PTR res1 = ServiceGetClientDescrW(wParam, 0);
- INT_PTR res2 = ServiceGetClientDescrW(lParam, 0);
- return (res1 == res2 && res1 != 0) ? res1 : NULL;
-}
-
-/****************************************************************************************
-* OnExtraIconListRebuild
-* Set all registered indexes in array to EMPTY_EXTRA_ICON (unregistered icon)
-*/
-
-static int OnExtraIconListRebuild(WPARAM, LPARAM)
-{
- ClearFI();
- return 0;
-}
-
-/****************************************************************************************
-* OnIconsChanged
-*/
-
-static int OnIconsChanged(WPARAM, LPARAM)
-{
- ClearFI();
- return 0;
-}
-
-/****************************************************************************************
-* OnExtraImageApply
-* Try to get MirVer value from db for contact and if success calls ApplyFingerprintImage
-*/
-
-int OnExtraImageApply(WPARAM hContact, LPARAM)
-{
- if (hContact == NULL)
- return 0;
-
- ptrW tszMirver;
- char *szProto = Proto_GetBaseAccountName(hContact);
- if (szProto != nullptr)
- tszMirver = db_get_wsa(hContact, szProto, "MirVer");
-
- ApplyFingerprintImage(hContact, tszMirver);
- return 0;
-}
-
-/****************************************************************************************
-* OnContactSettingChanged
-* if contact settings changed apply new image or remove it
-*/
-
-static int OnContactSettingChanged(WPARAM hContact, LPARAM lParam)
-{
- if (hContact == NULL)
- return 0;
-
- DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
- if (cws && cws->szSetting && !strcmp(cws->szSetting, "MirVer")) {
- switch (cws->value.type) {
- case DBVT_UTF8:
- ApplyFingerprintImage(hContact, ptrW(mir_utf8decodeW(cws->value.pszVal)));
- break;
- case DBVT_ASCIIZ:
- ApplyFingerprintImage(hContact, _A2T(cws->value.pszVal));
- break;
- case DBVT_WCHAR:
- ApplyFingerprintImage(hContact, cws->value.pwszVal);
- break;
- default:
- ApplyFingerprintImage(hContact, nullptr);
- }
- }
- return 0;
-}
-
-/****************************************************************************************
-* OnSrmmWindowEvent
-* Monitors SRMM window's creation to draw a statusbar icon
-*/
-
-static int OnSrmmWindowEvent(WPARAM, LPARAM lParam)
-{
- if (!g_plugin.getByte("StatusBarIcon", 1))
- return 0;
-
- MessageWindowEventData *event = (MessageWindowEventData *)lParam;
- if (event == nullptr)
- return 0;
-
- if (event->uType == MSG_WINDOW_EVT_OPEN) {
- ptrW ptszMirVer;
- char *szProto = Proto_GetBaseAccountName(event->hContact);
- if (szProto != nullptr)
- ptszMirVer = db_get_wsa(event->hContact, szProto, "MirVer");
- SetSrmmIcon(event->hContact, ptszMirVer);
- arMonitoredWindows.insert((HANDLE)event->hContact);
- }
- else if (event->uType == MSG_WINDOW_EVT_CLOSE)
- arMonitoredWindows.remove((HANDLE)event->hContact);
-
- return 0;
-}
-
-/****************************************************************************************
-* OnModulesLoaded
-* Hook necessary events here
-*/
-
-int OnModulesLoaded(WPARAM, LPARAM)
-{
- g_LPCodePage = Langpack_GetDefaultCodePage();
-
- //Hook necessary events
- HookEvent(ME_SKIN_ICONSCHANGED, OnIconsChanged);
- HookEvent(ME_MSG_WINDOWEVENT, OnSrmmWindowEvent);
-
- HookEvent(ME_MC_DEFAULTTCHANGED, OnExtraImageApply);
-
- PathToAbsoluteW(DEFAULT_SKIN_FOLDER, g_szSkinLib);
-
- RegisterIcons();
-
- hExtraIcon = ExtraIcon_RegisterCallback("Client", LPGEN("Fingerprint"), "client_Miranda_unknown",
- OnExtraIconListRebuild, OnExtraImageApply, OnExtraIconClick);
-
- if (g_plugin.getByte("StatusBarIcon", 1)) {
- StatusIconData sid = {};
- sid.szModule = MODULENAME;
- sid.flags = MBF_HIDDEN;
- sid.dwId = 1;
- Srmm_AddIcon(&sid, &g_plugin);
- }
-
- return 0;
-}
-
-void InitFingerModule()
-{
- HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
- HookEvent(ME_OPT_INITIALISE, OnOptInitialise);
- HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
-
- CreateServiceFunction(MS_FP_SAMECLIENTSW, ServiceSameClientsW);
- CreateServiceFunction(MS_FP_GETCLIENTDESCRW, ServiceGetClientDescrW);
- CreateServiceFunction(MS_FP_GETCLIENTICONW, ServiceGetClientIconW);
-}
+fv/*
+Fingerprint NG (client version) icons module for Miranda NG
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+//Start of header
+#include "stdafx.h"
+
+static UINT g_LPCodePage;
+static wchar_t g_szSkinLib[MAX_PATH];
+static HANDLE hExtraIcon = nullptr;
+static HANDLE hFolderChanged = nullptr, hIconFolder = nullptr;
+
+static int CompareFI(const FOUNDINFO *p1, const FOUNDINFO *p2)
+{
+ if (p1->iBase != p2->iBase)
+ return p1->iBase - p2->iBase;
+ return p1->iOverlay - p2->iOverlay;
+}
+
+static OBJLIST<FOUNDINFO> arFI(50, CompareFI);
+
+static LIST<void> arMonitoredWindows(3, PtrKeySortT);
+
+static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM lParam);
+
+/*
+* Prepare
+* prepares upperstring masks and registers them in IcoLib
+*/
+
+static wchar_t* getSectionName(int flag)
+{
+ switch (flag)
+ {
+#include "finger_groups.h"
+ }
+ return nullptr;
+}
+
+void __fastcall Prepare(KN_FP_MASK* mask, bool bEnable)
+{
+ mask->szMaskUpper = nullptr;
+
+ if (mask->hIcolibItem)
+ IcoLib_RemoveIcon(mask->szIconName);
+ mask->hIcolibItem = nullptr;
+
+ if (!mask->szMask || !bEnable)
+ return;
+
+ size_t iMaskLen = mir_wstrlen(mask->szMask) + 1;
+ LPTSTR pszNewMask = (LPTSTR)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, iMaskLen * sizeof(wchar_t));
+ wcscpy_s(pszNewMask, iMaskLen, mask->szMask);
+ _wcsupr_s(pszNewMask, iMaskLen);
+ mask->szMaskUpper = pszNewMask;
+
+ wchar_t destfile[MAX_PATH];
+ if (mask->iIconIndex == IDI_NOTFOUND || mask->iIconIndex == IDI_UNKNOWN || mask->iIconIndex == IDI_UNDETECTED)
+ GetModuleFileName(g_plugin.getInst(), destfile, MAX_PATH);
+ else {
+ wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE);
+
+ struct _stat64i32 stFileInfo;
+ if (_wstat(destfile, &stFileInfo) == -1)
+ return;
+ }
+
+ LPTSTR SectName = getSectionName(mask->iSectionFlag);
+ if (SectName == nullptr)
+ return;
+
+ SKINICONDESC sid = {};
+ sid.flags = SIDF_ALL_UNICODE;
+ sid.section.w = SectName;
+ sid.pszName = mask->szIconName;
+ sid.description.w = mask->szClientDescription;
+ sid.defaultFile.w = destfile;
+ sid.iDefaultIndex = -mask->iIconIndex;
+ mask->hIcolibItem = g_plugin.addIcon(&sid);
+}
+
+/*
+* Register icons
+*/
+
+void RegisterIcons()
+{
+ // prepare masks
+ int i;
+
+ if (hHeap)
+ HeapDestroy(hHeap);
+ hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
+
+ for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++)
+ Prepare(&def_kn_fp_mask[i], true);
+
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS_COUNT; i++)
+ Prepare(&def_kn_fp_overlays_mask[i], true);
+
+ if (g_plugin.getByte("GroupMirandaVersion", 0)) {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
+ Prepare(&def_kn_fp_overlays2_mask[i], true);
+ }
+ else {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS2_NO_VER_COUNT; i++)
+ Prepare(&def_kn_fp_overlays2_mask[i], true);
+ for (; i < DEFAULT_KN_FP_OVERLAYS2_COUNT; i++)
+ Prepare(&def_kn_fp_overlays2_mask[i], false);
+ }
+
+ if (g_plugin.getByte("GroupOverlaysUnicode", 1)) {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
+ Prepare(&def_kn_fp_overlays3_mask[i], true);
+ }
+ else {
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS3_NO_UNICODE_COUNT; i++)
+ Prepare(&def_kn_fp_overlays3_mask[i], true);
+ for (; i < DEFAULT_KN_FP_OVERLAYS3_COUNT; i++)
+ Prepare(&def_kn_fp_overlays3_mask[i], false);
+ }
+
+ for (i = 0; i < DEFAULT_KN_FP_OVERLAYS4_COUNT; i++)
+ Prepare(&def_kn_fp_overlays4_mask[i], true);
+}
+
+/* ApplyFingerprintImage
+* 1)Try to find appropriate mask
+* 2)Register icon in extraimage list if not yet registered (EMPTY_EXTRA_ICON)
+* 3)Set ExtraImage for contact
+*/
+
+static void SetSrmmIcon(MCONTACT hContact, LPTSTR ptszMirver)
+{
+ if (mir_wstrlen(ptszMirver))
+ Srmm_ModifyIcon(hContact, MODULENAME, 1, (HICON)ServiceGetClientIconW((WPARAM)ptszMirver, TRUE), ptszMirver);
+ else
+ Srmm_SetIconFlags(hContact, MODULENAME, 1, MBF_HIDDEN);
+}
+
+int __fastcall ApplyFingerprintImage(MCONTACT hContact, LPTSTR szMirVer)
+{
+ if (hContact == NULL)
+ return 0;
+
+ HANDLE hImage = INVALID_HANDLE_VALUE;
+ if (szMirVer)
+ hImage = GetIconIndexFromFI(szMirVer);
+
+ ExtraIcon_SetIcon(hExtraIcon, hContact, hImage);
+
+ if (arMonitoredWindows.getIndex((HANDLE)hContact) != -1)
+ SetSrmmIcon(hContact, szMirVer);
+
+ return 0;
+}
+
+int OnExtraIconClick(WPARAM wParam, LPARAM, LPARAM)
+{
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, NULL);
+ return 0;
+}
+
+
+/*
+* WildCompare
+* Compare 'name' string with 'mask' strings.
+* Masks can contain '*' or '?' wild symbols
+* Asterics '*' symbol covers 'empty' symbol too e.g WildCompare("Tst","T*st*"), returns TRUE
+* In order to handle situation 'at least one any sybol' use "?*" combination:
+* e.g WildCompare("Tst","T?*st*"), returns FALSE, but both WildCompare("Test","T?*st*") and
+* WildCompare("Teeest","T?*st*") return TRUE.
+*
+* Function is case sensitive! so convert input or modify func to use _qtoupper()
+*
+* Mask can contain several submasks. In this case each submask (including first)
+* should start from '|' e.g: "|first*submask|second*mask".
+*
+* Dec 25, 2006 by FYR:
+* Added Exception to masks: the mask "|^mask3|mask2|mask1" means:
+* if NOT according to mask 3 AND (mask1 OR mask2)
+* EXCEPTION should be BEFORE main mask:
+* IF Exception match - the comparing stops as FALSE
+* IF Exception does not match - comparing continue
+* IF Mask match - comparing stops as TRUE
+* IF Mask does not not match comparing continue
+*/
+BOOL __fastcall WildCompare(LPWSTR wszName, LPWSTR wszMask)
+{
+ if (wszMask == nullptr)
+ return NULL;
+
+ if (*wszMask != L'|')
+ return wildcmpw(wszName, wszMask);
+
+ size_t s = 1, e = 1;
+ LPWSTR wszTemp = (LPWSTR)_alloca(mir_wstrlen(wszMask) * sizeof(wchar_t) + sizeof(wchar_t));
+ BOOL bExcept;
+
+ while (wszMask[e] != L'\0')
+ {
+ s = e;
+ while (wszMask[e] != L'\0' && wszMask[e] != L'|') e++;
+
+ // exception mask
+ bExcept = (*(wszMask + s) == L'^');
+ if (bExcept) s++;
+
+ memcpy(wszTemp, wszMask + s, (e - s) * sizeof(wchar_t));
+ wszTemp[e - s] = L'\0';
+
+ if (wildcmpw(wszName, wszTemp))
+ return !bExcept;
+
+ if (wszMask[e] != L'\0')
+ e++;
+ else
+ return FALSE;
+ }
+ return FALSE;
+}
+
+static void MatchMasks(wchar_t* szMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4)
+{
+ int i = 0, j = -1, k = -1, n = -1, m = -1;
+
+ for (i = 0; i < DEFAULT_KN_FP_MASK_COUNT; i++) {
+ KN_FP_MASK& p = def_kn_fp_mask[i];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (!WildCompare(szMirVer, p.szMaskUpper))
+ continue;
+
+ if (p.iIconIndex != IDI_NOTFOUND && p.iIconIndex != IDI_UNKNOWN && p.iIconIndex != IDI_UNDETECTED) {
+ wchar_t destfile[MAX_PATH];
+ wcsncpy_s(destfile, g_szSkinLib, _TRUNCATE);
+
+ struct _stat64i32 stFileInfo;
+ if (_wstat(destfile, &stFileInfo) == -1)
+ i = NOTFOUND_MASK_NUMBER;
+ }
+ break;
+ }
+ if (i == DEFAULT_KN_FP_MASK_COUNT - 1)
+ i = -1;
+
+ else if (!def_kn_fp_mask[i].fNotUseOverlay && i < DEFAULT_KN_FP_MASK_COUNT) {
+ for (j = 0; j < DEFAULT_KN_FP_OVERLAYS_COUNT; j++) {
+ KN_FP_MASK& p = def_kn_fp_overlays_mask[j];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (!WildCompare(szMirVer, p.szMaskUpper))
+ continue;
+
+ struct _stat64i32 stFileInfo;
+ if (_wstat(g_szSkinLib, &stFileInfo) != -1)
+ break;
+ }
+
+ for (k = 0; k < DEFAULT_KN_FP_OVERLAYS2_COUNT; k++) {
+ KN_FP_MASK& p = def_kn_fp_overlays2_mask[k];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (WildCompare(szMirVer, p.szMaskUpper))
+ break;
+ }
+
+ for (n = 0; n < DEFAULT_KN_FP_OVERLAYS3_COUNT; n++) {
+ KN_FP_MASK& p = def_kn_fp_overlays3_mask[n];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (WildCompare(szMirVer, p.szMaskUpper))
+ break;
+ }
+
+ for (m = 0; m < DEFAULT_KN_FP_OVERLAYS4_COUNT; m++) {
+ KN_FP_MASK& p = def_kn_fp_overlays4_mask[m];
+ if (p.hIcolibItem == nullptr)
+ continue;
+
+ if (WildCompare(szMirVer, p.szMaskUpper))
+ break;
+ }
+ }
+
+ *base = (i < DEFAULT_KN_FP_MASK_COUNT) ? i : -1;
+ *overlay = (j < DEFAULT_KN_FP_OVERLAYS_COUNT) ? j : -1;
+ *overlay2 = (k < DEFAULT_KN_FP_OVERLAYS2_COUNT) ? k : -1;
+ *overlay3 = (n < DEFAULT_KN_FP_OVERLAYS3_COUNT) ? n : -1;
+ *overlay4 = (m < DEFAULT_KN_FP_OVERLAYS4_COUNT) ? m : -1;
+}
+
+/* GetIconsIndexes
+* Retrieves Icons indexes by Mirver
+*/
+
+void __fastcall GetIconsIndexes(LPWSTR wszMirVer, int *base, int *overlay, int *overlay2, int *overlay3, int *overlay4)
+{
+ if (mir_wstrcmp(wszMirVer, L"?") == 0) {
+ *base = UNKNOWN_MASK_NUMBER;
+ *overlay = -1;
+ *overlay2 = -1;
+ *overlay3 = -1;
+ *overlay4 = -1;
+ return;
+ }
+
+ LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer);
+ _wcsupr(wszMirVerUp);
+ MatchMasks(wszMirVerUp, base, overlay, overlay2, overlay3, overlay4);
+}
+
+/*
+* CreateIconFromIndexes
+* returns hIcon of joined icon by given indexes
+*/
+
+HICON __fastcall CreateIconFromIndexes(int base, int overlay, int overlay2, int overlay3, int overlay4)
+{
+ HICON hIcon = nullptr; // returned HICON
+ HICON hTmp = nullptr;
+ HICON icMain = nullptr;
+ HICON icOverlay = nullptr;
+ HICON icOverlay2 = nullptr;
+ HICON icOverlay3 = nullptr;
+ HICON icOverlay4 = nullptr;
+
+ KN_FP_MASK* mainMask = &(def_kn_fp_mask[base]);
+ icMain = IcoLib_GetIconByHandle(mainMask->hIcolibItem);
+
+ if (icMain) {
+ KN_FP_MASK* overlayMask = (overlay != -1) ? &(def_kn_fp_overlays_mask[overlay]) : nullptr;
+ KN_FP_MASK* overlay2Mask = (overlay2 != -1) ? &(def_kn_fp_overlays2_mask[overlay2]) : nullptr;
+ KN_FP_MASK* overlay3Mask = (overlay3 != -1) ? &(def_kn_fp_overlays3_mask[overlay3]) : nullptr;
+ KN_FP_MASK* overlay4Mask = (overlay4 != -1) ? &(def_kn_fp_overlays4_mask[overlay4]) : nullptr;
+ icOverlay = (overlayMask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlayMask->hIcolibItem);
+ icOverlay2 = (overlay2Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay2Mask->hIcolibItem);
+ icOverlay3 = (overlay3Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay3Mask->hIcolibItem);
+ icOverlay4 = (overlay4Mask == nullptr) ? nullptr : IcoLib_GetIconByHandle(overlay4Mask->hIcolibItem);
+
+ hIcon = icMain;
+
+ if (overlayMask)
+ hTmp = hIcon = CreateJoinedIcon(hIcon, icOverlay);
+
+ if (overlay2Mask) {
+ hIcon = CreateJoinedIcon(hIcon, icOverlay2);
+ if (hTmp) DestroyIcon(hTmp);
+ hTmp = hIcon;
+ }
+
+ if (overlay3Mask) {
+ hIcon = CreateJoinedIcon(hIcon, icOverlay3);
+ if (hTmp) DestroyIcon(hTmp);
+ hTmp = hIcon;
+ }
+
+ if (overlay4Mask) {
+ hIcon = CreateJoinedIcon(hIcon, icOverlay4);
+ if (hTmp) DestroyIcon(hTmp);
+ }
+ }
+
+ if (hIcon == icMain)
+ hIcon = CopyIcon(icMain);
+
+ IcoLib_ReleaseIcon(icMain);
+ IcoLib_ReleaseIcon(icOverlay);
+ IcoLib_ReleaseIcon(icOverlay2);
+ IcoLib_ReleaseIcon(icOverlay3);
+ IcoLib_ReleaseIcon(icOverlay4);
+ return hIcon;
+}
+
+/******************************************************************************
+ * Futher routines is for creating joined 'overlay' icons.
+ ******************************************************************************/
+
+ /*
+ * CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy
+ */
+
+HBITMAP __inline CreateBitmap32(int cx, int cy)
+{
+ return CreateBitmap32Point(cx, cy, nullptr);
+}
+
+/*
+* CreateBitmap32 - Create DIB 32 bitmap with sizes cx*cy and put reference
+* to new bitmap pixel image memory area to void ** bits
+*/
+HBITMAP __fastcall CreateBitmap32Point(int cx, int cy, LPVOID* bits)
+{
+ LPVOID ptPixels = nullptr;
+
+ if (cx < 0 || cy < 0) return nullptr;
+
+ BITMAPINFO bmpi = { 0 };
+ bmpi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmpi.bmiHeader.biWidth = cx;
+ bmpi.bmiHeader.biHeight = cy;
+ bmpi.bmiHeader.biPlanes = 1;
+ bmpi.bmiHeader.biBitCount = 32;
+ HBITMAP DirectBitmap = CreateDIBSection(nullptr, &bmpi, DIB_RGB_COLORS, &ptPixels, nullptr, 0);
+
+ GdiFlush();
+ if (ptPixels) memset(ptPixels, 0, cx * cy * 4);
+ if (bits != nullptr) *bits = ptPixels;
+
+ return DirectBitmap;
+}
+
+/*
+* checkHasAlfa - checks if image has at least one uint8_t in alpha channel
+* that is not a 0. (is image real 32 bit or just 24 bit)
+*/
+BOOL __fastcall checkHasAlfa(LPBYTE from, int width, int height)
+{
+ LPDWORD pt = (LPDWORD)from;
+ LPDWORD lim = pt + width * height;
+ while (pt < lim)
+ {
+ if (*pt & 0xFF000000)
+ return TRUE;
+ pt++;
+ }
+
+ return FALSE;
+}
+
+/*
+* checkMaskUsed - checks if mask image has at least one that is not a 0.
+* Not sure is it required or not
+*/
+BOOL __fastcall checkMaskUsed(LPBYTE from)
+{
+ int i;
+ for (i = 0; i < 16 * 16 / 8; i++)
+ {
+ if (from[i] != 0) return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+* GetMaskBit - return value of apropriate mask bit in line at x position
+*/
+BOOL __inline GetMaskBit(LPBYTE line, int x)
+{
+ return ((*(line + (x >> 3))) & (0x01 << (7 - (x & 0x07)))) != 0;
+}
+
+/*
+* blend - alpha blend ARGB values of 2 pixels. X1 - underlaying,
+* X2 - overlaying points.
+*/
+uint32_t __fastcall blend(uint32_t X1, uint32_t X2)
+{
+ RGBA* q1 = (RGBA*)&X1;
+ RGBA* q2 = (RGBA*)&X2;
+ uint8_t a_1 = ~q1->a;
+ uint8_t a_2 = ~q2->a;
+ uint16_t am = q1->a * a_2;
+
+ uint16_t ar = q1->a + ((a_1 * q2->a) / 255);
+ // if a2 more than 0 than result should be more
+ // or equal (if a1==0) to a2, else in combination
+ // with mask we can get here black points
+
+ ar = (q2->a > ar) ? q2->a : ar;
+
+ if (ar == 0) return 0;
+
+ {
+ uint16_t arm = ar * 255;
+ uint16_t rr = ((q1->r * am + q2->r * q2->a * 255)) / arm;
+ uint16_t gr = ((q1->g * am + q2->g * q2->a * 255)) / arm;
+ uint16_t br = ((q1->b * am + q2->b * q2->a * 255)) / arm;
+ return (ar << 24) | (rr << 16) | (gr << 8) | br;
+ }
+}
+
+/*
+* CreateJoinedIcon - creates new icon by drawing hTop over hBottom.
+*/
+HICON __fastcall CreateJoinedIcon(HICON hBottom, HICON hTop)
+{
+ BOOL drawn = FALSE;
+ HDC tempDC, tempDC2, tempDC3;
+ HICON res = nullptr;
+ HBITMAP oImage, nImage;
+ HBITMAP nMask, hbm, obmp, obmp2;
+ LPBYTE ptPixels = nullptr;
+ ICONINFO iNew = { 0 };
+ uint8_t p[32] = { 0 };
+
+ tempDC = CreateCompatibleDC(nullptr);
+ nImage = CreateBitmap32Point(16, 16, (LPVOID*)&ptPixels);
+ oImage = (HBITMAP)SelectObject(tempDC, nImage);
+
+ ICONINFO iciBottom = { 0 };
+ ICONINFO iciTop = { 0 };
+
+ BITMAP bmp_top = { 0 };
+ BITMAP bmp_top_mask = { 0 };
+
+ BITMAP bmp_bottom = { 0 };
+ BITMAP bmp_bottom_mask = { 0 };
+
+ GetIconInfo(hBottom, &iciBottom);
+ GetObject(iciBottom.hbmColor, sizeof(BITMAP), &bmp_bottom);
+ GetObject(iciBottom.hbmMask, sizeof(BITMAP), &bmp_bottom_mask);
+
+ GetIconInfo(hTop, &iciTop);
+ GetObject(iciTop.hbmColor, sizeof(BITMAP), &bmp_top);
+ GetObject(iciTop.hbmMask, sizeof(BITMAP), &bmp_top_mask);
+
+ if (bmp_bottom.bmBitsPixel == 32 && bmp_top.bmBitsPixel == 32)
+ {
+ LPBYTE BottomBuffer, TopBuffer, BottomMaskBuffer, TopMaskBuffer;
+ LPBYTE bb, tb, bmb, tmb;
+ LPBYTE db = ptPixels;
+ int vstep_d = 16 * 4;
+ int vstep_b = bmp_bottom.bmWidthBytes;
+ int vstep_t = bmp_top.bmWidthBytes;
+ int vstep_bm = bmp_bottom_mask.bmWidthBytes;
+ int vstep_tm = bmp_top_mask.bmWidthBytes;
+
+ if (bmp_bottom.bmBits)
+ bb = BottomBuffer = (LPBYTE)bmp_bottom.bmBits;
+ else
+ {
+ BottomBuffer = (LPBYTE)_alloca(bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmColor, bmp_bottom.bmHeight * bmp_bottom.bmWidthBytes, BottomBuffer);
+ bb = BottomBuffer + vstep_b * (bmp_bottom.bmHeight - 1);
+ vstep_b = -vstep_b;
+ }
+ if (bmp_top.bmBits)
+ tb = TopBuffer = (LPBYTE)bmp_top.bmBits;
+ else
+ {
+ TopBuffer = (LPBYTE)_alloca(bmp_top.bmHeight * bmp_top.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmColor, bmp_top.bmHeight * bmp_top.bmWidthBytes, TopBuffer);
+ tb = TopBuffer + vstep_t * (bmp_top.bmHeight - 1);
+ vstep_t = -vstep_t;
+ }
+ if (bmp_bottom_mask.bmBits)
+ bmb = BottomMaskBuffer = (LPBYTE)bmp_bottom_mask.bmBits;
+ else
+ {
+ BottomMaskBuffer = (LPBYTE)_alloca(bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes);
+ GetBitmapBits(iciBottom.hbmMask, bmp_bottom_mask.bmHeight * bmp_bottom_mask.bmWidthBytes, BottomMaskBuffer);
+ bmb = BottomMaskBuffer + vstep_bm * (bmp_bottom_mask.bmHeight - 1);
+ vstep_bm = -vstep_bm;
+ }
+ if (bmp_top_mask.bmBits)
+ tmb = TopMaskBuffer = (LPBYTE)bmp_top_mask.bmBits;
+ else
+ {
+ TopMaskBuffer = (LPBYTE)_alloca(bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes);
+ GetBitmapBits(iciTop.hbmMask, bmp_top_mask.bmHeight * bmp_top_mask.bmWidthBytes, TopMaskBuffer);
+ tmb = TopMaskBuffer + vstep_tm * (bmp_top_mask.bmHeight - 1);
+ vstep_tm = -vstep_tm;
+ }
+ {
+ int x; int y;
+ BOOL topHasAlpha = checkHasAlfa(TopBuffer, bmp_top.bmWidth, bmp_top.bmHeight);
+ BOOL bottomHasAlpha = checkHasAlfa(BottomBuffer, bmp_bottom.bmWidth, bmp_bottom.bmHeight);
+ BOOL topMaskUsed = !topHasAlpha && checkMaskUsed(TopMaskBuffer);
+ BOOL bottomMaskUsed = !bottomHasAlpha && checkMaskUsed(BottomMaskBuffer);
+
+ for (y = 0; y < 16; y++)
+ {
+ for (x = 0; x < 16; x++)
+ {
+ uint32_t bottom_d = ((LPDWORD)bb)[x];
+ uint32_t top_d = ((LPDWORD)tb)[x];
+
+ if (topMaskUsed)
+ {
+ if (GetMaskBit(tmb, x))
+ top_d &= 0x00FFFFFF;
+ else
+ top_d |= 0xFF000000;
+ }
+ else if (!topHasAlpha)
+ top_d |= 0xFF000000;
+
+ if (bottomMaskUsed)
+ {
+ if (GetMaskBit(bmb, x))
+ bottom_d &= 0x00FFFFFF;
+ else
+ bottom_d |= 0xFF000000;
+ }
+ else if (!bottomHasAlpha)
+ bottom_d |= 0xFF000000;
+
+ ((LPDWORD)db)[x] = blend(bottom_d, top_d);
+ }
+ bb += vstep_b;
+ tb += vstep_t;
+ bmb += vstep_bm;
+ tmb += vstep_tm;
+ db += vstep_d;
+ }
+ }
+
+ drawn = TRUE;
+ }
+
+ DeleteObject(iciBottom.hbmColor);
+ DeleteObject(iciTop.hbmColor);
+ DeleteObject(iciBottom.hbmMask);
+ DeleteObject(iciTop.hbmMask);
+
+ if (!drawn) {
+ DrawIconEx(tempDC, 0, 0, hBottom, 16, 16, 0, nullptr, DI_NORMAL);
+ DrawIconEx(tempDC, 0, 0, hTop, 16, 16, 0, nullptr, DI_NORMAL);
+ }
+
+ nMask = CreateBitmap(16, 16, 1, 1, p);
+ tempDC2 = CreateCompatibleDC(nullptr);
+ tempDC3 = CreateCompatibleDC(nullptr);
+ hbm = CreateCompatibleBitmap(tempDC3, 16, 16);
+ obmp = (HBITMAP)SelectObject(tempDC2, nMask);
+ obmp2 = (HBITMAP)SelectObject(tempDC3, hbm);
+ DrawIconEx(tempDC2, 0, 0, hBottom, 16, 16, 0, nullptr, DI_MASK);
+ DrawIconEx(tempDC3, 0, 0, hTop, 16, 16, 0, nullptr, DI_MASK);
+ BitBlt(tempDC2, 0, 0, 16, 16, tempDC3, 0, 0, SRCAND);
+
+ GdiFlush();
+
+ SelectObject(tempDC2, obmp);
+ DeleteDC(tempDC2);
+
+ SelectObject(tempDC3, obmp2);
+ DeleteDC(tempDC3);
+
+ SelectObject(tempDC, oImage);
+ DeleteDC(tempDC);
+
+ DeleteObject(hbm);
+
+ iNew.fIcon = TRUE;
+ iNew.hbmColor = nImage;
+ iNew.hbmMask = nMask;
+ res = CreateIconIndirect(&iNew);
+
+ DeleteObject(nImage);
+ DeleteObject(nMask);
+
+ return res;
+}
+
+HANDLE __fastcall GetIconIndexFromFI(LPTSTR szMirVer)
+{
+ int base, overlay, overlay2, overlay3, overlay4;
+ GetIconsIndexes(szMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4);
+ if (base == -1)
+ return INVALID_HANDLE_VALUE;
+
+ // MAX: 256 + 64 + 64 + 64 + 64
+ FOUNDINFO tmp = { base, ((overlay & 0xFF) << 18) | ((overlay2 & 0x3F) << 12) | ((overlay3 & 0x3F) << 6) | (overlay4 & 0x3F) };
+ auto *F = arFI.find(&tmp);
+ if (F != nullptr)
+ return F->hRegisteredImage;
+
+ // not found - then add
+ F = new FOUNDINFO(tmp);
+ HICON hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4);
+ if (hIcon != nullptr) {
+ F->hRegisteredImage = ExtraIcon_AddIcon(hIcon);
+ DestroyIcon(hIcon);
+ }
+ else F->hRegisteredImage = INVALID_HANDLE_VALUE;
+
+ arFI.insert(F);
+
+ return F->hRegisteredImage;
+}
+
+VOID ClearFI()
+{
+ arFI.destroy();
+}
+
+/****************************************************************************************
+* ServiceGetClientIconW
+* MS_FP_GETCLIENTICONW service implementation.
+* wParam - LPWSTR MirVer value to get client for.
+* lParam - int noCopy - if wParam is equal to "1" will return icon handler without copiing icon.
+* ICON IS ALWAYS COPIED!!!
+*/
+
+static INT_PTR ServiceGetClientIconW(WPARAM wParam, LPARAM)
+{
+ LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for.
+ if (wszMirVer == nullptr)
+ return 0;
+
+ int base, overlay, overlay2, overlay3, overlay4;
+ GetIconsIndexes(wszMirVer, &base, &overlay, &overlay2, &overlay3, &overlay4);
+
+ HICON hIcon = nullptr; // returned HICON
+ if (base != -1)
+ hIcon = CreateIconFromIndexes(base, overlay, overlay2, overlay3, overlay4);
+
+ return (INT_PTR)hIcon;
+}
+
+/****************************************************************************************
+ * ServiceGetClientDescrW
+ * MS_FP_GETCLIENTDESCRW service implementation.
+ * wParam - LPCWSTR MirVer value
+ * lParam - (NULL) unused
+ * returns LPCWSTR: client description (do not destroy) or NULL
+ */
+
+static INT_PTR ServiceGetClientDescrW(WPARAM wParam, LPARAM)
+{
+ LPWSTR wszMirVer = (LPWSTR)wParam; // MirVer value to get client for.
+ if (wszMirVer == nullptr)
+ return 0;
+
+ LPWSTR wszMirVerUp = NEWWSTR_ALLOCA(wszMirVer); _wcsupr(wszMirVerUp);
+ if (mir_wstrcmp(wszMirVerUp, L"?") == 0)
+ return (INT_PTR)def_kn_fp_mask[UNKNOWN_MASK_NUMBER].szClientDescription;
+
+ for (int index = 0; index < DEFAULT_KN_FP_MASK_COUNT; index++)
+ if (WildCompare(wszMirVerUp, def_kn_fp_mask[index].szMaskUpper))
+ return (INT_PTR)def_kn_fp_mask[index].szClientDescription;
+
+ return NULL;
+}
+
+/****************************************************************************************
+ * ServiceSameClientW
+ * MS_FP_SAMECLIENTSW service implementation.
+ * wParam - LPWSTR first MirVer value
+ * lParam - LPWSTR second MirVer value
+ * returns LPCWSTR: client description (do not destroy) if clients are same or NULL
+ */
+
+static INT_PTR ServiceSameClientsW(WPARAM wParam, LPARAM lParam)
+{
+ if (!wParam || !lParam)
+ return NULL; //one of its is not null
+
+ INT_PTR res1 = ServiceGetClientDescrW(wParam, 0);
+ INT_PTR res2 = ServiceGetClientDescrW(lParam, 0);
+ return (res1 == res2 && res1 != 0) ? res1 : NULL;
+}
+
+/****************************************************************************************
+* OnExtraIconListRebuild
+* Set all registered indexes in array to EMPTY_EXTRA_ICON (unregistered icon)
+*/
+
+static int OnExtraIconListRebuild(WPARAM, LPARAM)
+{
+ ClearFI();
+ return 0;
+}
+
+/****************************************************************************************
+* OnIconsChanged
+*/
+
+static int OnIconsChanged(WPARAM, LPARAM)
+{
+ ClearFI();
+ return 0;
+}
+
+/****************************************************************************************
+* OnExtraImageApply
+* Try to get MirVer value from db for contact and if success calls ApplyFingerprintImage
+*/
+
+int OnExtraImageApply(WPARAM hContact, LPARAM)
+{
+ if (hContact == NULL)
+ return 0;
+
+ ptrW tszMirver;
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto != nullptr)
+ tszMirver = db_get_wsa(hContact, szProto, "MirVer");
+
+ ApplyFingerprintImage(hContact, tszMirver);
+ return 0;
+}
+
+/****************************************************************************************
+* OnContactSettingChanged
+* if contact settings changed apply new image or remove it
+*/
+
+static int OnContactSettingChanged(WPARAM hContact, LPARAM lParam)
+{
+ if (hContact == NULL)
+ return 0;
+
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lParam;
+ if (cws && cws->szSetting && !strcmp(cws->szSetting, "MirVer")) {
+ switch (cws->value.type) {
+ case DBVT_UTF8:
+ ApplyFingerprintImage(hContact, ptrW(mir_utf8decodeW(cws->value.pszVal)));
+ break;
+ case DBVT_ASCIIZ:
+ ApplyFingerprintImage(hContact, _A2T(cws->value.pszVal));
+ break;
+ case DBVT_WCHAR:
+ ApplyFingerprintImage(hContact, cws->value.pwszVal);
+ break;
+ default:
+ ApplyFingerprintImage(hContact, nullptr);
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************************
+* OnSrmmWindowEvent
+* Monitors SRMM window's creation to draw a statusbar icon
+*/
+
+static int OnSrmmWindowEvent(WPARAM, LPARAM lParam)
+{
+ if (!g_plugin.getByte("StatusBarIcon", 1))
+ return 0;
+
+ MessageWindowEventData *event = (MessageWindowEventData *)lParam;
+ if (event == nullptr)
+ return 0;
+
+ if (event->uType == MSG_WINDOW_EVT_OPEN) {
+ ptrW ptszMirVer;
+ char *szProto = Proto_GetBaseAccountName(event->hContact);
+ if (szProto != nullptr)
+ ptszMirVer = db_get_wsa(event->hContact, szProto, "MirVer");
+ SetSrmmIcon(event->hContact, ptszMirVer);
+ arMonitoredWindows.insert((HANDLE)event->hContact);
+ }
+ else if (event->uType == MSG_WINDOW_EVT_CLOSE)
+ arMonitoredWindows.remove((HANDLE)event->hContact);
+
+ return 0;
+}
+
+/****************************************************************************************
+* OnModulesLoaded
+* Hook necessary events here
+*/
+
+int OnModulesLoaded(WPARAM, LPARAM)
+{
+ g_LPCodePage = Langpack_GetDefaultCodePage();
+
+ //Hook necessary events
+ HookEvent(ME_SKIN_ICONSCHANGED, OnIconsChanged);
+ HookEvent(ME_MSG_WINDOWEVENT, OnSrmmWindowEvent);
+
+ HookEvent(ME_MC_DEFAULTTCHANGED, OnExtraImageApply);
+
+ PathToAbsoluteW(DEFAULT_SKIN_FOLDER, g_szSkinLib);
+
+ RegisterIcons();
+
+ hExtraIcon = ExtraIcon_RegisterCallback("Client", LPGEN("Fingerprint"), "client_Miranda_unknown",
+ OnExtraIconListRebuild, OnExtraImageApply, OnExtraIconClick);
+
+ if (g_plugin.getByte("StatusBarIcon", 1)) {
+ StatusIconData sid = {};
+ sid.szModule = MODULENAME;
+ sid.flags = MBF_HIDDEN;
+ sid.dwId = 1;
+ Srmm_AddIcon(&sid, &g_plugin);
+ }
+
+ return 0;
+}
+
+void InitFingerModule()
+{
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_OPT_INITIALISE, OnOptInitialise);
+ HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
+
+ CreateServiceFunction(MS_FP_SAMECLIENTSW, ServiceSameClientsW);
+ CreateServiceFunction(MS_FP_GETCLIENTDESCRW, ServiceGetClientDescrW);
+ CreateServiceFunction(MS_FP_GETCLIENTICONW, ServiceGetClientIconW);
+}
diff --git a/plugins/FingerprintNG/src/main.cpp b/plugins/FingerprintNG/src/main.cpp
index bc0b262031..aaabd2d796 100644
--- a/plugins/FingerprintNG/src/main.cpp
+++ b/plugins/FingerprintNG/src/main.cpp
@@ -1,6 +1,6 @@
/*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 FYR, Bio, nullbie, ghazan, mataes, HierOS, faith_healer and all respective contributors.
+Copyright © 2006-23 FYR, Bio, nullbie, ghazan, mataes, HierOS, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/masks.cpp b/plugins/FingerprintNG/src/masks.cpp
index 64439f0615..c87335b7c1 100644
--- a/plugins/FingerprintNG/src/masks.cpp
+++ b/plugins/FingerprintNG/src/masks.cpp
@@ -1,6 +1,6 @@
/*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/options.cpp b/plugins/FingerprintNG/src/options.cpp
index f589db85da..60fc532249 100644
--- a/plugins/FingerprintNG/src/options.cpp
+++ b/plugins/FingerprintNG/src/options.cpp
@@ -1,7 +1,7 @@
/*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/stdafx.cxx b/plugins/FingerprintNG/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/FingerprintNG/src/stdafx.cxx
+++ b/plugins/FingerprintNG/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/plugins/FingerprintNG/src/stdafx.h b/plugins/FingerprintNG/src/stdafx.h
index 6a62fb57e8..091716993f 100644
--- a/plugins/FingerprintNG/src/stdafx.h
+++ b/plugins/FingerprintNG/src/stdafx.h
@@ -1,7 +1,7 @@
/*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
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/plugins/FingerprintNG/src/version.h b/plugins/FingerprintNG/src/version.h
index 1691434647..311340c4b9 100644
--- a/plugins/FingerprintNG/src/version.h
+++ b/plugins/FingerprintNG/src/version.h
@@ -1,6 +1,6 @@
/*
Fingerprint NG (client version) icons module for Miranda NG
-Copyright © 2006-22 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
+Copyright © 2006-23 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -29,4 +29,4 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define __DESCRIPTION "Fingerprint NG (client version) icons module for Miranda NG."
#define __AUTHOR "faith_healer, ghazan, Mataes"
#define __AUTHORWEB "https://miranda-ng.org/p/Fingerprint"
-#define __COPYRIGHT "© 2006-22 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors."
+#define __COPYRIGHT "© 2006-23 ghazan, Mataes, HierOS, FYR, Bio, nullbie, faith_healer and all respective contributors."
diff --git a/plugins/FloatingContacts/src/stdafx.cxx b/plugins/FloatingContacts/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/FloatingContacts/src/stdafx.cxx
+++ b/plugins/FloatingContacts/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/plugins/Folders/src/stdafx.cxx b/plugins/Folders/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Folders/src/stdafx.cxx
+++ b/plugins/Folders/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/plugins/Folders/src/version.h b/plugins/Folders/src/version.h
index 1dcea4ef9c..55c76bffe1 100644
--- a/plugins/Folders/src/version.h
+++ b/plugins/Folders/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Allows plugins to save their data to user selected folders; supports variables."
#define __AUTHOR "Cristian Libotean, Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Folders"
-#define __COPYRIGHT "© 2005-2012 Cristian Libotean, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2005-2012 Cristian Libotean, 2012-23 Miranda NG team"
diff --git a/plugins/HTTPServer/src/stdafx.cpp b/plugins/HTTPServer/src/stdafx.cpp
index 3665ac85d9..9bc36959d7 100644
--- a/plugins/HTTPServer/src/stdafx.cpp
+++ b/plugins/HTTPServer/src/stdafx.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/plugins/HistoryLinkListPlus/src/stdafx.cxx b/plugins/HistoryLinkListPlus/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/HistoryLinkListPlus/src/stdafx.cxx
+++ b/plugins/HistoryLinkListPlus/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/plugins/HistoryStats/src/stdafx.cxx b/plugins/HistoryStats/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/HistoryStats/src/stdafx.cxx
+++ b/plugins/HistoryStats/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/plugins/HistorySweeperLight/src/stdafx.cxx b/plugins/HistorySweeperLight/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/HistorySweeperLight/src/stdafx.cxx
+++ b/plugins/HistorySweeperLight/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/plugins/HwHotKeys/src/stdafx.cxx b/plugins/HwHotKeys/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/HwHotKeys/src/stdafx.cxx
+++ b/plugins/HwHotKeys/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/plugins/HwHotKeys/src/version.h b/plugins/HwHotKeys/src/version.h
index 86ab1761f1..55de414fcf 100644
--- a/plugins/HwHotKeys/src/version.h
+++ b/plugins/HwHotKeys/src/version.h
@@ -1,37 +1,37 @@
-/* ============================================================================
-Hardware HotKeys plugin for Miranda NG.
-Copyright © Eugene f2065, http://f2065.narod.ru, f2065 mail.ru, ICQ 35078112
-
-This program is free software; you can redistribute it and / or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA.
-============================================================================ */
-
-// история версий:
-// 1.0.0.6 - первая версия Hardware HotKeys для Miranda NG (более ранние версии были для Miranda IM), исправление старых ошибок.
-
-
-#define __MAJOR_VERSION 1
-#define __MINOR_VERSION 0
-#define __RELEASE_NUM 0
-#define __BUILD_NUM 6
-
-#include <stdver.h>
-
-#define __PLUGIN_NAME "Hardware HotKeys" // dll-fileinfo "FileVersion" и "ProductName", меню в настройках миранды, название плагина в миранде. Нелокализуемое!
-#define __FILENAME "HwHotKeys.dll" // dll-fileinfo "OriginalFilename"
-#define __DESCRIPTION "Allows you to assign expanded multimedia keys (only for PS/2 keyboards)." // описание плагина в миранде (локализуемое)
-#define __AUTHOR "Eugene f2065" // описание плагина в миранде
-#define __AUTHORWEB "http://f2065.narod.ru/" // описание плагина в dll-fileinfo "CompanyName", описание плагина в миранде
-#define __COPYRIGHT "© 2010-22 Eugene f2065" // описание плагина в dll-fileinfo "LegalCopyright", описание плагина в миранде
-
+/* ============================================================================
+Hardware HotKeys plugin for Miranda NG.
+Copyright © Eugene f2065, http://f2065.narod.ru, f2065 mail.ru, ICQ 35078112
+
+This program is free software; you can redistribute it and / or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA.
+============================================================================ */
+
+// история версий:
+// 1.0.0.6 - первая версия Hardware HotKeys для Miranda NG (более ранние версии были для Miranda IM), исправление старых ошибок.
+
+
+#define __MAJOR_VERSION 1
+#define __MINOR_VERSION 0
+#define __RELEASE_NUM 0
+#define __BUILD_NUM 6
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Hardware HotKeys" // dll-fileinfo "FileVersion" и "ProductName", меню в настройках миранды, название плагина в миранде. Нелокализуемое!
+#define __FILENAME "HwHotKeys.dll" // dll-fileinfo "OriginalFilename"
+#define __DESCRIPTION "Allows you to assign expanded multimedia keys (only for PS/2 keyboards)." // описание плагина в миранде (локализуемое)
+#define __AUTHOR "Eugene f2065" // описание плагина в миранде
+#define __AUTHORWEB "http://f2065.narod.ru/" // описание плагина в dll-fileinfo "CompanyName", описание плагина в миранде
+#define __COPYRIGHT "© 2010-23 Eugene f2065" // описание плагина в dll-fileinfo "LegalCopyright", описание плагина в миранде
+
diff --git a/plugins/IEHistory/src/version.h b/plugins/IEHistory/src/version.h
index 9175a02035..c725c65f6c 100644
--- a/plugins/IEHistory/src/version.h
+++ b/plugins/IEHistory/src/version.h
@@ -1,39 +1,39 @@
-/*
-Bonsai plugin for Miranda IM
-
-Copyright © 2006 Cristian Libotean
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef M_IEHISTORY_VERSION_H
-#define M_IEHISTORY_VERSION_H
-
-#define __MAJOR_VERSION 0
-#define __MINOR_VERSION 0
-#define __RELEASE_NUM 1
-#define __BUILD_NUM 7
-
-#include <stdver.h>
-
-#define __DESCRIPTION "Shows the history for a given contact using IEView."
-#define __PLUGIN_NAME "IE History"
-#define __FILENAME "IEHistory.dll"
-#define __AUTHOR "Cristian Libotean, Miranda NG team"
-#define __COPYRIGHT "© 2006 Cristian Libotean, 2014-22 Miranda NG team"
-#define __AUTHORWEB "https://miranda-ng.org/p/IEHistory"
-#define __PLUGIN_DISPLAY_NAME "IEView history viewer"
-
-#endif //M_IEHISTORY_VERSION_H
+/*
+Bonsai plugin for Miranda IM
+
+Copyright © 2006 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_IEHISTORY_VERSION_H
+#define M_IEHISTORY_VERSION_H
+
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 0
+#define __RELEASE_NUM 1
+#define __BUILD_NUM 7
+
+#include <stdver.h>
+
+#define __DESCRIPTION "Shows the history for a given contact using IEView."
+#define __PLUGIN_NAME "IE History"
+#define __FILENAME "IEHistory.dll"
+#define __AUTHOR "Cristian Libotean, Miranda NG team"
+#define __COPYRIGHT "© 2006 Cristian Libotean, 2014-23 Miranda NG team"
+#define __AUTHORWEB "https://miranda-ng.org/p/IEHistory"
+#define __PLUGIN_DISPLAY_NAME "IEView history viewer"
+
+#endif //M_IEHISTORY_VERSION_H
diff --git a/plugins/IEView/src/stdafx.cxx b/plugins/IEView/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/IEView/src/stdafx.cxx
+++ b/plugins/IEView/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/plugins/IgnoreState/src/stdafx.cxx b/plugins/IgnoreState/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/IgnoreState/src/stdafx.cxx
+++ b/plugins/IgnoreState/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/plugins/Import/src/dbrw/dbcontacts.cpp b/plugins/Import/src/dbrw/dbcontacts.cpp
index ce5fd91a6d..bd989afb47 100644
--- a/plugins/Import/src/dbrw/dbcontacts.cpp
+++ b/plugins/Import/src/dbrw/dbcontacts.cpp
@@ -1,68 +1,68 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "../stdafx.h"
-
-void CDbxSQLite::FillContacts()
-{
- sqlite3_stmt *st = nullptr;
- if (sql_prepare(m_sqlite, "SELECT c.id, s.val FROM dbrw_contacts as c INNER JOIN dbrw_settings as s on s.id = c.id WHERE s.module = 'Protocol' and s.setting = 'p';", &st) != SQLITE_OK)
- return;
-
- while (sql_step(st) == SQLITE_ROW) {
- MCONTACT contactID = sqlite3_column_int(st, 0);
- const char *proto = (const char*)sqlite3_column_text(st, 1);
- DBCachedContact *cc = m_cache->AddContactToCache(contactID);
- cc->szProto = mir_strdup(proto);
- }
- sql_finalize(st);
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::IsDbContact(MCONTACT contactID)
-{
- DBCachedContact *cc = m_cache->GetCachedContact(contactID);
- return (cc != nullptr);
-}
-
-STDMETHODIMP_(int) CDbxSQLite::GetContactCount(void)
-{
- int res = 0;
- if (sql_step(ctc_stmts_prep[SQL_CTC_STMT_COUNT]) == SQLITE_ROW)
- res = sqlite3_column_int(ctc_stmts_prep[SQL_CTC_STMT_COUNT], 0);
- sql_reset(ctc_stmts_prep[SQL_CTC_STMT_COUNT]);
- return res;
-}
-
-STDMETHODIMP_(int) CDbxSQLite::GetContactSize(void)
-{
- return sizeof(DBCachedContact);
-}
-
-STDMETHODIMP_(MCONTACT) CDbxSQLite::AddContact(void)
-{
- return INVALID_CONTACT_ID;
-}
-
-STDMETHODIMP_(int) CDbxSQLite::DeleteContact(MCONTACT)
-{
- return 1;
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+void CDbxSQLite::FillContacts()
+{
+ sqlite3_stmt *st = nullptr;
+ if (sql_prepare(m_sqlite, "SELECT c.id, s.val FROM dbrw_contacts as c INNER JOIN dbrw_settings as s on s.id = c.id WHERE s.module = 'Protocol' and s.setting = 'p';", &st) != SQLITE_OK)
+ return;
+
+ while (sql_step(st) == SQLITE_ROW) {
+ MCONTACT contactID = sqlite3_column_int(st, 0);
+ const char *proto = (const char*)sqlite3_column_text(st, 1);
+ DBCachedContact *cc = m_cache->AddContactToCache(contactID);
+ cc->szProto = mir_strdup(proto);
+ }
+ sql_finalize(st);
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::IsDbContact(MCONTACT contactID)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(contactID);
+ return (cc != nullptr);
+}
+
+STDMETHODIMP_(int) CDbxSQLite::GetContactCount(void)
+{
+ int res = 0;
+ if (sql_step(ctc_stmts_prep[SQL_CTC_STMT_COUNT]) == SQLITE_ROW)
+ res = sqlite3_column_int(ctc_stmts_prep[SQL_CTC_STMT_COUNT], 0);
+ sql_reset(ctc_stmts_prep[SQL_CTC_STMT_COUNT]);
+ return res;
+}
+
+STDMETHODIMP_(int) CDbxSQLite::GetContactSize(void)
+{
+ return sizeof(DBCachedContact);
+}
+
+STDMETHODIMP_(MCONTACT) CDbxSQLite::AddContact(void)
+{
+ return INVALID_CONTACT_ID;
+}
+
+STDMETHODIMP_(int) CDbxSQLite::DeleteContact(MCONTACT)
+{
+ return 1;
+}
diff --git a/plugins/Import/src/dbrw/dbevents.cpp b/plugins/Import/src/dbrw/dbevents.cpp
index 97091d0ab3..06995bd4a9 100644
--- a/plugins/Import/src/dbrw/dbevents.cpp
+++ b/plugins/Import/src/dbrw/dbevents.cpp
@@ -1,195 +1,195 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "../stdafx.h"
-
-STDMETHODIMP_(int) CDbxSQLite::GetEventCount(MCONTACT contactID)
-{
- mir_cslock lock(m_csDbAccess);
-
- int res = 0;
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 1, contactID);
- if (sql_step(evt_stmts_prep[SQL_EVT_STMT_COUNT]) == SQLITE_ROW)
- res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 0);
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_COUNT]);
- return res;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::AddEvent(MCONTACT, const DBEVENTINFO*)
-{
- return 0;
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::DeleteEvent(MEVENT)
-{
- return FALSE;
-}
-
-BOOL CDbxSQLite::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*)
-{
- return 1;
-}
-
-STDMETHODIMP_(int) CDbxSQLite::GetBlobSize(MEVENT hDbEvent)
-{
- mir_cslock lock(m_csDbAccess);
-
- int res = -1;
- sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE];
- sqlite3_bind_int(stmt, 1, hDbEvent);
- if (sql_step(stmt) == SQLITE_ROW)
- res = sqlite3_column_int(stmt, 0);
- sql_reset(stmt);
- return res;
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei)
-{
- if (dbei == nullptr)
- return 1;
-
- if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) {
- dbei->cbBlob = 0;
- return 1;
- }
-
- mir_cslock lock(m_csDbAccess);
-
- int res = 1;
- sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GET];
- sqlite3_bind_int(stmt, 1, hDbEvent);
- if (sql_step(stmt) == SQLITE_ROW) {
- const void *blob = sqlite3_column_blob(stmt, 4);
-
- dbei->timestamp = (uint32_t)sqlite3_column_int(stmt, 1);
- dbei->flags = (uint32_t)sqlite3_column_int(stmt, 2);
- dbei->eventType = (uint16_t)sqlite3_column_int(stmt, 3);
- dbei->szModule = mir_strdup((char*)sqlite3_column_text(stmt, 7));
-
- uint32_t cbBlob = sqlite3_column_int(stmt, 5);
- size_t bytesToCopy = cbBlob;
- if (dbei->cbBlob == -1)
- dbei->pBlob = (uint8_t*)mir_calloc(cbBlob + 2);
- else if (dbei->cbBlob < cbBlob)
- bytesToCopy = dbei->cbBlob;
-
- dbei->cbBlob = cbBlob;
- if (bytesToCopy && dbei->pBlob)
- memcpy(dbei->pBlob, blob, bytesToCopy);
- res = 0;
- }
- sql_reset(stmt);
- return res;
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::MarkEventRead(MCONTACT, MEVENT)
-{
- return FALSE;
-}
-
-STDMETHODIMP_(MCONTACT) CDbxSQLite::GetEventContact(MEVENT hDbEvent)
-{
- mir_cslock lock(m_csDbAccess);
-
- int res = INVALID_CONTACT_ID;
- sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETCONTACT];
- sqlite3_bind_int(stmt, 1, hDbEvent);
- if (sql_step(stmt) == SQLITE_ROW)
- res = sqlite3_column_int(stmt, 0);
- sql_reset(stmt);
- return res;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstEvent(MCONTACT contactID)
-{
- mir_cslock lock(m_csDbAccess);
-
- int res = 0;
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 1, contactID);
- if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]) == SQLITE_ROW)
- res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 0);
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]);
- return res;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstUnreadEvent(MCONTACT contactID)
-{
- mir_cslock lock(m_csDbAccess);
-
- int res = 0;
- uint32_t flags = 0;
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1, contactID);
- while (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]) == SQLITE_ROW) {
- flags = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 0);
- if (!(flags & (DBEF_READ | DBEF_SENT))) {
- res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1);
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
- return res;
- }
- }
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
- return res;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::FindLastEvent(MCONTACT contactID)
-{
- mir_cslock lock(m_csDbAccess);
-
- int res = 0;
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 1, contactID);
- if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]) == SQLITE_ROW)
- res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 0);
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]);
- return res;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::FindNextEvent(MCONTACT contactID, MEVENT hDbEvent)
-{
- if (hDbEvent == NULL)
- return 0;
-
- mir_cslock lock(m_csDbAccess);
-
- int res = 0;
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 1, contactID);
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 2, hDbEvent);
- if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]) == SQLITE_ROW)
- res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 0);
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]);
- return res;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent)
-{
- if (!hDbEvent)
- return 0;
-
- mir_cslock lock(m_csDbAccess);
-
- int res = 0;
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 1, contactID);
- sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 2, hDbEvent);
- if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]) == SQLITE_ROW)
- res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 0);
- sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]);
- return res;
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+STDMETHODIMP_(int) CDbxSQLite::GetEventCount(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 1, contactID);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_COUNT]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_COUNT]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::AddEvent(MCONTACT, const DBEVENTINFO*)
+{
+ return 0;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::DeleteEvent(MEVENT)
+{
+ return FALSE;
+}
+
+BOOL CDbxSQLite::EditEvent(MCONTACT, MEVENT, const DBEVENTINFO*)
+{
+ return 1;
+}
+
+STDMETHODIMP_(int) CDbxSQLite::GetBlobSize(MEVENT hDbEvent)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = -1;
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ if (sql_step(stmt) == SQLITE_ROW)
+ res = sqlite3_column_int(stmt, 0);
+ sql_reset(stmt);
+ return res;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei)
+{
+ if (dbei == nullptr)
+ return 1;
+
+ if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) {
+ dbei->cbBlob = 0;
+ return 1;
+ }
+
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 1;
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GET];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ if (sql_step(stmt) == SQLITE_ROW) {
+ const void *blob = sqlite3_column_blob(stmt, 4);
+
+ dbei->timestamp = (uint32_t)sqlite3_column_int(stmt, 1);
+ dbei->flags = (uint32_t)sqlite3_column_int(stmt, 2);
+ dbei->eventType = (uint16_t)sqlite3_column_int(stmt, 3);
+ dbei->szModule = mir_strdup((char*)sqlite3_column_text(stmt, 7));
+
+ uint32_t cbBlob = sqlite3_column_int(stmt, 5);
+ size_t bytesToCopy = cbBlob;
+ if (dbei->cbBlob == -1)
+ dbei->pBlob = (uint8_t*)mir_calloc(cbBlob + 2);
+ else if (dbei->cbBlob < cbBlob)
+ bytesToCopy = dbei->cbBlob;
+
+ dbei->cbBlob = cbBlob;
+ if (bytesToCopy && dbei->pBlob)
+ memcpy(dbei->pBlob, blob, bytesToCopy);
+ res = 0;
+ }
+ sql_reset(stmt);
+ return res;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::MarkEventRead(MCONTACT, MEVENT)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(MCONTACT) CDbxSQLite::GetEventContact(MEVENT hDbEvent)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = INVALID_CONTACT_ID;
+ sqlite3_stmt *stmt = evt_stmts_prep[SQL_EVT_STMT_GETCONTACT];
+ sqlite3_bind_int(stmt, 1, hDbEvent);
+ if (sql_step(stmt) == SQLITE_ROW)
+ res = sqlite3_column_int(stmt, 0);
+ sql_reset(stmt);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstEvent(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 1, contactID);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindFirstUnreadEvent(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ uint32_t flags = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1, contactID);
+ while (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]) == SQLITE_ROW) {
+ flags = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 0);
+ if (!(flags & (DBEF_READ | DBEF_SENT))) {
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
+ return res;
+ }
+ }
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindLastEvent(MCONTACT contactID)
+{
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 1, contactID);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindNextEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (hDbEvent == NULL)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 1, contactID);
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 2, hDbEvent);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]);
+ return res;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (!hDbEvent)
+ return 0;
+
+ mir_cslock lock(m_csDbAccess);
+
+ int res = 0;
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 1, contactID);
+ sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 2, hDbEvent);
+ if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]) == SQLITE_ROW)
+ res = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 0);
+ sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]);
+ return res;
+}
diff --git a/plugins/Import/src/dbrw/dbintf.cpp b/plugins/Import/src/dbrw/dbintf.cpp
index 9749ae4437..d7c1751371 100644
--- a/plugins/Import/src/dbrw/dbintf.cpp
+++ b/plugins/Import/src/dbrw/dbintf.cpp
@@ -1,87 +1,87 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "../stdafx.h"
-
-CDbxSQLite::CDbxSQLite()
- : sql_prepare_len(0)
-{
- sql_prepare_add(ctc_stmts, ctc_stmts_prep, SQL_CTC_STMT_NUM);
- sql_prepare_add(evt_stmts, evt_stmts_prep, SQL_EVT_STMT_NUM);
- sql_prepare_add(set_stmts, set_stmts_prep, SQL_SET_STMT_NUM);
-}
-
-CDbxSQLite::~CDbxSQLite()
-{
- sql_close(m_sqlite);
-
- for (int i = 0; i < sql_prepare_len; i++)
- sql_finalize(*sql_prepare_stmt[i]);
- mir_free(sql_prepare_text);
- mir_free(sql_prepare_stmt);
-}
-
-int CDbxSQLite::Open(const wchar_t *profile)
-{
- T2Utf path(profile);
- if (sql_open(path, &m_sqlite) != SQLITE_OK)
- return 1;
-
- //utils_vacuum_check();
- {
- sql_exec(m_sqlite, "BEGIN TRANSACTION;");
- sql_exec(m_sqlite, "PRAGMA locking_mode = EXCLUSIVE;");
- sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;");
- sql_exec(m_sqlite, "PRAGMA cache_size = 6000;");
- sql_exec(m_sqlite, "PRAGMA temp_store = MEMORY;");
- sql_exec(m_sqlite, "COMMIT;");
- }
-
- sql_prepare_statements();
-
- FillContacts();
-
- return 0;
-}
-
-STDMETHODIMP_(void) CDbxSQLite::SetCacheSafetyMode(BOOL safeMode)
-{
- if (safeMode)
- sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;");
- else
- sql_exec(m_sqlite, "PRAGMA synchronous = OFF;");
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::MetaMergeHistory(DBCachedContact*, DBCachedContact*)
-{
- return FALSE;
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::MetaSplitHistory(DBCachedContact*, DBCachedContact*)
-{
- return FALSE;
-}
-
-STDMETHODIMP_(MEVENT) CDbxSQLite::GetEventById(LPCSTR, LPCSTR)
-{
- return 0;
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+CDbxSQLite::CDbxSQLite()
+ : sql_prepare_len(0)
+{
+ sql_prepare_add(ctc_stmts, ctc_stmts_prep, SQL_CTC_STMT_NUM);
+ sql_prepare_add(evt_stmts, evt_stmts_prep, SQL_EVT_STMT_NUM);
+ sql_prepare_add(set_stmts, set_stmts_prep, SQL_SET_STMT_NUM);
+}
+
+CDbxSQLite::~CDbxSQLite()
+{
+ sql_close(m_sqlite);
+
+ for (int i = 0; i < sql_prepare_len; i++)
+ sql_finalize(*sql_prepare_stmt[i]);
+ mir_free(sql_prepare_text);
+ mir_free(sql_prepare_stmt);
+}
+
+int CDbxSQLite::Open(const wchar_t *profile)
+{
+ T2Utf path(profile);
+ if (sql_open(path, &m_sqlite) != SQLITE_OK)
+ return 1;
+
+ //utils_vacuum_check();
+ {
+ sql_exec(m_sqlite, "BEGIN TRANSACTION;");
+ sql_exec(m_sqlite, "PRAGMA locking_mode = EXCLUSIVE;");
+ sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;");
+ sql_exec(m_sqlite, "PRAGMA cache_size = 6000;");
+ sql_exec(m_sqlite, "PRAGMA temp_store = MEMORY;");
+ sql_exec(m_sqlite, "COMMIT;");
+ }
+
+ sql_prepare_statements();
+
+ FillContacts();
+
+ return 0;
+}
+
+STDMETHODIMP_(void) CDbxSQLite::SetCacheSafetyMode(BOOL safeMode)
+{
+ if (safeMode)
+ sql_exec(m_sqlite, "PRAGMA synchronous = NORMAL;");
+ else
+ sql_exec(m_sqlite, "PRAGMA synchronous = OFF;");
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::MetaMergeHistory(DBCachedContact*, DBCachedContact*)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::MetaSplitHistory(DBCachedContact*, DBCachedContact*)
+{
+ return FALSE;
+}
+
+STDMETHODIMP_(MEVENT) CDbxSQLite::GetEventById(LPCSTR, LPCSTR)
+{
+ return 0;
+}
diff --git a/plugins/Import/src/dbrw/dbintf.h b/plugins/Import/src/dbrw/dbintf.h
index 9ff2ee40ff..2a843b025d 100644
--- a/plugins/Import/src/dbrw/dbintf.h
+++ b/plugins/Import/src/dbrw/dbintf.h
@@ -1,159 +1,159 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#pragma once
-
-enum SQL_CTC_STMT
-{
- SQL_CTC_STMT_COUNT = 0,
- SQL_CTC_STMT_EXISTS,
- SQL_CTC_STMT_NUM
-};
-
-static char *ctc_stmts[SQL_CTC_STMT_NUM] =
-{
- "SELECT count(*) FROM dbrw_contacts;",
- "SELECT id FROM dbrw_contacts WHERE id = ? LIMIT 1;"
-};
-
-enum SQL_EVT_STMT
-{
- SQL_EVT_STMT_COUNT = 0,
- SQL_EVT_STMT_BLOBSIZE,
- SQL_EVT_STMT_GET,
- SQL_EVT_STMT_GETFLAGS,
- SQL_EVT_STMT_GETCONTACT,
- SQL_EVT_STMT_FINDFIRST,
- SQL_EVT_STMT_FINDFIRSTUNREAD,
- SQL_EVT_STMT_FINDLAST,
- SQL_EVT_STMT_FINDNEXT,
- SQL_EVT_STMT_FINDPREV,
- SQL_EVT_STMT_NUM
-};
-
-static char *evt_stmts[SQL_EVT_STMT_NUM] =
-{
- "SELECT count(*) FROM dbrw_events where contactid = ?;",
- "SELECT blobsize FROM dbrw_events where id = ? LIMIT 1;",
- "SELECT * FROM dbrw_events where id = ? LIMIT 1;",
- "SELECT flags FROM dbrw_events where id = ? LIMIT 1;",
- "SELECT contactid FROM dbrw_events where id = ? LIMIT 1;",
- "SELECT id FROM dbrw_events where contactid = ? ORDER by id LIMIT 1;",
- "SELECT flags, id FROM dbrw_events where contactid = ? ORDER by id;",
- "SELECT id FROM dbrw_events where contactid = ? ORDER by id DESC;",
- "SELECT id FROM dbrw_events where contactid = ? AND id > ? ORDER by id LIMIT 1;",
- "SELECT id FROM dbrw_events where contactid = ? AND id < ? ORDER by id DESC LIMIT 1;"
-};
-
-enum SQL_SET_STMT
-{
- SQL_SET_STMT_READ = 0,
- SQL_SET_STMT_ENUM,
- SQL_SET_STMT_ENUMMODULES,
- SQL_SET_STMT_SETTINGCHECK,
- SQL_SET_STMT_NUM
-};
-
-static char *set_stmts[SQL_SET_STMT_NUM] =
-{
- "SELECT type, val FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ? LIMIT 1;",
- "SELECT setting from dbrw_settings where id = ? AND module = ? ORDER by setting;",
- "SELECT DISTINCT module from dbrw_settings;",
- "SELECT count(*) FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ?;",
-};
-
-struct TSqlMessage {
- int op;
- sqlite3 *pDb;
- sqlite3_stmt *pStmt;
- int retCode;
- const char *zIn;
- HANDLE hDoneEvent;
-};
-
-struct CDbxSQLite : public MDatabaseReadonly, public MZeroedObject
-{
-private:
- sqlite3 *m_sqlite;
-
- int sql_prepare_len;
- char **sql_prepare_text;
- sqlite3_stmt ***sql_prepare_stmt;
- sqlite3_stmt *ctc_stmts_prep[SQL_CTC_STMT_NUM] = { 0 };
- sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = { 0 };
- sqlite3_stmt *set_stmts_prep[SQL_SET_STMT_NUM] = { 0 };
-
- void sql_prepare_add(char **text, sqlite3_stmt **stmts, int len);
- void sql_prepare_statements();
- static void CALLBACK sql_server_sync_apc(UINT_PTR dwParam);
- void sql_server_sync(TSqlMessage *msg);
- int sql_step(sqlite3_stmt *stmt);
- int sql_reset(sqlite3_stmt *stmt);
- int sql_exec(sqlite3 *sql, const char *query);
- int sql_open(const char *path, sqlite3 **sql);
- int sql_close(sqlite3 *sql);
- int sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt);
- int sql_finalize(sqlite3_stmt *stmt);
-
- void FillContacts();
-
-public:
- CDbxSQLite();
- ~CDbxSQLite();
-
- int Open(const wchar_t *profile);
-
- STDMETHODIMP_(BOOL) IsRelational(void) override { return FALSE; }
- STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override;
-
- STDMETHODIMP_(int) GetContactCount(void) override;
- STDMETHODIMP_(int) DeleteContact(MCONTACT contactID) override;
- STDMETHODIMP_(MCONTACT) AddContact(void) override;
- STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override;
- STDMETHODIMP_(int) GetContactSize(void) override;
-
- STDMETHODIMP_(int) GetEventCount(MCONTACT contactID) override;
- STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, const DBEVENTINFO *dbe) override;
- STDMETHODIMP_(BOOL) DeleteEvent(MEVENT hDbEvent) override;
- STDMETHODIMP_(BOOL) EditEvent(MCONTACT contactID, MEVENT hDbEvent, const DBEVENTINFO *dbe) override;
- STDMETHODIMP_(int) GetBlobSize(MEVENT hDbEvent) override;
- STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override;
- STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override;
- STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override;
- STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override;
- STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override;
- STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override;
- STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override;
- STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override;
-
- STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override;
-
- STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) override;
- STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override;
-
- STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
- STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
-
- STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override;
-
- STDMETHODIMP_(DATABASELINK *) GetDriver();
-};
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#pragma once
+
+enum SQL_CTC_STMT
+{
+ SQL_CTC_STMT_COUNT = 0,
+ SQL_CTC_STMT_EXISTS,
+ SQL_CTC_STMT_NUM
+};
+
+static char *ctc_stmts[SQL_CTC_STMT_NUM] =
+{
+ "SELECT count(*) FROM dbrw_contacts;",
+ "SELECT id FROM dbrw_contacts WHERE id = ? LIMIT 1;"
+};
+
+enum SQL_EVT_STMT
+{
+ SQL_EVT_STMT_COUNT = 0,
+ SQL_EVT_STMT_BLOBSIZE,
+ SQL_EVT_STMT_GET,
+ SQL_EVT_STMT_GETFLAGS,
+ SQL_EVT_STMT_GETCONTACT,
+ SQL_EVT_STMT_FINDFIRST,
+ SQL_EVT_STMT_FINDFIRSTUNREAD,
+ SQL_EVT_STMT_FINDLAST,
+ SQL_EVT_STMT_FINDNEXT,
+ SQL_EVT_STMT_FINDPREV,
+ SQL_EVT_STMT_NUM
+};
+
+static char *evt_stmts[SQL_EVT_STMT_NUM] =
+{
+ "SELECT count(*) FROM dbrw_events where contactid = ?;",
+ "SELECT blobsize FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT * FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT flags FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT contactid FROM dbrw_events where id = ? LIMIT 1;",
+ "SELECT id FROM dbrw_events where contactid = ? ORDER by id LIMIT 1;",
+ "SELECT flags, id FROM dbrw_events where contactid = ? ORDER by id;",
+ "SELECT id FROM dbrw_events where contactid = ? ORDER by id DESC;",
+ "SELECT id FROM dbrw_events where contactid = ? AND id > ? ORDER by id LIMIT 1;",
+ "SELECT id FROM dbrw_events where contactid = ? AND id < ? ORDER by id DESC LIMIT 1;"
+};
+
+enum SQL_SET_STMT
+{
+ SQL_SET_STMT_READ = 0,
+ SQL_SET_STMT_ENUM,
+ SQL_SET_STMT_ENUMMODULES,
+ SQL_SET_STMT_SETTINGCHECK,
+ SQL_SET_STMT_NUM
+};
+
+static char *set_stmts[SQL_SET_STMT_NUM] =
+{
+ "SELECT type, val FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ? LIMIT 1;",
+ "SELECT setting from dbrw_settings where id = ? AND module = ? ORDER by setting;",
+ "SELECT DISTINCT module from dbrw_settings;",
+ "SELECT count(*) FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ?;",
+};
+
+struct TSqlMessage {
+ int op;
+ sqlite3 *pDb;
+ sqlite3_stmt *pStmt;
+ int retCode;
+ const char *zIn;
+ HANDLE hDoneEvent;
+};
+
+struct CDbxSQLite : public MDatabaseReadonly, public MZeroedObject
+{
+private:
+ sqlite3 *m_sqlite;
+
+ int sql_prepare_len;
+ char **sql_prepare_text;
+ sqlite3_stmt ***sql_prepare_stmt;
+ sqlite3_stmt *ctc_stmts_prep[SQL_CTC_STMT_NUM] = { 0 };
+ sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = { 0 };
+ sqlite3_stmt *set_stmts_prep[SQL_SET_STMT_NUM] = { 0 };
+
+ void sql_prepare_add(char **text, sqlite3_stmt **stmts, int len);
+ void sql_prepare_statements();
+ static void CALLBACK sql_server_sync_apc(UINT_PTR dwParam);
+ void sql_server_sync(TSqlMessage *msg);
+ int sql_step(sqlite3_stmt *stmt);
+ int sql_reset(sqlite3_stmt *stmt);
+ int sql_exec(sqlite3 *sql, const char *query);
+ int sql_open(const char *path, sqlite3 **sql);
+ int sql_close(sqlite3 *sql);
+ int sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt);
+ int sql_finalize(sqlite3_stmt *stmt);
+
+ void FillContacts();
+
+public:
+ CDbxSQLite();
+ ~CDbxSQLite();
+
+ int Open(const wchar_t *profile);
+
+ STDMETHODIMP_(BOOL) IsRelational(void) override { return FALSE; }
+ STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override;
+
+ STDMETHODIMP_(int) GetContactCount(void) override;
+ STDMETHODIMP_(int) DeleteContact(MCONTACT contactID) override;
+ STDMETHODIMP_(MCONTACT) AddContact(void) override;
+ STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override;
+ STDMETHODIMP_(int) GetContactSize(void) override;
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, const DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(BOOL) DeleteEvent(MEVENT hDbEvent) override;
+ STDMETHODIMP_(BOOL) EditEvent(MCONTACT contactID, MEVENT hDbEvent, const DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(int) GetBlobSize(MEVENT hDbEvent) override;
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override;
+ STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override;
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override;
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override;
+
+ STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override;
+
+ STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic) override;
+ STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override;
+
+ STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
+ STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override;
+
+ STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override;
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
diff --git a/plugins/Import/src/dbrw/dbrw.cpp b/plugins/Import/src/dbrw/dbrw.cpp
index fded599583..7be7b2634d 100644
--- a/plugins/Import/src/dbrw/dbrw.cpp
+++ b/plugins/Import/src/dbrw/dbrw.cpp
@@ -1,105 +1,105 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "../stdafx.h"
-
-static int dbrw_makeDatabase(const wchar_t*)
-{
- return 1;
-}
-
-static int dbrw_grokHeader(const wchar_t *profile)
-{
- HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);
- int rc = 1;
- int err = EGROKPRF_CANTREAD;
-
- if (hFile != INVALID_HANDLE_VALUE) {
- BOOL r;
- char buf[64];
- DWORD dwRead;
-
- ZeroMemory(buf, sizeof(buf));
- r = ReadFile(hFile, buf, sizeof(buf), &dwRead, NULL);
- CloseHandle(hFile);
- if (r && memcmp(buf, DBRW_HEADER_STR, strlen(DBRW_HEADER_STR)) == 0) {
- sqlite3 *sqlcheck = NULL;
- char *szPath = mir_utf8encodeW(profile);
-
- rc = sqlite3_open(szPath, &sqlcheck);
- mir_free(szPath);
- if (rc == SQLITE_OK) {
- sqlite3_stmt *stmt;
- err = EGROKPRF_UNKHEADER;
-
- sqlite3_prepare_v2(sqlcheck, "select * from sqlite_master where type = 'table' and name = 'dbrw_core';", -1, &stmt, NULL);
- if (sqlite3_step(stmt) == SQLITE_ROW) {
-
- sqlite3_finalize(stmt);
- sqlite3_prepare_v2(sqlcheck, "select val from dbrw_core where setting = 'SchemaVersion';", -1, &stmt, NULL);
- if (sqlite3_step(stmt) == SQLITE_ROW) {
- int sVersion;
-
- sVersion = sqlite3_column_int(stmt, 0);
- if (sVersion == atoi(DBRW_SCHEMA_VERSION))
- rc = 0;
- else {
- // TODO: Return as valid and upgrade in
- // dbrw_Load() if schema version is upgradable
- }
- }
- }
- sqlite3_finalize(stmt);
- sqlite3_close(sqlcheck);
- }
- }
- else err = r ? EGROKPRF_UNKHEADER : EGROKPRF_CANTREAD;
- }
- return rc;
-}
-
-static MDatabaseCommon* dbrw_Load(const wchar_t *profile, BOOL)
-{
- CDbxSQLite *db = new CDbxSQLite();
- db->Open(profile);
- return db;
-}
-
-static DATABASELINK dblink =
-{
- 0,
- "dbrw",
- L"dbx SQLite driver",
- dbrw_makeDatabase,
- dbrw_grokHeader,
- dbrw_Load
-};
-
-STDMETHODIMP_(DATABASELINK *) CDbxSQLite::GetDriver()
-{
- return &g_patternDbLink;
-}
-
-void RegisterDbrw()
-{
- RegisterDatabasePlugin(&dblink);
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "../stdafx.h"
+
+static int dbrw_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int dbrw_grokHeader(const wchar_t *profile)
+{
+ HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);
+ int rc = 1;
+ int err = EGROKPRF_CANTREAD;
+
+ if (hFile != INVALID_HANDLE_VALUE) {
+ BOOL r;
+ char buf[64];
+ DWORD dwRead;
+
+ ZeroMemory(buf, sizeof(buf));
+ r = ReadFile(hFile, buf, sizeof(buf), &dwRead, NULL);
+ CloseHandle(hFile);
+ if (r && memcmp(buf, DBRW_HEADER_STR, strlen(DBRW_HEADER_STR)) == 0) {
+ sqlite3 *sqlcheck = NULL;
+ char *szPath = mir_utf8encodeW(profile);
+
+ rc = sqlite3_open(szPath, &sqlcheck);
+ mir_free(szPath);
+ if (rc == SQLITE_OK) {
+ sqlite3_stmt *stmt;
+ err = EGROKPRF_UNKHEADER;
+
+ sqlite3_prepare_v2(sqlcheck, "select * from sqlite_master where type = 'table' and name = 'dbrw_core';", -1, &stmt, NULL);
+ if (sqlite3_step(stmt) == SQLITE_ROW) {
+
+ sqlite3_finalize(stmt);
+ sqlite3_prepare_v2(sqlcheck, "select val from dbrw_core where setting = 'SchemaVersion';", -1, &stmt, NULL);
+ if (sqlite3_step(stmt) == SQLITE_ROW) {
+ int sVersion;
+
+ sVersion = sqlite3_column_int(stmt, 0);
+ if (sVersion == atoi(DBRW_SCHEMA_VERSION))
+ rc = 0;
+ else {
+ // TODO: Return as valid and upgrade in
+ // dbrw_Load() if schema version is upgradable
+ }
+ }
+ }
+ sqlite3_finalize(stmt);
+ sqlite3_close(sqlcheck);
+ }
+ }
+ else err = r ? EGROKPRF_UNKHEADER : EGROKPRF_CANTREAD;
+ }
+ return rc;
+}
+
+static MDatabaseCommon* dbrw_Load(const wchar_t *profile, BOOL)
+{
+ CDbxSQLite *db = new CDbxSQLite();
+ db->Open(profile);
+ return db;
+}
+
+static DATABASELINK dblink =
+{
+ 0,
+ "dbrw",
+ L"dbx SQLite driver",
+ dbrw_makeDatabase,
+ dbrw_grokHeader,
+ dbrw_Load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxSQLite::GetDriver()
+{
+ return &g_patternDbLink;
+}
+
+void RegisterDbrw()
+{
+ RegisterDatabasePlugin(&dblink);
+}
diff --git a/plugins/Import/src/dbrw/dbrw.h b/plugins/Import/src/dbrw/dbrw.h
index daf32ba745..11b153221a 100644
--- a/plugins/Import/src/dbrw/dbrw.h
+++ b/plugins/Import/src/dbrw/dbrw.h
@@ -1,30 +1,30 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#pragma once
-
-#include <sqlite3.h>
-
-#define DBRW_SCHEMA_VERSION "2"
-#define DBRW_HEADER_STR "SQLite format 3"
-
-void RegisterDbrw();
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#pragma once
+
+#include <sqlite3.h>
+
+#define DBRW_SCHEMA_VERSION "2"
+#define DBRW_HEADER_STR "SQLite format 3"
+
+void RegisterDbrw();
diff --git a/plugins/Import/src/dbrw/dbsettings.cpp b/plugins/Import/src/dbrw/dbsettings.cpp
index 360a56ea94..9efe213bcf 100644
--- a/plugins/Import/src/dbrw/dbsettings.cpp
+++ b/plugins/Import/src/dbrw/dbsettings.cpp
@@ -1,171 +1,171 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "..\stdafx.h"
-
-STDMETHODIMP_(BOOL) CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam)
-{
- mir_cslockfull lock(m_csDbAccess);
-
- int res = 0;
- while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]) == SQLITE_ROW && !res) {
- const char *szModule = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUMMODULES], 0);
- lock.unlock();
- res = (pFunc)(szModule, pParam);
- lock.lock();
- }
- sql_reset(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]);
- return res;
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic)
-{
- if (!szSetting || !szModule)
- return 1;
-
- size_t settingNameLen = strlen(szSetting);
- size_t moduleNameLen = strlen(szModule);
-
- mir_cslock lock(m_csDbAccess);
-
- char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
- DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 0);
- if (pCachedValue != nullptr) {
- if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
- int cbOrigLen = dbv->cchVal;
- char *cbOrigPtr = dbv->pszVal;
- memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
- if (isStatic) {
- int cbLen = 0;
- if (pCachedValue->pszVal != nullptr)
- cbLen = (int)strlen(pCachedValue->pszVal);
-
- cbOrigLen--;
- dbv->pszVal = cbOrigPtr;
- if (cbLen < cbOrigLen)
- cbOrigLen = cbLen;
- memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
- dbv->pszVal[cbOrigLen] = 0;
- dbv->cchVal = cbLen;
- }
- else {
- dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
- strcpy(dbv->pszVal, pCachedValue->pszVal);
- }
- }
- else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
-
- return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
- }
-
- // never look db for the resident variable
- if (szCachedSettingName[-1] != 0)
- return 1;
-
- sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 1, szSetting, -1, SQLITE_STATIC);
- sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 2, szModule, -1, SQLITE_STATIC);
- sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_READ], 3, contactID);
- if (sql_step(set_stmts_prep[SQL_SET_STMT_READ]) != SQLITE_ROW) {
- /*if (dbv->type != DBVT_BLOB) {
- DBVARIANT* pCachedValue = settings_getCachedValue(contactID, szCachedSettingName, 1);
-
- if (pCachedValue != NULL)
- pCachedValue->type = DBVT_DELETED;
- }*/
- sql_reset(set_stmts_prep[SQL_SET_STMT_READ]);
- return 1;
- }
- dbv->type = (int)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 0);
- switch (dbv->type) {
- case DBVT_BYTE:
- dbv->bVal = (uint8_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
- break;
- case DBVT_WORD:
- dbv->wVal = (uint16_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
- break;
- case DBVT_DWORD:
- dbv->dVal = (uint32_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
- break;
- case DBVT_UTF8:
- case DBVT_ASCIIZ:
- {
- const char *p = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_READ], 1);
-
- if (p != NULL) {
- size_t len = strlen(p) + 1;
- size_t copylen = isStatic ? (len < dbv->cchVal ? len : dbv->cchVal) : len;
- if (!isStatic)
- dbv->pszVal = (char*)mir_alloc(len);
- memmove(dbv->pszVal, p, copylen);
- }
- else
- dbv->pszVal = 0;
- break;
- }
- case DBVT_BLOB:
- {
- size_t len = sqlite3_column_bytes(set_stmts_prep[SQL_SET_STMT_READ], 1);
-
- if (len) {
- size_t copylen = isStatic ? (len < dbv->cpbVal ? len : dbv->cpbVal) : len;
- if (!isStatic)
- dbv->pbVal = (uint8_t*)mir_alloc(copylen);
- memcpy(dbv->pbVal, sqlite3_column_blob(set_stmts_prep[SQL_SET_STMT_READ], 1), copylen);
- dbv->cpbVal = (uint16_t)copylen;
- }
- else {
- dbv = 0;
- }
- }
- }
- sql_reset(set_stmts_prep[SQL_SET_STMT_READ]);
- // add to cache
- if (dbv->type != DBVT_BLOB/* && dbv->type != DBVT_ENCRYPTED*/) {
- pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 1);
- if (pCachedValue != nullptr)
- m_cache->SetCachedVariant(dbv, pCachedValue);
- }
-
- return 0;
-}
-
-STDMETHODIMP_(BOOL) CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param)
-{
- if (szModule == nullptr)
- return -1;
-
- mir_cslockfull lock(m_csDbAccess);
-
- int res = -1;
- sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_ENUM], 1, hContact);
- sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_ENUM], 2, szModule, -1, SQLITE_STATIC);
- while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUM]) == SQLITE_ROW) {
- const char *sczSetting = (const char*)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUM], 0);
- if (sczSetting) {
- lock.unlock();
- res = (pfnEnumProc)(sczSetting, param);
- lock.lock();
- }
- }
- sql_reset(set_stmts_prep[SQL_SET_STMT_ENUM]);
- return res;
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "..\stdafx.h"
+
+STDMETHODIMP_(BOOL) CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam)
+{
+ mir_cslockfull lock(m_csDbAccess);
+
+ int res = 0;
+ while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]) == SQLITE_ROW && !res) {
+ const char *szModule = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUMMODULES], 0);
+ lock.unlock();
+ res = (pFunc)(szModule, pParam);
+ lock.lock();
+ }
+ sql_reset(set_stmts_prep[SQL_SET_STMT_ENUMMODULES]);
+ return res;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::GetContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic)
+{
+ if (!szSetting || !szModule)
+ return 1;
+
+ size_t settingNameLen = strlen(szSetting);
+ size_t moduleNameLen = strlen(szModule);
+
+ mir_cslock lock(m_csDbAccess);
+
+ char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
+ strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ // never look db for the resident variable
+ if (szCachedSettingName[-1] != 0)
+ return 1;
+
+ sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 1, szSetting, -1, SQLITE_STATIC);
+ sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_READ], 2, szModule, -1, SQLITE_STATIC);
+ sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_READ], 3, contactID);
+ if (sql_step(set_stmts_prep[SQL_SET_STMT_READ]) != SQLITE_ROW) {
+ /*if (dbv->type != DBVT_BLOB) {
+ DBVARIANT* pCachedValue = settings_getCachedValue(contactID, szCachedSettingName, 1);
+
+ if (pCachedValue != NULL)
+ pCachedValue->type = DBVT_DELETED;
+ }*/
+ sql_reset(set_stmts_prep[SQL_SET_STMT_READ]);
+ return 1;
+ }
+ dbv->type = (int)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 0);
+ switch (dbv->type) {
+ case DBVT_BYTE:
+ dbv->bVal = (uint8_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
+ break;
+ case DBVT_WORD:
+ dbv->wVal = (uint16_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
+ break;
+ case DBVT_DWORD:
+ dbv->dVal = (uint32_t)sqlite3_column_int(set_stmts_prep[SQL_SET_STMT_READ], 1);
+ break;
+ case DBVT_UTF8:
+ case DBVT_ASCIIZ:
+ {
+ const char *p = (const char *)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_READ], 1);
+
+ if (p != NULL) {
+ size_t len = strlen(p) + 1;
+ size_t copylen = isStatic ? (len < dbv->cchVal ? len : dbv->cchVal) : len;
+ if (!isStatic)
+ dbv->pszVal = (char*)mir_alloc(len);
+ memmove(dbv->pszVal, p, copylen);
+ }
+ else
+ dbv->pszVal = 0;
+ break;
+ }
+ case DBVT_BLOB:
+ {
+ size_t len = sqlite3_column_bytes(set_stmts_prep[SQL_SET_STMT_READ], 1);
+
+ if (len) {
+ size_t copylen = isStatic ? (len < dbv->cpbVal ? len : dbv->cpbVal) : len;
+ if (!isStatic)
+ dbv->pbVal = (uint8_t*)mir_alloc(copylen);
+ memcpy(dbv->pbVal, sqlite3_column_blob(set_stmts_prep[SQL_SET_STMT_READ], 1), copylen);
+ dbv->cpbVal = (uint16_t)copylen;
+ }
+ else {
+ dbv = 0;
+ }
+ }
+ }
+ sql_reset(set_stmts_prep[SQL_SET_STMT_READ]);
+ // add to cache
+ if (dbv->type != DBVT_BLOB/* && dbv->type != DBVT_ENCRYPTED*/) {
+ pCachedValue = m_cache->GetCachedValuePtr(contactID, szCachedSettingName, 1);
+ if (pCachedValue != nullptr)
+ m_cache->SetCachedVariant(dbv, pCachedValue);
+ }
+
+ return 0;
+}
+
+STDMETHODIMP_(BOOL) CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param)
+{
+ if (szModule == nullptr)
+ return -1;
+
+ mir_cslockfull lock(m_csDbAccess);
+
+ int res = -1;
+ sqlite3_bind_int(set_stmts_prep[SQL_SET_STMT_ENUM], 1, hContact);
+ sqlite3_bind_text(set_stmts_prep[SQL_SET_STMT_ENUM], 2, szModule, -1, SQLITE_STATIC);
+ while (sql_step(set_stmts_prep[SQL_SET_STMT_ENUM]) == SQLITE_ROW) {
+ const char *sczSetting = (const char*)sqlite3_column_text(set_stmts_prep[SQL_SET_STMT_ENUM], 0);
+ if (sczSetting) {
+ lock.unlock();
+ res = (pfnEnumProc)(sczSetting, param);
+ lock.lock();
+ }
+ }
+ sql_reset(set_stmts_prep[SQL_SET_STMT_ENUM]);
+ return res;
+}
diff --git a/plugins/Import/src/dbrw/dbsql.cpp b/plugins/Import/src/dbrw/dbsql.cpp
index 516f378435..3eaf3ef0d6 100644
--- a/plugins/Import/src/dbrw/dbsql.cpp
+++ b/plugins/Import/src/dbrw/dbsql.cpp
@@ -1,162 +1,162 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "..\stdafx.h"
-
-enum SQL
-{
- SQL_STEP,
- SQL_RESET,
- SQL_EXEC,
- SQL_OPEN,
- SQL_CLOSE,
- SQL_PREPARE,
- SQL_FINALIZE
-};
-
-void CDbxSQLite::sql_prepare_add(char **text, sqlite3_stmt **stmts, int len)
-{
- sql_prepare_text = (char**)mir_realloc(sql_prepare_text, (sql_prepare_len + len) * sizeof(char*));
- sql_prepare_stmt = (sqlite3_stmt***)mir_realloc(sql_prepare_stmt, (sql_prepare_len + len) * sizeof(sqlite3_stmt**));
- for (int i = 0; i < len; i++) {
- sql_prepare_text[sql_prepare_len + i] = text[i];
- sql_prepare_stmt[sql_prepare_len + i] = &stmts[i];
- }
- sql_prepare_len += len;
-}
-
-void CDbxSQLite::sql_prepare_statements()
-{
- for (int i = 0; i < sql_prepare_len; i++)
- sql_prepare(m_sqlite, sql_prepare_text[i], sql_prepare_stmt[i]);
-}
-
-void CALLBACK CDbxSQLite::sql_server_sync_apc(UINT_PTR dwParam)
-{
- TSqlMessage *msg = (TSqlMessage*)dwParam;
-
- if (!msg)
- return;
-
- switch (msg->op) {
- case SQL_STEP:
- msg->retCode = sqlite3_step(msg->pStmt);
- break;
- case SQL_RESET:
- msg->retCode = sqlite3_reset(msg->pStmt);
- break;
- case SQL_EXEC:
- msg->retCode = sqlite3_exec(msg->pDb, msg->zIn, NULL, NULL, NULL);
- break;
- case SQL_OPEN:
- msg->retCode = sqlite3_open(msg->zIn, &msg->pDb);
- break;
- case SQL_CLOSE:
- msg->retCode = sqlite3_close(msg->pDb);
- break;
- case SQL_PREPARE:
- msg->retCode = sqlite3_prepare_v2(msg->pDb, msg->zIn, -1, &msg->pStmt, NULL);
- break;
- case SQL_FINALIZE:
- msg->retCode = sqlite3_finalize(msg->pStmt);
- break;
- default:
- return;
- }
- SetEvent(msg->hDoneEvent);
-}
-
-void CDbxSQLite::sql_server_sync(TSqlMessage *msg)
-{
- msg->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- sql_server_sync_apc((UINT_PTR)msg);
- PostMessage(Miranda_GetSystemWindow()->GetHwnd(), WM_NULL, 0, 0);
- WaitForSingleObject(msg->hDoneEvent, INFINITE);
- CloseHandle(msg->hDoneEvent);
-}
-
-int CDbxSQLite::sql_step(sqlite3_stmt *stmt)
-{
- TSqlMessage msg;
- msg.op = SQL_STEP;
- msg.pStmt = stmt;
- sql_server_sync(&msg);
- return msg.retCode;
-}
-
-int CDbxSQLite::sql_reset(sqlite3_stmt *stmt)
-{
- TSqlMessage msg;
- msg.op = SQL_RESET;
- msg.pStmt = stmt;
- sql_server_sync(&msg);
- return msg.retCode;
-}
-
-int CDbxSQLite::sql_exec(sqlite3 *sql, const char *query)
-{
- TSqlMessage msg;
- msg.op = SQL_EXEC;
- msg.pDb = sql;
- msg.zIn = query;
- sql_server_sync(&msg);
- return msg.retCode;
-}
-
-int CDbxSQLite::sql_open(const char *path, sqlite3 **sql)
-{
- TSqlMessage msg;
- msg.op = SQL_OPEN;
- msg.zIn = path;
- sql_server_sync(&msg);
- *sql = msg.pDb;
- return msg.retCode;
-}
-
-int CDbxSQLite::sql_close(sqlite3 *sql)
-{
- TSqlMessage msg;
- msg.op = SQL_CLOSE;
- msg.pDb = sql;
- sql_server_sync(&msg);
- return msg.retCode;
-}
-
-int CDbxSQLite::sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt)
-{
- TSqlMessage msg;
- msg.op = SQL_PREPARE;
- msg.pDb = sql;
- msg.zIn = query;
- sql_server_sync(&msg);
- *stmt = msg.pStmt;
- return msg.retCode;
-}
-
-int CDbxSQLite::sql_finalize(sqlite3_stmt *stmt)
-{
- TSqlMessage msg;
- msg.op = SQL_FINALIZE;
- msg.pStmt = stmt;
- sql_server_sync(&msg);
- return msg.retCode;
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "..\stdafx.h"
+
+enum SQL
+{
+ SQL_STEP,
+ SQL_RESET,
+ SQL_EXEC,
+ SQL_OPEN,
+ SQL_CLOSE,
+ SQL_PREPARE,
+ SQL_FINALIZE
+};
+
+void CDbxSQLite::sql_prepare_add(char **text, sqlite3_stmt **stmts, int len)
+{
+ sql_prepare_text = (char**)mir_realloc(sql_prepare_text, (sql_prepare_len + len) * sizeof(char*));
+ sql_prepare_stmt = (sqlite3_stmt***)mir_realloc(sql_prepare_stmt, (sql_prepare_len + len) * sizeof(sqlite3_stmt**));
+ for (int i = 0; i < len; i++) {
+ sql_prepare_text[sql_prepare_len + i] = text[i];
+ sql_prepare_stmt[sql_prepare_len + i] = &stmts[i];
+ }
+ sql_prepare_len += len;
+}
+
+void CDbxSQLite::sql_prepare_statements()
+{
+ for (int i = 0; i < sql_prepare_len; i++)
+ sql_prepare(m_sqlite, sql_prepare_text[i], sql_prepare_stmt[i]);
+}
+
+void CALLBACK CDbxSQLite::sql_server_sync_apc(UINT_PTR dwParam)
+{
+ TSqlMessage *msg = (TSqlMessage*)dwParam;
+
+ if (!msg)
+ return;
+
+ switch (msg->op) {
+ case SQL_STEP:
+ msg->retCode = sqlite3_step(msg->pStmt);
+ break;
+ case SQL_RESET:
+ msg->retCode = sqlite3_reset(msg->pStmt);
+ break;
+ case SQL_EXEC:
+ msg->retCode = sqlite3_exec(msg->pDb, msg->zIn, NULL, NULL, NULL);
+ break;
+ case SQL_OPEN:
+ msg->retCode = sqlite3_open(msg->zIn, &msg->pDb);
+ break;
+ case SQL_CLOSE:
+ msg->retCode = sqlite3_close(msg->pDb);
+ break;
+ case SQL_PREPARE:
+ msg->retCode = sqlite3_prepare_v2(msg->pDb, msg->zIn, -1, &msg->pStmt, NULL);
+ break;
+ case SQL_FINALIZE:
+ msg->retCode = sqlite3_finalize(msg->pStmt);
+ break;
+ default:
+ return;
+ }
+ SetEvent(msg->hDoneEvent);
+}
+
+void CDbxSQLite::sql_server_sync(TSqlMessage *msg)
+{
+ msg->hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ sql_server_sync_apc((UINT_PTR)msg);
+ PostMessage(Miranda_GetSystemWindow()->GetHwnd(), WM_NULL, 0, 0);
+ WaitForSingleObject(msg->hDoneEvent, INFINITE);
+ CloseHandle(msg->hDoneEvent);
+}
+
+int CDbxSQLite::sql_step(sqlite3_stmt *stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_STEP;
+ msg.pStmt = stmt;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_reset(sqlite3_stmt *stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_RESET;
+ msg.pStmt = stmt;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_exec(sqlite3 *sql, const char *query)
+{
+ TSqlMessage msg;
+ msg.op = SQL_EXEC;
+ msg.pDb = sql;
+ msg.zIn = query;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_open(const char *path, sqlite3 **sql)
+{
+ TSqlMessage msg;
+ msg.op = SQL_OPEN;
+ msg.zIn = path;
+ sql_server_sync(&msg);
+ *sql = msg.pDb;
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_close(sqlite3 *sql)
+{
+ TSqlMessage msg;
+ msg.op = SQL_CLOSE;
+ msg.pDb = sql;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_prepare(sqlite3 *sql, const char *query, sqlite3_stmt **stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_PREPARE;
+ msg.pDb = sql;
+ msg.zIn = query;
+ sql_server_sync(&msg);
+ *stmt = msg.pStmt;
+ return msg.retCode;
+}
+
+int CDbxSQLite::sql_finalize(sqlite3_stmt *stmt)
+{
+ TSqlMessage msg;
+ msg.op = SQL_FINALIZE;
+ msg.pStmt = stmt;
+ sql_server_sync(&msg);
+ return msg.retCode;
+}
diff --git a/plugins/Import/src/import.cpp b/plugins/Import/src/import.cpp
index 865d786b93..17aeaa073a 100644
--- a/plugins/Import/src/import.cpp
+++ b/plugins/Import/src/import.cpp
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/Import/src/main.cpp b/plugins/Import/src/main.cpp
index c800b5d611..f1c62ba97b 100644
--- a/plugins/Import/src/main.cpp
+++ b/plugins/Import/src/main.cpp
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/Import/src/mcontacts.cpp b/plugins/Import/src/mcontacts.cpp
index cfa826769f..5eb09ed6aa 100644
--- a/plugins/Import/src/mcontacts.cpp
+++ b/plugins/Import/src/mcontacts.cpp
@@ -1,322 +1,322 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "stdafx.h"
-
-#include <memory>
-#include <vector>
-
-#define HEADER_STR "HB"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// CDbxMcontacts database driver, read-only
-
-#pragma pack(push, 1)
-
-struct MC_FileHeader
-{
- uint8_t signature[2];
- uint32_t version;
- uint32_t dataSize;
-};
-
-struct MC_MsgHeader32
-{
- uint32_t cbSize; // size of the structure in bytes
- uint32_t szModule; // pointer to name of the module that 'owns' this
- // event, ie the one that is in control of the data format
- uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until
- // 2106 unless you use the standard C library which is
- // signed and can only do until 2038. In GMT.
- uint32_t flags; // the omnipresent flags
- uint32_t eventType; // module-defined event type field
- uint32_t cbBlob; // size of pBlob in bytes
- uint32_t pBlob; // pointer to buffer containing module-defined event data
-};
-
-struct MC_MsgHeader64
-{
- uint64_t cbSize; // size of the structure in bytes
- uint64_t szModule; // pointer to name of the module that 'owns' this
- // event, ie the one that is in control of the data format
- uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until
- // 2106 unless you use the standard C library which is
- // signed and can only do until 2038. In GMT.
- uint32_t flags; // the omnipresent flags
- uint32_t eventType; // module-defined event type field
- uint32_t cbBlob; // size of pBlob in bytes
- uint64_t pBlob; // pointer to buffer containing module-defined event data
-};
-
-#pragma pack(pop)
-
-class CDbxMc : public MDatabaseReadonly, public MZeroedObject
-{
- HANDLE m_hFile = INVALID_HANDLE_VALUE;
-
- MC_FileHeader m_hdr;
-
- std::vector<uint32_t> m_events;
- std::vector<uint32_t>::iterator m_curr;
-
- CMStringA readString()
- {
- CMStringA res;
- char c;
- DWORD dwRead;
- while (ReadFile(m_hFile, &c, 1, &dwRead, 0)) {
- if (c == 0)
- break;
- res.AppendChar(c);
- }
- return res;
- }
-
-public:
- CDbxMc()
- {}
-
- ~CDbxMc()
- {
- if (m_hFile != INVALID_HANDLE_VALUE)
- ::CloseHandle(m_hFile);
- }
-
- void Load()
- {
- // mcontacts operates with the only contact with pseudo id=1
- m_cache->AddContactToCache(1);
-
- uint32_t pos = 0;
- while (pos < m_hdr.dataSize) {
- DWORD dwPos = SetFilePointer(m_hFile, 0, 0, FILE_CURRENT), dwRead, dwSize;
- BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, 0);
- if (!r || dwRead < sizeof(dwSize))
- return;
- if (dwSize != sizeof(MC_MsgHeader32) && dwSize != sizeof(MC_MsgHeader64))
- return;
-
- m_events.push_back(dwPos);
- SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
-
- if (dwSize == sizeof(MC_MsgHeader32)) {
- MC_MsgHeader32 hdr;
- r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
- if (!r || dwRead < sizeof(hdr))
- return;
- SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT);
- }
- else {
- MC_MsgHeader64 hdr;
- r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
- if (!r || dwRead < sizeof(hdr))
- return;
- SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT);
- }
- pos += dwSize;
- }
- }
-
- int Open(const wchar_t *profile)
- {
- m_hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
- if (m_hFile == INVALID_HANDLE_VALUE)
- return EGROKPRF_CANTREAD;
-
- DWORD dwRead;
- BOOL r = ReadFile(m_hFile, &m_hdr, sizeof(m_hdr), &dwRead, nullptr);
- if (!r)
- return EGROKPRF_CANTREAD;
-
- return memcmp(&m_hdr.signature, HEADER_STR, 2) ? EGROKPRF_UNKHEADER : EGROKPRF_NOERROR;
- }
-
- // mcontacts format always store history for one contact only
- STDMETHODIMP_(int) GetBlobSize(MEVENT dwOffset) override
- {
- if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN))
- return 0;
-
- DWORD dwRead, dwSize;
- BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr);
- if (!r || dwRead != sizeof(dwSize))
- return 0;
-
- SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
-
- if (dwSize == sizeof(MC_MsgHeader32)) {
- MC_MsgHeader32 hdr;
- r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
- if (!r || dwRead != sizeof(hdr))
- return 0;
- return hdr.cbBlob+1;
- }
- if (dwSize == sizeof(MC_MsgHeader64)) {
- MC_MsgHeader64 hdr;
- r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
- if (!r || dwRead != sizeof(hdr))
- return 0;
- return hdr.cbBlob+1;
- }
- return 0;
- }
-
- STDMETHODIMP_(int) GetContactCount(void) override
- {
- return 1;
- }
-
- STDMETHODIMP_(int) GetEventCount(MCONTACT) override
- {
- return (int)m_events.size();
- }
-
- STDMETHODIMP_(BOOL) GetEvent(MEVENT dwOffset, DBEVENTINFO *dbei) override
- {
- if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN))
- return 1;
-
- DWORD dwRead, dwSize;
- BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr);
- if (!r || dwRead != sizeof(dwSize))
- return 1;
-
- SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
-
- int cbLen;
- if (dwSize == sizeof(MC_MsgHeader32)) {
- MC_MsgHeader32 hdr;
- r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
- if (!r || dwRead != sizeof(hdr))
- return 1;
-
- dbei->eventType = hdr.eventType;
- cbLen = hdr.cbBlob;
- dbei->flags = hdr.flags;
- dbei->timestamp = hdr.timestamp;
- }
- else if (dwSize == sizeof(MC_MsgHeader64)) {
- MC_MsgHeader64 hdr;
- r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
- if (!r || dwRead != sizeof(hdr))
- return 1;
-
- dbei->eventType = hdr.eventType;
- cbLen = hdr.cbBlob;
- dbei->flags = hdr.flags;
- dbei->timestamp = hdr.timestamp;
- }
- else return 1;
-
- if (dbei->cbBlob && cbLen) {
- uint32_t copySize = min(uint32_t(cbLen), dbei->cbBlob-1);
- if (!ReadFile(m_hFile, dbei->pBlob, copySize, &dwRead, 0) || dwRead != copySize)
- return 0;
-
- dbei->cbBlob = copySize;
- dbei->pBlob[copySize] = 0;
- }
-
- return 0;
- }
-
- STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override
- {
- if (m_events.empty())
- return 0;
-
- m_curr = m_events.begin();
- return *m_curr;
- }
-
- STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT) override
- {
- if (m_curr == m_events.end())
- return 0;
-
- ++m_curr;
- return *m_curr;
- }
-
- STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override
- {
- if (m_events.empty())
- return 0;
-
- m_curr = m_events.end();
- return *m_curr;
- }
-
- STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT) override
- {
- if (m_curr == m_events.begin())
- return 0;
-
- --m_curr;
- return *m_curr;
- }
-
- STDMETHODIMP_(DATABASELINK *) GetDriver() override;
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// database link functions
-
-static int mc_makeDatabase(const wchar_t*)
-{
- return 1;
-}
-
-static int mc_grokHeader(const wchar_t *profile)
-{
- return CDbxMc().Open(profile);
-}
-
-static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL)
-{
- std::unique_ptr<CDbxMc> db(new CDbxMc());
- if (db->Open(profile))
- return nullptr;
-
- db->Load();
- return db.release();
-}
-
-static DATABASELINK dblink =
-{
- 0,
- "mcontacts",
- L"mContacts file driver",
- mc_makeDatabase,
- mc_grokHeader,
- mc_load
-};
-
-STDMETHODIMP_(DATABASELINK *) CDbxMc::GetDriver()
-{
- return &dblink;
-}
-
-void RegisterMContacts()
-{
- RegisterDatabasePlugin(&dblink);
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <memory>
+#include <vector>
+
+#define HEADER_STR "HB"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// CDbxMcontacts database driver, read-only
+
+#pragma pack(push, 1)
+
+struct MC_FileHeader
+{
+ uint8_t signature[2];
+ uint32_t version;
+ uint32_t dataSize;
+};
+
+struct MC_MsgHeader32
+{
+ uint32_t cbSize; // size of the structure in bytes
+ uint32_t szModule; // pointer to name of the module that 'owns' this
+ // event, ie the one that is in control of the data format
+ uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until
+ // 2106 unless you use the standard C library which is
+ // signed and can only do until 2038. In GMT.
+ uint32_t flags; // the omnipresent flags
+ uint32_t eventType; // module-defined event type field
+ uint32_t cbBlob; // size of pBlob in bytes
+ uint32_t pBlob; // pointer to buffer containing module-defined event data
+};
+
+struct MC_MsgHeader64
+{
+ uint64_t cbSize; // size of the structure in bytes
+ uint64_t szModule; // pointer to name of the module that 'owns' this
+ // event, ie the one that is in control of the data format
+ uint32_t timestamp; // seconds since 00:00, 01/01/1970. Gives us times until
+ // 2106 unless you use the standard C library which is
+ // signed and can only do until 2038. In GMT.
+ uint32_t flags; // the omnipresent flags
+ uint32_t eventType; // module-defined event type field
+ uint32_t cbBlob; // size of pBlob in bytes
+ uint64_t pBlob; // pointer to buffer containing module-defined event data
+};
+
+#pragma pack(pop)
+
+class CDbxMc : public MDatabaseReadonly, public MZeroedObject
+{
+ HANDLE m_hFile = INVALID_HANDLE_VALUE;
+
+ MC_FileHeader m_hdr;
+
+ std::vector<uint32_t> m_events;
+ std::vector<uint32_t>::iterator m_curr;
+
+ CMStringA readString()
+ {
+ CMStringA res;
+ char c;
+ DWORD dwRead;
+ while (ReadFile(m_hFile, &c, 1, &dwRead, 0)) {
+ if (c == 0)
+ break;
+ res.AppendChar(c);
+ }
+ return res;
+ }
+
+public:
+ CDbxMc()
+ {}
+
+ ~CDbxMc()
+ {
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ ::CloseHandle(m_hFile);
+ }
+
+ void Load()
+ {
+ // mcontacts operates with the only contact with pseudo id=1
+ m_cache->AddContactToCache(1);
+
+ uint32_t pos = 0;
+ while (pos < m_hdr.dataSize) {
+ DWORD dwPos = SetFilePointer(m_hFile, 0, 0, FILE_CURRENT), dwRead, dwSize;
+ BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, 0);
+ if (!r || dwRead < sizeof(dwSize))
+ return;
+ if (dwSize != sizeof(MC_MsgHeader32) && dwSize != sizeof(MC_MsgHeader64))
+ return;
+
+ m_events.push_back(dwPos);
+ SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
+
+ if (dwSize == sizeof(MC_MsgHeader32)) {
+ MC_MsgHeader32 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead < sizeof(hdr))
+ return;
+ SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT);
+ }
+ else {
+ MC_MsgHeader64 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead < sizeof(hdr))
+ return;
+ SetFilePointer(m_hFile, hdr.cbBlob, 0, FILE_CURRENT);
+ }
+ pos += dwSize;
+ }
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ m_hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
+ if (m_hFile == INVALID_HANDLE_VALUE)
+ return EGROKPRF_CANTREAD;
+
+ DWORD dwRead;
+ BOOL r = ReadFile(m_hFile, &m_hdr, sizeof(m_hdr), &dwRead, nullptr);
+ if (!r)
+ return EGROKPRF_CANTREAD;
+
+ return memcmp(&m_hdr.signature, HEADER_STR, 2) ? EGROKPRF_UNKHEADER : EGROKPRF_NOERROR;
+ }
+
+ // mcontacts format always store history for one contact only
+ STDMETHODIMP_(int) GetBlobSize(MEVENT dwOffset) override
+ {
+ if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN))
+ return 0;
+
+ DWORD dwRead, dwSize;
+ BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr);
+ if (!r || dwRead != sizeof(dwSize))
+ return 0;
+
+ SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
+
+ if (dwSize == sizeof(MC_MsgHeader32)) {
+ MC_MsgHeader32 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 0;
+ return hdr.cbBlob+1;
+ }
+ if (dwSize == sizeof(MC_MsgHeader64)) {
+ MC_MsgHeader64 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 0;
+ return hdr.cbBlob+1;
+ }
+ return 0;
+ }
+
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return 1;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return (int)m_events.size();
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT dwOffset, DBEVENTINFO *dbei) override
+ {
+ if (INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN))
+ return 1;
+
+ DWORD dwRead, dwSize;
+ BOOL r = ReadFile(m_hFile, &dwSize, sizeof(dwSize), &dwRead, nullptr);
+ if (!r || dwRead != sizeof(dwSize))
+ return 1;
+
+ SetFilePointer(m_hFile, -4, 0, FILE_CURRENT);
+
+ int cbLen;
+ if (dwSize == sizeof(MC_MsgHeader32)) {
+ MC_MsgHeader32 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 1;
+
+ dbei->eventType = hdr.eventType;
+ cbLen = hdr.cbBlob;
+ dbei->flags = hdr.flags;
+ dbei->timestamp = hdr.timestamp;
+ }
+ else if (dwSize == sizeof(MC_MsgHeader64)) {
+ MC_MsgHeader64 hdr;
+ r = ReadFile(m_hFile, &hdr, sizeof(hdr), &dwRead, 0);
+ if (!r || dwRead != sizeof(hdr))
+ return 1;
+
+ dbei->eventType = hdr.eventType;
+ cbLen = hdr.cbBlob;
+ dbei->flags = hdr.flags;
+ dbei->timestamp = hdr.timestamp;
+ }
+ else return 1;
+
+ if (dbei->cbBlob && cbLen) {
+ uint32_t copySize = min(uint32_t(cbLen), dbei->cbBlob-1);
+ if (!ReadFile(m_hFile, dbei->pBlob, copySize, &dwRead, 0) || dwRead != copySize)
+ return 0;
+
+ dbei->cbBlob = copySize;
+ dbei->pBlob[copySize] = 0;
+ }
+
+ return 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override
+ {
+ if (m_events.empty())
+ return 0;
+
+ m_curr = m_events.begin();
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT) override
+ {
+ if (m_curr == m_events.end())
+ return 0;
+
+ ++m_curr;
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override
+ {
+ if (m_events.empty())
+ return 0;
+
+ m_curr = m_events.end();
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT) override
+ {
+ if (m_curr == m_events.begin())
+ return 0;
+
+ --m_curr;
+ return *m_curr;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver() override;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// database link functions
+
+static int mc_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int mc_grokHeader(const wchar_t *profile)
+{
+ return CDbxMc().Open(profile);
+}
+
+static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxMc> db(new CDbxMc());
+ if (db->Open(profile))
+ return nullptr;
+
+ db->Load();
+ return db.release();
+}
+
+static DATABASELINK dblink =
+{
+ 0,
+ "mcontacts",
+ L"mContacts file driver",
+ mc_makeDatabase,
+ mc_grokHeader,
+ mc_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxMc::GetDriver()
+{
+ return &dblink;
+}
+
+void RegisterMContacts()
+{
+ RegisterDatabasePlugin(&dblink);
+}
diff --git a/plugins/Import/src/miranda.cpp b/plugins/Import/src/miranda.cpp
index 7970027fe6..c43fc61a4d 100644
--- a/plugins/Import/src/miranda.cpp
+++ b/plugins/Import/src/miranda.cpp
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/Import/src/patterns.cpp b/plugins/Import/src/patterns.cpp
index 094bf1f33c..9dadce018c 100644
--- a/plugins/Import/src/patterns.cpp
+++ b/plugins/Import/src/patterns.cpp
@@ -1,732 +1,732 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "stdafx.h"
-
-#include <memory>
-#include <vector>
-
-void CMPlugin::LoadPatterns()
-{
- wchar_t wszPath[MAX_PATH], wszFullPath[MAX_PATH];
- GetModuleFileNameW(m_hInst, wszPath, _countof(wszPath));
- if (auto *p = wcsrchr(wszPath, '\\'))
- *p = 0;
-
- mir_snwprintf(wszFullPath, L"%s\\Import\\*.ini", wszPath);
-
- WIN32_FIND_DATAW fd;
- HANDLE hFind = FindFirstFileW(wszFullPath, &fd);
- if (hFind == INVALID_HANDLE_VALUE)
- return;
-
- do {
- mir_snwprintf(wszFullPath, L"%s\\Import\\%s", wszPath, fd.cFileName);
- LoadPattern(wszFullPath);
- }
- while (FindNextFileW(hFind, &fd) != 0);
-}
-
-void CMPlugin::LoadPattern(const wchar_t *pwszFileName)
-{
- // [General] section
- wchar_t buf[1024];
- if (!GetPrivateProfileStringW(L"General", L"Name", L"", buf, _countof(buf), pwszFileName))
- return;
-
- std::unique_ptr<CImportPattern> pNew(new CImportPattern());
- pNew->wszName = buf;
- pNew->iType = GetPrivateProfileIntW(L"General", L"Type", 1, pwszFileName);
-
- if (GetPrivateProfileStringW(L"General", L"DefaultExtension", L"", buf, _countof(buf), pwszFileName))
- pNew->wszExt = buf;
-
- if (pNew->iType == 1) {
- if (GetPrivateProfileStringW(L"General", L"Charset", L"", buf, _countof(buf), pwszFileName)) {
- if (!wcsicmp(buf, L"ANSI"))
- pNew->iCodePage = GetPrivateProfileIntW(L"General", L"Codepage", CP_ACP, pwszFileName);
- else if (!wcsicmp(buf, L"UCS2"))
- pNew->iCodePage = 1200;
- }
- else return;
- }
-
- pNew->iUseHeader = GetPrivateProfileIntW(L"General", L"UseHeader", 0, pwszFileName);
- pNew->iUsePreMsg = GetPrivateProfileIntW(L"General", L"UsePreMsg", 0, pwszFileName);
- pNew->iUseFilename = GetPrivateProfileIntW(L"General", L"UseFileName", 0, pwszFileName);
-
- // [Message] section
- int erroffset;
- const char *err;
- if (pNew->iType == 1) {
- if (GetPrivateProfileStringW(L"Message", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
- if ((pNew->regMessage.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
- return;
- pNew->regMessage.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
- }
- else return;
-
- if (GetPrivateProfileStringW(L"Message", L"In", L"", buf, _countof(buf), pwszFileName))
- pNew->wszIncoming = buf;
- if (GetPrivateProfileStringW(L"Message", L"Out", L"", buf, _countof(buf), pwszFileName))
- pNew->wszOutgoing = buf;
-
- pNew->iDirection = GetPrivateProfileIntW(L"Message", L"Direction", 0, pwszFileName);
- pNew->iDay = GetPrivateProfileIntW(L"Message", L"Day", 0, pwszFileName);
- pNew->iMonth = GetPrivateProfileIntW(L"Message", L"Month", 0, pwszFileName);
- pNew->iYear = GetPrivateProfileIntW(L"Message", L"Year", 0, pwszFileName);
- pNew->iHours = GetPrivateProfileIntW(L"Message", L"Hours", 0, pwszFileName);
- pNew->iMinutes = GetPrivateProfileIntW(L"Message", L"Minutes", 0, pwszFileName);
- pNew->iSeconds = GetPrivateProfileIntW(L"Message", L"Seconds", 0, pwszFileName);
- }
-
- if (pNew->iUseHeader) {
- if (GetPrivateProfileStringW(L"Header", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
- if ((pNew->regHeader.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
- return;
- pNew->regHeader.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
- }
- else return;
-
- pNew->iHdrIncoming = GetPrivateProfileIntW(L"Header", L"In", 0, pwszFileName);
- pNew->iHdrOutgoing = GetPrivateProfileIntW(L"Header", L"Out", 0, pwszFileName);
- pNew->iHdrInNick = GetPrivateProfileIntW(L"Header", L"InNick", 0, pwszFileName);
- pNew->iHdrOutNick = GetPrivateProfileIntW(L"Header", L"OutNick", 0, pwszFileName);
- pNew->iHdrInUID = GetPrivateProfileIntW(L"Header", L"InUID", 0, pwszFileName);
- pNew->iHdrOutUID = GetPrivateProfileIntW(L"Header", L"OutUID", 0, pwszFileName);
- }
-
- if (pNew->iUsePreMsg) {
- pNew->preRN = GetPrivateProfileIntW(L"PreMessage", L"PreRN", -1, pwszFileName);
- pNew->preSP = GetPrivateProfileIntW(L"PreMessage", L"PreSP", 0, pwszFileName);
- pNew->afterRN = GetPrivateProfileIntW(L"PreMessage", L"AfterRN", -1, pwszFileName);
- pNew->afterSP = GetPrivateProfileIntW(L"PreMessage", L"AfterSP", 0, pwszFileName);
- }
-
- if (pNew->iUseFilename) {
- if (!GetPrivateProfileStringW(L"FileName", L"Pattern", L"", buf, _countof(buf), pwszFileName))
- return;
- if ((pNew->regFilename.pattern = pcre16_compile(buf, 0, &err, &erroffset, nullptr)) == nullptr)
- return;
- pNew->regFilename.extra = pcre16_study(pNew->regFilename.pattern, 0, &err);
-
- pNew->iInNick = GetPrivateProfileIntW(L"FileName", L"InNick", 0, pwszFileName);
- pNew->iInUID = GetPrivateProfileIntW(L"FileName", L"InUID", 0, pwszFileName);
- pNew->iOutNick = GetPrivateProfileIntW(L"FileName", L"OutNick", 0, pwszFileName);
- pNew->iOutUID = GetPrivateProfileIntW(L"FileName", L"OutUID", 0, pwszFileName);
- }
-
- m_patterns.insert(pNew.release());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// pattern-based database driver
-
-class CDbxPattern : public MDatabaseReadonly, public MZeroedObject
-{
- typedef MDatabaseReadonly CSuper;
-
- CMStringW m_buf, m_folder;
- MCONTACT m_hCurrContact = INVALID_CONTACT_ID;
- HANDLE m_hFile = INVALID_HANDLE_VALUE, m_hMap = 0;
- const uint8_t *m_pFile = 0;
- int m_iFileVersion = 0, m_iMsgHeaderSize = 0;
-
- std::vector<uint32_t> m_events;
- std::vector<CMStringW> m_files;
-
- bool CheckContact(MCONTACT hContact)
- {
- if (hContact != m_hCurrContact) {
- m_hCurrContact = hContact;
- if (!Load(m_files[hContact - 1]))
- return false;
- }
- return true;
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////
- // QHF file format
-
- bool LoadBinaryFile(const uint8_t *pFile, uint32_t iSize)
- {
- if (memicmp(pFile, "QHF", 3)) {
- AddMessage(LPGENW("Invalid file header"));
- return false;
- }
-
- m_iFileVersion = pFile[3];
-
- uint32_t fsz = RLInteger(&pFile[4]); pFile += 0x2C;
- uint32_t UIDLen = RLWord(pFile); pFile += 2;
- char *UIDStr = (char*)_alloca(UIDLen + 2);
- if (m_iFileVersion <= 2)
- strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen);
- else
- strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen + 1);
- pFile += UIDLen;
-
- uint32_t NickLen = RLWord(pFile); pFile += 2;
- char *NickStr = (char*)_alloca(NickLen + 2);
- if (m_iFileVersion <= 2)
- strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen);
- else
- strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen + 1);
- pFile += NickLen;
-
- DBCONTACTWRITESETTING dbcws = {};
- dbcws.szModule = "Pattern";
- dbcws.value.type = DBVT_UTF8;
-
- dbcws.szSetting = "ID";
- dbcws.value.pszVal = UIDStr;
- WriteContactSetting(m_hCurrContact, &dbcws);
-
- dbcws.szSetting = "Nick";
- dbcws.value.pszVal = NickStr;
- WriteContactSetting(m_hCurrContact, &dbcws);
-
- uint32_t iHeaderSize = 0x30 + NickLen + UIDLen;
- if (fsz != iSize - iHeaderSize)
- fsz = iSize - iHeaderSize;
-
- m_iMsgHeaderSize = (m_iFileVersion >= 3) ? 0x23 : 0x21;
- for (uint32_t i = 0; i < fsz; i += m_iMsgHeaderSize) {
- m_events.push_back(i + iHeaderSize);
- i += RLWord(&pFile[i + m_iMsgHeaderSize - 2]);
- }
-
- return true;
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////
- // Text file format, parsed by regexps
-
- bool LoadTextFile(const uint8_t *pFile)
- {
- auto *pPattern = g_pBatch->m_pPattern;
-
- switch (pPattern->iCodePage) {
- case CP_UTF8:
- m_buf = mir_utf8decodeW((char*)pFile);
- break;
- case 1200:
- m_buf = mir_wstrdup((wchar_t*)pFile);
- break;
- default:
- m_buf = mir_a2u_cp((char*)pFile, pPattern->iCodePage);
- break;
- }
-
-
- // smth went wrong or empty file
- if (m_buf.IsEmpty())
- return false;
-
- int iOffset = 0;
- if (m_buf[0] == 0xFEFF)
- m_buf.Delete(0);
-
- if (pPattern->iUseHeader) {
- int offsets[99];
- int nMatch = pcre16_exec(pPattern->regHeader.pattern, pPattern->regHeader.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
- if (nMatch <= 0) {
- AddMessage(LPGENW("Cannot parse file header, skipping file"));
- return false;
- }
-
- const wchar_t **substrings;
- if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
- if (pPattern->iUseHeader & 1) {
- pPattern->wszIncoming = substrings[pPattern->iHdrIncoming];
- pPattern->wszOutgoing = substrings[pPattern->iHdrOutgoing];
- }
-
- if (pPattern->iUseHeader & 2) {
- DBCONTACTWRITESETTING dbcws = {};
- dbcws.szModule = "Pattern";
- dbcws.value.type = DBVT_WCHAR;
-
- if (pPattern->iInUID && substrings[pPattern->iHdrInUID]) {
- dbcws.szSetting = "ID";
- dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInUID];
- WriteContactSetting(m_hCurrContact, &dbcws);
- }
-
- if (pPattern->iInNick && substrings[pPattern->iHdrInNick]) {
- dbcws.szSetting = "Nick";
- dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInNick];
- WriteContactSetting(m_hCurrContact, &dbcws);
- }
- }
- }
-
- iOffset = offsets[1];
- }
-
- while (true) {
- int offsets[99];
- int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
- if (nMatch <= 0)
- break;
-
- m_events.push_back(offsets[0]);
- iOffset = offsets[1];
- }
- return true;
- }
-
-public:
- CDbxPattern()
- {}
-
- ~CDbxPattern()
- {
- Close();
- }
-
- void Close()
- {
- if (m_pFile != nullptr)
- ::UnmapViewOfFile(m_pFile);
-
- if (m_hMap != nullptr)
- ::CloseHandle(m_hMap);
-
- if (m_hFile != INVALID_HANDLE_VALUE)
- ::CloseHandle(m_hFile);
- }
-
- bool Load(const wchar_t *pwszFileName)
- {
- m_buf.Empty();
- m_events.clear();
- Close();
-
- AddMessage(LPGENW("Loading file '%s'..."), pwszFileName);
-
- m_hFile = ::CreateFileW(pwszFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0);
- if (m_hFile == INVALID_HANDLE_VALUE) {
- AddMessage(LPGENW("Failed to open file <%s> for import: %d"), pwszFileName, GetLastError());
- return false;
- }
-
- uint32_t cbLen = ::GetFileSize(m_hFile, 0);
- m_hMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, L"ImportMapfile");
- if (m_hMap == nullptr) {
- AddMessage(LPGENW("Failed to mmap file <%s> for import: %d"), pwszFileName, GetLastError());
- return false;
- }
-
- m_pFile = (const uint8_t*)::MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0);
- if (m_pFile == nullptr) {
- AddMessage(LPGENW("Failed to map view of file <%s> for import: %d"), pwszFileName, GetLastError());
- return false;
- }
-
- if (g_pBatch->m_pPattern->iType == 1) // text file
- return LoadTextFile(m_pFile);
- return LoadBinaryFile(m_pFile, cbLen);
- }
-
- int Open(const wchar_t *profile)
- {
- CMStringW wszBaseFolder(profile);
- auto *pPattern = g_pBatch->m_pPattern;
-
- // create a mask for loading multiple data files for a folder
- uint32_t dwAttr = GetFileAttributesW(profile);
- if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) {
- wszBaseFolder = profile;
- m_folder.AppendFormat(L"%s\\*.%s", profile, pPattern->wszExt.c_str());
- }
- else {
- int i = wszBaseFolder.ReverseFind('\\');
- if (i != -1)
- wszBaseFolder = wszBaseFolder.Left(i);
- m_folder = profile;
- }
-
- int hContact = 1;
- WIN32_FIND_DATA fd;
- HANDLE hFind = FindFirstFile(m_folder, &fd);
- if (hFind != INVALID_HANDLE_VALUE) {
- do {
- // find all subfolders except "." and ".."
- if (!mir_wstrcmp(fd.cFileName, L".") || !mir_wstrcmp(fd.cFileName, L".."))
- continue;
-
- CMStringW wszFullName(wszBaseFolder + L"\\" + fd.cFileName);
- m_files.push_back(wszFullName);
-
- auto *cc = m_cache->AddContactToCache(hContact);
- cc->szProto = "Pattern";
-
- // we try to restore user id from the file name
- if (pPattern->iUseFilename) {
- int offsets[100];
- int nMatch = pcre16_exec(pPattern->regFilename.pattern, pPattern->regFilename.extra, wszFullName, wszFullName.GetLength(), 0, 0, offsets, _countof(offsets));
- if (nMatch > 0) {
- const wchar_t **substrings;
- if (pcre16_get_substring_list(wszFullName, offsets, nMatch, &substrings) >= 0) {
- DBCONTACTWRITESETTING dbcws = {};
- dbcws.szModule = cc->szProto;
- dbcws.value.type = DBVT_WCHAR;
-
- if (pPattern->iInUID && substrings[pPattern->iInUID]) {
- dbcws.szSetting = "ID";
- dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInUID];
- WriteContactSetting(hContact, &dbcws);
- }
-
- if (pPattern->iInNick && substrings[pPattern->iInNick]) {
- dbcws.szSetting = "Nick";
- dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInNick];
- WriteContactSetting(hContact, &dbcws);
- }
-
- pcre16_free_substring_list(substrings);
- }
- }
- }
- hContact++;
- }
- while (FindNextFile(hFind, &fd));
-
- FindClose(hFind);
- }
-
- if (m_files.empty())
- return EGROKPRF_CANTREAD;
-
- return EGROKPRF_NOERROR;
- }
-
- // patterns file always stores history for the single contact only
- STDMETHODIMP_(int) GetBlobSize(MEVENT idx) override
- {
- if (m_events.size() == 0 || idx < 1 || idx > m_events.size())
- return 0;
-
- if (g_pBatch->m_pPattern->iType == 1) {
- int iStart = m_events[idx-1], iEnd = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
- CMStringW msg = m_buf.Mid(iStart, iEnd - iStart);
- return (LONG)mir_strlen(ptrA(mir_utf8encodeW(msg))) + 1;
- }
-
- if (m_pFile == nullptr)
- return 0;
-
- const uint8_t *pMsg = m_pFile + m_events[idx-1];
- return RLWord(&pMsg[m_iMsgHeaderSize - 2]) + 1;
- }
-
- STDMETHODIMP_(int) GetContactCount(void) override
- {
- return (int)m_files.size();
- }
-
- STDMETHODIMP_(MCONTACT) FindFirstContact(const char *szProto) override
- {
- MCONTACT ret = CSuper::FindFirstContact(szProto);
- if (ret != 0)
- if (!CheckContact(ret))
- return 0;
- return ret;
- }
-
- STDMETHODIMP_(MCONTACT) FindNextContact(MCONTACT contactID, const char *szProto) override
- {
- MCONTACT ret = CSuper::FindNextContact(contactID, szProto);
- if (ret != 0)
- if (!CheckContact(ret))
- return 0;
- return ret;
- }
-
- STDMETHODIMP_(int) GetEventCount(MCONTACT) override
- {
- return (int)m_events.size();
- }
-
- STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT* dbv, int isStatic)
- {
- if (szSetting == nullptr || szModule == nullptr)
- return 1;
-
- DBCachedContact *cc = nullptr;
- if (hContact) {
- cc = m_cache->GetCachedContact(hContact);
- if (cc == nullptr)
- return 1;
- }
-
- size_t settingNameLen = strlen(szSetting);
- size_t moduleNameLen = strlen(szModule);
- char* szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
-
- DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 0);
- if (pCachedValue != nullptr) {
- if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
- int cbOrigLen = dbv->cchVal;
- char *cbOrigPtr = dbv->pszVal;
- memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
- if (isStatic) {
- int cbLen = 0;
- if (pCachedValue->pszVal != nullptr)
- cbLen = (int)strlen(pCachedValue->pszVal);
-
- cbOrigLen--;
- dbv->pszVal = cbOrigPtr;
- if (cbLen < cbOrigLen)
- cbOrigLen = cbLen;
- memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
- dbv->pszVal[cbOrigLen] = 0;
- dbv->cchVal = cbLen;
- }
- else {
- dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
- strcpy(dbv->pszVal, pCachedValue->pszVal);
- }
- }
- else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
-
- return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
- }
-
- return 1;
- }
-
- STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws)
- {
- if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr)
- return 1;
-
- if (hContact) {
- DBCachedContact* cc = m_cache->GetCachedContact(hContact);
- if (cc == nullptr)
- return 1;
- }
-
- DBCONTACTWRITESETTING dbcwWork = *dbcws;
- if (dbcwWork.value.type == DBVT_WCHAR) {
- T2Utf value(dbcwWork.value.pwszVal);
- dbcwWork.value.pszVal = NEWSTR_ALLOCA(value);
- dbcwWork.value.type = DBVT_UTF8;
- dbcwWork.value.cchVal = (uint16_t)strlen(dbcwWork.value.pszVal);
- }
-
- char* cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting));
- DBVARIANT* cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
- if (cachedValue != nullptr)
- m_cache->SetCachedVariant(&dbcwWork.value, cachedValue);
-
- return 0;
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////
-
- static int str2int(const wchar_t* str)
- {
- if (str == nullptr || *str == 0)
- return 0;
-
- return _wtoi(str);
- }
-
- int getBinaryEvent(MEVENT idx, DBEVENTINFO *dbei)
- {
- if (m_pFile == nullptr)
- return 1;
-
- const uint8_t *pMsg = m_pFile + m_events[idx-1];
-
- dbei->eventType = EVENTTYPE_MESSAGE;
- dbei->flags = DBEF_READ | DBEF_UTF;
- if (pMsg[0x1A] != 0)
- dbei->flags |= DBEF_SENT;
- dbei->timestamp = RLInteger(&pMsg[0x12]);
- dbei->timestamp -= TimeZone_ToLocal(dbei->timestamp) - dbei->timestamp; // deduct time zone offset from timestamp
- dbei->cbBlob = RLWord(&pMsg[m_iMsgHeaderSize - 2]);
- dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob + 1);
- memcpy(dbei->pBlob, pMsg + m_iMsgHeaderSize, dbei->cbBlob);
- if (m_iFileVersion != 1)
- for (int i = 0; i < dbei->cbBlob; i++) {
- dbei->pBlob[i] += i+1;
- dbei->pBlob[i] = 255 - dbei->pBlob[i];
- }
-
- dbei->pBlob[dbei->cbBlob] = 0;
- return 0;
- }
-
- int getTextEvent(MEVENT idx, DBEVENTINFO *dbei)
- {
- auto *pPattern = g_pBatch->m_pPattern;
-
- int offsets[99];
- int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), m_events[idx-1], PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
- if (nMatch <= 0)
- return 1;
-
- dbei->eventType = EVENTTYPE_MESSAGE;
- dbei->flags = DBEF_READ | DBEF_UTF;
-
- int h1 = offsets[1], h2 = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
- int prn = -1, arn = -1;
- if (pPattern->iUsePreMsg)
- prn = pPattern->preRN, arn = pPattern->afterRN;
-
- if (prn != 0) {
- int i = 0;
- while (m_buf[h1] == '\r' && m_buf[h1 + 1] == '\n' && i < prn)
- h1 += 2, i++;
- }
-
- if (arn != 0) {
- int i = 0;
- while (m_buf[h2 - 2] == '\r' && m_buf[h2 - 1] == '\n' && i < arn)
- h2 -= 2, i++;
- }
-
- if (dbei->cbBlob) {
- CMStringW wszBody = m_buf.Mid(h1, h2 - h1).Trim();
- if (!wszBody.IsEmpty()) {
- ptrA tmp(mir_utf8encodeW(wszBody));
- int copySize = min(dbei->cbBlob - 1, (int)mir_strlen(tmp));
- memcpy(dbei->pBlob, tmp, copySize);
- dbei->pBlob[copySize] = 0;
- dbei->cbBlob = copySize;
- }
- else dbei->cbBlob = 0;
- }
-
- const wchar_t **substrings;
- if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
- struct tm st = {};
- st.tm_year = str2int(substrings[pPattern->iYear]);
- if (st.tm_year > 1900)
- st.tm_year -= 1900;
- st.tm_mon = str2int(substrings[pPattern->iMonth]) - 1;
- st.tm_mday = str2int(substrings[pPattern->iDay]);
- st.tm_hour = str2int(substrings[pPattern->iHours]);
- st.tm_min = str2int(substrings[pPattern->iMinutes]);
- st.tm_sec = (pPattern->iSeconds) ? str2int(substrings[pPattern->iSeconds]) : 0;
- dbei->timestamp = mktime(&st);
-
- if (pPattern->iDirection)
- if (pPattern->wszOutgoing == substrings[pPattern->iDirection])
- dbei->flags |= DBEF_SENT;
-
-
- pcre16_free_substring_list(substrings);
- }
- return 0;
- }
-
- STDMETHODIMP_(BOOL) GetEvent(MEVENT idx, DBEVENTINFO *dbei) override
- {
- if (dbei == nullptr || m_events.size() == 0 || idx < 1 || idx > m_events.size())
- return 1;
-
- if (g_pBatch->m_pPattern->iType == 1)
- return getTextEvent(idx, dbei);
-
- return getBinaryEvent(idx, dbei);
- }
-
- STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT hContact) override
- {
- // no system history
- if (hContact == 0)
- return 0;
-
- if (!CheckContact(hContact))
- return 0;
-
- return m_events.size() > 0 ? 1 : 0;
- }
-
- STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT idx) override
- {
- if (idx >= m_events.size())
- return 0;
-
- return idx + 1;
- }
-
- STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT hContact) override
- {
- // no system history
- if (hContact == 0)
- return 0;
-
- if (!CheckContact(hContact))
- return 0;
-
- return m_events.size() > 0 ? (MEVENT)m_events.size() : 0;
- }
-
- STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT idx) override
- {
- return (idx >= 1) ? idx-1 : 0;
- }
-
- STDMETHODIMP_(DATABASELINK *) GetDriver();
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// database link functions
-
-static int pattern_makeDatabase(const wchar_t*)
-{
- return 1;
-}
-
-static int pattern_grokHeader(const wchar_t *profile)
-{
- return CDbxPattern().Open(profile);
-}
-
-static MDatabaseCommon* pattern_load(const wchar_t *profile, BOOL)
-{
- std::unique_ptr<CDbxPattern> db(new CDbxPattern());
- if (db->Open(profile))
- return nullptr;
-
- return db.release();
-}
-
-DATABASELINK g_patternDbLink =
-{
- 0,
- "pattern",
- L"Pattern-based file driver",
- pattern_makeDatabase,
- pattern_grokHeader,
- pattern_load
-};
-
-STDMETHODIMP_(DATABASELINK *) CDbxPattern::GetDriver()
-{
- return &g_patternDbLink;
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <memory>
+#include <vector>
+
+void CMPlugin::LoadPatterns()
+{
+ wchar_t wszPath[MAX_PATH], wszFullPath[MAX_PATH];
+ GetModuleFileNameW(m_hInst, wszPath, _countof(wszPath));
+ if (auto *p = wcsrchr(wszPath, '\\'))
+ *p = 0;
+
+ mir_snwprintf(wszFullPath, L"%s\\Import\\*.ini", wszPath);
+
+ WIN32_FIND_DATAW fd;
+ HANDLE hFind = FindFirstFileW(wszFullPath, &fd);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do {
+ mir_snwprintf(wszFullPath, L"%s\\Import\\%s", wszPath, fd.cFileName);
+ LoadPattern(wszFullPath);
+ }
+ while (FindNextFileW(hFind, &fd) != 0);
+}
+
+void CMPlugin::LoadPattern(const wchar_t *pwszFileName)
+{
+ // [General] section
+ wchar_t buf[1024];
+ if (!GetPrivateProfileStringW(L"General", L"Name", L"", buf, _countof(buf), pwszFileName))
+ return;
+
+ std::unique_ptr<CImportPattern> pNew(new CImportPattern());
+ pNew->wszName = buf;
+ pNew->iType = GetPrivateProfileIntW(L"General", L"Type", 1, pwszFileName);
+
+ if (GetPrivateProfileStringW(L"General", L"DefaultExtension", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszExt = buf;
+
+ if (pNew->iType == 1) {
+ if (GetPrivateProfileStringW(L"General", L"Charset", L"", buf, _countof(buf), pwszFileName)) {
+ if (!wcsicmp(buf, L"ANSI"))
+ pNew->iCodePage = GetPrivateProfileIntW(L"General", L"Codepage", CP_ACP, pwszFileName);
+ else if (!wcsicmp(buf, L"UCS2"))
+ pNew->iCodePage = 1200;
+ }
+ else return;
+ }
+
+ pNew->iUseHeader = GetPrivateProfileIntW(L"General", L"UseHeader", 0, pwszFileName);
+ pNew->iUsePreMsg = GetPrivateProfileIntW(L"General", L"UsePreMsg", 0, pwszFileName);
+ pNew->iUseFilename = GetPrivateProfileIntW(L"General", L"UseFileName", 0, pwszFileName);
+
+ // [Message] section
+ int erroffset;
+ const char *err;
+ if (pNew->iType == 1) {
+ if (GetPrivateProfileStringW(L"Message", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
+ if ((pNew->regMessage.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regMessage.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
+ }
+ else return;
+
+ if (GetPrivateProfileStringW(L"Message", L"In", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszIncoming = buf;
+ if (GetPrivateProfileStringW(L"Message", L"Out", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszOutgoing = buf;
+
+ pNew->iDirection = GetPrivateProfileIntW(L"Message", L"Direction", 0, pwszFileName);
+ pNew->iDay = GetPrivateProfileIntW(L"Message", L"Day", 0, pwszFileName);
+ pNew->iMonth = GetPrivateProfileIntW(L"Message", L"Month", 0, pwszFileName);
+ pNew->iYear = GetPrivateProfileIntW(L"Message", L"Year", 0, pwszFileName);
+ pNew->iHours = GetPrivateProfileIntW(L"Message", L"Hours", 0, pwszFileName);
+ pNew->iMinutes = GetPrivateProfileIntW(L"Message", L"Minutes", 0, pwszFileName);
+ pNew->iSeconds = GetPrivateProfileIntW(L"Message", L"Seconds", 0, pwszFileName);
+ }
+
+ if (pNew->iUseHeader) {
+ if (GetPrivateProfileStringW(L"Header", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
+ if ((pNew->regHeader.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regHeader.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
+ }
+ else return;
+
+ pNew->iHdrIncoming = GetPrivateProfileIntW(L"Header", L"In", 0, pwszFileName);
+ pNew->iHdrOutgoing = GetPrivateProfileIntW(L"Header", L"Out", 0, pwszFileName);
+ pNew->iHdrInNick = GetPrivateProfileIntW(L"Header", L"InNick", 0, pwszFileName);
+ pNew->iHdrOutNick = GetPrivateProfileIntW(L"Header", L"OutNick", 0, pwszFileName);
+ pNew->iHdrInUID = GetPrivateProfileIntW(L"Header", L"InUID", 0, pwszFileName);
+ pNew->iHdrOutUID = GetPrivateProfileIntW(L"Header", L"OutUID", 0, pwszFileName);
+ }
+
+ if (pNew->iUsePreMsg) {
+ pNew->preRN = GetPrivateProfileIntW(L"PreMessage", L"PreRN", -1, pwszFileName);
+ pNew->preSP = GetPrivateProfileIntW(L"PreMessage", L"PreSP", 0, pwszFileName);
+ pNew->afterRN = GetPrivateProfileIntW(L"PreMessage", L"AfterRN", -1, pwszFileName);
+ pNew->afterSP = GetPrivateProfileIntW(L"PreMessage", L"AfterSP", 0, pwszFileName);
+ }
+
+ if (pNew->iUseFilename) {
+ if (!GetPrivateProfileStringW(L"FileName", L"Pattern", L"", buf, _countof(buf), pwszFileName))
+ return;
+ if ((pNew->regFilename.pattern = pcre16_compile(buf, 0, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regFilename.extra = pcre16_study(pNew->regFilename.pattern, 0, &err);
+
+ pNew->iInNick = GetPrivateProfileIntW(L"FileName", L"InNick", 0, pwszFileName);
+ pNew->iInUID = GetPrivateProfileIntW(L"FileName", L"InUID", 0, pwszFileName);
+ pNew->iOutNick = GetPrivateProfileIntW(L"FileName", L"OutNick", 0, pwszFileName);
+ pNew->iOutUID = GetPrivateProfileIntW(L"FileName", L"OutUID", 0, pwszFileName);
+ }
+
+ m_patterns.insert(pNew.release());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// pattern-based database driver
+
+class CDbxPattern : public MDatabaseReadonly, public MZeroedObject
+{
+ typedef MDatabaseReadonly CSuper;
+
+ CMStringW m_buf, m_folder;
+ MCONTACT m_hCurrContact = INVALID_CONTACT_ID;
+ HANDLE m_hFile = INVALID_HANDLE_VALUE, m_hMap = 0;
+ const uint8_t *m_pFile = 0;
+ int m_iFileVersion = 0, m_iMsgHeaderSize = 0;
+
+ std::vector<uint32_t> m_events;
+ std::vector<CMStringW> m_files;
+
+ bool CheckContact(MCONTACT hContact)
+ {
+ if (hContact != m_hCurrContact) {
+ m_hCurrContact = hContact;
+ if (!Load(m_files[hContact - 1]))
+ return false;
+ }
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // QHF file format
+
+ bool LoadBinaryFile(const uint8_t *pFile, uint32_t iSize)
+ {
+ if (memicmp(pFile, "QHF", 3)) {
+ AddMessage(LPGENW("Invalid file header"));
+ return false;
+ }
+
+ m_iFileVersion = pFile[3];
+
+ uint32_t fsz = RLInteger(&pFile[4]); pFile += 0x2C;
+ uint32_t UIDLen = RLWord(pFile); pFile += 2;
+ char *UIDStr = (char*)_alloca(UIDLen + 2);
+ if (m_iFileVersion <= 2)
+ strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen);
+ else
+ strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen + 1);
+ pFile += UIDLen;
+
+ uint32_t NickLen = RLWord(pFile); pFile += 2;
+ char *NickStr = (char*)_alloca(NickLen + 2);
+ if (m_iFileVersion <= 2)
+ strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen);
+ else
+ strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen + 1);
+ pFile += NickLen;
+
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = "Pattern";
+ dbcws.value.type = DBVT_UTF8;
+
+ dbcws.szSetting = "ID";
+ dbcws.value.pszVal = UIDStr;
+ WriteContactSetting(m_hCurrContact, &dbcws);
+
+ dbcws.szSetting = "Nick";
+ dbcws.value.pszVal = NickStr;
+ WriteContactSetting(m_hCurrContact, &dbcws);
+
+ uint32_t iHeaderSize = 0x30 + NickLen + UIDLen;
+ if (fsz != iSize - iHeaderSize)
+ fsz = iSize - iHeaderSize;
+
+ m_iMsgHeaderSize = (m_iFileVersion >= 3) ? 0x23 : 0x21;
+ for (uint32_t i = 0; i < fsz; i += m_iMsgHeaderSize) {
+ m_events.push_back(i + iHeaderSize);
+ i += RLWord(&pFile[i + m_iMsgHeaderSize - 2]);
+ }
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Text file format, parsed by regexps
+
+ bool LoadTextFile(const uint8_t *pFile)
+ {
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ switch (pPattern->iCodePage) {
+ case CP_UTF8:
+ m_buf = mir_utf8decodeW((char*)pFile);
+ break;
+ case 1200:
+ m_buf = mir_wstrdup((wchar_t*)pFile);
+ break;
+ default:
+ m_buf = mir_a2u_cp((char*)pFile, pPattern->iCodePage);
+ break;
+ }
+
+
+ // smth went wrong or empty file
+ if (m_buf.IsEmpty())
+ return false;
+
+ int iOffset = 0;
+ if (m_buf[0] == 0xFEFF)
+ m_buf.Delete(0);
+
+ if (pPattern->iUseHeader) {
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regHeader.pattern, pPattern->regHeader.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0) {
+ AddMessage(LPGENW("Cannot parse file header, skipping file"));
+ return false;
+ }
+
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
+ if (pPattern->iUseHeader & 1) {
+ pPattern->wszIncoming = substrings[pPattern->iHdrIncoming];
+ pPattern->wszOutgoing = substrings[pPattern->iHdrOutgoing];
+ }
+
+ if (pPattern->iUseHeader & 2) {
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = "Pattern";
+ dbcws.value.type = DBVT_WCHAR;
+
+ if (pPattern->iInUID && substrings[pPattern->iHdrInUID]) {
+ dbcws.szSetting = "ID";
+ dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInUID];
+ WriteContactSetting(m_hCurrContact, &dbcws);
+ }
+
+ if (pPattern->iInNick && substrings[pPattern->iHdrInNick]) {
+ dbcws.szSetting = "Nick";
+ dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInNick];
+ WriteContactSetting(m_hCurrContact, &dbcws);
+ }
+ }
+ }
+
+ iOffset = offsets[1];
+ }
+
+ while (true) {
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0)
+ break;
+
+ m_events.push_back(offsets[0]);
+ iOffset = offsets[1];
+ }
+ return true;
+ }
+
+public:
+ CDbxPattern()
+ {}
+
+ ~CDbxPattern()
+ {
+ Close();
+ }
+
+ void Close()
+ {
+ if (m_pFile != nullptr)
+ ::UnmapViewOfFile(m_pFile);
+
+ if (m_hMap != nullptr)
+ ::CloseHandle(m_hMap);
+
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ ::CloseHandle(m_hFile);
+ }
+
+ bool Load(const wchar_t *pwszFileName)
+ {
+ m_buf.Empty();
+ m_events.clear();
+ Close();
+
+ AddMessage(LPGENW("Loading file '%s'..."), pwszFileName);
+
+ m_hFile = ::CreateFileW(pwszFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0);
+ if (m_hFile == INVALID_HANDLE_VALUE) {
+ AddMessage(LPGENW("Failed to open file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ uint32_t cbLen = ::GetFileSize(m_hFile, 0);
+ m_hMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, L"ImportMapfile");
+ if (m_hMap == nullptr) {
+ AddMessage(LPGENW("Failed to mmap file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ m_pFile = (const uint8_t*)::MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0);
+ if (m_pFile == nullptr) {
+ AddMessage(LPGENW("Failed to map view of file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ if (g_pBatch->m_pPattern->iType == 1) // text file
+ return LoadTextFile(m_pFile);
+ return LoadBinaryFile(m_pFile, cbLen);
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ CMStringW wszBaseFolder(profile);
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ // create a mask for loading multiple data files for a folder
+ uint32_t dwAttr = GetFileAttributesW(profile);
+ if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ wszBaseFolder = profile;
+ m_folder.AppendFormat(L"%s\\*.%s", profile, pPattern->wszExt.c_str());
+ }
+ else {
+ int i = wszBaseFolder.ReverseFind('\\');
+ if (i != -1)
+ wszBaseFolder = wszBaseFolder.Left(i);
+ m_folder = profile;
+ }
+
+ int hContact = 1;
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(m_folder, &fd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ // find all subfolders except "." and ".."
+ if (!mir_wstrcmp(fd.cFileName, L".") || !mir_wstrcmp(fd.cFileName, L".."))
+ continue;
+
+ CMStringW wszFullName(wszBaseFolder + L"\\" + fd.cFileName);
+ m_files.push_back(wszFullName);
+
+ auto *cc = m_cache->AddContactToCache(hContact);
+ cc->szProto = "Pattern";
+
+ // we try to restore user id from the file name
+ if (pPattern->iUseFilename) {
+ int offsets[100];
+ int nMatch = pcre16_exec(pPattern->regFilename.pattern, pPattern->regFilename.extra, wszFullName, wszFullName.GetLength(), 0, 0, offsets, _countof(offsets));
+ if (nMatch > 0) {
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(wszFullName, offsets, nMatch, &substrings) >= 0) {
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = cc->szProto;
+ dbcws.value.type = DBVT_WCHAR;
+
+ if (pPattern->iInUID && substrings[pPattern->iInUID]) {
+ dbcws.szSetting = "ID";
+ dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInUID];
+ WriteContactSetting(hContact, &dbcws);
+ }
+
+ if (pPattern->iInNick && substrings[pPattern->iInNick]) {
+ dbcws.szSetting = "Nick";
+ dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInNick];
+ WriteContactSetting(hContact, &dbcws);
+ }
+
+ pcre16_free_substring_list(substrings);
+ }
+ }
+ }
+ hContact++;
+ }
+ while (FindNextFile(hFind, &fd));
+
+ FindClose(hFind);
+ }
+
+ if (m_files.empty())
+ return EGROKPRF_CANTREAD;
+
+ return EGROKPRF_NOERROR;
+ }
+
+ // patterns file always stores history for the single contact only
+ STDMETHODIMP_(int) GetBlobSize(MEVENT idx) override
+ {
+ if (m_events.size() == 0 || idx < 1 || idx > m_events.size())
+ return 0;
+
+ if (g_pBatch->m_pPattern->iType == 1) {
+ int iStart = m_events[idx-1], iEnd = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
+ CMStringW msg = m_buf.Mid(iStart, iEnd - iStart);
+ return (LONG)mir_strlen(ptrA(mir_utf8encodeW(msg))) + 1;
+ }
+
+ if (m_pFile == nullptr)
+ return 0;
+
+ const uint8_t *pMsg = m_pFile + m_events[idx-1];
+ return RLWord(&pMsg[m_iMsgHeaderSize - 2]) + 1;
+ }
+
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return (int)m_files.size();
+ }
+
+ STDMETHODIMP_(MCONTACT) FindFirstContact(const char *szProto) override
+ {
+ MCONTACT ret = CSuper::FindFirstContact(szProto);
+ if (ret != 0)
+ if (!CheckContact(ret))
+ return 0;
+ return ret;
+ }
+
+ STDMETHODIMP_(MCONTACT) FindNextContact(MCONTACT contactID, const char *szProto) override
+ {
+ MCONTACT ret = CSuper::FindNextContact(contactID, szProto);
+ if (ret != 0)
+ if (!CheckContact(ret))
+ return 0;
+ return ret;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return (int)m_events.size();
+ }
+
+ STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT* dbv, int isStatic)
+ {
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ DBCachedContact *cc = nullptr;
+ if (hContact) {
+ cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ size_t settingNameLen = strlen(szSetting);
+ size_t moduleNameLen = strlen(szModule);
+ char* szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
+
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
+ strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ return 1;
+ }
+
+ STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws)
+ {
+ if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact* cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ DBCONTACTWRITESETTING dbcwWork = *dbcws;
+ if (dbcwWork.value.type == DBVT_WCHAR) {
+ T2Utf value(dbcwWork.value.pwszVal);
+ dbcwWork.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwWork.value.type = DBVT_UTF8;
+ dbcwWork.value.cchVal = (uint16_t)strlen(dbcwWork.value.pszVal);
+ }
+
+ char* cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting));
+ DBVARIANT* cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (cachedValue != nullptr)
+ m_cache->SetCachedVariant(&dbcwWork.value, cachedValue);
+
+ return 0;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ static int str2int(const wchar_t* str)
+ {
+ if (str == nullptr || *str == 0)
+ return 0;
+
+ return _wtoi(str);
+ }
+
+ int getBinaryEvent(MEVENT idx, DBEVENTINFO *dbei)
+ {
+ if (m_pFile == nullptr)
+ return 1;
+
+ const uint8_t *pMsg = m_pFile + m_events[idx-1];
+
+ dbei->eventType = EVENTTYPE_MESSAGE;
+ dbei->flags = DBEF_READ | DBEF_UTF;
+ if (pMsg[0x1A] != 0)
+ dbei->flags |= DBEF_SENT;
+ dbei->timestamp = RLInteger(&pMsg[0x12]);
+ dbei->timestamp -= TimeZone_ToLocal(dbei->timestamp) - dbei->timestamp; // deduct time zone offset from timestamp
+ dbei->cbBlob = RLWord(&pMsg[m_iMsgHeaderSize - 2]);
+ dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob + 1);
+ memcpy(dbei->pBlob, pMsg + m_iMsgHeaderSize, dbei->cbBlob);
+ if (m_iFileVersion != 1)
+ for (int i = 0; i < dbei->cbBlob; i++) {
+ dbei->pBlob[i] += i+1;
+ dbei->pBlob[i] = 255 - dbei->pBlob[i];
+ }
+
+ dbei->pBlob[dbei->cbBlob] = 0;
+ return 0;
+ }
+
+ int getTextEvent(MEVENT idx, DBEVENTINFO *dbei)
+ {
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), m_events[idx-1], PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0)
+ return 1;
+
+ dbei->eventType = EVENTTYPE_MESSAGE;
+ dbei->flags = DBEF_READ | DBEF_UTF;
+
+ int h1 = offsets[1], h2 = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
+ int prn = -1, arn = -1;
+ if (pPattern->iUsePreMsg)
+ prn = pPattern->preRN, arn = pPattern->afterRN;
+
+ if (prn != 0) {
+ int i = 0;
+ while (m_buf[h1] == '\r' && m_buf[h1 + 1] == '\n' && i < prn)
+ h1 += 2, i++;
+ }
+
+ if (arn != 0) {
+ int i = 0;
+ while (m_buf[h2 - 2] == '\r' && m_buf[h2 - 1] == '\n' && i < arn)
+ h2 -= 2, i++;
+ }
+
+ if (dbei->cbBlob) {
+ CMStringW wszBody = m_buf.Mid(h1, h2 - h1).Trim();
+ if (!wszBody.IsEmpty()) {
+ ptrA tmp(mir_utf8encodeW(wszBody));
+ int copySize = min(dbei->cbBlob - 1, (int)mir_strlen(tmp));
+ memcpy(dbei->pBlob, tmp, copySize);
+ dbei->pBlob[copySize] = 0;
+ dbei->cbBlob = copySize;
+ }
+ else dbei->cbBlob = 0;
+ }
+
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
+ struct tm st = {};
+ st.tm_year = str2int(substrings[pPattern->iYear]);
+ if (st.tm_year > 1900)
+ st.tm_year -= 1900;
+ st.tm_mon = str2int(substrings[pPattern->iMonth]) - 1;
+ st.tm_mday = str2int(substrings[pPattern->iDay]);
+ st.tm_hour = str2int(substrings[pPattern->iHours]);
+ st.tm_min = str2int(substrings[pPattern->iMinutes]);
+ st.tm_sec = (pPattern->iSeconds) ? str2int(substrings[pPattern->iSeconds]) : 0;
+ dbei->timestamp = mktime(&st);
+
+ if (pPattern->iDirection)
+ if (pPattern->wszOutgoing == substrings[pPattern->iDirection])
+ dbei->flags |= DBEF_SENT;
+
+
+ pcre16_free_substring_list(substrings);
+ }
+ return 0;
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT idx, DBEVENTINFO *dbei) override
+ {
+ if (dbei == nullptr || m_events.size() == 0 || idx < 1 || idx > m_events.size())
+ return 1;
+
+ if (g_pBatch->m_pPattern->iType == 1)
+ return getTextEvent(idx, dbei);
+
+ return getBinaryEvent(idx, dbei);
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT hContact) override
+ {
+ // no system history
+ if (hContact == 0)
+ return 0;
+
+ if (!CheckContact(hContact))
+ return 0;
+
+ return m_events.size() > 0 ? 1 : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT idx) override
+ {
+ if (idx >= m_events.size())
+ return 0;
+
+ return idx + 1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT hContact) override
+ {
+ // no system history
+ if (hContact == 0)
+ return 0;
+
+ if (!CheckContact(hContact))
+ return 0;
+
+ return m_events.size() > 0 ? (MEVENT)m_events.size() : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT idx) override
+ {
+ return (idx >= 1) ? idx-1 : 0;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// database link functions
+
+static int pattern_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int pattern_grokHeader(const wchar_t *profile)
+{
+ return CDbxPattern().Open(profile);
+}
+
+static MDatabaseCommon* pattern_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxPattern> db(new CDbxPattern());
+ if (db->Open(profile))
+ return nullptr;
+
+ return db.release();
+}
+
+DATABASELINK g_patternDbLink =
+{
+ 0,
+ "pattern",
+ L"Pattern-based file driver",
+ pattern_makeDatabase,
+ pattern_grokHeader,
+ pattern_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxPattern::GetDriver()
+{
+ return &g_patternDbLink;
+}
diff --git a/plugins/Import/src/progress.cpp b/plugins/Import/src/progress.cpp
index b602d0a655..f9ca110438 100644
--- a/plugins/Import/src/progress.cpp
+++ b/plugins/Import/src/progress.cpp
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/Import/src/stdafx.cxx b/plugins/Import/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Import/src/stdafx.cxx
+++ b/plugins/Import/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/plugins/Import/src/stdafx.h b/plugins/Import/src/stdafx.h
index dccb07afc5..b60d8402a5 100644
--- a/plugins/Import/src/stdafx.h
+++ b/plugins/Import/src/stdafx.h
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/Import/src/textjson.cpp b/plugins/Import/src/textjson.cpp
index 22cab70e2b..d419807ac0 100644
--- a/plugins/Import/src/textjson.cpp
+++ b/plugins/Import/src/textjson.cpp
@@ -1,269 +1,269 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "stdafx.h"
-
-#include <m_json.h>
-
-static int mc_makeDatabase(const wchar_t*)
-{
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// JSON text driver, read-only
-
-static int CompareModules(const char *p1, const char *p2)
-{
- return mir_strcmp(p1, p2);
-}
-
-class CDbxJson : public MDatabaseReadonly, public MZeroedObject
-{
- JSONNode *m_root = nullptr;
- LIST<JSONNode> m_events;
- LIST<char> m_modules;
-
-public:
- CDbxJson() :
- m_events(100),
- m_modules(10, CompareModules)
- {}
-
- ~CDbxJson()
- {
- if (m_root != nullptr)
- json_delete(m_root);
-
- for (auto &it : m_modules)
- mir_free(it);
- }
-
- void Load()
- {
- // json operates with the only contact with pseudo id=1
- m_cache->AddContactToCache(1);
- }
-
- int Open(const wchar_t *profile)
- {
- HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
- if (hFile == INVALID_HANDLE_VALUE)
- return EGROKPRF_CANTREAD;
-
- DWORD dwSize = GetFileSize(hFile, nullptr), dwRead;
- ptrA szFile((char*)mir_alloc(dwSize + 1));
- BOOL r = ReadFile(hFile, szFile, dwSize, &dwRead, nullptr);
- CloseHandle(hFile);
- if (!r)
- return EGROKPRF_CANTREAD;
-
- szFile[dwSize] = 0;
- if ((m_root = json_parse(szFile)) == nullptr)
- return EGROKPRF_DAMAGED;
-
- for (auto &it : m_root->at("history"))
- m_events.insert(&it);
-
- return EGROKPRF_NOERROR;
- }
-
- // mcontacts format always store history for one contact only
- STDMETHODIMP_(int) GetContactCount(void) override
- {
- return 1;
- }
-
- STDMETHODIMP_(int) GetEventCount(MCONTACT) override
- {
- return m_events.getCount();
- }
-
- STDMETHODIMP_(BOOL) GetEvent(MEVENT iEvent, DBEVENTINFO *dbei) override
- {
- JSONNode *node = m_events[iEvent - 1];
- if (node == nullptr)
- return 0;
-
- dbei->eventType = (*node)["type"].as_int();
-
- dbei->timestamp = 0;
- std::string szTime = (*node)["time"].as_string();
- if (!szTime.empty()) {
- char c;
- struct tm st = {};
- int res = sscanf(szTime.c_str(), "%4d%c%2d%c%2d %2d:%2d:%2d", &st.tm_year, &c, &st.tm_mon, &c, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec);
- if (res == 8) {
- st.tm_mon--;
- st.tm_year -= 1900;
- time_t tm = mktime(&st);
- if (tm != -1)
- dbei->timestamp = tm;
- }
- }
- else {
- szTime = (*node)["isotime"].as_string();
- if (!szTime.empty()) {
- struct tm st = {};
- int res = sscanf(szTime.c_str(), "%4d-%2d-%2dT%2d:%2d:%2dZ", &st.tm_year, &st.tm_mon, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec);
- if (res == 6) {
- st.tm_mon--;
- st.tm_year -= 1900;
- time_t tm = _mkgmtime(&st);
- if (tm != -1)
- dbei->timestamp = tm;
- }
- }
- }
-
- if (dbei->timestamp == 0)
- dbei->timestamp = (*node)["timeStamp"].as_int();
-
- dbei->flags = 0;
- std::string szFlags = (*node)["flags"].as_string();
- for (auto &c : szFlags)
- switch (c) {
- case 'm': dbei->flags |= DBEF_SENT; break;
- case 'r': dbei->flags |= DBEF_READ; break;
- }
-
- std::string szModule = (*node)["module"].as_string();
- if (!szModule.empty()) {
- dbei->szModule = m_modules.find((char*)szModule.c_str());
- if (dbei->szModule == nullptr) {
- dbei->szModule = mir_strdup(szModule.c_str());
- m_modules.insert((char*)dbei->szModule);
- }
- }
-
- if (dbei->eventType == EVENTTYPE_FILE) {
- std::string szFile = (*node)["file"].as_string();
- std::string szDescr = (*node)["descr"].as_string();
-
- dbei->flags |= DBEF_UTF;
- MBinBuffer buf;
- uint32_t tmp = 0;
- buf.append(&tmp, sizeof(tmp));
- buf.append(szFile.c_str(), szFile.size());
- if (!szDescr.empty()) {
- buf.append(&tmp, 1);
- buf.append(szDescr.c_str(), szDescr.size());
- }
- buf.append(&tmp, 1);
-
- dbei->cbBlob = (int)buf.length();
- dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob);
- memcpy(dbei->pBlob, buf.data(), buf.length());
- }
- else {
- std::string szBody = (*node)["body"].as_string();
- if (!szBody.empty()) {
- int offset;
- switch (dbei->eventType) {
- case EVENTTYPE_ADDED:
- case EVENTTYPE_FILE:
- offset = sizeof(uint32_t);
- break;
-
- case EVENTTYPE_AUTHREQUEST:
- offset = sizeof(uint32_t) * 2;
- break;
-
- default:
- offset = 0;
- }
-
- dbei->flags |= DBEF_UTF;
- dbei->cbBlob = (uint32_t)szBody.size() + offset;
- dbei->pBlob = (uint8_t*)mir_calloc(dbei->cbBlob + 1);
- memcpy(dbei->pBlob + offset, szBody.c_str(), szBody.size());
- dbei->pBlob[dbei->cbBlob] = 0;
- }
- }
-
- return 0;
- }
-
- STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override
- {
- return 1;
- }
-
- STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT iEvent) override
- {
- if ((int)iEvent >= m_events.getCount())
- return 0;
-
- return iEvent+1;
- }
-
- STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override
- {
- int numEvents = m_events.getCount();
- return numEvents ? numEvents-1 : 0;
- }
-
- STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT iEvent) override
- {
- if (iEvent <= 1)
- return 0;
-
- return iEvent-1;
- }
-
- STDMETHODIMP_(DATABASELINK *) GetDriver();
-};
-
-static int mc_grokHeader(const wchar_t *profile)
-{
- return CDbxJson().Open(profile);
-}
-
-static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL)
-{
- std::unique_ptr<CDbxJson> db(new CDbxJson());
- if (db->Open(profile))
- return nullptr;
-
- db->Load();
- return db.release();
-}
-
-static DATABASELINK dblink =
-{
- 0,
- "mcontacts",
- L"mContacts file driver",
- mc_makeDatabase,
- mc_grokHeader,
- mc_load
-};
-
-STDMETHODIMP_(DATABASELINK *) CDbxJson::GetDriver()
-{
- return &g_patternDbLink;
-}
-
-void RegisterJson()
-{
- RegisterDatabasePlugin(&dblink);
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+#include <m_json.h>
+
+static int mc_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// JSON text driver, read-only
+
+static int CompareModules(const char *p1, const char *p2)
+{
+ return mir_strcmp(p1, p2);
+}
+
+class CDbxJson : public MDatabaseReadonly, public MZeroedObject
+{
+ JSONNode *m_root = nullptr;
+ LIST<JSONNode> m_events;
+ LIST<char> m_modules;
+
+public:
+ CDbxJson() :
+ m_events(100),
+ m_modules(10, CompareModules)
+ {}
+
+ ~CDbxJson()
+ {
+ if (m_root != nullptr)
+ json_delete(m_root);
+
+ for (auto &it : m_modules)
+ mir_free(it);
+ }
+
+ void Load()
+ {
+ // json operates with the only contact with pseudo id=1
+ m_cache->AddContactToCache(1);
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ HANDLE hFile = CreateFile(profile, GENERIC_READ, 0, 0, OPEN_ALWAYS, 0, 0);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return EGROKPRF_CANTREAD;
+
+ DWORD dwSize = GetFileSize(hFile, nullptr), dwRead;
+ ptrA szFile((char*)mir_alloc(dwSize + 1));
+ BOOL r = ReadFile(hFile, szFile, dwSize, &dwRead, nullptr);
+ CloseHandle(hFile);
+ if (!r)
+ return EGROKPRF_CANTREAD;
+
+ szFile[dwSize] = 0;
+ if ((m_root = json_parse(szFile)) == nullptr)
+ return EGROKPRF_DAMAGED;
+
+ for (auto &it : m_root->at("history"))
+ m_events.insert(&it);
+
+ return EGROKPRF_NOERROR;
+ }
+
+ // mcontacts format always store history for one contact only
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return 1;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return m_events.getCount();
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT iEvent, DBEVENTINFO *dbei) override
+ {
+ JSONNode *node = m_events[iEvent - 1];
+ if (node == nullptr)
+ return 0;
+
+ dbei->eventType = (*node)["type"].as_int();
+
+ dbei->timestamp = 0;
+ std::string szTime = (*node)["time"].as_string();
+ if (!szTime.empty()) {
+ char c;
+ struct tm st = {};
+ int res = sscanf(szTime.c_str(), "%4d%c%2d%c%2d %2d:%2d:%2d", &st.tm_year, &c, &st.tm_mon, &c, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec);
+ if (res == 8) {
+ st.tm_mon--;
+ st.tm_year -= 1900;
+ time_t tm = mktime(&st);
+ if (tm != -1)
+ dbei->timestamp = tm;
+ }
+ }
+ else {
+ szTime = (*node)["isotime"].as_string();
+ if (!szTime.empty()) {
+ struct tm st = {};
+ int res = sscanf(szTime.c_str(), "%4d-%2d-%2dT%2d:%2d:%2dZ", &st.tm_year, &st.tm_mon, &st.tm_mday, &st.tm_hour, &st.tm_min, &st.tm_sec);
+ if (res == 6) {
+ st.tm_mon--;
+ st.tm_year -= 1900;
+ time_t tm = _mkgmtime(&st);
+ if (tm != -1)
+ dbei->timestamp = tm;
+ }
+ }
+ }
+
+ if (dbei->timestamp == 0)
+ dbei->timestamp = (*node)["timeStamp"].as_int();
+
+ dbei->flags = 0;
+ std::string szFlags = (*node)["flags"].as_string();
+ for (auto &c : szFlags)
+ switch (c) {
+ case 'm': dbei->flags |= DBEF_SENT; break;
+ case 'r': dbei->flags |= DBEF_READ; break;
+ }
+
+ std::string szModule = (*node)["module"].as_string();
+ if (!szModule.empty()) {
+ dbei->szModule = m_modules.find((char*)szModule.c_str());
+ if (dbei->szModule == nullptr) {
+ dbei->szModule = mir_strdup(szModule.c_str());
+ m_modules.insert((char*)dbei->szModule);
+ }
+ }
+
+ if (dbei->eventType == EVENTTYPE_FILE) {
+ std::string szFile = (*node)["file"].as_string();
+ std::string szDescr = (*node)["descr"].as_string();
+
+ dbei->flags |= DBEF_UTF;
+ MBinBuffer buf;
+ uint32_t tmp = 0;
+ buf.append(&tmp, sizeof(tmp));
+ buf.append(szFile.c_str(), szFile.size());
+ if (!szDescr.empty()) {
+ buf.append(&tmp, 1);
+ buf.append(szDescr.c_str(), szDescr.size());
+ }
+ buf.append(&tmp, 1);
+
+ dbei->cbBlob = (int)buf.length();
+ dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob);
+ memcpy(dbei->pBlob, buf.data(), buf.length());
+ }
+ else {
+ std::string szBody = (*node)["body"].as_string();
+ if (!szBody.empty()) {
+ int offset;
+ switch (dbei->eventType) {
+ case EVENTTYPE_ADDED:
+ case EVENTTYPE_FILE:
+ offset = sizeof(uint32_t);
+ break;
+
+ case EVENTTYPE_AUTHREQUEST:
+ offset = sizeof(uint32_t) * 2;
+ break;
+
+ default:
+ offset = 0;
+ }
+
+ dbei->flags |= DBEF_UTF;
+ dbei->cbBlob = (uint32_t)szBody.size() + offset;
+ dbei->pBlob = (uint8_t*)mir_calloc(dbei->cbBlob + 1);
+ memcpy(dbei->pBlob + offset, szBody.c_str(), szBody.size());
+ dbei->pBlob[dbei->cbBlob] = 0;
+ }
+ }
+
+ return 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT) override
+ {
+ return 1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT iEvent) override
+ {
+ if ((int)iEvent >= m_events.getCount())
+ return 0;
+
+ return iEvent+1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT) override
+ {
+ int numEvents = m_events.getCount();
+ return numEvents ? numEvents-1 : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT iEvent) override
+ {
+ if (iEvent <= 1)
+ return 0;
+
+ return iEvent-1;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
+
+static int mc_grokHeader(const wchar_t *profile)
+{
+ return CDbxJson().Open(profile);
+}
+
+static MDatabaseCommon* mc_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxJson> db(new CDbxJson());
+ if (db->Open(profile))
+ return nullptr;
+
+ db->Load();
+ return db.release();
+}
+
+static DATABASELINK dblink =
+{
+ 0,
+ "mcontacts",
+ L"mContacts file driver",
+ mc_makeDatabase,
+ mc_grokHeader,
+ mc_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxJson::GetDriver()
+{
+ return &g_patternDbLink;
+}
+
+void RegisterJson()
+{
+ RegisterDatabasePlugin(&dblink);
+}
diff --git a/plugins/Import/src/ui.cpp b/plugins/Import/src/ui.cpp
index 12a84455c7..0a5d6cf4e1 100644
--- a/plugins/Import/src/ui.cpp
+++ b/plugins/Import/src/ui.cpp
@@ -1,130 +1,130 @@
-/*
-
-Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include "stdafx.h"
-
-class CContactImportDlg : public CDlgBase
-{
- friend INT_PTR ImportContact(WPARAM hContact, LPARAM);
-
- MCONTACT m_hContact;
- int m_flags = 0;
- CImportPattern *m_pPattern = 0;
- wchar_t m_wszFileName[MAX_PATH];
-
- CCtrlButton m_btnOpenFile;
- CCtrlCombo m_cmbFileType;
- CCtrlEdit edtFileName;
-
-public:
- CContactImportDlg(MCONTACT hContact) :
- CDlgBase(g_plugin, IDD_IMPORT_CONTACT),
- m_hContact(hContact),
- edtFileName(this, IDC_FILENAME),
- m_cmbFileType(this, IDC_FILETYPE),
- m_btnOpenFile(this, IDC_OPEN_FILE)
- {
- m_wszFileName[0] = 0;
-
- m_btnOpenFile.OnClick = Callback(this, &CContactImportDlg::onClick_OpenFile);
- }
-
- bool OnInitDialog() override
- {
- CMStringW wszTitle(FORMAT, TranslateT("Import history for %s"), Clist_GetContactDisplayName(m_hContact));
- SetWindowTextW(m_hwnd, wszTitle);
-
- m_cmbFileType.AddString(TranslateT("Miranda NG database/mContacts"), -1);
- m_cmbFileType.AddString(TranslateT("JSON file"), -2);
-
- int iType = 1;
- for (auto &it : g_plugin.m_patterns)
- m_cmbFileType.AddString(it->wszName, iType++);
-
- return true;
- }
-
- bool OnApply() override
- {
- edtFileName.GetText(m_wszFileName, _countof(m_wszFileName));
- if (m_wszFileName[0] == 0)
- return false;
-
- if (IsDlgButtonChecked(m_hwnd, IDC_CHECK_DUPS))
- m_flags = IOPT_CHECKDUPS;
- return true;
- }
-
- void onClick_OpenFile(CCtrlButton*)
- {
- int iCur = m_cmbFileType.GetCurSel();
- if (iCur == -1)
- return;
-
- CMStringW text, cmbText(ptrW(m_cmbFileType.GetText()));
- switch(int idx = m_cmbFileType.GetItemData(iCur)) {
- case -1:
- text.AppendFormat(L"%s (*.dat,*.bak)%c*.dat;*.bak%c", cmbText.c_str(), 0, 0);
- m_pPattern = nullptr;
- break;
-
- case -2:
- text.AppendFormat(L"%s (*.json)%c*.json%c", cmbText.c_str(), 0, 0);
- m_pPattern = nullptr;
- break;
-
- default:
- auto &p = g_plugin.m_patterns[idx-1];
- text.AppendFormat(L"%s (*.%s)%c*.%s%c", cmbText.c_str(), p.wszExt.c_str(), 0, p.wszExt.c_str(), 0);
- m_pPattern = &p;
- break;
- }
- text.AppendFormat(L"%s (*.*)%c*.*%c%c", TranslateT("All Files"), 0, 0, 0);
-
- OPENFILENAME ofn = { 0 };
- ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
- ofn.lpstrFilter = text;
- ofn.lpstrDefExt = L"dat";
- ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT;
- ofn.lpstrFile = m_wszFileName;
- ofn.nMaxFile = _countof(m_wszFileName);
- if (!GetOpenFileNameW(&ofn)) {
- m_wszFileName[0] = 0;
- m_pPattern = nullptr;
- }
- else edtFileName.SetText(m_wszFileName);
- }
-};
-
-INT_PTR ImportContact(WPARAM hContact, LPARAM)
-{
- CContactImportDlg dlg(hContact);
- if (!dlg.DoModal())
- return 0;
-
- g_pBatch = new CImportBatch();
- wcsncpy_s(g_pBatch->m_wszFileName, dlg.m_wszFileName, _TRUNCATE);
- g_pBatch->m_pPattern = dlg.m_pPattern;
- g_pBatch->m_hContact = hContact;
- g_pBatch->m_iOptions = IOPT_HISTORY + dlg.m_flags;
- return RunWizard(new CProgressPageDlg(), true);
-}
+/*
+
+Import 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "stdafx.h"
+
+class CContactImportDlg : public CDlgBase
+{
+ friend INT_PTR ImportContact(WPARAM hContact, LPARAM);
+
+ MCONTACT m_hContact;
+ int m_flags = 0;
+ CImportPattern *m_pPattern = 0;
+ wchar_t m_wszFileName[MAX_PATH];
+
+ CCtrlButton m_btnOpenFile;
+ CCtrlCombo m_cmbFileType;
+ CCtrlEdit edtFileName;
+
+public:
+ CContactImportDlg(MCONTACT hContact) :
+ CDlgBase(g_plugin, IDD_IMPORT_CONTACT),
+ m_hContact(hContact),
+ edtFileName(this, IDC_FILENAME),
+ m_cmbFileType(this, IDC_FILETYPE),
+ m_btnOpenFile(this, IDC_OPEN_FILE)
+ {
+ m_wszFileName[0] = 0;
+
+ m_btnOpenFile.OnClick = Callback(this, &CContactImportDlg::onClick_OpenFile);
+ }
+
+ bool OnInitDialog() override
+ {
+ CMStringW wszTitle(FORMAT, TranslateT("Import history for %s"), Clist_GetContactDisplayName(m_hContact));
+ SetWindowTextW(m_hwnd, wszTitle);
+
+ m_cmbFileType.AddString(TranslateT("Miranda NG database/mContacts"), -1);
+ m_cmbFileType.AddString(TranslateT("JSON file"), -2);
+
+ int iType = 1;
+ for (auto &it : g_plugin.m_patterns)
+ m_cmbFileType.AddString(it->wszName, iType++);
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ edtFileName.GetText(m_wszFileName, _countof(m_wszFileName));
+ if (m_wszFileName[0] == 0)
+ return false;
+
+ if (IsDlgButtonChecked(m_hwnd, IDC_CHECK_DUPS))
+ m_flags = IOPT_CHECKDUPS;
+ return true;
+ }
+
+ void onClick_OpenFile(CCtrlButton*)
+ {
+ int iCur = m_cmbFileType.GetCurSel();
+ if (iCur == -1)
+ return;
+
+ CMStringW text, cmbText(ptrW(m_cmbFileType.GetText()));
+ switch(int idx = m_cmbFileType.GetItemData(iCur)) {
+ case -1:
+ text.AppendFormat(L"%s (*.dat,*.bak)%c*.dat;*.bak%c", cmbText.c_str(), 0, 0);
+ m_pPattern = nullptr;
+ break;
+
+ case -2:
+ text.AppendFormat(L"%s (*.json)%c*.json%c", cmbText.c_str(), 0, 0);
+ m_pPattern = nullptr;
+ break;
+
+ default:
+ auto &p = g_plugin.m_patterns[idx-1];
+ text.AppendFormat(L"%s (*.%s)%c*.%s%c", cmbText.c_str(), p.wszExt.c_str(), 0, p.wszExt.c_str(), 0);
+ m_pPattern = &p;
+ break;
+ }
+ text.AppendFormat(L"%s (*.*)%c*.*%c%c", TranslateT("All Files"), 0, 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.lpstrFilter = text;
+ ofn.lpstrDefExt = L"dat";
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_DONTADDTORECENT;
+ ofn.lpstrFile = m_wszFileName;
+ ofn.nMaxFile = _countof(m_wszFileName);
+ if (!GetOpenFileNameW(&ofn)) {
+ m_wszFileName[0] = 0;
+ m_pPattern = nullptr;
+ }
+ else edtFileName.SetText(m_wszFileName);
+ }
+};
+
+INT_PTR ImportContact(WPARAM hContact, LPARAM)
+{
+ CContactImportDlg dlg(hContact);
+ if (!dlg.DoModal())
+ return 0;
+
+ g_pBatch = new CImportBatch();
+ wcsncpy_s(g_pBatch->m_wszFileName, dlg.m_wszFileName, _TRUNCATE);
+ g_pBatch->m_pPattern = dlg.m_pPattern;
+ g_pBatch->m_hContact = hContact;
+ g_pBatch->m_iOptions = IOPT_HISTORY + dlg.m_flags;
+ return RunWizard(new CProgressPageDlg(), true);
+}
diff --git a/plugins/Import/src/utils.cpp b/plugins/Import/src/utils.cpp
index 3c83bf11a9..d869ae2737 100644
--- a/plugins/Import/src/utils.cpp
+++ b/plugins/Import/src/utils.cpp
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/Import/src/version.h b/plugins/Import/src/version.h
index d34c120f7d..f76dfe14a3 100644
--- a/plugins/Import/src/version.h
+++ b/plugins/Import/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Imports contacts and messages from another Miranda profile or from an external program."
#define __AUTHOR "Miranda team"
#define __AUTHORWEB "https://miranda-ng.org/p/Import"
-#define __COPYRIGHT "© 2012-22 George Hazan"
+#define __COPYRIGHT "© 2012-23 George Hazan"
diff --git a/plugins/Import/src/wizard.cpp b/plugins/Import/src/wizard.cpp
index aab7e802a7..dde120289e 100644
--- a/plugins/Import/src/wizard.cpp
+++ b/plugins/Import/src/wizard.cpp
@@ -2,7 +2,7 @@
Import 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
diff --git a/plugins/KeyboardNotify/src/stdafx.cxx b/plugins/KeyboardNotify/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/KeyboardNotify/src/stdafx.cxx
+++ b/plugins/KeyboardNotify/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/plugins/ListeningTo/src/stdafx.cxx b/plugins/ListeningTo/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ListeningTo/src/stdafx.cxx
+++ b/plugins/ListeningTo/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/plugins/MagneticWindows/src/Version.h b/plugins/MagneticWindows/src/Version.h
index 6a45b1f20d..11635c5508 100644
--- a/plugins/MagneticWindows/src/Version.h
+++ b/plugins/MagneticWindows/src/Version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Makes the contact list and the chat windows snapping to the desktop border and to each other."
#define __AUTHOR "Michael Kunz"
#define __AUTHORWEB "https://miranda-ng.org/p/MagneticWindows"
-#define __COPYRIGHT " 2006 Michael Kunz, 2018-22 Miranda NG team"
+#define __COPYRIGHT " 2006 Michael Kunz, 2018-23 Miranda NG team"
diff --git a/plugins/MagneticWindows/src/stdafx.cxx b/plugins/MagneticWindows/src/stdafx.cxx
index f9c009e3f9..35f341c48b 100644
--- a/plugins/MagneticWindows/src/stdafx.cxx
+++ b/plugins/MagneticWindows/src/stdafx.cxx
@@ -1,18 +1,18 @@
-/*
-Copyright (C) 2012-22 Miranda NG project (http://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"
+/*
+Copyright (C) 2012-23 Miranda NG project (http://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"
diff --git a/plugins/MenuItemEx/src/stdafx.cxx b/plugins/MenuItemEx/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/MenuItemEx/src/stdafx.cxx
+++ b/plugins/MenuItemEx/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/plugins/MessageState/src/stdafx.cxx b/plugins/MessageState/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/MessageState/src/stdafx.cxx
+++ b/plugins/MessageState/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/plugins/MessageState/src/version.h b/plugins/MessageState/src/version.h
index 479f6cbe5b..c3caed1956 100644
--- a/plugins/MessageState/src/version.h
+++ b/plugins/MessageState/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Displays icons in message window showing whether your last outgoing message was read / is still unread."
#define __AUTHOR "MikalaiR"
#define __AUTHORWEB "https://miranda-ng.org/p/MessageState"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/MimCmd/src/stdafx.cxx b/plugins/MimCmd/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/MimCmd/src/stdafx.cxx
+++ b/plugins/MimCmd/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/plugins/MirFox/src/version.h b/plugins/MirFox/src/version.h
index 1ba06c8d39..0b447a7d3f 100644
--- a/plugins/MirFox/src/version.h
+++ b/plugins/MirFox/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "MirFox (Miranda NG) - part of Miranda-Firefox integration - http://wsx22.3.xpdev-hosted.com"
#define __AUTHOR "Szymon Tokarz"
#define __AUTHORWEB "https://miranda-ng.org/p/MirFox"
-#define __COPYRIGHT "© 2013-22 Szymon Tokarz"
+#define __COPYRIGHT "© 2013-23 Szymon Tokarz"
diff --git a/plugins/MirLua/src/version.h b/plugins/MirLua/src/version.h
index 6f9bf7d623..84527ae5c7 100644
--- a/plugins/MirLua/src/version.h
+++ b/plugins/MirLua/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Extends Miranda NG functionality with Lua scripts."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/MirLua"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/MirandaG15/src/stdafx.cxx b/plugins/MirandaG15/src/stdafx.cxx
index 4f32cda1a4..4f135bc766 100644
--- a/plugins/MirandaG15/src/stdafx.cxx
+++ b/plugins/MirandaG15/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/plugins/MobileState/src/clients.h b/plugins/MobileState/src/clients.h
index f1740cbc64..2b0d6aebdb 100644
--- a/plugins/MobileState/src/clients.h
+++ b/plugins/MobileState/src/clients.h
@@ -1,6 +1,6 @@
/*
Mobile State plugin for Miranda NG (www.miranda-ng.org)
- (c) 2012-17 Robert Pösel, 2017-22 Miranda NG team
+ (c) 2012-17 Robert Pösel, 2017-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
diff --git a/plugins/MobileState/src/main.cpp b/plugins/MobileState/src/main.cpp
index fe479ee0d8..8a63a8eaa4 100644
--- a/plugins/MobileState/src/main.cpp
+++ b/plugins/MobileState/src/main.cpp
@@ -1,6 +1,6 @@
/*
Mobile State plugin for Miranda NG (www.miranda-ng.org)
- (c) 2012-17 by Robert Pösel, 2017-22 Miranda NG team
+ (c) 2012-17 by Robert Pösel, 2017-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
diff --git a/plugins/MobileState/src/stdafx.cxx b/plugins/MobileState/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/MobileState/src/stdafx.cxx
+++ b/plugins/MobileState/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/plugins/MobileState/src/stdafx.h b/plugins/MobileState/src/stdafx.h
index f3581f06b2..7eb4d261f7 100644
--- a/plugins/MobileState/src/stdafx.h
+++ b/plugins/MobileState/src/stdafx.h
@@ -1,6 +1,6 @@
/*
Mobile State plugin for Miranda NG (www.miranda-ng.org)
- (c) 2012-17 by Robert Pösel, 2017-22 Miranda NG team
+ (c) 2012-17 by Robert Pösel, 2017-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
diff --git a/plugins/MobileState/src/version.h b/plugins/MobileState/src/version.h
index 10e440da90..85c8802b95 100644
--- a/plugins/MobileState/src/version.h
+++ b/plugins/MobileState/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Plugin shows mobile icon in contact list next to contacts which are using mobile client."
#define __AUTHOR "Robert Pösel"
#define __AUTHORWEB "https://miranda-ng.org/p/MobileState"
-#define __COPYRIGHT "© 2012-17 Robert Pösel, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2012-17 Robert Pösel, 2017-23 Miranda NG team"
diff --git a/plugins/MsgPopup/src/stdafx.cxx b/plugins/MsgPopup/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/MsgPopup/src/stdafx.cxx
+++ b/plugins/MsgPopup/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/plugins/Msg_Export/src/stdafx.cxx b/plugins/Msg_Export/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Msg_Export/src/stdafx.cxx
+++ b/plugins/Msg_Export/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/plugins/Msg_Export/src/version.h b/plugins/Msg_Export/src/version.h
index f260612881..85d9f8cfe4 100644
--- a/plugins/Msg_Export/src/version.h
+++ b/plugins/Msg_Export/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Exports every message, URL or file you receive to a text file."
#define __AUTHOR "Kennet Nielsen, mod by ring0"
#define __AUTHORWEB "https://miranda-ng.org/p/Msg_Export"
-#define __COPYRIGHT "© 2002 Kennet Nielsen, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2002 Kennet Nielsen, 2012-23 Miranda NG team"
diff --git a/plugins/MyDetails/src/stdafx.cxx b/plugins/MyDetails/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/MyDetails/src/stdafx.cxx
+++ b/plugins/MyDetails/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/plugins/NewAwaySysMod/src/stdafx.cxx b/plugins/NewAwaySysMod/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/NewAwaySysMod/src/stdafx.cxx
+++ b/plugins/NewAwaySysMod/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/plugins/NewEventNotify/src/stdafx.cxx b/plugins/NewEventNotify/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/NewEventNotify/src/stdafx.cxx
+++ b/plugins/NewEventNotify/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/plugins/NewStory/src/stdafx.cxx b/plugins/NewStory/src/stdafx.cxx
index 1ab0efee94..ebbde0ade1 100644
--- a/plugins/NewStory/src/stdafx.cxx
+++ b/plugins/NewStory/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/plugins/NewStory/src/version.h b/plugins/NewStory/src/version.h
index c6cdac6bb4..9be84bdbe7 100644
--- a/plugins/NewStory/src/version.h
+++ b/plugins/NewStory/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "History viewer for Miranda NG."
#define __AUTHOR "nullbie"
#define __AUTHORWEB "https://miranda-ng.org/p/NewStory"
-#define __COPYRIGHT "© 2005 Victor Pavlychko, 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2005 Victor Pavlychko, 2018-23 Miranda NG team"
diff --git a/plugins/NewXstatusNotify/src/stdafx.cxx b/plugins/NewXstatusNotify/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/NewXstatusNotify/src/stdafx.cxx
+++ b/plugins/NewXstatusNotify/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/plugins/New_GPG/src/globals.h b/plugins/New_GPG/src/globals.h
index 02b1607b6d..ac64da8ecd 100644
--- a/plugins/New_GPG/src/globals.h
+++ b/plugins/New_GPG/src/globals.h
@@ -1,42 +1,42 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef GLOBALS_H
-#define GLOBALS_H
-
-struct contact_data
-{
- list<string> msgs_to_send;// msgs_to_pass;
- string key_in_prescense;
-};
-
-struct globals_s
-{
- bool bDecryptFiles = false;
- CMStringW wszInopentag, wszInclosetag, wszOutopentag, wszOutclosetag, wszPassword;
- wchar_t key_id_global[17] = { 0 };
- list <JabberAccount*> Accounts;
- HFONT bold_font = nullptr;
- logtofile debuglog;
- bool gpg_valid = false, gpg_keyexist = false;
- std::map<MCONTACT, contact_data> hcontact_data;
- bool _terminate;
-};
-
-extern globals_s globals;
-
-
-#endif
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+struct contact_data
+{
+ list<string> msgs_to_send;// msgs_to_pass;
+ string key_in_prescense;
+};
+
+struct globals_s
+{
+ bool bDecryptFiles = false;
+ CMStringW wszInopentag, wszInclosetag, wszOutopentag, wszOutclosetag, wszPassword;
+ wchar_t key_id_global[17] = { 0 };
+ list <JabberAccount*> Accounts;
+ HFONT bold_font = nullptr;
+ logtofile debuglog;
+ bool gpg_valid = false, gpg_keyexist = false;
+ std::map<MCONTACT, contact_data> hcontact_data;
+ bool _terminate;
+};
+
+extern globals_s globals;
+
+
+#endif
diff --git a/plugins/New_GPG/src/gpg_wrapper.cpp b/plugins/New_GPG/src/gpg_wrapper.cpp
index 6b719e1e5e..8d12134c7c 100644
--- a/plugins/New_GPG/src/gpg_wrapper.cpp
+++ b/plugins/New_GPG/src/gpg_wrapper.cpp
@@ -1,168 +1,168 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include "stdafx.h"
-
-namespace bp = boost::process;
-
-gpg_execution_params::gpg_execution_params()
-{ }
-
-gpg_execution_params::~gpg_execution_params()
-{ }
-
-void pxEexcute_thread(gpg_execution_params *params)
-{
- if (!globals.gpg_valid)
- return;
-
- CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath"));
- if (_waccess(bin_path, 0)) {
- if (globals.debuglog)
- globals.debuglog << "GPG executable not found";
- params->result = pxNotFound;
- return;
- }
-
- bp::environment env = boost::this_process::environment();
- env.set("LANGUAGE", "en@quot");
- env.set("LC_ALL", "English");
- env.set("LANG", "C");
-
- std::vector<std::wstring> argv;
- CMStringW home_dir(g_plugin.getMStringW("szHomePath"));
- if (!home_dir.IsEmpty()) { // this check are required for first run gpg binary validation
- argv.push_back(L"--homedir");
- argv.push_back(home_dir.c_str());
- }
-
- argv.push_back(L"--display-charset");
- argv.push_back(L"utf-8");
- argv.push_back(L"-z9");
- argv.insert(argv.end(), params->aargv.begin(), params->aargv.end());
-
- if (globals.debuglog) {
- std::wstring args;
- for (unsigned int i = 0; i < argv.size(); ++i) {
- args += argv[i];
- args += L" ";
- }
- args.erase(args.size() - 1, 1);
-
- globals.debuglog << "gpg in: " << toUTF8(args);
- }
-
- params->out.Empty();
-
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
-
- bp::child *c;
- std::future<std::string> pout, perr;
- boost::asio::io_context ios;
- if (params->bNoOutput)
- c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), ios);
- else
- c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), bp::std_out > pout, bp::std_err > perr, ios);
-
- params->child = c;
-
- ios.run();
- c->wait();
-
- if (!params->bNoOutput) {
- std::string s = pout.get();
- if (!s.empty())
- params->out.Append(s.c_str());
-
- s = perr.get();
- if (!s.empty())
- params->out.Append(s.c_str());
-
- params->out.Replace("\r\n", "\n");
- params->out.Replace("\r\r", "");
-
- if (globals.debuglog)
- globals.debuglog << "gpg out: " << params->out.c_str();
- }
-
- params->child = nullptr;
- params->code = c->exit_code();
- delete c;
-
- if (params->code) {
- if (globals.debuglog)
- globals.debuglog << ": warning: wrong gpg exit status, gpg output: " << params->out.c_str();
- params->result = pxSuccessExitCodeInvalid;
- }
- else params->result = pxSuccess;
-}
-
-bool gpg_launcher(gpg_execution_params &params, boost::posix_time::time_duration t)
-{
- bool ret = true;
- HANDLE hThread = mir_forkThread<gpg_execution_params>(pxEexcute_thread, &params);
- if (WaitForSingleObject(hThread, t.total_milliseconds()) == WAIT_TIMEOUT) {
- ret = false;
- if (params.child)
- params.child->terminate();
- if (globals.debuglog)
- globals.debuglog << "GPG execution timed out, aborted";
- }
- return ret;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void pxEexcute_passwd_change_thread(gpg_execution_params_pass *params)
-{
- if (!globals.gpg_valid) {
- params->result = pxNotConfigured;
- return;
- }
-
- CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath"));
- if (_waccess(bin_path, 0)) {
- if (globals.debuglog)
- globals.debuglog << "GPG executable not found";
- params->result = pxNotFound;
- return;
- }
-
- bp::environment env = boost::this_process::environment();
- env.set("LANGUAGE", "en@quot");
- env.set("LC_ALL", "English");
-
- std::vector<std::wstring> argv;
-
- argv.push_back(bin_path.c_str());
- argv.push_back(L"--homedir");
- argv.push_back(g_plugin.getMStringW("szHomePath").c_str());
- argv.push_back(L"--display-charset");
- argv.push_back(L"utf-8");
- argv.push_back(L"-z9");
- argv.insert(argv.end(), params->aargv.begin(), params->aargv.end());
-
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
-
- bp::child c(bin_path.c_str(), argv, env, boost::process::windows::hide);
- params->child = &c;
-
- c.wait();
-
- params->child = nullptr;
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+namespace bp = boost::process;
+
+gpg_execution_params::gpg_execution_params()
+{ }
+
+gpg_execution_params::~gpg_execution_params()
+{ }
+
+void pxEexcute_thread(gpg_execution_params *params)
+{
+ if (!globals.gpg_valid)
+ return;
+
+ CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath"));
+ if (_waccess(bin_path, 0)) {
+ if (globals.debuglog)
+ globals.debuglog << "GPG executable not found";
+ params->result = pxNotFound;
+ return;
+ }
+
+ bp::environment env = boost::this_process::environment();
+ env.set("LANGUAGE", "en@quot");
+ env.set("LC_ALL", "English");
+ env.set("LANG", "C");
+
+ std::vector<std::wstring> argv;
+ CMStringW home_dir(g_plugin.getMStringW("szHomePath"));
+ if (!home_dir.IsEmpty()) { // this check are required for first run gpg binary validation
+ argv.push_back(L"--homedir");
+ argv.push_back(home_dir.c_str());
+ }
+
+ argv.push_back(L"--display-charset");
+ argv.push_back(L"utf-8");
+ argv.push_back(L"-z9");
+ argv.insert(argv.end(), params->aargv.begin(), params->aargv.end());
+
+ if (globals.debuglog) {
+ std::wstring args;
+ for (unsigned int i = 0; i < argv.size(); ++i) {
+ args += argv[i];
+ args += L" ";
+ }
+ args.erase(args.size() - 1, 1);
+
+ globals.debuglog << "gpg in: " << toUTF8(args);
+ }
+
+ params->out.Empty();
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+
+ bp::child *c;
+ std::future<std::string> pout, perr;
+ boost::asio::io_context ios;
+ if (params->bNoOutput)
+ c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), ios);
+ else
+ c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), bp::std_out > pout, bp::std_err > perr, ios);
+
+ params->child = c;
+
+ ios.run();
+ c->wait();
+
+ if (!params->bNoOutput) {
+ std::string s = pout.get();
+ if (!s.empty())
+ params->out.Append(s.c_str());
+
+ s = perr.get();
+ if (!s.empty())
+ params->out.Append(s.c_str());
+
+ params->out.Replace("\r\n", "\n");
+ params->out.Replace("\r\r", "");
+
+ if (globals.debuglog)
+ globals.debuglog << "gpg out: " << params->out.c_str();
+ }
+
+ params->child = nullptr;
+ params->code = c->exit_code();
+ delete c;
+
+ if (params->code) {
+ if (globals.debuglog)
+ globals.debuglog << ": warning: wrong gpg exit status, gpg output: " << params->out.c_str();
+ params->result = pxSuccessExitCodeInvalid;
+ }
+ else params->result = pxSuccess;
+}
+
+bool gpg_launcher(gpg_execution_params &params, boost::posix_time::time_duration t)
+{
+ bool ret = true;
+ HANDLE hThread = mir_forkThread<gpg_execution_params>(pxEexcute_thread, &params);
+ if (WaitForSingleObject(hThread, t.total_milliseconds()) == WAIT_TIMEOUT) {
+ ret = false;
+ if (params.child)
+ params.child->terminate();
+ if (globals.debuglog)
+ globals.debuglog << "GPG execution timed out, aborted";
+ }
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void pxEexcute_passwd_change_thread(gpg_execution_params_pass *params)
+{
+ if (!globals.gpg_valid) {
+ params->result = pxNotConfigured;
+ return;
+ }
+
+ CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath"));
+ if (_waccess(bin_path, 0)) {
+ if (globals.debuglog)
+ globals.debuglog << "GPG executable not found";
+ params->result = pxNotFound;
+ return;
+ }
+
+ bp::environment env = boost::this_process::environment();
+ env.set("LANGUAGE", "en@quot");
+ env.set("LC_ALL", "English");
+
+ std::vector<std::wstring> argv;
+
+ argv.push_back(bin_path.c_str());
+ argv.push_back(L"--homedir");
+ argv.push_back(g_plugin.getMStringW("szHomePath").c_str());
+ argv.push_back(L"--display-charset");
+ argv.push_back(L"utf-8");
+ argv.push_back(L"-z9");
+ argv.insert(argv.end(), params->aargv.begin(), params->aargv.end());
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+
+ bp::child c(bin_path.c_str(), argv, env, boost::process::windows::hide);
+ params->child = &c;
+
+ c.wait();
+
+ params->child = nullptr;
+}
diff --git a/plugins/New_GPG/src/gpg_wrapper.h b/plugins/New_GPG/src/gpg_wrapper.h
index fbf5f1a62a..fc4ec1db8a 100644
--- a/plugins/New_GPG/src/gpg_wrapper.h
+++ b/plugins/New_GPG/src/gpg_wrapper.h
@@ -1,67 +1,67 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-#ifndef GPG_WRAPPER_H
-#define GPG_WRAPPER_H
-enum pxResult
-{
- pxSuccess,
- pxSuccessExitCodeInvalid,
- pxCreatePipeFailed,
- pxDuplicateHandleFailed,
- pxCloseHandleFailed,
- pxCreateProcessFailed,
- pxThreadWaitFailed,
- pxReadFileFailed,
- pxBufferOverflow,
- pxNotFound,
- pxNotConfigured
-};
-
-struct gpg_execution_params
-{
- gpg_execution_params();
- ~gpg_execution_params();
-
- std::vector<std::wstring> aargv;
- CMStringA out;
- uint32_t code = 0;
- int bNoOutput = false;
- pxResult result = pxSuccess;
- boost::process::child *child = nullptr;
-
- __forceinline void addParam(const std::wstring &param)
- { aargv.push_back(param);
- }
-};
-
-struct gpg_execution_params_pass : public gpg_execution_params
-{
- string &old_pass, &new_pass;
-
- gpg_execution_params_pass(std::string &o, std::string &n):
- old_pass(o),
- new_pass(n)
- {
- }
-};
-
-
-bool gpg_launcher(gpg_execution_params &params, boost::posix_time::time_duration t = boost::posix_time::seconds(10));
-void __cdecl pxEexcute_passwd_change_thread(gpg_execution_params_pass *param);
-
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+#ifndef GPG_WRAPPER_H
+#define GPG_WRAPPER_H
+enum pxResult
+{
+ pxSuccess,
+ pxSuccessExitCodeInvalid,
+ pxCreatePipeFailed,
+ pxDuplicateHandleFailed,
+ pxCloseHandleFailed,
+ pxCreateProcessFailed,
+ pxThreadWaitFailed,
+ pxReadFileFailed,
+ pxBufferOverflow,
+ pxNotFound,
+ pxNotConfigured
+};
+
+struct gpg_execution_params
+{
+ gpg_execution_params();
+ ~gpg_execution_params();
+
+ std::vector<std::wstring> aargv;
+ CMStringA out;
+ uint32_t code = 0;
+ int bNoOutput = false;
+ pxResult result = pxSuccess;
+ boost::process::child *child = nullptr;
+
+ __forceinline void addParam(const std::wstring &param)
+ { aargv.push_back(param);
+ }
+};
+
+struct gpg_execution_params_pass : public gpg_execution_params
+{
+ string &old_pass, &new_pass;
+
+ gpg_execution_params_pass(std::string &o, std::string &n):
+ old_pass(o),
+ new_pass(n)
+ {
+ }
+};
+
+
+bool gpg_launcher(gpg_execution_params &params, boost::posix_time::time_duration t = boost::posix_time::seconds(10));
+void __cdecl pxEexcute_passwd_change_thread(gpg_execution_params_pass *param);
+
#endif \ No newline at end of file
diff --git a/plugins/New_GPG/src/icons.cpp b/plugins/New_GPG/src/icons.cpp
index a3c7614803..9935766781 100644
--- a/plugins/New_GPG/src/icons.cpp
+++ b/plugins/New_GPG/src/icons.cpp
@@ -1,55 +1,55 @@
-// Copyright © 2010-22 SecureIM developers (baloo and others), sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with 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 IconItem iconList[] =
-{
- { "Secured", "secured", IDI_SECURED },
- { "Unsecured", "unsecured", IDI_UNSECURED }
-};
-
-void InitIconLib()
-{
- g_plugin.registerIcon(MODULENAME, iconList);
-}
-
-HANDLE IconLibHookIconsChanged(MIRANDAHOOK hook)
-{
- return HookEvent(ME_SKIN_ICONSCHANGED, hook);
-}
-
-void setSrmmIcon(MCONTACT h)
-{
- MCONTACT hContact = db_mc_isMeta(h) ? metaGetMostOnline(h) : h;
- bool enabled = isContactSecured(hContact);
- MCONTACT hMC = db_mc_tryMeta(hContact);
-
- int flags = enabled ? 0 : MBF_HIDDEN;
- Srmm_SetIconFlags(hContact, MODULENAME, 1, flags);
- if (hMC != hContact)
- Srmm_SetIconFlags(hMC, MODULENAME, 1, flags);
-
- flags = enabled ? MBF_HIDDEN : 0;
- Srmm_SetIconFlags(hContact, MODULENAME, 2, flags);
- if (hMC != hContact)
- Srmm_SetIconFlags(hMC, MODULENAME, 2, flags);
-
- const char *szIconId = (enabled) ? "secured" : "unsecured";
- ExtraIcon_SetIconByName(g_plugin.hCLIcon, hContact, szIconId);
- if (hMC != hContact)
- ExtraIcon_SetIconByName(g_plugin.hCLIcon, hMC, szIconId);
-}
+// Copyright © 2010-23 SecureIM developers (baloo and others), sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with 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 IconItem iconList[] =
+{
+ { "Secured", "secured", IDI_SECURED },
+ { "Unsecured", "unsecured", IDI_UNSECURED }
+};
+
+void InitIconLib()
+{
+ g_plugin.registerIcon(MODULENAME, iconList);
+}
+
+HANDLE IconLibHookIconsChanged(MIRANDAHOOK hook)
+{
+ return HookEvent(ME_SKIN_ICONSCHANGED, hook);
+}
+
+void setSrmmIcon(MCONTACT h)
+{
+ MCONTACT hContact = db_mc_isMeta(h) ? metaGetMostOnline(h) : h;
+ bool enabled = isContactSecured(hContact);
+ MCONTACT hMC = db_mc_tryMeta(hContact);
+
+ int flags = enabled ? 0 : MBF_HIDDEN;
+ Srmm_SetIconFlags(hContact, MODULENAME, 1, flags);
+ if (hMC != hContact)
+ Srmm_SetIconFlags(hMC, MODULENAME, 1, flags);
+
+ flags = enabled ? MBF_HIDDEN : 0;
+ Srmm_SetIconFlags(hContact, MODULENAME, 2, flags);
+ if (hMC != hContact)
+ Srmm_SetIconFlags(hMC, MODULENAME, 2, flags);
+
+ const char *szIconId = (enabled) ? "secured" : "unsecured";
+ ExtraIcon_SetIconByName(g_plugin.hCLIcon, hContact, szIconId);
+ if (hMC != hContact)
+ ExtraIcon_SetIconByName(g_plugin.hCLIcon, hMC, szIconId);
+}
diff --git a/plugins/New_GPG/src/init.cpp b/plugins/New_GPG/src/init.cpp
index cbcf08c80b..4c4406a7ef 100644
--- a/plugins/New_GPG/src/init.cpp
+++ b/plugins/New_GPG/src/init.cpp
@@ -1,241 +1,241 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with 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 GpgOptInit(WPARAM, LPARAM);
-int OnPreBuildContactMenu(WPARAM, LPARAM);
-
-INT_PTR RecvMsgSvc(WPARAM, LPARAM);
-INT_PTR SendMsgSvc(WPARAM, LPARAM);
-INT_PTR onSendFile(WPARAM, LPARAM);
-
-int HookSendMsg(WPARAM, LPARAM);
-int GetJabberInterface(WPARAM, LPARAM);
-int onProtoAck(WPARAM, LPARAM);
-int onWindowEvent(WPARAM, LPARAM);
-int onIconPressed(WPARAM, LPARAM);
-int onExtraIconPressed(WPARAM, LPARAM, LPARAM);
-
-void InitCheck();
-void FirstRun();
-void RemoveHandlers();
-
-// global variables
-CMPlugin g_plugin;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx = {
- sizeof(PLUGININFOEX),
- __PLUGIN_NAME,
- PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
- __DESCRIPTION,
- __AUTHOR,
- __COPYRIGHT,
- __AUTHORWEB,
- UNICODE_AWARE,
- // {4227c050-8d97-48d2-91ec-6a952b3dab94}
- { 0x4227c050, 0x8d97, 0x48d2, { 0x91, 0xec, 0x6a, 0x95, 0x2b, 0x3d, 0xab, 0x94 } }
-};
-
-CMPlugin::CMPlugin() :
- PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
- bDebugLog(MODULENAME, "bDebugLog", false),
- bJabberAPI(MODULENAME, "bJabberAPI", true),
- bStripTags(MODULENAME, "bStripTags", false),
- bAppendTags(MODULENAME, "bAppendTags", false),
- bSameAction(MODULENAME, "bSameAction", false),
- bAutoExchange(MODULENAME, "bAutoExchange", false),
- bFileTransfers(MODULENAME, "bFileTransfers", false),
- bPresenceSigning(MODULENAME, "bPresenceSigning", false),
- bSendErrorMessages(MODULENAME, "bSendErrorMessages", false)
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-INT_PTR LoadKey(WPARAM, LPARAM);
-INT_PTR SendKey(WPARAM, LPARAM);
-INT_PTR ExportGpGKeys(WPARAM, LPARAM);
-INT_PTR ImportGpGKeys(WPARAM, LPARAM);
-INT_PTR ToggleEncryption(WPARAM, LPARAM);
-
-void InitIconLib();
-
-void init_vars()
-{
- globals.wszInopentag = g_plugin.getMStringW("szInOpenTag", L"<GPGdec>");
- globals.wszInclosetag = g_plugin.getMStringW("szInCloseTag", L"</GPGdec>");
- globals.wszOutopentag = g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>");
- globals.wszOutclosetag = g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>");
- globals.wszPassword = g_plugin.getMStringW("szKeyPassword");
- globals.bold_font = CreateFont(14, 0, 0, 0, 600, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Arial");
-
- globals.debuglog.init();
-}
-
-static int OnModulesLoaded(WPARAM, LPARAM)
-{
- FirstRun();
- if (!g_plugin.getByte("FirstRun", 1))
- InitCheck();
-
- StatusIconData sid = {};
- sid.szModule = MODULENAME;
- sid.flags = MBF_HIDDEN;
- sid.dwId = 0x00000001;
- sid.hIcon = IcoLib_GetIcon("secured");
- sid.szTooltip.a = LPGEN("GPG Turn off encryption");
- Srmm_AddIcon(&sid, &g_plugin);
-
- sid.dwId = 0x00000002;
- sid.hIcon = IcoLib_GetIcon("unsecured");
- sid.szTooltip.a = LPGEN("GPG Turn on encryption");
- Srmm_AddIcon(&sid, &g_plugin);
-
- if (g_plugin.bJabberAPI) {
- GetJabberInterface(0, 0);
- HookEvent(ME_PROTO_ACCLISTCHANGED, GetJabberInterface);
- }
-
- HookEvent(ME_MSG_WINDOWEVENT, onWindowEvent);
- HookEvent(ME_MSG_ICONPRESSED, onIconPressed);
-
- Proto_RegisterModule(PROTOTYPE_ENCRYPTION, MODULENAME);
-
- CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, RecvMsgSvc);
- CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SendMsgSvc);
- CreateProtoServiceFunction(MODULENAME, PSS_FILE, onSendFile);
- clean_temp_dir();
- return 0;
-}
-
-static int OnShutdown(WPARAM, LPARAM)
-{
- RemoveHandlers();
- return 0;
-}
-
-static INT_PTR EventGetIcon(WPARAM flags, LPARAM)
-{
- HICON hIcon = g_plugin.getIcon(IDI_SECURED);
- return (INT_PTR)((flags & LR_SHARED) ? hIcon : CopyIcon(hIcon));
-}
-
-static INT_PTR GetEventText(WPARAM pEvent, LPARAM datatype)
-{
- DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent;
- ptrW wszText(mir_utf8decodeW((char *)dbei->pBlob));
- return (datatype != DBVT_WCHAR) ? (INT_PTR)mir_u2a(wszText) : (INT_PTR)wszText.detach();
-}
-
-int CMPlugin::Load()
-{
- DBEVENTTYPEDESCR dbEventType = {};
- dbEventType.module = MODULENAME;
- dbEventType.descr = "GPG service event";
- dbEventType.iconService = MODULENAME "/GetEventIcon";
- dbEventType.textService = MODULENAME "/GetEventText";
- DbEvent_RegisterType(&dbEventType);
-
- CreateServiceFunction(dbEventType.iconService, &EventGetIcon);
- CreateServiceFunction(dbEventType.textService, &GetEventText);
-
- HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildContactMenu);
- HookEvent(ME_DB_EVENT_FILTER_ADD, HookSendMsg);
- HookEvent(ME_OPT_INITIALISE, GpgOptInit);
- HookEvent(ME_PROTO_ACK, onProtoAck);
- HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
- HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown);
-
- InitIconLib();
- init_vars();
-
- ////////////////////////////////////////////////////////////////////////////////////////
- // Comtact menu items
-
- CMenuItem mi(&g_plugin);
- mi.hIcolibItem = g_plugin.getIconHandle(IDI_SECURED);
-
- SET_UID(mi, 0xbd22e3f8, 0xc19c, 0x45a8, 0xb7, 0x37, 0x6b, 0x3b, 0x27, 0xf0, 0x8c, 0xbb);
- mi.position = -0x7FFFFFFF;
- mi.name.a = LPGEN("Load public GPG key");
- mi.pszService = "/LoadPubKey";
- Menu_AddContactMenuItem(&mi);
- CreateServiceFunction(mi.pszService, LoadKey);
-
- SET_UID(mi, 0xc8008193, 0x56a9, 0x414a, 0x82, 0x98, 0x78, 0xe8, 0xa8, 0x84, 0x20, 0x67);
- mi.position = -0x7FFFFFFe;
- mi.name.a = LPGEN("Toggle GPG encryption");
- mi.pszService = "/ToggleEncryption";
- g_plugin.hToggleEncryption = Menu_AddContactMenuItem(&mi);
- CreateServiceFunction(mi.pszService, ToggleEncryption);
-
- SET_UID(mi, 0x42bb535f, 0xd58e, 0x4edb, 0xbf, 0x2c, 0xfa, 0x9a, 0xbf, 0x1e, 0xb8, 0x69);
- mi.position = -0x7FFFFFFd;
- mi.name.a = LPGEN("Send public key");
- mi.pszService = "/SendKey";
- g_plugin.hSendKey = Menu_AddContactMenuItem(&mi);
- CreateServiceFunction(mi.pszService, SendKey);
-
- ////////////////////////////////////////////////////////////////////////////////////////
- // Main menu items
-
- SET_UID(mi, 0x0bac023bb, 0xd2e, 0x46e0, 0x93, 0x13, 0x7c, 0xf9, 0xf6, 0xb5, 0x02, 0xd1);
- mi.position = -0x7FFFFFFe;
- mi.name.a = "GPG";
- mi.root = Menu_AddMainMenuItem(&mi);
- mi.flags = CMIF_UNMOVABLE;
-
- SET_UID(mi, 0x33a204b2, 0xe3c0, 0x413b, 0xbf, 0xd8, 0x8b, 0x2e, 0x3d, 0xa0, 0xef, 0xa4);
- mi.position = -0x7FFFFFFe;
- mi.name.a = LPGEN("Export GPG Public keys");
- mi.pszService = "/ExportGPGKeys";
- Menu_AddMainMenuItem(&mi);
- CreateServiceFunction(mi.pszService, ExportGpGKeys);
-
- SET_UID(mi, 0x627fcfc1, 0x4e60, 0x4428, 0xaf, 0x96, 0x11, 0x42, 0x24, 0xeb, 0x07, 0xea);
- mi.position = -0x7FFFFFFF;
- mi.name.a = LPGEN("Import GPG Public keys");
- mi.pszService = "/ImportGPGKeys";
- Menu_AddMainMenuItem(&mi);
- CreateServiceFunction(mi.pszService, ImportGpGKeys);
-
- ////////////////////////////////////////////////////////////////////////////////////////
- // Extra icon
-
- hCLIcon = ExtraIcon_RegisterIcolib(MODULENAME, Translate("GPG encryption status"), "secured", &onExtraIconPressed);
- for (auto &cc : Contacts())
- if (isContactHaveKey(cc))
- setSrmmIcon(cc);
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-extern list<wstring> transfers;
-
-int CMPlugin::Unload()
-{
- for (auto p : transfers)
- if (!p.empty())
- boost::filesystem::remove(p);
-
- clean_temp_dir();
- return 0;
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with 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 GpgOptInit(WPARAM, LPARAM);
+int OnPreBuildContactMenu(WPARAM, LPARAM);
+
+INT_PTR RecvMsgSvc(WPARAM, LPARAM);
+INT_PTR SendMsgSvc(WPARAM, LPARAM);
+INT_PTR onSendFile(WPARAM, LPARAM);
+
+int HookSendMsg(WPARAM, LPARAM);
+int GetJabberInterface(WPARAM, LPARAM);
+int onProtoAck(WPARAM, LPARAM);
+int onWindowEvent(WPARAM, LPARAM);
+int onIconPressed(WPARAM, LPARAM);
+int onExtraIconPressed(WPARAM, LPARAM, LPARAM);
+
+void InitCheck();
+void FirstRun();
+void RemoveHandlers();
+
+// global variables
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {4227c050-8d97-48d2-91ec-6a952b3dab94}
+ { 0x4227c050, 0x8d97, 0x48d2, { 0x91, 0xec, 0x6a, 0x95, 0x2b, 0x3d, 0xab, 0x94 } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
+ bDebugLog(MODULENAME, "bDebugLog", false),
+ bJabberAPI(MODULENAME, "bJabberAPI", true),
+ bStripTags(MODULENAME, "bStripTags", false),
+ bAppendTags(MODULENAME, "bAppendTags", false),
+ bSameAction(MODULENAME, "bSameAction", false),
+ bAutoExchange(MODULENAME, "bAutoExchange", false),
+ bFileTransfers(MODULENAME, "bFileTransfers", false),
+ bPresenceSigning(MODULENAME, "bPresenceSigning", false),
+ bSendErrorMessages(MODULENAME, "bSendErrorMessages", false)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR LoadKey(WPARAM, LPARAM);
+INT_PTR SendKey(WPARAM, LPARAM);
+INT_PTR ExportGpGKeys(WPARAM, LPARAM);
+INT_PTR ImportGpGKeys(WPARAM, LPARAM);
+INT_PTR ToggleEncryption(WPARAM, LPARAM);
+
+void InitIconLib();
+
+void init_vars()
+{
+ globals.wszInopentag = g_plugin.getMStringW("szInOpenTag", L"<GPGdec>");
+ globals.wszInclosetag = g_plugin.getMStringW("szInCloseTag", L"</GPGdec>");
+ globals.wszOutopentag = g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>");
+ globals.wszOutclosetag = g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>");
+ globals.wszPassword = g_plugin.getMStringW("szKeyPassword");
+ globals.bold_font = CreateFont(14, 0, 0, 0, 600, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Arial");
+
+ globals.debuglog.init();
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ FirstRun();
+ if (!g_plugin.getByte("FirstRun", 1))
+ InitCheck();
+
+ StatusIconData sid = {};
+ sid.szModule = MODULENAME;
+ sid.flags = MBF_HIDDEN;
+ sid.dwId = 0x00000001;
+ sid.hIcon = IcoLib_GetIcon("secured");
+ sid.szTooltip.a = LPGEN("GPG Turn off encryption");
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ sid.dwId = 0x00000002;
+ sid.hIcon = IcoLib_GetIcon("unsecured");
+ sid.szTooltip.a = LPGEN("GPG Turn on encryption");
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ if (g_plugin.bJabberAPI) {
+ GetJabberInterface(0, 0);
+ HookEvent(ME_PROTO_ACCLISTCHANGED, GetJabberInterface);
+ }
+
+ HookEvent(ME_MSG_WINDOWEVENT, onWindowEvent);
+ HookEvent(ME_MSG_ICONPRESSED, onIconPressed);
+
+ Proto_RegisterModule(PROTOTYPE_ENCRYPTION, MODULENAME);
+
+ CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, RecvMsgSvc);
+ CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SendMsgSvc);
+ CreateProtoServiceFunction(MODULENAME, PSS_FILE, onSendFile);
+ clean_temp_dir();
+ return 0;
+}
+
+static int OnShutdown(WPARAM, LPARAM)
+{
+ RemoveHandlers();
+ return 0;
+}
+
+static INT_PTR EventGetIcon(WPARAM flags, LPARAM)
+{
+ HICON hIcon = g_plugin.getIcon(IDI_SECURED);
+ return (INT_PTR)((flags & LR_SHARED) ? hIcon : CopyIcon(hIcon));
+}
+
+static INT_PTR GetEventText(WPARAM pEvent, LPARAM datatype)
+{
+ DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent;
+ ptrW wszText(mir_utf8decodeW((char *)dbei->pBlob));
+ return (datatype != DBVT_WCHAR) ? (INT_PTR)mir_u2a(wszText) : (INT_PTR)wszText.detach();
+}
+
+int CMPlugin::Load()
+{
+ DBEVENTTYPEDESCR dbEventType = {};
+ dbEventType.module = MODULENAME;
+ dbEventType.descr = "GPG service event";
+ dbEventType.iconService = MODULENAME "/GetEventIcon";
+ dbEventType.textService = MODULENAME "/GetEventText";
+ DbEvent_RegisterType(&dbEventType);
+
+ CreateServiceFunction(dbEventType.iconService, &EventGetIcon);
+ CreateServiceFunction(dbEventType.textService, &GetEventText);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildContactMenu);
+ HookEvent(ME_DB_EVENT_FILTER_ADD, HookSendMsg);
+ HookEvent(ME_OPT_INITIALISE, GpgOptInit);
+ HookEvent(ME_PROTO_ACK, onProtoAck);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown);
+
+ InitIconLib();
+ init_vars();
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Comtact menu items
+
+ CMenuItem mi(&g_plugin);
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_SECURED);
+
+ SET_UID(mi, 0xbd22e3f8, 0xc19c, 0x45a8, 0xb7, 0x37, 0x6b, 0x3b, 0x27, 0xf0, 0x8c, 0xbb);
+ mi.position = -0x7FFFFFFF;
+ mi.name.a = LPGEN("Load public GPG key");
+ mi.pszService = "/LoadPubKey";
+ Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, LoadKey);
+
+ SET_UID(mi, 0xc8008193, 0x56a9, 0x414a, 0x82, 0x98, 0x78, 0xe8, 0xa8, 0x84, 0x20, 0x67);
+ mi.position = -0x7FFFFFFe;
+ mi.name.a = LPGEN("Toggle GPG encryption");
+ mi.pszService = "/ToggleEncryption";
+ g_plugin.hToggleEncryption = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, ToggleEncryption);
+
+ SET_UID(mi, 0x42bb535f, 0xd58e, 0x4edb, 0xbf, 0x2c, 0xfa, 0x9a, 0xbf, 0x1e, 0xb8, 0x69);
+ mi.position = -0x7FFFFFFd;
+ mi.name.a = LPGEN("Send public key");
+ mi.pszService = "/SendKey";
+ g_plugin.hSendKey = Menu_AddContactMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, SendKey);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Main menu items
+
+ SET_UID(mi, 0x0bac023bb, 0xd2e, 0x46e0, 0x93, 0x13, 0x7c, 0xf9, 0xf6, 0xb5, 0x02, 0xd1);
+ mi.position = -0x7FFFFFFe;
+ mi.name.a = "GPG";
+ mi.root = Menu_AddMainMenuItem(&mi);
+ mi.flags = CMIF_UNMOVABLE;
+
+ SET_UID(mi, 0x33a204b2, 0xe3c0, 0x413b, 0xbf, 0xd8, 0x8b, 0x2e, 0x3d, 0xa0, 0xef, 0xa4);
+ mi.position = -0x7FFFFFFe;
+ mi.name.a = LPGEN("Export GPG Public keys");
+ mi.pszService = "/ExportGPGKeys";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, ExportGpGKeys);
+
+ SET_UID(mi, 0x627fcfc1, 0x4e60, 0x4428, 0xaf, 0x96, 0x11, 0x42, 0x24, 0xeb, 0x07, 0xea);
+ mi.position = -0x7FFFFFFF;
+ mi.name.a = LPGEN("Import GPG Public keys");
+ mi.pszService = "/ImportGPGKeys";
+ Menu_AddMainMenuItem(&mi);
+ CreateServiceFunction(mi.pszService, ImportGpGKeys);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Extra icon
+
+ hCLIcon = ExtraIcon_RegisterIcolib(MODULENAME, Translate("GPG encryption status"), "secured", &onExtraIconPressed);
+ for (auto &cc : Contacts())
+ if (isContactHaveKey(cc))
+ setSrmmIcon(cc);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern list<wstring> transfers;
+
+int CMPlugin::Unload()
+{
+ for (auto p : transfers)
+ if (!p.empty())
+ boost::filesystem::remove(p);
+
+ clean_temp_dir();
+ return 0;
+}
diff --git a/plugins/New_GPG/src/jabber_account.h b/plugins/New_GPG/src/jabber_account.h
index 5026a410f9..b011f70426 100644
--- a/plugins/New_GPG/src/jabber_account.h
+++ b/plugins/New_GPG/src/jabber_account.h
@@ -1,52 +1,52 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef JABBER_ACCOUNT_H
-#define JABBER_ACCOUNT_H
-
-class JabberAccount
-{
- wchar_t *AccountName = nullptr;
- int AccountNumber = -1;
- IJabberInterface *JabberInterface = nullptr;
- HJHANDLER hSendHandler = INVALID_HANDLE_VALUE, hPresenceHandler = INVALID_HANDLE_VALUE, hMessageHandler = INVALID_HANDLE_VALUE;
-
-public:
- __forceinline JabberAccount()
- {
- }
-
- __forceinline ~JabberAccount()
- {
- mir_free(AccountName);
- }
-
- __forceinline void setAccountName(wchar_t *Name) { AccountName = Name; }
- __forceinline void setAccountNumber(int Number) { AccountNumber = Number; }
- __forceinline void setJabberInterface(IJabberInterface *JIf) { JabberInterface = JIf; }
- __forceinline void setSendHandler(HJHANDLER hHandler) { hSendHandler = hHandler; }
- __forceinline void setPresenceHandler(HJHANDLER hHandler) { hPresenceHandler = hHandler; }
- __forceinline void setMessageHandler(HJHANDLER hHandler) { hMessageHandler = hHandler; }
-
- __forceinline wchar_t* getAccountName() const { return AccountName; }
- __forceinline int getAccountNumber() const { return AccountNumber; }
- __forceinline IJabberInterface* getJabberInterface() const { return JabberInterface; }
- __forceinline HJHANDLER getSendHandler() const { return hSendHandler; }
- __forceinline HJHANDLER getPresenceHandler() const { return hPresenceHandler; }
- __forceinline HJHANDLER getMessageHandler() const { return hMessageHandler; }
-};
-
-#endif
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef JABBER_ACCOUNT_H
+#define JABBER_ACCOUNT_H
+
+class JabberAccount
+{
+ wchar_t *AccountName = nullptr;
+ int AccountNumber = -1;
+ IJabberInterface *JabberInterface = nullptr;
+ HJHANDLER hSendHandler = INVALID_HANDLE_VALUE, hPresenceHandler = INVALID_HANDLE_VALUE, hMessageHandler = INVALID_HANDLE_VALUE;
+
+public:
+ __forceinline JabberAccount()
+ {
+ }
+
+ __forceinline ~JabberAccount()
+ {
+ mir_free(AccountName);
+ }
+
+ __forceinline void setAccountName(wchar_t *Name) { AccountName = Name; }
+ __forceinline void setAccountNumber(int Number) { AccountNumber = Number; }
+ __forceinline void setJabberInterface(IJabberInterface *JIf) { JabberInterface = JIf; }
+ __forceinline void setSendHandler(HJHANDLER hHandler) { hSendHandler = hHandler; }
+ __forceinline void setPresenceHandler(HJHANDLER hHandler) { hPresenceHandler = hHandler; }
+ __forceinline void setMessageHandler(HJHANDLER hHandler) { hMessageHandler = hHandler; }
+
+ __forceinline wchar_t* getAccountName() const { return AccountName; }
+ __forceinline int getAccountNumber() const { return AccountNumber; }
+ __forceinline IJabberInterface* getJabberInterface() const { return JabberInterface; }
+ __forceinline HJHANDLER getSendHandler() const { return hSendHandler; }
+ __forceinline HJHANDLER getPresenceHandler() const { return hPresenceHandler; }
+ __forceinline HJHANDLER getMessageHandler() const { return hMessageHandler; }
+};
+
+#endif
diff --git a/plugins/New_GPG/src/log.cpp b/plugins/New_GPG/src/log.cpp
index 7fa5caf3a0..930ad56167 100644
--- a/plugins/New_GPG/src/log.cpp
+++ b/plugins/New_GPG/src/log.cpp
@@ -1,49 +1,49 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with 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 string time_str()
-{
- boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- return (string)boost::posix_time::to_simple_string(now) + ": ";
-}
-
-logtofile &logtofile::operator<<(const char *buf)
-{
- if (bEnabled)
- mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf);
- return *this;
-}
-
-logtofile& logtofile::operator<<(const string &buf)
-{
- if (bEnabled)
- mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf.c_str());
- return *this;
-}
-
-void logtofile::init()
-{
- if (g_plugin.bDebugLog)
- hLogger = mir_createLog("NewGPG", L"NewGPG log file", g_plugin.getMStringW("szLogFilePath", L"C:\\GPGdebug.log"), 0);
- else {
- mir_closeLog(hLogger);
- hLogger = nullptr;
- }
-
- bEnabled = g_plugin.bDebugLog;
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with 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 string time_str()
+{
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ return (string)boost::posix_time::to_simple_string(now) + ": ";
+}
+
+logtofile &logtofile::operator<<(const char *buf)
+{
+ if (bEnabled)
+ mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf);
+ return *this;
+}
+
+logtofile& logtofile::operator<<(const string &buf)
+{
+ if (bEnabled)
+ mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf.c_str());
+ return *this;
+}
+
+void logtofile::init()
+{
+ if (g_plugin.bDebugLog)
+ hLogger = mir_createLog("NewGPG", L"NewGPG log file", g_plugin.getMStringW("szLogFilePath", L"C:\\GPGdebug.log"), 0);
+ else {
+ mir_closeLog(hLogger);
+ hLogger = nullptr;
+ }
+
+ bEnabled = g_plugin.bDebugLog;
+}
diff --git a/plugins/New_GPG/src/log.h b/plugins/New_GPG/src/log.h
index b478f780aa..91328edb21 100644
--- a/plugins/New_GPG/src/log.h
+++ b/plugins/New_GPG/src/log.h
@@ -1,32 +1,32 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#ifndef LOG_H
-#define LOG_H
-
-class logtofile
-{
- HANDLE hLogger;
- bool bEnabled = false;
-
-public:
- logtofile& operator<<(const char *buf);
- logtofile& operator<<(const std::string &buf);
- void init();
-
- __forceinline operator bool() const { return bEnabled; }
-};
-
-#endif
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#ifndef LOG_H
+#define LOG_H
+
+class logtofile
+{
+ HANDLE hLogger;
+ bool bEnabled = false;
+
+public:
+ logtofile& operator<<(const char *buf);
+ logtofile& operator<<(const std::string &buf);
+ void init();
+
+ __forceinline operator bool() const { return bEnabled; }
+};
+
+#endif
diff --git a/plugins/New_GPG/src/main.cpp b/plugins/New_GPG/src/main.cpp
index bcaafe6ac0..6f2ed6c799 100644
--- a/plugins/New_GPG/src/main.cpp
+++ b/plugins/New_GPG/src/main.cpp
@@ -1,654 +1,654 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with 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 comment(lib, "shlwapi.lib")
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// GPG binaries options
-
-class CDlgGpgBinOpts : public CDlgBase
-{
- CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR, btn_OK, btn_GENERATE_RANDOM;
- CCtrlEdit edit_BIN_PATH, edit_HOME_DIR;
- CCtrlCheck chk_AUTO_EXCHANGE;
-
-public:
- CDlgGpgBinOpts() :
- CDlgBase(g_plugin, IDD_BIN_PATH),
- btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH),
- btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR),
- btn_OK(this, ID_OK),
- btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM),
- edit_BIN_PATH(this, IDC_BIN_PATH),
- edit_HOME_DIR(this, IDC_HOME_DIR),
- chk_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE)
- {
- btn_SET_BIN_PATH.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_BIN_PATH);
- btn_SET_HOME_DIR.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_HOME_DIR);
- btn_OK.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_OK);
- btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_GENERATE_RANDOM);
- }
-
- bool OnInitDialog() override
- {
- CMStringW path;
- bool gpg_exists = false, lang_exists = false;
-
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- SetCurrentDirectoryW(mir_path);
-
- CMStringW gpg_path(mir_path); gpg_path.Append(L"\\GnuPG\\gpg.exe");
- CMStringW gpg_lang_path(mir_path); gpg_lang_path.Append(L"\\GnuPG\\gnupg.nls\\en@quot.mo");
-
- if (boost::filesystem::exists(gpg_path.c_str())) {
- gpg_exists = true;
- path = L"GnuPG\\gpg.exe";
- }
- else path = gpg_path;
-
- if (boost::filesystem::exists(gpg_lang_path.c_str()))
- lang_exists = true;
- if (gpg_exists && !lang_exists)
- MessageBox(nullptr, TranslateT("GPG binary found in Miranda folder, but English locale does not exist.\nIt's highly recommended that you place \\gnupg.nls\\en@quot.mo in GnuPG folder under Miranda root.\nWithout this file you may experience many problems with GPG output on non-English systems\nand plugin may completely not work.\nYou have been warned."), TranslateT("Warning"), MB_OK);
-
- DWORD len = MAX_PATH;
- bool bad_version = false;
- {
- ptrW tmp;
- if (!gpg_exists) {
- tmp = g_plugin.getWStringA("szGpgBinPath", (SHGetValueW(HKEY_CURRENT_USER, L"Software\\GNU\\GnuPG", L"gpgProgram", 0, (void *)path.c_str(), &len) == ERROR_SUCCESS) ? path.c_str() : L"");
- if (tmp[0])
- if (!boost::filesystem::exists((wchar_t *)tmp))
- MessageBoxW(nullptr, TranslateT("Wrong GPG binary location found in system.\nPlease choose another location"), TranslateT("Warning"), MB_OK);
- }
- else tmp = mir_wstrdup(path.c_str());
-
- edit_BIN_PATH.SetText(tmp);
- if (gpg_exists/* && lang_exists*/) {
- g_plugin.setWString("szGpgBinPath", tmp);
-
- gpg_execution_params params;
- params.addParam(L"--version");
- bool _gpg_valid = globals.gpg_valid;
- globals.gpg_valid = true;
- gpg_launcher(params);
- globals.gpg_valid = _gpg_valid; //TODO: check this
- g_plugin.delSetting("szGpgBinPath");
- int p1 = params.out.Find("(GnuPG) ");
- if (p1 != -1) {
- p1 += mir_strlen("(GnuPG) ");
- if (params.out[p1] != '1')
- bad_version = true;
- }
- else {
- bad_version = false;
- MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Error"), MB_OK);
- }
- if (bad_version)
- MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
- }
- }
-
- CMStringW tmp(g_plugin.getMStringW("szHomePath"));
- if (tmp.IsEmpty()) {
- mir_wstrcat(mir_path, L"\\gpg");
- if (_waccess(mir_path, 0) != -1) {
- tmp = mir_path;
- MessageBoxW(nullptr, TranslateT("\"GPG\" directory found in Miranda root.\nAssuming it's GPG home directory.\nGPG home directory set."), TranslateT("Info"), MB_OK);
- }
- else {
- wstring path_ = _wgetenv(L"APPDATA");
- path_ += L"\\GnuPG";
- tmp = path_.c_str();
- }
- }
- edit_HOME_DIR.SetText(!gpg_exists ? tmp : L"gpg");
-
- // TODO: additional check for write access
- if (gpg_exists && lang_exists && !bad_version)
- MessageBox(nullptr, TranslateT("Your GPG version is supported. The language file was found.\nGPG plugin should work fine.\nPress OK to continue."), TranslateT("Info"), MB_OK);
- chk_AUTO_EXCHANGE.Enable();
- return true;
- }
-
- void OnDestroy() override
- {
- void InitCheck();
- InitCheck();
- }
-
- void onClick_SET_BIN_PATH(CCtrlButton *)
- {
- GetFilePath(L"Choose gpg.exe", "szGpgBinPath", L"*.exe", L"EXE Executables");
- CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
- edit_BIN_PATH.SetText(tmp);
-
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- if (tmp.Find(mir_path, 0) == 0) {
- CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
- edit_BIN_PATH.SetText(path);
- }
- }
-
- void onClick_SET_HOME_DIR(CCtrlButton *)
- {
- GetFolderPath(L"Set home directory");
- CMStringW tmp(g_plugin.getMStringW("szHomePath"));
- edit_HOME_DIR.SetText(tmp);
-
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- PathToAbsoluteW(L"\\", mir_path);
- if (tmp.Find(mir_path, 0) == 0) {
- CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
- edit_HOME_DIR.SetText(path);
- }
- }
-
- void onClick_OK(CCtrlButton *)
- {
- if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) {
- gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText());
- globals.gpg_valid = true;
- g_plugin.setByte("FirstRun", 0);
- this->Hide();
- this->Close();
- ShowFirstRunDialog();
- }
- }
-
- void onClick_GENERATE_RANDOM(CCtrlButton *)
- {
- if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) {
- gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText());
- globals.gpg_valid = true;
- if (gpg_use_new_random_key(nullptr)) {
- g_plugin.bAutoExchange = chk_AUTO_EXCHANGE.GetState();
- globals.gpg_valid = true;
- g_plugin.setByte("FirstRun", 0);
- this->Close();
- }
- }
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static int EnumProc(const char *szSetting, void *param)
-{
- auto *list = (OBJLIST<CMStringA> *)param;
- if (strchr(szSetting, '(') && strchr(szSetting, ')'))
- list->insert(new CMStringA(szSetting));
- return 0;
-}
-
-void FirstRun()
-{
- if (g_plugin.getByte("CompatLevel") != 1) {
- OBJLIST<CMStringA> settings(1);
- db_enum_settings(0, EnumProc, MODULENAME, &settings);
-
- for (auto &it : settings) {
- CMStringA newName(*it);
- int p1 = newName.Find('(');
- newName.Delete(0, p1+1);
- int p2 = newName.Find(')');
- if (p2 == -1)
- continue;
- newName.Delete(p2, 1);
-
- CMStringW val = g_plugin.getMStringW(it->c_str());
- g_plugin.delSetting(it->c_str());
- g_plugin.setWString(newName, val);
- }
-
- g_plugin.setByte("CompatLevel", 1);
- }
-
- if (g_plugin.getByte("FirstRun", 1))
- (new CDlgGpgBinOpts())->Show();
-}
-
-void InitCheck()
-{
- // parse gpg output
- {
- ptrW current_home(g_plugin.getWStringA("szHomePath", L""));
- g_plugin.setWString("szHomePath", L""); //we do not need home for gpg binary validation
- globals.gpg_valid = isGPGValid();
- g_plugin.setWString("szHomePath", current_home); //return current home dir back
- }
- {
- bool home_dir_access = false, temp_access = false;
- std::wstring test_path(ptrW(g_plugin.getWStringA("szHomePath", L"")));
- test_path += L"/";
- test_path += toUTF16(get_random(13));
- wfstream test_file;
- test_file.open(test_path, std::ios::trunc | std::ios::out);
- if (test_file.is_open() && test_file.good()) {
- test_file << L"access_test\n";
- if (test_file.good())
- home_dir_access = true;
- test_file.close();
- boost::filesystem::remove(test_path);
- }
-
- test_path = _wgetenv(L"TEMP");
- test_path += L"/";
- test_path += toUTF16(get_random(13));
- test_file.open(test_path, std::ios::trunc | std::ios::out);
- if (test_file.is_open() && test_file.good()) {
- test_file << L"access_test\n";
- if (test_file.good())
- temp_access = true;
- test_file.close();
- boost::filesystem::remove(test_path);
- }
- if (!home_dir_access || !temp_access || !globals.gpg_valid) {
- CMStringW buf;
- buf.Append(globals.gpg_valid ? TranslateT("GPG binary is set and valid (this is good).\n") : TranslateT("GPG binary unset or invalid (plugin will not work).\n"));
- buf.Append(home_dir_access ? TranslateT("Home dir write access granted (this is good).\n") : TranslateT("Home dir has no write access (plugin most probably will not work).\n"));
- buf.Append(temp_access ? TranslateT("Temp dir write access granted (this is good).\n") : TranslateT("Temp dir has no write access (plugin should work, but may have some problems, file transfers will not work)."));
- if (!globals.gpg_valid)
- buf.Append(TranslateT("\nGPG will be disabled until you solve these problems"));
- MessageBox(nullptr, buf, TranslateT("GPG plugin problems"), MB_OK);
- }
- if (!globals.gpg_valid)
- return;
- globals.gpg_keyexist = isGPGKeyExist();
-
- wstring::size_type p = 0, p2 = 0;
-
- gpg_execution_params params;
- params.addParam(L"--list-secret-keys");
- params.addParam(L"--batch");
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- _wmkdir(g_plugin.getMStringW("szHomePath") + L"\\tmp");
- string out(params.out);
-
- CMStringW wszQuestion;
- for (auto &pa : Accounts()) {
- if (StriStr(pa->szModuleName, "metacontacts"))
- continue;
- if (StriStr(pa->szModuleName, "weather"))
- continue;
-
- std::string acc = pa->szModuleName;
- acc += "_KeyID";
- CMStringA keyid = g_plugin.getMStringA(acc.c_str());
- if (!keyid.IsEmpty()) {
- wszQuestion = TranslateT("Your secret key with ID: ");
- keyid = g_plugin.getMStringA("KeyID");
- if ((p = out.find(keyid)) == string::npos) {
- wszQuestion += keyid;
- wszQuestion += TranslateT(" for account ");
- wszQuestion += pa->tszAccountName;
- wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?");
- if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
- ShowFirstRunDialog();
- }
- p2 = p;
- p = out.find("[", p);
- p2 = out.find("\n", p2);
- if ((p != std::string::npos) && (p < p2)) {
- p = out.find("expires:", p);
- p += mir_strlen("expires:");
- p++;
- p2 = out.find("]", p);
- wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
- bool expired = false;
- {
- boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- wchar_t buf[5];
- wcsncpy_s(buf, expire_date, _TRUNCATE);
- int year = _wtoi(buf);
- if (year < now.date().year())
- expired = true;
- else if (year == now.date().year()) {
- wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
- int month = _wtoi(buf);
- if (month < now.date().month())
- expired = true;
- else if (month == now.date().month()) {
- wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
- unsigned day = _wtoi(buf);
- if (day <= now.date().day_number())
- expired = true;
- }
- }
- }
- if (expired) {
- wszQuestion += keyid;
- wszQuestion += TranslateT(" for account ");
- wszQuestion += pa->tszAccountName;
- wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?");
- if (MessageBoxW(nullptr, wszQuestion.c_str(), TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
- ShowFirstRunDialog();
- }
- mir_free(expire_date);
- }
- }
- }
-
- wszQuestion = TranslateT("Your secret key with ID: ");
- CMStringA keyid(g_plugin.getMStringA("KeyID"));
- CMStringA key(g_plugin.getMStringA("GPGPubKey"));
- if (!g_plugin.getByte("FirstRun", 1) && (keyid.IsEmpty() || key.IsEmpty())) {
- wszQuestion = TranslateT("You didn't set a private key.\nWould you like to set it now?");
- if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own private key warning"), MB_YESNO) == IDYES)
- ShowFirstRunDialog();
- }
- if ((p = out.find(keyid)) == string::npos) {
- wszQuestion += keyid;
- wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?");
- if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
- ShowFirstRunDialog();
- }
- p2 = p;
- p = out.find("[", p);
- p2 = out.find("\n", p2);
- if ((p != std::string::npos) && (p < p2)) {
- p = out.find("expires:", p);
- p += mir_strlen("expires:");
- p++;
- p2 = out.find("]", p);
- wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
- bool expired = false;
- {
- boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- wchar_t buf[5];
- wcsncpy_s(buf, expire_date, _TRUNCATE);
- int year = _wtoi(buf);
- if (year < now.date().year())
- expired = true;
- else if (year == now.date().year()) {
- wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
- int month = _wtoi(buf);
- if (month < now.date().month())
- expired = true;
- else if (month == now.date().month()) {
- wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
- unsigned day = _wtoi(buf);
- if (day <= now.date().day_number())
- expired = true;
- }
- }
- }
- if (expired) {
- wszQuestion += keyid;
- wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?");
- if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
- ShowFirstRunDialog();
- }
- mir_free(expire_date);
- }
- // TODO: check for expired key
- }
- {
- CMStringW path(g_plugin.getMStringW("szHomePath"));
- uint32_t dwFileAttr = GetFileAttributes(path);
- if (dwFileAttr != INVALID_FILE_ATTRIBUTES) {
- dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
- SetFileAttributes(path, dwFileAttr);
- }
- }
-}
-
-void ImportKey(MCONTACT hContact, std::wstring new_key)
-{
- bool for_all_sub = false;
- if (db_mc_isMeta(hContact)) {
- if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES)
- for_all_sub = true;
-
- if (for_all_sub) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setWString(hcnt, "GPGPubKey", new_key.c_str());
- }
- }
- else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", new_key.c_str());
- }
- else g_plugin.setWString(hContact, "GPGPubKey", new_key.c_str());
-
- // gpg execute block
- CMStringW tmp2 = g_plugin.getMStringW("szHomePath");
- tmp2 += L"\\temporary_exported.asc";
- boost::filesystem::remove(tmp2.c_str());
-
- CMStringW ptmp;
- if (db_mc_isMeta(hContact))
- ptmp = g_plugin.getMStringW(metaGetMostOnline(hContact), "GPGPubKey");
- else
- ptmp = g_plugin.getMStringW(hContact, "GPGPubKey");
-
- wfstream f(tmp2, std::ios::out);
- f << ptmp.c_str();
- f.close();
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--import");
- params.addParam(tmp2.c_str());
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- string output(params.out);
- if (db_mc_isMeta(hContact)) {
- if (for_all_sub) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt) {
- char *tmp = nullptr;
- string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
- string::size_type s2 = output.find(":", s);
- g_plugin.setString(hcnt, "KeyID", output.substr(s, s2 - s).c_str());
- s = output.find(RUS_QUOTE, s2);
- if (s == string::npos) {
- s = output.find("\"", s2);
- s += 1;
- }
- else s += sizeof(RUS_QUOTE) - 1;
-
- bool uncommon = false;
- if ((s2 = output.find("(", s)) == string::npos) {
- if ((s2 = output.find("<", s)) == string::npos) {
- s2 = output.find(RUS_ANGLE, s);
- uncommon = true;
- }
- }
- else if (s2 > output.find("<", s))
- s2 = output.find("<", s);
- if (s != string::npos && s2 != string::npos) {
- tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
- mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hcnt, "KeyMainName", tmp);
- mir_free(tmp);
- }
-
- if ((s = output.find(")", s2)) == string::npos)
- s = output.find(">", s2);
- else if (s > output.find(">", s2))
- s = output.find(">", s2);
- s2++;
- if (s != string::npos && s2 != string::npos) {
- if (output[s] == ')') {
- tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
- mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hcnt, "KeyComment", tmp);
- mir_free(tmp);
- s += 3;
- s2 = output.find(">", s);
- if (s != string::npos && s2 != string::npos) {
- tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
- mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hcnt, "KeyMainEmail", tmp);
- mir_free(tmp);
- }
- }
- else {
- tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
- mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str());
- mir_free(tmp);
- }
- }
- g_plugin.delSetting(hcnt, "bAlwatsTrust");
- }
- }
- }
- else {
- char *tmp = nullptr;
- string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
- string::size_type s2 = output.find(":", s);
- g_plugin.setString(metaGetMostOnline(hContact), "KeyID", output.substr(s, s2 - s).c_str());
- s = output.find(RUS_QUOTE, s2);
- if (s == string::npos) {
- s = output.find("\"", s2);
- s += 1;
- }
- else s += sizeof(RUS_QUOTE) - 1;
-
- bool uncommon = false;
- if ((s2 = output.find("(", s)) == string::npos) {
- if ((s2 = output.find("<", s)) == string::npos) {
- s2 = output.find(RUS_ANGLE, s);
- uncommon = true;
- }
- }
- else if (s2 > output.find("<", s))
- s2 = output.find("<", s);
- if (s != string::npos && s2 != string::npos) {
- tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
- mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", tmp);
- mir_free(tmp);
- }
- if ((s = output.find(")", s2)) == string::npos)
- s = output.find(">", s2);
- else if (s > output.find(">", s2))
- s = output.find(">", s2);
- s2++;
- if (s != string::npos && s2 != string::npos) {
- if (output[s] == ')') {
- tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
- mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", tmp);
- mir_free(tmp);
- s += 3;
- s2 = output.find(">", s);
- if (s != string::npos && s2 != string::npos) {
- tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
- mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", tmp);
- mir_free(tmp);
- }
- }
- else {
- tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
- mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str());
- mir_free(tmp);
- }
- }
- g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust");
- }
- }
- else {
- char *tmp = nullptr;
- string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
- string::size_type s2 = output.find(":", s);
- g_plugin.setString(hContact, "KeyID", output.substr(s, s2 - s).c_str());
- s = output.find(RUS_QUOTE, s2);
- if (s == string::npos) {
- s = output.find("\"", s2);
- s += 1;
- }
- else s += sizeof(RUS_QUOTE) - 1;
-
- bool uncommon = false;
- if ((s2 = output.find("(", s)) == string::npos) {
- if ((s2 = output.find("<", s)) == string::npos) {
- s2 = output.find(RUS_ANGLE, s);
- uncommon = true;
- }
- }
- else if (s2 > output.find("<", s))
- s2 = output.find("<", s);
- if (s != string::npos && s2 != string::npos) {
- tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
- mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hContact, "KeyMainName", tmp);
- mir_free(tmp);
- }
- if ((s = output.find(")", s2)) == string::npos)
- s = output.find(">", s2);
- else if (s > output.find(">", s2))
- s = output.find(">", s2);
- s2++;
- if (s != string::npos && s2 != string::npos) {
- if (output[s] == ')') {
- tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
- mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hContact, "KeyComment", tmp);
- mir_free(tmp);
- s += 3;
- s2 = output.find(">", s);
- if (s != string::npos && s2 != string::npos) {
- tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
- mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hContact, "KeyMainEmail", tmp);
- mir_free(tmp);
- }
- }
- else {
- tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
- mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
- mir_free(tmp);
- }
- }
- g_plugin.delSetting(hContact, "bAlwatsTrust");
- }
-
- MessageBox(nullptr, toUTF16(output).c_str(), L"", MB_OK);
- boost::filesystem::remove(tmp2.c_str());
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with 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 comment(lib, "shlwapi.lib")
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// GPG binaries options
+
+class CDlgGpgBinOpts : public CDlgBase
+{
+ CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR, btn_OK, btn_GENERATE_RANDOM;
+ CCtrlEdit edit_BIN_PATH, edit_HOME_DIR;
+ CCtrlCheck chk_AUTO_EXCHANGE;
+
+public:
+ CDlgGpgBinOpts() :
+ CDlgBase(g_plugin, IDD_BIN_PATH),
+ btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH),
+ btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR),
+ btn_OK(this, ID_OK),
+ btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM),
+ edit_BIN_PATH(this, IDC_BIN_PATH),
+ edit_HOME_DIR(this, IDC_HOME_DIR),
+ chk_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE)
+ {
+ btn_SET_BIN_PATH.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_BIN_PATH);
+ btn_SET_HOME_DIR.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_HOME_DIR);
+ btn_OK.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_OK);
+ btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_GENERATE_RANDOM);
+ }
+
+ bool OnInitDialog() override
+ {
+ CMStringW path;
+ bool gpg_exists = false, lang_exists = false;
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+
+ CMStringW gpg_path(mir_path); gpg_path.Append(L"\\GnuPG\\gpg.exe");
+ CMStringW gpg_lang_path(mir_path); gpg_lang_path.Append(L"\\GnuPG\\gnupg.nls\\en@quot.mo");
+
+ if (boost::filesystem::exists(gpg_path.c_str())) {
+ gpg_exists = true;
+ path = L"GnuPG\\gpg.exe";
+ }
+ else path = gpg_path;
+
+ if (boost::filesystem::exists(gpg_lang_path.c_str()))
+ lang_exists = true;
+ if (gpg_exists && !lang_exists)
+ MessageBox(nullptr, TranslateT("GPG binary found in Miranda folder, but English locale does not exist.\nIt's highly recommended that you place \\gnupg.nls\\en@quot.mo in GnuPG folder under Miranda root.\nWithout this file you may experience many problems with GPG output on non-English systems\nand plugin may completely not work.\nYou have been warned."), TranslateT("Warning"), MB_OK);
+
+ DWORD len = MAX_PATH;
+ bool bad_version = false;
+ {
+ ptrW tmp;
+ if (!gpg_exists) {
+ tmp = g_plugin.getWStringA("szGpgBinPath", (SHGetValueW(HKEY_CURRENT_USER, L"Software\\GNU\\GnuPG", L"gpgProgram", 0, (void *)path.c_str(), &len) == ERROR_SUCCESS) ? path.c_str() : L"");
+ if (tmp[0])
+ if (!boost::filesystem::exists((wchar_t *)tmp))
+ MessageBoxW(nullptr, TranslateT("Wrong GPG binary location found in system.\nPlease choose another location"), TranslateT("Warning"), MB_OK);
+ }
+ else tmp = mir_wstrdup(path.c_str());
+
+ edit_BIN_PATH.SetText(tmp);
+ if (gpg_exists/* && lang_exists*/) {
+ g_plugin.setWString("szGpgBinPath", tmp);
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+ bool _gpg_valid = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = _gpg_valid; //TODO: check this
+ g_plugin.delSetting("szGpgBinPath");
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 != -1) {
+ p1 += mir_strlen("(GnuPG) ");
+ if (params.out[p1] != '1')
+ bad_version = true;
+ }
+ else {
+ bad_version = false;
+ MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Error"), MB_OK);
+ }
+ if (bad_version)
+ MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ }
+ }
+
+ CMStringW tmp(g_plugin.getMStringW("szHomePath"));
+ if (tmp.IsEmpty()) {
+ mir_wstrcat(mir_path, L"\\gpg");
+ if (_waccess(mir_path, 0) != -1) {
+ tmp = mir_path;
+ MessageBoxW(nullptr, TranslateT("\"GPG\" directory found in Miranda root.\nAssuming it's GPG home directory.\nGPG home directory set."), TranslateT("Info"), MB_OK);
+ }
+ else {
+ wstring path_ = _wgetenv(L"APPDATA");
+ path_ += L"\\GnuPG";
+ tmp = path_.c_str();
+ }
+ }
+ edit_HOME_DIR.SetText(!gpg_exists ? tmp : L"gpg");
+
+ // TODO: additional check for write access
+ if (gpg_exists && lang_exists && !bad_version)
+ MessageBox(nullptr, TranslateT("Your GPG version is supported. The language file was found.\nGPG plugin should work fine.\nPress OK to continue."), TranslateT("Info"), MB_OK);
+ chk_AUTO_EXCHANGE.Enable();
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ void InitCheck();
+ InitCheck();
+ }
+
+ void onClick_SET_BIN_PATH(CCtrlButton *)
+ {
+ GetFilePath(L"Choose gpg.exe", "szGpgBinPath", L"*.exe", L"EXE Executables");
+ CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
+ edit_BIN_PATH.SetText(tmp);
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_BIN_PATH.SetText(path);
+ }
+ }
+
+ void onClick_SET_HOME_DIR(CCtrlButton *)
+ {
+ GetFolderPath(L"Set home directory");
+ CMStringW tmp(g_plugin.getMStringW("szHomePath"));
+ edit_HOME_DIR.SetText(tmp);
+
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_HOME_DIR.SetText(path);
+ }
+ }
+
+ void onClick_OK(CCtrlButton *)
+ {
+ if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) {
+ gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText());
+ globals.gpg_valid = true;
+ g_plugin.setByte("FirstRun", 0);
+ this->Hide();
+ this->Close();
+ ShowFirstRunDialog();
+ }
+ }
+
+ void onClick_GENERATE_RANDOM(CCtrlButton *)
+ {
+ if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) {
+ gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText());
+ globals.gpg_valid = true;
+ if (gpg_use_new_random_key(nullptr)) {
+ g_plugin.bAutoExchange = chk_AUTO_EXCHANGE.GetState();
+ globals.gpg_valid = true;
+ g_plugin.setByte("FirstRun", 0);
+ this->Close();
+ }
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int EnumProc(const char *szSetting, void *param)
+{
+ auto *list = (OBJLIST<CMStringA> *)param;
+ if (strchr(szSetting, '(') && strchr(szSetting, ')'))
+ list->insert(new CMStringA(szSetting));
+ return 0;
+}
+
+void FirstRun()
+{
+ if (g_plugin.getByte("CompatLevel") != 1) {
+ OBJLIST<CMStringA> settings(1);
+ db_enum_settings(0, EnumProc, MODULENAME, &settings);
+
+ for (auto &it : settings) {
+ CMStringA newName(*it);
+ int p1 = newName.Find('(');
+ newName.Delete(0, p1+1);
+ int p2 = newName.Find(')');
+ if (p2 == -1)
+ continue;
+ newName.Delete(p2, 1);
+
+ CMStringW val = g_plugin.getMStringW(it->c_str());
+ g_plugin.delSetting(it->c_str());
+ g_plugin.setWString(newName, val);
+ }
+
+ g_plugin.setByte("CompatLevel", 1);
+ }
+
+ if (g_plugin.getByte("FirstRun", 1))
+ (new CDlgGpgBinOpts())->Show();
+}
+
+void InitCheck()
+{
+ // parse gpg output
+ {
+ ptrW current_home(g_plugin.getWStringA("szHomePath", L""));
+ g_plugin.setWString("szHomePath", L""); //we do not need home for gpg binary validation
+ globals.gpg_valid = isGPGValid();
+ g_plugin.setWString("szHomePath", current_home); //return current home dir back
+ }
+ {
+ bool home_dir_access = false, temp_access = false;
+ std::wstring test_path(ptrW(g_plugin.getWStringA("szHomePath", L"")));
+ test_path += L"/";
+ test_path += toUTF16(get_random(13));
+ wfstream test_file;
+ test_file.open(test_path, std::ios::trunc | std::ios::out);
+ if (test_file.is_open() && test_file.good()) {
+ test_file << L"access_test\n";
+ if (test_file.good())
+ home_dir_access = true;
+ test_file.close();
+ boost::filesystem::remove(test_path);
+ }
+
+ test_path = _wgetenv(L"TEMP");
+ test_path += L"/";
+ test_path += toUTF16(get_random(13));
+ test_file.open(test_path, std::ios::trunc | std::ios::out);
+ if (test_file.is_open() && test_file.good()) {
+ test_file << L"access_test\n";
+ if (test_file.good())
+ temp_access = true;
+ test_file.close();
+ boost::filesystem::remove(test_path);
+ }
+ if (!home_dir_access || !temp_access || !globals.gpg_valid) {
+ CMStringW buf;
+ buf.Append(globals.gpg_valid ? TranslateT("GPG binary is set and valid (this is good).\n") : TranslateT("GPG binary unset or invalid (plugin will not work).\n"));
+ buf.Append(home_dir_access ? TranslateT("Home dir write access granted (this is good).\n") : TranslateT("Home dir has no write access (plugin most probably will not work).\n"));
+ buf.Append(temp_access ? TranslateT("Temp dir write access granted (this is good).\n") : TranslateT("Temp dir has no write access (plugin should work, but may have some problems, file transfers will not work)."));
+ if (!globals.gpg_valid)
+ buf.Append(TranslateT("\nGPG will be disabled until you solve these problems"));
+ MessageBox(nullptr, buf, TranslateT("GPG plugin problems"), MB_OK);
+ }
+ if (!globals.gpg_valid)
+ return;
+ globals.gpg_keyexist = isGPGKeyExist();
+
+ wstring::size_type p = 0, p2 = 0;
+
+ gpg_execution_params params;
+ params.addParam(L"--list-secret-keys");
+ params.addParam(L"--batch");
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ _wmkdir(g_plugin.getMStringW("szHomePath") + L"\\tmp");
+ string out(params.out);
+
+ CMStringW wszQuestion;
+ for (auto &pa : Accounts()) {
+ if (StriStr(pa->szModuleName, "metacontacts"))
+ continue;
+ if (StriStr(pa->szModuleName, "weather"))
+ continue;
+
+ std::string acc = pa->szModuleName;
+ acc += "_KeyID";
+ CMStringA keyid = g_plugin.getMStringA(acc.c_str());
+ if (!keyid.IsEmpty()) {
+ wszQuestion = TranslateT("Your secret key with ID: ");
+ keyid = g_plugin.getMStringA("KeyID");
+ if ((p = out.find(keyid)) == string::npos) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" for account ");
+ wszQuestion += pa->tszAccountName;
+ wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ p2 = p;
+ p = out.find("[", p);
+ p2 = out.find("\n", p2);
+ if ((p != std::string::npos) && (p < p2)) {
+ p = out.find("expires:", p);
+ p += mir_strlen("expires:");
+ p++;
+ p2 = out.find("]", p);
+ wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ bool expired = false;
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ wchar_t buf[5];
+ wcsncpy_s(buf, expire_date, _TRUNCATE);
+ int year = _wtoi(buf);
+ if (year < now.date().year())
+ expired = true;
+ else if (year == now.date().year()) {
+ wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
+ int month = _wtoi(buf);
+ if (month < now.date().month())
+ expired = true;
+ else if (month == now.date().month()) {
+ wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
+ unsigned day = _wtoi(buf);
+ if (day <= now.date().day_number())
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" for account ");
+ wszQuestion += pa->tszAccountName;
+ wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion.c_str(), TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ mir_free(expire_date);
+ }
+ }
+ }
+
+ wszQuestion = TranslateT("Your secret key with ID: ");
+ CMStringA keyid(g_plugin.getMStringA("KeyID"));
+ CMStringA key(g_plugin.getMStringA("GPGPubKey"));
+ if (!g_plugin.getByte("FirstRun", 1) && (keyid.IsEmpty() || key.IsEmpty())) {
+ wszQuestion = TranslateT("You didn't set a private key.\nWould you like to set it now?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own private key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ if ((p = out.find(keyid)) == string::npos) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ p2 = p;
+ p = out.find("[", p);
+ p2 = out.find("\n", p2);
+ if ((p != std::string::npos) && (p < p2)) {
+ p = out.find("expires:", p);
+ p += mir_strlen("expires:");
+ p++;
+ p2 = out.find("]", p);
+ wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ bool expired = false;
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ wchar_t buf[5];
+ wcsncpy_s(buf, expire_date, _TRUNCATE);
+ int year = _wtoi(buf);
+ if (year < now.date().year())
+ expired = true;
+ else if (year == now.date().year()) {
+ wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
+ int month = _wtoi(buf);
+ if (month < now.date().month())
+ expired = true;
+ else if (month == now.date().month()) {
+ wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
+ unsigned day = _wtoi(buf);
+ if (day <= now.date().day_number())
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ wszQuestion += keyid;
+ wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?");
+ if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES)
+ ShowFirstRunDialog();
+ }
+ mir_free(expire_date);
+ }
+ // TODO: check for expired key
+ }
+ {
+ CMStringW path(g_plugin.getMStringW("szHomePath"));
+ uint32_t dwFileAttr = GetFileAttributes(path);
+ if (dwFileAttr != INVALID_FILE_ATTRIBUTES) {
+ dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
+ SetFileAttributes(path, dwFileAttr);
+ }
+ }
+}
+
+void ImportKey(MCONTACT hContact, std::wstring new_key)
+{
+ bool for_all_sub = false;
+ if (db_mc_isMeta(hContact)) {
+ if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES)
+ for_all_sub = true;
+
+ if (for_all_sub) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setWString(hcnt, "GPGPubKey", new_key.c_str());
+ }
+ }
+ else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", new_key.c_str());
+ }
+ else g_plugin.setWString(hContact, "GPGPubKey", new_key.c_str());
+
+ // gpg execute block
+ CMStringW tmp2 = g_plugin.getMStringW("szHomePath");
+ tmp2 += L"\\temporary_exported.asc";
+ boost::filesystem::remove(tmp2.c_str());
+
+ CMStringW ptmp;
+ if (db_mc_isMeta(hContact))
+ ptmp = g_plugin.getMStringW(metaGetMostOnline(hContact), "GPGPubKey");
+ else
+ ptmp = g_plugin.getMStringW(hContact, "GPGPubKey");
+
+ wfstream f(tmp2, std::ios::out);
+ f << ptmp.c_str();
+ f.close();
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ string output(params.out);
+ if (db_mc_isMeta(hContact)) {
+ if (for_all_sub) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt) {
+ char *tmp = nullptr;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ g_plugin.setString(hcnt, "KeyID", output.substr(s, s2 - s).c_str());
+ s = output.find(RUS_QUOTE, s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else s += sizeof(RUS_QUOTE) - 1;
+
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find(RUS_ANGLE, s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyMainName", tmp);
+ mir_free(tmp);
+ }
+
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyComment", tmp);
+ mir_free(tmp);
+ s += 3;
+ s2 = output.find(">", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ }
+ else {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp);
+ }
+ }
+ g_plugin.delSetting(hcnt, "bAlwatsTrust");
+ }
+ }
+ }
+ else {
+ char *tmp = nullptr;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyID", output.substr(s, s2 - s).c_str());
+ s = output.find(RUS_QUOTE, s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else s += sizeof(RUS_QUOTE) - 1;
+
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find(RUS_ANGLE, s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", tmp);
+ mir_free(tmp);
+ }
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", tmp);
+ mir_free(tmp);
+ s += 3;
+ s2 = output.find(">", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ }
+ else {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp);
+ }
+ }
+ g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust");
+ }
+ }
+ else {
+ char *tmp = nullptr;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ g_plugin.setString(hContact, "KeyID", output.substr(s, s2 - s).c_str());
+ s = output.find(RUS_QUOTE, s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else s += sizeof(RUS_QUOTE) - 1;
+
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find(RUS_ANGLE, s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyMainName", tmp);
+ mir_free(tmp);
+ }
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyComment", tmp);
+ mir_free(tmp);
+ s += 3;
+ s2 = output.find(">", s);
+ if (s != string::npos && s2 != string::npos) {
+ tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1));
+ mir_strcpy(tmp, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ }
+ else {
+ tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1));
+ mir_strcpy(tmp, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp);
+ }
+ }
+ g_plugin.delSetting(hContact, "bAlwatsTrust");
+ }
+
+ MessageBox(nullptr, toUTF16(output).c_str(), L"", MB_OK);
+ boost::filesystem::remove(tmp2.c_str());
+}
diff --git a/plugins/New_GPG/src/messages.cpp b/plugins/New_GPG/src/messages.cpp
index f79d09361c..c71281696d 100644
--- a/plugins/New_GPG/src/messages.cpp
+++ b/plugins/New_GPG/src/messages.cpp
@@ -1,788 +1,788 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include "stdafx.h"
-
-std::list<HANDLE> sent_msgs;
-
-struct RecvParams
-{
- RecvParams(MCONTACT _p1, std::wstring _p2, const char *_p3, uint32_t _p4) :
- hContact(_p1),
- str(_p2),
- msg(_p3),
- timestamp(_p4)
- {}
-
- MCONTACT hContact;
- std::wstring str;
- std::string msg;
- uint32_t timestamp;
-};
-
-static void RecvMsgSvc_func(RecvParams *param)
-{
- MCONTACT hContact = param->hContact;
- std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact)));
-
- // check for gpg related data
- wstring::size_type s1 = param->str.find(L"-----BEGIN PGP MESSAGE-----");
- wstring::size_type s2 = param->str.find(L"-----END PGP MESSAGE-----");
- if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block
- if (!isContactSecured(hContact)) {
- if (globals.debuglog)
- globals.debuglog << "info: received encrypted message from: " + szScreenName + " with turned off encryption";
- if (MessageBox(nullptr, TranslateT("We received encrypted message from contact with encryption turned off.\nDo you want to turn on encryption for this contact?"), TranslateT("Warning"), MB_YESNO) == IDYES) {
- if (!isContactHaveKey(hContact))
- ShowLoadPublicKeyDialog(hContact, true);
- else {
- g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1);
- setSrmmIcon(hContact);
- }
-
- if (isContactHaveKey(hContact)) {
- g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1);
- setSrmmIcon(hContact);
- }
- }
- else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) {
- HistoryLog(hContact, param->msg.c_str(), param->timestamp);
- delete param;
- return;
- }
- }
- else if (globals.debuglog)
- globals.debuglog << "info: received encrypted message from: " + szScreenName;
- boost::algorithm::erase_all(param->str, "\r");
- s2 += mir_wstrlen(L"-----END PGP MESSAGE-----");
-
- ptrW ptszHomePath(g_plugin.getWStringA("szHomePath", L""));
- wstring encfile = toUTF16(get_random(10));
- wstring decfile = toUTF16(get_random(10));
- {
- wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile;
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path, e);
- }
-
- {
- const int timeout = 5000, step = 100;
- int count = 0;
-
- fstream f(path.c_str(), std::ios::out);
- while (!f.is_open()) {
- ::Sleep(step);
- count += step;
- if (count >= timeout) {
- g_plugin.setByte(hContact, "GPGEncryption", 0);
- setSrmmIcon(hContact);
- globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock";
- delete param;
- return;
- }
- f.open(path.c_str(), std::ios::out);
- }
- char *tmp = mir_u2a(param->str.substr(s1, s2 - s1).c_str());
- f << tmp;
- mir_free(tmp);
- f.close();
- }
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- {
- CMStringA inkeyid = g_plugin.getMStringA(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID");
- CMStringW pass;
- if (!inkeyid.IsEmpty()) {
- string dbsetting = "szKey_";
- dbsetting += inkeyid;
- dbsetting += "_Password";
- pass = g_plugin.getMStringW(dbsetting.c_str());
- if (!pass.IsEmpty() && globals.debuglog)
- globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to decrypt message from " + szScreenName + " with password";
- }
- else {
- pass = g_plugin.getMStringW("szKeyPassword");
- if (!pass.IsEmpty() && globals.debuglog)
- globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + szScreenName + " with password";
- }
- if (!pass.IsEmpty()) {
- params.addParam(L"--passphrase");
- params.addParam(pass.c_str());
- }
- else if (!globals.wszPassword.IsEmpty()) {
- if (globals.debuglog)
- globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName + " with password";
- params.addParam(L"--passphrase");
- params.addParam(globals.wszPassword.c_str());
- }
- else if (globals.debuglog)
- globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + szScreenName + " without password";
- }
-
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e);
- }
-
- params.addParam(L"--output");
- params.addParam(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile);
- params.addParam(L"-d");
- params.addParam(L"-a");
- params.addParam(path);
-
- bool bRes = gpg_launcher(params);
- if (!bRes || params.result == pxNotFound) {
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path, e);
- }
-
- SendErrorMessage(hContact);
- HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
- delete param;
- return;
- }
-
- // TODO: check gpg output for errors
- globals._terminate = false;
-
- string out(params.out);
- while (out.find("public key decryption failed: bad passphrase") != string::npos) {
- if (globals.debuglog)
- globals.debuglog << "info: failed to decrypt message from " + szScreenName + " password needed, trying to get one";
- if (globals._terminate) {
- SendErrorMessage(hContact);
- break;
- }
- {
- // save inkey id
- string::size_type s = out.find(" encrypted with ");
- s = out.find(" ID ", s);
- s += mir_strlen(" ID ");
- g_plugin.setString(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID", out.substr(s, out.find(",", s) - s).c_str());
- }
-
- CDlgKeyPasswordMsgBox(hContact).DoModal();
-
- gpg_execution_params params2;
- params2.aargv = params.aargv;
- if (!globals.wszPassword.IsEmpty()) {
- if (globals.debuglog)
- globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName;
-
- params2.addParam(L"--passphrase");
- params2.addParam(globals.wszPassword.c_str());
- }
-
- bRes = gpg_launcher(params2);
- if (!bRes || params2.result == pxNotFound) {
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path, e);
- }
-
- HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
- SendErrorMessage(hContact);
- delete param;
- return;
- }
- }
-
- out.clear();
- bRes = gpg_launcher(params);
- if (!bRes || params.result == pxNotFound) {
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path, e);
- }
-
- HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
- SendErrorMessage(hContact);
- delete param;
- return;
- }
-
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e);
- }
-
- if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) {
- if (globals.debuglog)
- globals.debuglog << "info: Failed to decrypt GPG encrypted message.";
-
- string str1 = param->msg;
- str1.insert(0, "\n");
- str1.insert(0, TranslateU("Received unencrypted message:"));
-
- HistoryLog(hContact, str1.c_str(), param->timestamp);
- SendErrorMessage(hContact);
- delete param;
- return;
- }
-
- std::string str;
-
- wstring tszDecPath = wstring(ptszHomePath) + L"\\tmp\\" + decfile;
-
- fstream f(tszDecPath.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
- if (f.is_open()) {
- size_t size = f.tellg();
- char *tmp = new char[size + 1];
- f.seekg(0, std::ios::beg);
- f.read(tmp, size);
- tmp[size] = '\0';
-
- str.append(tmp);
- delete[] tmp;
- f.close();
- if (!globals.debuglog) {
- boost::system::error_code ec;
- boost::filesystem::remove(tszDecPath, ec);
- if (ec) {
- //TODO: handle error
- }
- }
- }
-
- if (str.empty()) {
- if (globals.debuglog)
- globals.debuglog << "info: Failed to decrypt GPG encrypted message.";
-
- string szMsg = param->msg;
- szMsg.insert(0, TranslateU("Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n"));
-
- HistoryLog(hContact, param->msg.c_str(), param->timestamp);
- SendErrorMessage(hContact);
- delete param;
- return;
- }
-
- fix_line_term(str);
- if (g_plugin.bAppendTags) {
- str.insert(0, toUTF8(globals.wszInopentag.c_str()));
- str.append(toUTF8(globals.wszInclosetag.c_str()));
- }
-
- HistoryLog(hContact, str.c_str(), param->timestamp);
- delete param;
- return;
- }
- }
-
- if (g_plugin.getByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption"))
- HistoryLog(hContact, param->msg.c_str(), param->timestamp, DBEF_READ);
- else
- HistoryLog(hContact, param->msg.c_str(), param->timestamp);
-
- delete param;
-}
-
-INT_PTR RecvMsgSvc(WPARAM w, LPARAM l)
-{
- CCSDATA *ccs = (CCSDATA*)l;
- if (!ccs)
- return Proto_ChainRecv(w, ccs);
-
- PROTORECVEVENT *pre = (PROTORECVEVENT*)(ccs->lParam);
- if (!pre)
- return Proto_ChainRecv(w, ccs);
-
- char *msg = pre->szMessage;
- if (!msg)
- return Proto_ChainRecv(w, ccs);
-
- if (db_mc_isMeta(ccs->hContact)) {
- if (!strstr(msg, "-----BEGIN PGP MESSAGE-----"))
- return Proto_ChainRecv(w, ccs);
- else {
- if (globals.debuglog)
- globals.debuglog << "info: blocked pgp message to metacontact:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
- return 0;
- }
- }
-
- wstring str = toUTF16(msg);
- size_t s1, s2;
- if (g_plugin.bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) {
- if (globals.debuglog)
- globals.debuglog << "info(autoexchange): parsing key response:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
- s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----");
- s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
- if (s1 != wstring::npos && s2 != wstring::npos) {
- if (globals.debuglog)
- globals.debuglog << "info(autoexchange): found pubkey block:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
- s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
- g_plugin.setWString(ccs->hContact, "GPGPubKey", str.substr(s1, s2 - s1).c_str());
- {
- // gpg execute block
- CMStringW tmp2(g_plugin.getMStringW("szHomePath"));
- tmp2 += L"\\";
- tmp2 += get_random(5).c_str();
- tmp2 += L".asc";
-
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(tmp2.c_str(), e);
- }
- wfstream f(tmp2, std::ios::out);
- {
- const int timeout = 5000, step = 100;
- int count = 0;
- while (!f.is_open()) {
- ::Sleep(step);
- count += step;
- if (count >= timeout) {
- g_plugin.setByte(ccs->hContact, "GPGEncryption", 0);
- setSrmmIcon(ccs->hContact);
- globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock";
- return 1;
- }
- f.open(tmp2, std::ios::out);
- }
- }
- f << g_plugin.getMStringW(ccs->hContact, "GPGPubKey").c_str();
- f.close();
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--import");
- params.addParam(tmp2.c_str());
- if (!gpg_launcher(params))
- return 1;
-
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(tmp2.c_str(), e);
- }
- if (params.result == pxNotFound)
- return 1;
-
- string output(params.out);
- s1 = output.find("gpg: key ") + mir_strlen("gpg: key ");
- s2 = output.find(":", s1);
- g_plugin.setString(ccs->hContact, "KeyID", output.substr(s1, s2 - s1).c_str());
- s2 += 2;
- s1 = output.find(RUS_QUOTE, s2);
- if (s1 == string::npos) {
- s1 = output.find("\"", s2);
- s1 += 1;
- }
- else s1 += sizeof(RUS_QUOTE) - 1;
-
- if ((s2 = output.find("(", s1)) == string::npos)
- s2 = output.find("<", s1);
- else if (s2 > output.find("<", s1))
- s2 = output.find("<", s1);
-
- char *tmp = (char*)mir_alloc(output.substr(s1, s2 - s1 - 1).length() + 1);
- mir_strcpy(tmp, output.substr(s1, s2 - s1 - 1).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(ccs->hContact, "KeyMainName", tmp);
- mir_free(tmp);
- if ((s1 = output.find(")", s2)) == string::npos)
- s1 = output.find(">", s2);
- else if (s1 > output.find(">", s2))
- s1 = output.find(">", s2);
- s2++;
- if (output[s1] == ')') {
- tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1);
- mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(ccs->hContact, "KeyComment", tmp);
- mir_free(tmp);
- s1 += 3;
- s2 = output.find(">", s1);
- tmp = (char*)mir_alloc(output.substr(s1, s2 - s1).length() + 1);
- mir_strcpy(tmp, output.substr(s1, s2 - s1).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(ccs->hContact, "KeyMainEmail", tmp);
- mir_free(tmp);
- }
- else {
- tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1);
- mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str());
- mir_utf8decode(tmp, nullptr);
- g_plugin.setString(ccs->hContact, "KeyMainEmail", output.substr(s2, s1 - s2).c_str());
- mir_free(tmp);
- }
- g_plugin.setByte(ccs->hContact, "GPGEncryption", 1);
- g_plugin.setByte(ccs->hContact, "bAlwatsTrust", 1);
- setSrmmIcon(ccs->hContact);
- if (db_mc_isSub(ccs->hContact))
- setSrmmIcon(db_mc_getMeta(ccs->hContact));
-
- HistoryLog(ccs->hContact, "PGP Encryption turned on by key autoexchange feature");
- }
- return 1;
- }
- }
- if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) == wstring::npos) || ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) == wstring::npos)) {
- s2 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----");
- s1 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
- }
- if ((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key
- if (globals.debuglog)
- globals.debuglog << "info: received key from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
- s1 = 0;
- while ((s1 = str.find(L"\r", s1)) != wstring::npos)
- str.erase(s1, 1);
- if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos))
- s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
- else if (((s2 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos))
- s2 += mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----");
- CDlgNewKey *d = new CDlgNewKey(ccs->hContact, str.substr(s1, s2 - s1));
- d->Show();
- HistoryLog(ccs->hContact, msg);
- return 0;
- }
-
- if (g_plugin.bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && globals.gpg_valid && globals.gpg_keyexist) {
- if (globals.debuglog)
- globals.debuglog << "info(autoexchange): received key request from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
-
- CMStringA tmp(g_plugin.getMStringA("GPGPubKey"));
- if (!tmp.IsEmpty()) {
- int enc_state = g_plugin.getByte(ccs->hContact, "GPGEncryption");
- if (enc_state)
- g_plugin.setByte(ccs->hContact, "GPGEncryption", 0);
-
- string str1 = "-----PGP KEY RESPONSE-----";
- str1.append(tmp);
- ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str());
- if (enc_state)
- g_plugin.setByte(ccs->hContact, "GPGEncryption", 1);
- }
- return 0;
- }
- else if (!isContactHaveKey(ccs->hContact) && g_plugin.bAutoExchange && globals.gpg_valid && globals.gpg_keyexist) {
- char *proto = Proto_GetBaseAccountName(ccs->hContact);
- ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", ""));
- if (jid[0]) {
- for (auto p : globals.Accounts) {
- ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
- if (caps) {
- string str1;
- for (int i = 0;; i++) {
- str1.push_back(caps[i]);
- if (caps[i] == '\0')
- if (caps[i + 1] == '\0')
- break;
- }
-
- if (str1.find("GPG_Key_Auto_Exchange:0") != string::npos) {
- ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----");
- return 0;
- }
- }
- }
- }
- }
-
- if (!strstr(msg, "-----BEGIN PGP MESSAGE-----"))
- return Proto_ChainRecv(w, ccs);
-
- mir_forkThread<RecvParams>(RecvMsgSvc_func, new RecvParams(ccs->hContact, str, msg, pre->timestamp));
- return 0;
-}
-
-void SendMsgSvc_func(MCONTACT hContact, char *msg, uint32_t flags)
-{
- string str = msg;
- if (g_plugin.bStripTags && g_plugin.bAppendTags) {
- if (globals.debuglog)
- globals.debuglog << "info: stripping tags in outgoing message, name: " + toUTF8(Clist_GetContactDisplayName(hContact));
- strip_tags(str);
- }
-
-LBL_Relaunch:
- wstring file = toUTF16(get_random(10));
- gpg_execution_params params;
- {
- CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID"));
- if (tmp.IsEmpty()) {
- HistoryLog(hContact, "Failed to encrypt message with GPG (not found key for encryption in db", DBEF_SENT);
- ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
- return;
- }
-
- if (g_plugin.getByte(hContact, "bAlwaysTrust", 0)) {
- params.addParam(L"--trust-model");
- params.addParam(L"always");
- }
- params.addParam(L"--batch");
- params.addParam(L"--yes");
- params.addParam(L"-eatr");
- params.addParam(_A2T(tmp).get());
- }
-
- CMStringW path(g_plugin.getMStringW("szHomePath"));
- path += L"\\tmp\\";
- path += file.c_str();
- params.addParam(path.c_str());
-
- const int timeout = 5000, step = 100;
- int count = 0;
- {
- fstream f(path.c_str(), std::ios::out);
- while (!f.is_open()) {
- ::Sleep(step);
- count += step;
- if (count >= timeout) {
- g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption
- setSrmmIcon(hContact);
- globals.debuglog << "info: failed to create temporary file for encryption, disabling encryption to avoid deadlock";
- break;
- }
- f.open(path.c_str(), std::ios::out);
- }
- if (count < timeout) {
- f.write(str.c_str(), str.size());
- f.close();
- }
- }
-
- if (!gpg_launcher(params)) {
- ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
- return;
- }
-
- if (params.result == pxNotFound) {
- ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
- return;
- }
-
- if (params.out.Find("There is no assurance this key belongs to the named user") != -1) {
- if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO))
- return;
-
- g_plugin.setByte(hContact, "bAlwaysTrust", 1);
- params.aargv.clear();
- goto LBL_Relaunch;
- }
-
- if (params.out.Find("usage: ") != -1) {
- MessageBox(nullptr, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK);
- //mir_free(msg);
- ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path.c_str(), e);
- }
- return;
- }
-
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path.c_str(), e);
- }
-
- path += L".asc";
- fstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
- count = 0;
- while (!f.is_open()) {
- ::Sleep(step);
- f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
- count += step;
- if (count >= timeout) {
- g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption
- setSrmmIcon(hContact);
- globals.debuglog << "info: gpg failed to encrypt message, disabling encryption to avoid deadlock";
- break;
- }
- }
-
- str.clear();
- if (f.is_open()) {
- size_t size = f.tellg();
- char *tmp = new char[size + 1];
- f.seekg(0, std::ios::beg);
- f.read(tmp, size);
- tmp[size] = '\0';
- str.append(tmp);
- delete[] tmp;
- f.close();
- if (!globals.debuglog) {
- boost::system::error_code e;
- boost::filesystem::remove(path.c_str(), e);
- }
- }
-
- if (str.empty()) {
- HistoryLog(hContact, "Failed to encrypt message with GPG", DBEF_SENT);
- if (globals.debuglog)
- globals.debuglog << "info: Failed to encrypt message with GPG";
- ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
- return;
- }
-
- string str_event = msg;
- if (g_plugin.bAppendTags) {
- str_event.insert(0, toUTF8(globals.wszOutopentag.c_str()));
- str_event.append(toUTF8(globals.wszOutclosetag.c_str()));
- }
-
- if (globals.debuglog)
- globals.debuglog << "adding event to contact: " + toUTF8(Clist_GetContactDisplayName(hContact)) + " on send message.";
-
- fix_line_term(str);
- sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)str.c_str()));
-}
-
-INT_PTR SendMsgSvc(WPARAM w, LPARAM l)
-{
- CCSDATA *ccs = (CCSDATA*)l;
- if (!ccs)
- return Proto_ChainSend(w, ccs);
-
- if (!ccs->lParam)
- return Proto_ChainSend(w, ccs);
-
- std::string szScreenName(toUTF8(Clist_GetContactDisplayName(ccs->hContact)));
- char *msg = (char*)ccs->lParam;
- if (!msg) {
- if (globals.debuglog)
- globals.debuglog << "info: failed to get message data, name: " + szScreenName;
- return Proto_ChainSend(w, ccs);
- }
-
- if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) {
- if (globals.debuglog)
- globals.debuglog << "info: encrypted message, let it go, name: " + szScreenName;
- return Proto_ChainSend(w, ccs);
- }
-
- if (globals.debuglog)
- globals.debuglog << "info: contact have key, name: " + szScreenName;
-
- if (globals.debuglog && db_mc_isMeta(ccs->hContact))
- globals.debuglog << "info: protocol is metacontacts, name: " + szScreenName;
-
- if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) {
- if (globals.debuglog)
- globals.debuglog << "info: contact not secured, name: " + szScreenName;
- return Proto_ChainSend(w, ccs);
- }
-
- ProtoBroadcastAsync(Proto_GetBaseAccountName(ccs->hContact), ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)777);
- return 777;
-}
-
-int HookSendMsg(WPARAM w, LPARAM l)
-{
- if (!l)
- return 0;
-
- DBEVENTINFO *dbei = (DBEVENTINFO*)l;
- if (dbei->eventType != EVENTTYPE_MESSAGE || (dbei->flags & DBEF_READ))
- return 0;
-
- MCONTACT hContact = (MCONTACT)w;
- std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact)));
-
- if (dbei->flags & DBEF_SENT) {
- if (isContactSecured(hContact) && strstr((char*)dbei->pBlob, "-----BEGIN PGP MESSAGE-----")) //our service data, can be double added by metacontacts e.w.c.
- {
- if (globals.debuglog)
- globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName;
- return 1;
- }
- if (g_plugin.bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history
- {
- if (globals.debuglog)
- globals.debuglog << "info(send handler): block pgp key request/response event, name: " + szScreenName;
- return 1;
- }
- }
-
- if (db_mc_isMeta(hContact))
- return 0;
-
- if (!isContactHaveKey(hContact)) {
- if (globals.debuglog)
- globals.debuglog << "info: contact have not key, name: " + szScreenName;
-
- if (g_plugin.bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && globals.gpg_valid) {
- if (globals.debuglog)
- globals.debuglog << "info: checking for autoexchange possibility, name: " + szScreenName;
-
- LPSTR proto = Proto_GetBaseAccountName(hContact);
- ptrA jid(db_get_utfa(hContact, proto, "jid", ""));
- if (jid[0]) {
- if (globals.debuglog)
- globals.debuglog << "info(autoexchange): protocol looks like jabber, name: " + szScreenName;
- for (auto p : globals.Accounts) {
- ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
- if (caps) {
- string str;
- for (int i = 0;; i++) {
- str.push_back(caps[i]);
- if (caps[i] == '\0')
- if (caps[i + 1] == '\0')
- break;
- }
-
- if (str.find("GPG_Key_Auto_Exchange:0") != string::npos) {
- if (globals.debuglog)
- globals.debuglog << "info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + szScreenName;
- ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----");
- globals.hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob);
- mir_forkthread(send_encrypted_msgs_thread, (void*)hContact);
- return 0;
- }
- }
- }
- }
- }
- else return 0;
- }
-
- if (isContactSecured(hContact) && (dbei->flags & DBEF_SENT)) //aggressive outgoing events filtering
- {
- SendMsgSvc_func(hContact, (char*)dbei->pBlob, 0);
- //TODO: handle errors somehow ...
- if (g_plugin.bAppendTags) {
- string str_event = (char*)dbei->pBlob;
- //mir_free(dbei->pBlob);
- str_event.insert(0, toUTF8(globals.wszOutopentag.c_str()));
- str_event.append(toUTF8(globals.wszOutclosetag.c_str()));
- dbei->pBlob = (uint8_t*)mir_strdup(str_event.c_str());
- dbei->cbBlob = (uint32_t)str_event.length() + 1;
- }
-
- return 0;
- }
-
- if (!isContactSecured(hContact)) {
- if (globals.debuglog)
- globals.debuglog << "event message: \"" + string((char*)dbei->pBlob) + "\" passed event filter, contact " + szScreenName + " is unsecured";
- return 0;
- }
-
- if (!(dbei->flags & DBEF_SENT) && db_mc_isMeta((MCONTACT)w)) {
- char tmp[29];
- strncpy(tmp, (char*)dbei->pBlob, 27);
- tmp[28] = '\0';
- if (strstr(tmp, "-----BEGIN PGP MESSAGE-----")) {
- if (globals.debuglog)
- globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName;
- return 1;
- }
- }
- return 0;
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+std::list<HANDLE> sent_msgs;
+
+struct RecvParams
+{
+ RecvParams(MCONTACT _p1, std::wstring _p2, const char *_p3, uint32_t _p4) :
+ hContact(_p1),
+ str(_p2),
+ msg(_p3),
+ timestamp(_p4)
+ {}
+
+ MCONTACT hContact;
+ std::wstring str;
+ std::string msg;
+ uint32_t timestamp;
+};
+
+static void RecvMsgSvc_func(RecvParams *param)
+{
+ MCONTACT hContact = param->hContact;
+ std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact)));
+
+ // check for gpg related data
+ wstring::size_type s1 = param->str.find(L"-----BEGIN PGP MESSAGE-----");
+ wstring::size_type s2 = param->str.find(L"-----END PGP MESSAGE-----");
+ if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block
+ if (!isContactSecured(hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: received encrypted message from: " + szScreenName + " with turned off encryption";
+ if (MessageBox(nullptr, TranslateT("We received encrypted message from contact with encryption turned off.\nDo you want to turn on encryption for this contact?"), TranslateT("Warning"), MB_YESNO) == IDYES) {
+ if (!isContactHaveKey(hContact))
+ ShowLoadPublicKeyDialog(hContact, true);
+ else {
+ g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ }
+
+ if (isContactHaveKey(hContact)) {
+ g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ }
+ }
+ else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) {
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp);
+ delete param;
+ return;
+ }
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: received encrypted message from: " + szScreenName;
+ boost::algorithm::erase_all(param->str, "\r");
+ s2 += mir_wstrlen(L"-----END PGP MESSAGE-----");
+
+ ptrW ptszHomePath(g_plugin.getWStringA("szHomePath", L""));
+ wstring encfile = toUTF16(get_random(10));
+ wstring decfile = toUTF16(get_random(10));
+ {
+ wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile;
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ {
+ const int timeout = 5000, step = 100;
+ int count = 0;
+
+ fstream f(path.c_str(), std::ios::out);
+ while (!f.is_open()) {
+ ::Sleep(step);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ setSrmmIcon(hContact);
+ globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock";
+ delete param;
+ return;
+ }
+ f.open(path.c_str(), std::ios::out);
+ }
+ char *tmp = mir_u2a(param->str.substr(s1, s2 - s1).c_str());
+ f << tmp;
+ mir_free(tmp);
+ f.close();
+ }
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ {
+ CMStringA inkeyid = g_plugin.getMStringA(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID");
+ CMStringW pass;
+ if (!inkeyid.IsEmpty()) {
+ string dbsetting = "szKey_";
+ dbsetting += inkeyid;
+ dbsetting += "_Password";
+ pass = g_plugin.getMStringW(dbsetting.c_str());
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to decrypt message from " + szScreenName + " with password";
+ }
+ else {
+ pass = g_plugin.getMStringW("szKeyPassword");
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + szScreenName + " with password";
+ }
+ if (!pass.IsEmpty()) {
+ params.addParam(L"--passphrase");
+ params.addParam(pass.c_str());
+ }
+ else if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName + " with password";
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + szScreenName + " without password";
+ }
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e);
+ }
+
+ params.addParam(L"--output");
+ params.addParam(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile);
+ params.addParam(L"-d");
+ params.addParam(L"-a");
+ params.addParam(path);
+
+ bool bRes = gpg_launcher(params);
+ if (!bRes || params.result == pxNotFound) {
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ SendErrorMessage(hContact);
+ HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
+ delete param;
+ return;
+ }
+
+ // TODO: check gpg output for errors
+ globals._terminate = false;
+
+ string out(params.out);
+ while (out.find("public key decryption failed: bad passphrase") != string::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info: failed to decrypt message from " + szScreenName + " password needed, trying to get one";
+ if (globals._terminate) {
+ SendErrorMessage(hContact);
+ break;
+ }
+ {
+ // save inkey id
+ string::size_type s = out.find(" encrypted with ");
+ s = out.find(" ID ", s);
+ s += mir_strlen(" ID ");
+ g_plugin.setString(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID", out.substr(s, out.find(",", s) - s).c_str());
+ }
+
+ CDlgKeyPasswordMsgBox(hContact).DoModal();
+
+ gpg_execution_params params2;
+ params2.aargv = params.aargv;
+ if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName;
+
+ params2.addParam(L"--passphrase");
+ params2.addParam(globals.wszPassword.c_str());
+ }
+
+ bRes = gpg_launcher(params2);
+ if (!bRes || params2.result == pxNotFound) {
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+ }
+
+ out.clear();
+ bRes = gpg_launcher(params);
+ if (!bRes || params.result == pxNotFound) {
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path, e);
+ }
+
+ HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e);
+ }
+
+ if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to decrypt GPG encrypted message.";
+
+ string str1 = param->msg;
+ str1.insert(0, "\n");
+ str1.insert(0, TranslateU("Received unencrypted message:"));
+
+ HistoryLog(hContact, str1.c_str(), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+
+ std::string str;
+
+ wstring tszDecPath = wstring(ptszHomePath) + L"\\tmp\\" + decfile;
+
+ fstream f(tszDecPath.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ if (f.is_open()) {
+ size_t size = f.tellg();
+ char *tmp = new char[size + 1];
+ f.seekg(0, std::ios::beg);
+ f.read(tmp, size);
+ tmp[size] = '\0';
+
+ str.append(tmp);
+ delete[] tmp;
+ f.close();
+ if (!globals.debuglog) {
+ boost::system::error_code ec;
+ boost::filesystem::remove(tszDecPath, ec);
+ if (ec) {
+ //TODO: handle error
+ }
+ }
+ }
+
+ if (str.empty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to decrypt GPG encrypted message.";
+
+ string szMsg = param->msg;
+ szMsg.insert(0, TranslateU("Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n"));
+
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp);
+ SendErrorMessage(hContact);
+ delete param;
+ return;
+ }
+
+ fix_line_term(str);
+ if (g_plugin.bAppendTags) {
+ str.insert(0, toUTF8(globals.wszInopentag.c_str()));
+ str.append(toUTF8(globals.wszInclosetag.c_str()));
+ }
+
+ HistoryLog(hContact, str.c_str(), param->timestamp);
+ delete param;
+ return;
+ }
+ }
+
+ if (g_plugin.getByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption"))
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp, DBEF_READ);
+ else
+ HistoryLog(hContact, param->msg.c_str(), param->timestamp);
+
+ delete param;
+}
+
+INT_PTR RecvMsgSvc(WPARAM w, LPARAM l)
+{
+ CCSDATA *ccs = (CCSDATA*)l;
+ if (!ccs)
+ return Proto_ChainRecv(w, ccs);
+
+ PROTORECVEVENT *pre = (PROTORECVEVENT*)(ccs->lParam);
+ if (!pre)
+ return Proto_ChainRecv(w, ccs);
+
+ char *msg = pre->szMessage;
+ if (!msg)
+ return Proto_ChainRecv(w, ccs);
+
+ if (db_mc_isMeta(ccs->hContact)) {
+ if (!strstr(msg, "-----BEGIN PGP MESSAGE-----"))
+ return Proto_ChainRecv(w, ccs);
+ else {
+ if (globals.debuglog)
+ globals.debuglog << "info: blocked pgp message to metacontact:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ return 0;
+ }
+ }
+
+ wstring str = toUTF16(msg);
+ size_t s1, s2;
+ if (g_plugin.bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): parsing key response:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----");
+ s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (s1 != wstring::npos && s2 != wstring::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): found pubkey block:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
+ g_plugin.setWString(ccs->hContact, "GPGPubKey", str.substr(s1, s2 - s1).c_str());
+ {
+ // gpg execute block
+ CMStringW tmp2(g_plugin.getMStringW("szHomePath"));
+ tmp2 += L"\\";
+ tmp2 += get_random(5).c_str();
+ tmp2 += L".asc";
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(tmp2.c_str(), e);
+ }
+ wfstream f(tmp2, std::ios::out);
+ {
+ const int timeout = 5000, step = 100;
+ int count = 0;
+ while (!f.is_open()) {
+ ::Sleep(step);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 0);
+ setSrmmIcon(ccs->hContact);
+ globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock";
+ return 1;
+ }
+ f.open(tmp2, std::ios::out);
+ }
+ }
+ f << g_plugin.getMStringW(ccs->hContact, "GPGPubKey").c_str();
+ f.close();
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ return 1;
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(tmp2.c_str(), e);
+ }
+ if (params.result == pxNotFound)
+ return 1;
+
+ string output(params.out);
+ s1 = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ s2 = output.find(":", s1);
+ g_plugin.setString(ccs->hContact, "KeyID", output.substr(s1, s2 - s1).c_str());
+ s2 += 2;
+ s1 = output.find(RUS_QUOTE, s2);
+ if (s1 == string::npos) {
+ s1 = output.find("\"", s2);
+ s1 += 1;
+ }
+ else s1 += sizeof(RUS_QUOTE) - 1;
+
+ if ((s2 = output.find("(", s1)) == string::npos)
+ s2 = output.find("<", s1);
+ else if (s2 > output.find("<", s1))
+ s2 = output.find("<", s1);
+
+ char *tmp = (char*)mir_alloc(output.substr(s1, s2 - s1 - 1).length() + 1);
+ mir_strcpy(tmp, output.substr(s1, s2 - s1 - 1).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyMainName", tmp);
+ mir_free(tmp);
+ if ((s1 = output.find(")", s2)) == string::npos)
+ s1 = output.find(">", s2);
+ else if (s1 > output.find(">", s2))
+ s1 = output.find(">", s2);
+ s2++;
+ if (output[s1] == ')') {
+ tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1);
+ mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyComment", tmp);
+ mir_free(tmp);
+ s1 += 3;
+ s2 = output.find(">", s1);
+ tmp = (char*)mir_alloc(output.substr(s1, s2 - s1).length() + 1);
+ mir_strcpy(tmp, output.substr(s1, s2 - s1).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyMainEmail", tmp);
+ mir_free(tmp);
+ }
+ else {
+ tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1);
+ mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str());
+ mir_utf8decode(tmp, nullptr);
+ g_plugin.setString(ccs->hContact, "KeyMainEmail", output.substr(s2, s1 - s2).c_str());
+ mir_free(tmp);
+ }
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 1);
+ g_plugin.setByte(ccs->hContact, "bAlwatsTrust", 1);
+ setSrmmIcon(ccs->hContact);
+ if (db_mc_isSub(ccs->hContact))
+ setSrmmIcon(db_mc_getMeta(ccs->hContact));
+
+ HistoryLog(ccs->hContact, "PGP Encryption turned on by key autoexchange feature");
+ }
+ return 1;
+ }
+ }
+ if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) == wstring::npos) || ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) == wstring::npos)) {
+ s2 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----");
+ s1 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
+ }
+ if ((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key
+ if (globals.debuglog)
+ globals.debuglog << "info: received key from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+ s1 = 0;
+ while ((s1 = str.find(L"\r", s1)) != wstring::npos)
+ str.erase(s1, 1);
+ if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos))
+ s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
+ else if (((s2 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos))
+ s2 += mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----");
+ CDlgNewKey *d = new CDlgNewKey(ccs->hContact, str.substr(s1, s2 - s1));
+ d->Show();
+ HistoryLog(ccs->hContact, msg);
+ return 0;
+ }
+
+ if (g_plugin.bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && globals.gpg_valid && globals.gpg_keyexist) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): received key request from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact));
+
+ CMStringA tmp(g_plugin.getMStringA("GPGPubKey"));
+ if (!tmp.IsEmpty()) {
+ int enc_state = g_plugin.getByte(ccs->hContact, "GPGEncryption");
+ if (enc_state)
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 0);
+
+ string str1 = "-----PGP KEY RESPONSE-----";
+ str1.append(tmp);
+ ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str());
+ if (enc_state)
+ g_plugin.setByte(ccs->hContact, "GPGEncryption", 1);
+ }
+ return 0;
+ }
+ else if (!isContactHaveKey(ccs->hContact) && g_plugin.bAutoExchange && globals.gpg_valid && globals.gpg_keyexist) {
+ char *proto = Proto_GetBaseAccountName(ccs->hContact);
+ ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", ""));
+ if (jid[0]) {
+ for (auto p : globals.Accounts) {
+ ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
+ if (caps) {
+ string str1;
+ for (int i = 0;; i++) {
+ str1.push_back(caps[i]);
+ if (caps[i] == '\0')
+ if (caps[i + 1] == '\0')
+ break;
+ }
+
+ if (str1.find("GPG_Key_Auto_Exchange:0") != string::npos) {
+ ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----");
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ if (!strstr(msg, "-----BEGIN PGP MESSAGE-----"))
+ return Proto_ChainRecv(w, ccs);
+
+ mir_forkThread<RecvParams>(RecvMsgSvc_func, new RecvParams(ccs->hContact, str, msg, pre->timestamp));
+ return 0;
+}
+
+void SendMsgSvc_func(MCONTACT hContact, char *msg, uint32_t flags)
+{
+ string str = msg;
+ if (g_plugin.bStripTags && g_plugin.bAppendTags) {
+ if (globals.debuglog)
+ globals.debuglog << "info: stripping tags in outgoing message, name: " + toUTF8(Clist_GetContactDisplayName(hContact));
+ strip_tags(str);
+ }
+
+LBL_Relaunch:
+ wstring file = toUTF16(get_random(10));
+ gpg_execution_params params;
+ {
+ CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID"));
+ if (tmp.IsEmpty()) {
+ HistoryLog(hContact, "Failed to encrypt message with GPG (not found key for encryption in db", DBEF_SENT);
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ if (g_plugin.getByte(hContact, "bAlwaysTrust", 0)) {
+ params.addParam(L"--trust-model");
+ params.addParam(L"always");
+ }
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"-eatr");
+ params.addParam(_A2T(tmp).get());
+ }
+
+ CMStringW path(g_plugin.getMStringW("szHomePath"));
+ path += L"\\tmp\\";
+ path += file.c_str();
+ params.addParam(path.c_str());
+
+ const int timeout = 5000, step = 100;
+ int count = 0;
+ {
+ fstream f(path.c_str(), std::ios::out);
+ while (!f.is_open()) {
+ ::Sleep(step);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption
+ setSrmmIcon(hContact);
+ globals.debuglog << "info: failed to create temporary file for encryption, disabling encryption to avoid deadlock";
+ break;
+ }
+ f.open(path.c_str(), std::ios::out);
+ }
+ if (count < timeout) {
+ f.write(str.c_str(), str.size());
+ f.close();
+ }
+ }
+
+ if (!gpg_launcher(params)) {
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ if (params.result == pxNotFound) {
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ if (params.out.Find("There is no assurance this key belongs to the named user") != -1) {
+ if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO))
+ return;
+
+ g_plugin.setByte(hContact, "bAlwaysTrust", 1);
+ params.aargv.clear();
+ goto LBL_Relaunch;
+ }
+
+ if (params.out.Find("usage: ") != -1) {
+ MessageBox(nullptr, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK);
+ //mir_free(msg);
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path.c_str(), e);
+ }
+ return;
+ }
+
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path.c_str(), e);
+ }
+
+ path += L".asc";
+ fstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ count = 0;
+ while (!f.is_open()) {
+ ::Sleep(step);
+ f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ count += step;
+ if (count >= timeout) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption
+ setSrmmIcon(hContact);
+ globals.debuglog << "info: gpg failed to encrypt message, disabling encryption to avoid deadlock";
+ break;
+ }
+ }
+
+ str.clear();
+ if (f.is_open()) {
+ size_t size = f.tellg();
+ char *tmp = new char[size + 1];
+ f.seekg(0, std::ios::beg);
+ f.read(tmp, size);
+ tmp[size] = '\0';
+ str.append(tmp);
+ delete[] tmp;
+ f.close();
+ if (!globals.debuglog) {
+ boost::system::error_code e;
+ boost::filesystem::remove(path.c_str(), e);
+ }
+ }
+
+ if (str.empty()) {
+ HistoryLog(hContact, "Failed to encrypt message with GPG", DBEF_SENT);
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to encrypt message with GPG";
+ ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg);
+ return;
+ }
+
+ string str_event = msg;
+ if (g_plugin.bAppendTags) {
+ str_event.insert(0, toUTF8(globals.wszOutopentag.c_str()));
+ str_event.append(toUTF8(globals.wszOutclosetag.c_str()));
+ }
+
+ if (globals.debuglog)
+ globals.debuglog << "adding event to contact: " + toUTF8(Clist_GetContactDisplayName(hContact)) + " on send message.";
+
+ fix_line_term(str);
+ sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)str.c_str()));
+}
+
+INT_PTR SendMsgSvc(WPARAM w, LPARAM l)
+{
+ CCSDATA *ccs = (CCSDATA*)l;
+ if (!ccs)
+ return Proto_ChainSend(w, ccs);
+
+ if (!ccs->lParam)
+ return Proto_ChainSend(w, ccs);
+
+ std::string szScreenName(toUTF8(Clist_GetContactDisplayName(ccs->hContact)));
+ char *msg = (char*)ccs->lParam;
+ if (!msg) {
+ if (globals.debuglog)
+ globals.debuglog << "info: failed to get message data, name: " + szScreenName;
+ return Proto_ChainSend(w, ccs);
+ }
+
+ if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) {
+ if (globals.debuglog)
+ globals.debuglog << "info: encrypted message, let it go, name: " + szScreenName;
+ return Proto_ChainSend(w, ccs);
+ }
+
+ if (globals.debuglog)
+ globals.debuglog << "info: contact have key, name: " + szScreenName;
+
+ if (globals.debuglog && db_mc_isMeta(ccs->hContact))
+ globals.debuglog << "info: protocol is metacontacts, name: " + szScreenName;
+
+ if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: contact not secured, name: " + szScreenName;
+ return Proto_ChainSend(w, ccs);
+ }
+
+ ProtoBroadcastAsync(Proto_GetBaseAccountName(ccs->hContact), ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)777);
+ return 777;
+}
+
+int HookSendMsg(WPARAM w, LPARAM l)
+{
+ if (!l)
+ return 0;
+
+ DBEVENTINFO *dbei = (DBEVENTINFO*)l;
+ if (dbei->eventType != EVENTTYPE_MESSAGE || (dbei->flags & DBEF_READ))
+ return 0;
+
+ MCONTACT hContact = (MCONTACT)w;
+ std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact)));
+
+ if (dbei->flags & DBEF_SENT) {
+ if (isContactSecured(hContact) && strstr((char*)dbei->pBlob, "-----BEGIN PGP MESSAGE-----")) //our service data, can be double added by metacontacts e.w.c.
+ {
+ if (globals.debuglog)
+ globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName;
+ return 1;
+ }
+ if (g_plugin.bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history
+ {
+ if (globals.debuglog)
+ globals.debuglog << "info(send handler): block pgp key request/response event, name: " + szScreenName;
+ return 1;
+ }
+ }
+
+ if (db_mc_isMeta(hContact))
+ return 0;
+
+ if (!isContactHaveKey(hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: contact have not key, name: " + szScreenName;
+
+ if (g_plugin.bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && globals.gpg_valid) {
+ if (globals.debuglog)
+ globals.debuglog << "info: checking for autoexchange possibility, name: " + szScreenName;
+
+ LPSTR proto = Proto_GetBaseAccountName(hContact);
+ ptrA jid(db_get_utfa(hContact, proto, "jid", ""));
+ if (jid[0]) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange): protocol looks like jabber, name: " + szScreenName;
+ for (auto p : globals.Accounts) {
+ ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
+ if (caps) {
+ string str;
+ for (int i = 0;; i++) {
+ str.push_back(caps[i]);
+ if (caps[i] == '\0')
+ if (caps[i + 1] == '\0')
+ break;
+ }
+
+ if (str.find("GPG_Key_Auto_Exchange:0") != string::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + szScreenName;
+ ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----");
+ globals.hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob);
+ mir_forkthread(send_encrypted_msgs_thread, (void*)hContact);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ else return 0;
+ }
+
+ if (isContactSecured(hContact) && (dbei->flags & DBEF_SENT)) //aggressive outgoing events filtering
+ {
+ SendMsgSvc_func(hContact, (char*)dbei->pBlob, 0);
+ //TODO: handle errors somehow ...
+ if (g_plugin.bAppendTags) {
+ string str_event = (char*)dbei->pBlob;
+ //mir_free(dbei->pBlob);
+ str_event.insert(0, toUTF8(globals.wszOutopentag.c_str()));
+ str_event.append(toUTF8(globals.wszOutclosetag.c_str()));
+ dbei->pBlob = (uint8_t*)mir_strdup(str_event.c_str());
+ dbei->cbBlob = (uint32_t)str_event.length() + 1;
+ }
+
+ return 0;
+ }
+
+ if (!isContactSecured(hContact)) {
+ if (globals.debuglog)
+ globals.debuglog << "event message: \"" + string((char*)dbei->pBlob) + "\" passed event filter, contact " + szScreenName + " is unsecured";
+ return 0;
+ }
+
+ if (!(dbei->flags & DBEF_SENT) && db_mc_isMeta((MCONTACT)w)) {
+ char tmp[29];
+ strncpy(tmp, (char*)dbei->pBlob, 27);
+ tmp[28] = '\0';
+ if (strstr(tmp, "-----BEGIN PGP MESSAGE-----")) {
+ if (globals.debuglog)
+ globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName;
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/plugins/New_GPG/src/metacontacts.cpp b/plugins/New_GPG/src/metacontacts.cpp
index 276a8fa5d3..524fceba1a 100644
--- a/plugins/New_GPG/src/metacontacts.cpp
+++ b/plugins/New_GPG/src/metacontacts.cpp
@@ -1,29 +1,29 @@
-// Copyright © 2010-22 SecureIM developers (baloo and others), sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include "stdafx.h"
-
-bool metaIsDefaultSubContact(MCONTACT hContact)
-{
- return db_mc_getDefault(db_mc_getMeta(hContact)) == hContact;
-}
-
-MCONTACT metaGetMostOnline(MCONTACT hContact)
-{
- if (db_mc_isMeta(hContact))
- return db_mc_getMostOnline(hContact);
- return NULL;
-}
+// Copyright © 2010-23 SecureIM developers (baloo and others), sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+bool metaIsDefaultSubContact(MCONTACT hContact)
+{
+ return db_mc_getDefault(db_mc_getMeta(hContact)) == hContact;
+}
+
+MCONTACT metaGetMostOnline(MCONTACT hContact)
+{
+ if (db_mc_isMeta(hContact))
+ return db_mc_getMostOnline(hContact);
+ return NULL;
+}
diff --git a/plugins/New_GPG/src/metacontacts.h b/plugins/New_GPG/src/metacontacts.h
index 307a9c3131..a0d9579bab 100644
--- a/plugins/New_GPG/src/metacontacts.h
+++ b/plugins/New_GPG/src/metacontacts.h
@@ -1,21 +1,21 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public 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
-
-bool metaIsDefaultSubContact(MCONTACT hContact) ;
-MCONTACT metaGetMostOnline(MCONTACT hContact);
-
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public 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
+
+bool metaIsDefaultSubContact(MCONTACT hContact) ;
+MCONTACT metaGetMostOnline(MCONTACT hContact);
+
diff --git a/plugins/New_GPG/src/options.cpp b/plugins/New_GPG/src/options.cpp
index 2462694f43..cc214ac95a 100644
--- a/plugins/New_GPG/src/options.cpp
+++ b/plugins/New_GPG/src/options.cpp
@@ -1,1155 +1,1155 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include "stdafx.h"
-
-globals_s globals;
-
-HWND hwndCurKey_p = nullptr;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Load existing key dialog
-
-class CDlgLoadExistingKey : public CDlgBase
-{
- wchar_t id[16];
- CCtrlListView list_EXISTING_KEY_LIST;
-
-public:
- CDlgLoadExistingKey() :
- CDlgBase(g_plugin, IDD_LOAD_EXISTING_KEY),
- list_EXISTING_KEY_LIST(this, IDC_EXISTING_KEY_LIST)
- {
- id[0] = 0;
-
- list_EXISTING_KEY_LIST.OnClick = Callback(this, &CDlgLoadExistingKey::onChange_EXISTING_KEY_LIST);
- }
-
- bool OnInitDialog() override
- {
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
-
- list_EXISTING_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50);
- list_EXISTING_KEY_LIST.AddColumn(1, TranslateT("Email"), 30);
- list_EXISTING_KEY_LIST.AddColumn(2, TranslateT("Name"), 250);
- list_EXISTING_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30);
- list_EXISTING_KEY_LIST.AddColumn(4, TranslateT("Expiration date"), 30);
- list_EXISTING_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30);
- list_EXISTING_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
-
- // parse gpg output
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--list-keys");
- if (!gpg_launcher(params))
- return false;
- if (params.result == pxNotFound)
- return false;
-
- int i = 1;
- string out(params.out);
- string::size_type p = 0, p2 = 0, stop = 0;
- while (p != string::npos) {
- if ((p = out.find("pub ", p)) == string::npos)
- break;
- p += 5;
- if (p < stop)
- break;
- stop = p;
- p2 = out.find("/", p) - 1;
-
- int row = list_EXISTING_KEY_LIST.AddItem(L"", 0);
- list_EXISTING_KEY_LIST.SetItemText(row, 5, toUTF16(out.substr(p, p2 - p)).c_str());
-
- p2 += 2;
- p = out.find(" ", p2);
- list_EXISTING_KEY_LIST.SetItemText(row, 0, toUTF16(out.substr(p2, p - p2)).c_str());
-
- p++;
- p2 = out.find("\n", p);
- string::size_type p3 = out.substr(p, p2 - p).find("[");
- if (p3 != string::npos) {
- p3 += p;
- p2 = p3;
- p2--;
- p3++;
- p3 += mir_strlen("expires: ");
- string::size_type p4 = out.find("]", p3);
- list_EXISTING_KEY_LIST.SetItemText(row, 4, toUTF16(out.substr(p3, p4 - p3)).c_str());
- }
- else p2--;
-
- list_EXISTING_KEY_LIST.SetItemText(row, 3, toUTF16(out.substr(p, p2 - p)).c_str());
-
- p = out.find("uid ", p);
- p += mir_strlen("uid ");
- p2 = out.find("\n", p);
- p3 = out.substr(p, p2 - p).find("<");
- if (p3 != string::npos) {
- p3 += p;
- p2 = p3;
- p2--;
- p3++;
- string::size_type p4 = out.find(">", p3);
- list_EXISTING_KEY_LIST.SetItemText(row, 1, toUTF16(out.substr(p3, p4 - p3)).c_str());
- }
- else p2--;
-
- p = out.find_first_not_of(" ", p);
- list_EXISTING_KEY_LIST.SetItemText(row, 2, toUTF16(out.substr(p, p2 - p)).c_str());
- i++;
- }
-
- if (list_EXISTING_KEY_LIST.GetItemCount()) {
- list_EXISTING_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
- list_EXISTING_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
- list_EXISTING_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
- list_EXISTING_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
- list_EXISTING_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
- list_EXISTING_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE);
- }
- return true;
- }
-
- bool OnApply() override
- {
- int i = list_EXISTING_KEY_LIST.GetSelectionMark();
- if (i == -1)
- return false; //TODO: error message
-
- list_EXISTING_KEY_LIST.GetItemText(i, 0, id, _countof(id));
- extern CCtrlEdit *edit_p_PubKeyEdit;
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"-a");
- params.addParam(L"--export");
- params.addParam(id);
- if (!gpg_launcher(params))
- return false;
- if (params.result == pxNotFound)
- return false;
-
- string out(params.out);
- size_t p1 = out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----");
- if (p1 != std::string::npos) {
- size_t p2 = out.find("-----END PGP PUBLIC KEY BLOCK-----", p1);
- if (p2 != std::string::npos) {
- p2 += mir_strlen("-----END PGP PUBLIC KEY BLOCK-----");
- out = out.substr(p1, p2 - p1);
- if (edit_p_PubKeyEdit)
- edit_p_PubKeyEdit->SetText(_A2T(out.c_str()));
- }
- else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK);
- }
- else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK);
-
- return true;
- }
-
- void OnDestroy() override
- {
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
- }
-
- void onChange_EXISTING_KEY_LIST(CCtrlListView::TEventInfo *)
- {
- EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE);
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Import key dialog
-
-class CDlgImportKey : public CDlgBase
-{
- MCONTACT hContact;
- CCtrlCombo combo_KEYSERVER;
- CCtrlButton btn_IMPORT;
-
-public:
- CDlgImportKey(MCONTACT _hContact) :
- CDlgBase(g_plugin, IDD_IMPORT_KEY),
- combo_KEYSERVER(this, IDC_KEYSERVER),
- btn_IMPORT(this, IDC_IMPORT)
- {
- hContact = _hContact;
- btn_IMPORT.OnClick = Callback(this, &CDlgImportKey::onClick_IMPORT);
- }
-
- bool OnInitDialog() override
- {
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow");
-
- combo_KEYSERVER.AddString(L"subkeys.pgp.net");
- combo_KEYSERVER.AddString(L"keys.gnupg.net");
- return true;
- }
-
- void OnDestroy() override
- {
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow");
- }
-
- void onClick_IMPORT(CCtrlButton *)
- {
- gpg_execution_params params;
- params.addParam(L"--keyserver");
- params.addParam(combo_KEYSERVER.GetText());
- params.addParam(L"--recv-keys");
- params.addParam(toUTF16(globals.hcontact_data[hContact].key_in_prescense));
- gpg_launcher(params);
-
- MessageBoxA(nullptr, params.out.c_str(), "GPG output", MB_OK);
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// COptGpgMainDlg class
-
-static class COptGpgMainDlg *g_pMainDlg;
-
-class COptGpgMainDlg : public CDlgBase
-{
- bool old_bFileTransfers = g_plugin.bFileTransfers;
-
- CCtrlListView list_USERLIST;
- CCtrlData lbl_CURRENT_KEY;
- CCtrlEdit edit_LOG_FILE_EDIT;
- CCtrlCheck check_DEBUG_LOG, check_JABBER_API, check_AUTO_EXCHANGE, check_FILE_TRANSFERS;
- CCtrlButton btn_DELETE_KEY_BUTTON, btn_SELECT_KEY, btn_SAVE_KEY_BUTTON, btn_COPY_KEY, btn_LOG_FILE_SET;
-
-public:
- COptGpgMainDlg() : CDlgBase(g_plugin, IDD_OPT_GPG),
- list_USERLIST(this, IDC_USERLIST), lbl_CURRENT_KEY(this, IDC_CURRENT_KEY), edit_LOG_FILE_EDIT(this, IDC_LOG_FILE_EDIT),
- check_DEBUG_LOG(this, IDC_DEBUG_LOG), check_JABBER_API(this, IDC_JABBER_API), check_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE), check_FILE_TRANSFERS(this, IDC_FILE_TRANSFERS),
- btn_DELETE_KEY_BUTTON(this, IDC_DELETE_KEY_BUTTON), btn_SELECT_KEY(this, IDC_SELECT_KEY), btn_SAVE_KEY_BUTTON(this, IDC_SAVE_KEY_BUTTON), btn_COPY_KEY(this, IDC_COPY_KEY), btn_LOG_FILE_SET(this, IDC_LOG_FILE_SET)
- {
- btn_DELETE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_DELETE_KEY_BUTTON);
- btn_SELECT_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_SELECT_KEY);
- btn_SAVE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_SAVE_KEY_BUTTON);
- btn_COPY_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_COPY_KEY);
- btn_LOG_FILE_SET.OnClick = Callback(this, &COptGpgMainDlg::onClick_LOG_FILE_SET);
-
- check_JABBER_API.OnChange = Callback(this, &COptGpgMainDlg::onChange_JABBER_API);
-
- list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST);
-
- CreateLink(check_DEBUG_LOG, g_plugin.bDebugLog);
- CreateLink(check_JABBER_API, g_plugin.bJabberAPI);
- CreateLink(check_AUTO_EXCHANGE, g_plugin.bAutoExchange);
- CreateLink(check_FILE_TRANSFERS, g_plugin.bFileTransfers);
- }
-
- bool OnInitDialog() override
- {
- g_pMainDlg = this;
-
- list_USERLIST.AddColumn(0, TranslateT("Contact"), 60);
- list_USERLIST.AddColumn(1, TranslateT("Key ID"), 50);
- list_USERLIST.AddColumn(2, TranslateT("Name"), 50);
- list_USERLIST.AddColumn(3, TranslateT("Email"), 50);
- list_USERLIST.AddColumn(4, TranslateT("Account"), 60);
- list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
-
- for (auto &hContact : Contacts()) {
- if (!isContactHaveKey(hContact))
- continue;
-
- auto *pa = Proto_GetAccount(Proto_GetBaseAccountName(hContact));
- if (pa == nullptr)
- continue;
-
- wchar_t *name = Clist_GetContactDisplayName(hContact);
-
- int row = list_USERLIST.AddItem(L"", 0, hContact);
- list_USERLIST.SetItemText(row, 0, name);
-
- list_USERLIST.SetItemText(row, 4, pa->tszAccountName);
-
- CMStringW tmp = g_plugin.getMStringW(hContact, "KeyID", L"not set");
- list_USERLIST.SetItemText(row, 1, tmp);
-
- tmp = g_plugin.getMStringW(hContact, "KeyMainName", L"not set");
- list_USERLIST.SetItemText(row, 2, tmp);
-
- tmp = g_plugin.getMStringW(hContact, "KeyMainEmail", L"not set");
- list_USERLIST.SetItemText(row, 3, tmp);
-
- if (g_plugin.getByte(hContact, "GPGEncryption", 0))
- list_USERLIST.SetCheckState(row, 1);
- }
-
- SetListAutoSize();
-
- edit_LOG_FILE_EDIT.SetText(ptrW(g_plugin.getWStringA("szLogFilePath", L"")));
-
- check_JABBER_API.Enable();
- check_AUTO_EXCHANGE.Enable(g_plugin.bJabberAPI);
-
- lbl_CURRENT_KEY.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("Default private key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get()));
-
- check_JABBER_API.SetState(g_plugin.getByte("bJabberAPI", 1));
- check_FILE_TRANSFERS.SetState(g_plugin.getByte("bFileTransfers", 0));
- check_AUTO_EXCHANGE.SetState(g_plugin.getByte("bAutoExchange", 0));
-
- //TODO: get rid of following s..t
- ////////////////
- hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd();
- ////////////////
- return true;
- }
-
- bool OnApply() override
- {
- globals.debuglog.init();
-
- if (g_plugin.bFileTransfers != old_bFileTransfers)
- g_plugin.bSameAction = false;
-
- g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText()));
- return true;
- }
-
- void OnDestroy() override
- {
- hwndCurKey_p = nullptr;
- g_pMainDlg = nullptr;
- }
-
- void onClick_DELETE_KEY_BUTTON(CCtrlButton*)
- {
- int idx = list_USERLIST.GetSelectionMark();
- if (idx == -1)
- return;
-
- bool keep = false;
- bool ismetacontact = false;
- MCONTACT meta = NULL;
- MCONTACT hContact = list_USERLIST.GetItemData(idx);
- if (db_mc_isMeta(hContact)) {
- meta = hContact;
- hContact = metaGetMostOnline(hContact);
- ismetacontact = true;
- }
- else if ((meta = db_mc_getMeta(hContact)) != NULL) {
- hContact = metaGetMostOnline(meta);
- ismetacontact = true;
- }
-
- CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID"));
- for (auto &hcnttmp : Contacts()) {
- if (hcnttmp != hContact) {
- ptrA tmp2(g_plugin.getStringA(hcnttmp, "KeyID"));
- if (!mir_strcmp(tmp, tmp2)) {
- keep = true;
- break;
- }
- }
- }
-
- if (!keep)
- if (MessageBox(nullptr, TranslateT("This key is not used by any contact. Do you want to remove it from public keyring?"), TranslateT("Key info"), MB_YESNO) == IDYES) {
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--yes");
- params.addParam(L"--delete-key");
- params.addParam(_A2T(tmp).get());
- if (!gpg_launcher(params))
- return;
-
- if (params.result == pxNotFound)
- return;
-
- if (params.out.Find("--delete-secret-keys") != -1)
- MessageBox(nullptr, TranslateT("we have secret key for this public key, do not removing from GPG keyring"), TranslateT("info"), MB_OK);
- else
- MessageBox(nullptr, TranslateT("Key removed from GPG keyring"), TranslateT("info"), MB_OK);
- }
-
- if (ismetacontact) {
- if (MessageBox(nullptr, TranslateT("Do you want to remove key from entire metacontact (all subcontacts)?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
- MCONTACT hcnt = NULL;
- int count = db_mc_getSubCount(meta);
- for (int i = 0; i < count; i++) {
- hcnt = db_mc_getSub(meta, i);
- if (hcnt) {
- g_plugin.delSetting(hcnt, "KeyID");
- g_plugin.delSetting(hcnt, "GPGPubKey");
- g_plugin.delSetting(hcnt, "KeyMainName");
- g_plugin.delSetting(hcnt, "KeyType");
- g_plugin.delSetting(hcnt, "KeyMainEmail");
- g_plugin.delSetting(hcnt, "KeyComment");
- setSrmmIcon(hcnt);
- }
- }
- }
- else {
- g_plugin.delSetting(hContact, "KeyID");
- g_plugin.delSetting(hContact, "GPGPubKey");
- g_plugin.delSetting(hContact, "KeyMainName");
- g_plugin.delSetting(hContact, "KeyType");
- g_plugin.delSetting(hContact, "KeyMainEmail");
- g_plugin.delSetting(hContact, "KeyComment");
- setSrmmIcon(hContact);
- }
- }
- else {
- g_plugin.delSetting(hContact, "KeyID");
- g_plugin.delSetting(hContact, "GPGPubKey");
- g_plugin.delSetting(hContact, "KeyMainName");
- g_plugin.delSetting(hContact, "KeyType");
- g_plugin.delSetting(hContact, "KeyMainEmail");
- g_plugin.delSetting(hContact, "KeyComment");
- setSrmmIcon(hContact);
- }
-
- list_USERLIST.SetItemText(idx, 3, TranslateT("not set"));
- list_USERLIST.SetItemText(idx, 2, TranslateT("not set"));
- list_USERLIST.SetItemText(idx, 1, TranslateT("not set"));
- }
-
- void onClick_SELECT_KEY(CCtrlButton*)
- {
- ShowFirstRunDialog();
- }
-
- void onClick_SAVE_KEY_BUTTON(CCtrlButton*)
- {
- int idx = list_USERLIST.GetSelectionMark();
- if (idx == -1)
- return;
-
- MCONTACT hContact = list_USERLIST.GetItemData(idx);
- ptrW tmp(GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true));
- if (tmp) {
- CMStringW str(g_plugin.getMStringW(hContact, "GPGPubKey"));
- str.Replace(L"\r", L"");
-
- wfstream f(tmp, std::ios::out);
- f << str.c_str();
- f.close();
- }
- }
-
- void onClick_COPY_KEY(CCtrlButton *)
- {
- CMStringW str(g_plugin.getMStringW("GPGPubKey"));
- str.Replace(L"\n", L"\r\n");
- Utils_ClipboardCopy(str);
- }
-
- void onClick_LOG_FILE_SET(CCtrlButton*)
- {
- edit_LOG_FILE_EDIT.SetText(ptrW(GetFilePath(TranslateT("Set log file"), L"*", TranslateT("LOG files"), 1)));
- }
-
- void onChange_JABBER_API(CCtrlCheck *chk)
- {
- check_AUTO_EXCHANGE.Enable(chk->GetState());
- }
-
- void onItemChanged_USERLIST(CCtrlListView::TEventInfo *ev)
- {
- NMLISTVIEW *hdr = ev->nmlv;
- if (hdr->iItem == -1)
- return;
-
- MCONTACT hContact = list_USERLIST.GetItemData(hdr->iItem);
- if (list_USERLIST.GetCheckState(hdr->iItem))
- g_plugin.setByte(hContact, "GPGEncryption", 1);
- else
- g_plugin.setByte(hContact, "GPGEncryption", 0);
- setSrmmIcon(hContact);
- }
-
- void SetLineText(int i, const wchar_t *pwszText)
- {
- int idx = list_USERLIST.GetSelectionMark();
- if (idx != -1)
- list_USERLIST.SetItemText(idx, i, pwszText);
- }
-
- void SetListAutoSize()
- {
- if (list_USERLIST.GetItemCount() == 0)
- return;
-
- list_USERLIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
- list_USERLIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
- list_USERLIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
- list_USERLIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
- list_USERLIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// COptGpgBinDlg class
-
-class COptGpgBinDlg : public CDlgBase
-{
- CCtrlEdit edit_BIN_PATH, edit_HOME_DIR;
- CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR;
-
-public:
- COptGpgBinDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_BIN),
- edit_BIN_PATH(this, IDC_BIN_PATH), edit_HOME_DIR(this, IDC_HOME_DIR),
- btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR)
- {
- btn_SET_BIN_PATH.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_BIN_PATH);
- btn_SET_HOME_DIR.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_HOME_DIR);
-
- }
-
- bool OnInitDialog() override
- {
- edit_BIN_PATH.SetText(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
- edit_HOME_DIR.SetText(g_plugin.getMStringW("szHomePath", L"gpg"));
- return true;
- }
-
- bool OnApply() override
- {
- g_plugin.setWString("szGpgBinPath", ptrW(edit_BIN_PATH.GetText()));
-
- ptrW wszHomeDir(edit_HOME_DIR.GetText());
- while (wszHomeDir[mir_wstrlen(wszHomeDir) - 1] == '\\')
- wszHomeDir[mir_wstrlen(wszHomeDir) - 1] = '\0';
- g_plugin.setWString("szHomePath", wszHomeDir);
- return true;
- }
-
- void onClick_SET_BIN_PATH(CCtrlButton*)
- {
- GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables"));
- CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
- edit_BIN_PATH.SetText(tmp);
- bool gpg_exists = false;
- {
- if (_waccess(tmp, 0) != -1)
- gpg_exists = true;
- if (gpg_exists) {
- bool bad_version = false;
- CMStringW tmp_path = g_plugin.getMStringW("szGpgBinPath", L"");
- g_plugin.setWString("szGpgBinPath", tmp);
-
- gpg_execution_params params;
- params.addParam(L"--version");
-
- bool old_gpg_state = globals.gpg_valid;
- globals.gpg_valid = true;
- gpg_launcher(params);
- globals.gpg_valid = old_gpg_state;
- g_plugin.setWString("szGpgBinPath", tmp_path);
-
- int p1 = params.out.Find("(GnuPG) ");
- if (p1 != string::npos) {
- p1 += mir_strlen("(GnuPG) ");
- if (params.out[p1] != '1')
- bad_version = true;
- }
- else {
- bad_version = false;
- MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
- }
- }
- }
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- if (tmp.Find(mir_path, 0) == 0) {
- CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
- edit_BIN_PATH.SetText(path);
- }
- }
-
- void onClick_SET_HOME_DIR(CCtrlButton*)
- {
- GetFolderPath(TranslateT("Set home directory"));
- CMStringW tmp(g_plugin.getMStringW("szHomePath", L""));
- edit_HOME_DIR.SetText(tmp);
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- if (tmp.Find(mir_path, 0) == 0) {
- CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
- edit_HOME_DIR.SetText(tmp);
- }
- }
-};
-
-class COptGpgMsgDlg : public CDlgBase
-{
- CCtrlCheck check_APPEND_TAGS, check_STRIP_TAGS;
- CCtrlEdit edit_IN_OPEN_TAG, edit_IN_CLOSE_TAG, edit_OUT_OPEN_TAG, edit_OUT_CLOSE_TAG;
-
-public:
- COptGpgMsgDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_MESSAGES),
- check_APPEND_TAGS(this, IDC_APPEND_TAGS), check_STRIP_TAGS(this, IDC_STRIP_TAGS),
- edit_IN_OPEN_TAG(this, IDC_IN_OPEN_TAG), edit_IN_CLOSE_TAG(this, IDC_IN_CLOSE_TAG), edit_OUT_OPEN_TAG(this, IDC_OUT_OPEN_TAG), edit_OUT_CLOSE_TAG(this, IDC_OUT_CLOSE_TAG)
- {
- CreateLink(check_STRIP_TAGS, g_plugin.bStripTags);
- CreateLink(check_APPEND_TAGS, g_plugin.bAppendTags);
- }
-
- bool OnInitDialog() override
- {
- edit_IN_OPEN_TAG.SetText(g_plugin.getMStringW("szInOpenTag", L"<GPGdec>"));
- edit_IN_CLOSE_TAG.SetText(g_plugin.getMStringW("szInCloseTag", L"</GPGdec>"));
- edit_OUT_OPEN_TAG.SetText(g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>"));
- edit_OUT_CLOSE_TAG.SetText(g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>"));
- return true;
- }
-
- bool OnApply() override
- {
- ptrW tmp(edit_IN_OPEN_TAG.GetText());
- g_plugin.setWString("szInOpenTag", tmp);
- globals.wszInopentag = tmp;
-
- tmp = edit_IN_CLOSE_TAG.GetText();
- g_plugin.setWString("szInCloseTag", tmp);
- globals.wszInclosetag = tmp;
-
- tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText());
- g_plugin.setWString("szOutOpenTag", tmp);
- globals.wszOutopentag = tmp;
-
- tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText());
- g_plugin.setWString("szOutCloseTag", tmp);
- globals.wszOutclosetag = tmp;
- return true;
- }
-};
-
-class COptGpgAdvDlg : public CDlgBase
-{
- CCtrlButton btn_EXPORT, btn_IMPORT;
- CCtrlCheck chkPresenceSub, chkSendErrors;
-
-public:
- COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED),
- btn_EXPORT(this, IDC_EXPORT),
- btn_IMPORT(this, IDC_IMPORT),
- chkSendErrors(this, IDC_SEND_ERRORS),
- chkPresenceSub(this, IDC_PRESCENSE_SUBSCRIPTION)
- {
- btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT);
- btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT);
-
- CreateLink(chkSendErrors, g_plugin.bSendErrorMessages);
- CreateLink(chkPresenceSub, g_plugin.bPresenceSigning);
- }
-
- bool OnInitDialog() override
- {
- chkPresenceSub.Enable(g_plugin.bJabberAPI);
- return true;
- }
-
- void onClick_EXPORT(CCtrlButton*)
- {
- INT_PTR ExportGpGKeys(WPARAM w, LPARAM l);
- ExportGpGKeys(NULL, NULL);
- }
-
- void onClick_IMPORT(CCtrlButton*)
- {
- INT_PTR ImportGpGKeys(WPARAM w, LPARAM l);
- ImportGpGKeys(NULL, NULL);
- }
-};
-
-CCtrlEdit *edit_p_PubKeyEdit = nullptr;
-
-static LRESULT CALLBACK editctrl_ctrl_a(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_KEYDOWN:
- if (wParam == 0x41 && GetKeyState(VK_CONTROL) < 0)
- SendMessage(hwndDlg, EM_SETSEL, 0, -1);
- return 0;
- }
- return mir_callNextSubclass(hwndDlg, editctrl_ctrl_a, msg, wParam, lParam);
-}
-
-class CDlgLoadPubKeyDlg : public CDlgBase
-{
- MCONTACT hContact;
- wstring key_buf;
- wstring::size_type ws1 = 0, ws2 = 0;
- CCtrlCheck chk_ENABLE_ENCRYPTION;
- CCtrlButton btn_SELECT_EXISTING, btn_OK, btn_LOAD_FROM_FILE, btn_IMPORT;
- CCtrlEdit edit_PUBLIC_KEY_EDIT;
-
-public:
- CDlgLoadPubKeyDlg(MCONTACT _p1) :
- CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY),
- hContact(_p1),
- chk_ENABLE_ENCRYPTION(this, IDC_ENABLE_ENCRYPTION),
- btn_SELECT_EXISTING(this, IDC_SELECT_EXISTING), btn_OK(this, ID_OK), btn_LOAD_FROM_FILE(this, ID_LOAD_FROM_FILE), btn_IMPORT(this, IDC_IMPORT),
- edit_PUBLIC_KEY_EDIT(this, IDC_PUBLIC_KEY_EDIT)
- {
- btn_SELECT_EXISTING.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_SELECT_EXISTING);
- btn_OK.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_OK);
- btn_LOAD_FROM_FILE.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_LOAD_FROM_FILE);
- btn_IMPORT.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_IMPORT);
- }
-
- bool OnInitDialog() override
- {
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
-
- mir_subclassWindow(GetDlgItem(m_hwnd, IDC_PUBLIC_KEY_EDIT), editctrl_ctrl_a);
- MCONTACT hcnt = db_mc_tryMeta(hContact);
- {
- wstring msg = TranslateT("Load Public GPG Key for ");
- msg += Clist_GetContactDisplayName(hcnt, 0);
- this->SetCaption(msg.c_str());
- }
- if (!hcnt) {
- btn_SELECT_EXISTING.Disable();
- chk_ENABLE_ENCRYPTION.Disable();
- }
- if (isContactSecured(hcnt))
- chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn off encryption"));
- else {
- chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn on encryption"));
- chk_ENABLE_ENCRYPTION.SetState(1);
- }
- if (hcnt) {
- wstring str = ptrW(g_plugin.getWStringA(hcnt, "GPGPubKey", L""));
- if (!str.empty()) {
- wstring::size_type p = 0, stop = 0;
- for (;;) {
- if ((p = str.find(L"\n", p + 2)) != wstring::npos) {
- if (p > stop) {
- stop = p;
- str.insert(p, L"\r");
- }
- else break;
- }
- }
- }
-
- if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) {
- if (g_plugin.getMStringA(hcnt, "KeyID").IsEmpty()) {
- gpg_execution_params params;
- params.addParam(L"--export");
- params.addParam(L"-a");
- params.addParam(toUTF16(globals.hcontact_data[hcnt].key_in_prescense));
- gpg_launcher(params); //TODO: handle errors
-
- if ((params.out.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != -1) && (params.out.Find("-----END PGP PUBLIC KEY BLOCK-----") != -1)) {
- params.out.Replace("\n", "\r\n");
-
- wchar_t *tmp3 = mir_a2u(params.out.c_str());
- str.clear();
- str.append(tmp3);
- mir_free(tmp3);
- string msg = Translate("Load Public GPG Key for ");
- msg += _T2A(Clist_GetContactDisplayName(hcnt));
- msg += " (Key ID: ";
- msg += globals.hcontact_data[hcnt].key_in_prescense;
- msg += Translate(" found in presence, and exists in keyring.)");
- SetCaption(toUTF16(msg).c_str());
- }
- else {
- string msg = Translate("Load Public GPG Key (Key ID: ");
- msg += globals.hcontact_data[hcnt].key_in_prescense;
- msg += Translate(" found in presence.)");
- SetCaption(toUTF16(msg).c_str());
- btn_IMPORT.Enable();
- }
- }
- }
-
- edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L"");
- }
- edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT;
- return true;
- }
-
- virtual void OnDestroy() override
- {
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
- edit_p_PubKeyEdit = nullptr;
- }
-
- void onClick_SELECT_EXISTING(CCtrlButton*)
- {
- (new CDlgLoadExistingKey())->Show();
- }
-
- void onClick_OK(CCtrlButton*)
- {
- wchar_t *tmp = mir_wstrdup(edit_PUBLIC_KEY_EDIT.GetText());
- wchar_t *begin, *end;
- key_buf.append(tmp);
- key_buf.append(L"\n"); //no new line at end of file )
- mir_free(tmp);
- while ((ws1 = key_buf.find(L"\r", ws1)) != wstring::npos) {
- key_buf.erase(ws1, 1); //remove windows specific trash
- }
- ws1 = 0;
- if (((ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) {
- begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PUBLIC KEY BLOCK-----") + 1));
- mir_wstrcpy(begin, L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
- end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----") + 1));
- mir_wstrcpy(end, L"-----END PGP PUBLIC KEY BLOCK-----");
- }
- else if (((ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) {
- begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PRIVATE KEY BLOCK-----") + 1));
- mir_wstrcpy(begin, L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
- end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----") + 1));
- mir_wstrcpy(end, L"-----END PGP PRIVATE KEY BLOCK-----");
- }
- else {
- MessageBox(nullptr, TranslateT("This is not public or private key"), L"INFO", MB_OK);
- return;
- }
- ws2 += mir_wstrlen(end);
- bool allsubcontacts = false;
- {
- if (db_mc_isMeta(hContact)) {
- if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
- allsubcontacts = true;
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setWString(hcnt, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
- }
- }
- else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
- }
- else g_plugin.setWString(hContact, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
- }
- tmp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (key_buf.length() + 1));
- mir_wstrcpy(tmp, key_buf.substr(ws1, ws2 - ws1).c_str());
- { //gpg execute block
- std::vector<wstring> cmd;
- CMStringW tmp2;
- {
- MCONTACT hcnt = db_mc_tryMeta(hContact);
- tmp2 = g_plugin.getMStringW("szHomePath");
- tmp2 += L"\\temporary_exported.asc";
- boost::filesystem::remove(tmp2.c_str());
-
- wfstream f(tmp2, std::ios::out);
- CMStringW str = g_plugin.getMStringW(hcnt, "GPGPubKey");
- str.Replace(L"\r", L"");
- f << str.c_str();
- f.close();
- }
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--import");
- params.addParam(tmp2.c_str());
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- mir_free(begin);
- mir_free(end);
- if (hContact) {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.delSetting(hcnt, "bAlwatsTrust");
- }
- }
- else g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust");
- }
- else g_plugin.delSetting(hContact, "bAlwatsTrust");
- }
-
- string output(params.out);
- {
- if (output.find("already in secret keyring") != string::npos) {
- MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK);
- boost::filesystem::remove(tmp2.c_str());
- return;
- }
- string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
- string::size_type s2 = output.find(":", s);
- {
- char *tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
- mir_strcpy(tmp3, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp3, nullptr);
- {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setString(hcnt, "KeyID", tmp3);
- }
- }
- else g_plugin.setString(metaGetMostOnline(hContact), "KeyID", tmp3);
- }
- else g_plugin.setString(hContact, "KeyID", tmp3);
- }
- mir_free(tmp3);
- }
-
- if (hContact && g_pMainDlg)
- g_pMainDlg->SetLineText(1, toUTF16(output.substr(s, s2 - s)).c_str());
-
- s = output.find("“", s2);
- if (s == string::npos) {
- s = output.find("\"", s2);
- s += 1;
- }
- else
- s += 3;
- bool uncommon = false;
- if ((s2 = output.find("(", s)) == string::npos) {
- if ((s2 = output.find("<", s)) == string::npos) {
- s2 = output.find("”", s);
- uncommon = true;
- }
- }
- else if (s2 > output.find("<", s))
- s2 = output.find("<", s);
- if (s2 != string::npos && s != string::npos) {
- {
- char *tmp3 = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
- mir_strcpy(tmp3, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
- mir_utf8decode(tmp3, nullptr);
- if (hContact) {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setString(hcnt, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
- }
- }
- else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", output.substr(s, s2 - s - 1).c_str());
- }
- else g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
- }
- mir_free(tmp3);
- }
-
- if (hContact && g_pMainDlg)
- g_pMainDlg->SetLineText(2, toUTF16(output.substr(s, s2 - s - 1)).c_str());
-
- if ((s = output.find(")", s2)) == string::npos)
- s = output.find(">", s2);
- else if (s > output.find(">", s2))
- s = output.find(">", s2);
- s2++;
- if (s != string::npos && s2 != string::npos) {
- if (output[s] == ')') {
- char *tmp3 = (char*)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
- mir_strcpy(tmp3, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp3, nullptr);
- if (hContact) {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setString(hcnt, "KeyComment", output.substr(s2, s - s2).c_str());
- }
- }
- else g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", output.substr(s2, s - s2).c_str());
- }
- else g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str());
- }
- mir_free(tmp3);
- s += 3;
- s2 = output.find(">", s);
- tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
- mir_strcpy(tmp3, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp3, nullptr);
- if (hContact) {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s, s2 - s).c_str());
- }
- }
- else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s, s2 - s).c_str());
- }
- else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str());
- }
- mir_free(tmp3);
-
- if (hContact && g_pMainDlg)
- g_pMainDlg->SetLineText(3, toUTF16(output.substr(s, s2 - s)).c_str());
- }
- else {
- char *tmp3 = (char*)mir_alloc(output.substr(s2, s - s2).length() + 1);
- mir_strcpy(tmp3, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp3, nullptr);
- if (hContact) {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str());
- }
- }
- else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str());
- }
- else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
- }
- mir_free(tmp3);
-
- if (hContact && g_pMainDlg)
- g_pMainDlg->SetLineText(3, toUTF16(output.substr(s2, s - s2)).c_str());
- }
- }
- }
- if (hContact && g_pMainDlg)
- g_pMainDlg->SetListAutoSize();
- }
- if (!hContact) {
- gpg_execution_params params2;
- params.addParam(L"--batch");
- params.addParam(L"-a");
- params.addParam(L"--export");
- params.addParam(g_plugin.getMStringW(hContact, "KeyID").c_str());
- if (!gpg_launcher(params2))
- return;
- if (params2.result == pxNotFound)
- return;
-
- params2.out.Remove('\r');
- g_plugin.setString(hContact, "GPGPubKey", params2.out.c_str());
- }
- MessageBoxA(nullptr, output.c_str(), "", MB_OK);
- boost::filesystem::remove(tmp2.c_str());
- }
- key_buf.clear();
- if (chk_ENABLE_ENCRYPTION.GetState()) {
- if (hContact) {
- if (db_mc_isMeta(hContact)) {
- if (allsubcontacts) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt) {
- g_plugin.setByte(hcnt, "GPGEncryption", !isContactSecured(hcnt));
- setSrmmIcon(hContact);
- }
- }
- }
- else g_plugin.setByte(metaGetMostOnline(hContact), "GPGEncryption", !isContactSecured(hContact));
- }
- else g_plugin.setByte(hContact, "GPGEncryption", !isContactSecured(hContact));
- }
- }
- this->Close();
- }
-
- void onClick_LOAD_FROM_FILE(CCtrlButton *)
- {
- ptrW tmp(GetFilePath(TranslateT("Set file containing GPG public key"), L"*", TranslateT("GPG public key file")));
- if (!tmp)
- return;
-
- wfstream f(tmp, std::ios::in | std::ios::ate | std::ios::binary);
- if (!f.is_open()) {
- MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
- return;
- }
- if (f.is_open()) {
- std::wifstream::pos_type size = f.tellg();
- wchar_t *temp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1];
- f.seekg(0, std::ios::beg);
- f.read(temp, size);
- temp[size] = '\0';
- key_buf.append(temp);
- delete[] temp;
- f.close();
- }
- if (key_buf.empty()) {
- key_buf.clear();
- if (globals.debuglog)
- globals.debuglog << "info: Failed to read key file";
- return;
- }
- ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----");
- ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
- if (ws2 == wstring::npos || ws1 == wstring::npos) {
- ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----");
- ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
- }
- if (ws2 == wstring::npos || ws1 == wstring::npos) {
- MessageBox(nullptr, TranslateT("There is no public or private key."), TranslateT("Info"), MB_OK);
- return;
- }
- ws2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
- edit_PUBLIC_KEY_EDIT.SetText(key_buf.substr(ws1, ws2 - ws1).c_str());
- key_buf.clear();
- }
-
- void onClick_IMPORT(CCtrlButton *)
- {
- CDlgImportKey *d = new CDlgImportKey(hContact);
- d->Show();
- }
-};
-
-
-void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal)
-{
- CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(hContact);
- if (bModal)
- d->DoModal();
- else
- d->Show();
-}
-
-int GpgOptInit(WPARAM wParam, LPARAM)
-{
- OPTIONSDIALOGPAGE odp = {};
- odp.szGroup.w = LPGENW("Services");
- odp.szTitle.w = _T(MODULENAME);
-
- odp.szTab.w = LPGENW("Main");
- odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE;
- odp.pDialog = new COptGpgMainDlg();
- g_plugin.addOptions(wParam, &odp);
-
- odp.szTab.w = LPGENW("GnuPG Variables");
- odp.pDialog = new COptGpgBinDlg();
- g_plugin.addOptions(wParam, &odp);
-
- odp.szTab.w = LPGENW("Messages");
- odp.pDialog = new COptGpgMsgDlg();
- g_plugin.addOptions(wParam, &odp);
-
- odp.szTab.w = LPGENW("Advanced");
- odp.pDialog = new COptGpgAdvDlg();
- g_plugin.addOptions(wParam, &odp);
- return 0;
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+globals_s globals;
+
+HWND hwndCurKey_p = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load existing key dialog
+
+class CDlgLoadExistingKey : public CDlgBase
+{
+ wchar_t id[16];
+ CCtrlListView list_EXISTING_KEY_LIST;
+
+public:
+ CDlgLoadExistingKey() :
+ CDlgBase(g_plugin, IDD_LOAD_EXISTING_KEY),
+ list_EXISTING_KEY_LIST(this, IDC_EXISTING_KEY_LIST)
+ {
+ id[0] = 0;
+
+ list_EXISTING_KEY_LIST.OnClick = Callback(this, &CDlgLoadExistingKey::onChange_EXISTING_KEY_LIST);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+
+ list_EXISTING_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50);
+ list_EXISTING_KEY_LIST.AddColumn(1, TranslateT("Email"), 30);
+ list_EXISTING_KEY_LIST.AddColumn(2, TranslateT("Name"), 250);
+ list_EXISTING_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30);
+ list_EXISTING_KEY_LIST.AddColumn(4, TranslateT("Expiration date"), 30);
+ list_EXISTING_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30);
+ list_EXISTING_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
+
+ // parse gpg output
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--list-keys");
+ if (!gpg_launcher(params))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ int i = 1;
+ string out(params.out);
+ string::size_type p = 0, p2 = 0, stop = 0;
+ while (p != string::npos) {
+ if ((p = out.find("pub ", p)) == string::npos)
+ break;
+ p += 5;
+ if (p < stop)
+ break;
+ stop = p;
+ p2 = out.find("/", p) - 1;
+
+ int row = list_EXISTING_KEY_LIST.AddItem(L"", 0);
+ list_EXISTING_KEY_LIST.SetItemText(row, 5, toUTF16(out.substr(p, p2 - p)).c_str());
+
+ p2 += 2;
+ p = out.find(" ", p2);
+ list_EXISTING_KEY_LIST.SetItemText(row, 0, toUTF16(out.substr(p2, p - p2)).c_str());
+
+ p++;
+ p2 = out.find("\n", p);
+ string::size_type p3 = out.substr(p, p2 - p).find("[");
+ if (p3 != string::npos) {
+ p3 += p;
+ p2 = p3;
+ p2--;
+ p3++;
+ p3 += mir_strlen("expires: ");
+ string::size_type p4 = out.find("]", p3);
+ list_EXISTING_KEY_LIST.SetItemText(row, 4, toUTF16(out.substr(p3, p4 - p3)).c_str());
+ }
+ else p2--;
+
+ list_EXISTING_KEY_LIST.SetItemText(row, 3, toUTF16(out.substr(p, p2 - p)).c_str());
+
+ p = out.find("uid ", p);
+ p += mir_strlen("uid ");
+ p2 = out.find("\n", p);
+ p3 = out.substr(p, p2 - p).find("<");
+ if (p3 != string::npos) {
+ p3 += p;
+ p2 = p3;
+ p2--;
+ p3++;
+ string::size_type p4 = out.find(">", p3);
+ list_EXISTING_KEY_LIST.SetItemText(row, 1, toUTF16(out.substr(p3, p4 - p3)).c_str());
+ }
+ else p2--;
+
+ p = out.find_first_not_of(" ", p);
+ list_EXISTING_KEY_LIST.SetItemText(row, 2, toUTF16(out.substr(p, p2 - p)).c_str());
+ i++;
+ }
+
+ if (list_EXISTING_KEY_LIST.GetItemCount()) {
+ list_EXISTING_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
+ list_EXISTING_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE);
+ }
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ int i = list_EXISTING_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return false; //TODO: error message
+
+ list_EXISTING_KEY_LIST.GetItemText(i, 0, id, _countof(id));
+ extern CCtrlEdit *edit_p_PubKeyEdit;
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(id);
+ if (!gpg_launcher(params))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ string out(params.out);
+ size_t p1 = out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (p1 != std::string::npos) {
+ size_t p2 = out.find("-----END PGP PUBLIC KEY BLOCK-----", p1);
+ if (p2 != std::string::npos) {
+ p2 += mir_strlen("-----END PGP PUBLIC KEY BLOCK-----");
+ out = out.substr(p1, p2 - p1);
+ if (edit_p_PubKeyEdit)
+ edit_p_PubKeyEdit->SetText(_A2T(out.c_str()));
+ }
+ else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK);
+ }
+ else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK);
+
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+ }
+
+ void onChange_EXISTING_KEY_LIST(CCtrlListView::TEventInfo *)
+ {
+ EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Import key dialog
+
+class CDlgImportKey : public CDlgBase
+{
+ MCONTACT hContact;
+ CCtrlCombo combo_KEYSERVER;
+ CCtrlButton btn_IMPORT;
+
+public:
+ CDlgImportKey(MCONTACT _hContact) :
+ CDlgBase(g_plugin, IDD_IMPORT_KEY),
+ combo_KEYSERVER(this, IDC_KEYSERVER),
+ btn_IMPORT(this, IDC_IMPORT)
+ {
+ hContact = _hContact;
+ btn_IMPORT.OnClick = Callback(this, &CDlgImportKey::onClick_IMPORT);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow");
+
+ combo_KEYSERVER.AddString(L"subkeys.pgp.net");
+ combo_KEYSERVER.AddString(L"keys.gnupg.net");
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow");
+ }
+
+ void onClick_IMPORT(CCtrlButton *)
+ {
+ gpg_execution_params params;
+ params.addParam(L"--keyserver");
+ params.addParam(combo_KEYSERVER.GetText());
+ params.addParam(L"--recv-keys");
+ params.addParam(toUTF16(globals.hcontact_data[hContact].key_in_prescense));
+ gpg_launcher(params);
+
+ MessageBoxA(nullptr, params.out.c_str(), "GPG output", MB_OK);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// COptGpgMainDlg class
+
+static class COptGpgMainDlg *g_pMainDlg;
+
+class COptGpgMainDlg : public CDlgBase
+{
+ bool old_bFileTransfers = g_plugin.bFileTransfers;
+
+ CCtrlListView list_USERLIST;
+ CCtrlData lbl_CURRENT_KEY;
+ CCtrlEdit edit_LOG_FILE_EDIT;
+ CCtrlCheck check_DEBUG_LOG, check_JABBER_API, check_AUTO_EXCHANGE, check_FILE_TRANSFERS;
+ CCtrlButton btn_DELETE_KEY_BUTTON, btn_SELECT_KEY, btn_SAVE_KEY_BUTTON, btn_COPY_KEY, btn_LOG_FILE_SET;
+
+public:
+ COptGpgMainDlg() : CDlgBase(g_plugin, IDD_OPT_GPG),
+ list_USERLIST(this, IDC_USERLIST), lbl_CURRENT_KEY(this, IDC_CURRENT_KEY), edit_LOG_FILE_EDIT(this, IDC_LOG_FILE_EDIT),
+ check_DEBUG_LOG(this, IDC_DEBUG_LOG), check_JABBER_API(this, IDC_JABBER_API), check_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE), check_FILE_TRANSFERS(this, IDC_FILE_TRANSFERS),
+ btn_DELETE_KEY_BUTTON(this, IDC_DELETE_KEY_BUTTON), btn_SELECT_KEY(this, IDC_SELECT_KEY), btn_SAVE_KEY_BUTTON(this, IDC_SAVE_KEY_BUTTON), btn_COPY_KEY(this, IDC_COPY_KEY), btn_LOG_FILE_SET(this, IDC_LOG_FILE_SET)
+ {
+ btn_DELETE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_DELETE_KEY_BUTTON);
+ btn_SELECT_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_SELECT_KEY);
+ btn_SAVE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_SAVE_KEY_BUTTON);
+ btn_COPY_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_COPY_KEY);
+ btn_LOG_FILE_SET.OnClick = Callback(this, &COptGpgMainDlg::onClick_LOG_FILE_SET);
+
+ check_JABBER_API.OnChange = Callback(this, &COptGpgMainDlg::onChange_JABBER_API);
+
+ list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST);
+
+ CreateLink(check_DEBUG_LOG, g_plugin.bDebugLog);
+ CreateLink(check_JABBER_API, g_plugin.bJabberAPI);
+ CreateLink(check_AUTO_EXCHANGE, g_plugin.bAutoExchange);
+ CreateLink(check_FILE_TRANSFERS, g_plugin.bFileTransfers);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_pMainDlg = this;
+
+ list_USERLIST.AddColumn(0, TranslateT("Contact"), 60);
+ list_USERLIST.AddColumn(1, TranslateT("Key ID"), 50);
+ list_USERLIST.AddColumn(2, TranslateT("Name"), 50);
+ list_USERLIST.AddColumn(3, TranslateT("Email"), 50);
+ list_USERLIST.AddColumn(4, TranslateT("Account"), 60);
+ list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
+
+ for (auto &hContact : Contacts()) {
+ if (!isContactHaveKey(hContact))
+ continue;
+
+ auto *pa = Proto_GetAccount(Proto_GetBaseAccountName(hContact));
+ if (pa == nullptr)
+ continue;
+
+ wchar_t *name = Clist_GetContactDisplayName(hContact);
+
+ int row = list_USERLIST.AddItem(L"", 0, hContact);
+ list_USERLIST.SetItemText(row, 0, name);
+
+ list_USERLIST.SetItemText(row, 4, pa->tszAccountName);
+
+ CMStringW tmp = g_plugin.getMStringW(hContact, "KeyID", L"not set");
+ list_USERLIST.SetItemText(row, 1, tmp);
+
+ tmp = g_plugin.getMStringW(hContact, "KeyMainName", L"not set");
+ list_USERLIST.SetItemText(row, 2, tmp);
+
+ tmp = g_plugin.getMStringW(hContact, "KeyMainEmail", L"not set");
+ list_USERLIST.SetItemText(row, 3, tmp);
+
+ if (g_plugin.getByte(hContact, "GPGEncryption", 0))
+ list_USERLIST.SetCheckState(row, 1);
+ }
+
+ SetListAutoSize();
+
+ edit_LOG_FILE_EDIT.SetText(ptrW(g_plugin.getWStringA("szLogFilePath", L"")));
+
+ check_JABBER_API.Enable();
+ check_AUTO_EXCHANGE.Enable(g_plugin.bJabberAPI);
+
+ lbl_CURRENT_KEY.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("Default private key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get()));
+
+ check_JABBER_API.SetState(g_plugin.getByte("bJabberAPI", 1));
+ check_FILE_TRANSFERS.SetState(g_plugin.getByte("bFileTransfers", 0));
+ check_AUTO_EXCHANGE.SetState(g_plugin.getByte("bAutoExchange", 0));
+
+ //TODO: get rid of following s..t
+ ////////////////
+ hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd();
+ ////////////////
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ globals.debuglog.init();
+
+ if (g_plugin.bFileTransfers != old_bFileTransfers)
+ g_plugin.bSameAction = false;
+
+ g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText()));
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ hwndCurKey_p = nullptr;
+ g_pMainDlg = nullptr;
+ }
+
+ void onClick_DELETE_KEY_BUTTON(CCtrlButton*)
+ {
+ int idx = list_USERLIST.GetSelectionMark();
+ if (idx == -1)
+ return;
+
+ bool keep = false;
+ bool ismetacontact = false;
+ MCONTACT meta = NULL;
+ MCONTACT hContact = list_USERLIST.GetItemData(idx);
+ if (db_mc_isMeta(hContact)) {
+ meta = hContact;
+ hContact = metaGetMostOnline(hContact);
+ ismetacontact = true;
+ }
+ else if ((meta = db_mc_getMeta(hContact)) != NULL) {
+ hContact = metaGetMostOnline(meta);
+ ismetacontact = true;
+ }
+
+ CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID"));
+ for (auto &hcnttmp : Contacts()) {
+ if (hcnttmp != hContact) {
+ ptrA tmp2(g_plugin.getStringA(hcnttmp, "KeyID"));
+ if (!mir_strcmp(tmp, tmp2)) {
+ keep = true;
+ break;
+ }
+ }
+ }
+
+ if (!keep)
+ if (MessageBox(nullptr, TranslateT("This key is not used by any contact. Do you want to remove it from public keyring?"), TranslateT("Key info"), MB_YESNO) == IDYES) {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"--delete-key");
+ params.addParam(_A2T(tmp).get());
+ if (!gpg_launcher(params))
+ return;
+
+ if (params.result == pxNotFound)
+ return;
+
+ if (params.out.Find("--delete-secret-keys") != -1)
+ MessageBox(nullptr, TranslateT("we have secret key for this public key, do not removing from GPG keyring"), TranslateT("info"), MB_OK);
+ else
+ MessageBox(nullptr, TranslateT("Key removed from GPG keyring"), TranslateT("info"), MB_OK);
+ }
+
+ if (ismetacontact) {
+ if (MessageBox(nullptr, TranslateT("Do you want to remove key from entire metacontact (all subcontacts)?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
+ MCONTACT hcnt = NULL;
+ int count = db_mc_getSubCount(meta);
+ for (int i = 0; i < count; i++) {
+ hcnt = db_mc_getSub(meta, i);
+ if (hcnt) {
+ g_plugin.delSetting(hcnt, "KeyID");
+ g_plugin.delSetting(hcnt, "GPGPubKey");
+ g_plugin.delSetting(hcnt, "KeyMainName");
+ g_plugin.delSetting(hcnt, "KeyType");
+ g_plugin.delSetting(hcnt, "KeyMainEmail");
+ g_plugin.delSetting(hcnt, "KeyComment");
+ setSrmmIcon(hcnt);
+ }
+ }
+ }
+ else {
+ g_plugin.delSetting(hContact, "KeyID");
+ g_plugin.delSetting(hContact, "GPGPubKey");
+ g_plugin.delSetting(hContact, "KeyMainName");
+ g_plugin.delSetting(hContact, "KeyType");
+ g_plugin.delSetting(hContact, "KeyMainEmail");
+ g_plugin.delSetting(hContact, "KeyComment");
+ setSrmmIcon(hContact);
+ }
+ }
+ else {
+ g_plugin.delSetting(hContact, "KeyID");
+ g_plugin.delSetting(hContact, "GPGPubKey");
+ g_plugin.delSetting(hContact, "KeyMainName");
+ g_plugin.delSetting(hContact, "KeyType");
+ g_plugin.delSetting(hContact, "KeyMainEmail");
+ g_plugin.delSetting(hContact, "KeyComment");
+ setSrmmIcon(hContact);
+ }
+
+ list_USERLIST.SetItemText(idx, 3, TranslateT("not set"));
+ list_USERLIST.SetItemText(idx, 2, TranslateT("not set"));
+ list_USERLIST.SetItemText(idx, 1, TranslateT("not set"));
+ }
+
+ void onClick_SELECT_KEY(CCtrlButton*)
+ {
+ ShowFirstRunDialog();
+ }
+
+ void onClick_SAVE_KEY_BUTTON(CCtrlButton*)
+ {
+ int idx = list_USERLIST.GetSelectionMark();
+ if (idx == -1)
+ return;
+
+ MCONTACT hContact = list_USERLIST.GetItemData(idx);
+ ptrW tmp(GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true));
+ if (tmp) {
+ CMStringW str(g_plugin.getMStringW(hContact, "GPGPubKey"));
+ str.Replace(L"\r", L"");
+
+ wfstream f(tmp, std::ios::out);
+ f << str.c_str();
+ f.close();
+ }
+ }
+
+ void onClick_COPY_KEY(CCtrlButton *)
+ {
+ CMStringW str(g_plugin.getMStringW("GPGPubKey"));
+ str.Replace(L"\n", L"\r\n");
+ Utils_ClipboardCopy(str);
+ }
+
+ void onClick_LOG_FILE_SET(CCtrlButton*)
+ {
+ edit_LOG_FILE_EDIT.SetText(ptrW(GetFilePath(TranslateT("Set log file"), L"*", TranslateT("LOG files"), 1)));
+ }
+
+ void onChange_JABBER_API(CCtrlCheck *chk)
+ {
+ check_AUTO_EXCHANGE.Enable(chk->GetState());
+ }
+
+ void onItemChanged_USERLIST(CCtrlListView::TEventInfo *ev)
+ {
+ NMLISTVIEW *hdr = ev->nmlv;
+ if (hdr->iItem == -1)
+ return;
+
+ MCONTACT hContact = list_USERLIST.GetItemData(hdr->iItem);
+ if (list_USERLIST.GetCheckState(hdr->iItem))
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ else
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ setSrmmIcon(hContact);
+ }
+
+ void SetLineText(int i, const wchar_t *pwszText)
+ {
+ int idx = list_USERLIST.GetSelectionMark();
+ if (idx != -1)
+ list_USERLIST.SetItemText(idx, i, pwszText);
+ }
+
+ void SetListAutoSize()
+ {
+ if (list_USERLIST.GetItemCount() == 0)
+ return;
+
+ list_USERLIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
+ list_USERLIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// COptGpgBinDlg class
+
+class COptGpgBinDlg : public CDlgBase
+{
+ CCtrlEdit edit_BIN_PATH, edit_HOME_DIR;
+ CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR;
+
+public:
+ COptGpgBinDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_BIN),
+ edit_BIN_PATH(this, IDC_BIN_PATH), edit_HOME_DIR(this, IDC_HOME_DIR),
+ btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR)
+ {
+ btn_SET_BIN_PATH.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_BIN_PATH);
+ btn_SET_HOME_DIR.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_HOME_DIR);
+
+ }
+
+ bool OnInitDialog() override
+ {
+ edit_BIN_PATH.SetText(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
+ edit_HOME_DIR.SetText(g_plugin.getMStringW("szHomePath", L"gpg"));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ g_plugin.setWString("szGpgBinPath", ptrW(edit_BIN_PATH.GetText()));
+
+ ptrW wszHomeDir(edit_HOME_DIR.GetText());
+ while (wszHomeDir[mir_wstrlen(wszHomeDir) - 1] == '\\')
+ wszHomeDir[mir_wstrlen(wszHomeDir) - 1] = '\0';
+ g_plugin.setWString("szHomePath", wszHomeDir);
+ return true;
+ }
+
+ void onClick_SET_BIN_PATH(CCtrlButton*)
+ {
+ GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables"));
+ CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe"));
+ edit_BIN_PATH.SetText(tmp);
+ bool gpg_exists = false;
+ {
+ if (_waccess(tmp, 0) != -1)
+ gpg_exists = true;
+ if (gpg_exists) {
+ bool bad_version = false;
+ CMStringW tmp_path = g_plugin.getMStringW("szGpgBinPath", L"");
+ g_plugin.setWString("szGpgBinPath", tmp);
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+
+ bool old_gpg_state = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = old_gpg_state;
+ g_plugin.setWString("szGpgBinPath", tmp_path);
+
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 != string::npos) {
+ p1 += mir_strlen("(GnuPG) ");
+ if (params.out[p1] != '1')
+ bad_version = true;
+ }
+ else {
+ bad_version = false;
+ MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ }
+ }
+ }
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_BIN_PATH.SetText(path);
+ }
+ }
+
+ void onClick_SET_HOME_DIR(CCtrlButton*)
+ {
+ GetFolderPath(TranslateT("Set home directory"));
+ CMStringW tmp(g_plugin.getMStringW("szHomePath", L""));
+ edit_HOME_DIR.SetText(tmp);
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ if (tmp.Find(mir_path, 0) == 0) {
+ CMStringW path = tmp.Mid(mir_wstrlen(mir_path));
+ edit_HOME_DIR.SetText(tmp);
+ }
+ }
+};
+
+class COptGpgMsgDlg : public CDlgBase
+{
+ CCtrlCheck check_APPEND_TAGS, check_STRIP_TAGS;
+ CCtrlEdit edit_IN_OPEN_TAG, edit_IN_CLOSE_TAG, edit_OUT_OPEN_TAG, edit_OUT_CLOSE_TAG;
+
+public:
+ COptGpgMsgDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_MESSAGES),
+ check_APPEND_TAGS(this, IDC_APPEND_TAGS), check_STRIP_TAGS(this, IDC_STRIP_TAGS),
+ edit_IN_OPEN_TAG(this, IDC_IN_OPEN_TAG), edit_IN_CLOSE_TAG(this, IDC_IN_CLOSE_TAG), edit_OUT_OPEN_TAG(this, IDC_OUT_OPEN_TAG), edit_OUT_CLOSE_TAG(this, IDC_OUT_CLOSE_TAG)
+ {
+ CreateLink(check_STRIP_TAGS, g_plugin.bStripTags);
+ CreateLink(check_APPEND_TAGS, g_plugin.bAppendTags);
+ }
+
+ bool OnInitDialog() override
+ {
+ edit_IN_OPEN_TAG.SetText(g_plugin.getMStringW("szInOpenTag", L"<GPGdec>"));
+ edit_IN_CLOSE_TAG.SetText(g_plugin.getMStringW("szInCloseTag", L"</GPGdec>"));
+ edit_OUT_OPEN_TAG.SetText(g_plugin.getMStringW("szOutOpenTag", L"<GPGenc>"));
+ edit_OUT_CLOSE_TAG.SetText(g_plugin.getMStringW("szOutCloseTag", L"</GPGenc>"));
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ ptrW tmp(edit_IN_OPEN_TAG.GetText());
+ g_plugin.setWString("szInOpenTag", tmp);
+ globals.wszInopentag = tmp;
+
+ tmp = edit_IN_CLOSE_TAG.GetText();
+ g_plugin.setWString("szInCloseTag", tmp);
+ globals.wszInclosetag = tmp;
+
+ tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText());
+ g_plugin.setWString("szOutOpenTag", tmp);
+ globals.wszOutopentag = tmp;
+
+ tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText());
+ g_plugin.setWString("szOutCloseTag", tmp);
+ globals.wszOutclosetag = tmp;
+ return true;
+ }
+};
+
+class COptGpgAdvDlg : public CDlgBase
+{
+ CCtrlButton btn_EXPORT, btn_IMPORT;
+ CCtrlCheck chkPresenceSub, chkSendErrors;
+
+public:
+ COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED),
+ btn_EXPORT(this, IDC_EXPORT),
+ btn_IMPORT(this, IDC_IMPORT),
+ chkSendErrors(this, IDC_SEND_ERRORS),
+ chkPresenceSub(this, IDC_PRESCENSE_SUBSCRIPTION)
+ {
+ btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT);
+ btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT);
+
+ CreateLink(chkSendErrors, g_plugin.bSendErrorMessages);
+ CreateLink(chkPresenceSub, g_plugin.bPresenceSigning);
+ }
+
+ bool OnInitDialog() override
+ {
+ chkPresenceSub.Enable(g_plugin.bJabberAPI);
+ return true;
+ }
+
+ void onClick_EXPORT(CCtrlButton*)
+ {
+ INT_PTR ExportGpGKeys(WPARAM w, LPARAM l);
+ ExportGpGKeys(NULL, NULL);
+ }
+
+ void onClick_IMPORT(CCtrlButton*)
+ {
+ INT_PTR ImportGpGKeys(WPARAM w, LPARAM l);
+ ImportGpGKeys(NULL, NULL);
+ }
+};
+
+CCtrlEdit *edit_p_PubKeyEdit = nullptr;
+
+static LRESULT CALLBACK editctrl_ctrl_a(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_KEYDOWN:
+ if (wParam == 0x41 && GetKeyState(VK_CONTROL) < 0)
+ SendMessage(hwndDlg, EM_SETSEL, 0, -1);
+ return 0;
+ }
+ return mir_callNextSubclass(hwndDlg, editctrl_ctrl_a, msg, wParam, lParam);
+}
+
+class CDlgLoadPubKeyDlg : public CDlgBase
+{
+ MCONTACT hContact;
+ wstring key_buf;
+ wstring::size_type ws1 = 0, ws2 = 0;
+ CCtrlCheck chk_ENABLE_ENCRYPTION;
+ CCtrlButton btn_SELECT_EXISTING, btn_OK, btn_LOAD_FROM_FILE, btn_IMPORT;
+ CCtrlEdit edit_PUBLIC_KEY_EDIT;
+
+public:
+ CDlgLoadPubKeyDlg(MCONTACT _p1) :
+ CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY),
+ hContact(_p1),
+ chk_ENABLE_ENCRYPTION(this, IDC_ENABLE_ENCRYPTION),
+ btn_SELECT_EXISTING(this, IDC_SELECT_EXISTING), btn_OK(this, ID_OK), btn_LOAD_FROM_FILE(this, ID_LOAD_FROM_FILE), btn_IMPORT(this, IDC_IMPORT),
+ edit_PUBLIC_KEY_EDIT(this, IDC_PUBLIC_KEY_EDIT)
+ {
+ btn_SELECT_EXISTING.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_SELECT_EXISTING);
+ btn_OK.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_OK);
+ btn_LOAD_FROM_FILE.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_LOAD_FROM_FILE);
+ btn_IMPORT.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_IMPORT);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+
+ mir_subclassWindow(GetDlgItem(m_hwnd, IDC_PUBLIC_KEY_EDIT), editctrl_ctrl_a);
+ MCONTACT hcnt = db_mc_tryMeta(hContact);
+ {
+ wstring msg = TranslateT("Load Public GPG Key for ");
+ msg += Clist_GetContactDisplayName(hcnt, 0);
+ this->SetCaption(msg.c_str());
+ }
+ if (!hcnt) {
+ btn_SELECT_EXISTING.Disable();
+ chk_ENABLE_ENCRYPTION.Disable();
+ }
+ if (isContactSecured(hcnt))
+ chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn off encryption"));
+ else {
+ chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn on encryption"));
+ chk_ENABLE_ENCRYPTION.SetState(1);
+ }
+ if (hcnt) {
+ wstring str = ptrW(g_plugin.getWStringA(hcnt, "GPGPubKey", L""));
+ if (!str.empty()) {
+ wstring::size_type p = 0, stop = 0;
+ for (;;) {
+ if ((p = str.find(L"\n", p + 2)) != wstring::npos) {
+ if (p > stop) {
+ stop = p;
+ str.insert(p, L"\r");
+ }
+ else break;
+ }
+ }
+ }
+
+ if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) {
+ if (g_plugin.getMStringA(hcnt, "KeyID").IsEmpty()) {
+ gpg_execution_params params;
+ params.addParam(L"--export");
+ params.addParam(L"-a");
+ params.addParam(toUTF16(globals.hcontact_data[hcnt].key_in_prescense));
+ gpg_launcher(params); //TODO: handle errors
+
+ if ((params.out.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != -1) && (params.out.Find("-----END PGP PUBLIC KEY BLOCK-----") != -1)) {
+ params.out.Replace("\n", "\r\n");
+
+ wchar_t *tmp3 = mir_a2u(params.out.c_str());
+ str.clear();
+ str.append(tmp3);
+ mir_free(tmp3);
+ string msg = Translate("Load Public GPG Key for ");
+ msg += _T2A(Clist_GetContactDisplayName(hcnt));
+ msg += " (Key ID: ";
+ msg += globals.hcontact_data[hcnt].key_in_prescense;
+ msg += Translate(" found in presence, and exists in keyring.)");
+ SetCaption(toUTF16(msg).c_str());
+ }
+ else {
+ string msg = Translate("Load Public GPG Key (Key ID: ");
+ msg += globals.hcontact_data[hcnt].key_in_prescense;
+ msg += Translate(" found in presence.)");
+ SetCaption(toUTF16(msg).c_str());
+ btn_IMPORT.Enable();
+ }
+ }
+ }
+
+ edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L"");
+ }
+ edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT;
+ return true;
+ }
+
+ virtual void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow");
+ edit_p_PubKeyEdit = nullptr;
+ }
+
+ void onClick_SELECT_EXISTING(CCtrlButton*)
+ {
+ (new CDlgLoadExistingKey())->Show();
+ }
+
+ void onClick_OK(CCtrlButton*)
+ {
+ wchar_t *tmp = mir_wstrdup(edit_PUBLIC_KEY_EDIT.GetText());
+ wchar_t *begin, *end;
+ key_buf.append(tmp);
+ key_buf.append(L"\n"); //no new line at end of file )
+ mir_free(tmp);
+ while ((ws1 = key_buf.find(L"\r", ws1)) != wstring::npos) {
+ key_buf.erase(ws1, 1); //remove windows specific trash
+ }
+ ws1 = 0;
+ if (((ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) {
+ begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PUBLIC KEY BLOCK-----") + 1));
+ mir_wstrcpy(begin, L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----") + 1));
+ mir_wstrcpy(end, L"-----END PGP PUBLIC KEY BLOCK-----");
+ }
+ else if (((ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) {
+ begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PRIVATE KEY BLOCK-----") + 1));
+ mir_wstrcpy(begin, L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
+ end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----") + 1));
+ mir_wstrcpy(end, L"-----END PGP PRIVATE KEY BLOCK-----");
+ }
+ else {
+ MessageBox(nullptr, TranslateT("This is not public or private key"), L"INFO", MB_OK);
+ return;
+ }
+ ws2 += mir_wstrlen(end);
+ bool allsubcontacts = false;
+ {
+ if (db_mc_isMeta(hContact)) {
+ if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
+ allsubcontacts = true;
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setWString(hcnt, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
+ }
+ }
+ else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
+ }
+ else g_plugin.setWString(hContact, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str());
+ }
+ tmp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (key_buf.length() + 1));
+ mir_wstrcpy(tmp, key_buf.substr(ws1, ws2 - ws1).c_str());
+ { //gpg execute block
+ std::vector<wstring> cmd;
+ CMStringW tmp2;
+ {
+ MCONTACT hcnt = db_mc_tryMeta(hContact);
+ tmp2 = g_plugin.getMStringW("szHomePath");
+ tmp2 += L"\\temporary_exported.asc";
+ boost::filesystem::remove(tmp2.c_str());
+
+ wfstream f(tmp2, std::ios::out);
+ CMStringW str = g_plugin.getMStringW(hcnt, "GPGPubKey");
+ str.Replace(L"\r", L"");
+ f << str.c_str();
+ f.close();
+ }
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ mir_free(begin);
+ mir_free(end);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.delSetting(hcnt, "bAlwatsTrust");
+ }
+ }
+ else g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust");
+ }
+ else g_plugin.delSetting(hContact, "bAlwatsTrust");
+ }
+
+ string output(params.out);
+ {
+ if (output.find("already in secret keyring") != string::npos) {
+ MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK);
+ boost::filesystem::remove(tmp2.c_str());
+ return;
+ }
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ {
+ char *tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp3, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyID", tmp3);
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyID", tmp3);
+ }
+ else g_plugin.setString(hContact, "KeyID", tmp3);
+ }
+ mir_free(tmp3);
+ }
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(1, toUTF16(output.substr(s, s2 - s)).c_str());
+
+ s = output.find("“", s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else
+ s += 3;
+ bool uncommon = false;
+ if ((s2 = output.find("(", s)) == string::npos) {
+ if ((s2 = output.find("<", s)) == string::npos) {
+ s2 = output.find("”", s);
+ uncommon = true;
+ }
+ }
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s2 != string::npos && s != string::npos) {
+ {
+ char *tmp3 = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1));
+ mir_strcpy(tmp3, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ mir_free(tmp3);
+ }
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(2, toUTF16(output.substr(s, s2 - s - 1)).c_str());
+
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (s != string::npos && s2 != string::npos) {
+ if (output[s] == ')') {
+ char *tmp3 = (char*)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
+ mir_strcpy(tmp3, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyComment", output.substr(s2, s - s2).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", output.substr(s2, s - s2).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str());
+ }
+ mir_free(tmp3);
+ s += 3;
+ s2 = output.find(">", s);
+ tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp3, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ }
+ mir_free(tmp3);
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(3, toUTF16(output.substr(s, s2 - s)).c_str());
+ }
+ else {
+ char *tmp3 = (char*)mir_alloc(output.substr(s2, s - s2).length() + 1);
+ mir_strcpy(tmp3, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp3, nullptr);
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ }
+ }
+ else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ }
+ else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ }
+ mir_free(tmp3);
+
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetLineText(3, toUTF16(output.substr(s2, s - s2)).c_str());
+ }
+ }
+ }
+ if (hContact && g_pMainDlg)
+ g_pMainDlg->SetListAutoSize();
+ }
+ if (!hContact) {
+ gpg_execution_params params2;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(g_plugin.getMStringW(hContact, "KeyID").c_str());
+ if (!gpg_launcher(params2))
+ return;
+ if (params2.result == pxNotFound)
+ return;
+
+ params2.out.Remove('\r');
+ g_plugin.setString(hContact, "GPGPubKey", params2.out.c_str());
+ }
+ MessageBoxA(nullptr, output.c_str(), "", MB_OK);
+ boost::filesystem::remove(tmp2.c_str());
+ }
+ key_buf.clear();
+ if (chk_ENABLE_ENCRYPTION.GetState()) {
+ if (hContact) {
+ if (db_mc_isMeta(hContact)) {
+ if (allsubcontacts) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt) {
+ g_plugin.setByte(hcnt, "GPGEncryption", !isContactSecured(hcnt));
+ setSrmmIcon(hContact);
+ }
+ }
+ }
+ else g_plugin.setByte(metaGetMostOnline(hContact), "GPGEncryption", !isContactSecured(hContact));
+ }
+ else g_plugin.setByte(hContact, "GPGEncryption", !isContactSecured(hContact));
+ }
+ }
+ this->Close();
+ }
+
+ void onClick_LOAD_FROM_FILE(CCtrlButton *)
+ {
+ ptrW tmp(GetFilePath(TranslateT("Set file containing GPG public key"), L"*", TranslateT("GPG public key file")));
+ if (!tmp)
+ return;
+
+ wfstream f(tmp, std::ios::in | std::ios::ate | std::ios::binary);
+ if (!f.is_open()) {
+ MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
+ return;
+ }
+ if (f.is_open()) {
+ std::wifstream::pos_type size = f.tellg();
+ wchar_t *temp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1];
+ f.seekg(0, std::ios::beg);
+ f.read(temp, size);
+ temp[size] = '\0';
+ key_buf.append(temp);
+ delete[] temp;
+ f.close();
+ }
+ if (key_buf.empty()) {
+ key_buf.clear();
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to read key file";
+ return;
+ }
+ ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----");
+ ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (ws2 == wstring::npos || ws1 == wstring::npos) {
+ ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----");
+ ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----");
+ }
+ if (ws2 == wstring::npos || ws1 == wstring::npos) {
+ MessageBox(nullptr, TranslateT("There is no public or private key."), TranslateT("Info"), MB_OK);
+ return;
+ }
+ ws2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----");
+ edit_PUBLIC_KEY_EDIT.SetText(key_buf.substr(ws1, ws2 - ws1).c_str());
+ key_buf.clear();
+ }
+
+ void onClick_IMPORT(CCtrlButton *)
+ {
+ CDlgImportKey *d = new CDlgImportKey(hContact);
+ d->Show();
+ }
+};
+
+
+void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal)
+{
+ CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(hContact);
+ if (bModal)
+ d->DoModal();
+ else
+ d->Show();
+}
+
+int GpgOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.szGroup.w = LPGENW("Services");
+ odp.szTitle.w = _T(MODULENAME);
+
+ odp.szTab.w = LPGENW("Main");
+ odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE;
+ odp.pDialog = new COptGpgMainDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("GnuPG Variables");
+ odp.pDialog = new COptGpgBinDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("Messages");
+ odp.pDialog = new COptGpgMsgDlg();
+ g_plugin.addOptions(wParam, &odp);
+
+ odp.szTab.w = LPGENW("Advanced");
+ odp.pDialog = new COptGpgAdvDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/New_GPG/src/options.h b/plugins/New_GPG/src/options.h
index 45c92f914a..b7d98935c2 100644
--- a/plugins/New_GPG/src/options.h
+++ b/plugins/New_GPG/src/options.h
@@ -1,22 +1,22 @@
-// Copyright © 2017-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef OPTIONS_H
-#define OPTIONS_H
-
-
-
+// Copyright © 2017-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+
+
#endif \ No newline at end of file
diff --git a/plugins/New_GPG/src/srmm.cpp b/plugins/New_GPG/src/srmm.cpp
index 589dda598d..ee575c4947 100644
--- a/plugins/New_GPG/src/srmm.cpp
+++ b/plugins/New_GPG/src/srmm.cpp
@@ -1,78 +1,78 @@
-// Copyright © 2010-22 SecureIM developers (baloo and others), sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with 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 void ToggleIcon(MCONTACT hContact)
-{
- MCONTACT hMeta = NULL;
- if (db_mc_isMeta(hContact)) {
- hMeta = hContact;
- hContact = metaGetMostOnline(hContact); // возьмем тот, через который пойдет сообщение
- }
- else if (db_mc_isSub(hContact))
- hMeta = db_mc_getMeta(hContact);
-
- int enc = g_plugin.getByte(hContact, "GPGEncryption");
- if (enc) {
- g_plugin.setByte(hContact, "GPGEncryption", 0);
- if (hMeta)
- g_plugin.setByte(hMeta, "GPGEncryption", 0);
- setSrmmIcon(hContact);
- }
- else if (!enc) {
- if (!isContactHaveKey(hContact))
- ShowLoadPublicKeyDialog(hContact, false);
- else {
- g_plugin.setByte(hContact, "GPGEncryption", 1);
- if (hMeta)
- g_plugin.setByte(hMeta, "GPGEncryption", 1);
- setSrmmIcon(hContact);
- return;
- }
-
- if (isContactHaveKey(hContact)) {
- g_plugin.setByte(hContact, "GPGEncryption", 1);
- if (hMeta)
- g_plugin.setByte(hMeta, "GPGEncryption", 1);
- setSrmmIcon(hContact);
- }
- }
-}
-
-int __cdecl onWindowEvent(WPARAM, LPARAM lParam)
-{
- MessageWindowEventData *mwd = (MessageWindowEventData *)lParam;
- if (mwd->uType == MSG_WINDOW_EVT_OPEN || mwd->uType == MSG_WINDOW_EVT_OPENING)
- if (isContactHaveKey(mwd->hContact))
- setSrmmIcon(mwd->hContact);
- return 0;
-}
-
-int __cdecl onIconPressed(WPARAM hContact, LPARAM lParam)
-{
- StatusIconClickData *sicd = (StatusIconClickData *)lParam;
- if (!mir_strcmp(sicd->szModule, MODULENAME))
- ToggleIcon(hContact);
-
- return 0;
-}
-
-int __cdecl onExtraIconPressed(WPARAM hContact, LPARAM, LPARAM)
-{
- ToggleIcon(hContact);
- return 0;
-}
+// Copyright © 2010-23 SecureIM developers (baloo and others), sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with 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 void ToggleIcon(MCONTACT hContact)
+{
+ MCONTACT hMeta = NULL;
+ if (db_mc_isMeta(hContact)) {
+ hMeta = hContact;
+ hContact = metaGetMostOnline(hContact); // возьмем тот, через который пойдет сообщение
+ }
+ else if (db_mc_isSub(hContact))
+ hMeta = db_mc_getMeta(hContact);
+
+ int enc = g_plugin.getByte(hContact, "GPGEncryption");
+ if (enc) {
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ if (hMeta)
+ g_plugin.setByte(hMeta, "GPGEncryption", 0);
+ setSrmmIcon(hContact);
+ }
+ else if (!enc) {
+ if (!isContactHaveKey(hContact))
+ ShowLoadPublicKeyDialog(hContact, false);
+ else {
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ if (hMeta)
+ g_plugin.setByte(hMeta, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ return;
+ }
+
+ if (isContactHaveKey(hContact)) {
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ if (hMeta)
+ g_plugin.setByte(hMeta, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ }
+ }
+}
+
+int __cdecl onWindowEvent(WPARAM, LPARAM lParam)
+{
+ MessageWindowEventData *mwd = (MessageWindowEventData *)lParam;
+ if (mwd->uType == MSG_WINDOW_EVT_OPEN || mwd->uType == MSG_WINDOW_EVT_OPENING)
+ if (isContactHaveKey(mwd->hContact))
+ setSrmmIcon(mwd->hContact);
+ return 0;
+}
+
+int __cdecl onIconPressed(WPARAM hContact, LPARAM lParam)
+{
+ StatusIconClickData *sicd = (StatusIconClickData *)lParam;
+ if (!mir_strcmp(sicd->szModule, MODULENAME))
+ ToggleIcon(hContact);
+
+ return 0;
+}
+
+int __cdecl onExtraIconPressed(WPARAM hContact, LPARAM, LPARAM)
+{
+ ToggleIcon(hContact);
+ return 0;
+}
diff --git a/plugins/New_GPG/src/stdafx.cxx b/plugins/New_GPG/src/stdafx.cxx
index d265a4c02e..8c570f6949 100644
--- a/plugins/New_GPG/src/stdafx.cxx
+++ b/plugins/New_GPG/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/>.
-*/
-
-#include "stdafx.h"
+/*
+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"
diff --git a/plugins/New_GPG/src/stdafx.h b/plugins/New_GPG/src/stdafx.h
index 3553a65c41..5abc9c16f7 100644
--- a/plugins/New_GPG/src/stdafx.h
+++ b/plugins/New_GPG/src/stdafx.h
@@ -1,98 +1,98 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef COMMONHEADERS_H
-#define COMMONHEADERS_H
-
-#pragma warning(disable:4512 4267 4127)
-
-#define WIN32_LEAN_AND_MEAN
-#define _SCL_SECURE_NO_WARNINGS
-
-#include <io.h>
-
-// windows
-#include <windows.h>
-#include <commdlg.h>
-#include <shlobj.h>
-#include <shlwapi.h>
-
-// c++
-#include <map>
-using std::map;
-#include <list>
-using std::list;
-#include <string>
-using std::string;
-using std::wstring;
-#include <fstream>
-using std::wfstream;
-using std::fstream;
-
-// boost
-#include <boost/nondet_random.hpp>
-#include <boost/random/variate_generator.hpp>
-#include <boost/random/uniform_int.hpp>
-#include <boost/date_time.hpp>
-#include <boost/iostreams/stream.hpp>
-
-// boost process
-#include <boost/process.hpp>
-#include <boost/process/windows.hpp>
-
-// miranda
-#include <newpluginapi.h>
-#include <m_contacts.h>
-#include <m_database.h>
-#include <m_options.h>
-#include <m_langpack.h>
-#include <m_skin.h>
-#include <m_jabber.h>
-#include <m_message.h>
-#include <m_clist.h>
-#include <m_cluiframes.h>
-#include <m_icolib.h>
-#include <m_extraicons.h>
-#include <m_gui.h>
-
-#include <m_metacontacts.h>
-
-struct CMPlugin : public PLUGIN<CMPlugin>
-{
- CMOption<bool> bJabberAPI, bPresenceSigning, bFileTransfers, bAutoExchange, bSameAction, bAppendTags, bStripTags, bDebugLog, bSendErrorMessages;
- HANDLE hCLIcon = nullptr;
- HGENMENU hToggleEncryption = nullptr, hSendKey = nullptr;
-
- CMPlugin();
-
- int Load() override;
- int Unload() override;
-};
-
-// internal
-#include "resource.h"
-#include "version.h"
-#include "constants.h"
-#include "log.h"
-#include "utilities.h"
-#include "gpg_wrapper.h"
-#include "jabber_account.h"
-#include "metacontacts.h"
-#include "ui.h"
-#include "options.h"
-#include "globals.h"
-
-#endif
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef COMMONHEADERS_H
+#define COMMONHEADERS_H
+
+#pragma warning(disable:4512 4267 4127)
+
+#define WIN32_LEAN_AND_MEAN
+#define _SCL_SECURE_NO_WARNINGS
+
+#include <io.h>
+
+// windows
+#include <windows.h>
+#include <commdlg.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+
+// c++
+#include <map>
+using std::map;
+#include <list>
+using std::list;
+#include <string>
+using std::string;
+using std::wstring;
+#include <fstream>
+using std::wfstream;
+using std::fstream;
+
+// boost
+#include <boost/nondet_random.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/date_time.hpp>
+#include <boost/iostreams/stream.hpp>
+
+// boost process
+#include <boost/process.hpp>
+#include <boost/process/windows.hpp>
+
+// miranda
+#include <newpluginapi.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_langpack.h>
+#include <m_skin.h>
+#include <m_jabber.h>
+#include <m_message.h>
+#include <m_clist.h>
+#include <m_cluiframes.h>
+#include <m_icolib.h>
+#include <m_extraicons.h>
+#include <m_gui.h>
+
+#include <m_metacontacts.h>
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMOption<bool> bJabberAPI, bPresenceSigning, bFileTransfers, bAutoExchange, bSameAction, bAppendTags, bStripTags, bDebugLog, bSendErrorMessages;
+ HANDLE hCLIcon = nullptr;
+ HGENMENU hToggleEncryption = nullptr, hSendKey = nullptr;
+
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
+// internal
+#include "resource.h"
+#include "version.h"
+#include "constants.h"
+#include "log.h"
+#include "utilities.h"
+#include "gpg_wrapper.h"
+#include "jabber_account.h"
+#include "metacontacts.h"
+#include "ui.h"
+#include "options.h"
+#include "globals.h"
+
+#endif
diff --git a/plugins/New_GPG/src/ui.cpp b/plugins/New_GPG/src/ui.cpp
index 78ff38af92..2118ce723a 100644
--- a/plugins/New_GPG/src/ui.cpp
+++ b/plugins/New_GPG/src/ui.cpp
@@ -1,902 +1,902 @@
-// Copyright (c) 2017-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-bool CDlgEncryptedFileMsgBox::OnInitDialog()
-{
- globals.bDecryptFiles = false;
- return true;
-}
-
-CDlgEncryptedFileMsgBox::CDlgEncryptedFileMsgBox() :
- CDlgBase(g_plugin, IDD_ENCRYPTED_FILE_MSG_BOX),
- chk_REMEMBER(this, IDC_REMEMBER),
- btn_IGNORE(this, IDC_IGNORE),
- btn_DECRYPT(this, IDC_DECRYPT)
-{
- btn_IGNORE.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_IGNORE);
- btn_DECRYPT.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_DECRYPT);
-}
-
-void CDlgEncryptedFileMsgBox::onClick_IGNORE(CCtrlButton*)
-{
- if (chk_REMEMBER.GetState())
- g_plugin.bSameAction = true;
-
- this->Close();
-}
-
-void CDlgEncryptedFileMsgBox::onClick_DECRYPT(CCtrlButton*)
-{
- globals.bDecryptFiles = true;
- if (chk_REMEMBER.GetState()) {
- g_plugin.bFileTransfers = true;
- g_plugin.bSameAction = false;
- }
- this->Close();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-class CDlgExportKeysMsgBox : public CDlgBase
-{
- CCtrlCheck chk_PUBLIC, chk_PRIVATE, chk_ALL;
-
-public:
- CDlgExportKeysMsgBox() :
- CDlgBase(g_plugin, IDD_EXPORT_TYPE),
- chk_PUBLIC(this, IDC_PUBLIC),
- chk_PRIVATE(this, IDC_PRIVATE),
- chk_ALL(this, IDC_ALL)
- {
- }
-
- bool OnInitDialog() override
- {
- chk_PUBLIC.SetState(true);
- return true;
- }
-
- bool OnApply() override
- {
- if (chk_PUBLIC.GetState())
- ExportGpGKeysFunc(0);
- else if (chk_PRIVATE.GetState())
- ExportGpGKeysFunc(1);
- else if (chk_ALL.GetState())
- ExportGpGKeysFunc(2);
- return true;
- }
-};
-
-INT_PTR ExportGpGKeys(WPARAM, LPARAM)
-{
- (new CDlgExportKeysMsgBox())->Show();
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-class CDlgChangePasswdMsgBox : public CDlgBase //always modal
-{
- CCtrlEdit edit_NEW_PASSWD1, edit_NEW_PASSWD2, edit_OLD_PASSWD;
-
-public:
- CDlgChangePasswdMsgBox() :
- CDlgBase(g_plugin, IDD_CHANGE_PASSWD),
- edit_NEW_PASSWD1(this, IDC_NEW_PASSWD1),
- edit_NEW_PASSWD2(this, IDC_NEW_PASSWD2),
- edit_OLD_PASSWD(this, IDC_OLD_PASSWD)
- {
- }
-
- bool OnApply() override
- {
- //TODO: show some prgress
- if (mir_wstrcmp(edit_NEW_PASSWD1.GetText(), edit_NEW_PASSWD2.GetText())) {
- MessageBox(m_hwnd, TranslateT("New passwords do not match"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- std::string old_pass, new_pass;
- new_pass = toUTF8(ptrW(edit_NEW_PASSWD1.GetText()).get());
- old_pass = toUTF8(ptrW(edit_OLD_PASSWD.GetText()).get());
-
- bool old_pass_match = false;
- if (!mir_strcmp(ptrA(g_plugin.getUStringA("szKeyPassword")), old_pass.c_str()))
- old_pass_match = true;
-
- if (!old_pass_match) {
- if (globals.key_id_global[0]) {
- string dbsetting = "szKey_";
- dbsetting += toUTF8(globals.key_id_global);
- dbsetting += "_Password";
- ptrA pass(g_plugin.getUStringA(dbsetting.c_str()));
- if (!mir_strcmp(pass, old_pass.c_str()))
- old_pass_match = true;
- }
- }
-
- if (!old_pass_match)
- if (MessageBox(m_hwnd, TranslateT("Old password does not match, you can continue, but GPG will reject wrong password.\nDo you want to continue?"), TranslateT("Error"), MB_YESNO) == IDNO)
- return false;
-
- gpg_execution_params_pass params(old_pass, new_pass);
- params.addParam(L"--edit-key");
- params.addParam(globals.key_id_global);
- params.addParam(L"passwd");
-
- HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(&pxEexcute_passwd_change_thread, &params);
- if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) {
- if (params.child)
- params.child->terminate();
- if (globals.debuglog)
- globals.debuglog << "GPG execution timed out, aborted";
- return true;
- }
-
- return params.result != pxNotFound;
- }
-};
-
-void ShowChangePasswdDlg()
-{
- CDlgChangePasswdMsgBox *d = new CDlgChangePasswdMsgBox;
- d->DoModal();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// New key generation dialog
-
-class CDlgKeyGen : public CDlgBase
-{
- CCtrlCombo combo_KEY_TYPE;
- CCtrlEdit edit_KEY_LENGTH, edit_KEY_PASSWD, edit_KEY_REAL_NAME, edit_KEY_EMAIL, edit_KEY_COMMENT, edit_KEY_EXPIRE_DATE;
- CCtrlData lbl_GENERATING_TEXT;
-
-public:
- CDlgKeyGen() :
- CDlgBase(g_plugin, IDD_KEY_GEN),
- combo_KEY_TYPE(this, IDC_KEY_TYPE),
- edit_KEY_LENGTH(this, IDC_KEY_LENGTH),
- edit_KEY_PASSWD(this, IDC_KEY_PASSWD),
- edit_KEY_REAL_NAME(this, IDC_KEY_REAL_NAME),
- edit_KEY_EMAIL(this, IDC_KEY_EMAIL),
- edit_KEY_COMMENT(this, IDC_KEY_COMMENT),
- edit_KEY_EXPIRE_DATE(this, IDC_KEY_EXPIRE_DATE),
- lbl_GENERATING_TEXT(this, IDC_GENERATING_TEXT)
- {
- }
-
- bool OnInitDialog() override
- {
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow");
- SetCaption(TranslateT("Key Generation dialog"));
-
- combo_KEY_TYPE.AddString(L"RSA");
- combo_KEY_TYPE.AddString(L"DSA");
- combo_KEY_TYPE.SelectString(L"RSA");
- edit_KEY_EXPIRE_DATE.SetText(L"0");
- edit_KEY_LENGTH.SetText(L"4096");
- return true;
- }
-
- bool OnApply() override
- {
- // data sanity checks
- ptrW tmp(combo_KEY_TYPE.GetText());
- if (mir_wstrlen(tmp) < 3) {
- MessageBox(nullptr, TranslateT("You must set encryption algorithm first"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- tmp = edit_KEY_LENGTH.GetText();
- int length = _wtoi(tmp);
- if (length < 1024 || length > 4096) {
- MessageBox(nullptr, TranslateT("Key length must be of length from 1024 to 4096 bits"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- tmp = edit_KEY_EXPIRE_DATE.GetText();
- if (mir_wstrlen(tmp) != 10 && tmp[0] != '0') {
- MessageBox(nullptr, TranslateT("Invalid date"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- tmp = edit_KEY_REAL_NAME.GetText();
- if (mir_wstrlen(tmp) < 4) {
- MessageBox(nullptr, TranslateT("Name must contain at least 4 characters"), TranslateT("Error"), MB_OK);
- return false;
- }
- if (wcschr(tmp, '(') || wcschr(tmp, ')')) {
- MessageBox(nullptr, TranslateT("Name cannot contain '(' or ')'"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- tmp = edit_KEY_EMAIL.GetText();
- if (mir_wstrlen(tmp) < 5 || !wcschr(tmp, '@')) {
- MessageBox(nullptr, TranslateT("Invalid Email"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- // generating key file
- CMStringW path = g_plugin.getMStringW("szHomePath");
- path += L"\\new_key";
- wfstream f(path.c_str(), std::ios::out);
- if (!f.is_open()) {
- MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
- return false;
- }
-
- f << "Key-Type: ";
- char *tmp2 = mir_u2a(combo_KEY_TYPE.GetText());
- char *subkeytype = (char *)mir_alloc(6);
- if (strstr(tmp2, "RSA"))
- mir_strcpy(subkeytype, "RSA");
- else if (strstr(tmp2, "DSA")) //this is useless check for now, but it will be required if someone add another key types support
- mir_strcpy(subkeytype, "ELG-E");
- f << tmp2;
- mir_free(tmp2);
- f << "\n";
- f << "Key-Length: ";
- f << _wtoi(edit_KEY_LENGTH.GetText());
- f << "\n";
- f << "Subkey-Length: ";
- f << _wtoi(edit_KEY_LENGTH.GetText());
- f << "\n";
- f << "Subkey-Type: ";
- f << subkeytype;
- mir_free(subkeytype);
- f << "\n";
- if (edit_KEY_PASSWD.GetText()[0]) {
- f << "Passphrase: ";
- f << toUTF8(edit_KEY_PASSWD.GetText()).c_str();
- f << "\n";
- }
- f << "Name-Real: ";
- f << toUTF8(edit_KEY_REAL_NAME.GetText()).c_str();
- f << "\n";
- if (edit_KEY_COMMENT.GetText()[0]) {
- f << "Name-Comment: ";
- f << toUTF8(edit_KEY_COMMENT.GetText()).c_str();
- f << "\n";
- }
- f << "Name-Email: ";
- f << toUTF8(edit_KEY_EMAIL.GetText()).c_str();
- f << "\n";
- f << "Expire-Date: ";
- f << toUTF8(edit_KEY_EXPIRE_DATE.GetText()).c_str();
- f << "\n";
- f.close();
-
- lbl_GENERATING_TEXT.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE);
- lbl_GENERATING_TEXT.SetText(TranslateT("Generating new key, please wait..."));
- combo_KEY_TYPE.Disable();
- edit_KEY_LENGTH.Disable();
- edit_KEY_PASSWD.Disable();
- edit_KEY_REAL_NAME.Disable();
- edit_KEY_EMAIL.Disable();
- edit_KEY_COMMENT.Disable();
- edit_KEY_EXPIRE_DATE.Disable();
-
- // gpg execution
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--yes");
- params.addParam(L"--gen-key");
- params.addParam(path.c_str());
- params.bNoOutput = true;
- if (!gpg_launcher(params, boost::posix_time::minutes(10)))
- return false;
- if (params.result == pxNotFound)
- return false;
-
- boost::filesystem::remove(path.c_str());
- return true;
- }
-
- void OnDestroy() override
- {
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow");
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// First run dialog
-
-class CDlgFirstRun : public CDlgBase
-{
- void refresh_key_list()
- {
- list_KEY_LIST.DeleteAllItems();
- int i = 1;
-
- // parse gpg output
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--list-secret-keys");
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- wstring::size_type p = 0, p2 = 0, stop = 0;
- string out(params.out);
- while (p != string::npos) {
- if ((p = out.find("sec ", p)) == string::npos)
- break;
- p += 5;
- if (p < stop)
- break;
- stop = p;
- p2 = out.find("/", p) - 1;
- wchar_t *key_len = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()), *creation_date = nullptr, *expire_date = nullptr;
- p2 += 2;
- p = out.find(" ", p2);
- std::wstring key_id = toUTF16(out.substr(p2, p - p2));
- p += 1;
- p2 = out.find(" ", p);
- std::string::size_type p3 = out.find("\n", p);
- if ((p2 != std::string::npos) && (p3 < p2)) {
- p2 = p3;
- creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p - 1)).c_str());
- }
- else {
- creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
- p2 = out.find("[", p2);
- p2 = out.find("expires:", p2);
- p2 += mir_strlen("expires:");
- if (p2 != std::string::npos) {
- p2++;
- p = p2;
- p2 = out.find("]", p);
- expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
- //check expiration
- bool expired = false;
- {
- boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
- wchar_t buf[5];
- wcsncpy_s(buf, expire_date, _TRUNCATE);
- int year = _wtoi(buf);
- if (year < now.date().year())
- expired = true;
- else if (year == now.date().year()) {
- wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
- int month = _wtoi(buf);
- if (month < now.date().month())
- expired = true;
- else if (month == now.date().month()) {
- wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
- unsigned day = _wtoi(buf);
- if (day <= now.date().day_number())
- expired = true;
- }
- }
- }
- if (expired) {
- mir_free(key_len);
- mir_free(creation_date);
- mir_free(expire_date);
- //mimic normal behaviour
- p = out.find("uid ", p);
- p2 = out.find_first_not_of(" ", p + 5);
- p = out.find("<", p2);
- p++;
- //p2 = out.find(">", p);
- //
- continue; //does not add to key list
- }
- }
- }
- int row = list_KEY_LIST.AddItem(L"", 0);
- list_KEY_LIST.SetItemText(row, 3, creation_date);
- mir_free(creation_date);
- if (expire_date) {
- list_KEY_LIST.SetItemText(row, 4, expire_date);
- mir_free(expire_date);
- }
- list_KEY_LIST.SetItemText(row, 5, key_len);
- mir_free(key_len);
- list_KEY_LIST.SetItemText(row, 0, (wchar_t *)key_id.c_str());
- p = out.find("uid ", p);
- p2 = out.find_first_not_of(" ", p + 5);
- p = out.find("<", p2);
-
- wstring tmp = toUTF16(out.substr(p2, p - p2));
- list_KEY_LIST.SetItemText(row, 2, (wchar_t *)tmp.c_str());
-
- p++;
- p2 = out.find(">", p);
-
- tmp = toUTF16(out.substr(p, p2 - p));
- list_KEY_LIST.SetItemText(row, 1, (wchar_t *)tmp.c_str());
-
- // get accounts
- std::wstring accs;
- for (auto &pa : Accounts()) {
- std::string setting = pa->szModuleName;
- setting += "_KeyID";
- ptrW str(g_plugin.getWStringA(setting.c_str(), L""));
- if (key_id == str.get()) {
- if (!accs.empty())
- accs += L",";
- accs += pa->tszAccountName;
- }
- }
- list_KEY_LIST.SetItemText(row, 6, accs.c_str());
- }
- i++;
- list_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
- list_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
- list_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
- list_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
- list_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
- list_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE);
- list_KEY_LIST.SetColumnWidth(6, LVSCW_AUTOSIZE);
- }
-
- CCtrlListView list_KEY_LIST;
- CCtrlButton btn_COPY_PUBKEY, btn_EXPORT_PRIVATE, btn_CHANGE_PASSWD, btn_GENERATE_RANDOM, btn_GENERATE_KEY, btn_OTHER, btn_DELETE_KEY, btn_OK;
- CCtrlEdit edit_KEY_PASSWORD;
- CCtrlCombo combo_ACCOUNT;
- CCtrlData lbl_KEY_ID, lbl_GENERATING_KEY;
- wchar_t fp[16];
- const char *m_szCurrAcc = nullptr;
-
-public:
- CDlgFirstRun() :
- CDlgBase(g_plugin, IDD_FIRST_RUN),
- list_KEY_LIST(this, IDC_KEY_LIST),
- btn_COPY_PUBKEY(this, IDC_COPY_PUBKEY),
- btn_EXPORT_PRIVATE(this, IDC_EXPORT_PRIVATE),
- btn_CHANGE_PASSWD(this, IDC_CHANGE_PASSWD),
- btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM),
- btn_GENERATE_KEY(this, IDC_GENERATE_KEY),
- btn_OTHER(this, IDC_OTHER),
- btn_DELETE_KEY(this, IDC_DELETE_KEY),
- btn_OK(this, ID_OK),
- edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD),
- combo_ACCOUNT(this, IDC_ACCOUNT),
- lbl_KEY_ID(this, IDC_KEY_ID),
- lbl_GENERATING_KEY(this, IDC_GENERATING_KEY)
- {
- fp[0] = 0;
-
- btn_COPY_PUBKEY.OnClick = Callback(this, &CDlgFirstRun::onClick_COPY_PUBKEY);
- btn_EXPORT_PRIVATE.OnClick = Callback(this, &CDlgFirstRun::onClick_EXPORT_PRIVATE);
- btn_CHANGE_PASSWD.OnClick = Callback(this, &CDlgFirstRun::onClick_CHANGE_PASSWD);
- btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_RANDOM);
- btn_GENERATE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_KEY);
- btn_OTHER.OnClick = Callback(this, &CDlgFirstRun::onClick_OTHER);
- btn_DELETE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_DELETE_KEY);
- btn_OK.OnClick = Callback(this, &CDlgFirstRun::onClick_OK);
-
- combo_ACCOUNT.OnChange = Callback(this, &CDlgFirstRun::onChange_ACCOUNT);
-
- list_KEY_LIST.OnClick = Callback(this, &CDlgFirstRun::onChange_KEY_LIST);
- }
-
- bool OnInitDialog() override
- {
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow");
- SetCaption(TranslateT("Bind own keys to accounts"));
- btn_COPY_PUBKEY.Disable();
- btn_EXPORT_PRIVATE.Disable();
- btn_CHANGE_PASSWD.Disable();
-
- list_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50);
- list_KEY_LIST.AddColumn(1, TranslateT("Email"), 30);
- list_KEY_LIST.AddColumn(2, TranslateT("Name"), 250);
- list_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30);
- list_KEY_LIST.AddColumn(4, TranslateT("Expire date"), 30);
- list_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30);
- list_KEY_LIST.AddColumn(6, TranslateT("Accounts"), 30);
- list_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
-
- refresh_key_list();
-
- combo_ACCOUNT.AddString(TranslateT("Default"));
-
- for (auto &pa : Accounts()) {
- if (StriStr(pa->szModuleName, "metacontacts"))
- continue;
- if (StriStr(pa->szModuleName, "weather"))
- continue;
-
- combo_ACCOUNT.AddString(pa->tszAccountName, (LPARAM)pa->szModuleName);
- }
- combo_ACCOUNT.SetCurSel(0);
-
- lbl_KEY_ID.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get()));
- return true;
- }
-
- void OnDestroy() override
- {
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow");
- }
-
- void onClick_COPY_PUBKEY(CCtrlButton *)
- {
- int i = list_KEY_LIST.GetSelectionMark();
- if (i == -1)
- return;
-
- list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"-a");
- params.addParam(L"--export");
- params.addParam(fp);
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- params.out.Remove('\r');
- Utils_ClipboardCopy(params.out);
- }
-
- void onClick_EXPORT_PRIVATE(CCtrlButton *)
- {
- int i = list_KEY_LIST.GetSelectionMark();
- if (i == -1)
- return;
-
- ptrW p(GetFilePath(L"Choose file to export key", L"*", L"Any file", true));
- if (!p || !p[0])
- return;
-
- std::ofstream file;
- file.open(p, std::ios::trunc | std::ios::out);
- if (!file.is_open())
- return; //TODO: handle error
-
- list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"-a");
- params.addParam(L"--export-secret-keys");
- params.addParam(fp);
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- params.out.Remove('\r');
- file << params.out.c_str();
- if (file.is_open())
- file.close();
- }
-
- void onClick_CHANGE_PASSWD(CCtrlButton *)
- {
- int i = list_KEY_LIST.GetSelectionMark();
- if (i == -1)
- return;
-
- list_KEY_LIST.GetItemText(i, 0, globals.key_id_global, _countof(globals.key_id_global));
-
- // temporary code follows
- std::string old_pass, new_pass;
-
- gpg_execution_params_pass params(old_pass, new_pass);
- params.addParam(L"--edit-key");
- params.addParam(globals.key_id_global);
- params.addParam(L"passwd");
-
- HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(pxEexcute_passwd_change_thread, &params);
- if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) {
- if (params.child)
- params.child->terminate();
- if (globals.debuglog)
- globals.debuglog << "GPG execution timed out, aborted";
- this->Close();
- }
- }
-
- void onClick_GENERATE_RANDOM(CCtrlButton *)
- {
- lbl_GENERATING_KEY.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE);
- lbl_GENERATING_KEY.SetText(TranslateT("Generating new random key, please wait"));
- btn_GENERATE_KEY.Disable();
- btn_OTHER.Disable();
- btn_DELETE_KEY.Disable();
- list_KEY_LIST.Disable();
- btn_GENERATE_RANDOM.Disable();
- gpg_use_new_random_key(m_szCurrAcc);
- this->Close();
- }
-
- void onClick_GENERATE_KEY(CCtrlButton *)
- {
- CDlgKeyGen().DoModal();
- refresh_key_list();
- }
-
- void onClick_OTHER(CCtrlButton *)
- {
- ShowLoadPublicKeyDialog(0, true);
- refresh_key_list();
- }
-
- void onClick_DELETE_KEY(CCtrlButton *)
- {
- int i = list_KEY_LIST.GetSelectionMark();
- if (i == -1)
- return;
-
- list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
- {
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--fingerprint");
- params.addParam(fp);
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- int s = params.out.Find("Key fingerprint = ");
- s += mir_strlen("Key fingerprint = ");
- int s2 = params.out.Find("\n", s);
-
- CMStringW tmp = params.out.Mid(s, s2 - s);
- tmp.Remove(' ');
-
- gpg_execution_params params2;
- params2.addParam(L"--batch");
- params2.addParam(L"--delete-secret-and-public-key");
- params2.addParam(L"--fingerprint");
- params2.addParam(tmp.c_str());
-
- if (!gpg_launcher(params2))
- return;
- if (params2.result == pxNotFound)
- return;
- }
-
- if (m_szCurrAcc == nullptr) {
- g_plugin.delSetting("GPGPubKey");
- g_plugin.delSetting("KeyID");
- g_plugin.delSetting("KeyComment");
- g_plugin.delSetting("KeyMainName");
- g_plugin.delSetting("KeyMainEmail");
- g_plugin.delSetting("KeyType");
- }
- else {
- CMStringA acc_str = m_szCurrAcc;
- g_plugin.delSetting(acc_str + "_GPGPubKey");
- g_plugin.delSetting(acc_str + "_KeyMainName");
- g_plugin.delSetting(acc_str + "_KeyID");
- g_plugin.delSetting(acc_str + "_KeyComment");
- g_plugin.delSetting(acc_str + "_KeyMainEmail");
- g_plugin.delSetting(acc_str + "_KeyType");
- }
-
- list_KEY_LIST.DeleteItem(i);
- }
-
- void onClick_OK(CCtrlButton *)
- {
- int i = list_KEY_LIST.GetSelectionMark();
- if (i == -1)
- return;
-
- list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
- wchar_t name[65];
- list_KEY_LIST.GetItemText(i, 2, name, 64);
- {
- if (wcschr(name, '(')) {
- wstring str = name;
- wstring::size_type p = str.find(L"(") - 1;
- mir_wstrcpy(name, str.substr(0, p).c_str());
- }
- }
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"-a");
- params.addParam(L"--export");
- params.addParam(fp);
- if (!gpg_launcher(params))
- return;
- if (params.result == pxNotFound)
- return;
-
- params.out.Remove('\r');
-
- if (m_szCurrAcc == nullptr) {
- g_plugin.setString("GPGPubKey", params.out.c_str());
- g_plugin.setWString("KeyMainName", name);
- g_plugin.setWString("KeyID", fp);
-
- wstring keyinfo = TranslateT("Default private key ID");
- keyinfo += L": ";
- keyinfo += (fp[0]) ? fp : L"not set";
- extern HWND hwndCurKey_p;
- SetWindowText(hwndCurKey_p, keyinfo.c_str());
- }
- else {
- CMStringA acc_str = m_szCurrAcc;
- g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str());
- g_plugin.setWString(acc_str + "_KeyMainName", name);
- g_plugin.setWString(acc_str + "_KeyID", fp);
- }
-
- ptrW passwd(edit_KEY_PASSWORD.GetText());
- if (mir_wstrlen(passwd)) {
- string dbsetting = "szKey_";
- dbsetting += _T2A(fp);
- dbsetting += "_Password";
- g_plugin.setWString(dbsetting.c_str(), passwd);
- }
-
- //bAutoExchange = CheckStateStoreDB(hwndDlg, IDC_AUTO_EXCHANGE, "bAutoExchange") != 0; //TODO: check is it just typo, or doing something
- globals.gpg_valid = isGPGValid();
- globals.gpg_keyexist = isGPGKeyExist();
- DestroyWindow(m_hwnd);
- }
-
- void onChange_ACCOUNT(CCtrlCombo *pCombo)
- {
- CMStringW keyinfo = TranslateT("key ID");
- keyinfo += ": ";
-
- m_szCurrAcc = (const char *)pCombo->GetCurData();
- if (m_szCurrAcc == nullptr) {
- keyinfo += g_plugin.getMStringW("KeyID", TranslateT("not set"));
- }
- else {
- std::string acc_str = m_szCurrAcc;
- acc_str += "_KeyID";
- keyinfo += g_plugin.getMStringW(acc_str.c_str(), TranslateT("not set"));
- }
- lbl_KEY_ID.SetText(keyinfo);
- }
-
- void onChange_KEY_LIST(CCtrlListView::TEventInfo *ev)
- {
- if (ev->nmlv) {
- NMLISTVIEW *hdr = ev->nmlv;
-
- if (hdr->hdr.code == NM_CLICK) {
- btn_OK.Enable();
- btn_COPY_PUBKEY.Enable();
- btn_EXPORT_PRIVATE.Enable();
- btn_CHANGE_PASSWD.Enable();
- }
- }
- }
-};
-
-void ShowFirstRunDialog()
-{
- CDlgFirstRun().DoModal();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CDlgNewKey::CDlgNewKey(MCONTACT _hContact, wstring _new_key) :
- CDlgBase(g_plugin, IDD_NEW_KEY),
- lbl_KEY_FROM(this, IDC_KEY_FROM),
- lbl_MESSAGE(this, IDC_MESSAGE),
- btn_IMPORT(this, ID_IMPORT),
- btn_IMPORT_AND_USE(this, IDC_IMPORT_AND_USE),
- btn_IGNORE_KEY(this, IDC_IGNORE_KEY)
-{
- hContact = _hContact;
- new_key = _new_key;
- btn_IMPORT.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT);
- btn_IMPORT_AND_USE.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT_AND_USE);
- btn_IGNORE_KEY.OnClick = Callback(this, &CDlgNewKey::onClick_IGNORE_KEY);
-}
-
-bool CDlgNewKey::OnInitDialog()
-{
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow");
-
- CMStringW tmp = g_plugin.getMStringW(hContact, "GPGPubKey");
- lbl_MESSAGE.SetText(!tmp.IsEmpty() ? TranslateT("There is existing key for contact, would you like to replace it with new key?") : TranslateT("New public key was received, do you want to import it?"));
- btn_IMPORT_AND_USE.Enable(g_plugin.getByte(hContact, "GPGEncryption", 0));
- btn_IMPORT.SetText(!tmp.IsEmpty() ? TranslateT("Replace") : TranslateT("Accept"));
-
- tmp.Format(TranslateT("Received key from %s"), Clist_GetContactDisplayName(hContact));
- lbl_KEY_FROM.SetText(tmp);
- return true;
-}
-
-void CDlgNewKey::OnDestroy()
-{
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow");
-}
-
-void CDlgNewKey::onClick_IMPORT(CCtrlButton*)
-{
- ImportKey(hContact, new_key);
- this->Close();
-}
-
-void CDlgNewKey::onClick_IMPORT_AND_USE(CCtrlButton*)
-{
- ImportKey(hContact, new_key);
- g_plugin.setByte(hContact, "GPGEncryption", 1);
- setSrmmIcon(hContact);
- this->Close();
-}
-
-void CDlgNewKey::onClick_IGNORE_KEY(CCtrlButton*)
-{
- this->Close();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CDlgKeyPasswordMsgBox::CDlgKeyPasswordMsgBox(MCONTACT _hContact) :
- CDlgBase(g_plugin, IDD_KEY_PASSWD),
- lbl_KEYID(this, IDC_KEYID),
- edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD),
- chk_DEFAULT_PASSWORD(this, IDC_DEFAULT_PASSWORD),
- chk_SAVE_PASSWORD(this, IDC_SAVE_PASSWORD)
-{
- hContact = _hContact;
-}
-
-bool CDlgKeyPasswordMsgBox::OnInitDialog()
-{
- Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow");
-
- CMStringW questionstr = TranslateT("Please enter password for key with ID: ");
- questionstr += g_plugin.getMStringW(hContact, "InKeyID");
- lbl_KEYID.SetText(questionstr.c_str());
-
- chk_DEFAULT_PASSWORD.Disable();
- return true;
-}
-
-bool CDlgKeyPasswordMsgBox::OnApply()
-{
- ptrW tmp(edit_KEY_PASSWORD.GetText());
- if (tmp && tmp[0]) {
- if (chk_SAVE_PASSWORD.GetState()) {
- inkeyid = g_plugin.getStringA(hContact, "InKeyID", "");
- if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) {
- string dbsetting = "szKey_";
- dbsetting += inkeyid;
- dbsetting += "_Password";
- g_plugin.setWString(dbsetting.c_str(), tmp);
- }
- else g_plugin.setWString("szKeyPassword", tmp);
- }
- globals.wszPassword = tmp;
- }
- mir_free(inkeyid);
- return true;
-}
-
-void CDlgKeyPasswordMsgBox::OnDestroy()
-{
- if (!m_bSucceeded)
- globals._terminate = true;
-
- mir_free(inkeyid);
- Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow");
-}
+// Copyright (c) 2017-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CDlgEncryptedFileMsgBox::OnInitDialog()
+{
+ globals.bDecryptFiles = false;
+ return true;
+}
+
+CDlgEncryptedFileMsgBox::CDlgEncryptedFileMsgBox() :
+ CDlgBase(g_plugin, IDD_ENCRYPTED_FILE_MSG_BOX),
+ chk_REMEMBER(this, IDC_REMEMBER),
+ btn_IGNORE(this, IDC_IGNORE),
+ btn_DECRYPT(this, IDC_DECRYPT)
+{
+ btn_IGNORE.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_IGNORE);
+ btn_DECRYPT.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_DECRYPT);
+}
+
+void CDlgEncryptedFileMsgBox::onClick_IGNORE(CCtrlButton*)
+{
+ if (chk_REMEMBER.GetState())
+ g_plugin.bSameAction = true;
+
+ this->Close();
+}
+
+void CDlgEncryptedFileMsgBox::onClick_DECRYPT(CCtrlButton*)
+{
+ globals.bDecryptFiles = true;
+ if (chk_REMEMBER.GetState()) {
+ g_plugin.bFileTransfers = true;
+ g_plugin.bSameAction = false;
+ }
+ this->Close();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CDlgExportKeysMsgBox : public CDlgBase
+{
+ CCtrlCheck chk_PUBLIC, chk_PRIVATE, chk_ALL;
+
+public:
+ CDlgExportKeysMsgBox() :
+ CDlgBase(g_plugin, IDD_EXPORT_TYPE),
+ chk_PUBLIC(this, IDC_PUBLIC),
+ chk_PRIVATE(this, IDC_PRIVATE),
+ chk_ALL(this, IDC_ALL)
+ {
+ }
+
+ bool OnInitDialog() override
+ {
+ chk_PUBLIC.SetState(true);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (chk_PUBLIC.GetState())
+ ExportGpGKeysFunc(0);
+ else if (chk_PRIVATE.GetState())
+ ExportGpGKeysFunc(1);
+ else if (chk_ALL.GetState())
+ ExportGpGKeysFunc(2);
+ return true;
+ }
+};
+
+INT_PTR ExportGpGKeys(WPARAM, LPARAM)
+{
+ (new CDlgExportKeysMsgBox())->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CDlgChangePasswdMsgBox : public CDlgBase //always modal
+{
+ CCtrlEdit edit_NEW_PASSWD1, edit_NEW_PASSWD2, edit_OLD_PASSWD;
+
+public:
+ CDlgChangePasswdMsgBox() :
+ CDlgBase(g_plugin, IDD_CHANGE_PASSWD),
+ edit_NEW_PASSWD1(this, IDC_NEW_PASSWD1),
+ edit_NEW_PASSWD2(this, IDC_NEW_PASSWD2),
+ edit_OLD_PASSWD(this, IDC_OLD_PASSWD)
+ {
+ }
+
+ bool OnApply() override
+ {
+ //TODO: show some prgress
+ if (mir_wstrcmp(edit_NEW_PASSWD1.GetText(), edit_NEW_PASSWD2.GetText())) {
+ MessageBox(m_hwnd, TranslateT("New passwords do not match"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ std::string old_pass, new_pass;
+ new_pass = toUTF8(ptrW(edit_NEW_PASSWD1.GetText()).get());
+ old_pass = toUTF8(ptrW(edit_OLD_PASSWD.GetText()).get());
+
+ bool old_pass_match = false;
+ if (!mir_strcmp(ptrA(g_plugin.getUStringA("szKeyPassword")), old_pass.c_str()))
+ old_pass_match = true;
+
+ if (!old_pass_match) {
+ if (globals.key_id_global[0]) {
+ string dbsetting = "szKey_";
+ dbsetting += toUTF8(globals.key_id_global);
+ dbsetting += "_Password";
+ ptrA pass(g_plugin.getUStringA(dbsetting.c_str()));
+ if (!mir_strcmp(pass, old_pass.c_str()))
+ old_pass_match = true;
+ }
+ }
+
+ if (!old_pass_match)
+ if (MessageBox(m_hwnd, TranslateT("Old password does not match, you can continue, but GPG will reject wrong password.\nDo you want to continue?"), TranslateT("Error"), MB_YESNO) == IDNO)
+ return false;
+
+ gpg_execution_params_pass params(old_pass, new_pass);
+ params.addParam(L"--edit-key");
+ params.addParam(globals.key_id_global);
+ params.addParam(L"passwd");
+
+ HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(&pxEexcute_passwd_change_thread, &params);
+ if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) {
+ if (params.child)
+ params.child->terminate();
+ if (globals.debuglog)
+ globals.debuglog << "GPG execution timed out, aborted";
+ return true;
+ }
+
+ return params.result != pxNotFound;
+ }
+};
+
+void ShowChangePasswdDlg()
+{
+ CDlgChangePasswdMsgBox *d = new CDlgChangePasswdMsgBox;
+ d->DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// New key generation dialog
+
+class CDlgKeyGen : public CDlgBase
+{
+ CCtrlCombo combo_KEY_TYPE;
+ CCtrlEdit edit_KEY_LENGTH, edit_KEY_PASSWD, edit_KEY_REAL_NAME, edit_KEY_EMAIL, edit_KEY_COMMENT, edit_KEY_EXPIRE_DATE;
+ CCtrlData lbl_GENERATING_TEXT;
+
+public:
+ CDlgKeyGen() :
+ CDlgBase(g_plugin, IDD_KEY_GEN),
+ combo_KEY_TYPE(this, IDC_KEY_TYPE),
+ edit_KEY_LENGTH(this, IDC_KEY_LENGTH),
+ edit_KEY_PASSWD(this, IDC_KEY_PASSWD),
+ edit_KEY_REAL_NAME(this, IDC_KEY_REAL_NAME),
+ edit_KEY_EMAIL(this, IDC_KEY_EMAIL),
+ edit_KEY_COMMENT(this, IDC_KEY_COMMENT),
+ edit_KEY_EXPIRE_DATE(this, IDC_KEY_EXPIRE_DATE),
+ lbl_GENERATING_TEXT(this, IDC_GENERATING_TEXT)
+ {
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow");
+ SetCaption(TranslateT("Key Generation dialog"));
+
+ combo_KEY_TYPE.AddString(L"RSA");
+ combo_KEY_TYPE.AddString(L"DSA");
+ combo_KEY_TYPE.SelectString(L"RSA");
+ edit_KEY_EXPIRE_DATE.SetText(L"0");
+ edit_KEY_LENGTH.SetText(L"4096");
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ // data sanity checks
+ ptrW tmp(combo_KEY_TYPE.GetText());
+ if (mir_wstrlen(tmp) < 3) {
+ MessageBox(nullptr, TranslateT("You must set encryption algorithm first"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_LENGTH.GetText();
+ int length = _wtoi(tmp);
+ if (length < 1024 || length > 4096) {
+ MessageBox(nullptr, TranslateT("Key length must be of length from 1024 to 4096 bits"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_EXPIRE_DATE.GetText();
+ if (mir_wstrlen(tmp) != 10 && tmp[0] != '0') {
+ MessageBox(nullptr, TranslateT("Invalid date"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_REAL_NAME.GetText();
+ if (mir_wstrlen(tmp) < 4) {
+ MessageBox(nullptr, TranslateT("Name must contain at least 4 characters"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+ if (wcschr(tmp, '(') || wcschr(tmp, ')')) {
+ MessageBox(nullptr, TranslateT("Name cannot contain '(' or ')'"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ tmp = edit_KEY_EMAIL.GetText();
+ if (mir_wstrlen(tmp) < 5 || !wcschr(tmp, '@')) {
+ MessageBox(nullptr, TranslateT("Invalid Email"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ // generating key file
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+ path += L"\\new_key";
+ wfstream f(path.c_str(), std::ios::out);
+ if (!f.is_open()) {
+ MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+
+ f << "Key-Type: ";
+ char *tmp2 = mir_u2a(combo_KEY_TYPE.GetText());
+ char *subkeytype = (char *)mir_alloc(6);
+ if (strstr(tmp2, "RSA"))
+ mir_strcpy(subkeytype, "RSA");
+ else if (strstr(tmp2, "DSA")) //this is useless check for now, but it will be required if someone add another key types support
+ mir_strcpy(subkeytype, "ELG-E");
+ f << tmp2;
+ mir_free(tmp2);
+ f << "\n";
+ f << "Key-Length: ";
+ f << _wtoi(edit_KEY_LENGTH.GetText());
+ f << "\n";
+ f << "Subkey-Length: ";
+ f << _wtoi(edit_KEY_LENGTH.GetText());
+ f << "\n";
+ f << "Subkey-Type: ";
+ f << subkeytype;
+ mir_free(subkeytype);
+ f << "\n";
+ if (edit_KEY_PASSWD.GetText()[0]) {
+ f << "Passphrase: ";
+ f << toUTF8(edit_KEY_PASSWD.GetText()).c_str();
+ f << "\n";
+ }
+ f << "Name-Real: ";
+ f << toUTF8(edit_KEY_REAL_NAME.GetText()).c_str();
+ f << "\n";
+ if (edit_KEY_COMMENT.GetText()[0]) {
+ f << "Name-Comment: ";
+ f << toUTF8(edit_KEY_COMMENT.GetText()).c_str();
+ f << "\n";
+ }
+ f << "Name-Email: ";
+ f << toUTF8(edit_KEY_EMAIL.GetText()).c_str();
+ f << "\n";
+ f << "Expire-Date: ";
+ f << toUTF8(edit_KEY_EXPIRE_DATE.GetText()).c_str();
+ f << "\n";
+ f.close();
+
+ lbl_GENERATING_TEXT.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE);
+ lbl_GENERATING_TEXT.SetText(TranslateT("Generating new key, please wait..."));
+ combo_KEY_TYPE.Disable();
+ edit_KEY_LENGTH.Disable();
+ edit_KEY_PASSWD.Disable();
+ edit_KEY_REAL_NAME.Disable();
+ edit_KEY_EMAIL.Disable();
+ edit_KEY_COMMENT.Disable();
+ edit_KEY_EXPIRE_DATE.Disable();
+
+ // gpg execution
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"--gen-key");
+ params.addParam(path.c_str());
+ params.bNoOutput = true;
+ if (!gpg_launcher(params, boost::posix_time::minutes(10)))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ boost::filesystem::remove(path.c_str());
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow");
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// First run dialog
+
+class CDlgFirstRun : public CDlgBase
+{
+ void refresh_key_list()
+ {
+ list_KEY_LIST.DeleteAllItems();
+ int i = 1;
+
+ // parse gpg output
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--list-secret-keys");
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ wstring::size_type p = 0, p2 = 0, stop = 0;
+ string out(params.out);
+ while (p != string::npos) {
+ if ((p = out.find("sec ", p)) == string::npos)
+ break;
+ p += 5;
+ if (p < stop)
+ break;
+ stop = p;
+ p2 = out.find("/", p) - 1;
+ wchar_t *key_len = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()), *creation_date = nullptr, *expire_date = nullptr;
+ p2 += 2;
+ p = out.find(" ", p2);
+ std::wstring key_id = toUTF16(out.substr(p2, p - p2));
+ p += 1;
+ p2 = out.find(" ", p);
+ std::string::size_type p3 = out.find("\n", p);
+ if ((p2 != std::string::npos) && (p3 < p2)) {
+ p2 = p3;
+ creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p - 1)).c_str());
+ }
+ else {
+ creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ p2 = out.find("[", p2);
+ p2 = out.find("expires:", p2);
+ p2 += mir_strlen("expires:");
+ if (p2 != std::string::npos) {
+ p2++;
+ p = p2;
+ p2 = out.find("]", p);
+ expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str());
+ //check expiration
+ bool expired = false;
+ {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ wchar_t buf[5];
+ wcsncpy_s(buf, expire_date, _TRUNCATE);
+ int year = _wtoi(buf);
+ if (year < now.date().year())
+ expired = true;
+ else if (year == now.date().year()) {
+ wcsncpy_s(buf, (expire_date + 5), _TRUNCATE);
+ int month = _wtoi(buf);
+ if (month < now.date().month())
+ expired = true;
+ else if (month == now.date().month()) {
+ wcsncpy_s(buf, (expire_date + 8), _TRUNCATE);
+ unsigned day = _wtoi(buf);
+ if (day <= now.date().day_number())
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ mir_free(key_len);
+ mir_free(creation_date);
+ mir_free(expire_date);
+ //mimic normal behaviour
+ p = out.find("uid ", p);
+ p2 = out.find_first_not_of(" ", p + 5);
+ p = out.find("<", p2);
+ p++;
+ //p2 = out.find(">", p);
+ //
+ continue; //does not add to key list
+ }
+ }
+ }
+ int row = list_KEY_LIST.AddItem(L"", 0);
+ list_KEY_LIST.SetItemText(row, 3, creation_date);
+ mir_free(creation_date);
+ if (expire_date) {
+ list_KEY_LIST.SetItemText(row, 4, expire_date);
+ mir_free(expire_date);
+ }
+ list_KEY_LIST.SetItemText(row, 5, key_len);
+ mir_free(key_len);
+ list_KEY_LIST.SetItemText(row, 0, (wchar_t *)key_id.c_str());
+ p = out.find("uid ", p);
+ p2 = out.find_first_not_of(" ", p + 5);
+ p = out.find("<", p2);
+
+ wstring tmp = toUTF16(out.substr(p2, p - p2));
+ list_KEY_LIST.SetItemText(row, 2, (wchar_t *)tmp.c_str());
+
+ p++;
+ p2 = out.find(">", p);
+
+ tmp = toUTF16(out.substr(p, p2 - p));
+ list_KEY_LIST.SetItemText(row, 1, (wchar_t *)tmp.c_str());
+
+ // get accounts
+ std::wstring accs;
+ for (auto &pa : Accounts()) {
+ std::string setting = pa->szModuleName;
+ setting += "_KeyID";
+ ptrW str(g_plugin.getWStringA(setting.c_str(), L""));
+ if (key_id == str.get()) {
+ if (!accs.empty())
+ accs += L",";
+ accs += pa->tszAccountName;
+ }
+ }
+ list_KEY_LIST.SetItemText(row, 6, accs.c_str());
+ }
+ i++;
+ list_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE);
+ list_KEY_LIST.SetColumnWidth(6, LVSCW_AUTOSIZE);
+ }
+
+ CCtrlListView list_KEY_LIST;
+ CCtrlButton btn_COPY_PUBKEY, btn_EXPORT_PRIVATE, btn_CHANGE_PASSWD, btn_GENERATE_RANDOM, btn_GENERATE_KEY, btn_OTHER, btn_DELETE_KEY, btn_OK;
+ CCtrlEdit edit_KEY_PASSWORD;
+ CCtrlCombo combo_ACCOUNT;
+ CCtrlData lbl_KEY_ID, lbl_GENERATING_KEY;
+ wchar_t fp[16];
+ const char *m_szCurrAcc = nullptr;
+
+public:
+ CDlgFirstRun() :
+ CDlgBase(g_plugin, IDD_FIRST_RUN),
+ list_KEY_LIST(this, IDC_KEY_LIST),
+ btn_COPY_PUBKEY(this, IDC_COPY_PUBKEY),
+ btn_EXPORT_PRIVATE(this, IDC_EXPORT_PRIVATE),
+ btn_CHANGE_PASSWD(this, IDC_CHANGE_PASSWD),
+ btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM),
+ btn_GENERATE_KEY(this, IDC_GENERATE_KEY),
+ btn_OTHER(this, IDC_OTHER),
+ btn_DELETE_KEY(this, IDC_DELETE_KEY),
+ btn_OK(this, ID_OK),
+ edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD),
+ combo_ACCOUNT(this, IDC_ACCOUNT),
+ lbl_KEY_ID(this, IDC_KEY_ID),
+ lbl_GENERATING_KEY(this, IDC_GENERATING_KEY)
+ {
+ fp[0] = 0;
+
+ btn_COPY_PUBKEY.OnClick = Callback(this, &CDlgFirstRun::onClick_COPY_PUBKEY);
+ btn_EXPORT_PRIVATE.OnClick = Callback(this, &CDlgFirstRun::onClick_EXPORT_PRIVATE);
+ btn_CHANGE_PASSWD.OnClick = Callback(this, &CDlgFirstRun::onClick_CHANGE_PASSWD);
+ btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_RANDOM);
+ btn_GENERATE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_KEY);
+ btn_OTHER.OnClick = Callback(this, &CDlgFirstRun::onClick_OTHER);
+ btn_DELETE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_DELETE_KEY);
+ btn_OK.OnClick = Callback(this, &CDlgFirstRun::onClick_OK);
+
+ combo_ACCOUNT.OnChange = Callback(this, &CDlgFirstRun::onChange_ACCOUNT);
+
+ list_KEY_LIST.OnClick = Callback(this, &CDlgFirstRun::onChange_KEY_LIST);
+ }
+
+ bool OnInitDialog() override
+ {
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow");
+ SetCaption(TranslateT("Bind own keys to accounts"));
+ btn_COPY_PUBKEY.Disable();
+ btn_EXPORT_PRIVATE.Disable();
+ btn_CHANGE_PASSWD.Disable();
+
+ list_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50);
+ list_KEY_LIST.AddColumn(1, TranslateT("Email"), 30);
+ list_KEY_LIST.AddColumn(2, TranslateT("Name"), 250);
+ list_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30);
+ list_KEY_LIST.AddColumn(4, TranslateT("Expire date"), 30);
+ list_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30);
+ list_KEY_LIST.AddColumn(6, TranslateT("Accounts"), 30);
+ list_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW);
+
+ refresh_key_list();
+
+ combo_ACCOUNT.AddString(TranslateT("Default"));
+
+ for (auto &pa : Accounts()) {
+ if (StriStr(pa->szModuleName, "metacontacts"))
+ continue;
+ if (StriStr(pa->szModuleName, "weather"))
+ continue;
+
+ combo_ACCOUNT.AddString(pa->tszAccountName, (LPARAM)pa->szModuleName);
+ }
+ combo_ACCOUNT.SetCurSel(0);
+
+ lbl_KEY_ID.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get()));
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow");
+ }
+
+ void onClick_COPY_PUBKEY(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ params.out.Remove('\r');
+ Utils_ClipboardCopy(params.out);
+ }
+
+ void onClick_EXPORT_PRIVATE(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ ptrW p(GetFilePath(L"Choose file to export key", L"*", L"Any file", true));
+ if (!p || !p[0])
+ return;
+
+ std::ofstream file;
+ file.open(p, std::ios::trunc | std::ios::out);
+ if (!file.is_open())
+ return; //TODO: handle error
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export-secret-keys");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ params.out.Remove('\r');
+ file << params.out.c_str();
+ if (file.is_open())
+ file.close();
+ }
+
+ void onClick_CHANGE_PASSWD(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, globals.key_id_global, _countof(globals.key_id_global));
+
+ // temporary code follows
+ std::string old_pass, new_pass;
+
+ gpg_execution_params_pass params(old_pass, new_pass);
+ params.addParam(L"--edit-key");
+ params.addParam(globals.key_id_global);
+ params.addParam(L"passwd");
+
+ HANDLE hThread = mir_forkThread<gpg_execution_params_pass>(pxEexcute_passwd_change_thread, &params);
+ if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) {
+ if (params.child)
+ params.child->terminate();
+ if (globals.debuglog)
+ globals.debuglog << "GPG execution timed out, aborted";
+ this->Close();
+ }
+ }
+
+ void onClick_GENERATE_RANDOM(CCtrlButton *)
+ {
+ lbl_GENERATING_KEY.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE);
+ lbl_GENERATING_KEY.SetText(TranslateT("Generating new random key, please wait"));
+ btn_GENERATE_KEY.Disable();
+ btn_OTHER.Disable();
+ btn_DELETE_KEY.Disable();
+ list_KEY_LIST.Disable();
+ btn_GENERATE_RANDOM.Disable();
+ gpg_use_new_random_key(m_szCurrAcc);
+ this->Close();
+ }
+
+ void onClick_GENERATE_KEY(CCtrlButton *)
+ {
+ CDlgKeyGen().DoModal();
+ refresh_key_list();
+ }
+
+ void onClick_OTHER(CCtrlButton *)
+ {
+ ShowLoadPublicKeyDialog(0, true);
+ refresh_key_list();
+ }
+
+ void onClick_DELETE_KEY(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+ {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--fingerprint");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ int s = params.out.Find("Key fingerprint = ");
+ s += mir_strlen("Key fingerprint = ");
+ int s2 = params.out.Find("\n", s);
+
+ CMStringW tmp = params.out.Mid(s, s2 - s);
+ tmp.Remove(' ');
+
+ gpg_execution_params params2;
+ params2.addParam(L"--batch");
+ params2.addParam(L"--delete-secret-and-public-key");
+ params2.addParam(L"--fingerprint");
+ params2.addParam(tmp.c_str());
+
+ if (!gpg_launcher(params2))
+ return;
+ if (params2.result == pxNotFound)
+ return;
+ }
+
+ if (m_szCurrAcc == nullptr) {
+ g_plugin.delSetting("GPGPubKey");
+ g_plugin.delSetting("KeyID");
+ g_plugin.delSetting("KeyComment");
+ g_plugin.delSetting("KeyMainName");
+ g_plugin.delSetting("KeyMainEmail");
+ g_plugin.delSetting("KeyType");
+ }
+ else {
+ CMStringA acc_str = m_szCurrAcc;
+ g_plugin.delSetting(acc_str + "_GPGPubKey");
+ g_plugin.delSetting(acc_str + "_KeyMainName");
+ g_plugin.delSetting(acc_str + "_KeyID");
+ g_plugin.delSetting(acc_str + "_KeyComment");
+ g_plugin.delSetting(acc_str + "_KeyMainEmail");
+ g_plugin.delSetting(acc_str + "_KeyType");
+ }
+
+ list_KEY_LIST.DeleteItem(i);
+ }
+
+ void onClick_OK(CCtrlButton *)
+ {
+ int i = list_KEY_LIST.GetSelectionMark();
+ if (i == -1)
+ return;
+
+ list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp));
+ wchar_t name[65];
+ list_KEY_LIST.GetItemText(i, 2, name, 64);
+ {
+ if (wcschr(name, '(')) {
+ wstring str = name;
+ wstring::size_type p = str.find(L"(") - 1;
+ mir_wstrcpy(name, str.substr(0, p).c_str());
+ }
+ }
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(fp);
+ if (!gpg_launcher(params))
+ return;
+ if (params.result == pxNotFound)
+ return;
+
+ params.out.Remove('\r');
+
+ if (m_szCurrAcc == nullptr) {
+ g_plugin.setString("GPGPubKey", params.out.c_str());
+ g_plugin.setWString("KeyMainName", name);
+ g_plugin.setWString("KeyID", fp);
+
+ wstring keyinfo = TranslateT("Default private key ID");
+ keyinfo += L": ";
+ keyinfo += (fp[0]) ? fp : L"not set";
+ extern HWND hwndCurKey_p;
+ SetWindowText(hwndCurKey_p, keyinfo.c_str());
+ }
+ else {
+ CMStringA acc_str = m_szCurrAcc;
+ g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str());
+ g_plugin.setWString(acc_str + "_KeyMainName", name);
+ g_plugin.setWString(acc_str + "_KeyID", fp);
+ }
+
+ ptrW passwd(edit_KEY_PASSWORD.GetText());
+ if (mir_wstrlen(passwd)) {
+ string dbsetting = "szKey_";
+ dbsetting += _T2A(fp);
+ dbsetting += "_Password";
+ g_plugin.setWString(dbsetting.c_str(), passwd);
+ }
+
+ //bAutoExchange = CheckStateStoreDB(hwndDlg, IDC_AUTO_EXCHANGE, "bAutoExchange") != 0; //TODO: check is it just typo, or doing something
+ globals.gpg_valid = isGPGValid();
+ globals.gpg_keyexist = isGPGKeyExist();
+ DestroyWindow(m_hwnd);
+ }
+
+ void onChange_ACCOUNT(CCtrlCombo *pCombo)
+ {
+ CMStringW keyinfo = TranslateT("key ID");
+ keyinfo += ": ";
+
+ m_szCurrAcc = (const char *)pCombo->GetCurData();
+ if (m_szCurrAcc == nullptr) {
+ keyinfo += g_plugin.getMStringW("KeyID", TranslateT("not set"));
+ }
+ else {
+ std::string acc_str = m_szCurrAcc;
+ acc_str += "_KeyID";
+ keyinfo += g_plugin.getMStringW(acc_str.c_str(), TranslateT("not set"));
+ }
+ lbl_KEY_ID.SetText(keyinfo);
+ }
+
+ void onChange_KEY_LIST(CCtrlListView::TEventInfo *ev)
+ {
+ if (ev->nmlv) {
+ NMLISTVIEW *hdr = ev->nmlv;
+
+ if (hdr->hdr.code == NM_CLICK) {
+ btn_OK.Enable();
+ btn_COPY_PUBKEY.Enable();
+ btn_EXPORT_PRIVATE.Enable();
+ btn_CHANGE_PASSWD.Enable();
+ }
+ }
+ }
+};
+
+void ShowFirstRunDialog()
+{
+ CDlgFirstRun().DoModal();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CDlgNewKey::CDlgNewKey(MCONTACT _hContact, wstring _new_key) :
+ CDlgBase(g_plugin, IDD_NEW_KEY),
+ lbl_KEY_FROM(this, IDC_KEY_FROM),
+ lbl_MESSAGE(this, IDC_MESSAGE),
+ btn_IMPORT(this, ID_IMPORT),
+ btn_IMPORT_AND_USE(this, IDC_IMPORT_AND_USE),
+ btn_IGNORE_KEY(this, IDC_IGNORE_KEY)
+{
+ hContact = _hContact;
+ new_key = _new_key;
+ btn_IMPORT.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT);
+ btn_IMPORT_AND_USE.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT_AND_USE);
+ btn_IGNORE_KEY.OnClick = Callback(this, &CDlgNewKey::onClick_IGNORE_KEY);
+}
+
+bool CDlgNewKey::OnInitDialog()
+{
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow");
+
+ CMStringW tmp = g_plugin.getMStringW(hContact, "GPGPubKey");
+ lbl_MESSAGE.SetText(!tmp.IsEmpty() ? TranslateT("There is existing key for contact, would you like to replace it with new key?") : TranslateT("New public key was received, do you want to import it?"));
+ btn_IMPORT_AND_USE.Enable(g_plugin.getByte(hContact, "GPGEncryption", 0));
+ btn_IMPORT.SetText(!tmp.IsEmpty() ? TranslateT("Replace") : TranslateT("Accept"));
+
+ tmp.Format(TranslateT("Received key from %s"), Clist_GetContactDisplayName(hContact));
+ lbl_KEY_FROM.SetText(tmp);
+ return true;
+}
+
+void CDlgNewKey::OnDestroy()
+{
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow");
+}
+
+void CDlgNewKey::onClick_IMPORT(CCtrlButton*)
+{
+ ImportKey(hContact, new_key);
+ this->Close();
+}
+
+void CDlgNewKey::onClick_IMPORT_AND_USE(CCtrlButton*)
+{
+ ImportKey(hContact, new_key);
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ setSrmmIcon(hContact);
+ this->Close();
+}
+
+void CDlgNewKey::onClick_IGNORE_KEY(CCtrlButton*)
+{
+ this->Close();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CDlgKeyPasswordMsgBox::CDlgKeyPasswordMsgBox(MCONTACT _hContact) :
+ CDlgBase(g_plugin, IDD_KEY_PASSWD),
+ lbl_KEYID(this, IDC_KEYID),
+ edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD),
+ chk_DEFAULT_PASSWORD(this, IDC_DEFAULT_PASSWORD),
+ chk_SAVE_PASSWORD(this, IDC_SAVE_PASSWORD)
+{
+ hContact = _hContact;
+}
+
+bool CDlgKeyPasswordMsgBox::OnInitDialog()
+{
+ Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow");
+
+ CMStringW questionstr = TranslateT("Please enter password for key with ID: ");
+ questionstr += g_plugin.getMStringW(hContact, "InKeyID");
+ lbl_KEYID.SetText(questionstr.c_str());
+
+ chk_DEFAULT_PASSWORD.Disable();
+ return true;
+}
+
+bool CDlgKeyPasswordMsgBox::OnApply()
+{
+ ptrW tmp(edit_KEY_PASSWORD.GetText());
+ if (tmp && tmp[0]) {
+ if (chk_SAVE_PASSWORD.GetState()) {
+ inkeyid = g_plugin.getStringA(hContact, "InKeyID", "");
+ if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) {
+ string dbsetting = "szKey_";
+ dbsetting += inkeyid;
+ dbsetting += "_Password";
+ g_plugin.setWString(dbsetting.c_str(), tmp);
+ }
+ else g_plugin.setWString("szKeyPassword", tmp);
+ }
+ globals.wszPassword = tmp;
+ }
+ mir_free(inkeyid);
+ return true;
+}
+
+void CDlgKeyPasswordMsgBox::OnDestroy()
+{
+ if (!m_bSucceeded)
+ globals._terminate = true;
+
+ mir_free(inkeyid);
+ Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow");
+}
diff --git a/plugins/New_GPG/src/ui.h b/plugins/New_GPG/src/ui.h
index 1e30441b83..1e5c936716 100644
--- a/plugins/New_GPG/src/ui.h
+++ b/plugins/New_GPG/src/ui.h
@@ -1,69 +1,69 @@
-// Copyright © 2017-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef UI_H
-#define UI_H
-
-void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal);
-void ShowFirstRunDialog();
-
-class CDlgEncryptedFileMsgBox : public CDlgBase
-{
- CCtrlCheck chk_REMEMBER;
- CCtrlButton btn_IGNORE, btn_DECRYPT;
-
-public:
- CDlgEncryptedFileMsgBox();
- bool OnInitDialog() override;
-
- void onClick_IGNORE(CCtrlButton*);
- void onClick_DECRYPT(CCtrlButton*);
-};
-
-class CDlgNewKey : public CDlgBase
-{
- wstring new_key;
- MCONTACT hContact;
- CCtrlData lbl_KEY_FROM, lbl_MESSAGE;
- CCtrlButton btn_IMPORT, btn_IMPORT_AND_USE, btn_IGNORE_KEY;
-
-public:
- CDlgNewKey(MCONTACT hContact, wstring new_key);
- bool OnInitDialog() override;
- void OnDestroy() override;
-
- void onClick_IMPORT(CCtrlButton*);
- void onClick_IMPORT_AND_USE(CCtrlButton*);
- void onClick_IGNORE_KEY(CCtrlButton*);
-};
-
-class CDlgKeyPasswordMsgBox : public CDlgBase //always modal
-{
- char *inkeyid = nullptr;
- MCONTACT hContact;
- CCtrlData lbl_KEYID;
- CCtrlEdit edit_KEY_PASSWORD;
- CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD;
-
-public:
- CDlgKeyPasswordMsgBox(MCONTACT _hContact);
-
- bool OnInitDialog() override;
- bool OnApply() override;
- void OnDestroy() override;
-};
-
+// Copyright © 2017-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef UI_H
+#define UI_H
+
+void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal);
+void ShowFirstRunDialog();
+
+class CDlgEncryptedFileMsgBox : public CDlgBase
+{
+ CCtrlCheck chk_REMEMBER;
+ CCtrlButton btn_IGNORE, btn_DECRYPT;
+
+public:
+ CDlgEncryptedFileMsgBox();
+ bool OnInitDialog() override;
+
+ void onClick_IGNORE(CCtrlButton*);
+ void onClick_DECRYPT(CCtrlButton*);
+};
+
+class CDlgNewKey : public CDlgBase
+{
+ wstring new_key;
+ MCONTACT hContact;
+ CCtrlData lbl_KEY_FROM, lbl_MESSAGE;
+ CCtrlButton btn_IMPORT, btn_IMPORT_AND_USE, btn_IGNORE_KEY;
+
+public:
+ CDlgNewKey(MCONTACT hContact, wstring new_key);
+ bool OnInitDialog() override;
+ void OnDestroy() override;
+
+ void onClick_IMPORT(CCtrlButton*);
+ void onClick_IMPORT_AND_USE(CCtrlButton*);
+ void onClick_IGNORE_KEY(CCtrlButton*);
+};
+
+class CDlgKeyPasswordMsgBox : public CDlgBase //always modal
+{
+ char *inkeyid = nullptr;
+ MCONTACT hContact;
+ CCtrlData lbl_KEYID;
+ CCtrlEdit edit_KEY_PASSWORD;
+ CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD;
+
+public:
+ CDlgKeyPasswordMsgBox(MCONTACT _hContact);
+
+ bool OnInitDialog() override;
+ bool OnApply() override;
+ void OnDestroy() override;
+};
+
#endif // UI_H \ No newline at end of file
diff --git a/plugins/New_GPG/src/utilities.cpp b/plugins/New_GPG/src/utilities.cpp
index f6466f89b6..cefc7ade47 100644
--- a/plugins/New_GPG/src/utilities.cpp
+++ b/plugins/New_GPG/src/utilities.cpp
@@ -1,1428 +1,1428 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with 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 "utf8.h"
-
-void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc)
-{
- wchar_t str[MAX_PATH + 2] = {};
- OPENFILENAME ofn = {};
- wchar_t filter[512], *pfilter;
- ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
- ofn.Flags = OFN_EXPLORER;
- ofn.lpstrTitle = TranslateW(WindowTittle);
- wcsncpy(filter, TranslateW(szExtDesc), _countof(filter) - 1);
- pfilter = filter + mir_wstrlen(filter) + 1;
- mir_wstrcpy(pfilter, szExt);
- pfilter[mir_wstrlen(pfilter) + 1] = '\0';
- pfilter[mir_wstrlen(pfilter) + 2] = '\0';
- ofn.lpstrFilter = filter;
- wcsncpy(str, g_plugin.getMStringW(szSetting), _countof(str) - 1);
- if (mir_wstrlen(str) < 2)
- str[0] = '\0';
- ofn.lpstrFile = str;
- ofn.nMaxFile = _MAX_PATH;
- ofn.nMaxFileTitle = MAX_PATH;
- if (GetOpenFileName(&ofn))
- g_plugin.setWString(szSetting, str);
-}
-
-wchar_t* GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file)
-{
- wchar_t str[MAX_PATH + 1];
- OPENFILENAME ofn = {};
- wchar_t filter[512], *pfilter;
- ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
- ofn.Flags = OFN_EXPLORER;
- ofn.lpstrTitle = TranslateW(WindowTittle);
- mir_wstrcpy(filter, TranslateW(szExtDesc));
- pfilter = filter + mir_wstrlen(filter) + 1;
- mir_wstrcpy(pfilter, szExt);
- pfilter[mir_wstrlen(pfilter) + 1] = '\0';
- pfilter[mir_wstrlen(pfilter) + 2] = '\0';
- ofn.lpstrFilter = filter;
- mir_wstrcpy(str, L"");
- if (mir_wstrlen(str) < 2)
- str[0] = '\0';
- ofn.lpstrFile = str;
- ofn.nMaxFile = _MAX_PATH;
- ofn.nMaxFileTitle = MAX_PATH;
- if (!save_file) {
- if (!GetOpenFileName(&ofn))
- return nullptr;
- }
- else {
- if (!GetSaveFileName(&ofn))
- return nullptr;
- }
- return mir_wstrdup(str);
-}
-
-void GetFolderPath(wchar_t *WindowTittle)
-{
- BROWSEINFO pbi = {};
- pbi.lpszTitle = WindowTittle;
- pbi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE;
- LPITEMIDLIST pidl = SHBrowseForFolder(&pbi);
- if (pidl != nullptr) {
- wchar_t path[MAX_PATH];
- if (SHGetPathFromIDList(pidl, path)) {
- g_plugin.setWString("szHomePath", path);
- }
- IMalloc * imalloc = nullptr;
- if (SUCCEEDED(SHGetMalloc(&imalloc))) {
- imalloc->Free(pidl);
- imalloc->Release();
- }
- }
-}
-
-INT_PTR LoadKey(WPARAM w, LPARAM)
-{
- ShowLoadPublicKeyDialog((MCONTACT)w, false);
- return 0;
-}
-
-INT_PTR SendKey(WPARAM w, LPARAM)
-{
- MCONTACT hContact = db_mc_tryMeta(w);
- std::string key_id_str;
-
- LPSTR proto = Proto_GetBaseAccountName(hContact);
- PROTOACCOUNT *acc = Proto_GetAccount(proto);
- std::string acc_str;
- if (acc) {
- acc_str = acc->szModuleName;
- key_id_str = acc_str;
- key_id_str += "_KeyID";
- acc_str += "_GPGPubKey";
- }
-
- CMStringA szMessage = g_plugin.getMStringA(acc_str.empty() ? "GPGPubKey" : acc_str.c_str());
- if (szMessage.IsEmpty())
- szMessage = g_plugin.getMStringA("GPGPubKey"); //try to get default key as fallback in any way
-
- if (!szMessage.IsEmpty()) {
- uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
- g_plugin.setByte(hContact, "GPGEncryption", 0);
- ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)szMessage.c_str());
- std::string msg = "Public key ";
- CMStringA keyid = g_plugin.getMStringA(key_id_str.c_str());
- if (keyid.IsEmpty())
- keyid = g_plugin.getMStringA("KeyID");
- msg += keyid;
- msg += " sent";
-
- HistoryLog(hContact, msg.c_str(), DBEF_SENT);
- g_plugin.setByte(hContact, "GPGEncryption", enc);
- }
-
- return 0;
-}
-
-INT_PTR ToggleEncryption(WPARAM w, LPARAM)
-{
- MCONTACT hContact = (MCONTACT)w;
- uint8_t enc;
- if (db_mc_isMeta(hContact)) {
- enc = g_plugin.getByte(metaGetMostOnline(hContact), "GPGEncryption");
- if (MessageBox(nullptr, TranslateT("Do you want to toggle encryption for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
- int count = db_mc_getSubCount(hContact);
- for (int i = 0; i < count; i++) {
- MCONTACT hcnt = db_mc_getSub(hContact, i);
- if (hcnt)
- g_plugin.getByte(hcnt, "GPGEncryption", enc ? 0 : 1);
- }
- g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1);
- }
- }
- else {
- enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
- g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1);
- }
- setSrmmIcon(hContact);
- return 0;
-}
-
-int OnPreBuildContactMenu(WPARAM w, LPARAM)
-{
- MCONTACT hContact = db_mc_tryMeta(w);
- {
- CMenuItem mi2(&g_plugin);
- LPSTR proto = Proto_GetBaseAccountName(hContact);
- PROTOACCOUNT *acc = Proto_GetAccount(proto);
- std::string setting;
- if (acc) {
- setting = acc->szModuleName;
- setting += "_KeyID";
- }
-
- CMStringA keyid = g_plugin.getMStringA(setting.c_str());
- if (keyid.IsEmpty())
- keyid = g_plugin.getMStringA("KeyID");
-
- wchar_t buf[128] = { 0 };
- mir_snwprintf(buf, L"%s: %S", TranslateT("Send public key"), keyid.c_str());
- Menu_ModifyItem(g_plugin.hSendKey, buf);
- }
-
- int flags;
- CMStringA tmp = g_plugin.getMStringW(hContact, "GPGPubKey");
- if (tmp.IsEmpty()) {
- g_plugin.delSetting(hContact, "GPGEncryption");
- flags = CMIF_GRAYED;
- }
- else flags = 0;
-
- if (g_plugin.getByte(hContact, "GPGEncryption"))
- Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn off GPG encryption"), g_plugin.getIconHandle(IDI_SECURED), flags);
- else
- Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn on GPG encryption"), g_plugin.getIconHandle(IDI_UNSECURED), flags);
- return 0;
-}
-
-list<wstring> transfers;
-
-int onProtoAck(WPARAM, LPARAM l)
-{
- ACKDATA *ack = (ACKDATA*)l;
- if (ack->type == ACKTYPE_FILE) {
- switch (ack->result) {
- case ACKRESULT_DENIED: case ACKRESULT_FAILED:
- break;
- case ACKRESULT_SUCCESS:
- if (ack->hProcess) {
- PROTOFILETRANSFERSTATUS *f = (PROTOFILETRANSFERSTATUS*)ack->hProcess;
- if (f == nullptr)
- break;
-
- if ((f->flags & PFTS_SENDING) != PFTS_SENDING) {
- wchar_t *filename = nullptr;
- if (f->flags & PFTS_UNICODE) {
- if (f->szCurrentFile.w && f->szCurrentFile.w[0])
- filename = mir_wstrdup(f->szCurrentFile.w);
- if (!filename)
- return 0;
- }
- else {
- if (f->szCurrentFile.a && f->szCurrentFile.a[0])
- filename = mir_utf8decodeW(f->szCurrentFile.a);
- if (!filename)
- return 0;
- }
- if (wcsstr(filename, L".gpg")) { //decrypt it
- //process encrypted file
- if (!g_plugin.bFileTransfers && !g_plugin.bSameAction) {
- void ShowEncryptedFileMsgBox();
- ShowEncryptedFileMsgBox();
- }
- if (!g_plugin.bFileTransfers && g_plugin.bSameAction)
- return 0;
- if (!globals.bDecryptFiles)
- return 0;
- HistoryLog(ack->hContact, "Received encrypted file, trying to decrypt");
- if (!boost::filesystem::exists(f->szCurrentFile.w))
- return 0;
-
- gpg_execution_params params;
- params.addParam(L"-o");
- wstring file = filename;
- wstring::size_type p1 = file.rfind(L".gpg");
- file.erase(p1, mir_wstrlen(L".gpg"));
- if (boost::filesystem::exists(file)) {
- if (MessageBox(nullptr, TranslateT("Target file exists, do you want to replace it?"), TranslateT("Warning"), MB_YESNO) == IDNO)
- return 0;
- }
- params.addParam(file);
- boost::filesystem::remove(file);
- {
- // password
- CMStringW pass;
- CMStringA keyid = g_plugin.getMStringA(ack->hContact, "KeyID");
- if (!keyid.IsEmpty()) {
- string dbsetting = "szKey_";
- dbsetting += keyid;
- dbsetting += "_Password";
- pass = g_plugin.getMStringW(dbsetting.c_str());
- if (!pass.IsEmpty() && globals.debuglog)
- globals.debuglog << "info: found password in database for key ID: " + string(keyid.c_str()) + ", trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
- }
- else {
- pass = g_plugin.getMStringW("szKeyPassword");
- if (!pass.IsEmpty() && globals.debuglog)
- globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
- }
- if (!pass.IsEmpty()) {
- params.addParam(L"--passphrase");
- params.addParam(pass.c_str());
- }
- else if (!globals.wszPassword.IsEmpty()) {
- if (globals.debuglog)
- globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
- params.addParam(L"--passphrase");
- params.addParam(globals.wszPassword.c_str());
- }
- else if (globals.debuglog)
- globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " without password";
- }
- params.addParam(L"-d");
- params.addParam(filename);
- if (!gpg_launcher(params, boost::posix_time::minutes(15)))
- return 0;
-
- string out(params.out);
- while (out.find("public key decryption failed: bad passphrase") != string::npos) {
- if (globals.debuglog)
- globals.debuglog << "info: failed to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " password needed, trying to get one";
- if (globals._terminate)
- break;
- { //save inkey id
- string::size_type s = out.find(" encrypted with ");
- s = out.find(" ID ", s);
- s += mir_strlen(" ID ");
- string::size_type s2 = out.find(",", s);
- if (db_mc_isMeta(ack->hContact))
- g_plugin.setString(metaGetMostOnline(ack->hContact), "InKeyID", out.substr(s, s2 - s).c_str());
- else
- g_plugin.setString(ack->hContact, "InKeyID", out.substr(s, s2 - s).c_str());
- }
-
- CDlgKeyPasswordMsgBox(ack->hContact).DoModal();
-
- if (!globals.wszPassword.IsEmpty()) {
- if (globals.debuglog)
- globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact));
-
- params.addParam(L"--passphrase");
- params.addParam(globals.wszPassword.c_str());
- }
-
- if (!gpg_launcher(params, boost::posix_time::seconds(15)))
- return 0;
- if (params.result == pxNotFound)
- return 0;
- }
- if (params.result == pxSuccess)
- boost::filesystem::remove(filename);
- }
- mir_free(filename);
- }
- }
- break;
- }
- }
- else if (ack->type == ACKTYPE_MESSAGE) {
- extern std::list<HANDLE> sent_msgs;
- if (!sent_msgs.empty()) {
- if (ack->result == ACKRESULT_FAILED) {
- std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess);
- if (it != sent_msgs.end())
- HistoryLog(ack->hContact, "Failed to send encrypted message");
- }
- else if (ack->result == ACKRESULT_SUCCESS) {
- std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess);
- if (it != sent_msgs.end())
- sent_msgs.erase(it);
- }
- }
- }
- return 0;
-}
-
-std::wstring encrypt_file(MCONTACT hContact, wchar_t *filename)
-{
- MCONTACT hcnt = db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact;
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--tes");
- params.addParam(L"-r");
-
- CMStringW keyid = g_plugin.getMStringW(hcnt, "KeyID");
- wchar_t *name = wcsrchr(filename, '\\');
- if (!name)
- name = filename;
- else
- name++;
- wchar_t *file_out = new wchar_t[mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1];
- mir_snwprintf(file_out, mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1, L"%s.gpg", name);
- params.addParam(keyid.c_str());
-
- if (g_plugin.getByte(hcnt, "bAlwaysTrust")) {
- params.addParam(L"--trust-model");
- params.addParam(L"always");
- }
-
- params.addParam(L"-o");
- wchar_t *temp = _tgetenv(L"TEMP");
- params.addParam(wstring(temp) + L"\\" + file_out);
- wstring path_out = temp;
- path_out += L"\\";
- path_out += file_out;
- boost::filesystem::remove(path_out);
- params.addParam(L"-e");
- params.addParam(filename);
- delete[] file_out;
-
- if (!gpg_launcher(params, boost::posix_time::minutes(3)))
- return nullptr;
-
- if (params.out.Find("There is no assurance this key belongs to the named user") != -1) {
- if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO))
- return nullptr;
-
- g_plugin.setByte(hcnt, "bAlwaysTrust", 1);
-
- params.addParam(L"--trust-model");
- params.addParam(L"always");
- if (!gpg_launcher(params, boost::posix_time::minutes(3)))
- return nullptr;
- }
- return path_out;
-}
-
-//from secureim partially
-INT_PTR onSendFile(WPARAM w, LPARAM l)
-{
- CCSDATA *ccs = (CCSDATA*)l;
- if (!g_plugin.bFileTransfers)
- return Proto_ChainSend(w, ccs);
-
- if (isContactSecured(ccs->hContact)) {
- char *proto = Proto_GetBaseAccountName(ccs->hContact);
- bool cap_found = false, supported_proto = false;
- ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", ""));
- if (jid[0]) {
- for (auto p : globals.Accounts) {
- ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
- if (caps) {
- supported_proto = true;
- string str;
- for (int i = 0;; i++) {
- str.push_back(caps[i]);
- if (caps[i] == '\0')
- if (caps[i + 1] == '\0')
- break;
- }
-
- if (str.find("GPG_Encrypted_FileTransfers:0") != string::npos)
- cap_found = true;
- }
- }
- }
-
- if (supported_proto && !cap_found) {
- if (MessageBox(nullptr, TranslateT("Capability to decrypt file not found on other side.\nRecipient may be unable to decrypt file(s).\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO)
- return Proto_ChainSend(w, ccs);
- }
- if (!supported_proto) {
- if (MessageBox(nullptr, TranslateT("Unable to check encryption support on other side.\nRecipient may be unable to decrypt file(s).\nCurrently capability check supported only for ICQ and Jabber protocols.\nIt will work for any other proto if Miranda with New_GPG is used on other side.\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO)
- return Proto_ChainSend(w, ccs);
- }
- HistoryLog(ccs->hContact, TranslateU("encrypting file for transfer"), DBEF_SENT);
- if (StriStr(ccs->szProtoService, "/sendfilew")) {
- wchar_t **file = (wchar_t **)ccs->lParam;
- for (int i = 0; file[i]; i++) {
- if (!boost::filesystem::exists(file[i]))
- return 0; //we do not want to send file unencrypted (sometimes ack have wrong info)
- if (wcsstr(file[i], L".gpg"))
- continue;
- std::wstring path_out = encrypt_file(ccs->hContact, file[i]);
- mir_free(file[i]);
- file[i] = mir_wstrdup(path_out.c_str());
- transfers.push_back(path_out);
- }
- }
- else {
- char **file = (char**)ccs->lParam;
- for (int i = 0; file[i]; i++) {
- if (!boost::filesystem::exists(file[i]))
- return 0; //we do not want to send file unencrypted (sometimes ack have wrong info)
- if (strstr(file[i], ".gpg"))
- continue;
- wchar_t *tmp = mir_utf8decodeW(file[i]);
- std::wstring path_out = encrypt_file(ccs->hContact, tmp);
- mir_free(tmp);
- char* tmp2 = mir_utf8encodeW(path_out.c_str());
- mir_free(file[i]);
- file[i] = tmp2;
- transfers.push_back(path_out);
-
- }
- }
- }
- return Proto_ChainSend(w, ccs);
-}
-
-void HistoryLog(MCONTACT hContact, const char *msg, uint32_t _time, uint32_t flags)
-{
- DBEVENTINFO dbei = {};
- dbei.szModule = MODULENAME;
- dbei.flags = DBEF_UTF | flags;
- dbei.timestamp = (_time) ? _time : (uint32_t)time(0);
- dbei.cbBlob = (uint32_t)mir_strlen(msg) + 1;
- dbei.pBlob = (uint8_t*)msg;
- db_event_add(hContact, &dbei);
-}
-
-static int ControlAddStringUtf(HWND ctrl, uint32_t msg, const wchar_t *szString)
-{
- int item = -1;
- item = SendMessage(ctrl, msg, 0, (LPARAM)szString);
- return item;
-}
-
-int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data)
-{
- int item = ControlAddStringUtf(hCombo, CB_ADDSTRING, szString);
- SendMessage(hCombo, CB_SETITEMDATA, item, data);
-
- return item;
-}
-
-static JABBER_HANDLER_FUNC SendHandler(IJabberInterface *ji, TiXmlElement *node, void*)
-{
- TiXmlDocument *pDoc = node->GetDocument();
-
- for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) {
- LPCSTR str = local_node->GetText();
- LPCSTR nodename = local_node->Name();
- LPCSTR attr = local_node->Attribute("to");
- if (attr) {
- MCONTACT hContact = ji->ContactFromJID(attr);
- if (hContact)
- if (!isContactSecured(hContact))
- break;
- }
-
- if (str == nullptr)
- continue;
-
- // TODO: make following block more readable
- if (g_plugin.bPresenceSigning && nodename && strstr(nodename, "status")) {
- string path_out = ptrA(g_plugin.getUStringA("szHomePath", ""));
- string file = get_random(10);
- path_out += "\\tmp\\";
- path_out += file;
- boost::filesystem::remove(path_out);
- wfstream f(path_out.c_str(), std::ios::out);
- f << str;
- f.close();
- if (!boost::filesystem::exists(path_out)) {
- if (globals.debuglog)
- globals.debuglog << "info: Failed to write prescense in file";
- break;
- }
-
- gpg_execution_params params;
- {
- char setting[64];
- mir_snprintf(setting, sizeof(setting) - 1, "%s_KeyID", ji->GetModuleName());
- CMStringA inkeyid = g_plugin.getMStringA(setting);
- if (inkeyid.IsEmpty())
- inkeyid = g_plugin.getMStringA("KeyID");
-
- CMStringW pass;
- if (!inkeyid.IsEmpty()) {
- string dbsetting = "szKey_";
- dbsetting += inkeyid;
- dbsetting += "_Password";
- pass = g_plugin.getMStringW(dbsetting.c_str());
- if (!pass.IsEmpty() && globals.debuglog)
- globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to encrypt message from self with password";
- }
- else {
- pass = g_plugin.getMStringW("szKeyPassword");
- if (!pass.IsEmpty() && globals.debuglog)
- globals.debuglog << "info: found password for all keys in database, trying to encrypt message from self with password";
- }
- if (pass[0]) {
- params.addParam(L"--passphrase");
- params.addParam(pass.c_str());
- }
- else if (!globals.wszPassword.IsEmpty()) {
- if (globals.debuglog)
- globals.debuglog << "info: found password in memory, trying to encrypt message from self with password";
- params.addParam(L"--passphrase");
- params.addParam(globals.wszPassword.c_str());
- }
- else if (globals.debuglog)
- globals.debuglog << "info: passwords not found in database or memory, trying to encrypt message from self without password";
- }
-
- params.addParam(L"--local-user");
- params.addParam(g_plugin.getMStringW("KeyID").c_str());
- params.addParam(L"--default-key");
- params.addParam(g_plugin.getMStringW("KeyID").c_str());
- params.addParam(L"--batch");
- params.addParam(L"--yes");
- params.addParam(L"-abs");
- params.addParam(Utf2T(path_out.c_str()).get());
- gpg_launcher(params, boost::posix_time::seconds(15)); // TODO: handle errors
-
- boost::filesystem::remove(path_out);
- path_out += ".asc";
- f.open(path_out.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
- wstring data;
- if (f.is_open()) {
- std::wifstream::pos_type size = f.tellg();
- wchar_t *tmp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1];
- f.seekg(0, std::ios::beg);
- f.read(tmp, size);
- tmp[size] = '\0';
- data.append(tmp);
- delete[] tmp;
- f.close();
- boost::filesystem::remove(path_out);
- }
- if (data.empty()) {
- if (globals.debuglog)
- globals.debuglog << "info: Failed to read prescense sign from file";
- break;
- }
- if (data.find(L"-----BEGIN PGP SIGNATURE-----") != wstring::npos && data.find(L"-----END PGP SIGNATURE-----") != wstring::npos) {
- wstring::size_type p1 = data.find(L"-----BEGIN PGP SIGNATURE-----") + mir_wstrlen(L"-----BEGIN PGP SIGNATURE-----");
- if (data.find(L"Version: ", p1) != wstring::npos) {
- p1 = data.find(L"Version: ", p1);
- p1 = data.find(L"\n", p1);
- if (data.find(L"Version: ", p1) != wstring::npos) {
- p1 = data.find(L"Version: ", p1);
- p1 = data.find(L"\n", p1) + 1;
- }
- else
- p1 += 1;
- }
- if (data.find(L"Comment: ", p1) != wstring::npos) {
- p1 = data.find(L"Comment: ", p1);
- p1 = data.find(L"\n", p1);
- if (data.find(L"Comment: ", p1) != wstring::npos) {
- p1 = data.find(L"Comment: ", p1);
- p1 = data.find(L"\n", p1) + 1;
- }
- else
- p1 += 1;
- }
- else
- p1 += 1;
- p1++;
- wstring::size_type p2 = data.find(L"-----END PGP SIGNATURE-----");
-
- std::wstring tmp = data.substr(p1, p2 - p1);
- strip_line_term(tmp);
- TiXmlElement* encrypted_data = pDoc->NewElement("x"); node->InsertEndChild(encrypted_data);
- encrypted_data->SetText(tmp.c_str());
- encrypted_data->SetAttribute("xmlns", "jabber:x:signed");
- }
- break;
- }
- }
- return FALSE;
-}
-
-static JABBER_HANDLER_FUNC PresenceHandler(IJabberInterface *ji, TiXmlElement* node, void*)
-{
- for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) {
- LPCSTR nodename = local_node->Name();
- if (nodename == nullptr)
- continue;
-
- if (!mir_strcmp(nodename, "x"))
- continue;
-
- for (auto *pAttr = local_node->FirstAttribute(); pAttr; pAttr = pAttr->Next()) {
- LPCSTR value = pAttr->Value();
- if (!mir_strcmp(value, "jabber:x:signed")) {
- std::string status_str;
-
- for (auto *local_node2 = node->FirstChildElement(); local_node2; local_node2 = local_node2->NextSiblingElement()) {
- LPCSTR nodename2 = local_node2->Name();
- if (!mir_strcmp(nodename2, "status")) {
- LPCSTR status = local_node2->GetText();
- if (status)
- status_str = status;
- break;
- }
- }
-
- LPCSTR data = local_node->GetText();
- string sign = "-----BEGIN PGP SIGNATURE-----\n\n";
- wstring file = toUTF16(get_random(10)), status_file = toUTF16(get_random(10));
- sign += data;
- sign += "\n-----END PGP SIGNATURE-----\n";
-
- CMStringW path_out = g_plugin.getMStringW("szHomePath"), status_file_out = path_out;
- path_out += L"\\tmp\\";
- path_out += file.c_str();
- path_out += L".sig";
- status_file_out += L"\\tmp\\";
- status_file_out += status_file.c_str();
- status_file_out += L".status";
-
- boost::filesystem::remove(path_out.c_str());
- boost::filesystem::remove(status_file_out.c_str());
- wfstream f(path_out.c_str(), std::ios::out);
- while (!f.is_open())
- f.open(path_out.c_str(), std::ios::out);
- f << sign.c_str();
- f.close();
- f.open(status_file_out.c_str(), std::ios::out);
- while (!f.is_open())
- f.open(status_file_out.c_str(), std::ios::out);
- f << status_str.c_str();
- f.close();
- if (!boost::filesystem::exists(path_out.c_str())) {
- if (globals.debuglog)
- globals.debuglog << "info: Failed to write sign in file";
- return FALSE;
- }
- {
- // gpg
- gpg_execution_params params;
- params.addParam(L"--verify");
- params.addParam(L"-a");
- params.addParam(path_out.c_str());
- params.addParam(status_file_out.c_str());
- if (!gpg_launcher(params, boost::posix_time::seconds(15)))
- return FALSE;
- if (params.result == pxNotFound)
- return FALSE;
-
- boost::filesystem::remove(path_out.c_str());
- boost::filesystem::remove(status_file_out.c_str());
-
- string out(params.out);
- if (out.find("key ID ") != string::npos) {
- //need to get hcontact here, i can get jid from hxml, and get handle from jid, maybe exists better way ?
- string::size_type p1 = out.find("key ID ") + mir_strlen("key ID ");
- string::size_type p2 = out.find("\n", p1);
- if (p1 != string::npos && p2 != string::npos) {
- MCONTACT hContact = ji->ContactFromJID(node->Attribute("from"));
- if (hContact)
- globals.hcontact_data[hContact].key_in_prescense = out.substr(p1, p2 - p1 - 1).c_str();
- }
- }
- }
- return FALSE;
- }
- }
- }
- return FALSE;
-}
-
-static JABBER_HANDLER_FUNC MessageHandler(IJabberInterface*, TiXmlElement*, void*)
-{
- return FALSE;
-}
-
-int GetJabberInterface(WPARAM, LPARAM) //get interface for all jabber accounts, options later
-{
- list <JabberAccount *>::iterator p;
- globals.Accounts.clear();
-
- int accNum = 0;
- for (auto &pa : Accounts()) {
- IJabberInterface *pApi = getJabberApi(pa->szModuleName);
- if (pApi == nullptr)
- continue;
-
- auto *pAcc = new JabberAccount();
- pAcc->setJabberInterface(pApi);
- if (pa->tszAccountName)
- pAcc->setAccountName(mir_wstrdup(pa->tszAccountName));
- else
- pAcc->setAccountName(mir_a2u(pa->szModuleName));
-
- pAcc->setAccountNumber(accNum++);
- pAcc->setSendHandler(pApi->AddSendHandler((JABBER_HANDLER_FUNC)SendHandler));
- pAcc->setPresenceHandler(pApi->AddPresenceHandler((JABBER_HANDLER_FUNC)PresenceHandler));
-
- if (g_plugin.bAutoExchange) {
- pApi->RegisterFeature("GPG_Key_Auto_Exchange:0", "Indicates that gpg installed and configured to public key auto exchange (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)");
- pApi->AddFeatures("GPG_Key_Auto_Exchange:0\0\0");
- }
- if (g_plugin.bFileTransfers) {
- pApi->RegisterFeature("GPG_Encrypted_FileTransfers:0", "Indicates that gpg installed and configured to encrypt files (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)");
- pApi->AddFeatures("GPG_Encrypted_FileTransfers:0\0\0");
- }
-
- globals.Accounts.push_back(pAcc);
- }
-
- return 0;
-}
-
-void RemoveHandlers()
-{
- for (auto &it : globals.Accounts) {
- auto *pApi = it->getJabberInterface();
- if (pApi == nullptr)
- continue;
-
- if (it->getMessageHandler() != INVALID_HANDLE_VALUE)
- pApi->RemoveHandler(it->getMessageHandler());
- if (it->getPresenceHandler() != INVALID_HANDLE_VALUE)
- pApi->RemoveHandler(it->getPresenceHandler());
- pApi->RemoveFeatures("GPG_Encrypted_FileTransfers:0\0\0");
- pApi->RemoveFeatures("GPG_Key_Auto_Exchange:0\0\0");
- }
-}
-
-bool isContactSecured(MCONTACT hContact)
-{
- uint8_t gpg_enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
- if (!gpg_enc) {
- if (globals.debuglog)
- globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact));
- return false;
- }
- if (!db_mc_isMeta(hContact)) {
- CMStringW key = g_plugin.getMStringW(hContact, "GPGPubKey");
- if (key.IsEmpty()) {
- if (globals.debuglog)
- globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact));
- return false;
- }
- }
- if (globals.debuglog)
- globals.debuglog << "encryption is turned on for " + toUTF8(Clist_GetContactDisplayName(hContact));
- return true;
-}
-
-bool isContactHaveKey(MCONTACT hContact)
-{
- ptrW key(g_plugin.getWStringA(hContact, "GPGPubKey"));
- return (mir_wstrlen(key) > 0);
-}
-
-bool isGPGKeyExist()
-{
- CMStringW id(g_plugin.getMStringW("KeyID"));
- CMStringA key(g_plugin.getMStringA("GPGPubKey"));
- return (!id.IsEmpty() && !key.IsEmpty());
-}
-
-bool isGPGValid()
-{
- ptrW tmp(g_plugin.getWStringA("szGpgBinPath", L""));
- bool gpg_exists = false, is_valid = true;
- boost::filesystem::path p(tmp);
-
- if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
- gpg_exists = true;
- else {
- wchar_t path[MAX_PATH], mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- SetCurrentDirectoryW(mir_path);
-
- //mir_realloc(path, (mir_wstrlen(path)+64)*sizeof(wchar_t));
- wchar_t gpg_path[MAX_PATH];
- mir_wstrcpy(gpg_path, mir_path);
- mir_wstrcat(gpg_path, L"\\GnuPG\\gpg.exe");
-
- p = boost::filesystem::path(gpg_path);
- if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) {
- gpg_exists = true;
- mir_wstrcpy(path, L"GnuPG\\gpg.exe");
- }
- tmp = mir_wstrdup(path);
- }
-
- if (gpg_exists) {
- g_plugin.setWString("szGpgBinPath", tmp);
-
- gpg_execution_params params;
- params.addParam(L"--version");
- bool _gpg_valid = globals.gpg_valid;
- globals.gpg_valid = true;
- gpg_launcher(params);
- globals.gpg_valid = _gpg_valid; //TODO: check this
- int p1 = params.out.Find("(GnuPG) ");
- if (p1 == -1)
- is_valid = false;
- }
-
- return is_valid && gpg_exists;
-}
-
-#define NEWTSTR_MALLOC(A) (A==NULL)?NULL:mir_strcpy((char*)mir_alloc(sizeof(char)*(mir_strlen(A)+1)),A)
-
-const bool StriStr(const char *str, const char *substr)
-{
- bool i = false;
- char *str_up = NEWTSTR_MALLOC(str);
- char *substr_up = NEWTSTR_MALLOC(substr);
-
- CharUpperBuffA(str_up, (uint32_t)mir_strlen(str_up));
- CharUpperBuffA(substr_up, (uint32_t)mir_strlen(substr_up));
-
- if (strstr(str_up, substr_up))
- i = true;
-
- mir_free(str_up);
- mir_free(substr_up);
-
- return i;
-}
-
-bool IsOnline(MCONTACT hContact)
-{
- if (g_plugin.getByte(hContact, "Status", 0) == ID_STATUS_OFFLINE)
- return false;
- return true;
-}
-
-string toUTF8(wstring str)
-{
- string ustr;
- try {
- utf8::utf16to8(str.begin(), str.end(), back_inserter(ustr));
- }
- catch (const utf8::exception& e) {
- if (globals.debuglog)
- globals.debuglog << std::string("utf8cpp encoding exception: ") + (char*)e.what();
- //TODO
- }
- return ustr;
-}
-
-wstring toUTF16(string str) //convert as much as possible
-{
- wstring ustr;
- string tmpstr;
- try {
- utf8::replace_invalid(str.begin(), str.end(), back_inserter(tmpstr));
- utf8::utf8to16(tmpstr.begin(), tmpstr.end(), back_inserter(ustr));
- }
- catch (const utf8::exception& e) {
- if (globals.debuglog)
- globals.debuglog << std::string("utf8cpp decoding exception: ") + (char*)e.what();
- //TODO
- }
- return ustr;
-}
-
-string get_random(int length)
-{
- string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
- string data;
- boost::random_device rng;
- boost::variate_generator<boost::random_device&, boost::uniform_int<>> gen(rng, boost::uniform_int<>(0, (int)chars.length() - 1));
- for (int i = 0; i < length; ++i)
- data += chars[gen()];
- return data;
-}
-
-void send_encrypted_msgs_thread(void *param)
-{
- MCONTACT hContact = (MCONTACT)(DWORD_PTR)param;
- while (true) {
- while (!isContactSecured(hContact))
- Sleep(1000);
-
- if (!globals.hcontact_data[hContact].msgs_to_send.empty()) {
- Sleep(1000);
- list<string>::iterator end = globals.hcontact_data[hContact].msgs_to_send.end();
- extern std::list<HANDLE> sent_msgs;
- for (list<string>::iterator p = globals.hcontact_data[hContact].msgs_to_send.begin(); p != end; ++p) {
- sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)p->c_str()));
- HistoryLog(hContact, p->c_str(), DBEF_SENT);
- Sleep(1000);
- }
- globals.hcontact_data[hContact].msgs_to_send.clear();
- return;
- }
- else
- return;
- }
-}
-
-void ExportGpGKeysFunc(int type)
-{
- ptrW p(GetFilePath(L"Choose file to export keys", L"*", L"Any file", true));
- if (!p || !p[0])
- return;
-
- std::ofstream file;
- file.open(p, std::ios::trunc | std::ios::out);
- if (!file.is_open())
- return; //TODO: handle error
-
- int exported_keys = 0;
- if (!type || type == 2) {
- for (auto &hContact : Contacts()) {
- CMStringA key = g_plugin.getMStringA(hContact, "GPGPubKey");
- if (key.IsEmpty())
- continue;
-
- ptrW wszLogin(Contact::GetInfo(CNF_UNIQUEID, 0, Proto_GetBaseAccountName(hContact))), wszContact(Contact::GetInfo(CNF_UNIQUEID, hContact));
- if (wszLogin == nullptr || wszContact == nullptr)
- continue;
-
- std::string id = "Comment: login ";
- id += T2Utf(wszLogin).get();
- id += " contact_id ";
- id += T2Utf(wszContact).get();
- id += '\n';
-
- int p1 = key.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----");
- if (p1 == -1)
- continue;
- p1 += mir_strlen("-----BEGIN PGP PUBLIC KEY BLOCK-----");
- p1++;
- key.Insert(p1, id.c_str());
- file << key;
- file << std::endl;
- exported_keys++;
- }
- }
-
- if (type == 1 || type == 2) {
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--export-secret-keys");
- params.addParam(L"-a");
- gpg_launcher(params); //TODO: handle errors
-
- file << params.out.c_str();
- file << std::endl;
- }
- if (file.is_open())
- file.close();
- wchar_t msg[512];
- if (type == 2)
- mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys and all private keys."), exported_keys);
- else if (type == 1)
- mir_snwprintf(msg, TranslateT("We have successfully exported all private keys."));
- else if (!type)
- mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys."), exported_keys);
- MessageBox(nullptr, msg, TranslateT("Keys export result"), MB_OK);
-}
-
-INT_PTR ImportGpGKeys(WPARAM, LPARAM)
-{
- ptrW p(GetFilePath(L"Choose file to import keys from", L"*", L"Any file"));
- if (!p || !p[0])
- return 1;
-
- std::ifstream file;
- file.open(p, std::ios::in);
- if (!file.is_open())
- return 1; //TODO: handle error
-
- int processed_keys = 0, processed_private_keys = 0;
-
- char line[256];
- file.getline(line, 255);
- if (!strstr(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && !strstr(line, "-----BEGIN PGP PRIVATE KEY BLOCK-----"))
- return 1; //TODO: handle error
-
- std::string key, login, contact_id;
- key += line;
- key += '\n';
- while (file.is_open() && !file.eof()) {
- file.getline(line, 255);
- key += line;
- key += '\n';
- if (strstr(line, "-----END PGP PUBLIC KEY BLOCK-----")) {
- std::string::size_type p1 = key.rfind("Comment: login "), p2 = 0;
- if (p1 == std::string::npos)
- {
- key.clear();
- continue; //TODO: warning about malformed file
- }
- p1 += mir_strlen("Comment: login ");
- p2 = key.rfind(" contact_id ");
- login = key.substr(p1, p2 - p1);
- p2 += mir_strlen(" contact_id ");
- p1 = key.find("\n", p2);
- contact_id = key.substr(p2, p1 - p2);
- p1 = key.rfind("Comment: login ");
- p2 = key.find("\n", p1);
- p2++;
- key.erase(p1, p2 - p1);
-
- PROTOACCOUNT *pFoundAcc = nullptr;
- for (auto &pa : Accounts()) {
- ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, 0, pa->szModuleName));
- if (wszUniqueId == nullptr)
- continue;
-
- if (login == T2Utf(wszUniqueId).get()) {
- pFoundAcc = pa;
- break;
- }
- }
-
- if (pFoundAcc == nullptr)
- continue;
-
- for (auto &hContact : Contacts(pFoundAcc->szModuleName)) {
- ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, hContact, pFoundAcc->szModuleName));
- if (wszUniqueId == nullptr)
- continue;
-
- if (contact_id != T2Utf(wszUniqueId).get())
- continue;
-
- CMStringW path = g_plugin.getMStringW("szHomePath");
-
- gpg_execution_params params;
- {
- wstring rand = toUTF16(get_random(10));
- path += L"\\";
- path += rand.c_str();
- boost::filesystem::remove(path.c_str());
- wfstream f(path, std::ios::out);
- f << toUTF16(key).c_str();
- f.close();
- params.addParam(L"--batch");
- params.addParam(L"--import");
- params.addParam(path.c_str());
- }
- if (!gpg_launcher(params))
- break;
- if (params.result == pxNotFound)
- break;
- if (params.result == pxSuccess)
- processed_keys++;
-
- string output(params.out);
- if (output.find("already in secret keyring") != string::npos) {
- MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK);
- boost::filesystem::remove(path.c_str());
- break;
- }
- char *tmp2;
- string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
- string::size_type s2 = output.find(":", s);
- tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
- mir_strcpy(tmp2, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp2, nullptr);
- g_plugin.setString(hContact, "KeyID", tmp2);
- mir_free(tmp2);
- s = output.find("“", s2);
- if (s == string::npos) {
- s = output.find("\"", s2);
- s += 1;
- }
- else
- s += 3;
- if ((s2 = output.find("(", s)) == string::npos)
- s2 = output.find("<", s);
- else if (s2 > output.find("<", s))
- s2 = output.find("<", s);
- if (s2 != string::npos) {
- tmp2 = (char *)mir_alloc((output.substr(s, s2 - s - 1).length() + 1) * sizeof(char));
- mir_strcpy(tmp2, output.substr(s, s2 - s - 1).c_str());
- mir_utf8decode(tmp2, nullptr);
- if (hContact) {
- g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
- }
- mir_free(tmp2);
- if ((s = output.find(")", s2)) == string::npos)
- s = output.find(">", s2);
- else if (s > output.find(">", s2))
- s = output.find(">", s2);
- s2++;
- if (output[s] == ')') {
- tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
- mir_strcpy(tmp2, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp2, nullptr);
- if (hContact)
- g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str());
- mir_free(tmp2);
- s += 3;
- s2 = output.find(">", s);
- tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
- mir_strcpy(tmp2, output.substr(s, s2 - s).c_str());
- mir_utf8decode(tmp2, nullptr);
- if (hContact)
- g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str());
- mir_free(tmp2);
- }
- else {
- tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
- mir_strcpy(tmp2, output.substr(s2, s - s2).c_str());
- mir_utf8decode(tmp2, nullptr);
- if (hContact)
- g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
- mir_free(tmp2);
- }
- }
- g_plugin.setByte(hContact, "GPGEncryption", 1);
- g_plugin.setWString(hContact, "GPGPubKey", toUTF16(key).c_str());
-
- boost::filesystem::remove(path.c_str());
- break;
- }
- key.clear();
- }
- else if (strstr(line, "-----END PGP PRIVATE KEY BLOCK-----")) {
- CMStringW tmp2 = g_plugin.getMStringW("szHomePath");
- tmp2 += L"\\temporary_exported.asc";
- boost::filesystem::remove(tmp2.c_str());
-
- wfstream f(tmp2, std::ios::out);
- f << toUTF16(key).c_str();
- f.close();
-
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--import");
- params.addParam(tmp2.c_str());
- if (!gpg_launcher(params))
- break;
- if (params.result == pxNotFound)
- break;
- if (params.result == pxSuccess)
- processed_private_keys++;
- key.clear();
- }
- }
- if (file.is_open())
- file.close();
- wchar_t msg[512];
- if (processed_private_keys)
- mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys and some private keys."), processed_keys);
- else
- mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys."), processed_keys);
- MessageBox(nullptr, msg, TranslateT("Keys import result"), MB_OK);
- return 0;
-}
-
-void SendErrorMessage(MCONTACT hContact)
-{
- if (!g_plugin.bSendErrorMessages)
- return;
-
- uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
- g_plugin.setByte(hContact, "GPGEncryption", 0);
- ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message");
- HistoryLog(hContact, "Error message sent", DBEF_SENT);
- g_plugin.setByte(hContact, "GPGEncryption", enc);
-}
-
-void fix_line_term(std::string &s)
-{
- if (s.empty())
- return;
-
- boost::algorithm::erase_all(s, "\r\r");
-
- // unified line endings for unix & windows port
- boost::algorithm::replace_all(s, "\r\n", "\n");
- boost::algorithm::replace_all(s, "\n", "\r\n");
-}
-
-void strip_line_term(std::wstring &s)
-{
- if (s.empty())
- return;
- boost::algorithm::erase_all(s, L"\r");
- boost::algorithm::erase_all(s, L"\n");
-}
-
-void strip_line_term(std::string &s)
-{
- if (s.empty())
- return;
- boost::algorithm::erase_all(s, "\r");
- boost::algorithm::erase_all(s, "\n");
-}
-
-void strip_tags(std::string &str)
-{
- if (str.empty())
- return;
- boost::algorithm::erase_all(str, globals.wszInopentag.c_str());
- boost::algorithm::erase_all(str, globals.wszInclosetag.c_str());
- boost::algorithm::erase_all(str, globals.wszOutopentag.c_str());
- boost::algorithm::erase_all(str, globals.wszOutclosetag.c_str());
-}
-
-
-void ShowEncryptedFileMsgBox()
-{
- CDlgEncryptedFileMsgBox *d = new CDlgEncryptedFileMsgBox;
- d->DoModal();
-}
-
-void clean_temp_dir()
-{
- using namespace boost::filesystem;
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- SetCurrentDirectoryW(mir_path);
-
- CMStringW tmp = mir_path;
- tmp += g_plugin.getMStringW("szHomePath");
- tmp += L"\\tmp";
- if (exists(tmp.c_str()) && is_directory(tmp.c_str())) {
- boost::filesystem::path p(tmp);
- for (directory_iterator i = directory_iterator(p), end = directory_iterator(); i != end; ++i) {
- if (boost::filesystem::is_regular_file(i->path())) {
- if ((i->path().filename().generic_string().length() == 10 && (i->path().filename().generic_string().find(".") == std::string::npos)) ||
- i->path().extension() == ".sig" || i->path().extension() == ".asc" || i->path().extension() == ".status")
- boost::filesystem::remove(i->path());
- }
- }
- }
-}
-
-bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path)
-{
- wstring tmp = gpg_bin_path;
- if (!tmp.empty()) {
- wchar_t mir_path[MAX_PATH];
- PathToAbsoluteW(L"\\", mir_path);
- SetCurrentDirectoryW(mir_path);
- if (!boost::filesystem::exists(tmp)) {
- MessageBox(nullptr, TranslateT("GPG binary does not exist.\nPlease choose another location"), TranslateT("Warning"), MB_OK);
- return false;
- }
- }
- else {
- MessageBox(nullptr, TranslateT("Please choose GPG binary location"), TranslateT("Warning"), MB_OK);
- return false;
- }
- {
- bool bad_version = false;
- g_plugin.setWString("szGpgBinPath", tmp.c_str());
-
- gpg_execution_params params;
- params.addParam(L"--version");
- bool _gpg_valid = globals.gpg_valid;
- globals.gpg_valid = true;
- gpg_launcher(params);
- globals.gpg_valid = _gpg_valid; //TODO: check this
- g_plugin.delSetting("szGpgBinPath");
- int p1 = params.out.Find("(GnuPG) ");
- if (p1 != -1) {
- p1 += mir_strlen("(GnuPG) ");
- if (params.out[p1] != '1')
- bad_version = true;
- }
- else {
- bad_version = false;
- MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
- return false;
- }
- if (bad_version)
- MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
- }
- tmp = gpg_home_path;
- if (tmp[tmp.length()] == '\\')
- tmp.erase(tmp.length());
- if (tmp.empty()) {
- MessageBox(nullptr, TranslateT("Please set keyring's home directory"), TranslateT("Warning"), MB_OK);
- return false;
- }
- {
- CMStringW path = g_plugin.getMStringW("szHomePath");
- uint32_t dwFileAttr = GetFileAttributes(path);
- if (dwFileAttr != INVALID_FILE_ATTRIBUTES) {
- dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
- SetFileAttributes(path, dwFileAttr);
- }
- }
- return true;
-}
-
-void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path)
-{
- g_plugin.setWString("szGpgBinPath", gpg_bin_path);
- g_plugin.setWString("szHomePath", gpg_home_path);
-}
-
-bool gpg_use_new_random_key(const char *account_name)
-{
- CMStringW path = g_plugin.getMStringW("szHomePath");
- path += L"\\new_key";
-
- // generating key file
- wfstream f(path.c_str(), std::ios::out);
- if (!f.is_open()) {
- MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
- return false;
- }
- f << "Key-Type: RSA";
- f << "\n";
- f << "Key-Length: 4096";
- f << "\n";
- f << "Subkey-Type: RSA";
- f << "\n";
- f << "Name-Real: ";
- f << get_random(6).c_str();
- f << "\n";
- f << "Name-Email: ";
- f << get_random(5).c_str();
- f << "@";
- f << get_random(5).c_str();
- f << ".";
- f << get_random(3).c_str();
- f << "\n";
- f.close();
-
- {
- // gpg execution
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"--yes");
- params.addParam(L"--gen-key");
- params.addParam(path.c_str());
- params.bNoOutput = true;
- if (!gpg_launcher(params, boost::posix_time::minutes(10)))
- return false;
- if (params.result == pxNotFound)
- return false;
-
- boost::filesystem::remove(path.c_str());
- string out(params.out);
- int p1 = params.out.Find("key ");
- if (p1 != -1)
- path = ptrW(mir_utf8decodeW(params.out.Mid(p1 + 4, 8).c_str()));
- else
- path.Empty();
- }
-
- if (!path.IsEmpty()) {
- gpg_execution_params params;
- params.addParam(L"--batch");
- params.addParam(L"-a");
- params.addParam(L"--export");
- params.addParam(path.c_str());
- if (!gpg_launcher(params))
- return false;
-
- if (params.result == pxNotFound)
- return false;
-
- params.out.Remove('\r');
-
- if (account_name == nullptr) {
- g_plugin.setString("GPGPubKey", params.out.c_str());
- g_plugin.setWString("KeyID", path.c_str());
- }
- else {
- CMStringA acc_str = account_name;
- g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str());
- g_plugin.setWString(acc_str + "_KeyID", path.c_str());
- }
- }
- return true;
-}
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with 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 "utf8.h"
+
+void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc)
+{
+ wchar_t str[MAX_PATH + 2] = {};
+ OPENFILENAME ofn = {};
+ wchar_t filter[512], *pfilter;
+ ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
+ ofn.Flags = OFN_EXPLORER;
+ ofn.lpstrTitle = TranslateW(WindowTittle);
+ wcsncpy(filter, TranslateW(szExtDesc), _countof(filter) - 1);
+ pfilter = filter + mir_wstrlen(filter) + 1;
+ mir_wstrcpy(pfilter, szExt);
+ pfilter[mir_wstrlen(pfilter) + 1] = '\0';
+ pfilter[mir_wstrlen(pfilter) + 2] = '\0';
+ ofn.lpstrFilter = filter;
+ wcsncpy(str, g_plugin.getMStringW(szSetting), _countof(str) - 1);
+ if (mir_wstrlen(str) < 2)
+ str[0] = '\0';
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (GetOpenFileName(&ofn))
+ g_plugin.setWString(szSetting, str);
+}
+
+wchar_t* GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file)
+{
+ wchar_t str[MAX_PATH + 1];
+ OPENFILENAME ofn = {};
+ wchar_t filter[512], *pfilter;
+ ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName);
+ ofn.Flags = OFN_EXPLORER;
+ ofn.lpstrTitle = TranslateW(WindowTittle);
+ mir_wstrcpy(filter, TranslateW(szExtDesc));
+ pfilter = filter + mir_wstrlen(filter) + 1;
+ mir_wstrcpy(pfilter, szExt);
+ pfilter[mir_wstrlen(pfilter) + 1] = '\0';
+ pfilter[mir_wstrlen(pfilter) + 2] = '\0';
+ ofn.lpstrFilter = filter;
+ mir_wstrcpy(str, L"");
+ if (mir_wstrlen(str) < 2)
+ str[0] = '\0';
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (!save_file) {
+ if (!GetOpenFileName(&ofn))
+ return nullptr;
+ }
+ else {
+ if (!GetSaveFileName(&ofn))
+ return nullptr;
+ }
+ return mir_wstrdup(str);
+}
+
+void GetFolderPath(wchar_t *WindowTittle)
+{
+ BROWSEINFO pbi = {};
+ pbi.lpszTitle = WindowTittle;
+ pbi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE;
+ LPITEMIDLIST pidl = SHBrowseForFolder(&pbi);
+ if (pidl != nullptr) {
+ wchar_t path[MAX_PATH];
+ if (SHGetPathFromIDList(pidl, path)) {
+ g_plugin.setWString("szHomePath", path);
+ }
+ IMalloc * imalloc = nullptr;
+ if (SUCCEEDED(SHGetMalloc(&imalloc))) {
+ imalloc->Free(pidl);
+ imalloc->Release();
+ }
+ }
+}
+
+INT_PTR LoadKey(WPARAM w, LPARAM)
+{
+ ShowLoadPublicKeyDialog((MCONTACT)w, false);
+ return 0;
+}
+
+INT_PTR SendKey(WPARAM w, LPARAM)
+{
+ MCONTACT hContact = db_mc_tryMeta(w);
+ std::string key_id_str;
+
+ LPSTR proto = Proto_GetBaseAccountName(hContact);
+ PROTOACCOUNT *acc = Proto_GetAccount(proto);
+ std::string acc_str;
+ if (acc) {
+ acc_str = acc->szModuleName;
+ key_id_str = acc_str;
+ key_id_str += "_KeyID";
+ acc_str += "_GPGPubKey";
+ }
+
+ CMStringA szMessage = g_plugin.getMStringA(acc_str.empty() ? "GPGPubKey" : acc_str.c_str());
+ if (szMessage.IsEmpty())
+ szMessage = g_plugin.getMStringA("GPGPubKey"); //try to get default key as fallback in any way
+
+ if (!szMessage.IsEmpty()) {
+ uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)szMessage.c_str());
+ std::string msg = "Public key ";
+ CMStringA keyid = g_plugin.getMStringA(key_id_str.c_str());
+ if (keyid.IsEmpty())
+ keyid = g_plugin.getMStringA("KeyID");
+ msg += keyid;
+ msg += " sent";
+
+ HistoryLog(hContact, msg.c_str(), DBEF_SENT);
+ g_plugin.setByte(hContact, "GPGEncryption", enc);
+ }
+
+ return 0;
+}
+
+INT_PTR ToggleEncryption(WPARAM w, LPARAM)
+{
+ MCONTACT hContact = (MCONTACT)w;
+ uint8_t enc;
+ if (db_mc_isMeta(hContact)) {
+ enc = g_plugin.getByte(metaGetMostOnline(hContact), "GPGEncryption");
+ if (MessageBox(nullptr, TranslateT("Do you want to toggle encryption for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) {
+ int count = db_mc_getSubCount(hContact);
+ for (int i = 0; i < count; i++) {
+ MCONTACT hcnt = db_mc_getSub(hContact, i);
+ if (hcnt)
+ g_plugin.getByte(hcnt, "GPGEncryption", enc ? 0 : 1);
+ }
+ g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1);
+ }
+ }
+ else {
+ enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1);
+ }
+ setSrmmIcon(hContact);
+ return 0;
+}
+
+int OnPreBuildContactMenu(WPARAM w, LPARAM)
+{
+ MCONTACT hContact = db_mc_tryMeta(w);
+ {
+ CMenuItem mi2(&g_plugin);
+ LPSTR proto = Proto_GetBaseAccountName(hContact);
+ PROTOACCOUNT *acc = Proto_GetAccount(proto);
+ std::string setting;
+ if (acc) {
+ setting = acc->szModuleName;
+ setting += "_KeyID";
+ }
+
+ CMStringA keyid = g_plugin.getMStringA(setting.c_str());
+ if (keyid.IsEmpty())
+ keyid = g_plugin.getMStringA("KeyID");
+
+ wchar_t buf[128] = { 0 };
+ mir_snwprintf(buf, L"%s: %S", TranslateT("Send public key"), keyid.c_str());
+ Menu_ModifyItem(g_plugin.hSendKey, buf);
+ }
+
+ int flags;
+ CMStringA tmp = g_plugin.getMStringW(hContact, "GPGPubKey");
+ if (tmp.IsEmpty()) {
+ g_plugin.delSetting(hContact, "GPGEncryption");
+ flags = CMIF_GRAYED;
+ }
+ else flags = 0;
+
+ if (g_plugin.getByte(hContact, "GPGEncryption"))
+ Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn off GPG encryption"), g_plugin.getIconHandle(IDI_SECURED), flags);
+ else
+ Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn on GPG encryption"), g_plugin.getIconHandle(IDI_UNSECURED), flags);
+ return 0;
+}
+
+list<wstring> transfers;
+
+int onProtoAck(WPARAM, LPARAM l)
+{
+ ACKDATA *ack = (ACKDATA*)l;
+ if (ack->type == ACKTYPE_FILE) {
+ switch (ack->result) {
+ case ACKRESULT_DENIED: case ACKRESULT_FAILED:
+ break;
+ case ACKRESULT_SUCCESS:
+ if (ack->hProcess) {
+ PROTOFILETRANSFERSTATUS *f = (PROTOFILETRANSFERSTATUS*)ack->hProcess;
+ if (f == nullptr)
+ break;
+
+ if ((f->flags & PFTS_SENDING) != PFTS_SENDING) {
+ wchar_t *filename = nullptr;
+ if (f->flags & PFTS_UNICODE) {
+ if (f->szCurrentFile.w && f->szCurrentFile.w[0])
+ filename = mir_wstrdup(f->szCurrentFile.w);
+ if (!filename)
+ return 0;
+ }
+ else {
+ if (f->szCurrentFile.a && f->szCurrentFile.a[0])
+ filename = mir_utf8decodeW(f->szCurrentFile.a);
+ if (!filename)
+ return 0;
+ }
+ if (wcsstr(filename, L".gpg")) { //decrypt it
+ //process encrypted file
+ if (!g_plugin.bFileTransfers && !g_plugin.bSameAction) {
+ void ShowEncryptedFileMsgBox();
+ ShowEncryptedFileMsgBox();
+ }
+ if (!g_plugin.bFileTransfers && g_plugin.bSameAction)
+ return 0;
+ if (!globals.bDecryptFiles)
+ return 0;
+ HistoryLog(ack->hContact, "Received encrypted file, trying to decrypt");
+ if (!boost::filesystem::exists(f->szCurrentFile.w))
+ return 0;
+
+ gpg_execution_params params;
+ params.addParam(L"-o");
+ wstring file = filename;
+ wstring::size_type p1 = file.rfind(L".gpg");
+ file.erase(p1, mir_wstrlen(L".gpg"));
+ if (boost::filesystem::exists(file)) {
+ if (MessageBox(nullptr, TranslateT("Target file exists, do you want to replace it?"), TranslateT("Warning"), MB_YESNO) == IDNO)
+ return 0;
+ }
+ params.addParam(file);
+ boost::filesystem::remove(file);
+ {
+ // password
+ CMStringW pass;
+ CMStringA keyid = g_plugin.getMStringA(ack->hContact, "KeyID");
+ if (!keyid.IsEmpty()) {
+ string dbsetting = "szKey_";
+ dbsetting += keyid;
+ dbsetting += "_Password";
+ pass = g_plugin.getMStringW(dbsetting.c_str());
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password in database for key ID: " + string(keyid.c_str()) + ", trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
+ }
+ else {
+ pass = g_plugin.getMStringW("szKeyPassword");
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
+ }
+ if (!pass.IsEmpty()) {
+ params.addParam(L"--passphrase");
+ params.addParam(pass.c_str());
+ }
+ else if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password";
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " without password";
+ }
+ params.addParam(L"-d");
+ params.addParam(filename);
+ if (!gpg_launcher(params, boost::posix_time::minutes(15)))
+ return 0;
+
+ string out(params.out);
+ while (out.find("public key decryption failed: bad passphrase") != string::npos) {
+ if (globals.debuglog)
+ globals.debuglog << "info: failed to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " password needed, trying to get one";
+ if (globals._terminate)
+ break;
+ { //save inkey id
+ string::size_type s = out.find(" encrypted with ");
+ s = out.find(" ID ", s);
+ s += mir_strlen(" ID ");
+ string::size_type s2 = out.find(",", s);
+ if (db_mc_isMeta(ack->hContact))
+ g_plugin.setString(metaGetMostOnline(ack->hContact), "InKeyID", out.substr(s, s2 - s).c_str());
+ else
+ g_plugin.setString(ack->hContact, "InKeyID", out.substr(s, s2 - s).c_str());
+ }
+
+ CDlgKeyPasswordMsgBox(ack->hContact).DoModal();
+
+ if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact));
+
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+
+ if (!gpg_launcher(params, boost::posix_time::seconds(15)))
+ return 0;
+ if (params.result == pxNotFound)
+ return 0;
+ }
+ if (params.result == pxSuccess)
+ boost::filesystem::remove(filename);
+ }
+ mir_free(filename);
+ }
+ }
+ break;
+ }
+ }
+ else if (ack->type == ACKTYPE_MESSAGE) {
+ extern std::list<HANDLE> sent_msgs;
+ if (!sent_msgs.empty()) {
+ if (ack->result == ACKRESULT_FAILED) {
+ std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess);
+ if (it != sent_msgs.end())
+ HistoryLog(ack->hContact, "Failed to send encrypted message");
+ }
+ else if (ack->result == ACKRESULT_SUCCESS) {
+ std::list<HANDLE>::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess);
+ if (it != sent_msgs.end())
+ sent_msgs.erase(it);
+ }
+ }
+ }
+ return 0;
+}
+
+std::wstring encrypt_file(MCONTACT hContact, wchar_t *filename)
+{
+ MCONTACT hcnt = db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact;
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--tes");
+ params.addParam(L"-r");
+
+ CMStringW keyid = g_plugin.getMStringW(hcnt, "KeyID");
+ wchar_t *name = wcsrchr(filename, '\\');
+ if (!name)
+ name = filename;
+ else
+ name++;
+ wchar_t *file_out = new wchar_t[mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1];
+ mir_snwprintf(file_out, mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1, L"%s.gpg", name);
+ params.addParam(keyid.c_str());
+
+ if (g_plugin.getByte(hcnt, "bAlwaysTrust")) {
+ params.addParam(L"--trust-model");
+ params.addParam(L"always");
+ }
+
+ params.addParam(L"-o");
+ wchar_t *temp = _tgetenv(L"TEMP");
+ params.addParam(wstring(temp) + L"\\" + file_out);
+ wstring path_out = temp;
+ path_out += L"\\";
+ path_out += file_out;
+ boost::filesystem::remove(path_out);
+ params.addParam(L"-e");
+ params.addParam(filename);
+ delete[] file_out;
+
+ if (!gpg_launcher(params, boost::posix_time::minutes(3)))
+ return nullptr;
+
+ if (params.out.Find("There is no assurance this key belongs to the named user") != -1) {
+ if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO))
+ return nullptr;
+
+ g_plugin.setByte(hcnt, "bAlwaysTrust", 1);
+
+ params.addParam(L"--trust-model");
+ params.addParam(L"always");
+ if (!gpg_launcher(params, boost::posix_time::minutes(3)))
+ return nullptr;
+ }
+ return path_out;
+}
+
+//from secureim partially
+INT_PTR onSendFile(WPARAM w, LPARAM l)
+{
+ CCSDATA *ccs = (CCSDATA*)l;
+ if (!g_plugin.bFileTransfers)
+ return Proto_ChainSend(w, ccs);
+
+ if (isContactSecured(ccs->hContact)) {
+ char *proto = Proto_GetBaseAccountName(ccs->hContact);
+ bool cap_found = false, supported_proto = false;
+ ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", ""));
+ if (jid[0]) {
+ for (auto p : globals.Accounts) {
+ ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid));
+ if (caps) {
+ supported_proto = true;
+ string str;
+ for (int i = 0;; i++) {
+ str.push_back(caps[i]);
+ if (caps[i] == '\0')
+ if (caps[i + 1] == '\0')
+ break;
+ }
+
+ if (str.find("GPG_Encrypted_FileTransfers:0") != string::npos)
+ cap_found = true;
+ }
+ }
+ }
+
+ if (supported_proto && !cap_found) {
+ if (MessageBox(nullptr, TranslateT("Capability to decrypt file not found on other side.\nRecipient may be unable to decrypt file(s).\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO)
+ return Proto_ChainSend(w, ccs);
+ }
+ if (!supported_proto) {
+ if (MessageBox(nullptr, TranslateT("Unable to check encryption support on other side.\nRecipient may be unable to decrypt file(s).\nCurrently capability check supported only for ICQ and Jabber protocols.\nIt will work for any other proto if Miranda with New_GPG is used on other side.\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO)
+ return Proto_ChainSend(w, ccs);
+ }
+ HistoryLog(ccs->hContact, TranslateU("encrypting file for transfer"), DBEF_SENT);
+ if (StriStr(ccs->szProtoService, "/sendfilew")) {
+ wchar_t **file = (wchar_t **)ccs->lParam;
+ for (int i = 0; file[i]; i++) {
+ if (!boost::filesystem::exists(file[i]))
+ return 0; //we do not want to send file unencrypted (sometimes ack have wrong info)
+ if (wcsstr(file[i], L".gpg"))
+ continue;
+ std::wstring path_out = encrypt_file(ccs->hContact, file[i]);
+ mir_free(file[i]);
+ file[i] = mir_wstrdup(path_out.c_str());
+ transfers.push_back(path_out);
+ }
+ }
+ else {
+ char **file = (char**)ccs->lParam;
+ for (int i = 0; file[i]; i++) {
+ if (!boost::filesystem::exists(file[i]))
+ return 0; //we do not want to send file unencrypted (sometimes ack have wrong info)
+ if (strstr(file[i], ".gpg"))
+ continue;
+ wchar_t *tmp = mir_utf8decodeW(file[i]);
+ std::wstring path_out = encrypt_file(ccs->hContact, tmp);
+ mir_free(tmp);
+ char* tmp2 = mir_utf8encodeW(path_out.c_str());
+ mir_free(file[i]);
+ file[i] = tmp2;
+ transfers.push_back(path_out);
+
+ }
+ }
+ }
+ return Proto_ChainSend(w, ccs);
+}
+
+void HistoryLog(MCONTACT hContact, const char *msg, uint32_t _time, uint32_t flags)
+{
+ DBEVENTINFO dbei = {};
+ dbei.szModule = MODULENAME;
+ dbei.flags = DBEF_UTF | flags;
+ dbei.timestamp = (_time) ? _time : (uint32_t)time(0);
+ dbei.cbBlob = (uint32_t)mir_strlen(msg) + 1;
+ dbei.pBlob = (uint8_t*)msg;
+ db_event_add(hContact, &dbei);
+}
+
+static int ControlAddStringUtf(HWND ctrl, uint32_t msg, const wchar_t *szString)
+{
+ int item = -1;
+ item = SendMessage(ctrl, msg, 0, (LPARAM)szString);
+ return item;
+}
+
+int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data)
+{
+ int item = ControlAddStringUtf(hCombo, CB_ADDSTRING, szString);
+ SendMessage(hCombo, CB_SETITEMDATA, item, data);
+
+ return item;
+}
+
+static JABBER_HANDLER_FUNC SendHandler(IJabberInterface *ji, TiXmlElement *node, void*)
+{
+ TiXmlDocument *pDoc = node->GetDocument();
+
+ for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) {
+ LPCSTR str = local_node->GetText();
+ LPCSTR nodename = local_node->Name();
+ LPCSTR attr = local_node->Attribute("to");
+ if (attr) {
+ MCONTACT hContact = ji->ContactFromJID(attr);
+ if (hContact)
+ if (!isContactSecured(hContact))
+ break;
+ }
+
+ if (str == nullptr)
+ continue;
+
+ // TODO: make following block more readable
+ if (g_plugin.bPresenceSigning && nodename && strstr(nodename, "status")) {
+ string path_out = ptrA(g_plugin.getUStringA("szHomePath", ""));
+ string file = get_random(10);
+ path_out += "\\tmp\\";
+ path_out += file;
+ boost::filesystem::remove(path_out);
+ wfstream f(path_out.c_str(), std::ios::out);
+ f << str;
+ f.close();
+ if (!boost::filesystem::exists(path_out)) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to write prescense in file";
+ break;
+ }
+
+ gpg_execution_params params;
+ {
+ char setting[64];
+ mir_snprintf(setting, sizeof(setting) - 1, "%s_KeyID", ji->GetModuleName());
+ CMStringA inkeyid = g_plugin.getMStringA(setting);
+ if (inkeyid.IsEmpty())
+ inkeyid = g_plugin.getMStringA("KeyID");
+
+ CMStringW pass;
+ if (!inkeyid.IsEmpty()) {
+ string dbsetting = "szKey_";
+ dbsetting += inkeyid;
+ dbsetting += "_Password";
+ pass = g_plugin.getMStringW(dbsetting.c_str());
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to encrypt message from self with password";
+ }
+ else {
+ pass = g_plugin.getMStringW("szKeyPassword");
+ if (!pass.IsEmpty() && globals.debuglog)
+ globals.debuglog << "info: found password for all keys in database, trying to encrypt message from self with password";
+ }
+ if (pass[0]) {
+ params.addParam(L"--passphrase");
+ params.addParam(pass.c_str());
+ }
+ else if (!globals.wszPassword.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: found password in memory, trying to encrypt message from self with password";
+ params.addParam(L"--passphrase");
+ params.addParam(globals.wszPassword.c_str());
+ }
+ else if (globals.debuglog)
+ globals.debuglog << "info: passwords not found in database or memory, trying to encrypt message from self without password";
+ }
+
+ params.addParam(L"--local-user");
+ params.addParam(g_plugin.getMStringW("KeyID").c_str());
+ params.addParam(L"--default-key");
+ params.addParam(g_plugin.getMStringW("KeyID").c_str());
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"-abs");
+ params.addParam(Utf2T(path_out.c_str()).get());
+ gpg_launcher(params, boost::posix_time::seconds(15)); // TODO: handle errors
+
+ boost::filesystem::remove(path_out);
+ path_out += ".asc";
+ f.open(path_out.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
+ wstring data;
+ if (f.is_open()) {
+ std::wifstream::pos_type size = f.tellg();
+ wchar_t *tmp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1];
+ f.seekg(0, std::ios::beg);
+ f.read(tmp, size);
+ tmp[size] = '\0';
+ data.append(tmp);
+ delete[] tmp;
+ f.close();
+ boost::filesystem::remove(path_out);
+ }
+ if (data.empty()) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to read prescense sign from file";
+ break;
+ }
+ if (data.find(L"-----BEGIN PGP SIGNATURE-----") != wstring::npos && data.find(L"-----END PGP SIGNATURE-----") != wstring::npos) {
+ wstring::size_type p1 = data.find(L"-----BEGIN PGP SIGNATURE-----") + mir_wstrlen(L"-----BEGIN PGP SIGNATURE-----");
+ if (data.find(L"Version: ", p1) != wstring::npos) {
+ p1 = data.find(L"Version: ", p1);
+ p1 = data.find(L"\n", p1);
+ if (data.find(L"Version: ", p1) != wstring::npos) {
+ p1 = data.find(L"Version: ", p1);
+ p1 = data.find(L"\n", p1) + 1;
+ }
+ else
+ p1 += 1;
+ }
+ if (data.find(L"Comment: ", p1) != wstring::npos) {
+ p1 = data.find(L"Comment: ", p1);
+ p1 = data.find(L"\n", p1);
+ if (data.find(L"Comment: ", p1) != wstring::npos) {
+ p1 = data.find(L"Comment: ", p1);
+ p1 = data.find(L"\n", p1) + 1;
+ }
+ else
+ p1 += 1;
+ }
+ else
+ p1 += 1;
+ p1++;
+ wstring::size_type p2 = data.find(L"-----END PGP SIGNATURE-----");
+
+ std::wstring tmp = data.substr(p1, p2 - p1);
+ strip_line_term(tmp);
+ TiXmlElement* encrypted_data = pDoc->NewElement("x"); node->InsertEndChild(encrypted_data);
+ encrypted_data->SetText(tmp.c_str());
+ encrypted_data->SetAttribute("xmlns", "jabber:x:signed");
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+static JABBER_HANDLER_FUNC PresenceHandler(IJabberInterface *ji, TiXmlElement* node, void*)
+{
+ for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) {
+ LPCSTR nodename = local_node->Name();
+ if (nodename == nullptr)
+ continue;
+
+ if (!mir_strcmp(nodename, "x"))
+ continue;
+
+ for (auto *pAttr = local_node->FirstAttribute(); pAttr; pAttr = pAttr->Next()) {
+ LPCSTR value = pAttr->Value();
+ if (!mir_strcmp(value, "jabber:x:signed")) {
+ std::string status_str;
+
+ for (auto *local_node2 = node->FirstChildElement(); local_node2; local_node2 = local_node2->NextSiblingElement()) {
+ LPCSTR nodename2 = local_node2->Name();
+ if (!mir_strcmp(nodename2, "status")) {
+ LPCSTR status = local_node2->GetText();
+ if (status)
+ status_str = status;
+ break;
+ }
+ }
+
+ LPCSTR data = local_node->GetText();
+ string sign = "-----BEGIN PGP SIGNATURE-----\n\n";
+ wstring file = toUTF16(get_random(10)), status_file = toUTF16(get_random(10));
+ sign += data;
+ sign += "\n-----END PGP SIGNATURE-----\n";
+
+ CMStringW path_out = g_plugin.getMStringW("szHomePath"), status_file_out = path_out;
+ path_out += L"\\tmp\\";
+ path_out += file.c_str();
+ path_out += L".sig";
+ status_file_out += L"\\tmp\\";
+ status_file_out += status_file.c_str();
+ status_file_out += L".status";
+
+ boost::filesystem::remove(path_out.c_str());
+ boost::filesystem::remove(status_file_out.c_str());
+ wfstream f(path_out.c_str(), std::ios::out);
+ while (!f.is_open())
+ f.open(path_out.c_str(), std::ios::out);
+ f << sign.c_str();
+ f.close();
+ f.open(status_file_out.c_str(), std::ios::out);
+ while (!f.is_open())
+ f.open(status_file_out.c_str(), std::ios::out);
+ f << status_str.c_str();
+ f.close();
+ if (!boost::filesystem::exists(path_out.c_str())) {
+ if (globals.debuglog)
+ globals.debuglog << "info: Failed to write sign in file";
+ return FALSE;
+ }
+ {
+ // gpg
+ gpg_execution_params params;
+ params.addParam(L"--verify");
+ params.addParam(L"-a");
+ params.addParam(path_out.c_str());
+ params.addParam(status_file_out.c_str());
+ if (!gpg_launcher(params, boost::posix_time::seconds(15)))
+ return FALSE;
+ if (params.result == pxNotFound)
+ return FALSE;
+
+ boost::filesystem::remove(path_out.c_str());
+ boost::filesystem::remove(status_file_out.c_str());
+
+ string out(params.out);
+ if (out.find("key ID ") != string::npos) {
+ //need to get hcontact here, i can get jid from hxml, and get handle from jid, maybe exists better way ?
+ string::size_type p1 = out.find("key ID ") + mir_strlen("key ID ");
+ string::size_type p2 = out.find("\n", p1);
+ if (p1 != string::npos && p2 != string::npos) {
+ MCONTACT hContact = ji->ContactFromJID(node->Attribute("from"));
+ if (hContact)
+ globals.hcontact_data[hContact].key_in_prescense = out.substr(p1, p2 - p1 - 1).c_str();
+ }
+ }
+ }
+ return FALSE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static JABBER_HANDLER_FUNC MessageHandler(IJabberInterface*, TiXmlElement*, void*)
+{
+ return FALSE;
+}
+
+int GetJabberInterface(WPARAM, LPARAM) //get interface for all jabber accounts, options later
+{
+ list <JabberAccount *>::iterator p;
+ globals.Accounts.clear();
+
+ int accNum = 0;
+ for (auto &pa : Accounts()) {
+ IJabberInterface *pApi = getJabberApi(pa->szModuleName);
+ if (pApi == nullptr)
+ continue;
+
+ auto *pAcc = new JabberAccount();
+ pAcc->setJabberInterface(pApi);
+ if (pa->tszAccountName)
+ pAcc->setAccountName(mir_wstrdup(pa->tszAccountName));
+ else
+ pAcc->setAccountName(mir_a2u(pa->szModuleName));
+
+ pAcc->setAccountNumber(accNum++);
+ pAcc->setSendHandler(pApi->AddSendHandler((JABBER_HANDLER_FUNC)SendHandler));
+ pAcc->setPresenceHandler(pApi->AddPresenceHandler((JABBER_HANDLER_FUNC)PresenceHandler));
+
+ if (g_plugin.bAutoExchange) {
+ pApi->RegisterFeature("GPG_Key_Auto_Exchange:0", "Indicates that gpg installed and configured to public key auto exchange (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)");
+ pApi->AddFeatures("GPG_Key_Auto_Exchange:0\0\0");
+ }
+ if (g_plugin.bFileTransfers) {
+ pApi->RegisterFeature("GPG_Encrypted_FileTransfers:0", "Indicates that gpg installed and configured to encrypt files (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)");
+ pApi->AddFeatures("GPG_Encrypted_FileTransfers:0\0\0");
+ }
+
+ globals.Accounts.push_back(pAcc);
+ }
+
+ return 0;
+}
+
+void RemoveHandlers()
+{
+ for (auto &it : globals.Accounts) {
+ auto *pApi = it->getJabberInterface();
+ if (pApi == nullptr)
+ continue;
+
+ if (it->getMessageHandler() != INVALID_HANDLE_VALUE)
+ pApi->RemoveHandler(it->getMessageHandler());
+ if (it->getPresenceHandler() != INVALID_HANDLE_VALUE)
+ pApi->RemoveHandler(it->getPresenceHandler());
+ pApi->RemoveFeatures("GPG_Encrypted_FileTransfers:0\0\0");
+ pApi->RemoveFeatures("GPG_Key_Auto_Exchange:0\0\0");
+ }
+}
+
+bool isContactSecured(MCONTACT hContact)
+{
+ uint8_t gpg_enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ if (!gpg_enc) {
+ if (globals.debuglog)
+ globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact));
+ return false;
+ }
+ if (!db_mc_isMeta(hContact)) {
+ CMStringW key = g_plugin.getMStringW(hContact, "GPGPubKey");
+ if (key.IsEmpty()) {
+ if (globals.debuglog)
+ globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact));
+ return false;
+ }
+ }
+ if (globals.debuglog)
+ globals.debuglog << "encryption is turned on for " + toUTF8(Clist_GetContactDisplayName(hContact));
+ return true;
+}
+
+bool isContactHaveKey(MCONTACT hContact)
+{
+ ptrW key(g_plugin.getWStringA(hContact, "GPGPubKey"));
+ return (mir_wstrlen(key) > 0);
+}
+
+bool isGPGKeyExist()
+{
+ CMStringW id(g_plugin.getMStringW("KeyID"));
+ CMStringA key(g_plugin.getMStringA("GPGPubKey"));
+ return (!id.IsEmpty() && !key.IsEmpty());
+}
+
+bool isGPGValid()
+{
+ ptrW tmp(g_plugin.getWStringA("szGpgBinPath", L""));
+ bool gpg_exists = false, is_valid = true;
+ boost::filesystem::path p(tmp);
+
+ if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p))
+ gpg_exists = true;
+ else {
+ wchar_t path[MAX_PATH], mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+
+ //mir_realloc(path, (mir_wstrlen(path)+64)*sizeof(wchar_t));
+ wchar_t gpg_path[MAX_PATH];
+ mir_wstrcpy(gpg_path, mir_path);
+ mir_wstrcat(gpg_path, L"\\GnuPG\\gpg.exe");
+
+ p = boost::filesystem::path(gpg_path);
+ if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) {
+ gpg_exists = true;
+ mir_wstrcpy(path, L"GnuPG\\gpg.exe");
+ }
+ tmp = mir_wstrdup(path);
+ }
+
+ if (gpg_exists) {
+ g_plugin.setWString("szGpgBinPath", tmp);
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+ bool _gpg_valid = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = _gpg_valid; //TODO: check this
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 == -1)
+ is_valid = false;
+ }
+
+ return is_valid && gpg_exists;
+}
+
+#define NEWTSTR_MALLOC(A) (A==NULL)?NULL:mir_strcpy((char*)mir_alloc(sizeof(char)*(mir_strlen(A)+1)),A)
+
+const bool StriStr(const char *str, const char *substr)
+{
+ bool i = false;
+ char *str_up = NEWTSTR_MALLOC(str);
+ char *substr_up = NEWTSTR_MALLOC(substr);
+
+ CharUpperBuffA(str_up, (uint32_t)mir_strlen(str_up));
+ CharUpperBuffA(substr_up, (uint32_t)mir_strlen(substr_up));
+
+ if (strstr(str_up, substr_up))
+ i = true;
+
+ mir_free(str_up);
+ mir_free(substr_up);
+
+ return i;
+}
+
+bool IsOnline(MCONTACT hContact)
+{
+ if (g_plugin.getByte(hContact, "Status", 0) == ID_STATUS_OFFLINE)
+ return false;
+ return true;
+}
+
+string toUTF8(wstring str)
+{
+ string ustr;
+ try {
+ utf8::utf16to8(str.begin(), str.end(), back_inserter(ustr));
+ }
+ catch (const utf8::exception& e) {
+ if (globals.debuglog)
+ globals.debuglog << std::string("utf8cpp encoding exception: ") + (char*)e.what();
+ //TODO
+ }
+ return ustr;
+}
+
+wstring toUTF16(string str) //convert as much as possible
+{
+ wstring ustr;
+ string tmpstr;
+ try {
+ utf8::replace_invalid(str.begin(), str.end(), back_inserter(tmpstr));
+ utf8::utf8to16(tmpstr.begin(), tmpstr.end(), back_inserter(ustr));
+ }
+ catch (const utf8::exception& e) {
+ if (globals.debuglog)
+ globals.debuglog << std::string("utf8cpp decoding exception: ") + (char*)e.what();
+ //TODO
+ }
+ return ustr;
+}
+
+string get_random(int length)
+{
+ string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
+ string data;
+ boost::random_device rng;
+ boost::variate_generator<boost::random_device&, boost::uniform_int<>> gen(rng, boost::uniform_int<>(0, (int)chars.length() - 1));
+ for (int i = 0; i < length; ++i)
+ data += chars[gen()];
+ return data;
+}
+
+void send_encrypted_msgs_thread(void *param)
+{
+ MCONTACT hContact = (MCONTACT)(DWORD_PTR)param;
+ while (true) {
+ while (!isContactSecured(hContact))
+ Sleep(1000);
+
+ if (!globals.hcontact_data[hContact].msgs_to_send.empty()) {
+ Sleep(1000);
+ list<string>::iterator end = globals.hcontact_data[hContact].msgs_to_send.end();
+ extern std::list<HANDLE> sent_msgs;
+ for (list<string>::iterator p = globals.hcontact_data[hContact].msgs_to_send.begin(); p != end; ++p) {
+ sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)p->c_str()));
+ HistoryLog(hContact, p->c_str(), DBEF_SENT);
+ Sleep(1000);
+ }
+ globals.hcontact_data[hContact].msgs_to_send.clear();
+ return;
+ }
+ else
+ return;
+ }
+}
+
+void ExportGpGKeysFunc(int type)
+{
+ ptrW p(GetFilePath(L"Choose file to export keys", L"*", L"Any file", true));
+ if (!p || !p[0])
+ return;
+
+ std::ofstream file;
+ file.open(p, std::ios::trunc | std::ios::out);
+ if (!file.is_open())
+ return; //TODO: handle error
+
+ int exported_keys = 0;
+ if (!type || type == 2) {
+ for (auto &hContact : Contacts()) {
+ CMStringA key = g_plugin.getMStringA(hContact, "GPGPubKey");
+ if (key.IsEmpty())
+ continue;
+
+ ptrW wszLogin(Contact::GetInfo(CNF_UNIQUEID, 0, Proto_GetBaseAccountName(hContact))), wszContact(Contact::GetInfo(CNF_UNIQUEID, hContact));
+ if (wszLogin == nullptr || wszContact == nullptr)
+ continue;
+
+ std::string id = "Comment: login ";
+ id += T2Utf(wszLogin).get();
+ id += " contact_id ";
+ id += T2Utf(wszContact).get();
+ id += '\n';
+
+ int p1 = key.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ if (p1 == -1)
+ continue;
+ p1 += mir_strlen("-----BEGIN PGP PUBLIC KEY BLOCK-----");
+ p1++;
+ key.Insert(p1, id.c_str());
+ file << key;
+ file << std::endl;
+ exported_keys++;
+ }
+ }
+
+ if (type == 1 || type == 2) {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--export-secret-keys");
+ params.addParam(L"-a");
+ gpg_launcher(params); //TODO: handle errors
+
+ file << params.out.c_str();
+ file << std::endl;
+ }
+ if (file.is_open())
+ file.close();
+ wchar_t msg[512];
+ if (type == 2)
+ mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys and all private keys."), exported_keys);
+ else if (type == 1)
+ mir_snwprintf(msg, TranslateT("We have successfully exported all private keys."));
+ else if (!type)
+ mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys."), exported_keys);
+ MessageBox(nullptr, msg, TranslateT("Keys export result"), MB_OK);
+}
+
+INT_PTR ImportGpGKeys(WPARAM, LPARAM)
+{
+ ptrW p(GetFilePath(L"Choose file to import keys from", L"*", L"Any file"));
+ if (!p || !p[0])
+ return 1;
+
+ std::ifstream file;
+ file.open(p, std::ios::in);
+ if (!file.is_open())
+ return 1; //TODO: handle error
+
+ int processed_keys = 0, processed_private_keys = 0;
+
+ char line[256];
+ file.getline(line, 255);
+ if (!strstr(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && !strstr(line, "-----BEGIN PGP PRIVATE KEY BLOCK-----"))
+ return 1; //TODO: handle error
+
+ std::string key, login, contact_id;
+ key += line;
+ key += '\n';
+ while (file.is_open() && !file.eof()) {
+ file.getline(line, 255);
+ key += line;
+ key += '\n';
+ if (strstr(line, "-----END PGP PUBLIC KEY BLOCK-----")) {
+ std::string::size_type p1 = key.rfind("Comment: login "), p2 = 0;
+ if (p1 == std::string::npos)
+ {
+ key.clear();
+ continue; //TODO: warning about malformed file
+ }
+ p1 += mir_strlen("Comment: login ");
+ p2 = key.rfind(" contact_id ");
+ login = key.substr(p1, p2 - p1);
+ p2 += mir_strlen(" contact_id ");
+ p1 = key.find("\n", p2);
+ contact_id = key.substr(p2, p1 - p2);
+ p1 = key.rfind("Comment: login ");
+ p2 = key.find("\n", p1);
+ p2++;
+ key.erase(p1, p2 - p1);
+
+ PROTOACCOUNT *pFoundAcc = nullptr;
+ for (auto &pa : Accounts()) {
+ ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, 0, pa->szModuleName));
+ if (wszUniqueId == nullptr)
+ continue;
+
+ if (login == T2Utf(wszUniqueId).get()) {
+ pFoundAcc = pa;
+ break;
+ }
+ }
+
+ if (pFoundAcc == nullptr)
+ continue;
+
+ for (auto &hContact : Contacts(pFoundAcc->szModuleName)) {
+ ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, hContact, pFoundAcc->szModuleName));
+ if (wszUniqueId == nullptr)
+ continue;
+
+ if (contact_id != T2Utf(wszUniqueId).get())
+ continue;
+
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+
+ gpg_execution_params params;
+ {
+ wstring rand = toUTF16(get_random(10));
+ path += L"\\";
+ path += rand.c_str();
+ boost::filesystem::remove(path.c_str());
+ wfstream f(path, std::ios::out);
+ f << toUTF16(key).c_str();
+ f.close();
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(path.c_str());
+ }
+ if (!gpg_launcher(params))
+ break;
+ if (params.result == pxNotFound)
+ break;
+ if (params.result == pxSuccess)
+ processed_keys++;
+
+ string output(params.out);
+ if (output.find("already in secret keyring") != string::npos) {
+ MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK);
+ boost::filesystem::remove(path.c_str());
+ break;
+ }
+ char *tmp2;
+ string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key ");
+ string::size_type s2 = output.find(":", s);
+ tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ g_plugin.setString(hContact, "KeyID", tmp2);
+ mir_free(tmp2);
+ s = output.find("“", s2);
+ if (s == string::npos) {
+ s = output.find("\"", s2);
+ s += 1;
+ }
+ else
+ s += 3;
+ if ((s2 = output.find("(", s)) == string::npos)
+ s2 = output.find("<", s);
+ else if (s2 > output.find("<", s))
+ s2 = output.find("<", s);
+ if (s2 != string::npos) {
+ tmp2 = (char *)mir_alloc((output.substr(s, s2 - s - 1).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s, s2 - s - 1).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact) {
+ g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str());
+ }
+ mir_free(tmp2);
+ if ((s = output.find(")", s2)) == string::npos)
+ s = output.find(">", s2);
+ else if (s > output.find(">", s2))
+ s = output.find(">", s2);
+ s2++;
+ if (output[s] == ')') {
+ tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact)
+ g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str());
+ mir_free(tmp2);
+ s += 3;
+ s2 = output.find(">", s);
+ tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s, s2 - s).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact)
+ g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str());
+ mir_free(tmp2);
+ }
+ else {
+ tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char));
+ mir_strcpy(tmp2, output.substr(s2, s - s2).c_str());
+ mir_utf8decode(tmp2, nullptr);
+ if (hContact)
+ g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str());
+ mir_free(tmp2);
+ }
+ }
+ g_plugin.setByte(hContact, "GPGEncryption", 1);
+ g_plugin.setWString(hContact, "GPGPubKey", toUTF16(key).c_str());
+
+ boost::filesystem::remove(path.c_str());
+ break;
+ }
+ key.clear();
+ }
+ else if (strstr(line, "-----END PGP PRIVATE KEY BLOCK-----")) {
+ CMStringW tmp2 = g_plugin.getMStringW("szHomePath");
+ tmp2 += L"\\temporary_exported.asc";
+ boost::filesystem::remove(tmp2.c_str());
+
+ wfstream f(tmp2, std::ios::out);
+ f << toUTF16(key).c_str();
+ f.close();
+
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--import");
+ params.addParam(tmp2.c_str());
+ if (!gpg_launcher(params))
+ break;
+ if (params.result == pxNotFound)
+ break;
+ if (params.result == pxSuccess)
+ processed_private_keys++;
+ key.clear();
+ }
+ }
+ if (file.is_open())
+ file.close();
+ wchar_t msg[512];
+ if (processed_private_keys)
+ mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys and some private keys."), processed_keys);
+ else
+ mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys."), processed_keys);
+ MessageBox(nullptr, msg, TranslateT("Keys import result"), MB_OK);
+ return 0;
+}
+
+void SendErrorMessage(MCONTACT hContact)
+{
+ if (!g_plugin.bSendErrorMessages)
+ return;
+
+ uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0);
+ g_plugin.setByte(hContact, "GPGEncryption", 0);
+ ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message");
+ HistoryLog(hContact, "Error message sent", DBEF_SENT);
+ g_plugin.setByte(hContact, "GPGEncryption", enc);
+}
+
+void fix_line_term(std::string &s)
+{
+ if (s.empty())
+ return;
+
+ boost::algorithm::erase_all(s, "\r\r");
+
+ // unified line endings for unix & windows port
+ boost::algorithm::replace_all(s, "\r\n", "\n");
+ boost::algorithm::replace_all(s, "\n", "\r\n");
+}
+
+void strip_line_term(std::wstring &s)
+{
+ if (s.empty())
+ return;
+ boost::algorithm::erase_all(s, L"\r");
+ boost::algorithm::erase_all(s, L"\n");
+}
+
+void strip_line_term(std::string &s)
+{
+ if (s.empty())
+ return;
+ boost::algorithm::erase_all(s, "\r");
+ boost::algorithm::erase_all(s, "\n");
+}
+
+void strip_tags(std::string &str)
+{
+ if (str.empty())
+ return;
+ boost::algorithm::erase_all(str, globals.wszInopentag.c_str());
+ boost::algorithm::erase_all(str, globals.wszInclosetag.c_str());
+ boost::algorithm::erase_all(str, globals.wszOutopentag.c_str());
+ boost::algorithm::erase_all(str, globals.wszOutclosetag.c_str());
+}
+
+
+void ShowEncryptedFileMsgBox()
+{
+ CDlgEncryptedFileMsgBox *d = new CDlgEncryptedFileMsgBox;
+ d->DoModal();
+}
+
+void clean_temp_dir()
+{
+ using namespace boost::filesystem;
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+
+ CMStringW tmp = mir_path;
+ tmp += g_plugin.getMStringW("szHomePath");
+ tmp += L"\\tmp";
+ if (exists(tmp.c_str()) && is_directory(tmp.c_str())) {
+ boost::filesystem::path p(tmp);
+ for (directory_iterator i = directory_iterator(p), end = directory_iterator(); i != end; ++i) {
+ if (boost::filesystem::is_regular_file(i->path())) {
+ if ((i->path().filename().generic_string().length() == 10 && (i->path().filename().generic_string().find(".") == std::string::npos)) ||
+ i->path().extension() == ".sig" || i->path().extension() == ".asc" || i->path().extension() == ".status")
+ boost::filesystem::remove(i->path());
+ }
+ }
+ }
+}
+
+bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path)
+{
+ wstring tmp = gpg_bin_path;
+ if (!tmp.empty()) {
+ wchar_t mir_path[MAX_PATH];
+ PathToAbsoluteW(L"\\", mir_path);
+ SetCurrentDirectoryW(mir_path);
+ if (!boost::filesystem::exists(tmp)) {
+ MessageBox(nullptr, TranslateT("GPG binary does not exist.\nPlease choose another location"), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ }
+ else {
+ MessageBox(nullptr, TranslateT("Please choose GPG binary location"), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ {
+ bool bad_version = false;
+ g_plugin.setWString("szGpgBinPath", tmp.c_str());
+
+ gpg_execution_params params;
+ params.addParam(L"--version");
+ bool _gpg_valid = globals.gpg_valid;
+ globals.gpg_valid = true;
+ gpg_launcher(params);
+ globals.gpg_valid = _gpg_valid; //TODO: check this
+ g_plugin.delSetting("szGpgBinPath");
+ int p1 = params.out.Find("(GnuPG) ");
+ if (p1 != -1) {
+ p1 += mir_strlen("(GnuPG) ");
+ if (params.out[p1] != '1')
+ bad_version = true;
+ }
+ else {
+ bad_version = false;
+ MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ if (bad_version)
+ MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK);
+ }
+ tmp = gpg_home_path;
+ if (tmp[tmp.length()] == '\\')
+ tmp.erase(tmp.length());
+ if (tmp.empty()) {
+ MessageBox(nullptr, TranslateT("Please set keyring's home directory"), TranslateT("Warning"), MB_OK);
+ return false;
+ }
+ {
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+ uint32_t dwFileAttr = GetFileAttributes(path);
+ if (dwFileAttr != INVALID_FILE_ATTRIBUTES) {
+ dwFileAttr &= ~FILE_ATTRIBUTE_READONLY;
+ SetFileAttributes(path, dwFileAttr);
+ }
+ }
+ return true;
+}
+
+void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path)
+{
+ g_plugin.setWString("szGpgBinPath", gpg_bin_path);
+ g_plugin.setWString("szHomePath", gpg_home_path);
+}
+
+bool gpg_use_new_random_key(const char *account_name)
+{
+ CMStringW path = g_plugin.getMStringW("szHomePath");
+ path += L"\\new_key";
+
+ // generating key file
+ wfstream f(path.c_str(), std::ios::out);
+ if (!f.is_open()) {
+ MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK);
+ return false;
+ }
+ f << "Key-Type: RSA";
+ f << "\n";
+ f << "Key-Length: 4096";
+ f << "\n";
+ f << "Subkey-Type: RSA";
+ f << "\n";
+ f << "Name-Real: ";
+ f << get_random(6).c_str();
+ f << "\n";
+ f << "Name-Email: ";
+ f << get_random(5).c_str();
+ f << "@";
+ f << get_random(5).c_str();
+ f << ".";
+ f << get_random(3).c_str();
+ f << "\n";
+ f.close();
+
+ {
+ // gpg execution
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"--yes");
+ params.addParam(L"--gen-key");
+ params.addParam(path.c_str());
+ params.bNoOutput = true;
+ if (!gpg_launcher(params, boost::posix_time::minutes(10)))
+ return false;
+ if (params.result == pxNotFound)
+ return false;
+
+ boost::filesystem::remove(path.c_str());
+ string out(params.out);
+ int p1 = params.out.Find("key ");
+ if (p1 != -1)
+ path = ptrW(mir_utf8decodeW(params.out.Mid(p1 + 4, 8).c_str()));
+ else
+ path.Empty();
+ }
+
+ if (!path.IsEmpty()) {
+ gpg_execution_params params;
+ params.addParam(L"--batch");
+ params.addParam(L"-a");
+ params.addParam(L"--export");
+ params.addParam(path.c_str());
+ if (!gpg_launcher(params))
+ return false;
+
+ if (params.result == pxNotFound)
+ return false;
+
+ params.out.Remove('\r');
+
+ if (account_name == nullptr) {
+ g_plugin.setString("GPGPubKey", params.out.c_str());
+ g_plugin.setWString("KeyID", path.c_str());
+ }
+ else {
+ CMStringA acc_str = account_name;
+ g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str());
+ g_plugin.setWString(acc_str + "_KeyID", path.c_str());
+ }
+ }
+ return true;
+}
diff --git a/plugins/New_GPG/src/utilities.h b/plugins/New_GPG/src/utilities.h
index 1ac119660d..c6390b61a2 100644
--- a/plugins/New_GPG/src/utilities.h
+++ b/plugins/New_GPG/src/utilities.h
@@ -1,54 +1,54 @@
-// Copyright © 2010-22 sss
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef UTILITIES_H
-#define UTILITIES_H
-
-void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc);
-wchar_t *GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file = false);
-void GetFolderPath(wchar_t *WindowTittle);
-
-void setSrmmIcon(MCONTACT);
-
-void send_encrypted_msgs_thread(void*);
-
-int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data);
-bool isContactSecured(MCONTACT hContact);
-bool isContactHaveKey(MCONTACT hContact);
-bool isGPGKeyExist();
-bool isGPGValid();
-
-void ExportGpGKeysFunc(int type);
-void ImportKey(MCONTACT hContact, std::wstring new_key);
-
-void SendErrorMessage(MCONTACT hContact);
-
-const bool StriStr(const char *str, const char *substr);
-string toUTF8(wstring str);
-wstring toUTF16(string str);
-string get_random(int length);
-
-void HistoryLog(MCONTACT, const char *msg, uint32_t _time = 0, uint32_t _flags = 0);
-void fix_line_term(std::string &s);
-void strip_line_term(std::wstring &s);
-void strip_line_term(std::string &s);
-void strip_tags(std::string &s);
-void clean_temp_dir();
-bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path);
-void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path);
-bool gpg_use_new_random_key(const char *account_name);
-
-#endif
+// Copyright © 2010-23 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef UTILITIES_H
+#define UTILITIES_H
+
+void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc);
+wchar_t *GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file = false);
+void GetFolderPath(wchar_t *WindowTittle);
+
+void setSrmmIcon(MCONTACT);
+
+void send_encrypted_msgs_thread(void*);
+
+int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data);
+bool isContactSecured(MCONTACT hContact);
+bool isContactHaveKey(MCONTACT hContact);
+bool isGPGKeyExist();
+bool isGPGValid();
+
+void ExportGpGKeysFunc(int type);
+void ImportKey(MCONTACT hContact, std::wstring new_key);
+
+void SendErrorMessage(MCONTACT hContact);
+
+const bool StriStr(const char *str, const char *substr);
+string toUTF8(wstring str);
+wstring toUTF16(string str);
+string get_random(int length);
+
+void HistoryLog(MCONTACT, const char *msg, uint32_t _time = 0, uint32_t _flags = 0);
+void fix_line_term(std::string &s);
+void strip_line_term(std::wstring &s);
+void strip_line_term(std::string &s);
+void strip_tags(std::string &s);
+void clean_temp_dir();
+bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path);
+void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path);
+bool gpg_use_new_random_key(const char *account_name);
+
+#endif
diff --git a/plugins/New_GPG/src/version.h b/plugins/New_GPG/src/version.h
index b0abab9a96..7dff493628 100644
--- a/plugins/New_GPG/src/version.h
+++ b/plugins/New_GPG/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "New GPG encryption support plugin, based on code from old GPG plugin and SecureIM."
#define __AUTHOR "sss, Miranda NG Team"
#define __AUTHORWEB "https://miranda-ng.org/p/New_GPG"
-#define __COPYRIGHT "© 2010-22 sss, Miranda NG Team"
+#define __COPYRIGHT "© 2010-23 sss, Miranda NG Team"
diff --git a/plugins/NoHistory/src/stdafx.cxx b/plugins/NoHistory/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/NoHistory/src/stdafx.cxx
+++ b/plugins/NoHistory/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/plugins/NoHistory/src/version.h b/plugins/NoHistory/src/version.h
index c3911a546c..bb3b8226d9 100644
--- a/plugins/NoHistory/src/version.h
+++ b/plugins/NoHistory/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Prevent Miranda from storing any history."
#define __AUTHOR "Scott Ellis, NightFox"
#define __AUTHORWEB "https://miranda-ng.org/p/NoHistory"
-#define __COPYRIGHT "© 2005 Scott Ellis, 2010-17 NightFox, 2017-22 Miranda NG team"
+#define __COPYRIGHT "© 2005 Scott Ellis, 2010-17 NightFox, 2017-23 Miranda NG team"
diff --git a/plugins/NotesAndReminders/src/stdafx.cxx b/plugins/NotesAndReminders/src/stdafx.cxx
index 1ab0efee94..ebbde0ade1 100644
--- a/plugins/NotesAndReminders/src/stdafx.cxx
+++ b/plugins/NotesAndReminders/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/plugins/NotifyAnything/src/stdafx.cxx b/plugins/NotifyAnything/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/NotifyAnything/src/stdafx.cxx
+++ b/plugins/NotifyAnything/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/plugins/Nudge/src/stdafx.cxx b/plugins/Nudge/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Nudge/src/stdafx.cxx
+++ b/plugins/Nudge/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/plugins/OpenFolder/src/stdafx.cxx b/plugins/OpenFolder/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/OpenFolder/src/stdafx.cxx
+++ b/plugins/OpenFolder/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/plugins/PackUpdater/Src/Events.cpp b/plugins/PackUpdater/Src/Events.cpp
index 85e5a03578..0437123043 100644
--- a/plugins/PackUpdater/Src/Events.cpp
+++ b/plugins/PackUpdater/Src/Events.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Notifications.cpp b/plugins/PackUpdater/Src/Notifications.cpp
index ba2b4862b1..afd26da397 100644
--- a/plugins/PackUpdater/Src/Notifications.cpp
+++ b/plugins/PackUpdater/Src/Notifications.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Notifications.h b/plugins/PackUpdater/Src/Notifications.h
index 29c9eba2a8..6fc184f932 100644
--- a/plugins/PackUpdater/Src/Notifications.h
+++ b/plugins/PackUpdater/Src/Notifications.h
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Options.cpp b/plugins/PackUpdater/Src/Options.cpp
index c6206cbe8c..16be7b7ff8 100644
--- a/plugins/PackUpdater/Src/Options.cpp
+++ b/plugins/PackUpdater/Src/Options.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/PackUpdater.cpp b/plugins/PackUpdater/Src/PackUpdater.cpp
index eac404ccc8..9b66b38c81 100644
--- a/plugins/PackUpdater/Src/PackUpdater.cpp
+++ b/plugins/PackUpdater/Src/PackUpdater.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/Utils.cpp b/plugins/PackUpdater/Src/Utils.cpp
index e6adf16e9e..57a1c7db82 100644
--- a/plugins/PackUpdater/Src/Utils.cpp
+++ b/plugins/PackUpdater/Src/Utils.cpp
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/stdafx.cxx b/plugins/PackUpdater/Src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/PackUpdater/Src/stdafx.cxx
+++ b/plugins/PackUpdater/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/plugins/PackUpdater/Src/stdafx.h b/plugins/PackUpdater/Src/stdafx.h
index 4e78e8ac6c..5206917ca5 100644
--- a/plugins/PackUpdater/Src/stdafx.h
+++ b/plugins/PackUpdater/Src/stdafx.h
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2011-22 Mataes
+Copyright (C) 2011-23 Mataes
This is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
diff --git a/plugins/PackUpdater/Src/version.h b/plugins/PackUpdater/Src/version.h
index 3a9c0f1eb3..e809fb1677 100644
--- a/plugins/PackUpdater/Src/version.h
+++ b/plugins/PackUpdater/Src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Simple updater for Miranda NG premodified packs."
#define __AUTHOR "Mataes, ZERO_BiT"
#define __AUTHORWEB "https://miranda-ng.org/p/PackUpdater"
-#define __COPYRIGHT "© 2011-22 Mataes, 2007 ZERO_BiT"
+#define __COPYRIGHT "© 2011-23 Mataes, 2007 ZERO_BiT"
diff --git a/plugins/PasteIt/src/stdafx.cxx b/plugins/PasteIt/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/PasteIt/src/stdafx.cxx
+++ b/plugins/PasteIt/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/plugins/Ping/src/stdafx.cxx b/plugins/Ping/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Ping/src/stdafx.cxx
+++ b/plugins/Ping/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/plugins/PluginUpdater/pu_stub/src/stdafx.cxx b/plugins/PluginUpdater/pu_stub/src/stdafx.cxx
index d265a4c02e..8c570f6949 100644
--- a/plugins/PluginUpdater/pu_stub/src/stdafx.cxx
+++ b/plugins/PluginUpdater/pu_stub/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/>.
-*/
-
-#include "stdafx.h"
+/*
+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"
diff --git a/plugins/PluginUpdater/src/stdafx.cxx b/plugins/PluginUpdater/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/PluginUpdater/src/stdafx.cxx
+++ b/plugins/PluginUpdater/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/plugins/PluginUpdater/src/version.h b/plugins/PluginUpdater/src/version.h
index 3697cde49a..acfdd6cb78 100644
--- a/plugins/PluginUpdater/src/version.h
+++ b/plugins/PluginUpdater/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Installs and updates plugins and other Miranda NG components."
#define __AUTHOR "Mataes, George Hazan"
#define __AUTHORWEB "https://miranda-ng.org/p/PluginUpdater"
-#define __COPYRIGHT "© 2012-22 Mataes, George Hazan"
+#define __COPYRIGHT "© 2012-23 Mataes, George Hazan"
diff --git a/plugins/Popup/src/stdafx.cxx b/plugins/Popup/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Popup/src/stdafx.cxx
+++ b/plugins/Popup/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/plugins/ProfileManager/src/stdafx.cxx b/plugins/ProfileManager/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ProfileManager/src/stdafx.cxx
+++ b/plugins/ProfileManager/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/plugins/ProxySwitch/src/stdafx.cxx b/plugins/ProxySwitch/src/stdafx.cxx
index 1ab0efee94..ebbde0ade1 100644
--- a/plugins/ProxySwitch/src/stdafx.cxx
+++ b/plugins/ProxySwitch/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/plugins/ProxySwitch/src/version.h b/plugins/ProxySwitch/src/version.h
index 49deea3476..de274fc508 100644
--- a/plugins/ProxySwitch/src/version.h
+++ b/plugins/ProxySwitch/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Watches IP address changes, displays popups, and adjusts the proxy settings of Miranda, Internet Explorer and Firefox."
#define __AUTHOR "Petr Smejkal"
#define __AUTHORWEB "https://miranda-ng.org/p/ProxySwitch"
-#define __COPYRIGHT "© 2005 Petr Smejkal, 2018-22 Miranda NG team"
+#define __COPYRIGHT "© 2005 Petr Smejkal, 2018-23 Miranda NG team"
diff --git a/plugins/QuickContacts/src/dialog.cpp b/plugins/QuickContacts/src/dialog.cpp
index 8d2bb44d2e..f52297f4cb 100644
--- a/plugins/QuickContacts/src/dialog.cpp
+++ b/plugins/QuickContacts/src/dialog.cpp
@@ -1,853 +1,853 @@
-/*
-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"
-
-#define IDC_ICO 12344
-
-#define IDC_ENTER 2000 // Pseudo control to handle enter in the main window
-
-HIMAGELIST hIml;
-
-long main_dialog_open = 0;
-HWND hwndMain = nullptr;
-
-// array where the contacts are put into
-struct c_struct
-{
- wchar_t szname[120];
- wchar_t szgroup[50];
- MCONTACT hcontact;
- wchar_t proto[20];
-
- c_struct()
- {
- szname[0] = 0;
- szgroup[0] = 0;
- hcontact = 0;
- proto[0] = 0;
- }
-};
-
-LIST<c_struct> contacts(200);
-long max_proto_width;
-
-
-// Get the name the contact has in list
-// This was not made to be called by more than one thread!
-wchar_t tmp_list_name[120];
-
-wchar_t *GetListName(c_struct *cs)
-{
- if (opts.group_append && cs->szgroup[0] != '\0') {
- mir_snwprintf(tmp_list_name, L"%s (%s)", cs->szname, cs->szgroup);
- return tmp_list_name;
- }
- else {
- return cs->szname;
- }
-}
-
-
-int lstreq(wchar_t *a, wchar_t *b, size_t len = -1)
-{
- a = CharLower(wcsdup(a));
- b = CharLower(wcsdup(b));
- int ret;
- if (len > 0)
- ret = wcsncmp(a, b, len);
- else
- ret = mir_wstrcmp(a, b);
- free(a);
- free(b);
- return ret;
-}
-
-
-// simple sorting function to have
-// the contact array in alphabetical order
-void SortArray(void)
-{
- int loop, doop;
- c_struct *cs_temp;
-
- SortedList *sl = (SortedList *)&contacts;
- for (loop = 0; loop < contacts.getCount(); loop++) {
- for (doop = loop + 1; doop < contacts.getCount(); doop++) {
- int cmp = lstreq(contacts[loop]->szname, contacts[doop]->szname);
- if (cmp > 0) {
- cs_temp = contacts[loop];
- sl->items[loop] = contacts[doop];
- sl->items[doop] = cs_temp;
- }
- else if (cmp == 0) {
- if (lstreq(contacts[loop]->proto, contacts[doop]->proto) > 0) {
- cs_temp = contacts[loop];
- sl->items[loop] = contacts[doop];
- sl->items[doop] = cs_temp;
- }
- }
-
- }
- }
-}
-
-
-int GetStatus(MCONTACT hContact, char *proto = nullptr)
-{
- if (proto == nullptr)
- proto = Proto_GetBaseAccountName(hContact);
-
- if (proto == nullptr)
- return ID_STATUS_OFFLINE;
-
- return db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE);
-}
-
-
-void FreeContacts()
-{
- for (auto &it : contacts)
- delete it;
- contacts.destroy();
-}
-
-
-void LoadContacts(HWND hwndDlg, BOOL show_all)
-{
- BOOL metacontactsEnabled = db_mc_isEnabled();
-
- // Read last-sent-to contact from db and set handle as window-userdata
- HANDLE hlastsent = (HANDLE)g_plugin.getDword("LastSentTo", -1);
- SetWindowLongPtr(hwndMain, GWLP_USERDATA, (LONG_PTR)hlastsent);
-
- // enumerate all contacts and write them to the array
- // item data of listbox-strings is the array position
- FreeContacts();
-
- for (auto &hContact : Contacts()) {
- char *pszProto = Proto_GetBaseAccountName(hContact);
- if (pszProto == nullptr)
- continue;
-
- // Get meta
- MCONTACT hMeta = NULL;
- if (metacontactsEnabled) {
- if ((!show_all && opts.hide_subcontacts) || opts.group_append)
- hMeta = db_mc_getMeta(hContact);
- }
- else if (!mir_strcmp(META_PROTO, pszProto))
- continue;
-
- if (!show_all) {
- // Check if is offline and have to show
- if (GetStatus(hContact, pszProto) <= ID_STATUS_OFFLINE) {
- // See if has to show
- char setting[128];
- mir_snprintf(setting, "ShowOffline%s", pszProto);
-
- if (!g_plugin.getByte(setting, FALSE))
- continue;
-
- // Check if proto offline
- else if (opts.hide_from_offline_proto && Proto_GetStatus(pszProto) <= ID_STATUS_OFFLINE)
- continue;
-
- }
-
- // Check if is subcontact
- if (opts.hide_subcontacts && hMeta != NULL) {
- if (!opts.keep_subcontacts_from_offline)
- continue;
-
- if (GetStatus(hMeta, META_PROTO) > ID_STATUS_OFFLINE)
- continue;
-
- char setting[128];
- mir_snprintf(setting, "ShowOffline%s", META_PROTO);
- if (g_plugin.getByte(setting, FALSE))
- continue;
- }
- }
-
- // Add to list
-
- // Get group
- c_struct *contact = new c_struct();
-
- if (opts.group_append) {
- ptrW wszGroup(Clist_GetGroup(hMeta == NULL ? hContact : hMeta));
- if (wszGroup)
- wcsncpy_s(contact->szgroup, wszGroup, _TRUNCATE);
- }
-
- // Make contact name
- wchar_t *tmp = (wchar_t *)Clist_GetContactDisplayName(hContact);
- mir_wstrncpy(contact->szname, tmp, _countof(contact->szname));
-
- PROTOACCOUNT *acc = Proto_GetAccount(pszProto);
- if (acc != nullptr)
- mir_wstrncpy(contact->proto, acc->tszAccountName, _countof(contact->proto));
-
- contact->hcontact = hContact;
- contacts.insert(contact);
- }
-
- SortArray();
-
- SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_RESETCONTENT, 0, 0);
- for (int loop = 0; loop < contacts.getCount(); loop++)
- SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_ADDSTRING, 0, (LPARAM)GetListName(contacts[loop])), loop);
-}
-
-
-// Enable buttons for the selected contact
-void EnableButtons(HWND hwndDlg, MCONTACT hContact)
-{
- if (hContact == NULL) {
- EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), FALSE);
-
- SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, 0, 0);
- }
- else {
- // Is a meta?
- MCONTACT hSub = db_mc_getMostOnline(hContact);
- if (hSub != NULL)
- hContact = hSub;
-
- // Get caps
- INT_PTR caps = 0;
-
- char *pszProto = Proto_GetBaseAccountName(hContact);
- if (pszProto != nullptr)
- caps = CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0);
-
- EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), caps & PF1_IMSEND ? TRUE : FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), caps & PF1_FILESEND ? TRUE : FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), TRUE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), TRUE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), TRUE);
-
- HICON ico = ImageList_GetIcon(hIml, Clist_GetContactIcon(hContact), ILD_IMAGE);
- SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, (WPARAM)ico, 0);
- }
-}
-
-
-// check if the char(s) entered appears in a contacts name
-int CheckText(HWND hdlg, wchar_t *sztext, BOOL only_enable = FALSE)
-{
- EnableButtons(hwndMain, NULL);
-
- if (sztext == nullptr || sztext[0] == '\0')
- return 0;
-
- size_t len = mir_wstrlen(sztext);
-
- if (only_enable) {
- for (auto &it : contacts) {
- if (lstreq(sztext, it->szname) == 0 || lstreq(sztext, GetListName(it)) == 0) {
- EnableButtons(hwndMain, it->hcontact);
- return 0;
- }
- }
- }
- else {
- for (auto &it : contacts) {
- if (lstreq(sztext, GetListName(it), len) == 0) {
- SetWindowText(hdlg, GetListName(it));
- SendMessage(hdlg, EM_SETSEL, (WPARAM)len, (LPARAM)-1);
- EnableButtons(hwndMain, it->hcontact);
- return 0;
- }
- }
- }
-
- EnableButtons(hwndMain, NULL);
- return 0;
-}
-
-MCONTACT GetSelectedContact(HWND hwndDlg)
-{
- // First try selection
- int sel = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
-
- if (sel != CB_ERR) {
- int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETITEMDATA, sel, 0);
- if (pos != CB_ERR)
- return contacts[pos]->hcontact;
- }
-
- // Now try the name
- wchar_t cname[120] = L"";
-
- GetDlgItemText(hwndDlg, IDC_USERNAME, cname, _countof(cname));
-
- for (auto &it : contacts)
- if (!mir_wstrcmpi(cname, GetListName(it)))
- return it->hcontact;
-
- return NULL;
-}
-
-// get array position from handle
-int GetItemPos(MCONTACT hcontact)
-{
- for (auto &it : contacts)
- if (hcontact == it->hcontact)
- return contacts.indexOf(&it);
-
- return -1;
-}
-
-
-// callback function for edit-box of the listbox
-// without this the autofill function isn't possible
-// this was done like ie does it..as far as spy++ could tell ;)
-LRESULT CALLBACK EditProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
-{
- switch (msg) {
- case WM_CHAR:
- {
- if (wparam < 32 && wparam != VK_BACK)
- break;
-
- wchar_t sztext[120] = L"";
- uint32_t start;
- uint32_t end;
-
- int ret = SendMessage(hdlg, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
-
- GetWindowText(hdlg, sztext, _countof(sztext));
-
- BOOL at_end = (mir_wstrlen(sztext) == (int)end);
-
- if (ret != -1) {
- if (wparam == VK_BACK) {
- if (start > 0)
- SendMessage(hdlg, EM_SETSEL, (WPARAM)start - 1, (LPARAM)end);
-
- sztext[0] = 0;
- }
- else {
- sztext[0] = wparam;
- sztext[1] = 0;
- }
-
- SendMessage(hdlg, EM_REPLACESEL, 0, (LPARAM)sztext);
- GetWindowText(hdlg, sztext, _countof(sztext));
- }
-
- CheckText(hdlg, sztext, !at_end);
-
- return 1;
- }
- case WM_KEYUP:
- {
- wchar_t sztext[120] = L"";
-
- if (wparam == VK_RETURN) {
- switch (SendMessage(GetParent(hdlg), CB_GETDROPPEDSTATE, 0, 0)) {
- case FALSE:
- SendMessage(GetParent(GetParent(hdlg)), WM_COMMAND, MAKEWPARAM(IDC_ENTER, STN_CLICKED), 0);
- break;
-
- case TRUE:
- SendMessage(GetParent(hdlg), CB_SHOWDROPDOWN, FALSE, 0);
- break;
- }
- }
- else if (wparam == VK_DELETE) {
- GetWindowText(hdlg, sztext, _countof(sztext));
- CheckText(hdlg, sztext, TRUE);
- }
-
- return 0;
- }
-
- case WM_GETDLGCODE:
- return DLGC_WANTCHARS | DLGC_WANTARROWS;
- }
-
- return mir_callNextSubclass(hdlg, EditProc, msg, wparam, lparam);
-}
-
-HACCEL hAcct;
-HHOOK hHook;
-
-// This function filters the message queue and translates
-// the keyboard accelerators
-LRESULT CALLBACK HookProc(int code, WPARAM, LPARAM lparam)
-{
- if (code != MSGF_DIALOGBOX)
- return 0;
-
- MSG *msg = (MSG*)lparam;
-
- int action = Hotkey_Check(msg, "Quick Contacts");
- if (action != 0) {
- SendMessage(hwndMain, WM_COMMAND, action, 0);
- return 1;
- }
-
- if (msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE) {
- switch (SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_GETDROPPEDSTATE, 0, 0)) {
- case FALSE:
- SendMessage(hwndMain, WM_CLOSE, 0, 0);
- break;
-
- case TRUE:
- SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_SHOWDROPDOWN, FALSE, 0);
- break;
- }
- }
-
- return 0;
-}
-
-BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
-{
- BOOL ret;
-
- POINT pt;
-
- pt.x = lpRect->left;
- pt.y = lpRect->top;
-
- ret = ScreenToClient(hWnd, &pt);
-
- if (!ret) return ret;
-
- lpRect->left = pt.x;
- lpRect->top = pt.y;
-
-
- pt.x = lpRect->right;
- pt.y = lpRect->bottom;
-
- ret = ScreenToClient(hWnd, &pt);
-
- lpRect->right = pt.x;
- lpRect->bottom = pt.y;
-
- return ret;
-}
-
-
-BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint)
-{
- return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint);
-}
-
-
-static void FillButton(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key, HICON icon)
-{
- wchar_t tmp[256];
- wchar_t *full = tmp;
-
- if (key == nullptr)
- full = TranslateW(name);
- else
- mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key);
-
- SendDlgItemMessage(hwndDlg, dlgItem, BUTTONSETASFLATBTN, 0, 0);
- SendDlgItemMessage(hwndDlg, dlgItem, BUTTONADDTOOLTIP, (LPARAM)full, BATF_UNICODE);
- SendDlgItemMessage(hwndDlg, dlgItem, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon);
-}
-
-
-static void FillCheckbox(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key)
-{
- wchar_t tmp[256];
- wchar_t *full = tmp;
-
- if (key == nullptr)
- full = TranslateW(name);
- else
- mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key);
-
- SetDlgItemText(hwndDlg, dlgItem, full);
-}
-
-
-static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- {
- RECT rc;
- GetWindowRect(GetDlgItem(hwndDlg, IDC_USERNAME), &rc);
- ScreenToClient(hwndDlg, &rc);
-
- CreateWindow(L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
- rc.left - 20, rc.top + (rc.bottom - rc.top - 16) / 2, 16, 16, hwndDlg, (HMENU)IDC_ICO,
- g_plugin.getInst(), nullptr);
-
- hHook = SetWindowsHookEx(WH_MSGFILTER, HookProc, nullptr, GetCurrentThreadId());
-
- // Combo
- SendDlgItemMessage(hwndDlg, IDC_USERNAME, EM_LIMITTEXT, (WPARAM)119, 0);
- mir_subclassWindow(GetWindow(GetDlgItem(hwndDlg, IDC_USERNAME), GW_CHILD), EditProc);
-
- // Buttons
- FillCheckbox(hwndDlg, IDC_SHOW_ALL_CONTACTS, LPGENW("Show all contacts"), NULL);
- FillButton(hwndDlg, IDC_MESSAGE, LPGENW("Send message"), nullptr, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
- FillButton(hwndDlg, IDC_FILE, LPGENW("Send file"), NULL, Skin_LoadIcon(SKINICON_EVENT_FILE));
- FillButton(hwndDlg, IDC_USERINFO, LPGENW("Open user info"), NULL, Skin_LoadIcon(SKINICON_OTHER_USERDETAILS));
- FillButton(hwndDlg, IDC_HISTORY, LPGENW("Open history"), NULL, Skin_LoadIcon(SKINICON_OTHER_HISTORY));
- FillButton(hwndDlg, IDC_MENU, LPGENW("Open contact menu"), NULL, Skin_LoadIcon(SKINICON_OTHER_DOWNARROW));
-
- SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETEXTENDEDUI, TRUE, 0);
-
- Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, MODULENAME, "window");
-
- LoadContacts(hwndDlg, FALSE);
-
- EnableButtons(hwndDlg, NULL);
- if (g_plugin.getByte("EnableLastSentTo", 0)) {
- int pos = GetItemPos(g_plugin.getDword("LastSentTo", -1));
- if (pos != -1) {
- SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETCURSEL, (WPARAM)pos, 0);
- EnableButtons(hwndDlg, contacts[pos]->hcontact);
- }
- }
-
- SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
- }
- return TRUE;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDC_USERNAME:
- if (HIWORD(wParam) == CBN_SELCHANGE) {
- int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
- EnableButtons(hwndDlg, pos < contacts.getCount() ? contacts[pos]->hcontact : NULL);
- }
- break;
-
- case IDC_ENTER:
- {
- MCONTACT hContact = GetSelectedContact(hwndDlg);
- if (hContact == NULL)
- break;
-
- Clist_ContactDoubleClicked(hContact);
-
- g_plugin.setDword("LastSentTo", hContact);
- SendMessage(hwndDlg, WM_CLOSE, 0, 0);
- }
- break;
- case IDC_MESSAGE:
- {
- MCONTACT hContact = GetSelectedContact(hwndDlg);
- if (hContact == NULL) {
- SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
- SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
- break;
- }
-
- // Is button enabled?
- if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MESSAGE)))
- break;
-
- CallService(MS_MSG_SENDMESSAGEW, hContact, 0);
-
- g_plugin.setDword("LastSentTo", hContact);
- SendMessage(hwndDlg, WM_CLOSE, 0, 0);
- break;
- }
-
- case HOTKEY_FILE:
- case IDC_FILE:
- {
- MCONTACT hContact = GetSelectedContact(hwndDlg);
- if (hContact == NULL) {
- SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
- SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
- break;
- }
-
- // Is button enabled?
- if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FILE)))
- break;
-
- CallService(MS_FILE_SENDFILE, hContact, 0);
-
- g_plugin.setDword("LastSentTo", hContact);
- SendMessage(hwndDlg, WM_CLOSE, 0, 0);
- }
- break;
-
- case HOTKEY_INFO:
- case IDC_USERINFO:
- {
- MCONTACT hContact = GetSelectedContact(hwndDlg);
- if (hContact == NULL) {
- SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
- SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
- break;
- }
-
- // Is button enabled?
- if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_USERINFO)))
- break;
-
- CallService(MS_USERINFO_SHOWDIALOG, hContact, 0);
-
- g_plugin.setDword("LastSentTo", hContact);
- SendMessage(hwndDlg, WM_CLOSE, 0, 0);
- }
- break;
-
- case HOTKEY_HISTORY:
- case IDC_HISTORY:
- {
- MCONTACT hContact = GetSelectedContact(hwndDlg);
- if (hContact == NULL) {
- SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
- SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
- break;
- }
-
- // Is button enabled?
- if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_HISTORY)))
- break;
-
- CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0);
-
- g_plugin.setDword("LastSentTo", hContact);
- SendMessage(hwndDlg, WM_CLOSE, 0, 0);
- }
- break;
-
- case HOTKEY_MENU:
- case IDC_MENU:
- {
- MCONTACT hContact = GetSelectedContact(hwndDlg);
- if (hContact == NULL) {
- SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
- SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
- break;
- }
-
- // Is button enabled?
- if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MENU)))
- break;
-
- RECT rc;
- GetWindowRect(GetDlgItem(hwndDlg, IDC_MENU), &rc);
- HMENU hMenu = Menu_BuildContactMenu(hContact);
- int ret = TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, nullptr);
- DestroyMenu(hMenu);
-
- if (ret) {
- SendMessage(hwndDlg, WM_CLOSE, 0, 0);
- Clist_MenuProcessCommand(LOWORD(ret), MPCF_CONTACTMENU, hContact);
- }
-
- g_plugin.setDword("LastSentTo", (uint32_t)hContact);
- }
- break;
-
- case HOTKEY_ALL_CONTACTS:
- case IDC_SHOW_ALL_CONTACTS:
- {
- // Get old text
- HWND hEdit = GetWindow(GetWindow(hwndDlg, GW_CHILD), GW_CHILD);
- wchar_t sztext[120] = L"";
-
- if (SendMessage(hEdit, EM_GETSEL, 0, 0) != -1)
- SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)L"");
-
- GetWindowText(hEdit, sztext, _countof(sztext));
-
- // Fill combo
- BOOL all = IsDlgButtonChecked(hwndDlg, IDC_SHOW_ALL_CONTACTS);
-
- if (LOWORD(wParam) == HOTKEY_ALL_CONTACTS) {
- // Toggle checkbox
- all = !all;
- CheckDlgButton(hwndDlg, IDC_SHOW_ALL_CONTACTS, all ? BST_CHECKED : BST_UNCHECKED);
- }
-
- LoadContacts(hwndDlg, all);
-
- // Return selection
- CheckText(hEdit, sztext);
- }
- }
- break;
-
- case WM_CLOSE:
- Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "window");
- DestroyWindow(hwndDlg);
- break;
-
- case WM_DESTROY:
- UnhookWindowsHookEx(hHook);
- hwndMain = nullptr;
- FreeContacts();
- InterlockedExchange(&main_dialog_open, 0);
- break;
-
- case WM_DRAWITEM:
- {
- // add icons and protocol to listbox
- LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
-
- // Handle contact menu
- if (lpdis->CtlID != IDC_USERNAME) {
- if (lpdis->CtlType == ODT_MENU)
- return Menu_DrawItem(lParam);
- break;
- }
-
- // Handle combo
- if (lpdis->itemID == -1)
- break;
-
- TEXTMETRIC tm;
- int icon_width = 0, icon_height = 0;
- RECT rc;
-
- GetTextMetrics(lpdis->hDC, &tm);
- ImageList_GetIconSize(hIml, &icon_width, &icon_height);
-
- COLORREF clrfore = SetTextColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
- COLORREF clrback = SetBkColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
-
- FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
-
- // Draw icon
- rc.left = lpdis->rcItem.left + 5;
- rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - icon_height) / 2;
- ImageList_Draw(hIml, Clist_GetContactIcon(contacts[lpdis->itemData]->hcontact), lpdis->hDC, rc.left, rc.top, ILD_NORMAL);
-
- // Make rect for text
- rc.left += icon_width + 5;
- rc.right = lpdis->rcItem.right - 1;
- rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
- rc.bottom = rc.top + tm.tmHeight;
-
- // Draw Protocol
- if (opts.num_protos > 1) {
- if (max_proto_width == 0) {
- // Has to be done, else the DC isnt the right one
- // Dont ask me why
- for (auto &it : contacts) {
- RECT rcc = { 0, 0, 0x7FFF, 0x7FFF };
- DrawText(lpdis->hDC, it->proto, -1, &rcc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT);
- max_proto_width = max(max_proto_width, rcc.right - rcc.left);
- }
-
- // Fix max_proto_width
- if (opts.group_append && opts.group_column)
- max_proto_width = min(max_proto_width, (rc.right - rc.left) / 5);
- else if (opts.group_append)
- max_proto_width = min(max_proto_width, (rc.right - rc.left) / 4);
- else
- max_proto_width = min(max_proto_width, (rc.right - rc.left) / 3);
- }
-
- RECT rc_tmp = rc;
- rc_tmp.left = rc_tmp.right - max_proto_width;
- DrawText(lpdis->hDC, contacts[lpdis->itemData]->proto, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
- rc.right = rc_tmp.left - 5;
- }
-
- // Draw group
- if (opts.group_append && opts.group_column) {
- RECT rc_tmp = rc;
-
- if (opts.group_column_left) {
- rc_tmp.right = rc_tmp.left + (rc.right - rc.left) / 3;
- rc.left = rc_tmp.right + 5;
- }
- else {
- rc_tmp.left = rc_tmp.right - (rc.right - rc.left) / 3;
- rc.right = rc_tmp.left - 5;
- }
-
- DrawText(lpdis->hDC, contacts[lpdis->itemData]->szgroup, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
- }
-
- // Draw text
- wchar_t *name;
- if (opts.group_append && !opts.group_column)
- name = GetListName(contacts[lpdis->itemData]);
- else
- name = contacts[lpdis->itemData]->szname;
-
- DrawText(lpdis->hDC, name, -1, &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
-
- // Restore old colors
- SetTextColor(lpdis->hDC, clrfore);
- SetBkColor(lpdis->hDC, clrback);
- }
- return TRUE;
-
- case WM_MEASUREITEM:
- {
- LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
-
- // Handle contact menu
- if (lpmis->CtlID != IDC_USERNAME) {
- if (lpmis->CtlType == ODT_MENU)
- return Menu_MeasureItem(lParam);
- break;
- }
-
- // Handle combo
-
- TEXTMETRIC tm;
- int icon_width = 0, icon_height = 0;
-
- GetTextMetrics(GetDC(hwndDlg), &tm);
- ImageList_GetIconSize(hIml, &icon_width, &icon_height);
-
- lpmis->itemHeight = max(icon_height, tm.tmHeight);
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-// Show the main dialog
-INT_PTR ShowDialog(WPARAM, LPARAM)
-{
- // Get the icons for the listbox
- hIml = Clist_GetImageList();
-
- if (!main_dialog_open) {
- InterlockedExchange(&main_dialog_open, 1);
-
- hwndMain = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_MAIN), nullptr, MainDlgProc);
- }
-
- // Show it
- SetForegroundWindow(hwndMain);
- SetFocus(hwndMain);
- ShowWindow(hwndMain, SW_SHOW);
- return 0;
-}
+/*
+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"
+
+#define IDC_ICO 12344
+
+#define IDC_ENTER 2000 // Pseudo control to handle enter in the main window
+
+HIMAGELIST hIml;
+
+long main_dialog_open = 0;
+HWND hwndMain = nullptr;
+
+// array where the contacts are put into
+struct c_struct
+{
+ wchar_t szname[120];
+ wchar_t szgroup[50];
+ MCONTACT hcontact;
+ wchar_t proto[20];
+
+ c_struct()
+ {
+ szname[0] = 0;
+ szgroup[0] = 0;
+ hcontact = 0;
+ proto[0] = 0;
+ }
+};
+
+LIST<c_struct> contacts(200);
+long max_proto_width;
+
+
+// Get the name the contact has in list
+// This was not made to be called by more than one thread!
+wchar_t tmp_list_name[120];
+
+wchar_t *GetListName(c_struct *cs)
+{
+ if (opts.group_append && cs->szgroup[0] != '\0') {
+ mir_snwprintf(tmp_list_name, L"%s (%s)", cs->szname, cs->szgroup);
+ return tmp_list_name;
+ }
+ else {
+ return cs->szname;
+ }
+}
+
+
+int lstreq(wchar_t *a, wchar_t *b, size_t len = -1)
+{
+ a = CharLower(wcsdup(a));
+ b = CharLower(wcsdup(b));
+ int ret;
+ if (len > 0)
+ ret = wcsncmp(a, b, len);
+ else
+ ret = mir_wstrcmp(a, b);
+ free(a);
+ free(b);
+ return ret;
+}
+
+
+// simple sorting function to have
+// the contact array in alphabetical order
+void SortArray(void)
+{
+ int loop, doop;
+ c_struct *cs_temp;
+
+ SortedList *sl = (SortedList *)&contacts;
+ for (loop = 0; loop < contacts.getCount(); loop++) {
+ for (doop = loop + 1; doop < contacts.getCount(); doop++) {
+ int cmp = lstreq(contacts[loop]->szname, contacts[doop]->szname);
+ if (cmp > 0) {
+ cs_temp = contacts[loop];
+ sl->items[loop] = contacts[doop];
+ sl->items[doop] = cs_temp;
+ }
+ else if (cmp == 0) {
+ if (lstreq(contacts[loop]->proto, contacts[doop]->proto) > 0) {
+ cs_temp = contacts[loop];
+ sl->items[loop] = contacts[doop];
+ sl->items[doop] = cs_temp;
+ }
+ }
+
+ }
+ }
+}
+
+
+int GetStatus(MCONTACT hContact, char *proto = nullptr)
+{
+ if (proto == nullptr)
+ proto = Proto_GetBaseAccountName(hContact);
+
+ if (proto == nullptr)
+ return ID_STATUS_OFFLINE;
+
+ return db_get_w(hContact, proto, "Status", ID_STATUS_OFFLINE);
+}
+
+
+void FreeContacts()
+{
+ for (auto &it : contacts)
+ delete it;
+ contacts.destroy();
+}
+
+
+void LoadContacts(HWND hwndDlg, BOOL show_all)
+{
+ BOOL metacontactsEnabled = db_mc_isEnabled();
+
+ // Read last-sent-to contact from db and set handle as window-userdata
+ HANDLE hlastsent = (HANDLE)g_plugin.getDword("LastSentTo", -1);
+ SetWindowLongPtr(hwndMain, GWLP_USERDATA, (LONG_PTR)hlastsent);
+
+ // enumerate all contacts and write them to the array
+ // item data of listbox-strings is the array position
+ FreeContacts();
+
+ for (auto &hContact : Contacts()) {
+ char *pszProto = Proto_GetBaseAccountName(hContact);
+ if (pszProto == nullptr)
+ continue;
+
+ // Get meta
+ MCONTACT hMeta = NULL;
+ if (metacontactsEnabled) {
+ if ((!show_all && opts.hide_subcontacts) || opts.group_append)
+ hMeta = db_mc_getMeta(hContact);
+ }
+ else if (!mir_strcmp(META_PROTO, pszProto))
+ continue;
+
+ if (!show_all) {
+ // Check if is offline and have to show
+ if (GetStatus(hContact, pszProto) <= ID_STATUS_OFFLINE) {
+ // See if has to show
+ char setting[128];
+ mir_snprintf(setting, "ShowOffline%s", pszProto);
+
+ if (!g_plugin.getByte(setting, FALSE))
+ continue;
+
+ // Check if proto offline
+ else if (opts.hide_from_offline_proto && Proto_GetStatus(pszProto) <= ID_STATUS_OFFLINE)
+ continue;
+
+ }
+
+ // Check if is subcontact
+ if (opts.hide_subcontacts && hMeta != NULL) {
+ if (!opts.keep_subcontacts_from_offline)
+ continue;
+
+ if (GetStatus(hMeta, META_PROTO) > ID_STATUS_OFFLINE)
+ continue;
+
+ char setting[128];
+ mir_snprintf(setting, "ShowOffline%s", META_PROTO);
+ if (g_plugin.getByte(setting, FALSE))
+ continue;
+ }
+ }
+
+ // Add to list
+
+ // Get group
+ c_struct *contact = new c_struct();
+
+ if (opts.group_append) {
+ ptrW wszGroup(Clist_GetGroup(hMeta == NULL ? hContact : hMeta));
+ if (wszGroup)
+ wcsncpy_s(contact->szgroup, wszGroup, _TRUNCATE);
+ }
+
+ // Make contact name
+ wchar_t *tmp = (wchar_t *)Clist_GetContactDisplayName(hContact);
+ mir_wstrncpy(contact->szname, tmp, _countof(contact->szname));
+
+ PROTOACCOUNT *acc = Proto_GetAccount(pszProto);
+ if (acc != nullptr)
+ mir_wstrncpy(contact->proto, acc->tszAccountName, _countof(contact->proto));
+
+ contact->hcontact = hContact;
+ contacts.insert(contact);
+ }
+
+ SortArray();
+
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_RESETCONTENT, 0, 0);
+ for (int loop = 0; loop < contacts.getCount(); loop++)
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_ADDSTRING, 0, (LPARAM)GetListName(contacts[loop])), loop);
+}
+
+
+// Enable buttons for the selected contact
+void EnableButtons(HWND hwndDlg, MCONTACT hContact)
+{
+ if (hContact == NULL) {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), FALSE);
+
+ SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, 0, 0);
+ }
+ else {
+ // Is a meta?
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if (hSub != NULL)
+ hContact = hSub;
+
+ // Get caps
+ INT_PTR caps = 0;
+
+ char *pszProto = Proto_GetBaseAccountName(hContact);
+ if (pszProto != nullptr)
+ caps = CallProtoService(pszProto, PS_GETCAPS, PFLAGNUM_1, 0);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MESSAGE), caps & PF1_IMSEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), caps & PF1_FILESEND ? TRUE : FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_USERINFO), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HISTORY), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_MENU), TRUE);
+
+ HICON ico = ImageList_GetIcon(hIml, Clist_GetContactIcon(hContact), ILD_IMAGE);
+ SendDlgItemMessage(hwndDlg, IDC_ICO, STM_SETICON, (WPARAM)ico, 0);
+ }
+}
+
+
+// check if the char(s) entered appears in a contacts name
+int CheckText(HWND hdlg, wchar_t *sztext, BOOL only_enable = FALSE)
+{
+ EnableButtons(hwndMain, NULL);
+
+ if (sztext == nullptr || sztext[0] == '\0')
+ return 0;
+
+ size_t len = mir_wstrlen(sztext);
+
+ if (only_enable) {
+ for (auto &it : contacts) {
+ if (lstreq(sztext, it->szname) == 0 || lstreq(sztext, GetListName(it)) == 0) {
+ EnableButtons(hwndMain, it->hcontact);
+ return 0;
+ }
+ }
+ }
+ else {
+ for (auto &it : contacts) {
+ if (lstreq(sztext, GetListName(it), len) == 0) {
+ SetWindowText(hdlg, GetListName(it));
+ SendMessage(hdlg, EM_SETSEL, (WPARAM)len, (LPARAM)-1);
+ EnableButtons(hwndMain, it->hcontact);
+ return 0;
+ }
+ }
+ }
+
+ EnableButtons(hwndMain, NULL);
+ return 0;
+}
+
+MCONTACT GetSelectedContact(HWND hwndDlg)
+{
+ // First try selection
+ int sel = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
+
+ if (sel != CB_ERR) {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETITEMDATA, sel, 0);
+ if (pos != CB_ERR)
+ return contacts[pos]->hcontact;
+ }
+
+ // Now try the name
+ wchar_t cname[120] = L"";
+
+ GetDlgItemText(hwndDlg, IDC_USERNAME, cname, _countof(cname));
+
+ for (auto &it : contacts)
+ if (!mir_wstrcmpi(cname, GetListName(it)))
+ return it->hcontact;
+
+ return NULL;
+}
+
+// get array position from handle
+int GetItemPos(MCONTACT hcontact)
+{
+ for (auto &it : contacts)
+ if (hcontact == it->hcontact)
+ return contacts.indexOf(&it);
+
+ return -1;
+}
+
+
+// callback function for edit-box of the listbox
+// without this the autofill function isn't possible
+// this was done like ie does it..as far as spy++ could tell ;)
+LRESULT CALLBACK EditProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg) {
+ case WM_CHAR:
+ {
+ if (wparam < 32 && wparam != VK_BACK)
+ break;
+
+ wchar_t sztext[120] = L"";
+ uint32_t start;
+ uint32_t end;
+
+ int ret = SendMessage(hdlg, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
+
+ GetWindowText(hdlg, sztext, _countof(sztext));
+
+ BOOL at_end = (mir_wstrlen(sztext) == (int)end);
+
+ if (ret != -1) {
+ if (wparam == VK_BACK) {
+ if (start > 0)
+ SendMessage(hdlg, EM_SETSEL, (WPARAM)start - 1, (LPARAM)end);
+
+ sztext[0] = 0;
+ }
+ else {
+ sztext[0] = wparam;
+ sztext[1] = 0;
+ }
+
+ SendMessage(hdlg, EM_REPLACESEL, 0, (LPARAM)sztext);
+ GetWindowText(hdlg, sztext, _countof(sztext));
+ }
+
+ CheckText(hdlg, sztext, !at_end);
+
+ return 1;
+ }
+ case WM_KEYUP:
+ {
+ wchar_t sztext[120] = L"";
+
+ if (wparam == VK_RETURN) {
+ switch (SendMessage(GetParent(hdlg), CB_GETDROPPEDSTATE, 0, 0)) {
+ case FALSE:
+ SendMessage(GetParent(GetParent(hdlg)), WM_COMMAND, MAKEWPARAM(IDC_ENTER, STN_CLICKED), 0);
+ break;
+
+ case TRUE:
+ SendMessage(GetParent(hdlg), CB_SHOWDROPDOWN, FALSE, 0);
+ break;
+ }
+ }
+ else if (wparam == VK_DELETE) {
+ GetWindowText(hdlg, sztext, _countof(sztext));
+ CheckText(hdlg, sztext, TRUE);
+ }
+
+ return 0;
+ }
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTCHARS | DLGC_WANTARROWS;
+ }
+
+ return mir_callNextSubclass(hdlg, EditProc, msg, wparam, lparam);
+}
+
+HACCEL hAcct;
+HHOOK hHook;
+
+// This function filters the message queue and translates
+// the keyboard accelerators
+LRESULT CALLBACK HookProc(int code, WPARAM, LPARAM lparam)
+{
+ if (code != MSGF_DIALOGBOX)
+ return 0;
+
+ MSG *msg = (MSG*)lparam;
+
+ int action = Hotkey_Check(msg, "Quick Contacts");
+ if (action != 0) {
+ SendMessage(hwndMain, WM_COMMAND, action, 0);
+ return 1;
+ }
+
+ if (msg->message == WM_KEYDOWN && msg->wParam == VK_ESCAPE) {
+ switch (SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_GETDROPPEDSTATE, 0, 0)) {
+ case FALSE:
+ SendMessage(hwndMain, WM_CLOSE, 0, 0);
+ break;
+
+ case TRUE:
+ SendDlgItemMessage(hwndMain, IDC_USERNAME, CB_SHOWDROPDOWN, FALSE, 0);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+BOOL ScreenToClient(HWND hWnd, LPRECT lpRect)
+{
+ BOOL ret;
+
+ POINT pt;
+
+ pt.x = lpRect->left;
+ pt.y = lpRect->top;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ if (!ret) return ret;
+
+ lpRect->left = pt.x;
+ lpRect->top = pt.y;
+
+
+ pt.x = lpRect->right;
+ pt.y = lpRect->bottom;
+
+ ret = ScreenToClient(hWnd, &pt);
+
+ lpRect->right = pt.x;
+ lpRect->bottom = pt.y;
+
+ return ret;
+}
+
+
+BOOL MoveWindow(HWND hWnd, const RECT &rect, BOOL bRepaint)
+{
+ return MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint);
+}
+
+
+static void FillButton(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key, HICON icon)
+{
+ wchar_t tmp[256];
+ wchar_t *full = tmp;
+
+ if (key == nullptr)
+ full = TranslateW(name);
+ else
+ mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key);
+
+ SendDlgItemMessage(hwndDlg, dlgItem, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, dlgItem, BUTTONADDTOOLTIP, (LPARAM)full, BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, dlgItem, BM_SETIMAGE, IMAGE_ICON, (LPARAM)icon);
+}
+
+
+static void FillCheckbox(HWND hwndDlg, int dlgItem, wchar_t *name, wchar_t *key)
+{
+ wchar_t tmp[256];
+ wchar_t *full = tmp;
+
+ if (key == nullptr)
+ full = TranslateW(name);
+ else
+ mir_snwprintf(tmp, L"%s (%s)", TranslateW(name), key);
+
+ SetDlgItemText(hwndDlg, dlgItem, full);
+}
+
+
+static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_USERNAME), &rc);
+ ScreenToClient(hwndDlg, &rc);
+
+ CreateWindow(L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE,
+ rc.left - 20, rc.top + (rc.bottom - rc.top - 16) / 2, 16, 16, hwndDlg, (HMENU)IDC_ICO,
+ g_plugin.getInst(), nullptr);
+
+ hHook = SetWindowsHookEx(WH_MSGFILTER, HookProc, nullptr, GetCurrentThreadId());
+
+ // Combo
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, EM_LIMITTEXT, (WPARAM)119, 0);
+ mir_subclassWindow(GetWindow(GetDlgItem(hwndDlg, IDC_USERNAME), GW_CHILD), EditProc);
+
+ // Buttons
+ FillCheckbox(hwndDlg, IDC_SHOW_ALL_CONTACTS, LPGENW("Show all contacts"), NULL);
+ FillButton(hwndDlg, IDC_MESSAGE, LPGENW("Send message"), nullptr, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
+ FillButton(hwndDlg, IDC_FILE, LPGENW("Send file"), NULL, Skin_LoadIcon(SKINICON_EVENT_FILE));
+ FillButton(hwndDlg, IDC_USERINFO, LPGENW("Open user info"), NULL, Skin_LoadIcon(SKINICON_OTHER_USERDETAILS));
+ FillButton(hwndDlg, IDC_HISTORY, LPGENW("Open history"), NULL, Skin_LoadIcon(SKINICON_OTHER_HISTORY));
+ FillButton(hwndDlg, IDC_MENU, LPGENW("Open contact menu"), NULL, Skin_LoadIcon(SKINICON_OTHER_DOWNARROW));
+
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETEXTENDEDUI, TRUE, 0);
+
+ Utils_RestoreWindowPositionNoSize(hwndDlg, NULL, MODULENAME, "window");
+
+ LoadContacts(hwndDlg, FALSE);
+
+ EnableButtons(hwndDlg, NULL);
+ if (g_plugin.getByte("EnableLastSentTo", 0)) {
+ int pos = GetItemPos(g_plugin.getDword("LastSentTo", -1));
+ if (pos != -1) {
+ SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_SETCURSEL, (WPARAM)pos, 0);
+ EnableButtons(hwndDlg, contacts[pos]->hcontact);
+ }
+ }
+
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_USERNAME:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int pos = SendDlgItemMessage(hwndDlg, IDC_USERNAME, CB_GETCURSEL, 0, 0);
+ EnableButtons(hwndDlg, pos < contacts.getCount() ? contacts[pos]->hcontact : NULL);
+ }
+ break;
+
+ case IDC_ENTER:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL)
+ break;
+
+ Clist_ContactDoubleClicked(hContact);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+ case IDC_MESSAGE:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MESSAGE)))
+ break;
+
+ CallService(MS_MSG_SENDMESSAGEW, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ break;
+ }
+
+ case HOTKEY_FILE:
+ case IDC_FILE:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_FILE)))
+ break;
+
+ CallService(MS_FILE_SENDFILE, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case HOTKEY_INFO:
+ case IDC_USERINFO:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_USERINFO)))
+ break;
+
+ CallService(MS_USERINFO_SHOWDIALOG, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case HOTKEY_HISTORY:
+ case IDC_HISTORY:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_HISTORY)))
+ break;
+
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0);
+
+ g_plugin.setDword("LastSentTo", hContact);
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ }
+ break;
+
+ case HOTKEY_MENU:
+ case IDC_MENU:
+ {
+ MCONTACT hContact = GetSelectedContact(hwndDlg);
+ if (hContact == NULL) {
+ SetDlgItemText(hwndDlg, IDC_USERNAME, L"");
+ SetFocus(GetDlgItem(hwndDlg, IDC_USERNAME));
+ break;
+ }
+
+ // Is button enabled?
+ if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDC_MENU)))
+ break;
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(hwndDlg, IDC_MENU), &rc);
+ HMENU hMenu = Menu_BuildContactMenu(hContact);
+ int ret = TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, rc.left, rc.bottom, 0, hwndDlg, nullptr);
+ DestroyMenu(hMenu);
+
+ if (ret) {
+ SendMessage(hwndDlg, WM_CLOSE, 0, 0);
+ Clist_MenuProcessCommand(LOWORD(ret), MPCF_CONTACTMENU, hContact);
+ }
+
+ g_plugin.setDword("LastSentTo", (uint32_t)hContact);
+ }
+ break;
+
+ case HOTKEY_ALL_CONTACTS:
+ case IDC_SHOW_ALL_CONTACTS:
+ {
+ // Get old text
+ HWND hEdit = GetWindow(GetWindow(hwndDlg, GW_CHILD), GW_CHILD);
+ wchar_t sztext[120] = L"";
+
+ if (SendMessage(hEdit, EM_GETSEL, 0, 0) != -1)
+ SendMessage(hEdit, EM_REPLACESEL, 0, (LPARAM)L"");
+
+ GetWindowText(hEdit, sztext, _countof(sztext));
+
+ // Fill combo
+ BOOL all = IsDlgButtonChecked(hwndDlg, IDC_SHOW_ALL_CONTACTS);
+
+ if (LOWORD(wParam) == HOTKEY_ALL_CONTACTS) {
+ // Toggle checkbox
+ all = !all;
+ CheckDlgButton(hwndDlg, IDC_SHOW_ALL_CONTACTS, all ? BST_CHECKED : BST_UNCHECKED);
+ }
+
+ LoadContacts(hwndDlg, all);
+
+ // Return selection
+ CheckText(hEdit, sztext);
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "window");
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ UnhookWindowsHookEx(hHook);
+ hwndMain = nullptr;
+ FreeContacts();
+ InterlockedExchange(&main_dialog_open, 0);
+ break;
+
+ case WM_DRAWITEM:
+ {
+ // add icons and protocol to listbox
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+
+ // Handle contact menu
+ if (lpdis->CtlID != IDC_USERNAME) {
+ if (lpdis->CtlType == ODT_MENU)
+ return Menu_DrawItem(lParam);
+ break;
+ }
+
+ // Handle combo
+ if (lpdis->itemID == -1)
+ break;
+
+ TEXTMETRIC tm;
+ int icon_width = 0, icon_height = 0;
+ RECT rc;
+
+ GetTextMetrics(lpdis->hDC, &tm);
+ ImageList_GetIconSize(hIml, &icon_width, &icon_height);
+
+ COLORREF clrfore = SetTextColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
+ COLORREF clrback = SetBkColor(lpdis->hDC, GetSysColor(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+
+ FillRect(lpdis->hDC, &lpdis->rcItem, GetSysColorBrush(lpdis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
+
+ // Draw icon
+ rc.left = lpdis->rcItem.left + 5;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - icon_height) / 2;
+ ImageList_Draw(hIml, Clist_GetContactIcon(contacts[lpdis->itemData]->hcontact), lpdis->hDC, rc.left, rc.top, ILD_NORMAL);
+
+ // Make rect for text
+ rc.left += icon_width + 5;
+ rc.right = lpdis->rcItem.right - 1;
+ rc.top = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
+ rc.bottom = rc.top + tm.tmHeight;
+
+ // Draw Protocol
+ if (opts.num_protos > 1) {
+ if (max_proto_width == 0) {
+ // Has to be done, else the DC isnt the right one
+ // Dont ask me why
+ for (auto &it : contacts) {
+ RECT rcc = { 0, 0, 0x7FFF, 0x7FFF };
+ DrawText(lpdis->hDC, it->proto, -1, &rcc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_CALCRECT);
+ max_proto_width = max(max_proto_width, rcc.right - rcc.left);
+ }
+
+ // Fix max_proto_width
+ if (opts.group_append && opts.group_column)
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 5);
+ else if (opts.group_append)
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 4);
+ else
+ max_proto_width = min(max_proto_width, (rc.right - rc.left) / 3);
+ }
+
+ RECT rc_tmp = rc;
+ rc_tmp.left = rc_tmp.right - max_proto_width;
+ DrawText(lpdis->hDC, contacts[lpdis->itemData]->proto, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+ rc.right = rc_tmp.left - 5;
+ }
+
+ // Draw group
+ if (opts.group_append && opts.group_column) {
+ RECT rc_tmp = rc;
+
+ if (opts.group_column_left) {
+ rc_tmp.right = rc_tmp.left + (rc.right - rc.left) / 3;
+ rc.left = rc_tmp.right + 5;
+ }
+ else {
+ rc_tmp.left = rc_tmp.right - (rc.right - rc.left) / 3;
+ rc.right = rc_tmp.left - 5;
+ }
+
+ DrawText(lpdis->hDC, contacts[lpdis->itemData]->szgroup, -1, &rc_tmp, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+ }
+
+ // Draw text
+ wchar_t *name;
+ if (opts.group_append && !opts.group_column)
+ name = GetListName(contacts[lpdis->itemData]);
+ else
+ name = contacts[lpdis->itemData]->szname;
+
+ DrawText(lpdis->hDC, name, -1, &rc, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
+
+ // Restore old colors
+ SetTextColor(lpdis->hDC, clrfore);
+ SetBkColor(lpdis->hDC, clrback);
+ }
+ return TRUE;
+
+ case WM_MEASUREITEM:
+ {
+ LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
+
+ // Handle contact menu
+ if (lpmis->CtlID != IDC_USERNAME) {
+ if (lpmis->CtlType == ODT_MENU)
+ return Menu_MeasureItem(lParam);
+ break;
+ }
+
+ // Handle combo
+
+ TEXTMETRIC tm;
+ int icon_width = 0, icon_height = 0;
+
+ GetTextMetrics(GetDC(hwndDlg), &tm);
+ ImageList_GetIconSize(hIml, &icon_width, &icon_height);
+
+ lpmis->itemHeight = max(icon_height, tm.tmHeight);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// Show the main dialog
+INT_PTR ShowDialog(WPARAM, LPARAM)
+{
+ // Get the icons for the listbox
+ hIml = Clist_GetImageList();
+
+ if (!main_dialog_open) {
+ InterlockedExchange(&main_dialog_open, 1);
+
+ hwndMain = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_MAIN), nullptr, MainDlgProc);
+ }
+
+ // Show it
+ SetForegroundWindow(hwndMain);
+ SetFocus(hwndMain);
+ ShowWindow(hwndMain, SW_SHOW);
+ return 0;
+}
diff --git a/plugins/QuickContacts/src/stdafx.cxx b/plugins/QuickContacts/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/QuickContacts/src/stdafx.cxx
+++ b/plugins/QuickContacts/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/plugins/QuickMessages/src/stdafx.cxx b/plugins/QuickMessages/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/QuickMessages/src/stdafx.cxx
+++ b/plugins/QuickMessages/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/plugins/QuickReplies/src/stdafx.cxx b/plugins/QuickReplies/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/QuickReplies/src/stdafx.cxx
+++ b/plugins/QuickReplies/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/plugins/QuickSearch/src/main.cpp b/plugins/QuickSearch/src/main.cpp
index cf068d7561..632bfb2aff 100644
--- a/plugins/QuickSearch/src/main.cpp
+++ b/plugins/QuickSearch/src/main.cpp
@@ -1,188 +1,188 @@
-/*
-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"
-
-CMPlugin g_plugin;
-
-HANDLE hTTBButton;
-
-bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled;
-
-int OnOptInit(WPARAM, LPARAM);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx = {
- sizeof(PLUGININFOEX),
- __PLUGIN_NAME,
- PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
- __DESCRIPTION,
- __AUTHOR,
- __COPYRIGHT,
- __AUTHORWEB,
- UNICODE_AWARE,
- // {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C}
- {0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}}
-};
-
-CMPlugin::CMPlugin() :
- PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
- m_columns(1)
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static int OnTTBLoaded(WPARAM, LPARAM)
-{
- TTBButton ttb = {};
- ttb.dwFlags = TTBBF_VISIBLE;
- ttb.pszService = QS_SHOWSERVICE;
- ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS);
- ttb.name = MODULENAME;
- ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search");
- hTTBButton = g_plugin.addTTB(&ttb);
- return 0;
-}
-
-static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM)
-{
- OpenSrWindow((wchar_t *)wParam);
- return 0;
-}
-
-static int OnCheckPlugins(WPARAM, LPARAM)
-{
- g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING);
- g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW);
- g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW);
-
- return 0;
-}
-
-static int OnModulesLoaded(WPARAM, LPARAM)
-{
- HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded);
-
- CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow);
-
- // add menu item
- CMenuItem mi(&g_plugin);
- SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0);
- mi.name.a = LPGEN("Quick Search");
- mi.position = 500050000;
- mi.pszService = QS_SHOWSERVICE;
- mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS);
- Menu_AddMainMenuItem(&mi);
-
- // register hotkey
- HOTKEYDESC hkd = {};
- hkd.pszName = "QS_Global";
- hkd.szDescription.a = LPGEN("Open Quick Search window");
- hkd.szSection.a = LPGEN("Quick Search");
- hkd.pszService = QS_SHOWSERVICE;
- hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3);
- g_plugin.addHotkey(&hkd);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static IconItem iconList[] =
-{
- { LPGEN("Quick Search"), "QS", IDI_QS },
- { LPGEN("New Column"), "New", IDI_NEW },
- { LPGEN("Column Up"), "Up", IDI_UP },
- { LPGEN("Column Down"), "Down", IDI_DOWN },
- { LPGEN("Delete Column"), "Delete", IDI_DELETE },
- { LPGEN("Default"), "Default", IDI_DEFAULT},
- { LPGEN("Reload"), "Reload", IDI_RELOAD },
- { LPGEN("Male"), "Male", IDI_MALE },
- { LPGEN("Female"), "Female", IDI_FEMALE },
-};
-
-struct
-{
- COLORREF defValue;
- const char *szSetting, *szDescr;
-}
-static sttColors[color_max] = {
- { 0x00FFFFFF, "back_norm", LPGEN("Normal background") },
- { 0x00000000, "fore_norm", LPGEN("Normal foreground") },
- { 0x00EBE6DE, "back_odd" , LPGEN("Odd background") },
- { 0x00000000, "fore_odd" , LPGEN("Odd foreground") },
- { 0x008080FF, "back_dis" , LPGEN("Disabled account background") },
- { 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") },
- { 0x008000FF, "back_del" , LPGEN("Deleted account background") },
- { 0x00000000, "fore_del" , LPGEN("Deleted account foreground") },
- { 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background") },
- { 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground") },
- { 0x00BAE699, "back_meta", LPGEN("Metacontact background") },
- { 0x00000000, "fore_meta", LPGEN("Metacontact foreground") },
- { 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background") },
- { 0x00000000, "fore_sub" , LPGEN("Subcontact foreground") },
-};
-
-static int OnColorReload(WPARAM, LPARAM)
-{
- for (int i = 0; i < color_max; i++)
- g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szDescr);
- return 0;
-}
-
-int CMPlugin::Load()
-{
- g_plugin.registerIcon(MODULENAME, iconList);
-
- ColourID colourid = {};
- strncpy_s(colourid.group, MODULENAME, _TRUNCATE);
- strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE);
-
- for (auto &it : sttColors) {
- strncpy_s(colourid.name, it.szDescr, _TRUNCATE);
- strncpy_s(colourid.setting, it.szSetting, _TRUNCATE);
- colourid.defcolour = it.defValue;
- colourid.order = int(&it - sttColors);
- g_plugin.addColor(&colourid);
- }
- OnColorReload(0, 0);
- OnCheckPlugins(0, 0);
-
- HookEvent(ME_COLOUR_RELOAD, OnColorReload);
- HookEvent(ME_OPT_INITIALISE, OnOptInit);
- HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
- HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
- HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins);
-
- if (!LoadColumns(m_columns))
- LoadDefaultColumns(m_columns);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPlugin::Unload()
-{
- if (hTTBButton) {
- CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0);
- hTTBButton = 0;
- }
-
- CloseSrWindow();
- return 0;
-}
+/*
+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"
+
+CMPlugin g_plugin;
+
+HANDLE hTTBButton;
+
+bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled;
+
+int OnOptInit(WPARAM, LPARAM);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C}
+ {0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
+ m_columns(1)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int OnTTBLoaded(WPARAM, LPARAM)
+{
+ TTBButton ttb = {};
+ ttb.dwFlags = TTBBF_VISIBLE;
+ ttb.pszService = QS_SHOWSERVICE;
+ ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS);
+ ttb.name = MODULENAME;
+ ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search");
+ hTTBButton = g_plugin.addTTB(&ttb);
+ return 0;
+}
+
+static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM)
+{
+ OpenSrWindow((wchar_t *)wParam);
+ return 0;
+}
+
+static int OnCheckPlugins(WPARAM, LPARAM)
+{
+ g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING);
+ g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW);
+ g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW);
+
+ return 0;
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+ HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded);
+
+ CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow);
+
+ // add menu item
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0);
+ mi.name.a = LPGEN("Quick Search");
+ mi.position = 500050000;
+ mi.pszService = QS_SHOWSERVICE;
+ mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS);
+ Menu_AddMainMenuItem(&mi);
+
+ // register hotkey
+ HOTKEYDESC hkd = {};
+ hkd.pszName = "QS_Global";
+ hkd.szDescription.a = LPGEN("Open Quick Search window");
+ hkd.szSection.a = LPGEN("Quick Search");
+ hkd.pszService = QS_SHOWSERVICE;
+ hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3);
+ g_plugin.addHotkey(&hkd);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static IconItem iconList[] =
+{
+ { LPGEN("Quick Search"), "QS", IDI_QS },
+ { LPGEN("New Column"), "New", IDI_NEW },
+ { LPGEN("Column Up"), "Up", IDI_UP },
+ { LPGEN("Column Down"), "Down", IDI_DOWN },
+ { LPGEN("Delete Column"), "Delete", IDI_DELETE },
+ { LPGEN("Default"), "Default", IDI_DEFAULT},
+ { LPGEN("Reload"), "Reload", IDI_RELOAD },
+ { LPGEN("Male"), "Male", IDI_MALE },
+ { LPGEN("Female"), "Female", IDI_FEMALE },
+};
+
+struct
+{
+ COLORREF defValue;
+ const char *szSetting, *szDescr;
+}
+static sttColors[color_max] = {
+ { 0x00FFFFFF, "back_norm", LPGEN("Normal background") },
+ { 0x00000000, "fore_norm", LPGEN("Normal foreground") },
+ { 0x00EBE6DE, "back_odd" , LPGEN("Odd background") },
+ { 0x00000000, "fore_odd" , LPGEN("Odd foreground") },
+ { 0x008080FF, "back_dis" , LPGEN("Disabled account background") },
+ { 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") },
+ { 0x008000FF, "back_del" , LPGEN("Deleted account background") },
+ { 0x00000000, "fore_del" , LPGEN("Deleted account foreground") },
+ { 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background") },
+ { 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground") },
+ { 0x00BAE699, "back_meta", LPGEN("Metacontact background") },
+ { 0x00000000, "fore_meta", LPGEN("Metacontact foreground") },
+ { 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background") },
+ { 0x00000000, "fore_sub" , LPGEN("Subcontact foreground") },
+};
+
+static int OnColorReload(WPARAM, LPARAM)
+{
+ for (int i = 0; i < color_max; i++)
+ g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szDescr);
+ return 0;
+}
+
+int CMPlugin::Load()
+{
+ g_plugin.registerIcon(MODULENAME, iconList);
+
+ ColourID colourid = {};
+ strncpy_s(colourid.group, MODULENAME, _TRUNCATE);
+ strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE);
+
+ for (auto &it : sttColors) {
+ strncpy_s(colourid.name, it.szDescr, _TRUNCATE);
+ strncpy_s(colourid.setting, it.szSetting, _TRUNCATE);
+ colourid.defcolour = it.defValue;
+ colourid.order = int(&it - sttColors);
+ g_plugin.addColor(&colourid);
+ }
+ OnColorReload(0, 0);
+ OnCheckPlugins(0, 0);
+
+ HookEvent(ME_COLOUR_RELOAD, OnColorReload);
+ HookEvent(ME_OPT_INITIALISE, OnOptInit);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
+ HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins);
+
+ if (!LoadColumns(m_columns))
+ LoadDefaultColumns(m_columns);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+ if (hTTBButton) {
+ CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0);
+ hTTBButton = 0;
+ }
+
+ CloseSrWindow();
+ return 0;
+}
diff --git a/plugins/QuickSearch/src/options.cpp b/plugins/QuickSearch/src/options.cpp
index d827416ecc..d74cf745aa 100644
--- a/plugins/QuickSearch/src/options.cpp
+++ b/plugins/QuickSearch/src/options.cpp
@@ -1,519 +1,519 @@
-/*
-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"
-
-class COptionsDlg : public CDlgBase
-{
- OBJLIST<ColumnItem> m_columns;
-
- void AddColumn(int idx, ColumnItem *pCol)
- {
- LVITEM lvi = {};
- lvi.mask = LVIF_PARAM;
- lvi.iItem = idx;
- lvi.lParam = LPARAM(pCol);
- m_list.InsertItem(&lvi);
- }
-
- void CheckDirection(int iItem)
- {
- btnUp.Enable(iItem > 0);
- btnDown.Enable(iItem < m_list.GetItemCount()-1);
- }
-
- void ClearScreen()
- {
- // setting
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE);
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE);
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE);
-
- // contact info
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE);
-
- // others
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE);
- }
-
- void DisplayCurInfo(const ColumnItem *pCol)
- {
- ClearScreen();
- SetupScreen(pCol->setting_type);
-
- editTitle.SetText(pCol->title);
- cmbVarType.SelectData(pCol->setting_type);
-
- switch (pCol->setting_type) {
- case QST_SETTING:
- cmbDataType.SelectData(pCol->datatype);
- editModule.SetTextA(pCol->module);
- editSetting.SetTextA(pCol->setting);
- break;
-
- case QST_SCRIPT:
- SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script);
- break;
-
- case QST_CONTACTINFO:
- cmbCnfType.SelectData(pCol->cnftype);
- break;
-
- case QST_OTHER:
- cmbOther.SelectData(pCol->other);
- break;
- }
- }
-
- void FillTableLine(int item, ColumnItem *pColumn)
- {
- m_list.SetItemText(item, 1, pColumn->title);
-
- switch (pColumn->setting_type) {
- case QST_SETTING:
- m_list.SetItemText(item, 2, _A2T(pColumn->module));
- m_list.SetItemText(item, 3, _A2T(pColumn->setting));
- break;
-
- case QST_SCRIPT:
- m_list.SetItemText(item, 2, TranslateT("Script"));
- break;
-
- case QST_SERVICE:
- m_list.SetItemText(item, 2, TranslateT("Service"));
- m_list.SetItemText(item, 3, _A2T(pColumn->svc.service));
- break;
-
- case QST_CONTACTINFO:
- m_list.SetItemText(item, 2, TranslateT("Contact info"));
- m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype));
- break;
-
- case QST_OTHER:
- m_list.SetItemText(item, 2, TranslateT("Other"));
- if (pColumn->other == QSTO_METACONTACT)
- m_list.SetItemText(item, 3, TranslateT("Metacontact"));
- break;
- }
- }
-
- void InitScreen()
- {
- // setting
- cmbDataType.SetCurSel(0);
- editModule.SetText(L"");
- editSetting.SetText(L"");
-
- // contact info
- cmbCnfType.SetCurSel(0);
-
- // others
- cmbOther.SetCurSel(0);
- }
-
- void ResizeControl(int id, int width)
- {
- HWND hwnd = GetDlgItem(m_hwnd, id);
- RECT rc;
- ::GetWindowRect(hwnd, &rc);
- ::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
- }
-
- void SetupScreen(int type)
- {
- if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE)))
- return;
-
- // setting
- int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE;
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd);
- editModule.Show(cmd == SW_SHOW);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd);
- editSetting.Show(cmd == SW_SHOW);
-
- // contact info
- cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE;
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd);
-
- // script
- cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE;
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd);
-
- // others
- cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE;
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd);
- }
-
- void UpdateList()
- {
- m_list.DeleteAllItems();
-
- int cnt = 0;
- for (auto &it : m_columns) {
- AddColumn(cnt, it);
- FillTableLine(cnt, it);
- m_list.SetCheckState(cnt, it->bEnabled);
- cnt++;
- }
-
- m_list.SetCurSel(0);
- }
-
- CCtrlEdit editTitle, editModule, editSetting;
- CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons;
- CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType;
- CCtrlListView m_list;
- CCtrlButton btnSave, btnResize;
- CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload;
-
-public:
- COptionsDlg() :
- CDlgBase(g_plugin, IDD_OPTIONS),
- m_columns(1),
- m_list(this, IDC_LIST),
- btnSave(this, IDC_SETITEM),
- btnResize(this, IDC_B_RESIZE),
- btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")),
- btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")),
- btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")),
- btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")),
- btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")),
- btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")),
- editTitle(this, IDC_E_TITLE),
- editModule(this, IDC_E_MODULE),
- editSetting(this, IDC_E_SETTING),
- cmbOther(this, IDC_C_OTHER),
- cmbCnfType(this, IDC_C_CNFTYPE),
- cmbVarType(this, IDC_C_VARTYPE),
- cmbDataType(this, IDC_C_DATATYPE),
- chkDrawGrid(this, IDC_CH_DRAWGRID),
- chkAutoClose(this, IDC_CH_AUTOCLOSE),
- chkSortStatus(this, IDC_CH_SORTSTATUS),
- chkSavePattern(this, IDC_CH_SAVEPATTERN),
- chkClientIcons(this, IDC_CH_CLIENTICONS),
- chkUseToolstyle(this, IDC_CH_USETOOLSTYLE)
- {
- m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List);
-
- btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up);
- btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New);
- btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down);
- btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save);
- btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete);
- btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload);
- btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize);
- btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default);
-
- cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var);
- }
-
- bool OnInitDialog() override
- {
- editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent();
- cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent();
-
- m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
- m_list.AddColumn(0, L"#", 20);
- m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1", 95));
- m_list.AddColumn(2, TranslateT("Module/Info type"), g_plugin.getWord("col2", 105));
- m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3", 85));
-
- cmbVarType.AddString(TranslateT("Database setting"), QST_SETTING);
- cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT);
- cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO);
- cmbVarType.AddString(TranslateT("Other"), QST_OTHER);
- cmbVarType.SetCurSel(0);
-
- cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE);
- cmbDataType.AddString(TranslateT("Word"), QSTS_WORD);
- cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD);
- cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED);
- cmbDataType.AddString(TranslateT("Hexadecimal"), QSTS_HEXNUM);
- cmbDataType.AddString(TranslateT("String"), QSTS_STRING);
- cmbDataType.AddString(TranslateT("Timestamp"), QSTS_TIMESTAMP);
- cmbDataType.SetCurSel(0);
-
- cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN);
- cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT);
- cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT);
- cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT);
- cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME);
- cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT);
- cmbOther.SetCurSel(0);
-
- for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++)
- if (auto *pwszText = cnf2str(i))
- cmbCnfType.AddString(pwszText, i);
- cmbCnfType.SetCurSel(0);
-
- chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0);
- chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0);
- chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0);
- chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0);
- chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0);
- chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0);
-
- // make local copy of column descriptions
- for (auto &it : g_plugin.m_columns)
- m_columns.insert(new ColumnItem(*it));
-
- UpdateList();
- onClick_Resize(0);
- if (m_columns.getCount())
- DisplayCurInfo(&m_columns[0]);
- return true;
- }
-
- bool OnApply() override
- {
- // checkboxes
- g_plugin.m_flags &= ~QSO_MAINOPTIONS;
- if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID;
- if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE;
- if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS;
- if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS;
- if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN;
- if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE;
-
- int tmpbool = CloseSrWindow(false);
-
- g_plugin.m_columns.destroy();
- int nCount = m_list.GetItemCount();
- for (int i = 0; i < nCount; i++) {
- auto *pCol = (ColumnItem *)m_list.GetItemData(i);
- pCol->bEnabled = m_list.GetCheckState(i) != 0;
- g_plugin.m_columns.insert(new ColumnItem(*pCol));
- }
-
- g_plugin.SaveOptions();
-
- if (tmpbool)
- OpenSrWindow(0);
- return true;
- }
-
- void OnDestroy() override
- {
- m_columns.destroy();
-
- g_plugin.setWord("col1", m_list.GetColumnWidth(1));
- g_plugin.setWord("col2", m_list.GetColumnWidth(2));
- g_plugin.setWord("col3", m_list.GetColumnWidth(3));
- }
-
- void onClick_New(CCtrlButton *)
- {
- int idx = m_list.GetSelectionMark()+1;
- auto *pNew = new ColumnItem(TranslateT("New column"));
- m_columns.insert(pNew);
-
- AddColumn(idx, pNew);
- m_list.EnsureVisible(idx, FALSE);
- m_list.SetCurSel(idx);
- InitScreen();
- CheckDirection(idx);
- btnDelete.Enable();
- NotifyChange();
- }
-
- void onClick_Delete(CCtrlButton *)
- {
- int idx = m_list.GetSelectionMark();
- auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
-
- m_list.DeleteItem(idx);
- m_columns.remove(pCol);
-
- int nCount = m_list.GetItemCount();
- if (nCount == 0) {
- m_list.Disable();
- InitScreen();
- }
- else {
- if (nCount == idx)
- idx--;
- m_list.SetCurSel(idx);
- }
- CheckDirection(idx);
- NotifyChange();
- }
-
- void onClick_Up(CCtrlButton *)
- {
- int idx = m_list.GetSelectionMark();
- if (idx > 0) {
- CheckDirection(m_list.MoveItem(idx, -1));
- NotifyChange();
- }
- }
-
- void onClick_Down(CCtrlButton *)
- {
- int idx = m_list.GetSelectionMark();
- if (idx < m_list.GetItemCount() - 1) {
- CheckDirection(m_list.MoveItem(idx, 1));
- NotifyChange();
- }
- }
-
- void onClick_Reload(CCtrlButton *)
- {
- g_plugin.LoadColumns(m_columns);
- UpdateList();
- }
-
- void onClick_Default(CCtrlButton *)
- {
- LoadDefaultColumns(m_columns);
- UpdateList();
- NotifyChange();
- }
-
- void onClick_Save(CCtrlButton *)
- {
- if (m_list.GetItemCount() == 0) {
- AddColumn(0, new ColumnItem(TranslateT("New column")));
- m_list.SetCurSel(0);
- btnDelete.Enable();
- }
-
- int idx = m_list.GetSelectionMark();
- auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
- pCol->dwFlags = 0;
- if (m_list.GetCheckState(idx))
- pCol->bEnabled = pCol->bFilter = true;
- pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel());
- replaceStrW(pCol->title, editTitle.GetText());
-
- switch (pCol->setting_type) {
- case QST_SETTING:
- pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel());
- pCol->module = mir_u2a(ptrW(editModule.GetText()));
- pCol->setting = mir_u2a(ptrW(editSetting.GetText()));
- break;
-
- case QST_CONTACTINFO:
- pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
- break;
-
- case QST_OTHER:
- pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
- break;
- }
-
- FillTableLine(idx, pCol);
- NotifyChange();
- }
-
- void onClick_Resize(CCtrlButton *)
- {
- wchar_t *pcw;
- int dx, rside;
-
- RECT rc, rc1;
- GetClientRect(m_hwnd, &rc);
- GetWindowRect(btnResize.GetHwnd(), &rc1);
-
- POINT pt = { rc1.left, 0 };
- ScreenToClient(m_hwnd, &pt);
- if (pt.x < (rc.right - 50)) {
- rside = SW_HIDE;
- dx = rc.right - (rc1.right - rc1.left) - 4;
- pcw = L"<";
- }
- else {
- rside = SW_SHOW;
-
- GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc);
- pt.x = rc.left;
- pt.y = 0;
- ScreenToClient(m_hwnd, &pt);
- dx = pt.x - (rc1.right - rc1.left) - 4;
- pcw = L">";
- }
-
- btnResize.SetText(pcw);
-
- // move separator button
- SetWindowPos(btnResize.GetHwnd(), 0, dx + 2, 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
-
- // resize left side controls
- ResizeControl(IDC_LIST, dx);
- ResizeControl(IDC_CH_GROUP, dx);
-
- ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8);
- ResizeControl(IDC_CH_DRAWGRID, dx - 8);
- ResizeControl(IDC_CH_SAVEPATTERN, dx - 8);
- ResizeControl(IDC_CH_AUTOCLOSE, dx - 8);
- ResizeControl(IDC_CH_SORTSTATUS, dx - 8);
- ResizeControl(IDC_CH_CLIENTICONS, dx - 8);
-
- // show/hide setting block (ugly, i know!)
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside);
- ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside);
-
- ClearScreen();
- if (rside == SW_SHOW)
- SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel()));
- }
-
- void onItemChanged_List(CCtrlListView::TEventInfo *ev)
- {
- auto *nmlv = ev->nmlv;
- // we got new focus
- if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) {
- CheckDirection(nmlv->iItem);
- InitScreen();
- DisplayCurInfo((ColumnItem*)nmlv->lParam);
- }
- }
-
- void onSelChanged_Var(CCtrlCombo *pCombo)
- {
- SetupScreen(pCombo->GetItemData(pCombo->GetCurSel()));
- }
-};
-
-int OnOptInit(WPARAM wParam, LPARAM)
-{
- OPTIONSDIALOGPAGE odp = {};
- odp.flags = ODPF_BOLDGROUPS;
- odp.szGroup.a = LPGEN("Contacts");
- odp.szTitle.a = LPGEN("Quick Search");
- odp.position = 900003000;
- odp.pDialog = new COptionsDlg();
- g_plugin.addOptions(wParam, &odp);
- return 0;
-}
+/*
+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"
+
+class COptionsDlg : public CDlgBase
+{
+ OBJLIST<ColumnItem> m_columns;
+
+ void AddColumn(int idx, ColumnItem *pCol)
+ {
+ LVITEM lvi = {};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = idx;
+ lvi.lParam = LPARAM(pCol);
+ m_list.InsertItem(&lvi);
+ }
+
+ void CheckDirection(int iItem)
+ {
+ btnUp.Enable(iItem > 0);
+ btnDown.Enable(iItem < m_list.GetItemCount()-1);
+ }
+
+ void ClearScreen()
+ {
+ // setting
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE);
+
+ // contact info
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE);
+
+ // others
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE);
+ }
+
+ void DisplayCurInfo(const ColumnItem *pCol)
+ {
+ ClearScreen();
+ SetupScreen(pCol->setting_type);
+
+ editTitle.SetText(pCol->title);
+ cmbVarType.SelectData(pCol->setting_type);
+
+ switch (pCol->setting_type) {
+ case QST_SETTING:
+ cmbDataType.SelectData(pCol->datatype);
+ editModule.SetTextA(pCol->module);
+ editSetting.SetTextA(pCol->setting);
+ break;
+
+ case QST_SCRIPT:
+ SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script);
+ break;
+
+ case QST_CONTACTINFO:
+ cmbCnfType.SelectData(pCol->cnftype);
+ break;
+
+ case QST_OTHER:
+ cmbOther.SelectData(pCol->other);
+ break;
+ }
+ }
+
+ void FillTableLine(int item, ColumnItem *pColumn)
+ {
+ m_list.SetItemText(item, 1, pColumn->title);
+
+ switch (pColumn->setting_type) {
+ case QST_SETTING:
+ m_list.SetItemText(item, 2, _A2T(pColumn->module));
+ m_list.SetItemText(item, 3, _A2T(pColumn->setting));
+ break;
+
+ case QST_SCRIPT:
+ m_list.SetItemText(item, 2, TranslateT("Script"));
+ break;
+
+ case QST_SERVICE:
+ m_list.SetItemText(item, 2, TranslateT("Service"));
+ m_list.SetItemText(item, 3, _A2T(pColumn->svc.service));
+ break;
+
+ case QST_CONTACTINFO:
+ m_list.SetItemText(item, 2, TranslateT("Contact info"));
+ m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype));
+ break;
+
+ case QST_OTHER:
+ m_list.SetItemText(item, 2, TranslateT("Other"));
+ if (pColumn->other == QSTO_METACONTACT)
+ m_list.SetItemText(item, 3, TranslateT("Metacontact"));
+ break;
+ }
+ }
+
+ void InitScreen()
+ {
+ // setting
+ cmbDataType.SetCurSel(0);
+ editModule.SetText(L"");
+ editSetting.SetText(L"");
+
+ // contact info
+ cmbCnfType.SetCurSel(0);
+
+ // others
+ cmbOther.SetCurSel(0);
+ }
+
+ void ResizeControl(int id, int width)
+ {
+ HWND hwnd = GetDlgItem(m_hwnd, id);
+ RECT rc;
+ ::GetWindowRect(hwnd, &rc);
+ ::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
+ }
+
+ void SetupScreen(int type)
+ {
+ if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE)))
+ return;
+
+ // setting
+ int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd);
+ editModule.Show(cmd == SW_SHOW);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd);
+ editSetting.Show(cmd == SW_SHOW);
+
+ // contact info
+ cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd);
+
+ // script
+ cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd);
+
+ // others
+ cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE;
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd);
+ }
+
+ void UpdateList()
+ {
+ m_list.DeleteAllItems();
+
+ int cnt = 0;
+ for (auto &it : m_columns) {
+ AddColumn(cnt, it);
+ FillTableLine(cnt, it);
+ m_list.SetCheckState(cnt, it->bEnabled);
+ cnt++;
+ }
+
+ m_list.SetCurSel(0);
+ }
+
+ CCtrlEdit editTitle, editModule, editSetting;
+ CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons;
+ CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType;
+ CCtrlListView m_list;
+ CCtrlButton btnSave, btnResize;
+ CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload;
+
+public:
+ COptionsDlg() :
+ CDlgBase(g_plugin, IDD_OPTIONS),
+ m_columns(1),
+ m_list(this, IDC_LIST),
+ btnSave(this, IDC_SETITEM),
+ btnResize(this, IDC_B_RESIZE),
+ btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")),
+ btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")),
+ btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")),
+ btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")),
+ btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")),
+ btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")),
+ editTitle(this, IDC_E_TITLE),
+ editModule(this, IDC_E_MODULE),
+ editSetting(this, IDC_E_SETTING),
+ cmbOther(this, IDC_C_OTHER),
+ cmbCnfType(this, IDC_C_CNFTYPE),
+ cmbVarType(this, IDC_C_VARTYPE),
+ cmbDataType(this, IDC_C_DATATYPE),
+ chkDrawGrid(this, IDC_CH_DRAWGRID),
+ chkAutoClose(this, IDC_CH_AUTOCLOSE),
+ chkSortStatus(this, IDC_CH_SORTSTATUS),
+ chkSavePattern(this, IDC_CH_SAVEPATTERN),
+ chkClientIcons(this, IDC_CH_CLIENTICONS),
+ chkUseToolstyle(this, IDC_CH_USETOOLSTYLE)
+ {
+ m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List);
+
+ btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up);
+ btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New);
+ btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down);
+ btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save);
+ btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete);
+ btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload);
+ btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize);
+ btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default);
+
+ cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var);
+ }
+
+ bool OnInitDialog() override
+ {
+ editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent();
+ cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent();
+
+ m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
+ m_list.AddColumn(0, L"#", 20);
+ m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1", 95));
+ m_list.AddColumn(2, TranslateT("Module/Info type"), g_plugin.getWord("col2", 105));
+ m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3", 85));
+
+ cmbVarType.AddString(TranslateT("Database setting"), QST_SETTING);
+ cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT);
+ cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO);
+ cmbVarType.AddString(TranslateT("Other"), QST_OTHER);
+ cmbVarType.SetCurSel(0);
+
+ cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE);
+ cmbDataType.AddString(TranslateT("Word"), QSTS_WORD);
+ cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD);
+ cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED);
+ cmbDataType.AddString(TranslateT("Hexadecimal"), QSTS_HEXNUM);
+ cmbDataType.AddString(TranslateT("String"), QSTS_STRING);
+ cmbDataType.AddString(TranslateT("Timestamp"), QSTS_TIMESTAMP);
+ cmbDataType.SetCurSel(0);
+
+ cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN);
+ cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT);
+ cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT);
+ cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT);
+ cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME);
+ cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT);
+ cmbOther.SetCurSel(0);
+
+ for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++)
+ if (auto *pwszText = cnf2str(i))
+ cmbCnfType.AddString(pwszText, i);
+ cmbCnfType.SetCurSel(0);
+
+ chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0);
+ chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0);
+ chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0);
+ chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0);
+ chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0);
+ chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0);
+
+ // make local copy of column descriptions
+ for (auto &it : g_plugin.m_columns)
+ m_columns.insert(new ColumnItem(*it));
+
+ UpdateList();
+ onClick_Resize(0);
+ if (m_columns.getCount())
+ DisplayCurInfo(&m_columns[0]);
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ // checkboxes
+ g_plugin.m_flags &= ~QSO_MAINOPTIONS;
+ if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID;
+ if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE;
+ if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS;
+ if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS;
+ if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN;
+ if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE;
+
+ int tmpbool = CloseSrWindow(false);
+
+ g_plugin.m_columns.destroy();
+ int nCount = m_list.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ auto *pCol = (ColumnItem *)m_list.GetItemData(i);
+ pCol->bEnabled = m_list.GetCheckState(i) != 0;
+ g_plugin.m_columns.insert(new ColumnItem(*pCol));
+ }
+
+ g_plugin.SaveOptions();
+
+ if (tmpbool)
+ OpenSrWindow(0);
+ return true;
+ }
+
+ void OnDestroy() override
+ {
+ m_columns.destroy();
+
+ g_plugin.setWord("col1", m_list.GetColumnWidth(1));
+ g_plugin.setWord("col2", m_list.GetColumnWidth(2));
+ g_plugin.setWord("col3", m_list.GetColumnWidth(3));
+ }
+
+ void onClick_New(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark()+1;
+ auto *pNew = new ColumnItem(TranslateT("New column"));
+ m_columns.insert(pNew);
+
+ AddColumn(idx, pNew);
+ m_list.EnsureVisible(idx, FALSE);
+ m_list.SetCurSel(idx);
+ InitScreen();
+ CheckDirection(idx);
+ btnDelete.Enable();
+ NotifyChange();
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark();
+ auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
+
+ m_list.DeleteItem(idx);
+ m_columns.remove(pCol);
+
+ int nCount = m_list.GetItemCount();
+ if (nCount == 0) {
+ m_list.Disable();
+ InitScreen();
+ }
+ else {
+ if (nCount == idx)
+ idx--;
+ m_list.SetCurSel(idx);
+ }
+ CheckDirection(idx);
+ NotifyChange();
+ }
+
+ void onClick_Up(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark();
+ if (idx > 0) {
+ CheckDirection(m_list.MoveItem(idx, -1));
+ NotifyChange();
+ }
+ }
+
+ void onClick_Down(CCtrlButton *)
+ {
+ int idx = m_list.GetSelectionMark();
+ if (idx < m_list.GetItemCount() - 1) {
+ CheckDirection(m_list.MoveItem(idx, 1));
+ NotifyChange();
+ }
+ }
+
+ void onClick_Reload(CCtrlButton *)
+ {
+ g_plugin.LoadColumns(m_columns);
+ UpdateList();
+ }
+
+ void onClick_Default(CCtrlButton *)
+ {
+ LoadDefaultColumns(m_columns);
+ UpdateList();
+ NotifyChange();
+ }
+
+ void onClick_Save(CCtrlButton *)
+ {
+ if (m_list.GetItemCount() == 0) {
+ AddColumn(0, new ColumnItem(TranslateT("New column")));
+ m_list.SetCurSel(0);
+ btnDelete.Enable();
+ }
+
+ int idx = m_list.GetSelectionMark();
+ auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
+ pCol->dwFlags = 0;
+ if (m_list.GetCheckState(idx))
+ pCol->bEnabled = pCol->bFilter = true;
+ pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel());
+ replaceStrW(pCol->title, editTitle.GetText());
+
+ switch (pCol->setting_type) {
+ case QST_SETTING:
+ pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel());
+ pCol->module = mir_u2a(ptrW(editModule.GetText()));
+ pCol->setting = mir_u2a(ptrW(editSetting.GetText()));
+ break;
+
+ case QST_CONTACTINFO:
+ pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
+ break;
+
+ case QST_OTHER:
+ pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
+ break;
+ }
+
+ FillTableLine(idx, pCol);
+ NotifyChange();
+ }
+
+ void onClick_Resize(CCtrlButton *)
+ {
+ wchar_t *pcw;
+ int dx, rside;
+
+ RECT rc, rc1;
+ GetClientRect(m_hwnd, &rc);
+ GetWindowRect(btnResize.GetHwnd(), &rc1);
+
+ POINT pt = { rc1.left, 0 };
+ ScreenToClient(m_hwnd, &pt);
+ if (pt.x < (rc.right - 50)) {
+ rside = SW_HIDE;
+ dx = rc.right - (rc1.right - rc1.left) - 4;
+ pcw = L"<";
+ }
+ else {
+ rside = SW_SHOW;
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc);
+ pt.x = rc.left;
+ pt.y = 0;
+ ScreenToClient(m_hwnd, &pt);
+ dx = pt.x - (rc1.right - rc1.left) - 4;
+ pcw = L">";
+ }
+
+ btnResize.SetText(pcw);
+
+ // move separator button
+ SetWindowPos(btnResize.GetHwnd(), 0, dx + 2, 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
+
+ // resize left side controls
+ ResizeControl(IDC_LIST, dx);
+ ResizeControl(IDC_CH_GROUP, dx);
+
+ ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8);
+ ResizeControl(IDC_CH_DRAWGRID, dx - 8);
+ ResizeControl(IDC_CH_SAVEPATTERN, dx - 8);
+ ResizeControl(IDC_CH_AUTOCLOSE, dx - 8);
+ ResizeControl(IDC_CH_SORTSTATUS, dx - 8);
+ ResizeControl(IDC_CH_CLIENTICONS, dx - 8);
+
+ // show/hide setting block (ugly, i know!)
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside);
+ ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside);
+
+ ClearScreen();
+ if (rside == SW_SHOW)
+ SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel()));
+ }
+
+ void onItemChanged_List(CCtrlListView::TEventInfo *ev)
+ {
+ auto *nmlv = ev->nmlv;
+ // we got new focus
+ if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) {
+ CheckDirection(nmlv->iItem);
+ InitScreen();
+ DisplayCurInfo((ColumnItem*)nmlv->lParam);
+ }
+ }
+
+ void onSelChanged_Var(CCtrlCombo *pCombo)
+ {
+ SetupScreen(pCombo->GetItemData(pCombo->GetCurSel()));
+ }
+};
+
+int OnOptInit(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.flags = ODPF_BOLDGROUPS;
+ odp.szGroup.a = LPGEN("Contacts");
+ odp.szTitle.a = LPGEN("Quick Search");
+ odp.position = 900003000;
+ odp.pDialog = new COptionsDlg();
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/QuickSearch/src/stdafx.cxx b/plugins/QuickSearch/src/stdafx.cxx
index 1ab0efee94..ebbde0ade1 100644
--- a/plugins/QuickSearch/src/stdafx.cxx
+++ b/plugins/QuickSearch/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/plugins/QuickSearch/src/utils.cpp b/plugins/QuickSearch/src/utils.cpp
index 9db7133c3b..2cf1cdbc9f 100644
--- a/plugins/QuickSearch/src/utils.cpp
+++ b/plugins/QuickSearch/src/utils.cpp
@@ -1,556 +1,556 @@
-/*
-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"
-
-#define ACF_TYPE_NUMBER 0x00 // Parameter is number
-#define ACF_TYPE_STRING 0x01 // Parameter is ANSI String
-#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string
-#define ACF_TYPE_STRUCT 0x03 // Parameter is (result is in) structure
-#define ACF_TYPE_PARAM 0x08 // Parameter is Call parameter
-#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window
-#define ACF_TYPE_RESULT 0x0A // Parameter is previous action result
-#define ACF_TYPE_MASK 0x0F // parameter/result type mask
-
-ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) :
- title(mir_wstrdup(pwszTitle)),
- width(_width),
- setting_type(_setting_type)
-{
- bEnabled = true;
-}
-
-ColumnItem::ColumnItem(const ColumnItem &src)
-{
- memcpy(this, &src, sizeof(ColumnItem));
-
- title = mir_wstrdup(title);
-
- switch (setting_type) {
- case QST_SETTING:
- module = mir_strdup(module);
- setting = mir_strdup(setting);
- break;
-
- case QST_SCRIPT:
- script = mir_wstrdup(script);
- break;
-
- case QST_SERVICE:
- svc.service = mir_strdup(svc.service);
- switch (svc.wFlags) {
- case ACF_TYPE_NUMBER:
- case ACF_TYPE_STRING:
- case ACF_TYPE_UNICODE:
- svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam);
- break;
- case ACF_TYPE_STRUCT:
- svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam);
- break;
- }
-
- switch (svc.lFlags) {
- case ACF_TYPE_NUMBER:
- case ACF_TYPE_STRING:
- case ACF_TYPE_UNICODE:
- svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam);
- break;
- case ACF_TYPE_STRUCT:
- svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam);
- break;
- }
- break;
- }
-}
-
-ColumnItem::~ColumnItem()
-{
- mir_free(title);
-
- switch (setting_type) {
- case QST_SETTING:
- mir_free(module);
- mir_free(setting);
- break;
-
- case QST_SCRIPT:
- mir_free(script);
- break;
-
- case QST_SERVICE:
- mir_free(svc.service);
- switch (svc.wFlags) {
- case ACF_TYPE_NUMBER:
- case ACF_TYPE_STRING:
- case ACF_TYPE_UNICODE:
- mir_free((wchar_t *)svc.wParam);
- break;
- case ACF_TYPE_STRUCT:
- mir_free((char *)svc.wParam);
- break;
- }
-
- switch (svc.lFlags) {
- case ACF_TYPE_NUMBER:
- case ACF_TYPE_STRING:
- case ACF_TYPE_UNICODE:
- mir_free((wchar_t *)svc.lParam);
- break;
- case ACF_TYPE_STRUCT:
- mir_free((char *)svc.lParam);
- break;
- }
- break;
- }
-}
-
-void ColumnItem::SetSpecialColumns()
-{
- if (setting_type == QST_SETTING) {
- if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group"))
- isGroup = true;
-
- else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW"))
- isContainer = true;
-
- else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId"))
- isXstatus = true;
-
- else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled)
- isClient = true;
- }
- else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER)
- isGender = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// column functions
-
-int ListViewToColumn(int col)
-{
- for (auto &it : g_plugin.m_columns) {
- if (!it->bEnabled)
- continue;
-
- if (col-- <= 0)
- return g_plugin.m_columns.indexOf(&it);
- }
- return -1;
-}
-
-int ColumnToListView(int col)
-{
- int res = -1;
- for (auto &it : g_plugin.m_columns) {
- if (it->bEnabled)
- res++;
-
- if (col-- <= 0)
- break;
- }
- return res;
-}
-
-void LoadDefaultColumns(OBJLIST<ColumnItem> &dst)
-{
- dst.destroy();
-
- auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER);
- pNew->other = QSTO_ACCOUNT;
- dst.insert(pNew);
-
- dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER));
-
- pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID);
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER);
- pNew->bFilter = true;
- pNew->other = QSTO_DISPLAYNAME;
-
- pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME);
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME);
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING);
- pNew->datatype = QSTS_STRING;
- pNew->module = mir_strdup("CList");
- pNew->setting = mir_strdup("Group");
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING);
- pNew->datatype = QSTS_STRING;
- pNew->module = mir_strdup("Tab_SRMsg");
- pNew->setting = mir_strdup("containerW");
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL);
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING);
- pNew->datatype = QSTS_STRING;
- pNew->setting = mir_strdup("MirVer");
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER);
- pNew->other = QSTO_LASTSEEN;
- pNew->dwFlags = 0;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER);
- pNew->other = QSTO_LASTEVENT;
- pNew->dwFlags = 0;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING);
- pNew->datatype = QSTS_TIMESTAMP;
- pNew->setting = mir_strdup("LogonTS");
- pNew->bFilter = true;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER);
- pNew->other = QSTO_METACONTACT;
- pNew->dwFlags = 0;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER);
- pNew->other = QSTO_EVENTCOUNT;
- pNew->dwFlags = 0;
- dst.insert(pNew);
-
- pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING);
- pNew->datatype = QSTS_TIMESTAMP;
- pNew->module = mir_strdup("UserInfo");
- pNew->setting = mir_strdup("ContactAddTime");
- dst.insert(pNew);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// window options
-
-void CMPlugin::LoadOptWnd()
-{
- m_rect.bottom = getDword(so_mbottom);
- m_rect.right = getDword(so_mright);
- m_rect.left = getDword(so_mleft);
- m_rect.top = getDword(so_mtop);
-
- m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
- m_sortOrder = getDword(so_columnsort);
-}
-
-void CMPlugin::SaveOptWnd()
-{
- setDword(so_mbottom, m_rect.bottom);
- setDword(so_mright, m_rect.right);
- setDword(so_mleft, m_rect.left);
- setDword(so_mtop, m_rect.top);
-
- setDword(so_flags, m_flags);
- setDword(so_columnsort, m_sortOrder);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// load options
-
-int CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst)
-{
- m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
- int numCols = getWord(so_numcolumns);
-
- for (int i = 0; i < numCols; i++) {
- auto *pNew = new ColumnItem(nullptr);
- LoadColumn(i, *pNew);
- dst.insert(pNew);
- }
-
- return numCols;
-}
-
-void CMPlugin::LoadColumn(int n, ColumnItem &col)
-{
- char buf[127];
- int offset = mir_snprintf(buf, "%s%d_", so_item, n);
-
- strcpy(buf + offset, so_title); col.title = getWStringA(buf);
- strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf);
- strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf);
- strcpy(buf + offset, so_width); col.width = getWord(buf);
-
- switch (col.setting_type) {
- case QST_SETTING:
- strcpy(buf + offset, so_datatype); col.datatype = getWord(buf);
- strcpy(buf + offset, so_module); col.module = getStringA(buf);
- strcpy(buf + offset, so_setting); col.setting = getStringA(buf);
- break;
-
- case QST_SCRIPT:
- strcpy(buf + offset, so_script); col.script = getWStringA(buf);
- break;
-
- case QST_CONTACTINFO:
- strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf);
- break;
-
- case QST_SERVICE:
- offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
- strcpy(buf + offset, so_service); col.svc.service = getStringA(buf);
- strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf);
- if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) {
- col.setting_type = QST_OTHER;
- col.other = QSTO_ACCOUNT;
- break;
- }
-
- strcpy(buf + offset, so_wparam);
- LoadParamValue(buf, col.svc.wFlags, col.svc.wParam);
-
- strcpy(buf + offset, so_lparam);
- LoadParamValue(buf, col.svc.lFlags, col.svc.lParam);
- break;
-
- case QST_OTHER:
- strcpy(buf + offset, so_other); col.other = getWord(buf);
- break;
- }
-}
-
-void CMPlugin::LoadParamValue(char *buf, uint32_t &dwFlags, LPARAM &dwWalue)
-{
- char *pEnd = buf + strlen(buf);
- strcpy(pEnd, "flags"); dwFlags = getDword(buf);
-
- strcpy(pEnd, "value");
- switch (dwFlags) {
- case ACF_TYPE_NUMBER:
- case ACF_TYPE_STRING:
- case ACF_TYPE_UNICODE:
- dwWalue = LPARAM(getWStringA(buf));
- break;
-
- case ACF_TYPE_STRUCT:
- dwWalue = LPARAM(getStringA(buf));
- break;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// save options
-
-static int ListSettings(const char *szSetting, void *param)
-{
- if (!memcmp(szSetting, so_item, 4)) {
- auto *pList = (LIST<char>*)param;
- pList->insert(mir_strdup(szSetting));
- }
- return 0;
-}
-
-void CMPlugin::SaveOptions()
-{
- // remove old settings
- LIST<char> settings(30);
- db_enum_settings(0, ListSettings, MODULENAME, &settings);
-
- for (auto &it : settings) {
- delSetting(it);
- mir_free(it);
- }
-
- // write new settings
- setDword(so_flags, m_flags);
- setWord(so_numcolumns, m_columns.getCount());
-
- int i = 0;
- for (auto &it : m_columns)
- SaveColumn(i++, *it);
-}
-
-void CMPlugin::SaveColumn(int n, const ColumnItem &col)
-{
- char buf[127];
- int offset = mir_snprintf(buf, "%s%d_", so_item, n);
-
- strcpy(buf + offset, so_title); setWString(buf, col.title);
- strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type);
- strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags);
- strcpy(buf + offset, so_width); setWord(buf, col.width);
-
- switch (col.setting_type) {
- case QST_SETTING:
- strcpy(buf + offset, so_datatype); setWord(buf, col.datatype);
- strcpy(buf + offset, so_module); setString(buf, col.module);
- strcpy(buf + offset, so_setting); setString(buf, col.setting);
- break;
-
- case QST_SCRIPT:
- strcpy(buf + offset, so_script); setWString(buf, col.script);
- break;
-
- case QST_CONTACTINFO:
- strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype);
- break;
-
- case QST_SERVICE:
- offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
- strcpy(buf + offset, so_service); setString(buf, col.svc.service);
- strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags);
-
- strcpy(buf + offset, so_wparam);
- SaveParamValue(buf, col.svc.wFlags, col.svc.wParam);
-
- strcpy(buf + offset, so_lparam);
- SaveParamValue(buf, col.svc.lFlags, col.svc.lParam);
- break;
-
- case QST_OTHER:
- strcpy(buf + offset, so_other); setWord(buf, col.other);
- break;
- }
-}
-
-void CMPlugin::SaveParamValue(char *buf, uint32_t flags, LPARAM value)
-{
- char *pEnd = buf + strlen(buf);
- strcpy(pEnd, "flags"); setDword(buf, flags);
-
- strcpy(pEnd, "value");
- switch (flags) {
- case ACF_TYPE_NUMBER:
- case ACF_TYPE_STRING:
- case ACF_TYPE_UNICODE:
- setWString(buf, (wchar_t *)value);
- break;
-
- case ACF_TYPE_STRUCT:
- setString(buf, (char *)value);
- break;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-const wchar_t* cnf2str(int cnf)
-{
- switch (cnf) {
- case CNF_FIRSTNAME: return TranslateT("First name");
- case CNF_LASTNAME: return TranslateT("Last name");
- case CNF_NICK: return TranslateT("Nick");
- case CNF_CUSTOMNICK: return TranslateT("Custom nick");
- case CNF_EMAIL: return TranslateT("Email");
- case CNF_CITY: return TranslateT("City");
- case CNF_STATE: return TranslateT("State");
- case CNF_COUNTRY: return TranslateT("Country");
- case CNF_PHONE: return TranslateT("Phone");
- case CNF_HOMEPAGE: return TranslateT("Homepage");
- case CNF_ABOUT: return TranslateT("About");
- case CNF_GENDER: return TranslateT("Gender");
- case CNF_AGE: return TranslateT("Age");
- case CNF_FIRSTLAST: return TranslateT("First name/Last name");
- case CNF_UNIQUEID: return TranslateT("Unique ID");
- case CNF_FAX: return TranslateT("Fax");
- case CNF_CELLULAR: return TranslateT("Cellular");
- case CNF_TIMEZONE: return TranslateT("Time zone");
- case CNF_MYNOTES: return TranslateT("My notes");
- case CNF_BIRTHDAY: return TranslateT("Birth day");
- case CNF_BIRTHMONTH: return TranslateT("Birth month");
- case CNF_BIRTHYEAR: return TranslateT("Birth year");
- case CNF_STREET: return TranslateT("Street");
- case CNF_ZIP: return TranslateT("ZIP code");
- case CNF_LANGUAGE1: return TranslateT("Language #1");
- case CNF_LANGUAGE2: return TranslateT("Language #2");
- case CNF_LANGUAGE3: return TranslateT("Language #3");
- case CNF_CONAME: return TranslateT("Company name");
- case CNF_CODEPT: return TranslateT("Company department");
- case CNF_COPOSITION: return TranslateT("Company position");
- case CNF_COSTREET: return TranslateT("Company street");
- case CNF_COCITY: return TranslateT("Company city");
- case CNF_COSTATE: return TranslateT("Company state");
- case CNF_COZIP: return TranslateT("Company ZIP");
- case CNF_COCOUNTRY: return TranslateT("Company country");
- case CNF_COHOMEPAGE: return TranslateT("Company homepage");
- case CNF_DISPLAYUID: return TranslateT("Display ID");
- }
- return nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// formatters
-
-wchar_t* BuildLastSeenTime(uint32_t ts)
-{
- int year = ts / (60 * 24 * 31 * 356);
- if (year == 0)
- return nullptr;
-
- year += 1980; ts = ts % (60 * 24 * 31 * 356);
-
- int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31);
- int day = ts / (60 * 24); ts = ts % (60 * 24);
- int hours = ts / 60;
- int mins = ts % 60;
-
- return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach();
-}
-
-uint32_t BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule)
-{
- int year = db_get_w(hContact, szModule, "Year");
- if (year == 0)
- return 0;
-
- int day = db_get_w(hContact, szModule, "Day");
- int month = db_get_w(hContact, szModule, "Month");
- int hours = db_get_w(hContact, szModule, "Hours");
- int minutes = db_get_w(hContact, szModule, "Minutes");
-
- return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes;
-}
-
-wchar_t* TimeToStrW(uint32_t timestamp)
-{
- wchar_t buf[63];
- TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf));
- return mir_wstrdup(buf);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void SnapToScreen(RECT &rc)
-{
- int left = GetSystemMetrics(SM_XVIRTUALSCREEN);
- int top = GetSystemMetrics(SM_YVIRTUALSCREEN);
- int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left;
- int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top;
- if (rc.right > right)
- rc.right = right;
- if (rc.bottom > bottom)
- rc.bottom = bottom;
- if (rc.left < left)
- rc.left = left;
- if (rc.top < top)
- rc.top = top;
-}
+/*
+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"
+
+#define ACF_TYPE_NUMBER 0x00 // Parameter is number
+#define ACF_TYPE_STRING 0x01 // Parameter is ANSI String
+#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string
+#define ACF_TYPE_STRUCT 0x03 // Parameter is (result is in) structure
+#define ACF_TYPE_PARAM 0x08 // Parameter is Call parameter
+#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window
+#define ACF_TYPE_RESULT 0x0A // Parameter is previous action result
+#define ACF_TYPE_MASK 0x0F // parameter/result type mask
+
+ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) :
+ title(mir_wstrdup(pwszTitle)),
+ width(_width),
+ setting_type(_setting_type)
+{
+ bEnabled = true;
+}
+
+ColumnItem::ColumnItem(const ColumnItem &src)
+{
+ memcpy(this, &src, sizeof(ColumnItem));
+
+ title = mir_wstrdup(title);
+
+ switch (setting_type) {
+ case QST_SETTING:
+ module = mir_strdup(module);
+ setting = mir_strdup(setting);
+ break;
+
+ case QST_SCRIPT:
+ script = mir_wstrdup(script);
+ break;
+
+ case QST_SERVICE:
+ svc.service = mir_strdup(svc.service);
+ switch (svc.wFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam);
+ break;
+ }
+
+ switch (svc.lFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam);
+ break;
+ }
+ break;
+ }
+}
+
+ColumnItem::~ColumnItem()
+{
+ mir_free(title);
+
+ switch (setting_type) {
+ case QST_SETTING:
+ mir_free(module);
+ mir_free(setting);
+ break;
+
+ case QST_SCRIPT:
+ mir_free(script);
+ break;
+
+ case QST_SERVICE:
+ mir_free(svc.service);
+ switch (svc.wFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ mir_free((wchar_t *)svc.wParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ mir_free((char *)svc.wParam);
+ break;
+ }
+
+ switch (svc.lFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ mir_free((wchar_t *)svc.lParam);
+ break;
+ case ACF_TYPE_STRUCT:
+ mir_free((char *)svc.lParam);
+ break;
+ }
+ break;
+ }
+}
+
+void ColumnItem::SetSpecialColumns()
+{
+ if (setting_type == QST_SETTING) {
+ if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group"))
+ isGroup = true;
+
+ else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW"))
+ isContainer = true;
+
+ else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId"))
+ isXstatus = true;
+
+ else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled)
+ isClient = true;
+ }
+ else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER)
+ isGender = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// column functions
+
+int ListViewToColumn(int col)
+{
+ for (auto &it : g_plugin.m_columns) {
+ if (!it->bEnabled)
+ continue;
+
+ if (col-- <= 0)
+ return g_plugin.m_columns.indexOf(&it);
+ }
+ return -1;
+}
+
+int ColumnToListView(int col)
+{
+ int res = -1;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled)
+ res++;
+
+ if (col-- <= 0)
+ break;
+ }
+ return res;
+}
+
+void LoadDefaultColumns(OBJLIST<ColumnItem> &dst)
+{
+ dst.destroy();
+
+ auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER);
+ pNew->other = QSTO_ACCOUNT;
+ dst.insert(pNew);
+
+ dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER));
+
+ pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER);
+ pNew->bFilter = true;
+ pNew->other = QSTO_DISPLAYNAME;
+
+ pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING);
+ pNew->datatype = QSTS_STRING;
+ pNew->module = mir_strdup("CList");
+ pNew->setting = mir_strdup("Group");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING);
+ pNew->datatype = QSTS_STRING;
+ pNew->module = mir_strdup("Tab_SRMsg");
+ pNew->setting = mir_strdup("containerW");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL);
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING);
+ pNew->datatype = QSTS_STRING;
+ pNew->setting = mir_strdup("MirVer");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER);
+ pNew->other = QSTO_LASTSEEN;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER);
+ pNew->other = QSTO_LASTEVENT;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING);
+ pNew->datatype = QSTS_TIMESTAMP;
+ pNew->setting = mir_strdup("LogonTS");
+ pNew->bFilter = true;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER);
+ pNew->other = QSTO_METACONTACT;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER);
+ pNew->other = QSTO_EVENTCOUNT;
+ pNew->dwFlags = 0;
+ dst.insert(pNew);
+
+ pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING);
+ pNew->datatype = QSTS_TIMESTAMP;
+ pNew->module = mir_strdup("UserInfo");
+ pNew->setting = mir_strdup("ContactAddTime");
+ dst.insert(pNew);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// window options
+
+void CMPlugin::LoadOptWnd()
+{
+ m_rect.bottom = getDword(so_mbottom);
+ m_rect.right = getDword(so_mright);
+ m_rect.left = getDword(so_mleft);
+ m_rect.top = getDword(so_mtop);
+
+ m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
+ m_sortOrder = getDword(so_columnsort);
+}
+
+void CMPlugin::SaveOptWnd()
+{
+ setDword(so_mbottom, m_rect.bottom);
+ setDword(so_mright, m_rect.right);
+ setDword(so_mleft, m_rect.left);
+ setDword(so_mtop, m_rect.top);
+
+ setDword(so_flags, m_flags);
+ setDword(so_columnsort, m_sortOrder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// load options
+
+int CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst)
+{
+ m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
+ int numCols = getWord(so_numcolumns);
+
+ for (int i = 0; i < numCols; i++) {
+ auto *pNew = new ColumnItem(nullptr);
+ LoadColumn(i, *pNew);
+ dst.insert(pNew);
+ }
+
+ return numCols;
+}
+
+void CMPlugin::LoadColumn(int n, ColumnItem &col)
+{
+ char buf[127];
+ int offset = mir_snprintf(buf, "%s%d_", so_item, n);
+
+ strcpy(buf + offset, so_title); col.title = getWStringA(buf);
+ strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf);
+ strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf);
+ strcpy(buf + offset, so_width); col.width = getWord(buf);
+
+ switch (col.setting_type) {
+ case QST_SETTING:
+ strcpy(buf + offset, so_datatype); col.datatype = getWord(buf);
+ strcpy(buf + offset, so_module); col.module = getStringA(buf);
+ strcpy(buf + offset, so_setting); col.setting = getStringA(buf);
+ break;
+
+ case QST_SCRIPT:
+ strcpy(buf + offset, so_script); col.script = getWStringA(buf);
+ break;
+
+ case QST_CONTACTINFO:
+ strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf);
+ break;
+
+ case QST_SERVICE:
+ offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
+ strcpy(buf + offset, so_service); col.svc.service = getStringA(buf);
+ strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf);
+ if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) {
+ col.setting_type = QST_OTHER;
+ col.other = QSTO_ACCOUNT;
+ break;
+ }
+
+ strcpy(buf + offset, so_wparam);
+ LoadParamValue(buf, col.svc.wFlags, col.svc.wParam);
+
+ strcpy(buf + offset, so_lparam);
+ LoadParamValue(buf, col.svc.lFlags, col.svc.lParam);
+ break;
+
+ case QST_OTHER:
+ strcpy(buf + offset, so_other); col.other = getWord(buf);
+ break;
+ }
+}
+
+void CMPlugin::LoadParamValue(char *buf, uint32_t &dwFlags, LPARAM &dwWalue)
+{
+ char *pEnd = buf + strlen(buf);
+ strcpy(pEnd, "flags"); dwFlags = getDword(buf);
+
+ strcpy(pEnd, "value");
+ switch (dwFlags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ dwWalue = LPARAM(getWStringA(buf));
+ break;
+
+ case ACF_TYPE_STRUCT:
+ dwWalue = LPARAM(getStringA(buf));
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// save options
+
+static int ListSettings(const char *szSetting, void *param)
+{
+ if (!memcmp(szSetting, so_item, 4)) {
+ auto *pList = (LIST<char>*)param;
+ pList->insert(mir_strdup(szSetting));
+ }
+ return 0;
+}
+
+void CMPlugin::SaveOptions()
+{
+ // remove old settings
+ LIST<char> settings(30);
+ db_enum_settings(0, ListSettings, MODULENAME, &settings);
+
+ for (auto &it : settings) {
+ delSetting(it);
+ mir_free(it);
+ }
+
+ // write new settings
+ setDword(so_flags, m_flags);
+ setWord(so_numcolumns, m_columns.getCount());
+
+ int i = 0;
+ for (auto &it : m_columns)
+ SaveColumn(i++, *it);
+}
+
+void CMPlugin::SaveColumn(int n, const ColumnItem &col)
+{
+ char buf[127];
+ int offset = mir_snprintf(buf, "%s%d_", so_item, n);
+
+ strcpy(buf + offset, so_title); setWString(buf, col.title);
+ strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type);
+ strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags);
+ strcpy(buf + offset, so_width); setWord(buf, col.width);
+
+ switch (col.setting_type) {
+ case QST_SETTING:
+ strcpy(buf + offset, so_datatype); setWord(buf, col.datatype);
+ strcpy(buf + offset, so_module); setString(buf, col.module);
+ strcpy(buf + offset, so_setting); setString(buf, col.setting);
+ break;
+
+ case QST_SCRIPT:
+ strcpy(buf + offset, so_script); setWString(buf, col.script);
+ break;
+
+ case QST_CONTACTINFO:
+ strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype);
+ break;
+
+ case QST_SERVICE:
+ offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
+ strcpy(buf + offset, so_service); setString(buf, col.svc.service);
+ strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags);
+
+ strcpy(buf + offset, so_wparam);
+ SaveParamValue(buf, col.svc.wFlags, col.svc.wParam);
+
+ strcpy(buf + offset, so_lparam);
+ SaveParamValue(buf, col.svc.lFlags, col.svc.lParam);
+ break;
+
+ case QST_OTHER:
+ strcpy(buf + offset, so_other); setWord(buf, col.other);
+ break;
+ }
+}
+
+void CMPlugin::SaveParamValue(char *buf, uint32_t flags, LPARAM value)
+{
+ char *pEnd = buf + strlen(buf);
+ strcpy(pEnd, "flags"); setDword(buf, flags);
+
+ strcpy(pEnd, "value");
+ switch (flags) {
+ case ACF_TYPE_NUMBER:
+ case ACF_TYPE_STRING:
+ case ACF_TYPE_UNICODE:
+ setWString(buf, (wchar_t *)value);
+ break;
+
+ case ACF_TYPE_STRUCT:
+ setString(buf, (char *)value);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+const wchar_t* cnf2str(int cnf)
+{
+ switch (cnf) {
+ case CNF_FIRSTNAME: return TranslateT("First name");
+ case CNF_LASTNAME: return TranslateT("Last name");
+ case CNF_NICK: return TranslateT("Nick");
+ case CNF_CUSTOMNICK: return TranslateT("Custom nick");
+ case CNF_EMAIL: return TranslateT("Email");
+ case CNF_CITY: return TranslateT("City");
+ case CNF_STATE: return TranslateT("State");
+ case CNF_COUNTRY: return TranslateT("Country");
+ case CNF_PHONE: return TranslateT("Phone");
+ case CNF_HOMEPAGE: return TranslateT("Homepage");
+ case CNF_ABOUT: return TranslateT("About");
+ case CNF_GENDER: return TranslateT("Gender");
+ case CNF_AGE: return TranslateT("Age");
+ case CNF_FIRSTLAST: return TranslateT("First name/Last name");
+ case CNF_UNIQUEID: return TranslateT("Unique ID");
+ case CNF_FAX: return TranslateT("Fax");
+ case CNF_CELLULAR: return TranslateT("Cellular");
+ case CNF_TIMEZONE: return TranslateT("Time zone");
+ case CNF_MYNOTES: return TranslateT("My notes");
+ case CNF_BIRTHDAY: return TranslateT("Birth day");
+ case CNF_BIRTHMONTH: return TranslateT("Birth month");
+ case CNF_BIRTHYEAR: return TranslateT("Birth year");
+ case CNF_STREET: return TranslateT("Street");
+ case CNF_ZIP: return TranslateT("ZIP code");
+ case CNF_LANGUAGE1: return TranslateT("Language #1");
+ case CNF_LANGUAGE2: return TranslateT("Language #2");
+ case CNF_LANGUAGE3: return TranslateT("Language #3");
+ case CNF_CONAME: return TranslateT("Company name");
+ case CNF_CODEPT: return TranslateT("Company department");
+ case CNF_COPOSITION: return TranslateT("Company position");
+ case CNF_COSTREET: return TranslateT("Company street");
+ case CNF_COCITY: return TranslateT("Company city");
+ case CNF_COSTATE: return TranslateT("Company state");
+ case CNF_COZIP: return TranslateT("Company ZIP");
+ case CNF_COCOUNTRY: return TranslateT("Company country");
+ case CNF_COHOMEPAGE: return TranslateT("Company homepage");
+ case CNF_DISPLAYUID: return TranslateT("Display ID");
+ }
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// formatters
+
+wchar_t* BuildLastSeenTime(uint32_t ts)
+{
+ int year = ts / (60 * 24 * 31 * 356);
+ if (year == 0)
+ return nullptr;
+
+ year += 1980; ts = ts % (60 * 24 * 31 * 356);
+
+ int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31);
+ int day = ts / (60 * 24); ts = ts % (60 * 24);
+ int hours = ts / 60;
+ int mins = ts % 60;
+
+ return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach();
+}
+
+uint32_t BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule)
+{
+ int year = db_get_w(hContact, szModule, "Year");
+ if (year == 0)
+ return 0;
+
+ int day = db_get_w(hContact, szModule, "Day");
+ int month = db_get_w(hContact, szModule, "Month");
+ int hours = db_get_w(hContact, szModule, "Hours");
+ int minutes = db_get_w(hContact, szModule, "Minutes");
+
+ return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes;
+}
+
+wchar_t* TimeToStrW(uint32_t timestamp)
+{
+ wchar_t buf[63];
+ TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf));
+ return mir_wstrdup(buf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SnapToScreen(RECT &rc)
+{
+ int left = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ int top = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left;
+ int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top;
+ if (rc.right > right)
+ rc.right = right;
+ if (rc.bottom > bottom)
+ rc.bottom = bottom;
+ if (rc.left < left)
+ rc.left = left;
+ if (rc.top < top)
+ rc.top = top;
+}
diff --git a/plugins/QuickSearch/src/version.h b/plugins/QuickSearch/src/version.h
index c054045e77..d1f948c4c1 100644
--- a/plugins/QuickSearch/src/version.h
+++ b/plugins/QuickSearch/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "This plugin allows you to quick search for nickname, firstname, lastname, email, uin in your contact list."
#define __AUTHOR "Bethoven, Awkward"
#define __AUTHORWEB "https://miranda-ng.org/p/QuickSearch"
-#define __COPYRIGHT "© 2004-05 Bethoven; 2006-13 Awkward; 2014-22 Miranda NG team"
+#define __COPYRIGHT "© 2004-05 Bethoven; 2006-13 Awkward; 2014-23 Miranda NG team"
diff --git a/plugins/QuickSearch/src/window.cpp b/plugins/QuickSearch/src/window.cpp
index 891d39aa92..8624be7d98 100644
--- a/plugins/QuickSearch/src/window.cpp
+++ b/plugins/QuickSearch/src/window.cpp
@@ -1,876 +1,876 @@
-/*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it &/|
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY | FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-#define IDM_STAYONTOP (WM_USER+1)
-
-static QSMainDlg *g_pDlg = 0;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// edit control window procedure
-
-static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
- if (pDlg)
- if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
- return res;
-
- return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
-}
-
-INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
-{
- switch (msg) {
- case WM_CHAR:
- if (wParam == 27) // Escape
- Close();
- break;
-
- case WM_KEYUP:
- if (wParam == VK_RETURN) {
- if (m_grid.GetSelectedCount() == 1)
- ShowContactMsgDlg(GetFocusedContact());
- return 0;
- }
- break;
-
- case WM_KEYDOWN:
- int count = m_grid.GetItemCount();
- int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
- int next = -1;
- if (count > 0) {
- switch (wParam) {
- case VK_UP:
- if (current > 0)
- next = current - 1;
- break;
-
- case VK_DOWN:
- if (current < count - 1)
- next = current + 1;
- break;
-
- case VK_F5:
- onClick_Refresh(0);
- return 0;
-
- case VK_NEXT:
- case VK_PRIOR:
- int perpage = m_grid.GetCountPerPage();
- if (wParam == VK_NEXT)
- next = min(current + perpage, count);
- else
- next = max(current - perpage, 0);
- break;
- }
- }
-
- if (next >= 0) {
- m_grid.SetItemState(-1, 0, LVIS_SELECTED);
- m_grid.SetCurSel(next);
- m_grid.EnsureVisible(next, FALSE);
- }
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// list header window procedure
-
-static void MakeColumnMenu()
-{
- HMENU hMenu = CreatePopupMenu();
-
- for (auto &it : g_plugin.m_columns) {
- int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
- AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
- }
-
- POINT pt;
- GetCursorPos(&pt);
-
- int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
- if (id >= 100)
- g_pDlg->ToggleColumn(id - 100);
-
- DestroyMenu(hMenu);
-}
-
-void QSMainDlg::ToggleColumn(int col)
-{
- auto &pCol = g_plugin.m_columns[col];
-
- if (!pCol.bEnabled) { // show column
- pCol.bEnabled = true;
-
- if (!pCol.bInit) {
- for (auto &it : m_rows)
- it->pValues[col].LoadOneItem(it->hContact, pCol, this);
- pCol.bInit = true;
- }
-
- // screen
- int lvcol = ColumnToListView(col);
- AddColumn(lvcol, &pCol);
-
- int nCount = m_grid.GetItemCount();
- for (int i = 0; i < nCount; i++) {
- auto *pRow = GetRow(i);
-
- LV_ITEMW li;
- li.iItem = i;
- li.iSubItem = lvcol;
- li.mask = LVIF_TEXT;
- li.pszText = pRow->pValues[col].text;
- if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
- li.mask |= LVIF_IMAGE;
- m_grid.SetItem(&li);
- }
- }
- else { // hide column
- int cnt = 0;
- for (auto &it : g_plugin.m_columns)
- if (it->bEnabled)
- cnt++;
-
- // keep at least one visible column (1 + this)
- if (cnt > 2) {
- m_grid.DeleteColumn(col);
- pCol.bEnabled = false;
- }
- }
-}
-
-static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- switch (msg) {
- case WM_RBUTTONUP:
- return 0;
-
- case WM_RBUTTONDOWN:
- MakeColumnMenu();
- break;
- }
-
- return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// grid list window procedure
-
-static int OldHSubItem = 0, OldHItem = 0;
-
-static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
- if (pDlg)
- if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
- return res;
-
- return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
-}
-
-INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
-{
- LV_HITTESTINFO pinfo;
-
- switch (msg) {
- case WM_CHAR:
- switch (wParam) { // ESC
- case 27:
- Close();
- break;
-
- case 1: // Cltr+A
- m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
- break;
-
- case 3: // Ctrl-C
- CopyMultiLines();
- break;
-
- case 8: // backspace
- edtFilter.SendMsg(msg, wParam, lParam);
- break;
- }
-
- if (wParam >= 32 && wParam <= 127) // letters
- edtFilter.SendMsg(msg, wParam, lParam);
- break;
-
- case WM_MOUSEMOVE:
- pinfo.pt.x = LOWORD(lParam);
- pinfo.pt.y = HIWORD(lParam);
- pinfo.flags = 0;
- if (m_grid.SubItemHitTest(&pinfo) == -1)
- break;
-
- if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
- OldHSubItem = pinfo.iSubItem;
- OldHItem = pinfo.iItem;
-
- if (g_bTipperInstalled) {
- if (TTShowed) {
- TTShowed = false;
- Tipper_Hide();
- }
- m_hover.Stop();
-
- if (OldHSubItem == 0)
- m_hover.Start(450);
- }
-
- TOOLINFOW ti = {};
- ti.cbSize = sizeof(ti);
- ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
- ti.hwnd = m_hwnd;
- ti.uId = LPARAM(m_hwnd);
-
- int num = ListViewToColumn(OldHSubItem);
- auto &pCol = g_plugin.m_columns[num];
- if (pCol.isXstatus || pCol.isGender) {
- auto *pRow = GetRow(OldHItem);
- if (pCol.isGender) {
- switch (pRow->pValues[num].data) {
- case 'M': ti.lpszText = TranslateT("Male"); break;
- case 'F': ti.lpszText = TranslateT("Female"); break;
- default: ti.lpszText = TranslateT("Unknown"); break;
- }
- }
- else {
- wchar_t buf[256];
- mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
- int iStatus = _wtoi(buf);
-
- CUSTOM_STATUS ics = {};
- ics.cbSize = sizeof(ics);
- ics.status = &iStatus;
- ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
- ics.pwszName = buf;
- CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
- ti.lpszText = TranslateW(buf);
- }
- }
-
- SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
- }
- break;
-
- case WM_KEYUP:
- switch (wParam) {
- case VK_RETURN:
- if (m_grid.GetSelectedCount() == 1)
- ShowContactMsgDlg(GetFocusedContact());
- break;
-
- case VK_INSERT:
- CallService(MS_FINDADD_FINDADD, 0, 0);
- break;
-
- case VK_DELETE:
- lParam = m_grid.GetSelectedCount();
- if (lParam > 1)
- DeleteByList();
- else if (lParam == 1)
- DeleteOneContact(GetFocusedContact());
- break;
-
- case VK_F5:
- onClick_Refresh(0);
- break;
- }
- break;
-
- case WM_NOTIFY:
- if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
- NMHEADER *pdhr = (NMHEADER *)lParam;
- if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
- int i = ListViewToColumn(pdhr->iItem);
- auto &pCol = g_plugin.m_columns[i];
-
- if (pdhr->pitem->fmt & HDF_CHECKED) {
- pCol.bFilter = false;
- pdhr->pitem->fmt &= ~HDF_CHECKED;
- }
- else {
- pCol.bFilter = true;
- pdhr->pitem->fmt |= HDF_CHECKED;
- }
-
- SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
- FillGrid();
- return TRUE;
- }
- }
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// QSMainDlg class implementation
-
-static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
-{
- if (p1->bAccDel != p2->bAccDel)
- return (p1->bAccDel) ? 1 : -1;
-
- if (p1->bAccOff != p2->bAccOff)
- return (p1->bAccOff) ? 1 : -1;
-
- return mir_strcmp(p1->szProto, p2->szProto);
-}
-
-QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
- CDlgBase(g_plugin, IDD_MAIN),
- m_rows(50),
- m_patterns(1),
- m_sbdata(10, CompareSb),
- m_grid(this, IDC_LIST),
- m_hover(this, 10),
- cmbProto(this, IDC_CB_PROTOCOLS),
- edtFilter(this, IDC_E_SEARCHTEXT),
- btnRefresh(this, IDC_REFRESH),
- chkColorize(this, IDC_CH_COLORIZE),
- chkShowOffline(this, IDC_CH_SHOWOFFLINE)
-{
- SetMinSize(300, 160);
-
- if (pwszPattern)
- m_wszPatternBuf = mir_wstrdup(pwszPattern);
- else if (g_plugin.m_flags & QSO_SAVEPATTERN)
- m_wszPatternBuf = g_plugin.getWStringA("pattern");
-
- m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
-
- m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
- m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
- m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
- m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
-
- btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
-
- cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
-
- edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
- chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
- chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
-}
-
-bool QSMainDlg::OnInitDialog()
-{
- g_pDlg = this;
- mnuhandle = 0;
-
- SetCaption(TranslateT("Quick Search"));
-
- hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
-
- HMENU smenu = GetSystemMenu(m_hwnd, false);
- InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
- InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
-
- if (g_plugin.m_flags & QSO_STAYONTOP) {
- CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
- SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- }
-
- chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
-
- szFilterProto = nullptr; // display all protocols
- if (g_plugin.m_flags & QSO_SHOWOFFLINE)
- bShowOffline = true;
-
- chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
-
- // Window
- INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
- if (g_plugin.m_flags & QSO_TOOLSTYLE)
- tmp |= WS_EX_TOOLWINDOW;
- else
- tmp &= ~WS_EX_TOOLWINDOW;
- SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
-
- SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
-
- // ListView
- m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
-
- tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
- LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
- if (g_plugin.m_flags & QSO_DRAWGRID)
- tmp |= LVS_EX_GRIDLINES;
- m_grid.SetExtendedListViewStyle(tmp);
-
- // ListView header
- HWND header = m_grid.GetHeader();
- SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
-
- mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
-
- SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
- mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
-
- SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
- mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
-
- FillProtoCombo();
-
- PrepareTable();
-
- if (m_wszPatternBuf) {
- edtFilter.SetText(m_wszPatternBuf);
- MakePattern(m_wszPatternBuf);
- }
- FillGrid();
-
- // Show sorting column
- HDITEM hdi = {};
- hdi.mask = HDI_FORMAT;
- SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
- if (g_plugin.m_flags & QSO_SORTASC)
- hdi.fmt |= HDF_SORTUP;
- else
- hdi.fmt |= HDF_SORTDOWN;
- SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
- RECT rc = g_plugin.m_rect;
- ::SnapToScreen(rc);
- ::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
-
- HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
-
- TOOLINFOW ti;
- ti.cbSize = sizeof(ti);
- ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
- ti.hwnd = m_hwnd;
- ti.uId = UINT_PTR(m_grid.GetHwnd());
- SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
-
- hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
- hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
- hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
- return true;
-}
-
-void QSMainDlg::OnDestroy()
-{
- if (mnuhandle)
- Menu_RemoveItem(mnuhandle);
-
- UnhookEvent(hAdd);
- UnhookEvent(hDelete);
- UnhookEvent(hChange);
-
- g_pDlg = nullptr;
-
- RECT rc;
- GetWindowRect(m_hwnd, &rc);
- CopyRect(&g_plugin.m_rect, &rc);
-
- // save column width/order
- SaveColumnOrder();
-
- g_plugin.SaveOptWnd();
-
- m_grid.SetImageList(0, LVSIL_SMALL);
-
- if (g_plugin.m_flags & QSO_SAVEPATTERN)
- g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
-
- m_rows.destroy();
- m_patterns.destroy();
-}
-
-int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
-{
- switch (urc->wId) {
- case IDC_REFRESH:
- return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
-
- case IDC_E_SEARCHTEXT:
- return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
-
- case IDC_LIST:
- return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
-
- case IDC_STATUSBAR:
- return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
- }
- return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// event handlers
-
-INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
-{
- auto *pRow = new CRowItem(hContact, this);
- m_rows.insert(pRow);
- AddContactToList(hContact, pRow);
- ProcessLine(pRow);
- Sort();
- UpdateSB();
- return 0;
-}
-
-INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
-{
- int idx = -1;
- CRowItem *pRow = 0;
-
- for (auto &it : m_rows)
- if (it->hContact == hContact) {
- pRow = it;
- idx = m_rows.indexOf(&it);
- break;
- }
-
- if (idx == -1)
- return 0;
-
- int iItem = FindItem(pRow);
- if (iItem != -1)
- m_grid.DeleteItem(iItem);
-
- m_rows.remove(idx);
- UpdateSB();
- return 0;
-}
-
-INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
-{
- auto *pRow = FindRow(hContact);
- if (pRow == nullptr)
- return 0;
-
- int oldStatus = pRow->status;
- int newStatus = Contact::GetStatus(hContact);
- pRow->status = newStatus;
-
- if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
- ChangeStatusPicture(pRow, hContact, lParam);
- else if (oldStatus != ID_STATUS_OFFLINE) {
- if (g_plugin.m_flags & QSO_SHOWOFFLINE)
- ChangeStatusPicture(pRow, hContact, lParam);
- else
- ProcessLine(pRow, true);
- }
- else if (newStatus != ID_STATUS_OFFLINE) {
- if (g_plugin.m_flags & QSO_SHOWOFFLINE)
- ChangeStatusPicture(pRow, hContact, lParam);
- else {
- pRow->bActive = false;
- m_grid.DeleteItem(FindItem(pRow));
- }
- }
-
- if (g_plugin.m_flags & QSO_SORTBYSTATUS)
- Sort();
-
- UpdateSB();
- return 0;
-}
-
-INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
-{
- if (wParam == IDM_STAYONTOP) {
- int h; HWND w;
- if (g_plugin.m_flags & QSO_STAYONTOP) {
- h = MF_BYCOMMAND | MF_UNCHECKED;
- w = HWND_NOTOPMOST;
- }
- else {
- h = MF_BYCOMMAND | MF_CHECKED;
- w = HWND_TOPMOST;
- }
- CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
- SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- g_plugin.m_flags ^= QSO_STAYONTOP;
- }
- return 0;
-}
-
-INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
-{
- if (g_bTipperInstalled) {
- RECT rc;
- GetWindowRect(m_grid.GetHwnd(), &rc);
-
- POINT pt = { LOWORD(lParam), HIWORD(lParam) };
- ClientToScreen(m_hwnd, &pt);
- if (!PtInRect(&rc, pt)) {
- if (TTShowed) {
- TTShowed = false;
- CallService(MS_TIPPER_HIDETIP, 0, 0);
- }
- }
-
- m_hover.Stop();
- }
- return 0;
-}
-
-INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
-{
- if (wParam == VK_F5)
- PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
- return 0;
-}
-
-void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
-{
- int w = m_grid.GetSelectedCount();
- if (w > 1)
- ShowMultiPopup(w);
- else
- ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
-}
-
-void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
-{
- LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
- if (lParam == -1 || lParam == 0)
- szFilterProto = nullptr;
- else
- szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
-
- AdvancedFilter();
-}
-
-void QSMainDlg::onChange_Filter(CCtrlEdit *)
-{
- if (!m_bInitialized)
- return;
-
- MakePattern(ptrW(edtFilter.GetText()));
- FillGrid();
-}
-
-void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
-{
- if (chkShowOffline.IsChecked()) {
- g_plugin.m_flags |= QSO_SHOWOFFLINE;
- bShowOffline = true;
- }
- else {
- g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
- bShowOffline = false;
- }
-
- AdvancedFilter();
-}
-
-void QSMainDlg::onChange_Colorize(CCtrlCheck *)
-{
- if (chkColorize.IsChecked())
- g_plugin.m_flags |= QSO_COLORIZE;
- else
- g_plugin.m_flags &= ~QSO_COLORIZE;
- RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
-}
-
-void QSMainDlg::onClick_Refresh(CCtrlButton *)
-{
- m_rows.destroy();
- PrepareToFill();
- PrepareTable(true);
- FillGrid();
-}
-
-void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
-{
- HWND header = m_grid.GetHeader();
-
- // clear sort mark
- HDITEM hdi = {};
- hdi.mask = HDI_FORMAT;
- SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
- hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
- SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
- if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
- g_plugin.m_flags |= QSO_SORTASC;
- g_plugin.m_sortOrder = ev->nmlv->iSubItem;
- }
- else g_plugin.m_flags ^= QSO_SORTASC;;
-
- // set new sort mark
- SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
- if ((g_plugin.m_flags & QSO_SORTASC) == 0)
- hdi.fmt |= HDF_SORTDOWN;
- else
- hdi.fmt &= ~HDF_SORTUP;
- SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
- Sort();
-}
-
-void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
-{
- ShowContactMsgDlg(GetFocusedContact());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
-{
- LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
- HICON h;
- RECT rc;
- auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
-
- int result = CDRF_DODEFAULT;
- switch (lplvcd->nmcd.dwDrawStage) {
- case CDDS_PREPAINT:
- result = CDRF_NOTIFYITEMDRAW;
- break;
-
- case CDDS_ITEMPREPAINT:
- pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
- result = CDRF_NOTIFYSUBITEMDRAW;
- break;
-
- case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
- pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
- {
- int sub = ListViewToColumn(lplvcd->iSubItem);
- auto *pCol = &g_plugin.m_columns[sub];
- if (pCol == nullptr)
- break;
-
- if (pCol->isGender) {
- m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
-
- switch (pRow->pValues[sub].data) {
- case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
- case 'M': h = g_plugin.getIcon(IDI_MALE); break;
- default: h = 0;
- }
-
- if (h)
- DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
- result = CDRF_SKIPDEFAULT;
- }
- else if (pCol->isXstatus) {
- int j = _wtoi(pRow->pValues[sub].text);
- if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
- h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
- m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
- DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
- }
- result = CDRF_SKIPDEFAULT;
- }
- else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
- result = CDRF_NOTIFYPOSTPAINT;
- }
- break;
-
- case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
- {
- int sub = ListViewToColumn(lplvcd->iSubItem);
- auto *pCol = &g_plugin.m_columns[sub];
- if (pCol == nullptr)
- break;
-
- if (pCol->isClient) {
- auto *MirVerW = pRow->pValues[sub].text;
- if (MirVerW && *MirVerW && g_bFingerInstalled) {
- h = Finger_GetClientIcon(MirVerW, FALSE);
- m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
- DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
- DestroyIcon(h);
- }
- }
- result = CDRF_SKIPDEFAULT;
- }
- break;
- }
-
- SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
-}
-
-void QSMainDlg::onTimer_Hover(CTimer *pTimer)
-{
- pTimer->Stop();
-
- if (GetForegroundWindow() != m_hwnd)
- return;
-
- auto *pRow = GetRow(OldHItem);
- if (pRow == 0)
- return;
-
- POINT pt;
- GetCursorPos(&pt);
-
- RECT rcItem;
- m_grid.GetItemRect(OldHItem, &rcItem, 0);
- ScreenToClient(m_grid.GetHwnd(), &pt);
- if (!PtInRect(&rcItem, pt))
- return;
-
- CLCINFOTIP info = {};
- info.cbSize = sizeof(info);
- info.hItem = HANDLE(pRow->hContact);
- Tipper_ShowTip(0, &info);
- TTShowed = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CloseSrWindow(bool)
-{
- if (!g_pDlg)
- return false;
-
- g_pDlg->Close();
- return true;
-}
-
-int OpenSrWindow(const wchar_t *pwszPattern)
-{
- if (g_pDlg) {
- WINDOWPLACEMENT wp;
- wp.length = sizeof(wp);
- GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
- if (wp.showCmd == SW_SHOWMINIMIZED)
- g_pDlg->Show(SW_RESTORE);
- SetForegroundWindow(g_pDlg->GetHwnd());
- return true;
- }
-
- int count = 0;
- for (auto &it : g_plugin.m_columns)
- if (it->bEnabled)
- count++;
-
- // no even one visible column
- if (count == 0)
- return true;
-
- g_plugin.LoadOptWnd();
-
- auto *pDlg = new QSMainDlg(pwszPattern);
- if (pDlg->PrepareToFill())
- pDlg->Create();
- else
- delete pDlg;
-
- return true;
-}
+/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY | FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+#define IDM_STAYONTOP (WM_USER+1)
+
+static QSMainDlg *g_pDlg = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// edit control window procedure
+
+static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+ if (pDlg)
+ if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
+ return res;
+
+ return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_CHAR:
+ if (wParam == 27) // Escape
+ Close();
+ break;
+
+ case WM_KEYUP:
+ if (wParam == VK_RETURN) {
+ if (m_grid.GetSelectedCount() == 1)
+ ShowContactMsgDlg(GetFocusedContact());
+ return 0;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ int count = m_grid.GetItemCount();
+ int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
+ int next = -1;
+ if (count > 0) {
+ switch (wParam) {
+ case VK_UP:
+ if (current > 0)
+ next = current - 1;
+ break;
+
+ case VK_DOWN:
+ if (current < count - 1)
+ next = current + 1;
+ break;
+
+ case VK_F5:
+ onClick_Refresh(0);
+ return 0;
+
+ case VK_NEXT:
+ case VK_PRIOR:
+ int perpage = m_grid.GetCountPerPage();
+ if (wParam == VK_NEXT)
+ next = min(current + perpage, count);
+ else
+ next = max(current - perpage, 0);
+ break;
+ }
+ }
+
+ if (next >= 0) {
+ m_grid.SetItemState(-1, 0, LVIS_SELECTED);
+ m_grid.SetCurSel(next);
+ m_grid.EnsureVisible(next, FALSE);
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// list header window procedure
+
+static void MakeColumnMenu()
+{
+ HMENU hMenu = CreatePopupMenu();
+
+ for (auto &it : g_plugin.m_columns) {
+ int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
+ AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
+ }
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
+ if (id >= 100)
+ g_pDlg->ToggleColumn(id - 100);
+
+ DestroyMenu(hMenu);
+}
+
+void QSMainDlg::ToggleColumn(int col)
+{
+ auto &pCol = g_plugin.m_columns[col];
+
+ if (!pCol.bEnabled) { // show column
+ pCol.bEnabled = true;
+
+ if (!pCol.bInit) {
+ for (auto &it : m_rows)
+ it->pValues[col].LoadOneItem(it->hContact, pCol, this);
+ pCol.bInit = true;
+ }
+
+ // screen
+ int lvcol = ColumnToListView(col);
+ AddColumn(lvcol, &pCol);
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ auto *pRow = GetRow(i);
+
+ LV_ITEMW li;
+ li.iItem = i;
+ li.iSubItem = lvcol;
+ li.mask = LVIF_TEXT;
+ li.pszText = pRow->pValues[col].text;
+ if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
+ li.mask |= LVIF_IMAGE;
+ m_grid.SetItem(&li);
+ }
+ }
+ else { // hide column
+ int cnt = 0;
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ cnt++;
+
+ // keep at least one visible column (1 + this)
+ if (cnt > 2) {
+ m_grid.DeleteColumn(col);
+ pCol.bEnabled = false;
+ }
+ }
+}
+
+static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_RBUTTONUP:
+ return 0;
+
+ case WM_RBUTTONDOWN:
+ MakeColumnMenu();
+ break;
+ }
+
+ return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid list window procedure
+
+static int OldHSubItem = 0, OldHItem = 0;
+
+static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+ if (pDlg)
+ if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
+ return res;
+
+ return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LV_HITTESTINFO pinfo;
+
+ switch (msg) {
+ case WM_CHAR:
+ switch (wParam) { // ESC
+ case 27:
+ Close();
+ break;
+
+ case 1: // Cltr+A
+ m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
+ break;
+
+ case 3: // Ctrl-C
+ CopyMultiLines();
+ break;
+
+ case 8: // backspace
+ edtFilter.SendMsg(msg, wParam, lParam);
+ break;
+ }
+
+ if (wParam >= 32 && wParam <= 127) // letters
+ edtFilter.SendMsg(msg, wParam, lParam);
+ break;
+
+ case WM_MOUSEMOVE:
+ pinfo.pt.x = LOWORD(lParam);
+ pinfo.pt.y = HIWORD(lParam);
+ pinfo.flags = 0;
+ if (m_grid.SubItemHitTest(&pinfo) == -1)
+ break;
+
+ if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
+ OldHSubItem = pinfo.iSubItem;
+ OldHItem = pinfo.iItem;
+
+ if (g_bTipperInstalled) {
+ if (TTShowed) {
+ TTShowed = false;
+ Tipper_Hide();
+ }
+ m_hover.Stop();
+
+ if (OldHSubItem == 0)
+ m_hover.Start(450);
+ }
+
+ TOOLINFOW ti = {};
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+ ti.hwnd = m_hwnd;
+ ti.uId = LPARAM(m_hwnd);
+
+ int num = ListViewToColumn(OldHSubItem);
+ auto &pCol = g_plugin.m_columns[num];
+ if (pCol.isXstatus || pCol.isGender) {
+ auto *pRow = GetRow(OldHItem);
+ if (pCol.isGender) {
+ switch (pRow->pValues[num].data) {
+ case 'M': ti.lpszText = TranslateT("Male"); break;
+ case 'F': ti.lpszText = TranslateT("Female"); break;
+ default: ti.lpszText = TranslateT("Unknown"); break;
+ }
+ }
+ else {
+ wchar_t buf[256];
+ mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
+ int iStatus = _wtoi(buf);
+
+ CUSTOM_STATUS ics = {};
+ ics.cbSize = sizeof(ics);
+ ics.status = &iStatus;
+ ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
+ ics.pwszName = buf;
+ CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
+ ti.lpszText = TranslateW(buf);
+ }
+ }
+
+ SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
+ }
+ break;
+
+ case WM_KEYUP:
+ switch (wParam) {
+ case VK_RETURN:
+ if (m_grid.GetSelectedCount() == 1)
+ ShowContactMsgDlg(GetFocusedContact());
+ break;
+
+ case VK_INSERT:
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ break;
+
+ case VK_DELETE:
+ lParam = m_grid.GetSelectedCount();
+ if (lParam > 1)
+ DeleteByList();
+ else if (lParam == 1)
+ DeleteOneContact(GetFocusedContact());
+ break;
+
+ case VK_F5:
+ onClick_Refresh(0);
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
+ NMHEADER *pdhr = (NMHEADER *)lParam;
+ if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
+ int i = ListViewToColumn(pdhr->iItem);
+ auto &pCol = g_plugin.m_columns[i];
+
+ if (pdhr->pitem->fmt & HDF_CHECKED) {
+ pCol.bFilter = false;
+ pdhr->pitem->fmt &= ~HDF_CHECKED;
+ }
+ else {
+ pCol.bFilter = true;
+ pdhr->pitem->fmt |= HDF_CHECKED;
+ }
+
+ SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
+ FillGrid();
+ return TRUE;
+ }
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// QSMainDlg class implementation
+
+static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
+{
+ if (p1->bAccDel != p2->bAccDel)
+ return (p1->bAccDel) ? 1 : -1;
+
+ if (p1->bAccOff != p2->bAccOff)
+ return (p1->bAccOff) ? 1 : -1;
+
+ return mir_strcmp(p1->szProto, p2->szProto);
+}
+
+QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
+ CDlgBase(g_plugin, IDD_MAIN),
+ m_rows(50),
+ m_patterns(1),
+ m_sbdata(10, CompareSb),
+ m_grid(this, IDC_LIST),
+ m_hover(this, 10),
+ cmbProto(this, IDC_CB_PROTOCOLS),
+ edtFilter(this, IDC_E_SEARCHTEXT),
+ btnRefresh(this, IDC_REFRESH),
+ chkColorize(this, IDC_CH_COLORIZE),
+ chkShowOffline(this, IDC_CH_SHOWOFFLINE)
+{
+ SetMinSize(300, 160);
+
+ if (pwszPattern)
+ m_wszPatternBuf = mir_wstrdup(pwszPattern);
+ else if (g_plugin.m_flags & QSO_SAVEPATTERN)
+ m_wszPatternBuf = g_plugin.getWStringA("pattern");
+
+ m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
+
+ m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
+ m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
+ m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
+ m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
+
+ btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
+
+ cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
+
+ edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
+ chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
+ chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
+}
+
+bool QSMainDlg::OnInitDialog()
+{
+ g_pDlg = this;
+ mnuhandle = 0;
+
+ SetCaption(TranslateT("Quick Search"));
+
+ hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
+
+ HMENU smenu = GetSystemMenu(m_hwnd, false);
+ InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+ InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
+
+ if (g_plugin.m_flags & QSO_STAYONTOP) {
+ CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
+ SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
+
+ szFilterProto = nullptr; // display all protocols
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ bShowOffline = true;
+
+ chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
+
+ // Window
+ INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
+ if (g_plugin.m_flags & QSO_TOOLSTYLE)
+ tmp |= WS_EX_TOOLWINDOW;
+ else
+ tmp &= ~WS_EX_TOOLWINDOW;
+ SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
+
+ SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
+
+ // ListView
+ m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
+
+ tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
+ LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
+ if (g_plugin.m_flags & QSO_DRAWGRID)
+ tmp |= LVS_EX_GRIDLINES;
+ m_grid.SetExtendedListViewStyle(tmp);
+
+ // ListView header
+ HWND header = m_grid.GetHeader();
+ SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
+
+ mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
+
+ SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
+
+ SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+ mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
+
+ FillProtoCombo();
+
+ PrepareTable();
+
+ if (m_wszPatternBuf) {
+ edtFilter.SetText(m_wszPatternBuf);
+ MakePattern(m_wszPatternBuf);
+ }
+ FillGrid();
+
+ // Show sorting column
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+ SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ if (g_plugin.m_flags & QSO_SORTASC)
+ hdi.fmt |= HDF_SORTUP;
+ else
+ hdi.fmt |= HDF_SORTDOWN;
+ SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ RECT rc = g_plugin.m_rect;
+ ::SnapToScreen(rc);
+ ::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
+
+ HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
+
+ TOOLINFOW ti;
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+ ti.hwnd = m_hwnd;
+ ti.uId = UINT_PTR(m_grid.GetHwnd());
+ SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
+
+ hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
+ hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
+ hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
+ return true;
+}
+
+void QSMainDlg::OnDestroy()
+{
+ if (mnuhandle)
+ Menu_RemoveItem(mnuhandle);
+
+ UnhookEvent(hAdd);
+ UnhookEvent(hDelete);
+ UnhookEvent(hChange);
+
+ g_pDlg = nullptr;
+
+ RECT rc;
+ GetWindowRect(m_hwnd, &rc);
+ CopyRect(&g_plugin.m_rect, &rc);
+
+ // save column width/order
+ SaveColumnOrder();
+
+ g_plugin.SaveOptWnd();
+
+ m_grid.SetImageList(0, LVSIL_SMALL);
+
+ if (g_plugin.m_flags & QSO_SAVEPATTERN)
+ g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
+
+ m_rows.destroy();
+ m_patterns.destroy();
+}
+
+int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId) {
+ case IDC_REFRESH:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+ case IDC_E_SEARCHTEXT:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+ case IDC_LIST:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDC_STATUSBAR:
+ return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// event handlers
+
+INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
+{
+ auto *pRow = new CRowItem(hContact, this);
+ m_rows.insert(pRow);
+ AddContactToList(hContact, pRow);
+ ProcessLine(pRow);
+ Sort();
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
+{
+ int idx = -1;
+ CRowItem *pRow = 0;
+
+ for (auto &it : m_rows)
+ if (it->hContact == hContact) {
+ pRow = it;
+ idx = m_rows.indexOf(&it);
+ break;
+ }
+
+ if (idx == -1)
+ return 0;
+
+ int iItem = FindItem(pRow);
+ if (iItem != -1)
+ m_grid.DeleteItem(iItem);
+
+ m_rows.remove(idx);
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
+{
+ auto *pRow = FindRow(hContact);
+ if (pRow == nullptr)
+ return 0;
+
+ int oldStatus = pRow->status;
+ int newStatus = Contact::GetStatus(hContact);
+ pRow->status = newStatus;
+
+ if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else if (oldStatus != ID_STATUS_OFFLINE) {
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else
+ ProcessLine(pRow, true);
+ }
+ else if (newStatus != ID_STATUS_OFFLINE) {
+ if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+ ChangeStatusPicture(pRow, hContact, lParam);
+ else {
+ pRow->bActive = false;
+ m_grid.DeleteItem(FindItem(pRow));
+ }
+ }
+
+ if (g_plugin.m_flags & QSO_SORTBYSTATUS)
+ Sort();
+
+ UpdateSB();
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
+{
+ if (wParam == IDM_STAYONTOP) {
+ int h; HWND w;
+ if (g_plugin.m_flags & QSO_STAYONTOP) {
+ h = MF_BYCOMMAND | MF_UNCHECKED;
+ w = HWND_NOTOPMOST;
+ }
+ else {
+ h = MF_BYCOMMAND | MF_CHECKED;
+ w = HWND_TOPMOST;
+ }
+ CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
+ SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ g_plugin.m_flags ^= QSO_STAYONTOP;
+ }
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
+{
+ if (g_bTipperInstalled) {
+ RECT rc;
+ GetWindowRect(m_grid.GetHwnd(), &rc);
+
+ POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+ ClientToScreen(m_hwnd, &pt);
+ if (!PtInRect(&rc, pt)) {
+ if (TTShowed) {
+ TTShowed = false;
+ CallService(MS_TIPPER_HIDETIP, 0, 0);
+ }
+ }
+
+ m_hover.Stop();
+ }
+ return 0;
+}
+
+INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
+{
+ if (wParam == VK_F5)
+ PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
+ return 0;
+}
+
+void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
+{
+ int w = m_grid.GetSelectedCount();
+ if (w > 1)
+ ShowMultiPopup(w);
+ else
+ ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
+}
+
+void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
+{
+ LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
+ if (lParam == -1 || lParam == 0)
+ szFilterProto = nullptr;
+ else
+ szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
+
+ AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Filter(CCtrlEdit *)
+{
+ if (!m_bInitialized)
+ return;
+
+ MakePattern(ptrW(edtFilter.GetText()));
+ FillGrid();
+}
+
+void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
+{
+ if (chkShowOffline.IsChecked()) {
+ g_plugin.m_flags |= QSO_SHOWOFFLINE;
+ bShowOffline = true;
+ }
+ else {
+ g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
+ bShowOffline = false;
+ }
+
+ AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Colorize(CCtrlCheck *)
+{
+ if (chkColorize.IsChecked())
+ g_plugin.m_flags |= QSO_COLORIZE;
+ else
+ g_plugin.m_flags &= ~QSO_COLORIZE;
+ RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
+}
+
+void QSMainDlg::onClick_Refresh(CCtrlButton *)
+{
+ m_rows.destroy();
+ PrepareToFill();
+ PrepareTable(true);
+ FillGrid();
+}
+
+void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
+{
+ HWND header = m_grid.GetHeader();
+
+ // clear sort mark
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+ SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
+ SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
+ g_plugin.m_flags |= QSO_SORTASC;
+ g_plugin.m_sortOrder = ev->nmlv->iSubItem;
+ }
+ else g_plugin.m_flags ^= QSO_SORTASC;;
+
+ // set new sort mark
+ SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+ if ((g_plugin.m_flags & QSO_SORTASC) == 0)
+ hdi.fmt |= HDF_SORTDOWN;
+ else
+ hdi.fmt &= ~HDF_SORTUP;
+ SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+ Sort();
+}
+
+void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
+{
+ ShowContactMsgDlg(GetFocusedContact());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
+{
+ LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
+ HICON h;
+ RECT rc;
+ auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
+
+ int result = CDRF_DODEFAULT;
+ switch (lplvcd->nmcd.dwDrawStage) {
+ case CDDS_PREPAINT:
+ result = CDRF_NOTIFYITEMDRAW;
+ break;
+
+ case CDDS_ITEMPREPAINT:
+ pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+ result = CDRF_NOTIFYSUBITEMDRAW;
+ break;
+
+ case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
+ pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+ {
+ int sub = ListViewToColumn(lplvcd->iSubItem);
+ auto *pCol = &g_plugin.m_columns[sub];
+ if (pCol == nullptr)
+ break;
+
+ if (pCol->isGender) {
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+
+ switch (pRow->pValues[sub].data) {
+ case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
+ case 'M': h = g_plugin.getIcon(IDI_MALE); break;
+ default: h = 0;
+ }
+
+ if (h)
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ result = CDRF_SKIPDEFAULT;
+ }
+ else if (pCol->isXstatus) {
+ int j = _wtoi(pRow->pValues[sub].text);
+ if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
+ h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ }
+ result = CDRF_SKIPDEFAULT;
+ }
+ else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
+ result = CDRF_NOTIFYPOSTPAINT;
+ }
+ break;
+
+ case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
+ {
+ int sub = ListViewToColumn(lplvcd->iSubItem);
+ auto *pCol = &g_plugin.m_columns[sub];
+ if (pCol == nullptr)
+ break;
+
+ if (pCol->isClient) {
+ auto *MirVerW = pRow->pValues[sub].text;
+ if (MirVerW && *MirVerW && g_bFingerInstalled) {
+ h = Finger_GetClientIcon(MirVerW, FALSE);
+ m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+ DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+ DestroyIcon(h);
+ }
+ }
+ result = CDRF_SKIPDEFAULT;
+ }
+ break;
+ }
+
+ SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
+}
+
+void QSMainDlg::onTimer_Hover(CTimer *pTimer)
+{
+ pTimer->Stop();
+
+ if (GetForegroundWindow() != m_hwnd)
+ return;
+
+ auto *pRow = GetRow(OldHItem);
+ if (pRow == 0)
+ return;
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rcItem;
+ m_grid.GetItemRect(OldHItem, &rcItem, 0);
+ ScreenToClient(m_grid.GetHwnd(), &pt);
+ if (!PtInRect(&rcItem, pt))
+ return;
+
+ CLCINFOTIP info = {};
+ info.cbSize = sizeof(info);
+ info.hItem = HANDLE(pRow->hContact);
+ Tipper_ShowTip(0, &info);
+ TTShowed = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CloseSrWindow(bool)
+{
+ if (!g_pDlg)
+ return false;
+
+ g_pDlg->Close();
+ return true;
+}
+
+int OpenSrWindow(const wchar_t *pwszPattern)
+{
+ if (g_pDlg) {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
+ if (wp.showCmd == SW_SHOWMINIMIZED)
+ g_pDlg->Show(SW_RESTORE);
+ SetForegroundWindow(g_pDlg->GetHwnd());
+ return true;
+ }
+
+ int count = 0;
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ count++;
+
+ // no even one visible column
+ if (count == 0)
+ return true;
+
+ g_plugin.LoadOptWnd();
+
+ auto *pDlg = new QSMainDlg(pwszPattern);
+ if (pDlg->PrepareToFill())
+ pDlg->Create();
+ else
+ delete pDlg;
+
+ return true;
+}
diff --git a/plugins/QuickSearch/src/window_misc.cpp b/plugins/QuickSearch/src/window_misc.cpp
index e6b5e23341..58d96561fc 100644
--- a/plugins/QuickSearch/src/window_misc.cpp
+++ b/plugins/QuickSearch/src/window_misc.cpp
@@ -1,875 +1,875 @@
-/*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it &/|
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY | FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// patterns
-
-bool QSMainDlg::CheckPattern(CRowItem *pRow)
-{
- if (m_patterns.getCount() == 0)
- return true;
-
- for (auto &p : m_patterns)
- p->res = 0;
-
- int i = 0;
- for (auto &it : g_plugin.m_columns) {
- if (it->bEnabled && it->bFilter && pRow->pValues[i].text != nullptr) {
- CMStringW buf(pRow->pValues[i].text);
- buf.MakeLower();
-
- for (auto &p : m_patterns)
- if (!p->res && buf.Find(p->str) != -1)
- p->res = true;
- }
- i++;
- }
-
- bool result = true;
- for (auto &p : m_patterns)
- result = result && p->res;
- return result;
-}
-
-void QSMainDlg::MakePattern(const wchar_t *pwszPattern)
-{
- m_patterns.destroy();
- if (mir_wstrlen(pwszPattern) == 0)
- return;
-
- // m_wszPatternBuf works as a storage for patterns, we store pointers to it in m_patterns
- m_wszPatternBuf = mir_wstrdup(pwszPattern);
- CharLowerW(m_wszPatternBuf);
-
- for (wchar_t *p = m_wszPatternBuf; *p; ) {
- auto *pWord = wcspbrk(p, L" \"");
- if (pWord == nullptr) {
- m_patterns.insert(new Pattern(p));
- return;
- }
-
- bool isSpace = pWord[0] == ' ';
-
- // there's some valuable info between p and pWord
- if (pWord != p) {
- *pWord = 0;
- m_patterns.insert(new Pattern(p));
- }
-
- if (isSpace) {
- p = ltrimpw(pWord + 1); // skip all spaces
- }
- else {
- auto *pEnd = wcschr(++pWord, '\"');
-
- // treat the rest of line as one pattern
- if (pEnd == nullptr) {
- m_patterns.insert(new Pattern(pWord));
- return;
- }
-
- *pEnd = 0;
- m_patterns.insert(new Pattern(pWord));
- p = ltrimpw(pEnd + 1);
- }
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void QSMainDlg::AddColumn(int idx, ColumnItem *pCol)
-{
- LV_COLUMN lvcol = {};
- lvcol.mask = LVCF_TEXT | LVCF_WIDTH;
- lvcol.pszText = TranslateW(pCol->title);
- lvcol.cx = pCol->width;
- m_grid.InsertColumn(idx, &lvcol);
-
- HDITEM hdi;
- hdi.mask = HDI_FORMAT;
- if (pCol->bFilter)
- hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED;
- else
- hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX;
- SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi));
-}
-
-void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow)
-{
- LV_ITEMW li = {};
- li.mask = LVIF_IMAGE | LVIF_PARAM;
- li.iItem = 100000;
- li.iImage = Clist_GetContactIcon(hContact);
- li.lParam = LPARAM(pRow);
-
- li.iItem = m_grid.InsertItem(&li);
- li.iImage = 0;
- li.iSubItem = 0;
-
- for (int i = 0; i < g_plugin.m_columns.getCount(); i++) {
- auto &col = g_plugin.m_columns[i];
- if (!col.bEnabled)
- continue;
-
- // Client icons preprocess
- li.pszText = pRow->pValues[i].text;
- li.mask = LVIF_TEXT;
- if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender)
- li.mask |= LVIF_IMAGE;
- m_grid.SetItem(&li);
- li.iSubItem++;
- }
-}
-
-void QSMainDlg::AdvancedFilter()
-{
- m_grid.SetDraw(false);
-
- for (auto &it : m_rows) {
- bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto);
- if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE)
- bShow = false;
-
- if (it->bPattern) {
- if (bShow) {
- if (!it->bActive)
- ProcessLine(it, false);
- }
- else {
- it->bActive = false;
- m_grid.DeleteItem(FindItem(it));
- }
- }
- }
-
- m_grid.SetDraw(true);
- InvalidateRect(m_grid.GetHwnd(), 0, false);
-
- Sort();
- UpdateSB();
-}
-
-void QSMainDlg::ChangeStatusPicture(CRowItem *pRow, MCONTACT, LPARAM lParam)
-{
- int idx = FindItem(pRow);
- if (idx == -1)
- return;
-
- LV_ITEMW li = {};
- li.iItem = idx;
- li.mask = LVIF_IMAGE;
- li.iImage = lParam; //CallService(MS_CLIST_GETCONTACTICON,hContact,0);
- m_grid.SetItem(&li);
-}
-
-void QSMainDlg::CopyMultiLines()
-{
- CMStringW buf;
-
- int i = 0;
- for (auto &it : g_plugin.m_columns) {
- if (it->bEnabled) {
- it->width = m_grid.GetColumnWidth(i);
- if (it->width >= 10)
- buf.AppendFormat(L"%s\t", it->title);
- }
- i++;
- }
- buf.Append(L"\r\n");
-
- int nRows = m_grid.GetItemCount();
- int nSelected = m_grid.GetSelectedCount();
-
- for (int j = 0; j < nRows; j++) {
- if (nSelected > 1 && !m_grid.GetItemState(j, LVIS_SELECTED))
- continue;
-
- auto *pRow = GetRow(j);
-
- i = 0;
- for (auto &it : g_plugin.m_columns) {
- if (it->bEnabled && it->width >= 10)
- buf.AppendFormat(L"%s\t", pRow->pValues[i].getText());
- i++;
- }
- buf.Append(L"\r\n");
- }
-
- Utils_ClipboardCopy(buf);
-}
-
-void QSMainDlg::DeleteByList()
-{
- if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts?"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING))
- return;
-
- m_grid.SetDraw(false);
-
- for (int i = m_grid.GetItemCount() - 1; i >= 0; i--)
- if (m_grid.GetItemState(i, LVIS_SELECTED))
- db_delete_contact(GetRow(i)->hContact);
-
- m_grid.SetDraw(true);
-}
-
-void QSMainDlg::DeleteOneContact(MCONTACT hContact)
-{
- if (ServiceExists(MS_CLIST_DELETECONTACT))
- CallService(MS_CLIST_DELETECONTACT, hContact, 0);
- else
- db_delete_contact(hContact);
-}
-
-wchar_t* QSMainDlg::DoMeta(MCONTACT hContact)
-{
- for (auto &it : m_rows) {
- if (it->hContact != hContact)
- continue;
-
- if (it->bIsMeta) {
- if (it->wparam == 0)
- it->wparam = ++hLastMeta;
- }
- else if (it->bIsSub)
- it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam);
-
- if (it->wparam > 0) {
- CMStringW tmp(FORMAT, L"[%d]", int(it->wparam));
- if (it->lparam > 0)
- tmp.AppendFormat(L" %d", int(it->lparam));
- return tmp.Detach();
- }
- break;
- }
-
- return nullptr;
-}
-
-void QSMainDlg::DrawSB()
-{
- CStatusBarItem global(0, 0);
- for (auto &it : m_sbdata) {
- global.found += it->found;
- global.liston += it->liston;
- global.online += it->online;
- global.total += it->total;
- }
-
- CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online);
-
- RECT rc;
- HDC hdc = GetDC(hwndStatusBar);
- DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT);
- ReleaseDC(hwndStatusBar, hdc);
-
- int all = rc.right - rc.left, i = 1;
-
- mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2)));
- parts[0] = all;
- for (auto &it : m_sbdata) {
- UNREFERENCED_PARAMETER(it);
- all += 55;
- parts[i++] = all;
- }
- parts[i] = -1;
- SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get()));
- SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str()));
-
- i = 1;
- for (auto &it : m_sbdata) {
- HICON hIcon;
- wchar_t c, *pc;
- if (it->bAccDel) {
- c = '!';
- pc = TranslateT("deleted");
- hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
- }
- else if (it->bAccOff) {
- c = '?';
- pc = TranslateT("disabled");
- hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
- }
- else {
- c = ' ';
- pc = TranslateT("active");
- hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE);
- }
-
- SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon);
-
- buf.Format(L"%c %d", c, it->found);
- SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str()));
-
- auto *pa = Proto_GetAccount(it->szProto);
- buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online);
- SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str()));
- i++;
- }
-}
-
-void QSMainDlg::FillGrid()
-{
- m_grid.SetDraw(false);
-
- for (auto &it: m_rows)
- ProcessLine(it);
-
- m_grid.SetDraw(true);
- InvalidateRect(m_grid.GetHwnd(), 0, FALSE);
-
- Sort();
- UpdateSB();
- AdvancedFilter();
-
- m_grid.SetCurSel(0);
-}
-
-void QSMainDlg::FillProtoCombo()
-{
- cmbProto.ResetContent();
- cmbProto.AddString(TranslateT("All"));
-
- for (auto &it : Accounts())
- cmbProto.AddString(it->tszAccountName, (LPARAM)it);
-
- cmbProto.SetCurSel(0);
-}
-
-int QSMainDlg::FindItem(CRowItem *pRow)
-{
- if (pRow == nullptr)
- return -1;
-
- LV_FINDINFO fi = {};
- fi.flags = LVFI_PARAM;
- fi.lParam = LPARAM(pRow);
- return m_grid.FindItem(-1, &fi);
-}
-
-int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum)
-{
- for (auto &it : m_rows) {
- if (it->hContact != hMeta)
- continue;
-
- // new meta
- if (it->wparam == 0) {
- it->wparam = ++hLastMeta;
- it->lparam = 0;
- }
- metaNum = it->wparam;
- it->lparam++;
- return it->lparam;
- }
-
- return 0;
-}
-
-CRowItem* QSMainDlg::FindRow(MCONTACT hContact)
-{
- for (auto &it : m_rows)
- if (it->hContact == hContact)
- return it;
-
- return nullptr;
-}
-
-MCONTACT QSMainDlg::GetFocusedContact()
-{
- int idx = m_grid.GetSelectionMark();
- if (idx == -1)
- return -1;
-
- INT_PTR data = m_grid.GetItemData(idx);
- return (data == -1) ? -1 : ((CRowItem *)data)->hContact;
-}
-
-int QSMainDlg::GetLVSubItem(int x, int y)
-{
- LV_HITTESTINFO info = {};
- info.pt.x = x;
- info.pt.y = y;
- ScreenToClient(m_grid.GetHwnd(), &info.pt);
- if (m_grid.SubItemHitTest(&info) == -1)
- return -1;
-
- return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1;
-}
-
-void QSMainDlg::PrepareTable(bool bReset)
-{
- m_grid.DeleteAllItems();
-
- HDITEM hdi = {};
- hdi.mask = HDI_FORMAT;
-
- int old = tableColumns;
- tableColumns = 0;
-
- LV_COLUMN lvc = {};
- lvc.mask = LVCF_TEXT | LVCF_WIDTH;
- for (auto &it : g_plugin.m_columns) {
- if (it->bEnabled)
- AddColumn(tableColumns++, it);
-
- it->SetSpecialColumns();
- }
-
- if (bReset)
- for (int i = old + tableColumns - 1; i >= tableColumns; i--)
- m_grid.DeleteColumn(i);
-}
-
-bool QSMainDlg::PrepareToFill()
-{
- if (g_plugin.m_columns.getCount() == 0)
- return false;
-
- for (auto &it : g_plugin.m_columns)
- if (it->bEnabled)
- it->bInit = true;
-
- hLastMeta = 0;
-
- m_rows.destroy();
- for (auto &hContact : Contacts())
- m_rows.insert(new CRowItem(hContact, this));
-
- return m_rows.getCount() != 0;
-}
-
-void QSMainDlg::ProcessLine(CRowItem *pRow, bool test)
-{
- if (pRow->bDeleted)
- return;
-
- if (test)
- pRow->bPattern = CheckPattern(pRow);
-
- if (pRow->bPattern) {
- if (!pRow->bActive) {
- if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) {
- // check for proto in combo
- if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) {
- pRow->bActive = true;
- AddContactToList(pRow->hContact, pRow);
- }
- }
- }
- }
- else if (pRow->bActive) {
- pRow->bActive = false;
- m_grid.DeleteItem(FindItem(pRow));
- }
-}
-
-void QSMainDlg::SaveColumnOrder()
-{
- int idx = 0, col = 0;
- for (auto &it : g_plugin.m_columns) {
- if (it->bEnabled) {
- it->width = m_grid.GetColumnWidth(col++);
- g_plugin.SaveColumn(idx, *it);
- }
- idx++;
- }
-}
-
-void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact)
-{
- if (hContact) {
- Clist_ContactDoubleClicked(hContact);
- if (g_plugin.m_flags & QSO_AUTOCLOSE)
- Close();
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// contact menu
-
-static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param)
-{
- ((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param);
- return 0;
-}
-
-void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col)
-{
- auto &pCol = g_plugin.m_columns[col];
-
- auto *pRow = FindRow(hContact);
- if (pRow == nullptr)
- return;
-
- const char *szModule = pCol.module;
- if (szModule == nullptr)
- szModule = pRow->szProto;
-
- auto &pVal = pRow->pValues[col];
- CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title);
-
- ENTER_STRING es = {};
- es.szModuleName = MODULENAME;
- es.caption = TranslateT("Enter new cell value");
- es.ptszInitVal = pVal.text;
- if (!EnterString(&es))
- return;
-
- replaceStrW(pVal.text, es.ptszResult);
- if (pCol.datatype != QSTS_STRING)
- pVal.data = _wtoi(pVal.text);
-
- switch (pCol.datatype) {
- case QSTS_BYTE:
- db_set_b(hContact, szModule, pCol.setting, pVal.data);
- break;
- case QSTS_WORD:
- db_set_w(hContact, szModule, pCol.setting, pVal.data);
- break;
- case QSTS_DWORD:
- case QSTS_SIGNED:
- case QSTS_HEXNUM:
- db_set_dw(hContact, szModule, pCol.setting, pVal.data);
- break;
-
- case QSTS_STRING:
- db_set_ws(hContact, szModule, pCol.setting, pVal.text);
- break;
- }
-
- UpdateLVCell(FindItem(pRow), col, pVal.text);
-}
-
-void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col)
-{
- if (hContact == 0)
- return;
-
- HANDLE srvhandle = 0;
-
- bool bDoit = false;
- if (col >= 0) {
- if ((col = ListViewToColumn(col)) == -1)
- return;
-
- auto &pCol = g_plugin.m_columns[col];
- if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) {
- bDoit = true;
-
- srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col);
-
- if (mnuhandle == nullptr) {
- CMenuItem mi(&g_plugin);
- SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81);
- mi.name.a = LPGEN("Change setting through QS");
- mi.pszService = "QS/Dummy";
- mnuhandle = Menu_AddContactMenuItem(&mi);
- }
- else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0);
- }
- }
-
- POINT pt;
- GetCursorPos(&pt);
- HMENU hMenu = Menu_BuildContactMenu(hContact);
- if (hMenu) {
- int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0);
- if (iCmd) {
- if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) {
- if (g_plugin.m_flags & QSO_AUTOCLOSE)
- CloseSrWindow();
- }
- }
-
- ::DestroyMenu(hMenu);
- }
-
- if (srvhandle)
- DestroyServiceFunction(srvhandle);
- if (bDoit)
- Menu_ShowItem(mnuhandle, false);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// muptiple selection popup menu
-
-static HMENU MakeContainerMenu()
-{
- HMENU hMenu = CreatePopupMenu();
-
- for (int i = 0;; i++) {
- char setting[10];
- _itoa_s(i, setting, 10);
- ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting));
- if (wszName != nullptr)
- AppendMenuW(hMenu, MF_STRING, 300 + i, wszName);
- else
- break;
- }
-
- return hMenu;
-}
-
-void QSMainDlg::ShowMultiPopup(int cnt)
-{
- HMENU hMenu = CreatePopupMenu();
- AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt));
- AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
- AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete"));
- AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy"));
- AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta"));
-
- HMENU cntmenu = MakeContainerMenu();
- AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container"));
-
- if (HMENU grpmenu = Clist_GroupBuildMenu(400))
- AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group"));
-
- POINT pt;
- GetCursorPos(&pt);
-
- int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0);
- switch (iRes) {
- case 101:
- DeleteByList();
- break;
-
- case 102:
- CopyMultiLines();
- break;
-
- case 103:
- ConvertToMeta();
- break;
- }
-
- if (iRes >= 300 && iRes <= 399) {
- wchar_t buf[100];
- if (iRes == 300) // default container, just delete setting
- buf[0] = 0;
- else
- GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND);
-
- MoveToContainer(buf);
- }
- else if (iRes >= 400 && iRes <= 499)
- MoveToGroup(Clist_GroupGetName(iRes - 400));
-}
-
-void QSMainDlg::ConvertToMeta()
-{
- MCONTACT hMeta = 0;
-
- int nCount = m_grid.GetItemCount();
- for (int i = 0; i < nCount; i++) {
- if (!m_grid.GetItemState(i, LVIS_SELECTED))
- continue;
-
- auto *pRow = GetRow(i);
- if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) {
- if (hMeta == 0)
- hMeta = tmp;
- else if (hMeta != tmp) {
- MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR);
- return;
- }
- }
- }
-
- if (hMeta != 0)
- if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING))
- return;
-
- for (int i = 0; i < nCount; i++) {
- if (!m_grid.GetItemState(i, LVIS_SELECTED))
- continue;
-
- auto *pRow = GetRow(i);
- if (hMeta)
- db_mc_addToMeta(pRow->hContact, hMeta);
- else
- db_mc_convertToMeta(pRow->hContact);
- }
-}
-
-void QSMainDlg::MoveToContainer(const wchar_t *pwszName)
-{
- int grcol = -1;
- for (auto &it : g_plugin.m_columns) {
- if (it->isContainer) {
- if (it->bEnabled)
- grcol = g_plugin.m_columns.indexOf(&it);
- else
- it->bInit = false;
- }
- }
-
- int nCount = m_grid.GetItemCount();
- for (int i = 0; i < nCount; i++) {
- if (!m_grid.GetItemState(i, LVIS_SELECTED))
- continue;
-
- auto *pRow = GetRow(i);
- if (*pwszName == 0)
- db_unset(pRow->hContact, "Tab_SRMsg", "containerW");
- else
- db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName);
-
- if (grcol != -1) {
- auto &pVal = pRow->pValues[grcol];
- replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr);
- UpdateLVCell(i, grcol, pwszName);
- }
- }
-}
-
-void QSMainDlg::MoveToGroup(const wchar_t *pwszName)
-{
- int grcol = -1;
- for (auto &it : g_plugin.m_columns) {
- if (it->isGroup) {
- if (it->bEnabled)
- grcol = g_plugin.m_columns.indexOf(&it);
- else
- it->bInit = false;
- }
- }
-
- int nCount = m_grid.GetItemCount();
- for (int i = 0; i < nCount; i++) {
- if (!m_grid.GetItemState(i, LVIS_SELECTED))
- continue;
-
- auto *pRow = GetRow(i);
- Clist_SetGroup(pRow->hContact, pwszName);
-
- if (grcol != -1) {
- auto &pVal = pRow->pValues[grcol];
- replaceStrW(pVal.text, pwszName);
- UpdateLVCell(i, grcol, pwszName);
- }
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// grid sorting
-
-static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type)
-{
- bool typ1, typ2;
- UINT_PTR i1, i2;
- int result = 0;
- CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2;
-
- if (type == StatusSort) {
- i1 = r1->status, i2 = r2->status;
- if (i1 == ID_STATUS_OFFLINE) i1 += 64;
- if (i2 == ID_STATUS_OFFLINE) i2 += 64;
- typ1 = typ2 = false;
- }
- else {
- auto &res1 = r1->pValues[type], &res2 = r2->pValues[type];
- i1 = res1.data, i2 = res2.data;
- typ1 = i1 == -1; typ2 = i1 == -1;
-
- if (typ1 && typ2) { // two strings
- if (res1.text == 0 && res2.text == 0)
- result = 0;
- else if (res2.text == 0)
- result = 1;
- else if (res1.text == 0)
- result = -1;
- else
- result = lstrcmpiW(res1.text, res2.text);
- }
- else if (typ1 || typ2) // string & num
- result = (typ1) ? 1 : -1;
- }
-
- if (!typ1 && !typ2) { // not strings
- if (i1 > i2)
- result = 1;
- else if (i1 < i2)
- result = -1;
- else
- result = 0;
- }
-
- if (g_plugin.m_flags & QSO_SORTASC)
- result = -result;
- return result;
-}
-
-void QSMainDlg::Sort()
-{
- if (g_plugin.m_sortOrder >= tableColumns)
- g_plugin.m_sortOrder = StatusSort;
- m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder));
-
- if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS))
- m_grid.SortItems(&CompareItem, StatusSort);
-}
-
-void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText)
-{
- auto &pCol = g_plugin.m_columns[column];
- auto *pRow = GetRow(item);
-
- if (pwszText == nullptr) {
- auto &pVal = pRow->pValues[column];
- replaceStrW(pVal.text, 0);
- pVal.LoadOneItem(pRow->hContact, pCol, this);
- pwszText = pVal.text;
- }
-
- m_grid.SetItemText(item, ColumnToListView(column), pwszText);
-
- if (pCol.bFilter)
- ProcessLine(pRow, true);
- if (g_plugin.m_sortOrder == column)
- Sort();
-}
-
-void QSMainDlg::UpdateSB()
-{
- m_sbdata.destroy();
-
- for (auto &it : m_rows) {
- if (it->szProto == nullptr)
- continue;
-
- CStatusBarItem tmp(it->szProto, it->flags);
- auto *pItem = m_sbdata.find(&tmp);
- if (pItem == nullptr)
- m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags));
-
- pItem->total++;
-
- if (it->bActive)
- pItem->found++;
-
- if (it->status != ID_STATUS_OFFLINE) {
- pItem->online++;
- if (it->bActive)
- pItem->liston++;
- }
- }
-
- DrawSB();
-}
+/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY | FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// patterns
+
+bool QSMainDlg::CheckPattern(CRowItem *pRow)
+{
+ if (m_patterns.getCount() == 0)
+ return true;
+
+ for (auto &p : m_patterns)
+ p->res = 0;
+
+ int i = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled && it->bFilter && pRow->pValues[i].text != nullptr) {
+ CMStringW buf(pRow->pValues[i].text);
+ buf.MakeLower();
+
+ for (auto &p : m_patterns)
+ if (!p->res && buf.Find(p->str) != -1)
+ p->res = true;
+ }
+ i++;
+ }
+
+ bool result = true;
+ for (auto &p : m_patterns)
+ result = result && p->res;
+ return result;
+}
+
+void QSMainDlg::MakePattern(const wchar_t *pwszPattern)
+{
+ m_patterns.destroy();
+ if (mir_wstrlen(pwszPattern) == 0)
+ return;
+
+ // m_wszPatternBuf works as a storage for patterns, we store pointers to it in m_patterns
+ m_wszPatternBuf = mir_wstrdup(pwszPattern);
+ CharLowerW(m_wszPatternBuf);
+
+ for (wchar_t *p = m_wszPatternBuf; *p; ) {
+ auto *pWord = wcspbrk(p, L" \"");
+ if (pWord == nullptr) {
+ m_patterns.insert(new Pattern(p));
+ return;
+ }
+
+ bool isSpace = pWord[0] == ' ';
+
+ // there's some valuable info between p and pWord
+ if (pWord != p) {
+ *pWord = 0;
+ m_patterns.insert(new Pattern(p));
+ }
+
+ if (isSpace) {
+ p = ltrimpw(pWord + 1); // skip all spaces
+ }
+ else {
+ auto *pEnd = wcschr(++pWord, '\"');
+
+ // treat the rest of line as one pattern
+ if (pEnd == nullptr) {
+ m_patterns.insert(new Pattern(pWord));
+ return;
+ }
+
+ *pEnd = 0;
+ m_patterns.insert(new Pattern(pWord));
+ p = ltrimpw(pEnd + 1);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::AddColumn(int idx, ColumnItem *pCol)
+{
+ LV_COLUMN lvcol = {};
+ lvcol.mask = LVCF_TEXT | LVCF_WIDTH;
+ lvcol.pszText = TranslateW(pCol->title);
+ lvcol.cx = pCol->width;
+ m_grid.InsertColumn(idx, &lvcol);
+
+ HDITEM hdi;
+ hdi.mask = HDI_FORMAT;
+ if (pCol->bFilter)
+ hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED;
+ else
+ hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX;
+ SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi));
+}
+
+void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow)
+{
+ LV_ITEMW li = {};
+ li.mask = LVIF_IMAGE | LVIF_PARAM;
+ li.iItem = 100000;
+ li.iImage = Clist_GetContactIcon(hContact);
+ li.lParam = LPARAM(pRow);
+
+ li.iItem = m_grid.InsertItem(&li);
+ li.iImage = 0;
+ li.iSubItem = 0;
+
+ for (int i = 0; i < g_plugin.m_columns.getCount(); i++) {
+ auto &col = g_plugin.m_columns[i];
+ if (!col.bEnabled)
+ continue;
+
+ // Client icons preprocess
+ li.pszText = pRow->pValues[i].text;
+ li.mask = LVIF_TEXT;
+ if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender)
+ li.mask |= LVIF_IMAGE;
+ m_grid.SetItem(&li);
+ li.iSubItem++;
+ }
+}
+
+void QSMainDlg::AdvancedFilter()
+{
+ m_grid.SetDraw(false);
+
+ for (auto &it : m_rows) {
+ bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto);
+ if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE)
+ bShow = false;
+
+ if (it->bPattern) {
+ if (bShow) {
+ if (!it->bActive)
+ ProcessLine(it, false);
+ }
+ else {
+ it->bActive = false;
+ m_grid.DeleteItem(FindItem(it));
+ }
+ }
+ }
+
+ m_grid.SetDraw(true);
+ InvalidateRect(m_grid.GetHwnd(), 0, false);
+
+ Sort();
+ UpdateSB();
+}
+
+void QSMainDlg::ChangeStatusPicture(CRowItem *pRow, MCONTACT, LPARAM lParam)
+{
+ int idx = FindItem(pRow);
+ if (idx == -1)
+ return;
+
+ LV_ITEMW li = {};
+ li.iItem = idx;
+ li.mask = LVIF_IMAGE;
+ li.iImage = lParam; //CallService(MS_CLIST_GETCONTACTICON,hContact,0);
+ m_grid.SetItem(&li);
+}
+
+void QSMainDlg::CopyMultiLines()
+{
+ CMStringW buf;
+
+ int i = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled) {
+ it->width = m_grid.GetColumnWidth(i);
+ if (it->width >= 10)
+ buf.AppendFormat(L"%s\t", it->title);
+ }
+ i++;
+ }
+ buf.Append(L"\r\n");
+
+ int nRows = m_grid.GetItemCount();
+ int nSelected = m_grid.GetSelectedCount();
+
+ for (int j = 0; j < nRows; j++) {
+ if (nSelected > 1 && !m_grid.GetItemState(j, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(j);
+
+ i = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled && it->width >= 10)
+ buf.AppendFormat(L"%s\t", pRow->pValues[i].getText());
+ i++;
+ }
+ buf.Append(L"\r\n");
+ }
+
+ Utils_ClipboardCopy(buf);
+}
+
+void QSMainDlg::DeleteByList()
+{
+ if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts?"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING))
+ return;
+
+ m_grid.SetDraw(false);
+
+ for (int i = m_grid.GetItemCount() - 1; i >= 0; i--)
+ if (m_grid.GetItemState(i, LVIS_SELECTED))
+ db_delete_contact(GetRow(i)->hContact);
+
+ m_grid.SetDraw(true);
+}
+
+void QSMainDlg::DeleteOneContact(MCONTACT hContact)
+{
+ if (ServiceExists(MS_CLIST_DELETECONTACT))
+ CallService(MS_CLIST_DELETECONTACT, hContact, 0);
+ else
+ db_delete_contact(hContact);
+}
+
+wchar_t* QSMainDlg::DoMeta(MCONTACT hContact)
+{
+ for (auto &it : m_rows) {
+ if (it->hContact != hContact)
+ continue;
+
+ if (it->bIsMeta) {
+ if (it->wparam == 0)
+ it->wparam = ++hLastMeta;
+ }
+ else if (it->bIsSub)
+ it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam);
+
+ if (it->wparam > 0) {
+ CMStringW tmp(FORMAT, L"[%d]", int(it->wparam));
+ if (it->lparam > 0)
+ tmp.AppendFormat(L" %d", int(it->lparam));
+ return tmp.Detach();
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+void QSMainDlg::DrawSB()
+{
+ CStatusBarItem global(0, 0);
+ for (auto &it : m_sbdata) {
+ global.found += it->found;
+ global.liston += it->liston;
+ global.online += it->online;
+ global.total += it->total;
+ }
+
+ CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online);
+
+ RECT rc;
+ HDC hdc = GetDC(hwndStatusBar);
+ DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT);
+ ReleaseDC(hwndStatusBar, hdc);
+
+ int all = rc.right - rc.left, i = 1;
+
+ mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2)));
+ parts[0] = all;
+ for (auto &it : m_sbdata) {
+ UNREFERENCED_PARAMETER(it);
+ all += 55;
+ parts[i++] = all;
+ }
+ parts[i] = -1;
+ SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get()));
+ SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str()));
+
+ i = 1;
+ for (auto &it : m_sbdata) {
+ HICON hIcon;
+ wchar_t c, *pc;
+ if (it->bAccDel) {
+ c = '!';
+ pc = TranslateT("deleted");
+ hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
+ }
+ else if (it->bAccOff) {
+ c = '?';
+ pc = TranslateT("disabled");
+ hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
+ }
+ else {
+ c = ' ';
+ pc = TranslateT("active");
+ hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE);
+ }
+
+ SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon);
+
+ buf.Format(L"%c %d", c, it->found);
+ SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str()));
+
+ auto *pa = Proto_GetAccount(it->szProto);
+ buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online);
+ SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str()));
+ i++;
+ }
+}
+
+void QSMainDlg::FillGrid()
+{
+ m_grid.SetDraw(false);
+
+ for (auto &it: m_rows)
+ ProcessLine(it);
+
+ m_grid.SetDraw(true);
+ InvalidateRect(m_grid.GetHwnd(), 0, FALSE);
+
+ Sort();
+ UpdateSB();
+ AdvancedFilter();
+
+ m_grid.SetCurSel(0);
+}
+
+void QSMainDlg::FillProtoCombo()
+{
+ cmbProto.ResetContent();
+ cmbProto.AddString(TranslateT("All"));
+
+ for (auto &it : Accounts())
+ cmbProto.AddString(it->tszAccountName, (LPARAM)it);
+
+ cmbProto.SetCurSel(0);
+}
+
+int QSMainDlg::FindItem(CRowItem *pRow)
+{
+ if (pRow == nullptr)
+ return -1;
+
+ LV_FINDINFO fi = {};
+ fi.flags = LVFI_PARAM;
+ fi.lParam = LPARAM(pRow);
+ return m_grid.FindItem(-1, &fi);
+}
+
+int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum)
+{
+ for (auto &it : m_rows) {
+ if (it->hContact != hMeta)
+ continue;
+
+ // new meta
+ if (it->wparam == 0) {
+ it->wparam = ++hLastMeta;
+ it->lparam = 0;
+ }
+ metaNum = it->wparam;
+ it->lparam++;
+ return it->lparam;
+ }
+
+ return 0;
+}
+
+CRowItem* QSMainDlg::FindRow(MCONTACT hContact)
+{
+ for (auto &it : m_rows)
+ if (it->hContact == hContact)
+ return it;
+
+ return nullptr;
+}
+
+MCONTACT QSMainDlg::GetFocusedContact()
+{
+ int idx = m_grid.GetSelectionMark();
+ if (idx == -1)
+ return -1;
+
+ INT_PTR data = m_grid.GetItemData(idx);
+ return (data == -1) ? -1 : ((CRowItem *)data)->hContact;
+}
+
+int QSMainDlg::GetLVSubItem(int x, int y)
+{
+ LV_HITTESTINFO info = {};
+ info.pt.x = x;
+ info.pt.y = y;
+ ScreenToClient(m_grid.GetHwnd(), &info.pt);
+ if (m_grid.SubItemHitTest(&info) == -1)
+ return -1;
+
+ return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1;
+}
+
+void QSMainDlg::PrepareTable(bool bReset)
+{
+ m_grid.DeleteAllItems();
+
+ HDITEM hdi = {};
+ hdi.mask = HDI_FORMAT;
+
+ int old = tableColumns;
+ tableColumns = 0;
+
+ LV_COLUMN lvc = {};
+ lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled)
+ AddColumn(tableColumns++, it);
+
+ it->SetSpecialColumns();
+ }
+
+ if (bReset)
+ for (int i = old + tableColumns - 1; i >= tableColumns; i--)
+ m_grid.DeleteColumn(i);
+}
+
+bool QSMainDlg::PrepareToFill()
+{
+ if (g_plugin.m_columns.getCount() == 0)
+ return false;
+
+ for (auto &it : g_plugin.m_columns)
+ if (it->bEnabled)
+ it->bInit = true;
+
+ hLastMeta = 0;
+
+ m_rows.destroy();
+ for (auto &hContact : Contacts())
+ m_rows.insert(new CRowItem(hContact, this));
+
+ return m_rows.getCount() != 0;
+}
+
+void QSMainDlg::ProcessLine(CRowItem *pRow, bool test)
+{
+ if (pRow->bDeleted)
+ return;
+
+ if (test)
+ pRow->bPattern = CheckPattern(pRow);
+
+ if (pRow->bPattern) {
+ if (!pRow->bActive) {
+ if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) {
+ // check for proto in combo
+ if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) {
+ pRow->bActive = true;
+ AddContactToList(pRow->hContact, pRow);
+ }
+ }
+ }
+ }
+ else if (pRow->bActive) {
+ pRow->bActive = false;
+ m_grid.DeleteItem(FindItem(pRow));
+ }
+}
+
+void QSMainDlg::SaveColumnOrder()
+{
+ int idx = 0, col = 0;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->bEnabled) {
+ it->width = m_grid.GetColumnWidth(col++);
+ g_plugin.SaveColumn(idx, *it);
+ }
+ idx++;
+ }
+}
+
+void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact)
+{
+ if (hContact) {
+ Clist_ContactDoubleClicked(hContact);
+ if (g_plugin.m_flags & QSO_AUTOCLOSE)
+ Close();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// contact menu
+
+static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param)
+{
+ ((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param);
+ return 0;
+}
+
+void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col)
+{
+ auto &pCol = g_plugin.m_columns[col];
+
+ auto *pRow = FindRow(hContact);
+ if (pRow == nullptr)
+ return;
+
+ const char *szModule = pCol.module;
+ if (szModule == nullptr)
+ szModule = pRow->szProto;
+
+ auto &pVal = pRow->pValues[col];
+ CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title);
+
+ ENTER_STRING es = {};
+ es.szModuleName = MODULENAME;
+ es.caption = TranslateT("Enter new cell value");
+ es.ptszInitVal = pVal.text;
+ if (!EnterString(&es))
+ return;
+
+ replaceStrW(pVal.text, es.ptszResult);
+ if (pCol.datatype != QSTS_STRING)
+ pVal.data = _wtoi(pVal.text);
+
+ switch (pCol.datatype) {
+ case QSTS_BYTE:
+ db_set_b(hContact, szModule, pCol.setting, pVal.data);
+ break;
+ case QSTS_WORD:
+ db_set_w(hContact, szModule, pCol.setting, pVal.data);
+ break;
+ case QSTS_DWORD:
+ case QSTS_SIGNED:
+ case QSTS_HEXNUM:
+ db_set_dw(hContact, szModule, pCol.setting, pVal.data);
+ break;
+
+ case QSTS_STRING:
+ db_set_ws(hContact, szModule, pCol.setting, pVal.text);
+ break;
+ }
+
+ UpdateLVCell(FindItem(pRow), col, pVal.text);
+}
+
+void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col)
+{
+ if (hContact == 0)
+ return;
+
+ HANDLE srvhandle = 0;
+
+ bool bDoit = false;
+ if (col >= 0) {
+ if ((col = ListViewToColumn(col)) == -1)
+ return;
+
+ auto &pCol = g_plugin.m_columns[col];
+ if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) {
+ bDoit = true;
+
+ srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col);
+
+ if (mnuhandle == nullptr) {
+ CMenuItem mi(&g_plugin);
+ SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81);
+ mi.name.a = LPGEN("Change setting through QS");
+ mi.pszService = "QS/Dummy";
+ mnuhandle = Menu_AddContactMenuItem(&mi);
+ }
+ else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0);
+ }
+ }
+
+ POINT pt;
+ GetCursorPos(&pt);
+ HMENU hMenu = Menu_BuildContactMenu(hContact);
+ if (hMenu) {
+ int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0);
+ if (iCmd) {
+ if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) {
+ if (g_plugin.m_flags & QSO_AUTOCLOSE)
+ CloseSrWindow();
+ }
+ }
+
+ ::DestroyMenu(hMenu);
+ }
+
+ if (srvhandle)
+ DestroyServiceFunction(srvhandle);
+ if (bDoit)
+ Menu_ShowItem(mnuhandle, false);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// muptiple selection popup menu
+
+static HMENU MakeContainerMenu()
+{
+ HMENU hMenu = CreatePopupMenu();
+
+ for (int i = 0;; i++) {
+ char setting[10];
+ _itoa_s(i, setting, 10);
+ ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting));
+ if (wszName != nullptr)
+ AppendMenuW(hMenu, MF_STRING, 300 + i, wszName);
+ else
+ break;
+ }
+
+ return hMenu;
+}
+
+void QSMainDlg::ShowMultiPopup(int cnt)
+{
+ HMENU hMenu = CreatePopupMenu();
+ AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt));
+ AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
+ AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete"));
+ AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy"));
+ AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta"));
+
+ HMENU cntmenu = MakeContainerMenu();
+ AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container"));
+
+ if (HMENU grpmenu = Clist_GroupBuildMenu(400))
+ AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group"));
+
+ POINT pt;
+ GetCursorPos(&pt);
+
+ int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0);
+ switch (iRes) {
+ case 101:
+ DeleteByList();
+ break;
+
+ case 102:
+ CopyMultiLines();
+ break;
+
+ case 103:
+ ConvertToMeta();
+ break;
+ }
+
+ if (iRes >= 300 && iRes <= 399) {
+ wchar_t buf[100];
+ if (iRes == 300) // default container, just delete setting
+ buf[0] = 0;
+ else
+ GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND);
+
+ MoveToContainer(buf);
+ }
+ else if (iRes >= 400 && iRes <= 499)
+ MoveToGroup(Clist_GroupGetName(iRes - 400));
+}
+
+void QSMainDlg::ConvertToMeta()
+{
+ MCONTACT hMeta = 0;
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) {
+ if (hMeta == 0)
+ hMeta = tmp;
+ else if (hMeta != tmp) {
+ MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR);
+ return;
+ }
+ }
+ }
+
+ if (hMeta != 0)
+ if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING))
+ return;
+
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ if (hMeta)
+ db_mc_addToMeta(pRow->hContact, hMeta);
+ else
+ db_mc_convertToMeta(pRow->hContact);
+ }
+}
+
+void QSMainDlg::MoveToContainer(const wchar_t *pwszName)
+{
+ int grcol = -1;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->isContainer) {
+ if (it->bEnabled)
+ grcol = g_plugin.m_columns.indexOf(&it);
+ else
+ it->bInit = false;
+ }
+ }
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ if (*pwszName == 0)
+ db_unset(pRow->hContact, "Tab_SRMsg", "containerW");
+ else
+ db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName);
+
+ if (grcol != -1) {
+ auto &pVal = pRow->pValues[grcol];
+ replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr);
+ UpdateLVCell(i, grcol, pwszName);
+ }
+ }
+}
+
+void QSMainDlg::MoveToGroup(const wchar_t *pwszName)
+{
+ int grcol = -1;
+ for (auto &it : g_plugin.m_columns) {
+ if (it->isGroup) {
+ if (it->bEnabled)
+ grcol = g_plugin.m_columns.indexOf(&it);
+ else
+ it->bInit = false;
+ }
+ }
+
+ int nCount = m_grid.GetItemCount();
+ for (int i = 0; i < nCount; i++) {
+ if (!m_grid.GetItemState(i, LVIS_SELECTED))
+ continue;
+
+ auto *pRow = GetRow(i);
+ Clist_SetGroup(pRow->hContact, pwszName);
+
+ if (grcol != -1) {
+ auto &pVal = pRow->pValues[grcol];
+ replaceStrW(pVal.text, pwszName);
+ UpdateLVCell(i, grcol, pwszName);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid sorting
+
+static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type)
+{
+ bool typ1, typ2;
+ UINT_PTR i1, i2;
+ int result = 0;
+ CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2;
+
+ if (type == StatusSort) {
+ i1 = r1->status, i2 = r2->status;
+ if (i1 == ID_STATUS_OFFLINE) i1 += 64;
+ if (i2 == ID_STATUS_OFFLINE) i2 += 64;
+ typ1 = typ2 = false;
+ }
+ else {
+ auto &res1 = r1->pValues[type], &res2 = r2->pValues[type];
+ i1 = res1.data, i2 = res2.data;
+ typ1 = i1 == -1; typ2 = i1 == -1;
+
+ if (typ1 && typ2) { // two strings
+ if (res1.text == 0 && res2.text == 0)
+ result = 0;
+ else if (res2.text == 0)
+ result = 1;
+ else if (res1.text == 0)
+ result = -1;
+ else
+ result = lstrcmpiW(res1.text, res2.text);
+ }
+ else if (typ1 || typ2) // string & num
+ result = (typ1) ? 1 : -1;
+ }
+
+ if (!typ1 && !typ2) { // not strings
+ if (i1 > i2)
+ result = 1;
+ else if (i1 < i2)
+ result = -1;
+ else
+ result = 0;
+ }
+
+ if (g_plugin.m_flags & QSO_SORTASC)
+ result = -result;
+ return result;
+}
+
+void QSMainDlg::Sort()
+{
+ if (g_plugin.m_sortOrder >= tableColumns)
+ g_plugin.m_sortOrder = StatusSort;
+ m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder));
+
+ if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS))
+ m_grid.SortItems(&CompareItem, StatusSort);
+}
+
+void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText)
+{
+ auto &pCol = g_plugin.m_columns[column];
+ auto *pRow = GetRow(item);
+
+ if (pwszText == nullptr) {
+ auto &pVal = pRow->pValues[column];
+ replaceStrW(pVal.text, 0);
+ pVal.LoadOneItem(pRow->hContact, pCol, this);
+ pwszText = pVal.text;
+ }
+
+ m_grid.SetItemText(item, ColumnToListView(column), pwszText);
+
+ if (pCol.bFilter)
+ ProcessLine(pRow, true);
+ if (g_plugin.m_sortOrder == column)
+ Sort();
+}
+
+void QSMainDlg::UpdateSB()
+{
+ m_sbdata.destroy();
+
+ for (auto &it : m_rows) {
+ if (it->szProto == nullptr)
+ continue;
+
+ CStatusBarItem tmp(it->szProto, it->flags);
+ auto *pItem = m_sbdata.find(&tmp);
+ if (pItem == nullptr)
+ m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags));
+
+ pItem->total++;
+
+ if (it->bActive)
+ pItem->found++;
+
+ if (it->status != ID_STATUS_OFFLINE) {
+ pItem->online++;
+ if (it->bActive)
+ pItem->liston++;
+ }
+ }
+
+ DrawSB();
+}
diff --git a/plugins/QuickSearch/src/window_row.cpp b/plugins/QuickSearch/src/window_row.cpp
index d05ddb0c43..991dcafd68 100644
--- a/plugins/QuickSearch/src/window_row.cpp
+++ b/plugins/QuickSearch/src/window_row.cpp
@@ -1,224 +1,224 @@
-/*
-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"
-
-CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) :
- hContact(_1)
-{
- auto *pa = Proto_GetContactAccount(hContact);
- if (pa != nullptr) {
- szProto = pa->szModuleName;
- if (!pa->IsEnabled())
- bAccOff = true;
-
- if (db_mc_isMeta(hContact))
- bIsMeta = true;
- else if (db_mc_isSub(hContact))
- bIsSub = true;
- }
- else {
- szProto = nullptr;
- bAccDel = true;
- }
-
- if (bAccDel || bAccOff)
- status = ID_STATUS_OFFLINE;
- else
- status = Contact::GetStatus(hContact);
-
- if (int nCount = g_plugin.m_columns.getCount()) {
- pValues = new Val[nCount];
- for (int i = 0; i < nCount; i++)
- pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg);
- }
- else pValues = nullptr;
-}
-
-CRowItem::~CRowItem()
-{
- delete[] pValues;
-}
-
-void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText)
-{
- if (g_plugin.m_flags & QSO_COLORIZE) {
- if (bAccDel) {
- clrBack = g_plugin.m_colors[bkg_del];
- clrText = g_plugin.m_colors[fgr_del];
- return;
- }
- if (bAccOff) {
- clrBack = g_plugin.m_colors[bkg_dis];
- clrText = g_plugin.m_colors[fgr_dis];
- return;
- }
- if (bIsMeta) {
- clrBack = g_plugin.m_colors[bkg_meta];
- clrText = g_plugin.m_colors[fgr_meta];
- return;
- }
- if (bIsSub) {
- clrBack = g_plugin.m_colors[bkg_sub];
- clrText = g_plugin.m_colors[fgr_sub];
- return;
- }
- if (bInList) {
- clrBack = g_plugin.m_colors[bkg_hid];
- clrText = g_plugin.m_colors[fgr_hid];
- return;
- }
- }
-
- if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) {
- clrBack = g_plugin.m_colors[bkg_odd];
- clrText = g_plugin.m_colors[fgr_odd];
- }
- else {
- clrBack = g_plugin.m_colors[bkg_norm];
- clrText = g_plugin.m_colors[fgr_norm];
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static wchar_t* int2strw(uint32_t num)
-{
- wchar_t buf[64];
- _itow_s(num, buf, 10);
- return mir_wstrdup(buf);
-}
-
-static wchar_t* hex2strw(uint32_t num)
-{
- wchar_t buf[64];
- _itow_s(num, buf, 16);
- return mir_wstrdup(buf);
-}
-
-void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg)
-{
- data = UINT_PTR(-1);
- replaceStrW(text, nullptr);
-
- switch (pCol.setting_type) {
- case QST_SCRIPT:
- {
- VARSW vars(pCol.script);
- if (g_bVarsInstalled)
- text = variables_parse(vars, 0, hContact);
- else
- text = vars.detach();
- }
- break;
-
- case QST_SERVICE:
- // !!!!!!!!!!!!!!!!!!! not implemented
- break;
-
- case QST_CONTACTINFO:
- text = Contact::GetInfo(pCol.cnftype, hContact);
- if (text)
- data = _wtoi(text);
- break;
-
- case QST_OTHER:
- switch (pCol.other) {
- case QSTO_ACCOUNT:
- if (auto *pa = Proto_GetContactAccount(hContact))
- text = mir_wstrdup(pa->tszAccountName);
- break;
-
- case QSTO_LASTSEEN:
- data = BuildLastSeenTimeInt(hContact, "SeenModule");
- text = BuildLastSeenTime(data);
- break;
-
- case QSTO_DISPLAYNAME:
- text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0));
- break;
-
- case QSTO_LASTEVENT:
- if (MEVENT hDbEvent = db_event_last(hContact)) {
- DBEVENTINFO dbei = {};
- db_event_get(hDbEvent, &dbei);
- data = dbei.timestamp;
- text = TimeToStrW(data);
- }
- else text = 0;
- break;
-
- case QSTO_METACONTACT:
- text = pDlg->DoMeta(hContact);
- break;
-
- case QSTO_EVENTCOUNT:
- data = db_event_count(hContact);
- text = int2strw(data);
- break;
- }
- break;
-
- case QST_SETTING:
- auto *szNodule = pCol.module;
- if (!mir_strlen(szNodule))
- szNodule = Proto_GetBaseAccountName(hContact);
-
- switch (pCol.datatype) {
- case QSTS_STRING:
- text = db_get_wsa(hContact, szNodule, pCol.setting);
- break;
-
- case QSTS_BYTE:
- data = db_get_b(hContact, szNodule, pCol.setting);
- text = int2strw(data);
- break;
-
- case QSTS_WORD:
- data = db_get_w(hContact, szNodule, pCol.setting);
- text = int2strw(data);
- break;
-
- case QSTS_DWORD:
- if (pCol.setting == nullptr) {
- data = hContact;
- text = hex2strw(data);
- }
- else {
- data = db_get_dw(hContact, szNodule, pCol.setting);
- text = int2strw(data);
- }
- break;
-
- case QSTS_SIGNED:
- data = db_get_dw(hContact, szNodule, pCol.setting);
- text = int2strw(data);
- break;
-
- case QSTS_HEXNUM:
- data = db_get_dw(hContact, szNodule, pCol.setting);
- text = hex2strw(data);
- break;
-
- case QSTS_TIMESTAMP:
- data = db_get_dw(hContact, szNodule, pCol.setting);
- if (data != 0)
- text = TimeToStrW(data);
- break;
- }
- }
-}
+/*
+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"
+
+CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) :
+ hContact(_1)
+{
+ auto *pa = Proto_GetContactAccount(hContact);
+ if (pa != nullptr) {
+ szProto = pa->szModuleName;
+ if (!pa->IsEnabled())
+ bAccOff = true;
+
+ if (db_mc_isMeta(hContact))
+ bIsMeta = true;
+ else if (db_mc_isSub(hContact))
+ bIsSub = true;
+ }
+ else {
+ szProto = nullptr;
+ bAccDel = true;
+ }
+
+ if (bAccDel || bAccOff)
+ status = ID_STATUS_OFFLINE;
+ else
+ status = Contact::GetStatus(hContact);
+
+ if (int nCount = g_plugin.m_columns.getCount()) {
+ pValues = new Val[nCount];
+ for (int i = 0; i < nCount; i++)
+ pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg);
+ }
+ else pValues = nullptr;
+}
+
+CRowItem::~CRowItem()
+{
+ delete[] pValues;
+}
+
+void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText)
+{
+ if (g_plugin.m_flags & QSO_COLORIZE) {
+ if (bAccDel) {
+ clrBack = g_plugin.m_colors[bkg_del];
+ clrText = g_plugin.m_colors[fgr_del];
+ return;
+ }
+ if (bAccOff) {
+ clrBack = g_plugin.m_colors[bkg_dis];
+ clrText = g_plugin.m_colors[fgr_dis];
+ return;
+ }
+ if (bIsMeta) {
+ clrBack = g_plugin.m_colors[bkg_meta];
+ clrText = g_plugin.m_colors[fgr_meta];
+ return;
+ }
+ if (bIsSub) {
+ clrBack = g_plugin.m_colors[bkg_sub];
+ clrText = g_plugin.m_colors[fgr_sub];
+ return;
+ }
+ if (bInList) {
+ clrBack = g_plugin.m_colors[bkg_hid];
+ clrText = g_plugin.m_colors[fgr_hid];
+ return;
+ }
+ }
+
+ if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) {
+ clrBack = g_plugin.m_colors[bkg_odd];
+ clrText = g_plugin.m_colors[fgr_odd];
+ }
+ else {
+ clrBack = g_plugin.m_colors[bkg_norm];
+ clrText = g_plugin.m_colors[fgr_norm];
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static wchar_t* int2strw(uint32_t num)
+{
+ wchar_t buf[64];
+ _itow_s(num, buf, 10);
+ return mir_wstrdup(buf);
+}
+
+static wchar_t* hex2strw(uint32_t num)
+{
+ wchar_t buf[64];
+ _itow_s(num, buf, 16);
+ return mir_wstrdup(buf);
+}
+
+void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg)
+{
+ data = UINT_PTR(-1);
+ replaceStrW(text, nullptr);
+
+ switch (pCol.setting_type) {
+ case QST_SCRIPT:
+ {
+ VARSW vars(pCol.script);
+ if (g_bVarsInstalled)
+ text = variables_parse(vars, 0, hContact);
+ else
+ text = vars.detach();
+ }
+ break;
+
+ case QST_SERVICE:
+ // !!!!!!!!!!!!!!!!!!! not implemented
+ break;
+
+ case QST_CONTACTINFO:
+ text = Contact::GetInfo(pCol.cnftype, hContact);
+ if (text)
+ data = _wtoi(text);
+ break;
+
+ case QST_OTHER:
+ switch (pCol.other) {
+ case QSTO_ACCOUNT:
+ if (auto *pa = Proto_GetContactAccount(hContact))
+ text = mir_wstrdup(pa->tszAccountName);
+ break;
+
+ case QSTO_LASTSEEN:
+ data = BuildLastSeenTimeInt(hContact, "SeenModule");
+ text = BuildLastSeenTime(data);
+ break;
+
+ case QSTO_DISPLAYNAME:
+ text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0));
+ break;
+
+ case QSTO_LASTEVENT:
+ if (MEVENT hDbEvent = db_event_last(hContact)) {
+ DBEVENTINFO dbei = {};
+ db_event_get(hDbEvent, &dbei);
+ data = dbei.timestamp;
+ text = TimeToStrW(data);
+ }
+ else text = 0;
+ break;
+
+ case QSTO_METACONTACT:
+ text = pDlg->DoMeta(hContact);
+ break;
+
+ case QSTO_EVENTCOUNT:
+ data = db_event_count(hContact);
+ text = int2strw(data);
+ break;
+ }
+ break;
+
+ case QST_SETTING:
+ auto *szNodule = pCol.module;
+ if (!mir_strlen(szNodule))
+ szNodule = Proto_GetBaseAccountName(hContact);
+
+ switch (pCol.datatype) {
+ case QSTS_STRING:
+ text = db_get_wsa(hContact, szNodule, pCol.setting);
+ break;
+
+ case QSTS_BYTE:
+ data = db_get_b(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ break;
+
+ case QSTS_WORD:
+ data = db_get_w(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ break;
+
+ case QSTS_DWORD:
+ if (pCol.setting == nullptr) {
+ data = hContact;
+ text = hex2strw(data);
+ }
+ else {
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ }
+ break;
+
+ case QSTS_SIGNED:
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ text = int2strw(data);
+ break;
+
+ case QSTS_HEXNUM:
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ text = hex2strw(data);
+ break;
+
+ case QSTS_TIMESTAMP:
+ data = db_get_dw(hContact, szNodule, pCol.setting);
+ if (data != 0)
+ text = TimeToStrW(data);
+ break;
+ }
+ }
+}
diff --git a/plugins/Rate/src/stdafx.cxx b/plugins/Rate/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Rate/src/stdafx.cxx
+++ b/plugins/Rate/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/plugins/RecentContacts/src/stdafx.cxx b/plugins/RecentContacts/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/RecentContacts/src/stdafx.cxx
+++ b/plugins/RecentContacts/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/plugins/RemovePersonalSettings/src/rps.cpp b/plugins/RemovePersonalSettings/src/rps.cpp
index fe68e8d6cb..1c6385f2ab 100644
--- a/plugins/RemovePersonalSettings/src/rps.cpp
+++ b/plugins/RemovePersonalSettings/src/rps.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-05 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/RemovePersonalSettings/src/stdafx.cxx b/plugins/RemovePersonalSettings/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/RemovePersonalSettings/src/stdafx.cxx
+++ b/plugins/RemovePersonalSettings/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/plugins/Restart/src/stdafx.cxx b/plugins/Restart/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Restart/src/stdafx.cxx
+++ b/plugins/Restart/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/plugins/Scriver/src/stdafx.cxx b/plugins/Scriver/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Scriver/src/stdafx.cxx
+++ b/plugins/Scriver/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/plugins/Scriver/src/version.h b/plugins/Scriver/src/version.h
index 42c116f4fa..d6cf1cca6b 100644
--- a/plugins/Scriver/src/version.h
+++ b/plugins/Scriver/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Scriver - send and receive instant messages."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Scriver"
-#define __COPYRIGHT "© 2000-2012 Miranda IM project, 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2000-2012 Miranda IM project, 2012-23 Miranda NG team"
diff --git a/plugins/SecureIM/src/stdafx.cpp b/plugins/SecureIM/src/stdafx.cpp
index b1991ac69e..73ac0be178 100644
--- a/plugins/SecureIM/src/stdafx.cpp
+++ b/plugins/SecureIM/src/stdafx.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/plugins/SeenPlugin/src/stdafx.cxx b/plugins/SeenPlugin/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SeenPlugin/src/stdafx.cxx
+++ b/plugins/SeenPlugin/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/plugins/SendScreenshotPlus/src/CSend.cpp b/plugins/SendScreenshotPlus/src/CSend.cpp
index 5a5e72e03e..3e83d4b6c6 100644
--- a/plugins/SendScreenshotPlus/src/CSend.cpp
+++ b/plugins/SendScreenshotPlus/src/CSend.cpp
@@ -1,613 +1,613 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with 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 CSEND_DIALOG 8800
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSend::CSend(HWND /*Owner*/, MCONTACT hContact, bool bAsync, bool bSilent) :
- m_bDeleteAfterSend(false),
- m_bAsync(bAsync),
- m_bSilent(bSilent),
- m_pszFile(nullptr),
- m_pszFileDesc(nullptr),
- m_pszSendTyp(nullptr),
- m_pszProto(nullptr),
- m_EnableItem(0),
- m_ChatRoom(0),
- m_cbEventMsg(0),
- m_hSend(nullptr),
- m_hOnSend(nullptr),
- m_ErrorMsg(nullptr),
- m_ErrorTitle(nullptr)
-{
- SetContact(hContact);
-}
-
-CSend::~CSend()
-{
- mir_free(m_pszFile);
- mir_free(m_pszFileDesc);
- mir_free(m_ErrorMsg);
- mir_free(m_ErrorTitle);
- if (m_hOnSend) UnhookEvent(m_hOnSend);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CSend::SetContact(MCONTACT hContact)
-{
- m_hContact = hContact;
- if (hContact) {
- m_pszProto = Proto_GetBaseAccountName(hContact);
- m_ChatRoom = Contact::IsGroupChat(hContact, m_pszProto);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-INT_PTR CALLBACK CSend::ResultDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- switch (uMsg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- Window_SetIcon_IcoLib(hwndDlg, GetIconHandle(ICO_MAIN));
- {
- CSend *self = (CSend*)lParam;
- SetDlgItemText(hwndDlg, IDC_HEADERBAR, CMStringW(TranslateT("Resulting URL from\n")) + self->m_pszSendTyp);
-
- SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIconBtn(ICO_BTN_ARROWR));
- SetDlgItemTextA(hwndDlg, ID_edtURL, self->m_URL);
- if (self->m_URLthumb) {
- SetDlgItemTextA(hwndDlg, ID_edtURLthumb, self->m_URLthumb);
- }
- else {
- SetDlgItemTextA(hwndDlg, ID_edtURLthumb, "-");
- for (int i = ID_btnThumbCopy; i <= ID_edtURLthumb; ++i) {
- EnableWindow(GetDlgItem(hwndDlg, i), FALSE);
- }
- }
- if (!self->m_pszFileDesc)
- SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_ErrorTitle);
- else
- SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_pszFileDesc);
- SendDlgItemMessage(hwndDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY));
- SendDlgItemMessage(hwndDlg, IDOK, BUTTONTRANSLATE, 0, 0);
- SendDlgItemMessage(hwndDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_CANCEL));
- SendDlgItemMessage(hwndDlg, IDCANCEL, BUTTONTRANSLATE, 0, 0);
- for (int i = ID_btnCopy; i <= ID_btnThumbBBC2; ++i) {
- SendDlgItemMessage(hwndDlg, i, BUTTONSETASTHEMEDBTN, 0, 0);
- SendDlgItemMessage(hwndDlg, i, BUTTONSETASFLATBTN, 1, 0);
- switch (i) {
- case ID_btnCopy:
- case ID_btnThumbCopy:
- SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY));
- SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy"), BATF_UNICODE);
- break;
- case ID_btnBBC:
- case ID_btnThumbBBC:
- SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBC));
- SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode"), BATF_UNICODE);
- break;
- default:
- SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBCLNK));
- SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode w/ link"), BATF_UNICODE);
- }
- }
- }
- return TRUE;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDCANCEL:
- DestroyWindow(hwndDlg);
- return TRUE;
-
- case IDOK:
- case ID_btnCopy:
- case ID_btnThumbCopy:
- case ID_btnBBC:
- case ID_btnThumbBBC:
- case ID_btnThumbBBC2:
- wchar_t tmp[2048];
- int edtID = ID_edtURL;
- int bbc = 0;
- switch (LOWORD(wParam)) {
- case ID_btnThumbBBC2: ++bbc;
- case ID_btnThumbBBC: ++bbc;
- case ID_btnThumbCopy:
- edtID = ID_edtURLthumb;
- break;
- case ID_btnBBC: ++bbc;
- break;
- }
- size_t len;
- if (bbc) {
- if (bbc == 1) {
- memcpy(tmp, L"[img]", 5 * sizeof(wchar_t)); len = 5;
- len += GetDlgItemText(hwndDlg, edtID, tmp + len, 2048 - 11);
- memcpy(tmp + len, L"[/img]", 7 * sizeof(wchar_t)); len += 7;
- }
- else {
- memcpy(tmp, L"[url=", 5 * sizeof(wchar_t)); len = 5;
- len += GetDlgItemText(hwndDlg, ID_edtURL, tmp + len, 1024);
- memcpy(tmp + len, L"][img]", 6 * sizeof(wchar_t)); len += 6;
- len += GetDlgItemText(hwndDlg, edtID, tmp + len, 1024);
- memcpy(tmp + len, L"[/img][/url]", 13 * sizeof(wchar_t)); len += 12;
- }
- }
- else len = GetDlgItemText(hwndDlg, edtID, tmp, _countof(tmp));
-
- Utils_ClipboardCopy(CMStringW(tmp, len + 1));
-
- if (LOWORD(wParam) == IDOK)
- DestroyWindow(hwndDlg);
- return TRUE;
- }
- }
- return FALSE;
-}
-
-void CSend::svcSendMsgExit(const char* szMessage)
-{
- if (m_bSilent) {
- Exit(ACKRESULT_SUCCESS); return;
- }
- if (!m_hContact) {
- if (!m_pszFileDesc)
- m_pszFileDesc = mir_a2u(szMessage);
- Exit(CSEND_DIALOG); return;
- }
-
- if (m_ChatRoom) {
- CMStringW tmp(szMessage);
- if (m_pszFileDesc) {
- tmp.Append(L"\r\n");
- tmp.Append(m_pszFileDesc);
- }
-
- int res = GC_RESULT_NOSESSION;
- int cnt = g_chatApi.SM_GetCount(m_pszProto);
-
- // loop on all gc session to get the right (save) ptszID for the chatroom from m_hContact
- GC_INFO gci = { 0 };
- gci.pszModule = m_pszProto;
- for (int i = 0; i < cnt; i++) {
- gci.iItem = i;
- gci.Flags = GCF_BYINDEX | GCF_HCONTACT | GCF_ID;
- Chat_GetInfo(&gci);
- if (gci.hContact == m_hContact) {
- Chat_SendUserMessage(m_pszProto, gci.pszID, tmp);
- res = 200;
- break;
- }
- }
- Exit(res); return;
- }
- else {
- m_szEventMsg = szMessage;
- if (m_pszFileDesc && m_pszFileDesc[0] != NULL) {
- m_szEventMsg.Append("\r\n");
- m_szEventMsg.Append(_T2A(m_pszFileDesc));
- m_cbEventMsg = m_szEventMsg.GetLength() + 1;
- }
-
- // create a HookEventObj on ME_PROTO_ACK
- if (!m_hOnSend)
- m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this);
-
- // start PSS_MESSAGE service
- m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_MESSAGE, NULL, ptrA(mir_utf8encode(m_szEventMsg)));
-
- // check we actually got an ft handle back from the protocol
- if (!m_hSend) {
- Unhook();
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED); return;
- }
- }
-}
-
-void CSend::svcSendFileExit()
-{
- // szMessage should be encoded as the File followed by the description, the
- // separator being a single nul (\0). If there is no description, do not forget
- // to end the File with two nuls.
- if (m_bSilent) {
- Exit(ACKRESULT_SUCCESS); return;
- }
-
- if (!m_hContact) {
- Error(LPGENW("%s requires a valid contact!"), m_pszSendTyp);
- Exit(ACKRESULT_FAILED); return;
- }
-
- m_szEventMsg = _T2A(m_pszFile);
-
- if (m_pszFileDesc && m_pszFileDesc[0] != NULL) {
- m_szEventMsg.AppendChar(0);
- m_szEventMsg.Append(_T2A(m_pszFileDesc));
- }
-
- m_cbEventMsg = m_szEventMsg.GetLength() + 1;
-
- // Сreate a HookEventObj on ME_PROTO_ACK
- if (!m_hOnSend) {
- m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this);
- }
-
- // Start miranda PSS_FILE based on mir ver (T)
- wchar_t* ppFile[2] = { nullptr, nullptr };
- wchar_t* pDesc = mir_wstrdup(m_pszFileDesc);
- ppFile[0] = mir_wstrdup(m_pszFile);
- ppFile[1] = nullptr;
- m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_FILE, (WPARAM)pDesc, (LPARAM)ppFile);
- mir_free(pDesc);
- mir_free(ppFile[0]);
-
- // check we actually got an ft handle back from the protocol
- if (!m_hSend) {
- Unhook();
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED); return;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSend::OnSend(void *obj, WPARAM, LPARAM lParam)
-{
- CSend* self = (CSend*)obj;
- ACKDATA *ack = (ACKDATA*)lParam;
- if (ack->hProcess != self->m_hSend)
- return 0;
-
- switch (ack->result) {
- case ACKRESULT_INITIALISING: // SetFtStatus(hwndDlg, LPGENW("Initialising..."), 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_LISTENING: // SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break;
- case ACKRESULT_CONNECTED: // SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break;
- case ACKRESULT_SENTREQUEST: // SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break;
- case ACKRESULT_NEXTFILE: // SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT);
- case ACKRESULT_FILERESUME:
- case ACKRESULT_DATA: // transfer is on progress
- break;
- case ACKRESULT_DENIED:
- self->Unhook();
- self->Exit(ack->result);
- break;
- case ACKRESULT_FAILED:
- self->Unhook();
- self->Exit(ack->result);
- // type=ACKTYPE_MESSAGE, result=success/failure, (char*)lParam=error message or NULL.
- // type=ACKTYPE_FILE, result=ACKRESULT_FAILED then lParam=(LPARAM)(const char*)szReason
- break;
- case ACKRESULT_SUCCESS:
- self->Unhook();
- switch (ack->type) {
- case ACKTYPE_CHAT:
- break;
- case ACKTYPE_MESSAGE:
- self->DB_EventAdd((uint16_t)EVENTTYPE_MESSAGE);
- break;
- case ACKTYPE_FILE:
- self->m_szEventMsg.Insert(0, "aaaa");
- self->m_cbEventMsg += sizeof(uint32_t);
- self->DB_EventAdd((uint16_t)EVENTTYPE_FILE);
- break;
- }
- self->Exit(ack->result);
- break;
- }
- return 0;
-}
-
-void CSend::DB_EventAdd(uint16_t EventType)
-{
- DBEVENTINFO dbei = {};
- dbei.szModule = m_pszProto;
- dbei.eventType = EventType;
- dbei.flags = DBEF_SENT;
- dbei.timestamp = time(0);
- dbei.flags |= DBEF_UTF;
- dbei.cbBlob = m_cbEventMsg;
- dbei.pBlob = (uint8_t*)m_szEventMsg.GetString();
- db_event_add(m_hContact, &dbei);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CSend::Error(LPCTSTR pszFormat, ...)
-{
- wchar_t tszMsg[MAX_SECONDLINE];
-
- mir_snwprintf(tszMsg, L"%s - %s", _A2W(MODULENAME), TranslateT("Error"));
- mir_free(m_ErrorTitle), m_ErrorTitle = mir_wstrdup(tszMsg);
-
- va_list vl;
- va_start(vl, pszFormat);
- mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl);
- va_end(vl);
- mir_free(m_ErrorMsg), m_ErrorMsg = mir_wstrdup(tszMsg);
-
- memset(&m_box, 0, sizeof(MSGBOX));
- m_box.cbSize = sizeof(MSGBOX);
- m_box.hParent = nullptr;
- m_box.hiLogo = GetIcon(ICO_MAIN);
- m_box.hiMsg = nullptr;
- m_box.ptszTitle = m_ErrorTitle;
- m_box.ptszMsg = m_ErrorMsg;
- m_box.uType = MB_OK | MB_ICON_ERROR;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CSend::Exit(unsigned int Result)
-{
- if (!m_bSilent) {
- bool err = true;
- switch (Result) {
- case CSEND_DIALOG:
- Skin_PlaySound("FileDone");
- DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UResultForm), nullptr, ResultDialogProc, (LPARAM)this);
- err = false;
- break;
- case ACKRESULT_SUCCESS:
- case GC_RESULT_SUCCESS:
- Skin_PlaySound("FileDone");
- err = false;
- break;
- case ACKRESULT_DENIED:
- Skin_PlaySound("FileDenied");
- Error(L"%s (%i):\nFile transfer denied.", TranslateW(m_pszSendTyp), Result);
- MsgBoxService(NULL, (LPARAM)&m_box);
- err = false;
- break;
- case GC_RESULT_WRONGVER: // You appear to be using the wrong version of GC API.
- Error(L"%s (%i):\nYou appear to be using the wrong version of GC API", TranslateT("GCHAT error"), Result);
- break;
- case GC_RESULT_ERROR: // An internal GC error occurred.
- Error(L"%s (%i):\nAn internal GC error occurred.", TranslateT("GCHAT error"), Result);
- break;
- case GC_RESULT_NOSESSION: // contact has no open GC session
- Error(L"%s (%i):\nContact has no open GC session.", TranslateT("GCHAT error"), Result);
- break;
- case ACKRESULT_FAILED:
- default:
- break;
- }
- if (err) {
- Skin_PlaySound("FileFailed");
- if (m_ErrorMsg) MsgBoxService(NULL, (LPARAM)&m_box);
- else MsgErr(nullptr, LPGENW("An unknown error has occurred."));
- }
- }
- if (m_pszFile && *m_pszFile && m_bDeleteAfterSend && m_EnableItem&SS_DLG_DELETEAFTERSSEND) {
- DeleteFile(m_pszFile), m_pszFile = nullptr;
- }
- if (m_bAsync)
- delete this; // deletes derived class since destructor is virtual (which also auto-calls base dtor)
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// helper functions used for HTTP uploads
-
-#define snprintf _snprintf
-
-const char* CSend::GetHTMLContent(char* str, const char* startTag, const char* endTag)
-{
- char* begin = strstr(str, startTag);
- if (!begin) return nullptr;
- begin += mir_strlen(startTag) - 1;
- for (; *begin != '>' && *begin; ++begin);
- if (*begin) {
- char* end = strstr(++begin, endTag);
- if (end) *end = 0;
- }
- return begin;
-}
-
-static void HTTPFormAppendData(NETLIBHTTPREQUEST* nlhr, size_t* dataMax, char** dataPos, const char* data, size_t len)
-{
- nlhr->dataLength = (*dataPos - nlhr->pData);
- if (nlhr->dataLength + len >= *dataMax) {
- *dataPos = nlhr->pData;
- *dataMax += 0x1000 + 0x1000 * (len >> 12);
- nlhr->pData = (char*)mir_realloc(nlhr->pData, *dataMax);
- if (!nlhr->pData) mir_free(*dataPos);
- *dataPos = nlhr->pData;
- if (!*dataPos)
- return;
- *dataPos += nlhr->dataLength;
- }
- if (data) {
- memcpy(*dataPos, data, sizeof(char)*len); *dataPos += len;
- nlhr->dataLength += (int)len; // not necessary
- }
-}
-
-void CSend::HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr)
-{
- mir_free(nlhr->headers[0].szValue), nlhr->headers[0].szValue = nullptr;
- mir_free(nlhr->headers), nlhr->headers = nullptr;
- mir_free(nlhr->pData), nlhr->pData = nullptr;
-}
-
-int CSend::HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum)
-{
- char boundary[16];
- memcpy(boundary, "--M461C/", 8);
- {
- union
- {
- uint32_t num;
- unsigned char cr[4];
- }; num = GetTickCount() ^ 0x8000;
- for (int i = 0; i < 4; ++i) {
- unsigned char chcode = cr[i] >> 4;
- boundary[8 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode;
- chcode = cr[i] & 0x0f;
- boundary[9 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode;
- }
- }
- nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
- nlhr->requestType = requestType;
- nlhr->flags = NLHRF_HTTP11;
- if (!strncmp(url, "https://", 8)) nlhr->flags |= NLHRF_SSL;
- nlhr->szUrl = (char*)url;
- nlhr->headersCount = 3;
- for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
- if (!(iter->flags&HTTPFF_HEADER)) break;
- ++nlhr->headersCount;
- }
- nlhr->headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER)*nlhr->headersCount);
- {
- char* contenttype = (char*)mir_alloc(sizeof(char)*(30 + 1 + sizeof(boundary)));
- memcpy(contenttype, "multipart/form-data; boundary=", 30);
- memcpy(contenttype + 30, boundary, sizeof(boundary));
- contenttype[30 + sizeof(boundary)] = '\0';
- nlhr->headers[0].szName = "Content-Type";
- nlhr->headers[0].szValue = contenttype;
- nlhr->headers[1].szName = "User-Agent";
- nlhr->headers[1].szValue = __USER_AGENT_STRING;
- nlhr->headers[2].szName = "Accept-Language";
- nlhr->headers[2].szValue = "en-us,en;q=0.8";
- int i = 3;
- for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
- if (!(iter->flags&HTTPFF_HEADER)) break;
- nlhr->headers[i].szName = (char*)iter->name;
- nlhr->headers[i++].szValue = (char*)iter->value_str;
- }
- }
- char* dataPos = nlhr->pData;
- size_t dataMax = 0;
- for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
- if (iter->flags&HTTPFF_HEADER) continue;
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 40);
- memset(dataPos, '-', 2); dataPos += 2;
- memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary);
- memcpy(dataPos, "\r\nContent-Disposition: form-data; name=\"", 40); dataPos += 40;
- size_t namelen = mir_strlen(iter->name), valuelen = 0;
- if (!(iter->flags&HTTPFF_INT))
- valuelen = mir_strlen(iter->value_str);
- if (iter->flags&HTTPFF_FILE) {
- const char* filename = strrchr(iter->value_str, '\\');
- if (!filename) filename = strrchr(iter->value_str, '/');
- if (!filename) filename = iter->value_str;
- else ++filename;
- valuelen = mir_strlen(filename);
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 13 + valuelen + 17);
- memcpy(dataPos, iter->name, namelen); dataPos += namelen;
- memcpy(dataPos, "\"; filename=\"", 13); dataPos += 13;
- memcpy(dataPos, filename, valuelen); dataPos += valuelen;
- memcpy(dataPos, "\"\r\nContent-Type: ", 17); dataPos += 17;
- /// add mime type
- const char* mime = "application/octet-stream";
- const char* fileext = strrchr(filename, '.');
- if (fileext) {
- if (!mir_strcmp(fileext, ".jpg") || !mir_strcmp(fileext, ".jpeg") || !mir_strcmp(fileext, ".jpe"))
- mime = "image/jpeg";
- else if (!mir_strcmp(fileext, ".bmp"))
- mime = "image/bmp";
- else if (!mir_strcmp(fileext, ".png"))
- mime = "image/png";
- else if (!mir_strcmp(fileext, ".gif"))
- mime = "image/gif";
- else if (!mir_strcmp(fileext, ".tif") || !mir_strcmp(fileext, ".tiff"))
- mime = "image/tiff";
- }
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, mime, mir_strlen(mime));
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, "\r\n\r\n", 4);
- /// add file content
- size_t filesize = 0;
- FILE* fp = fopen(iter->value_str, "rb");
- if (fp) {
- fseek(fp, 0, SEEK_END);
- filesize = ftell(fp); fseek(fp, 0, SEEK_SET);
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, filesize + 2);
- if (fread(dataPos, 1, filesize, fp) != filesize) {
- fclose(fp), fp = nullptr;
- }
- }
- if (!fp) {
- HTTPFormDestroy(nlhr);
- Error(L"Error occurred when opening local file.\nAborting file upload...");
- Exit(ACKRESULT_FAILED);
- return 1;
- }
- else
- fclose(fp);
- dataPos += filesize;
- memcpy(dataPos, "\r\n", 2); dataPos += 2;
- }
- else if (iter->flags&HTTPFF_8BIT) {
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 38 + valuelen + 2);
- memcpy(dataPos, iter->name, namelen); dataPos += namelen;
- memcpy(dataPos, "\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 38); dataPos += 38;
- memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen;
- memcpy(dataPos, "\r\n", 2); dataPos += 2;
- }
- else if (iter->flags&HTTPFF_INT) {
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + 17/*max numbers*/ + 2);
- memcpy(dataPos, iter->name, namelen); dataPos += namelen;
- memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5;
- int ret = snprintf(dataPos, 17, "%Id", iter->value_int);
- if (ret < 17 && ret>0) dataPos += ret;
- memcpy(dataPos, "\r\n", 2); dataPos += 2;
- }
- else {
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + valuelen + 2);
- memcpy(dataPos, iter->name, namelen); dataPos += namelen;
- memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5;
- memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen;
- memcpy(dataPos, "\r\n", 2); dataPos += 2;
- }
- }
- HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 4);
- memset(dataPos, '-', 2); dataPos += 2;
- memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary);
- memcpy(dataPos, "--\r\n", 4); dataPos += 4;
- nlhr->dataLength = dataPos - nlhr->pData;
-#ifdef _DEBUG /// print request content to "_sendss_tmp" file for debugging
- {
- FILE* fp = fopen("_sendss_tmp", "wb");
- if (fp) {
- fprintf(fp, "--Target-- %s\n", nlhr->szUrl);
- for (int i = 0; i < nlhr->headersCount; ++i) {
- fprintf(fp, "%s: %s\n", nlhr->headers[i].szName, nlhr->headers[i].szValue);
- }
- fprintf(fp, "\n\n");
- fwrite(nlhr->pData, 1, nlhr->dataLength, fp);
- fclose(fp);
- }
- }
-#endif // _DEBUG
- 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-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 CSEND_DIALOG 8800
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSend::CSend(HWND /*Owner*/, MCONTACT hContact, bool bAsync, bool bSilent) :
+ m_bDeleteAfterSend(false),
+ m_bAsync(bAsync),
+ m_bSilent(bSilent),
+ m_pszFile(nullptr),
+ m_pszFileDesc(nullptr),
+ m_pszSendTyp(nullptr),
+ m_pszProto(nullptr),
+ m_EnableItem(0),
+ m_ChatRoom(0),
+ m_cbEventMsg(0),
+ m_hSend(nullptr),
+ m_hOnSend(nullptr),
+ m_ErrorMsg(nullptr),
+ m_ErrorTitle(nullptr)
+{
+ SetContact(hContact);
+}
+
+CSend::~CSend()
+{
+ mir_free(m_pszFile);
+ mir_free(m_pszFileDesc);
+ mir_free(m_ErrorMsg);
+ mir_free(m_ErrorTitle);
+ if (m_hOnSend) UnhookEvent(m_hOnSend);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSend::SetContact(MCONTACT hContact)
+{
+ m_hContact = hContact;
+ if (hContact) {
+ m_pszProto = Proto_GetBaseAccountName(hContact);
+ m_ChatRoom = Contact::IsGroupChat(hContact, m_pszProto);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CALLBACK CSend::ResultDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ Window_SetIcon_IcoLib(hwndDlg, GetIconHandle(ICO_MAIN));
+ {
+ CSend *self = (CSend*)lParam;
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, CMStringW(TranslateT("Resulting URL from\n")) + self->m_pszSendTyp);
+
+ SendDlgItemMessage(hwndDlg, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIconBtn(ICO_BTN_ARROWR));
+ SetDlgItemTextA(hwndDlg, ID_edtURL, self->m_URL);
+ if (self->m_URLthumb) {
+ SetDlgItemTextA(hwndDlg, ID_edtURLthumb, self->m_URLthumb);
+ }
+ else {
+ SetDlgItemTextA(hwndDlg, ID_edtURLthumb, "-");
+ for (int i = ID_btnThumbCopy; i <= ID_edtURLthumb; ++i) {
+ EnableWindow(GetDlgItem(hwndDlg, i), FALSE);
+ }
+ }
+ if (!self->m_pszFileDesc)
+ SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_ErrorTitle);
+ else
+ SetDlgItemText(hwndDlg, ID_bvlDesc, self->m_pszFileDesc);
+ SendDlgItemMessage(hwndDlg, IDOK, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY));
+ SendDlgItemMessage(hwndDlg, IDOK, BUTTONTRANSLATE, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDCANCEL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_CANCEL));
+ SendDlgItemMessage(hwndDlg, IDCANCEL, BUTTONTRANSLATE, 0, 0);
+ for (int i = ID_btnCopy; i <= ID_btnThumbBBC2; ++i) {
+ SendDlgItemMessage(hwndDlg, i, BUTTONSETASTHEMEDBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, i, BUTTONSETASFLATBTN, 1, 0);
+ switch (i) {
+ case ID_btnCopy:
+ case ID_btnThumbCopy:
+ SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_COPY));
+ SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy"), BATF_UNICODE);
+ break;
+ case ID_btnBBC:
+ case ID_btnThumbBBC:
+ SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBC));
+ SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode"), BATF_UNICODE);
+ break;
+ default:
+ SendDlgItemMessage(hwndDlg, i, BM_SETIMAGE, IMAGE_ICON, (LPARAM)GetIconBtn(ICO_BTN_BBCLNK));
+ SendDlgItemMessage(hwndDlg, i, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Copy BBCode w/ link"), BATF_UNICODE);
+ }
+ }
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ return TRUE;
+
+ case IDOK:
+ case ID_btnCopy:
+ case ID_btnThumbCopy:
+ case ID_btnBBC:
+ case ID_btnThumbBBC:
+ case ID_btnThumbBBC2:
+ wchar_t tmp[2048];
+ int edtID = ID_edtURL;
+ int bbc = 0;
+ switch (LOWORD(wParam)) {
+ case ID_btnThumbBBC2: ++bbc;
+ case ID_btnThumbBBC: ++bbc;
+ case ID_btnThumbCopy:
+ edtID = ID_edtURLthumb;
+ break;
+ case ID_btnBBC: ++bbc;
+ break;
+ }
+ size_t len;
+ if (bbc) {
+ if (bbc == 1) {
+ memcpy(tmp, L"[img]", 5 * sizeof(wchar_t)); len = 5;
+ len += GetDlgItemText(hwndDlg, edtID, tmp + len, 2048 - 11);
+ memcpy(tmp + len, L"[/img]", 7 * sizeof(wchar_t)); len += 7;
+ }
+ else {
+ memcpy(tmp, L"[url=", 5 * sizeof(wchar_t)); len = 5;
+ len += GetDlgItemText(hwndDlg, ID_edtURL, tmp + len, 1024);
+ memcpy(tmp + len, L"][img]", 6 * sizeof(wchar_t)); len += 6;
+ len += GetDlgItemText(hwndDlg, edtID, tmp + len, 1024);
+ memcpy(tmp + len, L"[/img][/url]", 13 * sizeof(wchar_t)); len += 12;
+ }
+ }
+ else len = GetDlgItemText(hwndDlg, edtID, tmp, _countof(tmp));
+
+ Utils_ClipboardCopy(CMStringW(tmp, len + 1));
+
+ if (LOWORD(wParam) == IDOK)
+ DestroyWindow(hwndDlg);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void CSend::svcSendMsgExit(const char* szMessage)
+{
+ if (m_bSilent) {
+ Exit(ACKRESULT_SUCCESS); return;
+ }
+ if (!m_hContact) {
+ if (!m_pszFileDesc)
+ m_pszFileDesc = mir_a2u(szMessage);
+ Exit(CSEND_DIALOG); return;
+ }
+
+ if (m_ChatRoom) {
+ CMStringW tmp(szMessage);
+ if (m_pszFileDesc) {
+ tmp.Append(L"\r\n");
+ tmp.Append(m_pszFileDesc);
+ }
+
+ int res = GC_RESULT_NOSESSION;
+ int cnt = g_chatApi.SM_GetCount(m_pszProto);
+
+ // loop on all gc session to get the right (save) ptszID for the chatroom from m_hContact
+ GC_INFO gci = { 0 };
+ gci.pszModule = m_pszProto;
+ for (int i = 0; i < cnt; i++) {
+ gci.iItem = i;
+ gci.Flags = GCF_BYINDEX | GCF_HCONTACT | GCF_ID;
+ Chat_GetInfo(&gci);
+ if (gci.hContact == m_hContact) {
+ Chat_SendUserMessage(m_pszProto, gci.pszID, tmp);
+ res = 200;
+ break;
+ }
+ }
+ Exit(res); return;
+ }
+ else {
+ m_szEventMsg = szMessage;
+ if (m_pszFileDesc && m_pszFileDesc[0] != NULL) {
+ m_szEventMsg.Append("\r\n");
+ m_szEventMsg.Append(_T2A(m_pszFileDesc));
+ m_cbEventMsg = m_szEventMsg.GetLength() + 1;
+ }
+
+ // create a HookEventObj on ME_PROTO_ACK
+ if (!m_hOnSend)
+ m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this);
+
+ // start PSS_MESSAGE service
+ m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_MESSAGE, NULL, ptrA(mir_utf8encode(m_szEventMsg)));
+
+ // check we actually got an ft handle back from the protocol
+ if (!m_hSend) {
+ Unhook();
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+ }
+}
+
+void CSend::svcSendFileExit()
+{
+ // szMessage should be encoded as the File followed by the description, the
+ // separator being a single nul (\0). If there is no description, do not forget
+ // to end the File with two nuls.
+ if (m_bSilent) {
+ Exit(ACKRESULT_SUCCESS); return;
+ }
+
+ if (!m_hContact) {
+ Error(LPGENW("%s requires a valid contact!"), m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+
+ m_szEventMsg = _T2A(m_pszFile);
+
+ if (m_pszFileDesc && m_pszFileDesc[0] != NULL) {
+ m_szEventMsg.AppendChar(0);
+ m_szEventMsg.Append(_T2A(m_pszFileDesc));
+ }
+
+ m_cbEventMsg = m_szEventMsg.GetLength() + 1;
+
+ // Сreate a HookEventObj on ME_PROTO_ACK
+ if (!m_hOnSend) {
+ m_hOnSend = HookEventObj(ME_PROTO_ACK, OnSend, this);
+ }
+
+ // Start miranda PSS_FILE based on mir ver (T)
+ wchar_t* ppFile[2] = { nullptr, nullptr };
+ wchar_t* pDesc = mir_wstrdup(m_pszFileDesc);
+ ppFile[0] = mir_wstrdup(m_pszFile);
+ ppFile[1] = nullptr;
+ m_hSend = (HANDLE)ProtoChainSend(m_hContact, PSS_FILE, (WPARAM)pDesc, (LPARAM)ppFile);
+ mir_free(pDesc);
+ mir_free(ppFile[0]);
+
+ // check we actually got an ft handle back from the protocol
+ if (!m_hSend) {
+ Unhook();
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSend::OnSend(void *obj, WPARAM, LPARAM lParam)
+{
+ CSend* self = (CSend*)obj;
+ ACKDATA *ack = (ACKDATA*)lParam;
+ if (ack->hProcess != self->m_hSend)
+ return 0;
+
+ switch (ack->result) {
+ case ACKRESULT_INITIALISING: // SetFtStatus(hwndDlg, LPGENW("Initialising..."), 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_LISTENING: // SetFtStatus(hwndDlg, LPGENW("Waiting for connection..."), FTS_TEXT); break;
+ case ACKRESULT_CONNECTED: // SetFtStatus(hwndDlg, LPGENW("Connected"), FTS_TEXT); break;
+ case ACKRESULT_SENTREQUEST: // SetFtStatus(hwndDlg, LPGENW("Decision sent"), FTS_TEXT); break;
+ case ACKRESULT_NEXTFILE: // SetFtStatus(hwndDlg, LPGENW("Moving to next file..."), FTS_TEXT);
+ case ACKRESULT_FILERESUME:
+ case ACKRESULT_DATA: // transfer is on progress
+ break;
+ case ACKRESULT_DENIED:
+ self->Unhook();
+ self->Exit(ack->result);
+ break;
+ case ACKRESULT_FAILED:
+ self->Unhook();
+ self->Exit(ack->result);
+ // type=ACKTYPE_MESSAGE, result=success/failure, (char*)lParam=error message or NULL.
+ // type=ACKTYPE_FILE, result=ACKRESULT_FAILED then lParam=(LPARAM)(const char*)szReason
+ break;
+ case ACKRESULT_SUCCESS:
+ self->Unhook();
+ switch (ack->type) {
+ case ACKTYPE_CHAT:
+ break;
+ case ACKTYPE_MESSAGE:
+ self->DB_EventAdd((uint16_t)EVENTTYPE_MESSAGE);
+ break;
+ case ACKTYPE_FILE:
+ self->m_szEventMsg.Insert(0, "aaaa");
+ self->m_cbEventMsg += sizeof(uint32_t);
+ self->DB_EventAdd((uint16_t)EVENTTYPE_FILE);
+ break;
+ }
+ self->Exit(ack->result);
+ break;
+ }
+ return 0;
+}
+
+void CSend::DB_EventAdd(uint16_t EventType)
+{
+ DBEVENTINFO dbei = {};
+ dbei.szModule = m_pszProto;
+ dbei.eventType = EventType;
+ dbei.flags = DBEF_SENT;
+ dbei.timestamp = time(0);
+ dbei.flags |= DBEF_UTF;
+ dbei.cbBlob = m_cbEventMsg;
+ dbei.pBlob = (uint8_t*)m_szEventMsg.GetString();
+ db_event_add(m_hContact, &dbei);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSend::Error(LPCTSTR pszFormat, ...)
+{
+ wchar_t tszMsg[MAX_SECONDLINE];
+
+ mir_snwprintf(tszMsg, L"%s - %s", _A2W(MODULENAME), TranslateT("Error"));
+ mir_free(m_ErrorTitle), m_ErrorTitle = mir_wstrdup(tszMsg);
+
+ va_list vl;
+ va_start(vl, pszFormat);
+ mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl);
+ va_end(vl);
+ mir_free(m_ErrorMsg), m_ErrorMsg = mir_wstrdup(tszMsg);
+
+ memset(&m_box, 0, sizeof(MSGBOX));
+ m_box.cbSize = sizeof(MSGBOX);
+ m_box.hParent = nullptr;
+ m_box.hiLogo = GetIcon(ICO_MAIN);
+ m_box.hiMsg = nullptr;
+ m_box.ptszTitle = m_ErrorTitle;
+ m_box.ptszMsg = m_ErrorMsg;
+ m_box.uType = MB_OK | MB_ICON_ERROR;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSend::Exit(unsigned int Result)
+{
+ if (!m_bSilent) {
+ bool err = true;
+ switch (Result) {
+ case CSEND_DIALOG:
+ Skin_PlaySound("FileDone");
+ DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UResultForm), nullptr, ResultDialogProc, (LPARAM)this);
+ err = false;
+ break;
+ case ACKRESULT_SUCCESS:
+ case GC_RESULT_SUCCESS:
+ Skin_PlaySound("FileDone");
+ err = false;
+ break;
+ case ACKRESULT_DENIED:
+ Skin_PlaySound("FileDenied");
+ Error(L"%s (%i):\nFile transfer denied.", TranslateW(m_pszSendTyp), Result);
+ MsgBoxService(NULL, (LPARAM)&m_box);
+ err = false;
+ break;
+ case GC_RESULT_WRONGVER: // You appear to be using the wrong version of GC API.
+ Error(L"%s (%i):\nYou appear to be using the wrong version of GC API", TranslateT("GCHAT error"), Result);
+ break;
+ case GC_RESULT_ERROR: // An internal GC error occurred.
+ Error(L"%s (%i):\nAn internal GC error occurred.", TranslateT("GCHAT error"), Result);
+ break;
+ case GC_RESULT_NOSESSION: // contact has no open GC session
+ Error(L"%s (%i):\nContact has no open GC session.", TranslateT("GCHAT error"), Result);
+ break;
+ case ACKRESULT_FAILED:
+ default:
+ break;
+ }
+ if (err) {
+ Skin_PlaySound("FileFailed");
+ if (m_ErrorMsg) MsgBoxService(NULL, (LPARAM)&m_box);
+ else MsgErr(nullptr, LPGENW("An unknown error has occurred."));
+ }
+ }
+ if (m_pszFile && *m_pszFile && m_bDeleteAfterSend && m_EnableItem&SS_DLG_DELETEAFTERSSEND) {
+ DeleteFile(m_pszFile), m_pszFile = nullptr;
+ }
+ if (m_bAsync)
+ delete this; // deletes derived class since destructor is virtual (which also auto-calls base dtor)
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// helper functions used for HTTP uploads
+
+#define snprintf _snprintf
+
+const char* CSend::GetHTMLContent(char* str, const char* startTag, const char* endTag)
+{
+ char* begin = strstr(str, startTag);
+ if (!begin) return nullptr;
+ begin += mir_strlen(startTag) - 1;
+ for (; *begin != '>' && *begin; ++begin);
+ if (*begin) {
+ char* end = strstr(++begin, endTag);
+ if (end) *end = 0;
+ }
+ return begin;
+}
+
+static void HTTPFormAppendData(NETLIBHTTPREQUEST* nlhr, size_t* dataMax, char** dataPos, const char* data, size_t len)
+{
+ nlhr->dataLength = (*dataPos - nlhr->pData);
+ if (nlhr->dataLength + len >= *dataMax) {
+ *dataPos = nlhr->pData;
+ *dataMax += 0x1000 + 0x1000 * (len >> 12);
+ nlhr->pData = (char*)mir_realloc(nlhr->pData, *dataMax);
+ if (!nlhr->pData) mir_free(*dataPos);
+ *dataPos = nlhr->pData;
+ if (!*dataPos)
+ return;
+ *dataPos += nlhr->dataLength;
+ }
+ if (data) {
+ memcpy(*dataPos, data, sizeof(char)*len); *dataPos += len;
+ nlhr->dataLength += (int)len; // not necessary
+ }
+}
+
+void CSend::HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr)
+{
+ mir_free(nlhr->headers[0].szValue), nlhr->headers[0].szValue = nullptr;
+ mir_free(nlhr->headers), nlhr->headers = nullptr;
+ mir_free(nlhr->pData), nlhr->pData = nullptr;
+}
+
+int CSend::HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum)
+{
+ char boundary[16];
+ memcpy(boundary, "--M461C/", 8);
+ {
+ union
+ {
+ uint32_t num;
+ unsigned char cr[4];
+ }; num = GetTickCount() ^ 0x8000;
+ for (int i = 0; i < 4; ++i) {
+ unsigned char chcode = cr[i] >> 4;
+ boundary[8 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode;
+ chcode = cr[i] & 0x0f;
+ boundary[9 + i * 2] = (chcode < 0x0a ? '0' : 'a' - 0x0a) + chcode;
+ }
+ }
+ nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+ nlhr->requestType = requestType;
+ nlhr->flags = NLHRF_HTTP11;
+ if (!strncmp(url, "https://", 8)) nlhr->flags |= NLHRF_SSL;
+ nlhr->szUrl = (char*)url;
+ nlhr->headersCount = 3;
+ for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
+ if (!(iter->flags&HTTPFF_HEADER)) break;
+ ++nlhr->headersCount;
+ }
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER)*nlhr->headersCount);
+ {
+ char* contenttype = (char*)mir_alloc(sizeof(char)*(30 + 1 + sizeof(boundary)));
+ memcpy(contenttype, "multipart/form-data; boundary=", 30);
+ memcpy(contenttype + 30, boundary, sizeof(boundary));
+ contenttype[30 + sizeof(boundary)] = '\0';
+ nlhr->headers[0].szName = "Content-Type";
+ nlhr->headers[0].szValue = contenttype;
+ nlhr->headers[1].szName = "User-Agent";
+ nlhr->headers[1].szValue = __USER_AGENT_STRING;
+ nlhr->headers[2].szName = "Accept-Language";
+ nlhr->headers[2].szValue = "en-us,en;q=0.8";
+ int i = 3;
+ for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
+ if (!(iter->flags&HTTPFF_HEADER)) break;
+ nlhr->headers[i].szName = (char*)iter->name;
+ nlhr->headers[i++].szValue = (char*)iter->value_str;
+ }
+ }
+ char* dataPos = nlhr->pData;
+ size_t dataMax = 0;
+ for (HTTPFormData* iter = frm, *end = frm + frmNum; iter != end; ++iter) {
+ if (iter->flags&HTTPFF_HEADER) continue;
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 40);
+ memset(dataPos, '-', 2); dataPos += 2;
+ memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary);
+ memcpy(dataPos, "\r\nContent-Disposition: form-data; name=\"", 40); dataPos += 40;
+ size_t namelen = mir_strlen(iter->name), valuelen = 0;
+ if (!(iter->flags&HTTPFF_INT))
+ valuelen = mir_strlen(iter->value_str);
+ if (iter->flags&HTTPFF_FILE) {
+ const char* filename = strrchr(iter->value_str, '\\');
+ if (!filename) filename = strrchr(iter->value_str, '/');
+ if (!filename) filename = iter->value_str;
+ else ++filename;
+ valuelen = mir_strlen(filename);
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 13 + valuelen + 17);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"; filename=\"", 13); dataPos += 13;
+ memcpy(dataPos, filename, valuelen); dataPos += valuelen;
+ memcpy(dataPos, "\"\r\nContent-Type: ", 17); dataPos += 17;
+ /// add mime type
+ const char* mime = "application/octet-stream";
+ const char* fileext = strrchr(filename, '.');
+ if (fileext) {
+ if (!mir_strcmp(fileext, ".jpg") || !mir_strcmp(fileext, ".jpeg") || !mir_strcmp(fileext, ".jpe"))
+ mime = "image/jpeg";
+ else if (!mir_strcmp(fileext, ".bmp"))
+ mime = "image/bmp";
+ else if (!mir_strcmp(fileext, ".png"))
+ mime = "image/png";
+ else if (!mir_strcmp(fileext, ".gif"))
+ mime = "image/gif";
+ else if (!mir_strcmp(fileext, ".tif") || !mir_strcmp(fileext, ".tiff"))
+ mime = "image/tiff";
+ }
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, mime, mir_strlen(mime));
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, "\r\n\r\n", 4);
+ /// add file content
+ size_t filesize = 0;
+ FILE* fp = fopen(iter->value_str, "rb");
+ if (fp) {
+ fseek(fp, 0, SEEK_END);
+ filesize = ftell(fp); fseek(fp, 0, SEEK_SET);
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, filesize + 2);
+ if (fread(dataPos, 1, filesize, fp) != filesize) {
+ fclose(fp), fp = nullptr;
+ }
+ }
+ if (!fp) {
+ HTTPFormDestroy(nlhr);
+ Error(L"Error occurred when opening local file.\nAborting file upload...");
+ Exit(ACKRESULT_FAILED);
+ return 1;
+ }
+ else
+ fclose(fp);
+ dataPos += filesize;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ else if (iter->flags&HTTPFF_8BIT) {
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 38 + valuelen + 2);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", 38); dataPos += 38;
+ memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ else if (iter->flags&HTTPFF_INT) {
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + 17/*max numbers*/ + 2);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5;
+ int ret = snprintf(dataPos, 17, "%Id", iter->value_int);
+ if (ret < 17 && ret>0) dataPos += ret;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ else {
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, namelen + 5 + valuelen + 2);
+ memcpy(dataPos, iter->name, namelen); dataPos += namelen;
+ memcpy(dataPos, "\"\r\n\r\n", 5); dataPos += 5;
+ memcpy(dataPos, iter->value_str, valuelen); dataPos += valuelen;
+ memcpy(dataPos, "\r\n", 2); dataPos += 2;
+ }
+ }
+ HTTPFormAppendData(nlhr, &dataMax, &dataPos, nullptr, 2 + sizeof(boundary) + 4);
+ memset(dataPos, '-', 2); dataPos += 2;
+ memcpy(dataPos, boundary, sizeof(boundary)); dataPos += sizeof(boundary);
+ memcpy(dataPos, "--\r\n", 4); dataPos += 4;
+ nlhr->dataLength = dataPos - nlhr->pData;
+#ifdef _DEBUG /// print request content to "_sendss_tmp" file for debugging
+ {
+ FILE* fp = fopen("_sendss_tmp", "wb");
+ if (fp) {
+ fprintf(fp, "--Target-- %s\n", nlhr->szUrl);
+ for (int i = 0; i < nlhr->headersCount; ++i) {
+ fprintf(fp, "%s: %s\n", nlhr->headers[i].szName, nlhr->headers[i].szValue);
+ }
+ fprintf(fp, "\n\n");
+ fwrite(nlhr->pData, 1, nlhr->dataLength, fp);
+ fclose(fp);
+ }
+ }
+#endif // _DEBUG
+ return 0;
+}
diff --git a/plugins/SendScreenshotPlus/src/CSend.h b/plugins/SendScreenshotPlus/src/CSend.h
index 630222b69e..9301811f17 100644
--- a/plugins/SendScreenshotPlus/src/CSend.h
+++ b/plugins/SendScreenshotPlus/src/CSend.h
@@ -1,137 +1,137 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_H
-#define _CSEND_H
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-#define SS_AUTOSEND 1
-#define SS_DELETEAFTERSSEND 2
-
-#define SS_DLG_AUTOSEND 1 // Button_Enable(GetDlgItem(Owner, ID_chkEmulateClick), TRUE);
-#define SS_DLG_DELETEAFTERSSEND 2 // Button_Enable(GetDlgItem(Owner, ID_chkDeleteAfterSend), TRUE);
-#define SS_DLG_DESCRIPTION 4 // Button_Enable(GetDlgItem(Owner, ID_chkDesc), TRUE);
-
-#define GC_RESULT_SUCCESS 200
-#define GC_RESULT_WRONGVER 201
-#define GC_RESULT_ERROR 202
-#define GC_RESULT_NOSESSION 209
-
-const wchar_t SS_ERR_INIT[] = LPGENW("Unable to initiate %s.");
-const wchar_t SS_ERR_MAPI[] = LPGENW("MAPI error (%i):\n%s.");
-const wchar_t SS_ERR_RESPONSE[] = LPGENW("Unknown response from %s (%i)");
-const wchar_t SS_ERR_NORESPONSE[] = LPGENW("Got no response from %s (%i)");
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-class CSend
-{
-public:
- CSend(HWND Owner, MCONTACT hContact, bool bAsync, bool bSilent=false); // oder (TfrmMain & Owner)
- virtual ~CSend();
-
- virtual int Send() = 0; // returns 1 if sent (you must delete class) and 0 when still sending (class deletes itself)
- int SendSilent() { m_bAsync = m_bSilent = true; return Send(); }
-
- void SetFile(const wchar_t* file) { replaceStrW(m_pszFile, file); }
- void SetFile(const char* file) { mir_free(m_pszFile), m_pszFile=mir_a2u(file); }
- void SetDescription(const wchar_t* descr){ replaceStrW(m_pszFileDesc, descr); }
- void SetContact(MCONTACT hContact);
- const char* GetURL() { return m_URL; }
- const char* GetURLthumbnail() {return m_URLthumb; }
- uint8_t GetEnableItem() {return m_EnableItem;};
- wchar_t* GetErrorMsg() {return m_ErrorMsg;};
-
- bool m_bDeleteAfterSend;
-
-protected:
- bool m_bAsync;
- bool m_bSilent;
- wchar_t* m_pszFile;
- wchar_t* m_pszFileDesc;
- CMStringA m_URL;
- CMStringA m_URLthumb;
- static int OnSend(void *obj, WPARAM wParam, LPARAM lParam);
- wchar_t* m_pszSendTyp; // hold string for error mess
- char* m_pszProto; // Contact Proto Module
- MCONTACT m_hContact; // Contact handle
- uint8_t m_EnableItem; // hold flag for send type
- uint8_t m_ChatRoom; // is Contact chatroom
-
- void Error(LPCTSTR pszFormat, ...);
- void svcSendFileExit();
- void svcSendMsgExit(const char* szMessage);
- void Exit(unsigned int Result);
-
- uint32_t m_cbEventMsg; // sizeof EventMsg(T) buffer
- CMStringA m_szEventMsg; // EventMsg char*
- HANDLE m_hSend; // protocol send handle
- HANDLE m_hOnSend; // HookEventObj on ME_PROTO_ACK
-
- MSGBOX m_box;
- wchar_t* m_ErrorMsg;
- wchar_t* m_ErrorTitle;
-
- void Unhook(){if(m_hOnSend) {UnhookEvent(m_hOnSend);m_hOnSend = nullptr;}}
- void DB_EventAdd(uint16_t EventType);
-
- static INT_PTR CALLBACK ResultDialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);
-
- /// HTTP upload helper stuff
- enum HTTPFormFlags
- {
- HTTPFF_HEADER = 0x80,
- HTTPFF_TEXT = 0x00,
- HTTPFF_8BIT = 0x01,
- HTTPFF_FILE = 0x02,
- HTTPFF_INT = 0x04,
- };
-
- #define HTTPFORM_HEADER(str) str,HTTPFF_HEADER
- #define HTTPFORM_TEXT(str) str,HTTPFF_TEXT
- #define HTTPFORM_8BIT(str) str,HTTPFF_8BIT
- #define HTTPFORM_FILE(str) str,HTTPFF_FILE
- #define HTTPFORM_INT(int) (const char*)(int),HTTPFF_INT
-
- struct HTTPFormData
- {
- const char *name;
- union{
- const char* value_str;
- intptr_t value_int;
- };
- int flags;
- };
-
- static const char* GetHTMLContent(char* str, const char* startTag, const char* endTag); // changes "str", can be successfully used only once
- void HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr); // use to free data inside "nlhr" created by HTTPFormCreate
- int HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum); // returns "0" on success, Exit() will be called on failure (stop processing)
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_H
+#define _CSEND_H
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define SS_AUTOSEND 1
+#define SS_DELETEAFTERSSEND 2
+
+#define SS_DLG_AUTOSEND 1 // Button_Enable(GetDlgItem(Owner, ID_chkEmulateClick), TRUE);
+#define SS_DLG_DELETEAFTERSSEND 2 // Button_Enable(GetDlgItem(Owner, ID_chkDeleteAfterSend), TRUE);
+#define SS_DLG_DESCRIPTION 4 // Button_Enable(GetDlgItem(Owner, ID_chkDesc), TRUE);
+
+#define GC_RESULT_SUCCESS 200
+#define GC_RESULT_WRONGVER 201
+#define GC_RESULT_ERROR 202
+#define GC_RESULT_NOSESSION 209
+
+const wchar_t SS_ERR_INIT[] = LPGENW("Unable to initiate %s.");
+const wchar_t SS_ERR_MAPI[] = LPGENW("MAPI error (%i):\n%s.");
+const wchar_t SS_ERR_RESPONSE[] = LPGENW("Unknown response from %s (%i)");
+const wchar_t SS_ERR_NORESPONSE[] = LPGENW("Got no response from %s (%i)");
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CSend
+{
+public:
+ CSend(HWND Owner, MCONTACT hContact, bool bAsync, bool bSilent=false); // oder (TfrmMain & Owner)
+ virtual ~CSend();
+
+ virtual int Send() = 0; // returns 1 if sent (you must delete class) and 0 when still sending (class deletes itself)
+ int SendSilent() { m_bAsync = m_bSilent = true; return Send(); }
+
+ void SetFile(const wchar_t* file) { replaceStrW(m_pszFile, file); }
+ void SetFile(const char* file) { mir_free(m_pszFile), m_pszFile=mir_a2u(file); }
+ void SetDescription(const wchar_t* descr){ replaceStrW(m_pszFileDesc, descr); }
+ void SetContact(MCONTACT hContact);
+ const char* GetURL() { return m_URL; }
+ const char* GetURLthumbnail() {return m_URLthumb; }
+ uint8_t GetEnableItem() {return m_EnableItem;};
+ wchar_t* GetErrorMsg() {return m_ErrorMsg;};
+
+ bool m_bDeleteAfterSend;
+
+protected:
+ bool m_bAsync;
+ bool m_bSilent;
+ wchar_t* m_pszFile;
+ wchar_t* m_pszFileDesc;
+ CMStringA m_URL;
+ CMStringA m_URLthumb;
+ static int OnSend(void *obj, WPARAM wParam, LPARAM lParam);
+ wchar_t* m_pszSendTyp; // hold string for error mess
+ char* m_pszProto; // Contact Proto Module
+ MCONTACT m_hContact; // Contact handle
+ uint8_t m_EnableItem; // hold flag for send type
+ uint8_t m_ChatRoom; // is Contact chatroom
+
+ void Error(LPCTSTR pszFormat, ...);
+ void svcSendFileExit();
+ void svcSendMsgExit(const char* szMessage);
+ void Exit(unsigned int Result);
+
+ uint32_t m_cbEventMsg; // sizeof EventMsg(T) buffer
+ CMStringA m_szEventMsg; // EventMsg char*
+ HANDLE m_hSend; // protocol send handle
+ HANDLE m_hOnSend; // HookEventObj on ME_PROTO_ACK
+
+ MSGBOX m_box;
+ wchar_t* m_ErrorMsg;
+ wchar_t* m_ErrorTitle;
+
+ void Unhook(){if(m_hOnSend) {UnhookEvent(m_hOnSend);m_hOnSend = nullptr;}}
+ void DB_EventAdd(uint16_t EventType);
+
+ static INT_PTR CALLBACK ResultDialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);
+
+ /// HTTP upload helper stuff
+ enum HTTPFormFlags
+ {
+ HTTPFF_HEADER = 0x80,
+ HTTPFF_TEXT = 0x00,
+ HTTPFF_8BIT = 0x01,
+ HTTPFF_FILE = 0x02,
+ HTTPFF_INT = 0x04,
+ };
+
+ #define HTTPFORM_HEADER(str) str,HTTPFF_HEADER
+ #define HTTPFORM_TEXT(str) str,HTTPFF_TEXT
+ #define HTTPFORM_8BIT(str) str,HTTPFF_8BIT
+ #define HTTPFORM_FILE(str) str,HTTPFF_FILE
+ #define HTTPFORM_INT(int) (const char*)(int),HTTPFF_INT
+
+ struct HTTPFormData
+ {
+ const char *name;
+ union{
+ const char* value_str;
+ intptr_t value_int;
+ };
+ int flags;
+ };
+
+ static const char* GetHTMLContent(char* str, const char* startTag, const char* endTag); // changes "str", can be successfully used only once
+ void HTTPFormDestroy(NETLIBHTTPREQUEST* nlhr); // use to free data inside "nlhr" created by HTTPFormCreate
+ int HTTPFormCreate(NETLIBHTTPREQUEST* nlhr, int requestType, const char* url, HTTPFormData* frm, size_t frmNum); // returns "0" on success, Exit() will be called on failure (stop processing)
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendCloduFile.h b/plugins/SendScreenshotPlus/src/CSendCloduFile.h
index 663bbf526c..f5f0c221db 100644
--- a/plugins/SendScreenshotPlus/src/CSendCloduFile.h
+++ b/plugins/SendScreenshotPlus/src/CSendCloduFile.h
@@ -1,50 +1,50 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_CLOUD_FILE_H
-#define _CSEND_CLOUD_FILE_H
-
-#include "Utils.h"
-
-class CSendCloudFile : public CSend
-{
-
-public:
- CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service);
- ~CSendCloudFile();
-
- int Send() override;
-
-protected:
- const char *m_service;
-
- void SendThread();
- static void SendThreadWrapper(void *Obj);
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_CLOUD_FILE_H
+#define _CSEND_CLOUD_FILE_H
+
+#include "Utils.h"
+
+class CSendCloudFile : public CSend
+{
+
+public:
+ CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service);
+ ~CSendCloudFile();
+
+ int Send() override;
+
+protected:
+ const char *m_service;
+
+ void SendThread();
+ static void SendThreadWrapper(void *Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp b/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp
index ddcdb3b0a5..88f5da4dac 100644
--- a/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendCloudFile.cpp
@@ -1,78 +1,78 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSendCloudFile::CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service)
- : CSend(Owner, hContact, bAsync), m_service(service)
-{
- // @todo : re-enable SS_DLG_DELETEAFTERSSEND with full implemention of Dropbox upload with progress, msg and sounds
- m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
- m_pszSendTyp = TranslateT("CloudFile transfer");
-}
-
-CSendCloudFile::~CSendCloudFile()
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendCloudFile::Send()
-{
- mir_forkthread(&CSendCloudFile::SendThreadWrapper, this);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CSendCloudFile::SendThread()
-{
- // @todo : SS_DLG_DESCRIPTION and SS_DLG_DELETEAFTERSSEND are of no use as of now since we don't track upload progress
-
- CFUPLOADDATA ud = { m_service, m_pszFile, L"SendSS" };
- CFUPLOADRESULT ur = { };
-
- if (CallService(MS_CLOUDFILE_UPLOAD, (WPARAM)&ud, (LPARAM)&ur)) {
- Error(LPGENW("%s (%i):\nCould not add a share to the CloudFile plugin."), TranslateW(m_pszSendTyp), 0);
- Exit(ACKRESULT_FAILED);
- return;
- }
-
- m_URL = ur.link;
- if (m_URL)
- svcSendMsgExit(m_URL);
- else
- Exit(ACKRESULT_FAILED);
-}
-
-void CSendCloudFile::SendThreadWrapper(void * Obj)
-{
- reinterpret_cast<CSendCloudFile*>(Obj)->SendThread();
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendCloudFile::CSendCloudFile(HWND Owner, MCONTACT hContact, bool bAsync, const char *service)
+ : CSend(Owner, hContact, bAsync), m_service(service)
+{
+ // @todo : re-enable SS_DLG_DELETEAFTERSSEND with full implemention of Dropbox upload with progress, msg and sounds
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = TranslateT("CloudFile transfer");
+}
+
+CSendCloudFile::~CSendCloudFile()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendCloudFile::Send()
+{
+ mir_forkthread(&CSendCloudFile::SendThreadWrapper, this);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSendCloudFile::SendThread()
+{
+ // @todo : SS_DLG_DESCRIPTION and SS_DLG_DELETEAFTERSSEND are of no use as of now since we don't track upload progress
+
+ CFUPLOADDATA ud = { m_service, m_pszFile, L"SendSS" };
+ CFUPLOADRESULT ur = { };
+
+ if (CallService(MS_CLOUDFILE_UPLOAD, (WPARAM)&ud, (LPARAM)&ur)) {
+ Error(LPGENW("%s (%i):\nCould not add a share to the CloudFile plugin."), TranslateW(m_pszSendTyp), 0);
+ Exit(ACKRESULT_FAILED);
+ return;
+ }
+
+ m_URL = ur.link;
+ if (m_URL)
+ svcSendMsgExit(m_URL);
+ else
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendCloudFile::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendCloudFile*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendEmail.cpp b/plugins/SendScreenshotPlus/src/CSendEmail.cpp
index 8490004f21..2af6b0b174 100644
--- a/plugins/SendScreenshotPlus/src/CSendEmail.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendEmail.cpp
@@ -1,201 +1,201 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSendEmail::CSendEmail(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
- : CSend(Owner, hContact, true)
-{
- m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_DELETEAFTERSSEND; // SS_DLG_AUTOSEND | ;
- m_pszSendTyp = LPGENW("Email transfer");
- m_pszFileA = nullptr;
- m_pszFileName = nullptr;
- m_Email = nullptr;
- m_FriendlyName = nullptr;
- m_Subject = nullptr;
-}
-
-CSendEmail::~CSendEmail()
-{
- mir_free(m_pszFileA);
- mir_free(m_pszFileName);
- mir_free(m_Email);
- mir_free(m_FriendlyName);
- mir_free(m_Subject);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendEmail::Send()
-{
- if (!m_hContact) return 1;
- mir_free(m_pszFileName);
- m_pszFileName = GetFileNameA(m_pszFile);
-
- mir_free(m_pszFileA);
- m_pszFileA = mir_u2a(m_pszFile);
-
- m_Email = mir_u2a(ptrW(Contact::GetInfo(CNF_EMAIL, m_hContact, m_pszProto)));
- m_FriendlyName = mir_u2a(ptrW(Contact::GetInfo(CNF_DISPLAY, m_hContact, m_pszProto)));
- m_Subject = mir_u2a(m_pszFileDesc);
-
- // SendByEmail(m_pszFileA, "", m_FriendlyName, m_Email, m_Subject);
-
- // start Send thread
- mir_forkthread(&CSendEmail::SendThreadWrapper, this);
- return 0;
-}
-
-void CSendEmail::SendThread()
-{
- // This code based on SentTo.exe application.
- // The default mail client for Simple MAPI or MAPI calls is defined by the
- // HKLM\Software\Clients\Mail::(default) registry value.
-
- MapiFileDesc arrfileDesc[1];
-
- typedef ULONG(FAR PASCAL *MAPIFUNC)(LHANDLE, ULONG, lpMapiMessage, FLAGS, ULONG);
- MapiMessage Msg;
- MAPIFUNC lpMAPISendMail;
-
- HINSTANCE hMAPILib = ::LoadLibrary(L"MAPI32.DLL");
- if (hMAPILib == nullptr) {
- // return -1;
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED); return;
- }
-
- lpMAPISendMail = (MAPIFUNC)GetProcAddress(hMAPILib, "MAPISendMail");
- if (lpMAPISendMail == nullptr) {
- ::FreeLibrary(hMAPILib);
- // return -2;
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED); return;
- }
-
- memset(&Msg, 0, sizeof(Msg));
-
- arrfileDesc[0].ulReserved = 0;
- arrfileDesc[0].flFlags = 0;
- arrfileDesc[0].lpFileType = nullptr;
- arrfileDesc[0].nPosition = -1;
- arrfileDesc[0].lpszPathName = m_pszFileA;
- arrfileDesc[0].lpszFileName = nullptr;
-
- Msg.nFileCount = 1;
- Msg.lpFiles = arrfileDesc;
- Msg.lpszNoteText = ""; // body
- Msg.lpszSubject = m_Subject; // subject
-
- Msg.nRecipCount = 1;
- MapiRecipDesc recip;
- recip.ulReserved = 0;
- recip.ulRecipClass = MAPI_TO;
-
- if (m_FriendlyName && m_FriendlyName[0] != NULL) {
- recip.lpszName = m_FriendlyName; // friendly name set to contact's name
- }
- else {
- recip.lpszName = m_Email; // friendly name set to contact's email
- }
-
- recip.lpszAddress = m_Email; // email
- recip.ulEIDSize = 0;
- recip.lpEntryID = nullptr;
- Msg.lpRecips = &recip;
-
- try {
- int res = lpMAPISendMail(NULL, NULL, &Msg, MAPI_LOGON_UI | MAPI_DIALOG, 0);
- ::FreeLibrary(hMAPILib);
-
- wchar_t* err;
- switch (res) {
- case SUCCESS_SUCCESS:
- // The call succeeded and the message was sent.
- Exit(ACKRESULT_SUCCESS); return;
- // No message was sent
- case MAPI_E_AMBIGUOUS_RECIPIENT:
- err = LPGENW("A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set");
- break;
- case MAPI_E_ATTACHMENT_NOT_FOUND:
- err = LPGENW("The specified attachment was not found");
- break;
- case MAPI_E_ATTACHMENT_OPEN_FAILURE:
- err = LPGENW("The specified attachment could not be opened");
- break;
- case MAPI_E_BAD_RECIPTYPE:
- err = LPGENW("The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC");
- break;
- case MAPI_E_FAILURE:
- err = LPGENW("One or more unspecified errors occurred");
- break;
- case MAPI_E_INSUFFICIENT_MEMORY:
- err = LPGENW("There was insufficient memory to proceed");
- break;
- case MAPI_E_INVALID_RECIPS:
- err = LPGENW("One or more recipients were invalid or did not resolve to any address");
- break;
- case MAPI_E_LOGIN_FAILURE:
- err = LPGENW("There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed");
- break;
- case MAPI_E_TEXT_TOO_LARGE:
- err = LPGENW("The text in the message was too large");
- break;
- case MAPI_E_TOO_MANY_FILES:
- err = LPGENW("There were too many file attachments");
- break;
- case MAPI_E_TOO_MANY_RECIPIENTS:
- err = LPGENW("There were too many recipients");
- break;
- case MAPI_E_UNKNOWN_RECIPIENT:
- err = LPGENW("A recipient did not appear in the address list");
- break;
- case MAPI_E_USER_ABORT:
- err = LPGENW("The user canceled one of the dialog boxes");
- break;
- default:
- err = LPGENW("Unknown Error");
- break;
- }
- Error(SS_ERR_MAPI, res, err);
- Exit(ACKRESULT_FAILED);
- }
- catch (...) {
- ::FreeLibrary(hMAPILib);
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED);
- return;
- }
-}
-
-void CSendEmail::SendThreadWrapper(void * Obj)
-{
- reinterpret_cast<CSendEmail*>(Obj)->SendThread();
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendEmail::CSendEmail(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_DELETEAFTERSSEND; // SS_DLG_AUTOSEND | ;
+ m_pszSendTyp = LPGENW("Email transfer");
+ m_pszFileA = nullptr;
+ m_pszFileName = nullptr;
+ m_Email = nullptr;
+ m_FriendlyName = nullptr;
+ m_Subject = nullptr;
+}
+
+CSendEmail::~CSendEmail()
+{
+ mir_free(m_pszFileA);
+ mir_free(m_pszFileName);
+ mir_free(m_Email);
+ mir_free(m_FriendlyName);
+ mir_free(m_Subject);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendEmail::Send()
+{
+ if (!m_hContact) return 1;
+ mir_free(m_pszFileName);
+ m_pszFileName = GetFileNameA(m_pszFile);
+
+ mir_free(m_pszFileA);
+ m_pszFileA = mir_u2a(m_pszFile);
+
+ m_Email = mir_u2a(ptrW(Contact::GetInfo(CNF_EMAIL, m_hContact, m_pszProto)));
+ m_FriendlyName = mir_u2a(ptrW(Contact::GetInfo(CNF_DISPLAY, m_hContact, m_pszProto)));
+ m_Subject = mir_u2a(m_pszFileDesc);
+
+ // SendByEmail(m_pszFileA, "", m_FriendlyName, m_Email, m_Subject);
+
+ // start Send thread
+ mir_forkthread(&CSendEmail::SendThreadWrapper, this);
+ return 0;
+}
+
+void CSendEmail::SendThread()
+{
+ // This code based on SentTo.exe application.
+ // The default mail client for Simple MAPI or MAPI calls is defined by the
+ // HKLM\Software\Clients\Mail::(default) registry value.
+
+ MapiFileDesc arrfileDesc[1];
+
+ typedef ULONG(FAR PASCAL *MAPIFUNC)(LHANDLE, ULONG, lpMapiMessage, FLAGS, ULONG);
+ MapiMessage Msg;
+ MAPIFUNC lpMAPISendMail;
+
+ HINSTANCE hMAPILib = ::LoadLibrary(L"MAPI32.DLL");
+ if (hMAPILib == nullptr) {
+ // return -1;
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+
+ lpMAPISendMail = (MAPIFUNC)GetProcAddress(hMAPILib, "MAPISendMail");
+ if (lpMAPISendMail == nullptr) {
+ ::FreeLibrary(hMAPILib);
+ // return -2;
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED); return;
+ }
+
+ memset(&Msg, 0, sizeof(Msg));
+
+ arrfileDesc[0].ulReserved = 0;
+ arrfileDesc[0].flFlags = 0;
+ arrfileDesc[0].lpFileType = nullptr;
+ arrfileDesc[0].nPosition = -1;
+ arrfileDesc[0].lpszPathName = m_pszFileA;
+ arrfileDesc[0].lpszFileName = nullptr;
+
+ Msg.nFileCount = 1;
+ Msg.lpFiles = arrfileDesc;
+ Msg.lpszNoteText = ""; // body
+ Msg.lpszSubject = m_Subject; // subject
+
+ Msg.nRecipCount = 1;
+ MapiRecipDesc recip;
+ recip.ulReserved = 0;
+ recip.ulRecipClass = MAPI_TO;
+
+ if (m_FriendlyName && m_FriendlyName[0] != NULL) {
+ recip.lpszName = m_FriendlyName; // friendly name set to contact's name
+ }
+ else {
+ recip.lpszName = m_Email; // friendly name set to contact's email
+ }
+
+ recip.lpszAddress = m_Email; // email
+ recip.ulEIDSize = 0;
+ recip.lpEntryID = nullptr;
+ Msg.lpRecips = &recip;
+
+ try {
+ int res = lpMAPISendMail(NULL, NULL, &Msg, MAPI_LOGON_UI | MAPI_DIALOG, 0);
+ ::FreeLibrary(hMAPILib);
+
+ wchar_t* err;
+ switch (res) {
+ case SUCCESS_SUCCESS:
+ // The call succeeded and the message was sent.
+ Exit(ACKRESULT_SUCCESS); return;
+ // No message was sent
+ case MAPI_E_AMBIGUOUS_RECIPIENT:
+ err = LPGENW("A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set");
+ break;
+ case MAPI_E_ATTACHMENT_NOT_FOUND:
+ err = LPGENW("The specified attachment was not found");
+ break;
+ case MAPI_E_ATTACHMENT_OPEN_FAILURE:
+ err = LPGENW("The specified attachment could not be opened");
+ break;
+ case MAPI_E_BAD_RECIPTYPE:
+ err = LPGENW("The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC");
+ break;
+ case MAPI_E_FAILURE:
+ err = LPGENW("One or more unspecified errors occurred");
+ break;
+ case MAPI_E_INSUFFICIENT_MEMORY:
+ err = LPGENW("There was insufficient memory to proceed");
+ break;
+ case MAPI_E_INVALID_RECIPS:
+ err = LPGENW("One or more recipients were invalid or did not resolve to any address");
+ break;
+ case MAPI_E_LOGIN_FAILURE:
+ err = LPGENW("There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed");
+ break;
+ case MAPI_E_TEXT_TOO_LARGE:
+ err = LPGENW("The text in the message was too large");
+ break;
+ case MAPI_E_TOO_MANY_FILES:
+ err = LPGENW("There were too many file attachments");
+ break;
+ case MAPI_E_TOO_MANY_RECIPIENTS:
+ err = LPGENW("There were too many recipients");
+ break;
+ case MAPI_E_UNKNOWN_RECIPIENT:
+ err = LPGENW("A recipient did not appear in the address list");
+ break;
+ case MAPI_E_USER_ABORT:
+ err = LPGENW("The user canceled one of the dialog boxes");
+ break;
+ default:
+ err = LPGENW("Unknown Error");
+ break;
+ }
+ Error(SS_ERR_MAPI, res, err);
+ Exit(ACKRESULT_FAILED);
+ }
+ catch (...) {
+ ::FreeLibrary(hMAPILib);
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return;
+ }
+}
+
+void CSendEmail::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendEmail*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendEmail.h b/plugins/SendScreenshotPlus/src/CSendEmail.h
index 7e765f747f..1bc852975e 100644
--- a/plugins/SendScreenshotPlus/src/CSendEmail.h
+++ b/plugins/SendScreenshotPlus/src/CSendEmail.h
@@ -1,53 +1,53 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_EMAIL_H
-#define _CSEND_EMAIL_H
-
-class CSendEmail : public CSend
-{
-
-public:
- // Deklaration Standardkonstruktor/Standarddestructor
- CSendEmail(HWND Owner, MCONTACT hContact, bool bAsync);
- ~CSendEmail();
-
- int Send() override;
-
-protected:
- char* m_pszFileA;
- char* m_pszFileName;
- char* m_Email;
- char* m_FriendlyName;
- char* m_Subject;
-
- void SendThread();
- static void SendThreadWrapper(void * Obj);
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_EMAIL_H
+#define _CSEND_EMAIL_H
+
+class CSendEmail : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendEmail(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendEmail();
+
+ int Send() override;
+
+protected:
+ char* m_pszFileA;
+ char* m_pszFileName;
+ char* m_Email;
+ char* m_FriendlyName;
+ char* m_Subject;
+
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp b/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp
index e4f5cc11d1..1e4ae57e2c 100644
--- a/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendFTPFile.cpp
@@ -1,93 +1,93 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSendFTPFile::CSendFTPFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
- : CSend(Owner, hContact, true)
-{
- m_EnableItem = 0; //SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
- m_pszSendTyp = LPGENW("FTPFile transfer");
- m_pszFileName = nullptr;
-}
-
-CSendFTPFile::~CSendFTPFile()
-{
- mir_free(m_pszFileName);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendFTPFile::Send()
-{
- if (!m_hContact) return 1;
- /*********************************************************************************************
- * Send file (files) to the FTP server and copy file URL
- * to message log or clipboard (according to plugin setting)
- * wParam = (HANDLE)hContact
- * lParam = (char *)filename
- * Filename format is same as GetOpenFileName (OPENFILENAME.lpstrFile) returns,
- * see http://msdn2.microsoft.com/en-us/library/ms646839.aspx
- * Returns 0 on success or nonzero on failure
- * if (!wParam || !lParam) return 1
- ********************************************************************************************/
- mir_free(m_pszFileName);
- m_pszFileName = GetFileNameA(m_pszFile);
- size_t size = sizeof(char)*(mir_strlen(m_pszFileName) + 2);
- m_pszFileName = (char*)mir_realloc(m_pszFileName, size);
- m_pszFileName[size - 1] = NULL;
-
- // start Send thread
- mir_forkthread(&CSendFTPFile::SendThreadWrapper, this);
- return 0;
-}
-
-void CSendFTPFile::SendThread()
-{
-
- INT_PTR ret = FTPFileUploadA(m_hContact, FNUM_DEFAULT, FMODE_RAWFILE, &m_pszFileName, 1);
- if (ret != 0) {
- Error(LPGENW("%s (%i):\nCould not add a share to the FTP File plugin."), TranslateW(m_pszSendTyp), ret);
- Exit(ret); return;
- }
-
- // Can't delete the file since FTP File plugin will use it
- m_bDeleteAfterSend = false;
-
- if (m_URL && *m_URL) {/// @fixme : m_URL never set
- svcSendMsgExit(m_URL); return;
- }
- Exit(ACKRESULT_FAILED);
-}
-
-void CSendFTPFile::SendThreadWrapper(void * Obj)
-{
- reinterpret_cast<CSendFTPFile*>(Obj)->SendThread();
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendFTPFile::CSendFTPFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = 0; //SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("FTPFile transfer");
+ m_pszFileName = nullptr;
+}
+
+CSendFTPFile::~CSendFTPFile()
+{
+ mir_free(m_pszFileName);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendFTPFile::Send()
+{
+ if (!m_hContact) return 1;
+ /*********************************************************************************************
+ * Send file (files) to the FTP server and copy file URL
+ * to message log or clipboard (according to plugin setting)
+ * wParam = (HANDLE)hContact
+ * lParam = (char *)filename
+ * Filename format is same as GetOpenFileName (OPENFILENAME.lpstrFile) returns,
+ * see http://msdn2.microsoft.com/en-us/library/ms646839.aspx
+ * Returns 0 on success or nonzero on failure
+ * if (!wParam || !lParam) return 1
+ ********************************************************************************************/
+ mir_free(m_pszFileName);
+ m_pszFileName = GetFileNameA(m_pszFile);
+ size_t size = sizeof(char)*(mir_strlen(m_pszFileName) + 2);
+ m_pszFileName = (char*)mir_realloc(m_pszFileName, size);
+ m_pszFileName[size - 1] = NULL;
+
+ // start Send thread
+ mir_forkthread(&CSendFTPFile::SendThreadWrapper, this);
+ return 0;
+}
+
+void CSendFTPFile::SendThread()
+{
+
+ INT_PTR ret = FTPFileUploadA(m_hContact, FNUM_DEFAULT, FMODE_RAWFILE, &m_pszFileName, 1);
+ if (ret != 0) {
+ Error(LPGENW("%s (%i):\nCould not add a share to the FTP File plugin."), TranslateW(m_pszSendTyp), ret);
+ Exit(ret); return;
+ }
+
+ // Can't delete the file since FTP File plugin will use it
+ m_bDeleteAfterSend = false;
+
+ if (m_URL && *m_URL) {/// @fixme : m_URL never set
+ svcSendMsgExit(m_URL); return;
+ }
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendFTPFile::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendFTPFile*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendFTPFile.h b/plugins/SendScreenshotPlus/src/CSendFTPFile.h
index 9094f3f4f7..b57f9243b2 100644
--- a/plugins/SendScreenshotPlus/src/CSendFTPFile.h
+++ b/plugins/SendScreenshotPlus/src/CSendFTPFile.h
@@ -1,48 +1,48 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_FTP_FILE_H
-#define _CSEND_FTP_FILE_H
-
-class CSendFTPFile : public CSend
-{
-
-public:
- // Deklaration Standardkonstruktor/Standarddestructor
- CSendFTPFile(HWND Owner, MCONTACT hContact, bool bAsync);
- ~CSendFTPFile();
-
- int Send() override;
-
-protected:
- char* m_pszFileName;
- void SendThread();
- static void SendThreadWrapper(void * Obj);
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_FTP_FILE_H
+#define _CSEND_FTP_FILE_H
+
+class CSendFTPFile : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendFTPFile(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendFTPFile();
+
+ int Send() override;
+
+protected:
+ char* m_pszFileName;
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendFile.cpp b/plugins/SendScreenshotPlus/src/CSendFile.cpp
index 550ad4bda4..ed1500a03f 100644
--- a/plugins/SendScreenshotPlus/src/CSendFile.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendFile.cpp
@@ -1,50 +1,50 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSendFile::CSendFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
- : CSend(Owner, hContact, true)
-{
- m_EnableItem = SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND | SS_DLG_DESCRIPTION;
- m_pszSendTyp = LPGENW("File transfer");
-}
-
-CSendFile::~CSendFile()
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendFile::Send()
-{
- svcSendFileExit();
- 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-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendFile::CSendFile(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND | SS_DLG_DESCRIPTION;
+ m_pszSendTyp = LPGENW("File transfer");
+}
+
+CSendFile::~CSendFile()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendFile::Send()
+{
+ svcSendFileExit();
+ return 0;
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendFile.h b/plugins/SendScreenshotPlus/src/CSendFile.h
index b30a1ce6e3..e2b7462725 100644
--- a/plugins/SendScreenshotPlus/src/CSendFile.h
+++ b/plugins/SendScreenshotPlus/src/CSendFile.h
@@ -1,43 +1,43 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_FILE_H
-#define _CSEND_FILE_H
-
-class CSendFile : public CSend
-{
-
-public:
- // Deklaration Standardkonstruktor/Standarddestructor
- CSendFile(HWND Owner, MCONTACT hContact, bool bAsync);
- ~CSendFile();
-
- int Send() override;
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_FILE_H
+#define _CSEND_FILE_H
+
+class CSendFile : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendFile(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendFile();
+
+ int Send() override;
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp b/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp
index 9075d70951..7815d49bfb 100644
--- a/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendHTTPServer.cpp
@@ -1,117 +1,117 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with 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_PTR(*g_MirCallService)(const char *, WPARAM, LPARAM) = nullptr;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSendHTTPServer::CSendHTTPServer(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
- : CSend(Owner, hContact, true)
-{
- m_EnableItem = SS_DLG_DESCRIPTION; //| SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
- m_pszSendTyp = LPGENW("HTTPServer transfer");
- m_pszFileName = nullptr;
- m_fsi_pszRealPath = nullptr;
-}
-
-CSendHTTPServer::~CSendHTTPServer()
-{
- mir_free(m_pszFileName);
- mir_free(m_fsi_pszRealPath);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendHTTPServer::Send()
-{
- if (!m_hContact) return 1;
- if (CallService(MS_HTTP_ACCEPT_CONNECTIONS, TRUE, 0) != 0) {
- Error(LPGENW("Could not start the HTTP Server plugin."));
- Exit(ACKRESULT_FAILED);
- return !m_bAsync;
- }
-
- if (!m_pszFileName) {
- m_pszFileName = GetFileNameA(m_pszFile);
- }
-
- m_fsi_pszSrvPath.Empty();
- m_fsi_pszSrvPath.AppendChar('/');
- m_fsi_pszSrvPath.Append(m_pszFileName);
-
- replaceStr(m_fsi_pszRealPath, _T2A(m_pszFile));
-
- memset(&m_fsi, 0, sizeof(m_fsi));
- m_fsi.lStructSize = sizeof(STFileShareInfo);
- m_fsi.nMaxDownloads = -1; // -1 = infinite
- m_fsi.pszRealPath = m_fsi_pszRealPath;
-
- // start Send thread
- mir_forkthread(&CSendHTTPServer::SendThreadWrapper, this);
- return 0;
-}
-
-void CSendHTTPServer::SendThread()
-{
- INT_PTR ret;
-
- if (ServiceExists(MS_HTTP_GET_LINK)) {
- // patched plugin version
- ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi);
- if (!ret) {
- m_URL = ptrA((char*)CallService(MS_HTTP_GET_LINK, (WPARAM)m_fsi.pszSrvPath, 0));
- }
- }
- else {
- // original plugin
- m_fsi.dwOptions = OPT_SEND_LINK;
-
- // send DATA and wait for reply
- ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi);
- }
-
- if (ret != 0) {
- Error(LPGENW("%s (%i):\nCould not add a share to the HTTP Server plugin."), TranslateW(m_pszSendTyp), ret);
- Exit(ret); return;
- }
-
- // Share the file by HTTP Server plugin, SendSS does not own the file anymore = auto-delete won't work
- m_bDeleteAfterSend = false;
-
- if (m_URL && *m_URL) {
- svcSendMsgExit(m_URL); return;
- }
- Exit(ACKRESULT_FAILED);
-}
-
-void CSendHTTPServer::SendThreadWrapper(void * Obj)
-{
- reinterpret_cast<CSendHTTPServer*>(Obj)->SendThread();
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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_PTR(*g_MirCallService)(const char *, WPARAM, LPARAM) = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendHTTPServer::CSendHTTPServer(HWND Owner, MCONTACT hContact, bool /*bAsync*/)
+ : CSend(Owner, hContact, true)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION; //| SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("HTTPServer transfer");
+ m_pszFileName = nullptr;
+ m_fsi_pszRealPath = nullptr;
+}
+
+CSendHTTPServer::~CSendHTTPServer()
+{
+ mir_free(m_pszFileName);
+ mir_free(m_fsi_pszRealPath);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendHTTPServer::Send()
+{
+ if (!m_hContact) return 1;
+ if (CallService(MS_HTTP_ACCEPT_CONNECTIONS, TRUE, 0) != 0) {
+ Error(LPGENW("Could not start the HTTP Server plugin."));
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+
+ if (!m_pszFileName) {
+ m_pszFileName = GetFileNameA(m_pszFile);
+ }
+
+ m_fsi_pszSrvPath.Empty();
+ m_fsi_pszSrvPath.AppendChar('/');
+ m_fsi_pszSrvPath.Append(m_pszFileName);
+
+ replaceStr(m_fsi_pszRealPath, _T2A(m_pszFile));
+
+ memset(&m_fsi, 0, sizeof(m_fsi));
+ m_fsi.lStructSize = sizeof(STFileShareInfo);
+ m_fsi.nMaxDownloads = -1; // -1 = infinite
+ m_fsi.pszRealPath = m_fsi_pszRealPath;
+
+ // start Send thread
+ mir_forkthread(&CSendHTTPServer::SendThreadWrapper, this);
+ return 0;
+}
+
+void CSendHTTPServer::SendThread()
+{
+ INT_PTR ret;
+
+ if (ServiceExists(MS_HTTP_GET_LINK)) {
+ // patched plugin version
+ ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi);
+ if (!ret) {
+ m_URL = ptrA((char*)CallService(MS_HTTP_GET_LINK, (WPARAM)m_fsi.pszSrvPath, 0));
+ }
+ }
+ else {
+ // original plugin
+ m_fsi.dwOptions = OPT_SEND_LINK;
+
+ // send DATA and wait for reply
+ ret = CallService(MS_HTTP_ADD_CHANGE_REMOVE, (WPARAM)m_hContact, (LPARAM)&m_fsi);
+ }
+
+ if (ret != 0) {
+ Error(LPGENW("%s (%i):\nCould not add a share to the HTTP Server plugin."), TranslateW(m_pszSendTyp), ret);
+ Exit(ret); return;
+ }
+
+ // Share the file by HTTP Server plugin, SendSS does not own the file anymore = auto-delete won't work
+ m_bDeleteAfterSend = false;
+
+ if (m_URL && *m_URL) {
+ svcSendMsgExit(m_URL); return;
+ }
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendHTTPServer::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendHTTPServer*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHTTPServer.h b/plugins/SendScreenshotPlus/src/CSendHTTPServer.h
index e11ac3fae0..aa5dec4030 100644
--- a/plugins/SendScreenshotPlus/src/CSendHTTPServer.h
+++ b/plugins/SendScreenshotPlus/src/CSendHTTPServer.h
@@ -1,55 +1,55 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_HTTP_SERVER_H
-#define _CSEND_HTTP_SERVER_H
-
-class CSendHTTPServer : public CSend
-{
-public:
- // Deklaration Standardkonstruktor/Standarddestructor
- CSendHTTPServer(HWND Owner, MCONTACT hContact, bool bAsync);
- ~CSendHTTPServer();
-
- int Send() override;
-
-protected:
- char* m_pszFileName;
- CMStringA m_fsi_pszSrvPath;
- char* m_fsi_pszRealPath;
-
- STFileShareInfo m_fsi;
-
- void SendThread();
- static void SendThreadWrapper(void * Obj);
-
- typedef std::map<HANDLE, CSendHTTPServer *> CContactMapping;
- static CContactMapping _CContactMapping;
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_HTTP_SERVER_H
+#define _CSEND_HTTP_SERVER_H
+
+class CSendHTTPServer : public CSend
+{
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendHTTPServer(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendHTTPServer();
+
+ int Send() override;
+
+protected:
+ char* m_pszFileName;
+ CMStringA m_fsi_pszSrvPath;
+ char* m_fsi_pszRealPath;
+
+ STFileShareInfo m_fsi;
+
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+
+ typedef std::map<HANDLE, CSendHTTPServer *> CContactMapping;
+ static CContactMapping _CContactMapping;
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp
index 7940487e88..5a2a4d8a34 100644
--- a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.cpp
@@ -1,118 +1,118 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CSendHost_ImageShack::CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync)
- : CSend(Owner, hContact, bAsync)
-{
- m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
- m_pszSendTyp = LPGENW("Image upload");
-}
-
-CSendHost_ImageShack::~CSendHost_ImageShack()
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendHost_ImageShack::Send()
-{
- if (!g_hNetlibUser) { // check Netlib
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED);
- return !m_bAsync;
- }
- memset(&m_nlhr, 0, sizeof(m_nlhr));
- char* tmp; tmp = mir_u2a(m_pszFile);
- HTTPFormData frm[] = {
- // { "Referer", HTTPFORM_HEADER("http://www.imageshack.us/upload_api.php") },
- { "fileupload", HTTPFORM_FILE(tmp) },
- // { "rembar", "yes" },// no info bar on thumb
- { "public", "no" },
- { "key", HTTPFORM_8BIT(DEVKEY_IMAGESHACK) },
- };
- int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "http://imageshack.us/upload_api.php", frm, sizeof(frm) / sizeof(HTTPFormData));
- mir_free(tmp);
- if (error)
- return !m_bAsync;
- // start upload thread
- if (m_bAsync) {
- mir_forkthread(&CSendHost_ImageShack::SendThreadWrapper, this);
- return 0;
- }
- SendThread();
- return 1;
-}
-
-void CSendHost_ImageShack::SendThread()
-{
- // send DATA and wait for m_nlreply
- NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &m_nlhr));
- HTTPFormDestroy(&m_nlhr);
- if (reply) {
- if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) {
- reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated
- const char* url = nullptr;
- url = GetHTMLContent(reply->pData, "<image_link>", "</image_link>");
- if (url && *url) {
- m_URLthumb = m_URL = url;
-
- int idx = m_URLthumb.ReverseFind('.');
- if (idx != -1 && m_URLthumb.GetLength() - idx > 2)
- m_URLthumb.Insert(idx + 1, "th");
- else
- m_URLthumb.Empty();
-
- svcSendMsgExit(url);
- return;
- }
-
- url = GetHTMLContent(reply->pData, "<error ", "</error>");
- wchar_t* err = nullptr;
- if (url) err = mir_a2u(url);
- if (!err || !*err) { // fallback to server response mess
- mir_free(err);
- err = mir_a2u(reply->pData);
- }
- Error(L"%s", err);
- mir_free(err);
- }
- else Error(SS_ERR_RESPONSE, m_pszSendTyp, reply->resultCode);
- }
- else Error(SS_ERR_NORESPONSE, m_pszSendTyp, m_nlhr.resultCode);
-
- Exit(ACKRESULT_FAILED);
-}
-
-void CSendHost_ImageShack::SendThreadWrapper(void * Obj)
-{
- reinterpret_cast<CSendHost_ImageShack*>(Obj)->SendThread();
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CSendHost_ImageShack::CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync)
+ : CSend(Owner, hContact, bAsync)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("Image upload");
+}
+
+CSendHost_ImageShack::~CSendHost_ImageShack()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendHost_ImageShack::Send()
+{
+ if (!g_hNetlibUser) { // check Netlib
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+ memset(&m_nlhr, 0, sizeof(m_nlhr));
+ char* tmp; tmp = mir_u2a(m_pszFile);
+ HTTPFormData frm[] = {
+ // { "Referer", HTTPFORM_HEADER("http://www.imageshack.us/upload_api.php") },
+ { "fileupload", HTTPFORM_FILE(tmp) },
+ // { "rembar", "yes" },// no info bar on thumb
+ { "public", "no" },
+ { "key", HTTPFORM_8BIT(DEVKEY_IMAGESHACK) },
+ };
+ int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "http://imageshack.us/upload_api.php", frm, sizeof(frm) / sizeof(HTTPFormData));
+ mir_free(tmp);
+ if (error)
+ return !m_bAsync;
+ // start upload thread
+ if (m_bAsync) {
+ mir_forkthread(&CSendHost_ImageShack::SendThreadWrapper, this);
+ return 0;
+ }
+ SendThread();
+ return 1;
+}
+
+void CSendHost_ImageShack::SendThread()
+{
+ // send DATA and wait for m_nlreply
+ NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &m_nlhr));
+ HTTPFormDestroy(&m_nlhr);
+ if (reply) {
+ if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) {
+ reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated
+ const char* url = nullptr;
+ url = GetHTMLContent(reply->pData, "<image_link>", "</image_link>");
+ if (url && *url) {
+ m_URLthumb = m_URL = url;
+
+ int idx = m_URLthumb.ReverseFind('.');
+ if (idx != -1 && m_URLthumb.GetLength() - idx > 2)
+ m_URLthumb.Insert(idx + 1, "th");
+ else
+ m_URLthumb.Empty();
+
+ svcSendMsgExit(url);
+ return;
+ }
+
+ url = GetHTMLContent(reply->pData, "<error ", "</error>");
+ wchar_t* err = nullptr;
+ if (url) err = mir_a2u(url);
+ if (!err || !*err) { // fallback to server response mess
+ mir_free(err);
+ err = mir_a2u(reply->pData);
+ }
+ Error(L"%s", err);
+ mir_free(err);
+ }
+ else Error(SS_ERR_RESPONSE, m_pszSendTyp, reply->resultCode);
+ }
+ else Error(SS_ERR_NORESPONSE, m_pszSendTyp, m_nlhr.resultCode);
+
+ Exit(ACKRESULT_FAILED);
+}
+
+void CSendHost_ImageShack::SendThreadWrapper(void * Obj)
+{
+ reinterpret_cast<CSendHost_ImageShack*>(Obj)->SendThread();
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h
index a03647c063..7c58094aac 100644
--- a/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h
+++ b/plugins/SendScreenshotPlus/src/CSendHost_ImageShack.h
@@ -1,49 +1,49 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef _CSEND_IMAGESHACK_H
-#define _CSEND_IMAGESHACK_H
-
-class CSendHost_ImageShack : public CSend
-{
-
-public:
- // Deklaration Standardkonstruktor/Standarddestructor
- CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync);
- ~CSendHost_ImageShack();
-
- int Send() override;
-
-protected:
- NETLIBHTTPREQUEST m_nlhr;
-
- void SendThread();
- static void SendThreadWrapper(void * Obj);
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _CSEND_IMAGESHACK_H
+#define _CSEND_IMAGESHACK_H
+
+class CSendHost_ImageShack : public CSend
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ CSendHost_ImageShack(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendHost_ImageShack();
+
+ int Send() override;
+
+protected:
+ NETLIBHTTPREQUEST m_nlhr;
+
+ void SendThread();
+ static void SendThreadWrapper(void * Obj);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp b/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp
index 4aa5ccc44f..bde9f158bb 100644
--- a/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendHost_imgur.cpp
@@ -1,86 +1,86 @@
-/*
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- Version 2, December 2004
-
- Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org)
-
- Everyone is permitted to copy and distribute verbatim or modified
- copies of this license document, and changing it is allowed as long
- as the name is changed.
-
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. You just DO WHAT THE FUCK YOU WANT TO.
-*/
-#include "stdafx.h"
-
-CSendHost_Imgur::CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync)
- : CSend(Owner, hContact, bAsync)
-{
- m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
- m_pszSendTyp = LPGENW("Image upload");
-}
-
-CSendHost_Imgur::~CSendHost_Imgur()
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CSendHost_Imgur::Send()
-{
- if (!g_hNetlibUser) { // check Netlib
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED);
- return !m_bAsync;
- }
- memset(&m_nlhr, 0, sizeof(m_nlhr));
- char* tmp; tmp = mir_u2a(m_pszFile);
- HTTPFormData frm[] = {
- { "Authorization", HTTPFORM_HEADER("Client-ID 2a7303d78abe041") },
- { "image", HTTPFORM_FILE(tmp) },
- };
-
- int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "https://api.imgur.com/3/image", frm, _countof(frm));
- mir_free(tmp);
- if (error)
- return !m_bAsync;
- // start upload thread
- if (m_bAsync) {
- mir_forkthread(&CSendHost_Imgur::SendThread, this);
- return 0;
- }
- SendThread(this);
- return 1;
-}
-
-void CSendHost_Imgur::SendThread(void* obj)
-{
- CSendHost_Imgur *self = (CSendHost_Imgur*)obj;
- // send DATA and wait for m_nlreply
- NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr));
- self->HTTPFormDestroy(&self->m_nlhr);
- if (reply) {
- if (reply->dataLength) {
- JSONROOT root(reply->pData);
- if (root) {
- if ((*root)["success"].as_bool()) {
- self->m_URL = (*root)["data"]["link"].as_mstring();
- int idx = self->m_URL.ReverseFind('.');
- if (idx != -1) {
- self->m_URLthumb = self->m_URL;
- self->m_URLthumb.Insert(idx, 'm');
- }
- self->svcSendMsgExit(self->m_URL); return;
- }
- else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, (*root)["status"].as_int(), 0);
- }
- else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
- }
- else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
- }
- else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode);
-
- self->Exit(ACKRESULT_FAILED);
-}
+/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#include "stdafx.h"
+
+CSendHost_Imgur::CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync)
+ : CSend(Owner, hContact, bAsync)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("Image upload");
+}
+
+CSendHost_Imgur::~CSendHost_Imgur()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CSendHost_Imgur::Send()
+{
+ if (!g_hNetlibUser) { // check Netlib
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+ memset(&m_nlhr, 0, sizeof(m_nlhr));
+ char* tmp; tmp = mir_u2a(m_pszFile);
+ HTTPFormData frm[] = {
+ { "Authorization", HTTPFORM_HEADER("Client-ID 2a7303d78abe041") },
+ { "image", HTTPFORM_FILE(tmp) },
+ };
+
+ int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, "https://api.imgur.com/3/image", frm, _countof(frm));
+ mir_free(tmp);
+ if (error)
+ return !m_bAsync;
+ // start upload thread
+ if (m_bAsync) {
+ mir_forkthread(&CSendHost_Imgur::SendThread, this);
+ return 0;
+ }
+ SendThread(this);
+ return 1;
+}
+
+void CSendHost_Imgur::SendThread(void* obj)
+{
+ CSendHost_Imgur *self = (CSendHost_Imgur*)obj;
+ // send DATA and wait for m_nlreply
+ NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr));
+ self->HTTPFormDestroy(&self->m_nlhr);
+ if (reply) {
+ if (reply->dataLength) {
+ JSONROOT root(reply->pData);
+ if (root) {
+ if ((*root)["success"].as_bool()) {
+ self->m_URL = (*root)["data"]["link"].as_mstring();
+ int idx = self->m_URL.ReverseFind('.');
+ if (idx != -1) {
+ self->m_URLthumb = self->m_URL;
+ self->m_URLthumb.Insert(idx, 'm');
+ }
+ self->svcSendMsgExit(self->m_URL); return;
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, (*root)["status"].as_int(), 0);
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
+ }
+ else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode);
+
+ self->Exit(ACKRESULT_FAILED);
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_imgur.h b/plugins/SendScreenshotPlus/src/CSendHost_imgur.h
index b64b118391..544afecd53 100644
--- a/plugins/SendScreenshotPlus/src/CSendHost_imgur.h
+++ b/plugins/SendScreenshotPlus/src/CSendHost_imgur.h
@@ -1,30 +1,30 @@
-/*
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- Version 2, December 2004
-
- Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org)
-
- Everyone is permitted to copy and distribute verbatim or modified
- copies of this license document, and changing it is allowed as long
- as the name is changed.
-
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. You just DO WHAT THE FUCK YOU WANT TO.
-*/
-#ifndef _CSEND_HOST_IMGUR_H
-#define _CSEND_HOST_IMGUR_H
-class CSendHost_Imgur : public CSend {
-// API: http://api.imgur.com/endpoints/image
- public:
- CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync);
- ~CSendHost_Imgur();
-
- int Send() override;
-
- protected:
- NETLIBHTTPREQUEST m_nlhr;
- static void SendThread(void* obj);
-};
-#endif
+/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#ifndef _CSEND_HOST_IMGUR_H
+#define _CSEND_HOST_IMGUR_H
+class CSendHost_Imgur : public CSend {
+// API: http://api.imgur.com/endpoints/image
+ public:
+ CSendHost_Imgur(HWND Owner, MCONTACT hContact, bool bAsync);
+ ~CSendHost_Imgur();
+
+ int Send() override;
+
+ protected:
+ NETLIBHTTPREQUEST m_nlhr;
+ static void SendThread(void* obj);
+};
+#endif
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp
index 0b6c6c2311..58651ae85d 100644
--- a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp
+++ b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.cpp
@@ -1,105 +1,105 @@
-/*
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- Version 2, December 2004
-
- Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org)
-
- Everyone is permitted to copy and distribute verbatim or modified
- copies of this license document, and changing it is allowed as long
- as the name is changed.
-
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. You just DO WHAT THE FUCK YOU WANT TO.
-*/
-#include "stdafx.h"
-
-CSendHost_UploadPie::CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire)
- : m_expire(expire), CSend(Owner, hContact, bAsync)
-{
- m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
- m_pszSendTyp = LPGENW("Image upload");
-}
-
-CSendHost_UploadPie::~CSendHost_UploadPie()
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static const char kHostURL[] = "https://uploadpie.com/";
-
-int CSendHost_UploadPie::Send()
-{
- if (!g_hNetlibUser) { // check Netlib
- Error(SS_ERR_INIT, m_pszSendTyp);
- Exit(ACKRESULT_FAILED);
- return !m_bAsync;
- }
- memset(&m_nlhr, 0, sizeof(m_nlhr));
- char* tmp; tmp = mir_u2a(m_pszFile);
- HTTPFormData frm[] = {
- { "MAX_FILE_SIZE", HTTPFORM_INT(3145728) },
- { "upload", HTTPFORM_INT(1) },
- { "uploadedfile", HTTPFORM_FILE(tmp) },
- { "expire", HTTPFORM_INT(m_expire) },
- };
-
- int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, kHostURL, frm, _countof(frm));
- mir_free(tmp);
- if (error)
- return !m_bAsync;
-
- // start upload thread
- if (m_bAsync) {
- mir_forkthread(&CSendHost_UploadPie::SendThread, this);
- return 0;
- }
- SendThread(this);
- return 1;
-}
-
-void CSendHost_UploadPie::SendThread(void* obj)
-{
- CSendHost_UploadPie* self = (CSendHost_UploadPie*)obj;
- // send DATA and wait for m_nlreply
- NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr));
- self->HTTPFormDestroy(&self->m_nlhr);
- if (reply) {
- if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) {
- reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated
- char* url = reply->pData;
- do {
- char* pos;
- if ((url = strstr(url, kHostURL))) {
- for (pos = url + _countof(kHostURL)-1; (*pos >= '0'&&*pos <= '9') || (*pos >= 'a'&&*pos <= 'z') || (*pos >= 'A'&&*pos <= 'Z') || *pos == '_' || *pos == '-' || *pos == '"' || *pos == '\''; ++pos) {
- if (*pos == '"' || *pos == '\'') break;
- }
- if (url + _countof(kHostURL)-1 != pos && (*pos == '"' || *pos == '\'')) {
- *pos = '\0';
- break;
- }
- ++url;
- }
- } while (url);
-
- if (url) {
- self->m_URL = url;
- self->svcSendMsgExit(url); return;
- }
- else { // check error mess from server
- const char* err = GetHTMLContent(reply->pData, "<p id=\"error\"", "</p>");
- wchar_t* werr;
- if (err) werr = mir_a2u(err);
- else werr = mir_a2u(reply->pData);
- self->Error(L"%s", werr);
- mir_free(werr);
- }
- }
- else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
- }
- else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode);
-
- self->Exit(ACKRESULT_FAILED);
-}
+/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#include "stdafx.h"
+
+CSendHost_UploadPie::CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire)
+ : m_expire(expire), CSend(Owner, hContact, bAsync)
+{
+ m_EnableItem = SS_DLG_DESCRIPTION | SS_DLG_AUTOSEND | SS_DLG_DELETEAFTERSSEND;
+ m_pszSendTyp = LPGENW("Image upload");
+}
+
+CSendHost_UploadPie::~CSendHost_UploadPie()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static const char kHostURL[] = "https://uploadpie.com/";
+
+int CSendHost_UploadPie::Send()
+{
+ if (!g_hNetlibUser) { // check Netlib
+ Error(SS_ERR_INIT, m_pszSendTyp);
+ Exit(ACKRESULT_FAILED);
+ return !m_bAsync;
+ }
+ memset(&m_nlhr, 0, sizeof(m_nlhr));
+ char* tmp; tmp = mir_u2a(m_pszFile);
+ HTTPFormData frm[] = {
+ { "MAX_FILE_SIZE", HTTPFORM_INT(3145728) },
+ { "upload", HTTPFORM_INT(1) },
+ { "uploadedfile", HTTPFORM_FILE(tmp) },
+ { "expire", HTTPFORM_INT(m_expire) },
+ };
+
+ int error = HTTPFormCreate(&m_nlhr, REQUEST_POST, kHostURL, frm, _countof(frm));
+ mir_free(tmp);
+ if (error)
+ return !m_bAsync;
+
+ // start upload thread
+ if (m_bAsync) {
+ mir_forkthread(&CSendHost_UploadPie::SendThread, this);
+ return 0;
+ }
+ SendThread(this);
+ return 1;
+}
+
+void CSendHost_UploadPie::SendThread(void* obj)
+{
+ CSendHost_UploadPie* self = (CSendHost_UploadPie*)obj;
+ // send DATA and wait for m_nlreply
+ NLHR_PTR reply(Netlib_HttpTransaction(g_hNetlibUser, &self->m_nlhr));
+ self->HTTPFormDestroy(&self->m_nlhr);
+ if (reply) {
+ if (reply->resultCode >= 200 && reply->resultCode < 300 && reply->dataLength) {
+ reply->pData[reply->dataLength - 1] = '\0'; // make sure its null terminated
+ char* url = reply->pData;
+ do {
+ char* pos;
+ if ((url = strstr(url, kHostURL))) {
+ for (pos = url + _countof(kHostURL)-1; (*pos >= '0'&&*pos <= '9') || (*pos >= 'a'&&*pos <= 'z') || (*pos >= 'A'&&*pos <= 'Z') || *pos == '_' || *pos == '-' || *pos == '"' || *pos == '\''; ++pos) {
+ if (*pos == '"' || *pos == '\'') break;
+ }
+ if (url + _countof(kHostURL)-1 != pos && (*pos == '"' || *pos == '\'')) {
+ *pos = '\0';
+ break;
+ }
+ ++url;
+ }
+ } while (url);
+
+ if (url) {
+ self->m_URL = url;
+ self->svcSendMsgExit(url); return;
+ }
+ else { // check error mess from server
+ const char* err = GetHTMLContent(reply->pData, "<p id=\"error\"", "</p>");
+ wchar_t* werr;
+ if (err) werr = mir_a2u(err);
+ else werr = mir_a2u(reply->pData);
+ self->Error(L"%s", werr);
+ mir_free(werr);
+ }
+ }
+ else self->Error(SS_ERR_RESPONSE, self->m_pszSendTyp, reply->resultCode);
+ }
+ else self->Error(SS_ERR_NORESPONSE, self->m_pszSendTyp, self->m_nlhr.resultCode);
+
+ self->Exit(ACKRESULT_FAILED);
+}
diff --git a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h
index b1defe20c2..94cfdf1fbc 100644
--- a/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h
+++ b/plugins/SendScreenshotPlus/src/CSendHost_uploadpie.h
@@ -1,30 +1,30 @@
-/*
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- Version 2, December 2004
-
- Copyright (C) 2014-22 Miranda NG team (https://miranda-ng.org)
-
- Everyone is permitted to copy and distribute verbatim or modified
- copies of this license document, and changing it is allowed as long
- as the name is changed.
-
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. You just DO WHAT THE FUCK YOU WANT TO.
-*/
-#ifndef _CSEND_HOST_UPLOADPIE_H
-#define _CSEND_HOST_UPLOADPIE_H
-class CSendHost_UploadPie : public CSend {
- public:
- CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire);
- ~CSendHost_UploadPie();
-
- int Send() override;
-
- protected:
- int m_expire;
- NETLIBHTTPREQUEST m_nlhr;
- static void SendThread(void* obj);
-};
-#endif
+/*
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ Version 2, December 2004
+
+ Copyright (C) 2014-23 Miranda NG team (https://miranda-ng.org)
+
+ Everyone is permitted to copy and distribute verbatim or modified
+ copies of this license document, and changing it is allowed as long
+ as the name is changed.
+
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
+*/
+#ifndef _CSEND_HOST_UPLOADPIE_H
+#define _CSEND_HOST_UPLOADPIE_H
+class CSendHost_UploadPie : public CSend {
+ public:
+ CSendHost_UploadPie(HWND Owner, MCONTACT hContact, bool bAsync, int expire);
+ ~CSendHost_UploadPie();
+
+ int Send() override;
+
+ protected:
+ int m_expire;
+ NETLIBHTTPREQUEST m_nlhr;
+ static void SendThread(void* obj);
+};
+#endif
diff --git a/plugins/SendScreenshotPlus/src/Main.cpp b/plugins/SendScreenshotPlus/src/Main.cpp
index e3223f43cc..f2455932b2 100644
--- a/plugins/SendScreenshotPlus/src/Main.cpp
+++ b/plugins/SendScreenshotPlus/src/Main.cpp
@@ -1,336 +1,336 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original source code
-(c) 2004-2006 Sérgio Vieira Rolanski (ported from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-
-// Prototypes ///////////////////////////////////////////////////////////////////////////
-
-CMPlugin g_plugin;
-HGENMENU g_hMenu1, g_hMenu2;
-
-ATOM g_clsTargetHighlighter = 0;
-MGLOBAL g_myGlobals;
-HNETLIBUSER g_hNetlibUser;
-
-IconItem ICONS[ICO_END_] =
-{
- { LPGEN("Main Icon"), "main", IDI_MAIN, 32 },
- { LPGEN("Main Icon"), "mainxs", IDI_MAIN },
- { LPGEN("Target Cursor"), "target", IDI_TARGET, 32 },
- { LPGEN("Target Desktop"), "monitor", IDI_MONITOR, 32 },
-};
-
-IconItem ICONS_BTN[ICO_BTN_END_] =
-{
- { LPGEN("Help"), "help", IDI_HELP },
- { LPGEN("Open Folder"), "folder", IDI_FOLDER },
- { LPGEN("Description off"), "desc", IDI_DESC },
- { LPGEN("Description on"), "descon", IDI_DESCON },
- { LPGEN("Delete off"), "del", IDI_DEL },
- { LPGEN("Delete on"), "delon", IDI_DELON },
- { LPGEN("Prev"), "arrowl", IDI_ARROWL },
- { LPGEN("Next"), "arrowr", IDI_ARROWR },
- { LPGEN("Update"), "update", IDI_UPDATE },
- { LPGEN("OK"), "ok", IDI_OK },
- { LPGEN("Cancel"), "cancel", IDI_CANCEL },
- { LPGEN("Edit"), "edit", IDI_EDIT },
- { LPGEN("Edit on"), "editon", IDI_EDITON },
- { LPGEN("Copy"), "copy", IDI_COPY },
- { LPGEN("BBCode"), "bbc", IDI_BBC },
- { LPGEN("BBCode link"), "bbclnk", IDI_BBC2 },
- { LPGEN("Down arrow"), "downarrow", IDI_DOWNARROW },
-};
-
-static HANDLE m_hFolderScreenshot = nullptr;
-wchar_t* GetCustomPath()
-{
- wchar_t *pszPath = Utils_ReplaceVarsW(L"%miranda_userdata%\\Screenshots");
- if (m_hFolderScreenshot) {
- wchar_t szPath[1024] = { 0 };
- FoldersGetCustomPathW(m_hFolderScreenshot, szPath, 1024, pszPath);
- mir_free(pszPath);
- pszPath = mir_wstrdup(szPath);
- }
- if (!pszPath) {
- MessageBox(nullptr, L"Can not retrieve screenshot path.", L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL);
- return nullptr;
- }
- int result = CreateDirectoryTreeW(pszPath);
- if (result) {
- wchar_t szError[MAX_PATH];
- mir_snwprintf(szError, MAX_PATH, TranslateT("Could not create screenshot folder (error code: %d):\n%s\nDo you have write permissions?"), result, pszPath);
- MessageBox(nullptr, szError, L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL);
- mir_free(pszPath);
- return nullptr;
- }
- return pszPath;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Callback function of service for contact menu and main menu
-// wParam = contact handle
-// lParam = 0 (or 0xFFFF to preselect window under cursor)
-
-INT_PTR service_OpenCaptureDialog(WPARAM wParam, LPARAM lParam)
-{
- TfrmMain *frmMain = new TfrmMain();
- if (!frmMain) {
- MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL);
- return -1;
- }
- wchar_t *pszPath = GetCustomPath();
- if (!pszPath) {
- delete frmMain;
- return -1;
- }
- frmMain->Init(pszPath, wParam);
- mir_free(pszPath);
- if (lParam == 0xFFFF) {
- frmMain->SetTargetWindow(nullptr);
- }
- frmMain->Show();
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Callback function of service
-// 1. Send a screenshot of the desktop to the selected contact
-// wParam = contact handle
-// lParam = 0
-// 2. Open the capture dialog in take screenshot only mode (it will not be sent)
-// wParam = 0
-// lParam = anything but 0
-
-INT_PTR service_SendDesktop(WPARAM wParam, LPARAM)
-{
- TfrmMain *frmMain = new TfrmMain();
- if (!frmMain) {
- MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL);
- return -1;
- }
- wchar_t *pszPath = GetCustomPath();
- if (!pszPath) {
- delete frmMain;
- return -1;
- }
-
- MCONTACT hContact = (MCONTACT)wParam;
- frmMain->m_opt_chkTimed = false;
- frmMain->m_opt_tabCapture = 1;
- frmMain->m_opt_cboxDesktop = 0;
- frmMain->m_opt_chkEditor = false;
- frmMain->m_opt_cboxSendBy = Contact::IsGroupChat(hContact) ? SS_IMAGESHACK : SS_FILESEND;
- frmMain->Init(pszPath, hContact); // this method create the window hidden.
- mir_free(pszPath);
- frmMain->btnCaptureClick(); // this method will call Close()
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Callback function of service for sending image to imageshack.us
-// wParam = (char*)filename
-// lParam = (HANDLE)contact (can be null)
-
-INT_PTR service_Send2ImageShack(WPARAM wParam, LPARAM lParam)
-{
- char *result = nullptr;
- CSendHost_ImageShack *cSend = new CSendHost_ImageShack(nullptr, lParam, false);
- cSend->m_bDeleteAfterSend = false;
- cSend->SetFile((char *)wParam);
- if (lParam != NULL) {
- if (cSend->Send()) delete cSend;
- return NULL;
- }
- cSend->SendSilent();
- if (cSend->GetURL()) {
- result = mir_strdup(cSend->GetURL());
- }
- else {
- result = mir_u2a(cSend->GetErrorMsg());
- }
- delete cSend;
- return (INT_PTR)result;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static PLUGININFOEX pluginInfoEx =
-{
- sizeof(PLUGININFOEX),
- __PLUGIN_NAME,
- PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
- __DESCRIPTION,
- __AUTHOR,
- __COPYRIGHT,
- __AUTHORWEB,
- UNICODE_AWARE,
- // {ED39AF7C-BECD-404E-9499-4D04F711B9CB}
- { 0xed39af7c, 0xbecd, 0x404e, { 0x94, 0x99, 0x4d, 0x04, 0xf7, 0x11, 0xb9, 0xcb } }
-};
-
-CMPlugin::CMPlugin() :
- PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx)
-{}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// hooks
-
-int hook_ModulesLoaded(WPARAM, LPARAM)
-{
- g_myGlobals.PluginHTTPExist = ServiceExists(MS_HTTP_ACCEPT_CONNECTIONS);
- g_myGlobals.PluginFTPExist = ServiceExists(MS_FTPFILE_UPLOAD);
- g_myGlobals.PluginCloudFileExist = ServiceExists(MS_CLOUDFILE_UPLOAD);
-
- // Netlib register
- NETLIBUSER nlu = {};
- nlu.szSettingsModule = __PLUGIN_NAME;
- nlu.szDescriptiveName.w = TranslateT("SendSS");
- nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; //|NUF_NOHTTPSOPTION;
- g_hNetlibUser = Netlib_RegisterUser(&nlu);
-
- // load my button class / or use UInfoEx
- CtrlButtonLoadModule();
-
- // Folders plugin support
- m_hFolderScreenshot = FoldersRegisterCustomPathW(LPGEN("SendSS"), LPGEN("Screenshots"),
- PROFILE_PATHW L"\\" CURRENT_PROFILEW L"\\Screenshots");
- return 0;
-}
-
-int hook_SystemPreShutdown(WPARAM, LPARAM)
-{
- TfrmMain::Unload();
-
- // Netlib unregister
- Netlib_CloseHandle(g_hNetlibUser);
-
- // uninitialize classes
- CtrlButtonUnloadModule();
- return 0;
-}
-
-int hook_PrebuildContactMenu(WPARAM hContact, LPARAM)
-{
- INT_PTR flags = CallProtoService(Proto_GetBaseAccountName(hContact), PS_GETCAPS, PFLAGNUM_1, 0);
- bool bEnabled = (flags != CALLSERVICE_NOTFOUND) && (flags & PF1_FILE) != 0;
- Menu_ShowItem(g_hMenu1, bEnabled);
- Menu_ShowItem(g_hMenu2, bEnabled);
- return 0;
-}
-
-static int TabsrmmButtonsInit(WPARAM, LPARAM)
-{
- // SRMM toolbar button
- BBButton bbd = {};
- bbd.pszModuleName = MODULENAME;
- bbd.dwButtonID = 1;
- bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON;
- bbd.dwDefPos = 201;
- bbd.hIcon = GetIconHandle(ICO_MAINXS);
- Srmm_AddButton(&bbd, &g_plugin);
- return 0;
-}
-
-static int TabsrmmButtonPressed(WPARAM hContact, LPARAM lParam)
-{
- CustomButtonClickData *cbcd = (CustomButtonClickData *)lParam;
- if (!mir_strcmp(cbcd->pszModule, MODULENAME) && cbcd->dwButtonId == 1)
- CallService(MS_SENDSS_OPENDIALOG, hContact, 0);
-
- return 0;
-}
-
-int CMPlugin::Load()
-{
- // hook events
- HookEvent(ME_SYSTEM_MODULESLOADED, hook_ModulesLoaded);
- HookEvent(ME_SYSTEM_PRESHUTDOWN, hook_SystemPreShutdown);
- HookEvent(ME_CLIST_PREBUILDCONTACTMENU, hook_PrebuildContactMenu);
-
- HookEvent(ME_MSG_BUTTONPRESSED, TabsrmmButtonPressed);
- HookTemporaryEvent(ME_MSG_TOOLBARLOADED, TabsrmmButtonsInit);
-
- // icons
- g_plugin.registerIcon(MODULENAME, ICONS, MODULENAME);
- g_plugin.registerIcon(MODULENAME "/" LPGEN("Buttons"), ICONS_BTN, MODULENAME);
-
- // services
-#define srv_reg(name) CreateServiceFunction(MODULENAME "/" #name, service_##name);
- srv_reg(OpenCaptureDialog);
- srv_reg(SendDesktop);
- srv_reg(Send2ImageShack);
-
- // menu items
- CMenuItem mi(&g_plugin);
- mi.flags = CMIF_UNICODE;
- mi.hIcolibItem = GetIconHandle(ICO_MAINXS);
-
- SET_UID(mi, 0xa559a22e, 0xd0f9, 0x4553, 0x8e, 0x68, 0x55, 0xb3, 0xae, 0xc4, 0x5d, 0x93);
- mi.name.w = LPGENW("Take a screenshot");
- mi.pszService = MS_SENDSS_OPENDIALOG;
- mi.position = 1000001;
- Menu_AddMainMenuItem(&mi);
-
- SET_UID(mi, 0xfea0a84, 0x1767, 0x4605, 0x99, 0xf0, 0xa9, 0x48, 0x1a, 0xa6, 0x6f, 0xce);
- mi.name.w = LPGENW("Send screenshot");
- mi.pszService = MS_SENDSS_OPENDIALOG;
- mi.position = 1000000;
- g_hMenu1 = Menu_AddContactMenuItem(&mi);
-
- SET_UID(mi, 0x8d5b0d9a, 0x68d4, 0x4594, 0x9f, 0x41, 0x0, 0x64, 0x20, 0xe7, 0xf8, 0x9f);
- mi.name.w = LPGENW("Send desktop screenshot");
- mi.pszService = MS_SENDSS_SENDDESKTOP;
- mi.position = 1000001;
- g_hMenu2 = Menu_AddContactMenuItem(&mi);
-
- // hotkey's
- HOTKEYDESC hkd = {};
- hkd.pszName = "Open SendSS+";
- hkd.szDescription.w = LPGENW("Open SendSS+");
- hkd.szSection.w = L"SendSS+";
- hkd.pszService = MS_SENDSS_OPENDIALOG;
- hkd.lParam = 0xFFFF;
- hkd.dwFlags = HKD_UNICODE;
- g_plugin.addHotkey(&hkd);
-
- // register highlighter window class
- HBRUSH brush = CreateSolidBrush(0x0000FF00); // owned by class
- WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, DefWindowProc, 0, 0, g_plugin.getInst(), nullptr, nullptr, brush, nullptr, L"SendSSHighlighter" };
- g_clsTargetHighlighter = RegisterClass(&wndclass);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Prepare the plugin to stop
-
-int CMPlugin::Unload()
-{
- if (g_clsTargetHighlighter)
- UnregisterClass((wchar_t *)g_clsTargetHighlighter, g_plugin.getInst()), g_clsTargetHighlighter = 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-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original source code
+(c) 2004-2006 Sérgio Vieira Rolanski (ported from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+// Prototypes ///////////////////////////////////////////////////////////////////////////
+
+CMPlugin g_plugin;
+HGENMENU g_hMenu1, g_hMenu2;
+
+ATOM g_clsTargetHighlighter = 0;
+MGLOBAL g_myGlobals;
+HNETLIBUSER g_hNetlibUser;
+
+IconItem ICONS[ICO_END_] =
+{
+ { LPGEN("Main Icon"), "main", IDI_MAIN, 32 },
+ { LPGEN("Main Icon"), "mainxs", IDI_MAIN },
+ { LPGEN("Target Cursor"), "target", IDI_TARGET, 32 },
+ { LPGEN("Target Desktop"), "monitor", IDI_MONITOR, 32 },
+};
+
+IconItem ICONS_BTN[ICO_BTN_END_] =
+{
+ { LPGEN("Help"), "help", IDI_HELP },
+ { LPGEN("Open Folder"), "folder", IDI_FOLDER },
+ { LPGEN("Description off"), "desc", IDI_DESC },
+ { LPGEN("Description on"), "descon", IDI_DESCON },
+ { LPGEN("Delete off"), "del", IDI_DEL },
+ { LPGEN("Delete on"), "delon", IDI_DELON },
+ { LPGEN("Prev"), "arrowl", IDI_ARROWL },
+ { LPGEN("Next"), "arrowr", IDI_ARROWR },
+ { LPGEN("Update"), "update", IDI_UPDATE },
+ { LPGEN("OK"), "ok", IDI_OK },
+ { LPGEN("Cancel"), "cancel", IDI_CANCEL },
+ { LPGEN("Edit"), "edit", IDI_EDIT },
+ { LPGEN("Edit on"), "editon", IDI_EDITON },
+ { LPGEN("Copy"), "copy", IDI_COPY },
+ { LPGEN("BBCode"), "bbc", IDI_BBC },
+ { LPGEN("BBCode link"), "bbclnk", IDI_BBC2 },
+ { LPGEN("Down arrow"), "downarrow", IDI_DOWNARROW },
+};
+
+static HANDLE m_hFolderScreenshot = nullptr;
+wchar_t* GetCustomPath()
+{
+ wchar_t *pszPath = Utils_ReplaceVarsW(L"%miranda_userdata%\\Screenshots");
+ if (m_hFolderScreenshot) {
+ wchar_t szPath[1024] = { 0 };
+ FoldersGetCustomPathW(m_hFolderScreenshot, szPath, 1024, pszPath);
+ mir_free(pszPath);
+ pszPath = mir_wstrdup(szPath);
+ }
+ if (!pszPath) {
+ MessageBox(nullptr, L"Can not retrieve screenshot path.", L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ return nullptr;
+ }
+ int result = CreateDirectoryTreeW(pszPath);
+ if (result) {
+ wchar_t szError[MAX_PATH];
+ mir_snwprintf(szError, MAX_PATH, TranslateT("Could not create screenshot folder (error code: %d):\n%s\nDo you have write permissions?"), result, pszPath);
+ MessageBox(nullptr, szError, L"SendSS", MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ mir_free(pszPath);
+ return nullptr;
+ }
+ return pszPath;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callback function of service for contact menu and main menu
+// wParam = contact handle
+// lParam = 0 (or 0xFFFF to preselect window under cursor)
+
+INT_PTR service_OpenCaptureDialog(WPARAM wParam, LPARAM lParam)
+{
+ TfrmMain *frmMain = new TfrmMain();
+ if (!frmMain) {
+ MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ return -1;
+ }
+ wchar_t *pszPath = GetCustomPath();
+ if (!pszPath) {
+ delete frmMain;
+ return -1;
+ }
+ frmMain->Init(pszPath, wParam);
+ mir_free(pszPath);
+ if (lParam == 0xFFFF) {
+ frmMain->SetTargetWindow(nullptr);
+ }
+ frmMain->Show();
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callback function of service
+// 1. Send a screenshot of the desktop to the selected contact
+// wParam = contact handle
+// lParam = 0
+// 2. Open the capture dialog in take screenshot only mode (it will not be sent)
+// wParam = 0
+// lParam = anything but 0
+
+INT_PTR service_SendDesktop(WPARAM wParam, LPARAM)
+{
+ TfrmMain *frmMain = new TfrmMain();
+ if (!frmMain) {
+ MessageBox(nullptr, TranslateT("Could not create main dialog."), TranslateT("Error"), MB_OK | MB_ICONERROR | MB_APPLMODAL);
+ return -1;
+ }
+ wchar_t *pszPath = GetCustomPath();
+ if (!pszPath) {
+ delete frmMain;
+ return -1;
+ }
+
+ MCONTACT hContact = (MCONTACT)wParam;
+ frmMain->m_opt_chkTimed = false;
+ frmMain->m_opt_tabCapture = 1;
+ frmMain->m_opt_cboxDesktop = 0;
+ frmMain->m_opt_chkEditor = false;
+ frmMain->m_opt_cboxSendBy = Contact::IsGroupChat(hContact) ? SS_IMAGESHACK : SS_FILESEND;
+ frmMain->Init(pszPath, hContact); // this method create the window hidden.
+ mir_free(pszPath);
+ frmMain->btnCaptureClick(); // this method will call Close()
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Callback function of service for sending image to imageshack.us
+// wParam = (char*)filename
+// lParam = (HANDLE)contact (can be null)
+
+INT_PTR service_Send2ImageShack(WPARAM wParam, LPARAM lParam)
+{
+ char *result = nullptr;
+ CSendHost_ImageShack *cSend = new CSendHost_ImageShack(nullptr, lParam, false);
+ cSend->m_bDeleteAfterSend = false;
+ cSend->SetFile((char *)wParam);
+ if (lParam != NULL) {
+ if (cSend->Send()) delete cSend;
+ return NULL;
+ }
+ cSend->SendSilent();
+ if (cSend->GetURL()) {
+ result = mir_strdup(cSend->GetURL());
+ }
+ else {
+ result = mir_u2a(cSend->GetErrorMsg());
+ }
+ delete cSend;
+ return (INT_PTR)result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {ED39AF7C-BECD-404E-9499-4D04F711B9CB}
+ { 0xed39af7c, 0xbecd, 0x404e, { 0x94, 0x99, 0x4d, 0x04, 0xf7, 0x11, 0xb9, 0xcb } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// hooks
+
+int hook_ModulesLoaded(WPARAM, LPARAM)
+{
+ g_myGlobals.PluginHTTPExist = ServiceExists(MS_HTTP_ACCEPT_CONNECTIONS);
+ g_myGlobals.PluginFTPExist = ServiceExists(MS_FTPFILE_UPLOAD);
+ g_myGlobals.PluginCloudFileExist = ServiceExists(MS_CLOUDFILE_UPLOAD);
+
+ // Netlib register
+ NETLIBUSER nlu = {};
+ nlu.szSettingsModule = __PLUGIN_NAME;
+ nlu.szDescriptiveName.w = TranslateT("SendSS");
+ nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_UNICODE; //|NUF_NOHTTPSOPTION;
+ g_hNetlibUser = Netlib_RegisterUser(&nlu);
+
+ // load my button class / or use UInfoEx
+ CtrlButtonLoadModule();
+
+ // Folders plugin support
+ m_hFolderScreenshot = FoldersRegisterCustomPathW(LPGEN("SendSS"), LPGEN("Screenshots"),
+ PROFILE_PATHW L"\\" CURRENT_PROFILEW L"\\Screenshots");
+ return 0;
+}
+
+int hook_SystemPreShutdown(WPARAM, LPARAM)
+{
+ TfrmMain::Unload();
+
+ // Netlib unregister
+ Netlib_CloseHandle(g_hNetlibUser);
+
+ // uninitialize classes
+ CtrlButtonUnloadModule();
+ return 0;
+}
+
+int hook_PrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ INT_PTR flags = CallProtoService(Proto_GetBaseAccountName(hContact), PS_GETCAPS, PFLAGNUM_1, 0);
+ bool bEnabled = (flags != CALLSERVICE_NOTFOUND) && (flags & PF1_FILE) != 0;
+ Menu_ShowItem(g_hMenu1, bEnabled);
+ Menu_ShowItem(g_hMenu2, bEnabled);
+ return 0;
+}
+
+static int TabsrmmButtonsInit(WPARAM, LPARAM)
+{
+ // SRMM toolbar button
+ BBButton bbd = {};
+ bbd.pszModuleName = MODULENAME;
+ bbd.dwButtonID = 1;
+ bbd.bbbFlags = BBBF_ISIMBUTTON | BBBF_ISCHATBUTTON;
+ bbd.dwDefPos = 201;
+ bbd.hIcon = GetIconHandle(ICO_MAINXS);
+ Srmm_AddButton(&bbd, &g_plugin);
+ return 0;
+}
+
+static int TabsrmmButtonPressed(WPARAM hContact, LPARAM lParam)
+{
+ CustomButtonClickData *cbcd = (CustomButtonClickData *)lParam;
+ if (!mir_strcmp(cbcd->pszModule, MODULENAME) && cbcd->dwButtonId == 1)
+ CallService(MS_SENDSS_OPENDIALOG, hContact, 0);
+
+ return 0;
+}
+
+int CMPlugin::Load()
+{
+ // hook events
+ HookEvent(ME_SYSTEM_MODULESLOADED, hook_ModulesLoaded);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, hook_SystemPreShutdown);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, hook_PrebuildContactMenu);
+
+ HookEvent(ME_MSG_BUTTONPRESSED, TabsrmmButtonPressed);
+ HookTemporaryEvent(ME_MSG_TOOLBARLOADED, TabsrmmButtonsInit);
+
+ // icons
+ g_plugin.registerIcon(MODULENAME, ICONS, MODULENAME);
+ g_plugin.registerIcon(MODULENAME "/" LPGEN("Buttons"), ICONS_BTN, MODULENAME);
+
+ // services
+#define srv_reg(name) CreateServiceFunction(MODULENAME "/" #name, service_##name);
+ srv_reg(OpenCaptureDialog);
+ srv_reg(SendDesktop);
+ srv_reg(Send2ImageShack);
+
+ // menu items
+ CMenuItem mi(&g_plugin);
+ mi.flags = CMIF_UNICODE;
+ mi.hIcolibItem = GetIconHandle(ICO_MAINXS);
+
+ SET_UID(mi, 0xa559a22e, 0xd0f9, 0x4553, 0x8e, 0x68, 0x55, 0xb3, 0xae, 0xc4, 0x5d, 0x93);
+ mi.name.w = LPGENW("Take a screenshot");
+ mi.pszService = MS_SENDSS_OPENDIALOG;
+ mi.position = 1000001;
+ Menu_AddMainMenuItem(&mi);
+
+ SET_UID(mi, 0xfea0a84, 0x1767, 0x4605, 0x99, 0xf0, 0xa9, 0x48, 0x1a, 0xa6, 0x6f, 0xce);
+ mi.name.w = LPGENW("Send screenshot");
+ mi.pszService = MS_SENDSS_OPENDIALOG;
+ mi.position = 1000000;
+ g_hMenu1 = Menu_AddContactMenuItem(&mi);
+
+ SET_UID(mi, 0x8d5b0d9a, 0x68d4, 0x4594, 0x9f, 0x41, 0x0, 0x64, 0x20, 0xe7, 0xf8, 0x9f);
+ mi.name.w = LPGENW("Send desktop screenshot");
+ mi.pszService = MS_SENDSS_SENDDESKTOP;
+ mi.position = 1000001;
+ g_hMenu2 = Menu_AddContactMenuItem(&mi);
+
+ // hotkey's
+ HOTKEYDESC hkd = {};
+ hkd.pszName = "Open SendSS+";
+ hkd.szDescription.w = LPGENW("Open SendSS+");
+ hkd.szSection.w = L"SendSS+";
+ hkd.pszService = MS_SENDSS_OPENDIALOG;
+ hkd.lParam = 0xFFFF;
+ hkd.dwFlags = HKD_UNICODE;
+ g_plugin.addHotkey(&hkd);
+
+ // register highlighter window class
+ HBRUSH brush = CreateSolidBrush(0x0000FF00); // owned by class
+ WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, DefWindowProc, 0, 0, g_plugin.getInst(), nullptr, nullptr, brush, nullptr, L"SendSSHighlighter" };
+ g_clsTargetHighlighter = RegisterClass(&wndclass);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Prepare the plugin to stop
+
+int CMPlugin::Unload()
+{
+ if (g_clsTargetHighlighter)
+ UnregisterClass((wchar_t *)g_clsTargetHighlighter, g_plugin.getInst()), g_clsTargetHighlighter = 0;
+ return 0;
+}
diff --git a/plugins/SendScreenshotPlus/src/UMainForm.cpp b/plugins/SendScreenshotPlus/src/UMainForm.cpp
index 86b53ffef7..d67ebe3de0 100644
--- a/plugins/SendScreenshotPlus/src/UMainForm.cpp
+++ b/plugins/SendScreenshotPlus/src/UMainForm.cpp
@@ -1,1124 +1,1124 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with 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 <list>
-
-void TfrmMain::Unload()
-{
- std::list<TfrmMain*> lst;
- for (auto &it : _HandleMapping)
- lst.push_back(it.second); // we can't delete inside loop.. not MT compatible
-
- while (!lst.empty()) {
- DestroyWindow(lst.front()->m_hWnd); // deletes class
- lst.pop_front();
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-INT_PTR CALLBACK TfrmMain::DlgProc_CaptureTabPage(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- // main message handling is done inside TfrmMain::DlgTfrmMain
- switch (uMsg) {
- case WM_INITDIALOG:
- switch (lParam) {
- case IDD_UMain_CaptureWindow:
- Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_TARGET));
- SetDlgItemText(hDlg, ID_edtCaption, TranslateT("Drag&Drop the target on the desired window."));
- break;
- case IDD_UMain_CaptureDesktop:
- Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MONITOR));
- break;
- case IDD_UMain_CaptureFile:
- Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MAIN));
- break;
- }
- SetFocus(GetDlgItem(hDlg, ID_imgTarget));
- return FALSE;
-
- case WM_CTLCOLORDLG:
- case WM_CTLCOLOREDIT:
- case WM_CTLCOLORSTATIC:
- SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
- return (INT_PTR)GetStockObject(WHITE_BRUSH);
-
- case WM_COMMAND:
- if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_btnExplore) { // local file tab
- OPENFILENAME ofn = { sizeof(OPENFILENAME) };
- wchar_t filename[MAX_PATH];
- GetDlgItemText(hDlg, ID_edtSize, filename, _countof(filename));
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = hDlg;
- ofn.lpstrFilter = L"Images\0*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tif;*.tiff\0";
- ofn.nFilterIndex = 1;
- ofn.lpstrFile = filename;
- ofn.nMaxFile = MAX_PATH;
- ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY;
- if (GetOpenFileName(&ofn)) {
- SetDlgItemText(hDlg, ID_edtSize, filename);
- }
- break;
- }
- SendMessage(GetParent(hDlg), uMsg, wParam, lParam);
- break;
- case WM_NOTIFY:
- SendMessage(GetParent(hDlg), uMsg, wParam, lParam);
- break;
- case WM_DESTROY:
- break;
- }
- return FALSE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-TfrmMain::CHandleMapping TfrmMain::_HandleMapping;
-
-INT_PTR CALLBACK TfrmMain::DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- if (msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORSTATIC) {
- switch (GetWindowLongPtr((HWND)lParam, GWL_ID)) {
- case IDC_HEADERBAR:
- SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
- break;
- default:
- return 0;
- }
- SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
- return (INT_PTR)GetStockObject(WHITE_BRUSH);
- }
-
- CHandleMapping::iterator wnd;
- if (msg == WM_INITDIALOG) {
- wnd = _HandleMapping.insert(CHandleMapping::value_type(hWnd, reinterpret_cast<TfrmMain*>(lParam))).first;
- wnd->second->m_hWnd = hWnd;
- wnd->second->wmInitdialog(wParam, lParam);
- return 0;
- }
- wnd = _HandleMapping.find(hWnd);
- if (wnd == _HandleMapping.end())
- return 0;
-
- switch (msg) {
- case WM_DROPFILES:
- // Drag&Drop of local files
- {
- wchar_t filename[MAX_PATH];
- if (!DragQueryFile((HDROP)wParam, 0, filename, MAX_PATH))
- *filename = '\0';
- DragFinish((HDROP)wParam);
- if (wnd->second->m_hwndTabPage)
- ShowWindow(wnd->second->m_hwndTabPage, SW_HIDE);
-
- wnd->second->m_opt_tabCapture = 2; // activate file tab
- TabCtrl_SetCurSel(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture);
-
- TAB_INFO itab = { TCIF_PARAM };
- TabCtrl_GetItem(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture, &itab);
- wnd->second->m_hwndTabPage = itab.hwndTabPage;
-
- ShowWindow(wnd->second->m_hwndTabPage, SW_SHOW);
- SetDlgItemText(wnd->second->m_hwndTabPage, ID_edtSize, filename);
- }
- break;
- case WM_COMMAND:
- wnd->second->wmCommand(wParam, lParam);
- break;
- case WM_CLOSE:
- wnd->second->wmClose(wParam, lParam);
- break;
- case WM_DESTROY:
- delete wnd->second;
- break;
- case WM_NOTIFY:
- wnd->second->wmNotify(wParam, lParam);
- break;
- case WM_TIMER:
- wnd->second->wmTimer(wParam, lParam);
- break;
- case UM_EVENT:
- wnd->second->UMevent(wParam, lParam);
- break;
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// WM_INITDIALOG:
-
-int EnumCloudFileServices(const CFSERVICEINFO *serviceInfo, void *param)
-{
- HWND hCtrl = (HWND)param;
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, serviceInfo->userName), new UPLOAD_INFO(SS_CLOUDFILE, (void*)serviceInfo->accountName));
- return 0;
-}
-
-void TfrmMain::wmInitdialog(WPARAM, LPARAM)
-{
- HWND hCtrl;
- // Taskbar and Window icon
- Window_SetIcon_IcoLib(m_hWnd, GetIconHandle(ICO_MAIN));
-
- wchar_t *pt = mir_wstrdup(Clist_GetContactDisplayName(m_hContact));
- if (pt && (m_hContact != 0)) {
- CMStringW string;
- string.AppendFormat(TranslateT("Send screenshot to %s"), pt);
- SetWindowText(m_hWnd, string);
- }
- mir_free(pt);
-
- // Headerbar
- SendDlgItemMessage(m_hWnd, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIcon(ICO_MAIN));
-
- // Timed controls
- CheckDlgButton(m_hWnd, ID_chkTimed, m_opt_chkTimed ? BST_CHECKED : BST_UNCHECKED);
- SetDlgItemInt(m_hWnd, ID_edtTimed, (UINT)m_opt_edtTimed, FALSE);
- SendDlgItemMessage(m_hWnd, ID_upTimed, UDM_SETRANGE, 0, (LPARAM)MAKELONG(250, 1));
- chkTimedClick(); // enable disable Timed controls
-
- // create Image list for tab control
- if (!m_himlTab) {
- m_himlTab = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
- ImageList_AddIcon(m_himlTab, GetIcon(ICO_TARGET));
- ImageList_AddIcon(m_himlTab, GetIcon(ICO_MONITOR));
- ImageList_AddIcon(m_himlTab, GetIconBtn(ICO_BTN_FOLDER));
- }
-
- // create the tab control.
- {
- m_hwndTab = GetDlgItem(m_hWnd, IDC_CAPTURETAB);
- TabCtrl_SetImageList(m_hwndTab, m_himlTab);
- TabCtrl_SetItemExtra(m_hwndTab, sizeof(TAB_INFO) - sizeof(TCITEMHEADER));
- RECT rcTab;
- TAB_INFO itab;
- itab.hwndMain = m_hWnd;
- itab.hwndTab = m_hwndTab;
- itab.tcih.mask = TCIF_PARAM | TCIF_TEXT | TCIF_IMAGE;
-
- // Add a tab for each of the three child dialog boxes.
- itab.tcih.pszText = TranslateT("Window");
- itab.tcih.iImage = 0;
- itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureWindow), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureWindow);
- TabCtrl_InsertItem(m_hwndTab, 0, &itab);
-
- // get tab boundaries (required after 1st tab)
- GetClientRect(m_hwndTab, &rcTab);
- MapWindowPoints(m_hwndTab, m_hWnd, (POINT*)&rcTab, 2);
- TabCtrl_AdjustRect(m_hwndTab, 0, &rcTab);
- rcTab.bottom -= rcTab.top; rcTab.right -= rcTab.left;
-
- SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
- CheckDlgButton(itab.hwndTabPage, ID_chkIndirectCapture, m_opt_chkIndirectCapture ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(itab.hwndTabPage, ID_chkClientArea, m_opt_chkClientArea ? BST_CHECKED : BST_UNCHECKED);
-
- itab.tcih.pszText = TranslateT("Desktop");
- itab.tcih.iImage = 1;
- itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureDesktop), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureDesktop);
- TabCtrl_InsertItem(m_hwndTab, 1, &itab);
- SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
-
- hCtrl = GetDlgItem(itab.hwndTabPage, ID_edtCaption);
- ComboBox_ResetContent(hCtrl);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Entire Desktop>")), 0);
- ComboBox_SetCurSel(hCtrl, 0);
- if (m_MonitorCount > 1) {
- wchar_t tszTemp[120];
- for (size_t mon = 0; mon < m_MonitorCount; ++mon) { // @todo : fix format for non MSVC compilers
- mir_snwprintf(tszTemp, L"%Iu. %s%s",
- mon + 1, TranslateT("Monitor"),
- (m_Monitors[mon].dwFlags & MONITORINFOF_PRIMARY) ? TranslateT(" (primary)") : L""
- );
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, tszTemp), mon + 1);
- }
- ComboBox_SelectItem(hCtrl, m_opt_cboxDesktop);
- }
- PostMessage(m_hWnd, WM_COMMAND, MAKEWPARAM(ID_edtCaption, CBN_SELCHANGE), (LPARAM)hCtrl);
-
- itab.tcih.pszText = TranslateT("File");
- itab.tcih.iImage = 2;
- itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureFile), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureFile);
- TabCtrl_InsertItem(m_hwndTab, 2, &itab);
- SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
-
- // select tab and set m_hwndTabPage
- TabCtrl_SetCurSel(m_hwndTab, m_opt_tabCapture);
- itab.tcih.mask = TCIF_PARAM;
- TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab);
- m_hwndTabPage = itab.hwndTabPage;
- ShowWindow(m_hwndTabPage, SW_SHOW);
-
- // enable Drag&Drop for local file pane
- typedef BOOL(WINAPI *ChangeWindowMessageFilterEx_t)(HWND hwnd, UINT message, uint32_t action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
- ChangeWindowMessageFilterEx_t pChangeWindowMessageFilterEx;
- pChangeWindowMessageFilterEx = (ChangeWindowMessageFilterEx_t)GetProcAddress(GetModuleHandleA("user32"), "ChangeWindowMessageFilterEx");
- if (pChangeWindowMessageFilterEx) { // Win7+, UAC fix
- pChangeWindowMessageFilterEx(m_hWnd, WM_DROPFILES, MSGFLT_ALLOW, nullptr);
- pChangeWindowMessageFilterEx(m_hWnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr);
- pChangeWindowMessageFilterEx(m_hWnd, 0x0049/*WM_COPYGLOBALDATA*/, MSGFLT_ALLOW, nullptr);
- }
- DragAcceptFiles(m_hWnd, 1);
- }
-
- // init Format combo box
- {
- hCtrl = GetDlgItem(m_hWnd, ID_cboxFormat);
- ComboBox_ResetContent(hCtrl);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"PNG"), 0);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"JPG"), 1);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"BMP"), 2);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"TIF"), 3);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"GIF"), 4);
- ComboBox_SelectItem(hCtrl, m_opt_cboxFormat);
- }
-
- // init SendBy combo box
- UPLOAD_INFO *pDefault = nullptr;
- {
- hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy);
- ComboBox_ResetContent(hCtrl);
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Only save>")), new UPLOAD_INFO(SS_JUSTSAVE));
- if (m_hContact) {
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("File Transfer")), new UPLOAD_INFO(SS_FILESEND));
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("E-mail")), new UPLOAD_INFO(SS_EMAIL));
- if (g_myGlobals.PluginHTTPExist) {
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"HTTP Server"), new UPLOAD_INFO(SS_HTTPSERVER));
- }
- else if (m_opt_cboxSendBy == SS_HTTPSERVER) {
- m_opt_cboxSendBy = SS_IMAGESHACK;
- }
- if (g_myGlobals.PluginFTPExist) {
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("FTP File")), new UPLOAD_INFO(SS_FTPFILE));
- }
- else if (m_opt_cboxSendBy == SS_FTPFILE) {
- m_opt_cboxSendBy = SS_IMAGESHACK;
- }
- }
- else if (m_opt_cboxSendBy == SS_FILESEND || m_opt_cboxSendBy == SS_EMAIL || m_opt_cboxSendBy == SS_HTTPSERVER || m_opt_cboxSendBy == SS_FTPFILE) {
- m_opt_cboxSendBy = SS_IMAGESHACK;
- }
- if (g_myGlobals.PluginCloudFileExist) {
- CallService(MS_CLOUDFILE_ENUMSERVICES, (WPARAM)EnumCloudFileServices, (LPARAM)hCtrl);
- }
- else if (m_opt_cboxSendBy == SS_CLOUDFILE) {
- m_opt_cboxSendBy = SS_IMAGESHACK;
- }
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"ImageShack"), new UPLOAD_INFO(SS_IMAGESHACK));
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (30m)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)1));
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1d)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)4));
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1w)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)5));
- ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"Imgur"), new UPLOAD_INFO(SS_IMGUR));
-
- for (int i = 0; i < ComboBox_GetCount(hCtrl); i++) {
- UPLOAD_INFO *p = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i);
- if (p && p->sendBy == m_opt_cboxSendBy) {
- pDefault = p;
- ComboBox_SetCurSel(hCtrl, i);
- break;
- }
- }
- }
-
- // init footer options
- CheckDlgButton(m_hWnd, ID_chkOpenAgain, m_opt_chkOpenAgain ? BST_CHECKED : BST_UNCHECKED);
-
- if (hCtrl = GetDlgItem(m_hWnd, ID_btnExplore)) {
- SendDlgItemMessage(m_hWnd, ID_btnExplore, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open Folder"), MBBF_TCHAR);
- HICON hIcon = GetIconBtn(ICO_BTN_FOLDER);
- SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- SetWindowText(hCtrl, hIcon ? L"" : L"...");
- }
-
- if (hCtrl = GetDlgItem(m_hWnd, ID_chkDesc)) {
- SendDlgItemMessage(m_hWnd, ID_chkDesc, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Fill description textbox."), MBBF_TCHAR);
- HICON hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
- SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- SetWindowText(hCtrl, hIcon ? L"" : L"D");
- SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDesc ? BST_CHECKED : BST_UNCHECKED, NULL);
- }
-
- if (hCtrl = GetDlgItem(m_hWnd, ID_chkDeleteAfterSend)) {
- SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete after send"), MBBF_TCHAR);
- HICON hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
- SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- SetWindowText(hCtrl, hIcon ? L"" : L"X");
- SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDeleteAfterSend ? BST_CHECKED : BST_UNCHECKED, NULL);
- }
-
- if (hCtrl = GetDlgItem(m_hWnd, ID_chkEditor)) {
- SendDlgItemMessage(m_hWnd, ID_chkEditor, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open editor before sending"), MBBF_TCHAR);
- HICON hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT);
- SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- SetWindowText(hCtrl, hIcon ? L"" : L"E");
- SendMessage(hCtrl, BM_SETCHECK, m_opt_chkEditor ? BST_CHECKED : BST_UNCHECKED, NULL);
- }
-
- if (hCtrl = GetDlgItem(m_hWnd, ID_btnCapture)) {
- SendMessage(hCtrl, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Capture"), MBBF_TCHAR);
- HICON hIcon = GetIconBtn(ICO_BTN_OK);
- SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- SetWindowText(hCtrl, TranslateT("&Capture"));
- SendMessage(hCtrl, BUTTONSETDEFAULT, 1, NULL);
- }
- cboxSendByChange((pDefault) ? pDefault->param : nullptr); // enable disable controls
-
- TranslateDialogDefault(m_hWnd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// WM_COMMAND:
-
-void TfrmMain::wmCommand(WPARAM wParam, LPARAM lParam)
-{
- HICON hIcon;
-
- int IDControl = LOWORD(wParam);
- switch (HIWORD(wParam)) {
- case BN_CLICKED: // Button controls
- switch (IDControl) {
- case IDCANCEL: // ESC pressed
- this->Close();
- break;
- case ID_chkTimed:
- m_opt_chkTimed = (uint8_t)Button_GetCheck((HWND)lParam);
- TfrmMain::chkTimedClick();
- break;
- case ID_chkIndirectCapture:
- m_opt_chkIndirectCapture = (uint8_t)Button_GetCheck((HWND)lParam);
- break;
- case ID_chkClientArea:
- m_opt_chkClientArea = (uint8_t)Button_GetCheck((HWND)lParam);
- if (m_hTargetWindow)
- edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, GetParent((HWND)lParam), ID_edtSize);
- break;
- case ID_imgTarget:
- if (m_opt_tabCapture != 0) break;
- m_hLastWin = nullptr;
- SetTimer(m_hWnd, ID_imgTarget, BUTTON_POLLDELAY, nullptr);
- break;
- case ID_btnExplore:
- TfrmMain::btnExploreClick();
- break;
- case ID_chkDesc:
- m_opt_btnDesc = !m_opt_btnDesc;
- hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
- SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- break;
- case ID_chkDeleteAfterSend:
- m_opt_btnDeleteAfterSend = !m_opt_btnDeleteAfterSend;
- hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
- SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- if (m_cSend) m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend;
- break;
- case ID_chkEditor:
- m_opt_chkEditor = !m_opt_chkEditor;
- hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT);
- SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- break;
- case ID_chkOpenAgain:
- m_opt_chkOpenAgain = Button_GetCheck((HWND)lParam);
- break;
- case ID_btnCapture:
- TfrmMain::btnCaptureClick();
- break;
- }
- break;
-
- case CBN_SELCHANGE: // ComboBox controls
- switch (IDControl) { // lParam = Handle to the control
- case ID_cboxFormat: // not finish
- m_opt_cboxFormat = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
- break;
- case ID_cboxSendBy:
- {
- UPLOAD_INFO *upload = (UPLOAD_INFO*)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
- m_opt_cboxSendBy = upload->sendBy;
- cboxSendByChange(upload->param);
- }
- break;
-
- case ID_edtCaption: // cboxDesktopChange
- m_opt_cboxDesktop = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
- m_hTargetWindow = nullptr;
- if (m_opt_cboxDesktop > 0) {
- edtSizeUpdate(m_Monitors[m_opt_cboxDesktop - 1].rcMonitor, GetParent((HWND)lParam), ID_edtSize);
- }
- else {
- edtSizeUpdate(m_VirtualScreen, GetParent((HWND)lParam), ID_edtSize);
- }
- break;
- }
- break;
-
- case EN_CHANGE: // Edit controls
- switch (IDControl) { // lParam = Handle to the control
- case ID_edtQuality:
- m_opt_edtQuality = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtQuality, nullptr, FALSE);
- break;
- case ID_edtTimed:
- m_opt_edtTimed = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtTimed, nullptr, FALSE);
- break;
- }
- break;
- }
-}
-
-// WM_CLOSE:
-void TfrmMain::wmClose(WPARAM, LPARAM)
-{
- HWND hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy);
- size_t count = ComboBox_GetCount(hCtrl);
- for (size_t i = 0; i < count; i++) {
- UPLOAD_INFO *ui = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i);
- delete ui;
- }
- DestroyWindow(m_hWnd);
- return;
-}
-
-// WM_TIMER:
-const int g_iTargetBorder = 7;
-void TfrmMain::SetTargetWindow(HWND hwnd)
-{
- if (!hwnd) {
- POINT point; GetCursorPos(&point);
- hwnd = WindowFromPoint(point);
- for (HWND hTMP; (hTMP = GetParent(hwnd)); hwnd = hTMP)
- ;
- }
- m_hTargetWindow = hwnd;
- int len = GetWindowTextLength(m_hTargetWindow) + 1;
- wchar_t *lpTitle;
- if (len > 1) {
- lpTitle = (wchar_t*)mir_alloc(len*sizeof(wchar_t));
- GetWindowText(m_hTargetWindow, lpTitle, len);
- }
- else { // no WindowText present, use WindowClass
- lpTitle = (wchar_t*)mir_alloc(64 * sizeof(wchar_t));
- RealGetWindowClass(m_hTargetWindow, lpTitle, 64);
- }
- SetDlgItemText(m_hwndTabPage, ID_edtCaption, lpTitle);
- mir_free(lpTitle);
- edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, m_hwndTabPage, ID_edtSize);
-}
-
-void TfrmMain::wmTimer(WPARAM wParam, LPARAM)
-{
- if (wParam == ID_imgTarget) { // Timer for Target selector
- static int primarymouse;
- if (!m_hTargetHighlighter) {
- primarymouse = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
- m_hTargetHighlighter = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (wchar_t*)g_clsTargetHighlighter, nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, g_plugin.getInst(), nullptr);
- if (!m_hTargetHighlighter) return;
- SetLayeredWindowAttributes(m_hTargetHighlighter, 0, 123, LWA_ALPHA);
- SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_IBEAM); // text cursor
- SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_NORMAL);
- SetActiveWindow(m_hTargetHighlighter); // activate highlighter to fix focus problems with UAC (unelevated GetAsyncKeyState() fails if an elevated app got focus)
- Hide();
- }
- if (!(GetAsyncKeyState(primarymouse) & 0x8000)) {
- KillTimer(m_hWnd, ID_imgTarget);
- SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0);
- DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr;
- SetTargetWindow(m_hLastWin);
- Show();
- return;
- }
- POINT point; GetCursorPos(&point);
- HWND hwnd = WindowFromPoint(point);
- if (!((GetAsyncKeyState(VK_SHIFT) | GetAsyncKeyState(VK_MENU)) & 0x8000))
- for (HWND hTMP; (hTMP = GetAncestor(hwnd, GA_PARENT)) && IsChild(hTMP, hwnd); hwnd = hTMP);
- else {
- ScreenToClient(hwnd, &point);
- HWND hTMP; if ((hTMP = RealChildWindowFromPoint(hwnd, point)))
- hwnd = hTMP;
- }
- if (hwnd != m_hLastWin) {
- m_hLastWin = hwnd;
- RECT rect;
- if (m_opt_chkClientArea) {
- GetClientRect(hwnd, &rect);
- ClientToScreen(hwnd, (POINT*)&rect);
- rect.right = rect.left + rect.right;
- rect.bottom = rect.top + rect.bottom;
- }
- else
- GetWindowRect(hwnd, &rect);
- int width = rect.right - rect.left;
- int height = rect.bottom - rect.top;
- if (g_iTargetBorder) {
- SetWindowPos(m_hTargetHighlighter, nullptr, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE);
- if (width > g_iTargetBorder * 2 && height > g_iTargetBorder * 2) {
- HRGN hRegnNew = CreateRectRgn(0, 0, width, height);
- HRGN hRgnHole = CreateRectRgn(g_iTargetBorder, g_iTargetBorder, width - g_iTargetBorder, height - g_iTargetBorder);
- CombineRgn(hRegnNew, hRegnNew, hRgnHole, RGN_XOR);
- DeleteObject(hRgnHole);
- SetWindowRgn(m_hTargetHighlighter, hRegnNew, FALSE); // cleans up hRegnNew
- }
- else SetWindowRgn(m_hTargetHighlighter, nullptr, FALSE);
- }
- SetWindowPos(m_hTargetHighlighter, HWND_TOPMOST, rect.left, rect.top, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
- }
- return;
- }
- if (wParam == ID_chkTimed) { // Timer for Screenshot
-#ifdef _DEBUG
- OutputDebugStringA("SS Bitmap Timer Start\r\n");
-#endif
- if (!m_bCapture) { // only start once
- if (m_Screenshot) {
- FreeImage_Unload(m_Screenshot);
- m_Screenshot = nullptr;
- }
- m_bCapture = true;
- switch (m_opt_tabCapture) {
- case 0:
- m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture);
- break;
- case 1:
- m_Screenshot = CaptureMonitor((m_opt_cboxDesktop > 0) ? m_Monitors[m_opt_cboxDesktop - 1].szDevice : nullptr);
- break;
- case 2: // edge case, existing local file
- break;
-#ifdef _DEBUG
- default:
- OutputDebugStringA("SS Bitmap Timer Stop (no tabCapture)\r\n");
-#endif
- }
- m_bCapture = false;
- if (m_Screenshot || m_opt_tabCapture == 2) { // @note : test without "if"
- KillTimer(m_hWnd, ID_chkTimed);
-#ifdef _DEBUG
- OutputDebugStringA("SS Bitmap Timer Stop (CaptureDone)\r\n");
-#endif
- SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone);
- }
- }
- }
-}
-
-// WM_NOTIFY:
-void TfrmMain::wmNotify(WPARAM, LPARAM lParam)
-{
- switch (((LPNMHDR)lParam)->idFrom) {
- case IDC_CAPTURETAB:
- // HWND hwndFrom; = member is handle to the tab control
- // UINT_PTR idFrom; = member is the child window identifier of the tab control.
- // UINT code; = member is TCN_SELCHANGE
- switch (((LPNMHDR)lParam)->code) {
- case TCN_SELCHANGING:
- if (m_hwndTabPage) {
- ShowWindow(m_hwndTabPage, SW_HIDE);
- m_hwndTabPage = nullptr;
- }
- break;
-
- case TCN_SELCHANGE:
- {
- TAB_INFO itab = { TCIF_PARAM };
- m_opt_tabCapture = TabCtrl_GetCurSel(m_hwndTab);
- TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab);
- m_hwndTabPage = itab.hwndTabPage;
- }
- ShowWindow(m_hwndTabPage, SW_SHOW);
- break;
- }
- break;
- }
-}
-
-// UM_EVENT:
-void TfrmMain::UMevent(WPARAM, LPARAM lParam)
-{
- // HWND hWnd = (HWND)wParam;
- switch (lParam) {
- case EVT_CaptureDone:
- if (!m_Screenshot && m_opt_tabCapture != 2) {
- wchar_t *err = TranslateT("Couldn't take a screenshot");
- MessageBox(nullptr, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
- Show();
- return;
- }
- FormClose();
- break;
-
- case EVT_SendFileDone:
- break;
-
- case EVT_CheckOpenAgain:
- if (m_opt_chkOpenAgain) {
- if (m_Screenshot) {
- FreeImage_Unload(m_Screenshot);
- m_Screenshot = nullptr;
- }
- Show();
- }
- else {// Saving Options and close
- SaveOptions();
- Close();
- }
- break;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Standard konstruktor/destruktor
-
-TfrmMain::TfrmMain()
-{
- /* m_opt_XXX */
- m_bOnExitSave = TRUE;
-
- m_hWnd = nullptr;
- m_hContact = NULL;
- m_hTargetWindow = m_hLastWin = nullptr;
- m_hTargetHighlighter = nullptr;
- m_FDestFolder = m_pszFile = nullptr;
- m_Screenshot = nullptr;
- /* m_AlphaColor */
- m_cSend = nullptr;
-
- m_Monitors = nullptr;
- m_MonitorCount = MonitorInfoEnum(m_Monitors, m_VirtualScreen);
- /* m_opt_XXX */ LoadOptions();
- m_bCapture = false;
- /* m_hwndTab,m_hwndTabPage */
- m_himlTab = nullptr;
-}
-
-TfrmMain::~TfrmMain()
-{
- _HandleMapping.erase(m_hWnd);
- mir_free(m_pszFile);
- mir_free(m_FDestFolder);
- mir_free(m_Monitors);
- if (m_Screenshot) FreeImage_Unload(m_Screenshot);
- if (m_cSend) delete m_cSend;
- if (m_hTargetHighlighter) {
- DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr;
- SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Load / Saving options from miranda's database
-
-void TfrmMain::LoadOptions(void)
-{
- uint32_t rgb = g_plugin.getDword("AlphaColor", 16777215);
- m_AlphaColor.rgbRed = GetRValue(rgb);
- m_AlphaColor.rgbGreen = GetGValue(rgb);
- m_AlphaColor.rgbBlue = GetBValue(rgb);
- m_AlphaColor.rgbReserved = 0;
-
- m_opt_edtQuality = g_plugin.getByte("JpegQuality", 75);
-
- m_opt_tabCapture = g_plugin.getByte("Capture", 0);
- m_opt_chkIndirectCapture = g_plugin.getByte("IndirectCapture", 0);
- m_opt_chkClientArea = g_plugin.getByte("ClientArea", 0);
- m_opt_cboxDesktop = g_plugin.getByte("Desktop", 0);
-
- m_opt_chkTimed = g_plugin.getByte("TimedCap", 0);
- m_opt_edtTimed = g_plugin.getByte("CapTime", 3);
- m_opt_cboxFormat = g_plugin.getByte("OutputFormat", 0);
- m_opt_cboxSendBy = g_plugin.getByte("SendBy", 0);
-
- m_opt_btnDesc = g_plugin.getByte("AutoDescription", 1);
- m_opt_btnDeleteAfterSend = g_plugin.getByte("DelAfterSend", 1) != 0;
- m_opt_chkEditor = g_plugin.getByte("Preview", 0);
- m_opt_chkOpenAgain = g_plugin.getByte("OpenAgain", 0);
-}
-
-void TfrmMain::SaveOptions(void)
-{
- if (m_bOnExitSave) {
- g_plugin.setDword("AlphaColor",
- (uint32_t)RGB(m_AlphaColor.rgbRed, m_AlphaColor.rgbGreen, m_AlphaColor.rgbBlue));
-
- g_plugin.setByte("JpegQuality", m_opt_edtQuality);
-
- g_plugin.setByte("Capture", m_opt_tabCapture);
- g_plugin.setByte("IndirectCapture", m_opt_chkIndirectCapture);
- g_plugin.setByte("ClientArea", m_opt_chkClientArea);
- g_plugin.setByte("Desktop", m_opt_cboxDesktop);
-
- g_plugin.setByte("TimedCap", m_opt_chkTimed);
- g_plugin.setByte("CapTime", m_opt_edtTimed);
- g_plugin.setByte("OutputFormat", m_opt_cboxFormat);
- g_plugin.setByte("SendBy", m_opt_cboxSendBy);
-
- g_plugin.setByte("AutoDescription", m_opt_btnDesc);
- g_plugin.setByte("DelAfterSend", m_opt_btnDeleteAfterSend);
- g_plugin.setByte("Preview", m_opt_chkEditor);
- g_plugin.setByte("OpenAgain", m_opt_chkOpenAgain);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::Init(wchar_t *DestFolder, MCONTACT Contact)
-{
- m_FDestFolder = mir_wstrdup(DestFolder);
- m_hContact = Contact;
-
- // create window
- m_hWnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMainForm), nullptr, DlgTfrmMain, (LPARAM)this);
-
- // register object
- _HandleMapping.insert(CHandleMapping::value_type(m_hWnd, this));
-
- // check Contact
- if (m_cSend)
- m_cSend->SetContact(Contact);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::btnCaptureClick()
-{
- if (m_opt_tabCapture == 1) m_hTargetWindow = GetDesktopWindow();
- else if (m_opt_tabCapture == 2) {
- wchar_t filename[MAX_PATH];
- GetDlgItemText(m_hwndTabPage, ID_edtSize, filename, _countof(filename));
- FILE *fp = _wfopen(filename, L"rb");
- if (!fp) {
- wchar_t *err = TranslateT("Select a file");
- MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
- return;
- }
- fclose(fp);
- mir_free(m_pszFile); m_pszFile = mir_wstrdup(filename);
- }
- else if (!m_hTargetWindow) {
- wchar_t *err = TranslateT("Select a target window.");
- MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
- return;
- }
- TfrmMain::Hide();
-
- if (m_opt_chkTimed) {
- SetTimer(m_hWnd, ID_chkTimed, m_opt_edtTimed ? m_opt_edtTimed * 1000 : 500, nullptr); // calls EVT_CaptureDone
- return;
- }
- if (m_opt_tabCapture == 1) { // desktop needs always time to update from TfrmMain::Hide()
- SetTimer(m_hWnd, ID_chkTimed, 500, nullptr); // calls EVT_CaptureDone
- return;
- }
- if (m_opt_tabCapture != 2) {
- m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture);
- }
- SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::chkTimedClick()
-{
- Button_Enable(GetDlgItem(m_hWnd, ID_edtTimedLabel), (BOOL)m_opt_chkTimed);
- Button_Enable(GetDlgItem(m_hWnd, ID_edtTimed), (BOOL)m_opt_chkTimed);
- Button_Enable(GetDlgItem(m_hWnd, ID_upTimed), (BOOL)m_opt_chkTimed);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::cboxSendByChange(void *param)
-{
- BOOL bState;
- HICON hIcon;
- uint8_t itemFlag = SS_DLG_DESCRIPTION;
- if (m_cSend)
- delete m_cSend;
- switch (m_opt_cboxSendBy) {
- case SS_FILESEND: // "File Transfer"
- m_cSend = new CSendFile(m_hWnd, m_hContact, true);
- break;
- case SS_EMAIL: // "E-mail"
- m_cSend = new CSendEmail(m_hWnd, m_hContact, true);
- break;
- case SS_HTTPSERVER: // "HTTP Server"
- m_cSend = new CSendHTTPServer(m_hWnd, m_hContact, true);
- break;
- case SS_FTPFILE: // "FTP File"
- m_cSend = new CSendFTPFile(m_hWnd, m_hContact, true);
- break;
- case SS_CLOUDFILE: // "CloudFile"
- m_cSend = new CSendCloudFile(m_hWnd, m_hContact, false, (char*)param);
- break;
- case SS_IMAGESHACK: // "ImageShack"
- m_cSend = new CSendHost_ImageShack(m_hWnd, m_hContact, true);
- break;
- case SS_UPLOADPIE: // "Upload Pie"
- m_cSend = new CSendHost_UploadPie(m_hWnd, m_hContact, true, (INT_PTR)param);
- break;
- case SS_IMGUR:
- m_cSend = new CSendHost_Imgur(m_hWnd, m_hContact, true);
- break;
- default:
- m_cSend = nullptr;
- break;
- }
- if (m_cSend) {
- itemFlag = m_cSend->GetEnableItem();
- m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend;
- }
- bState = (itemFlag & SS_DLG_DELETEAFTERSSEND);
- hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
- SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr));
- Button_Enable(GetDlgItem(m_hWnd, ID_chkDeleteAfterSend), bState);
-
- bState = (itemFlag & SS_DLG_DESCRIPTION);
- hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
- SendDlgItemMessage(m_hWnd, ID_chkDesc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr));
- Button_Enable(GetDlgItem(m_hWnd, ID_chkDesc), bState);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::btnExploreClick()
-{
- if (m_FDestFolder)
- ShellExecute(nullptr, L"explore", m_FDestFolder, nullptr, nullptr, SW_SHOW);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl)
-{
- // get window dimensions
- RECT rect = { 0 };
- RECT cliRect = { 0 };
- wchar_t B[33], H[16];
- GetWindowRect(hWnd, &rect);
- if (ClientArea) {
- POINT pt = { 0 };
- GetClientRect(hWnd, &cliRect);
- pt.x = cliRect.left;
- pt.y = cliRect.top;
- ClientToScreen(hWnd, &pt);
- pt.x = pt.x - rect.left; // offset x for client area
- pt.y = pt.y - rect.top; // offset y for client area
- rect = cliRect;
- }
-
- _itow(rect.right - rect.left, B, 10);
- _itow(rect.bottom - rect.top, H, 10);
- mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B));
- mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B));
- SetDlgItemText(hTarget, Ctrl, B);
-}
-
-void TfrmMain::edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl)
-{
- wchar_t B[33], H[16];
- _itow(ABS(rect.right - rect.left), B, 10);
- _itow(ABS(rect.bottom - rect.top), H, 10);
- mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B));
- mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B));
- SetDlgItemText(hTarget, Ctrl, B);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-INT_PTR TfrmMain::SaveScreenshot(FIBITMAP *dib)
-{
- if (!dib)
- return 1;
-
- // generate file name
- unsigned FileNumber = g_plugin.getDword("FileNumber", 0) + 1;
- if (FileNumber > 99999)
- FileNumber = 1;
-
- CMStringW wszFileName(m_FDestFolder);
- if (wszFileName.Right(1) != L"\\")
- wszFileName.Append(L"\\");
- wszFileName.AppendFormat(L"shot%.5u", FileNumber);
-
- // generate a description according to the screenshot
- wchar_t winText[1024];
- GetDlgItemText(m_hwndTabPage, ID_edtCaption, winText, _countof(winText));
-
- CMStringW wszFileDesc;
- if (m_opt_tabCapture)
- wszFileDesc.Format(TranslateT("Screenshot of \"%s\""), winText);
- else {
- if (m_opt_chkClientArea)
- wszFileDesc.Format(TranslateT("Screenshot for client area of \"%s\" window"), winText);
- else
- wszFileDesc.Format(TranslateT("Screenshot of \"%s\" window"), winText);
- }
-
- // convert to 32Bits (make sure it is 32bit)
- FIBITMAP *dib_new = FreeImage_ConvertTo32Bits(dib);
- FreeImage_SetTransparent(dib_new, TRUE);
-
- BOOL ret = FALSE;
- HWND hwndCombo = GetDlgItem(m_hWnd, ID_cboxFormat);
- switch (ComboBox_GetItemData(hwndCombo, ComboBox_GetCurSel(hwndCombo))) {
- case 0: // PNG
- wszFileName.Append(L".png");
- ret = FreeImage_SaveU(FIF_PNG, dib_new, wszFileName, 0);
- break;
-
- case 1: // JPG
- wszFileName.Append(L".jpg");
- {
- FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
- FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
- FreeImage_Unload(dib32);
- ret = FreeImage_SaveU(FIF_JPEG, dib24, wszFileName, 0);
- FreeImage_Unload(dib24);
- }
- break;
-
- case 2: // BMP
- wszFileName.Append(L".bmp");
- {
- FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
- FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
- FreeImage_Unload(dib32);
- ret = FreeImage_SaveU(FIF_BMP, dib24, wszFileName, 0);
- FreeImage_Unload(dib24);
- }
- break;
-
- case 3: // TIFF (miranda freeimage interface do not support save tiff, we udse GDI+)
- wszFileName.Append(L".tif");
- {
- FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
- FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
- FreeImage_Unload(dib32);
-
- HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib24);
- FreeImage_Unload(dib24);
- SaveTIF(hBmp, wszFileName);
- DeleteObject(hBmp);
- }
- ret = TRUE;
- break;
-
- case 4: // GIF
- wszFileName.Append(L".gif");
- {
- HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib_new);
- SaveGIF(hBmp, wszFileName);
- DeleteObject(hBmp);
- }
- ret = TRUE;
- break;
- }
-
- FreeImage_Unload(dib_new);
-
- if (!ret)
- return 1;
-
- g_plugin.setDword("FileNumber", FileNumber);
- replaceStrW(m_pszFile, wszFileName);
-
- if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc)
- wszFileDesc.Empty();
-
- if (m_cSend) {
- m_cSend->SetFile(m_pszFile);
- m_cSend->SetDescription(wszFileDesc);
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void TfrmMain::FormClose()
-{
- bool bCanDelete = m_opt_btnDeleteAfterSend;
- if (m_opt_tabCapture == 2) { // existing file
- wchar_t description[1024];
- GetDlgItemText(m_hwndTabPage, ID_edtCaption, description, _countof(description));
- if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc)
- *description = '\0';
- if (m_cSend) {
- m_cSend->m_bDeleteAfterSend = false; // well... guess it's better to not delete existing files for now...
- m_cSend->SetFile(m_pszFile);
- m_cSend->SetDescription(description);
- }
- bCanDelete = false;
- }
- else if (SaveScreenshot(m_Screenshot)) { // Saving the screenshot
- Show(); // Error from SaveScreenshot
- return;
- }
-
- bool send = true;
- if (m_opt_chkEditor) {
- SHELLEXECUTEINFO shex = { sizeof(SHELLEXECUTEINFO) };
- shex.fMask = SEE_MASK_NOCLOSEPROCESS;
- shex.lpVerb = L"edit";
- shex.lpFile = m_pszFile;
- shex.nShow = SW_SHOWNORMAL;
- ShellExecuteEx(&shex);
- if (shex.hProcess) {
- uint32_t res;
- MSG msg;
- do {
- // wait for editor exit or messages/input
- res = MsgWaitForMultipleObjects(1, &shex.hProcess, 0, INFINITE, QS_ALLINPUT);
- while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
- if (msg.message == WM_QUIT) {
- res = WAIT_OBJECT_0;
- PostMessage(nullptr, WM_QUIT, 0, 0); // forward for outer message loops
- break;
- }
-
- // process dialog messages (of unknown dialogs)
- HWND hwndDlgModeless = GetActiveWindow();
- if (hwndDlgModeless != nullptr && IsDialogMessage(hwndDlgModeless, &msg)) /* Wine fix. */
- continue;
-
- // process messages
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- } while (res == WAIT_OBJECT_0 + 1);
- CloseHandle(shex.hProcess);
- }
- if (MessageBox(m_hWnd, TranslateT("Send screenshot?"), L"SendSS", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL) != IDYES)
- send = false;
- }
-
- if (send && m_cSend && m_pszFile) {
- if (!m_cSend->Send()) // not sent now, class deletes itself later
- m_cSend = nullptr;
- cboxSendByChange(nullptr);
- }
- else if (!send && bCanDelete)
- DeleteFile(m_pszFile);
-
- SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CheckOpenAgain);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 <list>
+
+void TfrmMain::Unload()
+{
+ std::list<TfrmMain*> lst;
+ for (auto &it : _HandleMapping)
+ lst.push_back(it.second); // we can't delete inside loop.. not MT compatible
+
+ while (!lst.empty()) {
+ DestroyWindow(lst.front()->m_hWnd); // deletes class
+ lst.pop_front();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR CALLBACK TfrmMain::DlgProc_CaptureTabPage(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ // main message handling is done inside TfrmMain::DlgTfrmMain
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ switch (lParam) {
+ case IDD_UMain_CaptureWindow:
+ Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_TARGET));
+ SetDlgItemText(hDlg, ID_edtCaption, TranslateT("Drag&Drop the target on the desired window."));
+ break;
+ case IDD_UMain_CaptureDesktop:
+ Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MONITOR));
+ break;
+ case IDD_UMain_CaptureFile:
+ Static_SetIcon(GetDlgItem(hDlg, ID_imgTarget), GetIcon(ICO_MAIN));
+ break;
+ }
+ SetFocus(GetDlgItem(hDlg, ID_imgTarget));
+ return FALSE;
+
+ case WM_CTLCOLORDLG:
+ case WM_CTLCOLOREDIT:
+ case WM_CTLCOLORSTATIC:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+
+ case WM_COMMAND:
+ if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_btnExplore) { // local file tab
+ OPENFILENAME ofn = { sizeof(OPENFILENAME) };
+ wchar_t filename[MAX_PATH];
+ GetDlgItemText(hDlg, ID_edtSize, filename, _countof(filename));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hDlg;
+ ofn.lpstrFilter = L"Images\0*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.tif;*.tiff\0";
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = filename;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_READONLY;
+ if (GetOpenFileName(&ofn)) {
+ SetDlgItemText(hDlg, ID_edtSize, filename);
+ }
+ break;
+ }
+ SendMessage(GetParent(hDlg), uMsg, wParam, lParam);
+ break;
+ case WM_NOTIFY:
+ SendMessage(GetParent(hDlg), uMsg, wParam, lParam);
+ break;
+ case WM_DESTROY:
+ break;
+ }
+ return FALSE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+TfrmMain::CHandleMapping TfrmMain::_HandleMapping;
+
+INT_PTR CALLBACK TfrmMain::DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_CTLCOLOREDIT || msg == WM_CTLCOLORSTATIC) {
+ switch (GetWindowLongPtr((HWND)lParam, GWL_ID)) {
+ case IDC_HEADERBAR:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ break;
+ default:
+ return 0;
+ }
+ SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+ return (INT_PTR)GetStockObject(WHITE_BRUSH);
+ }
+
+ CHandleMapping::iterator wnd;
+ if (msg == WM_INITDIALOG) {
+ wnd = _HandleMapping.insert(CHandleMapping::value_type(hWnd, reinterpret_cast<TfrmMain*>(lParam))).first;
+ wnd->second->m_hWnd = hWnd;
+ wnd->second->wmInitdialog(wParam, lParam);
+ return 0;
+ }
+ wnd = _HandleMapping.find(hWnd);
+ if (wnd == _HandleMapping.end())
+ return 0;
+
+ switch (msg) {
+ case WM_DROPFILES:
+ // Drag&Drop of local files
+ {
+ wchar_t filename[MAX_PATH];
+ if (!DragQueryFile((HDROP)wParam, 0, filename, MAX_PATH))
+ *filename = '\0';
+ DragFinish((HDROP)wParam);
+ if (wnd->second->m_hwndTabPage)
+ ShowWindow(wnd->second->m_hwndTabPage, SW_HIDE);
+
+ wnd->second->m_opt_tabCapture = 2; // activate file tab
+ TabCtrl_SetCurSel(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture);
+
+ TAB_INFO itab = { TCIF_PARAM };
+ TabCtrl_GetItem(wnd->second->m_hwndTab, wnd->second->m_opt_tabCapture, &itab);
+ wnd->second->m_hwndTabPage = itab.hwndTabPage;
+
+ ShowWindow(wnd->second->m_hwndTabPage, SW_SHOW);
+ SetDlgItemText(wnd->second->m_hwndTabPage, ID_edtSize, filename);
+ }
+ break;
+ case WM_COMMAND:
+ wnd->second->wmCommand(wParam, lParam);
+ break;
+ case WM_CLOSE:
+ wnd->second->wmClose(wParam, lParam);
+ break;
+ case WM_DESTROY:
+ delete wnd->second;
+ break;
+ case WM_NOTIFY:
+ wnd->second->wmNotify(wParam, lParam);
+ break;
+ case WM_TIMER:
+ wnd->second->wmTimer(wParam, lParam);
+ break;
+ case UM_EVENT:
+ wnd->second->UMevent(wParam, lParam);
+ break;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WM_INITDIALOG:
+
+int EnumCloudFileServices(const CFSERVICEINFO *serviceInfo, void *param)
+{
+ HWND hCtrl = (HWND)param;
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, serviceInfo->userName), new UPLOAD_INFO(SS_CLOUDFILE, (void*)serviceInfo->accountName));
+ return 0;
+}
+
+void TfrmMain::wmInitdialog(WPARAM, LPARAM)
+{
+ HWND hCtrl;
+ // Taskbar and Window icon
+ Window_SetIcon_IcoLib(m_hWnd, GetIconHandle(ICO_MAIN));
+
+ wchar_t *pt = mir_wstrdup(Clist_GetContactDisplayName(m_hContact));
+ if (pt && (m_hContact != 0)) {
+ CMStringW string;
+ string.AppendFormat(TranslateT("Send screenshot to %s"), pt);
+ SetWindowText(m_hWnd, string);
+ }
+ mir_free(pt);
+
+ // Headerbar
+ SendDlgItemMessage(m_hWnd, IDC_HEADERBAR, WM_SETICON, ICON_BIG, (LPARAM)GetIcon(ICO_MAIN));
+
+ // Timed controls
+ CheckDlgButton(m_hWnd, ID_chkTimed, m_opt_chkTimed ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemInt(m_hWnd, ID_edtTimed, (UINT)m_opt_edtTimed, FALSE);
+ SendDlgItemMessage(m_hWnd, ID_upTimed, UDM_SETRANGE, 0, (LPARAM)MAKELONG(250, 1));
+ chkTimedClick(); // enable disable Timed controls
+
+ // create Image list for tab control
+ if (!m_himlTab) {
+ m_himlTab = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 1);
+ ImageList_AddIcon(m_himlTab, GetIcon(ICO_TARGET));
+ ImageList_AddIcon(m_himlTab, GetIcon(ICO_MONITOR));
+ ImageList_AddIcon(m_himlTab, GetIconBtn(ICO_BTN_FOLDER));
+ }
+
+ // create the tab control.
+ {
+ m_hwndTab = GetDlgItem(m_hWnd, IDC_CAPTURETAB);
+ TabCtrl_SetImageList(m_hwndTab, m_himlTab);
+ TabCtrl_SetItemExtra(m_hwndTab, sizeof(TAB_INFO) - sizeof(TCITEMHEADER));
+ RECT rcTab;
+ TAB_INFO itab;
+ itab.hwndMain = m_hWnd;
+ itab.hwndTab = m_hwndTab;
+ itab.tcih.mask = TCIF_PARAM | TCIF_TEXT | TCIF_IMAGE;
+
+ // Add a tab for each of the three child dialog boxes.
+ itab.tcih.pszText = TranslateT("Window");
+ itab.tcih.iImage = 0;
+ itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureWindow), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureWindow);
+ TabCtrl_InsertItem(m_hwndTab, 0, &itab);
+
+ // get tab boundaries (required after 1st tab)
+ GetClientRect(m_hwndTab, &rcTab);
+ MapWindowPoints(m_hwndTab, m_hWnd, (POINT*)&rcTab, 2);
+ TabCtrl_AdjustRect(m_hwndTab, 0, &rcTab);
+ rcTab.bottom -= rcTab.top; rcTab.right -= rcTab.left;
+
+ SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
+ CheckDlgButton(itab.hwndTabPage, ID_chkIndirectCapture, m_opt_chkIndirectCapture ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(itab.hwndTabPage, ID_chkClientArea, m_opt_chkClientArea ? BST_CHECKED : BST_UNCHECKED);
+
+ itab.tcih.pszText = TranslateT("Desktop");
+ itab.tcih.iImage = 1;
+ itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureDesktop), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureDesktop);
+ TabCtrl_InsertItem(m_hwndTab, 1, &itab);
+ SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
+
+ hCtrl = GetDlgItem(itab.hwndTabPage, ID_edtCaption);
+ ComboBox_ResetContent(hCtrl);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Entire Desktop>")), 0);
+ ComboBox_SetCurSel(hCtrl, 0);
+ if (m_MonitorCount > 1) {
+ wchar_t tszTemp[120];
+ for (size_t mon = 0; mon < m_MonitorCount; ++mon) { // @todo : fix format for non MSVC compilers
+ mir_snwprintf(tszTemp, L"%Iu. %s%s",
+ mon + 1, TranslateT("Monitor"),
+ (m_Monitors[mon].dwFlags & MONITORINFOF_PRIMARY) ? TranslateT(" (primary)") : L""
+ );
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, tszTemp), mon + 1);
+ }
+ ComboBox_SelectItem(hCtrl, m_opt_cboxDesktop);
+ }
+ PostMessage(m_hWnd, WM_COMMAND, MAKEWPARAM(ID_edtCaption, CBN_SELCHANGE), (LPARAM)hCtrl);
+
+ itab.tcih.pszText = TranslateT("File");
+ itab.tcih.iImage = 2;
+ itab.hwndTabPage = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMain_CaptureFile), m_hWnd, DlgProc_CaptureTabPage, IDD_UMain_CaptureFile);
+ TabCtrl_InsertItem(m_hwndTab, 2, &itab);
+ SetWindowPos(itab.hwndTabPage, HWND_TOP, rcTab.left, rcTab.top, rcTab.right, rcTab.bottom, 0);
+
+ // select tab and set m_hwndTabPage
+ TabCtrl_SetCurSel(m_hwndTab, m_opt_tabCapture);
+ itab.tcih.mask = TCIF_PARAM;
+ TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab);
+ m_hwndTabPage = itab.hwndTabPage;
+ ShowWindow(m_hwndTabPage, SW_SHOW);
+
+ // enable Drag&Drop for local file pane
+ typedef BOOL(WINAPI *ChangeWindowMessageFilterEx_t)(HWND hwnd, UINT message, uint32_t action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
+ ChangeWindowMessageFilterEx_t pChangeWindowMessageFilterEx;
+ pChangeWindowMessageFilterEx = (ChangeWindowMessageFilterEx_t)GetProcAddress(GetModuleHandleA("user32"), "ChangeWindowMessageFilterEx");
+ if (pChangeWindowMessageFilterEx) { // Win7+, UAC fix
+ pChangeWindowMessageFilterEx(m_hWnd, WM_DROPFILES, MSGFLT_ALLOW, nullptr);
+ pChangeWindowMessageFilterEx(m_hWnd, WM_COPYDATA, MSGFLT_ALLOW, nullptr);
+ pChangeWindowMessageFilterEx(m_hWnd, 0x0049/*WM_COPYGLOBALDATA*/, MSGFLT_ALLOW, nullptr);
+ }
+ DragAcceptFiles(m_hWnd, 1);
+ }
+
+ // init Format combo box
+ {
+ hCtrl = GetDlgItem(m_hWnd, ID_cboxFormat);
+ ComboBox_ResetContent(hCtrl);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"PNG"), 0);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"JPG"), 1);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"BMP"), 2);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"TIF"), 3);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"GIF"), 4);
+ ComboBox_SelectItem(hCtrl, m_opt_cboxFormat);
+ }
+
+ // init SendBy combo box
+ UPLOAD_INFO *pDefault = nullptr;
+ {
+ hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy);
+ ComboBox_ResetContent(hCtrl);
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("<Only save>")), new UPLOAD_INFO(SS_JUSTSAVE));
+ if (m_hContact) {
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("File Transfer")), new UPLOAD_INFO(SS_FILESEND));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("E-mail")), new UPLOAD_INFO(SS_EMAIL));
+ if (g_myGlobals.PluginHTTPExist) {
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"HTTP Server"), new UPLOAD_INFO(SS_HTTPSERVER));
+ }
+ else if (m_opt_cboxSendBy == SS_HTTPSERVER) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ if (g_myGlobals.PluginFTPExist) {
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("FTP File")), new UPLOAD_INFO(SS_FTPFILE));
+ }
+ else if (m_opt_cboxSendBy == SS_FTPFILE) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ }
+ else if (m_opt_cboxSendBy == SS_FILESEND || m_opt_cboxSendBy == SS_EMAIL || m_opt_cboxSendBy == SS_HTTPSERVER || m_opt_cboxSendBy == SS_FTPFILE) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ if (g_myGlobals.PluginCloudFileExist) {
+ CallService(MS_CLOUDFILE_ENUMSERVICES, (WPARAM)EnumCloudFileServices, (LPARAM)hCtrl);
+ }
+ else if (m_opt_cboxSendBy == SS_CLOUDFILE) {
+ m_opt_cboxSendBy = SS_IMAGESHACK;
+ }
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"ImageShack"), new UPLOAD_INFO(SS_IMAGESHACK));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (30m)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)1));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1d)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)4));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, TranslateT("Upload Pie (1w)")), new UPLOAD_INFO(SS_UPLOADPIE, (void*)5));
+ ComboBox_SetItemData(hCtrl, ComboBox_AddString(hCtrl, L"Imgur"), new UPLOAD_INFO(SS_IMGUR));
+
+ for (int i = 0; i < ComboBox_GetCount(hCtrl); i++) {
+ UPLOAD_INFO *p = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i);
+ if (p && p->sendBy == m_opt_cboxSendBy) {
+ pDefault = p;
+ ComboBox_SetCurSel(hCtrl, i);
+ break;
+ }
+ }
+ }
+
+ // init footer options
+ CheckDlgButton(m_hWnd, ID_chkOpenAgain, m_opt_chkOpenAgain ? BST_CHECKED : BST_UNCHECKED);
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_btnExplore)) {
+ SendDlgItemMessage(m_hWnd, ID_btnExplore, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open Folder"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(ICO_BTN_FOLDER);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"...");
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_chkDesc)) {
+ SendDlgItemMessage(m_hWnd, ID_chkDesc, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Fill description textbox."), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"D");
+ SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDesc ? BST_CHECKED : BST_UNCHECKED, NULL);
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_chkDeleteAfterSend)) {
+ SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete after send"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"X");
+ SendMessage(hCtrl, BM_SETCHECK, m_opt_btnDeleteAfterSend ? BST_CHECKED : BST_UNCHECKED, NULL);
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_chkEditor)) {
+ SendDlgItemMessage(m_hWnd, ID_chkEditor, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Open editor before sending"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, hIcon ? L"" : L"E");
+ SendMessage(hCtrl, BM_SETCHECK, m_opt_chkEditor ? BST_CHECKED : BST_UNCHECKED, NULL);
+ }
+
+ if (hCtrl = GetDlgItem(m_hWnd, ID_btnCapture)) {
+ SendMessage(hCtrl, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Capture"), MBBF_TCHAR);
+ HICON hIcon = GetIconBtn(ICO_BTN_OK);
+ SendMessage(hCtrl, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ SetWindowText(hCtrl, TranslateT("&Capture"));
+ SendMessage(hCtrl, BUTTONSETDEFAULT, 1, NULL);
+ }
+ cboxSendByChange((pDefault) ? pDefault->param : nullptr); // enable disable controls
+
+ TranslateDialogDefault(m_hWnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// WM_COMMAND:
+
+void TfrmMain::wmCommand(WPARAM wParam, LPARAM lParam)
+{
+ HICON hIcon;
+
+ int IDControl = LOWORD(wParam);
+ switch (HIWORD(wParam)) {
+ case BN_CLICKED: // Button controls
+ switch (IDControl) {
+ case IDCANCEL: // ESC pressed
+ this->Close();
+ break;
+ case ID_chkTimed:
+ m_opt_chkTimed = (uint8_t)Button_GetCheck((HWND)lParam);
+ TfrmMain::chkTimedClick();
+ break;
+ case ID_chkIndirectCapture:
+ m_opt_chkIndirectCapture = (uint8_t)Button_GetCheck((HWND)lParam);
+ break;
+ case ID_chkClientArea:
+ m_opt_chkClientArea = (uint8_t)Button_GetCheck((HWND)lParam);
+ if (m_hTargetWindow)
+ edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, GetParent((HWND)lParam), ID_edtSize);
+ break;
+ case ID_imgTarget:
+ if (m_opt_tabCapture != 0) break;
+ m_hLastWin = nullptr;
+ SetTimer(m_hWnd, ID_imgTarget, BUTTON_POLLDELAY, nullptr);
+ break;
+ case ID_btnExplore:
+ TfrmMain::btnExploreClick();
+ break;
+ case ID_chkDesc:
+ m_opt_btnDesc = !m_opt_btnDesc;
+ hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
+ SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ break;
+ case ID_chkDeleteAfterSend:
+ m_opt_btnDeleteAfterSend = !m_opt_btnDeleteAfterSend;
+ hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
+ SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ if (m_cSend) m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend;
+ break;
+ case ID_chkEditor:
+ m_opt_chkEditor = !m_opt_chkEditor;
+ hIcon = GetIconBtn(m_opt_chkEditor ? ICO_BTN_EDITON : ICO_BTN_EDIT);
+ SendMessage((HWND)lParam, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ break;
+ case ID_chkOpenAgain:
+ m_opt_chkOpenAgain = Button_GetCheck((HWND)lParam);
+ break;
+ case ID_btnCapture:
+ TfrmMain::btnCaptureClick();
+ break;
+ }
+ break;
+
+ case CBN_SELCHANGE: // ComboBox controls
+ switch (IDControl) { // lParam = Handle to the control
+ case ID_cboxFormat: // not finish
+ m_opt_cboxFormat = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ break;
+ case ID_cboxSendBy:
+ {
+ UPLOAD_INFO *upload = (UPLOAD_INFO*)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ m_opt_cboxSendBy = upload->sendBy;
+ cboxSendByChange(upload->param);
+ }
+ break;
+
+ case ID_edtCaption: // cboxDesktopChange
+ m_opt_cboxDesktop = (uint8_t)ComboBox_GetItemData((HWND)lParam, ComboBox_GetCurSel((HWND)lParam));
+ m_hTargetWindow = nullptr;
+ if (m_opt_cboxDesktop > 0) {
+ edtSizeUpdate(m_Monitors[m_opt_cboxDesktop - 1].rcMonitor, GetParent((HWND)lParam), ID_edtSize);
+ }
+ else {
+ edtSizeUpdate(m_VirtualScreen, GetParent((HWND)lParam), ID_edtSize);
+ }
+ break;
+ }
+ break;
+
+ case EN_CHANGE: // Edit controls
+ switch (IDControl) { // lParam = Handle to the control
+ case ID_edtQuality:
+ m_opt_edtQuality = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtQuality, nullptr, FALSE);
+ break;
+ case ID_edtTimed:
+ m_opt_edtTimed = (uint8_t)GetDlgItemInt(m_hWnd, ID_edtTimed, nullptr, FALSE);
+ break;
+ }
+ break;
+ }
+}
+
+// WM_CLOSE:
+void TfrmMain::wmClose(WPARAM, LPARAM)
+{
+ HWND hCtrl = GetDlgItem(m_hWnd, ID_cboxSendBy);
+ size_t count = ComboBox_GetCount(hCtrl);
+ for (size_t i = 0; i < count; i++) {
+ UPLOAD_INFO *ui = (UPLOAD_INFO*)ComboBox_GetItemData(hCtrl, i);
+ delete ui;
+ }
+ DestroyWindow(m_hWnd);
+ return;
+}
+
+// WM_TIMER:
+const int g_iTargetBorder = 7;
+void TfrmMain::SetTargetWindow(HWND hwnd)
+{
+ if (!hwnd) {
+ POINT point; GetCursorPos(&point);
+ hwnd = WindowFromPoint(point);
+ for (HWND hTMP; (hTMP = GetParent(hwnd)); hwnd = hTMP)
+ ;
+ }
+ m_hTargetWindow = hwnd;
+ int len = GetWindowTextLength(m_hTargetWindow) + 1;
+ wchar_t *lpTitle;
+ if (len > 1) {
+ lpTitle = (wchar_t*)mir_alloc(len*sizeof(wchar_t));
+ GetWindowText(m_hTargetWindow, lpTitle, len);
+ }
+ else { // no WindowText present, use WindowClass
+ lpTitle = (wchar_t*)mir_alloc(64 * sizeof(wchar_t));
+ RealGetWindowClass(m_hTargetWindow, lpTitle, 64);
+ }
+ SetDlgItemText(m_hwndTabPage, ID_edtCaption, lpTitle);
+ mir_free(lpTitle);
+ edtSizeUpdate(m_hTargetWindow, m_opt_chkClientArea, m_hwndTabPage, ID_edtSize);
+}
+
+void TfrmMain::wmTimer(WPARAM wParam, LPARAM)
+{
+ if (wParam == ID_imgTarget) { // Timer for Target selector
+ static int primarymouse;
+ if (!m_hTargetHighlighter) {
+ primarymouse = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
+ m_hTargetHighlighter = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, (wchar_t*)g_clsTargetHighlighter, nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, g_plugin.getInst(), nullptr);
+ if (!m_hTargetHighlighter) return;
+ SetLayeredWindowAttributes(m_hTargetHighlighter, 0, 123, LWA_ALPHA);
+ SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_IBEAM); // text cursor
+ SetSystemCursor(CopyCursor(GetIcon(ICO_TARGET)), OCR_NORMAL);
+ SetActiveWindow(m_hTargetHighlighter); // activate highlighter to fix focus problems with UAC (unelevated GetAsyncKeyState() fails if an elevated app got focus)
+ Hide();
+ }
+ if (!(GetAsyncKeyState(primarymouse) & 0x8000)) {
+ KillTimer(m_hWnd, ID_imgTarget);
+ SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0);
+ DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr;
+ SetTargetWindow(m_hLastWin);
+ Show();
+ return;
+ }
+ POINT point; GetCursorPos(&point);
+ HWND hwnd = WindowFromPoint(point);
+ if (!((GetAsyncKeyState(VK_SHIFT) | GetAsyncKeyState(VK_MENU)) & 0x8000))
+ for (HWND hTMP; (hTMP = GetAncestor(hwnd, GA_PARENT)) && IsChild(hTMP, hwnd); hwnd = hTMP);
+ else {
+ ScreenToClient(hwnd, &point);
+ HWND hTMP; if ((hTMP = RealChildWindowFromPoint(hwnd, point)))
+ hwnd = hTMP;
+ }
+ if (hwnd != m_hLastWin) {
+ m_hLastWin = hwnd;
+ RECT rect;
+ if (m_opt_chkClientArea) {
+ GetClientRect(hwnd, &rect);
+ ClientToScreen(hwnd, (POINT*)&rect);
+ rect.right = rect.left + rect.right;
+ rect.bottom = rect.top + rect.bottom;
+ }
+ else
+ GetWindowRect(hwnd, &rect);
+ int width = rect.right - rect.left;
+ int height = rect.bottom - rect.top;
+ if (g_iTargetBorder) {
+ SetWindowPos(m_hTargetHighlighter, nullptr, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE);
+ if (width > g_iTargetBorder * 2 && height > g_iTargetBorder * 2) {
+ HRGN hRegnNew = CreateRectRgn(0, 0, width, height);
+ HRGN hRgnHole = CreateRectRgn(g_iTargetBorder, g_iTargetBorder, width - g_iTargetBorder, height - g_iTargetBorder);
+ CombineRgn(hRegnNew, hRegnNew, hRgnHole, RGN_XOR);
+ DeleteObject(hRgnHole);
+ SetWindowRgn(m_hTargetHighlighter, hRegnNew, FALSE); // cleans up hRegnNew
+ }
+ else SetWindowRgn(m_hTargetHighlighter, nullptr, FALSE);
+ }
+ SetWindowPos(m_hTargetHighlighter, HWND_TOPMOST, rect.left, rect.top, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
+ }
+ return;
+ }
+ if (wParam == ID_chkTimed) { // Timer for Screenshot
+#ifdef _DEBUG
+ OutputDebugStringA("SS Bitmap Timer Start\r\n");
+#endif
+ if (!m_bCapture) { // only start once
+ if (m_Screenshot) {
+ FreeImage_Unload(m_Screenshot);
+ m_Screenshot = nullptr;
+ }
+ m_bCapture = true;
+ switch (m_opt_tabCapture) {
+ case 0:
+ m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture);
+ break;
+ case 1:
+ m_Screenshot = CaptureMonitor((m_opt_cboxDesktop > 0) ? m_Monitors[m_opt_cboxDesktop - 1].szDevice : nullptr);
+ break;
+ case 2: // edge case, existing local file
+ break;
+#ifdef _DEBUG
+ default:
+ OutputDebugStringA("SS Bitmap Timer Stop (no tabCapture)\r\n");
+#endif
+ }
+ m_bCapture = false;
+ if (m_Screenshot || m_opt_tabCapture == 2) { // @note : test without "if"
+ KillTimer(m_hWnd, ID_chkTimed);
+#ifdef _DEBUG
+ OutputDebugStringA("SS Bitmap Timer Stop (CaptureDone)\r\n");
+#endif
+ SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone);
+ }
+ }
+ }
+}
+
+// WM_NOTIFY:
+void TfrmMain::wmNotify(WPARAM, LPARAM lParam)
+{
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_CAPTURETAB:
+ // HWND hwndFrom; = member is handle to the tab control
+ // UINT_PTR idFrom; = member is the child window identifier of the tab control.
+ // UINT code; = member is TCN_SELCHANGE
+ switch (((LPNMHDR)lParam)->code) {
+ case TCN_SELCHANGING:
+ if (m_hwndTabPage) {
+ ShowWindow(m_hwndTabPage, SW_HIDE);
+ m_hwndTabPage = nullptr;
+ }
+ break;
+
+ case TCN_SELCHANGE:
+ {
+ TAB_INFO itab = { TCIF_PARAM };
+ m_opt_tabCapture = TabCtrl_GetCurSel(m_hwndTab);
+ TabCtrl_GetItem(m_hwndTab, m_opt_tabCapture, &itab);
+ m_hwndTabPage = itab.hwndTabPage;
+ }
+ ShowWindow(m_hwndTabPage, SW_SHOW);
+ break;
+ }
+ break;
+ }
+}
+
+// UM_EVENT:
+void TfrmMain::UMevent(WPARAM, LPARAM lParam)
+{
+ // HWND hWnd = (HWND)wParam;
+ switch (lParam) {
+ case EVT_CaptureDone:
+ if (!m_Screenshot && m_opt_tabCapture != 2) {
+ wchar_t *err = TranslateT("Couldn't take a screenshot");
+ MessageBox(nullptr, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
+ Show();
+ return;
+ }
+ FormClose();
+ break;
+
+ case EVT_SendFileDone:
+ break;
+
+ case EVT_CheckOpenAgain:
+ if (m_opt_chkOpenAgain) {
+ if (m_Screenshot) {
+ FreeImage_Unload(m_Screenshot);
+ m_Screenshot = nullptr;
+ }
+ Show();
+ }
+ else {// Saving Options and close
+ SaveOptions();
+ Close();
+ }
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Standard konstruktor/destruktor
+
+TfrmMain::TfrmMain()
+{
+ /* m_opt_XXX */
+ m_bOnExitSave = TRUE;
+
+ m_hWnd = nullptr;
+ m_hContact = NULL;
+ m_hTargetWindow = m_hLastWin = nullptr;
+ m_hTargetHighlighter = nullptr;
+ m_FDestFolder = m_pszFile = nullptr;
+ m_Screenshot = nullptr;
+ /* m_AlphaColor */
+ m_cSend = nullptr;
+
+ m_Monitors = nullptr;
+ m_MonitorCount = MonitorInfoEnum(m_Monitors, m_VirtualScreen);
+ /* m_opt_XXX */ LoadOptions();
+ m_bCapture = false;
+ /* m_hwndTab,m_hwndTabPage */
+ m_himlTab = nullptr;
+}
+
+TfrmMain::~TfrmMain()
+{
+ _HandleMapping.erase(m_hWnd);
+ mir_free(m_pszFile);
+ mir_free(m_FDestFolder);
+ mir_free(m_Monitors);
+ if (m_Screenshot) FreeImage_Unload(m_Screenshot);
+ if (m_cSend) delete m_cSend;
+ if (m_hTargetHighlighter) {
+ DestroyWindow(m_hTargetHighlighter), m_hTargetHighlighter = nullptr;
+ SystemParametersInfo(SPI_SETCURSORS, 0, nullptr, 0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load / Saving options from miranda's database
+
+void TfrmMain::LoadOptions(void)
+{
+ uint32_t rgb = g_plugin.getDword("AlphaColor", 16777215);
+ m_AlphaColor.rgbRed = GetRValue(rgb);
+ m_AlphaColor.rgbGreen = GetGValue(rgb);
+ m_AlphaColor.rgbBlue = GetBValue(rgb);
+ m_AlphaColor.rgbReserved = 0;
+
+ m_opt_edtQuality = g_plugin.getByte("JpegQuality", 75);
+
+ m_opt_tabCapture = g_plugin.getByte("Capture", 0);
+ m_opt_chkIndirectCapture = g_plugin.getByte("IndirectCapture", 0);
+ m_opt_chkClientArea = g_plugin.getByte("ClientArea", 0);
+ m_opt_cboxDesktop = g_plugin.getByte("Desktop", 0);
+
+ m_opt_chkTimed = g_plugin.getByte("TimedCap", 0);
+ m_opt_edtTimed = g_plugin.getByte("CapTime", 3);
+ m_opt_cboxFormat = g_plugin.getByte("OutputFormat", 0);
+ m_opt_cboxSendBy = g_plugin.getByte("SendBy", 0);
+
+ m_opt_btnDesc = g_plugin.getByte("AutoDescription", 1);
+ m_opt_btnDeleteAfterSend = g_plugin.getByte("DelAfterSend", 1) != 0;
+ m_opt_chkEditor = g_plugin.getByte("Preview", 0);
+ m_opt_chkOpenAgain = g_plugin.getByte("OpenAgain", 0);
+}
+
+void TfrmMain::SaveOptions(void)
+{
+ if (m_bOnExitSave) {
+ g_plugin.setDword("AlphaColor",
+ (uint32_t)RGB(m_AlphaColor.rgbRed, m_AlphaColor.rgbGreen, m_AlphaColor.rgbBlue));
+
+ g_plugin.setByte("JpegQuality", m_opt_edtQuality);
+
+ g_plugin.setByte("Capture", m_opt_tabCapture);
+ g_plugin.setByte("IndirectCapture", m_opt_chkIndirectCapture);
+ g_plugin.setByte("ClientArea", m_opt_chkClientArea);
+ g_plugin.setByte("Desktop", m_opt_cboxDesktop);
+
+ g_plugin.setByte("TimedCap", m_opt_chkTimed);
+ g_plugin.setByte("CapTime", m_opt_edtTimed);
+ g_plugin.setByte("OutputFormat", m_opt_cboxFormat);
+ g_plugin.setByte("SendBy", m_opt_cboxSendBy);
+
+ g_plugin.setByte("AutoDescription", m_opt_btnDesc);
+ g_plugin.setByte("DelAfterSend", m_opt_btnDeleteAfterSend);
+ g_plugin.setByte("Preview", m_opt_chkEditor);
+ g_plugin.setByte("OpenAgain", m_opt_chkOpenAgain);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::Init(wchar_t *DestFolder, MCONTACT Contact)
+{
+ m_FDestFolder = mir_wstrdup(DestFolder);
+ m_hContact = Contact;
+
+ // create window
+ m_hWnd = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_UMainForm), nullptr, DlgTfrmMain, (LPARAM)this);
+
+ // register object
+ _HandleMapping.insert(CHandleMapping::value_type(m_hWnd, this));
+
+ // check Contact
+ if (m_cSend)
+ m_cSend->SetContact(Contact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::btnCaptureClick()
+{
+ if (m_opt_tabCapture == 1) m_hTargetWindow = GetDesktopWindow();
+ else if (m_opt_tabCapture == 2) {
+ wchar_t filename[MAX_PATH];
+ GetDlgItemText(m_hwndTabPage, ID_edtSize, filename, _countof(filename));
+ FILE *fp = _wfopen(filename, L"rb");
+ if (!fp) {
+ wchar_t *err = TranslateT("Select a file");
+ MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
+ return;
+ }
+ fclose(fp);
+ mir_free(m_pszFile); m_pszFile = mir_wstrdup(filename);
+ }
+ else if (!m_hTargetWindow) {
+ wchar_t *err = TranslateT("Select a target window.");
+ MessageBox(m_hWnd, err, ERROR_TITLE, MB_OK | MB_ICONWARNING);
+ return;
+ }
+ TfrmMain::Hide();
+
+ if (m_opt_chkTimed) {
+ SetTimer(m_hWnd, ID_chkTimed, m_opt_edtTimed ? m_opt_edtTimed * 1000 : 500, nullptr); // calls EVT_CaptureDone
+ return;
+ }
+ if (m_opt_tabCapture == 1) { // desktop needs always time to update from TfrmMain::Hide()
+ SetTimer(m_hWnd, ID_chkTimed, 500, nullptr); // calls EVT_CaptureDone
+ return;
+ }
+ if (m_opt_tabCapture != 2) {
+ m_Screenshot = CaptureWindow(m_hTargetWindow, m_opt_chkClientArea, m_opt_chkIndirectCapture);
+ }
+ SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CaptureDone);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::chkTimedClick()
+{
+ Button_Enable(GetDlgItem(m_hWnd, ID_edtTimedLabel), (BOOL)m_opt_chkTimed);
+ Button_Enable(GetDlgItem(m_hWnd, ID_edtTimed), (BOOL)m_opt_chkTimed);
+ Button_Enable(GetDlgItem(m_hWnd, ID_upTimed), (BOOL)m_opt_chkTimed);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::cboxSendByChange(void *param)
+{
+ BOOL bState;
+ HICON hIcon;
+ uint8_t itemFlag = SS_DLG_DESCRIPTION;
+ if (m_cSend)
+ delete m_cSend;
+ switch (m_opt_cboxSendBy) {
+ case SS_FILESEND: // "File Transfer"
+ m_cSend = new CSendFile(m_hWnd, m_hContact, true);
+ break;
+ case SS_EMAIL: // "E-mail"
+ m_cSend = new CSendEmail(m_hWnd, m_hContact, true);
+ break;
+ case SS_HTTPSERVER: // "HTTP Server"
+ m_cSend = new CSendHTTPServer(m_hWnd, m_hContact, true);
+ break;
+ case SS_FTPFILE: // "FTP File"
+ m_cSend = new CSendFTPFile(m_hWnd, m_hContact, true);
+ break;
+ case SS_CLOUDFILE: // "CloudFile"
+ m_cSend = new CSendCloudFile(m_hWnd, m_hContact, false, (char*)param);
+ break;
+ case SS_IMAGESHACK: // "ImageShack"
+ m_cSend = new CSendHost_ImageShack(m_hWnd, m_hContact, true);
+ break;
+ case SS_UPLOADPIE: // "Upload Pie"
+ m_cSend = new CSendHost_UploadPie(m_hWnd, m_hContact, true, (INT_PTR)param);
+ break;
+ case SS_IMGUR:
+ m_cSend = new CSendHost_Imgur(m_hWnd, m_hContact, true);
+ break;
+ default:
+ m_cSend = nullptr;
+ break;
+ }
+ if (m_cSend) {
+ itemFlag = m_cSend->GetEnableItem();
+ m_cSend->m_bDeleteAfterSend = m_opt_btnDeleteAfterSend;
+ }
+ bState = (itemFlag & SS_DLG_DELETEAFTERSSEND);
+ hIcon = GetIconBtn(m_opt_btnDeleteAfterSend ? ICO_BTN_DELON : ICO_BTN_DEL);
+ SendDlgItemMessage(m_hWnd, ID_chkDeleteAfterSend, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr));
+ Button_Enable(GetDlgItem(m_hWnd, ID_chkDeleteAfterSend), bState);
+
+ bState = (itemFlag & SS_DLG_DESCRIPTION);
+ hIcon = GetIconBtn(m_opt_btnDesc ? ICO_BTN_DESCON : ICO_BTN_DESC);
+ SendDlgItemMessage(m_hWnd, ID_chkDesc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(bState ? hIcon : nullptr));
+ Button_Enable(GetDlgItem(m_hWnd, ID_chkDesc), bState);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::btnExploreClick()
+{
+ if (m_FDestFolder)
+ ShellExecute(nullptr, L"explore", m_FDestFolder, nullptr, nullptr, SW_SHOW);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl)
+{
+ // get window dimensions
+ RECT rect = { 0 };
+ RECT cliRect = { 0 };
+ wchar_t B[33], H[16];
+ GetWindowRect(hWnd, &rect);
+ if (ClientArea) {
+ POINT pt = { 0 };
+ GetClientRect(hWnd, &cliRect);
+ pt.x = cliRect.left;
+ pt.y = cliRect.top;
+ ClientToScreen(hWnd, &pt);
+ pt.x = pt.x - rect.left; // offset x for client area
+ pt.y = pt.y - rect.top; // offset y for client area
+ rect = cliRect;
+ }
+
+ _itow(rect.right - rect.left, B, 10);
+ _itow(rect.bottom - rect.top, H, 10);
+ mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B));
+ mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B));
+ SetDlgItemText(hTarget, Ctrl, B);
+}
+
+void TfrmMain::edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl)
+{
+ wchar_t B[33], H[16];
+ _itow(ABS(rect.right - rect.left), B, 10);
+ _itow(ABS(rect.bottom - rect.top), H, 10);
+ mir_wstrncat(B, L"x", _countof(B) - mir_wstrlen(B));
+ mir_wstrncat(B, H, _countof(B) - mir_wstrlen(B));
+ SetDlgItemText(hTarget, Ctrl, B);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR TfrmMain::SaveScreenshot(FIBITMAP *dib)
+{
+ if (!dib)
+ return 1;
+
+ // generate file name
+ unsigned FileNumber = g_plugin.getDword("FileNumber", 0) + 1;
+ if (FileNumber > 99999)
+ FileNumber = 1;
+
+ CMStringW wszFileName(m_FDestFolder);
+ if (wszFileName.Right(1) != L"\\")
+ wszFileName.Append(L"\\");
+ wszFileName.AppendFormat(L"shot%.5u", FileNumber);
+
+ // generate a description according to the screenshot
+ wchar_t winText[1024];
+ GetDlgItemText(m_hwndTabPage, ID_edtCaption, winText, _countof(winText));
+
+ CMStringW wszFileDesc;
+ if (m_opt_tabCapture)
+ wszFileDesc.Format(TranslateT("Screenshot of \"%s\""), winText);
+ else {
+ if (m_opt_chkClientArea)
+ wszFileDesc.Format(TranslateT("Screenshot for client area of \"%s\" window"), winText);
+ else
+ wszFileDesc.Format(TranslateT("Screenshot of \"%s\" window"), winText);
+ }
+
+ // convert to 32Bits (make sure it is 32bit)
+ FIBITMAP *dib_new = FreeImage_ConvertTo32Bits(dib);
+ FreeImage_SetTransparent(dib_new, TRUE);
+
+ BOOL ret = FALSE;
+ HWND hwndCombo = GetDlgItem(m_hWnd, ID_cboxFormat);
+ switch (ComboBox_GetItemData(hwndCombo, ComboBox_GetCurSel(hwndCombo))) {
+ case 0: // PNG
+ wszFileName.Append(L".png");
+ ret = FreeImage_SaveU(FIF_PNG, dib_new, wszFileName, 0);
+ break;
+
+ case 1: // JPG
+ wszFileName.Append(L".jpg");
+ {
+ FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
+ FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
+ FreeImage_Unload(dib32);
+ ret = FreeImage_SaveU(FIF_JPEG, dib24, wszFileName, 0);
+ FreeImage_Unload(dib24);
+ }
+ break;
+
+ case 2: // BMP
+ wszFileName.Append(L".bmp");
+ {
+ FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
+ FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
+ FreeImage_Unload(dib32);
+ ret = FreeImage_SaveU(FIF_BMP, dib24, wszFileName, 0);
+ FreeImage_Unload(dib24);
+ }
+ break;
+
+ case 3: // TIFF (miranda freeimage interface do not support save tiff, we udse GDI+)
+ wszFileName.Append(L".tif");
+ {
+ FIBITMAP *dib32 = FreeImage_Composite(dib_new, FALSE, &m_AlphaColor, nullptr);
+ FIBITMAP *dib24 = FreeImage_ConvertTo24Bits(dib32);
+ FreeImage_Unload(dib32);
+
+ HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib24);
+ FreeImage_Unload(dib24);
+ SaveTIF(hBmp, wszFileName);
+ DeleteObject(hBmp);
+ }
+ ret = TRUE;
+ break;
+
+ case 4: // GIF
+ wszFileName.Append(L".gif");
+ {
+ HBITMAP hBmp = FreeImage_CreateHBITMAPFromDIB(dib_new);
+ SaveGIF(hBmp, wszFileName);
+ DeleteObject(hBmp);
+ }
+ ret = TRUE;
+ break;
+ }
+
+ FreeImage_Unload(dib_new);
+
+ if (!ret)
+ return 1;
+
+ g_plugin.setDword("FileNumber", FileNumber);
+ replaceStrW(m_pszFile, wszFileName);
+
+ if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc)
+ wszFileDesc.Empty();
+
+ if (m_cSend) {
+ m_cSend->SetFile(m_pszFile);
+ m_cSend->SetDescription(wszFileDesc);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void TfrmMain::FormClose()
+{
+ bool bCanDelete = m_opt_btnDeleteAfterSend;
+ if (m_opt_tabCapture == 2) { // existing file
+ wchar_t description[1024];
+ GetDlgItemText(m_hwndTabPage, ID_edtCaption, description, _countof(description));
+ if (!IsWindowEnabled(GetDlgItem(m_hWnd, ID_chkDesc)) || !m_opt_btnDesc)
+ *description = '\0';
+ if (m_cSend) {
+ m_cSend->m_bDeleteAfterSend = false; // well... guess it's better to not delete existing files for now...
+ m_cSend->SetFile(m_pszFile);
+ m_cSend->SetDescription(description);
+ }
+ bCanDelete = false;
+ }
+ else if (SaveScreenshot(m_Screenshot)) { // Saving the screenshot
+ Show(); // Error from SaveScreenshot
+ return;
+ }
+
+ bool send = true;
+ if (m_opt_chkEditor) {
+ SHELLEXECUTEINFO shex = { sizeof(SHELLEXECUTEINFO) };
+ shex.fMask = SEE_MASK_NOCLOSEPROCESS;
+ shex.lpVerb = L"edit";
+ shex.lpFile = m_pszFile;
+ shex.nShow = SW_SHOWNORMAL;
+ ShellExecuteEx(&shex);
+ if (shex.hProcess) {
+ uint32_t res;
+ MSG msg;
+ do {
+ // wait for editor exit or messages/input
+ res = MsgWaitForMultipleObjects(1, &shex.hProcess, 0, INFINITE, QS_ALLINPUT);
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT) {
+ res = WAIT_OBJECT_0;
+ PostMessage(nullptr, WM_QUIT, 0, 0); // forward for outer message loops
+ break;
+ }
+
+ // process dialog messages (of unknown dialogs)
+ HWND hwndDlgModeless = GetActiveWindow();
+ if (hwndDlgModeless != nullptr && IsDialogMessage(hwndDlgModeless, &msg)) /* Wine fix. */
+ continue;
+
+ // process messages
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ } while (res == WAIT_OBJECT_0 + 1);
+ CloseHandle(shex.hProcess);
+ }
+ if (MessageBox(m_hWnd, TranslateT("Send screenshot?"), L"SendSS", MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL) != IDYES)
+ send = false;
+ }
+
+ if (send && m_cSend && m_pszFile) {
+ if (!m_cSend->Send()) // not sent now, class deletes itself later
+ m_cSend = nullptr;
+ cboxSendByChange(nullptr);
+ }
+ else if (!send && bCanDelete)
+ DeleteFile(m_pszFile);
+
+ SendMessage(m_hWnd, UM_EVENT, 0, (LPARAM)EVT_CheckOpenAgain);
+}
diff --git a/plugins/SendScreenshotPlus/src/UMainForm.h b/plugins/SendScreenshotPlus/src/UMainForm.h
index 55b75e2373..fbe8a6a50b 100644
--- a/plugins/SendScreenshotPlus/src/UMainForm.h
+++ b/plugins/SendScreenshotPlus/src/UMainForm.h
@@ -1,146 +1,146 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef UMainFormH
-#define UMainFormH
-
-#define SS_JUSTSAVE 0
-#define SS_FILESEND 1
-#define SS_EMAIL 2
-#define SS_HTTPSERVER 3
-#define SS_FTPFILE 4
-#define SS_CLOUDFILE 5
-#define SS_IMAGESHACK 6
-#define SS_UPLOADPIE 7
-#define SS_IMGUR 8
-
-struct UPLOAD_INFO
-{
- uint8_t sendBy; //SS_*
- void *param;
-
- UPLOAD_INFO(uint8_t sb) : sendBy(sb), param(nullptr) { }
- UPLOAD_INFO(uint8_t sb, void *p) : sendBy(sb), param(p) { }
-};
-
-// Used for our own cheap TrackMouseEvent
-#define BUTTON_POLLDELAY 50
-
-// User Events
-#define EVT_CaptureDone 1
-#define EVT_SendFileDone 2
-#define EVT_CheckOpenAgain 3
-
-struct TAB_INFO
-{
- TCITEMHEADER tcih;
- HWND hwndMain; // main window
- HWND hwndTab; // tab control
- HWND hwndTabPage; // current child dialog box
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-class TfrmMain
-{
-
-public:
- // Deklaration Standardkonstruktor/Standarddestructor
- TfrmMain();
- ~TfrmMain();
-
- uint8_t m_opt_tabCapture; // capture tab page
- uint8_t m_opt_cboxDesktop; // TRadioButton *rbtnDesktop;
- uint8_t m_opt_chkTimed; // TCheckBox *chkTimed;
- uint8_t m_opt_cboxSendBy; // TComboBox *cboxSendBy;
- uint8_t m_opt_btnDesc; // TCheckBox *chkDesc;
- uint8_t m_opt_chkEditor; // TCheckBox *chkEditor;
- bool m_bOnExitSave;
-
- static void Unload();
- void Init(wchar_t* DestFolder, MCONTACT Contact);
- void Close(){ SendMessage(m_hWnd, WM_CLOSE, 0, 0); }
- void Show(){ ShowWindow(m_hWnd, SW_SHOW); }
- void Hide(){ ShowWindow(m_hWnd, SW_HIDE); }
- void SetTargetWindow(HWND hwnd = nullptr);
- void btnCaptureClick();
- void cboxSendByChange(void *param);
-
-private:
- HWND m_hWnd;
- MCONTACT m_hContact;
- HWND m_hTargetWindow, m_hLastWin;
- HWND m_hTargetHighlighter;
- wchar_t* m_FDestFolder;
- wchar_t* m_pszFile;
- FIBITMAP* m_Screenshot;
- RGBQUAD m_AlphaColor;
- CSend* m_cSend;
-
- void chkTimedClick();
- void btnExploreClick();
- void LoadOptions(void);
- void SaveOptions(void);
- INT_PTR SaveScreenshot(FIBITMAP* dib);
- void FormClose();
- static void edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl);
- static void edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl);
-
-protected:
- MONITORINFOEX* m_Monitors;
- size_t m_MonitorCount;
- RECT m_VirtualScreen;
-
- uint8_t m_opt_chkOpenAgain; // TCheckBox *chkOpenAgain;
- uint8_t m_opt_chkIndirectCapture; // TCheckBox *chkIndirectCapture;
- uint8_t m_opt_chkClientArea; // TCheckBox *chkClientArea;
- uint8_t m_opt_edtQuality; // TLabeledEdit *edtQuality;
- bool m_opt_btnDeleteAfterSend; // TCheckBox *chkDeleteAfterSend;
- uint8_t m_opt_cboxFormat; // TComboBox *cboxFormat;
- uint8_t m_opt_edtTimed; // TLabeledEdit *edtTimed;
- bool m_bCapture; // is capture active
- HWND m_hwndTab; // TabControl handle
- HWND m_hwndTabPage; // TabControl activ page handle
- HIMAGELIST m_himlTab; // TabControl imagelist
-
- typedef std::map<HWND, TfrmMain *> CHandleMapping;
- static CHandleMapping _HandleMapping;
- static INT_PTR CALLBACK DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
-
- void wmInitdialog(WPARAM wParam, LPARAM lParam);
- void wmCommand(WPARAM wParam, LPARAM lParam);
- void wmClose(WPARAM wParam, LPARAM lParam);
- void wmNotify(WPARAM wParam, LPARAM lParam);
- void wmTimer(WPARAM wParam, LPARAM lParam);
-
- void UMevent(WPARAM wParam, LPARAM lParam);
-
- static INT_PTR CALLBACK DlgProc_CaptureTabPage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef UMainFormH
+#define UMainFormH
+
+#define SS_JUSTSAVE 0
+#define SS_FILESEND 1
+#define SS_EMAIL 2
+#define SS_HTTPSERVER 3
+#define SS_FTPFILE 4
+#define SS_CLOUDFILE 5
+#define SS_IMAGESHACK 6
+#define SS_UPLOADPIE 7
+#define SS_IMGUR 8
+
+struct UPLOAD_INFO
+{
+ uint8_t sendBy; //SS_*
+ void *param;
+
+ UPLOAD_INFO(uint8_t sb) : sendBy(sb), param(nullptr) { }
+ UPLOAD_INFO(uint8_t sb, void *p) : sendBy(sb), param(p) { }
+};
+
+// Used for our own cheap TrackMouseEvent
+#define BUTTON_POLLDELAY 50
+
+// User Events
+#define EVT_CaptureDone 1
+#define EVT_SendFileDone 2
+#define EVT_CheckOpenAgain 3
+
+struct TAB_INFO
+{
+ TCITEMHEADER tcih;
+ HWND hwndMain; // main window
+ HWND hwndTab; // tab control
+ HWND hwndTabPage; // current child dialog box
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class TfrmMain
+{
+
+public:
+ // Deklaration Standardkonstruktor/Standarddestructor
+ TfrmMain();
+ ~TfrmMain();
+
+ uint8_t m_opt_tabCapture; // capture tab page
+ uint8_t m_opt_cboxDesktop; // TRadioButton *rbtnDesktop;
+ uint8_t m_opt_chkTimed; // TCheckBox *chkTimed;
+ uint8_t m_opt_cboxSendBy; // TComboBox *cboxSendBy;
+ uint8_t m_opt_btnDesc; // TCheckBox *chkDesc;
+ uint8_t m_opt_chkEditor; // TCheckBox *chkEditor;
+ bool m_bOnExitSave;
+
+ static void Unload();
+ void Init(wchar_t* DestFolder, MCONTACT Contact);
+ void Close(){ SendMessage(m_hWnd, WM_CLOSE, 0, 0); }
+ void Show(){ ShowWindow(m_hWnd, SW_SHOW); }
+ void Hide(){ ShowWindow(m_hWnd, SW_HIDE); }
+ void SetTargetWindow(HWND hwnd = nullptr);
+ void btnCaptureClick();
+ void cboxSendByChange(void *param);
+
+private:
+ HWND m_hWnd;
+ MCONTACT m_hContact;
+ HWND m_hTargetWindow, m_hLastWin;
+ HWND m_hTargetHighlighter;
+ wchar_t* m_FDestFolder;
+ wchar_t* m_pszFile;
+ FIBITMAP* m_Screenshot;
+ RGBQUAD m_AlphaColor;
+ CSend* m_cSend;
+
+ void chkTimedClick();
+ void btnExploreClick();
+ void LoadOptions(void);
+ void SaveOptions(void);
+ INT_PTR SaveScreenshot(FIBITMAP* dib);
+ void FormClose();
+ static void edtSizeUpdate(HWND hWnd, BOOL ClientArea, HWND hTarget, UINT Ctrl);
+ static void edtSizeUpdate(RECT rect, HWND hTarget, UINT Ctrl);
+
+protected:
+ MONITORINFOEX* m_Monitors;
+ size_t m_MonitorCount;
+ RECT m_VirtualScreen;
+
+ uint8_t m_opt_chkOpenAgain; // TCheckBox *chkOpenAgain;
+ uint8_t m_opt_chkIndirectCapture; // TCheckBox *chkIndirectCapture;
+ uint8_t m_opt_chkClientArea; // TCheckBox *chkClientArea;
+ uint8_t m_opt_edtQuality; // TLabeledEdit *edtQuality;
+ bool m_opt_btnDeleteAfterSend; // TCheckBox *chkDeleteAfterSend;
+ uint8_t m_opt_cboxFormat; // TComboBox *cboxFormat;
+ uint8_t m_opt_edtTimed; // TLabeledEdit *edtTimed;
+ bool m_bCapture; // is capture active
+ HWND m_hwndTab; // TabControl handle
+ HWND m_hwndTabPage; // TabControl activ page handle
+ HIMAGELIST m_himlTab; // TabControl imagelist
+
+ typedef std::map<HWND, TfrmMain *> CHandleMapping;
+ static CHandleMapping _HandleMapping;
+ static INT_PTR CALLBACK DlgTfrmMain(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ void wmInitdialog(WPARAM wParam, LPARAM lParam);
+ void wmCommand(WPARAM wParam, LPARAM lParam);
+ void wmClose(WPARAM wParam, LPARAM lParam);
+ void wmNotify(WPARAM wParam, LPARAM lParam);
+ void wmTimer(WPARAM wParam, LPARAM lParam);
+
+ void UMevent(WPARAM wParam, LPARAM lParam);
+
+ static INT_PTR CALLBACK DlgProc_CaptureTabPage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/Utils.cpp b/plugins/SendScreenshotPlus/src/Utils.cpp
index 1d0d92f5ea..bc543d1024 100644
--- a/plugins/SendScreenshotPlus/src/Utils.cpp
+++ b/plugins/SendScreenshotPlus/src/Utils.cpp
@@ -1,412 +1,412 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with 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 ComboBox_SelectItem(HWND hCombo, LPARAM data)
-{
- for (int i = 0;; i++) {
- LPARAM itemData = ComboBox_GetItemData(hCombo, i);
- if (itemData == data) {
- ComboBox_SetCurSel(hCombo, i);
- return;
- }
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// MonitorInfoEnum
-
-static BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
-{
- MONITORS* monitors = (MONITORS*)dwData;
- ++monitors->count;
- monitors->info = (MONITORINFOEX*)mir_realloc(monitors->info, sizeof(MONITORINFOEX)*monitors->count);
- monitors->info[monitors->count - 1].cbSize = sizeof(MONITORINFOEX);
- if (!GetMonitorInfo(hMonitor, (LPMONITORINFO)(monitors->info + monitors->count - 1)))
- return FALSE; // stop enumeration if error
-
- return TRUE;
-}
-
-size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen)
-{
- MONITORS tmp = { 0, nullptr };
- if (EnumDisplayMonitors(nullptr, nullptr, MonitorInfoEnumProc, (LPARAM)&tmp)) {
- myMonitors = tmp.info;
- memset(&virtualScreen, 0, sizeof(virtualScreen));
- for (size_t i = 0; i < tmp.count; ++i) {
- UnionRect(&virtualScreen, &virtualScreen, &tmp.info[i].rcMonitor);
- }
- return tmp.count;
- }
-
- mir_free(tmp.info);
- return 0;
-}
-
-FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture = nullptr);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// capture window as FIBITMAP - caller must FreeImage_Unload(dib)
-
-FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture)
-{
- FIBITMAP* dib;
- HWND hForegroundWin;
- RECT rect; // cropping rect
-
- if (!hCapture || !IsWindow(hCapture))
- return nullptr;
-
- hForegroundWin = GetForegroundWindow(); // old foreground window
- SetForegroundWindow(hCapture); // force target foreground
- BringWindowToTop(hCapture); // bring it to top as well
-
- // redraw window to prevent runtime artifacts in picture
- UpdateWindow(hCapture);
-
- HWND hParent = GetAncestor(hCapture, GA_PARENT);
- if (hParent && !IsChild(hParent, hCapture))
- hParent = nullptr;
- if (bIndirectCapture) {
- intptr_t wastopmost = GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_TOPMOST;
- if (!wastopmost)
- SetWindowPos(hCapture, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- if (bClientArea) {
- GetClientRect(hCapture, &rect);
- ClientToScreen(hCapture, (POINT*)&rect);
- rect.right += rect.left; rect.bottom += rect.top;
- }
- else
- GetWindowRect(hCapture, &rect);
- dib = CaptureMonitor(nullptr, &rect);
- if (!wastopmost)
- SetWindowPos(hCapture, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- }
- else {
- HDC hDCsrc;
- GetWindowRect(hCapture, &rect);
- if (hParent)
- hDCsrc = GetDC(hCapture); // hCapture is part of a window, capture that
- else
- hDCsrc = GetWindowDC(hCapture); // entire window w/ title bar
- rect.right = ABS(rect.right - rect.left);
- rect.bottom = ABS(rect.bottom - rect.top);
- rect.left = rect.top = 0;
- // capture window and get FIBITMAP
- dib = CreateDIBFromDC(hDCsrc, &rect, hCapture);
- ReleaseDC(hCapture, hDCsrc);
-
- // we could capture directly, but doing so breaks GetWindowRgn() and also includes artifacts...
- if (bClientArea) {
- GetWindowRect(hCapture, &rect);
- RECT rectCA; GetClientRect(hCapture, &rectCA);
- ClientToScreen(hCapture, (POINT*)&rectCA);
- rectCA.left = ABS(rectCA.left - rect.left);
- rectCA.top = ABS(rectCA.top - rect.top);
- rectCA.right += rectCA.left; rectCA.bottom += rectCA.top;
-
- // crop the window to ClientArea
- FIBITMAP* dibClient = FreeImage_Copy(dib, rectCA.left, rectCA.top, rectCA.right, rectCA.bottom);
- FreeImage_Unload(dib);
- dib = dibClient;
- }
- }
-
- // restore previous foreground window
- if (hForegroundWin) {
- SetForegroundWindow(hForegroundWin);
- BringWindowToTop(hForegroundWin);
- }
- return dib;
-}
-
-FIBITMAP* CaptureMonitor(const wchar_t* szDevice, const RECT* cropRect/*=NULL*/)
-{
- HDC hScrDC;
- RECT rect;
-
- // get screen resolution
- if (!szDevice) {
- hScrDC = CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
- rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
- rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
- rect.right = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN);
- rect.bottom = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN);
- }
- else {
- hScrDC = CreateDC(szDevice, nullptr, nullptr, nullptr);
- rect.left = rect.top = 0;
- rect.right = GetDeviceCaps(hScrDC, HORZRES);
- rect.bottom = GetDeviceCaps(hScrDC, VERTRES);
- }
- if (cropRect) {
- if (cropRect->left > rect.left) rect.left = cropRect->left;
- if (cropRect->top > rect.top) rect.top = cropRect->top;
- if (cropRect->right < rect.right) rect.right = cropRect->right;
- if (cropRect->bottom < rect.bottom) rect.bottom = cropRect->bottom;
- }
-
- FIBITMAP *dib = CreateDIBFromDC(hScrDC, &rect);
- ReleaseDC(nullptr, hScrDC);
- return dib;
-}
-
-FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture/*=NULL*/)
-{
- long width = rect->right - rect->left;
- long height = rect->bottom - rect->top;
-
- // create a DC for the screen and create
- // a memory DC compatible to screen DC
- HDC hScrDC = hDC;
- if (!hScrDC)
- hScrDC = GetDC(hCapture);
- HDC hMemDC = CreateCompatibleDC(hScrDC);
- // create a bitmap compatible with the screen DC
- HBITMAP hBitmap = CreateCompatibleBitmap(hScrDC, width, height);
- // select new bitmap into memory DC
- SelectObject(hMemDC, hBitmap);
-
- if (hCapture && hDC)
- PrintWindow(hCapture, hMemDC, 0);
- else // bitblt screen DC to memory DC
- BitBlt(hMemDC, 0, 0, width, height, hScrDC, rect->left, rect->top, CAPTUREBLT | SRCCOPY);
-
- FIBITMAP *dib = FreeImage_CreateDIBFromHBITMAP(hBitmap);
-
- // alpha channel from window is always wrong and sometimes even for desktop (Win7, no aero)
- // coz GDI do not draw all in alpha mode.
- // we have to create our own new alpha channel.
- bool bFixAlpha = true;
- bool bInvert = false;
- HBRUSH hBr = CreateSolidBrush(RGB(255, 255, 255)); // Create a SolidBrush object for non transparent area
- HBITMAP hMask = CreateBitmap(width, height, 1, 1, nullptr); // Create monochrome (1 bit) B+W mask bitmap.
- HDC hMaskDC = CreateCompatibleDC(nullptr);
- SelectBitmap(hMaskDC, hMask);
- HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
- if (hCapture && GetWindowRgn(hCapture, hRgn) == ERROR) {
- if ((GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_LAYERED)) {
- uint8_t bAlpha = 0;
- COLORREF crKey = 0x00000000;
- DWORD dwFlags = 0;
- if (GetLayeredWindowAttributes(hCapture, &crKey, &bAlpha, &dwFlags)) {
- // per window transparency (like fading in a whole window)
- if ((dwFlags&LWA_COLORKEY)) {
- SetBkColor(hMemDC, crKey);
- BitBlt(hMaskDC, 0, 0, width, height, hMemDC, rect->left, rect->top, SRCCOPY);
- bInvert = true;
- }
- else if ((dwFlags&LWA_ALPHA)) {
- bFixAlpha = false;
- }
- }
- else { // per-pixel transparency (won't use the WM_PAINT)
- bFixAlpha = false;
- }
- }
- else { // not layered - fill the window region
- SetRectRgn(hRgn, 0, 0, width, height);
- FillRgn(hMaskDC, hRgn, hBr);
- }
- }
- else {
- if (!hCapture) SetRectRgn(hRgn, 0, 0, width, height); // client area only, no transparency
- FillRgn(hMaskDC, hRgn, hBr);
- }
- DeleteObject(hRgn);
- if (bFixAlpha) {
- FIBITMAP* dibMask = FreeImage_CreateDIBFromHBITMAP(hMask);
- if (bInvert) FreeImage_Invert(dibMask);
- FIBITMAP* dib8 = FreeImage_ConvertTo8Bits(dibMask);
- // copy the dib8 alpha mask to dib32 main bitmap
- FreeImage_SetChannel(dib, dib8, FICC_ALPHA);
- FreeImage_Unload(dibMask);
- FreeImage_Unload(dib8);
- }
- DeleteDC(hMaskDC);
- DeleteObject(hMask);
- DeleteObject(hBr);
-
- // clean up
- DeleteDC(hMemDC);
- DeleteObject(hBitmap);
- if (!hDC)
- ReleaseDC(nullptr, hScrDC);
-
-#ifdef _DEBUG
- switch (FreeImage_GetImageType(dib)) {
- case FIT_UNKNOWN:
- OutputDebugStringA("FIBITMAP Type: FIT_UNKNOWN\r\n");
- break;
- case FIT_BITMAP:
- OutputDebugStringA("FIBITMAP Type: FIT_BITMAP\r\n");
- break;
- case FIT_UINT16:
- OutputDebugStringA("FIBITMAP Type: FIT_UINT16\r\n");
- break;
- case FIT_INT16:
- OutputDebugStringA("FIBITMAP Type: FIT_INT16\r\n");
- break;
- case FIT_UINT32:
- OutputDebugStringA("FIBITMAP Type: FIT_UINT32\r\n");
- break;
- case FIT_INT32:
- OutputDebugStringA("FIBITMAP Type: FIT_INT32\r\n");
- break;
- case FIT_FLOAT:
- OutputDebugStringA("FIBITMAP Type: FIT_FLOAT\r\n");
- break;
- case FIT_DOUBLE:
- OutputDebugStringA("FIBITMAP Type: FIT_DOUBLE\r\n");
- break;
- case FIT_COMPLEX:
- OutputDebugStringA("FIBITMAP Type: FIT_COMPLEX\r\n");
- break;
- case FIT_RGB16:
- OutputDebugStringA("FIBITMAP Type: FIT_RGB16\r\n");
- break;
- case FIT_RGBA16:
- OutputDebugStringA("FIBITMAP Type: FIT_RGBA16\r\n");
- break;
- case FIT_RGBF:
- OutputDebugStringA("FIBITMAP Type: FIT_RGBF\r\n");
- break;
- case FIT_RGBAF:
- OutputDebugStringA("FIBITMAP Type: FIT_RGBAF\r\n");
- break;
- default:
- OutputDebugStringA("FIBITMAP Type: non detectable image type (error)\r\n");
- break;
- }
- BOOL inf = FreeImage_IsTransparent(dib);
- OutputDebugStringA(inf ? "FIBITMAP Transparent: true\r\n" : "FIBITMAP Transparent: false\r\n");
-#endif
- return dib;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-char* GetFileNameA(const wchar_t* pszPath)
-{
- const wchar_t* slash = wcsrchr(pszPath, '\\');
- if (!slash) slash = wcsrchr(pszPath, '/');
- if (slash)
- return mir_u2a(slash + 1);
- else
- return mir_u2a(pszPath);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder)
-{
- UINT uiNum = 0;
- UINT uiSize = 0;
- BOOL bOk = FALSE;
- Gdiplus::GetImageEncodersSize(&uiNum, &uiSize);
- if (uiSize > 0) {
- Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)mir_alloc(uiSize);
- if (pImageCodecInfo) {
- Gdiplus::GetImageEncoders(uiNum, uiSize, pImageCodecInfo);
- for (UINT i = 0; i < uiNum; ++i) {
- if (!mir_wstrcmp(pImageCodecInfo[i].MimeType, wchMimeType)) {
- clsidEncoder = pImageCodecInfo[i].Clsid;
- bOk = TRUE;
- }
- }
- mir_free(pImageCodecInfo);
- }
- }
- return bOk;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void SaveGIF(HBITMAP hBmp, const wchar_t *szFilename)
-{
- Gdiplus::GdiplusStartupInput gdiplusStartupInput;
- ULONG_PTR gdiplusToken;
- Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
-
- Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE));
- if (pBitmap) {
- // Get the CLSID of the GIF encoder.
- CLSID clsidEncoder;
- if (GetEncoderClsid(L"image/gif", clsidEncoder)) {
- LPWSTR pswFile = mir_wstrdup(szFilename);
- pBitmap->Save((const wchar_t*)pswFile, &clsidEncoder, nullptr);
- mir_free(pswFile);
- }
- delete pBitmap;
- }
- Gdiplus::GdiplusShutdown(gdiplusToken);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void SaveTIF(HBITMAP hBmp, const wchar_t *szFilename)
-{
- // http://www.codeproject.com/Messages/1406708/How-to-reduce-the-size-of-an-Image-using-GDIplus.aspx
- ULONG_PTR gdiplusToken;
- Gdiplus::GdiplusStartupInput gdiplusStartupInput;
- Gdiplus::Status stat;
- Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
-
- Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE));
- if (pBitmap) {
- // Get the CLSID of the GIF encoder.
- CLSID EncCLSID;
- if (GetEncoderClsid(L"image/tiff", EncCLSID)) {
- //--- Create a 2-parameter array, for Compression and for Color Bit depth
- Gdiplus::EncoderParameters* EncParams = (Gdiplus::EncoderParameters*) malloc(sizeof(Gdiplus::EncoderParameters) + 1 * sizeof(Gdiplus::EncoderParameter));
- // Gdiplus::EncoderParameters pEncoderParameters;
- //--- Use LZW Compression instead of Group 4, since it works for color and G4 doesn't
- ULONG ulCompression = Gdiplus::EncoderValueCompressionLZW;
- ULONG ulColorDepth = 24L;
-
- EncParams->Count = 2;
- EncParams->Parameter[0].Guid = Gdiplus::EncoderCompression;
- EncParams->Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
- EncParams->Parameter[0].NumberOfValues = 1;
- EncParams->Parameter[0].Value = &ulCompression;
- EncParams->Parameter[1].Guid = Gdiplus::EncoderColorDepth;
- EncParams->Parameter[1].Type = Gdiplus::EncoderParameterValueTypeLong;
- EncParams->Parameter[1].NumberOfValues = 1;
- EncParams->Parameter[1].Value = &ulColorDepth;
-
- LPWSTR pswFile = mir_wstrdup(szFilename);
- stat = pBitmap->Save((const wchar_t*)pswFile, &EncCLSID, EncParams);
- mir_free(pswFile);
- free(EncParams);
- }
- delete pBitmap;
- }
- Gdiplus::GdiplusShutdown(gdiplusToken);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with 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 ComboBox_SelectItem(HWND hCombo, LPARAM data)
+{
+ for (int i = 0;; i++) {
+ LPARAM itemData = ComboBox_GetItemData(hCombo, i);
+ if (itemData == data) {
+ ComboBox_SetCurSel(hCombo, i);
+ return;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MonitorInfoEnum
+
+static BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ MONITORS* monitors = (MONITORS*)dwData;
+ ++monitors->count;
+ monitors->info = (MONITORINFOEX*)mir_realloc(monitors->info, sizeof(MONITORINFOEX)*monitors->count);
+ monitors->info[monitors->count - 1].cbSize = sizeof(MONITORINFOEX);
+ if (!GetMonitorInfo(hMonitor, (LPMONITORINFO)(monitors->info + monitors->count - 1)))
+ return FALSE; // stop enumeration if error
+
+ return TRUE;
+}
+
+size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen)
+{
+ MONITORS tmp = { 0, nullptr };
+ if (EnumDisplayMonitors(nullptr, nullptr, MonitorInfoEnumProc, (LPARAM)&tmp)) {
+ myMonitors = tmp.info;
+ memset(&virtualScreen, 0, sizeof(virtualScreen));
+ for (size_t i = 0; i < tmp.count; ++i) {
+ UnionRect(&virtualScreen, &virtualScreen, &tmp.info[i].rcMonitor);
+ }
+ return tmp.count;
+ }
+
+ mir_free(tmp.info);
+ return 0;
+}
+
+FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture = nullptr);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// capture window as FIBITMAP - caller must FreeImage_Unload(dib)
+
+FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture)
+{
+ FIBITMAP* dib;
+ HWND hForegroundWin;
+ RECT rect; // cropping rect
+
+ if (!hCapture || !IsWindow(hCapture))
+ return nullptr;
+
+ hForegroundWin = GetForegroundWindow(); // old foreground window
+ SetForegroundWindow(hCapture); // force target foreground
+ BringWindowToTop(hCapture); // bring it to top as well
+
+ // redraw window to prevent runtime artifacts in picture
+ UpdateWindow(hCapture);
+
+ HWND hParent = GetAncestor(hCapture, GA_PARENT);
+ if (hParent && !IsChild(hParent, hCapture))
+ hParent = nullptr;
+ if (bIndirectCapture) {
+ intptr_t wastopmost = GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_TOPMOST;
+ if (!wastopmost)
+ SetWindowPos(hCapture, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ if (bClientArea) {
+ GetClientRect(hCapture, &rect);
+ ClientToScreen(hCapture, (POINT*)&rect);
+ rect.right += rect.left; rect.bottom += rect.top;
+ }
+ else
+ GetWindowRect(hCapture, &rect);
+ dib = CaptureMonitor(nullptr, &rect);
+ if (!wastopmost)
+ SetWindowPos(hCapture, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+ else {
+ HDC hDCsrc;
+ GetWindowRect(hCapture, &rect);
+ if (hParent)
+ hDCsrc = GetDC(hCapture); // hCapture is part of a window, capture that
+ else
+ hDCsrc = GetWindowDC(hCapture); // entire window w/ title bar
+ rect.right = ABS(rect.right - rect.left);
+ rect.bottom = ABS(rect.bottom - rect.top);
+ rect.left = rect.top = 0;
+ // capture window and get FIBITMAP
+ dib = CreateDIBFromDC(hDCsrc, &rect, hCapture);
+ ReleaseDC(hCapture, hDCsrc);
+
+ // we could capture directly, but doing so breaks GetWindowRgn() and also includes artifacts...
+ if (bClientArea) {
+ GetWindowRect(hCapture, &rect);
+ RECT rectCA; GetClientRect(hCapture, &rectCA);
+ ClientToScreen(hCapture, (POINT*)&rectCA);
+ rectCA.left = ABS(rectCA.left - rect.left);
+ rectCA.top = ABS(rectCA.top - rect.top);
+ rectCA.right += rectCA.left; rectCA.bottom += rectCA.top;
+
+ // crop the window to ClientArea
+ FIBITMAP* dibClient = FreeImage_Copy(dib, rectCA.left, rectCA.top, rectCA.right, rectCA.bottom);
+ FreeImage_Unload(dib);
+ dib = dibClient;
+ }
+ }
+
+ // restore previous foreground window
+ if (hForegroundWin) {
+ SetForegroundWindow(hForegroundWin);
+ BringWindowToTop(hForegroundWin);
+ }
+ return dib;
+}
+
+FIBITMAP* CaptureMonitor(const wchar_t* szDevice, const RECT* cropRect/*=NULL*/)
+{
+ HDC hScrDC;
+ RECT rect;
+
+ // get screen resolution
+ if (!szDevice) {
+ hScrDC = CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);
+ rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ rect.right = GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ rect.bottom = GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ }
+ else {
+ hScrDC = CreateDC(szDevice, nullptr, nullptr, nullptr);
+ rect.left = rect.top = 0;
+ rect.right = GetDeviceCaps(hScrDC, HORZRES);
+ rect.bottom = GetDeviceCaps(hScrDC, VERTRES);
+ }
+ if (cropRect) {
+ if (cropRect->left > rect.left) rect.left = cropRect->left;
+ if (cropRect->top > rect.top) rect.top = cropRect->top;
+ if (cropRect->right < rect.right) rect.right = cropRect->right;
+ if (cropRect->bottom < rect.bottom) rect.bottom = cropRect->bottom;
+ }
+
+ FIBITMAP *dib = CreateDIBFromDC(hScrDC, &rect);
+ ReleaseDC(nullptr, hScrDC);
+ return dib;
+}
+
+FIBITMAP* CreateDIBFromDC(HDC hDC, const RECT* rect, HWND hCapture/*=NULL*/)
+{
+ long width = rect->right - rect->left;
+ long height = rect->bottom - rect->top;
+
+ // create a DC for the screen and create
+ // a memory DC compatible to screen DC
+ HDC hScrDC = hDC;
+ if (!hScrDC)
+ hScrDC = GetDC(hCapture);
+ HDC hMemDC = CreateCompatibleDC(hScrDC);
+ // create a bitmap compatible with the screen DC
+ HBITMAP hBitmap = CreateCompatibleBitmap(hScrDC, width, height);
+ // select new bitmap into memory DC
+ SelectObject(hMemDC, hBitmap);
+
+ if (hCapture && hDC)
+ PrintWindow(hCapture, hMemDC, 0);
+ else // bitblt screen DC to memory DC
+ BitBlt(hMemDC, 0, 0, width, height, hScrDC, rect->left, rect->top, CAPTUREBLT | SRCCOPY);
+
+ FIBITMAP *dib = FreeImage_CreateDIBFromHBITMAP(hBitmap);
+
+ // alpha channel from window is always wrong and sometimes even for desktop (Win7, no aero)
+ // coz GDI do not draw all in alpha mode.
+ // we have to create our own new alpha channel.
+ bool bFixAlpha = true;
+ bool bInvert = false;
+ HBRUSH hBr = CreateSolidBrush(RGB(255, 255, 255)); // Create a SolidBrush object for non transparent area
+ HBITMAP hMask = CreateBitmap(width, height, 1, 1, nullptr); // Create monochrome (1 bit) B+W mask bitmap.
+ HDC hMaskDC = CreateCompatibleDC(nullptr);
+ SelectBitmap(hMaskDC, hMask);
+ HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
+ if (hCapture && GetWindowRgn(hCapture, hRgn) == ERROR) {
+ if ((GetWindowLongPtr(hCapture, GWL_EXSTYLE)&WS_EX_LAYERED)) {
+ uint8_t bAlpha = 0;
+ COLORREF crKey = 0x00000000;
+ DWORD dwFlags = 0;
+ if (GetLayeredWindowAttributes(hCapture, &crKey, &bAlpha, &dwFlags)) {
+ // per window transparency (like fading in a whole window)
+ if ((dwFlags&LWA_COLORKEY)) {
+ SetBkColor(hMemDC, crKey);
+ BitBlt(hMaskDC, 0, 0, width, height, hMemDC, rect->left, rect->top, SRCCOPY);
+ bInvert = true;
+ }
+ else if ((dwFlags&LWA_ALPHA)) {
+ bFixAlpha = false;
+ }
+ }
+ else { // per-pixel transparency (won't use the WM_PAINT)
+ bFixAlpha = false;
+ }
+ }
+ else { // not layered - fill the window region
+ SetRectRgn(hRgn, 0, 0, width, height);
+ FillRgn(hMaskDC, hRgn, hBr);
+ }
+ }
+ else {
+ if (!hCapture) SetRectRgn(hRgn, 0, 0, width, height); // client area only, no transparency
+ FillRgn(hMaskDC, hRgn, hBr);
+ }
+ DeleteObject(hRgn);
+ if (bFixAlpha) {
+ FIBITMAP* dibMask = FreeImage_CreateDIBFromHBITMAP(hMask);
+ if (bInvert) FreeImage_Invert(dibMask);
+ FIBITMAP* dib8 = FreeImage_ConvertTo8Bits(dibMask);
+ // copy the dib8 alpha mask to dib32 main bitmap
+ FreeImage_SetChannel(dib, dib8, FICC_ALPHA);
+ FreeImage_Unload(dibMask);
+ FreeImage_Unload(dib8);
+ }
+ DeleteDC(hMaskDC);
+ DeleteObject(hMask);
+ DeleteObject(hBr);
+
+ // clean up
+ DeleteDC(hMemDC);
+ DeleteObject(hBitmap);
+ if (!hDC)
+ ReleaseDC(nullptr, hScrDC);
+
+#ifdef _DEBUG
+ switch (FreeImage_GetImageType(dib)) {
+ case FIT_UNKNOWN:
+ OutputDebugStringA("FIBITMAP Type: FIT_UNKNOWN\r\n");
+ break;
+ case FIT_BITMAP:
+ OutputDebugStringA("FIBITMAP Type: FIT_BITMAP\r\n");
+ break;
+ case FIT_UINT16:
+ OutputDebugStringA("FIBITMAP Type: FIT_UINT16\r\n");
+ break;
+ case FIT_INT16:
+ OutputDebugStringA("FIBITMAP Type: FIT_INT16\r\n");
+ break;
+ case FIT_UINT32:
+ OutputDebugStringA("FIBITMAP Type: FIT_UINT32\r\n");
+ break;
+ case FIT_INT32:
+ OutputDebugStringA("FIBITMAP Type: FIT_INT32\r\n");
+ break;
+ case FIT_FLOAT:
+ OutputDebugStringA("FIBITMAP Type: FIT_FLOAT\r\n");
+ break;
+ case FIT_DOUBLE:
+ OutputDebugStringA("FIBITMAP Type: FIT_DOUBLE\r\n");
+ break;
+ case FIT_COMPLEX:
+ OutputDebugStringA("FIBITMAP Type: FIT_COMPLEX\r\n");
+ break;
+ case FIT_RGB16:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGB16\r\n");
+ break;
+ case FIT_RGBA16:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGBA16\r\n");
+ break;
+ case FIT_RGBF:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGBF\r\n");
+ break;
+ case FIT_RGBAF:
+ OutputDebugStringA("FIBITMAP Type: FIT_RGBAF\r\n");
+ break;
+ default:
+ OutputDebugStringA("FIBITMAP Type: non detectable image type (error)\r\n");
+ break;
+ }
+ BOOL inf = FreeImage_IsTransparent(dib);
+ OutputDebugStringA(inf ? "FIBITMAP Transparent: true\r\n" : "FIBITMAP Transparent: false\r\n");
+#endif
+ return dib;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* GetFileNameA(const wchar_t* pszPath)
+{
+ const wchar_t* slash = wcsrchr(pszPath, '\\');
+ if (!slash) slash = wcsrchr(pszPath, '/');
+ if (slash)
+ return mir_u2a(slash + 1);
+ else
+ return mir_u2a(pszPath);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder)
+{
+ UINT uiNum = 0;
+ UINT uiSize = 0;
+ BOOL bOk = FALSE;
+ Gdiplus::GetImageEncodersSize(&uiNum, &uiSize);
+ if (uiSize > 0) {
+ Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)mir_alloc(uiSize);
+ if (pImageCodecInfo) {
+ Gdiplus::GetImageEncoders(uiNum, uiSize, pImageCodecInfo);
+ for (UINT i = 0; i < uiNum; ++i) {
+ if (!mir_wstrcmp(pImageCodecInfo[i].MimeType, wchMimeType)) {
+ clsidEncoder = pImageCodecInfo[i].Clsid;
+ bOk = TRUE;
+ }
+ }
+ mir_free(pImageCodecInfo);
+ }
+ }
+ return bOk;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SaveGIF(HBITMAP hBmp, const wchar_t *szFilename)
+{
+ Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ ULONG_PTR gdiplusToken;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+
+ Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE));
+ if (pBitmap) {
+ // Get the CLSID of the GIF encoder.
+ CLSID clsidEncoder;
+ if (GetEncoderClsid(L"image/gif", clsidEncoder)) {
+ LPWSTR pswFile = mir_wstrdup(szFilename);
+ pBitmap->Save((const wchar_t*)pswFile, &clsidEncoder, nullptr);
+ mir_free(pswFile);
+ }
+ delete pBitmap;
+ }
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SaveTIF(HBITMAP hBmp, const wchar_t *szFilename)
+{
+ // http://www.codeproject.com/Messages/1406708/How-to-reduce-the-size-of-an-Image-using-GDIplus.aspx
+ ULONG_PTR gdiplusToken;
+ Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ Gdiplus::Status stat;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+
+ Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, (HPALETTE)GetStockObject(DEFAULT_PALETTE));
+ if (pBitmap) {
+ // Get the CLSID of the GIF encoder.
+ CLSID EncCLSID;
+ if (GetEncoderClsid(L"image/tiff", EncCLSID)) {
+ //--- Create a 2-parameter array, for Compression and for Color Bit depth
+ Gdiplus::EncoderParameters* EncParams = (Gdiplus::EncoderParameters*) malloc(sizeof(Gdiplus::EncoderParameters) + 1 * sizeof(Gdiplus::EncoderParameter));
+ // Gdiplus::EncoderParameters pEncoderParameters;
+ //--- Use LZW Compression instead of Group 4, since it works for color and G4 doesn't
+ ULONG ulCompression = Gdiplus::EncoderValueCompressionLZW;
+ ULONG ulColorDepth = 24L;
+
+ EncParams->Count = 2;
+ EncParams->Parameter[0].Guid = Gdiplus::EncoderCompression;
+ EncParams->Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
+ EncParams->Parameter[0].NumberOfValues = 1;
+ EncParams->Parameter[0].Value = &ulCompression;
+ EncParams->Parameter[1].Guid = Gdiplus::EncoderColorDepth;
+ EncParams->Parameter[1].Type = Gdiplus::EncoderParameterValueTypeLong;
+ EncParams->Parameter[1].NumberOfValues = 1;
+ EncParams->Parameter[1].Value = &ulColorDepth;
+
+ LPWSTR pswFile = mir_wstrdup(szFilename);
+ stat = pBitmap->Save((const wchar_t*)pswFile, &EncCLSID, EncParams);
+ mir_free(pswFile);
+ free(EncParams);
+ }
+ delete pBitmap;
+ }
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
diff --git a/plugins/SendScreenshotPlus/src/Utils.h b/plugins/SendScreenshotPlus/src/Utils.h
index 89dd2f614e..df736da08f 100644
--- a/plugins/SendScreenshotPlus/src/Utils.h
+++ b/plugins/SendScreenshotPlus/src/Utils.h
@@ -1,73 +1,73 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#ifndef UTILSH
-#define UTILSH
-
-#define SPP_USERPANE 1
-
-#define ABS(x) ((x)<0?-(x):(x))
-
-struct MONITORS
-{
- size_t count;
- MONITORINFOEX* info;
-};
-
-extern HWND g_hCapture;
-extern HBITMAP g_hBitmap, g_hbmMask;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen);
-
-FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture);
-FIBITMAP* CaptureMonitor(const wchar_t *pwszDevice, const RECT *cropRect = nullptr);
-
-char* GetFileNameA(const wchar_t *pwszPath);
-
-BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder);
-
-void SaveGIF(HBITMAP hBmp, const wchar_t *pwszFilename);
-void SaveTIF(HBITMAP hBmp, const wchar_t *pwszFilename);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-class EventHandle
-{
- HANDLE _hEvent;
-public:
- inline EventHandle() { _hEvent = CreateEvent(nullptr, 0, 0, nullptr); }
- inline ~EventHandle() { CloseHandle(_hEvent); }
- inline void Set() { SetEvent(_hEvent); }
- inline void Wait() { WaitForSingleObject(_hEvent, INFINITE); }
- inline void Wait(uint32_t dwMilliseconds) { WaitForSingleObject(_hEvent, dwMilliseconds); }
- inline operator HANDLE() { return _hEvent; }
-};
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef UTILSH
+#define UTILSH
+
+#define SPP_USERPANE 1
+
+#define ABS(x) ((x)<0?-(x):(x))
+
+struct MONITORS
+{
+ size_t count;
+ MONITORINFOEX* info;
+};
+
+extern HWND g_hCapture;
+extern HBITMAP g_hBitmap, g_hbmMask;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+size_t MonitorInfoEnum(MONITORINFOEX* &myMonitors, RECT &virtualScreen);
+
+FIBITMAP* CaptureWindow(HWND hCapture, BOOL bClientArea, BOOL bIndirectCapture);
+FIBITMAP* CaptureMonitor(const wchar_t *pwszDevice, const RECT *cropRect = nullptr);
+
+char* GetFileNameA(const wchar_t *pwszPath);
+
+BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID &clsidEncoder);
+
+void SaveGIF(HBITMAP hBmp, const wchar_t *pwszFilename);
+void SaveTIF(HBITMAP hBmp, const wchar_t *pwszFilename);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class EventHandle
+{
+ HANDLE _hEvent;
+public:
+ inline EventHandle() { _hEvent = CreateEvent(nullptr, 0, 0, nullptr); }
+ inline ~EventHandle() { CloseHandle(_hEvent); }
+ inline void Set() { SetEvent(_hEvent); }
+ inline void Wait() { WaitForSingleObject(_hEvent, INFINITE); }
+ inline void Wait(uint32_t dwMilliseconds) { WaitForSingleObject(_hEvent, dwMilliseconds); }
+ inline operator HANDLE() { return _hEvent; }
+};
+
+#endif
diff --git a/plugins/SendScreenshotPlus/src/ctrl_button.h b/plugins/SendScreenshotPlus/src/ctrl_button.h
index 42ac4762a7..d2672c79d7 100644
--- a/plugins/SendScreenshotPlus/src/ctrl_button.h
+++ b/plugins/SendScreenshotPlus/src/ctrl_button.h
@@ -1,34 +1,34 @@
-/*
-imported from UserinfoEx plugin for Miranda NG
-Copyright:
-© 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/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.
-*/
-
-#ifndef _UINFOEX_BOTTONS_H_INCLUDED_
-#define _UINFOEX_BOTTONS_H_INCLUDED_ 1
-
-void CtrlButtonLoadModule();
-void CtrlButtonUnloadModule();
-
+/*
+imported from UserinfoEx plugin for Miranda NG
+Copyright:
+© 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/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.
+*/
+
+#ifndef _UINFOEX_BOTTONS_H_INCLUDED_
+#define _UINFOEX_BOTTONS_H_INCLUDED_ 1
+
+void CtrlButtonLoadModule();
+void CtrlButtonUnloadModule();
+
#endif /* _UINFOEX_BOTTONS_H_INCLUDED_ */ \ No newline at end of file
diff --git a/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp b/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp
index e64e686215..b0aa0bc04d 100644
--- a/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp
+++ b/plugins/SendScreenshotPlus/src/dlg_msgbox.cpp
@@ -1,714 +1,714 @@
-/*
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
-© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-// SendSS compatibility:
-#include "stdafx.h"
-#define ghInst g_plugin.getInst()
-#define myGlobals g_myGlobals
-#define MODNAME MODULENAME
-#define ICO_COMMON_MAIN 0xFFFF
-#define ICO_DLG_DETAILS 0xFFFF
-
-HICON Skin_GetIcon_SendSS(unsigned short id)
-{
- if(id==0xFFFF)
- return GetIcon(ICO_MAIN);
- return GetIconBtn(id);
-}
-#define IcoLib_GetIcon Skin_GetIcon_SendSS
-// original UserInfoEx codebase (almost):
-
-typedef struct _MSGPOPUPDATA
-{
- POPUPACTION pa[3];
- HWND hDialog;
-} MSGPOPUPDATA, *LPMSGPOPUPDATA;
-
-/**
- * This helper function moves and resizes a dialog box's control element.
- *
- * @param hDlg - the dialog box's window handle
- * @param idCtrl - the identication number of the control to move
- * @param dx -´number of pixels to horizontal move the control
- * @param dy - number of pixels to vertical move the control
- * @param dw - number of pixels to horizontal resize the control
- * @param dh - number of pixels to vertical resize the control
- *
- * @return nothing
- **/
-static FORCEINLINE void MoveCtrl(HWND hDlg, int idCtrl, int dx, int dy, int dw, int dh)
-{
- RECT ws;
- HWND hCtrl = GetDlgItem(hDlg, idCtrl);
- GetWindowRect(hCtrl, &ws);
- OffsetRect(&ws, dx, dy);
- MoveWindow(hCtrl, ws.left, ws.top, ws.right - ws.left + dw, ws.bottom - ws.top + dh, FALSE);
-}
-
-/**
-* This function loads the icon to display for the current message.
-*
-* @param pMsgBox - pointer to a MSGBOX structure, with information about the message to display.
-*
-* @retval HICON - The function returns an icon to display with the message.
-* @retval NULL - There is no icon for the message.
-**/
-static HICON MsgLoadIcon(LPMSGBOX pMsgBox)
-{
- HICON hIcon;
-
- // load the desired status icon
- switch (pMsgBox->uType & MB_ICONMASK) {
- case MB_ICON_OTHER: // custom icon defined by caller function
- hIcon = pMsgBox->hiMsg;
- break;
-
- // default windows icons
- case MB_ICON_ERROR:
- case MB_ICON_QUESTION:
- case MB_ICON_WARNING:
- case MB_ICON_INFO:
- {
- LPCTSTR ico[] = { nullptr, IDI_ERROR, IDI_QUESTION, IDI_WARNING, IDI_INFORMATION };
- hIcon = LoadIcon(nullptr, ico[MB_ICON_INDEX(pMsgBox->uType)]);
- }
- break;
-
- // no icon
- default:
- hIcon = nullptr;
- }
- return hIcon;
-}
-
-/**
- * This function fills a given POPUPACTION structure with the data of a given message id,
- * which is normally used by the message box. This is required to let the user interact
- * with a popup in the same way as with a normal message dialog box.
- *
- * @param pa - reference to a POPUPACTION structure to fill
- * @param id - the message id
- * @param result - This parameter is passed to the POPUPACTION structure as is.
- *
- * @return nothing
- **/
-static void MakePopupAction(POPUPACTION &pa, int id)
-{
- pa.cbSize = sizeof(POPUPACTION);
- pa.flags = PAF_ENABLED;
- pa.wParam = MAKEWORD(id, BN_CLICKED);
- pa.lParam = 0;
-
- switch (id) {
- case IDOK:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
- mir_strcpy(pa.lpzTitle, MODNAME"/Ok");
- break;
-
- case IDCLOSE:
- case IDCANCEL:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
- mir_strcpy(pa.lpzTitle, MODNAME"/Cancel");
- break;
-
- case IDABORT:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
- mir_strcpy(pa.lpzTitle, MODNAME"/Abort");
- break;
-
- case IDRETRY:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
- mir_strcpy(pa.lpzTitle, MODNAME"/Retry");
- break;
-
- case IDIGNORE:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
- mir_strcpy(pa.lpzTitle, MODNAME"/Ignore");
- break;
-
- case IDYES:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
- mir_strcpy(pa.lpzTitle, MODNAME"/Yes");
- break;
-
- case IDNO:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
- mir_strcpy(pa.lpzTitle, MODNAME"/No");
- break;
-
- case IDHELP:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
- mir_strcpy(pa.lpzTitle, MODNAME"/Help");
- break;
-
- case IDALL:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
- mir_strcpy(pa.lpzTitle, MODNAME"/All");
- break;
-
- case IDNONE:
- pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
- mir_strcpy(pa.lpzTitle, MODNAME"/None");
- }
-}
-
-/**
- * This is the message procedure for my nice looking message box
- *
- * @param hDlg - window handle
- * @param uMsg - message to handle
- * @param wParam - message specific parameter
- * @param lParam - message specific parameter
- *
- * @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
- **/
-static INT_PTR CALLBACK MsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- static int retOk = IDOK;
- static int retAll = IDALL;
- static int retNon = IDNONE;
- static int retCancel = IDCANCEL;
-
- switch (uMsg) {
- case WM_INITDIALOG:
- {
- LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
- if (PtrIsValid(pMsgBox)) {
- int icoWidth = 0;
- int InfoBarHeight = 0;
- HFONT hNormalFont;
-
- hNormalFont = (HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0);
- if (pMsgBox->uType & MB_INFOBAR) {
- LOGFONT lf;
-
- // set bold font for name in description area
- GetObject(hNormalFont, sizeof(lf), &lf);
- lf.lfWeight = FW_BOLD;
- hNormalFont = CreateFontIndirect(&lf);
-
- // set infobar's textfont
- SendDlgItemMessage(hDlg, TXT_NAME, WM_SETFONT, (WPARAM)hNormalFont, 0);
-
- // set infobar's logo icon
- SendDlgItemMessage(hDlg, ICO_DLGLOGO, STM_SETIMAGE, IMAGE_ICON,
- (pMsgBox->hiLogo ? (LPARAM)pMsgBox->hiLogo : (LPARAM)IcoLib_GetIcon(ICO_DLG_DETAILS)));
-
- // enable headerbar
- ShowWindow(GetDlgItem(hDlg, TXT_NAME), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, ICO_DLGLOGO), SW_SHOW);
- }
- else {
- RECT rc;
- GetClientRect(GetDlgItem(hDlg, TXT_NAME), &rc);
- InfoBarHeight = rc.bottom;
-
- if (pMsgBox->hiLogo)
- SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)pMsgBox->hiLogo);
- }
-
- // draw the desired status icon
- HICON hIcon = MsgLoadIcon(pMsgBox);
- if (hIcon)
- SendDlgItemMessage(hDlg, ICO_MSGDLG, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
- else {
- RECT ws;
- GetWindowRect(GetDlgItem(hDlg, ICO_MSGDLG), &ws);
- icoWidth = ws.right - ws.left;
- ShowWindow(GetDlgItem(hDlg, ICO_MSGDLG), SW_HIDE);
- }
-
- // resize the messagebox and reorganize the buttons
- if (HDC hDC = GetDC(hDlg)) {
- POINT mpt = { 0, 0 };
- RECT ws = { 0, 0, 0, 0 };
- int txtWidth = 0, txtHeight = 0, needX, needY;
- RECT rcDlg;
- SIZE ts;
- LPTSTR h, rs;
-
- SelectObject(hDC, hNormalFont);
- // get message text width and height
- if (pMsgBox->ptszMsg) for (rs = h = pMsgBox->ptszMsg;; ++h) {
- if (*h == '\n' || !*h) {
- GetTextExtentPoint32(hDC, rs, h - rs, &ts);
- if (ts.cx > txtWidth)
- txtWidth = ts.cx;
- txtHeight += ts.cy;
- if (!*h)
- break;
- rs = h + 1;
- }
- }
- // increase width if info text requires more
- if ((pMsgBox->uType&MB_INFOBAR) && pMsgBox->ptszInfoText && *pMsgBox->ptszInfoText) {
- int multiline = 0;
- RECT rcico; GetClientRect(GetDlgItem(hDlg, ICO_DLGLOGO), &rcico);
- rcico.right = rcico.right * 100 / 66; // padding
- for (rs = h = pMsgBox->ptszInfoText;; ++h) {
- if (*h == '\n' || !*h) {
- GetTextExtentPoint32(hDC, rs, h - rs, &ts);
- ts.cx += rcico.right;
- if (ts.cx > txtWidth)
- txtWidth = ts.cx;
- if (!*h)
- break;
- rs = h + 1;
- ++multiline;
- }
- }
- if (!multiline)
- SetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE) | SS_CENTERIMAGE);
- }
- ReleaseDC(hDlg, hDC);
-
- // calc new dialog size
- GetWindowRect(hDlg, &rcDlg);
- GetWindowRect(GetDlgItem(hDlg, TXT_MESSAGE), &ws);
- needX = txtWidth - (ws.right - ws.left) - icoWidth;
- needY = max(0, txtHeight - (ws.bottom - ws.top) + 5);
- rcDlg.left -= needX / 2; rcDlg.right += needX / 2;
- rcDlg.top -= (needY - InfoBarHeight) / 2; rcDlg.bottom += (needY - InfoBarHeight) / 2;
-
- // resize dialog window
- MoveWindow(hDlg, rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, FALSE);
- ClientToScreen(hDlg, &mpt);
-
- MoveCtrl(hDlg, STATIC_WHITERECT, -mpt.x, -mpt.y, needX, needY - InfoBarHeight);
- MoveCtrl(hDlg, TXT_NAME, -mpt.x, -mpt.y, needX, 0);
- MoveCtrl(hDlg, ICO_DLGLOGO, -mpt.x + needX, -mpt.y, 0, 0);
- MoveCtrl(hDlg, ICO_MSGDLG, -mpt.x, -mpt.y - InfoBarHeight, 0, 0);
- MoveCtrl(hDlg, TXT_MESSAGE, -mpt.x - icoWidth, -mpt.y - InfoBarHeight, needX, needY);
- MoveCtrl(hDlg, STATIC_LINE2, -mpt.x, -mpt.y + needY - InfoBarHeight, needX, 0);
-
- // Do pushbutton positioning
- RECT rcOk, rcAll, rcNone, rcCancel;
-
- // get button rectangles
- GetWindowRect(GetDlgItem(hDlg, IDOK), &rcOk);
- OffsetRect(&rcOk, -mpt.x, -mpt.y + needY - InfoBarHeight);
-
- GetWindowRect(GetDlgItem(hDlg, IDALL), &rcAll);
- OffsetRect(&rcAll, -mpt.x, -mpt.y + needY - InfoBarHeight);
-
- GetWindowRect(GetDlgItem(hDlg, IDNONE), &rcNone);
- OffsetRect(&rcNone, -mpt.x, -mpt.y + needY - InfoBarHeight);
-
- GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel);
- OffsetRect(&rcCancel, -mpt.x, -mpt.y + needY - InfoBarHeight);
-
- LONG okWidth = rcOk.right - rcOk.left;
- LONG allWidth = rcAll.right - rcAll.left;
- LONG noneWidth = rcNone.right - rcNone.left;
- LONG caWidth = rcCancel.right - rcCancel.left;
- LONG dlgMid = (rcDlg.right - rcDlg.left) / 2;
-
- // load button configuration
- switch (MB_TYPE(pMsgBox->uType)) {
- case MB_OK:
- rcOk.left = dlgMid - (okWidth / 2);
- rcOk.right = rcOk.left + okWidth;
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- break;
-
- case MB_OKCANCEL:
- retOk = IDRETRY;
- SetDlgItemText(hDlg, IDOK, LPGENW("OK"));
- retCancel = IDCANCEL;
- SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
- rcOk.left = dlgMid - okWidth - 10;
- rcOk.right = rcOk.left + okWidth;
- rcCancel.left = dlgMid + 10;
- rcCancel.right = rcCancel.left + caWidth;
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
- break;
-
- case MB_RETRYCANCEL:
- retOk = IDRETRY;
- SetDlgItemText(hDlg, IDOK, LPGENW("Retry"));
- retCancel = IDCANCEL;
- SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
- rcOk.left = dlgMid - okWidth - 10;
- rcOk.right = rcOk.left + okWidth;
- rcCancel.left = dlgMid + 10;
- rcCancel.right = rcCancel.left + caWidth;
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
- break;
-
- case MB_YESNO:
- retOk = IDYES;
- SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
- retCancel = IDNO;
- SetDlgItemText(hDlg, IDCANCEL, LPGENW("No"));
- rcOk.left = dlgMid - okWidth - 10;
- rcOk.right = rcOk.left + okWidth;
- rcCancel.left = dlgMid + 10;
- rcCancel.right = rcCancel.left + caWidth;
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
- break;
-
- case MB_ABORTRETRYIGNORE:
- retOk = IDABORT;
- SetDlgItemText(hDlg, IDOK, LPGENW("Abort"));
- retAll = IDABORT;
- SetDlgItemText(hDlg, IDALL, LPGENW("Retry"));
- retCancel = IDCANCEL;
- SetDlgItemText(hDlg, IDCANCEL, LPGENW("Ignore"));
- rcAll.left = dlgMid - (allWidth / 2);
- rcAll.right = rcAll.left + allWidth;
- rcOk.left = rcAll.left - okWidth - 5;
- rcOk.right = rcOk.left + okWidth;
- rcCancel.left = rcAll.right + 5;
- rcCancel.right = rcCancel.left + caWidth;
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
- break;
-
- case MB_YESNOCANCEL:
- retOk = IDYES;
- SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
- retAll = IDNO;
- SetDlgItemText(hDlg, IDALL, LPGENW("No"));
- retCancel = IDCANCEL;
- SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
- rcAll.left = dlgMid - (allWidth / 2);
- rcAll.right = rcAll.left + allWidth;
- rcOk.left = rcAll.left - okWidth - 5;
- rcOk.right = rcOk.left + okWidth;
- rcCancel.left = rcAll.right + 5;
- rcCancel.right = rcCancel.left + caWidth;
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
- break;
-
- case MB_YESALLNO:
- retOk = IDYES;
- SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
- retAll = IDALL;
- SetDlgItemText(hDlg, IDALL, LPGENW("All"));
- SetDlgItemText(hDlg, IDNONE, LPGENW("None"));
- retCancel = IDNO;
- SetDlgItemText(hDlg, IDCANCEL, LPGENW("No"));
- rcCancel.right = rcDlg.right - rcDlg.left - 10;
- rcCancel.left = rcCancel.right - caWidth;
- rcNone.right = rcCancel.left - 5;
- rcNone.left = rcNone.right - noneWidth;
- rcAll.right = rcNone.left - 5;
- rcAll.left = rcAll.right - allWidth;
- rcOk.right = rcAll.left - 5;
- rcOk.left = rcOk.right - okWidth;
- // show buttons
- ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDNONE), SW_SHOW);
- ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
- break;
-
- default:
- rcOk.left = dlgMid - (okWidth / 2);
- rcOk.right = rcOk.left + okWidth;
- }
-
- MoveWindow(GetDlgItem(hDlg, IDOK), rcOk.left, rcOk.top, rcOk.right - rcOk.left, rcOk.bottom - rcOk.top, FALSE);
- MoveWindow(GetDlgItem(hDlg, IDALL), rcAll.left, rcAll.top, rcAll.right - rcAll.left, rcAll.bottom - rcAll.top, FALSE);
- MoveWindow(GetDlgItem(hDlg, IDNONE), rcNone.left, rcNone.top, rcNone.right - rcNone.left, rcNone.bottom - rcNone.top, FALSE);
- MoveWindow(GetDlgItem(hDlg, IDCANCEL), rcCancel.left, rcCancel.top, rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top, FALSE);
- }
-
- // set text's
- SetWindowText(hDlg, pMsgBox->ptszTitle);
- SetDlgItemText(hDlg, TXT_NAME, pMsgBox->ptszInfoText);
- SetDlgItemText(hDlg, TXT_MESSAGE, pMsgBox->ptszMsg);
-
- TranslateDialogDefault(hDlg);
- return TRUE;
- }
- }
- break;
-
- case WM_CTLCOLORSTATIC:
- switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
- case STATIC_WHITERECT:
- case ICO_DLGLOGO:
- case ICO_MSGDLG:
- case TXT_MESSAGE:
- case TXT_NAME:
- SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
- return GetSysColor(COLOR_WINDOW);
- }
- break;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDOK:
- EndDialog(hDlg, retOk);
- break;
- case IDCANCEL:
- EndDialog(hDlg, retCancel);
- break;
- case IDALL:
- EndDialog(hDlg, retAll);
- break;
- case IDNONE:
- EndDialog(hDlg, retNon);
- }
- break;
-
- case WM_DESTROY:
- DeleteObject((HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0));
- break;
- }
- return FALSE;
-}
-
-/**
-* Dummi modal MsgBox for popup,
-* this set call function in wait stait and do not freece miranda main thread
-* the window is outside the desktop
-*/
-static INT_PTR CALLBACK MsgBoxPop(HWND hDlg, UINT uMsg, WPARAM, LPARAM lParam)
-{
- switch (uMsg) {
- case WM_INITDIALOG:
- LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
-
- MoveWindow(hDlg, -10, -10, 0, 0, FALSE);
- LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)mir_alloc(sizeof(MSGPOPUPDATA));
- if (pmpd) {
- POPUPDATAW ppd;
- ppd.lchContact = NULL; // (HANDLE)wParam;
- // icon
- ppd.lchIcon = MsgLoadIcon(pMsgBox);
- mir_wstrncpy(ppd.lpwzContactName, pMsgBox->ptszTitle, _countof(ppd.lpwzContactName));
- mir_wstrncpy(ppd.lpwzText, pMsgBox->ptszMsg, _countof(ppd.lpwzText));
-
- // CALLBAC Proc
- ppd.PluginWindowProc = PopupProc;
- ppd.PluginData = pmpd;
- ppd.iSeconds = -1;
- ppd.lpActions = pmpd->pa;
-
- // set color of popup
- switch (pMsgBox->uType & MB_ICONMASK) {
- case MB_ICON_ERROR:
- ppd.colorBack = RGB(200, 10, 0);
- ppd.colorText = RGB(255, 255, 255);
- break;
-
- case MB_ICON_WARNING:
- ppd.colorBack = RGB(200, 100, 0);
- ppd.colorText = RGB(255, 255, 255);
- break;
-
- default:
- if (pMsgBox->uType & MB_CUSTOMCOLOR) {
- ppd.colorBack = pMsgBox->colorBack;
- ppd.colorText = pMsgBox->colorText;
- }
- }
-
- // handle for MakePopupAction
- pmpd->hDialog = hDlg;
-
- // active buttons
- switch (MB_TYPE(pMsgBox->uType)) {
- case MB_OK:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK);
- break;
-
- case MB_OKCANCEL:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
- break;
-
- case MB_RETRYCANCEL:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
- break;
-
- case MB_YESNO:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
- break;
-
- case MB_ABORTRETRYIGNORE:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDABORT);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDIGNORE);
- break;
-
- case MB_YESNOCANCEL:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
- break;
-
- case MB_YESALLNO:
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDALL);
- MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
- break;
- }
-
- // create popup
- PUAddPopupW(&ppd);
- if (MB_TYPE(pMsgBox->uType) == MB_OK)
- EndDialog(hDlg, IDOK);
- }
- break;
- }
- return FALSE;
-}
-
-/**
-* This is the message procedure for popup
-*
-* @param hDlg - window handle
-* @param uMsg - message to handle
-* @param wParam - message specific parameter
-* @param lParam - message specific parameter
-*
-* @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
-**/
-
-LRESULT CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- switch (uMsg) {
- case UM_POPUPACTION:
- if (HIWORD(wParam) == BN_CLICKED) {
- LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
- if (pmpd) {
- switch (LOWORD(wParam)) {
- case IDOK:
- case IDCANCEL:
- case IDABORT:
- case IDRETRY:
- case IDIGNORE:
- case IDYES:
- case IDNO:
- case IDALL:
- case IDNONE:
- if (IsWindow(pmpd->hDialog))
- EndDialog(pmpd->hDialog, LOWORD(wParam));
- break;
-
- default:
- if (IsWindow(pmpd->hDialog))
- EndDialog(pmpd->hDialog, IDCANCEL);
- }
- }
- PUDeletePopup(hDlg);
- }
- break;
-
- case UM_FREEPLUGINDATA:
- LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
- if (pmpd > 0)
- MIR_FREE(pmpd);
- return TRUE;
- }
- return DefWindowProc(hDlg, uMsg, wParam, lParam);
-}
-
-/**
-* This is the service function for external plugins to use the nice messagebox
-*
-* @param wParam - MCONTACT hContact which can display an avatar for popups
-* @param lParam - MSGBOX structure holding parameters
-*
-* @return The function returns the ID of the clicked button (IDOK, IDCANCEL, ...)
-* or -1 on error.
-**/
-INT_PTR MsgBoxService(WPARAM, LPARAM lParam)
-{
- LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
-
- // check input
- if (PtrIsValid(pMsgBox) && pMsgBox->cbSize == sizeof(MSGBOX)) {
- // Shall the MessageBox displayed as popup?
- if (!(pMsgBox->uType & (MB_INFOBAR | MB_NOPOPUP)) // message box can be a popup?
- && myGlobals.PopupActionsExist == 1 // popup support ext stuct?
- && (db_get_dw(0, "Popup", "Actions", 0) & 1) // popup++ actions on?
- && db_get_b(0, MODNAME, SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX)) // user likes popups?
- return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOXDUMMI), pMsgBox->hParent, MsgBoxPop, lParam);
-
- return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOX), pMsgBox->hParent, MsgBoxProc, lParam);
- }
- return -1;
-}
-
-/**
-* name: MsgBox
-* desc: calls a messagebox
-* param:
-**/
-INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPCTSTR pszTitle, LPCTSTR pszInfo, LPCTSTR pszFormat, ...)
-{
- wchar_t tszMsg[MAX_SECONDLINE];
-
- va_list vl;
- va_start(vl, pszFormat);
- mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl);
- va_end(vl);
-
- MSGBOX mb = { 0 };
- mb.cbSize = sizeof(MSGBOX);
- mb.hParent = hParent;
- mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN);
- mb.hiMsg = nullptr;
- mb.ptszTitle = TranslateW(pszTitle);
- mb.ptszInfoText = TranslateW(pszInfo);
- mb.ptszMsg = tszMsg;
- mb.uType = uType;
- return MsgBoxService(NULL, (LPARAM)&mb);
-}
-
-/**
-* name: MsgErr
-* desc: calls a messagebox
-* param:
-**/
-INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...)
-{
- wchar_t tszTitle[MAX_SECONDLINE], tszMsg[MAX_SECONDLINE];
- mir_snwprintf(tszTitle, L"%s - %s", _A2W(MODNAME), TranslateT("Error"));
-
- va_list vl;
- va_start(vl, pszFormat);
- mir_vsnwprintf(tszMsg, TranslateW(pszFormat), vl);
- va_end(vl);
-
- MSGBOX mb = { 0 };
- mb.cbSize = sizeof(MSGBOX);
- mb.hParent = hParent;
- mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN);
- mb.hiMsg = nullptr;
- mb.ptszTitle = tszTitle;
- mb.ptszMsg = tszMsg;
- mb.uType = MB_OK | MB_ICON_ERROR;
- return MsgBoxService(NULL, (LPARAM)&mb);
-}
+/*
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright:
+© 2012-23 Miranda NG team (https://miranda-ng.org)
+© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+// SendSS compatibility:
+#include "stdafx.h"
+#define ghInst g_plugin.getInst()
+#define myGlobals g_myGlobals
+#define MODNAME MODULENAME
+#define ICO_COMMON_MAIN 0xFFFF
+#define ICO_DLG_DETAILS 0xFFFF
+
+HICON Skin_GetIcon_SendSS(unsigned short id)
+{
+ if(id==0xFFFF)
+ return GetIcon(ICO_MAIN);
+ return GetIconBtn(id);
+}
+#define IcoLib_GetIcon Skin_GetIcon_SendSS
+// original UserInfoEx codebase (almost):
+
+typedef struct _MSGPOPUPDATA
+{
+ POPUPACTION pa[3];
+ HWND hDialog;
+} MSGPOPUPDATA, *LPMSGPOPUPDATA;
+
+/**
+ * This helper function moves and resizes a dialog box's control element.
+ *
+ * @param hDlg - the dialog box's window handle
+ * @param idCtrl - the identication number of the control to move
+ * @param dx -´number of pixels to horizontal move the control
+ * @param dy - number of pixels to vertical move the control
+ * @param dw - number of pixels to horizontal resize the control
+ * @param dh - number of pixels to vertical resize the control
+ *
+ * @return nothing
+ **/
+static FORCEINLINE void MoveCtrl(HWND hDlg, int idCtrl, int dx, int dy, int dw, int dh)
+{
+ RECT ws;
+ HWND hCtrl = GetDlgItem(hDlg, idCtrl);
+ GetWindowRect(hCtrl, &ws);
+ OffsetRect(&ws, dx, dy);
+ MoveWindow(hCtrl, ws.left, ws.top, ws.right - ws.left + dw, ws.bottom - ws.top + dh, FALSE);
+}
+
+/**
+* This function loads the icon to display for the current message.
+*
+* @param pMsgBox - pointer to a MSGBOX structure, with information about the message to display.
+*
+* @retval HICON - The function returns an icon to display with the message.
+* @retval NULL - There is no icon for the message.
+**/
+static HICON MsgLoadIcon(LPMSGBOX pMsgBox)
+{
+ HICON hIcon;
+
+ // load the desired status icon
+ switch (pMsgBox->uType & MB_ICONMASK) {
+ case MB_ICON_OTHER: // custom icon defined by caller function
+ hIcon = pMsgBox->hiMsg;
+ break;
+
+ // default windows icons
+ case MB_ICON_ERROR:
+ case MB_ICON_QUESTION:
+ case MB_ICON_WARNING:
+ case MB_ICON_INFO:
+ {
+ LPCTSTR ico[] = { nullptr, IDI_ERROR, IDI_QUESTION, IDI_WARNING, IDI_INFORMATION };
+ hIcon = LoadIcon(nullptr, ico[MB_ICON_INDEX(pMsgBox->uType)]);
+ }
+ break;
+
+ // no icon
+ default:
+ hIcon = nullptr;
+ }
+ return hIcon;
+}
+
+/**
+ * This function fills a given POPUPACTION structure with the data of a given message id,
+ * which is normally used by the message box. This is required to let the user interact
+ * with a popup in the same way as with a normal message dialog box.
+ *
+ * @param pa - reference to a POPUPACTION structure to fill
+ * @param id - the message id
+ * @param result - This parameter is passed to the POPUPACTION structure as is.
+ *
+ * @return nothing
+ **/
+static void MakePopupAction(POPUPACTION &pa, int id)
+{
+ pa.cbSize = sizeof(POPUPACTION);
+ pa.flags = PAF_ENABLED;
+ pa.wParam = MAKEWORD(id, BN_CLICKED);
+ pa.lParam = 0;
+
+ switch (id) {
+ case IDOK:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Ok");
+ break;
+
+ case IDCLOSE:
+ case IDCANCEL:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Cancel");
+ break;
+
+ case IDABORT:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Abort");
+ break;
+
+ case IDRETRY:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_UPDATE);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Retry");
+ break;
+
+ case IDIGNORE:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Ignore");
+ break;
+
+ case IDYES:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Yes");
+ break;
+
+ case IDNO:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/No");
+ break;
+
+ case IDHELP:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/Help");
+ break;
+
+ case IDALL:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_OK);
+ mir_strcpy(pa.lpzTitle, MODNAME"/All");
+ break;
+
+ case IDNONE:
+ pa.lchIcon = IcoLib_GetIcon(ICO_BTN_CANCEL);
+ mir_strcpy(pa.lpzTitle, MODNAME"/None");
+ }
+}
+
+/**
+ * This is the message procedure for my nice looking message box
+ *
+ * @param hDlg - window handle
+ * @param uMsg - message to handle
+ * @param wParam - message specific parameter
+ * @param lParam - message specific parameter
+ *
+ * @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+ **/
+static INT_PTR CALLBACK MsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static int retOk = IDOK;
+ static int retAll = IDALL;
+ static int retNon = IDNONE;
+ static int retCancel = IDCANCEL;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+ if (PtrIsValid(pMsgBox)) {
+ int icoWidth = 0;
+ int InfoBarHeight = 0;
+ HFONT hNormalFont;
+
+ hNormalFont = (HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0);
+ if (pMsgBox->uType & MB_INFOBAR) {
+ LOGFONT lf;
+
+ // set bold font for name in description area
+ GetObject(hNormalFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ hNormalFont = CreateFontIndirect(&lf);
+
+ // set infobar's textfont
+ SendDlgItemMessage(hDlg, TXT_NAME, WM_SETFONT, (WPARAM)hNormalFont, 0);
+
+ // set infobar's logo icon
+ SendDlgItemMessage(hDlg, ICO_DLGLOGO, STM_SETIMAGE, IMAGE_ICON,
+ (pMsgBox->hiLogo ? (LPARAM)pMsgBox->hiLogo : (LPARAM)IcoLib_GetIcon(ICO_DLG_DETAILS)));
+
+ // enable headerbar
+ ShowWindow(GetDlgItem(hDlg, TXT_NAME), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, ICO_DLGLOGO), SW_SHOW);
+ }
+ else {
+ RECT rc;
+ GetClientRect(GetDlgItem(hDlg, TXT_NAME), &rc);
+ InfoBarHeight = rc.bottom;
+
+ if (pMsgBox->hiLogo)
+ SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)pMsgBox->hiLogo);
+ }
+
+ // draw the desired status icon
+ HICON hIcon = MsgLoadIcon(pMsgBox);
+ if (hIcon)
+ SendDlgItemMessage(hDlg, ICO_MSGDLG, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
+ else {
+ RECT ws;
+ GetWindowRect(GetDlgItem(hDlg, ICO_MSGDLG), &ws);
+ icoWidth = ws.right - ws.left;
+ ShowWindow(GetDlgItem(hDlg, ICO_MSGDLG), SW_HIDE);
+ }
+
+ // resize the messagebox and reorganize the buttons
+ if (HDC hDC = GetDC(hDlg)) {
+ POINT mpt = { 0, 0 };
+ RECT ws = { 0, 0, 0, 0 };
+ int txtWidth = 0, txtHeight = 0, needX, needY;
+ RECT rcDlg;
+ SIZE ts;
+ LPTSTR h, rs;
+
+ SelectObject(hDC, hNormalFont);
+ // get message text width and height
+ if (pMsgBox->ptszMsg) for (rs = h = pMsgBox->ptszMsg;; ++h) {
+ if (*h == '\n' || !*h) {
+ GetTextExtentPoint32(hDC, rs, h - rs, &ts);
+ if (ts.cx > txtWidth)
+ txtWidth = ts.cx;
+ txtHeight += ts.cy;
+ if (!*h)
+ break;
+ rs = h + 1;
+ }
+ }
+ // increase width if info text requires more
+ if ((pMsgBox->uType&MB_INFOBAR) && pMsgBox->ptszInfoText && *pMsgBox->ptszInfoText) {
+ int multiline = 0;
+ RECT rcico; GetClientRect(GetDlgItem(hDlg, ICO_DLGLOGO), &rcico);
+ rcico.right = rcico.right * 100 / 66; // padding
+ for (rs = h = pMsgBox->ptszInfoText;; ++h) {
+ if (*h == '\n' || !*h) {
+ GetTextExtentPoint32(hDC, rs, h - rs, &ts);
+ ts.cx += rcico.right;
+ if (ts.cx > txtWidth)
+ txtWidth = ts.cx;
+ if (!*h)
+ break;
+ rs = h + 1;
+ ++multiline;
+ }
+ }
+ if (!multiline)
+ SetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hDlg, TXT_NAME), GWL_STYLE) | SS_CENTERIMAGE);
+ }
+ ReleaseDC(hDlg, hDC);
+
+ // calc new dialog size
+ GetWindowRect(hDlg, &rcDlg);
+ GetWindowRect(GetDlgItem(hDlg, TXT_MESSAGE), &ws);
+ needX = txtWidth - (ws.right - ws.left) - icoWidth;
+ needY = max(0, txtHeight - (ws.bottom - ws.top) + 5);
+ rcDlg.left -= needX / 2; rcDlg.right += needX / 2;
+ rcDlg.top -= (needY - InfoBarHeight) / 2; rcDlg.bottom += (needY - InfoBarHeight) / 2;
+
+ // resize dialog window
+ MoveWindow(hDlg, rcDlg.left, rcDlg.top, rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top, FALSE);
+ ClientToScreen(hDlg, &mpt);
+
+ MoveCtrl(hDlg, STATIC_WHITERECT, -mpt.x, -mpt.y, needX, needY - InfoBarHeight);
+ MoveCtrl(hDlg, TXT_NAME, -mpt.x, -mpt.y, needX, 0);
+ MoveCtrl(hDlg, ICO_DLGLOGO, -mpt.x + needX, -mpt.y, 0, 0);
+ MoveCtrl(hDlg, ICO_MSGDLG, -mpt.x, -mpt.y - InfoBarHeight, 0, 0);
+ MoveCtrl(hDlg, TXT_MESSAGE, -mpt.x - icoWidth, -mpt.y - InfoBarHeight, needX, needY);
+ MoveCtrl(hDlg, STATIC_LINE2, -mpt.x, -mpt.y + needY - InfoBarHeight, needX, 0);
+
+ // Do pushbutton positioning
+ RECT rcOk, rcAll, rcNone, rcCancel;
+
+ // get button rectangles
+ GetWindowRect(GetDlgItem(hDlg, IDOK), &rcOk);
+ OffsetRect(&rcOk, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDALL), &rcAll);
+ OffsetRect(&rcAll, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDNONE), &rcNone);
+ OffsetRect(&rcNone, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ GetWindowRect(GetDlgItem(hDlg, IDCANCEL), &rcCancel);
+ OffsetRect(&rcCancel, -mpt.x, -mpt.y + needY - InfoBarHeight);
+
+ LONG okWidth = rcOk.right - rcOk.left;
+ LONG allWidth = rcAll.right - rcAll.left;
+ LONG noneWidth = rcNone.right - rcNone.left;
+ LONG caWidth = rcCancel.right - rcCancel.left;
+ LONG dlgMid = (rcDlg.right - rcDlg.left) / 2;
+
+ // load button configuration
+ switch (MB_TYPE(pMsgBox->uType)) {
+ case MB_OK:
+ rcOk.left = dlgMid - (okWidth / 2);
+ rcOk.right = rcOk.left + okWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ break;
+
+ case MB_OKCANCEL:
+ retOk = IDRETRY;
+ SetDlgItemText(hDlg, IDOK, LPGENW("OK"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_RETRYCANCEL:
+ retOk = IDRETRY;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Retry"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_YESNO:
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
+ retCancel = IDNO;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("No"));
+ rcOk.left = dlgMid - okWidth - 10;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = dlgMid + 10;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ retOk = IDABORT;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Abort"));
+ retAll = IDABORT;
+ SetDlgItemText(hDlg, IDALL, LPGENW("Retry"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Ignore"));
+ rcAll.left = dlgMid - (allWidth / 2);
+ rcAll.right = rcAll.left + allWidth;
+ rcOk.left = rcAll.left - okWidth - 5;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = rcAll.right + 5;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_YESNOCANCEL:
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
+ retAll = IDNO;
+ SetDlgItemText(hDlg, IDALL, LPGENW("No"));
+ retCancel = IDCANCEL;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("Cancel"));
+ rcAll.left = dlgMid - (allWidth / 2);
+ rcAll.right = rcAll.left + allWidth;
+ rcOk.left = rcAll.left - okWidth - 5;
+ rcOk.right = rcOk.left + okWidth;
+ rcCancel.left = rcAll.right + 5;
+ rcCancel.right = rcCancel.left + caWidth;
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ case MB_YESALLNO:
+ retOk = IDYES;
+ SetDlgItemText(hDlg, IDOK, LPGENW("Yes"));
+ retAll = IDALL;
+ SetDlgItemText(hDlg, IDALL, LPGENW("All"));
+ SetDlgItemText(hDlg, IDNONE, LPGENW("None"));
+ retCancel = IDNO;
+ SetDlgItemText(hDlg, IDCANCEL, LPGENW("No"));
+ rcCancel.right = rcDlg.right - rcDlg.left - 10;
+ rcCancel.left = rcCancel.right - caWidth;
+ rcNone.right = rcCancel.left - 5;
+ rcNone.left = rcNone.right - noneWidth;
+ rcAll.right = rcNone.left - 5;
+ rcAll.left = rcAll.right - allWidth;
+ rcOk.right = rcAll.left - 5;
+ rcOk.left = rcOk.right - okWidth;
+ // show buttons
+ ShowWindow(GetDlgItem(hDlg, IDOK), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDALL), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDNONE), SW_SHOW);
+ ShowWindow(GetDlgItem(hDlg, IDCANCEL), SW_SHOW);
+ break;
+
+ default:
+ rcOk.left = dlgMid - (okWidth / 2);
+ rcOk.right = rcOk.left + okWidth;
+ }
+
+ MoveWindow(GetDlgItem(hDlg, IDOK), rcOk.left, rcOk.top, rcOk.right - rcOk.left, rcOk.bottom - rcOk.top, FALSE);
+ MoveWindow(GetDlgItem(hDlg, IDALL), rcAll.left, rcAll.top, rcAll.right - rcAll.left, rcAll.bottom - rcAll.top, FALSE);
+ MoveWindow(GetDlgItem(hDlg, IDNONE), rcNone.left, rcNone.top, rcNone.right - rcNone.left, rcNone.bottom - rcNone.top, FALSE);
+ MoveWindow(GetDlgItem(hDlg, IDCANCEL), rcCancel.left, rcCancel.top, rcCancel.right - rcCancel.left, rcCancel.bottom - rcCancel.top, FALSE);
+ }
+
+ // set text's
+ SetWindowText(hDlg, pMsgBox->ptszTitle);
+ SetDlgItemText(hDlg, TXT_NAME, pMsgBox->ptszInfoText);
+ SetDlgItemText(hDlg, TXT_MESSAGE, pMsgBox->ptszMsg);
+
+ TranslateDialogDefault(hDlg);
+ return TRUE;
+ }
+ }
+ break;
+
+ case WM_CTLCOLORSTATIC:
+ switch (GetWindowLongPtr((HWND)lParam, GWLP_ID)) {
+ case STATIC_WHITERECT:
+ case ICO_DLGLOGO:
+ case ICO_MSGDLG:
+ case TXT_MESSAGE:
+ case TXT_NAME:
+ SetTextColor((HDC)wParam, GetSysColor(COLOR_WINDOWTEXT));
+ return GetSysColor(COLOR_WINDOW);
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ EndDialog(hDlg, retOk);
+ break;
+ case IDCANCEL:
+ EndDialog(hDlg, retCancel);
+ break;
+ case IDALL:
+ EndDialog(hDlg, retAll);
+ break;
+ case IDNONE:
+ EndDialog(hDlg, retNon);
+ }
+ break;
+
+ case WM_DESTROY:
+ DeleteObject((HFONT)SendDlgItemMessage(hDlg, TXT_NAME, WM_GETFONT, 0, 0));
+ break;
+ }
+ return FALSE;
+}
+
+/**
+* Dummi modal MsgBox for popup,
+* this set call function in wait stait and do not freece miranda main thread
+* the window is outside the desktop
+*/
+static INT_PTR CALLBACK MsgBoxPop(HWND hDlg, UINT uMsg, WPARAM, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ MoveWindow(hDlg, -10, -10, 0, 0, FALSE);
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)mir_alloc(sizeof(MSGPOPUPDATA));
+ if (pmpd) {
+ POPUPDATAW ppd;
+ ppd.lchContact = NULL; // (HANDLE)wParam;
+ // icon
+ ppd.lchIcon = MsgLoadIcon(pMsgBox);
+ mir_wstrncpy(ppd.lpwzContactName, pMsgBox->ptszTitle, _countof(ppd.lpwzContactName));
+ mir_wstrncpy(ppd.lpwzText, pMsgBox->ptszMsg, _countof(ppd.lpwzText));
+
+ // CALLBAC Proc
+ ppd.PluginWindowProc = PopupProc;
+ ppd.PluginData = pmpd;
+ ppd.iSeconds = -1;
+ ppd.lpActions = pmpd->pa;
+
+ // set color of popup
+ switch (pMsgBox->uType & MB_ICONMASK) {
+ case MB_ICON_ERROR:
+ ppd.colorBack = RGB(200, 10, 0);
+ ppd.colorText = RGB(255, 255, 255);
+ break;
+
+ case MB_ICON_WARNING:
+ ppd.colorBack = RGB(200, 100, 0);
+ ppd.colorText = RGB(255, 255, 255);
+ break;
+
+ default:
+ if (pMsgBox->uType & MB_CUSTOMCOLOR) {
+ ppd.colorBack = pMsgBox->colorBack;
+ ppd.colorText = pMsgBox->colorText;
+ }
+ }
+
+ // handle for MakePopupAction
+ pmpd->hDialog = hDlg;
+
+ // active buttons
+ switch (MB_TYPE(pMsgBox->uType)) {
+ case MB_OK:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK);
+ break;
+
+ case MB_OKCANCEL:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDOK);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
+ break;
+
+ case MB_RETRYCANCEL:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
+ break;
+
+ case MB_YESNO:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
+ break;
+
+ case MB_ABORTRETRYIGNORE:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDABORT);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDRETRY);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDIGNORE);
+ break;
+
+ case MB_YESNOCANCEL:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDCANCEL);
+ break;
+
+ case MB_YESALLNO:
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDYES);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDALL);
+ MakePopupAction(pmpd->pa[ppd.actionCount++], IDNO);
+ break;
+ }
+
+ // create popup
+ PUAddPopupW(&ppd);
+ if (MB_TYPE(pMsgBox->uType) == MB_OK)
+ EndDialog(hDlg, IDOK);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/**
+* This is the message procedure for popup
+*
+* @param hDlg - window handle
+* @param uMsg - message to handle
+* @param wParam - message specific parameter
+* @param lParam - message specific parameter
+*
+* @return TRUE, FALSE, IDOK, IDYES, IDALL, IDNO or IDCANCEL
+**/
+
+LRESULT CALLBACK PopupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case UM_POPUPACTION:
+ if (HIWORD(wParam) == BN_CLICKED) {
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+ if (pmpd) {
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDCANCEL:
+ case IDABORT:
+ case IDRETRY:
+ case IDIGNORE:
+ case IDYES:
+ case IDNO:
+ case IDALL:
+ case IDNONE:
+ if (IsWindow(pmpd->hDialog))
+ EndDialog(pmpd->hDialog, LOWORD(wParam));
+ break;
+
+ default:
+ if (IsWindow(pmpd->hDialog))
+ EndDialog(pmpd->hDialog, IDCANCEL);
+ }
+ }
+ PUDeletePopup(hDlg);
+ }
+ break;
+
+ case UM_FREEPLUGINDATA:
+ LPMSGPOPUPDATA pmpd = (LPMSGPOPUPDATA)PUGetPluginData(hDlg);
+ if (pmpd > 0)
+ MIR_FREE(pmpd);
+ return TRUE;
+ }
+ return DefWindowProc(hDlg, uMsg, wParam, lParam);
+}
+
+/**
+* This is the service function for external plugins to use the nice messagebox
+*
+* @param wParam - MCONTACT hContact which can display an avatar for popups
+* @param lParam - MSGBOX structure holding parameters
+*
+* @return The function returns the ID of the clicked button (IDOK, IDCANCEL, ...)
+* or -1 on error.
+**/
+INT_PTR MsgBoxService(WPARAM, LPARAM lParam)
+{
+ LPMSGBOX pMsgBox = (LPMSGBOX)lParam;
+
+ // check input
+ if (PtrIsValid(pMsgBox) && pMsgBox->cbSize == sizeof(MSGBOX)) {
+ // Shall the MessageBox displayed as popup?
+ if (!(pMsgBox->uType & (MB_INFOBAR | MB_NOPOPUP)) // message box can be a popup?
+ && myGlobals.PopupActionsExist == 1 // popup support ext stuct?
+ && (db_get_dw(0, "Popup", "Actions", 0) & 1) // popup++ actions on?
+ && db_get_b(0, MODNAME, SET_POPUPMSGBOX, DEFVAL_POPUPMSGBOX)) // user likes popups?
+ return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOXDUMMI), pMsgBox->hParent, MsgBoxPop, lParam);
+
+ return DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_MSGBOX), pMsgBox->hParent, MsgBoxProc, lParam);
+ }
+ return -1;
+}
+
+/**
+* name: MsgBox
+* desc: calls a messagebox
+* param:
+**/
+INT_PTR CALLBACK MsgBox(HWND hParent, UINT uType, LPCTSTR pszTitle, LPCTSTR pszInfo, LPCTSTR pszFormat, ...)
+{
+ wchar_t tszMsg[MAX_SECONDLINE];
+
+ va_list vl;
+ va_start(vl, pszFormat);
+ mir_vsnwprintf(tszMsg, _countof(tszMsg), TranslateW(pszFormat), vl);
+ va_end(vl);
+
+ MSGBOX mb = { 0 };
+ mb.cbSize = sizeof(MSGBOX);
+ mb.hParent = hParent;
+ mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN);
+ mb.hiMsg = nullptr;
+ mb.ptszTitle = TranslateW(pszTitle);
+ mb.ptszInfoText = TranslateW(pszInfo);
+ mb.ptszMsg = tszMsg;
+ mb.uType = uType;
+ return MsgBoxService(NULL, (LPARAM)&mb);
+}
+
+/**
+* name: MsgErr
+* desc: calls a messagebox
+* param:
+**/
+INT_PTR CALLBACK MsgErr(HWND hParent, LPCTSTR pszFormat, ...)
+{
+ wchar_t tszTitle[MAX_SECONDLINE], tszMsg[MAX_SECONDLINE];
+ mir_snwprintf(tszTitle, L"%s - %s", _A2W(MODNAME), TranslateT("Error"));
+
+ va_list vl;
+ va_start(vl, pszFormat);
+ mir_vsnwprintf(tszMsg, TranslateW(pszFormat), vl);
+ va_end(vl);
+
+ MSGBOX mb = { 0 };
+ mb.cbSize = sizeof(MSGBOX);
+ mb.hParent = hParent;
+ mb.hiLogo = IcoLib_GetIcon(ICO_COMMON_MAIN);
+ mb.hiMsg = nullptr;
+ mb.ptszTitle = tszTitle;
+ mb.ptszMsg = tszMsg;
+ mb.uType = MB_OK | MB_ICON_ERROR;
+ return MsgBoxService(NULL, (LPARAM)&mb);
+}
diff --git a/plugins/SendScreenshotPlus/src/dlg_msgbox.h b/plugins/SendScreenshotPlus/src/dlg_msgbox.h
index 68f40ea6b3..d1b31e6538 100644
--- a/plugins/SendScreenshotPlus/src/dlg_msgbox.h
+++ b/plugins/SendScreenshotPlus/src/dlg_msgbox.h
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
+© 2012-23 Miranda NG team (https://miranda-ng.org)
© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
This program is free software; you can redistribute it and/or
diff --git a/plugins/SendScreenshotPlus/src/stdafx.cxx b/plugins/SendScreenshotPlus/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SendScreenshotPlus/src/stdafx.cxx
+++ b/plugins/SendScreenshotPlus/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/plugins/SendScreenshotPlus/src/stdafx.h b/plugins/SendScreenshotPlus/src/stdafx.h
index bdf13cef1f..ecad2333c8 100644
--- a/plugins/SendScreenshotPlus/src/stdafx.h
+++ b/plugins/SendScreenshotPlus/src/stdafx.h
@@ -1,186 +1,186 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-09 Miranda ICQ/IM project,
-
-This file is part of Send Screenshot Plus, a Miranda IM plugin.
-Copyright (c) 2010 Ing.U.Horn
-
-Parts of this file based on original sorce code
-(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-#ifndef _GLOBAL_H_
-#define _GLOBAL_H_
-
-#define OEMRESOURCE
-
-#include <windows.h>
-#include <Windowsx.h>
-#include <commctrl.h>
-#include <time.h>
-#include <Shlwapi.h>
-#include <gdiplus.h>
-#include <mapi.h>
-#include <UxTheme.h>
-
-#include <map>
-#include <string>
-using namespace std;
-
-#include <msapi/vsstyle.h>
-#include <msapi/vssym32.h>
-
-#include <newpluginapi.h>
-#include <m_button.h>
-#include <m_chat_int.h>
-#include <m_clist.h>
-#include <m_contacts.h>
-#include <m_database.h>
-#include <m_hotkeys.h>
-#include <m_imgsrvc.h>
-#include <m_langpack.h>
-#include <m_netlib.h>
-#include <m_protosvc.h>
-#include <m_skin.h>
-#include <m_json.h>
-#include <m_popup.h>
-#include <m_icolib.h>
-#include <m_message.h>
-
-#include <m_folders.h>
-#include <m_HTTPServer.h>
-#include <m_ftpfile.h>
-#include <m_sendss.h>
-#include <m_userinfoex.h>
-#include <m_cloudfile.h>
-
-#include "ctrl_button.h"
-#include "dlg_msgbox.h"
-#include "resource.h"
-#include "version.h"
-#include "CSend.h"
-#include "CSendEmail.h"
-#include "CSendFile.h"
-#include "CSendFTPFile.h"
-#include "CSendHTTPServer.h"
-#include "CSendCloduFile.h"
-#include "CSendHost_ImageShack.h"
-#include "CSendHost_uploadpie.h"
-#include "CSendHost_imgur.h"
-#include "DevKey.h"
-#include "UMainForm.h"
-#include "Utils.h"
-
-#define UM_CLOSING WM_USER+1
-#define UM_EVENT WM_USER+2
-
-// Generic Message Box for Errors
-#define MSGERROR(text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONERROR)
-#define MSGINFO (text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONINFORMATION)
-
-typedef struct _MGLOBAL {
- uint32_t mirandaVersion; // mirandaVersion
- BOOLEAN PopupActionsExist : 1; // Popup+ or MS_POPUP_REGISTERACTIONS exist
- BOOLEAN PluginHTTPExist : 1; // HTTPServer or MS_HTTP_ACCEPT_CONNECTIONS exist
- BOOLEAN PluginFTPExist : 1; // FTPFile or MS_FTPFILE_UPLOAD exist
- BOOLEAN PluginCloudFileExist: 1; // CloudFile or MS_CLOUDFILE_UPLOAD exists
-
-} MGLOBAL, *LPMGLOBAL;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-#define ERROR_TITLE TranslateT("SendScreenshot - Error")
-
-// Miranda Database Key
-#define MODULENAME "SendSS"
-
-struct CMPlugin : public PLUGIN<CMPlugin>
-{
- CMPlugin();
-
- int Load() override;
- int Unload() override;
-};
-
-extern ATOM g_clsTargetHighlighter;
-extern MGLOBAL g_myGlobals;
-extern HNETLIBUSER g_hNetlibUser;
-
-enum
-{
- ICO_MAIN = 0,
- ICO_MAINXS,
- ICO_TARGET,
- ICO_MONITOR,
- ICO_END_,
- ICO_BTN_HELP = 0,
- ICO_BTN_FOLDER,
- ICO_BTN_DESC,
- ICO_BTN_DESCON,
- ICO_BTN_DEL,
- ICO_BTN_DELON,
- ICO_BTN_ARROWL,
- ICO_BTN_ARROWR,
- ICO_BTN_UPDATE,
- ICO_BTN_OK,
- ICO_BTN_CANCEL,
- ICO_BTN_EDIT,
- ICO_BTN_EDITON,
- ICO_BTN_COPY,
- ICO_BTN_BBC,
- ICO_BTN_BBCLNK,
- ICO_BTN_DOWNARROW,
- ICO_BTN_END_,
-};
-#define GetIconHandle(ico) ICONS[ico].hIcolib
-#define GetIcon(ico) IcoLib_GetIconByHandle(GetIconHandle(ico))
-extern IconItem ICONS[ICO_END_];
-#define GetIconBtnHandle(ico) ICONS_BTN[ico].hIcolib
-#define GetIconBtn(ico) IcoLib_GetIconByHandle(GetIconBtnHandle(ico))
-extern IconItem ICONS_BTN[ICO_BTN_END_];
-
-#define PtrIsValid(p) (((p)!=0)&&(((HANDLE)(p))!=INVALID_HANDLE_VALUE))
-#define MIR_FREE(p) {if (PtrIsValid(p)){mir_free((void*)p);(p)=NULL;}}
-#ifdef _DEBUG
-# define DBGMSG(str,...) do{char tmp[1024];sprintf(tmp,str,##__VA_ARGS__);OutputDebugStringA(tmp);}while(0)
-#else
-# define DBGMSG(str,...)
-#endif
-
-void ComboBox_SelectItem(HWND hCombo, LPARAM data);
-
-template<class _Elem>
-std::basic_string<_Elem> replace(const std::basic_string<_Elem> & Origninal, const std::basic_string<_Elem> & What, const std::basic_string<_Elem> & With)
-{
- std::basic_string<_Elem> res;
- size_t l = 0;
- for (size_t p = Origninal.find(What.c_str(), 0); p != std::basic_string<_Elem>::npos; p = Origninal.find(What.c_str(), l))
- {
- if (l != p)
- res.append(Origninal.c_str() + l, p - l);
- res.append(With);
- l = p + What.length();
- }
- if (l < Origninal.length())
- res.append(Origninal.c_str() + l);
-
- return res;
-}
-
-#endif
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-09 Miranda ICQ/IM project,
+
+This file is part of Send Screenshot Plus, a Miranda IM plugin.
+Copyright (c) 2010 Ing.U.Horn
+
+Parts of this file based on original sorce code
+(c) 2004-2006 Sérgio Vieira Rolanski (portet from Borland C++)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _GLOBAL_H_
+#define _GLOBAL_H_
+
+#define OEMRESOURCE
+
+#include <windows.h>
+#include <Windowsx.h>
+#include <commctrl.h>
+#include <time.h>
+#include <Shlwapi.h>
+#include <gdiplus.h>
+#include <mapi.h>
+#include <UxTheme.h>
+
+#include <map>
+#include <string>
+using namespace std;
+
+#include <msapi/vsstyle.h>
+#include <msapi/vssym32.h>
+
+#include <newpluginapi.h>
+#include <m_button.h>
+#include <m_chat_int.h>
+#include <m_clist.h>
+#include <m_contacts.h>
+#include <m_database.h>
+#include <m_hotkeys.h>
+#include <m_imgsrvc.h>
+#include <m_langpack.h>
+#include <m_netlib.h>
+#include <m_protosvc.h>
+#include <m_skin.h>
+#include <m_json.h>
+#include <m_popup.h>
+#include <m_icolib.h>
+#include <m_message.h>
+
+#include <m_folders.h>
+#include <m_HTTPServer.h>
+#include <m_ftpfile.h>
+#include <m_sendss.h>
+#include <m_userinfoex.h>
+#include <m_cloudfile.h>
+
+#include "ctrl_button.h"
+#include "dlg_msgbox.h"
+#include "resource.h"
+#include "version.h"
+#include "CSend.h"
+#include "CSendEmail.h"
+#include "CSendFile.h"
+#include "CSendFTPFile.h"
+#include "CSendHTTPServer.h"
+#include "CSendCloduFile.h"
+#include "CSendHost_ImageShack.h"
+#include "CSendHost_uploadpie.h"
+#include "CSendHost_imgur.h"
+#include "DevKey.h"
+#include "UMainForm.h"
+#include "Utils.h"
+
+#define UM_CLOSING WM_USER+1
+#define UM_EVENT WM_USER+2
+
+// Generic Message Box for Errors
+#define MSGERROR(text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONERROR)
+#define MSGINFO (text) MessageBox(NULL, text, L"SendSS", MB_OK | MB_ICONINFORMATION)
+
+typedef struct _MGLOBAL {
+ uint32_t mirandaVersion; // mirandaVersion
+ BOOLEAN PopupActionsExist : 1; // Popup+ or MS_POPUP_REGISTERACTIONS exist
+ BOOLEAN PluginHTTPExist : 1; // HTTPServer or MS_HTTP_ACCEPT_CONNECTIONS exist
+ BOOLEAN PluginFTPExist : 1; // FTPFile or MS_FTPFILE_UPLOAD exist
+ BOOLEAN PluginCloudFileExist: 1; // CloudFile or MS_CLOUDFILE_UPLOAD exists
+
+} MGLOBAL, *LPMGLOBAL;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define ERROR_TITLE TranslateT("SendScreenshot - Error")
+
+// Miranda Database Key
+#define MODULENAME "SendSS"
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
+extern ATOM g_clsTargetHighlighter;
+extern MGLOBAL g_myGlobals;
+extern HNETLIBUSER g_hNetlibUser;
+
+enum
+{
+ ICO_MAIN = 0,
+ ICO_MAINXS,
+ ICO_TARGET,
+ ICO_MONITOR,
+ ICO_END_,
+ ICO_BTN_HELP = 0,
+ ICO_BTN_FOLDER,
+ ICO_BTN_DESC,
+ ICO_BTN_DESCON,
+ ICO_BTN_DEL,
+ ICO_BTN_DELON,
+ ICO_BTN_ARROWL,
+ ICO_BTN_ARROWR,
+ ICO_BTN_UPDATE,
+ ICO_BTN_OK,
+ ICO_BTN_CANCEL,
+ ICO_BTN_EDIT,
+ ICO_BTN_EDITON,
+ ICO_BTN_COPY,
+ ICO_BTN_BBC,
+ ICO_BTN_BBCLNK,
+ ICO_BTN_DOWNARROW,
+ ICO_BTN_END_,
+};
+#define GetIconHandle(ico) ICONS[ico].hIcolib
+#define GetIcon(ico) IcoLib_GetIconByHandle(GetIconHandle(ico))
+extern IconItem ICONS[ICO_END_];
+#define GetIconBtnHandle(ico) ICONS_BTN[ico].hIcolib
+#define GetIconBtn(ico) IcoLib_GetIconByHandle(GetIconBtnHandle(ico))
+extern IconItem ICONS_BTN[ICO_BTN_END_];
+
+#define PtrIsValid(p) (((p)!=0)&&(((HANDLE)(p))!=INVALID_HANDLE_VALUE))
+#define MIR_FREE(p) {if (PtrIsValid(p)){mir_free((void*)p);(p)=NULL;}}
+#ifdef _DEBUG
+# define DBGMSG(str,...) do{char tmp[1024];sprintf(tmp,str,##__VA_ARGS__);OutputDebugStringA(tmp);}while(0)
+#else
+# define DBGMSG(str,...)
+#endif
+
+void ComboBox_SelectItem(HWND hCombo, LPARAM data);
+
+template<class _Elem>
+std::basic_string<_Elem> replace(const std::basic_string<_Elem> & Origninal, const std::basic_string<_Elem> & What, const std::basic_string<_Elem> & With)
+{
+ std::basic_string<_Elem> res;
+ size_t l = 0;
+ for (size_t p = Origninal.find(What.c_str(), 0); p != std::basic_string<_Elem>::npos; p = Origninal.find(What.c_str(), l))
+ {
+ if (l != p)
+ res.append(Origninal.c_str() + l, p - l);
+ res.append(With);
+ l = p + What.length();
+ }
+ if (l < Origninal.length())
+ res.append(Origninal.c_str() + l);
+
+ return res;
+}
+
+#endif
diff --git a/plugins/Sessions/Src/Import.cpp b/plugins/Sessions/Src/Import.cpp
index 47b2d80159..6eac8c639e 100644
--- a/plugins/Sessions/Src/Import.cpp
+++ b/plugins/Sessions/Src/Import.cpp
@@ -1,134 +1,134 @@
-/*
-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"
-
-bool LoadContactsFromMask(MCONTACT hContact, int mode, int count)
-{
- CMStringA szValue;
- if (mode == 0)
- szValue = g_plugin.getMStringA(hContact, "LastSessionsMarks");
- else if (mode == 1)
- szValue = g_plugin.getMStringA(hContact, "UserSessionsMarks");
-
- if (szValue.IsEmpty())
- return false;
-
- return szValue[count] == '1';
-}
-
-int GetInSessionOrder(MCONTACT hContact, int mode, int count)
-{
- char szTemp[3] = { 0, 0, 0 };
- count *= 2;
-
- if (mode == 0) {
- CMStringA szValue(g_plugin.getMStringA(hContact, "LastSessionsOrder"));
- if (!szValue.IsEmpty() && count < szValue.GetLength()) {
- memcpy(szTemp, szValue.c_str() + count, 2);
- return atoi(szTemp);
- }
- }
- else if (mode == 1) {
- CMStringA szValue(g_plugin.getMStringA(hContact, "UserSessionsOrder"));
- if (!szValue.IsEmpty() && count < szValue.GetLength()) {
- memcpy(szTemp, szValue.c_str() + count, 2);
- return atoi(szTemp);
- }
- }
- return 0;
-}
-
-void CMPlugin::CheckImport()
-{
- if (db_get_b(0, "Compatibility", MODULENAME) > 0)
- return;
-
- MCONTACT tmp[255];
-
- for (int i = 0;; i++) {
- char szSetting[100];
- mir_snprintf(szSetting, "SessionDate_%d", i);
- CMStringW wszName(getMStringW(szSetting));
- if (wszName.IsEmpty())
- break;
-
- delSetting(szSetting);
-
- memset(tmp, 0, sizeof(tmp));
- for (auto &hContact : Contacts()) {
- if (LoadContactsFromMask(hContact, 0, i)) {
- int idx = GetInSessionOrder(hContact, 0, i);
- if (idx < _countof(tmp))
- tmp[idx] = hContact;
- }
- }
- if (tmp[0] == 0)
- continue;
-
- CSession *pNew = new CSession();
- pNew->id = 255 - i;
- pNew->wszName = wszName;
- for (int k = 0; tmp[k]; k++)
- pNew->contacts.push_back(tmp[k]);
-
- pNew->save();
- }
-
- for (int i = 0;; i++) {
- char szSetting[100];
- mir_snprintf(szSetting, "UserSessionDsc_%d", i);
- CMStringW wszName(getMStringW(szSetting));
- if (wszName.IsEmpty())
- break;
-
- delSetting(szSetting);
-
- memset(tmp, 0, sizeof(tmp));
- for (auto &hContact : Contacts()) {
- if (LoadContactsFromMask(hContact, 1, i)) {
- int idx = GetInSessionOrder(hContact, 1, i);
- if (idx < _countof(tmp))
- tmp[idx] = hContact;
- }
- }
- if (tmp[0] == 0)
- continue;
-
- CSession *pNew = new CSession();
- pNew->id = 255 - i;
- pNew->bIsUser = true;
- pNew->wszName = wszName;
-
- mir_snprintf(szSetting, "FavUserSession_%d", i);
- pNew->bIsFavorite = getBool(szSetting);
- delSetting(szSetting);
-
- for (int k = 0; tmp[k]; k++)
- pNew->contacts.push_back(tmp[k]);
-
- pNew->save();
- }
-
- delSetting("UserSessionsCount");
-
- for (auto &hContact : Contacts())
- db_delete_module(hContact, MODULENAME);
-
- g_lastDateId = g_lastUserId = 256;
- db_set_b(0, "Compatibility", MODULENAME, 1);
-}
+/*
+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"
+
+bool LoadContactsFromMask(MCONTACT hContact, int mode, int count)
+{
+ CMStringA szValue;
+ if (mode == 0)
+ szValue = g_plugin.getMStringA(hContact, "LastSessionsMarks");
+ else if (mode == 1)
+ szValue = g_plugin.getMStringA(hContact, "UserSessionsMarks");
+
+ if (szValue.IsEmpty())
+ return false;
+
+ return szValue[count] == '1';
+}
+
+int GetInSessionOrder(MCONTACT hContact, int mode, int count)
+{
+ char szTemp[3] = { 0, 0, 0 };
+ count *= 2;
+
+ if (mode == 0) {
+ CMStringA szValue(g_plugin.getMStringA(hContact, "LastSessionsOrder"));
+ if (!szValue.IsEmpty() && count < szValue.GetLength()) {
+ memcpy(szTemp, szValue.c_str() + count, 2);
+ return atoi(szTemp);
+ }
+ }
+ else if (mode == 1) {
+ CMStringA szValue(g_plugin.getMStringA(hContact, "UserSessionsOrder"));
+ if (!szValue.IsEmpty() && count < szValue.GetLength()) {
+ memcpy(szTemp, szValue.c_str() + count, 2);
+ return atoi(szTemp);
+ }
+ }
+ return 0;
+}
+
+void CMPlugin::CheckImport()
+{
+ if (db_get_b(0, "Compatibility", MODULENAME) > 0)
+ return;
+
+ MCONTACT tmp[255];
+
+ for (int i = 0;; i++) {
+ char szSetting[100];
+ mir_snprintf(szSetting, "SessionDate_%d", i);
+ CMStringW wszName(getMStringW(szSetting));
+ if (wszName.IsEmpty())
+ break;
+
+ delSetting(szSetting);
+
+ memset(tmp, 0, sizeof(tmp));
+ for (auto &hContact : Contacts()) {
+ if (LoadContactsFromMask(hContact, 0, i)) {
+ int idx = GetInSessionOrder(hContact, 0, i);
+ if (idx < _countof(tmp))
+ tmp[idx] = hContact;
+ }
+ }
+ if (tmp[0] == 0)
+ continue;
+
+ CSession *pNew = new CSession();
+ pNew->id = 255 - i;
+ pNew->wszName = wszName;
+ for (int k = 0; tmp[k]; k++)
+ pNew->contacts.push_back(tmp[k]);
+
+ pNew->save();
+ }
+
+ for (int i = 0;; i++) {
+ char szSetting[100];
+ mir_snprintf(szSetting, "UserSessionDsc_%d", i);
+ CMStringW wszName(getMStringW(szSetting));
+ if (wszName.IsEmpty())
+ break;
+
+ delSetting(szSetting);
+
+ memset(tmp, 0, sizeof(tmp));
+ for (auto &hContact : Contacts()) {
+ if (LoadContactsFromMask(hContact, 1, i)) {
+ int idx = GetInSessionOrder(hContact, 1, i);
+ if (idx < _countof(tmp))
+ tmp[idx] = hContact;
+ }
+ }
+ if (tmp[0] == 0)
+ continue;
+
+ CSession *pNew = new CSession();
+ pNew->id = 255 - i;
+ pNew->bIsUser = true;
+ pNew->wszName = wszName;
+
+ mir_snprintf(szSetting, "FavUserSession_%d", i);
+ pNew->bIsFavorite = getBool(szSetting);
+ delSetting(szSetting);
+
+ for (int k = 0; tmp[k]; k++)
+ pNew->contacts.push_back(tmp[k]);
+
+ pNew->save();
+ }
+
+ delSetting("UserSessionsCount");
+
+ for (auto &hContact : Contacts())
+ db_delete_module(hContact, MODULENAME);
+
+ g_lastDateId = g_lastUserId = 256;
+ db_set_b(0, "Compatibility", MODULENAME, 1);
+}
diff --git a/plugins/Sessions/Src/LoadSessions.cpp b/plugins/Sessions/Src/LoadSessions.cpp
index de9e428d0a..690ff664d2 100644
--- a/plugins/Sessions/Src/LoadSessions.cpp
+++ b/plugins/Sessions/Src/LoadSessions.cpp
@@ -1,227 +1,227 @@
-/*
-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"
-
-static bool g_bDontShow, g_bStartup;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-class CLoadSessionDlg : public CDlgBase
-{
- CSession *pSession = nullptr;
-
- CTimer timerShow, timerLoad;
- CCtrlCombo m_list;
- CCtrlButton btnDelete;
-
-public:
- CLoadSessionDlg() :
- CDlgBase(g_plugin, IDD_WLCMDIALOG),
- m_list(this, IDC_LIST),
- btnDelete(this, IDC_SESSDEL),
- timerLoad(this, TIMERID_LOAD),
- timerShow(this, TIMERID_SHOW)
- {
- btnDelete.OnClick = Callback(this, &CLoadSessionDlg::onClick_Delete);
-
- m_list.OnSelChanged = Callback(this, &CLoadSessionDlg::onSelChange_List);
-
- timerLoad.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Load);
- timerShow.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Show);
- }
-
- bool OnInitDialog() override
- {
- g_hDlg = m_hwnd;
-
- LoadSessionToCombobox(m_list, false);
- LoadSessionToCombobox(m_list, true);
- m_list.SetCurSel(0);
- pSession = (CSession *)m_list.GetItemData(0);
-
- int iDelay = g_plugin.getWord("StartupModeDelay", 1500);
- if (g_bDontShow == TRUE)
- timerLoad.Start(iDelay);
- else {
- LoadPosition(m_hwnd, "LoadDlg");
- if (g_bStartup)
- timerShow.Start(iDelay);
- else
- Show();
- }
-
- return true;
- }
-
- bool OnApply() override
- {
- if (!LoadSession(pSession))
- return true;
-
- return false;
- }
-
- void OnDestroy() override
- {
- SavePosition(m_hwnd, "LoadDlg");
- g_hDlg = nullptr;
- }
-
- void onTimer_Load(CTimer *pTimer)
- {
- pTimer->Stop();
- LoadSession(pSession);
- g_bDontShow = g_bStartup = false;
- Close();
- }
-
- void onTimer_Show(CTimer *pTimer)
- {
- pTimer->Stop();
- Show();
- g_bStartup = FALSE;
- }
-
- void onSelChange_List(CCtrlCombo *)
- {
- int index = m_list.GetCurSel();
- if (index != CB_ERR)
- pSession = (CSession*)m_list.GetItemData(index);
- }
-
- void onClick_Delete(CCtrlButton *)
- {
- if (pSession == nullptr) {
- if (session_list_recovered[0]) {
- for (int i = 0; session_list_recovered[i]; i++)
- g_plugin.setByte(session_list_recovered[i], "wasInLastSession", 0);
- memset(session_list_recovered, 0, sizeof(session_list_recovered));
- }
- g_bIncompletedSave = 0;
- }
- else pSession->remove();
-
- m_list.ResetContent();
- LoadSessionToCombobox(m_list, false);
- LoadSessionToCombobox(m_list, true);
-
- m_list.SetCurSel(0);
- btnDelete.Enable(m_list.GetCount() != 0);
- }
-};
-
-INT_PTR OpenSessionsManagerWindow(WPARAM, LPARAM)
-{
- if (g_hDlg) {
- ShowWindow(g_hDlg, SW_SHOW);
- return 0;
- }
-
- if (g_bIncompletedSave || g_arDateSessions.getCount() || g_arUserSessions.getCount()) {
- (new CLoadSessionDlg())->Create();
- return 0;
- }
-
- if (g_bOtherWarnings)
- MessageBox(nullptr, TranslateT("No sessions to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
- return 1;
-}
-
-void CALLBACK LaunchSessions()
-{
- int startup = g_plugin.getByte("StartupMode", 3);
- if (startup == 1 || (startup == 3 && g_bLastSessionPresent)) {
- g_bStartup = true;
- (new CLoadSessionDlg())->Create();
- }
- else if (startup == 2 && g_bLastSessionPresent) {
- g_bDontShow = true;
- (new CLoadSessionDlg())->Create();
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-INT_PTR LoadLastSession(WPARAM, LPARAM)
-{
- int cnt = g_arDateSessions.getCount();
- if (g_bLastSessionPresent && cnt)
- return LoadSession(&g_arDateSessions[cnt-1]);
-
- if (g_bOtherWarnings)
- MessageBox(nullptr, TranslateT("Last Sessions is empty"), TranslateT("Sessions Manager"), MB_OK);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Load session dialog
-
-int LoadSession(CSession *pSession)
-{
- int dup = 0;
- int hidden[255] = { '0' };
-
- MCONTACT session_list_t[255] = {};
- if (session_list_recovered[0] && pSession == nullptr)
- memcpy(session_list_t, session_list_recovered, sizeof(session_list_t));
- else {
- int i = 0;
- for (auto &cc : pSession->contacts)
- session_list_t[i++] = cc;
- }
-
- int i = 0, j = 0;
- // TODO: change to "switch"
- while (session_list_t[i] != 0) {
- if (CheckForDuplicate(session_list, session_list_t[i]) == -1)
- Clist_ContactDoubleClicked(session_list_t[i]);
- else if (g_bWarnOnHidden) {
- if (!CheckContactVisibility(session_list_t[i])) {
- hidden[j] = i + 1;
- j++;
- }
- dup++;
- }
- i++;
- }
-
- if (i == 0) {
- if (g_bOtherWarnings)
- MessageBox(nullptr, TranslateT("No contacts to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
- return 1;
- }
-
- if (dup == i) {
- if (!hidden[i]) {
- if (g_bOtherWarnings)
- MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
- return 1;
- }
- if (!g_bWarnOnHidden && g_bOtherWarnings) {
- MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
- return 1;
- }
- if (g_bWarnOnHidden) {
- if (IDYES == MessageBox(nullptr, TranslateT("This Session already opened (but probably hidden).\nDo you want to show hidden contacts?"), TranslateT("Sessions Manager"), MB_YESNO | MB_ICONQUESTION))
- for (j = 0; hidden[j] != 0; j++)
- Clist_ContactDoubleClicked(session_list_t[hidden[j] - 1]);
- }
- }
-
- return 0;
-}
+/*
+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"
+
+static bool g_bDontShow, g_bStartup;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class CLoadSessionDlg : public CDlgBase
+{
+ CSession *pSession = nullptr;
+
+ CTimer timerShow, timerLoad;
+ CCtrlCombo m_list;
+ CCtrlButton btnDelete;
+
+public:
+ CLoadSessionDlg() :
+ CDlgBase(g_plugin, IDD_WLCMDIALOG),
+ m_list(this, IDC_LIST),
+ btnDelete(this, IDC_SESSDEL),
+ timerLoad(this, TIMERID_LOAD),
+ timerShow(this, TIMERID_SHOW)
+ {
+ btnDelete.OnClick = Callback(this, &CLoadSessionDlg::onClick_Delete);
+
+ m_list.OnSelChanged = Callback(this, &CLoadSessionDlg::onSelChange_List);
+
+ timerLoad.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Load);
+ timerShow.OnEvent = Callback(this, &CLoadSessionDlg::onTimer_Show);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_hDlg = m_hwnd;
+
+ LoadSessionToCombobox(m_list, false);
+ LoadSessionToCombobox(m_list, true);
+ m_list.SetCurSel(0);
+ pSession = (CSession *)m_list.GetItemData(0);
+
+ int iDelay = g_plugin.getWord("StartupModeDelay", 1500);
+ if (g_bDontShow == TRUE)
+ timerLoad.Start(iDelay);
+ else {
+ LoadPosition(m_hwnd, "LoadDlg");
+ if (g_bStartup)
+ timerShow.Start(iDelay);
+ else
+ Show();
+ }
+
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ if (!LoadSession(pSession))
+ return true;
+
+ return false;
+ }
+
+ void OnDestroy() override
+ {
+ SavePosition(m_hwnd, "LoadDlg");
+ g_hDlg = nullptr;
+ }
+
+ void onTimer_Load(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ LoadSession(pSession);
+ g_bDontShow = g_bStartup = false;
+ Close();
+ }
+
+ void onTimer_Show(CTimer *pTimer)
+ {
+ pTimer->Stop();
+ Show();
+ g_bStartup = FALSE;
+ }
+
+ void onSelChange_List(CCtrlCombo *)
+ {
+ int index = m_list.GetCurSel();
+ if (index != CB_ERR)
+ pSession = (CSession*)m_list.GetItemData(index);
+ }
+
+ void onClick_Delete(CCtrlButton *)
+ {
+ if (pSession == nullptr) {
+ if (session_list_recovered[0]) {
+ for (int i = 0; session_list_recovered[i]; i++)
+ g_plugin.setByte(session_list_recovered[i], "wasInLastSession", 0);
+ memset(session_list_recovered, 0, sizeof(session_list_recovered));
+ }
+ g_bIncompletedSave = 0;
+ }
+ else pSession->remove();
+
+ m_list.ResetContent();
+ LoadSessionToCombobox(m_list, false);
+ LoadSessionToCombobox(m_list, true);
+
+ m_list.SetCurSel(0);
+ btnDelete.Enable(m_list.GetCount() != 0);
+ }
+};
+
+INT_PTR OpenSessionsManagerWindow(WPARAM, LPARAM)
+{
+ if (g_hDlg) {
+ ShowWindow(g_hDlg, SW_SHOW);
+ return 0;
+ }
+
+ if (g_bIncompletedSave || g_arDateSessions.getCount() || g_arUserSessions.getCount()) {
+ (new CLoadSessionDlg())->Create();
+ return 0;
+ }
+
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("No sessions to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+}
+
+void CALLBACK LaunchSessions()
+{
+ int startup = g_plugin.getByte("StartupMode", 3);
+ if (startup == 1 || (startup == 3 && g_bLastSessionPresent)) {
+ g_bStartup = true;
+ (new CLoadSessionDlg())->Create();
+ }
+ else if (startup == 2 && g_bLastSessionPresent) {
+ g_bDontShow = true;
+ (new CLoadSessionDlg())->Create();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+INT_PTR LoadLastSession(WPARAM, LPARAM)
+{
+ int cnt = g_arDateSessions.getCount();
+ if (g_bLastSessionPresent && cnt)
+ return LoadSession(&g_arDateSessions[cnt-1]);
+
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("Last Sessions is empty"), TranslateT("Sessions Manager"), MB_OK);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load session dialog
+
+int LoadSession(CSession *pSession)
+{
+ int dup = 0;
+ int hidden[255] = { '0' };
+
+ MCONTACT session_list_t[255] = {};
+ if (session_list_recovered[0] && pSession == nullptr)
+ memcpy(session_list_t, session_list_recovered, sizeof(session_list_t));
+ else {
+ int i = 0;
+ for (auto &cc : pSession->contacts)
+ session_list_t[i++] = cc;
+ }
+
+ int i = 0, j = 0;
+ // TODO: change to "switch"
+ while (session_list_t[i] != 0) {
+ if (CheckForDuplicate(session_list, session_list_t[i]) == -1)
+ Clist_ContactDoubleClicked(session_list_t[i]);
+ else if (g_bWarnOnHidden) {
+ if (!CheckContactVisibility(session_list_t[i])) {
+ hidden[j] = i + 1;
+ j++;
+ }
+ dup++;
+ }
+ i++;
+ }
+
+ if (i == 0) {
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("No contacts to open"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+ }
+
+ if (dup == i) {
+ if (!hidden[i]) {
+ if (g_bOtherWarnings)
+ MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+ }
+ if (!g_bWarnOnHidden && g_bOtherWarnings) {
+ MessageBox(nullptr, TranslateT("This Session already opened"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return 1;
+ }
+ if (g_bWarnOnHidden) {
+ if (IDYES == MessageBox(nullptr, TranslateT("This Session already opened (but probably hidden).\nDo you want to show hidden contacts?"), TranslateT("Sessions Manager"), MB_YESNO | MB_ICONQUESTION))
+ for (j = 0; hidden[j] != 0; j++)
+ Clist_ContactDoubleClicked(session_list_t[hidden[j] - 1]);
+ }
+ }
+
+ return 0;
+}
diff --git a/plugins/Sessions/Src/SaveSessions.cpp b/plugins/Sessions/Src/SaveSessions.cpp
index 4d195b4c83..c9792d8ee2 100644
--- a/plugins/Sessions/Src/SaveSessions.cpp
+++ b/plugins/Sessions/Src/SaveSessions.cpp
@@ -1,194 +1,194 @@
-/*
-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"
-
-HWND g_hSDlg;
-
-bool bSC = false;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static int SaveUserSessionName(MCONTACT *contacts, wchar_t *szUSessionName)
-{
- if (contacts[0] == 0)
- return 1;
-
- CSession *pSession = nullptr;
- for (auto &it : g_arUserSessions)
- if (!it->wszName.CompareNoCase(szUSessionName))
- pSession = it;
-
- if (pSession)
- pSession->contacts.clear();
- else {
- g_plugin.g_lastUserId = g_plugin.g_lastUserId + 1;
-
- pSession = new CSession();
- pSession->id = g_plugin.g_lastUserId;
- pSession->bIsUser = true;
- pSession->wszName = szUSessionName;
- g_arUserSessions.insert(pSession);
- }
-
- for (int i = 0; contacts[i]; i++)
- pSession->contacts.push_back(contacts[i]);
-
- pSession->save();
-
- while (g_arUserSessions.getCount() > g_ses_limit) {
- g_arUserSessions[0].remove();
- g_arUserSessions.remove(int(0));
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Save session dialog
-
-class CSessionDlg : public CDlgBase
-{
- CCtrlClc m_clist;
- CCtrlCombo m_sessions;
- CCtrlCheck chkSelContacts, chkSaveAndClose;
-
- MCONTACT user_session_list[255];
-
-public:
- CSessionDlg() :
- CDlgBase(g_plugin, IDD_SAVEDIALOG),
- m_clist(this, IDC_CLIST),
- m_sessions(this, IDC_LIST),
- chkSelContacts(this, IDC_SELCONTACTS),
- chkSaveAndClose(this, IDC_SANDCCHECK)
- {
- memset(user_session_list, 0, sizeof(user_session_list));
-
- chkSelContacts.OnChange = Callback(this, &CSessionDlg::onChange_SelContacts);
-
- m_clist.OnCheckChanged = Callback(this, &CSessionDlg::onCheckChanged_Clist);
- }
-
- bool OnInitDialog() override
- {
- g_hSDlg = m_hwnd;
- LoadSessionToCombobox(m_sessions, true);
-
- SetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE,
- GetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE) | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
- m_clist.SetExStyle(CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT);
- m_clist.AutoRebuild();
-
- LoadPosition(m_hwnd, "SaveDlg");
- return true;
- }
-
- bool OnApply() override
- {
- wchar_t szUserSessionName[MAX_PATH];
- m_sessions.GetText(szUserSessionName, _countof(szUserSessionName));
- rtrimw(szUserSessionName);
- if (szUserSessionName[0] == 0) {
- MessageBox(nullptr, TranslateT("Session name is empty, enter the name and try again"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
- return false;
- }
-
- if (chkSelContacts.IsChecked() && bSC) {
- int i = 0;
- for (auto &hContact : Contacts()) {
- uint8_t res = m_clist.GetCheck(m_clist.FindContact(hContact));
- if (res) {
- user_session_list[i] = hContact;
- i++;
- }
- }
-
- SaveUserSessionName(user_session_list, szUserSessionName);
- return true;
- }
-
- if (!SaveUserSessionName(session_list, szUserSessionName)) {
- if (chkSaveAndClose.IsChecked())
- CloseCurrentSession(0, 0);
- return true;
- }
-
- MessageBox(nullptr, TranslateT("Current session is empty!"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
- return false;
- }
-
- void OnDestroy() override
- {
- SavePosition(m_hwnd, "SaveDlg");
- g_hSDlg = nullptr;
- }
-
- void onCheckChanged_Clist(CCtrlClc*)
- {
- bSC = TRUE;
- memcpy(user_session_list, session_list, sizeof(user_session_list));
- }
-
- void onChange_SelContacts(CCtrlCheck *)
- {
- if (!m_bInitialized)
- return;
-
- RECT rWnd;
- GetWindowRect(m_hwnd, &rWnd);
-
- int x = rWnd.right - rWnd.left, y = rWnd.bottom - rWnd.top, dy, dx, dd;
-
- if (chkSelContacts.IsChecked()) {
- chkSaveAndClose.Disable();
- dy = 20;
- dx = 150;
- dd = 5;
- m_clist.Show();
- }
- else {
- chkSaveAndClose.Enable();
- dy = -20;
- dx = -150;
- dd = 5;
- m_clist.Hide();
- }
-
- SetWindowPos(m_hwnd, nullptr, rWnd.left, rWnd.top, x + dx, y + (dx / 3), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE);
- SetWindowPos(m_clist.GetHwnd(), nullptr, x - dd, dd, dx - dd, y + (dx / 12), SWP_NOZORDER);
-
- for (int i = 0; session_list[i] > 0; i++)
- m_clist.SetCheck(m_clist.FindContact(session_list[i]), 1);
-
- OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_LIST), 0, dy);
- OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_STATIC), 0, dy);
- OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_SANDCCHECK), 0, dy);
- OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDOK), 0, dy);
- OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDCANCEL), 0, dy);
- }
-};
-
-INT_PTR SaveUserSessionHandles(WPARAM, LPARAM)
-{
- if (g_hSDlg) {
- ShowWindow(g_hSDlg, SW_SHOW);
- return 1;
- }
-
- (new CSessionDlg())->Show();
- return 0;
-}
+/*
+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"
+
+HWND g_hSDlg;
+
+bool bSC = false;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int SaveUserSessionName(MCONTACT *contacts, wchar_t *szUSessionName)
+{
+ if (contacts[0] == 0)
+ return 1;
+
+ CSession *pSession = nullptr;
+ for (auto &it : g_arUserSessions)
+ if (!it->wszName.CompareNoCase(szUSessionName))
+ pSession = it;
+
+ if (pSession)
+ pSession->contacts.clear();
+ else {
+ g_plugin.g_lastUserId = g_plugin.g_lastUserId + 1;
+
+ pSession = new CSession();
+ pSession->id = g_plugin.g_lastUserId;
+ pSession->bIsUser = true;
+ pSession->wszName = szUSessionName;
+ g_arUserSessions.insert(pSession);
+ }
+
+ for (int i = 0; contacts[i]; i++)
+ pSession->contacts.push_back(contacts[i]);
+
+ pSession->save();
+
+ while (g_arUserSessions.getCount() > g_ses_limit) {
+ g_arUserSessions[0].remove();
+ g_arUserSessions.remove(int(0));
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Save session dialog
+
+class CSessionDlg : public CDlgBase
+{
+ CCtrlClc m_clist;
+ CCtrlCombo m_sessions;
+ CCtrlCheck chkSelContacts, chkSaveAndClose;
+
+ MCONTACT user_session_list[255];
+
+public:
+ CSessionDlg() :
+ CDlgBase(g_plugin, IDD_SAVEDIALOG),
+ m_clist(this, IDC_CLIST),
+ m_sessions(this, IDC_LIST),
+ chkSelContacts(this, IDC_SELCONTACTS),
+ chkSaveAndClose(this, IDC_SANDCCHECK)
+ {
+ memset(user_session_list, 0, sizeof(user_session_list));
+
+ chkSelContacts.OnChange = Callback(this, &CSessionDlg::onChange_SelContacts);
+
+ m_clist.OnCheckChanged = Callback(this, &CSessionDlg::onCheckChanged_Clist);
+ }
+
+ bool OnInitDialog() override
+ {
+ g_hSDlg = m_hwnd;
+ LoadSessionToCombobox(m_sessions, true);
+
+ SetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE,
+ GetWindowLongPtr(m_clist.GetHwnd(), GWL_STYLE) | CLS_CHECKBOXES | CLS_HIDEEMPTYGROUPS | CLS_USEGROUPS | CLS_GREYALTERNATE | CLS_GROUPCHECKBOXES);
+ m_clist.SetExStyle(CLS_EX_DISABLEDRAGDROP | CLS_EX_TRACKSELECT);
+ m_clist.AutoRebuild();
+
+ LoadPosition(m_hwnd, "SaveDlg");
+ return true;
+ }
+
+ bool OnApply() override
+ {
+ wchar_t szUserSessionName[MAX_PATH];
+ m_sessions.GetText(szUserSessionName, _countof(szUserSessionName));
+ rtrimw(szUserSessionName);
+ if (szUserSessionName[0] == 0) {
+ MessageBox(nullptr, TranslateT("Session name is empty, enter the name and try again"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return false;
+ }
+
+ if (chkSelContacts.IsChecked() && bSC) {
+ int i = 0;
+ for (auto &hContact : Contacts()) {
+ uint8_t res = m_clist.GetCheck(m_clist.FindContact(hContact));
+ if (res) {
+ user_session_list[i] = hContact;
+ i++;
+ }
+ }
+
+ SaveUserSessionName(user_session_list, szUserSessionName);
+ return true;
+ }
+
+ if (!SaveUserSessionName(session_list, szUserSessionName)) {
+ if (chkSaveAndClose.IsChecked())
+ CloseCurrentSession(0, 0);
+ return true;
+ }
+
+ MessageBox(nullptr, TranslateT("Current session is empty!"), TranslateT("Sessions Manager"), MB_OK | MB_ICONWARNING);
+ return false;
+ }
+
+ void OnDestroy() override
+ {
+ SavePosition(m_hwnd, "SaveDlg");
+ g_hSDlg = nullptr;
+ }
+
+ void onCheckChanged_Clist(CCtrlClc*)
+ {
+ bSC = TRUE;
+ memcpy(user_session_list, session_list, sizeof(user_session_list));
+ }
+
+ void onChange_SelContacts(CCtrlCheck *)
+ {
+ if (!m_bInitialized)
+ return;
+
+ RECT rWnd;
+ GetWindowRect(m_hwnd, &rWnd);
+
+ int x = rWnd.right - rWnd.left, y = rWnd.bottom - rWnd.top, dy, dx, dd;
+
+ if (chkSelContacts.IsChecked()) {
+ chkSaveAndClose.Disable();
+ dy = 20;
+ dx = 150;
+ dd = 5;
+ m_clist.Show();
+ }
+ else {
+ chkSaveAndClose.Enable();
+ dy = -20;
+ dx = -150;
+ dd = 5;
+ m_clist.Hide();
+ }
+
+ SetWindowPos(m_hwnd, nullptr, rWnd.left, rWnd.top, x + dx, y + (dx / 3), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE);
+ SetWindowPos(m_clist.GetHwnd(), nullptr, x - dd, dd, dx - dd, y + (dx / 12), SWP_NOZORDER);
+
+ for (int i = 0; session_list[i] > 0; i++)
+ m_clist.SetCheck(m_clist.FindContact(session_list[i]), 1);
+
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_LIST), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_STATIC), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDC_SANDCCHECK), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDOK), 0, dy);
+ OffsetWindow(m_hwnd, GetDlgItem(m_hwnd, IDCANCEL), 0, dy);
+ }
+};
+
+INT_PTR SaveUserSessionHandles(WPARAM, LPARAM)
+{
+ if (g_hSDlg) {
+ ShowWindow(g_hSDlg, SW_SHOW);
+ return 1;
+ }
+
+ (new CSessionDlg())->Show();
+ return 0;
+}
diff --git a/plugins/Sessions/Src/stdafx.cxx b/plugins/Sessions/Src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Sessions/Src/stdafx.cxx
+++ b/plugins/Sessions/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/plugins/ShellExt/src/stdafx.cxx b/plugins/ShellExt/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ShellExt/src/stdafx.cxx
+++ b/plugins/ShellExt/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/plugins/SimpleAR/src/stdafx.cxx b/plugins/SimpleAR/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SimpleAR/src/stdafx.cxx
+++ b/plugins/SimpleAR/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/plugins/SimpleStatusMsg/src/awaymsg.cpp b/plugins/SimpleStatusMsg/src/awaymsg.cpp
index e034dc3009..61320cfb44 100644
--- a/plugins/SimpleStatusMsg/src/awaymsg.cpp
+++ b/plugins/SimpleStatusMsg/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-10 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/SimpleStatusMsg/src/stdafx.cxx b/plugins/SimpleStatusMsg/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SimpleStatusMsg/src/stdafx.cxx
+++ b/plugins/SimpleStatusMsg/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/plugins/SkypeStatusChange/src/stdafx.cxx b/plugins/SkypeStatusChange/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SkypeStatusChange/src/stdafx.cxx
+++ b/plugins/SkypeStatusChange/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/plugins/SmileyAdd/src/options.cpp b/plugins/SmileyAdd/src/options.cpp
index 324506dd8d..92e3e8676c 100644
--- a/plugins/SmileyAdd/src/options.cpp
+++ b/plugins/SmileyAdd/src/options.cpp
@@ -1,6 +1,6 @@
/*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/options.h b/plugins/SmileyAdd/src/options.h
index ee7bc61ba7..299ef72f2e 100644
--- a/plugins/SmileyAdd/src/options.h
+++ b/plugins/SmileyAdd/src/options.h
@@ -1,6 +1,6 @@
/*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/services.cpp b/plugins/SmileyAdd/src/services.cpp
index 01ecc57786..abc5e3cb93 100644
--- a/plugins/SmileyAdd/src/services.cpp
+++ b/plugins/SmileyAdd/src/services.cpp
@@ -1,6 +1,6 @@
/*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/smileys.cpp b/plugins/SmileyAdd/src/smileys.cpp
index 3c3dd69b33..a9f69a1c5c 100644
--- a/plugins/SmileyAdd/src/smileys.cpp
+++ b/plugins/SmileyAdd/src/smileys.cpp
@@ -1,977 +1,977 @@
-/*
-Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
-Copyright (C) 2003-04 Rein-Peter de Boer
-
-This program is free software; 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"
-
-SmileyPackListType g_SmileyPacks;
-SmileyCategoryListType g_SmileyCategories;
-
-static HWND hwndHidden = nullptr;
-
-static void CALLBACK timerProc(HWND, UINT, UINT_PTR param, DWORD)
-{
- SmileyType *pType = (SmileyType*)param;
- pType->MoveToNextFrame();
-}
-
-// these two functions must be called from the main thread
-static void CALLBACK sttStartTimer(PVOID obj)
-{
- if (hwndHidden == nullptr)
- hwndHidden = CreateWindowEx(0, L"STATIC", nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
-
- SmileyType *pType = (SmileyType*)obj;
- pType->SetFrameDelay();
-}
-
-static void CALLBACK sttStopTimer(PVOID obj)
-{
- KillTimer(hwndHidden, (DWORD_PTR)obj);
-}
-
-//
-// SmileyType
-//
-
-SmileyType::SmileyType(void) :
- m_arSmileys(10, PtrKeySortT)
-{
- m_SmileyIcon = nullptr;
- m_xepimg = nullptr;
- m_flags = 0;
- m_index = 0;
- m_size.cx = 0;
- m_size.cy = 0;
-}
-
-SmileyType::~SmileyType()
-{
- if (m_xepimg) {
- m_xepimg->Release();
- m_xepimg = nullptr;
- }
-
- if (m_SmileyIcon != nullptr) {
- DestroyIcon(m_SmileyIcon);
- m_SmileyIcon = nullptr;
- }
-}
-
-void SmileyType::AddObject(ISmileyBase *pObject)
-{
- if (m_arSmileys.getCount() == 0) {
- if (m_xepimg == nullptr)
- m_xepimg = AddCacheImage(m_filepath, m_index);
- CallFunctionAsync(sttStartTimer, this);
- }
-
- m_arSmileys.insert(pObject);
-}
-
-void SmileyType::RemoveObject(ISmileyBase *pObject)
-{
- int idx = m_arSmileys.getIndex(pObject);
- if (idx == -1)
- return;
-
- m_arSmileys.remove(idx);
- if (m_arSmileys.getCount() == 0)
- CallFunctionAsync(sttStopTimer, this);
-}
-
-void SmileyType::SetFrameDelay()
-{
- int iFrameDelay = (m_xepimg == nullptr) ? 0 : m_xepimg->GetFrameDelay();
- if (iFrameDelay <= 0)
- KillTimer(hwndHidden, (DWORD_PTR)this);
- else
- SetTimer(hwndHidden, (DWORD_PTR)this, iFrameDelay*10, timerProc);
-}
-
-void SmileyType::MoveToNextFrame()
-{
- m_index = m_xepimg->SelectNextFrame(m_index);
-
- for (auto &it : m_arSmileys.rev_iter())
- it->Draw();
-
- SetFrameDelay(); // reset timer
-}
-
-HICON SmileyType::GetIcon(void)
-{
- if (m_SmileyIcon == nullptr) {
- ImageBase *img = CreateCachedImage();
- if (!img)
- return nullptr;
-
- img->SelectFrame(m_index);
- m_SmileyIcon = img->GetIcon();
- img->Release();
- }
- return m_SmileyIcon;
-}
-
-HICON SmileyType::GetIconDup(void)
-{
- if (ImageBase *img = CreateCachedImage()) {
- img->SelectFrame(m_index);
- HICON hIcon = img->GetIcon();
- img->Release();
- return hIcon;
- }
- return nullptr;
-}
-
-bool SmileyType::LoadFromImage(IStream *pStream)
-{
- if (m_xepimg)
- m_xepimg->Release();
-
- CMStringW name;
- m_xepimg = new ImageType(0, name, pStream);
- return true;
-}
-
-bool SmileyType::LoadFromResource(const CMStringW &file, const int index)
-{
- m_index = index;
- m_filepath = file;
- return true;
-}
-
-void SmileyType::GetSize(SIZE &size)
-{
- if (m_size.cy == 0) {
- ImageBase *img = CreateCachedImage();
- if (img) {
- img->GetSize(m_size);
- img->Release();
- }
- }
- size = m_size;
-}
-
-ImageBase* SmileyType::CreateCachedImage(void)
-{
- if (m_xepimg) {
- m_xepimg->AddRef();
- return m_xepimg;
- }
- return AddCacheImage(m_filepath, m_index);
-}
-
-void SmileyType::SetImList(HIMAGELIST hImLst, long i)
-{
- if (m_xepimg) m_xepimg->Release();
- m_xepimg = new ImageListItemType(0, hImLst, i);
-}
-
-HBITMAP SmileyType::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY)
-{
- ImageBase *img = CreateCachedImage();
- if (!img) return nullptr;
- img->SelectFrame(m_index);
- HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY);
- img->Release();
-
- return hBmp;
-}
-
-//
-// SmileyPackType
-//
-
-SmileyPackType::SmileyPackType()
-{
- m_hSmList = nullptr;
- errorFound = false;
-}
-
-SmileyType* SmileyPackType::GetSmiley(unsigned index)
-{
- return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : nullptr;
-}
-
-SmileyPackType::~SmileyPackType()
-{
- if (m_hSmList != nullptr) ImageList_Destroy(m_hSmList);
-}
-
-static const wchar_t urlRegEx[] = L"(?:ftp|https|http|file|aim|webcal|irc|msnim|xmpp|gopher|mailto|news|nntp|telnet|wais|prospero)://?[\\w.?%:/$+;]*";
-static const wchar_t pathRegEx[] = L"[\\s\"][a-zA-Z]:[\\\\/][\\w.\\-\\\\/]*";
-static const wchar_t timeRegEx[] = L"\\d{1,2}:\\d{2}:\\d{2}|\\d{1,2}:\\d{2}";
-
-void SmileyPackType::AddTriggersToSmileyLookup(void)
-{
- CMStringW emptystr;
- m_SmileyLookup.insert(new SmileyLookup(urlRegEx, true, -1, emptystr));
- m_SmileyLookup.insert(new SmileyLookup(pathRegEx, true, -1, emptystr));
- m_SmileyLookup.insert(new SmileyLookup(timeRegEx, true, -1, emptystr));
-
- for (int dist = 0; dist < m_SmileyList.getCount(); dist++) {
- auto &p = m_SmileyList[dist];
- if (p.IsRegEx()) {
- SmileyLookup *dats = new SmileyLookup(p.GetTriggerText(), true, dist, GetFilename());
- if (dats->IsValid())
- m_SmileyLookup.insert(dats);
- else
- errorFound = true;
- if (p.m_InsertText.IsEmpty())
- p.m_InsertText = p.m_ToolText;
- }
- else if (!p.IsService()) {
- bool first = true;
- const CMStringW &text = p.GetTriggerText();
- int iStart = 0;
- while (true) {
- CMStringW wszWord = text.Tokenize(L" \t", iStart);
- if (iStart == -1)
- break;
-
- ReplaceAllSpecials(wszWord, wszWord);
- SmileyLookup *dats = new SmileyLookup(wszWord, false, dist, GetFilename());
- if (dats->IsValid()) {
- m_SmileyLookup.insert(dats);
- if (first) {
- p.m_InsertText = wszWord;
- first = false;
- }
- }
- else delete dats;
- }
- }
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static MRegexp16 isCode(L"\\&\\#(\\d*)\\;");
-
-static void replaceCodes(CMStringW &str)
-{
- if (isCode.match(str) < 0)
- return;
-
- str.Delete(isCode.getPos(), isCode.getLength());
-
- uint32_t iCode = _wtoi(isCode.getGroup(1));
- wchar_t tmp[3] = { 0, 0, 0 };
- if (iCode < 0x10000)
- tmp[0] = LOWORD(iCode), tmp[1] = HIWORD(iCode);
- else {
- iCode -= 0x10000;
- tmp[0] = 0xD800 + (iCode >> 10);
- tmp[1] = 0xDC00 + (iCode & 0x3FF);
- }
- str.Insert(isCode.getPos(), tmp);
-}
-
-void SmileyPackType::ReplaceAllSpecials(const CMStringW &Input, CMStringW &Output)
-{
- Output = Input;
- Output.Replace(L"%%_%%", L" ");
- Output.Replace(L"%%__%%", L" ");
- Output.Replace(L"%%''%%", L"\"");
- replaceCodes(Output);
-}
-
-void SmileyPackType::Clear(void)
-{
- m_SmileyList.destroy();
- m_SmileyLookup.destroy();
- if (m_hSmList != nullptr) { ImageList_Destroy(m_hSmList); m_hSmList = nullptr; }
- m_Filename.Empty();
- m_Name.Empty();
- m_Date.Empty();
- m_Version.Empty();
- m_Author.Empty();
- m_VisibleCount = 0;
- m_ButtonSmiley.Empty();
- errorFound = false;
-}
-
-bool SmileyPackType::LoadSmileyFile(const CMStringW &filename, const CMStringW &packname, bool onlyInfo, bool noerr)
-{
- Clear();
-
- if (filename.IsEmpty()) {
- m_Name = L"Nothing loaded";
- return false;
- }
-
- wchar_t wszTmp[MAX_PATH];
- CMStringW modpath = VARSW(filename);
- if (_waccess(modpath, 4) != 0) {
- PathToAbsoluteW(filename, wszTmp, g_plugin.wszDefaultPath);
- if (_waccess(wszTmp, 4) != 0) {
- if (!noerr) {
- static const wchar_t errmsg[] = LPGENW("Smiley pack %s for category \"%s\" not found.\nSelect correct smiley pack in the Options -> Customize -> Smileys.");
- wchar_t msgtxt[1024];
- mir_snwprintf(msgtxt, TranslateW(errmsg), modpath.c_str(), packname.c_str());
- ReportError(msgtxt);
- }
-
- m_Name = L"Nothing loaded";
- return false;
- }
- }
- else PathToAbsoluteW(modpath, wszTmp, g_plugin.wszDefaultPath);
-
- modpath = wszTmp;
- m_Filename = filename;
-
- // Load file
- bool res;
- if (filename.Find(L".xep") == -1)
- res = LoadSmileyFileMSL(modpath, onlyInfo, modpath);
- else
- res = LoadSmileyFileXEP(modpath, onlyInfo);
-
- if (errorFound)
- ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee network log for details."));
-
- return res;
-}
-
-static IStream* DecodeBase64Data(const char *pString)
-{
- if (pString == nullptr)
- return nullptr;
-
- size_t dataLen;
- ptrA data((char*)mir_base64_decode(pString, &dataLen));
- if (data == nullptr)
- return nullptr;
-
- // Read image list
- HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, dataLen);
- if (!hBuffer)
- return nullptr;
-
- void *dst = GlobalLock(hBuffer);
- memcpy(dst, data, dataLen);
- GlobalUnlock(hBuffer);
-
- IStream *pStream = nullptr;
- CreateStreamOnHGlobal(hBuffer, TRUE, &pStream);
- return pStream;
-}
-
-static CMStringW FilterQuotes(const char *pStr)
-{
- CMStringW res(pStr);
- int iStart = res.Find('\"', 0);
- if (iStart != -1) {
- int iEnd = res.Find('\"', ++iStart);
- if (iEnd != -1)
- res = res.Mid(iStart, iEnd - iStart);
- }
-
- return res.Trim();
-}
-
-bool SmileyPackType::LoadSmileyFileXEP(const CMStringW &fileName, bool onlyInfo)
-{
- FILE *in = _wfopen(fileName, L"rb");
- if (in == nullptr)
- return false;
-
- TiXmlDocument doc;
- int ret = doc.LoadFile(in);
- fclose(in);
- if (ret != 0)
- return false;
-
- auto *pSettings = doc.FirstChildElement("settings");
- if (pSettings != nullptr) {
- if (auto *pNode = pSettings->FirstChildElement("DataBaseName"))
- m_Name = CMStringW(Utf2T(pNode->GetText())).Trim();
-
- if (auto *pNode = pSettings->FirstChildElement("PackageAuthor"))
- m_Author = CMStringW(Utf2T(pNode->GetText())).Trim();
- }
-
- if (!onlyInfo) {
- auto *pImages = TiXmlConst(&doc)["lists"]["images"].ToElement();
- if (pImages) {
- IStream *pStream = DecodeBase64Data(pImages->GetText());
- if (pStream) {
- if (m_hSmList != nullptr)
- ImageList_Destroy(m_hSmList);
- m_hSmList = ImageList_Read(pStream);
- pStream->Release();
- }
- }
-
- for (auto *nRec : TiXmlFilter(doc.FirstChildElement("dataroot"), "record")) {
- int idx = nRec->IntAttribute("ImageIndex", -1);
- if (idx == -1)
- continue;
-
- SmileyType *dat = new SmileyType;
- dat->SetRegEx(true);
- dat->SetImList(m_hSmList, idx);
- dat->m_ToolText = FilterQuotes(nRec->GetText());
-
- if (auto *pNode = nRec->FirstChildElement("Expression"))
- dat->m_TriggerText = FilterQuotes(pNode->GetText());
- if (auto *pNode = nRec->FirstChildElement("PasteText"))
- dat->m_InsertText = FilterQuotes(pNode->GetText());
-
- dat->SetHidden(dat->m_InsertText.IsEmpty());
-
- if (auto *pNode = nRec->FirstChildElement("Image")) {
- IStream *pStream = DecodeBase64Data(pNode->GetText());
- if (pStream) {
- dat->LoadFromImage(pStream);
- pStream->Release();
- }
- }
-
- m_SmileyList.insert(dat);
- }
- }
-
- m_VisibleCount = m_SmileyList.getCount();
- AddTriggersToSmileyLookup();
-
- selec.x = selec.y = win.x = win.y = 0;
- return true;
-}
-
-bool SmileyPackType::LoadSmileyFileMSL(const CMStringW &filename, bool onlyInfo, CMStringW &modpath)
-{
- int fh = _wopen(filename.c_str(), _O_BINARY | _O_RDONLY);
- if (fh == -1)
- return false;
-
- // Find file size
- const long flen = _filelength(fh);
-
- // Allocate file buffer
- char *buf = new char[flen + sizeof(wchar_t)];
-
- // Read file in
- int len = _read(fh, buf, flen);
- *(wchar_t*)(buf + len) = 0;
-
- // Close file
- _close(fh);
-
- CMStringW tbuf;
- if (len > 2 && *(wchar_t*)buf == 0xfeff)
- tbuf = ((wchar_t*)buf + 1);
- else if (len > 3 && buf[0] == '\xef' && buf[1] == '\xbb' && buf[2] == '\xbf')
- tbuf = _A2T(buf + 3, CP_UTF8);
- else
- tbuf = _A2T(buf);
-
- delete[] buf;
-
- CMStringW pathstr, packstr;
- {
- MRegexp16 pathsplit(L"(.*\\\\)(.*)\\.|$");
- pathsplit.match(modpath);
-
- pathstr = pathsplit.getGroup(1);
- packstr = pathsplit.getGroup(2);
- }
-
- if (!onlyInfo)
- selec.x = selec.y = win.x = win.y = 0;
-
- int iStart = 0;
- MRegexp16 otherf(L"^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\"");
- MRegexp16 size(L"^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)");
- MRegexp16 smiley(
- L"^\\s*Smiley(\\*)?\\s*=" // Is Hidden
- L"(?:\\s*\"(.*)\")" // Smiley file name
- L"(?:[\\s,]+(\\-?\\d+))" // Icon resource id
- L"(?:[\\s,]+(R|S)?\"(.*?)\")" // Trigger text
- L"(?:[\\s,]+\"(.*?)\")?" // Tooltip or insert text
- L"(?:[\\s,]+\"(.*?)\")?"); // Tooltip text
-
- SmileyVectorType hiddenSmileys;
- unsigned smnum = 0;
-
- while (true) {
- CMStringW line = tbuf.Tokenize(L"\r\n", iStart);
- if (iStart == -1)
- break;
-
- if (line.IsEmpty() || line[0] == ';')
- continue;
-
- if (otherf.match(line) >= 0) {
- CMStringW key(otherf.getGroup(1)), value(otherf.getGroup(2));
- if (key == L"Name")
- m_Name = value;
- else if (key == L"Author")
- m_Author = value;
- else if (key == L"Date")
- m_Date = value;
- else if (key == L"Version")
- m_Version = value;
- else if (key == L"ButtonSmiley")
- m_ButtonSmiley = value;
- continue;
- }
-
- if (onlyInfo)
- continue;
-
- if (size.match(line) >= 0) {
- POINT tpt;
- tpt.x = _wtol(size.getGroup(2));
- tpt.y = _wtol(size.getGroup(3));
-
- if (size.getGroup(1) == L"Selection")
- selec = tpt;
- else if (size.getGroup(1) == L"Window")
- win = tpt;
- continue;
- }
-
- if (smiley.match(line)) {
- CMStringW resname = smiley.getGroup(2);
- if (resname.Find(L"http://") != -1) {
- if (GetSmileyFile(resname, packstr))
- continue;
- }
- else if (!resname.IsEmpty())
- resname.Insert(0, pathstr);
-
- SmileyType *dat = new SmileyType;
-
- const int iconIndex = _wtol(smiley.getGroup(3));
-
- dat->SetHidden(!smiley.getGroup(1).IsEmpty());
-
- CMStringW wszGrp4(smiley.getGroup(4));
- if (!wszGrp4.IsEmpty()) {
- dat->SetRegEx(wszGrp4 == L"R");
- dat->SetService(wszGrp4 == L"S");
- }
-
- dat->m_TriggerText = smiley.getGroup(5);
-
- CMStringW wszGrp6(smiley.getGroup(6)), wszGrp7(smiley.getGroup(7));
- if (dat->IsRegEx()) {
- if (!wszGrp6.IsEmpty())
- ReplaceAllSpecials(wszGrp6, dat->m_InsertText);
-
- if (!wszGrp7.IsEmpty())
- ReplaceAllSpecials(wszGrp7, dat->m_ToolText);
- else
- dat->m_ToolText = dat->m_InsertText;
- }
- else {
- if (!wszGrp6.IsEmpty())
- ReplaceAllSpecials(wszGrp6, dat->m_ToolText);
- else
- ReplaceAllSpecials(dat->m_TriggerText, dat->m_ToolText);
- }
-
- bool noerr;
- if (resname.IsEmpty()) {
- dat->SetHidden(true);
- dat->SetText(true);
- noerr = true;
- }
- else noerr = dat->LoadFromResource(resname, iconIndex);
-
- if (dat->IsHidden())
- hiddenSmileys.insert(dat);
- else
- m_SmileyList.insert(dat);
-
- if (!noerr) {
- static const wchar_t errmsg[] = LPGENW("Smiley #%u in file %s for smiley pack %s not found.");
- wchar_t msgtxt[1024];
- mir_snwprintf(msgtxt, TranslateW(errmsg), smnum, resname.c_str(), modpath.c_str());
- Netlib_LogW(hNetlibUser, msgtxt);
- errorFound = true;
- }
- smnum++;
- }
- }
-
- m_VisibleCount = m_SmileyList.getCount();
- m_SmileyList.splice(hiddenSmileys);
- AddTriggersToSmileyLookup();
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// SmileyPackListType
-
-bool SmileyPackListType::AddSmileyPack(CMStringW &filename, CMStringW &packname)
-{
- bool res = true;
- if (GetSmileyPack(filename) == nullptr) { //not exist yet, so add
- SmileyPackType *smileyPack = new SmileyPackType;
-
- res = smileyPack->LoadSmileyFile(filename, packname, FALSE);
- if (res)
- m_SmileyPacks.insert(smileyPack);
- else
- delete smileyPack;
- }
- return res;
-}
-
-SmileyPackType* SmileyPackListType::GetSmileyPack(CMStringW &filename)
-{
- CMStringW modpath = VARSW(filename);
-
- for (auto &it : m_SmileyPacks) {
- CMStringW modpath1(VARSW(it->GetFilename()));
- if (mir_wstrcmpi(modpath.c_str(), modpath1.c_str()) == 0)
- return it;
- }
- return nullptr;
-}
-
-void SmileyPackListType::ClearAndFreeAll()
-{
- m_SmileyPacks.destroy();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// SmileyCategoryType
-
-SmileyCategoryType::SmileyCategoryType(SmileyPackListType *pSPS, const CMStringW &name,
- const CMStringW &displayName, const CMStringW &defaultFilename, SmcType typ)
-{
- m_pSmileyPackStore = pSPS;
- type = typ;
- m_Name = name;
- m_DisplayName = displayName;
- visible = true;
-
- opt.ReadPackFileName(m_Filename, m_Name, defaultFilename);
-}
-
-void SmileyCategoryType::Load(void)
-{
- bool bVisibleCat = opt.UsePhysProto ? !IsAcc() : !IsPhysProto();
- bool bVisible = opt.UseOneForAll ? !IsProto() : bVisibleCat;
- if (bVisible && !m_Filename.IsEmpty()) {
- bool loaded = m_pSmileyPackStore->AddSmileyPack(m_Filename, m_DisplayName);
- if (!loaded) {
- ClearFilename();
- SaveSettings();
- }
- }
-}
-
-SmileyPackType* SmileyCategoryType::GetSmileyPack(void)
-{
- return m_pSmileyPackStore->GetSmileyPack(m_Filename);
-}
-
-void SmileyCategoryType::SaveSettings(void)
-{
- opt.WritePackFileName(m_Filename, m_Name);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// SmileyCategoryListType
-
-void SmileyCategoryListType::ClearAndLoadAll(void)
-{
- m_pSmileyPackStore->ClearAndFreeAll();
-
- for (auto &it : m_SmileyCategories)
- it->Load();
-}
-
-SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const CMStringW &name)
-{
- for (auto &it : m_SmileyCategories)
- if (name.CompareNoCase(it->GetName()) == 0)
- return it;
-
- return nullptr;
-}
-
-SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index)
-{
- return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : nullptr;
-}
-
-SmileyPackType* SmileyCategoryListType::GetSmileyPack(const CMStringW &categoryname)
-{
- SmileyCategoryType *smc = GetSmileyCategory(categoryname);
- return smc != nullptr ? smc->GetSmileyPack() : nullptr;
-}
-
-void SmileyCategoryListType::SaveSettings(void)
-{
- CMStringW catstr;
- for (auto &it : m_SmileyCategories) {
- it->SaveSettings();
- if (it->IsCustom()) {
- if (!catstr.IsEmpty())
- catstr += '#';
- catstr += it->GetName();
- }
- }
- opt.WriteCustomCategories(catstr);
-}
-
-void SmileyCategoryListType::AddAndLoad(const CMStringW &name, const CMStringW &displayName)
-{
- if (GetSmileyCategory(name) != nullptr)
- return;
-
- AddCategory(name, displayName, smcExt);
-
- // Load only if other smileys have been loaded already
- if (m_SmileyCategories.getCount() > 1)
- m_SmileyCategories[m_SmileyCategories.getCount() - 1].Load();
-}
-
-void SmileyCategoryListType::AddCategory(const CMStringW &name, const CMStringW &displayName, SmcType typ, const CMStringW &defaultFilename)
-{
- if (GetSmileyCategory(name) == nullptr)
- m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name, displayName, defaultFilename, typ));
-}
-
-bool SmileyCategoryListType::DeleteCustomCategory(int index)
-{
- if (index < m_SmileyCategories.getCount()) {
- if (m_SmileyCategories[index].IsCustom()) {
- m_SmileyCategories.remove(index);
- return true;
- }
- }
- return false;
-}
-
-void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const CMStringW &defaultFile)
-{
- if (acc->IsEnabled() && acc->szProtoName && IsSmileyProto(acc->szModuleName)) {
- CMStringW displayName(acc->tszAccountName ? acc->tszAccountName : _A2T(acc->szModuleName));
- CMStringW PhysProtoName, paths;
- DBVARIANT dbv;
-
- if (db_get_ws(0, acc->szModuleName, "AM_BaseProto", &dbv) == 0) {
- PhysProtoName = L"AllProto";
- PhysProtoName += dbv.pwszVal;
- db_free(&dbv);
- }
-
- if (!PhysProtoName.IsEmpty())
- paths = g_SmileyCategories.GetSmileyCategory(PhysProtoName) ? g_SmileyCategories.GetSmileyCategory(PhysProtoName)->GetFilename() : L"";
-
- // assemble default path
- if (paths.IsEmpty()) {
- const char *packnam = acc->szProtoName;
- if (mir_strcmp(packnam, "JABBER") == 0)
- packnam = "JGMail";
-
- wchar_t path[MAX_PATH];
- mir_snwprintf(path, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
- if (_waccess(path, 0) != 0)
- paths = defaultFile;
- }
-
- CMStringW tname(_A2T(acc->szModuleName));
- AddCategory(tname, displayName, acc->bIsVirtual ? smcVirtualProto : smcProto, paths);
- }
-}
-
-void SmileyCategoryListType::AddProtoAsCategory(char *acc, const CMStringW &defaultFile)
-{
- if (acc == nullptr)
- return;
-
- const char *packnam = acc;
- if (mir_strcmp(packnam, "JABBER") == 0)
- packnam = "JGMail";
-
- // assemble default path
- CMStringW paths(FORMAT, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
- paths = VARSW(paths);
- if (_waccess(paths.c_str(), 0) != 0)
- paths = defaultFile;
-
- CMStringW dName(acc), displayName;
- displayName.AppendFormat(TranslateT("%s global smiley pack"), dName.GetBuffer());
- CMStringW tname("AllProto");
- tname += _A2T(acc);
- AddCategory(tname, displayName, smcPhysProto, paths);
-}
-
-void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc)
-{
- CMStringW tname(_A2T(acc->szModuleName));
-
- for (auto &hContact : Contacts()) {
- char *proto = Proto_GetBaseAccountName(hContact);
- if (proto == nullptr)
- continue;
-
- DBVARIANT dbv;
- if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
- bool found = (tname.CompareNoCase(dbv.pwszVal) == 0);
- db_free(&dbv);
- if (found)
- return;
- }
- }
-
- for (auto &it : m_SmileyCategories) {
- if (tname.CompareNoCase(it->GetName()) == 0) {
- m_SmileyCategories.removeItem(&it);
- break;
- }
- }
-}
-
-void SmileyCategoryListType::AddContactTransportAsCategory(MCONTACT hContact, const CMStringW &defaultFile)
-{
- char *proto = Proto_GetBaseAccountName(hContact);
- if (proto == nullptr)
- return;
-
- DBVARIANT dbv;
- if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
- if (dbv.pwszVal[0] == '\0') {
- db_free(&dbv);
- return;
- }
- char *trsp = mir_strdup(_T2A(dbv.pwszVal));
- _strlwr(trsp);
-
- const char *packname = nullptr;
- if (strstr(trsp, "icq") != nullptr)
- packname = "icq";
-
- mir_free(trsp);
-
- CMStringW paths, displayName(dbv.pwszVal);
- if (packname != nullptr) {
- paths.Format(L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packname);
- paths = VARSW(paths);
- if (_waccess(paths.c_str(), 0) != 0)
- paths = defaultFile;
- }
- else paths = defaultFile;
-
- AddCategory(displayName, displayName, smcTransportProto, defaultFile);
-
- db_free(&dbv);
- }
-}
-
-void SmileyCategoryListType::AddAllProtocolsAsCategory(void)
-{
- CMStringW displayName = TranslateT("Standard");
- CMStringW tname = L"Standard";
- AddCategory(tname, displayName, smcStd);
-
- const CMStringW &defaultFile = GetSmileyCategory(tname)->GetFilename();
-
- PROTOCOLDESCRIPTOR **proto;
- int protoCount = 0;
- Proto_EnumProtocols(&protoCount, &proto);
-
- for (int i = 0; i < protoCount; i++) {
- PROTOCOLDESCRIPTOR *pd = proto[i];
- if (pd->type == PROTOTYPE_PROTOWITHACCS)
- AddProtoAsCategory(pd->szName, defaultFile);
- }
-
- for (auto &pa : Accounts())
- AddAccountAsCategory(pa, defaultFile);
-
- for (auto &hContact : Contacts())
- AddContactTransportAsCategory(hContact, defaultFile);
-
- CMStringW cats;
- opt.ReadCustomCategories(cats);
-
- int cppv = 0;
- for (;;) {
- int cp = cats.Find('#', cppv);
- if (cp == -1)
- break;
-
- displayName = cats.Mid(cppv, cp - cppv);
- AddCategory(displayName, displayName, smcCustom, defaultFile);
- cppv = cp + 1;
- }
-
- if (cppv != cats.GetLength()) {
- displayName = cats.Mid(cppv);
- AddCategory(displayName, displayName, smcCustom, defaultFile);
- }
-}
-
-static const CMStringW testString(L"Test String");
-
-SmileyLookup::SmileyLookup(const CMStringW &str, const bool regexs, const int ind, const CMStringW &smpt)
-{
- m_ind = ind;
- if (regexs) {
- m_pattern.compile(str);
- m_valid = m_pattern.isValid();
- if (!m_valid) {
- wchar_t msgtxt[1024];
- mir_snwprintf(msgtxt, TranslateT("Regular expression \"%s\" in smiley pack \"%s\" malformed."), str.c_str(), smpt.c_str());
- Netlib_LogW(hNetlibUser, msgtxt);
- }
- }
- else {
- m_text = str;
- replaceCodes(m_text);
- m_valid = !str.IsEmpty();
- }
-}
-
-SmileyLookup::~SmileyLookup()
-{
-}
-
-void SmileyLookup::Find(const CMStringW &str, SmileyLocVecType &smlcur, bool firstOnly)
-{
- if (!m_valid) return;
-
- if (m_text.IsEmpty()) {
- while (m_pattern.nextMatch(str) >= 0) {
- CMStringW wszMatch(m_pattern.getMatch());
- smlcur.insert(new SmileyLocType(m_pattern.getPos(), wszMatch.GetLength()));
- if (firstOnly && m_ind != -1)
- return;
- }
- }
- else {
- const wchar_t *pos = str.c_str();
- while ((pos = wcsstr(pos, m_text.c_str())) != nullptr) {
- smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.GetLength()));
- pos += m_text.GetLength();
- if (firstOnly && m_ind != -1)
- return;
- }
- }
-}
+/*
+Miranda NG SmileyAdd Plugin
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
+Copyright (C) 2003-04 Rein-Peter de Boer
+
+This program is free software; 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"
+
+SmileyPackListType g_SmileyPacks;
+SmileyCategoryListType g_SmileyCategories;
+
+static HWND hwndHidden = nullptr;
+
+static void CALLBACK timerProc(HWND, UINT, UINT_PTR param, DWORD)
+{
+ SmileyType *pType = (SmileyType*)param;
+ pType->MoveToNextFrame();
+}
+
+// these two functions must be called from the main thread
+static void CALLBACK sttStartTimer(PVOID obj)
+{
+ if (hwndHidden == nullptr)
+ hwndHidden = CreateWindowEx(0, L"STATIC", nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
+
+ SmileyType *pType = (SmileyType*)obj;
+ pType->SetFrameDelay();
+}
+
+static void CALLBACK sttStopTimer(PVOID obj)
+{
+ KillTimer(hwndHidden, (DWORD_PTR)obj);
+}
+
+//
+// SmileyType
+//
+
+SmileyType::SmileyType(void) :
+ m_arSmileys(10, PtrKeySortT)
+{
+ m_SmileyIcon = nullptr;
+ m_xepimg = nullptr;
+ m_flags = 0;
+ m_index = 0;
+ m_size.cx = 0;
+ m_size.cy = 0;
+}
+
+SmileyType::~SmileyType()
+{
+ if (m_xepimg) {
+ m_xepimg->Release();
+ m_xepimg = nullptr;
+ }
+
+ if (m_SmileyIcon != nullptr) {
+ DestroyIcon(m_SmileyIcon);
+ m_SmileyIcon = nullptr;
+ }
+}
+
+void SmileyType::AddObject(ISmileyBase *pObject)
+{
+ if (m_arSmileys.getCount() == 0) {
+ if (m_xepimg == nullptr)
+ m_xepimg = AddCacheImage(m_filepath, m_index);
+ CallFunctionAsync(sttStartTimer, this);
+ }
+
+ m_arSmileys.insert(pObject);
+}
+
+void SmileyType::RemoveObject(ISmileyBase *pObject)
+{
+ int idx = m_arSmileys.getIndex(pObject);
+ if (idx == -1)
+ return;
+
+ m_arSmileys.remove(idx);
+ if (m_arSmileys.getCount() == 0)
+ CallFunctionAsync(sttStopTimer, this);
+}
+
+void SmileyType::SetFrameDelay()
+{
+ int iFrameDelay = (m_xepimg == nullptr) ? 0 : m_xepimg->GetFrameDelay();
+ if (iFrameDelay <= 0)
+ KillTimer(hwndHidden, (DWORD_PTR)this);
+ else
+ SetTimer(hwndHidden, (DWORD_PTR)this, iFrameDelay*10, timerProc);
+}
+
+void SmileyType::MoveToNextFrame()
+{
+ m_index = m_xepimg->SelectNextFrame(m_index);
+
+ for (auto &it : m_arSmileys.rev_iter())
+ it->Draw();
+
+ SetFrameDelay(); // reset timer
+}
+
+HICON SmileyType::GetIcon(void)
+{
+ if (m_SmileyIcon == nullptr) {
+ ImageBase *img = CreateCachedImage();
+ if (!img)
+ return nullptr;
+
+ img->SelectFrame(m_index);
+ m_SmileyIcon = img->GetIcon();
+ img->Release();
+ }
+ return m_SmileyIcon;
+}
+
+HICON SmileyType::GetIconDup(void)
+{
+ if (ImageBase *img = CreateCachedImage()) {
+ img->SelectFrame(m_index);
+ HICON hIcon = img->GetIcon();
+ img->Release();
+ return hIcon;
+ }
+ return nullptr;
+}
+
+bool SmileyType::LoadFromImage(IStream *pStream)
+{
+ if (m_xepimg)
+ m_xepimg->Release();
+
+ CMStringW name;
+ m_xepimg = new ImageType(0, name, pStream);
+ return true;
+}
+
+bool SmileyType::LoadFromResource(const CMStringW &file, const int index)
+{
+ m_index = index;
+ m_filepath = file;
+ return true;
+}
+
+void SmileyType::GetSize(SIZE &size)
+{
+ if (m_size.cy == 0) {
+ ImageBase *img = CreateCachedImage();
+ if (img) {
+ img->GetSize(m_size);
+ img->Release();
+ }
+ }
+ size = m_size;
+}
+
+ImageBase* SmileyType::CreateCachedImage(void)
+{
+ if (m_xepimg) {
+ m_xepimg->AddRef();
+ return m_xepimg;
+ }
+ return AddCacheImage(m_filepath, m_index);
+}
+
+void SmileyType::SetImList(HIMAGELIST hImLst, long i)
+{
+ if (m_xepimg) m_xepimg->Release();
+ m_xepimg = new ImageListItemType(0, hImLst, i);
+}
+
+HBITMAP SmileyType::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY)
+{
+ ImageBase *img = CreateCachedImage();
+ if (!img) return nullptr;
+ img->SelectFrame(m_index);
+ HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY);
+ img->Release();
+
+ return hBmp;
+}
+
+//
+// SmileyPackType
+//
+
+SmileyPackType::SmileyPackType()
+{
+ m_hSmList = nullptr;
+ errorFound = false;
+}
+
+SmileyType* SmileyPackType::GetSmiley(unsigned index)
+{
+ return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : nullptr;
+}
+
+SmileyPackType::~SmileyPackType()
+{
+ if (m_hSmList != nullptr) ImageList_Destroy(m_hSmList);
+}
+
+static const wchar_t urlRegEx[] = L"(?:ftp|https|http|file|aim|webcal|irc|msnim|xmpp|gopher|mailto|news|nntp|telnet|wais|prospero)://?[\\w.?%:/$+;]*";
+static const wchar_t pathRegEx[] = L"[\\s\"][a-zA-Z]:[\\\\/][\\w.\\-\\\\/]*";
+static const wchar_t timeRegEx[] = L"\\d{1,2}:\\d{2}:\\d{2}|\\d{1,2}:\\d{2}";
+
+void SmileyPackType::AddTriggersToSmileyLookup(void)
+{
+ CMStringW emptystr;
+ m_SmileyLookup.insert(new SmileyLookup(urlRegEx, true, -1, emptystr));
+ m_SmileyLookup.insert(new SmileyLookup(pathRegEx, true, -1, emptystr));
+ m_SmileyLookup.insert(new SmileyLookup(timeRegEx, true, -1, emptystr));
+
+ for (int dist = 0; dist < m_SmileyList.getCount(); dist++) {
+ auto &p = m_SmileyList[dist];
+ if (p.IsRegEx()) {
+ SmileyLookup *dats = new SmileyLookup(p.GetTriggerText(), true, dist, GetFilename());
+ if (dats->IsValid())
+ m_SmileyLookup.insert(dats);
+ else
+ errorFound = true;
+ if (p.m_InsertText.IsEmpty())
+ p.m_InsertText = p.m_ToolText;
+ }
+ else if (!p.IsService()) {
+ bool first = true;
+ const CMStringW &text = p.GetTriggerText();
+ int iStart = 0;
+ while (true) {
+ CMStringW wszWord = text.Tokenize(L" \t", iStart);
+ if (iStart == -1)
+ break;
+
+ ReplaceAllSpecials(wszWord, wszWord);
+ SmileyLookup *dats = new SmileyLookup(wszWord, false, dist, GetFilename());
+ if (dats->IsValid()) {
+ m_SmileyLookup.insert(dats);
+ if (first) {
+ p.m_InsertText = wszWord;
+ first = false;
+ }
+ }
+ else delete dats;
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static MRegexp16 isCode(L"\\&\\#(\\d*)\\;");
+
+static void replaceCodes(CMStringW &str)
+{
+ if (isCode.match(str) < 0)
+ return;
+
+ str.Delete(isCode.getPos(), isCode.getLength());
+
+ uint32_t iCode = _wtoi(isCode.getGroup(1));
+ wchar_t tmp[3] = { 0, 0, 0 };
+ if (iCode < 0x10000)
+ tmp[0] = LOWORD(iCode), tmp[1] = HIWORD(iCode);
+ else {
+ iCode -= 0x10000;
+ tmp[0] = 0xD800 + (iCode >> 10);
+ tmp[1] = 0xDC00 + (iCode & 0x3FF);
+ }
+ str.Insert(isCode.getPos(), tmp);
+}
+
+void SmileyPackType::ReplaceAllSpecials(const CMStringW &Input, CMStringW &Output)
+{
+ Output = Input;
+ Output.Replace(L"%%_%%", L" ");
+ Output.Replace(L"%%__%%", L" ");
+ Output.Replace(L"%%''%%", L"\"");
+ replaceCodes(Output);
+}
+
+void SmileyPackType::Clear(void)
+{
+ m_SmileyList.destroy();
+ m_SmileyLookup.destroy();
+ if (m_hSmList != nullptr) { ImageList_Destroy(m_hSmList); m_hSmList = nullptr; }
+ m_Filename.Empty();
+ m_Name.Empty();
+ m_Date.Empty();
+ m_Version.Empty();
+ m_Author.Empty();
+ m_VisibleCount = 0;
+ m_ButtonSmiley.Empty();
+ errorFound = false;
+}
+
+bool SmileyPackType::LoadSmileyFile(const CMStringW &filename, const CMStringW &packname, bool onlyInfo, bool noerr)
+{
+ Clear();
+
+ if (filename.IsEmpty()) {
+ m_Name = L"Nothing loaded";
+ return false;
+ }
+
+ wchar_t wszTmp[MAX_PATH];
+ CMStringW modpath = VARSW(filename);
+ if (_waccess(modpath, 4) != 0) {
+ PathToAbsoluteW(filename, wszTmp, g_plugin.wszDefaultPath);
+ if (_waccess(wszTmp, 4) != 0) {
+ if (!noerr) {
+ static const wchar_t errmsg[] = LPGENW("Smiley pack %s for category \"%s\" not found.\nSelect correct smiley pack in the Options -> Customize -> Smileys.");
+ wchar_t msgtxt[1024];
+ mir_snwprintf(msgtxt, TranslateW(errmsg), modpath.c_str(), packname.c_str());
+ ReportError(msgtxt);
+ }
+
+ m_Name = L"Nothing loaded";
+ return false;
+ }
+ }
+ else PathToAbsoluteW(modpath, wszTmp, g_plugin.wszDefaultPath);
+
+ modpath = wszTmp;
+ m_Filename = filename;
+
+ // Load file
+ bool res;
+ if (filename.Find(L".xep") == -1)
+ res = LoadSmileyFileMSL(modpath, onlyInfo, modpath);
+ else
+ res = LoadSmileyFileXEP(modpath, onlyInfo);
+
+ if (errorFound)
+ ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee network log for details."));
+
+ return res;
+}
+
+static IStream* DecodeBase64Data(const char *pString)
+{
+ if (pString == nullptr)
+ return nullptr;
+
+ size_t dataLen;
+ ptrA data((char*)mir_base64_decode(pString, &dataLen));
+ if (data == nullptr)
+ return nullptr;
+
+ // Read image list
+ HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, dataLen);
+ if (!hBuffer)
+ return nullptr;
+
+ void *dst = GlobalLock(hBuffer);
+ memcpy(dst, data, dataLen);
+ GlobalUnlock(hBuffer);
+
+ IStream *pStream = nullptr;
+ CreateStreamOnHGlobal(hBuffer, TRUE, &pStream);
+ return pStream;
+}
+
+static CMStringW FilterQuotes(const char *pStr)
+{
+ CMStringW res(pStr);
+ int iStart = res.Find('\"', 0);
+ if (iStart != -1) {
+ int iEnd = res.Find('\"', ++iStart);
+ if (iEnd != -1)
+ res = res.Mid(iStart, iEnd - iStart);
+ }
+
+ return res.Trim();
+}
+
+bool SmileyPackType::LoadSmileyFileXEP(const CMStringW &fileName, bool onlyInfo)
+{
+ FILE *in = _wfopen(fileName, L"rb");
+ if (in == nullptr)
+ return false;
+
+ TiXmlDocument doc;
+ int ret = doc.LoadFile(in);
+ fclose(in);
+ if (ret != 0)
+ return false;
+
+ auto *pSettings = doc.FirstChildElement("settings");
+ if (pSettings != nullptr) {
+ if (auto *pNode = pSettings->FirstChildElement("DataBaseName"))
+ m_Name = CMStringW(Utf2T(pNode->GetText())).Trim();
+
+ if (auto *pNode = pSettings->FirstChildElement("PackageAuthor"))
+ m_Author = CMStringW(Utf2T(pNode->GetText())).Trim();
+ }
+
+ if (!onlyInfo) {
+ auto *pImages = TiXmlConst(&doc)["lists"]["images"].ToElement();
+ if (pImages) {
+ IStream *pStream = DecodeBase64Data(pImages->GetText());
+ if (pStream) {
+ if (m_hSmList != nullptr)
+ ImageList_Destroy(m_hSmList);
+ m_hSmList = ImageList_Read(pStream);
+ pStream->Release();
+ }
+ }
+
+ for (auto *nRec : TiXmlFilter(doc.FirstChildElement("dataroot"), "record")) {
+ int idx = nRec->IntAttribute("ImageIndex", -1);
+ if (idx == -1)
+ continue;
+
+ SmileyType *dat = new SmileyType;
+ dat->SetRegEx(true);
+ dat->SetImList(m_hSmList, idx);
+ dat->m_ToolText = FilterQuotes(nRec->GetText());
+
+ if (auto *pNode = nRec->FirstChildElement("Expression"))
+ dat->m_TriggerText = FilterQuotes(pNode->GetText());
+ if (auto *pNode = nRec->FirstChildElement("PasteText"))
+ dat->m_InsertText = FilterQuotes(pNode->GetText());
+
+ dat->SetHidden(dat->m_InsertText.IsEmpty());
+
+ if (auto *pNode = nRec->FirstChildElement("Image")) {
+ IStream *pStream = DecodeBase64Data(pNode->GetText());
+ if (pStream) {
+ dat->LoadFromImage(pStream);
+ pStream->Release();
+ }
+ }
+
+ m_SmileyList.insert(dat);
+ }
+ }
+
+ m_VisibleCount = m_SmileyList.getCount();
+ AddTriggersToSmileyLookup();
+
+ selec.x = selec.y = win.x = win.y = 0;
+ return true;
+}
+
+bool SmileyPackType::LoadSmileyFileMSL(const CMStringW &filename, bool onlyInfo, CMStringW &modpath)
+{
+ int fh = _wopen(filename.c_str(), _O_BINARY | _O_RDONLY);
+ if (fh == -1)
+ return false;
+
+ // Find file size
+ const long flen = _filelength(fh);
+
+ // Allocate file buffer
+ char *buf = new char[flen + sizeof(wchar_t)];
+
+ // Read file in
+ int len = _read(fh, buf, flen);
+ *(wchar_t*)(buf + len) = 0;
+
+ // Close file
+ _close(fh);
+
+ CMStringW tbuf;
+ if (len > 2 && *(wchar_t*)buf == 0xfeff)
+ tbuf = ((wchar_t*)buf + 1);
+ else if (len > 3 && buf[0] == '\xef' && buf[1] == '\xbb' && buf[2] == '\xbf')
+ tbuf = _A2T(buf + 3, CP_UTF8);
+ else
+ tbuf = _A2T(buf);
+
+ delete[] buf;
+
+ CMStringW pathstr, packstr;
+ {
+ MRegexp16 pathsplit(L"(.*\\\\)(.*)\\.|$");
+ pathsplit.match(modpath);
+
+ pathstr = pathsplit.getGroup(1);
+ packstr = pathsplit.getGroup(2);
+ }
+
+ if (!onlyInfo)
+ selec.x = selec.y = win.x = win.y = 0;
+
+ int iStart = 0;
+ MRegexp16 otherf(L"^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\"");
+ MRegexp16 size(L"^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)");
+ MRegexp16 smiley(
+ L"^\\s*Smiley(\\*)?\\s*=" // Is Hidden
+ L"(?:\\s*\"(.*)\")" // Smiley file name
+ L"(?:[\\s,]+(\\-?\\d+))" // Icon resource id
+ L"(?:[\\s,]+(R|S)?\"(.*?)\")" // Trigger text
+ L"(?:[\\s,]+\"(.*?)\")?" // Tooltip or insert text
+ L"(?:[\\s,]+\"(.*?)\")?"); // Tooltip text
+
+ SmileyVectorType hiddenSmileys;
+ unsigned smnum = 0;
+
+ while (true) {
+ CMStringW line = tbuf.Tokenize(L"\r\n", iStart);
+ if (iStart == -1)
+ break;
+
+ if (line.IsEmpty() || line[0] == ';')
+ continue;
+
+ if (otherf.match(line) >= 0) {
+ CMStringW key(otherf.getGroup(1)), value(otherf.getGroup(2));
+ if (key == L"Name")
+ m_Name = value;
+ else if (key == L"Author")
+ m_Author = value;
+ else if (key == L"Date")
+ m_Date = value;
+ else if (key == L"Version")
+ m_Version = value;
+ else if (key == L"ButtonSmiley")
+ m_ButtonSmiley = value;
+ continue;
+ }
+
+ if (onlyInfo)
+ continue;
+
+ if (size.match(line) >= 0) {
+ POINT tpt;
+ tpt.x = _wtol(size.getGroup(2));
+ tpt.y = _wtol(size.getGroup(3));
+
+ if (size.getGroup(1) == L"Selection")
+ selec = tpt;
+ else if (size.getGroup(1) == L"Window")
+ win = tpt;
+ continue;
+ }
+
+ if (smiley.match(line)) {
+ CMStringW resname = smiley.getGroup(2);
+ if (resname.Find(L"http://") != -1) {
+ if (GetSmileyFile(resname, packstr))
+ continue;
+ }
+ else if (!resname.IsEmpty())
+ resname.Insert(0, pathstr);
+
+ SmileyType *dat = new SmileyType;
+
+ const int iconIndex = _wtol(smiley.getGroup(3));
+
+ dat->SetHidden(!smiley.getGroup(1).IsEmpty());
+
+ CMStringW wszGrp4(smiley.getGroup(4));
+ if (!wszGrp4.IsEmpty()) {
+ dat->SetRegEx(wszGrp4 == L"R");
+ dat->SetService(wszGrp4 == L"S");
+ }
+
+ dat->m_TriggerText = smiley.getGroup(5);
+
+ CMStringW wszGrp6(smiley.getGroup(6)), wszGrp7(smiley.getGroup(7));
+ if (dat->IsRegEx()) {
+ if (!wszGrp6.IsEmpty())
+ ReplaceAllSpecials(wszGrp6, dat->m_InsertText);
+
+ if (!wszGrp7.IsEmpty())
+ ReplaceAllSpecials(wszGrp7, dat->m_ToolText);
+ else
+ dat->m_ToolText = dat->m_InsertText;
+ }
+ else {
+ if (!wszGrp6.IsEmpty())
+ ReplaceAllSpecials(wszGrp6, dat->m_ToolText);
+ else
+ ReplaceAllSpecials(dat->m_TriggerText, dat->m_ToolText);
+ }
+
+ bool noerr;
+ if (resname.IsEmpty()) {
+ dat->SetHidden(true);
+ dat->SetText(true);
+ noerr = true;
+ }
+ else noerr = dat->LoadFromResource(resname, iconIndex);
+
+ if (dat->IsHidden())
+ hiddenSmileys.insert(dat);
+ else
+ m_SmileyList.insert(dat);
+
+ if (!noerr) {
+ static const wchar_t errmsg[] = LPGENW("Smiley #%u in file %s for smiley pack %s not found.");
+ wchar_t msgtxt[1024];
+ mir_snwprintf(msgtxt, TranslateW(errmsg), smnum, resname.c_str(), modpath.c_str());
+ Netlib_LogW(hNetlibUser, msgtxt);
+ errorFound = true;
+ }
+ smnum++;
+ }
+ }
+
+ m_VisibleCount = m_SmileyList.getCount();
+ m_SmileyList.splice(hiddenSmileys);
+ AddTriggersToSmileyLookup();
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SmileyPackListType
+
+bool SmileyPackListType::AddSmileyPack(CMStringW &filename, CMStringW &packname)
+{
+ bool res = true;
+ if (GetSmileyPack(filename) == nullptr) { //not exist yet, so add
+ SmileyPackType *smileyPack = new SmileyPackType;
+
+ res = smileyPack->LoadSmileyFile(filename, packname, FALSE);
+ if (res)
+ m_SmileyPacks.insert(smileyPack);
+ else
+ delete smileyPack;
+ }
+ return res;
+}
+
+SmileyPackType* SmileyPackListType::GetSmileyPack(CMStringW &filename)
+{
+ CMStringW modpath = VARSW(filename);
+
+ for (auto &it : m_SmileyPacks) {
+ CMStringW modpath1(VARSW(it->GetFilename()));
+ if (mir_wstrcmpi(modpath.c_str(), modpath1.c_str()) == 0)
+ return it;
+ }
+ return nullptr;
+}
+
+void SmileyPackListType::ClearAndFreeAll()
+{
+ m_SmileyPacks.destroy();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SmileyCategoryType
+
+SmileyCategoryType::SmileyCategoryType(SmileyPackListType *pSPS, const CMStringW &name,
+ const CMStringW &displayName, const CMStringW &defaultFilename, SmcType typ)
+{
+ m_pSmileyPackStore = pSPS;
+ type = typ;
+ m_Name = name;
+ m_DisplayName = displayName;
+ visible = true;
+
+ opt.ReadPackFileName(m_Filename, m_Name, defaultFilename);
+}
+
+void SmileyCategoryType::Load(void)
+{
+ bool bVisibleCat = opt.UsePhysProto ? !IsAcc() : !IsPhysProto();
+ bool bVisible = opt.UseOneForAll ? !IsProto() : bVisibleCat;
+ if (bVisible && !m_Filename.IsEmpty()) {
+ bool loaded = m_pSmileyPackStore->AddSmileyPack(m_Filename, m_DisplayName);
+ if (!loaded) {
+ ClearFilename();
+ SaveSettings();
+ }
+ }
+}
+
+SmileyPackType* SmileyCategoryType::GetSmileyPack(void)
+{
+ return m_pSmileyPackStore->GetSmileyPack(m_Filename);
+}
+
+void SmileyCategoryType::SaveSettings(void)
+{
+ opt.WritePackFileName(m_Filename, m_Name);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// SmileyCategoryListType
+
+void SmileyCategoryListType::ClearAndLoadAll(void)
+{
+ m_pSmileyPackStore->ClearAndFreeAll();
+
+ for (auto &it : m_SmileyCategories)
+ it->Load();
+}
+
+SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const CMStringW &name)
+{
+ for (auto &it : m_SmileyCategories)
+ if (name.CompareNoCase(it->GetName()) == 0)
+ return it;
+
+ return nullptr;
+}
+
+SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index)
+{
+ return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : nullptr;
+}
+
+SmileyPackType* SmileyCategoryListType::GetSmileyPack(const CMStringW &categoryname)
+{
+ SmileyCategoryType *smc = GetSmileyCategory(categoryname);
+ return smc != nullptr ? smc->GetSmileyPack() : nullptr;
+}
+
+void SmileyCategoryListType::SaveSettings(void)
+{
+ CMStringW catstr;
+ for (auto &it : m_SmileyCategories) {
+ it->SaveSettings();
+ if (it->IsCustom()) {
+ if (!catstr.IsEmpty())
+ catstr += '#';
+ catstr += it->GetName();
+ }
+ }
+ opt.WriteCustomCategories(catstr);
+}
+
+void SmileyCategoryListType::AddAndLoad(const CMStringW &name, const CMStringW &displayName)
+{
+ if (GetSmileyCategory(name) != nullptr)
+ return;
+
+ AddCategory(name, displayName, smcExt);
+
+ // Load only if other smileys have been loaded already
+ if (m_SmileyCategories.getCount() > 1)
+ m_SmileyCategories[m_SmileyCategories.getCount() - 1].Load();
+}
+
+void SmileyCategoryListType::AddCategory(const CMStringW &name, const CMStringW &displayName, SmcType typ, const CMStringW &defaultFilename)
+{
+ if (GetSmileyCategory(name) == nullptr)
+ m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name, displayName, defaultFilename, typ));
+}
+
+bool SmileyCategoryListType::DeleteCustomCategory(int index)
+{
+ if (index < m_SmileyCategories.getCount()) {
+ if (m_SmileyCategories[index].IsCustom()) {
+ m_SmileyCategories.remove(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const CMStringW &defaultFile)
+{
+ if (acc->IsEnabled() && acc->szProtoName && IsSmileyProto(acc->szModuleName)) {
+ CMStringW displayName(acc->tszAccountName ? acc->tszAccountName : _A2T(acc->szModuleName));
+ CMStringW PhysProtoName, paths;
+ DBVARIANT dbv;
+
+ if (db_get_ws(0, acc->szModuleName, "AM_BaseProto", &dbv) == 0) {
+ PhysProtoName = L"AllProto";
+ PhysProtoName += dbv.pwszVal;
+ db_free(&dbv);
+ }
+
+ if (!PhysProtoName.IsEmpty())
+ paths = g_SmileyCategories.GetSmileyCategory(PhysProtoName) ? g_SmileyCategories.GetSmileyCategory(PhysProtoName)->GetFilename() : L"";
+
+ // assemble default path
+ if (paths.IsEmpty()) {
+ const char *packnam = acc->szProtoName;
+ if (mir_strcmp(packnam, "JABBER") == 0)
+ packnam = "JGMail";
+
+ wchar_t path[MAX_PATH];
+ mir_snwprintf(path, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
+ if (_waccess(path, 0) != 0)
+ paths = defaultFile;
+ }
+
+ CMStringW tname(_A2T(acc->szModuleName));
+ AddCategory(tname, displayName, acc->bIsVirtual ? smcVirtualProto : smcProto, paths);
+ }
+}
+
+void SmileyCategoryListType::AddProtoAsCategory(char *acc, const CMStringW &defaultFile)
+{
+ if (acc == nullptr)
+ return;
+
+ const char *packnam = acc;
+ if (mir_strcmp(packnam, "JABBER") == 0)
+ packnam = "JGMail";
+
+ // assemble default path
+ CMStringW paths(FORMAT, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
+ paths = VARSW(paths);
+ if (_waccess(paths.c_str(), 0) != 0)
+ paths = defaultFile;
+
+ CMStringW dName(acc), displayName;
+ displayName.AppendFormat(TranslateT("%s global smiley pack"), dName.GetBuffer());
+ CMStringW tname("AllProto");
+ tname += _A2T(acc);
+ AddCategory(tname, displayName, smcPhysProto, paths);
+}
+
+void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc)
+{
+ CMStringW tname(_A2T(acc->szModuleName));
+
+ for (auto &hContact : Contacts()) {
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (proto == nullptr)
+ continue;
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
+ bool found = (tname.CompareNoCase(dbv.pwszVal) == 0);
+ db_free(&dbv);
+ if (found)
+ return;
+ }
+ }
+
+ for (auto &it : m_SmileyCategories) {
+ if (tname.CompareNoCase(it->GetName()) == 0) {
+ m_SmileyCategories.removeItem(&it);
+ break;
+ }
+ }
+}
+
+void SmileyCategoryListType::AddContactTransportAsCategory(MCONTACT hContact, const CMStringW &defaultFile)
+{
+ char *proto = Proto_GetBaseAccountName(hContact);
+ if (proto == nullptr)
+ return;
+
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
+ if (dbv.pwszVal[0] == '\0') {
+ db_free(&dbv);
+ return;
+ }
+ char *trsp = mir_strdup(_T2A(dbv.pwszVal));
+ _strlwr(trsp);
+
+ const char *packname = nullptr;
+ if (strstr(trsp, "icq") != nullptr)
+ packname = "icq";
+
+ mir_free(trsp);
+
+ CMStringW paths, displayName(dbv.pwszVal);
+ if (packname != nullptr) {
+ paths.Format(L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packname);
+ paths = VARSW(paths);
+ if (_waccess(paths.c_str(), 0) != 0)
+ paths = defaultFile;
+ }
+ else paths = defaultFile;
+
+ AddCategory(displayName, displayName, smcTransportProto, defaultFile);
+
+ db_free(&dbv);
+ }
+}
+
+void SmileyCategoryListType::AddAllProtocolsAsCategory(void)
+{
+ CMStringW displayName = TranslateT("Standard");
+ CMStringW tname = L"Standard";
+ AddCategory(tname, displayName, smcStd);
+
+ const CMStringW &defaultFile = GetSmileyCategory(tname)->GetFilename();
+
+ PROTOCOLDESCRIPTOR **proto;
+ int protoCount = 0;
+ Proto_EnumProtocols(&protoCount, &proto);
+
+ for (int i = 0; i < protoCount; i++) {
+ PROTOCOLDESCRIPTOR *pd = proto[i];
+ if (pd->type == PROTOTYPE_PROTOWITHACCS)
+ AddProtoAsCategory(pd->szName, defaultFile);
+ }
+
+ for (auto &pa : Accounts())
+ AddAccountAsCategory(pa, defaultFile);
+
+ for (auto &hContact : Contacts())
+ AddContactTransportAsCategory(hContact, defaultFile);
+
+ CMStringW cats;
+ opt.ReadCustomCategories(cats);
+
+ int cppv = 0;
+ for (;;) {
+ int cp = cats.Find('#', cppv);
+ if (cp == -1)
+ break;
+
+ displayName = cats.Mid(cppv, cp - cppv);
+ AddCategory(displayName, displayName, smcCustom, defaultFile);
+ cppv = cp + 1;
+ }
+
+ if (cppv != cats.GetLength()) {
+ displayName = cats.Mid(cppv);
+ AddCategory(displayName, displayName, smcCustom, defaultFile);
+ }
+}
+
+static const CMStringW testString(L"Test String");
+
+SmileyLookup::SmileyLookup(const CMStringW &str, const bool regexs, const int ind, const CMStringW &smpt)
+{
+ m_ind = ind;
+ if (regexs) {
+ m_pattern.compile(str);
+ m_valid = m_pattern.isValid();
+ if (!m_valid) {
+ wchar_t msgtxt[1024];
+ mir_snwprintf(msgtxt, TranslateT("Regular expression \"%s\" in smiley pack \"%s\" malformed."), str.c_str(), smpt.c_str());
+ Netlib_LogW(hNetlibUser, msgtxt);
+ }
+ }
+ else {
+ m_text = str;
+ replaceCodes(m_text);
+ m_valid = !str.IsEmpty();
+ }
+}
+
+SmileyLookup::~SmileyLookup()
+{
+}
+
+void SmileyLookup::Find(const CMStringW &str, SmileyLocVecType &smlcur, bool firstOnly)
+{
+ if (!m_valid) return;
+
+ if (m_text.IsEmpty()) {
+ while (m_pattern.nextMatch(str) >= 0) {
+ CMStringW wszMatch(m_pattern.getMatch());
+ smlcur.insert(new SmileyLocType(m_pattern.getPos(), wszMatch.GetLength()));
+ if (firstOnly && m_ind != -1)
+ return;
+ }
+ }
+ else {
+ const wchar_t *pos = str.c_str();
+ while ((pos = wcsstr(pos, m_text.c_str())) != nullptr) {
+ smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.GetLength()));
+ pos += m_text.GetLength();
+ if (firstOnly && m_ind != -1)
+ return;
+ }
+ }
+}
diff --git a/plugins/SmileyAdd/src/smileys.h b/plugins/SmileyAdd/src/smileys.h
index b6f793ac45..da80c4b924 100644
--- a/plugins/SmileyAdd/src/smileys.h
+++ b/plugins/SmileyAdd/src/smileys.h
@@ -1,6 +1,6 @@
/*
Miranda NG SmileyAdd Plugin
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 Rein-Peter de Boer
diff --git a/plugins/SmileyAdd/src/stdafx.cxx b/plugins/SmileyAdd/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SmileyAdd/src/stdafx.cxx
+++ b/plugins/SmileyAdd/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/plugins/SmileyAdd/src/version.h b/plugins/SmileyAdd/src/version.h
index 354db970ef..b5cf594032 100644
--- a/plugins/SmileyAdd/src/version.h
+++ b/plugins/SmileyAdd/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Smiley support for Miranda NG."
#define __AUTHOR "Peacow, nightwish, bid, borkra"
#define __AUTHORWEB "https://miranda-ng.org/p/SmileyAdd"
-#define __COPYRIGHT "© 2012-22 Miranda NG team, 2004-12 Boris Krasnovskiy, portions by Rein-Peter de Boer"
+#define __COPYRIGHT "© 2012-23 Miranda NG team, 2004-12 Boris Krasnovskiy, portions by Rein-Peter de Boer"
diff --git a/plugins/Spamotron/src/stdafx.cxx b/plugins/Spamotron/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Spamotron/src/stdafx.cxx
+++ b/plugins/Spamotron/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/plugins/SpellChecker/src/stdafx.cxx b/plugins/SpellChecker/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SpellChecker/src/stdafx.cxx
+++ b/plugins/SpellChecker/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/plugins/SplashScreen/src/stdafx.cxx b/plugins/SplashScreen/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/SplashScreen/src/stdafx.cxx
+++ b/plugins/SplashScreen/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/plugins/StartPosition/src/main.cpp b/plugins/StartPosition/src/main.cpp
index f6a3eddb6b..d9c5014c36 100644
--- a/plugins/StartPosition/src/main.cpp
+++ b/plugins/StartPosition/src/main.cpp
@@ -5,7 +5,7 @@ Copyright (C) 2005-2008 Felipe Brahm - souFrag
ICQ#50566818
http://www.soufrag.cl
-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/plugins/StartPosition/src/options.h b/plugins/StartPosition/src/options.h
index c8b85af8fe..e9311851df 100644
--- a/plugins/StartPosition/src/options.h
+++ b/plugins/StartPosition/src/options.h
@@ -1,93 +1,93 @@
-/*
-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/>.
-*/
-
-#pragma once
-
-#include "stdafx.h"
-
-enum ClistAlign : uint8_t
-{
- left,
- right
-};
-
-enum ClistState : uint8_t
-{
- hidden,
- minimized,
- normal
-};
-
-struct ClistOptions
-{
- CMOption<uint8_t> isDocked;
- CMOption<uint8_t> state;
-
- CMOption<uint32_t> x;
- CMOption<uint32_t> y;
- CMOption<uint32_t> width;
- CMOption<uint32_t> height;
-
- ClistOptions() :
- isDocked(CLIST_MODULE_NAME, "Docked", 0),
- state(CLIST_MODULE_NAME, "State", ClistState::normal),
- x(CLIST_MODULE_NAME, "x", 0),
- y(CLIST_MODULE_NAME, "y", 0),
- width(CLIST_MODULE_NAME, "Width", 150),
- height(CLIST_MODULE_NAME, "Height", 350)
- { }
-};
-
-struct StartPositionOptions
-{
- CMOption<uint8_t> setTopPosition;
- CMOption<uint8_t> setBottomPosition;
- CMOption<uint8_t> setSidePosition;
- CMOption<uint8_t> clistAlign;
- CMOption<uint8_t> setClistWidth;
- CMOption<uint8_t> setClistStartState;
- CMOption<uint8_t> clistState;
-
- CMOption<uint32_t> pixelsFromTop;
- CMOption<uint32_t> pixelsFromBottom;
- CMOption<uint32_t> pixelsFromSide;
- CMOption<uint32_t> clistWidth;
-
- StartPositionOptions();
-};
-
-class COptionsDlg : public CDlgBase
-{
- CCtrlCheck chkPositionTop, chkPositionBottom, chkPositionSide, chkFromLeft, chkFromRight, chkWidth;
- CCtrlEdit edtPositionTop, edtPositionBottom, edtPositionSide, edtWidth;
- CCtrlCheck chkStartState, chkStartHidden, chkStartNormal;
-
-public:
- COptionsDlg();
-
- bool OnInitDialog() override;
- bool OnApply() override;
-
-private:
- void removeOldSettings();
-
- void onCheck_PositionTop(CCtrlCheck*);
- void onCheck_PositionBottom(CCtrlCheck*);
- void onCheck_PositionSide(CCtrlCheck*);
- void onCheck_Width(CCtrlCheck*);
- void onCheck_StartState(CCtrlCheck*);
-};
+/*
+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/>.
+*/
+
+#pragma once
+
+#include "stdafx.h"
+
+enum ClistAlign : uint8_t
+{
+ left,
+ right
+};
+
+enum ClistState : uint8_t
+{
+ hidden,
+ minimized,
+ normal
+};
+
+struct ClistOptions
+{
+ CMOption<uint8_t> isDocked;
+ CMOption<uint8_t> state;
+
+ CMOption<uint32_t> x;
+ CMOption<uint32_t> y;
+ CMOption<uint32_t> width;
+ CMOption<uint32_t> height;
+
+ ClistOptions() :
+ isDocked(CLIST_MODULE_NAME, "Docked", 0),
+ state(CLIST_MODULE_NAME, "State", ClistState::normal),
+ x(CLIST_MODULE_NAME, "x", 0),
+ y(CLIST_MODULE_NAME, "y", 0),
+ width(CLIST_MODULE_NAME, "Width", 150),
+ height(CLIST_MODULE_NAME, "Height", 350)
+ { }
+};
+
+struct StartPositionOptions
+{
+ CMOption<uint8_t> setTopPosition;
+ CMOption<uint8_t> setBottomPosition;
+ CMOption<uint8_t> setSidePosition;
+ CMOption<uint8_t> clistAlign;
+ CMOption<uint8_t> setClistWidth;
+ CMOption<uint8_t> setClistStartState;
+ CMOption<uint8_t> clistState;
+
+ CMOption<uint32_t> pixelsFromTop;
+ CMOption<uint32_t> pixelsFromBottom;
+ CMOption<uint32_t> pixelsFromSide;
+ CMOption<uint32_t> clistWidth;
+
+ StartPositionOptions();
+};
+
+class COptionsDlg : public CDlgBase
+{
+ CCtrlCheck chkPositionTop, chkPositionBottom, chkPositionSide, chkFromLeft, chkFromRight, chkWidth;
+ CCtrlEdit edtPositionTop, edtPositionBottom, edtPositionSide, edtWidth;
+ CCtrlCheck chkStartState, chkStartHidden, chkStartNormal;
+
+public:
+ COptionsDlg();
+
+ bool OnInitDialog() override;
+ bool OnApply() override;
+
+private:
+ void removeOldSettings();
+
+ void onCheck_PositionTop(CCtrlCheck*);
+ void onCheck_PositionBottom(CCtrlCheck*);
+ void onCheck_PositionSide(CCtrlCheck*);
+ void onCheck_Width(CCtrlCheck*);
+ void onCheck_StartState(CCtrlCheck*);
+};
diff --git a/plugins/StartPosition/src/startposition.h b/plugins/StartPosition/src/startposition.h
index 09e652aa5c..75c159e972 100644
--- a/plugins/StartPosition/src/startposition.h
+++ b/plugins/StartPosition/src/startposition.h
@@ -1,34 +1,34 @@
-/*
-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/>.
-*/
-
-#pragma once
-
-#include "stdafx.h"
-
-struct CMPlugin : public PLUGIN<CMPlugin>
-{
- CMPlugin();
-
- int Load() override;
-
- void Init();
-
- int __cdecl OnOptionsInit(WPARAM, LPARAM);
- void positionClist();
-
- StartPositionOptions spOptions;
-};
+/*
+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/>.
+*/
+
+#pragma once
+
+#include "stdafx.h"
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+
+ void Init();
+
+ int __cdecl OnOptionsInit(WPARAM, LPARAM);
+ void positionClist();
+
+ StartPositionOptions spOptions;
+};
diff --git a/plugins/StartPosition/src/stdafx.cxx b/plugins/StartPosition/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/StartPosition/src/stdafx.cxx
+++ b/plugins/StartPosition/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/plugins/StartPosition/src/stdafx.h b/plugins/StartPosition/src/stdafx.h
index 5c7e573b7f..6dbc8d5f9a 100644
--- a/plugins/StartPosition/src/stdafx.h
+++ b/plugins/StartPosition/src/stdafx.h
@@ -5,7 +5,7 @@ Copyright (C) 2005-2008 Felipe Brahm - souFrag
ICQ#50566818
http://www.soufrag.cl
-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/plugins/StartPosition/src/version.h b/plugins/StartPosition/src/version.h
index c1b334483d..7eb14073ef 100644
--- a/plugins/StartPosition/src/version.h
+++ b/plugins/StartPosition/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Plugin supports starting position of Contact List relative to the taskbar and screen side."
#define __AUTHOR "Felipe Brahm - souFrag"
#define __AUTHORWEB "https://miranda-ng.org/p/StartPosition"
-#define __COPYRIGHT "© 2005-2008 Felipe Brahm - souFrag, © 2012-22 Miranda NG team"
+#define __COPYRIGHT "© 2005-2008 Felipe Brahm - souFrag, © 2012-23 Miranda NG team"
diff --git a/plugins/StartupSilence/src/main.cpp b/plugins/StartupSilence/src/main.cpp
index b7035946d2..77b67af8a1 100644
--- a/plugins/StartupSilence/src/main.cpp
+++ b/plugins/StartupSilence/src/main.cpp
@@ -1,6 +1,6 @@
/*
Copyright (c) 2012-13 Vladimir Lyubimov
-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/plugins/StartupSilence/src/stdafx.cxx b/plugins/StartupSilence/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/StartupSilence/src/stdafx.cxx
+++ b/plugins/StartupSilence/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/plugins/StartupSilence/src/version.h b/plugins/StartupSilence/src/version.h
index 99a753db30..33b20e8aea 100644
--- a/plugins/StartupSilence/src/version.h
+++ b/plugins/StartupSilence/src/version.h
@@ -16,7 +16,7 @@
#define __DESCRIPTION "Suppresses popups and mutes sounds for 10-300 sec at Miranda NG startup, then sets them back to predefined state (Automatically per computer settings)."
#define __AUTHOR "Vladimir Lyubimov"
#define __AUTHORWEB "https://miranda-ng.org/"
-#define __COPYRIGHT "© 2012-22 Vladimir Lyubimov"
+#define __COPYRIGHT "© 2012-23 Vladimir Lyubimov"
// other stuff for Version resource
#include <stdver.h>
diff --git a/plugins/StatusChange/src/stdafx.cxx b/plugins/StatusChange/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/StatusChange/src/stdafx.cxx
+++ b/plugins/StatusChange/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/plugins/StatusManager/src/stdafx.cxx b/plugins/StatusManager/src/stdafx.cxx
index d265a4c02e..8c570f6949 100644
--- a/plugins/StatusManager/src/stdafx.cxx
+++ b/plugins/StatusManager/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/>.
-*/
-
-#include "stdafx.h"
+/*
+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"
diff --git a/plugins/StatusManager/src/version.h b/plugins/StatusManager/src/version.h
index 98d537c9fe..e2c084386a 100644
--- a/plugins/StatusManager/src/version.h
+++ b/plugins/StatusManager/src/version.h
@@ -13,4 +13,4 @@
#define __DESCRIPTION "A connection checker and auto away module. Also allows you to define the status Miranda should set on startup, configurable per protocol."
#define __AUTHOR "P Boon"
#define __AUTHORWEB "https://miranda-ng.org/p/StatusManager"
-#define __COPYRIGHT "© 2003-08 P. Boon, 2008-22 George Hazan"
+#define __COPYRIGHT "© 2003-08 P. Boon, 2008-23 George Hazan"
diff --git a/plugins/StopSpamMod/src/stdafx.cxx b/plugins/StopSpamMod/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/StopSpamMod/src/stdafx.cxx
+++ b/plugins/StopSpamMod/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/plugins/StopSpamMod/src/version.h b/plugins/StopSpamMod/src/version.h
index 4dda7de950..c2551e9567 100644
--- a/plugins/StopSpamMod/src/version.h
+++ b/plugins/StopSpamMod/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Anti-spam plugin for Miranda NG."
#define __AUTHOR "Roman Miklashevsky, sss, Elzor"
#define __AUTHORWEB "https://miranda-ng.org/p/StopSpamMod"
-#define __COPYRIGHT "© 2004-22 Roman Miklashevsky, A. Petkevich, Kosh&chka, sss, Elzor"
+#define __COPYRIGHT "© 2004-23 Roman Miklashevsky, A. Petkevich, Kosh&chka, sss, Elzor"
diff --git a/plugins/StopSpamPlus/src/stdafx.cxx b/plugins/StopSpamPlus/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/StopSpamPlus/src/stdafx.cxx
+++ b/plugins/StopSpamPlus/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/plugins/TabSRMM/TabSRMM_icons/version.h b/plugins/TabSRMM/TabSRMM_icons/version.h
index c825c52d39..94741dbf2e 100644
--- a/plugins/TabSRMM/TabSRMM_icons/version.h
+++ b/plugins/TabSRMM/TabSRMM_icons/version.h
@@ -1,11 +1,11 @@
-#define __MAJOR_VERSION 3
-#define __MINOR_VERSION 1
-#define __RELEASE_NUM 99
-#define __BUILD_NUM 8
-
-#include <stdver.h>
-
-#define __PLUGIN_NAME "TabSRMM icons"
-#define __FILENAME "TabSRMM_icons.dll"
-#define __DESCRIPTION "Iconpack for TabSRMM plugin of Miranda NG."
-#define __COPYRIGHT "© 2012-22 Miranda NG team, 2000-2010 Miranda Project and contributors."
+#define __MAJOR_VERSION 3
+#define __MINOR_VERSION 1
+#define __RELEASE_NUM 99
+#define __BUILD_NUM 8
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "TabSRMM icons"
+#define __FILENAME "TabSRMM_icons.dll"
+#define __DESCRIPTION "Iconpack for TabSRMM plugin of Miranda NG."
+#define __COPYRIGHT "© 2012-23 Miranda NG team, 2000-2010 Miranda Project and contributors."
diff --git a/plugins/TabSRMM/src/ImageDataObject.cpp b/plugins/TabSRMM/src/ImageDataObject.cpp
index 82c7756cef..6da7a5fda6 100644
--- a/plugins/TabSRMM/src/ImageDataObject.cpp
+++ b/plugins/TabSRMM/src/ImageDataObject.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/TSButton.cpp b/plugins/TabSRMM/src/TSButton.cpp
index 7ab8576319..444787e681 100644
--- a/plugins/TabSRMM/src/TSButton.cpp
+++ b/plugins/TabSRMM/src/TSButton.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat.h b/plugins/TabSRMM/src/chat.h
index 42df2909a2..75ceea90cf 100644
--- a/plugins/TabSRMM/src/chat.h
+++ b/plugins/TabSRMM/src/chat.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_log.cpp b/plugins/TabSRMM/src/chat_log.cpp
index e25c14e6fa..73884d4e84 100644
--- a/plugins/TabSRMM/src/chat_log.cpp
+++ b/plugins/TabSRMM/src/chat_log.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_main.cpp b/plugins/TabSRMM/src/chat_main.cpp
index c9662c5dbc..b398955f21 100644
--- a/plugins/TabSRMM/src/chat_main.cpp
+++ b/plugins/TabSRMM/src/chat_main.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_manager.cpp b/plugins/TabSRMM/src/chat_manager.cpp
index dab25a0ddf..bc540bed44 100644
--- a/plugins/TabSRMM/src/chat_manager.cpp
+++ b/plugins/TabSRMM/src/chat_manager.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_options.cpp b/plugins/TabSRMM/src/chat_options.cpp
index f8c1c99341..710a46a0af 100644
--- a/plugins/TabSRMM/src/chat_options.cpp
+++ b/plugins/TabSRMM/src/chat_options.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_tools.cpp b/plugins/TabSRMM/src/chat_tools.cpp
index d2d7fdc31f..78a316ea6f 100644
--- a/plugins/TabSRMM/src/chat_tools.cpp
+++ b/plugins/TabSRMM/src/chat_tools.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/contactcache.cpp b/plugins/TabSRMM/src/contactcache.cpp
index 88b06217ca..2071555d94 100644
--- a/plugins/TabSRMM/src/contactcache.cpp
+++ b/plugins/TabSRMM/src/contactcache.cpp
@@ -1,478 +1,478 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/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.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// contact cache implementation
-//
-// the contact cache provides various services to the message window(s)
-// it also abstracts meta contacts.
-
-#include "stdafx.h"
-
-static OBJLIST<CContactCache> arContacts(50, NumericKeySortT);
-
-static DBCachedContact ccInvalid;
-
-CContactCache::CContactCache(MCONTACT hContact) :
- m_hContact(hContact),
- m_history(10)
-{
- if (hContact) {
- if ((cc = db_get_contact(hContact)) != nullptr) {
- initPhaseTwo();
- return;
- }
- }
-
- cc = &ccInvalid;
- m_szAccount = C_INVALID_ACCOUNT;
- m_isMeta = false;
- m_isValid = false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// 2nd part of the object initialization that must be callable during the
-// object's lifetime (not only on construction).
-
-void CContactCache::initPhaseTwo()
-{
- m_szAccount = nullptr;
- if (cc->szProto) {
- PROTOACCOUNT *acc = Proto_GetAccount(cc->szProto);
- if (acc && acc->tszAccountName)
- m_szAccount = acc->tszAccountName;
- }
-
- m_isValid = (cc->szProto != nullptr && m_szAccount != nullptr) ? true : false;
- if (m_isValid) {
- m_iStatus = db_get_w(m_hContact, cc->szProto, "Status", ID_STATUS_OFFLINE);
- m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here
- if (m_isMeta)
- updateMeta();
- updateNick();
- }
- else {
- m_szAccount = C_INVALID_ACCOUNT;
- m_isMeta = false;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// reset meta contact information.Used when meta contacts are disabled
-// on user's request.
-
-void CContactCache::resetMeta()
-{
- m_isMeta = false;
- m_szMetaProto = nullptr;
- m_iMetaStatus = ID_STATUS_OFFLINE;
- initPhaseTwo();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// if the contact has an open message window, close it.
-// window procedure will use setWindowData() to reset m_hwnd to 0.
-
-void CContactCache::closeWindow()
-{
- if (m_dat) {
- m_dat->m_bForcedClose = true;
- m_dat->Close();
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update private copy of the nick name.Use contact list name cache
-//
-// @return bool: true if nick has changed.
-
-bool CContactCache::updateNick()
-{
- bool fChanged = false;
- if (m_isValid) {
- wchar_t *tszNick = Clist_GetContactDisplayName(getActiveContact());
- if (tszNick && mir_wstrcmp(m_szNick, tszNick))
- fChanged = true;
- wcsncpy_s(m_szNick, (tszNick ? tszNick : L"<undef>"), _TRUNCATE);
- }
- return fChanged;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update meta(subcontact and - protocol) status.This runs when the
-// MC protocol fires one of its events OR when a relevant database value changes
-// in the master contact.
-
-void CContactCache::updateMeta()
-{
- if (m_isValid) {
- MCONTACT hOldSub = m_hSub;
- m_hSub = db_mc_getSrmmSub(cc->contactID);
- m_szMetaProto = Proto_GetBaseAccountName(m_hSub);
- m_iMetaStatus = (uint16_t)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE);
- PROTOACCOUNT *pa = Proto_GetAccount(m_szMetaProto);
- if (pa)
- m_szAccount = pa->tszAccountName;
-
- if (hOldSub != m_hSub) {
- updateNick();
- updateUIN();
- }
- }
- else {
- m_hSub = 0;
- m_szMetaProto = nullptr;
- m_iMetaStatus = ID_STATUS_OFFLINE;
- m_xStatus = 0;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// obtain the UIN.This is only maintained for open message windows
-// it also run when the subcontact for a MC changes.
-
-bool CContactCache::updateUIN()
-{
- m_szUIN[0] = 0;
-
- if (m_isValid) {
- ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, getActiveContact(), getActiveProto()));
- if (uid != nullptr)
- wcsncpy_s(m_szUIN, uid, _TRUNCATE);
- }
-
- return false;
-}
-
-void CContactCache::updateStats(int iType, size_t value)
-{
- if (m_stats == nullptr)
- m_stats = new TSessionStats();
-
- switch (iType) {
- case TSessionStats::UPDATE_WITH_LAST_RCV:
- if (!m_stats->lastReceivedChars)
- break;
- m_stats->iReceived++;
- m_stats->messageCount++;
- m_stats->iReceivedBytes += m_stats->lastReceivedChars;
- m_stats->lastReceivedChars = 0;
- break;
- case TSessionStats::INIT_TIMER:
- m_stats->started = time(0);
- break;
- case TSessionStats::SET_LAST_RCV:
- m_stats->lastReceivedChars = (unsigned int)value;
- break;
- case TSessionStats::BYTES_SENT:
- m_stats->iSent++;
- m_stats->messageCount++;
- m_stats->iSentBytes += (unsigned int)value;
- break;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-//set the window data for this contact.The window procedure of the message
-// dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache
-// that a message window is open for this contact.
-//
-// @param dat: CMsgDialog* - window data structure
-
-void CContactCache::setWindowData(CMsgDialog *dat)
-{
- m_dat = dat;
-
- if (dat) {
- updateStatusMsg();
- }
- else {
- // release memory - not needed when window isn't open
- replaceStrW(m_szStatusMsg, nullptr);
- replaceStrW(m_ListeningInfo, nullptr);
- replaceStrW(m_xStatusMsg, nullptr);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// saves message to the input history.
-// it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history.
-
-void CContactCache::saveHistory()
-{
- if (m_dat == nullptr)
- return;
-
- CCtrlRichEdit &pEntry = m_dat->GetEntry();
- ptrA szFromStream(pEntry.GetRichTextRtf());
- if (szFromStream != nullptr) {
- m_iHistoryCurrent = -1;
- m_history.insert(szFromStream.detach());
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// handle the input history scrolling for the message input area
-// @param wParam: VK_ keyboard code (VK_UP or VK_DOWN)
-
-void CContactCache::inputHistoryEvent(WPARAM wParam)
-{
- if (m_dat == nullptr)
- return;
-
- CCtrlRichEdit &pEntry = m_dat->GetEntry();
- if (m_history.getCount() > 0) {
- char *pszText;
- if (wParam == VK_UP) {
- if (m_iHistoryCurrent == 0)
- return;
-
- if (m_iHistoryCurrent < 0)
- m_iHistoryCurrent = m_history.getCount()-1;
- else
- m_iHistoryCurrent--;
- pszText = m_history[m_iHistoryCurrent];
- }
- else {
- if (m_iHistoryCurrent == -1)
- return;
-
- if (m_iHistoryCurrent == m_history.getCount() - 1) {
- m_iHistoryCurrent = -1;
- pszText = "";
- }
- else {
- m_iHistoryCurrent++;
- pszText = m_history[m_iHistoryCurrent];
- }
- }
-
- SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
- pEntry.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pszText);
- pEntry.SendMsg(EM_SETSEL, -1, -1);
- }
-
- pEntry.OnChange(&pEntry);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// release additional memory resources
-
-void CContactCache::releaseAlloced()
-{
- if (m_stats) {
- delete m_stats;
- m_stats = nullptr;
- }
-
- for (auto &it : m_history)
- mir_free(it);
- m_history.destroy();
-
- mir_free(m_szStatusMsg);
- m_szStatusMsg = nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// when a contact is deleted, mark it as invalid in the cache and release
-// all memory it has allocated.
-
-void CContactCache::deletedHandler()
-{
- cc = &ccInvalid;
- m_isValid = false;
- if (m_dat) {
- m_dat->m_bForcedClose = true;
-
- // this message must be sent async to allow a contact to rest in peace before window gets closed
- ::PostMessage(m_dat->GetHwnd(), WM_CLOSE, 1, 2);
- }
-
- releaseAlloced();
- m_hContact = INVALID_CONTACT_ID;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update all or only the given status message information from the database
-//
-// @param szKey: char* database key name or 0 to reload all messages
-
-void CContactCache::updateStatusMsg(const char *szKey)
-{
- if (!m_isValid)
- return;
-
- MCONTACT hContact = getActiveContact();
-
- if (szKey == nullptr || (szKey && !mir_strcmp("StatusMsg", szKey))) {
- if (m_szStatusMsg)
- mir_free(m_szStatusMsg);
- m_szStatusMsg = nullptr;
- ptrW szStatus(db_get_wsa(hContact, "CList", "StatusMsg"));
- if (szStatus != 0)
- m_szStatusMsg = (mir_wstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : nullptr);
- }
- if (szKey == nullptr || (szKey && !mir_strcmp("ListeningTo", szKey))) {
- if (m_ListeningInfo)
- mir_free(m_ListeningInfo);
- m_ListeningInfo = nullptr;
- ptrW szListeningTo(db_get_wsa(hContact, cc->szProto, "ListeningTo"));
- if (szListeningTo != 0 && *szListeningTo)
- m_ListeningInfo = szListeningTo.detach();
- }
- if (szKey == nullptr || (szKey && !mir_strcmp("XStatusMsg", szKey))) {
- if (m_xStatusMsg)
- mir_free(m_xStatusMsg);
- m_xStatusMsg = nullptr;
- ptrW szXStatusMsg(db_get_wsa(hContact, cc->szProto, "XStatusMsg"));
- if (szXStatusMsg != 0 && *szXStatusMsg)
- m_xStatusMsg = szXStatusMsg.detach();
- }
- m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve contact cache entry for the given contact.It _never_ returns zero, for a hContact
-// 0, it retrieves a dummy object.
-// Non-existing cache entries are created on demand.
-//
-// @param hContact: contact handle
-// @return CContactCache* pointer to the cache entry for this contact
-
-CContactCache* CContactCache::getContactCache(MCONTACT hContact)
-{
- CContactCache *cc = arContacts.find((CContactCache*)&hContact);
- if (cc == nullptr) {
- cc = new CContactCache(hContact);
- arContacts.insert(cc);
- }
- return cc;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// when the state of the meta contacts protocol changes from enabled to disabled
-// (or vice versa), this updates the contact cache
-//
-// it is ONLY called from the DBSettingChanged() event handler when the relevant
-// database value is touched.
-
-int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM)
-{
- for (auto &c : arContacts) {
- if (c->isMeta() && !bMetaEnabled) {
- c->closeWindow();
- c->resetMeta();
- }
-
- // meta contacts are enabled, but current contact is a subcontact - > close window
- if (bMetaEnabled && c->isSubContact())
- c->closeWindow();
-
- // reset meta contact information, if metacontacts protocol became avail
- if (bMetaEnabled && !c->cc->IsMeta())
- c->resetMeta();
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// normalize the status message with proper cr / lf sequences.
-// @param src wchar_t*: original status message
-// @param fStripAll bool: strip all cr/lf sequences and replace them with spaces (use for title bar)
-// @return wchar_t*: converted status message. CALLER is responsible to mir_free it, MUST use mir_free()
-
-wchar_t* CContactCache::getNormalizedStatusMsg(const wchar_t *src, bool fStripAll)
-{
- if (src == nullptr || mir_wstrlen(src) < 2)
- return nullptr;
-
- CMStringW dest;
-
- for (int i = 0; src[i] != 0; i++) {
- if (src[i] == 0x0d || src[i] == '\t')
- continue;
- if (i && src[i] == (wchar_t)0x0a) {
- if (fStripAll) {
- dest.AppendChar(' ');
- continue;
- }
- dest.AppendChar('\n');
- continue;
- }
- dest.AppendChar(src[i]);
- }
-
- return mir_wstrndup(dest, dest.GetLength());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve the tab / title icon for the corresponding session.
-
-HICON CContactCache::getIcon(int &iSize) const
-{
- if (!m_dat)
- return Skin_LoadProtoIcon(cc->szProto, getStatus());
-
- if (m_dat->m_bErrorState)
- return PluginConfig.g_iconErr;
- if (m_dat->m_bCanFlashTab)
- return m_dat->m_iFlashIcon;
-
- if (m_dat->isChat() && m_dat->m_iFlashIcon) {
- int sizeX, sizeY;
- Utils::getIconSize(m_dat->m_iFlashIcon, sizeX, sizeY);
- iSize = sizeX;
- return m_dat->m_iFlashIcon;
- }
- if (m_dat->m_hTabIcon == m_dat->m_hTabStatusIcon && m_dat->m_hXStatusIcon)
- return m_dat->m_hXStatusIcon;
- return m_dat->m_hTabIcon;
-}
-
-size_t CContactCache::getMaxMessageLength()
-{
- if (m_nMax == 0) {
- MCONTACT hContact = getActiveContact();
- LPCSTR szProto = getActiveProto();
- if (szProto) {
- m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact);
- if (m_nMax) {
- if (M.GetByte("autosplit", 0))
- m_nMax = 20000;
- }
- else m_nMax = 20000;
-
- m_dat->LimitMessageText(m_nMax);
- }
- }
- return m_nMax;
-}
-
-bool CContactCache::updateStatus(int iStatus)
-{
- m_iOldStatus = m_iStatus;
- m_iStatus = iStatus;
- return m_iOldStatus != iStatus;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/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.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// contact cache implementation
+//
+// the contact cache provides various services to the message window(s)
+// it also abstracts meta contacts.
+
+#include "stdafx.h"
+
+static OBJLIST<CContactCache> arContacts(50, NumericKeySortT);
+
+static DBCachedContact ccInvalid;
+
+CContactCache::CContactCache(MCONTACT hContact) :
+ m_hContact(hContact),
+ m_history(10)
+{
+ if (hContact) {
+ if ((cc = db_get_contact(hContact)) != nullptr) {
+ initPhaseTwo();
+ return;
+ }
+ }
+
+ cc = &ccInvalid;
+ m_szAccount = C_INVALID_ACCOUNT;
+ m_isMeta = false;
+ m_isValid = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 2nd part of the object initialization that must be callable during the
+// object's lifetime (not only on construction).
+
+void CContactCache::initPhaseTwo()
+{
+ m_szAccount = nullptr;
+ if (cc->szProto) {
+ PROTOACCOUNT *acc = Proto_GetAccount(cc->szProto);
+ if (acc && acc->tszAccountName)
+ m_szAccount = acc->tszAccountName;
+ }
+
+ m_isValid = (cc->szProto != nullptr && m_szAccount != nullptr) ? true : false;
+ if (m_isValid) {
+ m_iStatus = db_get_w(m_hContact, cc->szProto, "Status", ID_STATUS_OFFLINE);
+ m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here
+ if (m_isMeta)
+ updateMeta();
+ updateNick();
+ }
+ else {
+ m_szAccount = C_INVALID_ACCOUNT;
+ m_isMeta = false;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reset meta contact information.Used when meta contacts are disabled
+// on user's request.
+
+void CContactCache::resetMeta()
+{
+ m_isMeta = false;
+ m_szMetaProto = nullptr;
+ m_iMetaStatus = ID_STATUS_OFFLINE;
+ initPhaseTwo();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// if the contact has an open message window, close it.
+// window procedure will use setWindowData() to reset m_hwnd to 0.
+
+void CContactCache::closeWindow()
+{
+ if (m_dat) {
+ m_dat->m_bForcedClose = true;
+ m_dat->Close();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update private copy of the nick name.Use contact list name cache
+//
+// @return bool: true if nick has changed.
+
+bool CContactCache::updateNick()
+{
+ bool fChanged = false;
+ if (m_isValid) {
+ wchar_t *tszNick = Clist_GetContactDisplayName(getActiveContact());
+ if (tszNick && mir_wstrcmp(m_szNick, tszNick))
+ fChanged = true;
+ wcsncpy_s(m_szNick, (tszNick ? tszNick : L"<undef>"), _TRUNCATE);
+ }
+ return fChanged;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update meta(subcontact and - protocol) status.This runs when the
+// MC protocol fires one of its events OR when a relevant database value changes
+// in the master contact.
+
+void CContactCache::updateMeta()
+{
+ if (m_isValid) {
+ MCONTACT hOldSub = m_hSub;
+ m_hSub = db_mc_getSrmmSub(cc->contactID);
+ m_szMetaProto = Proto_GetBaseAccountName(m_hSub);
+ m_iMetaStatus = (uint16_t)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE);
+ PROTOACCOUNT *pa = Proto_GetAccount(m_szMetaProto);
+ if (pa)
+ m_szAccount = pa->tszAccountName;
+
+ if (hOldSub != m_hSub) {
+ updateNick();
+ updateUIN();
+ }
+ }
+ else {
+ m_hSub = 0;
+ m_szMetaProto = nullptr;
+ m_iMetaStatus = ID_STATUS_OFFLINE;
+ m_xStatus = 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// obtain the UIN.This is only maintained for open message windows
+// it also run when the subcontact for a MC changes.
+
+bool CContactCache::updateUIN()
+{
+ m_szUIN[0] = 0;
+
+ if (m_isValid) {
+ ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, getActiveContact(), getActiveProto()));
+ if (uid != nullptr)
+ wcsncpy_s(m_szUIN, uid, _TRUNCATE);
+ }
+
+ return false;
+}
+
+void CContactCache::updateStats(int iType, size_t value)
+{
+ if (m_stats == nullptr)
+ m_stats = new TSessionStats();
+
+ switch (iType) {
+ case TSessionStats::UPDATE_WITH_LAST_RCV:
+ if (!m_stats->lastReceivedChars)
+ break;
+ m_stats->iReceived++;
+ m_stats->messageCount++;
+ m_stats->iReceivedBytes += m_stats->lastReceivedChars;
+ m_stats->lastReceivedChars = 0;
+ break;
+ case TSessionStats::INIT_TIMER:
+ m_stats->started = time(0);
+ break;
+ case TSessionStats::SET_LAST_RCV:
+ m_stats->lastReceivedChars = (unsigned int)value;
+ break;
+ case TSessionStats::BYTES_SENT:
+ m_stats->iSent++;
+ m_stats->messageCount++;
+ m_stats->iSentBytes += (unsigned int)value;
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//set the window data for this contact.The window procedure of the message
+// dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache
+// that a message window is open for this contact.
+//
+// @param dat: CMsgDialog* - window data structure
+
+void CContactCache::setWindowData(CMsgDialog *dat)
+{
+ m_dat = dat;
+
+ if (dat) {
+ updateStatusMsg();
+ }
+ else {
+ // release memory - not needed when window isn't open
+ replaceStrW(m_szStatusMsg, nullptr);
+ replaceStrW(m_ListeningInfo, nullptr);
+ replaceStrW(m_xStatusMsg, nullptr);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// saves message to the input history.
+// it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history.
+
+void CContactCache::saveHistory()
+{
+ if (m_dat == nullptr)
+ return;
+
+ CCtrlRichEdit &pEntry = m_dat->GetEntry();
+ ptrA szFromStream(pEntry.GetRichTextRtf());
+ if (szFromStream != nullptr) {
+ m_iHistoryCurrent = -1;
+ m_history.insert(szFromStream.detach());
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// handle the input history scrolling for the message input area
+// @param wParam: VK_ keyboard code (VK_UP or VK_DOWN)
+
+void CContactCache::inputHistoryEvent(WPARAM wParam)
+{
+ if (m_dat == nullptr)
+ return;
+
+ CCtrlRichEdit &pEntry = m_dat->GetEntry();
+ if (m_history.getCount() > 0) {
+ char *pszText;
+ if (wParam == VK_UP) {
+ if (m_iHistoryCurrent == 0)
+ return;
+
+ if (m_iHistoryCurrent < 0)
+ m_iHistoryCurrent = m_history.getCount()-1;
+ else
+ m_iHistoryCurrent--;
+ pszText = m_history[m_iHistoryCurrent];
+ }
+ else {
+ if (m_iHistoryCurrent == -1)
+ return;
+
+ if (m_iHistoryCurrent == m_history.getCount() - 1) {
+ m_iHistoryCurrent = -1;
+ pszText = "";
+ }
+ else {
+ m_iHistoryCurrent++;
+ pszText = m_history[m_iHistoryCurrent];
+ }
+ }
+
+ SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
+ pEntry.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pszText);
+ pEntry.SendMsg(EM_SETSEL, -1, -1);
+ }
+
+ pEntry.OnChange(&pEntry);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// release additional memory resources
+
+void CContactCache::releaseAlloced()
+{
+ if (m_stats) {
+ delete m_stats;
+ m_stats = nullptr;
+ }
+
+ for (auto &it : m_history)
+ mir_free(it);
+ m_history.destroy();
+
+ mir_free(m_szStatusMsg);
+ m_szStatusMsg = nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// when a contact is deleted, mark it as invalid in the cache and release
+// all memory it has allocated.
+
+void CContactCache::deletedHandler()
+{
+ cc = &ccInvalid;
+ m_isValid = false;
+ if (m_dat) {
+ m_dat->m_bForcedClose = true;
+
+ // this message must be sent async to allow a contact to rest in peace before window gets closed
+ ::PostMessage(m_dat->GetHwnd(), WM_CLOSE, 1, 2);
+ }
+
+ releaseAlloced();
+ m_hContact = INVALID_CONTACT_ID;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update all or only the given status message information from the database
+//
+// @param szKey: char* database key name or 0 to reload all messages
+
+void CContactCache::updateStatusMsg(const char *szKey)
+{
+ if (!m_isValid)
+ return;
+
+ MCONTACT hContact = getActiveContact();
+
+ if (szKey == nullptr || (szKey && !mir_strcmp("StatusMsg", szKey))) {
+ if (m_szStatusMsg)
+ mir_free(m_szStatusMsg);
+ m_szStatusMsg = nullptr;
+ ptrW szStatus(db_get_wsa(hContact, "CList", "StatusMsg"));
+ if (szStatus != 0)
+ m_szStatusMsg = (mir_wstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : nullptr);
+ }
+ if (szKey == nullptr || (szKey && !mir_strcmp("ListeningTo", szKey))) {
+ if (m_ListeningInfo)
+ mir_free(m_ListeningInfo);
+ m_ListeningInfo = nullptr;
+ ptrW szListeningTo(db_get_wsa(hContact, cc->szProto, "ListeningTo"));
+ if (szListeningTo != 0 && *szListeningTo)
+ m_ListeningInfo = szListeningTo.detach();
+ }
+ if (szKey == nullptr || (szKey && !mir_strcmp("XStatusMsg", szKey))) {
+ if (m_xStatusMsg)
+ mir_free(m_xStatusMsg);
+ m_xStatusMsg = nullptr;
+ ptrW szXStatusMsg(db_get_wsa(hContact, cc->szProto, "XStatusMsg"));
+ if (szXStatusMsg != 0 && *szXStatusMsg)
+ m_xStatusMsg = szXStatusMsg.detach();
+ }
+ m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve contact cache entry for the given contact.It _never_ returns zero, for a hContact
+// 0, it retrieves a dummy object.
+// Non-existing cache entries are created on demand.
+//
+// @param hContact: contact handle
+// @return CContactCache* pointer to the cache entry for this contact
+
+CContactCache* CContactCache::getContactCache(MCONTACT hContact)
+{
+ CContactCache *cc = arContacts.find((CContactCache*)&hContact);
+ if (cc == nullptr) {
+ cc = new CContactCache(hContact);
+ arContacts.insert(cc);
+ }
+ return cc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// when the state of the meta contacts protocol changes from enabled to disabled
+// (or vice versa), this updates the contact cache
+//
+// it is ONLY called from the DBSettingChanged() event handler when the relevant
+// database value is touched.
+
+int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM)
+{
+ for (auto &c : arContacts) {
+ if (c->isMeta() && !bMetaEnabled) {
+ c->closeWindow();
+ c->resetMeta();
+ }
+
+ // meta contacts are enabled, but current contact is a subcontact - > close window
+ if (bMetaEnabled && c->isSubContact())
+ c->closeWindow();
+
+ // reset meta contact information, if metacontacts protocol became avail
+ if (bMetaEnabled && !c->cc->IsMeta())
+ c->resetMeta();
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// normalize the status message with proper cr / lf sequences.
+// @param src wchar_t*: original status message
+// @param fStripAll bool: strip all cr/lf sequences and replace them with spaces (use for title bar)
+// @return wchar_t*: converted status message. CALLER is responsible to mir_free it, MUST use mir_free()
+
+wchar_t* CContactCache::getNormalizedStatusMsg(const wchar_t *src, bool fStripAll)
+{
+ if (src == nullptr || mir_wstrlen(src) < 2)
+ return nullptr;
+
+ CMStringW dest;
+
+ for (int i = 0; src[i] != 0; i++) {
+ if (src[i] == 0x0d || src[i] == '\t')
+ continue;
+ if (i && src[i] == (wchar_t)0x0a) {
+ if (fStripAll) {
+ dest.AppendChar(' ');
+ continue;
+ }
+ dest.AppendChar('\n');
+ continue;
+ }
+ dest.AppendChar(src[i]);
+ }
+
+ return mir_wstrndup(dest, dest.GetLength());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve the tab / title icon for the corresponding session.
+
+HICON CContactCache::getIcon(int &iSize) const
+{
+ if (!m_dat)
+ return Skin_LoadProtoIcon(cc->szProto, getStatus());
+
+ if (m_dat->m_bErrorState)
+ return PluginConfig.g_iconErr;
+ if (m_dat->m_bCanFlashTab)
+ return m_dat->m_iFlashIcon;
+
+ if (m_dat->isChat() && m_dat->m_iFlashIcon) {
+ int sizeX, sizeY;
+ Utils::getIconSize(m_dat->m_iFlashIcon, sizeX, sizeY);
+ iSize = sizeX;
+ return m_dat->m_iFlashIcon;
+ }
+ if (m_dat->m_hTabIcon == m_dat->m_hTabStatusIcon && m_dat->m_hXStatusIcon)
+ return m_dat->m_hXStatusIcon;
+ return m_dat->m_hTabIcon;
+}
+
+size_t CContactCache::getMaxMessageLength()
+{
+ if (m_nMax == 0) {
+ MCONTACT hContact = getActiveContact();
+ LPCSTR szProto = getActiveProto();
+ if (szProto) {
+ m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact);
+ if (m_nMax) {
+ if (M.GetByte("autosplit", 0))
+ m_nMax = 20000;
+ }
+ else m_nMax = 20000;
+
+ m_dat->LimitMessageText(m_nMax);
+ }
+ }
+ return m_nMax;
+}
+
+bool CContactCache::updateStatus(int iStatus)
+{
+ m_iOldStatus = m_iStatus;
+ m_iStatus = iStatus;
+ return m_iOldStatus != iStatus;
+}
diff --git a/plugins/TabSRMM/src/contactcache.h b/plugins/TabSRMM/src/contactcache.h
index 80a7ff18b2..1168b60258 100644
--- a/plugins/TabSRMM/src/contactcache.h
+++ b/plugins/TabSRMM/src/contactcache.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/container.cpp b/plugins/TabSRMM/src/container.cpp
index c26ddddb23..2c6c218631 100644
--- a/plugins/TabSRMM/src/container.cpp
+++ b/plugins/TabSRMM/src/container.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/containeroptions.cpp b/plugins/TabSRMM/src/containeroptions.cpp
index f956233387..96eaf7480d 100644
--- a/plugins/TabSRMM/src/containeroptions.cpp
+++ b/plugins/TabSRMM/src/containeroptions.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/controls.cpp b/plugins/TabSRMM/src/controls.cpp
index 4e72e2c8e2..1ac916f6cc 100644
--- a/plugins/TabSRMM/src/controls.cpp
+++ b/plugins/TabSRMM/src/controls.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/controls.h b/plugins/TabSRMM/src/controls.h
index c3ff022fd1..b878f347d4 100644
--- a/plugins/TabSRMM/src/controls.h
+++ b/plugins/TabSRMM/src/controls.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/eventpopups.cpp b/plugins/TabSRMM/src/eventpopups.cpp
index 37731f2f44..3b35900a1a 100644
--- a/plugins/TabSRMM/src/eventpopups.cpp
+++ b/plugins/TabSRMM/src/eventpopups.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/functions.h b/plugins/TabSRMM/src/functions.h
index e8c57f4476..dc0bb47c5f 100644
--- a/plugins/TabSRMM/src/functions.h
+++ b/plugins/TabSRMM/src/functions.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/generic_msghandlers.cpp b/plugins/TabSRMM/src/generic_msghandlers.cpp
index 9399fdc313..f704717da6 100644
--- a/plugins/TabSRMM/src/generic_msghandlers.cpp
+++ b/plugins/TabSRMM/src/generic_msghandlers.cpp
@@ -1,1358 +1,1358 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/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.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// these are generic message handlers which are used by the message dialog window procedure.
-// calling them directly instead of using SendMessage() is faster.
-// also contains various callback functions for custom buttons
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Save message log for given session as RTF document
-
-/*
-void CMsgDialog::DM_SaveLogAsRTF() const
-{
- if (m_hwndIEView != nullptr) {
- IEVIEWEVENT event = { sizeof(event) };
- event.hwnd = m_hwndIEView;
- event.hContact = m_hContact;
- event.iType = IEE_SAVE_DOCUMENT;
- CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
- }
- else {
- wchar_t szFilter[MAX_PATH], szFilename[MAX_PATH];
- mir_snwprintf(szFilter, L"%s%c*.rtf%c%c", TranslateT("Rich Edit file"), 0, 0, 0);
- mir_snwprintf(szFilename, L"%s.rtf", m_cache->getNick());
-
- Utils::sanitizeFilename(szFilename);
-
- wchar_t szInitialDir[MAX_PATH + 2];
- mir_snwprintf(szInitialDir, L"%s%s\\", M.getDataPath(), L"\\Saved message logs");
- CreateDirectoryTreeW(szInitialDir);
-
- OPENFILENAME ofn = { 0 };
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = m_hwnd;
- ofn.lpstrFile = szFilename;
- ofn.lpstrFilter = szFilter;
- ofn.lpstrInitialDir = szInitialDir;
- ofn.nMaxFile = MAX_PATH;
- ofn.Flags = OFN_HIDEREADONLY;
- ofn.lpstrDefExt = L"rtf";
- if (GetSaveFileName(&ofn)) {
- EDITSTREAM stream = { 0 };
- stream.dwCookie = (DWORD_PTR)szFilename;
- stream.dwError = 0;
- stream.pfnCallback = Utils::StreamOut;
- m_rtf.SendMsg(EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM)&stream);
- }
- }
-}
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// checks if the balloon tooltip can be dismissed (usually called by WM_MOUSEMOVE events)
-
-void CMsgDialog::DM_DismissTip(const POINT& pt)
-{
- if (!IsWindowVisible(m_hwndTip))
- return;
-
- RECT rc;
- GetWindowRect(m_hwndTip, &rc);
- if (PtInRect(&rc, pt))
- return;
-
- if (abs(pt.x - m_ptTipActivation.x) > 5 || abs(pt.y - m_ptTipActivation.y) > 5) {
- SendMessage(m_hwndTip, TTM_TRACKACTIVATE, FALSE, 0);
- m_ptTipActivation.x = m_ptTipActivation.y = 0;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// initialize the balloon tooltip for message window notifications
-
-void CMsgDialog::DM_InitTip()
-{
- m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, nullptr, g_plugin.getInst(), (LPVOID)nullptr);
-
- memset(&ti, 0, sizeof(ti));
- ti.cbSize = sizeof(ti);
- ti.lpszText = TranslateT("No status message");
- ti.hinst = g_plugin.getInst();
- ti.hwnd = m_hwnd;
- ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT;
- ti.uId = (UINT_PTR)m_hwnd;
- SendMessage(m_hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
-
- SetWindowPos(m_hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// checks generic hotkeys valid for both IM and MUC sessions
-//
-// returns 1 for handled hotkeys, 0 otherwise.
-
-bool CMsgDialog::DM_GenericHotkeysCheck(MSG *message)
-{
- LRESULT mim_hotkey_check = Hotkey_Check(message, TABSRMM_HK_SECTION_GENERIC);
-
- switch (mim_hotkey_check) {
- case TABSRMM_HK_PASTEANDSEND:
- HandlePasteAndSend();
- return true;
-
- case TABSRMM_HK_HISTORY:
- m_btnHistory.Click();
- return true;
-
- case TABSRMM_HK_CONTAINEROPTIONS:
- m_pContainer->OptionsDialog();
- return true;
-
- case TABSRMM_HK_TOGGLEINFOPANEL:
- m_pPanel.setActive(!m_pPanel.isActive());
- m_pPanel.showHide();
- return true;
-
- case TABSRMM_HK_TOGGLETOOLBAR:
- SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLETOOLBAR, 0);
- return true;
-
- case TABSRMM_HK_CLEARLOG:
- tabClearLog();
- return true;
-
- case TABSRMM_HK_TOGGLESIDEBAR:
- if (m_pContainer->m_pSideBar->isActive())
- SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
- return true;
-
- case TABSRMM_HK_CLOSE_OTHER:
- CloseOtherTabs(m_pContainer->m_hwndTabs, *this);
- return true;
- }
- return false;
-}
-
-LRESULT CMsgDialog::DM_MsgWindowCmdHandler(UINT cmd, WPARAM wParam, LPARAM lParam)
-{
- RECT rc;
- int iSelection;
- HMENU submenu;
-
- switch (cmd) {
- case IDC_SRMM_BOLD:
- case IDC_SRMM_ITALICS:
- case IDC_SRMM_UNDERLINE:
- case IDC_FONTSTRIKEOUT:
- if (m_SendFormat != 0) { // dont use formatting if disabled
- CHARFORMAT2 cf, cfOld;
- memset(&cf, 0, sizeof(CHARFORMAT2));
- memset(&cfOld, 0, sizeof(CHARFORMAT2));
- cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2);
- cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
- m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld);
- BOOL isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD);
- BOOL isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC);
- BOOL isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE);
- BOOL isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT);
-
- int ctrlId = LOWORD(wParam);
- if (ctrlId == IDC_SRMM_BOLD && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_BOLD)))
- break;
- if (ctrlId == IDC_SRMM_ITALICS && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_ITALICS)))
- break;
- if (ctrlId == IDC_SRMM_UNDERLINE && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_UNDERLINE)))
- break;
- if (ctrlId == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_FONTSTRIKEOUT)))
- break;
- if (ctrlId == IDC_SRMM_BOLD) {
- cf.dwEffects = isBold ? 0 : CFE_BOLD;
- cf.dwMask = CFM_BOLD;
- CheckDlgButton(m_hwnd, IDC_SRMM_BOLD, !isBold ? BST_CHECKED : BST_UNCHECKED);
- }
- else if (ctrlId == IDC_SRMM_ITALICS) {
- cf.dwEffects = isItalic ? 0 : CFE_ITALIC;
- cf.dwMask = CFM_ITALIC;
- CheckDlgButton(m_hwnd, IDC_SRMM_ITALICS, !isItalic ? BST_CHECKED : BST_UNCHECKED);
- }
- else if (ctrlId == IDC_SRMM_UNDERLINE) {
- cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE;
- cf.dwMask = CFM_UNDERLINE;
- CheckDlgButton(m_hwnd, IDC_SRMM_UNDERLINE, !isUnderline ? BST_CHECKED : BST_UNCHECKED);
- }
- else if (ctrlId == IDC_FONTSTRIKEOUT) {
- cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT;
- cf.dwMask = CFM_STRIKEOUT;
- CheckDlgButton(m_hwnd, IDC_FONTSTRIKEOUT, !isStrikeout ? BST_CHECKED : BST_UNCHECKED);
- }
- m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
- }
- break;
-
- case IDCANCEL:
- ShowWindow(m_pContainer->m_hwnd, SW_MINIMIZE);
- return FALSE;
-
- case IDC_CLOSE:
- PostMessage(m_hwnd, WM_CLOSE, 1, 0);
- break;
-
- case IDC_NAME:
- if (GetKeyState(VK_SHIFT) & 0x8000) // copy UIN
- Utils_ClipboardCopy(m_cache->getUIN());
- else
- CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(m_cache->getActiveContact()), 0);
- break;
-
- case IDC_TIME:
- submenu = GetSubMenu(PluginConfig.g_hMenuContext, 2);
- MsgWindowUpdateMenu(submenu, MENU_LOGMENU);
-
- GetWindowRect(GetDlgItem(m_hwnd, IDC_TIME), &rc);
-
- iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
- return MsgWindowMenuHandler(iSelection, MENU_LOGMENU);
-
- case IDC_PROTOMENU:
- submenu = GetSubMenu(PluginConfig.g_hMenuContext, 4);
- {
- bool iOldGlobalSendFormat = g_plugin.bSendFormat;
- int iLocalFormat = M.GetDword(m_hContact, "sendformat", 0);
- int iNewLocalFormat = iLocalFormat;
-
- GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
-
- CheckMenuItem(submenu, ID_MODE_GLOBAL, !m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_MODE_PRIVATE, m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
-
- // formatting menu..
- CheckMenuItem(submenu, ID_GLOBAL_BBCODE, (g_plugin.bSendFormat) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_GLOBAL_OFF, (g_plugin.bSendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
-
- CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, (iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, (iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_THISCONTACT_OFF, (iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED);
-
- iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
- switch (iSelection) {
- case ID_MODE_GLOBAL:
- m_bSplitterOverride = false;
- db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 0);
- LoadSplitter();
- AdjustBottomAvatarDisplay();
- DM_RecalcPictureSize();
- Resize();
- break;
-
- case ID_MODE_PRIVATE:
- m_bSplitterOverride = true;
- db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 1);
- LoadSplitter();
- AdjustBottomAvatarDisplay();
- DM_RecalcPictureSize();
- Resize();
- break;
-
- case ID_GLOBAL_BBCODE:
- g_plugin.bSendFormat = SENDFORMAT_BBCODE;
- break;
-
- case ID_GLOBAL_OFF:
- g_plugin.bSendFormat = SENDFORMAT_NONE;
- break;
-
- case ID_THISCONTACT_GLOBALSETTING:
- iNewLocalFormat = 0;
- break;
-
- case ID_THISCONTACT_BBCODE:
- iNewLocalFormat = SENDFORMAT_BBCODE;
- break;
-
- case ID_THISCONTACT_OFF:
- iNewLocalFormat = -1;
- break;
- }
-
- if (iNewLocalFormat == 0)
- db_unset(m_hContact, SRMSGMOD_T, "sendformat");
- else if (iNewLocalFormat != iLocalFormat)
- db_set_dw(m_hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat);
-
- if (iNewLocalFormat != iLocalFormat || g_plugin.bSendFormat != iOldGlobalSendFormat) {
- m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
- if (m_SendFormat == -1) // per contact override to disable it..
- m_SendFormat = 0;
- Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
- }
- }
- break;
-
- case IDC_TOGGLETOOLBAR:
- if (lParam == 1)
- m_pContainer->cfg.flags.m_bNoMenuBar = !m_pContainer->cfg.flags.m_bNoMenuBar;
- else
- m_pContainer->cfg.flags.m_bHideToolbar = !m_pContainer->cfg.flags.m_bHideToolbar;
- m_pContainer->ApplySetting(true);
- break;
-
- case IDC_SENDMENU:
- submenu = GetSubMenu(PluginConfig.g_hMenuContext, 3);
-
- GetWindowRect(GetDlgItem(m_hwnd, IDOK), &rc);
- CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, (m_sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, m_sendMode == 0 ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, (m_sendMode & SMODE_CONTAINER) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, (m_sendMode & SMODE_SENDLATER) ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, (m_sendMode & SMODE_NOACK) ? MF_CHECKED : MF_UNCHECKED);
-
- if (lParam)
- iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
- else
- iSelection = HIWORD(wParam);
-
- switch (iSelection) {
- case ID_SENDMENU_SENDTOMULTIPLEUSERS:
- m_sendMode ^= SMODE_MULTIPLE;
- if (m_sendMode & SMODE_MULTIPLE)
- DM_CreateClist();
- else if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
- DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
- break;
- case ID_SENDMENU_SENDDEFAULT:
- m_sendMode = 0;
- break;
- case ID_SENDMENU_SENDTOCONTAINER:
- m_sendMode ^= SMODE_CONTAINER;
- RedrawWindow(m_hwnd, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW);
- break;
- case ID_SENDMENU_SENDLATER:
- if (SendLater::Avail)
- m_sendMode ^= SMODE_SENDLATER;
- else
- CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
- break;
- case ID_SENDMENU_SENDWITHOUTTIMEOUTS:
- m_sendMode ^= SMODE_NOACK;
- if (m_sendMode & SMODE_NOACK)
- db_set_b(m_hContact, SRMSGMOD_T, "no_ack", 1);
- else
- db_unset(m_hContact, SRMSGMOD_T, "no_ack");
- break;
- }
- db_set_b(m_hContact, SRMSGMOD_T, "no_ack", (uint8_t)(m_sendMode & SMODE_NOACK ? 1 : 0));
- SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
- if (m_sendMode & SMODE_MULTIPLE || m_sendMode & SMODE_CONTAINER) {
- SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
- RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
- }
- else {
- if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
- DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
- SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
- RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
- }
- m_pContainer->QueryClientArea(rc);
- Resize();
- DM_ScrollToBottom(1, 1);
- Utils::showDlgControl(m_hwnd, IDC_MULTISPLITTER, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
- Utils::showDlgControl(m_hwnd, IDC_CLIST, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
- break;
-
- case IDC_TOGGLESIDEBAR:
- SendMessage(m_pContainer->m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
- break;
-
- case IDC_PIC:
- GetClientRect(m_hwnd, &rc);
-
- m_bEditNotesActive = !m_bEditNotesActive;
- if (m_bEditNotesActive) {
- int iLen = GetWindowTextLength(m_message.GetHwnd());
- if (iLen != 0) {
- ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("You cannot edit user notes when there are unsent messages"));
- m_bEditNotesActive = false;
- break;
- }
-
- if (!m_bIsAutosizingInput) {
- m_iSplitterSaved = m_iSplitterY;
- m_iSplitterY = rc.bottom / 2;
- SendMessage(m_hwnd, WM_SIZE, 1, 1);
- }
-
- ptrW wszText(db_get_wsa(m_hContact, "UserInfo", "MyNotes"));
- if (wszText != nullptr)
- m_message.SetText(wszText);
- }
- else {
- ptrW buf(m_message.GetText());
- db_set_ws(m_hContact, "UserInfo", "MyNotes", buf);
- m_message.SetText(L"");
-
- if (!m_bIsAutosizingInput) {
- m_iSplitterY = m_iSplitterSaved;
- Resize();
- DM_ScrollToBottom(0, 1);
- }
- }
- SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
- SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
- RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
-
- if (m_bEditNotesActive)
- CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK | MB_ICONINFORMATION);
- break;
-
- case IDM_CLEAR:
- tabClearLog();
- break;
-
- case IDC_PROTOCOL:
- submenu = Menu_BuildContactMenu(m_hContact);
- if (lParam == 0)
- GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
- else
- GetWindowRect((HWND)lParam, &rc);
-
- iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
- if (iSelection)
- Clist_MenuProcessCommand(LOWORD(iSelection), MPCF_CONTACTMENU, m_hContact);
-
- DestroyMenu(submenu);
- break;
-
- // error control
- case IDC_CANCELSEND:
- DM_ErrorDetected(MSGERROR_CANCEL, 0);
- break;
-
- case IDC_RETRY:
- DM_ErrorDetected(MSGERROR_RETRY, 0);
- break;
-
- case IDC_MSGSENDLATER:
- DM_ErrorDetected(MSGERROR_SENDLATER, 0);
- break;
-
- case IDC_SELFTYPING:
- if (AllowTyping()) {
- int iCurrentTypingMode = g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew);
- if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) {
- DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
- m_nTypeMode = PROTOTYPE_SELFTYPING_OFF;
- }
- g_plugin.setByte(m_hContact, SRMSGSET_TYPING, (uint8_t)!iCurrentTypingMode);
- }
- break;
-
- default:
- return 0;
- }
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// initialize rich edit control (log and edit control) for both MUC and
-// standard IM session windows.
-
-void CMsgDialog::DM_InitRichEdit()
-{
- char *szStreamOut = nullptr;
- if (!isChat() && GetWindowTextLength(m_message.GetHwnd()) > 0)
- szStreamOut = m_message.GetRichTextRtf();
- SetWindowText(m_message.GetHwnd(), L"");
-
- m_pLog->UpdateOptions();
-
- m_message.SendMsg(EM_SETBKGNDCOLOR, 0, m_pContainer->m_theme.inputbg);
-
- CHARFORMAT2 cf2 = {};
- cf2.cbSize = sizeof(cf2);
-
- if (isChat()) {
- LOGFONTW lf;
- COLORREF inputcharcolor;
- LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor);
-
- cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR;
- cf2.crTextColor = inputcharcolor;
- cf2.bCharSet = lf.lfCharSet;
- cf2.crBackColor = m_pContainer->m_theme.inputbg;
- wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
- cf2.dwEffects = 0;
- cf2.wWeight = (uint16_t)lf.lfWeight;
- cf2.bPitchAndFamily = lf.lfPitchAndFamily;
- cf2.yHeight = abs(lf.lfHeight) * 15;
- }
- else {
- LOGFONTW lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
- COLORREF inputcharcolor = m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA];
-
- for (auto &it : Utils::rtf_clrs)
- if (it->clr == inputcharcolor)
- inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1);
-
- cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC;
- cf2.crTextColor = inputcharcolor;
- cf2.bCharSet = lf.lfCharSet;
- wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
- cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0);
- cf2.wWeight = (uint16_t)lf.lfWeight;
- cf2.bPitchAndFamily = lf.lfPitchAndFamily;
- cf2.yHeight = abs(lf.lfHeight) * 15;
- }
- m_message.SendMsg(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
- m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); /* WINE: fix send colour text. */
- m_message.SendMsg(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2); /* WINE: fix send colour text. */
-
- // setup the rich edit control(s)
- // LOG is always set to RTL, because this is needed for proper bidirectional operation later.
- // The real text direction is then enforced by the streaming code which adds appropiate paragraph
- // and textflow formatting commands to the
- PARAFORMAT2 pf2;
- memset(&pf2, 0, sizeof(PARAFORMAT2));
- pf2.cbSize = sizeof(pf2);
- pf2.wEffects = PFE_RTLPARA;
- pf2.dwMask = PFM_RTLPARA;
- if (FindRTLLocale())
- m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
- if (!(m_dwFlags & MWF_LOG_RTL)) {
- pf2.wEffects = 0;
- m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
- }
- m_message.SendMsg(EM_SETLANGOPTIONS, 0, (LPARAM)m_message.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
-
- if (m_dwFlags & MWF_LOG_RTL)
- SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
- else
- SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
-
- if (szStreamOut != nullptr) {
- SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
- m_message.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut);
- mir_free(szStreamOut);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// set the states of defined database action buttons(only if button is a toggle)
-
-void CMsgDialog::DM_SetDBButtonStates()
-{
- ButtonItem *buttonItem = m_pContainer->m_buttonItems;
- MCONTACT hFinalContact = 0;
- HWND hwndContainer = m_pContainer->m_hwnd;
-
- while (buttonItem) {
- HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId);
-
- if (buttonItem->pfnCallback)
- buttonItem->pfnCallback(buttonItem, m_hwnd, this, hWnd);
-
- if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
- buttonItem = buttonItem->nextItem;
- continue;
- }
-
- BOOL result = FALSE;
- char *szModule = buttonItem->szModule;
- char *szSetting = buttonItem->szSetting;
- if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
- if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
- szModule = Proto_GetBaseAccountName(m_hContact);
- hFinalContact = m_hContact;
- }
- else hFinalContact = 0;
-
- switch (buttonItem->type) {
- case DBVT_BYTE:
- result = (db_get_b(hFinalContact, szModule, szSetting, 0) == buttonItem->bValuePush[0]);
- break;
- case DBVT_WORD:
- result = (db_get_w(hFinalContact, szModule, szSetting, 0) == *((uint16_t *)&buttonItem->bValuePush));
- break;
- case DBVT_DWORD:
- result = (db_get_dw(hFinalContact, szModule, szSetting, 0) == *((uint32_t *)&buttonItem->bValuePush));
- break;
- case DBVT_ASCIIZ:
- ptrA szValue(db_get_sa(hFinalContact, szModule, szSetting));
- if (szValue)
- result = !mir_strcmp((char*)buttonItem->bValuePush, szValue);
- break;
- }
- SendMessage(hWnd, BM_SETCHECK, result, 0);
- buttonItem = buttonItem->nextItem;
- }
-}
-
-void CMsgDialog::DM_ScrollToBottom(WPARAM wParam, LPARAM lParam)
-{
- if (m_bScrollingDisabled)
- return;
-
- if (IsIconic(m_pContainer->m_hwnd))
- m_bDeferredScroll = true;
-
- if (m_iLogMode == WANT_BUILTIN_LOG)
- ((CLogWindow *)m_pLog)->ScrollToBottom(wParam != 0, lParam != 0);
- else
- m_pLog->ScrollToBottom();
-}
-
-void CMsgDialog::DM_RecalcPictureSize()
-{
- HBITMAP hbm = ((m_pPanel.isActive()) && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
- if (hbm) {
- BITMAP bminfo;
- GetObject(hbm, sizeof(bminfo), &bminfo);
- CalcDynamicAvatarSize(&bminfo);
- Resize();
- }
- else m_pic.cy = m_pic.cx = 60;
-}
-
-void CMsgDialog::DM_UpdateLastMessage() const
-{
- if (m_pContainer->m_hwndStatus == nullptr || m_pContainer->m_hwndActive != m_hwnd)
- return;
-
- wchar_t szBuf[100];
- if (m_bShowTyping) {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
- mir_snwprintf(szBuf, TranslateT("%s is typing a message..."), m_cache->getNick());
- }
- else if (m_bStatusSet) {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
- return;
- }
- else {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
-
- if (m_pContainer->cfg.flags.m_bUinStatusBar)
- mir_snwprintf(szBuf, L"UID: %s", m_cache->getUIN());
- else if (m_lastMessage) {
- wchar_t date[64], time[64];
- TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"d", date, _countof(date), 0);
- if (m_pContainer->cfg.flags.m_bUinStatusBar && mir_wstrlen(date) > 6)
- date[mir_wstrlen(date) - 5] = 0;
- TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"t", time, _countof(time), 0);
- mir_snwprintf(szBuf, TranslateT("Last received: %s at %s"), date, time);
- }
- else szBuf[0] = 0;
- }
-
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szBuf);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// create embedded contact list control
-
-HWND CMsgDialog::DM_CreateClist()
-{
- if (!SendLater::Avail) {
- CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
- m_sendMode &= ~SMODE_MULTIPLE;
- return nullptr;
- }
-
- HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248, 184, 0, 30, 30, m_hwnd, (HMENU)IDC_CLIST, g_plugin.getInst(), nullptr);
- SendMessage(hwndClist, WM_TIMER, 14, 0);
- HANDLE hItem = (HANDLE)SendMessage(hwndClist, CLM_FINDCONTACT, m_hContact, 0);
-
- SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT);
- SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL));
-
- if (!g_plugin.bAllowOfflineMultisend)
- SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE);
-
- if (hItem)
- SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM)hItem, 1);
-
- SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, Clist::HideEmptyGroups, 0);
- SendMessage(hwndClist, CLM_SETUSEGROUPS, Clist::UseGroups, 0);
- SendMessage(hwndClist, CLM_FIRST + 106, 0, 1);
- SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0);
- if (hwndClist)
- RedrawWindow(hwndClist, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
- return hwndClist;
-}
-
-LRESULT CMsgDialog::DM_MouseWheelHandler(WPARAM wParam, LPARAM lParam)
-{
- POINT pt;
- GetCursorPos(&pt);
-
- RECT rc;
- GetWindowRect(m_message.GetHwnd(), &rc);
- if (PtInRect(&rc, pt))
- return 1;
-
- if (isChat()) { // scroll nick list by just hovering it
- RECT rcNicklist;
- GetWindowRect(m_nickList.GetHwnd(), &rcNicklist);
- if (PtInRect(&rcNicklist, pt)) {
- m_nickList.SendMsg(WM_MOUSEWHEEL, wParam, lParam);
- return 0;
- }
- }
-
- GetWindowRect(m_pLog->GetHwnd(), &rc);
- if (PtInRect(&rc, pt)) {
- short wDirection = (short)HIWORD(wParam);
-
- if (LOWORD(wParam) & MK_SHIFT || M.GetByte("fastscroll", 0)) {
- if (wDirection < 0)
- SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
- else if (wDirection > 0)
- SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
- }
- else SendMessage(m_pLog->GetHwnd(), WM_MOUSEWHEEL, wParam, lParam);
- return 0;
- }
-
- if (GetTabItemFromMouse(m_pContainer->m_hwndTabs, &pt) != -1) {
- SendMessage(m_pContainer->m_hwndTabs, WM_MOUSEWHEEL, wParam, -1);
- return 0;
- }
- return 1;
-}
-
-void CMsgDialog::DM_FreeTheme()
-{
- if (m_hTheme) {
- CloseThemeData(m_hTheme);
- m_hTheme = nullptr;
- }
- if (m_hThemeIP) {
- CloseThemeData(m_hThemeIP);
- m_hThemeIP = nullptr;
- }
- if (m_hThemeToolbar) {
- CloseThemeData(m_hThemeToolbar);
- m_hThemeToolbar = nullptr;
- }
-}
-
-void CMsgDialog::DM_ThemeChanged()
-{
- CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY];
- CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA];
-
- m_hTheme = OpenThemeData(m_hwnd, L"EDIT");
-
- if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_log->IGNORED)) {
- if (m_iLogMode == WANT_BUILTIN_LOG)
- LOG()->DisableStaticEdge();
-
- if (isChat())
- SetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
- }
-
- if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_msg->IGNORED))
- SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
-
- m_hThemeIP = M.isAero() ? OpenThemeData(m_hwnd, L"ButtonStyle") : nullptr;
- m_hThemeToolbar = (M.isAero() || (!CSkin::m_skinEnabled && M.isVSThemed())) ? OpenThemeData(m_hwnd, L"REBAR") : nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// send out message typing notifications (MTN) when the
-// user is typing/editing text in the message input area.
-
-void CMsgDialog::DM_NotifyTyping(int mode)
-{
- const char *szProto = m_cache->getActiveProto();
- MCONTACT hContact = m_cache->getActiveContact();
-
- // editing user notes or preparing a message for queued delivery -> don't send MTN
- if (m_bEditNotesActive || (m_sendMode & SMODE_SENDLATER))
- return;
-
- // allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle)
- if (!g_plugin.getByte(hContact, SRMSGSET_TYPING, g_plugin.bTypingNew))
- return;
-
- if (szProto == nullptr) // should not, but who knows...
- return;
-
- // check status and capabilities of the protocol
- uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
- if (!(typeCaps & PF4_SUPPORTTYPING))
- return;
-
- if (isChat()) {
- m_nTypeMode = mode;
- Chat_DoEventHook(m_si, GC_USER_TYPNOTIFY, 0, 0, m_nTypeMode);
- }
- else {
- uint32_t protoStatus = Proto_GetStatus(szProto);
- if (protoStatus < ID_STATUS_ONLINE)
- return;
-
- // check visibility/invisibility lists to not "accidentially" send MTN to contacts who
- // should not see them (privacy issue)
- uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
- if (protoCaps & PF1_VISLIST && db_get_w(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
- return;
-
- if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && db_get_w(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
- return;
-
- // don't send to contacts which are not permanently added to the contact list,
- // unless the option to ignore added status is set.
- if (!Contact::OnList(m_hContact) && !g_plugin.bTypingUnknown)
- return;
-
- // End user check
- m_nTypeMode = mode;
- CallService(MS_PROTO_SELFISTYPING, hContact, m_nTypeMode);
- }
-}
-
-void CMsgDialog::DM_OptionsApplied(bool bRemakeLog)
-{
- m_szMicroLf[0] = 0;
- if (!m_pContainer->m_theme.isPrivate) {
- m_pContainer->LoadThemeDefaults();
- m_dwFlags = m_pContainer->m_theme.dwFlags;
- }
-
- LoadLocalFlags();
- m_hTimeZone = TimeZone_CreateByContact(m_hContact, nullptr, TZF_KNOWNONLY);
-
- m_bShowUIElements = (m_pContainer->cfg.flags.m_bHideToolbar) == 0;
- m_bSplitterOverride = M.GetByte(m_hContact, "splitoverride", 0) != 0;
- m_pPanel.getVisibility();
-
- // small inner margins (padding) for the text areas
- m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
-
- GetSendFormat();
- SetDialogToType();
- SendMessage(m_hwnd, DM_CONFIGURETOOLBAR, 0, 0);
-
- DM_InitRichEdit();
- if (m_hwnd == m_pContainer->m_hwndActive)
- SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
- InvalidateRect(m_message.GetHwnd(), nullptr, FALSE);
- if (bRemakeLog) {
- if (IsIconic(m_pContainer->m_hwnd))
- m_bDeferredRemakeLog = true;
- else if (isChat())
- RedrawLog();
- else
- RemakeLog();
- }
-
- ShowWindow(m_hwndPanelPicParent, SW_SHOW);
- EnableWindow(m_hwndPanelPicParent, TRUE);
-
- UpdateWindowIcon();
-}
-
-void CMsgDialog::DM_Typing(bool fForceOff)
-{
- HWND hwndContainer = m_pContainer->m_hwnd;
- HWND hwndStatus = m_pContainer->m_hwndStatus;
-
- if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - m_nLastTyping > TIMEOUT_TYPEOFF)
- DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
-
- if (m_bShowTyping == 1) {
- if (m_nTypeSecs > 0) {
- m_nTypeSecs--;
- if (GetForegroundWindow() == hwndContainer)
- UpdateWindowIcon();
- }
- else {
- if (!fForceOff) {
- m_bShowTyping = 2;
- m_nTypeSecs = 86400;
-
- if (!isChat())
- mir_snwprintf(m_wszStatusBar, TranslateT("%s has entered text."), m_cache->getNick());
-
- if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd)
- SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
- }
- UpdateWindowIcon();
- HandleIconFeedback(this, (HICON)-1);
- CMsgDialog *dat_active = (CMsgDialog*)GetWindowLongPtr(m_pContainer->m_hwndActive, GWLP_USERDATA);
- if (dat_active && !dat_active->isChat())
- m_pContainer->UpdateTitle(0);
- else
- m_pContainer->UpdateTitle(0, dat_active);
- if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
- m_pContainer->ReflashContainer();
- }
- }
- else if (m_bShowTyping == 2) {
- if (m_nTypeSecs > 0)
- m_nTypeSecs--;
- else {
- m_wszStatusBar[0] = 0;
- m_bShowTyping = 0;
- }
- tabUpdateStatusBar();
- }
- else if (m_nTypeSecs > 0) {
- mir_snwprintf(m_wszStatusBar, TranslateT("%s is typing a message"),
- (m_pUserTyping) ? m_pUserTyping->pszNick : m_cache->getNick());
-
- m_nTypeSecs--;
- if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
- SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
- SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
- }
- if (IsIconic(hwndContainer) || !IsActive()) {
- SetWindowText(hwndContainer, m_wszStatusBar);
- m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
- if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
- m_pContainer->ReflashContainer();
- }
-
- if (m_pContainer->m_hwndActive != m_hwnd) {
- if (m_bCanFlashTab)
- m_iFlashIcon = PluginConfig.g_IconTypingEvent;
- HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
- }
- else { // active tab may show icon if status bar is disabled
- if (!hwndStatus) {
- if (TabCtrl_GetItemCount(m_hwndParent) > 1 || !m_pContainer->cfg.flags.m_bHideTabs)
- HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
- }
- }
- if ((GetForegroundWindow() != hwndContainer) || (m_pContainer->m_hwndStatus == nullptr) || (m_pContainer->m_hwndActive != m_hwnd))
- m_pContainer->SetIcon(this, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
-
- m_bShowTyping = 1;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// sync splitter position for all open sessions.
-// This cares about private / per container / MUC <> IM splitter syncing and everything.
-// called from IM and MUC windows via DM_SPLITTERGLOBALEVENT
-
-int CMsgDialog::DM_SplitterGlobalEvent(WPARAM wParam, LPARAM lParam)
-{
- CMsgDialog *srcDat = PluginConfig.lastSPlitterPos.pSrcDat;
- TContainerData *srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer;
- bool fCntGlobal = (!m_pContainer->cfg.fPrivate ? true : false);
-
- if (m_bIsAutosizingInput)
- return 0;
-
- RECT rcWin;
- GetWindowRect(m_hwnd, &rcWin);
-
- LONG newPos;
- if (wParam == 0 && lParam == 0) {
- if (m_bSplitterOverride && this != srcDat)
- return 0;
-
- if (srcDat->isChat() == isChat())
- newPos = PluginConfig.lastSPlitterPos.pos;
- else if (!srcDat->isChat() && isChat())
- newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
- else if (srcDat->isChat() && !isChat())
- newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
- else
- newPos = 0;
-
- if (this == srcDat) {
- m_pContainer->cfg.iSplitterY = m_iSplitterY;
- if (fCntGlobal)
- SaveSplitter();
- return 0;
- }
-
- if (!fCntGlobal && m_pContainer != srcCnt)
- return 0;
- if (srcCnt->cfg.fPrivate && m_pContainer != srcCnt)
- return 0;
-
- // for inactive sessions, delay the splitter repositioning until they become
- // active (faster, avoid redraw/resize problems for minimized windows)
- if (IsIconic(m_pContainer->m_hwnd) || m_pContainer->m_hwndActive != m_hwnd) {
- m_bDelayedSplitter = true;
- m_wParam = newPos;
- m_lParam = PluginConfig.lastSPlitterPos.lParam;
- return 0;
- }
- }
- else newPos = wParam;
-
- LoadSplitter();
- AdjustBottomAvatarDisplay();
- DM_RecalcPictureSize();
- Resize();
- DM_ScrollToBottom(1, 1);
- if (this != srcDat)
- UpdateToolbarBG();
- return 0;
-}
-
-void CMsgDialog::DM_AddDivider()
-{
- if (!m_bDividerSet && g_plugin.bUseDividers)
- if (GetWindowTextLength(m_pLog->GetHwnd()) > 0)
- m_bDividerSet = m_bDividerWanted = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// incoming event handler
-
-void CMsgDialog::DM_EventAdded(WPARAM, LPARAM lParam)
-{
- MEVENT hDbEvent = (MEVENT)lParam;
-
- DBEVENTINFO dbei = {};
- db_event_get(hDbEvent, &dbei);
- if (m_hDbEventFirst == 0)
- m_hDbEventFirst = hDbEvent;
-
- bool bIsStatusChangeEvent = IsStatusEvent(dbei.eventType);
- bool bDisableNotify = (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ));
-
- if (!DbEventIsShown(&dbei))
- return;
-
- if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
- m_lastMessage = dbei.timestamp;
- m_wszStatusBar[0] = 0;
- if (m_bShowTyping) {
- m_nTypeSecs = 0;
- DM_Typing(true);
- m_bShowTyping = 0;
- }
- HandleIconFeedback(this, (HICON)-1);
- if (m_pContainer->m_hwndStatus)
- PostMessage(m_hwnd, DM_UPDATELASTMESSAGE, 0, 0);
- }
-
- // set the message log divider to mark new (maybe unseen) messages, if the container has
- // been minimized or in the background.
- if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
- if (g_plugin.bDividersUsePopupConfig && g_plugin.bUseDividers) {
- if (!MessageWindowOpened(m_hContact, nullptr))
- DM_AddDivider();
- }
- else if (g_plugin.bUseDividers) {
- if (!m_pContainer->IsActive())
- DM_AddDivider();
- else if (m_pContainer->m_hwndActive != m_hwnd)
- DM_AddDivider();
- }
-
- if (IsWindowVisible(m_pContainer->m_hwnd))
- m_pContainer->m_bHidden = false;
- }
- m_cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0);
-
- if (hDbEvent != m_hDbEventFirst || isChat())
- StreamEvents(hDbEvent, 1, 1);
- else
- RemakeLog();
-
- // handle tab flashing
- if (!bDisableNotify && !bIsStatusChangeEvent)
- if ((TabCtrl_GetCurSel(m_hwndParent) != m_iTabID) && !(dbei.flags & DBEF_SENT)) {
- switch (dbei.eventType) {
- case EVENTTYPE_MESSAGE:
- m_iFlashIcon = PluginConfig.g_IconMsgEvent;
- break;
- case EVENTTYPE_FILE:
- m_iFlashIcon = PluginConfig.g_IconFileEvent;
- break;
- default:
- m_iFlashIcon = PluginConfig.g_IconMsgEvent;
- break;
- }
- timerFlash.Start(TIMEOUT_FLASHWND);
- m_bCanFlashTab = true;
- }
-
- // try to flash the contact list...
- if (!bDisableNotify)
- FlashOnClist(hDbEvent, &dbei);
-
- // autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch)
- // never switch for status changes...
- if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
- if (g_plugin.bAutoSwitchTabs && m_pContainer->m_hwndActive != m_hwnd) {
- if ((IsIconic(m_pContainer->m_hwnd) && !IsZoomed(m_pContainer->m_hwnd)) || (g_plugin.bHideOnClose && !IsWindowVisible(m_pContainer->m_hwnd))) {
- int iItem = GetTabIndexFromHWND(GetParent(m_hwnd), m_hwnd);
- if (iItem >= 0) {
- TabCtrl_SetCurSel(m_hwndParent, iItem);
- ShowWindow(m_pContainer->m_hwndActive, SW_HIDE);
- m_pContainer->m_hwndActive = m_hwnd;
- m_pContainer->UpdateTitle(m_hContact);
- m_pContainer->cfg.flags.m_bDeferredTabSelect = true;
- }
- }
- }
- }
-
- // flash window if it is not focused
- if (!bDisableNotify && !bIsStatusChangeEvent)
- if (!IsActive() && !(dbei.flags & DBEF_SENT)) {
- if (!m_pContainer->cfg.flags.m_bNoFlash && !m_pContainer->IsActive())
- m_pContainer->FlashContainer(1, 0);
- m_pContainer->SetIcon(this, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
- m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
- }
-
- // play a sound
- if (!bDisableNotify && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT)))
- PlayIncomingSound();
-
- if (m_pWnd)
- m_pWnd->Invalidate();
-}
-
-void CMsgDialog::DM_HandleAutoSizeRequest(REQRESIZE* rr)
-{
- if (rr == nullptr || GetForegroundWindow() != m_pContainer->m_hwnd)
- return;
-
- if (!m_bIsAutosizingInput || m_iInputAreaHeight == -1)
- return;
-
- LONG heightLimit = M.GetDword("autoSplitMinLimit", 0);
- LONG iNewHeight = rr->rc.bottom - rr->rc.top;
-
- if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
- iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2);
-
- if (heightLimit && iNewHeight < heightLimit)
- iNewHeight = heightLimit;
-
- if (iNewHeight == m_iInputAreaHeight)
- return;
-
- RECT rc;
- GetClientRect(m_hwnd, &rc);
- LONG cy = rc.bottom - rc.top;
- LONG panelHeight = (m_pPanel.isActive() ? m_pPanel.getHeight() : 0);
-
- if (iNewHeight > (cy - panelHeight) / 2)
- iNewHeight = (cy - panelHeight) / 2;
-
- m_dynaSplitter = iNewHeight - DPISCALEY_S(2);
- if (m_pContainer->cfg.flags.m_bBottomToolbar)
- m_dynaSplitter += DPISCALEY_S(22);
- m_iSplitterY = m_dynaSplitter + DPISCALEY_S(34);
- DM_RecalcPictureSize();
-
- m_iInputAreaHeight = iNewHeight;
- UpdateToolbarBG();
- DM_ScrollToBottom(1, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// status icon stuff (by sje, used for indicating encryption status in the status bar
-// this is now part of the message window api
-
-
-static int OnSrmmIconChanged(WPARAM hContact, LPARAM)
-{
- if (hContact == 0)
- Srmm_Broadcast(DM_STATUSICONCHANGE, 0, 0);
- else {
- HWND hwnd = Srmm_FindWindow(hContact);
- if (hwnd)
- PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
- }
- return 0;
-}
-
-void CMsgDialog::DrawStatusIcons(HDC hDC, const RECT &rc, int gap)
-{
- int x = rc.left;
- int y = (rc.top + rc.bottom - PluginConfig.m_smcxicon) >> 1;
-
- SetBkMode(hDC, TRANSPARENT);
-
- int nIcon = 0;
- while (StatusIconData *sid = Srmm_GetNthIcon(m_hContact, nIcon++)) {
- if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
- if (sid->dwId == MSG_ICON_SOUND) {
- DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS],
- PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
-
- DrawIconEx(hDC, x, y, m_pContainer->cfg.flags.m_bNoSound ?
- PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled,
- PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
- }
- else if (sid->dwId == MSG_ICON_UTN) {
- if (AllowTyping()) {
- DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
-
- DrawIconEx(hDC, x, y, g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew) ?
- PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
- }
- else CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50);
- }
- }
- else {
- HICON hIcon;
- if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled)
- hIcon = sid->hIconDisabled;
- else
- hIcon = sid->hIcon;
-
- if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled == nullptr)
- CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIcon, 50);
- else
- DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
- }
-
- x += PluginConfig.m_smcxicon + gap;
- }
-}
-
-void CMsgDialog::CheckStatusIconClick(POINT pt, const RECT &rc, int gap, int code)
-{
- if (code == NM_CLICK || code == NM_RCLICK) {
- POINT ptScreen;
- GetCursorPos(&ptScreen);
- if (!PtInRect(&rcLastStatusBarClick, ptScreen))
- return;
- }
-
- UINT iconNum = (pt.x - (rc.left + 0)) / (PluginConfig.m_smcxicon + gap);
-
- StatusIconData *sid = Srmm_GetNthIcon(m_hContact, iconNum);
- if (sid == nullptr)
- return;
-
- if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
- if (sid->dwId == MSG_ICON_SOUND && code != NM_RCLICK) {
- if (GetKeyState(VK_SHIFT) & 0x8000) {
- for (TContainerData *p = pFirstContainer; p; p = p->pNext) {
- p->cfg.flags.m_bNoSound = m_pContainer->cfg.flags.m_bNoSound;
- InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
- }
- }
- else {
- m_pContainer->cfg.flags.m_bNoSound = !m_pContainer->cfg.flags.m_bNoSound;
- InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
- }
- }
- else if (sid->dwId == MSG_ICON_UTN && code != NM_RCLICK && AllowTyping()) {
- SendMessage(m_pContainer->m_hwndActive, WM_COMMAND, IDC_SELFTYPING, 0);
- InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
- }
- }
- else {
- StatusIconClickData sicd = { sizeof(sicd) };
- GetCursorPos(&sicd.clickLocation);
- sicd.dwId = sid->dwId;
- sicd.szModule = sid->szModule;
- sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
- Srmm_ClickStatusIcon(m_hContact, &sicd);
- InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
- }
-}
-
-void CMsgDialog::DM_ErrorDetected(int type, int flag)
-{
- switch (type) {
- case MSGERROR_CANCEL:
- case MSGERROR_SENDLATER:
- if (m_bErrorState) {
- m_cache->saveHistory();
- if (type == MSGERROR_SENDLATER)
- sendQueue->doSendLater(m_iCurrentQueueError, this); // to be implemented at a later time
- m_iOpenJobs--;
- sendQueue->dec();
- if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS)
- sendQueue->clearJob(m_iCurrentQueueError);
- m_iCurrentQueueError = -1;
- sendQueue->showErrorControls(this, FALSE);
- if (type != MSGERROR_CANCEL || flag == 0)
- m_message.SetText(L"");
- sendQueue->checkQueue(this);
- int iNextFailed = sendQueue->findNextFailed(this);
- if (iNextFailed >= 0)
- sendQueue->handleError(this, iNextFailed);
- }
- break;
-
- case MSGERROR_RETRY:
- if (m_bErrorState) {
- int resent = 0;
-
- m_cache->saveHistory();
- if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) {
- SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
- if (job->iSendId == 0 && job->hContact == 0)
- break;
-
- job->iSendId = ProtoChainSend(job->hContact, PSS_MESSAGE, job->dwFlags, (LPARAM)job->szSendBuffer);
- resent++;
- }
-
- if (resent) {
- SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
-
- SetTimer(m_hwnd, TIMERID_MSGSEND + m_iCurrentQueueError, PluginConfig.m_MsgTimeout, nullptr);
- job->iStatus = SendQueue::SQ_INPROGRESS;
- m_iCurrentQueueError = -1;
- sendQueue->showErrorControls(this, FALSE);
- m_message.SetText(L"");
- sendQueue->checkQueue(this);
-
- int iNextFailed = sendQueue->findNextFailed(this);
- if (iNextFailed >= 0)
- sendQueue->handleError(this, iNextFailed);
- }
- }
- }
-}
-
-int SI_InitStatusIcons()
-{
- StatusIconData sid = {};
- sid.szModule = MSG_ICON_MODULE;
- sid.dwId = MSG_ICON_SOUND; // Sounds
- Srmm_AddIcon(&sid, &g_plugin);
-
- sid.dwId = MSG_ICON_UTN;
- Srmm_AddIcon(&sid, &g_plugin);
-
- HookEvent(ME_MSG_ICONSCHANGED, OnSrmmIconChanged);
- return 0;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/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.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// these are generic message handlers which are used by the message dialog window procedure.
+// calling them directly instead of using SendMessage() is faster.
+// also contains various callback functions for custom buttons
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Save message log for given session as RTF document
+
+/*
+void CMsgDialog::DM_SaveLogAsRTF() const
+{
+ if (m_hwndIEView != nullptr) {
+ IEVIEWEVENT event = { sizeof(event) };
+ event.hwnd = m_hwndIEView;
+ event.hContact = m_hContact;
+ event.iType = IEE_SAVE_DOCUMENT;
+ CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+ }
+ else {
+ wchar_t szFilter[MAX_PATH], szFilename[MAX_PATH];
+ mir_snwprintf(szFilter, L"%s%c*.rtf%c%c", TranslateT("Rich Edit file"), 0, 0, 0);
+ mir_snwprintf(szFilename, L"%s.rtf", m_cache->getNick());
+
+ Utils::sanitizeFilename(szFilename);
+
+ wchar_t szInitialDir[MAX_PATH + 2];
+ mir_snwprintf(szInitialDir, L"%s%s\\", M.getDataPath(), L"\\Saved message logs");
+ CreateDirectoryTreeW(szInitialDir);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = m_hwnd;
+ ofn.lpstrFile = szFilename;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrInitialDir = szInitialDir;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_HIDEREADONLY;
+ ofn.lpstrDefExt = L"rtf";
+ if (GetSaveFileName(&ofn)) {
+ EDITSTREAM stream = { 0 };
+ stream.dwCookie = (DWORD_PTR)szFilename;
+ stream.dwError = 0;
+ stream.pfnCallback = Utils::StreamOut;
+ m_rtf.SendMsg(EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM)&stream);
+ }
+ }
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// checks if the balloon tooltip can be dismissed (usually called by WM_MOUSEMOVE events)
+
+void CMsgDialog::DM_DismissTip(const POINT& pt)
+{
+ if (!IsWindowVisible(m_hwndTip))
+ return;
+
+ RECT rc;
+ GetWindowRect(m_hwndTip, &rc);
+ if (PtInRect(&rc, pt))
+ return;
+
+ if (abs(pt.x - m_ptTipActivation.x) > 5 || abs(pt.y - m_ptTipActivation.y) > 5) {
+ SendMessage(m_hwndTip, TTM_TRACKACTIVATE, FALSE, 0);
+ m_ptTipActivation.x = m_ptTipActivation.y = 0;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// initialize the balloon tooltip for message window notifications
+
+void CMsgDialog::DM_InitTip()
+{
+ m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, nullptr, g_plugin.getInst(), (LPVOID)nullptr);
+
+ memset(&ti, 0, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.lpszText = TranslateT("No status message");
+ ti.hinst = g_plugin.getInst();
+ ti.hwnd = m_hwnd;
+ ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT;
+ ti.uId = (UINT_PTR)m_hwnd;
+ SendMessage(m_hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ SetWindowPos(m_hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// checks generic hotkeys valid for both IM and MUC sessions
+//
+// returns 1 for handled hotkeys, 0 otherwise.
+
+bool CMsgDialog::DM_GenericHotkeysCheck(MSG *message)
+{
+ LRESULT mim_hotkey_check = Hotkey_Check(message, TABSRMM_HK_SECTION_GENERIC);
+
+ switch (mim_hotkey_check) {
+ case TABSRMM_HK_PASTEANDSEND:
+ HandlePasteAndSend();
+ return true;
+
+ case TABSRMM_HK_HISTORY:
+ m_btnHistory.Click();
+ return true;
+
+ case TABSRMM_HK_CONTAINEROPTIONS:
+ m_pContainer->OptionsDialog();
+ return true;
+
+ case TABSRMM_HK_TOGGLEINFOPANEL:
+ m_pPanel.setActive(!m_pPanel.isActive());
+ m_pPanel.showHide();
+ return true;
+
+ case TABSRMM_HK_TOGGLETOOLBAR:
+ SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLETOOLBAR, 0);
+ return true;
+
+ case TABSRMM_HK_CLEARLOG:
+ tabClearLog();
+ return true;
+
+ case TABSRMM_HK_TOGGLESIDEBAR:
+ if (m_pContainer->m_pSideBar->isActive())
+ SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ return true;
+
+ case TABSRMM_HK_CLOSE_OTHER:
+ CloseOtherTabs(m_pContainer->m_hwndTabs, *this);
+ return true;
+ }
+ return false;
+}
+
+LRESULT CMsgDialog::DM_MsgWindowCmdHandler(UINT cmd, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+ int iSelection;
+ HMENU submenu;
+
+ switch (cmd) {
+ case IDC_SRMM_BOLD:
+ case IDC_SRMM_ITALICS:
+ case IDC_SRMM_UNDERLINE:
+ case IDC_FONTSTRIKEOUT:
+ if (m_SendFormat != 0) { // dont use formatting if disabled
+ CHARFORMAT2 cf, cfOld;
+ memset(&cf, 0, sizeof(CHARFORMAT2));
+ memset(&cfOld, 0, sizeof(CHARFORMAT2));
+ cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2);
+ cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
+ m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld);
+ BOOL isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD);
+ BOOL isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC);
+ BOOL isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE);
+ BOOL isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT);
+
+ int ctrlId = LOWORD(wParam);
+ if (ctrlId == IDC_SRMM_BOLD && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_BOLD)))
+ break;
+ if (ctrlId == IDC_SRMM_ITALICS && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_ITALICS)))
+ break;
+ if (ctrlId == IDC_SRMM_UNDERLINE && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_UNDERLINE)))
+ break;
+ if (ctrlId == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_FONTSTRIKEOUT)))
+ break;
+ if (ctrlId == IDC_SRMM_BOLD) {
+ cf.dwEffects = isBold ? 0 : CFE_BOLD;
+ cf.dwMask = CFM_BOLD;
+ CheckDlgButton(m_hwnd, IDC_SRMM_BOLD, !isBold ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else if (ctrlId == IDC_SRMM_ITALICS) {
+ cf.dwEffects = isItalic ? 0 : CFE_ITALIC;
+ cf.dwMask = CFM_ITALIC;
+ CheckDlgButton(m_hwnd, IDC_SRMM_ITALICS, !isItalic ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else if (ctrlId == IDC_SRMM_UNDERLINE) {
+ cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE;
+ cf.dwMask = CFM_UNDERLINE;
+ CheckDlgButton(m_hwnd, IDC_SRMM_UNDERLINE, !isUnderline ? BST_CHECKED : BST_UNCHECKED);
+ }
+ else if (ctrlId == IDC_FONTSTRIKEOUT) {
+ cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT;
+ cf.dwMask = CFM_STRIKEOUT;
+ CheckDlgButton(m_hwnd, IDC_FONTSTRIKEOUT, !isStrikeout ? BST_CHECKED : BST_UNCHECKED);
+ }
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+ }
+ break;
+
+ case IDCANCEL:
+ ShowWindow(m_pContainer->m_hwnd, SW_MINIMIZE);
+ return FALSE;
+
+ case IDC_CLOSE:
+ PostMessage(m_hwnd, WM_CLOSE, 1, 0);
+ break;
+
+ case IDC_NAME:
+ if (GetKeyState(VK_SHIFT) & 0x8000) // copy UIN
+ Utils_ClipboardCopy(m_cache->getUIN());
+ else
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(m_cache->getActiveContact()), 0);
+ break;
+
+ case IDC_TIME:
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 2);
+ MsgWindowUpdateMenu(submenu, MENU_LOGMENU);
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_TIME), &rc);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ return MsgWindowMenuHandler(iSelection, MENU_LOGMENU);
+
+ case IDC_PROTOMENU:
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 4);
+ {
+ bool iOldGlobalSendFormat = g_plugin.bSendFormat;
+ int iLocalFormat = M.GetDword(m_hContact, "sendformat", 0);
+ int iNewLocalFormat = iLocalFormat;
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
+
+ CheckMenuItem(submenu, ID_MODE_GLOBAL, !m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_MODE_PRIVATE, m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
+
+ // formatting menu..
+ CheckMenuItem(submenu, ID_GLOBAL_BBCODE, (g_plugin.bSendFormat) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_GLOBAL_OFF, (g_plugin.bSendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
+
+ CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, (iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, (iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_THISCONTACT_OFF, (iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ switch (iSelection) {
+ case ID_MODE_GLOBAL:
+ m_bSplitterOverride = false;
+ db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 0);
+ LoadSplitter();
+ AdjustBottomAvatarDisplay();
+ DM_RecalcPictureSize();
+ Resize();
+ break;
+
+ case ID_MODE_PRIVATE:
+ m_bSplitterOverride = true;
+ db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 1);
+ LoadSplitter();
+ AdjustBottomAvatarDisplay();
+ DM_RecalcPictureSize();
+ Resize();
+ break;
+
+ case ID_GLOBAL_BBCODE:
+ g_plugin.bSendFormat = SENDFORMAT_BBCODE;
+ break;
+
+ case ID_GLOBAL_OFF:
+ g_plugin.bSendFormat = SENDFORMAT_NONE;
+ break;
+
+ case ID_THISCONTACT_GLOBALSETTING:
+ iNewLocalFormat = 0;
+ break;
+
+ case ID_THISCONTACT_BBCODE:
+ iNewLocalFormat = SENDFORMAT_BBCODE;
+ break;
+
+ case ID_THISCONTACT_OFF:
+ iNewLocalFormat = -1;
+ break;
+ }
+
+ if (iNewLocalFormat == 0)
+ db_unset(m_hContact, SRMSGMOD_T, "sendformat");
+ else if (iNewLocalFormat != iLocalFormat)
+ db_set_dw(m_hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat);
+
+ if (iNewLocalFormat != iLocalFormat || g_plugin.bSendFormat != iOldGlobalSendFormat) {
+ m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
+ if (m_SendFormat == -1) // per contact override to disable it..
+ m_SendFormat = 0;
+ Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
+ }
+ }
+ break;
+
+ case IDC_TOGGLETOOLBAR:
+ if (lParam == 1)
+ m_pContainer->cfg.flags.m_bNoMenuBar = !m_pContainer->cfg.flags.m_bNoMenuBar;
+ else
+ m_pContainer->cfg.flags.m_bHideToolbar = !m_pContainer->cfg.flags.m_bHideToolbar;
+ m_pContainer->ApplySetting(true);
+ break;
+
+ case IDC_SENDMENU:
+ submenu = GetSubMenu(PluginConfig.g_hMenuContext, 3);
+
+ GetWindowRect(GetDlgItem(m_hwnd, IDOK), &rc);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, (m_sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, m_sendMode == 0 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, (m_sendMode & SMODE_CONTAINER) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, (m_sendMode & SMODE_SENDLATER) ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, (m_sendMode & SMODE_NOACK) ? MF_CHECKED : MF_UNCHECKED);
+
+ if (lParam)
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ else
+ iSelection = HIWORD(wParam);
+
+ switch (iSelection) {
+ case ID_SENDMENU_SENDTOMULTIPLEUSERS:
+ m_sendMode ^= SMODE_MULTIPLE;
+ if (m_sendMode & SMODE_MULTIPLE)
+ DM_CreateClist();
+ else if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
+ break;
+ case ID_SENDMENU_SENDDEFAULT:
+ m_sendMode = 0;
+ break;
+ case ID_SENDMENU_SENDTOCONTAINER:
+ m_sendMode ^= SMODE_CONTAINER;
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW);
+ break;
+ case ID_SENDMENU_SENDLATER:
+ if (SendLater::Avail)
+ m_sendMode ^= SMODE_SENDLATER;
+ else
+ CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
+ break;
+ case ID_SENDMENU_SENDWITHOUTTIMEOUTS:
+ m_sendMode ^= SMODE_NOACK;
+ if (m_sendMode & SMODE_NOACK)
+ db_set_b(m_hContact, SRMSGMOD_T, "no_ack", 1);
+ else
+ db_unset(m_hContact, SRMSGMOD_T, "no_ack");
+ break;
+ }
+ db_set_b(m_hContact, SRMSGMOD_T, "no_ack", (uint8_t)(m_sendMode & SMODE_NOACK ? 1 : 0));
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
+ if (m_sendMode & SMODE_MULTIPLE || m_sendMode & SMODE_CONTAINER) {
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ else {
+ if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
+ DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ m_pContainer->QueryClientArea(rc);
+ Resize();
+ DM_ScrollToBottom(1, 1);
+ Utils::showDlgControl(m_hwnd, IDC_MULTISPLITTER, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
+ Utils::showDlgControl(m_hwnd, IDC_CLIST, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
+ break;
+
+ case IDC_TOGGLESIDEBAR:
+ SendMessage(m_pContainer->m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+ break;
+
+ case IDC_PIC:
+ GetClientRect(m_hwnd, &rc);
+
+ m_bEditNotesActive = !m_bEditNotesActive;
+ if (m_bEditNotesActive) {
+ int iLen = GetWindowTextLength(m_message.GetHwnd());
+ if (iLen != 0) {
+ ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("You cannot edit user notes when there are unsent messages"));
+ m_bEditNotesActive = false;
+ break;
+ }
+
+ if (!m_bIsAutosizingInput) {
+ m_iSplitterSaved = m_iSplitterY;
+ m_iSplitterY = rc.bottom / 2;
+ SendMessage(m_hwnd, WM_SIZE, 1, 1);
+ }
+
+ ptrW wszText(db_get_wsa(m_hContact, "UserInfo", "MyNotes"));
+ if (wszText != nullptr)
+ m_message.SetText(wszText);
+ }
+ else {
+ ptrW buf(m_message.GetText());
+ db_set_ws(m_hContact, "UserInfo", "MyNotes", buf);
+ m_message.SetText(L"");
+
+ if (!m_bIsAutosizingInput) {
+ m_iSplitterY = m_iSplitterSaved;
+ Resize();
+ DM_ScrollToBottom(0, 1);
+ }
+ }
+ SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+ if (m_bEditNotesActive)
+ CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK | MB_ICONINFORMATION);
+ break;
+
+ case IDM_CLEAR:
+ tabClearLog();
+ break;
+
+ case IDC_PROTOCOL:
+ submenu = Menu_BuildContactMenu(m_hContact);
+ if (lParam == 0)
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
+ else
+ GetWindowRect((HWND)lParam, &rc);
+
+ iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+ if (iSelection)
+ Clist_MenuProcessCommand(LOWORD(iSelection), MPCF_CONTACTMENU, m_hContact);
+
+ DestroyMenu(submenu);
+ break;
+
+ // error control
+ case IDC_CANCELSEND:
+ DM_ErrorDetected(MSGERROR_CANCEL, 0);
+ break;
+
+ case IDC_RETRY:
+ DM_ErrorDetected(MSGERROR_RETRY, 0);
+ break;
+
+ case IDC_MSGSENDLATER:
+ DM_ErrorDetected(MSGERROR_SENDLATER, 0);
+ break;
+
+ case IDC_SELFTYPING:
+ if (AllowTyping()) {
+ int iCurrentTypingMode = g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew);
+ if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) {
+ DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
+ m_nTypeMode = PROTOTYPE_SELFTYPING_OFF;
+ }
+ g_plugin.setByte(m_hContact, SRMSGSET_TYPING, (uint8_t)!iCurrentTypingMode);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// initialize rich edit control (log and edit control) for both MUC and
+// standard IM session windows.
+
+void CMsgDialog::DM_InitRichEdit()
+{
+ char *szStreamOut = nullptr;
+ if (!isChat() && GetWindowTextLength(m_message.GetHwnd()) > 0)
+ szStreamOut = m_message.GetRichTextRtf();
+ SetWindowText(m_message.GetHwnd(), L"");
+
+ m_pLog->UpdateOptions();
+
+ m_message.SendMsg(EM_SETBKGNDCOLOR, 0, m_pContainer->m_theme.inputbg);
+
+ CHARFORMAT2 cf2 = {};
+ cf2.cbSize = sizeof(cf2);
+
+ if (isChat()) {
+ LOGFONTW lf;
+ COLORREF inputcharcolor;
+ LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor);
+
+ cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR;
+ cf2.crTextColor = inputcharcolor;
+ cf2.bCharSet = lf.lfCharSet;
+ cf2.crBackColor = m_pContainer->m_theme.inputbg;
+ wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
+ cf2.dwEffects = 0;
+ cf2.wWeight = (uint16_t)lf.lfWeight;
+ cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+ cf2.yHeight = abs(lf.lfHeight) * 15;
+ }
+ else {
+ LOGFONTW lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
+ COLORREF inputcharcolor = m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA];
+
+ for (auto &it : Utils::rtf_clrs)
+ if (it->clr == inputcharcolor)
+ inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1);
+
+ cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC;
+ cf2.crTextColor = inputcharcolor;
+ cf2.bCharSet = lf.lfCharSet;
+ wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
+ cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0);
+ cf2.wWeight = (uint16_t)lf.lfWeight;
+ cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+ cf2.yHeight = abs(lf.lfHeight) * 15;
+ }
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); /* WINE: fix send colour text. */
+ m_message.SendMsg(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2); /* WINE: fix send colour text. */
+
+ // setup the rich edit control(s)
+ // LOG is always set to RTL, because this is needed for proper bidirectional operation later.
+ // The real text direction is then enforced by the streaming code which adds appropiate paragraph
+ // and textflow formatting commands to the
+ PARAFORMAT2 pf2;
+ memset(&pf2, 0, sizeof(PARAFORMAT2));
+ pf2.cbSize = sizeof(pf2);
+ pf2.wEffects = PFE_RTLPARA;
+ pf2.dwMask = PFM_RTLPARA;
+ if (FindRTLLocale())
+ m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ if (!(m_dwFlags & MWF_LOG_RTL)) {
+ pf2.wEffects = 0;
+ m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+ }
+ m_message.SendMsg(EM_SETLANGOPTIONS, 0, (LPARAM)m_message.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
+
+ if (m_dwFlags & MWF_LOG_RTL)
+ SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
+ else
+ SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
+
+ if (szStreamOut != nullptr) {
+ SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
+ m_message.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut);
+ mir_free(szStreamOut);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// set the states of defined database action buttons(only if button is a toggle)
+
+void CMsgDialog::DM_SetDBButtonStates()
+{
+ ButtonItem *buttonItem = m_pContainer->m_buttonItems;
+ MCONTACT hFinalContact = 0;
+ HWND hwndContainer = m_pContainer->m_hwnd;
+
+ while (buttonItem) {
+ HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId);
+
+ if (buttonItem->pfnCallback)
+ buttonItem->pfnCallback(buttonItem, m_hwnd, this, hWnd);
+
+ if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
+ buttonItem = buttonItem->nextItem;
+ continue;
+ }
+
+ BOOL result = FALSE;
+ char *szModule = buttonItem->szModule;
+ char *szSetting = buttonItem->szSetting;
+ if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
+ if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
+ szModule = Proto_GetBaseAccountName(m_hContact);
+ hFinalContact = m_hContact;
+ }
+ else hFinalContact = 0;
+
+ switch (buttonItem->type) {
+ case DBVT_BYTE:
+ result = (db_get_b(hFinalContact, szModule, szSetting, 0) == buttonItem->bValuePush[0]);
+ break;
+ case DBVT_WORD:
+ result = (db_get_w(hFinalContact, szModule, szSetting, 0) == *((uint16_t *)&buttonItem->bValuePush));
+ break;
+ case DBVT_DWORD:
+ result = (db_get_dw(hFinalContact, szModule, szSetting, 0) == *((uint32_t *)&buttonItem->bValuePush));
+ break;
+ case DBVT_ASCIIZ:
+ ptrA szValue(db_get_sa(hFinalContact, szModule, szSetting));
+ if (szValue)
+ result = !mir_strcmp((char*)buttonItem->bValuePush, szValue);
+ break;
+ }
+ SendMessage(hWnd, BM_SETCHECK, result, 0);
+ buttonItem = buttonItem->nextItem;
+ }
+}
+
+void CMsgDialog::DM_ScrollToBottom(WPARAM wParam, LPARAM lParam)
+{
+ if (m_bScrollingDisabled)
+ return;
+
+ if (IsIconic(m_pContainer->m_hwnd))
+ m_bDeferredScroll = true;
+
+ if (m_iLogMode == WANT_BUILTIN_LOG)
+ ((CLogWindow *)m_pLog)->ScrollToBottom(wParam != 0, lParam != 0);
+ else
+ m_pLog->ScrollToBottom();
+}
+
+void CMsgDialog::DM_RecalcPictureSize()
+{
+ HBITMAP hbm = ((m_pPanel.isActive()) && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
+ if (hbm) {
+ BITMAP bminfo;
+ GetObject(hbm, sizeof(bminfo), &bminfo);
+ CalcDynamicAvatarSize(&bminfo);
+ Resize();
+ }
+ else m_pic.cy = m_pic.cx = 60;
+}
+
+void CMsgDialog::DM_UpdateLastMessage() const
+{
+ if (m_pContainer->m_hwndStatus == nullptr || m_pContainer->m_hwndActive != m_hwnd)
+ return;
+
+ wchar_t szBuf[100];
+ if (m_bShowTyping) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ mir_snwprintf(szBuf, TranslateT("%s is typing a message..."), m_cache->getNick());
+ }
+ else if (m_bStatusSet) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+ return;
+ }
+ else {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+
+ if (m_pContainer->cfg.flags.m_bUinStatusBar)
+ mir_snwprintf(szBuf, L"UID: %s", m_cache->getUIN());
+ else if (m_lastMessage) {
+ wchar_t date[64], time[64];
+ TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"d", date, _countof(date), 0);
+ if (m_pContainer->cfg.flags.m_bUinStatusBar && mir_wstrlen(date) > 6)
+ date[mir_wstrlen(date) - 5] = 0;
+ TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"t", time, _countof(time), 0);
+ mir_snwprintf(szBuf, TranslateT("Last received: %s at %s"), date, time);
+ }
+ else szBuf[0] = 0;
+ }
+
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szBuf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// create embedded contact list control
+
+HWND CMsgDialog::DM_CreateClist()
+{
+ if (!SendLater::Avail) {
+ CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
+ m_sendMode &= ~SMODE_MULTIPLE;
+ return nullptr;
+ }
+
+ HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248, 184, 0, 30, 30, m_hwnd, (HMENU)IDC_CLIST, g_plugin.getInst(), nullptr);
+ SendMessage(hwndClist, WM_TIMER, 14, 0);
+ HANDLE hItem = (HANDLE)SendMessage(hwndClist, CLM_FINDCONTACT, m_hContact, 0);
+
+ SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT);
+ SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL));
+
+ if (!g_plugin.bAllowOfflineMultisend)
+ SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE);
+
+ if (hItem)
+ SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM)hItem, 1);
+
+ SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, Clist::HideEmptyGroups, 0);
+ SendMessage(hwndClist, CLM_SETUSEGROUPS, Clist::UseGroups, 0);
+ SendMessage(hwndClist, CLM_FIRST + 106, 0, 1);
+ SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0);
+ if (hwndClist)
+ RedrawWindow(hwndClist, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
+ return hwndClist;
+}
+
+LRESULT CMsgDialog::DM_MouseWheelHandler(WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rc;
+ GetWindowRect(m_message.GetHwnd(), &rc);
+ if (PtInRect(&rc, pt))
+ return 1;
+
+ if (isChat()) { // scroll nick list by just hovering it
+ RECT rcNicklist;
+ GetWindowRect(m_nickList.GetHwnd(), &rcNicklist);
+ if (PtInRect(&rcNicklist, pt)) {
+ m_nickList.SendMsg(WM_MOUSEWHEEL, wParam, lParam);
+ return 0;
+ }
+ }
+
+ GetWindowRect(m_pLog->GetHwnd(), &rc);
+ if (PtInRect(&rc, pt)) {
+ short wDirection = (short)HIWORD(wParam);
+
+ if (LOWORD(wParam) & MK_SHIFT || M.GetByte("fastscroll", 0)) {
+ if (wDirection < 0)
+ SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
+ else if (wDirection > 0)
+ SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
+ }
+ else SendMessage(m_pLog->GetHwnd(), WM_MOUSEWHEEL, wParam, lParam);
+ return 0;
+ }
+
+ if (GetTabItemFromMouse(m_pContainer->m_hwndTabs, &pt) != -1) {
+ SendMessage(m_pContainer->m_hwndTabs, WM_MOUSEWHEEL, wParam, -1);
+ return 0;
+ }
+ return 1;
+}
+
+void CMsgDialog::DM_FreeTheme()
+{
+ if (m_hTheme) {
+ CloseThemeData(m_hTheme);
+ m_hTheme = nullptr;
+ }
+ if (m_hThemeIP) {
+ CloseThemeData(m_hThemeIP);
+ m_hThemeIP = nullptr;
+ }
+ if (m_hThemeToolbar) {
+ CloseThemeData(m_hThemeToolbar);
+ m_hThemeToolbar = nullptr;
+ }
+}
+
+void CMsgDialog::DM_ThemeChanged()
+{
+ CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY];
+ CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA];
+
+ m_hTheme = OpenThemeData(m_hwnd, L"EDIT");
+
+ if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_log->IGNORED)) {
+ if (m_iLogMode == WANT_BUILTIN_LOG)
+ LOG()->DisableStaticEdge();
+
+ if (isChat())
+ SetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+ }
+
+ if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_msg->IGNORED))
+ SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+
+ m_hThemeIP = M.isAero() ? OpenThemeData(m_hwnd, L"ButtonStyle") : nullptr;
+ m_hThemeToolbar = (M.isAero() || (!CSkin::m_skinEnabled && M.isVSThemed())) ? OpenThemeData(m_hwnd, L"REBAR") : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send out message typing notifications (MTN) when the
+// user is typing/editing text in the message input area.
+
+void CMsgDialog::DM_NotifyTyping(int mode)
+{
+ const char *szProto = m_cache->getActiveProto();
+ MCONTACT hContact = m_cache->getActiveContact();
+
+ // editing user notes or preparing a message for queued delivery -> don't send MTN
+ if (m_bEditNotesActive || (m_sendMode & SMODE_SENDLATER))
+ return;
+
+ // allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle)
+ if (!g_plugin.getByte(hContact, SRMSGSET_TYPING, g_plugin.bTypingNew))
+ return;
+
+ if (szProto == nullptr) // should not, but who knows...
+ return;
+
+ // check status and capabilities of the protocol
+ uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+ if (!(typeCaps & PF4_SUPPORTTYPING))
+ return;
+
+ if (isChat()) {
+ m_nTypeMode = mode;
+ Chat_DoEventHook(m_si, GC_USER_TYPNOTIFY, 0, 0, m_nTypeMode);
+ }
+ else {
+ uint32_t protoStatus = Proto_GetStatus(szProto);
+ if (protoStatus < ID_STATUS_ONLINE)
+ return;
+
+ // check visibility/invisibility lists to not "accidentially" send MTN to contacts who
+ // should not see them (privacy issue)
+ uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ if (protoCaps & PF1_VISLIST && db_get_w(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+ return;
+
+ if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && db_get_w(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+ return;
+
+ // don't send to contacts which are not permanently added to the contact list,
+ // unless the option to ignore added status is set.
+ if (!Contact::OnList(m_hContact) && !g_plugin.bTypingUnknown)
+ return;
+
+ // End user check
+ m_nTypeMode = mode;
+ CallService(MS_PROTO_SELFISTYPING, hContact, m_nTypeMode);
+ }
+}
+
+void CMsgDialog::DM_OptionsApplied(bool bRemakeLog)
+{
+ m_szMicroLf[0] = 0;
+ if (!m_pContainer->m_theme.isPrivate) {
+ m_pContainer->LoadThemeDefaults();
+ m_dwFlags = m_pContainer->m_theme.dwFlags;
+ }
+
+ LoadLocalFlags();
+ m_hTimeZone = TimeZone_CreateByContact(m_hContact, nullptr, TZF_KNOWNONLY);
+
+ m_bShowUIElements = (m_pContainer->cfg.flags.m_bHideToolbar) == 0;
+ m_bSplitterOverride = M.GetByte(m_hContact, "splitoverride", 0) != 0;
+ m_pPanel.getVisibility();
+
+ // small inner margins (padding) for the text areas
+ m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
+
+ GetSendFormat();
+ SetDialogToType();
+ SendMessage(m_hwnd, DM_CONFIGURETOOLBAR, 0, 0);
+
+ DM_InitRichEdit();
+ if (m_hwnd == m_pContainer->m_hwndActive)
+ SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
+ InvalidateRect(m_message.GetHwnd(), nullptr, FALSE);
+ if (bRemakeLog) {
+ if (IsIconic(m_pContainer->m_hwnd))
+ m_bDeferredRemakeLog = true;
+ else if (isChat())
+ RedrawLog();
+ else
+ RemakeLog();
+ }
+
+ ShowWindow(m_hwndPanelPicParent, SW_SHOW);
+ EnableWindow(m_hwndPanelPicParent, TRUE);
+
+ UpdateWindowIcon();
+}
+
+void CMsgDialog::DM_Typing(bool fForceOff)
+{
+ HWND hwndContainer = m_pContainer->m_hwnd;
+ HWND hwndStatus = m_pContainer->m_hwndStatus;
+
+ if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - m_nLastTyping > TIMEOUT_TYPEOFF)
+ DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
+
+ if (m_bShowTyping == 1) {
+ if (m_nTypeSecs > 0) {
+ m_nTypeSecs--;
+ if (GetForegroundWindow() == hwndContainer)
+ UpdateWindowIcon();
+ }
+ else {
+ if (!fForceOff) {
+ m_bShowTyping = 2;
+ m_nTypeSecs = 86400;
+
+ if (!isChat())
+ mir_snwprintf(m_wszStatusBar, TranslateT("%s has entered text."), m_cache->getNick());
+
+ if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd)
+ SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+ }
+ UpdateWindowIcon();
+ HandleIconFeedback(this, (HICON)-1);
+ CMsgDialog *dat_active = (CMsgDialog*)GetWindowLongPtr(m_pContainer->m_hwndActive, GWLP_USERDATA);
+ if (dat_active && !dat_active->isChat())
+ m_pContainer->UpdateTitle(0);
+ else
+ m_pContainer->UpdateTitle(0, dat_active);
+ if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
+ m_pContainer->ReflashContainer();
+ }
+ }
+ else if (m_bShowTyping == 2) {
+ if (m_nTypeSecs > 0)
+ m_nTypeSecs--;
+ else {
+ m_wszStatusBar[0] = 0;
+ m_bShowTyping = 0;
+ }
+ tabUpdateStatusBar();
+ }
+ else if (m_nTypeSecs > 0) {
+ mir_snwprintf(m_wszStatusBar, TranslateT("%s is typing a message"),
+ (m_pUserTyping) ? m_pUserTyping->pszNick : m_cache->getNick());
+
+ m_nTypeSecs--;
+ if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
+ SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+ SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ }
+ if (IsIconic(hwndContainer) || !IsActive()) {
+ SetWindowText(hwndContainer, m_wszStatusBar);
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
+ if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
+ m_pContainer->ReflashContainer();
+ }
+
+ if (m_pContainer->m_hwndActive != m_hwnd) {
+ if (m_bCanFlashTab)
+ m_iFlashIcon = PluginConfig.g_IconTypingEvent;
+ HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
+ }
+ else { // active tab may show icon if status bar is disabled
+ if (!hwndStatus) {
+ if (TabCtrl_GetItemCount(m_hwndParent) > 1 || !m_pContainer->cfg.flags.m_bHideTabs)
+ HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
+ }
+ }
+ if ((GetForegroundWindow() != hwndContainer) || (m_pContainer->m_hwndStatus == nullptr) || (m_pContainer->m_hwndActive != m_hwnd))
+ m_pContainer->SetIcon(this, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+
+ m_bShowTyping = 1;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sync splitter position for all open sessions.
+// This cares about private / per container / MUC <> IM splitter syncing and everything.
+// called from IM and MUC windows via DM_SPLITTERGLOBALEVENT
+
+int CMsgDialog::DM_SplitterGlobalEvent(WPARAM wParam, LPARAM lParam)
+{
+ CMsgDialog *srcDat = PluginConfig.lastSPlitterPos.pSrcDat;
+ TContainerData *srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer;
+ bool fCntGlobal = (!m_pContainer->cfg.fPrivate ? true : false);
+
+ if (m_bIsAutosizingInput)
+ return 0;
+
+ RECT rcWin;
+ GetWindowRect(m_hwnd, &rcWin);
+
+ LONG newPos;
+ if (wParam == 0 && lParam == 0) {
+ if (m_bSplitterOverride && this != srcDat)
+ return 0;
+
+ if (srcDat->isChat() == isChat())
+ newPos = PluginConfig.lastSPlitterPos.pos;
+ else if (!srcDat->isChat() && isChat())
+ newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+ else if (srcDat->isChat() && !isChat())
+ newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+ else
+ newPos = 0;
+
+ if (this == srcDat) {
+ m_pContainer->cfg.iSplitterY = m_iSplitterY;
+ if (fCntGlobal)
+ SaveSplitter();
+ return 0;
+ }
+
+ if (!fCntGlobal && m_pContainer != srcCnt)
+ return 0;
+ if (srcCnt->cfg.fPrivate && m_pContainer != srcCnt)
+ return 0;
+
+ // for inactive sessions, delay the splitter repositioning until they become
+ // active (faster, avoid redraw/resize problems for minimized windows)
+ if (IsIconic(m_pContainer->m_hwnd) || m_pContainer->m_hwndActive != m_hwnd) {
+ m_bDelayedSplitter = true;
+ m_wParam = newPos;
+ m_lParam = PluginConfig.lastSPlitterPos.lParam;
+ return 0;
+ }
+ }
+ else newPos = wParam;
+
+ LoadSplitter();
+ AdjustBottomAvatarDisplay();
+ DM_RecalcPictureSize();
+ Resize();
+ DM_ScrollToBottom(1, 1);
+ if (this != srcDat)
+ UpdateToolbarBG();
+ return 0;
+}
+
+void CMsgDialog::DM_AddDivider()
+{
+ if (!m_bDividerSet && g_plugin.bUseDividers)
+ if (GetWindowTextLength(m_pLog->GetHwnd()) > 0)
+ m_bDividerSet = m_bDividerWanted = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// incoming event handler
+
+void CMsgDialog::DM_EventAdded(WPARAM, LPARAM lParam)
+{
+ MEVENT hDbEvent = (MEVENT)lParam;
+
+ DBEVENTINFO dbei = {};
+ db_event_get(hDbEvent, &dbei);
+ if (m_hDbEventFirst == 0)
+ m_hDbEventFirst = hDbEvent;
+
+ bool bIsStatusChangeEvent = IsStatusEvent(dbei.eventType);
+ bool bDisableNotify = (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ));
+
+ if (!DbEventIsShown(&dbei))
+ return;
+
+ if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
+ m_lastMessage = dbei.timestamp;
+ m_wszStatusBar[0] = 0;
+ if (m_bShowTyping) {
+ m_nTypeSecs = 0;
+ DM_Typing(true);
+ m_bShowTyping = 0;
+ }
+ HandleIconFeedback(this, (HICON)-1);
+ if (m_pContainer->m_hwndStatus)
+ PostMessage(m_hwnd, DM_UPDATELASTMESSAGE, 0, 0);
+ }
+
+ // set the message log divider to mark new (maybe unseen) messages, if the container has
+ // been minimized or in the background.
+ if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
+ if (g_plugin.bDividersUsePopupConfig && g_plugin.bUseDividers) {
+ if (!MessageWindowOpened(m_hContact, nullptr))
+ DM_AddDivider();
+ }
+ else if (g_plugin.bUseDividers) {
+ if (!m_pContainer->IsActive())
+ DM_AddDivider();
+ else if (m_pContainer->m_hwndActive != m_hwnd)
+ DM_AddDivider();
+ }
+
+ if (IsWindowVisible(m_pContainer->m_hwnd))
+ m_pContainer->m_bHidden = false;
+ }
+ m_cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0);
+
+ if (hDbEvent != m_hDbEventFirst || isChat())
+ StreamEvents(hDbEvent, 1, 1);
+ else
+ RemakeLog();
+
+ // handle tab flashing
+ if (!bDisableNotify && !bIsStatusChangeEvent)
+ if ((TabCtrl_GetCurSel(m_hwndParent) != m_iTabID) && !(dbei.flags & DBEF_SENT)) {
+ switch (dbei.eventType) {
+ case EVENTTYPE_MESSAGE:
+ m_iFlashIcon = PluginConfig.g_IconMsgEvent;
+ break;
+ case EVENTTYPE_FILE:
+ m_iFlashIcon = PluginConfig.g_IconFileEvent;
+ break;
+ default:
+ m_iFlashIcon = PluginConfig.g_IconMsgEvent;
+ break;
+ }
+ timerFlash.Start(TIMEOUT_FLASHWND);
+ m_bCanFlashTab = true;
+ }
+
+ // try to flash the contact list...
+ if (!bDisableNotify)
+ FlashOnClist(hDbEvent, &dbei);
+
+ // autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch)
+ // never switch for status changes...
+ if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
+ if (g_plugin.bAutoSwitchTabs && m_pContainer->m_hwndActive != m_hwnd) {
+ if ((IsIconic(m_pContainer->m_hwnd) && !IsZoomed(m_pContainer->m_hwnd)) || (g_plugin.bHideOnClose && !IsWindowVisible(m_pContainer->m_hwnd))) {
+ int iItem = GetTabIndexFromHWND(GetParent(m_hwnd), m_hwnd);
+ if (iItem >= 0) {
+ TabCtrl_SetCurSel(m_hwndParent, iItem);
+ ShowWindow(m_pContainer->m_hwndActive, SW_HIDE);
+ m_pContainer->m_hwndActive = m_hwnd;
+ m_pContainer->UpdateTitle(m_hContact);
+ m_pContainer->cfg.flags.m_bDeferredTabSelect = true;
+ }
+ }
+ }
+ }
+
+ // flash window if it is not focused
+ if (!bDisableNotify && !bIsStatusChangeEvent)
+ if (!IsActive() && !(dbei.flags & DBEF_SENT)) {
+ if (!m_pContainer->cfg.flags.m_bNoFlash && !m_pContainer->IsActive())
+ m_pContainer->FlashContainer(1, 0);
+ m_pContainer->SetIcon(this, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
+ }
+
+ // play a sound
+ if (!bDisableNotify && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT)))
+ PlayIncomingSound();
+
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+}
+
+void CMsgDialog::DM_HandleAutoSizeRequest(REQRESIZE* rr)
+{
+ if (rr == nullptr || GetForegroundWindow() != m_pContainer->m_hwnd)
+ return;
+
+ if (!m_bIsAutosizingInput || m_iInputAreaHeight == -1)
+ return;
+
+ LONG heightLimit = M.GetDword("autoSplitMinLimit", 0);
+ LONG iNewHeight = rr->rc.bottom - rr->rc.top;
+
+ if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+ iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2);
+
+ if (heightLimit && iNewHeight < heightLimit)
+ iNewHeight = heightLimit;
+
+ if (iNewHeight == m_iInputAreaHeight)
+ return;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ LONG cy = rc.bottom - rc.top;
+ LONG panelHeight = (m_pPanel.isActive() ? m_pPanel.getHeight() : 0);
+
+ if (iNewHeight > (cy - panelHeight) / 2)
+ iNewHeight = (cy - panelHeight) / 2;
+
+ m_dynaSplitter = iNewHeight - DPISCALEY_S(2);
+ if (m_pContainer->cfg.flags.m_bBottomToolbar)
+ m_dynaSplitter += DPISCALEY_S(22);
+ m_iSplitterY = m_dynaSplitter + DPISCALEY_S(34);
+ DM_RecalcPictureSize();
+
+ m_iInputAreaHeight = iNewHeight;
+ UpdateToolbarBG();
+ DM_ScrollToBottom(1, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// status icon stuff (by sje, used for indicating encryption status in the status bar
+// this is now part of the message window api
+
+
+static int OnSrmmIconChanged(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0)
+ Srmm_Broadcast(DM_STATUSICONCHANGE, 0, 0);
+ else {
+ HWND hwnd = Srmm_FindWindow(hContact);
+ if (hwnd)
+ PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
+ }
+ return 0;
+}
+
+void CMsgDialog::DrawStatusIcons(HDC hDC, const RECT &rc, int gap)
+{
+ int x = rc.left;
+ int y = (rc.top + rc.bottom - PluginConfig.m_smcxicon) >> 1;
+
+ SetBkMode(hDC, TRANSPARENT);
+
+ int nIcon = 0;
+ while (StatusIconData *sid = Srmm_GetNthIcon(m_hContact, nIcon++)) {
+ if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
+ if (sid->dwId == MSG_ICON_SOUND) {
+ DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS],
+ PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+
+ DrawIconEx(hDC, x, y, m_pContainer->cfg.flags.m_bNoSound ?
+ PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled,
+ PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+ }
+ else if (sid->dwId == MSG_ICON_UTN) {
+ if (AllowTyping()) {
+ DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+
+ DrawIconEx(hDC, x, y, g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew) ?
+ PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+ }
+ else CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50);
+ }
+ }
+ else {
+ HICON hIcon;
+ if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled)
+ hIcon = sid->hIconDisabled;
+ else
+ hIcon = sid->hIcon;
+
+ if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled == nullptr)
+ CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIcon, 50);
+ else
+ DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+ }
+
+ x += PluginConfig.m_smcxicon + gap;
+ }
+}
+
+void CMsgDialog::CheckStatusIconClick(POINT pt, const RECT &rc, int gap, int code)
+{
+ if (code == NM_CLICK || code == NM_RCLICK) {
+ POINT ptScreen;
+ GetCursorPos(&ptScreen);
+ if (!PtInRect(&rcLastStatusBarClick, ptScreen))
+ return;
+ }
+
+ UINT iconNum = (pt.x - (rc.left + 0)) / (PluginConfig.m_smcxicon + gap);
+
+ StatusIconData *sid = Srmm_GetNthIcon(m_hContact, iconNum);
+ if (sid == nullptr)
+ return;
+
+ if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
+ if (sid->dwId == MSG_ICON_SOUND && code != NM_RCLICK) {
+ if (GetKeyState(VK_SHIFT) & 0x8000) {
+ for (TContainerData *p = pFirstContainer; p; p = p->pNext) {
+ p->cfg.flags.m_bNoSound = m_pContainer->cfg.flags.m_bNoSound;
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+ }
+ else {
+ m_pContainer->cfg.flags.m_bNoSound = !m_pContainer->cfg.flags.m_bNoSound;
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+ }
+ else if (sid->dwId == MSG_ICON_UTN && code != NM_RCLICK && AllowTyping()) {
+ SendMessage(m_pContainer->m_hwndActive, WM_COMMAND, IDC_SELFTYPING, 0);
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+ }
+ else {
+ StatusIconClickData sicd = { sizeof(sicd) };
+ GetCursorPos(&sicd.clickLocation);
+ sicd.dwId = sid->dwId;
+ sicd.szModule = sid->szModule;
+ sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
+ Srmm_ClickStatusIcon(m_hContact, &sicd);
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ }
+}
+
+void CMsgDialog::DM_ErrorDetected(int type, int flag)
+{
+ switch (type) {
+ case MSGERROR_CANCEL:
+ case MSGERROR_SENDLATER:
+ if (m_bErrorState) {
+ m_cache->saveHistory();
+ if (type == MSGERROR_SENDLATER)
+ sendQueue->doSendLater(m_iCurrentQueueError, this); // to be implemented at a later time
+ m_iOpenJobs--;
+ sendQueue->dec();
+ if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS)
+ sendQueue->clearJob(m_iCurrentQueueError);
+ m_iCurrentQueueError = -1;
+ sendQueue->showErrorControls(this, FALSE);
+ if (type != MSGERROR_CANCEL || flag == 0)
+ m_message.SetText(L"");
+ sendQueue->checkQueue(this);
+ int iNextFailed = sendQueue->findNextFailed(this);
+ if (iNextFailed >= 0)
+ sendQueue->handleError(this, iNextFailed);
+ }
+ break;
+
+ case MSGERROR_RETRY:
+ if (m_bErrorState) {
+ int resent = 0;
+
+ m_cache->saveHistory();
+ if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) {
+ SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
+ if (job->iSendId == 0 && job->hContact == 0)
+ break;
+
+ job->iSendId = ProtoChainSend(job->hContact, PSS_MESSAGE, job->dwFlags, (LPARAM)job->szSendBuffer);
+ resent++;
+ }
+
+ if (resent) {
+ SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
+
+ SetTimer(m_hwnd, TIMERID_MSGSEND + m_iCurrentQueueError, PluginConfig.m_MsgTimeout, nullptr);
+ job->iStatus = SendQueue::SQ_INPROGRESS;
+ m_iCurrentQueueError = -1;
+ sendQueue->showErrorControls(this, FALSE);
+ m_message.SetText(L"");
+ sendQueue->checkQueue(this);
+
+ int iNextFailed = sendQueue->findNextFailed(this);
+ if (iNextFailed >= 0)
+ sendQueue->handleError(this, iNextFailed);
+ }
+ }
+ }
+}
+
+int SI_InitStatusIcons()
+{
+ StatusIconData sid = {};
+ sid.szModule = MSG_ICON_MODULE;
+ sid.dwId = MSG_ICON_SOUND; // Sounds
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ sid.dwId = MSG_ICON_UTN;
+ Srmm_AddIcon(&sid, &g_plugin);
+
+ HookEvent(ME_MSG_ICONSCHANGED, OnSrmmIconChanged);
+ return 0;
+}
diff --git a/plugins/TabSRMM/src/globals.cpp b/plugins/TabSRMM/src/globals.cpp
index 953996a5cd..a80ff3f2b2 100644
--- a/plugins/TabSRMM/src/globals.cpp
+++ b/plugins/TabSRMM/src/globals.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/globals.h b/plugins/TabSRMM/src/globals.h
index fb52d01df2..db6229428e 100644
--- a/plugins/TabSRMM/src/globals.h
+++ b/plugins/TabSRMM/src/globals.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/hotkeyhandler.cpp b/plugins/TabSRMM/src/hotkeyhandler.cpp
index ebbd290ec5..5deb191552 100644
--- a/plugins/TabSRMM/src/hotkeyhandler.cpp
+++ b/plugins/TabSRMM/src/hotkeyhandler.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/infopanel.cpp b/plugins/TabSRMM/src/infopanel.cpp
index 69b1400fcb..37d433cd7f 100644
--- a/plugins/TabSRMM/src/infopanel.cpp
+++ b/plugins/TabSRMM/src/infopanel.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/infopanel.h b/plugins/TabSRMM/src/infopanel.h
index ead415ea29..ffa8c884a1 100644
--- a/plugins/TabSRMM/src/infopanel.h
+++ b/plugins/TabSRMM/src/infopanel.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/mim.cpp b/plugins/TabSRMM/src/mim.cpp
index 736c9c5e86..ca1e9c3272 100644
--- a/plugins/TabSRMM/src/mim.cpp
+++ b/plugins/TabSRMM/src/mim.cpp
@@ -1,488 +1,488 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/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.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// wraps some parts of Miranda API
-// Also, OS dependent stuff (visual styles api etc.)
-
-#include "stdafx.h"
-
-PDTTE CMimAPI::m_pfnDrawThemeTextEx = nullptr;
-DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = nullptr;
-DICE CMimAPI::m_pfnDwmIsCompositionEnabled = nullptr;
-DRT CMimAPI::m_pfnDwmRegisterThumbnail = nullptr;
-BPI CMimAPI::m_pfnBufferedPaintInit = nullptr;
-BPU CMimAPI::m_pfnBufferedPaintUninit = nullptr;
-BBP CMimAPI::m_pfnBeginBufferedPaint = nullptr;
-EBP CMimAPI::m_pfnEndBufferedPaint = nullptr;
-BBW CMimAPI::m_pfnDwmBlurBehindWindow = nullptr;
-DGC CMimAPI::m_pfnDwmGetColorizationColor = nullptr;
-BPSA CMimAPI::m_pfnBufferedPaintSetAlpha = nullptr;
-DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = nullptr;
-DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = nullptr;
-DWMUT CMimAPI::m_pfnDwmUpdateThumbnailProperties = nullptr;
-DURT CMimAPI::m_pfnDwmUnregisterThumbnail = nullptr;
-DSIT CMimAPI::m_pfnDwmSetIconicThumbnail = nullptr;
-DSILP CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = nullptr;
-bool CMimAPI::m_shutDown = 0;
-wchar_t CMimAPI::m_userDir[] = L"\0";
-
-bool CMimAPI::m_haveBufferedPaint = false;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMimAPI::FoldersPathChanged(WPARAM, LPARAM)
-{
- return M.foldersPathChanged();
-}
-
-void CMimAPI::configureCustomFolders()
-{
- m_hDataPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Data path"), const_cast<wchar_t *>(getDataPath()));
- m_hSkinsPath = FoldersRegisterCustomPathW(LPGEN("Skins"), LPGEN("TabSRMM"), const_cast<wchar_t *>(getSkinPath()));
- m_hAvatarsPath = FoldersRegisterCustomPathW(LPGEN("Avatars"), LPGEN("Saved TabSRMM avatars"), const_cast<wchar_t *>(getSavedAvatarPath()));
- m_hChatLogsPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Group chat logs root"), const_cast<wchar_t *>(getChatLogPath()));
-
- if (m_hDataPath)
- HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged);
-
- foldersPathChanged();
-}
-
-INT_PTR CMimAPI::foldersPathChanged()
-{
- wchar_t szTemp[MAX_PATH + 2];
-
- if (m_hDataPath) {
- szTemp[0] = 0;
- FoldersGetCustomPathW(m_hDataPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getDataPath()));
- wcsncpy_s(m_szProfilePath, szTemp, _TRUNCATE);
-
- szTemp[0] = 0;
- FoldersGetCustomPathW(m_hSkinsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSkinPath()));
- wcsncpy_s(m_szSkinsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
- Utils::ensureTralingBackslash(m_szSkinsPath);
-
- szTemp[0] = 0;
- FoldersGetCustomPathW(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSavedAvatarPath()));
- wcsncpy_s(m_szSavedAvatarsPath, szTemp, _TRUNCATE);
-
- szTemp[0] = 0;
- FoldersGetCustomPathW(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getChatLogPath()));
- wcsncpy_s(m_szChatLogsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
- Utils::ensureTralingBackslash(m_szChatLogsPath);
- }
-
- CreateDirectoryTreeW(m_szProfilePath);
- CreateDirectoryTreeW(m_szSkinsPath);
- CreateDirectoryTreeW(m_szSavedAvatarsPath);
-
- Skin->extractSkinsAndLogo(true);
- Skin->setupAeroSkins();
- return 0;
-}
-
-const wchar_t* CMimAPI::getUserDir()
-{
- if (m_userDir[0] == 0) {
- if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
- wcsncpy_s(m_userDir, L"%miranda_userdata%", _TRUNCATE);
- else
- wcsncpy_s(m_userDir, VARSW(L"%miranda_userdata%"), _TRUNCATE);
-
- Utils::ensureTralingBackslash(m_userDir);
- }
- return m_userDir;
-}
-
-void CMimAPI::InitPaths()
-{
- const wchar_t *szUserdataDir = getUserDir();
-
- mir_snwprintf(m_szProfilePath, L"%stabSRMM", szUserdataDir);
- if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) {
- wcsncpy_s(m_szChatLogsPath, L"%miranda_logpath%", _TRUNCATE);
- wcsncpy_s(m_szSkinsPath, L"%miranda_path%\\Skins\\TabSRMM", _TRUNCATE);
- }
- else {
- wcsncpy_s(m_szChatLogsPath, VARSW(L"%miranda_logpath%"), _TRUNCATE);
- wcsncpy_s(m_szSkinsPath, VARSW(L"%miranda_path%\\Skins\\TabSRMM"), _TRUNCATE);
- }
-
- Utils::ensureTralingBackslash(m_szChatLogsPath);
- replaceStrW(g_Settings.pszLogDir, m_szChatLogsPath);
-
- Utils::ensureTralingBackslash(m_szSkinsPath);
-
- mir_snwprintf(m_szSavedAvatarsPath, L"%s\\Saved Contact Pictures", m_szProfilePath);
-}
-
-bool CMimAPI::getAeroState()
-{
- m_isAero = m_DwmActive = false;
- if (IsWinVerVistaPlus()) {
- BOOL result = FALSE;
- m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result);
- m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive;
-
- }
- m_isVsThemed = IsThemeActive() != 0;
- return m_isAero;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Initialize various Win32 API functions which are not common to all versions of Windows.
-// We have to work with functions pointers here.
-
-void CMimAPI::InitAPI()
-{
- m_hUxTheme = nullptr;
- m_hDwmApi = nullptr;
-
- // vista+ DWM API
- if (IsWinVerVistaPlus()) {
- m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll");
- if (m_hDwmApi) {
- m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi, "DwmExtendFrameIntoClientArea");
- m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi, "DwmIsCompositionEnabled");
- m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail");
- m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow");
- m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor");
- m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps");
- m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute");
- m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties");
- m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail");
- m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail");
- m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap");
- }
-
- // additional uxtheme APIs (Vista+)
- m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll");
- if (m_hUxTheme) {
- m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx");
- m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint");
- m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint");
- m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit");
- m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit");
- m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha");
- m_haveBufferedPaint = (m_pfnBeginBufferedPaint != nullptr && m_pfnEndBufferedPaint != nullptr) ? true : false;
- if (m_haveBufferedPaint)
- m_pfnBufferedPaintInit();
- }
- }
- else m_haveBufferedPaint = false;
-
- switch (GetByte("default_ieview", -1)) {
- case 1:
- db_set_s(0, "SRMM", "Logger", "ieview");
- __fallthrough;
-
- case 0:
- db_unset(0, SRMSGMOD_T, "default_ieview");
- }
-
- switch (GetByte("default_hpp", -1)) {
- case 1:
- db_set_s(0, "SRMM", "Logger", "hpp");
- __fallthrough;
-
- case 0:
- db_unset(0, SRMSGMOD_T, "default_hpp");
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// hook subscriber function for incoming message typing events
-
-int CMimAPI::TypingMessage(WPARAM hContact, LPARAM nSecs)
-{
- int foundWin = 0, preTyping = 0;
- BOOL fShowOnClist = TRUE;
-
- auto *pDlg = Srmm_FindDialog(hContact);
- MCONTACT hMeta = db_mc_getMeta(hContact);
- if (hMeta) {
- if (!pDlg)
- pDlg = Srmm_FindDialog(hMeta);
- hContact = hMeta;
- }
-
- if (pDlg && g_plugin.getByte(SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING))
- preTyping = pDlg->Typing(nSecs);
-
- if (pDlg && IsWindowVisible(pDlg->GetHwnd()))
- foundWin = MessageWindowOpened(0, pDlg);
- else
- foundWin = 0;
-
- TContainerData *pContainer = nullptr;
- if (pDlg) {
- pContainer = pDlg->m_pContainer;
- if (pContainer == nullptr) // should never happen
- return 0;
- }
-
- if (g_plugin.getByte(SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) {
- if (!pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGNOWINOPEN, 1))
- fShowOnClist = false;
- if (pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGWINOPEN, 1))
- fShowOnClist = false;
- }
- else fShowOnClist = false;
-
- if ((!foundWin || !pContainer->cfg.flags.m_bNoSound) && preTyping != (nSecs != 0))
- Skin_PlaySound(nSecs ? "TNStart" : "TNStop");
-
- if (g_plugin.bPopups) {
- BOOL fShow = false;
- int iMode = M.GetByte("MTN_PopupMode", 0);
-
- switch (iMode) {
- case 0:
- fShow = true;
- break;
- case 1:
- if (!foundWin || !(pContainer && pContainer->m_hwndActive == pDlg->GetHwnd() && GetForegroundWindow() == pContainer->m_hwnd))
- fShow = true;
- break;
- case 2:
- if (pDlg == nullptr)
- fShow = true;
- else {
- if (g_plugin.bHideOnClose) {
- TContainerData *pCont = pDlg->m_pContainer;
- if (pCont && pCont->m_bHidden)
- fShow = true;
- }
- }
- break;
- }
- if (fShow)
- TN_TypingMessage(hContact, nSecs);
- }
-
- if (nSecs) {
- wchar_t szTip[256];
- mir_snwprintf(szTip, TranslateT("%s is typing a message"), Clist_GetContactDisplayName(hContact));
- if (fShowOnClist && g_plugin.getByte("ShowTypingBalloon", 0))
- Clist_TrayNotifyW(nullptr, TranslateT("Typing notification"), szTip, NIIF_INFO, 1000 * 4);
-
- if (fShowOnClist) {
- g_clistApi.pfnRemoveEvent(hContact, 1);
-
- CLISTEVENT cle = {};
- cle.hContact = hContact;
- cle.hDbEvent = 1;
- cle.flags = CLEF_ONLYAFEW | CLEF_UNICODE;
- cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
- cle.pszService = MS_MSG_TYPINGMESSAGE;
- cle.szTooltip.w = szTip;
- g_clistApi.pfnAddEvent(&cle);
- }
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// this is the global ack dispatcher.It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events
-// for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches
-// it to the owners window
-//
-// ACKTYPE_AVATAR no longer handled here, because we have avs services now.
-
-int CMimAPI::ProtoAck(WPARAM, LPARAM lParam)
-{
- ACKDATA *pAck = (ACKDATA*)lParam;
-
- if ((pAck != nullptr) && (pAck->type == ACKTYPE_MESSAGE)) {
- int i = 0, iFound = SendQueue::NR_SENDJOBS;
- SendJob *jobs = sendQueue->getJobByIndex(0);
- MCONTACT hMeta = db_mc_getMeta(pAck->hContact);
- for (int j = 0; j < SendQueue::NR_SENDJOBS; j++) {
- SendJob &p = jobs[j];
- if (pAck->hProcess == (HANDLE)p.iSendId && pAck->hContact == p.hContact) {
- CMsgDialog *dat = p.hOwnerWnd ? (CMsgDialog*)GetWindowLongPtr(p.hOwnerWnd, GWLP_USERDATA) : nullptr;
- if (dat == nullptr) {
- sendQueue->ackMessage(nullptr, (WPARAM)MAKELONG(j, i), lParam);
- return 0;
- }
- if (dat->m_hContact == p.hContact || dat->m_hContact == hMeta) {
- iFound = j;
- break;
- }
- }
- }
- if (iFound == SendQueue::NR_SENDJOBS) // no matching send info found in the queue
- SendLater::processAck(pAck);
- else // try to find the process handle in the list of open send later jobs
- SendMessage(jobs[iFound].hOwnerWnd, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam);
- }
- return 0;
-}
-
-int CMimAPI::PrebuildContactMenu(WPARAM hContact, LPARAM)
-{
- if (hContact == 0)
- return 0;
-
- bool bEnabled = false;
- char *szProto = Proto_GetBaseAccountName(hContact);
- if (szProto) {
- // leave this menu item hidden for chats
- if (!Contact::IsGroupChat(hContact, szProto))
- if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
- bEnabled = true;
- }
-
- Menu_ShowItem(PluginConfig.m_hMenuItem, bEnabled);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// this handler is called first in the message window chain - it will handle events for which a message window
-// is already open. if not, it will do nothing and the 2nd handler(MessageEventAdded) will perform all
-// the needed actions.
-//
-// this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will
-// improve the overall responsiveness when receiving messages.
-
-int CMimAPI::DispatchNewEvent(WPARAM hContact, LPARAM hDbEvent)
-{
- if (hContact) {
- Utils::sendContactMessage(hContact, HM_DBEVENTADDED, hContact, hDbEvent);
-
- // we're in meta and an event belongs to a sub
- MCONTACT hReal = db_event_getContact(hDbEvent);
- if (hReal != hContact)
- Utils::sendContactMessage(hReal, HM_DBEVENTADDED, hContact, hDbEvent);
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Message event added is called when a new message is added to the database
-// if no session is open for the contact, this function will determine if and how a new message
-// session(tab) must be created.
-//
-// if a session is already created, it just does nothing and DispatchNewEvent() will take care.
-
-int CMimAPI::MessageEventAdded(WPARAM hContact, LPARAM hDbEvent)
-{
- if (hContact == 0)
- return 0;
-
- DBEVENTINFO dbei = {};
- db_event_get(hDbEvent, &dbei);
-
- auto *pDlg = Srmm_FindDialog(hContact);
- if (pDlg == nullptr)
- pDlg = Srmm_FindDialog(db_event_getContact(hDbEvent));
-
- BOOL isCustomEvent = IsCustomEvent(dbei.eventType);
- BOOL isShownCustomEvent = DbEventIsForMsgWindow(&dbei);
- if (dbei.markedRead() || (isCustomEvent && !isShownCustomEvent))
- return 0;
-
- g_clistApi.pfnRemoveEvent(hContact, 1);
-
- bool bAutoPopup = g_plugin.bAutoPopup;
- bool bAutoCreate = g_plugin.bAutoTabs;
- bool bAutoContainer = g_plugin.bAutoContainer;
-
- if (pDlg) {
- TContainerData *pTargetContainer = pDlg->m_pContainer;
- if (pTargetContainer == nullptr || !g_plugin.bHideOnClose || IsWindowVisible(pTargetContainer->m_hwnd))
- return 0;
-
- WINDOWPLACEMENT wp = { 0 };
- wp.length = sizeof(wp);
- GetWindowPlacement(pTargetContainer->m_hwnd, &wp);
-
- wchar_t szName[CONTAINER_NAMELEN + 1];
- GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN);
-
- if (bAutoPopup || bAutoCreate) {
- if (bAutoPopup) {
- if (wp.showCmd == SW_SHOWMAXIMIZED)
- ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMAXIMIZED);
- else
- ShowWindow(pTargetContainer->m_hwnd, SW_SHOWNOACTIVATE);
- return 0;
- }
-
- TContainerData *pContainer = FindContainerByName(szName);
- if (pContainer != nullptr) {
- if (bAutoContainer) {
- ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
- return 0;
- }
- goto nowindowcreate;
- }
- else if (bAutoContainer) {
- ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
- return 0;
- }
- }
- }
- else {
- switch (dbei.eventType) {
- case EVENTTYPE_AUTHREQUEST:
- case EVENTTYPE_ADDED:
- case EVENTTYPE_FILE:
- return 0;
- }
- }
-
- if (!NEN::bNoSounds)
- Skin_PlaySound("AlertMsg");
-
- if (NEN::bNoAutoPopup)
- goto nowindowcreate;
-
- PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_CREATECONTAINER, hContact, hDbEvent);
- return 0;
-
-nowindowcreate:
- // for tray support, we add the event to the tray menu. otherwise we send it back to
- // the contact list for flashing
- if (!(dbei.flags & DBEF_READ)) {
- AddUnreadContact(hContact);
-
- wchar_t toolTip[256];
- mir_snwprintf(toolTip, TranslateT("Message from %s"), Clist_GetContactDisplayName(hContact));
-
- CLISTEVENT cle = {};
- cle.hContact = hContact;
- cle.hDbEvent = hDbEvent;
- cle.flags = CLEF_UNICODE;
- cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
- cle.pszService = MS_MSG_READMESSAGE;
- cle.szTooltip.w = toolTip;
- g_clistApi.pfnAddEvent(&cle);
- }
- return 0;
-}
-
-CMimAPI M;
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/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.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// wraps some parts of Miranda API
+// Also, OS dependent stuff (visual styles api etc.)
+
+#include "stdafx.h"
+
+PDTTE CMimAPI::m_pfnDrawThemeTextEx = nullptr;
+DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = nullptr;
+DICE CMimAPI::m_pfnDwmIsCompositionEnabled = nullptr;
+DRT CMimAPI::m_pfnDwmRegisterThumbnail = nullptr;
+BPI CMimAPI::m_pfnBufferedPaintInit = nullptr;
+BPU CMimAPI::m_pfnBufferedPaintUninit = nullptr;
+BBP CMimAPI::m_pfnBeginBufferedPaint = nullptr;
+EBP CMimAPI::m_pfnEndBufferedPaint = nullptr;
+BBW CMimAPI::m_pfnDwmBlurBehindWindow = nullptr;
+DGC CMimAPI::m_pfnDwmGetColorizationColor = nullptr;
+BPSA CMimAPI::m_pfnBufferedPaintSetAlpha = nullptr;
+DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = nullptr;
+DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = nullptr;
+DWMUT CMimAPI::m_pfnDwmUpdateThumbnailProperties = nullptr;
+DURT CMimAPI::m_pfnDwmUnregisterThumbnail = nullptr;
+DSIT CMimAPI::m_pfnDwmSetIconicThumbnail = nullptr;
+DSILP CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = nullptr;
+bool CMimAPI::m_shutDown = 0;
+wchar_t CMimAPI::m_userDir[] = L"\0";
+
+bool CMimAPI::m_haveBufferedPaint = false;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMimAPI::FoldersPathChanged(WPARAM, LPARAM)
+{
+ return M.foldersPathChanged();
+}
+
+void CMimAPI::configureCustomFolders()
+{
+ m_hDataPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Data path"), const_cast<wchar_t *>(getDataPath()));
+ m_hSkinsPath = FoldersRegisterCustomPathW(LPGEN("Skins"), LPGEN("TabSRMM"), const_cast<wchar_t *>(getSkinPath()));
+ m_hAvatarsPath = FoldersRegisterCustomPathW(LPGEN("Avatars"), LPGEN("Saved TabSRMM avatars"), const_cast<wchar_t *>(getSavedAvatarPath()));
+ m_hChatLogsPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Group chat logs root"), const_cast<wchar_t *>(getChatLogPath()));
+
+ if (m_hDataPath)
+ HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged);
+
+ foldersPathChanged();
+}
+
+INT_PTR CMimAPI::foldersPathChanged()
+{
+ wchar_t szTemp[MAX_PATH + 2];
+
+ if (m_hDataPath) {
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hDataPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getDataPath()));
+ wcsncpy_s(m_szProfilePath, szTemp, _TRUNCATE);
+
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hSkinsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSkinPath()));
+ wcsncpy_s(m_szSkinsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
+ Utils::ensureTralingBackslash(m_szSkinsPath);
+
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSavedAvatarPath()));
+ wcsncpy_s(m_szSavedAvatarsPath, szTemp, _TRUNCATE);
+
+ szTemp[0] = 0;
+ FoldersGetCustomPathW(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getChatLogPath()));
+ wcsncpy_s(m_szChatLogsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
+ Utils::ensureTralingBackslash(m_szChatLogsPath);
+ }
+
+ CreateDirectoryTreeW(m_szProfilePath);
+ CreateDirectoryTreeW(m_szSkinsPath);
+ CreateDirectoryTreeW(m_szSavedAvatarsPath);
+
+ Skin->extractSkinsAndLogo(true);
+ Skin->setupAeroSkins();
+ return 0;
+}
+
+const wchar_t* CMimAPI::getUserDir()
+{
+ if (m_userDir[0] == 0) {
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
+ wcsncpy_s(m_userDir, L"%miranda_userdata%", _TRUNCATE);
+ else
+ wcsncpy_s(m_userDir, VARSW(L"%miranda_userdata%"), _TRUNCATE);
+
+ Utils::ensureTralingBackslash(m_userDir);
+ }
+ return m_userDir;
+}
+
+void CMimAPI::InitPaths()
+{
+ const wchar_t *szUserdataDir = getUserDir();
+
+ mir_snwprintf(m_szProfilePath, L"%stabSRMM", szUserdataDir);
+ if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) {
+ wcsncpy_s(m_szChatLogsPath, L"%miranda_logpath%", _TRUNCATE);
+ wcsncpy_s(m_szSkinsPath, L"%miranda_path%\\Skins\\TabSRMM", _TRUNCATE);
+ }
+ else {
+ wcsncpy_s(m_szChatLogsPath, VARSW(L"%miranda_logpath%"), _TRUNCATE);
+ wcsncpy_s(m_szSkinsPath, VARSW(L"%miranda_path%\\Skins\\TabSRMM"), _TRUNCATE);
+ }
+
+ Utils::ensureTralingBackslash(m_szChatLogsPath);
+ replaceStrW(g_Settings.pszLogDir, m_szChatLogsPath);
+
+ Utils::ensureTralingBackslash(m_szSkinsPath);
+
+ mir_snwprintf(m_szSavedAvatarsPath, L"%s\\Saved Contact Pictures", m_szProfilePath);
+}
+
+bool CMimAPI::getAeroState()
+{
+ m_isAero = m_DwmActive = false;
+ if (IsWinVerVistaPlus()) {
+ BOOL result = FALSE;
+ m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result);
+ m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive;
+
+ }
+ m_isVsThemed = IsThemeActive() != 0;
+ return m_isAero;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Initialize various Win32 API functions which are not common to all versions of Windows.
+// We have to work with functions pointers here.
+
+void CMimAPI::InitAPI()
+{
+ m_hUxTheme = nullptr;
+ m_hDwmApi = nullptr;
+
+ // vista+ DWM API
+ if (IsWinVerVistaPlus()) {
+ m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll");
+ if (m_hDwmApi) {
+ m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi, "DwmExtendFrameIntoClientArea");
+ m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi, "DwmIsCompositionEnabled");
+ m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail");
+ m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow");
+ m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor");
+ m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps");
+ m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute");
+ m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties");
+ m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail");
+ m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail");
+ m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap");
+ }
+
+ // additional uxtheme APIs (Vista+)
+ m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll");
+ if (m_hUxTheme) {
+ m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx");
+ m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint");
+ m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint");
+ m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit");
+ m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit");
+ m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha");
+ m_haveBufferedPaint = (m_pfnBeginBufferedPaint != nullptr && m_pfnEndBufferedPaint != nullptr) ? true : false;
+ if (m_haveBufferedPaint)
+ m_pfnBufferedPaintInit();
+ }
+ }
+ else m_haveBufferedPaint = false;
+
+ switch (GetByte("default_ieview", -1)) {
+ case 1:
+ db_set_s(0, "SRMM", "Logger", "ieview");
+ __fallthrough;
+
+ case 0:
+ db_unset(0, SRMSGMOD_T, "default_ieview");
+ }
+
+ switch (GetByte("default_hpp", -1)) {
+ case 1:
+ db_set_s(0, "SRMM", "Logger", "hpp");
+ __fallthrough;
+
+ case 0:
+ db_unset(0, SRMSGMOD_T, "default_hpp");
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// hook subscriber function for incoming message typing events
+
+int CMimAPI::TypingMessage(WPARAM hContact, LPARAM nSecs)
+{
+ int foundWin = 0, preTyping = 0;
+ BOOL fShowOnClist = TRUE;
+
+ auto *pDlg = Srmm_FindDialog(hContact);
+ MCONTACT hMeta = db_mc_getMeta(hContact);
+ if (hMeta) {
+ if (!pDlg)
+ pDlg = Srmm_FindDialog(hMeta);
+ hContact = hMeta;
+ }
+
+ if (pDlg && g_plugin.getByte(SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING))
+ preTyping = pDlg->Typing(nSecs);
+
+ if (pDlg && IsWindowVisible(pDlg->GetHwnd()))
+ foundWin = MessageWindowOpened(0, pDlg);
+ else
+ foundWin = 0;
+
+ TContainerData *pContainer = nullptr;
+ if (pDlg) {
+ pContainer = pDlg->m_pContainer;
+ if (pContainer == nullptr) // should never happen
+ return 0;
+ }
+
+ if (g_plugin.getByte(SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) {
+ if (!pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGNOWINOPEN, 1))
+ fShowOnClist = false;
+ if (pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGWINOPEN, 1))
+ fShowOnClist = false;
+ }
+ else fShowOnClist = false;
+
+ if ((!foundWin || !pContainer->cfg.flags.m_bNoSound) && preTyping != (nSecs != 0))
+ Skin_PlaySound(nSecs ? "TNStart" : "TNStop");
+
+ if (g_plugin.bPopups) {
+ BOOL fShow = false;
+ int iMode = M.GetByte("MTN_PopupMode", 0);
+
+ switch (iMode) {
+ case 0:
+ fShow = true;
+ break;
+ case 1:
+ if (!foundWin || !(pContainer && pContainer->m_hwndActive == pDlg->GetHwnd() && GetForegroundWindow() == pContainer->m_hwnd))
+ fShow = true;
+ break;
+ case 2:
+ if (pDlg == nullptr)
+ fShow = true;
+ else {
+ if (g_plugin.bHideOnClose) {
+ TContainerData *pCont = pDlg->m_pContainer;
+ if (pCont && pCont->m_bHidden)
+ fShow = true;
+ }
+ }
+ break;
+ }
+ if (fShow)
+ TN_TypingMessage(hContact, nSecs);
+ }
+
+ if (nSecs) {
+ wchar_t szTip[256];
+ mir_snwprintf(szTip, TranslateT("%s is typing a message"), Clist_GetContactDisplayName(hContact));
+ if (fShowOnClist && g_plugin.getByte("ShowTypingBalloon", 0))
+ Clist_TrayNotifyW(nullptr, TranslateT("Typing notification"), szTip, NIIF_INFO, 1000 * 4);
+
+ if (fShowOnClist) {
+ g_clistApi.pfnRemoveEvent(hContact, 1);
+
+ CLISTEVENT cle = {};
+ cle.hContact = hContact;
+ cle.hDbEvent = 1;
+ cle.flags = CLEF_ONLYAFEW | CLEF_UNICODE;
+ cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
+ cle.pszService = MS_MSG_TYPINGMESSAGE;
+ cle.szTooltip.w = szTip;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this is the global ack dispatcher.It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events
+// for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches
+// it to the owners window
+//
+// ACKTYPE_AVATAR no longer handled here, because we have avs services now.
+
+int CMimAPI::ProtoAck(WPARAM, LPARAM lParam)
+{
+ ACKDATA *pAck = (ACKDATA*)lParam;
+
+ if ((pAck != nullptr) && (pAck->type == ACKTYPE_MESSAGE)) {
+ int i = 0, iFound = SendQueue::NR_SENDJOBS;
+ SendJob *jobs = sendQueue->getJobByIndex(0);
+ MCONTACT hMeta = db_mc_getMeta(pAck->hContact);
+ for (int j = 0; j < SendQueue::NR_SENDJOBS; j++) {
+ SendJob &p = jobs[j];
+ if (pAck->hProcess == (HANDLE)p.iSendId && pAck->hContact == p.hContact) {
+ CMsgDialog *dat = p.hOwnerWnd ? (CMsgDialog*)GetWindowLongPtr(p.hOwnerWnd, GWLP_USERDATA) : nullptr;
+ if (dat == nullptr) {
+ sendQueue->ackMessage(nullptr, (WPARAM)MAKELONG(j, i), lParam);
+ return 0;
+ }
+ if (dat->m_hContact == p.hContact || dat->m_hContact == hMeta) {
+ iFound = j;
+ break;
+ }
+ }
+ }
+ if (iFound == SendQueue::NR_SENDJOBS) // no matching send info found in the queue
+ SendLater::processAck(pAck);
+ else // try to find the process handle in the list of open send later jobs
+ SendMessage(jobs[iFound].hOwnerWnd, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam);
+ }
+ return 0;
+}
+
+int CMimAPI::PrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+ if (hContact == 0)
+ return 0;
+
+ bool bEnabled = false;
+ char *szProto = Proto_GetBaseAccountName(hContact);
+ if (szProto) {
+ // leave this menu item hidden for chats
+ if (!Contact::IsGroupChat(hContact, szProto))
+ if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
+ bEnabled = true;
+ }
+
+ Menu_ShowItem(PluginConfig.m_hMenuItem, bEnabled);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this handler is called first in the message window chain - it will handle events for which a message window
+// is already open. if not, it will do nothing and the 2nd handler(MessageEventAdded) will perform all
+// the needed actions.
+//
+// this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will
+// improve the overall responsiveness when receiving messages.
+
+int CMimAPI::DispatchNewEvent(WPARAM hContact, LPARAM hDbEvent)
+{
+ if (hContact) {
+ Utils::sendContactMessage(hContact, HM_DBEVENTADDED, hContact, hDbEvent);
+
+ // we're in meta and an event belongs to a sub
+ MCONTACT hReal = db_event_getContact(hDbEvent);
+ if (hReal != hContact)
+ Utils::sendContactMessage(hReal, HM_DBEVENTADDED, hContact, hDbEvent);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Message event added is called when a new message is added to the database
+// if no session is open for the contact, this function will determine if and how a new message
+// session(tab) must be created.
+//
+// if a session is already created, it just does nothing and DispatchNewEvent() will take care.
+
+int CMimAPI::MessageEventAdded(WPARAM hContact, LPARAM hDbEvent)
+{
+ if (hContact == 0)
+ return 0;
+
+ DBEVENTINFO dbei = {};
+ db_event_get(hDbEvent, &dbei);
+
+ auto *pDlg = Srmm_FindDialog(hContact);
+ if (pDlg == nullptr)
+ pDlg = Srmm_FindDialog(db_event_getContact(hDbEvent));
+
+ BOOL isCustomEvent = IsCustomEvent(dbei.eventType);
+ BOOL isShownCustomEvent = DbEventIsForMsgWindow(&dbei);
+ if (dbei.markedRead() || (isCustomEvent && !isShownCustomEvent))
+ return 0;
+
+ g_clistApi.pfnRemoveEvent(hContact, 1);
+
+ bool bAutoPopup = g_plugin.bAutoPopup;
+ bool bAutoCreate = g_plugin.bAutoTabs;
+ bool bAutoContainer = g_plugin.bAutoContainer;
+
+ if (pDlg) {
+ TContainerData *pTargetContainer = pDlg->m_pContainer;
+ if (pTargetContainer == nullptr || !g_plugin.bHideOnClose || IsWindowVisible(pTargetContainer->m_hwnd))
+ return 0;
+
+ WINDOWPLACEMENT wp = { 0 };
+ wp.length = sizeof(wp);
+ GetWindowPlacement(pTargetContainer->m_hwnd, &wp);
+
+ wchar_t szName[CONTAINER_NAMELEN + 1];
+ GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN);
+
+ if (bAutoPopup || bAutoCreate) {
+ if (bAutoPopup) {
+ if (wp.showCmd == SW_SHOWMAXIMIZED)
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMAXIMIZED);
+ else
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWNOACTIVATE);
+ return 0;
+ }
+
+ TContainerData *pContainer = FindContainerByName(szName);
+ if (pContainer != nullptr) {
+ if (bAutoContainer) {
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
+ return 0;
+ }
+ goto nowindowcreate;
+ }
+ else if (bAutoContainer) {
+ ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
+ return 0;
+ }
+ }
+ }
+ else {
+ switch (dbei.eventType) {
+ case EVENTTYPE_AUTHREQUEST:
+ case EVENTTYPE_ADDED:
+ case EVENTTYPE_FILE:
+ return 0;
+ }
+ }
+
+ if (!NEN::bNoSounds)
+ Skin_PlaySound("AlertMsg");
+
+ if (NEN::bNoAutoPopup)
+ goto nowindowcreate;
+
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_CREATECONTAINER, hContact, hDbEvent);
+ return 0;
+
+nowindowcreate:
+ // for tray support, we add the event to the tray menu. otherwise we send it back to
+ // the contact list for flashing
+ if (!(dbei.flags & DBEF_READ)) {
+ AddUnreadContact(hContact);
+
+ wchar_t toolTip[256];
+ mir_snwprintf(toolTip, TranslateT("Message from %s"), Clist_GetContactDisplayName(hContact));
+
+ CLISTEVENT cle = {};
+ cle.hContact = hContact;
+ cle.hDbEvent = hDbEvent;
+ cle.flags = CLEF_UNICODE;
+ cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = MS_MSG_READMESSAGE;
+ cle.szTooltip.w = toolTip;
+ g_clistApi.pfnAddEvent(&cle);
+ }
+ return 0;
+}
+
+CMimAPI M;
diff --git a/plugins/TabSRMM/src/mim.h b/plugins/TabSRMM/src/mim.h
index e3081a18d5..b9d094868a 100644
--- a/plugins/TabSRMM/src/mim.h
+++ b/plugins/TabSRMM/src/mim.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/modplus.cpp b/plugins/TabSRMM/src/modplus.cpp
index 7fbcb934dc..a8daa352ef 100644
--- a/plugins/TabSRMM/src/modplus.cpp
+++ b/plugins/TabSRMM/src/modplus.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdialog.cpp b/plugins/TabSRMM/src/msgdialog.cpp
index dbf57a6c4e..9c8aa11318 100644
--- a/plugins/TabSRMM/src/msgdialog.cpp
+++ b/plugins/TabSRMM/src/msgdialog.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdlgother.cpp b/plugins/TabSRMM/src/msgdlgother.cpp
index edaa0bacbc..db968ba08e 100644
--- a/plugins/TabSRMM/src/msgdlgother.cpp
+++ b/plugins/TabSRMM/src/msgdlgother.cpp
@@ -1,2858 +1,2858 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/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.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// Helper functions for the message dialog.
-
-#include "stdafx.h"
-
-UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM, LPARAM lParam);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// show the balloon tooltip control.
-
-void CMsgDialog::ActivateTooltip(int iCtrlId, const wchar_t *pwszMessage)
-{
- if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd)
- m_pPanel.showTip(iCtrlId, pwszMessage);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::AddLog()
-{
- if (g_plugin.bUseDividers) {
- if (g_plugin.bDividersUsePopupConfig) {
- if (!MessageWindowOpened(0, this))
- DM_AddDivider();
- }
- else {
- if (!IsActive())
- DM_AddDivider();
- else if (m_pContainer->m_hwndActive != m_hwnd)
- DM_AddDivider();
- }
- }
-
- CSrmmBaseDialog::AddLog();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::AdjustBottomAvatarDisplay()
-{
- GetAvatarVisibility();
-
- bool bInfoPanel = m_pPanel.isActive();
- HBITMAP hbm = (bInfoPanel && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
- if (hbm) {
- if (m_dynaSplitter == 0 || m_iSplitterY == 0)
- LoadSplitter();
- m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
- DM_RecalcPictureSize();
- Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
- InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
- }
- else {
- Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
- m_pic.cy = m_pic.cx = DPISCALEY_S(60);
- InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// calculates avatar layouting, based on splitter position to find the optimal size
-// for the avatar w/o disturbing the toolbar too much.
-
-void CMsgDialog::CalcDynamicAvatarSize(BITMAP *bminfo)
-{
- if (m_bWasBackgroundCreate || m_pContainer->cfg.flags.m_bDeferredConfigure || m_pContainer->cfg.flags.m_bCreateMinimized || IsIconic(m_pContainer->m_hwnd))
- return; // at this stage, the layout is not yet ready...
-
- RECT rc;
- GetClientRect(m_hwnd, &rc);
-
- BOOL bBottomToolBar = m_pContainer->cfg.flags.m_bBottomToolbar;
- BOOL bToolBar = m_pContainer->cfg.flags.m_bHideToolbar ? 0 : 1;
- int iSplitOffset = m_bIsAutosizingInput ? 1 : 0;
-
- double picAspect = (bminfo->bmWidth == 0 || bminfo->bmHeight == 0) ? 1.0 : (double)(bminfo->bmWidth / (double)bminfo->bmHeight);
- double picProjectedWidth = (double)((m_dynaSplitter - ((bBottomToolBar && bToolBar) ? DPISCALEX_S(24) : 0) + ((m_bShowUIElements) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect;
-
- if ((rc.right - (int)picProjectedWidth) > (m_iButtonBarReallyNeeds) && !PluginConfig.m_bAlwaysFullToolbarWidth && bToolBar)
- m_iRealAvatarHeight = m_dynaSplitter + 3 + (m_bShowUIElements ? DPISCALEY_S(28) : DPISCALEY_S(2));
- else
- m_iRealAvatarHeight = m_dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset);
-
- m_iRealAvatarHeight -= ((bBottomToolBar && bToolBar) ? DPISCALEY_S(22) : 0);
-
- if (PluginConfig.m_LimitStaticAvatarHeight > 0)
- m_iRealAvatarHeight = min(m_iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight);
-
- if (M.GetByte(m_hContact, "dontscaleavatars", M.GetByte("dontscaleavatars", 0)))
- m_iRealAvatarHeight = min(bminfo->bmHeight, m_iRealAvatarHeight);
-
- double aspect = (bminfo->bmHeight != 0) ? (double)m_iRealAvatarHeight / (double)bminfo->bmHeight : 1.0;
- double newWidth = (double)bminfo->bmWidth * aspect;
- if (newWidth > (double)(rc.right) * 0.8)
- newWidth = (double)(rc.right) * 0.8;
- m_pic.cy = m_iRealAvatarHeight + 2;
- m_pic.cx = (int)newWidth + 2;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::CloseTab()
-{
- int iTabs = TabCtrl_GetItemCount(m_hwndParent);
- if (iTabs == 1) {
- SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 1);
- return;
- }
-
- m_pContainer->m_iChilds--;
- int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd);
-
- // after closing a tab, we need to activate the tab to the left side of
- // the previously open tab.
- // normally, this tab has the same index after the deletion of the formerly active tab
- // unless, of course, we closed the last (rightmost) tab.
- if (!m_pContainer->m_bDontSmartClose && iTabs > 1) {
- if (i == iTabs - 1)
- i--;
- else
- i++;
- TabCtrl_SetCurSel(m_hwndParent, i);
-
- m_pContainer->m_hwndActive = GetTabWindow(m_hwndParent, i);
-
- RECT rc;
- m_pContainer->QueryClientArea(rc);
- SetWindowPos(m_pContainer->m_hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW);
- ShowWindow(m_pContainer->m_hwndActive, SW_SHOW);
- SetForegroundWindow(m_pContainer->m_hwndActive);
- SetFocus(m_pContainer->m_hwndActive);
- }
-
- SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
- DestroyWindow(m_hwnd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// calculate the minimum required client height for the given message
-// window layout
-//
-// the container will use this in its WM_GETMINMAXINFO handler to set
-// minimum tracking height.
-
-void CMsgDialog::DetermineMinHeight()
-{
- RECT rc;
- LONG height = (m_pPanel.isActive() ? m_pPanel.getHeight() + 2 : 0);
- if (!m_pContainer->cfg.flags.m_bHideToolbar)
- height += DPISCALEY_S(24); // toolbar
- GetClientRect(m_message.GetHwnd(), &rc);
- height += rc.bottom; // input area
- height += 40; // min space for log area and some padding
-
- m_pContainer->m_uChildMinHeight = height;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// 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 RtfColorToIndex(int iNumColors, int *pIndex, int iCol)
-{
- for (int i = 0; i < iNumColors; i++)
- if (pIndex[i] == iCol)
- return i;
-
- return -1;
-}
-
-BOOL CMsgDialog::DoRtfToTags(CMStringW &pszText) const
-{
- if (pszText.IsEmpty())
- return FALSE;
-
- // used to filter out attributes which are already set for the default message input area font
- auto &lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
-
- // create an index of colors in the module and map them to
- // corresponding colors in the RTF color table
- int iNumColors = Utils::rtf_clrs.getCount();
- int *pIndex = (int *)_alloca(iNumColors * sizeof(int));
- COLORREF *pColors = (COLORREF *)_alloca(iNumColors * sizeof(COLORREF));
- for (int i = 0; i < iNumColors; i++)
- pColors[i] = Utils::rtf_clrs[i].clr;
- 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;
-
- MODULEINFO *mi = (isChat()) ? m_si->pMI : nullptr;
-
- 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 = RtfColorToIndex(iNumColors, pIndex, iCol);
-
- if (iCol > 0) {
- if (isChat()) {
- if (mi && mi->bColor) {
- if (iInd >= 0) {
- if (!(res.IsEmpty() && m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA] == pColors[iInd]))
- res.AppendFormat(L"%%c%u", iInd);
- }
- else if (!res.IsEmpty())
- res.Append(L"%%C");
- }
- }
- else res.AppendFormat((iInd >= 0) ? (bInsideColor ? L"[/color][color=%s]" : L"[color=%s]") : (bInsideColor ? L"[/color]" : L""), Utils::rtf_clrs[iInd].szName);
- }
-
- bInsideColor = iInd >= 0;
- }
- else if (!wcsncmp(p, L"\\highlight", 10)) { // background color
- if (isChat()) {
- if (mi && mi->bBkgColor) {
- int iInd = RtfColorToIndex(iNumColors, pIndex, _wtoi(p + 10));
- if (iInd >= 0) {
- // if the entry field is empty & the color passed is the back color, skip it
- if (!(res.IsEmpty() && m_pContainer->m_theme.inputbg == pColors[iInd]))
- res.AppendFormat(L"%%f%u", iInd);
- }
- else if (!res.IsEmpty())
- res.AppendFormat(L"%%F");
- }
- }
- }
- 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
- if (isChat()) {
- if (mi && mi->bBold)
- res.Append((p[2] != '0') ? L"%b" : L"%B");
- }
- else {
- if (!(lf.lfWeight == FW_BOLD)) // only allow bold if the font itself isn't a bold one, otherwise just strip it..
- if (m_SendFormat)
- res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
- }
- }
- else if (!wcsncmp(p, L"\\i", 2)) { // italics
- if (isChat()) {
- if (mi && mi->bItalics)
- res.Append((p[2] != '0') ? L"%i" : L"%I");
- }
- else {
- if (!lf.lfItalic && m_SendFormat)
- res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
- }
- }
- else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
- if (!lf.lfStrikeOut && m_SendFormat)
- res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
- }
- else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
- if (isChat()) {
- if (mi && mi->bUnderline)
- res.Append((p[3] != '0') ? L"%u" : L"%U");
- }
- else {
- if (!lf.lfUnderline && m_SendFormat) {
- 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];
-
- if (p[3] != ' ' && p[3] != '\\') {
- wcsncpy(tmp, p + 2, 3);
- tmp[3] = 0;
- }
- else {
- wcsncpy(tmp, p + 2, 2);
- tmp[2] = 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;
-
- case '%': // double % for stupid chat engine
- if (isChat())
- res.Append(L"%%");
- else
- res.AppendChar(*p);
- p++;
- break;
-
- default: // other text that should not be touched
- res.AppendChar(*p++);
- break;
- }
- }
-
- if (bInsideColor && !isChat())
- res.Append(L"[/color]");
- if (bInsideUl)
- res.Append(L"[/u]");
-
- pszText = res;
- return TRUE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::EnableSendButton(bool bMode) const
-{
- SendDlgItemMessage(m_hwnd, IDOK, BUTTONSETASNORMAL, bMode, 0);
- SendDlgItemMessage(m_hwnd, IDC_PIC, BUTTONSETASNORMAL, m_bEditNotesActive ? TRUE : (!bMode && m_iOpenJobs == 0) ? TRUE : FALSE, 0);
-
- HWND hwndOK = GetDlgItem(GetParent(GetParent(m_hwnd)), IDOK);
- if (IsWindow(hwndOK))
- SendMessage(hwndOK, BUTTONSETASNORMAL, bMode, 0);
-}
-
-void CMsgDialog::EnableSending(bool bMode) const
-{
- m_message.SendMsg(EM_SETREADONLY, !bMode, 0);
- Utils::enableDlgControl(m_hwnd, IDC_CLIST, bMode);
- EnableSendButton(bMode);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::FindFirstEvent()
-{
- int historyMode = g_plugin.getByte(m_hContact, SRMSGSET_LOADHISTORY, -1);
- if (historyMode == -1)
- historyMode = (int)g_plugin.getByte(SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY);
-
- m_hDbEventFirst = db_event_firstUnread(m_hContact);
-
- if (m_bActualHistory)
- historyMode = LOADHISTORY_COUNT;
-
- DBEVENTINFO dbei = {};
- DB::ECPTR pCursor(DB::EventsRev(m_hContact, m_hDbEventFirst));
-
- switch (historyMode) {
- case LOADHISTORY_COUNT:
- int i;
-
- // ability to load only current session's history
- if (m_bActualHistory)
- i = m_cache->getSessionMsgCount();
- else
- i = g_plugin.getWord(SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT);
-
- for (; i > 0; i--) {
- MEVENT hPrevEvent = pCursor.FetchNext();
- if (hPrevEvent == 0)
- break;
-
- dbei.cbBlob = 0;
- m_hDbEventFirst = hPrevEvent;
- db_event_get(m_hDbEventFirst, &dbei);
- if (!DbEventIsShown(&dbei))
- i++;
- }
- break;
-
- case LOADHISTORY_TIME:
- if (m_hDbEventFirst == 0)
- dbei.timestamp = time(0);
- else
- db_event_get(m_hDbEventFirst, &dbei);
-
- uint32_t firstTime = dbei.timestamp - 60 * g_plugin.getWord(SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME);
-
- while (MEVENT hPrevEvent = pCursor.FetchNext()) {
- dbei.cbBlob = 0;
- db_event_get(hPrevEvent, &dbei);
- if (dbei.timestamp < firstTime)
- break;
- m_hDbEventFirst = hPrevEvent;
- }
- break;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// returns != 0 when one of the installed keyboard layouts belongs to an rtl language
-// used to find out whether we need to configure the message input box for bidirectional mode
-
-int CMsgDialog::FindRTLLocale()
-{
- int result = 0;
-
- if (m_iHaveRTLLang == 0) {
- HKL layouts[20];
- memset(layouts, 0, sizeof(layouts));
- GetKeyboardLayoutList(20, layouts);
- for (int i = 0; i < 20 && layouts[i]; i++) {
- uint16_t wCtype2[5];
- LCID lcid = MAKELCID(LOWORD(layouts[i]), 0);
- GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2);
- if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT)
- result = 1;
- }
- m_iHaveRTLLang = (result ? 1 : -1);
- }
- else result = m_iHaveRTLLang == 1 ? 1 : 0;
-
- return result;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::FlashOnClist(MEVENT hEvent, DBEVENTINFO *dbei)
-{
- m_dwTickLastEvent = GetTickCount();
-
- if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) {
- m_dwUnread++;
- AddUnreadContact(m_hContact);
- }
-
- if (hEvent == 0)
- return;
-
- if (!g_plugin.bFlashOnClist)
- return;
-
- if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !m_bFlashClist) {
- CLISTEVENT cle = {};
- cle.hContact = m_hContact;
- cle.hDbEvent = hEvent;
- cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
- cle.pszService = MS_MSG_READMESSAGE;
- g_clistApi.pfnAddEvent(&cle);
-
- m_bFlashClist = true;
- m_hFlashingEvent = hEvent;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// flash a tab icon if mode = true, otherwise restore default icon
-// store flashing state into bState
-
-void CMsgDialog::FlashTab(bool bInvertMode)
-{
- if (bInvertMode)
- m_bTabFlash = !m_bTabFlash;
-
- TCITEM item = {};
- item.mask = TCIF_IMAGE;
- TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
- if (m_pContainer->cfg.flags.m_bSideBar)
- m_pContainer->m_pSideBar->updateSession(this);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// this translates formatting tags into rtf sequences...
-// flags: loword = words only for simple * /_ formatting
-// hiword = bbcode support (strip bbcodes if 0)
-
-static wchar_t *w_bbcodes_begin[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[color=" };
-static wchar_t *w_bbcodes_end[] = { L"[/b]", L"[/i]", L"[/u]", L"[/s]", L"[/color]" };
-
-static wchar_t *formatting_strings_begin[] = { L"b1 ", L"i1 ", L"u1 ", L"s1 ", L"c1 " };
-static wchar_t *formatting_strings_end[] = { L"b0 ", L"i0 ", L"u0 ", L"s0 ", L"c0 " };
-
-void CMsgDialog::FormatRaw(CMStringW &msg, int flags, bool isSent)
-{
- bool clr_was_added = false;
- int beginmark = 0, endmark = 0, tempmark = 0;
- int i, endindex;
-
- if (m_dwFlags & MWF_LOG_BBCODE) {
- beginmark = 0;
- while (true) {
- for (i = 0; i < _countof(w_bbcodes_begin); i++)
- if ((tempmark = msg.Find(w_bbcodes_begin[i], 0)) != -1)
- break;
-
- if (i >= _countof(w_bbcodes_begin))
- break;
-
- beginmark = tempmark;
- endindex = i;
- endmark = msg.Find(w_bbcodes_end[i], beginmark);
- if (endindex == 4) { // color
- int closing = msg.Find(L"]", beginmark);
- bool was_added = false;
-
- if (closing == -1) { // must be an invalid [color=] tag w/o closing bracket
- msg.SetAt(beginmark, ' ');
- continue;
- }
-
- CMStringW colorname = msg.Mid(beginmark + 7, closing - beginmark - 7);
-search_again:
- bool clr_found = false;
- for (int ii = 0; ii < Utils::rtf_clrs.getCount(); ii++) {
- auto &rtfc = Utils::rtf_clrs[ii];
- if (!wcsicmp(colorname, rtfc.szName)) {
- closing = beginmark + 7 + (int)mir_wstrlen(rtfc.szName);
- if (endmark != -1) {
- msg.Delete(endmark, 8);
- msg.Insert(endmark, L"c0 ");
- }
- msg.Delete(beginmark, closing - beginmark + 1);
-
- wchar_t szTemp[5];
- msg.Insert(beginmark, L"cxxx ");
- mir_snwprintf(szTemp, L"%02d", MSGDLGFONTCOUNT + 13 + ii);
- msg.SetAt(beginmark + 3, szTemp[0]);
- msg.SetAt(beginmark + 4, szTemp[1]);
- clr_found = true;
- if (was_added) {
- wchar_t wszTemp[100];
- mir_snwprintf(wszTemp, L"##col##%06u:%04u", endmark - closing, ii);
- wszTemp[99] = 0;
- msg.Insert(beginmark, wszTemp);
- }
- break;
- }
- }
- if (!clr_found) {
- int c_closing = colorname.Find(L"]");
- if (c_closing == -1)
- c_closing = colorname.GetLength();
- const wchar_t *wszColname = colorname.c_str();
- if (endmark != -1 && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing - 1])) {
- Utils::RTF_ColorAdd(wszColname);
- if (!was_added) {
- clr_was_added = was_added = true;
- goto search_again;
- }
- else goto invalid_code;
- }
- else {
-invalid_code:
- if (endmark != -1)
- msg.Delete(endmark, 8);
- if (closing != -1 && closing < endmark)
- msg.Delete(beginmark, (closing - beginmark) + 1);
- else
- msg.SetAt(beginmark, ' ');
- }
- }
- continue;
- }
-
- if (endmark != -1) {
- msg.Delete(endmark, 4);
- msg.Insert(endmark, formatting_strings_end[i]);
- }
- msg.Delete(beginmark, 3);
- msg.Insert(beginmark, L" ");
- msg.Insert(beginmark, formatting_strings_begin[i]);
- }
- }
-
- if ((m_dwFlags & MWF_LOG_TEXTFORMAT) && msg.Find(L"://") == -1) {
- while ((beginmark = msg.Find(L"*/_", beginmark)) != -1) {
- wchar_t endmarker = msg[beginmark];
- if (LOWORD(flags)) {
- if (beginmark > 0 && !iswspace(msg[beginmark - 1]) && !iswpunct(msg[beginmark - 1])) {
- beginmark++;
- continue;
- }
-
- // search a corresponding endmarker which fulfills the criteria
- INT_PTR mark = beginmark + 1;
- while ((endmark = msg.Find(endmarker, mark)) != -1) {
- if (iswpunct(msg[endmark + 1]) || iswspace(msg[endmark + 1]) || msg[endmark + 1] == 0 || wcschr(L"*/_", msg[endmark + 1]) != nullptr)
- goto ok;
- mark = endmark + 1;
- }
- break;
- }
- else {
- if ((endmark = msg.Find(endmarker, beginmark + 1)) == -1)
- break;
- }
-ok:
- if ((endmark - beginmark) < 2) {
- beginmark++;
- continue;
- }
-
- int index = 0;
- switch (endmarker) {
- case '*':
- index = 0;
- break;
- case '/':
- index = 1;
- break;
- case '_':
- index = 2;
- break;
- }
-
- // check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if
- // it really is one.
- if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) {
- CMStringW smcode = msg.Mid(beginmark, (endmark - beginmark) + 1);
-
- SMADD_BATCHPARSE2 smbp = {};
- smbp.cbSize = sizeof(smbp);
- smbp.Protocolname = m_cache->getActiveProto();
- smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0);
- smbp.str = (wchar_t*)smcode.c_str();
- smbp.hContact = m_hContact;
-
- SMADD_BATCHPARSERES *smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&smbp);
- if (smbpr) {
- CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr);
- beginmark = endmark + 1;
- continue;
- }
- }
- msg.Delete(endmark, 1);
- msg.Insert(endmark, formatting_strings_end[index]);
- msg.Delete(beginmark, 1);
- msg.Insert(beginmark, formatting_strings_begin[index]);
- }
- }
-
- m_bClrAdded = clr_was_added;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// format the title bar string for IM chat sessions using placeholders.
-// the caller must mir_free() the returned string
-
-static wchar_t* Trunc500(wchar_t *str)
-{
- if (mir_wstrlen(str) > 500)
- str[500] = 0;
- return str;
-}
-
-bool CMsgDialog::FormatTitleBar(const wchar_t *szFormat, CMStringW &dest)
-{
- for (const wchar_t *src = szFormat; *src; src++) {
- if (*src != '%') {
- dest.AppendChar(*src);
- continue;
- }
-
- switch (*++src) {
- case 'n':
- dest.Append(m_cache->getNick());
- break;
-
- case 'p':
- case 'a':
- dest.Append(m_cache->getRealAccount());
- break;
-
- case 's':
- dest.Append(m_wszStatus);
- break;
-
- case 'u':
- dest.Append(m_cache->getUIN());
- break;
-
- case 'c':
- dest.Append(!mir_wstrcmp(m_pContainer->m_wszName, L"default") ? TranslateT("Default container") : m_pContainer->m_wszName);
- break;
-
- case 'o':
- {
- const char *szProto = m_cache->getActiveProto();
- if (szProto)
- dest.Append(_A2T(szProto));
- }
- break;
-
- case 'x':
- {
- uint8_t xStatus = m_cache->getXStatusId();
- if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
- ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
- dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
- }
- }
- break;
-
- case 'm':
- {
- uint8_t xStatus = m_cache->getXStatusId();
- if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
- ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
- dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
- }
- else dest.Append(m_wszStatus[0] ? m_wszStatus : L"(undef)");
- }
- break;
-
- // status message (%T will skip the "No status message" for empty messages)
- case 't':
- case 'T':
- {
- ptrW tszStatus(m_cache->getNormalizedStatusMsg(m_cache->getStatusMsg(), true));
- if (tszStatus)
- dest.Append(tszStatus);
- else if (*src == 't')
- dest.Append(TranslateT("No status message"));
- }
- break;
-
- case 'g':
- {
- ptrW tszGroup(Clist_GetGroup(m_hContact));
- if (tszGroup != nullptr)
- dest.Append(tszGroup);
- }
- break;
-
- case 0: // wrongly formed format string
- return true;
- }
- }
-
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve the visiblity of the avatar window, depending on the global setting
-// and local mode
-
-bool CMsgDialog::GetAvatarVisibility()
-{
- uint8_t bAvatarMode = m_pContainer->cfg.avatarMode;
- uint8_t bOwnAvatarMode = m_pContainer->cfg.ownAvatarMode;
- char hideOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
-
- // infopanel visible, consider own avatar display
- m_bShowAvatar = false;
- if (m_si)
- bAvatarMode = 1;
-
- if (m_pPanel.isActive() && bAvatarMode != 3) {
- if (!bOwnAvatarMode) {
- m_bShowAvatar = (m_hOwnPic && m_hOwnPic != PluginConfig.g_hbmUnknown);
- if (!m_hwndContactPic)
- m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
- }
-
- switch (bAvatarMode) {
- case 2:
- m_bShowInfoAvatar = false;
- break;
- case 0:
- m_bShowInfoAvatar = true;
- case 1:
- HBITMAP hbm = ((m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr);
- if (hbm == nullptr && !bAvatarMode) {
- m_bShowInfoAvatar = false;
- break;
- }
-
- if (!m_hwndPanelPic) {
- m_hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, m_hwndPanelPicParent, (HMENU)7000, nullptr, nullptr);
- if (m_hwndPanelPic)
- SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE);
- }
-
- if (bAvatarMode != 0)
- m_bShowInfoAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
- break;
- }
-
- if (m_bShowInfoAvatar)
- m_bShowInfoAvatar = hideOverride == 0 ? false : m_bShowInfoAvatar;
- else
- m_bShowInfoAvatar = hideOverride == 1 ? true : m_bShowInfoAvatar;
-
- Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
- SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
- }
- else {
- m_bShowInfoAvatar = false;
-
- switch (bAvatarMode) {
- case 0: // globally on
- m_bShowAvatar = true;
- LBL_Check:
- if (!m_hwndContactPic)
- m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
- break;
- case 2: // globally OFF
- m_bShowAvatar = false;
- break;
- case 3: // on, if present
- case 1:
- HBITMAP hbm = (m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr;
- m_bShowAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
- goto LBL_Check;
- }
-
- if (m_bShowAvatar)
- m_bShowAvatar = hideOverride == 0 ? 0 : m_bShowAvatar;
- else
- m_bShowAvatar = hideOverride == 1 ? 1 : m_bShowAvatar;
-
- // reloads avatars
- if (m_hwndPanelPic) { // shows contact or user picture, depending on panel visibility
- SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
- Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
- }
- else Utils::setAvatarContact(m_hwndContactPic, m_hContact);
- }
- return m_bShowAvatar;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::GetClientIcon()
-{
- if (m_hClientIcon)
- DestroyIcon(m_hClientIcon);
-
- m_hClientIcon = nullptr;
- if (ServiceExists(MS_FP_GETCLIENTICONT)) {
- ptrW tszMirver(db_get_wsa(m_cache->getActiveContact(), m_cache->getActiveProto(), "MirVer"));
- if (tszMirver)
- m_hClientIcon = Finger_GetClientIcon(tszMirver, 1);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-HICON CMsgDialog::GetMyContactIcon(const CMOption<bool> *opt)
-{
- int bUseMeta = (opt == nullptr) ? false : M.GetByte(opt->GetDBSettingName(), mir_strcmp(opt->GetDBSettingName(), "MetaiconTab") == 0);
- if (bUseMeta)
- return Skin_LoadProtoIcon(m_cache->getProto(), m_cache->getStatus());
- return Skin_LoadProtoIcon(m_cache->getActiveProto(), m_cache->getActiveStatus());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::GetMyNick()
-{
- ptrW tszNick(Contact::GetInfo(CNF_CUSTOMNICK, 0, m_cache->getActiveProto()));
- if (tszNick == nullptr)
- tszNick = Contact::GetInfo(CNF_NICK, 0, m_cache->getActiveProto());
- if (tszNick != nullptr) {
- if (mir_wstrlen(tszNick) == 0 || !mir_wstrcmp(tszNick, TranslateT("'(Unknown contact)'")))
- wcsncpy_s(m_wszMyNickname, (m_myUin[0] ? m_myUin : TranslateT("'(Unknown contact)'")), _TRUNCATE);
- else
- wcsncpy_s(m_wszMyNickname, tszNick, _TRUNCATE);
- }
- else wcsncpy_s(m_wszMyNickname, L"<undef>", _TRUNCATE); // same here
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve both buddys and my own UIN for a message session and store them in the message
-// window *dat respects metacontacts and uses the current protocol if the contact is a MC
-
-void CMsgDialog::GetMYUIN()
-{
- ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, 0, m_cache->getActiveProto()));
- if (uid != nullptr)
- wcsncpy_s(m_myUin, uid, _TRUNCATE);
- else
- m_myUin[0] = 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// returns the status of Send button
-
-LRESULT CMsgDialog::GetSendButtonState()
-{
- return m_btnOk.SendMsg(BUTTONGETSTATEID, TRUE, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// reads send format and configures the toolbar buttons
-// if mode == 0, int only configures the buttons and does not change send format
-
-void CMsgDialog::GetSendFormat()
-{
- m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
- if (m_SendFormat == -1) // per contact override to disable it..
- m_SendFormat = 0;
- else if (m_SendFormat == 0)
- m_SendFormat = g_plugin.bSendFormat;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-HICON CMsgDialog::GetXStatusIcon() const
-{
- uint8_t xStatus = m_cache->getXStatusId();
- if (xStatus == 0)
- return nullptr;
-
- if (!ProtoServiceExists(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON))
- return nullptr;
-
- return (HICON)(CallProtoService(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON, xStatus, 0));
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// paste contents of the clipboard into the message input area and send it immediately
-
-void CMsgDialog::HandlePasteAndSend()
-{
- // is feature disabled?
- if (!g_plugin.bPasteAndSend) {
- ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending messages' section"));
- return;
- }
-
- m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0);
- if (GetWindowTextLength(m_message.GetHwnd()) > 0)
- SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// convert the avatar bitmap to icon format so that it can be used on the task bar
-// tries to keep correct aspect ratio of the avatar image
-//
-// @param dat: _MessageWindowData* pointer to the window data
-// @return HICON: the icon handle
-
-HICON CMsgDialog::IconFromAvatar() const
-{
- if (!ServiceExists(MS_AV_GETAVATARBITMAP))
- return nullptr;
-
- AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, m_hContact, 0);
- if (ace == nullptr || ace->hbmPic == nullptr)
- return nullptr;
-
- LONG lIconSize = Win7Taskbar->getIconSize();
- double dNewWidth, dNewHeight;
- Utils::scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize);
-
- // resize picture to fit it on the task bar, use an image list for converting it to
- // 32bpp icon format. hTaskbarIcon will cache it until avatar is changed
- HBITMAP hbmResized = ::Image_Resize(ace->hbmPic, RESIZEBITMAP_STRETCH, dNewWidth, dNewHeight);
- HIMAGELIST hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0);
-
- RECT rc = { 0, 0, lIconSize, lIconSize };
-
- HDC hdc = ::GetDC(m_pContainer->m_hwnd);
- HDC dc = ::CreateCompatibleDC(hdc);
- HDC dcResized = ::CreateCompatibleDC(hdc);
-
- ReleaseDC(m_pContainer->m_hwnd, hdc);
-
- HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc);
- HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew));
- HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized));
-
- LONG ix = (lIconSize - (LONG)dNewWidth) / 2;
- LONG iy = (lIconSize - (LONG)dNewHeight) / 2;
- CSkin::m_default_bf.SourceConstantAlpha = M.GetByte("taskBarIconAlpha", 255);
- GdiAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
-
- CSkin::m_default_bf.SourceConstantAlpha = 255;
- ::SelectObject(dc, hbmOld);
- ::ImageList_Add(hIml_c, hbmNew, nullptr);
- ::DeleteObject(hbmNew);
- ::DeleteDC(dc);
-
- ::SelectObject(dcResized, hbmOldResized);
- if (hbmResized != ace->hbmPic)
- ::DeleteObject(hbmResized);
- ::DeleteDC(dcResized);
- HICON hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL);
- ::ImageList_RemoveAll(hIml_c);
- ::ImageList_Destroy(hIml_c);
- return hIcon;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// is window active or not?
-
-bool CMsgDialog::IsActive() const
-{
- return m_pContainer->IsActive() && m_pContainer->m_hwndActive == m_hwnd;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// read keyboard state and return the state of the modifier keys
-
-void CMsgDialog::KbdState(bool &isShift, bool &isControl, bool &isAlt)
-{
- GetKeyboardState(kstate);
- isShift = (kstate[VK_SHIFT] & 0x80) != 0;
- isControl = (kstate[VK_CONTROL] & 0x80) != 0;
- isAlt = (kstate[VK_MENU] & 0x80) != 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LimitMessageText(int iLen)
-{
- if (this != nullptr)
- m_message.SendMsg(EM_EXLIMITTEXT, 0, iLen);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadContactAvatar()
-{
- m_ace = Utils::loadAvatarFromAVS(m_bIsMeta ? db_mc_getSrmmSub(m_hContact) : m_hContact);
-
- BITMAP bm;
- if (m_ace && m_ace->hbmPic)
- GetObject(m_ace->hbmPic, sizeof(bm), &bm);
- else if (m_ace == nullptr)
- GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm);
- else
- return;
-
- AdjustBottomAvatarDisplay();
- CalcDynamicAvatarSize(&bm);
-
- if (!m_pPanel.isActive() || m_pContainer->cfg.avatarMode == 3) {
- m_iRealAvatarHeight = 0;
- PostMessage(m_hwnd, WM_SIZE, 0, 0);
- }
- else if (m_pPanel.isActive())
- GetAvatarVisibility();
-
- if (m_pWnd != nullptr)
- m_pWnd->verifyDwmState();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadOwnAvatar()
-{
- if (ServiceExists(MS_AV_GETMYAVATAR))
- m_ownAce = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(m_cache->getActiveProto()));
- else
- m_ownAce = nullptr;
-
- if (m_ownAce)
- m_hOwnPic = m_ownAce->hbmPic;
- else
- m_hOwnPic = PluginConfig.g_hbmUnknown;
-
- if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
- BITMAP bm;
-
- m_iRealAvatarHeight = 0;
- AdjustBottomAvatarDisplay();
- GetObject(m_hOwnPic, sizeof(bm), &bm);
- CalcDynamicAvatarSize(&bm);
- Resize();
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadSettings()
-{
- m_clrInputBG = m_pContainer->m_theme.inputbg;
- LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadSplitter()
-{
- if (m_bIsAutosizingInput) {
- m_iSplitterY = (m_pContainer->cfg.flags.m_bBottomToolbar) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46);
-
- if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
- m_iSplitterY += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2);
- return;
- }
-
- if (!m_bSplitterOverride) {
- if (!m_pContainer->cfg.fPrivate)
- m_iSplitterY = (int)M.GetDword("splitsplity", 60);
- else
- m_iSplitterY = m_pContainer->cfg.iSplitterY;
- }
- else m_iSplitterY = (int)M.GetDword(m_hContact, "splitsplity", M.GetDword("splitsplity", 60));
-
- if (m_iSplitterY < MINSPLITTERY)
- m_iSplitterY = 150;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LogEvent(DBEVENTINFO &dbei)
-{
- if (m_iLogMode != WANT_BUILTIN_LOG) {
- dbei.flags |= DBEF_TEMPORARY;
-
- MEVENT hDbEvent = db_event_add(m_hContact, &dbei);
- if (hDbEvent) {
- m_pLog->LogEvents(hDbEvent, 1, true);
- db_event_delete(hDbEvent);
- }
- }
- else LOG()->LogEvents(0, 1, true, &dbei);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// draw various elements of the message window, like avatar(s), info panel fields
-// and the color formatting menu
-
-int CMsgDialog::MsgWindowDrawHandler(DRAWITEMSTRUCT *dis)
-{
- if ((dis->hwndItem == GetDlgItem(m_hwnd, IDC_CONTACTPIC) && m_bShowAvatar) || (dis->hwndItem == m_hwnd && m_pPanel.isActive())) {
- HBITMAP hbmAvatar = m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown;
- if (hbmAvatar == nullptr)
- return TRUE;
-
- int top, cx, cy;
- RECT rcClient, rcFrame;
- bool bPanelPic = (dis->hwndItem == m_hwnd);
- if (bPanelPic && !m_bShowInfoAvatar)
- return TRUE;
-
- RECT rc;
- GetClientRect(m_hwnd, &rc);
- if (bPanelPic) {
- rcClient = dis->rcItem;
- cx = (rcClient.right - rcClient.left);
- cy = (rcClient.bottom - rcClient.top) + 1;
- }
- else {
- GetClientRect(dis->hwndItem, &rcClient);
- cx = rcClient.right;
- cy = rcClient.bottom;
- }
-
- if (cx < 5 || cy < 5)
- return TRUE;
-
- HDC hdcDraw = CreateCompatibleDC(dis->hDC);
- HBITMAP hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy);
- HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw);
-
- bool bAero = M.isAero();
-
- HRGN clipRgn = nullptr;
- HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcDraw, bAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE));
- rcFrame = rcClient;
-
- if (!bPanelPic) {
- top = (cy - m_pic.cy) / 2;
- RECT rcEdge = { 0, top, m_pic.cx, top + m_pic.cy };
- if (CSkin::m_skinEnabled)
- CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, hdcDraw);
- else if (PluginConfig.m_fillColor) {
- HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor);
- FillRect(hdcDraw, &rcFrame, br);
- DeleteObject(br);
- }
- else if (bAero && CSkin::m_pCurrentAeroEffect) {
- COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh :
- (CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0);
-
- HBRUSH br = CreateSolidBrush(clr);
- FillRect(hdcDraw, &rcFrame, br);
- DeleteObject(br);
- }
- else FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE));
-
- HPEN hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr);
- HPEN hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder);
-
- if (CSkin::m_bAvatarBorderType == 1)
- Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom);
- else if (CSkin::m_bAvatarBorderType == 2) {
- clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, 6, 6);
- SelectClipRgn(hdcDraw, clipRgn);
-
- HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
- FrameRgn(hdcDraw, clipRgn, hbr, 1, 1);
- DeleteObject(hbr);
- DeleteObject(clipRgn);
- }
-
- SelectObject(hdcDraw, hPenOld);
- DeleteObject(hPenBorder);
- }
-
- if (bPanelPic) {
- bool bBorder = (CSkin::m_bAvatarBorderType ? true : false);
-
- int border_off = bBorder ? 1 : 0;
- int iMaxHeight = m_iPanelAvatarY - (bBorder ? 2 : 0);
- int iMaxWidth = m_iPanelAvatarX - (bBorder ? 2 : 0);
-
- rcFrame.left = rcFrame.top = 0;
- rcFrame.right = (rcClient.right - rcClient.left);
- rcFrame.bottom = (rcClient.bottom - rcClient.top);
-
- rcFrame.left = rcFrame.right - (LONG)m_iPanelAvatarX;
- rcFrame.bottom = (LONG)m_iPanelAvatarY;
-
- int height_off = (cy - iMaxHeight - (bBorder ? 2 : 0)) / 2;
- rcFrame.top += height_off;
- rcFrame.bottom += height_off;
-
- SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, bAero ? TRUE : FALSE);
- SetWindowPos(m_hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off,
- iMaxWidth, iMaxHeight, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING);
- }
-
- SelectObject(hdcDraw, hOldBrush);
- if (!bPanelPic)
- BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY);
- SelectObject(hdcDraw, hbmOld);
- DeleteObject(hbmDraw);
- DeleteDC(hdcDraw);
- return TRUE;
- }
-
- if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(m_hwnd, IDC_LOGFROZENTEXT)) {
- wchar_t szWindowText[256];
- if (CSkin::m_skinEnabled) {
- SetTextColor(dis->hDC, CSkin::m_DefaultFontColor);
- CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
- }
- else {
- SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
- CSkin::FillBack(dis->hDC, &dis->rcItem);
- }
- GetWindowText(dis->hwndItem, szWindowText, _countof(szWindowText));
- szWindowText[255] = 0;
- SetBkMode(dis->hDC, TRANSPARENT);
- DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS);
- return TRUE;
- }
-
- if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICERRORICON)) {
- if (CSkin::m_skinEnabled)
- CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
- else
- CSkin::FillBack(dis->hDC, &dis->rcItem);
- DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8,
- PluginConfig.g_iconErr, 16, 16, 0, nullptr, DI_NORMAL);
- return TRUE;
- }
-
- if (dis->CtlType == ODT_MENU && m_pPanel.isHovered()) {
- DrawMenuItem(dis, (HICON)dis->itemData, 0);
- return TRUE;
- }
-
- return Menu_DrawItem((LPARAM)dis);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMsgDialog::MsgWindowUpdateMenu(HMENU submenu, int menuID)
-{
- bool bInfoPanel = m_pPanel.isActive();
-
- if (menuID == MENU_TABCONTEXT) {
- EnableMenuItem(submenu, ID_TABMENU_LEAVECHATROOM, (isChat() && ProtoServiceExists(m_szProto, PS_LEAVECHAT)) ? MF_ENABLED : MF_GRAYED);
- EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) ? MF_GRAYED : MF_ENABLED);
- EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M.GetDword(m_hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED);
- }
- else if (menuID == MENU_PICMENU) {
- wchar_t *szText = nullptr;
- char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
- HMENU visMenu = GetSubMenu(submenu, 0);
- BOOL picValid = bInfoPanel ? (m_hOwnPic != nullptr) : (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown);
-
- MENUITEMINFO mii = { 0 };
- mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_STRING;
-
- EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, picValid ? MF_ENABLED : MF_GRAYED);
-
- CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
-
- CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, PluginConfig.m_bAlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED);
- if (!bInfoPanel) {
- EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
- szText = TranslateT("Contact picture settings...");
- EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED);
- }
- else {
- EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED);
- EnableMenuItem(submenu, ID_PICMENU_SETTINGS, (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED);
- szText = TranslateT("Set your avatar...");
- }
- mii.dwTypeData = szText;
- mii.cch = (int)mir_wstrlen(szText) + 1;
- SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii);
- }
- else if (menuID == MENU_PANELPICMENU) {
- HMENU visMenu = GetSubMenu(submenu, 0);
- char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
-
- CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
- CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
-
- EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
- EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED);
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update state of the container - this is called whenever a tab becomes active, no matter how and
-// deals with various things like updating the title bar, removing flashing icons, updating the
-// session list, switching the keyboard layout (autolocale active) and the general container status.
-//
-// it protects itself from being called more than once per session activation and is valid for
-// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c)
-
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMsgDialog::MsgWindowMenuHandler(int selection, int menuId)
-{
- if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) {
- switch (selection) {
- case ID_TABMENU_ATTACHTOCONTAINER:
- SelectContainer();
- return 1;
- case ID_TABMENU_CONTAINEROPTIONS:
- m_pContainer->OptionsDialog();
- return 1;
- case ID_TABMENU_CLOSECONTAINER:
- SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 0);
- return 1;
- case ID_TABMENU_CLOSETAB:
- PostMessage(m_hwnd, WM_CLOSE, 1, 0);
- return 1;
- case ID_TABMENU_SAVETABPOSITION:
- db_set_dw(m_hContact, SRMSGMOD_T, "tabindex", m_iTabID * 100);
- break;
- case ID_TABMENU_CLEARSAVEDTABPOSITION:
- db_unset(m_hContact, SRMSGMOD_T, "tabindex");
- break;
- case ID_TABMENU_LEAVECHATROOM:
- if (isChat()) {
- char *szProto = Proto_GetBaseAccountName(m_hContact);
- if (szProto)
- CallProtoService(szProto, PS_LEAVECHAT, m_hContact, 0);
- }
- return 1;
-
- case ID_VISIBILITY_DEFAULT:
- case ID_VISIBILITY_HIDDENFORTHISCONTACT:
- case ID_VISIBILITY_VISIBLEFORTHISCONTACT:
- {
- uint8_t avOverrideMode;
- if (selection == ID_VISIBILITY_DEFAULT)
- avOverrideMode = -1;
- else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT)
- avOverrideMode = 1;
- else
- avOverrideMode = 0;
- db_set_b(m_hContact, SRMSGMOD_T, "hideavatar", avOverrideMode);
- }
-
- ShowPicture(false);
- Resize();
- DM_ScrollToBottom(0, 1);
- return 1;
-
- case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH:
- PluginConfig.m_bAlwaysFullToolbarWidth = !PluginConfig.m_bAlwaysFullToolbarWidth;
- db_set_b(0, SRMSGMOD_T, "alwaysfulltoolbar", (uint8_t)PluginConfig.m_bAlwaysFullToolbarWidth);
- Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
- break;
-
- case ID_PICMENU_SAVETHISPICTUREAS:
- if (m_pPanel.isActive())
- SaveAvatarToFile(m_hOwnPic, 1);
- else if (m_ace)
- SaveAvatarToFile(m_ace->hbmPic, 0);
- break;
-
- case ID_PANELPICMENU_SAVETHISPICTUREAS:
- if (m_ace)
- SaveAvatarToFile(m_ace->hbmPic, 0);
- break;
-
- case ID_PICMENU_SETTINGS:
- if (menuId == MENU_PICMENU) {
- if (m_pPanel.isActive()) {
- if (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0))
- CallService(MS_AV_SETMYAVATARW, (WPARAM)(m_cache->getActiveProto()), 0);
- return TRUE;
- }
- }
- CallService(MS_AV_CONTACTOPTIONS, m_hContact, (LPARAM)m_hwnd);
- return 1;
- }
- }
- else if (menuId == MENU_LOGMENU) {
- switch (selection) {
- case ID_MESSAGELOGSETTINGS_GLOBAL:
- g_plugin.openOptions(nullptr, L"Message sessions", L"Message log");
- return 1;
-
- case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
- CallService(MS_TABMSG_SETUSERPREFS, m_hContact, 0);
- return 1;
- }
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::NotifyDeliveryFailure() const
-{
- if (!g_plugin.bErrorPopup || !Popup_Enabled())
- return;
-
- POPUPDATAW ppd = {};
- ppd.lchContact = m_hContact;
- ppd.PluginWindowProc = Utils::PopupDlgProcError;
- ppd.lchIcon = PluginConfig.g_iconErr;
- ppd.iSeconds = NEN::iDelayErr;
- if (!NEN::bColDefaultErr) {
- ppd.colorText = NEN::colTextErr;
- ppd.colorBack = NEN::colBackErr;
- }
- wcsncpy_s(ppd.lpwzContactName, m_cache->getNick(), _TRUNCATE);
- wcsncpy_s(ppd.lpwzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE);
- PUAddPopupW(&ppd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::PlayIncomingSound() const
-{
- int iPlay = m_pContainer->MustPlaySound(this);
- if (iPlay) {
- if (GetForegroundWindow() == m_pContainer->m_hwnd && m_pContainer->m_hwndActive == m_hwnd)
- Skin_PlaySound("RecvMsgActive");
- else
- Skin_PlaySound("RecvMsgInactive");
- }
-}
-
-void CMsgDialog::RemakeLog()
-{
- m_szMicroLf[0] = 0;
- m_lastEventTime = 0;
- m_iLastEventType = -1;
- StreamEvents(m_hDbEventFirst, -1, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// saves a contact picture to disk
-// takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar)
-// requires AVS service (Miranda 0.7+)
-
-void CMsgDialog::SaveAvatarToFile(HBITMAP hbm, int isOwnPic)
-{
- wchar_t szFinalFilename[MAX_PATH];
- time_t t = time(0);
- struct tm *lt = localtime(&t);
- uint32_t setView = 1;
-
- wchar_t szTimestamp[100];
- mir_snwprintf(szTimestamp, L"%04u %02u %02u_%02u%02u", lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min);
-
- wchar_t *szProto = mir_a2u(m_cache->getActiveProto());
-
- wchar_t szFinalPath[MAX_PATH];
- mir_snwprintf(szFinalPath, L"%s\\%s", M.getSavedAvatarPath(), szProto);
- mir_free(szProto);
-
- if (CreateDirectory(szFinalPath, nullptr) == 0) {
- if (GetLastError() != ERROR_ALREADY_EXISTS) {
- MessageBox(nullptr, TranslateT("Error creating destination directory"),
- TranslateT("Save contact picture"), MB_OK | MB_ICONSTOP);
- return;
- }
- }
-
- wchar_t szBaseName[MAX_PATH];
- if (isOwnPic)
- mir_snwprintf(szBaseName, L"My Avatar_%s", szTimestamp);
- else
- mir_snwprintf(szBaseName, L"%s_%s", m_cache->getNick(), szTimestamp);
-
- mir_snwprintf(szFinalFilename, L"%s.png", szBaseName);
-
- // do not allow / or \ or % in the filename
- Utils::sanitizeFilename(szFinalFilename);
-
- wchar_t filter[MAX_PATH];
- mir_snwprintf(filter, L"%s%c*.bmp;*.png;*.jpg;*.gif%c%c", TranslateT("Image files"), 0, 0, 0);
-
- OPENFILENAME ofn = { 0 };
- ofn.lpstrDefExt = L"png";
- ofn.lpstrFilter = filter;
- ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
- ofn.lpfnHook = OpenFileSubclass;
- ofn.lStructSize = sizeof(ofn);
- ofn.lpstrFile = szFinalFilename;
- ofn.lpstrInitialDir = szFinalPath;
- ofn.nMaxFile = MAX_PATH;
- ofn.nMaxFileTitle = MAX_PATH;
- ofn.lCustData = (LPARAM)& setView;
- if (GetSaveFileName(&ofn)) {
- if (PathFileExists(szFinalFilename))
- if (MessageBox(nullptr, TranslateT("The file exists. Do you want to overwrite it?"), TranslateT("Save contact picture"), MB_YESNO | MB_ICONQUESTION) == IDNO)
- return;
-
- IMGSRVC_INFO ii;
- ii.cbSize = sizeof(ii);
- ii.pwszName = szFinalFilename;
- ii.hbm = hbm;
- ii.dwMask = IMGI_HBITMAP;
- ii.fif = FIF_UNKNOWN; // get the format from the filename extension. png is default.
- Image_Save(&ii);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::SaveSplitter()
-{
- if (m_bIsAutosizingInput)
- return;
-
- if (m_iSplitterY < DPISCALEY_S(MINSPLITTERY) || m_iSplitterY < 0)
- m_iSplitterY = DPISCALEY_S(MINSPLITTERY);
-
- if (m_bSplitterOverride)
- db_set_dw(m_hContact, SRMSGMOD_T, "splitsplity", m_iSplitterY);
- else {
- if (m_pContainer->cfg.fPrivate)
- m_pContainer->cfg.iSplitterY = m_iSplitterY;
- else
- db_set_dw(0, SRMSGMOD_T, "splitsplity", m_iSplitterY);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// send a pasted bitmap by file transfer.
-
-static LIST<wchar_t> vTempFilenames(5);
-
-void CMsgDialog::SendHBitmapAsFile(HBITMAP hbmp) const
-{
- const wchar_t *mirandatempdir = L"Miranda";
- const wchar_t *filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg";
- wchar_t filename[MAX_PATH];
- size_t tempdirlen = GetTempPath(MAX_PATH, filename);
- bool fSend = true;
-
- const char *szProto = m_cache->getActiveProto();
- int wMyStatus = Proto_GetStatus(szProto);
-
- uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
- uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
-
- // check protocol capabilities, status modes and visibility lists (privacy)
- // to determine whether the file can be sent. Throw a warning if any of
- // these checks fails.
- if (!(protoCaps & PF1_FILESEND))
- fSend = false;
-
- if ((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == m_cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES)))
- fSend = false;
-
- if (protoCaps & PF1_VISLIST && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
- fSend = false;
-
- if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
- fSend = false;
-
- if (!fSend) {
- CWarning::show(CWarning::WARN_SENDFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
- return;
- }
-
- if (tempdirlen <= 0 || tempdirlen >= MAX_PATH - mir_wstrlen(mirandatempdir) - mir_wstrlen(filenametemplate) - 2) // -2 is because %Y takes 4 symbols
- filename[0] = 0; // prompt for a new name
- else {
- mir_wstrcpy(filename + tempdirlen, mirandatempdir);
- if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, nullptr) == 0)
- filename[0] = 0;
- else {
- tempdirlen = mir_wstrlen(filename);
-
- time_t rawtime;
- time(&rawtime);
- const tm *timeinfo;
- timeinfo = _localtime32((__time32_t *)& rawtime);
- wcsftime(filename + tempdirlen, MAX_PATH - tempdirlen, filenametemplate, timeinfo);
- size_t firstnumberpos = tempdirlen + 14;
- size_t lastnumberpos = tempdirlen + 20;
- while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) { // while it exists
- for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--)
- if (filename[pos]++ != '9')
- break;
- else
- if (pos == firstnumberpos)
- filename[0] = 0; // all filenames exist => prompt for a new name
- else
- filename[pos] = '0';
- }
- }
- }
-
- if (filename[0] == 0) { // prompting to save
- wchar_t filter[MAX_PATH];
- mir_snwprintf(filter, L"%s%c*.jpg%c%c", TranslateT("JPEG-compressed images"), 0, 0, 0);
-
- OPENFILENAME dlg;
- dlg.lStructSize = sizeof(dlg);
- dlg.lpstrFilter = filter;
- dlg.nFilterIndex = 1;
- dlg.lpstrFile = filename;
- dlg.nMaxFile = MAX_PATH;
- dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
- dlg.lpstrDefExt = L"jpg";
- if (!GetSaveFileName(&dlg))
- return;
- }
-
- IMGSRVC_INFO ii;
- ii.cbSize = sizeof(ii);
- ii.hbm = hbmp;
- ii.pwszName = filename;
- ii.dwMask = IMGI_HBITMAP;
- ii.fif = FIF_JPEG;
- if (!Image_Save(&ii)) {
- CWarning::show(CWarning::WARN_SAVEFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
- return;
- }
-
- vTempFilenames.insert(mir_wstrdup(filename));
-
- wchar_t *ppFiles[2] = { filename, nullptr };
- CallService(MS_FILE_SENDSPECIFICFILEST, m_cache->getActiveContact(), (LPARAM)&ppFiles);
-}
-
-// remove all temporary files created by the "send clipboard as file" feature.
-void TSAPI CleanTempFiles()
-{
- for (auto &it : vTempFilenames) {
- DeleteFileW(it);
- mir_free(it);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Sets a status bar text for a contact
-
-void CMsgDialog::SetStatusText(const wchar_t *wszText, HICON hIcon)
-{
- if (wszText != nullptr) {
- m_bStatusSet = true;
- m_szStatusText = wszText;
- m_szStatusIcon = hIcon;
- }
- else {
- m_bStatusSet = false;
- m_szStatusText.Empty();
- m_szStatusIcon = nullptr;
- }
-
- tabUpdateStatusBar();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// subclassing for the message filter dialog (set and configure event filters for the
-// current session
-
-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
-};
-
-INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- CMsgDialog *pDlg = (CMsgDialog *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
- switch (uMsg) {
- case WM_INITDIALOG:
- pDlg = (CMsgDialog *)lParam;
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
- {
- uint32_t dwMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", 0);
- uint32_t dwFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", 0);
-
- uint32_t dwPopupMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", 0);
- uint32_t dwPopupFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", 0);
-
- uint32_t dwTrayMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", 0);
- uint32_t dwTrayFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", 0);
-
- for (int i = 0; i < _countof(_eventorder); i++) {
- CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
- CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
- CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
- }
- }
- return FALSE;
-
- 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_CLOSE:
- if (wParam == 1 && lParam == 1 && pDlg) {
- int iFlags = 0;
- uint32_t dwMask = 0;
-
- for (int i = 0; i < _countof(_eventorder); i++) {
- int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i);
- dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
- iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
- }
-
- if (iFlags & GC_EVENT_ADDSTATUS)
- iFlags |= GC_EVENT_REMOVESTATUS;
-
- if (dwMask == 0) {
- db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterFlags");
- db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterMask");
- }
- else {
- db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", iFlags);
- db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", dwMask);
- }
-
- dwMask = iFlags = 0;
-
- for (int i = 0; i < _countof(_eventorder); i++) {
- int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i);
- dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
- iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
- }
-
- if (iFlags & GC_EVENT_ADDSTATUS)
- iFlags |= GC_EVENT_REMOVESTATUS;
-
- if (dwMask == 0) {
- db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupFlags");
- db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupMask");
- }
- else {
- db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", iFlags);
- db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", dwMask);
- }
-
- dwMask = iFlags = 0;
-
- for (int i = 0; i < _countof(_eventorder); i++) {
- int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i);
- dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
- iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
- }
- if (iFlags & GC_EVENT_ADDSTATUS)
- iFlags |= GC_EVENT_REMOVESTATUS;
-
- if (dwMask == 0) {
- db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags");
- db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask");
- }
- else {
- db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", iFlags);
- db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", dwMask);
- }
- Chat_SetFilters(pDlg->getChat());
-
- if (pDlg->m_bFilterEnabled) {
- if (pDlg->m_iLogFilterFlags == 0)
- pDlg->m_btnFilter.Click();
- pDlg->RedrawLog();
- db_set_b(pDlg->m_hContact, CHAT_MODULE, "FilterEnabled", pDlg->m_bFilterEnabled);
- }
- }
- DestroyWindow(hwndDlg);
- break;
-
- case WM_DESTROY:
- SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
- break;
- }
- return FALSE;
-}
-
-void CMsgDialog::ShowFilterMenu()
-{
- m_hwndFilter = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_pContainer->m_hwnd, FilterWndProc, (LPARAM)this);
- TranslateDialogDefault(m_hwndFilter);
-
- RECT rcFilter, rcLog;
- GetClientRect(m_hwndFilter, &rcFilter);
- GetWindowRect(m_pLog->GetHwnd(), &rcLog);
-
- POINT pt;
- pt.x = rcLog.right; pt.y = rcLog.bottom;
- ScreenToClient(m_pContainer->m_hwnd, &pt);
-
- SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::ShowPicture(bool showNewPic)
-{
- if (!m_pPanel.isActive())
- m_pic.cy = m_pic.cx = DPISCALEY_S(60);
-
- if (showNewPic) {
- if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
- if (!m_hwndPanelPic) {
- InvalidateRect(m_hwnd, nullptr, TRUE);
- UpdateWindow(m_hwnd);
- Resize();
- }
- return;
- }
- AdjustBottomAvatarDisplay();
- }
- else {
- m_bShowAvatar = !m_bShowAvatar;
- db_set_b(m_hContact, SRMSGMOD_T, "MOD_ShowPic", m_bShowAvatar);
- }
-
- RECT rc;
- GetWindowRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), &rc);
- if (m_minEditBoxSize.cy + DPISCALEY_S(3) > m_iSplitterY)
- SplitterMoved(rc.bottom - m_minEditBoxSize.cy, GetDlgItem(m_hwnd, IDC_SPLITTERY));
- if (!showNewPic)
- SetDialogToType();
- else
- Resize();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// show a modified context menu for the richedit control(s)
-
-void CMsgDialog::ShowPopupMenu(const CCtrlBase &pCtrl, POINT pt)
-{
- CHARRANGE sel, all = { 0, -1 };
-
- HMENU hSubMenu, hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
- if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
- hSubMenu = GetSubMenu(hMenu, 0);
- else {
- hSubMenu = GetSubMenu(hMenu, 2);
- EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, m_SendFormat != 0 ? MF_ENABLED : MF_GRAYED);
- EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, g_plugin.bPasteAndSend ? MF_ENABLED : MF_GRAYED);
- CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED);
- EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, m_pContainer->m_hwndStatus ? MF_ENABLED : MF_GRAYED);
- }
- TranslateMenu(hSubMenu);
- pCtrl.SendMsg(EM_EXGETSEL, 0, (LPARAM)& sel);
- if (sel.cpMin == sel.cpMax) {
- EnableMenuItem(hSubMenu, IDM_COPY, MF_GRAYED);
- EnableMenuItem(hSubMenu, IDM_QUOTE, MF_GRAYED);
- if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
- EnableMenuItem(hSubMenu, IDM_CUT, MF_GRAYED);
- }
-
- if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) {
- InsertMenuA(hSubMenu, 6, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
- CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, m_bScrollingDisabled ? MF_CHECKED : MF_UNCHECKED);
- }
-
- MessageWindowPopupData mwpd;
- // First notification
- mwpd.uType = MSG_WINDOWPOPUP_SHOWING;
- mwpd.uFlags = (pCtrl.GetCtrlId() == IDC_SRMM_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT);
- mwpd.hContact = m_hContact;
- mwpd.hwnd = pCtrl.GetHwnd();
- mwpd.hMenu = hSubMenu;
- mwpd.selection = 0;
- mwpd.pt = pt;
- NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
-
- int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
-
- // Second notification
- mwpd.selection = iSelection;
- mwpd.uType = MSG_WINDOWPOPUP_SELECTED;
- NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
-
- switch (iSelection) {
- case IDM_COPY:
- pCtrl.SendMsg(WM_COPY, 0, 0);
- break;
- case IDM_CUT:
- pCtrl.SendMsg(WM_CUT, 0, 0);
- break;
- case IDM_PASTE:
- case IDM_PASTEFORMATTED:
- if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
- pCtrl.SendMsg(EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0);
- break;
- case IDM_COPYALL:
- pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
- pCtrl.SendMsg(WM_COPY, 0, 0);
- pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& sel);
- break;
- case IDM_QUOTE:
- SendMessage(m_hwnd, WM_COMMAND, IDC_QUOTE, 0);
- break;
- case IDM_SELECTALL:
- pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
- break;
- case IDM_CLEAR:
- tabClearLog();
- break;
- case ID_LOG_FREEZELOG:
- SendDlgItemMessage(m_hwnd, IDC_SRMM_LOG, WM_KEYDOWN, VK_F12, 0);
- break;
- case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR:
- PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator;
- db_set_b(0, SRMSGMOD_T, "msgsizebar", (uint8_t)PluginConfig.m_visualMessageSizeIndicator);
- Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 0);
- Resize();
- if (m_pContainer->m_hwndStatus)
- RedrawWindow(m_pContainer->m_hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
- break;
- case ID_EDITOR_PASTEANDSENDIMMEDIATELY:
- HandlePasteAndSend();
- break;
- }
-
- if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
- RemoveMenu(hSubMenu, 7, MF_BYPOSITION);
- DestroyMenu(hMenu);
-}
-
-void CMsgDialog::SplitterMoved(int coord, HWND hwnd)
-{
- POINT pt;
- RECT rc;
-
- switch (GetDlgCtrlID(hwnd)) {
- case IDC_MULTISPLITTER:
- GetClientRect(m_hwnd, &rc);
- pt.x = coord;
- pt.y = 0;
- ScreenToClient(m_hwnd, &pt);
- {
- int oldSplitterX = m_iMultiSplit;
- m_iMultiSplit = rc.right - pt.x;
- if (m_iMultiSplit < 25)
- m_iMultiSplit = 25;
-
- if (m_iMultiSplit > ((rc.right - rc.left) - 80))
- m_iMultiSplit = oldSplitterX;
- }
- Resize();
- break;
-
- case IDC_SPLITTERX:
- GetClientRect(m_hwnd, &rc);
- pt.x = coord, pt.y = 0;
- ScreenToClient(m_hwnd, &pt);
- {
- int iSplitterX = rc.right - pt.x + 1;
- if (iSplitterX < 35)
- iSplitterX = 35;
- if (iSplitterX > rc.right - rc.left - 35)
- iSplitterX = rc.right - rc.left - 35;
- m_pContainer->cfg.iSplitterX = iSplitterX;
- }
- Resize();
- break;
-
- case IDC_SPLITTERY:
- GetClientRect(m_hwnd, &rc);
- rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30);
- pt.x = 0;
- pt.y = coord;
- ScreenToClient(m_hwnd, &pt);
- {
- int oldSplitterY = m_iSplitterY;
- int oldDynaSplitter = m_dynaSplitter;
-
- m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(23);
-
- // attempt to fix splitter troubles..
- // hardcoded limits... better solution is possible, but this works for now
- int bottomtoolbarH = 0;
- if (m_pContainer->cfg.flags.m_bBottomToolbar)
- bottomtoolbarH = 22;
-
- if (m_iSplitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) { // min splitter size
- m_iSplitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH);
- m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
- DM_RecalcPictureSize();
- }
- else if (m_iSplitterY > (rc.bottom - rc.top)) {
- m_iSplitterY = oldSplitterY;
- m_dynaSplitter = oldDynaSplitter;
- DM_RecalcPictureSize();
- }
- else {
- m_dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11);
- DM_RecalcPictureSize();
- }
- }
- UpdateToolbarBG();
- Resize();
- break;
-
- case IDC_PANELSPLITTER:
- GetClientRect(m_pLog->GetHwnd(), &rc);
-
- POINT pnt = { 0, coord };
- ScreenToClient(m_hwnd, &pnt);
- if ((pnt.y + 2 >= MIN_PANELHEIGHT + 2) && (pnt.y + 2 < 100) && (pnt.y + 2 < rc.bottom - 30))
- m_pPanel.setHeight(pnt.y + 2, true);
-
- RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
- if (M.isAero())
- InvalidateRect(GetParent(m_hwnd), nullptr, FALSE);
- break;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::StreamEvents(MEVENT hDbEventFirst, int count, bool bAppend)
-{
- m_pLog->LogEvents(hDbEventFirst, count, bAppend);
-
- DM_ScrollToBottom(0, 0);
- if (bAppend && hDbEventFirst)
- m_hDbEventLast = hDbEventFirst;
- else
- m_hDbEventLast = db_event_last(m_hContact);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// sent by the select container dialog box when a container was selected...
-
-void CMsgDialog::SwitchToContainer(const wchar_t *szNewName)
-{
- if (!mir_wstrcmp(szNewName, TranslateT("Default container")))
- szNewName = CGlobals::m_default_container_name;
-
- int iOldItems = TabCtrl_GetItemCount(m_hwndParent);
- if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN))
- return;
-
- TContainerData *pNewContainer = FindContainerByName(szNewName);
- if (pNewContainer == nullptr)
- if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr)
- return;
-
- db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName);
- PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact);
- if (iOldItems > 1) // there were more than 1 tab, container is still valid
- SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0);
- SetForegroundWindow(pNewContainer->m_hwnd);
- SetActiveWindow(pNewContainer->m_hwnd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-bool CMsgDialog::TabAutoComplete()
-{
- LRESULT lResult = m_message.SendMsg(EM_GETSEL, 0, 0);
- int start = LOWORD(lResult), end = HIWORD(lResult);
- int origStart = start, origEnd = end;
- m_message.SendMsg(EM_SETSEL, end, end);
-
- GETTEXTEX gt = { 0 };
- gt.codepage = 1200;
- gt.flags = GTL_DEFAULT | GTL_PRECISE;
- int iLen = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)& gt, 0);
- if (iLen <= 0)
- return false;
-
- bool isTopic = false, isRoom = false;
- wchar_t *pszText = (wchar_t *)mir_calloc((iLen + 10) * sizeof(wchar_t));
-
- gt.flags = GT_DEFAULT;
- gt.cb = (iLen + 9) * sizeof(wchar_t);
- m_message.SendMsg(EM_GETTEXTEX, (WPARAM)& gt, (LPARAM)pszText);
-
- if (m_wszSearchResult != nullptr) {
- int cbResult = (int)mir_wstrlen(m_wszSearchResult);
- if (start >= cbResult && !wcsnicmp(m_wszSearchResult, pszText + start - cbResult, cbResult)) {
- start -= cbResult;
- goto LBL_SkipEnd;
- }
- }
-
- while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB)
- start--;
-
-LBL_SkipEnd:
- while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB)
- end++;
-
- if (pszText[start] == '#')
- isRoom = true;
- else {
- int topicStart = start;
- while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB))
- topicStart--;
- if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6])
- isTopic = true;
- }
-
- if (m_wszSearchQuery == nullptr) {
- m_wszSearchQuery = mir_wstrndup(pszText + start, end - start);
- m_wszSearchResult = mir_wstrdup(m_wszSearchQuery);
- m_pLastSession = nullptr;
- }
-
- const wchar_t *pszName = nullptr;
- if (isTopic)
- pszName = m_si->ptszTopic;
- else if (isRoom) {
- m_pLastSession = SM_FindSessionAutoComplete(m_si->pszModule, m_si, m_pLastSession, m_wszSearchQuery, m_wszSearchResult);
- if (m_pLastSession != nullptr)
- pszName = m_pLastSession->ptszName;
- }
- else pszName = g_chatApi.UM_FindUserAutoComplete(m_si, m_wszSearchQuery, m_wszSearchResult);
-
- replaceStrW(m_wszSearchResult, nullptr);
-
- if (pszName != nullptr) {
- if (end != start) {
- CMStringW szReplace;
- if (!isRoom && !isTopic && start == 0) {
- szReplace = pszName;
- if (mir_wstrlen(g_Settings.pwszAutoText))
- szReplace.Append(g_Settings.pwszAutoText);
- szReplace.AppendChar(' ');
- m_wszSearchResult = szReplace.Detach();
- pszName = m_wszSearchResult;
- }
- else m_wszSearchResult = mir_wstrdup(pszName);
-
- m_message.SendMsg(EM_SETSEL, start, end);
- m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pszName);
- }
- else m_wszSearchResult = mir_wstrdup(pszName);
-
- return true;
- }
-
- if (end != start) {
- m_message.SendMsg(EM_SETSEL, start, end);
- m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)m_wszSearchQuery);
- }
- m_message.SendMsg(EM_SETSEL, origStart, origEnd);
- replaceStrW(m_wszSearchQuery, nullptr);
- return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::tabClearLog()
-{
- if (isChat()) {
- g_chatApi.LM_RemoveAll(&m_si->pLog, &m_si->pLogEnd);
- m_si->iEventCount = 0;
- m_si->LastTime = 0;
- PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
- }
-
- m_pLog->Clear();
- m_hDbEventFirst = 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CThumbBase *CMsgDialog::tabCreateThumb(CProxyWindow *pProxy) const
-{
- if (isChat())
- return new CThumbMUC(pProxy, m_si);
-
- return new CThumbIM(pProxy);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update all status bar fields and force a redraw of the status bar.
-
-void CMsgDialog::tabUpdateStatusBar() const
-{
- if (m_pContainer->m_hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
- if (!isChat()) {
- if (m_wszStatusBar[0]) {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
- }
- else if (m_bStatusSet) {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
- }
- else {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
- DM_UpdateLastMessage();
- }
- }
- else {
- if (m_bStatusSet) {
- SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
- }
- else SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
- }
- UpdateReadChars();
- InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
- SendMessage(m_pContainer->m_hwndStatus, WM_USER + 101, 0, (LPARAM)this);
- }
-}
-
-int CMsgDialog::Typing(int secs)
-{
- if (!AllowTyping())
- return 0;
-
- int preTyping = m_nTypeSecs != 0;
-
- setTyping(m_nTypeSecs = (secs > 0) ? secs : 0);
- if (m_nTypeSecs)
- m_bShowTyping = 0;
-
- return preTyping;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-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();
- m_hTabIcon = m_hTabStatusIcon;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateOptions()
-{
- GetSendFormat();
-
- DM_InitRichEdit();
- m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0);
-
- m_nickList.SetItemHeight(0, g_Settings.iNickListFontHeight);
- InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
-
- m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(m_bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0);
-
- CSuper::UpdateOptions();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update the status bar field which displays the number of characters in the input area
-// and various indicators (caps lock, num lock, insert mode).
-
-void CMsgDialog::UpdateReadChars() const
-{
- if (!m_pContainer->m_hwndStatus || m_pContainer->m_hwndActive != m_hwnd)
- return;
-
- int len;
- if (isChat())
- len = GetWindowTextLength(m_message.GetHwnd());
- else {
- // retrieve text length in UTF8 bytes, because this is the relevant length for most protocols
- GETTEXTLENGTHEX gtxl = { 0 };
- gtxl.codepage = CP_UTF8;
- gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
-
- len = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
- }
-
- BOOL fCaps = (GetKeyState(VK_CAPITAL) & 1);
- BOOL fNum = (GetKeyState(VK_NUMLOCK) & 1);
-
- wchar_t szBuf[20]; szBuf[0] = 0;
- if (m_bInsertMode)
- mir_wstrcat(szBuf, L"O");
- if (fCaps)
- mir_wstrcat(szBuf, L"C");
- if (fNum)
- mir_wstrcat(szBuf, L"N");
- if (m_bInsertMode || fCaps || fNum)
- mir_wstrcat(szBuf, L" | ");
-
- wchar_t buf[128];
- mir_snwprintf(buf, L"%s%s %d/%d", szBuf, m_lcID, m_iOpenJobs, len);
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)buf);
- if (PluginConfig.m_visualMessageSizeIndicator)
- InvalidateRect(m_pContainer->m_hwndStatus, nullptr, FALSE);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateSaveAndSendButton()
-{
- GETTEXTLENGTHEX gtxl = { 0 };
- gtxl.codepage = CP_UTF8;
- gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
-
- int len = SendDlgItemMessage(m_hwnd, IDC_SRMM_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
- if (len && GetSendButtonState() == PBS_DISABLED)
- EnableSendButton(true);
- else if (len == 0 && GetSendButtonState() != PBS_DISABLED)
- EnableSendButton(false);
-
- if (len) { // looks complex but avoids flickering on the button while typing.
- if (!m_bSaveBtn) {
- SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]);
- SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save and close session"), BATF_UNICODE);
- m_bSaveBtn = true;
- }
- }
- else {
- SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]);
- SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Close session"), BATF_UNICODE);
- m_bSaveBtn = false;
- }
- m_textLen = len;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateStatusBar()
-{
- if (m_pContainer->m_hwndActive != m_hwnd || m_pContainer->m_hwndStatus == nullptr || CMimAPI::m_shutDown || m_wszStatusBar[0])
- return;
-
- if (m_si->pszModule == nullptr)
- return;
-
- //Mad: strange rare crash here...
- MODULEINFO *mi = m_si->pMI;
- if (!mi->ptszModDispName)
- return;
-
- int x = 12;
- x += Chat_GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->m_hwndStatus, WM_GETFONT, 0, 0), true);
- x += GetSystemMetrics(SM_CXSMICON);
-
- wchar_t szFinalStatusBarText[512];
- if (m_pPanel.isActive()) {
- time_t now = time(0);
- uint32_t diff = (now - mi->idleTimeStamp) / 60;
- if (diff >= 1) {
- if (diff > 59) {
- uint32_t hours = diff / 60;
- uint32_t minutes = diff % 60;
- mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"),
- hours, hours > 1 ? TranslateT("hours") : TranslateT("hour"),
- minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute"));
- }
- else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute"));
- }
- else mi->tszIdleMsg[0] = 0;
-
- mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg);
- }
- else {
- if (m_si->ptszStatusbarText)
- mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, m_si->ptszStatusbarText);
- else
- wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE);
- }
- SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText);
- tabUpdateStatusBar();
- m_pPanel.Invalidate();
- if (m_pWnd)
- m_pWnd->Invalidate();
-}
-
-void CMsgDialog::UpdateTitle()
-{
- if (isChat()) {
- m_wStatus = m_si->wStatus;
-
- const wchar_t *szNick = m_cache->getNick();
- if (mir_wstrlen(szNick) > 0) {
- if (M.GetByte("cuttitle", 0))
- CutContactName(szNick, m_wszTitle, _countof(m_wszTitle));
- else
- wcsncpy_s(m_wszTitle, szNick, _TRUNCATE);
- }
-
- CMStringW wszTitle;
- HICON hIcon = nullptr;
- int nUsers = m_si->getUserList().getCount();
-
- switch (m_si->iType) {
- case GCW_CHATROOM:
- hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
- wszTitle.Format((nUsers == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"),
- szNick, nUsers, m_bFilterEnabled ? TranslateT(", event filter active") : L"");
- break;
-
- case GCW_PRIVMESS:
- hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
- if (nUsers == 1)
- wszTitle.Format(TranslateT("%s: message session"), szNick);
- else
- wszTitle.Format(TranslateT("%s: message session (%u users)"), szNick, nUsers);
- break;
-
- case GCW_SERVER:
- wszTitle.Format(L"%s: Server", szNick);
- hIcon = LoadIconEx("window");
- break;
-
- default:
- return;
- }
-
- if (m_pWnd) {
- m_pWnd->updateTitle(m_wszTitle);
- m_pWnd->updateIcon(hIcon);
- }
- m_hTabStatusIcon = hIcon;
-
- if (m_cache->getStatus() != m_cache->getOldStatus()) {
- wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_wStatus, 0), _TRUNCATE);
-
- TCITEM item = {};
- item.mask = TCIF_TEXT;
- item.pszText = m_wszTitle;
- TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
- }
- SetWindowText(m_hwnd, wszTitle);
- if (m_pContainer->m_hwndActive == m_hwnd) {
- m_pContainer->UpdateTitle(0, this);
- UpdateStatusBar();
- }
- }
- else {
- uint32_t dwOldIdle = m_idle;
- const char *szActProto = nullptr;
-
- m_wszStatus[0] = 0;
-
- if (m_iTabID == -1)
- return;
-
- TCITEM item = {};
-
- bool bChanged = false;
- wchar_t newtitle[128];
- if (m_szProto) {
- szActProto = m_cache->getProto();
-
- bool bHasName = (m_cache->getUIN()[0] != 0);
- m_idle = m_cache->getIdleTS();
- m_bIsIdle = m_idle != 0;
-
- m_wStatus = m_cache->getStatus();
- wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_szProto == nullptr ? ID_STATUS_OFFLINE : m_wStatus, 0), _TRUNCATE);
-
- wchar_t newcontactname[128]; newcontactname[0] = 0;
- if (PluginConfig.m_bCutContactNameOnTabs)
- CutContactName(m_cache->getNick(), newcontactname, _countof(newcontactname));
- else
- wcsncpy_s(newcontactname, m_cache->getNick(), _TRUNCATE);
-
- Utils::DoubleAmpersands(newcontactname, _countof(newcontactname));
-
- if (newcontactname[0] != 0) {
- if (g_plugin.bStatusOnTabs)
- mir_snwprintf(newtitle, L"%s (%s)", newcontactname, m_wszStatus);
- else
- wcsncpy_s(newtitle, newcontactname, _TRUNCATE);
- }
- else wcsncpy_s(newtitle, L"Forward", _TRUNCATE);
-
- if (mir_wstrcmp(newtitle, m_wszTitle))
- bChanged = true;
- else if (m_wStatus != m_wOldStatus)
- bChanged = true;
-
- UpdateWindowIcon();
-
- wchar_t fulluin[256];
- if (m_bIsMeta)
- mir_snwprintf(fulluin,
- TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nRight click for metacontact control\nClick dropdown to add or remove user from your favorites."),
- bHasName ? m_cache->getUIN() : TranslateT("No UID"));
- else
- mir_snwprintf(fulluin,
- TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nClick dropdown to change this contact's favorite status."),
- bHasName ? m_cache->getUIN() : TranslateT("No UID"));
-
- SendDlgItemMessage(m_hwnd, IDC_NAME, BUTTONADDTOOLTIP, (WPARAM)fulluin, BATF_UNICODE);
- }
- else wcsncpy_s(newtitle, L"Message Session", _TRUNCATE);
-
- m_wOldStatus = m_wStatus;
- if (m_idle != dwOldIdle || bChanged) {
- if (bChanged) {
- item.mask |= TCIF_TEXT;
- item.pszText = m_wszTitle;
- wcsncpy_s(m_wszTitle, newtitle, _TRUNCATE);
- if (m_pWnd)
- m_pWnd->updateTitle(m_cache->getNick());
- }
- if (m_iTabID >= 0) {
- TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
- if (m_pContainer->cfg.flags.m_bSideBar)
- m_pContainer->m_pSideBar->updateSession(this);
- }
- if (m_pContainer->m_hwndActive == m_hwnd && bChanged)
- m_pContainer->UpdateTitle(m_hContact);
-
- m_pPanel.Invalidate();
- if (m_pWnd)
- m_pWnd->Invalidate();
- }
-
- // care about MetaContacts and update the statusbar icon with the currently "most online" contact...
- if (m_bIsMeta) {
- PostMessage(m_hwnd, DM_OWNNICKCHANGED, 0, 0);
- if (m_pContainer->cfg.flags.m_bUinStatusBar)
- DM_UpdateLastMessage();
- }
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateWindowIcon()
-{
- if (m_hXStatusIcon) {
- DestroyIcon(m_hXStatusIcon);
- m_hXStatusIcon = nullptr;
- }
-
- if (LPCSTR szProto = m_cache->getProto()) {
- m_hTabIcon = m_hTabStatusIcon = GetMyContactIcon(&g_plugin.bMetaTab);
- if (g_plugin.bUseXStatus)
- m_hXStatusIcon = GetXStatusIcon();
-
- SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BUTTONSETASDIMMED, m_bIsIdle, 0);
- SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(m_hXStatusIcon ? m_hXStatusIcon : GetMyContactIcon(&g_plugin.bMetaBar)));
-
- if (m_pContainer->m_hwndActive == m_hwnd)
- m_pContainer->SetIcon(this, m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
-
- if (m_pWnd)
- m_pWnd->updateIcon(m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// called whenever a group chat tab becomes active(either by switching tabs or activating a
-// container window
-
-void CMsgDialog::UpdateWindowState(UINT msg)
-{
- if (m_iTabID < 0)
- return;
-
- if (msg == WM_ACTIVATE) {
- if (m_pContainer->cfg.flags.m_bTransparent) {
- uint32_t trans = LOWORD(m_pContainer->cfg.dwTransparency);
- SetLayeredWindowAttributes(m_pContainer->m_hwnd, CSkin::m_ContainerColorKey, (uint8_t)trans, (m_pContainer->cfg.flags.m_bTransparent ? LWA_ALPHA : 0));
- }
- }
-
- if (m_hwndFilter) {
- POINT pt;
- GetCursorPos(&pt);
-
- RECT rcFilter;
- GetWindowRect(m_hwndFilter, &rcFilter);
- if (!PtInRect(&rcFilter, pt)) {
- SendMessage(m_hwndFilter, WM_CLOSE, 1, 1);
- m_hwndFilter = nullptr;
- }
- }
-
- if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) {
- m_iInputAreaHeight = 0;
- m_message.SendMsg(EM_REQUESTRESIZE, 0, 0);
- }
-
- m_pPanel.dismissConfig();
- m_dwUnread = 0;
- if (m_pWnd) {
- m_pWnd->activateTab();
- m_pWnd->setOverlayIcon(nullptr, true);
- }
-
- if (m_pContainer->m_hwndSaved == m_hwnd)
- return;
-
- m_pContainer->m_hwndSaved = m_hwnd;
- m_dwTickLastEvent = 0;
- m_bDividerSet = false;
-
- if (m_pContainer->m_dwFlashingStarted != 0) {
- m_pContainer->FlashContainer(0, 0);
- m_pContainer->m_dwFlashingStarted = 0;
- }
-
- if (m_si) {
- g_chatApi.SetActiveSession(m_si);
- m_hTabIcon = m_hTabStatusIcon;
-
- if (db_get_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0) != 0)
- db_set_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0);
- if (g_clistApi.pfnGetEvent(m_si->hContact, 0))
- g_clistApi.pfnRemoveEvent(m_si->hContact, GC_FAKE_EVENT);
-
- UpdateTitle();
- m_hTabIcon = m_hTabStatusIcon;
- if (timerFlash.Stop() || m_iFlashIcon) {
- FlashTab(false);
- m_bCanFlashTab = FALSE;
- m_iFlashIcon = nullptr;
- }
-
- m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
-
- if (m_bNeedCheckSize)
- PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
-
- SetFocus(m_message.GetHwnd());
- m_dwLastActivity = GetTickCount();
- m_pContainer->m_dwLastActivity = m_dwLastActivity;
- m_pContainer->m_pMenuBar->configureMenu();
- }
- else {
- if (timerFlash.Stop()) {
- FlashTab(false);
- m_bCanFlashTab = false;
- }
-
- if (m_bFlashClist) {
- m_bFlashClist = false;
- if (m_hFlashingEvent != 0)
- g_clistApi.pfnRemoveEvent(m_hContact, m_hFlashingEvent);
- m_hFlashingEvent = 0;
- }
- m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
-
- if (m_bDeferredRemakeLog && !IsIconic(m_pContainer->m_hwnd)) {
- RemakeLog();
- m_bDeferredRemakeLog = false;
- }
-
- if (m_bNeedCheckSize)
- PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
-
- m_pContainer->m_hIconTaskbarOverlay = nullptr;
- m_pContainer->UpdateTitle(m_hContact);
-
- tabUpdateStatusBar();
- m_dwLastActivity = GetTickCount();
- m_pContainer->m_dwLastActivity = m_dwLastActivity;
-
- m_pContainer->m_pMenuBar->configureMenu();
- g_arUnreadWindows.remove(HANDLE(m_hContact));
-
- m_pPanel.Invalidate();
-
- if (m_bDeferredScroll) {
- m_bDeferredScroll = false;
- DM_ScrollToBottom(0, 1);
- }
- }
-
- DM_SetDBButtonStates();
-
- if (m_bDelayedSplitter) {
- m_bDelayedSplitter = false;
- ShowWindow(m_pContainer->m_hwnd, SW_RESTORE);
- PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam);
- PostMessage(m_hwnd, WM_SIZE, 0, 0);
- m_wParam = m_lParam = 0;
- }
-
- BB_SetButtonsPos();
- if (M.isAero())
- InvalidateRect(m_hwndParent, nullptr, FALSE);
-
- if (m_pContainer->cfg.flags.m_bSideBar)
- m_pContainer->m_pSideBar->setActiveItem(this, msg == WM_ACTIVATE);
-
- if (m_pWnd)
- m_pWnd->Invalidate();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// generic handler for the WM_COPY message in message log/chat history richedit control(s).
-// it filters out the invisible event boundary markers from the text copied to the clipboard.
-// WINE Fix: overwrite clippboad data from original control data
-
-LRESULT CMsgDialog::WMCopyHandler(UINT msg, WPARAM wParam, LPARAM lParam)
-{
- LRESULT result = mir_callNextSubclass(m_pLog->GetHwnd(), stubLogProc, msg, wParam, lParam);
-
- ptrA szFromStream(LOG()->GetRichTextRtf(true, true));
- if (szFromStream != nullptr) {
- ptrW converted(mir_utf8decodeW(szFromStream));
- if (converted != nullptr) {
- Utils::FilterEventMarkers(converted);
- Utils_ClipboardCopy(converted);
- }
- }
-
- return result;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/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.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// Helper functions for the message dialog.
+
+#include "stdafx.h"
+
+UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM, LPARAM lParam);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show the balloon tooltip control.
+
+void CMsgDialog::ActivateTooltip(int iCtrlId, const wchar_t *pwszMessage)
+{
+ if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd)
+ m_pPanel.showTip(iCtrlId, pwszMessage);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::AddLog()
+{
+ if (g_plugin.bUseDividers) {
+ if (g_plugin.bDividersUsePopupConfig) {
+ if (!MessageWindowOpened(0, this))
+ DM_AddDivider();
+ }
+ else {
+ if (!IsActive())
+ DM_AddDivider();
+ else if (m_pContainer->m_hwndActive != m_hwnd)
+ DM_AddDivider();
+ }
+ }
+
+ CSrmmBaseDialog::AddLog();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::AdjustBottomAvatarDisplay()
+{
+ GetAvatarVisibility();
+
+ bool bInfoPanel = m_pPanel.isActive();
+ HBITMAP hbm = (bInfoPanel && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
+ if (hbm) {
+ if (m_dynaSplitter == 0 || m_iSplitterY == 0)
+ LoadSplitter();
+ m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
+ DM_RecalcPictureSize();
+ Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
+ }
+ else {
+ Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
+ m_pic.cy = m_pic.cx = DPISCALEY_S(60);
+ InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// calculates avatar layouting, based on splitter position to find the optimal size
+// for the avatar w/o disturbing the toolbar too much.
+
+void CMsgDialog::CalcDynamicAvatarSize(BITMAP *bminfo)
+{
+ if (m_bWasBackgroundCreate || m_pContainer->cfg.flags.m_bDeferredConfigure || m_pContainer->cfg.flags.m_bCreateMinimized || IsIconic(m_pContainer->m_hwnd))
+ return; // at this stage, the layout is not yet ready...
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+
+ BOOL bBottomToolBar = m_pContainer->cfg.flags.m_bBottomToolbar;
+ BOOL bToolBar = m_pContainer->cfg.flags.m_bHideToolbar ? 0 : 1;
+ int iSplitOffset = m_bIsAutosizingInput ? 1 : 0;
+
+ double picAspect = (bminfo->bmWidth == 0 || bminfo->bmHeight == 0) ? 1.0 : (double)(bminfo->bmWidth / (double)bminfo->bmHeight);
+ double picProjectedWidth = (double)((m_dynaSplitter - ((bBottomToolBar && bToolBar) ? DPISCALEX_S(24) : 0) + ((m_bShowUIElements) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect;
+
+ if ((rc.right - (int)picProjectedWidth) > (m_iButtonBarReallyNeeds) && !PluginConfig.m_bAlwaysFullToolbarWidth && bToolBar)
+ m_iRealAvatarHeight = m_dynaSplitter + 3 + (m_bShowUIElements ? DPISCALEY_S(28) : DPISCALEY_S(2));
+ else
+ m_iRealAvatarHeight = m_dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset);
+
+ m_iRealAvatarHeight -= ((bBottomToolBar && bToolBar) ? DPISCALEY_S(22) : 0);
+
+ if (PluginConfig.m_LimitStaticAvatarHeight > 0)
+ m_iRealAvatarHeight = min(m_iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight);
+
+ if (M.GetByte(m_hContact, "dontscaleavatars", M.GetByte("dontscaleavatars", 0)))
+ m_iRealAvatarHeight = min(bminfo->bmHeight, m_iRealAvatarHeight);
+
+ double aspect = (bminfo->bmHeight != 0) ? (double)m_iRealAvatarHeight / (double)bminfo->bmHeight : 1.0;
+ double newWidth = (double)bminfo->bmWidth * aspect;
+ if (newWidth > (double)(rc.right) * 0.8)
+ newWidth = (double)(rc.right) * 0.8;
+ m_pic.cy = m_iRealAvatarHeight + 2;
+ m_pic.cx = (int)newWidth + 2;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::CloseTab()
+{
+ int iTabs = TabCtrl_GetItemCount(m_hwndParent);
+ if (iTabs == 1) {
+ SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 1);
+ return;
+ }
+
+ m_pContainer->m_iChilds--;
+ int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd);
+
+ // after closing a tab, we need to activate the tab to the left side of
+ // the previously open tab.
+ // normally, this tab has the same index after the deletion of the formerly active tab
+ // unless, of course, we closed the last (rightmost) tab.
+ if (!m_pContainer->m_bDontSmartClose && iTabs > 1) {
+ if (i == iTabs - 1)
+ i--;
+ else
+ i++;
+ TabCtrl_SetCurSel(m_hwndParent, i);
+
+ m_pContainer->m_hwndActive = GetTabWindow(m_hwndParent, i);
+
+ RECT rc;
+ m_pContainer->QueryClientArea(rc);
+ SetWindowPos(m_pContainer->m_hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW);
+ ShowWindow(m_pContainer->m_hwndActive, SW_SHOW);
+ SetForegroundWindow(m_pContainer->m_hwndActive);
+ SetFocus(m_pContainer->m_hwndActive);
+ }
+
+ SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
+ DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// calculate the minimum required client height for the given message
+// window layout
+//
+// the container will use this in its WM_GETMINMAXINFO handler to set
+// minimum tracking height.
+
+void CMsgDialog::DetermineMinHeight()
+{
+ RECT rc;
+ LONG height = (m_pPanel.isActive() ? m_pPanel.getHeight() + 2 : 0);
+ if (!m_pContainer->cfg.flags.m_bHideToolbar)
+ height += DPISCALEY_S(24); // toolbar
+ GetClientRect(m_message.GetHwnd(), &rc);
+ height += rc.bottom; // input area
+ height += 40; // min space for log area and some padding
+
+ m_pContainer->m_uChildMinHeight = height;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 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 RtfColorToIndex(int iNumColors, int *pIndex, int iCol)
+{
+ for (int i = 0; i < iNumColors; i++)
+ if (pIndex[i] == iCol)
+ return i;
+
+ return -1;
+}
+
+BOOL CMsgDialog::DoRtfToTags(CMStringW &pszText) const
+{
+ if (pszText.IsEmpty())
+ return FALSE;
+
+ // used to filter out attributes which are already set for the default message input area font
+ auto &lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
+
+ // create an index of colors in the module and map them to
+ // corresponding colors in the RTF color table
+ int iNumColors = Utils::rtf_clrs.getCount();
+ int *pIndex = (int *)_alloca(iNumColors * sizeof(int));
+ COLORREF *pColors = (COLORREF *)_alloca(iNumColors * sizeof(COLORREF));
+ for (int i = 0; i < iNumColors; i++)
+ pColors[i] = Utils::rtf_clrs[i].clr;
+ 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;
+
+ MODULEINFO *mi = (isChat()) ? m_si->pMI : nullptr;
+
+ 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 = RtfColorToIndex(iNumColors, pIndex, iCol);
+
+ if (iCol > 0) {
+ if (isChat()) {
+ if (mi && mi->bColor) {
+ if (iInd >= 0) {
+ if (!(res.IsEmpty() && m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA] == pColors[iInd]))
+ res.AppendFormat(L"%%c%u", iInd);
+ }
+ else if (!res.IsEmpty())
+ res.Append(L"%%C");
+ }
+ }
+ else res.AppendFormat((iInd >= 0) ? (bInsideColor ? L"[/color][color=%s]" : L"[color=%s]") : (bInsideColor ? L"[/color]" : L""), Utils::rtf_clrs[iInd].szName);
+ }
+
+ bInsideColor = iInd >= 0;
+ }
+ else if (!wcsncmp(p, L"\\highlight", 10)) { // background color
+ if (isChat()) {
+ if (mi && mi->bBkgColor) {
+ int iInd = RtfColorToIndex(iNumColors, pIndex, _wtoi(p + 10));
+ if (iInd >= 0) {
+ // if the entry field is empty & the color passed is the back color, skip it
+ if (!(res.IsEmpty() && m_pContainer->m_theme.inputbg == pColors[iInd]))
+ res.AppendFormat(L"%%f%u", iInd);
+ }
+ else if (!res.IsEmpty())
+ res.AppendFormat(L"%%F");
+ }
+ }
+ }
+ 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
+ if (isChat()) {
+ if (mi && mi->bBold)
+ res.Append((p[2] != '0') ? L"%b" : L"%B");
+ }
+ else {
+ if (!(lf.lfWeight == FW_BOLD)) // only allow bold if the font itself isn't a bold one, otherwise just strip it..
+ if (m_SendFormat)
+ res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
+ }
+ }
+ else if (!wcsncmp(p, L"\\i", 2)) { // italics
+ if (isChat()) {
+ if (mi && mi->bItalics)
+ res.Append((p[2] != '0') ? L"%i" : L"%I");
+ }
+ else {
+ if (!lf.lfItalic && m_SendFormat)
+ res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
+ }
+ }
+ else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
+ if (!lf.lfStrikeOut && m_SendFormat)
+ res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
+ }
+ else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
+ if (isChat()) {
+ if (mi && mi->bUnderline)
+ res.Append((p[3] != '0') ? L"%u" : L"%U");
+ }
+ else {
+ if (!lf.lfUnderline && m_SendFormat) {
+ 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];
+
+ if (p[3] != ' ' && p[3] != '\\') {
+ wcsncpy(tmp, p + 2, 3);
+ tmp[3] = 0;
+ }
+ else {
+ wcsncpy(tmp, p + 2, 2);
+ tmp[2] = 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;
+
+ case '%': // double % for stupid chat engine
+ if (isChat())
+ res.Append(L"%%");
+ else
+ res.AppendChar(*p);
+ p++;
+ break;
+
+ default: // other text that should not be touched
+ res.AppendChar(*p++);
+ break;
+ }
+ }
+
+ if (bInsideColor && !isChat())
+ res.Append(L"[/color]");
+ if (bInsideUl)
+ res.Append(L"[/u]");
+
+ pszText = res;
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::EnableSendButton(bool bMode) const
+{
+ SendDlgItemMessage(m_hwnd, IDOK, BUTTONSETASNORMAL, bMode, 0);
+ SendDlgItemMessage(m_hwnd, IDC_PIC, BUTTONSETASNORMAL, m_bEditNotesActive ? TRUE : (!bMode && m_iOpenJobs == 0) ? TRUE : FALSE, 0);
+
+ HWND hwndOK = GetDlgItem(GetParent(GetParent(m_hwnd)), IDOK);
+ if (IsWindow(hwndOK))
+ SendMessage(hwndOK, BUTTONSETASNORMAL, bMode, 0);
+}
+
+void CMsgDialog::EnableSending(bool bMode) const
+{
+ m_message.SendMsg(EM_SETREADONLY, !bMode, 0);
+ Utils::enableDlgControl(m_hwnd, IDC_CLIST, bMode);
+ EnableSendButton(bMode);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::FindFirstEvent()
+{
+ int historyMode = g_plugin.getByte(m_hContact, SRMSGSET_LOADHISTORY, -1);
+ if (historyMode == -1)
+ historyMode = (int)g_plugin.getByte(SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY);
+
+ m_hDbEventFirst = db_event_firstUnread(m_hContact);
+
+ if (m_bActualHistory)
+ historyMode = LOADHISTORY_COUNT;
+
+ DBEVENTINFO dbei = {};
+ DB::ECPTR pCursor(DB::EventsRev(m_hContact, m_hDbEventFirst));
+
+ switch (historyMode) {
+ case LOADHISTORY_COUNT:
+ int i;
+
+ // ability to load only current session's history
+ if (m_bActualHistory)
+ i = m_cache->getSessionMsgCount();
+ else
+ i = g_plugin.getWord(SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT);
+
+ for (; i > 0; i--) {
+ MEVENT hPrevEvent = pCursor.FetchNext();
+ if (hPrevEvent == 0)
+ break;
+
+ dbei.cbBlob = 0;
+ m_hDbEventFirst = hPrevEvent;
+ db_event_get(m_hDbEventFirst, &dbei);
+ if (!DbEventIsShown(&dbei))
+ i++;
+ }
+ break;
+
+ case LOADHISTORY_TIME:
+ if (m_hDbEventFirst == 0)
+ dbei.timestamp = time(0);
+ else
+ db_event_get(m_hDbEventFirst, &dbei);
+
+ uint32_t firstTime = dbei.timestamp - 60 * g_plugin.getWord(SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME);
+
+ while (MEVENT hPrevEvent = pCursor.FetchNext()) {
+ dbei.cbBlob = 0;
+ db_event_get(hPrevEvent, &dbei);
+ if (dbei.timestamp < firstTime)
+ break;
+ m_hDbEventFirst = hPrevEvent;
+ }
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns != 0 when one of the installed keyboard layouts belongs to an rtl language
+// used to find out whether we need to configure the message input box for bidirectional mode
+
+int CMsgDialog::FindRTLLocale()
+{
+ int result = 0;
+
+ if (m_iHaveRTLLang == 0) {
+ HKL layouts[20];
+ memset(layouts, 0, sizeof(layouts));
+ GetKeyboardLayoutList(20, layouts);
+ for (int i = 0; i < 20 && layouts[i]; i++) {
+ uint16_t wCtype2[5];
+ LCID lcid = MAKELCID(LOWORD(layouts[i]), 0);
+ GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2);
+ if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT)
+ result = 1;
+ }
+ m_iHaveRTLLang = (result ? 1 : -1);
+ }
+ else result = m_iHaveRTLLang == 1 ? 1 : 0;
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::FlashOnClist(MEVENT hEvent, DBEVENTINFO *dbei)
+{
+ m_dwTickLastEvent = GetTickCount();
+
+ if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) {
+ m_dwUnread++;
+ AddUnreadContact(m_hContact);
+ }
+
+ if (hEvent == 0)
+ return;
+
+ if (!g_plugin.bFlashOnClist)
+ return;
+
+ if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !m_bFlashClist) {
+ CLISTEVENT cle = {};
+ cle.hContact = m_hContact;
+ cle.hDbEvent = hEvent;
+ cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
+ cle.pszService = MS_MSG_READMESSAGE;
+ g_clistApi.pfnAddEvent(&cle);
+
+ m_bFlashClist = true;
+ m_hFlashingEvent = hEvent;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// flash a tab icon if mode = true, otherwise restore default icon
+// store flashing state into bState
+
+void CMsgDialog::FlashTab(bool bInvertMode)
+{
+ if (bInvertMode)
+ m_bTabFlash = !m_bTabFlash;
+
+ TCITEM item = {};
+ item.mask = TCIF_IMAGE;
+ TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+ if (m_pContainer->cfg.flags.m_bSideBar)
+ m_pContainer->m_pSideBar->updateSession(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this translates formatting tags into rtf sequences...
+// flags: loword = words only for simple * /_ formatting
+// hiword = bbcode support (strip bbcodes if 0)
+
+static wchar_t *w_bbcodes_begin[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[color=" };
+static wchar_t *w_bbcodes_end[] = { L"[/b]", L"[/i]", L"[/u]", L"[/s]", L"[/color]" };
+
+static wchar_t *formatting_strings_begin[] = { L"b1 ", L"i1 ", L"u1 ", L"s1 ", L"c1 " };
+static wchar_t *formatting_strings_end[] = { L"b0 ", L"i0 ", L"u0 ", L"s0 ", L"c0 " };
+
+void CMsgDialog::FormatRaw(CMStringW &msg, int flags, bool isSent)
+{
+ bool clr_was_added = false;
+ int beginmark = 0, endmark = 0, tempmark = 0;
+ int i, endindex;
+
+ if (m_dwFlags & MWF_LOG_BBCODE) {
+ beginmark = 0;
+ while (true) {
+ for (i = 0; i < _countof(w_bbcodes_begin); i++)
+ if ((tempmark = msg.Find(w_bbcodes_begin[i], 0)) != -1)
+ break;
+
+ if (i >= _countof(w_bbcodes_begin))
+ break;
+
+ beginmark = tempmark;
+ endindex = i;
+ endmark = msg.Find(w_bbcodes_end[i], beginmark);
+ if (endindex == 4) { // color
+ int closing = msg.Find(L"]", beginmark);
+ bool was_added = false;
+
+ if (closing == -1) { // must be an invalid [color=] tag w/o closing bracket
+ msg.SetAt(beginmark, ' ');
+ continue;
+ }
+
+ CMStringW colorname = msg.Mid(beginmark + 7, closing - beginmark - 7);
+search_again:
+ bool clr_found = false;
+ for (int ii = 0; ii < Utils::rtf_clrs.getCount(); ii++) {
+ auto &rtfc = Utils::rtf_clrs[ii];
+ if (!wcsicmp(colorname, rtfc.szName)) {
+ closing = beginmark + 7 + (int)mir_wstrlen(rtfc.szName);
+ if (endmark != -1) {
+ msg.Delete(endmark, 8);
+ msg.Insert(endmark, L"c0 ");
+ }
+ msg.Delete(beginmark, closing - beginmark + 1);
+
+ wchar_t szTemp[5];
+ msg.Insert(beginmark, L"cxxx ");
+ mir_snwprintf(szTemp, L"%02d", MSGDLGFONTCOUNT + 13 + ii);
+ msg.SetAt(beginmark + 3, szTemp[0]);
+ msg.SetAt(beginmark + 4, szTemp[1]);
+ clr_found = true;
+ if (was_added) {
+ wchar_t wszTemp[100];
+ mir_snwprintf(wszTemp, L"##col##%06u:%04u", endmark - closing, ii);
+ wszTemp[99] = 0;
+ msg.Insert(beginmark, wszTemp);
+ }
+ break;
+ }
+ }
+ if (!clr_found) {
+ int c_closing = colorname.Find(L"]");
+ if (c_closing == -1)
+ c_closing = colorname.GetLength();
+ const wchar_t *wszColname = colorname.c_str();
+ if (endmark != -1 && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing - 1])) {
+ Utils::RTF_ColorAdd(wszColname);
+ if (!was_added) {
+ clr_was_added = was_added = true;
+ goto search_again;
+ }
+ else goto invalid_code;
+ }
+ else {
+invalid_code:
+ if (endmark != -1)
+ msg.Delete(endmark, 8);
+ if (closing != -1 && closing < endmark)
+ msg.Delete(beginmark, (closing - beginmark) + 1);
+ else
+ msg.SetAt(beginmark, ' ');
+ }
+ }
+ continue;
+ }
+
+ if (endmark != -1) {
+ msg.Delete(endmark, 4);
+ msg.Insert(endmark, formatting_strings_end[i]);
+ }
+ msg.Delete(beginmark, 3);
+ msg.Insert(beginmark, L" ");
+ msg.Insert(beginmark, formatting_strings_begin[i]);
+ }
+ }
+
+ if ((m_dwFlags & MWF_LOG_TEXTFORMAT) && msg.Find(L"://") == -1) {
+ while ((beginmark = msg.Find(L"*/_", beginmark)) != -1) {
+ wchar_t endmarker = msg[beginmark];
+ if (LOWORD(flags)) {
+ if (beginmark > 0 && !iswspace(msg[beginmark - 1]) && !iswpunct(msg[beginmark - 1])) {
+ beginmark++;
+ continue;
+ }
+
+ // search a corresponding endmarker which fulfills the criteria
+ INT_PTR mark = beginmark + 1;
+ while ((endmark = msg.Find(endmarker, mark)) != -1) {
+ if (iswpunct(msg[endmark + 1]) || iswspace(msg[endmark + 1]) || msg[endmark + 1] == 0 || wcschr(L"*/_", msg[endmark + 1]) != nullptr)
+ goto ok;
+ mark = endmark + 1;
+ }
+ break;
+ }
+ else {
+ if ((endmark = msg.Find(endmarker, beginmark + 1)) == -1)
+ break;
+ }
+ok:
+ if ((endmark - beginmark) < 2) {
+ beginmark++;
+ continue;
+ }
+
+ int index = 0;
+ switch (endmarker) {
+ case '*':
+ index = 0;
+ break;
+ case '/':
+ index = 1;
+ break;
+ case '_':
+ index = 2;
+ break;
+ }
+
+ // check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if
+ // it really is one.
+ if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) {
+ CMStringW smcode = msg.Mid(beginmark, (endmark - beginmark) + 1);
+
+ SMADD_BATCHPARSE2 smbp = {};
+ smbp.cbSize = sizeof(smbp);
+ smbp.Protocolname = m_cache->getActiveProto();
+ smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0);
+ smbp.str = (wchar_t*)smcode.c_str();
+ smbp.hContact = m_hContact;
+
+ SMADD_BATCHPARSERES *smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&smbp);
+ if (smbpr) {
+ CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr);
+ beginmark = endmark + 1;
+ continue;
+ }
+ }
+ msg.Delete(endmark, 1);
+ msg.Insert(endmark, formatting_strings_end[index]);
+ msg.Delete(beginmark, 1);
+ msg.Insert(beginmark, formatting_strings_begin[index]);
+ }
+ }
+
+ m_bClrAdded = clr_was_added;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// format the title bar string for IM chat sessions using placeholders.
+// the caller must mir_free() the returned string
+
+static wchar_t* Trunc500(wchar_t *str)
+{
+ if (mir_wstrlen(str) > 500)
+ str[500] = 0;
+ return str;
+}
+
+bool CMsgDialog::FormatTitleBar(const wchar_t *szFormat, CMStringW &dest)
+{
+ for (const wchar_t *src = szFormat; *src; src++) {
+ if (*src != '%') {
+ dest.AppendChar(*src);
+ continue;
+ }
+
+ switch (*++src) {
+ case 'n':
+ dest.Append(m_cache->getNick());
+ break;
+
+ case 'p':
+ case 'a':
+ dest.Append(m_cache->getRealAccount());
+ break;
+
+ case 's':
+ dest.Append(m_wszStatus);
+ break;
+
+ case 'u':
+ dest.Append(m_cache->getUIN());
+ break;
+
+ case 'c':
+ dest.Append(!mir_wstrcmp(m_pContainer->m_wszName, L"default") ? TranslateT("Default container") : m_pContainer->m_wszName);
+ break;
+
+ case 'o':
+ {
+ const char *szProto = m_cache->getActiveProto();
+ if (szProto)
+ dest.Append(_A2T(szProto));
+ }
+ break;
+
+ case 'x':
+ {
+ uint8_t xStatus = m_cache->getXStatusId();
+ if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+ ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
+ dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
+ }
+ }
+ break;
+
+ case 'm':
+ {
+ uint8_t xStatus = m_cache->getXStatusId();
+ if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+ ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
+ dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
+ }
+ else dest.Append(m_wszStatus[0] ? m_wszStatus : L"(undef)");
+ }
+ break;
+
+ // status message (%T will skip the "No status message" for empty messages)
+ case 't':
+ case 'T':
+ {
+ ptrW tszStatus(m_cache->getNormalizedStatusMsg(m_cache->getStatusMsg(), true));
+ if (tszStatus)
+ dest.Append(tszStatus);
+ else if (*src == 't')
+ dest.Append(TranslateT("No status message"));
+ }
+ break;
+
+ case 'g':
+ {
+ ptrW tszGroup(Clist_GetGroup(m_hContact));
+ if (tszGroup != nullptr)
+ dest.Append(tszGroup);
+ }
+ break;
+
+ case 0: // wrongly formed format string
+ return true;
+ }
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve the visiblity of the avatar window, depending on the global setting
+// and local mode
+
+bool CMsgDialog::GetAvatarVisibility()
+{
+ uint8_t bAvatarMode = m_pContainer->cfg.avatarMode;
+ uint8_t bOwnAvatarMode = m_pContainer->cfg.ownAvatarMode;
+ char hideOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+
+ // infopanel visible, consider own avatar display
+ m_bShowAvatar = false;
+ if (m_si)
+ bAvatarMode = 1;
+
+ if (m_pPanel.isActive() && bAvatarMode != 3) {
+ if (!bOwnAvatarMode) {
+ m_bShowAvatar = (m_hOwnPic && m_hOwnPic != PluginConfig.g_hbmUnknown);
+ if (!m_hwndContactPic)
+ m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
+ }
+
+ switch (bAvatarMode) {
+ case 2:
+ m_bShowInfoAvatar = false;
+ break;
+ case 0:
+ m_bShowInfoAvatar = true;
+ case 1:
+ HBITMAP hbm = ((m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr);
+ if (hbm == nullptr && !bAvatarMode) {
+ m_bShowInfoAvatar = false;
+ break;
+ }
+
+ if (!m_hwndPanelPic) {
+ m_hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, m_hwndPanelPicParent, (HMENU)7000, nullptr, nullptr);
+ if (m_hwndPanelPic)
+ SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE);
+ }
+
+ if (bAvatarMode != 0)
+ m_bShowInfoAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
+ break;
+ }
+
+ if (m_bShowInfoAvatar)
+ m_bShowInfoAvatar = hideOverride == 0 ? false : m_bShowInfoAvatar;
+ else
+ m_bShowInfoAvatar = hideOverride == 1 ? true : m_bShowInfoAvatar;
+
+ Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
+ SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
+ }
+ else {
+ m_bShowInfoAvatar = false;
+
+ switch (bAvatarMode) {
+ case 0: // globally on
+ m_bShowAvatar = true;
+ LBL_Check:
+ if (!m_hwndContactPic)
+ m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
+ break;
+ case 2: // globally OFF
+ m_bShowAvatar = false;
+ break;
+ case 3: // on, if present
+ case 1:
+ HBITMAP hbm = (m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr;
+ m_bShowAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
+ goto LBL_Check;
+ }
+
+ if (m_bShowAvatar)
+ m_bShowAvatar = hideOverride == 0 ? 0 : m_bShowAvatar;
+ else
+ m_bShowAvatar = hideOverride == 1 ? 1 : m_bShowAvatar;
+
+ // reloads avatars
+ if (m_hwndPanelPic) { // shows contact or user picture, depending on panel visibility
+ SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
+ Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
+ }
+ else Utils::setAvatarContact(m_hwndContactPic, m_hContact);
+ }
+ return m_bShowAvatar;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::GetClientIcon()
+{
+ if (m_hClientIcon)
+ DestroyIcon(m_hClientIcon);
+
+ m_hClientIcon = nullptr;
+ if (ServiceExists(MS_FP_GETCLIENTICONT)) {
+ ptrW tszMirver(db_get_wsa(m_cache->getActiveContact(), m_cache->getActiveProto(), "MirVer"));
+ if (tszMirver)
+ m_hClientIcon = Finger_GetClientIcon(tszMirver, 1);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON CMsgDialog::GetMyContactIcon(const CMOption<bool> *opt)
+{
+ int bUseMeta = (opt == nullptr) ? false : M.GetByte(opt->GetDBSettingName(), mir_strcmp(opt->GetDBSettingName(), "MetaiconTab") == 0);
+ if (bUseMeta)
+ return Skin_LoadProtoIcon(m_cache->getProto(), m_cache->getStatus());
+ return Skin_LoadProtoIcon(m_cache->getActiveProto(), m_cache->getActiveStatus());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::GetMyNick()
+{
+ ptrW tszNick(Contact::GetInfo(CNF_CUSTOMNICK, 0, m_cache->getActiveProto()));
+ if (tszNick == nullptr)
+ tszNick = Contact::GetInfo(CNF_NICK, 0, m_cache->getActiveProto());
+ if (tszNick != nullptr) {
+ if (mir_wstrlen(tszNick) == 0 || !mir_wstrcmp(tszNick, TranslateT("'(Unknown contact)'")))
+ wcsncpy_s(m_wszMyNickname, (m_myUin[0] ? m_myUin : TranslateT("'(Unknown contact)'")), _TRUNCATE);
+ else
+ wcsncpy_s(m_wszMyNickname, tszNick, _TRUNCATE);
+ }
+ else wcsncpy_s(m_wszMyNickname, L"<undef>", _TRUNCATE); // same here
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve both buddys and my own UIN for a message session and store them in the message
+// window *dat respects metacontacts and uses the current protocol if the contact is a MC
+
+void CMsgDialog::GetMYUIN()
+{
+ ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, 0, m_cache->getActiveProto()));
+ if (uid != nullptr)
+ wcsncpy_s(m_myUin, uid, _TRUNCATE);
+ else
+ m_myUin[0] = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns the status of Send button
+
+LRESULT CMsgDialog::GetSendButtonState()
+{
+ return m_btnOk.SendMsg(BUTTONGETSTATEID, TRUE, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reads send format and configures the toolbar buttons
+// if mode == 0, int only configures the buttons and does not change send format
+
+void CMsgDialog::GetSendFormat()
+{
+ m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
+ if (m_SendFormat == -1) // per contact override to disable it..
+ m_SendFormat = 0;
+ else if (m_SendFormat == 0)
+ m_SendFormat = g_plugin.bSendFormat;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON CMsgDialog::GetXStatusIcon() const
+{
+ uint8_t xStatus = m_cache->getXStatusId();
+ if (xStatus == 0)
+ return nullptr;
+
+ if (!ProtoServiceExists(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON))
+ return nullptr;
+
+ return (HICON)(CallProtoService(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON, xStatus, 0));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// paste contents of the clipboard into the message input area and send it immediately
+
+void CMsgDialog::HandlePasteAndSend()
+{
+ // is feature disabled?
+ if (!g_plugin.bPasteAndSend) {
+ ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending messages' section"));
+ return;
+ }
+
+ m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0);
+ if (GetWindowTextLength(m_message.GetHwnd()) > 0)
+ SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert the avatar bitmap to icon format so that it can be used on the task bar
+// tries to keep correct aspect ratio of the avatar image
+//
+// @param dat: _MessageWindowData* pointer to the window data
+// @return HICON: the icon handle
+
+HICON CMsgDialog::IconFromAvatar() const
+{
+ if (!ServiceExists(MS_AV_GETAVATARBITMAP))
+ return nullptr;
+
+ AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, m_hContact, 0);
+ if (ace == nullptr || ace->hbmPic == nullptr)
+ return nullptr;
+
+ LONG lIconSize = Win7Taskbar->getIconSize();
+ double dNewWidth, dNewHeight;
+ Utils::scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize);
+
+ // resize picture to fit it on the task bar, use an image list for converting it to
+ // 32bpp icon format. hTaskbarIcon will cache it until avatar is changed
+ HBITMAP hbmResized = ::Image_Resize(ace->hbmPic, RESIZEBITMAP_STRETCH, dNewWidth, dNewHeight);
+ HIMAGELIST hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0);
+
+ RECT rc = { 0, 0, lIconSize, lIconSize };
+
+ HDC hdc = ::GetDC(m_pContainer->m_hwnd);
+ HDC dc = ::CreateCompatibleDC(hdc);
+ HDC dcResized = ::CreateCompatibleDC(hdc);
+
+ ReleaseDC(m_pContainer->m_hwnd, hdc);
+
+ HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc);
+ HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew));
+ HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized));
+
+ LONG ix = (lIconSize - (LONG)dNewWidth) / 2;
+ LONG iy = (lIconSize - (LONG)dNewHeight) / 2;
+ CSkin::m_default_bf.SourceConstantAlpha = M.GetByte("taskBarIconAlpha", 255);
+ GdiAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
+
+ CSkin::m_default_bf.SourceConstantAlpha = 255;
+ ::SelectObject(dc, hbmOld);
+ ::ImageList_Add(hIml_c, hbmNew, nullptr);
+ ::DeleteObject(hbmNew);
+ ::DeleteDC(dc);
+
+ ::SelectObject(dcResized, hbmOldResized);
+ if (hbmResized != ace->hbmPic)
+ ::DeleteObject(hbmResized);
+ ::DeleteDC(dcResized);
+ HICON hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL);
+ ::ImageList_RemoveAll(hIml_c);
+ ::ImageList_Destroy(hIml_c);
+ return hIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// is window active or not?
+
+bool CMsgDialog::IsActive() const
+{
+ return m_pContainer->IsActive() && m_pContainer->m_hwndActive == m_hwnd;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// read keyboard state and return the state of the modifier keys
+
+void CMsgDialog::KbdState(bool &isShift, bool &isControl, bool &isAlt)
+{
+ GetKeyboardState(kstate);
+ isShift = (kstate[VK_SHIFT] & 0x80) != 0;
+ isControl = (kstate[VK_CONTROL] & 0x80) != 0;
+ isAlt = (kstate[VK_MENU] & 0x80) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LimitMessageText(int iLen)
+{
+ if (this != nullptr)
+ m_message.SendMsg(EM_EXLIMITTEXT, 0, iLen);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadContactAvatar()
+{
+ m_ace = Utils::loadAvatarFromAVS(m_bIsMeta ? db_mc_getSrmmSub(m_hContact) : m_hContact);
+
+ BITMAP bm;
+ if (m_ace && m_ace->hbmPic)
+ GetObject(m_ace->hbmPic, sizeof(bm), &bm);
+ else if (m_ace == nullptr)
+ GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm);
+ else
+ return;
+
+ AdjustBottomAvatarDisplay();
+ CalcDynamicAvatarSize(&bm);
+
+ if (!m_pPanel.isActive() || m_pContainer->cfg.avatarMode == 3) {
+ m_iRealAvatarHeight = 0;
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ }
+ else if (m_pPanel.isActive())
+ GetAvatarVisibility();
+
+ if (m_pWnd != nullptr)
+ m_pWnd->verifyDwmState();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadOwnAvatar()
+{
+ if (ServiceExists(MS_AV_GETMYAVATAR))
+ m_ownAce = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(m_cache->getActiveProto()));
+ else
+ m_ownAce = nullptr;
+
+ if (m_ownAce)
+ m_hOwnPic = m_ownAce->hbmPic;
+ else
+ m_hOwnPic = PluginConfig.g_hbmUnknown;
+
+ if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
+ BITMAP bm;
+
+ m_iRealAvatarHeight = 0;
+ AdjustBottomAvatarDisplay();
+ GetObject(m_hOwnPic, sizeof(bm), &bm);
+ CalcDynamicAvatarSize(&bm);
+ Resize();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSettings()
+{
+ m_clrInputBG = m_pContainer->m_theme.inputbg;
+ LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSplitter()
+{
+ if (m_bIsAutosizingInput) {
+ m_iSplitterY = (m_pContainer->cfg.flags.m_bBottomToolbar) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46);
+
+ if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+ m_iSplitterY += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2);
+ return;
+ }
+
+ if (!m_bSplitterOverride) {
+ if (!m_pContainer->cfg.fPrivate)
+ m_iSplitterY = (int)M.GetDword("splitsplity", 60);
+ else
+ m_iSplitterY = m_pContainer->cfg.iSplitterY;
+ }
+ else m_iSplitterY = (int)M.GetDword(m_hContact, "splitsplity", M.GetDword("splitsplity", 60));
+
+ if (m_iSplitterY < MINSPLITTERY)
+ m_iSplitterY = 150;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LogEvent(DBEVENTINFO &dbei)
+{
+ if (m_iLogMode != WANT_BUILTIN_LOG) {
+ dbei.flags |= DBEF_TEMPORARY;
+
+ MEVENT hDbEvent = db_event_add(m_hContact, &dbei);
+ if (hDbEvent) {
+ m_pLog->LogEvents(hDbEvent, 1, true);
+ db_event_delete(hDbEvent);
+ }
+ }
+ else LOG()->LogEvents(0, 1, true, &dbei);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// draw various elements of the message window, like avatar(s), info panel fields
+// and the color formatting menu
+
+int CMsgDialog::MsgWindowDrawHandler(DRAWITEMSTRUCT *dis)
+{
+ if ((dis->hwndItem == GetDlgItem(m_hwnd, IDC_CONTACTPIC) && m_bShowAvatar) || (dis->hwndItem == m_hwnd && m_pPanel.isActive())) {
+ HBITMAP hbmAvatar = m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown;
+ if (hbmAvatar == nullptr)
+ return TRUE;
+
+ int top, cx, cy;
+ RECT rcClient, rcFrame;
+ bool bPanelPic = (dis->hwndItem == m_hwnd);
+ if (bPanelPic && !m_bShowInfoAvatar)
+ return TRUE;
+
+ RECT rc;
+ GetClientRect(m_hwnd, &rc);
+ if (bPanelPic) {
+ rcClient = dis->rcItem;
+ cx = (rcClient.right - rcClient.left);
+ cy = (rcClient.bottom - rcClient.top) + 1;
+ }
+ else {
+ GetClientRect(dis->hwndItem, &rcClient);
+ cx = rcClient.right;
+ cy = rcClient.bottom;
+ }
+
+ if (cx < 5 || cy < 5)
+ return TRUE;
+
+ HDC hdcDraw = CreateCompatibleDC(dis->hDC);
+ HBITMAP hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy);
+ HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw);
+
+ bool bAero = M.isAero();
+
+ HRGN clipRgn = nullptr;
+ HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcDraw, bAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE));
+ rcFrame = rcClient;
+
+ if (!bPanelPic) {
+ top = (cy - m_pic.cy) / 2;
+ RECT rcEdge = { 0, top, m_pic.cx, top + m_pic.cy };
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, hdcDraw);
+ else if (PluginConfig.m_fillColor) {
+ HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor);
+ FillRect(hdcDraw, &rcFrame, br);
+ DeleteObject(br);
+ }
+ else if (bAero && CSkin::m_pCurrentAeroEffect) {
+ COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh :
+ (CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0);
+
+ HBRUSH br = CreateSolidBrush(clr);
+ FillRect(hdcDraw, &rcFrame, br);
+ DeleteObject(br);
+ }
+ else FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE));
+
+ HPEN hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr);
+ HPEN hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder);
+
+ if (CSkin::m_bAvatarBorderType == 1)
+ Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom);
+ else if (CSkin::m_bAvatarBorderType == 2) {
+ clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, 6, 6);
+ SelectClipRgn(hdcDraw, clipRgn);
+
+ HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
+ FrameRgn(hdcDraw, clipRgn, hbr, 1, 1);
+ DeleteObject(hbr);
+ DeleteObject(clipRgn);
+ }
+
+ SelectObject(hdcDraw, hPenOld);
+ DeleteObject(hPenBorder);
+ }
+
+ if (bPanelPic) {
+ bool bBorder = (CSkin::m_bAvatarBorderType ? true : false);
+
+ int border_off = bBorder ? 1 : 0;
+ int iMaxHeight = m_iPanelAvatarY - (bBorder ? 2 : 0);
+ int iMaxWidth = m_iPanelAvatarX - (bBorder ? 2 : 0);
+
+ rcFrame.left = rcFrame.top = 0;
+ rcFrame.right = (rcClient.right - rcClient.left);
+ rcFrame.bottom = (rcClient.bottom - rcClient.top);
+
+ rcFrame.left = rcFrame.right - (LONG)m_iPanelAvatarX;
+ rcFrame.bottom = (LONG)m_iPanelAvatarY;
+
+ int height_off = (cy - iMaxHeight - (bBorder ? 2 : 0)) / 2;
+ rcFrame.top += height_off;
+ rcFrame.bottom += height_off;
+
+ SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, bAero ? TRUE : FALSE);
+ SetWindowPos(m_hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off,
+ iMaxWidth, iMaxHeight, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING);
+ }
+
+ SelectObject(hdcDraw, hOldBrush);
+ if (!bPanelPic)
+ BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY);
+ SelectObject(hdcDraw, hbmOld);
+ DeleteObject(hbmDraw);
+ DeleteDC(hdcDraw);
+ return TRUE;
+ }
+
+ if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(m_hwnd, IDC_LOGFROZENTEXT)) {
+ wchar_t szWindowText[256];
+ if (CSkin::m_skinEnabled) {
+ SetTextColor(dis->hDC, CSkin::m_DefaultFontColor);
+ CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
+ }
+ else {
+ SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
+ CSkin::FillBack(dis->hDC, &dis->rcItem);
+ }
+ GetWindowText(dis->hwndItem, szWindowText, _countof(szWindowText));
+ szWindowText[255] = 0;
+ SetBkMode(dis->hDC, TRANSPARENT);
+ DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS);
+ return TRUE;
+ }
+
+ if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICERRORICON)) {
+ if (CSkin::m_skinEnabled)
+ CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
+ else
+ CSkin::FillBack(dis->hDC, &dis->rcItem);
+ DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8,
+ PluginConfig.g_iconErr, 16, 16, 0, nullptr, DI_NORMAL);
+ return TRUE;
+ }
+
+ if (dis->CtlType == ODT_MENU && m_pPanel.isHovered()) {
+ DrawMenuItem(dis, (HICON)dis->itemData, 0);
+ return TRUE;
+ }
+
+ return Menu_DrawItem((LPARAM)dis);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMsgDialog::MsgWindowUpdateMenu(HMENU submenu, int menuID)
+{
+ bool bInfoPanel = m_pPanel.isActive();
+
+ if (menuID == MENU_TABCONTEXT) {
+ EnableMenuItem(submenu, ID_TABMENU_LEAVECHATROOM, (isChat() && ProtoServiceExists(m_szProto, PS_LEAVECHAT)) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) ? MF_GRAYED : MF_ENABLED);
+ EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M.GetDword(m_hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED);
+ }
+ else if (menuID == MENU_PICMENU) {
+ wchar_t *szText = nullptr;
+ char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+ HMENU visMenu = GetSubMenu(submenu, 0);
+ BOOL picValid = bInfoPanel ? (m_hOwnPic != nullptr) : (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown);
+
+ MENUITEMINFO mii = { 0 };
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STRING;
+
+ EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, picValid ? MF_ENABLED : MF_GRAYED);
+
+ CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
+
+ CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, PluginConfig.m_bAlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED);
+ if (!bInfoPanel) {
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
+ szText = TranslateT("Contact picture settings...");
+ EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED);
+ }
+ else {
+ EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED);
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED);
+ szText = TranslateT("Set your avatar...");
+ }
+ mii.dwTypeData = szText;
+ mii.cch = (int)mir_wstrlen(szText) + 1;
+ SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii);
+ }
+ else if (menuID == MENU_PANELPICMENU) {
+ HMENU visMenu = GetSubMenu(submenu, 0);
+ char avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+
+ CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
+
+ EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update state of the container - this is called whenever a tab becomes active, no matter how and
+// deals with various things like updating the title bar, removing flashing icons, updating the
+// session list, switching the keyboard layout (autolocale active) and the general container status.
+//
+// it protects itself from being called more than once per session activation and is valid for
+// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c)
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMsgDialog::MsgWindowMenuHandler(int selection, int menuId)
+{
+ if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) {
+ switch (selection) {
+ case ID_TABMENU_ATTACHTOCONTAINER:
+ SelectContainer();
+ return 1;
+ case ID_TABMENU_CONTAINEROPTIONS:
+ m_pContainer->OptionsDialog();
+ return 1;
+ case ID_TABMENU_CLOSECONTAINER:
+ SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 0);
+ return 1;
+ case ID_TABMENU_CLOSETAB:
+ PostMessage(m_hwnd, WM_CLOSE, 1, 0);
+ return 1;
+ case ID_TABMENU_SAVETABPOSITION:
+ db_set_dw(m_hContact, SRMSGMOD_T, "tabindex", m_iTabID * 100);
+ break;
+ case ID_TABMENU_CLEARSAVEDTABPOSITION:
+ db_unset(m_hContact, SRMSGMOD_T, "tabindex");
+ break;
+ case ID_TABMENU_LEAVECHATROOM:
+ if (isChat()) {
+ char *szProto = Proto_GetBaseAccountName(m_hContact);
+ if (szProto)
+ CallProtoService(szProto, PS_LEAVECHAT, m_hContact, 0);
+ }
+ return 1;
+
+ case ID_VISIBILITY_DEFAULT:
+ case ID_VISIBILITY_HIDDENFORTHISCONTACT:
+ case ID_VISIBILITY_VISIBLEFORTHISCONTACT:
+ {
+ uint8_t avOverrideMode;
+ if (selection == ID_VISIBILITY_DEFAULT)
+ avOverrideMode = -1;
+ else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT)
+ avOverrideMode = 1;
+ else
+ avOverrideMode = 0;
+ db_set_b(m_hContact, SRMSGMOD_T, "hideavatar", avOverrideMode);
+ }
+
+ ShowPicture(false);
+ Resize();
+ DM_ScrollToBottom(0, 1);
+ return 1;
+
+ case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH:
+ PluginConfig.m_bAlwaysFullToolbarWidth = !PluginConfig.m_bAlwaysFullToolbarWidth;
+ db_set_b(0, SRMSGMOD_T, "alwaysfulltoolbar", (uint8_t)PluginConfig.m_bAlwaysFullToolbarWidth);
+ Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
+ break;
+
+ case ID_PICMENU_SAVETHISPICTUREAS:
+ if (m_pPanel.isActive())
+ SaveAvatarToFile(m_hOwnPic, 1);
+ else if (m_ace)
+ SaveAvatarToFile(m_ace->hbmPic, 0);
+ break;
+
+ case ID_PANELPICMENU_SAVETHISPICTUREAS:
+ if (m_ace)
+ SaveAvatarToFile(m_ace->hbmPic, 0);
+ break;
+
+ case ID_PICMENU_SETTINGS:
+ if (menuId == MENU_PICMENU) {
+ if (m_pPanel.isActive()) {
+ if (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0))
+ CallService(MS_AV_SETMYAVATARW, (WPARAM)(m_cache->getActiveProto()), 0);
+ return TRUE;
+ }
+ }
+ CallService(MS_AV_CONTACTOPTIONS, m_hContact, (LPARAM)m_hwnd);
+ return 1;
+ }
+ }
+ else if (menuId == MENU_LOGMENU) {
+ switch (selection) {
+ case ID_MESSAGELOGSETTINGS_GLOBAL:
+ g_plugin.openOptions(nullptr, L"Message sessions", L"Message log");
+ return 1;
+
+ case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
+ CallService(MS_TABMSG_SETUSERPREFS, m_hContact, 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::NotifyDeliveryFailure() const
+{
+ if (!g_plugin.bErrorPopup || !Popup_Enabled())
+ return;
+
+ POPUPDATAW ppd = {};
+ ppd.lchContact = m_hContact;
+ ppd.PluginWindowProc = Utils::PopupDlgProcError;
+ ppd.lchIcon = PluginConfig.g_iconErr;
+ ppd.iSeconds = NEN::iDelayErr;
+ if (!NEN::bColDefaultErr) {
+ ppd.colorText = NEN::colTextErr;
+ ppd.colorBack = NEN::colBackErr;
+ }
+ wcsncpy_s(ppd.lpwzContactName, m_cache->getNick(), _TRUNCATE);
+ wcsncpy_s(ppd.lpwzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE);
+ PUAddPopupW(&ppd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::PlayIncomingSound() const
+{
+ int iPlay = m_pContainer->MustPlaySound(this);
+ if (iPlay) {
+ if (GetForegroundWindow() == m_pContainer->m_hwnd && m_pContainer->m_hwndActive == m_hwnd)
+ Skin_PlaySound("RecvMsgActive");
+ else
+ Skin_PlaySound("RecvMsgInactive");
+ }
+}
+
+void CMsgDialog::RemakeLog()
+{
+ m_szMicroLf[0] = 0;
+ m_lastEventTime = 0;
+ m_iLastEventType = -1;
+ StreamEvents(m_hDbEventFirst, -1, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// saves a contact picture to disk
+// takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar)
+// requires AVS service (Miranda 0.7+)
+
+void CMsgDialog::SaveAvatarToFile(HBITMAP hbm, int isOwnPic)
+{
+ wchar_t szFinalFilename[MAX_PATH];
+ time_t t = time(0);
+ struct tm *lt = localtime(&t);
+ uint32_t setView = 1;
+
+ wchar_t szTimestamp[100];
+ mir_snwprintf(szTimestamp, L"%04u %02u %02u_%02u%02u", lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min);
+
+ wchar_t *szProto = mir_a2u(m_cache->getActiveProto());
+
+ wchar_t szFinalPath[MAX_PATH];
+ mir_snwprintf(szFinalPath, L"%s\\%s", M.getSavedAvatarPath(), szProto);
+ mir_free(szProto);
+
+ if (CreateDirectory(szFinalPath, nullptr) == 0) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS) {
+ MessageBox(nullptr, TranslateT("Error creating destination directory"),
+ TranslateT("Save contact picture"), MB_OK | MB_ICONSTOP);
+ return;
+ }
+ }
+
+ wchar_t szBaseName[MAX_PATH];
+ if (isOwnPic)
+ mir_snwprintf(szBaseName, L"My Avatar_%s", szTimestamp);
+ else
+ mir_snwprintf(szBaseName, L"%s_%s", m_cache->getNick(), szTimestamp);
+
+ mir_snwprintf(szFinalFilename, L"%s.png", szBaseName);
+
+ // do not allow / or \ or % in the filename
+ Utils::sanitizeFilename(szFinalFilename);
+
+ wchar_t filter[MAX_PATH];
+ mir_snwprintf(filter, L"%s%c*.bmp;*.png;*.jpg;*.gif%c%c", TranslateT("Image files"), 0, 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lpstrDefExt = L"png";
+ ofn.lpstrFilter = filter;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
+ ofn.lpfnHook = OpenFileSubclass;
+ ofn.lStructSize = sizeof(ofn);
+ ofn.lpstrFile = szFinalFilename;
+ ofn.lpstrInitialDir = szFinalPath;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lCustData = (LPARAM)& setView;
+ if (GetSaveFileName(&ofn)) {
+ if (PathFileExists(szFinalFilename))
+ if (MessageBox(nullptr, TranslateT("The file exists. Do you want to overwrite it?"), TranslateT("Save contact picture"), MB_YESNO | MB_ICONQUESTION) == IDNO)
+ return;
+
+ IMGSRVC_INFO ii;
+ ii.cbSize = sizeof(ii);
+ ii.pwszName = szFinalFilename;
+ ii.hbm = hbm;
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_UNKNOWN; // get the format from the filename extension. png is default.
+ Image_Save(&ii);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::SaveSplitter()
+{
+ if (m_bIsAutosizingInput)
+ return;
+
+ if (m_iSplitterY < DPISCALEY_S(MINSPLITTERY) || m_iSplitterY < 0)
+ m_iSplitterY = DPISCALEY_S(MINSPLITTERY);
+
+ if (m_bSplitterOverride)
+ db_set_dw(m_hContact, SRMSGMOD_T, "splitsplity", m_iSplitterY);
+ else {
+ if (m_pContainer->cfg.fPrivate)
+ m_pContainer->cfg.iSplitterY = m_iSplitterY;
+ else
+ db_set_dw(0, SRMSGMOD_T, "splitsplity", m_iSplitterY);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send a pasted bitmap by file transfer.
+
+static LIST<wchar_t> vTempFilenames(5);
+
+void CMsgDialog::SendHBitmapAsFile(HBITMAP hbmp) const
+{
+ const wchar_t *mirandatempdir = L"Miranda";
+ const wchar_t *filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg";
+ wchar_t filename[MAX_PATH];
+ size_t tempdirlen = GetTempPath(MAX_PATH, filename);
+ bool fSend = true;
+
+ const char *szProto = m_cache->getActiveProto();
+ int wMyStatus = Proto_GetStatus(szProto);
+
+ uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+ uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+
+ // check protocol capabilities, status modes and visibility lists (privacy)
+ // to determine whether the file can be sent. Throw a warning if any of
+ // these checks fails.
+ if (!(protoCaps & PF1_FILESEND))
+ fSend = false;
+
+ if ((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == m_cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES)))
+ fSend = false;
+
+ if (protoCaps & PF1_VISLIST && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+ fSend = false;
+
+ if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+ fSend = false;
+
+ if (!fSend) {
+ CWarning::show(CWarning::WARN_SENDFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
+ return;
+ }
+
+ if (tempdirlen <= 0 || tempdirlen >= MAX_PATH - mir_wstrlen(mirandatempdir) - mir_wstrlen(filenametemplate) - 2) // -2 is because %Y takes 4 symbols
+ filename[0] = 0; // prompt for a new name
+ else {
+ mir_wstrcpy(filename + tempdirlen, mirandatempdir);
+ if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, nullptr) == 0)
+ filename[0] = 0;
+ else {
+ tempdirlen = mir_wstrlen(filename);
+
+ time_t rawtime;
+ time(&rawtime);
+ const tm *timeinfo;
+ timeinfo = _localtime32((__time32_t *)& rawtime);
+ wcsftime(filename + tempdirlen, MAX_PATH - tempdirlen, filenametemplate, timeinfo);
+ size_t firstnumberpos = tempdirlen + 14;
+ size_t lastnumberpos = tempdirlen + 20;
+ while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) { // while it exists
+ for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--)
+ if (filename[pos]++ != '9')
+ break;
+ else
+ if (pos == firstnumberpos)
+ filename[0] = 0; // all filenames exist => prompt for a new name
+ else
+ filename[pos] = '0';
+ }
+ }
+ }
+
+ if (filename[0] == 0) { // prompting to save
+ wchar_t filter[MAX_PATH];
+ mir_snwprintf(filter, L"%s%c*.jpg%c%c", TranslateT("JPEG-compressed images"), 0, 0, 0);
+
+ OPENFILENAME dlg;
+ dlg.lStructSize = sizeof(dlg);
+ dlg.lpstrFilter = filter;
+ dlg.nFilterIndex = 1;
+ dlg.lpstrFile = filename;
+ dlg.nMaxFile = MAX_PATH;
+ dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+ dlg.lpstrDefExt = L"jpg";
+ if (!GetSaveFileName(&dlg))
+ return;
+ }
+
+ IMGSRVC_INFO ii;
+ ii.cbSize = sizeof(ii);
+ ii.hbm = hbmp;
+ ii.pwszName = filename;
+ ii.dwMask = IMGI_HBITMAP;
+ ii.fif = FIF_JPEG;
+ if (!Image_Save(&ii)) {
+ CWarning::show(CWarning::WARN_SAVEFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
+ return;
+ }
+
+ vTempFilenames.insert(mir_wstrdup(filename));
+
+ wchar_t *ppFiles[2] = { filename, nullptr };
+ CallService(MS_FILE_SENDSPECIFICFILEST, m_cache->getActiveContact(), (LPARAM)&ppFiles);
+}
+
+// remove all temporary files created by the "send clipboard as file" feature.
+void TSAPI CleanTempFiles()
+{
+ for (auto &it : vTempFilenames) {
+ DeleteFileW(it);
+ mir_free(it);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Sets a status bar text for a contact
+
+void CMsgDialog::SetStatusText(const wchar_t *wszText, HICON hIcon)
+{
+ if (wszText != nullptr) {
+ m_bStatusSet = true;
+ m_szStatusText = wszText;
+ m_szStatusIcon = hIcon;
+ }
+ else {
+ m_bStatusSet = false;
+ m_szStatusText.Empty();
+ m_szStatusIcon = nullptr;
+ }
+
+ tabUpdateStatusBar();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// subclassing for the message filter dialog (set and configure event filters for the
+// current session
+
+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
+};
+
+INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CMsgDialog *pDlg = (CMsgDialog *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ pDlg = (CMsgDialog *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ {
+ uint32_t dwMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", 0);
+ uint32_t dwFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", 0);
+
+ uint32_t dwPopupMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", 0);
+ uint32_t dwPopupFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", 0);
+
+ uint32_t dwTrayMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", 0);
+ uint32_t dwTrayFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", 0);
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+ CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+ CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+ }
+ }
+ return FALSE;
+
+ 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_CLOSE:
+ if (wParam == 1 && lParam == 1 && pDlg) {
+ int iFlags = 0;
+ uint32_t dwMask = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i);
+ dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+ iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+ }
+
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ if (dwMask == 0) {
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterFlags");
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterMask");
+ }
+ else {
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", iFlags);
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", dwMask);
+ }
+
+ dwMask = iFlags = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i);
+ dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+ iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+ }
+
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ if (dwMask == 0) {
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupFlags");
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupMask");
+ }
+ else {
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", iFlags);
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", dwMask);
+ }
+
+ dwMask = iFlags = 0;
+
+ for (int i = 0; i < _countof(_eventorder); i++) {
+ int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i);
+ dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+ iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+ }
+ if (iFlags & GC_EVENT_ADDSTATUS)
+ iFlags |= GC_EVENT_REMOVESTATUS;
+
+ if (dwMask == 0) {
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags");
+ db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask");
+ }
+ else {
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", iFlags);
+ db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", dwMask);
+ }
+ Chat_SetFilters(pDlg->getChat());
+
+ if (pDlg->m_bFilterEnabled) {
+ if (pDlg->m_iLogFilterFlags == 0)
+ pDlg->m_btnFilter.Click();
+ pDlg->RedrawLog();
+ db_set_b(pDlg->m_hContact, CHAT_MODULE, "FilterEnabled", pDlg->m_bFilterEnabled);
+ }
+ }
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+ break;
+ }
+ return FALSE;
+}
+
+void CMsgDialog::ShowFilterMenu()
+{
+ m_hwndFilter = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_pContainer->m_hwnd, FilterWndProc, (LPARAM)this);
+ TranslateDialogDefault(m_hwndFilter);
+
+ RECT rcFilter, rcLog;
+ GetClientRect(m_hwndFilter, &rcFilter);
+ GetWindowRect(m_pLog->GetHwnd(), &rcLog);
+
+ POINT pt;
+ pt.x = rcLog.right; pt.y = rcLog.bottom;
+ ScreenToClient(m_pContainer->m_hwnd, &pt);
+
+ SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::ShowPicture(bool showNewPic)
+{
+ if (!m_pPanel.isActive())
+ m_pic.cy = m_pic.cx = DPISCALEY_S(60);
+
+ if (showNewPic) {
+ if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
+ if (!m_hwndPanelPic) {
+ InvalidateRect(m_hwnd, nullptr, TRUE);
+ UpdateWindow(m_hwnd);
+ Resize();
+ }
+ return;
+ }
+ AdjustBottomAvatarDisplay();
+ }
+ else {
+ m_bShowAvatar = !m_bShowAvatar;
+ db_set_b(m_hContact, SRMSGMOD_T, "MOD_ShowPic", m_bShowAvatar);
+ }
+
+ RECT rc;
+ GetWindowRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), &rc);
+ if (m_minEditBoxSize.cy + DPISCALEY_S(3) > m_iSplitterY)
+ SplitterMoved(rc.bottom - m_minEditBoxSize.cy, GetDlgItem(m_hwnd, IDC_SPLITTERY));
+ if (!showNewPic)
+ SetDialogToType();
+ else
+ Resize();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show a modified context menu for the richedit control(s)
+
+void CMsgDialog::ShowPopupMenu(const CCtrlBase &pCtrl, POINT pt)
+{
+ CHARRANGE sel, all = { 0, -1 };
+
+ HMENU hSubMenu, hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
+ if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
+ hSubMenu = GetSubMenu(hMenu, 0);
+ else {
+ hSubMenu = GetSubMenu(hMenu, 2);
+ EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, m_SendFormat != 0 ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, g_plugin.bPasteAndSend ? MF_ENABLED : MF_GRAYED);
+ CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED);
+ EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, m_pContainer->m_hwndStatus ? MF_ENABLED : MF_GRAYED);
+ }
+ TranslateMenu(hSubMenu);
+ pCtrl.SendMsg(EM_EXGETSEL, 0, (LPARAM)& sel);
+ if (sel.cpMin == sel.cpMax) {
+ EnableMenuItem(hSubMenu, IDM_COPY, MF_GRAYED);
+ EnableMenuItem(hSubMenu, IDM_QUOTE, MF_GRAYED);
+ if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
+ EnableMenuItem(hSubMenu, IDM_CUT, MF_GRAYED);
+ }
+
+ if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) {
+ InsertMenuA(hSubMenu, 6, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+ CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, m_bScrollingDisabled ? MF_CHECKED : MF_UNCHECKED);
+ }
+
+ MessageWindowPopupData mwpd;
+ // First notification
+ mwpd.uType = MSG_WINDOWPOPUP_SHOWING;
+ mwpd.uFlags = (pCtrl.GetCtrlId() == IDC_SRMM_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT);
+ mwpd.hContact = m_hContact;
+ mwpd.hwnd = pCtrl.GetHwnd();
+ mwpd.hMenu = hSubMenu;
+ mwpd.selection = 0;
+ mwpd.pt = pt;
+ NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
+
+ int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
+
+ // Second notification
+ mwpd.selection = iSelection;
+ mwpd.uType = MSG_WINDOWPOPUP_SELECTED;
+ NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
+
+ switch (iSelection) {
+ case IDM_COPY:
+ pCtrl.SendMsg(WM_COPY, 0, 0);
+ break;
+ case IDM_CUT:
+ pCtrl.SendMsg(WM_CUT, 0, 0);
+ break;
+ case IDM_PASTE:
+ case IDM_PASTEFORMATTED:
+ if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
+ pCtrl.SendMsg(EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0);
+ break;
+ case IDM_COPYALL:
+ pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
+ pCtrl.SendMsg(WM_COPY, 0, 0);
+ pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& sel);
+ break;
+ case IDM_QUOTE:
+ SendMessage(m_hwnd, WM_COMMAND, IDC_QUOTE, 0);
+ break;
+ case IDM_SELECTALL:
+ pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
+ break;
+ case IDM_CLEAR:
+ tabClearLog();
+ break;
+ case ID_LOG_FREEZELOG:
+ SendDlgItemMessage(m_hwnd, IDC_SRMM_LOG, WM_KEYDOWN, VK_F12, 0);
+ break;
+ case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR:
+ PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator;
+ db_set_b(0, SRMSGMOD_T, "msgsizebar", (uint8_t)PluginConfig.m_visualMessageSizeIndicator);
+ Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 0);
+ Resize();
+ if (m_pContainer->m_hwndStatus)
+ RedrawWindow(m_pContainer->m_hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ break;
+ case ID_EDITOR_PASTEANDSENDIMMEDIATELY:
+ HandlePasteAndSend();
+ break;
+ }
+
+ if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
+ RemoveMenu(hSubMenu, 7, MF_BYPOSITION);
+ DestroyMenu(hMenu);
+}
+
+void CMsgDialog::SplitterMoved(int coord, HWND hwnd)
+{
+ POINT pt;
+ RECT rc;
+
+ switch (GetDlgCtrlID(hwnd)) {
+ case IDC_MULTISPLITTER:
+ GetClientRect(m_hwnd, &rc);
+ pt.x = coord;
+ pt.y = 0;
+ ScreenToClient(m_hwnd, &pt);
+ {
+ int oldSplitterX = m_iMultiSplit;
+ m_iMultiSplit = rc.right - pt.x;
+ if (m_iMultiSplit < 25)
+ m_iMultiSplit = 25;
+
+ if (m_iMultiSplit > ((rc.right - rc.left) - 80))
+ m_iMultiSplit = oldSplitterX;
+ }
+ Resize();
+ break;
+
+ case IDC_SPLITTERX:
+ GetClientRect(m_hwnd, &rc);
+ pt.x = coord, pt.y = 0;
+ ScreenToClient(m_hwnd, &pt);
+ {
+ int iSplitterX = rc.right - pt.x + 1;
+ if (iSplitterX < 35)
+ iSplitterX = 35;
+ if (iSplitterX > rc.right - rc.left - 35)
+ iSplitterX = rc.right - rc.left - 35;
+ m_pContainer->cfg.iSplitterX = iSplitterX;
+ }
+ Resize();
+ break;
+
+ case IDC_SPLITTERY:
+ GetClientRect(m_hwnd, &rc);
+ rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30);
+ pt.x = 0;
+ pt.y = coord;
+ ScreenToClient(m_hwnd, &pt);
+ {
+ int oldSplitterY = m_iSplitterY;
+ int oldDynaSplitter = m_dynaSplitter;
+
+ m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(23);
+
+ // attempt to fix splitter troubles..
+ // hardcoded limits... better solution is possible, but this works for now
+ int bottomtoolbarH = 0;
+ if (m_pContainer->cfg.flags.m_bBottomToolbar)
+ bottomtoolbarH = 22;
+
+ if (m_iSplitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) { // min splitter size
+ m_iSplitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH);
+ m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
+ DM_RecalcPictureSize();
+ }
+ else if (m_iSplitterY > (rc.bottom - rc.top)) {
+ m_iSplitterY = oldSplitterY;
+ m_dynaSplitter = oldDynaSplitter;
+ DM_RecalcPictureSize();
+ }
+ else {
+ m_dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11);
+ DM_RecalcPictureSize();
+ }
+ }
+ UpdateToolbarBG();
+ Resize();
+ break;
+
+ case IDC_PANELSPLITTER:
+ GetClientRect(m_pLog->GetHwnd(), &rc);
+
+ POINT pnt = { 0, coord };
+ ScreenToClient(m_hwnd, &pnt);
+ if ((pnt.y + 2 >= MIN_PANELHEIGHT + 2) && (pnt.y + 2 < 100) && (pnt.y + 2 < rc.bottom - 30))
+ m_pPanel.setHeight(pnt.y + 2, true);
+
+ RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
+ if (M.isAero())
+ InvalidateRect(GetParent(m_hwnd), nullptr, FALSE);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::StreamEvents(MEVENT hDbEventFirst, int count, bool bAppend)
+{
+ m_pLog->LogEvents(hDbEventFirst, count, bAppend);
+
+ DM_ScrollToBottom(0, 0);
+ if (bAppend && hDbEventFirst)
+ m_hDbEventLast = hDbEventFirst;
+ else
+ m_hDbEventLast = db_event_last(m_hContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sent by the select container dialog box when a container was selected...
+
+void CMsgDialog::SwitchToContainer(const wchar_t *szNewName)
+{
+ if (!mir_wstrcmp(szNewName, TranslateT("Default container")))
+ szNewName = CGlobals::m_default_container_name;
+
+ int iOldItems = TabCtrl_GetItemCount(m_hwndParent);
+ if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN))
+ return;
+
+ TContainerData *pNewContainer = FindContainerByName(szNewName);
+ if (pNewContainer == nullptr)
+ if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr)
+ return;
+
+ db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName);
+ PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact);
+ if (iOldItems > 1) // there were more than 1 tab, container is still valid
+ SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0);
+ SetForegroundWindow(pNewContainer->m_hwnd);
+ SetActiveWindow(pNewContainer->m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CMsgDialog::TabAutoComplete()
+{
+ LRESULT lResult = m_message.SendMsg(EM_GETSEL, 0, 0);
+ int start = LOWORD(lResult), end = HIWORD(lResult);
+ int origStart = start, origEnd = end;
+ m_message.SendMsg(EM_SETSEL, end, end);
+
+ GETTEXTEX gt = { 0 };
+ gt.codepage = 1200;
+ gt.flags = GTL_DEFAULT | GTL_PRECISE;
+ int iLen = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)& gt, 0);
+ if (iLen <= 0)
+ return false;
+
+ bool isTopic = false, isRoom = false;
+ wchar_t *pszText = (wchar_t *)mir_calloc((iLen + 10) * sizeof(wchar_t));
+
+ gt.flags = GT_DEFAULT;
+ gt.cb = (iLen + 9) * sizeof(wchar_t);
+ m_message.SendMsg(EM_GETTEXTEX, (WPARAM)& gt, (LPARAM)pszText);
+
+ if (m_wszSearchResult != nullptr) {
+ int cbResult = (int)mir_wstrlen(m_wszSearchResult);
+ if (start >= cbResult && !wcsnicmp(m_wszSearchResult, pszText + start - cbResult, cbResult)) {
+ start -= cbResult;
+ goto LBL_SkipEnd;
+ }
+ }
+
+ while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB)
+ start--;
+
+LBL_SkipEnd:
+ while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB)
+ end++;
+
+ if (pszText[start] == '#')
+ isRoom = true;
+ else {
+ int topicStart = start;
+ while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB))
+ topicStart--;
+ if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6])
+ isTopic = true;
+ }
+
+ if (m_wszSearchQuery == nullptr) {
+ m_wszSearchQuery = mir_wstrndup(pszText + start, end - start);
+ m_wszSearchResult = mir_wstrdup(m_wszSearchQuery);
+ m_pLastSession = nullptr;
+ }
+
+ const wchar_t *pszName = nullptr;
+ if (isTopic)
+ pszName = m_si->ptszTopic;
+ else if (isRoom) {
+ m_pLastSession = SM_FindSessionAutoComplete(m_si->pszModule, m_si, m_pLastSession, m_wszSearchQuery, m_wszSearchResult);
+ if (m_pLastSession != nullptr)
+ pszName = m_pLastSession->ptszName;
+ }
+ else pszName = g_chatApi.UM_FindUserAutoComplete(m_si, m_wszSearchQuery, m_wszSearchResult);
+
+ replaceStrW(m_wszSearchResult, nullptr);
+
+ if (pszName != nullptr) {
+ if (end != start) {
+ CMStringW szReplace;
+ if (!isRoom && !isTopic && start == 0) {
+ szReplace = pszName;
+ if (mir_wstrlen(g_Settings.pwszAutoText))
+ szReplace.Append(g_Settings.pwszAutoText);
+ szReplace.AppendChar(' ');
+ m_wszSearchResult = szReplace.Detach();
+ pszName = m_wszSearchResult;
+ }
+ else m_wszSearchResult = mir_wstrdup(pszName);
+
+ m_message.SendMsg(EM_SETSEL, start, end);
+ m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pszName);
+ }
+ else m_wszSearchResult = mir_wstrdup(pszName);
+
+ return true;
+ }
+
+ if (end != start) {
+ m_message.SendMsg(EM_SETSEL, start, end);
+ m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)m_wszSearchQuery);
+ }
+ m_message.SendMsg(EM_SETSEL, origStart, origEnd);
+ replaceStrW(m_wszSearchQuery, nullptr);
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::tabClearLog()
+{
+ if (isChat()) {
+ g_chatApi.LM_RemoveAll(&m_si->pLog, &m_si->pLogEnd);
+ m_si->iEventCount = 0;
+ m_si->LastTime = 0;
+ PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+ }
+
+ m_pLog->Clear();
+ m_hDbEventFirst = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CThumbBase *CMsgDialog::tabCreateThumb(CProxyWindow *pProxy) const
+{
+ if (isChat())
+ return new CThumbMUC(pProxy, m_si);
+
+ return new CThumbIM(pProxy);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update all status bar fields and force a redraw of the status bar.
+
+void CMsgDialog::tabUpdateStatusBar() const
+{
+ if (m_pContainer->m_hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
+ if (!isChat()) {
+ if (m_wszStatusBar[0]) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+ }
+ else if (m_bStatusSet) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+ }
+ else {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+ DM_UpdateLastMessage();
+ }
+ }
+ else {
+ if (m_bStatusSet) {
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+ }
+ else SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+ }
+ UpdateReadChars();
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+ SendMessage(m_pContainer->m_hwndStatus, WM_USER + 101, 0, (LPARAM)this);
+ }
+}
+
+int CMsgDialog::Typing(int secs)
+{
+ if (!AllowTyping())
+ return 0;
+
+ int preTyping = m_nTypeSecs != 0;
+
+ setTyping(m_nTypeSecs = (secs > 0) ? secs : 0);
+ if (m_nTypeSecs)
+ m_bShowTyping = 0;
+
+ return preTyping;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+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();
+ m_hTabIcon = m_hTabStatusIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateOptions()
+{
+ GetSendFormat();
+
+ DM_InitRichEdit();
+ m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0);
+
+ m_nickList.SetItemHeight(0, g_Settings.iNickListFontHeight);
+ InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
+
+ m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(m_bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0);
+
+ CSuper::UpdateOptions();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update the status bar field which displays the number of characters in the input area
+// and various indicators (caps lock, num lock, insert mode).
+
+void CMsgDialog::UpdateReadChars() const
+{
+ if (!m_pContainer->m_hwndStatus || m_pContainer->m_hwndActive != m_hwnd)
+ return;
+
+ int len;
+ if (isChat())
+ len = GetWindowTextLength(m_message.GetHwnd());
+ else {
+ // retrieve text length in UTF8 bytes, because this is the relevant length for most protocols
+ GETTEXTLENGTHEX gtxl = { 0 };
+ gtxl.codepage = CP_UTF8;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+ len = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
+ }
+
+ BOOL fCaps = (GetKeyState(VK_CAPITAL) & 1);
+ BOOL fNum = (GetKeyState(VK_NUMLOCK) & 1);
+
+ wchar_t szBuf[20]; szBuf[0] = 0;
+ if (m_bInsertMode)
+ mir_wstrcat(szBuf, L"O");
+ if (fCaps)
+ mir_wstrcat(szBuf, L"C");
+ if (fNum)
+ mir_wstrcat(szBuf, L"N");
+ if (m_bInsertMode || fCaps || fNum)
+ mir_wstrcat(szBuf, L" | ");
+
+ wchar_t buf[128];
+ mir_snwprintf(buf, L"%s%s %d/%d", szBuf, m_lcID, m_iOpenJobs, len);
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)buf);
+ if (PluginConfig.m_visualMessageSizeIndicator)
+ InvalidateRect(m_pContainer->m_hwndStatus, nullptr, FALSE);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateSaveAndSendButton()
+{
+ GETTEXTLENGTHEX gtxl = { 0 };
+ gtxl.codepage = CP_UTF8;
+ gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+ int len = SendDlgItemMessage(m_hwnd, IDC_SRMM_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
+ if (len && GetSendButtonState() == PBS_DISABLED)
+ EnableSendButton(true);
+ else if (len == 0 && GetSendButtonState() != PBS_DISABLED)
+ EnableSendButton(false);
+
+ if (len) { // looks complex but avoids flickering on the button while typing.
+ if (!m_bSaveBtn) {
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]);
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save and close session"), BATF_UNICODE);
+ m_bSaveBtn = true;
+ }
+ }
+ else {
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]);
+ SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Close session"), BATF_UNICODE);
+ m_bSaveBtn = false;
+ }
+ m_textLen = len;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateStatusBar()
+{
+ if (m_pContainer->m_hwndActive != m_hwnd || m_pContainer->m_hwndStatus == nullptr || CMimAPI::m_shutDown || m_wszStatusBar[0])
+ return;
+
+ if (m_si->pszModule == nullptr)
+ return;
+
+ //Mad: strange rare crash here...
+ MODULEINFO *mi = m_si->pMI;
+ if (!mi->ptszModDispName)
+ return;
+
+ int x = 12;
+ x += Chat_GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->m_hwndStatus, WM_GETFONT, 0, 0), true);
+ x += GetSystemMetrics(SM_CXSMICON);
+
+ wchar_t szFinalStatusBarText[512];
+ if (m_pPanel.isActive()) {
+ time_t now = time(0);
+ uint32_t diff = (now - mi->idleTimeStamp) / 60;
+ if (diff >= 1) {
+ if (diff > 59) {
+ uint32_t hours = diff / 60;
+ uint32_t minutes = diff % 60;
+ mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"),
+ hours, hours > 1 ? TranslateT("hours") : TranslateT("hour"),
+ minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute"));
+ }
+ else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute"));
+ }
+ else mi->tszIdleMsg[0] = 0;
+
+ mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg);
+ }
+ else {
+ if (m_si->ptszStatusbarText)
+ mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, m_si->ptszStatusbarText);
+ else
+ wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE);
+ }
+ SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText);
+ tabUpdateStatusBar();
+ m_pPanel.Invalidate();
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+}
+
+void CMsgDialog::UpdateTitle()
+{
+ if (isChat()) {
+ m_wStatus = m_si->wStatus;
+
+ const wchar_t *szNick = m_cache->getNick();
+ if (mir_wstrlen(szNick) > 0) {
+ if (M.GetByte("cuttitle", 0))
+ CutContactName(szNick, m_wszTitle, _countof(m_wszTitle));
+ else
+ wcsncpy_s(m_wszTitle, szNick, _TRUNCATE);
+ }
+
+ CMStringW wszTitle;
+ HICON hIcon = nullptr;
+ int nUsers = m_si->getUserList().getCount();
+
+ switch (m_si->iType) {
+ case GCW_CHATROOM:
+ hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
+ wszTitle.Format((nUsers == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"),
+ szNick, nUsers, m_bFilterEnabled ? TranslateT(", event filter active") : L"");
+ break;
+
+ case GCW_PRIVMESS:
+ hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
+ if (nUsers == 1)
+ wszTitle.Format(TranslateT("%s: message session"), szNick);
+ else
+ wszTitle.Format(TranslateT("%s: message session (%u users)"), szNick, nUsers);
+ break;
+
+ case GCW_SERVER:
+ wszTitle.Format(L"%s: Server", szNick);
+ hIcon = LoadIconEx("window");
+ break;
+
+ default:
+ return;
+ }
+
+ if (m_pWnd) {
+ m_pWnd->updateTitle(m_wszTitle);
+ m_pWnd->updateIcon(hIcon);
+ }
+ m_hTabStatusIcon = hIcon;
+
+ if (m_cache->getStatus() != m_cache->getOldStatus()) {
+ wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_wStatus, 0), _TRUNCATE);
+
+ TCITEM item = {};
+ item.mask = TCIF_TEXT;
+ item.pszText = m_wszTitle;
+ TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+ }
+ SetWindowText(m_hwnd, wszTitle);
+ if (m_pContainer->m_hwndActive == m_hwnd) {
+ m_pContainer->UpdateTitle(0, this);
+ UpdateStatusBar();
+ }
+ }
+ else {
+ uint32_t dwOldIdle = m_idle;
+ const char *szActProto = nullptr;
+
+ m_wszStatus[0] = 0;
+
+ if (m_iTabID == -1)
+ return;
+
+ TCITEM item = {};
+
+ bool bChanged = false;
+ wchar_t newtitle[128];
+ if (m_szProto) {
+ szActProto = m_cache->getProto();
+
+ bool bHasName = (m_cache->getUIN()[0] != 0);
+ m_idle = m_cache->getIdleTS();
+ m_bIsIdle = m_idle != 0;
+
+ m_wStatus = m_cache->getStatus();
+ wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_szProto == nullptr ? ID_STATUS_OFFLINE : m_wStatus, 0), _TRUNCATE);
+
+ wchar_t newcontactname[128]; newcontactname[0] = 0;
+ if (PluginConfig.m_bCutContactNameOnTabs)
+ CutContactName(m_cache->getNick(), newcontactname, _countof(newcontactname));
+ else
+ wcsncpy_s(newcontactname, m_cache->getNick(), _TRUNCATE);
+
+ Utils::DoubleAmpersands(newcontactname, _countof(newcontactname));
+
+ if (newcontactname[0] != 0) {
+ if (g_plugin.bStatusOnTabs)
+ mir_snwprintf(newtitle, L"%s (%s)", newcontactname, m_wszStatus);
+ else
+ wcsncpy_s(newtitle, newcontactname, _TRUNCATE);
+ }
+ else wcsncpy_s(newtitle, L"Forward", _TRUNCATE);
+
+ if (mir_wstrcmp(newtitle, m_wszTitle))
+ bChanged = true;
+ else if (m_wStatus != m_wOldStatus)
+ bChanged = true;
+
+ UpdateWindowIcon();
+
+ wchar_t fulluin[256];
+ if (m_bIsMeta)
+ mir_snwprintf(fulluin,
+ TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nRight click for metacontact control\nClick dropdown to add or remove user from your favorites."),
+ bHasName ? m_cache->getUIN() : TranslateT("No UID"));
+ else
+ mir_snwprintf(fulluin,
+ TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nClick dropdown to change this contact's favorite status."),
+ bHasName ? m_cache->getUIN() : TranslateT("No UID"));
+
+ SendDlgItemMessage(m_hwnd, IDC_NAME, BUTTONADDTOOLTIP, (WPARAM)fulluin, BATF_UNICODE);
+ }
+ else wcsncpy_s(newtitle, L"Message Session", _TRUNCATE);
+
+ m_wOldStatus = m_wStatus;
+ if (m_idle != dwOldIdle || bChanged) {
+ if (bChanged) {
+ item.mask |= TCIF_TEXT;
+ item.pszText = m_wszTitle;
+ wcsncpy_s(m_wszTitle, newtitle, _TRUNCATE);
+ if (m_pWnd)
+ m_pWnd->updateTitle(m_cache->getNick());
+ }
+ if (m_iTabID >= 0) {
+ TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+ if (m_pContainer->cfg.flags.m_bSideBar)
+ m_pContainer->m_pSideBar->updateSession(this);
+ }
+ if (m_pContainer->m_hwndActive == m_hwnd && bChanged)
+ m_pContainer->UpdateTitle(m_hContact);
+
+ m_pPanel.Invalidate();
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+ }
+
+ // care about MetaContacts and update the statusbar icon with the currently "most online" contact...
+ if (m_bIsMeta) {
+ PostMessage(m_hwnd, DM_OWNNICKCHANGED, 0, 0);
+ if (m_pContainer->cfg.flags.m_bUinStatusBar)
+ DM_UpdateLastMessage();
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateWindowIcon()
+{
+ if (m_hXStatusIcon) {
+ DestroyIcon(m_hXStatusIcon);
+ m_hXStatusIcon = nullptr;
+ }
+
+ if (LPCSTR szProto = m_cache->getProto()) {
+ m_hTabIcon = m_hTabStatusIcon = GetMyContactIcon(&g_plugin.bMetaTab);
+ if (g_plugin.bUseXStatus)
+ m_hXStatusIcon = GetXStatusIcon();
+
+ SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BUTTONSETASDIMMED, m_bIsIdle, 0);
+ SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(m_hXStatusIcon ? m_hXStatusIcon : GetMyContactIcon(&g_plugin.bMetaBar)));
+
+ if (m_pContainer->m_hwndActive == m_hwnd)
+ m_pContainer->SetIcon(this, m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
+
+ if (m_pWnd)
+ m_pWnd->updateIcon(m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// called whenever a group chat tab becomes active(either by switching tabs or activating a
+// container window
+
+void CMsgDialog::UpdateWindowState(UINT msg)
+{
+ if (m_iTabID < 0)
+ return;
+
+ if (msg == WM_ACTIVATE) {
+ if (m_pContainer->cfg.flags.m_bTransparent) {
+ uint32_t trans = LOWORD(m_pContainer->cfg.dwTransparency);
+ SetLayeredWindowAttributes(m_pContainer->m_hwnd, CSkin::m_ContainerColorKey, (uint8_t)trans, (m_pContainer->cfg.flags.m_bTransparent ? LWA_ALPHA : 0));
+ }
+ }
+
+ if (m_hwndFilter) {
+ POINT pt;
+ GetCursorPos(&pt);
+
+ RECT rcFilter;
+ GetWindowRect(m_hwndFilter, &rcFilter);
+ if (!PtInRect(&rcFilter, pt)) {
+ SendMessage(m_hwndFilter, WM_CLOSE, 1, 1);
+ m_hwndFilter = nullptr;
+ }
+ }
+
+ if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) {
+ m_iInputAreaHeight = 0;
+ m_message.SendMsg(EM_REQUESTRESIZE, 0, 0);
+ }
+
+ m_pPanel.dismissConfig();
+ m_dwUnread = 0;
+ if (m_pWnd) {
+ m_pWnd->activateTab();
+ m_pWnd->setOverlayIcon(nullptr, true);
+ }
+
+ if (m_pContainer->m_hwndSaved == m_hwnd)
+ return;
+
+ m_pContainer->m_hwndSaved = m_hwnd;
+ m_dwTickLastEvent = 0;
+ m_bDividerSet = false;
+
+ if (m_pContainer->m_dwFlashingStarted != 0) {
+ m_pContainer->FlashContainer(0, 0);
+ m_pContainer->m_dwFlashingStarted = 0;
+ }
+
+ if (m_si) {
+ g_chatApi.SetActiveSession(m_si);
+ m_hTabIcon = m_hTabStatusIcon;
+
+ if (db_get_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0) != 0)
+ db_set_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0);
+ if (g_clistApi.pfnGetEvent(m_si->hContact, 0))
+ g_clistApi.pfnRemoveEvent(m_si->hContact, GC_FAKE_EVENT);
+
+ UpdateTitle();
+ m_hTabIcon = m_hTabStatusIcon;
+ if (timerFlash.Stop() || m_iFlashIcon) {
+ FlashTab(false);
+ m_bCanFlashTab = FALSE;
+ m_iFlashIcon = nullptr;
+ }
+
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
+
+ if (m_bNeedCheckSize)
+ PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
+
+ SetFocus(m_message.GetHwnd());
+ m_dwLastActivity = GetTickCount();
+ m_pContainer->m_dwLastActivity = m_dwLastActivity;
+ m_pContainer->m_pMenuBar->configureMenu();
+ }
+ else {
+ if (timerFlash.Stop()) {
+ FlashTab(false);
+ m_bCanFlashTab = false;
+ }
+
+ if (m_bFlashClist) {
+ m_bFlashClist = false;
+ if (m_hFlashingEvent != 0)
+ g_clistApi.pfnRemoveEvent(m_hContact, m_hFlashingEvent);
+ m_hFlashingEvent = 0;
+ }
+ m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
+
+ if (m_bDeferredRemakeLog && !IsIconic(m_pContainer->m_hwnd)) {
+ RemakeLog();
+ m_bDeferredRemakeLog = false;
+ }
+
+ if (m_bNeedCheckSize)
+ PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
+
+ m_pContainer->m_hIconTaskbarOverlay = nullptr;
+ m_pContainer->UpdateTitle(m_hContact);
+
+ tabUpdateStatusBar();
+ m_dwLastActivity = GetTickCount();
+ m_pContainer->m_dwLastActivity = m_dwLastActivity;
+
+ m_pContainer->m_pMenuBar->configureMenu();
+ g_arUnreadWindows.remove(HANDLE(m_hContact));
+
+ m_pPanel.Invalidate();
+
+ if (m_bDeferredScroll) {
+ m_bDeferredScroll = false;
+ DM_ScrollToBottom(0, 1);
+ }
+ }
+
+ DM_SetDBButtonStates();
+
+ if (m_bDelayedSplitter) {
+ m_bDelayedSplitter = false;
+ ShowWindow(m_pContainer->m_hwnd, SW_RESTORE);
+ PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam);
+ PostMessage(m_hwnd, WM_SIZE, 0, 0);
+ m_wParam = m_lParam = 0;
+ }
+
+ BB_SetButtonsPos();
+ if (M.isAero())
+ InvalidateRect(m_hwndParent, nullptr, FALSE);
+
+ if (m_pContainer->cfg.flags.m_bSideBar)
+ m_pContainer->m_pSideBar->setActiveItem(this, msg == WM_ACTIVATE);
+
+ if (m_pWnd)
+ m_pWnd->Invalidate();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// generic handler for the WM_COPY message in message log/chat history richedit control(s).
+// it filters out the invisible event boundary markers from the text copied to the clipboard.
+// WINE Fix: overwrite clippboad data from original control data
+
+LRESULT CMsgDialog::WMCopyHandler(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT result = mir_callNextSubclass(m_pLog->GetHwnd(), stubLogProc, msg, wParam, lParam);
+
+ ptrA szFromStream(LOG()->GetRichTextRtf(true, true));
+ if (szFromStream != nullptr) {
+ ptrW converted(mir_utf8decodeW(szFromStream));
+ if (converted != nullptr) {
+ Utils::FilterEventMarkers(converted);
+ Utils_ClipboardCopy(converted);
+ }
+ }
+
+ return result;
+}
diff --git a/plugins/TabSRMM/src/msgdlgutils.cpp b/plugins/TabSRMM/src/msgdlgutils.cpp
index daec4e8349..eff4705074 100644
--- a/plugins/TabSRMM/src/msgdlgutils.cpp
+++ b/plugins/TabSRMM/src/msgdlgutils.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdlgutils.h b/plugins/TabSRMM/src/msgdlgutils.h
index b6666fd6ce..b9cd0fd792 100644
--- a/plugins/TabSRMM/src/msgdlgutils.h
+++ b/plugins/TabSRMM/src/msgdlgutils.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msglog.cpp b/plugins/TabSRMM/src/msglog.cpp
index b4143a69a6..e12507d8e0 100644
--- a/plugins/TabSRMM/src/msglog.cpp
+++ b/plugins/TabSRMM/src/msglog.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgoptions.cpp b/plugins/TabSRMM/src/msgoptions.cpp
index b87a801874..b71a71dbc7 100644
--- a/plugins/TabSRMM/src/msgoptions.cpp
+++ b/plugins/TabSRMM/src/msgoptions.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgs.cpp b/plugins/TabSRMM/src/msgs.cpp
index 7753b418d0..1c4448bff0 100644
--- a/plugins/TabSRMM/src/msgs.cpp
+++ b/plugins/TabSRMM/src/msgs.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgs.h b/plugins/TabSRMM/src/msgs.h
index 693f5aa08c..382ad1f54d 100644
--- a/plugins/TabSRMM/src/msgs.h
+++ b/plugins/TabSRMM/src/msgs.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/muchighlight.cpp b/plugins/TabSRMM/src/muchighlight.cpp
index ffa1759fa9..9eb5cd96f5 100644
--- a/plugins/TabSRMM/src/muchighlight.cpp
+++ b/plugins/TabSRMM/src/muchighlight.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/muchighlight.h b/plugins/TabSRMM/src/muchighlight.h
index 197e130859..7338765c40 100644
--- a/plugins/TabSRMM/src/muchighlight.h
+++ b/plugins/TabSRMM/src/muchighlight.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/nen.h b/plugins/TabSRMM/src/nen.h
index bca0b37885..d40d7367b6 100644
--- a/plugins/TabSRMM/src/nen.h
+++ b/plugins/TabSRMM/src/nen.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/selectcontainer.cpp b/plugins/TabSRMM/src/selectcontainer.cpp
index c8189f9622..dc835292ca 100644
--- a/plugins/TabSRMM/src/selectcontainer.cpp
+++ b/plugins/TabSRMM/src/selectcontainer.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendlater.cpp b/plugins/TabSRMM/src/sendlater.cpp
index 1ad1690076..1e8614d1fd 100644
--- a/plugins/TabSRMM/src/sendlater.cpp
+++ b/plugins/TabSRMM/src/sendlater.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendlater.h b/plugins/TabSRMM/src/sendlater.h
index bfe97b5fe6..5dc2a58df3 100644
--- a/plugins/TabSRMM/src/sendlater.h
+++ b/plugins/TabSRMM/src/sendlater.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendqueue.cpp b/plugins/TabSRMM/src/sendqueue.cpp
index 4e8372b7e4..be8566f188 100644
--- a/plugins/TabSRMM/src/sendqueue.cpp
+++ b/plugins/TabSRMM/src/sendqueue.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendqueue.h b/plugins/TabSRMM/src/sendqueue.h
index 89f32b17f8..7dfb4ff9b1 100644
--- a/plugins/TabSRMM/src/sendqueue.h
+++ b/plugins/TabSRMM/src/sendqueue.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sidebar.cpp b/plugins/TabSRMM/src/sidebar.cpp
index c3014aa210..8f6de0a0f3 100644
--- a/plugins/TabSRMM/src/sidebar.cpp
+++ b/plugins/TabSRMM/src/sidebar.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sidebar.h b/plugins/TabSRMM/src/sidebar.h
index b02e742d37..2e67eae807 100644
--- a/plugins/TabSRMM/src/sidebar.h
+++ b/plugins/TabSRMM/src/sidebar.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/srmm.cpp b/plugins/TabSRMM/src/srmm.cpp
index 9c9c3c6d2f..388565d140 100644
--- a/plugins/TabSRMM/src/srmm.cpp
+++ b/plugins/TabSRMM/src/srmm.cpp
@@ -1,144 +1,144 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/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.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// plugin loading functions and global exports.
-
-#include "stdafx.h"
-
-LOGFONT lfDefault = { 0 };
-
-/*
- * miranda interfaces
- */
-
-CMPlugin g_plugin;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx = {
- sizeof(PLUGININFOEX),
- __PLUGIN_NAME,
- PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
- __DESCRIPTION,
- __AUTHOR,
- __COPYRIGHT,
- __AUTHORWEB,
- UNICODE_AWARE,
- // {6CA5F042-7A7F-47CC-A715-FC8C46FBF434}
- { 0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 } }
-};
-
-CMPlugin::CMPlugin() :
- PLUGIN<CMPlugin>("SRMsg", pluginInfoEx),
-
- // main settings
- bAutoMin(SRMSGMOD_T, "AutoMin", false),
- bAutoCopy(SRMSGMOD_T, "autocopy", true),
- bAutoTabs(SRMSGMOD_T, "autotabs", true),
- bAllowTab(SRMSGMOD_T, "tabmode", false),
- bAutoClose(SRMSGMOD_T, "AutoClose", false),
- bAutoPopup(SRMSGMOD_T, "AutoPopup", false),
- bAutoSplit(SRMSGMOD_T, "autosplit", false),
- bDeleteTemp(SRMSGMOD_T, "deletetemp", false),
- bUseXStatus(SRMSGMOD_T, "use_xicons", true),
- bSendFormat(SRMSGMOD_T, "sendformat", false),
- bHideOnClose(SRMSGMOD_T, "hideonclose", false),
- bStatusOnTabs(SRMSGMOD_T, "tabstatus", true),
- bFlashOnClist(SRMSGMOD_T, "flashcl", false),
- bPasteAndSend(SRMSGMOD_T, "pasteandsend", true),
- bAutoContainer(SRMSGMOD_T, "autocontainer", true),
- bAutoSwitchTabs(SRMSGMOD_T, "autoswitchtabs", true),
- bPopupContainer(SRMSGMOD_T, "cpopup", true),
- bDetailedTooltips(SRMSGMOD_T, "d_tooltips", false),
- bUseSameSplitSize(SRMSGMOD_T, "usesamesplitsize", true),
- bAllowOfflineMultisend(SRMSGMOD_T, "AllowOfflineMultisend", true),
-
- // advanced options
- bMetaBar(SRMSGMOD_T, "MetaiconBar", true),
- bMetaTab(SRMSGMOD_T, "MetaiconTab", true),
- bShowDesc(SRMSGMOD_T, "ShowClientDescription", false),
- bCloseSend(SRMSGMOD_T, "adv_AutoClose_2", false),
- bErrorPopup(SRMSGMOD_T, "adv_ErrorPopups", true),
-
- // chat settings
- bOpenInDefault(CHAT_MODULE, "DefaultContainer", true),
- bCreateWindowOnHighlight(CHAT_MODULE, "CreateWindowOnHighlight", false),
- bBBCodeInPopups(CHAT_MODULE, "BBCodeInPopups", false),
- bClassicIndicators(CHAT_MODULE, "ClassicIndicators", false),
- bLogClassicIndicators(CHAT_MODULE, "LogClassicIndicators", false),
- bAlternativeSorting(CHAT_MODULE, "AlternativeSorting", true),
- bAnnoyingHighlight(CHAT_MODULE, "AnnoyingHighlight", false),
- bLogSymbols(CHAT_MODULE, "LogSymbols", true),
- bClickableNicks(CHAT_MODULE, "ClickableNicks", true),
- bColorizeNicks(CHAT_MODULE, "ColorizeNicks", true),
- bColorizeNicksInLog(CHAT_MODULE, "ColorizeNicksInLog", true),
- bScaleIcons(CHAT_MODULE, "ScaleIcons", true),
- bNewLineAfterNames(CHAT_MODULE, "NewlineAfterNames", false),
-
- // typing settings
- bPopups(TypingModule, "TypingPopup", true),
- bTypingNew(TypingModule, "DefaultTyping", true),
- bTypingUnknown(TypingModule, "UnknownTyping", false),
-
- // log options
- bUseDividers(SRMSGMOD_T, "usedividers", false),
- bLogStatusChanges(SRMSGMOD_T, "logstatuschanges", false),
- bDividersUsePopupConfig(SRMSGMOD_T, "div_popupconfig", false)
-{}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_SRMM, MIID_LAST };
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPlugin::Load()
-{
- SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
-
- hLogger = RegisterSrmmLog(this, "built-in", LPGENW("tabSRMM internal log"), &logBuilder);
-
- Chat_Load();
-
- return LoadSendRecvMessageModule();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPlugin::Unload()
-{
- UnregisterSrmmLog(hLogger);
- FreeLogFonts();
- Chat_Unload();
- int iRet = SplitmsgShutdown();
- Skin->setupTabCloseBitmap(true);
- Skin->UnloadAeroTabs();
- CleanTempFiles();
- SendLater::shutDown();
- delete Skin;
- delete sendQueue;
- return iRet;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/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.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// plugin loading functions and global exports.
+
+#include "stdafx.h"
+
+LOGFONT lfDefault = { 0 };
+
+/*
+ * miranda interfaces
+ */
+
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {6CA5F042-7A7F-47CC-A715-FC8C46FBF434}
+ { 0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 } }
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>("SRMsg", pluginInfoEx),
+
+ // main settings
+ bAutoMin(SRMSGMOD_T, "AutoMin", false),
+ bAutoCopy(SRMSGMOD_T, "autocopy", true),
+ bAutoTabs(SRMSGMOD_T, "autotabs", true),
+ bAllowTab(SRMSGMOD_T, "tabmode", false),
+ bAutoClose(SRMSGMOD_T, "AutoClose", false),
+ bAutoPopup(SRMSGMOD_T, "AutoPopup", false),
+ bAutoSplit(SRMSGMOD_T, "autosplit", false),
+ bDeleteTemp(SRMSGMOD_T, "deletetemp", false),
+ bUseXStatus(SRMSGMOD_T, "use_xicons", true),
+ bSendFormat(SRMSGMOD_T, "sendformat", false),
+ bHideOnClose(SRMSGMOD_T, "hideonclose", false),
+ bStatusOnTabs(SRMSGMOD_T, "tabstatus", true),
+ bFlashOnClist(SRMSGMOD_T, "flashcl", false),
+ bPasteAndSend(SRMSGMOD_T, "pasteandsend", true),
+ bAutoContainer(SRMSGMOD_T, "autocontainer", true),
+ bAutoSwitchTabs(SRMSGMOD_T, "autoswitchtabs", true),
+ bPopupContainer(SRMSGMOD_T, "cpopup", true),
+ bDetailedTooltips(SRMSGMOD_T, "d_tooltips", false),
+ bUseSameSplitSize(SRMSGMOD_T, "usesamesplitsize", true),
+ bAllowOfflineMultisend(SRMSGMOD_T, "AllowOfflineMultisend", true),
+
+ // advanced options
+ bMetaBar(SRMSGMOD_T, "MetaiconBar", true),
+ bMetaTab(SRMSGMOD_T, "MetaiconTab", true),
+ bShowDesc(SRMSGMOD_T, "ShowClientDescription", false),
+ bCloseSend(SRMSGMOD_T, "adv_AutoClose_2", false),
+ bErrorPopup(SRMSGMOD_T, "adv_ErrorPopups", true),
+
+ // chat settings
+ bOpenInDefault(CHAT_MODULE, "DefaultContainer", true),
+ bCreateWindowOnHighlight(CHAT_MODULE, "CreateWindowOnHighlight", false),
+ bBBCodeInPopups(CHAT_MODULE, "BBCodeInPopups", false),
+ bClassicIndicators(CHAT_MODULE, "ClassicIndicators", false),
+ bLogClassicIndicators(CHAT_MODULE, "LogClassicIndicators", false),
+ bAlternativeSorting(CHAT_MODULE, "AlternativeSorting", true),
+ bAnnoyingHighlight(CHAT_MODULE, "AnnoyingHighlight", false),
+ bLogSymbols(CHAT_MODULE, "LogSymbols", true),
+ bClickableNicks(CHAT_MODULE, "ClickableNicks", true),
+ bColorizeNicks(CHAT_MODULE, "ColorizeNicks", true),
+ bColorizeNicksInLog(CHAT_MODULE, "ColorizeNicksInLog", true),
+ bScaleIcons(CHAT_MODULE, "ScaleIcons", true),
+ bNewLineAfterNames(CHAT_MODULE, "NewlineAfterNames", false),
+
+ // typing settings
+ bPopups(TypingModule, "TypingPopup", true),
+ bTypingNew(TypingModule, "DefaultTyping", true),
+ bTypingUnknown(TypingModule, "UnknownTyping", false),
+
+ // log options
+ bUseDividers(SRMSGMOD_T, "usedividers", false),
+ bLogStatusChanges(SRMSGMOD_T, "logstatuschanges", false),
+ bDividersUsePopupConfig(SRMSGMOD_T, "div_popupconfig", false)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_SRMM, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Load()
+{
+ SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
+
+ hLogger = RegisterSrmmLog(this, "built-in", LPGENW("tabSRMM internal log"), &logBuilder);
+
+ Chat_Load();
+
+ return LoadSendRecvMessageModule();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+ UnregisterSrmmLog(hLogger);
+ FreeLogFonts();
+ Chat_Unload();
+ int iRet = SplitmsgShutdown();
+ Skin->setupTabCloseBitmap(true);
+ Skin->UnloadAeroTabs();
+ CleanTempFiles();
+ SendLater::shutDown();
+ delete Skin;
+ delete sendQueue;
+ return iRet;
+}
diff --git a/plugins/TabSRMM/src/stdafx.cxx b/plugins/TabSRMM/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TabSRMM/src/stdafx.cxx
+++ b/plugins/TabSRMM/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/plugins/TabSRMM/src/stdafx.h b/plugins/TabSRMM/src/stdafx.h
index e09f24597b..4c87b4c428 100644
--- a/plugins/TabSRMM/src/stdafx.h
+++ b/plugins/TabSRMM/src/stdafx.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/tabctrl.cpp b/plugins/TabSRMM/src/tabctrl.cpp
index 29d2b0511b..35523e14f7 100644
--- a/plugins/TabSRMM/src/tabctrl.cpp
+++ b/plugins/TabSRMM/src/tabctrl.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/taskbar.cpp b/plugins/TabSRMM/src/taskbar.cpp
index a6eb6b8a2d..d49d937af5 100644
--- a/plugins/TabSRMM/src/taskbar.cpp
+++ b/plugins/TabSRMM/src/taskbar.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/taskbar.h b/plugins/TabSRMM/src/taskbar.h
index 92f8dcf85d..ed407dadbd 100644
--- a/plugins/TabSRMM/src/taskbar.h
+++ b/plugins/TabSRMM/src/taskbar.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/templates.cpp b/plugins/TabSRMM/src/templates.cpp
index 369c5ee4fe..a27a3df00f 100644
--- a/plugins/TabSRMM/src/templates.cpp
+++ b/plugins/TabSRMM/src/templates.cpp
@@ -1,124 +1,124 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/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.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// Simple editor for the message log templates
-
-#include "stdafx.h"
-
-/*
-* hardcoded default set of templates for both LTR and RTL.
-* cannot be changed and may be used at any time to "revert" to a working layout
-*/
-
-char* TemplateNames[TMPL_MAX] =
-{
- LPGEN("Message In"),
- LPGEN("Message Out"),
- LPGEN("Group In (Start)"),
- LPGEN("Group Out (Start)"),
- LPGEN("Group In (Inner)"),
- LPGEN("Group Out (Inner)"),
- LPGEN("Status change"),
- LPGEN("Error message")
-};
-
-wchar_t* TemplateNamesW[TMPL_MAX] =
-{
- LPGENW("Message In"),
- LPGENW("Message Out"),
- LPGENW("Group In (Start)"),
- LPGENW("Group Out (Start)"),
- LPGENW("Group In (Inner)"),
- LPGENW("Group Out (Inner)"),
- LPGENW("Status change"),
- LPGENW("Error message")
-};
-
-TTemplateSet LTR_Default =
-{
- TRUE,
- L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
- L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
- L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
- L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
- L"%S %T%|%M",
- L"%S %T%|%M",
- L"%I %S %&r, %&T, %N %M%! ",
- L"%I%S %r, %T, %e%l%M",
- "Default LTR"
-};
-
-TTemplateSet RTL_Default =
-{
- TRUE,
- L"%I %N %r%n%S %T%|%M",
- L"%I %N %r%n%S %T%|%M",
- L"%I %N %r%n%S %T%|%M",
- L"%I %N %r%n%S %T%|%M",
- L"%S %T%|%M",
- L"%S %T%|%M",
- L"%I%S %r, %T, %N %M%! ",
- L"%I%S %r, %T, %e%l%M",
- "Default RTL"
-};
-
-TTemplateSet LTR_Active, RTL_Active;
-
-/*
-* loads template set overrides from hContact into the given set of already existing
-* templates
-*/
-
-static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl)
-{
- for (int i = 0; i < TMPL_MAX; i++) {
- DBVARIANT dbv = { 0 };
- if (db_get_ws(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv))
- continue;
- if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR)
- wcsncpy_s(tSet->szTemplates[i], dbv.pwszVal, _TRUNCATE);
- db_free(&dbv);
- }
-}
-
-void LoadDefaultTemplates()
-{
- LTR_Active = LTR_Default;
- RTL_Active = RTL_Default;
-
- if (db_get_b(0, RTLTEMPLATES_MODULE, "setup", 0) < 2) {
- for (int i = 0; i < TMPL_MAX; i++)
- db_set_ws(0, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]);
- db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2);
- }
- if (db_get_b(0, TEMPLATES_MODULE, "setup", 0) < 2) {
- for (int i = 0; i < TMPL_MAX; i++)
- db_set_ws(0, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]);
- db_set_b(0, TEMPLATES_MODULE, "setup", 2);
- }
- LoadTemplatesFrom(&LTR_Active, 0, 0);
- LoadTemplatesFrom(&RTL_Active, 0, 1);
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/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.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// Simple editor for the message log templates
+
+#include "stdafx.h"
+
+/*
+* hardcoded default set of templates for both LTR and RTL.
+* cannot be changed and may be used at any time to "revert" to a working layout
+*/
+
+char* TemplateNames[TMPL_MAX] =
+{
+ LPGEN("Message In"),
+ LPGEN("Message Out"),
+ LPGEN("Group In (Start)"),
+ LPGEN("Group Out (Start)"),
+ LPGEN("Group In (Inner)"),
+ LPGEN("Group Out (Inner)"),
+ LPGEN("Status change"),
+ LPGEN("Error message")
+};
+
+wchar_t* TemplateNamesW[TMPL_MAX] =
+{
+ LPGENW("Message In"),
+ LPGENW("Message Out"),
+ LPGENW("Group In (Start)"),
+ LPGENW("Group Out (Start)"),
+ LPGENW("Group In (Inner)"),
+ LPGENW("Group Out (Inner)"),
+ LPGENW("Status change"),
+ LPGENW("Error message")
+};
+
+TTemplateSet LTR_Default =
+{
+ TRUE,
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%I %N %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+ L"%S %T%|%M",
+ L"%S %T%|%M",
+ L"%I %S %&r, %&T, %N %M%! ",
+ L"%I%S %r, %T, %e%l%M",
+ "Default LTR"
+};
+
+TTemplateSet RTL_Default =
+{
+ TRUE,
+ L"%I %N %r%n%S %T%|%M",
+ L"%I %N %r%n%S %T%|%M",
+ L"%I %N %r%n%S %T%|%M",
+ L"%I %N %r%n%S %T%|%M",
+ L"%S %T%|%M",
+ L"%S %T%|%M",
+ L"%I%S %r, %T, %N %M%! ",
+ L"%I%S %r, %T, %e%l%M",
+ "Default RTL"
+};
+
+TTemplateSet LTR_Active, RTL_Active;
+
+/*
+* loads template set overrides from hContact into the given set of already existing
+* templates
+*/
+
+static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl)
+{
+ for (int i = 0; i < TMPL_MAX; i++) {
+ DBVARIANT dbv = { 0 };
+ if (db_get_ws(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv))
+ continue;
+ if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR)
+ wcsncpy_s(tSet->szTemplates[i], dbv.pwszVal, _TRUNCATE);
+ db_free(&dbv);
+ }
+}
+
+void LoadDefaultTemplates()
+{
+ LTR_Active = LTR_Default;
+ RTL_Active = RTL_Default;
+
+ if (db_get_b(0, RTLTEMPLATES_MODULE, "setup", 0) < 2) {
+ for (int i = 0; i < TMPL_MAX; i++)
+ db_set_ws(0, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]);
+ db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2);
+ }
+ if (db_get_b(0, TEMPLATES_MODULE, "setup", 0) < 2) {
+ for (int i = 0; i < TMPL_MAX; i++)
+ db_set_ws(0, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]);
+ db_set_b(0, TEMPLATES_MODULE, "setup", 2);
+ }
+ LoadTemplatesFrom(&LTR_Active, 0, 0);
+ LoadTemplatesFrom(&RTL_Active, 0, 1);
+}
diff --git a/plugins/TabSRMM/src/themeio.cpp b/plugins/TabSRMM/src/themeio.cpp
index 7648b8f90b..deaecbfcc6 100644
--- a/plugins/TabSRMM/src/themeio.cpp
+++ b/plugins/TabSRMM/src/themeio.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/themes.cpp b/plugins/TabSRMM/src/themes.cpp
index 14a762cc0a..7c48a65d5e 100644
--- a/plugins/TabSRMM/src/themes.cpp
+++ b/plugins/TabSRMM/src/themes.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/themes.h b/plugins/TabSRMM/src/themes.h
index 471c1a73f5..2e7bd1bf1c 100644
--- a/plugins/TabSRMM/src/themes.h
+++ b/plugins/TabSRMM/src/themes.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/userprefs.cpp b/plugins/TabSRMM/src/userprefs.cpp
index 573f2b883e..880112e041 100644
--- a/plugins/TabSRMM/src/userprefs.cpp
+++ b/plugins/TabSRMM/src/userprefs.cpp
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/utils.cpp b/plugins/TabSRMM/src/utils.cpp
index 74fab10f8a..484b5dca1d 100644
--- a/plugins/TabSRMM/src/utils.cpp
+++ b/plugins/TabSRMM/src/utils.cpp
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/utils.h b/plugins/TabSRMM/src/utils.h
index f60cebe2e3..66716896ba 100644
--- a/plugins/TabSRMM/src/utils.h
+++ b/plugins/TabSRMM/src/utils.h
@@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/version.h b/plugins/TabSRMM/src/version.h
index 37a59b1f3a..376079948b 100644
--- a/plugins/TabSRMM/src/version.h
+++ b/plugins/TabSRMM/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "IM and group chat module for Miranda NG."
#define __AUTHOR "The Miranda developers team and contributors"
#define __AUTHORWEB "https://miranda-ng.org/p/TabSRMM"
-#define __COPYRIGHT "© 2012-22 Miranda NG team, 2000-2010 Miranda Project and contributors."
+#define __COPYRIGHT "© 2012-23 Miranda NG team, 2000-2010 Miranda Project and contributors."
diff --git a/plugins/TabSRMM/src/warning.cpp b/plugins/TabSRMM/src/warning.cpp
index c5bf30736f..fe93ea374c 100644
--- a/plugins/TabSRMM/src/warning.cpp
+++ b/plugins/TabSRMM/src/warning.cpp
@@ -1,328 +1,328 @@
-/*
-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"
-
-using namespace CWarning;
-
-static MWindowList hWindowList;
-
-class CWarningImpl
-{
- ptrW m_szTitle, m_szText;
- UINT m_uId;
- HFONT m_hFontCaption = nullptr;
- uint32_t m_dwFlags;
- HWND m_hwnd = nullptr;
- bool m_fIsModal;
-
-public:
- CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) :
- m_szTitle(mir_wstrdup(tszTitle)),
- m_szText(mir_wstrdup(tszText))
- {
- m_uId = uId;
- m_dwFlags = dwFlags;
- m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
- }
-
- ~CWarningImpl()
- {
- if (m_hFontCaption)
- ::DeleteObject(m_hFontCaption);
- }
-
- // static function to construct and show the dialog, returns the user's choice
- LRESULT ShowDialog() const
- {
- if (!m_fIsModal) {
- ::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
- return 0;
- }
-
- return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
- }
-
- /////////////////////////////////////////////////////////////////////////////////////////
- // stub dlg procedure.Just register the object pointer in WM_INITDIALOG
-
- static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
- if (w)
- return(w->dlgProc(hwnd, msg, wParam, lParam));
-
- switch (msg) {
- case WM_INITDIALOG:
- w = reinterpret_cast<CWarningImpl *>(lParam);
- if (w) {
- ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
- return(w->dlgProc(hwnd, msg, wParam, lParam));
- }
- break;
- }
- return FALSE;
- }
-
- /////////////////////////////////////////////////////////////////////////////////////////
- // dialog procedure for the warning dialog box
-
- INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- switch (msg) {
- case WM_INITDIALOG:
- m_hwnd = hwnd;
-
- ::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message"));
- ::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA);
- ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0);
- ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
-
- TranslateDialogDefault(hwnd);
- {
- CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15);
- str.Append(m_szText);
- str.Append(L"}");
- str.Replace(L"\n", L"\\line ");
- SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
- ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str));
-
- ::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle);
-
- if (m_dwFlags & CWF_NOALLOWHIDE)
- Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
- if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
- Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
- ::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
- }
- else {
- Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
- Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
- Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
- ::SetFocus(::GetDlgItem(hwnd, IDOK));
- }
-
- UINT uResId = 0;
- if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND))
- uResId = 32513;
- else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING))
- uResId = 32515;
- else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION))
- uResId = 32516;
- else if (m_dwFlags & MB_ICONQUESTION)
- uResId = 32514;
-
- HICON hIcon;
- if (uResId)
- hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
- else
- hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true);
- ::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);
-
- if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
- ::ShowWindow(hwnd, SW_SHOWNORMAL);
-
- WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd);
- }
- return TRUE;
-
- case WM_CTLCOLORSTATIC:
- {
- HWND hwndChild = reinterpret_cast<HWND>(lParam);
- UINT id = ::GetDlgCtrlID(hwndChild);
- if (nullptr == m_hFontCaption) {
- HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
- LOGFONT lf = {0};
-
- ::GetObject(hFont, sizeof(lf), &lf);
- lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
- m_hFontCaption = ::CreateFontIndirect(&lf);
- ::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
- }
-
- if (IDC_CAPTION == id) {
- ::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
- ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
- }
-
- if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
- ::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
- return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
- }
- }
- break;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDOK:
- case IDYES:
- case IDNO:
- if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
- uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId);
- db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal);
-
- if (LOWORD(wParam) != IDNO) {
- newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId);
- db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal);
- }
- }
- __fallthrough;
-
- case IDCANCEL:
- if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll()
- ::DestroyWindow(hwnd);
- else
- ::EndDialog(hwnd, LOWORD(wParam));
- break;
- }
- break;
-
- case WM_NOTIFY:
- switch (((NMHDR *)lParam)->code) {
- case EN_LINK:
- switch (((ENLINK *)lParam)->msg) {
- case WM_LBUTTONUP:
- ENLINK *e = reinterpret_cast<ENLINK *>(lParam);
-
- const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
- if (wszUrl) {
- Utils_OpenUrlW(wszUrl);
- mir_free(const_cast<wchar_t *>(wszUrl));
- }
- }
- }
- break;
-
- case WM_DESTROY:
- ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
- delete this;
-
- WindowList_Remove(hWindowList, hwnd);
- Window_FreeIcon_IcoLib(hwnd);
- break;
- }
-
- return FALSE;
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// implementation of the CWarningImpl class
-//
-// IMPORTANT note to translators for translation of the warning dialogs:
-//
-// Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
-// warning title from the actual warning text.
-//
-// Also, do NOT insert multiple | characters in the translated string. Not well-formatted
-// warnings cannot be translated and the plugin will show the untranslated versions.
-//
-// strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
-// will be used for important and critical error messages only.
-//
-// some strings are empty, this is intentional and used for error messages that share
-// the message with other possible error notifications (popups, tool tips etc.)
-//
-// Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them.
-
-static wchar_t *Warnings[] = {
- nullptr,
- LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE
- LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"), /* WARN_EDITUSERNOTES */
- LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"), /* WARN_ICONPACKMISSING */
- LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."), /* WARN_AEROPEEKSKIN */
- LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */
- LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0 is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
- LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0 and \\b1 send to multiple contacts\\b0 features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */
- LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"), /* WARN_CLOSEWINDOW */
- LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
- LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// send cancel message to all open warning dialogs so they are destroyed
-// before TabSRMM is unloaded.
-//
-// called by the OkToExit handler in globals.cpp
-
-void CWarning::destroyAll()
-{
- if (hWindowList)
- WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// show a warning dialog using the id value. Check whether the user has chosen to
-// not show this message again. This has room for 64 different warning dialogs, which
-// should be enough in the first place. Extending it should not be too hard though.
-
-LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt)
-{
- if (hWindowList == nullptr)
- hWindowList = WindowList_Create();
-
- // don't open new warnings when shutdown was initiated (modal ones will otherwise
- // block the shutdown)
- if (CMimAPI::m_shutDown)
- return -1;
-
- wchar_t *_s = nullptr;
- if (tszTxt)
- _s = const_cast<wchar_t *>(tszTxt);
- else {
- if (uId == -1)
- return -1;
-
- if (dwFlags & CWF_UNTRANSLATED)
- _s = TranslateW(Warnings[uId]);
- else {
- // revert to untranslated warning when the translated message
- // is not well-formatted.
- _s = TranslateW(Warnings[uId]);
-
- if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|'))
- _s = TranslateW(Warnings[uId]);
- }
- }
-
- if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) {
- if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) {
- uint32_t val = M.GetDword("cWarningsL", 0);
- uint32_t mask = ((__int64)1L) << uId;
- if (mask & val) {
- bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0;
- if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
- return (bResult) ? IDYES : IDNO;
- return IDOK;
- }
- }
-
- ptrW s(mir_wstrdup(_s));
- wchar_t *separator_pos = wcschr(s, '|');
-
- if (separator_pos) {
- *separator_pos = 0;
-
- CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags);
- if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
- return w->ShowDialog();
-
- w->ShowDialog();
- }
- }
- return -1;
-}
+/*
+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"
+
+using namespace CWarning;
+
+static MWindowList hWindowList;
+
+class CWarningImpl
+{
+ ptrW m_szTitle, m_szText;
+ UINT m_uId;
+ HFONT m_hFontCaption = nullptr;
+ uint32_t m_dwFlags;
+ HWND m_hwnd = nullptr;
+ bool m_fIsModal;
+
+public:
+ CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) :
+ m_szTitle(mir_wstrdup(tszTitle)),
+ m_szText(mir_wstrdup(tszText))
+ {
+ m_uId = uId;
+ m_dwFlags = dwFlags;
+ m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
+ }
+
+ ~CWarningImpl()
+ {
+ if (m_hFontCaption)
+ ::DeleteObject(m_hFontCaption);
+ }
+
+ // static function to construct and show the dialog, returns the user's choice
+ LRESULT ShowDialog() const
+ {
+ if (!m_fIsModal) {
+ ::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
+ return 0;
+ }
+
+ return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // stub dlg procedure.Just register the object pointer in WM_INITDIALOG
+
+ static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+ if (w)
+ return(w->dlgProc(hwnd, msg, wParam, lParam));
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ w = reinterpret_cast<CWarningImpl *>(lParam);
+ if (w) {
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ return(w->dlgProc(hwnd, msg, wParam, lParam));
+ }
+ break;
+ }
+ return FALSE;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // dialog procedure for the warning dialog box
+
+ INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+ {
+ switch (msg) {
+ case WM_INITDIALOG:
+ m_hwnd = hwnd;
+
+ ::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message"));
+ ::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA);
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0);
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
+
+ TranslateDialogDefault(hwnd);
+ {
+ CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15);
+ str.Append(m_szText);
+ str.Append(L"}");
+ str.Replace(L"\n", L"\\line ");
+ SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
+ ::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str));
+
+ ::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle);
+
+ if (m_dwFlags & CWF_NOALLOWHIDE)
+ Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
+ if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
+ Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
+ ::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
+ }
+ else {
+ Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
+ Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
+ Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
+ ::SetFocus(::GetDlgItem(hwnd, IDOK));
+ }
+
+ UINT uResId = 0;
+ if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND))
+ uResId = 32513;
+ else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING))
+ uResId = 32515;
+ else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION))
+ uResId = 32516;
+ else if (m_dwFlags & MB_ICONQUESTION)
+ uResId = 32514;
+
+ HICON hIcon;
+ if (uResId)
+ hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
+ else
+ hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true);
+ ::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);
+
+ if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
+ ::ShowWindow(hwnd, SW_SHOWNORMAL);
+
+ WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd);
+ }
+ return TRUE;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ HWND hwndChild = reinterpret_cast<HWND>(lParam);
+ UINT id = ::GetDlgCtrlID(hwndChild);
+ if (nullptr == m_hFontCaption) {
+ HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
+ LOGFONT lf = {0};
+
+ ::GetObject(hFont, sizeof(lf), &lf);
+ lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
+ m_hFontCaption = ::CreateFontIndirect(&lf);
+ ::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+ }
+
+ if (IDC_CAPTION == id) {
+ ::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
+ ::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+ }
+
+ if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
+ ::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
+ return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDOK:
+ case IDYES:
+ case IDNO:
+ if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
+ uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId);
+ db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal);
+
+ if (LOWORD(wParam) != IDNO) {
+ newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId);
+ db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal);
+ }
+ }
+ __fallthrough;
+
+ case IDCANCEL:
+ if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll()
+ ::DestroyWindow(hwnd);
+ else
+ ::EndDialog(hwnd, LOWORD(wParam));
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR *)lParam)->code) {
+ case EN_LINK:
+ switch (((ENLINK *)lParam)->msg) {
+ case WM_LBUTTONUP:
+ ENLINK *e = reinterpret_cast<ENLINK *>(lParam);
+
+ const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
+ if (wszUrl) {
+ Utils_OpenUrlW(wszUrl);
+ mir_free(const_cast<wchar_t *>(wszUrl));
+ }
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ ::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ delete this;
+
+ WindowList_Remove(hWindowList, hwnd);
+ Window_FreeIcon_IcoLib(hwnd);
+ break;
+ }
+
+ return FALSE;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// implementation of the CWarningImpl class
+//
+// IMPORTANT note to translators for translation of the warning dialogs:
+//
+// Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
+// warning title from the actual warning text.
+//
+// Also, do NOT insert multiple | characters in the translated string. Not well-formatted
+// warnings cannot be translated and the plugin will show the untranslated versions.
+//
+// strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
+// will be used for important and critical error messages only.
+//
+// some strings are empty, this is intentional and used for error messages that share
+// the message with other possible error notifications (popups, tool tips etc.)
+//
+// Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them.
+
+static wchar_t *Warnings[] = {
+ nullptr,
+ LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE
+ LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"), /* WARN_EDITUSERNOTES */
+ LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"), /* WARN_ICONPACKMISSING */
+ LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."), /* WARN_AEROPEEKSKIN */
+ LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */
+ LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0 is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
+ LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0 and \\b1 send to multiple contacts\\b0 features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */
+ LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"), /* WARN_CLOSEWINDOW */
+ LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
+ LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send cancel message to all open warning dialogs so they are destroyed
+// before TabSRMM is unloaded.
+//
+// called by the OkToExit handler in globals.cpp
+
+void CWarning::destroyAll()
+{
+ if (hWindowList)
+ WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show a warning dialog using the id value. Check whether the user has chosen to
+// not show this message again. This has room for 64 different warning dialogs, which
+// should be enough in the first place. Extending it should not be too hard though.
+
+LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt)
+{
+ if (hWindowList == nullptr)
+ hWindowList = WindowList_Create();
+
+ // don't open new warnings when shutdown was initiated (modal ones will otherwise
+ // block the shutdown)
+ if (CMimAPI::m_shutDown)
+ return -1;
+
+ wchar_t *_s = nullptr;
+ if (tszTxt)
+ _s = const_cast<wchar_t *>(tszTxt);
+ else {
+ if (uId == -1)
+ return -1;
+
+ if (dwFlags & CWF_UNTRANSLATED)
+ _s = TranslateW(Warnings[uId]);
+ else {
+ // revert to untranslated warning when the translated message
+ // is not well-formatted.
+ _s = TranslateW(Warnings[uId]);
+
+ if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|'))
+ _s = TranslateW(Warnings[uId]);
+ }
+ }
+
+ if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) {
+ if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) {
+ uint32_t val = M.GetDword("cWarningsL", 0);
+ uint32_t mask = ((__int64)1L) << uId;
+ if (mask & val) {
+ bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0;
+ if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
+ return (bResult) ? IDYES : IDNO;
+ return IDOK;
+ }
+ }
+
+ ptrW s(mir_wstrdup(_s));
+ wchar_t *separator_pos = wcschr(s, '|');
+
+ if (separator_pos) {
+ *separator_pos = 0;
+
+ CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags);
+ if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
+ return w->ShowDialog();
+
+ w->ShowDialog();
+ }
+ }
+ return -1;
+}
diff --git a/plugins/TipperYM/src/stdafx.cxx b/plugins/TipperYM/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TipperYM/src/stdafx.cxx
+++ b/plugins/TipperYM/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/plugins/Toaster/src/version.h b/plugins/Toaster/src/version.h
index 7a78c7aa15..a34fa9e38e 100644
--- a/plugins/Toaster/src/version.h
+++ b/plugins/Toaster/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Provides popup services based on Windows toast notification for different plugins."
#define __AUTHOR "Miranda NG team"
#define __AUTHORWEB "https://miranda-ng.org/p/Toaster"
-#define __COPYRIGHT "© 2015-22 Miranda NG team"
+#define __COPYRIGHT "© 2015-23 Miranda NG team"
diff --git a/plugins/TooltipNotify/src/stdafx.cxx b/plugins/TooltipNotify/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TooltipNotify/src/stdafx.cxx
+++ b/plugins/TooltipNotify/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/plugins/TopToolBar/src/stdafx.cxx b/plugins/TopToolBar/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TopToolBar/src/stdafx.cxx
+++ b/plugins/TopToolBar/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/plugins/TrafficCounter/src/stdafx.cxx b/plugins/TrafficCounter/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TrafficCounter/src/stdafx.cxx
+++ b/plugins/TrafficCounter/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/plugins/TranslitSwitcher/src/stdafx.cxx b/plugins/TranslitSwitcher/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TranslitSwitcher/src/stdafx.cxx
+++ b/plugins/TranslitSwitcher/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/plugins/TranslitSwitcher/src/version.h b/plugins/TranslitSwitcher/src/version.h
index edbc8f70f6..4174dbf099 100644
--- a/plugins/TranslitSwitcher/src/version.h
+++ b/plugins/TranslitSwitcher/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Allows you to switch a layout or transliterate or invert case of the entered text in the message window with SmileyAdd support."
#define __AUTHOR "Mataes, tico-tico, Tim"
#define __AUTHORWEB "https://miranda-ng.org/p/TranslitSwitcher"
-#define __COPYRIGHT "© 2007 Dmitry Titkov, 2011-22 Mataes, tico-tico"
+#define __COPYRIGHT "© 2007 Dmitry Titkov, 2011-23 Mataes, tico-tico"
diff --git a/plugins/UserGuide/src/stdafx.cxx b/plugins/UserGuide/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/UserGuide/src/stdafx.cxx
+++ b/plugins/UserGuide/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/plugins/UserInfoEx/src/dlg_msgbox.cpp b/plugins/UserInfoEx/src/dlg_msgbox.cpp
index 37b54314a2..c97ac57579 100644
--- a/plugins/UserInfoEx/src/dlg_msgbox.cpp
+++ b/plugins/UserInfoEx/src/dlg_msgbox.cpp
@@ -2,7 +2,7 @@
UserinfoEx plugin for Miranda NG
Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
+© 2012-23 Miranda NG team (https://miranda-ng.org)
© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
This program is free software; you can redistribute it and/or
diff --git a/plugins/UserInfoEx/src/dlg_msgbox.h b/plugins/UserInfoEx/src/dlg_msgbox.h
index d1133cd29a..4f21859077 100644
--- a/plugins/UserInfoEx/src/dlg_msgbox.h
+++ b/plugins/UserInfoEx/src/dlg_msgbox.h
@@ -2,7 +2,7 @@
UserinfoEx plugin for Miranda NG
Copyright:
-© 2012-22 Miranda NG team (https://miranda-ng.org)
+© 2012-23 Miranda NG team (https://miranda-ng.org)
© 2006-10 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol
This program is free software; you can redistribute it and/or
diff --git a/plugins/UserInfoEx/src/stdafx.cxx b/plugins/UserInfoEx/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/UserInfoEx/src/stdafx.cxx
+++ b/plugins/UserInfoEx/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/plugins/Variables/src/stdafx.cxx b/plugins/Variables/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Variables/src/stdafx.cxx
+++ b/plugins/Variables/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/plugins/VoiceService/src/stdafx.cxx b/plugins/VoiceService/src/stdafx.cxx
index 1ab0efee94..ebbde0ade1 100644
--- a/plugins/VoiceService/src/stdafx.cxx
+++ b/plugins/VoiceService/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/plugins/VoiceService/src/version.h b/plugins/VoiceService/src/version.h
index 1c7453b966..b80a24ec4a 100644
--- a/plugins/VoiceService/src/version.h
+++ b/plugins/VoiceService/src/version.h
@@ -10,4 +10,4 @@
#define __DESCRIPTION "Provide services for protocols that support voice calls."
#define __AUTHOR "Ricardo Pescuma Domenecci"
#define __AUTHORWEB "https://miranda-ng.org/p/VoiceService"
-#define __COPYRIGHT "© 2007-2009 Ricardo Pescuma Domenecci, 2020-22 Miranda NG team"
+#define __COPYRIGHT "© 2007-2009 Ricardo Pescuma Domenecci, 2020-23 Miranda NG team"
diff --git a/plugins/Watrack_MPD/src/stdafx.cxx b/plugins/Watrack_MPD/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/Watrack_MPD/src/stdafx.cxx
+++ b/plugins/Watrack_MPD/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/plugins/WhenWasIt/src/dlg_handlers.cpp b/plugins/WhenWasIt/src/dlg_handlers.cpp
index a9ffcc8fed..ce856c2f75 100644
--- a/plugins/WhenWasIt/src/dlg_handlers.cpp
+++ b/plugins/WhenWasIt/src/dlg_handlers.cpp
@@ -2,7 +2,7 @@
WhenWasIt (birthday reminder) plugin for Miranda IM
Copyright © 2006 Cristian Libotean
-Copyright (C) 2014-22 Rozhuk Ivan
+Copyright (C) 2014-23 Rozhuk Ivan
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/plugins/WhenWasIt/src/stdafx.cxx b/plugins/WhenWasIt/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/WhenWasIt/src/stdafx.cxx
+++ b/plugins/WhenWasIt/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/plugins/WhoUsesMyFiles/src/options.cpp b/plugins/WhoUsesMyFiles/src/options.cpp
index 57d3c9570f..750bbcab13 100644
--- a/plugins/WhoUsesMyFiles/src/options.cpp
+++ b/plugins/WhoUsesMyFiles/src/options.cpp
@@ -1,258 +1,258 @@
-/*
-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"
-
-void ShowThePreview()
-{
- if (WumfOptions.AlertFolders) {
- ShowThePopup(nullptr, L"Guest", L"C:\\My Share");
- Sleep(300);
- ShowThePopup(nullptr, L"Guest", L"C:\\My Share\\Photos");
- Sleep(300);
- }
- ShowThePopup(nullptr, L"Guest", L"C:\\Share\\My Photos\\photo.jpg");
- Sleep(300);
- if (WumfOptions.AlertFolders) {
- ShowThePopup(nullptr, L"User", L"C:\\My Share");
- Sleep(300);
- ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies");
- Sleep(300);
- }
- ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies\\The Two Towers.avi");
- Sleep(300);
- if (WumfOptions.AlertFolders) {
- ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives");
- Sleep(300);
- ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k");
- Sleep(300);
- }
- ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k\\setup.exe");
-}
-
-void DisableDelayOptions(HWND hwndDlg)
-{
- CheckDlgButton(hwndDlg, IDC_DELAY_INF,BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DELAY_SET,BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DELAY_DEF,BST_CHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_INF), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SET), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_DEF), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_TX_DELAY_SEC), FALSE);
-}
-
-void ChooseFile(HWND hwndDlg)
-{
- wchar_t szFile[MAX_PATH]; szFile[0]=0;
-
- // Initialize OPENFILENAME
- OPENFILENAME ofn = {0}; // common dialog box structure
- ofn.lStructSize = sizeof(OPENFILENAME);
- ofn.hwndOwner = hwndDlg;
- ofn.lpstrFile = szFile;
- ofn.nMaxFile = _countof(szFile);
- ofn.lpstrFilter = L"All files (*.*)\0*.*\0Text files (*.txt)\0*.txt\0Log files (*.log)\0*.log\0\0";
- ofn.nFilterIndex = 2;
- ofn.Flags = OFN_CREATEPROMPT;
- // Display the Open dialog box.
- if (GetSaveFileName(&ofn)) {
- HANDLE hf = CreateFile(szFile,GENERIC_WRITE,0,nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, nullptr);
- if (hf != INVALID_HANDLE_VALUE) {
- SetDlgItemText(hwndDlg,IDC_FILE,szFile);
- mir_wstrncpy(WumfOptions.LogFile, szFile, MAX_PATH);
- CloseHandle(hf);
- }
- }
- else if (CommDlgExtendedError() != 0) {
- wchar_t str[256];
- mir_snwprintf(str, TranslateT("Common Dialog Error 0x%lx"), CommDlgExtendedError());
- MessageBox(hwndDlg, str, TranslateT("Error"), MB_OK | MB_ICONSTOP);
- }
-}
-
-INT_PTR CALLBACK OptionsDlgProc(HWND hwndDlg,UINT msg,WPARAM wparam,LPARAM lparam)
-{
- uint16_t wControlId = LOWORD(wparam);
- uint16_t wNotifyCode = HIWORD(wparam);
- int seconds;
-
- switch(msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- CheckDlgButton(hwndDlg, IDC_COLOR_WIN, WumfOptions.UseWinColor ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_COLOR_DEF, WumfOptions.UseDefColor ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_COLOR_SET, WumfOptions.SelectColor ? BST_CHECKED : BST_UNCHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK), WumfOptions.SelectColor);
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor);
- if (WumfOptions.SelectColor) {
- SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack);
- SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText);
- }
-
- CheckDlgButton(hwndDlg, IDC_DELAY_INF, WumfOptions.DelayInf ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DELAY_DEF, WumfOptions.DelayDef ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DELAY_SET, WumfOptions.DelaySet ? BST_CHECKED : BST_UNCHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet);
- SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE);
- //Logging & alerts
- CheckDlgButton(hwndDlg, IDC_LOG_FOLDER, WumfOptions.LogFolders ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_ALERT_FOLDER, WumfOptions.AlertFolders ? BST_CHECKED : BST_UNCHECKED);
-
- if (WumfOptions.LogToFile) {
- CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_CHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), TRUE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), TRUE);
- SetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile);
- }
- else {
- CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_UNCHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
- EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), FALSE);
- SetDlgItemText(hwndDlg, IDC_FILE, L"");
- }
- break;
-
- case WM_COMMAND:
- switch(wNotifyCode) {
- case BN_CLICKED :
- switch(wControlId) {
- case IDC_DELAY_SET:
- case IDC_DELAY_DEF:
- case IDC_DELAY_INF:
- WumfOptions.DelaySet = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_SET) == BST_CHECKED);
- WumfOptions.DelayDef = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_DEF) == BST_CHECKED);
- WumfOptions.DelayInf = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_INF) == BST_CHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet);
- SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, TRUE);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- case IDC_COLOR_SET:
- case IDC_COLOR_DEF:
- case IDC_COLOR_WIN:
- WumfOptions.SelectColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_SET) == BST_CHECKED);
- WumfOptions.UseDefColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_DEF) == BST_CHECKED);
- WumfOptions.UseWinColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_WIN) == BST_CHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK),WumfOptions.SelectColor);
- EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor);
- SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack);
- SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- /* end */
- case IDC_LOG_INTO_FILE:
- WumfOptions.LogToFile = (IsDlgButtonChecked(hwndDlg, IDC_LOG_INTO_FILE) == BST_CHECKED);
- EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), WumfOptions.LogToFile);
- EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), WumfOptions.LogToFile);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- case IDC_SEL_FILE:
- ChooseFile(hwndDlg);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- case IDC_LOG_FOLDER:
- WumfOptions.LogFolders = (IsDlgButtonChecked(hwndDlg, IDC_LOG_FOLDER) == BST_CHECKED);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- case IDC_ALERT_FOLDER:
- WumfOptions.AlertFolders = (IsDlgButtonChecked(hwndDlg, IDC_ALERT_FOLDER) == BST_CHECKED);
- break;
- case IDC_PREVIEW:
- ShowThePreview();
- break;
- case IDC_CONN:
- CallService(MS_WUMF_CONNECTIONSSHOW, 0, 0);
- break;
- }
- break;
-
- case CPN_COLOURCHANGED:
- WumfOptions.ColorText = SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_GETCOLOUR,0,0);
- WumfOptions.ColorBack = SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_GETCOLOUR,0,0);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case EN_CHANGE:
- switch(wControlId) {
- case IDC_DELAY_SEC:
- seconds = GetDlgItemInt(hwndDlg, IDC_DELAY_SEC, nullptr, FALSE);
- if (seconds > LIFETIME_MAX)
- WumfOptions.DelaySec = LIFETIME_MAX;
- else if (seconds < LIFETIME_MIN)
- WumfOptions.DelaySec = LIFETIME_MIN;
- else if (seconds <= LIFETIME_MAX || seconds >= LIFETIME_MIN)
- WumfOptions.DelaySec = seconds;
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- case IDC_FILE:
- GetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile, _countof(WumfOptions.LogFile));
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
- }
- break;
- case EN_KILLFOCUS:
- switch(wControlId) {
- case IDC_DELAY_SEC:
- SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE);
- break;
- }
- break;
- }
- break;
-
- case WM_NOTIFY:
- switch(((LPNMHDR)lparam)->idFrom) {
- case 0:
- switch (((LPNMHDR)lparam)->code) {
- case PSN_RESET:
- LoadOptions();
- return TRUE;
-
- case PSN_APPLY:
- g_plugin.setDword(COLOR_TEXT, (uint32_t)WumfOptions.ColorText);
- g_plugin.setDword(COLOR_BACK, (uint32_t)WumfOptions.ColorBack);
- g_plugin.setByte(COLOR_DEF, (uint8_t)WumfOptions.UseDefColor);
- g_plugin.setByte(COLOR_WIN, (uint8_t)WumfOptions.UseWinColor);
- g_plugin.setByte(COLOR_SET, (uint8_t)WumfOptions.SelectColor );
- g_plugin.setByte(DELAY_DEF, (uint8_t)WumfOptions.DelayDef);
- g_plugin.setByte(DELAY_INF, (uint8_t)WumfOptions.DelayInf);
- g_plugin.setByte(DELAY_SET, (uint8_t)WumfOptions.DelaySet);
- g_plugin.setByte(DELAY_SEC, (uint8_t)WumfOptions.DelaySec);
- g_plugin.setByte(LOG_INTO_FILE, (uint8_t)WumfOptions.LogToFile);
- g_plugin.setByte(LOG_FOLDER, (uint8_t)WumfOptions.LogFolders);
- g_plugin.setByte(ALERT_FOLDER, (uint8_t)WumfOptions.AlertFolders);
- GetDlgItemText(hwndDlg, IDC_FILE, WumfOptions.LogFile, _countof(WumfOptions.LogFile));
- g_plugin.setWString(OPT_FILE, WumfOptions.LogFile);
- }
- }
- break;
- }
- return 0;
-}
-
-int OptionsInit(WPARAM wparam, LPARAM)
-{
- OPTIONSDIALOGPAGE odp = {};
- odp.position = 945000000;
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
- odp.szTitle.a = LPGEN("Who uses my files");
- odp.pfnDlgProc = OptionsDlgProc;
- odp.szGroup.a = LPGEN("Services");
- odp.flags = ODPF_BOLDGROUPS;
- g_plugin.addOptions(wparam, &odp);
- return 0;
-}
+/*
+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"
+
+void ShowThePreview()
+{
+ if (WumfOptions.AlertFolders) {
+ ShowThePopup(nullptr, L"Guest", L"C:\\My Share");
+ Sleep(300);
+ ShowThePopup(nullptr, L"Guest", L"C:\\My Share\\Photos");
+ Sleep(300);
+ }
+ ShowThePopup(nullptr, L"Guest", L"C:\\Share\\My Photos\\photo.jpg");
+ Sleep(300);
+ if (WumfOptions.AlertFolders) {
+ ShowThePopup(nullptr, L"User", L"C:\\My Share");
+ Sleep(300);
+ ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies");
+ Sleep(300);
+ }
+ ShowThePopup(nullptr, L"User", L"C:\\My Share\\Movies\\The Two Towers.avi");
+ Sleep(300);
+ if (WumfOptions.AlertFolders) {
+ ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives");
+ Sleep(300);
+ ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k");
+ Sleep(300);
+ }
+ ShowThePopup(nullptr, L"Administrator", L"C:\\Distributives\\Win2k\\setup.exe");
+}
+
+void DisableDelayOptions(HWND hwndDlg)
+{
+ CheckDlgButton(hwndDlg, IDC_DELAY_INF,BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_SET,BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_DEF,BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_INF), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SET), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_DEF), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TX_DELAY_SEC), FALSE);
+}
+
+void ChooseFile(HWND hwndDlg)
+{
+ wchar_t szFile[MAX_PATH]; szFile[0]=0;
+
+ // Initialize OPENFILENAME
+ OPENFILENAME ofn = {0}; // common dialog box structure
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = _countof(szFile);
+ ofn.lpstrFilter = L"All files (*.*)\0*.*\0Text files (*.txt)\0*.txt\0Log files (*.log)\0*.log\0\0";
+ ofn.nFilterIndex = 2;
+ ofn.Flags = OFN_CREATEPROMPT;
+ // Display the Open dialog box.
+ if (GetSaveFileName(&ofn)) {
+ HANDLE hf = CreateFile(szFile,GENERIC_WRITE,0,nullptr,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hf != INVALID_HANDLE_VALUE) {
+ SetDlgItemText(hwndDlg,IDC_FILE,szFile);
+ mir_wstrncpy(WumfOptions.LogFile, szFile, MAX_PATH);
+ CloseHandle(hf);
+ }
+ }
+ else if (CommDlgExtendedError() != 0) {
+ wchar_t str[256];
+ mir_snwprintf(str, TranslateT("Common Dialog Error 0x%lx"), CommDlgExtendedError());
+ MessageBox(hwndDlg, str, TranslateT("Error"), MB_OK | MB_ICONSTOP);
+ }
+}
+
+INT_PTR CALLBACK OptionsDlgProc(HWND hwndDlg,UINT msg,WPARAM wparam,LPARAM lparam)
+{
+ uint16_t wControlId = LOWORD(wparam);
+ uint16_t wNotifyCode = HIWORD(wparam);
+ int seconds;
+
+ switch(msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_COLOR_WIN, WumfOptions.UseWinColor ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_COLOR_DEF, WumfOptions.UseDefColor ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_COLOR_SET, WumfOptions.SelectColor ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK), WumfOptions.SelectColor);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor);
+ if (WumfOptions.SelectColor) {
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack);
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText);
+ }
+
+ CheckDlgButton(hwndDlg, IDC_DELAY_INF, WumfOptions.DelayInf ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_DEF, WumfOptions.DelayDef ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DELAY_SET, WumfOptions.DelaySet ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet);
+ SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE);
+ //Logging & alerts
+ CheckDlgButton(hwndDlg, IDC_LOG_FOLDER, WumfOptions.LogFolders ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_ALERT_FOLDER, WumfOptions.AlertFolders ? BST_CHECKED : BST_UNCHECKED);
+
+ if (WumfOptions.LogToFile) {
+ CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), TRUE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), TRUE);
+ SetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile);
+ }
+ else {
+ CheckDlgButton(hwndDlg,IDC_LOG_INTO_FILE,BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), FALSE);
+ SetDlgItemText(hwndDlg, IDC_FILE, L"");
+ }
+ break;
+
+ case WM_COMMAND:
+ switch(wNotifyCode) {
+ case BN_CLICKED :
+ switch(wControlId) {
+ case IDC_DELAY_SET:
+ case IDC_DELAY_DEF:
+ case IDC_DELAY_INF:
+ WumfOptions.DelaySet = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_SET) == BST_CHECKED);
+ WumfOptions.DelayDef = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_DEF) == BST_CHECKED);
+ WumfOptions.DelayInf = (IsDlgButtonChecked(hwndDlg, IDC_DELAY_INF) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELAY_SEC), WumfOptions.DelaySet);
+ SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, TRUE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_COLOR_SET:
+ case IDC_COLOR_DEF:
+ case IDC_COLOR_WIN:
+ WumfOptions.SelectColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_SET) == BST_CHECKED);
+ WumfOptions.UseDefColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_DEF) == BST_CHECKED);
+ WumfOptions.UseWinColor = (IsDlgButtonChecked(hwndDlg, IDC_COLOR_WIN) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_BACK),WumfOptions.SelectColor);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_COLOR_TEXT), WumfOptions.SelectColor);
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_SETCOLOUR,0,WumfOptions.ColorBack);
+ SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_SETCOLOUR,0,WumfOptions.ColorText);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ /* end */
+ case IDC_LOG_INTO_FILE:
+ WumfOptions.LogToFile = (IsDlgButtonChecked(hwndDlg, IDC_LOG_INTO_FILE) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FILE), WumfOptions.LogToFile);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SEL_FILE), WumfOptions.LogToFile);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_SEL_FILE:
+ ChooseFile(hwndDlg);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_LOG_FOLDER:
+ WumfOptions.LogFolders = (IsDlgButtonChecked(hwndDlg, IDC_LOG_FOLDER) == BST_CHECKED);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_ALERT_FOLDER:
+ WumfOptions.AlertFolders = (IsDlgButtonChecked(hwndDlg, IDC_ALERT_FOLDER) == BST_CHECKED);
+ break;
+ case IDC_PREVIEW:
+ ShowThePreview();
+ break;
+ case IDC_CONN:
+ CallService(MS_WUMF_CONNECTIONSSHOW, 0, 0);
+ break;
+ }
+ break;
+
+ case CPN_COLOURCHANGED:
+ WumfOptions.ColorText = SendDlgItemMessage(hwndDlg,IDC_COLOR_TEXT,CPM_GETCOLOUR,0,0);
+ WumfOptions.ColorBack = SendDlgItemMessage(hwndDlg,IDC_COLOR_BACK,CPM_GETCOLOUR,0,0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case EN_CHANGE:
+ switch(wControlId) {
+ case IDC_DELAY_SEC:
+ seconds = GetDlgItemInt(hwndDlg, IDC_DELAY_SEC, nullptr, FALSE);
+ if (seconds > LIFETIME_MAX)
+ WumfOptions.DelaySec = LIFETIME_MAX;
+ else if (seconds < LIFETIME_MIN)
+ WumfOptions.DelaySec = LIFETIME_MIN;
+ else if (seconds <= LIFETIME_MAX || seconds >= LIFETIME_MIN)
+ WumfOptions.DelaySec = seconds;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_FILE:
+ GetDlgItemText(hwndDlg,IDC_FILE,WumfOptions.LogFile, _countof(WumfOptions.LogFile));
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ break;
+ case EN_KILLFOCUS:
+ switch(wControlId) {
+ case IDC_DELAY_SEC:
+ SetDlgItemInt(hwndDlg, IDC_DELAY_SEC, WumfOptions.DelaySec, FALSE);
+ break;
+ }
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ switch(((LPNMHDR)lparam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lparam)->code) {
+ case PSN_RESET:
+ LoadOptions();
+ return TRUE;
+
+ case PSN_APPLY:
+ g_plugin.setDword(COLOR_TEXT, (uint32_t)WumfOptions.ColorText);
+ g_plugin.setDword(COLOR_BACK, (uint32_t)WumfOptions.ColorBack);
+ g_plugin.setByte(COLOR_DEF, (uint8_t)WumfOptions.UseDefColor);
+ g_plugin.setByte(COLOR_WIN, (uint8_t)WumfOptions.UseWinColor);
+ g_plugin.setByte(COLOR_SET, (uint8_t)WumfOptions.SelectColor );
+ g_plugin.setByte(DELAY_DEF, (uint8_t)WumfOptions.DelayDef);
+ g_plugin.setByte(DELAY_INF, (uint8_t)WumfOptions.DelayInf);
+ g_plugin.setByte(DELAY_SET, (uint8_t)WumfOptions.DelaySet);
+ g_plugin.setByte(DELAY_SEC, (uint8_t)WumfOptions.DelaySec);
+ g_plugin.setByte(LOG_INTO_FILE, (uint8_t)WumfOptions.LogToFile);
+ g_plugin.setByte(LOG_FOLDER, (uint8_t)WumfOptions.LogFolders);
+ g_plugin.setByte(ALERT_FOLDER, (uint8_t)WumfOptions.AlertFolders);
+ GetDlgItemText(hwndDlg, IDC_FILE, WumfOptions.LogFile, _countof(WumfOptions.LogFile));
+ g_plugin.setWString(OPT_FILE, WumfOptions.LogFile);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+int OptionsInit(WPARAM wparam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 945000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.szTitle.a = LPGEN("Who uses my files");
+ odp.pfnDlgProc = OptionsDlgProc;
+ odp.szGroup.a = LPGEN("Services");
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wparam, &odp);
+ return 0;
+}
diff --git a/plugins/WhoUsesMyFiles/src/stdafx.cxx b/plugins/WhoUsesMyFiles/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/WhoUsesMyFiles/src/stdafx.cxx
+++ b/plugins/WhoUsesMyFiles/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/plugins/WinterSpeak/src/stdafx.cxx b/plugins/WinterSpeak/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/WinterSpeak/src/stdafx.cxx
+++ b/plugins/WinterSpeak/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/plugins/XSoundNotify/src/stdafx.cxx b/plugins/XSoundNotify/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/XSoundNotify/src/stdafx.cxx
+++ b/plugins/XSoundNotify/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/plugins/YARelay/src/stdafx.cxx b/plugins/YARelay/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/YARelay/src/stdafx.cxx
+++ b/plugins/YARelay/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/plugins/ZeroNotification/src/options.cpp b/plugins/ZeroNotification/src/options.cpp
index 2643116729..2e3ce755e5 100644
--- a/plugins/ZeroNotification/src/options.cpp
+++ b/plugins/ZeroNotification/src/options.cpp
@@ -1,137 +1,137 @@
-/*
-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 CheckBoxValues_t
-{
- uint32_t style;
- wchar_t *szDescr;
-}
-static const statusValues[] =
-{
- { PF2_ONLINE, TEXT("Online") },
- { PF2_SHORTAWAY, TEXT("Away") },
- { PF2_LONGAWAY, TEXT("Not available") },
- { PF2_LIGHTDND, TEXT("Occupied") },
- { PF2_HEAVYDND, TEXT("Do not disturb") },
- { PF2_FREECHAT, TEXT("Free for chat") },
- { PF2_INVISIBLE, TEXT("Invisible") }
-};
-
-static void FillCheckBoxTree(HWND hwndTree, const struct CheckBoxValues_t *values, int nValues, uint32_t style)
-{
- TVINSERTSTRUCT tvis;
- tvis.hParent = nullptr;
- tvis.hInsertAfter = TVI_LAST;
- tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
- for (int i = 0; i < nValues; i++) {
- tvis.item.lParam = values[i].style;
- tvis.item.pszText = TranslateW(values[i].szDescr);
- tvis.item.stateMask = TVIS_STATEIMAGEMASK;
- tvis.item.state = INDEXTOSTATEIMAGEMASK((style & tvis.item.lParam) != 0 ? 2 : 1);
- TreeView_InsertItem(hwndTree, &tvis);
- }
-}
-
-static uint32_t MakeCheckBoxTreeFlags(HWND hwndTree)
-{
- uint32_t flags = 0;
-
- TVITEM tvi;
- tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
- tvi.hItem = TreeView_GetRoot(hwndTree);
- while (tvi.hItem) {
- TreeView_GetItem(hwndTree, &tvi);
- if (((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2))
- flags |= tvi.lParam;
- tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
- }
- return flags;
-}
-
-static INT_PTR CALLBACK DlgProcNoSoundOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
-{
- uint32_t test;
- switch (msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
- SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
- SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
-
- FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOSOUND), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoSound", DEFAULT_NOSOUND));
- FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoBlink", DEFAULT_NOBLINK));
- FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoCLCBlink", DEFAULT_NOCLCBLINK));
- return TRUE;
-
- case WM_COMMAND:
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case IDC_NOSOUND:
- case IDC_NOBLINK:
- case IDC_NOCLCBLINK:
- if (((LPNMHDR)lParam)->code == NM_CLICK) {
- TVHITTESTINFO hti;
- hti.pt.x = (short)LOWORD(GetMessagePos());
- hti.pt.y = (short)HIWORD(GetMessagePos());
- ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
- if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti)) {
- if (hti.flags & TVHT_ONITEMSTATEICON) {
- TVITEM tvi;
- tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
- tvi.hItem = hti.hItem;
- TreeView_GetItem(((LPNMHDR)lParam)->hwndFrom, &tvi);
- tvi.iImage = tvi.iSelectedImage = tvi.iImage == 1 ? 2 : 1;
- TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom, &tvi);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- }
- }
- }
- break;
- case 0:
- switch (((LPNMHDR)lParam)->code) {
- case PSN_APPLY:
- g_plugin.setDword("NoSound", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOSOUND)));
- g_plugin.setDword("NoBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOBLINK)));
- g_plugin.setDword("NoCLCBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOCLCBLINK)));
-
- test = db_get_w(0, "CList", "Status", 0);
- SetNotify(Proto_Status2Flag(db_get_w(0, "CList", "Status", 0)));
- return TRUE;
- }
- break;
- }
- }
- return FALSE;
-}
-
-int OptionsInitialize(WPARAM wParam, LPARAM)
-{
- OPTIONSDIALOGPAGE odp = {};
- odp.position = 100000000;
- odp.flags = ODPF_UNICODE;
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NOSOUND);
- odp.szTitle.w = LPGENW("Zero Notifications");
- odp.szGroup.w = LPGENW("Plugins");
- odp.pfnDlgProc = DlgProcNoSoundOpts;
- g_plugin.addOptions(wParam, &odp);
- return 0;
-}
+/*
+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 CheckBoxValues_t
+{
+ uint32_t style;
+ wchar_t *szDescr;
+}
+static const statusValues[] =
+{
+ { PF2_ONLINE, TEXT("Online") },
+ { PF2_SHORTAWAY, TEXT("Away") },
+ { PF2_LONGAWAY, TEXT("Not available") },
+ { PF2_LIGHTDND, TEXT("Occupied") },
+ { PF2_HEAVYDND, TEXT("Do not disturb") },
+ { PF2_FREECHAT, TEXT("Free for chat") },
+ { PF2_INVISIBLE, TEXT("Invisible") }
+};
+
+static void FillCheckBoxTree(HWND hwndTree, const struct CheckBoxValues_t *values, int nValues, uint32_t style)
+{
+ TVINSERTSTRUCT tvis;
+ tvis.hParent = nullptr;
+ tvis.hInsertAfter = TVI_LAST;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ for (int i = 0; i < nValues; i++) {
+ tvis.item.lParam = values[i].style;
+ tvis.item.pszText = TranslateW(values[i].szDescr);
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((style & tvis.item.lParam) != 0 ? 2 : 1);
+ TreeView_InsertItem(hwndTree, &tvis);
+ }
+}
+
+static uint32_t MakeCheckBoxTreeFlags(HWND hwndTree)
+{
+ uint32_t flags = 0;
+
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
+ tvi.hItem = TreeView_GetRoot(hwndTree);
+ while (tvi.hItem) {
+ TreeView_GetItem(hwndTree, &tvi);
+ if (((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2))
+ flags |= tvi.lParam;
+ tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem);
+ }
+ return flags;
+}
+
+static INT_PTR CALLBACK DlgProcNoSoundOpts(HWND hwndDlg, UINT msg, WPARAM, LPARAM lParam)
+{
+ uint32_t test;
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOSOUND), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+ SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE, GetWindowLongPtr(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);
+
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOSOUND), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoSound", DEFAULT_NOSOUND));
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoBlink", DEFAULT_NOBLINK));
+ FillCheckBoxTree(GetDlgItem(hwndDlg, IDC_NOCLCBLINK), statusValues, sizeof(statusValues) / sizeof(statusValues[0]), g_plugin.getDword("NoCLCBlink", DEFAULT_NOCLCBLINK));
+ return TRUE;
+
+ case WM_COMMAND:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case IDC_NOSOUND:
+ case IDC_NOBLINK:
+ case IDC_NOCLCBLINK:
+ if (((LPNMHDR)lParam)->code == NM_CLICK) {
+ TVHITTESTINFO hti;
+ hti.pt.x = (short)LOWORD(GetMessagePos());
+ hti.pt.y = (short)HIWORD(GetMessagePos());
+ ScreenToClient(((LPNMHDR)lParam)->hwndFrom, &hti.pt);
+ if (TreeView_HitTest(((LPNMHDR)lParam)->hwndFrom, &hti)) {
+ if (hti.flags & TVHT_ONITEMSTATEICON) {
+ TVITEM tvi;
+ tvi.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+ tvi.hItem = hti.hItem;
+ TreeView_GetItem(((LPNMHDR)lParam)->hwndFrom, &tvi);
+ tvi.iImage = tvi.iSelectedImage = tvi.iImage == 1 ? 2 : 1;
+ TreeView_SetItem(((LPNMHDR)lParam)->hwndFrom, &tvi);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ }
+ }
+ break;
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ g_plugin.setDword("NoSound", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOSOUND)));
+ g_plugin.setDword("NoBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOBLINK)));
+ g_plugin.setDword("NoCLCBlink", MakeCheckBoxTreeFlags(GetDlgItem(hwndDlg, IDC_NOCLCBLINK)));
+
+ test = db_get_w(0, "CList", "Status", 0);
+ SetNotify(Proto_Status2Flag(db_get_w(0, "CList", "Status", 0)));
+ return TRUE;
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+int OptionsInitialize(WPARAM wParam, LPARAM)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 100000000;
+ odp.flags = ODPF_UNICODE;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NOSOUND);
+ odp.szTitle.w = LPGENW("Zero Notifications");
+ odp.szGroup.w = LPGENW("Plugins");
+ odp.pfnDlgProc = DlgProcNoSoundOpts;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/ZeroNotification/src/stdafx.cxx b/plugins/ZeroNotification/src/stdafx.cxx
index 564f422ca2..8c570f6949 100644
--- a/plugins/ZeroNotification/src/stdafx.cxx
+++ b/plugins/ZeroNotification/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/plugins/ZeroSwitch/src/stdafx.cxx b/plugins/ZeroSwitch/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/ZeroSwitch/src/stdafx.cxx
+++ b/plugins/ZeroSwitch/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/plugins/helpers/commonheaders.h b/plugins/helpers/commonheaders.h
index e8075d563a..025b0da3d9 100644
--- a/plugins/helpers/commonheaders.h
+++ b/plugins/helpers/commonheaders.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-03 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/plugins/wbOSD/src/stdafx.cxx b/plugins/wbOSD/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/wbOSD/src/stdafx.cxx
+++ b/plugins/wbOSD/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